--- a/mercurial/copies.py Sat Oct 05 10:29:34 2019 -0400
+++ b/mercurial/copies.py Sun Oct 06 09:45:02 2019 -0400
@@ -19,9 +19,8 @@
pathutil,
util,
)
-from .utils import (
- stringutil,
-)
+from .utils import stringutil
+
def _findlimit(repo, ctxa, ctxb):
"""
@@ -62,7 +61,7 @@
limit = node.wdirrev
while interesting:
- r = -heapq.heappop(visit)
+ r = -(heapq.heappop(visit))
if r == node.wdirrev:
parents = [pctx.rev() for pctx in wdirparents]
else:
@@ -81,7 +80,7 @@
side[p] = 0
interesting -= 1
if side[r]:
- limit = r # lowest rev visited
+ limit = r # lowest rev visited
interesting -= 1
# Consider the following flow (see test-commit-amend.t under issue4405):
@@ -107,6 +106,7 @@
# This only occurs when a is a descendent of b or visa-versa.
return min(limit, a, b)
+
def _filter(src, dst, t):
"""filters out invalid copies after chaining"""
@@ -140,6 +140,7 @@
elif k not in dst:
del t[k]
+
def _chain(a, b):
"""chain two sets of copies 'a' and 'b'"""
t = a.copy()
@@ -150,6 +151,7 @@
t[k] = v
return t
+
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"""
@@ -163,6 +165,7 @@
if not f.isintroducedafter(limit):
return None
+
def _dirstatecopies(repo, match=None):
ds = repo.dirstate
c = ds.copies().copy()
@@ -171,6 +174,7 @@
del c[k]
return c
+
def _computeforwardmissing(a, b, match=None):
"""Computes which files are in b but not a.
This is its own function so extensions can easily wrap this call to see what
@@ -180,12 +184,14 @@
mb = b.manifest()
return mb.filesnotin(ma, match=match)
+
def usechangesetcentricalgo(repo):
"""Checks if we should use changeset-centric copy algorithms"""
readfrom = repo.ui.config('experimental', 'copies.read-from')
changesetsource = ('changeset-only', 'compatibility')
return readfrom in changesetsource
+
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
@@ -198,8 +204,7 @@
debug = repo.ui.debugflag and repo.ui.configbool('devel', 'debug.copies')
dbg = repo.ui.debug
if debug:
- dbg('debug.copies: looking into rename from %s to %s\n'
- % (a, b))
+ dbg('debug.copies: looking into rename from %s to %s\n' % (a, b))
limit = _findlimit(repo, a, b)
if debug:
dbg('debug.copies: search limit: %d\n' % limit)
@@ -242,10 +247,13 @@
dbg('debug.copies: rename of: %s\n' % opath)
cm[f] = opath
if debug:
- dbg('debug.copies: time: %f seconds\n'
- % (util.timer() - start))
+ dbg(
+ 'debug.copies: time: %f seconds\n'
+ % (util.timer() - start)
+ )
return cm
+
def _changesetforwardcopies(a, b, match):
if a.rev() in (node.nullrev, b.rev()):
return {}
@@ -300,8 +308,9 @@
parent = 2
childcopies = childctx.p2copies()
if not alwaysmatch:
- childcopies = {dst: src for dst, src in childcopies.items()
- if match(dst)}
+ childcopies = {
+ dst: src for dst, src in childcopies.items() if match(dst)
+ }
# Copy the dict only if later iterations will also need it
if i != len(children[r]) - 1:
newcopies = copies.copy()
@@ -315,6 +324,7 @@
heapq.heappush(work, (c, parent, newcopies))
assert False
+
def _forwardcopies(a, b, base=None, match=None):
"""find {dst@b: src@a} copy mapping where a is an ancestor of b"""
@@ -327,9 +337,10 @@
# combine copies from dirstate if necessary
copies = _chain(cm, _dirstatecopies(b._repo, match))
else:
- copies = _committedforwardcopies(a, b, base, match)
+ copies = _committedforwardcopies(a, b, base, match)
return copies
+
def _backwardrenames(a, b, match):
if a._repo.ui.config('experimental', 'copytrace') == 'off':
return {}
@@ -351,13 +362,13 @@
r[v] = k
return r
+
def pathcopies(x, y, match=None):
"""find {dst@y: src@x} copy mapping for directed compare"""
repo = x._repo
debug = repo.ui.debugflag and repo.ui.configbool('devel', 'debug.copies')
if debug:
- repo.ui.debug('debug.copies: searching copies from %s to %s\n'
- % (x, y))
+ repo.ui.debug('debug.copies: searching copies from %s to %s\n' % (x, y))
if x == y or not x or not y:
return {}
a = y.ancestor(x)
@@ -378,11 +389,14 @@
base = None
if a.rev() != node.nullrev:
base = x
- copies = _chain(_backwardrenames(x, a, match=match),
- _forwardcopies(a, y, base, match=match))
+ copies = _chain(
+ _backwardrenames(x, a, match=match),
+ _forwardcopies(a, y, base, match=match),
+ )
_filter(x, y, copies)
return copies
+
def mergecopies(repo, c1, c2, base):
"""
Finds moves and copies between context c1 and c2 that are relevant for
@@ -462,6 +476,7 @@
else:
return _fullcopytracing(repo, c1, c2, base)
+
def _isfullcopytraceable(repo, c1, base):
""" Checks that if base, source and destination are all no-public branches,
if yes let's use the full copytrace algorithm for increased capabilities
@@ -474,14 +489,17 @@
if c1.rev() is None:
c1 = c1.p1()
if c1.mutable() and base.mutable():
- sourcecommitlimit = repo.ui.configint('experimental',
- 'copytrace.sourcecommitlimit')
+ sourcecommitlimit = repo.ui.configint(
+ 'experimental', 'copytrace.sourcecommitlimit'
+ )
commits = len(repo.revs('%d::%d', base.rev(), c1.rev()))
return commits < sourcecommitlimit
return False
-def _checksinglesidecopies(src, dsts1, m1, m2, mb, c2, base,
- copy, renamedelete):
+
+def _checksinglesidecopies(
+ src, dsts1, m1, m2, mb, c2, base, copy, renamedelete
+):
if src not in m2:
# deleted on side 2
if src not in m1:
@@ -497,6 +515,7 @@
# "both created" case in manifestmerge otherwise)
copy[dst] = src
+
def _fullcopytracing(repo, c1, c2, base):
""" The full copytracing algorithm which finds all the new files that were
added from merge base up to the top commit and for each file it checks if
@@ -537,7 +556,7 @@
# to 'b' and 'c' and deletes 'a', and side 2 copies 'a' to 'c'
# and 'd' and deletes 'a'.
if dsts1 & dsts2:
- for dst in (dsts1 & dsts2):
+ for dst in dsts1 & dsts2:
copy[dst] = src
else:
diverge[src] = sorted(dsts1 | dsts2)
@@ -545,18 +564,20 @@
# copied on both sides
dsts1 = set(dsts1)
dsts2 = set(dsts2)
- for dst in (dsts1 & dsts2):
+ for dst in dsts1 & dsts2:
copy[dst] = src
# TODO: Handle cases where it was renamed on one side and copied
# on the other side
elif dsts1:
# copied/renamed only on side 1
- _checksinglesidecopies(src, dsts1, m1, m2, mb, c2, base,
- copy, renamedelete)
+ _checksinglesidecopies(
+ src, dsts1, m1, m2, mb, c2, base, copy, renamedelete
+ )
elif dsts2:
# copied/renamed only on side 2
- _checksinglesidecopies(src, dsts2, m2, m1, mb, c1, base,
- copy, renamedelete)
+ _checksinglesidecopies(
+ src, dsts2, m2, m1, mb, c1, base, copy, renamedelete
+ )
renamedeleteset = set()
divergeset = set()
@@ -583,8 +604,10 @@
return copy, {}, diverge, renamedelete, {}
if repo.ui.debugflag:
- repo.ui.debug(" all copies found (* = to merge, ! = divergent, "
- "% = renamed and deleted):\n")
+ repo.ui.debug(
+ " all copies found (* = to merge, ! = divergent, "
+ "% = renamed and deleted):\n"
+ )
for f in sorted(fullcopy):
note = ""
if f in copy:
@@ -593,8 +616,9 @@
note += "!"
if f in renamedeleteset:
note += "%"
- repo.ui.debug(" src: '%s' -> dst: '%s' %s\n" % (fullcopy[f], f,
- note))
+ repo.ui.debug(
+ " src: '%s' -> dst: '%s' %s\n" % (fullcopy[f], f, note)
+ )
del divergeset
repo.ui.debug(" checking for directory renames\n")
@@ -635,8 +659,9 @@
dirmove = {k + "/": v + "/" for k, v in dirmove.iteritems()}
for d in dirmove:
- repo.ui.debug(" discovered dir src: '%s' -> dst: '%s'\n" %
- (d, dirmove[d]))
+ repo.ui.debug(
+ " discovered dir src: '%s' -> dst: '%s'\n" % (d, dirmove[d])
+ )
movewithdir = {}
# check unaccounted nonoverlapping files against directory moves
@@ -645,15 +670,18 @@
for d in dirmove:
if f.startswith(d):
# new file added in a directory that was moved, move it
- df = dirmove[d] + f[len(d):]
+ df = dirmove[d] + f[len(d) :]
if df not in copy:
movewithdir[f] = df
- repo.ui.debug((" pending file src: '%s' -> "
- "dst: '%s'\n") % (f, df))
+ repo.ui.debug(
+ (" pending file src: '%s' -> " "dst: '%s'\n")
+ % (f, df)
+ )
break
return copy, movewithdir, diverge, renamedelete, dirmove
+
def _heuristicscopytracing(repo, c1, c2, base):
""" Fast copytracing using filename heuristics
@@ -690,8 +718,10 @@
m1 = c1.manifest()
if not repo.revs('%d::%d', base.rev(), c2.rev()):
# If base is not in c2 branch, we switch to fullcopytracing
- repo.ui.debug("switching to full copytracing as base is not "
- "an ancestor of c2\n")
+ repo.ui.debug(
+ "switching to full copytracing as base is not "
+ "an ancestor of c2\n"
+ )
return _fullcopytracing(repo, c1, c2, base)
ctx = c2
@@ -736,13 +766,18 @@
f2 = c2.filectx(f)
# we can have a lot of candidates which can slow down the heuristics
# config value to limit the number of candidates moves to check
- maxcandidates = repo.ui.configint('experimental',
- 'copytrace.movecandidateslimit')
+ maxcandidates = repo.ui.configint(
+ 'experimental', 'copytrace.movecandidateslimit'
+ )
if len(movecandidates) > maxcandidates:
- repo.ui.status(_("skipping copytracing for '%s', more "
- "candidates than the limit: %d\n")
- % (f, len(movecandidates)))
+ repo.ui.status(
+ _(
+ "skipping copytracing for '%s', more "
+ "candidates than the limit: %d\n"
+ )
+ % (f, len(movecandidates))
+ )
continue
for candidate in movecandidates:
@@ -755,6 +790,7 @@
return copies, {}, {}, {}, {}
+
def _related(f1, f2):
"""return True if f1 and f2 filectx have a common ancestor
@@ -765,7 +801,7 @@
"""
if f1 == f2:
- return True # a match
+ return True # a match
g1, g2 = f1.ancestors(), f2.ancestors()
try:
@@ -782,11 +818,12 @@
f1 = next(g1)
elif f2r > f1r:
f2 = next(g2)
- else: # f1 and f2 point to files in the same linkrev
- return f1 == f2 # true if they point to the same file
+ else: # f1 and f2 point to files in the same linkrev
+ return f1 == f2 # true if they point to the same file
except StopIteration:
return False
+
def duplicatecopies(repo, wctx, rev, fromrev, skiprev=None):
"""reproduce copies from fromrev to rev in the dirstate
@@ -798,8 +835,9 @@
exclude = {}
ctraceconfig = repo.ui.config('experimental', 'copytrace')
bctrace = stringutil.parsebool(ctraceconfig)
- if (skiprev is not None and
- (ctraceconfig == 'heuristics' or bctrace or bctrace is None)):
+ if skiprev is not None and (
+ ctraceconfig == 'heuristics' or bctrace or bctrace is None
+ ):
# copytrace='off' skips this line, but not the entire function because
# the line below is O(size of the repo) during a rebase, while the rest
# of the function is much faster (and is required for carrying copy
@@ -811,6 +849,7 @@
if dst in wctx:
wctx[dst].markcopied(src)
+
def computechangesetcopies(ctx):
"""return the copies data for a changeset