3 test_description
='git maintenance builtin'
7 GIT_TEST_COMMIT_GRAPH
=0
8 GIT_TEST_MULTI_PACK_INDEX
=0
10 test_expect_success
'help text' '
11 test_expect_code 129 git maintenance -h 2>err &&
12 test_i18ngrep "usage: git maintenance <subcommand>" err &&
13 test_expect_code 128 git maintenance barf 2>err &&
14 test_i18ngrep "invalid subcommand: barf" err &&
15 test_expect_code 129 git maintenance 2>err &&
16 test_i18ngrep "usage: git maintenance" err
19 test_expect_success
'run [--auto|--quiet]' '
20 GIT_TRACE2_EVENT="$(pwd)/run-no-auto.txt" \
21 git maintenance run 2>/dev/null &&
22 GIT_TRACE2_EVENT="$(pwd)/run-auto.txt" \
23 git maintenance run --auto 2>/dev/null &&
24 GIT_TRACE2_EVENT="$(pwd)/run-no-quiet.txt" \
25 git maintenance run --no-quiet 2>/dev/null &&
26 test_subcommand git gc --quiet <run-no-auto.txt &&
27 test_subcommand ! git gc --auto --quiet <run-auto.txt &&
28 test_subcommand git gc --no-quiet <run-no-quiet.txt
31 test_expect_success
'maintenance.auto config option' '
32 GIT_TRACE2_EVENT="$(pwd)/default" git commit --quiet --allow-empty -m 1 &&
33 test_subcommand git maintenance run --auto --quiet <default &&
34 GIT_TRACE2_EVENT="$(pwd)/true" \
35 git -c maintenance.auto=true \
36 commit --quiet --allow-empty -m 2 &&
37 test_subcommand git maintenance run --auto --quiet <true &&
38 GIT_TRACE2_EVENT="$(pwd)/false" \
39 git -c maintenance.auto=false \
40 commit --quiet --allow-empty -m 3 &&
41 test_subcommand ! git maintenance run --auto --quiet <false
44 test_expect_success
'maintenance.<task>.enabled' '
45 git config maintenance.gc.enabled false &&
46 git config maintenance.commit-graph.enabled true &&
47 GIT_TRACE2_EVENT="$(pwd)/run-config.txt" git maintenance run 2>err &&
48 test_subcommand ! git gc --quiet <run-config.txt &&
49 test_subcommand git commit-graph write --split --reachable --no-progress <run-config.txt
52 test_expect_success
'run --task=<task>' '
53 GIT_TRACE2_EVENT="$(pwd)/run-commit-graph.txt" \
54 git maintenance run --task=commit-graph 2>/dev/null &&
55 GIT_TRACE2_EVENT="$(pwd)/run-gc.txt" \
56 git maintenance run --task=gc 2>/dev/null &&
57 GIT_TRACE2_EVENT="$(pwd)/run-commit-graph.txt" \
58 git maintenance run --task=commit-graph 2>/dev/null &&
59 GIT_TRACE2_EVENT="$(pwd)/run-both.txt" \
60 git maintenance run --task=commit-graph --task=gc 2>/dev/null &&
61 test_subcommand ! git gc --quiet <run-commit-graph.txt &&
62 test_subcommand git gc --quiet <run-gc.txt &&
63 test_subcommand git gc --quiet <run-both.txt &&
64 test_subcommand git commit-graph write --split --reachable --no-progress <run-commit-graph.txt &&
65 test_subcommand ! git commit-graph write --split --reachable --no-progress <run-gc.txt &&
66 test_subcommand git commit-graph write --split --reachable --no-progress <run-both.txt
69 test_expect_success
'run --task=bogus' '
70 test_must_fail git maintenance run --task=bogus 2>err &&
71 test_i18ngrep "is not a valid task" err
74 test_expect_success
'run --task duplicate' '
75 test_must_fail git maintenance run --task=gc --task=gc 2>err &&
76 test_i18ngrep "cannot be selected multiple times" err
79 test_expect_success
'run --task=prefetch with no remotes' '
80 git maintenance run --task=prefetch 2>err &&
81 test_must_be_empty err
84 test_expect_success
'prefetch multiple remotes' '
87 git remote add remote1 "file://$(pwd)/clone1" &&
88 git remote add remote2 "file://$(pwd)/clone2" &&
89 git -C clone1 switch -c one &&
90 git -C clone2 switch -c two &&
91 test_commit -C clone1 one &&
92 test_commit -C clone2 two &&
93 GIT_TRACE2_EVENT="$(pwd)/run-prefetch.txt" git maintenance run --task=prefetch 2>/dev/null &&
94 fetchargs="--prune --no-tags --no-write-fetch-head --recurse-submodules=no --refmap= --quiet" &&
95 test_subcommand git fetch remote1 $fetchargs +refs/heads/\\*:refs/prefetch/remote1/\\* <run-prefetch.txt &&
96 test_subcommand git fetch remote2 $fetchargs +refs/heads/\\*:refs/prefetch/remote2/\\* <run-prefetch.txt &&
97 test_path_is_missing .git/refs/remotes &&
98 git log prefetch/remote1/one &&
99 git log prefetch/remote2/two &&
101 test_cmp_rev refs/remotes/remote1/one refs/prefetch/remote1/one &&
102 test_cmp_rev refs/remotes/remote2/two refs/prefetch/remote2/two
105 test_expect_success
'loose-objects task' '
106 # Repack everything so we know the state of the object dir
109 # Hack to stop maintenance from running during "git commit"
110 echo in use >.git/objects/maintenance.lock &&
112 # Assuming that "git commit" creates at least one loose object
113 test_commit create-loose-object &&
114 rm .git/objects/maintenance.lock &&
116 ls .git/objects >obj-dir-before &&
117 test_file_not_empty obj-dir-before &&
118 ls .git/objects/pack/*.pack >packs-before &&
119 test_line_count = 1 packs-before &&
121 # The first run creates a pack-file
122 # but does not delete loose objects.
123 git maintenance run --task=loose-objects &&
124 ls .git/objects >obj-dir-between &&
125 test_cmp obj-dir-before obj-dir-between &&
126 ls .git/objects/pack/*.pack >packs-between &&
127 test_line_count = 2 packs-between &&
128 ls .git/objects/pack/loose-*.pack >loose-packs &&
129 test_line_count = 1 loose-packs &&
131 # The second run deletes loose objects
132 # but does not create a pack-file.
133 git maintenance run --task=loose-objects &&
134 ls .git/objects >obj-dir-after &&
135 cat >expect <<-\EOF &&
139 test_cmp expect obj-dir-after &&
140 ls .git/objects/pack/*.pack >packs-after &&
141 test_cmp packs-between packs-after
144 test_expect_success
'maintenance.loose-objects.auto' '
146 GIT_TRACE2_EVENT="$(pwd)/trace-lo1.txt" \
147 git -c maintenance.loose-objects.auto=1 maintenance \
148 run --auto --task=loose-objects 2>/dev/null &&
149 test_subcommand ! git prune-packed --quiet <trace-lo1.txt &&
150 printf data-A | git hash-object -t blob --stdin -w &&
151 GIT_TRACE2_EVENT="$(pwd)/trace-loA" \
152 git -c maintenance.loose-objects.auto=2 \
153 maintenance run --auto --task=loose-objects 2>/dev/null &&
154 test_subcommand ! git prune-packed --quiet <trace-loA &&
155 printf data-B | git hash-object -t blob --stdin -w &&
156 GIT_TRACE2_EVENT="$(pwd)/trace-loB" \
157 git -c maintenance.loose-objects.auto=2 \
158 maintenance run --auto --task=loose-objects 2>/dev/null &&
159 test_subcommand git prune-packed --quiet <trace-loB &&
160 GIT_TRACE2_EVENT="$(pwd)/trace-loC" \
161 git -c maintenance.loose-objects.auto=2 \
162 maintenance run --auto --task=loose-objects 2>/dev/null &&
163 test_subcommand git prune-packed --quiet <trace-loC
166 test_expect_success
'incremental-repack task' '
167 packDir=.git/objects/pack &&
168 for i in $(test_seq 1 5)
170 test_commit $i || return 1
173 # Create three disjoint pack-files with size BIG, small, small.
174 echo HEAD~2 | git pack-objects --revs $packDir/test-1 &&
176 git pack-objects --revs $packDir/test-2 <<-\EOF &&
181 git pack-objects --revs $packDir/test-3 <<-\EOF &&
185 rm -f $packDir/pack-* &&
186 rm -f $packDir/loose-* &&
187 ls $packDir/*.pack >packs-before &&
188 test_line_count = 3 packs-before &&
190 # the job repacks the two into a new pack, but does not
191 # delete the old ones.
192 git maintenance run --task=incremental-repack &&
193 ls $packDir/*.pack >packs-between &&
194 test_line_count = 4 packs-between &&
196 # the job deletes the two old packs, and does not write
197 # a new one because the batch size is not high enough to
198 # pack the largest pack-file.
199 git maintenance run --task=incremental-repack &&
200 ls .git/objects/pack/*.pack >packs-after &&
201 test_line_count = 2 packs-after
204 test_expect_success EXPENSIVE
'incremental-repack 2g limit' '
205 for i in $(test_seq 1 5)
207 test-tool genrandom foo$i $((512 * 1024 * 1024 + 1)) >>big ||
211 git commit -m "Add big file (1)" &&
213 # ensure any possible loose objects are in a pack-file
214 git maintenance run --task=loose-objects &&
217 for i in $(test_seq 6 10)
219 test-tool genrandom foo$i $((512 * 1024 * 1024 + 1)) >>big ||
223 git commit -m "Add big file (2)" &&
225 # ensure any possible loose objects are in a pack-file
226 git maintenance run --task=loose-objects &&
228 # Now run the incremental-repack task and check the batch-size
229 GIT_TRACE2_EVENT="$(pwd)/run-2g.txt" git maintenance run \
230 --task=incremental-repack 2>/dev/null &&
231 test_subcommand git multi-pack-index repack \
232 --no-progress --batch-size=2147483647 <run-2g.txt
235 test_expect_success
'maintenance.incremental-repack.auto' '
237 git config core.multiPackIndex true &&
238 git multi-pack-index write &&
239 GIT_TRACE2_EVENT="$(pwd)/midx-init.txt" git \
240 -c maintenance.incremental-repack.auto=1 \
241 maintenance run --auto --task=incremental-repack 2>/dev/null &&
242 test_subcommand ! git multi-pack-index write --no-progress <midx-init.txt &&
244 git pack-objects --revs .git/objects/pack/pack <<-\EOF &&
248 GIT_TRACE2_EVENT=$(pwd)/trace-A git \
249 -c maintenance.incremental-repack.auto=2 \
250 maintenance run --auto --task=incremental-repack 2>/dev/null &&
251 test_subcommand ! git multi-pack-index write --no-progress <trace-A &&
253 git pack-objects --revs .git/objects/pack/pack <<-\EOF &&
257 GIT_TRACE2_EVENT=$(pwd)/trace-B git \
258 -c maintenance.incremental-repack.auto=2 \
259 maintenance run --auto --task=incremental-repack 2>/dev/null &&
260 test_subcommand git multi-pack-index write --no-progress <trace-B
263 test_expect_success
'--auto and --schedule incompatible' '
264 test_must_fail git maintenance run --auto --schedule=daily 2>err &&
265 test_i18ngrep "at most one" err
268 test_expect_success
'invalid --schedule value' '
269 test_must_fail git maintenance run --schedule=annually 2>err &&
270 test_i18ngrep "unrecognized --schedule" err
273 test_expect_success
'--schedule inheritance weekly -> daily -> hourly' '
274 git config maintenance.loose-objects.enabled true &&
275 git config maintenance.loose-objects.schedule hourly &&
276 git config maintenance.commit-graph.enabled true &&
277 git config maintenance.commit-graph.schedule daily &&
278 git config maintenance.incremental-repack.enabled true &&
279 git config maintenance.incremental-repack.schedule weekly &&
281 GIT_TRACE2_EVENT="$(pwd)/hourly.txt" \
282 git maintenance run --schedule=hourly 2>/dev/null &&
283 test_subcommand git prune-packed --quiet <hourly.txt &&
284 test_subcommand ! git commit-graph write --split --reachable \
285 --no-progress <hourly.txt &&
286 test_subcommand ! git multi-pack-index write --no-progress <hourly.txt &&
288 GIT_TRACE2_EVENT="$(pwd)/daily.txt" \
289 git maintenance run --schedule=daily 2>/dev/null &&
290 test_subcommand git prune-packed --quiet <daily.txt &&
291 test_subcommand git commit-graph write --split --reachable \
292 --no-progress <daily.txt &&
293 test_subcommand ! git multi-pack-index write --no-progress <daily.txt &&
295 GIT_TRACE2_EVENT="$(pwd)/weekly.txt" \
296 git maintenance run --schedule=weekly 2>/dev/null &&
297 test_subcommand git prune-packed --quiet <weekly.txt &&
298 test_subcommand git commit-graph write --split --reachable \
299 --no-progress <weekly.txt &&
300 test_subcommand git multi-pack-index write --no-progress <weekly.txt
303 test_expect_success
'maintenance.strategy inheritance' '
304 for task in commit-graph loose-objects incremental-repack
306 git config --unset maintenance.$task.schedule || return 1
309 test_when_finished git config --unset maintenance.strategy &&
310 git config maintenance.strategy incremental &&
312 GIT_TRACE2_EVENT="$(pwd)/incremental-hourly.txt" \
313 git maintenance run --schedule=hourly --quiet &&
314 GIT_TRACE2_EVENT="$(pwd)/incremental-daily.txt" \
315 git maintenance run --schedule=daily --quiet &&
317 test_subcommand git commit-graph write --split --reachable \
318 --no-progress <incremental-hourly.txt &&
319 test_subcommand ! git prune-packed --quiet <incremental-hourly.txt &&
320 test_subcommand ! git multi-pack-index write --no-progress \
321 <incremental-hourly.txt &&
323 test_subcommand git commit-graph write --split --reachable \
324 --no-progress <incremental-daily.txt &&
325 test_subcommand git prune-packed --quiet <incremental-daily.txt &&
326 test_subcommand git multi-pack-index write --no-progress \
327 <incremental-daily.txt &&
330 git config maintenance.commit-graph.schedule daily &&
331 git config maintenance.loose-objects.schedule hourly &&
332 git config maintenance.incremental-repack.enabled false &&
334 GIT_TRACE2_EVENT="$(pwd)/modified-hourly.txt" \
335 git maintenance run --schedule=hourly --quiet &&
336 GIT_TRACE2_EVENT="$(pwd)/modified-daily.txt" \
337 git maintenance run --schedule=daily --quiet &&
339 test_subcommand ! git commit-graph write --split --reachable \
340 --no-progress <modified-hourly.txt &&
341 test_subcommand git prune-packed --quiet <modified-hourly.txt &&
342 test_subcommand ! git multi-pack-index write --no-progress \
343 <modified-hourly.txt &&
345 test_subcommand git commit-graph write --split --reachable \
346 --no-progress <modified-daily.txt &&
347 test_subcommand git prune-packed --quiet <modified-daily.txt &&
348 test_subcommand ! git multi-pack-index write --no-progress \
352 test_expect_success
'register and unregister' '
353 test_when_finished git config --global --unset-all maintenance.repo &&
354 git config --global --add maintenance.repo /existing1 &&
355 git config --global --add maintenance.repo /existing2 &&
356 git config --global --get-all maintenance.repo >before &&
358 git maintenance register &&
359 test_cmp_config false maintenance.auto &&
360 git config --global --get-all maintenance.repo >between &&
363 test_cmp expect between &&
365 git maintenance unregister &&
366 git config --global --get-all maintenance.repo >actual &&
367 test_cmp before actual
370 test_expect_success
'start from empty cron table' '
371 GIT_TEST_CRONTAB="test-tool crontab cron.txt" git maintenance start &&
373 # start registers the repo
374 git config --get --global maintenance.repo "$(pwd)" &&
376 grep "for-each-repo --config=maintenance.repo maintenance run --schedule=daily" cron.txt &&
377 grep "for-each-repo --config=maintenance.repo maintenance run --schedule=hourly" cron.txt &&
378 grep "for-each-repo --config=maintenance.repo maintenance run --schedule=weekly" cron.txt
381 test_expect_success
'stop from existing schedule' '
382 GIT_TEST_CRONTAB="test-tool crontab cron.txt" git maintenance stop &&
384 # stop does not unregister the repo
385 git config --get --global maintenance.repo "$(pwd)" &&
387 # Operation is idempotent
388 GIT_TEST_CRONTAB="test-tool crontab cron.txt" git maintenance stop &&
389 test_must_be_empty cron.txt
392 test_expect_success
'start preserves existing schedule' '
393 echo "Important information!" >cron.txt &&
394 GIT_TEST_CRONTAB="test-tool crontab cron.txt" git maintenance start &&
395 grep "Important information!" cron.txt
398 test_expect_success
'register preserves existing strategy' '
399 git config maintenance.strategy none &&
400 git maintenance register &&
401 test_config maintenance.strategy none &&
402 git config --unset maintenance.strategy &&
403 git maintenance register &&
404 test_config maintenance.strategy incremental