D6604: copies: follow copies across merge base without source file (issue6163)
martinvonz (Martin von Zweigbergk)
phabricator at mercurial-scm.org
Thu Jul 4 15:00:30 UTC 2019
martinvonz created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.
REVISION SUMMARY
As in the previous patch, consider these two histories:
@ 4 'rename x to y'
|
o 3 'add x again'
|
o 2 'remove x'
|
| o 1 'modify x'
|/
o 0 'add x'
@ 4 'rename x to y'
|
o 3 'add x again'
|
| o 2 'modify x'
| |
| o 1 'add x'
|/
o 0 'base'
We trace copies from the 'modify x' commit to commit 4 by going via
the merge base (commit 0). When tracing file 'y' (_tracefile()) in the
first case, we immediately find the rename from 'x'. We check to see
if 'x' exists in the merge base, which it does, so we consider it a
valid copy. In the second case, 'x' does not exist in the merge base,
so it's not considered a valid copy. As a workaround, this patch makes
it so we also attempt the check in mergecopies's base commit (commit 1
in the second case). That feels pretty ugly to me, but I don't have
any better ideas.
Note that we actually also check not only that the filename matches,
but also that the file's nodeid matches. I don't know why we do that,
but it was like that already before I rewrote mergecopies(). That
means that the rebase will still fail in cases like this (again, it
already failed before my rewrite):
@ 4 'rename x to y'
|
o 3 'add x again with content X2'
|
o 2 'remove x'
|
| o 1 'modify x to content X2'
|/
o 1 'modify x to content X1'
|
o 0 'add x with content X0'
REPOSITORY
rHG Mercurial
REVISION DETAIL
https://phab.mercurial-scm.org/D6604
AFFECTED FILES
mercurial/copies.py
tests/test-copies-unrelated.t
CHANGE DETAILS
diff --git a/tests/test-copies-unrelated.t b/tests/test-copies-unrelated.t
--- a/tests/test-copies-unrelated.t
+++ b/tests/test-copies-unrelated.t
@@ -299,22 +299,10 @@
o 0 base
a
$ hg debugpathcopies 1 4
- x -> y (no-filelog !)
-#if filelog
-BROKEN: This should succeed and merge the changes from x into y
- $ hg graft -r 2
- grafting 2:* "modify x" (glob)
- file 'x' was deleted in local [local] but was modified in other [graft].
- What do you want to do?
- use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u
- abort: unresolved conflicts, can't continue
- (use 'hg resolve' and 'hg graft --continue')
- [255]
-#else
+ x -> y
$ hg graft -r 2
grafting 2:* "modify x" (glob)
merging y and x to y
-#endif
$ hg co -qC 2
$ hg graft -r 4
grafting 4:* "rename x to y"* (glob)
diff --git a/mercurial/copies.py b/mercurial/copies.py
--- a/mercurial/copies.py
+++ b/mercurial/copies.py
@@ -150,7 +150,7 @@
t[k] = v
return t
-def _tracefile(fctx, am, limit):
+def _tracefile(fctx, am, basemf, limit):
"""return file context that is the ancestor of fctx present in ancestor
manifest am, stopping after the first ancestor lower than limit"""
@@ -158,6 +158,8 @@
path = f.path()
if am.get(path, None) == f.filenode():
return path
+ if basemf and basemf.get(path, None) == f.filenode():
+ return path
if not f.isintroducedafter(limit):
return None
@@ -183,7 +185,7 @@
return (repo.ui.config('experimental', 'copies.read-from') in
('changeset-only', 'compatibility'))
-def _committedforwardcopies(a, b, match):
+def _committedforwardcopies(a, b, base, match):
"""Like _forwardcopies(), but b.rev() cannot be None (working copy)"""
# files might have to be traced back to the fctx parent of the last
# one-side-only changeset, but not further back than that
@@ -201,6 +203,7 @@
if debug:
dbg('debug.copies: search limit: %d\n' % limit)
am = a.manifest()
+ basemf = None if base is None else base.manifest()
# find where new files came from
# we currently don't try to find where old files went, too expensive
@@ -232,7 +235,7 @@
if debug:
start = util.timer()
- opath = _tracefile(fctx, am, limit)
+ opath = _tracefile(fctx, am, basemf, limit)
if opath:
if debug:
dbg('debug.copies: rename of: %s\n' % opath)
@@ -311,17 +314,19 @@
heapq.heappush(work, (c, parent, newcopies))
assert False
-def _forwardcopies(a, b, match=None):
+def _forwardcopies(a, b, base=None, match=None):
"""find {dst at b: src at a} copy mapping where a is an ancestor of b"""
+ if base is None:
+ base = a
match = a.repo().narrowmatch(match)
# check for working copy
if b.rev() is None:
- cm = _committedforwardcopies(a, b.p1(), match)
+ cm = _committedforwardcopies(a, b.p1(), base, match)
# combine copies from dirstate if necessary
copies = _chain(cm, _dirstatecopies(b._repo, match))
else:
- copies = _committedforwardcopies(a, b, match)
+ copies = _committedforwardcopies(a, b, base, match)
return copies
def _backwardrenames(a, b, match):
@@ -369,8 +374,11 @@
else:
if debug:
repo.ui.debug('debug.copies: search mode: combined\n')
+ base = None
+ if a.rev() != node.nullrev:
+ base = x
copies = _chain(_backwardrenames(x, a, match=match),
- _forwardcopies(a, y, match=match))
+ _forwardcopies(a, y, base, match=match))
_filter(x, y, copies)
return copies
To: martinvonz, #hg-reviewers
Cc: mercurial-devel
More information about the Mercurial-devel
mailing list