--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/drophack.py Wed Feb 12 17:18:50 2014 -0800
@@ -0,0 +1,163 @@
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+'''This extension add a hacky command to drop changeset during review
+
+This extension is intended as a temporary hack to allow Matt Mackall to use
+evolve in the Mercurial review it self. You should probably not use it if your
+name is not Matt Mackall.
+'''
+
+import os
+import time
+import contextlib
+
+from mercurial.i18n import _
+from mercurial import cmdutil
+from mercurial import repair
+from mercurial import scmutil
+from mercurial import lock as lockmod
+from mercurial import util
+from mercurial import commands
+
+cmdtable = {}
+command = cmdutil.command(cmdtable)
+
+
+@contextlib.contextmanager
+def timed(ui, caption):
+ ostart = os.times()
+ cstart = time.time()
+ yield
+ cstop = time.time()
+ ostop = os.times()
+ wall = cstop - cstart
+ user = ostop[0] - ostart[0]
+ sys = ostop[1] - ostart[1]
+ comb = user + sys
+ ui.write("%s: wall %f comb %f user %f sys %f\n"
+ % (caption, wall, comb, user, sys))
+
+def obsmarkerchainfrom(obsstore, nodes):
+ """return all marker chain starting from node
+
+ Starting from mean "use as successors"."""
+ # XXX need something smarter for descendant of bumped changeset
+ seennodes = set(nodes)
+ seenmarkers = set()
+ pendingnodes = set(nodes)
+ precursorsmarkers = obsstore.precursors
+ while pendingnodes:
+ current = pendingnodes.pop()
+ new = set()
+ for precmark in precursorsmarkers.get(current, ()):
+ if precmark in seenmarkers:
+ continue
+ seenmarkers.add(precmark)
+ new.add(precmark[0])
+ yield precmark
+ new -= seennodes
+ pendingnodes |= new
+
+def stripmarker(ui, repo, markers):
+ """remove <markers> from the repo obsstore
+
+ The old obsstore content is saved in a `obsstore.prestrip` file
+ """
+ repo = repo.unfiltered()
+ repo.destroying()
+ oldmarkers = list(repo.obsstore._all)
+ util.rename(repo.sjoin('obsstore'),
+ repo.join('obsstore.prestrip'))
+ del repo.obsstore # drop the cache
+ newstore = repo.obsstore
+ assert not newstore # should be empty after rename
+ tr = repo.transaction('drophack')
+ try:
+ for m in oldmarkers:
+ if m not in markers:
+ newstore.add(tr, [m])
+ tr.close()
+ finally:
+ tr.release()
+ repo.destroyed()
+
+
+@command('drop', [('r', 'rev', [], 'revision to update')], _('[-r] revs'))
+def cmddrop(ui, repo, *revs, **opts):
+ """I'm hacky do not use me!
+
+ This command strip a changeset, its precursors and all obsolescence marker
+ associated to its chain.
+
+ There is no way to limit the extend of the purge yet. You may have to
+ repull from other source to get some changeset and obsolescence marker
+ back.
+
+ This intended for Matt Mackall usage only. do not use me.
+ """
+ revs = list(revs)
+ revs.extend(opts['rev'])
+ if not revs:
+ revs = ['.']
+ # get the changeset
+ revs = scmutil.revrange(repo, revs)
+ if not revs:
+ ui.write_err('no revision to drop\n')
+ return 1
+ # lock from the beginning to prevent race
+ wlock = lock = None
+ try:
+ lock = repo.wlock()
+ lock = repo.lock()
+ # check they have no children
+ if repo.revs('%ld and public()', revs):
+ ui.write_err('cannot drop public revision')
+ return 1
+ if repo.revs('children(%ld) - %ld', revs, revs):
+ ui.write_err('cannot drop revision with children')
+ return 1
+ if repo.revs('. and %ld', revs):
+ newrevs = repo.revs('max(::. - %ld)', revs)
+ if newrevs:
+ assert len(newrevs) == 1
+ newrev = newrevs[0]
+ else:
+ newrev = -1
+ commands.update(ui, repo, newrev)
+ ui.status(_('working directory now at %s\n') % repo[newrev])
+ # get all markers and successors up to root
+ nodes = [repo[r].node() for r in revs]
+ with timed(ui, 'search obsmarker'):
+ markers = set(obsmarkerchainfrom(repo.obsstore, nodes))
+ ui.write('%i obsmarkers found\n' % len(markers))
+ cl = repo.unfiltered().changelog
+ with timed(ui, 'search nodes'):
+ allnodes = set(nodes)
+ allnodes.update(m[0] for m in markers if cl.hasnode(m[0]))
+ ui.write('%i nodes found\n' % len(allnodes))
+ cl = repo.changelog
+ visiblenodes = set(n for n in allnodes if cl.hasnode(n))
+ # check constraint again
+ if repo.revs('%ln and public()', visiblenodes):
+ ui.write_err('cannot drop public revision')
+ return 1
+ if repo.revs('children(%ln) - %ln', visiblenodes, visiblenodes):
+ ui.write_err('cannot drop revision with children')
+ return 1
+
+ if markers:
+ # strip them
+ with timed(ui, 'strip obsmarker'):
+ stripmarker(ui, repo, markers)
+ # strip the changeset
+ with timed(ui, 'strip nodes'):
+ repair.strip(ui, repo, allnodes, backup="all", topic='drophack')
+
+ finally:
+ lockmod.release(lock, wlock)
+
+ # rewrite the whole file.
+ # print data.
+ # - time to compute the chain
+ # - time to strip the changeset
+ # - time to strip the obs marker.