[PATCH] rebase: don't rebase obsolete commits with no successor

Laurent Charignon lcharignon at fb.com
Wed Nov 18 19:07:35 CST 2015


> On Nov 18, 2015, at 1:46 PM, Laurent Charignon <lcharignon at fb.com> wrote:
> 
> # HG changeset patch
> # User Laurent Charignon <lcharignon at fb.com>
> # Date 1447883069 28800
> #      Wed Nov 18 13:44:29 2015 -0800
> # Node ID aa8771a01f32c86b4f9df8c45386bfe880a4e9dd
> # Parent  2da6a2dbfc42bdec4bcaf47da947c64ff959a59c
> rebase: don't rebase obsolete commits with no successor
> 
> This patch avoids unnecessary conflicts to resolve during rebase for the users
> of changeset evolution.
> 
> This patch modifies rebase to skip obsolete commits with no successor.
> It introduces a new rebase state 'revpruned' for these revisions that are
> being skipped and a new message to inform the user of what is happening.
> This feature is gated behind the config flag experimental.rebaseskipobsolete
> 
> When an obsolete commit is skipped, the output is:
> note: not rebasing 7:360bbaa7d3ce "O", it has no successor
> 
> diff --git a/hgext/rebase.py b/hgext/rebase.py
> --- a/hgext/rebase.py
> +++ b/hgext/rebase.py
> @@ -32,6 +32,7 @@
> revignored = -3
> # To do with obsolescence
> revprecursor = -4
> +revpruned = -5
> 
> cmdtable = {}
> command = cmdutil.command(cmdtable)
> @@ -487,6 +488,9 @@
>                              targetctx.description().split('\n', 1)[0])
>                 msg = _('note: not rebasing %s, already in destination as %s\n')
>                 ui.status(msg % (desc, desctarget))
> +            elif state[rev] == revpruned:
> +                msg = _('note: not rebasing %s, it has no successor\n')
> +                ui.status(msg % desc)
>             else:
>                 ui.status(_('already rebased %s as %s\n') %
>                           (desc, repo[state[rev]]))
> @@ -676,7 +680,7 @@
>     elif p1n in state:
>         if state[p1n] == nullmerge:
>             p1 = target
> -        elif state[p1n] in (revignored, revprecursor):
> +        elif state[p1n] in (revignored, revprecursor, revpruned):
>             p1 = nearestrebased(repo, p1n, state)
>             if p1 is None:
>                 p1 = target
> @@ -692,7 +696,7 @@
>         if p2n in state:
>             if p1 == target: # p1n in targetancestors or external
>                 p1 = state[p2n]
> -            elif state[p2n] in (revignored, revprecursor):
> +            elif state[p2n] in (revignored, revprecursor, revpruned):
>                 p2 = nearestrebased(repo, p2n, state)
>                 if p2 is None:
>                     # no ancestors rebased yet, detach
> @@ -882,7 +886,7 @@
>             else:
>                 oldrev, newrev = l.split(':')
>                 if newrev in (str(nullmerge), str(revignored),
> -                              str(revprecursor)):
> +                              str(revprecursor), str(revpruned)):
>                     state[repo[oldrev].rev()] = int(newrev)
>                 elif newrev == nullid:
>                     state[repo[oldrev].rev()] = revtodo
> @@ -1066,7 +1070,10 @@
>         for ignored in set(rebasedomain) - set(rebaseset):
>             state[ignored] = revignored
>     for r in obsoletenotrebased:
> -        state[r] = revprecursor
> +        if obsoletenotrebased[r] is None:
> +            state[r] = revpruned
> +        else:
> +            state[r] = revprecursor
>     return repo['.'].rev(), dest.rev(), state
> 
> def clearrebased(ui, repo, state, skipped, collapsedas=None):
> @@ -1177,7 +1184,9 @@
> 
> def _computeobsoletenotrebased(repo, rebasesetrevs, dest):
>     """return a mapping obsolete => successor for all obsolete nodes to be
> -    rebased that have a successors in the destination"""
> +    rebased that have a successors in the destination
> +
> +    obsolete => None entries in the mapping indicate nodes with no succesor"""
>     obsoletenotrebased = {}
> 
>     # Build a mapping successor => obsolete nodes for the obsolete
> @@ -1203,6 +1212,11 @@
>         for s in allsuccessors:
>             if s in ancs:
>                 obsoletenotrebased[allsuccessors[s]] = s
> +            elif (s == allsuccessors[s] and
> +                  allsuccessors.values().count(s) == 1):
> +                # plain prune
> +                obsoletenotrebased[s] = None
> +
>     return obsoletenotrebased
> 
> def summaryhook(ui, repo):
> diff --git a/tests/test-rebase-obsolete.t b/tests/test-rebase-obsolete.t
> --- a/tests/test-rebase-obsolete.t
> +++ b/tests/test-rebase-obsolete.t
> @@ -659,3 +659,31 @@
>   $ hg rebase -d 'desc(B2)'
>   note: not rebasing 1:a8b11f55fb19 "B0", already in destination as 2:261e70097290 "B2"
>   rebasing 5:1a79b7535141 "D" (tip)
> +  $ hg up 4
> +  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
> +  $ echo "O" > O
> +  $ hg add O
> +  $ hg commit -m O
> +  $ echo "P" > P
> +  $ hg add P
> +  $ hg commit -m P
> +  $ hg log -G
> +  @  8:8d47583e023f P
> +  |
> +  o  7:360bbaa7d3ce O
> +  |
> +  | o  6:9c48361117de D
> +  | |
> +  o |  4:ff2c4d47b71d C
> +  |/
> +  o  2:261e70097290 B2
> +  |
> +  o  0:4a2df7238c3b A
> +  
> +  $ hg prune 7 --config experimental.evolution=all --config extensions.evolve=

Looking back on this patch, this does not seem right isn't it?
It introduces a dependency on evolve to run this test for one of the extension of core.
Do we have a way to do a plain prune with what is in core?

> +  1 changesets pruned
> +  1 new unstable changesets
> +  $ hg rebase -d 6 -r "4::"
> +  rebasing 4:ff2c4d47b71d "C"
> +  note: not rebasing 7:360bbaa7d3ce "O", it has no successor
> +  rebasing 8:8d47583e023f "P" (tip)
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel at selenic.com
> https://selenic.com/mailman/listinfo/mercurial-devel


Thanks,

Laurent



More information about the Mercurial-devel mailing list