comparison hgext/rebase.py @ 34005:5e83a8fe6bc4

rebase: initial support for multiple destinations This patch defines `SRC` (a single source revision) and `ALLSRC` (all source revisions) to be valid names in `--dest` revset if `--src` or `--rev` is used. So destination could be defined differently according to source revisions. The names are capitalized to make it clear they are "dynamically defined", distinguishable from normal revsets (Thanks Augie for the suggestion). This is useful, for example, `-r 'orphan()' -d 'calc-dest(SRC)'` to solve instability, which seems to be a highly wanted feature. The feature is not completed, namely if `-d` overlaps with `-r`, things could go wrong. A later patch will handle that case. The feature is also gated by `experimental.rebase.multidest` config option which is default off. Differential Revision: https://phab.mercurial-scm.org/D469
author Jun Wu <quark@fb.com>
date Tue, 29 Aug 2017 17:27:37 -0700
parents af609bb3487f
children 32528419db64
comparison
equal deleted inserted replaced
34004:af609bb3487f 34005:5e83a8fe6bc4
44 phases, 44 phases,
45 registrar, 45 registrar,
46 repair, 46 repair,
47 repoview, 47 repoview,
48 revset, 48 revset,
49 revsetlang,
49 scmutil, 50 scmutil,
50 smartset, 51 smartset,
51 util, 52 util,
52 ) 53 )
53 54
734 735
735 if ui.configbool('commands', 'rebase.requiredest') and not destf: 736 if ui.configbool('commands', 'rebase.requiredest') and not destf:
736 raise error.Abort(_('you must specify a destination'), 737 raise error.Abort(_('you must specify a destination'),
737 hint=_('use: hg rebase -d REV')) 738 hint=_('use: hg rebase -d REV'))
738 739
739 if destf: 740 dest = None
740 dest = scmutil.revsingle(repo, destf)
741 741
742 if revf: 742 if revf:
743 rebaseset = scmutil.revrange(repo, revf) 743 rebaseset = scmutil.revrange(repo, revf)
744 if not rebaseset: 744 if not rebaseset:
745 ui.status(_('empty "rev" revision set - nothing to rebase\n')) 745 ui.status(_('empty "rev" revision set - nothing to rebase\n'))
755 base = scmutil.revrange(repo, [basef or '.']) 755 base = scmutil.revrange(repo, [basef or '.'])
756 if not base: 756 if not base:
757 ui.status(_('empty "base" revision set - ' 757 ui.status(_('empty "base" revision set - '
758 "can't compute rebase set\n")) 758 "can't compute rebase set\n"))
759 return None 759 return None
760 if not destf: 760 if destf:
761 # --base does not support multiple destinations
762 dest = scmutil.revsingle(repo, destf)
763 else:
761 dest = repo[_destrebase(repo, base, destspace=destspace)] 764 dest = repo[_destrebase(repo, base, destspace=destspace)]
762 destf = str(dest) 765 destf = str(dest)
763 766
764 roots = [] # selected children of branching points 767 roots = [] # selected children of branching points
765 bpbase = {} # {branchingpoint: [origbase]} 768 bpbase = {} # {branchingpoint: [origbase]}
804 807
805 if not destf: 808 if not destf:
806 dest = repo[_destrebase(repo, rebaseset, destspace=destspace)] 809 dest = repo[_destrebase(repo, rebaseset, destspace=destspace)]
807 destf = str(dest) 810 destf = str(dest)
808 811
809 # assign dest to each rev in rebaseset 812 allsrc = revsetlang.formatspec('%ld', rebaseset)
810 destrev = dest.rev() 813 alias = {'ALLSRC': allsrc}
811 destmap = {r: destrev for r in rebaseset} # {srcrev: destrev} 814
815 if dest is None:
816 try:
817 # fast path: try to resolve dest without SRC alias
818 dest = scmutil.revsingle(repo, destf, localalias=alias)
819 except error.RepoLookupError:
820 if not ui.configbool('experimental', 'rebase.multidest'):
821 raise
822 # multi-dest path: resolve dest for each SRC separately
823 destmap = {}
824 for r in rebaseset:
825 alias['SRC'] = revsetlang.formatspec('%d', r)
826 # use repo.anyrevs instead of scmutil.revsingle because we
827 # don't want to abort if destset is empty.
828 destset = repo.anyrevs([destf], user=True, localalias=alias)
829 size = len(destset)
830 if size == 1:
831 destmap[r] = destset.first()
832 elif size == 0:
833 ui.note(_('skipping %s - empty destination\n') % repo[r])
834 else:
835 raise error.Abort(_('rebase destination for %s is not '
836 'unique') % repo[r])
837
838 if dest is not None:
839 # single-dest case: assign dest to each rev in rebaseset
840 destrev = dest.rev()
841 destmap = {r: destrev for r in rebaseset} # {srcrev: destrev}
842
843 if not destmap:
844 ui.status(_('nothing to rebase - empty destination\n'))
845 return None
812 846
813 return destmap 847 return destmap
814 848
815 def externalparent(repo, state, destancestors): 849 def externalparent(repo, state, destancestors):
816 """Return the revision that should be used as the second parent 850 """Return the revision that should be used as the second parent
901 935
902 rev is what is being rebased. Return a list of two revs, which are the 936 rev is what is being rebased. Return a list of two revs, which are the
903 adjusted destinations for rev's p1 and p2, respectively. If a parent is 937 adjusted destinations for rev's p1 and p2, respectively. If a parent is
904 nullrev, return dest without adjustment for it. 938 nullrev, return dest without adjustment for it.
905 939
906 For example, when doing rebase -r B+E -d F, rebase will first move B to B1, 940 For example, when doing rebasing B+E to F, C to G, rebase will first move B
907 and E's destination will be adjusted from F to B1. 941 to B1, and E's destination will be adjusted from F to B1.
908 942
909 B1 <- written during rebasing B 943 B1 <- written during rebasing B
910 | 944 |
911 F <- original destination of B, E 945 F <- original destination of B, E
912 | 946 |
914 | | 948 | |
915 | D <- prev, one parent of rev being checked 949 | D <- prev, one parent of rev being checked
916 | | 950 | |
917 | x <- skipped, ex. no successor or successor in (::dest) 951 | x <- skipped, ex. no successor or successor in (::dest)
918 | | 952 | |
919 | C 953 | C <- rebased as C', different destination
920 | | 954 | |
921 | B <- rebased as B1 955 | B <- rebased as B1 C'
922 |/ 956 |/ |
923 A 957 A G <- destination of C, different
924 958
925 Another example about merge changeset, rebase -r C+G+H -d K, rebase will 959 Another example about merge changeset, rebase -r C+G+H -d K, rebase will
926 first move C to C1, G to G1, and when it's checking H, the adjusted 960 first move C to C1, G to G1, and when it's checking H, the adjusted
927 destinations will be [C1, G1]. 961 destinations will be [C1, G1].
928 962