diff options
Diffstat (limited to 't')
145 files changed, 4962 insertions, 1034 deletions
diff --git a/t/Makefile b/t/Makefile index 46cd5fc527..056ce55dcc 100644 --- a/t/Makefile +++ b/t/Makefile @@ -1,3 +1,6 @@ +# Import tree-wide shared Makefile behavior and libraries +include ../shared.mak + # Run tests # # Copyright (c) 2005 Junio C Hamano diff --git a/t/helper/test-csprng.c b/t/helper/test-csprng.c new file mode 100644 index 0000000000..65d14973c5 --- /dev/null +++ b/t/helper/test-csprng.c @@ -0,0 +1,29 @@ +#include "test-tool.h" +#include "git-compat-util.h" + + +int cmd__csprng(int argc, const char **argv) +{ + unsigned long count; + unsigned char buf[1024]; + + if (argc > 2) { + fprintf(stderr, "usage: %s [<size>]\n", argv[0]); + return 2; + } + + count = (argc == 2) ? strtoul(argv[1], NULL, 0) : -1L; + + while (count) { + unsigned long chunk = count < sizeof(buf) ? count : sizeof(buf); + if (csprng_bytes(buf, chunk) < 0) { + perror("failed to read"); + return 5; + } + if (fwrite(buf, chunk, 1, stdout) != chunk) + return 1; + count -= chunk; + } + + return 0; +} diff --git a/t/helper/test-date.c b/t/helper/test-date.c index 099eff4f0f..45951b1df8 100644 --- a/t/helper/test-date.c +++ b/t/helper/test-date.c @@ -1,5 +1,6 @@ #include "test-tool.h" #include "cache.h" +#include "date.h" static const char *usage_msg = "\n" " test-tool date relative [time_t]...\n" @@ -34,7 +35,7 @@ static void show_human_dates(const char **argv) static void show_dates(const char **argv, const char *format) { - struct date_mode mode; + struct date_mode mode = DATE_MODE_INIT; parse_date_format(format, &mode); for (; *argv; argv++) { @@ -53,6 +54,8 @@ static void show_dates(const char **argv, const char *format) printf("%s -> %s\n", *argv, show_date(t, tz, &mode)); } + + date_mode_release(&mode); } static void parse_dates(const char **argv) diff --git a/t/helper/test-progress.c b/t/helper/test-progress.c index 5d05cbe789..6cc9735b60 100644 --- a/t/helper/test-progress.c +++ b/t/helper/test-progress.c @@ -3,6 +3,9 @@ * * Reads instructions from standard input, one instruction per line: * + * "start <total>[ <title>]" - Call start_progress(title, total), + * Uses the default title of "Working hard" + * if the " <title>" is omitted. * "progress <items>" - Call display_progress() with the given item count * as parameter. * "throughput <bytes> <millis> - Call display_throughput() with the given @@ -10,6 +13,7 @@ * specify the time elapsed since the * start_progress() call. * "update" - Set the 'progress_update' flag. + * "stop" - Call stop_progress(). * * See 't0500-progress-display.sh' for examples. */ @@ -19,34 +23,50 @@ #include "parse-options.h" #include "progress.h" #include "strbuf.h" +#include "string-list.h" int cmd__progress(int argc, const char **argv) { - int total = 0; - const char *title; + const char *const default_title = "Working hard"; + struct string_list titles = STRING_LIST_INIT_DUP; struct strbuf line = STRBUF_INIT; - struct progress *progress; + struct progress *progress = NULL; const char *usage[] = { - "test-tool progress [--total=<n>] <progress-title>", + "test-tool progress <stdin", NULL }; struct option options[] = { - OPT_INTEGER(0, "total", &total, "total number of items"), OPT_END(), }; argc = parse_options(argc, argv, NULL, options, usage, 0); - if (argc != 1) - die("need a title for the progress output"); - title = argv[0]; + if (argc) + usage_with_options(usage, options); progress_testing = 1; - progress = start_progress(title, total); while (strbuf_getline(&line, stdin) != EOF) { char *end; - if (skip_prefix(line.buf, "progress ", (const char **) &end)) { + if (skip_prefix(line.buf, "start ", (const char **) &end)) { + uint64_t total = strtoull(end, &end, 10); + const char *title; + + /* + * We can't use "end + 1" as an argument to + * start_progress(), it doesn't xstrdup() its + * "title" argument. We need to hold onto a + * valid "char *" for it until the end. + */ + if (!*end) + title = default_title; + else if (*end == ' ') + title = string_list_insert(&titles, end + 1)->string; + else + die("invalid input: '%s'\n", line.buf); + + progress = start_progress(title, total); + } else if (skip_prefix(line.buf, "progress ", (const char **) &end)) { uint64_t item_count = strtoull(end, &end, 10); if (*end != '\0') die("invalid input: '%s'\n", line.buf); @@ -63,12 +83,16 @@ int cmd__progress(int argc, const char **argv) die("invalid input: '%s'\n", line.buf); progress_test_ns = test_ms * 1000 * 1000; display_throughput(progress, byte_count); - } else if (!strcmp(line.buf, "update")) + } else if (!strcmp(line.buf, "update")) { progress_test_force_update(); - else + } else if (!strcmp(line.buf, "stop")) { + stop_progress(&progress); + } else { die("invalid input: '%s'\n", line.buf); + } } - stop_progress(&progress); + strbuf_release(&line); + string_list_clear(&titles, 0); return 0; } diff --git a/t/helper/test-read-graph.c b/t/helper/test-read-graph.c index 75927b2c81..98b73bb8f2 100644 --- a/t/helper/test-read-graph.c +++ b/t/helper/test-read-graph.c @@ -3,6 +3,7 @@ #include "commit-graph.h" #include "repository.h" #include "object-store.h" +#include "bloom.h" int cmd__read_graph(int argc, const char **argv) { @@ -45,6 +46,18 @@ int cmd__read_graph(int argc, const char **argv) printf(" bloom_data"); printf("\n"); + printf("options:"); + if (graph->bloom_filter_settings) + printf(" bloom(%"PRIu32",%"PRIu32",%"PRIu32")", + graph->bloom_filter_settings->hash_version, + graph->bloom_filter_settings->bits_per_entry, + graph->bloom_filter_settings->num_hashes); + if (graph->read_generation_data) + printf(" read_generation_data"); + if (graph->topo_levels) + printf(" topo_levels"); + printf("\n"); + UNLEAK(graph); return 0; diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c index 3e4ddaee70..9646d85fc8 100644 --- a/t/helper/test-ref-store.c +++ b/t/helper/test-ref-store.c @@ -180,10 +180,9 @@ static int cmd_resolve_ref(struct ref_store *refs, const char **argv) int resolve_flags = arg_flags(*argv++, "resolve-flags", empty_flags); int flags; const char *ref; - int ignore_errno; ref = refs_resolve_ref_unsafe(refs, refname, resolve_flags, - &oid, &flags, &ignore_errno); + &oid, &flags); printf("%s %s 0x%x\n", oid_to_hex(&oid), ref ? ref : "(null)", flags); return ref ? 0 : 1; } diff --git a/t/helper/test-reftable.c b/t/helper/test-reftable.c index 26b03d7b78..1f0a28cbb6 100644 --- a/t/helper/test-reftable.c +++ b/t/helper/test-reftable.c @@ -3,15 +3,16 @@ int cmd__reftable(int argc, const char **argv) { + /* test from simple to complex. */ basics_test_main(argc, argv); + record_test_main(argc, argv); block_test_main(argc, argv); - merged_test_main(argc, argv); + tree_test_main(argc, argv); pq_test_main(argc, argv); - record_test_main(argc, argv); - refname_test_main(argc, argv); readwrite_test_main(argc, argv); + merged_test_main(argc, argv); stack_test_main(argc, argv); - tree_test_main(argc, argv); + refname_test_main(argc, argv); return 0; } diff --git a/t/helper/test-run-command.c b/t/helper/test-run-command.c index 913775a14b..f3b90aa834 100644 --- a/t/helper/test-run-command.c +++ b/t/helper/test-run-command.c @@ -19,7 +19,6 @@ #include "thread-utils.h" #include "wildmatch.h" #include "gettext.h" -#include "parse-options.h" static int number_callbacks; static int parallel_next(struct child_process *cp, @@ -180,15 +179,16 @@ static int testsuite(int argc, const char **argv) if (max_jobs > suite.tests.nr) max_jobs = suite.tests.nr; - fprintf(stderr, "Running %d tests (%d at a time)\n", - suite.tests.nr, max_jobs); + fprintf(stderr, "Running %"PRIuMAX" tests (%d at a time)\n", + (uintmax_t)suite.tests.nr, max_jobs); ret = run_processes_parallel(max_jobs, next_test, test_failed, test_finished, &suite); if (suite.failed.nr > 0) { ret = 1; - fprintf(stderr, "%d tests failed:\n\n", suite.failed.nr); + fprintf(stderr, "%"PRIuMAX" tests failed:\n\n", + (uintmax_t)suite.failed.nr); for (i = 0; i < suite.failed.nr; i++) fprintf(stderr, "\t%s\n", suite.failed.items[i].string); } @@ -221,9 +221,9 @@ static int quote_stress_test(int argc, const char **argv) struct strbuf out = STRBUF_INIT; struct strvec args = STRVEC_INIT; struct option options[] = { - OPT_INTEGER('n', "trials", &trials, "Number of trials"), - OPT_INTEGER('s', "skip", &skip, "Skip <n> trials"), - OPT_BOOL('m', "msys2", &msys2, "Test quoting for MSYS2's sh"), + OPT_INTEGER('n', "trials", &trials, "number of trials"), + OPT_INTEGER('s', "skip", &skip, "skip <n> trials"), + OPT_BOOL('m', "msys2", &msys2, "test quoting for MSYS2's sh"), OPT_END() }; const char * const usage[] = { diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c index 338a57b104..e6ec69cf32 100644 --- a/t/helper/test-tool.c +++ b/t/helper/test-tool.c @@ -20,6 +20,7 @@ static struct test_cmd cmds[] = { { "chmtime", cmd__chmtime }, { "config", cmd__config }, { "crontab", cmd__crontab }, + { "csprng", cmd__csprng }, { "ctype", cmd__ctype }, { "date", cmd__date }, { "delta", cmd__delta }, diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h index 48cee1f4a2..20756eefdd 100644 --- a/t/helper/test-tool.h +++ b/t/helper/test-tool.h @@ -10,6 +10,7 @@ int cmd__bloom(int argc, const char **argv); int cmd__chmtime(int argc, const char **argv); int cmd__config(int argc, const char **argv); int cmd__crontab(int argc, const char **argv); +int cmd__csprng(int argc, const char **argv); int cmd__ctype(int argc, const char **argv); int cmd__date(int argc, const char **argv); int cmd__delta(int argc, const char **argv); diff --git a/t/interop/Makefile b/t/interop/Makefile index 31a4bbc716..6911c2915a 100644 --- a/t/interop/Makefile +++ b/t/interop/Makefile @@ -1,3 +1,6 @@ +# Import tree-wide shared Makefile behavior and libraries +include ../../shared.mak + -include ../../config.mak export GIT_TEST_OPTIONS diff --git a/t/lib-bitmap.sh b/t/lib-bitmap.sh index 21d0392dda..a95537e759 100644 --- a/t/lib-bitmap.sh +++ b/t/lib-bitmap.sh @@ -1,6 +1,9 @@ # Helpers for scripts testing bitmap functionality; see t5310 for # example usage. +objdir=.git/objects +midx=$objdir/pack/multi-pack-index + # Compare a file containing rev-list bitmap traversal output to its non-bitmap # counterpart. You can't just use test_cmp for this, because the two produce # subtly different output: @@ -264,3 +267,185 @@ have_delta () { midx_checksum () { test-tool read-midx --checksum "$1" } + +# midx_pack_source <obj> +midx_pack_source () { + test-tool read-midx --show-objects .git/objects | grep "^$1 " | cut -f2 +} + +test_rev_exists () { + commit="$1" + kind="$2" + + test_expect_success "reverse index exists ($kind)" ' + GIT_TRACE2_EVENT=$(pwd)/event.trace \ + git rev-list --test-bitmap "$commit" && + + if test "rev" = "$kind" + then + test_path_is_file $midx-$(midx_checksum $objdir).rev + fi && + grep "\"category\":\"load_midx_revindex\",\"key\":\"source\",\"value\":\"$kind\"" event.trace + ' +} + +midx_bitmap_core () { + rev_kind="${1:-midx}" + + setup_bitmap_history + + test_expect_success 'create single-pack midx with bitmaps' ' + git repack -ad && + git multi-pack-index write --bitmap && + test_path_is_file $midx && + test_path_is_file $midx-$(midx_checksum $objdir).bitmap + ' + + test_rev_exists HEAD "$rev_kind" + + basic_bitmap_tests + + test_expect_success 'create new additional packs' ' + for i in $(test_seq 1 16) + do + test_commit "$i" && + git repack -d || return 1 + done && + + git checkout -b other2 HEAD~8 && + for i in $(test_seq 1 8) + do + test_commit "side-$i" && + git repack -d || return 1 + done && + git checkout second + ' + + test_expect_success 'create multi-pack midx with bitmaps' ' + git multi-pack-index write --bitmap && + + ls $objdir/pack/pack-*.pack >packs && + test_line_count = 25 packs && + + test_path_is_file $midx && + test_path_is_file $midx-$(midx_checksum $objdir).bitmap + ' + + test_rev_exists HEAD "$rev_kind" + + basic_bitmap_tests + + test_expect_success '--no-bitmap is respected when bitmaps exist' ' + git multi-pack-index write --bitmap && + + test_commit respect--no-bitmap && + git repack -d && + + test_path_is_file $midx && + test_path_is_file $midx-$(midx_checksum $objdir).bitmap && + + git multi-pack-index write --no-bitmap && + + test_path_is_file $midx && + test_path_is_missing $midx-$(midx_checksum $objdir).bitmap && + test_path_is_missing $midx-$(midx_checksum $objdir).rev + ' + + test_expect_success 'setup midx with base from later pack' ' + # Write a and b so that "a" is a delta on top of base "b", since Git + # prefers to delete contents out of a base rather than add to a shorter + # object. + test_seq 1 128 >a && + test_seq 1 130 >b && + + git add a b && + git commit -m "initial commit" && + + a=$(git rev-parse HEAD:a) && + b=$(git rev-parse HEAD:b) && + + # In the first pack, "a" is stored as a delta to "b". + p1=$(git pack-objects .git/objects/pack/pack <<-EOF + $a + $b + EOF + ) && + + # In the second pack, "a" is missing, and "b" is not a delta nor base to + # any other object. + p2=$(git pack-objects .git/objects/pack/pack <<-EOF + $b + $(git rev-parse HEAD) + $(git rev-parse HEAD^{tree}) + EOF + ) && + + git prune-packed && + # Use the second pack as the preferred source, so that "b" occurs + # earlier in the MIDX object order, rendering "a" unusable for pack + # reuse. + git multi-pack-index write --bitmap --preferred-pack=pack-$p2.idx && + + have_delta $a $b && + test $(midx_pack_source $a) != $(midx_pack_source $b) + ' + + rev_list_tests 'full bitmap with backwards delta' + + test_expect_success 'clone with bitmaps enabled' ' + git clone --no-local --bare . clone-reverse-delta.git && + test_when_finished "rm -fr clone-reverse-delta.git" && + + git rev-parse HEAD >expect && + git --git-dir=clone-reverse-delta.git rev-parse HEAD >actual && + test_cmp expect actual + ' + + test_expect_success 'changing the preferred pack does not corrupt bitmaps' ' + rm -fr repo && + git init repo && + test_when_finished "rm -fr repo" && + ( + cd repo && + + test_commit A && + test_commit B && + + git rev-list --objects --no-object-names HEAD^ >A.objects && + git rev-list --objects --no-object-names HEAD^.. >B.objects && + + A=$(git pack-objects $objdir/pack/pack <A.objects) && + B=$(git pack-objects $objdir/pack/pack <B.objects) && + + cat >indexes <<-EOF && + pack-$A.idx + pack-$B.idx + EOF + + git multi-pack-index write --bitmap --stdin-packs \ + --preferred-pack=pack-$A.pack <indexes && + git rev-list --test-bitmap A && + + git multi-pack-index write --bitmap --stdin-packs \ + --preferred-pack=pack-$B.pack <indexes && + git rev-list --test-bitmap A + ) + ' +} + +midx_bitmap_partial_tests () { + rev_kind="${1:-midx}" + + test_expect_success 'setup partial bitmaps' ' + test_commit packed && + git repack && + test_commit loose && + git multi-pack-index write --bitmap 2>err && + test_path_is_file $midx && + test_path_is_file $midx-$(midx_checksum $objdir).bitmap + ' + + test_rev_exists HEAD~ "$rev_kind" + + basic_bitmap_tests HEAD~ +} diff --git a/t/lib-commit-graph.sh b/t/lib-commit-graph.sh new file mode 100755 index 0000000000..5d79e1a4e9 --- /dev/null +++ b/t/lib-commit-graph.sh @@ -0,0 +1,58 @@ +#!/bin/sh + +# Helper functions for testing commit-graphs. + +# Initialize OID cache with oid_version +test_oid_cache <<-EOF +oid_version sha1:1 +oid_version sha256:2 +EOF + +graph_git_two_modes() { + git -c core.commitGraph=true $1 >output && + git -c core.commitGraph=false $1 >expect && + test_cmp expect output +} + +graph_git_behavior() { + MSG=$1 + DIR=$2 + BRANCH=$3 + COMPARE=$4 + test_expect_success "check normal git operations: $MSG" ' + cd "$TRASH_DIRECTORY/$DIR" && + graph_git_two_modes "log --oneline $BRANCH" && + graph_git_two_modes "log --topo-order $BRANCH" && + graph_git_two_modes "log --graph $COMPARE..$BRANCH" && + graph_git_two_modes "branch -vv" && + graph_git_two_modes "merge-base -a $BRANCH $COMPARE" + ' +} + +graph_read_expect() { + OPTIONAL="" + NUM_CHUNKS=3 + if test -n "$2" + then + OPTIONAL=" $2" + NUM_CHUNKS=$((3 + $(echo "$2" | wc -w))) + fi + GENERATION_VERSION=2 + if test -n "$3" + then + GENERATION_VERSION=$3 + fi + OPTIONS= + if test $GENERATION_VERSION -gt 1 + then + OPTIONS=" read_generation_data" + fi + cat >expect <<- EOF + header: 43475048 1 $(test_oid oid_version) $NUM_CHUNKS 0 + num_commits: $1 + chunks: oid_fanout oid_lookup commit_metadata$OPTIONAL + options:$OPTIONS + EOF + test-tool read-graph >output && + test_cmp expect output +} diff --git a/t/lib-gpg.sh b/t/lib-gpg.sh index 3e7ee1386a..114785586a 100644 --- a/t/lib-gpg.sh +++ b/t/lib-gpg.sh @@ -40,7 +40,7 @@ test_lazy_prereq GPG ' # > lib-gpg/ownertrust mkdir "$GNUPGHOME" && chmod 0700 "$GNUPGHOME" && - (gpgconf --kill gpg-agent || : ) && + (gpgconf --kill all || : ) && gpg --homedir "${GNUPGHOME}" --import \ "$TEST_DIRECTORY"/lib-gpg/keyring.gpg && gpg --homedir "${GNUPGHOME}" --import-ownertrust \ @@ -72,12 +72,11 @@ test_lazy_prereq GPGSM ' --passphrase-fd 0 --pinentry-mode loopback \ --import "$TEST_DIRECTORY"/lib-gpg/gpgsm_cert.p12 && - gpgsm --homedir "${GNUPGHOME}" -K | - grep fingerprint: | - cut -d" " -f4 | - tr -d "\\n" >"${GNUPGHOME}/trustlist.txt" && + gpgsm --homedir "${GNUPGHOME}" -K --with-colons | + awk -F ":" "/^fpr:/ {printf \"%s S relax\\n\", \$10}" \ + >"${GNUPGHOME}/trustlist.txt" && + (gpgconf --reload all || : ) && - echo " S relax" >>"${GNUPGHOME}/trustlist.txt" && echo hello | gpgsm --homedir "${GNUPGHOME}" >/dev/null \ -u committer@example.com -o /dev/null --sign - ' diff --git a/t/lib-read-tree-m-3way.sh b/t/lib-read-tree-m-3way.sh index 168329adbc..2da25b3144 100644 --- a/t/lib-read-tree-m-3way.sh +++ b/t/lib-read-tree-m-3way.sh @@ -3,21 +3,21 @@ mkdir Z for a in N D M do - for b in N D M - do - p=$a$b + for b in N D M + do + p=$a$b echo This is $p from the original tree. >$p echo This is Z/$p from the original tree. >Z/$p - test_expect_success \ - "adding test file $p and Z/$p" \ - 'git update-index --add $p && - git update-index --add Z/$p' + test_expect_success "adding test file $p and Z/$p" ' + git update-index --add $p && + git update-index --add Z/$p + ' done done echo This is SS from the original tree. >SS -test_expect_success \ - 'adding test file SS' \ - 'git update-index --add SS' +test_expect_success 'adding test file SS' ' + git update-index --add SS +' cat >TT <<\EOF This is a trivial merge sample text. Branch A is expected to upcase this word, here. @@ -30,12 +30,12 @@ At the very end, here comes another line, that is the word, expected to be upcased by Branch B. This concludes the trivial merge sample file. EOF -test_expect_success \ - 'adding test file TT' \ - 'git update-index --add TT' -test_expect_success \ - 'prepare initial tree' \ - 'tree_O=$(git write-tree)' +test_expect_success 'adding test file TT' ' + git update-index --add TT +' +test_expect_success 'prepare initial tree' ' + tree_O=$(git write-tree) +' ################################################################ # Branch A and B makes the changes according to the above matrix. @@ -45,48 +45,48 @@ test_expect_success \ to_remove=$(echo D? Z/D?) rm -f $to_remove -test_expect_success \ - 'change in branch A (removal)' \ - 'git update-index --remove $to_remove' +test_expect_success 'change in branch A (removal)' ' + git update-index --remove $to_remove +' for p in M? Z/M? do - echo This is modified $p in the branch A. >$p - test_expect_success \ - 'change in branch A (modification)' \ - "git update-index $p" + echo This is modified $p in the branch A. >$p + test_expect_success 'change in branch A (modification)' ' + git update-index $p + ' done for p in AN AA Z/AN Z/AA do - echo This is added $p in the branch A. >$p - test_expect_success \ - 'change in branch A (addition)' \ - "git update-index --add $p" + echo This is added $p in the branch A. >$p + test_expect_success 'change in branch A (addition)' ' + git update-index --add $p + ' done echo This is SS from the modified tree. >SS echo This is LL from the modified tree. >LL -test_expect_success \ - 'change in branch A (addition)' \ - 'git update-index --add LL && - git update-index SS' +test_expect_success 'change in branch A (addition)' ' + git update-index --add LL && + git update-index SS +' mv TT TT- sed -e '/Branch A/s/word/WORD/g' <TT- >TT rm -f TT- -test_expect_success \ - 'change in branch A (edit)' \ - 'git update-index TT' +test_expect_success 'change in branch A (edit)' ' + git update-index TT +' mkdir DF echo Branch A makes a file at DF/DF, creating a directory DF. >DF/DF -test_expect_success \ - 'change in branch A (change file to directory)' \ - 'git update-index --add DF/DF' +test_expect_success 'change in branch A (change file to directory)' ' + git update-index --add DF/DF +' -test_expect_success \ - 'recording branch A tree' \ - 'tree_A=$(git write-tree)' +test_expect_success 'recording branch A tree' ' + tree_A=$(git write-tree) +' ################################################################ # Branch B @@ -94,65 +94,65 @@ test_expect_success \ rm -rf [NDMASLT][NDMASLT] Z DF mkdir Z -test_expect_success \ - 'reading original tree and checking out' \ - 'git read-tree $tree_O && - git checkout-index -a' +test_expect_success 'reading original tree and checking out' ' + git read-tree $tree_O && + git checkout-index -a +' to_remove=$(echo ?D Z/?D) rm -f $to_remove -test_expect_success \ - 'change in branch B (removal)' \ - "git update-index --remove $to_remove" +test_expect_success 'change in branch B (removal)' ' + git update-index --remove $to_remove +' for p in ?M Z/?M do - echo This is modified $p in the branch B. >$p - test_expect_success \ - 'change in branch B (modification)' \ - "git update-index $p" + echo This is modified $p in the branch B. >$p + test_expect_success 'change in branch B (modification)' ' + git update-index $p + ' done for p in NA AA Z/NA Z/AA do - echo This is added $p in the branch B. >$p - test_expect_success \ - 'change in branch B (addition)' \ - "git update-index --add $p" + echo This is added $p in the branch B. >$p + test_expect_success 'change in branch B (addition)' ' + git update-index --add $p + ' done echo This is SS from the modified tree. >SS echo This is LL from the modified tree. >LL -test_expect_success \ - 'change in branch B (addition and modification)' \ - 'git update-index --add LL && - git update-index SS' +test_expect_success 'change in branch B (addition and modification)' ' + git update-index --add LL && + git update-index SS +' mv TT TT- sed -e '/Branch B/s/word/WORD/g' <TT- >TT rm -f TT- -test_expect_success \ - 'change in branch B (modification)' \ - 'git update-index TT' +test_expect_success 'change in branch B (modification)' ' + git update-index TT +' echo Branch B makes a file at DF. >DF -test_expect_success \ - 'change in branch B (addition of a file to conflict with directory)' \ - 'git update-index --add DF' - -test_expect_success \ - 'recording branch B tree' \ - 'tree_B=$(git write-tree)' - -test_expect_success \ - 'keep contents of 3 trees for easy access' \ - 'rm -f .git/index && - git read-tree $tree_O && - mkdir .orig-O && - git checkout-index --prefix=.orig-O/ -f -q -a && - rm -f .git/index && - git read-tree $tree_A && - mkdir .orig-A && - git checkout-index --prefix=.orig-A/ -f -q -a && - rm -f .git/index && - git read-tree $tree_B && - mkdir .orig-B && - git checkout-index --prefix=.orig-B/ -f -q -a' +test_expect_success 'change in branch B (addition of a file to conflict with directory)' ' + git update-index --add DF +' + +test_expect_success 'recording branch B tree' ' + tree_B=$(git write-tree) +' + +test_expect_success 'keep contents of 3 trees for easy access' ' + rm -f .git/index && + git read-tree $tree_O && + mkdir .orig-O && + git checkout-index --prefix=.orig-O/ -f -q -a && + rm -f .git/index && + git read-tree $tree_A && + mkdir .orig-A && + git checkout-index --prefix=.orig-A/ -f -q -a && + rm -f .git/index && + git read-tree $tree_B && + mkdir .orig-B && + git checkout-index --prefix=.orig-B/ -f -q -a +' diff --git a/t/perf/Makefile b/t/perf/Makefile index 2465770a78..e4808aebed 100644 --- a/t/perf/Makefile +++ b/t/perf/Makefile @@ -1,3 +1,6 @@ +# Import tree-wide shared Makefile behavior and libraries +include ../../shared.mak + -include ../../config.mak export GIT_TEST_OPTIONS diff --git a/t/perf/p2000-sparse-operations.sh b/t/perf/p2000-sparse-operations.sh index cb777c74a2..382716cfca 100755 --- a/t/perf/p2000-sparse-operations.sh +++ b/t/perf/p2000-sparse-operations.sh @@ -117,5 +117,8 @@ test_perf_on_all git diff test_perf_on_all git diff --cached test_perf_on_all git blame $SPARSE_CONE/a test_perf_on_all git blame $SPARSE_CONE/f3/a +test_perf_on_all git read-tree -mu HEAD +test_perf_on_all git checkout-index -f --all +test_perf_on_all git update-index --add --remove $SPARSE_CONE/a test_done diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh index b007f0efef..9dcbf518a7 100755 --- a/t/t0000-basic.sh +++ b/t/t0000-basic.sh @@ -1089,7 +1089,8 @@ test_expect_success 'update-index D/F conflict' ' mv path2 path0 && mv tmp path2 && git update-index --add --replace path2 path0/file2 && - numpath0=$(git ls-files path0 | wc -l) && + git ls-files path0 >tmp && + numpath0=$(wc -l <tmp) && test $numpath0 = 1 ' @@ -1103,13 +1104,14 @@ test_expect_success 'very long name in the index handled sanely' ' >path4 && git update-index --add path4 && + git ls-files -s path4 >tmp && ( - git ls-files -s path4 | - sed -e "s/ .*/ /" | + sed -e "s/ .*/ /" tmp | tr -d "\012" && echo "$a" ) | git update-index --index-info && - len=$(git ls-files "a*" | wc -c) && + git ls-files "a*" >tmp && + len=$(wc -c <tmp) && test $len = 4098 ' diff --git a/t/t0001-init.sh b/t/t0001-init.sh index 3235ab4d53..d479303efa 100755 --- a/t/t0001-init.sh +++ b/t/t0001-init.sh @@ -6,7 +6,8 @@ TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh check_config () { - if test -d "$1" && test -f "$1/config" && test -d "$1/refs" + if test_path_is_dir "$1" && + test_path_is_file "$1/config" && test_path_is_dir "$1/refs" then : happy else diff --git a/t/t0002-gitfile.sh b/t/t0002-gitfile.sh index 76052cb562..f6356db183 100755 --- a/t/t0002-gitfile.sh +++ b/t/t0002-gitfile.sh @@ -65,9 +65,11 @@ test_expect_success 'check commit-tree' ' test_path_is_file "$REAL/objects/$(objpath $SHA)" ' -test_expect_success 'check rev-list' ' +test_expect_success !SANITIZE_LEAK 'check rev-list' ' git update-ref "HEAD" "$SHA" && - test "$SHA" = "$(git rev-list HEAD)" + git rev-list HEAD >actual && + echo $SHA >expected && + test_cmp expected actual ' test_expect_success 'setup_git_dir twice in subdir' ' diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh index b9ed612af1..143f100517 100755 --- a/t/t0003-attributes.sh +++ b/t/t0003-attributes.sh @@ -205,15 +205,18 @@ test_expect_success 'attribute test: read paths from stdin' ' test_expect_success 'attribute test: --all option' ' grep -v unspecified <expect-all | sort >specified-all && sed -e "s/:.*//" <expect-all | uniq >stdin-all && - git check-attr --stdin --all <stdin-all | sort >actual && + git check-attr --stdin --all <stdin-all >tmp && + sort tmp >actual && test_cmp specified-all actual ' test_expect_success 'attribute test: --cached option' ' - git check-attr --cached --stdin --all <stdin-all | sort >actual && + git check-attr --cached --stdin --all <stdin-all >tmp && + sort tmp >actual && test_must_be_empty actual && git add .gitattributes a/.gitattributes a/b/.gitattributes && - git check-attr --cached --stdin --all <stdin-all | sort >actual && + git check-attr --cached --stdin --all <stdin-all >tmp && + sort tmp >actual && test_cmp specified-all actual ' diff --git a/t/t0006-date.sh b/t/t0006-date.sh index 794186961e..2490162071 100755 --- a/t/t0006-date.sh +++ b/t/t0006-date.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='test date parsing and printing' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh # arbitrary reference time: 2009-08-30 19:20:00 diff --git a/t/t0012-help.sh b/t/t0012-help.sh index 91b68c74a1..6c3e1f7159 100755 --- a/t/t0012-help.sh +++ b/t/t0012-help.sh @@ -35,6 +35,9 @@ test_expect_success 'basic help commands' ' ' test_expect_success 'invalid usage' ' + test_expect_code 129 git help -a add && + test_expect_code 129 git help --all add && + test_expect_code 129 git help -g add && test_expect_code 129 git help -a -c && @@ -46,6 +49,29 @@ test_expect_success 'invalid usage' ' test_expect_code 129 git help --config-sections-for-completion add ' +for opt in '-a' '-g' '-c' '--config-for-completion' '--config-sections-for-completion' +do + test_expect_success "invalid usage of '$opt' with [-i|-m|-w]" ' + git help $opt && + test_expect_code 129 git help $opt -i && + test_expect_code 129 git help $opt -m && + test_expect_code 129 git help $opt -w + ' + + if test "$opt" = "-a" + then + continue + fi + + test_expect_success "invalid usage of '$opt' with --no-external-commands" ' + test_expect_code 129 git help $opt --no-external-commands + ' + + test_expect_success "invalid usage of '$opt' with --no-aliases" ' + test_expect_code 129 git help $opt --no-external-commands + ' +done + test_expect_success "works for commands and guides by default" ' configure_help && git help status && @@ -138,14 +164,87 @@ test_expect_success 'git help --config-sections-for-completion' ' test_cmp human.munged sections ' +test_section_spacing () { + cat >expect && + "$@" >out && + grep -E "(^[^ ]|^$)" out >actual +} + +test_section_spacing_trailer () { + test_section_spacing "$@" && + test_expect_code 1 git >out && + sed -n '/list available subcommands/,$p' <out >>expect +} + + +for cmd in git "git help" +do + test_expect_success "'$cmd' section spacing" ' + test_section_spacing_trailer git help <<-\EOF && + usage: git [--version] [--help] [-C <path>] [-c <name>=<value>] + + These are common Git commands used in various situations: + + start a working area (see also: git help tutorial) + + work on the current change (see also: git help everyday) + + examine the history and state (see also: git help revisions) + + grow, mark and tweak your common history + + collaborate (see also: git help workflows) + + EOF + test_cmp expect actual + ' +done + +test_expect_success "'git help -a' section spacing" ' + test_section_spacing \ + git help -a --no-external-commands --no-aliases <<-\EOF && + See '\''git help <command>'\'' to read about a specific subcommand + + Main Porcelain Commands + + Ancillary Commands / Manipulators + + Ancillary Commands / Interrogators + + Interacting with Others + + Low-level Commands / Manipulators + + Low-level Commands / Interrogators + + Low-level Commands / Syncing Repositories + + Low-level Commands / Internal Helpers + EOF + test_cmp expect actual +' + +test_expect_success "'git help -g' section spacing" ' + test_section_spacing_trailer git help -g <<-\EOF && + The Git concept guides are: + + EOF + test_cmp expect actual +' + test_expect_success 'generate builtin list' ' + mkdir -p sub && git --list-cmds=builtins >builtins ' while read builtin do test_expect_success "$builtin can handle -h" ' - test_expect_code 129 git $builtin -h >output 2>&1 && + ( + GIT_CEILING_DIRECTORIES=$(pwd) && + export GIT_CEILING_DIRECTORIES && + test_expect_code 129 git -C sub $builtin -h >output 2>&1 + ) && test_i18ngrep usage output ' done <builtins diff --git a/t/t0015-hash.sh b/t/t0015-hash.sh index 291e9061f3..086822fc45 100755 --- a/t/t0015-hash.sh +++ b/t/t0015-hash.sh @@ -15,7 +15,7 @@ test_expect_success 'test basic SHA-1 hash values' ' grep c12252ceda8be8994d5fa0290a47231c1d16aae3 actual && printf "abcdefghijklmnopqrstuvwxyz" | test-tool sha1 >actual && grep 32d10c7b8cf96570ca04ce37f2a19d84240d3a89 actual && - perl -e "$| = 1; print q{aaaaaaaaaa} for 1..100000;" | \ + perl -e "$| = 1; print q{aaaaaaaaaa} for 1..100000;" | test-tool sha1 >actual && grep 34aa973cd4c4daa4f61eeb2bdbad27316534016f actual && printf "blob 0\0" | test-tool sha1 >actual && @@ -38,10 +38,10 @@ test_expect_success 'test basic SHA-256 hash values' ' printf "abcdefghijklmnopqrstuvwxyz" | test-tool sha256 >actual && grep 71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73 actual && # Try to exercise the chunking code by turning autoflush on. - perl -e "$| = 1; print q{aaaaaaaaaa} for 1..100000;" | \ + perl -e "$| = 1; print q{aaaaaaaaaa} for 1..100000;" | test-tool sha256 >actual && grep cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0 actual && - perl -e "$| = 1; print q{abcdefghijklmnopqrstuvwxyz} for 1..100000;" | \ + perl -e "$| = 1; print q{abcdefghijklmnopqrstuvwxyz} for 1..100000;" | test-tool sha256 >actual && grep e406ba321ca712ad35a698bf0af8d61fc4dc40eca6bdcea4697962724ccbde35 actual && printf "blob 0\0" | test-tool sha256 >actual && diff --git a/t/t0022-crlf-rename.sh b/t/t0022-crlf-rename.sh index c1a331e9e9..9fe9891251 100755 --- a/t/t0022-crlf-rename.sh +++ b/t/t0022-crlf-rename.sh @@ -24,8 +24,8 @@ test_expect_success setup ' test_expect_success 'diff -M' ' - git diff-tree -M -r --name-status HEAD^ HEAD | - sed -e "s/R[0-9]*/RNUM/" >actual && + git diff-tree -M -r --name-status HEAD^ HEAD >tmp && + sed -e "s/R[0-9]*/RNUM/" tmp >actual && echo "RNUM sample elpmas" >expect && test_cmp expect actual diff --git a/t/t0025-crlf-renormalize.sh b/t/t0025-crlf-renormalize.sh index 81447978b7..f7202c192e 100755 --- a/t/t0025-crlf-renormalize.sh +++ b/t/t0025-crlf-renormalize.sh @@ -22,8 +22,8 @@ test_expect_success 'renormalize CRLF in repo' ' i/lf w/lf attr/text=auto LF.txt i/lf w/mixed attr/text=auto CRLF_mix_LF.txt EOF - git ls-files --eol | - sed -e "s/ / /g" -e "s/ */ /g" | + git ls-files --eol >tmp && + sed -e "s/ / /g" -e "s/ */ /g" tmp | sort >actual && test_cmp expect actual ' diff --git a/t/t0027-auto-crlf.sh b/t/t0027-auto-crlf.sh index 4a5c5c602c..0feb41a23f 100755 --- a/t/t0027-auto-crlf.sh +++ b/t/t0027-auto-crlf.sh @@ -311,8 +311,8 @@ checkout_files () { i/-text w/$(stats_ascii $crlfnul) attr/$(attr_ascii $attr $aeol) crlf_false_attr__CRLF_nul.txt i/-text w/$(stats_ascii $crlfnul) attr/$(attr_ascii $attr $aeol) crlf_false_attr__LF_nul.txt EOF - git ls-files --eol crlf_false_attr__* | - sed -e "s/ / /g" -e "s/ */ /g" | + git ls-files --eol crlf_false_attr__* >tmp && + sed -e "s/ / /g" -e "s/ */ /g" tmp | sort >actual && test_cmp expect actual ' @@ -359,12 +359,12 @@ test_expect_success 'ls-files --eol -o Text/Binary' ' i/ w/crlf TeBi_126_CL i/ w/-text TeBi_126_CLC EOF - git ls-files --eol -o | + git ls-files --eol -o >tmp && sed -n -e "/TeBi_/{s!attr/[ ]*!!g s! ! !g s! *! !g p - }" | sort >actual && + }" tmp | sort >actual && test_cmp expect actual ' @@ -597,6 +597,12 @@ do # auto: core.autocrlf=false and core.eol unset(or native) uses native eol checkout_files auto "$id" "" false "" $NL CRLF CRLF_mix_LF LF_mix_CR LF_nul checkout_files auto "$id" "" false native $NL CRLF CRLF_mix_LF LF_mix_CR LF_nul + # core.autocrlf false, .gitattributes sets eol + checkout_files "" "$id" "lf" false "" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul + checkout_files "" "$id" "crlf" false "" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul + # core.autocrlf true, .gitattributes sets eol + checkout_files "" "$id" "lf" true "" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul + checkout_files "" "$id" "crlf" true "" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul done # The rest of the tests are unique; do the usual linting. @@ -611,8 +617,8 @@ test_expect_success 'ls-files --eol -d -z' ' i/lf w/ crlf_false_attr__LF.txt i/mixed w/ crlf_false_attr__CRLF_mix_LF.txt EOF - git ls-files --eol -d | - sed -e "s!attr/[^ ]*!!g" -e "s/ / /g" -e "s/ */ /g" | + git ls-files --eol -d >tmp && + sed -e "s!attr/[^ ]*!!g" -e "s/ / /g" -e "s/ */ /g" tmp | sort >actual && test_cmp expect actual ' diff --git a/t/t0030-stripspace.sh b/t/t0030-stripspace.sh index ae1ca380c1..0a5713c524 100755 --- a/t/t0030-stripspace.sh +++ b/t/t0030-stripspace.sh @@ -13,6 +13,10 @@ s40=' ' sss="$s40$s40$s40$s40$s40$s40$s40$s40$s40$s40" # 400 ttt="$t40$t40$t40$t40$t40$t40$t40$t40$t40$t40" # 400 +printf_git_stripspace () { + printf "$1" | git stripspace +} + test_expect_success \ 'long lines without spaces should be unchanged' ' echo "$ttt" >expect && @@ -225,32 +229,38 @@ test_expect_success \ test_expect_success \ 'text without newline at end should end with newline' ' - test $(printf "$ttt" | git stripspace | wc -l) -gt 0 && - test $(printf "$ttt$ttt" | git stripspace | wc -l) -gt 0 && - test $(printf "$ttt$ttt$ttt" | git stripspace | wc -l) -gt 0 && - test $(printf "$ttt$ttt$ttt$ttt" | git stripspace | wc -l) -gt 0 + test_stdout_line_count -gt 0 printf_git_stripspace "$ttt" && + test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt" && + test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt$ttt" && + test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt$ttt$ttt" ' # text plus spaces at the end: test_expect_success \ 'text plus spaces without newline at end should end with newline' ' - test $(printf "$ttt$sss" | git stripspace | wc -l) -gt 0 && - test $(printf "$ttt$ttt$sss" | git stripspace | wc -l) -gt 0 && - test $(printf "$ttt$ttt$ttt$sss" | git stripspace | wc -l) -gt 0 && - test $(printf "$ttt$sss$sss" | git stripspace | wc -l) -gt 0 && - test $(printf "$ttt$ttt$sss$sss" | git stripspace | wc -l) -gt 0 && - test $(printf "$ttt$sss$sss$sss" | git stripspace | wc -l) -gt 0 + test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$sss" && + test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt$sss" && + test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt$ttt$sss" && + test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$sss$sss" && + test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt$sss$sss" && + test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$sss$sss$sss" ' test_expect_success \ 'text plus spaces without newline at end should not show spaces' ' - ! (printf "$ttt$sss" | git stripspace | grep " " >/dev/null) && - ! (printf "$ttt$ttt$sss" | git stripspace | grep " " >/dev/null) && - ! (printf "$ttt$ttt$ttt$sss" | git stripspace | grep " " >/dev/null) && - ! (printf "$ttt$sss$sss" | git stripspace | grep " " >/dev/null) && - ! (printf "$ttt$ttt$sss$sss" | git stripspace | grep " " >/dev/null) && - ! (printf "$ttt$sss$sss$sss" | git stripspace | grep " " >/dev/null) + printf "$ttt$sss" | git stripspace >tmp && + ! grep " " tmp >/dev/null && + printf "$ttt$ttt$sss" | git stripspace >tmp && + ! grep " " tmp >/dev/null && + printf "$ttt$ttt$ttt$sss" | git stripspace >tmp && + ! grep " " tmp >/dev/null && + printf "$ttt$sss$sss" | git stripspace >tmp && + ! grep " " tmp >/dev/null && + printf "$ttt$ttt$sss$sss" | git stripspace >tmp && + ! grep " " tmp >/dev/null && + printf "$ttt$sss$sss$sss" | git stripspace >tmp && + ! grep " " tmp >/dev/null ' test_expect_success \ @@ -282,12 +292,18 @@ test_expect_success \ test_expect_success \ 'text plus spaces at end should not show spaces' ' - ! (echo "$ttt$sss" | git stripspace | grep " " >/dev/null) && - ! (echo "$ttt$ttt$sss" | git stripspace | grep " " >/dev/null) && - ! (echo "$ttt$ttt$ttt$sss" | git stripspace | grep " " >/dev/null) && - ! (echo "$ttt$sss$sss" | git stripspace | grep " " >/dev/null) && - ! (echo "$ttt$ttt$sss$sss" | git stripspace | grep " " >/dev/null) && - ! (echo "$ttt$sss$sss$sss" | git stripspace | grep " " >/dev/null) + echo "$ttt$sss" | git stripspace >tmp && + ! grep " " tmp >/dev/null && + echo "$ttt$ttt$sss" | git stripspace >tmp && + ! grep " " tmp >/dev/null && + echo "$ttt$ttt$ttt$sss" | git stripspace >tmp && + ! grep " " tmp >/dev/null && + echo "$ttt$sss$sss" | git stripspace >tmp && + ! grep " " tmp >/dev/null && + echo "$ttt$ttt$sss$sss" | git stripspace >tmp && + ! grep " " tmp >/dev/null && + echo "$ttt$sss$sss$sss" | git stripspace >tmp && + ! grep " " tmp >/dev/null ' test_expect_success \ @@ -339,11 +355,16 @@ test_expect_success \ test_expect_success \ 'spaces without newline at end should not show spaces' ' - ! (printf "" | git stripspace | grep " " >/dev/null) && - ! (printf "$sss" | git stripspace | grep " " >/dev/null) && - ! (printf "$sss$sss" | git stripspace | grep " " >/dev/null) && - ! (printf "$sss$sss$sss" | git stripspace | grep " " >/dev/null) && - ! (printf "$sss$sss$sss$sss" | git stripspace | grep " " >/dev/null) + printf "" | git stripspace >tmp && + ! grep " " tmp >/dev/null && + printf "$sss" | git stripspace >tmp && + ! grep " " tmp >/dev/null && + printf "$sss$sss" | git stripspace >tmp && + ! grep " " tmp >/dev/null && + printf "$sss$sss$sss" | git stripspace >tmp && + ! grep " " tmp >/dev/null && + printf "$sss$sss$sss$sss" | git stripspace >tmp && + ! grep " " tmp >/dev/null ' test_expect_success \ diff --git a/t/t0050-filesystem.sh b/t/t0050-filesystem.sh index afc343cf9b..5c9dc90d0b 100755 --- a/t/t0050-filesystem.sh +++ b/t/t0050-filesystem.sh @@ -104,7 +104,8 @@ test_expect_failure CASE_INSENSITIVE_FS 'add (with different case)' ' rm camelcase && echo 1 >CamelCase && git add CamelCase && - camel=$(git ls-files | grep -i camelcase) && + git ls-files >tmp && + camel=$(grep -i camelcase tmp) && test $(echo "$camel" | wc -l) = 1 && test "z$(git cat-file blob :$camel)" = z1 ' diff --git a/t/t0051-windows-named-pipe.sh b/t/t0051-windows-named-pipe.sh index 10ac92d225..412f413360 100755 --- a/t/t0051-windows-named-pipe.sh +++ b/t/t0051-windows-named-pipe.sh @@ -3,8 +3,13 @@ test_description='Windows named pipes' . ./test-lib.sh +if ! test_have_prereq MINGW +then + skip_all='skipping Windows-specific tests' + test_done +fi -test_expect_success MINGW 'o_append write to named pipe' ' +test_expect_success 'o_append write to named pipe' ' GIT_TRACE="$(pwd)/expect" git status >/dev/null 2>&1 && { test-tool windows-named-pipe t0051 >actual 2>&1 & } && pid=$! && diff --git a/t/t0500-progress-display.sh b/t/t0500-progress-display.sh index 22058b503a..1eb3a8306b 100755 --- a/t/t0500-progress-display.sh +++ b/t/t0500-progress-display.sh @@ -2,6 +2,7 @@ test_description='progress display' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh show_cr () { @@ -17,6 +18,7 @@ test_expect_success 'simple progress display' ' EOF cat >in <<-\EOF && + start 0 update progress 1 update @@ -25,8 +27,9 @@ test_expect_success 'simple progress display' ' progress 4 update progress 5 + stop EOF - test-tool progress "Working hard" <in 2>stderr && + test-tool progress <in 2>stderr && show_cr <stderr >out && test_cmp expect out @@ -41,11 +44,13 @@ test_expect_success 'progress display with total' ' EOF cat >in <<-\EOF && + start 3 progress 1 progress 2 progress 3 + stop EOF - test-tool progress --total=3 "Working hard" <in 2>stderr && + test-tool progress <in 2>stderr && show_cr <stderr >out && test_cmp expect out @@ -62,14 +67,14 @@ Working hard.......2.........3.........4.........5.........6: EOF cat >in <<-\EOF && + start 100000 Working hard.......2.........3.........4.........5.........6 progress 100 progress 1000 progress 10000 progress 100000 + stop EOF - test-tool progress --total=100000 \ - "Working hard.......2.........3.........4.........5.........6" \ - <in 2>stderr && + test-tool progress <in 2>stderr && show_cr <stderr >out && test_cmp expect out @@ -88,16 +93,16 @@ Working hard.......2.........3.........4.........5.........6: EOF cat >in <<-\EOF && + start 100000 Working hard.......2.........3.........4.........5.........6 update progress 1 update progress 2 progress 10000 progress 100000 + stop EOF - test-tool progress --total=100000 \ - "Working hard.......2.........3.........4.........5.........6" \ - <in 2>stderr && + test-tool progress <in 2>stderr && show_cr <stderr >out && test_cmp expect out @@ -116,14 +121,14 @@ Working hard.......2.........3.........4.........5.........6: EOF cat >in <<-\EOF && + start 100000 Working hard.......2.........3.........4.........5.........6 progress 25000 progress 50000 progress 75000 progress 100000 + stop EOF - test-tool progress --total=100000 \ - "Working hard.......2.........3.........4.........5.........6" \ - <in 2>stderr && + test-tool progress <in 2>stderr && show_cr <stderr >out && test_cmp expect out @@ -140,14 +145,14 @@ Working hard.......2.........3.........4.........5.........6.........7.........: EOF cat >in <<-\EOF && + start 100000 Working hard.......2.........3.........4.........5.........6.........7......... progress 25000 progress 50000 progress 75000 progress 100000 + stop EOF - test-tool progress --total=100000 \ - "Working hard.......2.........3.........4.........5.........6.........7........." \ - <in 2>stderr && + test-tool progress <in 2>stderr && show_cr <stderr >out && test_cmp expect out @@ -164,12 +169,14 @@ test_expect_success 'progress shortens - crazy caller' ' EOF cat >in <<-\EOF && + start 1000 progress 100 progress 200 progress 1 progress 1000 + stop EOF - test-tool progress --total=1000 "Working hard" <in 2>stderr && + test-tool progress <in 2>stderr && show_cr <stderr >out && test_cmp expect out @@ -185,6 +192,7 @@ test_expect_success 'progress display with throughput' ' EOF cat >in <<-\EOF && + start 0 throughput 102400 1000 update progress 10 @@ -197,8 +205,9 @@ test_expect_success 'progress display with throughput' ' throughput 409600 4000 update progress 40 + stop EOF - test-tool progress "Working hard" <in 2>stderr && + test-tool progress <in 2>stderr && show_cr <stderr >out && test_cmp expect out @@ -214,6 +223,7 @@ test_expect_success 'progress display with throughput and total' ' EOF cat >in <<-\EOF && + start 40 throughput 102400 1000 progress 10 throughput 204800 2000 @@ -222,8 +232,9 @@ test_expect_success 'progress display with throughput and total' ' progress 30 throughput 409600 4000 progress 40 + stop EOF - test-tool progress --total=40 "Working hard" <in 2>stderr && + test-tool progress <in 2>stderr && show_cr <stderr >out && test_cmp expect out @@ -239,6 +250,7 @@ test_expect_success 'cover up after throughput shortens' ' EOF cat >in <<-\EOF && + start 0 throughput 409600 1000 update progress 1 @@ -251,8 +263,9 @@ test_expect_success 'cover up after throughput shortens' ' throughput 1638400 4000 update progress 4 + stop EOF - test-tool progress "Working hard" <in 2>stderr && + test-tool progress <in 2>stderr && show_cr <stderr >out && test_cmp expect out @@ -267,6 +280,7 @@ test_expect_success 'cover up after throughput shortens a lot' ' EOF cat >in <<-\EOF && + start 0 throughput 1 1000 update progress 1 @@ -276,8 +290,9 @@ test_expect_success 'cover up after throughput shortens a lot' ' throughput 3145728 3000 update progress 3 + stop EOF - test-tool progress "Working hard" <in 2>stderr && + test-tool progress <in 2>stderr && show_cr <stderr >out && test_cmp expect out @@ -285,6 +300,7 @@ test_expect_success 'cover up after throughput shortens a lot' ' test_expect_success 'progress generates traces' ' cat >in <<-\EOF && + start 40 throughput 102400 1000 update progress 10 @@ -297,10 +313,11 @@ test_expect_success 'progress generates traces' ' throughput 409600 4000 update progress 40 + stop EOF - GIT_TRACE2_EVENT="$(pwd)/trace.event" test-tool progress --total=40 \ - "Working hard" <in 2>stderr && + GIT_TRACE2_EVENT="$(pwd)/trace.event" test-tool progress \ + <in 2>stderr && # t0212/parse_events.perl intentionally omits regions and data. test_region progress "Working hard" trace.event && @@ -308,4 +325,54 @@ test_expect_success 'progress generates traces' ' grep "\"key\":\"total_bytes\",\"value\":\"409600\"" trace.event ' +test_expect_success 'progress generates traces: stop / start' ' + cat >in <<-\EOF && + start 0 + stop + EOF + + GIT_TRACE2_EVENT="$PWD/trace-startstop.event" test-tool progress \ + <in 2>stderr && + test_region progress "Working hard" trace-startstop.event +' + +test_expect_success 'progress generates traces: start without stop' ' + cat >in <<-\EOF && + start 0 + EOF + + GIT_TRACE2_EVENT="$PWD/trace-start.event" \ + LSAN_OPTIONS=detect_leaks=0 \ + test-tool progress \ + <in 2>stderr && + grep region_enter.*progress trace-start.event && + ! grep region_leave.*progress trace-start.event +' + +test_expect_success 'progress generates traces: stop without start' ' + cat >in <<-\EOF && + stop + EOF + + GIT_TRACE2_EVENT="$PWD/trace-stop.event" test-tool progress \ + <in 2>stderr && + ! grep region_enter.*progress trace-stop.event && + ! grep region_leave.*progress trace-stop.event +' + +test_expect_success 'progress generates traces: start with active progress bar (no stops)' ' + cat >in <<-\EOF && + start 0 One + start 0 Two + EOF + + GIT_TRACE2_EVENT="$PWD/trace-2start.event" \ + LSAN_OPTIONS=detect_leaks=0 \ + test-tool progress \ + <in 2>stderr && + grep region_enter.*progress.*One trace-2start.event && + grep region_enter.*progress.*Two trace-2start.event && + ! grep region_leave trace-2start.event +' + test_done diff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh index d1115528cb..0710b1fb1e 100755 --- a/t/t1001-read-tree-m-2way.sh +++ b/t/t1001-read-tree-m-2way.sh @@ -21,7 +21,6 @@ In the test, these paths are used: yomin - not in H or M ' -TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-read-tree.sh @@ -38,11 +37,12 @@ compare_change () { } check_cache_at () { - clean_if_empty=$(git diff-files -- "$1") + git diff-files -- "$1" >out && + clean_if_empty=$(cat out) && case "$clean_if_empty" in '') echo "$1: clean" ;; ?*) echo "$1: dirty" ;; - esac + esac && case "$2,$clean_if_empty" in clean,) : ;; clean,?*) false ;; diff --git a/t/t1002-read-tree-m-u-2way.sh b/t/t1002-read-tree-m-u-2way.sh index ca5c5510c7..46cbd5514a 100755 --- a/t/t1002-read-tree-m-u-2way.sh +++ b/t/t1002-read-tree-m-u-2way.sh @@ -9,7 +9,6 @@ This is identical to t1001, but uses -u to update the work tree as well. ' -TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-read-tree.sh @@ -23,11 +22,12 @@ compare_change () { } check_cache_at () { - clean_if_empty=$(git diff-files -- "$1") + git diff-files -- "$1" >out && + clean_if_empty=$(cat out) && case "$clean_if_empty" in '') echo "$1: clean" ;; ?*) echo "$1: dirty" ;; - esac + esac && case "$2,$clean_if_empty" in clean,) : ;; clean,?*) false ;; diff --git a/t/t1003-read-tree-prefix.sh b/t/t1003-read-tree-prefix.sh index e0db2066f3..c860c08ecb 100755 --- a/t/t1003-read-tree-prefix.sh +++ b/t/t1003-read-tree-prefix.sh @@ -25,4 +25,14 @@ test_expect_success 'read-tree --prefix' ' cmp expect actual ' +test_expect_success 'read-tree --prefix with leading slash exits with error' ' + git rm -rf . && + test_must_fail git read-tree --prefix=/two/ $tree && + git read-tree --prefix=two/ $tree && + + git rm -rf . && + test_must_fail git read-tree --prefix=/ $tree && + git read-tree --prefix= $tree +' + test_done diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh index 39382fa195..1b85207694 100755 --- a/t/t1006-cat-file.sh +++ b/t/t1006-cat-file.sh @@ -4,6 +4,98 @@ test_description='git cat-file' . ./test-lib.sh +test_cmdmode_usage () { + test_expect_code 129 "$@" 2>err && + grep "^error:.*is incompatible with" err +} + +for switches in \ + '-e -p' \ + '-p -t' \ + '-t -s' \ + '-s --textconv' \ + '--textconv --filters' \ + '--batch-all-objects -e' +do + test_expect_success "usage: cmdmode $switches" ' + test_cmdmode_usage git cat-file $switches + ' +done + +test_incompatible_usage () { + test_expect_code 129 "$@" 2>err && + grep -E "^(fatal|error):.*(requires|incompatible with|needs)" err +} + +for opt in --batch --batch-check +do + test_expect_success "usage: incompatible options: --path with $opt" ' + test_incompatible_usage git cat-file --path=foo $opt + ' +done + +test_missing_usage () { + test_expect_code 129 "$@" 2>err && + grep -E "^fatal:.*required" err +} + +short_modes="-e -p -t -s" +cw_modes="--textconv --filters" + +for opt in $cw_modes +do + test_expect_success "usage: $opt requires another option" ' + test_missing_usage git cat-file $opt + ' +done + +for opt in $short_modes +do + test_expect_success "usage: $opt requires another option" ' + test_missing_usage git cat-file $opt + ' + + for opt2 in --batch \ + --batch-check \ + --follow-symlinks \ + "--path=foo HEAD:some-path.txt" + do + test_expect_success "usage: incompatible options: $opt and $opt2" ' + test_incompatible_usage git cat-file $opt $opt2 + ' + done +done + +test_too_many_arguments () { + test_expect_code 129 "$@" 2>err && + grep -E "^fatal: too many arguments$" err +} + +for opt in $short_modes $cw_modes +do + args="one two three" + test_expect_success "usage: too many arguments: $opt $args" ' + test_too_many_arguments git cat-file $opt $args + ' + + for opt2 in --buffer --follow-symlinks + do + test_expect_success "usage: incompatible arguments: $opt with batch option $opt2" ' + test_incompatible_usage git cat-file $opt $opt2 + ' + done +done + +for opt in --buffer \ + --follow-symlinks \ + --batch-all-objects +do + test_expect_success "usage: bad option combination: $opt without batch mode" ' + test_incompatible_usage git cat-file $opt && + test_incompatible_usage git cat-file $opt commit HEAD + ' +done + echo_without_newline () { printf '%s' "$*" } @@ -13,13 +105,18 @@ strlen () { } maybe_remove_timestamp () { - if test -z "$2"; then - echo_without_newline "$1" - else - echo_without_newline "$(printf '%s\n' "$1" | sed -e 's/ [0-9][0-9]* [-+][0-9][0-9][0-9][0-9]$//')" - fi + if test -z "$2"; then + echo_without_newline "$1" + else + echo_without_newline "$(printf '%s\n' "$1" | remove_timestamp)" + fi +} + +remove_timestamp () { + sed -e 's/ [0-9][0-9]* [-+][0-9][0-9][0-9][0-9]$//' } + run_tests () { type=$1 sha1=$2 @@ -85,12 +182,36 @@ $content" test_cmp expect actual ' + for opt in --buffer --no-buffer + do + test -z "$content" || + test_expect_success "--batch-command $opt output of $type content is correct" ' + maybe_remove_timestamp "$batch_output" $no_ts >expect && + maybe_remove_timestamp "$(test_write_lines "contents $sha1" | + git cat-file --batch-command $opt)" $no_ts >actual && + test_cmp expect actual + ' + + test_expect_success "--batch-command $opt output of $type info is correct" ' + echo "$sha1 $type $size" >expect && + test_write_lines "info $sha1" | + git cat-file --batch-command $opt >actual && + test_cmp expect actual + ' + done + test_expect_success "custom --batch-check format" ' echo "$type $sha1" >expect && echo $sha1 | git cat-file --batch-check="%(objecttype) %(objectname)" >actual && test_cmp expect actual ' + test_expect_success "custom --batch-command format" ' + echo "$type $sha1" >expect && + echo "info $sha1" | git cat-file --batch-command="%(objecttype) %(objectname)" >actual && + test_cmp expect actual + ' + test_expect_success '--batch-check with %(rest)' ' echo "$type this is some extra content" >expect && echo "$sha1 this is some extra content" | @@ -132,6 +253,22 @@ test_expect_success "setup" ' run_tests 'blob' $hello_sha1 $hello_size "$hello_content" "$hello_content" +test_expect_success '--batch-command --buffer with flush for blob info' ' + echo "$hello_sha1 blob $hello_size" >expect && + test_write_lines "info $hello_sha1" "flush" | + GIT_TEST_CAT_FILE_NO_FLUSH_ON_EXIT=1 \ + git cat-file --batch-command --buffer >actual && + test_cmp expect actual +' + +test_expect_success '--batch-command --buffer without flush for blob info' ' + touch output && + test_write_lines "info $hello_sha1" | + GIT_TEST_CAT_FILE_NO_FLUSH_ON_EXIT=1 \ + git cat-file --batch-command --buffer >>output && + test_must_be_empty output +' + test_expect_success '--batch-check without %(rest) considers whole line' ' echo "$hello_sha1 blob $hello_size" >expect && git update-index --add --cacheinfo 100644 $hello_sha1 "white space" && @@ -175,7 +312,7 @@ test_expect_success \ "Reach a blob from a tag pointing to it" \ "test '$hello_content' = \"\$(git cat-file blob $tag_sha1)\"" -for batch in batch batch-check +for batch in batch batch-check batch-command do for opt in t s e p do @@ -281,6 +418,49 @@ test_expect_success "--batch-check with multiple sha1s gives correct format" ' "$(echo_without_newline "$batch_check_input" | git cat-file --batch-check)" ' +test_expect_success '--batch-command with multiple info calls gives correct format' ' + cat >expect <<-EOF && + $hello_sha1 blob $hello_size + $tree_sha1 tree $tree_size + $commit_sha1 commit $commit_size + $tag_sha1 tag $tag_size + deadbeef missing + EOF + + git cat-file --batch-command --buffer >actual <<-EOF && + info $hello_sha1 + info $tree_sha1 + info $commit_sha1 + info $tag_sha1 + info deadbeef + EOF + + test_cmp expect actual +' + +test_expect_success '--batch-command with multiple command calls gives correct format' ' + remove_timestamp >expect <<-EOF && + $hello_sha1 blob $hello_size + $hello_content + $commit_sha1 commit $commit_size + $commit_content + $tag_sha1 tag $tag_size + $tag_content + deadbeef missing + EOF + + git cat-file --batch-command --buffer >actual_raw <<-EOF && + contents $hello_sha1 + contents $commit_sha1 + contents $tag_sha1 + contents deadbeef + flush + EOF + + remove_timestamp <actual_raw >actual && + test_cmp expect actual +' + test_expect_success 'setup blobs which are likely to delta' ' test-tool genrandom foo 10240 >foo && { cat foo && echo plus; } >foo-plus && @@ -871,5 +1051,40 @@ test_expect_success 'cat-file --batch-all-objects --batch-check ignores replace' echo "$orig commit $orig_size" >expect && test_cmp expect actual ' +test_expect_success 'batch-command empty command' ' + echo "" >cmd && + test_expect_code 128 git cat-file --batch-command <cmd 2>err && + grep "^fatal:.*empty command in input.*" err +' + +test_expect_success 'batch-command whitespace before command' ' + echo " info deadbeef" >cmd && + test_expect_code 128 git cat-file --batch-command <cmd 2>err && + grep "^fatal:.*whitespace before command.*" err +' + +test_expect_success 'batch-command unknown command' ' + echo unknown_command >cmd && + test_expect_code 128 git cat-file --batch-command <cmd 2>err && + grep "^fatal:.*unknown command.*" err +' + +test_expect_success 'batch-command missing arguments' ' + echo "info" >cmd && + test_expect_code 128 git cat-file --batch-command <cmd 2>err && + grep "^fatal:.*info requires arguments.*" err +' + +test_expect_success 'batch-command flush with arguments' ' + echo "flush arg" >cmd && + test_expect_code 128 git cat-file --batch-command --buffer <cmd 2>err && + grep "^fatal:.*flush takes no arguments.*" err +' + +test_expect_success 'batch-command flush without --buffer' ' + echo "flush" >cmd && + test_expect_code 128 git cat-file --batch-command <cmd 2>err && + grep "^fatal:.*flush is only for --buffer mode.*" err +' test_done diff --git a/t/t1007-hash-object.sh b/t/t1007-hash-object.sh index 64b340f227..ac5ad8c740 100755 --- a/t/t1007-hash-object.sh +++ b/t/t1007-hash-object.sh @@ -2,6 +2,7 @@ test_description="git hash-object" +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh echo_without_newline() { diff --git a/t/t1011-read-tree-sparse-checkout.sh b/t/t1011-read-tree-sparse-checkout.sh index 24092c09a9..dd957be1b7 100755 --- a/t/t1011-read-tree-sparse-checkout.sh +++ b/t/t1011-read-tree-sparse-checkout.sh @@ -187,11 +187,32 @@ test_expect_success 'read-tree updates worktree, absent case' ' test ! -f init.t ' +test_expect_success 'read-tree will not throw away dirty changes, non-sparse' ' + echo "/*" >.git/info/sparse-checkout && + read_tree_u_must_succeed -m -u HEAD && + + echo dirty >init.t && + read_tree_u_must_fail -m -u HEAD^ && + test_path_is_file init.t && + grep -q dirty init.t +' + +test_expect_success 'read-tree will not throw away dirty changes, sparse' ' + echo "/*" >.git/info/sparse-checkout && + read_tree_u_must_succeed -m -u HEAD && + + echo dirty >init.t && + echo sub/added >.git/info/sparse-checkout && + read_tree_u_must_fail -m -u HEAD^ && + test_path_is_file init.t && + grep -q dirty init.t +' + test_expect_success 'read-tree updates worktree, dirty case' ' echo sub/added >.git/info/sparse-checkout && git checkout -f top && echo dirty >init.t && - read_tree_u_must_succeed -m -u HEAD^ && + read_tree_u_must_fail -m -u HEAD^ && grep -q dirty init.t && rm init.t ' diff --git a/t/t1090-sparse-checkout-scope.sh b/t/t1090-sparse-checkout-scope.sh index 3deb490187..d1833c0f31 100755 --- a/t/t1090-sparse-checkout-scope.sh +++ b/t/t1090-sparse-checkout-scope.sh @@ -52,6 +52,25 @@ test_expect_success 'return to full checkout of main' ' test "$(cat b)" = "modified" ' +test_expect_success 'skip-worktree on files outside sparse patterns' ' + git sparse-checkout disable && + git sparse-checkout set --no-cone "a*" && + git checkout-index --all --ignore-skip-worktree-bits && + + git ls-files -t >output && + ! grep ^S output >actual && + test_must_be_empty actual && + + test_config sparse.expectFilesOutsideOfPatterns true && + cat <<-\EOF >expect && + S b + S c + EOF + git ls-files -t >output && + grep ^S output >actual && + test_cmp expect actual +' + test_expect_success 'in partial clone, sparse checkout only fetches needed blobs' ' test_create_repo server && git clone "file://$(pwd)/server" client && diff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh index 42776984fe..9a90031018 100755 --- a/t/t1091-sparse-checkout-builtin.sh +++ b/t/t1091-sparse-checkout-builtin.sh @@ -5,6 +5,9 @@ test_description='sparse checkout builtin tests' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +GIT_TEST_SPLIT_INDEX=false +export GIT_TEST_SPLIT_INDEX + . ./test-lib.sh list_files() { @@ -79,6 +82,12 @@ test_expect_success 'git sparse-checkout init' ' check_files repo a ' +test_expect_success 'git sparse-checkout init in empty repo' ' + test_when_finished rm -rf empty-repo blank-template && + git init --template= empty-repo && + git -C empty-repo sparse-checkout init +' + test_expect_success 'git sparse-checkout list after init' ' git -C repo sparse-checkout list >actual && cat >expect <<-\EOF && @@ -117,7 +126,7 @@ test_expect_success 'switching to cone mode with non-cone mode patterns' ' cd bad-patterns && git sparse-checkout init && git sparse-checkout add dir && - git config core.sparseCheckoutCone true && + git config --worktree core.sparseCheckoutCone true && test_must_fail git sparse-checkout add dir 2>err && grep "existing sparse-checkout patterns do not use cone mode" err ) @@ -146,9 +155,9 @@ test_expect_success 'interaction with clone --no-checkout (unborn index)' ' ' test_expect_success 'set enables config' ' - git init empty-config && + git init worktree-config && ( - cd empty-config && + cd worktree-config && test_commit test file && test_path_is_missing .git/config.worktree && git sparse-checkout set nothing && @@ -201,6 +210,21 @@ test_expect_success 'add to sparse-checkout' ' check_files repo "a folder1 folder2" ' +test_expect_success 'worktree: add copies sparse-checkout patterns' ' + cat repo/.git/info/sparse-checkout >old && + test_when_finished cp old repo/.git/info/sparse-checkout && + test_when_finished git -C repo worktree remove ../worktree && + git -C repo sparse-checkout set --no-cone "/*" && + git -C repo worktree add --quiet ../worktree 2>err && + test_must_be_empty err && + new="$(git -C worktree rev-parse --git-path info/sparse-checkout)" && + test_path_is_file "$new" && + test_cmp repo/.git/info/sparse-checkout "$new" && + git -C worktree sparse-checkout set --cone && + test_cmp_config -C worktree true core.sparseCheckoutCone && + test_must_fail git -C repo core.sparseCheckoutCone +' + test_expect_success 'cone mode: match patterns' ' git -C repo config --worktree core.sparseCheckoutCone true && rm -rf repo/a repo/folder1 repo/folder2 && @@ -228,36 +252,31 @@ test_expect_success 'sparse-checkout disable' ' ' test_expect_success 'sparse-index enabled and disabled' ' - ( - sane_unset GIT_TEST_SPLIT_INDEX && - git -C repo update-index --no-split-index && - - git -C repo sparse-checkout init --cone --sparse-index && - test_cmp_config -C repo true index.sparse && - git -C repo ls-files --sparse >sparse && - git -C repo sparse-checkout disable && - git -C repo ls-files --sparse >full && - - cat >expect <<-\EOF && - @@ -1,4 +1,7 @@ - a - -deep/ - -folder1/ - -folder2/ - +deep/a - +deep/deeper1/a - +deep/deeper1/deepest/a - +deep/deeper2/a - +folder1/a - +folder2/a - EOF + git -C repo sparse-checkout init --cone --sparse-index && + test_cmp_config -C repo true index.sparse && + git -C repo ls-files --sparse >sparse && + git -C repo sparse-checkout disable && + git -C repo ls-files --sparse >full && - diff -u sparse full | tail -n +3 >actual && - test_cmp expect actual && + cat >expect <<-\EOF && + @@ -1,4 +1,7 @@ + a + -deep/ + -folder1/ + -folder2/ + +deep/a + +deep/deeper1/a + +deep/deeper1/deepest/a + +deep/deeper2/a + +folder1/a + +folder2/a + EOF + + diff -u sparse full | tail -n +3 >actual && + test_cmp expect actual && - git -C repo config --list >config && - ! grep index.sparse config - ) + git -C repo config --list >config && + test_cmp_config -C repo false index.sparse ' test_expect_success 'cone mode: init and set' ' @@ -491,6 +510,37 @@ test_expect_failure 'sparse-checkout reapply' ' git -C tweak sparse-checkout disable ' +test_expect_success 'reapply can handle config options' ' + git -C repo sparse-checkout init --cone --no-sparse-index && + git -C repo config --worktree --list >actual && + cat >expect <<-\EOF && + core.sparsecheckout=true + core.sparsecheckoutcone=true + index.sparse=false + EOF + test_cmp expect actual && + + git -C repo sparse-checkout reapply --no-cone --no-sparse-index && + git -C repo config --worktree --list >actual && + cat >expect <<-\EOF && + core.sparsecheckout=true + core.sparsecheckoutcone=false + index.sparse=false + EOF + test_cmp expect actual && + + git -C repo sparse-checkout reapply --cone --sparse-index && + git -C repo config --worktree --list >actual && + cat >expect <<-\EOF && + core.sparsecheckout=true + core.sparsecheckoutcone=true + index.sparse=true + EOF + test_cmp expect actual && + + git -C repo sparse-checkout disable +' + test_expect_success 'cone mode: set with core.ignoreCase=true' ' rm repo/.git/info/sparse-checkout && git -C repo sparse-checkout init --cone && @@ -520,17 +570,17 @@ test_expect_success 'interaction with submodules' ' ' test_expect_success 'different sparse-checkouts with worktrees' ' + git -C repo sparse-checkout set --cone deep folder1 && git -C repo worktree add --detach ../worktree && - check_files worktree "a deep folder1 folder2" && - git -C worktree sparse-checkout init --cone && - git -C repo sparse-checkout set folder1 && - git -C worktree sparse-checkout set deep/deeper1 && - check_files repo a folder1 && - check_files worktree a deep + check_files worktree "a deep folder1" && + git -C repo sparse-checkout set --cone folder1 && + git -C worktree sparse-checkout set --cone deep/deeper1 && + check_files repo "a folder1" && + check_files worktree "a deep" ' test_expect_success 'set using filename keeps file on-disk' ' - git -C repo sparse-checkout set a deep && + git -C repo sparse-checkout set --skip-checks a deep && cat >expect <<-\EOF && /* !/*/ @@ -641,7 +691,7 @@ test_expect_success BSLASHPSPEC 'pattern-checks: escaped characters' ' git -C escaped reset --hard $COMMIT && check_files escaped "a deep folder1 folder2 zbad\\dir zdoes*exist" zglob[!a]? && git -C escaped sparse-checkout init --cone && - git -C escaped sparse-checkout set zbad\\dir/bogus "zdoes*not*exist" "zdoes*exist" "zglob[!a]?" && + git -C escaped sparse-checkout set --skip-checks zbad\\dir/bogus "zdoes*not*exist" "zdoes*exist" "zglob[!a]?" && cat >expect <<-\EOF && /* !/*/ @@ -766,4 +816,59 @@ test_expect_success 'malformed cone-mode patterns' ' grep "warning: disabling cone pattern matching" err ' +test_expect_success 'set from subdir pays attention to prefix' ' + git -C repo sparse-checkout disable && + git -C repo/deep sparse-checkout set --cone deeper2 ../folder1 && + + git -C repo sparse-checkout list >actual && + + cat >expect <<-\EOF && + deep/deeper2 + folder1 + EOF + test_cmp expect actual +' + +test_expect_success 'add from subdir pays attention to prefix' ' + git -C repo sparse-checkout set --cone deep/deeper2 && + git -C repo/deep sparse-checkout add deeper1/deepest ../folder1 && + + git -C repo sparse-checkout list >actual && + + cat >expect <<-\EOF && + deep/deeper1/deepest + deep/deeper2 + folder1 + EOF + test_cmp expect actual +' + +test_expect_success 'set from subdir in non-cone mode throws an error' ' + git -C repo sparse-checkout disable && + test_must_fail git -C repo/deep sparse-checkout set --no-cone deeper2 ../folder1 2>error && + + grep "run from the toplevel directory in non-cone mode" error +' + +test_expect_success 'set from subdir in non-cone mode throws an error' ' + git -C repo sparse-checkout set --no-cone deep/deeper2 && + test_must_fail git -C repo/deep sparse-checkout add deeper1/deepest ../folder1 2>error && + + grep "run from the toplevel directory in non-cone mode" error +' + +test_expect_success 'by default, cone mode will error out when passed files' ' + git -C repo sparse-checkout reapply --cone && + test_must_fail git -C repo sparse-checkout add .gitignore 2>error && + + grep ".gitignore.*is not a directory" error +' + +test_expect_success 'by default, non-cone mode will warn on individual files' ' + git -C repo sparse-checkout reapply --no-cone && + git -C repo sparse-checkout add .gitignore 2>warning && + + grep "pass a leading slash before paths.*if you want a single file" warning +' + test_done diff --git a/t/t1092-sparse-checkout-compatibility.sh b/t/t1092-sparse-checkout-compatibility.sh index 4ba1617752..dcc0a30d4a 100755 --- a/t/t1092-sparse-checkout-compatibility.sh +++ b/t/t1092-sparse-checkout-compatibility.sh @@ -244,6 +244,24 @@ test_expect_success 'expanded in-memory index matches full index' ' test_sparse_match git ls-files --stage ' +test_expect_success 'root directory cannot be sparse' ' + init_repos && + + # Remove all in-cone files and directories from the index, collapse index + # with `git sparse-checkout reapply` + git -C sparse-index rm -r . && + git -C sparse-index sparse-checkout reapply && + + # Verify sparse directories still present, root directory is not sparse + cat >expect <<-EOF && + folder1/ + folder2/ + x/ + EOF + git -C sparse-index ls-files --sparse >actual && + test_cmp expect actual +' + test_expect_success 'status with options' ' init_repos && test_sparse_match ls && @@ -260,6 +278,13 @@ test_expect_success 'status with options' ' test_all_match git status --porcelain=v2 -uno ' +test_expect_success 'status with diff in unexpanded sparse directory' ' + init_repos && + test_all_match git checkout rename-base && + test_all_match git reset --soft rename-out-to-out && + test_all_match git status --porcelain=v2 +' + test_expect_success 'status reports sparse-checkout' ' init_repos && git -C sparse-checkout status >full && @@ -367,7 +392,7 @@ test_expect_success 'status/add: outside sparse cone' ' write_script edit-contents <<-\EOF && echo text >>$1 EOF - run_on_sparse ../edit-contents folder1/a && + run_on_all ../edit-contents folder1/a && run_on_all ../edit-contents folder1/new && test_sparse_match git status --porcelain=v2 && @@ -376,8 +401,8 @@ test_expect_success 'status/add: outside sparse cone' ' test_sparse_match test_must_fail git add folder1/a && grep "Disable or modify the sparsity rules" sparse-checkout-err && test_sparse_unstaged folder1/a && - test_sparse_match test_must_fail git add --refresh folder1/a && - grep "Disable or modify the sparsity rules" sparse-checkout-err && + test_all_match git add --refresh folder1/a && + test_must_be_empty sparse-checkout-err && test_sparse_unstaged folder1/a && test_sparse_match test_must_fail git add folder1/new && grep "Disable or modify the sparsity rules" sparse-checkout-err && @@ -593,13 +618,11 @@ test_expect_success 'reset with pathspecs outside sparse definition' ' test_sparse_match git reset update-folder1 -- folder1 && git -C full-checkout reset update-folder1 -- folder1 && - test_sparse_match git status --porcelain=v2 && - test_all_match git rev-parse HEAD:folder1 && + test_all_match git ls-files -s -- folder1 && test_sparse_match git reset update-folder2 -- folder2/a && git -C full-checkout reset update-folder2 -- folder2/a && - test_sparse_match git status --porcelain=v2 && - test_all_match git rev-parse HEAD:folder2/a + test_all_match git ls-files -s -- folder2/a ' test_expect_success 'reset with wildcard pathspec' ' @@ -629,6 +652,260 @@ test_expect_success 'reset with wildcard pathspec' ' test_all_match git ls-files -s -- folder1 ' +test_expect_success 'update-index modify outside sparse definition' ' + init_repos && + + write_script edit-contents <<-\EOF && + echo text >>$1 + EOF + + # Create & modify folder1/a + # Note that this setup is a manual way of reaching the erroneous + # condition in which a `skip-worktree` enabled, outside-of-cone file + # exists on disk. It is used here to ensure `update-index` is stable + # and behaves predictably if such a condition occurs. + run_on_sparse mkdir -p folder1 && + run_on_sparse cp ../initial-repo/folder1/a folder1/a && + run_on_all ../edit-contents folder1/a && + + # If file has skip-worktree enabled, but the file is present, it is + # treated the same as if skip-worktree is disabled + test_all_match git status --porcelain=v2 && + test_all_match git update-index folder1/a && + test_all_match git status --porcelain=v2 && + + # When skip-worktree is disabled (even on files outside sparse cone), file + # is updated in the index + test_sparse_match git update-index --no-skip-worktree folder1/a && + test_all_match git status --porcelain=v2 && + test_all_match git update-index folder1/a && + test_all_match git status --porcelain=v2 +' + +test_expect_success 'update-index --add outside sparse definition' ' + init_repos && + + write_script edit-contents <<-\EOF && + echo text >>$1 + EOF + + # Create folder1, add new file + run_on_sparse mkdir -p folder1 && + run_on_all ../edit-contents folder1/b && + + # The *untracked* out-of-cone file is added to the index because it does + # not have a `skip-worktree` bit to signal that it should be ignored + # (unlike in `git add`, which will fail due to the file being outside + # the sparse checkout definition). + test_all_match git update-index --add folder1/b && + test_all_match git status --porcelain=v2 +' + +# NEEDSWORK: `--remove`, unlike the rest of `update-index`, does not ignore +# `skip-worktree` entries by default and will remove them from the index. +# The `--ignore-skip-worktree-entries` flag must be used in conjunction with +# `--remove` to ignore the `skip-worktree` entries and prevent their removal +# from the index. +test_expect_success 'update-index --remove outside sparse definition' ' + init_repos && + + # When --ignore-skip-worktree-entries is _not_ specified: + # out-of-cone, not-on-disk files are removed from the index + test_sparse_match git update-index --remove folder1/a && + cat >expect <<-EOF && + D folder1/a + EOF + test_sparse_match git diff --cached --name-status && + test_cmp expect sparse-checkout-out && + + # Reset the state + test_all_match git reset --hard && + + # When --ignore-skip-worktree-entries is specified, out-of-cone + # (skip-worktree) files are ignored + test_sparse_match git update-index --remove --ignore-skip-worktree-entries folder1/a && + test_sparse_match git diff --cached --name-status && + test_must_be_empty sparse-checkout-out && + + # Reset the state + test_all_match git reset --hard && + + # --force-remove supercedes --ignore-skip-worktree-entries, removing + # a skip-worktree file from the index (and disk) when both are specified + # with --remove + test_sparse_match git update-index --force-remove --ignore-skip-worktree-entries folder1/a && + cat >expect <<-EOF && + D folder1/a + EOF + test_sparse_match git diff --cached --name-status && + test_cmp expect sparse-checkout-out +' + +test_expect_success 'update-index with directories' ' + init_repos && + + # update-index will exit silently when provided with a directory name + # containing a trailing slash + test_all_match git update-index deep/ folder1/ && + grep "Ignoring path deep/" sparse-checkout-err && + grep "Ignoring path folder1/" sparse-checkout-err && + + # When update-index is given a directory name WITHOUT a trailing slash, it will + # behave in different ways depending on the status of the directory on disk: + # * if it exists, the command exits with an error ("add individual files instead") + # * if it does NOT exist (e.g., in a sparse-checkout), it is assumed to be a + # file and either triggers an error ("does not exist and --remove not passed") + # or is ignored completely (when using --remove) + test_all_match test_must_fail git update-index deep && + run_on_all test_must_fail git update-index folder1 && + test_must_fail git -C full-checkout update-index --remove folder1 && + test_sparse_match git update-index --remove folder1 && + test_all_match git status --porcelain=v2 +' + +test_expect_success 'update-index --again file outside sparse definition' ' + init_repos && + + test_all_match git checkout -b test-reupdate && + + # Update HEAD without modifying the index to introduce a difference in + # folder1/a + test_sparse_match git reset --soft update-folder1 && + + # Because folder1/a differs in the index vs HEAD, + # `git update-index --no-skip-worktree --again` will effectively perform + # `git update-index --no-skip-worktree folder1/a` and remove the skip-worktree + # flag from folder1/a + test_sparse_match git update-index --no-skip-worktree --again && + test_sparse_match git status --porcelain=v2 && + + cat >expect <<-EOF && + D folder1/a + EOF + test_sparse_match git diff --name-status && + test_cmp expect sparse-checkout-out +' + +test_expect_success 'update-index --cacheinfo' ' + init_repos && + + deep_a_oid=$(git -C full-checkout rev-parse update-deep:deep/a) && + folder2_oid=$(git -C full-checkout rev-parse update-folder2:folder2) && + folder1_a_oid=$(git -C full-checkout rev-parse update-folder1:folder1/a) && + + test_all_match git update-index --cacheinfo 100644 $deep_a_oid deep/a && + test_all_match git status --porcelain=v2 && + + # Cannot add sparse directory, even in sparse index case + test_all_match test_must_fail git update-index --add --cacheinfo 040000 $folder2_oid folder2/ && + + # Sparse match only: the new outside-of-cone entry is added *without* skip-worktree, + # so `git status` reports it as "deleted" in the worktree + test_sparse_match git update-index --add --cacheinfo 100644 $folder1_a_oid folder1/a && + test_sparse_match git status --porcelain=v2 && + cat >expect <<-EOF && + MD folder1/a + EOF + test_sparse_match git status --short -- folder1/a && + test_cmp expect sparse-checkout-out && + + # To return folder1/a to "normal" for a sparse checkout (ignored & + # outside-of-cone), add the skip-worktree flag. + test_sparse_match git update-index --skip-worktree folder1/a && + cat >expect <<-EOF && + S folder1/a + EOF + test_sparse_match git ls-files -t -- folder1/a && + test_cmp expect sparse-checkout-out +' + +for MERGE_TREES in "base HEAD update-folder2" \ + "update-folder1 update-folder2" \ + "update-folder2" +do + test_expect_success "'read-tree -mu $MERGE_TREES' with files outside sparse definition" ' + init_repos && + + # Although the index matches, without --no-sparse-checkout, outside-of- + # definition files will not exist on disk for sparse checkouts + test_all_match git read-tree -mu $MERGE_TREES && + test_all_match git status --porcelain=v2 && + test_path_is_missing sparse-checkout/folder2 && + test_path_is_missing sparse-index/folder2 && + + test_all_match git read-tree --reset -u HEAD && + test_all_match git status --porcelain=v2 && + + test_all_match git read-tree -mu --no-sparse-checkout $MERGE_TREES && + test_all_match git status --porcelain=v2 && + test_cmp sparse-checkout/folder2/a sparse-index/folder2/a && + test_cmp sparse-checkout/folder2/a full-checkout/folder2/a + + ' +done + +test_expect_success 'read-tree --merge with edit/edit conflicts in sparse directories' ' + init_repos && + + # Merge of multiple changes to same directory (but not same files) should + # succeed + test_all_match git read-tree -mu base rename-base update-folder1 && + test_all_match git status --porcelain=v2 && + + test_all_match git reset --hard && + + test_all_match git read-tree -mu rename-base update-folder2 && + test_all_match git status --porcelain=v2 && + + test_all_match git reset --hard && + + test_all_match test_must_fail git read-tree -mu base update-folder1 rename-out-to-in && + test_all_match test_must_fail git read-tree -mu rename-out-to-in update-folder1 +' + +test_expect_success 'read-tree --prefix' ' + init_repos && + + # If files differing between the index and target <commit-ish> exist + # inside the prefix, `read-tree --prefix` should fail + test_all_match test_must_fail git read-tree --prefix=deep/ deepest && + test_all_match test_must_fail git read-tree --prefix=folder1/ update-folder1 && + + # If no differing index entries exist matching the prefix, + # `read-tree --prefix` updates the index successfully + test_all_match git rm -rf deep/deeper1/deepest/ && + test_all_match git read-tree --prefix=deep/deeper1/deepest -u deepest && + test_all_match git status --porcelain=v2 && + + test_all_match git rm -rf --sparse folder1/ && + test_all_match git read-tree --prefix=folder1/ -u update-folder1 && + test_all_match git status --porcelain=v2 && + + test_all_match git rm -rf --sparse folder2/0 && + test_all_match git read-tree --prefix=folder2/0/ -u rename-out-to-out && + test_all_match git status --porcelain=v2 +' + +test_expect_success 'read-tree --merge with directory-file conflicts' ' + init_repos && + + test_all_match git checkout -b test-branch rename-base && + + # Although the index matches, without --no-sparse-checkout, outside-of- + # definition files will not exist on disk for sparse checkouts + test_sparse_match git read-tree -mu rename-out-to-out && + test_sparse_match git status --porcelain=v2 && + test_path_is_missing sparse-checkout/folder2 && + test_path_is_missing sparse-index/folder2 && + + test_sparse_match git read-tree --reset -u HEAD && + test_sparse_match git status --porcelain=v2 && + + test_sparse_match git read-tree -mu --no-sparse-checkout rename-out-to-out && + test_sparse_match git status --porcelain=v2 && + test_cmp sparse-checkout/folder2/0/1 sparse-index/folder2/0/1 +' + test_expect_success 'merge, cherry-pick, and rebase' ' init_repos && @@ -754,6 +1031,74 @@ test_expect_success 'cherry-pick with conflicts' ' test_all_match test_must_fail git cherry-pick to-cherry-pick ' +test_expect_success 'checkout-index inside sparse definition' ' + init_repos && + + run_on_all rm -f deep/a && + test_all_match git checkout-index -- deep/a && + test_all_match git status --porcelain=v2 && + + echo test >>new-a && + run_on_all cp ../new-a a && + test_all_match test_must_fail git checkout-index -- a && + test_all_match git checkout-index -f -- a && + test_all_match git status --porcelain=v2 +' + +test_expect_success 'checkout-index outside sparse definition' ' + init_repos && + + # Without --ignore-skip-worktree-bits, outside-of-cone files will trigger + # an error + test_sparse_match test_must_fail git checkout-index -- folder1/a && + test_i18ngrep "folder1/a has skip-worktree enabled" sparse-checkout-err && + test_path_is_missing folder1/a && + + # With --ignore-skip-worktree-bits, outside-of-cone files are checked out + test_sparse_match git checkout-index --ignore-skip-worktree-bits -- folder1/a && + test_cmp sparse-checkout/folder1/a sparse-index/folder1/a && + test_cmp sparse-checkout/folder1/a full-checkout/folder1/a && + + run_on_sparse rm -rf folder1 && + echo test >new-a && + run_on_sparse mkdir -p folder1 && + run_on_all cp ../new-a folder1/a && + + test_all_match test_must_fail git checkout-index --ignore-skip-worktree-bits -- folder1/a && + test_all_match git checkout-index -f --ignore-skip-worktree-bits -- folder1/a && + test_cmp sparse-checkout/folder1/a sparse-index/folder1/a && + test_cmp sparse-checkout/folder1/a full-checkout/folder1/a +' + +test_expect_success 'checkout-index with folders' ' + init_repos && + + # Inside checkout definition + test_all_match test_must_fail git checkout-index -f -- deep/ && + + # Outside checkout definition + # Note: although all tests fail (as expected), the messaging differs. For + # non-sparse index checkouts, the error is that the "file" does not appear + # in the index; for sparse checkouts, the error is explicitly that the + # entry is a sparse directory. + run_on_all test_must_fail git checkout-index -f -- folder1/ && + test_cmp full-checkout-err sparse-checkout-err && + ! test_cmp full-checkout-err sparse-index-err && + grep "is a sparse directory" sparse-index-err +' + +test_expect_success 'checkout-index --all' ' + init_repos && + + test_all_match git checkout-index --all && + test_sparse_match test_path_is_missing folder1 && + + # --ignore-skip-worktree-bits will cause `skip-worktree` files to be + # checked out, causing the outside-of-cone `folder1` to exist on-disk + test_all_match git checkout-index --ignore-skip-worktree-bits --all && + test_all_match test_path_exists folder1 +' + test_expect_success 'clean' ' init_repos && @@ -763,23 +1108,42 @@ test_expect_success 'clean' ' test_all_match git commit -m "ignore bogus files" && run_on_sparse mkdir folder1 && + run_on_all mkdir -p deep/untracked-deep && run_on_all touch folder1/bogus && + run_on_all touch folder1/untracked && + run_on_all touch deep/untracked-deep/bogus && + run_on_all touch deep/untracked-deep/untracked && test_all_match git status --porcelain=v2 && test_all_match git clean -f && test_all_match git status --porcelain=v2 && test_sparse_match ls && test_sparse_match ls folder1 && + run_on_all test_path_exists folder1/bogus && + run_on_all test_path_is_missing folder1/untracked && + run_on_all test_path_exists deep/untracked-deep/bogus && + run_on_all test_path_exists deep/untracked-deep/untracked && + + test_all_match git clean -fd && + test_all_match git status --porcelain=v2 && + test_sparse_match ls && + test_sparse_match ls folder1 && + run_on_all test_path_exists folder1/bogus && + run_on_all test_path_exists deep/untracked-deep/bogus && + run_on_all test_path_is_missing deep/untracked-deep/untracked && test_all_match git clean -xf && test_all_match git status --porcelain=v2 && test_sparse_match ls && test_sparse_match ls folder1 && + run_on_all test_path_is_missing folder1/bogus && + run_on_all test_path_exists deep/untracked-deep/bogus && test_all_match git clean -xdf && test_all_match git status --porcelain=v2 && test_sparse_match ls && test_sparse_match ls folder1 && + run_on_all test_path_is_missing deep/untracked-deep/bogus && test_sparse_match test_path_is_dir folder1 ' @@ -898,6 +1262,8 @@ test_expect_success 'sparse-index is not expanded' ' echo >>sparse-index/untracked.txt && ensure_not_expanded add . && + ensure_not_expanded checkout-index -f a && + ensure_not_expanded checkout-index -f --all && for ref in update-deep update-folder1 update-folder2 update-deep do echo >>sparse-index/README.md && @@ -926,6 +1292,8 @@ test_expect_success 'sparse-index is not expanded' ' # Wildcard identifies only full sparse directories, no index expansion ensure_not_expanded reset deepest -- folder\* && + ensure_not_expanded clean -fd && + ensure_not_expanded checkout -f update-deep && test_config -C sparse-index pull.twohead ort && ( @@ -1001,6 +1369,24 @@ test_expect_success 'sparse index is not expanded: diff' ' ensure_not_expanded diff --cached ' +test_expect_success 'sparse index is not expanded: update-index' ' + init_repos && + + deep_a_oid=$(git -C full-checkout rev-parse update-deep:deep/a) && + ensure_not_expanded update-index --cacheinfo 100644 $deep_a_oid deep/a && + + echo "test" >sparse-index/README.md && + echo "test2" >sparse-index/a && + rm -f sparse-index/deep/a && + + ensure_not_expanded update-index --add README.md && + ensure_not_expanded update-index a && + ensure_not_expanded update-index --remove deep/a && + + ensure_not_expanded reset --soft update-deep && + ensure_not_expanded update-index --add --remove --again +' + test_expect_success 'sparse index is not expanded: blame' ' init_repos && @@ -1023,6 +1409,27 @@ test_expect_success 'sparse index is not expanded: fetch/pull' ' ensure_not_expanded pull full base ' +test_expect_success 'sparse index is not expanded: read-tree' ' + init_repos && + + ensure_not_expanded checkout -b test-branch update-folder1 && + for MERGE_TREES in "base HEAD update-folder2" \ + "base HEAD rename-base" \ + "base update-folder2" \ + "base rename-base" \ + "update-folder2" + do + ensure_not_expanded read-tree -mu $MERGE_TREES && + ensure_not_expanded reset --hard || return 1 + done && + + rm -rf sparse-index/deep/deeper2 && + ensure_not_expanded add . && + ensure_not_expanded commit -m "test" && + + ensure_not_expanded read-tree --prefix=deep/deeper2 -u deepest +' + test_expect_success 'ls-files' ' init_repos && @@ -1057,30 +1464,27 @@ test_expect_success 'ls-files' ' test_cmp dense sparse && # Set up a strange condition of having a file edit - # outside of the sparse-checkout cone. This is just - # to verify that sparse-checkout and sparse-index - # behave the same in this case. + # outside of the sparse-checkout cone. We want to verify + # that all modes handle this the same, and detect the + # modification. write_script edit-content <<-\EOF && - mkdir folder1 && + mkdir -p folder1 && echo content >>folder1/a EOF - run_on_sparse ../edit-content && + run_on_all ../edit-content && - # ls-files does not currently notice modified files whose - # cache entries are marked SKIP_WORKTREE. This may change - # in the future, but here we test that sparse index does - # not accidentally create a change of behavior. - test_sparse_match git ls-files --modified && - test_must_be_empty sparse-checkout-out && - test_must_be_empty sparse-index-out && + test_all_match git ls-files --modified && git -C sparse-index ls-files --sparse --modified >sparse-index-out && - test_must_be_empty sparse-index-out && + cat >expect <<-\EOF && + folder1/a + EOF + test_cmp expect sparse-index-out && # Add folder1 to the sparse-checkout cone and # check that ls-files shows the expanded files. test_sparse_match git sparse-checkout add folder1 && - test_sparse_match git ls-files --modified && + test_all_match git ls-files --modified && test_all_match git ls-files && git -C sparse-index ls-files --sparse >actual && diff --git a/t/t1300-config.sh b/t/t1300-config.sh index 78359f1f4a..7dd9b325d9 100755 --- a/t/t1300-config.sh +++ b/t/t1300-config.sh @@ -2388,4 +2388,122 @@ test_expect_success '--get and --get-all with --fixed-value' ' test_must_fail git config --file=config --get-regexp --fixed-value fixed+ non-existent ' +test_expect_success 'includeIf.hasconfig:remote.*.url' ' + git init hasremoteurlTest && + test_when_finished "rm -rf hasremoteurlTest" && + + cat >include-this <<-\EOF && + [user] + this = this-is-included + EOF + cat >dont-include-that <<-\EOF && + [user] + that = that-is-not-included + EOF + cat >>hasremoteurlTest/.git/config <<-EOF && + [includeIf "hasconfig:remote.*.url:foourl"] + path = "$(pwd)/include-this" + [includeIf "hasconfig:remote.*.url:barurl"] + path = "$(pwd)/dont-include-that" + [remote "foo"] + url = foourl + EOF + + echo this-is-included >expect-this && + git -C hasremoteurlTest config --get user.this >actual-this && + test_cmp expect-this actual-this && + + test_must_fail git -C hasremoteurlTest config --get user.that +' + +test_expect_success 'includeIf.hasconfig:remote.*.url respects last-config-wins' ' + git init hasremoteurlTest && + test_when_finished "rm -rf hasremoteurlTest" && + + cat >include-two-three <<-\EOF && + [user] + two = included-config + three = included-config + EOF + cat >>hasremoteurlTest/.git/config <<-EOF && + [remote "foo"] + url = foourl + [user] + one = main-config + two = main-config + [includeIf "hasconfig:remote.*.url:foourl"] + path = "$(pwd)/include-two-three" + [user] + three = main-config + EOF + + echo main-config >expect-main-config && + echo included-config >expect-included-config && + + git -C hasremoteurlTest config --get user.one >actual && + test_cmp expect-main-config actual && + + git -C hasremoteurlTest config --get user.two >actual && + test_cmp expect-included-config actual && + + git -C hasremoteurlTest config --get user.three >actual && + test_cmp expect-main-config actual +' + +test_expect_success 'includeIf.hasconfig:remote.*.url globs' ' + git init hasremoteurlTest && + test_when_finished "rm -rf hasremoteurlTest" && + + printf "[user]\ndss = yes\n" >double-star-start && + printf "[user]\ndse = yes\n" >double-star-end && + printf "[user]\ndsm = yes\n" >double-star-middle && + printf "[user]\nssm = yes\n" >single-star-middle && + printf "[user]\nno = no\n" >no && + + cat >>hasremoteurlTest/.git/config <<-EOF && + [remote "foo"] + url = https://foo/bar/baz + [includeIf "hasconfig:remote.*.url:**/baz"] + path = "$(pwd)/double-star-start" + [includeIf "hasconfig:remote.*.url:**/nomatch"] + path = "$(pwd)/no" + [includeIf "hasconfig:remote.*.url:https:/**"] + path = "$(pwd)/double-star-end" + [includeIf "hasconfig:remote.*.url:nomatch:/**"] + path = "$(pwd)/no" + [includeIf "hasconfig:remote.*.url:https:/**/baz"] + path = "$(pwd)/double-star-middle" + [includeIf "hasconfig:remote.*.url:https:/**/nomatch"] + path = "$(pwd)/no" + [includeIf "hasconfig:remote.*.url:https://*/bar/baz"] + path = "$(pwd)/single-star-middle" + [includeIf "hasconfig:remote.*.url:https://*/baz"] + path = "$(pwd)/no" + EOF + + git -C hasremoteurlTest config --get user.dss && + git -C hasremoteurlTest config --get user.dse && + git -C hasremoteurlTest config --get user.dsm && + git -C hasremoteurlTest config --get user.ssm && + test_must_fail git -C hasremoteurlTest config --get user.no +' + +test_expect_success 'includeIf.hasconfig:remote.*.url forbids remote url in such included files' ' + git init hasremoteurlTest && + test_when_finished "rm -rf hasremoteurlTest" && + + cat >include-with-url <<-\EOF && + [remote "bar"] + url = barurl + EOF + cat >>hasremoteurlTest/.git/config <<-EOF && + [includeIf "hasconfig:remote.*.url:foourl"] + path = "$(pwd)/include-with-url" + EOF + + # test with any Git command + test_must_fail git -C hasremoteurlTest status 2>err && + grep "fatal: remote URLs cannot be configured in file directly or indirectly included by includeIf.hasconfig:remote.*.url" err +' + test_done diff --git a/t/t1405-main-ref-store.sh b/t/t1405-main-ref-store.sh index 1a3ee8845d..51f8291628 100755 --- a/t/t1405-main-ref-store.sh +++ b/t/t1405-main-ref-store.sh @@ -40,6 +40,12 @@ test_expect_success 'delete_refs(FOO, refs/tags/new-tag)' ' test_must_fail git rev-parse refs/tags/new-tag -- ' +# In reftable, we keep the reflogs around for deleted refs. +test_expect_success !REFFILES 'delete-reflog(FOO, refs/tags/new-tag)' ' + $RUN delete-reflog FOO && + $RUN delete-reflog refs/tags/new-tag +' + test_expect_success 'rename_refs(main, new-main)' ' git rev-parse main >expected && $RUN rename-ref refs/heads/main refs/heads/new-main && @@ -105,7 +111,7 @@ test_expect_success 'delete_reflog(HEAD)' ' test_must_fail git reflog exists HEAD ' -test_expect_success 'create-reflog(HEAD)' ' +test_expect_success REFFILES 'create-reflog(HEAD)' ' $RUN create-reflog HEAD && git reflog exists HEAD ' diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh index d7ddf7612d..ea8e6ac2a0 100755 --- a/t/t1410-reflog.sh +++ b/t/t1410-reflog.sh @@ -341,7 +341,7 @@ test_expect_success 'stale dirs do not cause d/f conflicts (reflogs off)' ' # Each line is 114 characters, so we need 75 to still have a few before the # last 8K. The 89-character padding on the final entry lines up our # newline exactly. -test_expect_success SHA1 'parsing reverse reflogs at BUFSIZ boundaries' ' +test_expect_success REFFILES,SHA1 'parsing reverse reflogs at BUFSIZ boundaries' ' git checkout -b reflogskip && zf=$(test_oid zero_2) && ident="abc <xyz> 0000000001 +0000" && @@ -418,8 +418,18 @@ test_expect_success 'expire with multiple worktrees' ' test_commit -C link-wt foobar && test_tick && git reflog expire --verbose --all --expire=$test_tick && - test_must_be_empty .git/worktrees/link-wt/logs/HEAD + test-tool ref-store worktree:link-wt for-each-reflog-ent HEAD >actual && + test_must_be_empty actual ) ' +test_expect_success REFFILES 'empty reflog' ' + test_when_finished "rm -rf empty" && + git init empty && + test_commit -C empty A && + >empty/.git/logs/refs/heads/foo && + git -C empty reflog expire --all 2>err && + test_must_be_empty err +' + test_done diff --git a/t/t1416-ref-transaction-hooks.sh b/t/t1416-ref-transaction-hooks.sh index 6c941027a8..4e1e84a91f 100755 --- a/t/t1416-ref-transaction-hooks.sh +++ b/t/t1416-ref-transaction-hooks.sh @@ -136,4 +136,54 @@ test_expect_success 'interleaving hook calls succeed' ' test_cmp expect target-repo.git/actual ' +test_expect_success 'hook does not get called on packing refs' ' + # Pack references first such that we are in a known state. + git pack-refs --all && + + write_script .git/hooks/reference-transaction <<-\EOF && + echo "$@" >>actual + cat >>actual + EOF + rm -f actual && + + git update-ref refs/heads/unpacked-ref $POST_OID && + git pack-refs --all && + + # We only expect a single hook invocation, which is the call to + # git-update-ref(1). + cat >expect <<-EOF && + prepared + $ZERO_OID $POST_OID refs/heads/unpacked-ref + committed + $ZERO_OID $POST_OID refs/heads/unpacked-ref + EOF + + test_cmp expect actual +' + +test_expect_success 'deleting packed ref calls hook once' ' + # Create a reference and pack it. + git update-ref refs/heads/to-be-deleted $POST_OID && + git pack-refs --all && + + write_script .git/hooks/reference-transaction <<-\EOF && + echo "$@" >>actual + cat >>actual + EOF + rm -f actual && + + git update-ref -d refs/heads/to-be-deleted $POST_OID && + + # We only expect a single hook invocation, which is the logical + # deletion. + cat >expect <<-EOF && + prepared + $POST_OID $ZERO_OID refs/heads/to-be-deleted + committed + $POST_OID $ZERO_OID refs/heads/to-be-deleted + EOF + + test_cmp expect actual +' + test_done diff --git a/t/t1503-rev-parse-verify.sh b/t/t1503-rev-parse-verify.sh index 94fe413ee3..ba43168d12 100755 --- a/t/t1503-rev-parse-verify.sh +++ b/t/t1503-rev-parse-verify.sh @@ -132,8 +132,9 @@ test_expect_success 'use --default' ' test_must_fail git rev-parse --verify --default bar ' -test_expect_success 'main@{n} for various n' ' - N=$(git reflog | wc -l) && +test_expect_success !SANITIZE_LEAK 'main@{n} for various n' ' + git reflog >out && + N=$(wc -l <out) && Nm1=$(($N-1)) && Np1=$(($N+1)) && git rev-parse --verify main@{0} && diff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh index b0119bf8bc..98cefe3b70 100755 --- a/t/t1512-rev-parse-disambiguation.sh +++ b/t/t1512-rev-parse-disambiguation.sh @@ -25,6 +25,87 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME . ./test-lib.sh +test_cmp_failed_rev_parse () { + dir=$1 + rev=$2 + + cat >expect && + test_must_fail git -C "$dir" rev-parse "$rev" 2>actual.raw && + sed "s/\($rev\)[0-9a-f]*/\1.../" <actual.raw >actual && + test_cmp expect actual +} + +test_expect_success 'ambiguous blob output' ' + git init --bare blob.prefix && + ( + cd blob.prefix && + + # Both start with "dead..", under both SHA-1 and SHA-256 + echo brocdnra | git hash-object -w --stdin && + echo brigddsv | git hash-object -w --stdin && + + # Both start with "beef.." + echo 1agllotbh | git hash-object -w --stdin && + echo 1bbfctrkc | git hash-object -w --stdin + ) && + + test_must_fail git -C blob.prefix rev-parse dead && + test_cmp_failed_rev_parse blob.prefix beef <<-\EOF + error: short object ID beef... is ambiguous + hint: The candidates are: + hint: beef... blob + hint: beef... blob + fatal: ambiguous argument '\''beef...'\'': unknown revision or path not in the working tree. + Use '\''--'\'' to separate paths from revisions, like this: + '\''git <command> [<revision>...] -- [<file>...]'\'' + EOF +' + +test_expect_success 'ambiguous loose bad object parsed as OBJ_BAD' ' + git init --bare blob.bad && + ( + cd blob.bad && + + # Both have the prefix "bad0" + echo xyzfaowcoh | git hash-object -t bad -w --stdin --literally && + echo xyzhjpyvwl | git hash-object -t bad -w --stdin --literally + ) && + + test_cmp_failed_rev_parse blob.bad bad0 <<-\EOF + error: short object ID bad0... is ambiguous + fatal: invalid object type + EOF +' + +test_expect_success POSIXPERM 'ambigous zlib corrupt loose blob' ' + git init --bare blob.corrupt && + ( + cd blob.corrupt && + + # Both have the prefix "cafe" + echo bnkxmdwz | git hash-object -w --stdin && + oid=$(echo bmwsjxzi | git hash-object -w --stdin) && + + oidf=objects/$(test_oid_to_path "$oid") && + chmod 755 $oidf && + echo broken >$oidf + ) && + + test_cmp_failed_rev_parse blob.corrupt cafe <<-\EOF + error: short object ID cafe... is ambiguous + error: inflate: data stream error (incorrect header check) + error: unable to unpack cafe... header + error: inflate: data stream error (incorrect header check) + error: unable to unpack cafe... header + hint: The candidates are: + hint: cafe... [bad object] + hint: cafe... blob + fatal: ambiguous argument '\''cafe...'\'': unknown revision or path not in the working tree. + Use '\''--'\'' to separate paths from revisions, like this: + '\''git <command> [<revision>...] -- [<file>...]'\'' + EOF +' + if ! test_have_prereq SHA1 then skip_all='not using SHA-1 for objects' diff --git a/t/t1800-hook.sh b/t/t1800-hook.sh new file mode 100755 index 0000000000..29718aa991 --- /dev/null +++ b/t/t1800-hook.sh @@ -0,0 +1,134 @@ +#!/bin/sh + +test_description='git-hook command' + +TEST_PASSES_SANITIZE_LEAK=true +. ./test-lib.sh + +test_expect_success 'git hook usage' ' + test_expect_code 129 git hook && + test_expect_code 129 git hook run && + test_expect_code 129 git hook run -h && + test_expect_code 129 git hook run --unknown 2>err && + grep "unknown option" err +' + +test_expect_success 'git hook run: nonexistent hook' ' + cat >stderr.expect <<-\EOF && + error: cannot find a hook named test-hook + EOF + test_expect_code 1 git hook run test-hook 2>stderr.actual && + test_cmp stderr.expect stderr.actual +' + +test_expect_success 'git hook run: nonexistent hook with --ignore-missing' ' + git hook run --ignore-missing does-not-exist 2>stderr.actual && + test_must_be_empty stderr.actual +' + +test_expect_success 'git hook run: basic' ' + write_script .git/hooks/test-hook <<-EOF && + echo Test hook + EOF + + cat >expect <<-\EOF && + Test hook + EOF + git hook run test-hook 2>actual && + test_cmp expect actual +' + +test_expect_success 'git hook run: stdout and stderr both write to our stderr' ' + write_script .git/hooks/test-hook <<-EOF && + echo >&1 Will end up on stderr + echo >&2 Will end up on stderr + EOF + + cat >stderr.expect <<-\EOF && + Will end up on stderr + Will end up on stderr + EOF + git hook run test-hook >stdout.actual 2>stderr.actual && + test_cmp stderr.expect stderr.actual && + test_must_be_empty stdout.actual +' + +test_expect_success 'git hook run: exit codes are passed along' ' + write_script .git/hooks/test-hook <<-EOF && + exit 1 + EOF + + test_expect_code 1 git hook run test-hook && + + write_script .git/hooks/test-hook <<-EOF && + exit 2 + EOF + + test_expect_code 2 git hook run test-hook && + + write_script .git/hooks/test-hook <<-EOF && + exit 128 + EOF + + test_expect_code 128 git hook run test-hook && + + write_script .git/hooks/test-hook <<-EOF && + exit 129 + EOF + + test_expect_code 129 git hook run test-hook +' + +test_expect_success 'git hook run arg u ments without -- is not allowed' ' + test_expect_code 129 git hook run test-hook arg u ments +' + +test_expect_success 'git hook run -- pass arguments' ' + write_script .git/hooks/test-hook <<-\EOF && + echo $1 + echo $2 + EOF + + cat >expect <<-EOF && + arg + u ments + EOF + + git hook run test-hook -- arg "u ments" 2>actual && + test_cmp expect actual +' + +test_expect_success 'git hook run -- out-of-repo runs excluded' ' + write_script .git/hooks/test-hook <<-EOF && + echo Test hook + EOF + + nongit test_must_fail git hook run test-hook +' + +test_expect_success 'git -c core.hooksPath=<PATH> hook run' ' + mkdir my-hooks && + write_script my-hooks/test-hook <<-\EOF && + echo Hook ran $1 >>actual + EOF + + cat >expect <<-\EOF && + Test hook + Hook ran one + Hook ran two + Hook ran three + Hook ran four + EOF + + # Test various ways of specifying the path. See also + # t1350-config-hooks-path.sh + >actual && + git hook run test-hook -- ignored 2>>actual && + git -c core.hooksPath=my-hooks hook run test-hook -- one 2>>actual && + git -c core.hooksPath=my-hooks/ hook run test-hook -- two 2>>actual && + git -c core.hooksPath="$PWD/my-hooks" hook run test-hook -- three 2>>actual && + git -c core.hooksPath="$PWD/my-hooks/" hook run test-hook -- four 2>>actual && + test_cmp expect actual +' + +test_done diff --git a/t/t2012-checkout-last.sh b/t/t2012-checkout-last.sh index 42601d5a31..1f6c4ed042 100755 --- a/t/t2012-checkout-last.sh +++ b/t/t2012-checkout-last.sh @@ -21,14 +21,20 @@ test_expect_success 'first branch switch' ' git checkout other ' +test_cmp_symbolic_HEAD_ref () { + echo refs/heads/"$1" >expect && + git symbolic-ref HEAD >actual && + test_cmp expect actual +} + test_expect_success '"checkout -" switches back' ' git checkout - && - test "z$(git symbolic-ref HEAD)" = "zrefs/heads/main" + test_cmp_symbolic_HEAD_ref main ' test_expect_success '"checkout -" switches forth' ' git checkout - && - test "z$(git symbolic-ref HEAD)" = "zrefs/heads/other" + test_cmp_symbolic_HEAD_ref other ' test_expect_success 'detach HEAD' ' @@ -37,12 +43,16 @@ test_expect_success 'detach HEAD' ' test_expect_success '"checkout -" attaches again' ' git checkout - && - test "z$(git symbolic-ref HEAD)" = "zrefs/heads/other" + test_cmp_symbolic_HEAD_ref other ' test_expect_success '"checkout -" detaches again' ' git checkout - && - test "z$(git rev-parse HEAD)" = "z$(git rev-parse other)" && + + git rev-parse other >expect && + git rev-parse HEAD >actual && + test_cmp expect actual && + test_must_fail git symbolic-ref HEAD ' @@ -63,31 +73,31 @@ more_switches () { test_expect_success 'switch to the last' ' more_switches && git checkout @{-1} && - test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch2" + test_cmp_symbolic_HEAD_ref branch2 ' test_expect_success 'switch to second from the last' ' more_switches && git checkout @{-2} && - test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch3" + test_cmp_symbolic_HEAD_ref branch3 ' test_expect_success 'switch to third from the last' ' more_switches && git checkout @{-3} && - test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch4" + test_cmp_symbolic_HEAD_ref branch4 ' test_expect_success 'switch to fourth from the last' ' more_switches && git checkout @{-4} && - test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch5" + test_cmp_symbolic_HEAD_ref branch5 ' test_expect_success 'switch to twelfth from the last' ' more_switches && git checkout @{-12} && - test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch13" + test_cmp_symbolic_HEAD_ref branch13 ' test_expect_success 'merge base test setup' ' @@ -98,19 +108,28 @@ test_expect_success 'merge base test setup' ' test_expect_success 'another...main' ' git checkout another && git checkout another...main && - test "z$(git rev-parse --verify HEAD)" = "z$(git rev-parse --verify main^)" + + git rev-parse --verify main^ >expect && + git rev-parse --verify HEAD >actual && + test_cmp expect actual ' test_expect_success '...main' ' git checkout another && git checkout ...main && - test "z$(git rev-parse --verify HEAD)" = "z$(git rev-parse --verify main^)" + + git rev-parse --verify main^ >expect && + git rev-parse --verify HEAD >actual && + test_cmp expect actual ' test_expect_success 'main...' ' git checkout another && git checkout main... && - test "z$(git rev-parse --verify HEAD)" = "z$(git rev-parse --verify main^)" + + git rev-parse --verify main^ >expect && + git rev-parse --verify HEAD >actual && + test_cmp expect actual ' test_expect_success '"checkout -" works after a rebase A' ' @@ -118,7 +137,7 @@ test_expect_success '"checkout -" works after a rebase A' ' git checkout other && git rebase main && git checkout - && - test "z$(git symbolic-ref HEAD)" = "zrefs/heads/main" + test_cmp_symbolic_HEAD_ref main ' test_expect_success '"checkout -" works after a rebase A B' ' @@ -127,7 +146,7 @@ test_expect_success '"checkout -" works after a rebase A B' ' git checkout other && git rebase main moodle && git checkout - && - test "z$(git symbolic-ref HEAD)" = "zrefs/heads/main" + test_cmp_symbolic_HEAD_ref main ' test_expect_success '"checkout -" works after a rebase -i A' ' @@ -135,7 +154,7 @@ test_expect_success '"checkout -" works after a rebase -i A' ' git checkout other && git rebase -i main && git checkout - && - test "z$(git symbolic-ref HEAD)" = "zrefs/heads/main" + test_cmp_symbolic_HEAD_ref main ' test_expect_success '"checkout -" works after a rebase -i A B' ' @@ -144,7 +163,7 @@ test_expect_success '"checkout -" works after a rebase -i A B' ' git checkout other && git rebase main foodle && git checkout - && - test "z$(git symbolic-ref HEAD)" = "zrefs/heads/main" + test_cmp_symbolic_HEAD_ref main ' test_done diff --git a/t/t2060-switch.sh b/t/t2060-switch.sh index ebb961be29..5a7caf958c 100755 --- a/t/t2060-switch.sh +++ b/t/t2060-switch.sh @@ -32,6 +32,17 @@ test_expect_success 'switch and detach' ' test_must_fail git symbolic-ref HEAD ' +test_expect_success 'suggestion to detach' ' + test_must_fail git switch main^{commit} 2>stderr && + grep "try again with the --detach option" stderr +' + +test_expect_success 'suggestion to detach is suppressed with advice.suggestDetachingHead=false' ' + test_config advice.suggestDetachingHead false && + test_must_fail git switch main^{commit} 2>stderr && + ! grep "try again with the --detach option" stderr +' + test_expect_success 'switch and detach current branch' ' test_when_finished git switch main && git switch main && diff --git a/t/t2108-update-index-refresh-racy.sh b/t/t2108-update-index-refresh-racy.sh new file mode 100755 index 0000000000..bc5f2886fa --- /dev/null +++ b/t/t2108-update-index-refresh-racy.sh @@ -0,0 +1,64 @@ +#!/bin/sh + +test_description='update-index refresh tests related to racy timestamps' + +TEST_PASSES_SANITIZE_LEAK=true +. ./test-lib.sh + +reset_files () { + echo content >file && + echo content >other && + test_set_magic_mtime file && + test_set_magic_mtime other +} + +update_assert_changed () { + test_set_magic_mtime .git/index && + test_might_fail git update-index "$1" && + ! test_is_magic_mtime .git/index +} + +test_expect_success 'setup' ' + reset_files && + # we are calling reset_files() a couple of times during tests; + # test-tool chmtime does not change the ctime; to not weaken + # or even break our tests, disable ctime-checks entirely + git config core.trustctime false && + git add file other && + git commit -m "initial import" +' + +test_expect_success '--refresh has no racy timestamps to fix' ' + reset_files && + # set the index time far enough to the future; + # it must be at least 3 seconds for VFAT + test_set_magic_mtime .git/index +60 && + git update-index --refresh && + test_is_magic_mtime .git/index +60 +' + +test_expect_success '--refresh should fix racy timestamp' ' + reset_files && + update_assert_changed --refresh +' + +test_expect_success '--really-refresh should fix racy timestamp' ' + reset_files && + update_assert_changed --really-refresh +' + +test_expect_success '--refresh should fix racy timestamp if other file needs update' ' + reset_files && + echo content2 >other && + test_set_magic_mtime other && + update_assert_changed --refresh +' + +test_expect_success '--refresh should fix racy timestamp if racy file needs update' ' + reset_files && + echo content2 >file && + test_set_magic_mtime file && + update_assert_changed --refresh +' + +test_done diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh index acd3650d3c..0c38f8e356 100755 --- a/t/t2200-add-update.sh +++ b/t/t2200-add-update.sh @@ -14,7 +14,6 @@ only the updates to dir/sub. Also tested are "git add -u" without limiting, and "git add -u" without contents changes, and other conditions' -TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success setup ' @@ -41,20 +40,28 @@ test_expect_success update ' ' test_expect_success 'update noticed a removal' ' - test "$(git ls-files dir1/sub1)" = "" + git ls-files dir1/sub1 >out && + test_must_be_empty out ' test_expect_success 'update touched correct path' ' - test "$(git diff-files --name-status dir2/sub3)" = "" + git diff-files --name-status dir2/sub3 >out && + test_must_be_empty out ' test_expect_success 'update did not touch other tracked files' ' - test "$(git diff-files --name-status check)" = "M check" && - test "$(git diff-files --name-status top)" = "M top" + echo "M check" >expect && + git diff-files --name-status check >actual && + test_cmp expect actual && + + echo "M top" >expect && + git diff-files --name-status top >actual && + test_cmp expect actual ' test_expect_success 'update did not touch untracked files' ' - test "$(git ls-files dir2/other)" = "" + git ls-files dir2/other >out && + test_must_be_empty out ' test_expect_success 'cache tree has not been corrupted' ' @@ -76,9 +83,8 @@ test_expect_success 'update from a subdirectory' ' ' test_expect_success 'change gets noticed' ' - - test "$(git diff-files --name-status dir1)" = "" - + git diff-files --name-status dir1 >out && + test_must_be_empty out ' test_expect_success 'non-qualified update in subdir updates from the root' ' @@ -103,7 +109,8 @@ test_expect_success 'replace a file with a symlink' ' test_expect_success 'add everything changed' ' git add -u && - test -z "$(git diff-files)" + git diff-files >out && + test_must_be_empty out ' @@ -111,7 +118,8 @@ test_expect_success 'touch and then add -u' ' touch check && git add -u && - test -z "$(git diff-files)" + git diff-files >out && + test_must_be_empty out ' @@ -119,7 +127,8 @@ test_expect_success 'touch and then add explicitly' ' touch check && git add check && - test -z "$(git diff-files)" + git diff-files >out && + test_must_be_empty out ' diff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh index 37ad79470f..43139af08f 100755 --- a/t/t2400-worktree-add.sh +++ b/t/t2400-worktree-add.sh @@ -165,8 +165,62 @@ test_expect_success '"add" default branch of a bare repo' ' ( git clone --bare . bare2 && cd bare2 && - git worktree add ../there3 main - ) + git worktree add ../there3 main && + cd ../there3 && + # Simple check that a Git command does not + # immediately fail with the current setup + git status + ) && + cat >expect <<-EOF && + init.t + EOF + ls there3 >actual && + test_cmp expect actual +' + +test_expect_success '"add" to bare repo with worktree config' ' + ( + git clone --bare . bare3 && + cd bare3 && + git config extensions.worktreeconfig true && + + # Add config values that are erroneous to have in + # a config.worktree file outside of the main + # working tree, to check that Git filters them out + # when copying config during "git worktree add". + git config --worktree core.bare true && + git config --worktree core.worktree "$(pwd)" && + + # We want to check that bogus.key is copied + git config --worktree bogus.key value && + git config --unset core.bare && + git worktree add ../there4 main && + cd ../there4 && + + # Simple check that a Git command does not + # immediately fail with the current setup + git status && + git worktree add --detach ../there5 && + cd ../there5 && + git status + ) && + + # the worktree has the arbitrary value copied. + test_cmp_config -C there4 value bogus.key && + test_cmp_config -C there5 value bogus.key && + + # however, core.bare and core.worktree were removed. + test_must_fail git -C there4 config core.bare && + test_must_fail git -C there4 config core.worktree && + + cat >expect <<-EOF && + init.t + EOF + + ls there4 >actual && + test_cmp expect actual && + ls there5 >actual && + test_cmp expect actual ' test_expect_success 'checkout with grafts' ' diff --git a/t/t3007-ls-files-recurse-submodules.sh b/t/t3007-ls-files-recurse-submodules.sh index 4a08000713..dd7770e85d 100755 --- a/t/t3007-ls-files-recurse-submodules.sh +++ b/t/t3007-ls-files-recurse-submodules.sh @@ -34,6 +34,23 @@ test_expect_success 'ls-files correctly outputs files in submodule' ' test_cmp expect actual ' +test_expect_success '--stage' ' + GITMODULES_HASH=$(git rev-parse HEAD:.gitmodules) && + A_HASH=$(git rev-parse HEAD:a) && + B_HASH=$(git rev-parse HEAD:b/b) && + C_HASH=$(git -C submodule rev-parse HEAD:c) && + + cat >expect <<-EOF && + 100644 $GITMODULES_HASH 0 .gitmodules + 100644 $A_HASH 0 a + 100644 $B_HASH 0 b/b + 100644 $C_HASH 0 submodule/c + EOF + + git ls-files --stage --recurse-submodules >actual && + test_cmp expect actual +' + test_expect_success 'ls-files correctly outputs files in submodule with -z' ' lf_to_nul >expect <<-\EOF && .gitmodules @@ -292,7 +309,6 @@ test_incompatible_with_recurse_submodules () { test_incompatible_with_recurse_submodules --deleted test_incompatible_with_recurse_submodules --modified test_incompatible_with_recurse_submodules --others -test_incompatible_with_recurse_submodules --stage test_incompatible_with_recurse_submodules --killed test_incompatible_with_recurse_submodules --unmerged diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index 1bc3795847..7a0ff75ba8 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -42,6 +42,23 @@ test_expect_success 'git branch abc should create a branch' ' git branch abc && test_path_is_file .git/refs/heads/abc ' +test_expect_success 'git branch abc should fail when abc exists' ' + test_must_fail git branch abc +' + +test_expect_success 'git branch --force abc should fail when abc is checked out' ' + test_when_finished git switch main && + git switch abc && + test_must_fail git branch --force abc HEAD~1 +' + +test_expect_success 'git branch --force abc should succeed when abc exists' ' + git rev-parse HEAD~1 >expect && + git branch --force abc HEAD~1 && + git rev-parse abc >actual && + test_cmp expect actual +' + test_expect_success 'git branch a/b/c should create a branch' ' git branch a/b/c && test_path_is_file .git/refs/heads/a/b/c ' diff --git a/t/t3207-branch-submodule.sh b/t/t3207-branch-submodule.sh new file mode 100755 index 0000000000..0d93f7516c --- /dev/null +++ b/t/t3207-branch-submodule.sh @@ -0,0 +1,292 @@ +#!/bin/sh + +test_description='git branch submodule tests' + +GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main +export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME + +. ./test-lib.sh +. "$TEST_DIRECTORY"/lib-rebase.sh + +pwd=$(pwd) + +# Creates a clean test environment in "pwd" by copying the repo setup +# from test_dirs. +reset_test () { + rm -fr super && + rm -fr sub-sub-upstream && + rm -fr sub-upstream && + cp -r test_dirs/* . +} + +# Tests that the expected branch does not exist +test_no_branch () { + DIR=$1 && + BRANCH_NAME=$2 && + test_must_fail git -C "$DIR" rev-parse "$BRANCH_NAME" 2>err && + grep "ambiguous argument .$BRANCH_NAME." err +} + +test_expect_success 'setup superproject and submodule' ' + mkdir test_dirs && + ( + cd test_dirs && + git init super && + test_commit -C super foo && + git init sub-sub-upstream && + test_commit -C sub-sub-upstream foo && + git init sub-upstream && + # Submodule in a submodule + git -C sub-upstream submodule add "${pwd}/test_dirs/sub-sub-upstream" sub-sub && + git -C sub-upstream commit -m "add submodule" && + # Regular submodule + git -C super submodule add "${pwd}/test_dirs/sub-upstream" sub && + # Submodule in a subdirectory + git -C super submodule add "${pwd}/test_dirs/sub-sub-upstream" second/sub && + git -C super commit -m "add submodule" && + git -C super config submodule.propagateBranches true && + git -C super/sub submodule update --init + ) && + reset_test +' + +# Test the argument parsing +test_expect_success '--recurse-submodules should create branches' ' + test_when_finished "reset_test" && + ( + cd super && + git branch --recurse-submodules branch-a && + git rev-parse branch-a && + git -C sub rev-parse branch-a && + git -C sub/sub-sub rev-parse branch-a && + git -C second/sub rev-parse branch-a + ) +' + +test_expect_success '--recurse-submodules should die if submodule.propagateBranches is false' ' + test_when_finished "reset_test" && + ( + cd super && + echo "fatal: branch with --recurse-submodules can only be used if submodule.propagateBranches is enabled" >expected && + test_must_fail git -c submodule.propagateBranches=false branch --recurse-submodules branch-a 2>actual && + test_cmp expected actual + ) +' + +test_expect_success '--recurse-submodules should fail when not creating branches' ' + test_when_finished "reset_test" && + ( + cd super && + git branch --recurse-submodules branch-a && + echo "fatal: --recurse-submodules can only be used to create branches" >expected && + test_must_fail git branch --recurse-submodules -D branch-a 2>actual && + test_cmp expected actual && + # Assert that the branches were not deleted + git rev-parse branch-a && + git -C sub rev-parse branch-a + ) +' + +test_expect_success 'should respect submodule.recurse when creating branches' ' + test_when_finished "reset_test" && + ( + cd super && + git -c submodule.recurse=true branch branch-a && + git rev-parse branch-a && + git -C sub rev-parse branch-a + ) +' + +test_expect_success 'should ignore submodule.recurse when not creating branches' ' + test_when_finished "reset_test" && + ( + cd super && + git branch --recurse-submodules branch-a && + git -c submodule.recurse=true branch -D branch-a && + test_no_branch . branch-a && + git -C sub rev-parse branch-a + ) +' + +# Test branch creation behavior +test_expect_success 'should create branches based off commit id in superproject' ' + test_when_finished "reset_test" && + ( + cd super && + git branch --recurse-submodules branch-a && + git checkout --recurse-submodules branch-a && + git -C sub rev-parse HEAD >expected && + # Move the tip of sub:branch-a so that it no longer matches the commit in super:branch-a + git -C sub checkout branch-a && + test_commit -C sub bar && + # Create a new branch-b branch with start-point=branch-a + git branch --recurse-submodules branch-b branch-a && + git rev-parse branch-b && + git -C sub rev-parse branch-b >actual && + # Assert that the commit id of sub:second-branch matches super:branch-a and not sub:branch-a + test_cmp expected actual + ) +' + +test_expect_success 'should not create any branches if branch is not valid for all repos' ' + test_when_finished "reset_test" && + ( + cd super && + git -C sub branch branch-a && + test_must_fail git branch --recurse-submodules branch-a 2>actual && + test_no_branch . branch-a && + grep "submodule .sub.: fatal: a branch named .branch-a. already exists" actual + ) +' + +test_expect_success 'should create branches if branch exists and --force is given' ' + test_when_finished "reset_test" && + ( + cd super && + git -C sub rev-parse HEAD >expected && + test_commit -C sub baz && + # branch-a in sub now points to a newer commit. + git -C sub branch branch-a HEAD && + git -C sub rev-parse branch-a >actual-old-branch-a && + git branch --recurse-submodules --force branch-a && + git rev-parse branch-a && + git -C sub rev-parse branch-a >actual-new-branch-a && + test_cmp expected actual-new-branch-a && + # assert that branch --force actually moved the sub + # branch + ! test_cmp expected actual-old-branch-a + ) +' + +test_expect_success 'should create branch when submodule is not in HEAD:.gitmodules' ' + test_when_finished "reset_test" && + ( + cd super && + git branch branch-a && + git checkout -b branch-b && + git submodule add ../sub-upstream sub2 && + git -C sub2 submodule update --init && + # branch-b now has a committed submodule not in branch-a + git commit -m "add second submodule" && + git checkout branch-a && + git branch --recurse-submodules branch-c branch-b && + git checkout --recurse-submodules branch-c && + git -C sub2 rev-parse branch-c && + git -C sub2/sub-sub rev-parse branch-c + ) +' + +test_expect_success 'should not create branches in inactive submodules' ' + test_when_finished "reset_test" && + test_config -C super submodule.sub.active false && + ( + cd super && + git branch --recurse-submodules branch-a && + git rev-parse branch-a && + test_no_branch sub branch-a + ) +' + +test_expect_success 'should set up tracking of local branches with track=always' ' + test_when_finished "reset_test" && + ( + cd super && + git -c branch.autoSetupMerge=always branch --recurse-submodules branch-a main && + git -C sub rev-parse main && + test_cmp_config -C sub . branch.branch-a.remote && + test_cmp_config -C sub refs/heads/main branch.branch-a.merge + ) +' + +test_expect_success 'should set up tracking of local branches with explicit track' ' + test_when_finished "reset_test" && + ( + cd super && + git branch --track --recurse-submodules branch-a main && + git -C sub rev-parse main && + test_cmp_config -C sub . branch.branch-a.remote && + test_cmp_config -C sub refs/heads/main branch.branch-a.merge + ) +' + +test_expect_success 'should not set up unnecessary tracking of local branches' ' + test_when_finished "reset_test" && + ( + cd super && + git branch --recurse-submodules branch-a main && + git -C sub rev-parse main && + test_cmp_config -C sub "" --default "" branch.branch-a.remote && + test_cmp_config -C sub "" --default "" branch.branch-a.merge + ) +' + +reset_remote_test () { + rm -fr super-clone && + reset_test +} + +test_expect_success 'setup tests with remotes' ' + ( + cd test_dirs && + ( + cd super && + git branch branch-a && + git checkout -b branch-b && + git submodule add ../sub-upstream sub2 && + # branch-b now has a committed submodule not in branch-a + git commit -m "add second submodule" + ) && + git clone --branch main --recurse-submodules super super-clone && + git -C super-clone config submodule.propagateBranches true + ) && + reset_remote_test +' + +test_expect_success 'should get fatal error upon branch creation when submodule is not in .git/modules' ' + test_when_finished "reset_remote_test" && + ( + cd super-clone && + # This should succeed because super-clone has sub in .git/modules + git branch --recurse-submodules branch-a origin/branch-a && + # This should fail because super-clone does not have sub2 .git/modules + test_must_fail git branch --recurse-submodules branch-b origin/branch-b 2>actual && + grep "fatal: submodule .sub2.: unable to find submodule" actual && + test_no_branch . branch-b && + test_no_branch sub branch-b && + # User can fix themselves by initializing the submodule + git checkout origin/branch-b && + git submodule update --init --recursive && + git branch --recurse-submodules branch-b origin/branch-b + ) +' + +test_expect_success 'should set up tracking of remote-tracking branches' ' + test_when_finished "reset_remote_test" && + ( + cd super-clone && + git branch --recurse-submodules branch-a origin/branch-a && + test_cmp_config origin branch.branch-a.remote && + test_cmp_config refs/heads/branch-a branch.branch-a.merge && + # "origin/branch-a" does not exist for "sub", but it matches the refspec + # so tracking should be set up + test_cmp_config -C sub origin branch.branch-a.remote && + test_cmp_config -C sub refs/heads/branch-a branch.branch-a.merge && + test_cmp_config -C sub/sub-sub origin branch.branch-a.remote && + test_cmp_config -C sub/sub-sub refs/heads/branch-a branch.branch-a.merge + ) +' + +test_expect_success 'should not fail when unable to set up tracking in submodule' ' + test_when_finished "reset_remote_test" && + ( + cd super-clone && + git remote rename origin ex-origin && + git branch --recurse-submodules branch-a ex-origin/branch-a && + test_cmp_config ex-origin branch.branch-a.remote && + test_cmp_config refs/heads/branch-a branch.branch-a.merge && + test_cmp_config -C sub "" --default "" branch.branch-a.remote && + test_cmp_config -C sub "" --default "" branch.branch-a.merge + ) +' + +test_done diff --git a/t/t3302-notes-index-expensive.sh b/t/t3302-notes-index-expensive.sh index bc9d8ee1e6..bb5fea02a0 100755 --- a/t/t3302-notes-index-expensive.sh +++ b/t/t3302-notes-index-expensive.sh @@ -8,7 +8,6 @@ test_description='Test commit notes index (expensive!)' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME -TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh create_repo () { @@ -65,7 +64,8 @@ create_repo () { test_notes () { count=$1 && git config core.notesRef refs/notes/commits && - git log | grep "^ " >output && + git log >tmp && + grep "^ " tmp >output && i=$count && while test $i -gt 0 do @@ -90,7 +90,7 @@ write_script time_notes <<\EOF unset GIT_NOTES_REF ;; esac - git log + git log || exit $? i=$(($i+1)) done >/dev/null EOF diff --git a/t/t3303-notes-subtrees.sh b/t/t3303-notes-subtrees.sh index 7e0a8960af..eac193757b 100755 --- a/t/t3303-notes-subtrees.sh +++ b/t/t3303-notes-subtrees.sh @@ -5,7 +5,6 @@ test_description='Test commit notes organized in subtrees' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME -TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh number_of_commits=100 @@ -79,7 +78,7 @@ test_sha1_based () { ( start_note_commit && nr=$number_of_commits && - git rev-list refs/heads/main | + git rev-list refs/heads/main >out && while read sha1; do note_path=$(echo "$sha1" | sed "$1") cat <<INPUT_END && @@ -91,9 +90,9 @@ EOF INPUT_END nr=$(($nr-1)) - done - ) | - git fast-import --quiet + done <out + ) >gfi && + git fast-import --quiet <gfi } test_expect_success 'test notes in 2/38-fanout' 'test_sha1_based "s|^..|&/|"' diff --git a/t/t3305-notes-fanout.sh b/t/t3305-notes-fanout.sh index 1f5964865a..9976d787f4 100755 --- a/t/t3305-notes-fanout.sh +++ b/t/t3305-notes-fanout.sh @@ -2,7 +2,6 @@ test_description='Test that adding/removing many notes triggers automatic fanout restructuring' -TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh path_has_fanout() { @@ -24,7 +23,7 @@ touched_one_note_with_fanout() { all_notes_have_fanout() { notes_commit=$1 && fanout=$2 && - git ls-tree -r --name-only $notes_commit 2>/dev/null | + git ls-tree -r --name-only $notes_commit | while read path do path_has_fanout $path $fanout || return 1 @@ -51,8 +50,9 @@ test_expect_success 'creating many notes with git-notes' ' done ' -test_expect_success 'many notes created correctly with git-notes' ' - git log | grep "^ " > output && +test_expect_success !SANITIZE_LEAK 'many notes created correctly with git-notes' ' + git log >output.raw && + grep "^ " output.raw >output && i=$num_notes && while test $i -gt 0 do @@ -91,13 +91,13 @@ test_expect_success 'stable fanout 0 is followed by stable fanout 1' ' test_expect_success 'deleting most notes with git-notes' ' remove_notes=285 && i=0 && - git rev-list HEAD | + git rev-list HEAD >revs && while test $i -lt $remove_notes && read sha1 do i=$(($i + 1)) && test_tick && - git notes remove "$sha1" 2>/dev/null || return 1 - done + git notes remove "$sha1" || return 1 + done <revs ' test_expect_success 'most notes deleted correctly with git-notes' ' diff --git a/t/t3406-rebase-message.sh b/t/t3406-rebase-message.sh index 77a313f62e..d17b450e81 100755 --- a/t/t3406-rebase-message.sh +++ b/t/t3406-rebase-message.sh @@ -105,6 +105,29 @@ test_expect_success 'GIT_REFLOG_ACTION' ' test_cmp expect actual ' +test_expect_success 'rebase --apply reflog' ' + git checkout -b reflog-apply start && + old_head_reflog="$(git log -g --format=%gs -1 HEAD)" && + + git rebase --apply Y && + + git log -g --format=%gs -4 HEAD >actual && + cat >expect <<-EOF && + rebase finished: returning to refs/heads/reflog-apply + rebase: Z + rebase: checkout Y + $old_head_reflog + EOF + test_cmp expect actual && + + git log -g --format=%gs -2 reflog-apply >actual && + cat >expect <<-EOF && + rebase finished: refs/heads/reflog-apply onto $(git rev-parse Y) + branch: Created from start + EOF + test_cmp expect actual +' + test_expect_success 'rebase -i onto unrelated history' ' git init unrelated && test_commit -C unrelated 1 && diff --git a/t/t3412-rebase-root.sh b/t/t3412-rebase-root.sh index 19c6f4acbf..1e9f7833dd 100755 --- a/t/t3412-rebase-root.sh +++ b/t/t3412-rebase-root.sh @@ -11,7 +11,7 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME log_with_names () { git rev-list --topo-order --parents --pretty="tformat:%s" HEAD | - git name-rev --stdin --name-only --refs=refs/heads/$1 + git name-rev --annotate-stdin --name-only --refs=refs/heads/$1 } diff --git a/t/t3418-rebase-continue.sh b/t/t3418-rebase-continue.sh index 22eca73aa3..130e2f9b55 100755 --- a/t/t3418-rebase-continue.sh +++ b/t/t3418-rebase-continue.sh @@ -308,4 +308,30 @@ test_expect_success 'there is no --no-reschedule-failed-exec in an ongoing rebas test_expect_code 129 git rebase --edit-todo --no-reschedule-failed-exec ' +test_orig_head_helper () { + test_when_finished 'git rebase --abort && + git checkout topic && + git reset --hard commit-new-file-F2-on-topic-branch' && + git update-ref -d ORIG_HEAD && + test_must_fail git rebase "$@" && + test_cmp_rev ORIG_HEAD commit-new-file-F2-on-topic-branch +} + +test_orig_head () { + type=$1 + test_expect_success "rebase $type sets ORIG_HEAD correctly" ' + git checkout topic && + git reset --hard commit-new-file-F2-on-topic-branch && + test_orig_head_helper $type main + ' + + test_expect_success "rebase $type <upstream> <branch> sets ORIG_HEAD correctly" ' + git checkout main && + test_orig_head_helper $type main topic + ' +} + +test_orig_head --apply +test_orig_head --merge + test_done diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index 207714655f..94537a6b40 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -326,7 +326,9 @@ test_expect_success 'correct message when there is nothing to do' ' test_expect_success 'setup again' ' git reset --hard && test_chmod +x file && - echo content >>file + echo content >>file && + test_write_lines A B C D>file2 && + git add file2 ' # Write the patch file with a new line at the top and bottom @@ -341,13 +343,27 @@ test_expect_success 'setup patch' ' content +lastline \ No newline at end of file + diff --git a/file2 b/file2 + index 8422d40..35b930a 100644 + --- a/file2 + +++ b/file2 + @@ -1,4 +1,5 @@ + -A + +Z + B + +Y + C + -D + +X EOF ' # Expected output, diff is similar to the patch but w/ diff at the top test_expect_success 'setup expected' ' echo diff --git a/file b/file >expected && - cat patch |sed "/^index/s/ 100644/ 100755/" >>expected && + sed -e "/^index 180b47c/s/ 100644/ 100755/" \ + -e /1,5/s//1,4/ \ + -e /Y/d patch >>expected && cat >expected-output <<-\EOF --- a/file +++ b/file @@ -366,6 +382,28 @@ test_expect_success 'setup expected' ' content +lastline \ No newline at end of file + --- a/file2 + +++ b/file2 + @@ -1,4 +1,5 @@ + -A + +Z + B + +Y + C + -D + +X + @@ -1,2 +1,2 @@ + -A + +Z + B + @@ -2,2 +2,3 @@ + B + +Y + C + @@ -3,2 +4,2 @@ + C + -D + +X EOF ' @@ -373,9 +411,9 @@ test_expect_success 'setup expected' ' test_expect_success 'add first line works' ' git commit -am "clear local changes" && git apply patch && - printf "%s\n" s y y | git add -p file 2>error | - sed -n -e "s/^([1-2]\/[1-2]) Stage this hunk[^@]*\(@@ .*\)/\1/" \ - -e "/^[-+@ \\\\]"/p >output && + test_write_lines s y y s y n y | git add -p 2>error >raw-output && + sed -n -e "s/^([1-9]\/[1-9]) Stage this hunk[^@]*\(@@ .*\)/\1/" \ + -e "/^[-+@ \\\\]"/p raw-output >output && test_must_be_empty error && git diff --cached >diff && diff_cmp expected diff && diff --git a/t/t3705-add-sparse-checkout.sh b/t/t3705-add-sparse-checkout.sh index 81f3384eee..95609046c6 100755 --- a/t/t3705-add-sparse-checkout.sh +++ b/t/t3705-add-sparse-checkout.sh @@ -19,6 +19,7 @@ setup_sparse_entry () { fi && git add sparse_entry && git update-index --skip-worktree sparse_entry && + git config core.sparseCheckout false && git commit --allow-empty -m "ensure sparse_entry exists at HEAD" && SPARSE_ENTRY_BLOB=$(git rev-parse :sparse_entry) } @@ -126,6 +127,7 @@ test_expect_success 'git add --chmod does not update sparse entries' ' ' test_expect_success 'git add --renormalize does not update sparse entries' ' + test_when_finished rm .gitattributes && test_config core.autocrlf false && setup_sparse_entry "LINEONE\r\nLINETWO\r\n" && echo "sparse_entry text=auto" >.gitattributes && diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh index 686747e55a..42638b11d8 100755 --- a/t/t3903-stash.sh +++ b/t/t3903-stash.sh @@ -41,7 +41,7 @@ diff_cmp () { rm -f "$1.compare" "$2.compare" } -test_expect_success 'stash some dirty working directory' ' +setup_stash() { echo 1 >file && git add file && echo unrelated >other-file && @@ -55,6 +55,10 @@ test_expect_success 'stash some dirty working directory' ' git stash && git diff-files --quiet && git diff-index --cached --quiet HEAD +} + +test_expect_success 'stash some dirty working directory' ' + setup_stash ' cat >expect <<EOF @@ -185,6 +189,43 @@ test_expect_success 'drop middle stash by index' ' test 1 = $(git show HEAD:file) ' +test_expect_success 'drop stash reflog updates refs/stash' ' + git reset --hard && + git rev-parse refs/stash >expect && + echo 9 >file && + git stash && + git stash drop stash@{0} && + git rev-parse refs/stash >actual && + test_cmp expect actual +' + +test_expect_success REFFILES 'drop stash reflog updates refs/stash with rewrite' ' + git init repo && + ( + cd repo && + setup_stash + ) && + echo 9 >repo/file && + + old_oid="$(git -C repo rev-parse stash@{0})" && + git -C repo stash && + new_oid="$(git -C repo rev-parse stash@{0})" && + + cat >expect <<-EOF && + $(test_oid zero) $old_oid + $old_oid $new_oid + EOF + cut -d" " -f1-2 repo/.git/logs/refs/stash >actual && + test_cmp expect actual && + + git -C repo stash drop stash@{1} && + cut -d" " -f1-2 repo/.git/logs/refs/stash >actual && + cat >expect <<-EOF && + $(test_oid zero) $new_oid + EOF + test_cmp expect actual +' + test_expect_success 'stash pop' ' git reset --hard && git stash pop && @@ -390,10 +431,11 @@ test_expect_success SYMLINKS 'stash file to symlink' ' rm file && ln -s file2 file && git stash save "file to symlink" && - test -f file && + test_path_is_file_not_symlink file && test bar = "$(cat file)" && git stash apply && - case "$(ls -l file)" in *" file -> file2") :;; *) false;; esac + test_path_is_symlink file && + test "$(test_readlink file)" = file2 ' test_expect_success SYMLINKS 'stash file to symlink (stage rm)' ' @@ -401,10 +443,11 @@ test_expect_success SYMLINKS 'stash file to symlink (stage rm)' ' git rm file && ln -s file2 file && git stash save "file to symlink (stage rm)" && - test -f file && + test_path_is_file_not_symlink file && test bar = "$(cat file)" && git stash apply && - case "$(ls -l file)" in *" file -> file2") :;; *) false;; esac + test_path_is_symlink file && + test "$(test_readlink file)" = file2 ' test_expect_success SYMLINKS 'stash file to symlink (full stage)' ' @@ -413,10 +456,11 @@ test_expect_success SYMLINKS 'stash file to symlink (full stage)' ' ln -s file2 file && git add file && git stash save "file to symlink (full stage)" && - test -f file && + test_path_is_file_not_symlink file && test bar = "$(cat file)" && git stash apply && - case "$(ls -l file)" in *" file -> file2") :;; *) false;; esac + test_path_is_symlink file && + test "$(test_readlink file)" = file2 ' # This test creates a commit with a symlink used for the following tests @@ -487,7 +531,7 @@ test_expect_failure 'stash directory to file' ' rm -fr dir && echo bar >dir && git stash save "directory to file" && - test -d dir && + test_path_is_dir dir && test foo = "$(cat dir/file)" && test_must_fail git stash apply && test bar = "$(cat dir)" && @@ -500,10 +544,10 @@ test_expect_failure 'stash file to directory' ' mkdir file && echo foo >file/file && git stash save "file to directory" && - test -f file && + test_path_is_file file && test bar = "$(cat file)" && git stash apply && - test -f file/file && + test_path_is_file file/file && test foo = "$(cat file/file)" ' @@ -1042,6 +1086,17 @@ test_expect_success 'create stores correct message' ' test_cmp expect actual ' +test_expect_success 'create when branch name has /' ' + test_when_finished "git checkout main" && + git checkout -b some/topic && + >foo && + git add foo && + STASH_ID=$(git stash create "create test message") && + echo "On some/topic: create test message" >expect && + git show --pretty=%s -s ${STASH_ID} >actual && + test_cmp expect actual +' + test_expect_success 'create with multiple arguments for the message' ' >foo && git add foo && @@ -1272,7 +1327,6 @@ test_expect_success 'stash works when user.name and user.email are not set' ' >2 && git add 2 && test_config user.useconfigonly true && - test_config stash.usebuiltin true && ( sane_unset GIT_AUTHOR_NAME && sane_unset GIT_AUTHOR_EMAIL && @@ -1323,20 +1377,6 @@ test_expect_success 'stash handles skip-worktree entries nicely' ' git rev-parse --verify refs/stash:A.t ' -test_expect_success 'stash -c stash.useBuiltin=false warning ' ' - expected="stash.useBuiltin support has been removed" && - - git -c stash.useBuiltin=false stash 2>err && - test_i18ngrep "$expected" err && - env GIT_TEST_STASH_USE_BUILTIN=false git stash 2>err && - test_i18ngrep "$expected" err && - - git -c stash.useBuiltin=true stash 2>err && - test_must_be_empty err && - env GIT_TEST_STASH_USE_BUILTIN=true git stash 2>err && - test_must_be_empty err -' - test_expect_success 'git stash succeeds despite directory/file change' ' test_create_repo directory_file_switch_v1 && ( diff --git a/t/t4018/kotlin-class b/t/t4018/kotlin-class new file mode 100644 index 0000000000..bb864f22e6 --- /dev/null +++ b/t/t4018/kotlin-class @@ -0,0 +1,5 @@ +class RIGHT { + //comment + //comment + return ChangeMe +} diff --git a/t/t4018/kotlin-enum-class b/t/t4018/kotlin-enum-class new file mode 100644 index 0000000000..8885f908fd --- /dev/null +++ b/t/t4018/kotlin-enum-class @@ -0,0 +1,5 @@ +enum class RIGHT{ + // Left + // a comment + ChangeMe +} diff --git a/t/t4018/kotlin-fun b/t/t4018/kotlin-fun new file mode 100644 index 0000000000..2a60280256 --- /dev/null +++ b/t/t4018/kotlin-fun @@ -0,0 +1,5 @@ +fun RIGHT(){ + //a comment + //b comment + return ChangeMe() +} diff --git a/t/t4018/kotlin-inheritace-class b/t/t4018/kotlin-inheritace-class new file mode 100644 index 0000000000..77376c1f05 --- /dev/null +++ b/t/t4018/kotlin-inheritace-class @@ -0,0 +1,5 @@ +open class RIGHT{ + // a comment + // b comment + // ChangeMe +} diff --git a/t/t4018/kotlin-inline-class b/t/t4018/kotlin-inline-class new file mode 100644 index 0000000000..7bf46dd8d4 --- /dev/null +++ b/t/t4018/kotlin-inline-class @@ -0,0 +1,5 @@ +value class RIGHT(Args){ + // a comment + // b comment + ChangeMe +} diff --git a/t/t4018/kotlin-interface b/t/t4018/kotlin-interface new file mode 100644 index 0000000000..f686ba7770 --- /dev/null +++ b/t/t4018/kotlin-interface @@ -0,0 +1,5 @@ +interface RIGHT{ + //another comment + //another comment + //ChangeMe +} diff --git a/t/t4018/kotlin-nested-fun b/t/t4018/kotlin-nested-fun new file mode 100644 index 0000000000..12186858cb --- /dev/null +++ b/t/t4018/kotlin-nested-fun @@ -0,0 +1,9 @@ +class LEFT{ + class CENTER{ + fun RIGHT( a:Int){ + //comment + //comment + ChangeMe + } + } +} diff --git a/t/t4018/kotlin-public-class b/t/t4018/kotlin-public-class new file mode 100644 index 0000000000..9433fcc226 --- /dev/null +++ b/t/t4018/kotlin-public-class @@ -0,0 +1,5 @@ +public class RIGHT{ + //comment1 + //comment2 + ChangeMe +} diff --git a/t/t4018/kotlin-sealed-class b/t/t4018/kotlin-sealed-class new file mode 100644 index 0000000000..0efa4a4eaf --- /dev/null +++ b/t/t4018/kotlin-sealed-class @@ -0,0 +1,5 @@ +sealed class RIGHT { + // a comment + // b comment + ChangeMe +} diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh index 54bb8ef27e..1219f8bd4c 100755 --- a/t/t4020-diff-external.sh +++ b/t/t4020-diff-external.sh @@ -24,45 +24,38 @@ test_expect_success setup ' ' test_expect_success 'GIT_EXTERNAL_DIFF environment' ' - - GIT_EXTERNAL_DIFF=echo git diff | { - read path oldfile oldhex oldmode newfile newhex newmode && - test "z$path" = zfile && - test "z$oldmode" = z100644 && - test "z$newhex" = "z$ZERO_OID" && - test "z$newmode" = z100644 && - oh=$(git rev-parse --verify HEAD:file) && - test "z$oh" = "z$oldhex" - } + cat >expect <<-EOF && + file $(git rev-parse --verify HEAD:file) 100644 file $(test_oid zero) 100644 + EOF + GIT_EXTERNAL_DIFF=echo git diff >out && + cut -d" " -f1,3- <out >actual && + test_cmp expect actual ' -test_expect_success 'GIT_EXTERNAL_DIFF environment should apply only to diff' ' - - GIT_EXTERNAL_DIFF=echo git log -p -1 HEAD | - grep "^diff --git a/file b/file" +test_expect_success !SANITIZE_LEAK 'GIT_EXTERNAL_DIFF environment should apply only to diff' ' + GIT_EXTERNAL_DIFF=echo git log -p -1 HEAD >out && + grep "^diff --git a/file b/file" out ' test_expect_success 'GIT_EXTERNAL_DIFF environment and --no-ext-diff' ' - - GIT_EXTERNAL_DIFF=echo git diff --no-ext-diff | - grep "^diff --git a/file b/file" + GIT_EXTERNAL_DIFF=echo git diff --no-ext-diff >out && + grep "^diff --git a/file b/file" out ' test_expect_success SYMLINKS 'typechange diff' ' rm -f file && ln -s elif file && - GIT_EXTERNAL_DIFF=echo git diff | { - read path oldfile oldhex oldmode newfile newhex newmode && - test "z$path" = zfile && - test "z$oldmode" = z100644 && - test "z$newhex" = "z$ZERO_OID" && - test "z$newmode" = z120000 && - oh=$(git rev-parse --verify HEAD:file) && - test "z$oh" = "z$oldhex" - } && + + cat >expect <<-EOF && + file $(git rev-parse --verify HEAD:file) 100644 $(test_oid zero) 120000 + EOF + GIT_EXTERNAL_DIFF=echo git diff >out && + cut -d" " -f1,3-4,6- <out >actual && + test_cmp expect actual && + GIT_EXTERNAL_DIFF=echo git diff --no-ext-diff >actual && git diff >expect && test_cmp expect actual @@ -72,27 +65,25 @@ test_expect_success 'diff.external' ' git reset --hard && echo third >file && test_config diff.external echo && - git diff | { - read path oldfile oldhex oldmode newfile newhex newmode && - test "z$path" = zfile && - test "z$oldmode" = z100644 && - test "z$newhex" = "z$ZERO_OID" && - test "z$newmode" = z100644 && - oh=$(git rev-parse --verify HEAD:file) && - test "z$oh" = "z$oldhex" - } + + cat >expect <<-EOF && + file $(git rev-parse --verify HEAD:file) 100644 $(test_oid zero) 100644 + EOF + git diff >out && + cut -d" " -f1,3-4,6- <out >actual && + test_cmp expect actual ' -test_expect_success 'diff.external should apply only to diff' ' +test_expect_success !SANITIZE_LEAK 'diff.external should apply only to diff' ' test_config diff.external echo && - git log -p -1 HEAD | - grep "^diff --git a/file b/file" + git log -p -1 HEAD >out && + grep "^diff --git a/file b/file" out ' test_expect_success 'diff.external and --no-ext-diff' ' test_config diff.external echo && - git diff --no-ext-diff | - grep "^diff --git a/file b/file" + git diff --no-ext-diff >out && + grep "^diff --git a/file b/file" out ' test_expect_success 'diff attribute' ' @@ -103,29 +94,23 @@ test_expect_success 'diff attribute' ' echo >.gitattributes "file diff=parrot" && - git diff | { - read path oldfile oldhex oldmode newfile newhex newmode && - test "z$path" = zfile && - test "z$oldmode" = z100644 && - test "z$newhex" = "z$ZERO_OID" && - test "z$newmode" = z100644 && - oh=$(git rev-parse --verify HEAD:file) && - test "z$oh" = "z$oldhex" - } - + cat >expect <<-EOF && + file $(git rev-parse --verify HEAD:file) 100644 $(test_oid zero) 100644 + EOF + git diff >out && + cut -d" " -f1,3-4,6- <out >actual && + test_cmp expect actual ' -test_expect_success 'diff attribute should apply only to diff' ' - - git log -p -1 HEAD | - grep "^diff --git a/file b/file" +test_expect_success !SANITIZE_LEAK 'diff attribute should apply only to diff' ' + git log -p -1 HEAD >out && + grep "^diff --git a/file b/file" out ' test_expect_success 'diff attribute and --no-ext-diff' ' - - git diff --no-ext-diff | - grep "^diff --git a/file b/file" + git diff --no-ext-diff >out && + grep "^diff --git a/file b/file" out ' @@ -136,48 +121,55 @@ test_expect_success 'diff attribute' ' echo >.gitattributes "file diff=color" && - git diff | { - read path oldfile oldhex oldmode newfile newhex newmode && - test "z$path" = zfile && - test "z$oldmode" = z100644 && - test "z$newhex" = "z$ZERO_OID" && - test "z$newmode" = z100644 && - oh=$(git rev-parse --verify HEAD:file) && - test "z$oh" = "z$oldhex" - } - + cat >expect <<-EOF && + file $(git rev-parse --verify HEAD:file) 100644 $(test_oid zero) 100644 + EOF + git diff >out && + cut -d" " -f1,3-4,6- <out >actual && + test_cmp expect actual ' -test_expect_success 'diff attribute should apply only to diff' ' - - git log -p -1 HEAD | - grep "^diff --git a/file b/file" +test_expect_success !SANITIZE_LEAK 'diff attribute should apply only to diff' ' + git log -p -1 HEAD >out && + grep "^diff --git a/file b/file" out ' test_expect_success 'diff attribute and --no-ext-diff' ' - - git diff --no-ext-diff | - grep "^diff --git a/file b/file" + git diff --no-ext-diff >out && + grep "^diff --git a/file b/file" out ' test_expect_success 'GIT_EXTERNAL_DIFF trumps diff.external' ' >.gitattributes && test_config diff.external "echo ext-global" && - GIT_EXTERNAL_DIFF="echo ext-env" git diff | grep ext-env + + cat >expect <<-EOF && + ext-env file $(git rev-parse --verify HEAD:file) 100644 file $(test_oid zero) 100644 + EOF + GIT_EXTERNAL_DIFF="echo ext-env" git diff >out && + cut -d" " -f1-2,4- <out >actual && + test_cmp expect actual ' test_expect_success 'attributes trump GIT_EXTERNAL_DIFF and diff.external' ' test_config diff.foo.command "echo ext-attribute" && test_config diff.external "echo ext-global" && echo "file diff=foo" >.gitattributes && - GIT_EXTERNAL_DIFF="echo ext-env" git diff | grep ext-attribute + + cat >expect <<-EOF && + ext-attribute file $(git rev-parse --verify HEAD:file) 100644 file $(test_oid zero) 100644 + EOF + GIT_EXTERNAL_DIFF="echo ext-env" git diff >out && + cut -d" " -f1-2,4- <out >actual && + test_cmp expect actual ' test_expect_success 'no diff with -diff' ' echo >.gitattributes "file -diff" && - git diff | grep Binary + git diff >out && + grep Binary out ' echo NULZbetweenZwords | perl -pe 'y/Z/\000/' > file @@ -217,7 +209,12 @@ test_expect_success 'GIT_EXTERNAL_DIFF generates pretty paths' ' touch file.ext && git add file.ext && echo with extension > file.ext && - GIT_EXTERNAL_DIFF=echo git diff file.ext | grep ......_file\.ext && + + cat >expect <<-EOF && + file.ext file $(git rev-parse --verify HEAD:file) 100644 file.ext $(test_oid zero) 100644 + EOF + GIT_EXTERNAL_DIFF=echo git diff file.ext >out && + cut -d" " -f1,3- <out >actual && git update-index --force-remove file.ext && rm file.ext ' diff --git a/t/t4027-diff-submodule.sh b/t/t4027-diff-submodule.sh index 6cef0da982..295da987cc 100755 --- a/t/t4027-diff-submodule.sh +++ b/t/t4027-diff-submodule.sh @@ -2,7 +2,6 @@ test_description='difference in submodules' -TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-diff.sh @@ -28,10 +27,8 @@ test_expect_success setup ' git commit -m "submodule #2" ) && - set x $( - cd sub && - git rev-list HEAD - ) && + git -C sub rev-list HEAD >revs && + set x $(cat revs) && echo ":160000 160000 $3 $ZERO_OID M sub" >expect && subtip=$3 subprev=$2 ' diff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh index d5abcf4b4c..15764ee9ac 100755 --- a/t/t4034-diff-words.sh +++ b/t/t4034-diff-words.sh @@ -324,6 +324,7 @@ test_language_driver dts test_language_driver fortran test_language_driver html test_language_driver java +test_language_driver kotlin test_language_driver matlab test_language_driver objc test_language_driver pascal diff --git a/t/t4034/kotlin/expect b/t/t4034/kotlin/expect new file mode 100644 index 0000000000..7f76f7540d --- /dev/null +++ b/t/t4034/kotlin/expect @@ -0,0 +1,43 @@ +<BOLD>diff --git a/pre b/post<RESET> +<BOLD>index 11ea3de..2e1df4c 100644<RESET> +<BOLD>--- a/pre<RESET> +<BOLD>+++ b/post<RESET> +<CYAN>@@ -1,30 +1,30 @@<RESET> +println("Hello World<RED>!\n<RESET><GREEN>?<RESET>") +<GREEN>(<RESET>1<GREEN>) (<RESET>-1e10<GREEN>) (<RESET>0xabcdef<GREEN>)<RESET> '<RED>x<RESET><GREEN>y<RESET>' +[<RED>a<RESET><GREEN>x<RESET>] <RED>a<RESET><GREEN>x<RESET>-><RED>b a<RESET><GREEN>y x<RESET>.<RED>b<RESET><GREEN>y<RESET> +!<RED>a a<RESET><GREEN>x x<RESET>.inv() <RED>a<RESET><GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>&<RED>b<RESET> +<RED>a<RESET><GREEN>y<RESET> +<GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b a<RESET><GREEN>y x<RESET>%<RED>b<RESET> +<RED>a<RESET><GREEN>y<RESET> +<GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET><GREEN>y<RESET> +a <RED>shr<RESET><GREEN>shl<RESET> b +<RED>a<RESET><GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET> +<RED>a<RESET><GREEN>y<RESET> +<GREEN>x<RESET>==<RED>b a<RESET><GREEN>y x<RESET>!=<RED>b a<RESET><GREEN>y x<RESET>===<RED>b<RESET> +<RED>a<RESET><GREEN>y<RESET> +<GREEN>x<RESET> and <RED>b<RESET> +<RED>a<RESET><GREEN>y<RESET> +<GREEN>x<RESET>^<RED>b<RESET> +<RED>a<RESET><GREEN>y<RESET> +<GREEN>x<RESET> or <RED>b<RESET> +<RED>a<RESET><GREEN>y<RESET> +<GREEN>x<RESET>&&<RED>b a<RESET><GREEN>y x<RESET>||<RED>b<RESET> +<RED>a<RESET><GREEN>y<RESET> +<GREEN>x<RESET>=<RED>b a<RESET><GREEN>y x<RESET>+=<RED>b a<RESET><GREEN>y x<RESET>-=<RED>b a<RESET><GREEN>y x<RESET>*=<RED>b a<RESET><GREEN>y x<RESET>/=<RED>b a<RESET><GREEN>y x<RESET>%=<RED>b a<RESET><GREEN>y x<RESET><<=<RED>b a<RESET><GREEN>y x<RESET>>>=<RED>b a<RESET><GREEN>y x<RESET>&=<RED>b a<RESET><GREEN>y x<RESET>^=<RED>b a<RESET><GREEN>y x<RESET>|=<RED>b<RESET><GREEN>y<RESET> +a<RED>=<RESET><GREEN>+=<RESET>b c<RED>+=<RESET><GREEN>=<RESET>d e<RED>-=<RESET><GREEN><=<RESET>f g<RED>*=<RESET><GREEN>>=<RESET>h i<RED>/=<RESET><GREEN>/<RESET>j k<RED>%=<RESET><GREEN>%<RESET>l m<RED><<=<RESET><GREEN><<<RESET>n o<RED>>>=<RESET><GREEN>>><RESET>p q<RED>&=<RESET><GREEN>&<RESET>r s<RED>^=<RESET><GREEN>^<RESET>t u<RED>|=<RESET><GREEN>|<RESET>v +a<RED><<=<RESET><GREEN><=<RESET>b +a<RED>||<RESET><GREEN>|<RESET>b a<RED>&&<RESET><GREEN>&<RESET>b +<RED>a<RESET><GREEN>x<RESET>,y +--a<RED>==<RESET><GREEN>!=<RESET>--b +a++<RED>==<RESET><GREEN>!=<RESET>++b +<RED>0xFF_EC_DE_5E 0b100_000 100_000<RESET><GREEN>0xFF_E1_DE_5E 0b100_100 200_000<RESET> +a<RED>==<RESET><GREEN>===<RESET>b +a<RED>!!<RESET><GREEN>!=<RESET>b +<RED>_32<RESET><GREEN>_33<RESET>.find(arr) +X.<RED>fill<RESET><GREEN>find<RESET>() +X.<RED>u<RESET><GREEN>f<RESET>+1 +X.u<RED>-<RESET><GREEN>+<RESET>2 +a<RED>.<RESET><GREEN>..<RESET>b +a<RED>?.<RESET><GREEN>?:<RESET>b +<RED>.32_00_456<RESET><GREEN>.32_00_446<RESET> diff --git a/t/t4034/kotlin/post b/t/t4034/kotlin/post new file mode 100644 index 0000000000..2e1df4c6d5 --- /dev/null +++ b/t/t4034/kotlin/post @@ -0,0 +1,30 @@ +println("Hello World?") +(1) (-1e10) (0xabcdef) 'y' +[x] x->y x.y +!x x.inv() x*y x&y +x*y x/y x%y +x+y x-y +a shl b +x<y x<=y x>y x>=y +x==y x!=y x===y +x and y +x^y +x or y +x&&y x||y +x=y x+=y x-=y x*=y x/=y x%=y x<<=y x>>=y x&=y x^=y x|=y +a+=b c=d e<=f g>=h i/j k%l m<<n o>>p q&r s^t u|v +a<=b +a|b a&b +x,y +--a!=--b +a++!=++b +0xFF_E1_DE_5E 0b100_100 200_000 +a===b +a!=b +_33.find(arr) +X.find() +X.f+1 +X.u+2 +a..b +a?:b +.32_00_446 diff --git a/t/t4034/kotlin/pre b/t/t4034/kotlin/pre new file mode 100644 index 0000000000..11ea3de665 --- /dev/null +++ b/t/t4034/kotlin/pre @@ -0,0 +1,30 @@ +println("Hello World!\n") +1 -1e10 0xabcdef 'x' +[a] a->b a.b +!a a.inv() a*b a&b +a*b a/b a%b +a+b a-b +a shr b +a<b a<=b a>b a>=b +a==b a!=b a===b +a and b +a^b +a or b +a&&b a||b +a=b a+=b a-=b a*=b a/=b a%=b a<<=b a>>=b a&=b a^=b a|=b +a=b c+=d e-=f g*=h i/=j k%=l m<<=n o>>=p q&=r s^=t u|=v +a<<=b +a||b a&&b +a,y +--a==--b +a++==++b +0xFF_EC_DE_5E 0b100_000 100_000 +a==b +a!!b +_32.find(arr) +X.fill() +X.u+1 +X.u-2 +a.b +a?.b +.32_00_456 diff --git a/t/t4069-remerge-diff.sh b/t/t4069-remerge-diff.sh new file mode 100755 index 0000000000..35f94957fc --- /dev/null +++ b/t/t4069-remerge-diff.sh @@ -0,0 +1,291 @@ +#!/bin/sh + +test_description='remerge-diff handling' + +. ./test-lib.sh + +# This test is ort-specific +if test "${GIT_TEST_MERGE_ALGORITHM}" != ort +then + skip_all="GIT_TEST_MERGE_ALGORITHM != ort" + test_done +fi + +test_expect_success 'setup basic merges' ' + test_write_lines 1 2 3 4 5 6 7 8 9 >numbers && + git add numbers && + git commit -m base && + + git branch feature_a && + git branch feature_b && + git branch feature_c && + + git branch ab_resolution && + git branch bc_resolution && + + git checkout feature_a && + test_write_lines 1 2 three 4 5 6 7 eight 9 >numbers && + git commit -a -m change_a && + + git checkout feature_b && + test_write_lines 1 2 tres 4 5 6 7 8 9 >numbers && + git commit -a -m change_b && + + git checkout feature_c && + test_write_lines 1 2 3 4 5 6 7 8 9 10 >numbers && + git commit -a -m change_c && + + git checkout bc_resolution && + git merge --ff-only feature_b && + # no conflict + git merge feature_c && + + git checkout ab_resolution && + git merge --ff-only feature_a && + # conflicts! + test_must_fail git merge feature_b && + # Resolve conflict...and make another change elsewhere + test_write_lines 1 2 drei 4 5 6 7 acht 9 >numbers && + git add numbers && + git merge --continue +' + +test_expect_success 'remerge-diff on a clean merge' ' + git log -1 --oneline bc_resolution >expect && + git show --oneline --remerge-diff bc_resolution >actual && + test_cmp expect actual +' + +test_expect_success 'remerge-diff with both a resolved conflict and an unrelated change' ' + git log -1 --oneline ab_resolution >tmp && + cat <<-EOF >>tmp && + diff --git a/numbers b/numbers + remerge CONFLICT (content): Merge conflict in numbers + index a1fb731..6875544 100644 + --- a/numbers + +++ b/numbers + @@ -1,13 +1,9 @@ + 1 + 2 + -<<<<<<< b0ed5cb (change_a) + -three + -======= + -tres + ->>>>>>> 6cd3f82 (change_b) + +drei + 4 + 5 + 6 + 7 + -eight + +acht + 9 + EOF + # Hashes above are sha1; rip them out so test works with sha256 + sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >expect && + + git show --oneline --remerge-diff ab_resolution >tmp && + sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >actual && + test_cmp expect actual +' + +test_expect_success 'setup non-content conflicts' ' + git switch --orphan base && + + test_write_lines 1 2 3 4 5 6 7 8 9 >numbers && + test_write_lines a b c d e f g h i >letters && + test_write_lines in the way >content && + git add numbers letters content && + git commit -m base && + + git branch side1 && + git branch side2 && + + git checkout side1 && + test_write_lines 1 2 three 4 5 6 7 8 9 >numbers && + git mv letters letters_side1 && + git mv content file_or_directory && + git add numbers && + git commit -m side1 && + + git checkout side2 && + git rm numbers && + git mv letters letters_side2 && + mkdir file_or_directory && + echo hello >file_or_directory/world && + git add file_or_directory/world && + git commit -m side2 && + + git checkout -b resolution side1 && + test_must_fail git merge side2 && + test_write_lines 1 2 three 4 5 6 7 8 9 >numbers && + git add numbers && + git add letters_side1 && + git rm letters && + git rm letters_side2 && + git add file_or_directory~HEAD && + git mv file_or_directory~HEAD wanted_content && + git commit -m resolved +' + +test_expect_success 'remerge-diff with non-content conflicts' ' + git log -1 --oneline resolution >tmp && + cat <<-EOF >>tmp && + diff --git a/file_or_directory~HASH (side1) b/wanted_content + similarity index 100% + rename from file_or_directory~HASH (side1) + rename to wanted_content + remerge CONFLICT (file/directory): directory in the way of file_or_directory from HASH (side1); moving it to file_or_directory~HASH (side1) instead. + diff --git a/letters b/letters + remerge CONFLICT (rename/rename): letters renamed to letters_side1 in HASH (side1) and to letters_side2 in HASH (side2). + diff --git a/letters_side2 b/letters_side2 + deleted file mode 100644 + index b236ae5..0000000 + --- a/letters_side2 + +++ /dev/null + @@ -1,9 +0,0 @@ + -a + -b + -c + -d + -e + -f + -g + -h + -i + diff --git a/numbers b/numbers + remerge CONFLICT (modify/delete): numbers deleted in HASH (side2) and modified in HASH (side1). Version HASH (side1) of numbers left in tree. + EOF + # We still have some sha1 hashes above; rip them out so test works + # with sha256 + sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >expect && + + git show --oneline --remerge-diff resolution >tmp && + sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >actual && + test_cmp expect actual +' + +test_expect_success 'remerge-diff w/ diff-filter=U: all conflict headers, no diff content' ' + git log -1 --oneline resolution >tmp && + cat <<-EOF >>tmp && + diff --git a/file_or_directory~HASH (side1) b/file_or_directory~HASH (side1) + remerge CONFLICT (file/directory): directory in the way of file_or_directory from HASH (side1); moving it to file_or_directory~HASH (side1) instead. + diff --git a/letters b/letters + remerge CONFLICT (rename/rename): letters renamed to letters_side1 in HASH (side1) and to letters_side2 in HASH (side2). + diff --git a/numbers b/numbers + remerge CONFLICT (modify/delete): numbers deleted in HASH (side2) and modified in HASH (side1). Version HASH (side1) of numbers left in tree. + EOF + # We still have some sha1 hashes above; rip them out so test works + # with sha256 + sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >expect && + + git show --oneline --remerge-diff --diff-filter=U resolution >tmp && + sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >actual && + test_cmp expect actual +' + +test_expect_success 'remerge-diff w/ diff-filter=R: relevant file + conflict header' ' + git log -1 --oneline resolution >tmp && + cat <<-EOF >>tmp && + diff --git a/file_or_directory~HASH (side1) b/wanted_content + similarity index 100% + rename from file_or_directory~HASH (side1) + rename to wanted_content + remerge CONFLICT (file/directory): directory in the way of file_or_directory from HASH (side1); moving it to file_or_directory~HASH (side1) instead. + EOF + # We still have some sha1 hashes above; rip them out so test works + # with sha256 + sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >expect && + + git show --oneline --remerge-diff --diff-filter=R resolution >tmp && + sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >actual && + test_cmp expect actual +' + +test_expect_success 'remerge-diff w/ pathspec: limits to relevant file including conflict header' ' + git log -1 --oneline resolution >tmp && + cat <<-EOF >>tmp && + diff --git a/letters b/letters + remerge CONFLICT (rename/rename): letters renamed to letters_side1 in HASH (side1) and to letters_side2 in HASH (side2). + diff --git a/letters_side2 b/letters_side2 + deleted file mode 100644 + index b236ae5..0000000 + --- a/letters_side2 + +++ /dev/null + @@ -1,9 +0,0 @@ + -a + -b + -c + -d + -e + -f + -g + -h + -i + EOF + # We still have some sha1 hashes above; rip them out so test works + # with sha256 + sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >expect && + + git show --oneline --remerge-diff resolution -- "letters*" >tmp && + sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >actual && + test_cmp expect actual +' + +test_expect_success 'setup non-content conflicts' ' + git switch --orphan newbase && + + test_write_lines 1 2 3 4 5 6 7 8 9 >numbers && + git add numbers && + git commit -m base && + + git branch newside1 && + git branch newside2 && + + git checkout newside1 && + test_write_lines 1 2 three 4 5 6 7 8 9 >numbers && + git add numbers && + git commit -m side1 && + + git checkout newside2 && + test_write_lines 1 2 drei 4 5 6 7 8 9 >numbers && + git add numbers && + git commit -m side2 && + + git checkout -b newresolution newside1 && + test_must_fail git merge newside2 && + git checkout --theirs numbers && + git add -u numbers && + git commit -m resolved +' + +test_expect_success 'remerge-diff turns off history simplification' ' + git log -1 --oneline newresolution >tmp && + cat <<-EOF >>tmp && + diff --git a/numbers b/numbers + remerge CONFLICT (content): Merge conflict in numbers + index 070e9e7..5335e78 100644 + --- a/numbers + +++ b/numbers + @@ -1,10 +1,6 @@ + 1 + 2 + -<<<<<<< 96f1e45 (side1) + -three + -======= + drei + ->>>>>>> 4fd522f (side2) + 4 + 5 + 6 + EOF + # We still have some sha1 hashes above; rip them out so test works + # with sha256 + sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >expect && + + git show --oneline --remerge-diff newresolution -- numbers >tmp && + sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >actual && + test_cmp expect actual +' + +test_done diff --git a/t/t4123-apply-shrink.sh b/t/t4123-apply-shrink.sh index dfa053ff28..3ef84619f5 100755 --- a/t/t4123-apply-shrink.sh +++ b/t/t4123-apply-shrink.sh @@ -2,8 +2,6 @@ test_description='apply a patch that is larger than the preimage' - -TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh cat >F <<\EOF @@ -41,20 +39,8 @@ test_expect_success setup ' ' test_expect_success 'apply should fail gracefully' ' - - if git apply --index patch - then - echo Oops, should not have succeeded - false - else - status=$? && - echo "Status was $status" && - if test -f .git/index.lock - then - echo Oops, should not have crashed - false - fi - fi + test_must_fail git apply --index patch && + test_path_is_missing .git/index.lock ' test_done diff --git a/t/t4128-apply-root.sh b/t/t4128-apply-root.sh index cb3181e8b7..f6db5a79dd 100755 --- a/t/t4128-apply-root.sh +++ b/t/t4128-apply-root.sh @@ -2,8 +2,6 @@ test_description='apply same filename' - -TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' @@ -26,10 +24,11 @@ diff a/bla/blub/dir/file b/bla/blub/dir/file EOF test_expect_success 'apply --directory -p (1)' ' - git apply --directory=some/sub -p3 --index patch && - test Bello = $(git show :some/sub/dir/file) && - test Bello = $(cat some/sub/dir/file) + echo Bello >expect && + git show :some/sub/dir/file >actual && + test_cmp expect actual && + test_cmp expect some/sub/dir/file ' @@ -37,8 +36,10 @@ test_expect_success 'apply --directory -p (2) ' ' git reset --hard initial && git apply --directory=some/sub/ -p3 --index patch && - test Bello = $(git show :some/sub/dir/file) && - test Bello = $(cat some/sub/dir/file) + echo Bello >expect && + git show :some/sub/dir/file >actual && + test_cmp expect actual && + test_cmp expect some/sub/dir/file ' @@ -55,8 +56,10 @@ EOF test_expect_success 'apply --directory (new file)' ' git reset --hard initial && git apply --directory=some/sub/dir/ --index patch && - test content = $(git show :some/sub/dir/newfile) && - test content = $(cat some/sub/dir/newfile) + echo content >expect && + git show :some/sub/dir/newfile >actual && + test_cmp expect actual && + test_cmp expect some/sub/dir/newfile ' cat > patch << EOF @@ -72,8 +75,10 @@ EOF test_expect_success 'apply --directory -p (new file)' ' git reset --hard initial && git apply -p2 --directory=some/sub/dir/ --index patch && - test content = $(git show :some/sub/dir/newfile2) && - test content = $(cat some/sub/dir/newfile2) + echo content >expect && + git show :some/sub/dir/newfile2 >actual && + test_cmp expect actual && + test_cmp expect some/sub/dir/newfile2 ' cat > patch << EOF @@ -91,7 +96,8 @@ test_expect_success 'apply --directory (delete file)' ' echo content >some/sub/dir/delfile && git add some/sub/dir/delfile && git apply --directory=some/sub/dir/ --index patch && - ! (git ls-files | grep delfile) + git ls-files >out && + ! grep delfile out ' cat > patch << 'EOF' @@ -107,8 +113,10 @@ EOF test_expect_success 'apply --directory (quoted filename)' ' git reset --hard initial && git apply --directory=some/sub/dir/ --index patch && - test content = $(git show :some/sub/dir/quotefile) && - test content = $(cat some/sub/dir/quotefile) + echo content >expect && + git show :some/sub/dir/quotefile >actual && + test_cmp expect actual && + test_cmp expect some/sub/dir/quotefile ' test_done diff --git a/t/t4150-am.sh b/t/t4150-am.sh index 6caff0ca39..159fae8d01 100755 --- a/t/t4150-am.sh +++ b/t/t4150-am.sh @@ -1169,7 +1169,7 @@ test_expect_success 'invalid when passing the --empty option alone' ' test_when_finished "git am --abort || :" && git checkout empty-commit^ && test_must_fail git am --empty empty-commit.patch 2>err && - echo "error: Invalid value for --empty: empty-commit.patch" >expected && + echo "error: invalid value for '\''--empty'\'': '\''empty-commit.patch'\''" >expected && test_cmp expected err ' diff --git a/t/t4202-log.sh b/t/t4202-log.sh index 5049559861..be07407f85 100755 --- a/t/t4202-log.sh +++ b/t/t4202-log.sh @@ -142,6 +142,19 @@ test_expect_success 'diff-filter=R' ' ' +test_expect_success 'multiple --diff-filter bits' ' + + git log -M --pretty="format:%s" --diff-filter=R HEAD >expect && + git log -M --pretty="format:%s" --diff-filter=Ra HEAD >actual && + test_cmp expect actual && + git log -M --pretty="format:%s" --diff-filter=aR HEAD >actual && + test_cmp expect actual && + git log -M --pretty="format:%s" \ + --diff-filter=a --diff-filter=R HEAD >actual && + test_cmp expect actual + +' + test_expect_success 'diff-filter=C' ' git log -C -C --pretty="format:%s" --diff-filter=C HEAD >actual && @@ -449,6 +462,29 @@ test_expect_success !FAIL_PREREQS 'log with various grep.patternType configurati ) ' +for cmd in show whatchanged reflog format-patch +do + case "$cmd" in + format-patch) myarg="HEAD~.." ;; + *) myarg= ;; + esac + + test_expect_success "$cmd: understands grep.patternType, like 'log'" ' + git init "pattern-type-$cmd" && + ( + cd "pattern-type-$cmd" && + test_commit 1 file A && + test_commit "(1|2)" file B 2 && + + git -c grep.patternType=fixed $cmd --grep="..." $myarg >actual && + test_must_be_empty actual && + + git -c grep.patternType=basic $cmd --grep="..." $myarg >actual && + test_file_not_empty actual + ) + ' +done + test_expect_success 'log --author' ' cat >expect <<-\EOF && Author: <BOLD;RED>A U<RESET> Thor <author@example.com> @@ -659,7 +695,7 @@ EOF test_expect_success 'log --graph with full output' ' git log --graph --date-order --pretty=short | - git name-rev --name-only --stdin | + git name-rev --name-only --annotate-stdin | sed "s/Merge:.*/Merge: A B/;s/ *\$//" >actual && test_cmp expect actual ' @@ -1671,6 +1707,75 @@ test_expect_success 'log --graph with --name-only' ' test_cmp_graph --name-only tangle..reach ' +test_expect_success '--no-graph countermands --graph' ' + git log >expect && + git log --graph --no-graph >actual && + test_cmp expect actual +' + +test_expect_success '--graph countermands --no-graph' ' + git log --graph >expect && + git log --no-graph --graph >actual && + test_cmp expect actual +' + +test_expect_success '--no-graph does not unset --topo-order' ' + git log --topo-order >expect && + git log --topo-order --no-graph >actual && + test_cmp expect actual +' + +test_expect_success '--no-graph does not unset --parents' ' + git log --parents >expect && + git log --parents --no-graph >actual && + test_cmp expect actual +' + +test_expect_success '--reverse and --graph conflict' ' + test_must_fail git log --reverse --graph 2>stderr && + test_i18ngrep "cannot be used together" stderr +' + +test_expect_success '--reverse --graph --no-graph works' ' + git log --reverse >expect && + git log --reverse --graph --no-graph >actual && + test_cmp expect actual +' + +test_expect_success '--show-linear-break and --graph conflict' ' + test_must_fail git log --show-linear-break --graph 2>stderr && + test_i18ngrep "cannot be used together" stderr +' + +test_expect_success '--show-linear-break --graph --no-graph works' ' + git log --show-linear-break >expect && + git log --show-linear-break --graph --no-graph >actual && + test_cmp expect actual +' + +test_expect_success '--no-walk and --graph conflict' ' + test_must_fail git log --no-walk --graph 2>stderr && + test_i18ngrep "cannot be used together" stderr +' + +test_expect_success '--no-walk --graph --no-graph works' ' + git log --no-walk >expect && + git log --no-walk --graph --no-graph >actual && + test_cmp expect actual +' + +test_expect_success '--walk-reflogs and --graph conflict' ' + test_must_fail git log --walk-reflogs --graph 2>stderr && + (test_i18ngrep "cannot combine" stderr || + test_i18ngrep "cannot be used together" stderr) +' + +test_expect_success '--walk-reflogs --graph --no-graph works' ' + git log --walk-reflogs >expect && + git log --walk-reflogs --graph --no-graph >actual && + test_cmp expect actual +' + test_expect_success 'dotdot is a parent directory' ' mkdir -p a/b && ( echo sixth && echo fifth ) >expect && @@ -1931,7 +2036,8 @@ test_expect_success GPGSM 'log --graph --show-signature for merged tag x509 miss git merge --no-ff -m msg signed_tag_x509_nokey && GNUPGHOME=. git log --graph --show-signature -n1 plain-x509-nokey >actual && grep "^|\\\ merged tag" actual && - grep "^| | gpgsm: certificate not found" actual + grep -e "^| | gpgsm: certificate not found" \ + -e "^| | gpgsm: failed to find the certificate: Not found" actual ' test_expect_success GPGSM 'log --graph --show-signature for merged tag x509 bad signature' ' diff --git a/t/t4204-patch-id.sh b/t/t4204-patch-id.sh index 80f4a65b28..a730c0db98 100755 --- a/t/t4204-patch-id.sh +++ b/t/t4204-patch-id.sh @@ -38,7 +38,7 @@ calc_patch_id () { shift git patch-id "$@" >patch-id.output && sed "s/ .*//" patch-id.output >patch-id_"$patch_name" && - test_line_count -gt 0 patch-id_"$patch_name" + test_line_count -eq 1 patch-id_"$patch_name" } get_top_diff () { @@ -166,40 +166,67 @@ test_expect_success 'patch-id respects config from subdir' ' ) ' -cat >nonl <<\EOF -diff --git i/a w/a -index e69de29..2e65efe 100644 ---- i/a -+++ w/a -@@ -0,0 +1 @@ -+a -\ No newline at end of file -diff --git i/b w/b -index e69de29..6178079 100644 ---- i/b -+++ w/b -@@ -0,0 +1 @@ -+b -EOF - -cat >withnl <<\EOF -diff --git i/a w/a -index e69de29..7898192 100644 ---- i/a -+++ w/a -@@ -0,0 +1 @@ -+a -diff --git i/b w/b -index e69de29..6178079 100644 ---- i/b -+++ w/b -@@ -0,0 +1 @@ -+b -EOF - test_expect_success 'patch-id handles no-nl-at-eof markers' ' - cat nonl | calc_patch_id nonl && - cat withnl | calc_patch_id withnl && + cat >nonl <<-\EOF && + diff --git i/a w/a + index e69de29..2e65efe 100644 + --- i/a + +++ w/a + @@ -0,0 +1 @@ + +a + \ No newline at end of file + diff --git i/b w/b + index e69de29..6178079 100644 + --- i/b + +++ w/b + @@ -0,0 +1 @@ + +b + EOF + cat >withnl <<-\EOF && + diff --git i/a w/a + index e69de29..7898192 100644 + --- i/a + +++ w/a + @@ -0,0 +1 @@ + +a + diff --git i/b w/b + index e69de29..6178079 100644 + --- i/b + +++ w/b + @@ -0,0 +1 @@ + +b + EOF + calc_patch_id nonl <nonl && + calc_patch_id withnl <withnl && test_cmp patch-id_nonl patch-id_withnl ' + +test_expect_success 'patch-id handles diffs with one line of before/after' ' + cat >diffu1 <<-\EOF && + diff --git a/bar b/bar + index bdaf90f..31051f6 100644 + --- a/bar + +++ b/bar + @@ -2 +2,2 @@ + b + +c + diff --git a/car b/car + index 00750ed..2ae5e34 100644 + --- a/car + +++ b/car + @@ -1 +1,2 @@ + 3 + +d + diff --git a/foo b/foo + index e439850..7146eb8 100644 + --- a/foo + +++ b/foo + @@ -2 +2,2 @@ + a + +e + EOF + calc_patch_id diffu1 <diffu1 && + test_config patchid.stable true && + calc_patch_id diffu1stable <diffu1 +' test_done diff --git a/t/t4216-log-bloom.sh b/t/t4216-log-bloom.sh index cc3cebf672..fa9d32facf 100755 --- a/t/t4216-log-bloom.sh +++ b/t/t4216-log-bloom.sh @@ -48,6 +48,7 @@ graph_read_expect () { header: 43475048 1 $(test_oid oid_version) $NUM_CHUNKS 0 num_commits: $1 chunks: oid_fanout oid_lookup commit_metadata generation_data bloom_indexes bloom_data + options: bloom(1,10,7) read_generation_data EOF test-tool read-graph >actual && test_cmp expect actual diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh index 2fd845187e..a11d61206a 100755 --- a/t/t5300-pack-object.sh +++ b/t/t5300-pack-object.sh @@ -315,8 +315,10 @@ test_expect_success \ git index-pack -o tmp.idx test-3.pack && cmp tmp.idx test-1-${packname_1}.idx && - git index-pack test-3.pack && + git index-pack --promisor=message test-3.pack && cmp test-3.idx test-1-${packname_1}.idx && + echo message >expect && + test_cmp expect test-3.promisor && cat test-2-${packname_2}.pack >test-3.pack && git index-pack -o tmp.idx test-2-${packname_2}.pack && diff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh index 8ee67df38f..b0095ab41d 100755 --- a/t/t5302-pack-index.sh +++ b/t/t5302-pack-index.sh @@ -284,4 +284,12 @@ test_expect_success 'index-pack -v --stdin produces progress for both phases' ' test_i18ngrep "Resolving deltas" err ' +test_expect_success 'too-large packs report the breach' ' + pack=$(git pack-objects --all pack </dev/null) && + sz="$(test_file_size pack-$pack.pack)" && + test "$sz" -gt 20 && + test_must_fail git index-pack --max-input-size=20 pack-$pack.pack 2>err && + grep "maximum allowed size (20 bytes)" err +' + test_done diff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh index d05ab716f6..f775fc1ce6 100755 --- a/t/t5310-pack-bitmaps.sh +++ b/t/t5310-pack-bitmaps.sh @@ -397,4 +397,32 @@ test_expect_success 'pack.preferBitmapTips' ' ) ' +test_expect_success 'complains about multiple pack bitmaps' ' + rm -fr repo && + git init repo && + test_when_finished "rm -fr repo" && + ( + cd repo && + + test_commit base && + + git repack -adb && + bitmap="$(ls .git/objects/pack/pack-*.bitmap)" && + mv "$bitmap" "$bitmap.bak" && + + test_commit other && + git repack -ab && + + mv "$bitmap.bak" "$bitmap" && + + find .git/objects/pack -type f -name "*.pack" >packs && + find .git/objects/pack -type f -name "*.bitmap" >bitmaps && + test_line_count = 2 packs && + test_line_count = 2 bitmaps && + + git rev-list --use-bitmap-index HEAD 2>err && + grep "ignoring extra bitmap file" err + ) +' + test_done diff --git a/t/t5312-prune-corruption.sh b/t/t5312-prune-corruption.sh index ea889c088a..9d8e249ae8 100755 --- a/t/t5312-prune-corruption.sh +++ b/t/t5312-prune-corruption.sh @@ -22,8 +22,8 @@ test_expect_success 'disable reflogs' ' ' create_bogus_ref () { - test_when_finished 'rm -f .git/refs/heads/bogus..name' && - echo $bogus >.git/refs/heads/bogus..name + test-tool ref-store main update-ref msg "refs/heads/bogus..name" $bogus $ZERO_OID REF_SKIP_REFNAME_VERIFICATION && + test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/bogus..name" } test_expect_success 'create history reachable only from a bogus-named ref' ' @@ -113,7 +113,7 @@ test_expect_success 'pack-refs does not silently delete broken loose ref' ' # we do not want to count on running pack-refs to # actually pack it, as it is perfectly reasonable to # skip processing a broken ref -test_expect_success 'create packed-refs file with broken ref' ' +test_expect_success REFFILES 'create packed-refs file with broken ref' ' rm -f .git/refs/heads/main && cat >.git/packed-refs <<-EOF && $missing refs/heads/main @@ -124,13 +124,13 @@ test_expect_success 'create packed-refs file with broken ref' ' test_cmp expect actual ' -test_expect_success 'pack-refs does not silently delete broken packed ref' ' +test_expect_success REFFILES 'pack-refs does not silently delete broken packed ref' ' git pack-refs --all --prune && git rev-parse refs/heads/main >actual && test_cmp expect actual ' -test_expect_success 'pack-refs does not drop broken refs during deletion' ' +test_expect_success REFFILES 'pack-refs does not drop broken refs during deletion' ' git update-ref -d refs/heads/other && git rev-parse refs/heads/main >actual && test_cmp expect actual diff --git a/t/t5316-pack-delta-depth.sh b/t/t5316-pack-delta-depth.sh index df524f7b6d..e9045009a1 100755 --- a/t/t5316-pack-delta-depth.sh +++ b/t/t5316-pack-delta-depth.sh @@ -64,7 +64,11 @@ test_expect_success 'create series of packs' ' echo $cur && echo "$(git rev-parse :file) file" } | git pack-objects --stdout >tmp && - git index-pack --stdin --fix-thin <tmp || return 1 + GIT_TRACE2_EVENT=$PWD/trace \ + git index-pack -v --stdin --fix-thin <tmp || return 1 && + grep -c region_enter.*progress trace >enter && + grep -c region_leave.*progress trace >leave && + test_cmp enter leave && prev=$cur done ' diff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh index edb728f77c..fbf0d64578 100755 --- a/t/t5318-commit-graph.sh +++ b/t/t5318-commit-graph.sh @@ -29,12 +29,7 @@ test_expect_success 'setup full repo' ' cd "$TRASH_DIRECTORY/full" && git init && git config core.commitGraph true && - objdir=".git/objects" && - - test_oid_cache <<-EOF - oid_version sha1:1 - oid_version sha256:2 - EOF + objdir=".git/objects" ' test_expect_success POSIXPERM 'tweak umask for modebit tests' ' @@ -69,46 +64,10 @@ test_expect_success 'create commits and repack' ' git repack ' -graph_git_two_modes() { - git -c core.commitGraph=true $1 >output && - git -c core.commitGraph=false $1 >expect && - test_cmp expect output -} - -graph_git_behavior() { - MSG=$1 - DIR=$2 - BRANCH=$3 - COMPARE=$4 - test_expect_success "check normal git operations: $MSG" ' - cd "$TRASH_DIRECTORY/$DIR" && - graph_git_two_modes "log --oneline $BRANCH" && - graph_git_two_modes "log --topo-order $BRANCH" && - graph_git_two_modes "log --graph $COMPARE..$BRANCH" && - graph_git_two_modes "branch -vv" && - graph_git_two_modes "merge-base -a $BRANCH $COMPARE" - ' -} +. "$TEST_DIRECTORY"/lib-commit-graph.sh graph_git_behavior 'no graph' full commits/3 commits/1 -graph_read_expect() { - OPTIONAL="" - NUM_CHUNKS=3 - if test ! -z "$2" - then - OPTIONAL=" $2" - NUM_CHUNKS=$((3 + $(echo "$2" | wc -w))) - fi - cat >expect <<- EOF - header: 43475048 1 $(test_oid oid_version) $NUM_CHUNKS 0 - num_commits: $1 - chunks: oid_fanout oid_lookup commit_metadata$OPTIONAL - EOF - test-tool read-graph >output && - test_cmp expect output -} - test_expect_success 'exit with correct error on bad input to --stdin-commits' ' cd "$TRASH_DIRECTORY/full" && # invalid, non-hex OID @@ -466,10 +425,10 @@ test_expect_success 'warn on improper hash version' ' ) ' -test_expect_success 'lower layers have overflow chunk' ' +test_expect_success TIME_IS_64BIT,TIME_T_IS_64BIT 'lower layers have overflow chunk' ' cd "$TRASH_DIRECTORY/full" && UNIX_EPOCH_ZERO="@0 +0000" && - FUTURE_DATE="@2147483646 +0000" && + FUTURE_DATE="@4147483646 +0000" && rm -f .git/objects/info/commit-graph && test_commit --date "$FUTURE_DATE" future-1 && test_commit --date "$UNIX_EPOCH_ZERO" old-1 && @@ -497,7 +456,7 @@ test_expect_success 'git commit-graph verify' ' cd "$TRASH_DIRECTORY/full" && git rev-parse commits/8 | git -c commitGraph.generationVersion=1 commit-graph write --stdin-commits && git commit-graph verify >output && - graph_read_expect 9 extra_edges + graph_read_expect 9 extra_edges 1 ' NUM_COMMITS=9 @@ -825,10 +784,6 @@ test_expect_success 'set up and verify repo with generation data overflow chunk' objdir=".git/objects" && UNIX_EPOCH_ZERO="@0 +0000" && FUTURE_DATE="@2147483646 +0000" && - test_oid_cache <<-EOF && - oid_version sha1:1 - oid_version sha256:2 - EOF cd "$TRASH_DIRECTORY" && mkdir repo && cd repo && diff --git a/t/t5324-split-commit-graph.sh b/t/t5324-split-commit-graph.sh index 847b809710..669ddc645f 100755 --- a/t/t5324-split-commit-graph.sh +++ b/t/t5324-split-commit-graph.sh @@ -30,10 +30,16 @@ graph_read_expect() { then NUM_BASE=$2 fi + OPTIONS= + if test -z "$3" + then + OPTIONS=" read_generation_data" + fi cat >expect <<- EOF header: 43475048 1 $(test_oid oid_version) 4 $NUM_BASE num_commits: $1 chunks: oid_fanout oid_lookup commit_metadata generation_data + options:$OPTIONS EOF test-tool read-graph >output && test_cmp expect output @@ -508,6 +514,7 @@ test_expect_success 'setup repo for mixed generation commit-graph-chain' ' header: 43475048 1 $(test_oid oid_version) 4 1 num_commits: $NUM_SECOND_LAYER_COMMITS chunks: oid_fanout oid_lookup commit_metadata + options: EOF test_cmp expect output && git commit-graph verify && @@ -540,6 +547,7 @@ test_expect_success 'do not write generation data chunk if not present on existi header: 43475048 1 $(test_oid oid_version) 4 2 num_commits: $NUM_THIRD_LAYER_COMMITS chunks: oid_fanout oid_lookup commit_metadata + options: EOF test_cmp expect output && git commit-graph verify @@ -581,6 +589,7 @@ test_expect_success 'do not write generation data chunk if the topmost remaining header: 43475048 1 $(test_oid oid_version) 4 2 num_commits: $(($NUM_THIRD_LAYER_COMMITS + $NUM_FOURTH_LAYER_COMMITS)) chunks: oid_fanout oid_lookup commit_metadata + options: EOF test_cmp expect output && git commit-graph verify @@ -620,6 +629,7 @@ test_expect_success 'write generation data chunk if topmost remaining layer has header: 43475048 1 $(test_oid oid_version) 5 1 num_commits: $(($NUM_SECOND_LAYER_COMMITS + $NUM_THIRD_LAYER_COMMITS + $NUM_FOURTH_LAYER_COMMITS + $NUM_FIFTH_LAYER_COMMITS)) chunks: oid_fanout oid_lookup commit_metadata generation_data + options: read_generation_data EOF test_cmp expect output ) diff --git a/t/t5326-multi-pack-bitmaps.sh b/t/t5326-multi-pack-bitmaps.sh index e187f90f29..4fe57414c1 100755 --- a/t/t5326-multi-pack-bitmaps.sh +++ b/t/t5326-multi-pack-bitmaps.sh @@ -9,125 +9,13 @@ test_description='exercise basic multi-pack bitmap functionality' GIT_TEST_MULTI_PACK_INDEX=0 GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=0 -objdir=.git/objects -midx=$objdir/pack/multi-pack-index +# This test exercise multi-pack bitmap functionality where the object order is +# stored and read from a special chunk within the MIDX, so use the default +# behavior here. +sane_unset GIT_TEST_MIDX_WRITE_REV +sane_unset GIT_TEST_MIDX_READ_RIDX -# midx_pack_source <obj> -midx_pack_source () { - test-tool read-midx --show-objects .git/objects | grep "^$1 " | cut -f2 -} - -setup_bitmap_history - -test_expect_success 'enable core.multiPackIndex' ' - git config core.multiPackIndex true -' - -test_expect_success 'create single-pack midx with bitmaps' ' - git repack -ad && - git multi-pack-index write --bitmap && - test_path_is_file $midx && - test_path_is_file $midx-$(midx_checksum $objdir).bitmap && - test_path_is_file $midx-$(midx_checksum $objdir).rev -' - -basic_bitmap_tests - -test_expect_success 'create new additional packs' ' - for i in $(test_seq 1 16) - do - test_commit "$i" && - git repack -d || return 1 - done && - - git checkout -b other2 HEAD~8 && - for i in $(test_seq 1 8) - do - test_commit "side-$i" && - git repack -d || return 1 - done && - git checkout second -' - -test_expect_success 'create multi-pack midx with bitmaps' ' - git multi-pack-index write --bitmap && - - ls $objdir/pack/pack-*.pack >packs && - test_line_count = 25 packs && - - test_path_is_file $midx && - test_path_is_file $midx-$(midx_checksum $objdir).bitmap && - test_path_is_file $midx-$(midx_checksum $objdir).rev -' - -basic_bitmap_tests - -test_expect_success '--no-bitmap is respected when bitmaps exist' ' - git multi-pack-index write --bitmap && - - test_commit respect--no-bitmap && - git repack -d && - - test_path_is_file $midx && - test_path_is_file $midx-$(midx_checksum $objdir).bitmap && - test_path_is_file $midx-$(midx_checksum $objdir).rev && - - git multi-pack-index write --no-bitmap && - - test_path_is_file $midx && - test_path_is_missing $midx-$(midx_checksum $objdir).bitmap && - test_path_is_missing $midx-$(midx_checksum $objdir).rev -' - -test_expect_success 'setup midx with base from later pack' ' - # Write a and b so that "a" is a delta on top of base "b", since Git - # prefers to delete contents out of a base rather than add to a shorter - # object. - test_seq 1 128 >a && - test_seq 1 130 >b && - - git add a b && - git commit -m "initial commit" && - - a=$(git rev-parse HEAD:a) && - b=$(git rev-parse HEAD:b) && - - # In the first pack, "a" is stored as a delta to "b". - p1=$(git pack-objects .git/objects/pack/pack <<-EOF - $a - $b - EOF - ) && - - # In the second pack, "a" is missing, and "b" is not a delta nor base to - # any other object. - p2=$(git pack-objects .git/objects/pack/pack <<-EOF - $b - $(git rev-parse HEAD) - $(git rev-parse HEAD^{tree}) - EOF - ) && - - git prune-packed && - # Use the second pack as the preferred source, so that "b" occurs - # earlier in the MIDX object order, rendering "a" unusable for pack - # reuse. - git multi-pack-index write --bitmap --preferred-pack=pack-$p2.idx && - - have_delta $a $b && - test $(midx_pack_source $a) != $(midx_pack_source $b) -' - -rev_list_tests 'full bitmap with backwards delta' - -test_expect_success 'clone with bitmaps enabled' ' - git clone --no-local --bare . clone-reverse-delta.git && - test_when_finished "rm -fr clone-reverse-delta.git" && - - git rev-parse HEAD >expect && - git --git-dir=clone-reverse-delta.git rev-parse HEAD >actual && - test_cmp expect actual -' +midx_bitmap_core bitmap_reuse_tests() { from=$1 @@ -204,17 +92,7 @@ test_expect_success 'missing object closure fails gracefully' ' ) ' -test_expect_success 'setup partial bitmaps' ' - test_commit packed && - git repack && - test_commit loose && - git multi-pack-index write --bitmap 2>err && - test_path_is_file $midx && - test_path_is_file $midx-$(midx_checksum $objdir).bitmap && - test_path_is_file $midx-$(midx_checksum $objdir).rev -' - -basic_bitmap_tests HEAD~ +midx_bitmap_partial_tests test_expect_success 'removing a MIDX clears stale bitmaps' ' rm -fr repo && @@ -228,7 +106,6 @@ test_expect_success 'removing a MIDX clears stale bitmaps' ' # Write a MIDX and bitmap; remove the MIDX but leave the bitmap. stale_bitmap=$midx-$(midx_checksum $objdir).bitmap && - stale_rev=$midx-$(midx_checksum $objdir).rev && rm $midx && # Then write a new MIDX. @@ -238,9 +115,7 @@ test_expect_success 'removing a MIDX clears stale bitmaps' ' test_path_is_file $midx && test_path_is_file $midx-$(midx_checksum $objdir).bitmap && - test_path_is_file $midx-$(midx_checksum $objdir).rev && - test_path_is_missing $stale_bitmap && - test_path_is_missing $stale_rev + test_path_is_missing $stale_bitmap ) ' @@ -261,7 +136,6 @@ test_expect_success 'pack.preferBitmapTips' ' git multi-pack-index write --bitmap && test_path_is_file $midx && test_path_is_file $midx-$(midx_checksum $objdir).bitmap && - test_path_is_file $midx-$(midx_checksum $objdir).rev && test-tool bitmap list-commits | sort >bitmaps && comm -13 bitmaps commits >before && @@ -271,7 +145,6 @@ test_expect_success 'pack.preferBitmapTips' ' <before | git update-ref --stdin && rm -fr $midx-$(midx_checksum $objdir).bitmap && - rm -fr $midx-$(midx_checksum $objdir).rev && rm -fr $midx && git -c pack.preferBitmapTips=refs/tags/include \ @@ -309,7 +182,6 @@ test_expect_success 'writing a bitmap with --refs-snapshot' ' grep "$(git rev-parse two)" bitmaps && rm -fr $midx-$(midx_checksum $objdir).bitmap && - rm -fr $midx-$(midx_checksum $objdir).rev && rm -fr $midx && # Then again, but with a refs snapshot which only sees @@ -354,7 +226,6 @@ test_expect_success 'write a bitmap with --refs-snapshot (preferred tips)' ' ) >snapshot && rm -fr $midx-$(midx_checksum $objdir).bitmap && - rm -fr $midx-$(midx_checksum $objdir).rev && rm -fr $midx && git multi-pack-index write --bitmap --refs-snapshot=snapshot && @@ -395,4 +266,45 @@ test_expect_success 'hash-cache values are propagated from pack bitmaps' ' ) ' +test_expect_success 'no .bitmap is written without any objects' ' + rm -fr repo && + git init repo && + test_when_finished "rm -fr repo" && + ( + cd repo && + + empty="$(git pack-objects $objdir/pack/pack </dev/null)" && + cat >packs <<-EOF && + pack-$empty.idx + EOF + + git multi-pack-index write --bitmap --stdin-packs \ + <packs 2>err && + + grep "bitmap without any objects" err && + + test_path_is_file $midx && + test_path_is_missing $midx-$(midx_checksum $objdir).bitmap + ) +' + +test_expect_success 'graceful fallback when missing reverse index' ' + rm -fr repo && + git init repo && + test_when_finished "rm -fr repo" && + ( + cd repo && + + test_commit base && + + # write a pack and MIDX bitmap containing base + git repack -adb && + git multi-pack-index write --bitmap && + + GIT_TEST_MIDX_READ_RIDX=0 \ + git rev-list --use-bitmap-index HEAD 2>err && + ! grep "ignoring extra bitmap file" err + ) +' + test_done diff --git a/t/t5327-multi-pack-bitmaps-rev.sh b/t/t5327-multi-pack-bitmaps-rev.sh new file mode 100755 index 0000000000..d30ba632c8 --- /dev/null +++ b/t/t5327-multi-pack-bitmaps-rev.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +test_description='exercise basic multi-pack bitmap functionality (.rev files)' + +. ./test-lib.sh +. "${TEST_DIRECTORY}/lib-bitmap.sh" + +# We'll be writing our own midx and bitmaps, so avoid getting confused by the +# automatic ones. +GIT_TEST_MULTI_PACK_INDEX=0 +GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=0 + +# Unlike t5326, this test exercise multi-pack bitmap functionality where the +# object order is stored in a separate .rev file. +GIT_TEST_MIDX_WRITE_REV=1 +GIT_TEST_MIDX_READ_RIDX=0 +export GIT_TEST_MIDX_WRITE_REV +export GIT_TEST_MIDX_READ_RIDX + +midx_bitmap_core rev +midx_bitmap_partial_tests rev + +test_done diff --git a/t/t5328-commit-graph-64bit-time.sh b/t/t5328-commit-graph-64bit-time.sh new file mode 100755 index 0000000000..093f0c067a --- /dev/null +++ b/t/t5328-commit-graph-64bit-time.sh @@ -0,0 +1,66 @@ +#!/bin/sh + +test_description='commit graph with 64-bit timestamps' +. ./test-lib.sh + +if ! test_have_prereq TIME_IS_64BIT || ! test_have_prereq TIME_T_IS_64BIT +then + skip_all='skipping 64-bit timestamp tests' + test_done +fi + +. "$TEST_DIRECTORY"/lib-commit-graph.sh + +UNIX_EPOCH_ZERO="@0 +0000" +FUTURE_DATE="@4147483646 +0000" + +GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=0 + +test_expect_success 'lower layers have overflow chunk' ' + rm -f .git/objects/info/commit-graph && + test_commit --date "$FUTURE_DATE" future-1 && + test_commit --date "$UNIX_EPOCH_ZERO" old-1 && + git commit-graph write --reachable && + test_commit --date "$FUTURE_DATE" future-2 && + test_commit --date "$UNIX_EPOCH_ZERO" old-2 && + git commit-graph write --reachable --split=no-merge && + test_commit extra && + git commit-graph write --reachable --split=no-merge && + git commit-graph write --reachable && + graph_read_expect 5 "generation_data generation_data_overflow" && + mv .git/objects/info/commit-graph commit-graph-upgraded && + git commit-graph write --reachable && + graph_read_expect 5 "generation_data generation_data_overflow" && + test_cmp .git/objects/info/commit-graph commit-graph-upgraded +' + +graph_git_behavior 'overflow' '' HEAD~2 HEAD + +test_expect_success 'set up and verify repo with generation data overflow chunk' ' + mkdir repo && + cd repo && + git init && + test_commit --date "$UNIX_EPOCH_ZERO" 1 && + test_commit 2 && + test_commit --date "$UNIX_EPOCH_ZERO" 3 && + git commit-graph write --reachable && + graph_read_expect 3 generation_data && + test_commit --date "$FUTURE_DATE" 4 && + test_commit 5 && + test_commit --date "$UNIX_EPOCH_ZERO" 6 && + git branch left && + git reset --hard 3 && + test_commit 7 && + test_commit --date "$FUTURE_DATE" 8 && + test_commit 9 && + git branch right && + git reset --hard 3 && + test_merge M left right && + git commit-graph write --reachable && + graph_read_expect 10 "generation_data generation_data_overflow" && + git commit-graph verify +' + +graph_git_behavior 'overflow 2' repo left right + +test_done diff --git a/t/t5403-post-checkout-hook.sh b/t/t5403-post-checkout-hook.sh index 1ec9e23be7..d118181690 100755 --- a/t/t5403-post-checkout-hook.sh +++ b/t/t5403-post-checkout-hook.sh @@ -49,23 +49,60 @@ test_expect_success 'post-checkout receives the right args when not switching br test $old = $new && test $flag = 0 ' -test_expect_success 'post-checkout is triggered on rebase' ' - test_when_finished "rm -f .git/post-checkout.args" && - git checkout -b rebase-test main && - rm -f .git/post-checkout.args && - git rebase rebase-on-me && - read old new flag <.git/post-checkout.args && - test $old != $new && test $flag = 1 -' +test_rebase () { + args="$*" && + test_expect_success "post-checkout is triggered on rebase $args" ' + test_when_finished "rm -f .git/post-checkout.args" && + git checkout -B rebase-test main && + rm -f .git/post-checkout.args && + git rebase $args rebase-on-me && + read old new flag <.git/post-checkout.args && + test_cmp_rev main $old && + test_cmp_rev rebase-on-me $new && + test $flag = 1 + ' + + test_expect_success "post-checkout is triggered on rebase $args with fast-forward" ' + test_when_finished "rm -f .git/post-checkout.args" && + git checkout -B ff-rebase-test rebase-on-me^ && + rm -f .git/post-checkout.args && + git rebase $args rebase-on-me && + read old new flag <.git/post-checkout.args && + test_cmp_rev rebase-on-me^ $old && + test_cmp_rev rebase-on-me $new && + test $flag = 1 + ' + + test_expect_success "rebase $args fast-forward branch checkout runs post-checkout hook" ' + test_when_finished "test_might_fail git rebase --abort" && + test_when_finished "rm -f .git/post-checkout.args" && + git update-ref refs/heads/rebase-fast-forward three && + git checkout two && + rm -f .git/post-checkout.args && + git rebase $args HEAD rebase-fast-forward && + read old new flag <.git/post-checkout.args && + test_cmp_rev two $old && + test_cmp_rev three $new && + test $flag = 1 + ' + + test_expect_success "rebase $args checkout does not remove untracked files" ' + test_when_finished "test_might_fail git rebase --abort" && + test_when_finished "rm -f .git/post-checkout.args" && + git update-ref refs/heads/rebase-fast-forward three && + git checkout two && + rm -f .git/post-checkout.args && + echo untracked >three.t && + test_when_finished "rm three.t" && + test_must_fail git rebase $args HEAD rebase-fast-forward 2>err && + grep "untracked working tree files would be overwritten by checkout" err && + test_path_is_missing .git/post-checkout.args -test_expect_success 'post-checkout is triggered on rebase with fast-forward' ' - test_when_finished "rm -f .git/post-checkout.args" && - git checkout -b ff-rebase-test rebase-on-me^ && - rm -f .git/post-checkout.args && - git rebase rebase-on-me && - read old new flag <.git/post-checkout.args && - test $old != $new && test $flag = 1 ' +} + +test_rebase --apply && +test_rebase --merge test_expect_success 'post-checkout hook is triggered by clone' ' mkdir -p templates/hooks && diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index f0dc4e6968..ee6d2dde9f 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -927,7 +927,8 @@ test_expect_success 'fetching deepen' ' ) ' -test_expect_success 'use ref advertisement to prune "have" lines sent' ' +test_negotiation_algorithm_default () { + test_when_finished rm -rf clientv0 clientv2 && rm -rf server client && git init server && test_commit -C server both_have_1 && @@ -946,7 +947,7 @@ test_expect_success 'use ref advertisement to prune "have" lines sent' ' rm -f trace && cp -r client clientv0 && GIT_TRACE_PACKET="$(pwd)/trace" git -C clientv0 \ - fetch origin server_has both_have_2 && + "$@" fetch origin server_has both_have_2 && grep "have $(git -C client rev-parse client_has)" trace && grep "have $(git -C client rev-parse both_have_2)" trace && ! grep "have $(git -C client rev-parse both_have_2^)" trace && @@ -954,10 +955,27 @@ test_expect_success 'use ref advertisement to prune "have" lines sent' ' rm -f trace && cp -r client clientv2 && GIT_TRACE_PACKET="$(pwd)/trace" git -C clientv2 -c protocol.version=2 \ - fetch origin server_has both_have_2 && + "$@" fetch origin server_has both_have_2 && grep "have $(git -C client rev-parse client_has)" trace && grep "have $(git -C client rev-parse both_have_2)" trace && ! grep "have $(git -C client rev-parse both_have_2^)" trace +} + +test_expect_success 'use ref advertisement to prune "have" lines sent' ' + test_negotiation_algorithm_default +' + +test_expect_success 'same as last but with config overrides' ' + test_negotiation_algorithm_default \ + -c feature.experimental=true \ + -c fetch.negotiationAlgorithm=consecutive +' + +test_expect_success 'ensure bogus fetch.negotiationAlgorithm yields error' ' + test_when_finished rm -rf clientv0 && + cp -r client clientv0 && + test_must_fail git -C clientv0 --fetch.negotiationAlgorithm=bogus \ + fetch origin server_has both_have_2 ' test_expect_success 'filtering by size' ' diff --git a/t/t5503-tagfollow.sh b/t/t5503-tagfollow.sh index 195fc64dd4..a3c01014b7 100755 --- a/t/t5503-tagfollow.sh +++ b/t/t5503-tagfollow.sh @@ -160,4 +160,68 @@ test_expect_success 'new clone fetch main and tags' ' test_cmp expect actual ' +test_expect_success 'atomic fetch with failing backfill' ' + git init clone3 && + + # We want to test whether a failure when backfilling tags correctly + # aborts the complete transaction when `--atomic` is passed: we should + # neither create the branch nor should we create the tag when either + # one of both fails to update correctly. + # + # To trigger failure we simply abort when backfilling a tag. + write_script clone3/.git/hooks/reference-transaction <<-\EOF && + while read oldrev newrev reference + do + if test "$reference" = refs/tags/tag1 + then + exit 1 + fi + done + EOF + + test_must_fail git -C clone3 fetch --atomic .. $B:refs/heads/something && + test_must_fail git -C clone3 rev-parse --verify refs/heads/something && + test_must_fail git -C clone3 rev-parse --verify refs/tags/tag2 +' + +test_expect_success 'atomic fetch with backfill should use single transaction' ' + git init clone4 && + + # Fetching with the `--atomic` flag should update all references in a + # single transaction, including backfilled tags. We thus expect to see + # a single reference transaction for the created branch and tags. + cat >expected <<-EOF && + prepared + $ZERO_OID $B refs/heads/something + $ZERO_OID $S refs/tags/tag2 + $ZERO_OID $T refs/tags/tag1 + committed + $ZERO_OID $B refs/heads/something + $ZERO_OID $S refs/tags/tag2 + $ZERO_OID $T refs/tags/tag1 + EOF + + write_script clone4/.git/hooks/reference-transaction <<-\EOF && + ( echo "$*" && cat ) >>actual + EOF + + git -C clone4 fetch --atomic .. $B:refs/heads/something && + test_cmp expected clone4/actual +' + +test_expect_success 'backfill failure causes command to fail' ' + git init clone5 && + + # Create a tag that is nested below the tag we are about to fetch via + # the backfill mechanism. This causes a D/F conflict when backfilling + # and should thus cause the command to fail. + empty_blob=$(git -C clone5 hash-object -w --stdin </dev/null) && + git -C clone5 update-ref refs/tags/tag1/nested $empty_blob && + + test_must_fail git -C clone5 fetch .. $B:refs/heads/something && + test $B = $(git -C clone5 rev-parse --verify refs/heads/something) && + test $S = $(git -C clone5 rev-parse --verify tag2) && + test_must_fail git -C clone5 rev-parse --verify tag1 +' + test_done diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh index 9ab315424c..c90cf47acd 100755 --- a/t/t5505-remote.sh +++ b/t/t5505-remote.sh @@ -753,7 +753,9 @@ test_expect_success 'rename a remote' ' ( cd four && git config branch.main.pushRemote origin && - git remote rename origin upstream && + GIT_TRACE2_EVENT=$(pwd)/trace \ + git remote rename --progress origin upstream && + test_region progress "Renaming remote references" trace && grep "pushRemote" .git/config && test -z "$(git for-each-ref refs/remotes/origin)" && test "$(git symbolic-ref refs/remotes/upstream/HEAD)" = "refs/remotes/upstream/main" && diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index 20f7110ec1..48e14e2dab 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -164,6 +164,17 @@ test_expect_success 'fetch --prune --tags with refspec prunes based on refspec' git rev-parse sometag ' +test_expect_success REFFILES 'fetch --prune fails to delete branches' ' + cd "$D" && + git clone . prune-fail && + cd prune-fail && + git update-ref refs/remotes/origin/extrabranch main && + : this will prevent --prune from locking packed-refs for deleting refs, but adding loose refs still succeeds && + >.git/packed-refs.new && + + test_must_fail git fetch --prune origin +' + test_expect_success 'fetch --atomic works with a single branch' ' test_when_finished "rm -rf \"$D\"/atomic" && @@ -332,6 +343,35 @@ test_expect_success 'fetch --atomic --append appends to FETCH_HEAD' ' test_cmp expected atomic/.git/FETCH_HEAD ' +test_expect_success 'fetch --atomic --prune executes a single reference transaction only' ' + test_when_finished "rm -rf \"$D\"/atomic" && + + cd "$D" && + git branch scheduled-for-deletion && + git clone . atomic && + git branch -D scheduled-for-deletion && + git branch new-branch && + head_oid=$(git rev-parse HEAD) && + + # Fetching with the `--atomic` flag should update all references in a + # single transaction. + cat >expected <<-EOF && + prepared + $ZERO_OID $ZERO_OID refs/remotes/origin/scheduled-for-deletion + $ZERO_OID $head_oid refs/remotes/origin/new-branch + committed + $ZERO_OID $ZERO_OID refs/remotes/origin/scheduled-for-deletion + $ZERO_OID $head_oid refs/remotes/origin/new-branch + EOF + + write_script atomic/.git/hooks/reference-transaction <<-\EOF && + ( echo "$*" && cat ) >>actual + EOF + + git -C atomic fetch --atomic --prune origin && + test_cmp expected atomic/actual +' + test_expect_success '--refmap="" ignores configured refspec' ' cd "$TRASH_DIRECTORY" && git clone "$D" remote-refs && diff --git a/t/t5511-refspec.sh b/t/t5511-refspec.sh index be025b90f9..fc55681a3f 100755 --- a/t/t5511-refspec.sh +++ b/t/t5511-refspec.sh @@ -2,6 +2,7 @@ test_description='refspec parsing' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_refspec () { diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index 2f04cf9a1c..3137eb8d4d 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -229,6 +229,18 @@ test_expect_success 'push with negotiation proceeds anyway even if negotiation f test_i18ngrep "push negotiation failed" err ' +test_expect_success 'push with negotiation does not attempt to fetch submodules' ' + mk_empty submodule_upstream && + test_commit -C submodule_upstream submodule_commit && + git submodule add ./submodule_upstream submodule && + mk_empty testrepo && + git push testrepo $the_first_commit:refs/remotes/origin/first_commit && + test_commit -C testrepo unrelated_commit && + git -C testrepo config receive.hideRefs refs/remotes/origin/first_commit && + git -c submodule.recurse=true -c protocol.version=2 -c push.negotiate=1 push testrepo refs/heads/main:refs/remotes/origin/main 2>err && + ! grep "Fetching submodule" err +' + test_expect_success 'push without wildcard' ' mk_empty testrepo && @@ -1809,4 +1821,12 @@ test_expect_success 'refuse fetch to current branch of bare repository worktree' git -C bare.git fetch -u .. HEAD:wt ' +test_expect_success 'refuse to push a hidden ref, and make sure do not pollute the repository' ' + mk_empty testrepo && + git -C testrepo config receive.hiderefs refs/hidden && + git -C testrepo config receive.unpackLimit 1 && + test_must_fail git push testrepo HEAD:refs/hidden/foo && + test_dir_is_empty testrepo/.git/objects/pack +' + test_done diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh index 93ecfcdd24..081808009b 100755 --- a/t/t5520-pull.sh +++ b/t/t5520-pull.sh @@ -330,6 +330,19 @@ test_expect_success '--rebase --autostash fast forward' ' test_cmp_rev HEAD to-rebase-ff ' +test_expect_success '--rebase with rebase.autostash succeeds on ff' ' + test_when_finished "rm -fr src dst actual" && + git init src && + test_commit -C src "initial" file "content" && + git clone src dst && + test_commit -C src --printf "more_content" file "more content\ncontent\n" && + echo "dirty" >>dst/file && + test_config -C dst rebase.autostash true && + git -C dst pull --rebase >actual 2>&1 && + grep -q "Fast-forward" actual && + grep -q "Applied autostash." actual +' + test_expect_success '--rebase with conflicts shows advice' ' test_when_finished "git rebase --abort; git checkout -f to-rebase" && git checkout -b seq && diff --git a/t/t5571-pre-push-hook.sh b/t/t5571-pre-push-hook.sh index 660f876eec..96d6ecc0af 100755 --- a/t/t5571-pre-push-hook.sh +++ b/t/t5571-pre-push-hook.sh @@ -11,7 +11,7 @@ HOOKDIR="$(git rev-parse --git-dir)/hooks" HOOK="$HOOKDIR/pre-push" mkdir -p "$HOOKDIR" write_script "$HOOK" <<EOF -cat >/dev/null +cat >actual exit 0 EOF @@ -20,10 +20,16 @@ test_expect_success 'setup' ' git init --bare repo1 && git remote add parent1 repo1 && test_commit one && - git push parent1 HEAD:foreign + cat >expect <<-EOF && + HEAD $(git rev-parse HEAD) refs/heads/foreign $(test_oid zero) + EOF + + test_when_finished "rm actual" && + git push parent1 HEAD:foreign && + test_cmp expect actual ' write_script "$HOOK" <<EOF -cat >/dev/null +cat >actual exit 1 EOF @@ -32,11 +38,18 @@ export COMMIT1 test_expect_success 'push with failing hook' ' test_commit two && - test_must_fail git push parent1 HEAD + cat >expect <<-EOF && + HEAD $(git rev-parse HEAD) refs/heads/main $(test_oid zero) + EOF + + test_when_finished "rm actual" && + test_must_fail git push parent1 HEAD && + test_cmp expect actual ' test_expect_success '--no-verify bypasses hook' ' - git push --no-verify parent1 HEAD + git push --no-verify parent1 HEAD && + test_path_is_missing actual ' COMMIT2="$(git rev-parse HEAD)" @@ -48,15 +61,15 @@ echo "$2" >>actual cat >>actual EOF -cat >expected <<EOF -parent1 -repo1 -refs/heads/main $COMMIT2 refs/heads/foreign $COMMIT1 -EOF - test_expect_success 'push with hook' ' + cat >expect <<-EOF && + parent1 + repo1 + refs/heads/main $COMMIT2 refs/heads/foreign $COMMIT1 + EOF + git push parent1 main:foreign && - diff expected actual + test_cmp expect actual ' test_expect_success 'add a branch' ' @@ -67,49 +80,48 @@ test_expect_success 'add a branch' ' COMMIT3="$(git rev-parse HEAD)" export COMMIT3 -cat >expected <<EOF -parent1 -repo1 -refs/heads/other $COMMIT3 refs/heads/foreign $COMMIT2 -EOF - test_expect_success 'push to default' ' + cat >expect <<-EOF && + parent1 + repo1 + refs/heads/other $COMMIT3 refs/heads/foreign $COMMIT2 + EOF git push && - diff expected actual + test_cmp expect actual ' -cat >expected <<EOF -parent1 -repo1 -refs/tags/one $COMMIT1 refs/tags/tag1 $ZERO_OID -HEAD~ $COMMIT2 refs/heads/prev $ZERO_OID -EOF - test_expect_success 'push non-branches' ' + cat >expect <<-EOF && + parent1 + repo1 + refs/tags/one $COMMIT1 refs/tags/tag1 $ZERO_OID + HEAD~ $COMMIT2 refs/heads/prev $ZERO_OID + EOF + git push parent1 one:tag1 HEAD~:refs/heads/prev && - diff expected actual + test_cmp expect actual ' -cat >expected <<EOF -parent1 -repo1 -(delete) $ZERO_OID refs/heads/prev $COMMIT2 -EOF - test_expect_success 'push delete' ' + cat >expect <<-EOF && + parent1 + repo1 + (delete) $ZERO_OID refs/heads/prev $COMMIT2 + EOF + git push parent1 :prev && - diff expected actual + test_cmp expect actual ' -cat >expected <<EOF -repo1 -repo1 -HEAD $COMMIT3 refs/heads/other $ZERO_OID -EOF - test_expect_success 'push to URL' ' + cat >expect <<-EOF && + repo1 + repo1 + HEAD $COMMIT3 refs/heads/other $ZERO_OID + EOF + git push repo1 HEAD && - diff expected actual + test_cmp expect actual ' test_expect_success 'set up many-ref tests' ' diff --git a/t/t5617-clone-submodules-remote.sh b/t/t5617-clone-submodules-remote.sh index e2dbb4eaba..ca8f80083a 100755 --- a/t/t5617-clone-submodules-remote.sh +++ b/t/t5617-clone-submodules-remote.sh @@ -28,6 +28,13 @@ test_expect_success 'setup' ' ) ' +# bare clone giving "srv.bare" for use as our server. +test_expect_success 'setup bare clone for server' ' + git clone --bare "file://$(pwd)/." srv.bare && + git -C srv.bare config --local uploadpack.allowfilter 1 && + git -C srv.bare config --local uploadpack.allowanysha1inwant 1 +' + test_expect_success 'clone with --no-remote-submodules' ' test_when_finished "rm -rf super_clone" && git clone --recurse-submodules --no-remote-submodules "file://$pwd/." super_clone && @@ -65,4 +72,38 @@ test_expect_success 'clone with --single-branch' ' ) ' +# do basic partial clone from "srv.bare" +# confirm partial clone was registered in the local config for super and sub. +test_expect_success 'clone with --filter' ' + git clone --recurse-submodules \ + --filter blob:none --also-filter-submodules \ + "file://$pwd/srv.bare" super_clone && + test_cmp_config -C super_clone true remote.origin.promisor && + test_cmp_config -C super_clone blob:none remote.origin.partialclonefilter && + test_cmp_config -C super_clone/sub true remote.origin.promisor && + test_cmp_config -C super_clone/sub blob:none remote.origin.partialclonefilter +' + +# check that clone.filterSubmodules works (--also-filter-submodules can be +# omitted) +test_expect_success 'filters applied with clone.filterSubmodules' ' + test_config_global clone.filterSubmodules true && + git clone --recurse-submodules --filter blob:none \ + "file://$pwd/srv.bare" super_clone2 && + test_cmp_config -C super_clone2 true remote.origin.promisor && + test_cmp_config -C super_clone2 blob:none remote.origin.partialclonefilter && + test_cmp_config -C super_clone2/sub true remote.origin.promisor && + test_cmp_config -C super_clone2/sub blob:none remote.origin.partialclonefilter +' + +test_expect_success '--no-also-filter-submodules overrides clone.filterSubmodules=true' ' + test_config_global clone.filterSubmodules true && + git clone --recurse-submodules --filter blob:none \ + --no-also-filter-submodules \ + "file://$pwd/srv.bare" super_clone3 && + test_cmp_config -C super_clone3 true remote.origin.promisor && + test_cmp_config -C super_clone3 blob:none remote.origin.partialclonefilter && + test_cmp_config -C super_clone3/sub false --default false remote.origin.promisor +' + test_done diff --git a/t/t5700-protocol-v1.sh b/t/t5700-protocol-v1.sh index 468bd3e13e..6c8d4c6cf1 100755 --- a/t/t5700-protocol-v1.sh +++ b/t/t5700-protocol-v1.sh @@ -149,6 +149,21 @@ test_expect_success 'push with file:// using protocol v1' ' grep "push< version 1" log ' +test_expect_success 'cloning branchless tagless but not refless remote' ' + rm -rf server client && + + git -c init.defaultbranch=main init server && + echo foo >server/foo.txt && + git -C server add foo.txt && + git -C server commit -m "message" && + git -C server update-ref refs/notbranch/alsonottag HEAD && + git -C server checkout --detach && + git -C server branch -D main && + git -C server symbolic-ref HEAD refs/heads/nonexistentbranch && + + git -c protocol.version=1 clone "file://$(pwd)/server" client +' + # Test protocol v1 with 'ssh://' transport # test_expect_success 'setup ssh wrapper' ' diff --git a/t/t5702-protocol-v2.sh b/t/t5702-protocol-v2.sh index 710f33e2aa..00ce9aec23 100755 --- a/t/t5702-protocol-v2.sh +++ b/t/t5702-protocol-v2.sh @@ -619,7 +619,7 @@ test_expect_success 'usage: --negotiate-only without --negotiation-tip' ' setup_negotiate_only "$SERVER" "$URI" && cat >err.expect <<-\EOF && - fatal: --negotiate-only needs one or more --negotiate-tip=* + fatal: --negotiate-only needs one or more --negotiation-tip=* EOF test_must_fail git -c protocol.version=2 -C client fetch \ @@ -628,6 +628,18 @@ test_expect_success 'usage: --negotiate-only without --negotiation-tip' ' test_cmp err.expect err.actual ' +test_expect_success 'usage: --negotiate-only with --recurse-submodules' ' + cat >err.expect <<-\EOF && + fatal: options '\''--negotiate-only'\'' and '\''--recurse-submodules'\'' cannot be used together + EOF + + test_must_fail git -c protocol.version=2 -C client fetch \ + --negotiate-only \ + --recurse-submodules \ + origin 2>err.actual && + test_cmp err.expect err.actual +' + test_expect_success 'file:// --negotiate-only' ' SERVER="server" && URI="file://$(pwd)/server" && diff --git a/t/t6005-rev-list-count.sh b/t/t6005-rev-list-count.sh index 86542c650e..e960049f64 100755 --- a/t/t6005-rev-list-count.sh +++ b/t/t6005-rev-list-count.sh @@ -2,7 +2,6 @@ test_description='git rev-list --max-count and --skip test' -TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' @@ -14,39 +13,39 @@ test_expect_success 'setup' ' ' test_expect_success 'no options' ' - test $(git rev-list HEAD | wc -l) = 5 + test_stdout_line_count = 5 git rev-list HEAD ' test_expect_success '--max-count' ' - test $(git rev-list HEAD --max-count=0 | wc -l) = 0 && - test $(git rev-list HEAD --max-count=3 | wc -l) = 3 && - test $(git rev-list HEAD --max-count=5 | wc -l) = 5 && - test $(git rev-list HEAD --max-count=10 | wc -l) = 5 + test_stdout_line_count = 0 git rev-list HEAD --max-count=0 && + test_stdout_line_count = 3 git rev-list HEAD --max-count=3 && + test_stdout_line_count = 5 git rev-list HEAD --max-count=5 && + test_stdout_line_count = 5 git rev-list HEAD --max-count=10 ' test_expect_success '--max-count all forms' ' - test $(git rev-list HEAD --max-count=1 | wc -l) = 1 && - test $(git rev-list HEAD -1 | wc -l) = 1 && - test $(git rev-list HEAD -n1 | wc -l) = 1 && - test $(git rev-list HEAD -n 1 | wc -l) = 1 + test_stdout_line_count = 1 git rev-list HEAD --max-count=1 && + test_stdout_line_count = 1 git rev-list HEAD -1 && + test_stdout_line_count = 1 git rev-list HEAD -n1 && + test_stdout_line_count = 1 git rev-list HEAD -n 1 ' test_expect_success '--skip' ' - test $(git rev-list HEAD --skip=0 | wc -l) = 5 && - test $(git rev-list HEAD --skip=3 | wc -l) = 2 && - test $(git rev-list HEAD --skip=5 | wc -l) = 0 && - test $(git rev-list HEAD --skip=10 | wc -l) = 0 + test_stdout_line_count = 5 git rev-list HEAD --skip=0 && + test_stdout_line_count = 2 git rev-list HEAD --skip=3 && + test_stdout_line_count = 0 git rev-list HEAD --skip=5 && + test_stdout_line_count = 0 git rev-list HEAD --skip=10 ' test_expect_success '--skip --max-count' ' - test $(git rev-list HEAD --skip=0 --max-count=0 | wc -l) = 0 && - test $(git rev-list HEAD --skip=0 --max-count=10 | wc -l) = 5 && - test $(git rev-list HEAD --skip=3 --max-count=0 | wc -l) = 0 && - test $(git rev-list HEAD --skip=3 --max-count=1 | wc -l) = 1 && - test $(git rev-list HEAD --skip=3 --max-count=2 | wc -l) = 2 && - test $(git rev-list HEAD --skip=3 --max-count=10 | wc -l) = 2 && - test $(git rev-list HEAD --skip=5 --max-count=10 | wc -l) = 0 && - test $(git rev-list HEAD --skip=10 --max-count=10 | wc -l) = 0 + test_stdout_line_count = 0 git rev-list HEAD --skip=0 --max-count=0 && + test_stdout_line_count = 5 git rev-list HEAD --skip=0 --max-count=10 && + test_stdout_line_count = 0 git rev-list HEAD --skip=3 --max-count=0 && + test_stdout_line_count = 1 git rev-list HEAD --skip=3 --max-count=1 && + test_stdout_line_count = 2 git rev-list HEAD --skip=3 --max-count=2 && + test_stdout_line_count = 2 git rev-list HEAD --skip=3 --max-count=10 && + test_stdout_line_count = 0 git rev-list HEAD --skip=5 --max-count=10 && + test_stdout_line_count = 0 git rev-list HEAD --skip=10 --max-count=10 ' test_done diff --git a/t/t6007-rev-list-cherry-pick-file.sh b/t/t6007-rev-list-cherry-pick-file.sh index aebe4b69e1..6f3e543977 100755 --- a/t/t6007-rev-list-cherry-pick-file.sh +++ b/t/t6007-rev-list-cherry-pick-file.sh @@ -58,7 +58,7 @@ EOF test_expect_success '--left-right' ' git rev-list --left-right B...C > actual && - git name-rev --stdin --name-only --refs="*tags/*" \ + git name-rev --annotate-stdin --name-only --refs="*tags/*" \ < actual > actual.named && test_cmp expect actual.named ' @@ -78,14 +78,14 @@ EOF test_expect_success '--cherry-pick bar does not come up empty' ' git rev-list --left-right --cherry-pick B...C -- bar > actual && - git name-rev --stdin --name-only --refs="*tags/*" \ + git name-rev --annotate-stdin --name-only --refs="*tags/*" \ < actual > actual.named && test_cmp expect actual.named ' test_expect_success 'bar does not come up empty' ' git rev-list --left-right B...C -- bar > actual && - git name-rev --stdin --name-only --refs="*tags/*" \ + git name-rev --annotate-stdin --name-only --refs="*tags/*" \ < actual > actual.named && test_cmp expect actual.named ' @@ -97,14 +97,14 @@ EOF test_expect_success '--cherry-pick bar does not come up empty (II)' ' git rev-list --left-right --cherry-pick F...E -- bar > actual && - git name-rev --stdin --name-only --refs="*tags/*" \ + git name-rev --annotate-stdin --name-only --refs="*tags/*" \ < actual > actual.named && test_cmp expect actual.named ' test_expect_success 'name-rev multiple --refs combine inclusive' ' git rev-list --left-right --cherry-pick F...E -- bar >actual && - git name-rev --stdin --name-only --refs="*tags/F" --refs="*tags/E" \ + git name-rev --annotate-stdin --name-only --refs="*tags/F" --refs="*tags/E" \ <actual >actual.named && test_cmp expect actual.named ' @@ -116,7 +116,7 @@ EOF test_expect_success 'name-rev --refs excludes non-matched patterns' ' git rev-list --left-right --right-only --cherry-pick F...E -- bar >>expect && git rev-list --left-right --cherry-pick F...E -- bar >actual && - git name-rev --stdin --name-only --refs="*tags/F" \ + git name-rev --annotate-stdin --name-only --refs="*tags/F" \ <actual >actual.named && test_cmp expect actual.named ' @@ -128,14 +128,14 @@ EOF test_expect_success 'name-rev --exclude excludes matched patterns' ' git rev-list --left-right --right-only --cherry-pick F...E -- bar >>expect && git rev-list --left-right --cherry-pick F...E -- bar >actual && - git name-rev --stdin --name-only --refs="*tags/*" --exclude="*E" \ + git name-rev --annotate-stdin --name-only --refs="*tags/*" --exclude="*E" \ <actual >actual.named && test_cmp expect actual.named ' test_expect_success 'name-rev --no-refs clears the refs list' ' git rev-list --left-right --cherry-pick F...E -- bar >expect && - git name-rev --stdin --name-only --refs="*tags/F" --refs="*tags/E" --no-refs --refs="*tags/G" \ + git name-rev --annotate-stdin --name-only --refs="*tags/F" --refs="*tags/E" --no-refs --refs="*tags/G" \ <expect >actual && test_cmp expect actual ' @@ -149,7 +149,7 @@ EOF test_expect_success '--cherry-mark' ' git rev-list --cherry-mark F...E -- bar > actual && - git name-rev --stdin --name-only --refs="*tags/*" \ + git name-rev --annotate-stdin --name-only --refs="*tags/*" \ < actual > actual.named && test_cmp expect actual.named ' @@ -163,7 +163,7 @@ EOF test_expect_success '--cherry-mark --left-right' ' git rev-list --cherry-mark --left-right F...E -- bar > actual && - git name-rev --stdin --name-only --refs="*tags/*" \ + git name-rev --annotate-stdin --name-only --refs="*tags/*" \ < actual > actual.named && test_cmp expect actual.named ' @@ -174,14 +174,14 @@ EOF test_expect_success '--cherry-pick --right-only' ' git rev-list --cherry-pick --right-only F...E -- bar > actual && - git name-rev --stdin --name-only --refs="*tags/*" \ + git name-rev --annotate-stdin --name-only --refs="*tags/*" \ < actual > actual.named && test_cmp expect actual.named ' test_expect_success '--cherry-pick --left-only' ' git rev-list --cherry-pick --left-only E...F -- bar > actual && - git name-rev --stdin --name-only --refs="*tags/*" \ + git name-rev --annotate-stdin --name-only --refs="*tags/*" \ < actual > actual.named && test_cmp expect actual.named ' @@ -193,7 +193,7 @@ EOF test_expect_success '--cherry' ' git rev-list --cherry F...E -- bar > actual && - git name-rev --stdin --name-only --refs="*tags/*" \ + git name-rev --annotate-stdin --name-only --refs="*tags/*" \ < actual > actual.named && test_cmp expect actual.named ' diff --git a/t/t6012-rev-list-simplify.sh b/t/t6012-rev-list-simplify.sh index 4f7fa8b6c0..de1e87f162 100755 --- a/t/t6012-rev-list-simplify.sh +++ b/t/t6012-rev-list-simplify.sh @@ -12,17 +12,18 @@ note () { } unnote () { - git name-rev --tags --stdin | sed -e "s|$OID_REGEX (tags/\([^)]*\)) |\1 |g" + test_when_finished "rm -f tmp" && + git name-rev --tags --annotate-stdin >tmp && + sed -e "s|$OID_REGEX (tags/\([^)]*\)) |\1 |g" <tmp } # -# Create a test repo with interesting commit graph: +# Create a test repo with an interesting commit graph: # -# A--B----------G--H--I--K--L -# \ \ / / -# \ \ / / -# C------E---F J -# \_/ +# A-----B-----G--H--I--K--L +# \ \ / / +# \ \ / / +# C--D--E--F J # # The commits are laid out from left-to-right starting with # the root commit A and terminating at the tip commit L. @@ -112,8 +113,8 @@ check_outcome () { shift && param="$*" && test_expect_$outcome "log $param" ' - git log --pretty="$FMT" --parents $param | - unnote >actual && + git log --pretty="$FMT" --parents $param >out && + unnote >actual <out && sed -e "s/^.* \([^ ]*\) .*/\1/" >check <actual && test_cmp expect check ' @@ -142,11 +143,18 @@ check_result 'I B A' --author-date-order -- file check_result 'H' --first-parent -- another-file check_result 'H' --first-parent --topo-order -- another-file +check_result 'L K I H G B A' --first-parent L +check_result 'F E D C' --exclude-first-parent-only F ^L +check_result '' F ^L +check_result 'L K I H G J' L ^F +check_result 'L K I H G B J' --exclude-first-parent-only L ^F +check_result 'L K I H G B' --exclude-first-parent-only --first-parent L ^F + check_result 'E C B A' --full-history E -- lost test_expect_success 'full history simplification without parent' ' printf "%s\n" E C B A >expect && - git log --pretty="$FMT" --full-history E -- lost | - unnote >actual && + git log --pretty="$FMT" --full-history E -- lost >out && + unnote >actual <out && sed -e "s/^.* \([^ ]*\) .*/\1/" >check <actual && test_cmp expect check ' diff --git a/t/t6020-bundle-misc.sh b/t/t6020-bundle-misc.sh index b13e8a52a9..ed95d19542 100755 --- a/t/t6020-bundle-misc.sh +++ b/t/t6020-bundle-misc.sh @@ -475,4 +475,78 @@ test_expect_success 'clone from bundle' ' test_cmp expect actual ' +test_expect_success 'unfiltered bundle with --objects' ' + git bundle create all-objects.bdl \ + --all --objects && + git bundle create all.bdl \ + --all && + + # Compare the headers of these files. + sed -n -e "/^$/q" -e "p" all.bdl >expect && + sed -n -e "/^$/q" -e "p" all-objects.bdl >actual && + test_cmp expect actual +' + +for filter in "blob:none" "tree:0" "tree:1" "blob:limit=100" +do + test_expect_success "filtered bundle: $filter" ' + test_when_finished rm -rf .git/objects/pack cloned unbundled && + git bundle create partial.bdl \ + --all \ + --filter=$filter && + + git bundle verify partial.bdl >unfiltered && + make_user_friendly_and_stable_output <unfiltered >actual && + + cat >expect <<-EOF && + The bundle contains these 10 refs: + <COMMIT-P> refs/heads/main + <COMMIT-N> refs/heads/release + <COMMIT-D> refs/heads/topic/1 + <COMMIT-H> refs/heads/topic/2 + <COMMIT-D> refs/pull/1/head + <COMMIT-G> refs/pull/2/head + <TAG-1> refs/tags/v1 + <TAG-2> refs/tags/v2 + <TAG-3> refs/tags/v3 + <COMMIT-P> HEAD + The bundle uses this filter: $filter + The bundle records a complete history. + EOF + test_cmp expect actual && + + test_config uploadpack.allowfilter 1 && + test_config uploadpack.allowanysha1inwant 1 && + git clone --no-local --filter=$filter --bare "file://$(pwd)" cloned && + + git init unbundled && + git -C unbundled bundle unbundle ../partial.bdl >ref-list.txt && + ls unbundled/.git/objects/pack/pack-*.promisor >promisor && + test_line_count = 1 promisor && + + # Count the same number of reachable objects. + reflist=$(git for-each-ref --format="%(objectname)") && + git rev-list --objects --filter=$filter --missing=allow-any \ + $reflist >expect && + for repo in cloned unbundled + do + git -C $repo rev-list --objects --missing=allow-any \ + $reflist >actual && + test_cmp expect actual || return 1 + done + ' +done + +# NEEDSWORK: 'git clone --bare' should be able to clone from a filtered +# bundle, but that requires a change to promisor/filter config options. +# For now, we fail gracefully with a helpful error. This behavior can be +# changed in the future to succeed as much as possible. +test_expect_success 'cloning from filtered bundle has useful error' ' + git bundle create partial.bdl \ + --all \ + --filter=blob:none && + test_must_fail git clone --bare partial.bdl partial 2>err && + grep "cannot clone from filtered bundle" err +' + test_done diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index 1be85d064e..5382e5d216 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh @@ -278,6 +278,51 @@ test_expect_success '"git bisect run" with more complex "git bisect start"' ' git bisect reset ' +test_expect_success 'bisect run accepts exit code 126 as bad' ' + test_when_finished "git bisect reset" && + write_script test_script.sh <<-\EOF && + ! grep Another hello || exit 126 >/dev/null + EOF + git bisect start && + git bisect good $HASH1 && + git bisect bad $HASH4 && + git bisect run ./test_script.sh >my_bisect_log.txt && + grep "$HASH3 is the first bad commit" my_bisect_log.txt +' + +test_expect_success POSIXPERM 'bisect run fails with non-executable test script' ' + test_when_finished "git bisect reset" && + >not-executable.sh && + chmod -x not-executable.sh && + git bisect start && + git bisect good $HASH1 && + git bisect bad $HASH4 && + test_must_fail git bisect run ./not-executable.sh >my_bisect_log.txt && + ! grep "is the first bad commit" my_bisect_log.txt +' + +test_expect_success 'bisect run accepts exit code 127 as bad' ' + test_when_finished "git bisect reset" && + write_script test_script.sh <<-\EOF && + ! grep Another hello || exit 127 >/dev/null + EOF + git bisect start && + git bisect good $HASH1 && + git bisect bad $HASH4 && + git bisect run ./test_script.sh >my_bisect_log.txt && + grep "$HASH3 is the first bad commit" my_bisect_log.txt +' + +test_expect_success 'bisect run fails with missing test script' ' + test_when_finished "git bisect reset" && + rm -f does-not-exist.sh && + git bisect start && + git bisect good $HASH1 && + git bisect bad $HASH4 && + test_must_fail git bisect run ./does-not-exist.sh >my_bisect_log.txt && + ! grep "is the first bad commit" my_bisect_log.txt +' + # $HASH1 is good, $HASH5 is bad, we skip $HASH3 # but $HASH4 is good, # so we should find $HASH5 as the first bad commit diff --git a/t/t6102-rev-list-unexpected-objects.sh b/t/t6102-rev-list-unexpected-objects.sh index 6f0902b863..cf0195e826 100755 --- a/t/t6102-rev-list-unexpected-objects.sh +++ b/t/t6102-rev-list-unexpected-objects.sh @@ -17,8 +17,13 @@ test_expect_success 'setup unexpected non-blob entry' ' broken_tree="$(git hash-object -w --literally -t tree broken-tree)" ' -test_expect_failure 'traverse unexpected non-blob entry (lone)' ' - test_must_fail git rev-list --objects $broken_tree +test_expect_success !SANITIZE_LEAK 'TODO (should fail!): traverse unexpected non-blob entry (lone)' ' + sed "s/Z$//" >expect <<-EOF && + $broken_tree Z + $tree foo + EOF + git rev-list --objects $broken_tree >actual && + test_cmp expect actual ' test_expect_success 'traverse unexpected non-blob entry (seen)' ' @@ -116,8 +121,8 @@ test_expect_success 'setup unexpected non-blob tag' ' tag=$(git hash-object -w --literally -t tag broken-tag) ' -test_expect_failure 'traverse unexpected non-blob tag (lone)' ' - test_must_fail git rev-list --objects $tag +test_expect_success !SANITIZE_LEAK 'TODO (should fail!): traverse unexpected non-blob tag (lone)' ' + git rev-list --objects $tag ' test_expect_success 'traverse unexpected non-blob tag (seen)' ' diff --git a/t/t6111-rev-list-treesame.sh b/t/t6111-rev-list-treesame.sh index e07b6070e0..90ff141640 100755 --- a/t/t6111-rev-list-treesame.sh +++ b/t/t6111-rev-list-treesame.sh @@ -23,7 +23,8 @@ note () { } unnote () { - git name-rev --tags --stdin | sed -e "s|$OID_REGEX (tags/\([^)]*\))\([ ]\)|\1\2|g" + git name-rev --tags --annotate-stdin | \ + sed -e "s|$OID_REGEX (tags/\([^)]*\))\([ ]\)|\1\2|g" } test_expect_success setup ' diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh index d8af2bb9d2..9a35e783a7 100755 --- a/t/t6120-describe.sh +++ b/t/t6120-describe.sh @@ -270,7 +270,7 @@ test_expect_success 'name-rev --all' ' test_cmp expect actual ' -test_expect_success 'name-rev --stdin' ' +test_expect_success 'name-rev --annotate-stdin' ' >expect.unsorted && for rev in $(git rev-list --all) do @@ -278,11 +278,16 @@ test_expect_success 'name-rev --stdin' ' echo "$rev ($name)" >>expect.unsorted || return 1 done && sort <expect.unsorted >expect && - git rev-list --all | git name-rev --stdin >actual.unsorted && + git rev-list --all | git name-rev --annotate-stdin >actual.unsorted && sort <actual.unsorted >actual && test_cmp expect actual ' +test_expect_success 'name-rev --stdin deprecated' " + git rev-list --all | git name-rev --stdin 2>actual && + grep -E 'warning: --stdin is deprecated' actual +" + test_expect_success 'describe --contains with the exact tags' ' echo "A^0" >expect && tag_object=$(git rev-parse refs/tags/A) && @@ -483,6 +488,124 @@ test_expect_success 'name-rev covers all conditions while looking at parents' ' ) ' +# A-B-C-D-E-main +# +# Where C has a non-monotonically increasing commit timestamp w.r.t. other +# commits +test_expect_success 'non-monotonic commit dates setup' ' + UNIX_EPOCH_ZERO="@0 +0000" && + git init non-monotonic && + test_commit -C non-monotonic A && + test_commit -C non-monotonic --no-tag B && + test_commit -C non-monotonic --no-tag --date "$UNIX_EPOCH_ZERO" C && + test_commit -C non-monotonic D && + test_commit -C non-monotonic E +' + +test_expect_success 'name-rev with commitGraph handles non-monotonic timestamps' ' + test_config -C non-monotonic core.commitGraph true && + ( + cd non-monotonic && + + git commit-graph write --reachable && + + echo "main~3 tags/D~2" >expect && + git name-rev --tags main~3 >actual && + + test_cmp expect actual + ) +' + +test_expect_success 'name-rev --all works with non-monotonic timestamps' ' + test_config -C non-monotonic core.commitGraph false && + ( + cd non-monotonic && + + rm -rf .git/info/commit-graph* && + + cat >tags <<-\EOF && + tags/E + tags/D + tags/D~1 + tags/D~2 + tags/A + EOF + + git log --pretty=%H >revs && + + paste -d" " revs tags | sort >expect && + + git name-rev --tags --all | sort >actual && + test_cmp expect actual + ) +' + +test_expect_success 'name-rev --annotate-stdin works with non-monotonic timestamps' ' + test_config -C non-monotonic core.commitGraph false && + ( + cd non-monotonic && + + rm -rf .git/info/commit-graph* && + + cat >expect <<-\EOF && + E + D + D~1 + D~2 + A + EOF + + git log --pretty=%H >revs && + git name-rev --tags --annotate-stdin --name-only <revs >actual && + test_cmp expect actual + ) +' + +test_expect_success 'name-rev --all works with commitGraph' ' + test_config -C non-monotonic core.commitGraph true && + ( + cd non-monotonic && + + git commit-graph write --reachable && + + cat >tags <<-\EOF && + tags/E + tags/D + tags/D~1 + tags/D~2 + tags/A + EOF + + git log --pretty=%H >revs && + + paste -d" " revs tags | sort >expect && + + git name-rev --tags --all | sort >actual && + test_cmp expect actual + ) +' + +test_expect_success 'name-rev --annotate-stdin works with commitGraph' ' + test_config -C non-monotonic core.commitGraph true && + ( + cd non-monotonic && + + git commit-graph write --reachable && + + cat >expect <<-\EOF && + E + D + D~1 + D~2 + A + EOF + + git log --pretty=%H >revs && + git name-rev --tags --annotate-stdin --name-only <revs >actual && + test_cmp expect actual + ) +' + # B # o # \ diff --git a/t/t6404-recursive-merge.sh b/t/t6404-recursive-merge.sh index eaf48e941e..b8735c6db4 100755 --- a/t/t6404-recursive-merge.sh +++ b/t/t6404-recursive-merge.sh @@ -108,8 +108,13 @@ test_expect_success 'refuse to merge binary files' ' printf "\0\0" >binary-file && git add binary-file && git commit -m binary2 && - test_must_fail git merge F >merge.out 2>merge.err && - grep "Cannot merge binary files: binary-file (HEAD vs. F)" merge.err + if test "$GIT_TEST_MERGE_ALGORITHM" = ort + then + test_must_fail git merge F >merge_output + else + test_must_fail git merge F 2>merge_output + fi && + grep "Cannot merge binary files: binary-file (HEAD vs. F)" merge_output ' test_expect_success 'mark rename/delete as unmerged' ' diff --git a/t/t6406-merge-attr.sh b/t/t6406-merge-attr.sh index 57e6af5eaa..99abefd44b 100755 --- a/t/t6406-merge-attr.sh +++ b/t/t6406-merge-attr.sh @@ -221,8 +221,13 @@ test_expect_success 'binary files with union attribute' ' printf "two\0" >bin.txt && git commit -am two && - test_must_fail git merge bin-main 2>stderr && - grep -i "warning.*cannot merge.*HEAD vs. bin-main" stderr + if test "$GIT_TEST_MERGE_ALGORITHM" = ort + then + test_must_fail git merge bin-main >output + else + test_must_fail git merge bin-main 2>output + fi && + grep -i "warning.*cannot merge.*HEAD vs. bin-main" output ' test_done diff --git a/t/t6407-merge-binary.sh b/t/t6407-merge-binary.sh index 8e6241f92e..0753fc95f4 100755 --- a/t/t6407-merge-binary.sh +++ b/t/t6407-merge-binary.sh @@ -43,14 +43,9 @@ test_expect_success resolve ' rm -f a* m* && git reset --hard anchor && - if git merge -s resolve main - then - echo Oops, should not have succeeded - false - else - git ls-files -s >current && - test_cmp expect current - fi + test_must_fail git merge -s resolve main && + git ls-files -s >current && + test_cmp expect current ' test_expect_success recursive ' @@ -58,14 +53,9 @@ test_expect_success recursive ' rm -f a* m* && git reset --hard anchor && - if git merge -s recursive main - then - echo Oops, should not have succeeded - false - else - git ls-files -s >current && - test_cmp expect current - fi + test_must_fail git merge -s recursive main && + git ls-files -s >current && + test_cmp expect current ' test_done diff --git a/t/t6423-merge-rename-directories.sh b/t/t6423-merge-rename-directories.sh index 5b81a130e9..479db32cd6 100755 --- a/t/t6423-merge-rename-directories.sh +++ b/t/t6423-merge-rename-directories.sh @@ -4421,14 +4421,14 @@ test_setup_12c1 () { git checkout A && git mv node2/ node1/ && - for i in `git ls-files`; do echo side A >>$i; done && + for i in $(git ls-files); do echo side A >>$i; done && git add -u && test_tick && git commit -m "A" && git checkout B && git mv node1/ node2/ && - for i in `git ls-files`; do echo side B >>$i; done && + for i in $(git ls-files); do echo side B >>$i; done && git add -u && test_tick && git commit -m "B" @@ -4511,7 +4511,7 @@ test_setup_12c2 () { git checkout A && git mv node2/ node1/ && - for i in `git ls-files`; do echo side A >>$i; done && + for i in $(git ls-files); do echo side A >>$i; done && git add -u && echo leaf5 >node1/leaf5 && git add node1/leaf5 && @@ -4520,7 +4520,7 @@ test_setup_12c2 () { git checkout B && git mv node1/ node2/ && - for i in `git ls-files`; do echo side B >>$i; done && + for i in $(git ls-files); do echo side B >>$i; done && git add -u && echo leaf6 >node2/leaf6 && git add node2/leaf6 && @@ -4759,7 +4759,7 @@ test_setup_12f () { echo g >dir/subdir/tweaked/g && echo h >dir/subdir/tweaked/h && test_seq 20 30 >dir/subdir/tweaked/Makefile && - for i in `test_seq 1 88`; do + for i in $(test_seq 1 88); do echo content $i >dir/unchanged/file_$i done && git add . && diff --git a/t/t6428-merge-conflicts-sparse.sh b/t/t6428-merge-conflicts-sparse.sh index 7e8bf497f8..142c9aaabc 100755 --- a/t/t6428-merge-conflicts-sparse.sh +++ b/t/t6428-merge-conflicts-sparse.sh @@ -112,7 +112,7 @@ test_expect_success 'conflicting entries written to worktree even if sparse' ' ) ' -test_expect_merge_algorithm failure success 'present-despite-SKIP_WORKTREE handled reasonably' ' +test_expect_success 'present-despite-SKIP_WORKTREE handled reasonably' ' test_setup_numerals in_the_way && ( cd numerals_in_the_way && @@ -132,26 +132,13 @@ test_expect_merge_algorithm failure success 'present-despite-SKIP_WORKTREE handl test_must_fail git merge -s recursive B^0 && - git ls-files -t >index_files && - test_cmp expected-index index_files && + test_path_is_missing .git/MERGE_HEAD && - test_path_is_file README && test_path_is_file numerals && - test_cmp expected-merge numerals && - - # There should still be a file with "foobar" in it - grep foobar * && - - # 5 other files: - # * expected-merge - # * expected-index - # * index_files - # * others - # * whatever name was given to the numerals file that had - # "foobar" in it - git ls-files -o >others && - test_line_count = 5 others + # numerals should still have "foobar" in it + echo foobar >expect && + test_cmp expect numerals ) ' diff --git a/t/t6429-merge-sequence-rename-caching.sh b/t/t6429-merge-sequence-rename-caching.sh index 035edc40b1..f2bc8a7d2a 100755 --- a/t/t6429-merge-sequence-rename-caching.sh +++ b/t/t6429-merge-sequence-rename-caching.sh @@ -697,4 +697,71 @@ test_expect_success 'caching renames only on upstream side, part 2' ' ) ' +# +# The following testcase just creates two simple renames (slightly modified +# on both sides but without conflicting changes), and a directory full of +# files that are otherwise uninteresting. The setup is as follows: +# +# base: unrelated/<BUNCH OF FILES> +# numbers +# values +# upstream: modify: numbers +# modify: values +# topic: add: unrelated/foo +# modify: numbers +# modify: values +# rename: numbers -> sequence +# rename: values -> progression +# +# This is a trivial rename case, but we're curious what happens with a very +# low renameLimit interacting with the restart optimization trying to notice +# that unrelated/ looks like a trivial merge candidate. +# +test_expect_success 'avoid assuming we detected renames' ' + git init redo-weirdness && + ( + cd redo-weirdness && + + mkdir unrelated && + for i in $(test_seq 1 10) + do + >unrelated/$i + done && + test_seq 2 10 >numbers && + test_seq 12 20 >values && + git add numbers values unrelated/ && + git commit -m orig && + + git branch upstream && + git branch topic && + + git switch upstream && + test_seq 1 10 >numbers && + test_seq 11 20 >values && + git add numbers && + git commit -m "Some tweaks" && + + git switch topic && + + >unrelated/foo && + test_seq 2 12 >numbers && + test_seq 12 22 >values && + git add numbers values unrelated/ && + git mv numbers sequence && + git mv values progression && + git commit -m A && + + # + # Actual testing + # + + git switch --detach topic^0 && + + test_must_fail git -c merge.renameLimit=1 rebase upstream && + + git ls-files -u >actual && + ! test_file_is_empty actual + ) +' + test_done diff --git a/t/t7012-skip-worktree-writing.sh b/t/t7012-skip-worktree-writing.sh index a1080b94e3..cb9f1a6981 100755 --- a/t/t7012-skip-worktree-writing.sh +++ b/t/t7012-skip-worktree-writing.sh @@ -171,50 +171,20 @@ test_expect_success 'stash restore in sparse checkout' ' # Put a file in the working directory in the way echo in the way >modified && - git stash apply && + test_must_fail git stash apply 2>error&& - # Ensure stash vivifies modifies paths... - cat >expect <<-EOF && - H addme - H modified - H removeme - H subdir/A - S untouched - EOF - git ls-files -t >actual && - test_cmp expect actual && + grep "changes.*would be overwritten by merge" error && - # ...and that the paths show up in status as changed... - cat >expect <<-EOF && - A addme - M modified - D removeme - M subdir/A - ?? actual - ?? expect - ?? modified.stash.XXXXXX - EOF - git status --porcelain | \ - sed -e s/stash......./stash.XXXXXX/ >actual && - test_cmp expect actual && + echo in the way >expect && + test_cmp expect modified && + git diff --quiet HEAD ":!modified" && # ...and that working directory reflects the files correctly - test_path_is_file addme && + test_path_is_missing addme && test_path_is_file modified && test_path_is_missing removeme && test_path_is_file subdir/A && - test_path_is_missing untouched && - - # ...including that we have the expected "modified" file... - cat >expect <<-EOF && - modified - tweaked - EOF - test_cmp expect modified && - - # ...and that the other "modified" file is still present... - echo in the way >expect && - test_cmp expect modified.stash.* + test_path_is_missing untouched ) ' diff --git a/t/t7063-status-untracked-cache.sh b/t/t7063-status-untracked-cache.sh index a0c123b0a7..ca90ee805e 100755 --- a/t/t7063-status-untracked-cache.sh +++ b/t/t7063-status-untracked-cache.sh @@ -90,6 +90,9 @@ test_expect_success 'setup' ' cd worktree && mkdir done dtwo dthree && touch one two three done/one dtwo/two dthree/three && + test-tool chmtime =-300 one two three done/one dtwo/two dthree/three && + test-tool chmtime =-300 done dtwo dthree && + test-tool chmtime =-300 . && git add one two done/one && : >.git/info/exclude && git update-index --untracked-cache && @@ -142,7 +145,6 @@ two EOF test_expect_success 'status first time (empty cache)' ' - avoid_racy && : >../trace.output && GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \ git status --porcelain >../actual && @@ -166,7 +168,6 @@ test_expect_success 'untracked cache after first status' ' ' test_expect_success 'status second time (fully populated cache)' ' - avoid_racy && : >../trace.output && GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \ git status --porcelain >../actual && @@ -190,8 +191,8 @@ test_expect_success 'untracked cache after second status' ' ' test_expect_success 'modify in root directory, one dir invalidation' ' - avoid_racy && : >four && + test-tool chmtime =-240 four && : >../trace.output && GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \ git status --porcelain >../actual && @@ -241,7 +242,6 @@ EOF ' test_expect_success 'new .gitignore invalidates recursively' ' - avoid_racy && echo four >.gitignore && : >../trace.output && GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \ @@ -292,7 +292,6 @@ EOF ' test_expect_success 'new info/exclude invalidates everything' ' - avoid_racy && echo three >>.git/info/exclude && : >../trace.output && GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \ @@ -520,14 +519,14 @@ test_expect_success 'create/modify files, some of which are gitignored' ' echo three >done/three && # three is gitignored echo four >done/four && # four is gitignored at a higher level echo five >done/five && # five is not gitignored - echo test >base && #we need to ensure that the root dir is touched - rm base && + test-tool chmtime =-180 done/two done/three done/four done/five done && + # we need to ensure that the root dir is touched (in the past); + test-tool chmtime =-180 . && sync_mtime ' test_expect_success 'test sparse status with untracked cache' ' : >../trace.output && - avoid_racy && GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \ git status --porcelain >../status.actual && iuc status --porcelain >../status.iuc && @@ -570,7 +569,6 @@ EOF ' test_expect_success 'test sparse status again with untracked cache' ' - avoid_racy && : >../trace.output && GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \ git status --porcelain >../status.actual && @@ -597,11 +595,11 @@ EOF test_expect_success 'set up for test of subdir and sparse checkouts' ' mkdir done/sub && mkdir done/sub/sub && - echo "sub" > done/sub/sub/file + echo "sub" > done/sub/sub/file && + test-tool chmtime =-120 done/sub/sub/file done/sub/sub done/sub done ' test_expect_success 'test sparse status with untracked cache and subdir' ' - avoid_racy && : >../trace.output && GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \ git status --porcelain >../status.actual && @@ -651,7 +649,6 @@ EOF ' test_expect_success 'test sparse status again with untracked cache and subdir' ' - avoid_racy && : >../trace.output && GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \ git status --porcelain >../status.actual && diff --git a/t/t7103-reset-bare.sh b/t/t7103-reset-bare.sh index 0de83e3619..a60153f9f3 100755 --- a/t/t7103-reset-bare.sh +++ b/t/t7103-reset-bare.sh @@ -63,9 +63,12 @@ test_expect_success '"mixed" reset is not allowed in bare' ' test_must_fail git reset --mixed HEAD^ ' -test_expect_success '"soft" reset is allowed in bare' ' +test_expect_success !SANITIZE_LEAK '"soft" reset is allowed in bare' ' git reset --soft HEAD^ && - test "$(git show --pretty=format:%s | head -n 1)" = "one" + git show --pretty=format:%s >out && + echo one >expect && + head -n 1 out >actual && + test_cmp expect actual ' test_done diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh index 11cccbb333..000e055811 100755 --- a/t/t7406-submodule-update.sh +++ b/t/t7406-submodule-update.sh @@ -205,8 +205,18 @@ test_expect_success 'submodule update should fail due to local changes' ' (cd submodule && compare_head ) && - test_must_fail git submodule update submodule - ) + test_must_fail git submodule update submodule 2>../actual.raw + ) && + sed "s/^> //" >expect <<-\EOF && + > error: Your local changes to the following files would be overwritten by checkout: + > file + > Please commit your changes or stash them before you switch branches. + > Aborting + > fatal: Unable to checkout OID in submodule path '\''submodule'\'' + EOF + sed -e "s/checkout $SQ[^$SQ]*$SQ/checkout OID/" <actual.raw >actual && + test_cmp expect actual + ' test_expect_success 'submodule update should throw away changes with --force ' ' (cd super && @@ -1061,4 +1071,16 @@ test_expect_success 'submodule update --quiet passes quietness to fetch with a s ) ' +test_expect_success 'submodule update --filter requires --init' ' + test_expect_code 129 git -C super submodule update --filter blob:none +' + +test_expect_success 'submodule update --filter sets partial clone settings' ' + test_when_finished "rm -rf super-filter" && + git clone cloned super-filter && + git -C super-filter submodule update --init --filter blob:none && + test_cmp_config -C super-filter/submodule true remote.origin.promisor && + test_cmp_config -C super-filter/submodule blob:none remote.origin.partialclonefilter +' + test_done diff --git a/t/t7408-submodule-reference.sh b/t/t7408-submodule-reference.sh index a3892f494b..c3a4545510 100755 --- a/t/t7408-submodule-reference.sh +++ b/t/t7408-submodule-reference.sh @@ -193,7 +193,19 @@ test_expect_success 'missing nested submodule alternate fails clone and submodul cd supersuper-clone && check_that_two_of_three_alternates_are_used && # update of the submodule fails - test_must_fail git submodule update --init --recursive + cat >expect <<-\EOF && + fatal: submodule '\''sub'\'' cannot add alternate: path ... does not exist + Failed to clone '\''sub'\''. Retry scheduled + fatal: submodule '\''sub-dissociate'\'' cannot add alternate: path ... does not exist + Failed to clone '\''sub-dissociate'\''. Retry scheduled + fatal: submodule '\''sub'\'' cannot add alternate: path ... does not exist + Failed to clone '\''sub'\'' a second time, aborting + fatal: Failed to recurse into submodule path ... + EOF + test_must_fail git submodule update --init --recursive 2>err && + grep -e fatal: -e ^Failed err >actual.raw && + sed -e "s/path $SQ[^$SQ]*$SQ/path .../" <actual.raw >actual && + test_cmp expect actual ) ' diff --git a/t/t7500-commit-template-squash-signoff.sh b/t/t7500-commit-template-squash-signoff.sh index 91964653a0..5fcaa0b4f2 100755 --- a/t/t7500-commit-template-squash-signoff.sh +++ b/t/t7500-commit-template-squash-signoff.sh @@ -442,7 +442,7 @@ test_expect_success '--fixup=reword: give error with pathsec' ' ' test_expect_success '--fixup=reword: -F give error message' ' - echo "fatal: Only one of -c/-C/-F/--fixup can be used." >expect && + echo "fatal: options '\''-F'\'' and '\''--fixup'\'' cannot be used together" >expect && test_must_fail git commit --fixup=reword:HEAD~ -F msg 2>actual && test_cmp expect actual ' diff --git a/t/t7508-status.sh b/t/t7508-status.sh index 05c6c02435..2b7ef6c41a 100755 --- a/t/t7508-status.sh +++ b/t/t7508-status.sh @@ -1647,13 +1647,33 @@ test_expect_success '"Initial commit" should not be noted in commit template' ' ' test_expect_success '--no-optional-locks prevents index update' ' - test-tool chmtime =1234567890 .git/index && + test_set_magic_mtime .git/index && git --no-optional-locks status && - test-tool chmtime --get .git/index >out && - grep ^1234567890 out && + test_is_magic_mtime .git/index && git status && - test-tool chmtime --get .git/index >out && - ! grep ^1234567890 out + ! test_is_magic_mtime .git/index +' + +test_expect_success 'racy timestamps will be fixed for clean worktree' ' + echo content >racy-dirty && + echo content >racy-racy && + git add racy* && + git commit -m "racy test files" && + # let status rewrite the index, if necessary; after that we expect + # no more index writes unless caused by racy timestamps; note that + # timestamps may already be racy now (depending on previous tests) + git status && + test_set_magic_mtime .git/index && + git status && + ! test_is_magic_mtime .git/index +' + +test_expect_success 'racy timestamps will be fixed for dirty worktree' ' + echo content2 >racy-dirty && + git status && + test_set_magic_mtime .git/index && + git status && + ! test_is_magic_mtime .git/index ' test_done diff --git a/t/t7519-status-fsmonitor.sh b/t/t7519-status-fsmonitor.sh index a6308acf00..fffc57120d 100755 --- a/t/t7519-status-fsmonitor.sh +++ b/t/t7519-status-fsmonitor.sh @@ -324,17 +324,24 @@ test_expect_success UNTRACKED_CACHE 'ignore .git changes when invalidating UNTR' cd dot-git && mkdir -p .git/hooks && : >tracked && + test-tool chmtime =-60 tracked && : >modified && + test-tool chmtime =-60 modified && mkdir dir1 && : >dir1/tracked && + test-tool chmtime =-60 dir1/tracked && : >dir1/modified && + test-tool chmtime =-60 dir1/modified && mkdir dir2 && : >dir2/tracked && + test-tool chmtime =-60 dir2/tracked && : >dir2/modified && + test-tool chmtime =-60 dir2/modified && write_integration_script && git config core.fsmonitor .git/hooks/fsmonitor-test && git update-index --untracked-cache && git update-index --fsmonitor && + git status && GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace-before" \ git status && test-tool dump-untracked-cache >../before diff --git a/t/t7700-repack.sh b/t/t7700-repack.sh index a2f52a3faf..770d143204 100755 --- a/t/t7700-repack.sh +++ b/t/t7700-repack.sh @@ -312,16 +312,13 @@ test_expect_success 'cleans up MIDX when appropriate' ' checksum=$(midx_checksum $objdir) && test_path_is_file $midx && test_path_is_file $midx-$checksum.bitmap && - test_path_is_file $midx-$checksum.rev && test_commit repack-3 && GIT_TEST_MULTI_PACK_INDEX=0 git repack -Adb --write-midx && test_path_is_file $midx && test_path_is_missing $midx-$checksum.bitmap && - test_path_is_missing $midx-$checksum.rev && test_path_is_file $midx-$(midx_checksum $objdir).bitmap && - test_path_is_file $midx-$(midx_checksum $objdir).rev && test_commit repack-4 && GIT_TEST_MULTI_PACK_INDEX=0 git repack -Adb && @@ -354,7 +351,6 @@ test_expect_success '--write-midx with preferred bitmap tips' ' test_line_count = 1 before && rm -fr $midx-$(midx_checksum $objdir).bitmap && - rm -fr $midx-$(midx_checksum $objdir).rev && rm -fr $midx && # instead of constructing the snapshot ourselves (c.f., the test diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh index 424c31c328..6935601171 100755 --- a/t/t7810-grep.sh +++ b/t/t7810-grep.sh @@ -98,6 +98,37 @@ test_expect_success 'grep should not segfault with a bad input' ' test_invalid_grep_expression --and -e A +test_pattern_type () { + H=$1 && + HC=$2 && + L=$3 && + type=$4 && + shift 4 && + + expected_str= && + case "$type" in + BRE) + expected_str="${HC}ab:a+bc" + ;; + ERE) + expected_str="${HC}ab:abc" + ;; + FIX) + expected_str="${HC}ab:a+b*c" + ;; + *) + BUG "unknown pattern type '$type'" + ;; + esac && + config_str="$@" && + + test_expect_success "grep $L with '$config_str' interpreted as $type" ' + echo $expected_str >expected && + git $config_str grep "a+b*c" $H ab >actual && + test_cmp expected actual + ' +} + for H in HEAD '' do case "$H" in @@ -393,35 +424,13 @@ do git grep --no-recursive -n -e vvv $H -- t . >actual && test_cmp expected actual ' - test_expect_success "grep $L with grep.extendedRegexp=false" ' - echo "${HC}ab:a+bc" >expected && - git -c grep.extendedRegexp=false grep "a+b*c" $H ab >actual && - test_cmp expected actual - ' - test_expect_success "grep $L with grep.extendedRegexp=true" ' - echo "${HC}ab:abc" >expected && - git -c grep.extendedRegexp=true grep "a+b*c" $H ab >actual && - test_cmp expected actual - ' - test_expect_success "grep $L with grep.patterntype=basic" ' - echo "${HC}ab:a+bc" >expected && - git -c grep.patterntype=basic grep "a+b*c" $H ab >actual && - test_cmp expected actual - ' - - test_expect_success "grep $L with grep.patterntype=extended" ' - echo "${HC}ab:abc" >expected && - git -c grep.patterntype=extended grep "a+b*c" $H ab >actual && - test_cmp expected actual - ' - - test_expect_success "grep $L with grep.patterntype=fixed" ' - echo "${HC}ab:a+b*c" >expected && - git -c grep.patterntype=fixed grep "a+b*c" $H ab >actual && - test_cmp expected actual - ' + test_pattern_type "$H" "$HC" "$L" BRE -c grep.extendedRegexp=false + test_pattern_type "$H" "$HC" "$L" ERE -c grep.extendedRegexp=true + test_pattern_type "$H" "$HC" "$L" BRE -c grep.patternType=basic + test_pattern_type "$H" "$HC" "$L" ERE -c grep.patternType=extended + test_pattern_type "$H" "$HC" "$L" FIX -c grep.patternType=fixed test_expect_success PCRE "grep $L with grep.patterntype=perl" ' echo "${HC}ab:a+b*c" >expected && @@ -433,59 +442,76 @@ do test_must_fail git -c grep.patterntype=perl grep "foo.*bar" ' - test_expect_success "grep $L with grep.patternType=default and grep.extendedRegexp=true" ' - echo "${HC}ab:abc" >expected && - git \ - -c grep.patternType=default \ - -c grep.extendedRegexp=true \ - grep "a+b*c" $H ab >actual && - test_cmp expected actual - ' - - test_expect_success "grep $L with grep.extendedRegexp=true and grep.patternType=default" ' - echo "${HC}ab:abc" >expected && - git \ - -c grep.extendedRegexp=true \ - -c grep.patternType=default \ - grep "a+b*c" $H ab >actual && - test_cmp expected actual - ' - - test_expect_success "grep $L with grep.patternType=extended and grep.extendedRegexp=false" ' - echo "${HC}ab:abc" >expected && - git \ - -c grep.patternType=extended \ - -c grep.extendedRegexp=false \ - grep "a+b*c" $H ab >actual && - test_cmp expected actual - ' - - test_expect_success "grep $L with grep.patternType=basic and grep.extendedRegexp=true" ' - echo "${HC}ab:a+bc" >expected && - git \ - -c grep.patternType=basic \ - -c grep.extendedRegexp=true \ - grep "a+b*c" $H ab >actual && - test_cmp expected actual - ' - - test_expect_success "grep $L with grep.extendedRegexp=false and grep.patternType=extended" ' - echo "${HC}ab:abc" >expected && - git \ - -c grep.extendedRegexp=false \ - -c grep.patternType=extended \ - grep "a+b*c" $H ab >actual && - test_cmp expected actual - ' - - test_expect_success "grep $L with grep.extendedRegexp=true and grep.patternType=basic" ' - echo "${HC}ab:a+bc" >expected && - git \ - -c grep.extendedRegexp=true \ - -c grep.patternType=basic \ - grep "a+b*c" $H ab >actual && - test_cmp expected actual - ' + test_pattern_type "$H" "$HC" "$L" ERE \ + -c grep.patternType=default \ + -c grep.extendedRegexp=true + test_pattern_type "$H" "$HC" "$L" ERE \ + -c grep.extendedRegexp=true \ + -c grep.patternType=default + test_pattern_type "$H" "$HC" "$L" ERE \ + -c grep.patternType=extended \ + -c grep.extendedRegexp=false + test_pattern_type "$H" "$HC" "$L" BRE \ + -c grep.patternType=basic \ + -c grep.extendedRegexp=true + test_pattern_type "$H" "$HC" "$L" ERE \ + -c grep.extendedRegexp=false \ + -c grep.patternType=extended + test_pattern_type "$H" "$HC" "$L" BRE \ + -c grep.extendedRegexp=true \ + -c grep.patternType=basic + + # grep.extendedRegexp is last-one-wins + test_pattern_type "$H" "$HC" "$L" BRE \ + -c grep.extendedRegexp=true \ + -c grep.extendedRegexp=false + + # grep.patternType=basic pays no attention to grep.extendedRegexp + test_pattern_type "$H" "$HC" "$L" BRE \ + -c grep.extendedRegexp=true \ + -c grep.patternType=basic \ + -c grep.extendedRegexp=false + + # grep.patternType=extended pays no attention to grep.extendedRegexp + test_pattern_type "$H" "$HC" "$L" ERE \ + -c grep.extendedRegexp=true \ + -c grep.patternType=extended \ + -c grep.extendedRegexp=false + + # grep.extendedRegexp is used with a last-one-wins grep.patternType=default + test_pattern_type "$H" "$HC" "$L" ERE \ + -c grep.patternType=fixed \ + -c grep.extendedRegexp=true \ + -c grep.patternType=default + + # grep.extendedRegexp is used with earlier grep.patternType=default + test_pattern_type "$H" "$HC" "$L" ERE \ + -c grep.extendedRegexp=false \ + -c grep.patternType=default \ + -c grep.extendedRegexp=true + + # grep.extendedRegexp is used with a last-one-loses grep.patternType=default + test_pattern_type "$H" "$HC" "$L" ERE \ + -c grep.extendedRegexp=false \ + -c grep.extendedRegexp=true \ + -c grep.patternType=default + + # grep.extendedRegexp and grep.patternType are both last-one-wins independently + test_pattern_type "$H" "$HC" "$L" BRE \ + -c grep.patternType=default \ + -c grep.extendedRegexp=true \ + -c grep.patternType=basic + + # grep.patternType=extended and grep.patternType=default + test_pattern_type "$H" "$HC" "$L" BRE \ + -c grep.patternType=extended \ + -c grep.patternType=default + + # grep.patternType=[extended -> default -> fixed] (BRE)" ' + test_pattern_type "$H" "$HC" "$L" FIX \ + -c grep.patternType=extended \ + -c grep.patternType=default \ + -c grep.patternType=fixed test_expect_success "grep --count $L" ' echo ${HC}ab:3 >expected && diff --git a/t/t7812-grep-icase-non-ascii.sh b/t/t7812-grep-icase-non-ascii.sh index ca3f24f807..9047d665a1 100755 --- a/t/t7812-grep-icase-non-ascii.sh +++ b/t/t7812-grep-icase-non-ascii.sh @@ -11,9 +11,19 @@ test_expect_success GETTEXT_LOCALE 'setup' ' export LC_ALL ' -test_have_prereq GETTEXT_LOCALE && -test-tool regex "HALLÓ" "Halló" ICASE && -test_set_prereq REGEX_LOCALE +test_expect_success GETTEXT_LOCALE 'setup REGEX_LOCALE prerequisite' ' + # This "test-tool" invocation is identical... + if test-tool regex "HALLÓ" "Halló" ICASE + then + test_set_prereq REGEX_LOCALE + else + + # ... to this one, but this way "test_must_fail" will + # tell a segfault or abort() from the regexec() test + # itself + test_must_fail test-tool regex "HALLÓ" "Halló" ICASE + fi +' test_expect_success REGEX_LOCALE 'grep literal string, no -F' ' git grep -i "TILRAUN: Halló Heimur!" && diff --git a/t/t7814-grep-recurse-submodules.sh b/t/t7814-grep-recurse-submodules.sh index 058e5d0c96..a4476dc492 100755 --- a/t/t7814-grep-recurse-submodules.sh +++ b/t/t7814-grep-recurse-submodules.sh @@ -544,4 +544,45 @@ test_expect_failure 'grep saves textconv cache in the appropriate repository' ' test_path_is_file "$sub_textconv_cache" ' +test_expect_success 'grep partially-cloned submodule' ' + # Set up clean superproject and submodule for partial cloning. + git init super && + git init super/sub && + ( + cd super && + test_commit --no-tag "Add file in superproject" \ + super-file "Some content for super-file" && + test_commit -C sub --no-tag "Add file in submodule" \ + sub-file "Some content for sub-file" && + git submodule add ./sub && + git commit -m "Add other as submodule sub" && + test_tick && + test_commit -C sub --no-tag --append "Update file in submodule" \ + sub-file "Some more content for sub-file" && + git add sub && + git commit -m "Update submodule" && + test_tick && + git config --local uploadpack.allowfilter 1 && + git config --local uploadpack.allowanysha1inwant 1 && + git -C sub config --local uploadpack.allowfilter 1 && + git -C sub config --local uploadpack.allowanysha1inwant 1 + ) && + # Clone the superproject & submodule, then make sure we can lazy-fetch submodule objects. + git clone --filter=blob:none --also-filter-submodules \ + --recurse-submodules "file://$(pwd)/super" partial && + ( + cd partial && + cat >expect <<-\EOF && + HEAD^:sub/sub-file:Some content for sub-file + HEAD^:super-file:Some content for super-file + EOF + + GIT_TRACE2_EVENT="$(pwd)/trace2.log" git grep -e content \ + --recurse-submodules HEAD^ >actual && + test_cmp expect actual && + # Verify that we actually fetched data from the promisor remote: + grep \"category\":\"promisor\",\"key\":\"fetch_count\",\"value\":\"1\" trace2.log + ) +' + test_done diff --git a/t/t7817-grep-sparse-checkout.sh b/t/t7817-grep-sparse-checkout.sh index 590b99bbb6..eb59564565 100755 --- a/t/t7817-grep-sparse-checkout.sh +++ b/t/t7817-grep-sparse-checkout.sh @@ -83,10 +83,13 @@ test_expect_success 'setup' ' # The test below covers a special case: the sparsity patterns exclude '/b' and # sparse checkout is enabled, but the path exists in the working tree (e.g. -# manually created after `git sparse-checkout init`). git grep should skip it. +# manually created after `git sparse-checkout init`). Although b is marked +# as SKIP_WORKTREE, git grep should notice it IS present in the worktree and +# report it. test_expect_success 'working tree grep honors sparse checkout' ' cat >expect <<-EOF && a:text + b:new-text EOF test_when_finished "rm -f b" && echo "new-text" >b && @@ -126,12 +129,16 @@ test_expect_success 'grep --cached searches entries with the SKIP_WORKTREE bit' ' # Note that sub2/ is present in the worktree but it is excluded by the sparsity -# patterns, so grep should not recurse into it. +# patterns. We also explicitly mark it as SKIP_WORKTREE in case it got cleared +# by previous git commands. Thus sub2 starts as SKIP_WORKTREE but since it is +# present in the working tree, grep should recurse into it. test_expect_success 'grep --recurse-submodules honors sparse checkout in submodule' ' cat >expect <<-EOF && a:text sub/B/b:text + sub2/a:text EOF + git update-index --skip-worktree sub2 && git grep --recurse-submodules "text" >actual && test_cmp expect actual ' diff --git a/t/t8007-cat-file-textconv.sh b/t/t8007-cat-file-textconv.sh index eacd49ade6..b067983ba1 100755 --- a/t/t8007-cat-file-textconv.sh +++ b/t/t8007-cat-file-textconv.sh @@ -19,6 +19,48 @@ test_expect_success 'setup ' ' GIT_AUTHOR_NAME=Number2 git commit -a -m Second --date="2010-01-01 20:00:00" ' +test_expect_success 'usage: <bad rev>' ' + cat >expect <<-\EOF && + fatal: Not a valid object name HEAD2 + EOF + test_must_fail git cat-file --textconv HEAD2 2>actual && + test_cmp expect actual +' + +test_expect_success 'usage: <bad rev>:<bad path>' ' + cat >expect <<-\EOF && + fatal: invalid object name '\''HEAD2'\''. + EOF + test_must_fail git cat-file --textconv HEAD2:two.bin 2>actual && + test_cmp expect actual +' + +test_expect_success 'usage: <rev>:<bad path>' ' + cat >expect <<-\EOF && + fatal: path '\''two.bin'\'' does not exist in '\''HEAD'\'' + EOF + test_must_fail git cat-file --textconv HEAD:two.bin 2>actual && + test_cmp expect actual +' + + +test_expect_success 'usage: <rev> with no <path>' ' + cat >expect <<-\EOF && + fatal: <object>:<path> required, only <object> '\''HEAD'\'' given + EOF + test_must_fail git cat-file --textconv HEAD 2>actual && + test_cmp expect actual +' + + +test_expect_success 'usage: <bad rev>:<good (in HEAD) path>' ' + cat >expect <<-\EOF && + fatal: invalid object name '\''HEAD2'\''. + EOF + test_must_fail git cat-file --textconv HEAD2:one.bin 2>actual && + test_cmp expect actual +' + cat >expected <<EOF bin: test version 2 EOF diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index aa0c20499b..84d0f40d76 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -539,7 +539,7 @@ test_expect_success $PREREQ "--validate respects relative core.hooksPath path" ' test_path_is_file my-hooks.ran && cat >expect <<-EOF && fatal: longline.patch: rejected by sendemail-validate hook - fatal: command '"'"'my-hooks/sendemail-validate'"'"' died with exit code 1 + fatal: command '"'"'git hook run --ignore-missing sendemail-validate -- <patch>'"'"' died with exit code 1 warning: no patches were sent EOF test_cmp expect actual @@ -558,7 +558,7 @@ test_expect_success $PREREQ "--validate respects absolute core.hooksPath path" ' test_path_is_file my-hooks.ran && cat >expect <<-EOF && fatal: longline.patch: rejected by sendemail-validate hook - fatal: command '"'"'$hooks_path/sendemail-validate'"'"' died with exit code 1 + fatal: command '"'"'git hook run --ignore-missing sendemail-validate -- <patch>'"'"' died with exit code 1 warning: no patches were sent EOF test_cmp expect actual diff --git a/t/t9102-git-svn-deep-rmdir.sh b/t/t9102-git-svn-deep-rmdir.sh index 7b2049caa0..946ef85eb9 100755 --- a/t/t9102-git-svn-deep-rmdir.sh +++ b/t/t9102-git-svn-deep-rmdir.sh @@ -1,7 +1,6 @@ #!/bin/sh test_description='git svn rmdir' -TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-svn.sh test_expect_success 'initialize repo' ' diff --git a/t/t9123-git-svn-rebuild-with-rewriteroot.sh b/t/t9123-git-svn-rebuild-with-rewriteroot.sh index 3320b1f39c..ead404589e 100755 --- a/t/t9123-git-svn-rebuild-with-rewriteroot.sh +++ b/t/t9123-git-svn-rebuild-with-rewriteroot.sh @@ -5,7 +5,6 @@ test_description='git svn respects rewriteRoot during rebuild' -TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-svn.sh mkdir import diff --git a/t/t9128-git-svn-cmd-branch.sh b/t/t9128-git-svn-cmd-branch.sh index 9871f5abc9..783e3ba0c5 100755 --- a/t/t9128-git-svn-cmd-branch.sh +++ b/t/t9128-git-svn-cmd-branch.sh @@ -5,7 +5,6 @@ test_description='git svn partial-rebuild tests' -TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-svn.sh test_expect_success 'initialize svnrepo' ' diff --git a/t/t9167-git-svn-cmd-branch-subproject.sh b/t/t9167-git-svn-cmd-branch-subproject.sh index d9fd111c10..d8128430a8 100755 --- a/t/t9167-git-svn-cmd-branch-subproject.sh +++ b/t/t9167-git-svn-cmd-branch-subproject.sh @@ -5,7 +5,6 @@ test_description='git svn branch for subproject clones' -TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-svn.sh test_expect_success 'initialize svnrepo' ' diff --git a/t/t9502-gitweb-standalone-parse-output.sh b/t/t9502-gitweb-standalone-parse-output.sh index 3167473b30..8cb582f0e6 100755 --- a/t/t9502-gitweb-standalone-parse-output.sh +++ b/t/t9502-gitweb-standalone-parse-output.sh @@ -34,7 +34,7 @@ EOF # # This will check that gitweb HTTP header contains proposed filename # as <basename> with '.tar' suffix added, and that generated tarfile -# (gitweb message body) has <prefix> as prefix for al files in tarfile +# (gitweb message body) has <prefix> as prefix for all files in tarfile # # <prefix> default to <basename> check_snapshot () { @@ -207,4 +207,17 @@ test_expect_success 'xss checks' ' xss "" "$TAG+" ' +no_http_equiv_content_type() { + gitweb_run "$@" && + ! grep -E "http-equiv=['\"]?content-type" gitweb.body +} + +# See: <https://html.spec.whatwg.org/dev/semantics.html#attr-meta-http-equiv-content-type> +test_expect_success 'no http-equiv="content-type" in XHTML' ' + no_http_equiv_content_type && + no_http_equiv_content_type "p=.git" && + no_http_equiv_content_type "p=.git;a=log" && + no_http_equiv_content_type "p=.git;a=tree" +' + test_done diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh index 98c6280632..24117cb901 100755 --- a/t/t9902-completion.sh +++ b/t/t9902-completion.sh @@ -1444,6 +1444,144 @@ test_expect_success 'git checkout - with --detach, complete only references' ' EOF ' +test_expect_success 'setup sparse-checkout tests' ' + # set up sparse-checkout repo + git init sparse-checkout && + ( + cd sparse-checkout && + mkdir -p folder1/0/1 folder2/0 folder3 && + touch folder1/0/1/t.txt && + touch folder2/0/t.txt && + touch folder3/t.txt && + git add . && + git commit -am "Initial commit" + ) +' + +test_expect_success 'sparse-checkout completes subcommands' ' + test_completion "git sparse-checkout " <<-\EOF + list Z + init Z + set Z + add Z + reapply Z + disable Z + EOF +' + +test_expect_success 'cone mode sparse-checkout completes directory names' ' + # initialize sparse-checkout definitions + git -C sparse-checkout sparse-checkout set --cone folder1/0 folder3 && + + # test tab completion + ( + cd sparse-checkout && + test_completion "git sparse-checkout set f" <<-\EOF + folder1/ + folder2/ + folder3/ + EOF + ) && + + ( + cd sparse-checkout && + test_completion "git sparse-checkout set folder1/" <<-\EOF + folder1/0/ + EOF + ) && + + ( + cd sparse-checkout && + test_completion "git sparse-checkout set folder1/0/" <<-\EOF + folder1/0/1/ + EOF + ) && + + ( + cd sparse-checkout/folder1 && + test_completion "git sparse-checkout add 0" <<-\EOF + 0/ + EOF + ) +' + +test_expect_success 'cone mode sparse-checkout completes directory names with spaces and accents' ' + # reset sparse-checkout + git -C sparse-checkout sparse-checkout disable && + ( + cd sparse-checkout && + mkdir "directory with spaces" && + mkdir "directory-with-áccent" && + >"directory with spaces/randomfile" && + >"directory-with-áccent/randomfile" && + git add . && + git commit -m "Add directory with spaces and directory with accent" && + git sparse-checkout set --cone "directory with spaces" \ + "directory-with-áccent" && + test_completion "git sparse-checkout add dir" <<-\EOF && + directory with spaces/ + directory-with-áccent/ + EOF + rm -rf "directory with spaces" && + rm -rf "directory-with-áccent" && + git add . && + git commit -m "Remove directory with spaces and directory with accent" + ) +' + +# use FUNNYNAMES to avoid running on Windows, which doesn't permit backslashes or tabs in paths +test_expect_success FUNNYNAMES 'cone mode sparse-checkout completes directory names with backslashes and tabs' ' + # reset sparse-checkout + git -C sparse-checkout sparse-checkout disable && + ( + cd sparse-checkout && + mkdir "directory\with\backslashes" && + mkdir "$(printf "directory\twith\ttabs")" && + >"directory\with\backslashes/randomfile" && + >"$(printf "directory\twith\ttabs")/randomfile" && + git add . && + git commit -m "Add directory with backslashes and directory with tabs" && + git sparse-checkout set --cone "directory\with\backslashes" \ + "$(printf "directory\twith\ttabs")" && + test_completion "git sparse-checkout add dir" <<-\EOF && + directory\with\backslashes/ + directory with tabs/ + EOF + rm -rf "directory\with\backslashes" && + rm -rf "$(printf "directory\twith\ttabs")" && + git add . && + git commit -m "Remove directory with backslashes and directory with tabs" + ) +' + +test_expect_success 'non-cone mode sparse-checkout uses bash completion' ' + # reset sparse-checkout repo to non-cone mode + git -C sparse-checkout sparse-checkout disable && + git -C sparse-checkout sparse-checkout set --no-cone && + + ( + cd sparse-checkout && + # expected to be empty since we have not configured + # custom completion for non-cone mode + test_completion "git sparse-checkout set f" <<-\EOF + + EOF + ) +' + +test_expect_success 'git sparse-checkout set --cone completes directory names' ' + git -C sparse-checkout sparse-checkout disable && + + ( + cd sparse-checkout && + test_completion "git sparse-checkout set --cone f" <<-\EOF + folder1/ + folder2/ + folder3/ + EOF + ) +' + test_expect_success 'git switch - with -d, complete all references' ' test_completion "git switch -d " <<-\EOF HEAD Z @@ -2396,27 +2534,33 @@ test_expect_success 'options with value' ' ' test_expect_success 'sourcing the completion script clears cached commands' ' - __git_compute_all_commands && - verbose test -n "$__git_all_commands" && - . "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" && - verbose test -z "$__git_all_commands" + ( + __git_compute_all_commands && + verbose test -n "$__git_all_commands" && + . "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" && + verbose test -z "$__git_all_commands" + ) ' test_expect_success 'sourcing the completion script clears cached merge strategies' ' - __git_compute_merge_strategies && - verbose test -n "$__git_merge_strategies" && - . "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" && - verbose test -z "$__git_merge_strategies" + ( + __git_compute_merge_strategies && + verbose test -n "$__git_merge_strategies" && + . "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" && + verbose test -z "$__git_merge_strategies" + ) ' test_expect_success 'sourcing the completion script clears cached --options' ' - __gitcomp_builtin checkout && - verbose test -n "$__gitcomp_builtin_checkout" && - __gitcomp_builtin notes_edit && - verbose test -n "$__gitcomp_builtin_notes_edit" && - . "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" && - verbose test -z "$__gitcomp_builtin_checkout" && - verbose test -z "$__gitcomp_builtin_notes_edit" + ( + __gitcomp_builtin checkout && + verbose test -n "$__gitcomp_builtin_checkout" && + __gitcomp_builtin notes_edit && + verbose test -n "$__gitcomp_builtin_notes_edit" && + . "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" && + verbose test -z "$__gitcomp_builtin_checkout" && + verbose test -z "$__gitcomp_builtin_notes_edit" + ) ' test_expect_success 'option aliases are not shown by default' ' @@ -2424,12 +2568,45 @@ test_expect_success 'option aliases are not shown by default' ' ' test_expect_success 'option aliases are shown with GIT_COMPLETION_SHOW_ALL' ' - . "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" && - GIT_COMPLETION_SHOW_ALL=1 && export GIT_COMPLETION_SHOW_ALL && - test_completion "git clone --recurs" <<-\EOF - --recurse-submodules Z - --recursive Z - EOF + ( + . "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" && + GIT_COMPLETION_SHOW_ALL=1 && export GIT_COMPLETION_SHOW_ALL && + test_completion "git clone --recurs" <<-\EOF + --recurse-submodules Z + --recursive Z + EOF + ) +' + +test_expect_success 'plumbing commands are excluded without GIT_COMPLETION_SHOW_ALL_COMMANDS' ' + ( + . "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" && + sane_unset GIT_TESTING_PORCELAIN_COMMAND_LIST && + + # Just mainporcelain, not plumbing commands + run_completion "git c" && + grep checkout out && + ! grep cat-file out + ) +' + +test_expect_success 'all commands are shown with GIT_COMPLETION_SHOW_ALL_COMMANDS (also main non-builtin)' ' + ( + . "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" && + GIT_COMPLETION_SHOW_ALL_COMMANDS=1 && + export GIT_COMPLETION_SHOW_ALL_COMMANDS && + sane_unset GIT_TESTING_PORCELAIN_COMMAND_LIST && + + # Both mainporcelain and plumbing commands + run_completion "git c" && + grep checkout out && + grep cat-file out && + + # Check "gitk", a "main" command, but not a built-in + more plumbing + run_completion "git g" && + grep gitk out && + grep get-tar-commit-id out + ) ' test_expect_success '__git_complete' ' diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh index c3d38aaccb..0f439c99d6 100644 --- a/t/test-lib-functions.sh +++ b/t/test-lib-functions.sh @@ -856,6 +856,16 @@ test_path_is_file () { fi } +test_path_is_file_not_symlink () { + test "$#" -ne 1 && BUG "1 param" + test_path_is_file "$1" && + if test -h "$1" + then + echo "$1 shouldn't be a symbolic link" + false + fi +} + test_path_is_dir () { test "$#" -ne 1 && BUG "1 param" if ! test -d "$1" @@ -865,6 +875,16 @@ test_path_is_dir () { fi } +test_path_is_dir_not_symlink () { + test "$#" -ne 1 && BUG "1 param" + test_path_is_dir "$1" && + if test -h "$1" + then + echo "$1 shouldn't be a symbolic link" + false + fi +} + test_path_exists () { test "$#" -ne 1 && BUG "1 param" if ! test -e "$1" @@ -874,6 +894,15 @@ test_path_exists () { fi } +test_path_is_symlink () { + test "$#" -ne 1 && BUG "1 param" + if ! test -h "$1" + then + echo "Symbolic link $1 doesn't exist" + false + fi +} + # Check if the directory exists and is empty as expected, barf otherwise. test_dir_is_empty () { test "$#" -ne 1 && BUG "1 param" @@ -1840,3 +1869,36 @@ test_region () { test_readlink () { perl -le 'print readlink($_) for @ARGV' "$@" } + +# Set mtime to a fixed "magic" timestamp in mid February 2009, before we +# run an operation that may or may not touch the file. If the file was +# touched, its timestamp will not accidentally have such an old timestamp, +# as long as your filesystem clock is reasonably correct. To verify the +# timestamp, follow up with test_is_magic_mtime. +# +# An optional increment to the magic timestamp may be specified as second +# argument. +test_set_magic_mtime () { + local inc=${2:-0} && + local mtime=$((1234567890 + $inc)) && + test-tool chmtime =$mtime "$1" && + test_is_magic_mtime "$1" $inc +} + +# Test whether the given file has the "magic" mtime set. This is meant to +# be used in combination with test_set_magic_mtime. +# +# An optional increment to the magic timestamp may be specified as second +# argument. Usually, this should be the same increment which was used for +# the associated test_set_magic_mtime. +test_is_magic_mtime () { + local inc=${2:-0} && + local mtime=$((1234567890 + $inc)) && + echo $mtime >.git/test-mtime-expect && + test-tool chmtime --get "$1" >.git/test-mtime-actual && + test_cmp .git/test-mtime-expect .git/test-mtime-actual + local ret=$? + rm -f .git/test-mtime-expect + rm -f .git/test-mtime-actual + return $ret +} diff --git a/t/test-lib.sh b/t/test-lib.sh index 0f7a137c7d..515b1af7ed 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -19,13 +19,20 @@ # t/ subdirectory and are run in 'trash directory' subdirectory. if test -z "$TEST_DIRECTORY" then - # We allow tests to override this, in case they want to run tests - # outside of t/, e.g. for running tests on the test library - # itself. - TEST_DIRECTORY=$(pwd) -else # ensure that TEST_DIRECTORY is an absolute path so that it # is valid even if the current working directory is changed + TEST_DIRECTORY=$(pwd) +else + # The TEST_DIRECTORY will always be the path to the "t" + # directory in the git.git checkout. This is overridden by + # e.g. t/lib-subtest.sh, but only because its $(pwd) is + # different. Those tests still set "$TEST_DIRECTORY" to the + # same path. + # + # See use of "$GIT_BUILD_DIR" and "$TEST_DIRECTORY" below for + # hard assumptions about "$GIT_BUILD_DIR/t" existing and being + # the "$TEST_DIRECTORY", and e.g. "$TEST_DIRECTORY/helper" + # needing to exist. TEST_DIRECTORY=$(cd "$TEST_DIRECTORY" && pwd) || exit 1 fi if test -z "$TEST_OUTPUT_DIRECTORY" @@ -34,19 +41,42 @@ then # elsewhere TEST_OUTPUT_DIRECTORY=$TEST_DIRECTORY fi -GIT_BUILD_DIR="$TEST_DIRECTORY"/.. +GIT_BUILD_DIR="${TEST_DIRECTORY%/t}" +if test "$TEST_DIRECTORY" = "$GIT_BUILD_DIR" +then + echo "PANIC: Running in a $TEST_DIRECTORY that doesn't end in '/t'?" >&2 + exit 1 +fi + +# Prepend a string to a VAR using an arbitrary ":" delimiter, not +# adding the delimiter if VAR or VALUE is empty. I.e. a generalized: +# +# VAR=$1${VAR:+${1:+$2}$VAR} +# +# Usage (using ":" as the $2 delimiter): +# +# prepend_var VAR : VALUE +prepend_var () { + eval "$1=$3\${$1:+${3:+$2}\$$1}" +} + +# If [AL]SAN is in effect we want to abort so that we notice +# problems. The GIT_SAN_OPTIONS variable can be used to set common +# defaults shared between [AL]SAN_OPTIONS. +prepend_var GIT_SAN_OPTIONS : abort_on_error=1 +prepend_var GIT_SAN_OPTIONS : strip_path_prefix=\"$GIT_BUILD_DIR/\" # If we were built with ASAN, it may complain about leaks # of program-lifetime variables. Disable it by default to lower # the noise level. This needs to happen at the start of the script, # before we even do our "did we build git yet" check (since we don't # want that one to complain to stderr). -: ${ASAN_OPTIONS=detect_leaks=0:abort_on_error=1} +prepend_var ASAN_OPTIONS : $GIT_SAN_OPTIONS +prepend_var ASAN_OPTIONS : detect_leaks=0 export ASAN_OPTIONS -# If LSAN is in effect we _do_ want leak checking, but we still -# want to abort so that we notice the problems. -: ${LSAN_OPTIONS=abort_on_error=1} +prepend_var LSAN_OPTIONS : $GIT_SAN_OPTIONS +prepend_var LSAN_OPTIONS : fast_unwind_on_malloc=0 export LSAN_OPTIONS if test ! -f "$GIT_BUILD_DIR"/GIT-BUILD-OPTIONS @@ -449,6 +479,8 @@ unset VISUAL EMAIL LANGUAGE $("$PERL_PATH" -e ' unset XDG_CACHE_HOME unset XDG_CONFIG_HOME unset GITPERLLIB +unset GIT_TRACE2_PARENT_NAME +unset GIT_TRACE2_PARENT_SID TEST_AUTHOR_LOCALNAME=author TEST_AUTHOR_DOMAIN=example.com GIT_AUTHOR_EMAIL=${TEST_AUTHOR_LOCALNAME}@${TEST_AUTHOR_DOMAIN} @@ -516,11 +548,29 @@ then } else setup_malloc_check () { + local g + local t MALLOC_CHECK_=3 MALLOC_PERTURB_=165 export MALLOC_CHECK_ MALLOC_PERTURB_ + if _GLIBC_VERSION=$(getconf GNU_LIBC_VERSION 2>/dev/null) && + _GLIBC_VERSION=${_GLIBC_VERSION#"glibc "} && + expr 2.34 \<= "$_GLIBC_VERSION" >/dev/null + then + g= + LD_PRELOAD="libc_malloc_debug.so.0" + for t in \ + glibc.malloc.check=1 \ + glibc.malloc.perturb=165 + do + g="${g#:}:$t" + done + GLIBC_TUNABLES=$g + export LD_PRELOAD GLIBC_TUNABLES + fi } teardown_malloc_check () { unset MALLOC_CHECK_ MALLOC_PERTURB_ + unset LD_PRELOAD GLIBC_TUNABLES } fi |