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