[PATCH 1 of 1] rebase: add a --rev option to rebase

Matt Mackall mpm at selenic.com
Sat Oct 15 09:24:16 CDT 2011


On Wed, 2011-10-12 at 23:42 +0200, Pierre-Yves David wrote:
> # HG changeset patch
> # User Pierre-Yves David <pierre-yves.david at ens-lyon.org>
> # Date 1318455664 -7200
> # Node ID 4fae86ab22887c43473df899dd27a93d9fadb247
> # Parent  b7a59f645f0e67bd424b0e9ceef1f8f6ffa7900d
> rebase: add a --rev option to rebase
> 
> This option allows to specify a strict subset of changeset to rebase.
> Rebase will fail if the revset contains ancestors of destination. Only
> changeset in the set will be rebased not their descendants. Using revset
> in rebase implies two new cases to handle:
> - rebasing revset with multiple head,
> - changeset rebased without they descendant and no --keep option.
> Later commit are dedicated to handle them properly.

This looks promising.

We probably need to warn about any changesets we would normally strip
but can't because they have unrebased ancestors.

> diff --git a/hg b/hg
> --- a/hg
> +++ b/hg
> @@ -36,3 +36,4 @@
>      mercurial.util.setbinary(fp)
>  
>  mercurial.dispatch.run()
> + 

???

> 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
> @@ -36,6 +36,8 @@
>       _('REV')),
>      ('d', 'dest', '',
>       _('rebase onto the specified changeset'), _('REV')),
> +    ('r', 'rev', [],
> +     _('rebase the specified revision only'), _('REV')),
>      ('', 'collapse', False, _('collapse the rebased changesets')),
>      ('m', 'message', '',
>       _('use text as collapse commit message'), _('TEXT')),
> @@ -119,6 +121,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)
> @@ -143,7 +146,7 @@
>                      _('cannot use collapse with continue or abort'))
>              if detachf:
>                  raise util.Abort(_('cannot use detach with continue or abort'))
> -            if srcf or basef or destf:
> +            if srcf or basef or destf or revf:
>                  raise util.Abort(
>                      _('abort and continue do not allow specifying revisions'))
>              if opts.get('tool', False):
> @@ -157,6 +160,12 @@
>              if srcf and basef:
>                  raise util.Abort(_('cannot specify both a '
>                                     'revision and a base'))

s/revision/source/

> +            if revf and basef:
> +                raise util.Abort(_('cannot specify both a'
> +                                   'revset and a base'))

s/revset/revision/

> +            if revf and srcf:
> +                raise util.Abort(_('cannot specify both a'
> +                                   'revset and a source revision'))
>              if detachf:
>                  if not srcf:
>                      raise util.Abort(
> @@ -164,8 +173,15 @@
>                  if basef:
>                      raise util.Abort(_('cannot specify a base with detach'))
>  
> +            if revf:
> +                revs = [int(c) for c in scmutil.revrange(repo, revf)]
> +                if not revs:
> +                    raise util.Abort( _('empty revset'))

'no matching revisions'

> +            else:
> +                revs = None
> +
>              cmdutil.bailifchanged(repo)
> -            result = buildstate(repo, destf, srcf, basef, detachf)
> +            result = buildstate(repo, destf, srcf, basef, detachf, revs)

So.. to do this right, we should convert the whole thing to revset-based
logic. That is, at the earliest point, we take all of our flags that
specify which changesets to rebase and calculate a revset from them.
Then only pass around that set, rather than track all the incompatible
flags and do further special-case logic on them. This will make the code
simpler than it was when you started.

So before we even get here, we should calculate the set for --source or
--base.. and get rid of those options in the rest of the code. The
buildstate function should only get revs.

>              if not result:
>                  # Empty state built, nothing to rebase
>                  ui.status(_('nothing to rebase\n'))
> @@ -507,7 +523,7 @@
>          repo.ui.warn(_('rebase aborted\n'))
>          return 0
>  
> -def buildstate(repo, dest, src, base, detach):
> +def buildstate(repo, dest, src, base, detach, revs=None):
>      'Define which revisions are going to be rebased and where'
>      targetancestors = set()
>      detachset = set()
> @@ -526,7 +542,9 @@
>                              [s.node for s in repo.mq.applied]):
>          raise util.Abort(_('cannot rebase onto an applied mq patch'))
>  
> -    if src:
> +    if src or revs:
> +        if not src:
> +            src = min(revs)

..and this whole clause goes away.

>          commonbase = repo[src].ancestor(repo[dest])
>          if commonbase == repo[src]:
>              raise util.Abort(_('source is ancestor of destination'))
> @@ -543,6 +561,7 @@
>              baseancestors = set(repo.changelog.ancestors(commonbase.rev()))
>              detachset = srcancestors - baseancestors
>              detachset.discard(commonbase.rev())
> +

?

>      else:
>          if base:
>              cwd = repo[base].rev()
> @@ -568,7 +587,9 @@
>          source = min(rebasingbranch)
>  
>      repo.ui.debug('rebase onto %d starting from %d\n' % (dest, source))
> -    state = dict.fromkeys(repo.changelog.descendants(source), nullrev)
> +    if revs is None:
> +        revs = repo.changelog.descendants(source)

And so does this.

> +    state = dict.fromkeys(revs, nullrev)
>      state.update(dict.fromkeys(detachset, nullmerge))
>      state[source] = nullrev
>      return repo['.'].rev(), repo[dest].rev(), state
> 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,212 @@
>    |/
>    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 --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 --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 --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 --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 test
> +
> +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'
> +  
> +
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel at selenic.com
> http://selenic.com/mailman/listinfo/mercurial-devel


-- 
Mathematics is the supreme nostalgia of our time.




More information about the Mercurial-devel mailing list