Raw
1 #!/bin/sh
2
3 test_description='basic git gc tests
4 '
5
6 . ./test-lib.sh
7 . "$TEST_DIRECTORY"/lib-terminal.sh
8
9 test_expect_success 'setup' '
10 # do not let the amount of physical memory affects gc
11 # behavior, make sure we always pack everything to one pack by
12 # default
13 git config gc.bigPackThreshold 2g &&
14 git config set --global maintenance.strategy gc &&
15 test_oid_init
16 '
17
18 test_expect_success 'gc empty repository' '
19 git gc
20 '
21
22 test_expect_success 'gc does not leave behind pid file' '
23 git gc &&
24 test_path_is_missing .git/gc.pid
25 '
26
27 test_expect_success 'gc --gobbledegook' '
28 test_expect_code 129 git gc --nonsense 2>err &&
29 test_grep "[Uu]sage: git gc" err
30 '
31
32 test_expect_success 'gc -h with invalid configuration' '
33 mkdir broken &&
34 (
35 cd broken &&
36 git init &&
37 echo "[gc] pruneexpire = CORRUPT" >>.git/config &&
38 git gc -h >usage 2>&1
39 ) &&
40 test_grep "[Uu]sage" broken/usage
41 '
42
43 test_expect_success 'gc is not aborted due to a stale symref' '
44 git init remote &&
45 (
46 cd remote &&
47 test_commit initial &&
48 git clone . ../client &&
49 git branch -m develop &&
50 cd ../client &&
51 git fetch --prune &&
52 git gc
53 )
54 '
55
56 test_expect_success 'gc --keep-largest-pack' '
57 test_create_repo keep-pack &&
58 (
59 cd keep-pack &&
60 test_commit one &&
61 test_commit two &&
62 test_commit three &&
63 git gc &&
64 ( cd .git/objects/pack && ls *.pack ) >pack-list &&
65 test_line_count = 1 pack-list &&
66 cp pack-list base-pack-list &&
67 test_commit four &&
68 git repack -d &&
69 test_commit five &&
70 git repack -d &&
71 ( cd .git/objects/pack && ls *.pack ) >pack-list &&
72 test_line_count = 3 pack-list &&
73 git gc --keep-largest-pack &&
74 ( cd .git/objects/pack && ls *.pack ) >pack-list &&
75 test_line_count = 2 pack-list &&
76 awk "/^P /{print \$2}" <.git/objects/info/packs >pack-info &&
77 test_line_count = 2 pack-info &&
78 test_path_is_file .git/objects/pack/$(cat base-pack-list) &&
79 git fsck
80 )
81 '
82
83 test_expect_success 'pre-auto-gc hook can stop auto gc' '
84 cat >err.expect <<-\EOF &&
85 no gc for you
86 EOF
87
88 git init pre-auto-gc-hook &&
89 test_hook -C pre-auto-gc-hook pre-auto-gc <<-\EOF &&
90 echo >&2 no gc for you &&
91 exit 1
92 EOF
93 (
94 cd pre-auto-gc-hook &&
95
96 git config gc.auto 3 &&
97 git config gc.autoDetach false &&
98
99 # We need to create two object whose sha1s start with 17
100 # since this is what git gc counts. As it happens, these
101 # two blobs will do so.
102 test_commit "$(test_oid blob17_1)" &&
103 test_commit "$(test_oid blob17_2)" &&
104
105 git gc --auto >../out.actual 2>../err.actual
106 ) &&
107 test_must_be_empty out.actual &&
108 test_cmp err.expect err.actual &&
109
110 cat >err.expect <<-\EOF &&
111 will gc for you
112 Auto packing the repository for optimum performance.
113 See "git help gc" for manual housekeeping.
114 EOF
115
116 test_hook -C pre-auto-gc-hook --clobber pre-auto-gc <<-\EOF &&
117 echo >&2 will gc for you &&
118 exit 0
119 EOF
120
121 git -C pre-auto-gc-hook gc --auto >out.actual 2>err.actual &&
122
123 test_must_be_empty out.actual &&
124 test_cmp err.expect err.actual
125 '
126
127 test_expect_success 'auto gc with too many loose objects does not attempt to create bitmaps' '
128 test_config gc.auto 3 &&
129 test_config gc.autodetach false &&
130 test_config pack.writebitmaps true &&
131 # We need to create two object whose sha1s start with 17
132 # since this is what git gc counts. As it happens, these
133 # two blobs will do so.
134 test_commit "$(test_oid blob17_1)" &&
135 test_commit "$(test_oid blob17_2)" &&
136 # Our first gc will create a pack; our second will create a second pack
137 git gc --auto &&
138 ls .git/objects/pack/pack-*.pack | sort >existing_packs &&
139 test_commit "$(test_oid blob17_3)" &&
140 test_commit "$(test_oid blob17_4)" &&
141
142 git gc --auto 2>err &&
143 test_grep ! "^warning:" err &&
144 ls .git/objects/pack/pack-*.pack | sort >post_packs &&
145 comm -1 -3 existing_packs post_packs >new &&
146 comm -2 -3 existing_packs post_packs >del &&
147 test_line_count = 0 del && # No packs are deleted
148 test_line_count = 1 new # There is one new pack
149 '
150
151 test_expect_success 'gc --no-quiet' '
152 GIT_PROGRESS_DELAY=0 git -c gc.writeCommitGraph=true gc --no-quiet >stdout 2>stderr &&
153 test_must_be_empty stdout &&
154 test_grep "Computing commit graph generation numbers" stderr
155 '
156
157 test_expect_success TTY 'with TTY: gc --no-quiet' '
158 test_terminal env GIT_PROGRESS_DELAY=0 \
159 git -c gc.writeCommitGraph=true gc --no-quiet >stdout 2>stderr &&
160 test_must_be_empty stdout &&
161 test_grep "Enumerating objects" stderr &&
162 test_grep "Computing commit graph generation numbers: 100% (4/4), done." stderr
163 '
164
165 test_expect_success 'gc --quiet' '
166 git -c gc.writeCommitGraph=true gc --quiet >stdout 2>stderr &&
167 test_must_be_empty stdout &&
168 test_must_be_empty stderr
169 '
170
171 test_expect_success 'gc.reflogExpire{Unreachable,}=never skips "expire" via "gc"' '
172 test_config gc.reflogExpire never &&
173 test_config gc.reflogExpireUnreachable never &&
174
175 GIT_TRACE=$(pwd)/trace.out git gc &&
176
177 # Check that git-pack-refs is run as a sanity check (done via
178 # gc_before_repack()) but that git-expire is not.
179 test_grep -E "^trace: (built-in|exec|run_command): git pack-refs --" trace.out &&
180 test_grep ! -E "^trace: (built-in|exec|run_command): git reflog expire --" trace.out
181 '
182
183 test_expect_success 'one of gc.reflogExpire{Unreachable,}=never does not skip "expire" via "gc"' '
184 >trace.out &&
185 test_config gc.reflogExpire never &&
186 GIT_TRACE=$(pwd)/trace.out git gc &&
187 test_grep -E "^trace: (built-in|exec|run_command): git reflog expire --" trace.out
188 '
189
190 test_expect_success 'gc.repackFilter launches repack with a filter' '
191 git clone --no-local --bare . bare.git &&
192
193 git -C bare.git -c gc.cruftPacks=false gc &&
194 test_stdout_line_count = 1 ls bare.git/objects/pack/*.pack &&
195
196 GIT_TRACE=$(pwd)/trace.out git -C bare.git -c gc.repackFilter=blob:none \
197 -c repack.writeBitmaps=false -c gc.cruftPacks=false gc &&
198 test_stdout_line_count = 2 ls bare.git/objects/pack/*.pack &&
199 test_grep -E "^trace: (built-in|exec|run_command): git repack .* --filter=blob:none ?.*" trace.out
200 '
201
202 test_expect_success 'gc.repackFilterTo store filtered out objects' '
203 test_when_finished "rm -rf bare.git filtered.git" &&
204
205 git init --bare filtered.git &&
206 git -C bare.git -c gc.repackFilter=blob:none \
207 -c gc.repackFilterTo=../filtered.git/objects/pack/pack \
208 -c repack.writeBitmaps=false -c gc.cruftPacks=false gc &&
209
210 test_stdout_line_count = 1 ls bare.git/objects/pack/*.pack &&
211 test_stdout_line_count = 1 ls filtered.git/objects/pack/*.pack
212 '
213
214 prepare_cruft_history () {
215 test_commit base &&
216
217 test_commit --no-tag foo &&
218 test_commit --no-tag bar &&
219 git reset HEAD^^
220 }
221
222 assert_no_cruft_packs () {
223 find .git/objects/pack -name "*.mtimes" >mtimes &&
224 test_must_be_empty mtimes
225 }
226
227 for argv in \
228 "gc" \
229 "-c gc.cruftPacks=true gc" \
230 "-c gc.cruftPacks=false gc --cruft"
231 do
232 test_expect_success "git $argv generates a cruft pack" '
233 test_when_finished "rm -fr repo" &&
234 git init repo &&
235 (
236 cd repo &&
237
238 prepare_cruft_history &&
239 git $argv &&
240
241 find .git/objects/pack -name "*.mtimes" >mtimes &&
242 sed -e 's/\.mtimes$/\.pack/g' mtimes >packs &&
243
244 test_file_not_empty packs &&
245 while read pack
246 do
247 test_path_is_file "$pack" || return 1
248 done <packs
249 )
250 '
251 done
252
253 for argv in \
254 "gc --no-cruft" \
255 "-c gc.cruftPacks=false gc" \
256 "-c gc.cruftPacks=true gc --no-cruft"
257 do
258 test_expect_success "git $argv does not generate a cruft pack" '
259 test_when_finished "rm -fr repo" &&
260 git init repo &&
261 (
262 cd repo &&
263
264 prepare_cruft_history &&
265 git $argv &&
266
267 assert_no_cruft_packs
268 )
269 '
270 done
271
272 test_expect_success '--keep-largest-pack ignores cruft packs' '
273 test_when_finished "rm -fr repo" &&
274 git init repo &&
275 (
276 cd repo &&
277
278 # Generate a pack for reachable objects (of which there
279 # are 3), and one for unreachable objects (of which
280 # there are 6).
281 prepare_cruft_history &&
282 git gc --cruft &&
283
284 mtimes="$(find .git/objects/pack -type f -name "pack-*.mtimes")" &&
285 sz="$(test_file_size "${mtimes%.mtimes}.pack")" &&
286
287 # Ensure that the cruft pack gets removed (due to
288 # `--prune=now`) despite it being the largest pack.
289 git -c gc.bigPackThreshold=$sz gc --cruft --prune=now &&
290
291 assert_no_cruft_packs
292 )
293 '
294
295 test_expect_success 'gc.bigPackThreshold ignores cruft packs' '
296 test_when_finished "rm -fr repo" &&
297 git init repo &&
298 (
299 cd repo &&
300
301 # Generate a pack for reachable objects (of which there
302 # are 3), and one for unreachable objects (of which
303 # there are 6).
304 prepare_cruft_history &&
305 git gc --cruft &&
306
307 # Ensure that the cruft pack gets removed (due to
308 # `--prune=now`) despite it being the largest pack.
309 git gc --cruft --prune=now --keep-largest-pack &&
310
311 assert_no_cruft_packs
312 )
313 '
314
315 cruft_max_size_opts="git repack -d -l --cruft --cruft-expiration=2.weeks.ago"
316
317 test_expect_success 'setup for --max-cruft-size tests' '
318 git init cruft--max-size &&
319 (
320 cd cruft--max-size &&
321 prepare_cruft_history
322 )
323 '
324
325 test_expect_success '--max-cruft-size sets appropriate repack options' '
326 GIT_TRACE2_EVENT=$(pwd)/trace2.txt git -C cruft--max-size \
327 gc --cruft --max-cruft-size=1M &&
328 test_subcommand $cruft_max_size_opts --max-cruft-size=1048576 <trace2.txt
329 '
330
331 test_expect_success 'gc.maxCruftSize sets appropriate repack options' '
332 GIT_TRACE2_EVENT=$(pwd)/trace2.txt \
333 git -C cruft--max-size -c gc.maxCruftSize=2M gc --cruft &&
334 test_subcommand $cruft_max_size_opts --max-cruft-size=2097152 <trace2.txt &&
335
336 GIT_TRACE2_EVENT=$(pwd)/trace2.txt \
337 git -C cruft--max-size -c gc.maxCruftSize=2M gc --cruft \
338 --max-cruft-size=3M &&
339 test_subcommand $cruft_max_size_opts --max-cruft-size=3145728 <trace2.txt
340 '
341
342 test_expect_success '--expire-to sets repack --expire-to' '
343 rm -rf expired &&
344 mkdir expired &&
345 expire_to="$(pwd)/expired/pack" &&
346 GIT_TRACE2_EVENT=$(pwd)/trace2.txt git -C cruft--max-size gc --cruft --expire-to="$expire_to" &&
347 test_subcommand $cruft_max_size_opts --expire-to="$expire_to" <trace2.txt
348 '
349
350 test_expect_success '--expire-to with --prune=now sets repack --expire-to' '
351 rm -rf expired &&
352 mkdir expired &&
353 expire_to="$(pwd)/expired/pack" &&
354 GIT_TRACE2_EVENT=$(pwd)/trace2.txt git -C cruft--max-size gc --cruft --prune=now --expire-to="$expire_to" &&
355 test_subcommand git repack -d -l --cruft --cruft-expiration=now --expire-to="$expire_to" <trace2.txt
356 '
357
358
359 test_expect_success '--expire-to with --no-cruft sets repack -A' '
360 rm -rf expired &&
361 mkdir expired &&
362 expire_to="$(pwd)/expired/pack" &&
363 GIT_TRACE2_EVENT=$(pwd)/trace2.txt git -C cruft--max-size gc --no-cruft --expire-to="$expire_to" &&
364 test_subcommand git repack -d -l -A --unpack-unreachable=2.weeks.ago <trace2.txt
365 '
366
367 test_expect_success '--expire-to with --no-cruft sets repack -a' '
368 rm -rf expired &&
369 mkdir expired &&
370 expire_to="$(pwd)/expired/pack" &&
371 GIT_TRACE2_EVENT=$(pwd)/trace2.txt git -C cruft--max-size gc --no-cruft --prune=now --expire-to="$expire_to" &&
372 test_subcommand git repack -d -l -a <trace2.txt
373 '
374
375 run_and_wait_for_gc () {
376 # We read stdout from gc for the side effect of waiting until the
377 # background gc process exits, closing its fd 9. Furthermore, the
378 # variable assignment from a command substitution preserves the
379 # exit status of the main gc process.
380 # Note: this fd trickery doesn't work on Windows, but there is no
381 # need to, because on Win the auto gc always runs in the foreground.
382 doesnt_matter=$(git gc "$@" 9>&1)
383 }
384
385 test_expect_success 'background auto gc does not run if gc.log is present and recent but does if it is old' '
386 test_commit foo &&
387 test_commit bar &&
388 git repack &&
389 test_config gc.autopacklimit 1 &&
390 test_config gc.autodetach true &&
391 echo fleem >.git/gc.log &&
392 git gc --auto 2>err &&
393 test_grep "^warning:" err &&
394 test_config gc.logexpiry 5.days &&
395 test-tool chmtime =-345600 .git/gc.log &&
396 git gc --auto &&
397 test_config gc.logexpiry 2.days &&
398 run_and_wait_for_gc --auto &&
399 ls .git/objects/pack/pack-*.pack >packs &&
400 test_line_count = 1 packs
401 '
402
403 test_expect_success 'background auto gc respects lock for all operations' '
404 # make sure we run a background auto-gc
405 test_commit make-pack &&
406 git repack &&
407 test_config gc.autopacklimit 1 &&
408 test_config gc.autodetach true &&
409
410 # create a ref whose loose presence we can use to detect a pack-refs run
411 git update-ref refs/heads/should-be-loose HEAD &&
412 (ls -1 .git/refs/heads .git/reftable >expect || true) &&
413
414 # now fake a concurrent gc that holds the lock; we can use our
415 # shell pid so that it looks valid.
416 hostname=$(hostname || echo unknown) &&
417 shell_pid=$$ &&
418 if test_have_prereq MINGW && test -f /proc/$shell_pid/winpid
419 then
420 # In Git for Windows, Bash (actually, the MSYS2 runtime) has a
421 # different idea of PIDs than git.exe (actually Windows). Use
422 # the Windows PID in this case.
423 shell_pid=$(cat /proc/$shell_pid/winpid)
424 fi &&
425 printf "%d %s" "$shell_pid" "$hostname" >.git/gc.pid &&
426
427 # our gc should exit zero without doing anything
428 run_and_wait_for_gc --auto &&
429 (ls -1 .git/refs/heads .git/reftable >actual || true) &&
430 test_cmp expect actual
431 '
432
433 test_expect_success '--detach overrides gc.autoDetach=false' '
434 test_when_finished "rm -rf repo" &&
435 git init repo &&
436 (
437 cd repo &&
438
439 # Prepare the repository such that git-gc(1) ends up repacking.
440 test_commit "$(test_oid blob17_1)" &&
441 test_commit "$(test_oid blob17_2)" &&
442 git config gc.autodetach false &&
443 git config gc.auto 2 &&
444
445 # Note that we cannot use `test_cmp` here to compare stderr
446 # because it may contain output from `set -x`.
447 run_and_wait_for_gc --auto --detach 2>actual &&
448 test_grep "Auto packing the repository in background for optimum performance." actual
449 )
450 '
451
452 test_expect_success '--no-detach overrides gc.autoDetach=true' '
453 test_when_finished "rm -rf repo" &&
454 git init repo &&
455 (
456 cd repo &&
457
458 # Prepare the repository such that git-gc(1) ends up repacking.
459 test_commit "$(test_oid blob17_1)" &&
460 test_commit "$(test_oid blob17_2)" &&
461 git config gc.autodetach true &&
462 git config gc.auto 2 &&
463
464 GIT_PROGRESS_DELAY=0 git gc --auto --no-detach 2>output &&
465 test_grep "Auto packing the repository for optimum performance." output &&
466 test_grep "Collecting referenced commits: 2, done." output
467 )
468 '
469
470 # DO NOT leave a detached auto gc process running near the end of the
471 # test script: it can run long enough in the background to racily
472 # interfere with the cleanup in 'test_done'.
473
474 test_done