385 obsolete.createmarkers(repo, [(other, (new,))]) |
390 obsolete.createmarkers(repo, [(other, (new,))]) |
386 phases.retractboundary(repo, tr, other.phase(), [new.node()]) |
391 phases.retractboundary(repo, tr, other.phase(), [new.node()]) |
387 return (True, new.node()) |
392 return (True, new.node()) |
388 finally: |
393 finally: |
389 repo.ui.restoreconfig(emtpycommitallowed) |
394 repo.ui.restoreconfig(emtpycommitallowed) |
|
395 |
|
396 class MergeFailure(error.Abort): |
|
397 pass |
|
398 |
|
399 def relocate(repo, orig, dest, pctx=None, keepbranch=False): |
|
400 """rewrites the orig rev on dest rev |
|
401 |
|
402 returns the node of new commit which is formed |
|
403 """ |
|
404 if orig.rev() == dest.rev(): |
|
405 raise error.Abort(_('tried to relocate a node on top of itself'), |
|
406 hint=_("This shouldn't happen. If you still " |
|
407 "need to move changesets, please do so " |
|
408 "manually with nothing to rebase - working " |
|
409 "directory parent is also destination")) |
|
410 |
|
411 if pctx is None: |
|
412 if len(orig.parents()) == 2: |
|
413 raise error.Abort(_("tried to relocate a merge commit without " |
|
414 "specifying which parent should be moved"), |
|
415 hint=_("Specify the parent by passing in pctx")) |
|
416 pctx = orig.p1() |
|
417 |
|
418 commitmsg = orig.description() |
|
419 |
|
420 cache = {} |
|
421 sha1s = re.findall(sha1re, commitmsg) |
|
422 unfi = repo.unfiltered() |
|
423 for sha1 in sha1s: |
|
424 ctx = None |
|
425 try: |
|
426 ctx = unfi[sha1] |
|
427 except error.RepoLookupError: |
|
428 continue |
|
429 |
|
430 if not ctx.obsolete(): |
|
431 continue |
|
432 |
|
433 successors = compat.successorssets(repo, ctx.node(), cache) |
|
434 |
|
435 # We can't make any assumptions about how to update the hash if the |
|
436 # cset in question was split or diverged. |
|
437 if len(successors) == 1 and len(successors[0]) == 1: |
|
438 newsha1 = node.hex(successors[0][0]) |
|
439 commitmsg = commitmsg.replace(sha1, newsha1[:len(sha1)]) |
|
440 else: |
|
441 repo.ui.note(_('The stale commit message reference to %s could ' |
|
442 'not be updated\n') % sha1) |
|
443 |
|
444 tr = repo.currenttransaction() |
|
445 assert tr is not None |
|
446 try: |
|
447 r = _evolvemerge(repo, orig, dest, pctx, keepbranch) |
|
448 if r[-1]: # some conflict |
|
449 raise error.Abort(_('unresolved merge conflicts ' |
|
450 '(see hg help resolve)')) |
|
451 nodenew = _relocatecommit(repo, orig, commitmsg) |
|
452 except error.Abort as exc: |
|
453 with repo.dirstate.parentchange(): |
|
454 repo.setparents(repo['.'].node(), node.nullid) |
|
455 repo.dirstate.write(tr) |
|
456 # fix up dirstate for copies and renames |
|
457 compat.duplicatecopies(repo, repo[None], dest.rev(), orig.p1().rev()) |
|
458 |
|
459 class LocalMergeFailure(MergeFailure, exc.__class__): |
|
460 pass |
|
461 exc.__class__ = LocalMergeFailure |
|
462 tr.close() # to keep changes in this transaction (e.g. dirstate) |
|
463 raise |
|
464 _finalizerelocate(repo, orig, dest, nodenew, tr) |
|
465 return nodenew |
|
466 |
|
467 def _relocatecommit(repo, orig, commitmsg): |
|
468 if commitmsg is None: |
|
469 commitmsg = orig.description() |
|
470 extra = dict(orig.extra()) |
|
471 if 'branch' in extra: |
|
472 del extra['branch'] |
|
473 extra['rebase_source'] = orig.hex() |
|
474 |
|
475 backup = repo.ui.backupconfig('phases', 'new-commit') |
|
476 try: |
|
477 targetphase = max(orig.phase(), phases.draft) |
|
478 repo.ui.setconfig('phases', 'new-commit', targetphase, 'evolve') |
|
479 # Commit might fail if unresolved files exist |
|
480 nodenew = repo.commit(text=commitmsg, user=orig.user(), |
|
481 date=orig.date(), extra=extra) |
|
482 finally: |
|
483 repo.ui.restoreconfig(backup) |
|
484 return nodenew |
|
485 |
|
486 def _finalizerelocate(repo, orig, dest, nodenew, tr): |
|
487 destbookmarks = repo.nodebookmarks(dest.node()) |
|
488 nodesrc = orig.node() |
|
489 oldbookmarks = repo.nodebookmarks(nodesrc) |
|
490 bmchanges = [] |
|
491 |
|
492 if nodenew is not None: |
|
493 obsolete.createmarkers(repo, [(repo[nodesrc], (repo[nodenew],))]) |
|
494 for book in oldbookmarks: |
|
495 bmchanges.append((book, nodenew)) |
|
496 else: |
|
497 obsolete.createmarkers(repo, [(repo[nodesrc], ())]) |
|
498 # Behave like rebase, move bookmarks to dest |
|
499 for book in oldbookmarks: |
|
500 bmchanges.append((book, dest.node())) |
|
501 for book in destbookmarks: # restore bookmark that rebase move |
|
502 bmchanges.append((book, dest.node())) |
|
503 if bmchanges: |
|
504 compat.bookmarkapplychanges(repo, tr, bmchanges) |
|
505 |
|
506 def _evolvemerge(repo, orig, dest, pctx, keepbranch): |
|
507 """Used by the evolve function to merge dest on top of pctx. |
|
508 return the same tuple as merge.graft""" |
|
509 if repo['.'].rev() != dest.rev(): |
|
510 merge.update(repo, |
|
511 dest, |
|
512 branchmerge=False, |
|
513 force=True) |
|
514 if repo._activebookmark: |
|
515 repo.ui.status(_("(leaving bookmark %s)\n") % repo._activebookmark) |
|
516 bookmarksmod.deactivate(repo) |
|
517 if keepbranch: |
|
518 repo.dirstate.setbranch(orig.branch()) |
|
519 if util.safehasattr(repo, 'currenttopic'): |
|
520 # uurrgs |
|
521 # there no other topic setter yet |
|
522 if not orig.topic() and repo.vfs.exists('topic'): |
|
523 repo.vfs.unlink('topic') |
|
524 else: |
|
525 with repo.vfs.open('topic', 'w') as f: |
|
526 f.write(orig.topic()) |
|
527 |
|
528 return merge.graft(repo, orig, pctx, ['destination', 'evolving'], True) |