diff --git a/mercurial/configitems.py b/mercurial/configitems.py --- a/mercurial/configitems.py +++ b/mercurial/configitems.py @@ -330,6 +330,9 @@ coreconfigitem('experimental', 'copytrace', default='on', ) +coreconfigitem('experimental', 'copytrace.movecandidateslimit', + default=100, +) coreconfigitem('experimental', 'copytrace.sourcecommitlimit', default=100, ) diff --git a/mercurial/copies.py b/mercurial/copies.py --- a/mercurial/copies.py +++ b/mercurial/copies.py @@ -11,6 +11,8 @@ import heapq import os +from .i18n import _ + from . import ( match as matchmod, node, @@ -644,6 +646,11 @@ [experimental] copytrace = heuristics + + In some cases the copy/move candidates found by heuristics can be very large + in number and that will make the algorithm slow. The number of possible + candidates to check can be limited by using the config + `experimental.copytrace.movecandidateslimit` which defaults to 100. """ if c1.rev() is None: @@ -704,6 +711,17 @@ # f is guaranteed to be present in c2, that's why # c2.filectx(f) won't fail 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') + + if len(movecandidates) > maxcandidates: + repo.ui.status(_("skipping copytracing for '%s', more " + "candidates than the limit: %d\n") + % (f, len(movecandidates))) + continue + for candidate in movecandidates: f1 = c1.filectx(candidate) if _related(f1, f2, anc.rev()): diff --git a/tests/test-copytrace-heuristics.t b/tests/test-copytrace-heuristics.t --- a/tests/test-copytrace-heuristics.t +++ b/tests/test-copytrace-heuristics.t @@ -201,6 +201,65 @@ $ cd .. $ rm -rf repo +Test the copytrace.movecandidateslimit with many move candidates +---------------------------------------------------------------- + + $ hg init repo + $ initclient repo + $ cd repo + $ echo a > a + $ hg add a + $ hg ci -m initial + $ hg mv a foo + $ echo a > b + $ echo a > c + $ echo a > d + $ echo a > e + $ echo a > f + $ echo a > g + $ hg add b + $ hg add c + $ hg add d + $ hg add e + $ hg add f + $ hg add g + $ hg ci -m 'mv a foo, add many files' + $ hg up -q ".^" + $ echo b > a + $ hg ci -m 'mod a' + created new head + + $ hg log -G -T 'changeset: {node}\n desc: {desc}\n' + @ changeset: ef716627c70bf4ca0bdb623cfb0d6fe5b9acc51e + | desc: mod a + | o changeset: 8329d5c6bf479ec5ca59b9864f3f45d07213f5a4 + |/ desc: mv a foo, add many files + o changeset: 1451231c87572a7d3f92fc210b4b35711c949a98 + desc: initial + +With small limit + + $ hg rebase -s 2 -d 1 --config experimental.copytrace.movecandidateslimit=0 + rebasing 2:ef716627c70b "mod a" (tip) + skipping copytracing for 'a', more candidates than the limit: 7 + other [source] changed a which local [dest] deleted + use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u + unresolved conflicts (see hg resolve, then hg rebase --continue) + [1] + + $ hg rebase --abort + rebase aborted + +With default limit which is 100 + + $ hg rebase -s 2 -d 1 + rebasing 2:ef716627c70b "mod a" (tip) + merging foo and a to foo + saved backup bundle to $TESTTMP/repo/.hg/strip-backup/ef716627c70b-24681561-rebase.hg (glob) + + $ cd .. + $ rm -rf repo + Move file in one branch and delete it in another -----------------------------------------------