Raw
1 #!/bin/sh
2
3 test_description='pack-objects breaks long cross-pack delta chains'
4
5 . ./test-lib.sh
6
7 # This mirrors a repeated push setup:
8 #
9 # 1. A client repeatedly modifies some files, makes a
10 # commit, and pushes the result. It does this N times
11 # before we get around to repacking.
12 #
13 # 2. Each push generates a thin pack with the new version of
14 # various objects. Let's consider some file in the root tree
15 # which is updated in each commit.
16 #
17 # When generating push number X, we feed commit X-1 (and
18 # thus blob X-1) as a preferred base. The resulting pack has
19 # blob X as a thin delta against blob X-1.
20 #
21 # On the receiving end, "index-pack --fix-thin" will
22 # complete the pack with a base copy of blob X-1.
23 #
24 # 3. In older versions of git, if we used the delta from
25 # pack X, then we'd always find blob X-1 as a base in the
26 # same pack (and generate a fresh delta).
27 #
28 # But with the pack mru, we jump from delta to delta
29 # following the traversal order:
30 #
31 # a. We grab blob X from pack X as a delta, putting it at
32 # the tip of our mru list.
33 #
34 # b. Eventually we move onto commit X-1. We need other
35 # objects which are only in pack X-1 (in the test code
36 # below, it's the containing tree). That puts pack X-1
37 # at the tip of our mru list.
38 #
39 # c. Eventually we look for blob X-1, and we find the
40 # version in pack X-1 (because it's the mru tip).
41 #
42 # Now we have blob X as a delta against X-1, which is a delta
43 # against X-2, and so forth.
44 #
45 # In the real world, these small pushes would get exploded by
46 # unpack-objects rather than "index-pack --fix-thin", but the
47 # same principle applies to larger pushes (they only need one
48 # repeatedly-modified file to generate the delta chain).
49
50 test_expect_success 'create series of packs' '
51 test_config maintenance.auto false &&
52 test-tool genrandom foo 4096 >content &&
53 prev= &&
54 for i in $(test_seq 1 10)
55 do
56 cat content >file &&
57 echo $i >>file &&
58 git add file &&
59 git commit -m $i &&
60 cur=$(git rev-parse HEAD^{tree}) &&
61 {
62 if test -n "$prev"
63 then
64 echo "-$prev"
65 fi &&
66 echo $cur &&
67 echo "$(git rev-parse :file) file"
68 } | git pack-objects --stdout >tmp &&
69 GIT_TRACE2_EVENT=$PWD/trace \
70 git index-pack -v --stdin --fix-thin <tmp || return 1 &&
71 grep -c region_enter.*progress trace >enter &&
72 grep -c region_leave.*progress trace >leave &&
73 test_cmp enter leave &&
74 prev=$cur
75 done
76 '
77
78 max_chain() {
79 git index-pack --verify-stat-only "$1" >output &&
80 awk '
81 BEGIN { len=0 }
82 /chain length = [0-9]+:/{ len=$4 }
83 END { print len }
84 ' <output | tr -d ':'
85 }
86
87 # Note that this whole setup is pretty reliant on the current
88 # packing heuristics. We double-check that our test case
89 # actually produces a long chain. If it doesn't, it should be
90 # adjusted (or scrapped if the heuristics have become too unreliable)
91 test_expect_success 'packing produces a long delta' '
92 # Use --window=0 to make sure we are seeing reused deltas,
93 # not computing a new long chain. (Also avoid the --path-walk
94 # option as it may break delta chains.)
95 pack=$(git pack-objects --all --window=0 --no-path-walk </dev/null pack) &&
96 echo 9 >expect &&
97 max_chain pack-$pack.pack >actual &&
98 test_cmp expect actual
99 '
100
101 test_expect_success '--depth limits depth' '
102 # Avoid --path-walk to avoid breaking delta chains across path
103 # boundaries.
104 pack=$(git pack-objects --all --depth=5 --no-path-walk </dev/null pack) &&
105 echo 5 >expect &&
106 max_chain pack-$pack.pack >actual &&
107 test_cmp expect actual
108 '
109
110 test_expect_success '--depth=0 disables deltas' '
111 pack=$(git pack-objects --all --depth=0 </dev/null pack) &&
112 echo 0 >expect &&
113 max_chain pack-$pack.pack >actual &&
114 test_cmp expect actual
115 '
116
117 test_expect_success 'negative depth disables deltas' '
118 pack=$(git pack-objects --all --depth=-1 </dev/null pack) &&
119 echo 0 >expect &&
120 max_chain pack-$pack.pack >actual &&
121 test_cmp expect actual
122 '
123
124 test_done