Mercurial > hg
view tests/test-largefiles-wireproto.t @ 42222:57203e0210f8
copies: calculate mergecopies() based on pathcopies()
When copies are stored in changesets, we need a changeset-centric
version of mergecopies() just like we have a changeset-centric version
of pathcopies(). I think the natural way of thinking about
mergecopies() is in terms of pathcopies() from the base to each of the
commits. So if we can rewrite mergecopies() based on two such
pathcopies() calls, we'll get the changeset-centric version for
free. That's what this patch does.
A nice bonus is that it ends up being a lot simpler. mergecopies() has
accumulated a lot of technical debt over time. One good example is the
code for dealing with grafts (the "partial/incomplete/dirty"
stuff). Since pathcopies() already deals with backwards renames and
ping-pong renames, we get that for free.
I've run tests with hard-coded debug logging for "fullcopy" and while
I haven't looked at every difference it produces, all the ones I have
looked at seemed reasonable to me. I'm a little surprised that no more
tests fail when run with '--extra-config-opt
experimental.copies.read-from=compatibility' compared to before this
patch. This patch also fixes the broken cases in test-annotate.t and
test-fastannotate.t. It also enables the part of test-copies.t that
was previously disabled exactly because mergecopies() needed to get a
changeset-centric version.
One drawback of the rewritten code is that we may now make
remotefilelog prefetch more files. We used to prefetch files that were
unique to either side of the merge compared to the other. We now
prefetch files that are unique to either side of the merge compared to
the base. This means that if you added the same file to each side, we
would not prefetch it before, but we would now. Such cases are
probably quite rare, but one likely scenario where they happen is when
moving from a commit to its successor (or the other way around). The
user will probably already have the files in the cache in such cases,
so it's probably not a big deal.
Some timings for calculating mergecopies between two revisions
(revisions shown on each line, all using the common ancestor as base):
In the hg repo:
4.8 4.9: 0.21s -> 0.21s
4.0 4.8: 0.35s -> 0.63s
In and old copy of the mozilla-unified repo:
FIREFOX_BETA_60_BASE^ FIREFOX_BETA_60_BASE: 0.82s -> 0.82s
FIREFOX_NIGHTLY_59_END FIREFOX_BETA_60_BASE: 2.5s -> 2.6s
FIREFOX_BETA_59_END FIREFOX_BETA_60_BASE: 3.9s -> 4.1s
FIREFOX_AURORA_50_BASE FIREFOX_BETA_60_BASE: 31s -> 33s
So it's measurably slower in most cases. The most significant
difference is in the hg repo between revisions 4.0 and 4.8. In that
case it seems to come from the fact that pathcopies() uses
fctx.isintroducedafter() (in _tracefile), while the old mergecopies()
used fctx.linkrev() (in _checkcopies()). That results in a single call
to filectx._adjustlinkrev(), which is responsible for the entire
difference in time (in my repo). So we pay a performance penalty but
we get more correct code (see change in
test-mv-cp-st-diff.t). Deleting the "== f.filenode()" in _tracefile()
recovers the lost performance in the hg repo.
There were are few other optimizations in _checkcopies() that I could
not measure any impact from. One was from the "seen" set. Another was
from a "continue" when the file was not in the destination manifest
(corresponding to "am" in _tracefile).
Also note that merge copies are not calculated when updating with a
clean working copy, which is probably the most common case. I
therefore think the much simpler code is worth the slowdown.
Differential Revision: https://phab.mercurial-scm.org/D6255
author | Martin von Zweigbergk <martinvonz@google.com> |
---|---|
date | Thu, 11 Apr 2019 23:22:54 -0700 |
parents | 549af2fa089f |
children | ebee234d952a |
line wrap: on
line source
#testcases sshv1 sshv2 #if sshv2 $ cat >> $HGRCPATH << EOF > [experimental] > sshpeer.advertise-v2 = true > sshserver.support-v2 = true > EOF #endif This file contains testcases that tend to be related to the wire protocol part of largefiles. $ USERCACHE="$TESTTMP/cache"; export USERCACHE $ mkdir "${USERCACHE}" $ cat >> $HGRCPATH <<EOF > [extensions] > largefiles= > purge= > rebase= > transplant= > [phases] > publish=False > [largefiles] > minsize=2 > patterns=glob:**.dat > usercache=${USERCACHE} > [web] > allow-archive = zip > [hooks] > precommit=sh -c "echo \\"Invoking status precommit hook\\"; hg status" > EOF #if serve vanilla clients not locked out from largefiles servers on vanilla repos $ mkdir r1 $ cd r1 $ hg init $ echo c1 > f1 $ hg add f1 $ hg commit -m "m1" Invoking status precommit hook A f1 $ cd .. $ hg serve -R r1 -d -p $HGPORT --pid-file hg.pid $ cat hg.pid >> $DAEMON_PIDS $ hg --config extensions.largefiles=! clone http://localhost:$HGPORT r2 requesting all changes adding changesets adding manifests adding file changes added 1 changesets with 1 changes to 1 files new changesets b6eb3a2e2efe (1 drafts) updating to branch default 1 files updated, 0 files merged, 0 files removed, 0 files unresolved largefiles clients still work with vanilla servers $ hg serve --config extensions.largefiles=! -R r1 -d -p $HGPORT1 --pid-file hg.pid $ cat hg.pid >> $DAEMON_PIDS $ hg clone http://localhost:$HGPORT1 r3 requesting all changes adding changesets adding manifests adding file changes added 1 changesets with 1 changes to 1 files new changesets b6eb3a2e2efe (1 drafts) updating to branch default 1 files updated, 0 files merged, 0 files removed, 0 files unresolved #endif vanilla clients locked out from largefiles http repos $ mkdir r4 $ cd r4 $ hg init $ echo c1 > f1 $ hg add --large f1 $ hg commit -m "m1" Invoking status precommit hook A f1 $ cd .. largefiles can be pushed locally (issue3583) $ hg init dest $ cd r4 $ hg outgoing ../dest comparing with ../dest searching for changes changeset: 0:639881c12b4c tag: tip user: test date: Thu Jan 01 00:00:00 1970 +0000 summary: m1 $ hg push ../dest pushing to ../dest searching for changes adding changesets adding manifests adding file changes added 1 changesets with 1 changes to 1 files exit code with nothing outgoing (issue3611) $ hg outgoing ../dest comparing with ../dest searching for changes no changes found [1] $ cd .. #if serve $ hg serve -R r4 -d -p $HGPORT2 --pid-file hg.pid $ cat hg.pid >> $DAEMON_PIDS $ hg --config extensions.largefiles=! clone http://localhost:$HGPORT2 r5 abort: remote error: This repository uses the largefiles extension. Please enable it in your Mercurial config file. [255] used all HGPORTs, kill all daemons $ killdaemons.py #endif vanilla clients locked out from largefiles ssh repos $ hg --config extensions.largefiles=! clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/r4 r5 remote: remote: This repository uses the largefiles extension. remote: remote: Please enable it in your Mercurial config file. remote: remote: - abort: remote error (check previous remote output) [255] #if serve largefiles clients refuse to push largefiles repos to vanilla servers $ mkdir r6 $ cd r6 $ hg init $ echo c1 > f1 $ hg add f1 $ hg commit -m "m1" Invoking status precommit hook A f1 $ cat >> .hg/hgrc <<! > [web] > push_ssl = false > allow_push = * > ! $ cd .. $ hg clone r6 r7 updating to branch default 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ cd r7 $ echo c2 > f2 $ hg add --large f2 $ hg commit -m "m2" Invoking status precommit hook A f2 $ hg verify --large checking changesets checking manifests crosschecking files in changesets and manifests checking files checked 2 changesets with 2 changes to 2 files searching 1 changesets for largefiles verified existence of 1 revisions of 1 largefiles $ hg serve --config extensions.largefiles=! -R ../r6 -d -p $HGPORT --pid-file ../hg.pid $ cat ../hg.pid >> $DAEMON_PIDS $ hg push http://localhost:$HGPORT pushing to http://localhost:$HGPORT/ searching for changes abort: http://localhost:$HGPORT/ does not appear to be a largefile store [255] $ cd .. putlfile errors are shown (issue3123) Corrupt the cached largefile in r7 and move it out of the servers usercache $ mv r7/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8 . $ echo 'client side corruption' > r7/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8 $ rm "$USERCACHE/4cdac4d8b084d0b599525cf732437fb337d422a8" $ hg init empty $ hg serve -R empty -d -p $HGPORT1 --pid-file hg.pid \ > --config 'web.allow_push=*' --config web.push_ssl=False $ cat hg.pid >> $DAEMON_PIDS $ hg push -R r7 http://localhost:$HGPORT1 pushing to http://localhost:$HGPORT1/ searching for changes remote: largefiles: failed to put 4cdac4d8b084d0b599525cf732437fb337d422a8 into store: largefile contents do not match hash abort: remotestore: could not put $TESTTMP/r7/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8 to remote store http://localhost:$HGPORT1/ [255] $ mv 4cdac4d8b084d0b599525cf732437fb337d422a8 r7/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8 Push of file that exists on server but is corrupted - magic healing would be nice ... but too magic $ echo "server side corruption" > empty/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8 $ hg push -R r7 http://localhost:$HGPORT1 pushing to http://localhost:$HGPORT1/ searching for changes remote: adding changesets remote: adding manifests remote: adding file changes remote: added 2 changesets with 2 changes to 2 files $ cat empty/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8 server side corruption $ rm -rf empty Push a largefiles repository to a served empty repository $ hg init r8 $ echo c3 > r8/f1 $ hg add --large r8/f1 -R r8 $ hg commit -m "m1" -R r8 Invoking status precommit hook A f1 $ hg init empty $ hg serve -R empty -d -p $HGPORT2 --pid-file hg.pid \ > --config 'web.allow_push=*' --config web.push_ssl=False $ cat hg.pid >> $DAEMON_PIDS $ rm "${USERCACHE}"/* $ hg push -R r8 http://localhost:$HGPORT2/#default pushing to http://localhost:$HGPORT2/ searching for changes remote: adding changesets remote: adding manifests remote: adding file changes remote: added 1 changesets with 1 changes to 1 files $ [ -f "${USERCACHE}"/02a439e5c31c526465ab1a0ca1f431f76b827b90 ] $ [ -f empty/.hg/largefiles/02a439e5c31c526465ab1a0ca1f431f76b827b90 ] Clone over http, no largefiles pulled on clone. $ hg clone http://localhost:$HGPORT2/#default http-clone -U adding changesets adding manifests adding file changes added 1 changesets with 1 changes to 1 files new changesets cf03e5bb9936 (1 drafts) Archive contains largefiles >>> import os >>> from mercurial import urllibcompat >>> u = 'http://localhost:%s/archive/default.zip' % os.environ['HGPORT2'] >>> with open('archive.zip', 'wb') as f: ... f.write(urllibcompat.urlreq.urlopen(u).read()) and None $ unzip -t archive.zip Archive: archive.zip testing: empty-default/.hg_archival.txt*OK (glob) testing: empty-default/f1*OK (glob) No errors detected in compressed data of archive.zip. test 'verify' with remotestore: $ rm "${USERCACHE}"/02a439e5c31c526465ab1a0ca1f431f76b827b90 $ mv empty/.hg/largefiles/02a439e5c31c526465ab1a0ca1f431f76b827b90 . $ hg -R http-clone verify --large --lfa checking changesets checking manifests crosschecking files in changesets and manifests checking files checked 1 changesets with 1 changes to 1 files searching 1 changesets for largefiles changeset 0:cf03e5bb9936: f1 missing verified existence of 1 revisions of 1 largefiles [1] $ mv 02a439e5c31c526465ab1a0ca1f431f76b827b90 empty/.hg/largefiles/ $ hg -R http-clone -q verify --large --lfa largefiles pulled on update - a largefile missing on the server: $ mv empty/.hg/largefiles/02a439e5c31c526465ab1a0ca1f431f76b827b90 . $ hg -R http-clone up --config largefiles.usercache=http-clone-usercache getting changed largefiles f1: largefile 02a439e5c31c526465ab1a0ca1f431f76b827b90 not available from http://localhost:$HGPORT2/ 0 largefiles updated, 0 removed 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg -R http-clone st ! f1 $ hg -R http-clone up -Cqr null largefiles pulled on update - a largefile corrupted on the server: $ echo corruption > empty/.hg/largefiles/02a439e5c31c526465ab1a0ca1f431f76b827b90 $ hg -R http-clone up --config largefiles.usercache=http-clone-usercache getting changed largefiles f1: data corruption (expected 02a439e5c31c526465ab1a0ca1f431f76b827b90, got 6a7bb2556144babe3899b25e5428123735bb1e27) 0 largefiles updated, 0 removed 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg -R http-clone st ! f1 $ [ ! -f http-clone/.hg/largefiles/02a439e5c31c526465ab1a0ca1f431f76b827b90 ] $ [ ! -f http-clone/f1 ] $ [ ! -f http-clone-usercache ] $ hg -R http-clone verify --large --lfc checking changesets checking manifests crosschecking files in changesets and manifests checking files checked 1 changesets with 1 changes to 1 files searching 1 changesets for largefiles verified contents of 1 revisions of 1 largefiles $ hg -R http-clone up -Cqr null largefiles pulled on update - no server side problems: $ mv 02a439e5c31c526465ab1a0ca1f431f76b827b90 empty/.hg/largefiles/ $ hg -R http-clone --debug up --config largefiles.usercache=http-clone-usercache --config progress.debug=true resolving manifests branchmerge: False, force: False, partial: False ancestor: 000000000000, local: 000000000000+, remote: cf03e5bb9936 .hglf/f1: remote created -> g getting .hglf/f1 updating: .hglf/f1 1/1 files (100.00%) getting changed largefiles using http://localhost:$HGPORT2/ sending capabilities command sending statlfile command getting largefiles: 0/1 files (0.00%) getting f1:02a439e5c31c526465ab1a0ca1f431f76b827b90 sending getlfile command found 02a439e5c31c526465ab1a0ca1f431f76b827b90 in store 1 largefiles updated, 0 removed 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ ls http-clone-usercache/* http-clone-usercache/02a439e5c31c526465ab1a0ca1f431f76b827b90 $ rm -rf empty http-clone* used all HGPORTs, kill all daemons $ killdaemons.py largefiles should batch verify remote calls $ hg init batchverifymain $ cd batchverifymain $ echo "aaa" >> a $ hg add --large a $ hg commit -m "a" Invoking status precommit hook A a $ echo "bbb" >> b $ hg add --large b $ hg commit -m "b" Invoking status precommit hook A b $ cd .. $ hg serve -R batchverifymain -d -p $HGPORT --pid-file hg.pid \ > -A access.log $ cat hg.pid >> $DAEMON_PIDS $ hg clone --noupdate http://localhost:$HGPORT batchverifyclone requesting all changes adding changesets adding manifests adding file changes added 2 changesets with 2 changes to 2 files new changesets 567253b0f523:04d19c27a332 (2 drafts) $ hg -R batchverifyclone verify --large --lfa checking changesets checking manifests crosschecking files in changesets and manifests checking files checked 2 changesets with 2 changes to 2 files searching 2 changesets for largefiles verified existence of 2 revisions of 2 largefiles $ tail -1 access.log $LOCALIP - - [$LOGDATE$] "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=statlfile+sha%3D972a1a11f19934401291cc99117ec614933374ce%3Bstatlfile+sha%3Dc801c9cfe94400963fcb683246217d5db77f9a9a x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob) $ hg -R batchverifyclone update getting changed largefiles 2 largefiles updated, 0 removed 2 files updated, 0 files merged, 0 files removed, 0 files unresolved Clear log file before next test $ printf "" > access.log Verify should check file on remote server only when file is not available locally. $ echo "ccc" >> batchverifymain/c $ hg -R batchverifymain status ? c $ hg -R batchverifymain add --large batchverifymain/c $ hg -R batchverifymain commit -m "c" Invoking status precommit hook A c $ hg -R batchverifyclone pull pulling from http://localhost:$HGPORT/ searching for changes adding changesets adding manifests adding file changes added 1 changesets with 1 changes to 1 files new changesets 6bba8cb6935d (1 drafts) (run 'hg update' to get a working copy) $ hg -R batchverifyclone verify --lfa checking changesets checking manifests crosschecking files in changesets and manifests checking files checked 3 changesets with 3 changes to 3 files searching 3 changesets for largefiles verified existence of 3 revisions of 3 largefiles $ tail -1 access.log $LOCALIP - - [$LOGDATE$] "GET /?cmd=statlfile HTTP/1.1" 200 - x-hgarg-1:sha=c8559c3c9cfb42131794b7d8009230403b9b454c x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob) $ killdaemons.py largefiles should not ask for password again after successful authorization $ hg init credentialmain $ cd credentialmain $ echo "aaa" >> a $ hg add --large a $ hg commit -m "a" Invoking status precommit hook A a Before running server clear the user cache to force clone to download a large file from the server rather than to get it from the cache $ rm "${USERCACHE}"/* $ cd .. $ hg serve --config extensions.x=$TESTDIR/httpserverauth.py -R credentialmain \ > -d -p $HGPORT --pid-file hg.pid -A access.log $ cat hg.pid >> $DAEMON_PIDS $ cat << EOF > get_pass.py > import getpass > def newgetpass(arg): > return "pass" > getpass.getpass = newgetpass > EOF $ hg clone --config ui.interactive=true --config extensions.getpass=get_pass.py \ > http://user@localhost:$HGPORT credentialclone http authorization required for http://localhost:$HGPORT/ realm: mercurial user: user password: requesting all changes adding changesets adding manifests adding file changes added 1 changesets with 1 changes to 1 files new changesets 567253b0f523 (1 drafts) updating to branch default getting changed largefiles 1 largefiles updated, 0 removed 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ killdaemons.py $ rm hg.pid access.log #endif