test-lib: introduce test_commit_bulk

Some tests need to create a string of commits. Doing this with test_commit is very heavy-weight, as it needs at least one process per commit (and in fact, uses several). For bulk creation, we can do much better by using fast-import, but it's often a pain to generate the input. Let's provide a helper to do so. We'll use t5310 as a guinea pig, as it has three 10-commit loops. Here are hyperfine results before and after: [before] Benchmark #1: ./t5310-pack-bitmaps.sh --root=/var/ram/git-tests Time (mean ± σ): 2.846 s ± 0.305 s [User: 3.042 s, System: 0.919 s] Range (min … max): 2.250 s … 3.210 s 10 runs [after] Benchmark #1: ./t5310-pack-bitmaps.sh --root=/var/ram/git-tests Time (mean ± σ): 2.210 s ± 0.174 s [User: 2.570 s, System: 0.604 s] Range (min … max): 1.999 s … 2.590 s 10 runs So we're over 20% faster, while making the callers slightly shorter. We added a lot more lines in test-lib-function.sh, of course, and the helper is way more featureful than we need here. But my hope is that it will be flexible enough to use in more places. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed Jul 2, 2019 at 01:16 UTC b1c36cb84908a31ede05e5a16fbc584c7681cffb
2 files changed +126 -12
t/t5310-pack-bitmaps.sh
+3 -12
@@ -21,15 +21,9 @@ has_any () {
21 }
22
23 test_expect_success 'setup repo with moderate-sized history' '
24 - for i in $(test_seq 1 10)
25 - do
26 - test_commit $i
27 - done &&
24 + test_commit_bulk --id=file 10 &&
25 git checkout -b other HEAD~5 &&
29 - for i in $(test_seq 1 10)
30 - do
31 - test_commit side-$i
32 - done &&
26 + test_commit_bulk --id=side 10 &&
27 git checkout master &&
28 bitmaptip=$(git rev-parse master) &&
29 blob=$(echo tagged-blob | git hash-object -w --stdin) &&
@@ -106,10 +100,7 @@ test_expect_success 'clone from bitmapped repository' '
100 '
101
102 test_expect_success 'setup further non-bitmapped commits' '
109 - for i in $(test_seq 1 10)
110 - do
111 - test_commit further-$i
112 - done
103 + test_commit_bulk --id=further 10
104 '
105
106 rev_list_tests 'partial bitmap'
t/test-lib-functions.sh
+123
@@ -233,6 +233,129 @@ test_merge () {
233 git tag "$1"
234 }
235
236 +# Efficiently create <nr> commits, each with a unique number (from 1 to <nr>
237 +# by default) in the commit message.
238 +#
239 +# Usage: test_commit_bulk [options] <nr>
240 +# -C <dir>:
241 +# Run all git commands in directory <dir>
242 +# --ref=<n>:
243 +# ref on which to create commits (default: HEAD)
244 +# --start=<n>:
245 +# number commit messages from <n> (default: 1)
246 +# --message=<msg>:
247 +# use <msg> as the commit mesasge (default: "commit %s")
248 +# --filename=<fn>:
249 +# modify <fn> in each commit (default: %s.t)
250 +# --contents=<string>:
251 +# place <string> in each file (default: "content %s")
252 +# --id=<string>:
253 +# shorthand to use <string> and %s in message, filename, and contents
254 +#
255 +# The message, filename, and contents strings are evaluated by printf, with the
256 +# first "%s" replaced by the current commit number. So you can do:
257 +#
258 +# test_commit_bulk --filename=file --contents="modification %s"
259 +#
260 +# to have every commit touch the same file, but with unique content.
261 +#
262 +test_commit_bulk () {
263 + tmpfile=.bulk-commit.input
264 + indir=.
265 + ref=HEAD
266 + n=1
267 + message='commit %s'
268 + filename='%s.t'
269 + contents='content %s'
270 + while test $# -gt 0
271 + do
272 + case "$1" in
273 + -C)
274 + indir=$2
275 + shift
276 + ;;
277 + --ref=*)
278 + ref=${1#--*=}
279 + ;;
280 + --start=*)
281 + n=${1#--*=}
282 + ;;
283 + --message=*)
284 + message=${1#--*=}
285 + ;;
286 + --filename=*)
287 + filename=${1#--*=}
288 + ;;
289 + --contents=*)
290 + contents=${1#--*=}
291 + ;;
292 + --id=*)
293 + message="${1#--*=} %s"
294 + filename="${1#--*=}-%s.t"
295 + contents="${1#--*=} %s"
296 + ;;
297 + -*)
298 + BUG "invalid test_commit_bulk option: $1"
299 + ;;
300 + *)
301 + break
302 + ;;
303 + esac
304 + shift
305 + done
306 + total=$1
307 +
308 + add_from=
309 + if git -C "$indir" rev-parse --verify "$ref"
310 + then
311 + add_from=t
312 + fi
313 +
314 + while test "$total" -gt 0
315 + do
316 + test_tick &&
317 + echo "commit $ref"
318 + printf 'author %s <%s> %s\n' \
319 + "$GIT_AUTHOR_NAME" \
320 + "$GIT_AUTHOR_EMAIL" \
321 + "$GIT_AUTHOR_DATE"
322 + printf 'committer %s <%s> %s\n' \
323 + "$GIT_COMMITTER_NAME" \
324 + "$GIT_COMMITTER_EMAIL" \
325 + "$GIT_COMMITTER_DATE"
326 + echo "data <<EOF"
327 + printf "$message\n" $n
328 + echo "EOF"
329 + if test -n "$add_from"
330 + then
331 + echo "from $ref^0"
332 + add_from=
333 + fi
334 + printf "M 644 inline $filename\n" $n
335 + echo "data <<EOF"
336 + printf "$contents\n" $n
337 + echo "EOF"
338 + echo
339 + n=$((n + 1))
340 + total=$((total - 1))
341 + done >"$tmpfile"
342 +
343 + git -C "$indir" \
344 + -c fastimport.unpacklimit=0 \
345 + fast-import <"$tmpfile" || return 1
346 +
347 + # This will be left in place on failure, which may aid debugging.
348 + rm -f "$tmpfile"
349 +
350 + # If we updated HEAD, then be nice and update the index and working
351 + # tree, too.
352 + if test "$ref" = "HEAD"
353 + then
354 + git -C "$indir" checkout -f HEAD || return 1
355 + fi
356 +
357 +}
358 +
359 # This function helps systems where core.filemode=false is set.
360 # Use it instead of plain 'chmod +x' to set or unset the executable bit
361 # of a file in the working directory and add it to the index.