33 return ancestor.ancestor(a, b, pfunc) |
33 return ancestor.ancestor(a, b, pfunc) |
34 |
34 |
35 if not first: |
35 if not first: |
36 ancestor.ancestor = newancestor |
36 ancestor.ancestor = newancestor |
37 else: |
37 else: |
38 repo.ui.debug(_("first revision, do not change ancestor\n")) |
38 repo.ui.debug("first revision, do not change ancestor\n") |
39 stats = merge.update(repo, rev, True, True, False) |
39 stats = merge.update(repo, rev, True, True, False) |
40 return stats |
40 return stats |
41 |
41 |
42 def rebase(ui, repo, **opts): |
42 def rebase(ui, repo, **opts): |
43 """move changeset (and descendants) to a different branch |
43 """move changeset (and descendants) to a different branch |
147 def concludenode(repo, rev, p1, p2, state, collapse, last=False, skipped=None, |
147 def concludenode(repo, rev, p1, p2, state, collapse, last=False, skipped=None, |
148 extrafn=None): |
148 extrafn=None): |
149 """Skip commit if collapsing has been required and rev is not the last |
149 """Skip commit if collapsing has been required and rev is not the last |
150 revision, commit otherwise |
150 revision, commit otherwise |
151 """ |
151 """ |
152 repo.ui.debug(_(" set parents\n")) |
152 repo.ui.debug(" set parents\n") |
153 if collapse and not last: |
153 if collapse and not last: |
154 repo.dirstate.setparents(repo[p1].node()) |
154 repo.dirstate.setparents(repo[p1].node()) |
155 return None |
155 return None |
156 |
156 |
157 repo.dirstate.setparents(repo[p1].node(), repo[p2].node()) |
157 repo.dirstate.setparents(repo[p1].node(), repo[p2].node()) |
185 raise |
185 raise |
186 |
186 |
187 def rebasenode(repo, rev, target, state, skipped, targetancestors, collapse, |
187 def rebasenode(repo, rev, target, state, skipped, targetancestors, collapse, |
188 extrafn): |
188 extrafn): |
189 'Rebase a single revision' |
189 'Rebase a single revision' |
190 repo.ui.debug(_("rebasing %d:%s\n") % (rev, repo[rev])) |
190 repo.ui.debug("rebasing %d:%s\n" % (rev, repo[rev])) |
191 |
191 |
192 p1, p2 = defineparents(repo, rev, target, state, targetancestors) |
192 p1, p2 = defineparents(repo, rev, target, state, targetancestors) |
193 |
193 |
194 repo.ui.debug(_(" future parents are %d and %d\n") % (repo[p1].rev(), |
194 repo.ui.debug(" future parents are %d and %d\n" % (repo[p1].rev(), |
195 repo[p2].rev())) |
195 repo[p2].rev())) |
196 |
196 |
197 # Merge phase |
197 # Merge phase |
198 if len(repo.parents()) != 2: |
198 if len(repo.parents()) != 2: |
199 # Update to target and merge it with local |
199 # Update to target and merge it with local |
200 if repo['.'].rev() != repo[p1].rev(): |
200 if repo['.'].rev() != repo[p1].rev(): |
201 repo.ui.debug(_(" update to %d:%s\n") % (repo[p1].rev(), repo[p1])) |
201 repo.ui.debug(" update to %d:%s\n" % (repo[p1].rev(), repo[p1])) |
202 merge.update(repo, p1, False, True, False) |
202 merge.update(repo, p1, False, True, False) |
203 else: |
203 else: |
204 repo.ui.debug(_(" already in target\n")) |
204 repo.ui.debug(" already in target\n") |
205 repo.dirstate.write() |
205 repo.dirstate.write() |
206 repo.ui.debug(_(" merge against %d:%s\n") % (repo[rev].rev(), repo[rev])) |
206 repo.ui.debug(" merge against %d:%s\n" % (repo[rev].rev(), repo[rev])) |
207 first = repo[rev].rev() == repo[min(state)].rev() |
207 first = repo[rev].rev() == repo[min(state)].rev() |
208 stats = rebasemerge(repo, rev, first) |
208 stats = rebasemerge(repo, rev, first) |
209 |
209 |
210 if stats[3] > 0: |
210 if stats[3] > 0: |
211 raise util.Abort(_('fix unresolved conflicts with hg resolve then ' |
211 raise util.Abort(_('fix unresolved conflicts with hg resolve then ' |
212 'run hg rebase --continue')) |
212 'run hg rebase --continue')) |
213 else: # we have an interrupted rebase |
213 else: # we have an interrupted rebase |
214 repo.ui.debug(_('resuming interrupted rebase\n')) |
214 repo.ui.debug('resuming interrupted rebase\n') |
215 |
215 |
216 # Keep track of renamed files in the revision that is going to be rebased |
216 # Keep track of renamed files in the revision that is going to be rebased |
217 # Here we simulate the copies and renames in the source changeset |
217 # Here we simulate the copies and renames in the source changeset |
218 cop, diver = copies.copies(repo, repo[rev], repo[target], repo[p2], True) |
218 cop, diver = copies.copies(repo, repo[rev], repo[target], repo[p2], True) |
219 m1 = repo[rev].manifest() |
219 m1 = repo[rev].manifest() |
232 if newrev is not None: |
232 if newrev is not None: |
233 state[rev] = repo[newrev].rev() |
233 state[rev] = repo[newrev].rev() |
234 else: |
234 else: |
235 if not collapse: |
235 if not collapse: |
236 repo.ui.note(_('no changes, revision %d skipped\n') % rev) |
236 repo.ui.note(_('no changes, revision %d skipped\n') % rev) |
237 repo.ui.debug(_('next revision set to %s\n') % p1) |
237 repo.ui.debug('next revision set to %s\n' % p1) |
238 skipped.add(rev) |
238 skipped.add(rev) |
239 state[rev] = p1 |
239 state[rev] = p1 |
240 |
240 |
241 def defineparents(repo, rev, target, state, targetancestors): |
241 def defineparents(repo, rev, target, state, targetancestors): |
242 'Return the new parent relationship of the revision that will be rebased' |
242 'Return the new parent relationship of the revision that will be rebased' |
278 def updatemq(repo, state, skipped, **opts): |
278 def updatemq(repo, state, skipped, **opts): |
279 'Update rebased mq patches - finalize and then import them' |
279 'Update rebased mq patches - finalize and then import them' |
280 mqrebase = {} |
280 mqrebase = {} |
281 for p in repo.mq.applied: |
281 for p in repo.mq.applied: |
282 if repo[p.rev].rev() in state: |
282 if repo[p.rev].rev() in state: |
283 repo.ui.debug(_('revision %d is an mq patch (%s), finalize it.\n') % |
283 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' % |
284 (repo[p.rev].rev(), p.name)) |
284 (repo[p.rev].rev(), p.name)) |
285 mqrebase[repo[p.rev].rev()] = (p.name, isagitpatch(repo, p.name)) |
285 mqrebase[repo[p.rev].rev()] = (p.name, isagitpatch(repo, p.name)) |
286 |
286 |
287 if mqrebase: |
287 if mqrebase: |
288 repo.mq.finish(repo, mqrebase.keys()) |
288 repo.mq.finish(repo, mqrebase.keys()) |
289 |
289 |
290 # We must start import from the newest revision |
290 # We must start import from the newest revision |
291 for rev in sorted(mqrebase, reverse=True): |
291 for rev in sorted(mqrebase, reverse=True): |
292 if rev not in skipped: |
292 if rev not in skipped: |
293 repo.ui.debug(_('import mq patch %d (%s)\n') |
293 repo.ui.debug('import mq patch %d (%s)\n' |
294 % (state[rev], mqrebase[rev][0])) |
294 % (state[rev], mqrebase[rev][0])) |
295 repo.mq.qimport(repo, (), patchname=mqrebase[rev][0], |
295 repo.mq.qimport(repo, (), patchname=mqrebase[rev][0], |
296 git=mqrebase[rev][1],rev=[str(state[rev])]) |
296 git=mqrebase[rev][1],rev=[str(state[rev])]) |
297 repo.mq.save_dirty() |
297 repo.mq.save_dirty() |
298 |
298 |
309 for d, v in state.iteritems(): |
309 for d, v in state.iteritems(): |
310 oldrev = repo[d].hex() |
310 oldrev = repo[d].hex() |
311 newrev = repo[v].hex() |
311 newrev = repo[v].hex() |
312 f.write("%s:%s\n" % (oldrev, newrev)) |
312 f.write("%s:%s\n" % (oldrev, newrev)) |
313 f.close() |
313 f.close() |
314 repo.ui.debug(_('rebase status stored\n')) |
314 repo.ui.debug('rebase status stored\n') |
315 |
315 |
316 def clearstatus(repo): |
316 def clearstatus(repo): |
317 'Remove the status files' |
317 'Remove the status files' |
318 if os.path.exists(repo.join("rebasestate")): |
318 if os.path.exists(repo.join("rebasestate")): |
319 util.unlink(repo.join("rebasestate")) |
319 util.unlink(repo.join("rebasestate")) |
340 elif i == 5: |
340 elif i == 5: |
341 keepbranches = bool(int(l)) |
341 keepbranches = bool(int(l)) |
342 else: |
342 else: |
343 oldrev, newrev = l.split(':') |
343 oldrev, newrev = l.split(':') |
344 state[repo[oldrev].rev()] = repo[newrev].rev() |
344 state[repo[oldrev].rev()] = repo[newrev].rev() |
345 repo.ui.debug(_('rebase status resumed\n')) |
345 repo.ui.debug('rebase status resumed\n') |
346 return originalwd, target, state, collapse, keep, keepbranches, external |
346 return originalwd, target, state, collapse, keep, keepbranches, external |
347 except IOError, err: |
347 except IOError, err: |
348 if err.errno != errno.ENOENT: |
348 if err.errno != errno.ENOENT: |
349 raise |
349 raise |
350 raise util.Abort(_('no rebase in progress')) |
350 raise util.Abort(_('no rebase in progress')) |
390 cwd = repo[base].rev() |
390 cwd = repo[base].rev() |
391 else: |
391 else: |
392 cwd = repo['.'].rev() |
392 cwd = repo['.'].rev() |
393 |
393 |
394 if cwd == dest: |
394 if cwd == dest: |
395 repo.ui.debug(_('already working on current\n')) |
395 repo.ui.debug('already working on current\n') |
396 return None |
396 return None |
397 |
397 |
398 targetancestors = set(repo.changelog.ancestors(dest)) |
398 targetancestors = set(repo.changelog.ancestors(dest)) |
399 if cwd in targetancestors: |
399 if cwd in targetancestors: |
400 repo.ui.debug(_('already working on the current branch\n')) |
400 repo.ui.debug('already working on the current branch\n') |
401 return None |
401 return None |
402 |
402 |
403 cwdancestors = set(repo.changelog.ancestors(cwd)) |
403 cwdancestors = set(repo.changelog.ancestors(cwd)) |
404 cwdancestors.add(cwd) |
404 cwdancestors.add(cwd) |
405 rebasingbranch = cwdancestors - targetancestors |
405 rebasingbranch = cwdancestors - targetancestors |
406 source = min(rebasingbranch) |
406 source = min(rebasingbranch) |
407 |
407 |
408 repo.ui.debug(_('rebase onto %d starting from %d\n') % (dest, source)) |
408 repo.ui.debug('rebase onto %d starting from %d\n' % (dest, source)) |
409 state = dict.fromkeys(repo.changelog.descendants(source), nullrev) |
409 state = dict.fromkeys(repo.changelog.descendants(source), nullrev) |
410 external = nullrev |
410 external = nullrev |
411 if collapse: |
411 if collapse: |
412 if not targetancestors: |
412 if not targetancestors: |
413 targetancestors = set(repo.changelog.ancestors(dest)) |
413 targetancestors = set(repo.changelog.ancestors(dest)) |
427 def pullrebase(orig, ui, repo, *args, **opts): |
427 def pullrebase(orig, ui, repo, *args, **opts): |
428 'Call rebase after pull if the latter has been invoked with --rebase' |
428 'Call rebase after pull if the latter has been invoked with --rebase' |
429 if opts.get('rebase'): |
429 if opts.get('rebase'): |
430 if opts.get('update'): |
430 if opts.get('update'): |
431 del opts['update'] |
431 del opts['update'] |
432 ui.debug(_('--update and --rebase are not compatible, ignoring ' |
432 ui.debug('--update and --rebase are not compatible, ignoring ' |
433 'the update flag\n')) |
433 'the update flag\n') |
434 |
434 |
435 cmdutil.bail_if_changed(repo) |
435 cmdutil.bail_if_changed(repo) |
436 revsprepull = len(repo) |
436 revsprepull = len(repo) |
437 orig(ui, repo, *args, **opts) |
437 orig(ui, repo, *args, **opts) |
438 revspostpull = len(repo) |
438 revspostpull = len(repo) |