update: add a Rust fast-path when updating from null (and clean)
This case is easy to detect and we have all we need to generate a valid
working copy and dirstate entirely in Rust, which speeds things up
considerably:
On my machine updating a repo of ~300k files goes from 10.00s down to 4.2s,
all while consuming 50% less system time, with all caches hot.
Something to note is that further improvements will probably happen
with the upcoming `InnerRevlog` series that does smarter
mmap hanlding, especially for filelogs.
Here are benchmark numbers on a machine with only 4 cores (and no SMT enabled)
```
### data-env-vars.name = heptapod-public-2024-03-25-ds2-pnm
# benchmark.name = hg.command.update
# bin-env-vars.hg.py-re2-module = default
# bin-env-vars.hg.changeset.node = <this change>
# benchmark.variants.atomic-update = no
# benchmark.variants.scenario = null-to-tip
# benchmark.variants.worker = default
default: 5.328762 ~~~~~
rust: 1.308654 (-75.44%, -4.02)
### data-env-vars.name = mercurial-devel-2024-03-22-ds2-pnm
# benchmark.name = hg.command.update
# bin-env-vars.hg.py-re2-module = default
# bin-env-vars.hg.changeset.node = <this change>
# benchmark.variants.atomic-update = no
# benchmark.variants.scenario = null-to-tip
# benchmark.variants.worker = default
default: 1.693271 ~~~~~
rust: 1.151053 (-32.02%, -0.54)
### data-env-vars.name = mozilla-unified-2024-03-22-ds2-pnm
# benchmark.name = hg.command.update
# bin-env-vars.hg.py-re2-module = default
# bin-env-vars.hg.changeset.node = <this change>
# benchmark.variants.atomic-update = no
# benchmark.variants.scenario = null-to-tip
# benchmark.variants.worker = default
default: 38.901613 ~~~~~
rust: 11.637880 (-70.08%, -27.26)
### data-env-vars.name = netbsd-xsrc-public-2024-09-19-ds2-pnm
# benchmark.name = hg.command.update
# bin-env-vars.hg.py-re2-module = default
# bin-env-vars.hg.changeset.node = <this change>
# benchmark.variants.atomic-update = no
# benchmark.variants.scenario = null-to-tip
# benchmark.variants.worker = default
default: 4.793727 ~~~~~
rust: 1.505905 (-68.59%, -3.29)
```
#require serve
$ hg init server
$ hg serve -R server -p $HGPORT -d --pid-file=hg1.pid -E ../error.log
$ cat hg1.pid >> $DAEMON_PIDS
Check basic fetching
$ hg debugdownload "http://localhost:$HGPORT/?cmd=lookup&key=tip"
1 0000000000000000000000000000000000000000
$ hg debugdownload -o null.txt "http://localhost:$HGPORT/?cmd=lookup&key=null"
$ cat null.txt
1 0000000000000000000000000000000000000000
Check the request is made from the usual Mercurial logic
(rev details, give different content if the request has a Mercurial user agent)
$ get-with-headers.py --headeronly "localhost:$HGPORT" "rev/tip" content-type
200 Script output follows
content-type: text/html; charset=ascii
$ hg debugdownload "http://localhost:$HGPORT/rev/tip"
# HG changeset patch
# User
# Date 0 0
# Node ID 0000000000000000000000000000000000000000
Check other kind of compatible url
$ hg debugdownload ./null.txt
1 0000000000000000000000000000000000000000
$ cat ../error.log
Test largefile URL
------------------
$ cat << EOF >> $HGRCPATH
> [extensions]
> largefiles=
> EOF
$ killdaemons.py
$ rm -f error.log hg1.pid
$ hg serve -R server -p $HGPORT -d --pid-file=hg1.pid -E error.log
$ cat hg1.pid >> $DAEMON_PIDS
$ hg -R server debuglfput null.txt
a57b57b39ee4dc3da1e03526596007f480ecdbe8
$ hg debugdownload "largefile://a57b57b39ee4dc3da1e03526596007f480ecdbe8" --config paths.default=http://localhost:$HGPORT/
1 0000000000000000000000000000000000000000
from within a repository
$ hg clone http://localhost:$HGPORT/ client
no changes found
updating to branch default
0 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ cd client
$ hg path
default = http://localhost:$HGPORT/
$ hg debugdownload "largefile://a57b57b39ee4dc3da1e03526596007f480ecdbe8"
1 0000000000000000000000000000000000000000
$ cd ..
$ cat error.log