diff options
Diffstat (limited to 't')
175 files changed, 6758 insertions, 880 deletions
diff --git a/t/Makefile b/t/Makefile index 43b15e36ae..18e2b28b26 100644 --- a/t/Makefile +++ b/t/Makefile @@ -27,7 +27,6 @@ PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH)) TEST_RESULTS_DIRECTORY_SQ = $(subst ','\'',$(TEST_RESULTS_DIRECTORY)) T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)) -TSVN = $(sort $(wildcard t91[0-9][0-9]-*.sh)) TGITWEB = $(sort $(wildcard t95[0-9][0-9]-*.sh)) THELPERS = $(sort $(filter-out $(T),$(wildcard *.sh))) @@ -77,11 +76,6 @@ aggregate-results: echo "$$f"; \ done | '$(SHELL_PATH_SQ)' ./aggregate-results.sh -# we can test NO_OPTIMIZE_COMMITS independently of LC_ALL -full-svn-test: - $(MAKE) $(TSVN) GIT_SVN_NO_OPTIMIZE_COMMITS=1 LC_ALL=C - $(MAKE) $(TSVN) GIT_SVN_NO_OPTIMIZE_COMMITS=0 LC_ALL=en_US.UTF-8 - gitweb-test: $(MAKE) $(TGITWEB) @@ -84,9 +84,9 @@ appropriately before running "make". -x:: Turn on shell tracing (i.e., `set -x`) during the tests - themselves. Implies `--verbose`. Note that this can cause - failures in some tests which redirect and test the - output of shell functions. Use with caution. + themselves. Implies `--verbose`. Note that in non-bash shells, + this can cause failures in some tests which redirect and test + the output of shell functions. Use with caution. -d:: --debug:: diff --git a/t/helper/.gitignore b/t/helper/.gitignore new file mode 100644 index 0000000000..d6e8b36798 --- /dev/null +++ b/t/helper/.gitignore @@ -0,0 +1,33 @@ +/test-chmtime +/test-ctype +/test-config +/test-date +/test-delta +/test-dump-cache-tree +/test-dump-split-index +/test-dump-untracked-cache +/test-fake-ssh +/test-scrap-cache-tree +/test-genrandom +/test-hashmap +/test-index-version +/test-line-buffer +/test-match-trees +/test-mergesort +/test-mktemp +/test-parse-options +/test-path-utils +/test-prio-queue +/test-read-cache +/test-regex +/test-revision-walking +/test-run-command +/test-sha1 +/test-sha1-array +/test-sigchain +/test-string-list +/test-submodule-config +/test-subprocess +/test-svn-fe +/test-urlmatch-normalization +/test-wildmatch diff --git a/t/helper/test-chmtime.c b/t/helper/test-chmtime.c new file mode 100644 index 0000000000..dfe8a83261 --- /dev/null +++ b/t/helper/test-chmtime.c @@ -0,0 +1,119 @@ +/* + * This program can either change modification time of the given + * file(s) or just print it. The program does not change atime or + * ctime (their values are explicitly preserved). + * + * The mtime can be changed to an absolute value: + * + * test-chmtime =<seconds> file... + * + * Relative to the current time as returned by time(3): + * + * test-chmtime =+<seconds> (or =-<seconds>) file... + * + * Or relative to the current mtime of the file: + * + * test-chmtime <seconds> file... + * test-chmtime +<seconds> (or -<seconds>) file... + * + * Examples: + * + * To just print the mtime use --verbose and set the file mtime offset to 0: + * + * test-chmtime -v +0 file + * + * To set the mtime to current time: + * + * test-chmtime =+0 file + * + */ +#include "git-compat-util.h" +#include <utime.h> + +static const char usage_str[] = "-v|--verbose (+|=|=+|=-|-)<seconds> <file>..."; + +static int timespec_arg(const char *arg, long int *set_time, int *set_eq) +{ + char *test; + const char *timespec = arg; + *set_eq = (*timespec == '=') ? 1 : 0; + if (*set_eq) { + timespec++; + if (*timespec == '+') { + *set_eq = 2; /* relative "in the future" */ + timespec++; + } + } + *set_time = strtol(timespec, &test, 10); + if (*test) { + fprintf(stderr, "Not a base-10 integer: %s\n", arg + 1); + return 0; + } + if ((*set_eq && *set_time < 0) || *set_eq == 2) { + time_t now = time(NULL); + *set_time += now; + } + return 1; +} + +int main(int argc, char *argv[]) +{ + static int verbose; + + int i = 1; + /* no mtime change by default */ + int set_eq = 0; + long int set_time = 0; + + if (argc < 3) + goto usage; + + if (strcmp(argv[i], "--verbose") == 0 || strcmp(argv[i], "-v") == 0) { + verbose = 1; + ++i; + } + if (timespec_arg(argv[i], &set_time, &set_eq)) + ++i; + else + goto usage; + + for (; i < argc; i++) { + struct stat sb; + struct utimbuf utb; + + if (stat(argv[i], &sb) < 0) { + fprintf(stderr, "Failed to stat %s: %s\n", + argv[i], strerror(errno)); + return 1; + } + +#ifdef GIT_WINDOWS_NATIVE + if (!(sb.st_mode & S_IWUSR) && + chmod(argv[i], sb.st_mode | S_IWUSR)) { + fprintf(stderr, "Could not make user-writable %s: %s", + argv[i], strerror(errno)); + return 1; + } +#endif + + utb.actime = sb.st_atime; + utb.modtime = set_eq ? set_time : sb.st_mtime + set_time; + + if (verbose) { + uintmax_t mtime = utb.modtime < 0 ? 0: utb.modtime; + printf("%"PRIuMAX"\t%s\n", mtime, argv[i]); + } + + if (utb.modtime != sb.st_mtime && utime(argv[i], &utb) < 0) { + fprintf(stderr, "Failed to modify time on %s: %s\n", + argv[i], strerror(errno)); + return 1; + } + } + + return 0; + +usage: + fprintf(stderr, "usage: %s %s\n", argv[0], usage_str); + return 1; +} diff --git a/t/helper/test-config.c b/t/helper/test-config.c new file mode 100644 index 0000000000..6a77552210 --- /dev/null +++ b/t/helper/test-config.c @@ -0,0 +1,152 @@ +#include "cache.h" +#include "string-list.h" + +/* + * This program exposes the C API of the configuration mechanism + * as a set of simple commands in order to facilitate testing. + * + * Reads stdin and prints result of command to stdout: + * + * get_value -> prints the value with highest priority for the entered key + * + * get_value_multi -> prints all values for the entered key in increasing order + * of priority + * + * get_int -> print integer value for the entered key or die + * + * get_bool -> print bool value for the entered key or die + * + * get_string -> print string value for the entered key or die + * + * configset_get_value -> returns value with the highest priority for the entered key + * from a config_set constructed from files entered as arguments. + * + * configset_get_value_multi -> returns value_list for the entered key sorted in + * ascending order of priority from a config_set + * constructed from files entered as arguments. + * + * Examples: + * + * To print the value with highest priority for key "foo.bAr Baz.rock": + * test-config get_value "foo.bAr Baz.rock" + * + */ + + +int main(int argc, char **argv) +{ + int i, val; + const char *v; + const struct string_list *strptr; + struct config_set cs; + git_configset_init(&cs); + + if (argc < 2) { + fprintf(stderr, "Please, provide a command name on the command-line\n"); + goto exit1; + } else if (argc == 3 && !strcmp(argv[1], "get_value")) { + if (!git_config_get_value(argv[2], &v)) { + if (!v) + printf("(NULL)\n"); + else + printf("%s\n", v); + goto exit0; + } else { + printf("Value not found for \"%s\"\n", argv[2]); + goto exit1; + } + } else if (argc == 3 && !strcmp(argv[1], "get_value_multi")) { + strptr = git_config_get_value_multi(argv[2]); + if (strptr) { + for (i = 0; i < strptr->nr; i++) { + v = strptr->items[i].string; + if (!v) + printf("(NULL)\n"); + else + printf("%s\n", v); + } + goto exit0; + } else { + printf("Value not found for \"%s\"\n", argv[2]); + goto exit1; + } + } else if (argc == 3 && !strcmp(argv[1], "get_int")) { + if (!git_config_get_int(argv[2], &val)) { + printf("%d\n", val); + goto exit0; + } else { + printf("Value not found for \"%s\"\n", argv[2]); + goto exit1; + } + } else if (argc == 3 && !strcmp(argv[1], "get_bool")) { + if (!git_config_get_bool(argv[2], &val)) { + printf("%d\n", val); + goto exit0; + } else { + printf("Value not found for \"%s\"\n", argv[2]); + goto exit1; + } + } else if (argc == 3 && !strcmp(argv[1], "get_string")) { + if (!git_config_get_string_const(argv[2], &v)) { + printf("%s\n", v); + goto exit0; + } else { + printf("Value not found for \"%s\"\n", argv[2]); + goto exit1; + } + } else if (!strcmp(argv[1], "configset_get_value")) { + for (i = 3; i < argc; i++) { + int err; + if ((err = git_configset_add_file(&cs, argv[i]))) { + fprintf(stderr, "Error (%d) reading configuration file %s.\n", err, argv[i]); + goto exit2; + } + } + if (!git_configset_get_value(&cs, argv[2], &v)) { + if (!v) + printf("(NULL)\n"); + else + printf("%s\n", v); + goto exit0; + } else { + printf("Value not found for \"%s\"\n", argv[2]); + goto exit1; + } + } else if (!strcmp(argv[1], "configset_get_value_multi")) { + for (i = 3; i < argc; i++) { + int err; + if ((err = git_configset_add_file(&cs, argv[i]))) { + fprintf(stderr, "Error (%d) reading configuration file %s.\n", err, argv[i]); + goto exit2; + } + } + strptr = git_configset_get_value_multi(&cs, argv[2]); + if (strptr) { + for (i = 0; i < strptr->nr; i++) { + v = strptr->items[i].string; + if (!v) + printf("(NULL)\n"); + else + printf("%s\n", v); + } + goto exit0; + } else { + printf("Value not found for \"%s\"\n", argv[2]); + goto exit1; + } + } + + die("%s: Please check the syntax and the function name", argv[0]); + +exit0: + git_configset_clear(&cs); + return 0; + +exit1: + git_configset_clear(&cs); + return 1; + +exit2: + git_configset_clear(&cs); + return 2; +} diff --git a/t/helper/test-ctype.c b/t/helper/test-ctype.c new file mode 100644 index 0000000000..707a821f03 --- /dev/null +++ b/t/helper/test-ctype.c @@ -0,0 +1,42 @@ +#include "cache.h" + +static int rc; + +static void report_error(const char *class, int ch) +{ + printf("%s classifies char %d (0x%02x) wrongly\n", class, ch, ch); + rc = 1; +} + +static int is_in(const char *s, int ch) +{ + /* We can't find NUL using strchr. It's classless anyway. */ + if (ch == '\0') + return 0; + return !!strchr(s, ch); +} + +#define TEST_CLASS(t,s) { \ + int i; \ + for (i = 0; i < 256; i++) { \ + if (is_in(s, i) != t(i)) \ + report_error(#t, i); \ + } \ +} + +#define DIGIT "0123456789" +#define LOWER "abcdefghijklmnopqrstuvwxyz" +#define UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + +int main(int argc, char **argv) +{ + TEST_CLASS(isdigit, DIGIT); + TEST_CLASS(isspace, " \n\r\t"); + TEST_CLASS(isalpha, LOWER UPPER); + TEST_CLASS(isalnum, LOWER UPPER DIGIT); + TEST_CLASS(is_glob_special, "*?[\\"); + TEST_CLASS(is_regex_special, "$()*+.?[\\^{|"); + TEST_CLASS(is_pathspec_magic, "!\"#%&',-/:;<=>@_`~"); + + return rc; +} diff --git a/t/helper/test-date.c b/t/helper/test-date.c new file mode 100644 index 0000000000..63f373557e --- /dev/null +++ b/t/helper/test-date.c @@ -0,0 +1,73 @@ +#include "cache.h" + +static const char *usage_msg = "\n" +" test-date show [time_t]...\n" +" test-date parse [date]...\n" +" test-date approxidate [date]...\n"; + +static void show_dates(char **argv, struct timeval *now) +{ + struct strbuf buf = STRBUF_INIT; + + for (; *argv; argv++) { + time_t t = atoi(*argv); + show_date_relative(t, 0, now, &buf); + printf("%s -> %s\n", *argv, buf.buf); + } + strbuf_release(&buf); +} + +static void parse_dates(char **argv, struct timeval *now) +{ + struct strbuf result = STRBUF_INIT; + + for (; *argv; argv++) { + unsigned long t; + int tz; + + strbuf_reset(&result); + parse_date(*argv, &result); + if (sscanf(result.buf, "%lu %d", &t, &tz) == 2) + printf("%s -> %s\n", + *argv, show_date(t, tz, DATE_MODE(ISO8601))); + else + printf("%s -> bad\n", *argv); + } + strbuf_release(&result); +} + +static void parse_approxidate(char **argv, struct timeval *now) +{ + for (; *argv; argv++) { + time_t t; + t = approxidate_relative(*argv, now); + printf("%s -> %s\n", *argv, show_date(t, 0, DATE_MODE(ISO8601))); + } +} + +int main(int argc, char **argv) +{ + struct timeval now; + const char *x; + + x = getenv("TEST_DATE_NOW"); + if (x) { + now.tv_sec = atoi(x); + now.tv_usec = 0; + } + else + gettimeofday(&now, NULL); + + argv++; + if (!*argv) + usage(usage_msg); + if (!strcmp(*argv, "show")) + show_dates(argv+1, &now); + else if (!strcmp(*argv, "parse")) + parse_dates(argv+1, &now); + else if (!strcmp(*argv, "approxidate")) + parse_approxidate(argv+1, &now); + else + usage(usage_msg); + return 0; +} diff --git a/t/helper/test-delta.c b/t/helper/test-delta.c new file mode 100644 index 0000000000..4595cd6433 --- /dev/null +++ b/t/helper/test-delta.c @@ -0,0 +1,78 @@ +/* + * test-delta.c: test code to exercise diff-delta.c and patch-delta.c + * + * (C) 2005 Nicolas Pitre <nico@fluxnic.net> + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "git-compat-util.h" +#include "delta.h" +#include "cache.h" + +static const char usage_str[] = + "test-delta (-d|-p) <from_file> <data_file> <out_file>"; + +int main(int argc, char *argv[]) +{ + int fd; + struct stat st; + void *from_buf, *data_buf, *out_buf; + unsigned long from_size, data_size, out_size; + + if (argc != 5 || (strcmp(argv[1], "-d") && strcmp(argv[1], "-p"))) { + fprintf(stderr, "usage: %s\n", usage_str); + return 1; + } + + fd = open(argv[2], O_RDONLY); + if (fd < 0 || fstat(fd, &st)) { + perror(argv[2]); + return 1; + } + from_size = st.st_size; + from_buf = mmap(NULL, from_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (from_buf == MAP_FAILED) { + perror(argv[2]); + close(fd); + return 1; + } + close(fd); + + fd = open(argv[3], O_RDONLY); + if (fd < 0 || fstat(fd, &st)) { + perror(argv[3]); + return 1; + } + data_size = st.st_size; + data_buf = mmap(NULL, data_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (data_buf == MAP_FAILED) { + perror(argv[3]); + close(fd); + return 1; + } + close(fd); + + if (argv[1][1] == 'd') + out_buf = diff_delta(from_buf, from_size, + data_buf, data_size, + &out_size, 0); + else + out_buf = patch_delta(from_buf, from_size, + data_buf, data_size, + &out_size); + if (!out_buf) { + fprintf(stderr, "delta operation failed (returned NULL)\n"); + return 1; + } + + fd = open (argv[4], O_WRONLY|O_CREAT|O_TRUNC, 0666); + if (fd < 0 || write_in_full(fd, out_buf, out_size) != out_size) { + perror(argv[4]); + return 1; + } + + return 0; +} diff --git a/t/helper/test-dump-cache-tree.c b/t/helper/test-dump-cache-tree.c new file mode 100644 index 0000000000..bb53c0aa65 --- /dev/null +++ b/t/helper/test-dump-cache-tree.c @@ -0,0 +1,67 @@ +#include "cache.h" +#include "tree.h" +#include "cache-tree.h" + + +static void dump_one(struct cache_tree *it, const char *pfx, const char *x) +{ + if (it->entry_count < 0) + printf("%-40s %s%s (%d subtrees)\n", + "invalid", x, pfx, it->subtree_nr); + else + printf("%s %s%s (%d entries, %d subtrees)\n", + sha1_to_hex(it->sha1), x, pfx, + it->entry_count, it->subtree_nr); +} + +static int dump_cache_tree(struct cache_tree *it, + struct cache_tree *ref, + const char *pfx) +{ + int i; + int errs = 0; + + if (!it || !ref) + /* missing in either */ + return 0; + + if (it->entry_count < 0) { + /* invalid */ + dump_one(it, pfx, ""); + dump_one(ref, pfx, "#(ref) "); + } + else { + dump_one(it, pfx, ""); + if (hashcmp(it->sha1, ref->sha1) || + ref->entry_count != it->entry_count || + ref->subtree_nr != it->subtree_nr) { + /* claims to be valid but is lying */ + dump_one(ref, pfx, "#(ref) "); + errs = 1; + } + } + + for (i = 0; i < it->subtree_nr; i++) { + char path[PATH_MAX]; + struct cache_tree_sub *down = it->down[i]; + struct cache_tree_sub *rdwn; + + rdwn = cache_tree_sub(ref, down->name); + xsnprintf(path, sizeof(path), "%s%.*s/", pfx, down->namelen, down->name); + if (dump_cache_tree(down->cache_tree, rdwn->cache_tree, path)) + errs = 1; + } + return errs; +} + +int main(int ac, char **av) +{ + struct index_state istate; + struct cache_tree *another = cache_tree(); + if (read_cache() < 0) + die("unable to read index file"); + istate = the_index; + istate.cache_tree = another; + cache_tree_update(&istate, WRITE_TREE_DRY_RUN); + return dump_cache_tree(active_cache_tree, another, ""); +} diff --git a/t/helper/test-dump-split-index.c b/t/helper/test-dump-split-index.c new file mode 100644 index 0000000000..861d28c9b6 --- /dev/null +++ b/t/helper/test-dump-split-index.c @@ -0,0 +1,36 @@ +#include "cache.h" +#include "split-index.h" +#include "ewah/ewok.h" + +static void show_bit(size_t pos, void *data) +{ + printf(" %d", (int)pos); +} + +int main(int ac, char **av) +{ + struct split_index *si; + int i; + + do_read_index(&the_index, av[1], 1); + printf("own %s\n", sha1_to_hex(the_index.sha1)); + si = the_index.split_index; + if (!si) { + printf("not a split index\n"); + return 0; + } + printf("base %s\n", sha1_to_hex(si->base_sha1)); + for (i = 0; i < the_index.cache_nr; i++) { + struct cache_entry *ce = the_index.cache[i]; + printf("%06o %s %d\t%s\n", ce->ce_mode, + sha1_to_hex(ce->sha1), ce_stage(ce), ce->name); + } + printf("replacements:"); + if (si->replace_bitmap) + ewah_each_bit(si->replace_bitmap, show_bit, NULL); + printf("\ndeletions:"); + if (si->delete_bitmap) + ewah_each_bit(si->delete_bitmap, show_bit, NULL); + printf("\n"); + return 0; +} diff --git a/t/helper/test-dump-untracked-cache.c b/t/helper/test-dump-untracked-cache.c new file mode 100644 index 0000000000..0a1c285246 --- /dev/null +++ b/t/helper/test-dump-untracked-cache.c @@ -0,0 +1,66 @@ +#include "cache.h" +#include "dir.h" + +static int compare_untracked(const void *a_, const void *b_) +{ + const char *const *a = a_; + const char *const *b = b_; + return strcmp(*a, *b); +} + +static int compare_dir(const void *a_, const void *b_) +{ + const struct untracked_cache_dir *const *a = a_; + const struct untracked_cache_dir *const *b = b_; + return strcmp((*a)->name, (*b)->name); +} + +static void dump(struct untracked_cache_dir *ucd, struct strbuf *base) +{ + int i, len; + qsort(ucd->untracked, ucd->untracked_nr, sizeof(*ucd->untracked), + compare_untracked); + qsort(ucd->dirs, ucd->dirs_nr, sizeof(*ucd->dirs), + compare_dir); + len = base->len; + strbuf_addf(base, "%s/", ucd->name); + printf("%s %s", base->buf, + sha1_to_hex(ucd->exclude_sha1)); + if (ucd->recurse) + fputs(" recurse", stdout); + if (ucd->check_only) + fputs(" check_only", stdout); + if (ucd->valid) + fputs(" valid", stdout); + printf("\n"); + for (i = 0; i < ucd->untracked_nr; i++) + printf("%s\n", ucd->untracked[i]); + for (i = 0; i < ucd->dirs_nr; i++) + dump(ucd->dirs[i], base); + strbuf_setlen(base, len); +} + +int main(int ac, char **av) +{ + struct untracked_cache *uc; + struct strbuf base = STRBUF_INIT; + + /* Hack to avoid modifying the untracked cache when we read it */ + ignore_untracked_cache_config = 1; + + setup_git_directory(); + if (read_cache() < 0) + die("unable to read index file"); + uc = the_index.untracked; + if (!uc) { + printf("no untracked cache\n"); + return 0; + } + printf("info/exclude %s\n", sha1_to_hex(uc->ss_info_exclude.sha1)); + printf("core.excludesfile %s\n", sha1_to_hex(uc->ss_excludes_file.sha1)); + printf("exclude_per_dir %s\n", uc->exclude_per_dir); + printf("flags %08x\n", uc->dir_flags); + if (uc->root) + dump(uc->root, &base); + return 0; +} diff --git a/t/helper/test-fake-ssh.c b/t/helper/test-fake-ssh.c new file mode 100644 index 0000000000..980de216e1 --- /dev/null +++ b/t/helper/test-fake-ssh.c @@ -0,0 +1,30 @@ +#include "git-compat-util.h" +#include "run-command.h" +#include "strbuf.h" + +int main(int argc, char **argv) +{ + const char *trash_directory = getenv("TRASH_DIRECTORY"); + struct strbuf buf = STRBUF_INIT; + FILE *f; + int i; + const char *child_argv[] = { NULL, NULL }; + + /* First, print all parameters into $TRASH_DIRECTORY/ssh-output */ + if (!trash_directory) + die("Need a TRASH_DIRECTORY!"); + strbuf_addf(&buf, "%s/ssh-output", trash_directory); + f = fopen(buf.buf, "w"); + if (!f) + die("Could not write to %s", buf.buf); + for (i = 0; i < argc; i++) + fprintf(f, "%s%s", i > 0 ? " " : "", i > 0 ? argv[i] : "ssh:"); + fprintf(f, "\n"); + fclose(f); + + /* Now, evaluate the *last* parameter */ + if (argc < 2) + return 0; + child_argv[0] = argv[argc - 1]; + return run_command_v_opt(child_argv, RUN_USING_SHELL); +} diff --git a/t/helper/test-genrandom.c b/t/helper/test-genrandom.c new file mode 100644 index 0000000000..54824d0754 --- /dev/null +++ b/t/helper/test-genrandom.c @@ -0,0 +1,33 @@ +/* + * Simple random data generator used to create reproducible test files. + * This is inspired from POSIX.1-2001 implementation example for rand(). + * Copyright (C) 2007 by Nicolas Pitre, licensed under the GPL version 2. + */ + +#include "git-compat-util.h" + +int main(int argc, char *argv[]) +{ + unsigned long count, next = 0; + unsigned char *c; + + if (argc < 2 || argc > 3) { + fprintf(stderr, "usage: %s <seed_string> [<size>]\n", argv[0]); + return 1; + } + + c = (unsigned char *) argv[1]; + do { + next = next * 11 + *c; + } while (*c++); + + count = (argc == 3) ? strtoul(argv[2], NULL, 0) : -1L; + + while (count--) { + next = next * 1103515245 + 12345; + if (putchar((next >> 16) & 0xff) == EOF) + return -1; + } + + return 0; +} diff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c new file mode 100644 index 0000000000..cc2891dd97 --- /dev/null +++ b/t/helper/test-hashmap.c @@ -0,0 +1,264 @@ +#include "git-compat-util.h" +#include "hashmap.h" + +struct test_entry +{ + struct hashmap_entry ent; + /* key and value as two \0-terminated strings */ + char key[FLEX_ARRAY]; +}; + +static const char *get_value(const struct test_entry *e) +{ + return e->key + strlen(e->key) + 1; +} + +static int test_entry_cmp(const struct test_entry *e1, + const struct test_entry *e2, const char* key) +{ + return strcmp(e1->key, key ? key : e2->key); +} + +static int test_entry_cmp_icase(const struct test_entry *e1, + const struct test_entry *e2, const char* key) +{ + return strcasecmp(e1->key, key ? key : e2->key); +} + +static struct test_entry *alloc_test_entry(int hash, char *key, int klen, + char *value, int vlen) +{ + struct test_entry *entry = malloc(sizeof(struct test_entry) + klen + + vlen + 2); + hashmap_entry_init(entry, hash); + memcpy(entry->key, key, klen + 1); + memcpy(entry->key + klen + 1, value, vlen + 1); + return entry; +} + +#define HASH_METHOD_FNV 0 +#define HASH_METHOD_I 1 +#define HASH_METHOD_IDIV10 2 +#define HASH_METHOD_0 3 +#define HASH_METHOD_X2 4 +#define TEST_SPARSE 8 +#define TEST_ADD 16 +#define TEST_SIZE 100000 + +static unsigned int hash(unsigned int method, unsigned int i, const char *key) +{ + unsigned int hash = 0; + switch (method & 3) + { + case HASH_METHOD_FNV: + hash = strhash(key); + break; + case HASH_METHOD_I: + hash = i; + break; + case HASH_METHOD_IDIV10: + hash = i / 10; + break; + case HASH_METHOD_0: + hash = 0; + break; + } + + if (method & HASH_METHOD_X2) + hash = 2 * hash; + return hash; +} + +/* + * Test performance of hashmap.[ch] + * Usage: time echo "perfhashmap method rounds" | test-hashmap + */ +static void perf_hashmap(unsigned int method, unsigned int rounds) +{ + struct hashmap map; + char buf[16]; + struct test_entry **entries; + unsigned int *hashes; + unsigned int i, j; + + entries = malloc(TEST_SIZE * sizeof(struct test_entry *)); + hashes = malloc(TEST_SIZE * sizeof(int)); + for (i = 0; i < TEST_SIZE; i++) { + snprintf(buf, sizeof(buf), "%i", i); + entries[i] = alloc_test_entry(0, buf, strlen(buf), "", 0); + hashes[i] = hash(method, i, entries[i]->key); + } + + if (method & TEST_ADD) { + /* test adding to the map */ + for (j = 0; j < rounds; j++) { + hashmap_init(&map, (hashmap_cmp_fn) test_entry_cmp, 0); + + /* add entries */ + for (i = 0; i < TEST_SIZE; i++) { + hashmap_entry_init(entries[i], hashes[i]); + hashmap_add(&map, entries[i]); + } + + hashmap_free(&map, 0); + } + } else { + /* test map lookups */ + hashmap_init(&map, (hashmap_cmp_fn) test_entry_cmp, 0); + + /* fill the map (sparsely if specified) */ + j = (method & TEST_SPARSE) ? TEST_SIZE / 10 : TEST_SIZE; + for (i = 0; i < j; i++) { + hashmap_entry_init(entries[i], hashes[i]); + hashmap_add(&map, entries[i]); + } + + for (j = 0; j < rounds; j++) { + for (i = 0; i < TEST_SIZE; i++) { + hashmap_get_from_hash(&map, hashes[i], + entries[i]->key); + } + } + + hashmap_free(&map, 0); + } +} + +#define DELIM " \t\r\n" + +/* + * Read stdin line by line and print result of commands to stdout: + * + * hash key -> strhash(key) memhash(key) strihash(key) memihash(key) + * put key value -> NULL / old value + * get key -> NULL / value + * remove key -> NULL / old value + * iterate -> key1 value1\nkey2 value2\n... + * size -> tablesize numentries + * + * perfhashmap method rounds -> test hashmap.[ch] performance + */ +int main(int argc, char *argv[]) +{ + char line[1024]; + struct hashmap map; + int icase; + + /* init hash map */ + icase = argc > 1 && !strcmp("ignorecase", argv[1]); + hashmap_init(&map, (hashmap_cmp_fn) (icase ? test_entry_cmp_icase + : test_entry_cmp), 0); + + /* process commands from stdin */ + while (fgets(line, sizeof(line), stdin)) { + char *cmd, *p1 = NULL, *p2 = NULL; + int l1 = 0, l2 = 0, hash = 0; + struct test_entry *entry; + + /* break line into command and up to two parameters */ + cmd = strtok(line, DELIM); + /* ignore empty lines */ + if (!cmd || *cmd == '#') + continue; + + p1 = strtok(NULL, DELIM); + if (p1) { + l1 = strlen(p1); + hash = icase ? strihash(p1) : strhash(p1); + p2 = strtok(NULL, DELIM); + if (p2) + l2 = strlen(p2); + } + + if (!strcmp("hash", cmd) && l1) { + + /* print results of different hash functions */ + printf("%u %u %u %u\n", strhash(p1), memhash(p1, l1), + strihash(p1), memihash(p1, l1)); + + } else if (!strcmp("add", cmd) && l1 && l2) { + + /* create entry with key = p1, value = p2 */ + entry = alloc_test_entry(hash, p1, l1, p2, l2); + + /* add to hashmap */ + hashmap_add(&map, entry); + + } else if (!strcmp("put", cmd) && l1 && l2) { + + /* create entry with key = p1, value = p2 */ + entry = alloc_test_entry(hash, p1, l1, p2, l2); + + /* add / replace entry */ + entry = hashmap_put(&map, entry); + + /* print and free replaced entry, if any */ + puts(entry ? get_value(entry) : "NULL"); + free(entry); + + } else if (!strcmp("get", cmd) && l1) { + + /* lookup entry in hashmap */ + entry = hashmap_get_from_hash(&map, hash, p1); + + /* print result */ + if (!entry) + puts("NULL"); + while (entry) { + puts(get_value(entry)); + entry = hashmap_get_next(&map, entry); + } + + } else if (!strcmp("remove", cmd) && l1) { + + /* setup static key */ + struct hashmap_entry key; + hashmap_entry_init(&key, hash); + + /* remove entry from hashmap */ + entry = hashmap_remove(&map, &key, p1); + + /* print result and free entry*/ + puts(entry ? get_value(entry) : "NULL"); + free(entry); + + } else if (!strcmp("iterate", cmd)) { + + struct hashmap_iter iter; + hashmap_iter_init(&map, &iter); + while ((entry = hashmap_iter_next(&iter))) + printf("%s %s\n", entry->key, get_value(entry)); + + } else if (!strcmp("size", cmd)) { + + /* print table sizes */ + printf("%u %u\n", map.tablesize, map.size); + + } else if (!strcmp("intern", cmd) && l1) { + + /* test that strintern works */ + const char *i1 = strintern(p1); + const char *i2 = strintern(p1); + if (strcmp(i1, p1)) + printf("strintern(%s) returns %s\n", p1, i1); + else if (i1 == p1) + printf("strintern(%s) returns input pointer\n", p1); + else if (i1 != i2) + printf("strintern(%s) != strintern(%s)", i1, i2); + else + printf("%s\n", i1); + + } else if (!strcmp("perfhashmap", cmd) && l1 && l2) { + + perf_hashmap(atoi(p1), atoi(p2)); + + } else { + + printf("Unknown command %s\n", cmd); + + } + } + + hashmap_free(&map, 1); + return 0; +} diff --git a/t/helper/test-index-version.c b/t/helper/test-index-version.c new file mode 100644 index 0000000000..05d4699c4a --- /dev/null +++ b/t/helper/test-index-version.c @@ -0,0 +1,14 @@ +#include "cache.h" + +int main(int argc, char **argv) +{ + struct cache_header hdr; + int version; + + memset(&hdr,0,sizeof(hdr)); + if (read(0, &hdr, sizeof(hdr)) != sizeof(hdr)) + return 0; + version = ntohl(hdr.hdr_version); + printf("%d\n", version); + return 0; +} diff --git a/t/helper/test-line-buffer.c b/t/helper/test-line-buffer.c new file mode 100644 index 0000000000..1e58f0476f --- /dev/null +++ b/t/helper/test-line-buffer.c @@ -0,0 +1,91 @@ +/* + * test-line-buffer.c: code to exercise the svn importer's input helper + */ + +#include "git-compat-util.h" +#include "strbuf.h" +#include "vcs-svn/line_buffer.h" + +static uint32_t strtouint32(const char *s) +{ + char *end; + uintmax_t n = strtoumax(s, &end, 10); + if (*s == '\0' || *end != '\0') + die("invalid count: %s", s); + return (uint32_t) n; +} + +static void handle_command(const char *command, const char *arg, struct line_buffer *buf) +{ + switch (*command) { + case 'b': + if (starts_with(command, "binary ")) { + struct strbuf sb = STRBUF_INIT; + strbuf_addch(&sb, '>'); + buffer_read_binary(buf, &sb, strtouint32(arg)); + fwrite(sb.buf, 1, sb.len, stdout); + strbuf_release(&sb); + return; + } + case 'c': + if (starts_with(command, "copy ")) { + buffer_copy_bytes(buf, strtouint32(arg)); + return; + } + case 's': + if (starts_with(command, "skip ")) { + buffer_skip_bytes(buf, strtouint32(arg)); + return; + } + default: + die("unrecognized command: %s", command); + } +} + +static void handle_line(const char *line, struct line_buffer *stdin_buf) +{ + const char *arg = strchr(line, ' '); + if (!arg) + die("no argument in line: %s", line); + handle_command(line, arg + 1, stdin_buf); +} + +int main(int argc, char *argv[]) +{ + struct line_buffer stdin_buf = LINE_BUFFER_INIT; + struct line_buffer file_buf = LINE_BUFFER_INIT; + struct line_buffer *input = &stdin_buf; + const char *filename; + char *s; + + if (argc == 1) + filename = NULL; + else if (argc == 2) + filename = argv[1]; + else + usage("test-line-buffer [file | &fd] < script"); + + if (buffer_init(&stdin_buf, NULL)) + die_errno("open error"); + if (filename) { + if (*filename == '&') { + if (buffer_fdinit(&file_buf, strtouint32(filename + 1))) + die_errno("error opening fd %s", filename + 1); + } else { + if (buffer_init(&file_buf, filename)) + die_errno("error opening %s", filename); + } + input = &file_buf; + } + + while ((s = buffer_read_line(&stdin_buf))) + handle_line(s, input); + + if (filename && buffer_deinit(&file_buf)) + die("error reading from %s", filename); + if (buffer_deinit(&stdin_buf)) + die("input error"); + if (ferror(stdout)) + die("output error"); + return 0; +} diff --git a/t/helper/test-match-trees.c b/t/helper/test-match-trees.c new file mode 100644 index 0000000000..d446b8eaca --- /dev/null +++ b/t/helper/test-match-trees.c @@ -0,0 +1,26 @@ +#include "cache.h" +#include "tree.h" + +int main(int ac, char **av) +{ + struct object_id hash1, hash2, shifted; + struct tree *one, *two; + + setup_git_directory(); + + if (get_oid(av[1], &hash1)) + die("cannot parse %s as an object name", av[1]); + if (get_oid(av[2], &hash2)) + die("cannot parse %s as an object name", av[2]); + one = parse_tree_indirect(hash1.hash); + if (!one) + die("not a tree-ish %s", av[1]); + two = parse_tree_indirect(hash2.hash); + if (!two) + die("not a tree-ish %s", av[2]); + + shift_tree(&one->object.oid, &two->object.oid, &shifted, -1); + printf("shifted: %s\n", oid_to_hex(&shifted)); + + exit(0); +} diff --git a/t/helper/test-mergesort.c b/t/helper/test-mergesort.c new file mode 100644 index 0000000000..ea3b959e94 --- /dev/null +++ b/t/helper/test-mergesort.c @@ -0,0 +1,52 @@ +#include "cache.h" +#include "mergesort.h" + +struct line { + char *text; + struct line *next; +}; + +static void *get_next(const void *a) +{ + return ((const struct line *)a)->next; +} + +static void set_next(void *a, void *b) +{ + ((struct line *)a)->next = b; +} + +static int compare_strings(const void *a, const void *b) +{ + const struct line *x = a, *y = b; + return strcmp(x->text, y->text); +} + +int main(int argc, char **argv) +{ + struct line *line, *p = NULL, *lines = NULL; + struct strbuf sb = STRBUF_INIT; + + for (;;) { + if (strbuf_getwholeline(&sb, stdin, '\n')) + break; + line = xmalloc(sizeof(struct line)); + line->text = strbuf_detach(&sb, NULL); + if (p) { + line->next = p->next; + p->next = line; + } else { + line->next = NULL; + lines = line; + } + p = line; + } + + lines = llist_mergesort(lines, get_next, set_next, compare_strings); + + while (lines) { + printf("%s", lines->text); + lines = lines->next; + } + return 0; +} diff --git a/t/helper/test-mktemp.c b/t/helper/test-mktemp.c new file mode 100644 index 0000000000..c8c54213a3 --- /dev/null +++ b/t/helper/test-mktemp.c @@ -0,0 +1,14 @@ +/* + * test-mktemp.c: code to exercise the creation of temporary files + */ +#include "git-compat-util.h" + +int main(int argc, char *argv[]) +{ + if (argc != 2) + usage("Expected 1 parameter defining the temporary file template"); + + xmkstemp(xstrdup(argv[1])); + + return 0; +} diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c new file mode 100644 index 0000000000..8a1235d03e --- /dev/null +++ b/t/helper/test-parse-options.c @@ -0,0 +1,179 @@ +#include "cache.h" +#include "parse-options.h" +#include "string-list.h" + +static int boolean = 0; +static int integer = 0; +static unsigned long magnitude = 0; +static unsigned long timestamp; +static int abbrev = 7; +static int verbose = -1; /* unspecified */ +static int dry_run = 0, quiet = 0; +static char *string = NULL; +static char *file = NULL; +static int ambiguous; +static struct string_list list; + +static struct { + int called; + const char *arg; + int unset; +} length_cb; + +static int length_callback(const struct option *opt, const char *arg, int unset) +{ + length_cb.called = 1; + length_cb.arg = arg; + length_cb.unset = unset; + + if (unset) + return 1; /* do not support unset */ + + *(int *)opt->value = strlen(arg); + return 0; +} + +static int number_callback(const struct option *opt, const char *arg, int unset) +{ + *(int *)opt->value = strtol(arg, NULL, 10); + return 0; +} + +static int collect_expect(const struct option *opt, const char *arg, int unset) +{ + struct string_list *expect; + struct string_list_item *item; + struct strbuf label = STRBUF_INIT; + const char *colon; + + if (!arg || unset) + die("malformed --expect option"); + + expect = (struct string_list *)opt->value; + colon = strchr(arg, ':'); + if (!colon) + die("malformed --expect option, lacking a colon"); + strbuf_add(&label, arg, colon - arg); + item = string_list_insert(expect, strbuf_detach(&label, NULL)); + if (item->util) + die("malformed --expect option, duplicate %s", label.buf); + item->util = (void *)arg; + return 0; +} + +__attribute__((format (printf,3,4))) +static void show(struct string_list *expect, int *status, const char *fmt, ...) +{ + struct string_list_item *item; + struct strbuf buf = STRBUF_INIT; + va_list args; + + va_start(args, fmt); + strbuf_vaddf(&buf, fmt, args); + va_end(args); + + if (!expect->nr) + printf("%s\n", buf.buf); + else { + char *colon = strchr(buf.buf, ':'); + if (!colon) + die("malformed output format, output lacking colon: %s", fmt); + *colon = '\0'; + item = string_list_lookup(expect, buf.buf); + *colon = ':'; + if (!item) + ; /* not among entries being checked */ + else { + if (strcmp((const char *)item->util, buf.buf)) { + printf("-%s\n", (char *)item->util); + printf("+%s\n", buf.buf); + *status = 1; + } + } + } + strbuf_release(&buf); +} + +int main(int argc, char **argv) +{ + const char *prefix = "prefix/"; + const char *usage[] = { + "test-parse-options <options>", + NULL + }; + struct string_list expect = STRING_LIST_INIT_NODUP; + struct option options[] = { + OPT_BOOL(0, "yes", &boolean, "get a boolean"), + OPT_BOOL('D', "no-doubt", &boolean, "begins with 'no-'"), + { OPTION_SET_INT, 'B', "no-fear", &boolean, NULL, + "be brave", PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 }, + OPT_COUNTUP('b', "boolean", &boolean, "increment by one"), + OPT_BIT('4', "or4", &boolean, + "bitwise-or boolean with ...0100", 4), + OPT_NEGBIT(0, "neg-or4", &boolean, "same as --no-or4", 4), + OPT_GROUP(""), + OPT_INTEGER('i', "integer", &integer, "get a integer"), + OPT_INTEGER('j', NULL, &integer, "get a integer, too"), + OPT_MAGNITUDE('m', "magnitude", &magnitude, "get a magnitude"), + OPT_SET_INT(0, "set23", &integer, "set integer to 23", 23), + OPT_DATE('t', NULL, ×tamp, "get timestamp of <time>"), + OPT_CALLBACK('L', "length", &integer, "str", + "get length of <str>", length_callback), + OPT_FILENAME('F', "file", &file, "set file to <file>"), + OPT_GROUP("String options"), + OPT_STRING('s', "string", &string, "string", "get a string"), + OPT_STRING(0, "string2", &string, "str", "get another string"), + OPT_STRING(0, "st", &string, "st", "get another string (pervert ordering)"), + OPT_STRING('o', NULL, &string, "str", "get another string"), + OPT_NOOP_NOARG(0, "obsolete"), + OPT_STRING_LIST(0, "list", &list, "str", "add str to list"), + OPT_GROUP("Magic arguments"), + OPT_ARGUMENT("quux", "means --quux"), + OPT_NUMBER_CALLBACK(&integer, "set integer to NUM", + number_callback), + { OPTION_COUNTUP, '+', NULL, &boolean, NULL, "same as -b", + PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH }, + { OPTION_COUNTUP, 0, "ambiguous", &ambiguous, NULL, + "positive ambiguity", PARSE_OPT_NOARG | PARSE_OPT_NONEG }, + { OPTION_COUNTUP, 0, "no-ambiguous", &ambiguous, NULL, + "negative ambiguity", PARSE_OPT_NOARG | PARSE_OPT_NONEG }, + OPT_GROUP("Standard options"), + OPT__ABBREV(&abbrev), + OPT__VERBOSE(&verbose, "be verbose"), + OPT__DRY_RUN(&dry_run, "dry run"), + OPT__QUIET(&quiet, "be quiet"), + OPT_CALLBACK(0, "expect", &expect, "string", + "expected output in the variable dump", + collect_expect), + OPT_END(), + }; + int i; + int ret = 0; + + argc = parse_options(argc, (const char **)argv, prefix, options, usage, 0); + + if (length_cb.called) { + const char *arg = length_cb.arg; + int unset = length_cb.unset; + show(&expect, &ret, "Callback: \"%s\", %d", + (arg ? arg : "not set"), unset); + } + show(&expect, &ret, "boolean: %d", boolean); + show(&expect, &ret, "integer: %d", integer); + show(&expect, &ret, "magnitude: %lu", magnitude); + show(&expect, &ret, "timestamp: %lu", timestamp); + show(&expect, &ret, "string: %s", string ? string : "(not set)"); + show(&expect, &ret, "abbrev: %d", abbrev); + show(&expect, &ret, "verbose: %d", verbose); + show(&expect, &ret, "quiet: %d", quiet); + show(&expect, &ret, "dry run: %s", dry_run ? "yes" : "no"); + show(&expect, &ret, "file: %s", file ? file : "(not set)"); + + for (i = 0; i < list.nr; i++) + show(&expect, &ret, "list: %s", list.items[i].string); + + for (i = 0; i < argc; i++) + show(&expect, &ret, "arg %02d: %s", i, argv[i]); + + return ret; +} diff --git a/t/helper/test-path-utils.c b/t/helper/test-path-utils.c new file mode 100644 index 0000000000..ba805b374c --- /dev/null +++ b/t/helper/test-path-utils.c @@ -0,0 +1,262 @@ +#include "cache.h" +#include "string-list.h" + +/* + * A "string_list_each_func_t" function that normalizes an entry from + * GIT_CEILING_DIRECTORIES. If the path is unusable for some reason, + * die with an explanation. + */ +static int normalize_ceiling_entry(struct string_list_item *item, void *unused) +{ + char *ceil = item->string; + + if (!*ceil) + die("Empty path is not supported"); + if (!is_absolute_path(ceil)) + die("Path \"%s\" is not absolute", ceil); + if (normalize_path_copy(ceil, ceil) < 0) + die("Path \"%s\" could not be normalized", ceil); + return 1; +} + +static void normalize_argv_string(const char **var, const char *input) +{ + if (!strcmp(input, "<null>")) + *var = NULL; + else if (!strcmp(input, "<empty>")) + *var = ""; + else + *var = input; + + if (*var && (**var == '<' || **var == '(')) + die("Bad value: %s\n", input); +} + +struct test_data { + const char *from; /* input: transform from this ... */ + const char *to; /* output: ... to this. */ + const char *alternative; /* output: ... or this. */ +}; + +static int test_function(struct test_data *data, char *(*func)(char *input), + const char *funcname) +{ + int failed = 0, i; + char buffer[1024]; + char *to; + + for (i = 0; data[i].to; i++) { + if (!data[i].from) + to = func(NULL); + else { + xsnprintf(buffer, sizeof(buffer), "%s", data[i].from); + to = func(buffer); + } + if (!strcmp(to, data[i].to)) + continue; + if (!data[i].alternative) + error("FAIL: %s(%s) => '%s' != '%s'\n", + funcname, data[i].from, to, data[i].to); + else if (!strcmp(to, data[i].alternative)) + continue; + else + error("FAIL: %s(%s) => '%s' != '%s', '%s'\n", + funcname, data[i].from, to, data[i].to, + data[i].alternative); + failed = 1; + } + return failed; +} + +static struct test_data basename_data[] = { + /* --- POSIX type paths --- */ + { NULL, "." }, + { "", "." }, + { ".", "." }, + { "..", ".." }, + { "/", "/" }, + { "//", "/", "//" }, + { "///", "/", "//" }, + { "////", "/", "//" }, + { "usr", "usr" }, + { "/usr", "usr" }, + { "/usr/", "usr" }, + { "/usr//", "usr" }, + { "/usr/lib", "lib" }, + { "usr/lib", "lib" }, + { "usr/lib///", "lib" }, + +#if defined(__MINGW32__) || defined(_MSC_VER) + /* --- win32 type paths --- */ + { "\\usr", "usr" }, + { "\\usr\\", "usr" }, + { "\\usr\\\\", "usr" }, + { "\\usr\\lib", "lib" }, + { "usr\\lib", "lib" }, + { "usr\\lib\\\\\\", "lib" }, + { "C:/usr", "usr" }, + { "C:/usr", "usr" }, + { "C:/usr/", "usr" }, + { "C:/usr//", "usr" }, + { "C:/usr/lib", "lib" }, + { "C:usr/lib", "lib" }, + { "C:usr/lib///", "lib" }, + { "C:", "." }, + { "C:a", "a" }, + { "C:/", "/" }, + { "C:///", "/" }, + { "\\", "\\", "/" }, + { "\\\\", "\\", "/" }, + { "\\\\\\", "\\", "/" }, +#endif + { NULL, NULL } +}; + +static struct test_data dirname_data[] = { + /* --- POSIX type paths --- */ + { NULL, "." }, + { "", "." }, + { ".", "." }, + { "..", "." }, + { "/", "/" }, + { "//", "/", "//" }, + { "///", "/", "//" }, + { "////", "/", "//" }, + { "usr", "." }, + { "/usr", "/" }, + { "/usr/", "/" }, + { "/usr//", "/" }, + { "/usr/lib", "/usr" }, + { "usr/lib", "usr" }, + { "usr/lib///", "usr" }, + +#if defined(__MINGW32__) || defined(_MSC_VER) + /* --- win32 type paths --- */ + { "\\", "\\" }, + { "\\\\", "\\\\" }, + { "\\usr", "\\" }, + { "\\usr\\", "\\" }, + { "\\usr\\\\", "\\" }, + { "\\usr\\lib", "\\usr" }, + { "usr\\lib", "usr" }, + { "usr\\lib\\\\\\", "usr" }, + { "C:a", "C:." }, + { "C:/", "C:/" }, + { "C:///", "C:/" }, + { "C:/usr", "C:/" }, + { "C:/usr/", "C:/" }, + { "C:/usr//", "C:/" }, + { "C:/usr/lib", "C:/usr" }, + { "C:usr/lib", "C:usr" }, + { "C:usr/lib///", "C:usr" }, + { "\\\\\\", "\\" }, + { "\\\\\\\\", "\\" }, + { "C:", "C:.", "." }, +#endif + { NULL, NULL } +}; + +int main(int argc, char **argv) +{ + if (argc == 3 && !strcmp(argv[1], "normalize_path_copy")) { + char *buf = xmallocz(strlen(argv[2])); + int rv = normalize_path_copy(buf, argv[2]); + if (rv) + buf = "++failed++"; + puts(buf); + return 0; + } + + if (argc >= 2 && !strcmp(argv[1], "real_path")) { + while (argc > 2) { + puts(real_path(argv[2])); + argc--; + argv++; + } + return 0; + } + + if (argc >= 2 && !strcmp(argv[1], "absolute_path")) { + while (argc > 2) { + puts(absolute_path(argv[2])); + argc--; + argv++; + } + return 0; + } + + if (argc == 4 && !strcmp(argv[1], "longest_ancestor_length")) { + int len; + struct string_list ceiling_dirs = STRING_LIST_INIT_DUP; + char *path = xstrdup(argv[2]); + + /* + * We have to normalize the arguments because under + * Windows, bash mangles arguments that look like + * absolute POSIX paths or colon-separate lists of + * absolute POSIX paths into DOS paths (e.g., + * "/foo:/foo/bar" might be converted to + * "D:\Src\msysgit\foo;D:\Src\msysgit\foo\bar"), + * whereas longest_ancestor_length() requires paths + * that use forward slashes. + */ + if (normalize_path_copy(path, path)) + die("Path \"%s\" could not be normalized", argv[2]); + string_list_split(&ceiling_dirs, argv[3], PATH_SEP, -1); + filter_string_list(&ceiling_dirs, 0, + normalize_ceiling_entry, NULL); + len = longest_ancestor_length(path, &ceiling_dirs); + string_list_clear(&ceiling_dirs, 0); + free(path); + printf("%d\n", len); + return 0; + } + + if (argc >= 4 && !strcmp(argv[1], "prefix_path")) { + char *prefix = argv[2]; + int prefix_len = strlen(prefix); + int nongit_ok; + setup_git_directory_gently(&nongit_ok); + while (argc > 3) { + puts(prefix_path(prefix, prefix_len, argv[3])); + argc--; + argv++; + } + return 0; + } + + if (argc == 4 && !strcmp(argv[1], "strip_path_suffix")) { + char *prefix = strip_path_suffix(argv[2], argv[3]); + printf("%s\n", prefix ? prefix : "(null)"); + return 0; + } + + if (argc == 3 && !strcmp(argv[1], "print_path")) { + puts(argv[2]); + return 0; + } + + if (argc == 4 && !strcmp(argv[1], "relative_path")) { + struct strbuf sb = STRBUF_INIT; + const char *in, *prefix, *rel; + normalize_argv_string(&in, argv[2]); + normalize_argv_string(&prefix, argv[3]); + rel = relative_path(in, prefix, &sb); + if (!rel) + puts("(null)"); + else + puts(strlen(rel) > 0 ? rel : "(empty)"); + strbuf_release(&sb); + return 0; + } + + if (argc == 2 && !strcmp(argv[1], "basename")) + return test_function(basename_data, basename, argv[1]); + + if (argc == 2 && !strcmp(argv[1], "dirname")) + return test_function(dirname_data, dirname, argv[1]); + + fprintf(stderr, "%s: unknown function name: %s\n", argv[0], + argv[1] ? argv[1] : "(there was none)"); + return 1; +} diff --git a/t/helper/test-prio-queue.c b/t/helper/test-prio-queue.c new file mode 100644 index 0000000000..7be72f0086 --- /dev/null +++ b/t/helper/test-prio-queue.c @@ -0,0 +1,39 @@ +#include "cache.h" +#include "prio-queue.h" + +static int intcmp(const void *va, const void *vb, void *data) +{ + const int *a = va, *b = vb; + return *a - *b; +} + +static void show(int *v) +{ + if (!v) + printf("NULL\n"); + else + printf("%d\n", *v); + free(v); +} + +int main(int argc, char **argv) +{ + struct prio_queue pq = { intcmp }; + + while (*++argv) { + if (!strcmp(*argv, "get")) + show(prio_queue_get(&pq)); + else if (!strcmp(*argv, "dump")) { + int *v; + while ((v = prio_queue_get(&pq))) + show(v); + } + else { + int *v = malloc(sizeof(*v)); + *v = atoi(*argv); + prio_queue_put(&pq, v); + } + } + + return 0; +} diff --git a/t/helper/test-read-cache.c b/t/helper/test-read-cache.c new file mode 100644 index 0000000000..b25bcf139b --- /dev/null +++ b/t/helper/test-read-cache.c @@ -0,0 +1,13 @@ +#include "cache.h" + +int main (int argc, char **argv) +{ + int i, cnt = 1; + if (argc == 2) + cnt = strtol(argv[1], NULL, 0); + for (i = 0; i < cnt; i++) { + read_cache(); + discard_cache(); + } + return 0; +} diff --git a/t/helper/test-regex.c b/t/helper/test-regex.c new file mode 100644 index 0000000000..0dc598ecdc --- /dev/null +++ b/t/helper/test-regex.c @@ -0,0 +1,20 @@ +#include "git-compat-util.h" + +int main(int argc, char **argv) +{ + char *pat = "[^={} \t]+"; + char *str = "={}\nfred"; + regex_t r; + regmatch_t m[1]; + + if (regcomp(&r, pat, REG_EXTENDED | REG_NEWLINE)) + die("failed regcomp() for pattern '%s'", pat); + if (regexec(&r, str, 1, m, 0)) + die("no match of pattern '%s' to string '%s'", pat, str); + + /* http://sourceware.org/bugzilla/show_bug.cgi?id=3957 */ + if (m[0].rm_so == 3) /* matches '\n' when it should not */ + die("regex bug confirmed: re-build git with NO_REGEX=1"); + + exit(0); +} diff --git a/t/helper/test-revision-walking.c b/t/helper/test-revision-walking.c new file mode 100644 index 0000000000..3d0313354b --- /dev/null +++ b/t/helper/test-revision-walking.c @@ -0,0 +1,68 @@ +/* + * test-revision-walking.c: test revision walking API. + * + * (C) 2012 Heiko Voigt <hvoigt@hvoigt.net> + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "cache.h" +#include "commit.h" +#include "diff.h" +#include "revision.h" + +static void print_commit(struct commit *commit) +{ + struct strbuf sb = STRBUF_INIT; + struct pretty_print_context ctx = {0}; + ctx.date_mode.type = DATE_NORMAL; + format_commit_message(commit, " %m %s", &sb, &ctx); + printf("%s\n", sb.buf); + strbuf_release(&sb); +} + +static int run_revision_walk(void) +{ + struct rev_info rev; + struct commit *commit; + const char *argv[] = {NULL, "--all", NULL}; + int argc = ARRAY_SIZE(argv) - 1; + int got_revision = 0; + + init_revisions(&rev, NULL); + setup_revisions(argc, argv, &rev, NULL); + if (prepare_revision_walk(&rev)) + die("revision walk setup failed"); + + while ((commit = get_revision(&rev)) != NULL) { + print_commit(commit); + got_revision = 1; + } + + reset_revision_walk(); + return got_revision; +} + +int main(int argc, char **argv) +{ + if (argc < 2) + return 1; + + setup_git_directory(); + + if (!strcmp(argv[1], "run-twice")) { + printf("1st\n"); + if (!run_revision_walk()) + return 1; + printf("2nd\n"); + if (!run_revision_walk()) + return 1; + + return 0; + } + + fprintf(stderr, "check usage\n"); + return 1; +} diff --git a/t/helper/test-run-command.c b/t/helper/test-run-command.c new file mode 100644 index 0000000000..30a64a98dc --- /dev/null +++ b/t/helper/test-run-command.c @@ -0,0 +1,87 @@ +/* + * test-run-command.c: test run command API. + * + * (C) 2009 Ilari Liusvaara <ilari.liusvaara@elisanet.fi> + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "git-compat-util.h" +#include "run-command.h" +#include "argv-array.h" +#include "strbuf.h" +#include <string.h> +#include <errno.h> + +static int number_callbacks; +static int parallel_next(struct child_process *cp, + struct strbuf *err, + void *cb, + void **task_cb) +{ + struct child_process *d = cb; + if (number_callbacks >= 4) + return 0; + + argv_array_pushv(&cp->args, d->argv); + strbuf_addf(err, "preloaded output of a child\n"); + number_callbacks++; + return 1; +} + +static int no_job(struct child_process *cp, + struct strbuf *err, + void *cb, + void **task_cb) +{ + strbuf_addf(err, "no further jobs available\n"); + return 0; +} + +static int task_finished(int result, + struct strbuf *err, + void *pp_cb, + void *pp_task_cb) +{ + strbuf_addf(err, "asking for a quick stop\n"); + return 1; +} + +int main(int argc, char **argv) +{ + struct child_process proc = CHILD_PROCESS_INIT; + int jobs; + + if (argc < 3) + return 1; + proc.argv = (const char **)argv + 2; + + if (!strcmp(argv[1], "start-command-ENOENT")) { + if (start_command(&proc) < 0 && errno == ENOENT) + return 0; + fprintf(stderr, "FAIL %s\n", argv[1]); + return 1; + } + if (!strcmp(argv[1], "run-command")) + exit(run_command(&proc)); + + jobs = atoi(argv[2]); + proc.argv = (const char **)argv + 3; + + if (!strcmp(argv[1], "run-command-parallel")) + exit(run_processes_parallel(jobs, parallel_next, + NULL, NULL, &proc)); + + if (!strcmp(argv[1], "run-command-abort")) + exit(run_processes_parallel(jobs, parallel_next, + NULL, task_finished, &proc)); + + if (!strcmp(argv[1], "run-command-no-jobs")) + exit(run_processes_parallel(jobs, no_job, + NULL, task_finished, &proc)); + + fprintf(stderr, "check usage\n"); + return 1; +} diff --git a/t/helper/test-scrap-cache-tree.c b/t/helper/test-scrap-cache-tree.c new file mode 100644 index 0000000000..6efee31a48 --- /dev/null +++ b/t/helper/test-scrap-cache-tree.c @@ -0,0 +1,17 @@ +#include "cache.h" +#include "lockfile.h" +#include "tree.h" +#include "cache-tree.h" + +static struct lock_file index_lock; + +int main(int ac, char **av) +{ + hold_locked_index(&index_lock, 1); + if (read_cache() < 0) + die("unable to read index file"); + active_cache_tree = NULL; + if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK)) + die("unable to write index file"); + return 0; +} diff --git a/t/helper/test-sha1-array.c b/t/helper/test-sha1-array.c new file mode 100644 index 0000000000..60ea1d5f14 --- /dev/null +++ b/t/helper/test-sha1-array.c @@ -0,0 +1,34 @@ +#include "cache.h" +#include "sha1-array.h" + +static void print_sha1(const unsigned char sha1[20], void *data) +{ + puts(sha1_to_hex(sha1)); +} + +int main(int argc, char **argv) +{ + struct sha1_array array = SHA1_ARRAY_INIT; + struct strbuf line = STRBUF_INIT; + + while (strbuf_getline(&line, stdin) != EOF) { + const char *arg; + unsigned char sha1[20]; + + if (skip_prefix(line.buf, "append ", &arg)) { + if (get_sha1_hex(arg, sha1)) + die("not a hexadecimal SHA1: %s", arg); + sha1_array_append(&array, sha1); + } else if (skip_prefix(line.buf, "lookup ", &arg)) { + if (get_sha1_hex(arg, sha1)) + die("not a hexadecimal SHA1: %s", arg); + printf("%d\n", sha1_array_lookup(&array, sha1)); + } else if (!strcmp(line.buf, "clear")) + sha1_array_clear(&array); + else if (!strcmp(line.buf, "for_each_unique")) + sha1_array_for_each_unique(&array, print_sha1, NULL); + else + die("unknown command: %s", line.buf); + } + return 0; +} diff --git a/t/helper/test-sha1.c b/t/helper/test-sha1.c new file mode 100644 index 0000000000..e57eae10bf --- /dev/null +++ b/t/helper/test-sha1.c @@ -0,0 +1,56 @@ +#include "cache.h" + +int main(int ac, char **av) +{ + git_SHA_CTX ctx; + unsigned char sha1[20]; + unsigned bufsz = 8192; + int binary = 0; + char *buffer; + + if (ac == 2) { + if (!strcmp(av[1], "-b")) + binary = 1; + else + bufsz = strtoul(av[1], NULL, 10) * 1024 * 1024; + } + + if (!bufsz) + bufsz = 8192; + + while ((buffer = malloc(bufsz)) == NULL) { + fprintf(stderr, "bufsz %u is too big, halving...\n", bufsz); + bufsz /= 2; + if (bufsz < 1024) + die("OOPS"); + } + + git_SHA1_Init(&ctx); + + while (1) { + ssize_t sz, this_sz; + char *cp = buffer; + unsigned room = bufsz; + this_sz = 0; + while (room) { + sz = xread(0, cp, room); + if (sz == 0) + break; + if (sz < 0) + die_errno("test-sha1"); + this_sz += sz; + cp += sz; + room -= sz; + } + if (this_sz == 0) + break; + git_SHA1_Update(&ctx, buffer, this_sz); + } + git_SHA1_Final(sha1, &ctx); + + if (binary) + fwrite(sha1, 1, 20, stdout); + else + puts(sha1_to_hex(sha1)); + exit(0); +} diff --git a/t/helper/test-sha1.sh b/t/helper/test-sha1.sh new file mode 100755 index 0000000000..750b95a0a1 --- /dev/null +++ b/t/helper/test-sha1.sh @@ -0,0 +1,83 @@ +#!/bin/sh + +dd if=/dev/zero bs=1048576 count=100 2>/dev/null | +/usr/bin/time t/helper/test-sha1 >/dev/null + +while read expect cnt pfx +do + case "$expect" in '#'*) continue ;; esac + actual=$( + { + test -z "$pfx" || echo "$pfx" + dd if=/dev/zero bs=1048576 count=$cnt 2>/dev/null | + perl -pe 'y/\000/g/' + } | ./t/helper/test-sha1 $cnt + ) + if test "$expect" = "$actual" + then + echo "OK: $expect $cnt $pfx" + else + echo >&2 "OOPS: $cnt" + echo >&2 "expect: $expect" + echo >&2 "actual: $actual" + exit 1 + fi +done <<EOF +da39a3ee5e6b4b0d3255bfef95601890afd80709 0 +3f786850e387550fdab836ed7e6dc881de23001b 0 a +5277cbb45a15902137d332d97e89cf8136545485 0 ab +03cfd743661f07975fa2f1220c5194cbaff48451 0 abc +3330b4373640f9e4604991e73c7e86bfd8da2dc3 0 abcd +ec11312386ad561674f724b8cca7cf1796e26d1d 0 abcde +bdc37c074ec4ee6050d68bc133c6b912f36474df 0 abcdef +69bca99b923859f2dc486b55b87f49689b7358c7 0 abcdefg +e414af7161c9554089f4106d6f1797ef14a73666 0 abcdefgh +0707f2970043f9f7c22029482db27733deaec029 0 abcdefghi +a4dd8aa74a5636728fe52451636e2e17726033aa 1 +9986b45e2f4d7086372533bb6953a8652fa3644a 1 frotz +23d8d4f788e8526b4877548a32577543cbaaf51f 10 +8cd23f822ab44c7f481b8c92d591f6d1fcad431c 10 frotz +f3b5604a4e604899c1233edb3bf1cc0ede4d8c32 512 +b095bd837a371593048136e429e9ac4b476e1bb3 512 frotz +08fa81d6190948de5ccca3966340cc48c10cceac 1200 xyzzy +e33a291f42c30a159733dd98b8b3e4ff34158ca0 4090 4G +#a3bf783bc20caa958f6cb24dd140a7b21984838d 9999 nitfol +EOF + +exit + +# generating test vectors +# inputs are number of megabytes followed by some random string to prefix. + +while read cnt pfx +do + actual=$( + { + test -z "$pfx" || echo "$pfx" + dd if=/dev/zero bs=1048576 count=$cnt 2>/dev/null | + perl -pe 'y/\000/g/' + } | sha1sum | + sed -e 's/ .*//' + ) + echo "$actual $cnt $pfx" +done <<EOF +0 +0 a +0 ab +0 abc +0 abcd +0 abcde +0 abcdef +0 abcdefg +0 abcdefgh +0 abcdefghi +1 +1 frotz +10 +10 frotz +512 +512 frotz +1200 xyzzy +4090 4G +9999 nitfol +EOF diff --git a/t/helper/test-sigchain.c b/t/helper/test-sigchain.c new file mode 100644 index 0000000000..e499fce60f --- /dev/null +++ b/t/helper/test-sigchain.c @@ -0,0 +1,22 @@ +#include "cache.h" +#include "sigchain.h" + +#define X(f) \ +static void f(int sig) { \ + puts(#f); \ + fflush(stdout); \ + sigchain_pop(sig); \ + raise(sig); \ +} +X(one) +X(two) +X(three) +#undef X + +int main(int argc, char **argv) { + sigchain_push(SIGTERM, one); + sigchain_push(SIGTERM, two); + sigchain_push(SIGTERM, three); + raise(SIGTERM); + return 0; +} diff --git a/t/helper/test-string-list.c b/t/helper/test-string-list.c new file mode 100644 index 0000000000..14bdf9d215 --- /dev/null +++ b/t/helper/test-string-list.c @@ -0,0 +1,103 @@ +#include "cache.h" +#include "string-list.h" + +/* + * Parse an argument into a string list. arg should either be a + * ':'-separated list of strings, or "-" to indicate an empty string + * list (as opposed to "", which indicates a string list containing a + * single empty string). list->strdup_strings must be set. + */ +static void parse_string_list(struct string_list *list, const char *arg) +{ + if (!strcmp(arg, "-")) + return; + + (void)string_list_split(list, arg, ':', -1); +} + +static void write_list(const struct string_list *list) +{ + int i; + for (i = 0; i < list->nr; i++) + printf("[%d]: \"%s\"\n", i, list->items[i].string); +} + +static void write_list_compact(const struct string_list *list) +{ + int i; + if (!list->nr) + printf("-\n"); + else { + printf("%s", list->items[0].string); + for (i = 1; i < list->nr; i++) + printf(":%s", list->items[i].string); + printf("\n"); + } +} + +static int prefix_cb(struct string_list_item *item, void *cb_data) +{ + const char *prefix = (const char *)cb_data; + return starts_with(item->string, prefix); +} + +int main(int argc, char **argv) +{ + if (argc == 5 && !strcmp(argv[1], "split")) { + struct string_list list = STRING_LIST_INIT_DUP; + int i; + const char *s = argv[2]; + int delim = *argv[3]; + int maxsplit = atoi(argv[4]); + + i = string_list_split(&list, s, delim, maxsplit); + printf("%d\n", i); + write_list(&list); + string_list_clear(&list, 0); + return 0; + } + + if (argc == 5 && !strcmp(argv[1], "split_in_place")) { + struct string_list list = STRING_LIST_INIT_NODUP; + int i; + char *s = xstrdup(argv[2]); + int delim = *argv[3]; + int maxsplit = atoi(argv[4]); + + i = string_list_split_in_place(&list, s, delim, maxsplit); + printf("%d\n", i); + write_list(&list); + string_list_clear(&list, 0); + free(s); + return 0; + } + + if (argc == 4 && !strcmp(argv[1], "filter")) { + /* + * Retain only the items that have the specified prefix. + * Arguments: list|- prefix + */ + struct string_list list = STRING_LIST_INIT_DUP; + const char *prefix = argv[3]; + + parse_string_list(&list, argv[2]); + filter_string_list(&list, 0, prefix_cb, (void *)prefix); + write_list_compact(&list); + string_list_clear(&list, 0); + return 0; + } + + if (argc == 3 && !strcmp(argv[1], "remove_duplicates")) { + struct string_list list = STRING_LIST_INIT_DUP; + + parse_string_list(&list, argv[2]); + string_list_remove_duplicates(&list, 0); + write_list_compact(&list); + string_list_clear(&list, 0); + return 0; + } + + fprintf(stderr, "%s: unknown function name: %s\n", argv[0], + argv[1] ? argv[1] : "(there was none)"); + return 1; +} diff --git a/t/helper/test-submodule-config.c b/t/helper/test-submodule-config.c new file mode 100644 index 0000000000..dab8c27768 --- /dev/null +++ b/t/helper/test-submodule-config.c @@ -0,0 +1,76 @@ +#include "cache.h" +#include "submodule-config.h" +#include "submodule.h" + +static void die_usage(int argc, char **argv, const char *msg) +{ + fprintf(stderr, "%s\n", msg); + fprintf(stderr, "Usage: %s [<commit> <submodulepath>] ...\n", argv[0]); + exit(1); +} + +static int git_test_config(const char *var, const char *value, void *cb) +{ + return parse_submodule_config_option(var, value); +} + +int main(int argc, char **argv) +{ + char **arg = argv; + int my_argc = argc; + int output_url = 0; + int lookup_name = 0; + + arg++; + my_argc--; + while (starts_with(arg[0], "--")) { + if (!strcmp(arg[0], "--url")) + output_url = 1; + if (!strcmp(arg[0], "--name")) + lookup_name = 1; + arg++; + my_argc--; + } + + if (my_argc % 2 != 0) + die_usage(argc, argv, "Wrong number of arguments."); + + setup_git_directory(); + gitmodules_config(); + git_config(git_test_config, NULL); + + while (*arg) { + unsigned char commit_sha1[20]; + const struct submodule *submodule; + const char *commit; + const char *path_or_name; + + commit = arg[0]; + path_or_name = arg[1]; + + if (commit[0] == '\0') + hashcpy(commit_sha1, null_sha1); + else if (get_sha1(commit, commit_sha1) < 0) + die_usage(argc, argv, "Commit not found."); + + if (lookup_name) { + submodule = submodule_from_name(commit_sha1, path_or_name); + } else + submodule = submodule_from_path(commit_sha1, path_or_name); + if (!submodule) + die_usage(argc, argv, "Submodule not found."); + + if (output_url) + printf("Submodule url: '%s' for path '%s'\n", + submodule->url, submodule->path); + else + printf("Submodule name: '%s' for path '%s'\n", + submodule->name, submodule->path); + + arg += 2; + } + + submodule_free(); + + return 0; +} diff --git a/t/helper/test-subprocess.c b/t/helper/test-subprocess.c new file mode 100644 index 0000000000..56881a0324 --- /dev/null +++ b/t/helper/test-subprocess.c @@ -0,0 +1,19 @@ +#include "cache.h" +#include "run-command.h" + +int main(int argc, char **argv) +{ + struct child_process cp = CHILD_PROCESS_INIT; + int nogit = 0; + + setup_git_directory_gently(&nogit); + if (nogit) + die("No git repo found"); + if (argc > 1 && !strcmp(argv[1], "--setup-work-tree")) { + setup_work_tree(); + argv++; + } + cp.git_cmd = 1; + cp.argv = (const char **)argv + 1; + return run_command(&cp); +} diff --git a/t/helper/test-svn-fe.c b/t/helper/test-svn-fe.c new file mode 100644 index 0000000000..120ec96b0d --- /dev/null +++ b/t/helper/test-svn-fe.c @@ -0,0 +1,52 @@ +/* + * test-svn-fe: Code to exercise the svn import lib + */ + +#include "git-compat-util.h" +#include "vcs-svn/svndump.h" +#include "vcs-svn/svndiff.h" +#include "vcs-svn/sliding_window.h" +#include "vcs-svn/line_buffer.h" + +static const char test_svnfe_usage[] = + "test-svn-fe (<dumpfile> | [-d] <preimage> <delta> <len>)"; + +static int apply_delta(int argc, char *argv[]) +{ + struct line_buffer preimage = LINE_BUFFER_INIT; + struct line_buffer delta = LINE_BUFFER_INIT; + struct sliding_view preimage_view = SLIDING_VIEW_INIT(&preimage, -1); + + if (argc != 5) + usage(test_svnfe_usage); + + if (buffer_init(&preimage, argv[2])) + die_errno("cannot open preimage"); + if (buffer_init(&delta, argv[3])) + die_errno("cannot open delta"); + if (svndiff0_apply(&delta, (off_t) strtoumax(argv[4], NULL, 0), + &preimage_view, stdout)) + return 1; + if (buffer_deinit(&preimage)) + die_errno("cannot close preimage"); + if (buffer_deinit(&delta)) + die_errno("cannot close delta"); + strbuf_release(&preimage_view.buf); + return 0; +} + +int main(int argc, char *argv[]) +{ + if (argc == 2) { + if (svndump_init(argv[1])) + return 1; + svndump_read(NULL, "refs/heads/master", "refs/notes/svn/revs"); + svndump_deinit(); + svndump_reset(); + return 0; + } + + if (argc >= 2 && !strcmp(argv[1], "-d")) + return apply_delta(argc, argv); + usage(test_svnfe_usage); +} diff --git a/t/helper/test-urlmatch-normalization.c b/t/helper/test-urlmatch-normalization.c new file mode 100644 index 0000000000..090bf219a7 --- /dev/null +++ b/t/helper/test-urlmatch-normalization.c @@ -0,0 +1,50 @@ +#include "git-compat-util.h" +#include "urlmatch.h" + +int main(int argc, char **argv) +{ + const char usage[] = "test-urlmatch-normalization [-p | -l] <url1> | <url1> <url2>"; + char *url1, *url2; + int opt_p = 0, opt_l = 0; + + /* + * For one url, succeed if url_normalize succeeds on it, fail otherwise. + * For two urls, succeed only if url_normalize succeeds on both and + * the results compare equal with strcmp. If -p is given (one url only) + * and url_normalize succeeds, print the result followed by "\n". If + * -l is given (one url only) and url_normalize succeeds, print the + * returned length in decimal followed by "\n". + */ + + if (argc > 1 && !strcmp(argv[1], "-p")) { + opt_p = 1; + argc--; + argv++; + } else if (argc > 1 && !strcmp(argv[1], "-l")) { + opt_l = 1; + argc--; + argv++; + } + + if (argc < 2 || argc > 3) + die("%s", usage); + + if (argc == 2) { + struct url_info info; + url1 = url_normalize(argv[1], &info); + if (!url1) + return 1; + if (opt_p) + printf("%s\n", url1); + if (opt_l) + printf("%u\n", (unsigned)info.url_len); + return 0; + } + + if (opt_p || opt_l) + die("%s", usage); + + url1 = url_normalize(argv[1], NULL); + url2 = url_normalize(argv[2], NULL); + return (url1 && url2 && !strcmp(url1, url2)) ? 0 : 1; +} diff --git a/t/helper/test-wildmatch.c b/t/helper/test-wildmatch.c new file mode 100644 index 0000000000..578b164fe6 --- /dev/null +++ b/t/helper/test-wildmatch.c @@ -0,0 +1,21 @@ +#include "cache.h" + +int main(int argc, char **argv) +{ + int i; + for (i = 2; i < argc; i++) { + if (argv[i][0] == '/') + die("Forward slash is not allowed at the beginning of the\n" + "pattern because Windows does not like it. Use `XXX/' instead."); + else if (!strncmp(argv[i], "XXX/", 4)) + argv[i] += 3; + } + if (!strcmp(argv[1], "wildmatch")) + return !!wildmatch(argv[3], argv[2], WM_PATHNAME, NULL); + else if (!strcmp(argv[1], "iwildmatch")) + return !!wildmatch(argv[3], argv[2], WM_PATHNAME | WM_CASEFOLD, NULL); + else if (!strcmp(argv[1], "pathmatch")) + return !!wildmatch(argv[3], argv[2], 0, NULL); + else + return 1; +} diff --git a/t/lib-git-p4.sh b/t/lib-git-p4.sh index f9ae1d780d..54fd5a6ca0 100644 --- a/t/lib-git-p4.sh +++ b/t/lib-git-p4.sh @@ -33,7 +33,7 @@ fi # Older versions of perforce were available compiled natively for # cygwin. Those do not accept native windows paths, so make sure # not to convert for them. -native_path() { +native_path () { path="$1" && if test_have_prereq CYGWIN && ! p4 -V | grep -q CYGWIN then @@ -49,8 +49,8 @@ native_path() { # Attention: This function is not safe again against time offset updates # at runtime (e.g. via NTP). The 'clock_gettime(CLOCK_MONOTONIC)' # function could fix that but it is not in Python until 3.3. -time_in_seconds() { - python -c 'import time; print int(time.time())' +time_in_seconds () { + (cd / && "$PYTHON_PATH" -c 'import time; print(int(time.time()))') } # Try to pick a unique port: guess a large number, then hope @@ -75,7 +75,7 @@ git="$TRASH_DIRECTORY/git" pidfile="$TRASH_DIRECTORY/p4d.pid" # Sometimes "prove" seems to hang on exit because p4d is still running -cleanup() { +cleanup () { if test -f "$pidfile" then kill -9 $(cat "$pidfile") 2>/dev/null && exit 255 @@ -89,7 +89,7 @@ trap cleanup EXIT TMPDIR="$TRASH_DIRECTORY" export TMPDIR -start_p4d() { +start_p4d () { mkdir -p "$db" "$cli" "$git" && rm -f "$pidfile" && ( @@ -151,7 +151,7 @@ start_p4d() { return 0 } -p4_add_user() { +p4_add_user () { name=$1 && p4 user -f -i <<-EOF User: $name @@ -160,7 +160,16 @@ p4_add_user() { EOF } -retry_until_success() { +p4_add_job () { + p4 job -f -i <<-EOF + Job: $1 + Status: open + User: dummy + Description: + EOF +} + +retry_until_success () { timeout=$(($(time_in_seconds) + $RETRY_TIMEOUT)) until "$@" 2>/dev/null || test $(time_in_seconds) -gt $timeout do @@ -168,7 +177,7 @@ retry_until_success() { done } -retry_until_fail() { +retry_until_fail () { timeout=$(($(time_in_seconds) + $RETRY_TIMEOUT)) until ! "$@" 2>/dev/null || test $(time_in_seconds) -gt $timeout do @@ -176,7 +185,7 @@ retry_until_fail() { done } -kill_p4d() { +kill_p4d () { pid=$(cat "$pidfile") retry_until_fail kill $pid retry_until_fail kill -9 $pid @@ -186,21 +195,22 @@ kill_p4d() { retry_until_fail kill -9 $watchdog_pid } -cleanup_git() { +cleanup_git () { retry_until_success rm -r "$git" test_must_fail test -d "$git" && retry_until_success mkdir "$git" } -marshal_dump() { +marshal_dump () { what=$1 && line=${2:-1} && cat >"$TRASH_DIRECTORY/marshal-dump.py" <<-EOF && import marshal import sys + instream = getattr(sys.stdin, 'buffer', sys.stdin) for i in range($line): - d = marshal.load(sys.stdin) - print d['$what'] + d = marshal.load(instream) + print(d[b'$what'].decode('utf-8')) EOF "$PYTHON_PATH" "$TRASH_DIRECTORY/marshal-dump.py" } @@ -208,7 +218,7 @@ marshal_dump() { # # Construct a client with this list of View lines # -client_view() { +client_view () { ( cat <<-EOF && Client: $P4CLIENT @@ -222,7 +232,7 @@ client_view() { ) | p4 client -i } -is_cli_file_writeable() { +is_cli_file_writeable () { # cygwin version of p4 does not set read-only attr, # will be marked 444 but -w is true file="$1" && diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh index b0ec12ff6c..fb8823224e 100644 --- a/t/lib-git-svn.sh +++ b/t/lib-git-svn.sh @@ -1,8 +1,5 @@ . ./test-lib.sh -remotes_git_svn=remotes/git""-svn -git_svn_id=git""-svn-id - if test -n "$NO_SVN_TESTS" then skip_all='skipping git svn tests, NO_SVN_TESTS defined' @@ -186,3 +183,15 @@ start_svnserve () { --listen-host 127.0.0.1 & } +prepare_a_utf8_locale () { + a_utf8_locale=$(locale -a | sed -n '/\.[uU][tT][fF]-*8$/{ + p + q +}') + if test -n "$a_utf8_locale" + then + test_set_prereq UTF8 + else + say "# UTF-8 locale not available, some tests are skipped" + fi +} diff --git a/t/lib-gpg.sh b/t/lib-gpg.sh index db2ef22e8f..ec2aa8f687 100755 --- a/t/lib-gpg.sh +++ b/t/lib-gpg.sh @@ -1,9 +1,8 @@ #!/bin/sh gpg_version=$(gpg --version 2>&1) -if test $? = 127; then - say "You do not seem to have gpg installed" -else +if test $? != 127 +then # As said here: http://www.gnupg.org/documentation/faqs.html#q6.19 # the gpg version 1.0.6 didn't parse trust packets correctly, so for # that version, creation of signed tags using the generated key fails. diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf index 7d15e6d44c..018a83a5a1 100644 --- a/t/lib-httpd/apache.conf +++ b/t/lib-httpd/apache.conf @@ -64,6 +64,9 @@ LockFile accept.lock <IfModule !mod_mpm_prefork.c> LoadModule mpm_prefork_module modules/mod_mpm_prefork.so </IfModule> +<IfModule !mod_unixd.c> + LoadModule unixd_module modules/mod_unixd.so +</IfModule> </IfVersion> PassEnv GIT_VALGRIND @@ -71,6 +74,7 @@ PassEnv GIT_VALGRIND_OPTIONS PassEnv GNUPGHOME PassEnv ASAN_OPTIONS PassEnv GIT_TRACE +PassEnv GIT_CONFIG_NOSYSTEM Alias /dumb/ www/ Alias /auth/dumb/ www/auth/dumb/ @@ -98,6 +102,10 @@ Alias /auth/dumb/ www/auth/dumb/ SetEnv GIT_HTTP_EXPORT_ALL Header set Set-Cookie name=value </LocationMatch> +<LocationMatch /smart_headers/> + SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH} + SetEnv GIT_HTTP_EXPORT_ALL +</LocationMatch> ScriptAliasMatch /smart_*[^/]*/(.*) ${GIT_EXEC_PATH}/git-http-backend/$1 ScriptAlias /broken_smart/ broken-smart-http.sh/ ScriptAlias /error/ error.sh/ @@ -124,6 +132,18 @@ RewriteRule ^/ftp-redir/(.*)$ ftp://localhost:1000/$1 [R=302] RewriteRule ^/loop-redir/x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-(.*) /$1 [R=302] RewriteRule ^/loop-redir/(.*)$ /loop-redir/x-$1 [R=302] +# Apache 2.2 does not understand <RequireAll>, so we use RewriteCond. +# And as RewriteCond does not allow testing for non-matches, we match +# the desired case first (one has abra, two has cadabra), and let it +# pass by marking the RewriteRule as [L], "last rule, do not process +# any other matching RewriteRules after this"), and then have another +# RewriteRule that matches all other cases and lets them fail via '[F]', +# "fail the request". +RewriteCond %{HTTP:x-magic-one} =abra +RewriteCond %{HTTP:x-magic-two} =cadabra +RewriteRule ^/smart_headers/.* - [L] +RewriteRule ^/smart_headers/.* - [F] + <IfDefine SSL> LoadModule ssl_module modules/mod_ssl.so diff --git a/t/perf/p3404-rebase-interactive.sh b/t/perf/p3404-rebase-interactive.sh new file mode 100755 index 0000000000..88f47de282 --- /dev/null +++ b/t/perf/p3404-rebase-interactive.sh @@ -0,0 +1,36 @@ +#!/bin/sh + +test_description='Tests rebase -i performance' +. ./perf-lib.sh + +test_perf_default_repo + +# This commit merges a sufficiently long topic branch for reasonable +# performance testing +branch_merge=ba5312da19c6fdb6c6747d479f58932aae6e900c^{commit} +export branch_merge + +git rev-parse --verify $branch_merge >/dev/null 2>&1 || { + skip_all='skipping because $branch_merge was not found' + test_done +} + +write_script swap-first-two.sh <<\EOF +case "$1" in +*/COMMIT_EDITMSG) + mv "$1" "$1".bak && + sed -e '1{h;d}' -e 2G <"$1".bak >"$1" + ;; +esac +EOF + +test_expect_success 'setup' ' + git config core.editor "\"$PWD"/swap-first-two.sh\" && + git checkout -f $branch_merge^2 +' + +test_perf 'rebase -i' ' + git rebase -i $branch_merge^ +' + +test_done diff --git a/t/perf/perf-lib.sh b/t/perf/perf-lib.sh index 5cf74eddec..5ef17440c0 100644 --- a/t/perf/perf-lib.sh +++ b/t/perf/perf-lib.sh @@ -80,23 +80,26 @@ test_perf_create_repo_from () { error "bug in the test script: not 2 parameters to test-create-repo" repo="$1" source="$2" - source_git=$source/$(cd "$source" && git rev-parse --git-dir) + source_git="$(git -C "$source" rev-parse --git-dir)" + objects_dir="$(git -C "$source" rev-parse --git-path objects)" mkdir -p "$repo/.git" ( - cd "$repo/.git" && - { cp -Rl "$source_git/objects" . 2>/dev/null || - cp -R "$source_git/objects" .; } && + { cp -Rl "$objects_dir" "$repo/.git/" 2>/dev/null || + cp -R "$objects_dir" "$repo/.git/"; } && for stuff in "$source_git"/*; do case "$stuff" in - */objects|*/hooks|*/config) + */objects|*/hooks|*/config|*/commondir) ;; *) - cp -R "$stuff" . || exit 1 + cp -R "$stuff" "$repo/.git/" || exit 1 ;; esac done && - cd .. && - git init -q && + cd "$repo" && + git init -q && { + test_have_prereq SYMLINKS || + git config core.symlinks false + } && mv .git/hooks .git/hooks-disabled 2>/dev/null ) || error "failed to copy repository '$source' to '$repo'" } diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh index 79b9074172..60811a3a7c 100755 --- a/t/t0000-basic.sh +++ b/t/t0000-basic.sh @@ -98,7 +98,7 @@ check_sub_test_lib_test () { } check_sub_test_lib_test_err () { - name="$1" # stdin is the expected output output from the test + name="$1" # stdin is the expected output from the test # expected error output is in descriptior 3 ( cd "$name" && diff --git a/t/t0001-init.sh b/t/t0001-init.sh index 295aa5949a..a6fdd5ef3a 100755 --- a/t/t0001-init.sh +++ b/t/t0001-init.sh @@ -88,19 +88,17 @@ test_expect_success 'plain nested in bare through aliased command' ' ' test_expect_success 'No extra GIT_* on alias scripts' ' - ( - env | sed -ne "/^GIT_/s/=.*//p" && - echo GIT_PREFIX && # setup.c - echo GIT_TEXTDOMAINDIR # wrapper-for-bin.sh - ) | sort | uniq >expected && - cat <<-\EOF >script && - #!/bin/sh - env | sed -ne "/^GIT_/s/=.*//p" | sort >actual - exit 0 + write_script script <<-\EOF && + env | + sed -n \ + -e "/^GIT_PREFIX=/d" \ + -e "/^GIT_TEXTDOMAINDIR=/d" \ + -e "/^GIT_/s/=.*//p" | + sort EOF - chmod 755 script && + ./script >expected && git config alias.script \!./script && - ( mkdir sub && cd sub && git script ) && + ( mkdir sub && cd sub && git script >../actual ) && test_cmp expected actual ' @@ -356,4 +354,34 @@ test_expect_success SYMLINKS 're-init to move gitdir symlink' ' test_path_is_dir realgitdir/refs ' +# Tests for the hidden file attribute on windows +is_hidden () { + # Use the output of `attrib`, ignore the absolute path + case "$(attrib "$1")" in *H*?:*) return 0;; esac + return 1 +} + +test_expect_success MINGW '.git hidden' ' + rm -rf newdir && + ( + unset GIT_DIR GIT_WORK_TREE + mkdir newdir && + cd newdir && + git init && + is_hidden .git + ) && + check_config newdir/.git false unset +' + +test_expect_success MINGW 'bare git dir not hidden' ' + rm -rf newdir && + ( + unset GIT_DIR GIT_WORK_TREE GIT_CONFIG + mkdir newdir && + cd newdir && + git --bare init + ) && + ! is_hidden newdir +' + test_done diff --git a/t/t0027-auto-crlf.sh b/t/t0027-auto-crlf.sh index 504e5a02a1..93725895a4 100755 --- a/t/t0027-auto-crlf.sh +++ b/t/t0027-auto-crlf.sh @@ -12,7 +12,7 @@ fi compare_files () { tr '\015\000' QN <"$1" >"$1".expect && - tr '\015\000' QN <"$2" >"$2".actual && + tr '\015\000' QN <"$2" | tr -d 'Z' >"$2".actual && test_cmp "$1".expect "$2".actual && rm "$1".expect "$2".actual } @@ -21,51 +21,48 @@ compare_ws_file () { pfx=$1 exp=$2.expect act=$pfx.actual.$3 - tr '\015\000' QN <"$2" >"$exp" && - tr '\015\000' QN <"$3" >"$act" && - test_cmp $exp $act && - rm $exp $act + tr '\015\000abcdef0123456789' QN00000000000000000 <"$2" >"$exp" && + tr '\015\000abcdef0123456789' QN00000000000000000 <"$3" >"$act" && + test_cmp "$exp" "$act" && + rm "$exp" "$act" } create_gitattributes () { - attr=$1 - case "$attr" in - auto) - echo "*.txt text=auto" >.gitattributes - ;; - text) - echo "*.txt text" >.gitattributes - ;; - -text) - echo "*.txt -text" >.gitattributes - ;; - crlf) - echo "*.txt eol=crlf" >.gitattributes - ;; - lf) - echo "*.txt eol=lf" >.gitattributes - ;; - "") - echo >.gitattributes - ;; - *) - echo >&2 invalid attribute: $attr - exit 1 - ;; - esac + { + while test "$#" != 0 + do + case "$1" in + auto) echo '*.txt text=auto' ;; + ident) echo '*.txt ident' ;; + text) echo '*.txt text' ;; + -text) echo '*.txt -text' ;; + crlf) echo '*.txt eol=crlf' ;; + lf) echo '*.txt eol=lf' ;; + "") ;; + *) + echo >&2 invalid attribute: "$1" + exit 1 + ;; + esac && + shift + done + } >.gitattributes } create_NNO_files () { for crlf in false true input do - for attr in "" auto text -text lf crlf + for attr in "" auto text -text do - pfx=NNO_${crlf}_attr_${attr} && - cp CRLF_mix_LF ${pfx}_LF.txt && - cp CRLF_mix_LF ${pfx}_CRLF.txt && - cp CRLF_mix_LF ${pfx}_CRLF_mix_LF.txt && - cp CRLF_mix_LF ${pfx}_LF_mix_CR.txt && - cp CRLF_mix_LF ${pfx}_CRLF_nul.txt + for aeol in "" lf crlf + do + pfx=NNO_attr_${attr}_aeol_${aeol}_${crlf} + cp CRLF_mix_LF ${pfx}_LF.txt && + cp CRLF_mix_LF ${pfx}_CRLF.txt && + cp CRLF_mix_LF ${pfx}_CRLF_mix_LF.txt && + cp CRLF_mix_LF ${pfx}_LF_mix_CR.txt && + cp CRLF_mix_LF ${pfx}_CRLF_nul.txt + done done done } @@ -106,20 +103,22 @@ commit_check_warn () { } commit_chk_wrnNNO () { - crlf=$1 - attr=$2 - lfwarn=$3 - crlfwarn=$4 - lfmixcrlf=$5 - lfmixcr=$6 - crlfnul=$7 - pfx=NNO_${crlf}_attr_${attr} + attr=$1 ; shift + aeol=$1 ; shift + crlf=$1 ; shift + lfwarn=$1 ; shift + crlfwarn=$1 ; shift + lfmixcrlf=$1 ; shift + lfmixcr=$1 ; shift + crlfnul=$1 ; shift + pfx=NNO_attr_${attr}_aeol_${aeol}_${crlf} #Commit files on top of existing file - create_gitattributes "$attr" && + create_gitattributes "$attr" $aeol && for f in LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul do fname=${pfx}_$f.txt && cp $f $fname && + printf Z >>"$fname" && git -c core.autocrlf=$crlf add $fname 2>/dev/null && git -c core.autocrlf=$crlf commit -m "commit_$fname" $fname >"${pfx}_$f.err" 2>&1 done @@ -127,19 +126,19 @@ commit_chk_wrnNNO () { test_expect_success "commit NNO files crlf=$crlf attr=$attr LF" ' check_warning "$lfwarn" ${pfx}_LF.err ' - test_expect_success "commit NNO files crlf=$crlf attr=$attr CRLF" ' + test_expect_success "commit NNO files attr=$attr aeol=$aeol crlf=$crlf CRLF" ' check_warning "$crlfwarn" ${pfx}_CRLF.err ' - test_expect_success "commit NNO files crlf=$crlf attr=$attr CRLF_mix_LF" ' + test_expect_success "commit NNO files attr=$attr aeol=$aeol crlf=$crlf CRLF_mix_LF" ' check_warning "$lfmixcrlf" ${pfx}_CRLF_mix_LF.err ' - test_expect_success "commit NNO files crlf=$crlf attr=$attr LF_mix_cr" ' + test_expect_success "commit NNO files attr=$attr aeol=$aeol crlf=$crlf LF_mix_cr" ' check_warning "$lfmixcr" ${pfx}_LF_mix_CR.err ' - test_expect_success "commit NNO files crlf=$crlf attr=$attr CRLF_nul" ' + test_expect_success "commit NNO files attr=$attr aeol=$aeol crlf=$crlf CRLF_nul" ' check_warning "$crlfnul" ${pfx}_CRLF_nul.err ' } @@ -165,6 +164,26 @@ stats_ascii () { } + +# contruct the attr/ returned by git ls-files --eol +# Take none (=empty), one or two args +# convert.c: eol=XX overrides text=auto +attr_ascii () { + case $1,$2 in + -text,*) echo "-text" ;; + text,) echo "text" ;; + text,lf) echo "text eol=lf" ;; + text,crlf) echo "text eol=crlf" ;; + auto,) echo "text=auto" ;; + auto,lf) echo "text eol=lf" ;; + auto,crlf) echo "text eol=crlf" ;; + lf,) echo "text eol=lf" ;; + crlf,) echo "text eol=crlf" ;; + ,) echo "" ;; + *) echo invalid_attr "$1,$2" ;; + esac +} + check_files_in_repo () { crlf=$1 attr=$2 @@ -182,81 +201,84 @@ check_files_in_repo () { } check_in_repo_NNO () { - crlf=$1 - attr=$2 - lfname=$3 - crlfname=$4 - lfmixcrlf=$5 - lfmixcr=$6 - crlfnul=$7 - pfx=NNO_${crlf}_attr_${attr}_ - test_expect_success "compare_files $lfname ${pfx}LF.txt" ' - compare_files $lfname ${pfx}LF.txt + attr=$1 ; shift + aeol=$1 ; shift + crlf=$1 ; shift + lfname=$1 ; shift + crlfname=$1 ; shift + lfmixcrlf=$1 ; shift + lfmixcr=$1 ; shift + crlfnul=$1 ; shift + pfx=NNO_attr_${attr}_aeol_${aeol}_${crlf} + test_expect_success "compare_files $lfname ${pfx}_LF.txt" ' + compare_files $lfname ${pfx}_LF.txt ' - test_expect_success "compare_files $crlfname ${pfx}CRLF.txt" ' - compare_files $crlfname ${pfx}CRLF.txt + test_expect_success "compare_files $crlfname ${pfx}_CRLF.txt" ' + compare_files $crlfname ${pfx}_CRLF.txt ' - test_expect_success "compare_files $lfmixcrlf ${pfx}CRLF_mix_LF.txt" ' - compare_files $lfmixcrlf ${pfx}CRLF_mix_LF.txt + test_expect_success "compare_files $lfmixcrlf ${pfx}_CRLF_mix_LF.txt" ' + compare_files $lfmixcrlf ${pfx}_CRLF_mix_LF.txt ' - test_expect_success "compare_files $lfmixcr ${pfx}LF_mix_CR.txt" ' - compare_files $lfmixcr ${pfx}LF_mix_CR.txt + test_expect_success "compare_files $lfmixcr ${pfx}_LF_mix_CR.txt" ' + compare_files $lfmixcr ${pfx}_LF_mix_CR.txt ' - test_expect_success "compare_files $crlfnul ${pfx}CRLF_nul.txt" ' - compare_files $crlfnul ${pfx}CRLF_nul.txt + test_expect_success "compare_files $crlfnul ${pfx}_CRLF_nul.txt" ' + compare_files $crlfnul ${pfx}_CRLF_nul.txt ' } checkout_files () { - eol=$1 - crlf=$2 - attr=$3 - lfname=$4 - crlfname=$5 - lfmixcrlf=$6 - lfmixcr=$7 - crlfnul=$8 - create_gitattributes $attr && + attr=$1 ; shift + ident=$1; shift + aeol=$1 ; shift + crlf=$1 ; shift + ceol=$1 ; shift + lfname=$1 ; shift + crlfname=$1 ; shift + lfmixcrlf=$1 ; shift + lfmixcr=$1 ; shift + crlfnul=$1 ; shift + create_gitattributes "$attr" $ident $aeol && git config core.autocrlf $crlf && - pfx=eol_${eol}_crlf_${crlf}_attr_${attr}_ && + pfx=eol_${ceol}_crlf_${crlf}_attr_${attr}_ && for f in LF CRLF LF_mix_CR CRLF_mix_LF LF_nul do rm crlf_false_attr__$f.txt && - if test -z "$eol"; then + if test -z "$ceol"; then git checkout crlf_false_attr__$f.txt else - git -c core.eol=$eol checkout crlf_false_attr__$f.txt + git -c core.eol=$ceol checkout crlf_false_attr__$f.txt fi done - test_expect_success "ls-files --eol $lfname ${pfx}LF.txt" ' + test_expect_success "ls-files --eol attr=$attr $ident aeol=$aeol core.autocrlf=$crlf core.eol=$ceol" ' test_when_finished "rm expect actual" && sort <<-EOF >expect && - i/crlf w/$(stats_ascii $crlfname) crlf_false_attr__CRLF.txt - i/mixed w/$(stats_ascii $lfmixcrlf) crlf_false_attr__CRLF_mix_LF.txt - i/lf w/$(stats_ascii $lfname) crlf_false_attr__LF.txt - i/-text w/$(stats_ascii $lfmixcr) crlf_false_attr__LF_mix_CR.txt - i/-text w/$(stats_ascii $crlfnul) crlf_false_attr__CRLF_nul.txt - i/-text w/$(stats_ascii $crlfnul) crlf_false_attr__LF_nul.txt + i/crlf w/$(stats_ascii $crlfname) attr/$(attr_ascii $attr $aeol) crlf_false_attr__CRLF.txt + i/mixed w/$(stats_ascii $lfmixcrlf) attr/$(attr_ascii $attr $aeol) crlf_false_attr__CRLF_mix_LF.txt + i/lf w/$(stats_ascii $lfname) attr/$(attr_ascii $attr $aeol) crlf_false_attr__LF.txt + i/-text w/$(stats_ascii $lfmixcr) attr/$(attr_ascii $attr $aeol) crlf_false_attr__LF_mix_CR.txt + i/-text w/$(stats_ascii $crlfnul) attr/$(attr_ascii $attr $aeol) crlf_false_attr__CRLF_nul.txt + i/-text w/$(stats_ascii $crlfnul) attr/$(attr_ascii $attr $aeol) crlf_false_attr__LF_nul.txt EOF git ls-files --eol crlf_false_attr__* | - sed -e "s!attr/[^ ]*!!g" -e "s/ / /g" -e "s/ */ /g" | + sed -e "s/ / /g" -e "s/ */ /g" | sort >actual && test_cmp expect actual ' - test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$attr file=LF" " + test_expect_success "checkout attr=$attr $ident aeol=$aeol core.autocrlf=$crlf core.eol=$ceol file=LF" " compare_ws_file $pfx $lfname crlf_false_attr__LF.txt " - test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$attr file=CRLF" " + test_expect_success "checkout attr=$attr $ident aeol=$aeol core.autocrlf=$crlf core.eol=$ceol file=CRLF" " compare_ws_file $pfx $crlfname crlf_false_attr__CRLF.txt " - test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$attr file=CRLF_mix_LF" " + test_expect_success "checkout attr=$attr $ident aeol=$aeol core.autocrlf=$crlf core.eol=$ceol file=CRLF_mix_LF" " compare_ws_file $pfx $lfmixcrlf crlf_false_attr__CRLF_mix_LF.txt " - test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$attr file=LF_mix_CR" " + test_expect_success "checkout attr=$attr $ident aeol=$aeol core.autocrlf=$crlf core.eol=$ceol file=LF_mix_CR" " compare_ws_file $pfx $lfmixcr crlf_false_attr__LF_mix_CR.txt " - test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$attr file=LF_nul" " + test_expect_success "checkout attr=$attr $ident aeol=$aeol core.autocrlf=$crlf core.eol=$ceol file=LF_nul" " compare_ws_file $pfx $crlfnul crlf_false_attr__LF_nul.txt " } @@ -301,14 +323,13 @@ test_expect_success 'setup master' ' git checkout -b master && git add .gitattributes && git commit -m "add .gitattributes" "" && - printf "line1\nline2\nline3" >LF && - printf "line1\r\nline2\r\nline3" >CRLF && - printf "line1\r\nline2\nline3" >repoMIX && - printf "line1\r\nline2\nline3" >CRLF_mix_LF && - printf "line1\nline2\rline3" >LF_mix_CR && - printf "line1\r\nline2\rline3" >CRLF_mix_CR && - printf "line1Q\r\nline2\r\nline3" | q_to_nul >CRLF_nul && - printf "line1Q\nline2\nline3" | q_to_nul >LF_nul && + printf "\$Id: 0000000000000000000000000000000000000000 \$\nLINEONE\nLINETWO\nLINETHREE" >LF && + printf "\$Id: 0000000000000000000000000000000000000000 \$\r\nLINEONE\r\nLINETWO\r\nLINETHREE" >CRLF && + printf "\$Id: 0000000000000000000000000000000000000000 \$\nLINEONE\r\nLINETWO\nLINETHREE" >CRLF_mix_LF && + printf "\$Id: 0000000000000000000000000000000000000000 \$\nLINEONE\nLINETWO\rLINETHREE" >LF_mix_CR && + printf "\$Id: 0000000000000000000000000000000000000000 \$\r\nLINEONE\r\nLINETWO\rLINETHREE" >CRLF_mix_CR && + printf "\$Id: 0000000000000000000000000000000000000000 \$\r\nLINEONEQ\r\nLINETWO\r\nLINETHREE" | q_to_nul >CRLF_nul && + printf "\$Id: 0000000000000000000000000000000000000000 \$\nLINEONEQ\nLINETWO\nLINETHREE" | q_to_nul >LF_nul && create_NNO_files CRLF_mix_LF CRLF_mix_LF CRLF_mix_LF CRLF_mix_LF CRLF_mix_LF && git -c core.autocrlf=false add NNO_*.txt && git commit -m "mixed line endings" && @@ -371,31 +392,31 @@ test_expect_success 'commit files attr=crlf' ' commit_check_warn input "crlf" "LF_CRLF" "" "LF_CRLF" "LF_CRLF" "" ' -# attr LF CRLF CRLFmixLF LF_mix_CR CRLFNUL -commit_chk_wrnNNO false "" "" "" "" "" "" -commit_chk_wrnNNO true "" "LF_CRLF" "" "" "" "" -commit_chk_wrnNNO input "" "" "" "" "" "" - - -commit_chk_wrnNNO false "auto" "$WILC" "$WICL" "$WAMIX" "" "" -commit_chk_wrnNNO true "auto" "LF_CRLF" "" "LF_CRLF" "" "" -commit_chk_wrnNNO input "auto" "" "CRLF_LF" "CRLF_LF" "" "" - -commit_chk_wrnNNO false "text" "$WILC" "$WICL" "$WAMIX" "$WILC" "$WICL" -commit_chk_wrnNNO true "text" "LF_CRLF" "" "LF_CRLF" "LF_CRLF" "" -commit_chk_wrnNNO input "text" "" "CRLF_LF" "CRLF_LF" "" "CRLF_LF" - -commit_chk_wrnNNO false "-text" "" "" "" "" "" -commit_chk_wrnNNO true "-text" "" "" "" "" "" -commit_chk_wrnNNO input "-text" "" "" "" "" "" - -commit_chk_wrnNNO false "lf" "" "CRLF_LF" "CRLF_LF" "" "CRLF_LF" -commit_chk_wrnNNO true "lf" "" "CRLF_LF" "CRLF_LF" "" "CRLF_LF" -commit_chk_wrnNNO input "lf" "" "CRLF_LF" "CRLF_LF" "" "CRLF_LF" - -commit_chk_wrnNNO false "crlf" "LF_CRLF" "" "LF_CRLF" "LF_CRLF" "" -commit_chk_wrnNNO true "crlf" "LF_CRLF" "" "LF_CRLF" "LF_CRLF" "" -commit_chk_wrnNNO input "crlf" "LF_CRLF" "" "LF_CRLF" "LF_CRLF" "" +# attr LF CRLF CRLFmixLF LF_mix_CR CRLFNUL +commit_chk_wrnNNO "" "" false "" "" "" "" "" +commit_chk_wrnNNO "" "" true LF_CRLF "" "" "" "" +commit_chk_wrnNNO "" "" input "" "" "" "" "" + +commit_chk_wrnNNO "auto" "" false "$WILC" "$WICL" "$WAMIX" "" "" +commit_chk_wrnNNO "auto" "" true LF_CRLF "" LF_CRLF "" "" +commit_chk_wrnNNO "auto" "" input "" CRLF_LF CRLF_LF "" "" + +for crlf in true false input +do + commit_chk_wrnNNO -text "" $crlf "" "" "" "" "" + commit_chk_wrnNNO -text lf $crlf "" "" "" "" "" + commit_chk_wrnNNO -text crlf $crlf "" "" "" "" "" + commit_chk_wrnNNO "" lf $crlf "" CRLF_LF CRLF_LF "" CRLF_LF + commit_chk_wrnNNO "" crlf $crlf LF_CRLF "" LF_CRLF LF_CRLF "" + commit_chk_wrnNNO auto lf $crlf "" CRLF_LF CRLF_LF "" CRLF_LF + commit_chk_wrnNNO auto crlf $crlf LF_CRLF "" LF_CRLF LF_CRLF "" + commit_chk_wrnNNO text lf $crlf "" CRLF_LF CRLF_LF "" CRLF_LF + commit_chk_wrnNNO text crlf $crlf LF_CRLF "" LF_CRLF LF_CRLF "" +done + +commit_chk_wrnNNO "text" "" false "$WILC" "$WICL" "$WAMIX" "$WILC" "$WICL" +commit_chk_wrnNNO "text" "" true LF_CRLF "" LF_CRLF LF_CRLF "" +commit_chk_wrnNNO "text" "" input "" CRLF_LF CRLF_LF "" CRLF_LF test_expect_success 'create files cleanup' ' rm -f *.txt && @@ -426,46 +447,37 @@ test_expect_success 'commit -text' ' check_files_in_repo input "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul ' -# attr LF CRLF CRLF_mix_LF LF_mix_CR CRLFNUL -check_in_repo_NNO false "" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul -check_in_repo_NNO true "" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul -check_in_repo_NNO input "" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul - -check_in_repo_NNO false "auto" LF LF LF LF_mix_CR CRLF_nul -check_in_repo_NNO true "auto" LF LF LF LF_mix_CR CRLF_nul -check_in_repo_NNO input "auto" LF LF LF LF_mix_CR CRLF_nul - -check_in_repo_NNO false "text" LF LF LF LF_mix_CR LF_nul -check_in_repo_NNO true "text" LF LF LF LF_mix_CR LF_nul -check_in_repo_NNO input "text" LF LF LF LF_mix_CR LF_nul - -check_in_repo_NNO false "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul -check_in_repo_NNO true "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul -check_in_repo_NNO input "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul - - +for crlf in true false input +do + # attr aeol LF CRLF CRLF_mix_LF LF_mix_CR CRLFNUL + check_in_repo_NNO "" "" $crlf LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul + check_in_repo_NNO -text "" $crlf LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul + check_in_repo_NNO -text lf $crlf LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul + check_in_repo_NNO -text crlf $crlf LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul + check_in_repo_NNO auto "" $crlf LF LF LF LF_mix_CR CRLF_nul + check_in_repo_NNO auto lf $crlf LF LF LF LF_mix_CR LF_nul + check_in_repo_NNO auto crlf $crlf LF LF LF LF_mix_CR LF_nul + check_in_repo_NNO text "" $crlf LF LF LF LF_mix_CR LF_nul + check_in_repo_NNO text lf $crlf LF LF LF LF_mix_CR LF_nul + check_in_repo_NNO text crlf $crlf LF LF LF LF_mix_CR LF_nul +done ################################################################################ # Check how files in the repo are changed when they are checked out # How to read the table below: # - checkout_files will check multiple files with a combination of settings # and attributes (core.autocrlf=input is forbidden with core.eol=crlf) -# - parameter $1 : core.eol lf | crlf -# - parameter $2 : core.autocrlf false | true | input -# - parameter $3 : text in .gitattributs "" (empty) | auto | text | -text -# - parameter $4 : reference for a file with only LF in the repo -# - parameter $5 : reference for a file with only CRLF in the repo -# - parameter $6 : reference for a file with mixed LF and CRLF in the repo -# - parameter $7 : reference for a file with LF and CR in the repo (does somebody uses this ?) -# - parameter $8 : reference for a file with CRLF and a NUL (should be handled as binary when auto) - -# What we have in the repo: -# ----------------- EOL in repo ---------------- -# LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul -# settings with checkout: -# core. core. .gitattr -# eol acrlf -# ---------------------------------------------- -# What we want to have in the working tree: +# +# - parameter $1 : text in .gitattributs "" (empty) | auto | text | -text +# - parameter $2 : ident "" | i (i == ident) +# - parameter $3 : eol in .gitattributs "" (empty) | lf | crlf +# - parameter $4 : core.autocrlf false | true | input +# - parameter $5 : core.eol "" | lf | crlf | "native" +# - parameter $6 : reference for a file with only LF in the repo +# - parameter $7 : reference for a file with only CRLF in the repo +# - parameter $8 : reference for a file with mixed LF and CRLF in the repo +# - parameter $9 : reference for a file with LF and CR in the repo +# - parameter $10 : reference for a file with CRLF and a NUL (should be handled as binary when auto) + if test_have_prereq NATIVE_CRLF then MIX_CRLF_LF=CRLF @@ -480,69 +492,48 @@ LFNUL=LF_nul fi export CRLF_MIX_LF_CR MIX NL -checkout_files lf false "" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files lf true "" CRLF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files lf input "" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files lf false "auto" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files lf true "auto" CRLF CRLF CRLF LF_mix_CR LF_nul -checkout_files lf input "auto" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files lf false "text" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files lf true "text" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul -checkout_files lf input "text" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files lf false "-text" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files lf true "-text" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files lf input "-text" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files lf false "lf" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files lf true "lf" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files lf input "lf" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files lf false "crlf" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul -checkout_files lf true "crlf" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul -checkout_files lf input "crlf" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul - -checkout_files crlf false "" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files crlf true "" CRLF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files crlf false "auto" CRLF CRLF CRLF LF_mix_CR LF_nul -checkout_files crlf true "auto" CRLF CRLF CRLF LF_mix_CR LF_nul -checkout_files crlf false "text" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul -checkout_files crlf true "text" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul -checkout_files crlf false "-text" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files crlf true "-text" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files crlf false "lf" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files crlf true "lf" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files crlf false "crlf" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul -checkout_files crlf true "crlf" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul - -checkout_files "" false "" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files "" true "" CRLF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files "" input "" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files "" false "auto" $NL CRLF $MIX_CRLF_LF LF_mix_CR LF_nul -checkout_files "" true "auto" CRLF CRLF CRLF LF_mix_CR LF_nul -checkout_files "" input "auto" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files "" false "text" $NL CRLF $MIX_CRLF_LF $MIX_LF_CR $LFNUL -checkout_files "" true "text" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul -checkout_files "" input "text" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files "" false "-text" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files "" true "-text" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files "" input "-text" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files "" false "lf" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files "" true "lf" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files "" input "lf" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files "" false "crlf" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul -checkout_files "" true "crlf" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul -checkout_files "" input "crlf" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul - -checkout_files native false "" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files native true "" CRLF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files native false "auto" $NL CRLF $MIX_CRLF_LF LF_mix_CR LF_nul -checkout_files native true "auto" CRLF CRLF CRLF LF_mix_CR LF_nul -checkout_files native false "text" $NL CRLF $MIX_CRLF_LF $MIX_LF_CR $LFNUL -checkout_files native true "text" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul -checkout_files native false "-text" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files native true "-text" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files native false "lf" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files native true "lf" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul -checkout_files native false "crlf" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul -checkout_files native true "crlf" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul +# Same handling with and without ident +for id in "" ident +do + for ceol in lf crlf native + do + for crlf in true false input + do + # -text overrides core.autocrlf and core.eol + # text and eol=crlf or eol=lf override core.autocrlf and core.eol + checkout_files -text "$id" "" "$crlf" "$ceol" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul + checkout_files -text "$id" "lf" "$crlf" "$ceol" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul + checkout_files -text "$id" "crlf" "$crlf" "$ceol" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul + # text + checkout_files text "$id" "lf" "$crlf" "$ceol" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul + checkout_files text "$id" "crlf" "$crlf" "$ceol" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul + # currently the same as text, eol=XXX + checkout_files auto "$id" "lf" "$crlf" "$ceol" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul + checkout_files auto "$id" "crlf" "$crlf" "$ceol" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul + done + + # core.autocrlf false, different core.eol + checkout_files "" "$id" "" false "$ceol" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul + # core.autocrlf true + checkout_files "" "$id" "" true "$ceol" CRLF CRLF CRLF_mix_LF LF_mix_CR LF_nul + # text: core.autocrlf = true overrides core.eol + checkout_files auto "$id" "" true "$ceol" CRLF CRLF CRLF LF_mix_CR LF_nul + checkout_files text "$id" "" true "$ceol" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul + # text: core.autocrlf = input overrides core.eol + checkout_files text "$id" "" input "$ceol" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul + checkout_files auto "$id" "" input "$ceol" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul + # text=auto + eol=XXX + done + # text: core.autocrlf=false uses core.eol + checkout_files text "$id" "" false crlf CRLF CRLF CRLF CRLF_mix_CR CRLF_nul + checkout_files text "$id" "" false lf LF CRLF CRLF_mix_LF LF_mix_CR LF_nul + # text: core.autocrlf=false and core.eol unset(or native) uses native eol + checkout_files text "$id" "" false "" $NL CRLF $MIX_CRLF_LF $MIX_LF_CR $LFNUL + checkout_files text "$id" "" false native $NL CRLF $MIX_CRLF_LF $MIX_LF_CR $LFNUL + # auto: core.autocrlf=false and core.eol unset(or native) uses native eol + checkout_files auto "$id" "" false "" $NL CRLF $MIX_CRLF_LF LF_mix_CR LF_nul + checkout_files auto "$id" "" false native $NL CRLF $MIX_CRLF_LF LF_mix_CR LF_nul +done # Should be the last test case: remove some files from the worktree test_expect_success 'ls-files --eol -d -z' ' diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh index 9be6411104..db5f60d0c5 100755 --- a/t/t0040-parse-options.sh +++ b/t/t0040-parse-options.sh @@ -7,7 +7,7 @@ test_description='our own option parser' . ./test-lib.sh -cat > expect << EOF +cat >expect <<\EOF usage: test-parse-options <options> --yes get a boolean @@ -45,63 +45,24 @@ Standard options -v, --verbose be verbose -n, --dry-run dry run -q, --quiet be quiet + --expect <string> expected output in the variable dump EOF test_expect_success 'test help' ' - test_must_fail test-parse-options -h > output 2> output.err && + test_must_fail test-parse-options -h >output 2>output.err && test_must_be_empty output.err && test_i18ncmp expect output ' mv expect expect.err -cat >expect.template <<EOF -boolean: 0 -integer: 0 -magnitude: 0 -timestamp: 0 -string: (not set) -abbrev: 7 -verbose: 0 -quiet: no -dry run: no -file: (not set) -EOF - -check() { +check () { what="$1" && shift && expect="$1" && shift && - sed "s/^$what .*/$what $expect/" <expect.template >expect && - test-parse-options $* >output 2>output.err && - test_must_be_empty output.err && - test_cmp expect output -} - -check_i18n() { - what="$1" && - shift && - expect="$1" && - shift && - sed "s/^$what .*/$what $expect/" <expect.template >expect && - test-parse-options $* >output 2>output.err && - test_must_be_empty output.err && - test_i18ncmp expect output -} - -check_unknown() { - case "$1" in - --*) - echo error: unknown option \`${1#--}\' >expect ;; - -*) - echo error: unknown switch \`${1#-}\' >expect ;; - esac && - cat expect.err >>expect && - test_must_fail test-parse-options $* >output 2>output.err && - test_must_be_empty output && - test_cmp expect output.err + test-parse-options --expect="$what $expect" "$@" } check_unknown_i18n() { @@ -156,7 +117,7 @@ test_expect_success 'OPT_MAGNITUDE() 3giga' ' check magnitude: 3221225472 -m 3g ' -cat > expect << EOF +cat >expect <<\EOF boolean: 2 integer: 1729 magnitude: 16384 @@ -164,7 +125,7 @@ timestamp: 0 string: 123 abbrev: 7 verbose: 2 -quiet: no +quiet: 0 dry run: yes file: prefix/my.file EOF @@ -176,7 +137,7 @@ test_expect_success 'short options' ' test_must_be_empty output.err ' -cat > expect << EOF +cat >expect <<\EOF boolean: 2 integer: 1729 magnitude: 16384 @@ -184,7 +145,7 @@ timestamp: 0 string: 321 abbrev: 10 verbose: 2 -quiet: no +quiet: 0 dry run: no file: prefix/fi.le EOF @@ -204,15 +165,15 @@ test_expect_success 'missing required value' ' test_expect_code 129 test-parse-options --file ' -cat > expect << EOF +cat >expect <<\EOF boolean: 1 integer: 13 magnitude: 0 timestamp: 0 string: 123 abbrev: 7 -verbose: 0 -quiet: no +verbose: -1 +quiet: 0 dry run: no file: (not set) arg 00: a1 @@ -222,32 +183,32 @@ EOF test_expect_success 'intermingled arguments' ' test-parse-options a1 --string 123 b1 --boolean -j 13 -- --boolean \ - > output 2> output.err && + >output 2>output.err && test_must_be_empty output.err && test_cmp expect output ' -cat > expect << EOF +cat >expect <<\EOF boolean: 0 integer: 2 magnitude: 0 timestamp: 0 string: (not set) abbrev: 7 -verbose: 0 -quiet: no +verbose: -1 +quiet: 0 dry run: no file: (not set) EOF test_expect_success 'unambiguously abbreviated option' ' - test-parse-options --int 2 --boolean --no-bo > output 2> output.err && + test-parse-options --int 2 --boolean --no-bo >output 2>output.err && test_must_be_empty output.err && test_cmp expect output ' test_expect_success 'unambiguously abbreviated option with "="' ' - test-parse-options --int=2 > output 2> output.err && + test-parse-options --int=2 >output 2>output.err && test_must_be_empty output.err && test_cmp expect output ' @@ -256,74 +217,74 @@ test_expect_success 'ambiguously abbreviated option' ' test_expect_code 129 test-parse-options --strin 123 ' -cat > expect << EOF +cat >expect <<\EOF boolean: 0 integer: 0 magnitude: 0 timestamp: 0 string: 123 abbrev: 7 -verbose: 0 -quiet: no +verbose: -1 +quiet: 0 dry run: no file: (not set) EOF test_expect_success 'non ambiguous option (after two options it abbreviates)' ' - test-parse-options --st 123 > output 2> output.err && + test-parse-options --st 123 >output 2>output.err && test_must_be_empty output.err && test_cmp expect output ' -cat > typo.err << EOF -error: did you mean \`--boolean\` (with two dashes ?) +cat >typo.err <<\EOF +error: did you mean `--boolean` (with two dashes ?) EOF test_expect_success 'detect possible typos' ' - test_must_fail test-parse-options -boolean > output 2> output.err && + test_must_fail test-parse-options -boolean >output 2>output.err && test_must_be_empty output && test_cmp typo.err output.err ' -cat > typo.err << EOF -error: did you mean \`--ambiguous\` (with two dashes ?) +cat >typo.err <<\EOF +error: did you mean `--ambiguous` (with two dashes ?) EOF test_expect_success 'detect possible typos' ' - test_must_fail test-parse-options -ambiguous > output 2> output.err && + test_must_fail test-parse-options -ambiguous >output 2>output.err && test_must_be_empty output && test_cmp typo.err output.err ' -cat > expect <<EOF +cat >expect <<\EOF boolean: 0 integer: 0 magnitude: 0 timestamp: 0 string: (not set) abbrev: 7 -verbose: 0 -quiet: no +verbose: -1 +quiet: 0 dry run: no file: (not set) arg 00: --quux EOF test_expect_success 'keep some options as arguments' ' - test-parse-options --quux > output 2> output.err && + test-parse-options --quux >output 2>output.err && test_must_be_empty output.err && - test_cmp expect output + test_cmp expect output ' -cat > expect <<EOF +cat >expect <<\EOF boolean: 0 integer: 0 magnitude: 0 timestamp: 1 string: (not set) abbrev: 7 -verbose: 0 -quiet: yes +verbose: -1 +quiet: 1 dry run: no file: (not set) arg 00: foo @@ -331,12 +292,12 @@ EOF test_expect_success 'OPT_DATE() works' ' test-parse-options -t "1970-01-01 00:00:01 +0000" \ - foo -q > output 2> output.err && + foo -q >output 2>output.err && test_must_be_empty output.err && test_cmp expect output ' -cat > expect <<EOF +cat >expect <<\EOF Callback: "four", 0 boolean: 5 integer: 4 @@ -344,112 +305,110 @@ magnitude: 0 timestamp: 0 string: (not set) abbrev: 7 -verbose: 0 -quiet: no +verbose: -1 +quiet: 0 dry run: no file: (not set) EOF test_expect_success 'OPT_CALLBACK() and OPT_BIT() work' ' - test-parse-options --length=four -b -4 > output 2> output.err && + test-parse-options --length=four -b -4 >output 2>output.err && test_must_be_empty output.err && test_cmp expect output ' -cat > expect <<EOF -Callback: "not set", 1 -EOF +>expect test_expect_success 'OPT_CALLBACK() and callback errors work' ' - test_must_fail test-parse-options --no-length > output 2> output.err && + test_must_fail test-parse-options --no-length >output 2>output.err && test_i18ncmp expect output && test_i18ncmp expect.err output.err ' -cat > expect <<EOF +cat >expect <<\EOF boolean: 1 integer: 23 magnitude: 0 timestamp: 0 string: (not set) abbrev: 7 -verbose: 0 -quiet: no +verbose: -1 +quiet: 0 dry run: no file: (not set) EOF test_expect_success 'OPT_BIT() and OPT_SET_INT() work' ' - test-parse-options --set23 -bbbbb --no-or4 > output 2> output.err && + test-parse-options --set23 -bbbbb --no-or4 >output 2>output.err && test_must_be_empty output.err && test_cmp expect output ' test_expect_success 'OPT_NEGBIT() and OPT_SET_INT() work' ' - test-parse-options --set23 -bbbbb --neg-or4 > output 2> output.err && + test-parse-options --set23 -bbbbb --neg-or4 >output 2>output.err && test_must_be_empty output.err && test_cmp expect output ' -cat > expect <<EOF +cat >expect <<\EOF boolean: 6 integer: 0 magnitude: 0 timestamp: 0 string: (not set) abbrev: 7 -verbose: 0 -quiet: no +verbose: -1 +quiet: 0 dry run: no file: (not set) EOF test_expect_success 'OPT_BIT() works' ' - test-parse-options -bb --or4 > output 2> output.err && + test-parse-options -bb --or4 >output 2>output.err && test_must_be_empty output.err && test_cmp expect output ' test_expect_success 'OPT_NEGBIT() works' ' - test-parse-options -bb --no-neg-or4 > output 2> output.err && + test-parse-options -bb --no-neg-or4 >output 2>output.err && test_must_be_empty output.err && test_cmp expect output ' test_expect_success 'OPT_COUNTUP() with PARSE_OPT_NODASH works' ' - test-parse-options + + + + + + > output 2> output.err && + test-parse-options + + + + + + >output 2>output.err && test_must_be_empty output.err && test_cmp expect output ' -cat > expect <<EOF +cat >expect <<\EOF boolean: 0 integer: 12345 magnitude: 0 timestamp: 0 string: (not set) abbrev: 7 -verbose: 0 -quiet: no +verbose: -1 +quiet: 0 dry run: no file: (not set) EOF test_expect_success 'OPT_NUMBER_CALLBACK() works' ' - test-parse-options -12345 > output 2> output.err && + test-parse-options -12345 >output 2>output.err && test_must_be_empty output.err && test_cmp expect output ' -cat >expect <<EOF +cat >expect <<\EOF boolean: 0 integer: 0 magnitude: 0 timestamp: 0 string: (not set) abbrev: 7 -verbose: 0 -quiet: no +verbose: -1 +quiet: 0 dry run: no file: (not set) EOF @@ -460,7 +419,7 @@ test_expect_success 'negation of OPT_NONEG flags is not ambiguous' ' test_cmp expect output ' -cat >>expect <<'EOF' +cat >>expect <<\EOF list: foo list: bar list: baz @@ -476,4 +435,118 @@ test_expect_success '--no-list resets list' ' test_cmp expect output ' +cat >expect <<\EOF +boolean: 0 +integer: 0 +magnitude: 0 +timestamp: 0 +string: (not set) +abbrev: 7 +verbose: -1 +quiet: 3 +dry run: no +file: (not set) +EOF + +test_expect_success 'multiple quiet levels' ' + test-parse-options -q -q -q >output 2>output.err && + test_must_be_empty output.err && + test_cmp expect output +' + +cat >expect <<\EOF +boolean: 0 +integer: 0 +magnitude: 0 +timestamp: 0 +string: (not set) +abbrev: 7 +verbose: 3 +quiet: 0 +dry run: no +file: (not set) +EOF + +test_expect_success 'multiple verbose levels' ' + test-parse-options -v -v -v >output 2>output.err && + test_must_be_empty output.err && + test_cmp expect output +' + +cat >expect <<\EOF +boolean: 0 +integer: 0 +magnitude: 0 +timestamp: 0 +string: (not set) +abbrev: 7 +verbose: -1 +quiet: 0 +dry run: no +file: (not set) +EOF + +test_expect_success '--no-quiet sets --quiet to 0' ' + test-parse-options --no-quiet >output 2>output.err && + test_must_be_empty output.err && + test_cmp expect output +' + +cat >expect <<\EOF +boolean: 0 +integer: 0 +magnitude: 0 +timestamp: 0 +string: (not set) +abbrev: 7 +verbose: -1 +quiet: 0 +dry run: no +file: (not set) +EOF + +test_expect_success '--no-quiet resets multiple -q to 0' ' + test-parse-options -q -q -q --no-quiet >output 2>output.err && + test_must_be_empty output.err && + test_cmp expect output +' + +cat >expect <<\EOF +boolean: 0 +integer: 0 +magnitude: 0 +timestamp: 0 +string: (not set) +abbrev: 7 +verbose: 0 +quiet: 0 +dry run: no +file: (not set) +EOF + +test_expect_success '--no-verbose sets verbose to 0' ' + test-parse-options --no-verbose >output 2>output.err && + test_must_be_empty output.err && + test_cmp expect output +' + +cat >expect <<\EOF +boolean: 0 +integer: 0 +magnitude: 0 +timestamp: 0 +string: (not set) +abbrev: 7 +verbose: 0 +quiet: 0 +dry run: no +file: (not set) +EOF + +test_expect_success '--no-verbose resets multiple verbose to 0' ' + test-parse-options -v -v -v --no-verbose >output 2>output.err && + test_must_be_empty output.err && + test_cmp expect output +' + test_done diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh index 8532a028e7..bf2deee109 100755 --- a/t/t0060-path-utils.sh +++ b/t/t0060-path-utils.sh @@ -19,6 +19,13 @@ relative_path() { "test \"\$(test-path-utils relative_path '$1' '$2')\" = '$expected'" } +test_submodule_relative_url() { + test_expect_success "test_submodule_relative_url: $1 $2 $3 => $4" " + actual=\$(git submodule--helper resolve-relative-url-test '$1' '$2' '$3') && + test \"\$actual\" = '$4' + " +} + test_git_path() { test_expect_success "git-path $1 $2 => $3" " $1 git rev-parse --git-path $2 >actual && @@ -298,4 +305,43 @@ test_git_path GIT_COMMON_DIR=bar config bar/config test_git_path GIT_COMMON_DIR=bar packed-refs bar/packed-refs test_git_path GIT_COMMON_DIR=bar shallow bar/shallow +# In the tests below, the distinction between $PWD and $(pwd) is important: +# on Windows, $PWD is POSIX style (/c/foo), $(pwd) has drive letter (c:/foo). + +test_submodule_relative_url "../" "../foo" "../submodule" "../../submodule" +test_submodule_relative_url "../" "../foo/bar" "../submodule" "../../foo/submodule" +test_submodule_relative_url "../" "../foo/submodule" "../submodule" "../../foo/submodule" +test_submodule_relative_url "../" "./foo" "../submodule" "../submodule" +test_submodule_relative_url "../" "./foo/bar" "../submodule" "../foo/submodule" +test_submodule_relative_url "../../../" "../foo/bar" "../sub/a/b/c" "../../../../foo/sub/a/b/c" +test_submodule_relative_url "../" "$PWD/addtest" "../repo" "$(pwd)/repo" +test_submodule_relative_url "../" "foo/bar" "../submodule" "../foo/submodule" +test_submodule_relative_url "../" "foo" "../submodule" "../submodule" + +test_submodule_relative_url "(null)" "../foo/bar" "../sub/a/b/c" "../foo/sub/a/b/c" +test_submodule_relative_url "(null)" "../foo/bar" "../submodule" "../foo/submodule" +test_submodule_relative_url "(null)" "../foo/submodule" "../submodule" "../foo/submodule" +test_submodule_relative_url "(null)" "../foo" "../submodule" "../submodule" +test_submodule_relative_url "(null)" "./foo/bar" "../submodule" "foo/submodule" +test_submodule_relative_url "(null)" "./foo" "../submodule" "submodule" +test_submodule_relative_url "(null)" "//somewhere else/repo" "../subrepo" "//somewhere else/subrepo" +test_submodule_relative_url "(null)" "$PWD/subsuper_update_r" "../subsubsuper_update_r" "$(pwd)/subsubsuper_update_r" +test_submodule_relative_url "(null)" "$PWD/super_update_r2" "../subsuper_update_r" "$(pwd)/subsuper_update_r" +test_submodule_relative_url "(null)" "$PWD/." "../." "$(pwd)/." +test_submodule_relative_url "(null)" "$PWD" "./." "$(pwd)/." +test_submodule_relative_url "(null)" "$PWD/addtest" "../repo" "$(pwd)/repo" +test_submodule_relative_url "(null)" "$PWD" "./Ã¥ äö" "$(pwd)/Ã¥ äö" +test_submodule_relative_url "(null)" "$PWD/." "../submodule" "$(pwd)/submodule" +test_submodule_relative_url "(null)" "$PWD/submodule" "../submodule" "$(pwd)/submodule" +test_submodule_relative_url "(null)" "$PWD/home2/../remote" "../bundle1" "$(pwd)/home2/../bundle1" +test_submodule_relative_url "(null)" "$PWD/submodule_update_repo" "./." "$(pwd)/submodule_update_repo/." +test_submodule_relative_url "(null)" "file:///tmp/repo" "../subrepo" "file:///tmp/subrepo" +test_submodule_relative_url "(null)" "foo/bar" "../submodule" "foo/submodule" +test_submodule_relative_url "(null)" "foo" "../submodule" "submodule" +test_submodule_relative_url "(null)" "helper:://hostname/repo" "../subrepo" "helper:://hostname/subrepo" +test_submodule_relative_url "(null)" "ssh://hostname/repo" "../subrepo" "ssh://hostname/subrepo" +test_submodule_relative_url "(null)" "ssh://hostname:22/repo" "../subrepo" "ssh://hostname:22/subrepo" +test_submodule_relative_url "(null)" "user@host:path/to/repo" "../subrepo" "user@host:path/to/subrepo" +test_submodule_relative_url "(null)" "user@host:repo" "../subrepo" "user@host:subrepo" + test_done diff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh index d7ef44b4a2..03bd31e9f2 100755 --- a/t/t0300-credentials.sh +++ b/t/t0300-credentials.sh @@ -298,4 +298,15 @@ test_expect_success 'helpers can abort the process' ' test_cmp expect stdout ' +test_expect_success 'empty helper spec resets helper list' ' + test_config credential.helper "verbatim file file" && + check fill "" "verbatim cmdline cmdline" <<-\EOF + -- + username=cmdline + password=cmdline + -- + verbatim: get + EOF +' + test_done diff --git a/t/t1020-subdirectory.sh b/t/t1020-subdirectory.sh index 8e22b03cdd..df3183ea1a 100755 --- a/t/t1020-subdirectory.sh +++ b/t/t1020-subdirectory.sh @@ -141,13 +141,13 @@ test_expect_success 'GIT_PREFIX for !alias' ' test_expect_success 'GIT_PREFIX for built-ins' ' # Use GIT_EXTERNAL_DIFF to test that the "diff" built-in # receives the GIT_PREFIX variable. - printf "dir/" >expect && - printf "#!/bin/sh\n" >diff && - printf "printf \"\$GIT_PREFIX\"" >>diff && - chmod +x diff && + echo "dir/" >expect && + write_script diff <<-\EOF && + printf "%s\n" "$GIT_PREFIX" + EOF ( cd dir && - printf "change" >two && + echo "change" >two && GIT_EXTERNAL_DIFF=./diff git diff >../actual git checkout -- two ) && diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh index 52678e7d0a..d934a24417 100755 --- a/t/t1300-repo-config.sh +++ b/t/t1300-repo-config.sh @@ -699,11 +699,13 @@ test_expect_success 'invalid unit' ' echo 1auto >expect && git config aninvalid.unit >actual && test_cmp expect actual && - cat >expect <<-\EOF && - fatal: bad numeric config value '\''1auto'\'' for '\''aninvalid.unit'\'' in .git/config: invalid unit - EOF test_must_fail git config --int --get aninvalid.unit 2>actual && - test_i18ncmp expect actual + test_i18ngrep "bad numeric config value .1auto. for .aninvalid.unit. in file .git/config: invalid unit" actual +' + +test_expect_success 'invalid stdin config' ' + echo "[broken" | test_must_fail git config --list --file - >output 2>&1 && + test_i18ngrep "bad config line 1 in standard input" output ' cat > expect << EOF @@ -957,13 +959,15 @@ Qsection.sub=section.val4 Qsection.sub=section.val5Q EOF test_expect_success '--null --list' ' - git config --null --list | nul_to_q >result && + git config --null --list >result.raw && + nul_to_q <result.raw >result && echo >>result && test_cmp expect result ' test_expect_success '--null --get-regexp' ' - git config --null --get-regexp "val[0-9]" | nul_to_q >result && + git config --null --get-regexp "val[0-9]" >result.raw && + nul_to_q <result.raw >result && echo >>result && test_cmp expect result ' @@ -1083,6 +1087,20 @@ test_expect_success 'git -c complains about empty key and value' ' test_must_fail git -c "" rev-parse ' +test_expect_success 'multiple git -c appends config' ' + test_config alias.x "!git -c x.two=2 config --get-regexp ^x\.*" && + cat >expect <<-\EOF && + x.one 1 + x.two 2 + EOF + git -c x.one=1 x >actual && + test_cmp expect actual +' + +test_expect_success 'git -c is not confused by empty environment' ' + GIT_CONFIG_PARAMETERS="" git -c x.one=1 config --list +' + test_expect_success 'git config --edit works' ' git config -f tmp test.value no && echo test.value=yes >expect && @@ -1140,6 +1158,9 @@ test_expect_success 'urlmatch' ' 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://good.example.com >actual && test_cmp expect actual && @@ -1201,4 +1222,154 @@ test_expect_success POSIXPERM,PERL 'preserves existing permissions' ' "die q(badrename) if ((stat(q(.git/config)))[2] & 07777) != 0600" ' +! test_have_prereq MINGW || +HOME="$(pwd)" # convert to Windows path + +test_expect_success 'set up --show-origin tests' ' + INCLUDE_DIR="$HOME/include" && + mkdir -p "$INCLUDE_DIR" && + cat >"$INCLUDE_DIR"/absolute.include <<-\EOF && + [user] + absolute = include + EOF + cat >"$INCLUDE_DIR"/relative.include <<-\EOF && + [user] + relative = include + EOF + cat >"$HOME"/.gitconfig <<-EOF && + [user] + global = true + override = global + [include] + path = "$INCLUDE_DIR/absolute.include" + EOF + cat >.git/config <<-\EOF + [user] + local = true + override = local + [include] + path = ../include/relative.include + EOF +' + +test_expect_success '--show-origin with --list' ' + cat >expect <<-EOF && + file:$HOME/.gitconfig user.global=true + file:$HOME/.gitconfig user.override=global + file:$HOME/.gitconfig include.path=$INCLUDE_DIR/absolute.include + file:$INCLUDE_DIR/absolute.include user.absolute=include + file:.git/config user.local=true + file:.git/config user.override=local + file:.git/config include.path=../include/relative.include + file:.git/../include/relative.include user.relative=include + command line: user.cmdline=true + EOF + git -c user.cmdline=true config --list --show-origin >output && + test_cmp expect output +' + +test_expect_success '--show-origin with --list --null' ' + cat >expect <<-EOF && + file:$HOME/.gitconfigQuser.global + trueQfile:$HOME/.gitconfigQuser.override + globalQfile:$HOME/.gitconfigQinclude.path + $INCLUDE_DIR/absolute.includeQfile:$INCLUDE_DIR/absolute.includeQuser.absolute + includeQfile:.git/configQuser.local + trueQfile:.git/configQuser.override + localQfile:.git/configQinclude.path + ../include/relative.includeQfile:.git/../include/relative.includeQuser.relative + includeQcommand line:Quser.cmdline + trueQ + EOF + git -c user.cmdline=true config --null --list --show-origin >output.raw && + nul_to_q <output.raw >output && + # The here-doc above adds a newline that the --null output would not + # include. Add it here to make the two comparable. + echo >>output && + test_cmp expect output +' + +test_expect_success '--show-origin with single file' ' + cat >expect <<-\EOF && + file:.git/config user.local=true + file:.git/config user.override=local + file:.git/config include.path=../include/relative.include + EOF + git config --local --list --show-origin >output && + test_cmp expect output +' + +test_expect_success '--show-origin with --get-regexp' ' + cat >expect <<-EOF && + file:$HOME/.gitconfig user.global true + file:.git/config user.local true + EOF + git config --show-origin --get-regexp "user\.[g|l].*" >output && + test_cmp expect output +' + +test_expect_success '--show-origin getting a single key' ' + cat >expect <<-\EOF && + file:.git/config local + EOF + git config --show-origin user.override >output && + test_cmp expect output +' + +test_expect_success 'set up custom config file' ' + CUSTOM_CONFIG_FILE="file\" (dq) and spaces.conf" && + cat >"$CUSTOM_CONFIG_FILE" <<-\EOF + [user] + custom = true + EOF +' + +test_expect_success !MINGW '--show-origin escape special file name characters' ' + cat >expect <<-\EOF && + file:"file\" (dq) and spaces.conf" user.custom=true + EOF + git config --file "$CUSTOM_CONFIG_FILE" --show-origin --list >output && + test_cmp expect output +' + +test_expect_success '--show-origin stdin' ' + cat >expect <<-\EOF && + standard input: user.custom=true + EOF + git config --file - --show-origin --list <"$CUSTOM_CONFIG_FILE" >output && + test_cmp expect output +' + +test_expect_success '--show-origin stdin with file include' ' + cat >"$INCLUDE_DIR"/stdin.include <<-EOF && + [user] + stdin = include + EOF + cat >expect <<-EOF && + file:$INCLUDE_DIR/stdin.include include + EOF + echo "[include]path=\"$INCLUDE_DIR\"/stdin.include" \ + | git config --show-origin --includes --file - user.stdin >output && + test_cmp expect output +' + +test_expect_success !MINGW '--show-origin blob' ' + cat >expect <<-\EOF && + blob:a9d9f9e555b5c6f07cbe09d3f06fe3df11e09c08 user.custom=true + EOF + blob=$(git hash-object -w "$CUSTOM_CONFIG_FILE") && + git config --blob=$blob --show-origin --list >output && + test_cmp expect output +' + +test_expect_success !MINGW '--show-origin blob ref' ' + cat >expect <<-\EOF && + blob:"master:file\" (dq) and spaces.conf" user.custom=true + EOF + git add "$CUSTOM_CONFIG_FILE" && + git commit -m "new config file" && + git config --blob=master:"$CUSTOM_CONFIG_FILE" --show-origin --list >output && + test_cmp expect output +' + test_done diff --git a/t/t1308-config-set.sh b/t/t1308-config-set.sh index 91235b76ba..005d66dbef 100755 --- a/t/t1308-config-set.sh +++ b/t/t1308-config-set.sh @@ -195,14 +195,14 @@ test_expect_success 'proper error on error in default config files' ' cp .git/config .git/config.old && test_when_finished "mv .git/config.old .git/config" && echo "[" >>.git/config && - echo "fatal: bad config file line 34 in .git/config" >expect && + echo "fatal: bad config line 34 in file .git/config" >expect && test_expect_code 128 test-config get_value foo.bar 2>actual && test_cmp expect actual ' test_expect_success 'proper error on error in custom config files' ' echo "[" >>syntax-error && - echo "fatal: bad config file line 1 in syntax-error" >expect && + echo "fatal: bad config line 1 in file syntax-error" >expect && test_expect_code 128 test-config configset_get_value foo.bar syntax-error 2>actual && test_cmp expect actual ' @@ -218,4 +218,15 @@ test_expect_success 'check line errors for malformed values' ' test_i18ngrep "fatal: .*alias\.br.*\.git/config.*line 2" result ' +test_expect_success 'error on modifying repo config without repo' ' + mkdir no-repo && + ( + GIT_CEILING_DIRECTORIES=$(pwd) && + export GIT_CEILING_DIRECTORIES && + cd no-repo && + test_must_fail git config a.b c 2>err && + grep "not in a git directory" err + ) +' + test_done diff --git a/t/t1350-config-hooks-path.sh b/t/t1350-config-hooks-path.sh new file mode 100755 index 0000000000..5e3fb3a6af --- /dev/null +++ b/t/t1350-config-hooks-path.sh @@ -0,0 +1,37 @@ +#!/bin/sh + +test_description='Test the core.hooksPath configuration variable' + +. ./test-lib.sh + +test_expect_success 'set up a pre-commit hook in core.hooksPath' ' + mkdir -p .git/custom-hooks .git/hooks && + write_script .git/custom-hooks/pre-commit <<-\EOF && + echo CUSTOM >>actual + EOF + write_script .git/hooks/pre-commit <<-\EOF + echo NORMAL >>actual + EOF +' + +test_expect_success 'Check that various forms of specifying core.hooksPath work' ' + test_commit no_custom_hook && + git config core.hooksPath .git/custom-hooks && + test_commit have_custom_hook && + git config core.hooksPath .git/custom-hooks/ && + test_commit have_custom_hook_trailing_slash && + git config core.hooksPath "$PWD/.git/custom-hooks" && + test_commit have_custom_hook_abs_path && + git config core.hooksPath "$PWD/.git/custom-hooks/" && + test_commit have_custom_hook_abs_path_trailing_slash && + cat >expect <<-\EOF && + NORMAL + CUSTOM + CUSTOM + CUSTOM + CUSTOM + EOF + test_cmp expect actual +' + +test_done diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh index c623824b4d..9cf91dc6d2 100755 --- a/t/t1410-reflog.sh +++ b/t/t1410-reflog.sh @@ -338,4 +338,14 @@ test_expect_failure 'reflog with non-commit entries displays all entries' ' test_line_count = 3 actual ' +test_expect_success 'reflog expire operates on symref not referrent' ' + git branch -l the_symref && + git branch -l referrent && + git update-ref referrent HEAD && + git symbolic-ref refs/heads/the_symref refs/heads/referrent && + test_when_finished "rm -f .git/refs/heads/referrent.lock" && + touch .git/refs/heads/referrent.lock && + git reflog expire --expire=all the_symref +' + test_done diff --git a/t/t1430-bad-ref-name.sh b/t/t1430-bad-ref-name.sh index c465abe8e3..25ddab4e98 100755 --- a/t/t1430-bad-ref-name.sh +++ b/t/t1430-bad-ref-name.sh @@ -42,7 +42,7 @@ test_expect_success 'git branch shows badly named ref as warning' ' cp .git/refs/heads/master .git/refs/heads/broken...ref && test_when_finished "rm -f .git/refs/heads/broken...ref" && git branch >output 2>error && - grep -e "broken\.\.\.ref" error && + test_i18ngrep -e "ignoring ref with broken name refs/heads/broken\.\.\.ref" error && ! grep -e "broken\.\.\.ref" output ' @@ -147,35 +147,145 @@ test_expect_success 'rev-parse skips symref pointing to broken name' ' test_when_finished "rm -f .git/refs/heads/broken...ref" && git branch shadow one && cp .git/refs/heads/master .git/refs/heads/broken...ref && - git symbolic-ref refs/tags/shadow refs/heads/broken...ref && - + printf "ref: refs/heads/broken...ref\n" >.git/refs/tags/shadow && + test_when_finished "rm -f .git/refs/tags/shadow" && git rev-parse --verify one >expect && git rev-parse --verify shadow >actual 2>err && test_cmp expect actual && - test_i18ngrep "ignoring.*refs/tags/shadow" err + test_i18ngrep "ignoring dangling symref refs/tags/shadow" err ' -test_expect_success 'update-ref --no-deref -d can delete reference to broken name' ' - git symbolic-ref refs/heads/badname refs/heads/broken...ref && +test_expect_success 'for-each-ref emits warnings for broken names' ' + cp .git/refs/heads/master .git/refs/heads/broken...ref && + test_when_finished "rm -f .git/refs/heads/broken...ref" && + printf "ref: refs/heads/broken...ref\n" >.git/refs/heads/badname && test_when_finished "rm -f .git/refs/heads/badname" && - test_path_is_file .git/refs/heads/badname && - git update-ref --no-deref -d refs/heads/badname && - test_path_is_missing .git/refs/heads/badname + printf "ref: refs/heads/master\n" >.git/refs/heads/broken...symref && + test_when_finished "rm -f .git/refs/heads/broken...symref" && + git for-each-ref >output 2>error && + ! grep -e "broken\.\.\.ref" output && + ! grep -e "badname" output && + ! grep -e "broken\.\.\.symref" output && + test_i18ngrep "ignoring ref with broken name refs/heads/broken\.\.\.ref" error && + test_i18ngrep "ignoring broken ref refs/heads/badname" error && + test_i18ngrep "ignoring ref with broken name refs/heads/broken\.\.\.symref" error ' test_expect_success 'update-ref -d can delete broken name' ' cp .git/refs/heads/master .git/refs/heads/broken...ref && test_when_finished "rm -f .git/refs/heads/broken...ref" && - git update-ref -d refs/heads/broken...ref && + git update-ref -d refs/heads/broken...ref >output 2>error && + test_must_be_empty output && + test_must_be_empty error && + git branch >output 2>error && + ! grep -e "broken\.\.\.ref" error && + ! grep -e "broken\.\.\.ref" output +' + +test_expect_success 'branch -d can delete broken name' ' + cp .git/refs/heads/master .git/refs/heads/broken...ref && + test_when_finished "rm -f .git/refs/heads/broken...ref" && + git branch -d broken...ref >output 2>error && + test_i18ngrep "Deleted branch broken...ref (was broken)" output && + test_must_be_empty error && git branch >output 2>error && ! grep -e "broken\.\.\.ref" error && ! grep -e "broken\.\.\.ref" output ' +test_expect_success 'update-ref --no-deref -d can delete symref to broken name' ' + cp .git/refs/heads/master .git/refs/heads/broken...ref && + test_when_finished "rm -f .git/refs/heads/broken...ref" && + printf "ref: refs/heads/broken...ref\n" >.git/refs/heads/badname && + test_when_finished "rm -f .git/refs/heads/badname" && + git update-ref --no-deref -d refs/heads/badname >output 2>error && + test_path_is_missing .git/refs/heads/badname && + test_must_be_empty output && + test_must_be_empty error +' + +test_expect_success 'branch -d can delete symref to broken name' ' + cp .git/refs/heads/master .git/refs/heads/broken...ref && + test_when_finished "rm -f .git/refs/heads/broken...ref" && + printf "ref: refs/heads/broken...ref\n" >.git/refs/heads/badname && + test_when_finished "rm -f .git/refs/heads/badname" && + git branch -d badname >output 2>error && + test_path_is_missing .git/refs/heads/badname && + test_i18ngrep "Deleted branch badname (was refs/heads/broken\.\.\.ref)" output && + test_must_be_empty error +' + +test_expect_success 'update-ref --no-deref -d can delete dangling symref to broken name' ' + printf "ref: refs/heads/broken...ref\n" >.git/refs/heads/badname && + test_when_finished "rm -f .git/refs/heads/badname" && + git update-ref --no-deref -d refs/heads/badname >output 2>error && + test_path_is_missing .git/refs/heads/badname && + test_must_be_empty output && + test_must_be_empty error +' + +test_expect_success 'branch -d can delete dangling symref to broken name' ' + printf "ref: refs/heads/broken...ref\n" >.git/refs/heads/badname && + test_when_finished "rm -f .git/refs/heads/badname" && + git branch -d badname >output 2>error && + test_path_is_missing .git/refs/heads/badname && + test_i18ngrep "Deleted branch badname (was refs/heads/broken\.\.\.ref)" output && + test_must_be_empty error +' + +test_expect_success 'update-ref -d can delete broken name through symref' ' + cp .git/refs/heads/master .git/refs/heads/broken...ref && + test_when_finished "rm -f .git/refs/heads/broken...ref" && + printf "ref: refs/heads/broken...ref\n" >.git/refs/heads/badname && + test_when_finished "rm -f .git/refs/heads/badname" && + git update-ref -d refs/heads/badname >output 2>error && + test_path_is_missing .git/refs/heads/broken...ref && + test_must_be_empty output && + test_must_be_empty error +' + +test_expect_success 'update-ref --no-deref -d can delete symref with broken name' ' + printf "ref: refs/heads/master\n" >.git/refs/heads/broken...symref && + test_when_finished "rm -f .git/refs/heads/broken...symref" && + git update-ref --no-deref -d refs/heads/broken...symref >output 2>error && + test_path_is_missing .git/refs/heads/broken...symref && + test_must_be_empty output && + test_must_be_empty error +' + +test_expect_success 'branch -d can delete symref with broken name' ' + printf "ref: refs/heads/master\n" >.git/refs/heads/broken...symref && + test_when_finished "rm -f .git/refs/heads/broken...symref" && + git branch -d broken...symref >output 2>error && + test_path_is_missing .git/refs/heads/broken...symref && + test_i18ngrep "Deleted branch broken...symref (was refs/heads/master)" output && + test_must_be_empty error +' + +test_expect_success 'update-ref --no-deref -d can delete dangling symref with broken name' ' + printf "ref: refs/heads/idonotexist\n" >.git/refs/heads/broken...symref && + test_when_finished "rm -f .git/refs/heads/broken...symref" && + git update-ref --no-deref -d refs/heads/broken...symref >output 2>error && + test_path_is_missing .git/refs/heads/broken...symref && + test_must_be_empty output && + test_must_be_empty error +' + +test_expect_success 'branch -d can delete dangling symref with broken name' ' + printf "ref: refs/heads/idonotexist\n" >.git/refs/heads/broken...symref && + test_when_finished "rm -f .git/refs/heads/broken...symref" && + git branch -d broken...symref >output 2>error && + test_path_is_missing .git/refs/heads/broken...symref && + test_i18ngrep "Deleted branch broken...symref (was refs/heads/idonotexist)" output && + test_must_be_empty error +' + test_expect_success 'update-ref -d cannot delete non-ref in .git dir' ' echo precious >.git/my-private-file && echo precious >expect && - test_must_fail git update-ref -d my-private-file && + test_must_fail git update-ref -d my-private-file >output 2>error && + test_must_be_empty output && + test_i18ngrep -e "cannot lock .*: unable to resolve reference" error && test_cmp expect .git/my-private-file ' diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh index e66b7cb697..7ee8ea004f 100755 --- a/t/t1450-fsck.sh +++ b/t/t1450-fsck.sh @@ -427,6 +427,24 @@ test_expect_success 'fsck allows .Ňit' ' ) ' +test_expect_success 'NUL in commit' ' + rm -fr nul-in-commit && + git init nul-in-commit && + ( + cd nul-in-commit && + git commit --allow-empty -m "initial commitQNUL after message" && + git cat-file commit HEAD >original && + q_to_nul <original >munged && + git hash-object -w -t commit --stdin <munged >name && + git branch bad $(cat name) && + + test_must_fail git -c fsck.nulInCommit=error fsck 2>warn.1 && + grep nulInCommit warn.1 && + git fsck 2>warn.2 && + grep nulInCommit warn.2 + ) +' + # create a static test repo which is broken by omitting # one particular object ($1, which is looked up via rev-parse # in the new repository). diff --git a/t/t1501-worktree.sh b/t/t1501-work-tree.sh index cc5b870e58..cc5b870e58 100755 --- a/t/t1501-worktree.sh +++ b/t/t1501-work-tree.sh diff --git a/t/t1506-rev-parse-diagnosis.sh b/t/t1506-rev-parse-diagnosis.sh index 613d9bfe1b..86c2ff255d 100755 --- a/t/t1506-rev-parse-diagnosis.sh +++ b/t/t1506-rev-parse-diagnosis.sh @@ -166,11 +166,6 @@ test_expect_success 'relative path when cwd is outside worktree' ' grep "relative path syntax can.t be used outside working tree." error ' -test_expect_success 'relative path when startup_info is NULL' ' - test_must_fail test-match-trees HEAD:./file.txt HEAD:./file.txt 2>error && - grep "BUG: startup_info struct is not initialized." error -' - test_expect_success '<commit>:file correctly diagnosed after a pathname' ' test_must_fail git rev-parse file.txt HEAD:file.txt 1>actual 2>error && test_i18ngrep ! "exists on disk" error && diff --git a/t/t1509-root-worktree.sh b/t/t1509-root-work-tree.sh index 553a3f601b..553a3f601b 100755 --- a/t/t1509-root-worktree.sh +++ b/t/t1509-root-work-tree.sh diff --git a/t/t1515-rev-parse-outside-repo.sh b/t/t1515-rev-parse-outside-repo.sh new file mode 100755 index 0000000000..3ec2971ee5 --- /dev/null +++ b/t/t1515-rev-parse-outside-repo.sh @@ -0,0 +1,45 @@ +#!/bin/sh + +test_description='check that certain rev-parse options work outside repo' +. ./test-lib.sh + +test_expect_success 'set up non-repo directory' ' + GIT_CEILING_DIRECTORIES=$(pwd) && + export GIT_CEILING_DIRECTORIES && + mkdir non-repo && + cd non-repo && + # confirm that git does not find a repo + test_must_fail git rev-parse --git-dir +' + +# Rather than directly test the output of sq-quote directly, +# make sure the shell can read back a tricky case, since +# that's what we really care about anyway. +tricky="really tricky with \\ and \" and '" +dump_args () { + for i in "$@"; do + echo "arg: $i" + done +} +test_expect_success 'rev-parse --sq-quote' ' + dump_args "$tricky" easy >expect && + eval "dump_args $(git rev-parse --sq-quote "$tricky" easy)" >actual && + test_cmp expect actual +' + +test_expect_success 'rev-parse --local-env-vars' ' + git rev-parse --local-env-vars >actual && + # we do not want to depend on the complete list here, + # so just look for something plausible + grep ^GIT_DIR actual +' + +test_expect_success 'rev-parse --resolve-git-dir' ' + git init --separate-git-dir repo dir && + test_must_fail git rev-parse --resolve-git-dir . && + echo "$(pwd)/repo" >expect && + git rev-parse --resolve-git-dir dir/.git >actual && + test_cmp expect actual +' + +test_done diff --git a/t/t2019-checkout-ambiguous-ref.sh b/t/t2019-checkout-ambiguous-ref.sh index 199b22d85e..b99d5192a9 100755 --- a/t/t2019-checkout-ambiguous-ref.sh +++ b/t/t2019-checkout-ambiguous-ref.sh @@ -56,30 +56,4 @@ test_expect_success VAGUENESS_SUCCESS 'checkout reports switch to branch' ' test_i18ngrep ! "^HEAD is now at" stderr ' -test_expect_success 'wildcard ambiguation, paths win' ' - git init ambi && - ( - cd ambi && - echo a >a.c && - git add a.c && - echo b >a.c && - git checkout "*.c" && - echo a >expect && - test_cmp expect a.c - ) -' - -test_expect_success !MINGW 'wildcard ambiguation, refs lose' ' - git init ambi2 && - ( - cd ambi2 && - echo a >"*.c" && - git add . && - test_must_fail git show :"*.c" && - git show :"*.c" -- >actual && - echo a >expect && - test_cmp expect actual - ) -' - test_done diff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh index 0a804dab63..3a22fc55fc 100755 --- a/t/t2025-worktree-add.sh +++ b/t/t2025-worktree-add.sh @@ -4,6 +4,8 @@ test_description='test git worktree add' . ./test-lib.sh +. "$TEST_DIRECTORY"/lib-rebase.sh + test_expect_success 'setup' ' test_commit init ' @@ -193,9 +195,93 @@ test_expect_success '"add" -B/--detach mutually exclusive' ' test_must_fail git worktree add -B poodle --detach bamboo master ' +test_expect_success '"add -B" fails if the branch is checked out' ' + git rev-parse newmaster >before && + test_must_fail git worktree add -B newmaster bamboo master && + git rev-parse newmaster >after && + test_cmp before after +' + +test_expect_success 'add -B' ' + git worktree add -B poodle bamboo2 master^ && + git -C bamboo2 symbolic-ref HEAD >actual && + echo refs/heads/poodle >expected && + test_cmp expected actual && + test_cmp_rev master^ poodle +' + test_expect_success 'local clone from linked checkout' ' git clone --local here here-clone && ( cd here-clone && git fsck ) ' +test_expect_success '"add" worktree with --no-checkout' ' + git worktree add --no-checkout -b swamp swamp && + ! test -e swamp/init.t && + git -C swamp reset --hard && + test_cmp init.t swamp/init.t +' + +test_expect_success '"add" worktree with --checkout' ' + git worktree add --checkout -b swmap2 swamp2 && + test_cmp init.t swamp2/init.t +' + +test_expect_success 'put a worktree under rebase' ' + git worktree add under-rebase && + ( + cd under-rebase && + set_fake_editor && + FAKE_LINES="edit 1" git rebase -i HEAD^ && + git worktree list | grep "under-rebase.*detached HEAD" + ) +' + +test_expect_success 'add a worktree, checking out a rebased branch' ' + test_must_fail git worktree add new-rebase under-rebase && + ! test -d new-rebase +' + +test_expect_success 'checking out a rebased branch from another worktree' ' + git worktree add new-place && + test_must_fail git -C new-place checkout under-rebase +' + +test_expect_success 'not allow to delete a branch under rebase' ' + ( + cd under-rebase && + test_must_fail git branch -D under-rebase + ) +' + +test_expect_success 'rename a branch under rebase not allowed' ' + test_must_fail git branch -M under-rebase rebase-with-new-name +' + +test_expect_success 'check out from current worktree branch ok' ' + ( + cd under-rebase && + git checkout under-rebase && + git checkout - && + git rebase --abort + ) +' + +test_expect_success 'checkout a branch under bisect' ' + git worktree add under-bisect && + ( + cd under-bisect && + git bisect start && + git bisect bad && + git bisect good HEAD~2 && + git worktree list | grep "under-bisect.*detached HEAD" && + test_must_fail git worktree add new-bisect under-bisect && + ! test -d new-bisect + ) +' + +test_expect_success 'rename a branch under bisect not allowed' ' + test_must_fail git branch -M under-bisect bisect-with-new-name +' + test_done diff --git a/t/t2027-worktree-list.sh b/t/t2027-worktree-list.sh index 75ebb1b4a0..1b1b65a6b0 100755 --- a/t/t2027-worktree-list.sh +++ b/t/t2027-worktree-list.sh @@ -8,6 +8,16 @@ test_expect_success 'setup' ' test_commit init ' +test_expect_success 'rev-parse --git-common-dir on main worktree' ' + git rev-parse --git-common-dir >actual && + echo .git >expected && + test_cmp expected actual && + mkdir sub && + git -C sub rev-parse --git-common-dir >actual2 && + echo sub/.git >expected2 && + test_cmp expected2 actual2 +' + 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/t3032-merge-recursive-options.sh b/t/t3032-merge-recursive-space-options.sh index 4029c9c8c0..b56180ee4a 100755 --- a/t/t3032-merge-recursive-options.sh +++ b/t/t3032-merge-recursive-space-options.sh @@ -1,6 +1,6 @@ #!/bin/sh -test_description='merge-recursive options +test_description='merge-recursive space options * [master] Clarify ! [remote] Remove cruft diff --git a/t/t3033-merge-toplevel.sh b/t/t3033-merge-toplevel.sh index 46aadc410b..d314599428 100755 --- a/t/t3033-merge-toplevel.sh +++ b/t/t3033-merge-toplevel.sh @@ -19,6 +19,8 @@ test_expect_success setup ' test_commit three && git checkout right && test_commit four && + git checkout --orphan newroot && + test_commit five && git checkout master ' @@ -133,4 +135,18 @@ test_expect_success 'merge FETCH_HEAD octopus non-fast-forward' ' test_cmp expect actual ' +# two-project merge +test_expect_success 'refuse two-project merge by default' ' + t3033_reset && + git reset --hard four && + test_must_fail git merge five +' + +test_expect_success 'two-project merge with --allow-unrelated-histories' ' + t3033_reset && + git reset --hard four && + git merge --allow-unrelated-histories five && + git diff --exit-code five +' + test_done diff --git a/t/t3034-merge-recursive-rename-options.sh b/t/t3034-merge-recursive-rename-options.sh new file mode 100755 index 0000000000..b9c4028496 --- /dev/null +++ b/t/t3034-merge-recursive-rename-options.sh @@ -0,0 +1,312 @@ +#!/bin/sh + +test_description='merge-recursive rename options + +Test rename detection by examining rename/delete conflicts. + +* (HEAD -> rename) rename +| * (master) delete +|/ +* base + +git diff --name-status base master +D 0-old +D 1-old +D 2-old +D 3-old + +git diff --name-status -M01 base rename +R025 0-old 0-new +R050 1-old 1-new +R075 2-old 2-new +R100 3-old 3-new + +Actual similarity indices are parsed from diff output. We rely on the fact that +they are rounded down (see, e.g., Documentation/diff-generate-patch.txt, which +mentions this in a different context). +' + +. ./test-lib.sh + +get_expected_stages () { + git checkout rename -- $1-new && + git ls-files --stage $1-new >expected-stages-undetected-$1 && + sed "s/ 0 / 2 /" <expected-stages-undetected-$1 \ + >expected-stages-detected-$1 && + git read-tree -u --reset HEAD +} + +rename_detected () { + git ls-files --stage $1-old $1-new >stages-actual-$1 && + test_cmp expected-stages-detected-$1 stages-actual-$1 +} + +rename_undetected () { + git ls-files --stage $1-old $1-new >stages-actual-$1 && + test_cmp expected-stages-undetected-$1 stages-actual-$1 +} + +check_common () { + git ls-files --stage >stages-actual && + test_line_count = 4 stages-actual +} + +check_threshold_0 () { + check_common && + rename_detected 0 && + rename_detected 1 && + rename_detected 2 && + rename_detected 3 +} + +check_threshold_1 () { + check_common && + rename_undetected 0 && + rename_detected 1 && + rename_detected 2 && + rename_detected 3 +} + +check_threshold_2 () { + check_common && + rename_undetected 0 && + rename_undetected 1 && + rename_detected 2 && + rename_detected 3 +} + +check_exact_renames () { + check_common && + rename_undetected 0 && + rename_undetected 1 && + rename_undetected 2 && + rename_detected 3 +} + +check_no_renames () { + check_common && + rename_undetected 0 && + rename_undetected 1 && + rename_undetected 2 && + rename_undetected 3 +} + +test_expect_success 'setup repo' ' + cat <<-\EOF >3-old && + 33a + 33b + 33c + 33d + EOF + sed s/33/22/ <3-old >2-old && + sed s/33/11/ <3-old >1-old && + sed s/33/00/ <3-old >0-old && + git add [0-3]-old && + git commit -m base && + git rm [0-3]-old && + git commit -m delete && + git checkout -b rename HEAD^ && + cp 3-old 3-new && + sed 1,1s/./x/ <2-old >2-new && + sed 1,2s/./x/ <1-old >1-new && + sed 1,3s/./x/ <0-old >0-new && + git add [0-3]-new && + git rm [0-3]-old && + git commit -m rename && + get_expected_stages 0 && + get_expected_stages 1 && + get_expected_stages 2 && + get_expected_stages 3 && + check_50="false" && + tail="HEAD^ -- HEAD master" +' + +test_expect_success 'setup thresholds' ' + git diff --name-status -M01 HEAD^ HEAD >diff-output && + test_debug "cat diff-output" && + test_line_count = 4 diff-output && + grep "R[0-9][0-9][0-9] \([0-3]\)-old \1-new" diff-output \ + >grep-output && + test_cmp diff-output grep-output && + th0=$(sed -n "s/R\(...\) 0-old 0-new/\1/p" <diff-output) && + th1=$(sed -n "s/R\(...\) 1-old 1-new/\1/p" <diff-output) && + th2=$(sed -n "s/R\(...\) 2-old 2-new/\1/p" <diff-output) && + th3=$(sed -n "s/R\(...\) 3-old 3-new/\1/p" <diff-output) && + test "$th0" -lt "$th1" && + test "$th1" -lt "$th2" && + test "$th2" -lt "$th3" && + test "$th3" = 100 && + if test 50 -le "$th0" + then + check_50=check_threshold_0 + elif test 50 -le "$th1" + then + check_50=check_threshold_1 + elif test 50 -le "$th2" + then + check_50=check_threshold_2 + fi && + th0="$th0%" && + th1="$th1%" && + th2="$th2%" && + th3="$th3%" +' + +test_expect_success 'assumption for tests: rename detection with diff' ' + git diff --name-status -M$th0 --diff-filter=R HEAD^ HEAD \ + >diff-output-0 && + git diff --name-status -M$th1 --diff-filter=R HEAD^ HEAD \ + >diff-output-1 && + git diff --name-status -M$th2 --diff-filter=R HEAD^ HEAD \ + >diff-output-2 && + git diff --name-status -M100% --diff-filter=R HEAD^ HEAD \ + >diff-output-3 && + test_line_count = 4 diff-output-0 && + test_line_count = 3 diff-output-1 && + test_line_count = 2 diff-output-2 && + test_line_count = 1 diff-output-3 +' + +test_expect_success 'default similarity threshold is 50%' ' + git read-tree --reset -u HEAD && + test_must_fail git merge-recursive $tail && + $check_50 +' + +test_expect_success 'low rename threshold' ' + git read-tree --reset -u HEAD && + test_must_fail git merge-recursive --find-renames=$th0 $tail && + check_threshold_0 +' + +test_expect_success 'medium rename threshold' ' + git read-tree --reset -u HEAD && + test_must_fail git merge-recursive --find-renames=$th1 $tail && + check_threshold_1 +' + +test_expect_success 'high rename threshold' ' + git read-tree --reset -u HEAD && + test_must_fail git merge-recursive --find-renames=$th2 $tail && + check_threshold_2 +' + +test_expect_success 'exact renames only' ' + git read-tree --reset -u HEAD && + test_must_fail git merge-recursive --find-renames=100% $tail && + check_exact_renames +' + +test_expect_success 'rename threshold is truncated' ' + git read-tree --reset -u HEAD && + test_must_fail git merge-recursive --find-renames=200% $tail && + check_exact_renames +' + +test_expect_success 'disabled rename detection' ' + git read-tree --reset -u HEAD && + git merge-recursive --no-renames $tail && + check_no_renames +' + +test_expect_success 'last wins in --find-renames=<m> --find-renames=<n>' ' + git read-tree --reset -u HEAD && + test_must_fail git merge-recursive \ + --find-renames=$th0 --find-renames=$th2 $tail && + check_threshold_2 +' + +test_expect_success '--find-renames resets threshold' ' + git read-tree --reset -u HEAD && + test_must_fail git merge-recursive \ + --find-renames=$th0 --find-renames $tail && + $check_50 +' + +test_expect_success 'last wins in --no-renames --find-renames' ' + git read-tree --reset -u HEAD && + test_must_fail git merge-recursive --no-renames --find-renames $tail && + $check_50 +' + +test_expect_success 'last wins in --find-renames --no-renames' ' + git read-tree --reset -u HEAD && + git merge-recursive --find-renames --no-renames $tail && + check_no_renames +' + +test_expect_success 'assumption for further tests: trivial merge succeeds' ' + git read-tree --reset -u HEAD && + git merge-recursive HEAD -- HEAD HEAD && + git diff --quiet --cached && + git merge-recursive --find-renames=$th0 HEAD -- HEAD HEAD && + git diff --quiet --cached && + git merge-recursive --find-renames=$th2 HEAD -- HEAD HEAD && + git diff --quiet --cached && + git merge-recursive --find-renames=100% HEAD -- HEAD HEAD && + git diff --quiet --cached && + git merge-recursive --no-renames HEAD -- HEAD HEAD && + git diff --quiet --cached +' + +test_expect_success '--find-renames rejects negative argument' ' + git read-tree --reset -u HEAD && + test_must_fail git merge-recursive --find-renames=-25 \ + HEAD -- HEAD HEAD && + git diff --quiet --cached +' + +test_expect_success '--find-renames rejects non-numbers' ' + git read-tree --reset -u HEAD && + test_must_fail git merge-recursive --find-renames=0xf \ + HEAD -- HEAD HEAD && + git diff --quiet --cached +' + +test_expect_success 'rename-threshold=<n> is a synonym for find-renames=<n>' ' + git read-tree --reset -u HEAD && + test_must_fail git merge-recursive --rename-threshold=$th0 $tail && + check_threshold_0 +' + +test_expect_success 'last wins in --no-renames --rename-threshold=<n>' ' + git read-tree --reset -u HEAD && + test_must_fail git merge-recursive --no-renames --rename-threshold=$th0 $tail && + check_threshold_0 +' + +test_expect_success 'last wins in --rename-threshold=<n> --no-renames' ' + git read-tree --reset -u HEAD && + git merge-recursive --rename-threshold=$th0 --no-renames $tail && + check_no_renames +' + +test_expect_success '--rename-threshold=<n> rejects negative argument' ' + git read-tree --reset -u HEAD && + test_must_fail git merge-recursive --rename-threshold=-25 \ + HEAD -- HEAD HEAD && + git diff --quiet --cached +' + +test_expect_success '--rename-threshold=<n> rejects non-numbers' ' + git read-tree --reset -u HEAD && + test_must_fail git merge-recursive --rename-threshold=0xf \ + HEAD -- HEAD HEAD && + git diff --quiet --cached +' + +test_expect_success 'last wins in --rename-threshold=<m> --find-renames=<n>' ' + git read-tree --reset -u HEAD && + test_must_fail git merge-recursive \ + --rename-threshold=$th0 --find-renames=$th2 $tail && + check_threshold_2 +' + +test_expect_success 'last wins in --find-renames=<m> --rename-threshold=<n>' ' + git read-tree --reset -u HEAD && + test_must_fail git merge-recursive \ + --find-renames=$th2 --rename-threshold=$th0 $tail && + check_threshold_0 +' + +test_done diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index cdaf6f64ec..f3e3b6cf2e 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -126,7 +126,28 @@ test_expect_success 'git branch -M foo bar should fail when bar is checked out' test_expect_success 'git branch -M baz bam should succeed when baz is checked out' ' git checkout -b baz && git branch bam && - git branch -M baz bam + git branch -M baz bam && + test $(git rev-parse --abbrev-ref HEAD) = bam +' + +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 && + git worktree add -f bazdir2 baz && + git branch -M baz bam && + test $(git -C bazdir rev-parse --abbrev-ref HEAD) = bam && + test $(git -C bazdir2 rev-parse --abbrev-ref HEAD) = bam +' + +test_expect_success 'git branch -M baz bam should succeed within a worktree in which baz is checked out' ' + git checkout -b baz && + git worktree add -f bazdir3 baz && + ( + cd bazdir3 && + git branch -M baz bam && + test $(git rev-parse --abbrev-ref HEAD) = bam + ) && + test $(git rev-parse --abbrev-ref HEAD) = bam ' test_expect_success 'git branch -M master should work when master is checked out' ' @@ -403,6 +424,12 @@ test_expect_success 'test deleting branch without config' ' test_i18ncmp expect actual ' +test_expect_success 'deleting currently checked out branch fails' ' + git worktree add -b my7 my7 && + test_must_fail git -C my7 branch -d my7 && + test_must_fail git branch -d my7 +' + test_expect_success 'test --track without .fetch entries' ' git branch --track my8 && test "$(git config branch.my8.remote)" && @@ -446,6 +473,13 @@ test_expect_success '--set-upstream-to fails on a non-ref' ' test_must_fail git branch --set-upstream-to HEAD^{} ' +test_expect_success '--set-upstream-to fails on locked config' ' + test_when_finished "rm -f .git/config.lock" && + >.git/config.lock && + git branch locked && + test_must_fail git branch --set-upstream-to locked +' + test_expect_success 'use --set-upstream-to modify HEAD' ' test_config branch.master.remote foo && test_config branch.master.merge foo && @@ -466,6 +500,13 @@ test_expect_success '--unset-upstream should fail if given a non-existent branch test_must_fail git branch --unset-upstream i-dont-exist ' +test_expect_success '--unset-upstream should fail if config is locked' ' + test_when_finished "rm -f .git/config.lock" && + git branch --set-upstream-to locked && + >.git/config.lock && + test_must_fail git branch --unset-upstream +' + test_expect_success 'test --unset-upstream on HEAD' ' git branch my14 && test_config branch.master.remote foo && @@ -579,7 +620,7 @@ test_expect_success 'avoid ambiguous track' ' git config remote.ambi1.fetch refs/heads/lalala:refs/heads/master && git config remote.ambi2.url lilili && git config remote.ambi2.fetch refs/heads/lilili:refs/heads/master && - git branch all1 master && + test_must_fail git branch all1 master && test -z "$(git config branch.all1.merge)" ' diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh index 4261403cf6..c6a3ccba1b 100755 --- a/t/t3203-branch-output.sh +++ b/t/t3203-branch-output.sh @@ -184,4 +184,16 @@ test_expect_success 'ambiguous branch/tag not marked' ' test_cmp expect actual ' +test_expect_success 'local-branch symrefs shortened properly' ' + git symbolic-ref refs/heads/ref-to-branch refs/heads/branch-one && + 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 + EOF + git branch >actual.raw && + grep ref-to <actual.raw >actual && + test_cmp expect actual +' + test_done diff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh index 8f64505e4f..488945e007 100755 --- a/t/t3402-rebase-merge.sh +++ b/t/t3402-rebase-merge.sh @@ -85,6 +85,15 @@ test_expect_success 'rebase -Xtheirs' ' ! grep 11 original ' +test_expect_success 'rebase -Xtheirs from orphan' ' + git checkout --orphan orphan-conflicting master~2 && + echo "AB $T" >> original && + git commit -morphan-conflicting original && + git rebase -Xtheirs master && + grep AB original && + ! grep 11 original +' + test_expect_success 'merge and rebase should match' ' git diff-tree -r test-rebase test-merge >difference && if test -s difference diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 544f9ad508..66348f11d1 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -62,7 +62,7 @@ test_expect_success 'setup' ' # "exec" commands are ran with the user shell by default, but this may # be non-POSIX. For example, if SHELL=zsh then ">file" doesn't work -# to create a file. Unseting SHELL avoids such non-portable behavior +# to create a file. Unsetting SHELL avoids such non-portable behavior # in tests. It must be exported for it to take effect where needed. SHELL= export SHELL @@ -555,10 +555,9 @@ test_expect_success 'rebase a detached HEAD' ' test_expect_success 'rebase a commit violating pre-commit' ' mkdir -p .git/hooks && - PRE_COMMIT=.git/hooks/pre-commit && - echo "#!/bin/sh" > $PRE_COMMIT && - echo "test -z \"\$(git diff --cached --check)\"" >> $PRE_COMMIT && - chmod a+x $PRE_COMMIT && + write_script .git/hooks/pre-commit <<-\EOF && + test -z "$(git diff --cached --check)" + EOF echo "monde! " >> file1 && test_tick && test_must_fail git commit -m doesnt-verify file1 && @@ -771,7 +770,6 @@ test_expect_success 'rebase-i history with funny messages' ' test_cmp expect actual ' - test_expect_success 'prepare for rebase -i --exec' ' git checkout master && git checkout -b execute && @@ -780,7 +778,6 @@ test_expect_success 'prepare for rebase -i --exec' ' test_commit three_exec main.txt three_exec ' - test_expect_success 'running "git rebase -i --exec git show HEAD"' ' set_fake_editor && git rebase -i --exec "git show HEAD" HEAD~2 >actual && @@ -793,7 +790,6 @@ test_expect_success 'running "git rebase -i --exec git show HEAD"' ' test_cmp expected actual ' - test_expect_success 'running "git rebase --exec git show HEAD -i"' ' git reset --hard execute && set_fake_editor && @@ -807,7 +803,6 @@ test_expect_success 'running "git rebase --exec git show HEAD -i"' ' test_cmp expected actual ' - test_expect_success 'running "git rebase -ix git show HEAD"' ' git reset --hard execute && set_fake_editor && @@ -835,7 +830,6 @@ test_expect_success 'rebase -ix with several <CMD>' ' test_cmp expected actual ' - test_expect_success 'rebase -ix with several instances of --exec' ' git reset --hard execute && set_fake_editor && @@ -850,7 +844,6 @@ test_expect_success 'rebase -ix with several instances of --exec' ' test_cmp expected actual ' - test_expect_success 'rebase -ix with --autosquash' ' git reset --hard execute && git checkout -b autosquash && @@ -876,16 +869,15 @@ test_expect_success 'rebase -ix with --autosquash' ' test_cmp expected actual ' - -test_expect_success 'rebase --exec without -i shows error message' ' +test_expect_success 'rebase --exec works without -i ' ' git reset --hard execute && - set_fake_editor && - test_must_fail git rebase --exec "git show HEAD" HEAD~2 2>actual && - echo "The --exec option must be used with the --interactive option" >expected && - test_i18ncmp expected actual + rm -rf exec_output && + EDITOR="echo >invoked_editor" git rebase --exec "echo a line >>exec_output" HEAD~2 2>actual && + test_i18ngrep "Successfully rebased and updated" actual && + test_line_count = 2 exec_output && + test_path_is_missing invoked_editor ' - test_expect_success 'rebase -i --exec without <CMD>' ' git reset --hard execute && set_fake_editor && diff --git a/t/t3412-rebase-root.sh b/t/t3412-rebase-root.sh index 0b52105728..73a39f2923 100755 --- a/t/t3412-rebase-root.sh +++ b/t/t3412-rebase-root.sh @@ -133,7 +133,7 @@ test_expect_success 'set up second root and merge' ' rm A B C && test_commit 6 D && git checkout other && - git merge third + git merge --allow-unrelated-histories third ' cat > expect-third <<'EOF' diff --git a/t/t3419-rebase-patch-id.sh b/t/t3419-rebase-patch-id.sh index 217dd79b2e..49f548cdb9 100755 --- a/t/t3419-rebase-patch-id.sh +++ b/t/t3419-rebase-patch-id.sh @@ -73,17 +73,17 @@ do_tests () { run git format-patch --stdout --ignore-if-in-upstream master " - test_expect_success $pr 'detect upstream patch' " + test_expect_success $pr 'detect upstream patch' ' git checkout -q master && scramble file && git add file && - git commit -q -m 'change big file again' && + git commit -q -m "change big file again" && git checkout -q other^{} && git rebase master && - test_must_fail test -n \"\$(git rev-list master...HEAD~)\" - " + test_must_fail test -n "$(git rev-list master...HEAD~)" + ' - test_expect_success $pr 'do not drop patch' " + test_expect_success $pr 'do not drop patch' ' git branch -f squashed master && git checkout -q -f squashed && git reset -q --soft HEAD~2 && @@ -91,7 +91,7 @@ do_tests () { git checkout -q other^{} && test_must_fail git rebase squashed && rm -rf .git/rebase-apply - " + ' } do_tests 500 diff --git a/t/t3421-rebase-topology-linear.sh b/t/t3421-rebase-topology-linear.sh index 9c55cba198..68fe2003ef 100755 --- a/t/t3421-rebase-topology-linear.sh +++ b/t/t3421-rebase-topology-linear.sh @@ -253,7 +253,7 @@ test_run_rebase () { " } test_run_rebase success '' -test_run_rebase failure -m +test_run_rebase success -m test_run_rebase success -i test_run_rebase success -p @@ -268,7 +268,7 @@ test_run_rebase () { " } test_run_rebase success '' -test_run_rebase failure -m +test_run_rebase success -m test_run_rebase success -i test_run_rebase failure -p diff --git a/t/t3513-revert-submodule.sh b/t/t3513-revert-submodule.sh index a1c4e0216f..db9378142a 100755 --- a/t/t3513-revert-submodule.sh +++ b/t/t3513-revert-submodule.sh @@ -14,11 +14,11 @@ test_description='revert can handle submodules' git_revert () { git status -su >expect && ls -1pR * >>expect && - tar czf "$TRASH_DIRECTORY/tmp.tgz" * && + tar cf "$TRASH_DIRECTORY/tmp.tar" * && git checkout "$1" && git revert HEAD && rm -rf * && - tar xzf "$TRASH_DIRECTORY/tmp.tgz" && + tar xf "$TRASH_DIRECTORY/tmp.tar" && git status -su >actual && ls -1pR * >>actual && test_cmp expect actual && diff --git a/t/t3910-mac-os-precompose.sh b/t/t3910-mac-os-precompose.sh index 831935665e..26dd5b7f78 100755 --- a/t/t3910-mac-os-precompose.sh +++ b/t/t3910-mac-os-precompose.sh @@ -49,12 +49,54 @@ test_expect_success "setup" ' test_expect_success "setup case mac" ' git checkout -b mac_os ' +# This will test nfd2nfc in git diff +test_expect_success "git diff f.Adiar" ' + touch f.$Adiarnfc && + git add f.$Adiarnfc && + echo f.Adiarnfc >f.$Adiarnfc && + git diff f.$Adiarnfd >expect && + git diff f.$Adiarnfc >actual && + test_cmp expect actual && + git reset HEAD f.Adiarnfc && + rm f.$Adiarnfc expect actual +' +# This will test nfd2nfc in git diff-files +test_expect_success "git diff-files f.Adiar" ' + touch f.$Adiarnfc && + git add f.$Adiarnfc && + echo f.Adiarnfc >f.$Adiarnfc && + git diff-files f.$Adiarnfd >expect && + git diff-files f.$Adiarnfc >actual && + test_cmp expect actual && + git reset HEAD f.Adiarnfc && + rm f.$Adiarnfc expect actual +' +# This will test nfd2nfc in git diff-index +test_expect_success "git diff-index f.Adiar" ' + touch f.$Adiarnfc && + git add f.$Adiarnfc && + echo f.Adiarnfc >f.$Adiarnfc && + git diff-index HEAD f.$Adiarnfd >expect && + git diff-index HEAD f.$Adiarnfc >actual && + test_cmp expect actual && + git reset HEAD f.Adiarnfc && + rm f.$Adiarnfc expect actual +' # This will test nfd2nfc in readdir() test_expect_success "add file Adiarnfc" ' echo f.Adiarnfc >f.$Adiarnfc && git add f.$Adiarnfc && git commit -m "add f.$Adiarnfc" ' +# This will test nfd2nfc in git diff-tree +test_expect_success "git diff-tree f.Adiar" ' + echo f.Adiarnfc >>f.$Adiarnfc && + git diff-tree HEAD f.$Adiarnfd >expect && + git diff-tree HEAD f.$Adiarnfc >actual && + test_cmp expect actual && + git checkout f.$Adiarnfc && + rm expect actual +' # This will test nfd2nfc in git stage() test_expect_success "stage file d.Adiarnfd/f.Adiarnfd" ' mkdir d.$Adiarnfd && diff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh index 2f327b7495..0d1fa45d25 100755 --- a/t/t4001-diff-rename.sh +++ b/t/t4001-diff-rename.sh @@ -9,21 +9,84 @@ test_description='Test rename detection in diff engine. . ./test-lib.sh . "$TEST_DIRECTORY"/diff-lib.sh -echo >path0 'Line 1 -Line 2 -Line 3 -Line 4 -Line 5 -Line 6 -Line 7 -Line 8 -Line 9 -Line 10 -line 11 -Line 12 -Line 13 -Line 14 -Line 15 +test_expect_success 'setup' ' + cat >path0 <<-\EOF && + Line 1 + Line 2 + Line 3 + Line 4 + Line 5 + Line 6 + Line 7 + Line 8 + Line 9 + Line 10 + line 11 + Line 12 + Line 13 + Line 14 + Line 15 + EOF + cat >expected <<-\EOF && + diff --git a/path0 b/path1 + rename from path0 + rename to path1 + --- a/path0 + +++ b/path1 + @@ -8,7 +8,7 @@ Line 7 + Line 8 + Line 9 + Line 10 + -line 11 + +Line 11 + Line 12 + Line 13 + Line 14 + EOF + cat >no-rename <<-\EOF + diff --git a/path0 b/path0 + deleted file mode 100644 + index fdbec44..0000000 + --- a/path0 + +++ /dev/null + @@ -1,15 +0,0 @@ + -Line 1 + -Line 2 + -Line 3 + -Line 4 + -Line 5 + -Line 6 + -Line 7 + -Line 8 + -Line 9 + -Line 10 + -line 11 + -Line 12 + -Line 13 + -Line 14 + -Line 15 + diff --git a/path1 b/path1 + new file mode 100644 + index 0000000..752c50e + --- /dev/null + +++ b/path1 + @@ -0,0 +1,15 @@ + +Line 1 + +Line 2 + +Line 3 + +Line 4 + +Line 5 + +Line 6 + +Line 7 + +Line 8 + +Line 9 + +Line 10 + +Line 11 + +Line 12 + +Line 13 + +Line 14 + +Line 15 + EOF ' test_expect_success \ @@ -43,27 +106,27 @@ test_expect_success \ test_expect_success \ 'git diff-index -p -M after rename and editing.' \ 'git diff-index -p -M $tree >current' -cat >expected <<\EOF -diff --git a/path0 b/path1 -rename from path0 -rename to path1 ---- a/path0 -+++ b/path1 -@@ -8,7 +8,7 @@ Line 7 - Line 8 - Line 9 - Line 10 --line 11 -+Line 11 - Line 12 - Line 13 - Line 14 -EOF + test_expect_success \ 'validate the output.' \ 'compare_diff_patch current expected' +test_expect_success 'test diff.renames=true' ' + git -c diff.renames=true diff --cached $tree >current && + compare_diff_patch current expected +' + +test_expect_success 'test diff.renames=false' ' + git -c diff.renames=false diff --cached $tree >current && + compare_diff_patch current no-rename +' + +test_expect_success 'test diff.renames unset' ' + git diff --cached $tree >current && + compare_diff_patch current expected +' + test_expect_success 'favour same basenames over different ones' ' cp path1 another-path && git add another-path && @@ -77,6 +140,17 @@ test_expect_success 'favour same basenames even with minor differences' ' git show HEAD:path1 | sed "s/15/16/" > subdir/path1 && git status | test_i18ngrep "renamed: .*path1 -> subdir/path1"' +test_expect_success 'two files with same basename and same content' ' + git reset --hard && + mkdir -p dir/A dir/B && + cp path1 dir/A/file && + cp path1 dir/B/file && + git add dir && + git commit -m 2 && + git mv dir other-dir && + git status | test_i18ngrep "renamed: .*dir/A/file -> other-dir/A/file" +' + test_expect_success 'setup for many rename source candidates' ' git reset --hard && for i in 0 1 2 3 4 5 6 7 8 9; diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh index 6ec6072118..94ef5000e7 100755 --- a/t/t4013-diff-various.sh +++ b/t/t4013-diff-various.sh @@ -90,6 +90,8 @@ test_expect_success setup ' git commit -m "Rearranged lines in dir/sub" && git checkout master && + git config diff.renames false && + git show-branch ' diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh index 3b99434e3e..8049cad374 100755 --- a/t/t4014-format-patch.sh +++ b/t/t4014-format-patch.sh @@ -549,7 +549,7 @@ test_expect_success 'cover-letter inherits diff options' ' git mv file foo && git commit -m foo && - git format-patch --cover-letter -1 && + git format-patch --no-renames --cover-letter -1 && check_patch 0000-cover-letter.patch && ! grep "file => foo .* 0 *\$" 0000-cover-letter.patch && git format-patch --cover-letter -1 -M && @@ -703,7 +703,7 @@ test_expect_success 'options no longer allowed for format-patch' ' test_expect_success 'format-patch --numstat should produce a patch' ' git format-patch --numstat --stdout master..side > output && - test 6 = $(grep "^diff --git a/" output | wc -l)' + test 5 = $(grep "^diff --git a/" output | wc -l)' test_expect_success 'format-patch -- <path>' ' git format-patch master..side -- file 2>error && @@ -1460,4 +1460,109 @@ test_expect_success 'format-patch -o overrides format.outputDirectory' ' test_path_is_dir patchset ' +test_expect_success 'format-patch --base' ' + git checkout side && + git format-patch --stdout --base=HEAD~3 -1 >patch && + grep "^base-commit:" patch >actual && + grep "^prerequisite-patch-id:" patch >>actual && + echo "base-commit: $(git rev-parse HEAD~3)" >expected && + echo "prerequisite-patch-id: $(git show --patch HEAD~2 | git patch-id --stable | awk "{print \$1}")" >>expected && + echo "prerequisite-patch-id: $(git show --patch HEAD~1 | git patch-id --stable | awk "{print \$1}")" >>expected && + test_cmp expected actual +' + +test_expect_success 'format-patch --base errors out when base commit is in revision list' ' + test_must_fail git format-patch --base=HEAD -2 && + test_must_fail git format-patch --base=HEAD~1 -2 && + git format-patch --stdout --base=HEAD~2 -2 >patch && + grep "^base-commit:" patch >actual && + echo "base-commit: $(git rev-parse HEAD~2)" >expected && + test_cmp expected actual +' + +test_expect_success 'format-patch --base errors out when base commit is not ancestor of revision list' ' + # For history as below: + # + # ---Q---P---Z---Y---*---X + # \ / + # ------------W + # + # If "format-patch Z..X" is given, P and Z can not be specified as the base commit + git checkout -b topic1 master && + git rev-parse HEAD >commit-id-base && + test_commit P && + git rev-parse HEAD >commit-id-P && + test_commit Z && + git rev-parse HEAD >commit-id-Z && + test_commit Y && + git checkout -b topic2 master && + test_commit W && + git merge topic1 && + test_commit X && + test_must_fail git format-patch --base=$(cat commit-id-P) -3 && + test_must_fail git format-patch --base=$(cat commit-id-Z) -3 && + git format-patch --stdout --base=$(cat commit-id-base) -3 >patch && + grep "^base-commit:" patch >actual && + echo "base-commit: $(cat commit-id-base)" >expected && + test_cmp expected actual +' + +test_expect_success 'format-patch --base=auto' ' + git checkout -b upstream master && + git checkout -b local upstream && + git branch --set-upstream-to=upstream && + test_commit N1 && + test_commit N2 && + git format-patch --stdout --base=auto -2 >patch && + grep "^base-commit:" patch >actual && + echo "base-commit: $(git rev-parse upstream)" >expected && + test_cmp expected actual +' + +test_expect_success 'format-patch errors out when history involves criss-cross' ' + # setup criss-cross history + # + # B---M1---D + # / \ / + # A X + # \ / \ + # C---M2---E + # + git checkout master && + test_commit A && + git checkout -b xb master && + test_commit B && + git checkout -b xc master && + test_commit C && + git checkout -b xbc xb -- && + git merge xc && + git checkout -b xcb xc -- && + git branch --set-upstream-to=xbc && + git merge xb && + git checkout xbc && + test_commit D && + git checkout xcb && + test_commit E && + test_must_fail git format-patch --base=auto -1 +' + +test_expect_success 'format-patch format.useAutoBaseoption' ' + test_when_finished "git config --unset format.useAutoBase" && + git checkout local && + git config format.useAutoBase true && + git format-patch --stdout -1 >patch && + grep "^base-commit:" patch >actual && + echo "base-commit: $(git rev-parse upstream)" >expected && + test_cmp expected actual +' + +test_expect_success 'format-patch --base overrides format.useAutoBase' ' + test_when_finished "git config --unset format.useAutoBase" && + git config format.useAutoBase true && + git format-patch --stdout --base=HEAD~1 -1 >patch && + grep "^base-commit:" patch >actual && + echo "base-commit: $(git rev-parse HEAD~1)" >expected && + test_cmp expected actual +' + test_done diff --git a/t/t4047-diff-dirstat.sh b/t/t4047-diff-dirstat.sh index 3b8b7921d6..447a8ffa3a 100755 --- a/t/t4047-diff-dirstat.sh +++ b/t/t4047-diff-dirstat.sh @@ -248,7 +248,8 @@ EOF git rm -r src/move/unchanged && git rm -r src/move/changed && git rm -r src/move/rearranged && - git commit -m "changes" + git commit -m "changes" && + git config diff.renames false ' cat <<EOF >expect_diff_stat diff --git a/t/t4151-am-abort.sh b/t/t4151-am-abort.sh index ea5ace99a1..9473c2779e 100755 --- a/t/t4151-am-abort.sh +++ b/t/t4151-am-abort.sh @@ -82,7 +82,7 @@ test_expect_success 'am -3 --abort removes otherfile-4' ' test 4 = "$(cat otherfile-4)" && git am --abort && test_cmp_rev initial HEAD && - test -z $(git ls-files -u) && + test -z "$(git ls-files -u)" && test_path_is_missing otherfile-4 ' diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh index ed9c91e25b..1a080e7823 100755 --- a/t/t4200-rerere.sh +++ b/t/t4200-rerere.sh @@ -184,12 +184,27 @@ test_expect_success 'rerere updates postimage timestamp' ' ' test_expect_success 'rerere clear' ' - rm $rr/postimage && + mv $rr/postimage .git/post-saved && echo "$sha1 a1" | perl -pe "y/\012/\000/" >.git/MERGE_RR && git rerere clear && ! test -d $rr ' +test_expect_success 'leftover directory' ' + git reset --hard && + mkdir -p $rr && + test_must_fail git merge first && + test -f $rr/preimage +' + +test_expect_success 'missing preimage' ' + git reset --hard && + mkdir -p $rr && + cp .git/post-saved $rr/postimage && + test_must_fail git merge first && + test -f $rr/preimage +' + test_expect_success 'set up for garbage collection tests' ' mkdir -p $rr && echo Hello >$rr/preimage && @@ -391,4 +406,157 @@ test_expect_success 'rerere -h' ' test_i18ngrep [Uu]sage help ' +concat_insert () { + last=$1 + shift + cat early && printf "%s\n" "$@" && cat late "$last" +} + +count_pre_post () { + find .git/rr-cache/ -type f -name "preimage*" >actual && + test_line_count = "$1" actual && + find .git/rr-cache/ -type f -name "postimage*" >actual && + test_line_count = "$2" actual +} + +test_expect_success 'rerere gc' ' + find .git/rr-cache -type f >original && + xargs test-chmtime -172800 <original && + + git -c gc.rerereresolved=5 -c gc.rerereunresolved=5 rerere gc && + find .git/rr-cache -type f >actual && + test_cmp original actual && + + git -c gc.rerereresolved=5 -c gc.rerereunresolved=0 rerere gc && + find .git/rr-cache -type f >actual && + test_cmp original actual && + + git -c gc.rerereresolved=0 -c gc.rerereunresolved=0 rerere gc && + find .git/rr-cache -type f >actual && + >expect && + test_cmp expect actual +' + +merge_conflict_resolve () { + git reset --hard && + test_must_fail git merge six.1 && + # Resolution is to replace 7 with 6.1 and 6.2 (i.e. take both) + concat_insert short 6.1 6.2 >file1 && + concat_insert long 6.1 6.2 >file2 +} + +test_expect_success 'multiple identical conflicts' ' + git reset --hard && + + test_seq 1 6 >early && + >late && + test_seq 11 15 >short && + test_seq 111 120 >long && + concat_insert short >file1 && + concat_insert long >file2 && + git add file1 file2 && + git commit -m base && + git tag base && + git checkout -b six.1 && + concat_insert short 6.1 >file1 && + concat_insert long 6.1 >file2 && + git add file1 file2 && + git commit -m 6.1 && + git checkout -b six.2 HEAD^ && + concat_insert short 6.2 >file1 && + concat_insert long 6.2 >file2 && + git add file1 file2 && + git commit -m 6.2 && + + # At this point, six.1 and six.2 + # - derive from common ancestor that has two files + # 1...6 7 11..15 (file1) and 1...6 7 111..120 (file2) + # - six.1 replaces these 7s with 6.1 + # - six.2 replaces these 7s with 6.2 + + merge_conflict_resolve && + + # Check that rerere knows that file1 and file2 have conflicts + + printf "%s\n" file1 file2 >expect && + git ls-files -u | sed -e "s/^.* //" | sort -u >actual && + test_cmp expect actual && + + git rerere status | sort >actual && + test_cmp expect actual && + + git rerere remaining >actual && + test_cmp expect actual && + + count_pre_post 2 0 && + + # Pretend that the conflicts were made quite some time ago + find .git/rr-cache/ -type f | xargs test-chmtime -172800 && + + # Unresolved entries have not expired yet + git -c gc.rerereresolved=5 -c gc.rerereunresolved=5 rerere gc && + count_pre_post 2 0 && + + # Unresolved entries have expired + git -c gc.rerereresolved=5 -c gc.rerereunresolved=1 rerere gc && + count_pre_post 0 0 && + + # Recreate the conflicted state + merge_conflict_resolve && + count_pre_post 2 0 && + + # Clear it + git rerere clear && + count_pre_post 0 0 && + + # Recreate the conflicted state + merge_conflict_resolve && + count_pre_post 2 0 && + + # We resolved file1 and file2 + git rerere && + >expect && + git rerere remaining >actual && + test_cmp expect actual && + + # We must have recorded both of them + count_pre_post 2 2 && + + # Now we should be able to resolve them both + git reset --hard && + test_must_fail git merge six.1 && + git rerere && + + >expect && + git rerere remaining >actual && + test_cmp expect actual && + + concat_insert short 6.1 6.2 >file1.expect && + concat_insert long 6.1 6.2 >file2.expect && + test_cmp file1.expect file1 && + test_cmp file2.expect file2 && + + # Forget resolution for file2 + git rerere forget file2 && + echo file2 >expect && + git rerere status >actual && + test_cmp expect actual && + count_pre_post 2 1 && + + # file2 already has correct resolution, so record it again + git rerere && + + # Pretend that the resolutions are old again + find .git/rr-cache/ -type f | xargs test-chmtime -172800 && + + # Resolved entries have not expired yet + git -c gc.rerereresolved=5 -c gc.rerereunresolved=5 rerere gc && + + count_pre_post 2 2 && + + # Resolved entries have expired + git -c gc.rerereresolved=1 -c gc.rerereunresolved=5 rerere gc && + count_pre_post 0 0 +' + test_done diff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh index f5e63670fa..a9773658f0 100755 --- a/t/t4201-shortlog.sh +++ b/t/t4201-shortlog.sh @@ -115,7 +115,7 @@ EOF ' test_expect_success !MINGW 'shortlog from non-git directory' ' - git log HEAD >log && + git log --no-expand-tabs HEAD >log && GIT_DIR=non-existing git shortlog -w <log >out && test_cmp expect out ' diff --git a/t/t4202-log.sh b/t/t4202-log.sh index cb82eb7e66..128ba93537 100755 --- a/t/t4202-log.sh +++ b/t/t4202-log.sh @@ -101,8 +101,8 @@ test_expect_success 'oneline' ' test_expect_success 'diff-filter=A' ' - git log --pretty="format:%s" --diff-filter=A HEAD > actual && - git log --pretty="format:%s" --diff-filter A HEAD > actual-separate && + git log --no-renames --pretty="format:%s" --diff-filter=A HEAD > actual && + git log --no-renames --pretty="format:%s" --diff-filter A HEAD > actual-separate && printf "fifth\nfourth\nthird\ninitial" > expect && test_cmp expect actual && test_cmp expect actual-separate @@ -119,7 +119,7 @@ test_expect_success 'diff-filter=M' ' test_expect_success 'diff-filter=D' ' - actual=$(git log --pretty="format:%s" --diff-filter=D HEAD) && + actual=$(git log --no-renames --pretty="format:%s" --diff-filter=D HEAD) && expect=$(echo sixth ; echo third) && verbose test "$actual" = "$expect" @@ -848,7 +848,7 @@ sanitize_output () { } test_expect_success 'log --graph with diff and stats' ' - git log --graph --pretty=short --stat -p >actual && + git log --no-renames --graph --pretty=short --stat -p >actual && sanitize_output >actual.sanitized <actual && test_i18ncmp expect actual.sanitized ' diff --git a/t/t4204-patch-id.sh b/t/t4204-patch-id.sh index baa9d3c82e..84a809690e 100755 --- a/t/t4204-patch-id.sh +++ b/t/t4204-patch-id.sh @@ -30,11 +30,11 @@ test_expect_success 'patch-id output is well-formed' ' #calculate patch id. Make sure output is not empty. calc_patch_id () { - name="$1" + patch_name="$1" shift git patch-id "$@" | - sed "s/ .*//" >patch-id_"$name" && - test_line_count -gt 0 patch-id_"$name" + sed "s/ .*//" >patch-id_"$patch_name" && + test_line_count -gt 0 patch-id_"$patch_name" } get_top_diff () { diff --git a/t/t4213-log-tabexpand.sh b/t/t4213-log-tabexpand.sh new file mode 100755 index 0000000000..e01a8f6ac9 --- /dev/null +++ b/t/t4213-log-tabexpand.sh @@ -0,0 +1,105 @@ +#!/bin/sh + +test_description='log/show --expand-tabs' + +. ./test-lib.sh + +HT=" " +title='tab indent at the beginning of the title line' +body='tab indent on a line in the body' + +# usage: count_expand $indent $numSP $numHT @format_args +count_expand () +{ + expect= + count=$(( $1 + $2 )) ;# expected spaces + while test $count -gt 0 + do + expect="$expect " + count=$(( $count - 1 )) + done + shift 2 + count=$1 ;# expected tabs + while test $count -gt 0 + do + expect="$expect$HT" + count=$(( $count - 1 )) + done + shift + + # The remainder of the command line is "git show -s" options + case " $* " in + *' --pretty=short '*) + line=$title ;; + *) + line=$body ;; + esac + + # Prefix the output with the command line arguments, and + # replace SP with a dot both in the expecte and actual output + # so that test_cmp would show the differene together with the + # breakage in a way easier to consume by the debugging user. + { + echo "git show -s $*" + echo "$expect$line" + } | sed -e 's/ /./g' >expect + + { + echo "git show -s $*" + git show -s "$@" | + sed -n -e "/$line\$/p" + } | sed -e 's/ /./g' >actual + + test_cmp expect actual +} + +test_expand () +{ + fmt=$1 + case "$fmt" in + *=raw | *=short | *=email) + default="0 1" ;; + *) + default="8 0" ;; + esac + case "$fmt" in + *=email) + in=0 ;; + *) + in=4 ;; + esac + test_expect_success "expand/no-expand${fmt:+ for $fmt}" ' + count_expand $in $default $fmt && + count_expand $in 8 0 $fmt --expand-tabs && + count_expand $in 8 0 --expand-tabs $fmt && + count_expand $in 8 0 $fmt --expand-tabs=8 && + count_expand $in 8 0 --expand-tabs=8 $fmt && + count_expand $in 0 1 $fmt --no-expand-tabs && + count_expand $in 0 1 --no-expand-tabs $fmt && + count_expand $in 0 1 $fmt --expand-tabs=0 && + count_expand $in 0 1 --expand-tabs=0 $fmt && + count_expand $in 4 0 $fmt --expand-tabs=4 && + count_expand $in 4 0 --expand-tabs=4 $fmt + ' +} + +test_expect_success 'setup' ' + test_tick && + sed -e "s/Q/$HT/g" <<-EOF >msg && + Q$title + + Q$body + EOF + git commit --allow-empty -F msg +' + +test_expand "" +test_expand --pretty +test_expand --pretty=short +test_expand --pretty=medium +test_expand --pretty=full +test_expand --pretty=fuller +test_expand --pretty=raw +test_expand --pretty=email + +test_done diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh index fc2be63e02..899e52d50f 100755 --- a/t/t5300-pack-object.sh +++ b/t/t5300-pack-object.sh @@ -284,6 +284,12 @@ test_expect_success \ git index-pack test-3.pack && cmp test-3.idx test-3-${packname_3}.idx && + cat test-1-${packname_1}.pack >test-4.pack && + rm -f test-4.keep && + git index-pack --keep=why test-4.pack && + cmp test-1-${packname_1}.idx test-4.idx && + test -f test-4.keep && + :' test_expect_success 'unpacking with --strict' ' diff --git a/t/t5313-pack-bounds-checks.sh b/t/t5313-pack-bounds-checks.sh new file mode 100755 index 0000000000..a8a587abc3 --- /dev/null +++ b/t/t5313-pack-bounds-checks.sh @@ -0,0 +1,179 @@ +#!/bin/sh + +test_description='bounds-checking of access to mmapped on-disk file formats' +. ./test-lib.sh + +clear_base () { + test_when_finished 'restore_base' && + rm -f $base +} + +restore_base () { + cp base-backup/* .git/objects/pack/ +} + +do_pack () { + pack_objects=$1; shift + sha1=$( + for i in $pack_objects + do + echo $i + done | git pack-objects "$@" .git/objects/pack/pack + ) && + pack=.git/objects/pack/pack-$sha1.pack && + idx=.git/objects/pack/pack-$sha1.idx && + chmod +w $pack $idx && + test_when_finished 'rm -f "$pack" "$idx"' +} + +munge () { + printf "$3" | dd of="$1" bs=1 conv=notrunc seek=$2 +} + +# Offset in a v2 .idx to its initial and extended offset tables. For an index +# with "nr" objects, this is: +# +# magic(4) + version(4) + fan-out(4*256) + sha1s(20*nr) + crc(4*nr), +# +# for the initial, and another ofs(4*nr) past that for the extended. +# +ofs_table () { + echo $((4 + 4 + 4*256 + 20*$1 + 4*$1)) +} +extended_table () { + echo $(($(ofs_table "$1") + 4*$1)) +} + +test_expect_success 'set up base packfile and variables' ' + # the hash of this content starts with ff, which + # makes some later computations much simpler + echo 74 >file && + git add file && + git commit -m base && + git repack -ad && + base=$(echo .git/objects/pack/*) && + chmod +w $base && + mkdir base-backup && + cp $base base-backup/ && + object=$(git rev-parse HEAD:file) +' + +test_expect_success 'pack/index object count mismatch' ' + do_pack $object && + munge $pack 8 "\377\0\0\0" && + clear_base && + + # We enumerate the objects from the completely-fine + # .idx, but notice later that the .pack is bogus + # and fail to show any data. + echo "$object missing" >expect && + git cat-file --batch-all-objects --batch-check >actual && + test_cmp expect actual && + + # ...and here fail to load the object (without segfaulting), + # but fallback to a good copy if available. + test_must_fail git cat-file blob $object && + restore_base && + git cat-file blob $object >actual && + test_cmp file actual && + + # ...and make sure that index-pack --verify, which has its + # own reading routines, does not segfault. + test_must_fail git index-pack --verify $pack +' + +test_expect_success 'matched bogus object count' ' + do_pack $object && + munge $pack 8 "\377\0\0\0" && + munge $idx $((255 * 4)) "\377\0\0\0" && + clear_base && + + # Unlike above, we should notice early that the .idx is totally + # bogus, and not even enumerate its contents. + >expect && + git cat-file --batch-all-objects --batch-check >actual && + test_cmp expect actual && + + # But as before, we can do the same object-access checks. + test_must_fail git cat-file blob $object && + restore_base && + git cat-file blob $object >actual && + test_cmp file actual && + + test_must_fail git index-pack --verify $pack +' + +# Note that we cannot check the fallback case for these +# further .idx tests, as we notice the problem in functions +# whose interface doesn't allow an error return (like use_pack()), +# and thus we just die(). +# +# There's also no point in doing enumeration tests, as +# we are munging offsets here, which are about looking up +# specific objects. + +test_expect_success 'bogus object offset (v1)' ' + do_pack $object --index-version=1 && + munge $idx $((4 * 256)) "\377\0\0\0" && + clear_base && + test_must_fail git cat-file blob $object && + test_must_fail git index-pack --verify $pack +' + +test_expect_success 'bogus object offset (v2, no msb)' ' + do_pack $object --index-version=2 && + munge $idx $(ofs_table 1) "\0\377\0\0" && + clear_base && + test_must_fail git cat-file blob $object && + test_must_fail git index-pack --verify $pack +' + +test_expect_success 'bogus offset into v2 extended table' ' + do_pack $object --index-version=2 && + munge $idx $(ofs_table 1) "\377\0\0\0" && + clear_base && + test_must_fail git cat-file blob $object && + test_must_fail git index-pack --verify $pack +' + +test_expect_success 'bogus offset inside v2 extended table' ' + # We need two objects here, so we can plausibly require + # an extended table (if the first object were larger than 2^31). + do_pack "$object $(git rev-parse HEAD)" --index-version=2 && + + # We have to make extra room for the table, so we cannot + # just munge in place as usual. + { + dd if=$idx bs=1 count=$(($(ofs_table 2) + 4)) && + printf "\200\0\0\0" && + printf "\377\0\0\0\0\0\0\0" && + dd if=$idx bs=1 skip=$(extended_table 2) + } >tmp && + mv tmp "$idx" && + clear_base && + test_must_fail git cat-file blob $object && + test_must_fail git index-pack --verify $pack +' + +test_expect_success 'bogus OFS_DELTA in packfile' ' + # Generate a pack with a delta in it. + base=$(test-genrandom foo 3000 | git hash-object --stdin -w) && + delta=$(test-genrandom foo 2000 | git hash-object --stdin -w) && + do_pack "$base $delta" --delta-base-offset && + rm -f .git/objects/??/* && + + # Double check that we have the delta we expect. + echo $base >expect && + echo $delta | git cat-file --batch-check="%(deltabase)" >actual && + test_cmp expect actual && + + # Now corrupt it. We assume the varint size for the delta is small + # enough to fit in the first byte (which it should be, since it + # is a pure deletion from the base), and that original ofs_delta + # takes 2 bytes (which it should, as it should be ~3000). + ofs=$(git show-index <$idx | grep $delta | cut -d" " -f1) && + munge $pack $(($ofs + 1)) "\177\377" && + test_must_fail git cat-file blob $delta >/dev/null +' + +test_done diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh index 04cea97f87..305ca7a930 100755 --- a/t/t5400-send-pack.sh +++ b/t/t5400-send-pack.sh @@ -128,6 +128,18 @@ test_expect_success 'denyNonFastforwards trumps --force' ' test "$victim_orig" = "$victim_head" ' +test_expect_success 'send-pack --all sends all branches' ' + # make sure we have at least 2 branches with different + # values, just to be thorough + git branch other-branch HEAD^ && + + git init --bare all.git && + git send-pack --all all.git && + git for-each-ref refs/heads >expect && + git -C all.git for-each-ref refs/heads >actual && + test_cmp expect actual +' + test_expect_success 'push --all excludes remote-tracking hierarchy' ' mkdir parent && ( diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index e5f83bf5e4..91a69fc33a 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -259,7 +259,8 @@ test_expect_success 'clone shallow object count' ' test_expect_success 'pull in shallow repo with missing merge base' ' ( cd shallow && - test_must_fail git pull --depth 4 .. A + git fetch --depth 4 .. A + test_must_fail git merge --allow-unrelated-histories FETCH_HEAD ) ' @@ -279,9 +280,10 @@ test_expect_success 'clone shallow depth count' ' test_expect_success 'clone shallow object count' ' ( cd shallow && + git prune && git count-objects -v ) > count.shallow && - grep "^count: 55" count.shallow + grep "^count: 54" count.shallow ' test_expect_success 'fetch --no-shallow on full repo' ' @@ -531,6 +533,20 @@ test_expect_success 'shallow fetch with tags does not break the repository' ' git fsck ) ' + +test_expect_success 'fetch-pack can fetch a raw sha1' ' + git init hidden && + ( + cd hidden && + test_commit 1 && + test_commit 2 && + git update-ref refs/hidden/one HEAD^ && + git config transfer.hiderefs refs/hidden && + git config uploadpack.allowtipsha1inwant true + ) && + git fetch-pack hidden $(git -C hidden rev-parse refs/hidden/one) +' + check_prot_path () { cat >expected <<-EOF && Diag: url=$1 diff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh index 89224edcc5..44f3d5fb28 100755 --- a/t/t5504-fetch-receive-strict.sh +++ b/t/t5504-fetch-receive-strict.sh @@ -100,7 +100,7 @@ test_expect_success 'push with receive.fsckobjects' ' git config receive.fsckobjects true && git config transfer.fsckobjects false ) && - test_must_fail ok=sigpipe git push --porcelain dst master:refs/heads/test >act && + test_must_fail git push --porcelain dst master:refs/heads/test >act && test_cmp exp act ' @@ -111,7 +111,8 @@ test_expect_success 'push with transfer.fsckobjects' ' cd dst && git config transfer.fsckobjects true ) && - test_must_fail ok=sigpipe git push --porcelain dst master:refs/heads/test >act + test_must_fail git push --porcelain dst master:refs/heads/test >act && + test_cmp exp act ' cat >bogus-commit <<\EOF diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh index 1a8e3b81c8..dd2e6ce34e 100755 --- a/t/t5505-remote.sh +++ b/t/t5505-remote.sh @@ -51,6 +51,11 @@ test_expect_success setup ' git clone one test ' +test_expect_success 'add remote whose URL agrees with url.<...>.insteadOf' ' + test_config url.git@host.com:team/repo.git.insteadOf myremote && + git remote add myremote git@host.com:team/repo.git +' + test_expect_success C_LOCALE_OUTPUT 'remote information for the origin' ' ( cd test && @@ -139,6 +144,39 @@ test_expect_success 'remove remote protects local branches' ' ) ' +test_expect_success 'remove errors out early when deleting non-existent branch' ' + ( + cd test && + echo "fatal: No such remote: foo" >expect && + test_must_fail git remote rm foo 2>actual && + test_i18ncmp expect actual + ) +' + +test_expect_success 'rename errors out early when deleting non-existent branch' ' + ( + cd test && + echo "fatal: No such remote: foo" >expect && + test_must_fail git remote rename foo bar 2>actual && + test_i18ncmp expect actual + ) +' + +test_expect_success 'add existing foreign_vcs remote' ' + test_config remote.foo.vcs bar && + echo "fatal: remote foo already exists." >expect && + test_must_fail git remote add foo bar 2>actual && + test_i18ncmp expect actual +' + +test_expect_success 'add existing foreign_vcs remote' ' + test_config remote.foo.vcs bar && + test_config remote.bar.vcs bar && + echo "fatal: remote bar already exists." >expect && + test_must_fail git remote rename foo bar 2>actual && + test_i18ncmp expect actual +' + cat >test/expect <<EOF * remote origin Fetch URL: $(pwd)/one @@ -932,6 +970,15 @@ test_expect_success 'get-url on new remote' ' echo foo | get_url_test --push --all someremote ' +test_expect_success 'remote set-url with locked config' ' + test_when_finished "rm -f .git/config.lock" && + git config --get-all remote.someremote.url >expect && + >.git/config.lock && + test_must_fail git remote set-url someremote baz && + git config --get-all remote.someremote.url >actual && + cmp expect actual +' + test_expect_success 'remote set-url bar' ' git remote set-url someremote bar && echo bar >expect && diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index 0c10c856a9..454d896390 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -679,10 +679,13 @@ test_expect_success 'fetching with auto-gc does not lock up' ' EOF git clone "file://$D" auto-gc && test_commit test2 && - cd auto-gc && - git config gc.autoPackLimit 1 && - GIT_ASK_YESNO="$D/askyesno" git fetch >fetch.out 2>&1 && - ! grep "Should I try again" fetch.out + ( + cd auto-gc && + git config gc.autoPackLimit 1 && + git config gc.autoDetach false && + GIT_ASK_YESNO="$D/askyesno" git fetch >fetch.out 2>&1 && + ! grep "Should I try again" fetch.out + ) ' test_done diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh index c952d5ef5c..739c089d50 100755 --- a/t/t5520-pull.sh +++ b/t/t5520-pull.sh @@ -9,6 +9,24 @@ modify () { mv "$2.x" "$2" } +test_pull_autostash () { + git reset --hard before-rebase && + echo dirty >new_file && + git add new_file && + git pull "$@" . copy && + test_cmp_rev HEAD^ copy && + test "$(cat new_file)" = dirty && + test "$(cat file)" = "modified again" +} + +test_pull_autostash_fail () { + git reset --hard before-rebase && + echo dirty >new_file && + git add new_file && + test_must_fail git pull "$@" . copy 2>err && + test_i18ngrep "uncommitted changes." err +} + test_expect_success setup ' echo file >file && git add file && @@ -247,15 +265,47 @@ test_expect_success '--rebase fails with multiple branches' ' test_expect_success 'pull --rebase succeeds with dirty working directory and rebase.autostash set' ' test_config rebase.autostash true && - git reset --hard before-rebase && - echo dirty >new_file && - git add new_file && - git pull --rebase . copy && - test_cmp_rev HEAD^ copy && - test "$(cat new_file)" = dirty && - test "$(cat file)" = "modified again" + test_pull_autostash --rebase ' +test_expect_success 'pull --rebase --autostash & rebase.autostash=true' ' + test_config rebase.autostash true && + test_pull_autostash --rebase --autostash +' + +test_expect_success 'pull --rebase --autostash & rebase.autostash=false' ' + test_config rebase.autostash false && + test_pull_autostash --rebase --autostash +' + +test_expect_success 'pull --rebase --autostash & rebase.autostash unset' ' + test_unconfig rebase.autostash && + test_pull_autostash --rebase --autostash +' + +test_expect_success 'pull --rebase --no-autostash & rebase.autostash=true' ' + test_config rebase.autostash true && + test_pull_autostash_fail --rebase --no-autostash +' + +test_expect_success 'pull --rebase --no-autostash & rebase.autostash=false' ' + test_config rebase.autostash false && + test_pull_autostash_fail --rebase --no-autostash +' + +test_expect_success 'pull --rebase --no-autostash & rebase.autostash unset' ' + test_unconfig rebase.autostash && + test_pull_autostash_fail --rebase --no-autostash +' + +for i in --autostash --no-autostash +do + test_expect_success "pull $i (without --rebase) is illegal" ' + test_must_fail git pull $i . copy 2>err && + test_i18ngrep "only valid with --rebase" err + ' +done + test_expect_success 'pull.rebase' ' git reset --hard before-rebase && test_config pull.rebase true && @@ -264,6 +314,16 @@ test_expect_success 'pull.rebase' ' test new = "$(git show HEAD:file2)" ' +test_expect_success 'pull --autostash & pull.rebase=true' ' + test_config pull.rebase true && + test_pull_autostash --autostash +' + +test_expect_success 'pull --no-autostash & pull.rebase=true' ' + test_config pull.rebase true && + test_pull_autostash_fail --no-autostash +' + test_expect_success 'branch.to-rebase.rebase' ' git reset --hard before-rebase && test_config branch.to-rebase.rebase true && diff --git a/t/t5521-pull-options.sh b/t/t5521-pull-options.sh index 18372caa15..ded8f98dbe 100755 --- a/t/t5521-pull-options.sh +++ b/t/t5521-pull-options.sh @@ -144,4 +144,25 @@ test_expect_success 'git pull --all --dry-run' ' ) ' +test_expect_success 'git pull --allow-unrelated-histories' ' + test_when_finished "rm -fr src dst" && + git init src && + ( + cd src && + test_commit one && + test_commit two + ) && + git clone src dst && + ( + cd src && + git checkout --orphan side HEAD^ && + test_commit three + ) && + ( + cd dst && + test_must_fail git pull ../src side && + git pull --allow-unrelated-histories ../src side + ) +' + test_done diff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh index 1241146227..954d0e43f5 100755 --- a/t/t5526-fetch-submodules.sh +++ b/t/t5526-fetch-submodules.sh @@ -471,4 +471,18 @@ test_expect_success "don't fetch submodule when newly recorded commits are alrea test_i18ncmp expect.err actual.err ' +test_expect_success 'fetching submodules respects parallel settings' ' + git config fetch.recurseSubmodules true && + ( + cd downstream && + GIT_TRACE=$(pwd)/trace.out git fetch --jobs 7 && + grep "7 tasks" trace.out && + git config submodule.fetchJobs 8 && + GIT_TRACE=$(pwd)/trace.out git fetch && + grep "8 tasks" trace.out && + GIT_TRACE=$(pwd)/trace.out git fetch --jobs 9 && + grep "9 tasks" trace.out + ) +' + test_done diff --git a/t/t5532-fetch-proxy.sh b/t/t5532-fetch-proxy.sh index d75ef0ea2b..51c9669398 100755 --- a/t/t5532-fetch-proxy.sh +++ b/t/t5532-fetch-proxy.sh @@ -12,10 +12,8 @@ test_expect_success 'setup remote repo' ' ) ' -cat >proxy <<'EOF' -#!/bin/sh -echo >&2 "proxying for $*" -cmd=$("$PERL_PATH" -e ' +test_expect_success 'setup proxy script' ' + write_script proxy-get-cmd "$PERL_PATH" <<-\EOF && read(STDIN, $buf, 4); my $n = hex($buf) - 4; read(STDIN, $buf, $n); @@ -23,11 +21,16 @@ cmd=$("$PERL_PATH" -e ' # drop absolute-path on repo name $cmd =~ s{ /}{ }; print $cmd; -') -echo >&2 "Running '$cmd'" -exec $cmd -EOF -chmod +x proxy + EOF + + write_script proxy <<-\EOF + echo >&2 "proxying for $*" + cmd=$(./proxy-get-cmd) + echo >&2 "Running $cmd" + exec $cmd + EOF +' + test_expect_success 'setup local repo' ' git remote add fake git://example.com/remote && git config core.gitproxy ./proxy diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh index 64146352ae..3484b6f0f3 100755 --- a/t/t5550-http-fetch-dumb.sh +++ b/t/t5550-http-fetch-dumb.sh @@ -91,6 +91,55 @@ test_expect_success 'configured username does not override URL' ' expect_askpass pass user@host ' +test_expect_success 'set up repo with http submodules' ' + git init super && + set_askpass user@host pass@host && + ( + cd super && + git submodule add "$HTTPD_URL/auth/dumb/repo.git" sub && + git commit -m "add submodule" + ) +' + +test_expect_success 'cmdline credential config passes to submodule via clone' ' + set_askpass wrong pass@host && + test_must_fail git clone --recursive super super-clone && + rm -rf super-clone && + + set_askpass wrong pass@host && + git -c "credential.$HTTPD_URL.username=user@host" \ + clone --recursive super super-clone && + expect_askpass pass user@host +' + +test_expect_success 'cmdline credential config passes submodule via fetch' ' + set_askpass wrong pass@host && + test_must_fail git -C super-clone fetch --recurse-submodules && + + set_askpass wrong pass@host && + git -C super-clone \ + -c "credential.$HTTPD_URL.username=user@host" \ + fetch --recurse-submodules && + expect_askpass pass user@host +' + +test_expect_success 'cmdline credential config passes submodule update' ' + # advance the submodule HEAD so that a fetch is required + git commit --allow-empty -m foo && + git push "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/repo.git" HEAD && + sha1=$(git rev-parse HEAD) && + git -C super-clone update-index --cacheinfo 160000,$sha1,sub && + + set_askpass wrong pass@host && + test_must_fail git -C super-clone submodule update && + + set_askpass wrong pass@host && + git -C super-clone \ + -c "credential.$HTTPD_URL.username=user@host" \ + submodule update && + expect_askpass pass user@host +' + test_expect_success 'fetch changes via http' ' echo content >>file && git commit -a -m two && diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh index 58207d8825..2f375eb94d 100755 --- a/t/t5551-http-fetch-smart.sh +++ b/t/t5551-http-fetch-smart.sh @@ -282,5 +282,22 @@ test_expect_success EXPENSIVE 'http can handle enormous ref negotiation' ' test_line_count = 100000 tags ' +test_expect_success 'custom http headers' ' + test_must_fail git -c http.extraheader="x-magic-two: cadabra" \ + fetch "$HTTPD_URL/smart_headers/repo.git" && + git -c http.extraheader="x-magic-one: abra" \ + -c http.extraheader="x-magic-two: cadabra" \ + fetch "$HTTPD_URL/smart_headers/repo.git" && + git update-index --add --cacheinfo 160000,$(git rev-parse HEAD),sub && + git config -f .gitmodules submodule.sub.path sub && + git config -f .gitmodules submodule.sub.url \ + "$HTTPD_URL/smart_headers/repo.git" && + git submodule init sub && + test_must_fail git submodule update sub && + git -c http.extraheader="x-magic-one: abra" \ + -c http.extraheader="x-magic-two: cadabra" \ + submodule update sub +' + stop_httpd test_done diff --git a/t/t5570-git-daemon.sh b/t/t5570-git-daemon.sh index d76269a8fc..225a022e8a 100755 --- a/t/t5570-git-daemon.sh +++ b/t/t5570-git-daemon.sh @@ -6,6 +6,12 @@ test_description='test fetching over git protocol' . "$TEST_DIRECTORY"/lib-git-daemon.sh start_git_daemon +check_verbose_connect () { + grep -F "Looking up 127.0.0.1 ..." stderr && + grep -F "Connecting to 127.0.0.1 (port " stderr && + grep -F "done." stderr +} + test_expect_success 'setup repository' ' git config push.default matching && echo content >file && @@ -24,7 +30,8 @@ test_expect_success 'create git-accessible bare repository' ' ' test_expect_success 'clone git repository' ' - git clone "$GIT_DAEMON_URL/repo.git" clone && + git clone -v "$GIT_DAEMON_URL/repo.git" clone 2>stderr && + check_verbose_connect && test_cmp file clone/file ' @@ -32,10 +39,21 @@ test_expect_success 'fetch changes via git protocol' ' echo content >>file && git commit -a -m two && git push public && - (cd clone && git pull) && + (cd clone && git pull -v) 2>stderr && + check_verbose_connect && test_cmp file clone/file ' +test_expect_success 'no-op fetch -v stderr is as expected' ' + (cd clone && git fetch -v) 2>stderr && + check_verbose_connect +' + +test_expect_success 'no-op fetch without "-v" is quiet' ' + (cd clone && git fetch) 2>stderr && + ! test -s stderr +' + test_expect_success 'remote detects correct HEAD' ' git push public master:other && (cd clone && diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh index c1efb8e445..a433394200 100755 --- a/t/t5601-clone.sh +++ b/t/t5601-clone.sh @@ -308,7 +308,7 @@ test_expect_success 'clone checking out a tag' ' setup_ssh_wrapper () { test_expect_success 'setup ssh wrapper' ' - cp "$GIT_BUILD_DIR/test-fake-ssh$X" \ + cp "$GIT_BUILD_DIR/t/helper/test-fake-ssh$X" \ "$TRASH_DIRECTORY/ssh-wrapper$X" && GIT_SSH="$TRASH_DIRECTORY/ssh-wrapper$X" && export GIT_SSH && @@ -466,7 +466,7 @@ test_expect_success 'clone ssh://host.xz:22/~repo' ' #IPv6 for tuah in ::1 [::1] [::1]: user@::1 user@[::1] user@[::1]: [user@::1] [user@::1]: do - ehost=$(echo $tuah | sed -e "s/1]:/1]/ "| tr -d "[]") + ehost=$(echo $tuah | sed -e "s/1]:/1]/" | tr -d "[]") test_expect_success "clone ssh://$tuah/home/user/repo" " test_clone_url ssh://$tuah/home/user/repo $ehost /home/user/repo " diff --git a/t/t5700-clone-reference.sh b/t/t5604-clone-reference.sh index 4320082b1b..4320082b1b 100755 --- a/t/t5700-clone-reference.sh +++ b/t/t5604-clone-reference.sh diff --git a/t/t5701-clone-local.sh b/t/t5605-clone-local.sh index 3c087e907c..3c087e907c 100755 --- a/t/t5701-clone-local.sh +++ b/t/t5605-clone-local.sh diff --git a/t/t5702-clone-options.sh b/t/t5606-clone-options.sh index 9e24ec88e6..9e24ec88e6 100755 --- a/t/t5702-clone-options.sh +++ b/t/t5606-clone-options.sh diff --git a/t/t5704-bundle.sh b/t/t5607-clone-bundle.sh index 348d9b3bc7..348d9b3bc7 100755 --- a/t/t5704-bundle.sh +++ b/t/t5607-clone-bundle.sh diff --git a/t/t5705-clone-2gb.sh b/t/t5608-clone-2gb.sh index 191d6d3a78..191d6d3a78 100755 --- a/t/t5705-clone-2gb.sh +++ b/t/t5608-clone-2gb.sh diff --git a/t/t5706-clone-branch.sh b/t/t5609-clone-branch.sh index 6e7a7be052..6e7a7be052 100755 --- a/t/t5706-clone-branch.sh +++ b/t/t5609-clone-branch.sh diff --git a/t/t5707-clone-detached.sh b/t/t5610-clone-detached.sh index 8b0d607df1..8b0d607df1 100755 --- a/t/t5707-clone-detached.sh +++ b/t/t5610-clone-detached.sh diff --git a/t/t5708-clone-config.sh b/t/t5611-clone-config.sh index 27d730c0a7..e4850b778c 100755 --- a/t/t5708-clone-config.sh +++ b/t/t5611-clone-config.sh @@ -37,4 +37,24 @@ test_expect_success 'clone -c config is available during clone' ' test_cmp expect child/file ' +# Tests for the hidden file attribute on windows +is_hidden () { + # Use the output of `attrib`, ignore the absolute path + case "$(attrib "$1")" in *H*?:*) return 0;; esac + return 1 +} + +test_expect_success MINGW 'clone -c core.hideDotFiles' ' + test_commit attributes .gitattributes "" && + rm -rf child && + git clone -c core.hideDotFiles=false . child && + ! is_hidden child/.gitattributes && + rm -rf child && + git clone -c core.hideDotFiles=dotGitOnly . child && + ! is_hidden child/.gitattributes && + rm -rf child && + git clone -c core.hideDotFiles=true . child && + is_hidden child/.gitattributes +' + test_done diff --git a/t/t5709-clone-refspec.sh b/t/t5612-clone-refspec.sh index 7ace2535c8..7ace2535c8 100755 --- a/t/t5709-clone-refspec.sh +++ b/t/t5612-clone-refspec.sh diff --git a/t/t5710-info-alternate.sh b/t/t5613-info-alternate.sh index 9cd2626dba..9cd2626dba 100755 --- a/t/t5710-info-alternate.sh +++ b/t/t5613-info-alternate.sh diff --git a/t/t5614-clone-submodules.sh b/t/t5614-clone-submodules.sh new file mode 100755 index 0000000000..62044c5a02 --- /dev/null +++ b/t/t5614-clone-submodules.sh @@ -0,0 +1,85 @@ +#!/bin/sh + +test_description='Test shallow cloning of repos with submodules' + +. ./test-lib.sh + +pwd=$(pwd) + +test_expect_success 'setup' ' + git checkout -b master && + test_commit commit1 && + test_commit commit2 && + mkdir sub && + ( + cd sub && + git init && + test_commit subcommit1 && + test_commit subcommit2 && + test_commit subcommit3 + ) && + git submodule add "file://$pwd/sub" sub && + git commit -m "add submodule" +' + +test_expect_success 'nonshallow clone implies nonshallow submodule' ' + test_when_finished "rm -rf super_clone" && + git clone --recurse-submodules "file://$pwd/." super_clone && + ( + cd super_clone && + git log --oneline >lines && + test_line_count = 3 lines + ) && + ( + cd super_clone/sub && + git log --oneline >lines && + test_line_count = 3 lines + ) +' + +test_expect_success 'shallow clone implies shallow submodule' ' + test_when_finished "rm -rf super_clone" && + git clone --recurse-submodules --depth 2 "file://$pwd/." super_clone && + ( + cd super_clone && + git log --oneline >lines && + test_line_count = 2 lines + ) && + ( + cd super_clone/sub && + git log --oneline >lines && + test_line_count = 1 lines + ) +' + +test_expect_success 'shallow clone with non shallow submodule' ' + test_when_finished "rm -rf super_clone" && + git clone --recurse-submodules --depth 2 --no-shallow-submodules "file://$pwd/." super_clone && + ( + cd super_clone && + git log --oneline >lines && + test_line_count = 2 lines + ) && + ( + cd super_clone/sub && + git log --oneline >lines && + test_line_count = 3 lines + ) +' + +test_expect_success 'non shallow clone with shallow submodule' ' + test_when_finished "rm -rf super_clone" && + git clone --recurse-submodules --no-local --shallow-submodules "file://$pwd/." super_clone && + ( + cd super_clone && + git log --oneline >lines && + test_line_count = 3 lines + ) && + ( + cd super_clone/sub && + git log --oneline >lines && + test_line_count = 1 lines + ) +' + +test_done diff --git a/t/t6009-rev-list-parent.sh b/t/t6009-rev-list-parent.sh index 66cda17ef3..20e3e2554a 100755 --- a/t/t6009-rev-list-parent.sh +++ b/t/t6009-rev-list-parent.sh @@ -47,7 +47,9 @@ test_expect_success 'setup roots, merges and octopuses' ' git checkout -b yetanotherbranch four && test_commit eight && git checkout master && - test_merge normalmerge newroot && + test_tick && + git merge --allow-unrelated-histories -m normalmerge newroot && + git tag normalmerge && test_tick && git merge -m tripus sidebranch anotherbranch && git tag tripus && diff --git a/t/t6010-merge-base.sh b/t/t6010-merge-base.sh index 39b3238da2..e0c5f44cac 100755 --- a/t/t6010-merge-base.sh +++ b/t/t6010-merge-base.sh @@ -215,11 +215,13 @@ test_expect_success 'criss-cross merge-base for octopus-step' ' git reset --hard E && test_commit CC2 && test_tick && - git merge -s ours CC1 && + # E is a root commit unrelated to MMR root on which CC1 is based + git merge -s ours --allow-unrelated-histories CC1 && test_commit CC-o && test_commit CCB && git reset --hard CC1 && - git merge -s ours CC2 && + # E is a root commit unrelated to MMR root on which CC1 is based + git merge -s ours --allow-unrelated-histories CC2 && test_commit CCA && git rev-parse CC1 CC2 >expected && diff --git a/t/t6012-rev-list-simplify.sh b/t/t6012-rev-list-simplify.sh index b89cd6b07a..2a0fbb87b1 100755 --- a/t/t6012-rev-list-simplify.sh +++ b/t/t6012-rev-list-simplify.sh @@ -71,7 +71,7 @@ test_expect_success setup ' note J && git checkout master && - test_tick && git merge -m "Coolest" unrelated && + test_tick && git merge --allow-unrelated-histories -m "Coolest" unrelated && note K && echo "Immaterial" >elif && diff --git a/t/t6024-recursive-merge.sh b/t/t6024-recursive-merge.sh index 755d30ce2a..3f59e58dfb 100755 --- a/t/t6024-recursive-merge.sh +++ b/t/t6024-recursive-merge.sh @@ -76,7 +76,7 @@ test_expect_success "result contains a conflict" "test_cmp expect a1" git ls-files --stage > out cat > expect << EOF -100644 439cc46de773d8a83c77799b7cc9191c128bfcff 1 a1 +100644 ec3fe2a791706733f2d8fa7ad45d9a9672031f5e 1 a1 100644 cf84443e49e1b366fac938711ddf4be2d4d1d9e9 2 a1 100644 fd7923529855d0b274795ae3349c5e0438333979 3 a1 EOF diff --git a/t/t6026-merge-attr.sh b/t/t6026-merge-attr.sh index 04c0509c47..ef0cbceafe 100755 --- a/t/t6026-merge-attr.sh +++ b/t/t6026-merge-attr.sh @@ -176,7 +176,8 @@ test_expect_success 'up-to-date merge without common ancestor' ' test_tick && ( cd repo1 && - git pull ../repo2 master + git fetch ../repo2 master && + git merge --allow-unrelated-histories FETCH_HEAD ) ' diff --git a/t/t6029-merge-subtree.sh b/t/t6029-merge-subtree.sh index 73fc240e85..3e692454a7 100755 --- a/t/t6029-merge-subtree.sh +++ b/t/t6029-merge-subtree.sh @@ -49,7 +49,7 @@ test_expect_success 'setup' ' test_expect_success 'initial merge' ' git remote add -f gui ../git-gui && - git merge -s ours --no-commit gui/master && + git merge -s ours --no-commit --allow-unrelated-histories gui/master && git read-tree --prefix=git-gui/ -u gui/master && git commit -m "Merge git-gui as our subdirectory" && git checkout -b work && diff --git a/t/t6036-recursive-corner-cases.sh b/t/t6036-recursive-corner-cases.sh index 9d6621c056..18aa88b5c0 100755 --- a/t/t6036-recursive-corner-cases.sh +++ b/t/t6036-recursive-corner-cases.sh @@ -212,7 +212,8 @@ test_expect_success 'git detects differently handled merges conflict' ' -L "" \ -L "Temporary merge branch 1" \ merged empty merge-me && - test $(git rev-parse :1:new_a) = $(git hash-object merged) + sed -e "s/^\([<=>]\)/\1\1\1/" merged >merged-internal && + test $(git rev-parse :1:new_a) = $(git hash-object merged-internal) ' # @@ -299,89 +300,6 @@ test_expect_success 'git detects conflict merging criss-cross+modify/delete, rev ' # -# criss-cross + modify/modify with very contrived file contents: -# -# B D -# o---o -# / \ / \ -# A o X ? F -# \ / \ / -# o---o -# C E -# -# Commit A: file with contents 'A\n' -# Commit B: file with contents 'B\n' -# Commit C: file with contents 'C\n' -# Commit D: file with contents 'D\n' -# Commit E: file with contents: -# <<<<<<< Temporary merge branch 1 -# C -# ======= -# B -# >>>>>>> Temporary merge branch 2 -# -# Now, when we merge commits D & E, does git detect the conflict? - -test_expect_success 'setup differently handled merges of content conflict' ' - git clean -fdqx && - rm -rf .git && - git init && - - echo A >file && - git add file && - test_tick && - git commit -m A && - - git branch B && - git checkout -b C && - echo C >file && - git add file && - test_tick && - git commit -m C && - - git checkout B && - echo B >file && - git add file && - test_tick && - git commit -m B && - - git checkout B^0 && - test_must_fail git merge C && - echo D >file && - git add file && - test_tick && - git commit -m D && - git tag D && - - git checkout C^0 && - test_must_fail git merge B && - cat <<EOF >file && -<<<<<<< Temporary merge branch 1 -C -======= -B ->>>>>>> Temporary merge branch 2 -EOF - git add file && - test_tick && - git commit -m E && - git tag E -' - -test_expect_failure 'git detects conflict w/ criss-cross+contrived resolution' ' - git checkout D^0 && - - test_must_fail git merge -s recursive E^0 && - - test 3 -eq $(git ls-files -s | wc -l) && - test 3 -eq $(git ls-files -u | wc -l) && - test 0 -eq $(git ls-files -o | wc -l) && - - test $(git rev-parse :2:file) = $(git rev-parse D:file) && - test $(git rev-parse :3:file) = $(git rev-parse E:file) -' - -# # criss-cross + d/f conflict via add/add: # Commit A: Neither file 'a' nor directory 'a/' exists. # Commit B: Introduce 'a' diff --git a/t/t6041-bisect-submodule.sh b/t/t6041-bisect-submodule.sh index c6b7aa6977..62b8a2e7bb 100755 --- a/t/t6041-bisect-submodule.sh +++ b/t/t6041-bisect-submodule.sh @@ -8,7 +8,7 @@ test_description='bisect can handle submodules' git_bisect () { git status -su >expect && ls -1pR * >>expect && - tar czf "$TRASH_DIRECTORY/tmp.tgz" * && + tar cf "$TRASH_DIRECTORY/tmp.tar" * && GOOD=$(git rev-parse --verify HEAD) && git checkout "$1" && echo "foo" >bar && @@ -20,7 +20,7 @@ git_bisect () { git bisect start && git bisect good $GOOD && rm -rf * && - tar xzf "$TRASH_DIRECTORY/tmp.tgz" && + tar xf "$TRASH_DIRECTORY/tmp.tar" && git status -su >actual && ls -1pR * >>actual && test_cmp expect actual && diff --git a/t/t6044-merge-unrelated-index-changes.sh b/t/t6044-merge-unrelated-index-changes.sh new file mode 100755 index 0000000000..01023486c5 --- /dev/null +++ b/t/t6044-merge-unrelated-index-changes.sh @@ -0,0 +1,153 @@ +#!/bin/sh + +test_description="merges with unrelated index changes" + +. ./test-lib.sh + +# Testcase for some simple merges +# A +# o-----o B +# \ +# \---o C +# \ +# \-o D +# \ +# o E +# Commit A: some file a +# Commit B: adds file b, modifies end of a +# Commit C: adds file c +# Commit D: adds file d, modifies beginning of a +# Commit E: renames a->subdir/a, adds subdir/e + +test_expect_success 'setup trivial merges' ' + test_seq 1 10 >a && + git add a && + test_tick && git commit -m A && + + git branch A && + git branch B && + git branch C && + git branch D && + git branch E && + + git checkout B && + echo b >b && + echo 11 >>a && + git add a b && + test_tick && git commit -m B && + + git checkout C && + echo c >c && + git add c && + test_tick && git commit -m C && + + git checkout D && + test_seq 2 10 >a && + echo d >d && + git add a d && + test_tick && git commit -m D && + + git checkout E && + mkdir subdir && + git mv a subdir/a && + echo e >subdir/e && + git add subdir && + test_tick && git commit -m E +' + +test_expect_success 'ff update' ' + git reset --hard && + git checkout A^0 && + + touch random_file && git add random_file && + + git merge E^0 && + + test_must_fail git rev-parse HEAD:random_file && + test "$(git diff --name-only --cached E)" = "random_file" +' + +test_expect_success 'ff update, important file modified' ' + git reset --hard && + git checkout A^0 && + + mkdir subdir && + touch subdir/e && + git add subdir/e && + + test_must_fail git merge E^0 +' + +test_expect_success 'resolve, trivial' ' + git reset --hard && + git checkout B^0 && + + touch random_file && git add random_file && + + test_must_fail git merge -s resolve C^0 +' + +test_expect_success 'resolve, non-trivial' ' + git reset --hard && + git checkout B^0 && + + touch random_file && git add random_file && + + test_must_fail git merge -s resolve D^0 +' + +test_expect_success 'recursive' ' + git reset --hard && + git checkout B^0 && + + touch random_file && git add random_file && + + test_must_fail git merge -s recursive C^0 +' + +test_expect_success 'octopus, unrelated file touched' ' + git reset --hard && + git checkout B^0 && + + touch random_file && git add random_file && + + test_must_fail git merge C^0 D^0 +' + +test_expect_success 'octopus, related file removed' ' + git reset --hard && + git checkout B^0 && + + git rm b && + + test_must_fail git merge C^0 D^0 +' + +test_expect_success 'octopus, related file modified' ' + git reset --hard && + git checkout B^0 && + + echo 12 >>a && git add a && + + test_must_fail git merge C^0 D^0 +' + +test_expect_success 'ours' ' + git reset --hard && + git checkout B^0 && + + touch random_file && git add random_file && + + test_must_fail git merge -s ours C^0 +' + +test_expect_success 'subtree' ' + git reset --hard && + git checkout B^0 && + + touch random_file && git add random_file && + + test_must_fail git merge -s subtree E^0 +' + +test_done diff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh index 10b1452766..1c6952d049 100755 --- a/t/t6101-rev-parse-parents.sh +++ b/t/t6101-rev-parse-parents.sh @@ -19,7 +19,7 @@ test_expect_success 'setup' ' git checkout --orphan tmp && test_commit start2 && git checkout master && - git merge -m next start2 && + git merge -m next --allow-unrelated-histories start2 && test_commit final && test_seq 40 | diff --git a/t/t6133-pathspec-rev-dwim.sh b/t/t6133-pathspec-rev-dwim.sh new file mode 100755 index 0000000000..a290ffca0d --- /dev/null +++ b/t/t6133-pathspec-rev-dwim.sh @@ -0,0 +1,48 @@ +#!/bin/sh + +test_description='test dwim of revs versus pathspecs in revision parser' +. ./test-lib.sh + +test_expect_success 'setup' ' + test_commit base && + echo content >"br[ack]ets" && + git add . && + test_tick && + git commit -m brackets +' + +test_expect_success 'non-rev wildcard dwims to pathspec' ' + git log -- "*.t" >expect && + git log "*.t" >actual && + test_cmp expect actual +' + +test_expect_success 'tree:path with metacharacters dwims to rev' ' + git show "HEAD:br[ack]ets" -- >expect && + git show "HEAD:br[ack]ets" >actual && + test_cmp expect actual +' + +test_expect_success '^{foo} with metacharacters dwims to rev' ' + git log "HEAD^{/b.*}" -- >expect && + git log "HEAD^{/b.*}" >actual && + test_cmp expect actual +' + +test_expect_success '@{foo} with metacharacters dwims to rev' ' + git log "HEAD@{now [or thereabouts]}" -- >expect && + git log "HEAD@{now [or thereabouts]}" >actual && + test_cmp expect actual +' + +test_expect_success ':/*.t from a subdir dwims to a pathspec' ' + mkdir subdir && + ( + cd subdir && + git log -- ":/*.t" >expect && + git log ":/*.t" >actual && + test_cmp expect actual + ) +' + +test_done diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh index fe4796cc9c..d0ab09f4bd 100755 --- a/t/t6302-for-each-ref-filter.sh +++ b/t/t6302-for-each-ref-filter.sh @@ -5,20 +5,27 @@ test_description='test for-each-refs usage of ref-filter APIs' . ./test-lib.sh . "$TEST_DIRECTORY"/lib-gpg.sh -if ! test_have_prereq GPG -then - skip_all="skipping for-each-ref tests, GPG not available" - test_done -fi - test_expect_success 'setup some history and refs' ' test_commit one && test_commit two && test_commit three && git checkout -b side && test_commit four && - git tag -s -m "A signed tag message" signed-tag && - git tag -s -m "Annonated doubly" double-tag signed-tag && + git tag -m "An annotated tag" annotated-tag && + git tag -m "Annonated doubly" doubly-annotated-tag annotated-tag && + + # Note that these "signed" tags might not actually be signed. + # Tests which care about the distinction should be marked + # with the GPG prereq. + if test_have_prereq GPG + then + sign=-s + else + sign= + fi && + git tag $sign -m "A signed tag" signed-tag && + git tag $sign -m "Signed doubly" doubly-signed-tag signed-tag && + git checkout master && git update-ref refs/odd/spot master ' @@ -36,6 +43,7 @@ test_expect_success 'filtering with --points-at' ' test_expect_success 'check signed tags with --points-at' ' sed -e "s/Z$//" >expect <<-\EOF && refs/heads/side Z + refs/tags/annotated-tag four refs/tags/four Z refs/tags/signed-tag four EOF @@ -58,7 +66,9 @@ test_expect_success 'filtering with --merged' ' test_expect_success 'filtering with --no-merged' ' cat >expect <<-\EOF && refs/heads/side - refs/tags/double-tag + refs/tags/annotated-tag + refs/tags/doubly-annotated-tag + refs/tags/doubly-signed-tag refs/tags/four refs/tags/signed-tag EOF @@ -71,7 +81,9 @@ test_expect_success 'filtering with --contains' ' refs/heads/master refs/heads/side refs/odd/spot - refs/tags/double-tag + refs/tags/annotated-tag + refs/tags/doubly-annotated-tag + refs/tags/doubly-signed-tag refs/tags/four refs/tags/signed-tag refs/tags/three @@ -90,7 +102,9 @@ test_expect_success 'left alignment is default' ' refname is refs/heads/master |refs/heads/master refname is refs/heads/side |refs/heads/side refname is refs/odd/spot |refs/odd/spot - refname is refs/tags/double-tag|refs/tags/double-tag + refname is refs/tags/annotated-tag|refs/tags/annotated-tag + refname is refs/tags/doubly-annotated-tag|refs/tags/doubly-annotated-tag + refname is refs/tags/doubly-signed-tag|refs/tags/doubly-signed-tag refname is refs/tags/four |refs/tags/four refname is refs/tags/one |refs/tags/one refname is refs/tags/signed-tag|refs/tags/signed-tag @@ -106,7 +120,9 @@ test_expect_success 'middle alignment' ' | refname is refs/heads/master |refs/heads/master | refname is refs/heads/side |refs/heads/side | refname is refs/odd/spot |refs/odd/spot - |refname is refs/tags/double-tag|refs/tags/double-tag + |refname is refs/tags/annotated-tag|refs/tags/annotated-tag + |refname is refs/tags/doubly-annotated-tag|refs/tags/doubly-annotated-tag + |refname is refs/tags/doubly-signed-tag|refs/tags/doubly-signed-tag | refname is refs/tags/four |refs/tags/four | refname is refs/tags/one |refs/tags/one |refname is refs/tags/signed-tag|refs/tags/signed-tag @@ -122,7 +138,9 @@ test_expect_success 'right alignment' ' | refname is refs/heads/master|refs/heads/master | refname is refs/heads/side|refs/heads/side | refname is refs/odd/spot|refs/odd/spot - |refname is refs/tags/double-tag|refs/tags/double-tag + |refname is refs/tags/annotated-tag|refs/tags/annotated-tag + |refname is refs/tags/doubly-annotated-tag|refs/tags/doubly-annotated-tag + |refname is refs/tags/doubly-signed-tag|refs/tags/doubly-signed-tag | refname is refs/tags/four|refs/tags/four | refname is refs/tags/one|refs/tags/one |refname is refs/tags/signed-tag|refs/tags/signed-tag @@ -133,6 +151,50 @@ test_expect_success 'right alignment' ' test_cmp expect actual ' +cat >expect <<-\EOF +| refname is refs/heads/master |refs/heads/master +| refname is refs/heads/side |refs/heads/side +| refname is refs/odd/spot |refs/odd/spot +| refname is refs/tags/annotated-tag |refs/tags/annotated-tag +|refname is refs/tags/doubly-annotated-tag |refs/tags/doubly-annotated-tag +| refname is refs/tags/doubly-signed-tag |refs/tags/doubly-signed-tag +| refname is refs/tags/four |refs/tags/four +| refname is refs/tags/one |refs/tags/one +| refname is refs/tags/signed-tag |refs/tags/signed-tag +| refname is refs/tags/three |refs/tags/three +| refname is refs/tags/two |refs/tags/two +EOF + +test_align_permutations() { + while read -r option + do + test_expect_success "align:$option" ' + git for-each-ref --format="|%(align:$option)refname is %(refname)%(end)|%(refname)" >actual && + test_cmp expect actual + ' + done +} + +test_align_permutations <<-\EOF + middle,42 + 42,middle + position=middle,42 + 42,position=middle + middle,width=42 + width=42,middle + position=middle,width=42 + width=42,position=middle +EOF + +# Last one wins (silently) when multiple arguments of the same type are given + +test_align_permutations <<-\EOF + 32,width=42,middle + width=30,42,middle + width=42,position=right,middle + 42,right,position=middle +EOF + # Individual atoms inside %(align:...) and %(end) must not be quoted. test_expect_success 'alignment with format quote' " @@ -140,7 +202,9 @@ test_expect_success 'alignment with format quote' " |' '\''master| A U Thor'\'' '| |' '\''side| A U Thor'\'' '| |' '\''odd/spot| A U Thor'\'' '| - |' '\''double-tag| '\'' '| + |' '\''annotated-tag| '\'' '| + |' '\''doubly-annotated-tag| '\'' '| + |' '\''doubly-signed-tag| '\'' '| |' '\''four| A U Thor'\'' '| |' '\''one| A U Thor'\'' '| |' '\''signed-tag| '\'' '| @@ -156,7 +220,9 @@ test_expect_success 'nested alignment with quote formatting' " |' master '| |' side '| |' odd/spot '| - |' double-tag '| + |' annotated-tag '| + |'doubly-annotated-tag '| + |'doubly-signed-tag '| |' four '| |' one '| |' signed-tag '| @@ -172,10 +238,12 @@ test_expect_success 'check `%(contents:lines=1)`' ' master |three side |four odd/spot |three - double-tag |Annonated doubly + annotated-tag |An annotated tag + doubly-annotated-tag |Annonated doubly + doubly-signed-tag |Signed doubly four |four one |one - signed-tag |A signed tag message + signed-tag |A signed tag three |three two |two EOF @@ -188,7 +256,9 @@ test_expect_success 'check `%(contents:lines=0)`' ' master | side | odd/spot | - double-tag | + annotated-tag | + doubly-annotated-tag | + doubly-signed-tag | four | one | signed-tag | @@ -204,10 +274,12 @@ test_expect_success 'check `%(contents:lines=99999)`' ' master |three side |four odd/spot |three - double-tag |Annonated doubly + annotated-tag |An annotated tag + doubly-annotated-tag |Annonated doubly + doubly-signed-tag |Signed doubly four |four one |one - signed-tag |A signed tag message + signed-tag |A signed tag three |three two |two EOF diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh index 51dd2b4e0e..4a2570ed95 100755 --- a/t/t7001-mv.sh +++ b/t/t7001-mv.sh @@ -102,7 +102,7 @@ test_expect_success \ test_expect_success \ 'adding another file' \ - 'cp "$TEST_DIRECTORY"/../README path0/README && + 'cp "$TEST_DIRECTORY"/../README.md path0/README && git add path0/README && git commit -m add2 -a' @@ -292,6 +292,9 @@ test_expect_success 'setup submodule' ' echo content >file && git add file && git commit -m "added sub and file" && + mkdir -p deep/directory/hierachy && + git submodule add ./. deep/directory/hierachy/sub && + git commit -m "added another submodule" && git branch submodule ' @@ -475,4 +478,17 @@ test_expect_success 'mv -k does not accidentally destroy submodules' ' git checkout . ' +test_expect_success 'moving a submodule in nested directories' ' + ( + cd deep && + git mv directory ../ && + # git status would fail if the update of linking git dir to + # work dir of the submodule failed. + git status && + git config -f ../.gitmodules submodule.deep/directory/hierachy/sub.path >../actual && + echo "directory/hierachy/sub" >../expect + ) && + test_cmp actual expect +' + test_done diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh index cf3469b142..f9b7d79af5 100755 --- a/t/t7004-tag.sh +++ b/t/t7004-tag.sh @@ -775,6 +775,47 @@ test_expect_success GPG '-s implies annotated tag' ' test_cmp expect actual ' +get_tag_header forcesignannotated-implied-sign $commit commit $time >expect +echo "A message" >>expect +echo '-----BEGIN PGP SIGNATURE-----' >>expect +test_expect_success GPG \ + 'git tag -s implied if configured with tag.forcesignannotated' \ + 'test_config tag.forcesignannotated true && + git tag -m "A message" forcesignannotated-implied-sign && + get_tag_msg forcesignannotated-implied-sign >actual && + test_cmp expect actual +' + +test_expect_success GPG \ + 'lightweight with no message when configured with tag.forcesignannotated' \ + 'test_config tag.forcesignannotated true && + git tag forcesignannotated-lightweight && + tag_exists forcesignannotated-lightweight && + test_must_fail git tag -v forcesignannotated-no-message +' + +get_tag_header forcesignannotated-annotate $commit commit $time >expect +echo "A message" >>expect +test_expect_success GPG \ + 'git tag -a disable configured tag.forcesignannotated' \ + 'test_config tag.forcesignannotated true && + git tag -a -m "A message" forcesignannotated-annotate && + get_tag_msg forcesignannotated-annotate >actual && + test_cmp expect actual && + test_must_fail git tag -v forcesignannotated-annotate +' + +get_tag_header forcesignannotated-disabled $commit commit $time >expect +echo "A message" >>expect +echo '-----BEGIN PGP SIGNATURE-----' >>expect +test_expect_success GPG \ + 'git tag --sign enable GPG sign' \ + 'test_config tag.forcesignannotated false && + git tag --sign -m "A message" forcesignannotated-disabled && + get_tag_msg forcesignannotated-disabled >actual && + test_cmp expect actual +' + test_expect_success GPG \ 'trying to create a signed tag with non-existing -F file should fail' ' ! test -f nonexistingfile && diff --git a/t/t7008-grep-binary.sh b/t/t7008-grep-binary.sh index b146406e9c..9c9c378119 100755 --- a/t/t7008-grep-binary.sh +++ b/t/t7008-grep-binary.sh @@ -141,7 +141,8 @@ test_expect_success 'grep respects not-binary diff attribute' ' test_cmp expect actual && echo "b diff" >.gitattributes && echo "b:binQary" >expect && - git grep bin b | nul_to_q >actual && + git grep bin b >actual.raw && + nul_to_q <actual.raw >actual && test_cmp expect actual ' diff --git a/t/t7030-verify-tag.sh b/t/t7030-verify-tag.sh index 4608e71343..07079a41c4 100755 --- a/t/t7030-verify-tag.sh +++ b/t/t7030-verify-tag.sh @@ -112,4 +112,17 @@ test_expect_success GPG 'verify signatures with --raw' ' ) ' +test_expect_success GPG 'verify multiple tags' ' + tags="fourth-signed sixth-signed seventh-signed" && + for i in $tags + do + git verify-tag -v --raw $i || return 1 + done >expect.stdout 2>expect.stderr.1 && + grep "^.GNUPG:." <expect.stderr.1 >expect.stderr && + git verify-tag -v --raw $tags >actual.stdout 2>actual.stderr.1 && + grep "^.GNUPG:." <actual.stderr.1 >actual.stderr && + test_cmp expect.stdout actual.stdout && + test_cmp expect.stderr actual.stderr +' + test_done diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh index 86ceb38b01..b89fd2a6ad 100755 --- a/t/t7300-clean.sh +++ b/t/t7300-clean.sh @@ -495,7 +495,7 @@ test_expect_success 'should not clean submodules' ' test_path_is_missing to_clean ' -test_expect_success POSIXPERM 'should avoid cleaning possible submodules' ' +test_expect_success POSIXPERM,SANITY 'should avoid cleaning possible submodules' ' rm -fr to_clean possible_sub1 && mkdir to_clean possible_sub1 && test_when_finished "rm -rf possible_sub*" && diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh index 540771ca41..3570f7bb8c 100755 --- a/t/t7400-submodule-basic.sh +++ b/t/t7400-submodule-basic.sh @@ -11,6 +11,10 @@ subcommands of git submodule. . ./test-lib.sh +test_expect_success 'submodule deinit works on empty repository' ' + git submodule deinit --all +' + test_expect_success 'setup - initial commit' ' >t && git add t && @@ -18,6 +22,22 @@ test_expect_success 'setup - initial commit' ' git branch initial ' +test_expect_success 'submodule init aborts on missing .gitmodules file' ' + test_when_finished "git update-index --remove sub" && + git update-index --add --cacheinfo 160000,$(git rev-parse HEAD),sub && + # missing the .gitmodules file here + test_must_fail git submodule init 2>actual && + test_i18ngrep "No url found for submodule path" actual +' + +test_expect_success 'submodule update aborts on missing .gitmodules file' ' + test_when_finished "git update-index --remove sub" && + git update-index --add --cacheinfo 160000,$(git rev-parse HEAD),sub && + # missing the .gitmodules file here + git submodule update sub 2>actual && + test_i18ngrep "Submodule path .sub. not initialized" actual +' + test_expect_success 'configuration parsing' ' test_when_finished "rm -f .gitmodules" && cat >.gitmodules <<-\EOF && @@ -462,7 +482,7 @@ test_expect_success 'update --init' ' git config --remove-section submodule.example && test_must_fail git config submodule.example.url && - git submodule update init > update.out && + git submodule update init 2> update.out && cat update.out && test_i18ngrep "not initialized" update.out && test_must_fail git rev-parse --resolve-git-dir init/.git && @@ -480,7 +500,7 @@ test_expect_success 'update --init from subdirectory' ' mkdir -p sub && ( cd sub && - git submodule update ../init >update.out && + git submodule update ../init 2>update.out && cat update.out && test_i18ngrep "not initialized" update.out && test_must_fail git rev-parse --resolve-git-dir ../init/.git && @@ -818,6 +838,47 @@ test_expect_success 'submodule add --name allows to replace a submodule with ano ) ' +test_expect_success 'recursive relative submodules stay relative' ' + test_when_finished "rm -rf super clone2 subsub sub3" && + mkdir subsub && + ( + cd subsub && + git init && + >t && + git add t && + git commit -m "initial commit" + ) && + mkdir sub3 && + ( + cd sub3 && + git init && + >t && + git add t && + git commit -m "initial commit" && + git submodule add ../subsub dirdir/subsub && + git commit -m "add submodule subsub" + ) && + mkdir super && + ( + cd super && + git init && + >t && + git add t && + git commit -m "initial commit" && + git submodule add ../sub3 && + git commit -m "add submodule sub" + ) && + git clone super clone2 && + ( + cd clone2 && + git submodule update --init --recursive && + echo "gitdir: ../.git/modules/sub3" >./sub3/.git_expect && + echo "gitdir: ../../../.git/modules/sub3/modules/dirdir/subsub" >./sub3/dirdir/subsub/.git_expect + ) && + test_cmp clone2/sub3/.git_expect clone2/sub3/.git && + test_cmp clone2/sub3/dirdir/subsub/.git_expect clone2/sub3/dirdir/subsub/.git +' + test_expect_success 'submodule add with an existing name fails unless forced' ' ( cd addtest2 && @@ -849,6 +910,20 @@ test_expect_success 'set up a second submodule' ' git commit -m "submodule example2 added" ' +test_expect_success 'submodule deinit works on repository without submodules' ' + test_when_finished "rm -rf newdirectory" && + mkdir newdirectory && + ( + cd newdirectory && + git init && + >file && + git add file && + git commit -m "repo should not be empty" && + git submodule deinit . && + git submodule deinit --all + ) +' + test_expect_success 'submodule deinit should remove the whole submodule section from .git/config' ' git config submodule.example.foo bar && git config submodule.example2.frotz nitfol && @@ -887,6 +962,19 @@ test_expect_success 'submodule deinit . deinits all initialized submodules' ' rmdir init example2 ' +test_expect_success 'submodule deinit --all deinits all initialized submodules' ' + git submodule update --init && + git config submodule.example.foo bar && + git config submodule.example2.frotz nitfol && + test_must_fail git submodule deinit && + git submodule deinit --all >actual && + test -z "$(git config --get-regexp "submodule\.example\.")" && + test -z "$(git config --get-regexp "submodule\.example2\.")" && + test_i18ngrep "Cleared directory .init" actual && + test_i18ngrep "Cleared directory .example2" actual && + rmdir init example2 +' + test_expect_success 'submodule deinit deinits a submodule when its work tree is missing or empty' ' git submodule update --init && rm -rf init example2/* example2/.git && @@ -953,6 +1041,10 @@ test_expect_success 'submodule deinit is silent when used on an uninitialized su test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual && test_i18ngrep ! "Submodule .example2. (.*) unregistered for path .example2" actual && test_i18ngrep "Cleared directory .init" actual && + git submodule deinit --all >actual && + test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual && + test_i18ngrep ! "Submodule .example2. (.*) unregistered for path .example2" actual && + test_i18ngrep "Cleared directory .init" actual && rmdir init example2 ' @@ -999,5 +1091,30 @@ test_expect_success 'submodule add clone shallow submodule' ' ) ' +test_expect_success 'submodule helper list is not confused by common prefixes' ' + mkdir -p dir1/b && + ( + cd dir1/b && + git init && + echo hi >testfile2 && + git add . && + git commit -m "test1" + ) && + mkdir -p dir2/b && + ( + cd dir2/b && + git init && + echo hello >testfile1 && + git add . && + git commit -m "test2" + ) && + git submodule add /dir1/b dir1/b && + git submodule add /dir2/b dir2/b && + git commit -m "first submodule commit" && + git submodule--helper list dir1/b |cut -c51- >actual && + echo "dir1/b" >expect && + test_cmp expect actual +' + test_done diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh index 68ea31d693..5f278799d5 100755 --- a/t/t7406-submodule-update.sh +++ b/t/t7406-submodule-update.sh @@ -63,6 +63,10 @@ test_expect_success 'setup a submodule tree' ' git submodule add ../none none && test_tick && git commit -m "none" + ) && + git clone . recursivesuper && + ( cd recursivesuper + git submodule add ../super super ) ' @@ -95,6 +99,47 @@ test_expect_success 'submodule update from subdirectory' ' ) ' +supersha1=$(git -C super rev-parse HEAD) +mergingsha1=$(git -C super/merging rev-parse HEAD) +nonesha1=$(git -C super/none rev-parse HEAD) +rebasingsha1=$(git -C super/rebasing rev-parse HEAD) +submodulesha1=$(git -C super/submodule rev-parse HEAD) +pwd=$(pwd) + +cat <<EOF >expect +Submodule path '../super': checked out '$supersha1' +Submodule path '../super/merging': checked out '$mergingsha1' +Submodule path '../super/none': checked out '$nonesha1' +Submodule path '../super/rebasing': checked out '$rebasingsha1' +Submodule path '../super/submodule': checked out '$submodulesha1' +EOF + +cat <<EOF >expect2 +Submodule 'merging' ($pwd/merging) registered for path '../super/merging' +Submodule 'none' ($pwd/none) registered for path '../super/none' +Submodule 'rebasing' ($pwd/rebasing) registered for path '../super/rebasing' +Submodule 'submodule' ($pwd/submodule) registered for path '../super/submodule' +Cloning into '$pwd/recursivesuper/super/merging'... +done. +Cloning into '$pwd/recursivesuper/super/none'... +done. +Cloning into '$pwd/recursivesuper/super/rebasing'... +done. +Cloning into '$pwd/recursivesuper/super/submodule'... +done. +EOF + +test_expect_success 'submodule update --init --recursive from subdirectory' ' + git -C recursivesuper/super reset --hard HEAD^ && + (cd recursivesuper && + mkdir tmp && + cd tmp && + git submodule update --init --recursive ../super >../../actual 2>../../actual2 + ) && + test_cmp expect actual && + test_cmp expect2 actual2 +' + apos="'"; test_expect_success 'submodule update does not fetch already present commits' ' (cd submodule && @@ -311,16 +356,59 @@ test_expect_success 'submodule update - command in .git/config' ' ) ' +cat << EOF >expect +Execution of 'false $submodulesha1' failed in submodule path 'submodule' +EOF + test_expect_success 'submodule update - command in .git/config catches failure' ' (cd super && git config submodule.submodule.update "!false" ) && (cd super/submodule && - git reset --hard HEAD^ + git reset --hard $submodulesha1^ ) && (cd super && - test_must_fail git submodule update submodule - ) + test_must_fail git submodule update submodule 2>../actual + ) && + test_cmp actual expect +' + +cat << EOF >expect +Execution of 'false $submodulesha1' failed in submodule path '../submodule' +EOF + +test_expect_success 'submodule update - command in .git/config catches failure -- subdirectory' ' + (cd super && + git config submodule.submodule.update "!false" + ) && + (cd super/submodule && + git reset --hard $submodulesha1^ + ) && + (cd super && + mkdir tmp && cd tmp && + test_must_fail git submodule update ../submodule 2>../../actual + ) && + test_cmp actual expect +' + +cat << EOF >expect +Execution of 'false $submodulesha1' failed in submodule path '../super/submodule' +Failed to recurse into submodule path '../super' +EOF + +test_expect_success 'recursive submodule update - command in .git/config catches failure -- subdirectory' ' + (cd recursivesuper && + git submodule update --remote super && + git add super && + git commit -m "update to latest to have more than one commit in submodules" + ) && + git -C recursivesuper/super config submodule.submodule.update "!false" && + git -C recursivesuper/super/submodule reset --hard $submodulesha1^ && + (cd recursivesuper && + mkdir -p tmp && cd tmp && + test_must_fail git submodule update --recursive ../super 2>../../actual + ) && + test_cmp actual expect ' test_expect_success 'submodule init does not copy command into .git/config' ' @@ -774,4 +862,31 @@ test_expect_success 'submodule update --recursive drops module name before recur test_i18ngrep "Submodule path .deeper/submodule/subsubmodule.: checked out" actual ) ' + +test_expect_success 'submodule update can be run in parallel' ' + (cd super2 && + GIT_TRACE=$(pwd)/trace.out git submodule update --jobs 7 && + grep "7 tasks" trace.out && + git config submodule.fetchJobs 8 && + GIT_TRACE=$(pwd)/trace.out git submodule update && + grep "8 tasks" trace.out && + GIT_TRACE=$(pwd)/trace.out git submodule update --jobs 9 && + grep "9 tasks" trace.out + ) +' + +test_expect_success 'git clone passes the parallel jobs config on to submodules' ' + test_when_finished "rm -rf super4" && + GIT_TRACE=$(pwd)/trace.out git clone --recurse-submodules --jobs 7 . super4 && + grep "7 tasks" trace.out && + rm -rf super4 && + git config --global submodule.fetchJobs 8 && + GIT_TRACE=$(pwd)/trace.out git clone --recurse-submodules . super4 && + grep "8 tasks" trace.out && + rm -rf super4 && + GIT_TRACE=$(pwd)/trace.out git clone --recurse-submodules --jobs 9 . super4 && + grep "9 tasks" trace.out && + rm -rf super4 +' + test_done diff --git a/t/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh index 7ca10b8606..6ba5daf42e 100755 --- a/t/t7407-submodule-foreach.sh +++ b/t/t7407-submodule-foreach.sh @@ -178,6 +178,26 @@ test_expect_success 'test messages from "foreach --recursive"' ' ' cat > expect <<EOF +Entering '../nested1' +Entering '../nested1/nested2' +Entering '../nested1/nested2/nested3' +Entering '../nested1/nested2/nested3/submodule' +Entering '../sub1' +Entering '../sub2' +Entering '../sub3' +EOF + +test_expect_success 'test messages from "foreach --recursive" from subdirectory' ' + ( + cd clone2 && + mkdir untracked && + cd untracked && + git submodule foreach --recursive >../../actual + ) && + test_i18ncmp expect actual +' + +cat > expect <<EOF nested1-nested1 nested2-nested2 nested3-nested3 @@ -242,8 +262,12 @@ test_expect_success 'test "status --recursive"' ' test_cmp expect actual ' -sed -e "/nested2 /s/.*/+$nested2sha1 nested1\/nested2 (file2~1)/;/sub[1-3]/d" < expect > expect2 -mv -f expect2 expect +cat > expect <<EOF + $nested1sha1 nested1 (heads/master) ++$nested2sha1 nested1/nested2 (file2~1) + $nested3sha1 nested1/nested2/nested3 (heads/master) + $submodulesha1 nested1/nested2/nested3/submodule (heads/master) +EOF test_expect_success 'ensure "status --cached --recursive" preserves the --cached flag' ' ( @@ -257,6 +281,27 @@ test_expect_success 'ensure "status --cached --recursive" preserves the --cached test_cmp expect actual ' +nested2sha1=$(git -C clone3/nested1/nested2 rev-parse HEAD) + +cat > expect <<EOF + $nested1sha1 ../nested1 (heads/master) ++$nested2sha1 ../nested1/nested2 (file2) + $nested3sha1 ../nested1/nested2/nested3 (heads/master) + $submodulesha1 ../nested1/nested2/nested3/submodule (heads/master) + $sub1sha1 ../sub1 ($sub1sha1_short) + $sub2sha1 ../sub2 ($sub2sha1_short) + $sub3sha1 ../sub3 (heads/master) +EOF + +test_expect_success 'test "status --recursive" from sub directory' ' + ( + cd clone3 && + mkdir tmp && cd tmp && + git submodule status --recursive > ../../actual + ) && + test_cmp expect actual +' + test_expect_success 'use "git clone --recursive" to checkout all submodules' ' git clone --recursive super clone4 && ( diff --git a/t/t7409-submodule-detached-worktree.sh b/t/t7409-submodule-detached-work-tree.sh index c20717181e..c20717181e 100755 --- a/t/t7409-submodule-detached-worktree.sh +++ b/t/t7409-submodule-detached-work-tree.sh diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh index 63e04277f9..d84897a67a 100755 --- a/t/t7501-commit.sh +++ b/t/t7501-commit.sh @@ -200,6 +200,26 @@ test_expect_success '--amend --edit of empty message' ' test_cmp expect msg ' +test_expect_success '--amend to set message to empty' ' + echo bata >file && + git add file && + git commit -m "unamended" && + git commit --amend --allow-empty-message -m "" && + git diff-tree -s --format=%s HEAD >msg && + echo "" >expect && + test_cmp expect msg +' + +test_expect_success '--amend to set empty message needs --allow-empty-message' ' + echo conga >file && + git add file && + git commit -m "unamended" && + test_must_fail git commit --amend -m "" && + git diff-tree -s --format=%s HEAD >msg && + echo "unamended" >expect && + test_cmp expect msg +' + test_expect_success '-m --edit' ' echo amended >expect && git commit --allow-empty -m buffer && @@ -587,4 +607,24 @@ test_expect_success '--only works on to-be-born branch' ' test_cmp expected actual ' +test_expect_success '--dry-run with conflicts fixed from a merge' ' + # setup two branches with conflicting information + # in the same file, resolve the conflict, + # call commit with --dry-run + echo "Initial contents, unimportant" >test-file && + git add test-file && + git commit -m "Initial commit" && + echo "commit-1-state" >test-file && + git commit -m "commit 1" -i test-file && + git tag commit-1 && + git checkout -b branch-2 HEAD^1 && + echo "commit-2-state" >test-file && + git commit -m "commit 2" -i test-file && + ! $(git merge --no-commit commit-1) && + echo "commit-2-state" >test-file && + git add test-file && + git commit --dry-run && + git commit -m "conflicts fixed from merge." +' + test_done diff --git a/t/t7502-commit.sh b/t/t7502-commit.sh index b39e313ac2..725687d5d5 100755 --- a/t/t7502-commit.sh +++ b/t/t7502-commit.sh @@ -527,11 +527,6 @@ try_commit_status_combo () { test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG ' - test_expect_success 'commit' ' - try_commit "" && - test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG - ' - test_expect_success 'commit --status' ' try_commit --status && test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG diff --git a/t/t7507-commit-verbose.sh b/t/t7507-commit-verbose.sh index 2ddf28c984..ed2653d46f 100755 --- a/t/t7507-commit-verbose.sh +++ b/t/t7507-commit-verbose.sh @@ -3,11 +3,10 @@ test_description='verbose commit template' . ./test-lib.sh -cat >check-for-diff <<EOF -#!$SHELL_PATH -exec grep '^diff --git' "\$1" +write_script "check-for-diff" <<\EOF && +grep '^diff --git' "$1" >out +exit 0 EOF -chmod +x check-for-diff test_set_editor "$PWD/check-for-diff" cat >message <<'EOF' @@ -23,7 +22,8 @@ test_expect_success 'setup' ' ' test_expect_success 'initial commit shows verbose diff' ' - git commit --amend -v + git commit --amend -v && + test_line_count = 1 out ' test_expect_success 'second commit' ' @@ -39,13 +39,15 @@ check_message() { test_expect_success 'verbose diff is stripped out' ' git commit --amend -v && - check_message message + check_message message && + test_line_count = 1 out ' test_expect_success 'verbose diff is stripped out (mnemonicprefix)' ' git config diff.mnemonicprefix true && git commit --amend -v && - check_message message + check_message message && + test_line_count = 1 out ' cat >diff <<'EOF' @@ -96,4 +98,60 @@ test_expect_success 'verbose diff is stripped out with set core.commentChar' ' test_i18ngrep "Aborting commit due to empty commit message." err ' +test_expect_success 'status does not verbose without --verbose' ' + git status >actual && + ! grep "^diff --git" actual +' + +test_expect_success 'setup -v -v' ' + echo dirty >file +' + +for i in true 1 +do + test_expect_success "commit.verbose=$i and --verbose omitted" " + git -c commit.verbose=$i commit --amend && + test_line_count = 1 out + " +done + +for i in false -2 -1 0 +do + test_expect_success "commit.verbose=$i and --verbose omitted" " + git -c commit.verbose=$i commit --amend && + test_line_count = 0 out + " +done + +for i in 2 3 +do + test_expect_success "commit.verbose=$i and --verbose omitted" " + git -c commit.verbose=$i commit --amend && + test_line_count = 2 out + " +done + +for i in true false -2 -1 0 1 2 3 +do + test_expect_success "commit.verbose=$i and --verbose" " + git -c commit.verbose=$i commit --amend --verbose && + test_line_count = 1 out + " + + test_expect_success "commit.verbose=$i and --no-verbose" " + git -c commit.verbose=$i commit --amend --no-verbose && + test_line_count = 0 out + " + + test_expect_success "commit.verbose=$i and -v -v" " + git -c commit.verbose=$i commit --amend -v -v && + test_line_count = 2 out + " +done + +test_expect_success "status ignores commit.verbose=true" ' + git -c commit.verbose=true status >actual && + ! grep "^diff --git actual" +' + test_done diff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh index 18e5cf0663..4177a8609a 100755 --- a/t/t7510-signed-commit.sh +++ b/t/t7510-signed-commit.sh @@ -45,12 +45,18 @@ test_expect_success GPG 'create signed commits' ' git tag seventh-signed && echo 8 >file && test_tick && git commit -a -m eighth -SB7227189 && - git tag eighth-signed-alt + git tag eighth-signed-alt && + + # commit.gpgsign is still on but this must not be signed + git tag ninth-unsigned $(echo 9 | git commit-tree HEAD^{tree}) && + # explicit -S of course must sign. + git tag tenth-signed $(echo 9 | git commit-tree -S HEAD^{tree}) ' test_expect_success GPG 'verify and show signatures' ' ( - for commit in initial second merge fourth-signed fifth-signed sixth-signed seventh-signed + for commit in initial second merge fourth-signed \ + fifth-signed sixth-signed seventh-signed tenth-signed do git verify-commit $commit && git show --pretty=short --show-signature $commit >actual && @@ -60,7 +66,8 @@ test_expect_success GPG 'verify and show signatures' ' done ) && ( - for commit in merge^2 fourth-unsigned sixth-unsigned seventh-unsigned + for commit in merge^2 fourth-unsigned sixth-unsigned \ + seventh-unsigned ninth-unsigned do test_must_fail git verify-commit $commit && git show --pretty=short --show-signature $commit >actual && diff --git a/t/t7517-per-repo-email.sh b/t/t7517-per-repo-email.sh new file mode 100755 index 0000000000..337e6e30c3 --- /dev/null +++ b/t/t7517-per-repo-email.sh @@ -0,0 +1,39 @@ +#!/bin/sh +# +# Copyright (c) 2016 Dan Aloni +# Copyright (c) 2016 Jeff King +# + +test_description='per-repo forced setting of email address' + +. ./test-lib.sh + +test_expect_success 'setup a likely user.useConfigOnly use case' ' + # we want to make sure a reflog is written, since that needs + # a non-strict ident. So be sure we have an actual commit. + test_commit foo && + + sane_unset GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL && + sane_unset GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL && + git config user.name "test" && + git config --global user.useConfigOnly true +' + +test_expect_success 'fails committing if clone email is not set' ' + test_must_fail git commit --allow-empty -m msg +' + +test_expect_success 'fails committing if clone email is not set, but EMAIL set' ' + test_must_fail env EMAIL=test@fail.com git commit --allow-empty -m msg +' + +test_expect_success 'succeeds committing if clone email is set' ' + test_config user.email "test@ok.com" && + git commit --allow-empty -m msg +' + +test_expect_success 'succeeds cloning if global email is not set' ' + git clone . clone +' + +test_done diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh index 302e238263..85248a14b6 100755 --- a/t/t7600-merge.sh +++ b/t/t7600-merge.sh @@ -33,9 +33,11 @@ printf '%s\n' 1 2 3 4 5 6 7 8 9 >file printf '%s\n' '1 X' 2 3 4 5 6 7 8 9 >file.1 printf '%s\n' 1 2 3 4 '5 X' 6 7 8 9 >file.5 printf '%s\n' 1 2 3 4 5 6 7 8 '9 X' >file.9 +printf '%s\n' 1 2 3 4 5 6 7 8 '9 Y' >file.9y printf '%s\n' '1 X' 2 3 4 5 6 7 8 9 >result.1 printf '%s\n' '1 X' 2 3 4 '5 X' 6 7 8 9 >result.1-5 printf '%s\n' '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9 +printf '%s\n' 1 2 3 4 5 6 7 8 '9 Z' >result.9z >empty create_merge_msgs () { @@ -128,6 +130,12 @@ test_expect_success 'setup' ' git tag c2 && c2=$(git rev-parse HEAD) && git reset --hard "$c0" && + cp file.9y file && + git add file && + test_tick && + git commit -m "commit 7" && + git tag c7 && + git reset --hard "$c0" && cp file.9 file && git add file && test_tick && @@ -218,6 +226,26 @@ test_expect_success 'merge c1 with c2' ' verify_parents $c1 $c2 ' +test_expect_success 'merge --squash c3 with c7' ' + git reset --hard c3 && + test_must_fail git merge --squash c7 && + cat result.9z >file && + git commit --no-edit -a && + + { + cat <<-EOF + Squashed commit of the following: + + $(git show -s c7) + + # Conflicts: + # file + EOF + } >expect && + git cat-file commit HEAD | sed -e '1,/^$/d' >actual && + test_cmp expect actual +' + test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'merge c1 with c2 and c3' ' @@ -725,4 +753,14 @@ test_expect_success 'merge detects mod-256 conflicts (resolve)' ' test_must_fail git merge -s resolve master ' +test_expect_success 'merge nothing into void' ' + git init void && + ( + cd void && + git remote add up .. && + git fetch up && + test_must_fail git merge FETCH_HEAD + ) +' + test_done diff --git a/t/t7605-merge-resolve.sh b/t/t7605-merge-resolve.sh index 0cb9d11f21..5d56c38546 100755 --- a/t/t7605-merge-resolve.sh +++ b/t/t7605-merge-resolve.sh @@ -27,7 +27,7 @@ test_expect_success 'setup' ' git tag c3 ' -test_expect_success 'merge c1 to c2' ' +merge_c1_to_c2_cmds=' git reset --hard c1 && git merge -s resolve c2 && test "$(git rev-parse c1)" != "$(git rev-parse HEAD)" && @@ -41,6 +41,10 @@ test_expect_success 'merge c1 to c2' ' test 3 = $(git ls-files | wc -l) ' +test_expect_success 'merge c1 to c2' "$merge_c1_to_c2_cmds" + +test_expect_success 'merge c1 to c2, again' "$merge_c1_to_c2_cmds" + test_expect_success 'merge c2 to c3 (fails)' ' git reset --hard c2 && test_must_fail git merge -s resolve c3 diff --git a/t/t7609-merge-co-error-msgs.sh b/t/t7609-merge-co-error-msgs.sh index 0e4a682c64..6729cb379f 100755 --- a/t/t7609-merge-co-error-msgs.sh +++ b/t/t7609-merge-co-error-msgs.sh @@ -37,14 +37,14 @@ EOF test_expect_success 'untracked files overwritten by merge (fast and non-fast forward)' ' test_must_fail git merge branch 2>out && - test_cmp out expect && + test_i18ncmp out expect && git commit --allow-empty -m empty && ( GIT_MERGE_VERBOSITY=0 && export GIT_MERGE_VERBOSITY && test_must_fail git merge branch 2>out2 ) && - test_cmp out2 expect && + test_i18ncmp out2 expect && git reset --hard HEAD^ ' @@ -53,7 +53,7 @@ error: Your local changes to the following files would be overwritten by merge: four three two -Please, commit your changes or stash them before you can merge. +Please commit your changes or stash them before you can merge. error: The following untracked working tree files would be overwritten by merge: five Please move or remove them before you can merge. @@ -65,14 +65,14 @@ test_expect_success 'untracked files or local changes ovewritten by merge' ' git add three && git add four && test_must_fail git merge branch 2>out && - test_cmp out expect + test_i18ncmp out expect ' cat >expect <<\EOF error: Your local changes to the following files would be overwritten by checkout: rep/one rep/two -Please, commit your changes or stash them before you can switch branches. +Please commit your changes or stash them before you can switch branches. Aborting EOF @@ -87,21 +87,21 @@ test_expect_success 'cannot switch branches because of local changes' ' echo uno >rep/one && echo dos >rep/two && test_must_fail git checkout branch 2>out && - test_cmp out expect + test_i18ncmp out expect ' cat >expect <<\EOF error: Your local changes to the following files would be overwritten by checkout: rep/one rep/two -Please, commit your changes or stash them before you can switch branches. +Please commit your changes or stash them before you can switch branches. Aborting EOF test_expect_success 'not uptodate file porcelain checkout error' ' git add rep/one rep/two && test_must_fail git checkout branch 2>out && - test_cmp out expect + test_i18ncmp out expect ' cat >expect <<\EOF @@ -132,7 +132,7 @@ test_expect_success 'not_uptodate_dir porcelain checkout error' ' >rep/untracked-file && >rep2/untracked-file && test_must_fail git checkout branch 2>out && - test_cmp out ../expect + test_i18ncmp out ../expect ' test_done diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh index 6f12b235b3..76306cf268 100755 --- a/t/t7610-mergetool.sh +++ b/t/t7610-mergetool.sh @@ -243,6 +243,70 @@ test_expect_success 'mergetool takes partial path' ' git reset --hard ' +test_expect_success 'mergetool delete/delete conflict' ' + git checkout -b delete-base branch1 && + mkdir -p a/a && + (echo one; echo two; echo 3; echo 4) >a/a/file.txt && + git add a/a/file.txt && + git commit -m"base file" && + git checkout -b move-to-b delete-base && + mkdir -p b/b && + git mv a/a/file.txt b/b/file.txt && + (echo one; echo two; echo 4) >b/b/file.txt && + git commit -a -m"move to b" && + git checkout -b move-to-c delete-base && + mkdir -p c/c && + git mv a/a/file.txt c/c/file.txt && + (echo one; echo two; echo 3) >c/c/file.txt && + git commit -a -m"move to c" && + test_must_fail git merge move-to-b && + echo d | git mergetool a/a/file.txt && + ! test -f a/a/file.txt && + git reset --hard HEAD && + test_must_fail git merge move-to-b && + echo m | git mergetool a/a/file.txt && + test -f b/b/file.txt && + git reset --hard HEAD && + test_must_fail git merge move-to-b && + ! echo a | git mergetool a/a/file.txt && + ! test -f a/a/file.txt && + git reset --hard HEAD +' + +test_expect_success 'mergetool produces no errors when keepBackup is used' ' + test_config mergetool.keepBackup true && + test_must_fail git merge move-to-b && + : >expect && + echo d | git mergetool a/a/file.txt 2>actual && + test_cmp expect actual && + ! test -d a && + git reset --hard HEAD +' + +test_expect_success 'mergetool honors tempfile config for deleted files' ' + test_config mergetool.keepTemporaries false && + test_must_fail git merge move-to-b && + echo d | git mergetool a/a/file.txt && + ! test -d a && + git reset --hard HEAD +' + +test_expect_success 'mergetool keeps tempfiles when aborting delete/delete' ' + test_config mergetool.keepTemporaries true && + test_must_fail git merge move-to-b && + ! (echo a; echo n) | git mergetool a/a/file.txt && + test -d a/a && + cat >expect <<-\EOF && + file_BASE_.txt + file_LOCAL_.txt + file_REMOTE_.txt + EOF + ls -1 a/a | sed -e "s/[0-9]*//g" >actual && + test_cmp expect actual && + git clean -fdx && + git reset --hard HEAD +' + test_expect_success 'deleted vs modified submodule' ' git checkout -b test6 branch1 && git submodule update -N && diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh index 4e713f7aa5..7ce4cd753e 100755 --- a/t/t7800-difftool.sh +++ b/t/t7800-difftool.sh @@ -20,7 +20,7 @@ difftool_test_setup () prompt_given () { prompt="$1" - test "$prompt" = "Launch 'test-tool' [Y/n]: branch" + test "$prompt" = "Launch 'test-tool' [Y/n]? branch" } # Create a file on master and change it on branch @@ -419,6 +419,29 @@ run_dir_diff_test 'difftool --dir-diff when worktree file is missing' ' grep file2 output ' +run_dir_diff_test 'difftool --dir-diff with unmerged files' ' + test_when_finished git reset --hard && + test_config difftool.echo.cmd "echo ok" && + git checkout -B conflict-a && + git checkout -B conflict-b && + git checkout conflict-a && + echo a >>file && + git add file && + git commit -m conflict-a && + git checkout conflict-b && + echo b >>file && + git add file && + git commit -m conflict-b && + git checkout master && + git merge conflict-a && + test_must_fail git merge conflict-b && + cat >expect <<-EOF && + ok + EOF + git difftool --dir-diff $symlinks -t echo >actual && + test_cmp expect actual +' + write_script .git/CHECK_SYMLINKS <<\EOF for f in file file2 sub/sub do diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh index b540944408..1e72971a16 100755 --- a/t/t7810-grep.sh +++ b/t/t7810-grep.sh @@ -905,6 +905,33 @@ test_expect_success 'inside git repository but with --no-index' ' ) ' +test_expect_success 'grep --no-index descends into repos, but not .git' ' + rm -fr non && + mkdir -p non/git && + ( + GIT_CEILING_DIRECTORIES="$(pwd)/non" && + export GIT_CEILING_DIRECTORIES && + cd non/git && + + echo magic >file && + git init repo && + ( + cd repo && + echo magic >file && + git add file && + git commit -m foo && + echo magic >.git/file + ) && + + cat >expect <<-\EOF && + file + repo/file + EOF + git grep -l --no-index magic >actual && + test_cmp expect actual + ) +' + test_expect_success 'setup double-dash tests' ' cat >double-dash <<EOF && -- diff --git a/t/t8003-blame-corner-cases.sh b/t/t8003-blame-corner-cases.sh index 6568429753..a9b266f0d3 100755 --- a/t/t8003-blame-corner-cases.sh +++ b/t/t8003-blame-corner-cases.sh @@ -212,4 +212,18 @@ test_expect_success 'blame file with CRLF attributes text' ' grep "A U Thor" actual ' +test_expect_success 'blame file with CRLF core.autocrlf=true' ' + git config core.autocrlf false && + printf "testcase\r\n" >crlfinrepo && + >.gitattributes && + git add crlfinrepo && + git commit -m "add crlfinrepo" && + git config core.autocrlf true && + mv crlfinrepo tmp && + git checkout crlfinrepo && + rm tmp && + git blame crlfinrepo >actual && + grep "A U Thor" actual +' + test_done diff --git a/t/t8005-blame-i18n.sh b/t/t8005-blame-i18n.sh index 847d098c09..75da219ed1 100755 --- a/t/t8005-blame-i18n.sh +++ b/t/t8005-blame-i18n.sh @@ -33,11 +33,15 @@ author $SJIS_NAME summary $SJIS_MSG EOF +filter_author_summary () { + sed -n -e '/^author /p' -e '/^summary /p' "$@" +} + test_expect_success !MINGW \ 'blame respects i18n.commitencoding' ' - git blame --incremental file | \ - egrep "^(author|summary) " > actual && - test_cmp actual expected + git blame --incremental file >output && + filter_author_summary output >actual && + test_cmp expected actual ' cat >expected <<EOF @@ -52,9 +56,9 @@ EOF test_expect_success !MINGW \ 'blame respects i18n.logoutputencoding' ' git config i18n.logoutputencoding eucJP && - git blame --incremental file | \ - egrep "^(author|summary) " > actual && - test_cmp actual expected + git blame --incremental file >output && + filter_author_summary output >actual && + test_cmp expected actual ' cat >expected <<EOF @@ -68,9 +72,9 @@ EOF test_expect_success !MINGW \ 'blame respects --encoding=UTF-8' ' - git blame --incremental --encoding=UTF-8 file | \ - egrep "^(author|summary) " > actual && - test_cmp actual expected + git blame --incremental --encoding=UTF-8 file >output && + filter_author_summary output >actual && + test_cmp expected actual ' cat >expected <<EOF @@ -84,9 +88,9 @@ EOF test_expect_success !MINGW \ 'blame respects --encoding=none' ' - git blame --incremental --encoding=none file | \ - egrep "^(author|summary) " > actual && - test_cmp actual expected + git blame --incremental --encoding=none file >output && + filter_author_summary output >actual && + test_cmp expected actual ' test_done diff --git a/t/t9003-help-autocorrect.sh b/t/t9003-help-autocorrect.sh new file mode 100755 index 0000000000..dfe95c923b --- /dev/null +++ b/t/t9003-help-autocorrect.sh @@ -0,0 +1,52 @@ +#!/bin/sh + +test_description='help.autocorrect finding a match' +. ./test-lib.sh + +test_expect_success 'setup' ' + # An alias + git config alias.lgf "log --format=%s --first-parent" && + + # A random user-defined command + write_script git-distimdistim <<-EOF && + echo distimdistim was called + EOF + + PATH="$PATH:." && + export PATH && + + git commit --allow-empty -m "a single log entry" && + + # Sanity check + git lgf >actual && + echo "a single log entry" >expect && + test_cmp expect actual && + + git distimdistim >actual && + echo "distimdistim was called" >expect && + test_cmp expect actual +' + +test_expect_success 'autocorrect showing candidates' ' + git config help.autocorrect 0 && + + test_must_fail git lfg 2>actual && + sed -e "1,/^Did you mean this/d" actual | grep lgf && + + test_must_fail git distimdist 2>actual && + sed -e "1,/^Did you mean this/d" actual | grep distimdistim +' + +test_expect_success 'autocorrect running commands' ' + git config help.autocorrect -1 && + + git lfg >actual && + echo "a single log entry" >expect && + test_cmp expect actual && + + git distimdist >actual && + echo "distimdistim was called" >expect && + test_cmp expect actual +' + +test_done diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh index 56acc1e70e..28082b134f 100755 --- a/t/t9100-git-svn-basic.sh +++ b/t/t9100-git-svn-basic.sh @@ -30,7 +30,8 @@ test_expect_success \ echo "deep dir" >dir/a/b/c/d/e/file && mkdir bar && echo "zzz" >bar/zzz && - write_script exec.sh </dev/null && + echo "#!/bin/sh" >exec.sh && + chmod +x exec.sh && svn_cmd import -m "import for git svn" . "$svnrepo" >/dev/null ) && rm -rf import && @@ -44,13 +45,13 @@ test_expect_success "checkout from svn" 'svn co "$svnrepo" "$SVN_TREE"' name='try a deep --rmdir with a commit' test_expect_success "$name" ' - git checkout -f -b mybranch ${remotes_git_svn} && + git checkout -f -b mybranch remotes/git-svn && mv dir/a/b/c/d/e/file dir/file && cp dir/file file && git update-index --add --remove dir/a/b/c/d/e/file dir/file file && git commit -m "$name" && git svn set-tree --find-copies-harder --rmdir \ - ${remotes_git_svn}..mybranch && + remotes/git-svn..mybranch && svn_cmd up "$SVN_TREE" && test -d "$SVN_TREE"/dir && test ! -d "$SVN_TREE"/dir/a' @@ -64,14 +65,14 @@ test_expect_success "$name" " git update-index --add dir/file/file && git commit -m '$name' && test_must_fail git svn set-tree --find-copies-harder --rmdir \ - ${remotes_git_svn}..mybranch + remotes/git-svn..mybranch " name='detect node change from directory to file #1' test_expect_success "$name" ' rm -rf dir "$GIT_DIR"/index && - git checkout -f -b mybranch2 ${remotes_git_svn} && + git checkout -f -b mybranch2 remotes/git-svn && mv bar/zzz zzz && rm -rf bar && mv zzz bar && @@ -79,14 +80,14 @@ test_expect_success "$name" ' git update-index --add -- bar && git commit -m "$name" && test_must_fail git svn set-tree --find-copies-harder --rmdir \ - ${remotes_git_svn}..mybranch2 + remotes/git-svn..mybranch2 ' name='detect node change from file to directory #2' test_expect_success "$name" ' rm -f "$GIT_DIR"/index && - git checkout -f -b mybranch3 ${remotes_git_svn} && + git checkout -f -b mybranch3 remotes/git-svn && rm bar/zzz && git update-index --remove bar/zzz && mkdir bar/zzz && @@ -94,7 +95,7 @@ test_expect_success "$name" ' git update-index --add bar/zzz/yyy && git commit -m "$name" && git svn set-tree --find-copies-harder --rmdir \ - ${remotes_git_svn}..mybranch3 && + remotes/git-svn..mybranch3 && svn_cmd up "$SVN_TREE" && test -d "$SVN_TREE"/bar/zzz && test -e "$SVN_TREE"/bar/zzz/yyy @@ -103,7 +104,7 @@ test_expect_success "$name" ' name='detect node change from directory to file #2' test_expect_success "$name" ' rm -f "$GIT_DIR"/index && - git checkout -f -b mybranch4 ${remotes_git_svn} && + git checkout -f -b mybranch4 remotes/git-svn && rm -rf dir && git update-index --remove -- dir/file && touch dir && @@ -111,19 +112,19 @@ test_expect_success "$name" ' git update-index --add -- dir && git commit -m "$name" && test_must_fail git svn set-tree --find-copies-harder --rmdir \ - ${remotes_git_svn}..mybranch4 + remotes/git-svn..mybranch4 ' name='remove executable bit from a file' test_expect_success POSIXPERM "$name" ' rm -f "$GIT_DIR"/index && - git checkout -f -b mybranch5 ${remotes_git_svn} && + git checkout -f -b mybranch5 remotes/git-svn && chmod -x exec.sh && git update-index exec.sh && git commit -m "$name" && git svn set-tree --find-copies-harder --rmdir \ - ${remotes_git_svn}..mybranch5 && + remotes/git-svn..mybranch5 && svn_cmd up "$SVN_TREE" && test ! -x "$SVN_TREE"/exec.sh' @@ -134,7 +135,7 @@ test_expect_success POSIXPERM "$name" ' git update-index exec.sh && git commit -m "$name" && git svn set-tree --find-copies-harder --rmdir \ - ${remotes_git_svn}..mybranch5 && + remotes/git-svn..mybranch5 && svn_cmd up "$SVN_TREE" && test -x "$SVN_TREE"/exec.sh' @@ -146,7 +147,7 @@ test_expect_success SYMLINKS "$name" ' git update-index exec.sh && git commit -m "$name" && git svn set-tree --find-copies-harder --rmdir \ - ${remotes_git_svn}..mybranch5 && + remotes/git-svn..mybranch5 && svn_cmd up "$SVN_TREE" && test -h "$SVN_TREE"/exec.sh' @@ -158,7 +159,7 @@ test_expect_success POSIXPERM,SYMLINKS "$name" ' git update-index --add file exec-2.sh && git commit -m "$name" && git svn set-tree --find-copies-harder --rmdir \ - ${remotes_git_svn}..mybranch5 && + remotes/git-svn..mybranch5 && svn_cmd up "$SVN_TREE" && test -x "$SVN_TREE"/file && test -h "$SVN_TREE"/exec-2.sh' @@ -171,7 +172,7 @@ test_expect_success POSIXPERM,SYMLINKS "$name" ' git update-index exec-2.sh && git commit -m "$name" && git svn set-tree --find-copies-harder --rmdir \ - ${remotes_git_svn}..mybranch5 && + remotes/git-svn..mybranch5 && svn_cmd up "$SVN_TREE" && test -f "$SVN_TREE"/exec-2.sh && test ! -h "$SVN_TREE"/exec-2.sh && @@ -193,7 +194,7 @@ GIT_SVN_ID=alt export GIT_SVN_ID test_expect_success "$name" \ 'git svn init "$svnrepo" && git svn fetch && - git rev-list --pretty=raw ${remotes_git_svn} | grep ^tree | uniq > a && + git rev-list --pretty=raw remotes/git-svn | grep ^tree | uniq > a && git rev-list --pretty=raw remotes/alt | grep ^tree | uniq > b && test_cmp a b' @@ -216,17 +217,17 @@ EOF test_expect_success POSIXPERM,SYMLINKS "$name" "test_cmp a expected" -test_expect_success 'exit if remote refs are ambigious' " +test_expect_success 'exit if remote refs are ambigious' ' git config --add svn-remote.svn.fetch \ - bar:refs/${remotes_git_svn} && + bar:refs/remotes/git-svn && test_must_fail git svn migrate -" +' test_expect_success 'exit if init-ing a would clobber a URL' ' svnadmin create "${PWD}/svnrepo2" && svn mkdir -m "mkdir bar" "${svnrepo}2/bar" && git config --unset svn-remote.svn.fetch \ - "^bar:refs/${remotes_git_svn}$" && + "^bar:refs/remotes/git-svn$" && test_must_fail git svn init "${svnrepo}2/bar" ' @@ -236,7 +237,7 @@ test_expect_success \ git config --get svn-remote.svn.fetch \ "^bar:refs/remotes/bar$" && git config --get svn-remote.svn.fetch \ - "^:refs/${remotes_git_svn}$" + "^:refs/remotes/git-svn$" ' test_expect_success 'dcommit $rev does not clobber current branch' ' @@ -258,26 +259,26 @@ test_expect_success 'dcommit $rev does not clobber current branch' ' git branch -D my-bar ' -test_expect_success 'able to dcommit to a subdirectory' " +test_expect_success 'able to dcommit to a subdirectory' ' git svn fetch -i bar && git checkout -b my-bar refs/remotes/bar && echo abc > d && git update-index --add d && - git commit -m '/bar/d should be in the log' && + git commit -m "/bar/d should be in the log" && git svn dcommit -i bar && - test -z \"\$(git diff refs/heads/my-bar refs/remotes/bar)\" && + test -z "$(git diff refs/heads/my-bar refs/remotes/bar)" && mkdir newdir && echo new > newdir/dir && git update-index --add newdir/dir && - git commit -m 'add a new directory' && + git commit -m "add a new directory" && git svn dcommit -i bar && - test -z \"\$(git diff refs/heads/my-bar refs/remotes/bar)\" && + test -z "$(git diff refs/heads/my-bar refs/remotes/bar)" && echo foo >> newdir/dir && git update-index newdir/dir && - git commit -m 'modify a file in new directory' && + git commit -m "modify a file in new directory" && git svn dcommit -i bar && - test -z \"\$(git diff refs/heads/my-bar refs/remotes/bar)\" - " + test -z "$(git diff refs/heads/my-bar refs/remotes/bar)" +' test_expect_success 'dcommit should not fail with a touched file' ' test_commit "commit-new-file-foo2" foo2 && @@ -290,13 +291,13 @@ test_expect_success 'rebase should not fail with a touched file' ' git svn rebase ' -test_expect_success 'able to set-tree to a subdirectory' " +test_expect_success 'able to set-tree to a subdirectory' ' echo cba > d && git update-index d && - git commit -m 'update /bar/d' && + git commit -m "update /bar/d" && git svn set-tree -i bar HEAD && - test -z \"\$(git diff refs/heads/my-bar refs/remotes/bar)\" - " + test -z "$(git diff refs/heads/my-bar refs/remotes/bar)" +' test_expect_success 'git-svn works in a bare repository' ' mkdir bare-repo && diff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh index e8173d5fef..07bfb63777 100755 --- a/t/t9101-git-svn-props.sh +++ b/t/t9101-git-svn-props.sh @@ -73,11 +73,11 @@ test_expect_success 'fetch revisions from svn' 'git svn fetch' name='test svn:keywords ignoring' test_expect_success "$name" \ - 'git checkout -b mybranch ${remotes_git_svn} && + 'git checkout -b mybranch remotes/git-svn && echo Hi again >> kw.c && git commit -a -m "test keywords ignoring" && - git svn set-tree ${remotes_git_svn}..mybranch && - git pull . ${remotes_git_svn}' + git svn set-tree remotes/git-svn..mybranch && + git pull . remotes/git-svn' expect='/* $Id$ */' got="$(sed -ne 2p kw.c)" @@ -95,7 +95,7 @@ test_expect_success "propset CR on crlf files" ' test_expect_success 'fetch and pull latest from svn and checkout a new wc' \ 'git svn fetch && - git pull . ${remotes_git_svn} && + git pull . remotes/git-svn && svn_cmd co "$svnrepo" new_wc' for i in crlf ne_crlf lf ne_lf cr ne_cr empty_cr empty_lf empty empty_crlf @@ -117,7 +117,7 @@ cd test_wc svn_cmd commit -m "propset CRLF on cr files"' cd .. test_expect_success 'fetch and pull latest from svn' \ - 'git svn fetch && git pull . ${remotes_git_svn}' + 'git svn fetch && git pull . remotes/git-svn' b_cr="$(git hash-object cr)" b_ne_cr="$(git hash-object ne_cr)" @@ -168,7 +168,7 @@ cat >create-ignore-index.expect <<\EOF EOF test_expect_success 'test create-ignore' " - git svn fetch && git pull . ${remotes_git_svn} && + git svn fetch && git pull . remotes/git-svn && git svn create-ignore && cmp ./.gitignore create-ignore.expect && cmp ./deeply/.gitignore create-ignore.expect && diff --git a/t/t9102-git-svn-deep-rmdir.sh b/t/t9102-git-svn-deep-rmdir.sh index eb70f4839c..66cd51102c 100755 --- a/t/t9102-git-svn-deep-rmdir.sh +++ b/t/t9102-git-svn-deep-rmdir.sh @@ -17,7 +17,7 @@ test_expect_success 'initialize repo' ' test_expect_success 'mirror via git svn' ' git svn init "$svnrepo" && git svn fetch && - git checkout -f -b test-rmdir ${remotes_git_svn} + git checkout -f -b test-rmdir remotes/git-svn ' test_expect_success 'Try a commit on rmdir' ' diff --git a/t/t9103-git-svn-tracked-directory-removed.sh b/t/t9103-git-svn-tracked-directory-removed.sh index 3413164cb1..b28271345c 100755 --- a/t/t9103-git-svn-tracked-directory-removed.sh +++ b/t/t9103-git-svn-tracked-directory-removed.sh @@ -23,17 +23,19 @@ test_expect_success 'make history for tracking' ' test_expect_success 'clone repo with git' ' git svn clone -s "$svnrepo" x && - test -f x/FOLLOWME && - test ! -f x/README + test_path_is_file x/FOLLOWME && + test_path_is_missing x/README ' -test_expect_success 'make sure r2 still has old file' " - cd x && - test -n \"\$(git svn find-rev r1)\" && - git reset --hard \$(git svn find-rev r1) && - test -f README && - test ! -f FOLLOWME && - test x\$(git svn find-rev r2) = x -" +test_expect_success 'make sure r2 still has old file' ' + ( + cd x && + test -n "$(git svn find-rev r1)" && + git reset --hard "$(git svn find-rev r1)" && + test_path_is_file README && + test_path_is_missing FOLLOWME && + test -z "$(git svn find-rev r2)" + ) +' test_done diff --git a/t/t9106-git-svn-commit-diff-clobber.sh b/t/t9106-git-svn-commit-diff-clobber.sh index f6d7ac7c5f..dbe8deac0d 100755 --- a/t/t9106-git-svn-commit-diff-clobber.sh +++ b/t/t9106-git-svn-commit-diff-clobber.sh @@ -44,7 +44,7 @@ test_expect_success 'commit complementing change from git' ' test_expect_success 'dcommit fails to commit because of conflict' ' git svn init "$svnrepo" && git svn fetch && - git reset --hard refs/${remotes_git_svn} && + git reset --hard refs/remotes/git-svn && svn_cmd co "$svnrepo" t.svn && ( cd t.svn && @@ -59,7 +59,7 @@ test_expect_success 'dcommit fails to commit because of conflict' ' ' test_expect_success 'dcommit does the svn equivalent of an index merge' " - git reset --hard refs/${remotes_git_svn} && + git reset --hard refs/remotes/git-svn && echo 'index merge' > file2 && git update-index --add file2 && git commit -a -m 'index merge' && @@ -81,7 +81,7 @@ test_expect_success 'commit another change from svn side' ' ' test_expect_success 'multiple dcommit from git svn will not clobber svn' " - git reset --hard refs/${remotes_git_svn} && + git reset --hard refs/remotes/git-svn && echo new file >> new-file && git update-index --add new-file && git commit -a -m 'new file' && diff --git a/t/t9107-git-svn-migrate.sh b/t/t9107-git-svn-migrate.sh index 9060198037..9f3ef8f2ef 100755 --- a/t/t9107-git-svn-migrate.sh +++ b/t/t9107-git-svn-migrate.sh @@ -19,13 +19,14 @@ test_expect_success 'setup old-looking metadata' ' git svn init "$svnrepo" && git svn fetch && rm -rf "$GIT_DIR"/svn && - git update-ref refs/heads/git-svn-HEAD refs/${remotes_git_svn} && - git update-ref refs/heads/svn-HEAD refs/${remotes_git_svn} && - git update-ref -d refs/${remotes_git_svn} refs/${remotes_git_svn} + git update-ref refs/heads/git-svn-HEAD refs/remotes/git-svn && + git update-ref refs/heads/svn-HEAD refs/remotes/git-svn && + git update-ref -d refs/remotes/git-svn refs/remotes/git-svn ' -head=$(git rev-parse --verify refs/heads/git-svn-HEAD^0) -test_expect_success 'git-svn-HEAD is a real HEAD' "test -n '$head'" +test_expect_success 'git-svn-HEAD is a real HEAD' ' + git rev-parse --verify refs/heads/git-svn-HEAD^0 +' svnrepo_escaped=$(echo $svnrepo | sed 's/ /%20/') @@ -35,11 +36,11 @@ test_expect_success 'initialize old-style (v0) git svn layout' ' echo "$svnrepo" > "$GIT_DIR"/svn/info/url && git svn migrate && ! test -d "$GIT_DIR"/git-svn && - git rev-parse --verify refs/${remotes_git_svn}^0 && + git rev-parse --verify refs/remotes/git-svn^0 && git rev-parse --verify refs/remotes/svn^0 && test "$(git config --get svn-remote.svn.url)" = "$svnrepo_escaped" && test $(git config --get svn-remote.svn.fetch) = \ - ":refs/${remotes_git_svn}" + ":refs/remotes/git-svn" ' test_expect_success 'initialize a multi-repository repo' ' @@ -56,9 +57,11 @@ test_expect_success 'initialize a multi-repository repo' ' "^tags/\*:refs/remotes/origin/tags/\*$" && git config --add svn-remote.svn.fetch "branches/a:refs/remotes/origin/a" && git config --add svn-remote.svn.fetch "branches/b:refs/remotes/origin/b" && - for i in tags/0.1 tags/0.2 tags/0.3; do + for i in tags/0.1 tags/0.2 tags/0.3 + do git config --add svn-remote.svn.fetch \ - $i:refs/remotes/origin/$i || exit 1; done && + $i:refs/remotes/origin/$i || return 1 + done && git config --get-all svn-remote.svn.fetch > fetch.out && grep "^trunk:refs/remotes/origin/trunk$" fetch.out && grep "^branches/a:refs/remotes/origin/a$" fetch.out && @@ -66,34 +69,42 @@ test_expect_success 'initialize a multi-repository repo' ' grep "^tags/0\.1:refs/remotes/origin/tags/0\.1$" fetch.out && grep "^tags/0\.2:refs/remotes/origin/tags/0\.2$" fetch.out && grep "^tags/0\.3:refs/remotes/origin/tags/0\.3$" fetch.out && - grep "^:refs/${remotes_git_svn}" fetch.out + grep "^:refs/remotes/git-svn" fetch.out ' # refs should all be different, but the trees should all be the same: -test_expect_success 'multi-fetch works on partial urls + paths' " +test_expect_success 'multi-fetch works on partial urls + paths' ' + refs="trunk a b tags/0.1 tags/0.2 tags/0.3" && git svn multi-fetch && - for i in trunk a b tags/0.1 tags/0.2 tags/0.3; do - git rev-parse --verify refs/remotes/origin/\$i^0 >> refs.out || exit 1; - done && - test -z \"\$(sort < refs.out | uniq -d)\" && - for i in trunk a b tags/0.1 tags/0.2 tags/0.3; do - for j in trunk a b tags/0.1 tags/0.2 tags/0.3; do - if test \$j != \$i; then continue; fi - test -z \"\$(git diff refs/remotes/origin/\$i \ - refs/remotes/origin/\$j)\" ||exit 1; done; done - " + for i in $refs + do + git rev-parse --verify refs/remotes/origin/$i^0 || return 1; + done >refs.out && + test -z "$(sort <refs.out | uniq -d)" && + for i in $refs + do + for j in $refs + do + git diff --exit-code refs/remotes/origin/$i \ + refs/remotes/origin/$j || + return 1 + done + done +' test_expect_success 'migrate --minimize on old inited layout' ' git config --unset-all svn-remote.svn.fetch && git config --unset-all svn-remote.svn.url && rm -rf "$GIT_DIR"/svn && - for i in $(cat fetch.out); do + for i in $(cat fetch.out) + do path=$(expr $i : "\([^:]*\):.*$") ref=$(expr $i : "[^:]*:\(refs/remotes/.*\)$") if test -z "$ref"; then continue; fi if test -n "$path"; then path="/$path"; fi - ( mkdir -p "$GIT_DIR"/svn/$ref/info/ && - echo "$svnrepo"$path > "$GIT_DIR"/svn/$ref/info/url ) || exit 1; + mkdir -p "$GIT_DIR"/svn/$ref/info/ && + echo "$svnrepo"$path >"$GIT_DIR"/svn/$ref/info/url || + return 1 done && git svn migrate --minimize && test -z "$(git config -l | grep "^svn-remote\.git-svn\.")" && @@ -104,7 +115,7 @@ test_expect_success 'migrate --minimize on old inited layout' ' grep "^tags/0\.1:refs/remotes/origin/tags/0\.1$" fetch.out && grep "^tags/0\.2:refs/remotes/origin/tags/0\.2$" fetch.out && grep "^tags/0\.3:refs/remotes/origin/tags/0\.3$" fetch.out && - grep "^:refs/${remotes_git_svn}" fetch.out + grep "^:refs/remotes/git-svn" fetch.out ' test_expect_success ".rev_db auto-converted to .rev_map.UUID" ' diff --git a/t/t9108-git-svn-glob.sh b/t/t9108-git-svn-glob.sh index a526d60379..a94286c8ec 100755 --- a/t/t9108-git-svn-glob.sh +++ b/t/t9108-git-svn-glob.sh @@ -86,9 +86,12 @@ test_expect_success 'test left-hand-side only globbing' ' test_cmp expect.two output.two ' -echo "Only one set of wildcard directories" \ - "(e.g. '*' or '*/*/*') is supported: 'branches/*/t/*'" > expect.three -echo "" >> expect.three +test_expect_success 'prepare test disallow multi-globs' " +cat >expect.three <<EOF +Only one set of wildcards (e.g. '*' or '*/*/*') is supported: branches/*/t/* + +EOF + " test_expect_success 'test disallow multi-globs' ' git config --add svn-remote.three.url "$svnrepo" && diff --git a/t/t9109-git-svn-multi-glob.sh b/t/t9109-git-svn-multi-glob.sh index f36b749242..8d99e848d4 100755 --- a/t/t9109-git-svn-multi-glob.sh +++ b/t/t9109-git-svn-multi-glob.sh @@ -135,9 +135,12 @@ test_expect_success 'test another branch' ' test_cmp expect.four output.four ' -echo "Only one set of wildcard directories" \ - "(e.g. '*' or '*/*/*') is supported: 'branches/*/t/*'" > expect.three -echo "" >> expect.three +test_expect_success 'prepare test disallow multiple globs' " +cat >expect.three <<EOF +Only one set of wildcards (e.g. '*' or '*/*/*') is supported: branches/*/t/* + +EOF + " test_expect_success 'test disallow multiple globs' ' git config --add svn-remote.three.url "$svnrepo" && diff --git a/t/t9110-git-svn-use-svm-props.sh b/t/t9110-git-svn-use-svm-props.sh index 29fbdfdd3f..dde0a3c222 100755 --- a/t/t9110-git-svn-use-svm-props.sh +++ b/t/t9110-git-svn-use-svm-props.sh @@ -22,31 +22,31 @@ uuid=161ce429-a9dd-4828-af4a-52023f968c89 bar_url=http://mayonaise/svnrepo/bar test_expect_success 'verify metadata for /bar' " git cat-file commit refs/remotes/bar | \ - grep '^${git_svn_id}: $bar_url@12 $uuid$' && + grep '^git-svn-id: $bar_url@12 $uuid$' && git cat-file commit refs/remotes/bar~1 | \ - grep '^${git_svn_id}: $bar_url@11 $uuid$' && + grep '^git-svn-id: $bar_url@11 $uuid$' && git cat-file commit refs/remotes/bar~2 | \ - grep '^${git_svn_id}: $bar_url@10 $uuid$' && + grep '^git-svn-id: $bar_url@10 $uuid$' && git cat-file commit refs/remotes/bar~3 | \ - grep '^${git_svn_id}: $bar_url@9 $uuid$' && + grep '^git-svn-id: $bar_url@9 $uuid$' && git cat-file commit refs/remotes/bar~4 | \ - grep '^${git_svn_id}: $bar_url@6 $uuid$' && + grep '^git-svn-id: $bar_url@6 $uuid$' && git cat-file commit refs/remotes/bar~5 | \ - grep '^${git_svn_id}: $bar_url@1 $uuid$' + grep '^git-svn-id: $bar_url@1 $uuid$' " e_url=http://mayonaise/svnrepo/dir/a/b/c/d/e test_expect_success 'verify metadata for /dir/a/b/c/d/e' " git cat-file commit refs/remotes/e | \ - grep '^${git_svn_id}: $e_url@1 $uuid$' + grep '^git-svn-id: $e_url@1 $uuid$' " dir_url=http://mayonaise/svnrepo/dir test_expect_success 'verify metadata for /dir' " git cat-file commit refs/remotes/dir | \ - grep '^${git_svn_id}: $dir_url@2 $uuid$' && + grep '^git-svn-id: $dir_url@2 $uuid$' && git cat-file commit refs/remotes/dir~1 | \ - grep '^${git_svn_id}: $dir_url@1 $uuid$' + grep '^git-svn-id: $dir_url@1 $uuid$' " test_expect_success 'find commit based on SVN revision number' " diff --git a/t/t9111-git-svn-use-svnsync-props.sh b/t/t9111-git-svn-use-svnsync-props.sh index bd081c2ec3..22b6e5ee7d 100755 --- a/t/t9111-git-svn-use-svnsync-props.sh +++ b/t/t9111-git-svn-use-svnsync-props.sh @@ -21,31 +21,31 @@ uuid=161ce429-a9dd-4828-af4a-52023f968c89 bar_url=http://mayonaise/svnrepo/bar test_expect_success 'verify metadata for /bar' " git cat-file commit refs/remotes/bar | \ - grep '^${git_svn_id}: $bar_url@12 $uuid$' && + grep '^git-svn-id: $bar_url@12 $uuid$' && git cat-file commit refs/remotes/bar~1 | \ - grep '^${git_svn_id}: $bar_url@11 $uuid$' && + grep '^git-svn-id: $bar_url@11 $uuid$' && git cat-file commit refs/remotes/bar~2 | \ - grep '^${git_svn_id}: $bar_url@10 $uuid$' && + grep '^git-svn-id: $bar_url@10 $uuid$' && git cat-file commit refs/remotes/bar~3 | \ - grep '^${git_svn_id}: $bar_url@9 $uuid$' && + grep '^git-svn-id: $bar_url@9 $uuid$' && git cat-file commit refs/remotes/bar~4 | \ - grep '^${git_svn_id}: $bar_url@6 $uuid$' && + grep '^git-svn-id: $bar_url@6 $uuid$' && git cat-file commit refs/remotes/bar~5 | \ - grep '^${git_svn_id}: $bar_url@1 $uuid$' + grep '^git-svn-id: $bar_url@1 $uuid$' " e_url=http://mayonaise/svnrepo/dir/a/b/c/d/e test_expect_success 'verify metadata for /dir/a/b/c/d/e' " git cat-file commit refs/remotes/e | \ - grep '^${git_svn_id}: $e_url@1 $uuid$' + grep '^git-svn-id: $e_url@1 $uuid$' " dir_url=http://mayonaise/svnrepo/dir test_expect_success 'verify metadata for /dir' " git cat-file commit refs/remotes/dir | \ - grep '^${git_svn_id}: $dir_url@2 $uuid$' && + grep '^git-svn-id: $dir_url@2 $uuid$' && git cat-file commit refs/remotes/dir~1 | \ - grep '^${git_svn_id}: $dir_url@1 $uuid$' + grep '^git-svn-id: $dir_url@1 $uuid$' " test_done diff --git a/t/t9115-git-svn-dcommit-funky-renames.sh b/t/t9115-git-svn-dcommit-funky-renames.sh index 6a48e40429..a87d3d3fc1 100755 --- a/t/t9115-git-svn-dcommit-funky-renames.sh +++ b/t/t9115-git-svn-dcommit-funky-renames.sh @@ -77,11 +77,47 @@ test_expect_success 'make a commit to test rebase' ' ' test_expect_success 'git svn rebase works inside a fresh-cloned repository' ' - cd test-rebase && + ( + cd test-rebase && git svn rebase && test -e test-rebase-main && test -e test-rebase - ' + )' + +# Without this, LC_ALL=C as set in test-lib.sh, and Cygwin converts +# non-ASCII characters in filenames unexpectedly, and causes errors. +# https://cygwin.com/cygwin-ug-net/using-specialnames.html#pathnames-specialchars +# > Some characters are disallowed in filenames on Windows filesystems. ... +# ... +# > ... All of the above characters, except for the backslash, are converted +# > to special UNICODE characters in the range 0xf000 to 0xf0ff (the +# > "Private use area") when creating or accessing files. +prepare_a_utf8_locale +test_expect_success UTF8,!MINGW,!UTF8_NFD_TO_NFC 'svn.pathnameencoding=cp932 new file on dcommit' ' + LC_ALL=$a_utf8_locale && + export LC_ALL && + neq=$(printf "\201\202") && + git config svn.pathnameencoding cp932 && + echo neq >"$neq" && + git add "$neq" && + git commit -m "neq" && + git svn dcommit +' + +# See the comment on the above test for setting of LC_ALL. +test_expect_success !MINGW,!UTF8_NFD_TO_NFC 'svn.pathnameencoding=cp932 rename on dcommit' ' + LC_ALL=$a_utf8_locale && + export LC_ALL && + inf=$(printf "\201\207") && + git config svn.pathnameencoding cp932 && + echo inf >"$inf" && + git add "$inf" && + git commit -m "inf" && + git svn dcommit && + git mv "$inf" inf && + git commit -m "inf rename" && + git svn dcommit +' stop_httpd diff --git a/t/t9117-git-svn-init-clone.sh b/t/t9117-git-svn-init-clone.sh index a66f43c6b1..69a675052e 100755 --- a/t/t9117-git-svn-init-clone.sh +++ b/t/t9117-git-svn-init-clone.sh @@ -119,4 +119,10 @@ test_expect_success 'clone with -s/-T/-b/-t and --prefix "" still works' ' rm -f warning ' +test_expect_success 'init with -T as a full url works' ' + test ! -d project && + git svn init -T "$svnrepo"/project/trunk project && + rm -rf project + ' + test_done diff --git a/t/t9120-git-svn-clone-with-percent-escapes.sh b/t/t9120-git-svn-clone-with-percent-escapes.sh index 1c84ce1023..59465b147e 100755 --- a/t/t9120-git-svn-clone-with-percent-escapes.sh +++ b/t/t9120-git-svn-clone-with-percent-escapes.sh @@ -22,7 +22,7 @@ test_expect_success 'test clone with percent escapes' ' git svn clone "$svnrepo/pr%20ject" clone && ( cd clone && - git rev-parse refs/${remotes_git_svn} + git rev-parse refs/remotes/git-svn ) ' @@ -42,7 +42,7 @@ test_expect_success 'test clone trunk with percent escapes and minimize-url' ' git svn clone --minimize-url "$svnrepo/pr%20ject/trunk" minimize && ( cd minimize && - git rev-parse refs/${remotes_git_svn} + git rev-parse refs/remotes/git-svn ) ' @@ -50,7 +50,7 @@ test_expect_success 'test clone trunk with percent escapes' ' git svn clone "$svnrepo/pr%20ject/trunk" trunk && ( cd trunk && - git rev-parse refs/${remotes_git_svn} + git rev-parse refs/remotes/git-svn ) ' diff --git a/t/t9123-git-svn-rebuild-with-rewriteroot.sh b/t/t9123-git-svn-rebuild-with-rewriteroot.sh index fd8184787f..ead404589e 100755 --- a/t/t9123-git-svn-rebuild-with-rewriteroot.sh +++ b/t/t9123-git-svn-rebuild-with-rewriteroot.sh @@ -17,7 +17,7 @@ rm -rf import test_expect_success 'init, fetch and checkout repository' ' git svn init --rewrite-root=http://invalid.invalid/ "$svnrepo" && git svn fetch && - git checkout -b mybranch ${remotes_git_svn} + git checkout -b mybranch remotes/git-svn ' test_expect_success 'remove rev_map' ' diff --git a/t/t9129-git-svn-i18n-commitencoding.sh b/t/t9129-git-svn-i18n-commitencoding.sh index 39b6bcb398..8dbd6476fa 100755 --- a/t/t9129-git-svn-i18n-commitencoding.sh +++ b/t/t9129-git-svn-i18n-commitencoding.sh @@ -14,17 +14,7 @@ compare_git_head_with () { test_cmp current "$1" } -a_utf8_locale=$(locale -a | sed -n '/\.[uU][tT][fF]-*8$/{ - p - q -}') - -if test -n "$a_utf8_locale" -then - test_set_prereq UTF8 -else - say "# UTF-8 locale not available, some tests are skipped" -fi +prepare_a_utf8_locale compare_svn_head_with () { # extract just the log message and strip out committer info. diff --git a/t/t9153-git-svn-rewrite-uuid.sh b/t/t9153-git-svn-rewrite-uuid.sh index 88a2cfa233..372ef15685 100755 --- a/t/t9153-git-svn-rewrite-uuid.sh +++ b/t/t9153-git-svn-rewrite-uuid.sh @@ -17,9 +17,9 @@ test_expect_success 'load svn repo' " test_expect_success 'verify uuid' " git cat-file commit refs/remotes/git-svn~0 | \ - grep '^${git_svn_id}: .*@2 $uuid$' && + grep '^git-svn-id: .*@2 $uuid$' && git cat-file commit refs/remotes/git-svn~1 | \ - grep '^${git_svn_id}: .*@1 $uuid$' + grep '^git-svn-id: .*@1 $uuid$' " test_done diff --git a/t/t9168-git-svn-partially-globbed-names.sh b/t/t9168-git-svn-partially-globbed-names.sh new file mode 100755 index 0000000000..8b22f2272c --- /dev/null +++ b/t/t9168-git-svn-partially-globbed-names.sh @@ -0,0 +1,223 @@ +#!/bin/sh +test_description='git svn globbing refspecs with prefixed globs' +. ./lib-git-svn.sh + +test_expect_success 'prepare test refspec prefixed globbing' ' + cat >expect.end <<EOF +the end +hi +start a new branch +initial +EOF + ' + +test_expect_success 'test refspec prefixed globbing' ' + mkdir -p trunk/src/a trunk/src/b trunk/doc && + echo "hello world" >trunk/src/a/readme && + echo "goodbye world" >trunk/src/b/readme && + svn_cmd import -m "initial" trunk "$svnrepo"/trunk && + svn_cmd co "$svnrepo" tmp && + ( + cd tmp && + mkdir branches tags && + svn_cmd add branches tags && + svn_cmd cp trunk branches/b_start && + svn_cmd commit -m "start a new branch" && + svn_cmd up && + echo "hi" >>branches/b_start/src/b/readme && + poke branches/b_start/src/b/readme && + echo "hey" >>branches/b_start/src/a/readme && + poke branches/b_start/src/a/readme && + svn_cmd commit -m "hi" && + svn_cmd up && + svn_cmd cp branches/b_start tags/t_end && + echo "bye" >>tags/t_end/src/b/readme && + poke tags/t_end/src/b/readme && + echo "aye" >>tags/t_end/src/a/readme && + poke tags/t_end/src/a/readme && + svn_cmd commit -m "the end" && + echo "byebye" >>tags/t_end/src/b/readme && + poke tags/t_end/src/b/readme && + svn_cmd commit -m "nothing to see here" + ) && + git config --add svn-remote.svn.url "$svnrepo" && + git config --add svn-remote.svn.fetch \ + "trunk/src/a:refs/remotes/trunk" && + git config --add svn-remote.svn.branches \ + "branches/b_*/src/a:refs/remotes/branches/b_*" && + git config --add svn-remote.svn.tags\ + "tags/t_*/src/a:refs/remotes/tags/t_*" && + git svn multi-fetch && + git log --pretty=oneline refs/remotes/tags/t_end | \ + sed -e "s/^.\{41\}//" >output.end && + test_cmp expect.end output.end && + test "$(git rev-parse refs/remotes/tags/t_end~1)" = \ + "$(git rev-parse refs/remotes/branches/b_start)" && + test "$(git rev-parse refs/remotes/branches/b_start~2)" = \ + "$(git rev-parse refs/remotes/trunk)" && + test_must_fail git rev-parse refs/remotes/tags/t_end@3 + ' + +test_expect_success 'prepare test left-hand-side only prefixed globbing' ' + echo try to try >expect.two && + echo nothing to see here >>expect.two && + cat expect.end >>expect.two + ' + +test_expect_success 'test left-hand-side only prefixed globbing' ' + git config --add svn-remote.two.url "$svnrepo" && + git config --add svn-remote.two.fetch trunk:refs/remotes/two/trunk && + git config --add svn-remote.two.branches \ + "branches/b_*:refs/remotes/two/branches/*" && + git config --add svn-remote.two.tags \ + "tags/t_*:refs/remotes/two/tags/*" && + ( + cd tmp && + echo "try try" >>tags/t_end/src/b/readme && + poke tags/t_end/src/b/readme && + svn_cmd commit -m "try to try" + ) && + git svn fetch two && + test $(git rev-list refs/remotes/two/tags/t_end | wc -l) -eq 6 && + test $(git rev-list refs/remotes/two/branches/b_start | wc -l) -eq 3 && + test $(git rev-parse refs/remotes/two/branches/b_start~2) = \ + $(git rev-parse refs/remotes/two/trunk) && + test $(git rev-parse refs/remotes/two/tags/t_end~3) = \ + $(git rev-parse refs/remotes/two/branches/b_start) && + git log --pretty=oneline refs/remotes/two/tags/t_end | \ + sed -e "s/^.\{41\}//" >output.two && + test_cmp expect.two output.two + ' + +test_expect_success 'prepare test prefixed globs match just prefix' ' + cat >expect.three <<EOF +Tag commit to t_ +Branch commit to b_ +initial +EOF + ' + +test_expect_success 'test prefixed globs match just prefix' ' + git config --add svn-remote.three.url "$svnrepo" && + git config --add svn-remote.three.fetch \ + trunk:refs/remotes/three/trunk && + git config --add svn-remote.three.branches \ + "branches/b_*:refs/remotes/three/branches/*" && + git config --add svn-remote.three.tags \ + "tags/t_*:refs/remotes/three/tags/*" && + ( + cd tmp && + svn_cmd cp trunk branches/b_ && + echo "Branch commit to b_" >>branches/b_/src/a/readme && + poke branches/b_/src/a/readme && + svn_cmd commit -m "Branch commit to b_" && + svn_cmd up && svn_cmd cp branches/b_ tags/t_ && + echo "Tag commit to t_" >>tags/t_/src/a/readme && + poke tags/t_/src/a/readme && + svn_cmd commit -m "Tag commit to t_" && + svn_cmd up + ) && + git svn fetch three && + test $(git rev-list refs/remotes/three/branches/b_ | wc -l) -eq 2 && + test $(git rev-list refs/remotes/three/tags/t_ | wc -l) -eq 3 && + test $(git rev-parse refs/remotes/three/branches/b_~1) = \ + $(git rev-parse refs/remotes/three/trunk) && + test $(git rev-parse refs/remotes/three/tags/t_~1) = \ + $(git rev-parse refs/remotes/three/branches/b_) && + git log --pretty=oneline refs/remotes/three/tags/t_ | \ + sed -e "s/^.\{41\}//" >output.three && + test_cmp expect.three output.three + ' + +test_expect_success 'prepare test disallow prefixed multi-globs' " +cat >expect.four <<EOF +Only one set of wildcards (e.g. '*' or '*/*/*') is supported: branches/b_*/t/* + +EOF + " + +test_expect_success 'test disallow prefixed multi-globs' ' + git config --add svn-remote.four.url "$svnrepo" && + git config --add svn-remote.four.fetch \ + trunk:refs/remotes/four/trunk && + git config --add svn-remote.four.branches \ + "branches/b_*/t/*:refs/remotes/four/branches/*" && + git config --add svn-remote.four.tags \ + "tags/t_*/*:refs/remotes/four/tags/*" && + ( + cd tmp && + echo "try try" >>tags/t_end/src/b/readme && + poke tags/t_end/src/b/readme && + svn_cmd commit -m "try to try" + ) && + test_must_fail git svn fetch four 2>stderr.four && + test_cmp expect.four stderr.four && + git config --unset svn-remote.four.branches && + git config --unset svn-remote.four.tags + ' + +test_expect_success 'prepare test globbing in the middle of the word' ' + cat >expect.five <<EOF +Tag commit to fghij +Branch commit to abcde +initial +EOF + ' + +test_expect_success 'test globbing in the middle of the word' ' + git config --add svn-remote.five.url "$svnrepo" && + git config --add svn-remote.five.fetch \ + trunk:refs/remotes/five/trunk && + git config --add svn-remote.five.branches \ + "branches/a*e:refs/remotes/five/branches/*" && + git config --add svn-remote.five.tags \ + "tags/f*j:refs/remotes/five/tags/*" && + ( + cd tmp && + svn_cmd cp trunk branches/abcde && + echo "Branch commit to abcde" >>branches/abcde/src/a/readme && + poke branches/b_/src/a/readme && + svn_cmd commit -m "Branch commit to abcde" && + svn_cmd up && + svn_cmd cp branches/abcde tags/fghij && + echo "Tag commit to fghij" >>tags/fghij/src/a/readme && + poke tags/fghij/src/a/readme && + svn_cmd commit -m "Tag commit to fghij" && + svn_cmd up + ) && + git svn fetch five && + test $(git rev-list refs/remotes/five/branches/abcde | wc -l) -eq 2 && + test $(git rev-list refs/remotes/five/tags/fghij | wc -l) -eq 3 && + test $(git rev-parse refs/remotes/five/branches/abcde~1) = \ + $(git rev-parse refs/remotes/five/trunk) && + test $(git rev-parse refs/remotes/five/tags/fghij~1) = \ + $(git rev-parse refs/remotes/five/branches/abcde) && + git log --pretty=oneline refs/remotes/five/tags/fghij | \ + sed -e "s/^.\{41\}//" >output.five && + test_cmp expect.five output.five + ' + +test_expect_success 'prepare test disallow multiple asterisks in one word' " + echo \"Only one '*' is allowed in a pattern: 'a*c*e'\" >expect.six && + echo \"\" >>expect.six + " + +test_expect_success 'test disallow multiple asterisks in one word' ' + git config --add svn-remote.six.url "$svnrepo" && + git config --add svn-remote.six.fetch \ + trunk:refs/remotes/six/trunk && + git config --add svn-remote.six.branches \ + "branches/a*c*e:refs/remotes/six/branches/*" && + git config --add svn-remote.six.tags \ + "tags/f*h*j:refs/remotes/six/tags/*" && + ( + cd tmp && + echo "try try" >>tags/fghij/src/b/readme && + poke tags/fghij/src/b/readme && + svn_cmd commit -m "try to try" + ) && + test_must_fail git svn fetch six 2>stderr.six && + test_cmp expect.six stderr.six + ' + +test_done diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh index 5cfb9cfc52..bb879a527d 100755 --- a/t/t9200-git-cvsexportcommit.sh +++ b/t/t9200-git-cvsexportcommit.sh @@ -35,7 +35,7 @@ exit 1 check_entries () { # $1 == directory, $2 == expected - grep '^/' "$1/CVS/Entries" | sort | cut -d/ -f2,3,5 >actual + sed -ne '/^\//p' "$1/CVS/Entries" | sort | cut -d/ -f2,3,5 >actual if test -z "$2" then >expected diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh index 4c5f3c9d41..25bb60b281 100755 --- a/t/t9300-fast-import.sh +++ b/t/t9300-fast-import.sh @@ -55,6 +55,10 @@ test_expect_success 'empty stream succeeds' ' git fast-import </dev/null ' +test_expect_success 'truncated stream complains' ' + echo "tag foo" | test_must_fail git fast-import +' + test_expect_success 'A: create pack from stdin' ' test_tick && cat >input <<-INPUT_END && diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh index d708cbf032..432c61d246 100755 --- a/t/t9400-git-cvsserver-server.sh +++ b/t/t9400-git-cvsserver-server.sh @@ -45,7 +45,8 @@ test_expect_success 'setup' ' touch secondrootfile && git add secondrootfile && git commit -m "second root") && - git pull secondroot master && + git fetch secondroot master && + git merge --allow-unrelated-histories FETCH_HEAD && git clone -q --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 && GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true && GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log" && diff --git a/t/t9700/test.pl b/t/t9700/test.pl index 7e8c40b97b..1b75c91965 100755 --- a/t/t9700/test.pl +++ b/t/t9700/test.pl @@ -17,6 +17,12 @@ BEGIN { use Cwd; use File::Basename; +sub adjust_dirsep { + my $path = shift; + $path =~ s{\\}{/}g; + return $path; +} + BEGIN { use_ok('Git') } # set up @@ -33,7 +39,7 @@ is($r->config_int("test.int"), 2048, "config_int: integer"); is($r->config_int("test.nonexistent"), undef, "config_int: nonexistent"); ok($r->config_bool("test.booltrue"), "config_bool: true"); ok(!$r->config_bool("test.boolfalse"), "config_bool: false"); -is($r->config_path("test.path") =~ s/\\/\//gr, $r->config("test.pathexpanded"), +is(adjust_dirsep($r->config_path("test.path")), $r->config("test.pathexpanded"), "config_path: ~/foo expansion"); is_deeply([$r->config_path("test.pathmulti")], ["foo", "bar"], "config_path: multiple values"); diff --git a/t/t9802-git-p4-filetype.sh b/t/t9802-git-p4-filetype.sh index 66d3fc91a7..eb9a8ed197 100755 --- a/t/t9802-git-p4-filetype.sh +++ b/t/t9802-git-p4-filetype.sh @@ -223,12 +223,12 @@ build_gendouble() { import sys import struct - s = struct.pack(">LL18s", + s = struct.pack(b">LL18s", 0x00051607, # AppleDouble 0x00020000, # version 2 - "" # pad to 26 bytes + b"" # pad to 26 bytes ) - sys.stdout.write(s) + getattr(sys.stdout, 'buffer', sys.stdout).write(s) EOF } diff --git a/t/t9824-git-p4-git-lfs.sh b/t/t9824-git-p4-git-lfs.sh index 0b664a377c..110a7e7924 100755 --- a/t/t9824-git-p4-git-lfs.sh +++ b/t/t9824-git-p4-git-lfs.sh @@ -13,6 +13,10 @@ test_file_in_lfs () { FILE="$1" && SIZE="$2" && EXPECTED_CONTENT="$3" && + sed -n '1,1 p' "$FILE" | grep "^version " && + sed -n '2,2 p' "$FILE" | grep "^oid " && + sed -n '3,3 p' "$FILE" | grep "^size " && + test_line_count = 3 "$FILE" && cat "$FILE" | grep "size $SIZE" && HASH=$(cat "$FILE" | grep "oid sha256:" | sed -e "s/oid sha256://g") && LFS_FILE=".git/lfs/objects/$(echo "$HASH" | cut -c1-2)/$(echo "$HASH" | cut -c3-4)/$HASH" && @@ -265,7 +269,7 @@ test_expect_success 'Add big files to repo and store files in LFS based on compr # We only import HEAD here ("@all" is missing!) git p4 clone --destination="$git" //depot && - test_file_in_lfs file6.bin 13 "content 6 bin 39 bytes XXXXXYYYYYZZZZZ" + test_file_in_lfs file6.bin 39 "content 6 bin 39 bytes XXXXXYYYYYZZZZZ" && test_file_count_in_dir ".git/lfs/objects" 1 && cat >expect <<-\EOF && diff --git a/t/t9826-git-p4-keep-empty-commits.sh b/t/t9826-git-p4-keep-empty-commits.sh index be12960d39..fa8b9daf1f 100755 --- a/t/t9826-git-p4-keep-empty-commits.sh +++ b/t/t9826-git-p4-keep-empty-commits.sh @@ -47,23 +47,23 @@ test_expect_success 'Clone repo root path with all history' ' git init . && git p4 clone --use-client-spec --destination="$git" //depot@all && cat >expect <<-\EOF && -Remove file 4 -[git-p4: depot-paths = "//depot/": change = 6] + Remove file 4 + [git-p4: depot-paths = "//depot/": change = 6] -Remove file 3 -[git-p4: depot-paths = "//depot/": change = 5] + Remove file 3 + [git-p4: depot-paths = "//depot/": change = 5] -Add file 4 -[git-p4: depot-paths = "//depot/": change = 4] + Add file 4 + [git-p4: depot-paths = "//depot/": change = 4] -Add file 3 -[git-p4: depot-paths = "//depot/": change = 3] + Add file 3 + [git-p4: depot-paths = "//depot/": change = 3] -Add file 2 -[git-p4: depot-paths = "//depot/": change = 2] + Add file 2 + [git-p4: depot-paths = "//depot/": change = 2] -Add file 1 -[git-p4: depot-paths = "//depot/": change = 1] + Add file 1 + [git-p4: depot-paths = "//depot/": change = 1] EOF git log --format=%B >actual && @@ -80,23 +80,23 @@ test_expect_success 'Clone repo subdir with all history but keep empty commits' git config git-p4.keepEmptyCommits true && git p4 clone --use-client-spec --destination="$git" //depot@all && cat >expect <<-\EOF && -Remove file 4 -[git-p4: depot-paths = "//depot/": change = 6] + Remove file 4 + [git-p4: depot-paths = "//depot/": change = 6] -Remove file 3 -[git-p4: depot-paths = "//depot/": change = 5] + Remove file 3 + [git-p4: depot-paths = "//depot/": change = 5] -Add file 4 -[git-p4: depot-paths = "//depot/": change = 4] + Add file 4 + [git-p4: depot-paths = "//depot/": change = 4] -Add file 3 -[git-p4: depot-paths = "//depot/": change = 3] + Add file 3 + [git-p4: depot-paths = "//depot/": change = 3] -Add file 2 -[git-p4: depot-paths = "//depot/": change = 2] + Add file 2 + [git-p4: depot-paths = "//depot/": change = 2] -Add file 1 -[git-p4: depot-paths = "//depot/": change = 1] + Add file 1 + [git-p4: depot-paths = "//depot/": change = 1] EOF git log --format=%B >actual && @@ -112,14 +112,14 @@ test_expect_success 'Clone repo subdir with all history' ' git init . && git p4 clone --use-client-spec --destination="$git" --verbose //depot@all && cat >expect <<-\EOF && -Remove file 3 -[git-p4: depot-paths = "//depot/": change = 5] + Remove file 3 + [git-p4: depot-paths = "//depot/": change = 5] -Add file 3 -[git-p4: depot-paths = "//depot/": change = 3] + Add file 3 + [git-p4: depot-paths = "//depot/": change = 3] -Add file 1 -[git-p4: depot-paths = "//depot/": change = 1] + Add file 1 + [git-p4: depot-paths = "//depot/": change = 1] EOF git log --format=%B >actual && diff --git a/t/t9828-git-p4-map-user.sh b/t/t9828-git-p4-map-user.sh new file mode 100755 index 0000000000..e20395c89f --- /dev/null +++ b/t/t9828-git-p4-map-user.sh @@ -0,0 +1,61 @@ +#!/bin/sh + +test_description='Clone repositories and map users' + +. ./lib-git-p4.sh + +test_expect_success 'start p4d' ' + start_p4d +' + +test_expect_success 'Create a repo with different users' ' + client_view "//depot/... //client/..." && + ( + cd "$cli" && + + >author.txt && + p4 add author.txt && + p4 submit -d "Add file author\\n" && + + P4USER=mmax && + >max.txt && + p4 add max.txt && + p4 submit -d "Add file max" && + + P4USER=eri && + >moritz.txt && + p4 add moritz.txt && + p4 submit -d "Add file moritz" && + + P4USER=no && + >nobody.txt && + p4 add nobody.txt && + p4 submit -d "Add file nobody" + ) +' + +test_expect_success 'Clone repo root path with all history' ' + client_view "//depot/... //client/..." && + test_when_finished cleanup_git && + ( + cd "$git" && + git init . && + git config --add git-p4.mapUser "mmax = Max Musterman <max@example.com> " && + git config --add git-p4.mapUser " eri=Erika Musterman <erika@example.com>" && + git p4 clone --use-client-spec --destination="$git" //depot@all && + cat >expect <<-\EOF && + no <no@client> + Erika Musterman <erika@example.com> + Max Musterman <max@example.com> + Dr. author <author@example.com> + EOF + git log --format="%an <%ae>" >actual && + test_cmp expect actual + ) +' + +test_expect_success 'kill p4d' ' + kill_p4d +' + +test_done diff --git a/t/t9829-git-p4-jobs.sh b/t/t9829-git-p4-jobs.sh new file mode 100755 index 0000000000..971aeeea1f --- /dev/null +++ b/t/t9829-git-p4-jobs.sh @@ -0,0 +1,99 @@ +#!/bin/sh + +test_description='git p4 retrieve job info' + +. ./lib-git-p4.sh + +test_expect_success 'start p4d' ' + start_p4d +' + +test_expect_success 'add p4 jobs' ' + ( + p4_add_job TESTJOB-A && + p4_add_job TESTJOB-B + ) +' + +test_expect_success 'add p4 files' ' + client_view "//depot/... //client/..." && + ( + cd "$cli" && + >file1 && + p4 add file1 && + p4 submit -d "Add file 1" + ) +' + +test_expect_success 'check log message of changelist with no jobs' ' + client_view "//depot/... //client/..." && + test_when_finished cleanup_git && + ( + cd "$git" && + git init . && + git p4 clone --use-client-spec --destination="$git" //depot@all && + cat >expect <<-\EOF && + Add file 1 + [git-p4: depot-paths = "//depot/": change = 1] + + EOF + git log --format=%B >actual && + test_cmp expect actual + ) +' + +test_expect_success 'add TESTJOB-A to change 1' ' + ( + cd "$cli" && + p4 fix -c 1 TESTJOB-A + ) +' + +test_expect_success 'check log message of changelist with one job' ' + client_view "//depot/... //client/..." && + test_when_finished cleanup_git && + ( + cd "$git" && + git init . && + git p4 clone --use-client-spec --destination="$git" //depot@all && + cat >expect <<-\EOF && + Add file 1 + Jobs: TESTJOB-A + [git-p4: depot-paths = "//depot/": change = 1] + + EOF + git log --format=%B >actual && + test_cmp expect actual + ) +' + +test_expect_success 'add TESTJOB-B to change 1' ' + ( + cd "$cli" && + p4 fix -c 1 TESTJOB-B + ) +' + +test_expect_success 'check log message of changelist with more jobs' ' + client_view "//depot/... //client/..." && + test_when_finished cleanup_git && + ( + cd "$git" && + git init . && + git p4 clone --use-client-spec --destination="$git" //depot@all && + cat >expect <<-\EOF && + Add file 1 + Jobs: TESTJOB-A TESTJOB-B + [git-p4: depot-paths = "//depot/": change = 1] + + EOF + git log --format=%B >actual && + test_cmp expect actual + ) +' + +test_expect_success 'kill p4d' ' + kill_p4d +' + +test_done diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh index ffbfa0efb8..0db4469c89 100755 --- a/t/t9903-bash-prompt.sh +++ b/t/t9903-bash-prompt.sh @@ -107,7 +107,7 @@ test_expect_success 'prompt - describe detached head - contains' ' ' test_expect_success 'prompt - describe detached head - branch' ' - printf " ((b1~1))" >expected && + printf " ((tags/t2~1))" >expected && git checkout b1^ && test_when_finished "git checkout master" && ( diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh index c64e5a5025..3978fc0b45 100644 --- a/t/test-lib-functions.sh +++ b/t/test-lib-functions.sh @@ -617,7 +617,7 @@ test_must_fail () { return 0 elif test $exit_code -gt 129 && test $exit_code -le 192 then - echo >&2 "test_must_fail: died by signal: $*" + echo >&2 "test_must_fail: died by signal $(($exit_code - 128)): $*" return 1 elif test $exit_code -eq 127 then @@ -718,20 +718,13 @@ test_cmp_rev () { test_cmp expect.rev actual.rev } -# Print a sequence of numbers or letters in increasing order. This is -# similar to GNU seq(1), but the latter might not be available -# everywhere (and does not do letters). It may be used like: +# Print a sequence of integers in increasing order, either with +# two arguments (start and end): # -# for i in $(test_seq 100) -# do -# for j in $(test_seq 10 20) -# do -# for k in $(test_seq a z) -# do -# echo $i-$j-$k -# done -# done -# done +# test_seq 1 5 -- outputs 1 2 3 4 5 one line at a time +# +# or with one argument (end), in which case it starts counting +# from 1. test_seq () { case $# in @@ -739,7 +732,12 @@ test_seq () { 2) ;; *) error "bug in the test script: not 1 or 2 parameters to test_seq" ;; esac - perl -le 'print for $ARGV[0]..$ARGV[1]' -- "$@" + test_seq_counter__=$1 + while test "$test_seq_counter__" -le "$2" + do + echo "$test_seq_counter__" + test_seq_counter__=$(( $test_seq_counter__ + 1 )) + done } # This function can be used to schedule some commands to be run diff --git a/t/test-lib.sh b/t/test-lib.sh index 0b47eb6bb2..0055ebba46 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -202,13 +202,13 @@ do } run_list=$1; shift ;; --run=*) - run_list=$(expr "z$1" : 'z[^=]*=\(.*\)'); shift ;; + run_list=${1#--*=}; shift ;; -h|--h|--he|--hel|--help) help=t; shift ;; -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose) verbose=t; shift ;; --verbose-only=*) - verbose_only=$(expr "z$1" : 'z[^=]*=\(.*\)') + verbose_only=${1#--*=} shift ;; -q|--q|--qu|--qui|--quie|--quiet) # Ignore --quiet under a TAP::Harness. Saying how many tests @@ -222,15 +222,15 @@ do valgrind=memcheck shift ;; --valgrind=*) - valgrind=$(expr "z$1" : 'z[^=]*=\(.*\)') + valgrind=${1#--*=} shift ;; --valgrind-only=*) - valgrind_only=$(expr "z$1" : 'z[^=]*=\(.*\)') + valgrind_only=${1#--*=} shift ;; --tee) shift ;; # was handled already --root=*) - root=$(expr "z$1" : 'z[^=]*=\(.*\)') + root=${1#--*=} shift ;; --chain-lint) GIT_TEST_CHAIN_LINT=1 @@ -322,6 +322,19 @@ else exec 4>/dev/null 3>/dev/null fi +# Send any "-x" output directly to stderr to avoid polluting tests +# which capture stderr. We can do this unconditionally since it +# has no effect if tracing isn't turned on. +# +# Note that this sets up the trace fd as soon as we assign the variable, so it +# must come after the creation of descriptor 4 above. Likewise, we must never +# unset this, as it has the side effect of closing descriptor 4, which we +# use to show verbose tests to the user. +# +# Note also that we don't need or want to export it. The tracing is local to +# this shell, and we would not want to influence any shells we exec. +BASH_XTRACEFD=4 + test_failure=0 test_count=0 test_fixed=0 @@ -854,10 +867,10 @@ test -d "$GIT_BUILD_DIR"/templates/blt || { error "You haven't built things yet, have you?" } -if ! test -x "$GIT_BUILD_DIR"/test-chmtime +if ! test -x "$GIT_BUILD_DIR"/t/helper/test-chmtime then echo >&2 'You need to build test-chmtime:' - echo >&2 'Run "make test-chmtime" in the source (toplevel) directory' + echo >&2 'Run "make t/helper/test-chmtime" in the source (toplevel) directory' exit 1 fi |