drophack: add a new drop hack extension for Matt Mackall usage
This extension is hacky and not intended for real world usage. See documentation
for details.
--- /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.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-drop.t Wed Feb 12 17:18:50 2014 -0800
@@ -0,0 +1,267 @@
+
+ $ cat >> $HGRCPATH <<EOF
+ > [extensions]
+ > hgext.rebase=
+ > hgext.graphlog=
+ > EOF
+ $ echo "drophack=$(echo $(dirname $TESTDIR))/hgext/drophack.py" >> $HGRCPATH
+ $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext/evolve.py" >> $HGRCPATH
+ $ mkcommit() {
+ > echo "$1" > "$1"
+ > hg add "$1"
+ > hg ci -m "add $1"
+ > }
+ $ summary() {
+ > echo ============ graph ==============
+ > hg log -G
+ > echo ============ hidden =============
+ > hg log --hidden -G
+ > echo ============ obsmark ============
+ > hg debugobsolete
+ > }
+
+
+ $ hg init repo
+ $ cd repo
+ $ mkcommit base
+
+drop a single changeset without any rewrite
+================================================
+
+
+ $ mkcommit simple-single
+ $ summary
+ ============ graph ==============
+ @ changeset: 1:d4e7845543ff
+ | tag: tip
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add simple-single
+ |
+ o changeset: 0:b4952fcf48cf
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: add base
+
+ ============ hidden =============
+ @ changeset: 1:d4e7845543ff
+ | tag: tip
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add simple-single
+ |
+ o changeset: 0:b4952fcf48cf
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: add base
+
+ ============ obsmark ============
+ $ hg drop .
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ working directory now at b4952fcf48cf
+ search obsmarker: wall * comb * user * sys * (glob)
+ 0 obsmarkers found
+ search nodes: wall * comb * user * sys * (glob)
+ 1 nodes found
+ saved backup bundle to $TESTTMP/repo/.hg/strip-backup/d4e7845543ff-drophack.hg
+ strip nodes: wall * comb * user * sys * (glob)
+ $ summary
+ ============ graph ==============
+ @ changeset: 0:b4952fcf48cf
+ tag: tip
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: add base
+
+ ============ hidden =============
+ @ changeset: 0:b4952fcf48cf
+ tag: tip
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: add base
+
+ ============ obsmark ============
+
+Try to drop a changeset with children
+================================================
+
+ $ mkcommit parent
+ $ mkcommit child
+ $ summary
+ ============ graph ==============
+ @ changeset: 2:34b6c051bf1f
+ | tag: tip
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add child
+ |
+ o changeset: 1:19509a42b0d0
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add parent
+ |
+ o changeset: 0:b4952fcf48cf
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: add base
+
+ ============ hidden =============
+ @ changeset: 2:34b6c051bf1f
+ | tag: tip
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add child
+ |
+ o changeset: 1:19509a42b0d0
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add parent
+ |
+ o changeset: 0:b4952fcf48cf
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: add base
+
+ ============ obsmark ============
+ $ hg drop 1
+ cannot drop revision with children (no-eol)
+ [1]
+ $ summary
+ ============ graph ==============
+ @ changeset: 2:34b6c051bf1f
+ | tag: tip
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add child
+ |
+ o changeset: 1:19509a42b0d0
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add parent
+ |
+ o changeset: 0:b4952fcf48cf
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: add base
+
+ ============ hidden =============
+ @ changeset: 2:34b6c051bf1f
+ | tag: tip
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add child
+ |
+ o changeset: 1:19509a42b0d0
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add parent
+ |
+ o changeset: 0:b4952fcf48cf
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: add base
+
+ ============ obsmark ============
+
+Try to drop a public changeset
+================================================
+
+ $ hg phase --public 2
+ $ hg drop 2
+ cannot drop public revision (no-eol)
+ [1]
+
+
+Try to drop a changeset with rewrite
+================================================
+
+ $ hg phase --force --draft 2
+ $ echo babar >> child
+ $ hg commit --amend
+ $ summary
+ ============ graph ==============
+ @ changeset: 4:a2c06c884bfe
+ | tag: tip
+ | parent: 1:19509a42b0d0
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add child
+ |
+ o changeset: 1:19509a42b0d0
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add parent
+ |
+ o changeset: 0:b4952fcf48cf
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: add base
+
+ ============ hidden =============
+ @ changeset: 4:a2c06c884bfe
+ | tag: tip
+ | parent: 1:19509a42b0d0
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add child
+ |
+ | x changeset: 3:87ea30a976fd
+ | | user: test
+ | | date: Thu Jan 01 00:00:00 1970 +0000
+ | | summary: temporary amend commit for 34b6c051bf1f
+ | |
+ | x changeset: 2:34b6c051bf1f
+ |/ user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add child
+ |
+ o changeset: 1:19509a42b0d0
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add parent
+ |
+ o changeset: 0:b4952fcf48cf
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: add base
+
+ ============ obsmark ============
+ 34b6c051bf1f78db6aef400776de5cb964470207 a2c06c884bfe53d3840026248bd8a7eafa152df8 0 {'date': '* *', 'user': 'test'} (glob)
+ 87ea30a976fdf235bf096f04899cb02a903873e2 0 {'date': '* *', 'user': 'test'} (glob)
+ $ hg drop .
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ working directory now at 19509a42b0d0
+ search obsmarker: wall * comb * user * sys * (glob)
+ 1 obsmarkers found
+ search nodes: wall * comb * user * sys * (glob)
+ 2 nodes found
+ strip obsmarker: wall * comb * user * sys * (glob)
+ saved backup bundle to $TESTTMP/repo/.hg/strip-backup/a2c06c884bfe-drophack.hg (glob)
+ strip nodes: wall * comb * user * sys * (glob)
+ $ summary
+ ============ graph ==============
+ @ changeset: 1:19509a42b0d0
+ | tag: tip
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add parent
+ |
+ o changeset: 0:b4952fcf48cf
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: add base
+
+ ============ hidden =============
+ @ changeset: 1:19509a42b0d0
+ | tag: tip
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add parent
+ |
+ o changeset: 0:b4952fcf48cf
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: add base
+
+ ============ obsmark ============
+ 87ea30a976fdf235bf096f04899cb02a903873e2 0 {'date': '* *', 'user': 'test'} (glob)