comparison hgext/rebase.py @ 46801:32399d0813e0

rebase: skip obsolete commits even if they have pruned successors Issue 5782 reported that `hg rebase -r <obsolete commit with pruned successor>` failed with an error saying that it would cause divergence. Commit b7e2cf114e85 (rebase: do not consider extincts for divergence detection (issue5782), 2018-02-09) fixed it by letting you rebase the commit. However, that fix seems inconsistent with how we handle `hg rebase -r <pruned commit>`. To me, it should make no difference whether a commit is pruned itself or if it has (only) pruned successors. This patch changes it so we treat these two kinds of commits the same way. I let the message we print remain "note: not rebasing <commit>, it has no successor" even though that last part is not technically correct for commits with pruned successors. I doubt it will confuse users. Differential Revision: https://phab.mercurial-scm.org/D10240
author Martin von Zweigbergk <martinvonz@google.com>
date Fri, 19 Mar 2021 12:08:46 -0700
parents 7ed7b13fc00a
children 7d80622fc212
comparison
equal deleted inserted replaced
46800:186c0f6fbc16 46801:32399d0813e0
359 return 359 return
360 obsoleteset = set(obsoleterevs) 360 obsoleteset = set(obsoleterevs)
361 ( 361 (
362 self.obsoletenotrebased, 362 self.obsoletenotrebased,
363 self.obsoletewithoutsuccessorindestination, 363 self.obsoletewithoutsuccessorindestination,
364 obsoleteextinctsuccessors,
365 ) = _computeobsoletenotrebased(self.repo, obsoleteset, destmap) 364 ) = _computeobsoletenotrebased(self.repo, obsoleteset, destmap)
366 skippedset = set(self.obsoletenotrebased) 365 skippedset = set(self.obsoletenotrebased)
367 skippedset.update(self.obsoletewithoutsuccessorindestination) 366 skippedset.update(self.obsoletewithoutsuccessorindestination)
368 skippedset.update(obsoleteextinctsuccessors)
369 _checkobsrebase(self.repo, self.ui, obsoleteset, skippedset) 367 _checkobsrebase(self.repo, self.ui, obsoleteset, skippedset)
370 368
371 def _prepareabortorcontinue( 369 def _prepareabortorcontinue(
372 self, isabort, backup=True, suppwarns=False, dryrun=False, confirm=False 370 self, isabort, backup=True, suppwarns=False, dryrun=False, confirm=False
373 ): 371 ):
2190 `obsoletenotrebased` is a mapping mapping obsolete => successor for all 2188 `obsoletenotrebased` is a mapping mapping obsolete => successor for all
2191 obsolete nodes to be rebased given in `rebaseobsrevs`. 2189 obsolete nodes to be rebased given in `rebaseobsrevs`.
2192 2190
2193 `obsoletewithoutsuccessorindestination` is a set with obsolete revisions 2191 `obsoletewithoutsuccessorindestination` is a set with obsolete revisions
2194 without a successor in destination. 2192 without a successor in destination.
2195
2196 `obsoleteextinctsuccessors` is a set of obsolete revisions with only
2197 obsolete successors.
2198 """ 2193 """
2199 obsoletenotrebased = {} 2194 obsoletenotrebased = {}
2200 obsoletewithoutsuccessorindestination = set() 2195 obsoletewithoutsuccessorindestination = set()
2201 obsoleteextinctsuccessors = set()
2202 2196
2203 assert repo.filtername is None 2197 assert repo.filtername is None
2204 cl = repo.changelog 2198 cl = repo.changelog
2205 get_rev = cl.index.get_rev 2199 get_rev = cl.index.get_rev
2206 extinctrevs = set(repo.revs(b'extinct()')) 2200 extinctrevs = set(repo.revs(b'extinct()'))
2210 successors = set(obsutil.allsuccessors(repo.obsstore, [srcnode])) 2204 successors = set(obsutil.allsuccessors(repo.obsstore, [srcnode]))
2211 # obsutil.allsuccessors includes node itself 2205 # obsutil.allsuccessors includes node itself
2212 successors.remove(srcnode) 2206 successors.remove(srcnode)
2213 succrevs = {get_rev(s) for s in successors} 2207 succrevs = {get_rev(s) for s in successors}
2214 succrevs.discard(None) 2208 succrevs.discard(None)
2215 if succrevs.issubset(extinctrevs): 2209 if not successors or succrevs.issubset(extinctrevs):
2216 # all successors are extinct 2210 # no successor, or all successors are extinct
2217 obsoleteextinctsuccessors.add(srcrev)
2218 if not successors:
2219 # no successor
2220 obsoletenotrebased[srcrev] = None 2211 obsoletenotrebased[srcrev] = None
2221 else: 2212 else:
2222 dstrev = destmap[srcrev] 2213 dstrev = destmap[srcrev]
2223 for succrev in succrevs: 2214 for succrev in succrevs:
2224 if cl.isancestorrev(succrev, dstrev): 2215 if cl.isancestorrev(succrev, dstrev):
2229 # destination (which would be catched above), we shall skip it 2220 # destination (which would be catched above), we shall skip it
2230 # and its descendants to avoid divergence. 2221 # and its descendants to avoid divergence.
2231 if srcrev in extinctrevs or any(s in destmap for s in succrevs): 2222 if srcrev in extinctrevs or any(s in destmap for s in succrevs):
2232 obsoletewithoutsuccessorindestination.add(srcrev) 2223 obsoletewithoutsuccessorindestination.add(srcrev)
2233 2224
2234 return ( 2225 return obsoletenotrebased, obsoletewithoutsuccessorindestination
2235 obsoletenotrebased,
2236 obsoletewithoutsuccessorindestination,
2237 obsoleteextinctsuccessors,
2238 )
2239 2226
2240 2227
2241 def abortrebase(ui, repo): 2228 def abortrebase(ui, repo):
2242 with repo.wlock(), repo.lock(): 2229 with repo.wlock(), repo.lock():
2243 rbsrt = rebaseruntime(repo, ui) 2230 rbsrt = rebaseruntime(repo, ui)