From b3208d45a6b2947b8e3612daa94ac17a29a6878f Mon Sep 17 00:00:00 2001 From: Kirill Smelkov Date: Fri, 28 Mar 2008 11:17:10 +0300 Subject: [PATCH 01/11] churn: allow whitespaces as delimiter in aliases it was exactly on space character before. --- hgext/churn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hgext/churn.py b/hgext/churn.py index cad2880b6a..33a6dcc8f7 100644 --- a/hgext/churn.py +++ b/hgext/churn.py @@ -155,7 +155,7 @@ def churn(ui, repo, **opts): for l in f.readlines(): l = l.strip() - alias, actual = l.split(" ") + alias, actual = l.split() aliases[alias] = actual return aliases From fc40a373043eaebadc5e623eec5ca94f09f896fd Mon Sep 17 00:00:00 2001 From: Matt Mackall Date: Sat, 29 Mar 2008 12:39:47 -0500 Subject: [PATCH 02/11] copies: fix silly precedence bug --- mercurial/copies.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mercurial/copies.py b/mercurial/copies.py index aeade394cd..a4ad1793f2 100644 --- a/mercurial/copies.py +++ b/mercurial/copies.py @@ -97,7 +97,7 @@ def copies(repo, c1, c2, ca): c2 = ctx(of, m2[of]) ca = c1.ancestor(c2) # related and named changed on only one side? - if ca and ca.path() == f or ca.path() == c2.path(): + if ca and (ca.path() == f or ca.path() == c2.path()): if c1 != ca or c2 != ca: # merge needed? copy[f] = of elif of in ma: From 8a7d38f2e2973698ff0ffc616332e9790bcc5664 Mon Sep 17 00:00:00 2001 From: Matt Mackall Date: Sat, 29 Mar 2008 12:39:47 -0500 Subject: [PATCH 03/11] backout: reverse changeset belongs on current branch Backing out a changeset that is before a named branch branchpoint was making the reverse changeset the tip of the old branch, which is wrong and very confusing. So instead, we put it on the current named branch. --- mercurial/commands.py | 3 +++ tests/test-backout.out | 9 +++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/mercurial/commands.py b/mercurial/commands.py index 2dea2e2407..1f3a17f22d 100644 --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -227,7 +227,10 @@ def backout(ui, repo, node=None, rev=None, **opts): raise util.Abort(_('cannot use --parent on non-merge changeset')) parent = p1 + # the backout should appear on the same branch + branch = repo.dirstate.branch() hg.clean(repo, node, show_stats=False) + repo.dirstate.setbranch(branch) revert_opts = opts.copy() revert_opts['date'] = None revert_opts['all'] = True diff --git a/tests/test-backout.out b/tests/test-backout.out index a81905e221..f245e525ed 100644 --- a/tests/test-backout.out +++ b/tests/test-backout.out @@ -74,7 +74,7 @@ marked working directory as branch branch2 adding file2 removing file1 created new head -changeset 3:f1c642b1d8e5 backs out changeset 1:bf1602f437f3 +changeset 3:d4e8f6db59fb backs out changeset 1:bf1602f437f3 the backout changeset is a new head - do not forget to merge (use "backout --merge" if you want to auto-merge) % on branch2 with branch1 not merged, so file1 should still exist: @@ -85,10 +85,11 @@ C file2 % on branch2 with branch1 merged, so file1 should be gone: 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (branch merge, don't forget to commit) -21d4dc6f9a41 (branch2) tip +22149cdde76d (branch2) tip C default C file2 % on branch1, so no file1 and file2: -0 files updated, 0 files merged, 1 files removed, 0 files unresolved -f1c642b1d8e5 (branch1) +1 files updated, 0 files merged, 1 files removed, 0 files unresolved +bf1602f437f3 (branch1) C default +C file1 From 1b20d834b6a73fa07b100b54e4d4000f8f2af568 Mon Sep 17 00:00:00 2001 From: Matt Mackall Date: Sat, 29 Mar 2008 12:39:47 -0500 Subject: [PATCH 04/11] copies: sort old names by depth --- mercurial/copies.py | 13 +++++++------ tests/test-diff-copy-depth | 31 +++++++++++++++++++++++++++++++ tests/test-diff-copy-depth.out | 20 ++++++++++++++++++++ 3 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 tests/test-diff-copy-depth create mode 100644 tests/test-diff-copy-depth.out diff --git a/mercurial/copies.py b/mercurial/copies.py index a4ad1793f2..9c4420021a 100644 --- a/mercurial/copies.py +++ b/mercurial/copies.py @@ -35,22 +35,23 @@ def _findoldnames(fctx, limit): old = {} seen = {} orig = fctx.path() - visit = [fctx] + visit = [(fctx, 0)] while visit: - fc = visit.pop() + fc, depth = visit.pop() s = str(fc) if s in seen: continue seen[s] = 1 if fc.path() != orig and fc.path() not in old: - old[fc.path()] = 1 + old[fc.path()] = (depth, fc.path()) # remember depth if fc.rev() < limit and fc.rev() is not None: continue - visit += fc.parents() + visit += [(p, depth - 1) for p in fc.parents()] - old = old.keys() + # return old names sorted by depth + old = old.values() old.sort() - return old + return [o[1] for o in old] def copies(repo, c1, c2, ca): """ diff --git a/tests/test-diff-copy-depth b/tests/test-diff-copy-depth new file mode 100644 index 0000000000..f4a7138c6e --- /dev/null +++ b/tests/test-diff-copy-depth @@ -0,0 +1,31 @@ +#!/bin/bash + +for i in aaa zzz; do + hg init t + cd t + + echo "-- With $i" + + touch file + hg add file + hg ci -m "Add" + + hg cp file $i + hg ci -m "a -> $i" + + hg cp $i other-file + echo "different" >> $i + hg ci -m "$i -> other-file" + + hg cp other-file somename + + echo "Status": + hg st -C + echo + echo "Diff:" + hg diff -g + echo + + cd .. + rm -rf t +done diff --git a/tests/test-diff-copy-depth.out b/tests/test-diff-copy-depth.out new file mode 100644 index 0000000000..bc31e058d5 --- /dev/null +++ b/tests/test-diff-copy-depth.out @@ -0,0 +1,20 @@ +-- With aaa +Status: +A somename + other-file + +Diff: +diff --git a/other-file b/somename +copy from other-file +copy to somename + +-- With zzz +Status: +A somename + other-file + +Diff: +diff --git a/other-file b/somename +copy from other-file +copy to somename + From c9895536f8684cfd95ac9e86a7c27d5fc472a44d Mon Sep 17 00:00:00 2001 From: Matt Mackall Date: Sat, 29 Mar 2008 12:39:47 -0500 Subject: [PATCH 05/11] copies: skip directory rename checks when not merging The directory rename checks are not meaningful unless we're moving or copying files across a branch during a merge. --- mercurial/copies.py | 10 +- mercurial/merge.py | 4 +- tests/test-mv-cp-st-diff | 8 + tests/test-mv-cp-st-diff.out | 318 +++++++++++++++++++++++++++++++++++ 4 files changed, 333 insertions(+), 7 deletions(-) diff --git a/mercurial/copies.py b/mercurial/copies.py index 9c4420021a..19aed5a877 100644 --- a/mercurial/copies.py +++ b/mercurial/copies.py @@ -53,7 +53,7 @@ def _findoldnames(fctx, limit): old.sort() return [o[1] for o in old] -def copies(repo, c1, c2, ca): +def copies(repo, c1, c2, ca, checkdirs=False): """ Find moves and copies between context c1 and c2 """ @@ -104,9 +104,6 @@ def copies(repo, c1, c2, ca): elif of in ma: diverge.setdefault(of, []).append(f) - if not repo.ui.configbool("merge", "followcopies", True): - return {}, {} - repo.ui.debug(_(" searching for copies back to rev %d\n") % limit) u1 = _nonoverlap(m1, m2, ma) @@ -140,7 +137,7 @@ def copies(repo, c1, c2, ca): repo.ui.debug(_(" %s -> %s %s\n") % (f, fullcopy[f], note)) del diverge2 - if not fullcopy or not repo.ui.configbool("merge", "followdirs", True): + if not fullcopy or not checkdirs: return copy, diverge repo.ui.debug(_(" checking for directory renames\n")) @@ -187,7 +184,8 @@ def copies(repo, c1, c2, ca): for d in dirmove: if f.startswith(d): # new file added in a directory that was moved, move it - copy[f] = dirmove[d] + f[len(d):] + df = dirmove[d] + f[len(d):] + copy[f] = df repo.ui.debug(_(" file %s -> %s\n") % (f, copy[f])) break diff --git a/mercurial/merge.py b/mercurial/merge.py index b311f750ed..793915822f 100644 --- a/mercurial/merge.py +++ b/mercurial/merge.py @@ -101,7 +101,9 @@ def manifestmerge(repo, p1, p2, pa, overwrite, partial): action.append((f, m) + args) if pa and not (backwards or overwrite): - copy, diverge = copies.copies(repo, p1, p2, pa) + if repo.ui.configbool("merge", "followcopies", True): + dirs = repo.ui.configbool("merge", "followdirs", True) + copy, diverge = copies.copies(repo, p1, p2, pa, dirs) copied = dict.fromkeys(copy.values()) for of, fl in diverge.items(): act("divergent renames", "dr", of, fl) diff --git a/tests/test-mv-cp-st-diff b/tests/test-mv-cp-st-diff index d64e37a560..0f153da11f 100755 --- a/tests/test-mv-cp-st-diff +++ b/tests/test-mv-cp-st-diff @@ -11,12 +11,17 @@ cd t # set up a boring main branch add a a hg add a +mkdir x +add x/x x +hg add x/x hg ci -m0 add a m1 hg ci -m1 add a m2 +add x/y y1 +hg add x/y hg ci -m2 show() @@ -59,6 +64,7 @@ tb() echo } + tb "add a a1" "add a a2" "hg mv a b" "rename in working dir" tb "add a a1" "add a a2" "hg cp a b" "copy in working dir" tb "hg mv a b" "add b b1" "add b w" "single rename" @@ -66,3 +72,5 @@ tb "hg cp a b" "add b b1" "add a w" "single copy" tb "hg mv a b" "hg mv b c" "hg mv c d" "rename chain" tb "hg cp a b" "hg cp b c" "hg cp c d" "copy chain" tb "add a a1" "hg mv a b" "hg mv b a" "circular rename" + +tb "hg mv x y" "add y/x x1" "add y/x x2" "directory move" diff --git a/tests/test-mv-cp-st-diff.out b/tests/test-mv-cp-st-diff.out index c617d1198e..32060a2a87 100644 --- a/tests/test-mv-cp-st-diff.out +++ b/tests/test-mv-cp-st-diff.out @@ -30,6 +30,7 @@ rename to b A b a R a +R x/y diff --git a/a b/b rename from a @@ -43,6 +44,12 @@ rename to b +0 +a1 +a2 +diff --git a/x/y b/x/y +deleted file mode 100644 +--- a/x/y ++++ /dev/null +@@ -1,1 +0,0 @@ +-y1 - root to parent: --rev 0 --rev . M a @@ -70,6 +77,7 @@ diff --git a/a b/a - branch to parent: --rev 2 --rev . M a +R x/y diff --git a/a b/a --- a/a @@ -81,9 +89,16 @@ diff --git a/a b/a +0 +a1 +a2 +diff --git a/x/y b/x/y +deleted file mode 100644 +--- a/x/y ++++ /dev/null +@@ -1,1 +0,0 @@ +-y1 - parent to branch: --rev . --rev 2 M a +A x/y diff --git a/a b/a --- a/a @@ -95,6 +110,12 @@ diff --git a/a b/a -a2 +m1 +m2 +diff --git a/x/y b/x/y +new file mode 100644 +--- /dev/null ++++ b/x/y +@@ -0,0 +1,1 @@ ++y1 created new head @@ -136,6 +157,7 @@ copy to b M a A b a +R x/y diff --git a/a b/a --- a/a @@ -159,6 +181,12 @@ copy to b +1 +a1 +a2 +diff --git a/x/y b/x/y +deleted file mode 100644 +--- a/x/y ++++ /dev/null +@@ -1,1 +0,0 @@ +-y1 - root to parent: --rev 0 --rev . M a @@ -186,6 +214,7 @@ diff --git a/a b/a - branch to parent: --rev 2 --rev . M a +R x/y diff --git a/a b/a --- a/a @@ -197,9 +226,16 @@ diff --git a/a b/a +1 +a1 +a2 +diff --git a/x/y b/x/y +deleted file mode 100644 +--- a/x/y ++++ /dev/null +@@ -1,1 +0,0 @@ +-y1 - parent to branch: --rev . --rev 2 M a +A x/y diff --git a/a b/a --- a/a @@ -211,6 +247,12 @@ diff --git a/a b/a -a2 +m1 +m2 +diff --git a/x/y b/x/y +new file mode 100644 +--- /dev/null ++++ b/x/y +@@ -0,0 +1,1 @@ ++y1 created new head @@ -248,6 +290,7 @@ rename to b A b a R a +R x/y diff --git a/a b/b rename from a @@ -261,6 +304,12 @@ rename to b +2 +b1 +w +diff --git a/x/y b/x/y +deleted file mode 100644 +--- a/x/y ++++ /dev/null +@@ -1,1 +0,0 @@ +-y1 - root to parent: --rev 0 --rev . A b @@ -296,6 +345,7 @@ rename to a A b a R a +R x/y diff --git a/a b/b rename from a @@ -308,10 +358,17 @@ rename to b -m2 +2 +b1 +diff --git a/x/y b/x/y +deleted file mode 100644 +--- a/x/y ++++ /dev/null +@@ -1,1 +0,0 @@ +-y1 - parent to branch: --rev . --rev 2 A a b +A x/y R b diff --git a/b b/a @@ -325,6 +382,12 @@ rename to a -b1 +m1 +m2 +diff --git a/x/y b/x/y +new file mode 100644 +--- /dev/null ++++ b/x/y +@@ -0,0 +1,1 @@ ++y1 created new head @@ -367,6 +430,7 @@ copy to b M a A b a +R x/y diff --git a/a b/a --- a/a @@ -388,6 +452,12 @@ copy to b -m2 +3 +b1 +diff --git a/x/y b/x/y +deleted file mode 100644 +--- a/x/y ++++ /dev/null +@@ -1,1 +0,0 @@ +-y1 - root to parent: --rev 0 --rev . M a @@ -433,6 +503,7 @@ deleted file mode 100644 M a A b a +R x/y diff --git a/a b/a --- a/a @@ -453,9 +524,16 @@ copy to b -m2 +3 +b1 +diff --git a/x/y b/x/y +deleted file mode 100644 +--- a/x/y ++++ /dev/null +@@ -1,1 +0,0 @@ +-y1 - parent to branch: --rev . --rev 2 M a +A x/y R b diff --git a/a b/a @@ -474,6 +552,12 @@ deleted file mode 100644 -a -3 -b1 +diff --git a/x/y b/x/y +new file mode 100644 +--- /dev/null ++++ b/x/y +@@ -0,0 +1,1 @@ ++y1 created new head @@ -506,6 +590,7 @@ rename to d A d a R a +R x/y diff --git a/a b/d rename from a @@ -517,6 +602,12 @@ rename to d -m1 -m2 +4 +diff --git a/x/y b/x/y +deleted file mode 100644 +--- a/x/y ++++ /dev/null +@@ -1,1 +0,0 @@ +-y1 - root to parent: --rev 0 --rev . A c @@ -550,6 +641,7 @@ rename to a A c a R a +R x/y diff --git a/a b/c rename from a @@ -561,10 +653,17 @@ rename to c -m1 -m2 +4 +diff --git a/x/y b/x/y +deleted file mode 100644 +--- a/x/y ++++ /dev/null +@@ -1,1 +0,0 @@ +-y1 - parent to branch: --rev . --rev 2 A a c +A x/y R c diff --git a/c b/a @@ -577,6 +676,12 @@ rename to a -4 +m1 +m2 +diff --git a/x/y b/x/y +new file mode 100644 +--- /dev/null ++++ b/x/y +@@ -0,0 +1,1 @@ ++y1 created new head @@ -638,6 +743,7 @@ A c a A d a +R x/y diff --git a/a b/a --- a/a @@ -677,6 +783,12 @@ copy to d -m1 -m2 +5 +diff --git a/x/y b/x/y +deleted file mode 100644 +--- a/x/y ++++ /dev/null +@@ -1,1 +0,0 @@ +-y1 - root to parent: --rev 0 --rev . M a @@ -740,6 +852,7 @@ A b a A c a +R x/y diff --git a/a b/a --- a/a @@ -769,9 +882,16 @@ copy to c -m1 -m2 +5 +diff --git a/x/y b/x/y +deleted file mode 100644 +--- a/x/y ++++ /dev/null +@@ -1,1 +0,0 @@ +-y1 - parent to branch: --rev . --rev 2 M a +A x/y R b R c @@ -797,6 +917,12 @@ deleted file mode 100644 @@ -1,2 +0,0 @@ -a -5 +diff --git a/x/y b/x/y +new file mode 100644 +--- /dev/null ++++ b/x/y +@@ -0,0 +1,1 @@ ++y1 created new head @@ -824,6 +950,7 @@ diff --git a/a b/a - working to branch: --rev 2 M a +R x/y diff --git a/a b/a --- a/a @@ -834,6 +961,12 @@ diff --git a/a b/a -m2 +6 +a1 +diff --git a/x/y b/x/y +deleted file mode 100644 +--- a/x/y ++++ /dev/null +@@ -1,1 +0,0 @@ +-y1 - root to parent: --rev 0 --rev . A b @@ -869,6 +1002,7 @@ rename to a A b a R a +R x/y diff --git a/a b/b rename from a @@ -881,10 +1015,17 @@ rename to b -m2 +6 +a1 +diff --git a/x/y b/x/y +deleted file mode 100644 +--- a/x/y ++++ /dev/null +@@ -1,1 +0,0 @@ +-y1 - parent to branch: --rev . --rev 2 A a b +A x/y R b diff --git a/b b/a @@ -898,5 +1039,182 @@ rename to a -a1 +m1 +m2 +diff --git a/x/y b/x/y +new file mode 100644 +--- /dev/null ++++ b/x/y +@@ -0,0 +1,1 @@ ++y1 + + +created new head +moving x/x to y/x +** directory move ** +** hg mv x y / add y/x x1 / add y/x x2 +- working to parent: +M y/x + +diff --git a/y/x b/y/x +--- a/y/x ++++ b/y/x +@@ -1,2 +1,3 @@ + x + x1 ++x2 + +- working to root: --rev 0 +M a +A y/x + x/x +R x/x + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,1 +1,2 @@ + a ++7 +diff --git a/x/x b/y/x +rename from x/x +rename to y/x +--- a/x/x ++++ b/y/x +@@ -1,1 +1,3 @@ + x ++x1 ++x2 + +- working to branch: --rev 2 +M a +A y/x + x/x +R x/x +R x/y + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,3 +1,2 @@ + a +-m1 +-m2 ++7 +diff --git a/x/y b/x/y +deleted file mode 100644 +--- a/x/y ++++ /dev/null +@@ -1,1 +0,0 @@ +-y1 +diff --git a/x/x b/y/x +rename from x/x +rename to y/x +--- a/x/x ++++ b/y/x +@@ -1,1 +1,3 @@ + x ++x1 ++x2 + +- root to parent: --rev 0 --rev . +M a +A y/x + x/x +R x/x + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,1 +1,2 @@ + a ++7 +diff --git a/x/x b/y/x +rename from x/x +rename to y/x +--- a/x/x ++++ b/y/x +@@ -1,1 +1,2 @@ + x ++x1 + +- parent to root: --rev . --rev 0 +M a +A x/x + y/x +R y/x + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,2 +1,1 @@ + a +-7 +diff --git a/y/x b/x/x +rename from y/x +rename to x/x +--- a/y/x ++++ b/x/x +@@ -1,2 +1,1 @@ + x +-x1 + +- branch to parent: --rev 2 --rev . +M a +A y/x + x/x +R x/x +R x/y + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,3 +1,2 @@ + a +-m1 +-m2 ++7 +diff --git a/x/y b/x/y +deleted file mode 100644 +--- a/x/y ++++ /dev/null +@@ -1,1 +0,0 @@ +-y1 +diff --git a/x/x b/y/x +rename from x/x +rename to y/x +--- a/x/x ++++ b/y/x +@@ -1,1 +1,2 @@ + x ++x1 + +- parent to branch: --rev . --rev 2 +M a +A x/x + y/x +A x/y +R y/x + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,2 +1,3 @@ + a +-7 ++m1 ++m2 +diff --git a/y/x b/x/x +rename from y/x +rename to x/x +--- a/y/x ++++ b/x/x +@@ -1,2 +1,1 @@ + x +-x1 +diff --git a/x/y b/x/y +new file mode 100644 +--- /dev/null ++++ b/x/y +@@ -0,0 +1,1 @@ ++y1 From ba8f438e7a6d3f1f2b7fb4cd7fd2a858d4860350 Mon Sep 17 00:00:00 2001 From: Matt Mackall Date: Sat, 29 Mar 2008 12:39:47 -0500 Subject: [PATCH 06/11] copies: don't double-detect items in the directory copy check --- mercurial/copies.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mercurial/copies.py b/mercurial/copies.py index 19aed5a877..9a1fe57349 100644 --- a/mercurial/copies.py +++ b/mercurial/copies.py @@ -185,8 +185,9 @@ def copies(repo, c1, c2, ca, checkdirs=False): if f.startswith(d): # new file added in a directory that was moved, move it df = dirmove[d] + f[len(d):] - copy[f] = df - repo.ui.debug(_(" file %s -> %s\n") % (f, copy[f])) + if df not in copy: + copy[f] = df + repo.ui.debug(_(" file %s -> %s\n") % (f, copy[f])) break return copy, diverge From 469b1769731c01ee0be5239af75bd81028db2f12 Mon Sep 17 00:00:00 2001 From: Matt Mackall Date: Sat, 29 Mar 2008 12:39:47 -0500 Subject: [PATCH 07/11] ancestors: simplify symmetric difference - n_wanted/wanted -> interesting - scan colors rather than managing ret --- mercurial/ancestor.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/mercurial/ancestor.py b/mercurial/ancestor.py index 90744b810a..37a3087c5b 100644 --- a/mercurial/ancestor.py +++ b/mercurial/ancestor.py @@ -107,28 +107,24 @@ def symmetricdifference(a, b, pfunc): visit = [-a, -b] heapq.heapify(visit) - n_wanted = len(visit) - ret = [] + interesting = len(visit) - while n_wanted: + while interesting: r = -heapq.heappop(visit) - wanted = colors[r] != ALLCOLORS - n_wanted -= wanted - if wanted: - ret.append(r) + if colors[r] != ALLCOLORS: + interesting -= 1 for p in pfunc(r): if p not in colors: # first time we see p; add it to visit - n_wanted += wanted colors[p] = colors[r] + if colors[p] != ALLCOLORS: + interesting += 1 heapq.heappush(visit, -p) elif colors[p] != ALLCOLORS and colors[p] != colors[r]: # at first we thought we wanted p, but now # we know we don't really want it - n_wanted -= 1 colors[p] |= colors[r] + interesting -= 1 - del colors[r] - - return ret + return [r for r in colors if colors[r] != ALLCOLORS] From ab64062215f1de6f4710eaa147743d228921bbe5 Mon Sep 17 00:00:00 2001 From: Matt Mackall Date: Sat, 29 Mar 2008 12:39:47 -0500 Subject: [PATCH 08/11] symmetricdifference: change colors to sides --- mercurial/ancestor.py | 39 ++++++++++++++++----------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/mercurial/ancestor.py b/mercurial/ancestor.py index 37a3087c5b..d395ab57e1 100644 --- a/mercurial/ancestor.py +++ b/mercurial/ancestor.py @@ -88,43 +88,36 @@ def symmetricdifference(a, b, pfunc): I.e. revisions that are ancestors of a or b, but not both. """ # basic idea: - # - mark a and b with different colors + # - mark a and b with different sides + # - if a parent's children are all on the same side, the parent is + # on that side, otherwise it is on no side # - walk the graph in topological order with the help of a heap; - # for each revision r: - # - if r has only one color, we want to return it - # - add colors[r] to its parents - # - # We keep track of the number of revisions in the heap that - # we may be interested in. We stop walking the graph as soon - # as this number reaches 0. + # - add unseen parents to side map + # - clear side of any parent that has children on different sides + # - track number of unvisited nodes that might still be on a side + # - quit when interesting nodes is zero if a == b: return [a] - WHITE = 1 - BLACK = 2 - ALLCOLORS = WHITE | BLACK - colors = {a: WHITE, b: BLACK} - + side = {a: -1, b: 1} visit = [-a, -b] heapq.heapify(visit) interesting = len(visit) while interesting: r = -heapq.heappop(visit) - if colors[r] != ALLCOLORS: + if side[r]: interesting -= 1 - for p in pfunc(r): - if p not in colors: + if p not in side: # first time we see p; add it to visit - colors[p] = colors[r] - if colors[p] != ALLCOLORS: + side[p] = side[r] + if side[p]: interesting += 1 heapq.heappush(visit, -p) - elif colors[p] != ALLCOLORS and colors[p] != colors[r]: - # at first we thought we wanted p, but now - # we know we don't really want it - colors[p] |= colors[r] + elif side[p] and side[p] != side[r]: + # p was interesting but now we know better + side[p] = 0 interesting -= 1 - return [r for r in colors if colors[r] != ALLCOLORS] + return [r for r in side if side[r]] From 56e621f7bc12e14ba644f6b56cbb9e45e6cd46ed Mon Sep 17 00:00:00 2001 From: Matt Mackall Date: Sat, 29 Mar 2008 12:39:47 -0500 Subject: [PATCH 09/11] symmetricdifference: move back to copies It's too tightly dependent on known revlog ordering to fit well in ancestors --- mercurial/ancestor.py | 40 ---------------------------------------- mercurial/copies.py | 41 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 42 deletions(-) diff --git a/mercurial/ancestor.py b/mercurial/ancestor.py index d395ab57e1..9ec2843220 100644 --- a/mercurial/ancestor.py +++ b/mercurial/ancestor.py @@ -81,43 +81,3 @@ def ancestor(a, b, pfunc): gx = x.next() except StopIteration: return None - -def symmetricdifference(a, b, pfunc): - """symmetric difference of the sets of ancestors of a and b - - I.e. revisions that are ancestors of a or b, but not both. - """ - # basic idea: - # - mark a and b with different sides - # - if a parent's children are all on the same side, the parent is - # on that side, otherwise it is on no side - # - walk the graph in topological order with the help of a heap; - # - add unseen parents to side map - # - clear side of any parent that has children on different sides - # - track number of unvisited nodes that might still be on a side - # - quit when interesting nodes is zero - if a == b: - return [a] - - side = {a: -1, b: 1} - visit = [-a, -b] - heapq.heapify(visit) - interesting = len(visit) - - while interesting: - r = -heapq.heappop(visit) - if side[r]: - interesting -= 1 - for p in pfunc(r): - if p not in side: - # first time we see p; add it to visit - side[p] = side[r] - if side[p]: - interesting += 1 - heapq.heappush(visit, -p) - elif side[p] and side[p] != side[r]: - # p was interesting but now we know better - side[p] = 0 - interesting -= 1 - - return [r for r in side if side[r]] diff --git a/mercurial/copies.py b/mercurial/copies.py index 9a1fe57349..89a79b6042 100644 --- a/mercurial/copies.py +++ b/mercurial/copies.py @@ -7,7 +7,7 @@ from node import nullid, nullrev from i18n import _ -import util, ancestor +import util, heapq def _nonoverlap(d1, d2, d3): "Return list of elements in d1 not in d2 or d3" @@ -53,6 +53,43 @@ def _findoldnames(fctx, limit): old.sort() return [o[1] for o in old] +def _symmetricdifference(a, b, pfunc): + """find revisions that are ancestors of a or b, but not both""" + # basic idea: + # - mark a and b with different sides + # - if a parent's children are all on the same side, the parent is + # on that side, otherwise it is on no side + # - walk the graph in topological order with the help of a heap; + # - add unseen parents to side map + # - clear side of any parent that has children on different sides + # - track number of unvisited nodes that might still be on a side + # - quit when interesting nodes is zero + if a == b: + return [a] + + side = {a: -1, b: 1} + visit = [-a, -b] + heapq.heapify(visit) + interesting = len(visit) + + while interesting: + r = -heapq.heappop(visit) + if side[r]: + interesting -= 1 + for p in pfunc(r): + if p not in side: + # first time we see p; add it to visit + side[p] = side[r] + if side[p]: + interesting += 1 + heapq.heappush(visit, -p) + elif side[p] and side[p] != side[r]: + # p was interesting but now we know better + side[p] = 0 + interesting -= 1 + + return [r for r in side if side[r]] + def copies(repo, c1, c2, ca, checkdirs=False): """ Find moves and copies between context c1 and c2 @@ -69,7 +106,7 @@ def copies(repo, c1, c2, ca, checkdirs=False): pr = repo.changelog.parentrevs def parents(rev): return [p for p in pr(rev) if p != nullrev] - limit = min(ancestor.symmetricdifference(rev1, rev2, parents)) + limit = min(_symmetricdifference(rev1, rev2, parents)) m1 = c1.manifest() m2 = c2.manifest() ma = ca.manifest() From 95434837e145199ba3cdeaa4016870a2d0fd49bc Mon Sep 17 00:00:00 2001 From: Matt Mackall Date: Sat, 29 Mar 2008 12:39:47 -0500 Subject: [PATCH 10/11] copies: teach symmetric difference about working revisions - use changelog.count() as a pseudo revision number - abort early in copies if revs are the same - eliminate working dir hacks in copies - yield results as they're found --- mercurial/copies.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/mercurial/copies.py b/mercurial/copies.py index 89a79b6042..b04cf14bdf 100644 --- a/mercurial/copies.py +++ b/mercurial/copies.py @@ -53,7 +53,7 @@ def _findoldnames(fctx, limit): old.sort() return [o[1] for o in old] -def _symmetricdifference(a, b, pfunc): +def _symmetricdifference(repo, a, b): """find revisions that are ancestors of a or b, but not both""" # basic idea: # - mark a and b with different sides @@ -64,8 +64,13 @@ def _symmetricdifference(a, b, pfunc): # - clear side of any parent that has children on different sides # - track number of unvisited nodes that might still be on a side # - quit when interesting nodes is zero - if a == b: - return [a] + + cl = repo.changelog + working = cl.count() + if a is None: + a = working + if b is None: + b = working side = {a: -1, b: 1} visit = [-a, -b] @@ -74,9 +79,11 @@ def _symmetricdifference(a, b, pfunc): while interesting: r = -heapq.heappop(visit) - if side[r]: - interesting -= 1 - for p in pfunc(r): + if r == working: + parents = [cl.rev(p) for p in repo.dirstate.parents()] + else: + parents = cl.parentrevs(r) + for p in parents: if p not in side: # first time we see p; add it to visit side[p] = side[r] @@ -87,26 +94,19 @@ def _symmetricdifference(a, b, pfunc): # p was interesting but now we know better side[p] = 0 interesting -= 1 - - return [r for r in side if side[r]] + if side[r]: + interesting -= 1 + yield r def copies(repo, c1, c2, ca, checkdirs=False): """ Find moves and copies between context c1 and c2 """ # avoid silly behavior for update from empty dir - if not c1 or not c2: + if not c1 or not c2 or c1 == c2: return {}, {} - rev1, rev2 = c1.rev(), c2.rev() - if rev1 is None: # c1 is a workingctx - rev1 = c1.parents()[0].rev() - if rev2 is None: # c2 is a workingctx - rev2 = c2.parents()[0].rev() - pr = repo.changelog.parentrevs - def parents(rev): - return [p for p in pr(rev) if p != nullrev] - limit = min(_symmetricdifference(rev1, rev2, parents)) + limit = min(_symmetricdifference(repo, c1.rev(), c2.rev())) m1 = c1.manifest() m2 = c2.manifest() ma = ca.manifest() From 52b4d9ec255895466b822b998f0519c49e051328 Mon Sep 17 00:00:00 2001 From: Matt Mackall Date: Sat, 29 Mar 2008 12:39:47 -0500 Subject: [PATCH 11/11] copies: refactor symmetricdifference as _findlimit We only need to track the lowest revision seen, which makes things simpler. --- mercurial/copies.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/mercurial/copies.py b/mercurial/copies.py index b04cf14bdf..3447948b49 100644 --- a/mercurial/copies.py +++ b/mercurial/copies.py @@ -53,8 +53,8 @@ def _findoldnames(fctx, limit): old.sort() return [o[1] for o in old] -def _symmetricdifference(repo, a, b): - """find revisions that are ancestors of a or b, but not both""" +def _findlimit(repo, a, b): + "find the earliest revision that's an ancestor of a or b but not both" # basic idea: # - mark a and b with different sides # - if a parent's children are all on the same side, the parent is @@ -62,11 +62,12 @@ def _symmetricdifference(repo, a, b): # - walk the graph in topological order with the help of a heap; # - add unseen parents to side map # - clear side of any parent that has children on different sides - # - track number of unvisited nodes that might still be on a side - # - quit when interesting nodes is zero + # - track number of interesting revs that might still be on a side + # - track the lowest interesting rev seen + # - quit when interesting revs is zero cl = repo.changelog - working = cl.count() + working = cl.count() # pseudo rev for the working directory if a is None: a = working if b is None: @@ -76,6 +77,7 @@ def _symmetricdifference(repo, a, b): visit = [-a, -b] heapq.heapify(visit) interesting = len(visit) + limit = working while interesting: r = -heapq.heappop(visit) @@ -95,8 +97,9 @@ def _symmetricdifference(repo, a, b): side[p] = 0 interesting -= 1 if side[r]: + limit = r # lowest rev visited interesting -= 1 - yield r + return limit def copies(repo, c1, c2, ca, checkdirs=False): """ @@ -106,7 +109,7 @@ def copies(repo, c1, c2, ca, checkdirs=False): if not c1 or not c2 or c1 == c2: return {}, {} - limit = min(_symmetricdifference(repo, c1.rev(), c2.rev())) + limit = _findlimit(repo, c1.rev(), c2.rev()) m1 = c1.manifest() m2 = c2.manifest() ma = ca.manifest()