summaryrefslogtreecommitdiff
path: root/t
diff options
context:
space:
mode:
Diffstat (limited to 't')
-rw-r--r--t/helper/.gitignore33
-rw-r--r--t/helper/test-chmtime.c119
-rw-r--r--t/helper/test-config.c152
-rw-r--r--t/helper/test-ctype.c42
-rw-r--r--t/helper/test-date.c73
-rw-r--r--t/helper/test-delta.c78
-rw-r--r--t/helper/test-dump-cache-tree.c67
-rw-r--r--t/helper/test-dump-split-index.c36
-rw-r--r--t/helper/test-dump-untracked-cache.c66
-rw-r--r--t/helper/test-fake-ssh.c30
-rw-r--r--t/helper/test-genrandom.c33
-rw-r--r--t/helper/test-hashmap.c264
-rw-r--r--t/helper/test-index-version.c14
-rw-r--r--t/helper/test-line-buffer.c91
-rw-r--r--t/helper/test-match-trees.c26
-rw-r--r--t/helper/test-mergesort.c52
-rw-r--r--t/helper/test-mktemp.c14
-rw-r--r--t/helper/test-parse-options.c104
-rw-r--r--t/helper/test-path-utils.c262
-rw-r--r--t/helper/test-prio-queue.c39
-rw-r--r--t/helper/test-read-cache.c13
-rw-r--r--t/helper/test-regex.c20
-rw-r--r--t/helper/test-revision-walking.c68
-rw-r--r--t/helper/test-run-command.c87
-rw-r--r--t/helper/test-scrap-cache-tree.c17
-rw-r--r--t/helper/test-sha1-array.c34
-rw-r--r--t/helper/test-sha1.c56
-rwxr-xr-xt/helper/test-sha1.sh83
-rw-r--r--t/helper/test-sigchain.c22
-rw-r--r--t/helper/test-string-list.c103
-rw-r--r--t/helper/test-submodule-config.c76
-rw-r--r--t/helper/test-subprocess.c19
-rw-r--r--t/helper/test-svn-fe.c52
-rw-r--r--t/helper/test-urlmatch-normalization.c50
-rw-r--r--t/helper/test-wildmatch.c21
-rw-r--r--t/lib-git-p4.sh33
-rwxr-xr-xt/lib-gpg.sh5
-rwxr-xr-xt/t0300-credentials.sh11
-rwxr-xr-xt/t1020-subdirectory.sh10
-rwxr-xr-xt/t1300-repo-config.sh14
-rwxr-xr-xt/t1410-reflog.sh10
-rwxr-xr-xt/t1430-bad-ref-name.sh132
-rwxr-xr-xt/t2025-worktree-add.sh12
-rwxr-xr-xt/t3033-merge-toplevel.sh16
-rwxr-xr-xt/t3200-branch.sh29
-rwxr-xr-xt/t3203-branch-output.sh12
-rwxr-xr-xt/t3404-rebase-interactive.sh26
-rwxr-xr-xt/t3412-rebase-root.sh2
-rwxr-xr-xt/t4001-diff-rename.sh136
-rwxr-xr-xt/t4013-diff-various.sh2
-rwxr-xr-xt/t4014-format-patch.sh4
-rwxr-xr-xt/t4047-diff-dirstat.sh3
-rwxr-xr-xt/t4200-rerere.sh170
-rwxr-xr-xt/t4201-shortlog.sh2
-rwxr-xr-xt/t4202-log.sh8
-rwxr-xr-xt/t4213-log-tabexpand.sh105
-rwxr-xr-xt/t5400-send-pack.sh12
-rwxr-xr-xt/t5500-fetch-pack.sh6
-rwxr-xr-xt/t5504-fetch-receive-strict.sh10
-rwxr-xr-xt/t5520-pull.sh74
-rwxr-xr-xt/t5521-pull-options.sh21
-rwxr-xr-xt/t5526-fetch-submodules.sh14
-rwxr-xr-xt/t5532-fetch-proxy.sh21
-rwxr-xr-xt/t5550-http-fetch-dumb.sh17
-rwxr-xr-xt/t5601-clone.sh2
-rwxr-xr-xt/t6009-rev-list-parent.sh4
-rwxr-xr-xt/t6010-merge-base.sh6
-rwxr-xr-xt/t6012-rev-list-simplify.sh2
-rwxr-xr-xt/t6026-merge-attr.sh3
-rwxr-xr-xt/t6029-merge-subtree.sh2
-rwxr-xr-xt/t6044-merge-unrelated-index-changes.sh153
-rwxr-xr-xt/t6101-rev-parse-parents.sh2
-rwxr-xr-xt/t6302-for-each-ref-filter.sh95
-rwxr-xr-xt/t7001-mv.sh16
-rwxr-xr-xt/t7004-tag.sh41
-rwxr-xr-xt/t7030-verify-tag.sh13
-rwxr-xr-xt/t7400-submodule-basic.sh45
-rwxr-xr-xt/t7406-submodule-update.sh109
-rwxr-xr-xt/t7407-submodule-foreach.sh49
-rwxr-xr-xt/t7412-submodule--helper.sh26
-rwxr-xr-xt/t7501-commit.sh20
-rwxr-xr-xt/t7605-merge-resolve.sh6
-rwxr-xr-xt/t8003-blame-corner-cases.sh14
-rwxr-xr-xt/t9400-git-cvsserver-server.sh3
-rwxr-xr-xt/t9826-git-p4-keep-empty-commits.sh60
-rwxr-xr-xt/t9828-git-p4-map-user.sh61
-rwxr-xr-xt/t9829-git-p4-jobs.sh99
-rwxr-xr-xt/t9903-bash-prompt.sh2
-rw-r--r--t/test-lib.sh4
89 files changed, 3874 insertions, 196 deletions
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..4dad7095f1
--- /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)
+{
+ unsigned char hash1[20], hash2[20], shifted[20];
+ struct tree *one, *two;
+
+ setup_git_directory();
+
+ if (get_sha1(av[1], hash1))
+ die("cannot parse %s as an object name", av[1]);
+ if (get_sha1(av[2], hash2))
+ die("cannot parse %s as an object name", av[2]);
+ one = parse_tree_indirect(hash1);
+ if (!one)
+ die("not a tree-ish %s", av[1]);
+ two = parse_tree_indirect(hash2);
+ if (!two)
+ die("not a tree-ish %s", av[2]);
+
+ shift_tree(one->object.oid.hash, two->object.oid.hash, shifted, -1);
+ printf("shifted: %s\n", sha1_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..2c8c8f18ed
--- /dev/null
+++ b/t/helper/test-parse-options.c
@@ -0,0 +1,104 @@
+#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 = 0, dry_run = 0, quiet = 0;
+static char *string = NULL;
+static char *file = NULL;
+static int ambiguous;
+static struct string_list list;
+
+static int length_callback(const struct option *opt, const char *arg, int unset)
+{
+ printf("Callback: \"%s\", %d\n",
+ (arg ? arg : "not set"), 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;
+}
+
+int main(int argc, char **argv)
+{
+ const char *prefix = "prefix/";
+ const char *usage[] = {
+ "test-parse-options <options>",
+ NULL
+ };
+ 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, &timestamp, "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_END(),
+ };
+ int i;
+
+ argc = parse_options(argc, (const char **)argv, prefix, options, usage, 0);
+
+ printf("boolean: %d\n", boolean);
+ printf("integer: %d\n", integer);
+ printf("magnitude: %lu\n", magnitude);
+ printf("timestamp: %lu\n", timestamp);
+ printf("string: %s\n", string ? string : "(not set)");
+ printf("abbrev: %d\n", abbrev);
+ printf("verbose: %d\n", verbose);
+ printf("quiet: %s\n", quiet ? "yes" : "no");
+ printf("dry run: %s\n", dry_run ? "yes" : "no");
+ printf("file: %s\n", file ? file : "(not set)");
+
+ for (i = 0; i < list.nr; i++)
+ printf("list: %s\n", list.items[i].string);
+
+ for (i = 0; i < argc; i++)
+ printf("arg %02d: %s\n", i, argv[i]);
+
+ return 0;
+}
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..ce3536e4a0 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,7 +49,7 @@ 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() {
+time_in_seconds () {
python -c 'import time; print int(time.time())'
}
@@ -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,13 +195,13 @@ 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 &&
@@ -208,7 +217,7 @@ marshal_dump() {
#
# Construct a client with this list of View lines
#
-client_view() {
+client_view () {
(
cat <<-EOF &&
Client: $P4CLIENT
@@ -222,7 +231,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-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/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 3d6f1db9da..d934a24417 100755
--- a/t/t1300-repo-config.sh
+++ b/t/t1300-repo-config.sh
@@ -1087,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 &&
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/t2025-worktree-add.sh b/t/t2025-worktree-add.sh
index cbfa41ec61..3acb9926f2 100755
--- a/t/t2025-worktree-add.sh
+++ b/t/t2025-worktree-add.sh
@@ -213,4 +213,16 @@ test_expect_success 'local clone from linked checkout' '
( 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_done
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/t3200-branch.sh b/t/t3200-branch.sh
index a897248490..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)" &&
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/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 544f9ad508..d96d0e4c9b 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -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/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..eed2981b96 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 &&
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/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/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/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 9b9bec468a..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' '
diff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh
index a3e12d295a..44f3d5fb28 100755
--- a/t/t5504-fetch-receive-strict.sh
+++ b/t/t5504-fetch-receive-strict.sh
@@ -100,11 +100,8 @@ 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_cmp exp act ||
- ! test -s act
- }
+ test_must_fail git push --porcelain dst master:refs/heads/test >act &&
+ test_cmp exp act
'
test_expect_success 'push with transfer.fsckobjects' '
@@ -114,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/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..48e2ab62da 100755
--- a/t/t5550-http-fetch-dumb.sh
+++ b/t/t5550-http-fetch-dumb.sh
@@ -91,6 +91,23 @@ test_expect_success 'configured username does not override URL' '
expect_askpass pass user@host
'
+test_expect_success 'cmdline credential config passes into 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"
+ ) &&
+ 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.$HTTP_URL.username=user@host" \
+ clone --recursive super super-clone &&
+ expect_askpass pass user@host
+'
+
test_expect_success 'fetch changes via http' '
echo content >>file &&
git commit -a -m two &&
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index c1efb8e445..150aeaf713 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 &&
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/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/t6044-merge-unrelated-index-changes.sh b/t/t6044-merge-unrelated-index-changes.sh
new file mode 100755
index 0000000000..20a3ffed69
--- /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' '
+ 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 &&
+ 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/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh
index bcf472bf51..70afb44271 100755
--- a/t/t6302-for-each-ref-filter.sh
+++ b/t/t6302-for-each-ref-filter.sh
@@ -5,11 +5,14 @@ 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_prepare_expect () {
+ if test_have_prereq GPG
+ then
+ cat
+ else
+ sed '/signed/d'
+ fi
+}
test_expect_success 'setup some history and refs' '
test_commit one &&
@@ -17,8 +20,13 @@ test_expect_success 'setup some history and refs' '
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 &&
+ if test_have_prereq GPG
+ then
+ git tag -s -m "A signed tag" signed-tag &&
+ git tag -s -m "Signed doubly" doubly-signed-tag signed-tag
+ fi &&
git checkout master &&
git update-ref refs/odd/spot master
'
@@ -34,8 +42,9 @@ test_expect_success 'filtering with --points-at' '
'
test_expect_success 'check signed tags with --points-at' '
- sed -e "s/Z$//" >expect <<-\EOF &&
+ test_prepare_expect <<-\EOF | sed -e "s/Z$//" >expect &&
refs/heads/side Z
+ refs/tags/annotated-tag four
refs/tags/four Z
refs/tags/signed-tag four
EOF
@@ -56,9 +65,11 @@ test_expect_success 'filtering with --merged' '
'
test_expect_success 'filtering with --no-merged' '
- cat >expect <<-\EOF &&
+ test_prepare_expect >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
@@ -67,11 +78,13 @@ test_expect_success 'filtering with --no-merged' '
'
test_expect_success 'filtering with --contains' '
- cat >expect <<-\EOF &&
+ test_prepare_expect >expect <<-\EOF &&
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
@@ -86,11 +99,13 @@ test_expect_success '%(color) must fail' '
'
test_expect_success 'left alignment is default' '
- cat >expect <<-\EOF &&
+ test_prepare_expect >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/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
@@ -102,11 +117,13 @@ test_expect_success 'left alignment is default' '
'
test_expect_success 'middle alignment' '
- cat >expect <<-\EOF &&
+ test_prepare_expect >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/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
@@ -118,11 +135,13 @@ test_expect_success 'middle alignment' '
'
test_expect_success 'right alignment' '
- cat >expect <<-\EOF &&
+ test_prepare_expect >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/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,11 +152,13 @@ test_expect_success 'right alignment' '
test_cmp expect actual
'
-cat >expect <<-\EOF
+test_prepare_expect >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/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
@@ -178,11 +199,13 @@ EOF
# Individual atoms inside %(align:...) and %(end) must not be quoted.
test_expect_success 'alignment with format quote' "
- cat >expect <<-\EOF &&
+ test_prepare_expect >expect <<-\EOF &&
|' '\''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| '\'' '|
@@ -194,11 +217,13 @@ test_expect_success 'alignment with format quote' "
"
test_expect_success 'nested alignment with quote formatting' "
- cat >expect <<-\EOF &&
+ test_prepare_expect >expect <<-\EOF &&
|' master '|
|' side '|
|' odd/spot '|
- |' double-tag '|
+ |' annotated-tag '|
+ |'doubly-annotated-tag '|
+ |'doubly-signed-tag '|
|' four '|
|' one '|
|' signed-tag '|
@@ -210,14 +235,16 @@ test_expect_success 'nested alignment with quote formatting' "
"
test_expect_success 'check `%(contents:lines=1)`' '
- cat >expect <<-\EOF &&
+ test_prepare_expect >expect <<-\EOF &&
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
@@ -226,11 +253,13 @@ test_expect_success 'check `%(contents:lines=1)`' '
'
test_expect_success 'check `%(contents:lines=0)`' '
- cat >expect <<-\EOF &&
+ test_prepare_expect >expect <<-\EOF &&
master |
side |
odd/spot |
- double-tag |
+ annotated-tag |
+ doubly-annotated-tag |
+ doubly-signed-tag |
four |
one |
signed-tag |
@@ -242,14 +271,16 @@ test_expect_success 'check `%(contents:lines=0)`' '
'
test_expect_success 'check `%(contents:lines=99999)`' '
- cat >expect <<-\EOF &&
+ test_prepare_expect >expect <<-\EOF &&
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 4008faead8..4a2570ed95 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -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/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/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index e1abd19230..f99f674ac7 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -462,7 +462,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 +480,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 +818,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 &&
diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
index 68ea31d693..fd741f506f 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,35 @@ 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 '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'
+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
+
+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
+ ) &&
+ test_cmp expect actual
+'
+
apos="'";
test_expect_success 'submodule update does not fetch already present commits' '
(cd submodule &&
@@ -311,16 +344,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 +850,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/t7412-submodule--helper.sh b/t/t7412-submodule--helper.sh
new file mode 100755
index 0000000000..149d42864f
--- /dev/null
+++ b/t/t7412-submodule--helper.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+#
+# Copyright (c) 2016 Jacob Keller
+#
+
+test_description='Basic plumbing support of submodule--helper
+
+This test verifies the submodule--helper plumbing command used to implement
+git-submodule.
+'
+
+. ./test-lib.sh
+
+test_expect_success 'sanitize-config clears configuration' '
+ git -c user.name="Some User" submodule--helper sanitize-config >actual &&
+ test_must_be_empty actual
+'
+
+sq="'"
+test_expect_success 'sanitize-config keeps credential.helper' '
+ git -c credential.helper=helper submodule--helper sanitize-config >actual &&
+ echo "${sq}credential.helper=helper${sq}" >expect &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh
index 63e04277f9..900f7de05a 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 &&
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/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/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/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.sh b/t/test-lib.sh
index 79afa8748e..286c5f33d1 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -854,10 +854,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