[PATCH] rebase: add --rev option to rebase

Pierre-Yves David pierre-yves.david at ens-lyon.org
Sat Oct 15 13:13:06 CDT 2011


# HG changeset patch
# User Pierre-Yves David <pierre-yves.david at ens-lyon.org>
# Date 1318702352 -7200
# Node ID 7b44db3f6a54ae010c45584c79de034fd773c621
# Parent  f608548d5af3bec27d54ed650fc63646032368b1
rebase: add --rev option to rebase

This option allow a strict set of revision to be specified instead of using -s
or -b. Rebase will refuse start if striping  rebased changeset will strip non
rebased changeset. Rebase will refuse to work on set with multiple root.

diff --git a/hgext/rebase.py b/hgext/rebase.py
--- a/hgext/rebase.py
+++ b/hgext/rebase.py
@@ -15,7 +15,7 @@
 '''
 
 from mercurial import hg, util, repair, merge, cmdutil, commands, bookmarks
-from mercurial import extensions, patch
+from mercurial import extensions, patch, scmutil
 from mercurial.commands import templateopts
 from mercurial.node import nullrev
 from mercurial.lock import release
@@ -34,6 +34,9 @@
      _('rebase from the base of the specified changeset '
        '(up to greatest common ancestor of base and dest)'),
      _('REV')),
+    ('r', 'rev', [],
+     _('rebase this set of revision'),
+     _('REV')),
     ('d', 'dest', '',
      _('rebase onto the specified changeset'), _('REV')),
     ('', 'collapse', False, _('collapse the rebased changesets')),
@@ -119,6 +122,7 @@
         destf = opts.get('dest', None)
         srcf = opts.get('source', None)
         basef = opts.get('base', None)
+        revf = opts.get('rev', [])
         contf = opts.get('continue')
         abortf = opts.get('abort')
         collapsef = opts.get('collapse', False)
@@ -157,6 +161,12 @@
             if srcf and basef:
                 raise util.Abort(_('cannot specify both a '
                                    'revision and a base'))
+            if revf and basef:
+                raise util.Abort(_('cannot specify both a'
+                                   'revset and a base'))
+            if revf and srcf:
+                raise util.Abort(_('cannot specify both a'
+                                   'revset and a source revision'))
             if detachf:
                 if not srcf:
                     raise util.Abort(
@@ -173,14 +183,28 @@
             else:
                 dest = repo[destf]
 
+            rebaseset = None
             if srcf:
                 revsetargs = ('(%s)::', srcf)
+            elif revf:
+                rebaseset = scmutil.revrange(repo, revf)
+                if not keepf and rebaseset:
+                    try:
+                        repo.set('children(%ld) - %ld',
+                                 rebaseset, rebaseset).next()
+                    except StopIteration:
+                        pass # empty revset is what we look for
+                    else:
+                        msg = _("can't remove original changesets with"
+                                " unrebased descendants")
+                        hint = _('use --keep to keep original changesets')
+                        raise util.Abort(msg, hint=hint)
             else:
                 base = basef or '.'
                 revsetargs = ('(children(ancestor(%s, %d)) and ::(%s))::',
                              base, dest, base)
-
-            rebaseset = [c.rev() for c in repo.set(*revsetargs)]
+            if rebaseset is None:
+                rebaseset = [c.rev() for c in repo.set(*revsetargs)]
             if rebaseset:
                 result = buildstate(repo, dest, rebaseset, detachf)
             else:
diff --git a/tests/bundles/rebase-revset.hg b/tests/bundles/rebase-revset.hg
new file mode 100644
index 0000000000000000000000000000000000000000..2a016a38aab1229a00cbd1425942c67a02c9a98c
GIT binary patch
literal 1844
zc$@(=2g~?KM=>x$T4*^jL0KkKSxR?b#sC0pfB*mg+kgN6|NsC0|NsC0|NsBn|KH2^
z|9}7g{r~>%bG6U~yTcOz8rgu1l_4calTD~?A*ZO&$Qm>TnI52IXaEC7jR47@^)xg9
zXvw2PKmZJbK=e%j00w}`pa3xjhD{AJJwyNi8UO$Q007CL0B8UJ0002N8X6h^00000
z01X-d0001J4GbE9XbGbzQ&2PjGyv0000T#;Gy#)CMw$Qs(8-_ypa3)t0MktdgFqSp
z27mwopa4Js0iXZ?000b{00w{n0000C5uu@=00000007aT00006fY8AuNlz4do{v*O
zG-4VLQ$PdM$OAwCXa<4l13)x105Jg2&}aYv0000qXa<1OKnBzXgC>WV2j(-n-fu^i
z%kIwU+D?1g_p~#`FU^{3cc$aq<fb at XzbT&3d4QXhz`|#Mh7v|}1`;hGLq@={izXdT
zu)|icwGbj;p2$c&diFyg1uO|rAZqT~fd)gxI%WaWEWkRhKtk4H5CT&y2`E-ypV at 4X
z<$;&>i88`Kvupv33A|KE7J;$>y9a?3WLgZ#Fj$7-BiPW6!=`$XEg(~Al<0_vihQmv
zMa!9NNm6B&ty;Bfsd{~@u=L^C|2YET1Z#C%k<21sht|`^hYhNi`=V0LB6Lg&mH@>c
zRBlV^If|SrZva~y(TYrwo<qHWSj?)QnO^-UT=6Z at J$q&>ih`4EYpcMwV(oFl%u|#F
zG9TuRXW>^SX at x>~0?2|zfyh<Gt}}wU24M_|^6dOf2p;#~C*6v2Nvg`u3#^PoP%bxD
zTm6|R#;pbbX+`p_JG*&>Kv)n(#ymk8ZesssL1eDRjB8R6;F6%Uy$c1vsy8Gp#H14(
zcs+_cAjSo*BWIaI9w&<=T<e=Dqa~UEY;?UzRZ5UmW?&I37!U)Nbm~@xLZo+Mv?BuG
z3=oURWrr4CWU&z`ur1VqIX1>63!6o;j*6%tcD<wDFhPj!Fb6|W^D024VKBfwWQGMQ
z;T2v~XsxuuR=&cx&4R>MGp5LxG at p6`6$)8^v2vTF#)%v+AQbkkgz3bJg(|L75=yr5
zZY-H>PZ(PeliJxOhrx5Eb>TsfYZ((ILI#sWoT6qSl|{g!H}P(e8W5cieW(EmxFv at d
z9&A!h8z?YMn^JE at 54A_5n6{=B^u|=eOPpDAZd)@30s?HA$>5t;&aQ<i9S8|hlp at BZ
z5<280i8vHVvVbs+kHUE#f(w!z+{O}n*tG+-uVtM!zmd<FV>5T{y<y%V92hM)s at _Y!
z{SdwiBK{)6g5rHHIHBujf$S#HISGLyWwILPTc}mp`6*Jw#4XK^YUiT(RbE)DJa3vb
z)t>NL*S9ie9&;GRGWyoFukAKK4H1ZliyUE>Fzx{n5p0GSb%bIO!-ozWPlNg!H*Vd&
z^Sz}-ITe$47)^^px8V5{BzDhyi1d2DM9lAiDy_4qlfEIKHy2=AmDdxm$Yy^?!6t}t
zxJS<oA>YKsGy*srk?xT3F)0%IxUr5rET0cpRS^gg`6#B?)?`9*c<Ye0qYHw`?5I1L
ziflr0so`L9k<{|kPn<qv!V?)yt at hm0&2=cqOzkyEQ3*SqEv%hH22M{RTk}Q-F#QFx
zohc`#Ru$5Zk5|W|5KGh>N<m^(y4!sd3-OA{RRu8lDrE0^i9OkC6F_si#$yFa$GUYF
zJ{DM-MT*r5py~l+qm|2Dk<D}I92lM9THksJZ@{}}TjWA8WNAuSB)N@)tb^1~QxN#n
z>9t6E8=1f`$I1=#3OpUHns3^AvW%_Fb5tpKqHE2IQ+o8?`B=RxSQwocz%cTO&}ko^
z8xQboPIGcF)<6TO)%P%v1)>kNs`BFG<e!{n at 6598$HV-XB|(+2ugwR`WGcbtTSIUT
z!u9)`o>haSMM8<X68rRXm(?8<GsIIInlv_vMJgQ8*@(K at AlDY-(7@;*6k;b!bZDDT
zqO24z%!46Yc|%}taAM&_vvUtahFT(R95HKkEhEeB5S>GzK&d}3N|cG}Zk&%<aC1lf
z4>BOgl86|14rGI$^+eJ*NQVc!x5 at -ft3fzT3EaZN674d0O}4QV7H+#GSQGl3l5uw)
z(qWIvJ4lc=<&3BgK>~}xD@(f^l_mQZ&X7AV#BhD#p|Z at dPVUg*pG8njtTb7f?lI9e
zEA1RXgVp{y5O7TzLCS5>*+oDs^g)0yR0w#B!RpQi-o)baOzy%8CwebrSb32&F9|;_
z7kh|xNDpg8PN0K)qC-N6M$rTZK at 8{ufR`e+D7X&<_%kY|p(q9PI3?t^MxuB>%7|N0
zT*X5TtP4y(T@^z-Dv+c at Dx*jxSd$x0qx-^R;wa$H%$Tb_B_H6I#5yDCD1zEW6Ti^3
i%A$xhR+3w2FY&?9Mr7(gtnqHY;_gVN3K9uU>-fMt$~1)l

diff --git a/tests/test-rebase-scenario-global.t b/tests/test-rebase-scenario-global.t
--- a/tests/test-rebase-scenario-global.t
+++ b/tests/test-rebase-scenario-global.t
@@ -269,4 +269,240 @@
   |/
   o  0: 'A'
   
+  $ cd ..
 
+Test for revset
+
+We need a bit different graph
+All destination are B
+
+  $ hg init ah
+  $ cd ah
+  $ hg unbundle $TESTDIR/bundles/rebase-revset.hg
+  adding changesets
+  adding manifests
+  adding file changes
+  added 9 changesets with 9 changes to 9 files (+2 heads)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  $ hg tglog
+  o  8: 'I'
+  |
+  o  7: 'H'
+  |
+  o  6: 'G'
+  |
+  | o  5: 'F'
+  | |
+  | o  4: 'E'
+  |/
+  o  3: 'D'
+  |
+  o  2: 'C'
+  |
+  | o  1: 'B'
+  |/
+  o  0: 'A'
+  
+  $ cd ..
+
+
+Simple case with keep:
+
+Source on have two descendant heads but ask for one
+
+  $ hg clone -q -u . ah ah1
+  $ cd ah1
+  $ hg rebase -r '2::8' -d 1
+  abort: can't remove original changesets with unrebased descendants
+  (use --keep to keep original changesets)
+  [255]
+  $ hg rebase -r '2::8' -d 1 --keep
+  $ hg tglog
+  @  13: 'I'
+  |
+  o  12: 'H'
+  |
+  o  11: 'G'
+  |
+  o  10: 'D'
+  |
+  o  9: 'C'
+  |
+  | o  8: 'I'
+  | |
+  | o  7: 'H'
+  | |
+  | o  6: 'G'
+  | |
+  | | o  5: 'F'
+  | | |
+  | | o  4: 'E'
+  | |/
+  | o  3: 'D'
+  | |
+  | o  2: 'C'
+  | |
+  o |  1: 'B'
+  |/
+  o  0: 'A'
+  
+
+  $ cd ..
+
+Base on have one descendant heads we ask for but common ancestor have two
+
+  $ hg clone -q -u . ah ah2
+  $ cd ah2
+  $ hg rebase -r '3::8' -d 1
+  abort: can't remove original changesets with unrebased descendants
+  (use --keep to keep original changesets)
+  [255]
+  $ hg rebase -r '3::8' -d 1 --keep
+  $ hg tglog
+  @  12: 'I'
+  |
+  o  11: 'H'
+  |
+  o  10: 'G'
+  |
+  o    9: 'D'
+  |\
+  | | o  8: 'I'
+  | | |
+  | | o  7: 'H'
+  | | |
+  | | o  6: 'G'
+  | | |
+  | | | o  5: 'F'
+  | | | |
+  | | | o  4: 'E'
+  | | |/
+  | | o  3: 'D'
+  | |/
+  | o  2: 'C'
+  | |
+  o |  1: 'B'
+  |/
+  o  0: 'A'
+  
+
+  $ cd ..
+
+rebase subset
+
+  $ hg clone -q -u . ah ah3
+  $ cd ah3
+  $ hg rebase -r '3::7' -d 1
+  abort: can't remove original changesets with unrebased descendants
+  (use --keep to keep original changesets)
+  [255]
+  $ hg rebase -r '3::7' -d 1 --keep
+  $ hg tglog
+  @  11: 'H'
+  |
+  o  10: 'G'
+  |
+  o    9: 'D'
+  |\
+  | | o  8: 'I'
+  | | |
+  | | o  7: 'H'
+  | | |
+  | | o  6: 'G'
+  | | |
+  | | | o  5: 'F'
+  | | | |
+  | | | o  4: 'E'
+  | | |/
+  | | o  3: 'D'
+  | |/
+  | o  2: 'C'
+  | |
+  o |  1: 'B'
+  |/
+  o  0: 'A'
+  
+
+  $ cd ..
+
+rebase subset with multiple head
+
+  $ hg clone -q -u . ah ah4
+  $ cd ah4
+  $ hg rebase -r '3::(7+5)' -d 1
+  abort: can't remove original changesets with unrebased descendants
+  (use --keep to keep original changesets)
+  [255]
+  $ hg rebase -r '3::(7+5)' -d 1 --keep
+  $ hg tglog
+  @  13: 'H'
+  |
+  o  12: 'G'
+  |
+  | o  11: 'F'
+  | |
+  | o  10: 'E'
+  |/
+  o    9: 'D'
+  |\
+  | | o  8: 'I'
+  | | |
+  | | o  7: 'H'
+  | | |
+  | | o  6: 'G'
+  | | |
+  | | | o  5: 'F'
+  | | | |
+  | | | o  4: 'E'
+  | | |/
+  | | o  3: 'D'
+  | |/
+  | o  2: 'C'
+  | |
+  o |  1: 'B'
+  |/
+  o  0: 'A'
+  
+
+  $ cd ..
+
+More advanced tests
+
+rebase on ancestor with revset
+
+  $ hg clone -q -u . ah ah5
+  $ cd ah5
+  $ hg rebase -r '6::' -d 2
+  saved backup bundle to $TESTTMP/ah5/.hg/strip-backup/3d8a618087a7-backup.hg
+  $ hg tglog
+  @  8: 'I'
+  |
+  o  7: 'H'
+  |
+  o  6: 'G'
+  |
+  | o  5: 'F'
+  | |
+  | o  4: 'E'
+  | |
+  | o  3: 'D'
+  |/
+  o  2: 'C'
+  |
+  | o  1: 'B'
+  |/
+  o  0: 'A'
+  
+  $ cd ..
+
+
+rebase with multiple root.
+We rebase E and G on B
+We would expect heads are I, F if it was supported
+
+  $ hg clone -q -u . ah ah6
+  $ cd ah6
+  $ hg rebase -r '(4+6)::' -d 1
+  abort: Can't multiple roots
+  [255]
+  $ cd ..


More information about the Mercurial-devel mailing list