[PATCH 5 of 8] histedit: fold in memory
pierre-yves.david at logilab.fr
pierre-yves.david at logilab.fr
Fri Sep 21 12:28:28 CDT 2012
# HG changeset patch
# User Pierre-Yves David <pierre-yves.david at logilab.fr>
# Date 1348248271 -7200
# Node ID 11bf8e9d0ddb77fc4bdfce8f2db648e2f2495571
# Parent 6923d7fab598e16f1c2d15faeeb67b8360759a4f
histedit: fold in memory
Update the folding code to works in memory instead of applying patches on the
working directory. This is cleaner, faster and prepare the removal of the whole
patching logic.
This new collapse function will probably move into core sooner or later. A lot
of other rewriting operation may benefit from it.
diff --git a/hgext/histedit.py b/hgext/histedit.py
--- a/hgext/histedit.py
+++ b/hgext/histedit.py
@@ -147,10 +147,12 @@ import os
from mercurial import bookmarks
from mercurial import cmdutil
from mercurial import discovery
from mercurial import error
+from mercurial import copies
+from mercurial import context
from mercurial import hg
from mercurial import lock as lockmod
from mercurial import node
from mercurial import patch
from mercurial import repair
@@ -193,10 +195,80 @@ def foldchanges(ui, repo, node1, node2,
patch.patch(ui, repo, patchfile, files=files, eolmode=None)
finally:
os.unlink(patchfile)
return files
+def collapse(repo, first, last, commitopts):
+ """collapse the set of revisions from first to last as new one.
+
+ Expected commit options are:
+ - message
+ - date
+ - username
+ Edition of commit message is trigered in all case.
+
+ This function works in memory."""
+ ctxs = list(repo.set('%d::%d', first, last))
+ if not ctxs:
+ return None
+ base = first.parents()[0]
+
+ # commit a new version of the old changeset, including the update
+ # collect all files which might be affected
+ files = set()
+ for ctx in ctxs:
+ files.update(ctx.files())
+
+ # Recompute copies (avoid recording a -> b -> a)
+ copied = copies.pathcopies(first, last)
+
+ # prune files which were reverted by the updates
+ def samefile(f):
+ if f in last.manifest():
+ a = last.filectx(f)
+ if f in base.manifest():
+ b = base.filectx(f)
+ return (a.data() == b.data()
+ and a.flags() == b.flags())
+ else:
+ return False
+ else:
+ return f not in base.manifest()
+ files = [f for f in files if not samefile(f)]
+ # commit version of these files as defined by head
+ headmf = last.manifest()
+ def filectxfn(repo, ctx, path):
+ if path in headmf:
+ fctx = last[path]
+ flags = fctx.flags()
+ mctx = context.memfilectx(fctx.path(), fctx.data(),
+ islink='l' in flags,
+ isexec='x' in flags,
+ copied=copied.get(path))
+ return mctx
+ raise IOError()
+
+ if commitopts.get('message'):
+ message = commitopts['message']
+ else:
+ message = first.description()
+ user = commitopts.get('user')
+ date = commitopts.get('date')
+ extra = first.extra()
+
+ parents = (first.p1().node(), first.p2().node())
+ new = context.memctx(repo,
+ parents=parents,
+ text=message,
+ files=files,
+ filectxfn=filectxfn,
+ user=user,
+ date=date,
+ extra=extra)
+ new._text = cmdutil.commitforceeditor(repo, new, [])
+ return repo.commitctx(new)
+
def pick(ui, repo, ctx, ha, opts):
oldctx = repo[ha]
if oldctx.parents()[0] == ctx:
ui.debug('node %s unchanged\n' % ha)
return oldctx, [], [], []
@@ -243,23 +315,30 @@ def fold(ui, repo, ctx, ha, opts):
return finishfold(ui, repo, ctx, oldctx, n, opts, [])
def finishfold(ui, repo, ctx, oldctx, newnode, opts, internalchanges):
parent = ctx.parents()[0].node()
hg.update(repo, parent)
- foldchanges(ui, repo, parent, newnode, opts)
+ ### prepare new commit data
+ commitopts = opts.copy()
+ # username
+ if ctx.user() == oldctx.user():
+ username = ctx.user()
+ else:
+ username = ui.username()
+ commitopts['user'] = username
+ # commit message
newmessage = '\n***\n'.join(
[ctx.description()] +
[repo[r].description() for r in internalchanges] +
[oldctx.description()]) + '\n'
- # If the changesets are from the same author, keep it.
- if ctx.user() == oldctx.user():
- username = ctx.user()
- else:
- username = ui.username()
- newmessage = ui.edit(newmessage, username)
- n = repo.commit(text=newmessage, user=username,
- date=max(ctx.date(), oldctx.date()), extra=oldctx.extra())
+ commitopts['message'] = newmessage
+ # date
+ commitopts['date'] = max(ctx.date(), oldctx.date())
+ n = collapse(repo, ctx, repo[newnode], commitopts)
+ if n is None:
+ return ctx, [], [], []
+ hg.update(repo, n)
return repo[n], [n], [oldctx.node(), ctx.node()], [newnode]
def drop(ui, repo, ctx, ha, opts):
return ctx, [], [repo[ha].node()], []
diff --git a/tests/test-histedit-fold-non-commute.t b/tests/test-histedit-fold-non-commute.t
--- a/tests/test-histedit-fold-non-commute.t
+++ b/tests/test-histedit-fold-non-commute.t
@@ -90,10 +90,21 @@ fix up
***
does not commute with e
+ HG: Enter commit message. Lines beginning with 'HG:' are removed.
+ HG: Leave message empty to abort commit.
+ HG: --
+ HG: user: test
+ HG: branch 'default'
+ HG: changed d
+ HG: changed e
+
+
+
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
0 files updated, 0 files merged, 0 files removed, 0 files unresolved
file e already exists
1 out of 1 hunks FAILED -- saving rejects to file e.rej
abort: Fix up the change and run hg histedit --continue
diff --git a/tests/test-histedit-fold.t b/tests/test-histedit-fold.t
--- a/tests/test-histedit-fold.t
+++ b/tests/test-histedit-fold.t
@@ -64,10 +64,11 @@ edit the history
$ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle
0 files updated, 0 files merged, 4 files removed, 0 files unresolved
0 files updated, 0 files merged, 0 files removed, 0 files unresolved
0 files updated, 0 files merged, 0 files removed, 0 files unresolved
0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
0 files updated, 0 files merged, 0 files removed, 0 files unresolved
0 files updated, 0 files merged, 0 files removed, 0 files unresolved
log after edit
$ hg log --graph
@@ -230,9 +231,19 @@ dropped revision.
+4
***
+5.2
***
+6
+
+
+
+ HG: Enter commit message. Lines beginning with 'HG:' are removed.
+ HG: Leave message empty to abort commit.
+ HG: --
+ HG: user: test
+ HG: branch 'default'
+ HG: changed file
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
0 files updated, 0 files merged, 0 files removed, 0 files unresolved
saved backup bundle to $TESTTMP/fold-with-dropped/.hg/strip-backup/617f94f13c0f-backup.hg (glob)
$ cd ..
More information about the Mercurial-devel
mailing list