Mercurial > evolve
comparison hgext/obsolete.py @ 282:05ab164c6593
obsolete: handle rebase --collapse
Recording obsolete in concludenode() did not work for --collapse because
only the first revision was passed to the call. The new strategy is to
track the rebase state in defineparents() and concludenode() and to
create markers only after a successful non-abort call. In theory, this
should also fix --continue/--abort cases.
The change in test-stabilize-order.t comes from concludenode() no longer
creating obsolete marker. stabilize command was actually duplicating
markers, once in concludenode(), once explicitely.
author | Patrick Mezard <patrick@mezard.eu> |
---|---|
date | Tue, 19 Jun 2012 18:05:23 +0200 |
parents | 0d87b1fbf32b |
children | 691cb55358b0 |
comparison
equal
deleted
inserted
replaced
281:258169d3428b | 282:05ab164c6593 |
---|---|
223 def buildstate(orig, repo, dest, rebaseset, *ags, **kws): | 223 def buildstate(orig, repo, dest, rebaseset, *ags, **kws): |
224 """wrapper for rebase 's buildstate that exclude obsolete changeset""" | 224 """wrapper for rebase 's buildstate that exclude obsolete changeset""" |
225 rebaseset = repo.revs('%ld - extinct()', rebaseset) | 225 rebaseset = repo.revs('%ld - extinct()', rebaseset) |
226 return orig(repo, dest, rebaseset, *ags, **kws) | 226 return orig(repo, dest, rebaseset, *ags, **kws) |
227 | 227 |
228 | 228 def defineparents(orig, repo, rev, target, state, *args, **kwargs): |
229 def concludenode(orig, repo, rev, *args, **kwargs): | 229 rebasestate = getattr(repo, '_rebasestate', None) |
230 if rebasestate is not None: | |
231 repo._rebasestate = dict(state) | |
232 repo._rebasetarget = target | |
233 return orig(repo, rev, target, state, *args, **kwargs) | |
234 | |
235 def concludenode(orig, repo, rev, p1, *args, **kwargs): | |
230 """wrapper for rebase 's concludenode that set obsolete relation""" | 236 """wrapper for rebase 's concludenode that set obsolete relation""" |
231 newrev = orig(repo, rev, *args, **kwargs) | 237 newrev = orig(repo, rev, p1, *args, **kwargs) |
232 oldnode = repo[rev].node() | 238 rebasestate = getattr(repo, '_rebasestate', None) |
233 if newrev is not None: | 239 if rebasestate is not None: |
234 newnode = repo[newrev].node() | 240 if newrev is not None: |
235 else: | 241 nrev = repo[newrev].rev() |
236 # Revision was emptied and removed, there is no successor. | 242 else: |
237 newnode = nullid | 243 nrev = p1 |
238 repo.addobsolete(newnode, oldnode) | 244 repo._rebasestate[rev] = nrev |
239 return newrev | 245 return newrev |
240 | 246 |
241 def cmdrebase(orig, ui, repo, *args, **kwargs): | 247 def cmdrebase(orig, ui, repo, *args, **kwargs): |
242 if kwargs.get('keep', False): | 248 if kwargs.get('keep', False): |
243 raise util.Abort(_('rebase --keep option is unsupported with obsolete ' | 249 raise util.Abort(_('rebase --keep option is unsupported with obsolete ' |
244 'extension'), hint=_("see 'hg help obsolete'")) | 250 'extension'), hint=_("see 'hg help obsolete'")) |
245 kwargs = dict(kwargs) | 251 kwargs = dict(kwargs) |
246 kwargs['keep'] = True | 252 kwargs['keep'] = True |
247 return orig(ui, repo, *args, **kwargs) | 253 |
248 | 254 # We want to mark rebased revision as obsolete and set their |
255 # replacements if any. Doing it in concludenode() prevents | |
256 # aborting the rebase, and is not called with all relevant | |
257 # revisions in --collapse case. Instead, we try to track the | |
258 # rebase state structure by sampling/updating it in | |
259 # defineparents() and concludenode(). The obsolete markers are | |
260 # added from this state after a successful call. | |
261 repo._rebasestate = {} | |
262 repo._rebasetarget = None | |
263 maxrev = len(repo) - 1 | |
264 try: | |
265 res = orig(ui, repo, *args, **kwargs) | |
266 if not res and not kwargs.get('abort') and repo._rebasetarget: | |
267 # We have to tell rewritten revisions from removed | |
268 # ones. When collapsing, removed revisions are considered | |
269 # to be collapsed onto the final one, while in the normal | |
270 # case their are marked obsolete without successor. | |
271 emptynode = nullid | |
272 if kwargs.get('collapse'): | |
273 emptynode = repo[max(repo._rebasestate.values())].node() | |
274 # Rebased revisions are assumed to be descendants of | |
275 # targetrev. If a source revision is mapped to targetrev | |
276 # or to another rebased revision, it must have been | |
277 # removed. | |
278 targetrev = repo[repo._rebasetarget].rev() | |
279 newrevs = set([targetrev]) | |
280 for rev, newrev in sorted(repo._rebasestate.items()): | |
281 oldnode = repo[rev].node() | |
282 if newrev not in newrevs and newrev >= 0: | |
283 newnode = repo[newrev].node() | |
284 newrevs.add(newrev) | |
285 else: | |
286 newnode = emptynode | |
287 repo.addobsolete(newnode, oldnode) | |
288 return res | |
289 finally: | |
290 delattr(repo, '_rebasestate') | |
291 delattr(repo, '_rebasetarget') | |
249 | 292 |
250 | 293 |
251 def extsetup(ui): | 294 def extsetup(ui): |
252 | 295 |
253 revset.symbols["obsolete"] = revsetobsolete | 296 revset.symbols["obsolete"] = revsetobsolete |
260 | 303 |
261 try: | 304 try: |
262 rebase = extensions.find('rebase') | 305 rebase = extensions.find('rebase') |
263 if rebase: | 306 if rebase: |
264 extensions.wrapfunction(rebase, 'buildstate', buildstate) | 307 extensions.wrapfunction(rebase, 'buildstate', buildstate) |
308 extensions.wrapfunction(rebase, 'defineparents', defineparents) | |
265 extensions.wrapfunction(rebase, 'concludenode', concludenode) | 309 extensions.wrapfunction(rebase, 'concludenode', concludenode) |
266 extensions.wrapcommand(rebase.cmdtable, "rebase", cmdrebase) | 310 extensions.wrapcommand(rebase.cmdtable, "rebase", cmdrebase) |
267 except KeyError: | 311 except KeyError: |
268 pass # rebase not found | 312 pass # rebase not found |
269 | 313 |