maintenance: use Windows scheduled tasks
[git/debian.git] / t / t7900-maintenance.sh
blob135505f6195743735de20aaf81c92909fd8efc0c
1 #!/bin/sh
3 test_description='git maintenance builtin'
5 . ./test-lib.sh
7 GIT_TEST_COMMIT_GRAPH=0
8 GIT_TEST_MULTI_PACK_INDEX=0
10 test_lazy_prereq XMLLINT '
11 xmllint --version
14 test_xmllint () {
15 if test_have_prereq XMLLINT
16 then
17 xmllint --noout "$@"
18 else
19 true
23 test_expect_success 'help text' '
24 test_expect_code 129 git maintenance -h 2>err &&
25 test_i18ngrep "usage: git maintenance <subcommand>" err &&
26 test_expect_code 128 git maintenance barf 2>err &&
27 test_i18ngrep "invalid subcommand: barf" err &&
28 test_expect_code 129 git maintenance 2>err &&
29 test_i18ngrep "usage: git maintenance" err
32 test_expect_success 'run [--auto|--quiet]' '
33 GIT_TRACE2_EVENT="$(pwd)/run-no-auto.txt" \
34 git maintenance run 2>/dev/null &&
35 GIT_TRACE2_EVENT="$(pwd)/run-auto.txt" \
36 git maintenance run --auto 2>/dev/null &&
37 GIT_TRACE2_EVENT="$(pwd)/run-no-quiet.txt" \
38 git maintenance run --no-quiet 2>/dev/null &&
39 test_subcommand git gc --quiet <run-no-auto.txt &&
40 test_subcommand ! git gc --auto --quiet <run-auto.txt &&
41 test_subcommand git gc --no-quiet <run-no-quiet.txt
44 test_expect_success 'maintenance.auto config option' '
45 GIT_TRACE2_EVENT="$(pwd)/default" git commit --quiet --allow-empty -m 1 &&
46 test_subcommand git maintenance run --auto --quiet <default &&
47 GIT_TRACE2_EVENT="$(pwd)/true" \
48 git -c maintenance.auto=true \
49 commit --quiet --allow-empty -m 2 &&
50 test_subcommand git maintenance run --auto --quiet <true &&
51 GIT_TRACE2_EVENT="$(pwd)/false" \
52 git -c maintenance.auto=false \
53 commit --quiet --allow-empty -m 3 &&
54 test_subcommand ! git maintenance run --auto --quiet <false
57 test_expect_success 'maintenance.<task>.enabled' '
58 git config maintenance.gc.enabled false &&
59 git config maintenance.commit-graph.enabled true &&
60 GIT_TRACE2_EVENT="$(pwd)/run-config.txt" git maintenance run 2>err &&
61 test_subcommand ! git gc --quiet <run-config.txt &&
62 test_subcommand git commit-graph write --split --reachable --no-progress <run-config.txt
65 test_expect_success 'run --task=<task>' '
66 GIT_TRACE2_EVENT="$(pwd)/run-commit-graph.txt" \
67 git maintenance run --task=commit-graph 2>/dev/null &&
68 GIT_TRACE2_EVENT="$(pwd)/run-gc.txt" \
69 git maintenance run --task=gc 2>/dev/null &&
70 GIT_TRACE2_EVENT="$(pwd)/run-commit-graph.txt" \
71 git maintenance run --task=commit-graph 2>/dev/null &&
72 GIT_TRACE2_EVENT="$(pwd)/run-both.txt" \
73 git maintenance run --task=commit-graph --task=gc 2>/dev/null &&
74 test_subcommand ! git gc --quiet <run-commit-graph.txt &&
75 test_subcommand git gc --quiet <run-gc.txt &&
76 test_subcommand git gc --quiet <run-both.txt &&
77 test_subcommand git commit-graph write --split --reachable --no-progress <run-commit-graph.txt &&
78 test_subcommand ! git commit-graph write --split --reachable --no-progress <run-gc.txt &&
79 test_subcommand git commit-graph write --split --reachable --no-progress <run-both.txt
82 test_expect_success 'run --task=bogus' '
83 test_must_fail git maintenance run --task=bogus 2>err &&
84 test_i18ngrep "is not a valid task" err
87 test_expect_success 'run --task duplicate' '
88 test_must_fail git maintenance run --task=gc --task=gc 2>err &&
89 test_i18ngrep "cannot be selected multiple times" err
92 test_expect_success 'run --task=prefetch with no remotes' '
93 git maintenance run --task=prefetch 2>err &&
94 test_must_be_empty err
97 test_expect_success 'prefetch multiple remotes' '
98 git clone . clone1 &&
99 git clone . clone2 &&
100 git remote add remote1 "file://$(pwd)/clone1" &&
101 git remote add remote2 "file://$(pwd)/clone2" &&
102 git -C clone1 switch -c one &&
103 git -C clone2 switch -c two &&
104 test_commit -C clone1 one &&
105 test_commit -C clone2 two &&
106 GIT_TRACE2_EVENT="$(pwd)/run-prefetch.txt" git maintenance run --task=prefetch 2>/dev/null &&
107 fetchargs="--prune --no-tags --no-write-fetch-head --recurse-submodules=no --refmap= --quiet" &&
108 test_subcommand git fetch remote1 $fetchargs +refs/heads/\\*:refs/prefetch/remote1/\\* <run-prefetch.txt &&
109 test_subcommand git fetch remote2 $fetchargs +refs/heads/\\*:refs/prefetch/remote2/\\* <run-prefetch.txt &&
110 test_path_is_missing .git/refs/remotes &&
111 git log prefetch/remote1/one &&
112 git log prefetch/remote2/two &&
113 git fetch --all &&
114 test_cmp_rev refs/remotes/remote1/one refs/prefetch/remote1/one &&
115 test_cmp_rev refs/remotes/remote2/two refs/prefetch/remote2/two
118 test_expect_success 'loose-objects task' '
119 # Repack everything so we know the state of the object dir
120 git repack -adk &&
122 # Hack to stop maintenance from running during "git commit"
123 echo in use >.git/objects/maintenance.lock &&
125 # Assuming that "git commit" creates at least one loose object
126 test_commit create-loose-object &&
127 rm .git/objects/maintenance.lock &&
129 ls .git/objects >obj-dir-before &&
130 test_file_not_empty obj-dir-before &&
131 ls .git/objects/pack/*.pack >packs-before &&
132 test_line_count = 1 packs-before &&
134 # The first run creates a pack-file
135 # but does not delete loose objects.
136 git maintenance run --task=loose-objects &&
137 ls .git/objects >obj-dir-between &&
138 test_cmp obj-dir-before obj-dir-between &&
139 ls .git/objects/pack/*.pack >packs-between &&
140 test_line_count = 2 packs-between &&
141 ls .git/objects/pack/loose-*.pack >loose-packs &&
142 test_line_count = 1 loose-packs &&
144 # The second run deletes loose objects
145 # but does not create a pack-file.
146 git maintenance run --task=loose-objects &&
147 ls .git/objects >obj-dir-after &&
148 cat >expect <<-\EOF &&
149 info
150 pack
152 test_cmp expect obj-dir-after &&
153 ls .git/objects/pack/*.pack >packs-after &&
154 test_cmp packs-between packs-after
157 test_expect_success 'maintenance.loose-objects.auto' '
158 git repack -adk &&
159 GIT_TRACE2_EVENT="$(pwd)/trace-lo1.txt" \
160 git -c maintenance.loose-objects.auto=1 maintenance \
161 run --auto --task=loose-objects 2>/dev/null &&
162 test_subcommand ! git prune-packed --quiet <trace-lo1.txt &&
163 printf data-A | git hash-object -t blob --stdin -w &&
164 GIT_TRACE2_EVENT="$(pwd)/trace-loA" \
165 git -c maintenance.loose-objects.auto=2 \
166 maintenance run --auto --task=loose-objects 2>/dev/null &&
167 test_subcommand ! git prune-packed --quiet <trace-loA &&
168 printf data-B | git hash-object -t blob --stdin -w &&
169 GIT_TRACE2_EVENT="$(pwd)/trace-loB" \
170 git -c maintenance.loose-objects.auto=2 \
171 maintenance run --auto --task=loose-objects 2>/dev/null &&
172 test_subcommand git prune-packed --quiet <trace-loB &&
173 GIT_TRACE2_EVENT="$(pwd)/trace-loC" \
174 git -c maintenance.loose-objects.auto=2 \
175 maintenance run --auto --task=loose-objects 2>/dev/null &&
176 test_subcommand git prune-packed --quiet <trace-loC
179 test_expect_success 'incremental-repack task' '
180 packDir=.git/objects/pack &&
181 for i in $(test_seq 1 5)
183 test_commit $i || return 1
184 done &&
186 # Create three disjoint pack-files with size BIG, small, small.
187 echo HEAD~2 | git pack-objects --revs $packDir/test-1 &&
188 test_tick &&
189 git pack-objects --revs $packDir/test-2 <<-\EOF &&
190 HEAD~1
191 ^HEAD~2
193 test_tick &&
194 git pack-objects --revs $packDir/test-3 <<-\EOF &&
195 HEAD
196 ^HEAD~1
198 rm -f $packDir/pack-* &&
199 rm -f $packDir/loose-* &&
200 ls $packDir/*.pack >packs-before &&
201 test_line_count = 3 packs-before &&
203 # the job repacks the two into a new pack, but does not
204 # delete the old ones.
205 git maintenance run --task=incremental-repack &&
206 ls $packDir/*.pack >packs-between &&
207 test_line_count = 4 packs-between &&
209 # the job deletes the two old packs, and does not write
210 # a new one because the batch size is not high enough to
211 # pack the largest pack-file.
212 git maintenance run --task=incremental-repack &&
213 ls .git/objects/pack/*.pack >packs-after &&
214 test_line_count = 2 packs-after
217 test_expect_success EXPENSIVE 'incremental-repack 2g limit' '
218 for i in $(test_seq 1 5)
220 test-tool genrandom foo$i $((512 * 1024 * 1024 + 1)) >>big ||
221 return 1
222 done &&
223 git add big &&
224 git commit -m "Add big file (1)" &&
226 # ensure any possible loose objects are in a pack-file
227 git maintenance run --task=loose-objects &&
229 rm big &&
230 for i in $(test_seq 6 10)
232 test-tool genrandom foo$i $((512 * 1024 * 1024 + 1)) >>big ||
233 return 1
234 done &&
235 git add big &&
236 git commit -m "Add big file (2)" &&
238 # ensure any possible loose objects are in a pack-file
239 git maintenance run --task=loose-objects &&
241 # Now run the incremental-repack task and check the batch-size
242 GIT_TRACE2_EVENT="$(pwd)/run-2g.txt" git maintenance run \
243 --task=incremental-repack 2>/dev/null &&
244 test_subcommand git multi-pack-index repack \
245 --no-progress --batch-size=2147483647 <run-2g.txt
248 test_expect_success 'maintenance.incremental-repack.auto' '
249 git repack -adk &&
250 git config core.multiPackIndex true &&
251 git multi-pack-index write &&
252 GIT_TRACE2_EVENT="$(pwd)/midx-init.txt" git \
253 -c maintenance.incremental-repack.auto=1 \
254 maintenance run --auto --task=incremental-repack 2>/dev/null &&
255 test_subcommand ! git multi-pack-index write --no-progress <midx-init.txt &&
256 test_commit A &&
257 git pack-objects --revs .git/objects/pack/pack <<-\EOF &&
258 HEAD
259 ^HEAD~1
261 GIT_TRACE2_EVENT=$(pwd)/trace-A git \
262 -c maintenance.incremental-repack.auto=2 \
263 maintenance run --auto --task=incremental-repack 2>/dev/null &&
264 test_subcommand ! git multi-pack-index write --no-progress <trace-A &&
265 test_commit B &&
266 git pack-objects --revs .git/objects/pack/pack <<-\EOF &&
267 HEAD
268 ^HEAD~1
270 GIT_TRACE2_EVENT=$(pwd)/trace-B git \
271 -c maintenance.incremental-repack.auto=2 \
272 maintenance run --auto --task=incremental-repack 2>/dev/null &&
273 test_subcommand git multi-pack-index write --no-progress <trace-B
276 test_expect_success '--auto and --schedule incompatible' '
277 test_must_fail git maintenance run --auto --schedule=daily 2>err &&
278 test_i18ngrep "at most one" err
281 test_expect_success 'invalid --schedule value' '
282 test_must_fail git maintenance run --schedule=annually 2>err &&
283 test_i18ngrep "unrecognized --schedule" err
286 test_expect_success '--schedule inheritance weekly -> daily -> hourly' '
287 git config maintenance.loose-objects.enabled true &&
288 git config maintenance.loose-objects.schedule hourly &&
289 git config maintenance.commit-graph.enabled true &&
290 git config maintenance.commit-graph.schedule daily &&
291 git config maintenance.incremental-repack.enabled true &&
292 git config maintenance.incremental-repack.schedule weekly &&
294 GIT_TRACE2_EVENT="$(pwd)/hourly.txt" \
295 git maintenance run --schedule=hourly 2>/dev/null &&
296 test_subcommand git prune-packed --quiet <hourly.txt &&
297 test_subcommand ! git commit-graph write --split --reachable \
298 --no-progress <hourly.txt &&
299 test_subcommand ! git multi-pack-index write --no-progress <hourly.txt &&
301 GIT_TRACE2_EVENT="$(pwd)/daily.txt" \
302 git maintenance run --schedule=daily 2>/dev/null &&
303 test_subcommand git prune-packed --quiet <daily.txt &&
304 test_subcommand git commit-graph write --split --reachable \
305 --no-progress <daily.txt &&
306 test_subcommand ! git multi-pack-index write --no-progress <daily.txt &&
308 GIT_TRACE2_EVENT="$(pwd)/weekly.txt" \
309 git maintenance run --schedule=weekly 2>/dev/null &&
310 test_subcommand git prune-packed --quiet <weekly.txt &&
311 test_subcommand git commit-graph write --split --reachable \
312 --no-progress <weekly.txt &&
313 test_subcommand git multi-pack-index write --no-progress <weekly.txt
316 test_expect_success 'maintenance.strategy inheritance' '
317 for task in commit-graph loose-objects incremental-repack
319 git config --unset maintenance.$task.schedule || return 1
320 done &&
322 test_when_finished git config --unset maintenance.strategy &&
323 git config maintenance.strategy incremental &&
325 GIT_TRACE2_EVENT="$(pwd)/incremental-hourly.txt" \
326 git maintenance run --schedule=hourly --quiet &&
327 GIT_TRACE2_EVENT="$(pwd)/incremental-daily.txt" \
328 git maintenance run --schedule=daily --quiet &&
330 test_subcommand git commit-graph write --split --reachable \
331 --no-progress <incremental-hourly.txt &&
332 test_subcommand ! git prune-packed --quiet <incremental-hourly.txt &&
333 test_subcommand ! git multi-pack-index write --no-progress \
334 <incremental-hourly.txt &&
336 test_subcommand git commit-graph write --split --reachable \
337 --no-progress <incremental-daily.txt &&
338 test_subcommand git prune-packed --quiet <incremental-daily.txt &&
339 test_subcommand git multi-pack-index write --no-progress \
340 <incremental-daily.txt &&
342 # Modify defaults
343 git config maintenance.commit-graph.schedule daily &&
344 git config maintenance.loose-objects.schedule hourly &&
345 git config maintenance.incremental-repack.enabled false &&
347 GIT_TRACE2_EVENT="$(pwd)/modified-hourly.txt" \
348 git maintenance run --schedule=hourly --quiet &&
349 GIT_TRACE2_EVENT="$(pwd)/modified-daily.txt" \
350 git maintenance run --schedule=daily --quiet &&
352 test_subcommand ! git commit-graph write --split --reachable \
353 --no-progress <modified-hourly.txt &&
354 test_subcommand git prune-packed --quiet <modified-hourly.txt &&
355 test_subcommand ! git multi-pack-index write --no-progress \
356 <modified-hourly.txt &&
358 test_subcommand git commit-graph write --split --reachable \
359 --no-progress <modified-daily.txt &&
360 test_subcommand git prune-packed --quiet <modified-daily.txt &&
361 test_subcommand ! git multi-pack-index write --no-progress \
362 <modified-daily.txt
365 test_expect_success 'register and unregister' '
366 test_when_finished git config --global --unset-all maintenance.repo &&
367 git config --global --add maintenance.repo /existing1 &&
368 git config --global --add maintenance.repo /existing2 &&
369 git config --global --get-all maintenance.repo >before &&
371 git maintenance register &&
372 test_cmp_config false maintenance.auto &&
373 git config --global --get-all maintenance.repo >between &&
374 cp before expect &&
375 pwd >>expect &&
376 test_cmp expect between &&
378 git maintenance unregister &&
379 git config --global --get-all maintenance.repo >actual &&
380 test_cmp before actual
383 test_expect_success 'start from empty cron table' '
384 GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab cron.txt" git maintenance start &&
386 # start registers the repo
387 git config --get --global maintenance.repo "$(pwd)" &&
389 grep "for-each-repo --config=maintenance.repo maintenance run --schedule=daily" cron.txt &&
390 grep "for-each-repo --config=maintenance.repo maintenance run --schedule=hourly" cron.txt &&
391 grep "for-each-repo --config=maintenance.repo maintenance run --schedule=weekly" cron.txt
394 test_expect_success 'stop from existing schedule' '
395 GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab cron.txt" git maintenance stop &&
397 # stop does not unregister the repo
398 git config --get --global maintenance.repo "$(pwd)" &&
400 # Operation is idempotent
401 GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab cron.txt" git maintenance stop &&
402 test_must_be_empty cron.txt
405 test_expect_success 'start preserves existing schedule' '
406 echo "Important information!" >cron.txt &&
407 GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab cron.txt" git maintenance start &&
408 grep "Important information!" cron.txt
411 test_expect_success 'start and stop macOS maintenance' '
412 # ensure $HOME can be compared against hook arguments on all platforms
413 pfx=$(cd "$HOME" && pwd) &&
415 write_script print-args <<-\EOF &&
416 echo $* | sed "s:gui/[0-9][0-9]*:gui/[UID]:" >>args
419 rm -f args &&
420 GIT_TEST_MAINT_SCHEDULER=launchctl:./print-args git maintenance start &&
422 # start registers the repo
423 git config --get --global maintenance.repo "$(pwd)" &&
425 ls "$HOME/Library/LaunchAgents" >actual &&
426 cat >expect <<-\EOF &&
427 org.git-scm.git.daily.plist
428 org.git-scm.git.hourly.plist
429 org.git-scm.git.weekly.plist
431 test_cmp expect actual &&
433 rm -f expect &&
434 for frequency in hourly daily weekly
436 PLIST="$pfx/Library/LaunchAgents/org.git-scm.git.$frequency.plist" &&
437 test_xmllint "$PLIST" &&
438 grep schedule=$frequency "$PLIST" &&
439 echo "bootout gui/[UID] $PLIST" >>expect &&
440 echo "bootstrap gui/[UID] $PLIST" >>expect || return 1
441 done &&
442 test_cmp expect args &&
444 rm -f args &&
445 GIT_TEST_MAINT_SCHEDULER=launchctl:./print-args git maintenance stop &&
447 # stop does not unregister the repo
448 git config --get --global maintenance.repo "$(pwd)" &&
450 printf "bootout gui/[UID] $pfx/Library/LaunchAgents/org.git-scm.git.%s.plist\n" \
451 hourly daily weekly >expect &&
452 test_cmp expect args &&
453 ls "$HOME/Library/LaunchAgents" >actual &&
454 test_line_count = 0 actual
457 test_expect_success 'start and stop Windows maintenance' '
458 write_script print-args <<-\EOF &&
459 echo $* >>args
460 while test $# -gt 0
462 case "$1" in
463 /xml) shift; xmlfile=$1; break ;;
464 *) shift ;;
465 esac
466 done
467 test -z "$xmlfile" || cp "$xmlfile" "$xmlfile.xml"
470 rm -f args &&
471 GIT_TEST_MAINT_SCHEDULER="schtasks:./print-args" git maintenance start &&
473 # start registers the repo
474 git config --get --global maintenance.repo "$(pwd)" &&
476 for frequency in hourly daily weekly
478 grep "/create /tn Git Maintenance ($frequency) /f /xml" args &&
479 file=$(ls .git/schedule_${frequency}*.xml) &&
480 test_xmllint "$file" || return 1
481 done &&
483 rm -f args &&
484 GIT_TEST_MAINT_SCHEDULER="schtasks:./print-args" git maintenance stop &&
486 # stop does not unregister the repo
487 git config --get --global maintenance.repo "$(pwd)" &&
489 printf "/delete /tn Git Maintenance (%s) /f\n" \
490 hourly daily weekly >expect &&
491 test_cmp expect args
494 test_expect_success 'register preserves existing strategy' '
495 git config maintenance.strategy none &&
496 git maintenance register &&
497 test_config maintenance.strategy none &&
498 git config --unset maintenance.strategy &&
499 git maintenance register &&
500 test_config maintenance.strategy incremental
503 test_done