diff options
Diffstat (limited to 't')
67 files changed, 5144 insertions, 323 deletions
@@ -471,13 +471,13 @@ Don't: their output. You can glean some further possible issues from the TAP grammar - (see http://search.cpan.org/perldoc?TAP::Parser::Grammar#TAP_Grammar) + (see https://metacpan.org/pod/TAP::Parser::Grammar#TAP-GRAMMAR) but the best indication is to just run the tests with prove(1), it'll complain if anything is amiss. Keep in mind: - - Inside <script> part, the standard output and standard error + - Inside the <script> part, the standard output and standard error streams are discarded, and the test harness only reports "ok" or "not ok" to the end user running the tests. Under --verbose, they are shown to help debugging the tests. @@ -611,9 +611,11 @@ library for your script to use. - test_have_prereq <prereq> - Check if we have a prerequisite previously set with - test_set_prereq. The most common use of this directly is to skip - all the tests if we don't have some essential prerequisite: + Check if we have a prerequisite previously set with test_set_prereq. + The most common way to use this explicitly (as opposed to the + implicit use when an argument is passed to test_expect_*) is to skip + all the tests at the start of the test script if we don't have some + essential prerequisite: if ! test_have_prereq PERL then diff --git a/t/helper/.gitignore b/t/helper/.gitignore index d6e8b36798..d1fa751cf9 100644 --- a/t/helper/.gitignore +++ b/t/helper/.gitignore @@ -11,6 +11,7 @@ /test-genrandom /test-hashmap /test-index-version +/test-lazy-init-name-hash /test-line-buffer /test-match-trees /test-mergesort @@ -19,6 +20,7 @@ /test-path-utils /test-prio-queue /test-read-cache +/test-ref-store /test-regex /test-revision-walking /test-run-command diff --git a/t/helper/test-config.c b/t/helper/test-config.c index 83a4f2ab86..8e3ed6a76c 100644 --- a/t/helper/test-config.c +++ b/t/helper/test-config.c @@ -66,6 +66,16 @@ static int iterate_cb(const char *var, const char *value, void *data) return 0; } +static int early_config_cb(const char *var, const char *value, void *vdata) +{ + const char *key = vdata; + + if (!strcmp(key, var)) + printf("%s\n", value); + + return 0; +} + int cmd_main(int argc, const char **argv) { int i, val; @@ -73,6 +83,11 @@ int cmd_main(int argc, const char **argv) const struct string_list *strptr; struct config_set cs; + if (argc == 3 && !strcmp(argv[1], "read_early_config")) { + read_early_config(early_config_cb, (void *)argv[2]); + return 0; + } + setup_git_directory(); git_configset_init(&cs); diff --git a/t/helper/test-lazy-init-name-hash.c b/t/helper/test-lazy-init-name-hash.c new file mode 100644 index 0000000000..6368a89345 --- /dev/null +++ b/t/helper/test-lazy-init-name-hash.c @@ -0,0 +1,264 @@ +#include "cache.h" +#include "parse-options.h" + +static int single; +static int multi; +static int count = 1; +static int dump; +static int perf; +static int analyze; +static int analyze_step; + +/* + * Dump the contents of the "dir" and "name" hash tables to stdout. + * If you sort the result, you can compare it with the other type + * mode and verify that both single and multi produce the same set. + */ +static void dump_run(void) +{ + struct hashmap_iter iter_dir; + struct hashmap_iter iter_cache; + + /* Stolen from name-hash.c */ + struct dir_entry { + struct hashmap_entry ent; + struct dir_entry *parent; + int nr; + unsigned int namelen; + char name[FLEX_ARRAY]; + }; + + struct dir_entry *dir; + struct cache_entry *ce; + + read_cache(); + if (single) { + test_lazy_init_name_hash(&the_index, 0); + } else { + int nr_threads_used = test_lazy_init_name_hash(&the_index, 1); + if (!nr_threads_used) + die("non-threaded code path used"); + } + + dir = hashmap_iter_first(&the_index.dir_hash, &iter_dir); + while (dir) { + printf("dir %08x %7d %s\n", dir->ent.hash, dir->nr, dir->name); + dir = hashmap_iter_next(&iter_dir); + } + + ce = hashmap_iter_first(&the_index.name_hash, &iter_cache); + while (ce) { + printf("name %08x %s\n", ce->ent.hash, ce->name); + ce = hashmap_iter_next(&iter_cache); + } + + discard_cache(); +} + +/* + * Run the single or multi threaded version "count" times and + * report on the time taken. + */ +static uint64_t time_runs(int try_threaded) +{ + uint64_t t0, t1, t2; + uint64_t sum = 0; + uint64_t avg; + int nr_threads_used; + int i; + + for (i = 0; i < count; i++) { + t0 = getnanotime(); + read_cache(); + t1 = getnanotime(); + nr_threads_used = test_lazy_init_name_hash(&the_index, try_threaded); + t2 = getnanotime(); + + sum += (t2 - t1); + + if (try_threaded && !nr_threads_used) + die("non-threaded code path used"); + + if (nr_threads_used) + printf("%f %f %d multi %d\n", + ((double)(t1 - t0))/1000000000, + ((double)(t2 - t1))/1000000000, + the_index.cache_nr, + nr_threads_used); + else + printf("%f %f %d single\n", + ((double)(t1 - t0))/1000000000, + ((double)(t2 - t1))/1000000000, + the_index.cache_nr); + fflush(stdout); + + discard_cache(); + } + + avg = sum / count; + if (count > 1) + printf("avg %f %s\n", + (double)avg/1000000000, + (try_threaded) ? "multi" : "single"); + + return avg; +} + +/* + * Try a series of runs varying the "istate->cache_nr" and + * try to find a good value for the multi-threaded criteria. + */ +static void analyze_run(void) +{ + uint64_t t1s, t1m, t2s, t2m; + int cache_nr_limit; + int nr_threads_used; + int i; + int nr; + + read_cache(); + cache_nr_limit = the_index.cache_nr; + discard_cache(); + + nr = analyze; + while (1) { + uint64_t sum_single = 0; + uint64_t sum_multi = 0; + uint64_t avg_single; + uint64_t avg_multi; + + if (nr > cache_nr_limit) + nr = cache_nr_limit; + + for (i = 0; i < count; i++) { + read_cache(); + the_index.cache_nr = nr; /* cheap truncate of index */ + t1s = getnanotime(); + test_lazy_init_name_hash(&the_index, 0); + t2s = getnanotime(); + sum_single += (t2s - t1s); + the_index.cache_nr = cache_nr_limit; + discard_cache(); + + read_cache(); + the_index.cache_nr = nr; /* cheap truncate of index */ + t1m = getnanotime(); + nr_threads_used = test_lazy_init_name_hash(&the_index, 1); + t2m = getnanotime(); + sum_multi += (t2m - t1m); + the_index.cache_nr = cache_nr_limit; + discard_cache(); + + if (!nr_threads_used) + printf(" [size %8d] [single %f] non-threaded code path used\n", + nr, ((double)(t2s - t1s))/1000000000); + else + printf(" [size %8d] [single %f] %c [multi %f %d]\n", + nr, + ((double)(t2s - t1s))/1000000000, + (((t2s - t1s) < (t2m - t1m)) ? '<' : '>'), + ((double)(t2m - t1m))/1000000000, + nr_threads_used); + fflush(stdout); + } + if (count > 1) { + avg_single = sum_single / count; + avg_multi = sum_multi / count; + if (!nr_threads_used) + printf("avg [size %8d] [single %f]\n", + nr, + (double)avg_single/1000000000); + else + printf("avg [size %8d] [single %f] %c [multi %f %d]\n", + nr, + (double)avg_single/1000000000, + (avg_single < avg_multi ? '<' : '>'), + (double)avg_multi/1000000000, + nr_threads_used); + fflush(stdout); + } + + if (nr >= cache_nr_limit) + return; + nr += analyze_step; + } +} + +int cmd_main(int argc, const char **argv) +{ + const char *usage[] = { + "test-lazy-init-name-hash -d (-s | -m)", + "test-lazy-init-name-hash -p [-c c]", + "test-lazy-init-name-hash -a a [--step s] [-c c]", + "test-lazy-init-name-hash (-s | -m) [-c c]", + "test-lazy-init-name-hash -s -m [-c c]", + NULL + }; + struct option options[] = { + OPT_BOOL('s', "single", &single, "run single-threaded code"), + OPT_BOOL('m', "multi", &multi, "run multi-threaded code"), + OPT_INTEGER('c', "count", &count, "number of passes"), + OPT_BOOL('d', "dump", &dump, "dump hash tables"), + OPT_BOOL('p', "perf", &perf, "compare single vs multi"), + OPT_INTEGER('a', "analyze", &analyze, "analyze different multi sizes"), + OPT_INTEGER(0, "step", &analyze_step, "analyze step factor"), + OPT_END(), + }; + const char *prefix; + uint64_t avg_single, avg_multi; + + prefix = setup_git_directory(); + + argc = parse_options(argc, argv, prefix, options, usage, 0); + + /* + * istate->dir_hash is only created when ignore_case is set. + */ + ignore_case = 1; + + if (dump) { + if (perf || analyze > 0) + die("cannot combine dump, perf, or analyze"); + if (count > 1) + die("count not valid with dump"); + if (single && multi) + die("cannot use both single and multi with dump"); + if (!single && !multi) + die("dump requires either single or multi"); + dump_run(); + return 0; + } + + if (perf) { + if (analyze > 0) + die("cannot combine dump, perf, or analyze"); + if (single || multi) + die("cannot use single or multi with perf"); + avg_single = time_runs(0); + avg_multi = time_runs(1); + if (avg_multi > avg_single) + die("multi is slower"); + return 0; + } + + if (analyze) { + if (analyze < 500) + die("analyze must be at least 500"); + if (!analyze_step) + analyze_step = analyze; + if (single || multi) + die("cannot use single or multi with analyze"); + analyze_run(); + return 0; + } + + if (!single && !multi) + die("require either -s or -m or both"); + + if (single) + time_runs(0); + if (multi) + time_runs(1); + + return 0; +} diff --git a/t/helper/test-read-cache.c b/t/helper/test-read-cache.c index 2a7990efc3..48255eef31 100644 --- a/t/helper/test-read-cache.c +++ b/t/helper/test-read-cache.c @@ -5,6 +5,7 @@ int cmd_main(int argc, const char **argv) int i, cnt = 1; if (argc == 2) cnt = strtol(argv[1], NULL, 0); + setup_git_directory(); for (i = 0; i < cnt; i++) { read_cache(); discard_cache(); diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c new file mode 100644 index 0000000000..2d84c45ffe --- /dev/null +++ b/t/helper/test-ref-store.c @@ -0,0 +1,277 @@ +#include "cache.h" +#include "refs.h" + +static const char *notnull(const char *arg, const char *name) +{ + if (!arg) + die("%s required", name); + return arg; +} + +static unsigned int arg_flags(const char *arg, const char *name) +{ + return atoi(notnull(arg, name)); +} + +static const char **get_store(const char **argv, struct ref_store **refs) +{ + const char *gitdir; + + if (!argv[0]) { + die("ref store required"); + } else if (!strcmp(argv[0], "main")) { + *refs = get_main_ref_store(); + } else if (skip_prefix(argv[0], "submodule:", &gitdir)) { + struct strbuf sb = STRBUF_INIT; + int ret; + + ret = strbuf_git_path_submodule(&sb, gitdir, "objects/"); + if (ret) + die("strbuf_git_path_submodule failed: %d", ret); + add_to_alternates_memory(sb.buf); + strbuf_release(&sb); + + *refs = get_submodule_ref_store(gitdir); + } else + die("unknown backend %s", argv[0]); + + if (!*refs) + die("no ref store"); + + /* consume store-specific optional arguments if needed */ + + return argv + 1; +} + + +static int cmd_pack_refs(struct ref_store *refs, const char **argv) +{ + unsigned int flags = arg_flags(*argv++, "flags"); + + return refs_pack_refs(refs, flags); +} + +static int cmd_peel_ref(struct ref_store *refs, const char **argv) +{ + const char *refname = notnull(*argv++, "refname"); + unsigned char sha1[20]; + int ret; + + ret = refs_peel_ref(refs, refname, sha1); + if (!ret) + puts(sha1_to_hex(sha1)); + return ret; +} + +static int cmd_create_symref(struct ref_store *refs, const char **argv) +{ + const char *refname = notnull(*argv++, "refname"); + const char *target = notnull(*argv++, "target"); + const char *logmsg = *argv++; + + return refs_create_symref(refs, refname, target, logmsg); +} + +static int cmd_delete_refs(struct ref_store *refs, const char **argv) +{ + unsigned int flags = arg_flags(*argv++, "flags"); + struct string_list refnames = STRING_LIST_INIT_NODUP; + + while (*argv) + string_list_append(&refnames, *argv++); + + return refs_delete_refs(refs, &refnames, flags); +} + +static int cmd_rename_ref(struct ref_store *refs, const char **argv) +{ + const char *oldref = notnull(*argv++, "oldref"); + const char *newref = notnull(*argv++, "newref"); + const char *logmsg = *argv++; + + return refs_rename_ref(refs, oldref, newref, logmsg); +} + +static int each_ref(const char *refname, const struct object_id *oid, + int flags, void *cb_data) +{ + printf("%s %s 0x%x\n", oid_to_hex(oid), refname, flags); + return 0; +} + +static int cmd_for_each_ref(struct ref_store *refs, const char **argv) +{ + const char *prefix = notnull(*argv++, "prefix"); + + return refs_for_each_ref_in(refs, prefix, each_ref, NULL); +} + +static int cmd_resolve_ref(struct ref_store *refs, const char **argv) +{ + unsigned char sha1[20]; + const char *refname = notnull(*argv++, "refname"); + int resolve_flags = arg_flags(*argv++, "resolve-flags"); + int flags; + const char *ref; + + ref = refs_resolve_ref_unsafe(refs, refname, resolve_flags, + sha1, &flags); + printf("%s %s 0x%x\n", sha1_to_hex(sha1), ref, flags); + return ref ? 0 : 1; +} + +static int cmd_verify_ref(struct ref_store *refs, const char **argv) +{ + const char *refname = notnull(*argv++, "refname"); + struct strbuf err = STRBUF_INIT; + int ret; + + ret = refs_verify_refname_available(refs, refname, NULL, NULL, &err); + if (err.len) + puts(err.buf); + return ret; +} + +static int cmd_for_each_reflog(struct ref_store *refs, const char **argv) +{ + return refs_for_each_reflog(refs, each_ref, NULL); +} + +static int each_reflog(struct object_id *old_oid, struct object_id *new_oid, + const char *committer, unsigned long timestamp, + int tz, const char *msg, void *cb_data) +{ + printf("%s %s %s %lu %d %s\n", + oid_to_hex(old_oid), oid_to_hex(new_oid), + committer, timestamp, tz, msg); + return 0; +} + +static int cmd_for_each_reflog_ent(struct ref_store *refs, const char **argv) +{ + const char *refname = notnull(*argv++, "refname"); + + return refs_for_each_reflog_ent(refs, refname, each_reflog, refs); +} + +static int cmd_for_each_reflog_ent_reverse(struct ref_store *refs, const char **argv) +{ + const char *refname = notnull(*argv++, "refname"); + + return refs_for_each_reflog_ent_reverse(refs, refname, each_reflog, refs); +} + +static int cmd_reflog_exists(struct ref_store *refs, const char **argv) +{ + const char *refname = notnull(*argv++, "refname"); + + return !refs_reflog_exists(refs, refname); +} + +static int cmd_create_reflog(struct ref_store *refs, const char **argv) +{ + const char *refname = notnull(*argv++, "refname"); + int force_create = arg_flags(*argv++, "force-create"); + struct strbuf err = STRBUF_INIT; + int ret; + + ret = refs_create_reflog(refs, refname, force_create, &err); + if (err.len) + puts(err.buf); + return ret; +} + +static int cmd_delete_reflog(struct ref_store *refs, const char **argv) +{ + const char *refname = notnull(*argv++, "refname"); + + return refs_delete_reflog(refs, refname); +} + +static int cmd_reflog_expire(struct ref_store *refs, const char **argv) +{ + die("not supported yet"); +} + +static int cmd_delete_ref(struct ref_store *refs, const char **argv) +{ + const char *msg = notnull(*argv++, "msg"); + const char *refname = notnull(*argv++, "refname"); + const char *sha1_buf = notnull(*argv++, "old-sha1"); + unsigned int flags = arg_flags(*argv++, "flags"); + unsigned char old_sha1[20]; + + if (get_sha1_hex(sha1_buf, old_sha1)) + die("not sha-1"); + + return refs_delete_ref(refs, msg, refname, old_sha1, flags); +} + +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 *old_sha1_buf = notnull(*argv++, "old-sha1"); + unsigned int flags = arg_flags(*argv++, "flags"); + unsigned char old_sha1[20]; + unsigned char new_sha1[20]; + + if (get_sha1_hex(old_sha1_buf, old_sha1) || + get_sha1_hex(new_sha1_buf, new_sha1)) + die("not sha-1"); + + return refs_update_ref(refs, msg, refname, + new_sha1, old_sha1, + flags, UPDATE_REFS_DIE_ON_ERR); +} + +struct command { + const char *name; + int (*func)(struct ref_store *refs, const char **argv); +}; + +static struct command commands[] = { + { "pack-refs", cmd_pack_refs }, + { "peel-ref", cmd_peel_ref }, + { "create-symref", cmd_create_symref }, + { "delete-refs", cmd_delete_refs }, + { "rename-ref", cmd_rename_ref }, + { "for-each-ref", cmd_for_each_ref }, + { "resolve-ref", cmd_resolve_ref }, + { "verify-ref", cmd_verify_ref }, + { "for-each-reflog", cmd_for_each_reflog }, + { "for-each-reflog-ent", cmd_for_each_reflog_ent }, + { "for-each-reflog-ent-reverse", cmd_for_each_reflog_ent_reverse }, + { "reflog-exists", cmd_reflog_exists }, + { "create-reflog", cmd_create_reflog }, + { "delete-reflog", cmd_delete_reflog }, + { "reflog-expire", cmd_reflog_expire }, + /* + * backend transaction functions can't be tested separately + */ + { "delete-ref", cmd_delete_ref }, + { "update-ref", cmd_update_ref }, + { NULL, NULL } +}; + +int cmd_main(int argc, const char **argv) +{ + struct ref_store *refs; + const char *func; + struct command *cmd; + + setup_git_directory(); + + argv = get_store(argv + 1, &refs); + + func = *argv++; + if (!func) + die("ref function required"); + for (cmd = commands; cmd->name; cmd++) { + if (!strcmp(func, cmd->name)) + return cmd->func(refs, argv); + } + die("unknown function %s", func); + return 0; +} diff --git a/t/helper/test-sha1-array.c b/t/helper/test-sha1-array.c index f7a53c4ad6..edfd52d82a 100644 --- a/t/helper/test-sha1-array.c +++ b/t/helper/test-sha1-array.c @@ -1,33 +1,33 @@ #include "cache.h" #include "sha1-array.h" -static int print_sha1(const unsigned char sha1[20], void *data) +static int print_oid(const struct object_id *oid, void *data) { - puts(sha1_to_hex(sha1)); + puts(oid_to_hex(oid)); return 0; } int cmd_main(int argc, const char **argv) { - struct sha1_array array = SHA1_ARRAY_INIT; + struct oid_array array = OID_ARRAY_INIT; struct strbuf line = STRBUF_INIT; while (strbuf_getline(&line, stdin) != EOF) { const char *arg; - unsigned char sha1[20]; + struct object_id oid; if (skip_prefix(line.buf, "append ", &arg)) { - if (get_sha1_hex(arg, sha1)) + if (get_oid_hex(arg, &oid)) die("not a hexadecimal SHA1: %s", arg); - sha1_array_append(&array, sha1); + oid_array_append(&array, &oid); } else if (skip_prefix(line.buf, "lookup ", &arg)) { - if (get_sha1_hex(arg, sha1)) + if (get_oid_hex(arg, &oid)) die("not a hexadecimal SHA1: %s", arg); - printf("%d\n", sha1_array_lookup(&array, sha1)); + printf("%d\n", oid_array_lookup(&array, &oid)); } else if (!strcmp(line.buf, "clear")) - sha1_array_clear(&array); + oid_array_clear(&array); else if (!strcmp(line.buf, "for_each_unique")) - sha1_array_for_each_unique(&array, print_sha1, NULL); + oid_array_for_each_unique(&array, print_oid, NULL); else die("unknown command: %s", line.buf); } diff --git a/t/lib-submodule-update.sh b/t/lib-submodule-update.sh index 915eb4a7c6..fb4f7b014e 100755 --- a/t/lib-submodule-update.sh +++ b/t/lib-submodule-update.sh @@ -4,6 +4,7 @@ # - New submodule (no_submodule => add_sub1) # - Removed submodule (add_sub1 => remove_sub1) # - Updated submodule (add_sub1 => modify_sub1) +# - Updated submodule recursively (add_nested_sub => modify_sub1_recursively) # - Submodule updated to invalid commit (add_sub1 => invalid_sub1) # - Submodule updated from invalid commit (invalid_sub1 => valid_sub1) # - Submodule replaced by tracked files in directory (add_sub1 => @@ -15,23 +16,50 @@ # - Tracked file replaced by submodule (replace_sub1_with_file => # replace_file_with_sub1) # -# --O-----O -# / ^ replace_directory_with_sub1 -# / replace_sub1_with_directory -# /----O -# / ^ -# / modify_sub1 -# O------O-------O -# ^ ^\ ^ -# | | \ remove_sub1 -# | | -----O-----O -# | | \ ^ replace_file_with_sub1 -# | | \ replace_sub1_with_file -# | add_sub1 --O-----O -# no_submodule ^ valid_sub1 -# invalid_sub1 +# ----O +# / ^ +# / remove_sub1 +# / +# add_sub1 /-------O---------O--------O modify_sub1_recursively +# | / ^ add_nested_sub +# | / modify_sub1 +# v/ +# O------O-----------O---------O +# ^ \ ^ replace_directory_with_sub1 +# | \ replace_sub1_with_directory +# no_submodule \ +# --------O---------O +# \ ^ replace_file_with_sub1 +# \ replace_sub1_with_file +# \ +# ----O---------O +# ^ valid_sub1 +# invalid_sub1 # + create_lib_submodule_repo () { + git init submodule_update_sub1 && + ( + cd submodule_update_sub1 && + echo "expect" >>.gitignore && + echo "actual" >>.gitignore && + echo "x" >file1 && + echo "y" >file2 && + git add .gitignore file1 file2 && + git commit -m "Base inside first submodule" && + git branch "no_submodule" + ) && + git init submodule_update_sub2 && + ( + cd submodule_update_sub2 + echo "expect" >>.gitignore && + echo "actual" >>.gitignore && + echo "x" >file1 && + echo "y" >file2 && + git add .gitignore file1 file2 && + git commit -m "nested submodule base" && + git branch "no_submodule" + ) && git init submodule_update_repo && ( cd submodule_update_repo && @@ -44,15 +72,16 @@ create_lib_submodule_repo () { git branch "no_submodule" && git checkout -b "add_sub1" && - git submodule add ./. sub1 && + git submodule add ../submodule_update_sub1 sub1 && git config -f .gitmodules submodule.sub1.ignore all && git config submodule.sub1.ignore all && git add .gitmodules && git commit -m "Add sub1" && - git checkout -b remove_sub1 && + + git checkout -b remove_sub1 add_sub1 && git revert HEAD && - git checkout -b "modify_sub1" "add_sub1" && + git checkout -b modify_sub1 add_sub1 && git submodule update && ( cd sub1 && @@ -67,7 +96,27 @@ create_lib_submodule_repo () { git add sub1 && git commit -m "Modify sub1" && - git checkout -b "replace_sub1_with_directory" "add_sub1" && + git checkout -b add_nested_sub modify_sub1 && + git -C sub1 checkout -b "add_nested_sub" && + git -C sub1 submodule add --branch no_submodule ../submodule_update_sub2 sub2 && + git -C sub1 commit -a -m "add a nested submodule" && + git add sub1 && + git commit -a -m "update submodule, that updates a nested submodule" && + git checkout -b modify_sub1_recursively && + git -C sub1 checkout -b modify_sub1_recursively && + git -C sub1/sub2 checkout -b modify_sub1_recursively && + echo change >sub1/sub2/file3 && + git -C sub1/sub2 add file3 && + git -C sub1/sub2 commit -m "make a change in nested sub" && + git -C sub1 add sub2 && + git -C sub1 commit -m "update nested sub" && + git add sub1 && + git commit -m "update sub1, that updates nested sub" && + git -C sub1 push origin modify_sub1_recursively && + git -C sub1/sub2 push origin modify_sub1_recursively && + git -C sub1 submodule deinit -f --all && + + git checkout -b replace_sub1_with_directory add_sub1 && git submodule update && git -C sub1 checkout modifications && git rm --cached sub1 && @@ -75,22 +124,25 @@ create_lib_submodule_repo () { git config -f .gitmodules --remove-section "submodule.sub1" && git add .gitmodules sub1/* && git commit -m "Replace sub1 with directory" && + git checkout -b replace_directory_with_sub1 && git revert HEAD && - git checkout -b "replace_sub1_with_file" "add_sub1" && + git checkout -b replace_sub1_with_file add_sub1 && git rm sub1 && echo "content" >sub1 && git add sub1 && git commit -m "Replace sub1 with file" && + git checkout -b replace_file_with_sub1 && git revert HEAD && - git checkout -b "invalid_sub1" "add_sub1" && + git checkout -b invalid_sub1 add_sub1 && git update-index --cacheinfo 160000 0123456789012345678901234567890123456789 sub1 && git commit -m "Invalid sub1 commit" && git checkout -b valid_sub1 && git revert HEAD && + git checkout master ) } @@ -130,6 +182,15 @@ test_git_directory_is_unchanged () { ) } +test_git_directory_exists() { + test -e ".git/modules/$1" && + if test -f sub1/.git + then + # does core.worktree point at the right place? + test "$(git -C .git/modules/$1 config core.worktree)" = "../../../$1" + fi +} + # Helper function to be executed at the start of every test below, it sets up # the submodule repo if it doesn't exist and configures the most problematic # settings for diff.ignoreSubmodules. @@ -151,15 +212,36 @@ reset_work_tree_to () { git checkout -f "$1" && git status -u -s >actual && test_must_be_empty actual && - sha1=$(git rev-parse --revs-only HEAD:sub1) && - if test -n "$sha1" && - test $(cd "sub1" && git rev-parse --verify "$sha1^{commit}") + hash=$(git rev-parse --revs-only HEAD:sub1) && + if test -n "$hash" && + test $(cd "../submodule_update_sub1" && git rev-parse --verify "$hash^{commit}") then git submodule update --init --recursive "sub1" fi ) } +reset_work_tree_to_interested () { + reset_work_tree_to $1 && + # make the submodule git dirs available + if ! test -d submodule_update/.git/modules/sub1 + then + mkdir -p submodule_update/.git/modules && + cp -r submodule_update_repo/.git/modules/sub1 submodule_update/.git/modules/sub1 + GIT_WORK_TREE=. git -C submodule_update/.git/modules/sub1 config --unset core.worktree + fi && + if ! test -d submodule_update/.git/modules/sub1/modules/sub2 + then + mkdir -p submodule_update/.git/modules/sub1/modules && + cp -r submodule_update_repo/.git/modules/sub1/modules/sub2 submodule_update/.git/modules/sub1/modules/sub2 + GIT_WORK_TREE=. git -C submodule_update/.git/modules/sub1/modules/sub2 config --unset core.worktree + fi && + # indicate we are interested in the submodule: + git -C submodule_update config submodule.sub1.url "bogus" && + # sub1 might not be checked out, so use the git dir + git -C submodule_update/.git/modules/sub1 config submodule.sub2.url "bogus" +} + # Test that the superproject contains the content according to commit "$1" # (the work tree must match the index for everything but submodules but the # index must exactly match the given commit including any submodule SHA-1s). @@ -173,6 +255,11 @@ test_superproject_content () { # Test that the given submodule at path "$1" contains the content according # to the submodule commit recorded in the superproject's commit "$2" test_submodule_content () { + if test x"$1" = "x-C" + then + cd "$2" + shift; shift; + fi if test $# != 2 then echo "test_submodule_content needs two arguments" @@ -675,3 +762,464 @@ test_submodule_forced_switch () { ) ' } + +# Test that submodule contents are correctly updated when switching +# between commits that change a submodule. +# Test that the following transitions are correctly handled: +# (These tests are also above in the case where we expect no change +# in the submodule) +# - Updated submodule +# - New submodule +# - Removed submodule +# - Directory containing tracked files replaced by submodule +# - Submodule replaced by tracked files in directory +# - Submodule replaced by tracked file with the same name +# - tracked file replaced by submodule +# +# New test cases +# - Removing a submodule with a git directory absorbs the submodules +# git directory first into the superproject. + +test_submodule_switch_recursing () { + command="$1" + RESULTDS=success + if test "$KNOWN_FAILURE_DIRECTORY_SUBMODULE_CONFLICTS" = 1 + then + RESULTDS=failure + fi + RESULTR=success + if test "$KNOWN_FAILURE_SUBMODULE_RECURSIVE_NESTED" = 1 + then + RESULTR=failure + fi + RESULTOI=success + if test "$KNOWN_FAILURE_SUBMODULE_OVERWRITE_IGNORED_UNTRACKED" = 1 + then + RESULTOI=failure + fi + ######################### Appearing submodule ######################### + # Switching to a commit letting a submodule appear checks it out ... + test_expect_success "$command: added submodule is checked out" ' + prolog && + reset_work_tree_to_interested no_submodule && + ( + cd submodule_update && + git branch -t add_sub1 origin/add_sub1 && + $command add_sub1 && + test_superproject_content origin/add_sub1 && + test_submodule_content sub1 origin/add_sub1 + ) + ' + # ... ignoring an empty existing directory ... + test_expect_success "$command: added submodule is checked out in empty dir" ' + prolog && + reset_work_tree_to_interested no_submodule && + ( + cd submodule_update && + mkdir sub1 && + git branch -t add_sub1 origin/add_sub1 && + $command add_sub1 && + test_superproject_content origin/add_sub1 && + test_submodule_content sub1 origin/add_sub1 + ) + ' + # ... unless there is an untracked file in its place. + test_expect_success "$command: added submodule doesn't remove untracked file with same name" ' + prolog && + reset_work_tree_to_interested no_submodule && + ( + cd submodule_update && + git branch -t add_sub1 origin/add_sub1 && + : >sub1 && + test_must_fail $command add_sub1 && + test_superproject_content origin/no_submodule && + test_must_be_empty sub1 + ) + ' + # ... but an ignored file is fine. + test_expect_$RESULTOI "$command: added submodule removes an untracked ignored file" ' + test_when_finished "rm submodule_update/.git/info/exclude" && + prolog && + reset_work_tree_to_interested no_submodule && + ( + cd submodule_update && + git branch -t add_sub1 origin/add_sub1 && + : >sub1 && + echo sub1 >.git/info/exclude + $command add_sub1 && + test_superproject_content origin/add_sub1 && + test_submodule_content sub1 origin/add_sub1 + ) + ' + # Replacing a tracked file with a submodule produces a checked out submodule + test_expect_success "$command: replace tracked file with submodule checks out submodule" ' + prolog && + reset_work_tree_to_interested replace_sub1_with_file && + ( + cd submodule_update && + git branch -t replace_file_with_sub1 origin/replace_file_with_sub1 && + $command replace_file_with_sub1 && + test_superproject_content origin/replace_file_with_sub1 && + test_submodule_content sub1 origin/replace_file_with_sub1 + ) + ' + # ... as does removing a directory with tracked files with a submodule. + test_expect_success "$command: replace directory with submodule" ' + prolog && + reset_work_tree_to_interested replace_sub1_with_directory && + ( + cd submodule_update && + git branch -t replace_directory_with_sub1 origin/replace_directory_with_sub1 && + $command replace_directory_with_sub1 && + test_superproject_content origin/replace_directory_with_sub1 && + test_submodule_content sub1 origin/replace_directory_with_sub1 + ) + ' + + ######################## Disappearing submodule ####################### + # Removing a submodule removes its work tree ... + test_expect_success "$command: removed submodule removes submodules working tree" ' + prolog && + reset_work_tree_to_interested add_sub1 && + ( + cd submodule_update && + git branch -t remove_sub1 origin/remove_sub1 && + $command remove_sub1 && + test_superproject_content origin/remove_sub1 && + ! test -e sub1 + ) + ' + # ... absorbing a .git directory along the way. + test_expect_success "$command: removed submodule absorbs submodules .git directory" ' + prolog && + reset_work_tree_to_interested add_sub1 && + ( + cd submodule_update && + git branch -t remove_sub1 origin/remove_sub1 && + replace_gitfile_with_git_dir sub1 && + rm -rf .git/modules && + $command remove_sub1 && + test_superproject_content origin/remove_sub1 && + ! test -e sub1 && + test_git_directory_exists sub1 + ) + ' + # Replacing a submodule with files in a directory must succeeds + # when the submodule is clean + test_expect_$RESULTDS "$command: replace submodule with a directory" ' + prolog && + reset_work_tree_to_interested add_sub1 && + ( + cd submodule_update && + git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory && + $command replace_sub1_with_directory && + test_superproject_content origin/replace_sub1_with_directory && + test_submodule_content sub1 origin/replace_sub1_with_directory + ) + ' + # ... absorbing a .git directory. + test_expect_$RESULTDS "$command: replace submodule containing a .git directory with a directory must absorb the git dir" ' + prolog && + reset_work_tree_to_interested add_sub1 && + ( + cd submodule_update && + git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory && + replace_gitfile_with_git_dir sub1 && + rm -rf .git/modules && + $command replace_sub1_with_directory && + test_superproject_content origin/replace_sub1_with_directory && + test_git_directory_exists sub1 + ) + ' + + # Replacing it with a file ... + test_expect_success "$command: replace submodule with a file" ' + prolog && + reset_work_tree_to_interested add_sub1 && + ( + cd submodule_update && + git branch -t replace_sub1_with_file origin/replace_sub1_with_file && + $command replace_sub1_with_file && + test_superproject_content origin/replace_sub1_with_file && + test -f sub1 + ) + ' + + # ... must check its local work tree for untracked files + test_expect_$RESULTDS "$command: replace submodule with a file must fail with untracked files" ' + prolog && + reset_work_tree_to_interested add_sub1 && + ( + cd submodule_update && + git branch -t replace_sub1_with_file origin/replace_sub1_with_file && + : >sub1/untrackedfile && + test_must_fail $command replace_sub1_with_file && + test_superproject_content origin/add_sub1 && + test_submodule_content sub1 origin/add_sub1 + ) + ' + + # ... and ignored files are ignored + test_expect_success "$command: replace submodule with a file works ignores ignored files in submodule" ' + test_when_finished "rm submodule_update/.git/modules/sub1/info/exclude" && + prolog && + reset_work_tree_to_interested add_sub1 && + ( + cd submodule_update && + git branch -t replace_sub1_with_file origin/replace_sub1_with_file && + : >sub1/ignored && + $command replace_sub1_with_file && + test_superproject_content origin/replace_sub1_with_file && + test -f sub1 + ) + ' + + ########################## Modified submodule ######################### + # Updating a submodule sha1 updates the submodule's work tree + test_expect_success "$command: modified submodule updates submodule work tree" ' + prolog && + reset_work_tree_to_interested add_sub1 && + ( + cd submodule_update && + git branch -t modify_sub1 origin/modify_sub1 && + $command modify_sub1 && + test_superproject_content origin/modify_sub1 && + test_submodule_content sub1 origin/modify_sub1 + ) + ' + + # Updating a submodule to an invalid sha1 doesn't update the + # superproject nor the submodule's work tree. + test_expect_success "$command: updating to a missing submodule commit fails" ' + prolog && + reset_work_tree_to_interested add_sub1 && + ( + cd submodule_update && + git branch -t invalid_sub1 origin/invalid_sub1 && + test_must_fail $command invalid_sub1 && + test_superproject_content origin/add_sub1 && + test_submodule_content sub1 origin/add_sub1 + ) + ' + + # recursing deeper than one level doesn't work yet. + test_expect_$RESULTR "$command: modified submodule updates submodule recursively" ' + prolog && + reset_work_tree_to_interested add_nested_sub && + ( + cd submodule_update && + git branch -t modify_sub1_recursively origin/modify_sub1_recursively && + $command modify_sub1_recursively && + test_superproject_content origin/modify_sub1_recursively && + test_submodule_content sub1 origin/modify_sub1_recursively && + test_submodule_content -C sub1 sub2 origin/modify_sub1_recursively + ) + ' +} + +# Test that submodule contents are updated when switching between commits +# that change a submodule, but throwing away local changes in +# the superproject as well as the submodule is allowed. +test_submodule_forced_switch_recursing () { + command="$1" + RESULT=success + if test "$KNOWN_FAILURE_DIRECTORY_SUBMODULE_CONFLICTS" = 1 + then + RESULT=failure + fi + ######################### Appearing submodule ######################### + # Switching to a commit letting a submodule appear creates empty dir ... + test_expect_success "$command: added submodule is checked out" ' + prolog && + reset_work_tree_to_interested no_submodule && + ( + cd submodule_update && + git branch -t add_sub1 origin/add_sub1 && + $command add_sub1 && + test_superproject_content origin/add_sub1 && + test_submodule_content sub1 origin/add_sub1 + ) + ' + # ... and doesn't care if it already exists ... + test_expect_success "$command: added submodule ignores empty directory" ' + prolog && + reset_work_tree_to_interested no_submodule && + ( + cd submodule_update && + git branch -t add_sub1 origin/add_sub1 && + mkdir sub1 && + $command add_sub1 && + test_superproject_content origin/add_sub1 && + test_submodule_content sub1 origin/add_sub1 + ) + ' + # ... not caring about an untracked file either + test_expect_success "$command: added submodule does remove untracked unignored file with same name when forced" ' + prolog && + reset_work_tree_to_interested no_submodule && + ( + cd submodule_update && + git branch -t add_sub1 origin/add_sub1 && + >sub1 && + $command add_sub1 && + test_superproject_content origin/add_sub1 && + test_submodule_content sub1 origin/add_sub1 + ) + ' + # Replacing a tracked file with a submodule checks out the submodule + test_expect_success "$command: replace tracked file with submodule populates the submodule" ' + prolog && + reset_work_tree_to_interested replace_sub1_with_file && + ( + cd submodule_update && + git branch -t replace_file_with_sub1 origin/replace_file_with_sub1 && + $command replace_file_with_sub1 && + test_superproject_content origin/replace_file_with_sub1 && + test_submodule_content sub1 origin/replace_file_with_sub1 + ) + ' + # ... as does removing a directory with tracked files with a + # submodule. + test_expect_success "$command: replace directory with submodule" ' + prolog && + reset_work_tree_to_interested replace_sub1_with_directory && + ( + cd submodule_update && + git branch -t replace_directory_with_sub1 origin/replace_directory_with_sub1 && + $command replace_directory_with_sub1 && + test_superproject_content origin/replace_directory_with_sub1 && + test_submodule_content sub1 origin/replace_directory_with_sub1 + ) + ' + + ######################## Disappearing submodule ####################### + # Removing a submodule doesn't remove its work tree ... + test_expect_success "$command: removed submodule leaves submodule directory and its contents in place" ' + prolog && + reset_work_tree_to_interested add_sub1 && + ( + cd submodule_update && + git branch -t remove_sub1 origin/remove_sub1 && + $command remove_sub1 && + test_superproject_content origin/remove_sub1 && + ! test -e sub1 + ) + ' + # ... especially when it contains a .git directory. + test_expect_success "$command: removed submodule leaves submodule containing a .git directory alone" ' + prolog && + reset_work_tree_to_interested add_sub1 && + ( + cd submodule_update && + git branch -t remove_sub1 origin/remove_sub1 && + replace_gitfile_with_git_dir sub1 && + rm -rf .git/modules/sub1 && + $command remove_sub1 && + test_superproject_content origin/remove_sub1 && + test_git_directory_exists sub1 && + ! test -e sub1 + ) + ' + # Replacing a submodule with files in a directory ... + test_expect_success "$command: replace submodule with a directory" ' + prolog && + reset_work_tree_to_interested add_sub1 && + ( + cd submodule_update && + git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory && + $command replace_sub1_with_directory && + test_superproject_content origin/replace_sub1_with_directory + ) + ' + # ... absorbing a .git directory. + test_expect_success "$command: replace submodule containing a .git directory with a directory must fail" ' + prolog && + reset_work_tree_to_interested add_sub1 && + ( + cd submodule_update && + git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory && + replace_gitfile_with_git_dir sub1 && + rm -rf .git/modules/sub1 && + $command replace_sub1_with_directory && + test_superproject_content origin/replace_sub1_with_directory && + test_submodule_content sub1 origin/modify_sub1 + test_git_directory_exists sub1 + ) + ' + # Replacing it with a file + test_expect_success "$command: replace submodule with a file" ' + prolog && + reset_work_tree_to_interested add_sub1 && + ( + cd submodule_update && + git branch -t replace_sub1_with_file origin/replace_sub1_with_file && + $command replace_sub1_with_file && + test_superproject_content origin/replace_sub1_with_file + ) + ' + + # ... even if the submodule contains ignored files + test_expect_success "$command: replace submodule with a file ignoring ignored files" ' + prolog && + reset_work_tree_to_interested add_sub1 && + ( + cd submodule_update && + git branch -t replace_sub1_with_file origin/replace_sub1_with_file && + : >sub1/expect && + $command replace_sub1_with_file && + test_superproject_content origin/replace_sub1_with_file + ) + ' + + # ... but stops for untracked files that would be lost + test_expect_$RESULT "$command: replace submodule with a file stops for untracked files" ' + prolog && + reset_work_tree_to_interested add_sub1 && + ( + cd submodule_update && + git branch -t replace_sub1_with_file origin/replace_sub1_with_file && + : >sub1/untracked_file && + test_must_fail $command replace_sub1_with_file && + test_superproject_content origin/add_sub1 && + test -f sub1/untracked_file + ) + ' + + ########################## Modified submodule ######################### + # Updating a submodule sha1 updates the submodule's work tree + test_expect_success "$command: modified submodule updates submodule work tree" ' + prolog && + reset_work_tree_to_interested add_sub1 && + ( + cd submodule_update && + git branch -t modify_sub1 origin/modify_sub1 && + $command modify_sub1 && + test_superproject_content origin/modify_sub1 && + test_submodule_content sub1 origin/modify_sub1 + ) + ' + # Updating a submodule to an invalid sha1 doesn't update the + # submodule's work tree, subsequent update will fail + test_expect_success "$command: modified submodule does not update submodule work tree to invalid commit" ' + prolog && + reset_work_tree_to_interested add_sub1 && + ( + cd submodule_update && + git branch -t invalid_sub1 origin/invalid_sub1 && + test_must_fail $command invalid_sub1 && + test_superproject_content origin/add_sub1 && + test_submodule_content sub1 origin/add_sub1 + ) + ' + # Updating a submodule from an invalid sha1 updates + test_expect_success "$command: modified submodule does not update submodule work tree from invalid commit" ' + prolog && + reset_work_tree_to_interested invalid_sub1 && + ( + cd submodule_update && + git branch -t valid_sub1 origin/valid_sub1 && + test_must_fail $command valid_sub1 && + test_superproject_content origin/invalid_sub1 + ) + ' +} diff --git a/t/perf/p0004-lazy-init-name-hash.sh b/t/perf/p0004-lazy-init-name-hash.sh new file mode 100644 index 0000000000..5afa8c8df3 --- /dev/null +++ b/t/perf/p0004-lazy-init-name-hash.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +test_description='Tests multi-threaded lazy_init_name_hash' +. ./perf-lib.sh + +test_perf_large_repo +test_checkout_worktree + +test_expect_success 'verify both methods build the same hashmaps' ' + $GIT_BUILD_DIR/t/helper/test-lazy-init-name-hash$X --dump --single | sort >out.single && + $GIT_BUILD_DIR/t/helper/test-lazy-init-name-hash$X --dump --multi | sort >out.multi && + test_cmp out.single out.multi +' + +test_expect_success 'multithreaded should be faster' ' + $GIT_BUILD_DIR/t/helper/test-lazy-init-name-hash$X --perf >out.perf +' + +test_done diff --git a/t/t0001-init.sh b/t/t0001-init.sh index e424de5363..c4814d248f 100755 --- a/t/t0001-init.sh +++ b/t/t0001-init.sh @@ -315,6 +315,20 @@ test_expect_success 'init with separate gitdir' ' test_path_is_dir realgitdir/refs ' +test_expect_success 'init in long base path' ' + # exceed initial buffer size of strbuf_getcwd() + component=123456789abcdef && + test_when_finished "chmod 0700 $component; rm -rf $component" && + p31=$component/$component && + p127=$p31/$p31/$p31/$p31 && + mkdir -p $p127 && + chmod 0111 $component && + ( + cd $p127 && + git init newdir + ) +' + test_expect_success 're-init on .git file' ' ( cd newdir && git init ) ' diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh index f0fbb42554..f19ae4f8cc 100755 --- a/t/t0003-attributes.sh +++ b/t/t0003-attributes.sh @@ -13,10 +13,31 @@ attr_check () { test_line_count = 0 err } +attr_check_quote () { + + path="$1" + quoted_path="$2" + expect="$3" + + git check-attr test -- "$path" >actual && + echo "\"$quoted_path\": test: $expect" >expect && + test_cmp expect actual + +} + +test_expect_success 'open-quoted pathname' ' + echo "\"a test=a" >.gitattributes && + test_must_fail attr_check a a +' + + test_expect_success 'setup' ' mkdir -p a/b/d a/c b && ( echo "[attr]notest !test" + echo "\" d \" test=d" + echo " e test=e" + echo " e\" test=e" echo "f test=f" echo "a/i test=a/i" echo "onoff test -test" @@ -69,6 +90,11 @@ test_expect_success 'command line checks' ' ' test_expect_success 'attribute test' ' + + attr_check " d " d && + attr_check e e && + attr_check_quote e\" e\\\" e && + attr_check f f && attr_check a/f f && attr_check a/c/f f && diff --git a/t/t0013-sha1dc.sh b/t/t0013-sha1dc.sh new file mode 100755 index 0000000000..6d655cb161 --- /dev/null +++ b/t/t0013-sha1dc.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +test_description='test sha1 collision detection' +. ./test-lib.sh +TEST_DATA="$TEST_DIRECTORY/t0013" + +if test -z "$DC_SHA1" +then + skip_all='skipping sha1 collision tests, DC_SHA1 not set' + test_done +fi + +test_expect_success 'test-sha1 detects shattered pdf' ' + test_must_fail test-sha1 <"$TEST_DATA/shattered-1.pdf" 2>err && + test_i18ngrep collision err && + grep 38762cf7f55934b34d179ae6a4c80cadccbb7f0a err +' + +test_done diff --git a/t/t0013/shattered-1.pdf b/t/t0013/shattered-1.pdf Binary files differnew file mode 100644 index 0000000000..ba9aaa145c --- /dev/null +++ b/t/t0013/shattered-1.pdf diff --git a/t/t0301-credential-cache.sh b/t/t0301-credential-cache.sh index 82c8411210..fd92533acf 100755 --- a/t/t0301-credential-cache.sh +++ b/t/t0301-credential-cache.sh @@ -12,7 +12,100 @@ test -z "$NO_UNIX_SOCKETS" || { # don't leave a stale daemon running trap 'code=$?; git credential-cache exit; (exit $code); die' EXIT +# test that the daemon works with no special setup helper_test cache + +test_expect_success 'socket defaults to ~/.cache/git/credential/socket' ' + test_when_finished " + git credential-cache exit && + rmdir -p .cache/git/credential/ + " && + test_path_is_missing "$HOME/.git-credential-cache" && + test -S "$HOME/.cache/git/credential/socket" +' + +XDG_CACHE_HOME="$HOME/xdg" +export XDG_CACHE_HOME +# test behavior when XDG_CACHE_HOME is set +helper_test cache + +test_expect_success "use custom XDG_CACHE_HOME if set and default sockets are not created" ' + test_when_finished "git credential-cache exit" && + test -S "$XDG_CACHE_HOME/git/credential/socket" && + test_path_is_missing "$HOME/.git-credential-cache/socket" && + test_path_is_missing "$HOME/.cache/git/credential/socket" +' +unset XDG_CACHE_HOME + +test_expect_success 'credential-cache --socket option overrides default location' ' + test_when_finished " + git credential-cache exit --socket \"\$HOME/dir/socket\" && + rmdir \"\$HOME/dir\" + " && + check approve "cache --socket \"\$HOME/dir/socket\"" <<-\EOF && + protocol=https + host=example.com + username=store-user + password=store-pass + EOF + test -S "$HOME/dir/socket" +' + +test_expect_success "use custom XDG_CACHE_HOME even if xdg socket exists" ' + test_when_finished " + git credential-cache exit && + sane_unset XDG_CACHE_HOME + " && + check approve cache <<-\EOF && + protocol=https + host=example.com + username=store-user + password=store-pass + EOF + test -S "$HOME/.cache/git/credential/socket" && + XDG_CACHE_HOME="$HOME/xdg" && + export XDG_CACHE_HOME && + check approve cache <<-\EOF && + protocol=https + host=example.com + username=store-user + password=store-pass + EOF + test -S "$XDG_CACHE_HOME/git/credential/socket" +' + +test_expect_success 'use user socket if user directory exists' ' + test_when_finished " + git credential-cache exit && + rmdir \"\$HOME/.git-credential-cache/\" + " && + mkdir -p -m 700 "$HOME/.git-credential-cache/" && + check approve cache <<-\EOF && + protocol=https + host=example.com + username=store-user + password=store-pass + EOF + test -S "$HOME/.git-credential-cache/socket" +' + +test_expect_success SYMLINKS 'use user socket if user directory is a symlink to a directory' ' + test_when_finished " + git credential-cache exit && + rmdir \"\$HOME/dir/\" && + rm \"\$HOME/.git-credential-cache\" + " && + mkdir -p -m 700 "$HOME/dir/" && + ln -s "$HOME/dir" "$HOME/.git-credential-cache" && + check approve cache <<-\EOF && + protocol=https + host=example.com + username=store-user + password=store-pass + EOF + test -S "$HOME/.git-credential-cache/socket" +' + helper_test_timeout cache --timeout=1 # we can't rely on our "trap" above working after test_done, diff --git a/t/t1007-hash-object.sh b/t/t1007-hash-object.sh index c5245c5cb4..532682f51c 100755 --- a/t/t1007-hash-object.sh +++ b/t/t1007-hash-object.sh @@ -134,6 +134,16 @@ test_expect_success 'gitattributes also work in a subdirectory' ' ) ' +test_expect_success '--path works in a subdirectory' ' + ( + cd subdir && + path1_sha=$(git hash-object --path=../file1 ../file0) && + path0_sha=$(git hash-object --path=../file0 ../file1) && + test "$file0_sha" = "$path0_sha" && + test "$file1_sha" = "$path1_sha" + ) +' + test_expect_success 'check that --no-filters option works' ' nofilters_file1=$(git hash-object --no-filters file1) && test "$file0_sha" = "$nofilters_file1" && diff --git a/t/t1013-read-tree-submodule.sh b/t/t1013-read-tree-submodule.sh index 20526aed34..de1ba02dc5 100755 --- a/t/t1013-read-tree-submodule.sh +++ b/t/t1013-read-tree-submodule.sh @@ -5,6 +5,14 @@ test_description='read-tree can handle submodules' . ./test-lib.sh . "$TEST_DIRECTORY"/lib-submodule-update.sh +KNOWN_FAILURE_SUBMODULE_RECURSIVE_NESTED=1 +KNOWN_FAILURE_DIRECTORY_SUBMODULE_CONFLICTS=1 +KNOWN_FAILURE_SUBMODULE_OVERWRITE_IGNORED_UNTRACKED=1 + +test_submodule_switch_recursing "git read-tree --recurse-submodules -u -m" + +test_submodule_forced_switch_recursing "git read-tree --recurse-submodules -u --reset" + test_submodule_switch "git read-tree -u -m" test_submodule_forced_switch "git read-tree -u --reset" diff --git a/t/t1060-object-corruption.sh b/t/t1060-object-corruption.sh index 3f8705139d..ac1f189fd2 100755 --- a/t/t1060-object-corruption.sh +++ b/t/t1060-object-corruption.sh @@ -21,6 +21,14 @@ test_expect_success 'setup corrupt repo' ' cd bit-error && test_commit content && corrupt_byte HEAD:content.t 10 + ) && + git init no-bit-error && + ( + # distinct commit from bit-error, but containing a + # non-corrupted version of the same blob + cd no-bit-error && + test_tick && + test_commit content ) ' @@ -53,6 +61,13 @@ test_expect_success 'streaming a corrupt blob fails' ' ) ' +test_expect_success 'getting type of a corrupt blob fails' ' + ( + cd bit-error && + test_must_fail git cat-file -s HEAD:content.t + ) +' + test_expect_success 'read-tree -u detects bit-errors in blobs' ' ( cd bit-error && @@ -101,4 +116,13 @@ test_expect_failure 'clone --local detects misnamed objects' ' test_must_fail git clone --local misnamed misnamed-checkout ' +test_expect_success 'fetch into corrupted repo with index-pack' ' + ( + cd bit-error && + test_must_fail git -c transfer.unpackLimit=1 \ + fetch ../no-bit-error 2>stderr && + test_i18ngrep ! -i collision stderr + ) +' + test_done diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh index ea371020fa..afcca0d52c 100755 --- a/t/t1300-repo-config.sh +++ b/t/t1300-repo-config.sh @@ -1239,6 +1239,111 @@ test_expect_success 'urlmatch' ' test_cmp expect actual ' +test_expect_success 'urlmatch favors more specific URLs' ' + cat >.git/config <<-\EOF && + [http "https://example.com/"] + cookieFile = /tmp/root.txt + [http "https://example.com/subdirectory"] + cookieFile = /tmp/subdirectory.txt + [http "https://user@example.com/"] + cookieFile = /tmp/user.txt + [http "https://averylonguser@example.com/"] + cookieFile = /tmp/averylonguser.txt + [http "https://preceding.example.com"] + cookieFile = /tmp/preceding.txt + [http "https://*.example.com"] + cookieFile = /tmp/wildcard.txt + [http "https://*.example.com/wildcardwithsubdomain"] + cookieFile = /tmp/wildcardwithsubdomain.txt + [http "https://trailing.example.com"] + cookieFile = /tmp/trailing.txt + [http "https://user@*.example.com/"] + cookieFile = /tmp/wildcardwithuser.txt + [http "https://sub.example.com/"] + cookieFile = /tmp/sub.txt + EOF + + echo http.cookiefile /tmp/root.txt >expect && + git config --get-urlmatch HTTP https://example.com >actual && + test_cmp expect actual && + + echo http.cookiefile /tmp/subdirectory.txt >expect && + git config --get-urlmatch HTTP https://example.com/subdirectory >actual && + test_cmp expect actual && + + echo http.cookiefile /tmp/subdirectory.txt >expect && + git config --get-urlmatch HTTP https://example.com/subdirectory/nested >actual && + test_cmp expect actual && + + echo http.cookiefile /tmp/user.txt >expect && + git config --get-urlmatch HTTP https://user@example.com/ >actual && + test_cmp expect actual && + + echo http.cookiefile /tmp/subdirectory.txt >expect && + git config --get-urlmatch HTTP https://averylonguser@example.com/subdirectory >actual && + test_cmp expect actual && + + echo http.cookiefile /tmp/preceding.txt >expect && + git config --get-urlmatch HTTP https://preceding.example.com >actual && + test_cmp expect actual && + + echo http.cookiefile /tmp/wildcard.txt >expect && + git config --get-urlmatch HTTP https://wildcard.example.com >actual && + test_cmp expect actual && + + echo http.cookiefile /tmp/sub.txt >expect && + git config --get-urlmatch HTTP https://sub.example.com/wildcardwithsubdomain >actual && + test_cmp expect actual && + + echo http.cookiefile /tmp/trailing.txt >expect && + git config --get-urlmatch HTTP https://trailing.example.com >actual && + test_cmp expect actual && + + echo http.cookiefile /tmp/sub.txt >expect && + git config --get-urlmatch HTTP https://user@sub.example.com >actual && + test_cmp expect actual +' + +test_expect_success 'urlmatch with wildcard' ' + cat >.git/config <<-\EOF && + [http] + sslVerify + [http "https://*.example.com"] + sslVerify = false + cookieFile = /tmp/cookie.txt + EOF + + test_expect_code 1 git config --bool --get-urlmatch doesnt.exist https://good.example.com >actual && + test_must_be_empty actual && + + echo true >expect && + git config --bool --get-urlmatch http.SSLverify https://example.com >actual && + test_cmp expect actual && + + echo true >expect && + git config --bool --get-urlmatch http.SSLverify https://good-example.com >actual && + test_cmp expect actual && + + echo true >expect && + git config --bool --get-urlmatch http.sslverify https://deep.nested.example.com >actual && + test_cmp expect actual && + + echo false >expect && + git config --bool --get-urlmatch http.sslverify https://good.example.com >actual && + test_cmp expect actual && + + { + echo http.cookiefile /tmp/cookie.txt && + echo http.sslverify false + } >expect && + git config --get-urlmatch HTTP https://good.example.com >actual && + test_cmp expect actual && + + echo http.sslverify >expect && + git config --get-urlmatch HTTP https://more.example.com.au >actual && + test_cmp expect actual +' + # good section hygiene test_expect_failure 'unsetting the last key in a section removes header' ' cat >.git/config <<-\EOF && diff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh index 9ba2ba11c3..e833939320 100755 --- a/t/t1305-config-include.sh +++ b/t/t1305-config-include.sh @@ -102,7 +102,7 @@ test_expect_success 'config modification does not affect includes' ' test_expect_success 'missing include files are ignored' ' cat >.gitconfig <<-\EOF && - [include]path = foo + [include]path = non-existent [test]value = yes EOF echo yes >expect && @@ -152,6 +152,62 @@ test_expect_success 'relative includes from stdin line fail' ' test_must_fail git config --file - test.one ' +test_expect_success 'conditional include, both unanchored' ' + git init foo && + ( + cd foo && + echo "[includeIf \"gitdir:foo/\"]path=bar" >>.git/config && + echo "[test]one=1" >.git/bar && + echo 1 >expect && + git config test.one >actual && + test_cmp expect actual + ) +' + +test_expect_success 'conditional include, $HOME expansion' ' + ( + cd foo && + echo "[includeIf \"gitdir:~/foo/\"]path=bar2" >>.git/config && + echo "[test]two=2" >.git/bar2 && + echo 2 >expect && + git config test.two >actual && + test_cmp expect actual + ) +' + +test_expect_success 'conditional include, full pattern' ' + ( + cd foo && + echo "[includeIf \"gitdir:**/foo/**\"]path=bar3" >>.git/config && + echo "[test]three=3" >.git/bar3 && + echo 3 >expect && + git config test.three >actual && + test_cmp expect actual + ) +' + +test_expect_success 'conditional include, relative path' ' + echo "[includeIf \"gitdir:./foo/.git\"]path=bar4" >>.gitconfig && + echo "[test]four=4" >bar4 && + ( + cd foo && + echo 4 >expect && + git config test.four >actual && + test_cmp expect actual + ) +' + +test_expect_success 'conditional include, both unanchored, icase' ' + ( + cd foo && + echo "[includeIf \"gitdir/i:FOO/\"]path=bar5" >>.git/config && + echo "[test]five=5" >.git/bar5 && + echo 5 >expect && + git config test.five >actual && + test_cmp expect actual + ) +' + test_expect_success 'include cycles are detected' ' cat >.gitconfig <<-\EOF && [test]value = gitconfig diff --git a/t/t1309-early-config.sh b/t/t1309-early-config.sh new file mode 100755 index 0000000000..b97357b8ab --- /dev/null +++ b/t/t1309-early-config.sh @@ -0,0 +1,74 @@ +#!/bin/sh + +test_description='Test read_early_config()' + +. ./test-lib.sh + +test_expect_success 'read early config' ' + test_config early.config correct && + test-config read_early_config early.config >output && + test correct = "$(cat output)" +' + +test_expect_success 'in a sub-directory' ' + test_config early.config sub && + mkdir -p sub && + ( + cd sub && + test-config read_early_config early.config + ) >output && + test sub = "$(cat output)" +' + +test_expect_success 'ceiling' ' + test_config early.config ceiling && + mkdir -p sub && + ( + GIT_CEILING_DIRECTORIES="$PWD" && + export GIT_CEILING_DIRECTORIES && + cd sub && + test-config read_early_config early.config + ) >output && + test -z "$(cat output)" +' + +test_expect_success 'ceiling #2' ' + mkdir -p xdg/git && + git config -f xdg/git/config early.config xdg && + test_config early.config ceiling && + mkdir -p sub && + ( + XDG_CONFIG_HOME="$PWD"/xdg && + GIT_CEILING_DIRECTORIES="$PWD" && + export GIT_CEILING_DIRECTORIES XDG_CONFIG_HOME && + cd sub && + test-config read_early_config early.config + ) >output && + test xdg = "$(cat output)" +' + +test_with_config () { + rm -rf throwaway && + git init throwaway && + ( + cd throwaway && + echo "$*" >.git/config && + test-config read_early_config early.config + ) +} + +test_expect_success 'ignore .git/ with incompatible repository version' ' + test_with_config "[core]repositoryformatversion = 999999" 2>err && + grep "warning:.* Expected git repo version <= [1-9]" err +' + +test_expect_failure 'ignore .git/ with invalid repository version' ' + test_with_config "[core]repositoryformatversion = invalid" +' + + +test_expect_failure 'ignore .git/ with invalid config' ' + test_with_config "[" +' + +test_done diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index b0ffc0b573..a23cd7b576 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -40,7 +40,7 @@ test_expect_success \ "git update-ref $m $A && test $A"' = $(cat .git/'"$m"')' test_expect_success \ - "create $m" \ + "create $m with oldvalue verification" \ "git update-ref $m $B $A && test $B"' = $(cat .git/'"$m"')' test_expect_success "fail to delete $m with stale ref" ' @@ -48,31 +48,31 @@ test_expect_success "fail to delete $m with stale ref" ' test $B = "$(cat .git/$m)" ' test_expect_success "delete $m" ' + test_when_finished "rm -f .git/$m" && git update-ref -d $m $B && - ! test -f .git/$m + test_path_is_missing .git/$m ' -rm -f .git/$m -test_expect_success "delete $m without oldvalue verification" " +test_expect_success "delete $m without oldvalue verification" ' + test_when_finished "rm -f .git/$m" && git update-ref $m $A && - test $A = \$(cat .git/$m) && + test $A = $(cat .git/$m) && git update-ref -d $m && - ! test -f .git/$m -" -rm -f .git/$m + test_path_is_missing .git/$m +' -test_expect_success \ - "fail to create $n" \ - "touch .git/$n_dir && - test_must_fail git update-ref $n $A >out 2>err" -rm -f .git/$n_dir out err +test_expect_success "fail to create $n" ' + test_when_finished "rm -f .git/$n_dir" && + touch .git/$n_dir && + test_must_fail git update-ref $n $A +' test_expect_success \ "create $m (by HEAD)" \ "git update-ref HEAD $A && test $A"' = $(cat .git/'"$m"')' test_expect_success \ - "create $m (by HEAD)" \ + "create $m (by HEAD) with oldvalue verification" \ "git update-ref HEAD $B $A && test $B"' = $(cat .git/'"$m"')' test_expect_success "fail to delete $m (by HEAD) with stale ref" ' @@ -80,10 +80,28 @@ test_expect_success "fail to delete $m (by HEAD) with stale ref" ' test $B = $(cat .git/$m) ' test_expect_success "delete $m (by HEAD)" ' + test_when_finished "rm -f .git/$m" && git update-ref -d HEAD $B && - ! test -f .git/$m + test_path_is_missing .git/$m +' + +test_expect_success "deleting current branch adds message to HEAD's log" ' + test_when_finished "rm -f .git/$m" && + git update-ref $m $A && + git symbolic-ref HEAD $m && + git update-ref -m delete-$m -d $m && + test_path_is_missing .git/$m && + grep "delete-$m$" .git/logs/HEAD +' + +test_expect_success "deleting by HEAD adds message to HEAD's log" ' + test_when_finished "rm -f .git/$m" && + git update-ref $m $A && + git symbolic-ref HEAD $m && + git update-ref -m delete-by-head -d HEAD && + test_path_is_missing .git/$m && + grep "delete-by-head$" .git/logs/HEAD ' -rm -f .git/$m test_expect_success 'update-ref does not create reflogs by default' ' test_when_finished "git update-ref -d $outside" && @@ -170,28 +188,29 @@ test_expect_success \ "git update-ref HEAD $B $A && test $B"' = $(cat .git/'"$m"')' test_expect_success "delete $m (by HEAD) should remove both packed and loose $m" ' + test_when_finished "rm -f .git/$m" && git update-ref -d HEAD $B && ! grep "$m" .git/packed-refs && - ! test -f .git/$m + test_path_is_missing .git/$m ' -rm -f .git/$m cp -f .git/HEAD .git/HEAD.orig test_expect_success "delete symref without dereference" ' + test_when_finished "cp -f .git/HEAD.orig .git/HEAD" && git update-ref --no-deref -d HEAD && - ! test -f .git/HEAD + test_path_is_missing .git/HEAD ' -cp -f .git/HEAD.orig .git/HEAD test_expect_success "delete symref without dereference when the referred ref is packed" ' + test_when_finished "cp -f .git/HEAD.orig .git/HEAD" && echo foo >foo.c && git add foo.c && git commit -m foo && git pack-refs --all && git update-ref --no-deref -d HEAD && - ! test -f .git/HEAD + test_path_is_missing .git/HEAD ' -cp -f .git/HEAD.orig .git/HEAD + git update-ref -d $m test_expect_success 'update-ref -d is not confused by self-reference' ' @@ -223,10 +242,10 @@ test_expect_success 'update-ref --no-deref -d can delete reference to bad ref' ' test_expect_success '(not) create HEAD with old sha1' " test_must_fail git update-ref HEAD $A $B " -test_expect_success "(not) prior created .git/$m" " - ! test -f .git/$m -" -rm -f .git/$m +test_expect_success "(not) prior created .git/$m" ' + test_when_finished "rm -f .git/$m" && + test_path_is_missing .git/$m +' test_expect_success \ "create HEAD" \ @@ -234,56 +253,84 @@ test_expect_success \ test_expect_success '(not) change HEAD with wrong SHA1' " test_must_fail git update-ref HEAD $B $Z " -test_expect_success "(not) changed .git/$m" " - ! test $B"' = $(cat .git/'"$m"') +test_expect_success "(not) changed .git/$m" ' + test_when_finished "rm -f .git/$m" && + ! test $B = $(cat .git/$m) ' -rm -f .git/$m rm -f .git/logs/refs/heads/master test_expect_success \ "create $m (logged by touch)" \ - 'GIT_COMMITTER_DATE="2005-05-26 23:30" \ + 'test_config core.logAllRefUpdates false && + GIT_COMMITTER_DATE="2005-05-26 23:30" \ git update-ref --create-reflog HEAD '"$A"' -m "Initial Creation" && test '"$A"' = $(cat .git/'"$m"')' test_expect_success \ "update $m (logged by touch)" \ - 'GIT_COMMITTER_DATE="2005-05-26 23:31" \ + 'test_config core.logAllRefUpdates false && + GIT_COMMITTER_DATE="2005-05-26 23:31" \ git update-ref HEAD'" $B $A "'-m "Switch" && test '"$B"' = $(cat .git/'"$m"')' test_expect_success \ "set $m (logged by touch)" \ - 'GIT_COMMITTER_DATE="2005-05-26 23:41" \ + 'test_config core.logAllRefUpdates false && + GIT_COMMITTER_DATE="2005-05-26 23:41" \ git update-ref HEAD'" $A && test $A"' = $(cat .git/'"$m"')' +test_expect_success "empty directory removal" ' + git branch d1/d2/r1 HEAD && + git branch d1/r2 HEAD && + test_path_is_file .git/refs/heads/d1/d2/r1 && + test_path_is_file .git/logs/refs/heads/d1/d2/r1 && + git branch -d d1/d2/r1 && + test_path_is_missing .git/refs/heads/d1/d2 && + test_path_is_missing .git/logs/refs/heads/d1/d2 && + test_path_is_file .git/refs/heads/d1/r2 && + test_path_is_file .git/logs/refs/heads/d1/r2 +' + +test_expect_success "symref empty directory removal" ' + git branch e1/e2/r1 HEAD && + git branch e1/r2 HEAD && + git checkout e1/e2/r1 && + test_when_finished "git checkout master" && + test_path_is_file .git/refs/heads/e1/e2/r1 && + test_path_is_file .git/logs/refs/heads/e1/e2/r1 && + git update-ref -d HEAD && + test_path_is_missing .git/refs/heads/e1/e2 && + test_path_is_missing .git/logs/refs/heads/e1/e2 && + test_path_is_file .git/refs/heads/e1/r2 && + test_path_is_file .git/logs/refs/heads/e1/r2 && + test_path_is_file .git/logs/HEAD +' + cat >expect <<EOF $Z $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 Initial Creation $A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150260 +0000 Switch $B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150860 +0000 EOF -test_expect_success \ - "verifying $m's log" \ - "test_cmp expect .git/logs/$m" -rm -rf .git/$m .git/logs expect - -test_expect_success \ - 'enable core.logAllRefUpdates' \ - 'git config core.logAllRefUpdates true && - test true = $(git config --bool --get core.logAllRefUpdates)' +test_expect_success "verifying $m's log (logged by touch)" ' + test_when_finished "rm -rf .git/$m .git/logs expect" && + test_cmp expect .git/logs/$m +' test_expect_success \ "create $m (logged by config)" \ - 'GIT_COMMITTER_DATE="2005-05-26 23:32" \ + 'test_config core.logAllRefUpdates true && + GIT_COMMITTER_DATE="2005-05-26 23:32" \ git update-ref HEAD'" $A "'-m "Initial Creation" && test '"$A"' = $(cat .git/'"$m"')' test_expect_success \ "update $m (logged by config)" \ - 'GIT_COMMITTER_DATE="2005-05-26 23:33" \ + 'test_config core.logAllRefUpdates true && + GIT_COMMITTER_DATE="2005-05-26 23:33" \ git update-ref HEAD'" $B $A "'-m "Switch" && test '"$B"' = $(cat .git/'"$m"')' test_expect_success \ "set $m (logged by config)" \ - 'GIT_COMMITTER_DATE="2005-05-26 23:43" \ + 'test_config core.logAllRefUpdates true && + GIT_COMMITTER_DATE="2005-05-26 23:43" \ git update-ref HEAD '"$A && test $A"' = $(cat .git/'"$m"')' @@ -293,9 +340,9 @@ $A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150380 +0000 Switch $B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 +0000 EOF test_expect_success \ - "verifying $m's log" \ - 'test_cmp expect .git/logs/$m' -rm -f .git/$m .git/logs/$m expect + "verifying $m's log (logged by config)" \ + 'test_when_finished "rm -f .git/$m .git/logs/$m expect" && + test_cmp expect .git/logs/$m' git update-ref $m $D cat >.git/logs/$m <<EOF @@ -311,55 +358,55 @@ gd="Thu, 26 May 2005 18:33:00 -0500" ld="Thu, 26 May 2005 18:43:00 -0500" test_expect_success \ 'Query "master@{May 25 2005}" (before history)' \ - 'rm -f o e && + 'test_when_finished "rm -f o e" && git rev-parse --verify "master@{May 25 2005}" >o 2>e && test '"$C"' = $(cat o) && test "warning: Log for '\'master\'' only goes back to $ed." = "$(cat e)"' test_expect_success \ "Query master@{2005-05-25} (before history)" \ - 'rm -f o e && + 'test_when_finished "rm -f o e" && git rev-parse --verify master@{2005-05-25} >o 2>e && test '"$C"' = $(cat o) && echo test "warning: Log for '\'master\'' only goes back to $ed." = "$(cat e)"' test_expect_success \ 'Query "master@{May 26 2005 23:31:59}" (1 second before history)' \ - 'rm -f o e && + 'test_when_finished "rm -f o e" && git rev-parse --verify "master@{May 26 2005 23:31:59}" >o 2>e && test '"$C"' = $(cat o) && test "warning: Log for '\''master'\'' only goes back to $ed." = "$(cat e)"' test_expect_success \ 'Query "master@{May 26 2005 23:32:00}" (exactly history start)' \ - 'rm -f o e && + 'test_when_finished "rm -f o e" && git rev-parse --verify "master@{May 26 2005 23:32:00}" >o 2>e && test '"$C"' = $(cat o) && test "" = "$(cat e)"' test_expect_success \ 'Query "master@{May 26 2005 23:32:30}" (first non-creation change)' \ - 'rm -f o e && + 'test_when_finished "rm -f o e" && git rev-parse --verify "master@{May 26 2005 23:32:30}" >o 2>e && test '"$A"' = $(cat o) && test "" = "$(cat e)"' test_expect_success \ 'Query "master@{2005-05-26 23:33:01}" (middle of history with gap)' \ - 'rm -f o e && + 'test_when_finished "rm -f o e" && git rev-parse --verify "master@{2005-05-26 23:33:01}" >o 2>e && test '"$B"' = $(cat o) && test "warning: Log for ref '"$m has gap after $gd"'." = "$(cat e)"' test_expect_success \ 'Query "master@{2005-05-26 23:38:00}" (middle of history)' \ - 'rm -f o e && + 'test_when_finished "rm -f o e" && git rev-parse --verify "master@{2005-05-26 23:38:00}" >o 2>e && test '"$Z"' = $(cat o) && test "" = "$(cat e)"' test_expect_success \ 'Query "master@{2005-05-26 23:43:00}" (exact end of history)' \ - 'rm -f o e && + 'test_when_finished "rm -f o e" && git rev-parse --verify "master@{2005-05-26 23:43:00}" >o 2>e && test '"$E"' = $(cat o) && test "" = "$(cat e)"' test_expect_success \ 'Query "master@{2005-05-28}" (past end of history)' \ - 'rm -f o e && + 'test_when_finished "rm -f o e" && git rev-parse --verify "master@{2005-05-28}" >o 2>e && test '"$D"' = $(cat o) && test "warning: Log for ref '"$m unexpectedly ended on $ld"'." = "$(cat e)"' @@ -369,7 +416,8 @@ rm -f .git/$m .git/logs/$m expect test_expect_success \ 'creating initial files' \ - 'echo TEST >F && + 'test_when_finished rm -f M && + echo TEST >F && git add F && GIT_AUTHOR_DATE="2005-05-26 23:30" \ GIT_COMMITTER_DATE="2005-05-26 23:30" git commit -m add -a && @@ -387,8 +435,7 @@ test_expect_success \ echo $h_TEST >.git/MERGE_HEAD && GIT_AUTHOR_DATE="2005-05-26 23:45" \ GIT_COMMITTER_DATE="2005-05-26 23:45" git commit -F M && - h_MERGED=$(git rev-parse --verify HEAD) && - rm -f M' + h_MERGED=$(git rev-parse --verify HEAD)' cat >expect <<EOF $Z $h_TEST $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 commit (initial): add diff --git a/t/t1405-main-ref-store.sh b/t/t1405-main-ref-store.sh new file mode 100755 index 0000000000..490521f8cb --- /dev/null +++ b/t/t1405-main-ref-store.sh @@ -0,0 +1,129 @@ +#!/bin/sh + +test_description='test main ref store api' + +. ./test-lib.sh + +RUN="test-ref-store main" + +test_expect_success 'pack_refs(PACK_REFS_ALL | PACK_REFS_PRUNE)' ' + test_commit one && + N=`find .git/refs -type f | wc -l` && + test "$N" != 0 && + $RUN pack-refs 3 && + N=`find .git/refs -type f | wc -l` +' + +test_expect_success 'peel_ref(new-tag)' ' + git rev-parse HEAD >expected && + git tag -a -m new-tag new-tag HEAD && + $RUN peel-ref refs/tags/new-tag >actual && + test_cmp expected actual +' + +test_expect_success 'create_symref(FOO, refs/heads/master)' ' + $RUN create-symref FOO refs/heads/master nothing && + echo refs/heads/master >expected && + git symbolic-ref FOO >actual && + test_cmp expected actual +' + +test_expect_success 'delete_refs(FOO, refs/tags/new-tag)' ' + git rev-parse FOO -- && + git rev-parse refs/tags/new-tag -- && + $RUN delete-refs 0 FOO refs/tags/new-tag && + test_must_fail git rev-parse FOO -- && + test_must_fail git rev-parse refs/tags/new-tag -- +' + +test_expect_success 'rename_refs(master, new-master)' ' + git rev-parse master >expected && + $RUN rename-ref refs/heads/master refs/heads/new-master && + git rev-parse new-master >actual && + test_cmp expected actual && + test_commit recreate-master +' + +test_expect_success 'for_each_ref(refs/heads/)' ' + $RUN for-each-ref refs/heads/ | cut -c 42- >actual && + cat >expected <<-\EOF && + master 0x0 + new-master 0x0 + EOF + test_cmp expected actual +' + +test_expect_success 'for_each_ref() is sorted' ' + $RUN for-each-ref refs/heads/ | cut -c 42- >actual && + sort actual > expected && + test_cmp expected actual +' + +test_expect_success 'resolve_ref(new-master)' ' + SHA1=`git rev-parse new-master` && + echo "$SHA1 refs/heads/new-master 0x0" >expected && + $RUN resolve-ref refs/heads/new-master 0 >actual && + test_cmp expected actual +' + +test_expect_success 'verify_ref(new-master)' ' + $RUN verify-ref refs/heads/new-master +' + +test_expect_success 'for_each_reflog()' ' + $RUN for-each-reflog | sort | cut -c 42- >actual && + cat >expected <<-\EOF && + HEAD 0x1 + refs/heads/master 0x0 + refs/heads/new-master 0x0 + EOF + test_cmp expected actual +' + +test_expect_success 'for_each_reflog_ent()' ' + $RUN for-each-reflog-ent HEAD >actual && + head -n1 actual | grep one && + tail -n2 actual | head -n1 | grep recreate-master +' + +test_expect_success 'for_each_reflog_ent_reverse()' ' + $RUN for-each-reflog-ent-reverse HEAD >actual && + head -n1 actual | grep recreate-master && + tail -n2 actual | head -n1 | grep one +' + +test_expect_success 'reflog_exists(HEAD)' ' + $RUN reflog-exists HEAD +' + +test_expect_success 'delete_reflog(HEAD)' ' + $RUN delete-reflog HEAD && + ! test -f .git/logs/HEAD +' + +test_expect_success 'create-reflog(HEAD)' ' + $RUN create-reflog HEAD 1 && + test -f .git/logs/HEAD +' + +test_expect_success 'delete_ref(refs/heads/foo)' ' + git checkout -b foo && + FOO_SHA1=`git rev-parse foo` && + git checkout --detach && + test_commit bar-commit && + git checkout -b bar && + BAR_SHA1=`git rev-parse bar` && + $RUN update-ref updating refs/heads/foo $BAR_SHA1 $FOO_SHA1 0 && + echo $BAR_SHA1 >expected && + git rev-parse refs/heads/foo >actual && + test_cmp expected actual +' + +test_expect_success 'delete_ref(refs/heads/foo)' ' + SHA1=`git rev-parse foo` && + git checkout --detach && + $RUN delete-ref msg refs/heads/foo $SHA1 0 && + test_must_fail git rev-parse refs/heads/foo -- +' + +test_done diff --git a/t/t1406-submodule-ref-store.sh b/t/t1406-submodule-ref-store.sh new file mode 100755 index 0000000000..13b5454c56 --- /dev/null +++ b/t/t1406-submodule-ref-store.sh @@ -0,0 +1,101 @@ +#!/bin/sh + +test_description='test submodule ref store api' + +. ./test-lib.sh + +RUN="test-ref-store submodule:sub" + +test_expect_success 'setup' ' + git init sub && + ( + cd sub && + test_commit first && + git checkout -b new-master + ) +' + +test_expect_success 'pack_refs() not allowed' ' + test_must_fail $RUN pack-refs 3 +' + +test_expect_success 'peel_ref(new-tag)' ' + git -C sub rev-parse HEAD >expected && + git -C sub tag -a -m new-tag new-tag HEAD && + $RUN peel-ref refs/tags/new-tag >actual && + test_cmp expected actual +' + +test_expect_success 'create_symref() not allowed' ' + test_must_fail $RUN create-symref FOO refs/heads/master nothing +' + +test_expect_success 'delete_refs() not allowed' ' + test_must_fail $RUN delete-refs 0 FOO refs/tags/new-tag +' + +test_expect_success 'rename_refs() not allowed' ' + test_must_fail $RUN rename-ref refs/heads/master refs/heads/new-master +' + +test_expect_success 'for_each_ref(refs/heads/)' ' + $RUN for-each-ref refs/heads/ | cut -c 42- >actual && + cat >expected <<-\EOF && + master 0x0 + new-master 0x0 + EOF + test_cmp expected actual +' + +test_expect_success 'for_each_ref() is sorted' ' + $RUN for-each-ref refs/heads/ | cut -c 42- >actual && + sort actual > expected && + test_cmp expected actual +' + +test_expect_success 'resolve_ref(master)' ' + SHA1=`git -C sub rev-parse master` && + echo "$SHA1 refs/heads/master 0x0" >expected && + $RUN resolve-ref refs/heads/master 0 >actual && + test_cmp expected actual +' + +test_expect_success 'verify_ref(new-master)' ' + $RUN verify-ref refs/heads/new-master +' + +test_expect_success 'for_each_reflog()' ' + $RUN for-each-reflog | sort | cut -c 42- >actual && + cat >expected <<-\EOF && + HEAD 0x1 + refs/heads/master 0x0 + refs/heads/new-master 0x0 + EOF + test_cmp expected actual +' + +test_expect_success 'for_each_reflog_ent()' ' + $RUN for-each-reflog-ent HEAD >actual && cat actual && + head -n1 actual | grep first && + tail -n2 actual | head -n1 | grep master.to.new +' + +test_expect_success 'for_each_reflog_ent_reverse()' ' + $RUN for-each-reflog-ent-reverse HEAD >actual && + head -n1 actual | grep master.to.new && + tail -n2 actual | head -n1 | grep first +' + +test_expect_success 'reflog_exists(HEAD)' ' + $RUN reflog-exists HEAD +' + +test_expect_success 'delete_reflog() not allowed' ' + test_must_fail $RUN delete-reflog HEAD +' + +test_expect_success 'create-reflog() not allowed' ' + test_must_fail $RUN create-reflog HEAD 1 +' + +test_done diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh index 038e24c401..03d3c7f6d6 100755 --- a/t/t1500-rev-parse.sh +++ b/t/t1500-rev-parse.sh @@ -3,7 +3,7 @@ test_description='test git rev-parse' . ./test-lib.sh -# usage: [options] label is-bare is-inside-git is-inside-work prefix git-dir +# usage: [options] label is-bare is-inside-git is-inside-work prefix git-dir absolute-git-dir test_rev_parse () { d= bare= @@ -29,7 +29,8 @@ test_rev_parse () { --is-inside-git-dir \ --is-inside-work-tree \ --show-prefix \ - --git-dir + --git-dir \ + --absolute-git-dir do test $# -eq 0 && break expect="$1" @@ -62,29 +63,71 @@ test_expect_success 'setup' ' cp -R .git repo.git ' -test_rev_parse toplevel false false true '' .git +test_rev_parse toplevel false false true '' .git "$ROOT/.git" -test_rev_parse -C .git .git/ false true false '' . -test_rev_parse -C .git/objects .git/objects/ false true false '' "$ROOT/.git" +test_rev_parse -C .git .git/ false true false '' . "$ROOT/.git" +test_rev_parse -C .git/objects .git/objects/ false true false '' "$ROOT/.git" "$ROOT/.git" -test_rev_parse -C sub/dir subdirectory false false true sub/dir/ "$ROOT/.git" +test_rev_parse -C sub/dir subdirectory false false true sub/dir/ "$ROOT/.git" "$ROOT/.git" test_rev_parse -b t 'core.bare = true' true false false test_rev_parse -b u 'core.bare undefined' false false true -test_rev_parse -C work -g ../.git -b f 'GIT_DIR=../.git, core.bare = false' false false true '' +test_rev_parse -C work -g ../.git -b f 'GIT_DIR=../.git, core.bare = false' false false true '' "../.git" "$ROOT/.git" test_rev_parse -C work -g ../.git -b t 'GIT_DIR=../.git, core.bare = true' true false false '' test_rev_parse -C work -g ../.git -b u 'GIT_DIR=../.git, core.bare undefined' false false true '' -test_rev_parse -C work -g ../repo.git -b f 'GIT_DIR=../repo.git, core.bare = false' false false true '' +test_rev_parse -C work -g ../repo.git -b f 'GIT_DIR=../repo.git, core.bare = false' false false true '' "../repo.git" "$ROOT/repo.git" test_rev_parse -C work -g ../repo.git -b t 'GIT_DIR=../repo.git, core.bare = true' true false false '' test_rev_parse -C work -g ../repo.git -b u 'GIT_DIR=../repo.git, core.bare undefined' false false true '' +test_expect_success 'git-common-dir from worktree root' ' + echo .git >expect && + git rev-parse --git-common-dir >actual && + test_cmp expect actual +' + +test_expect_success 'git-common-dir inside sub-dir' ' + mkdir -p path/to/child && + test_when_finished "rm -rf path" && + echo "$(git -C path/to/child rev-parse --show-cdup).git" >expect && + git -C path/to/child rev-parse --git-common-dir >actual && + test_cmp expect actual +' + +test_expect_success 'git-path from worktree root' ' + echo .git/objects >expect && + git rev-parse --git-path objects >actual && + test_cmp expect actual +' + +test_expect_success 'git-path inside sub-dir' ' + mkdir -p path/to/child && + test_when_finished "rm -rf path" && + echo "$(git -C path/to/child rev-parse --show-cdup).git/objects" >expect && + git -C path/to/child rev-parse --git-path objects >actual && + test_cmp expect actual +' + +test_expect_success 'showing the superproject correctly' ' + git rev-parse --show-superproject-working-tree >out && + test_must_be_empty out && + + test_create_repo super && + test_commit -C super test_commit && + test_create_repo sub && + test_commit -C sub test_commit && + git -C super submodule add ../sub dir/sub && + echo $(pwd)/super >expect && + git -C super/dir/sub rev-parse --show-superproject-working-tree >out && + test_cmp expect out +' + test_done diff --git a/t/t1507-rev-parse-upstream.sh b/t/t1507-rev-parse-upstream.sh index 46ef1f22dc..b23c4e3fab 100755 --- a/t/t1507-rev-parse-upstream.sh +++ b/t/t1507-rev-parse-upstream.sh @@ -46,11 +46,14 @@ error_message () { } test_expect_success '@{upstream} resolves to correct full name' ' - test refs/remotes/origin/master = "$(full_name @{upstream})" + test refs/remotes/origin/master = "$(full_name @{upstream})" && + test refs/remotes/origin/master = "$(full_name @{UPSTREAM})" && + test refs/remotes/origin/master = "$(full_name @{UpSTReam})" ' test_expect_success '@{u} resolves to correct full name' ' - test refs/remotes/origin/master = "$(full_name @{u})" + test refs/remotes/origin/master = "$(full_name @{u})" && + test refs/remotes/origin/master = "$(full_name @{U})" ' test_expect_success 'my-side@{upstream} resolves to correct full name' ' @@ -60,6 +63,8 @@ test_expect_success 'my-side@{upstream} resolves to correct full name' ' test_expect_success 'upstream of branch with @ in middle' ' full_name fun@ny@{u} >actual && echo refs/remotes/origin/side >expect && + test_cmp expect actual && + full_name fun@ny@{U} >actual && test_cmp expect actual ' @@ -96,12 +101,14 @@ test_expect_success 'not-tracking@{u} fails' ' test_expect_success '<branch>@{u}@{1} resolves correctly' ' test_commit 6 && (cd clone && git fetch) && - test 5 = $(commit_subject my-side@{u}@{1}) + test 5 = $(commit_subject my-side@{u}@{1}) && + test 5 = $(commit_subject my-side@{U}@{1}) ' test_expect_success '@{u} without specifying branch fails on a detached HEAD' ' git checkout HEAD^0 && - test_must_fail git rev-parse @{u} + test_must_fail git rev-parse @{u} && + test_must_fail git rev-parse @{U} ' test_expect_success 'checkout -b new my-side@{u} forks from the same' ' diff --git a/t/t1514-rev-parse-push.sh b/t/t1514-rev-parse-push.sh index 623a32aa6e..788cc91e45 100755 --- a/t/t1514-rev-parse-push.sh +++ b/t/t1514-rev-parse-push.sh @@ -24,12 +24,16 @@ test_expect_success 'setup' ' test_expect_success '@{push} with default=nothing' ' test_config push.default nothing && - test_must_fail git rev-parse master@{push} + test_must_fail git rev-parse master@{push} && + test_must_fail git rev-parse master@{PUSH} && + test_must_fail git rev-parse master@{PuSH} ' test_expect_success '@{push} with default=simple' ' test_config push.default simple && - resolve master@{push} refs/remotes/origin/master + resolve master@{push} refs/remotes/origin/master && + resolve master@{PUSH} refs/remotes/origin/master && + resolve master@{pUSh} refs/remotes/origin/master ' test_expect_success 'triangular @{push} fails with default=simple' ' diff --git a/t/t1700-split-index.sh b/t/t1700-split-index.sh index 292a0720fc..af3ec0da5a 100755 --- a/t/t1700-split-index.sh +++ b/t/t1700-split-index.sh @@ -8,6 +8,7 @@ test_description='split index mode tests' sane_unset GIT_TEST_SPLIT_INDEX test_expect_success 'enable split index' ' + git config splitIndex.maxPercentChange 100 && git update-index --split-index && test-dump-split-index .git/index >actual && indexversion=$(test-index-version <.git/index) && @@ -19,12 +20,12 @@ test_expect_success 'enable split index' ' own=8299b0bcd1ac364e5f1d7768efb62fa2da79a339 base=39d890139ee5356c7ef572216cebcd27aa41f9df fi && - cat >expect <<EOF && -own $own -base $base -replacements: -deletions: -EOF + cat >expect <<-EOF && + own $own + base $base + replacements: + deletions: + EOF test_cmp expect actual ' @@ -32,51 +33,51 @@ test_expect_success 'add one file' ' : >one && git update-index --add one && git ls-files --stage >ls-files.actual && - cat >ls-files.expect <<EOF && -100644 $EMPTY_BLOB 0 one -EOF + cat >ls-files.expect <<-EOF && + 100644 $EMPTY_BLOB 0 one + EOF test_cmp ls-files.expect ls-files.actual && test-dump-split-index .git/index | sed "/^own/d" >actual && - cat >expect <<EOF && -base $base -100644 $EMPTY_BLOB 0 one -replacements: -deletions: -EOF + cat >expect <<-EOF && + base $base + 100644 $EMPTY_BLOB 0 one + replacements: + deletions: + EOF test_cmp expect actual ' test_expect_success 'disable split index' ' git update-index --no-split-index && git ls-files --stage >ls-files.actual && - cat >ls-files.expect <<EOF && -100644 $EMPTY_BLOB 0 one -EOF + cat >ls-files.expect <<-EOF && + 100644 $EMPTY_BLOB 0 one + EOF test_cmp ls-files.expect ls-files.actual && BASE=$(test-dump-split-index .git/index | grep "^own" | sed "s/own/base/") && test-dump-split-index .git/index | sed "/^own/d" >actual && - cat >expect <<EOF && -not a split index -EOF + cat >expect <<-EOF && + not a split index + EOF test_cmp expect actual ' test_expect_success 'enable split index again, "one" now belongs to base index"' ' git update-index --split-index && git ls-files --stage >ls-files.actual && - cat >ls-files.expect <<EOF && -100644 $EMPTY_BLOB 0 one -EOF + cat >ls-files.expect <<-EOF && + 100644 $EMPTY_BLOB 0 one + EOF test_cmp ls-files.expect ls-files.actual && test-dump-split-index .git/index | sed "/^own/d" >actual && - cat >expect <<EOF && -$BASE -replacements: -deletions: -EOF + cat >expect <<-EOF && + $BASE + replacements: + deletions: + EOF test_cmp expect actual ' @@ -84,18 +85,18 @@ test_expect_success 'modify original file, base index untouched' ' echo modified >one && git update-index one && git ls-files --stage >ls-files.actual && - cat >ls-files.expect <<EOF && -100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0 one -EOF + cat >ls-files.expect <<-EOF && + 100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0 one + EOF test_cmp ls-files.expect ls-files.actual && test-dump-split-index .git/index | sed "/^own/d" >actual && - q_to_tab >expect <<EOF && -$BASE -100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q -replacements: 0 -deletions: -EOF + q_to_tab >expect <<-EOF && + $BASE + 100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q + replacements: 0 + deletions: + EOF test_cmp expect actual ' @@ -103,54 +104,54 @@ test_expect_success 'add another file, which stays index' ' : >two && git update-index --add two && git ls-files --stage >ls-files.actual && - cat >ls-files.expect <<EOF && -100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0 one -100644 $EMPTY_BLOB 0 two -EOF + cat >ls-files.expect <<-EOF && + 100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0 one + 100644 $EMPTY_BLOB 0 two + EOF test_cmp ls-files.expect ls-files.actual && test-dump-split-index .git/index | sed "/^own/d" >actual && - q_to_tab >expect <<EOF && -$BASE -100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q -100644 $EMPTY_BLOB 0 two -replacements: 0 -deletions: -EOF + q_to_tab >expect <<-EOF && + $BASE + 100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q + 100644 $EMPTY_BLOB 0 two + replacements: 0 + deletions: + EOF test_cmp expect actual ' test_expect_success 'remove file not in base index' ' git update-index --force-remove two && git ls-files --stage >ls-files.actual && - cat >ls-files.expect <<EOF && -100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0 one -EOF + cat >ls-files.expect <<-EOF && + 100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0 one + EOF test_cmp ls-files.expect ls-files.actual && test-dump-split-index .git/index | sed "/^own/d" >actual && - q_to_tab >expect <<EOF && -$BASE -100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q -replacements: 0 -deletions: -EOF + q_to_tab >expect <<-EOF && + $BASE + 100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q + replacements: 0 + deletions: + EOF test_cmp expect actual ' test_expect_success 'remove file in base index' ' git update-index --force-remove one && git ls-files --stage >ls-files.actual && - cat >ls-files.expect <<EOF && -EOF + cat >ls-files.expect <<-EOF && + EOF test_cmp ls-files.expect ls-files.actual && test-dump-split-index .git/index | sed "/^own/d" >actual && - cat >expect <<EOF && -$BASE -replacements: -deletions: 0 -EOF + cat >expect <<-EOF && + $BASE + replacements: + deletions: 0 + EOF test_cmp expect actual ' @@ -158,18 +159,18 @@ test_expect_success 'add original file back' ' : >one && git update-index --add one && git ls-files --stage >ls-files.actual && - cat >ls-files.expect <<EOF && -100644 $EMPTY_BLOB 0 one -EOF + cat >ls-files.expect <<-EOF && + 100644 $EMPTY_BLOB 0 one + EOF test_cmp ls-files.expect ls-files.actual && test-dump-split-index .git/index | sed "/^own/d" >actual && - cat >expect <<EOF && -$BASE -100644 $EMPTY_BLOB 0 one -replacements: -deletions: 0 -EOF + cat >expect <<-EOF && + $BASE + 100644 $EMPTY_BLOB 0 one + replacements: + deletions: 0 + EOF test_cmp expect actual ' @@ -177,27 +178,196 @@ test_expect_success 'add new file' ' : >two && git update-index --add two && git ls-files --stage >actual && - cat >expect <<EOF && -100644 $EMPTY_BLOB 0 one -100644 $EMPTY_BLOB 0 two -EOF + cat >expect <<-EOF && + 100644 $EMPTY_BLOB 0 one + 100644 $EMPTY_BLOB 0 two + EOF test_cmp expect actual ' test_expect_success 'unify index, two files remain' ' git update-index --no-split-index && git ls-files --stage >ls-files.actual && - cat >ls-files.expect <<EOF && -100644 $EMPTY_BLOB 0 one -100644 $EMPTY_BLOB 0 two -EOF + cat >ls-files.expect <<-EOF && + 100644 $EMPTY_BLOB 0 one + 100644 $EMPTY_BLOB 0 two + EOF test_cmp ls-files.expect ls-files.actual && test-dump-split-index .git/index | sed "/^own/d" >actual && - cat >expect <<EOF && -not a split index -EOF + cat >expect <<-EOF && + not a split index + EOF test_cmp expect actual ' +test_expect_success 'rev-parse --shared-index-path' ' + test_create_repo split-index && + ( + cd split-index && + git update-index --split-index && + echo .git/sharedindex* >expect && + git rev-parse --shared-index-path >actual && + test_cmp expect actual && + mkdir subdirectory && + cd subdirectory && + echo ../.git/sharedindex* >expect && + git rev-parse --shared-index-path >actual && + test_cmp expect actual + ) +' + +test_expect_success 'set core.splitIndex config variable to true' ' + git config core.splitIndex true && + : >three && + git update-index --add three && + git ls-files --stage >ls-files.actual && + cat >ls-files.expect <<-EOF && + 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 one + 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 three + 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 two + EOF + test_cmp ls-files.expect ls-files.actual && + BASE=$(test-dump-split-index .git/index | grep "^base") && + test-dump-split-index .git/index | sed "/^own/d" >actual && + cat >expect <<-EOF && + $BASE + replacements: + deletions: + EOF + test_cmp expect actual +' + +test_expect_success 'set core.splitIndex config variable to false' ' + git config core.splitIndex false && + git update-index --force-remove three && + git ls-files --stage >ls-files.actual && + cat >ls-files.expect <<-EOF && + 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 one + 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 two + EOF + test_cmp ls-files.expect ls-files.actual && + test-dump-split-index .git/index | sed "/^own/d" >actual && + cat >expect <<-EOF && + not a split index + EOF + test_cmp expect actual +' + +test_expect_success 'set core.splitIndex config variable to true' ' + git config core.splitIndex true && + : >three && + git update-index --add three && + BASE=$(test-dump-split-index .git/index | grep "^base") && + test-dump-split-index .git/index | sed "/^own/d" >actual && + cat >expect <<-EOF && + $BASE + replacements: + deletions: + EOF + test_cmp expect actual && + : >four && + git update-index --add four && + test-dump-split-index .git/index | sed "/^own/d" >actual && + cat >expect <<-EOF && + $BASE + 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 four + replacements: + deletions: + EOF + test_cmp expect actual +' + +test_expect_success 'check behavior with splitIndex.maxPercentChange unset' ' + git config --unset splitIndex.maxPercentChange && + : >five && + git update-index --add five && + BASE=$(test-dump-split-index .git/index | grep "^base") && + test-dump-split-index .git/index | sed "/^own/d" >actual && + cat >expect <<-EOF && + $BASE + replacements: + deletions: + EOF + test_cmp expect actual && + : >six && + git update-index --add six && + test-dump-split-index .git/index | sed "/^own/d" >actual && + cat >expect <<-EOF && + $BASE + 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 six + replacements: + deletions: + EOF + test_cmp expect actual +' + +test_expect_success 'check splitIndex.maxPercentChange set to 0' ' + git config splitIndex.maxPercentChange 0 && + : >seven && + git update-index --add seven && + BASE=$(test-dump-split-index .git/index | grep "^base") && + test-dump-split-index .git/index | sed "/^own/d" >actual && + cat >expect <<-EOF && + $BASE + replacements: + deletions: + EOF + test_cmp expect actual && + : >eight && + git update-index --add eight && + BASE=$(test-dump-split-index .git/index | grep "^base") && + test-dump-split-index .git/index | sed "/^own/d" >actual && + cat >expect <<-EOF && + $BASE + replacements: + deletions: + EOF + test_cmp expect actual +' + +test_expect_success 'shared index files expire after 2 weeks by default' ' + : >ten && + git update-index --add ten && + test $(ls .git/sharedindex.* | wc -l) -gt 2 && + just_under_2_weeks_ago=$((5-14*86400)) && + test-chmtime =$just_under_2_weeks_ago .git/sharedindex.* && + : >eleven && + git update-index --add eleven && + test $(ls .git/sharedindex.* | wc -l) -gt 2 && + just_over_2_weeks_ago=$((-1-14*86400)) && + test-chmtime =$just_over_2_weeks_ago .git/sharedindex.* && + : >twelve && + git update-index --add twelve && + test $(ls .git/sharedindex.* | wc -l) -le 2 +' + +test_expect_success 'check splitIndex.sharedIndexExpire set to 16 days' ' + git config splitIndex.sharedIndexExpire "16.days.ago" && + test-chmtime =$just_over_2_weeks_ago .git/sharedindex.* && + : >thirteen && + git update-index --add thirteen && + test $(ls .git/sharedindex.* | wc -l) -gt 2 && + just_over_16_days_ago=$((-1-16*86400)) && + test-chmtime =$just_over_16_days_ago .git/sharedindex.* && + : >fourteen && + git update-index --add fourteen && + test $(ls .git/sharedindex.* | wc -l) -le 2 +' + +test_expect_success 'check splitIndex.sharedIndexExpire set to "never" and "now"' ' + git config splitIndex.sharedIndexExpire never && + just_10_years_ago=$((-365*10*86400)) && + test-chmtime =$just_10_years_ago .git/sharedindex.* && + : >fifteen && + git update-index --add fifteen && + test $(ls .git/sharedindex.* | wc -l) -gt 2 && + git config splitIndex.sharedIndexExpire now && + just_1_second_ago=-1 && + test-chmtime =$just_1_second_ago .git/sharedindex.* && + : >sixteen && + git update-index --add sixteen && + test $(ls .git/sharedindex.* | wc -l) -le 2 +' + test_done diff --git a/t/t2013-checkout-submodule.sh b/t/t2013-checkout-submodule.sh index 6847f75822..e8f70b806f 100755 --- a/t/t2013-checkout-submodule.sh +++ b/t/t2013-checkout-submodule.sh @@ -63,6 +63,12 @@ test_expect_success '"checkout <submodule>" honors submodule.*.ignore from .git/ ! test -s actual ' +KNOWN_FAILURE_DIRECTORY_SUBMODULE_CONFLICTS=1 +KNOWN_FAILURE_SUBMODULE_RECURSIVE_NESTED=1 +test_submodule_switch_recursing "git checkout --recurse-submodules" + +test_submodule_forced_switch_recursing "git checkout -f --recurse-submodules" + test_submodule_switch "git checkout" test_submodule_forced_switch "git checkout -f" diff --git a/t/t2027-worktree-list.sh b/t/t2027-worktree-list.sh index 465eeeacd3..848da5f368 100755 --- a/t/t2027-worktree-list.sh +++ b/t/t2027-worktree-list.sh @@ -14,10 +14,18 @@ test_expect_success 'rev-parse --git-common-dir on main worktree' ' test_cmp expected actual && mkdir sub && git -C sub rev-parse --git-common-dir >actual2 && - echo sub/.git >expected2 && + echo ../.git >expected2 && test_cmp expected2 actual2 ' +test_expect_success 'rev-parse --git-path objects linked worktree' ' + echo "$(git rev-parse --show-toplevel)/.git/objects" >expect && + test_when_finished "rm -rf linked-tree && git worktree prune" && + git worktree add --detach linked-tree master && + git -C linked-tree rev-parse --git-path objects >actual && + test_cmp expect actual +' + test_expect_success '"list" all worktrees from main' ' echo "$(git rev-parse --show-toplevel) $(git rev-parse --short HEAD) [$(git symbolic-ref --short HEAD)]" >expect && test_when_finished "rm -rf here && git worktree prune" && diff --git a/t/t3007-ls-files-recurse-submodules.sh b/t/t3007-ls-files-recurse-submodules.sh index a5426171d3..4cf6ccf5a8 100755 --- a/t/t3007-ls-files-recurse-submodules.sh +++ b/t/t3007-ls-files-recurse-submodules.sh @@ -188,6 +188,45 @@ test_expect_success '--recurse-submodules and pathspecs' ' test_cmp expect actual ' +test_expect_success '--recurse-submodules and relative paths' ' + # From subdir + cat >expect <<-\EOF && + b + EOF + git -C b ls-files --recurse-submodules >actual && + test_cmp expect actual && + + # Relative path to top + cat >expect <<-\EOF && + ../.gitmodules + ../a + b + ../h.txt + ../sib/file + ../sub/file + ../submodule/.gitmodules + ../submodule/c + ../submodule/f.TXT + ../submodule/g.txt + ../submodule/subsub/d + ../submodule/subsub/e.txt + EOF + git -C b ls-files --recurse-submodules -- .. >actual && + test_cmp expect actual && + + # Relative path to submodule + cat >expect <<-\EOF && + ../submodule/.gitmodules + ../submodule/c + ../submodule/f.TXT + ../submodule/g.txt + ../submodule/subsub/d + ../submodule/subsub/e.txt + EOF + git -C b ls-files --recurse-submodules -- ../submodule >actual && + test_cmp expect actual +' + test_expect_success '--recurse-submodules does not support --error-unmatch' ' test_must_fail git ls-files --recurse-submodules --error-unmatch 2>actual && test_i18ngrep "does not support --error-unmatch" actual diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index 8a833f354e..fe62e7c775 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -139,6 +139,12 @@ test_expect_success 'git branch -M baz bam should succeed when baz is checked ou test $(git rev-parse --abbrev-ref HEAD) = bam ' +test_expect_success 'git branch -M baz bam should add entries to .git/logs/HEAD' ' + msg="Branch: renamed refs/heads/baz to refs/heads/bam" && + grep " 0\{40\}.*$msg$" .git/logs/HEAD && + grep "^0\{40\}.*$msg$" .git/logs/HEAD +' + test_expect_success 'git branch -M baz bam should succeed when baz is checked out as linked working tree' ' git checkout master && git worktree add -b baz bazdir && @@ -207,6 +213,31 @@ test_expect_success 'git branch --list -d t should fail' ' test_path_is_missing .git/refs/heads/t ' +test_expect_success 'git branch --list -v with --abbrev' ' + test_when_finished "git branch -D t" && + git branch t && + git branch -v --list t >actual.default && + git branch -v --list --abbrev t >actual.abbrev && + test_cmp actual.default actual.abbrev && + + git branch -v --list --no-abbrev t >actual.noabbrev && + git branch -v --list --abbrev=0 t >actual.0abbrev && + test_cmp actual.noabbrev actual.0abbrev && + + git branch -v --list --abbrev=36 t >actual.36abbrev && + # how many hexdigits are used? + read name objdefault rest <actual.abbrev && + read name obj36 rest <actual.36abbrev && + objfull=$(git rev-parse --verify t) && + + # are we really getting abbreviations? + test "$obj36" != "$objdefault" && + expr "$obj36" : "$objdefault" >/dev/null && + test "$objfull" != "$obj36" && + expr "$objfull" : "$obj36" >/dev/null + +' + test_expect_success 'git branch --column' ' COLUMNS=81 git branch --column=column >actual && cat >expected <<\EOF && @@ -947,6 +978,10 @@ test_expect_success '--merged catches invalid object names' ' test_must_fail git branch --merged 0000000000000000000000000000000000000000 ' +test_expect_success '--merged is incompatible with --no-merged' ' + test_must_fail git branch --merged HEAD --no-merged HEAD +' + test_expect_success 'tracking with unexpected .fetch refspec' ' rm -rf a b c d && git init a && diff --git a/t/t3201-branch-contains.sh b/t/t3201-branch-contains.sh index 7f3ec47241..0ef1b6fdcc 100755 --- a/t/t3201-branch-contains.sh +++ b/t/t3201-branch-contains.sh @@ -1,6 +1,6 @@ #!/bin/sh -test_description='branch --contains <commit>, --merged, and --no-merged' +test_description='branch --contains <commit>, --no-contains <commit> --merged, and --no-merged' . ./test-lib.sh @@ -45,6 +45,22 @@ test_expect_success 'branch --contains master' ' ' +test_expect_success 'branch --no-contains=master' ' + + git branch --no-contains=master >actual && + >expect && + test_cmp expect actual + +' + +test_expect_success 'branch --no-contains master' ' + + git branch --no-contains master >actual && + >expect && + test_cmp expect actual + +' + test_expect_success 'branch --contains=side' ' git branch --contains=side >actual && @@ -55,6 +71,16 @@ test_expect_success 'branch --contains=side' ' ' +test_expect_success 'branch --no-contains=side' ' + + git branch --no-contains=side >actual && + { + echo " master" + } >expect && + test_cmp expect actual + +' + test_expect_success 'branch --contains with pattern implies --list' ' git branch --contains=master master >actual && @@ -65,6 +91,14 @@ test_expect_success 'branch --contains with pattern implies --list' ' ' +test_expect_success 'branch --no-contains with pattern implies --list' ' + + git branch --no-contains=master master >actual && + >expect && + test_cmp expect actual + +' + test_expect_success 'side: branch --merged' ' git branch --merged >actual && @@ -126,8 +160,20 @@ test_expect_success 'branch --no-merged with pattern implies --list' ' test_expect_success 'implicit --list conflicts with modification options' ' test_must_fail git branch --contains=master -d && - test_must_fail git branch --contains=master -m foo + test_must_fail git branch --contains=master -m foo && + test_must_fail git branch --no-contains=master -d && + test_must_fail git branch --no-contains=master -m foo + +' +test_expect_success 'Assert that --contains only works on commits, not trees & blobs' ' + test_must_fail git branch --contains master^{tree} && + blob=$(git hash-object -w --stdin <<-\EOF + Some blob + EOF + ) && + test_must_fail git branch --contains $blob && + test_must_fail git branch --no-contains $blob ' # We want to set up a case where the walk for the tracking info @@ -159,4 +205,15 @@ test_expect_success 'branch --merged with --verbose' ' test_i18ncmp expect actual ' +test_expect_success 'branch --contains combined with --no-contains' ' + git branch --contains zzz --no-contains topic >actual && + cat >expect <<-\EOF && + master + side + zzz + EOF + test_cmp expect actual + +' + test_done diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh index 52283dfc8c..5778c0afe1 100755 --- a/t/t3203-branch-output.sh +++ b/t/t3203-branch-output.sh @@ -194,7 +194,7 @@ test_expect_success 'local-branch symrefs shortened properly' ' git symbolic-ref refs/heads/ref-to-remote refs/remotes/origin/branch-one && cat >expect <<-\EOF && ref-to-branch -> branch-one - ref-to-remote -> refs/remotes/origin/branch-one + ref-to-remote -> origin/branch-one EOF git branch >actual.raw && grep ref-to <actual.raw >actual && @@ -225,4 +225,18 @@ test_expect_success 'sort branches, ignore case' ' ) ' +test_expect_success 'git branch --format option' ' + cat >expect <<-\EOF && + Refname is (HEAD detached from fromtag) + Refname is refs/heads/ambiguous + Refname is refs/heads/branch-one + Refname is refs/heads/branch-two + Refname is refs/heads/master + Refname is refs/heads/ref-to-branch + Refname is refs/heads/ref-to-remote + EOF + git branch --format="Refname is %(refname)" >actual && + test_cmp expect actual +' + test_done diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index e2f18d11f6..33d392ba11 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -556,7 +556,7 @@ test_expect_success 'clean error after failed "exec"' ' echo "edited again" > file7 && git add file7 && test_must_fail git rebase --continue 2>error && - test_i18ngrep "You have staged changes in your working tree." error + test_i18ngrep "you have staged changes in your working tree" error ' test_expect_success 'rebase a detached HEAD' ' diff --git a/t/t3502-cherry-pick-merge.sh b/t/t3502-cherry-pick-merge.sh index e37547f41a..b1602718f8 100755 --- a/t/t3502-cherry-pick-merge.sh +++ b/t/t3502-cherry-pick-merge.sh @@ -31,6 +31,15 @@ test_expect_success setup ' ' +test_expect_success 'cherry-pick -m complains of bogus numbers' ' + # expect 129 here to distinguish between cases where + # there was nothing to cherry-pick + test_expect_code 129 git cherry-pick -m && + test_expect_code 129 git cherry-pick -m foo b && + test_expect_code 129 git cherry-pick -m -1 b && + test_expect_code 129 git cherry-pick -m 0 b +' + test_expect_success 'cherry-pick a non-merge with -m should fail' ' git reset --hard && diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh index 3c63455729..5f9913ba33 100755 --- a/t/t3600-rm.sh +++ b/t/t3600-rm.sh @@ -268,6 +268,14 @@ cat >expect.modified <<EOF M submod EOF +cat >expect.modified_inside <<EOF + m submod +EOF + +cat >expect.modified_untracked <<EOF + ? submod +EOF + cat >expect.cached <<EOF D submod EOF @@ -421,7 +429,7 @@ test_expect_success 'rm of a populated submodule with modifications fails unless test -d submod && test -f submod/.git && git status -s -uno --ignore-submodules=none >actual && - test_cmp expect.modified actual && + test_cmp expect.modified_inside actual && git rm -f submod && test ! -d submod && git status -s -uno --ignore-submodules=none >actual && @@ -436,7 +444,7 @@ test_expect_success 'rm of a populated submodule with untracked files fails unle test -d submod && test -f submod/.git && git status -s -uno --ignore-submodules=none >actual && - test_cmp expect.modified actual && + test_cmp expect.modified_untracked actual && git rm -f submod && test ! -d submod && git status -s -uno --ignore-submodules=none >actual && @@ -621,7 +629,7 @@ test_expect_success 'rm of a populated nested submodule with different nested HE test -d submod && test -f submod/.git && git status -s -uno --ignore-submodules=none >actual && - test_cmp expect.modified actual && + test_cmp expect.modified_inside actual && git rm -f submod && test ! -d submod && git status -s -uno --ignore-submodules=none >actual && @@ -636,7 +644,7 @@ test_expect_success 'rm of a populated nested submodule with nested modification test -d submod && test -f submod/.git && git status -s -uno --ignore-submodules=none >actual && - test_cmp expect.modified actual && + test_cmp expect.modified_inside actual && git rm -f submod && test ! -d submod && git status -s -uno --ignore-submodules=none >actual && @@ -651,7 +659,7 @@ test_expect_success 'rm of a populated nested submodule with nested untracked fi test -d submod && test -f submod/.git && git status -s -uno --ignore-submodules=none >actual && - test_cmp expect.modified actual && + test_cmp expect.modified_untracked actual && git rm -f submod && test ! -d submod && git status -s -uno --ignore-submodules=none >actual && diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index aaa258daa3..f9528fa00c 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -412,4 +412,47 @@ test_expect_success 'patch-mode via -i prompts for files' ' test_cmp expect actual ' +test_expect_success 'add -p handles globs' ' + git reset --hard && + + mkdir -p subdir && + echo base >one.c && + echo base >subdir/two.c && + git add "*.c" && + git commit -m base && + + echo change >one.c && + echo change >subdir/two.c && + git add -p "*.c" <<-\EOF && + y + y + EOF + + cat >expect <<-\EOF && + one.c + subdir/two.c + EOF + git diff --cached --name-only >actual && + test_cmp expect actual +' + +test_expect_success 'add -p does not expand argument lists' ' + git reset --hard && + + echo content >not-changed && + git add not-changed && + git commit -m "add not-changed file" && + + echo change >file && + GIT_TRACE=$(pwd)/trace.out git add -p . <<-\EOF && + y + EOF + + # we know that "file" must be mentioned since we actually + # update it, but we want to be sure that our "." pathspec + # was not expanded into the argument list of any command. + # So look only for "not-changed". + ! grep not-changed trace.out +' + test_done diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh index 2de3e18ce6..b71d1e659e 100755 --- a/t/t3903-stash.sh +++ b/t/t3903-stash.sh @@ -274,9 +274,7 @@ test_expect_success 'stash --invalid-option' ' git add file2 && test_must_fail git stash --invalid-option && test_must_fail git stash save --invalid-option && - test bar5,bar6 = $(cat file),$(cat file2) && - git stash -- -message-starting-with-dash && - test bar,bar2 = $(cat file),$(cat file2) + test bar5,bar6 = $(cat file),$(cat file2) ' test_expect_success 'stash an added file' ' @@ -665,7 +663,7 @@ test_expect_success 'stash apply shows status same as git status (relative to cu sane_unset GIT_MERGE_VERBOSITY && git stash apply ) | - sed -e 1,2d >actual && # drop "Saved..." and "HEAD is now..." + sed -e 1d >actual && # drop "Saved..." test_i18ncmp expect actual ' @@ -775,4 +773,152 @@ test_expect_success 'stash is not confused by partial renames' ' test_path_is_missing file ' +test_expect_success 'push -m shows right message' ' + >foo && + git add foo && + git stash push -m "test message" && + echo "stash@{0}: On master: test message" >expect && + git stash list -1 >actual && + test_cmp expect actual +' + +test_expect_success 'create stores correct message' ' + >foo && + git add foo && + STASH_ID=$(git stash create "create test message") && + echo "On master: create test message" >expect && + git show --pretty=%s -s ${STASH_ID} >actual && + test_cmp expect actual +' + +test_expect_success 'create with multiple arguments for the message' ' + >foo && + git add foo && + STASH_ID=$(git stash create test untracked) && + echo "On master: test untracked" >expect && + git show --pretty=%s -s ${STASH_ID} >actual && + test_cmp expect actual +' + +test_expect_success 'stash -- <pathspec> stashes and restores the file' ' + >foo && + >bar && + git add foo bar && + git stash push -- foo && + test_path_is_file bar && + test_path_is_missing foo && + git stash pop && + test_path_is_file foo && + test_path_is_file bar +' + +test_expect_success 'stash with multiple pathspec arguments' ' + >foo && + >bar && + >extra && + git add foo bar extra && + git stash push -- foo bar && + test_path_is_missing bar && + test_path_is_missing foo && + test_path_is_file extra && + git stash pop && + test_path_is_file foo && + test_path_is_file bar && + test_path_is_file extra +' + +test_expect_success 'stash with file including $IFS character' ' + >"foo bar" && + >foo && + >bar && + git add foo* && + git stash push -- "foo b*" && + test_path_is_missing "foo bar" && + test_path_is_file foo && + test_path_is_file bar && + git stash pop && + test_path_is_file "foo bar" && + test_path_is_file foo && + test_path_is_file bar +' + +test_expect_success 'stash with pathspec matching multiple paths' ' + echo original >file && + echo original >other-file && + git commit -m "two" file other-file && + echo modified >file && + echo modified >other-file && + git stash push -- "*file" && + echo original >expect && + test_cmp expect file && + test_cmp expect other-file && + git stash pop && + echo modified >expect && + test_cmp expect file && + test_cmp expect other-file +' + +test_expect_success 'stash push -p with pathspec shows no changes only once' ' + >foo && + git add foo && + git commit -m "tmp" && + git stash push -p foo >actual && + echo "No local changes to save" >expect && + git reset --hard HEAD~ && + test_cmp expect actual +' + +test_expect_success 'stash push with pathspec shows no changes when there are none' ' + >foo && + git add foo && + git commit -m "tmp" && + git stash push foo >actual && + echo "No local changes to save" >expect && + git reset --hard HEAD~ && + test_cmp expect actual +' + +test_expect_success 'stash push with pathspec not in the repository errors out' ' + >untracked && + test_must_fail git stash push untracked && + test_path_is_file untracked +' + +test_expect_success 'untracked files are left in place when -u is not given' ' + >file && + git add file && + >untracked && + git stash push file && + test_path_is_file untracked +' + +test_expect_success 'stash without verb with pathspec' ' + >"foo bar" && + >foo && + >bar && + git add foo* && + git stash -- "foo b*" && + test_path_is_missing "foo bar" && + test_path_is_file foo && + test_path_is_file bar && + git stash pop && + test_path_is_file "foo bar" && + test_path_is_file foo && + test_path_is_file bar +' + +test_expect_success 'stash -k -- <pathspec> leaves unstaged files intact' ' + git reset && + >foo && + >bar && + git add foo bar && + git commit -m "test" && + echo "foo" >foo && + echo "bar" >bar && + git stash -k -- foo && + test "",bar = $(cat foo),$(cat bar) && + git stash pop && + test foo,bar = $(cat foo),$(cat bar) +' + test_done diff --git a/t/t3904-stash-patch.sh b/t/t3904-stash-patch.sh index 38e730090f..83744f8c93 100755 --- a/t/t3904-stash-patch.sh +++ b/t/t3904-stash-patch.sh @@ -77,6 +77,14 @@ test_expect_success 'git stash --no-keep-index -p' ' verify_state dir/foo work index ' +test_expect_success 'stash -p --no-keep-index -- <pathspec> does not unstage other files' ' + set_state HEAD HEADfile_work HEADfile_index && + set_state dir/foo work index && + echo y | git stash push -p --no-keep-index -- HEAD && + verify_state HEAD committed committed && + verify_state dir/foo work index +' + test_expect_success 'none of this moved HEAD' ' verify_saved_head ' diff --git a/t/t3905-stash-include-untracked.sh b/t/t3905-stash-include-untracked.sh index f372fc8ca8..193adc7b68 100755 --- a/t/t3905-stash-include-untracked.sh +++ b/t/t3905-stash-include-untracked.sh @@ -185,4 +185,30 @@ test_expect_success 'stash save --all is stash poppable' ' test -s .gitignore ' +test_expect_success 'stash push --include-untracked with pathspec' ' + >foo && + >bar && + git stash push --include-untracked -- foo && + test_path_is_file bar && + test_path_is_missing foo && + git stash pop && + test_path_is_file bar && + test_path_is_file foo +' + +test_expect_success 'stash push with $IFS character' ' + >"foo bar" && + >foo && + >bar && + git add foo* && + git stash push --include-untracked -- "foo b*" && + test_path_is_missing "foo bar" && + test_path_is_file foo && + test_path_is_file bar && + git stash pop && + test_path_is_file "foo bar" && + test_path_is_file foo && + test_path_is_file bar +' + test_done diff --git a/t/t4060-diff-submodule-option-diff-format.sh b/t/t4060-diff-submodule-option-diff-format.sh index 7e23b55ea4..d4a3ffa69c 100755 --- a/t/t4060-diff-submodule-option-diff-format.sh +++ b/t/t4060-diff-submodule-option-diff-format.sh @@ -746,4 +746,33 @@ test_expect_success 'diff --submodule=diff with .git file' ' test_cmp expected actual ' +test_expect_success 'setup nested submodule' ' + git submodule add -f ./sm2 && + git commit -a -m "add sm2" && + git -C sm2 submodule add ../sm2 nested && + git -C sm2 commit -a -m "nested sub" +' + +test_expect_success 'move nested submodule HEAD' ' + echo "nested content" >sm2/nested/file && + git -C sm2/nested add file && + git -C sm2/nested commit --allow-empty -m "new HEAD" +' + +test_expect_success 'diff --submodule=diff with moved nested submodule HEAD' ' + cat >expected <<-EOF && + Submodule nested a5a65c9..b55928c: + diff --git a/nested/file b/nested/file + new file mode 100644 + index 0000000..ca281f5 + --- /dev/null + +++ b/nested/file + @@ -0,0 +1 @@ + +nested content + EOF + git -C sm2 diff --submodule=diff >actual 2>err && + test_must_be_empty err && + test_cmp expected actual +' + test_done diff --git a/t/t4150-am.sh b/t/t4150-am.sh index 89a5bacac5..44807e218d 100755 --- a/t/t4150-am.sh +++ b/t/t4150-am.sh @@ -983,7 +983,9 @@ test_expect_success 'am works with multi-line in-body headers' ' rm -fr .git/rebase-apply && git checkout -f first && echo one >> file && - git commit -am "$LONG" --author="$LONG <long@example.com>" && + git commit -am "$LONG + + Body test" --author="$LONG <long@example.com>" && git format-patch --stdout -1 >patch && # bump from, date, and subject down to in-body header perl -lpe " @@ -997,7 +999,7 @@ test_expect_success 'am works with multi-line in-body headers' ' git am msg && # Ensure that the author and full message are present git cat-file commit HEAD | grep "^author.*long@example.com" && - git cat-file commit HEAD | grep "^$LONG" + git cat-file commit HEAD | grep "^$LONG$" ' test_done diff --git a/t/t4202-log.sh b/t/t4202-log.sh index 48b55bfd27..f577990716 100755 --- a/t/t4202-log.sh +++ b/t/t4202-log.sh @@ -4,6 +4,7 @@ test_description='git log' . ./test-lib.sh . "$TEST_DIRECTORY/lib-gpg.sh" +. "$TEST_DIRECTORY/lib-terminal.sh" test_expect_success setup ' @@ -520,7 +521,7 @@ test_expect_success 'log --graph with merge' ' ' test_expect_success 'log.decorate configuration' ' - git log --oneline >expect.none && + git log --oneline --no-decorate >expect.none && git log --oneline --decorate >expect.short && git log --oneline --decorate=full >expect.full && @@ -576,6 +577,13 @@ test_expect_success 'log.decorate configuration' ' ' +test_expect_success TTY 'log output on a TTY' ' + git log --oneline --decorate >expect.short && + + test_terminal git log --oneline >actual && + test_cmp expect.short actual +' + test_expect_success 'reflog is expected format' ' git log -g --abbrev-commit --pretty=oneline >expect && git reflog >actual && diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh index 305ca7a930..3331e0f534 100755 --- a/t/t5400-send-pack.sh +++ b/t/t5400-send-pack.sh @@ -255,4 +255,42 @@ test_expect_success 'deny pushing to delete current branch' ' ) ' +extract_ref_advertisement () { + perl -lne ' + # \\ is there to skip capabilities after \0 + /push< ([^\\]+)/ or next; + exit 0 if $1 eq "0000"; + print $1; + ' +} + +test_expect_success 'receive-pack de-dupes .have lines' ' + git init shared && + git -C shared commit --allow-empty -m both && + git clone -s shared fork && + ( + cd shared && + git checkout -b only-shared && + git commit --allow-empty -m only-shared && + git update-ref refs/heads/foo HEAD + ) && + + # Notable things in this expectation: + # - local refs are not de-duped + # - .have does not duplicate locals + # - .have does not duplicate itself + local=$(git -C fork rev-parse HEAD) && + shared=$(git -C shared rev-parse only-shared) && + cat >expect <<-EOF && + $local refs/heads/master + $local refs/remotes/origin/HEAD + $local refs/remotes/origin/master + $shared .have + EOF + + GIT_TRACE_PACKET=$(pwd)/trace git push fork HEAD:foo && + extract_ref_advertisement <trace >refs && + test_cmp expect refs +' + test_done diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh index 3ea27107c2..a6c0178f3a 100755 --- a/t/t5505-remote.sh +++ b/t/t5505-remote.sh @@ -744,7 +744,7 @@ test_expect_success 'rename a remote' ' ( cd four && git remote rename origin upstream && - rmdir .git/refs/remotes/origin && + test -z "$(git for-each-ref refs/remotes/origin)" && test "$(git symbolic-ref refs/remotes/upstream/HEAD)" = "refs/remotes/upstream/master" && test "$(git rev-parse upstream/master)" = "$(git rev-parse master)" && test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/remotes/upstream/*" && diff --git a/t/t5531-deep-submodule-push.sh b/t/t5531-deep-submodule-push.sh index f55137f76f..57ba322628 100755 --- a/t/t5531-deep-submodule-push.sh +++ b/t/t5531-deep-submodule-push.sh @@ -475,4 +475,56 @@ test_expect_success 'push only unpushed submodules recursively' ' test_cmp expected_pub actual_pub ' +test_expect_success 'push propagating the remotes name to a submodule' ' + git -C work remote add origin ../pub.git && + git -C work remote add pub ../pub.git && + + > work/gar/bage/junk10 && + git -C work/gar/bage add junk10 && + git -C work/gar/bage commit -m "Tenth junk" && + git -C work add gar/bage && + git -C work commit -m "Tenth junk added to gar/bage" && + + # Fails when submodule does not have a matching remote + test_must_fail git -C work push --recurse-submodules=on-demand pub master && + # Succeeds when submodules has matching remote and refspec + git -C work push --recurse-submodules=on-demand origin master && + + git -C submodule.git rev-parse master >actual_submodule && + git -C pub.git rev-parse master >actual_pub && + git -C work/gar/bage rev-parse master >expected_submodule && + git -C work rev-parse master >expected_pub && + test_cmp expected_submodule actual_submodule && + test_cmp expected_pub actual_pub +' + +test_expect_success 'push propagating refspec to a submodule' ' + > work/gar/bage/junk11 && + git -C work/gar/bage add junk11 && + git -C work/gar/bage commit -m "Eleventh junk" && + + git -C work checkout branch2 && + git -C work add gar/bage && + git -C work commit -m "updating gar/bage in branch2" && + + # Fails when submodule does not have a matching branch + test_must_fail git -C work push --recurse-submodules=on-demand origin branch2 && + # Fails when refspec includes an object id + test_must_fail git -C work push --recurse-submodules=on-demand origin \ + "$(git -C work rev-parse branch2):refs/heads/branch2" && + # Fails when refspec includes 'HEAD' as it is unsupported at this time + test_must_fail git -C work push --recurse-submodules=on-demand origin \ + HEAD:refs/heads/branch2 && + + git -C work/gar/bage branch branch2 master && + git -C work push --recurse-submodules=on-demand origin branch2 && + + git -C submodule.git rev-parse branch2 >actual_submodule && + git -C pub.git rev-parse branch2 >actual_pub && + git -C work/gar/bage rev-parse branch2 >expected_submodule && + git -C work rev-parse branch2 >expected_pub && + test_cmp expected_submodule actual_submodule && + test_cmp expected_pub actual_pub +' + test_done diff --git a/t/t5545-push-options.sh b/t/t5545-push-options.sh index 9a57a7d8f2..f9232f5d0f 100755 --- a/t/t5545-push-options.sh +++ b/t/t5545-push-options.sh @@ -102,17 +102,86 @@ test_expect_success 'two push options work' ' test_cmp expect upstream/.git/hooks/post-receive.push_options ' -test_expect_success 'push option denied properly by http remote helper' '\ +test_expect_success 'push option denied properly by http server' ' + test_when_finished "rm -rf test_http_clone" && + test_when_finished "rm -rf \"$HTTPD_DOCUMENT_ROOT_PATH\"/upstream.git" && mk_repo_pair && git -C upstream config receive.advertisePushOptions false && git -C upstream config http.receivepack true && cp -R upstream/.git "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git && git clone "$HTTPD_URL"/smart/upstream test_http_clone && test_commit -C test_http_clone one && - test_must_fail git -C test_http_clone push --push-option=asdf origin master && + test_must_fail git -C test_http_clone push --push-option=asdf origin master 2>actual && + test_i18ngrep "the receiving end does not support push options" actual && git -C test_http_clone push origin master ' +test_expect_success 'push options work properly across http' ' + test_when_finished "rm -rf test_http_clone" && + test_when_finished "rm -rf \"$HTTPD_DOCUMENT_ROOT_PATH\"/upstream.git" && + mk_repo_pair && + git -C upstream config receive.advertisePushOptions true && + git -C upstream config http.receivepack true && + cp -R upstream/.git "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git && + git clone "$HTTPD_URL"/smart/upstream test_http_clone && + + test_commit -C test_http_clone one && + git -C test_http_clone push origin master && + git -C "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git rev-parse --verify master >expect && + git -C test_http_clone rev-parse --verify master >actual && + test_cmp expect actual && + + test_commit -C test_http_clone two && + git -C test_http_clone push --push-option=asdf --push-option="more structured text" origin master && + printf "asdf\nmore structured text\n" >expect && + test_cmp expect "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git/hooks/pre-receive.push_options && + test_cmp expect "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git/hooks/post-receive.push_options && + + git -C "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git rev-parse --verify master >expect && + git -C test_http_clone rev-parse --verify master >actual && + test_cmp expect actual +' + +test_expect_success 'push options and submodules' ' + test_when_finished "rm -rf parent" && + test_when_finished "rm -rf parent_upstream" && + mk_repo_pair && + git -C upstream config receive.advertisePushOptions true && + cp -r upstream parent_upstream && + test_commit -C upstream one && + + test_create_repo parent && + git -C parent remote add up ../parent_upstream && + test_commit -C parent one && + git -C parent push --mirror up && + + git -C parent submodule add ../upstream workbench && + git -C parent/workbench remote add up ../../upstream && + git -C parent commit -m "add submoule" && + + test_commit -C parent/workbench two && + git -C parent add workbench && + git -C parent commit -m "update workbench" && + + git -C parent push \ + --push-option=asdf --push-option="more structured text" \ + --recurse-submodules=on-demand up master && + + git -C upstream rev-parse --verify master >expect && + git -C parent/workbench rev-parse --verify master >actual && + test_cmp expect actual && + + git -C parent_upstream rev-parse --verify master >expect && + git -C parent rev-parse --verify master >actual && + test_cmp expect actual && + + printf "asdf\nmore structured text\n" >expect && + test_cmp expect upstream/.git/hooks/pre-receive.push_options && + test_cmp expect upstream/.git/hooks/post-receive.push_options && + test_cmp expect parent_upstream/.git/hooks/pre-receive.push_options && + test_cmp expect parent_upstream/.git/hooks/post-receive.push_options +' + stop_httpd test_done diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh index 4241ea5b32..b52b8acf98 100755 --- a/t/t5601-clone.sh +++ b/t/t5601-clone.sh @@ -386,6 +386,47 @@ test_expect_success 'tortoiseplink is like putty, with extra arguments' ' expect_ssh "-batch -P 123" myhost src ' +test_expect_success 'double quoted plink.exe in GIT_SSH_COMMAND' ' + copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink.exe" && + GIT_SSH_COMMAND="\"$TRASH_DIRECTORY/plink.exe\" -v" \ + git clone "[myhost:123]:src" ssh-bracket-clone-plink-3 && + expect_ssh "-v -P 123" myhost src +' + +SQ="'" +test_expect_success 'single quoted plink.exe in GIT_SSH_COMMAND' ' + copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink.exe" && + GIT_SSH_COMMAND="$SQ$TRASH_DIRECTORY/plink.exe$SQ -v" \ + git clone "[myhost:123]:src" ssh-bracket-clone-plink-4 && + expect_ssh "-v -P 123" myhost src +' + +test_expect_success 'GIT_SSH_VARIANT overrides plink detection' ' + copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink" && + GIT_SSH_VARIANT=ssh \ + git clone "[myhost:123]:src" ssh-bracket-clone-variant-1 && + expect_ssh "-p 123" myhost src +' + +test_expect_success 'ssh.variant overrides plink detection' ' + copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink" && + git -c ssh.variant=ssh \ + clone "[myhost:123]:src" ssh-bracket-clone-variant-2 && + expect_ssh "-p 123" myhost src +' + +test_expect_success 'GIT_SSH_VARIANT overrides plink detection to plink' ' + GIT_SSH_VARIANT=plink \ + git clone "[myhost:123]:src" ssh-bracket-clone-variant-3 && + expect_ssh "-P 123" myhost src +' + +test_expect_success 'GIT_SSH_VARIANT overrides plink to tortoiseplink' ' + GIT_SSH_VARIANT=tortoiseplink \ + git clone "[myhost:123]:src" ssh-bracket-clone-variant-4 && + expect_ssh "-batch -P 123" myhost src +' + # Reset the GIT_SSH environment variable for clone tests. setup_ssh_wrapper diff --git a/t/t6007-rev-list-cherry-pick-file.sh b/t/t6007-rev-list-cherry-pick-file.sh index 1408b608eb..2959745196 100755 --- a/t/t6007-rev-list-cherry-pick-file.sh +++ b/t/t6007-rev-list-cherry-pick-file.sh @@ -99,6 +99,44 @@ test_expect_success '--cherry-pick bar does not come up empty (II)' ' test_cmp actual.named expect ' +test_expect_success 'name-rev multiple --refs combine inclusive' ' + git rev-list --left-right --cherry-pick F...E -- bar >actual && + git name-rev --stdin --name-only --refs="*tags/F" --refs="*tags/E" \ + <actual >actual.named && + test_cmp actual.named expect +' + +cat >expect <<EOF +<tags/F +EOF + +test_expect_success 'name-rev --refs excludes non-matched patterns' ' + git rev-list --left-right --right-only --cherry-pick F...E -- bar >>expect && + git rev-list --left-right --cherry-pick F...E -- bar >actual && + git name-rev --stdin --name-only --refs="*tags/F" \ + <actual >actual.named && + test_cmp actual.named expect +' + +cat >expect <<EOF +<tags/F +EOF + +test_expect_success 'name-rev --exclude excludes matched patterns' ' + git rev-list --left-right --right-only --cherry-pick F...E -- bar >>expect && + git rev-list --left-right --cherry-pick F...E -- bar >actual && + git name-rev --stdin --name-only --refs="*tags/*" --exclude="*E" \ + <actual >actual.named && + test_cmp actual.named expect +' + +test_expect_success 'name-rev --no-refs clears the refs list' ' + git rev-list --left-right --cherry-pick F...E -- bar >expect && + git name-rev --stdin --name-only --refs="*tags/F" --refs="*tags/E" --no-refs --refs="*tags/G" \ + <expect >actual && + test_cmp actual expect +' + cat >expect <<EOF +tags/F =tags/D diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh index 3d5c238c81..97a07655a0 100755 --- a/t/t6040-tracking-info.sh +++ b/t/t6040-tracking-info.sh @@ -44,7 +44,7 @@ b1 [ahead 1, behind 1] d b2 [ahead 1, behind 1] d b3 [behind 1] b b4 [ahead 2] f -b5 g +b5 [gone] g b6 c EOF diff --git a/t/t6045-merge-rename-delete.sh b/t/t6045-merge-rename-delete.sh new file mode 100755 index 0000000000..5d33577d2f --- /dev/null +++ b/t/t6045-merge-rename-delete.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +test_description='Merge-recursive rename/delete conflict message' +. ./test-lib.sh + +test_expect_success 'rename/delete' ' + echo foo >A && + git add A && + git commit -m "initial" && + + git checkout -b rename && + git mv A B && + git commit -m "rename" && + + git checkout master && + git rm A && + git commit -m "delete" && + + test_must_fail git merge --strategy=recursive rename >output && + test_i18ngrep "CONFLICT (rename/delete): A deleted in HEAD and renamed to B in rename. Version rename of B left in tree." output +' + +test_done diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh index 85f269411c..16952e44fc 100755 --- a/t/t6120-describe.sh +++ b/t/t6120-describe.sh @@ -182,6 +182,10 @@ check_describe "test2-lightweight-*" --tags --match="test2-*" check_describe "test2-lightweight-*" --long --tags --match="test2-*" HEAD^ +check_describe "test1-lightweight-*" --long --tags --match="test1-*" --match="test2-*" HEAD^ + +check_describe "test2-lightweight-*" --long --tags --match="test1-*" --no-match --match="test2-*" HEAD^ + test_expect_success 'name-rev with exact tags' ' echo A >expect && tag_object=$(git rev-parse refs/tags/A) && @@ -206,4 +210,47 @@ test_expect_success 'describe --contains with the exact tags' ' test_cmp expect actual ' +test_expect_success 'describe --contains and --match' ' + echo "A^0" >expect && + tagged_commit=$(git rev-parse "refs/tags/A^0") && + test_must_fail git describe --contains --match="B" $tagged_commit && + git describe --contains --match="B" --match="A" $tagged_commit >actual && + test_cmp expect actual +' + +test_expect_success 'describe --exclude' ' + echo "c~1" >expect && + tagged_commit=$(git rev-parse "refs/tags/A^0") && + test_must_fail git describe --contains --match="B" $tagged_commit && + git describe --contains --match="?" --exclude="A" $tagged_commit >actual && + test_cmp expect actual +' + +test_expect_success 'describe --contains and --no-match' ' + echo "A^0" >expect && + tagged_commit=$(git rev-parse "refs/tags/A^0") && + git describe --contains --match="B" --no-match $tagged_commit >actual && + test_cmp expect actual +' + +test_expect_success 'setup and absorb a submodule' ' + test_create_repo sub1 && + test_commit -C sub1 initial && + git submodule add ./sub1 && + git submodule absorbgitdirs && + git commit -a -m "add submodule" && + git describe --dirty >expect && + git describe --broken >out && + test_cmp expect out +' + +test_expect_success 'describe chokes on severly broken submodules' ' + mv .git/modules/sub1/ .git/modules/sub_moved && + test_must_fail git describe --dirty +' +test_expect_success 'describe ignoring a borken submodule' ' + git describe --broken >out && + grep broken out +' + test_done diff --git a/t/t6132-pathspec-exclude.sh b/t/t6132-pathspec-exclude.sh index d51595cf6b..9dd5cde5fc 100755 --- a/t/t6132-pathspec-exclude.sh +++ b/t/t6132-pathspec-exclude.sh @@ -25,8 +25,10 @@ EOF test_cmp expect actual ' -test_expect_success 'exclude only should error out' ' - test_must_fail git log --oneline --format=%s -- ":(exclude)sub" +test_expect_success 'exclude only no longer errors out' ' + git log --oneline --format=%s -- . ":(exclude)sub" >expect && + git log --oneline --format=%s -- ":(exclude)sub" >actual && + test_cmp expect actual ' test_expect_success 't_e_i() exclude sub' ' diff --git a/t/t6135-pathspec-with-attrs.sh b/t/t6135-pathspec-with-attrs.sh new file mode 100755 index 0000000000..77b8cef661 --- /dev/null +++ b/t/t6135-pathspec-with-attrs.sh @@ -0,0 +1,200 @@ +#!/bin/sh + +test_description='test labels in pathspecs' +. ./test-lib.sh + +test_expect_success 'setup a tree' ' + cat <<-\EOF >expect && + fileA + fileAB + fileAC + fileB + fileBC + fileC + fileNoLabel + fileSetLabel + fileUnsetLabel + fileValue + fileWrongLabel + sub/fileA + sub/fileAB + sub/fileAC + sub/fileB + sub/fileBC + sub/fileC + sub/fileNoLabel + sub/fileSetLabel + sub/fileUnsetLabel + sub/fileValue + sub/fileWrongLabel + EOF + mkdir sub && + while read path + do + : >$path && + git add $path || return 1 + done <expect && + git commit -m "initial commit" && + git ls-files >actual && + test_cmp expect actual +' + +test_expect_success 'pathspec with no attr' ' + test_must_fail git ls-files ":(attr:)" +' + +test_expect_success 'pathspec with labels and non existent .gitattributes' ' + git ls-files ":(attr:label)" >actual && + test_must_be_empty actual +' + +test_expect_success 'setup .gitattributes' ' + cat <<-\EOF >.gitattributes && + fileA labelA + fileB labelB + fileC labelC + fileAB labelA labelB + fileAC labelA labelC + fileBC labelB labelC + fileUnsetLabel -label + fileSetLabel label + fileValue label=foo + fileWrongLabel label☺ + EOF + git add .gitattributes && + git commit -m "add attributes" +' + +test_expect_success 'check specific set attr' ' + cat <<-\EOF >expect && + fileSetLabel + sub/fileSetLabel + EOF + git ls-files ":(attr:label)" >actual && + test_cmp expect actual +' + +test_expect_success 'check specific unset attr' ' + cat <<-\EOF >expect && + fileUnsetLabel + sub/fileUnsetLabel + EOF + git ls-files ":(attr:-label)" >actual && + test_cmp expect actual +' + +test_expect_success 'check specific value attr' ' + cat <<-\EOF >expect && + fileValue + sub/fileValue + EOF + git ls-files ":(attr:label=foo)" >actual && + test_cmp expect actual && + git ls-files ":(attr:label=bar)" >actual && + test_must_be_empty actual +' + +test_expect_success 'check unspecified attr' ' + cat <<-\EOF >expect && + .gitattributes + fileA + fileAB + fileAC + fileB + fileBC + fileC + fileNoLabel + fileWrongLabel + sub/fileA + sub/fileAB + sub/fileAC + sub/fileB + sub/fileBC + sub/fileC + sub/fileNoLabel + sub/fileWrongLabel + EOF + git ls-files ":(attr:!label)" >actual && + test_cmp expect actual +' + +test_expect_success 'check multiple unspecified attr' ' + cat <<-\EOF >expect && + .gitattributes + fileC + fileNoLabel + fileWrongLabel + sub/fileC + sub/fileNoLabel + sub/fileWrongLabel + EOF + git ls-files ":(attr:!labelB !labelA !label)" >actual && + test_cmp expect actual +' + +test_expect_success 'check label with more labels but excluded path' ' + cat <<-\EOF >expect && + fileAB + fileB + fileBC + EOF + git ls-files ":(attr:labelB)" ":(exclude)sub/" >actual && + test_cmp expect actual +' + +test_expect_success 'check label excluding other labels' ' + cat <<-\EOF >expect && + fileAB + fileB + fileBC + sub/fileAB + sub/fileB + EOF + git ls-files ":(attr:labelB)" ":(exclude,attr:labelC)sub/" >actual && + test_cmp expect actual +' + +test_expect_success 'fail on multiple attr specifiers in one pathspec item' ' + test_must_fail git ls-files . ":(attr:labelB,attr:labelC)" 2>actual && + test_i18ngrep "Only one" actual +' + +test_expect_success 'fail if attr magic is used places not implemented' ' + # The main purpose of this test is to check that we actually fail + # when you attempt to use attr magic in commands that do not implement + # attr magic. This test does not advocate git-add to stay that way, + # though, but git-add is convenient as it has its own internal pathspec + # parsing. + test_must_fail git add ":(attr:labelB)" 2>actual && + test_i18ngrep "unsupported magic" actual +' + +test_expect_success 'abort on giving invalid label on the command line' ' + test_must_fail git ls-files . ":(attr:☺)" +' + +test_expect_success 'abort on asking for wrong magic' ' + test_must_fail git ls-files . ":(attr:-label=foo)" && + test_must_fail git ls-files . ":(attr:!label=foo)" +' + +test_expect_success 'check attribute list' ' + cat <<-EOF >>.gitattributes && + * whitespace=indent,trail,space + EOF + git ls-files ":(attr:whitespace=indent\,trail\,space)" >actual && + git ls-files >expect && + test_cmp expect actual +' + +test_expect_success 'backslash cannot be the last character' ' + test_must_fail git ls-files ":(attr:label=foo\\ labelA=bar)" 2>actual && + test_i18ngrep "not allowed as last character in attr value" actual +' + +test_expect_success 'backslash cannot be used as a value' ' + test_must_fail git ls-files ":(attr:label=f\\\oo)" 2>actual && + test_i18ngrep "for value matching" actual +' + +test_done diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh index a468041c50..834a9ed168 100755 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh @@ -38,6 +38,7 @@ test_atom() { case "$1" in head) ref=refs/heads/master ;; tag) ref=refs/tags/testtag ;; + sym) ref=refs/heads/sym ;; *) ref=$1 ;; esac printf '%s\n' "$3" >expected @@ -50,16 +51,40 @@ test_atom() { test_atom head refname refs/heads/master test_atom head refname:short master +test_atom head refname:lstrip=1 heads/master +test_atom head refname:lstrip=2 master +test_atom head refname:lstrip=-1 master +test_atom head refname:lstrip=-2 heads/master +test_atom head refname:rstrip=1 refs/heads +test_atom head refname:rstrip=2 refs +test_atom head refname:rstrip=-1 refs +test_atom head refname:rstrip=-2 refs/heads test_atom head refname:strip=1 heads/master test_atom head refname:strip=2 master +test_atom head refname:strip=-1 master +test_atom head refname:strip=-2 heads/master test_atom head upstream refs/remotes/origin/master test_atom head upstream:short origin/master +test_atom head upstream:lstrip=2 origin/master +test_atom head upstream:lstrip=-2 origin/master +test_atom head upstream:rstrip=2 refs/remotes +test_atom head upstream:rstrip=-2 refs/remotes +test_atom head upstream:strip=2 origin/master +test_atom head upstream:strip=-2 origin/master test_atom head push refs/remotes/myfork/master test_atom head push:short myfork/master +test_atom head push:lstrip=1 remotes/myfork/master +test_atom head push:lstrip=-1 master +test_atom head push:rstrip=1 refs/remotes/myfork +test_atom head push:rstrip=-1 refs +test_atom head push:strip=1 remotes/myfork/master +test_atom head push:strip=-1 master test_atom head objecttype commit test_atom head objectsize 171 test_atom head objectname $(git rev-parse refs/heads/master) test_atom head objectname:short $(git rev-parse --short refs/heads/master) +test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master) +test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/master) test_atom head tree $(git rev-parse refs/heads/master^{tree}) test_atom head parent '' test_atom head numparent 0 @@ -99,6 +124,8 @@ test_atom tag objecttype tag test_atom tag objectsize 154 test_atom tag objectname $(git rev-parse refs/tags/testtag) test_atom tag objectname:short $(git rev-parse --short refs/tags/testtag) +test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master) +test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/master) test_atom tag tree '' test_atom tag parent '' test_atom tag numparent '' @@ -134,16 +161,6 @@ test_expect_success 'Check invalid atoms names are errors' ' test_must_fail git for-each-ref --format="%(INVALID)" refs/heads ' -test_expect_success 'arguments to :strip must be positive integers' ' - test_must_fail git for-each-ref --format="%(refname:strip=0)" && - test_must_fail git for-each-ref --format="%(refname:strip=-1)" && - test_must_fail git for-each-ref --format="%(refname:strip=foo)" -' - -test_expect_success 'stripping refnames too far gives an error' ' - test_must_fail git for-each-ref --format="%(refname:strip=3)" -' - test_expect_success 'Check format specifiers are ignored in naming date atoms' ' git for-each-ref --format="%(authordate)" refs/heads && git for-each-ref --format="%(authordate:default) %(authordate)" refs/heads && @@ -164,6 +181,12 @@ test_expect_success 'Check invalid format specifiers are errors' ' test_must_fail git for-each-ref --format="%(authordate:INVALID)" refs/heads ' +test_expect_success 'arguments to %(objectname:short=) must be positive integers' ' + test_must_fail git for-each-ref --format="%(objectname:short=0)" && + test_must_fail git for-each-ref --format="%(objectname:short=-1)" && + test_must_fail git for-each-ref --format="%(objectname:short=foo)" +' + test_date () { f=$1 && committer_date=$2 && @@ -362,6 +385,8 @@ test_expect_success 'setup for upstream:track[short]' ' test_atom head upstream:track '[ahead 1]' test_atom head upstream:trackshort '>' +test_atom head upstream:track,nobracket 'ahead 1' +test_atom head upstream:nobracket,track 'ahead 1' test_atom head push:track '[ahead 1]' test_atom head push:trackshort '>' @@ -372,7 +397,7 @@ test_expect_success 'Check that :track[short] cannot be used with other atoms' ' test_expect_success 'Check that :track[short] works when upstream is invalid' ' cat >expected <<-\EOF && - + [gone] EOF test_when_finished "git config branch.master.merge refs/heads/master" && @@ -554,6 +579,7 @@ test_expect_success 'Verify sort with multiple keys' ' test_cmp expected actual ' + test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' ' test_when_finished "git checkout master" && git for-each-ref --format="%(HEAD) %(refname:short)" refs/heads/ >actual && @@ -588,4 +614,52 @@ test_expect_success 'basic atom: head contents:trailers' ' test_cmp expect actual.clean ' +test_expect_success 'Add symbolic ref for the following tests' ' + git symbolic-ref refs/heads/sym refs/heads/master +' + +cat >expected <<EOF +refs/heads/master +EOF + +test_expect_success 'Verify usage of %(symref) atom' ' + git for-each-ref --format="%(symref)" refs/heads/sym >actual && + test_cmp expected actual +' + +cat >expected <<EOF +heads/master +EOF + +test_expect_success 'Verify usage of %(symref:short) atom' ' + git for-each-ref --format="%(symref:short)" refs/heads/sym >actual && + test_cmp expected actual +' + +cat >expected <<EOF +master +heads/master +EOF + +test_expect_success 'Verify usage of %(symref:lstrip) atom' ' + git for-each-ref --format="%(symref:lstrip=2)" refs/heads/sym > actual && + git for-each-ref --format="%(symref:lstrip=-2)" refs/heads/sym >> actual && + test_cmp expected actual && + + git for-each-ref --format="%(symref:strip=2)" refs/heads/sym > actual && + git for-each-ref --format="%(symref:strip=-2)" refs/heads/sym >> actual && + test_cmp expected actual +' + +cat >expected <<EOF +refs +refs/heads +EOF + +test_expect_success 'Verify usage of %(symref:rstrip) atom' ' + git for-each-ref --format="%(symref:rstrip=2)" refs/heads/sym > actual && + git for-each-ref --format="%(symref:rstrip=-2)" refs/heads/sym >> actual && + test_cmp expected actual +' + test_done diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh index d0ab09f4bd..fc067ed672 100755 --- a/t/t6302-for-each-ref-filter.sh +++ b/t/t6302-for-each-ref-filter.sh @@ -93,6 +93,22 @@ test_expect_success 'filtering with --contains' ' test_cmp expect actual ' +test_expect_success 'filtering with --no-contains' ' + cat >expect <<-\EOF && + refs/tags/one + EOF + git for-each-ref --format="%(refname)" --no-contains=two >actual && + test_cmp expect actual +' + +test_expect_success 'filtering with --contains and --no-contains' ' + cat >expect <<-\EOF && + refs/tags/two + EOF + git for-each-ref --format="%(refname)" --contains=two --no-contains=three >actual && + test_cmp expect actual +' + test_expect_success '%(color) must fail' ' test_must_fail git for-each-ref --format="%(color)%(refname)" ' @@ -327,4 +343,102 @@ test_expect_success 'reverse version sort' ' test_cmp expect actual ' +test_expect_success 'improper usage of %(if), %(then), %(else) and %(end) atoms' ' + test_must_fail git for-each-ref --format="%(if)" && + test_must_fail git for-each-ref --format="%(then) %(end)" && + test_must_fail git for-each-ref --format="%(else) %(end)" && + test_must_fail git for-each-ref --format="%(if) %(else) %(end)" && + test_must_fail git for-each-ref --format="%(if) %(then) %(then) %(end)" && + test_must_fail git for-each-ref --format="%(then) %(else) %(end)" && + test_must_fail git for-each-ref --format="%(if) %(else) %(end)" && + test_must_fail git for-each-ref --format="%(if) %(then) %(else)" && + test_must_fail git for-each-ref --format="%(if) %(else) %(then) %(end)" && + test_must_fail git for-each-ref --format="%(if) %(then) %(else) %(else) %(end)" && + test_must_fail git for-each-ref --format="%(if) %(end)" +' + +test_expect_success 'check %(if)...%(then)...%(end) atoms' ' + git for-each-ref --format="%(refname)%(if)%(authorname)%(then) Author: %(authorname)%(end)" >actual && + cat >expect <<-\EOF && + refs/heads/master Author: A U Thor + refs/heads/side Author: A U Thor + refs/odd/spot Author: A U Thor + refs/tags/annotated-tag + refs/tags/doubly-annotated-tag + refs/tags/doubly-signed-tag + refs/tags/foo1.10 Author: A U Thor + refs/tags/foo1.3 Author: A U Thor + refs/tags/foo1.6 Author: A U Thor + refs/tags/four Author: A U Thor + refs/tags/one Author: A U Thor + refs/tags/signed-tag + refs/tags/three Author: A U Thor + refs/tags/two Author: A U Thor + EOF + test_cmp expect actual +' + +test_expect_success 'check %(if)...%(then)...%(else)...%(end) atoms' ' + git for-each-ref --format="%(if)%(authorname)%(then)%(authorname)%(else)No author%(end): %(refname)" >actual && + cat >expect <<-\EOF && + A U Thor: refs/heads/master + A U Thor: refs/heads/side + A U Thor: refs/odd/spot + No author: refs/tags/annotated-tag + No author: refs/tags/doubly-annotated-tag + No author: refs/tags/doubly-signed-tag + A U Thor: refs/tags/foo1.10 + A U Thor: refs/tags/foo1.3 + A U Thor: refs/tags/foo1.6 + A U Thor: refs/tags/four + A U Thor: refs/tags/one + No author: refs/tags/signed-tag + A U Thor: refs/tags/three + A U Thor: refs/tags/two + EOF + test_cmp expect actual +' +test_expect_success 'ignore spaces in %(if) atom usage' ' + git for-each-ref --format="%(refname:short): %(if)%(HEAD)%(then)Head ref%(else)Not Head ref%(end)" >actual && + cat >expect <<-\EOF && + master: Head ref + side: Not Head ref + odd/spot: Not Head ref + annotated-tag: Not Head ref + doubly-annotated-tag: Not Head ref + doubly-signed-tag: Not Head ref + foo1.10: Not Head ref + foo1.3: Not Head ref + foo1.6: Not Head ref + four: Not Head ref + one: Not Head ref + signed-tag: Not Head ref + three: Not Head ref + two: Not Head ref + EOF + test_cmp expect actual +' + +test_expect_success 'check %(if:equals=<string>)' ' + git for-each-ref --format="%(if:equals=master)%(refname:short)%(then)Found master%(else)Not master%(end)" refs/heads/ >actual && + cat >expect <<-\EOF && + Found master + Not master + EOF + test_cmp expect actual +' + +test_expect_success 'check %(if:notequals=<string>)' ' + git for-each-ref --format="%(if:notequals=master)%(refname:short)%(then)Not master%(else)Found master%(end)" refs/heads/ >actual && + cat >expect <<-\EOF && + Found master + Not master + EOF + test_cmp expect actual +' + +test_expect_success '--merged is incompatible with --no-merged' ' + test_must_fail git for-each-ref --merged HEAD --no-merged HEAD +' + test_done diff --git a/t/t6500-gc.sh b/t/t6500-gc.sh index 08de2e8ab0..cc7acd101d 100755 --- a/t/t6500-gc.sh +++ b/t/t6500-gc.sh @@ -67,6 +67,16 @@ test_expect_success 'auto gc with too many loose objects does not attempt to cre test_line_count = 2 new # There is one new pack and its .idx ' +run_and_wait_for_auto_gc () { + # We read stdout from gc for the side effect of waiting until the + # background gc process exits, closing its fd 9. Furthermore, the + # variable assignment from a command substitution preserves the + # exit status of the main gc process. + # Note: this fd trickery doesn't work on Windows, but there is no + # need to, because on Win the auto gc always runs in the foreground. + doesnt_matter=$(git gc --auto 9>&1) +} + test_expect_success 'background auto gc does not run if gc.log is present and recent but does if it is old' ' test_commit foo && test_commit bar && @@ -80,7 +90,13 @@ test_expect_success 'background auto gc does not run if gc.log is present and re test-chmtime =-345600 .git/gc.log && test_must_fail git gc --auto && test_config gc.logexpiry 2.days && - git gc --auto + run_and_wait_for_auto_gc && + ls .git/objects/pack/pack-*.pack >packs && + test_line_count = 1 packs ' +# DO NOT leave a detached auto gc process running near the end of the +# test script: it can run long enough in the background to racily +# interfere with the cleanup in 'test_done'. + test_done diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh index b8ad076c25..bb2e4d704d 100755 --- a/t/t7004-tag.sh +++ b/t/t7004-tag.sh @@ -16,7 +16,6 @@ tag_exists () { git show-ref --quiet --verify refs/tags/"$1" } -# todo: git tag -l now returns always zero, when fixed, change this test test_expect_success 'listing all tags in an empty tree should succeed' ' git tag -l && git tag @@ -81,9 +80,25 @@ test_expect_success 'creating a tag using default HEAD should succeed' ' ' test_expect_success 'creating a tag with --create-reflog should create reflog' ' + git log -1 \ + --format="format:tag: tagging %h (%s, %cd)%n" \ + --date=format:%Y-%m-%d >expected && test_when_finished "git tag -d tag_with_reflog" && git tag --create-reflog tag_with_reflog && - git reflog exists refs/tags/tag_with_reflog + git reflog exists refs/tags/tag_with_reflog && + sed -e "s/^.* //" .git/logs/refs/tags/tag_with_reflog >actual && + test_cmp expected actual +' + +test_expect_success 'annotated tag with --create-reflog has correct message' ' + git log -1 \ + --format="format:tag: tagging %h (%s, %cd)%n" \ + --date=format:%Y-%m-%d >expected && + test_when_finished "git tag -d tag_with_reflog" && + git tag -m "annotated tag" --create-reflog tag_with_reflog && + git reflog exists refs/tags/tag_with_reflog && + sed -e "s/^.* //" .git/logs/refs/tags/tag_with_reflog >actual && + test_cmp expected actual ' test_expect_success '--create-reflog does not create reflog on failure' ' @@ -103,6 +118,18 @@ test_expect_success 'listing all tags if one exists should succeed' ' git tag ' +cat >expect <<EOF +mytag +EOF +test_expect_success 'Multiple -l or --list options are equivalent to one -l option' ' + git tag -l -l >actual && + test_cmp expect actual && + git tag --list --list >actual && + test_cmp expect actual && + git tag --list -l --list >actual && + test_cmp expect actual +' + test_expect_success 'listing all tags if one exists should output that tag' ' test $(git tag -l) = mytag && test $(git tag) = mytag @@ -120,9 +147,8 @@ test_expect_success \ 'listing a tag using a matching pattern should output that tag' \ 'test $(git tag -l mytag) = mytag' -# todo: git tag -l now returns always zero, when fixed, change this test test_expect_success \ - 'listing tags using a non-matching pattern should suceed' \ + 'listing tags using a non-matching pattern should succeed' \ 'git tag -l xxx' test_expect_success \ @@ -322,6 +348,19 @@ test_expect_success 'tag -l can accept multiple patterns' ' test_cmp expect actual ' +# Between v1.7.7 & v2.13.0 a fair reading of the git-tag documentation +# could leave you with the impression that "-l <pattern> -l <pattern>" +# was how we wanted to accept multiple patterns. +# +# This test should not imply that this is a sane thing to support. but +# since the documentation was worded like it was let's at least find +# out if we're going to break this long-documented form of taking +# multiple patterns. +test_expect_success 'tag -l <pattern> -l <pattern> works, as our buggy documentation previously suggested' ' + git tag -l "v1*" -l "v0*" >actual && + test_cmp expect actual +' + test_expect_success 'listing tags in column' ' COLUMNS=40 git tag -l --column=row >actual && cat >expected <<\EOF && @@ -604,6 +643,11 @@ test_expect_success \ git tag -n0 -l tag-one-line >actual && test_cmp expect actual && + git tag -n0 | grep "^tag-one-line" >actual && + test_cmp expect actual && + git tag -n0 tag-one-line >actual && + test_cmp expect actual && + echo "tag-one-line A msg" >expect && git tag -n1 -l | grep "^tag-one-line" >actual && test_cmp expect actual && @@ -617,6 +661,17 @@ test_expect_success \ test_cmp expect actual ' +test_expect_success 'The -n 100 invocation means -n --list 100, not -n100' ' + >expect && + git tag -n 100 >actual && + test_cmp expect actual && + + git tag -m "A msg" 100 && + echo "100 A msg" >expect && + git tag -n 100 >actual && + test_cmp expect actual +' + test_expect_success \ 'listing the zero-lines message of a non-signed tag should succeed' ' git tag -m "" tag-zero-lines && @@ -1367,6 +1422,23 @@ test_expect_success 'checking that first commit is in all tags (relative)' " test_cmp expected actual " +# All the --contains tests above, but with --no-contains +test_expect_success 'checking that first commit is not listed in any tag with --no-contains (hash)' " + >expected && + git tag -l --no-contains $hash1 v* >actual && + test_cmp expected actual +" + +test_expect_success 'checking that first commit is in all tags (tag)' " + git tag -l --no-contains v1.0 v* >actual && + test_cmp expected actual +" + +test_expect_success 'checking that first commit is in all tags (relative)' " + git tag -l --no-contains HEAD~2 v* >actual && + test_cmp expected actual +" + cat > expected <<EOF v2.0 EOF @@ -1376,6 +1448,17 @@ test_expect_success 'checking that second commit only has one tag' " test_cmp expected actual " +cat > expected <<EOF +v0.2.1 +v1.0 +v1.0.1 +v1.1.3 +EOF + +test_expect_success 'inverse of the last test, with --no-contains' " + git tag -l --no-contains $hash2 v* >actual && + test_cmp expected actual +" cat > expected <<EOF EOF @@ -1385,6 +1468,19 @@ test_expect_success 'checking that third commit has no tags' " test_cmp expected actual " +cat > expected <<EOF +v0.2.1 +v1.0 +v1.0.1 +v1.1.3 +v2.0 +EOF + +test_expect_success 'conversely --no-contains on the third commit lists all tags' " + git tag -l --no-contains $hash3 v* >actual && + test_cmp expected actual +" + # how about a simple merge? test_expect_success 'creating simple branch' ' @@ -1406,6 +1502,19 @@ test_expect_success 'checking that branch head only has one tag' " test_cmp expected actual " +cat > expected <<EOF +v0.2.1 +v1.0 +v1.0.1 +v1.1.3 +v2.0 +EOF + +test_expect_success 'checking that branch head with --no-contains lists all but one tag' " + git tag -l --no-contains $hash4 v* >actual && + test_cmp expected actual +" + test_expect_success 'merging original branch into this branch' ' git merge --strategy=ours master && git tag v4.0 @@ -1427,6 +1536,20 @@ v1.0.1 v1.1.3 v2.0 v3.0 +EOF + +test_expect_success 'checking that original branch head with --no-contains lists all but one tag now' " + git tag -l --no-contains $hash3 v* >actual && + test_cmp expected actual +" + +cat > expected <<EOF +v0.2.1 +v1.0 +v1.0.1 +v1.1.3 +v2.0 +v3.0 v4.0 EOF @@ -1435,21 +1558,76 @@ test_expect_success 'checking that initial commit is in all tags' " test_cmp expected actual " +test_expect_success 'checking that --contains can be used in non-list mode' ' + git tag --contains $hash1 v* >actual && + test_cmp expected actual +' + +test_expect_success 'checking that initial commit is in all tags with --no-contains' " + >expected && + git tag -l --no-contains $hash1 v* >actual && + test_cmp expected actual +" + # mixing modes and options: test_expect_success 'mixing incompatibles modes and options is forbidden' ' test_must_fail git tag -a && + test_must_fail git tag -a -l && + test_must_fail git tag -s && + test_must_fail git tag -s -l && + test_must_fail git tag -m && + test_must_fail git tag -m -l && + test_must_fail git tag -m "hlagh" && + test_must_fail git tag -m "hlagh" -l && + test_must_fail git tag -F && + test_must_fail git tag -F -l && + test_must_fail git tag -f && + test_must_fail git tag -f -l && + test_must_fail git tag -a -s -m -F && + test_must_fail git tag -a -s -m -F -l && test_must_fail git tag -l -v && - test_must_fail git tag -n 100 && + test_must_fail git tag -l -d && + test_must_fail git tag -l -v -d && + test_must_fail git tag -n 100 -v && test_must_fail git tag -l -m msg && test_must_fail git tag -l -F some file && - test_must_fail git tag -v -s -' + test_must_fail git tag -v -s && + test_must_fail git tag --contains tag-tree && + test_must_fail git tag --contains tag-blob && + test_must_fail git tag --no-contains tag-tree && + test_must_fail git tag --no-contains tag-blob && + test_must_fail git tag --contains --no-contains && + test_must_fail git tag --no-with HEAD && + test_must_fail git tag --no-without HEAD +' + +for option in --contains --with --no-contains --without --merged --no-merged --points-at +do + test_expect_success "mixing incompatible modes with $option is forbidden" " + test_must_fail git tag -d $option HEAD && + test_must_fail git tag -d $option HEAD some-tag && + test_must_fail git tag -v $option HEAD + " + test_expect_success "Doing 'git tag --list-like $option <commit> <pattern> is permitted" " + git tag -n $option HEAD HEAD && + git tag $option HEAD HEAD && + git tag $option + " +done # check points-at -test_expect_success '--points-at cannot be used in non-list mode' ' - test_must_fail git tag --points-at=v4.0 foo +test_expect_success '--points-at can be used in non-list mode' ' + echo v4.0 >expect && + git tag --points-at=v4.0 "v*" >actual && + test_cmp expect actual +' + +test_expect_success '--points-at is a synonym for --points-at HEAD' ' + echo v4.0 >expect && + git tag --points-at >actual && + test_cmp expect actual ' test_expect_success '--points-at finds lightweight tags' ' @@ -1691,7 +1869,7 @@ run_with_limited_stack () { test_lazy_prereq ULIMIT_STACK_SIZE 'run_with_limited_stack true' # we require ulimit, this excludes Windows -test_expect_success ULIMIT_STACK_SIZE '--contains works in a deep repo' ' +test_expect_success ULIMIT_STACK_SIZE '--contains and --no-contains work in a deep repo' ' >expect && i=1 && while test $i -lt 8000 @@ -1707,7 +1885,9 @@ EOF" git checkout master && git tag far-far-away HEAD^ && run_with_limited_stack git tag --contains HEAD >actual && - test_cmp expect actual + test_cmp expect actual && + run_with_limited_stack git tag --no-contains HEAD >actual && + test_line_count ">" 10 actual ' test_expect_success '--format should list tags as per format given' ' @@ -1726,8 +1906,17 @@ test_expect_success 'setup --merged test tags' ' git tag mergetest-3 HEAD ' -test_expect_success '--merged cannot be used in non-list mode' ' - test_must_fail git tag --merged=mergetest-2 foo +test_expect_success '--merged can be used in non-list mode' ' + cat >expect <<-\EOF && + mergetest-1 + mergetest-2 + EOF + git tag --merged=mergetest-2 "mergetest*" >actual && + test_cmp expect actual +' + +test_expect_success '--merged is incompatible with --no-merged' ' + test_must_fail git tag --merged HEAD --no-merged HEAD ' test_expect_success '--merged shows merged tags' ' @@ -1747,6 +1936,11 @@ test_expect_success '--no-merged show unmerged tags' ' test_cmp expect actual ' +test_expect_success '--no-merged can be used in non-list mode' ' + git tag --no-merged=mergetest-2 mergetest-* >actual && + test_cmp expect actual +' + test_expect_success 'ambiguous branch/tags not marked' ' git tag ambiguous && git branch ambiguous && @@ -1755,4 +1949,47 @@ test_expect_success 'ambiguous branch/tags not marked' ' test_cmp expect actual ' +test_expect_success '--contains combined with --no-contains' ' + ( + git init no-contains && + cd no-contains && + test_commit v0.1 && + test_commit v0.2 && + test_commit v0.3 && + test_commit v0.4 && + test_commit v0.5 && + cat >expected <<-\EOF && + v0.2 + v0.3 + v0.4 + EOF + git tag --contains v0.2 --no-contains v0.5 >actual && + test_cmp expected actual + ) +' + +# As the docs say, list tags which contain a specified *commit*. We +# don't recurse down to tags for trees or blobs pointed to by *those* +# commits. +test_expect_success 'Does --[no-]contains stop at commits? Yes!' ' + cd no-contains && + blob=$(git rev-parse v0.3:v0.3.t) && + tree=$(git rev-parse v0.3^{tree}) && + git tag tag-blob $blob && + git tag tag-tree $tree && + git tag --contains v0.3 >actual && + cat >expected <<-\EOF && + v0.3 + v0.4 + v0.5 + EOF + test_cmp expected actual && + git tag --no-contains v0.3 >actual && + cat >expected <<-\EOF && + v0.1 + v0.2 + EOF + test_cmp expected actual +' + test_done diff --git a/t/t7006-pager.sh b/t/t7006-pager.sh index c8dc665f2f..4f3794d415 100755 --- a/t/t7006-pager.sh +++ b/t/t7006-pager.sh @@ -360,27 +360,37 @@ test_pager_choices 'git aliasedlog' test_default_pager expect_success 'git -p aliasedlog' test_PAGER_overrides expect_success 'git -p aliasedlog' test_core_pager_overrides expect_success 'git -p aliasedlog' -test_core_pager_subdir expect_failure 'git -p aliasedlog' +test_core_pager_subdir expect_success 'git -p aliasedlog' test_GIT_PAGER_overrides expect_success 'git -p aliasedlog' test_default_pager expect_success 'git -p true' test_PAGER_overrides expect_success 'git -p true' test_core_pager_overrides expect_success 'git -p true' -test_core_pager_subdir expect_failure 'git -p true' +test_core_pager_subdir expect_success 'git -p true' test_GIT_PAGER_overrides expect_success 'git -p true' test_default_pager expect_success test_must_fail 'git -p request-pull' test_PAGER_overrides expect_success test_must_fail 'git -p request-pull' test_core_pager_overrides expect_success test_must_fail 'git -p request-pull' -test_core_pager_subdir expect_failure test_must_fail 'git -p request-pull' +test_core_pager_subdir expect_success test_must_fail 'git -p request-pull' test_GIT_PAGER_overrides expect_success test_must_fail 'git -p request-pull' test_default_pager expect_success test_must_fail 'git -p' test_PAGER_overrides expect_success test_must_fail 'git -p' test_local_config_ignored expect_failure test_must_fail 'git -p' -test_no_local_config_subdir expect_success test_must_fail 'git -p' test_GIT_PAGER_overrides expect_success test_must_fail 'git -p' +test_expect_success TTY 'core.pager in repo config works and retains cwd' ' + sane_unset GIT_PAGER && + test_config core.pager "cat >cwd-retained" && + ( + cd sub && + rm -f cwd-retained && + test_terminal git -p rev-parse HEAD && + test_path_is_file cwd-retained + ) +' + test_doesnt_paginate expect_failure test_must_fail 'git -p nonsense' test_pager_choices 'git shortlog' diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh index c09ce0d4c1..cf77a3a357 100755 --- a/t/t7400-submodule-basic.sh +++ b/t/t7400-submodule-basic.sh @@ -1130,5 +1130,141 @@ test_expect_success 'submodule helper list is not confused by common prefixes' ' test_cmp expect actual ' +test_expect_success 'setup superproject with submodules' ' + git init sub1 && + test_commit -C sub1 test && + test_commit -C sub1 test2 && + git init multisuper && + git -C multisuper submodule add ../sub1 sub0 && + git -C multisuper submodule add ../sub1 sub1 && + git -C multisuper submodule add ../sub1 sub2 && + git -C multisuper submodule add ../sub1 sub3 && + git -C multisuper commit -m "add some submodules" +' + +cat >expect <<-EOF +-sub0 + sub1 (test2) + sub2 (test2) + sub3 (test2) +EOF + +test_expect_success 'submodule update --init with a specification' ' + test_when_finished "rm -rf multisuper_clone" && + pwd=$(pwd) && + git clone file://"$pwd"/multisuper multisuper_clone && + git -C multisuper_clone submodule update --init . ":(exclude)sub0" && + git -C multisuper_clone submodule status |cut -c 1,43- >actual && + test_cmp expect actual +' + +test_expect_success 'submodule update --init with submodule.active set' ' + test_when_finished "rm -rf multisuper_clone" && + pwd=$(pwd) && + git clone file://"$pwd"/multisuper multisuper_clone && + git -C multisuper_clone config submodule.active "." && + git -C multisuper_clone config --add submodule.active ":(exclude)sub0" && + git -C multisuper_clone submodule update --init && + git -C multisuper_clone submodule status |cut -c 1,43- >actual && + test_cmp expect actual +' + +test_expect_success 'submodule update and setting submodule.<name>.active' ' + test_when_finished "rm -rf multisuper_clone" && + pwd=$(pwd) && + git clone file://"$pwd"/multisuper multisuper_clone && + git -C multisuper_clone config --bool submodule.sub0.active "true" && + git -C multisuper_clone config --bool submodule.sub1.active "false" && + git -C multisuper_clone config --bool submodule.sub2.active "true" && + + cat >expect <<-\EOF && + sub0 (test2) + -sub1 + sub2 (test2) + -sub3 + EOF + git -C multisuper_clone submodule update && + git -C multisuper_clone submodule status |cut -c 1,43- >actual && + test_cmp expect actual +' + +test_expect_success 'clone --recurse-submodules with a pathspec works' ' + test_when_finished "rm -rf multisuper_clone" && + cat >expected <<-\EOF && + sub0 (test2) + -sub1 + -sub2 + -sub3 + EOF + + git clone --recurse-submodules="sub0" multisuper multisuper_clone && + git -C multisuper_clone submodule status |cut -c1,43- >actual && + test_cmp actual expected +' + +test_expect_success 'clone with multiple --recurse-submodules options' ' + test_when_finished "rm -rf multisuper_clone" && + cat >expect <<-\EOF && + -sub0 + sub1 (test2) + -sub2 + sub3 (test2) + EOF + + git clone --recurse-submodules="." \ + --recurse-submodules=":(exclude)sub0" \ + --recurse-submodules=":(exclude)sub2" \ + multisuper multisuper_clone && + git -C multisuper_clone submodule status |cut -c1,43- >actual && + test_cmp expect actual +' + +test_expect_success 'clone and subsequent updates correctly auto-initialize submodules' ' + test_when_finished "rm -rf multisuper_clone" && + cat <<-\EOF >expect && + -sub0 + sub1 (test2) + -sub2 + sub3 (test2) + EOF + + cat <<-\EOF >expect2 && + -sub0 + sub1 (test2) + -sub2 + sub3 (test2) + -sub4 + sub5 (test2) + EOF + + git clone --recurse-submodules="." \ + --recurse-submodules=":(exclude)sub0" \ + --recurse-submodules=":(exclude)sub2" \ + --recurse-submodules=":(exclude)sub4" \ + multisuper multisuper_clone && + + git -C multisuper_clone submodule status |cut -c1,43- >actual && + test_cmp expect actual && + + git -C multisuper submodule add ../sub1 sub4 && + git -C multisuper submodule add ../sub1 sub5 && + git -C multisuper commit -m "add more submodules" && + # obtain the new superproject + git -C multisuper_clone pull && + git -C multisuper_clone submodule update --init && + git -C multisuper_clone submodule status |cut -c1,43- >actual && + test_cmp expect2 actual +' + +test_expect_success 'init properly sets the config' ' + test_when_finished "rm -rf multisuper_clone" && + git clone --recurse-submodules="." \ + --recurse-submodules=":(exclude)sub0" \ + multisuper multisuper_clone && + + git -C multisuper_clone submodule init -- sub0 sub1 && + git -C multisuper_clone config --get submodule.sub0.active && + test_must_fail git -C multisuper_clone config --get submodule.sub1.active +' test_done diff --git a/t/t7413-submodule-is-active.sh b/t/t7413-submodule-is-active.sh new file mode 100755 index 0000000000..9c785b07ec --- /dev/null +++ b/t/t7413-submodule-is-active.sh @@ -0,0 +1,107 @@ +#!/bin/sh + +test_description='Test submodule--helper is-active + +This test verifies that `git submodue--helper is-active` correclty identifies +submodules which are "active" and interesting to the user. +' + +. ./test-lib.sh + +test_expect_success 'setup' ' + git init sub && + test_commit -C sub initial && + git init super && + test_commit -C super initial && + git -C super submodule add ../sub sub1 && + git -C super submodule add ../sub sub2 && + + # Remove submodule.<name>.active entries in order to test in an + # environment where only URLs are present in the conifg + git -C super config --unset submodule.sub1.active && + git -C super config --unset submodule.sub2.active && + + git -C super commit -a -m "add 2 submodules at sub{1,2}" +' + +test_expect_success 'is-active works with urls' ' + git -C super submodule--helper is-active sub1 && + git -C super submodule--helper is-active sub2 && + + git -C super config --unset submodule.sub1.URL && + test_must_fail git -C super submodule--helper is-active sub1 && + git -C super config submodule.sub1.URL ../sub && + git -C super submodule--helper is-active sub1 +' + +test_expect_success 'is-active works with submodule.<name>.active config' ' + test_when_finished "git -C super config --unset submodule.sub1.active" && + test_when_finished "git -C super config submodule.sub1.URL ../sub" && + + git -C super config --bool submodule.sub1.active "false" && + test_must_fail git -C super submodule--helper is-active sub1 && + + git -C super config --bool submodule.sub1.active "true" && + git -C super config --unset submodule.sub1.URL && + git -C super submodule--helper is-active sub1 +' + +test_expect_success 'is-active works with basic submodule.active config' ' + test_when_finished "git -C super config submodule.sub1.URL ../sub" && + test_when_finished "git -C super config --unset-all submodule.active" && + + git -C super config --add submodule.active "." && + git -C super config --unset submodule.sub1.URL && + + git -C super submodule--helper is-active sub1 && + git -C super submodule--helper is-active sub2 +' + +test_expect_success 'is-active correctly works with paths that are not submodules' ' + test_when_finished "git -C super config --unset-all submodule.active" && + + test_must_fail git -C super submodule--helper is-active not-a-submodule && + + git -C super config --add submodule.active "." && + test_must_fail git -C super submodule--helper is-active not-a-submodule +' + +test_expect_success 'is-active works with exclusions in submodule.active config' ' + test_when_finished "git -C super config --unset-all submodule.active" && + + git -C super config --add submodule.active "." && + git -C super config --add submodule.active ":(exclude)sub1" && + + test_must_fail git -C super submodule--helper is-active sub1 && + git -C super submodule--helper is-active sub2 +' + +test_expect_success 'is-active with submodule.active and submodule.<name>.active' ' + test_when_finished "git -C super config --unset-all submodule.active" && + test_when_finished "git -C super config --unset submodule.sub1.active" && + test_when_finished "git -C super config --unset submodule.sub2.active" && + + git -C super config --add submodule.active "sub1" && + git -C super config --bool submodule.sub1.active "false" && + git -C super config --bool submodule.sub2.active "true" && + + test_must_fail git -C super submodule--helper is-active sub1 && + git -C super submodule--helper is-active sub2 +' + +test_expect_success 'is-active, submodule.active and submodule add' ' + test_when_finished "rm -rf super2" && + git init super2 && + test_commit -C super2 initial && + git -C super2 config --add submodule.active "sub*" && + + # submodule add should only add submodule.<name>.active + # to the config if not matched by the pathspec + git -C super2 submodule add ../sub sub1 && + test_must_fail git -C super2 config --get submodule.sub1.active && + + git -C super2 submodule add ../sub mod && + git -C super2 config --get submodule.mod.active +' + +test_done diff --git a/t/t7504-commit-msg-hook.sh b/t/t7504-commit-msg-hook.sh index 8728db61d3..88d4cda299 100755 --- a/t/t7504-commit-msg-hook.sh +++ b/t/t7504-commit-msg-hook.sh @@ -220,4 +220,21 @@ test_expect_success "hook doesn't edit commit message (editor)" ' ' +# set up fake editor to replace `pick` by `reword` +cat > reword-editor <<'EOF' +#!/bin/sh +mv "$1" "$1".bup && +sed 's/^pick/reword/' <"$1".bup >"$1" +EOF +chmod +x reword-editor +REWORD_EDITOR="$(pwd)/reword-editor" +export REWORD_EDITOR + +test_expect_success 'hook is called for reword during `rebase -i`' ' + + GIT_SEQUENCE_EDITOR="\"$REWORD_EDITOR\"" git rebase -i HEAD^ && + commit_msg_is "new message" + +' + test_done diff --git a/t/t7506-status-submodule.sh b/t/t7506-status-submodule.sh index d31b34da83..055c90736e 100755 --- a/t/t7506-status-submodule.sh +++ b/t/t7506-status-submodule.sh @@ -17,6 +17,12 @@ test_create_repo_with_commit () { ) } +sanitize_output () { + sed -e "s/$_x40/HASH/" -e "s/$_x40/HASH/" output >output2 && + mv output2 output +} + + test_expect_success 'setup' ' test_create_repo_with_commit sub && echo output > .gitignore && @@ -50,6 +56,15 @@ test_expect_success 'status with modified file in submodule (porcelain)' ' EOF ' +test_expect_success 'status with modified file in submodule (short)' ' + (cd sub && git reset --hard) && + echo "changed" >sub/foo && + git status --short >output && + diff output - <<-\EOF + m sub + EOF +' + test_expect_success 'status with added file in submodule' ' (cd sub && git reset --hard && echo >foo && git add foo) && git status >output && @@ -64,6 +79,14 @@ test_expect_success 'status with added file in submodule (porcelain)' ' EOF ' +test_expect_success 'status with added file in submodule (short)' ' + (cd sub && git reset --hard && echo >foo && git add foo) && + git status --short >output && + diff output - <<-\EOF + m sub + EOF +' + test_expect_success 'status with untracked file in submodule' ' (cd sub && git reset --hard) && echo "content" >sub/new-file && @@ -83,6 +106,13 @@ test_expect_success 'status with untracked file in submodule (porcelain)' ' EOF ' +test_expect_success 'status with untracked file in submodule (short)' ' + git status --short >output && + diff output - <<-\EOF + ? sub + EOF +' + test_expect_success 'status with added and untracked file in submodule' ' (cd sub && git reset --hard && echo >foo && git add foo) && echo "content" >sub/new-file && @@ -177,8 +207,24 @@ test_expect_success 'status with added file in modified submodule with .git file test_i18ngrep "modified: sub (new commits, modified content)" output ' +test_expect_success 'status with a lot of untracked files in the submodule' ' + ( + cd sub + i=0 && + while test $i -lt 1024 + do + >some-file-$i + i=$(( $i + 1 )) + done + ) && + git status --porcelain sub 2>err.actual && + test_must_be_empty err.actual && + rm err.actual +' + test_expect_success 'rm submodule contents' ' - rm -rf sub/* sub/.git + rm -rf sub && + mkdir sub ' test_expect_success 'status clean (empty submodule dir)' ' @@ -271,4 +317,91 @@ test_expect_success 'diff --submodule with merge conflict in .gitmodules' ' test_cmp diff_submodule_actual diff_submodule_expect ' +# We'll setup different cases for further testing: +# sub1 will contain a nested submodule, +# sub2 will have an untracked file +# sub3 will have an untracked repository +test_expect_success 'setup superproject with untracked file in nested submodule' ' + ( + cd super && + git clean -dfx && + rm .gitmodules && + git submodule add -f ./sub1 && + git submodule add -f ./sub2 && + git submodule add -f ./sub1 sub3 && + git commit -a -m "messy merge in superproject" && + ( + cd sub1 && + git submodule add ../sub2 && + git commit -a -m "add sub2 to sub1" + ) && + git add sub1 && + git commit -a -m "update sub1 to contain nested sub" + ) && + echo content >super/sub1/sub2/file && + echo content >super/sub2/file && + git -C super/sub3 clone ../../sub2 untracked_repository +' + +test_expect_success 'status with untracked file in nested submodule (porcelain)' ' + git -C super status --porcelain >output && + diff output - <<-\EOF + M sub1 + M sub2 + M sub3 + EOF +' + +test_expect_success 'status with untracked file in nested submodule (porcelain=2)' ' + git -C super status --porcelain=2 >output && + sanitize_output output && + diff output - <<-\EOF + 1 .M S..U 160000 160000 160000 HASH HASH sub1 + 1 .M S..U 160000 160000 160000 HASH HASH sub2 + 1 .M S..U 160000 160000 160000 HASH HASH sub3 + EOF +' + +test_expect_success 'status with untracked file in nested submodule (short)' ' + git -C super status --short >output && + diff output - <<-\EOF + ? sub1 + ? sub2 + ? sub3 + EOF +' + +test_expect_success 'setup superproject with modified file in nested submodule' ' + git -C super/sub1/sub2 add file && + git -C super/sub2 add file +' + +test_expect_success 'status with added file in nested submodule (porcelain)' ' + git -C super status --porcelain >output && + diff output - <<-\EOF + M sub1 + M sub2 + M sub3 + EOF +' + +test_expect_success 'status with added file in nested submodule (porcelain=2)' ' + git -C super status --porcelain=2 >output && + sanitize_output output && + diff output - <<-\EOF + 1 .M S.M. 160000 160000 160000 HASH HASH sub1 + 1 .M S.M. 160000 160000 160000 HASH HASH sub2 + 1 .M S..U 160000 160000 160000 HASH HASH sub3 + EOF +' + +test_expect_success 'status with added file in nested submodule (short)' ' + git -C super status --short >output && + diff output - <<-\EOF + m sub1 + m sub2 + ? sub3 + EOF +' + test_done diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh index 25241f4096..7f09867478 100755 --- a/t/t7800-difftool.sh +++ b/t/t7800-difftool.sh @@ -393,6 +393,25 @@ test_expect_success 'setup change in subdirectory' ' git commit -m "modified both" ' +test_expect_success 'difftool -d with growing paths' ' + a=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa && + git init growing && + ( + cd growing && + echo "test -f \"\$2/b\"" | write_script .git/test-for-b.sh && + one=$(printf 1 | git hash-object -w --stdin) && + two=$(printf 2 | git hash-object -w --stdin) && + git update-index --add \ + --cacheinfo 100644,$one,$a --cacheinfo 100644,$two,b && + tree1=$(git write-tree) && + git update-index --add \ + --cacheinfo 100644,$two,$a --cacheinfo 100644,$one,b && + tree2=$(git write-tree) && + git checkout -- $a && + git difftool -d --extcmd .git/test-for-b.sh $tree1 $tree2 + ) +' + run_dir_diff_test () { test_expect_success "$1 --no-symlinks" " symlinks=--no-symlinks && @@ -428,7 +447,7 @@ run_dir_diff_test 'difftool --dir-diff branch from subdirectory' ' git difftool --dir-diff $symlinks --extcmd ls branch >output && # "sub" must only exist in "right" # "file" and "file2" must be listed in both "left" and "right" - grep sub output > sub-output && + grep sub output >sub-output && test_line_count = 1 sub-output && grep file"$" output >file-output && test_line_count = 2 file-output && @@ -591,6 +610,7 @@ test_expect_success 'difftool --no-symlinks detects conflict ' ' ' test_expect_success 'difftool properly honors gitlink and core.worktree' ' + test_when_finished rm -rf submod/ule && git submodule add ./. submod/ule && test_config -C submod/ule diff.tool checktrees && test_config -C submod/ule difftool.checktrees.cmd '\'' @@ -600,11 +620,13 @@ test_expect_success 'difftool properly honors gitlink and core.worktree' ' cd submod/ule && echo good >expect && git difftool --tool=checktrees --dir-diff HEAD~ >actual && - test_cmp expect actual + test_cmp expect actual && + rm -f expect actual ) ' test_expect_success SYMLINKS 'difftool --dir-diff symlinked directories' ' + test_when_finished git reset --hard && git init dirlinks && ( cd dirlinks && @@ -623,4 +645,64 @@ test_expect_success SYMLINKS 'difftool --dir-diff symlinked directories' ' ) ' +test_expect_success SYMLINKS 'difftool --dir-diff handles modified symlinks' ' + test_when_finished git reset --hard && + touch b && + ln -s b c && + git add b c && + test_tick && + git commit -m initial && + touch d && + rm c && + ln -s d c && + cat >expect <<-EOF && + b + c + + c + EOF + git difftool --symlinks --dir-diff --extcmd ls >output && + grep -v ^/ output >actual && + test_cmp expect actual && + + git difftool --no-symlinks --dir-diff --extcmd ls >output && + grep -v ^/ output >actual && + test_cmp expect actual && + + # The left side contains symlink "c" that points to "b" + test_config difftool.cat.cmd "cat \$LOCAL/c" && + printf "%s\n" b >expect && + + git difftool --symlinks --dir-diff --tool cat >actual && + test_cmp expect actual && + + git difftool --symlinks --no-symlinks --dir-diff --tool cat >actual && + test_cmp expect actual && + + # The right side contains symlink "c" that points to "d" + test_config difftool.cat.cmd "cat \$REMOTE/c" && + printf "%s\n" d >expect && + + git difftool --symlinks --dir-diff --tool cat >actual && + test_cmp expect actual && + + git difftool --no-symlinks --dir-diff --tool cat >actual && + test_cmp expect actual && + + # Deleted symlinks + rm -f c && + cat >expect <<-EOF && + b + c + + EOF + git difftool --symlinks --dir-diff --extcmd ls >output && + grep -v ^/ output >actual && + test_cmp expect actual && + + git difftool --no-symlinks --dir-diff --extcmd ls >output && + grep -v ^/ output >actual && + test_cmp expect actual +' + test_done diff --git a/t/t7814-grep-recurse-submodules.sh b/t/t7814-grep-recurse-submodules.sh index 67247a01d6..5b6eb3a65e 100755 --- a/t/t7814-grep-recurse-submodules.sh +++ b/t/t7814-grep-recurse-submodules.sh @@ -227,6 +227,81 @@ test_expect_success 'grep history with moved submoules' ' test_cmp expect actual ' +test_expect_success 'grep using relative path' ' + test_when_finished "rm -rf parent sub" && + git init sub && + echo "foobar" >sub/file && + git -C sub add file && + git -C sub commit -m "add file" && + + git init parent && + echo "foobar" >parent/file && + git -C parent add file && + mkdir parent/src && + echo "foobar" >parent/src/file2 && + git -C parent add src/file2 && + git -C parent submodule add ../sub && + git -C parent commit -m "add files and submodule" && + + # From top works + cat >expect <<-\EOF && + file:foobar + src/file2:foobar + sub/file:foobar + EOF + git -C parent grep --recurse-submodules -e "foobar" >actual && + test_cmp expect actual && + + # Relative path to top + cat >expect <<-\EOF && + ../file:foobar + file2:foobar + ../sub/file:foobar + EOF + git -C parent/src grep --recurse-submodules -e "foobar" -- .. >actual && + test_cmp expect actual && + + # Relative path to submodule + cat >expect <<-\EOF && + ../sub/file:foobar + EOF + git -C parent/src grep --recurse-submodules -e "foobar" -- ../sub >actual && + test_cmp expect actual +' + +test_expect_success 'grep from a subdir' ' + test_when_finished "rm -rf parent sub" && + git init sub && + echo "foobar" >sub/file && + git -C sub add file && + git -C sub commit -m "add file" && + + git init parent && + mkdir parent/src && + echo "foobar" >parent/src/file && + git -C parent add src/file && + git -C parent submodule add ../sub src/sub && + git -C parent submodule add ../sub sub && + git -C parent commit -m "add files and submodules" && + + # Verify grep from root works + cat >expect <<-\EOF && + src/file:foobar + src/sub/file:foobar + sub/file:foobar + EOF + git -C parent grep --recurse-submodules -e "foobar" >actual && + test_cmp expect actual && + + # Verify grep from a subdir works + cat >expect <<-\EOF && + file:foobar + sub/file:foobar + EOF + git -C parent/src grep --recurse-submodules -e "foobar" >actual && + test_cmp expect actual +' + test_incompatible_with_recurse_submodules () { test_expect_success "--recurse-submodules and $1 are incompatible" " diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh index a34e55f874..5ed28135be 100755 --- a/t/t9902-completion.sh +++ b/t/t9902-completion.sh @@ -98,7 +98,7 @@ test_gitcomp () { local -a COMPREPLY && sed -e 's/Z$//' >expected && - cur="$1" && + local cur="$1" && shift && __gitcomp "$@" && print_comp && @@ -113,7 +113,7 @@ test_gitcomp_nl () { local -a COMPREPLY && sed -e 's/Z$//' >expected && - cur="$1" && + local cur="$1" && shift && __gitcomp_nl "$@" && print_comp && @@ -124,140 +124,296 @@ invalid_variable_name='${foo.bar}' actual="$TRASH_DIRECTORY/actual" -test_expect_success 'setup for __gitdir tests' ' +if test_have_prereq MINGW +then + ROOT="$(pwd -W)" +else + ROOT="$(pwd)" +fi + +test_expect_success 'setup for __git_find_repo_path/__gitdir tests' ' mkdir -p subdir/subsubdir && + mkdir -p non-repo && git init otherrepo ' -test_expect_success '__gitdir - from command line (through $__git_dir)' ' - echo "$TRASH_DIRECTORY/otherrepo/.git" >expected && +test_expect_success '__git_find_repo_path - from command line (through $__git_dir)' ' + echo "$ROOT/otherrepo/.git" >expected && ( - __git_dir="$TRASH_DIRECTORY/otherrepo/.git" && - __gitdir >"$actual" + __git_dir="$ROOT/otherrepo/.git" && + __git_find_repo_path && + echo "$__git_repo_path" >"$actual" ) && test_cmp expected "$actual" ' -test_expect_success '__gitdir - repo as argument' ' - echo "otherrepo/.git" >expected && - __gitdir "otherrepo" >"$actual" && - test_cmp expected "$actual" -' - -test_expect_success '__gitdir - remote as argument' ' - echo "remote" >expected && - __gitdir "remote" >"$actual" && - test_cmp expected "$actual" -' - -test_expect_success '__gitdir - .git directory in cwd' ' +test_expect_success '__git_find_repo_path - .git directory in cwd' ' echo ".git" >expected && - __gitdir >"$actual" && + ( + __git_find_repo_path && + echo "$__git_repo_path" >"$actual" + ) && test_cmp expected "$actual" ' -test_expect_success '__gitdir - .git directory in parent' ' - echo "$(pwd -P)/.git" >expected && +test_expect_success '__git_find_repo_path - .git directory in parent' ' + echo "$ROOT/.git" >expected && ( cd subdir/subsubdir && - __gitdir >"$actual" + __git_find_repo_path && + echo "$__git_repo_path" >"$actual" ) && test_cmp expected "$actual" ' -test_expect_success '__gitdir - cwd is a .git directory' ' +test_expect_success '__git_find_repo_path - cwd is a .git directory' ' echo "." >expected && ( cd .git && - __gitdir >"$actual" + __git_find_repo_path && + echo "$__git_repo_path" >"$actual" ) && test_cmp expected "$actual" ' -test_expect_success '__gitdir - parent is a .git directory' ' - echo "$(pwd -P)/.git" >expected && +test_expect_success '__git_find_repo_path - parent is a .git directory' ' + echo "$ROOT/.git" >expected && ( cd .git/refs/heads && - __gitdir >"$actual" + __git_find_repo_path && + echo "$__git_repo_path" >"$actual" ) && test_cmp expected "$actual" ' -test_expect_success '__gitdir - $GIT_DIR set while .git directory in cwd' ' - echo "$TRASH_DIRECTORY/otherrepo/.git" >expected && +test_expect_success '__git_find_repo_path - $GIT_DIR set while .git directory in cwd' ' + echo "$ROOT/otherrepo/.git" >expected && ( - GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" && + GIT_DIR="$ROOT/otherrepo/.git" && export GIT_DIR && - __gitdir >"$actual" + __git_find_repo_path && + echo "$__git_repo_path" >"$actual" ) && test_cmp expected "$actual" ' -test_expect_success '__gitdir - $GIT_DIR set while .git directory in parent' ' - echo "$TRASH_DIRECTORY/otherrepo/.git" >expected && +test_expect_success '__git_find_repo_path - $GIT_DIR set while .git directory in parent' ' + echo "$ROOT/otherrepo/.git" >expected && ( - GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" && + GIT_DIR="$ROOT/otherrepo/.git" && export GIT_DIR && cd subdir && - __gitdir >"$actual" + __git_find_repo_path && + echo "$__git_repo_path" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_find_repo_path - from command line while "git -C"' ' + echo "$ROOT/.git" >expected && + ( + __git_dir="$ROOT/.git" && + __git_C_args=(-C otherrepo) && + __git_find_repo_path && + echo "$__git_repo_path" >"$actual" ) && test_cmp expected "$actual" ' -test_expect_success '__gitdir - non-existing $GIT_DIR' ' +test_expect_success '__git_find_repo_path - relative dir from command line and "git -C"' ' + echo "$ROOT/otherrepo/.git" >expected && ( - GIT_DIR="$TRASH_DIRECTORY/non-existing" && + cd subdir && + __git_dir="otherrepo/.git" && + __git_C_args=(-C ..) && + __git_find_repo_path && + echo "$__git_repo_path" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_find_repo_path - $GIT_DIR set while "git -C"' ' + echo "$ROOT/.git" >expected && + ( + GIT_DIR="$ROOT/.git" && export GIT_DIR && - test_must_fail __gitdir - ) + __git_C_args=(-C otherrepo) && + __git_find_repo_path && + echo "$__git_repo_path" >"$actual" + ) && + test_cmp expected "$actual" ' -function pwd_P_W () { - if test_have_prereq MINGW - then - pwd -W - else - pwd -P - fi -} +test_expect_success '__git_find_repo_path - relative dir in $GIT_DIR and "git -C"' ' + echo "$ROOT/otherrepo/.git" >expected && + ( + cd subdir && + GIT_DIR="otherrepo/.git" && + export GIT_DIR && + __git_C_args=(-C ..) && + __git_find_repo_path && + echo "$__git_repo_path" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_find_repo_path - "git -C" while .git directory in cwd' ' + echo "$ROOT/otherrepo/.git" >expected && + ( + __git_C_args=(-C otherrepo) && + __git_find_repo_path && + echo "$__git_repo_path" >"$actual" + ) && + test_cmp expected "$actual" +' -test_expect_success '__gitdir - gitfile in cwd' ' - echo "$(pwd_P_W)/otherrepo/.git" >expected && - echo "gitdir: $(pwd_P_W)/otherrepo/.git" >subdir/.git && +test_expect_success '__git_find_repo_path - "git -C" while cwd is a .git directory' ' + echo "$ROOT/otherrepo/.git" >expected && + ( + cd .git && + __git_C_args=(-C .. -C otherrepo) && + __git_find_repo_path && + echo "$__git_repo_path" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_find_repo_path - "git -C" while .git directory in parent' ' + echo "$ROOT/otherrepo/.git" >expected && + ( + cd subdir && + __git_C_args=(-C .. -C otherrepo) && + __git_find_repo_path && + echo "$__git_repo_path" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_find_repo_path - non-existing path in "git -C"' ' + ( + __git_C_args=(-C non-existing) && + test_must_fail __git_find_repo_path && + printf "$__git_repo_path" >"$actual" + ) && + test_must_be_empty "$actual" +' + +test_expect_success '__git_find_repo_path - non-existing path in $__git_dir' ' + ( + __git_dir="non-existing" && + test_must_fail __git_find_repo_path && + printf "$__git_repo_path" >"$actual" + ) && + test_must_be_empty "$actual" +' + +test_expect_success '__git_find_repo_path - non-existing $GIT_DIR' ' + ( + GIT_DIR="$ROOT/non-existing" && + export GIT_DIR && + test_must_fail __git_find_repo_path && + printf "$__git_repo_path" >"$actual" + ) && + test_must_be_empty "$actual" +' + +test_expect_success '__git_find_repo_path - gitfile in cwd' ' + echo "$ROOT/otherrepo/.git" >expected && + echo "gitdir: $ROOT/otherrepo/.git" >subdir/.git && test_when_finished "rm -f subdir/.git" && ( cd subdir && - __gitdir >"$actual" + __git_find_repo_path && + echo "$__git_repo_path" >"$actual" ) && test_cmp expected "$actual" ' -test_expect_success '__gitdir - gitfile in parent' ' - echo "$(pwd_P_W)/otherrepo/.git" >expected && - echo "gitdir: $(pwd_P_W)/otherrepo/.git" >subdir/.git && +test_expect_success '__git_find_repo_path - gitfile in parent' ' + echo "$ROOT/otherrepo/.git" >expected && + echo "gitdir: $ROOT/otherrepo/.git" >subdir/.git && test_when_finished "rm -f subdir/.git" && ( cd subdir/subsubdir && - __gitdir >"$actual" + __git_find_repo_path && + echo "$__git_repo_path" >"$actual" ) && test_cmp expected "$actual" ' -test_expect_success SYMLINKS '__gitdir - resulting path avoids symlinks' ' - echo "$(pwd -P)/otherrepo/.git" >expected && +test_expect_success SYMLINKS '__git_find_repo_path - resulting path avoids symlinks' ' + echo "$ROOT/otherrepo/.git" >expected && mkdir otherrepo/dir && test_when_finished "rm -rf otherrepo/dir" && ln -s otherrepo/dir link && test_when_finished "rm -f link" && ( cd link && + __git_find_repo_path && + echo "$__git_repo_path" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_find_repo_path - not a git repository' ' + ( + cd non-repo && + GIT_CEILING_DIRECTORIES="$ROOT" && + export GIT_CEILING_DIRECTORIES && + test_must_fail __git_find_repo_path && + printf "$__git_repo_path" >"$actual" + ) && + test_must_be_empty "$actual" +' + +test_expect_success '__gitdir - finds repo' ' + echo "$ROOT/.git" >expected && + ( + cd subdir/subsubdir && __gitdir >"$actual" ) && test_cmp expected "$actual" ' -test_expect_success '__gitdir - not a git repository' ' - nongit test_must_fail __gitdir + +test_expect_success '__gitdir - returns error when cant find repo' ' + ( + __git_dir="non-existing" && + test_must_fail __gitdir >"$actual" + ) && + test_must_be_empty "$actual" +' + +test_expect_success '__gitdir - repo as argument' ' + echo "otherrepo/.git" >expected && + ( + __gitdir "otherrepo" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__gitdir - remote as argument' ' + echo "remote" >expected && + ( + __gitdir "remote" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__gitcomp_direct - puts everything into COMPREPLY as-is' ' + sed -e "s/Z$//g" >expected <<-EOF && + with-trailing-space Z + without-trailing-spaceZ + --option Z + --option=Z + $invalid_variable_name Z + EOF + ( + cur=should_be_ignored && + __gitcomp_direct "$(cat expected)" && + print_comp + ) && + test_cmp expected out ' test_expect_success '__gitcomp - trailing space - options' ' @@ -361,10 +517,657 @@ test_expect_success '__git_remotes - list remotes from $GIT_DIR/remotes and from git remote add remote_in_config_1 git://remote_1 && test_when_finished "git remote remove remote_in_config_2" && git remote add remote_in_config_2 git://remote_2 && - __git_remotes >actual && + ( + __git_remotes >actual + ) && test_cmp expect actual ' +test_expect_success '__git_is_configured_remote' ' + test_when_finished "git remote remove remote_1" && + git remote add remote_1 git://remote_1 && + test_when_finished "git remote remove remote_2" && + git remote add remote_2 git://remote_2 && + ( + verbose __git_is_configured_remote remote_2 && + test_must_fail __git_is_configured_remote non-existent + ) +' + +test_expect_success 'setup for ref completion' ' + git commit --allow-empty -m initial && + git branch matching-branch && + git tag matching-tag && + ( + cd otherrepo && + git commit --allow-empty -m initial && + git branch -m master master-in-other && + git branch branch-in-other && + git tag tag-in-other + ) && + git remote add other "$ROOT/otherrepo/.git" && + git fetch --no-tags other && + rm -f .git/FETCH_HEAD && + git init thirdrepo +' + +test_expect_success '__git_refs - simple' ' + cat >expected <<-EOF && + HEAD + master + matching-branch + other/branch-in-other + other/master-in-other + matching-tag + EOF + ( + cur= && + __git_refs >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - full refs' ' + cat >expected <<-EOF && + refs/heads/master + refs/heads/matching-branch + refs/remotes/other/branch-in-other + refs/remotes/other/master-in-other + refs/tags/matching-tag + EOF + ( + cur=refs/heads/ && + __git_refs >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - repo given on the command line' ' + cat >expected <<-EOF && + HEAD + branch-in-other + master-in-other + tag-in-other + EOF + ( + __git_dir="$ROOT/otherrepo/.git" && + cur= && + __git_refs >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - remote on local file system' ' + cat >expected <<-EOF && + HEAD + branch-in-other + master-in-other + tag-in-other + EOF + ( + cur= && + __git_refs otherrepo >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - remote on local file system - full refs' ' + cat >expected <<-EOF && + refs/heads/branch-in-other + refs/heads/master-in-other + refs/tags/tag-in-other + EOF + ( + cur=refs/ && + __git_refs otherrepo >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - configured remote' ' + cat >expected <<-EOF && + HEAD + branch-in-other + master-in-other + EOF + ( + cur= && + __git_refs other >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - configured remote - full refs' ' + cat >expected <<-EOF && + HEAD + refs/heads/branch-in-other + refs/heads/master-in-other + refs/tags/tag-in-other + EOF + ( + cur=refs/ && + __git_refs other >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - configured remote - repo given on the command line' ' + cat >expected <<-EOF && + HEAD + branch-in-other + master-in-other + EOF + ( + cd thirdrepo && + __git_dir="$ROOT/.git" && + cur= && + __git_refs other >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - configured remote - full refs - repo given on the command line' ' + cat >expected <<-EOF && + HEAD + refs/heads/branch-in-other + refs/heads/master-in-other + refs/tags/tag-in-other + EOF + ( + cd thirdrepo && + __git_dir="$ROOT/.git" && + cur=refs/ && + __git_refs other >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - configured remote - remote name matches a directory' ' + cat >expected <<-EOF && + HEAD + branch-in-other + master-in-other + EOF + mkdir other && + test_when_finished "rm -rf other" && + ( + cur= && + __git_refs other >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - URL remote' ' + cat >expected <<-EOF && + HEAD + branch-in-other + master-in-other + tag-in-other + EOF + ( + cur= && + __git_refs "file://$ROOT/otherrepo/.git" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - URL remote - full refs' ' + cat >expected <<-EOF && + HEAD + refs/heads/branch-in-other + refs/heads/master-in-other + refs/tags/tag-in-other + EOF + ( + cur=refs/ && + __git_refs "file://$ROOT/otherrepo/.git" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - non-existing remote' ' + ( + cur= && + __git_refs non-existing >"$actual" + ) && + test_must_be_empty "$actual" +' + +test_expect_success '__git_refs - non-existing remote - full refs' ' + ( + cur=refs/ && + __git_refs non-existing >"$actual" + ) && + test_must_be_empty "$actual" +' + +test_expect_success '__git_refs - non-existing URL remote' ' + ( + cur= && + __git_refs "file://$ROOT/non-existing" >"$actual" + ) && + test_must_be_empty "$actual" +' + +test_expect_success '__git_refs - non-existing URL remote - full refs' ' + ( + cur=refs/ && + __git_refs "file://$ROOT/non-existing" >"$actual" + ) && + test_must_be_empty "$actual" +' + +test_expect_success '__git_refs - not in a git repository' ' + ( + GIT_CEILING_DIRECTORIES="$ROOT" && + export GIT_CEILING_DIRECTORIES && + cd subdir && + cur= && + __git_refs >"$actual" + ) && + test_must_be_empty "$actual" +' + +test_expect_success '__git_refs - unique remote branches for git checkout DWIMery' ' + cat >expected <<-EOF && + HEAD + master + matching-branch + other/ambiguous + other/branch-in-other + other/master-in-other + remote/ambiguous + remote/branch-in-remote + matching-tag + branch-in-other + branch-in-remote + master-in-other + EOF + for remote_ref in refs/remotes/other/ambiguous \ + refs/remotes/remote/ambiguous \ + refs/remotes/remote/branch-in-remote + do + git update-ref $remote_ref master && + test_when_finished "git update-ref -d $remote_ref" + done && + ( + cur= && + __git_refs "" 1 >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - after --opt=' ' + cat >expected <<-EOF && + HEAD + master + matching-branch + other/branch-in-other + other/master-in-other + matching-tag + EOF + ( + cur="--opt=" && + __git_refs "" "" "" "" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - after --opt= - full refs' ' + cat >expected <<-EOF && + refs/heads/master + refs/heads/matching-branch + refs/remotes/other/branch-in-other + refs/remotes/other/master-in-other + refs/tags/matching-tag + EOF + ( + cur="--opt=refs/" && + __git_refs "" "" "" refs/ >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git refs - exluding refs' ' + cat >expected <<-EOF && + ^HEAD + ^master + ^matching-branch + ^other/branch-in-other + ^other/master-in-other + ^matching-tag + EOF + ( + cur=^ && + __git_refs >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git refs - exluding full refs' ' + cat >expected <<-EOF && + ^refs/heads/master + ^refs/heads/matching-branch + ^refs/remotes/other/branch-in-other + ^refs/remotes/other/master-in-other + ^refs/tags/matching-tag + EOF + ( + cur=^refs/ && + __git_refs >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'setup for filtering matching refs' ' + git branch matching/branch && + git tag matching/tag && + git -C otherrepo branch matching/branch-in-other && + git fetch --no-tags other && + rm -f .git/FETCH_HEAD +' + +test_expect_success '__git_refs - dont filter refs unless told so' ' + cat >expected <<-EOF && + HEAD + master + matching-branch + matching/branch + other/branch-in-other + other/master-in-other + other/matching/branch-in-other + matching-tag + matching/tag + EOF + ( + cur=master && + __git_refs >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - only matching refs' ' + cat >expected <<-EOF && + matching-branch + matching/branch + matching-tag + matching/tag + EOF + ( + cur=mat && + __git_refs "" "" "" "$cur" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - only matching refs - full refs' ' + cat >expected <<-EOF && + refs/heads/matching-branch + refs/heads/matching/branch + EOF + ( + cur=refs/heads/mat && + __git_refs "" "" "" "$cur" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - only matching refs - remote on local file system' ' + cat >expected <<-EOF && + master-in-other + matching/branch-in-other + EOF + ( + cur=ma && + __git_refs otherrepo "" "" "$cur" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - only matching refs - configured remote' ' + cat >expected <<-EOF && + master-in-other + matching/branch-in-other + EOF + ( + cur=ma && + __git_refs other "" "" "$cur" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - only matching refs - remote - full refs' ' + cat >expected <<-EOF && + refs/heads/master-in-other + refs/heads/matching/branch-in-other + EOF + ( + cur=refs/heads/ma && + __git_refs other "" "" "$cur" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - only matching refs - checkout DWIMery' ' + cat >expected <<-EOF && + matching-branch + matching/branch + matching-tag + matching/tag + matching/branch-in-other + EOF + for remote_ref in refs/remotes/other/ambiguous \ + refs/remotes/remote/ambiguous \ + refs/remotes/remote/branch-in-remote + do + git update-ref $remote_ref master && + test_when_finished "git update-ref -d $remote_ref" + done && + ( + cur=mat && + __git_refs "" 1 "" "$cur" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'teardown after filtering matching refs' ' + git branch -d matching/branch && + git tag -d matching/tag && + git update-ref -d refs/remotes/other/matching/branch-in-other && + git -C otherrepo branch -D matching/branch-in-other +' + +test_expect_success '__git_refs - for-each-ref format specifiers in prefix' ' + cat >expected <<-EOF && + evil-%%-%42-%(refname)..master + EOF + ( + cur="evil-%%-%42-%(refname)..mas" && + __git_refs "" "" "evil-%%-%42-%(refname).." mas >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_complete_refs - simple' ' + sed -e "s/Z$//" >expected <<-EOF && + HEAD Z + master Z + matching-branch Z + other/branch-in-other Z + other/master-in-other Z + matching-tag Z + EOF + ( + cur= && + __git_complete_refs && + print_comp + ) && + test_cmp expected out +' + +test_expect_success '__git_complete_refs - matching' ' + sed -e "s/Z$//" >expected <<-EOF && + matching-branch Z + matching-tag Z + EOF + ( + cur=mat && + __git_complete_refs && + print_comp + ) && + test_cmp expected out +' + +test_expect_success '__git_complete_refs - remote' ' + sed -e "s/Z$//" >expected <<-EOF && + HEAD Z + branch-in-other Z + master-in-other Z + EOF + ( + cur= + __git_complete_refs --remote=other && + print_comp + ) && + test_cmp expected out +' + +test_expect_success '__git_complete_refs - track' ' + sed -e "s/Z$//" >expected <<-EOF && + HEAD Z + master Z + matching-branch Z + other/branch-in-other Z + other/master-in-other Z + matching-tag Z + branch-in-other Z + master-in-other Z + EOF + ( + cur= + __git_complete_refs --track && + print_comp + ) && + test_cmp expected out +' + +test_expect_success '__git_complete_refs - current word' ' + sed -e "s/Z$//" >expected <<-EOF && + matching-branch Z + matching-tag Z + EOF + ( + cur="--option=mat" && + __git_complete_refs --cur="${cur#*=}" && + print_comp + ) && + test_cmp expected out +' + +test_expect_success '__git_complete_refs - prefix' ' + sed -e "s/Z$//" >expected <<-EOF && + v1.0..matching-branch Z + v1.0..matching-tag Z + EOF + ( + cur=v1.0..mat && + __git_complete_refs --pfx=v1.0.. --cur=mat && + print_comp + ) && + test_cmp expected out +' + +test_expect_success '__git_complete_refs - suffix' ' + cat >expected <<-EOF && + HEAD. + master. + matching-branch. + other/branch-in-other. + other/master-in-other. + matching-tag. + EOF + ( + cur= && + __git_complete_refs --sfx=. && + print_comp + ) && + test_cmp expected out +' + +test_expect_success '__git_complete_fetch_refspecs - simple' ' + sed -e "s/Z$//" >expected <<-EOF && + HEAD:HEAD Z + branch-in-other:branch-in-other Z + master-in-other:master-in-other Z + EOF + ( + cur= && + __git_complete_fetch_refspecs other && + print_comp + ) && + test_cmp expected out +' + +test_expect_success '__git_complete_fetch_refspecs - matching' ' + sed -e "s/Z$//" >expected <<-EOF && + branch-in-other:branch-in-other Z + EOF + ( + cur=br && + __git_complete_fetch_refspecs other "" br && + print_comp + ) && + test_cmp expected out +' + +test_expect_success '__git_complete_fetch_refspecs - prefix' ' + sed -e "s/Z$//" >expected <<-EOF && + +HEAD:HEAD Z + +branch-in-other:branch-in-other Z + +master-in-other:master-in-other Z + EOF + ( + cur="+" && + __git_complete_fetch_refspecs other "+" "" && + print_comp + ) && + test_cmp expected out +' + +test_expect_success '__git_complete_fetch_refspecs - fully qualified' ' + sed -e "s/Z$//" >expected <<-EOF && + refs/heads/branch-in-other:refs/heads/branch-in-other Z + refs/heads/master-in-other:refs/heads/master-in-other Z + refs/tags/tag-in-other:refs/tags/tag-in-other Z + EOF + ( + cur=refs/ && + __git_complete_fetch_refspecs other "" refs/ && + print_comp + ) && + test_cmp expected out +' + +test_expect_success '__git_complete_fetch_refspecs - fully qualified & prefix' ' + sed -e "s/Z$//" >expected <<-EOF && + +refs/heads/branch-in-other:refs/heads/branch-in-other Z + +refs/heads/master-in-other:refs/heads/master-in-other Z + +refs/tags/tag-in-other:refs/tags/tag-in-other Z + EOF + ( + cur=+refs/ && + __git_complete_fetch_refspecs other + refs/ && + print_comp + ) && + test_cmp expected out +' + +test_expect_success 'teardown after ref completion' ' + git branch -d matching-branch && + git tag -d matching-tag && + git remote remove other +' + test_expect_success '__git_get_config_variables' ' cat >expect <<-EOF && name-1 @@ -475,7 +1278,12 @@ test_expect_success 'general options plus command' ' test_completion "git --namespace=foo check" "checkout " && test_completion "git --paginate check" "checkout " && test_completion "git --info-path check" "checkout " && - test_completion "git --no-replace-objects check" "checkout " + test_completion "git --no-replace-objects check" "checkout " && + test_completion "git --git-dir some/path check" "checkout " && + test_completion "git -c conf.var=value check" "checkout " && + test_completion "git -C some/path check" "checkout " && + test_completion "git --work-tree some/path check" "checkout " && + test_completion "git --namespace name/space check" "checkout " ' test_expect_success 'git --help completion' ' @@ -483,10 +1291,10 @@ test_expect_success 'git --help completion' ' test_completion "git --help core" "core-tutorial " ' -test_expect_success 'setup for ref completion' ' +test_expect_success 'setup for integration tests' ' echo content >file1 && echo more >file2 && - git add . && + git add file1 file2 && git commit -m one && git branch mybranch && git tag mytag @@ -500,6 +1308,12 @@ test_expect_success 'checkout completes ref names' ' EOF ' +test_expect_success 'git -C <path> checkout uses the right repo' ' + test_completion "git -C subdir -C subsubdir -C .. -C ../otherrepo checkout b" <<-\EOF + branch-in-other Z + EOF +' + test_expect_success 'show completes all refs' ' test_completion "git show m" <<-\EOF master Z @@ -517,7 +1331,7 @@ test_expect_success '<ref>: completes paths' ' test_expect_success 'complete tree filename with spaces' ' echo content >"name with spaces" && - git add . && + git add "name with spaces" && git commit -m spaces && test_completion "git show HEAD:nam" <<-\EOF name with spaces Z @@ -526,7 +1340,7 @@ test_expect_success 'complete tree filename with spaces' ' test_expect_success 'complete tree filename with metacharacters' ' echo content >"name with \${meta}" && - git add . && + git add "name with \${meta}" && git commit -m meta && test_completion "git show HEAD:nam" <<-\EOF name with ${meta} Z diff --git a/t/test-lib.sh b/t/test-lib.sh index 23c29bce6e..13b5696822 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -625,9 +625,9 @@ test_run_ () { trace= # 117 is magic because it is unlikely to match the exit # code of other programs - test_eval_ "(exit 117) && $1" - if test "$?" != 117; then - error "bug in the test script: broken &&-chain: $1" + if test "OK-117" != "$(test_eval_ "(exit 117) && $1${LF}${LF}echo OK-\$?" 3>&1)" + then + error "bug in the test script: broken &&-chain or run-away HERE-DOC: $1" fi trace=$trace_tmp fi |