diff options
Diffstat (limited to 't')
103 files changed, 3924 insertions, 348 deletions
diff --git a/t/.gitignore b/t/.gitignore index 348715f0e4..91cf5772fe 100644 --- a/t/.gitignore +++ b/t/.gitignore @@ -2,3 +2,4 @@ /test-results /.prove /chainlinttmp +/out/ @@ -170,6 +170,15 @@ appropriately before running "make". implied by other options like --valgrind and GIT_TEST_INSTALLED. +--no-bin-wrappers:: + By default, the test suite uses the wrappers in + `../bin-wrappers/` to execute `git` and friends. With this option, + `../git` and friends are run directly. This is not recommended + in general, as the wrappers contain safeguards to ensure that no + files from an installed Git are used, but can speed up test runs + especially on platforms where running shell scripts is expensive + (most notably, Windows). + --root=<directory>:: Create "trash" directories used to store all temporary data during testing under <directory>, instead of the t/ directory. @@ -202,6 +211,11 @@ appropriately before running "make". '.stress-<nr>' suffix, and the trash directory of the failed test job is renamed to end with a '.stress-failed' suffix. +--stress-limit=<N>:: + When combined with --stress run the test script repeatedly + this many times in each of the parallel jobs or until one of + them fails, whichever comes first. + You can also set the GIT_TEST_INSTALLED environment variable to the bindir of an existing git installation to test that installation. You still need to have built this git sandbox, from which various @@ -358,6 +372,10 @@ GIT_TEST_INDEX_VERSION=<n> exercises the index read/write code path for the index version specified. Can be set to any valid version (currently 2, 3, or 4). +GIT_TEST_PACK_SPARSE=<boolean> if enabled will default the pack-objects +builtin to use the sparse object walk. This can still be overridden by +the --no-sparse command-line argument. + GIT_TEST_PRELOAD_INDEX=<boolean> exercises the preload-index code path by overriding the minimum number of cache entries required per thread. @@ -374,6 +392,11 @@ GIT_TEST_MULTI_PACK_INDEX=<boolean>, when true, forces the multi-pack- index to be written after every 'git repack' command, and overrides the 'core.multiPackIndex' setting to true. +GIT_TEST_SIDEBAND_ALL=<boolean>, when true, overrides the +'uploadpack.allowSidebandAll' setting to true, and when false, forces +fetch-pack to not request sideband-all (even if the server advertises +sideband-all). + Naming Tests ------------ @@ -909,6 +932,15 @@ library for your script to use. test_oid_init or test_oid_cache. Providing an unknown key is an error. + - yes [<string>] + + This is often seen in modern UNIX but some platforms lack it, so + the test harness overrides the platform implementation with a + more limited one. Use this only when feeding a handful lines of + output to the downstream---unlike the real version, it generates + only up to 99 lines. + + Prerequisites ------------- diff --git a/t/check-non-portable-shell.pl b/t/check-non-portable-shell.pl index 8037eef777..166d64d4a2 100755 --- a/t/check-non-portable-shell.pl +++ b/t/check-non-portable-shell.pl @@ -36,7 +36,7 @@ while (<>) { } /\bcp\s+-a/ and err 'cp -a is not portable'; - /\bsed\s+-i/ and err 'sed -i is not portable'; + /\bsed\s+-[^efn]\s+/ and err 'sed option not portable (use only -n, -e, -f)'; /\becho\s+-[neE]/ and err 'echo with option is not portable (use printf)'; /^\s*declare\s+/ and err 'arrays/declare not portable'; /^\s*[^#]\s*which\s/ and err 'which is not portable (use type)'; diff --git a/t/helper/test-date.c b/t/helper/test-date.c index a0837371ab..b3253803ac 100644 --- a/t/helper/test-date.c +++ b/t/helper/test-date.c @@ -3,10 +3,12 @@ static const char *usage_msg = "\n" " test-tool date relative [time_t]...\n" +" test-tool date human [time_t]...\n" " test-tool date show:<format> [time_t]...\n" " test-tool date parse [date]...\n" " test-tool date approxidate [date]...\n" " test-tool date timestamp [date]...\n" +" test-tool date getnanos [start-nanos]\n" " test-tool date is64bit\n" " test-tool date time_t-is64bit\n"; @@ -16,12 +18,20 @@ static void show_relative_dates(const char **argv, struct timeval *now) for (; *argv; argv++) { time_t t = atoi(*argv); - show_date_relative(t, 0, now, &buf); + show_date_relative(t, now, &buf); printf("%s -> %s\n", *argv, buf.buf); } strbuf_release(&buf); } +static void show_human_dates(const char **argv) +{ + for (; *argv; argv++) { + time_t t = atoi(*argv); + printf("%s -> %s\n", *argv, show_date(t, 0, DATE_MODE(HUMAN))); + } +} + static void show_dates(const char **argv, const char *format) { struct date_mode mode; @@ -82,12 +92,21 @@ static void parse_approx_timestamp(const char **argv, struct timeval *now) } } +static void getnanos(const char **argv) +{ + double seconds = getnanotime() / 1.0e9; + + if (*argv) + seconds -= strtod(*argv, NULL); + printf("%lf\n", seconds); +} + int cmd__date(int argc, const char **argv) { struct timeval now; const char *x; - x = getenv("TEST_DATE_NOW"); + x = getenv("GIT_TEST_DATE_NOW"); if (x) { now.tv_sec = atoi(x); now.tv_usec = 0; @@ -100,6 +119,8 @@ int cmd__date(int argc, const char **argv) usage(usage_msg); if (!strcmp(*argv, "relative")) show_relative_dates(argv+1, &now); + else if (!strcmp(*argv, "human")) + show_human_dates(argv+1); else if (skip_prefix(*argv, "show:", &x)) show_dates(argv+1, x); else if (!strcmp(*argv, "parse")) @@ -108,6 +129,8 @@ int cmd__date(int argc, const char **argv) parse_approxidate(argv+1, &now); else if (!strcmp(*argv, "timestamp")) parse_approx_timestamp(argv+1, &now); + else if (!strcmp(*argv, "getnanos")) + getnanos(argv+1); else if (!strcmp(*argv, "is64bit")) return sizeof(timestamp_t) == 8 ? 0 : 1; else if (!strcmp(*argv, "time_t-is64bit")) diff --git a/t/helper/test-dump-fsmonitor.c b/t/helper/test-dump-fsmonitor.c index 08e3684aff..2786f47088 100644 --- a/t/helper/test-dump-fsmonitor.c +++ b/t/helper/test-dump-fsmonitor.c @@ -3,11 +3,11 @@ int cmd__dump_fsmonitor(int ac, const char **av) { - struct index_state *istate = &the_index; + struct index_state *istate = the_repository->index; int i; setup_git_directory(); - if (do_read_index(istate, get_index_file(), 0) < 0) + if (do_read_index(istate, the_repository->index_file, 0) < 0) die("unable to read index file"); if (!istate->fsmonitor_last_update) { printf("no fsmonitor\n"); diff --git a/t/helper/test-dump-untracked-cache.c b/t/helper/test-dump-untracked-cache.c index 52870ebbb3..cf0f2c7228 100644 --- a/t/helper/test-dump-untracked-cache.c +++ b/t/helper/test-dump-untracked-cache.c @@ -1,3 +1,4 @@ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "test-tool.h" #include "cache.h" #include "dir.h" diff --git a/t/helper/test-genzeros.c b/t/helper/test-genzeros.c new file mode 100644 index 0000000000..9532f5bac9 --- /dev/null +++ b/t/helper/test-genzeros.c @@ -0,0 +1,21 @@ +#include "test-tool.h" +#include "git-compat-util.h" + +int cmd__genzeros(int argc, const char **argv) +{ + long count; + + if (argc > 2) { + fprintf(stderr, "usage: %s [<count>]\n", argv[0]); + return 1; + } + + count = argc > 1 ? strtol(argv[1], NULL, 0) : -1L; + + while (count < 0 || count--) { + if (putchar(0) == EOF) + return -1; + } + + return 0; +} diff --git a/t/helper/test-hash-speed.c b/t/helper/test-hash-speed.c new file mode 100644 index 0000000000..432233c7f0 --- /dev/null +++ b/t/helper/test-hash-speed.c @@ -0,0 +1,61 @@ +#include "test-tool.h" +#include "cache.h" + +#define NUM_SECONDS 3 + +static inline void compute_hash(const struct git_hash_algo *algo, git_hash_ctx *ctx, uint8_t *final, const void *p, size_t len) +{ + algo->init_fn(ctx); + algo->update_fn(ctx, p, len); + algo->final_fn(final, ctx); +} + +int cmd__hash_speed(int ac, const char **av) +{ + git_hash_ctx ctx; + unsigned char hash[GIT_MAX_RAWSZ]; + clock_t initial, start, end; + unsigned bufsizes[] = { 64, 256, 1024, 8192, 16384 }; + int i; + void *p; + const struct git_hash_algo *algo = NULL; + + if (ac == 2) { + for (i = 1; i < GIT_HASH_NALGOS; i++) { + if (!strcmp(av[1], hash_algos[i].name)) { + algo = &hash_algos[i]; + break; + } + } + } + if (!algo) + die("usage: test-tool hash-speed algo_name"); + + /* Use this as an offset to make overflow less likely. */ + initial = clock(); + + printf("algo: %s\n", algo->name); + + for (i = 0; i < ARRAY_SIZE(bufsizes); i++) { + unsigned long j, kb; + double kb_per_sec; + p = xcalloc(1, bufsizes[i]); + start = end = clock() - initial; + for (j = 0; ((end - start) / CLOCKS_PER_SEC) < NUM_SECONDS; j++) { + compute_hash(algo, &ctx, hash, p, bufsizes[i]); + + /* + * Only check elapsed time every 128 iterations to avoid + * dominating the runtime with system calls. + */ + if (!(j & 127)) + end = clock() - initial; + } + kb = j * bufsizes[i]; + kb_per_sec = kb / (1024 * ((double)end - start) / CLOCKS_PER_SEC); + printf("size %u: %lu iters; %lu KiB; %0.2f KiB/s\n", bufsizes[i], j, kb, kb_per_sec); + free(p); + } + + exit(0); +} diff --git a/t/helper/test-hash.c b/t/helper/test-hash.c new file mode 100644 index 0000000000..0a31de66f3 --- /dev/null +++ b/t/helper/test-hash.c @@ -0,0 +1,58 @@ +#include "test-tool.h" +#include "cache.h" + +int cmd_hash_impl(int ac, const char **av, int algo) +{ + git_hash_ctx ctx; + unsigned char hash[GIT_MAX_HEXSZ]; + unsigned bufsz = 8192; + int binary = 0; + char *buffer; + const struct git_hash_algo *algop = &hash_algos[algo]; + + if (ac == 2) { + if (!strcmp(av[1], "-b")) + binary = 1; + else + bufsz = strtoul(av[1], NULL, 10) * 1024 * 1024; + } + + if (!bufsz) + bufsz = 8192; + + while ((buffer = malloc(bufsz)) == NULL) { + fprintf(stderr, "bufsz %u is too big, halving...\n", bufsz); + bufsz /= 2; + if (bufsz < 1024) + die("OOPS"); + } + + algop->init_fn(&ctx); + + while (1) { + ssize_t sz, this_sz; + char *cp = buffer; + unsigned room = bufsz; + this_sz = 0; + while (room) { + sz = xread(0, cp, room); + if (sz == 0) + break; + if (sz < 0) + die_errno("test-hash"); + this_sz += sz; + cp += sz; + room -= sz; + } + if (this_sz == 0) + break; + algop->update_fn(&ctx, buffer, this_sz); + } + algop->final_fn(hash, &ctx); + + if (binary) + fwrite(hash, 1, algop->rawsz, stdout); + else + puts(hash_to_hex_algop(hash, algop)); + exit(0); +} diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c index 47fee660b8..cc88fba057 100644 --- a/t/helper/test-parse-options.c +++ b/t/helper/test-parse-options.c @@ -2,6 +2,7 @@ #include "cache.h" #include "parse-options.h" #include "string-list.h" +#include "trace2.h" static int boolean = 0; static int integer = 0; @@ -153,6 +154,8 @@ int cmd__parse_options(int argc, const char **argv) int i; int ret = 0; + trace2_cmd_name("_parse_"); + argc = parse_options(argc, (const char **)argv, prefix, options, usage, 0); if (length_cb.called) { diff --git a/t/helper/test-path-utils.c b/t/helper/test-path-utils.c index ae091d9b3e..5d543ad21f 100644 --- a/t/helper/test-path-utils.c +++ b/t/helper/test-path-utils.c @@ -177,6 +177,14 @@ static int is_dotgitmodules(const char *path) return is_hfs_dotgitmodules(path) || is_ntfs_dotgitmodules(path); } +static int cmp_by_st_size(const void *a, const void *b) +{ + intptr_t x = (intptr_t)((struct string_list_item *)a)->util; + intptr_t y = (intptr_t)((struct string_list_item *)b)->util; + + return x > y ? -1 : (x < y ? +1 : 0); +} + int cmd__path_utils(int argc, const char **argv) { if (argc == 3 && !strcmp(argv[1], "normalize_path_copy")) { @@ -291,6 +299,62 @@ int cmd__path_utils(int argc, const char **argv) return !!res; } + if (argc > 2 && !strcmp(argv[1], "file-size")) { + int res = 0, i; + struct stat st; + + for (i = 2; i < argc; i++) + if (stat(argv[i], &st)) + res = error_errno("Cannot stat '%s'", argv[i]); + else + printf("%"PRIuMAX"\n", (uintmax_t)st.st_size); + return !!res; + } + + if (argc == 4 && !strcmp(argv[1], "skip-n-bytes")) { + int fd = open(argv[2], O_RDONLY), offset = atoi(argv[3]); + char buffer[65536]; + + if (fd < 0) + die_errno("could not open '%s'", argv[2]); + if (lseek(fd, offset, SEEK_SET) < 0) + die_errno("could not skip %d bytes", offset); + for (;;) { + ssize_t count = read(fd, buffer, sizeof(buffer)); + if (count < 0) + die_errno("could not read '%s'", argv[2]); + if (!count) + break; + if (write(1, buffer, count) < 0) + die_errno("could not write to stdout"); + } + close(fd); + return 0; + } + + if (argc > 5 && !strcmp(argv[1], "slice-tests")) { + int res = 0; + long offset, stride, i; + struct string_list list = STRING_LIST_INIT_NODUP; + struct stat st; + + offset = strtol(argv[2], NULL, 10); + stride = strtol(argv[3], NULL, 10); + if (stride < 1) + stride = 1; + for (i = 4; i < argc; i++) + if (stat(argv[i], &st)) + res = error_errno("Cannot stat '%s'", argv[i]); + else + string_list_append(&list, argv[i])->util = + (void *)(intptr_t)st.st_size; + QSORT(list.items, list.nr, cmp_by_st_size); + for (i = offset; i < list.nr; i+= stride) + printf("%s\n", list.items[i].string); + + return !!res; + } + fprintf(stderr, "%s: unknown function name: %s\n", argv[0], argv[1] ? argv[1] : "(there was none)"); return 1; diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c index e9e0541276..799fc00aa1 100644 --- a/t/helper/test-ref-store.c +++ b/t/helper/test-ref-store.c @@ -233,7 +233,7 @@ static int cmd_update_ref(struct ref_store *refs, const char **argv) { const char *msg = notnull(*argv++, "msg"); const char *refname = notnull(*argv++, "refname"); - const char *new_sha1_buf = notnull(*argv++, "old-sha1"); + const char *new_sha1_buf = notnull(*argv++, "new-sha1"); const char *old_sha1_buf = notnull(*argv++, "old-sha1"); unsigned int flags = arg_flags(*argv++, "flags"); struct object_id old_oid; diff --git a/t/helper/test-repository.c b/t/helper/test-repository.c index 6a84a53efb..f7f8618445 100644 --- a/t/helper/test-repository.c +++ b/t/helper/test-repository.c @@ -17,6 +17,11 @@ static void test_parse_commit_in_graph(const char *gitdir, const char *worktree, setup_git_env(gitdir); + memset(the_repository, 0, sizeof(*the_repository)); + + /* TODO: Needed for temporary hack in hashcmp, see 183a638b7da. */ + repo_set_hash_algo(the_repository, GIT_HASH_SHA1); + if (repo_init(&r, gitdir, worktree)) die("Couldn't init repo"); @@ -43,6 +48,11 @@ static void test_get_commit_tree_in_graph(const char *gitdir, setup_git_env(gitdir); + memset(the_repository, 0, sizeof(*the_repository)); + + /* TODO: Needed for temporary hack in hashcmp, see 183a638b7da. */ + repo_set_hash_algo(the_repository, GIT_HASH_SHA1); + if (repo_init(&r, gitdir, worktree)) die("Couldn't init repo"); diff --git a/t/helper/test-sha1.c b/t/helper/test-sha1.c index 1ba0675c75..d860c387c3 100644 --- a/t/helper/test-sha1.c +++ b/t/helper/test-sha1.c @@ -3,55 +3,5 @@ int cmd__sha1(int ac, const char **av) { - git_SHA_CTX ctx; - unsigned char sha1[20]; - unsigned bufsz = 8192; - int binary = 0; - char *buffer; - - if (ac == 2) { - if (!strcmp(av[1], "-b")) - binary = 1; - else - bufsz = strtoul(av[1], NULL, 10) * 1024 * 1024; - } - - if (!bufsz) - bufsz = 8192; - - while ((buffer = malloc(bufsz)) == NULL) { - fprintf(stderr, "bufsz %u is too big, halving...\n", bufsz); - bufsz /= 2; - if (bufsz < 1024) - die("OOPS"); - } - - git_SHA1_Init(&ctx); - - while (1) { - ssize_t sz, this_sz; - char *cp = buffer; - unsigned room = bufsz; - this_sz = 0; - while (room) { - sz = xread(0, cp, room); - if (sz == 0) - break; - if (sz < 0) - die_errno("test-sha1"); - this_sz += sz; - cp += sz; - room -= sz; - } - if (this_sz == 0) - break; - git_SHA1_Update(&ctx, buffer, this_sz); - } - git_SHA1_Final(sha1, &ctx); - - if (binary) - fwrite(sha1, 1, 20, stdout); - else - puts(sha1_to_hex(sha1)); - exit(0); + return cmd_hash_impl(ac, av, GIT_HASH_SHA1); } diff --git a/t/helper/test-sha256.c b/t/helper/test-sha256.c new file mode 100644 index 0000000000..0ac6a99d5f --- /dev/null +++ b/t/helper/test-sha256.c @@ -0,0 +1,7 @@ +#include "test-tool.h" +#include "cache.h" + +int cmd__sha256(int ac, const char **av) +{ + return cmd_hash_impl(ac, av, GIT_HASH_SHA256); +} diff --git a/t/helper/test-submodule-nested-repo-config.c b/t/helper/test-submodule-nested-repo-config.c index a31e2a9bea..bc97929bbc 100644 --- a/t/helper/test-submodule-nested-repo-config.c +++ b/t/helper/test-submodule-nested-repo-config.c @@ -10,19 +10,21 @@ static void die_usage(int argc, const char **argv, const char *msg) int cmd__submodule_nested_repo_config(int argc, const char **argv) { - struct repository submodule; + struct repository subrepo; + const struct submodule *sub; if (argc < 3) die_usage(argc, argv, "Wrong number of arguments."); setup_git_directory(); - if (repo_submodule_init(&submodule, the_repository, argv[1])) { + sub = submodule_from_path(the_repository, &null_oid, argv[1]); + if (repo_submodule_init(&subrepo, the_repository, sub)) { die_usage(argc, argv, "Submodule not found."); } /* Read the config of _child_ submodules. */ - print_config_from_gitmodules(&submodule, argv[2]); + print_config_from_gitmodules(&subrepo, argv[2]); submodule_free(the_repository); diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c index bfb195b1a8..53c06932c4 100644 --- a/t/helper/test-tool.c +++ b/t/helper/test-tool.c @@ -1,5 +1,6 @@ #include "git-compat-util.h" #include "test-tool.h" +#include "trace2.h" struct test_cmd { const char *name; @@ -19,7 +20,9 @@ static struct test_cmd cmds[] = { { "dump-untracked-cache", cmd__dump_untracked_cache }, { "example-decorate", cmd__example_decorate }, { "genrandom", cmd__genrandom }, + { "genzeros", cmd__genzeros }, { "hashmap", cmd__hashmap }, + { "hash-speed", cmd__hash_speed }, { "index-version", cmd__index_version }, { "json-writer", cmd__json_writer }, { "lazy-init-name-hash", cmd__lazy_init_name_hash }, @@ -42,13 +45,16 @@ static struct test_cmd cmds[] = { { "scrap-cache-tree", cmd__scrap_cache_tree }, { "sha1", cmd__sha1 }, { "sha1-array", cmd__sha1_array }, + { "sha256", cmd__sha256 }, { "sigchain", cmd__sigchain }, { "strcmp-offset", cmd__strcmp_offset }, { "string-list", cmd__string_list }, { "submodule-config", cmd__submodule_config }, { "submodule-nested-repo-config", cmd__submodule_nested_repo_config }, { "subprocess", cmd__subprocess }, + { "trace2", cmd__trace2 }, { "urlmatch-normalization", cmd__urlmatch_normalization }, + { "xml-encode", cmd__xml_encode }, { "wildmatch", cmd__wildmatch }, #ifdef GIT_WINDOWS_NATIVE { "windows-named-pipe", cmd__windows_named_pipe }, @@ -78,6 +84,8 @@ int cmd_main(int argc, const char **argv) if (!strcmp(cmds[i].name, argv[1])) { argv++; argc--; + trace2_cmd_name(cmds[i].name); + trace2_cmd_list_config(); return cmds[i].fn(argc, argv); } } diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h index 042f12464b..ffab4d19d7 100644 --- a/t/helper/test-tool.h +++ b/t/helper/test-tool.h @@ -1,6 +1,7 @@ #ifndef TEST_TOOL_H #define TEST_TOOL_H +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "git-compat-util.h" int cmd__chmtime(int argc, const char **argv); @@ -15,7 +16,9 @@ int cmd__dump_split_index(int argc, const char **argv); int cmd__dump_untracked_cache(int argc, const char **argv); int cmd__example_decorate(int argc, const char **argv); int cmd__genrandom(int argc, const char **argv); +int cmd__genzeros(int argc, const char **argv); int cmd__hashmap(int argc, const char **argv); +int cmd__hash_speed(int argc, const char **argv); int cmd__index_version(int argc, const char **argv); int cmd__json_writer(int argc, const char **argv); int cmd__lazy_init_name_hash(int argc, const char **argv); @@ -38,17 +41,22 @@ int cmd__run_command(int argc, const char **argv); int cmd__scrap_cache_tree(int argc, const char **argv); int cmd__sha1(int argc, const char **argv); int cmd__sha1_array(int argc, const char **argv); +int cmd__sha256(int argc, const char **argv); int cmd__sigchain(int argc, const char **argv); int cmd__strcmp_offset(int argc, const char **argv); int cmd__string_list(int argc, const char **argv); int cmd__submodule_config(int argc, const char **argv); int cmd__submodule_nested_repo_config(int argc, const char **argv); int cmd__subprocess(int argc, const char **argv); +int cmd__trace2(int argc, const char **argv); int cmd__urlmatch_normalization(int argc, const char **argv); +int cmd__xml_encode(int argc, const char **argv); int cmd__wildmatch(int argc, const char **argv); #ifdef GIT_WINDOWS_NATIVE int cmd__windows_named_pipe(int argc, const char **argv); #endif int cmd__write_cache(int argc, const char **argv); +int cmd_hash_impl(int ac, const char **av, int algo); + #endif diff --git a/t/helper/test-trace2.c b/t/helper/test-trace2.c new file mode 100644 index 0000000000..197819c872 --- /dev/null +++ b/t/helper/test-trace2.c @@ -0,0 +1,273 @@ +#include "test-tool.h" +#include "cache.h" +#include "argv-array.h" +#include "run-command.h" +#include "exec-cmd.h" +#include "config.h" + +typedef int(fn_unit_test)(int argc, const char **argv); + +struct unit_test { + fn_unit_test *ut_fn; + const char *ut_name; + const char *ut_usage; +}; + +#define MyOk 0 +#define MyError 1 + +static int get_i(int *p_value, const char *data) +{ + char *endptr; + + if (!data || !*data) + return MyError; + + *p_value = strtol(data, &endptr, 10); + if (*endptr || errno == ERANGE) + return MyError; + + return MyOk; +} + +/* + * Cause process to exit with the requested value via "return". + * + * Rely on test-tool.c:cmd_main() to call trace2_cmd_exit() + * with our result. + * + * Test harness can confirm: + * [] the process-exit value. + * [] the "code" field in the "exit" trace2 event. + * [] the "code" field in the "atexit" trace2 event. + * [] the "name" field in the "cmd_name" trace2 event. + * [] "def_param" events for all of the "interesting" pre-defined + * config settings. + */ +static int ut_001return(int argc, const char **argv) +{ + int rc; + + if (get_i(&rc, argv[0])) + die("expect <exit_code>"); + + return rc; +} + +/* + * Cause the process to exit with the requested value via "exit()". + * + * Test harness can confirm: + * [] the "code" field in the "exit" trace2 event. + * [] the "code" field in the "atexit" trace2 event. + * [] the "name" field in the "cmd_name" trace2 event. + * [] "def_param" events for all of the "interesting" pre-defined + * config settings. + */ +static int ut_002exit(int argc, const char **argv) +{ + int rc; + + if (get_i(&rc, argv[0])) + die("expect <exit_code>"); + + exit(rc); +} + +/* + * Send an "error" event with each value in argv. Normally, git only issues + * a single "error" event immediately before issuing an "exit" event (such + * as in die() or BUG()), but multiple "error" events are allowed. + * + * Test harness can confirm: + * [] a trace2 "error" event for each value in argv. + * [] the "name" field in the "cmd_name" trace2 event. + * [] (optional) the file:line in the "exit" event refers to this function. + */ +static int ut_003error(int argc, const char **argv) +{ + int k; + + if (!argv[0] || !*argv[0]) + die("expect <error_message>"); + + for (k = 0; k < argc; k++) + error("%s", argv[k]); + + return 0; +} + +/* + * Run a child process and wait for it to finish and exit with its return code. + * test-tool trace2 004child [<child-command-line>] + * + * For example: + * test-tool trace2 004child git version + * test-tool trace2 004child test-tool trace2 001return 0 + * test-tool trace2 004child test-tool trace2 004child test-tool trace2 004child + * test-tool trace2 004child git -c alias.xyz=version xyz + * + * Test harness can confirm: + * [] the "name" field in the "cmd_name" trace2 event. + * [] that the outer process has a single component SID (or depth "d0" in + * the PERF stream). + * [] that "child_start" and "child_exit" events are generated for the child. + * [] if the child process is an instrumented executable: + * [] that "version", "start", ..., "exit", and "atexit" events are + * generated by the child process. + * [] that the child process events have a multiple component SID (or + * depth "dN+1" in the PERF stream). + * [] that the child exit code is propagated to the parent process "exit" + * and "atexit" events.. + * [] (optional) that the "t_abs" field in the child process "atexit" event + * is less than the "t_rel" field in the "child_exit" event of the parent + * process. + * [] if the child process is like the alias example above, + * [] (optional) the child process attempts to run "git-xyx" as a dashed + * command. + * [] the child process emits an "alias" event with "xyz" => "version" + * [] the child process runs "git version" as a child process. + * [] the child process has a 3 component SID (or depth "d2" in the PERF + * stream). + */ +static int ut_004child(int argc, const char **argv) +{ + int result; + + /* + * Allow empty <child_command_line> so we can do arbitrarily deep + * command nesting and let the last one be null. + */ + if (!argc) + return 0; + + result = run_command_v_opt(argv, 0); + exit(result); +} + +/* + * Exec a git command. This may either create a child process (Windows) + * or replace the existing process. + * test-tool trace2 005exec <git_command_args> + * + * For example: + * test-tool trace2 005exec version + * + * Test harness can confirm (on Windows): + * [] the "name" field in the "cmd_name" trace2 event. + * [] that the outer process has a single component SID (or depth "d0" in + * the PERF stream). + * [] that "exec" and "exec_result" events are generated for the child + * process (since the Windows compatibility layer fakes an exec() with + * a CreateProcess(), WaitForSingleObject(), and exit()). + * [] that the child process has multiple component SID (or depth "dN+1" + * in the PERF stream). + * + * Test harness can confirm (on platforms with a real exec() function): + * [] TODO talk about process replacement and how it affects SID. + */ +static int ut_005exec(int argc, const char **argv) +{ + int result; + + if (!argc) + return 0; + + result = execv_git_cmd(argv); + return result; +} + +static int ut_006data(int argc, const char **argv) +{ + const char *usage_error = + "expect <cat0> <k0> <v0> [<cat1> <k1> <v1> [...]]"; + + if (argc % 3 != 0) + die("%s", usage_error); + + while (argc) { + if (!argv[0] || !*argv[0] || !argv[1] || !*argv[1] || + !argv[2] || !*argv[2]) + die("%s", usage_error); + + trace2_data_string(argv[0], the_repository, argv[1], argv[2]); + argv += 3; + argc -= 3; + } + + return 0; +} + +/* + * Usage: + * test-tool trace2 <ut_name_1> <ut_usage_1> + * test-tool trace2 <ut_name_2> <ut_usage_2> + * ... + */ +#define USAGE_PREFIX "test-tool trace2" + +/* clang-format off */ +static struct unit_test ut_table[] = { + { ut_001return, "001return", "<exit_code>" }, + { ut_002exit, "002exit", "<exit_code>" }, + { ut_003error, "003error", "<error_message>+" }, + { ut_004child, "004child", "[<child_command_line>]" }, + { ut_005exec, "005exec", "<git_command_args>" }, + { ut_006data, "006data", "[<category> <key> <value>]+" }, +}; +/* clang-format on */ + +/* clang-format off */ +#define for_each_ut(k, ut_k) \ + for (k = 0, ut_k = &ut_table[k]; \ + k < ARRAY_SIZE(ut_table); \ + k++, ut_k = &ut_table[k]) +/* clang-format on */ + +static int print_usage(void) +{ + int k; + struct unit_test *ut_k; + + fprintf(stderr, "usage:\n"); + for_each_ut (k, ut_k) + fprintf(stderr, "\t%s %s %s\n", USAGE_PREFIX, ut_k->ut_name, + ut_k->ut_usage); + + return 129; +} + +/* + * Issue various trace2 events for testing. + * + * We assume that these trace2 routines has already been called: + * [] trace2_initialize() [common-main.c:main()] + * [] trace2_cmd_start() [common-main.c:main()] + * [] trace2_cmd_name() [test-tool.c:cmd_main()] + * [] tracd2_cmd_list_config() [test-tool.c:cmd_main()] + * So that: + * [] the various trace2 streams are open. + * [] the process SID has been created. + * [] the "version" event has been generated. + * [] the "start" event has been generated. + * [] the "cmd_name" event has been generated. + * [] this writes various "def_param" events for interesting config values. + * + * We further assume that if we return (rather than exit()), trace2_cmd_exit() + * will be called by test-tool.c:cmd_main(). + */ +int cmd__trace2(int argc, const char **argv) +{ + int k; + struct unit_test *ut_k; + + argc--; /* skip over "trace2" arg */ + argv++; + + if (argc) + for_each_ut (k, ut_k) + if (!strcmp(argv[0], ut_k->ut_name)) + return ut_k->ut_fn(argc - 1, argv + 1); + + return print_usage(); +} diff --git a/t/helper/test-xml-encode.c b/t/helper/test-xml-encode.c new file mode 100644 index 0000000000..a648bbd961 --- /dev/null +++ b/t/helper/test-xml-encode.c @@ -0,0 +1,80 @@ +#include "test-tool.h" + +static const char *utf8_replace_character = "�"; + +/* + * Encodes (possibly incorrect) UTF-8 on <stdin> to <stdout>, to be embedded + * in an XML file. + */ +int cmd__xml_encode(int argc, const char **argv) +{ + unsigned char buf[1024], tmp[4], *tmp2 = NULL; + ssize_t cur = 0, len = 1, remaining = 0; + unsigned char ch; + + for (;;) { + if (++cur == len) { + len = xread(0, buf, sizeof(buf)); + if (!len) + return 0; + if (len < 0) + die_errno("Could not read <stdin>"); + cur = 0; + } + ch = buf[cur]; + + if (tmp2) { + if ((ch & 0xc0) != 0x80) { + fputs(utf8_replace_character, stdout); + tmp2 = NULL; + cur--; + continue; + } + *tmp2 = ch; + tmp2++; + if (--remaining == 0) { + fwrite(tmp, tmp2 - tmp, 1, stdout); + tmp2 = NULL; + } + continue; + } + + if (!(ch & 0x80)) { + /* 0xxxxxxx */ + if (ch == '&') + fputs("&", stdout); + else if (ch == '\'') + fputs("'", stdout); + else if (ch == '"') + fputs(""", stdout); + else if (ch == '<') + fputs("<", stdout); + else if (ch == '>') + fputs(">", stdout); + else if (ch >= 0x20) + fputc(ch, stdout); + else if (ch == 0x09 || ch == 0x0a || ch == 0x0d) + fprintf(stdout, "&#x%02x;", ch); + else + fputs(utf8_replace_character, stdout); + } else if ((ch & 0xe0) == 0xc0) { + /* 110XXXXx 10xxxxxx */ + tmp[0] = ch; + remaining = 1; + tmp2 = tmp + 1; + } else if ((ch & 0xf0) == 0xe0) { + /* 1110XXXX 10Xxxxxx 10xxxxxx */ + tmp[0] = ch; + remaining = 2; + tmp2 = tmp + 1; + } else if ((ch & 0xf8) == 0xf0) { + /* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */ + tmp[0] = ch; + remaining = 3; + tmp2 = tmp + 1; + } else + fputs(utf8_replace_character, stdout); + } + + return 0; +} diff --git a/t/lib-gpg.sh b/t/lib-gpg.sh index f1277bef4f..8d28652b72 100755 --- a/t/lib-gpg.sh +++ b/t/lib-gpg.sh @@ -63,8 +63,7 @@ then cut -d" " -f4 | tr -d '\n' >"${GNUPGHOME}/trustlist.txt" && - echo " S relax" >> ${GNUPGHOME}/trustlist.txt && - (gpgconf --kill gpg-agent >/dev/null 2>&1 || : ) && + echo " S relax" >>"${GNUPGHOME}/trustlist.txt" && echo hello | gpgsm --homedir "${GNUPGHOME}" >/dev/null \ -u committer@example.com -o /dev/null --sign - 2>&1 && test_set_prereq GPGSM diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh index e465116ef9..0dfb48c2f6 100644 --- a/t/lib-httpd.sh +++ b/t/lib-httpd.sh @@ -91,6 +91,7 @@ HTTPD_DOCUMENT_ROOT_PATH=$HTTPD_ROOT_PATH/www # hack to suppress apache PassEnv warnings GIT_VALGRIND=$GIT_VALGRIND; export GIT_VALGRIND GIT_VALGRIND_OPTIONS=$GIT_VALGRIND_OPTIONS; export GIT_VALGRIND_OPTIONS +GIT_TEST_SIDEBAND_ALL=$GIT_TEST_SIDEBAND_ALL; export GIT_TEST_SIDEBAND_ALL GIT_TRACE=$GIT_TRACE; export GIT_TRACE if ! test -x "$LIB_HTTPD_PATH" @@ -131,6 +132,7 @@ prepare_httpd() { mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH" cp "$TEST_PATH"/passwd "$HTTPD_ROOT_PATH" install_script broken-smart-http.sh + install_script error-smart-http.sh install_script error.sh install_script apply-one-time-sed.sh diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf index 581c010d8f..06a81b54c7 100644 --- a/t/lib-httpd/apache.conf +++ b/t/lib-httpd/apache.conf @@ -78,6 +78,7 @@ PassEnv GNUPGHOME PassEnv ASAN_OPTIONS PassEnv GIT_TRACE PassEnv GIT_CONFIG_NOSYSTEM +PassEnv GIT_TEST_SIDEBAND_ALL SetEnvIf Git-Protocol ".*" GIT_PROTOCOL=$0 @@ -115,8 +116,10 @@ Alias /auth/dumb/ www/auth/dumb/ SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH} SetEnv GIT_HTTP_EXPORT_ALL </LocationMatch> +ScriptAliasMatch /error_git_upload_pack/(.*)/git-upload-pack error.sh/ ScriptAliasMatch /smart_*[^/]*/(.*) ${GIT_EXEC_PATH}/git-http-backend/$1 ScriptAlias /broken_smart/ broken-smart-http.sh/ +ScriptAlias /error_smart/ error-smart-http.sh/ ScriptAlias /error/ error.sh/ ScriptAliasMatch /one_time_sed/(.*) apply-one-time-sed.sh/$1 <Directory ${GIT_EXEC_PATH}> @@ -125,6 +128,9 @@ ScriptAliasMatch /one_time_sed/(.*) apply-one-time-sed.sh/$1 <Files broken-smart-http.sh> Options ExecCGI </Files> +<Files error-smart-http.sh> + Options ExecCGI +</Files> <Files error.sh> Options ExecCGI </Files> diff --git a/t/lib-httpd/error-smart-http.sh b/t/lib-httpd/error-smart-http.sh new file mode 100644 index 0000000000..e65d447fc4 --- /dev/null +++ b/t/lib-httpd/error-smart-http.sh @@ -0,0 +1,3 @@ +echo "Content-Type: application/x-git-upload-pack-advertisement" +echo +printf "%s" "0019ERR server-side error" diff --git a/t/perf/p5304-prune.sh b/t/perf/p5304-prune.sh new file mode 100755 index 0000000000..83baedb8a4 --- /dev/null +++ b/t/perf/p5304-prune.sh @@ -0,0 +1,35 @@ +#!/bin/sh + +test_description='performance tests of prune' +. ./perf-lib.sh + +test_perf_default_repo + +test_expect_success 'remove reachable loose objects' ' + git repack -ad +' + +test_expect_success 'remove unreachable loose objects' ' + git prune +' + +test_expect_success 'confirm there are no loose objects' ' + git count-objects | grep ^0 +' + +test_perf 'prune with no objects' ' + git prune +' + +test_expect_success 'repack with bitmaps' ' + git repack -adb +' + +# We have to create the object in each trial run, since otherwise +# runs after the first see no object and just skip the traversal entirely! +test_perf 'prune with bitmaps' ' + echo "probably not present in repo" | git hash-object -w --stdin && + git prune +' + +test_done diff --git a/t/t0001-init.sh b/t/t0001-init.sh index 42a263cada..5e27604b24 100755 --- a/t/t0001-init.sh +++ b/t/t0001-init.sh @@ -93,6 +93,7 @@ test_expect_success 'No extra GIT_* on alias scripts' ' sed -n \ -e "/^GIT_PREFIX=/d" \ -e "/^GIT_TEXTDOMAINDIR=/d" \ + -e "/^GIT_TR2_PARENT/d" \ -e "/^GIT_/s/=.*//p" | sort EOF diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh index 22499bce5f..71e63d8b50 100755 --- a/t/t0003-attributes.sh +++ b/t/t0003-attributes.sh @@ -322,4 +322,24 @@ test_expect_success 'bare repository: test info/attributes' ' ) ' +test_expect_success 'binary macro expanded by -a' ' + echo "file binary" >.gitattributes && + cat >expect <<-\EOF && + file: binary: set + file: diff: unset + file: merge: unset + file: text: unset + EOF + git check-attr -a file >actual && + test_cmp expect actual +' + + +test_expect_success 'query binary macro directly' ' + echo "file binary" >.gitattributes && + echo file: binary: set >expect && + git check-attr binary file >actual && + test_cmp expect actual +' + test_done diff --git a/t/t0006-date.sh b/t/t0006-date.sh index ffb2975e48..d9fcc829a9 100755 --- a/t/t0006-date.sh +++ b/t/t0006-date.sh @@ -4,10 +4,10 @@ test_description='test date parsing and printing' . ./test-lib.sh # arbitrary reference time: 2009-08-30 19:20:00 -TEST_DATE_NOW=1251660000; export TEST_DATE_NOW +GIT_TEST_DATE_NOW=1251660000; export GIT_TEST_DATE_NOW check_relative() { - t=$(($TEST_DATE_NOW - $1)) + t=$(($GIT_TEST_DATE_NOW - $1)) echo "$t -> $2" >expect test_expect_${3:-success} "relative date ($2)" " test-tool date relative $t >actual && @@ -128,4 +128,22 @@ check_approxidate '6AM, June 7, 2009' '2009-06-07 06:00:00' check_approxidate '2008-12-01' '2008-12-01 19:20:00' check_approxidate '2009-12-01' '2009-12-01 19:20:00' +check_date_format_human() { + t=$(($GIT_TEST_DATE_NOW - $1)) + echo "$t -> $2" >expect + test_expect_success "human date $t" ' + test-tool date human $t >actual && + test_i18ncmp expect actual +' +} + +check_date_format_human 18000 "5 hours ago" # 5 hours ago +check_date_format_human 432000 "Tue Aug 25 19:20" # 5 days ago +check_date_format_human 1728000 "Mon Aug 10 19:20" # 3 weeks ago +check_date_format_human 13000000 "Thu Apr 2 08:13" # 5 months ago +check_date_format_human 31449600 "Aug 31 2008" # 12 months ago +check_date_format_human 37500000 "Jun 22 2008" # 1 year, 2 months ago +check_date_format_human 55188000 "Dec 1 2007" # 1 year, 9 months ago +check_date_format_human 630000000 "Sep 13 1989" # 20 years ago + test_done diff --git a/t/t0015-hash.sh b/t/t0015-hash.sh new file mode 100755 index 0000000000..291e9061f3 --- /dev/null +++ b/t/t0015-hash.sh @@ -0,0 +1,55 @@ +#!/bin/sh + +test_description='test basic hash implementation' +. ./test-lib.sh + + +test_expect_success 'test basic SHA-1 hash values' ' + test-tool sha1 </dev/null >actual && + grep da39a3ee5e6b4b0d3255bfef95601890afd80709 actual && + printf "a" | test-tool sha1 >actual && + grep 86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 actual && + printf "abc" | test-tool sha1 >actual && + grep a9993e364706816aba3e25717850c26c9cd0d89d actual && + printf "message digest" | test-tool sha1 >actual && + grep c12252ceda8be8994d5fa0290a47231c1d16aae3 actual && + printf "abcdefghijklmnopqrstuvwxyz" | test-tool sha1 >actual && + grep 32d10c7b8cf96570ca04ce37f2a19d84240d3a89 actual && + perl -e "$| = 1; print q{aaaaaaaaaa} for 1..100000;" | \ + test-tool sha1 >actual && + grep 34aa973cd4c4daa4f61eeb2bdbad27316534016f actual && + printf "blob 0\0" | test-tool sha1 >actual && + grep e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 actual && + printf "blob 3\0abc" | test-tool sha1 >actual && + grep f2ba8f84ab5c1bce84a7b441cb1959cfc7093b7f actual && + printf "tree 0\0" | test-tool sha1 >actual && + grep 4b825dc642cb6eb9a060e54bf8d69288fbee4904 actual +' + +test_expect_success 'test basic SHA-256 hash values' ' + test-tool sha256 </dev/null >actual && + grep e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 actual && + printf "a" | test-tool sha256 >actual && + grep ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb actual && + printf "abc" | test-tool sha256 >actual && + grep ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad actual && + printf "message digest" | test-tool sha256 >actual && + grep f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650 actual && + 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;" | \ + test-tool sha256 >actual && + grep cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0 actual && + perl -e "$| = 1; print q{abcdefghijklmnopqrstuvwxyz} for 1..100000;" | \ + test-tool sha256 >actual && + grep e406ba321ca712ad35a698bf0af8d61fc4dc40eca6bdcea4697962724ccbde35 actual && + printf "blob 0\0" | test-tool sha256 >actual && + grep 473a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813 actual && + printf "blob 3\0abc" | test-tool sha256 >actual && + grep c1cf6e465077930e88dc5136641d402f72a229ddd996f627d60e9639eaba35a6 actual && + printf "tree 0\0" | test-tool sha256 >actual && + grep 6ef19b41225c5369f1c104d45d8d85efa9b057b53b14b4b9b939dd74decc5321 actual +' + +test_done diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh index fd5f1ac649..e10f5f787f 100755 --- a/t/t0021-conversion.sh +++ b/t/t0021-conversion.sh @@ -24,7 +24,7 @@ generate_random_characters () { } file_size () { - perl -e 'print -s $ARGV[0]' "$1" + test-tool path-utils file-size "$1" } filter_git () { diff --git a/t/t0025-crlf-renormalize.sh b/t/t0025-crlf-renormalize.sh index 9d9e02a211..e13363ade5 100755 --- a/t/t0025-crlf-renormalize.sh +++ b/t/t0025-crlf-renormalize.sh @@ -27,4 +27,13 @@ test_expect_success 'renormalize CRLF in repo' ' test_cmp expect actual ' +test_expect_success 'ignore-errors not mistaken for renormalize' ' + git reset --hard && + echo "*.txt text=auto" >.gitattributes && + git ls-files --eol >expect && + git add --ignore-errors "*.txt" && + git ls-files --eol >actual && + test_cmp expect actual +' + test_done diff --git a/t/t0028-working-tree-encoding.sh b/t/t0028-working-tree-encoding.sh index 7e87b5a200..1090e650ed 100755 --- a/t/t0028-working-tree-encoding.sh +++ b/t/t0028-working-tree-encoding.sh @@ -6,14 +6,41 @@ test_description='working-tree-encoding conversion via gitattributes' GIT_TRACE_WORKING_TREE_ENCODING=1 && export GIT_TRACE_WORKING_TREE_ENCODING +test_lazy_prereq NO_UTF16_BOM ' + test $(printf abc | iconv -f UTF-8 -t UTF-16 | wc -c) = 6 +' + +test_lazy_prereq NO_UTF32_BOM ' + test $(printf abc | iconv -f UTF-8 -t UTF-32 | wc -c) = 12 +' + +write_utf16 () { + if test_have_prereq NO_UTF16_BOM + then + printf '\xfe\xff' + fi && + iconv -f UTF-8 -t UTF-16 +} + +write_utf32 () { + if test_have_prereq NO_UTF32_BOM + then + printf '\x00\x00\xfe\xff' + fi && + iconv -f UTF-8 -t UTF-32 +} + test_expect_success 'setup test files' ' git config core.eol lf && text="hallo there!\ncan you read me?" && echo "*.utf16 text working-tree-encoding=utf-16" >.gitattributes && + echo "*.utf16lebom text working-tree-encoding=UTF-16LE-BOM" >>.gitattributes && printf "$text" >test.utf8.raw && - printf "$text" | iconv -f UTF-8 -t UTF-16 >test.utf16.raw && - printf "$text" | iconv -f UTF-8 -t UTF-32 >test.utf32.raw && + printf "$text" | write_utf16 >test.utf16.raw && + printf "$text" | write_utf32 >test.utf32.raw && + printf "\377\376" >test.utf16lebom.raw && + printf "$text" | iconv -f UTF-8 -t UTF-32LE >>test.utf16lebom.raw && # Line ending tests printf "one\ntwo\nthree\n" >lf.utf8.raw && @@ -22,17 +49,18 @@ test_expect_success 'setup test files' ' # BOM tests printf "\0a\0b\0c" >nobom.utf16be.raw && printf "a\0b\0c\0" >nobom.utf16le.raw && - printf "\376\777\0a\0b\0c" >bebom.utf16be.raw && - printf "\777\376a\0b\0c\0" >lebom.utf16le.raw && + printf "\376\377\0a\0b\0c" >bebom.utf16be.raw && + printf "\377\376a\0b\0c\0" >lebom.utf16le.raw && printf "\0\0\0a\0\0\0b\0\0\0c" >nobom.utf32be.raw && printf "a\0\0\0b\0\0\0c\0\0\0" >nobom.utf32le.raw && - printf "\0\0\376\777\0\0\0a\0\0\0b\0\0\0c" >bebom.utf32be.raw && - printf "\777\376\0\0a\0\0\0b\0\0\0c\0\0\0" >lebom.utf32le.raw && + printf "\0\0\376\377\0\0\0a\0\0\0b\0\0\0c" >bebom.utf32be.raw && + printf "\377\376\0\0a\0\0\0b\0\0\0c\0\0\0" >lebom.utf32le.raw && # Add only UTF-16 file, we will add the UTF-32 file later cp test.utf16.raw test.utf16 && cp test.utf32.raw test.utf32 && - git add .gitattributes test.utf16 && + cp test.utf16lebom.raw test.utf16lebom && + git add .gitattributes test.utf16 test.utf16lebom && git commit -m initial ' @@ -51,6 +79,12 @@ test_expect_success 're-encode to UTF-16 on checkout' ' test_cmp_bin test.utf16.raw test.utf16 ' +test_expect_success 're-encode to UTF-16-LE-BOM on checkout' ' + rm test.utf16lebom && + git checkout test.utf16lebom && + test_cmp_bin test.utf16lebom.raw test.utf16lebom +' + test_expect_success 'check $GIT_DIR/info/attributes support' ' test_when_finished "rm -f test.utf32.git" && test_when_finished "git reset --hard HEAD" && @@ -114,8 +148,8 @@ do test_when_finished "rm -f crlf.utf${i}.raw lf.utf${i}.raw" && test_when_finished "git reset --hard HEAD^" && - cat lf.utf8.raw | iconv -f UTF-8 -t UTF-${i} >lf.utf${i}.raw && - cat crlf.utf8.raw | iconv -f UTF-8 -t UTF-${i} >crlf.utf${i}.raw && + cat lf.utf8.raw | write_utf${i} >lf.utf${i}.raw && + cat crlf.utf8.raw | write_utf${i} >crlf.utf${i}.raw && cp crlf.utf${i}.raw eol.utf${i} && cat >expectIndexLF <<-EOF && @@ -213,7 +247,7 @@ test_expect_success ICONV_SHIFT_JIS 'check roundtrip encoding' ' text="hallo there!\nroundtrip test here!" && printf "$text" | iconv -f UTF-8 -t SHIFT-JIS >roundtrip.shift && - printf "$text" | iconv -f UTF-8 -t UTF-16 >roundtrip.utf16 && + printf "$text" | write_utf16 >roundtrip.utf16 && echo "*.shift text working-tree-encoding=SHIFT-JIS" >>.gitattributes && # SHIFT-JIS encoded files are round-trip checked by default... diff --git a/t/t0061-run-command.sh b/t/t0061-run-command.sh index 99a614bc7c..ebc49561ac 100755 --- a/t/t0061-run-command.sh +++ b/t/t0061-run-command.sh @@ -166,7 +166,8 @@ test_trace () { expect="$1" shift GIT_TRACE=1 test-tool run-command "$@" run-command true 2>&1 >/dev/null | \ - sed -e 's/.* run_command: //' -e '/trace: .*/d' >actual && + sed -e 's/.* run_command: //' -e '/trace: .*/d' \ + -e '/RUNTIME_PREFIX requested/d' >actual && echo "$expect true" >expect && test_cmp expect actual } @@ -199,4 +200,14 @@ test_expect_success 'GIT_TRACE with environment variables' ' ) ' +test_expect_success MINGW 'verify curlies are quoted properly' ' + : force the rev-parse through the MSYS2 Bash && + git -c alias.r="!git rev-parse" r -- a{b}c >actual && + cat >expect <<-\EOF && + -- + a{b}c + EOF + test_cmp expect actual +' + test_done diff --git a/t/t0210-trace2-normal.sh b/t/t0210-trace2-normal.sh new file mode 100755 index 0000000000..03a0aedb1d --- /dev/null +++ b/t/t0210-trace2-normal.sh @@ -0,0 +1,135 @@ +#!/bin/sh + +test_description='test trace2 facility (normal target)' +. ./test-lib.sh + +# Add t/helper directory to PATH so that we can use a relative +# path to run nested instances of test-tool.exe (see 004child). +# This helps with HEREDOC comparisons later. +TTDIR="$GIT_BUILD_DIR/t/helper/" && export TTDIR +PATH="$TTDIR:$PATH" && export PATH + +# Warning: use of 'test_cmp' may run test-tool.exe and/or git.exe +# Warning: to do the actual diff/comparison, so the HEREDOCs here +# Warning: only cover our actual calls to test-tool and/or git. +# Warning: So you may see extra lines in artifact files when +# Warning: interactively debugging. + +# Turn off any inherited trace2 settings for this test. +unset GIT_TR2 GIT_TR2_PERF GIT_TR2_EVENT +unset GIT_TR2_BRIEF +unset GIT_TR2_CONFIG_PARAMS + +V=$(git version | sed -e 's/^git version //') && export V + +# There are multiple trace2 targets: normal, perf, and event. +# Trace2 events will/can be written to each active target (subject +# to whatever filtering that target decides to do). +# This script tests the normal target in isolation. +# +# Defer setting GIT_TR2 until the actual command line we want to test +# because hidden git and test-tool commands run by the test harness +# can contaminate our output. + +# Enable "brief" feature which turns off "<clock> <file>:<line> " prefix. +GIT_TR2_BRIEF=1 && export GIT_TR2_BRIEF + +# Basic tests of the trace2 normal stream. Since this stream is used +# primarily with printf-style debugging/tracing, we do limited testing +# here. +# +# We do confirm the following API features: +# [] the 'version <v>' event +# [] the 'start <argv>' event +# [] the 'cmd_name <name>' event +# [] the 'exit <time> code:<code>' event +# [] the 'atexit <time> code:<code>' event +# +# Fields of the form _FIELD_ are tokens that have been replaced (such +# as the elapsed time). + +# Verb 001return +# +# Implicit return from cmd_<verb> function propagates <code>. + +test_expect_success 'normal stream, return code 0' ' + test_when_finished "rm trace.normal actual expect" && + GIT_TR2="$(pwd)/trace.normal" test-tool trace2 001return 0 && + perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual && + cat >expect <<-EOF && + version $V + start _EXE_ trace2 001return 0 + cmd_name trace2 (trace2) + exit elapsed:_TIME_ code:0 + atexit elapsed:_TIME_ code:0 + EOF + test_cmp expect actual +' + +test_expect_success 'normal stream, return code 1' ' + test_when_finished "rm trace.normal actual expect" && + test_must_fail env GIT_TR2="$(pwd)/trace.normal" test-tool trace2 001return 1 && + perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual && + cat >expect <<-EOF && + version $V + start _EXE_ trace2 001return 1 + cmd_name trace2 (trace2) + exit elapsed:_TIME_ code:1 + atexit elapsed:_TIME_ code:1 + EOF + test_cmp expect actual +' + +# Verb 002exit +# +# Explicit exit(code) from within cmd_<verb> propagates <code>. + +test_expect_success 'normal stream, exit code 0' ' + test_when_finished "rm trace.normal actual expect" && + GIT_TR2="$(pwd)/trace.normal" test-tool trace2 002exit 0 && + perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual && + cat >expect <<-EOF && + version $V + start _EXE_ trace2 002exit 0 + cmd_name trace2 (trace2) + exit elapsed:_TIME_ code:0 + atexit elapsed:_TIME_ code:0 + EOF + test_cmp expect actual +' + +test_expect_success 'normal stream, exit code 1' ' + test_when_finished "rm trace.normal actual expect" && + test_must_fail env GIT_TR2="$(pwd)/trace.normal" test-tool trace2 002exit 1 && + perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual && + cat >expect <<-EOF && + version $V + start _EXE_ trace2 002exit 1 + cmd_name trace2 (trace2) + exit elapsed:_TIME_ code:1 + atexit elapsed:_TIME_ code:1 + EOF + test_cmp expect actual +' + +# Verb 003error +# +# To the above, add multiple 'error <msg>' events + +test_expect_success 'normal stream, error event' ' + test_when_finished "rm trace.normal actual expect" && + GIT_TR2="$(pwd)/trace.normal" test-tool trace2 003error "hello world" "this is a test" && + perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual && + cat >expect <<-EOF && + version $V + start _EXE_ trace2 003error '\''hello world'\'' '\''this is a test'\'' + cmd_name trace2 (trace2) + error hello world + error this is a test + exit elapsed:_TIME_ code:0 + atexit elapsed:_TIME_ code:0 + EOF + test_cmp expect actual +' + +test_done diff --git a/t/t0210/scrub_normal.perl b/t/t0210/scrub_normal.perl new file mode 100644 index 0000000000..c65d1a815e --- /dev/null +++ b/t/t0210/scrub_normal.perl @@ -0,0 +1,48 @@ +#!/usr/bin/perl +# +# Scrub the variable fields from the normal trace2 output to +# make testing easier. + +use strict; +use warnings; + +my $float = '[0-9]*\.[0-9]+([eE][-+]?[0-9]+)?'; + +# This code assumes that the trace2 data was written with bare +# turned on (which omits the "<clock> <file>:<line>" prefix. + +while (<>) { + # Various messages include an elapsed time in the middle + # of the message. Replace the time with a placeholder to + # simplify our HEREDOC in the test script. + s/elapsed:$float/elapsed:_TIME_/g; + + my $line = $_; + + # we expect: + # start <argv0> [<argv1> [<argv2> [...]]] + # + # where argv0 might be a relative or absolute path, with + # or without quotes, and platform dependent. Replace argv0 + # with a token for HEREDOC matching in the test script. + + if ($line =~ m/^start/) { + $line =~ /^start\s+(.*)/; + my $argv = $1; + $argv =~ m/(\'[^\']*\'|[^ ]+)\s+(.*)/; + my $argv_0 = $1; + my $argv_rest = $2; + + print "start _EXE_ $argv_rest\n"; + } + elsif ($line =~ m/^cmd_path/) { + # Likewise, the 'cmd_path' message breaks out argv[0]. + # + # This line is only emitted when RUNTIME_PREFIX is defined, + # so just omit it for testing purposes. + # print "cmd_path _EXE_\n"; + } + else { + print "$line"; + } +} diff --git a/t/t0211-trace2-perf.sh b/t/t0211-trace2-perf.sh new file mode 100755 index 0000000000..953e2f7847 --- /dev/null +++ b/t/t0211-trace2-perf.sh @@ -0,0 +1,153 @@ +#!/bin/sh + +test_description='test trace2 facility (perf target)' +. ./test-lib.sh + +# Add t/helper directory to PATH so that we can use a relative +# path to run nested instances of test-tool.exe (see 004child). +# This helps with HEREDOC comparisons later. +TTDIR="$GIT_BUILD_DIR/t/helper/" && export TTDIR +PATH="$TTDIR:$PATH" && export PATH + +# Warning: use of 'test_cmp' may run test-tool.exe and/or git.exe +# Warning: to do the actual diff/comparison, so the HEREDOCs here +# Warning: only cover our actual calls to test-tool and/or git. +# Warning: So you may see extra lines in artifact files when +# Warning: interactively debugging. + +# Turn off any inherited trace2 settings for this test. +unset GIT_TR2 GIT_TR2_PERF GIT_TR2_EVENT +unset GIT_TR2_PERF_BRIEF +unset GIT_TR2_CONFIG_PARAMS + +V=$(git version | sed -e 's/^git version //') && export V + +# There are multiple trace2 targets: normal, perf, and event. +# Trace2 events will/can be written to each active target (subject +# to whatever filtering that target decides to do). +# Test each target independently. +# +# Defer setting GIT_TR2_PERF until the actual command we want to +# test because hidden git and test-tool commands in the test +# harness can contaminate our output. + +# Enable "brief" feature which turns off the prefix: +# "<clock> <file>:<line> | <nr_parents> | " +GIT_TR2_PERF_BRIEF=1 && export GIT_TR2_PERF_BRIEF + +# Repeat some of the t0210 tests using the perf target stream instead of +# the normal stream. +# +# Tokens here of the form _FIELD_ have been replaced in the observed output. + +# Verb 001return +# +# Implicit return from cmd_<verb> function propagates <code>. + +test_expect_success 'perf stream, return code 0' ' + test_when_finished "rm trace.perf actual expect" && + GIT_TR2_PERF="$(pwd)/trace.perf" test-tool trace2 001return 0 && + perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual && + cat >expect <<-EOF && + d0|main|version|||||$V + d0|main|start|||||_EXE_ trace2 001return 0 + d0|main|cmd_name|||||trace2 (trace2) + d0|main|exit||_T_ABS_|||code:0 + d0|main|atexit||_T_ABS_|||code:0 + EOF + test_cmp expect actual +' + +test_expect_success 'perf stream, return code 1' ' + test_when_finished "rm trace.perf actual expect" && + test_must_fail env GIT_TR2_PERF="$(pwd)/trace.perf" test-tool trace2 001return 1 && + perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual && + cat >expect <<-EOF && + d0|main|version|||||$V + d0|main|start|||||_EXE_ trace2 001return 1 + d0|main|cmd_name|||||trace2 (trace2) + d0|main|exit||_T_ABS_|||code:1 + d0|main|atexit||_T_ABS_|||code:1 + EOF + test_cmp expect actual +' + +# Verb 003error +# +# To the above, add multiple 'error <msg>' events + +test_expect_success 'perf stream, error event' ' + test_when_finished "rm trace.perf actual expect" && + GIT_TR2_PERF="$(pwd)/trace.perf" test-tool trace2 003error "hello world" "this is a test" && + perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual && + cat >expect <<-EOF && + d0|main|version|||||$V + d0|main|start|||||_EXE_ trace2 003error '\''hello world'\'' '\''this is a test'\'' + d0|main|cmd_name|||||trace2 (trace2) + d0|main|error|||||hello world + d0|main|error|||||this is a test + d0|main|exit||_T_ABS_|||code:0 + d0|main|atexit||_T_ABS_|||code:0 + EOF + test_cmp expect actual +' + +# Verb 004child +# +# Test nested spawning of child processes. +# +# Conceptually, this looks like: +# P1: TT trace2 004child +# P2: |--- TT trace2 004child +# P3: |--- TT trace2 001return 0 +# +# Which should generate events: +# P1: version +# P1: start +# P1: cmd_name +# P1: child_start +# P2: version +# P2: start +# P2: cmd_name +# P2: child_start +# P3: version +# P3: start +# P3: cmd_name +# P3: exit +# P3: atexit +# P2: child_exit +# P2: exit +# P2: atexit +# P1: child_exit +# P1: exit +# P1: atexit + +test_expect_success 'perf stream, child processes' ' + test_when_finished "rm trace.perf actual expect" && + GIT_TR2_PERF="$(pwd)/trace.perf" test-tool trace2 004child test-tool trace2 004child test-tool trace2 001return 0 && + perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual && + cat >expect <<-EOF && + d0|main|version|||||$V + d0|main|start|||||_EXE_ trace2 004child test-tool trace2 004child test-tool trace2 001return 0 + d0|main|cmd_name|||||trace2 (trace2) + d0|main|child_start||_T_ABS_|||[ch0] class:? argv: test-tool trace2 004child test-tool trace2 001return 0 + d1|main|version|||||$V + d1|main|start|||||_EXE_ trace2 004child test-tool trace2 001return 0 + d1|main|cmd_name|||||trace2 (trace2/trace2) + d1|main|child_start||_T_ABS_|||[ch0] class:? argv: test-tool trace2 001return 0 + d2|main|version|||||$V + d2|main|start|||||_EXE_ trace2 001return 0 + d2|main|cmd_name|||||trace2 (trace2/trace2/trace2) + d2|main|exit||_T_ABS_|||code:0 + d2|main|atexit||_T_ABS_|||code:0 + d1|main|child_exit||_T_ABS_|_T_REL_||[ch0] pid:_PID_ code:0 + d1|main|exit||_T_ABS_|||code:0 + d1|main|atexit||_T_ABS_|||code:0 + d0|main|child_exit||_T_ABS_|_T_REL_||[ch0] pid:_PID_ code:0 + d0|main|exit||_T_ABS_|||code:0 + d0|main|atexit||_T_ABS_|||code:0 + EOF + test_cmp expect actual +' + +test_done diff --git a/t/t0211/scrub_perf.perl b/t/t0211/scrub_perf.perl new file mode 100644 index 0000000000..351af7844e --- /dev/null +++ b/t/t0211/scrub_perf.perl @@ -0,0 +1,76 @@ +#!/usr/bin/perl +# +# Scrub the variable fields from the perf trace2 output to +# make testing easier. + +use strict; +use warnings; + +my $qpath = '\'[^\']*\'|[^ ]*'; + +my $col_depth=0; +my $col_thread=1; +my $col_event=2; +my $col_repo=3; +my $col_t_abs=4; +my $col_t_rel=5; +my $col_category=6; +my $col_rest=7; + +# This code assumes that the trace2 data was written with bare +# turned on (which omits the "<clock> <file>:<line> | <parents>" +# prefix. + +while (<>) { + my @tokens = split /\|/; + + foreach my $col (@tokens) { $col =~ s/^\s+|\s+$//g; } + + if ($tokens[$col_event] =~ m/^start/) { + # The 'start' message lists the contents of argv in $col_rest. + # On some platforms (Windows), argv[0] is *sometimes* a canonical + # absolute path to the EXE rather than the value passed in the + # shell script. Replace it with a placeholder to simplify our + # HEREDOC in the test script. + my $argv0; + my $argvRest; + $tokens[$col_rest] =~ s/^($qpath)\W*(.*)/_EXE_ $2/; + } + elsif ($tokens[$col_event] =~ m/cmd_path/) { + # Likewise, the 'cmd_path' message breaks out argv[0]. + # + # This line is only emitted when RUNTIME_PREFIX is defined, + # so just omit it for testing purposes. + # $tokens[$col_rest] = "_EXE_"; + goto SKIP_LINE; + } + elsif ($tokens[$col_event] =~ m/child_exit/) { + $tokens[$col_rest] =~ s/ pid:\d* / pid:_PID_ /; + } + elsif ($tokens[$col_event] =~ m/data/) { + if ($tokens[$col_category] =~ m/process/) { + # 'data' and 'data_json' events containing 'process' + # category data are assumed to be platform-specific + # and highly variable. Just omit them. + goto SKIP_LINE; + } + } + + # t_abs and t_rel are either blank or a float. Replace the float + # with a constant for matching the HEREDOC in the test script. + if ($tokens[$col_t_abs] =~ m/\d/) { + $tokens[$col_t_abs] = "_T_ABS_"; + } + if ($tokens[$col_t_rel] =~ m/\d/) { + $tokens[$col_t_rel] = "_T_REL_"; + } + + my $out; + + $out = join('|', @tokens); + print "$out\n"; + + SKIP_LINE: +} + + diff --git a/t/t0212-trace2-event.sh b/t/t0212-trace2-event.sh new file mode 100755 index 0000000000..028b6c5671 --- /dev/null +++ b/t/t0212-trace2-event.sh @@ -0,0 +1,236 @@ +#!/bin/sh + +test_description='test trace2 facility' +. ./test-lib.sh + +perl -MJSON::PP -e 0 >/dev/null 2>&1 && test_set_prereq JSON_PP + +# Add t/helper directory to PATH so that we can use a relative +# path to run nested instances of test-tool.exe (see 004child). +# This helps with HEREDOC comparisons later. +TTDIR="$GIT_BUILD_DIR/t/helper/" && export TTDIR +PATH="$TTDIR:$PATH" && export PATH + +# Warning: use of 'test_cmp' may run test-tool.exe and/or git.exe +# Warning: to do the actual diff/comparison, so the HEREDOCs here +# Warning: only cover our actual calls to test-tool and/or git. +# Warning: So you may see extra lines in artifact files when +# Warning: interactively debugging. + +# Turn off any inherited trace2 settings for this test. +unset GIT_TR2 GIT_TR2_PERF GIT_TR2_EVENT +unset GIT_TR2_BARE +unset GIT_TR2_CONFIG_PARAMS + +V=$(git version | sed -e 's/^git version //') && export V + +# There are multiple trace2 targets: normal, perf, and event. +# Trace2 events will/can be written to each active target (subject +# to whatever filtering that target decides to do). +# Test each target independently. +# +# Defer setting GIT_TR2_PERF until the actual command we want to +# test because hidden git and test-tool commands in the test +# harness can contaminate our output. + +# We don't bother repeating the 001return and 002exit tests, since they +# have coverage in the normal and perf targets. + +# Verb 003error +# +# To the above, add multiple 'error <msg>' events + +test_expect_success JSON_PP 'event stream, error event' ' + test_when_finished "rm trace.event actual expect" && + GIT_TR2_EVENT="$(pwd)/trace.event" test-tool trace2 003error "hello world" "this is a test" && + perl "$TEST_DIRECTORY/t0212/parse_events.perl" <trace.event >actual && + sed -e "s/^|//" >expect <<-EOF && + |VAR1 = { + | "_SID0_":{ + | "argv":[ + | "_EXE_", + | "trace2", + | "003error", + | "hello world", + | "this is a test" + | ], + | "errors":[ + | "%s", + | "%s" + | ], + | "exit_code":0, + | "hierarchy":"trace2", + | "name":"trace2", + | "version":"$V" + | } + |}; + EOF + test_cmp expect actual +' + +# Verb 004child +# +# Test nested spawning of child processes. +# +# Conceptually, this looks like: +# P1: TT trace2 004child +# P2: |--- TT trace2 004child +# P3: |--- TT trace2 001return 0 + +test_expect_success JSON_PP 'event stream, return code 0' ' + test_when_finished "rm trace.event actual expect" && + GIT_TR2_EVENT="$(pwd)/trace.event" test-tool trace2 004child test-tool trace2 004child test-tool trace2 001return 0 && + perl "$TEST_DIRECTORY/t0212/parse_events.perl" <trace.event >actual && + sed -e "s/^|//" >expect <<-EOF && + |VAR1 = { + | "_SID0_":{ + | "argv":[ + | "_EXE_", + | "trace2", + | "004child", + | "test-tool", + | "trace2", + | "004child", + | "test-tool", + | "trace2", + | "001return", + | "0" + | ], + | "child":{ + | "0":{ + | "child_argv":[ + | "_EXE_", + | "trace2", + | "004child", + | "test-tool", + | "trace2", + | "001return", + | "0" + | ], + | "child_class":"?", + | "child_code":0, + | "use_shell":0 + | } + | }, + | "exit_code":0, + | "hierarchy":"trace2", + | "name":"trace2", + | "version":"$V" + | }, + | "_SID0_/_SID1_":{ + | "argv":[ + | "_EXE_", + | "trace2", + | "004child", + | "test-tool", + | "trace2", + | "001return", + | "0" + | ], + | "child":{ + | "0":{ + | "child_argv":[ + | "_EXE_", + | "trace2", + | "001return", + | "0" + | ], + | "child_class":"?", + | "child_code":0, + | "use_shell":0 + | } + | }, + | "exit_code":0, + | "hierarchy":"trace2/trace2", + | "name":"trace2", + | "version":"$V" + | }, + | "_SID0_/_SID1_/_SID2_":{ + | "argv":[ + | "_EXE_", + | "trace2", + | "001return", + | "0" + | ], + | "exit_code":0, + | "hierarchy":"trace2/trace2/trace2", + | "name":"trace2", + | "version":"$V" + | } + |}; + EOF + test_cmp expect actual +' + +# Test listing of all "interesting" config settings. + +test_expect_success JSON_PP 'event stream, list config' ' + test_when_finished "rm trace.event actual expect" && + git config --local t0212.abc 1 && + git config --local t0212.def "hello world" && + GIT_TR2_EVENT="$(pwd)/trace.event" GIT_TR2_CONFIG_PARAMS="t0212.*" test-tool trace2 001return 0 && + perl "$TEST_DIRECTORY/t0212/parse_events.perl" <trace.event >actual && + sed -e "s/^|//" >expect <<-EOF && + |VAR1 = { + | "_SID0_":{ + | "argv":[ + | "_EXE_", + | "trace2", + | "001return", + | "0" + | ], + | "exit_code":0, + | "hierarchy":"trace2", + | "name":"trace2", + | "params":[ + | { + | "param":"t0212.abc", + | "value":"1" + | }, + | { + | "param":"t0212.def", + | "value":"hello world" + | } + | ], + | "version":"$V" + | } + |}; + EOF + test_cmp expect actual +' + +test_expect_success JSON_PP 'basic trace2_data' ' + test_when_finished "rm trace.event actual expect" && + GIT_TR2_EVENT="$(pwd)/trace.event" test-tool trace2 006data test_category k1 v1 test_category k2 v2 && + perl "$TEST_DIRECTORY/t0212/parse_events.perl" <trace.event >actual && + sed -e "s/^|//" >expect <<-EOF && + |VAR1 = { + | "_SID0_":{ + | "argv":[ + | "_EXE_", + | "trace2", + | "006data", + | "test_category", + | "k1", + | "v1", + | "test_category", + | "k2", + | "v2" + | ], + | "data":{ + | "test_category":{ + | "k1":"v1", + | "k2":"v2" + | } + | }, + | "exit_code":0, + | "hierarchy":"trace2", + | "name":"trace2", + | "version":"$V" + | } + |}; + EOF + test_cmp expect actual +' + +test_done diff --git a/t/t0212/parse_events.perl b/t/t0212/parse_events.perl new file mode 100644 index 0000000000..a2776ba216 --- /dev/null +++ b/t/t0212/parse_events.perl @@ -0,0 +1,251 @@ +#!/usr/bin/perl +# +# Parse event stream and convert individual events into a summary +# record for the process. +# +# Git.exe generates one or more "event" records for each API method, +# such as "start <argv>" and "exit <code>", during the life of the git +# process. Additionally, the input may contain interleaved events +# from multiple concurrent git processes and/or multiple threads from +# within a git process. +# +# Accumulate events for each process (based on its unique SID) in a +# dictionary and emit process summary records. +# +# Convert some of the variable fields (such as elapsed time) into +# placeholders (or omit them) to make HEREDOC comparisons easier in +# the test scripts. +# +# We may also omit fields not (currently) useful for testing purposes. + +use strict; +use warnings; +use JSON::PP; +use Data::Dumper; +use Getopt::Long; + +# The version of the trace2 event target format that we understand. +# This is reported in the 'version' event in the 'evt' field. +# It comes from the GIT_TR2_EVENT_VERSION macro in trace2/tr2_tgt_event.c +my $evt_version = '1'; + +my $show_children = 1; +my $show_exec = 1; +my $show_threads = 1; + +# A hack to generate test HEREDOC data for pasting into the test script. +# Usage: +# cd "t/trash directory.t0212-trace2-event" +# $TT trace ... >trace.event +# VV=$(../../git.exe version | sed -e 's/^git version //') +# perl ../t0212/parse_events.perl --HEREDOC --VERSION=$VV <trace.event >heredoc +# Then paste heredoc into your new test. + +my $gen_heredoc = 0; +my $gen_version = ''; + +GetOptions("children!" => \$show_children, + "exec!" => \$show_exec, + "threads!" => \$show_threads, + "HEREDOC!" => \$gen_heredoc, + "VERSION=s" => \$gen_version ) + or die("Error in command line arguments\n"); + + +# SIDs contains timestamps and PIDs of the process and its parents. +# This makes it difficult to match up in a HEREDOC in the test script. +# Build a map from actual SIDs to predictable constant values and yet +# keep the parent/child relationships. For example: +# {..., "sid":"1539706952458276-8652", ...} +# {..., "sid":"1539706952458276-8652/1539706952649493-15452", ...} +# becomes: +# {..., "sid":"_SID1_", ...} +# {..., "sid":"_SID1_/_SID2_", ...} +my $sid_map; +my $sid_count = 0; + +my $processes; + +while (<>) { + my $line = decode_json( $_ ); + + my $sid = ""; + my $sid_sep = ""; + + my $raw_sid = $line->{'sid'}; + my @raw_sid_parts = split /\//, $raw_sid; + foreach my $raw_sid_k (@raw_sid_parts) { + if (!exists $sid_map->{$raw_sid_k}) { + $sid_map->{$raw_sid_k} = '_SID' . $sid_count . '_'; + $sid_count++; + } + $sid = $sid . $sid_sep . $sid_map->{$raw_sid_k}; + $sid_sep = '/'; + } + + my $event = $line->{'event'}; + + if ($event eq 'version') { + $processes->{$sid}->{'version'} = $line->{'exe'}; + if ($gen_heredoc == 1 && $gen_version eq $line->{'exe'}) { + # If we are generating data FOR the test script, replace + # the reported git.exe version with a reference to an + # environment variable. When our output is pasted into + # the test script, it will then be expanded in future + # test runs to the THEN current version of git.exe. + # We assume that the test script uses env var $V. + $processes->{$sid}->{'version'} = "\$V"; + } + } + + elsif ($event eq 'start') { + $processes->{$sid}->{'argv'} = $line->{'argv'}; + $processes->{$sid}->{'argv'}[0] = "_EXE_"; + } + + elsif ($event eq 'exit') { + $processes->{$sid}->{'exit_code'} = $line->{'code'}; + } + + elsif ($event eq 'atexit') { + $processes->{$sid}->{'exit_code'} = $line->{'code'}; + } + + elsif ($event eq 'error') { + # For HEREDOC purposes, use the error message format string if + # available, rather than the formatted message (which probably + # has an absolute pathname). + if (exists $line->{'fmt'}) { + push( @{$processes->{$sid}->{'errors'}}, $line->{'fmt'} ); + } + elsif (exists $line->{'msg'}) { + push( @{$processes->{$sid}->{'errors'}}, $line->{'msg'} ); + } + } + + elsif ($event eq 'cmd_path') { + ## $processes->{$sid}->{'path'} = $line->{'path'}; + # + # Like in the 'start' event, we need to replace the value of + # argv[0] with a token for HEREDOC purposes. However, the + # event is only emitted when RUNTIME_PREFIX is defined, so + # just omit it for testing purposes. + # $processes->{$sid}->{'path'} = "_EXE_"; + } + + elsif ($event eq 'cmd_name') { + $processes->{$sid}->{'name'} = $line->{'name'}; + $processes->{$sid}->{'hierarchy'} = $line->{'hierarchy'}; + } + + elsif ($event eq 'alias') { + $processes->{$sid}->{'alias'}->{'key'} = $line->{'alias'}; + $processes->{$sid}->{'alias'}->{'argv'} = $line->{'argv'}; + } + + elsif ($event eq 'def_param') { + my $kv; + $kv->{'param'} = $line->{'param'}; + $kv->{'value'} = $line->{'value'}; + push( @{$processes->{$sid}->{'params'}}, $kv ); + } + + elsif ($event eq 'child_start') { + if ($show_children == 1) { + $processes->{$sid}->{'child'}->{$line->{'child_id'}}->{'child_class'} = $line->{'child_class'}; + $processes->{$sid}->{'child'}->{$line->{'child_id'}}->{'child_argv'} = $line->{'argv'}; + $processes->{$sid}->{'child'}->{$line->{'child_id'}}->{'child_argv'}[0] = "_EXE_"; + $processes->{$sid}->{'child'}->{$line->{'child_id'}}->{'use_shell'} = $line->{'use_shell'} ? 1 : 0; + } + } + + elsif ($event eq 'child_exit') { + if ($show_children == 1) { + $processes->{$sid}->{'child'}->{$line->{'child_id'}}->{'child_code'} = $line->{'code'}; + } + } + + # TODO decide what information we want to test from thread events. + + elsif ($event eq 'thread_start') { + if ($show_threads == 1) { + } + } + + elsif ($event eq 'thread_exit') { + if ($show_threads == 1) { + } + } + + # TODO decide what information we want to test from exec events. + + elsif ($event eq 'exec') { + if ($show_exec == 1) { + } + } + + elsif ($event eq 'exec_result') { + if ($show_exec == 1) { + } + } + + elsif ($event eq 'def_param') { + # Accumulate parameter key/value pairs by key rather than in an array + # so that we get overwrite (last one wins) effects. + $processes->{$sid}->{'params'}->{$line->{'param'}} = $line->{'value'}; + } + + elsif ($event eq 'def_repo') { + # $processes->{$sid}->{'repos'}->{$line->{'repo'}} = $line->{'worktree'}; + $processes->{$sid}->{'repos'}->{$line->{'repo'}} = "_WORKTREE_"; + } + + # A series of potentially nested and threaded region and data events + # is fundamentally incompatibile with the type of summary record we + # are building in this script. Since they are intended for + # perf-trace-like analysis rather than a result summary, we ignore + # most of them here. + + # elsif ($event eq 'region_enter') { + # } + # elsif ($event eq 'region_leave') { + # } + + elsif ($event eq 'data') { + my $cat = $line->{'category'}; + if ($cat eq 'test_category') { + + my $key = $line->{'key'}; + my $value = $line->{'value'}; + $processes->{$sid}->{'data'}->{$cat}->{$key} = $value; + } + } + + # This trace2 target does not emit 'printf' events. + # + # elsif ($event eq 'printf') { + # } +} + +# Dump the resulting hash into something that we can compare against +# in the test script. These options make Dumper output look a little +# bit like JSON. Also convert variable references of the form "$VAR*" +# so that the matching HEREDOC doesn't need to escape it. + +$Data::Dumper::Sortkeys = 1; +$Data::Dumper::Indent = 1; +$Data::Dumper::Purity = 1; +$Data::Dumper::Pair = ':'; + +my $out = Dumper($processes); +$out =~ s/'/"/g; +$out =~ s/\$VAR/VAR/g; + +# Finally, if we're running this script to generate (manually confirmed) +# data to add to the test script, guard the indentation. + +if ($gen_heredoc == 1) { + $out =~ s/^/\t\|/gms; +} + +print $out; diff --git a/t/t1050-large.sh b/t/t1050-large.sh index 1a9b21b293..dcb4dbba67 100755 --- a/t/t1050-large.sh +++ b/t/t1050-large.sh @@ -8,7 +8,7 @@ test_description='adding and checking out large blobs' # This should be moved to test-lib.sh together with the # copy in t0021 after both topics have graduated to 'master'. file_size () { - perl -e 'print -s $ARGV[0]' "$1" + test-tool path-utils file-size "$1" } test_expect_success setup ' diff --git a/t/t1300-config.sh b/t/t1300-config.sh index 9652b241c7..428177c390 100755 --- a/t/t1300-config.sh +++ b/t/t1300-config.sh @@ -892,6 +892,7 @@ test_expect_success 'get --expiry-date' ' 1510348087 0 EOF + : "work around heredoc parsing bug fixed in dash 0.5.7 (in ec2c84d)" && { echo "$rel_out $(git config --expiry-date date.valid1)" git config --expiry-date date.valid2 && diff --git a/t/t1404-update-ref-errors.sh b/t/t1404-update-ref-errors.sh index 51a4f4c0ac..6b6a8e2292 100755 --- a/t/t1404-update-ref-errors.sh +++ b/t/t1404-update-ref-errors.sh @@ -614,7 +614,7 @@ test_expect_success 'delete fails cleanly if packed-refs file is locked' ' test_when_finished "rm -f .git/packed-refs.lock" && test_must_fail git update-ref -d $prefix/foo >out 2>err && git for-each-ref $prefix >actual && - test_i18ngrep "Unable to create $Q.*packed-refs.lock$Q: File exists" err && + test_i18ngrep "Unable to create $Q.*packed-refs.lock$Q: " err && test_cmp unchanged actual ' diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh index 2e5e979336..c61f972141 100755 --- a/t/t1450-fsck.sh +++ b/t/t1450-fsck.sh @@ -406,7 +406,7 @@ test_expect_success 'rev-list --verify-objects with bad sha1' ' test_might_fail git rev-list --verify-objects refs/heads/bogus >/dev/null 2>out && cat out && - test_i18ngrep -q "error: sha1 mismatch 63ffffffffffffffffffffffffffffffffffffff" out + test_i18ngrep -q "error: hash mismatch 63ffffffffffffffffffffffffffffffffffffff" out ' test_expect_success 'force fsck to ignore double author' ' diff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh index e4d5b56014..c19fb500cb 100755 --- a/t/t1512-rev-parse-disambiguation.sh +++ b/t/t1512-rev-parse-disambiguation.sh @@ -388,4 +388,14 @@ test_expect_success C_LOCALE_OUTPUT 'ambiguous commits are printed by type first done ' +test_expect_success 'cat-file --batch and --batch-check show ambiguous' ' + echo "0000 ambiguous" >expect && + echo 0000 | git cat-file --batch-check >actual 2>err && + test_cmp expect actual && + test_i18ngrep hint: err && + echo 0000 | git cat-file --batch >actual 2>err && + test_cmp expect actual && + test_i18ngrep hint: err +' + test_done diff --git a/t/t1700-split-index.sh b/t/t1700-split-index.sh index 4667e1a190..4f2f84f309 100755 --- a/t/t1700-split-index.sh +++ b/t/t1700-split-index.sh @@ -472,4 +472,22 @@ test_expect_success 'writing split index with null sha1 does not write cache tre test_line_count = 0 cache-tree.out ' +test_expect_success 'do not refresh null base index' ' + test_create_repo merge && + ( + cd merge && + test_commit initial && + git checkout -b side-branch && + test_commit extra && + git checkout master && + git update-index --split-index && + test_commit more && + # must not write a new shareindex, or we wont catch the problem + git -c splitIndex.maxPercentChange=100 merge --no-edit side-branch 2>err && + # i.e. do not expect warnings like + # could not freshen shared index .../shareindex.00000... + test_must_be_empty err + ) +' + test_done diff --git a/t/t2018-checkout-branch.sh b/t/t2018-checkout-branch.sh index 2131fb2a56..c5014ad9a6 100755 --- a/t/t2018-checkout-branch.sh +++ b/t/t2018-checkout-branch.sh @@ -198,4 +198,13 @@ test_expect_success 'checkout -B to the current branch works' ' test_dirty_mergeable ' +test_expect_success 'checkout -b after clone --no-checkout does a checkout of HEAD' ' + git init src && + test_commit -C src a && + rev="$(git -C src rev-parse HEAD)" && + git clone --no-checkout src dest && + git -C dest checkout "$rev" -b branch && + test_path_is_file dest/a.t +' + test_done diff --git a/t/t2025-checkout-no-overlay.sh b/t/t2025-checkout-no-overlay.sh new file mode 100755 index 0000000000..76330cb5ab --- /dev/null +++ b/t/t2025-checkout-no-overlay.sh @@ -0,0 +1,47 @@ +#!/bin/sh + +test_description='checkout --no-overlay <tree-ish> -- <pathspec>' + +. ./test-lib.sh + +test_expect_success 'setup' ' + git commit --allow-empty -m "initial" +' + +test_expect_success 'checkout --no-overlay deletes files not in <tree-ish>' ' + >file && + mkdir dir && + >dir/file1 && + git add file dir/file1 && + git checkout --no-overlay HEAD -- file && + test_path_is_missing file && + test_path_is_file dir/file1 +' + +test_expect_success 'checkout --no-overlay removing last file from directory' ' + git checkout --no-overlay HEAD -- dir/file1 && + test_path_is_missing dir +' + +test_expect_success 'checkout -p --overlay is disallowed' ' + test_must_fail git checkout -p --overlay HEAD 2>actual && + test_i18ngrep "fatal: -p and --overlay are mutually exclusive" actual +' + +test_expect_success '--no-overlay --theirs with D/F conflict deletes file' ' + test_commit file1 file1 && + test_commit file2 file2 && + git rm --cached file1 && + echo 1234 >file1 && + F1=$(git rev-parse HEAD:file1) && + F2=$(git rev-parse HEAD:file2) && + { + echo "100644 $F1 1 file1" && + echo "100644 $F2 2 file1" + } | git update-index --index-info && + test_path_is_file file1 && + git checkout --theirs --no-overlay -- file1 && + test_path_is_missing file1 +' + +test_done diff --git a/t/t2025-worktree-add.sh b/t/t2400-worktree-add.sh index 286bba35d8..286bba35d8 100755 --- a/t/t2025-worktree-add.sh +++ b/t/t2400-worktree-add.sh diff --git a/t/t2026-worktree-prune.sh b/t/t2401-worktree-prune.sh index b7d6d5d45a..b7d6d5d45a 100755 --- a/t/t2026-worktree-prune.sh +++ b/t/t2401-worktree-prune.sh diff --git a/t/t2027-worktree-list.sh b/t/t2402-worktree-list.sh index bb6fb9b12c..bb6fb9b12c 100755 --- a/t/t2027-worktree-list.sh +++ b/t/t2402-worktree-list.sh diff --git a/t/t2028-worktree-move.sh b/t/t2403-worktree-move.sh index 939d18d728..939d18d728 100755 --- a/t/t2028-worktree-move.sh +++ b/t/t2403-worktree-move.sh diff --git a/t/t2029-worktree-config.sh b/t/t2404-worktree-config.sh index 286121d8de..286121d8de 100755 --- a/t/t2029-worktree-config.sh +++ b/t/t2404-worktree-config.sh diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh index ee6787614c..be55148930 100755 --- a/t/t3203-branch-output.sh +++ b/t/t3203-branch-output.sh @@ -100,6 +100,50 @@ test_expect_success 'git branch -v pattern does not show branch summaries' ' test_must_fail git branch -v branch* ' +test_expect_success 'git branch `--show-current` shows current branch' ' + cat >expect <<-\EOF && + branch-two + EOF + git checkout branch-two && + git branch --show-current >actual && + test_cmp expect actual +' + +test_expect_success 'git branch `--show-current` is silent when detached HEAD' ' + git checkout HEAD^0 && + git branch --show-current >actual && + test_must_be_empty actual +' + +test_expect_success 'git branch `--show-current` works properly when tag exists' ' + cat >expect <<-\EOF && + branch-and-tag-name + EOF + test_when_finished " + git checkout branch-one + git branch -D branch-and-tag-name + " && + git checkout -b branch-and-tag-name && + test_when_finished "git tag -d branch-and-tag-name" && + git tag branch-and-tag-name && + git branch --show-current >actual && + test_cmp expect actual +' + +test_expect_success 'git branch `--show-current` works properly with worktrees' ' + cat >expect <<-\EOF && + branch-one + branch-two + EOF + git checkout branch-one && + git worktree add worktree branch-two && + { + git branch --show-current && + git -C worktree branch --show-current + } >actual && + test_cmp expect actual +' + test_expect_success 'git branch shows detached HEAD properly' ' cat >expect <<EOF && * (HEAD detached at $(git rev-parse --short HEAD^0)) diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 7a440e08d8..b60b11f9f2 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -147,6 +147,28 @@ test_expect_success 'rebase -i with the exec command checks tree cleanness' ' git rebase --continue ' +test_expect_success 'rebase -x with empty command fails' ' + test_when_finished "git rebase --abort ||:" && + test_must_fail env GIT_TEST_REBASE_USE_BUILTIN=true \ + git rebase -x "" @ 2>actual && + test_write_lines "error: empty exec command" >expected && + test_i18ncmp expected actual && + test_must_fail env GIT_TEST_REBASE_USE_BUILTIN=true \ + git rebase -x " " @ 2>actual && + test_i18ncmp expected actual +' + +LF=' +' +test_expect_success 'rebase -x with newline in command fails' ' + test_when_finished "git rebase --abort ||:" && + test_must_fail env GIT_TEST_REBASE_USE_BUILTIN=true \ + git rebase -x "a${LF}b" @ 2>actual && + test_write_lines "error: exec commands cannot contain newlines" \ + >expected && + test_i18ncmp expected actual +' + test_expect_success 'rebase -i with exec of inexistent command' ' git checkout master && test_when_finished "git rebase --abort" && @@ -156,6 +178,11 @@ test_expect_success 'rebase -i with exec of inexistent command' ' ! grep "Maybe git-rebase is broken" actual ' +test_expect_success 'implicit interactive rebase does not invoke sequence editor' ' + test_when_finished "git rebase --abort ||:" && + GIT_SEQUENCE_EDITOR="echo bad >" git rebase -x"echo one" @^ +' + test_expect_success 'no changes are a nop' ' git checkout branch2 && set_fake_editor && diff --git a/t/t3406-rebase-message.sh b/t/t3406-rebase-message.sh index f64b130cb8..b393e1e9fe 100755 --- a/t/t3406-rebase-message.sh +++ b/t/t3406-rebase-message.sh @@ -17,14 +17,9 @@ test_expect_success 'setup' ' git tag start ' -cat >expect <<\EOF -Already applied: 0001 A -Already applied: 0002 B -Committed: 0003 Z -EOF - test_expect_success 'rebase -m' ' git rebase -m master >report && + >expect && sed -n -e "/^Already applied: /p" \ -e "/^Committed: /p" report >actual && test_cmp expect actual diff --git a/t/t3418-rebase-continue.sh b/t/t3418-rebase-continue.sh index 0210b2ac6f..bdaa511bb0 100755 --- a/t/t3418-rebase-continue.sh +++ b/t/t3418-rebase-continue.sh @@ -254,4 +254,15 @@ test_expect_success 'the todo command "break" works' ' test_path_is_file execed ' +test_expect_success '--reschedule-failed-exec' ' + test_when_finished "git rebase --abort" && + test_must_fail git rebase -x false --reschedule-failed-exec HEAD^ && + grep "^exec false" .git/rebase-merge/git-rebase-todo && + git rebase --abort && + test_must_fail git -c rebase.rescheduleFailedExec=true \ + rebase -x false HEAD^ 2>err && + grep "^exec false" .git/rebase-merge/git-rebase-todo && + test_i18ngrep "has been rescheduled" err +' + test_done diff --git a/t/t3420-rebase-autostash.sh b/t/t3420-rebase-autostash.sh index 4c7494cc8f..2d1094e483 100755 --- a/t/t3420-rebase-autostash.sh +++ b/t/t3420-rebase-autostash.sh @@ -53,41 +53,6 @@ create_expected_success_interactive () { EOF } -create_expected_success_merge () { - cat >expected <<-EOF - $(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual) - HEAD is now at $(git rev-parse --short feature-branch) third commit - First, rewinding head to replay your work on top of it... - Merging unrelated-onto-branch with HEAD~1 - Merging: - $(git rev-parse --short unrelated-onto-branch) unrelated commit - $(git rev-parse --short feature-branch^) second commit - found 1 common ancestor: - $(git rev-parse --short feature-branch~2) initial commit - [detached HEAD $(git rev-parse --short rebased-feature-branch~1)] second commit - Author: A U Thor <author@example.com> - Date: Thu Apr 7 15:14:13 2005 -0700 - 2 files changed, 2 insertions(+) - create mode 100644 file1 - create mode 100644 file2 - Committed: 0001 second commit - Merging unrelated-onto-branch with HEAD~0 - Merging: - $(git rev-parse --short rebased-feature-branch~1) second commit - $(git rev-parse --short feature-branch) third commit - found 1 common ancestor: - $(git rev-parse --short feature-branch~1) second commit - [detached HEAD $(git rev-parse --short rebased-feature-branch)] third commit - Author: A U Thor <author@example.com> - Date: Thu Apr 7 15:15:13 2005 -0700 - 1 file changed, 1 insertion(+) - create mode 100644 file3 - Committed: 0002 third commit - All done. - Applied autostash. - EOF -} - create_expected_failure_am () { cat >expected <<-EOF $(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual) @@ -112,43 +77,6 @@ create_expected_failure_interactive () { EOF } -create_expected_failure_merge () { - cat >expected <<-EOF - $(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual) - HEAD is now at $(git rev-parse --short feature-branch) third commit - First, rewinding head to replay your work on top of it... - Merging unrelated-onto-branch with HEAD~1 - Merging: - $(git rev-parse --short unrelated-onto-branch) unrelated commit - $(git rev-parse --short feature-branch^) second commit - found 1 common ancestor: - $(git rev-parse --short feature-branch~2) initial commit - [detached HEAD $(git rev-parse --short rebased-feature-branch~1)] second commit - Author: A U Thor <author@example.com> - Date: Thu Apr 7 15:14:13 2005 -0700 - 2 files changed, 2 insertions(+) - create mode 100644 file1 - create mode 100644 file2 - Committed: 0001 second commit - Merging unrelated-onto-branch with HEAD~0 - Merging: - $(git rev-parse --short rebased-feature-branch~1) second commit - $(git rev-parse --short feature-branch) third commit - found 1 common ancestor: - $(git rev-parse --short feature-branch~1) second commit - [detached HEAD $(git rev-parse --short rebased-feature-branch)] third commit - Author: A U Thor <author@example.com> - Date: Thu Apr 7 15:15:13 2005 -0700 - 1 file changed, 1 insertion(+) - create mode 100644 file3 - Committed: 0002 third commit - All done. - Applying autostash resulted in conflicts. - Your changes are safe in the stash. - You can run "git stash pop" or "git stash drop" at any time. - EOF -} - testrebase () { type=$1 dotest=$2 @@ -177,6 +105,9 @@ testrebase () { test_expect_success "rebase$type --autostash: check output" ' test_when_finished git branch -D rebased-feature-branch && suffix=${type#\ --} && suffix=${suffix:-am} && + if test ${suffix} = "merge"; then + suffix=interactive + fi && create_expected_success_$suffix && test_i18ncmp expected actual ' @@ -274,6 +205,9 @@ testrebase () { test_expect_success "rebase$type: check output with conflicting stash" ' test_when_finished git branch -D rebased-feature-branch && suffix=${type#\ --} && suffix=${suffix:-am} && + if test ${suffix} = "merge"; then + suffix=interactive + fi && create_expected_failure_$suffix && test_i18ncmp expected actual ' diff --git a/t/t3421-rebase-topology-linear.sh b/t/t3421-rebase-topology-linear.sh index 23ad4cff35..7274dca40b 100755 --- a/t/t3421-rebase-topology-linear.sh +++ b/t/t3421-rebase-topology-linear.sh @@ -111,7 +111,7 @@ test_run_rebase () { " } test_run_rebase success '' -test_run_rebase failure -m +test_run_rebase success -m test_run_rebase success -i test_have_prereq !REBASE_P || test_run_rebase success -p @@ -126,7 +126,7 @@ test_run_rebase () { " } test_run_rebase success '' -test_run_rebase failure -m +test_run_rebase success -m test_run_rebase success -i test_have_prereq !REBASE_P || test_run_rebase success -p @@ -141,7 +141,7 @@ test_run_rebase () { " } test_run_rebase success '' -test_run_rebase failure -m +test_run_rebase success -m test_run_rebase success -i test_have_prereq !REBASE_P || test_run_rebase success -p @@ -284,7 +284,7 @@ test_run_rebase () { " } test_run_rebase success '' -test_run_rebase failure -m +test_run_rebase success -m test_run_rebase success -i test_have_prereq !REBASE_P || test_run_rebase success -p @@ -315,7 +315,7 @@ test_run_rebase () { " } test_run_rebase success '' -test_run_rebase failure -m +test_run_rebase success -m test_run_rebase success -i test_have_prereq !REBASE_P || test_run_rebase failure -p diff --git a/t/t3425-rebase-topology-merges.sh b/t/t3425-rebase-topology-merges.sh index 5f892e33d7..fd8efe84fe 100755 --- a/t/t3425-rebase-topology-merges.sh +++ b/t/t3425-rebase-topology-merges.sh @@ -70,9 +70,8 @@ test_run_rebase () { test_linear_range "\'"$expected"\'" d.. " } -#TODO: make order consistent across all flavors of rebase -test_run_rebase success 'e n o' '' -test_run_rebase success 'e n o' -m +test_run_rebase success 'n o e' '' +test_run_rebase success 'n o e' -m test_run_rebase success 'n o e' -i test_run_rebase () { @@ -87,9 +86,8 @@ test_run_rebase () { test_linear_range "\'"$expected"\'" c.. " } -#TODO: make order consistent across all flavors of rebase -test_run_rebase success 'd e n o' '' -test_run_rebase success 'd e n o' -m +test_run_rebase success 'd n o e' '' +test_run_rebase success 'd n o e' -m test_run_rebase success 'd n o e' -i test_run_rebase () { @@ -104,9 +102,8 @@ test_run_rebase () { test_linear_range "\'"$expected"\'" c.. " } -#TODO: make order consistent across all flavors of rebase -test_run_rebase success 'd e n o' '' -test_run_rebase success 'd e n o' -m +test_run_rebase success 'd n o e' '' +test_run_rebase success 'd n o e' -m test_run_rebase success 'd n o e' -i if ! test_have_prereq REBASE_P; then diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh index cc5646836f..4c69255ee6 100755 --- a/t/t3430-rebase-merges.sh +++ b/t/t3430-rebase-merges.sh @@ -125,7 +125,7 @@ test_expect_success '`reset` refuses to overwrite untracked files' ' : >dont-overwrite-untracked.t && echo "reset refs/tags/dont-overwrite-untracked" >script-from-scratch && test_config sequence.editor \""$PWD"/replace-editor.sh\" && - test_must_fail git rebase -r HEAD && + test_must_fail git rebase -ir HEAD && git rebase --abort ' diff --git a/t/t3700-add.sh b/t/t3700-add.sh index 37729ba258..be582a513b 100755 --- a/t/t3700-add.sh +++ b/t/t3700-add.sh @@ -402,4 +402,11 @@ test_expect_success 'all statuses changed in folder if . is given' ' test $(git ls-files --stage | grep ^100755 | wc -l) -eq 0 ' +test_expect_success CASE_INSENSITIVE_FS 'path is case-insensitive' ' + path="$(pwd)/BLUB" && + touch "$path" && + downcased="$(echo "$path" | tr A-Z a-z)" && + git add "$downcased" +' + test_done diff --git a/t/t4006-diff-mode.sh b/t/t4006-diff-mode.sh index a8e01eccd1..03489aff14 100755 --- a/t/t4006-diff-mode.sh +++ b/t/t4006-diff-mode.sh @@ -32,28 +32,37 @@ test_expect_success 'prepare binary file' ' git commit -m binbin ' -# test_expect_success '--stat output after text chmod' ' -# test_chmod -x rezrov && -# echo " 0 files changed" >expect && -# git diff HEAD --stat >actual && -# test_i18ncmp expect actual -# ' -# -# test_expect_success '--shortstat output after text chmod' ' -# git diff HEAD --shortstat >actual && -# test_i18ncmp expect actual -# ' -# -# test_expect_success '--stat output after binary chmod' ' -# test_chmod +x binbin && -# echo " 0 files changed" >expect && -# git diff HEAD --stat >actual && -# test_i18ncmp expect actual -# ' -# -# test_expect_success '--shortstat output after binary chmod' ' -# git diff HEAD --shortstat >actual && -# test_i18ncmp expect actual -# ' +test_expect_success '--stat output after text chmod' ' + test_chmod -x rezrov && + cat >expect <<-\EOF && + rezrov | 0 + 1 file changed, 0 insertions(+), 0 deletions(-) + EOF + git diff HEAD --stat >actual && + test_i18ncmp expect actual +' + +test_expect_success '--shortstat output after text chmod' ' + tail -n 1 <expect >expect.short && + git diff HEAD --shortstat >actual && + test_i18ncmp expect.short actual +' + +test_expect_success '--stat output after binary chmod' ' + test_chmod +x binbin && + cat >expect <<-EOF && + binbin | Bin + rezrov | 0 + 2 files changed, 0 insertions(+), 0 deletions(-) + EOF + git diff HEAD --stat >actual && + test_i18ncmp expect actual +' + +test_expect_success '--shortstat output after binary chmod' ' + tail -n 1 <expect >expect.short && + git diff HEAD --shortstat >actual && + test_i18ncmp expect.short actual +' test_done diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh index 7d985ff6b1..9f8f0e84ad 100755 --- a/t/t4013-diff-various.sh +++ b/t/t4013-diff-various.sh @@ -98,6 +98,12 @@ test_expect_success setup ' git commit -m "update mode" && git checkout -f master && + # Same merge as master, but with parents reversed. Hide it in a + # pseudo-ref to avoid impacting tests with --all. + commit=$(echo reverse | + git commit-tree -p master^2 -p master^1 master^{tree}) && + git update-ref REVERSE $commit && + git config diff.renames false && git show-branch @@ -239,6 +245,8 @@ diff-tree --cc --stat --summary master # stat summary should show the diffstat and summary with the first parent diff-tree -c --stat --summary side diff-tree --cc --stat --summary side +diff-tree --cc --shortstat master +diff-tree --cc --summary REVERSE # improved by Timo's patch diff-tree --cc --patch-with-stat master # improved by Timo's patch @@ -350,6 +358,7 @@ diff --line-prefix=abc master master^ side diff --dirstat master~1 master~2 diff --dirstat initial rearrange diff --dirstat-by-file initial rearrange +diff --dirstat --cc master~1 master # No-index --abbrev and --no-abbrev diff --raw initial :noellipses diff --raw initial diff --git a/t/t4013/diff.diff-tree_--cc_--shortstat_master b/t/t4013/diff.diff-tree_--cc_--shortstat_master new file mode 100644 index 0000000000..a4ca42df2a --- /dev/null +++ b/t/t4013/diff.diff-tree_--cc_--shortstat_master @@ -0,0 +1,4 @@ +$ git diff-tree --cc --shortstat master +59d314ad6f356dd08601a4cd5e530381da3e3c64 + 2 files changed, 5 insertions(+) +$ diff --git a/t/t4013/diff.diff-tree_--cc_--summary_REVERSE b/t/t4013/diff.diff-tree_--cc_--summary_REVERSE new file mode 100644 index 0000000000..e208dd5682 --- /dev/null +++ b/t/t4013/diff.diff-tree_--cc_--summary_REVERSE @@ -0,0 +1,6 @@ +$ git diff-tree --cc --summary REVERSE +2562325a7ee916efb2481da93073b82cec801cbc + create mode 100644 file1 + delete mode 100644 file2 + delete mode 100644 file3 +$ diff --git a/t/t4013/diff.diff_--dirstat_--cc_master~1_master b/t/t4013/diff.diff_--dirstat_--cc_master~1_master new file mode 100644 index 0000000000..fba4e34175 --- /dev/null +++ b/t/t4013/diff.diff_--dirstat_--cc_master~1_master @@ -0,0 +1,3 @@ +$ git diff --dirstat --cc master~1 master + 40.0% dir/ +$ diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh index 9a3e4fdfec..ab4670d236 100755 --- a/t/t4015-diff-whitespace.sh +++ b/t/t4015-diff-whitespace.sh @@ -1802,8 +1802,8 @@ test_expect_success 'only move detection ignores white spaces' ' <BOLD;MAGENTA>-a long line to exceed per-line minimum<RESET> <BOLD;MAGENTA>-another long line to exceed per-line minimum<RESET> <RED>-original file<RESET> - <BOLD;YELLOW>+<RESET>Q<BOLD;YELLOW>a long line to exceed per-line minimum<RESET> - <BOLD;YELLOW>+<RESET>Q<BOLD;YELLOW>another long line to exceed per-line minimum<RESET> + <BOLD;CYAN>+<RESET>Q<BOLD;CYAN>a long line to exceed per-line minimum<RESET> + <BOLD;CYAN>+<RESET>Q<BOLD;CYAN>another long line to exceed per-line minimum<RESET> <GREEN>+<RESET><GREEN>new file<RESET> EOF test_cmp expected actual @@ -1827,6 +1827,7 @@ test_expect_success 'compare whitespace delta across moved blocks' ' QQQthat has similar lines QQQto previous blocks, but with different indent QQQYetQAnotherQoutlierQ + QLine with internal w h i t e s p a c e change EOF git add text.txt && @@ -1847,6 +1848,7 @@ test_expect_success 'compare whitespace delta across moved blocks' ' QQthat has similar lines QQto previous blocks, but with different indent QQYetQAnotherQoutlier + QLine with internal whitespace change EOF git diff --color --color-moved --color-moved-ws=allow-indentation-change >actual.raw && @@ -1856,7 +1858,7 @@ test_expect_success 'compare whitespace delta across moved blocks' ' <BOLD>diff --git a/text.txt b/text.txt<RESET> <BOLD>--- a/text.txt<RESET> <BOLD>+++ b/text.txt<RESET> - <CYAN>@@ -1,14 +1,14 @@<RESET> + <CYAN>@@ -1,15 +1,15 @@<RESET> <BOLD;MAGENTA>-QIndented<RESET> <BOLD;MAGENTA>-QText across<RESET> <BOLD;MAGENTA>-Qsome lines<RESET> @@ -1871,6 +1873,7 @@ test_expect_success 'compare whitespace delta across moved blocks' ' <BOLD;MAGENTA>-QQQthat has similar lines<RESET> <BOLD;MAGENTA>-QQQto previous blocks, but with different indent<RESET> <RED>-QQQYetQAnotherQoutlierQ<RESET> + <RED>-QLine with internal w h i t e s p a c e change<RESET> <BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>Indented<RESET> <BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>Text across<RESET> <BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>some lines<RESET> @@ -1885,6 +1888,7 @@ test_expect_success 'compare whitespace delta across moved blocks' ' <BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>that has similar lines<RESET> <BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>to previous blocks, but with different indent<RESET> <GREEN>+<RESET>QQ<GREEN>YetQAnotherQoutlier<RESET> + <GREEN>+<RESET>Q<GREEN>Line with internal whitespace change<RESET> EOF test_cmp expected actual @@ -1915,4 +1919,93 @@ test_expect_success 'compare whitespace delta incompatible with other space opti test_i18ngrep allow-indentation-change err ' +EMPTY='' +test_expect_success 'compare mixed whitespace delta across moved blocks' ' + + git reset --hard && + tr Q_ "\t " <<-EOF >text.txt && + ${EMPTY} + ____too short without + ${EMPTY} + ___being grouped across blank line + ${EMPTY} + context + lines + to + anchor + ____Indented text to + _Q____be further indented by four spaces across + ____Qseveral lines + QQ____These two lines have had their + ____indentation reduced by four spaces + Qdifferent indentation change + ____too short + EOF + + git add text.txt && + git commit -m "add text.txt" && + + tr Q_ "\t " <<-EOF >text.txt && + context + lines + to + anchor + QIndented text to + QQbe further indented by four spaces across + Q____several lines + ${EMPTY} + QQtoo short without + ${EMPTY} + Q_______being grouped across blank line + ${EMPTY} + Q_QThese two lines have had their + indentation reduced by four spaces + QQdifferent indentation change + __Qtoo short + EOF + + git -c color.diff.whitespace="normal red" \ + -c core.whitespace=space-before-tab \ + diff --color --color-moved --ws-error-highlight=all \ + --color-moved-ws=allow-indentation-change >actual.raw && + grep -v "index" actual.raw | test_decode_color >actual && + + cat <<-\EOF >expected && + <BOLD>diff --git a/text.txt b/text.txt<RESET> + <BOLD>--- a/text.txt<RESET> + <BOLD>+++ b/text.txt<RESET> + <CYAN>@@ -1,16 +1,16 @@<RESET> + <BOLD;MAGENTA>-<RESET> + <BOLD;MAGENTA>-<RESET><BOLD;MAGENTA> too short without<RESET> + <BOLD;MAGENTA>-<RESET> + <BOLD;MAGENTA>-<RESET><BOLD;MAGENTA> being grouped across blank line<RESET> + <BOLD;MAGENTA>-<RESET> + <RESET>context<RESET> + <RESET>lines<RESET> + <RESET>to<RESET> + <RESET>anchor<RESET> + <BOLD;MAGENTA>-<RESET><BOLD;MAGENTA> Indented text to<RESET> + <BOLD;MAGENTA>-<RESET><BRED> <RESET> <BOLD;MAGENTA> be further indented by four spaces across<RESET> + <BOLD;MAGENTA>-<RESET><BRED> <RESET> <BOLD;MAGENTA>several lines<RESET> + <BOLD;BLUE>-<RESET> <BOLD;BLUE> These two lines have had their<RESET> + <BOLD;BLUE>-<RESET><BOLD;BLUE> indentation reduced by four spaces<RESET> + <BOLD;MAGENTA>-<RESET> <BOLD;MAGENTA>different indentation change<RESET> + <RED>-<RESET><RED> too short<RESET> + <BOLD;CYAN>+<RESET> <BOLD;CYAN>Indented text to<RESET> + <BOLD;CYAN>+<RESET> <BOLD;CYAN>be further indented by four spaces across<RESET> + <BOLD;CYAN>+<RESET> <BOLD;CYAN> several lines<RESET> + <BOLD;YELLOW>+<RESET> + <BOLD;YELLOW>+<RESET> <BOLD;YELLOW>too short without<RESET> + <BOLD;YELLOW>+<RESET> + <BOLD;YELLOW>+<RESET> <BOLD;YELLOW> being grouped across blank line<RESET> + <BOLD;YELLOW>+<RESET> + <BOLD;CYAN>+<RESET> <BRED> <RESET> <BOLD;CYAN>These two lines have had their<RESET> + <BOLD;CYAN>+<RESET><BOLD;CYAN>indentation reduced by four spaces<RESET> + <BOLD;YELLOW>+<RESET> <BOLD;YELLOW>different indentation change<RESET> + <GREEN>+<RESET><BRED> <RESET> <GREEN>too short<RESET> + EOF + + test_cmp expected actual +' + test_done diff --git a/t/t4038-diff-combined.sh b/t/t4038-diff-combined.sh index e2824d3437..07b49f6d6d 100755 --- a/t/t4038-diff-combined.sh +++ b/t/t4038-diff-combined.sh @@ -435,4 +435,92 @@ test_expect_success 'combine diff gets tree sorting right' ' test_cmp expect actual ' +test_expect_success 'setup for --combined-all-paths' ' + git branch side1c && + git branch side2c && + git checkout side1c && + test_seq 1 10 >filename-side1c && + git add filename-side1c && + git commit -m with && + git checkout side2c && + test_seq 1 9 >filename-side2c && + echo ten >>filename-side2c && + git add filename-side2c && + git commit -m iam && + git checkout -b mergery side1c && + git merge --no-commit side2c && + git rm filename-side1c && + echo eleven >>filename-side2c && + git mv filename-side2c filename-merged && + git add filename-merged && + git commit +' + +test_expect_success '--combined-all-paths and --raw' ' + cat <<-\EOF >expect && + ::100644 100644 100644 f00c965d8307308469e537302baa73048488f162 088bd5d92c2a8e0203ca8e7e4c2a5c692f6ae3f7 333b9c62519f285e1854830ade0fe1ef1d40ee1b RR filename-side1c filename-side2c filename-merged + EOF + git diff-tree -c -M --raw --combined-all-paths HEAD >actual.tmp && + sed 1d <actual.tmp >actual && + test_cmp expect actual +' + +test_expect_success '--combined-all-paths and --cc' ' + cat <<-\EOF >expect && + --- a/filename-side1c + --- a/filename-side2c + +++ b/filename-merged + EOF + git diff-tree --cc -M --combined-all-paths HEAD >actual.tmp && + grep ^[-+][-+][-+] <actual.tmp >actual && + test_cmp expect actual +' + +test_expect_success FUNNYNAMES 'setup for --combined-all-paths with funny names' ' + git branch side1d && + git branch side2d && + git checkout side1d && + test_seq 1 10 >$(printf "file\twith\ttabs") && + git add file* && + git commit -m with && + git checkout side2d && + test_seq 1 9 >$(printf "i\tam\ttabbed") && + echo ten >>$(printf "i\tam\ttabbed") && + git add *tabbed && + git commit -m iam && + git checkout -b funny-names-mergery side1d && + git merge --no-commit side2d && + git rm *tabs && + echo eleven >>$(printf "i\tam\ttabbed") && + git mv "$(printf "i\tam\ttabbed")" "$(printf "fickle\tnaming")" && + git add fickle* && + git commit +' + +test_expect_success FUNNYNAMES '--combined-all-paths and --raw and funny names' ' + cat <<-\EOF >expect && + ::100644 100644 100644 f00c965d8307308469e537302baa73048488f162 088bd5d92c2a8e0203ca8e7e4c2a5c692f6ae3f7 333b9c62519f285e1854830ade0fe1ef1d40ee1b RR "file\twith\ttabs" "i\tam\ttabbed" "fickle\tnaming" + EOF + git diff-tree -c -M --raw --combined-all-paths HEAD >actual.tmp && + sed 1d <actual.tmp >actual && + test_cmp expect actual +' + +test_expect_success FUNNYNAMES '--combined-all-paths and --raw -and -z and funny names' ' + printf "aaf8087c3cbd4db8e185a2d074cf27c53cfb75d7\0::100644 100644 100644 f00c965d8307308469e537302baa73048488f162 088bd5d92c2a8e0203ca8e7e4c2a5c692f6ae3f7 333b9c62519f285e1854830ade0fe1ef1d40ee1b RR\0file\twith\ttabs\0i\tam\ttabbed\0fickle\tnaming\0" >expect && + git diff-tree -c -M --raw --combined-all-paths -z HEAD >actual && + test_cmp -a expect actual +' + +test_expect_success FUNNYNAMES '--combined-all-paths and --cc and funny names' ' + cat <<-\EOF >expect && + --- "a/file\twith\ttabs" + --- "a/i\tam\ttabbed" + +++ "b/fickle\tnaming" + EOF + git diff-tree --cc -M --combined-all-paths HEAD >actual.tmp && + grep ^[-+][-+][-+] <actual.tmp >actual && + test_cmp expect actual +' + test_done diff --git a/t/t4066-diff-emit-delay.sh b/t/t4066-diff-emit-delay.sh new file mode 100755 index 0000000000..5df6b5e64e --- /dev/null +++ b/t/t4066-diff-emit-delay.sh @@ -0,0 +1,79 @@ +#!/bin/sh + +test_description='test combined/stat/moved interaction' +. ./test-lib.sh + +# This test covers a weird 3-way interaction between "--cc -p", which will run +# the combined diff code, along with "--stat", which will be computed as a +# first-parent stat during the combined diff, and "--color-moved", which +# enables the emitted_symbols list to store the diff in memory. + +test_expect_success 'set up history with a merge' ' + test_commit A && + test_commit B && + git checkout -b side HEAD^ && + test_commit C && + git merge -m M master && + test_commit D +' + +test_expect_success 'log --cc -p --stat --color-moved' ' + cat >expect <<-\EOF && + commit D + --- + D.t | 1 + + 1 file changed, 1 insertion(+) + + diff --git a/D.t b/D.t + new file mode 100644 + index 0000000..1784810 + --- /dev/null + +++ b/D.t + @@ -0,0 +1 @@ + +D + commit M + + B.t | 1 + + 1 file changed, 1 insertion(+) + commit C + --- + C.t | 1 + + 1 file changed, 1 insertion(+) + + diff --git a/C.t b/C.t + new file mode 100644 + index 0000000..3cc58df + --- /dev/null + +++ b/C.t + @@ -0,0 +1 @@ + +C + commit B + --- + B.t | 1 + + 1 file changed, 1 insertion(+) + + diff --git a/B.t b/B.t + new file mode 100644 + index 0000000..223b783 + --- /dev/null + +++ b/B.t + @@ -0,0 +1 @@ + +B + commit A + --- + A.t | 1 + + 1 file changed, 1 insertion(+) + + diff --git a/A.t b/A.t + new file mode 100644 + index 0000000..f70f10e + --- /dev/null + +++ b/A.t + @@ -0,0 +1 @@ + +A + EOF + git log --format="commit %s" --cc -p --stat --color-moved >actual && + test_cmp expect actual +' + +test_done diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh index 978a8a66ff..f42a69faa2 100755 --- a/t/t4205-log-pretty-formats.sh +++ b/t/t4205-log-pretty-formats.sh @@ -578,6 +578,24 @@ test_expect_success '%(trailers:only) shows only "key: value" trailers' ' test_cmp expect actual ' +test_expect_success '%(trailers:only=yes) shows only "key: value" trailers' ' + git log --no-walk --pretty=format:"%(trailers:only=yes)" >actual && + grep -v patch.description <trailers >expect && + test_cmp expect actual +' + +test_expect_success '%(trailers:only=no) shows all trailers' ' + git log --no-walk --pretty=format:"%(trailers:only=no)" >actual && + cat trailers >expect && + test_cmp expect actual +' + +test_expect_success '%(trailers:only=no,only=true) shows only "key: value" trailers' ' + git log --no-walk --pretty=format:"%(trailers:only=yes)" >actual && + grep -v patch.description <trailers >expect && + test_cmp expect actual +' + test_expect_success '%(trailers:unfold) unfolds trailers' ' git log --no-walk --pretty="%(trailers:unfold)" >actual && { @@ -598,6 +616,105 @@ test_expect_success ':only and :unfold work together' ' test_cmp expect actual ' +test_expect_success 'pretty format %(trailers:key=foo) shows that trailer' ' + git log --no-walk --pretty="format:%(trailers:key=Acked-by)" >actual && + echo "Acked-by: A U Thor <author@example.com>" >expect && + test_cmp expect actual +' + +test_expect_success 'pretty format %(trailers:key=foo) is case insensitive' ' + git log --no-walk --pretty="format:%(trailers:key=AcKed-bY)" >actual && + echo "Acked-by: A U Thor <author@example.com>" >expect && + test_cmp expect actual +' + +test_expect_success 'pretty format %(trailers:key=foo:) trailing colon also works' ' + git log --no-walk --pretty="format:%(trailers:key=Acked-by:)" >actual && + echo "Acked-by: A U Thor <author@example.com>" >expect && + test_cmp expect actual +' + +test_expect_success 'pretty format %(trailers:key=foo) multiple keys' ' + git log --no-walk --pretty="format:%(trailers:key=Acked-by:,key=Signed-off-By)" >actual && + grep -v patch.description <trailers >expect && + test_cmp expect actual +' + +test_expect_success '%(trailers:key=nonexistant) becomes empty' ' + git log --no-walk --pretty="x%(trailers:key=Nacked-by)x" >actual && + echo "xx" >expect && + test_cmp expect actual +' + +test_expect_success '%(trailers:key=foo) handles multiple lines even if folded' ' + git log --no-walk --pretty="format:%(trailers:key=Signed-Off-by)" >actual && + grep -v patch.description <trailers | grep -v Acked-by >expect && + test_cmp expect actual +' + +test_expect_success '%(trailers:key=foo,unfold) properly unfolds' ' + git log --no-walk --pretty="format:%(trailers:key=Signed-Off-by,unfold)" >actual && + unfold <trailers | grep Signed-off-by >expect && + test_cmp expect actual +' + +test_expect_success 'pretty format %(trailers:key=foo,only=no) also includes nontrailer lines' ' + git log --no-walk --pretty="format:%(trailers:key=Acked-by,only=no)" >actual && + { + echo "Acked-by: A U Thor <author@example.com>" && + grep patch.description <trailers + } >expect && + test_cmp expect actual +' + +test_expect_success '%(trailers:key) without value is error' ' + git log --no-walk --pretty="tformat:%(trailers:key)" >actual && + echo "%(trailers:key)" >expect && + test_cmp expect actual +' + +test_expect_success '%(trailers:key=foo,valueonly) shows only value' ' + git log --no-walk --pretty="format:%(trailers:key=Acked-by,valueonly)" >actual && + echo "A U Thor <author@example.com>" >expect && + test_cmp expect actual +' + +test_expect_success 'pretty format %(trailers:separator) changes separator' ' + git log --no-walk --pretty=format:"X%(trailers:separator=%x00,unfold)X" >actual && + printf "XSigned-off-by: A U Thor <author@example.com>\0Acked-by: A U Thor <author@example.com>\0[ v2 updated patch description ]\0Signed-off-by: A U Thor <author@example.com>X" >expect && + test_cmp expect actual +' + +test_expect_success 'pretty format %(trailers) combining separator/key/valueonly' ' + git commit --allow-empty -F - <<-\EOF && + Important fix + + The fix is explained here + + Closes: #1234 + EOF + + git commit --allow-empty -F - <<-\EOF && + Another fix + + The fix is explained here + + Closes: #567 + Closes: #890 + EOF + + git commit --allow-empty -F - <<-\EOF && + Does not close any tickets + EOF + + git log --pretty="%s% (trailers:separator=%x2c%x20,key=Closes,valueonly)" HEAD~3.. >actual && + test_write_lines \ + "Does not close any tickets" \ + "Another fix #567, #890" \ + "Important fix #1234" >expect && + test_cmp expect actual +' + test_expect_success 'trailer parsing not fooled by --- line' ' git commit --allow-empty -F - <<-\EOF && this is the subject @@ -621,4 +738,54 @@ test_expect_success 'trailer parsing not fooled by --- line' ' test_cmp expect actual ' +test_expect_success 'set up %S tests' ' + git checkout --orphan source-a && + test_commit one && + test_commit two && + git checkout -b source-b HEAD^ && + test_commit three +' + +test_expect_success 'log --format=%S paints branch names' ' + cat >expect <<-\EOF && + source-b + source-a + source-b + EOF + git log --format=%S source-a source-b >actual && + test_cmp expect actual +' + +test_expect_success 'log --format=%S paints tag names' ' + git tag -m tagged source-tag && + cat >expect <<-\EOF && + source-tag + source-a + source-tag + EOF + git log --format=%S source-tag source-a >actual && + test_cmp expect actual +' + +test_expect_success 'log --format=%S paints symmetric ranges' ' + cat >expect <<-\EOF && + source-b + source-a + EOF + git log --format=%S source-a...source-b >actual && + test_cmp expect actual +' + +test_expect_success '%S in git log --format works with other placeholders (part 1)' ' + git log --format="source-b %h" source-b >expect && + git log --format="%S %h" source-b >actual && + test_cmp expect actual +' + +test_expect_success '%S in git log --format works with other placeholders (part 2)' ' + git log --format="%h source-b" source-b >expect && + git log --format="%h %S" source-b >actual && + test_cmp expect actual +' + test_done diff --git a/t/t5304-prune.sh b/t/t5304-prune.sh index 270da21ac3..1eeb828a15 100755 --- a/t/t5304-prune.sh +++ b/t/t5304-prune.sh @@ -118,10 +118,10 @@ test_expect_success 'prune: do not prune detached HEAD with no reflog' ' test_expect_success 'prune: prune former HEAD after checking out branch' ' - head_sha1=$(git rev-parse HEAD) && + head_oid=$(git rev-parse HEAD) && git checkout --quiet master && git prune -v >prune_actual && - grep "$head_sha1" prune_actual + grep "$head_oid" prune_actual ' @@ -265,15 +265,27 @@ EOF ' test_expect_success 'prune .git/shallow' ' - SHA1=$(echo hi|git commit-tree HEAD^{tree}) && - echo $SHA1 >.git/shallow && + oid=$(echo hi|git commit-tree HEAD^{tree}) && + echo $oid >.git/shallow && git prune --dry-run >out && - grep $SHA1 .git/shallow && - grep $SHA1 out && + grep $oid .git/shallow && + grep $oid out && git prune && test_path_is_missing .git/shallow ' +test_expect_success 'prune .git/shallow when there are no loose objects' ' + oid=$(echo hi|git commit-tree HEAD^{tree}) && + echo $oid >.git/shallow && + git update-ref refs/heads/shallow-tip $oid && + git repack -ad && + # verify assumption that all loose objects are gone + git count-objects | grep ^0 && + git prune && + echo $oid >expect && + test_cmp expect .git/shallow +' + test_expect_success 'prune: handle alternate object database' ' test_create_repo A && git -C A commit --allow-empty -m "initial commit" && @@ -314,8 +326,8 @@ test_expect_success 'prune: handle HEAD reflog in multiple worktrees' ' git reset --hard HEAD^ ) && git prune --expire=now && - SHA1=`git hash-object expected` && - git -C third-worktree show "$SHA1" >actual && + oid=`git hash-object expected` && + git -C third-worktree show "$oid" >actual && test_cmp expected actual ' diff --git a/t/t5315-pack-objects-compression.sh b/t/t5315-pack-objects-compression.sh index 34c47dae09..df970d7584 100755 --- a/t/t5315-pack-objects-compression.sh +++ b/t/t5315-pack-objects-compression.sh @@ -7,7 +7,7 @@ test_description='pack-object compression configuration' # This should be moved to test-lib.sh together with the # copy in t0021 after both topics have graduated to 'master'. file_size () { - perl -e 'print -s $ARGV[0]' "$1" + test-tool path-utils file-size "$1" } test_expect_success setup ' diff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh index 5fe21db99f..561796f280 100755 --- a/t/t5318-commit-graph.sh +++ b/t/t5318-commit-graph.sh @@ -122,7 +122,7 @@ test_expect_success 'write graph with merges' ' cd "$TRASH_DIRECTORY/full" && git commit-graph write && test_path_is_file $objdir/info/commit-graph && - graph_read_expect "10" "large_edges" + graph_read_expect "10" "extra_edges" ' graph_git_behavior 'merge 1 vs 2' full merge/1 merge/2 @@ -157,7 +157,7 @@ test_expect_success 'write graph with new commit' ' cd "$TRASH_DIRECTORY/full" && git commit-graph write && test_path_is_file $objdir/info/commit-graph && - graph_read_expect "11" "large_edges" + graph_read_expect "11" "extra_edges" ' graph_git_behavior 'full graph, commit 8 vs merge 1' full commits/8 merge/1 @@ -167,7 +167,7 @@ test_expect_success 'write graph with nothing new' ' cd "$TRASH_DIRECTORY/full" && git commit-graph write && test_path_is_file $objdir/info/commit-graph && - graph_read_expect "11" "large_edges" + graph_read_expect "11" "extra_edges" ' graph_git_behavior 'cleared graph, commit 8 vs merge 1' full commits/8 merge/1 @@ -177,7 +177,7 @@ test_expect_success 'build graph from latest pack with closure' ' cd "$TRASH_DIRECTORY/full" && cat new-idx | git commit-graph write --stdin-packs && test_path_is_file $objdir/info/commit-graph && - graph_read_expect "9" "large_edges" + graph_read_expect "9" "extra_edges" ' graph_git_behavior 'graph from pack, commit 8 vs merge 1' full commits/8 merge/1 @@ -200,7 +200,7 @@ test_expect_success 'build graph from commits with append' ' cd "$TRASH_DIRECTORY/full" && git rev-parse merge/3 | git commit-graph write --stdin-commits --append && test_path_is_file $objdir/info/commit-graph && - graph_read_expect "10" "large_edges" + graph_read_expect "10" "extra_edges" ' graph_git_behavior 'append graph, commit 8 vs merge 1' full commits/8 merge/1 @@ -210,7 +210,7 @@ test_expect_success 'build graph using --reachable' ' cd "$TRASH_DIRECTORY/full" && git commit-graph write --reachable && test_path_is_file $objdir/info/commit-graph && - graph_read_expect "11" "large_edges" + graph_read_expect "11" "extra_edges" ' graph_git_behavior 'append graph, commit 8 vs merge 1' full commits/8 merge/1 @@ -231,7 +231,7 @@ test_expect_success 'write graph in bare repo' ' cd "$TRASH_DIRECTORY/bare" && git commit-graph write && test_path_is_file $baredir/info/commit-graph && - graph_read_expect "11" "large_edges" + graph_read_expect "11" "extra_edges" ' graph_git_behavior 'bare repo with graph, commit 8 vs merge 1' bare commits/8 merge/1 @@ -366,9 +366,10 @@ GRAPH_OCTOPUS_DATA_OFFSET=$(($GRAPH_COMMIT_DATA_OFFSET + \ GRAPH_BYTE_OCTOPUS=$(($GRAPH_OCTOPUS_DATA_OFFSET + 4)) GRAPH_BYTE_FOOTER=$(($GRAPH_OCTOPUS_DATA_OFFSET + 4 * $NUM_OCTOPUS_EDGES)) -# usage: corrupt_graph_and_verify <position> <data> <string> +# usage: corrupt_graph_and_verify <position> <data> <string> [<zero_pos>] # Manipulates the commit-graph file at the position -# by inserting the data, then runs 'git commit-graph verify' +# by inserting the data, optionally zeroing the file +# starting at <zero_pos>, then runs 'git commit-graph verify' # and places the output in the file 'err'. Test 'err' for # the given string. corrupt_graph_and_verify() { @@ -376,11 +377,15 @@ corrupt_graph_and_verify() { data="${2:-\0}" grepstr=$3 cd "$TRASH_DIRECTORY/full" && + orig_size=$(wc -c < $objdir/info/commit-graph) && + zero_pos=${4:-${orig_size}} && test_when_finished mv commit-graph-backup $objdir/info/commit-graph && cp $objdir/info/commit-graph commit-graph-backup && printf "$data" | dd of="$objdir/info/commit-graph" bs=1 seek="$pos" conv=notrunc && + dd of="$objdir/info/commit-graph" bs=1 seek="$zero_pos" if=/dev/null && + generate_zero_bytes $(($orig_size - $zero_pos)) >>"$objdir/info/commit-graph" && test_must_fail git commit-graph verify 2>test_err && - grep -v "^+" test_err >err + grep -v "^+" test_err >err && test_i18ngrep "$grepstr" err } @@ -484,6 +489,11 @@ test_expect_success 'detect invalid checksum hash' ' "incorrect checksum" ' +test_expect_success 'detect incorrect chunk count' ' + corrupt_graph_and_verify $GRAPH_BYTE_CHUNK_COUNT "\377" \ + "chunk lookup table entry missing" $GRAPH_CHUNK_LOOKUP_OFFSET +' + test_expect_success 'git fsck (checks commit-graph)' ' cd "$TRASH_DIRECTORY/full" && git fsck && diff --git a/t/t5322-pack-objects-sparse.sh b/t/t5322-pack-objects-sparse.sh new file mode 100755 index 0000000000..7124b5581a --- /dev/null +++ b/t/t5322-pack-objects-sparse.sh @@ -0,0 +1,136 @@ +#!/bin/sh + +test_description='pack-objects object selection using sparse algorithm' +. ./test-lib.sh + +test_expect_success 'setup repo' ' + test_commit initial && + for i in $(test_seq 1 3) + do + mkdir f$i && + for j in $(test_seq 1 3) + do + mkdir f$i/f$j && + echo $j >f$i/f$j/data.txt + done + done && + git add . && + git commit -m "Initialized trees" && + for i in $(test_seq 1 3) + do + git checkout -b topic$i master && + echo change-$i >f$i/f$i/data.txt && + git commit -a -m "Changed f$i/f$i/data.txt" + done && + cat >packinput.txt <<-EOF && + topic1 + ^topic2 + ^topic3 + EOF + git rev-parse \ + topic1 \ + topic1^{tree} \ + topic1:f1 \ + topic1:f1/f1 \ + topic1:f1/f1/data.txt | sort >expect_objects.txt +' + +test_expect_success 'non-sparse pack-objects' ' + git pack-objects --stdout --revs --no-sparse <packinput.txt >nonsparse.pack && + git index-pack -o nonsparse.idx nonsparse.pack && + git show-index <nonsparse.idx | awk "{print \$2}" >nonsparse_objects.txt && + test_cmp expect_objects.txt nonsparse_objects.txt +' + +test_expect_success 'sparse pack-objects' ' + git pack-objects --stdout --revs --sparse <packinput.txt >sparse.pack && + git index-pack -o sparse.idx sparse.pack && + git show-index <sparse.idx | awk "{print \$2}" >sparse_objects.txt && + test_cmp expect_objects.txt sparse_objects.txt +' + +test_expect_success 'duplicate a folder from f3 and commit to topic1' ' + git checkout topic1 && + echo change-3 >f3/f3/data.txt && + git commit -a -m "Changed f3/f3/data.txt" && + git rev-parse \ + topic1~1 \ + topic1~1^{tree} \ + topic1^{tree} \ + topic1 \ + topic1:f1 \ + topic1:f1/f1 \ + topic1:f1/f1/data.txt | sort >required_objects.txt +' + +test_expect_success 'non-sparse pack-objects' ' + git pack-objects --stdout --revs --no-sparse <packinput.txt >nonsparse.pack && + git index-pack -o nonsparse.idx nonsparse.pack && + git show-index <nonsparse.idx | awk "{print \$2}" >nonsparse_objects.txt && + comm -1 -2 required_objects.txt nonsparse_objects.txt >nonsparse_required_objects.txt && + test_cmp required_objects.txt nonsparse_required_objects.txt +' + +test_expect_success 'sparse pack-objects' ' + git pack-objects --stdout --revs --sparse <packinput.txt >sparse.pack && + git index-pack -o sparse.idx sparse.pack && + git show-index <sparse.idx | awk "{print \$2}" >sparse_objects.txt && + comm -1 -2 required_objects.txt sparse_objects.txt >sparse_required_objects.txt && + test_cmp required_objects.txt sparse_required_objects.txt +' + +# Demonstrate that the algorithms differ when we copy a tree wholesale +# from one folder to another. + +test_expect_success 'duplicate a folder from f1 into f3' ' + mkdir f3/f4 && + cp -r f1/f1/* f3/f4 && + git add f3/f4 && + git commit -m "Copied f1/f1 to f3/f4" && + cat >packinput.txt <<-EOF && + topic1 + ^topic1~1 + EOF + git rev-parse \ + topic1 \ + topic1^{tree} \ + topic1:f3 | sort >required_objects.txt +' + +test_expect_success 'non-sparse pack-objects' ' + git pack-objects --stdout --revs --no-sparse <packinput.txt >nonsparse.pack && + git index-pack -o nonsparse.idx nonsparse.pack && + git show-index <nonsparse.idx | awk "{print \$2}" >nonsparse_objects.txt && + comm -1 -2 required_objects.txt nonsparse_objects.txt >nonsparse_required_objects.txt && + test_cmp required_objects.txt nonsparse_required_objects.txt +' + +test_expect_success 'sparse pack-objects' ' + git rev-parse \ + topic1 \ + topic1^{tree} \ + topic1:f3 \ + topic1:f3/f4 \ + topic1:f3/f4/data.txt | sort >expect_sparse_objects.txt && + git pack-objects --stdout --revs --sparse <packinput.txt >sparse.pack && + git index-pack -o sparse.idx sparse.pack && + git show-index <sparse.idx | awk "{print \$2}" >sparse_objects.txt && + test_cmp expect_sparse_objects.txt sparse_objects.txt +' + +test_expect_success 'pack.useSparse enables algorithm' ' + git config pack.useSparse true && + git pack-objects --stdout --revs <packinput.txt >sparse.pack && + git index-pack -o sparse.idx sparse.pack && + git show-index <sparse.idx | awk "{print \$2}" >sparse_objects.txt && + test_cmp expect_sparse_objects.txt sparse_objects.txt +' + +test_expect_success 'pack.useSparse overridden' ' + git pack-objects --stdout --revs --no-sparse <packinput.txt >sparse.pack && + git index-pack -o sparse.idx sparse.pack && + git show-index <sparse.idx | awk "{print \$2}" >sparse_objects.txt && + test_cmp required_objects.txt sparse_objects.txt +' + +test_done diff --git a/t/t5323-pack-redundant.sh b/t/t5323-pack-redundant.sh new file mode 100755 index 0000000000..6b4d1ca353 --- /dev/null +++ b/t/t5323-pack-redundant.sh @@ -0,0 +1,467 @@ +#!/bin/sh +# +# Copyright (c) 2018 Jiang Xin +# + +test_description='Test git pack-redundant + +In order to test git-pack-redundant, we will create a number of objects and +packs in the repository `master.git`. The relationship between packs (P1-P8) +and objects (T, A-R) is showed in the following chart. Objects of a pack will +be marked with letter x, while objects of redundant packs will be marked with +exclamation point, and redundant pack itself will be marked with asterisk. + + | T A B C D E F G H I J K L M N O P Q R + ----+-------------------------------------- + P1 | x x x x x x x x + P2* | ! ! ! ! ! ! ! + P3 | x x x x x x + P4* | ! ! ! ! ! + P5 | x x x x + P6* | ! ! ! + P7 | x x + P8* | ! + ----+-------------------------------------- + ALL | x x x x x x x x x x x x x x x x x x x + +Another repository `shared.git` has unique objects (X-Z), while other objects +(marked with letter s) are shared through alt-odb (of `master.git`). The +relationship between packs and objects is as follows: + + | T A B C D E F G H I J K L M N O P Q R X Y Z + ----+---------------------------------------------- + Px1 | s s s x x x + Px2 | s s s x x x +' + +. ./test-lib.sh + +master_repo=master.git +shared_repo=shared.git + +# Create commits in <repo> and assign each commit's oid to shell variables +# given in the arguments (A, B, and C). E.g.: +# +# create_commits_in <repo> A B C +# +# NOTE: Avoid calling this function from a subshell since variable +# assignments will disappear when subshell exits. +create_commits_in () { + repo="$1" && + if ! parent=$(git -C "$repo" rev-parse HEAD^{} 2>/dev/null) + then + parent= + fi && + T=$(git -C "$repo" write-tree) && + shift && + while test $# -gt 0 + do + name=$1 && + test_tick && + if test -z "$parent" + then + oid=$(echo $name | git -C "$repo" commit-tree $T) + else + oid=$(echo $name | git -C "$repo" commit-tree -p $parent $T) + fi && + eval $name=$oid && + parent=$oid && + shift || + return 1 + done && + git -C "$repo" update-ref refs/heads/master $oid +} + +# Create pack in <repo> and assign pack id to variable given in the 2nd argument +# (<name>). Commits in the pack will be read from stdin. E.g.: +# +# create_pack_in <repo> <name> <<-EOF +# ... +# EOF +# +# NOTE: commits from stdin should be given using heredoc, not using pipe, and +# avoid calling this function from a subshell since variable assignments will +# disappear when subshell exits. +create_pack_in () { + repo="$1" && + name="$2" && + pack=$(git -C "$repo/objects/pack" pack-objects -q pack) && + eval $name=$pack && + eval P$pack=$name:$pack +} + +format_packfiles () { + sed \ + -e "s#.*/pack-\(.*\)\.idx#\1#" \ + -e "s#.*/pack-\(.*\)\.pack#\1#" | + sort -u | + while read p + do + if test -z "$(eval echo \${P$p})" + then + echo $p + else + eval echo "\${P$p}" + fi + done | + sort +} + +test_expect_success 'setup master repo' ' + git init --bare "$master_repo" && + create_commits_in "$master_repo" A B C D E F G H I J K L M N O P Q R +' + +############################################################################# +# Chart of packs and objects for this test case +# +# | T A B C D E F G H I J K L M N O P Q R +# ----+-------------------------------------- +# P1 | x x x x x x x x +# P2 | x x x x x x x +# P3 | x x x x x x +# ----+-------------------------------------- +# ALL | x x x x x x x x x x x x x x x +# +############################################################################# +test_expect_success 'master: no redundant for pack 1, 2, 3' ' + create_pack_in "$master_repo" P1 <<-EOF && + $T + $A + $B + $C + $D + $E + $F + $R + EOF + create_pack_in "$master_repo" P2 <<-EOF && + $B + $C + $D + $E + $G + $H + $I + EOF + create_pack_in "$master_repo" P3 <<-EOF && + $F + $I + $J + $K + $L + $M + EOF + ( + cd "$master_repo" && + git pack-redundant --all >out && + test_must_be_empty out + ) +' + +############################################################################# +# Chart of packs and objects for this test case +# +# | T A B C D E F G H I J K L M N O P Q R +# ----+-------------------------------------- +# P1 | x x x x x x x x +# P2 | x x x x x x x +# P3* | ! ! ! ! ! ! +# P4 | x x x x x +# P5 | x x x x +# ----+-------------------------------------- +# ALL | x x x x x x x x x x x x x x x x x x +# +############################################################################# +test_expect_success 'master: one of pack-2/pack-3 is redundant' ' + create_pack_in "$master_repo" P4 <<-EOF && + $J + $K + $L + $M + $P + EOF + create_pack_in "$master_repo" P5 <<-EOF && + $G + $H + $N + $O + EOF + ( + cd "$master_repo" && + cat >expect <<-EOF && + P3:$P3 + EOF + git pack-redundant --all >out && + format_packfiles <out >actual && + test_cmp expect actual + ) +' + +############################################################################# +# Chart of packs and objects for this test case +# +# | T A B C D E F G H I J K L M N O P Q R +# ----+-------------------------------------- +# P1 | x x x x x x x x +# P2* | ! ! ! ! ! ! ! +# P3 | x x x x x x +# P4* | ! ! ! ! ! +# P5 | x x x x +# P6* | ! ! ! +# P7 | x x +# ----+-------------------------------------- +# ALL | x x x x x x x x x x x x x x x x x x x +# +############################################################################# +test_expect_success 'master: pack 2, 4, and 6 are redundant' ' + create_pack_in "$master_repo" P6 <<-EOF && + $N + $O + $Q + EOF + create_pack_in "$master_repo" P7 <<-EOF && + $P + $Q + EOF + ( + cd "$master_repo" && + cat >expect <<-EOF && + P2:$P2 + P4:$P4 + P6:$P6 + EOF + git pack-redundant --all >out && + format_packfiles <out >actual && + test_cmp expect actual + ) +' + +############################################################################# +# Chart of packs and objects for this test case +# +# | T A B C D E F G H I J K L M N O P Q R +# ----+-------------------------------------- +# P1 | x x x x x x x x +# P2* | ! ! ! ! ! ! ! +# P3 | x x x x x x +# P4* | ! ! ! ! ! +# P5 | x x x x +# P6* | ! ! ! +# P7 | x x +# P8* | ! +# ----+-------------------------------------- +# ALL | x x x x x x x x x x x x x x x x x x x +# +############################################################################# +test_expect_success 'master: pack-8 (subset of pack-1) is also redundant' ' + create_pack_in "$master_repo" P8 <<-EOF && + $A + EOF + ( + cd "$master_repo" && + cat >expect <<-EOF && + P2:$P2 + P4:$P4 + P6:$P6 + P8:$P8 + EOF + git pack-redundant --all >out && + format_packfiles <out >actual && + test_cmp expect actual + ) +' + +test_expect_success 'master: clean loose objects' ' + ( + cd "$master_repo" && + git prune-packed && + find objects -type f | sed -e "/objects\/pack\//d" >out && + test_must_be_empty out + ) +' + +test_expect_success 'master: remove redundant packs and pass fsck' ' + ( + cd "$master_repo" && + git pack-redundant --all | xargs rm && + git fsck && + git pack-redundant --all >out && + test_must_be_empty out + ) +' + +# The following test cases will execute inside `shared.git`, instead of +# inside `master.git`. +test_expect_success 'setup shared.git' ' + git clone --mirror "$master_repo" "$shared_repo" && + ( + cd "$shared_repo" && + printf "../../$master_repo/objects\n" >objects/info/alternates + ) +' + +test_expect_success 'shared: all packs are redundant, but no output without --alt-odb' ' + ( + cd "$shared_repo" && + git pack-redundant --all >out && + test_must_be_empty out + ) +' + +############################################################################# +# Chart of packs and objects for this test case +# +# ================ master.git =============== +# | T A B C D E F G H I J K L M N O P Q R <----------+ +# ----+-------------------------------------- | +# P1 | x x x x x x x x | +# P3 | x x x x x x | +# P5 | x x x x | +# P7 | x x | +# ----+-------------------------------------- | +# ALL | x x x x x x x x x x x x x x x x x x x | +# | +# | +# ================ shared.git =============== | +# | T A B C D E F G H I J K L M N O P Q R <objects/info/alternates> +# ----+-------------------------------------- +# P1* | s s s s s s s s +# P3* | s s s s s s +# P5* | s s s s +# P7* | s s +# ----+-------------------------------------- +# ALL | x x x x x x x x x x x x x x x x x x x +# +############################################################################# +test_expect_success 'shared: show redundant packs in stderr for verbose mode' ' + ( + cd "$shared_repo" && + cat >expect <<-EOF && + P1:$P1 + P3:$P3 + P5:$P5 + P7:$P7 + EOF + git pack-redundant --all --verbose >out 2>out.err && + test_must_be_empty out && + grep "pack$" out.err | format_packfiles >actual && + test_cmp expect actual + ) +' + +test_expect_success 'shared: remove redundant packs, no packs left' ' + ( + cd "$shared_repo" && + cat >expect <<-EOF && + fatal: Zero packs found! + EOF + git pack-redundant --all --alt-odb | xargs rm && + git fsck && + test_must_fail git pack-redundant --all --alt-odb >actual 2>&1 && + test_cmp expect actual + ) +' + +test_expect_success 'shared: create new objects and packs' ' + create_commits_in "$shared_repo" X Y Z && + create_pack_in "$shared_repo" Px1 <<-EOF && + $X + $Y + $Z + $A + $B + $C + EOF + create_pack_in "$shared_repo" Px2 <<-EOF + $X + $Y + $Z + $D + $E + $F + EOF +' + +test_expect_success 'shared: no redundant without --alt-odb' ' + ( + cd "$shared_repo" && + git pack-redundant --all >out && + test_must_be_empty out + ) +' + +############################################################################# +# Chart of packs and objects for this test case +# +# ================ master.git =============== +# | T A B C D E F G H I J K L M N O P Q R <----------------+ +# ----+-------------------------------------- | +# P1 | x x x x x x x x | +# P3 | x x x x x x | +# P5 | x x x x | +# P7 | x x | +# ----+-------------------------------------- | +# ALL | x x x x x x x x x x x x x x x x x x x | +# | +# | +# ================ shared.git ======================= | +# | T A B C D E F G H I J K L M N O P Q R X Y Z <objects/info/alternates> +# ----+---------------------------------------------- +# Px1 | s s s x x x +# Px2*| s s s ! ! ! +# ----+---------------------------------------------- +# ALL | s s s s s s s s s s s s s s s s s s s x x x +# +############################################################################# +test_expect_success 'shared: one pack is redundant with --alt-odb' ' + ( + cd "$shared_repo" && + git pack-redundant --all --alt-odb >out && + format_packfiles <out >actual && + test_line_count = 1 actual + ) +' + +############################################################################# +# Chart of packs and objects for this test case +# +# ================ master.git =============== +# | T A B C D E F G H I J K L M N O P Q R <----------------+ +# ----+-------------------------------------- | +# P1 | x x x x x x x x | +# P3 | x x x x x x | +# P5 | x x x x | +# P7 | x x | +# ----+-------------------------------------- | +# ALL | x x x x x x x x x x x x x x x x x x x | +# | +# | +# ================ shared.git ======================= | +# | T A B C D E F G H I J K L M N O P Q R X Y Z <objects/info/alternates> +# ----+---------------------------------------------- +# Px1*| s s s i i i +# Px2*| s s s i i i +# ----+---------------------------------------------- +# ALL | s s s s s s s s s s s s s s s s s s s i i i +# (ignored objects, marked with i) +# +############################################################################# +test_expect_success 'shared: ignore unique objects and all two packs are redundant' ' + ( + cd "$shared_repo" && + cat >expect <<-EOF && + Px1:$Px1 + Px2:$Px2 + EOF + git pack-redundant --all --alt-odb >out <<-EOF && + $X + $Y + $Z + EOF + format_packfiles <out >actual && + test_cmp expect actual + ) +' + +test_done diff --git a/t/t5403-post-checkout-hook.sh b/t/t5403-post-checkout-hook.sh index fc898c9eac..a39b3b5c78 100755 --- a/t/t5403-post-checkout-hook.sh +++ b/t/t5403-post-checkout-hook.sh @@ -7,82 +7,70 @@ test_description='Test the post-checkout hook.' . ./test-lib.sh test_expect_success setup ' - echo Data for commit0. >a && - echo Data for commit0. >b && - git update-index --add a && - git update-index --add b && - tree0=$(git write-tree) && - commit0=$(echo setup | git commit-tree $tree0) && - git update-ref refs/heads/master $commit0 && - git clone ./. clone1 && - git clone ./. clone2 && - GIT_DIR=clone2/.git git branch new2 && - echo Data for commit1. >clone2/b && - GIT_DIR=clone2/.git git add clone2/b && - GIT_DIR=clone2/.git git commit -m new2 -' - -for clone in 1 2; do - cat >clone${clone}/.git/hooks/post-checkout <<'EOF' -#!/bin/sh -echo $@ > $GIT_DIR/post-checkout.args -EOF - chmod u+x clone${clone}/.git/hooks/post-checkout -done - -test_expect_success 'post-checkout runs as expected ' ' - GIT_DIR=clone1/.git git checkout master && - test -e clone1/.git/post-checkout.args + mkdir -p .git/hooks && + write_script .git/hooks/post-checkout <<-\EOF && + echo "$@" >.git/post-checkout.args + EOF + test_commit one && + test_commit two && + test_commit rebase-on-me && + git reset --hard HEAD^ && + test_commit three ' test_expect_success 'post-checkout receives the right arguments with HEAD unchanged ' ' - old=$(awk "{print \$1}" clone1/.git/post-checkout.args) && - new=$(awk "{print \$2}" clone1/.git/post-checkout.args) && - flag=$(awk "{print \$3}" clone1/.git/post-checkout.args) && + test_when_finished "rm -f .git/post-checkout.args" && + git checkout master && + read old new flag <.git/post-checkout.args && test $old = $new && test $flag = 1 ' -test_expect_success 'post-checkout runs as expected ' ' - GIT_DIR=clone1/.git git checkout master && - test -e clone1/.git/post-checkout.args -' - test_expect_success 'post-checkout args are correct with git checkout -b ' ' - GIT_DIR=clone1/.git git checkout -b new1 && - old=$(awk "{print \$1}" clone1/.git/post-checkout.args) && - new=$(awk "{print \$2}" clone1/.git/post-checkout.args) && - flag=$(awk "{print \$3}" clone1/.git/post-checkout.args) && + test_when_finished "rm -f .git/post-checkout.args" && + git checkout -b new1 && + read old new flag <.git/post-checkout.args && test $old = $new && test $flag = 1 ' test_expect_success 'post-checkout receives the right args with HEAD changed ' ' - GIT_DIR=clone2/.git git checkout new2 && - old=$(awk "{print \$1}" clone2/.git/post-checkout.args) && - new=$(awk "{print \$2}" clone2/.git/post-checkout.args) && - flag=$(awk "{print \$3}" clone2/.git/post-checkout.args) && + test_when_finished "rm -f .git/post-checkout.args" && + git checkout two && + read old new flag <.git/post-checkout.args && test $old != $new && test $flag = 1 ' test_expect_success 'post-checkout receives the right args when not switching branches ' ' - GIT_DIR=clone2/.git git checkout master b && - old=$(awk "{print \$1}" clone2/.git/post-checkout.args) && - new=$(awk "{print \$2}" clone2/.git/post-checkout.args) && - flag=$(awk "{print \$3}" clone2/.git/post-checkout.args) && + test_when_finished "rm -f .git/post-checkout.args" && + git checkout master -- three.t && + read old new flag <.git/post-checkout.args && test $old = $new && test $flag = 0 ' -if test "$(git config --bool core.filemode)" = true; then -mkdir -p templates/hooks -cat >templates/hooks/post-checkout <<'EOF' -#!/bin/sh -echo $@ > $GIT_DIR/post-checkout.args -EOF -chmod +x templates/hooks/post-checkout +test_expect_success 'post-checkout is triggered on rebase' ' + test_when_finished "rm -f .git/post-checkout.args" && + git checkout -b rebase-test master && + 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_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_expect_success 'post-checkout hook is triggered by clone' ' + mkdir -p templates/hooks && + write_script templates/hooks/post-checkout <<-\EOF && + echo "$@" >"$GIT_DIR/post-checkout.args" + EOF git clone --template=templates . clone3 && test -f clone3/.git/post-checkout.args ' -fi test_done diff --git a/t/t5407-post-rewrite-hook.sh b/t/t5407-post-rewrite-hook.sh index 9b2a274c71..a4a5903cba 100755 --- a/t/t5407-post-rewrite-hook.sh +++ b/t/t5407-post-rewrite-hook.sh @@ -78,6 +78,7 @@ test_expect_success 'git rebase --skip' ' git rebase --continue && echo rebase >expected.args && cat >expected.data <<-EOF && + $(git rev-parse C) $(git rev-parse HEAD^) $(git rev-parse D) $(git rev-parse HEAD) EOF verify_hook_input @@ -91,6 +92,7 @@ test_expect_success 'git rebase --skip the last one' ' echo rebase >expected.args && cat >expected.data <<-EOF && $(git rev-parse E) $(git rev-parse HEAD) + $(git rev-parse F) $(git rev-parse HEAD) EOF verify_hook_input ' @@ -120,6 +122,38 @@ test_expect_success 'git rebase -m --skip' ' git rebase --continue && echo rebase >expected.args && cat >expected.data <<-EOF && + $(git rev-parse C) $(git rev-parse HEAD^) + $(git rev-parse D) $(git rev-parse HEAD) + EOF + verify_hook_input +' + +test_expect_success 'git rebase with implicit use of interactive backend' ' + git reset --hard D && + clear_hook_input && + test_must_fail git rebase --keep --onto A B && + echo C > foo && + git add foo && + git rebase --continue && + echo rebase >expected.args && + cat >expected.data <<-EOF && + $(git rev-parse C) $(git rev-parse HEAD^) + $(git rev-parse D) $(git rev-parse HEAD) + EOF + verify_hook_input +' + +test_expect_success 'git rebase --skip with implicit use of interactive backend' ' + git reset --hard D && + clear_hook_input && + test_must_fail git rebase --keep --onto A B && + test_must_fail git rebase --skip && + echo D > foo && + git add foo && + git rebase --continue && + echo rebase >expected.args && + cat >expected.data <<-EOF && + $(git rev-parse C) $(git rev-parse HEAD^) $(git rev-parse D) $(git rev-parse HEAD) EOF verify_hook_input diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index 086f2c40f6..49c540b1e1 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -439,15 +439,23 @@ test_expect_success 'setup tests for the --stdin parameter' ' ) >input.dup ' -test_expect_success 'fetch refs from cmdline' ' - ( - cd client && - git fetch-pack --no-progress .. $(cat ../input) - ) >output && - cut -d " " -f 2 <output | sort >actual && - test_cmp expect actual +test_expect_success 'setup fetch refs from cmdline v[12]' ' + cp -r client client1 && + cp -r client client2 ' +for version in '' 1 2 +do + test_expect_success "protocol.version=$version fetch refs from cmdline" " + ( + cd client$version && + GIT_TEST_PROTOCOL_VERSION=$version git fetch-pack --no-progress .. \$(cat ../input) + ) >output && + cut -d ' ' -f 2 <output | sort >actual && + test_cmp expect actual + " +done + test_expect_success 'fetch refs from stdin' ' ( cd client && diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh index cd9e60632d..ced15ae122 100755 --- a/t/t5512-ls-remote.sh +++ b/t/t5512-ls-remote.sh @@ -204,6 +204,12 @@ test_expect_success 'overrides work between mixed transfer/upload-pack hideRefs' grep refs/tags/magic actual ' +test_expect_success 'protocol v2 supports hiderefs' ' + test_config uploadpack.hiderefs refs/tags && + git -c protocol.version=2 ls-remote . >actual && + ! grep refs/tags actual +' + test_expect_success 'ls-remote --symref' ' git fetch origin && cat >expect <<-EOF && diff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh index a0317556c6..63205dfdf9 100755 --- a/t/t5526-fetch-submodules.sh +++ b/t/t5526-fetch-submodules.sh @@ -602,4 +602,121 @@ test_expect_success "fetch new commits when submodule got renamed" ' test_cmp expect actual ' +test_expect_success "fetch new submodule commits on-demand outside standard refspec" ' + # add a second submodule and ensure it is around in downstream first + git clone submodule sub1 && + git submodule add ./sub1 && + git commit -m "adding a second submodule" && + git -C downstream pull && + git -C downstream submodule update --init --recursive && + + git checkout --detach && + + C=$(git -C submodule commit-tree -m "new change outside refs/heads" HEAD^{tree}) && + git -C submodule update-ref refs/changes/1 $C && + git update-index --cacheinfo 160000 $C submodule && + test_tick && + + D=$(git -C sub1 commit-tree -m "new change outside refs/heads" HEAD^{tree}) && + git -C sub1 update-ref refs/changes/2 $D && + git update-index --cacheinfo 160000 $D sub1 && + + git commit -m "updated submodules outside of refs/heads" && + E=$(git rev-parse HEAD) && + git update-ref refs/changes/3 $E && + ( + cd downstream && + git fetch --recurse-submodules origin refs/changes/3:refs/heads/my_branch && + git -C submodule cat-file -t $C && + git -C sub1 cat-file -t $D && + git checkout --recurse-submodules FETCH_HEAD + ) +' + +test_expect_success 'fetch new submodule commit on-demand in FETCH_HEAD' ' + # depends on the previous test for setup + + C=$(git -C submodule commit-tree -m "another change outside refs/heads" HEAD^{tree}) && + git -C submodule update-ref refs/changes/4 $C && + git update-index --cacheinfo 160000 $C submodule && + test_tick && + + D=$(git -C sub1 commit-tree -m "another change outside refs/heads" HEAD^{tree}) && + git -C sub1 update-ref refs/changes/5 $D && + git update-index --cacheinfo 160000 $D sub1 && + + git commit -m "updated submodules outside of refs/heads" && + E=$(git rev-parse HEAD) && + git update-ref refs/changes/6 $E && + ( + cd downstream && + git fetch --recurse-submodules origin refs/changes/6 && + git -C submodule cat-file -t $C && + git -C sub1 cat-file -t $D && + git checkout --recurse-submodules FETCH_HEAD + ) +' + +test_expect_success 'fetch new submodule commits on-demand without .gitmodules entry' ' + # depends on the previous test for setup + + git config -f .gitmodules --remove-section submodule.sub1 && + git add .gitmodules && + git commit -m "delete gitmodules file" && + git checkout -B master && + git -C downstream fetch && + git -C downstream checkout origin/master && + + C=$(git -C submodule commit-tree -m "yet another change outside refs/heads" HEAD^{tree}) && + git -C submodule update-ref refs/changes/7 $C && + git update-index --cacheinfo 160000 $C submodule && + test_tick && + + D=$(git -C sub1 commit-tree -m "yet another change outside refs/heads" HEAD^{tree}) && + git -C sub1 update-ref refs/changes/8 $D && + git update-index --cacheinfo 160000 $D sub1 && + + git commit -m "updated submodules outside of refs/heads" && + E=$(git rev-parse HEAD) && + git update-ref refs/changes/9 $E && + ( + cd downstream && + git fetch --recurse-submodules origin refs/changes/9 && + git -C submodule cat-file -t $C && + git -C sub1 cat-file -t $D && + git checkout --recurse-submodules FETCH_HEAD + ) +' + +test_expect_success 'fetch new submodule commit intermittently referenced by superproject' ' + # depends on the previous test for setup + + D=$(git -C sub1 commit-tree -m "change 10 outside refs/heads" HEAD^{tree}) && + E=$(git -C sub1 commit-tree -m "change 11 outside refs/heads" HEAD^{tree}) && + F=$(git -C sub1 commit-tree -m "change 12 outside refs/heads" HEAD^{tree}) && + + git -C sub1 update-ref refs/changes/10 $D && + git update-index --cacheinfo 160000 $D sub1 && + git commit -m "updated submodules outside of refs/heads" && + + git -C sub1 update-ref refs/changes/11 $E && + git update-index --cacheinfo 160000 $E sub1 && + git commit -m "updated submodules outside of refs/heads" && + + git -C sub1 update-ref refs/changes/12 $F && + git update-index --cacheinfo 160000 $F sub1 && + git commit -m "updated submodules outside of refs/heads" && + + G=$(git rev-parse HEAD) && + git update-ref refs/changes/13 $G && + ( + cd downstream && + git fetch --recurse-submodules origin refs/changes/13 && + + git -C sub1 cat-file -t $D && + git -C sub1 cat-file -t $E && + git -C sub1 cat-file -t $F + ) +' + test_done diff --git a/t/t5537-fetch-shallow.sh b/t/t5537-fetch-shallow.sh index 6faf17e17a..6caf628efa 100755 --- a/t/t5537-fetch-shallow.sh +++ b/t/t5537-fetch-shallow.sh @@ -243,7 +243,8 @@ test_expect_success 'shallow fetches check connectivity before writing shallow f "$(git -C "$REPO" rev-parse HEAD)" \ "$(git -C "$REPO" rev-parse HEAD^)" \ >"$HTTPD_ROOT_PATH/one-time-sed" && - test_must_fail git -C client fetch --depth=1 "$HTTPD_URL/one_time_sed/repo" \ + test_must_fail env GIT_TEST_SIDEBAND_ALL=0 git -C client \ + fetch --depth=1 "$HTTPD_URL/one_time_sed/repo" \ master:a_branch && # Ensure that the one-time-sed script was used. diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh index 8630b0cc39..ba83e567e5 100755 --- a/t/t5551-http-fetch-smart.sh +++ b/t/t5551-http-fetch-smart.sh @@ -429,5 +429,10 @@ test_expect_success 'GIT_TRACE_CURL_NO_DATA prevents data from being traced' ' ! grep "=> Send data" err ' +test_expect_success 'server-side error detected' ' + test_must_fail git clone $HTTPD_URL/error_smart/repo.git 2>actual && + grep "server-side error" actual +' + stop_httpd test_done diff --git a/t/t5562-http-backend-content-length.sh b/t/t5562-http-backend-content-length.sh index 90d890d02f..f0f425b2cf 100755 --- a/t/t5562-http-backend-content-length.sh +++ b/t/t5562-http-backend-content-length.sh @@ -8,12 +8,12 @@ test_lazy_prereq GZIP 'gzip --version' verify_http_result() { # some fatal errors still produce status 200 # so check if there is the error message - if grep 'fatal:' act.err + if grep 'fatal:' act.err.$test_count then return 1 fi - if ! grep "Status" act.out >act + if ! grep "Status" act.out.$test_count >act then printf "Status: 200 OK\r\n" >act fi @@ -33,7 +33,7 @@ test_http_env() { REQUEST_METHOD=POST \ "$PERL_PATH" \ "$TEST_DIRECTORY"/t5562/invoke-with-content-length.pl \ - "$request_body" git http-backend >act.out 2>act.err + "$request_body" git http-backend >act.out.$test_count 2>act.err.$test_count } ssize_b100dots() { @@ -150,7 +150,7 @@ test_expect_success 'CONTENT_LENGTH overflow ssite_t' ' GIT_HTTP_EXPORT_ALL=TRUE \ REQUEST_METHOD=POST \ CONTENT_LENGTH="$NOT_FIT_IN_SSIZE" \ - git http-backend </dev/zero >/dev/null 2>err && + git http-backend </dev/null >/dev/null 2>err && grep "fatal:.*CONTENT_LENGTH" err ' @@ -161,7 +161,7 @@ test_expect_success 'empty CONTENT_LENGTH' ' GIT_HTTP_EXPORT_ALL=TRUE \ REQUEST_METHOD=GET \ CONTENT_LENGTH="" \ - git http-backend <empty_body >act.out 2>act.err && + git http-backend <empty_body >act.out.$test_count 2>act.err.$test_count && verify_http_result "200 OK" ' diff --git a/t/t5580-clone-push-unc.sh b/t/t5580-clone-push-unc.sh index ba548df4a9..217adf3a63 100755 --- a/t/t5580-clone-push-unc.sh +++ b/t/t5580-clone-push-unc.sh @@ -40,6 +40,11 @@ test_expect_success clone ' git clone "file://$UNCPATH" clone ' +test_expect_success 'clone with backslashed path' ' + BACKSLASHED="$(echo "$UNCPATH" | tr / \\\\)" && + git clone "$BACKSLASHED" backslashed +' + test_expect_success push ' ( cd clone && diff --git a/t/t5581-http-curl-verbose.sh b/t/t5581-http-curl-verbose.sh new file mode 100755 index 0000000000..cd9283eeec --- /dev/null +++ b/t/t5581-http-curl-verbose.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +test_description='test GIT_CURL_VERBOSE' +. ./test-lib.sh +. "$TEST_DIRECTORY"/lib-httpd.sh +start_httpd + +test_expect_success 'setup repository' ' + mkdir "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && + git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" --bare init && + git config push.default matching && + echo content >file && + git add file && + git commit -m one && + git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && + git push public master:master +' + +test_expect_success 'failure in git-upload-pack is shown' ' + test_might_fail env GIT_CURL_VERBOSE=1 \ + git clone "$HTTPD_URL/error_git_upload_pack/smart/repo.git" \ + 2>curl_log && + grep "< HTTP/1.1 500 Intentional Breakage" curl_log +' + +stop_httpd + +test_done diff --git a/t/t5701-git-serve.sh b/t/t5701-git-serve.sh index ae79c6bbc0..fe45bf828d 100755 --- a/t/t5701-git-serve.sh +++ b/t/t5701-git-serve.sh @@ -14,7 +14,7 @@ test_expect_success 'test capability advertisement' ' 0000 EOF - git serve --advertise-capabilities >out && + GIT_TEST_SIDEBAND_ALL=0 git serve --advertise-capabilities >out && test-tool pkt-line unpack <out >actual && test_cmp expect actual ' diff --git a/t/t5702-protocol-v2.sh b/t/t5702-protocol-v2.sh index 0f2b09ebb8..db4ae09f2f 100755 --- a/t/t5702-protocol-v2.sh +++ b/t/t5702-protocol-v2.sh @@ -471,6 +471,53 @@ test_expect_success 'upload-pack respects client shallows' ' grep "fetch< version 2" trace ' +test_expect_success 'ensure that multiple fetches in same process from a shallow repo works' ' + rm -rf server client trace && + + test_create_repo server && + test_commit -C server one && + test_commit -C server two && + test_commit -C server three && + git clone --shallow-exclude two "file://$(pwd)/server" client && + + git -C server tag -a -m "an annotated tag" twotag two && + + # Triggers tag following (thus, 2 fetches in one process) + GIT_TRACE_PACKET="$(pwd)/trace" git -C client -c protocol.version=2 \ + fetch --shallow-exclude one origin && + # Ensure that protocol v2 is used + grep "fetch< version 2" trace +' + +test_expect_success 'deepen-relative' ' + rm -rf server client trace && + + test_create_repo server && + test_commit -C server one && + test_commit -C server two && + test_commit -C server three && + git clone --depth 1 "file://$(pwd)/server" client && + test_commit -C server four && + + # Sanity check that only "three" is downloaded + git -C client log --pretty=tformat:%s master >actual && + echo three >expected && + test_cmp expected actual && + + GIT_TRACE_PACKET="$(pwd)/trace" git -C client -c protocol.version=2 \ + fetch --deepen=1 origin && + # Ensure that protocol v2 is used + grep "fetch< version 2" trace && + + git -C client log --pretty=tformat:%s origin/master >actual && + cat >expected <<-\EOF && + four + three + two + EOF + test_cmp expected actual +' + # Test protocol v2 with 'http://' transport # . "$TEST_DIRECTORY"/lib-httpd.sh @@ -514,6 +561,27 @@ test_expect_success 'fetch with http:// using protocol v2' ' grep "git< version 2" log ' +test_expect_success 'fetch from namespaced repo respects namespaces' ' + test_when_finished "rm -f log" && + + git init "$HTTPD_DOCUMENT_ROOT_PATH/nsrepo" && + test_commit -C "$HTTPD_DOCUMENT_ROOT_PATH/nsrepo" one && + test_commit -C "$HTTPD_DOCUMENT_ROOT_PATH/nsrepo" two && + git -C "$HTTPD_DOCUMENT_ROOT_PATH/nsrepo" \ + update-ref refs/namespaces/ns/refs/heads/master one && + + GIT_TRACE_PACKET="$(pwd)/log" git -C http_child -c protocol.version=2 \ + fetch "$HTTPD_URL/smart_namespace/nsrepo" \ + refs/heads/master:refs/heads/theirs && + + # Server responded using protocol v2 + grep "fetch< version 2" log && + + git -C "$HTTPD_DOCUMENT_ROOT_PATH/nsrepo" rev-parse one >expect && + git -C http_child rev-parse theirs >actual && + test_cmp expect actual +' + test_expect_success 'push with http:// and a config of v2 does not request v2' ' test_when_finished "rm -f log" && # Till v2 for push is designed, make sure that if a client has @@ -583,8 +651,8 @@ test_expect_success 'when server does not send "ready", expect FLUSH' ' test_must_fail env GIT_TRACE_PACKET="$(pwd)/log" git -C http_child \ -c protocol.version=2 \ fetch "$HTTPD_URL/one_time_sed/http_parent" 2> err && - grep "fetch< acknowledgments" log && - ! grep "fetch< ready" log && + grep "fetch< .*acknowledgments" log && + ! grep "fetch< .*ready" log && test_i18ngrep "expected no other sections to be sent after no .ready." err ' diff --git a/t/t5703-upload-pack-ref-in-want.sh b/t/t5703-upload-pack-ref-in-want.sh index 7053899cb5..f87b2f6df3 100755 --- a/t/t5703-upload-pack-ref-in-want.sh +++ b/t/t5703-upload-pack-ref-in-want.sh @@ -208,7 +208,7 @@ test_expect_success 'server is initially ahead - no ref in want' ' cp -r "$LOCAL_PRISTINE" local && inconsistency master 1234567890123456789012345678901234567890 && test_must_fail git -C local fetch 2>err && - test_i18ngrep "ERR upload-pack: not our ref" err + test_i18ngrep "fatal: remote error: upload-pack: not our ref" err ' test_expect_success 'server is initially ahead - ref in want' ' @@ -254,7 +254,7 @@ test_expect_success 'server loses a ref - ref in want' ' echo "s/master/raster/" >"$HTTPD_ROOT_PATH/one-time-sed" && test_must_fail git -C local fetch 2>err && - test_i18ngrep "ERR unknown ref refs/heads/raster" err + test_i18ngrep "fatal: remote error: unknown ref refs/heads/raster" err ' stop_httpd diff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh index ec42c2f779..da113d975b 100755 --- a/t/t6006-rev-list-format.sh +++ b/t/t6006-rev-list-format.sh @@ -185,6 +185,10 @@ test_expect_success 'basic colors' ' test_cmp expect actual ' +test_expect_success '%S is not a placeholder for rev-list yet' ' + git rev-list --format="%S" -1 master | grep "%S" +' + test_expect_success 'advanced colors' ' cat >expect <<-EOF && commit $head2 diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index f84ff941c3..55835ee4a4 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh @@ -802,7 +802,7 @@ test_expect_success 'bisect terms needs 0 or 1 argument' ' test_must_fail git bisect terms only-one && test_must_fail git bisect terms 1 2 && test_must_fail git bisect terms 2>actual && - echo "no terms defined" >expected && + echo "error: no terms defined" >expected && test_i18ncmp expected actual ' diff --git a/t/t6042-merge-rename-corner-cases.sh b/t/t6042-merge-rename-corner-cases.sh index 7cc34e7579..09dfa8bd92 100755 --- a/t/t6042-merge-rename-corner-cases.sh +++ b/t/t6042-merge-rename-corner-cases.sh @@ -1175,7 +1175,7 @@ test_expect_success 'setup nested conflicts from rename/rename(2to1)' ' # Handle the left side git checkout L && - git mv one three && + git rm one two && mv -f file_v2 three && mv -f file_v5 two && git add two three && @@ -1183,7 +1183,7 @@ test_expect_success 'setup nested conflicts from rename/rename(2to1)' ' # Handle the right side git checkout R && - git mv two three && + git rm one two && mv -f file_v3 one && mv -f file_v6 three && git add one three && diff --git a/t/t6112-rev-list-filters-objects.sh b/t/t6112-rev-list-filters-objects.sh index eb32505a6e..9c11427719 100755 --- a/t/t6112-rev-list-filters-objects.sh +++ b/t/t6112-rev-list-filters-objects.sh @@ -283,7 +283,7 @@ test_expect_success 'verify tree:0 includes trees in "filtered" output' ' # Make sure tree:0 does not iterate through any trees. -test_expect_success 'filter a GIANT tree through tree:0' ' +test_expect_success 'verify skipping tree iteration when not collecting omits' ' GIT_TRACE=1 git -C r3 rev-list \ --objects --filter=tree:0 HEAD 2>filter_trace && grep "Skipping contents of tree [.][.][.]" filter_trace >actual && @@ -294,6 +294,126 @@ test_expect_success 'filter a GIANT tree through tree:0' ' ! grep "Skipping contents of tree [^.]" filter_trace ' +# Test tree:# filters. + +expect_has () { + commit=$1 && + name=$2 && + + hash=$(git -C r3 rev-parse $commit:$name) && + grep "^$hash $name$" actual +} + +test_expect_success 'verify tree:1 includes root trees' ' + git -C r3 rev-list --objects --filter=tree:1 HEAD >actual && + + # We should get two root directories and two commits. + expect_has HEAD "" && + expect_has HEAD~1 "" && + test_line_count = 4 actual +' + +test_expect_success 'verify tree:2 includes root trees and immediate children' ' + git -C r3 rev-list --objects --filter=tree:2 HEAD >actual && + + expect_has HEAD "" && + expect_has HEAD~1 "" && + expect_has HEAD dir1 && + expect_has HEAD pattern && + expect_has HEAD sparse1 && + expect_has HEAD sparse2 && + + # There are also 2 commit objects + test_line_count = 8 actual +' + +test_expect_success 'verify tree:3 includes everything expected' ' + git -C r3 rev-list --objects --filter=tree:3 HEAD >actual && + + expect_has HEAD "" && + expect_has HEAD~1 "" && + expect_has HEAD dir1 && + expect_has HEAD dir1/sparse1 && + expect_has HEAD dir1/sparse2 && + expect_has HEAD pattern && + expect_has HEAD sparse1 && + expect_has HEAD sparse2 && + + # There are also 2 commit objects + test_line_count = 10 actual +' + +# Test provisional omit collection logic with a repo that has objects appearing +# at multiple depths - first deeper than the filter's threshold, then shallow. + +test_expect_success 'setup r4' ' + git init r4 && + + echo foo > r4/foo && + mkdir r4/subdir && + echo bar > r4/subdir/bar && + + mkdir r4/filt && + cp -r r4/foo r4/subdir r4/filt && + + git -C r4 add foo subdir filt && + git -C r4 commit -m "commit msg" +' + +expect_has_with_different_name () { + repo=$1 && + name=$2 && + + hash=$(git -C $repo rev-parse HEAD:$name) && + ! grep "^$hash $name$" actual && + grep "^$hash " actual && + ! grep "~$hash" actual +} + +test_expect_success 'test tree:# filter provisional omit for blob and tree' ' + git -C r4 rev-list --objects --filter-print-omitted --filter=tree:2 \ + HEAD >actual && + expect_has_with_different_name r4 filt/foo && + expect_has_with_different_name r4 filt/subdir +' + +test_expect_success 'verify skipping tree iteration when collecting omits' ' + GIT_TRACE=1 git -C r4 rev-list --filter-print-omitted \ + --objects --filter=tree:0 HEAD 2>filter_trace && + grep "^Skipping contents of tree " filter_trace >actual && + + echo "Skipping contents of tree subdir/..." >expect && + test_cmp expect actual +' + +# Test tree:<depth> where a tree is iterated to twice - once where a subentry is +# too deep to be included, and again where the blob inside it is shallow enough +# to be included. This makes sure we don't use LOFR_MARK_SEEN incorrectly (we +# can't use it because a tree can be iterated over again at a lower depth). + +test_expect_success 'tree:<depth> where we iterate over tree at two levels' ' + git init r5 && + + mkdir -p r5/a/subdir/b && + echo foo > r5/a/subdir/b/foo && + + mkdir -p r5/subdir/b && + echo foo > r5/subdir/b/foo && + + git -C r5 add a subdir && + git -C r5 commit -m "commit msg" && + + git -C r5 rev-list --objects --filter=tree:4 HEAD >actual && + expect_has_with_different_name r5 a/subdir/b/foo +' + +test_expect_success 'tree:<depth> which filters out blob but given as arg' ' + blob_hash=$(git -C r4 rev-parse HEAD:subdir/bar) && + + git -C r4 rev-list --objects --filter=tree:1 HEAD $blob_hash >actual && + grep ^$blob_hash actual +' + # Delete some loose objects and use rev-list, but WITHOUT any filtering. # This models previously omitted objects that we did not receive. @@ -324,4 +444,21 @@ test_expect_success 'rev-list W/ missing=allow-any' ' git -C r1 rev-list --quiet --missing=allow-any --objects HEAD ' +# Test expansion of filter specs. + +test_expect_success 'expand blob limit in protocol' ' + git -C r2 config --local uploadpack.allowfilter 1 && + GIT_TRACE_PACKET="$(pwd)/trace" git -c protocol.version=2 clone \ + --filter=blob:limit=1k "file://$(pwd)/r2" limit && + ! grep "blob:limit=1k" trace && + grep "blob:limit=1024" trace +' + +test_expect_success 'expand tree depth limit in protocol' ' + GIT_TRACE_PACKET="$(pwd)/tree_trace" git -c protocol.version=2 clone \ + --filter=tree:0k "file://$(pwd)/r2" tree && + ! grep "tree:0k" tree_trace && + grep "tree:0" tree_trace +' + test_done diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh index d639d94696..2b883d8174 100755 --- a/t/t6120-describe.sh +++ b/t/t6120-describe.sh @@ -143,16 +143,46 @@ test_expect_success 'rename tag Q back to A' ' test_expect_success 'pack tag refs' 'git pack-refs' check_describe A-* HEAD +test_expect_success 'describe works from outside repo using --git-dir' ' + git clone --bare "$TRASH_DIRECTORY" "$TRASH_DIRECTORY/bare" && + git --git-dir "$TRASH_DIRECTORY/bare" describe >out && + grep -E "^A-[1-9][0-9]?-g[0-9a-f]+$" out +' + check_describe "A-*[0-9a-f]" --dirty +test_expect_success 'describe --dirty with --work-tree' ' + ( + cd "$TEST_DIRECTORY" && + git --git-dir "$TRASH_DIRECTORY/.git" --work-tree "$TRASH_DIRECTORY" describe --dirty >"$TRASH_DIRECTORY/out" + ) && + grep -E "^A-[1-9][0-9]?-g[0-9a-f]+$" out +' + test_expect_success 'set-up dirty work tree' ' echo >>file ' check_describe "A-*[0-9a-f]-dirty" --dirty +test_expect_success 'describe --dirty with --work-tree (dirty)' ' + ( + cd "$TEST_DIRECTORY" && + git --git-dir "$TRASH_DIRECTORY/.git" --work-tree "$TRASH_DIRECTORY" describe --dirty >"$TRASH_DIRECTORY/out" + ) && + grep -E "^A-[1-9][0-9]?-g[0-9a-f]+-dirty$" out +' + check_describe "A-*[0-9a-f].mod" --dirty=.mod +test_expect_success 'describe --dirty=.mod with --work-tree (dirty)' ' + ( + cd "$TEST_DIRECTORY" && + git --git-dir "$TRASH_DIRECTORY/.git" --work-tree "$TRASH_DIRECTORY" describe --dirty=.mod >"$TRASH_DIRECTORY/out" + ) && + grep -E "^A-[1-9][0-9]?-g[0-9a-f]+.mod$" out +' + test_expect_success 'describe --dirty HEAD' ' test_must_fail git describe --dirty HEAD ' @@ -303,8 +333,17 @@ test_expect_success 'describe chokes on severely broken submodules' ' mv .git/modules/sub1/ .git/modules/sub_moved && test_must_fail git describe --dirty ' + test_expect_success 'describe ignoring a broken submodule' ' git describe --broken >out && + grep broken out +' + +test_expect_success 'describe with --work-tree ignoring a broken submodule' ' + ( + cd "$TEST_DIRECTORY" && + git --git-dir "$TRASH_DIRECTORY/.git" --work-tree "$TRASH_DIRECTORY" describe --broken >"$TRASH_DIRECTORY/out" + ) && test_when_finished "mv .git/modules/sub_moved .git/modules/sub1" && grep broken out ' diff --git a/t/t7505-prepare-commit-msg-hook.sh b/t/t7505-prepare-commit-msg-hook.sh index ebfcad9c4c..ba8bd1b514 100755 --- a/t/t7505-prepare-commit-msg-hook.sh +++ b/t/t7505-prepare-commit-msg-hook.sh @@ -215,7 +215,7 @@ test_expect_success 'with hook and editor (merge)' ' test_rebase () { expect=$1 && mode=$2 && - test_expect_$expect C_LOCALE_OUTPUT "with hook (rebase $mode)" ' + test_expect_$expect C_LOCALE_OUTPUT "with hook (rebase ${mode:--i})" ' test_when_finished "\ git rebase --abort git checkout -f master @@ -225,7 +225,7 @@ test_rebase () { GIT_EDITOR="\"$FAKE_EDITOR\"" && ( export GIT_SEQUENCE_EDITOR GIT_EDITOR && - test_must_fail git rebase $mode b && + test_must_fail git rebase -i $mode b && echo x >a && git add a && test_must_fail git rebase --continue && @@ -241,18 +241,18 @@ test_rebase () { git add b && git rebase --continue ) && - if test $mode = -p # reword amended after pick + if test "$mode" = -p # reword amended after pick then n=18 else n=17 fi && git log --pretty=%s -g -n$n HEAD@{1} >actual && - test_cmp "$TEST_DIRECTORY/t7505/expected-rebase$mode" actual + test_cmp "$TEST_DIRECTORY/t7505/expected-rebase${mode:--i}" actual ' } -test_rebase success -i +test_rebase success test_have_prereq !REBASE_P || test_rebase success -p test_expect_success 'with hook (cherry-pick)' ' diff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh index 86d3f93fa2..682b23a068 100755 --- a/t/t7510-signed-commit.sh +++ b/t/t7510-signed-commit.sh @@ -49,15 +49,28 @@ test_expect_success GPG 'create signed commits' ' git tag eighth-signed-alt && # commit.gpgsign is still on but this must not be signed - git tag ninth-unsigned $(echo 9 | git commit-tree HEAD^{tree}) && + echo 9 | git commit-tree HEAD^{tree} >oid && + test_line_count = 1 oid && + git tag ninth-unsigned $(cat oid) && # explicit -S of course must sign. - git tag tenth-signed $(echo 9 | git commit-tree -S HEAD^{tree}) + echo 10 | git commit-tree -S HEAD^{tree} >oid && + test_line_count = 1 oid && + git tag tenth-signed $(cat oid) && + + # --gpg-sign[=<key-id>] must sign. + echo 11 | git commit-tree --gpg-sign HEAD^{tree} >oid && + test_line_count = 1 oid && + git tag eleventh-signed $(cat oid) && + echo 12 | git commit-tree --gpg-sign=B7227189 HEAD^{tree} >oid && + test_line_count = 1 oid && + git tag twelfth-signed-alt $(cat oid) ' test_expect_success GPG 'verify and show signatures' ' ( for commit in initial second merge fourth-signed \ - fifth-signed sixth-signed seventh-signed tenth-signed + fifth-signed sixth-signed seventh-signed tenth-signed \ + eleventh-signed do git verify-commit $commit && git show --pretty=short --show-signature $commit >actual && @@ -78,7 +91,7 @@ test_expect_success GPG 'verify and show signatures' ' done ) && ( - for commit in eighth-signed-alt + for commit in eighth-signed-alt twelfth-signed-alt do git show --pretty=short --show-signature $commit >actual && grep "Good signature from" actual && diff --git a/t/t7517-per-repo-email.sh b/t/t7517-per-repo-email.sh index 231b8cc19d..b2401cec3e 100755 --- a/t/t7517-per-repo-email.sh +++ b/t/t7517-per-repo-email.sh @@ -85,4 +85,78 @@ test_expect_success REBASE_P \ test_must_fail git rebase -p master ' +test_expect_success 'author.name overrides user.name' ' + test_config user.name user && + test_config user.email user@example.com && + test_config author.name author && + test_commit author-name-override-user && + echo author user@example.com > expected-author && + echo user user@example.com > expected-committer && + git log --format="%an %ae" -1 > actual-author && + git log --format="%cn %ce" -1 > actual-committer && + test_cmp expected-author actual-author && + test_cmp expected-committer actual-committer +' + +test_expect_success 'author.email overrides user.email' ' + test_config user.name user && + test_config user.email user@example.com && + test_config author.email author@example.com && + test_commit author-email-override-user && + echo user author@example.com > expected-author && + echo user user@example.com > expected-committer && + git log --format="%an %ae" -1 > actual-author && + git log --format="%cn %ce" -1 > actual-committer && + test_cmp expected-author actual-author && + test_cmp expected-committer actual-committer +' + +test_expect_success 'committer.name overrides user.name' ' + test_config user.name user && + test_config user.email user@example.com && + test_config committer.name committer && + test_commit committer-name-override-user && + echo user user@example.com > expected-author && + echo committer user@example.com > expected-committer && + git log --format="%an %ae" -1 > actual-author && + git log --format="%cn %ce" -1 > actual-committer && + test_cmp expected-author actual-author && + test_cmp expected-committer actual-committer +' + +test_expect_success 'committer.email overrides user.email' ' + test_config user.name user && + test_config user.email user@example.com && + test_config committer.email committer@example.com && + test_commit committer-email-override-user && + echo user user@example.com > expected-author && + echo user committer@example.com > expected-committer && + git log --format="%an %ae" -1 > actual-author && + git log --format="%cn %ce" -1 > actual-committer && + test_cmp expected-author actual-author && + test_cmp expected-committer actual-committer +' + +test_expect_success 'author and committer environment variables override config settings' ' + test_config user.name user && + test_config user.email user@example.com && + test_config author.name author && + test_config author.email author@example.com && + test_config committer.name committer && + test_config committer.email committer@example.com && + GIT_AUTHOR_NAME=env_author && export GIT_AUTHOR_NAME && + GIT_AUTHOR_EMAIL=env_author@example.com && export GIT_AUTHOR_EMAIL && + GIT_COMMITTER_NAME=env_commit && export GIT_COMMITTER_NAME && + GIT_COMMITTER_EMAIL=env_commit@example.com && export GIT_COMMITTER_EMAIL && + test_commit env-override-conf && + echo env_author env_author@example.com > expected-author && + echo env_commit env_commit@example.com > expected-committer && + git log --format="%an %ae" -1 > actual-author && + git log --format="%cn %ce" -1 > actual-committer && + sane_unset GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL && + sane_unset GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL && + test_cmp expected-author actual-author && + test_cmp expected-committer actual-committer +' + test_done diff --git a/t/t9303-fast-import-compression.sh b/t/t9303-fast-import-compression.sh index 856219f46a..5045f02a53 100755 --- a/t/t9303-fast-import-compression.sh +++ b/t/t9303-fast-import-compression.sh @@ -6,7 +6,7 @@ test_description='compression setting of fast-import utility' # This should be moved to test-lib.sh together with the # copy in t0021 after both topics have graduated to 'master'. file_size () { - perl -e 'print -s $ARGV[0]' "$1" + test-tool path-utils file-size "$1" } import_large () { diff --git a/t/t9807-git-p4-submit.sh b/t/t9807-git-p4-submit.sh index 2325599ee6..850d979119 100755 --- a/t/t9807-git-p4-submit.sh +++ b/t/t9807-git-p4-submit.sh @@ -500,6 +500,10 @@ test_expect_success 'submit --shelve' ' ) ' +last_shelve () { + p4 -G changes -s shelved -m 1 //depot/... | marshal_dump change +} + make_shelved_cl() { test_commit "$1" >/dev/null && git p4 submit --origin HEAD^ --shelve >/dev/null && @@ -533,12 +537,59 @@ test_expect_success 'submit --update-shelve' ' ) && ( cd "$cli" && - change=$(p4 -G changes -s shelved -m 1 //depot/... | \ - marshal_dump change) && + change=$(last_shelve) && p4 unshelve -c $change -s $change && grep -q updated-line shelf.t && p4 describe -S $change | grep added-file.t && - test_path_is_missing shelved-change-1.t + test_path_is_missing shelved-change-1.t && + p4 revert ... + ) +' + +test_expect_success 'update a shelve involving moved and copied files' ' + test_when_finished cleanup_git && + ( + cd "$cli" && + : >file_to_move && + p4 add file_to_move && + p4 submit -d "change1" && + p4 edit file_to_move && + echo change >>file_to_move && + p4 submit -d "change2" && + p4 opened + ) && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + git config git-p4.detectCopies true && + git config git-p4.detectRenames true && + git config git-p4.skipSubmitEdit true && + mkdir moved && + cp file_to_move copy_of_file && + git add copy_of_file && + git mv file_to_move moved/ && + git commit -m "rename a file" && + git p4 submit -M --shelve --origin HEAD^ && + : >new_file && + git add new_file && + git commit --amend && + git show --stat HEAD && + change=$(last_shelve) && + git p4 submit -M --update-shelve $change --commit HEAD + ) && + ( + cd "$cli" && + change=$(last_shelve) && + echo change=$change && + p4 unshelve -s $change && + p4 submit -d "Testing update-shelve" && + test_path_is_file copy_of_file && + test_path_is_file moved/file_to_move && + test_path_is_missing file_to_move && + test_path_is_file new_file && + echo "unshelved and submitted change $change" && + p4 changes moved/file_to_move | grep "Testing update-shelve" && + p4 changes copy_of_file | grep "Testing update-shelve" ) ' diff --git a/t/t9833-errors.sh b/t/t9833-errors.sh index 277d347012..47b312e1c9 100755 --- a/t/t9833-errors.sh +++ b/t/t9833-errors.sh @@ -45,33 +45,6 @@ test_expect_success 'ticket logged out' ' ) ' -test_expect_success 'create group with short ticket expiry' ' - P4TICKETS="$cli/tickets" && - echo "newpassword" | p4 login && - p4_add_user short_expiry_user && - p4 -u short_expiry_user passwd -P password && - p4 group -i <<-EOF && - Group: testgroup - Timeout: 3 - Users: short_expiry_user - EOF - - p4 users | grep short_expiry_user -' - -test_expect_success 'git operation with expired ticket' ' - P4TICKETS="$cli/tickets" && - P4USER=short_expiry_user && - echo "password" | p4 login && - ( - cd "$git" && - git p4 sync && - sleep 5 && - test_must_fail git p4 sync 2>errmsg && - grep "failure accessing depot" errmsg - ) -' - test_expect_success 'kill p4d' ' kill_p4d ' diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh index 3a2c6326d8..f5e21bf970 100755 --- a/t/t9902-completion.sh +++ b/t/t9902-completion.sh @@ -1437,6 +1437,7 @@ test_expect_success 'double dash "git checkout"' ' --guess Z --no-guess Z --no-... Z + --overlay Z EOF ' diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh index 81a5179e28..5cadedb2a9 100755 --- a/t/t9903-bash-prompt.sh +++ b/t/t9903-bash-prompt.sh @@ -180,7 +180,7 @@ test_expect_success 'prompt - interactive rebase' ' ' test_expect_success 'prompt - rebase merge' ' - printf " (b2|REBASE-m 1/3)" >expected && + printf " (b2|REBASE-i 1/3)" >expected && git checkout b2 && test_when_finished "git checkout master" && test_must_fail git rebase --merge b1 b2 && diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh index 92cf8f812c..80402a428f 100644 --- a/t/test-lib-functions.sh +++ b/t/test-lib-functions.sh @@ -116,6 +116,13 @@ remove_cr () { tr '\015' Q | sed -e 's/Q$//' } +# Generate an output of $1 bytes of all zeroes (NULs, not ASCII zeroes). +# If $1 is 'infinity', output forever or until the receiving pipe stops reading, +# whichever comes first. +generate_zero_bytes () { + test-tool genzeros "$@" +} + # In some bourne shell implementations, the "unset" builtin returns # nonzero status when a variable to be unset was not set in the first # place. @@ -1289,7 +1296,7 @@ test_set_port () { port=$(($port + 10000)) fi ;; - *[^0-9]*|0*) + *[!0-9]*|0*) error >&7 "invalid port number: $port" ;; *) diff --git a/t/test-lib.sh b/t/test-lib.sh index a1abb1177a..4e79e140c9 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -111,6 +111,8 @@ do test -z "$HARNESS_ACTIVE" && quiet=t ;; --with-dashes) with_dashes=t ;; + --no-bin-wrappers) + no_bin_wrappers=t ;; --no-color) color= ;; --va|--val|--valg|--valgr|--valgri|--valgrin|--valgrind) @@ -139,12 +141,15 @@ do verbose_log=t tee=t ;; + --write-junit-xml) + write_junit_xml=t + ;; --stress) stress=t ;; --stress=*) stress=${opt#--*=} case "$stress" in - *[^0-9]*|0*|"") + *[!0-9]*|0*|"") echo "error: --stress=<N> requires the number of jobs to run" >&2 exit 1 ;; @@ -152,6 +157,17 @@ do ;; esac ;; + --stress-limit=*) + stress_limit=${opt#--*=} + case "$stress_limit" in + *[!0-9]*|0*|"") + echo "error: --stress-limit=<N> requires the number of repetitions" >&2 + exit 1 + ;; + *) # Good. + ;; + esac + ;; *) echo "error: unknown test option '$opt'" >&2; exit 1 ;; esac @@ -237,8 +253,10 @@ then exit 1 ' TERM INT - cnt=0 - while ! test -e "$stressfail" + cnt=1 + while ! test -e "$stressfail" && + { test -z "$stress_limit" || + test $cnt -le $stress_limit ; } do $TEST_SHELL_PATH "$0" "$@" >"$TEST_RESULTS_BASE.stress-$job_nr.out" 2>&1 & test_pid=$! @@ -261,6 +279,7 @@ then if test -f "$stressfail" then + stress_exit=1 echo "Log(s) of failed test run(s):" for failed_job_nr in $(sort -n "$stressfail") do @@ -622,11 +641,35 @@ trap 'exit $?' INT TERM HUP # the test_expect_* functions instead. test_ok_ () { + if test -n "$write_junit_xml" + then + write_junit_xml_testcase "$*" + fi test_success=$(($test_success + 1)) say_color "" "ok $test_count - $@" } test_failure_ () { + if test -n "$write_junit_xml" + then + junit_insert="<failure message=\"not ok $test_count -" + junit_insert="$junit_insert $(xml_attr_encode "$1")\">" + junit_insert="$junit_insert $(xml_attr_encode \ + "$(if test -n "$GIT_TEST_TEE_OUTPUT_FILE" + then + test-tool path-utils skip-n-bytes \ + "$GIT_TEST_TEE_OUTPUT_FILE" $GIT_TEST_TEE_OFFSET + else + printf '%s\n' "$@" | sed 1d + fi)")" + junit_insert="$junit_insert</failure>" + if test -n "$GIT_TEST_TEE_OUTPUT_FILE" + then + junit_insert="$junit_insert<system-err>$(xml_attr_encode \ + "$(cat "$GIT_TEST_TEE_OUTPUT_FILE")")</system-err>" + fi + write_junit_xml_testcase "$1" " $junit_insert" + fi test_failure=$(($test_failure + 1)) say_color error "not ok $test_count - $1" shift @@ -635,11 +678,19 @@ test_failure_ () { } test_known_broken_ok_ () { + if test -n "$write_junit_xml" + then + write_junit_xml_testcase "$* (breakage fixed)" + fi test_fixed=$(($test_fixed+1)) say_color error "ok $test_count - $@ # TODO known breakage vanished" } test_known_broken_failure_ () { + if test -n "$write_junit_xml" + then + write_junit_xml_testcase "$* (known breakage)" + fi test_broken=$(($test_broken+1)) say_color warn "not ok $test_count - $@ # TODO known breakage" } @@ -897,12 +948,21 @@ test_start_ () { test_count=$(($test_count+1)) maybe_setup_verbose maybe_setup_valgrind + if test -n "$write_junit_xml" + then + junit_start=$(test-tool date getnanos) + fi } test_finish_ () { echo >&3 "" maybe_teardown_valgrind maybe_teardown_verbose + if test -n "$GIT_TEST_TEE_OFFSET" + then + GIT_TEST_TEE_OFFSET=$(test-tool path-utils file-size \ + "$GIT_TEST_TEE_OUTPUT_FILE") + fi } test_skip () { @@ -934,6 +994,13 @@ test_skip () { case "$to_skip" in t) + if test -n "$write_junit_xml" + then + message="$(xml_attr_encode "$skipped_reason")" + write_junit_xml_testcase "$1" \ + " <skipped message=\"$message\" />" + fi + say_color skip >&3 "skipping test: $@" say_color skip "ok $test_count # skip $1 ($skipped_reason)" : true @@ -949,9 +1016,51 @@ test_at_end_hook_ () { : } +write_junit_xml () { + case "$1" in + --truncate) + >"$junit_xml_path" + junit_have_testcase= + shift + ;; + esac + printf '%s\n' "$@" >>"$junit_xml_path" +} + +xml_attr_encode () { + printf '%s\n' "$@" | test-tool xml-encode +} + +write_junit_xml_testcase () { + junit_attrs="name=\"$(xml_attr_encode "$this_test.$test_count $1")\"" + shift + junit_attrs="$junit_attrs classname=\"$this_test\"" + junit_attrs="$junit_attrs time=\"$(test-tool \ + date getnanos $junit_start)\"" + write_junit_xml "$(printf '%s\n' \ + " <testcase $junit_attrs>" "$@" " </testcase>")" + junit_have_testcase=t +} + test_done () { GIT_EXIT_OK=t + if test -n "$write_junit_xml" && test -n "$junit_xml_path" + then + test -n "$junit_have_testcase" || { + junit_start=$(test-tool date getnanos) + write_junit_xml_testcase "all tests skipped" + } + + # adjust the overall time + junit_time=$(test-tool date getnanos $junit_suite_start) + sed "s/<testsuite [^>]*/& time=\"$junit_time\"/" \ + <"$junit_xml_path" >"$junit_xml_path.new" + mv "$junit_xml_path.new" "$junit_xml_path" + + write_junit_xml " </testsuite>" "</testsuites>" + fi + if test -z "$HARNESS_ACTIVE" then mkdir -p "$TEST_RESULTS_DIR" @@ -1011,7 +1120,11 @@ test_done () { error "Tests passed but trash directory already removed before test cleanup; aborting" cd "$TRASH_DIRECTORY/.." && - rm -fr "$TRASH_DIRECTORY" || + rm -fr "$TRASH_DIRECTORY" || { + # try again in a bit + sleep 5; + rm -fr "$TRASH_DIRECTORY" + } || error "Tests passed but test cleanup failed; aborting" fi test_at_end_hook_ @@ -1117,20 +1230,25 @@ then PATH=$GIT_TEST_INSTALLED:$GIT_BUILD_DIR/t/helper:$PATH GIT_EXEC_PATH=${GIT_TEST_EXEC_PATH:-$GIT_EXEC_PATH} else # normal case, use ../bin-wrappers only unless $with_dashes: - git_bin_dir="$GIT_BUILD_DIR/bin-wrappers" - if ! test -x "$git_bin_dir/git" + if test -n "$no_bin_wrappers" then - if test -z "$with_dashes" + with_dashes=t + else + git_bin_dir="$GIT_BUILD_DIR/bin-wrappers" + if ! test -x "$git_bin_dir/git" then - say "$git_bin_dir/git is not executable; using GIT_EXEC_PATH" + if test -z "$with_dashes" + then + say "$git_bin_dir/git is not executable; using GIT_EXEC_PATH" + fi + with_dashes=t fi - with_dashes=t + PATH="$git_bin_dir:$PATH" fi - PATH="$git_bin_dir:$PATH" GIT_EXEC_PATH=$GIT_BUILD_DIR if test -n "$with_dashes" then - PATH="$GIT_BUILD_DIR:$PATH" + PATH="$GIT_BUILD_DIR:$GIT_BUILD_DIR/t/helper:$PATH" fi fi GIT_TEMPLATE_DIR="$GIT_BUILD_DIR"/templates/blt @@ -1154,7 +1272,7 @@ test -d "$GIT_BUILD_DIR"/templates/blt || { error "You haven't built things yet, have you?" } -if ! test -x "$GIT_BUILD_DIR"/t/helper/test-tool +if ! test -x "$GIT_BUILD_DIR"/t/helper/test-tool$X then echo >&2 'You need to build test-tool:' echo >&2 'Run "make t/helper/test-tool" in the source (toplevel) directory' @@ -1178,6 +1296,7 @@ then else mkdir -p "$TRASH_DIRECTORY" fi + # Use -P to resolve symlinks in our working directory so that the cwd # in subprocesses like git equals our $PWD (for pathname comparisons). cd -P "$TRASH_DIRECTORY" || exit 1 @@ -1191,7 +1310,28 @@ then test_done fi -# Provide an implementation of the 'yes' utility +if test -n "$write_junit_xml" +then + junit_xml_dir="$TEST_OUTPUT_DIRECTORY/out" + mkdir -p "$junit_xml_dir" + junit_xml_base=${0##*/} + junit_xml_path="$junit_xml_dir/TEST-${junit_xml_base%.sh}.xml" + junit_attrs="name=\"${junit_xml_base%.sh}\"" + junit_attrs="$junit_attrs timestamp=\"$(TZ=UTC \ + date +%Y-%m-%dT%H:%M:%S)\"" + write_junit_xml --truncate "<testsuites>" " <testsuite $junit_attrs>" + junit_suite_start=$(test-tool date getnanos) + if test -n "$GIT_TEST_TEE_OUTPUT_FILE" + then + GIT_TEST_TEE_OFFSET=0 + fi +fi + +# Provide an implementation of the 'yes' utility; the upper bound +# limit is there to help Windows that cannot stop this loop from +# wasting cycles when the downstream stops reading, so do not be +# tempted to turn it into an infinite loop. cf. 6129c930 ("test-lib: +# limit the output of the yes utility", 2016-02-02) yes () { if test $# = 0 then |