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