Commit 3f4473e
committed
connected: add incremental connectivity check via rev-list
The connectivity check uses rev-list to find commits reachable
from the incoming tips but not from any local ref and then walks
their object closure. Commit traversal stops at the connectivity
boundary, but trees and blobs reachable from that boundary still
need to be walked so they can be marked uninteresting, allocating
a struct object for each one. On repositories where the boundary
commits have large trees, the connectivity check for small
incoming changes visits and tracks more objects than needed.
Add an alternative connectivity check that verifies incoming
commits incrementally against their parents.
Instead of traversing the full boundary closure, the new check
compares each new commit's tree with its parent trees. Already
trusted entries are skipped, changed subtrees are descended into
recursively, and blobs are checked for existence. This approach
thus avoids descending into untouched subtrees.
For example, consider a commit that changes one file under lib/
and also moves an unchanged subtree from src/ to dev/:
Parent tree New tree
+-- src/ (aaa) +-- dev/ (aaa)
+-- lib/ (bbb) +-- lib/ (ccc)
+-- foo.c (ddd) +-- foo.c (ddd)
+-- bar.c (eee) +-- bar.c (fff)
The verifier first scans the new root and collects aaa and ccc as
work items. It then scans the parent root, publishing aaa and bbb
into the trusted sets and recording bbb as the comparison base for
ccc.
When the work list is revisited, aaa is now trusted and skipped
even though it appears at a different path. The verifier descends
into ccc using bbb as its parent base. Scanning bbb similarly
makes ddd and eee trusted, leaving only the new fff blob to be
checked for existence.
Thus neither the moved subtree nor any other unchanged subtree is
recursively explored; only the changed lib/ subtree is descended
into, and only the new bar.c blob needs an existence check. The
root trees still need to be read and scanned as comparison bases.
New commits are processed with ancestors before descendants.
Parents outside the incoming commit set are on the already-connected
side of the boundary and provide the initial trusted bases. Once
an incoming commit has been verified, its tree can in turn be used
as a trusted base for descendant commits.
The verifier distinguishes trusted trees from expanded trees. A
trusted tree can be accepted without further verification. An
expanded tree has additionally published its direct non-gitlink
entries into the trusted sets. Expanded parent trees therefore
need not be read again for blob-only work, but may still be reread
when recursive verification needs same-path parent subtrees.
The implementation adds a --verify-trees-incremental flag to
rev-list, following the same pattern as --exclude-promisor-objects:
a pre-setup_revisions() scan sets the flag, the
post-setup_revisions() option loop skips it, and the main
traversal short-circuits into the incremental verifier after
collecting commits from the revision walk.
Because the incremental block eagerly consumes all commits via
get_revision(), the subsequent mark_edges_uninteresting() and
traverse_commit_list_filtered() calls naturally find no commit
work to do and only process any non-commit tips (trees, blobs)
left in revs.pending.
For partial clones, missing promisor objects (trees and blobs
promised by a promisor remote) are silently accepted during
verification instead of triggering errors. The revision walk
receives --exclude-promisor-objects so promisor commits do not
enter the verification set.
Gate the new algorithm behind transfer.connectivityCheck=incremental,
keeping full as the default. Fall back to the full algorithm for
deepening fetches to keep this commit easy to reason about, though
incremental mode could potentially handle them as well.
p5412 results (median of 3), scaling one dimension at a time.
Each modified file is in a different directory, with
directories chosen round-robin.
Scaling tree size (10 commits, 10 files/commit):
files full incr. full/incr
5K 0.01s 0.01s 1.1x
50K 0.04s 0.01s 2.2x
200K 0.14s 0.02s 5.7x
800K 0.60s 0.04s 14.8x
The full mode must traverse the boundary tree closure, which
grows with overall tree size. Incremental still scans the
root trees, but avoids descending into unchanged subtrees,
so it grows much more slowly with repository size.
Scaling commit count (200K files, 10 files/commit):
commits full incr. full/incr
1 0.14s 0.01s 7.5x
10 0.15s 0.02s 6.8x
100 0.16s 0.07s 2.2x
500 0.29s 0.29s 0.9x
3000 1.26s 1.67s 0.7x
5000 1.95s 2.71s 0.7x
10000 3.74s 6.02s 0.6x
With many commits the per-commit overhead of scanning both
the new and parent root trees accumulates and incremental
becomes slower. Breakeven is around 500 commits and the
ratio stabilizes near 0.6x for this fixture.
Scaling files per commit (200K files, 10 commits):
files/commit full incr. full/incr
1 0.15s 0.02s 7.5x
10 0.15s 0.02s 6.8x
100 0.15s 0.04s 3.1x
500 0.26s 0.16s 1.6x
1000 0.36s 0.35s 1.0x
2000 0.66s 0.79s 0.8x
Breakeven is around 1000 files/commit. At 2000 files/commit
(every directory touched), incremental is about 1.2x slower.
For small repositories both modes are fast enough that the
difference is difficult to measure reliably.
Signed-off-by: Kristofer Karlsson <krka@spotify.com>1 parent fa7f929 commit 3f4473e
12 files changed
Lines changed: 1057 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
1 | 21 | | |
2 | 22 | | |
3 | 23 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1089 | 1089 | | |
1090 | 1090 | | |
1091 | 1091 | | |
| 1092 | + | |
| 1093 | + | |
| 1094 | + | |
| 1095 | + | |
| 1096 | + | |
| 1097 | + | |
1092 | 1098 | | |
1093 | 1099 | | |
1094 | 1100 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1357 | 1357 | | |
1358 | 1358 | | |
1359 | 1359 | | |
| 1360 | + | |
1360 | 1361 | | |
1361 | 1362 | | |
1362 | 1363 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
28 | 28 | | |
29 | 29 | | |
30 | 30 | | |
| 31 | + | |
31 | 32 | | |
32 | 33 | | |
33 | 34 | | |
| |||
706 | 707 | | |
707 | 708 | | |
708 | 709 | | |
| 710 | + | |
709 | 711 | | |
710 | 712 | | |
711 | 713 | | |
| |||
748 | 750 | | |
749 | 751 | | |
750 | 752 | | |
| 753 | + | |
| 754 | + | |
751 | 755 | | |
752 | 756 | | |
753 | 757 | | |
| |||
822 | 826 | | |
823 | 827 | | |
824 | 828 | | |
| 829 | + | |
| 830 | + | |
825 | 831 | | |
826 | 832 | | |
827 | 833 | | |
| |||
935 | 941 | | |
936 | 942 | | |
937 | 943 | | |
| 944 | + | |
| 945 | + | |
| 946 | + | |
| 947 | + | |
| 948 | + | |
| 949 | + | |
| 950 | + | |
| 951 | + | |
| 952 | + | |
| 953 | + | |
| 954 | + | |
| 955 | + | |
938 | 956 | | |
939 | 957 | | |
940 | 958 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
| 4 | + | |
4 | 5 | | |
5 | 6 | | |
6 | 7 | | |
| |||
67 | 68 | | |
68 | 69 | | |
69 | 70 | | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
70 | 91 | | |
71 | 92 | | |
72 | 93 | | |
| |||
133 | 154 | | |
134 | 155 | | |
135 | 156 | | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
136 | 160 | | |
137 | 161 | | |
138 | 162 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
562 | 562 | | |
563 | 563 | | |
564 | 564 | | |
| 565 | + | |
565 | 566 | | |
566 | 567 | | |
567 | 568 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
652 | 652 | | |
653 | 653 | | |
654 | 654 | | |
| 655 | + | |
655 | 656 | | |
656 | 657 | | |
657 | 658 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
0 commit comments