Mercurial > hg-stable
view tests/test-subrepo-recursion.t @ 32200:4d504e541d3d
rebase: use matcher to optimize manifestmerge
The old merge code would call manifestmerge and calculate the complete diff
between the source to the destination. In many cases, like rebase, the vast
majority of differences between the source and destination are irrelevant
because they are differences between the destination and the common ancestor
only, and therefore don't affect the merge. Since most actions are 'keep', all
the effort to compute them is wasted.
Instead, let's compute the difference between the source and the common ancestor
and only perform the diff of those files against the merge destination. When
using treemanifest, this lets us avoid loading almost the entire tree when
rebasing from a very old ancestor. This speeds up rebase of an old stack of 27
commits by 20x.
In mozilla-central, without treemanifest, when rebasing a commit from
default~100000 to default, this speeds up the manifestmerge step from 2.6s to
1.2s. However, the additional diff adds an overhead to all manifestmerge calls,
especially for flat manifests. When rebasing a commit from default~1 to default
it appears to add 100ms in mozilla-central. While we could put this optimization
behind a flag, I think the fact that it makes merge O(number of changes being
applied) instead of O(number of changes between X and Y) justifies it.
author | Durham Goode <durham@fb.com> |
---|---|
date | Wed, 03 May 2017 10:43:59 -0700 |
parents | 2406dbba49bd |
children | 6d88468d435b |
line wrap: on
line source
Create test repository: $ hg init repo $ cd repo $ echo x1 > x.txt $ hg init foo $ cd foo $ echo y1 > y.txt $ hg init bar $ cd bar $ echo z1 > z.txt $ cd .. $ echo 'bar = bar' > .hgsub $ cd .. $ echo 'foo = foo' > .hgsub Add files --- .hgsub files must go first to trigger subrepos: $ hg add -S .hgsub $ hg add -S foo/.hgsub $ hg add -S foo/bar adding foo/bar/z.txt (glob) $ hg add -S adding x.txt adding foo/y.txt (glob) Test recursive status without committing anything: $ hg status -S A .hgsub A foo/.hgsub A foo/bar/z.txt A foo/y.txt A x.txt Test recursive diff without committing anything: $ hg diff --nodates -S foo diff -r 000000000000 foo/.hgsub --- /dev/null +++ b/foo/.hgsub @@ -0,0 +1,1 @@ +bar = bar diff -r 000000000000 foo/y.txt --- /dev/null +++ b/foo/y.txt @@ -0,0 +1,1 @@ +y1 diff -r 000000000000 foo/bar/z.txt --- /dev/null +++ b/foo/bar/z.txt @@ -0,0 +1,1 @@ +z1 Commits: $ hg commit -m fails abort: uncommitted changes in subrepository 'foo' (use --subrepos for recursive commit) [255] The --subrepos flag overwrite the config setting: $ hg commit -m 0-0-0 --config ui.commitsubrepos=No --subrepos committing subrepository foo committing subrepository foo/bar (glob) $ cd foo $ echo y2 >> y.txt $ hg commit -m 0-1-0 $ cd bar $ echo z2 >> z.txt $ hg commit -m 0-1-1 $ cd .. $ hg commit -m 0-2-1 $ cd .. $ hg commit -m 1-2-1 Change working directory: $ echo y3 >> foo/y.txt $ echo z3 >> foo/bar/z.txt $ hg status -S M foo/bar/z.txt M foo/y.txt $ hg diff --nodates -S diff -r d254738c5f5e foo/y.txt --- a/foo/y.txt +++ b/foo/y.txt @@ -1,2 +1,3 @@ y1 y2 +y3 diff -r 9647f22de499 foo/bar/z.txt --- a/foo/bar/z.txt +++ b/foo/bar/z.txt @@ -1,2 +1,3 @@ z1 z2 +z3 Status call crossing repository boundaries: $ hg status -S foo/bar/z.txt M foo/bar/z.txt $ hg status -S -I 'foo/?.txt' M foo/y.txt $ hg status -S -I '**/?.txt' M foo/bar/z.txt M foo/y.txt $ hg diff --nodates -S -I '**/?.txt' diff -r d254738c5f5e foo/y.txt --- a/foo/y.txt +++ b/foo/y.txt @@ -1,2 +1,3 @@ y1 y2 +y3 diff -r 9647f22de499 foo/bar/z.txt --- a/foo/bar/z.txt +++ b/foo/bar/z.txt @@ -1,2 +1,3 @@ z1 z2 +z3 Status from within a subdirectory: $ mkdir dir $ cd dir $ echo a1 > a.txt $ hg status -S M foo/bar/z.txt M foo/y.txt ? dir/a.txt $ hg diff --nodates -S diff -r d254738c5f5e foo/y.txt --- a/foo/y.txt +++ b/foo/y.txt @@ -1,2 +1,3 @@ y1 y2 +y3 diff -r 9647f22de499 foo/bar/z.txt --- a/foo/bar/z.txt +++ b/foo/bar/z.txt @@ -1,2 +1,3 @@ z1 z2 +z3 Status with relative path: $ hg status -S .. M ../foo/bar/z.txt M ../foo/y.txt ? a.txt XXX: filtering lfilesrepo.status() in 3.3-rc causes these files to be listed as added instead of modified. $ hg status -S .. --config extensions.largefiles= M ../foo/bar/z.txt M ../foo/y.txt ? a.txt $ hg diff --nodates -S .. diff -r d254738c5f5e foo/y.txt --- a/foo/y.txt +++ b/foo/y.txt @@ -1,2 +1,3 @@ y1 y2 +y3 diff -r 9647f22de499 foo/bar/z.txt --- a/foo/bar/z.txt +++ b/foo/bar/z.txt @@ -1,2 +1,3 @@ z1 z2 +z3 $ cd .. Cleanup and final commit: $ rm -r dir $ hg commit --subrepos -m 2-3-2 committing subrepository foo committing subrepository foo/bar (glob) Test explicit path commands within subrepos: add/forget $ echo z1 > foo/bar/z2.txt $ hg status -S ? foo/bar/z2.txt $ hg add foo/bar/z2.txt $ hg status -S A foo/bar/z2.txt $ hg forget foo/bar/z2.txt $ hg status -S ? foo/bar/z2.txt $ hg forget foo/bar/z2.txt not removing foo/bar/z2.txt: file is already untracked (glob) [1] $ hg status -S ? foo/bar/z2.txt $ rm foo/bar/z2.txt Log with the relationships between repo and its subrepo: $ hg log --template '{rev}:{node|short} {desc}\n' 2:1326fa26d0c0 2-3-2 1:4b3c9ff4f66b 1-2-1 0:23376cbba0d8 0-0-0 $ hg -R foo log --template '{rev}:{node|short} {desc}\n' 3:65903cebad86 2-3-2 2:d254738c5f5e 0-2-1 1:8629ce7dcc39 0-1-0 0:af048e97ade2 0-0-0 $ hg -R foo/bar log --template '{rev}:{node|short} {desc}\n' 2:31ecbdafd357 2-3-2 1:9647f22de499 0-1-1 0:4904098473f9 0-0-0 Status between revisions: $ hg status -S $ hg status -S --rev 0:1 M .hgsubstate M foo/.hgsubstate M foo/bar/z.txt M foo/y.txt $ hg diff --nodates -S -I '**/?.txt' --rev 0:1 diff -r af048e97ade2 -r d254738c5f5e foo/y.txt --- a/foo/y.txt +++ b/foo/y.txt @@ -1,1 +1,2 @@ y1 +y2 diff -r 4904098473f9 -r 9647f22de499 foo/bar/z.txt --- a/foo/bar/z.txt +++ b/foo/bar/z.txt @@ -1,1 +1,2 @@ z1 +z2 #if serve $ cd .. $ hg serve -R repo --debug -S -p $HGPORT -d --pid-file=hg1.pid -E error.log -A access.log adding = $TESTTMP/repo (glob) adding foo = $TESTTMP/repo/foo (glob) adding foo/bar = $TESTTMP/repo/foo/bar (glob) listening at http://*:$HGPORT/ (bound to *:$HGPORT) (glob) (?) adding = $TESTTMP/repo (glob) (?) adding foo = $TESTTMP/repo/foo (glob) (?) adding foo/bar = $TESTTMP/repo/foo/bar (glob) (?) $ cat hg1.pid >> $DAEMON_PIDS $ hg clone http://localhost:$HGPORT clone --config progress.disable=True requesting all changes adding changesets adding manifests adding file changes added 3 changesets with 5 changes to 3 files updating to branch default cloning subrepo foo from http://localhost:$HGPORT/foo requesting all changes adding changesets adding manifests adding file changes added 4 changesets with 7 changes to 3 files cloning subrepo foo/bar from http://localhost:$HGPORT/foo/bar (glob) requesting all changes adding changesets adding manifests adding file changes added 3 changesets with 3 changes to 1 files 3 files updated, 0 files merged, 0 files removed, 0 files unresolved $ cat clone/foo/bar/z.txt z1 z2 z3 $ cat access.log * "GET /?cmd=capabilities HTTP/1.1" 200 - (glob) * "GET /?cmd=batch HTTP/1.1" 200 - * (glob) * "GET /?cmd=getbundle HTTP/1.1" 200 - * (glob) * "GET /foo?cmd=capabilities HTTP/1.1" 200 - (glob) * "GET /foo?cmd=batch HTTP/1.1" 200 - * (glob) * "GET /foo?cmd=getbundle HTTP/1.1" 200 - * (glob) * "GET /foo/bar?cmd=capabilities HTTP/1.1" 200 - (glob) * "GET /foo/bar?cmd=batch HTTP/1.1" 200 - * (glob) * "GET /foo/bar?cmd=getbundle HTTP/1.1" 200 - * (glob) $ killdaemons.py $ rm hg1.pid error.log access.log $ cd repo #endif Enable progress extension for archive tests: $ cp $HGRCPATH $HGRCPATH.no-progress $ cat >> $HGRCPATH <<EOF > [progress] > disable=False > assume-tty = 1 > delay = 0 > # set changedelay really large so we don't see nested topics > changedelay = 30000 > format = topic bar number > refresh = 0 > width = 60 > EOF Test archiving to a directory tree (the doubled lines in the output only show up in the test output, not in real usage): $ hg archive --subrepos ../archive \r (no-eol) (esc) archiving [ ] 0/3\r (no-eol) (esc) archiving [=============> ] 1/3\r (no-eol) (esc) archiving [===========================> ] 2/3\r (no-eol) (esc) archiving [==========================================>] 3/3\r (no-eol) (esc) \r (no-eol) (esc) \r (no-eol) (esc) archiving (foo) [ ] 0/3\r (no-eol) (esc) archiving (foo) [===========> ] 1/3\r (no-eol) (esc) archiving (foo) [=======================> ] 2/3\r (no-eol) (esc) archiving (foo) [====================================>] 3/3\r (no-eol) (esc) \r (no-eol) (esc) \r (no-eol) (esc) archiving (foo/bar) [ ] 0/1\r (no-eol) (glob) (esc) archiving (foo/bar) [================================>] 1/1\r (no-eol) (glob) (esc) \r (no-eol) (esc) $ find ../archive | sort ../archive ../archive/.hg_archival.txt ../archive/.hgsub ../archive/.hgsubstate ../archive/foo ../archive/foo/.hgsub ../archive/foo/.hgsubstate ../archive/foo/bar ../archive/foo/bar/z.txt ../archive/foo/y.txt ../archive/x.txt Test archiving to zip file (unzip output is unstable): $ hg archive --subrepos --prefix '.' ../archive.zip \r (no-eol) (esc) archiving [ ] 0/3\r (no-eol) (esc) archiving [=============> ] 1/3\r (no-eol) (esc) archiving [===========================> ] 2/3\r (no-eol) (esc) archiving [==========================================>] 3/3\r (no-eol) (esc) \r (no-eol) (esc) \r (no-eol) (esc) archiving (foo) [ ] 0/3\r (no-eol) (esc) archiving (foo) [===========> ] 1/3\r (no-eol) (esc) archiving (foo) [=======================> ] 2/3\r (no-eol) (esc) archiving (foo) [====================================>] 3/3\r (no-eol) (esc) \r (no-eol) (esc) \r (no-eol) (esc) archiving (foo/bar) [ ] 0/1\r (no-eol) (glob) (esc) archiving (foo/bar) [================================>] 1/1\r (no-eol) (glob) (esc) \r (no-eol) (esc) (unzip date formating is unstable, we do not care about it and glob it out) $ unzip -l ../archive.zip | grep -v -- ----- | egrep -v files$ Archive: ../archive.zip Length [ ]* Date [ ]* Time [ ]* Name (re) 172 [0-9:\- ]* .hg_archival.txt (re) 10 [0-9:\- ]* .hgsub (re) 45 [0-9:\- ]* .hgsubstate (re) 3 [0-9:\- ]* x.txt (re) 10 [0-9:\- ]* foo/.hgsub (re) 45 [0-9:\- ]* foo/.hgsubstate (re) 9 [0-9:\- ]* foo/y.txt (re) 9 [0-9:\- ]* foo/bar/z.txt (re) Test archiving a revision that references a subrepo that is not yet cloned: #if hardlink $ hg clone -U . ../empty \r (no-eol) (esc) linking [ <=> ] 1\r (no-eol) (esc) linking [ <=> ] 2\r (no-eol) (esc) linking [ <=> ] 3\r (no-eol) (esc) linking [ <=> ] 4\r (no-eol) (esc) linking [ <=> ] 5\r (no-eol) (esc) linking [ <=> ] 6\r (no-eol) (esc) linking [ <=> ] 7\r (no-eol) (esc) linking [ <=> ] 8\r (no-eol) (esc) \r (no-eol) (esc) #else $ hg clone -U . ../empty \r (no-eol) (esc) linking [ <=> ] 1 (no-eol) #endif $ cd ../empty #if hardlink $ hg archive --subrepos -r tip --prefix './' ../archive.tar.gz \r (no-eol) (esc) archiving [ ] 0/3\r (no-eol) (esc) archiving [=============> ] 1/3\r (no-eol) (esc) archiving [===========================> ] 2/3\r (no-eol) (esc) archiving [==========================================>] 3/3\r (no-eol) (esc) \r (no-eol) (esc) \r (no-eol) (esc) linking [ <=> ] 1\r (no-eol) (esc) linking [ <=> ] 2\r (no-eol) (esc) linking [ <=> ] 3\r (no-eol) (esc) linking [ <=> ] 4\r (no-eol) (esc) linking [ <=> ] 5\r (no-eol) (esc) linking [ <=> ] 6\r (no-eol) (esc) linking [ <=> ] 7\r (no-eol) (esc) linking [ <=> ] 8\r (no-eol) (esc) \r (no-eol) (esc) \r (no-eol) (esc) archiving (foo) [ ] 0/3\r (no-eol) (esc) archiving (foo) [===========> ] 1/3\r (no-eol) (esc) archiving (foo) [=======================> ] 2/3\r (no-eol) (esc) archiving (foo) [====================================>] 3/3\r (no-eol) (esc) \r (no-eol) (esc) \r (no-eol) (esc) linking [ <=> ] 1\r (no-eol) (esc) linking [ <=> ] 2\r (no-eol) (esc) linking [ <=> ] 3\r (no-eol) (esc) linking [ <=> ] 4\r (no-eol) (esc) linking [ <=> ] 5\r (no-eol) (esc) linking [ <=> ] 6\r (no-eol) (esc) \r (no-eol) (esc) \r (no-eol) (esc) archiving (foo/bar) [ ] 0/1\r (no-eol) (glob) (esc) archiving (foo/bar) [================================>] 1/1\r (no-eol) (glob) (esc) \r (no-eol) (esc) cloning subrepo foo from $TESTTMP/repo/foo cloning subrepo foo/bar from $TESTTMP/repo/foo/bar (glob) #else Note there's a slight output glitch on non-hardlink systems: the last "linking" progress topic never gets closed, leading to slight output corruption on that platform. $ hg archive --subrepos -r tip --prefix './' ../archive.tar.gz \r (no-eol) (esc) archiving [ ] 0/3\r (no-eol) (esc) archiving [=============> ] 1/3\r (no-eol) (esc) archiving [===========================> ] 2/3\r (no-eol) (esc) archiving [==========================================>] 3/3\r (no-eol) (esc) \r (no-eol) (esc) \r (no-eol) (esc) linking [ <=> ] 1\r (no-eol) (esc) cloning subrepo foo/bar from $TESTTMP/repo/foo/bar (glob) #endif Archive + subrepos uses '/' for all component separators $ tar -tzf ../archive.tar.gz | sort .hg_archival.txt .hgsub .hgsubstate foo/.hgsub foo/.hgsubstate foo/bar/z.txt foo/y.txt x.txt The newly cloned subrepos contain no working copy: $ hg -R foo summary parent: -1:000000000000 (no revision checked out) branch: default commit: (clean) update: 4 new changesets (update) Disable progress extension and cleanup: $ mv $HGRCPATH.no-progress $HGRCPATH Test archiving when there is a directory in the way for a subrepo created by archive: $ hg clone -U . ../almost-empty $ cd ../almost-empty $ mkdir foo $ echo f > foo/f $ hg archive --subrepos -r tip archive cloning subrepo foo from $TESTTMP/empty/foo abort: destination '$TESTTMP/almost-empty/foo' is not empty (in subrepo foo) (glob) [255] Clone and test outgoing: $ cd .. $ hg clone repo repo2 updating to branch default cloning subrepo foo from $TESTTMP/repo/foo cloning subrepo foo/bar from $TESTTMP/repo/foo/bar (glob) 3 files updated, 0 files merged, 0 files removed, 0 files unresolved $ cd repo2 $ hg outgoing -S comparing with $TESTTMP/repo (glob) searching for changes no changes found comparing with $TESTTMP/repo/foo searching for changes no changes found comparing with $TESTTMP/repo/foo/bar searching for changes no changes found [1] Make nested change: $ echo y4 >> foo/y.txt $ hg diff --nodates -S diff -r 65903cebad86 foo/y.txt --- a/foo/y.txt +++ b/foo/y.txt @@ -1,3 +1,4 @@ y1 y2 y3 +y4 $ hg commit --subrepos -m 3-4-2 committing subrepository foo $ hg outgoing -S comparing with $TESTTMP/repo (glob) searching for changes changeset: 3:2655b8ecc4ee tag: tip user: test date: Thu Jan 01 00:00:00 1970 +0000 summary: 3-4-2 comparing with $TESTTMP/repo/foo searching for changes changeset: 4:e96193d6cb36 tag: tip user: test date: Thu Jan 01 00:00:00 1970 +0000 summary: 3-4-2 comparing with $TESTTMP/repo/foo/bar searching for changes no changes found Switch to original repo and setup default path: $ cd ../repo $ echo '[paths]' >> .hg/hgrc $ echo 'default = ../repo2' >> .hg/hgrc Test incoming: $ hg incoming -S comparing with $TESTTMP/repo2 (glob) searching for changes changeset: 3:2655b8ecc4ee tag: tip user: test date: Thu Jan 01 00:00:00 1970 +0000 summary: 3-4-2 comparing with $TESTTMP/repo2/foo searching for changes changeset: 4:e96193d6cb36 tag: tip user: test date: Thu Jan 01 00:00:00 1970 +0000 summary: 3-4-2 comparing with $TESTTMP/repo2/foo/bar searching for changes no changes found $ hg incoming -S --bundle incoming.hg abort: cannot combine --bundle and --subrepos [255] Test missing subrepo: $ rm -r foo $ hg status -S warning: error "unknown revision '65903cebad86f1a84bd4f1134f62fa7dcb7a1c98'" in subrepository "foo" Issue2619: IndexError: list index out of range on hg add with subrepos The subrepo must sorts after the explicit filename. $ cd .. $ hg init test $ cd test $ hg init x $ echo abc > abc.txt $ hg ci -Am "abc" adding abc.txt $ echo "x = x" >> .hgsub $ hg add .hgsub $ touch a x/a $ hg add a x/a $ hg ci -Sm "added x" committing subrepository x $ echo abc > x/a $ hg revert --rev '.^' "set:subrepo('glob:x*')" abort: subrepository 'x' does not exist in 25ac2c9b3180! [255] $ cd ..