[PATCH 8 of 8] histedit: replaces patching logic by merges
pierre-yves.david at logilab.fr
pierre-yves.david at logilab.fr
Fri Sep 21 12:28:31 CDT 2012
# HG changeset patch
# User Pierre-Yves David <pierre-yves.david at ens-lyon.org>
# Date 1348248442 -7200
# Node ID 79ee8c918550c9491fd89081becd4b30e7e1cbfe
# Parent bdf1a024374d676d8626f441f49ec84a81fe56d7
histedit: replaces patching logic by merges
The old and fragile patching logic is replaced by smart merges (as rebase and
graft do). This should prevents some conflicts and smoother human resolution.
For this purpose the "foldchanges" function is renamed to "applychanges" and
handle a single revision only.
diff --git a/hgext/histedit.py b/hgext/histedit.py
--- a/hgext/histedit.py
+++ b/hgext/histedit.py
@@ -140,11 +140,10 @@ repo, you can add a ``--force`` option.
try:
import cPickle as pickle
except ImportError:
import pickle
-import tempfile
import os
from mercurial import bookmarks
from mercurial import cmdutil
from mercurial import discovery
@@ -152,14 +151,14 @@ 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
from mercurial import scmutil
from mercurial import util
+from mercurial import merge as mergemod
from mercurial.i18n import _
cmdtable = {}
command = cmdutil.command(cmdtable)
@@ -175,29 +174,31 @@ editcomment = _("""# Edit history betwee
# d, drop = remove commit from history
# m, mess = edit message without changing commit content
#
""")
-def foldchanges(ui, repo, node1, node2, opts):
- """Produce a new changeset that represents the diff from node1 to node2."""
- try:
- fd, patchfile = tempfile.mkstemp(prefix='hg-histedit-')
- fp = os.fdopen(fd, 'w')
- diffopts = patch.diffopts(ui, opts)
- diffopts.git = True
- diffopts.ignorews = False
- diffopts.ignorewsamount = False
- diffopts.ignoreblanklines = False
- gen = patch.diff(repo, node1, node2, opts=diffopts)
- for chunk in gen:
- fp.write(chunk)
- fp.close()
- files = set()
- patch.patch(ui, repo, patchfile, files=files, eolmode=None)
- finally:
- os.unlink(patchfile)
- return files
+def applychanges(ui, repo, ctx, opts):
+ """Merge changeset from ctx (only) in the current working directory"""
+ wcpar = repo.dirstate.parents()[0]
+ if ctx.p1().node() == wcpar:
+ # edition ar "in place" we do not need to make any merge,
+ # just applies changes on parent for edition
+ cmdutil.revert(ui, repo, ctx, (wcpar, node.nullid), all=True)
+ stats = None
+ else:
+ try:
+ # ui.forcemerge is an internal variable, do not document
+ repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
+ stats = mergemod.update(repo, ctx.node(), True, True, False,
+ ctx.p1().node())
+ finally:
+ repo.ui.setconfig('ui', 'forcemerge', '')
+ repo.setparents(wcpar, node.nullid)
+ repo.dirstate.write()
+ # fix up dirstate for copies and renames
+ cmdutil.duplicatecopies(repo, ctx.rev(), ctx.p1().rev())
+ return stats
def collapse(repo, first, last, commitopts):
"""collapse the set of revisions from first to last as new one.
Expected commit options are:
@@ -271,49 +272,45 @@ def pick(ui, repo, ctx, ha, opts):
oldctx = repo[ha]
if oldctx.parents()[0] == ctx:
ui.debug('node %s unchanged\n' % ha)
return oldctx, [], [], []
hg.update(repo, ctx.node())
- try:
- files = foldchanges(ui, repo, oldctx.p1().node() , ha, opts)
- if not files:
- ui.warn(_('%s: empty changeset')
- % node.hex(ha))
- return ctx, [], [], []
- except Exception:
+ stats = applychanges(ui, repo, oldctx, opts)
+ if stats and stats[3] > 0:
raise util.Abort(_('Fix up the change and run '
'hg histedit --continue'))
+ # drop the second merge parent
n = repo.commit(text=oldctx.description(), user=oldctx.user(),
date=oldctx.date(), extra=oldctx.extra())
+ if n is None:
+ ui.warn(_('%s: empty changeset\n')
+ % node.hex(ha))
+ return ctx, [], [], []
return repo[n], [n], [oldctx.node()], []
def edit(ui, repo, ctx, ha, opts):
oldctx = repo[ha]
hg.update(repo, ctx.node())
- try:
- foldchanges(ui, repo, oldctx.p1().node() , ha, opts)
- except Exception:
- pass
+ applychanges(ui, repo, oldctx, opts)
raise util.Abort(_('Make changes as needed, you may commit or record as '
'needed now.\nWhen you are finished, run hg'
' histedit --continue to resume.'))
def fold(ui, repo, ctx, ha, opts):
oldctx = repo[ha]
hg.update(repo, ctx.node())
- try:
- files = foldchanges(ui, repo, oldctx.p1().node() , ha, opts)
- if not files:
- ui.warn(_('%s: empty changeset')
- % node.hex(ha))
- return ctx, [], [], []
- except Exception:
+ stats = applychanges(ui, repo, oldctx, opts)
+ if stats and stats[3] > 0:
raise util.Abort(_('Fix up the change and run '
'hg histedit --continue'))
n = repo.commit(text='fold-temp-revision %s' % ha, user=oldctx.user(),
date=oldctx.date(), extra=oldctx.extra())
+ if n is None:
+ ui.warn(_('%s: empty changeset')
+ % node.hex(ha))
+ return ctx, [], [], []
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)
@@ -344,13 +341,12 @@ def drop(ui, repo, ctx, ha, opts):
def message(ui, repo, ctx, ha, opts):
oldctx = repo[ha]
hg.update(repo, ctx.node())
- try:
- foldchanges(ui, repo, oldctx.p1().node() , ha, opts)
- except Exception:
+ stats = applychanges(ui, repo, oldctx, opts)
+ if stats and stats[3] > 0:
raise util.Abort(_('Fix up the change and run '
'hg histedit --continue'))
message = oldctx.description() + '\n'
message = ui.edit(message, ui.username())
new = repo.commit(text=message, user=oldctx.user(), date=oldctx.date(),
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
@@ -86,17 +86,18 @@ log before edit
edit the history
$ HGEDITOR="cat \"$EDITED\" > " hg histedit 3 2>&1 | fixbundle
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
- patching file e
- Hunk #1 FAILED at 0
- 1 out of 1 hunks FAILED -- saving rejects to file e.rej
+ merging e
+ warning: conflicts during merge.
+ merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
abort: Fix up the change and run hg histedit --continue
fix up
- $ echo a > e
+ $ echo 'I can haz no commute' > e
+ $ hg resolve --mark e
$ cat > cat.py <<EOF
> import sys
> print open(sys.argv[1]).read()
> print
> print
@@ -119,29 +120,31 @@ fix up
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
0 files updated, 0 files merged, 0 files removed, 0 files unresolved
- patching file e
- Hunk #1 FAILED at 0
- 1 out of 1 hunks FAILED -- saving rejects to file e.rej
+ merging e
+ warning: conflicts during merge.
+ merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
abort: Fix up the change and run hg histedit --continue
just continue this time
+ $ hg revert -r 'p1()' e
+ $ hg resolve --mark e
$ hg histedit --continue 2>&1 | fixbundle
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
- @ changeset: 5:45bd04206744
+ @ changeset: 5:2696a654c663
| tag: tip
| user: test
| date: Thu Jan 01 00:00:00 1970 +0000
| summary: f
|
- o changeset: 4:abff6367c13a
+ o changeset: 4:ec2c1cf833a8
| user: test
| date: Thu Jan 01 00:00:00 1970 +0000
| summary: d
|
o changeset: 3:65a9a84f33fd
@@ -165,11 +168,11 @@ log after edit
summary: Initial commit
contents of e
$ hg cat e
- a
+ I can haz no commute
manifest
$ hg manifest
a
b
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
@@ -155,20 +155,25 @@ folding and creating no new change doesn
> 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
+ merging file
+ warning: conflicts during merge.
+ merging file incomplete! (edit conflicts, then use 'hg resolve --mark')
abort: Fix up the change and run hg histedit --continue
[255]
-There were conflicts, but we'll continue without resolving. This
+There were conflicts, we keep P1 content. This
should effectively drop the changes from +6.
$ hg status
+ M file
? editor.py
- ? file.rej
+ ? file.orig
+ $ hg resolve -l
+ U file
+ $ hg revert -r 'p1()' file
+ $ hg resolve --mark file
$ 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
@@ -215,16 +220,23 @@ dropped revision.
> drop 888f9082bf99 2 +5
> fold 251d831eeec5 3 +6
> EOF
$ HGEDITOR="cat $EDITED >" 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
+ merging file
+ warning: conflicts during merge.
+ merging file incomplete! (edit conflicts, then use 'hg resolve --mark')
abort: Fix up the change and run hg histedit --continue
[255]
- $ echo 5 >> file
+ $ cat > file << EOF
+ > 1
+ > 2
+ > 3
+ > 4
+ > 5
+ > EOF
+ $ hg resolve --mark file
$ hg commit -m '+5.2'
created new head
$ echo 6 >> file
$ HGEDITOR=cat hg histedit --continue
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
diff --git a/tests/test-histedit-non-commute-abort.t b/tests/test-histedit-non-commute-abort.t
--- a/tests/test-histedit-non-commute-abort.t
+++ b/tests/test-histedit-non-commute-abort.t
@@ -71,27 +71,25 @@ log before edit
edit the history
$ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle
0 files updated, 0 files merged, 2 files removed, 0 files unresolved
- 1 out of 1 hunks FAILED -- saving rejects to file e.rej
+ remote changed e which local deleted
+ use (c)hanged version or leave (d)eleted? c
+ 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ merging e
+ warning: conflicts during merge.
+ merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
abort: Fix up the change and run hg histedit --continue
-fix up (pre abort)
- $ echo a > e
- $ hg add e
- $ hg histedit --continue 2>&1 | fixbundle
- 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
abort the edit
$ hg histedit --abort 2>&1 | fixbundle
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
log after abort
+ $ hg resolve -l
$ hg log --graph
@ changeset: 6:bfa474341cc9
| tag: tip
| user: test
| date: Thu Jan 01 00:00:00 1970 +0000
diff --git a/tests/test-histedit-non-commute.t b/tests/test-histedit-non-commute.t
--- a/tests/test-histedit-non-commute.t
+++ b/tests/test-histedit-non-commute.t
@@ -87,13 +87,13 @@ log before edit
edit the history
$ HGEDITOR="cat \"$EDITED\" > " hg histedit 3 2>&1 | fixbundle
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
- patching file e
- Hunk #1 FAILED at 0
- 1 out of 1 hunks FAILED -- saving rejects to file e.rej
+ merging e
+ warning: conflicts during merge.
+ merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
abort: Fix up the change and run hg histedit --continue
abort the edit
$ hg histedit --abort 2>&1 | fixbundle
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
@@ -145,25 +145,28 @@ second edit set
edit the history
$ HGEDITOR="cat \"$EDITED\" > " hg histedit 3 2>&1 | fixbundle
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
- patching file e
- Hunk #1 FAILED at 0
- 1 out of 1 hunks FAILED -- saving rejects to file e.rej
+ merging e
+ warning: conflicts during merge.
+ merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
abort: Fix up the change and run hg histedit --continue
fix up
$ echo 'I can haz no commute' > e
+ $ hg resolve --mark e
$ hg histedit --continue 2>&1 | fixbundle
0 files updated, 0 files merged, 0 files removed, 0 files unresolved
- patching file e
- Hunk #1 FAILED at 0
- 1 out of 1 hunks FAILED -- saving rejects to file e.rej
+ merging e
+ warning: conflicts during merge.
+ merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
abort: Fix up the change and run hg histedit --continue
just continue this time
+ $ hg revert -r 'p1()' e
+ $ hg resolve --mark e
$ hg histedit --continue 2>&1 | fixbundle
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
@@ -225,23 +228,26 @@ start over
pick 500cac37a696 6 f
edit the history, this time with a fold action
$ HGEDITOR="cat \"$EDITED\" > " hg histedit 3 2>&1 | fixbundle
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
- patching file e
- Hunk #1 FAILED at 0
- 1 out of 1 hunks FAILED -- saving rejects to file e.rej
+ merging e
+ warning: conflicts during merge.
+ merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
abort: Fix up the change and run hg histedit --continue
$ echo 'I can haz no commute' > e
+ $ hg resolve --mark e
$ HGEDITOR="cat \"$EDITED\" > " hg histedit --continue 2>&1 | fixbundle
0 files updated, 0 files merged, 0 files removed, 0 files unresolved
- patching file e
- Hunk #1 FAILED at 0
- 1 out of 1 hunks FAILED -- saving rejects to file e.rej
+ merging e
+ warning: conflicts during merge.
+ merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
abort: Fix up the change and run hg histedit --continue
second edit also fails, but just continue
+ $ hg revert -r 'p1()' e
+ $ hg resolve --mark e
$ hg histedit --continue 2>&1 | fixbundle
0 files updated, 0 files merged, 0 files removed, 0 files unresolved
0 files updated, 0 files merged, 0 files removed, 0 files unresolved
post message fix
More information about the Mercurial-devel
mailing list