--- a/hgext/histedit.py Wed Jul 11 15:39:00 2012 -0700
+++ b/hgext/histedit.py Thu Jul 12 10:03:50 2012 +0200
@@ -4,10 +4,142 @@
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
-"""Interactive history editing.
+"""interactive history editing
+
+With this extension installed, Mercurial gains one new command: histedit. Usage
+is as follows, assuming the following history::
+
+ @ 3[tip] 7c2fd3b9020c 2009-04-27 18:04 -0500 durin42
+ | Add delta
+ |
+ o 2 030b686bedc4 2009-04-27 18:04 -0500 durin42
+ | Add gamma
+ |
+ o 1 c561b4e977df 2009-04-27 18:04 -0500 durin42
+ | Add beta
+ |
+ o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
+ Add alpha
+
+If you were to run ``hg histedit c561b4e977df``, you would see the following
+file open in your editor::
+
+ pick c561b4e977df Add beta
+ pick 030b686bedc4 Add gamma
+ pick 7c2fd3b9020c Add delta
+
+ # Edit history between 633536316234 and 7c2fd3b9020c
+ #
+ # Commands:
+ # p, pick = use commit
+ # e, edit = use commit, but stop for amending
+ # f, fold = use commit, but fold into previous commit
+ # d, drop = remove commit from history
+ # m, mess = edit message without changing commit content
+ #
+ 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+In this file, lines beginning with ``#`` are ignored. You must specify a rule
+for each revision in your history. For example, if you had meant to add gamma
+before beta, and then wanted to add delta in the same revision as beta, you
+would reorganize the file to look like this::
+
+ pick 030b686bedc4 Add gamma
+ pick c561b4e977df Add beta
+ fold 7c2fd3b9020c Add delta
+
+ # Edit history between 633536316234 and 7c2fd3b9020c
+ #
+ # Commands:
+ # p, pick = use commit
+ # e, edit = use commit, but stop for amending
+ # f, fold = use commit, but fold into previous commit
+ # d, drop = remove commit from history
+ # m, mess = edit message without changing commit content
+ #
+ 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+At which point you close the editor and ``histedit`` starts working. When you
+specify a ``fold`` operation, ``histedit`` will open an editor when it folds
+those revisions together, offering you a chance to clean up the commit message::
+
+ Add beta
+ ***
+ Add delta
-Inspired by git rebase --interactive.
+Edit the commit message to your liking, then close the editor. For
+this example, let's assume that the commit message was changed to
+``Add beta and delta.`` After histedit has run and had a chance to
+remove any old or temporary revisions it needed, the history looks
+like this::
+
+ @ 2[tip] 989b4d060121 2009-04-27 18:04 -0500 durin42
+ | Add beta and delta.
+ |
+ o 1 081603921c3f 2009-04-27 18:04 -0500 durin42
+ | Add gamma
+ |
+ o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
+ Add alpha
+
+Note that ``histedit`` does *not* remove any revisions (even its own temporary
+ones) until after it has completed all the editing operations, so it will
+probably perform several strip operations when it's done. For the above example,
+it had to run strip twice. Strip can be slow depending on a variety of factors,
+so you might need to be a little patient. You can choose to keep the original
+revisions by passing the ``--keep`` flag.
+
+The ``edit`` operation will drop you back to a command prompt,
+allowing you to edit files freely, or even use ``hg record`` to commit
+some changes as a separate commit. When you're done, any remaining
+uncommitted changes will be committed as well. When done, run ``hg
+histedit --continue`` to finish this step. You'll be prompted for a
+new commit message, but the default commit message will be the
+original message for the ``edit`` ed revision.
+
+The ``message`` operation will give you a chance to revise a commit
+message without changing the contents. It's a shortcut for doing
+``edit`` immediately followed by `hg histedit --continue``.
+
+If ``histedit`` encounters a conflict when moving a revision (while
+handling ``pick`` or ``fold``), it'll stop in a similar manner to
+``edit`` with the difference that it won't prompt you for a commit
+message when done. If you decide at this point that you don't like how
+much work it will be to rearrange history, or that you made a mistake,
+you can use ``hg histedit --abort`` to abandon the new changes you
+have made and return to the state before you attempted to edit your
+history.
+
+If we clone the example repository above and add three more changes, such that
+we have the following history::
+
+ @ 6[tip] 038383181893 2009-04-27 18:04 -0500 stefan
+ | Add theta
+ |
+ o 5 140988835471 2009-04-27 18:04 -0500 stefan
+ | Add eta
+ |
+ o 4 122930637314 2009-04-27 18:04 -0500 stefan
+ | Add zeta
+ |
+ o 3 836302820282 2009-04-27 18:04 -0500 stefan
+ | Add epsilon
+ |
+ o 2 989b4d060121 2009-04-27 18:04 -0500 durin42
+ | Add beta and delta.
+ |
+ o 1 081603921c3f 2009-04-27 18:04 -0500 durin42
+ | Add gamma
+ |
+ o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
+ Add alpha
+
+If you run ``hg histedit --outgoing`` on the clone then it is the same
+as running ``hg histedit 836302820282``. If you need plan to push to a
+repository that Mercurial does not detect to be related to the source
+repo, you can add a ``--force`` option.
"""
+
try:
import cPickle as pickle
except ImportError:
@@ -243,7 +375,7 @@
'mess': message,
}
def histedit(ui, repo, *parent, **opts):
- """hg histedit <parent>
+ """interactively edit changeset history
"""
# TODO only abort if we try and histedit mq patches, not just
# blanket if mq patches are applied somewhere
@@ -307,7 +439,11 @@
new = repo.commit(text=message, user=oldctx.user(),
date=oldctx.date(), extra=oldctx.extra())
- if action in ('f', 'fold'):
+ # If we're resuming a fold and we have new changes, mark the
+ # replacements and finish the fold. If not, it's more like a
+ # drop of the changesets that disappeared, and we can skip
+ # this step.
+ if action in ('f', 'fold') and (new or newchildren):
if new:
tmpnodes.append(new)
else:
@@ -396,13 +532,11 @@
(parentctx, created_, replaced_, tmpnodes_) = actiontable[action](
ui, repo, parentctx, ha, opts)
- hexshort = lambda x: node.hex(x)[:12]
-
if replaced_:
clen, rlen = len(created_), len(replaced_)
if clen == rlen == 1:
ui.debug('histedit: exact replacement of %s with %s\n' % (
- hexshort(replaced_[0]), hexshort(created_[0])))
+ node.short(replaced_[0]), node.short(created_[0])))
replacemap[replaced_[0]] = created_[0]
elif clen > rlen:
@@ -412,7 +546,7 @@
# TODO synthesize patch names for created patches
replacemap[replaced_[0]] = created_[-1]
ui.debug('histedit: created many, assuming %s replaced by %s' %
- (hexshort(replaced_[0]), hexshort(created_[-1])))
+ (node.short(replaced_[0]), node.short(created_[-1])))
elif rlen > clen:
if not created_:
# This must be a drop. Try and put our metadata on
@@ -420,7 +554,7 @@
assert rlen == 1
r = replaced_[0]
ui.debug('histedit: %s seems replaced with nothing, '
- 'finding a parent\n' % (hexshort(r)))
+ 'finding a parent\n' % (node.short(r)))
pctx = repo[r].parents()[0]
if pctx.node() in replacemap:
ui.debug('histedit: parent is already replaced\n')
@@ -428,12 +562,12 @@
else:
replacemap[r] = pctx.node()
ui.debug('histedit: %s best replaced by %s\n' % (
- hexshort(r), hexshort(replacemap[r])))
+ node.short(r), node.short(replacemap[r])))
else:
assert len(created_) == 1
for r in replaced_:
ui.debug('histedit: %s replaced by %s\n' % (
- hexshort(r), hexshort(created_[0])))
+ node.short(r), node.short(created_[0])))
replacemap[r] = created_[0]
else:
assert False, (
@@ -456,8 +590,8 @@
return
while new in replacemap:
new = replacemap[new]
- ui.note(_('histedit: %s to %s\n') % (hexshort(old),
- hexshort(new)))
+ ui.note(_('histedit: %s to %s\n') % (node.short(old),
+ node.short(new)))
octx = repo[old]
marks = octx.bookmarks()
if marks:
@@ -559,6 +693,6 @@
'force outgoing even for unrelated repositories')),
('r', 'rev', [], _('first revision to be edited')),
],
- __doc__,
+ _("[PARENT]"),
),
}
--- a/hgext/largefiles/basestore.py Wed Jul 11 15:39:00 2012 -0700
+++ b/hgext/largefiles/basestore.py Thu Jul 12 10:03:50 2012 +0200
@@ -48,8 +48,8 @@
'''Put source file into the store under <filename>/<hash>.'''
raise NotImplementedError('abstract method')
- def exists(self, hash):
- '''Check to see if the store contains the given hash.'''
+ def exists(self, hashes):
+ '''Check to see if the store contains the given hashes.'''
raise NotImplementedError('abstract method')
def get(self, files):
--- a/hgext/largefiles/lfcommands.py Wed Jul 11 15:39:00 2012 -0700
+++ b/hgext/largefiles/lfcommands.py Thu Jul 12 10:03:50 2012 +0200
@@ -340,7 +340,11 @@
store = basestore._openstore(rsrc, rdst, put=True)
at = 0
- files = filter(lambda h: not store.exists(h), files)
+ ui.debug("sending statlfile command for %d largefiles\n" % len(files))
+ retval = store.exists(files)
+ files = filter(lambda h: not retval[h], files)
+ ui.debug("%d largefiles need to be uploaded\n" % len(files))
+
for hash in files:
ui.progress(_('uploading largefiles'), at, unit='largefile',
total=len(files))
--- a/hgext/largefiles/proto.py Wed Jul 11 15:39:00 2012 -0700
+++ b/hgext/largefiles/proto.py Thu Jul 12 10:03:50 2012 +0200
@@ -7,6 +7,7 @@
import urllib2
from mercurial import error, httprepo, util, wireproto
+from mercurial.wireproto import batchable, future
from mercurial.i18n import _
import lfutil
@@ -119,15 +120,19 @@
length))
return (length, stream)
+ @batchable
def statlfile(self, sha):
+ f = future()
+ result = {'sha': sha}
+ yield result, f
try:
- return int(self._call("statlfile", sha=sha))
+ yield int(f.value)
except (ValueError, urllib2.HTTPError):
# If the server returns anything but an integer followed by a
# newline, newline, it's not speaking our language; if we get
# an HTTP error, we can't be sure the largefile is present;
# either way, consider it missing.
- return 2
+ yield 2
repo.__class__ = lfileswirerepository
--- a/hgext/largefiles/remotestore.py Wed Jul 11 15:39:00 2012 -0700
+++ b/hgext/largefiles/remotestore.py Thu Jul 12 10:03:50 2012 +0200
@@ -10,6 +10,7 @@
from mercurial import util
from mercurial.i18n import _
+from mercurial.wireproto import remotebatch
import lfutil
import basestore
@@ -20,8 +21,6 @@
super(remotestore, self).__init__(ui, repo, url)
def put(self, source, hash):
- if self._verify(hash):
- return
if self.sendfile(source, hash):
raise util.Abort(
_('remotestore: could not put %s to remote store %s')
@@ -29,8 +28,8 @@
self.ui.debug(
_('remotestore: put %s to remote store %s') % (source, self.url))
- def exists(self, hash):
- return self._verify(hash)
+ def exists(self, hashes):
+ return self._verify(hashes)
def sendfile(self, filename, hash):
self.ui.debug('remotestore: sendfile(%s, %s)\n' % (filename, hash))
@@ -74,8 +73,8 @@
infile = lfutil.limitreader(infile, length)
return lfutil.copyandhash(lfutil.blockstream(infile), tmpfile)
- def _verify(self, hash):
- return not self._stat(hash)
+ def _verify(self, hashes):
+ return self._stat(hashes)
def _verifyfile(self, cctx, cset, contents, standin, verified):
filename = lfutil.splitstandin(standin)
@@ -104,3 +103,8 @@
else:
raise RuntimeError('verify failed: unexpected response from '
'statlfile (%r)' % stat)
+
+ def batch(self):
+ '''Support for remote batching.'''
+ return remotebatch(self)
+
--- a/hgext/largefiles/wirestore.py Wed Jul 11 15:39:00 2012 -0700
+++ b/hgext/largefiles/wirestore.py Thu Jul 12 10:03:50 2012 +0200
@@ -25,5 +25,13 @@
def _get(self, hash):
return self.remote.getlfile(hash)
- def _stat(self, hash):
- return self.remote.statlfile(hash)
+ def _stat(self, hashes):
+ batch = self.remote.batch()
+ futures = {}
+ for hash in hashes:
+ futures[hash] = batch.statlfile(hash)
+ batch.submit()
+ retval = {}
+ for hash in hashes:
+ retval[hash] = not futures[hash].value
+ return retval
--- a/mercurial/commands.py Wed Jul 11 15:39:00 2012 -0700
+++ b/mercurial/commands.py Thu Jul 12 10:03:50 2012 +0200
@@ -2061,17 +2061,22 @@
succs = tuple(bin(succ) for succ in successors)
l = repo.lock()
try:
- repo.obsstore.create(bin(precursor), succs, 0, metadata)
+ tr = repo.transaction('debugobsolete')
+ try:
+ repo.obsstore.create(tr, bin(precursor), succs, 0, metadata)
+ tr.close()
+ finally:
+ tr.release()
finally:
l.release()
else:
- for mctx in obsolete.allmarkers(repo):
- ui.write(hex(mctx.precnode()))
- for repl in mctx.succnodes():
+ for m in obsolete.allmarkers(repo):
+ ui.write(hex(m.precnode()))
+ for repl in m.succnodes():
ui.write(' ')
ui.write(hex(repl))
- ui.write(' %X ' % mctx._data[2])
- ui.write(mctx.metadata())
+ ui.write(' %X ' % m._data[2])
+ ui.write(m.metadata())
ui.write('\n')
@command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
--- a/mercurial/localrepo.py Wed Jul 11 15:39:00 2012 -0700
+++ b/mercurial/localrepo.py Thu Jul 12 10:03:50 2012 +0200
@@ -197,10 +197,7 @@
@storecache('obsstore')
def obsstore(self):
- store = obsolete.obsstore()
- data = self.sopener.tryread('obsstore')
- if data:
- store.loadmarkers(data)
+ store = obsolete.obsstore(self.sopener)
return store
@storecache('00changelog.i')
@@ -994,16 +991,6 @@
self.store.write()
if '_phasecache' in vars(self):
self._phasecache.write()
- if 'obsstore' in vars(self) and self.obsstore._new:
- # XXX: transaction logic should be used here. But for
- # now rewriting the whole file is good enough.
- f = self.sopener('obsstore', 'wb', atomictemp=True)
- try:
- self.obsstore.flushmarkers(f)
- f.close()
- except: # re-raises
- f.discard()
- raise
for k, ce in self._filecache.items():
if k == 'dirstate':
continue
@@ -1622,6 +1609,10 @@
return r
def pull(self, remote, heads=None, force=False):
+ # don't open transaction for nothing or you break future useful
+ # rollback call
+ tr = None
+ trname = 'pull\n' + util.hidepassword(remote.url())
lock = self.lock()
try:
tmp = discovery.findcommonincoming(self, remote, heads=heads,
@@ -1632,6 +1623,7 @@
added = []
result = 0
else:
+ tr = self.transaction(trname)
if heads is None and list(common) == [nullid]:
self.ui.status(_("requesting all changes\n"))
elif heads is None and remote.capable('changegroupsubset'):
@@ -1680,9 +1672,15 @@
remoteobs = remote.listkeys('obsolete')
if 'dump' in remoteobs:
+ if tr is None:
+ tr = self.transaction(trname)
data = base85.b85decode(remoteobs['dump'])
- self.obsstore.mergemarkers(data)
+ self.obsstore.mergemarkers(tr, data)
+ if tr is not None:
+ tr.close()
finally:
+ if tr is not None:
+ tr.release()
lock.release()
return result
@@ -1823,9 +1821,8 @@
self.ui.warn(_('updating %s to public failed!\n')
% newremotehead)
if 'obsolete' in self.listkeys('namespaces') and self.obsstore:
- data = self.obsstore._writemarkers()
- r = remote.pushkey('obsolete', 'dump', '',
- base85.b85encode(data))
+ data = self.listkeys('obsolete')['dump']
+ r = remote.pushkey('obsolete', 'dump', '', data)
if not r:
self.ui.warn(_('failed to push obsolete markers!\n'))
finally:
--- a/mercurial/obsolete.py Wed Jul 11 15:39:00 2012 -0700
+++ b/mercurial/obsolete.py Thu Jul 12 10:03:50 2012 +0200
@@ -156,12 +156,16 @@
- successors: new -> set(old)
"""
- def __init__(self):
+ def __init__(self, sopener):
self._all = []
# new markers to serialize
- self._new = []
self.precursors = {}
self.successors = {}
+ self.sopener = sopener
+ data = sopener.tryread('obsstore')
+ if data:
+ for marker in _readmarkers(data):
+ self._load(marker)
def __iter__(self):
return iter(self._all)
@@ -169,7 +173,7 @@
def __nonzero__(self):
return bool(self._all)
- def create(self, prec, succs=(), flag=0, metadata=None):
+ def create(self, transaction, prec, succs=(), flag=0, metadata=None):
"""obsolete: add a new obsolete marker
* ensuring it is hashable
@@ -184,33 +188,33 @@
if len(succ) != 20:
raise ValueError(succ)
marker = (str(prec), tuple(succs), int(flag), encodemeta(metadata))
- self.add(marker)
-
- def add(self, marker):
- """Add a new marker to the store
+ self.add(transaction, marker)
- This marker still needs to be written to disk"""
- self._new.append(marker)
- self._load(marker)
-
- def loadmarkers(self, data):
- """Load all markers in data, mark them as known."""
- for marker in _readmarkers(data):
+ def add(self, transaction, marker):
+ """Add a new marker to the store"""
+ if marker not in self._all:
+ f = self.sopener('obsstore', 'ab')
+ try:
+ offset = f.tell()
+ transaction.add('obsstore', offset)
+ if offset == 0:
+ # new file add version header
+ f.write(_pack('>B', _fmversion))
+ _writemarkers(f.write, [marker])
+ finally:
+ # XXX: f.close() == filecache invalidation == obsstore rebuilt.
+ # call 'filecacheentry.refresh()' here
+ f.close()
self._load(marker)
- def mergemarkers(self, data):
- other = set(_readmarkers(data))
+ def mergemarkers(self, transation, data):
+ other = _readmarkers(data)
local = set(self._all)
- new = other - local
+ new = [m for m in other if m not in local]
for marker in new:
- self.add(marker)
-
- def flushmarkers(self, stream):
- """Write all markers to a stream
-
- After this operation, "new" markers are considered "known"."""
- self._writemarkers(stream)
- self._new[:] = []
+ # XXX: N marker == N x (open, write, close)
+ # we should write them all at once
+ self.add(transation, marker)
def _load(self, marker):
self._all.append(marker)
@@ -219,32 +223,25 @@
for suc in sucs:
self.successors.setdefault(suc, set()).add(marker)
- def _writemarkers(self, stream=None):
- # Kept separate from flushmarkers(), it will be reused for
- # markers exchange.
- if stream is None:
- final = []
- w = final.append
- else:
- w = stream.write
- w(_pack('>B', _fmversion))
- for marker in self._all:
- pre, sucs, flags, metadata = marker
- nbsuc = len(sucs)
- format = _fmfixed + (_fmnode * nbsuc)
- data = [nbsuc, len(metadata), flags, pre]
- data.extend(sucs)
- w(_pack(format, *data))
- w(metadata)
- if stream is None:
- return ''.join(final)
+def _writemarkers(write, markers):
+ # Kept separate from flushmarkers(), it will be reused for
+ # markers exchange.
+ for marker in markers:
+ pre, sucs, flags, metadata = marker
+ nbsuc = len(sucs)
+ format = _fmfixed + (_fmnode * nbsuc)
+ data = [nbsuc, len(metadata), flags, pre]
+ data.extend(sucs)
+ write(_pack(format, *data))
+ write(metadata)
def listmarkers(repo):
"""List markers over pushkey"""
if not repo.obsstore:
return {}
- data = repo.obsstore._writemarkers()
- return {'dump': base85.b85encode(data)}
+ data = [_pack('>B', _fmversion)]
+ _writemarkers(data.append, repo.obsstore)
+ return {'dump': base85.b85encode(''.join(data))}
def pushmarker(repo, key, old, new):
"""Push markers over pushkey"""
@@ -257,8 +254,13 @@
data = base85.b85decode(new)
lock = repo.lock()
try:
- repo.obsstore.mergemarkers(data)
- return 1
+ tr = repo.transaction('pushkey: obsolete markers')
+ try:
+ repo.obsstore.mergemarkers(tr, data)
+ tr.close()
+ return 1
+ finally:
+ tr.release()
finally:
lock.release()
--- a/mercurial/revlog.py Wed Jul 11 15:39:00 2012 -0700
+++ b/mercurial/revlog.py Thu Jul 12 10:03:50 2012 +0200
@@ -75,35 +75,6 @@
s.update(text)
return s.digest()
-def compress(text):
- """ generate a possibly-compressed representation of text """
- if not text:
- return ("", text)
- l = len(text)
- bin = None
- if l < 44:
- pass
- elif l > 1000000:
- # zlib makes an internal copy, thus doubling memory usage for
- # large files, so lets do this in pieces
- z = zlib.compressobj()
- p = []
- pos = 0
- while pos < l:
- pos2 = pos + 2**20
- p.append(z.compress(text[pos:pos2]))
- pos = pos2
- p.append(z.flush())
- if sum(map(len, p)) < l:
- bin = "".join(p)
- else:
- bin = _compress(text)
- if bin is None or len(bin) > l:
- if text[0] == '\0':
- return ("", text)
- return ('u', text)
- return ("", bin)
-
def decompress(bin):
""" decompress the given input """
if not bin:
@@ -1017,6 +988,35 @@
dfh.close()
ifh.close()
+ def compress(self, text):
+ """ generate a possibly-compressed representation of text """
+ if not text:
+ return ("", text)
+ l = len(text)
+ bin = None
+ if l < 44:
+ pass
+ elif l > 1000000:
+ # zlib makes an internal copy, thus doubling memory usage for
+ # large files, so lets do this in pieces
+ z = zlib.compressobj()
+ p = []
+ pos = 0
+ while pos < l:
+ pos2 = pos + 2**20
+ p.append(z.compress(text[pos:pos2]))
+ pos = pos2
+ p.append(z.flush())
+ if sum(map(len, p)) < l:
+ bin = "".join(p)
+ else:
+ bin = _compress(text)
+ if bin is None or len(bin) > l:
+ if text[0] == '\0':
+ return ("", text)
+ return ('u', text)
+ return ("", bin)
+
def _addrevision(self, node, text, transaction, link, p1, p2,
cachedelta, ifh, dfh):
"""internal function to add revisions to the log
@@ -1049,7 +1049,7 @@
t = buildtext()
ptext = self.revision(self.node(rev))
delta = mdiff.textdiff(ptext, t)
- data = compress(delta)
+ data = self.compress(delta)
l = len(data[1]) + len(data[0])
if basecache[0] == rev:
chainbase = basecache[1]
@@ -1094,7 +1094,7 @@
textlen = len(text)
if d is None or dist > textlen * 2:
text = buildtext()
- data = compress(text)
+ data = self.compress(text)
l = len(data[1]) + len(data[0])
base = chainbase = curr
--- a/tests/test-histedit-fold.t Wed Jul 11 15:39:00 2012 -0700
+++ b/tests/test-histedit-fold.t Thu Jul 12 10:03:50 2012 +0200
@@ -108,3 +108,78 @@
f
$ cd ..
+
+folding and creating no new change doesn't break:
+ $ mkdir fold-to-empty-test
+ $ cd fold-to-empty-test
+ $ hg init
+ $ printf "1\n2\n3\n" > file
+ $ hg add file
+ $ hg commit -m '1+2+3'
+ $ echo 4 >> file
+ $ hg commit -m '+4'
+ $ echo 5 >> file
+ $ hg commit -m '+5'
+ $ echo 6 >> file
+ $ hg commit -m '+6'
+ $ hg log --graph
+ @ changeset: 3:251d831eeec5
+ | tag: tip
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: +6
+ |
+ o changeset: 2:888f9082bf99
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: +5
+ |
+ o changeset: 1:617f94f13c0f
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: +4
+ |
+ o changeset: 0:0189ba417d34
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: 1+2+3
+
+
+ $ cat > editor.py <<EOF
+ > import re, sys
+ > rules = sys.argv[1]
+ > data = open(rules).read()
+ > data = re.sub(r'pick ([0-9a-f]{12} 2 \+5)', r'drop \1', data)
+ > data = re.sub(r'pick ([0-9a-f]{12} 2 \+6)', r'fold \1', data)
+ > open(rules, 'w').write(data)
+ > EOF
+
+ $ HGEDITOR='python editor.py' hg histedit 1
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ patching file file
+ Hunk #1 FAILED at 2
+ 1 out of 1 hunks FAILED -- saving rejects to file file.rej
+ abort: Fix up the change and run hg histedit --continue
+ [255]
+There were conflicts, but we'll continue without resolving. This
+should effectively drop the changes from +6.
+ $ hg status
+ ? editor.py
+ ? file.rej
+ $ hg histedit --continue
+ 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ saved backup bundle to $TESTTMP/*-backup.hg (glob)
+ $ hg log --graph
+ @ changeset: 1:617f94f13c0f
+ | tag: tip
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: +4
+ |
+ o changeset: 0:0189ba417d34
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: 1+2+3
+
+
+ $ cd ..
--- a/tests/test-obsolete.t Wed Jul 11 15:39:00 2012 -0700
+++ b/tests/test-obsolete.t Thu Jul 12 10:03:50 2012 +0200
@@ -158,9 +158,28 @@
added 6 changesets with 6 changes to 6 files (+3 heads)
(run 'hg heads' to see heads, 'hg merge' to merge)
$ hg debugobsolete
+ 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
+ cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
+ 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
+
+Rollback//Transaction support
+
+ $ hg debugobsolete -d '1340 0' aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+ $ hg debugobsolete
+ 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
+ ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
+ 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 0 {'date': '1340 0', 'user': 'test'}
+ $ hg rollback -n
+ repository tip rolled back to revision 5 (undo debugobsolete)
+ $ hg rollback
+ repository tip rolled back to revision 5 (undo debugobsolete)
+ $ hg debugobsolete
245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
+ cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
+ ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
$ cd ..
@@ -176,9 +195,9 @@
adding file changes
added 6 changesets with 6 changes to 6 files (+3 heads)
$ hg -R tmpd debugobsolete
- ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
+ 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
- 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
+ ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
@@ -200,9 +219,9 @@
(run 'hg heads' to see heads, 'hg merge' to merge)
$ hg debugobsolete
2448244824482448244824482448244824482448 1339133913391339133913391339133913391339 0 {'date': '1339 0', 'user': 'test'}
- ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
+ 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
- 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
+ ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
On push
@@ -213,8 +232,8 @@
no changes found
[1]
$ hg -R ../tmpc debugobsolete
- ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
+ 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
- 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
+ ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
2448244824482448244824482448244824482448 1339133913391339133913391339133913391339 0 {'date': '1339 0', 'user': 'test'}