amend: rework "--to" to not use histedit

Summary:
The amend "--to" flag amends the specified commit rather than ".". Previously it made a temporary commit and used histedit to scoot it back. This is not optimal due to unnecessary disk operations and fragile conflict handling.

Instead, "--to" now does its work in memory, checking for conflicts as it goes. If it finds any conflicts it aborts the operation. It works by generating a patch based on the working context and applying it to the specified commit. Then it does a mini-rebase of the stack tail onto the amended comit.

I tweaked patch.py to unlink the "from" of a rename _after_ creating the "to", which seems like the natural order to me. Other than the repobackend which defers unlinking, I don't see how other patch backends would have worked when renaming a file.

Reviewed By: DurhamG

Differential Revision: D29471052

fbshipit-source-id: 83406ec16b724b27d9a23473b630cafbb75da4d2
This commit is contained in:
Muir Manders 2021-07-13 09:06:56 -07:00 committed by Facebook GitHub Bot
parent 2c108cf958
commit a03c904c9f
4 changed files with 663 additions and 250 deletions

View File

@ -49,25 +49,27 @@ following advice for resolution will be shown::
from __future__ import absolute_import
import tempfile
import io
from bindings import checkout as nativecheckout
from edenscm.mercurial import (
bookmarks,
context,
cmdutil,
commands,
error,
extensions,
hintutil,
lock as lockmod,
mutation,
patch,
phases,
pycompat,
registrar,
scmutil,
)
from edenscm.mercurial.i18n import _
from edenscm.mercurial.node import hex, short
from edenscm.mercurial.node import short, nullid
from .. import histedit, rebase as rebasemod
from .. import rebase as rebasemod
from . import (
common,
fold,
@ -351,7 +353,7 @@ def amend(ui, repo, *pats, **opts):
"template",
]
if to and any(opts.get(flag, None) for flag in badtoflags):
if to and (any(opts.get(flag, None) for flag in badtoflags) or pats):
raise error.Abort(_("--to cannot be used with any other options"))
if fixup:
@ -518,53 +520,116 @@ def fixupamend(ui, repo, noconflict=None, noconflictmsg=None):
def amendtocommit(ui, repo, commitspec):
"""amend to a specific commit"""
with repo.wlock(), repo.lock():
originalcommits = list(repo.set("::. - public()"))
try:
revs = scmutil.revrange(repo, [commitspec])
except error.RepoLookupError:
raise error.Abort(_("revision '%s' cannot be found") % commitspec)
if len(revs) > 1:
raise error.Abort(_("'%s' refers to multiple changesets") % commitspec)
targetcommit = repo[revs.first()]
if targetcommit not in originalcommits:
"""amend to a specific commit
This works by patching the working diff on to the specified commit
and then performing a simplified rebase of the stack's tail on to
the amended ancestor.
commitspec must refer to a single commit that is a linear ancestor
of ".".
"""
with repo.wlock(), repo.lock(), repo.transaction("amend"):
revs = list(scmutil.revrange(repo, [commitspec]))
if len(revs) != 1:
raise error.Abort(_("'%s' must refer to a single changeset") % commitspec)
draftctxs = list(repo.revs("(%d)::.", revs[0]).iterctx())
if len(draftctxs) == 0:
raise error.Abort(
_("revision '%s' is not a parent of the working copy") % commitspec
_("revision '%s' is not an ancestor of the working copy") % commitspec
)
tempcommit = repo.commit(text="tempCommit")
if repo.revs("%ld & merge()", draftctxs):
raise error.Abort(_("cannot amend non-linear stack"))
if not tempcommit:
raise error.Abort(_("no pending changes to amend"))
dest = draftctxs.pop(0)
if dest.phase() == phases.public:
raise error.Abort(_("cannot amend public changesets"))
tempcommithex = hex(tempcommit)
# Generate patch from wctx and apply to dest commit.
mergedctx = mirrorwithmetadata(dest, "amend")
wctx = repo[None]
store = patch.mempatchstore(mergedctx)
backend = patch.mempatchbackend(ui, mergedctx, store)
ret = patch.applydiff(
ui,
io.BytesIO(b"".join(list(wctx.diff()))),
backend,
store,
)
if ret < 0:
raise error.Abort(_("amend would conflict in %s") % ", ".join(backend.rejs))
fp = tempfile.NamedTemporaryFile()
try:
found = False
for curcommit in originalcommits:
fp.write(b"pick %s\n" % bytes(curcommit))
if curcommit == targetcommit:
fp.write(b"roll %s\n" % pycompat.encodeutf8(tempcommithex[:12]))
found = True
if not found:
raise error.Abort(_("revision '%s' cannot be found") % commitspec)
fp.flush()
try:
histedit.histedit(
ui, repo, rev=[originalcommits[0].hex()], commands=fp.name
)
except error.InterventionRequired:
ui.warn(
_(
"amend --to encountered an issue - "
"use hg histedit to continue or abort"
)
)
raise
finally:
fp.close()
memctxs = [mergedctx]
mappednodes = [dest.node()]
# Perform mini-rebase of our stack.
for ctx in draftctxs:
memctxs.append(inmemorymerge(ui, repo, ctx, memctxs[-1], ctx.p1()))
mappednodes.append(ctx.node())
parentnode = None
mapping = {}
# Execute our list of in-memory commits, updating descendants'
# parent as we go.
for i, memctx in enumerate(memctxs):
if i > 0:
memctx = context.memctx.mirror(memctx, parentnodes=(parentnode, nullid))
parentnode = memctx.commit()
mapping[mappednodes[i]] = (parentnode,)
scmutil.cleanupnodes(repo, {dest.node(): mapping.pop(dest.node())}, "amend")
scmutil.cleanupnodes(repo, mapping, "rebase")
with repo.dirstate.parentchange():
# Update dirstate status of amended files.
repo.dirstate.rebuild(
parentnode, repo[parentnode].manifest(), wctx.files(), exact=True
)
def inmemorymerge(ui, repo, src, dest, base):
"""Return memctx representing three way merge of src, dest, and base
src is "remote" and dest is "local".
"""
mergeresult = nativecheckout.mergeresult(
src.manifest(), dest.manifest(), base.manifest()
)
manifestbuilder = mergeresult.manifestbuilder()
if manifestbuilder is None:
raise error.Abort(
_("amend would conflict in %s") % ", ".join(mergeresult.conflict_paths())
)
try:
resolved = rebasemod._simplemerge(ui, base, src, dest, manifestbuilder)
except error.InMemoryMergeConflictsError as ex:
raise error.Abort(_("amend would conflict in %s") % ", ".join(ex.paths))
mergedctx = mirrorwithmetadata(src, "rebase")
for path in manifestbuilder.removed():
mergedctx[path] = None
for path, merged in resolved.items():
mergedctx[path] = context.overlayfilectx(
src[path],
datafunc=lambda: merged,
ctx=mergedctx,
)
return mergedctx
def mirrorwithmetadata(ctx, op):
extra = ctx.extra().copy()
extra[op + "_source"] = ctx.hex()
mutinfo = mutation.record(ctx.repo(), extra, [ctx.node()], op)
loginfo = {"predecessors": ctx.hex(), "mutation": op}
return context.memctx.mirror(ctx, mutinfo=mutinfo, loginfo=loginfo, extra=extra)
def wraprebase(orig, ui, repo, *pats, **opts):

View File

@ -669,6 +669,72 @@ class repobackend(abstractbackend):
return self.changed | self.removed
class mempatchbackend(abstractbackend):
"""implements patch backend interface on top of context.memctx"""
def __init__(self, ui, ctx, store):
super(mempatchbackend, self).__init__(ui)
self.ctx = ctx
self.repo = ctx.repo()
self.store = store
self.rejs = []
def unlink(self, fname):
self.ctx[fname] = None
def writerej(self, fname, failed, total, lines):
self.rejs.append(fname)
def getfile(self, fname):
return self.store.getfile(fname)
def exists(self, fname):
return fname in self.ctx
def setfile(self, fname, data, mode, copysource):
self.store.setfile(fname, data, mode, copysource)
class mempatchstore(object):
"""implements patch store interface on top of context.memctx"""
def __init__(self, ctx):
self.ctx = ctx
def getfile(self, fname):
fctx = self.ctx[fname]
if fctx is None:
return None, None
return fctx.data(), (fctx.islink(), fctx.isexec())
def setfile(self, fname, data, mode, copysource=None):
# Don't lose copy info when patching a copied file.
if copysource is None and fname in self.ctx:
copysource = self.ctx[fname].renamed()
if copysource:
copysource = copysource[0]
# Avoid "can't find ancestor for <file>" warning when renaming
# a file in the commit it was added.
if copysource and copysource not in self.ctx.p1():
copysource = None
from . import context # avoid circular import
self.ctx[fname] = context.memfilectx(
self.ctx.repo(),
self.ctx,
fname,
data,
islink=mode[0],
isexec=mode[1],
copied=copysource,
)
def close(self):
pass
# @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1
unidesc = re.compile(b"@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@")
contextdesc = re.compile(b"(?:---|\*\*\*) (\d+)(?:,(\d+))? (?:---|\*\*\*)")
@ -2195,8 +2261,6 @@ def _applydiff(ui, fp, patcher, backend, store, strip=1, prefix="", eolmode="str
gp.oldpath = pstrip(gp.oldpath)
else:
gp = makepatchmeta(backend, afile, bfile, first_hunk, strip, prefix)
if gp.op == "RENAME":
backend.unlink(gp.oldpath)
if not first_hunk:
if gp.op == "DELETE":
backend.unlink(gp.path)
@ -2222,14 +2286,16 @@ def _applydiff(ui, fp, patcher, backend, store, strip=1, prefix="", eolmode="str
% gp.path
)
backend.setfile(gp.path, data, mode, gp.oldpath)
continue
try:
current_file = patcher(ui, gp, backend, store, eolmode=eolmode)
except PatchError as inst:
ui.warn(str(inst) + "\n")
current_file = None
rejects += 1
continue
else:
try:
current_file = patcher(ui, gp, backend, store, eolmode=eolmode)
except PatchError as inst:
ui.warn(str(inst) + "\n")
current_file = None
rejects += 1
if gp.op == "RENAME":
backend.unlink(gp.oldpath)
elif state == "git":
for gp in values:
path = pstrip(gp.oldpath)

View File

@ -1,205 +1,488 @@
#chg-compatible
Set up test environment.
$ configure mutation-norecord
$ enable amend directaccess histedit rebase
Test that amend --to option
$ newrepo repo
$ mkcommit() {
> echo "$1" > "$1"
> hg add "$1"
> hg ci -m "$1"
> }
$ mkcommit "ROOT"
$ hg debugmakepublic "desc(ROOT)"
$ mkcommit "SIDE"
$ hg debugmakepublic "desc(SIDE)"
$ hg update -r "desc(ROOT)"
0 files updated, 0 files merged, 1 files removed, 0 files unresolved
$ mkcommit "A"
$ mkcommit "B"
$ mkcommit "C"
Test
----
$ configure modern
$ configure mutation
$ setconfig diff.git=True
$ hg log -G -vp -T "{desc} {node|short}"
@ C a8df460dbbfediff -r c473644ee0e9 -r a8df460dbbfe C
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/C Thu Jan 01 00:00:00 1970 +0000
Test adding, modifying, removing, and renaming files in amend.
$ newrepo
$ echo foo > foo
$ echo bar > bar
$ echo baz > baz
$ hg ci -m "one" -Aq
$ echo qux > qux
$ hg ci -m "two" -Aq
$ hg mv foo foo_renamed
$ hg rm bar
$ echo morebaz >> baz
$ echo new > new
$ hg add new
$ hg amend --to .^
$ hg log -G -vp -T "{desc} {join(extras, ' ')} {mutations} {node|short}"
@ two branch=default mutdate=0 0 mutop=rebase mutpred=hg/7d3606fd19e3e6bb309681ed5af095d173314ab5 mutuser=test rebase_source=7d3606fd19e3e6bb309681ed5af095d173314ab5 01f78cddc939diff --git a/qux b/qux
new file mode 100644
--- /dev/null
+++ b/qux
@@ -0,0 +1,1 @@
+C
+qux
o B c473644ee0e9diff -r 2a34000d3544 -r c473644ee0e9 B
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/B Thu Jan 01 00:00:00 1970 +0000
@@ -0,0 +1,1 @@
+B
o A 2a34000d3544diff -r ea207398892e -r 2a34000d3544 A
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/A Thu Jan 01 00:00:00 1970 +0000
@@ -0,0 +1,1 @@
+A
o SIDE 3c489f4f07a6diff -r ea207398892e -r 3c489f4f07a6 SIDE
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SIDE Thu Jan 01 00:00:00 1970 +0000
@@ -0,0 +1,1 @@
+SIDE
o ROOT ea207398892ediff -r 000000000000 -r ea207398892e ROOT
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ROOT Thu Jan 01 00:00:00 1970 +0000
o one amend_source=c5580d2879b81dcc2465e5dde5f0ee86218c0dc0 branch=default mutdate=0 0 mutop=amend mutpred=hg/c5580d2879b81dcc2465e5dde5f0ee86218c0dc0 mutuser=test a48652e46d72diff --git a/baz b/baz
new file mode 100644
--- /dev/null
+++ b/baz
@@ -0,0 +1,2 @@
+baz
+morebaz
diff --git a/foo_renamed b/foo_renamed
new file mode 100644
--- /dev/null
+++ b/foo_renamed
@@ -0,0 +1,1 @@
+ROOT
$ cat > testFile << EOF
> line1
> line2
> line3
> EOF
$ hg add testFile
$ hg amend --to dorito
abort: revision 'dorito' cannot be found
[255]
$ hg amend --to c473644
$ hg amend --to 3c489f4f07a6
abort: revision '3c489f4f07a6' is not a parent of the working copy
[255]
$ hg amend --to 'children(ea207398892e)'
abort: 'children(ea207398892e)' refers to multiple changesets
[255]
$ hg amend --to 'min(children(ea207398892e))'
abort: revision 'min(children(ea207398892e))' is not a parent of the working copy
[255]
$ hg log -G -vp -T "{desc} {node|short}"
@ C 86de924a3b95diff -r ce91eb673f02 -r 86de924a3b95 C
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/C Thu Jan 01 00:00:00 1970 +0000
@@ -0,0 +1,1 @@
+C
o B ce91eb673f02diff -r 2a34000d3544 -r ce91eb673f02 B
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/B Thu Jan 01 00:00:00 1970 +0000
@@ -0,0 +1,1 @@
+B
diff -r 2a34000d3544 -r ce91eb673f02 testFile
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/testFile Thu Jan 01 00:00:00 1970 +0000
@@ -0,0 +1,3 @@
+line1
+line2
+line3
o A 2a34000d3544diff -r ea207398892e -r 2a34000d3544 A
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/A Thu Jan 01 00:00:00 1970 +0000
@@ -0,0 +1,1 @@
+A
o SIDE 3c489f4f07a6diff -r ea207398892e -r 3c489f4f07a6 SIDE
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SIDE Thu Jan 01 00:00:00 1970 +0000
@@ -0,0 +1,1 @@
+SIDE
o ROOT ea207398892ediff -r 000000000000 -r ea207398892e ROOT
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ROOT Thu Jan 01 00:00:00 1970 +0000
+foo
diff --git a/new b/new
new file mode 100644
--- /dev/null
+++ b/new
@@ -0,0 +1,1 @@
+ROOT
+new
Test removing, modifying and renaming files in subsequent commit.
$ newrepo
$ echo foo > foo
$ echo bar > bar
$ echo baz > baz
$ hg ci -m "one" -Aq
$ echo two > two
$ hg ci -m "two" -Aq
$ echo three > three
$ hg ci -m "three" -Aq
$ hg mv foo foo_renamed
$ hg rm bar
$ echo morebaz >> baz
$ hg amend --to 'desc(two)'
$ hg log -G -vp -T "{desc} {join(extras, ' ')} {mutations} {node|short}"
@ three branch=default mutdate=0 0 mutop=rebase mutpred=hg/0b73272bdcf2bf38c71192959d0e3f750de85ea0 mutuser=test rebase_source=0b73272bdcf2bf38c71192959d0e3f750de85ea0 7b8c275b5725diff --git a/three b/three
new file mode 100644
--- /dev/null
+++ b/three
@@ -0,0 +1,1 @@
+three
o two amend_source=fbe2abd632c8512c5496e98dedd5cddb43126c37 branch=default mutdate=0 0 mutop=amend mutpred=hg/fbe2abd632c8512c5496e98dedd5cddb43126c37 mutuser=test 9c63fb016abfdiff --git a/bar b/bar
deleted file mode 100644
--- a/bar
+++ /dev/null
@@ -1,1 +0,0 @@
-bar
diff --git a/baz b/baz
--- a/baz
+++ b/baz
@@ -1,1 +1,2 @@
baz
+morebaz
diff --git a/foo b/foo_renamed
rename from foo
rename to foo_renamed
diff --git a/two b/two
new file mode 100644
--- /dev/null
+++ b/two
@@ -0,0 +1,1 @@
+two
o one branch=default c5580d2879b8diff --git a/bar b/bar
new file mode 100644
--- /dev/null
+++ b/bar
@@ -0,0 +1,1 @@
+bar
diff --git a/baz b/baz
new file mode 100644
--- /dev/null
+++ b/baz
@@ -0,0 +1,1 @@
+baz
diff --git a/foo b/foo
new file mode 100644
--- /dev/null
+++ b/foo
@@ -0,0 +1,1 @@
+foo
Test three way merge during rebase.
$ newrepo
$ printf "one\n\ntwo\n\nthree\n" > foo
$ hg ci -m "one" -Aq
$ printf "one\n\ntwo\n\nfour\n" > foo
$ hg ci -m "two"
$ printf "five\n\ntwo\n\nfour\n" > foo
$ hg amend --to .^
merging foo
$ hg log -G -vp -T "{desc} {node|short}"
@ two 706f867d1a33diff --git a/foo b/foo
--- a/foo
+++ b/foo
@@ -2,4 +2,4 @@
two
-three
+four
o one 997db81b26b2diff --git a/foo b/foo
new file mode 100644
--- /dev/null
+++ b/foo
@@ -0,0 +1,5 @@
+five
+
+two
+
+three
Test replacing file with directory.
$ newrepo
$ echo foo > foo
$ hg ci -m "one" -Aq
$ echo bar > bar
$ hg ci -m "two" -Aq
$ hg rm foo
$ mkdir foo
$ echo foo > foo/foo
$ hg add foo/foo
$ hg amend --to .^
$ hg log -G -vp -T "{desc} {node|short}"
@ two 3b1456dbf553diff --git a/bar b/bar
new file mode 100644
--- /dev/null
+++ b/bar
@@ -0,0 +1,1 @@
+bar
o one 59fa10ae0edbdiff --git a/foo/foo b/foo/foo
new file mode 100644
--- /dev/null
+++ b/foo/foo
@@ -0,0 +1,1 @@
+foo
Test replacing file with symlink.
$ newrepo
$ echo foo > foo
$ hg ci -m "one" -Aq
$ echo bar > bar
$ hg ci -m "two" -Aq
$ ln -sf bar foo
$ hg amend --to .^
$ hg log -G -vp -T "{desc} {node|short}"
@ two 6e7cc816fa6fdiff --git a/bar b/bar
new file mode 100644
--- /dev/null
+++ b/bar
@@ -0,0 +1,1 @@
+bar
o one 7b9a757afa3cdiff --git a/foo b/foo
new file mode 120000
--- /dev/null
+++ b/foo
@@ -0,0 +1,1 @@
+bar
\ No newline at end of file
Test replacing file with symlink in subsequent commit.
$ newrepo
$ echo foo > foo
$ hg ci -m "one" -Aq
$ echo bar > bar
$ hg ci -m "two" -Aq
$ ln -sf bar foo
$ hg amend --to .
$ hg log -G -vp -T "{desc} {node|short}"
@ two d17906564d89diff --git a/bar b/bar
new file mode 100644
--- /dev/null
+++ b/bar
@@ -0,0 +1,1 @@
+bar
diff --git a/foo b/foo
old mode 100644
new mode 120000
--- a/foo
+++ b/foo
@@ -1,1 +1,1 @@
-foo
+bar
\ No newline at end of file
o one 0174aede5e86diff --git a/foo b/foo
new file mode 100644
--- /dev/null
+++ b/foo
@@ -0,0 +1,1 @@
+foo
Test renaming a file modified by later commit (not supported).
$ newrepo
$ echo foo > foo
$ hg ci -m "one" -Aq
$ echo bar >> foo
$ hg ci -m "two"
$ hg mv foo bar
$ hg amend --to .^
abort: amend would conflict in foo
[255]
$ hg status
$ echo "line4" >> testFile
$ hg ci -m "line4"
$ echo "line5" >> testFile
$ hg amend --to ce91eb673f02
1 files updated, 0 files merged, 1 files removed, 0 files unresolved
merging testFile
warning: 1 conflicts while merging testFile! (edit, then use 'hg resolve --mark')
amend --to encountered an issue - use hg histedit to continue or abortFix up the change (roll 8a18ce6b4d69)
(hg histedit --continue to resume)
[1]
$ hg histedit --abort > /dev/null
$ hg hide . > /dev/null
Test amending to commit when side branch is present
$ hg update -q ce91eb67
$ mkcommit "SIDE2"
$ hg update -q 75f11a3d
$ echo EXTRA >> A
$ hg amend --to 2a34000
A bar
R foo
$ hg log -G -vp -T "{desc} {node|short}"
@ line4 19b94b1dc3f8diff -r 66a8a892708f -r 19b94b1dc3f8 testFile
--- a/testFile Thu Jan 01 00:00:00 1970 +0000
+++ b/testFile Thu Jan 01 00:00:00 1970 +0000
@@ -1,3 +1,4 @@
line1
line2
line3
+line4
@ two 7fa82f87fe73diff --git a/foo b/foo
--- a/foo
+++ b/foo
@@ -1,1 +1,2 @@
foo
+bar
o C 66a8a892708fdiff -r 33635c2e36d9 -r 66a8a892708f C
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/C Thu Jan 01 00:00:00 1970 +0000
@@ -0,0 +1,1 @@
+C
o B 33635c2e36d9diff -r 4cc5b3966b69 -r 33635c2e36d9 B
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/B Thu Jan 01 00:00:00 1970 +0000
@@ -0,0 +1,1 @@
+B
diff -r 4cc5b3966b69 -r 33635c2e36d9 testFile
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/testFile Thu Jan 01 00:00:00 1970 +0000
@@ -0,0 +1,3 @@
+line1
+line2
+line3
o A 4cc5b3966b69diff -r ea207398892e -r 4cc5b3966b69 A
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/A Thu Jan 01 00:00:00 1970 +0000
@@ -0,0 +1,2 @@
+A
+EXTRA
o SIDE2 059ea8bdb89ediff -r ce91eb673f02 -r 059ea8bdb89e SIDE2
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SIDE2 Thu Jan 01 00:00:00 1970 +0000
@@ -0,0 +1,1 @@
+SIDE2
x B ce91eb673f02diff -r 2a34000d3544 -r ce91eb673f02 B
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/B Thu Jan 01 00:00:00 1970 +0000
@@ -0,0 +1,1 @@
+B
diff -r 2a34000d3544 -r ce91eb673f02 testFile
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/testFile Thu Jan 01 00:00:00 1970 +0000
@@ -0,0 +1,3 @@
+line1
+line2
+line3
x A 2a34000d3544diff -r ea207398892e -r 2a34000d3544 A
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/A Thu Jan 01 00:00:00 1970 +0000
@@ -0,0 +1,1 @@
+A
o SIDE 3c489f4f07a6diff -r ea207398892e -r 3c489f4f07a6 SIDE
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SIDE Thu Jan 01 00:00:00 1970 +0000
@@ -0,0 +1,1 @@
+SIDE
o ROOT ea207398892ediff -r 000000000000 -r ea207398892e ROOT
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ROOT Thu Jan 01 00:00:00 1970 +0000
o one 0174aede5e86diff --git a/foo b/foo
new file mode 100644
--- /dev/null
+++ b/foo
@@ -0,0 +1,1 @@
+ROOT
+foo
Test conflict during initial patch.
$ newrepo
$ echo foo > foo
$ hg ci -m "one" -Aq
$ echo bar > foo
$ hg ci -m "two"
$ echo baz > foo
$ hg amend --to .^
patching file foo
Hunk #1 FAILED at 0
abort: amend would conflict in foo
[255]
$ hg status
M foo
$ hg log -G -vp -T "{desc} {node|short}"
@ two 171ec227cf58diff --git a/foo b/foo
--- a/foo
+++ b/foo
@@ -1,1 +1,1 @@
-foo
+bar
o one 0174aede5e86diff --git a/foo b/foo
new file mode 100644
--- /dev/null
+++ b/foo
@@ -0,0 +1,1 @@
+foo
Test conflict during rebase.
$ newrepo
$ echo foo > foo
$ hg ci -m "one" -Aq
$ echo bar > foo
$ hg ci -m "two"
$ echo foo > foo
$ hg ci -m "three"
$ echo baz > foo
$ hg amend --to 'desc(one)'
merging foo
abort: amend would conflict in foo
[255]
$ hg status
M foo
$ hg log -G -vp -T "{desc} {node|short}"
@ three f2c76c6d797ediff --git a/foo b/foo
--- a/foo
+++ b/foo
@@ -1,1 +1,1 @@
-bar
+foo
o two 171ec227cf58diff --git a/foo b/foo
--- a/foo
+++ b/foo
@@ -1,1 +1,1 @@
-foo
+bar
o one 0174aede5e86diff --git a/foo b/foo
new file mode 100644
--- /dev/null
+++ b/foo
@@ -0,0 +1,1 @@
+foo
Test amending when target commit has other children.
$ newrepo
$ echo foo > foo
$ hg ci -m "root" -Aq
$ echo bar > bar
$ hg ci -m "a" -Aq
$ hg up 'desc(root)'
0 files updated, 0 files merged, 1 files removed, 0 files unresolved
$ echo baz > baz
$ hg ci -m "b" -Aq
$ echo more >> foo
$ hg amend --to 'desc(root)'
$ hg log -G -vp -T "{desc} {node|short}"
@ b 016648f10c01diff --git a/baz b/baz
new file mode 100644
--- /dev/null
+++ b/baz
@@ -0,0 +1,1 @@
+baz
o root 41500d1b8742diff --git a/foo b/foo
new file mode 100644
--- /dev/null
+++ b/foo
@@ -0,0 +1,2 @@
+foo
+more
o a 07a119ce0bd1diff --git a/bar b/bar
new file mode 100644
--- /dev/null
+++ b/bar
@@ -0,0 +1,1 @@
+bar
x root e1062ec6bdfadiff --git a/foo b/foo
new file mode 100644
--- /dev/null
+++ b/foo
@@ -0,0 +1,1 @@
+foo
Test amending a renamed file (don't lose copysource).
$ newrepo
$ echo foo > foo
$ hg ci -m "one" -Aq
$ hg mv foo bar
$ hg ci -m "two"
$ echo foo >> bar
$ hg amend --to .
$ hg log -G -vp -T "{desc} {node|short}"
@ two 1560771da02ediff --git a/foo b/bar
rename from foo
rename to bar
--- a/foo
+++ b/bar
@@ -1,1 +1,2 @@
foo
+foo
o one 0174aede5e86diff --git a/foo b/foo
new file mode 100644
--- /dev/null
+++ b/foo
@@ -0,0 +1,1 @@
+foo
Test various error cases.
$ newrepo
$ echo foo > foo
$ hg ci -m "one" -Aq
$ hg debugmakepublic .
$ echo bar > bar
$ hg ci -m "two" -Aq
$ echo more >> bar
$ hg amend --to .^
abort: cannot amend public changesets
[255]
$ hg amend --to banana
abort: unknown revision 'banana'!
[255]
$ hg revert bar
$ hg up .^
0 files updated, 0 files merged, 1 files removed, 0 files unresolved
$ echo fork > fork
$ hg ci -m "fork" -Aq
$ echo more >> bar
$ hg amend --to 'desc(two)'
abort: revision 'desc(two)' is not an ancestor of the working copy
[255]
$ hg amend --to '::.'
abort: '::.' must refer to a single changeset
[255]
$ hg amend --to '.' -i
abort: --to cannot be used with any other options
[255]
$ hg amend --to '.' bar
abort: --to cannot be used with any other options
[255]

View File

@ -1503,7 +1503,6 @@ sh % "hg init repo"
sh % "cd repo"
sh % "printf 'diff --git a/a b/b\\nrename from a\\nrename to b'" | "hg import -" == r"""
applying patch from stdin
a not tracked!
abort: source file 'a' does not exist
[255]"""