Merge branch 'jk/config-blob-sans-repo'
[git] / t / t0410-partial-clone.sh
1 #!/bin/sh
2
3 test_description='partial clone'
4
5 . ./test-lib.sh
6
7 delete_object () {
8         rm $1/.git/objects/$(echo $2 | sed -e 's|^..|&/|')
9 }
10
11 pack_as_from_promisor () {
12         HASH=$(git -C repo pack-objects .git/objects/pack/pack) &&
13         >repo/.git/objects/pack/pack-$HASH.promisor &&
14         echo $HASH
15 }
16
17 promise_and_delete () {
18         HASH=$(git -C repo rev-parse "$1") &&
19         git -C repo tag -a -m message my_annotated_tag "$HASH" &&
20         git -C repo rev-parse my_annotated_tag | pack_as_from_promisor &&
21         # tag -d prints a message to stdout, so redirect it
22         git -C repo tag -d my_annotated_tag >/dev/null &&
23         delete_object repo "$HASH"
24 }
25
26 test_expect_success 'missing reflog object, but promised by a commit, passes fsck' '
27         test_create_repo repo &&
28         test_commit -C repo my_commit &&
29
30         A=$(git -C repo commit-tree -m a HEAD^{tree}) &&
31         C=$(git -C repo commit-tree -m c -p $A HEAD^{tree}) &&
32
33         # Reference $A only from reflog, and delete it
34         git -C repo branch my_branch "$A" &&
35         git -C repo branch -f my_branch my_commit &&
36         delete_object repo "$A" &&
37
38         # State that we got $C, which refers to $A, from promisor
39         printf "$C\n" | pack_as_from_promisor &&
40
41         # Normally, it fails
42         test_must_fail git -C repo fsck &&
43
44         # But with the extension, it succeeds
45         git -C repo config core.repositoryformatversion 1 &&
46         git -C repo config extensions.partialclone "arbitrary string" &&
47         git -C repo fsck
48 '
49
50 test_expect_success 'missing reflog object, but promised by a tag, passes fsck' '
51         rm -rf repo &&
52         test_create_repo repo &&
53         test_commit -C repo my_commit &&
54
55         A=$(git -C repo commit-tree -m a HEAD^{tree}) &&
56         git -C repo tag -a -m d my_tag_name $A &&
57         T=$(git -C repo rev-parse my_tag_name) &&
58         git -C repo tag -d my_tag_name &&
59
60         # Reference $A only from reflog, and delete it
61         git -C repo branch my_branch "$A" &&
62         git -C repo branch -f my_branch my_commit &&
63         delete_object repo "$A" &&
64
65         # State that we got $T, which refers to $A, from promisor
66         printf "$T\n" | pack_as_from_promisor &&
67
68         git -C repo config core.repositoryformatversion 1 &&
69         git -C repo config extensions.partialclone "arbitrary string" &&
70         git -C repo fsck
71 '
72
73 test_expect_success 'missing reflog object alone fails fsck, even with extension set' '
74         rm -rf repo &&
75         test_create_repo repo &&
76         test_commit -C repo my_commit &&
77
78         A=$(git -C repo commit-tree -m a HEAD^{tree}) &&
79         B=$(git -C repo commit-tree -m b HEAD^{tree}) &&
80
81         # Reference $A only from reflog, and delete it
82         git -C repo branch my_branch "$A" &&
83         git -C repo branch -f my_branch my_commit &&
84         delete_object repo "$A" &&
85
86         git -C repo config core.repositoryformatversion 1 &&
87         git -C repo config extensions.partialclone "arbitrary string" &&
88         test_must_fail git -C repo fsck
89 '
90
91 test_expect_success 'missing ref object, but promised, passes fsck' '
92         rm -rf repo &&
93         test_create_repo repo &&
94         test_commit -C repo my_commit &&
95
96         A=$(git -C repo commit-tree -m a HEAD^{tree}) &&
97
98         # Reference $A only from ref
99         git -C repo branch my_branch "$A" &&
100         promise_and_delete "$A" &&
101
102         git -C repo config core.repositoryformatversion 1 &&
103         git -C repo config extensions.partialclone "arbitrary string" &&
104         git -C repo fsck
105 '
106
107 test_expect_success 'missing object, but promised, passes fsck' '
108         rm -rf repo &&
109         test_create_repo repo &&
110         test_commit -C repo 1 &&
111         test_commit -C repo 2 &&
112         test_commit -C repo 3 &&
113         git -C repo tag -a annotated_tag -m "annotated tag" &&
114
115         C=$(git -C repo rev-parse 1) &&
116         T=$(git -C repo rev-parse 2^{tree}) &&
117         B=$(git hash-object repo/3.t) &&
118         AT=$(git -C repo rev-parse annotated_tag) &&
119
120         promise_and_delete "$C" &&
121         promise_and_delete "$T" &&
122         promise_and_delete "$B" &&
123         promise_and_delete "$AT" &&
124
125         git -C repo config core.repositoryformatversion 1 &&
126         git -C repo config extensions.partialclone "arbitrary string" &&
127         git -C repo fsck
128 '
129
130 test_expect_success 'missing CLI object, but promised, passes fsck' '
131         rm -rf repo &&
132         test_create_repo repo &&
133         test_commit -C repo my_commit &&
134
135         A=$(git -C repo commit-tree -m a HEAD^{tree}) &&
136         promise_and_delete "$A" &&
137
138         git -C repo config core.repositoryformatversion 1 &&
139         git -C repo config extensions.partialclone "arbitrary string" &&
140         git -C repo fsck "$A"
141 '
142
143 test_expect_success 'fetching of missing objects' '
144         rm -rf repo &&
145         test_create_repo server &&
146         test_commit -C server foo &&
147         git -C server repack -a -d --write-bitmap-index &&
148
149         git clone "file://$(pwd)/server" repo &&
150         HASH=$(git -C repo rev-parse foo) &&
151         rm -rf repo/.git/objects/* &&
152
153         git -C repo config core.repositoryformatversion 1 &&
154         git -C repo config extensions.partialclone "origin" &&
155         git -C repo cat-file -p "$HASH" &&
156
157         # Ensure that the .promisor file is written, and check that its
158         # associated packfile contains the object
159         ls repo/.git/objects/pack/pack-*.promisor >promisorlist &&
160         test_line_count = 1 promisorlist &&
161         IDX=$(cat promisorlist | sed "s/promisor$/idx/") &&
162         git verify-pack --verbose "$IDX" | grep "$HASH"
163 '
164
165 test_expect_success 'rev-list stops traversal at missing and promised commit' '
166         rm -rf repo &&
167         test_create_repo repo &&
168         test_commit -C repo foo &&
169         test_commit -C repo bar &&
170
171         FOO=$(git -C repo rev-parse foo) &&
172         promise_and_delete "$FOO" &&
173
174         git -C repo config core.repositoryformatversion 1 &&
175         git -C repo config extensions.partialclone "arbitrary string" &&
176         git -C repo rev-list --exclude-promisor-objects --objects bar >out &&
177         grep $(git -C repo rev-parse bar) out &&
178         ! grep $FOO out
179 '
180
181 test_expect_success 'rev-list stops traversal at missing and promised tree' '
182         rm -rf repo &&
183         test_create_repo repo &&
184         test_commit -C repo foo &&
185         mkdir repo/a_dir &&
186         echo something >repo/a_dir/something &&
187         git -C repo add a_dir/something &&
188         git -C repo commit -m bar &&
189
190         # foo^{tree} (tree referenced from commit)
191         TREE=$(git -C repo rev-parse foo^{tree}) &&
192
193         # a tree referenced by HEAD^{tree} (tree referenced from tree)
194         TREE2=$(git -C repo ls-tree HEAD^{tree} | grep " tree " | head -1 | cut -b13-52) &&
195
196         promise_and_delete "$TREE" &&
197         promise_and_delete "$TREE2" &&
198
199         git -C repo config core.repositoryformatversion 1 &&
200         git -C repo config extensions.partialclone "arbitrary string" &&
201         git -C repo rev-list --exclude-promisor-objects --objects HEAD >out &&
202         grep $(git -C repo rev-parse foo) out &&
203         ! grep $TREE out &&
204         grep $(git -C repo rev-parse HEAD) out &&
205         ! grep $TREE2 out
206 '
207
208 test_expect_success 'rev-list stops traversal at missing and promised blob' '
209         rm -rf repo &&
210         test_create_repo repo &&
211         echo something >repo/something &&
212         git -C repo add something &&
213         git -C repo commit -m foo &&
214
215         BLOB=$(git -C repo hash-object -w something) &&
216         promise_and_delete "$BLOB" &&
217
218         git -C repo config core.repositoryformatversion 1 &&
219         git -C repo config extensions.partialclone "arbitrary string" &&
220         git -C repo rev-list --exclude-promisor-objects --objects HEAD >out &&
221         grep $(git -C repo rev-parse HEAD) out &&
222         ! grep $BLOB out
223 '
224
225 test_expect_success 'rev-list stops traversal at promisor commit, tree, and blob' '
226         rm -rf repo &&
227         test_create_repo repo &&
228         test_commit -C repo foo &&
229         test_commit -C repo bar &&
230         test_commit -C repo baz &&
231
232         COMMIT=$(git -C repo rev-parse foo) &&
233         TREE=$(git -C repo rev-parse bar^{tree}) &&
234         BLOB=$(git hash-object repo/baz.t) &&
235         printf "%s\n%s\n%s\n" $COMMIT $TREE $BLOB | pack_as_from_promisor &&
236
237         git -C repo config core.repositoryformatversion 1 &&
238         git -C repo config extensions.partialclone "arbitrary string" &&
239         git -C repo rev-list --exclude-promisor-objects --objects HEAD >out &&
240         ! grep $COMMIT out &&
241         ! grep $TREE out &&
242         ! grep $BLOB out &&
243         grep $(git -C repo rev-parse bar) out  # sanity check that some walking was done
244 '
245
246 test_expect_success 'rev-list accepts missing and promised objects on command line' '
247         rm -rf repo &&
248         test_create_repo repo &&
249         test_commit -C repo foo &&
250         test_commit -C repo bar &&
251         test_commit -C repo baz &&
252
253         COMMIT=$(git -C repo rev-parse foo) &&
254         TREE=$(git -C repo rev-parse bar^{tree}) &&
255         BLOB=$(git hash-object repo/baz.t) &&
256
257         promise_and_delete $COMMIT &&
258         promise_and_delete $TREE &&
259         promise_and_delete $BLOB &&
260
261         git -C repo config core.repositoryformatversion 1 &&
262         git -C repo config extensions.partialclone "arbitrary string" &&
263         git -C repo rev-list --exclude-promisor-objects --objects "$COMMIT" "$TREE" "$BLOB"
264 '
265
266 test_expect_success 'gc does not repack promisor objects' '
267         rm -rf repo &&
268         test_create_repo repo &&
269         test_commit -C repo my_commit &&
270
271         TREE_HASH=$(git -C repo rev-parse HEAD^{tree}) &&
272         HASH=$(printf "$TREE_HASH\n" | pack_as_from_promisor) &&
273
274         git -C repo config core.repositoryformatversion 1 &&
275         git -C repo config extensions.partialclone "arbitrary string" &&
276         git -C repo gc &&
277
278         # Ensure that the promisor packfile still exists, and remove it
279         test -e repo/.git/objects/pack/pack-$HASH.pack &&
280         rm repo/.git/objects/pack/pack-$HASH.* &&
281
282         # Ensure that the single other pack contains the commit, but not the tree
283         ls repo/.git/objects/pack/pack-*.pack >packlist &&
284         test_line_count = 1 packlist &&
285         git verify-pack repo/.git/objects/pack/pack-*.pack -v >out &&
286         grep "$(git -C repo rev-parse HEAD)" out &&
287         ! grep "$TREE_HASH" out
288 '
289
290 test_expect_success 'gc stops traversal when a missing but promised object is reached' '
291         rm -rf repo &&
292         test_create_repo repo &&
293         test_commit -C repo my_commit &&
294
295         TREE_HASH=$(git -C repo rev-parse HEAD^{tree}) &&
296         HASH=$(promise_and_delete $TREE_HASH) &&
297
298         git -C repo config core.repositoryformatversion 1 &&
299         git -C repo config extensions.partialclone "arbitrary string" &&
300         git -C repo gc &&
301
302         # Ensure that the promisor packfile still exists, and remove it
303         test -e repo/.git/objects/pack/pack-$HASH.pack &&
304         rm repo/.git/objects/pack/pack-$HASH.* &&
305
306         # Ensure that the single other pack contains the commit, but not the tree
307         ls repo/.git/objects/pack/pack-*.pack >packlist &&
308         test_line_count = 1 packlist &&
309         git verify-pack repo/.git/objects/pack/pack-*.pack -v >out &&
310         grep "$(git -C repo rev-parse HEAD)" out &&
311         ! grep "$TREE_HASH" out
312 '
313
314 LIB_HTTPD_PORT=12345  # default port, 410, cannot be used as non-root
315 . "$TEST_DIRECTORY"/lib-httpd.sh
316 start_httpd
317
318 test_expect_success 'fetching of missing objects from an HTTP server' '
319         rm -rf repo &&
320         SERVER="$HTTPD_DOCUMENT_ROOT_PATH/server" &&
321         test_create_repo "$SERVER" &&
322         test_commit -C "$SERVER" foo &&
323         git -C "$SERVER" repack -a -d --write-bitmap-index &&
324
325         git clone $HTTPD_URL/smart/server repo &&
326         HASH=$(git -C repo rev-parse foo) &&
327         rm -rf repo/.git/objects/* &&
328
329         git -C repo config core.repositoryformatversion 1 &&
330         git -C repo config extensions.partialclone "origin" &&
331         git -C repo cat-file -p "$HASH" &&
332
333         # Ensure that the .promisor file is written, and check that its
334         # associated packfile contains the object
335         ls repo/.git/objects/pack/pack-*.promisor >promisorlist &&
336         test_line_count = 1 promisorlist &&
337         IDX=$(cat promisorlist | sed "s/promisor$/idx/") &&
338         git verify-pack --verbose "$IDX" | grep "$HASH"
339 '
340
341 stop_httpd
342
343 test_done