summaryrefslogtreecommitdiff
path: root/t
diff options
context:
space:
mode:
Diffstat (limited to 't')
-rw-r--r--t/helper/.gitignore3
-rw-r--r--t/helper/test-date.c12
-rw-r--r--t/helper/test-drop-caches.c164
-rw-r--r--t/helper/test-dump-fsmonitor.c21
-rw-r--r--t/helper/test-hashmap.c37
-rw-r--r--t/helper/test-line-buffer.c32
-rw-r--r--t/helper/test-parse-options.c2
-rw-r--r--t/helper/test-ref-store.c28
-rw-r--r--t/helper/test-submodule-config.c17
-rw-r--r--t/helper/test-write-cache.c23
-rwxr-xr-xt/lib-credential.sh19
-rwxr-xr-xt/lib-gpg.sh2
-rwxr-xr-xt/lib-submodule-update.sh581
-rwxr-xr-xt/perf/aggregate.perl11
-rw-r--r--t/perf/lib-pack.sh25
-rwxr-xr-xt/perf/p0007-write-cache.sh29
-rwxr-xr-xt/perf/p4211-line-log.sh4
-rwxr-xr-xt/perf/p5550-fetch-tags.sh25
-rwxr-xr-xt/perf/p5551-fetch-rescan.sh55
-rwxr-xr-xt/perf/p7519-fsmonitor.sh184
-rw-r--r--t/perf/perf-lib.sh4
-rwxr-xr-xt/perf/run89
-rwxr-xr-xt/t0000-basic.sh25
-rwxr-xr-xt/t0001-init.sh12
-rw-r--r--t/t0021/rot13-filter.pl127
-rwxr-xr-xt/t0025-crlf-renormalize.sh30
-rwxr-xr-xt/t0027-auto-crlf.sh2
-rwxr-xr-xt/t0040-parse-options.sh2
-rwxr-xr-xt/t1004-read-tree-m-u-wf.sh2
-rwxr-xr-xt/t1200-tutorial.sh268
-rwxr-xr-xt/t1300-repo-config.sh30
-rwxr-xr-xt/t1400-update-ref.sh5
-rwxr-xr-xt/t1401-symbolic-ref.sh26
-rwxr-xr-xt/t1402-check-ref-format.sh16
-rwxr-xr-xt/t1404-update-ref-errors.sh214
-rwxr-xr-xt/t1407-worktree-ref-store.sh30
-rwxr-xr-xt/t1408-packed-refs.sh42
-rwxr-xr-xt/t1409-avoid-packing-refs.sh118
-rwxr-xr-xt/t1430-bad-ref-name.sh43
-rwxr-xr-xt/t1500-rev-parse.sh15
-rwxr-xr-xt/t1502-rev-parse-parseopt.sh112
-rwxr-xr-xt/t1700-split-index.sh1
-rwxr-xr-xt/t3200-branch.sh314
-rwxr-xr-xt/t3210-pack-refs.sh42
-rwxr-xr-xt/t3308-notes-merge.sh2
-rwxr-xr-xt/t3310-notes-merge-manual-resolve.sh8
-rwxr-xr-xt/t3320-notes-merge-worktrees.sh2
-rwxr-xr-xt/t3400-rebase.sh22
-rwxr-xr-xt/t3404-rebase-interactive.sh33
-rwxr-xr-xt/t3415-rebase-autosquash.sh28
-rwxr-xr-xt/t3426-rebase-submodule.sh17
-rwxr-xr-xt/t3512-cherry-pick-submodule.sh36
-rwxr-xr-xt/t3600-rm.sh7
-rwxr-xr-xt/t3700-add.sh5
-rwxr-xr-xt/t3903-stash.sh34
-rwxr-xr-xt/t3905-stash-include-untracked.sh17
-rwxr-xr-xt/t4013-diff-various.sh12
-rw-r--r--t/t4013/diff.diff-tree_--stat_initial_mode4
-rw-r--r--t/t4013/diff.diff-tree_--summary_initial_mode3
-rw-r--r--t/t4013/diff.diff-tree_initial_mode3
-rw-r--r--t/t4013/diff.log_--decorate=full_--all6
-rw-r--r--t/t4013/diff.log_--decorate_--all6
-rwxr-xr-xt/t4015-diff-whitespace.sh742
-rwxr-xr-xt/t4027-diff-submodule.sh67
-rwxr-xr-xt/t4051-diff-function-context.sh4
-rw-r--r--t/t4051/hello.c3
-rwxr-xr-xt/t4059-diff-submodule-not-initialized.sh2
-rwxr-xr-xt/t4107-apply-ignore-whitespace.sh14
-rwxr-xr-xt/t4200-rerere.sh57
-rwxr-xr-xt/t4201-shortlog.sh5
-rwxr-xr-xt/t4205-log-pretty-formats.sh51
-rwxr-xr-xt/t5304-prune.sh37
-rwxr-xr-xt/t5520-pull.sh4
-rwxr-xr-xt/t5521-pull-options.sh45
-rwxr-xr-xt/t5526-fetch-submodules.sh77
-rwxr-xr-xt/t5531-deep-submodule-push.sh10
-rwxr-xr-xt/t5534-push-signed.sh7
-rwxr-xr-xt/t5545-push-options.sh77
-rwxr-xr-xt/t5572-pull-submodule.sh32
-rwxr-xr-xt/t5580-clone-push-unc.sh14
-rwxr-xr-xt/t5601-clone.sh2
-rwxr-xr-xt/t6007-rev-list-cherry-pick-file.sh32
-rwxr-xr-xt/t6013-rev-list-reverse-parents.sh4
-rwxr-xr-xt/t6030-bisect-porcelain.sh17
-rwxr-xr-xt/t6040-tracking-info.sh24
-rwxr-xr-xt/t6120-describe.sh83
-rwxr-xr-xt/t6300-for-each-ref.sh122
-rwxr-xr-xt/t7001-mv.sh29
-rwxr-xr-xt/t7004-tag.sh7
-rwxr-xr-xt/t7005-editor.sh6
-rwxr-xr-xt/t7006-pager.sh52
-rwxr-xr-xt/t7061-wtstatus-ignore.sh11
-rwxr-xr-xt/t7102-reset.sh4
-rwxr-xr-xt/t7201-co.sh4
-rwxr-xr-xt/t7301-clean-interactive.sh11
-rwxr-xr-xt/t7400-submodule-basic.sh12
-rwxr-xr-xt/t7405-submodule-merge.sh2
-rwxr-xr-xt/t7406-submodule-update.sh8
-rwxr-xr-xt/t7411-submodule-config.sh87
-rwxr-xr-xt/t7504-commit-msg-hook.sh64
-rwxr-xr-xt/t7506-status-submodule.sh4
-rwxr-xr-xt/t7508-status.sh10
-rwxr-xr-xt/t7513-interpret-trailers.sh142
-rwxr-xr-xt/t7519-status-fsmonitor.sh317
-rwxr-xr-xt/t7519/fsmonitor-all24
-rwxr-xr-xt/t7519/fsmonitor-none22
-rwxr-xr-xt/t7519/fsmonitor-watchman133
-rwxr-xr-xt/t7520-ignored-hook-warning.sh41
-rwxr-xr-xt/t7521-ignored-mode.sh233
-rwxr-xr-xt/t7600-merge.sh6
-rwxr-xr-xt/t7610-mergetool.sh4
-rwxr-xr-xt/t7614-merge-signoff.sh69
-rwxr-xr-xt/t7810-grep.sh41
-rwxr-xr-xt/t9001-send-email.sh6
-rwxr-xr-xt/t9010-svn-fe.sh55
-rwxr-xr-xt/t9114-git-svn-dcommit-merge.sh4
-rwxr-xr-xt/t9902-completion.sh4
-rw-r--r--t/test-lib.sh38
118 files changed, 4804 insertions, 1266 deletions
diff --git a/t/helper/.gitignore b/t/helper/.gitignore
index 721650256e..d02f9b39ac 100644
--- a/t/helper/.gitignore
+++ b/t/helper/.gitignore
@@ -3,7 +3,9 @@
/test-config
/test-date
/test-delta
+/test-drop-caches
/test-dump-cache-tree
+/test-dump-fsmonitor
/test-dump-split-index
/test-dump-untracked-cache
/test-fake-ssh
@@ -35,3 +37,4 @@
/test-svn-fe
/test-urlmatch-normalization
/test-wildmatch
+/test-write-cache
diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index f414a3ac67..ac83687970 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -5,6 +5,7 @@ static const char *usage_msg = "\n"
" test-date show:<format> [time_t]...\n"
" test-date parse [date]...\n"
" test-date approxidate [date]...\n"
+" test-date timestamp [date]...\n"
" test-date is64bit\n"
" test-date time_t-is64bit\n";
@@ -71,6 +72,15 @@ static void parse_approxidate(const char **argv, struct timeval *now)
}
}
+static void parse_approx_timestamp(const char **argv, struct timeval *now)
+{
+ for (; *argv; argv++) {
+ timestamp_t t;
+ t = approxidate_relative(*argv, now);
+ printf("%s -> %"PRItime"\n", *argv, t);
+ }
+}
+
int cmd_main(int argc, const char **argv)
{
struct timeval now;
@@ -95,6 +105,8 @@ int cmd_main(int argc, const char **argv)
parse_dates(argv+1, &now);
else if (!strcmp(*argv, "approxidate"))
parse_approxidate(argv+1, &now);
+ else if (!strcmp(*argv, "timestamp"))
+ parse_approx_timestamp(argv+1, &now);
else if (!strcmp(*argv, "is64bit"))
return sizeof(timestamp_t) == 8 ? 0 : 1;
else if (!strcmp(*argv, "time_t-is64bit"))
diff --git a/t/helper/test-drop-caches.c b/t/helper/test-drop-caches.c
new file mode 100644
index 0000000000..bd1a857d52
--- /dev/null
+++ b/t/helper/test-drop-caches.c
@@ -0,0 +1,164 @@
+#include "git-compat-util.h"
+
+#if defined(GIT_WINDOWS_NATIVE)
+
+static int cmd_sync(void)
+{
+ char Buffer[MAX_PATH];
+ DWORD dwRet;
+ char szVolumeAccessPath[] = "\\\\.\\X:";
+ HANDLE hVolWrite;
+ int success = 0;
+
+ dwRet = GetCurrentDirectory(MAX_PATH, Buffer);
+ if ((0 == dwRet) || (dwRet > MAX_PATH))
+ return error("Error getting current directory");
+
+ if ((Buffer[0] < 'A') || (Buffer[0] > 'Z'))
+ return error("Invalid drive letter '%c'", Buffer[0]);
+
+ szVolumeAccessPath[4] = Buffer[0];
+ hVolWrite = CreateFile(szVolumeAccessPath, GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+ if (INVALID_HANDLE_VALUE == hVolWrite)
+ return error("Unable to open volume for writing, need admin access");
+
+ success = FlushFileBuffers(hVolWrite);
+ if (!success)
+ error("Unable to flush volume");
+
+ CloseHandle(hVolWrite);
+
+ return !success;
+}
+
+#define STATUS_SUCCESS (0x00000000L)
+#define STATUS_PRIVILEGE_NOT_HELD (0xC0000061L)
+
+typedef enum _SYSTEM_INFORMATION_CLASS {
+ SystemMemoryListInformation = 80,
+} SYSTEM_INFORMATION_CLASS;
+
+typedef enum _SYSTEM_MEMORY_LIST_COMMAND {
+ MemoryCaptureAccessedBits,
+ MemoryCaptureAndResetAccessedBits,
+ MemoryEmptyWorkingSets,
+ MemoryFlushModifiedList,
+ MemoryPurgeStandbyList,
+ MemoryPurgeLowPriorityStandbyList,
+ MemoryCommandMax
+} SYSTEM_MEMORY_LIST_COMMAND;
+
+static BOOL GetPrivilege(HANDLE TokenHandle, LPCSTR lpName, int flags)
+{
+ BOOL bResult;
+ DWORD dwBufferLength;
+ LUID luid;
+ TOKEN_PRIVILEGES tpPreviousState;
+ TOKEN_PRIVILEGES tpNewState;
+
+ dwBufferLength = 16;
+ bResult = LookupPrivilegeValueA(0, lpName, &luid);
+ if (bResult) {
+ tpNewState.PrivilegeCount = 1;
+ tpNewState.Privileges[0].Luid = luid;
+ tpNewState.Privileges[0].Attributes = 0;
+ bResult = AdjustTokenPrivileges(TokenHandle, 0, &tpNewState,
+ (DWORD)((LPBYTE)&(tpNewState.Privileges[1]) - (LPBYTE)&tpNewState),
+ &tpPreviousState, &dwBufferLength);
+ if (bResult) {
+ tpPreviousState.PrivilegeCount = 1;
+ tpPreviousState.Privileges[0].Luid = luid;
+ tpPreviousState.Privileges[0].Attributes = flags != 0 ? 2 : 0;
+ bResult = AdjustTokenPrivileges(TokenHandle, 0, &tpPreviousState,
+ dwBufferLength, 0, 0);
+ }
+ }
+ return bResult;
+}
+
+static int cmd_dropcaches(void)
+{
+ HANDLE hProcess = GetCurrentProcess();
+ HANDLE hToken;
+ HMODULE ntdll;
+ DWORD(WINAPI *NtSetSystemInformation)(INT, PVOID, ULONG);
+ SYSTEM_MEMORY_LIST_COMMAND command;
+ int status;
+
+ if (!OpenProcessToken(hProcess, TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken))
+ return error("Can't open current process token");
+
+ if (!GetPrivilege(hToken, "SeProfileSingleProcessPrivilege", 1))
+ return error("Can't get SeProfileSingleProcessPrivilege");
+
+ CloseHandle(hToken);
+
+ ntdll = LoadLibrary("ntdll.dll");
+ if (!ntdll)
+ return error("Can't load ntdll.dll, wrong Windows version?");
+
+ NtSetSystemInformation =
+ (DWORD(WINAPI *)(INT, PVOID, ULONG))GetProcAddress(ntdll, "NtSetSystemInformation");
+ if (!NtSetSystemInformation)
+ return error("Can't get function addresses, wrong Windows version?");
+
+ command = MemoryPurgeStandbyList;
+ status = NtSetSystemInformation(
+ SystemMemoryListInformation,
+ &command,
+ sizeof(SYSTEM_MEMORY_LIST_COMMAND)
+ );
+ if (status == STATUS_PRIVILEGE_NOT_HELD)
+ error("Insufficient privileges to purge the standby list, need admin access");
+ else if (status != STATUS_SUCCESS)
+ error("Unable to execute the memory list command %d", status);
+
+ FreeLibrary(ntdll);
+
+ return status;
+}
+
+#elif defined(__linux__)
+
+static int cmd_sync(void)
+{
+ return system("sync");
+}
+
+static int cmd_dropcaches(void)
+{
+ return system("echo 3 | sudo tee /proc/sys/vm/drop_caches");
+}
+
+#elif defined(__APPLE__)
+
+static int cmd_sync(void)
+{
+ return system("sync");
+}
+
+static int cmd_dropcaches(void)
+{
+ return system("sudo purge");
+}
+
+#else
+
+static int cmd_sync(void)
+{
+ return 0;
+}
+
+static int cmd_dropcaches(void)
+{
+ return error("drop caches not implemented on this platform");
+}
+
+#endif
+
+int cmd_main(int argc, const char **argv)
+{
+ cmd_sync();
+ return cmd_dropcaches();
+}
diff --git a/t/helper/test-dump-fsmonitor.c b/t/helper/test-dump-fsmonitor.c
new file mode 100644
index 0000000000..ad452707e8
--- /dev/null
+++ b/t/helper/test-dump-fsmonitor.c
@@ -0,0 +1,21 @@
+#include "cache.h"
+
+int cmd_main(int ac, const char **av)
+{
+ struct index_state *istate = &the_index;
+ int i;
+
+ setup_git_directory();
+ if (do_read_index(istate, get_index_file(), 0) < 0)
+ die("unable to read index file");
+ if (!istate->fsmonitor_last_update) {
+ printf("no fsmonitor\n");
+ return 0;
+ }
+ printf("fsmonitor last update %"PRIuMAX"\n", (uintmax_t)istate->fsmonitor_last_update);
+
+ for (i = 0; i < istate->cache_nr; i++)
+ printf((istate->cache[i]->ce_flags & CE_FSMONITOR_VALID) ? "+" : "-");
+
+ return 0;
+}
diff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c
index 095d7395f3..1145d51671 100644
--- a/t/helper/test-hashmap.c
+++ b/t/helper/test-hashmap.c
@@ -13,20 +13,20 @@ static const char *get_value(const struct test_entry *e)
return e->key + strlen(e->key) + 1;
}
-static int test_entry_cmp(const void *unused_cmp_data,
- const struct test_entry *e1,
- const struct test_entry *e2,
- const char* key)
+static int test_entry_cmp(const void *cmp_data,
+ const void *entry,
+ const void *entry_or_key,
+ const void *keydata)
{
- return strcmp(e1->key, key ? key : e2->key);
-}
-
-static int test_entry_cmp_icase(const void *unused_cmp_data,
- const struct test_entry *e1,
- const struct test_entry *e2,
- const char* key)
-{
- return strcasecmp(e1->key, key ? key : e2->key);
+ const int ignore_case = cmp_data ? *((int *)cmp_data) : 0;
+ const struct test_entry *e1 = entry;
+ const struct test_entry *e2 = entry_or_key;
+ const char *key = keydata;
+
+ if (ignore_case)
+ return strcasecmp(e1->key, key ? key : e2->key);
+ else
+ return strcmp(e1->key, key ? key : e2->key);
}
static struct test_entry *alloc_test_entry(int hash, char *key, int klen,
@@ -96,8 +96,7 @@ static void perf_hashmap(unsigned int method, unsigned int rounds)
if (method & TEST_ADD) {
/* test adding to the map */
for (j = 0; j < rounds; j++) {
- hashmap_init(&map, (hashmap_cmp_fn) test_entry_cmp,
- NULL, 0);
+ hashmap_init(&map, test_entry_cmp, NULL, 0);
/* add entries */
for (i = 0; i < TEST_SIZE; i++) {
@@ -109,7 +108,7 @@ static void perf_hashmap(unsigned int method, unsigned int rounds)
}
} else {
/* test map lookups */
- hashmap_init(&map, (hashmap_cmp_fn) test_entry_cmp, NULL, 0);
+ hashmap_init(&map, test_entry_cmp, NULL, 0);
/* fill the map (sparsely if specified) */
j = (method & TEST_SPARSE) ? TEST_SIZE / 10 : TEST_SIZE;
@@ -151,8 +150,7 @@ int cmd_main(int argc, const char **argv)
/* init hash map */
icase = argc > 1 && !strcmp("ignorecase", argv[1]);
- hashmap_init(&map, (hashmap_cmp_fn) (icase ? test_entry_cmp_icase
- : test_entry_cmp), NULL, 0);
+ hashmap_init(&map, test_entry_cmp, &icase, 0);
/* process commands from stdin */
while (fgets(line, sizeof(line), stdin)) {
@@ -237,7 +235,8 @@ int cmd_main(int argc, const char **argv)
} else if (!strcmp("size", cmd)) {
/* print table sizes */
- printf("%u %u\n", map.tablesize, map.size);
+ printf("%u %u\n", map.tablesize,
+ hashmap_get_size(&map));
} else if (!strcmp("intern", cmd) && l1) {
diff --git a/t/helper/test-line-buffer.c b/t/helper/test-line-buffer.c
index 81575fe2ab..078dd7e29d 100644
--- a/t/helper/test-line-buffer.c
+++ b/t/helper/test-line-buffer.c
@@ -17,27 +17,17 @@ static uint32_t strtouint32(const char *s)
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:
+ 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);
+ } else if (starts_with(command, "copy ")) {
+ buffer_copy_bytes(buf, strtouint32(arg));
+ } else if (starts_with(command, "skip ")) {
+ buffer_skip_bytes(buf, strtouint32(arg));
+ } else {
die("unrecognized command: %s", command);
}
}
diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c
index 75fe883aac..630c76d127 100644
--- a/t/helper/test-parse-options.c
+++ b/t/helper/test-parse-options.c
@@ -99,6 +99,8 @@ int cmd_main(int argc, const char **argv)
const char *prefix = "prefix/";
const char *usage[] = {
"test-parse-options <options>",
+ "",
+ "A helper function for the parse-options API.",
NULL
};
struct string_list expect = STRING_LIST_INIT_NODUP;
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
index 05d8c4d8af..7120634b04 100644
--- a/t/helper/test-ref-store.c
+++ b/t/helper/test-ref-store.c
@@ -72,12 +72,12 @@ static int cmd_pack_refs(struct ref_store *refs, const char **argv)
static int cmd_peel_ref(struct ref_store *refs, const char **argv)
{
const char *refname = notnull(*argv++, "refname");
- unsigned char sha1[20];
+ struct object_id oid;
int ret;
- ret = refs_peel_ref(refs, refname, sha1);
+ ret = refs_peel_ref(refs, refname, &oid);
if (!ret)
- puts(sha1_to_hex(sha1));
+ puts(oid_to_hex(&oid));
return ret;
}
@@ -127,15 +127,15 @@ static int cmd_for_each_ref(struct ref_store *refs, const char **argv)
static int cmd_resolve_ref(struct ref_store *refs, const char **argv)
{
- unsigned char sha1[20];
+ struct object_id oid;
const char *refname = notnull(*argv++, "refname");
int resolve_flags = arg_flags(*argv++, "resolve-flags");
int flags;
const char *ref;
ref = refs_resolve_ref_unsafe(refs, refname, resolve_flags,
- sha1, &flags);
- printf("%s %s 0x%x\n", sha1_to_hex(sha1), ref, flags);
+ &oid, &flags);
+ printf("%s %s 0x%x\n", oid_to_hex(&oid), ref ? ref : "(null)", flags);
return ref ? 0 : 1;
}
@@ -218,12 +218,12 @@ static int cmd_delete_ref(struct ref_store *refs, const char **argv)
const char *refname = notnull(*argv++, "refname");
const char *sha1_buf = notnull(*argv++, "old-sha1");
unsigned int flags = arg_flags(*argv++, "flags");
- unsigned char old_sha1[20];
+ struct object_id old_oid;
- if (get_sha1_hex(sha1_buf, old_sha1))
+ if (get_oid_hex(sha1_buf, &old_oid))
die("not sha-1");
- return refs_delete_ref(refs, msg, refname, old_sha1, flags);
+ return refs_delete_ref(refs, msg, refname, &old_oid, flags);
}
static int cmd_update_ref(struct ref_store *refs, const char **argv)
@@ -233,15 +233,15 @@ static int cmd_update_ref(struct ref_store *refs, const char **argv)
const char *new_sha1_buf = notnull(*argv++, "old-sha1");
const char *old_sha1_buf = notnull(*argv++, "old-sha1");
unsigned int flags = arg_flags(*argv++, "flags");
- unsigned char old_sha1[20];
- unsigned char new_sha1[20];
+ struct object_id old_oid;
+ struct object_id new_oid;
- if (get_sha1_hex(old_sha1_buf, old_sha1) ||
- get_sha1_hex(new_sha1_buf, new_sha1))
+ if (get_oid_hex(old_sha1_buf, &old_oid) ||
+ get_oid_hex(new_sha1_buf, &new_oid))
die("not sha-1");
return refs_update_ref(refs, msg, refname,
- new_sha1, old_sha1,
+ &new_oid, &old_oid,
flags, UPDATE_REFS_DIE_ON_ERR);
}
diff --git a/t/helper/test-submodule-config.c b/t/helper/test-submodule-config.c
index c6c57bba0d..f23db3b19a 100644
--- a/t/helper/test-submodule-config.c
+++ b/t/helper/test-submodule-config.c
@@ -10,11 +10,6 @@ static void die_usage(int argc, const char **argv, const char *msg)
exit(1);
}
-static int git_test_config(const char *var, const char *value, void *cb)
-{
- return parse_submodule_config_option(var, value);
-}
-
int cmd_main(int argc, const char **argv)
{
const char **arg = argv;
@@ -37,11 +32,9 @@ int cmd_main(int argc, const char **argv)
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];
+ struct object_id commit_oid;
const struct submodule *submodule;
const char *commit;
const char *path_or_name;
@@ -50,14 +43,14 @@ int cmd_main(int argc, const char **argv)
path_or_name = arg[1];
if (commit[0] == '\0')
- hashclr(commit_sha1);
- else if (get_sha1(commit, commit_sha1) < 0)
+ oidclr(&commit_oid);
+ else if (get_oid(commit, &commit_oid) < 0)
die_usage(argc, argv, "Commit not found.");
if (lookup_name) {
- submodule = submodule_from_name(commit_sha1, path_or_name);
+ submodule = submodule_from_name(&commit_oid, path_or_name);
} else
- submodule = submodule_from_path(commit_sha1, path_or_name);
+ submodule = submodule_from_path(&commit_oid, path_or_name);
if (!submodule)
die_usage(argc, argv, "Submodule not found.");
diff --git a/t/helper/test-write-cache.c b/t/helper/test-write-cache.c
new file mode 100644
index 0000000000..b7ee039669
--- /dev/null
+++ b/t/helper/test-write-cache.c
@@ -0,0 +1,23 @@
+#include "cache.h"
+#include "lockfile.h"
+
+static struct lock_file index_lock;
+
+int cmd_main(int argc, const char **argv)
+{
+ int i, cnt = 1, lockfd;
+ if (argc == 2)
+ cnt = strtol(argv[1], NULL, 0);
+ setup_git_directory();
+ read_cache();
+ for (i = 0; i < cnt; i++) {
+ lockfd = hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
+ if (0 <= lockfd) {
+ write_locked_index(&the_index, &index_lock, COMMIT_LOCK);
+ } else {
+ rollback_lock_file(&index_lock);
+ }
+ }
+
+ return 0;
+}
diff --git a/t/lib-credential.sh b/t/lib-credential.sh
index d8e41f7ddd..937b831ea6 100755
--- a/t/lib-credential.sh
+++ b/t/lib-credential.sh
@@ -44,6 +44,7 @@ helper_test_clean() {
reject $1 https example.com user2
reject $1 http path.tld user
reject $1 https timeout.tld user
+ reject $1 https sso.tld
}
reject() {
@@ -250,6 +251,24 @@ helper_test() {
password=pass2
EOF
'
+
+ test_expect_success "helper ($HELPER) can store empty username" '
+ check approve $HELPER <<-\EOF &&
+ protocol=https
+ host=sso.tld
+ username=
+ password=
+ EOF
+ check fill $HELPER <<-\EOF
+ protocol=https
+ host=sso.tld
+ --
+ protocol=https
+ host=sso.tld
+ username=
+ password=
+ EOF
+ '
}
helper_test_timeout() {
diff --git a/t/lib-gpg.sh b/t/lib-gpg.sh
index 43679a4c64..a5d3b2cbaa 100755
--- a/t/lib-gpg.sh
+++ b/t/lib-gpg.sh
@@ -31,7 +31,7 @@ then
chmod 0700 ./gpghome &&
GNUPGHOME="$(pwd)/gpghome" &&
export GNUPGHOME &&
- (gpgconf --kill gpg-agent 2>&1 >/dev/null || : ) &&
+ (gpgconf --kill gpg-agent >/dev/null 2>&1 || : ) &&
gpg --homedir "${GNUPGHOME}" 2>/dev/null --import \
"$TEST_DIRECTORY"/lib-gpg/keyring.gpg &&
gpg --homedir "${GNUPGHOME}" 2>/dev/null --import-ownertrust \
diff --git a/t/lib-submodule-update.sh b/t/lib-submodule-update.sh
index 2d26f86800..38dadd2c29 100755
--- a/t/lib-submodule-update.sh
+++ b/t/lib-submodule-update.sh
@@ -306,9 +306,9 @@ test_submodule_content () {
# to protect the history!
#
-# Test that submodule contents are currently not updated when switching
-# between commits that change a submodule.
-test_submodule_switch () {
+# Internal function; use test_submodule_switch() or
+# test_submodule_forced_switch() instead.
+test_submodule_switch_common() {
command="$1"
######################### Appearing submodule #########################
# Switching to a commit letting a submodule appear creates empty dir ...
@@ -332,7 +332,7 @@ test_submodule_switch () {
test_submodule_content sub1 origin/add_sub1
)
'
- # ... and doesn't care if it already exists ...
+ # ... and doesn't care if it already exists.
test_expect_$RESULT "$command: added submodule leaves existing empty directory alone" '
prolog &&
reset_work_tree_to no_submodule &&
@@ -347,19 +347,6 @@ test_submodule_switch () {
test_submodule_content sub1 origin/add_sub1
)
'
- # ... unless there is an untracked file in its place.
- test_expect_success "$command: added submodule doesn't remove untracked unignored file with same name" '
- prolog &&
- reset_work_tree_to no_submodule &&
- (
- cd submodule_update &&
- git branch -t add_sub1 origin/add_sub1 &&
- >sub1 &&
- test_must_fail $command add_sub1 &&
- test_superproject_content origin/no_submodule &&
- test_must_be_empty sub1
- )
- '
# Replacing a tracked file with a submodule produces an empty
# directory ...
test_expect_$RESULT "$command: replace tracked file with submodule creates empty directory" '
@@ -441,6 +428,11 @@ test_submodule_switch () {
# submodule files with the newly checked out ones in the
# directory of the same name while it shouldn't.
RESULT="failure"
+ elif test "$KNOWN_FAILURE_FORCED_SWITCH_TESTS" = 1
+ then
+ # All existing tests that use test_submodule_forced_switch()
+ # require this.
+ RESULT="failure"
else
RESULT="success"
fi
@@ -522,7 +514,6 @@ test_submodule_switch () {
test_submodule_content sub1 origin/modify_sub1
)
'
-
# Updating a submodule to an invalid sha1 doesn't update the
# submodule's work tree, subsequent update will fail
test_expect_$RESULT "$command: modified submodule does not update submodule work tree to invalid commit" '
@@ -555,235 +546,269 @@ test_submodule_switch () {
'
}
-# Test that submodule contents are currently not updated when switching
-# between commits that change a submodule, but throwing away local changes in
+# Declares and invokes several tests that, in various situations, checks that
+# the provided transition function:
+# - succeeds in updating the worktree and index of a superproject to a target
+# commit, or fails atomically (depending on the test situation)
+# - if succeeds, the contents of submodule directories are unchanged
+# - if succeeds, once "git submodule update" is invoked, the contents of
+# submodule directories are updated
+#
+# If the command under test is known to not work with submodules in certain
+# conditions, set the appropriate KNOWN_FAILURE_* variable used in the tests
+# below to 1.
+#
+# Use as follows:
+#
+# my_func () {
+# target=$1
+# # Do something here that updates the worktree and index to match target,
+# # but not any submodule directories.
+# }
+# test_submodule_switch "my_func"
+test_submodule_switch () {
+ command="$1"
+ test_submodule_switch_common "$command"
+
+ # An empty directory does not prevent the creation of a submodule of
+ # the same name, but a file does.
+ test_expect_success "$command: added submodule doesn't remove untracked unignored file with same name" '
+ prolog &&
+ reset_work_tree_to no_submodule &&
+ (
+ cd submodule_update &&
+ git branch -t add_sub1 origin/add_sub1 &&
+ >sub1 &&
+ test_must_fail $command add_sub1 &&
+ test_superproject_content origin/no_submodule &&
+ test_must_be_empty sub1
+ )
+ '
+}
+
+# Same as test_submodule_switch(), except that throwing away local changes in
# the superproject is allowed.
test_submodule_forced_switch () {
command="$1"
- ######################### Appearing submodule #########################
- # Switching to a commit letting a submodule appear creates empty dir ...
- test_expect_success "$command: added submodule creates empty directory" '
+ KNOWN_FAILURE_FORCED_SWITCH_TESTS=1
+ test_submodule_switch_common "$command"
+
+ # When forced, a file in the superproject does not prevent creating a
+ # submodule of the same name.
+ test_expect_success "$command: added submodule does remove untracked unignored file with same name when forced" '
prolog &&
reset_work_tree_to no_submodule &&
(
cd submodule_update &&
git branch -t add_sub1 origin/add_sub1 &&
+ >sub1 &&
$command add_sub1 &&
test_superproject_content origin/add_sub1 &&
- test_dir_is_empty sub1 &&
- git submodule update --init --recursive &&
- test_submodule_content sub1 origin/add_sub1
+ test_dir_is_empty sub1
)
'
- # ... and doesn't care if it already exists ...
- test_expect_success "$command: added submodule leaves existing empty directory alone" '
+}
+
+# Test that submodule contents are correctly updated when switching
+# between commits that change a submodule.
+# Test that the following transitions are correctly handled:
+# (These tests are also above in the case where we expect no change
+# in the submodule)
+# - Updated submodule
+# - New submodule
+# - Removed submodule
+# - Directory containing tracked files replaced by submodule
+# - Submodule replaced by tracked files in directory
+# - Submodule replaced by tracked file with the same name
+# - tracked file replaced by submodule
+#
+# New test cases
+# - Removing a submodule with a git directory absorbs the submodules
+# git directory first into the superproject.
+
+# Internal function; use test_submodule_switch_recursing_with_args() or
+# test_submodule_forced_switch_recursing_with_args() instead.
+test_submodule_recursing_with_args_common() {
+ command="$1"
+
+ ######################### Appearing submodule #########################
+ # Switching to a commit letting a submodule appear checks it out ...
+ test_expect_success "$command: added submodule is checked out" '
prolog &&
- reset_work_tree_to no_submodule &&
+ reset_work_tree_to_interested no_submodule &&
(
cd submodule_update &&
git branch -t add_sub1 origin/add_sub1 &&
- mkdir sub1 &&
$command add_sub1 &&
test_superproject_content origin/add_sub1 &&
- test_dir_is_empty sub1 &&
- git submodule update --init --recursive &&
test_submodule_content sub1 origin/add_sub1
)
'
- # ... unless there is an untracked file in its place.
- test_expect_success "$command: added submodule does remove untracked unignored file with same name when forced" '
+ # ... ignoring an empty existing directory.
+ test_expect_success "$command: added submodule is checked out in empty dir" '
prolog &&
- reset_work_tree_to no_submodule &&
+ reset_work_tree_to_interested no_submodule &&
(
cd submodule_update &&
+ mkdir sub1 &&
git branch -t add_sub1 origin/add_sub1 &&
- >sub1 &&
$command add_sub1 &&
test_superproject_content origin/add_sub1 &&
- test_dir_is_empty sub1
+ test_submodule_content sub1 origin/add_sub1
)
'
- # Replacing a tracked file with a submodule produces an empty
- # directory ...
- test_expect_success "$command: replace tracked file with submodule creates empty directory" '
+ test_expect_success "$command: submodule branch is not changed, detach HEAD instead" '
prolog &&
- reset_work_tree_to replace_sub1_with_file &&
+ reset_work_tree_to_interested add_sub1 &&
+ (
+ cd submodule_update &&
+ git -C sub1 checkout -b keep_branch &&
+ git -C sub1 rev-parse HEAD >expect &&
+ git branch -t check-keep origin/modify_sub1 &&
+ $command check-keep &&
+ test_superproject_content origin/modify_sub1 &&
+ test_submodule_content sub1 origin/modify_sub1 &&
+ git -C sub1 rev-parse keep_branch >actual &&
+ test_cmp expect actual &&
+ test_must_fail git -C sub1 symbolic-ref HEAD
+ )
+ '
+
+ # Replacing a tracked file with a submodule produces a checked out submodule
+ test_expect_success "$command: replace tracked file with submodule checks out submodule" '
+ prolog &&
+ reset_work_tree_to_interested replace_sub1_with_file &&
(
cd submodule_update &&
git branch -t replace_file_with_sub1 origin/replace_file_with_sub1 &&
$command replace_file_with_sub1 &&
test_superproject_content origin/replace_file_with_sub1 &&
- test_dir_is_empty sub1 &&
- git submodule update --init --recursive &&
test_submodule_content sub1 origin/replace_file_with_sub1
)
'
- # ... as does removing a directory with tracked files with a
- # submodule.
+ # ... as does removing a directory with tracked files with a submodule.
test_expect_success "$command: replace directory with submodule" '
prolog &&
- reset_work_tree_to replace_sub1_with_directory &&
+ reset_work_tree_to_interested replace_sub1_with_directory &&
(
cd submodule_update &&
git branch -t replace_directory_with_sub1 origin/replace_directory_with_sub1 &&
$command replace_directory_with_sub1 &&
test_superproject_content origin/replace_directory_with_sub1 &&
- test_dir_is_empty sub1 &&
- git submodule update --init --recursive &&
test_submodule_content sub1 origin/replace_directory_with_sub1
)
'
######################## Disappearing submodule #######################
- # Removing a submodule doesn't remove its work tree ...
- test_expect_success "$command: removed submodule leaves submodule directory and its contents in place" '
+ # Removing a submodule removes its work tree ...
+ test_expect_success "$command: removed submodule removes submodules working tree" '
prolog &&
- reset_work_tree_to add_sub1 &&
+ reset_work_tree_to_interested add_sub1 &&
(
cd submodule_update &&
git branch -t remove_sub1 origin/remove_sub1 &&
$command remove_sub1 &&
test_superproject_content origin/remove_sub1 &&
- test_submodule_content sub1 origin/add_sub1
+ ! test -e sub1
)
'
- # ... especially when it contains a .git directory.
- test_expect_success "$command: removed submodule leaves submodule containing a .git directory alone" '
+ # ... absorbing a .git directory along the way.
+ test_expect_success "$command: removed submodule absorbs submodules .git directory" '
prolog &&
- reset_work_tree_to add_sub1 &&
+ reset_work_tree_to_interested add_sub1 &&
(
cd submodule_update &&
git branch -t remove_sub1 origin/remove_sub1 &&
replace_gitfile_with_git_dir sub1 &&
+ rm -rf .git/modules &&
$command remove_sub1 &&
test_superproject_content origin/remove_sub1 &&
- test_git_directory_is_unchanged sub1 &&
- test_submodule_content sub1 origin/add_sub1
- )
- '
- # Replacing a submodule with files in a directory must fail as the
- # submodule work tree isn't removed ...
- test_expect_failure "$command: replace submodule with a directory must fail" '
- prolog &&
- reset_work_tree_to add_sub1 &&
- (
- cd submodule_update &&
- git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory &&
- test_must_fail $command replace_sub1_with_directory &&
- test_superproject_content origin/add_sub1 &&
- test_submodule_content sub1 origin/add_sub1
- )
- '
- # ... especially when it contains a .git directory.
- test_expect_failure "$command: replace submodule containing a .git directory with a directory must fail" '
- prolog &&
- reset_work_tree_to add_sub1 &&
- (
- cd submodule_update &&
- git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory &&
- replace_gitfile_with_git_dir sub1 &&
- test_must_fail $command replace_sub1_with_directory &&
- test_superproject_content origin/add_sub1 &&
- test_git_directory_is_unchanged sub1 &&
- test_submodule_content sub1 origin/add_sub1
+ ! test -e sub1 &&
+ test_git_directory_exists sub1
)
'
- # Replacing it with a file must fail as it could throw away any local
- # work tree changes ...
- test_expect_failure "$command: replace submodule with a file must fail" '
+
+ # Replacing it with a file ...
+ test_expect_success "$command: replace submodule with a file" '
prolog &&
- reset_work_tree_to add_sub1 &&
+ reset_work_tree_to_interested add_sub1 &&
(
cd submodule_update &&
git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
- test_must_fail $command replace_sub1_with_file &&
- test_superproject_content origin/add_sub1 &&
- test_submodule_content sub1 origin/add_sub1
+ $command replace_sub1_with_file &&
+ test_superproject_content origin/replace_sub1_with_file &&
+ test -f sub1
)
'
- # ... or even destroy unpushed parts of submodule history if that
- # still uses a .git directory.
- test_expect_failure "$command: replace submodule containing a .git directory with a file must fail" '
+ RESULTDS=success
+ if test "$KNOWN_FAILURE_DIRECTORY_SUBMODULE_CONFLICTS" = 1
+ then
+ RESULTDS=failure
+ fi
+ # ... must check its local work tree for untracked files
+ test_expect_$RESULTDS "$command: replace submodule with a file must fail with untracked files" '
prolog &&
- reset_work_tree_to add_sub1 &&
+ reset_work_tree_to_interested add_sub1 &&
(
cd submodule_update &&
git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
- replace_gitfile_with_git_dir sub1 &&
+ : >sub1/untrackedfile &&
test_must_fail $command replace_sub1_with_file &&
test_superproject_content origin/add_sub1 &&
- test_git_directory_is_unchanged sub1 &&
test_submodule_content sub1 origin/add_sub1
+ test -f sub1/untracked_file
)
'
########################## Modified submodule #########################
- # Updating a submodule sha1 doesn't update the submodule's work tree
- test_expect_success "$command: modified submodule does not update submodule work tree" '
+ # Updating a submodule sha1 updates the submodule's work tree
+ test_expect_success "$command: modified submodule updates submodule work tree" '
prolog &&
- reset_work_tree_to add_sub1 &&
+ reset_work_tree_to_interested add_sub1 &&
(
cd submodule_update &&
git branch -t modify_sub1 origin/modify_sub1 &&
$command modify_sub1 &&
test_superproject_content origin/modify_sub1 &&
- test_submodule_content sub1 origin/add_sub1 &&
- git submodule update &&
test_submodule_content sub1 origin/modify_sub1
)
'
# Updating a submodule to an invalid sha1 doesn't update the
- # submodule's work tree, subsequent update will fail
- test_expect_success "$command: modified submodule does not update submodule work tree to invalid commit" '
+ # superproject nor the submodule's work tree.
+ test_expect_success "$command: updating to a missing submodule commit fails" '
prolog &&
- reset_work_tree_to add_sub1 &&
+ reset_work_tree_to_interested add_sub1 &&
(
cd submodule_update &&
git branch -t invalid_sub1 origin/invalid_sub1 &&
- $command invalid_sub1 &&
- test_superproject_content origin/invalid_sub1 &&
- test_submodule_content sub1 origin/add_sub1 &&
- test_must_fail git submodule update &&
+ test_must_fail $command invalid_sub1 &&
+ test_superproject_content origin/add_sub1 &&
test_submodule_content sub1 origin/add_sub1
)
'
- # Updating a submodule from an invalid sha1 doesn't update the
- # submodule's work tree, subsequent update will succeed
- test_expect_success "$command: modified submodule does not update submodule work tree from invalid commit" '
- prolog &&
- reset_work_tree_to invalid_sub1 &&
- (
- cd submodule_update &&
- git branch -t valid_sub1 origin/valid_sub1 &&
- $command valid_sub1 &&
- test_superproject_content origin/valid_sub1 &&
- test_dir_is_empty sub1 &&
- git submodule update --init --recursive &&
- test_submodule_content sub1 origin/valid_sub1
- )
- '
}
-# Test that submodule contents are correctly updated when switching
-# between commits that change a submodule.
-# Test that the following transitions are correctly handled:
-# (These tests are also above in the case where we expect no change
-# in the submodule)
-# - Updated submodule
-# - New submodule
-# - Removed submodule
-# - Directory containing tracked files replaced by submodule
-# - Submodule replaced by tracked files in directory
-# - Submodule replaced by tracked file with the same name
-# - tracked file replaced by submodule
+# Declares and invokes several tests that, in various situations, checks that
+# the provided Git command, when invoked with --recurse-submodules:
+# - succeeds in updating the worktree and index of a superproject to a target
+# commit, or fails atomically (depending on the test situation)
+# - if succeeds, the contents of submodule directories are updated
#
-# New test cases
-# - Removing a submodule with a git directory absorbs the submodules
-# git directory first into the superproject.
-
+# Specify the Git command so that "git $GIT_COMMAND --recurse-submodules"
+# works.
+#
+# If the command under test is known to not work with submodules in certain
+# conditions, set the appropriate KNOWN_FAILURE_* variable used in the tests
+# below to 1.
+#
+# Use as follows:
+#
+# test_submodule_switch_recursing_with_args "$GIT_COMMAND"
test_submodule_switch_recursing_with_args () {
cmd_args="$1"
command="git $cmd_args --recurse-submodules"
+ test_submodule_recursing_with_args_common "$command"
+
RESULTDS=success
if test "$KNOWN_FAILURE_DIRECTORY_SUBMODULE_CONFLICTS" = 1
then
@@ -794,33 +819,8 @@ test_submodule_switch_recursing_with_args () {
then
RESULTOI=failure
fi
- ######################### Appearing submodule #########################
- # Switching to a commit letting a submodule appear checks it out ...
- test_expect_success "$command: added submodule is checked out" '
- prolog &&
- reset_work_tree_to_interested no_submodule &&
- (
- cd submodule_update &&
- git branch -t add_sub1 origin/add_sub1 &&
- $command add_sub1 &&
- test_superproject_content origin/add_sub1 &&
- test_submodule_content sub1 origin/add_sub1
- )
- '
- # ... ignoring an empty existing directory ...
- test_expect_success "$command: added submodule is checked out in empty dir" '
- prolog &&
- reset_work_tree_to_interested no_submodule &&
- (
- cd submodule_update &&
- mkdir sub1 &&
- git branch -t add_sub1 origin/add_sub1 &&
- $command add_sub1 &&
- test_superproject_content origin/add_sub1 &&
- test_submodule_content sub1 origin/add_sub1
- )
- '
- # ... unless there is an untracked file in its place.
+ # Switching to a commit letting a submodule appear cannot override an
+ # untracked file.
test_expect_success "$command: added submodule doesn't remove untracked file with same name" '
prolog &&
reset_work_tree_to_interested no_submodule &&
@@ -848,59 +848,7 @@ test_submodule_switch_recursing_with_args () {
test_submodule_content sub1 origin/add_sub1
)
'
- # Replacing a tracked file with a submodule produces a checked out submodule
- test_expect_success "$command: replace tracked file with submodule checks out submodule" '
- prolog &&
- reset_work_tree_to_interested replace_sub1_with_file &&
- (
- cd submodule_update &&
- git branch -t replace_file_with_sub1 origin/replace_file_with_sub1 &&
- $command replace_file_with_sub1 &&
- test_superproject_content origin/replace_file_with_sub1 &&
- test_submodule_content sub1 origin/replace_file_with_sub1
- )
- '
- # ... as does removing a directory with tracked files with a submodule.
- test_expect_success "$command: replace directory with submodule" '
- prolog &&
- reset_work_tree_to_interested replace_sub1_with_directory &&
- (
- cd submodule_update &&
- git branch -t replace_directory_with_sub1 origin/replace_directory_with_sub1 &&
- $command replace_directory_with_sub1 &&
- test_superproject_content origin/replace_directory_with_sub1 &&
- test_submodule_content sub1 origin/replace_directory_with_sub1
- )
- '
- ######################## Disappearing submodule #######################
- # Removing a submodule removes its work tree ...
- test_expect_success "$command: removed submodule removes submodules working tree" '
- prolog &&
- reset_work_tree_to_interested add_sub1 &&
- (
- cd submodule_update &&
- git branch -t remove_sub1 origin/remove_sub1 &&
- $command remove_sub1 &&
- test_superproject_content origin/remove_sub1 &&
- ! test -e sub1
- )
- '
- # ... absorbing a .git directory along the way.
- test_expect_success "$command: removed submodule absorbs submodules .git directory" '
- prolog &&
- reset_work_tree_to_interested add_sub1 &&
- (
- cd submodule_update &&
- git branch -t remove_sub1 origin/remove_sub1 &&
- replace_gitfile_with_git_dir sub1 &&
- rm -rf .git/modules &&
- $command remove_sub1 &&
- test_superproject_content origin/remove_sub1 &&
- ! test -e sub1 &&
- test_git_directory_exists sub1
- )
- '
# Replacing a submodule with files in a directory must succeeds
# when the submodule is clean
test_expect_$RESULTDS "$command: replace submodule with a directory" '
@@ -929,33 +877,6 @@ test_submodule_switch_recursing_with_args () {
)
'
- # Replacing it with a file ...
- test_expect_success "$command: replace submodule with a file" '
- prolog &&
- reset_work_tree_to_interested add_sub1 &&
- (
- cd submodule_update &&
- git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
- $command replace_sub1_with_file &&
- test_superproject_content origin/replace_sub1_with_file &&
- test -f sub1
- )
- '
-
- # ... must check its local work tree for untracked files
- test_expect_$RESULTDS "$command: replace submodule with a file must fail with untracked files" '
- prolog &&
- reset_work_tree_to_interested add_sub1 &&
- (
- cd submodule_update &&
- git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
- : >sub1/untrackedfile &&
- test_must_fail $command replace_sub1_with_file &&
- test_superproject_content origin/add_sub1 &&
- test_submodule_content sub1 origin/add_sub1
- )
- '
-
# ... and ignored files are ignored
test_expect_success "$command: replace submodule with a file works ignores ignored files in submodule" '
test_when_finished "rm submodule_update/.git/modules/sub1/info/exclude" &&
@@ -971,20 +892,6 @@ test_submodule_switch_recursing_with_args () {
)
'
- ########################## Modified submodule #########################
- # Updating a submodule sha1 updates the submodule's work tree
- test_expect_success "$command: modified submodule updates submodule work tree" '
- prolog &&
- reset_work_tree_to_interested add_sub1 &&
- (
- cd submodule_update &&
- git branch -t modify_sub1 origin/modify_sub1 &&
- $command modify_sub1 &&
- test_superproject_content origin/modify_sub1 &&
- test_submodule_content sub1 origin/modify_sub1
- )
- '
-
test_expect_success "git -c submodule.recurse=true $cmd_args: modified submodule updates submodule work tree" '
prolog &&
reset_work_tree_to_interested add_sub1 &&
@@ -997,20 +904,6 @@ test_submodule_switch_recursing_with_args () {
)
'
- # Updating a submodule to an invalid sha1 doesn't update the
- # superproject nor the submodule's work tree.
- test_expect_success "$command: updating to a missing submodule commit fails" '
- prolog &&
- reset_work_tree_to_interested add_sub1 &&
- (
- cd submodule_update &&
- git branch -t invalid_sub1 origin/invalid_sub1 &&
- test_must_fail $command invalid_sub1 &&
- test_superproject_content origin/add_sub1 &&
- test_submodule_content sub1 origin/add_sub1
- )
- '
-
# recursing deeper than one level doesn't work yet.
test_expect_success "$command: modified submodule updates submodule recursively" '
prolog &&
@@ -1026,44 +919,20 @@ test_submodule_switch_recursing_with_args () {
'
}
-# Test that submodule contents are updated when switching between commits
-# that change a submodule, but throwing away local changes in
-# the superproject as well as the submodule is allowed.
+# Same as test_submodule_switch_recursing_with_args(), except that throwing
+# away local changes in the superproject is allowed.
test_submodule_forced_switch_recursing_with_args () {
cmd_args="$1"
command="git $cmd_args --recurse-submodules"
+ test_submodule_recursing_with_args_common "$command"
+
RESULT=success
if test "$KNOWN_FAILURE_DIRECTORY_SUBMODULE_CONFLICTS" = 1
then
RESULT=failure
fi
- ######################### Appearing submodule #########################
- # Switching to a commit letting a submodule appear creates empty dir ...
- test_expect_success "$command: added submodule is checked out" '
- prolog &&
- reset_work_tree_to_interested no_submodule &&
- (
- cd submodule_update &&
- git branch -t add_sub1 origin/add_sub1 &&
- $command add_sub1 &&
- test_superproject_content origin/add_sub1 &&
- test_submodule_content sub1 origin/add_sub1
- )
- '
- # ... and doesn't care if it already exists ...
- test_expect_success "$command: added submodule ignores empty directory" '
- prolog &&
- reset_work_tree_to_interested no_submodule &&
- (
- cd submodule_update &&
- git branch -t add_sub1 origin/add_sub1 &&
- mkdir sub1 &&
- $command add_sub1 &&
- test_superproject_content origin/add_sub1 &&
- test_submodule_content sub1 origin/add_sub1
- )
- '
- # ... not caring about an untracked file either
+ # Switching to a commit letting a submodule appear does not care about
+ # an untracked file.
test_expect_success "$command: added submodule does remove untracked unignored file with same name when forced" '
prolog &&
reset_work_tree_to_interested no_submodule &&
@@ -1076,60 +945,7 @@ test_submodule_forced_switch_recursing_with_args () {
test_submodule_content sub1 origin/add_sub1
)
'
- # Replacing a tracked file with a submodule checks out the submodule
- test_expect_success "$command: replace tracked file with submodule populates the submodule" '
- prolog &&
- reset_work_tree_to_interested replace_sub1_with_file &&
- (
- cd submodule_update &&
- git branch -t replace_file_with_sub1 origin/replace_file_with_sub1 &&
- $command replace_file_with_sub1 &&
- test_superproject_content origin/replace_file_with_sub1 &&
- test_submodule_content sub1 origin/replace_file_with_sub1
- )
- '
- # ... as does removing a directory with tracked files with a
- # submodule.
- test_expect_success "$command: replace directory with submodule" '
- prolog &&
- reset_work_tree_to_interested replace_sub1_with_directory &&
- (
- cd submodule_update &&
- git branch -t replace_directory_with_sub1 origin/replace_directory_with_sub1 &&
- $command replace_directory_with_sub1 &&
- test_superproject_content origin/replace_directory_with_sub1 &&
- test_submodule_content sub1 origin/replace_directory_with_sub1
- )
- '
- ######################## Disappearing submodule #######################
- # Removing a submodule doesn't remove its work tree ...
- test_expect_success "$command: removed submodule leaves submodule directory and its contents in place" '
- prolog &&
- reset_work_tree_to_interested add_sub1 &&
- (
- cd submodule_update &&
- git branch -t remove_sub1 origin/remove_sub1 &&
- $command remove_sub1 &&
- test_superproject_content origin/remove_sub1 &&
- ! test -e sub1
- )
- '
- # ... especially when it contains a .git directory.
- test_expect_success "$command: removed submodule leaves submodule containing a .git directory alone" '
- prolog &&
- reset_work_tree_to_interested add_sub1 &&
- (
- cd submodule_update &&
- git branch -t remove_sub1 origin/remove_sub1 &&
- replace_gitfile_with_git_dir sub1 &&
- rm -rf .git/modules/sub1 &&
- $command remove_sub1 &&
- test_superproject_content origin/remove_sub1 &&
- test_git_directory_exists sub1 &&
- ! test -e sub1
- )
- '
# Replacing a submodule with files in a directory ...
test_expect_success "$command: replace submodule with a directory" '
prolog &&
@@ -1156,17 +972,6 @@ test_submodule_forced_switch_recursing_with_args () {
test_git_directory_exists sub1
)
'
- # Replacing it with a file
- test_expect_success "$command: replace submodule with a file" '
- prolog &&
- reset_work_tree_to_interested add_sub1 &&
- (
- cd submodule_update &&
- git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
- $command replace_sub1_with_file &&
- test_superproject_content origin/replace_sub1_with_file
- )
- '
# ... even if the submodule contains ignored files
test_expect_success "$command: replace submodule with a file ignoring ignored files" '
@@ -1181,46 +986,6 @@ test_submodule_forced_switch_recursing_with_args () {
)
'
- # ... but stops for untracked files that would be lost
- test_expect_$RESULT "$command: replace submodule with a file stops for untracked files" '
- prolog &&
- reset_work_tree_to_interested add_sub1 &&
- (
- cd submodule_update &&
- git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
- : >sub1/untracked_file &&
- test_must_fail $command replace_sub1_with_file &&
- test_superproject_content origin/add_sub1 &&
- test -f sub1/untracked_file
- )
- '
-
- ########################## Modified submodule #########################
- # Updating a submodule sha1 updates the submodule's work tree
- test_expect_success "$command: modified submodule updates submodule work tree" '
- prolog &&
- reset_work_tree_to_interested add_sub1 &&
- (
- cd submodule_update &&
- git branch -t modify_sub1 origin/modify_sub1 &&
- $command modify_sub1 &&
- test_superproject_content origin/modify_sub1 &&
- test_submodule_content sub1 origin/modify_sub1
- )
- '
- # Updating a submodule to an invalid sha1 doesn't update the
- # submodule's work tree, subsequent update will fail
- test_expect_success "$command: modified submodule does not update submodule work tree to invalid commit" '
- prolog &&
- reset_work_tree_to_interested add_sub1 &&
- (
- cd submodule_update &&
- git branch -t invalid_sub1 origin/invalid_sub1 &&
- test_must_fail $command invalid_sub1 &&
- test_superproject_content origin/add_sub1 &&
- test_submodule_content sub1 origin/add_sub1
- )
- '
# Updating a submodule from an invalid sha1 updates
test_expect_success "$command: modified submodule does update submodule work tree from invalid commit" '
prolog &&
diff --git a/t/perf/aggregate.perl b/t/perf/aggregate.perl
index 1dbc85b214..e401208488 100755
--- a/t/perf/aggregate.perl
+++ b/t/perf/aggregate.perl
@@ -69,12 +69,17 @@ if (not @tests) {
@tests = glob "p????-*.sh";
}
+my $resultsdir = "test-results";
+if ($ENV{GIT_PERF_SUBSECTION} ne "") {
+ $resultsdir .= "/" . $ENV{GIT_PERF_SUBSECTION};
+}
+
my @subtests;
my %shorttests;
for my $t (@tests) {
$t =~ s{(?:.*/)?(p(\d+)-[^/]+)\.sh$}{$1} or die "bad test name: $t";
my $n = $2;
- my $fname = "test-results/$t.subtests";
+ my $fname = "$resultsdir/$t.subtests";
open my $fp, "<", $fname or die "cannot open $fname: $!";
for (<$fp>) {
chomp;
@@ -98,7 +103,7 @@ sub read_descr {
my %descrs;
my $descrlen = 4; # "Test"
for my $t (@subtests) {
- $descrs{$t} = $shorttests{$t}.": ".read_descr("test-results/$t.descr");
+ $descrs{$t} = $shorttests{$t}.": ".read_descr("$resultsdir/$t.descr");
$descrlen = length $descrs{$t} if length $descrs{$t}>$descrlen;
}
@@ -138,7 +143,7 @@ for my $t (@subtests) {
my $firstr;
for my $i (0..$#dirs) {
my $d = $dirs[$i];
- $times{$prefixes{$d}.$t} = [get_times("test-results/$prefixes{$d}$t.times")];
+ $times{$prefixes{$d}.$t} = [get_times("$resultsdir/$prefixes{$d}$t.times")];
my ($r,$u,$s) = @{$times{$prefixes{$d}.$t}};
my $w = length format_times($r,$u,$s,$firstr);
$colwidth[$i] = $w if $w > $colwidth[$i];
diff --git a/t/perf/lib-pack.sh b/t/perf/lib-pack.sh
new file mode 100644
index 0000000000..d3865db286
--- /dev/null
+++ b/t/perf/lib-pack.sh
@@ -0,0 +1,25 @@
+# Helpers for dealing with large numbers of packs.
+
+# create $1 nonsense packs, each with a single blob
+create_packs () {
+ perl -le '
+ my ($n) = @ARGV;
+ for (1..$n) {
+ print "blob";
+ print "data <<EOF";
+ print "$_";
+ print "EOF";
+ print "checkpoint"
+ }
+ ' "$@" |
+ git fast-import
+}
+
+# create a large number of packs, disabling any gc which might
+# cause us to repack them
+setup_many_packs () {
+ git config gc.auto 0 &&
+ git config gc.autopacklimit 0 &&
+ git config fastimport.unpacklimit 0 &&
+ create_packs 500
+}
diff --git a/t/perf/p0007-write-cache.sh b/t/perf/p0007-write-cache.sh
new file mode 100755
index 0000000000..261fe92fd9
--- /dev/null
+++ b/t/perf/p0007-write-cache.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+test_description="Tests performance of writing the index"
+
+. ./perf-lib.sh
+
+test_perf_default_repo
+
+test_expect_success "setup repo" '
+ if git rev-parse --verify refs/heads/p0006-ballast^{commit}
+ then
+ echo Assuming synthetic repo from many-files.sh
+ git config --local core.sparsecheckout 1
+ cat >.git/info/sparse-checkout <<-EOF
+ /*
+ !ballast/*
+ EOF
+ else
+ echo Assuming non-synthetic repo...
+ fi &&
+ nr_files=$(git ls-files | wc -l)
+'
+
+count=3
+test_perf "write_locked_index $count times ($nr_files files)" "
+ test-write-cache $count
+"
+
+test_done
diff --git a/t/perf/p4211-line-log.sh b/t/perf/p4211-line-log.sh
index b7ff68d4fa..e0ed05907c 100755
--- a/t/perf/p4211-line-log.sh
+++ b/t/perf/p4211-line-log.sh
@@ -31,4 +31,8 @@ test_perf 'git log -L (renames on)' '
git log -M -L 1:"$file" >/dev/null
'
+test_perf 'git log --oneline --raw --parents' '
+ git log --oneline --raw --parents >/dev/null
+'
+
test_done
diff --git a/t/perf/p5550-fetch-tags.sh b/t/perf/p5550-fetch-tags.sh
index a5dc39f86a..d0e0e019ea 100755
--- a/t/perf/p5550-fetch-tags.sh
+++ b/t/perf/p5550-fetch-tags.sh
@@ -20,6 +20,7 @@ start to show a noticeable performance problem on my machine, but without
taking too long to set up and run the tests.
'
. ./perf-lib.sh
+. "$TEST_DIRECTORY/perf/lib-pack.sh"
# make a long nonsense history on branch $1, consisting of $2 commits, each
# with a unique file pointing to the blob at $2.
@@ -44,26 +45,6 @@ create_tags () {
git update-ref --stdin
}
-# create $1 nonsense packs, each with a single blob
-create_packs () {
- perl -le '
- my ($n) = @ARGV;
- for (1..$n) {
- print "blob";
- print "data <<EOF";
- print "$_";
- print "EOF";
- }
- ' "$@" |
- git fast-import &&
-
- git cat-file --batch-all-objects --batch-check='%(objectname)' |
- while read sha1
- do
- echo $sha1 | git pack-objects .git/objects/pack/pack
- done
-}
-
test_expect_success 'create parent and child' '
git init parent &&
git -C parent commit --allow-empty -m base &&
@@ -84,9 +65,7 @@ test_expect_success 'populate parent tags' '
test_expect_success 'create child packs' '
(
cd child &&
- git config gc.auto 0 &&
- git config gc.autopacklimit 0 &&
- create_packs 500
+ setup_many_packs
)
'
diff --git a/t/perf/p5551-fetch-rescan.sh b/t/perf/p5551-fetch-rescan.sh
new file mode 100755
index 0000000000..b99dc23e32
--- /dev/null
+++ b/t/perf/p5551-fetch-rescan.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+test_description='fetch performance with many packs
+
+It is common for fetch to consider objects that we might not have, and it is an
+easy mistake for the code to use a function like `parse_object` that might
+give the correct _answer_ on such an object, but do so slowly (due to
+re-scanning the pack directory for lookup failures).
+
+The resulting performance drop can be hard to notice in a real repository, but
+becomes quite large in a repository with a large number of packs. So this
+test creates a more pathological case, since any mistakes would produce a more
+noticeable slowdown.
+'
+. ./perf-lib.sh
+. "$TEST_DIRECTORY"/perf/lib-pack.sh
+
+test_expect_success 'create parent and child' '
+ git init parent &&
+ git clone parent child
+'
+
+
+test_expect_success 'create refs in the parent' '
+ (
+ cd parent &&
+ git commit --allow-empty -m foo &&
+ head=$(git rev-parse HEAD) &&
+ test_seq 1000 |
+ sed "s,.*,update refs/heads/& $head," |
+ $MODERN_GIT update-ref --stdin
+ )
+'
+
+test_expect_success 'create many packs in the child' '
+ (
+ cd child &&
+ setup_many_packs
+ )
+'
+
+test_perf 'fetch' '
+ # start at the same state for each iteration
+ obj=$($MODERN_GIT -C parent rev-parse HEAD) &&
+ (
+ cd child &&
+ $MODERN_GIT for-each-ref --format="delete %(refname)" refs/remotes |
+ $MODERN_GIT update-ref --stdin &&
+ rm -vf .git/objects/$(echo $obj | sed "s|^..|&/|") &&
+
+ git fetch
+ )
+'
+
+test_done
diff --git a/t/perf/p7519-fsmonitor.sh b/t/perf/p7519-fsmonitor.sh
new file mode 100755
index 0000000000..16d1bf72e5
--- /dev/null
+++ b/t/perf/p7519-fsmonitor.sh
@@ -0,0 +1,184 @@
+#!/bin/sh
+
+test_description="Test core.fsmonitor"
+
+. ./perf-lib.sh
+
+#
+# Performance test for the fsmonitor feature which enables git to talk to a
+# file system change monitor and avoid having to scan the working directory
+# for new or modified files.
+#
+# By default, the performance test will utilize the Watchman file system
+# monitor if it is installed. If Watchman is not installed, it will use a
+# dummy integration script that does not report any new or modified files.
+# The dummy script has very little overhead which provides optimistic results.
+#
+# The performance test will also use the untracked cache feature if it is
+# available as fsmonitor uses it to speed up scanning for untracked files.
+#
+# There are 3 environment variables that can be used to alter the default
+# behavior of the performance test:
+#
+# GIT_PERF_7519_UNTRACKED_CACHE: used to configure core.untrackedCache
+# GIT_PERF_7519_SPLIT_INDEX: used to configure core.splitIndex
+# GIT_PERF_7519_FSMONITOR: used to configure core.fsMonitor
+#
+# The big win for using fsmonitor is the elimination of the need to scan the
+# working directory looking for changed and untracked files. If the file
+# information is all cached in RAM, the benefits are reduced.
+#
+# GIT_PERF_7519_DROP_CACHE: if set, the OS caches are dropped between tests
+#
+
+test_perf_large_repo
+test_checkout_worktree
+
+test_lazy_prereq UNTRACKED_CACHE '
+ { git update-index --test-untracked-cache; ret=$?; } &&
+ test $ret -ne 1
+'
+
+test_lazy_prereq WATCHMAN '
+ { command -v watchman >/dev/null 2>&1; ret=$?; } &&
+ test $ret -ne 1
+'
+
+if test_have_prereq WATCHMAN
+then
+ # Convert unix style paths to escaped Windows style paths for Watchman
+ case "$(uname -s)" in
+ MSYS_NT*)
+ GIT_WORK_TREE="$(cygpath -aw "$PWD" | sed 's,\\,/,g')"
+ ;;
+ *)
+ GIT_WORK_TREE="$PWD"
+ ;;
+ esac
+fi
+
+if test -n "$GIT_PERF_7519_DROP_CACHE"
+then
+ # When using GIT_PERF_7519_DROP_CACHE, GIT_PERF_REPEAT_COUNT must be 1 to
+ # generate valid results. Otherwise the caching that happens for the nth
+ # run will negate the validity of the comparisons.
+ if test "$GIT_PERF_REPEAT_COUNT" -ne 1
+ then
+ echo "warning: Setting GIT_PERF_REPEAT_COUNT=1" >&2
+ GIT_PERF_REPEAT_COUNT=1
+ fi
+fi
+
+test_expect_success "setup for fsmonitor" '
+ # set untrackedCache depending on the environment
+ if test -n "$GIT_PERF_7519_UNTRACKED_CACHE"
+ then
+ git config core.untrackedCache "$GIT_PERF_7519_UNTRACKED_CACHE"
+ else
+ if test_have_prereq UNTRACKED_CACHE
+ then
+ git config core.untrackedCache true
+ else
+ git config core.untrackedCache false
+ fi
+ fi &&
+
+ # set core.splitindex depending on the environment
+ if test -n "$GIT_PERF_7519_SPLIT_INDEX"
+ then
+ git config core.splitIndex "$GIT_PERF_7519_SPLIT_INDEX"
+ fi &&
+
+ # set INTEGRATION_SCRIPT depending on the environment
+ if test -n "$GIT_PERF_7519_FSMONITOR"
+ then
+ INTEGRATION_SCRIPT="$GIT_PERF_7519_FSMONITOR"
+ else
+ #
+ # Choose integration script based on existence of Watchman.
+ # If Watchman exists, watch the work tree and attempt a query.
+ # If everything succeeds, use Watchman integration script,
+ # else fall back to an empty integration script.
+ #
+ mkdir .git/hooks &&
+ if test_have_prereq WATCHMAN
+ then
+ INTEGRATION_SCRIPT=".git/hooks/fsmonitor-watchman" &&
+ cp "$TEST_DIRECTORY/../templates/hooks--fsmonitor-watchman.sample" "$INTEGRATION_SCRIPT" &&
+ watchman watch "$GIT_WORK_TREE" &&
+ watchman watch-list | grep -q -F "$GIT_WORK_TREE"
+ else
+ INTEGRATION_SCRIPT=".git/hooks/fsmonitor-empty" &&
+ write_script "$INTEGRATION_SCRIPT"<<-\EOF
+ EOF
+ fi
+ fi &&
+
+ git config core.fsmonitor "$INTEGRATION_SCRIPT" &&
+ git update-index --fsmonitor
+'
+
+if test -n "$GIT_PERF_7519_DROP_CACHE"; then
+ test-drop-caches
+fi
+
+test_perf "status (fsmonitor=$INTEGRATION_SCRIPT)" '
+ git status
+'
+
+if test -n "$GIT_PERF_7519_DROP_CACHE"; then
+ test-drop-caches
+fi
+
+test_perf "status -uno (fsmonitor=$INTEGRATION_SCRIPT)" '
+ git status -uno
+'
+
+if test -n "$GIT_PERF_7519_DROP_CACHE"; then
+ test-drop-caches
+fi
+
+test_perf "status -uall (fsmonitor=$INTEGRATION_SCRIPT)" '
+ git status -uall
+'
+
+test_expect_success "setup without fsmonitor" '
+ unset INTEGRATION_SCRIPT &&
+ git config --unset core.fsmonitor &&
+ git update-index --no-fsmonitor
+'
+
+if test -n "$GIT_PERF_7519_DROP_CACHE"; then
+ test-drop-caches
+fi
+
+test_perf "status (fsmonitor=$INTEGRATION_SCRIPT)" '
+ git status
+'
+
+if test -n "$GIT_PERF_7519_DROP_CACHE"; then
+ test-drop-caches
+fi
+
+test_perf "status -uno (fsmonitor=$INTEGRATION_SCRIPT)" '
+ git status -uno
+'
+
+if test -n "$GIT_PERF_7519_DROP_CACHE"; then
+ test-drop-caches
+fi
+
+test_perf "status -uall (fsmonitor=$INTEGRATION_SCRIPT)" '
+ git status -uall
+'
+
+if test_have_prereq WATCHMAN
+then
+ watchman watch-del "$GIT_WORK_TREE" >/dev/null 2>&1 &&
+
+ # Work around Watchman bug on Windows where it holds on to handles
+ # preventing the removal of the trash directory
+ watchman shutdown-server >/dev/null 2>&1
+fi
+
+test_done
diff --git a/t/perf/perf-lib.sh b/t/perf/perf-lib.sh
index b50211b259..e4c343a6b7 100644
--- a/t/perf/perf-lib.sh
+++ b/t/perf/perf-lib.sh
@@ -56,12 +56,10 @@ MODERN_GIT=$GIT_BUILD_DIR/bin-wrappers/git
export MODERN_GIT
perf_results_dir=$TEST_OUTPUT_DIRECTORY/test-results
+test -n "$GIT_PERF_SUBSECTION" && perf_results_dir="$perf_results_dir/$GIT_PERF_SUBSECTION"
mkdir -p "$perf_results_dir"
rm -f "$perf_results_dir"/$(basename "$0" .sh).subtests
-if test -z "$GIT_PERF_REPEAT_COUNT"; then
- GIT_PERF_REPEAT_COUNT=3
-fi
die_if_build_dir_not_repo () {
if ! ( cd "$TEST_DIRECTORY/.." &&
git rev-parse --build-dir >/dev/null 2>&1 ); then
diff --git a/t/perf/run b/t/perf/run
index beb4acc0e4..43e4de49ef 100755
--- a/t/perf/run
+++ b/t/perf/run
@@ -2,9 +2,14 @@
case "$1" in
--help)
- echo "usage: $0 [other_git_tree...] [--] [test_scripts]"
+ echo "usage: $0 [--config file] [other_git_tree...] [--] [test_scripts]"
exit 0
;;
+ --config)
+ shift
+ GIT_PERF_CONFIG_FILE=$(cd "$(dirname "$1")"; pwd)/$(basename "$1")
+ export GIT_PERF_CONFIG_FILE
+ shift ;;
esac
die () {
@@ -29,8 +34,10 @@ unpack_git_rev () {
(cd "$(git rev-parse --show-cdup)" && git archive --format=tar $rev) |
(cd build/$rev && tar x)
}
+
build_git_rev () {
rev=$1
+ name="$2"
for config in config.mak config.mak.autogen config.status
do
if test -e "../../$config"
@@ -38,7 +45,7 @@ build_git_rev () {
cp "../../$config" "build/$rev/"
fi
done
- echo "=== Building $rev ==="
+ echo "=== Building $rev ($name) ==="
(
cd build/$rev &&
if test -n "$GIT_PERF_MAKE_COMMAND"
@@ -65,7 +72,7 @@ run_dirs_helper () {
if [ ! -d build/$rev ]; then
unpack_git_rev $rev
fi
- build_git_rev $rev
+ build_git_rev $rev "$mydir"
mydir=build/$rev
fi
if test "$mydir" = .; then
@@ -87,14 +94,78 @@ run_dirs () {
done
}
-GIT_PERF_AGGREGATING_LATER=t
-export GIT_PERF_AGGREGATING_LATER
+get_subsections () {
+ section="$1"
+ test -z "$GIT_PERF_CONFIG_FILE" && return
+ git config -f "$GIT_PERF_CONFIG_FILE" --name-only --get-regex "$section\..*\.[^.]+" |
+ sed -e "s/$section\.\(.*\)\..*/\1/" | sort | uniq
+}
+
+get_var_from_env_or_config () {
+ env_var="$1"
+ conf_sec="$2"
+ conf_var="$3"
+ # $4 can be set to a default value
+
+ # Do nothing if the env variable is already set
+ eval "test -z \"\${$env_var+x}\"" || return
+
+ test -z "$GIT_PERF_CONFIG_FILE" && return
+
+ # Check if the variable is in the config file
+ if test -n "$GIT_PERF_SUBSECTION"
+ then
+ var="$conf_sec.$GIT_PERF_SUBSECTION.$conf_var"
+ conf_value=$(git config -f "$GIT_PERF_CONFIG_FILE" "$var") &&
+ eval "$env_var=\"$conf_value\"" && return
+ fi
+ var="$conf_sec.$conf_var"
+ conf_value=$(git config -f "$GIT_PERF_CONFIG_FILE" "$var") &&
+ eval "$env_var=\"$conf_value\"" && return
+
+ test -n "${4+x}" && eval "$env_var=\"$4\""
+}
+
+run_subsection () {
+ get_var_from_env_or_config "GIT_PERF_REPEAT_COUNT" "perf" "repeatCount" 3
+ export GIT_PERF_REPEAT_COUNT
+
+ get_var_from_env_or_config "GIT_PERF_DIRS_OR_REVS" "perf" "dirsOrRevs"
+ set -- $GIT_PERF_DIRS_OR_REVS "$@"
+
+ get_var_from_env_or_config "GIT_PERF_MAKE_COMMAND" "perf" "makeCommand"
+ get_var_from_env_or_config "GIT_PERF_MAKE_OPTS" "perf" "makeOpts"
+
+ GIT_PERF_AGGREGATING_LATER=t
+ export GIT_PERF_AGGREGATING_LATER
+
+ if test $# = 0 -o "$1" = -- -o -f "$1"; then
+ set -- . "$@"
+ fi
+
+ run_dirs "$@"
+ ./aggregate.perl "$@"
+}
cd "$(dirname $0)"
. ../../GIT-BUILD-OPTIONS
-if test $# = 0 -o "$1" = -- -o -f "$1"; then
- set -- . "$@"
+mkdir -p test-results
+get_subsections "perf" >test-results/run_subsections.names
+
+if test $(wc -l <test-results/run_subsections.names) -eq 0
+then
+ (
+ run_subsection "$@"
+ )
+else
+ while read -r subsec
+ do
+ (
+ GIT_PERF_SUBSECTION="$subsec"
+ export GIT_PERF_SUBSECTION
+ echo "======== Run for subsection '$GIT_PERF_SUBSECTION' ========"
+ run_subsection "$@"
+ )
+ done <test-results/run_subsections.names
fi
-run_dirs "$@"
-./aggregate.perl "$@"
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index 1aa5093f36..7fd87dd544 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -20,6 +20,31 @@ modification *should* take notice and update the test vectors here.
. ./test-lib.sh
+try_local_x () {
+ local x="local" &&
+ echo "$x"
+}
+
+# This test is an experiment to check whether any Git users are using
+# Shells that don't support the "local" keyword. "local" is not
+# POSIX-standard, but it is very widely supported by POSIX-compliant
+# shells, and if it doesn't cause problems for people, we would like
+# to be able to use it in Git code.
+#
+# For now, this is the only test that requires "local". If your shell
+# fails this test, you can ignore the failure, but please report the
+# problem to the Git mailing list <git@vger.kernel.org>, as it might
+# convince us to continue avoiding the use of "local".
+test_expect_success 'verify that the running shell supports "local"' '
+ x="notlocal" &&
+ echo "local" >expected1 &&
+ try_local_x >actual1 &&
+ test_cmp expected1 actual1 &&
+ echo "notlocal" >expected2 &&
+ echo "$x" >actual2 &&
+ test_cmp expected2 actual2
+'
+
################################################################
# git init has been done in an empty repository.
# make sure it is empty.
diff --git a/t/t0001-init.sh b/t/t0001-init.sh
index 86c1a51654..c413bff9cf 100755
--- a/t/t0001-init.sh
+++ b/t/t0001-init.sh
@@ -453,4 +453,16 @@ test_expect_success 're-init from a linked worktree' '
)
'
+test_expect_success MINGW 'redirect std handles' '
+ GIT_REDIRECT_STDOUT=output.txt git rev-parse --git-dir &&
+ test .git = "$(cat output.txt)" &&
+ test -z "$(GIT_REDIRECT_STDOUT=off git rev-parse --git-dir)" &&
+ test_must_fail env \
+ GIT_REDIRECT_STDOUT=output.txt \
+ GIT_REDIRECT_STDERR="2>&1" \
+ git rev-parse --git-dir --verify refs/invalid &&
+ printf ".git\nfatal: Needed a single revision\n" >expect &&
+ test_cmp expect output.txt
+'
+
test_done
diff --git a/t/t0021/rot13-filter.pl b/t/t0021/rot13-filter.pl
index ad685d92f8..f1678851de 100644
--- a/t/t0021/rot13-filter.pl
+++ b/t/t0021/rot13-filter.pl
@@ -30,9 +30,12 @@
# to the "list_available_blobs" response.
#
+use 5.008;
+use lib (split(/:/, $ENV{GITPERLLIB}));
use strict;
use warnings;
use IO::File;
+use Git::Packet;
my $MAX_PACKET_CONTENT_SIZE = 65516;
my $log_file = shift @ARGV;
@@ -55,89 +58,30 @@ sub rot13 {
return $str;
}
-sub packet_bin_read {
- my $buffer;
- my $bytes_read = read STDIN, $buffer, 4;
- if ( $bytes_read == 0 ) {
- # EOF - Git stopped talking to us!
- print $debug "STOP\n";
- exit();
- }
- elsif ( $bytes_read != 4 ) {
- die "invalid packet: '$buffer'";
- }
- my $pkt_size = hex($buffer);
- if ( $pkt_size == 0 ) {
- return ( 1, "" );
- }
- elsif ( $pkt_size > 4 ) {
- my $content_size = $pkt_size - 4;
- $bytes_read = read STDIN, $buffer, $content_size;
- if ( $bytes_read != $content_size ) {
- die "invalid packet ($content_size bytes expected; $bytes_read bytes read)";
- }
- return ( 0, $buffer );
- }
- else {
- die "invalid packet size: $pkt_size";
- }
-}
-
-sub packet_txt_read {
- my ( $res, $buf ) = packet_bin_read();
- unless ( $buf eq '' or $buf =~ s/\n$// ) {
- die "A non-binary line MUST be terminated by an LF.";
- }
- return ( $res, $buf );
-}
-
-sub packet_bin_write {
- my $buf = shift;
- print STDOUT sprintf( "%04x", length($buf) + 4 );
- print STDOUT $buf;
- STDOUT->flush();
-}
-
-sub packet_txt_write {
- packet_bin_write( $_[0] . "\n" );
-}
-
-sub packet_flush {
- print STDOUT sprintf( "%04x", 0 );
- STDOUT->flush();
-}
-
print $debug "START\n";
$debug->flush();
-( packet_txt_read() eq ( 0, "git-filter-client" ) ) || die "bad initialize";
-( packet_txt_read() eq ( 0, "version=2" ) ) || die "bad version";
-( packet_bin_read() eq ( 1, "" ) ) || die "bad version end";
+packet_initialize("git-filter", 2);
-packet_txt_write("git-filter-server");
-packet_txt_write("version=2");
-packet_flush();
+my %remote_caps = packet_read_and_check_capabilities("clean", "smudge", "delay");
+packet_check_and_write_capabilities(\%remote_caps, @capabilities);
-( packet_txt_read() eq ( 0, "capability=clean" ) ) || die "bad capability";
-( packet_txt_read() eq ( 0, "capability=smudge" ) ) || die "bad capability";
-( packet_txt_read() eq ( 0, "capability=delay" ) ) || die "bad capability";
-( packet_bin_read() eq ( 1, "" ) ) || die "bad capability end";
-
-foreach (@capabilities) {
- packet_txt_write( "capability=" . $_ );
-}
-packet_flush();
print $debug "init handshake complete\n";
$debug->flush();
while (1) {
- my ( $command ) = packet_txt_read() =~ /^command=(.+)$/;
+ my ( $res, $command ) = packet_key_val_read("command");
+ if ( $res == -1 ) {
+ print $debug "STOP\n";
+ exit();
+ }
print $debug "IN: $command";
$debug->flush();
if ( $command eq "list_available_blobs" ) {
# Flush
- packet_bin_read();
+ packet_compare_lists([1, ""], packet_bin_read()) ||
+ die "bad list_available_blobs end";
foreach my $pathname ( sort keys %DELAY ) {
if ( $DELAY{$pathname}{"requested"} >= 1 ) {
@@ -161,16 +105,14 @@ while (1) {
$debug->flush();
packet_txt_write("status=success");
packet_flush();
- }
- else {
- my ( $pathname ) = packet_txt_read() =~ /^pathname=(.+)$/;
+ } else {
+ my ( $res, $pathname ) = packet_key_val_read("pathname");
+ if ( $res == -1 ) {
+ die "unexpected EOF while expecting pathname";
+ }
print $debug " $pathname";
$debug->flush();
- if ( $pathname eq "" ) {
- die "bad pathname '$pathname'";
- }
-
# Read until flush
my ( $done, $buffer ) = packet_txt_read();
while ( $buffer ne '' ) {
@@ -184,6 +126,9 @@ while (1) {
( $done, $buffer ) = packet_txt_read();
}
+ if ( $done == -1 ) {
+ die "unexpected EOF after pathname '$pathname'";
+ }
my $input = "";
{
@@ -194,6 +139,9 @@ while (1) {
( $done, $buffer ) = packet_bin_read();
$input .= $buffer;
}
+ if ( $done == -1 ) {
+ die "unexpected EOF while reading input for '$pathname'";
+ }
print $debug " " . length($input) . " [OK] -- ";
$debug->flush();
}
@@ -201,17 +149,13 @@ while (1) {
my $output;
if ( exists $DELAY{$pathname} and exists $DELAY{$pathname}{"output"} ) {
$output = $DELAY{$pathname}{"output"}
- }
- elsif ( $pathname eq "error.r" or $pathname eq "abort.r" ) {
+ } elsif ( $pathname eq "error.r" or $pathname eq "abort.r" ) {
$output = "";
- }
- elsif ( $command eq "clean" and grep( /^clean$/, @capabilities ) ) {
+ } elsif ( $command eq "clean" and grep( /^clean$/, @capabilities ) ) {
$output = rot13($input);
- }
- elsif ( $command eq "smudge" and grep( /^smudge$/, @capabilities ) ) {
+ } elsif ( $command eq "smudge" and grep( /^smudge$/, @capabilities ) ) {
$output = rot13($input);
- }
- else {
+ } else {
die "bad command '$command'";
}
@@ -220,25 +164,21 @@ while (1) {
$debug->flush();
packet_txt_write("status=error");
packet_flush();
- }
- elsif ( $pathname eq "abort.r" ) {
+ } elsif ( $pathname eq "abort.r" ) {
print $debug "[ABORT]\n";
$debug->flush();
packet_txt_write("status=abort");
packet_flush();
- }
- elsif ( $command eq "smudge" and
+ } elsif ( $command eq "smudge" and
exists $DELAY{$pathname} and
- $DELAY{$pathname}{"requested"} == 1
- ) {
+ $DELAY{$pathname}{"requested"} == 1 ) {
print $debug "[DELAYED]\n";
$debug->flush();
packet_txt_write("status=delayed");
packet_flush();
$DELAY{$pathname}{"requested"} = 2;
$DELAY{$pathname}{"output"} = $output;
- }
- else {
+ } else {
packet_txt_write("status=success");
packet_flush();
@@ -258,8 +198,7 @@ while (1) {
print $debug ".";
if ( length($output) > $MAX_PACKET_CONTENT_SIZE ) {
$output = substr( $output, $MAX_PACKET_CONTENT_SIZE );
- }
- else {
+ } else {
$output = "";
}
}
diff --git a/t/t0025-crlf-renormalize.sh b/t/t0025-crlf-renormalize.sh
new file mode 100755
index 0000000000..9d9e02a211
--- /dev/null
+++ b/t/t0025-crlf-renormalize.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+test_description='CRLF renormalization'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ git config core.autocrlf false &&
+ printf "LINEONE\nLINETWO\nLINETHREE\n" >LF.txt &&
+ printf "LINEONE\r\nLINETWO\r\nLINETHREE\r\n" >CRLF.txt &&
+ printf "LINEONE\r\nLINETWO\nLINETHREE\n" >CRLF_mix_LF.txt &&
+ git add . &&
+ git commit -m initial
+'
+
+test_expect_success 'renormalize CRLF in repo' '
+ echo "*.txt text=auto" >.gitattributes &&
+ git add --renormalize "*.txt" &&
+ cat >expect <<-\EOF &&
+ i/lf w/crlf attr/text=auto CRLF.txt
+ i/lf w/lf attr/text=auto LF.txt
+ i/lf w/mixed attr/text=auto CRLF_mix_LF.txt
+ EOF
+ git ls-files --eol |
+ sed -e "s/ / /g" -e "s/ */ /g" |
+ sort >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t0027-auto-crlf.sh b/t/t0027-auto-crlf.sh
index deb3ae7813..68108d956a 100755
--- a/t/t0027-auto-crlf.sh
+++ b/t/t0027-auto-crlf.sh
@@ -315,7 +315,7 @@ test_expect_success 'setup master' '
echo >.gitattributes &&
git checkout -b master &&
git add .gitattributes &&
- git commit -m "add .gitattributes" "" &&
+ git commit -m "add .gitattributes" . &&
printf "\$Id: 0000000000000000000000000000000000000000 \$\nLINEONE\nLINETWO\nLINETHREE" >LF &&
printf "\$Id: 0000000000000000000000000000000000000000 \$\r\nLINEONE\r\nLINETWO\r\nLINETHREE" >CRLF &&
printf "\$Id: 0000000000000000000000000000000000000000 \$\nLINEONE\r\nLINETWO\nLINETHREE" >CRLF_mix_LF &&
diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh
index 74d2cd76fe..0c2fc81d7b 100755
--- a/t/t0040-parse-options.sh
+++ b/t/t0040-parse-options.sh
@@ -10,6 +10,8 @@ test_description='our own option parser'
cat >expect <<\EOF
usage: test-parse-options <options>
+ A helper function for the parse-options API.
+
--yes get a boolean
-D, --no-doubt begins with 'no-'
-B, --no-fear be brave
diff --git a/t/t1004-read-tree-m-u-wf.sh b/t/t1004-read-tree-m-u-wf.sh
index c70cf42300..c7ce5d8bb5 100755
--- a/t/t1004-read-tree-m-u-wf.sh
+++ b/t/t1004-read-tree-m-u-wf.sh
@@ -218,7 +218,7 @@ test_expect_success 'D/F' '
echo "100644 $a 2 subdir/file2"
echo "100644 $b 3 subdir/file2/another"
) >expect &&
- test_cmp actual expect
+ test_cmp expect actual
'
diff --git a/t/t1200-tutorial.sh b/t/t1200-tutorial.sh
deleted file mode 100755
index 397ccb6909..0000000000
--- a/t/t1200-tutorial.sh
+++ /dev/null
@@ -1,268 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2005 Johannes Schindelin
-#
-
-test_description='A simple turial in the form of a test case'
-
-. ./test-lib.sh
-
-test_expect_success 'blob' '
- echo "Hello World" > hello &&
- echo "Silly example" > example &&
-
- git update-index --add hello example &&
-
- test blob = "$(git cat-file -t 557db03)"
-'
-
-test_expect_success 'blob 557db03' '
- test "Hello World" = "$(git cat-file blob 557db03)"
-'
-
-echo "It's a new day for git" >>hello
-cat > diff.expect << EOF
-diff --git a/hello b/hello
-index 557db03..263414f 100644
---- a/hello
-+++ b/hello
-@@ -1 +1,2 @@
- Hello World
-+It's a new day for git
-EOF
-
-test_expect_success 'git diff-files -p' '
- git diff-files -p > diff.output &&
- test_cmp diff.expect diff.output
-'
-
-test_expect_success 'git diff' '
- git diff > diff.output &&
- test_cmp diff.expect diff.output
-'
-
-test_expect_success 'tree' '
- tree=$(git write-tree 2>/dev/null) &&
- test 8988da15d077d4829fc51d8544c097def6644dbb = $tree
-'
-
-test_expect_success 'git diff-index -p HEAD' '
- test_tick &&
- tree=$(git write-tree) &&
- commit=$(echo "Initial commit" | git commit-tree $tree) &&
- git update-ref HEAD $commit &&
- git diff-index -p HEAD > diff.output &&
- test_cmp diff.expect diff.output
-'
-
-test_expect_success 'git diff HEAD' '
- git diff HEAD > diff.output &&
- test_cmp diff.expect diff.output
-'
-
-cat > whatchanged.expect << EOF
-commit VARIABLE
-Author: VARIABLE
-Date: VARIABLE
-
- Initial commit
-
-diff --git a/example b/example
-new file mode 100644
-index 0000000..f24c74a
---- /dev/null
-+++ b/example
-@@ -0,0 +1 @@
-+Silly example
-diff --git a/hello b/hello
-new file mode 100644
-index 0000000..557db03
---- /dev/null
-+++ b/hello
-@@ -0,0 +1 @@
-+Hello World
-EOF
-
-test_expect_success 'git whatchanged -p --root' '
- git whatchanged -p --root |
- sed -e "1s/^\(.\{7\}\).\{40\}/\1VARIABLE/" \
- -e "2,3s/^\(.\{8\}\).*$/\1VARIABLE/" \
- > whatchanged.output &&
- test_cmp whatchanged.expect whatchanged.output
-'
-
-test_expect_success 'git tag my-first-tag' '
- git tag my-first-tag &&
- test_cmp .git/refs/heads/master .git/refs/tags/my-first-tag
-'
-
-test_expect_success 'git checkout -b mybranch' '
- git checkout -b mybranch &&
- test_cmp .git/refs/heads/master .git/refs/heads/mybranch
-'
-
-cat > branch.expect <<EOF
- master
-* mybranch
-EOF
-
-test_expect_success 'git branch' '
- git branch > branch.output &&
- test_cmp branch.expect branch.output
-'
-
-test_expect_success 'git resolve now fails' '
- git checkout mybranch &&
- echo "Work, work, work" >>hello &&
- test_tick &&
- git commit -m "Some work." -i hello &&
-
- git checkout master &&
-
- echo "Play, play, play" >>hello &&
- echo "Lots of fun" >>example &&
- test_tick &&
- git commit -m "Some fun." -i hello example &&
-
- test_must_fail git merge -m "Merge work in mybranch" mybranch
-'
-
-cat > hello << EOF
-Hello World
-It's a new day for git
-Play, play, play
-Work, work, work
-EOF
-
-cat > show-branch.expect << EOF
-* [master] Merge work in mybranch
- ! [mybranch] Some work.
---
-- [master] Merge work in mybranch
-*+ [mybranch] Some work.
-* [master^] Some fun.
-EOF
-
-test_expect_success 'git show-branch' '
- test_tick &&
- git commit -m "Merge work in mybranch" -i hello &&
- git show-branch --topo-order --more=1 master mybranch \
- > show-branch.output &&
- test_cmp show-branch.expect show-branch.output
-'
-
-cat > resolve.expect << EOF
-Updating VARIABLE..VARIABLE
-FASTFORWARD (no commit created; -m option ignored)
- example | 1 +
- hello | 1 +
- 2 files changed, 2 insertions(+)
-EOF
-
-test_expect_success 'git resolve' '
- git checkout mybranch &&
- git merge -m "Merge upstream changes." master |
- sed -e "1s/[0-9a-f]\{7\}/VARIABLE/g" \
- -e "s/^Fast[- ]forward /FASTFORWARD /" >resolve.output
-'
-
-test_expect_success 'git resolve output' '
- test_i18ncmp resolve.expect resolve.output
-'
-
-cat > show-branch2.expect << EOF
-! [master] Merge work in mybranch
- * [mybranch] Merge work in mybranch
---
--- [master] Merge work in mybranch
-EOF
-
-test_expect_success 'git show-branch (part 2)' '
- git show-branch --topo-order master mybranch > show-branch2.output &&
- test_cmp show-branch2.expect show-branch2.output
-'
-
-cat > show-branch3.expect << EOF
-! [master] Merge work in mybranch
- * [mybranch] Merge work in mybranch
---
--- [master] Merge work in mybranch
-+* [master^2] Some work.
-+* [master^] Some fun.
-EOF
-
-test_expect_success 'git show-branch (part 3)' '
- git show-branch --topo-order --more=2 master mybranch \
- > show-branch3.output &&
- test_cmp show-branch3.expect show-branch3.output
-'
-
-test_expect_success 'rewind to "Some fun." and "Some work."' '
- git checkout mybranch &&
- git reset --hard master^2 &&
- git checkout master &&
- git reset --hard master^
-'
-
-cat > show-branch4.expect << EOF
-* [master] Some fun.
- ! [mybranch] Some work.
---
-* [master] Some fun.
- + [mybranch] Some work.
-*+ [master^] Initial commit
-EOF
-
-test_expect_success 'git show-branch (part 4)' '
- git show-branch --topo-order > show-branch4.output &&
- test_cmp show-branch4.expect show-branch4.output
-'
-
-test_expect_success 'manual merge' '
- mb=$(git merge-base HEAD mybranch) &&
- git name-rev --name-only --tags $mb > name-rev.output &&
- test "my-first-tag" = $(cat name-rev.output) &&
-
- git read-tree -m -u $mb HEAD mybranch
-'
-
-cat > ls-files.expect << EOF
-100644 7f8b141b65fdcee47321e399a2598a235a032422 0 example
-100644 557db03de997c86a4a028e1ebd3a1ceb225be238 1 hello
-100644 ba42a2a96e3027f3333e13ede4ccf4498c3ae942 2 hello
-100644 cc44c73eb783565da5831b4d820c962954019b69 3 hello
-EOF
-
-test_expect_success 'git ls-files --stage' '
- git ls-files --stage > ls-files.output &&
- test_cmp ls-files.expect ls-files.output
-'
-
-cat > ls-files-unmerged.expect << EOF
-100644 557db03de997c86a4a028e1ebd3a1ceb225be238 1 hello
-100644 ba42a2a96e3027f3333e13ede4ccf4498c3ae942 2 hello
-100644 cc44c73eb783565da5831b4d820c962954019b69 3 hello
-EOF
-
-test_expect_success 'git ls-files --unmerged' '
- git ls-files --unmerged > ls-files-unmerged.output &&
- test_cmp ls-files-unmerged.expect ls-files-unmerged.output
-'
-
-test_expect_success 'git-merge-index' '
- test_must_fail git merge-index git-merge-one-file hello
-'
-
-test_expect_success 'git ls-files --stage (part 2)' '
- git ls-files --stage > ls-files.output2 &&
- test_cmp ls-files.expect ls-files.output2
-'
-
-test_expect_success 'git repack' 'git repack'
-test_expect_success 'git prune-packed' 'git prune-packed'
-test_expect_success '-> only packed objects' '
- git prune && # Remove conflict marked blobs
- test $(find .git/objects/[0-9a-f][0-9a-f] -type f -print 2>/dev/null | wc -l) = 0
-'
-
-test_done
diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh
index 364a537000..cbeb9bebee 100755
--- a/t/t1300-repo-config.sh
+++ b/t/t1300-repo-config.sh
@@ -901,6 +901,36 @@ test_expect_success 'get --path barfs on boolean variable' '
test_must_fail git config --get --path path.bool
'
+test_expect_success 'get --expiry-date' '
+ rel="3.weeks.5.days.00:00" &&
+ rel_out="$rel ->" &&
+ cat >.git/config <<-\EOF &&
+ [date]
+ valid1 = "3.weeks.5.days 00:00"
+ valid2 = "Fri Jun 4 15:46:55 2010"
+ valid3 = "2017/11/11 11:11:11PM"
+ valid4 = "2017/11/10 09:08:07 PM"
+ valid5 = "never"
+ invalid1 = "abc"
+ EOF
+ cat >expect <<-EOF &&
+ $(test-date timestamp $rel)
+ 1275666415
+ 1510441871
+ 1510348087
+ 0
+ EOF
+ {
+ echo "$rel_out $(git config --expiry-date date.valid1)"
+ git config --expiry-date date.valid2 &&
+ git config --expiry-date date.valid3 &&
+ git config --expiry-date date.valid4 &&
+ git config --expiry-date date.valid5
+ } >actual &&
+ test_cmp expect actual &&
+ test_must_fail git config --expiry-date date.invalid1
+'
+
cat > expect << EOF
[quote]
leading = " test"
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index dc98b4bc6d..664a3a4e4e 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -1253,7 +1253,10 @@ run_with_limited_open_files () {
(ulimit -n 32 && "$@")
}
-test_lazy_prereq ULIMIT_FILE_DESCRIPTORS 'run_with_limited_open_files true'
+test_lazy_prereq ULIMIT_FILE_DESCRIPTORS '
+ test_have_prereq !MINGW,!CYGWIN &&
+ run_with_limited_open_files true
+'
test_expect_success ULIMIT_FILE_DESCRIPTORS 'large transaction creating branches does not burst open file limit' '
(
diff --git a/t/t1401-symbolic-ref.sh b/t/t1401-symbolic-ref.sh
index eec3e90f9c..9e782a8122 100755
--- a/t/t1401-symbolic-ref.sh
+++ b/t/t1401-symbolic-ref.sh
@@ -129,11 +129,35 @@ test_expect_success 'symbolic-ref does not create ref d/f conflicts' '
test_must_fail git symbolic-ref refs/heads/df/conflict refs/heads/df
'
-test_expect_success 'symbolic-ref handles existing pointer to invalid name' '
+test_expect_success 'symbolic-ref can overwrite pointer to invalid name' '
+ test_when_finished reset_to_sane &&
head=$(git rev-parse HEAD) &&
git symbolic-ref HEAD refs/heads/outer &&
+ test_when_finished "git update-ref -d refs/heads/outer/inner" &&
git update-ref refs/heads/outer/inner $head &&
git symbolic-ref HEAD refs/heads/unrelated
'
+test_expect_success 'symbolic-ref can resolve d/f name (EISDIR)' '
+ test_when_finished reset_to_sane &&
+ head=$(git rev-parse HEAD) &&
+ git symbolic-ref HEAD refs/heads/outer/inner &&
+ test_when_finished "git update-ref -d refs/heads/outer" &&
+ git update-ref refs/heads/outer $head &&
+ echo refs/heads/outer/inner >expect &&
+ git symbolic-ref HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'symbolic-ref can resolve d/f name (ENOTDIR)' '
+ test_when_finished reset_to_sane &&
+ head=$(git rev-parse HEAD) &&
+ git symbolic-ref HEAD refs/heads/outer &&
+ test_when_finished "git update-ref -d refs/heads/outer/inner" &&
+ git update-ref refs/heads/outer/inner $head &&
+ echo refs/heads/outer >expect &&
+ git symbolic-ref HEAD >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t1402-check-ref-format.sh b/t/t1402-check-ref-format.sh
index 0790edf60d..98e4a8613b 100755
--- a/t/t1402-check-ref-format.sh
+++ b/t/t1402-check-ref-format.sh
@@ -144,6 +144,11 @@ test_expect_success "check-ref-format --branch @{-1}" '
refname2=$(git check-ref-format --branch @{-2}) &&
test "$refname2" = master'
+test_expect_success 'check-ref-format --branch -naster' '
+ test_must_fail git check-ref-format --branch -naster >actual &&
+ test_must_be_empty actual
+'
+
test_expect_success 'check-ref-format --branch from subdir' '
mkdir subdir &&
@@ -161,6 +166,17 @@ test_expect_success 'check-ref-format --branch from subdir' '
test "$refname" = "$sha1"
'
+test_expect_success 'check-ref-format --branch @{-1} from non-repo' '
+ nongit test_must_fail git check-ref-format --branch @{-1} >actual &&
+ test_must_be_empty actual
+'
+
+test_expect_success 'check-ref-format --branch master from non-repo' '
+ echo master >expect &&
+ nongit git check-ref-format --branch master >actual &&
+ test_cmp expect actual
+'
+
valid_ref_normalized() {
prereq=
case $1 in
diff --git a/t/t1404-update-ref-errors.sh b/t/t1404-update-ref-errors.sh
index c34ece48f5..3a887b5113 100755
--- a/t/t1404-update-ref-errors.sh
+++ b/t/t1404-update-ref-errors.sh
@@ -34,6 +34,81 @@ test_update_rejected () {
Q="'"
+# Test adding and deleting D/F-conflicting references in a single
+# transaction.
+df_test() {
+ prefix="$1"
+ pack=: symadd=false symdel=false add_del=false addref= delref=
+ shift
+ while test $# -gt 0
+ do
+ case "$1" in
+ --pack)
+ pack="git pack-refs --all"
+ shift
+ ;;
+ --sym-add)
+ # Perform the add via a symbolic reference
+ symadd=true
+ shift
+ ;;
+ --sym-del)
+ # Perform the del via a symbolic reference
+ symdel=true
+ shift
+ ;;
+ --del-add)
+ # Delete first reference then add second
+ add_del=false
+ delref="$prefix/r/$2"
+ addref="$prefix/r/$3"
+ shift 3
+ ;;
+ --add-del)
+ # Add first reference then delete second
+ add_del=true
+ addref="$prefix/r/$2"
+ delref="$prefix/r/$3"
+ shift 3
+ ;;
+ *)
+ echo 1>&2 "Extra args to df_test: $*"
+ return 1
+ ;;
+ esac
+ done
+ git update-ref "$delref" $C &&
+ if $symadd
+ then
+ addname="$prefix/s/symadd" &&
+ git symbolic-ref "$addname" "$addref"
+ else
+ addname="$addref"
+ fi &&
+ if $symdel
+ then
+ delname="$prefix/s/symdel" &&
+ git symbolic-ref "$delname" "$delref"
+ else
+ delname="$delref"
+ fi &&
+ cat >expected-err <<-EOF &&
+ fatal: cannot lock ref $Q$addname$Q: $Q$delref$Q exists; cannot create $Q$addref$Q
+ EOF
+ $pack &&
+ if $add_del
+ then
+ printf "%s\n" "create $addname $D" "delete $delname"
+ else
+ printf "%s\n" "delete $delname" "create $addname $D"
+ fi >commands &&
+ test_must_fail git update-ref --stdin <commands 2>output.err &&
+ test_cmp expected-err output.err &&
+ printf "%s\n" "$C $delref" >expected-refs &&
+ git for-each-ref --format="%(objectname) %(refname)" $prefix/r >actual-refs &&
+ test_cmp expected-refs actual-refs
+}
+
test_expect_success 'setup' '
git commit --allow-empty -m Initial &&
@@ -188,6 +263,72 @@ test_expect_success 'empty directory should not fool 1-arg delete' '
git update-ref --stdin
'
+test_expect_success 'D/F conflict prevents add long + delete short' '
+ df_test refs/df-al-ds --add-del foo/bar foo
+'
+
+test_expect_success 'D/F conflict prevents add short + delete long' '
+ df_test refs/df-as-dl --add-del foo foo/bar
+'
+
+test_expect_success 'D/F conflict prevents delete long + add short' '
+ df_test refs/df-dl-as --del-add foo/bar foo
+'
+
+test_expect_success 'D/F conflict prevents delete short + add long' '
+ df_test refs/df-ds-al --del-add foo foo/bar
+'
+
+test_expect_success 'D/F conflict prevents add long + delete short packed' '
+ df_test refs/df-al-dsp --pack --add-del foo/bar foo
+'
+
+test_expect_success 'D/F conflict prevents add short + delete long packed' '
+ df_test refs/df-as-dlp --pack --add-del foo foo/bar
+'
+
+test_expect_success 'D/F conflict prevents delete long packed + add short' '
+ df_test refs/df-dlp-as --pack --del-add foo/bar foo
+'
+
+test_expect_success 'D/F conflict prevents delete short packed + add long' '
+ df_test refs/df-dsp-al --pack --del-add foo foo/bar
+'
+
+# Try some combinations involving symbolic refs...
+
+test_expect_success 'D/F conflict prevents indirect add long + delete short' '
+ df_test refs/df-ial-ds --sym-add --add-del foo/bar foo
+'
+
+test_expect_success 'D/F conflict prevents indirect add long + indirect delete short' '
+ df_test refs/df-ial-ids --sym-add --sym-del --add-del foo/bar foo
+'
+
+test_expect_success 'D/F conflict prevents indirect add short + indirect delete long' '
+ df_test refs/df-ias-idl --sym-add --sym-del --add-del foo foo/bar
+'
+
+test_expect_success 'D/F conflict prevents indirect delete long + indirect add short' '
+ df_test refs/df-idl-ias --sym-add --sym-del --del-add foo/bar foo
+'
+
+test_expect_success 'D/F conflict prevents indirect add long + delete short packed' '
+ df_test refs/df-ial-dsp --sym-add --pack --add-del foo/bar foo
+'
+
+test_expect_success 'D/F conflict prevents indirect add long + indirect delete short packed' '
+ df_test refs/df-ial-idsp --sym-add --sym-del --pack --add-del foo/bar foo
+'
+
+test_expect_success 'D/F conflict prevents add long + indirect delete short packed' '
+ df_test refs/df-al-idsp --sym-del --pack --add-del foo/bar foo
+'
+
+test_expect_success 'D/F conflict prevents indirect delete long packed + indirect add short' '
+ df_test refs/df-idlp-ias --sym-add --sym-del --pack --del-add foo/bar foo
+'
+
# Test various errors when reading the old values of references...
test_expect_success 'missing old value blocks update' '
@@ -404,4 +545,77 @@ test_expect_success 'broken reference blocks indirect create' '
test_cmp expected output.err
'
+test_expect_success 'no bogus intermediate values during delete' '
+ prefix=refs/slow-transaction &&
+ # Set up a reference with differing loose and packed versions:
+ git update-ref $prefix/foo $C &&
+ git pack-refs --all &&
+ git update-ref $prefix/foo $D &&
+ git for-each-ref $prefix >unchanged &&
+ # Now try to update the reference, but hold the `packed-refs` lock
+ # for a while to see what happens while the process is blocked:
+ : >.git/packed-refs.lock &&
+ test_when_finished "rm -f .git/packed-refs.lock" &&
+ {
+ # Note: the following command is intentionally run in the
+ # background. We increase the timeout so that `update-ref`
+ # attempts to acquire the `packed-refs` lock for longer than
+ # it takes for us to do the check then delete it:
+ git -c core.packedrefstimeout=3000 update-ref -d $prefix/foo &
+ } &&
+ pid2=$! &&
+ # Give update-ref plenty of time to get to the point where it tries
+ # to lock packed-refs:
+ sleep 1 &&
+ # Make sure that update-ref did not complete despite the lock:
+ kill -0 $pid2 &&
+ # Verify that the reference still has its old value:
+ sha1=$(git rev-parse --verify --quiet $prefix/foo || echo undefined) &&
+ case "$sha1" in
+ $D)
+ # This is what we hope for; it means that nothing
+ # user-visible has changed yet.
+ : ;;
+ undefined)
+ # This is not correct; it means the deletion has happened
+ # already even though update-ref should not have been
+ # able to acquire the lock yet.
+ echo "$prefix/foo deleted prematurely" &&
+ break
+ ;;
+ $C)
+ # This value should never be seen. Probably the loose
+ # reference has been deleted but the packed reference
+ # is still there:
+ echo "$prefix/foo incorrectly observed to be C" &&
+ break
+ ;;
+ *)
+ # WTF?
+ echo "unexpected value observed for $prefix/foo: $sha1" &&
+ break
+ ;;
+ esac >out &&
+ rm -f .git/packed-refs.lock &&
+ wait $pid2 &&
+ test_must_be_empty out &&
+ test_must_fail git rev-parse --verify --quiet $prefix/foo
+'
+
+test_expect_success 'delete fails cleanly if packed-refs file is locked' '
+ prefix=refs/locked-packed-refs &&
+ # Set up a reference with differing loose and packed versions:
+ git update-ref $prefix/foo $C &&
+ git pack-refs --all &&
+ git update-ref $prefix/foo $D &&
+ git for-each-ref $prefix >unchanged &&
+ # Now try to delete it while the `packed-refs` lock is held:
+ : >.git/packed-refs.lock &&
+ test_when_finished "rm -f .git/packed-refs.lock" &&
+ test_must_fail git update-ref -d $prefix/foo >out 2>err &&
+ git for-each-ref $prefix >actual &&
+ test_i18ngrep "Unable to create $Q.*packed-refs.lock$Q: File exists" err &&
+ test_cmp unchanged actual
+'
+
test_done
diff --git a/t/t1407-worktree-ref-store.sh b/t/t1407-worktree-ref-store.sh
index 5df06f3556..8842d0329f 100755
--- a/t/t1407-worktree-ref-store.sh
+++ b/t/t1407-worktree-ref-store.sh
@@ -49,4 +49,34 @@ test_expect_success 'create_symref(FOO, refs/heads/master)' '
test_cmp expected actual
'
+test_expect_success 'for_each_reflog()' '
+ echo $_z40 > .git/logs/PSEUDO-MAIN &&
+ mkdir -p .git/logs/refs/bisect &&
+ echo $_z40 > .git/logs/refs/bisect/random &&
+
+ echo $_z40 > .git/worktrees/wt/logs/PSEUDO-WT &&
+ mkdir -p .git/worktrees/wt/logs/refs/bisect &&
+ echo $_z40 > .git/worktrees/wt/logs/refs/bisect/wt-random &&
+
+ $RWT for-each-reflog | cut -c 42- | sort >actual &&
+ cat >expected <<-\EOF &&
+ HEAD 0x1
+ PSEUDO-WT 0x0
+ refs/bisect/wt-random 0x0
+ refs/heads/master 0x0
+ refs/heads/wt-master 0x0
+ EOF
+ test_cmp expected actual &&
+
+ $RMAIN for-each-reflog | cut -c 42- | sort >actual &&
+ cat >expected <<-\EOF &&
+ HEAD 0x1
+ PSEUDO-MAIN 0x0
+ refs/bisect/random 0x0
+ refs/heads/master 0x0
+ refs/heads/wt-master 0x0
+ EOF
+ test_cmp expected actual
+'
+
test_done
diff --git a/t/t1408-packed-refs.sh b/t/t1408-packed-refs.sh
new file mode 100755
index 0000000000..1e44a17eea
--- /dev/null
+++ b/t/t1408-packed-refs.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+test_description='packed-refs entries are covered by loose refs'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ test_tick &&
+ git commit --allow-empty -m one &&
+ one=$(git rev-parse HEAD) &&
+ git for-each-ref >actual &&
+ echo "$one commit refs/heads/master" >expect &&
+ test_cmp expect actual &&
+
+ git pack-refs --all &&
+ git for-each-ref >actual &&
+ echo "$one commit refs/heads/master" >expect &&
+ test_cmp expect actual &&
+
+ git checkout --orphan another &&
+ test_tick &&
+ git commit --allow-empty -m two &&
+ two=$(git rev-parse HEAD) &&
+ git checkout -B master &&
+ git branch -D another &&
+
+ git for-each-ref >actual &&
+ echo "$two commit refs/heads/master" >expect &&
+ test_cmp expect actual &&
+
+ git reflog expire --expire=now --all &&
+ git prune &&
+ git tag -m v1.0 v1.0 master
+'
+
+test_expect_success 'no error from stale entry in packed-refs' '
+ git describe master >actual 2>&1 &&
+ echo "v1.0" >expect &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t1409-avoid-packing-refs.sh b/t/t1409-avoid-packing-refs.sh
new file mode 100755
index 0000000000..e5cb8a252d
--- /dev/null
+++ b/t/t1409-avoid-packing-refs.sh
@@ -0,0 +1,118 @@
+#!/bin/sh
+
+test_description='avoid rewriting packed-refs unnecessarily'
+
+. ./test-lib.sh
+
+# Add an identifying mark to the packed-refs file header line. This
+# shouldn't upset readers, and it should be omitted if the file is
+# ever rewritten.
+mark_packed_refs () {
+ sed -e "s/^\(#.*\)/\1 t1409 /" <.git/packed-refs >.git/packed-refs.new &&
+ mv .git/packed-refs.new .git/packed-refs
+}
+
+# Verify that the packed-refs file is still marked.
+check_packed_refs_marked () {
+ grep -q '^#.* t1409 ' .git/packed-refs
+}
+
+test_expect_success 'setup' '
+ git commit --allow-empty -m "Commit A" &&
+ A=$(git rev-parse HEAD) &&
+ git commit --allow-empty -m "Commit B" &&
+ B=$(git rev-parse HEAD) &&
+ git commit --allow-empty -m "Commit C" &&
+ C=$(git rev-parse HEAD)
+'
+
+test_expect_success 'do not create packed-refs file gratuitously' '
+ test_must_fail test -f .git/packed-refs &&
+ git update-ref refs/heads/foo $A &&
+ test_must_fail test -f .git/packed-refs &&
+ git update-ref refs/heads/foo $B &&
+ test_must_fail test -f .git/packed-refs &&
+ git update-ref refs/heads/foo $C $B &&
+ test_must_fail test -f .git/packed-refs &&
+ git update-ref -d refs/heads/foo &&
+ test_must_fail test -f .git/packed-refs
+'
+
+test_expect_success 'check that marking the packed-refs file works' '
+ git for-each-ref >expected &&
+ git pack-refs --all &&
+ mark_packed_refs &&
+ check_packed_refs_marked &&
+ git for-each-ref >actual &&
+ test_cmp expected actual &&
+ git pack-refs --all &&
+ test_must_fail check_packed_refs_marked &&
+ git for-each-ref >actual2 &&
+ test_cmp expected actual2
+'
+
+test_expect_success 'leave packed-refs untouched on update of packed' '
+ git update-ref refs/heads/packed-update $A &&
+ git pack-refs --all &&
+ mark_packed_refs &&
+ git update-ref refs/heads/packed-update $B &&
+ check_packed_refs_marked
+'
+
+test_expect_success 'leave packed-refs untouched on checked update of packed' '
+ git update-ref refs/heads/packed-checked-update $A &&
+ git pack-refs --all &&
+ mark_packed_refs &&
+ git update-ref refs/heads/packed-checked-update $B $A &&
+ check_packed_refs_marked
+'
+
+test_expect_success 'leave packed-refs untouched on verify of packed' '
+ git update-ref refs/heads/packed-verify $A &&
+ git pack-refs --all &&
+ mark_packed_refs &&
+ echo "verify refs/heads/packed-verify $A" | git update-ref --stdin &&
+ check_packed_refs_marked
+'
+
+test_expect_success 'touch packed-refs on delete of packed' '
+ git update-ref refs/heads/packed-delete $A &&
+ git pack-refs --all &&
+ mark_packed_refs &&
+ git update-ref -d refs/heads/packed-delete &&
+ test_must_fail check_packed_refs_marked
+'
+
+test_expect_success 'leave packed-refs untouched on update of loose' '
+ git pack-refs --all &&
+ git update-ref refs/heads/loose-update $A &&
+ mark_packed_refs &&
+ git update-ref refs/heads/loose-update $B &&
+ check_packed_refs_marked
+'
+
+test_expect_success 'leave packed-refs untouched on checked update of loose' '
+ git pack-refs --all &&
+ git update-ref refs/heads/loose-checked-update $A &&
+ mark_packed_refs &&
+ git update-ref refs/heads/loose-checked-update $B $A &&
+ check_packed_refs_marked
+'
+
+test_expect_success 'leave packed-refs untouched on verify of loose' '
+ git pack-refs --all &&
+ git update-ref refs/heads/loose-verify $A &&
+ mark_packed_refs &&
+ echo "verify refs/heads/loose-verify $A" | git update-ref --stdin &&
+ check_packed_refs_marked
+'
+
+test_expect_success 'leave packed-refs untouched on delete of loose' '
+ git pack-refs --all &&
+ git update-ref refs/heads/loose-delete $A &&
+ mark_packed_refs &&
+ git update-ref -d refs/heads/loose-delete &&
+ check_packed_refs_marked
+'
+
+test_done
diff --git a/t/t1430-bad-ref-name.sh b/t/t1430-bad-ref-name.sh
index e88349c8a0..c7878a60ed 100755
--- a/t/t1430-bad-ref-name.sh
+++ b/t/t1430-bad-ref-name.sh
@@ -331,4 +331,47 @@ test_expect_success 'update-ref --stdin -z fails delete with bad ref name' '
grep "fatal: invalid ref format: ~a" err
'
+test_expect_success 'branch rejects HEAD as a branch name' '
+ test_must_fail git branch HEAD HEAD^ &&
+ test_must_fail git show-ref refs/heads/HEAD
+'
+
+test_expect_success 'checkout -b rejects HEAD as a branch name' '
+ test_must_fail git checkout -B HEAD HEAD^ &&
+ test_must_fail git show-ref refs/heads/HEAD
+'
+
+test_expect_success 'update-ref can operate on refs/heads/HEAD' '
+ git update-ref refs/heads/HEAD HEAD^ &&
+ git show-ref refs/heads/HEAD &&
+ git update-ref -d refs/heads/HEAD &&
+ test_must_fail git show-ref refs/heads/HEAD
+'
+
+test_expect_success 'branch -d can remove refs/heads/HEAD' '
+ git update-ref refs/heads/HEAD HEAD^ &&
+ git branch -d HEAD &&
+ test_must_fail git show-ref refs/heads/HEAD
+'
+
+test_expect_success 'branch -m can rename refs/heads/HEAD' '
+ git update-ref refs/heads/HEAD HEAD^ &&
+ git branch -m HEAD tail &&
+ test_must_fail git show-ref refs/heads/HEAD &&
+ git show-ref refs/heads/tail
+'
+
+test_expect_success 'branch -d can remove refs/heads/-dash' '
+ git update-ref refs/heads/-dash HEAD^ &&
+ git branch -d -- -dash &&
+ test_must_fail git show-ref refs/heads/-dash
+'
+
+test_expect_success 'branch -m can rename refs/heads/-dash' '
+ git update-ref refs/heads/-dash HEAD^ &&
+ git branch -m -- -dash dash &&
+ test_must_fail git show-ref refs/heads/-dash &&
+ git show-ref refs/heads/dash
+'
+
test_done
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
index 03d3c7f6d6..5c715fe2cf 100755
--- a/t/t1500-rev-parse.sh
+++ b/t/t1500-rev-parse.sh
@@ -116,6 +116,21 @@ test_expect_success 'git-path inside sub-dir' '
test_cmp expect actual
'
+test_expect_success 'rev-parse --is-shallow-repository in shallow repo' '
+ test_commit test_commit &&
+ echo true >expect &&
+ git clone --depth 1 --no-local . shallow &&
+ test_when_finished "rm -rf shallow" &&
+ git -C shallow rev-parse --is-shallow-repository >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rev-parse --is-shallow-repository in non-shallow repo' '
+ echo false >expect &&
+ git rev-parse --is-shallow-repository >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'showing the superproject correctly' '
git rev-parse --show-superproject-working-tree >out &&
test_must_be_empty out &&
diff --git a/t/t1502-rev-parse-parseopt.sh b/t/t1502-rev-parse-parseopt.sh
index 310f93fd30..a859abedf5 100755
--- a/t/t1502-rev-parse-parseopt.sh
+++ b/t/t1502-rev-parse-parseopt.sh
@@ -28,6 +28,9 @@ test_expect_success 'setup optionspec' '
|g,fluf?path short and long option optional argument
|longest=very-long-argument-hint a very long argument hint
|pair=key=value with an equals sign in the hint
+|aswitch help te=t contains? fl*g characters!`
+|bswitch=hint hint has trailing tab character
+|cswitch switch has trailing tab character
|short-hint=a with a one symbol hint
|
|Extras
@@ -35,6 +38,25 @@ test_expect_success 'setup optionspec' '
EOF
'
+test_expect_success 'setup optionspec-no-switches' '
+ sed -e "s/^|//" >optionspec_no_switches <<\EOF
+|some-command [options] <args>...
+|
+|some-command does foo and bar!
+|--
+EOF
+'
+
+test_expect_success 'setup optionspec-only-hidden-switches' '
+ sed -e "s/^|//" >optionspec_only_hidden_switches <<\EOF
+|some-command [options] <args>...
+|
+|some-command does foo and bar!
+|--
+|hidden1* A hidden switch
+EOF
+'
+
test_expect_success 'test --parseopt help output' '
sed -e "s/^|//" >expect <<\END_EXPECT &&
|cat <<\EOF
@@ -62,6 +84,9 @@ test_expect_success 'test --parseopt help output' '
| --longest <very-long-argument-hint>
| a very long argument hint
| --pair <key=value> with an equals sign in the hint
+| --aswitch help te=t contains? fl*g characters!`
+| --bswitch <hint> hint has trailing tab character
+| --cswitch switch has trailing tab character
| --short-hint <a> with a one symbol hint
|
|Extras
@@ -73,19 +98,100 @@ END_EXPECT
test_i18ncmp expect output
'
+test_expect_success 'test --parseopt help output no switches' '
+ sed -e "s/^|//" >expect <<\END_EXPECT &&
+|cat <<\EOF
+|usage: some-command [options] <args>...
+|
+| some-command does foo and bar!
+|
+|EOF
+END_EXPECT
+ test_expect_code 129 git rev-parse --parseopt -- -h > output < optionspec_no_switches &&
+ test_i18ncmp expect output
+'
+
+test_expect_success 'test --parseopt help output hidden switches' '
+ sed -e "s/^|//" >expect <<\END_EXPECT &&
+|cat <<\EOF
+|usage: some-command [options] <args>...
+|
+| some-command does foo and bar!
+|
+|EOF
+END_EXPECT
+ test_expect_code 129 git rev-parse --parseopt -- -h > output < optionspec_only_hidden_switches &&
+ test_i18ncmp expect output
+'
+
+test_expect_success 'test --parseopt help-all output hidden switches' '
+ sed -e "s/^|//" >expect <<\END_EXPECT &&
+|cat <<\EOF
+|usage: some-command [options] <args>...
+|
+| some-command does foo and bar!
+|
+| --hidden1 A hidden switch
+|
+|EOF
+END_EXPECT
+ test_expect_code 129 git rev-parse --parseopt -- --help-all > output < optionspec_only_hidden_switches &&
+ test_i18ncmp expect output
+'
+
+test_expect_success 'test --parseopt invalid switch help output' '
+ sed -e "s/^|//" >expect <<\END_EXPECT &&
+|error: unknown option `does-not-exist'\''
+|usage: some-command [options] <args>...
+|
+| some-command does foo and bar!
+|
+| -h, --help show the help
+| --foo some nifty option --foo
+| --bar ... some cool option --bar with an argument
+| -b, --baz a short and long option
+|
+|An option group Header
+| -C[...] option C with an optional argument
+| -d, --data[=...] short and long option with an optional argument
+|
+|Argument hints
+| -B <arg> short option required argument
+| --bar2 <arg> long option required argument
+| -e, --fuz <with-space>
+| short and long option required argument
+| -s[<some>] short option optional argument
+| --long[=<data>] long option optional argument
+| -g, --fluf[=<path>] short and long option optional argument
+| --longest <very-long-argument-hint>
+| a very long argument hint
+| --pair <key=value> with an equals sign in the hint
+| --aswitch help te=t contains? fl*g characters!`
+| --bswitch <hint> hint has trailing tab character
+| --cswitch switch has trailing tab character
+| --short-hint <a> with a one symbol hint
+|
+|Extras
+| --extra1 line above used to cause a segfault but no longer does
+|
+END_EXPECT
+ test_expect_code 129 git rev-parse --parseopt -- --does-not-exist 1>/dev/null 2>output < optionspec &&
+ test_i18ncmp expect output
+'
+
test_expect_success 'setup expect.1' "
cat > expect <<EOF
-set -- --foo --bar 'ham' -b -- 'arg'
+set -- --foo --bar 'ham' -b --aswitch -- 'arg'
EOF
"
test_expect_success 'test --parseopt' '
- git rev-parse --parseopt -- --foo --bar=ham --baz arg < optionspec > output &&
+ git rev-parse --parseopt -- --foo --bar=ham --baz --aswitch arg < optionspec > output &&
test_cmp expect output
'
test_expect_success 'test --parseopt with mixed options and arguments' '
- git rev-parse --parseopt -- --foo arg --bar=ham --baz < optionspec > output &&
+ git rev-parse --parseopt -- --foo arg --bar=ham --baz --aswitch < optionspec > output &&
test_cmp expect output
'
diff --git a/t/t1700-split-index.sh b/t/t1700-split-index.sh
index 22f69a410b..af9b847761 100755
--- a/t/t1700-split-index.sh
+++ b/t/t1700-split-index.sh
@@ -6,6 +6,7 @@ test_description='split index mode tests'
# We need total control of index splitting here
sane_unset GIT_TEST_SPLIT_INDEX
+sane_unset GIT_FSMONITOR_TEST
test_expect_success 'enable split index' '
git config splitIndex.maxPercentChange 100 &&
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index 5fc2fb425b..503a88d029 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -117,6 +117,16 @@ test_expect_success 'git branch -m bbb should rename checked out branch' '
test_cmp expect actual
'
+test_expect_success 'renaming checked out branch works with d/f conflict' '
+ test_when_finished "git branch -D foo/bar || git branch -D foo" &&
+ test_when_finished git checkout master &&
+ git checkout -b foo &&
+ git branch -m foo/bar &&
+ git symbolic-ref HEAD >actual &&
+ echo refs/heads/foo/bar >expect &&
+ test_cmp expect actual
+'
+
test_expect_success 'git branch -m o/o o should fail when o/p exists' '
git branch o/o &&
git branch o/p &&
@@ -381,6 +391,262 @@ test_expect_success 'config information was renamed, too' '
test_must_fail git config branch.s/s.dummy
'
+test_expect_success 'git branch -m correctly renames multiple config sections' '
+ test_when_finished "git checkout master" &&
+ git checkout -b source master &&
+
+ # Assert that a config file with multiple config sections has
+ # those sections preserved...
+ cat >expect <<-\EOF &&
+ branch.dest.key1=value1
+ some.gar.b=age
+ branch.dest.key2=value2
+ EOF
+ cat >config.branch <<\EOF &&
+;; Note the lack of -\EOF above & mixed indenting here. This is
+;; intentional, we are also testing that the formatting of copied
+;; sections is preserved.
+
+;; Comment for source. Tabs
+[branch "source"]
+ ;; Comment for the source value
+ key1 = value1
+;; Comment for some.gar. Spaces
+[some "gar"]
+ ;; Comment for the some.gar value
+ b = age
+;; Comment for source, again. Mixed tabs/spaces.
+[branch "source"]
+ ;; Comment for the source value, again
+ key2 = value2
+EOF
+ cat config.branch >>.git/config &&
+ git branch -m source dest &&
+ git config -f .git/config -l | grep -F -e source -e dest -e some.gar >actual &&
+ test_cmp expect actual &&
+
+ # ...and that the comments for those sections are also
+ # preserved.
+ cat config.branch | sed "s/\"source\"/\"dest\"/" >expect &&
+ sed -n -e "/Note the lack/,\$p" .git/config >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git branch -c dumps usage' '
+ test_expect_code 128 git branch -c 2>err &&
+ test_i18ngrep "branch name required" err
+'
+
+test_expect_success 'git branch --copy dumps usage' '
+ test_expect_code 128 git branch --copy 2>err &&
+ test_i18ngrep "branch name required" err
+'
+
+test_expect_success 'git branch -c d e should work' '
+ git branch -l d &&
+ git reflog exists refs/heads/d &&
+ git config branch.d.dummy Hello &&
+ git branch -c d e &&
+ git reflog exists refs/heads/d &&
+ git reflog exists refs/heads/e &&
+ echo Hello >expect &&
+ git config branch.e.dummy >actual &&
+ test_cmp expect actual &&
+ echo Hello >expect &&
+ git config branch.d.dummy >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git branch --copy is a synonym for -c' '
+ git branch -l copy &&
+ git reflog exists refs/heads/copy &&
+ git config branch.copy.dummy Hello &&
+ git branch --copy copy copy-to &&
+ git reflog exists refs/heads/copy &&
+ git reflog exists refs/heads/copy-to &&
+ echo Hello >expect &&
+ git config branch.copy.dummy >actual &&
+ test_cmp expect actual &&
+ echo Hello >expect &&
+ git config branch.copy-to.dummy >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git branch -c ee ef should copy ee to create branch ef' '
+ git checkout -b ee &&
+ git reflog exists refs/heads/ee &&
+ git config branch.ee.dummy Hello &&
+ git branch -c ee ef &&
+ git reflog exists refs/heads/ee &&
+ git reflog exists refs/heads/ef &&
+ test $(git config branch.ee.dummy) = Hello &&
+ test $(git config branch.ef.dummy) = Hello &&
+ test $(git rev-parse --abbrev-ref HEAD) = ee
+'
+
+test_expect_success 'git branch -c f/f g/g should work' '
+ git branch -l f/f &&
+ git reflog exists refs/heads/f/f &&
+ git config branch.f/f.dummy Hello &&
+ git branch -c f/f g/g &&
+ git reflog exists refs/heads/f/f &&
+ git reflog exists refs/heads/g/g &&
+ test $(git config branch.f/f.dummy) = Hello &&
+ test $(git config branch.g/g.dummy) = Hello
+'
+
+test_expect_success 'git branch -c m2 m2 should work' '
+ git branch -l m2 &&
+ git reflog exists refs/heads/m2 &&
+ git config branch.m2.dummy Hello &&
+ git branch -c m2 m2 &&
+ git reflog exists refs/heads/m2 &&
+ test $(git config branch.m2.dummy) = Hello
+'
+
+test_expect_success 'git branch -c zz zz/zz should fail' '
+ git branch -l zz &&
+ git reflog exists refs/heads/zz &&
+ test_must_fail git branch -c zz zz/zz
+'
+
+test_expect_success 'git branch -c b/b b should fail' '
+ git branch -l b/b &&
+ test_must_fail git branch -c b/b b
+'
+
+test_expect_success 'git branch -C o/q o/p should work when o/p exists' '
+ git branch -l o/q &&
+ git reflog exists refs/heads/o/q &&
+ git reflog exists refs/heads/o/p &&
+ git branch -C o/q o/p
+'
+
+test_expect_success 'git branch -c -f o/q o/p should work when o/p exists' '
+ git reflog exists refs/heads/o/q &&
+ git reflog exists refs/heads/o/p &&
+ git branch -c -f o/q o/p
+'
+
+test_expect_success 'git branch -c qq rr/qq should fail when r exists' '
+ git branch qq &&
+ git branch rr &&
+ test_must_fail git branch -c qq rr/qq
+'
+
+test_expect_success 'git branch -C b1 b2 should fail when b2 is checked out' '
+ git branch b1 &&
+ git checkout -b b2 &&
+ test_must_fail git branch -C b1 b2
+'
+
+test_expect_success 'git branch -C c1 c2 should succeed when c1 is checked out' '
+ git checkout -b c1 &&
+ git branch c2 &&
+ git branch -C c1 c2 &&
+ test $(git rev-parse --abbrev-ref HEAD) = c1
+'
+
+test_expect_success 'git branch -C c1 c2 should never touch HEAD' '
+ msg="Branch: copied refs/heads/c1 to refs/heads/c2" &&
+ ! grep "$msg$" .git/logs/HEAD
+'
+
+test_expect_success 'git branch -C master should work when master is checked out' '
+ git checkout master &&
+ git branch -C master
+'
+
+test_expect_success 'git branch -C master master should work when master is checked out' '
+ git checkout master &&
+ git branch -C master master
+'
+
+test_expect_success 'git branch -C master5 master5 should work when master is checked out' '
+ git checkout master &&
+ git branch master5 &&
+ git branch -C master5 master5
+'
+
+test_expect_success 'git branch -C ab cd should overwrite existing config for cd' '
+ git branch -l cd &&
+ git reflog exists refs/heads/cd &&
+ git config branch.cd.dummy CD &&
+ git branch -l ab &&
+ git reflog exists refs/heads/ab &&
+ git config branch.ab.dummy AB &&
+ git branch -C ab cd &&
+ git reflog exists refs/heads/ab &&
+ git reflog exists refs/heads/cd &&
+ test $(git config branch.ab.dummy) = AB &&
+ test $(git config branch.cd.dummy) = AB
+'
+
+test_expect_success 'git branch -c correctly copies multiple config sections' '
+ FOO=1 &&
+ export FOO &&
+ test_when_finished "git checkout master" &&
+ git checkout -b source2 master &&
+
+ # Assert that a config file with multiple config sections has
+ # those sections preserved...
+ cat >expect <<-\EOF &&
+ branch.source2.key1=value1
+ branch.dest2.key1=value1
+ more.gar.b=age
+ branch.source2.key2=value2
+ branch.dest2.key2=value2
+ EOF
+ cat >config.branch <<\EOF &&
+;; Note the lack of -\EOF above & mixed indenting here. This is
+;; intentional, we are also testing that the formatting of copied
+;; sections is preserved.
+
+;; Comment for source2. Tabs
+[branch "source2"]
+ ;; Comment for the source2 value
+ key1 = value1
+;; Comment for more.gar. Spaces
+[more "gar"]
+ ;; Comment for the more.gar value
+ b = age
+;; Comment for source2, again. Mixed tabs/spaces.
+[branch "source2"]
+ ;; Comment for the source2 value, again
+ key2 = value2
+EOF
+ cat config.branch >>.git/config &&
+ git branch -c source2 dest2 &&
+ git config -f .git/config -l | grep -F -e source2 -e dest2 -e more.gar >actual &&
+ test_cmp expect actual &&
+
+ # ...and that the comments and formatting for those sections
+ # is also preserved.
+ cat >expect <<\EOF &&
+;; Comment for source2. Tabs
+[branch "source2"]
+ ;; Comment for the source2 value
+ key1 = value1
+;; Comment for more.gar. Spaces
+[branch "dest2"]
+ ;; Comment for the source2 value
+ key1 = value1
+;; Comment for more.gar. Spaces
+[more "gar"]
+ ;; Comment for the more.gar value
+ b = age
+;; Comment for source2, again. Mixed tabs/spaces.
+[branch "source2"]
+ ;; Comment for the source2 value, again
+ key2 = value2
+[branch "dest2"]
+ ;; Comment for the source2 value, again
+ key2 = value2
+EOF
+ sed -n -e "/Comment for source2/,\$p" .git/config >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'deleting a symref' '
git branch target &&
git symbolic-ref refs/heads/symref refs/heads/target &&
@@ -572,6 +838,7 @@ test_expect_success 'use --set-upstream-to modify HEAD' '
test_expect_success 'use --set-upstream-to modify a particular branch' '
git branch my13 &&
git branch --set-upstream-to master my13 &&
+ test_when_finished "git branch --unset-upstream my13" &&
test "$(git config branch.my13.remote)" = "." &&
test "$(git config branch.my13.merge)" = "refs/heads/master"
'
@@ -617,38 +884,8 @@ test_expect_success 'test --unset-upstream on a particular branch' '
test_must_fail git config branch.my14.merge
'
-test_expect_success '--set-upstream shows message when creating a new branch that exists as remote-tracking' '
- git update-ref refs/remotes/origin/master HEAD &&
- git branch --set-upstream origin/master 2>actual &&
- test_when_finished git update-ref -d refs/remotes/origin/master &&
- test_when_finished git branch -d origin/master &&
- cat >expected <<EOF &&
-The --set-upstream flag is deprecated and will be removed. Consider using --track or --set-upstream-to
-
-If you wanted to make '"'master'"' track '"'origin/master'"', do this:
-
- git branch -d origin/master
- git branch --set-upstream-to origin/master
-EOF
- test_i18ncmp expected actual
-'
-
-test_expect_success '--set-upstream with two args only shows the deprecation message' '
- git branch --set-upstream master my13 2>actual &&
- test_when_finished git branch --unset-upstream master &&
- cat >expected <<EOF &&
-The --set-upstream flag is deprecated and will be removed. Consider using --track or --set-upstream-to
-EOF
- test_i18ncmp expected actual
-'
-
-test_expect_success '--set-upstream with one arg only shows the deprecation message if the branch existed' '
- git branch --set-upstream my13 2>actual &&
- test_when_finished git branch --unset-upstream my13 &&
- cat >expected <<EOF &&
-The --set-upstream flag is deprecated and will be removed. Consider using --track or --set-upstream-to
-EOF
- test_i18ncmp expected actual
+test_expect_success '--set-upstream fails' '
+ test_must_fail git branch --set-upstream origin/master
'
test_expect_success '--set-upstream-to notices an error to set branch as own upstream' '
@@ -973,19 +1210,6 @@ test_expect_success 'attempt to delete a branch merged to its base' '
test_must_fail git branch -d my10
'
-test_expect_success 'use set-upstream on the current branch' '
- git checkout master &&
- git --bare init myupstream.git &&
- git push myupstream.git master:refs/heads/frotz &&
- git remote add origin myupstream.git &&
- git fetch &&
- git branch --set-upstream master origin/frotz &&
-
- test "z$(git config branch.master.remote)" = "zorigin" &&
- test "z$(git config branch.master.merge)" = "zrefs/heads/frotz"
-
-'
-
test_expect_success 'use --edit-description' '
write_script editor <<-\EOF &&
echo "New contents" >"$1"
diff --git a/t/t3210-pack-refs.sh b/t/t3210-pack-refs.sh
index 9b182a0c32..afa27ffe2d 100755
--- a/t/t3210-pack-refs.sh
+++ b/t/t3210-pack-refs.sh
@@ -194,6 +194,33 @@ test_expect_success 'notice d/f conflict with existing ref' '
test_must_fail git branch foo/bar/baz/lots/of/extra/components
'
+test_expect_success 'reject packed-refs with unterminated line' '
+ cp .git/packed-refs .git/packed-refs.bak &&
+ test_when_finished "mv .git/packed-refs.bak .git/packed-refs" &&
+ printf "%s" "$HEAD refs/zzzzz" >>.git/packed-refs &&
+ echo "fatal: unterminated line in .git/packed-refs: $HEAD refs/zzzzz" >expected_err &&
+ test_must_fail git for-each-ref >out 2>err &&
+ test_cmp expected_err err
+'
+
+test_expect_success 'reject packed-refs containing junk' '
+ cp .git/packed-refs .git/packed-refs.bak &&
+ test_when_finished "mv .git/packed-refs.bak .git/packed-refs" &&
+ printf "%s\n" "bogus content" >>.git/packed-refs &&
+ echo "fatal: unexpected line in .git/packed-refs: bogus content" >expected_err &&
+ test_must_fail git for-each-ref >out 2>err &&
+ test_cmp expected_err err
+'
+
+test_expect_success 'reject packed-refs with a short SHA-1' '
+ cp .git/packed-refs .git/packed-refs.bak &&
+ test_when_finished "mv .git/packed-refs.bak .git/packed-refs" &&
+ printf "%.7s %s\n" $HEAD refs/zzzzz >>.git/packed-refs &&
+ printf "fatal: unexpected line in .git/packed-refs: %.7s %s\n" $HEAD refs/zzzzz >expected_err &&
+ test_must_fail git for-each-ref >out 2>err &&
+ test_cmp expected_err err
+'
+
test_expect_success 'timeout if packed-refs.lock exists' '
LOCK=.git/packed-refs.lock &&
>"$LOCK" &&
@@ -211,4 +238,19 @@ test_expect_success 'retry acquiring packed-refs.lock' '
git -c core.packedrefstimeout=3000 pack-refs --all --prune
'
+test_expect_success SYMLINKS 'pack symlinked packed-refs' '
+ # First make sure that symlinking works when reading:
+ git update-ref refs/heads/loosy refs/heads/master &&
+ git for-each-ref >all-refs-before &&
+ mv .git/packed-refs .git/my-deviant-packed-refs &&
+ ln -s my-deviant-packed-refs .git/packed-refs &&
+ git for-each-ref >all-refs-linked &&
+ test_cmp all-refs-before all-refs-linked &&
+ git pack-refs --all --prune &&
+ git for-each-ref >all-refs-packed &&
+ test_cmp all-refs-before all-refs-packed &&
+ test -h .git/packed-refs &&
+ test "$(readlink .git/packed-refs)" = "my-deviant-packed-refs"
+'
+
test_done
diff --git a/t/t3308-notes-merge.sh b/t/t3308-notes-merge.sh
index 19aed7ec95..ab946a5153 100755
--- a/t/t3308-notes-merge.sh
+++ b/t/t3308-notes-merge.sh
@@ -79,7 +79,7 @@ test_expect_success 'fail to merge empty notes ref into empty notes ref (z => y)
test_expect_success 'fail to merge into various non-notes refs' '
test_must_fail git -c "core.notesRef=refs/notes" notes merge x &&
test_must_fail git -c "core.notesRef=refs/notes/" notes merge x &&
- mkdir -p .git/refs/notes/dir &&
+ git update-ref refs/notes/dir/foo HEAD &&
test_must_fail git -c "core.notesRef=refs/notes/dir" notes merge x &&
test_must_fail git -c "core.notesRef=refs/notes/dir/" notes merge x &&
test_must_fail git -c "core.notesRef=refs/heads/master" notes merge x &&
diff --git a/t/t3310-notes-merge-manual-resolve.sh b/t/t3310-notes-merge-manual-resolve.sh
index baef2d6924..9c1bf6eb3d 100755
--- a/t/t3310-notes-merge-manual-resolve.sh
+++ b/t/t3310-notes-merge-manual-resolve.sh
@@ -176,7 +176,7 @@ git rev-parse refs/notes/z > pre_merge_z
test_expect_success 'merge z into m (== y) with default ("manual") resolver => Conflicting 3-way merge' '
git update-ref refs/notes/m refs/notes/y &&
git config core.notesRef refs/notes/m &&
- test_must_fail git notes merge z >output &&
+ test_must_fail git notes merge z >output 2>&1 &&
# Output should point to where to resolve conflicts
test_i18ngrep "\\.git/NOTES_MERGE_WORKTREE" output &&
# Inspect merge conflicts
@@ -379,7 +379,7 @@ git rev-parse refs/notes/z > pre_merge_z
test_expect_success 'redo merge of z into m (== y) with default ("manual") resolver => Conflicting 3-way merge' '
git update-ref refs/notes/m refs/notes/y &&
git config core.notesRef refs/notes/m &&
- test_must_fail git notes merge z >output &&
+ test_must_fail git notes merge z >output 2>&1 &&
# Output should point to where to resolve conflicts
test_i18ngrep "\\.git/NOTES_MERGE_WORKTREE" output &&
# Inspect merge conflicts
@@ -413,7 +413,7 @@ git rev-parse refs/notes/y > pre_merge_y
git rev-parse refs/notes/z > pre_merge_z
test_expect_success 'redo merge of z into m (== y) with default ("manual") resolver => Conflicting 3-way merge' '
- test_must_fail git notes merge z >output &&
+ test_must_fail git notes merge z >output 2>&1 &&
# Output should point to where to resolve conflicts
test_i18ngrep "\\.git/NOTES_MERGE_WORKTREE" output &&
# Inspect merge conflicts
@@ -494,7 +494,7 @@ cp expect_log_y expect_log_m
test_expect_success 'redo merge of z into m (== y) with default ("manual") resolver => Conflicting 3-way merge' '
git update-ref refs/notes/m refs/notes/y &&
- test_must_fail git notes merge z >output &&
+ test_must_fail git notes merge z >output 2>&1 &&
# Output should point to where to resolve conflicts
test_i18ngrep "\\.git/NOTES_MERGE_WORKTREE" output &&
# Inspect merge conflicts
diff --git a/t/t3320-notes-merge-worktrees.sh b/t/t3320-notes-merge-worktrees.sh
index b9c3bc2487..10bfc8b947 100755
--- a/t/t3320-notes-merge-worktrees.sh
+++ b/t/t3320-notes-merge-worktrees.sh
@@ -61,7 +61,7 @@ test_expect_success 'merge z into x while mid-merge on y succeeds' '
(
cd worktree2 &&
git config core.notesRef refs/notes/x &&
- test_must_fail git notes merge z 2>&1 >out &&
+ test_must_fail git notes merge z >out 2>&1 &&
test_i18ngrep "Automatic notes merge failed" out &&
grep -v "A notes merge into refs/notes/x is already in-progress in" out
) &&
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index f5fd15e559..8ac58d5ea5 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -255,4 +255,26 @@ test_expect_success 'rebase commit with an ancient timestamp' '
grep "author .* 34567 +0600$" actual
'
+test_expect_success 'rebase with "From " line in commit message' '
+ git checkout -b preserve-from master~1 &&
+ cat >From_.msg <<EOF &&
+Somebody embedded an mbox in a commit message
+
+This is from so-and-so:
+
+From a@b Mon Sep 17 00:00:00 2001
+From: John Doe <nobody@example.com>
+Date: Sat, 11 Nov 2017 00:00:00 +0000
+Subject: not this message
+
+something
+EOF
+ >From_ &&
+ git add From_ &&
+ git commit -F From_.msg &&
+ git rebase master &&
+ git log -1 --pretty=format:%B >out &&
+ test_cmp From_.msg out
+'
+
test_done
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 37821d2454..6a82d1ed87 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -108,6 +108,17 @@ test_expect_success 'rebase -i with the exec command runs from tree root' '
rm -fr subdir
'
+test_expect_success 'rebase -i with exec allows git commands in subdirs' '
+ test_when_finished "rm -rf subdir" &&
+ test_when_finished "git rebase --abort ||:" &&
+ git checkout master &&
+ mkdir subdir && (cd subdir &&
+ set_fake_editor &&
+ FAKE_LINES="1 exec_cd_subdir_&&_git_rev-parse_--is-inside-work-tree" \
+ git rebase -i HEAD^
+ )
+'
+
test_expect_success 'rebase -i with the exec command checks tree cleanness' '
git checkout master &&
set_fake_editor &&
@@ -1249,20 +1260,13 @@ test_expect_success 'rebase -i respects rebase.missingCommitsCheck = error' '
test B = $(git cat-file commit HEAD^ | sed -ne \$p)
'
-cat >expect <<EOF
-Warning: the command isn't recognized in the following line:
- - badcmd $(git rev-list --oneline -1 master~1)
-
-You can fix this with 'git rebase --edit-todo' and then run 'git rebase --continue'.
-Or you can abort the rebase with 'git rebase --abort'.
-EOF
-
test_expect_success 'static check of bad command' '
rebase_setup_and_clean bad-cmd &&
set_fake_editor &&
test_must_fail env FAKE_LINES="1 2 3 bad 4 5" \
git rebase -i --root 2>actual &&
- test_i18ncmp expect actual &&
+ test_i18ngrep "badcmd $(git rev-list --oneline -1 master~1)" actual &&
+ test_i18ngrep "You can fix this with .git rebase --edit-todo.." actual &&
FAKE_LINES="1 2 3 drop 4 5" git rebase --edit-todo &&
git rebase --continue &&
test E = $(git cat-file commit HEAD | sed -ne \$p) &&
@@ -1284,20 +1288,13 @@ test_expect_success 'tabs and spaces are accepted in the todolist' '
test E = $(git cat-file commit HEAD | sed -ne \$p)
'
-cat >expect <<EOF
-Warning: the SHA-1 is missing or isn't a commit in the following line:
- - edit XXXXXXX False commit
-
-You can fix this with 'git rebase --edit-todo' and then run 'git rebase --continue'.
-Or you can abort the rebase with 'git rebase --abort'.
-EOF
-
test_expect_success 'static check of bad SHA-1' '
rebase_setup_and_clean bad-sha &&
set_fake_editor &&
test_must_fail env FAKE_LINES="1 2 edit fakesha 3 4 5 #" \
git rebase -i --root 2>actual &&
- test_i18ncmp expect actual &&
+ test_i18ngrep "edit XXXXXXX False commit" actual &&
+ test_i18ngrep "You can fix this with .git rebase --edit-todo.." actual &&
FAKE_LINES="1 2 4 5 6" git rebase --edit-todo &&
git rebase --continue &&
test E = $(git cat-file commit HEAD | sed -ne \$p)
diff --git a/t/t3415-rebase-autosquash.sh b/t/t3415-rebase-autosquash.sh
index 5848949ec3..e364c12622 100755
--- a/t/t3415-rebase-autosquash.sh
+++ b/t/t3415-rebase-autosquash.sh
@@ -271,6 +271,18 @@ test_expect_success C_LOCALE_OUTPUT 'autosquash with custom inst format' '
test 2 = $(git cat-file commit HEAD^ | grep squash | wc -l)
'
+test_expect_success 'autosquash with empty custom instructionFormat' '
+ git reset --hard base &&
+ test_commit empty-instructionFormat-test &&
+ (
+ set_cat_todo_editor &&
+ test_must_fail git -c rebase.instructionFormat= \
+ rebase --autosquash --force -i HEAD^ >actual &&
+ git log -1 --format="pick %h %s" >expect &&
+ test_cmp expect actual
+ )
+'
+
set_backup_editor () {
write_script backup-editor.sh <<-\EOF
cp "$1" .git/backup-"$(basename "$1")"
@@ -278,7 +290,7 @@ set_backup_editor () {
test_set_editor "$PWD/backup-editor.sh"
}
-test_expect_failure 'autosquash with multiple empty patches' '
+test_expect_success 'autosquash with multiple empty patches' '
test_tick &&
git commit --allow-empty -m "empty" &&
test_tick &&
@@ -304,4 +316,18 @@ test_expect_success 'extra spaces after fixup!' '
test $base = $parent
'
+test_expect_success 'wrapped original subject' '
+ if test -d .git/rebase-merge; then git rebase --abort; fi &&
+ base=$(git rev-parse HEAD) &&
+ echo "wrapped subject" >wrapped &&
+ git add wrapped &&
+ test_tick &&
+ git commit --allow-empty -m "$(printf "To\nfixup")" &&
+ test_tick &&
+ git commit --allow-empty -m "fixup! To fixup" &&
+ git rebase -i --autosquash --keep-empty HEAD~2 &&
+ parent=$(git rev-parse HEAD^) &&
+ test $base = $parent
+'
+
test_done
diff --git a/t/t3426-rebase-submodule.sh b/t/t3426-rebase-submodule.sh
index ebf4f5e4b2..a2bba04ba9 100755
--- a/t/t3426-rebase-submodule.sh
+++ b/t/t3426-rebase-submodule.sh
@@ -40,4 +40,21 @@ git_rebase_interactive () {
test_submodule_switch "git_rebase_interactive"
+test_expect_success 'rebase interactive ignores modified submodules' '
+ test_when_finished "rm -rf super sub" &&
+ git init sub &&
+ git -C sub commit --allow-empty -m "Initial commit" &&
+ git init super &&
+ git -C super submodule add ../sub &&
+ git -C super config submodule.sub.ignore dirty &&
+ >super/foo &&
+ git -C super add foo &&
+ git -C super commit -m "Initial commit" &&
+ test_commit -C super a &&
+ test_commit -C super b &&
+ test_commit -C super/sub c &&
+ set_fake_editor &&
+ git -C super rebase -i HEAD^^
+'
+
test_done
diff --git a/t/t3512-cherry-pick-submodule.sh b/t/t3512-cherry-pick-submodule.sh
index 6863b7bb6f..ce48c4fcca 100755
--- a/t/t3512-cherry-pick-submodule.sh
+++ b/t/t3512-cherry-pick-submodule.sh
@@ -10,4 +10,40 @@ KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES=1
test_submodule_switch "git cherry-pick"
+test_expect_success 'unrelated submodule/file conflict is ignored' '
+ test_create_repo sub &&
+
+ touch sub/file &&
+ git -C sub add file &&
+ git -C sub commit -m "add a file in a submodule" &&
+
+ test_create_repo a_repo &&
+ (
+ cd a_repo &&
+ >a_file &&
+ git add a_file &&
+ git commit -m "add a file" &&
+
+ git branch test &&
+ git checkout test &&
+
+ mkdir sub &&
+ >sub/content &&
+ git add sub/content &&
+ git commit -m "add a regular folder with name sub" &&
+
+ echo "123" >a_file &&
+ git add a_file &&
+ git commit -m "modify a file" &&
+
+ git checkout master &&
+
+ git submodule add ../sub sub &&
+ git submodule update sub &&
+ git commit -m "add a submodule info folder with name sub" &&
+
+ git cherry-pick test
+ )
+'
+
test_done
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index f8568f8841..46f15169f5 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -688,7 +688,7 @@ test_expect_success 'checking out a commit after submodule removal needs manual
git submodule update &&
git checkout -q HEAD^ &&
git checkout -q master 2>actual &&
- test_i18ngrep "^warning: unable to rmdir submod:" actual &&
+ test_i18ngrep "^warning: unable to rmdir '\''submod'\'':" actual &&
git status -s submod >actual &&
echo "?? submod/" >expected &&
test_cmp expected actual &&
@@ -858,9 +858,8 @@ test_expect_success 'rm files with two different errors' '
test_i18ncmp expect actual
'
-test_expect_success 'rm empty string should invoke warning' '
- git rm -rf "" 2>output &&
- test_i18ngrep "warning: empty strings" output
+test_expect_success 'rm empty string should fail' '
+ test_must_fail git rm -rf ""
'
test_done
diff --git a/t/t3700-add.sh b/t/t3700-add.sh
index 0aae21d698..2748805642 100755
--- a/t/t3700-add.sh
+++ b/t/t3700-add.sh
@@ -331,9 +331,8 @@ test_expect_success 'git add --dry-run --ignore-missing of non-existing file out
test_i18ncmp expect.err actual.err
'
-test_expect_success 'git add empty string should invoke warning' '
- git add "" 2>output &&
- test_i18ngrep "warning: empty strings" output
+test_expect_success 'git add empty string should fail' '
+ test_must_fail git add ""
'
test_expect_success 'git add --chmod=[+-]x stages correctly' '
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index c0122c7f3b..39c7f2ebd7 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -444,6 +444,14 @@ test_expect_failure 'stash file to directory' '
test foo = "$(cat file/file)"
'
+test_expect_success 'stash create - no changes' '
+ git stash clear &&
+ test_when_finished "git reset --hard HEAD" &&
+ git reset --hard &&
+ git stash create >actual &&
+ test_must_be_empty actual
+'
+
test_expect_success 'stash branch - no stashes on stack, stash-like argument' '
git stash clear &&
test_when_finished "git reset --hard HEAD" &&
@@ -648,6 +656,20 @@ test_expect_success 'stash branch should not drop the stash if the branch exists
git rev-parse stash@{0} --
'
+test_expect_success 'stash branch should not drop the stash if the apply fails' '
+ git stash clear &&
+ git reset HEAD~1 --hard &&
+ echo foo >file &&
+ git add file &&
+ git commit -m initial &&
+ echo bar >file &&
+ git stash &&
+ echo baz >file &&
+ test_when_finished "git checkout master" &&
+ test_must_fail git stash branch new_branch stash@{0} &&
+ git rev-parse stash@{0} --
+'
+
test_expect_success 'stash apply shows status same as git status (relative to current directory)' '
git stash clear &&
echo 1 >subdir/subfile1 &&
@@ -893,6 +915,18 @@ test_expect_success 'create with multiple arguments for the message' '
test_cmp expect actual
'
+test_expect_success 'create in a detached state' '
+ test_when_finished "git checkout master" &&
+ git checkout HEAD~1 &&
+ >foo &&
+ git add foo &&
+ STASH_ID=$(git stash create) &&
+ HEAD_ID=$(git rev-parse --short HEAD) &&
+ echo "WIP on (no branch): ${HEAD_ID} initial" >expect &&
+ git show --pretty=%s -s ${STASH_ID} >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'stash -- <pathspec> stashes and restores the file' '
>foo &&
>bar &&
diff --git a/t/t3905-stash-include-untracked.sh b/t/t3905-stash-include-untracked.sh
index 193adc7b68..bfde4057ad 100755
--- a/t/t3905-stash-include-untracked.sh
+++ b/t/t3905-stash-include-untracked.sh
@@ -211,4 +211,21 @@ test_expect_success 'stash push with $IFS character' '
test_path_is_file bar
'
+cat > .gitignore <<EOF
+ignored
+ignored.d/*
+EOF
+
+test_expect_success 'stash previously ignored file' '
+ git reset HEAD &&
+ git add .gitignore &&
+ git commit -m "Add .gitignore" &&
+ >ignored.d/foo &&
+ echo "!ignored.d/foo" >> .gitignore &&
+ git stash save --include-untracked &&
+ test_path_is_missing ignored.d/foo &&
+ git stash pop &&
+ test_path_is_file ignored.d/foo
+'
+
test_done
diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh
index d09acfe48e..c515e3e53f 100755
--- a/t/t4013-diff-various.sh
+++ b/t/t4013-diff-various.sh
@@ -90,6 +90,14 @@ test_expect_success setup '
git commit -m "Rearranged lines in dir/sub" &&
git checkout master &&
+ GIT_AUTHOR_DATE="2006-06-26 00:06:00 +0000" &&
+ GIT_COMMITTER_DATE="2006-06-26 00:06:00 +0000" &&
+ export GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&
+ git checkout -b mode initial &&
+ git update-index --chmod=+x file0 &&
+ git commit -m "update mode" &&
+ git checkout -f master &&
+
git config diff.renames false &&
git show-branch
@@ -192,6 +200,10 @@ diff-tree --pretty side
diff-tree --pretty -p side
diff-tree --pretty --patch-with-stat side
+diff-tree initial mode
+diff-tree --stat initial mode
+diff-tree --summary initial mode
+
diff-tree master
diff-tree -p master
diff-tree -p -m master
diff --git a/t/t4013/diff.diff-tree_--stat_initial_mode b/t/t4013/diff.diff-tree_--stat_initial_mode
new file mode 100644
index 0000000000..0e5943c2c6
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--stat_initial_mode
@@ -0,0 +1,4 @@
+$ git diff-tree --stat initial mode
+ file0 | 0
+ 1 file changed, 0 insertions(+), 0 deletions(-)
+$
diff --git a/t/t4013/diff.diff-tree_--summary_initial_mode b/t/t4013/diff.diff-tree_--summary_initial_mode
new file mode 100644
index 0000000000..25846b6af8
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--summary_initial_mode
@@ -0,0 +1,3 @@
+$ git diff-tree --summary initial mode
+ mode change 100644 => 100755 file0
+$
diff --git a/t/t4013/diff.diff-tree_initial_mode b/t/t4013/diff.diff-tree_initial_mode
new file mode 100644
index 0000000000..c47c09423e
--- /dev/null
+++ b/t/t4013/diff.diff-tree_initial_mode
@@ -0,0 +1,3 @@
+$ git diff-tree initial mode
+:100644 100755 01e79c32a8c99c557f0757da7cb6d65b3414466d 01e79c32a8c99c557f0757da7cb6d65b3414466d M file0
+$
diff --git a/t/t4013/diff.log_--decorate=full_--all b/t/t4013/diff.log_--decorate=full_--all
index b345b2ebfa..2afe91f116 100644
--- a/t/t4013/diff.log_--decorate=full_--all
+++ b/t/t4013/diff.log_--decorate=full_--all
@@ -1,4 +1,10 @@
$ git log --decorate=full --all
+commit b7e0bc69303b488b47deca799a7d723971dfa6cd (refs/heads/mode)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ update mode
+
commit cd4e72fd96faed3f0ba949dc42967430374e2290 (refs/heads/rearrange)
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:06:00 2006 +0000
diff --git a/t/t4013/diff.log_--decorate_--all b/t/t4013/diff.log_--decorate_--all
index 3aa16a9e42..d0f308ab2b 100644
--- a/t/t4013/diff.log_--decorate_--all
+++ b/t/t4013/diff.log_--decorate_--all
@@ -1,4 +1,10 @@
$ git log --decorate --all
+commit b7e0bc69303b488b47deca799a7d723971dfa6cd (mode)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ update mode
+
commit cd4e72fd96faed3f0ba949dc42967430374e2290 (rearrange)
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:06:00 2006 +0000
diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh
index 94597ff284..559a7541a8 100755
--- a/t/t4015-diff-whitespace.sh
+++ b/t/t4015-diff-whitespace.sh
@@ -106,6 +106,8 @@ test_expect_success 'another test, without options' '
git diff -w -b --ignore-space-at-eol >out &&
test_cmp expect out &&
+ git diff -w --ignore-cr-at-eol >out &&
+ test_cmp expect out &&
tr "Q_" "\015 " <<-\EOF >expect &&
diff --git a/x b/x
@@ -128,6 +130,9 @@ test_expect_success 'another test, without options' '
git diff -b --ignore-space-at-eol >out &&
test_cmp expect out &&
+ git diff -b --ignore-cr-at-eol >out &&
+ test_cmp expect out &&
+
tr "Q_" "\015 " <<-\EOF >expect &&
diff --git a/x b/x
index d99af23..22d9f73 100644
@@ -145,6 +150,29 @@ test_expect_success 'another test, without options' '
CR at end
EOF
git diff --ignore-space-at-eol >out &&
+ test_cmp expect out &&
+
+ git diff --ignore-space-at-eol --ignore-cr-at-eol >out &&
+ test_cmp expect out &&
+
+ tr "Q_" "\015 " <<-\EOF >expect &&
+ diff --git a/x b/x
+ index_d99af23..22d9f73 100644
+ --- a/x
+ +++ b/x
+ @@ -1,6 +1,6 @@
+ -whitespace at beginning
+ -whitespace change
+ -whitespace in the middle
+ -whitespace at end
+ +_ whitespace at beginning
+ +whitespace_ _change
+ +white space in the middle
+ +whitespace at end__
+ unchanged line
+ CR at end
+ EOF
+ git diff --ignore-cr-at-eol >out &&
test_cmp expect out
'
@@ -155,7 +183,7 @@ test_expect_success 'ignore-blank-lines: only new lines' '
" >x &&
git diff --ignore-blank-lines >out &&
>expect &&
- test_cmp out expect
+ test_cmp expect out
'
test_expect_success 'ignore-blank-lines: only new lines with space' '
@@ -165,7 +193,7 @@ test_expect_success 'ignore-blank-lines: only new lines with space' '
" >x &&
git diff -w --ignore-blank-lines >out &&
>expect &&
- test_cmp out expect
+ test_cmp expect out
'
test_expect_success 'ignore-blank-lines: after change' '
@@ -802,7 +830,6 @@ test_expect_success 'combined diff with autocrlf conversion' '
# Start testing the colored format for whitespace checks
test_expect_success 'setup diff colors' '
- git config color.diff always &&
git config color.diff.plain normal &&
git config color.diff.meta bold &&
git config color.diff.frag cyan &&
@@ -972,4 +999,713 @@ test_expect_success 'option overrides diff.wsErrorHighlight' '
'
+test_expect_success 'detect moved code, complete file' '
+ git reset --hard &&
+ cat <<-\EOF >test.c &&
+ #include<stdio.h>
+ main()
+ {
+ printf("Hello World");
+ }
+ EOF
+ git add test.c &&
+ git commit -m "add main function" &&
+ git mv test.c main.c &&
+ test_config color.diff.oldMoved "normal red" &&
+ test_config color.diff.newMoved "normal green" &&
+ git diff HEAD --color-moved=zebra --color --no-renames | test_decode_color >actual &&
+ cat >expected <<-\EOF &&
+ <BOLD>diff --git a/main.c b/main.c<RESET>
+ <BOLD>new file mode 100644<RESET>
+ <BOLD>index 0000000..a986c57<RESET>
+ <BOLD>--- /dev/null<RESET>
+ <BOLD>+++ b/main.c<RESET>
+ <CYAN>@@ -0,0 +1,5 @@<RESET>
+ <BGREEN>+<RESET><BGREEN>#include<stdio.h><RESET>
+ <BGREEN>+<RESET><BGREEN>main()<RESET>
+ <BGREEN>+<RESET><BGREEN>{<RESET>
+ <BGREEN>+<RESET><BGREEN>printf("Hello World");<RESET>
+ <BGREEN>+<RESET><BGREEN>}<RESET>
+ <BOLD>diff --git a/test.c b/test.c<RESET>
+ <BOLD>deleted file mode 100644<RESET>
+ <BOLD>index a986c57..0000000<RESET>
+ <BOLD>--- a/test.c<RESET>
+ <BOLD>+++ /dev/null<RESET>
+ <CYAN>@@ -1,5 +0,0 @@<RESET>
+ <BRED>-#include<stdio.h><RESET>
+ <BRED>-main()<RESET>
+ <BRED>-{<RESET>
+ <BRED>-printf("Hello World");<RESET>
+ <BRED>-}<RESET>
+ EOF
+
+ test_cmp expected actual
+'
+
+test_expect_success 'detect malicious moved code, inside file' '
+ test_config color.diff.oldMoved "normal red" &&
+ test_config color.diff.newMoved "normal green" &&
+ test_config color.diff.oldMovedAlternative "blue" &&
+ test_config color.diff.newMovedAlternative "yellow" &&
+ git reset --hard &&
+ cat <<-\EOF >main.c &&
+ #include<stdio.h>
+ int stuff()
+ {
+ printf("Hello ");
+ printf("World\n");
+ }
+
+ int secure_foo(struct user *u)
+ {
+ if (!u->is_allowed_foo)
+ return;
+ foo(u);
+ }
+
+ int main()
+ {
+ foo();
+ }
+ EOF
+ cat <<-\EOF >test.c &&
+ #include<stdio.h>
+ int bar()
+ {
+ printf("Hello World, but different\n");
+ }
+
+ int another_function()
+ {
+ bar();
+ }
+ EOF
+ git add main.c test.c &&
+ git commit -m "add main and test file" &&
+ cat <<-\EOF >main.c &&
+ #include<stdio.h>
+ int stuff()
+ {
+ printf("Hello ");
+ printf("World\n");
+ }
+
+ int main()
+ {
+ foo();
+ }
+ EOF
+ cat <<-\EOF >test.c &&
+ #include<stdio.h>
+ int bar()
+ {
+ printf("Hello World, but different\n");
+ }
+
+ int secure_foo(struct user *u)
+ {
+ foo(u);
+ if (!u->is_allowed_foo)
+ return;
+ }
+
+ int another_function()
+ {
+ bar();
+ }
+ EOF
+ git diff HEAD --no-renames --color-moved=zebra --color | test_decode_color >actual &&
+ cat <<-\EOF >expected &&
+ <BOLD>diff --git a/main.c b/main.c<RESET>
+ <BOLD>index 27a619c..7cf9336 100644<RESET>
+ <BOLD>--- a/main.c<RESET>
+ <BOLD>+++ b/main.c<RESET>
+ <CYAN>@@ -5,13 +5,6 @@<RESET> <RESET>printf("Hello ");<RESET>
+ printf("World\n");<RESET>
+ }<RESET>
+ <RESET>
+ <BRED>-int secure_foo(struct user *u)<RESET>
+ <BRED>-{<RESET>
+ <BLUE>-if (!u->is_allowed_foo)<RESET>
+ <BLUE>-return;<RESET>
+ <RED>-foo(u);<RESET>
+ <RED>-}<RESET>
+ <RED>-<RESET>
+ int main()<RESET>
+ {<RESET>
+ foo();<RESET>
+ <BOLD>diff --git a/test.c b/test.c<RESET>
+ <BOLD>index 1dc1d85..2bedec9 100644<RESET>
+ <BOLD>--- a/test.c<RESET>
+ <BOLD>+++ b/test.c<RESET>
+ <CYAN>@@ -4,6 +4,13 @@<RESET> <RESET>int bar()<RESET>
+ printf("Hello World, but different\n");<RESET>
+ }<RESET>
+ <RESET>
+ <BGREEN>+<RESET><BGREEN>int secure_foo(struct user *u)<RESET>
+ <BGREEN>+<RESET><BGREEN>{<RESET>
+ <GREEN>+<RESET><GREEN>foo(u);<RESET>
+ <BGREEN>+<RESET><BGREEN>if (!u->is_allowed_foo)<RESET>
+ <BGREEN>+<RESET><BGREEN>return;<RESET>
+ <GREEN>+<RESET><GREEN>}<RESET>
+ <GREEN>+<RESET>
+ int another_function()<RESET>
+ {<RESET>
+ bar();<RESET>
+ EOF
+
+ test_cmp expected actual
+'
+
+test_expect_success 'plain moved code, inside file' '
+ test_config color.diff.oldMoved "normal red" &&
+ test_config color.diff.newMoved "normal green" &&
+ test_config color.diff.oldMovedAlternative "blue" &&
+ test_config color.diff.newMovedAlternative "yellow" &&
+ # needs previous test as setup
+ git diff HEAD --no-renames --color-moved=plain --color | test_decode_color >actual &&
+ cat <<-\EOF >expected &&
+ <BOLD>diff --git a/main.c b/main.c<RESET>
+ <BOLD>index 27a619c..7cf9336 100644<RESET>
+ <BOLD>--- a/main.c<RESET>
+ <BOLD>+++ b/main.c<RESET>
+ <CYAN>@@ -5,13 +5,6 @@<RESET> <RESET>printf("Hello ");<RESET>
+ printf("World\n");<RESET>
+ }<RESET>
+ <RESET>
+ <BRED>-int secure_foo(struct user *u)<RESET>
+ <BRED>-{<RESET>
+ <BRED>-if (!u->is_allowed_foo)<RESET>
+ <BRED>-return;<RESET>
+ <BRED>-foo(u);<RESET>
+ <BRED>-}<RESET>
+ <BRED>-<RESET>
+ int main()<RESET>
+ {<RESET>
+ foo();<RESET>
+ <BOLD>diff --git a/test.c b/test.c<RESET>
+ <BOLD>index 1dc1d85..2bedec9 100644<RESET>
+ <BOLD>--- a/test.c<RESET>
+ <BOLD>+++ b/test.c<RESET>
+ <CYAN>@@ -4,6 +4,13 @@<RESET> <RESET>int bar()<RESET>
+ printf("Hello World, but different\n");<RESET>
+ }<RESET>
+ <RESET>
+ <BGREEN>+<RESET><BGREEN>int secure_foo(struct user *u)<RESET>
+ <BGREEN>+<RESET><BGREEN>{<RESET>
+ <BGREEN>+<RESET><BGREEN>foo(u);<RESET>
+ <BGREEN>+<RESET><BGREEN>if (!u->is_allowed_foo)<RESET>
+ <BGREEN>+<RESET><BGREEN>return;<RESET>
+ <BGREEN>+<RESET><BGREEN>}<RESET>
+ <BGREEN>+<RESET>
+ int another_function()<RESET>
+ {<RESET>
+ bar();<RESET>
+ EOF
+
+ test_cmp expected actual
+'
+
+test_expect_success 'detect permutations inside moved code -- dimmed_zebra' '
+ git reset --hard &&
+ cat <<-\EOF >lines.txt &&
+ long line 1
+ long line 2
+ long line 3
+ line 4
+ line 5
+ line 6
+ line 7
+ line 8
+ line 9
+ line 10
+ line 11
+ line 12
+ line 13
+ long line 14
+ long line 15
+ long line 16
+ EOF
+ git add lines.txt &&
+ git commit -m "add poetry" &&
+ cat <<-\EOF >lines.txt &&
+ line 4
+ line 5
+ line 6
+ line 7
+ line 8
+ line 9
+ long line 1
+ long line 2
+ long line 3
+ long line 14
+ long line 15
+ long line 16
+ line 10
+ line 11
+ line 12
+ line 13
+ EOF
+ test_config color.diff.oldMoved "magenta" &&
+ test_config color.diff.newMoved "cyan" &&
+ test_config color.diff.oldMovedAlternative "blue" &&
+ test_config color.diff.newMovedAlternative "yellow" &&
+ test_config color.diff.oldMovedDimmed "normal magenta" &&
+ test_config color.diff.newMovedDimmed "normal cyan" &&
+ test_config color.diff.oldMovedAlternativeDimmed "normal blue" &&
+ test_config color.diff.newMovedAlternativeDimmed "normal yellow" &&
+ git diff HEAD --no-renames --color-moved=dimmed_zebra --color |
+ grep -v "index" |
+ test_decode_color >actual &&
+ cat <<-\EOF >expected &&
+ <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
+ <BOLD>--- a/lines.txt<RESET>
+ <BOLD>+++ b/lines.txt<RESET>
+ <CYAN>@@ -1,16 +1,16 @@<RESET>
+ <BMAGENTA>-long line 1<RESET>
+ <BMAGENTA>-long line 2<RESET>
+ <BMAGENTA>-long line 3<RESET>
+ line 4<RESET>
+ line 5<RESET>
+ line 6<RESET>
+ line 7<RESET>
+ line 8<RESET>
+ line 9<RESET>
+ <BCYAN>+<RESET><BCYAN>long line 1<RESET>
+ <BCYAN>+<RESET><BCYAN>long line 2<RESET>
+ <CYAN>+<RESET><CYAN>long line 3<RESET>
+ <YELLOW>+<RESET><YELLOW>long line 14<RESET>
+ <BYELLOW>+<RESET><BYELLOW>long line 15<RESET>
+ <BYELLOW>+<RESET><BYELLOW>long line 16<RESET>
+ line 10<RESET>
+ line 11<RESET>
+ line 12<RESET>
+ line 13<RESET>
+ <BMAGENTA>-long line 14<RESET>
+ <BMAGENTA>-long line 15<RESET>
+ <BMAGENTA>-long line 16<RESET>
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'cmd option assumes configured colored-moved' '
+ test_config color.diff.oldMoved "magenta" &&
+ test_config color.diff.newMoved "cyan" &&
+ test_config color.diff.oldMovedAlternative "blue" &&
+ test_config color.diff.newMovedAlternative "yellow" &&
+ test_config color.diff.oldMovedDimmed "normal magenta" &&
+ test_config color.diff.newMovedDimmed "normal cyan" &&
+ test_config color.diff.oldMovedAlternativeDimmed "normal blue" &&
+ test_config color.diff.newMovedAlternativeDimmed "normal yellow" &&
+ test_config diff.colorMoved zebra &&
+ git diff HEAD --no-renames --color-moved --color |
+ grep -v "index" |
+ test_decode_color >actual &&
+ cat <<-\EOF >expected &&
+ <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
+ <BOLD>--- a/lines.txt<RESET>
+ <BOLD>+++ b/lines.txt<RESET>
+ <CYAN>@@ -1,16 +1,16 @@<RESET>
+ <MAGENTA>-long line 1<RESET>
+ <MAGENTA>-long line 2<RESET>
+ <MAGENTA>-long line 3<RESET>
+ line 4<RESET>
+ line 5<RESET>
+ line 6<RESET>
+ line 7<RESET>
+ line 8<RESET>
+ line 9<RESET>
+ <CYAN>+<RESET><CYAN>long line 1<RESET>
+ <CYAN>+<RESET><CYAN>long line 2<RESET>
+ <CYAN>+<RESET><CYAN>long line 3<RESET>
+ <YELLOW>+<RESET><YELLOW>long line 14<RESET>
+ <YELLOW>+<RESET><YELLOW>long line 15<RESET>
+ <YELLOW>+<RESET><YELLOW>long line 16<RESET>
+ line 10<RESET>
+ line 11<RESET>
+ line 12<RESET>
+ line 13<RESET>
+ <MAGENTA>-long line 14<RESET>
+ <MAGENTA>-long line 15<RESET>
+ <MAGENTA>-long line 16<RESET>
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'no effect from --color-moved with --word-diff' '
+ cat <<-\EOF >text.txt &&
+ Lorem Ipsum is simply dummy text of the printing and typesetting industry.
+ EOF
+ git add text.txt &&
+ git commit -a -m "clean state" &&
+ cat <<-\EOF >text.txt &&
+ simply Lorem Ipsum dummy is text of the typesetting and printing industry.
+ EOF
+ git diff --color-moved --word-diff >actual &&
+ git diff --word-diff >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'set up whitespace tests' '
+ git reset --hard &&
+ # Note that these lines have no leading or trailing whitespace.
+ cat <<-\EOF >lines.txt &&
+ line 1
+ line 2
+ line 3
+ line 4
+ line 5
+ long line 6
+ long line 7
+ long line 8
+ long line 9
+ EOF
+ git add lines.txt &&
+ git commit -m "add poetry" &&
+ git config color.diff.oldMoved "magenta" &&
+ git config color.diff.newMoved "cyan"
+'
+
+test_expect_success 'move detection ignoring whitespace ' '
+ q_to_tab <<-\EOF >lines.txt &&
+ Qlong line 6
+ Qlong line 7
+ Qlong line 8
+ Qchanged long line 9
+ line 1
+ line 2
+ line 3
+ line 4
+ line 5
+ EOF
+ git diff HEAD --no-renames --color-moved --color |
+ grep -v "index" |
+ test_decode_color >actual &&
+ cat <<-\EOF >expected &&
+ <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
+ <BOLD>--- a/lines.txt<RESET>
+ <BOLD>+++ b/lines.txt<RESET>
+ <CYAN>@@ -1,9 +1,9 @@<RESET>
+ <GREEN>+<RESET> <GREEN>long line 6<RESET>
+ <GREEN>+<RESET> <GREEN>long line 7<RESET>
+ <GREEN>+<RESET> <GREEN>long line 8<RESET>
+ <GREEN>+<RESET> <GREEN>changed long line 9<RESET>
+ line 1<RESET>
+ line 2<RESET>
+ line 3<RESET>
+ line 4<RESET>
+ line 5<RESET>
+ <RED>-long line 6<RESET>
+ <RED>-long line 7<RESET>
+ <RED>-long line 8<RESET>
+ <RED>-long line 9<RESET>
+ EOF
+ test_cmp expected actual &&
+
+ git diff HEAD --no-renames -w --color-moved --color |
+ grep -v "index" |
+ test_decode_color >actual &&
+ cat <<-\EOF >expected &&
+ <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
+ <BOLD>--- a/lines.txt<RESET>
+ <BOLD>+++ b/lines.txt<RESET>
+ <CYAN>@@ -1,9 +1,9 @@<RESET>
+ <CYAN>+<RESET> <CYAN>long line 6<RESET>
+ <CYAN>+<RESET> <CYAN>long line 7<RESET>
+ <CYAN>+<RESET> <CYAN>long line 8<RESET>
+ <GREEN>+<RESET> <GREEN>changed long line 9<RESET>
+ line 1<RESET>
+ line 2<RESET>
+ line 3<RESET>
+ line 4<RESET>
+ line 5<RESET>
+ <MAGENTA>-long line 6<RESET>
+ <MAGENTA>-long line 7<RESET>
+ <MAGENTA>-long line 8<RESET>
+ <RED>-long line 9<RESET>
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'move detection ignoring whitespace changes' '
+ git reset --hard &&
+ # Lines 6-8 have a space change, but 9 is new whitespace
+ q_to_tab <<-\EOF >lines.txt &&
+ longQline 6
+ longQline 7
+ longQline 8
+ long liQne 9
+ line 1
+ line 2
+ line 3
+ line 4
+ line 5
+ EOF
+
+ git diff HEAD --no-renames --color-moved --color |
+ grep -v "index" |
+ test_decode_color >actual &&
+ cat <<-\EOF >expected &&
+ <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
+ <BOLD>--- a/lines.txt<RESET>
+ <BOLD>+++ b/lines.txt<RESET>
+ <CYAN>@@ -1,9 +1,9 @@<RESET>
+ <GREEN>+<RESET><GREEN>long line 6<RESET>
+ <GREEN>+<RESET><GREEN>long line 7<RESET>
+ <GREEN>+<RESET><GREEN>long line 8<RESET>
+ <GREEN>+<RESET><GREEN>long li ne 9<RESET>
+ line 1<RESET>
+ line 2<RESET>
+ line 3<RESET>
+ line 4<RESET>
+ line 5<RESET>
+ <RED>-long line 6<RESET>
+ <RED>-long line 7<RESET>
+ <RED>-long line 8<RESET>
+ <RED>-long line 9<RESET>
+ EOF
+ test_cmp expected actual &&
+
+ git diff HEAD --no-renames -b --color-moved --color |
+ grep -v "index" |
+ test_decode_color >actual &&
+ cat <<-\EOF >expected &&
+ <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
+ <BOLD>--- a/lines.txt<RESET>
+ <BOLD>+++ b/lines.txt<RESET>
+ <CYAN>@@ -1,9 +1,9 @@<RESET>
+ <CYAN>+<RESET><CYAN>long line 6<RESET>
+ <CYAN>+<RESET><CYAN>long line 7<RESET>
+ <CYAN>+<RESET><CYAN>long line 8<RESET>
+ <GREEN>+<RESET><GREEN>long li ne 9<RESET>
+ line 1<RESET>
+ line 2<RESET>
+ line 3<RESET>
+ line 4<RESET>
+ line 5<RESET>
+ <MAGENTA>-long line 6<RESET>
+ <MAGENTA>-long line 7<RESET>
+ <MAGENTA>-long line 8<RESET>
+ <RED>-long line 9<RESET>
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'move detection ignoring whitespace at eol' '
+ git reset --hard &&
+ # Lines 6-9 have new eol whitespace, but 9 also has it in the middle
+ q_to_tab <<-\EOF >lines.txt &&
+ long line 6Q
+ long line 7Q
+ long line 8Q
+ longQline 9Q
+ line 1
+ line 2
+ line 3
+ line 4
+ line 5
+ EOF
+
+ # avoid cluttering the output with complaints about our eol whitespace
+ test_config core.whitespace -blank-at-eol &&
+
+ git diff HEAD --no-renames --color-moved --color |
+ grep -v "index" |
+ test_decode_color >actual &&
+ cat <<-\EOF >expected &&
+ <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
+ <BOLD>--- a/lines.txt<RESET>
+ <BOLD>+++ b/lines.txt<RESET>
+ <CYAN>@@ -1,9 +1,9 @@<RESET>
+ <GREEN>+<RESET><GREEN>long line 6 <RESET>
+ <GREEN>+<RESET><GREEN>long line 7 <RESET>
+ <GREEN>+<RESET><GREEN>long line 8 <RESET>
+ <GREEN>+<RESET><GREEN>long line 9 <RESET>
+ line 1<RESET>
+ line 2<RESET>
+ line 3<RESET>
+ line 4<RESET>
+ line 5<RESET>
+ <RED>-long line 6<RESET>
+ <RED>-long line 7<RESET>
+ <RED>-long line 8<RESET>
+ <RED>-long line 9<RESET>
+ EOF
+ test_cmp expected actual &&
+
+ git diff HEAD --no-renames --ignore-space-at-eol --color-moved --color |
+ grep -v "index" |
+ test_decode_color >actual &&
+ cat <<-\EOF >expected &&
+ <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
+ <BOLD>--- a/lines.txt<RESET>
+ <BOLD>+++ b/lines.txt<RESET>
+ <CYAN>@@ -1,9 +1,9 @@<RESET>
+ <CYAN>+<RESET><CYAN>long line 6 <RESET>
+ <CYAN>+<RESET><CYAN>long line 7 <RESET>
+ <CYAN>+<RESET><CYAN>long line 8 <RESET>
+ <GREEN>+<RESET><GREEN>long line 9 <RESET>
+ line 1<RESET>
+ line 2<RESET>
+ line 3<RESET>
+ line 4<RESET>
+ line 5<RESET>
+ <MAGENTA>-long line 6<RESET>
+ <MAGENTA>-long line 7<RESET>
+ <MAGENTA>-long line 8<RESET>
+ <RED>-long line 9<RESET>
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'clean up whitespace-test colors' '
+ git config --unset color.diff.oldMoved &&
+ git config --unset color.diff.newMoved
+'
+
+test_expect_success '--color-moved block at end of diff output respects MIN_ALNUM_COUNT' '
+ git reset --hard &&
+ >bar &&
+ cat <<-\EOF >foo &&
+ irrelevant_line
+ line1
+ EOF
+ git add foo bar &&
+ git commit -m x &&
+
+ cat <<-\EOF >bar &&
+ line1
+ EOF
+ cat <<-\EOF >foo &&
+ irrelevant_line
+ EOF
+
+ git diff HEAD --color-moved=zebra --color --no-renames |
+ grep -v "index" |
+ test_decode_color >actual &&
+ cat >expected <<-\EOF &&
+ <BOLD>diff --git a/bar b/bar<RESET>
+ <BOLD>--- a/bar<RESET>
+ <BOLD>+++ b/bar<RESET>
+ <CYAN>@@ -0,0 +1 @@<RESET>
+ <GREEN>+<RESET><GREEN>line1<RESET>
+ <BOLD>diff --git a/foo b/foo<RESET>
+ <BOLD>--- a/foo<RESET>
+ <BOLD>+++ b/foo<RESET>
+ <CYAN>@@ -1,2 +1 @@<RESET>
+ irrelevant_line<RESET>
+ <RED>-line1<RESET>
+ EOF
+
+ test_cmp expected actual
+'
+
+test_expect_success '--color-moved respects MIN_ALNUM_COUNT' '
+ git reset --hard &&
+ cat <<-\EOF >foo &&
+ nineteen chars 456789
+ irrelevant_line
+ twenty chars 234567890
+ EOF
+ >bar &&
+ git add foo bar &&
+ git commit -m x &&
+
+ cat <<-\EOF >foo &&
+ irrelevant_line
+ EOF
+ cat <<-\EOF >bar &&
+ twenty chars 234567890
+ nineteen chars 456789
+ EOF
+
+ git diff HEAD --color-moved=zebra --color --no-renames |
+ grep -v "index" |
+ test_decode_color >actual &&
+ cat >expected <<-\EOF &&
+ <BOLD>diff --git a/bar b/bar<RESET>
+ <BOLD>--- a/bar<RESET>
+ <BOLD>+++ b/bar<RESET>
+ <CYAN>@@ -0,0 +1,2 @@<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN>twenty chars 234567890<RESET>
+ <GREEN>+<RESET><GREEN>nineteen chars 456789<RESET>
+ <BOLD>diff --git a/foo b/foo<RESET>
+ <BOLD>--- a/foo<RESET>
+ <BOLD>+++ b/foo<RESET>
+ <CYAN>@@ -1,3 +1 @@<RESET>
+ <RED>-nineteen chars 456789<RESET>
+ irrelevant_line<RESET>
+ <BOLD;MAGENTA>-twenty chars 234567890<RESET>
+ EOF
+
+ test_cmp expected actual
+'
+
+test_expect_success '--color-moved treats adjacent blocks as separate for MIN_ALNUM_COUNT' '
+ git reset --hard &&
+ cat <<-\EOF >foo &&
+ 7charsA
+ irrelevant_line
+ 7charsB
+ 7charsC
+ EOF
+ >bar &&
+ git add foo bar &&
+ git commit -m x &&
+
+ cat <<-\EOF >foo &&
+ irrelevant_line
+ EOF
+ cat <<-\EOF >bar &&
+ 7charsB
+ 7charsC
+ 7charsA
+ EOF
+
+ git diff HEAD --color-moved=zebra --color --no-renames | grep -v "index" | test_decode_color >actual &&
+ cat >expected <<-\EOF &&
+ <BOLD>diff --git a/bar b/bar<RESET>
+ <BOLD>--- a/bar<RESET>
+ <BOLD>+++ b/bar<RESET>
+ <CYAN>@@ -0,0 +1,3 @@<RESET>
+ <GREEN>+<RESET><GREEN>7charsB<RESET>
+ <GREEN>+<RESET><GREEN>7charsC<RESET>
+ <GREEN>+<RESET><GREEN>7charsA<RESET>
+ <BOLD>diff --git a/foo b/foo<RESET>
+ <BOLD>--- a/foo<RESET>
+ <BOLD>+++ b/foo<RESET>
+ <CYAN>@@ -1,4 +1 @@<RESET>
+ <RED>-7charsA<RESET>
+ irrelevant_line<RESET>
+ <RED>-7charsB<RESET>
+ <RED>-7charsC<RESET>
+ EOF
+
+ test_cmp expected actual
+'
+
+test_expect_success 'move detection with submodules' '
+ test_create_repo bananas &&
+ echo ripe >bananas/recipe &&
+ git -C bananas add recipe &&
+ test_commit fruit &&
+ test_commit -C bananas recipe &&
+ git submodule add ./bananas &&
+ git add bananas &&
+ git commit -a -m "bananas are like a heavy library?" &&
+ echo foul >bananas/recipe &&
+ echo ripe >fruit.t &&
+
+ git diff --submodule=diff --color-moved --color >actual &&
+
+ # no move detection as the moved line is across repository boundaries.
+ test_decode_color <actual >decoded_actual &&
+ ! grep BGREEN decoded_actual &&
+ ! grep BRED decoded_actual &&
+
+ # nor did we mess with it another way
+ git diff --submodule=diff --color | test_decode_color >expect &&
+ test_cmp expect decoded_actual
+'
+
test_done
diff --git a/t/t4027-diff-submodule.sh b/t/t4027-diff-submodule.sh
index 518bf9524e..2ffd11a142 100755
--- a/t/t4027-diff-submodule.sh
+++ b/t/t4027-diff-submodule.sh
@@ -113,35 +113,6 @@ test_expect_success 'git diff HEAD with dirty submodule (work tree, refs match)'
! test -s actual4
'
-test_expect_success 'git diff HEAD with dirty submodule (work tree, refs match) [.git/config]' '
- git config diff.ignoreSubmodules all &&
- git diff HEAD >actual &&
- ! test -s actual &&
- git config submodule.subname.ignore none &&
- git config submodule.subname.path sub &&
- git diff HEAD >actual &&
- sed -e "1,/^@@/d" actual >actual.body &&
- expect_from_to >expect.body $subprev $subprev-dirty &&
- test_cmp expect.body actual.body &&
- git config submodule.subname.ignore all &&
- git diff HEAD >actual2 &&
- ! test -s actual2 &&
- git config submodule.subname.ignore untracked &&
- git diff HEAD >actual3 &&
- sed -e "1,/^@@/d" actual3 >actual3.body &&
- expect_from_to >expect.body $subprev $subprev-dirty &&
- test_cmp expect.body actual3.body &&
- git config submodule.subname.ignore dirty &&
- git diff HEAD >actual4 &&
- ! test -s actual4 &&
- git diff HEAD --ignore-submodules=none >actual &&
- sed -e "1,/^@@/d" actual >actual.body &&
- expect_from_to >expect.body $subprev $subprev-dirty &&
- test_cmp expect.body actual.body &&
- git config --remove-section submodule.subname &&
- git config --unset diff.ignoreSubmodules
-'
-
test_expect_success 'git diff HEAD with dirty submodule (work tree, refs match) [.gitmodules]' '
git config diff.ignoreSubmodules dirty &&
git diff HEAD >actual &&
@@ -208,24 +179,6 @@ test_expect_success 'git diff HEAD with dirty submodule (untracked, refs match)'
! test -s actual4
'
-test_expect_success 'git diff HEAD with dirty submodule (untracked, refs match) [.git/config]' '
- git config submodule.subname.ignore all &&
- git config submodule.subname.path sub &&
- git diff HEAD >actual2 &&
- ! test -s actual2 &&
- git config submodule.subname.ignore untracked &&
- git diff HEAD >actual3 &&
- ! test -s actual3 &&
- git config submodule.subname.ignore dirty &&
- git diff HEAD >actual4 &&
- ! test -s actual4 &&
- git diff --ignore-submodules=none HEAD >actual &&
- sed -e "1,/^@@/d" actual >actual.body &&
- expect_from_to >expect.body $subprev $subprev-dirty &&
- test_cmp expect.body actual.body &&
- git config --remove-section submodule.subname
-'
-
test_expect_success 'git diff HEAD with dirty submodule (untracked, refs match) [.gitmodules]' '
git config --add -f .gitmodules submodule.subname.ignore all &&
git config --add -f .gitmodules submodule.subname.path sub &&
@@ -261,26 +214,6 @@ test_expect_success 'git diff between submodule commits' '
! test -s actual
'
-test_expect_success 'git diff between submodule commits [.git/config]' '
- git diff HEAD^..HEAD >actual &&
- sed -e "1,/^@@/d" actual >actual.body &&
- expect_from_to >expect.body $subtip $subprev &&
- test_cmp expect.body actual.body &&
- git config submodule.subname.ignore dirty &&
- git config submodule.subname.path sub &&
- git diff HEAD^..HEAD >actual &&
- sed -e "1,/^@@/d" actual >actual.body &&
- expect_from_to >expect.body $subtip $subprev &&
- test_cmp expect.body actual.body &&
- git config submodule.subname.ignore all &&
- git diff HEAD^..HEAD >actual &&
- ! test -s actual &&
- git diff --ignore-submodules=dirty HEAD^..HEAD >actual &&
- sed -e "1,/^@@/d" actual >actual.body &&
- expect_from_to >expect.body $subtip $subprev &&
- git config --remove-section submodule.subname
-'
-
test_expect_success 'git diff between submodule commits [.gitmodules]' '
git diff HEAD^..HEAD >actual &&
sed -e "1,/^@@/d" actual >actual.body &&
diff --git a/t/t4051-diff-function-context.sh b/t/t4051-diff-function-context.sh
index 3e6b485ecb..2d76a971c4 100755
--- a/t/t4051-diff-function-context.sh
+++ b/t/t4051-diff-function-context.sh
@@ -85,6 +85,10 @@ test_expect_success 'setup' '
check_diff changed_hello 'changed function'
+test_expect_success ' context includes comment' '
+ grep "^ .*Hello comment" changed_hello.diff
+'
+
test_expect_success ' context includes begin' '
grep "^ .*Begin of hello" changed_hello.diff
'
diff --git a/t/t4051/hello.c b/t/t4051/hello.c
index 63b1a1e4ef..73e767e178 100644
--- a/t/t4051/hello.c
+++ b/t/t4051/hello.c
@@ -1,4 +1,7 @@
+/*
+ * Hello comment.
+ */
static void hello(void) // Begin of hello
{
/*
diff --git a/t/t4059-diff-submodule-not-initialized.sh b/t/t4059-diff-submodule-not-initialized.sh
index cd70fd5192..49bca7b48d 100755
--- a/t/t4059-diff-submodule-not-initialized.sh
+++ b/t/t4059-diff-submodule-not-initialized.sh
@@ -95,7 +95,7 @@ test_expect_success 'submodule not initialized in new clone' '
git clone . sm3 &&
git -C sm3 diff-tree -p --no-commit-id --submodule=log HEAD >actual &&
cat >expected <<-EOF &&
- Submodule sm1 $smhead1...$smhead2 (not initialized)
+ Submodule sm1 $smhead1...$smhead2 (commits not present)
EOF
test_cmp expected actual
'
diff --git a/t/t4107-apply-ignore-whitespace.sh b/t/t4107-apply-ignore-whitespace.sh
index 9e29b5262d..ac72eeaf27 100755
--- a/t/t4107-apply-ignore-whitespace.sh
+++ b/t/t4107-apply-ignore-whitespace.sh
@@ -178,4 +178,18 @@ test_expect_success 'patch5 fails (--no-ignore-whitespace)' '
test_must_fail git apply --no-ignore-whitespace patch5.patch
'
+test_expect_success 'apply --ignore-space-change --inaccurate-eof' '
+ echo 1 >file &&
+ git apply --ignore-space-change --inaccurate-eof <<-\EOF &&
+ diff --git a/file b/file
+ --- a/file
+ +++ b/file
+ @@ -1 +1 @@
+ -1
+ +2
+ EOF
+ printf 2 >expect &&
+ test_cmp expect file
+'
+
test_done
diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh
index 1a080e7823..d97d2bebc9 100755
--- a/t/t4200-rerere.sh
+++ b/t/t4200-rerere.sh
@@ -239,6 +239,43 @@ test_expect_success 'old records rest in peace' '
! test -f $rr2/preimage
'
+rerere_gc_custom_expiry_test () {
+ five_days="$1" right_now="$2"
+ test_expect_success "rerere gc with custom expiry ($five_days, $right_now)" '
+ rm -fr .git/rr-cache &&
+ rr=.git/rr-cache/$_z40 &&
+ mkdir -p "$rr" &&
+ >"$rr/preimage" &&
+ >"$rr/postimage" &&
+
+ two_days_ago=$((-2*86400)) &&
+ test-chmtime =$two_days_ago "$rr/preimage" &&
+ test-chmtime =$two_days_ago "$rr/postimage" &&
+
+ find .git/rr-cache -type f | sort >original &&
+
+ git -c "gc.rerereresolved=$five_days" \
+ -c "gc.rerereunresolved=$five_days" rerere gc &&
+ find .git/rr-cache -type f | sort >actual &&
+ test_cmp original actual &&
+
+ git -c "gc.rerereresolved=$five_days" \
+ -c "gc.rerereunresolved=$right_now" rerere gc &&
+ find .git/rr-cache -type f | sort >actual &&
+ test_cmp original actual &&
+
+ git -c "gc.rerereresolved=$right_now" \
+ -c "gc.rerereunresolved=$right_now" rerere gc &&
+ find .git/rr-cache -type f | sort >actual &&
+ >expect &&
+ test_cmp expect actual
+ '
+}
+
+rerere_gc_custom_expiry_test 5 0
+
+rerere_gc_custom_expiry_test 5.days.ago now
+
test_expect_success 'setup: file2 added differently in two branches' '
git reset --hard &&
@@ -419,24 +456,6 @@ count_pre_post () {
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 &&
@@ -446,6 +465,8 @@ merge_conflict_resolve () {
}
test_expect_success 'multiple identical conflicts' '
+ rm -fr .git/rr-cache &&
+ mkdir .git/rr-cache &&
git reset --hard &&
test_seq 1 6 >early &&
diff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh
index 9df054bf05..da10478f59 100755
--- a/t/t4201-shortlog.sh
+++ b/t/t4201-shortlog.sh
@@ -9,6 +9,7 @@ test_description='git shortlog
. ./test-lib.sh
test_expect_success 'setup' '
+ test_tick &&
echo 1 >a1 &&
git add a1 &&
tree=$(git write-tree) &&
@@ -59,7 +60,7 @@ fuzz() {
file=$1 &&
sed "
s/$_x40/OBJECT_NAME/g
- s/$_x05/OBJID/g
+ s/$_x35/OBJID/g
s/^ \{6\}[CTa].*/ SUBJECT/g
s/^ \{8\}[^ ].*/ CONTINUATION/g
" <"$file" >"$file.fuzzy" &&
@@ -81,7 +82,7 @@ test_expect_success 'pretty format' '
test_expect_success '--abbrev' '
sed s/SUBJECT/OBJID/ expect.template >expect &&
- git shortlog --format="%h" --abbrev=5 HEAD >log &&
+ git shortlog --format="%h" --abbrev=35 HEAD >log &&
fuzz log >log.predictable &&
test_cmp expect log.predictable
'
diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh
index 18aa1b5889..591f35daaf 100755
--- a/t/t4205-log-pretty-formats.sh
+++ b/t/t4205-log-pretty-formats.sh
@@ -539,25 +539,62 @@ cat >trailers <<EOF
Signed-off-by: A U Thor <author@example.com>
Acked-by: A U Thor <author@example.com>
[ v2 updated patch description ]
-Signed-off-by: A U Thor <author@example.com>
+Signed-off-by: A U Thor
+ <author@example.com>
EOF
-test_expect_success 'pretty format %(trailers) shows trailers' '
+unfold () {
+ perl -0pe 's/\n\s+/ /g'
+}
+
+test_expect_success 'set up trailer tests' '
echo "Some contents" >trailerfile &&
git add trailerfile &&
- git commit -F - <<-EOF &&
+ git commit -F - <<-EOF
trailers: this commit message has trailers
This commit is a test commit with trailers at the end. We parse this
- message and display the trailers using %bT
+ message and display the trailers using %(trailers).
$(cat trailers)
EOF
+'
+
+test_expect_success 'pretty format %(trailers) shows trailers' '
git log --no-walk --pretty="%(trailers)" >actual &&
- cat >expect <<-EOF &&
- $(cat trailers)
+ {
+ cat trailers &&
+ echo
+ } >expect &&
+ test_cmp expect actual
+'
- EOF
+test_expect_success '%(trailers:only) shows only "key: value" trailers' '
+ git log --no-walk --pretty="%(trailers:only)" >actual &&
+ {
+ grep -v patch.description <trailers &&
+ echo
+ } >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success '%(trailers:unfold) unfolds trailers' '
+ git log --no-walk --pretty="%(trailers:unfold)" >actual &&
+ {
+ unfold <trailers &&
+ echo
+ } >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success ':only and :unfold work together' '
+ git log --no-walk --pretty="%(trailers:only,unfold)" >actual &&
+ git log --no-walk --pretty="%(trailers:unfold,only)" >reverse &&
+ test_cmp actual reverse &&
+ {
+ grep -v patch.description <trailers | unfold &&
+ echo
+ } >expect &&
test_cmp expect actual
'
diff --git a/t/t5304-prune.sh b/t/t5304-prune.sh
index 133b5842b1..6694c19a1e 100755
--- a/t/t5304-prune.sh
+++ b/t/t5304-prune.sh
@@ -283,4 +283,41 @@ test_expect_success 'prune: handle alternate object database' '
git -C B prune
'
+test_expect_success 'prune: handle index in multiple worktrees' '
+ git worktree add second-worktree &&
+ echo "new blob for second-worktree" >second-worktree/blob &&
+ git -C second-worktree add blob &&
+ git prune --expire=now &&
+ git -C second-worktree show :blob >actual &&
+ test_cmp second-worktree/blob actual
+'
+
+test_expect_success 'prune: handle HEAD in multiple worktrees' '
+ git worktree add --detach third-worktree &&
+ echo "new blob for third-worktree" >third-worktree/blob &&
+ git -C third-worktree add blob &&
+ git -C third-worktree commit -m "third" &&
+ rm .git/worktrees/third-worktree/index &&
+ test_must_fail git -C third-worktree show :blob &&
+ git prune --expire=now &&
+ git -C third-worktree show HEAD:blob >actual &&
+ test_cmp third-worktree/blob actual
+'
+
+test_expect_success 'prune: handle HEAD reflog in multiple worktrees' '
+ git config core.logAllRefUpdates true &&
+ echo "lost blob for third-worktree" >expected &&
+ (
+ cd third-worktree &&
+ cat ../expected >blob &&
+ git add blob &&
+ git commit -m "second commit in third" &&
+ git reset --hard HEAD^
+ ) &&
+ git prune --expire=now &&
+ SHA1=`git hash-object expected` &&
+ git -C third-worktree show "$SHA1" >actual &&
+ test_cmp expected actual
+'
+
test_done
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index f15f7a3329..59c4b778d3 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -305,7 +305,7 @@ test_expect_success '--rebase with conflicts shows advice' '
test_tick &&
git commit -m "Create conflict" seq.txt &&
test_must_fail git pull --rebase . seq 2>err >out &&
- test_i18ngrep "When you have resolved this problem" out
+ test_i18ngrep "Resolve all conflicts manually" out
'
test_expect_success 'failed --rebase shows advice' '
@@ -319,7 +319,7 @@ test_expect_success 'failed --rebase shows advice' '
git checkout -f -b fails-to-rebase HEAD^ &&
test_commit v2-without-cr file "2" file2-lf &&
test_must_fail git pull --rebase . diverging 2>err >out &&
- test_i18ngrep "When you have resolved this problem" out
+ test_i18ngrep "Resolve all conflicts manually" out
'
test_expect_success '--rebase fails with multiple branches' '
diff --git a/t/t5521-pull-options.sh b/t/t5521-pull-options.sh
index ded8f98dbe..c19d8dbc9d 100755
--- a/t/t5521-pull-options.sh
+++ b/t/t5521-pull-options.sh
@@ -165,4 +165,49 @@ test_expect_success 'git pull --allow-unrelated-histories' '
)
'
+test_expect_success 'git pull does not add a sign-off line' '
+ test_when_finished "rm -fr src dst actual" &&
+ git init src &&
+ test_commit -C src one &&
+ git clone src dst &&
+ test_commit -C src two &&
+ git -C dst pull --no-ff &&
+ git -C dst show -s --pretty="format:%(trailers)" HEAD >actual &&
+ test_must_be_empty actual
+'
+
+test_expect_success 'git pull --no-signoff does not add sign-off line' '
+ test_when_finished "rm -fr src dst actual" &&
+ git init src &&
+ test_commit -C src one &&
+ git clone src dst &&
+ test_commit -C src two &&
+ git -C dst pull --no-signoff --no-ff &&
+ git -C dst show -s --pretty="format:%(trailers)" HEAD >actual &&
+ test_must_be_empty actual
+'
+
+test_expect_success 'git pull --signoff add a sign-off line' '
+ test_when_finished "rm -fr src dst expected actual" &&
+ echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" >expected &&
+ git init src &&
+ test_commit -C src one &&
+ git clone src dst &&
+ test_commit -C src two &&
+ git -C dst pull --signoff --no-ff &&
+ git -C dst show -s --pretty="format:%(trailers)" HEAD >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'git pull --no-signoff flag cancels --signoff flag' '
+ test_when_finished "rm -fr src dst actual" &&
+ git init src &&
+ test_commit -C src one &&
+ git clone src dst &&
+ test_commit -C src two &&
+ git -C dst pull --signoff --no-signoff --no-ff &&
+ git -C dst show -s --pretty="format:%(trailers)" HEAD >actual &&
+ test_must_be_empty actual
+'
+
test_done
diff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh
index 42251f7f3a..a552ad4ead 100755
--- a/t/t5526-fetch-submodules.sh
+++ b/t/t5526-fetch-submodules.sh
@@ -478,7 +478,47 @@ test_expect_success "don't fetch submodule when newly recorded commits are alrea
git fetch >../actual.out 2>../actual.err
) &&
! test -s actual.out &&
- test_i18ncmp expect.err actual.err
+ test_i18ncmp expect.err actual.err &&
+ (
+ cd submodule &&
+ git checkout -q master
+ )
+'
+
+test_expect_success "'fetch.recurseSubmodules=on-demand' works also without .gitmodule entry" '
+ (
+ cd downstream &&
+ git fetch --recurse-submodules
+ ) &&
+ add_upstream_commit &&
+ head1=$(git rev-parse --short HEAD) &&
+ git add submodule &&
+ git rm .gitmodules &&
+ git commit -m "new submodule without .gitmodules" &&
+ printf "" >expect.out &&
+ head2=$(git rev-parse --short HEAD) &&
+ echo "From $pwd/." >expect.err.2 &&
+ echo " $head1..$head2 master -> origin/master" >>expect.err.2 &&
+ head -3 expect.err >>expect.err.2 &&
+ (
+ cd downstream &&
+ rm .gitmodules &&
+ git config fetch.recurseSubmodules on-demand &&
+ # fake submodule configuration to avoid skipping submodule handling
+ git config -f .gitmodules submodule.fake.path fake &&
+ git config -f .gitmodules submodule.fake.url fakeurl &&
+ git add .gitmodules &&
+ git config --unset submodule.submodule.url &&
+ git fetch >../actual.out 2>../actual.err &&
+ # cleanup
+ git config --unset fetch.recurseSubmodules &&
+ git reset --hard
+ ) &&
+ test_i18ncmp expect.out actual.out &&
+ test_i18ncmp expect.err.2 actual.err &&
+ git checkout HEAD^ -- .gitmodules &&
+ git add .gitmodules &&
+ git commit -m "new submodule restored .gitmodules"
'
test_expect_success 'fetching submodules respects parallel settings' '
@@ -530,4 +570,39 @@ test_expect_success 'fetching submodule into a broken repository' '
test_must_fail git -C dst fetch --recurse-submodules
'
+test_expect_success "fetch new commits when submodule got renamed" '
+ git clone . downstream_rename &&
+ (
+ cd downstream_rename &&
+ git submodule update --init &&
+# NEEDSWORK: we omitted --recursive for the submodule update here since
+# that does not work. See test 7001 for mv "moving nested submodules"
+# for details. Once that is fixed we should add the --recursive option
+# here.
+ git checkout -b rename &&
+ git mv submodule submodule_renamed &&
+ (
+ cd submodule_renamed &&
+ git checkout -b rename_sub &&
+ echo a >a &&
+ git add a &&
+ git commit -ma &&
+ git push origin rename_sub &&
+ git rev-parse HEAD >../../expect
+ ) &&
+ git add submodule_renamed &&
+ git commit -m "update renamed submodule" &&
+ git push origin rename
+ ) &&
+ (
+ cd downstream &&
+ git fetch --recurse-submodules=on-demand &&
+ (
+ cd submodule &&
+ git rev-parse origin/rename_sub >../../actual
+ )
+ ) &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t5531-deep-submodule-push.sh b/t/t5531-deep-submodule-push.sh
index 0f84a53146..39cb2c1c34 100755
--- a/t/t5531-deep-submodule-push.sh
+++ b/t/t5531-deep-submodule-push.sh
@@ -298,6 +298,16 @@ test_expect_success 'push succeeds if submodule commit disabling recursion from
)
'
+test_expect_success 'submodule entry pointing at a tag is error' '
+ git -C work/gar/bage tag -a test1 -m "tag" &&
+ tag=$(git -C work/gar/bage rev-parse test1^{tag}) &&
+ git -C work update-index --cacheinfo 160000 "$tag" gar/bage &&
+ git -C work commit -m "bad commit" &&
+ test_when_finished "git -C work reset --hard HEAD^" &&
+ test_must_fail git -C work push --recurse-submodules=on-demand ../pub.git master 2>err &&
+ test_i18ngrep "is a tag, not a commit" err
+'
+
test_expect_success 'push fails if recurse submodules option passed as yes' '
(
cd work/gar/bage &&
diff --git a/t/t5534-push-signed.sh b/t/t5534-push-signed.sh
index 464ffdd147..1cea758f78 100755
--- a/t/t5534-push-signed.sh
+++ b/t/t5534-push-signed.sh
@@ -71,6 +71,13 @@ test_expect_success 'push --signed fails with a receiver without push certificat
test_i18ngrep "the receiving end does not support" err
'
+test_expect_success 'push --signed=1 is accepted' '
+ prepare_dst &&
+ mkdir -p dst/.git/hooks &&
+ test_must_fail git push --signed=1 dst noop ff +noff 2>err &&
+ test_i18ngrep "the receiving end does not support" err
+'
+
test_expect_success GPG 'no certificate for a signed push with no update' '
prepare_dst &&
mkdir -p dst/.git/hooks &&
diff --git a/t/t5545-push-options.sh b/t/t5545-push-options.sh
index 90a4b0d2fe..463783789c 100755
--- a/t/t5545-push-options.sh
+++ b/t/t5545-push-options.sh
@@ -140,6 +140,83 @@ test_expect_success 'push options and submodules' '
test_cmp expect parent_upstream/.git/hooks/post-receive.push_options
'
+test_expect_success 'default push option' '
+ mk_repo_pair &&
+ git -C upstream config receive.advertisePushOptions true &&
+ (
+ cd workbench &&
+ test_commit one &&
+ git push --mirror up &&
+ test_commit two &&
+ git -c push.pushOption=default push up master
+ ) &&
+ test_refs master master &&
+ echo "default" >expect &&
+ test_cmp expect upstream/.git/hooks/pre-receive.push_options &&
+ test_cmp expect upstream/.git/hooks/post-receive.push_options
+'
+
+test_expect_success 'two default push options' '
+ mk_repo_pair &&
+ git -C upstream config receive.advertisePushOptions true &&
+ (
+ cd workbench &&
+ test_commit one &&
+ git push --mirror up &&
+ test_commit two &&
+ git -c push.pushOption=default1 -c push.pushOption=default2 push up master
+ ) &&
+ test_refs master master &&
+ printf "default1\ndefault2\n" >expect &&
+ test_cmp expect upstream/.git/hooks/pre-receive.push_options &&
+ test_cmp expect upstream/.git/hooks/post-receive.push_options
+'
+
+test_expect_success 'push option from command line overrides from-config push option' '
+ mk_repo_pair &&
+ git -C upstream config receive.advertisePushOptions true &&
+ (
+ cd workbench &&
+ test_commit one &&
+ git push --mirror up &&
+ test_commit two &&
+ git -c push.pushOption=default push --push-option=manual up master
+ ) &&
+ test_refs master master &&
+ echo "manual" >expect &&
+ test_cmp expect upstream/.git/hooks/pre-receive.push_options &&
+ test_cmp expect upstream/.git/hooks/post-receive.push_options
+'
+
+test_expect_success 'empty value of push.pushOption in config clears the list' '
+ mk_repo_pair &&
+ git -C upstream config receive.advertisePushOptions true &&
+ (
+ cd workbench &&
+ test_commit one &&
+ git push --mirror up &&
+ test_commit two &&
+ git -c push.pushOption=default1 -c push.pushOption= -c push.pushOption=default2 push up master
+ ) &&
+ test_refs master master &&
+ echo "default2" >expect &&
+ test_cmp expect upstream/.git/hooks/pre-receive.push_options &&
+ test_cmp expect upstream/.git/hooks/post-receive.push_options
+'
+
+test_expect_success 'invalid push option in config' '
+ mk_repo_pair &&
+ git -C upstream config receive.advertisePushOptions true &&
+ (
+ cd workbench &&
+ test_commit one &&
+ git push --mirror up &&
+ test_commit two &&
+ test_must_fail git -c push.pushOption push up master
+ ) &&
+ test_refs master HEAD@{1}
+'
+
. "$TEST_DIRECTORY"/lib-httpd.sh
start_httpd
diff --git a/t/t5572-pull-submodule.sh b/t/t5572-pull-submodule.sh
index 077eb07e11..321bd37deb 100755
--- a/t/t5572-pull-submodule.sh
+++ b/t/t5572-pull-submodule.sh
@@ -65,6 +65,38 @@ test_expect_success 'recursive pull updates working tree' '
test_path_is_file super/sub/merge_strategy.t
'
+test_expect_success "submodule.recurse option triggers recursive pull" '
+ test_commit -C child merge_strategy_2 &&
+ git -C parent submodule update --remote &&
+ git -C parent add sub &&
+ git -C parent commit -m "update submodule" &&
+
+ git -C super -c submodule.recurse pull --no-rebase &&
+ test_path_is_file super/sub/merge_strategy_2.t
+'
+
+test_expect_success " --[no-]recurse-submodule and submodule.recurse" '
+ test_commit -C child merge_strategy_3 &&
+ git -C parent submodule update --remote &&
+ git -C parent add sub &&
+ git -C parent commit -m "update submodule" &&
+
+ git -C super -c submodule.recurse pull --no-recurse-submodules --no-rebase &&
+ test_path_is_missing super/sub/merge_strategy_3.t &&
+ git -C super -c submodule.recurse=false pull --recurse-submodules --no-rebase &&
+ test_path_is_file super/sub/merge_strategy_3.t &&
+
+ test_commit -C child merge_strategy_4 &&
+ git -C parent submodule update --remote &&
+ git -C parent add sub &&
+ git -C parent commit -m "update submodule" &&
+
+ git -C super -c submodule.recurse=false pull --no-recurse-submodules --no-rebase &&
+ test_path_is_missing super/sub/merge_strategy_4.t &&
+ git -C super -c submodule.recurse=true pull --recurse-submodules --no-rebase &&
+ test_path_is_file super/sub/merge_strategy_4.t
+'
+
test_expect_success 'recursive rebasing pull' '
# change upstream
test_commit -C child rebase_strategy &&
diff --git a/t/t5580-clone-push-unc.sh b/t/t5580-clone-push-unc.sh
index b322c2f722..ba548df4a9 100755
--- a/t/t5580-clone-push-unc.sh
+++ b/t/t5580-clone-push-unc.sh
@@ -3,12 +3,18 @@
test_description='various Windows-only path tests'
. ./test-lib.sh
-if ! test_have_prereq MINGW; then
+if test_have_prereq CYGWIN
+then
+ alias winpwd='cygpath -aw .'
+elif test_have_prereq MINGW
+then
+ alias winpwd=pwd
+else
skip_all='skipping Windows-only path tests'
test_done
fi
-UNCPATH="$(pwd)"
+UNCPATH="$(winpwd)"
case "$UNCPATH" in
[A-Z]:*)
# Use administrative share e.g. \\localhost\C$\git-sdk-64\usr\src\git
@@ -45,8 +51,8 @@ test_expect_success push '
test "$rev" = "$(git rev-parse --verify refs/heads/to-push)"
'
-test_expect_success 'remote nick cannot contain backslashes' '
- BACKSLASHED="$(pwd | tr / \\\\)" &&
+test_expect_success MINGW 'remote nick cannot contain backslashes' '
+ BACKSLASHED="$(winpwd | tr / \\\\)" &&
git ls-remote "$BACKSLASHED" >out 2>err &&
test_i18ngrep ! "unable to access" err
'
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index 9c56f771b6..50e40abb11 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -308,6 +308,7 @@ test_expect_success 'clone checking out a tag' '
setup_ssh_wrapper () {
test_expect_success 'setup ssh wrapper' '
+ rm -f "$TRASH_DIRECTORY/ssh-wrapper$X" &&
cp "$GIT_BUILD_DIR/t/helper/test-fake-ssh$X" \
"$TRASH_DIRECTORY/ssh-wrapper$X" &&
GIT_SSH="$TRASH_DIRECTORY/ssh-wrapper$X" &&
@@ -318,6 +319,7 @@ setup_ssh_wrapper () {
}
copy_ssh_wrapper_as () {
+ rm -f "${1%$X}$X" &&
cp "$TRASH_DIRECTORY/ssh-wrapper$X" "${1%$X}$X" &&
GIT_SSH="${1%$X}$X" &&
export GIT_SSH
diff --git a/t/t6007-rev-list-cherry-pick-file.sh b/t/t6007-rev-list-cherry-pick-file.sh
index 2959745196..f0268372d2 100755
--- a/t/t6007-rev-list-cherry-pick-file.sh
+++ b/t/t6007-rev-list-cherry-pick-file.sh
@@ -57,7 +57,7 @@ test_expect_success '--left-right' '
git rev-list --left-right B...C > actual &&
git name-rev --stdin --name-only --refs="*tags/*" \
< actual > actual.named &&
- test_cmp actual.named expect
+ test_cmp expect actual.named
'
test_expect_success '--count' '
@@ -77,14 +77,14 @@ test_expect_success '--cherry-pick bar does not come up empty' '
git rev-list --left-right --cherry-pick B...C -- bar > actual &&
git name-rev --stdin --name-only --refs="*tags/*" \
< actual > actual.named &&
- test_cmp actual.named expect
+ test_cmp expect actual.named
'
test_expect_success 'bar does not come up empty' '
git rev-list --left-right B...C -- bar > actual &&
git name-rev --stdin --name-only --refs="*tags/*" \
< actual > actual.named &&
- test_cmp actual.named expect
+ test_cmp expect actual.named
'
cat >expect <<EOF
@@ -96,14 +96,14 @@ test_expect_success '--cherry-pick bar does not come up empty (II)' '
git rev-list --left-right --cherry-pick F...E -- bar > actual &&
git name-rev --stdin --name-only --refs="*tags/*" \
< actual > actual.named &&
- test_cmp actual.named expect
+ test_cmp expect actual.named
'
test_expect_success 'name-rev multiple --refs combine inclusive' '
git rev-list --left-right --cherry-pick F...E -- bar >actual &&
git name-rev --stdin --name-only --refs="*tags/F" --refs="*tags/E" \
<actual >actual.named &&
- test_cmp actual.named expect
+ test_cmp expect actual.named
'
cat >expect <<EOF
@@ -115,7 +115,7 @@ test_expect_success 'name-rev --refs excludes non-matched patterns' '
git rev-list --left-right --cherry-pick F...E -- bar >actual &&
git name-rev --stdin --name-only --refs="*tags/F" \
<actual >actual.named &&
- test_cmp actual.named expect
+ test_cmp expect actual.named
'
cat >expect <<EOF
@@ -127,14 +127,14 @@ test_expect_success 'name-rev --exclude excludes matched patterns' '
git rev-list --left-right --cherry-pick F...E -- bar >actual &&
git name-rev --stdin --name-only --refs="*tags/*" --exclude="*E" \
<actual >actual.named &&
- test_cmp actual.named expect
+ test_cmp expect actual.named
'
test_expect_success 'name-rev --no-refs clears the refs list' '
git rev-list --left-right --cherry-pick F...E -- bar >expect &&
git name-rev --stdin --name-only --refs="*tags/F" --refs="*tags/E" --no-refs --refs="*tags/G" \
<expect >actual &&
- test_cmp actual expect
+ test_cmp expect actual
'
cat >expect <<EOF
@@ -148,7 +148,7 @@ test_expect_success '--cherry-mark' '
git rev-list --cherry-mark F...E -- bar > actual &&
git name-rev --stdin --name-only --refs="*tags/*" \
< actual > actual.named &&
- test_cmp actual.named expect
+ test_cmp expect actual.named
'
cat >expect <<EOF
@@ -162,7 +162,7 @@ test_expect_success '--cherry-mark --left-right' '
git rev-list --cherry-mark --left-right F...E -- bar > actual &&
git name-rev --stdin --name-only --refs="*tags/*" \
< actual > actual.named &&
- test_cmp actual.named expect
+ test_cmp expect actual.named
'
cat >expect <<EOF
@@ -173,14 +173,14 @@ test_expect_success '--cherry-pick --right-only' '
git rev-list --cherry-pick --right-only F...E -- bar > actual &&
git name-rev --stdin --name-only --refs="*tags/*" \
< actual > actual.named &&
- test_cmp actual.named expect
+ test_cmp expect actual.named
'
test_expect_success '--cherry-pick --left-only' '
git rev-list --cherry-pick --left-only E...F -- bar > actual &&
git name-rev --stdin --name-only --refs="*tags/*" \
< actual > actual.named &&
- test_cmp actual.named expect
+ test_cmp expect actual.named
'
cat >expect <<EOF
@@ -192,7 +192,7 @@ test_expect_success '--cherry' '
git rev-list --cherry F...E -- bar > actual &&
git name-rev --stdin --name-only --refs="*tags/*" \
< actual > actual.named &&
- test_cmp actual.named expect
+ test_cmp expect actual.named
'
cat >expect <<EOF
@@ -201,7 +201,7 @@ EOF
test_expect_success '--cherry --count' '
git rev-list --cherry --count F...E -- bar > actual &&
- test_cmp actual expect
+ test_cmp expect actual
'
cat >expect <<EOF
@@ -210,7 +210,7 @@ EOF
test_expect_success '--cherry-mark --count' '
git rev-list --cherry-mark --count F...E -- bar > actual &&
- test_cmp actual expect
+ test_cmp expect actual
'
cat >expect <<EOF
@@ -219,7 +219,7 @@ EOF
test_expect_success '--cherry-mark --left-right --count' '
git rev-list --cherry-mark --left-right --count F...E -- bar > actual &&
- test_cmp actual expect
+ test_cmp expect actual
'
test_expect_success '--cherry-pick with independent, but identical branches' '
diff --git a/t/t6013-rev-list-reverse-parents.sh b/t/t6013-rev-list-reverse-parents.sh
index 59fc2f06e0..89458d370f 100755
--- a/t/t6013-rev-list-reverse-parents.sh
+++ b/t/t6013-rev-list-reverse-parents.sh
@@ -28,7 +28,7 @@ test_expect_success '--reverse --parents --full-history combines correctly' '
perl -e "print reverse <>" > expected &&
git rev-list --reverse --parents --full-history master -- foo \
> actual &&
- test_cmp actual expected
+ test_cmp expected actual
'
test_expect_success '--boundary does too' '
@@ -36,7 +36,7 @@ test_expect_success '--boundary does too' '
perl -e "print reverse <>" > expected &&
git rev-list --boundary --reverse --parents --full-history \
master ^root -- foo > actual &&
- test_cmp actual expected
+ test_cmp expected actual
'
test_done
diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh
index 8c2c6eaef8..f84ff941c3 100755
--- a/t/t6030-bisect-porcelain.sh
+++ b/t/t6030-bisect-porcelain.sh
@@ -894,4 +894,21 @@ test_expect_success 'bisect start takes options and revs in any order' '
test_cmp expected actual
'
+test_expect_success 'git bisect reset cleans bisection state properly' '
+ git bisect reset &&
+ git bisect start &&
+ git bisect good $HASH1 &&
+ git bisect bad $HASH4 &&
+ git bisect reset &&
+ test -z "$(git for-each-ref "refs/bisect/*")" &&
+ test_path_is_missing "$GIT_DIR/BISECT_EXPECTED_REV" &&
+ test_path_is_missing "$GIT_DIR/BISECT_ANCESTORS_OK" &&
+ test_path_is_missing "$GIT_DIR/BISECT_LOG" &&
+ test_path_is_missing "$GIT_DIR/BISECT_RUN" &&
+ test_path_is_missing "$GIT_DIR/BISECT_TERMS" &&
+ test_path_is_missing "$GIT_DIR/head-name" &&
+ test_path_is_missing "$GIT_DIR/BISECT_HEAD" &&
+ test_path_is_missing "$GIT_DIR/BISECT_START"
+'
+
test_done
diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh
index 97a07655a0..8f17fd9da8 100755
--- a/t/t6040-tracking-info.sh
+++ b/t/t6040-tracking-info.sh
@@ -100,7 +100,7 @@ test_expect_success 'checkout (up-to-date with upstream)' '
(
cd test && git checkout b6
) >actual &&
- test_i18ngrep "Your branch is up-to-date with .origin/master" actual
+ test_i18ngrep "Your branch is up to date with .origin/master" actual
'
test_expect_success 'status (diverged from upstream)' '
@@ -130,7 +130,7 @@ test_expect_success 'status (up-to-date with upstream)' '
# reports nothing to commit
test_must_fail git commit --dry-run
) >actual &&
- test_i18ngrep "Your branch is up-to-date with .origin/master" actual
+ test_i18ngrep "Your branch is up to date with .origin/master" actual
'
cat >expect <<\EOF
@@ -188,35 +188,29 @@ test_expect_success 'fail to track annotated tags' '
test_must_fail git checkout heavytrack
'
-test_expect_success 'setup tracking with branch --set-upstream on existing branch' '
+test_expect_success '--set-upstream-to does not change branch' '
git branch from-master master &&
- test_must_fail git config branch.from-master.merge > actual &&
- git branch --set-upstream from-master master &&
- git config branch.from-master.merge > actual &&
- grep -q "^refs/heads/master$" actual
-'
-
-test_expect_success '--set-upstream does not change branch' '
+ git branch --set-upstream-to master from-master &&
git branch from-master2 master &&
test_must_fail git config branch.from-master2.merge > actual &&
git rev-list from-master2 &&
git update-ref refs/heads/from-master2 from-master2^ &&
git rev-parse from-master2 >expect2 &&
- git branch --set-upstream from-master2 master &&
+ git branch --set-upstream-to master from-master2 &&
git config branch.from-master.merge > actual &&
git rev-parse from-master2 >actual2 &&
grep -q "^refs/heads/master$" actual &&
cmp expect2 actual2
'
-test_expect_success '--set-upstream @{-1}' '
- git checkout from-master &&
+test_expect_success '--set-upstream-to @{-1}' '
+ git checkout follower &&
git checkout from-master2 &&
git config branch.from-master2.merge > expect2 &&
- git branch --set-upstream @{-1} follower &&
+ git branch --set-upstream-to @{-1} from-master &&
git config branch.from-master.merge > actual &&
git config branch.from-master2.merge > actual2 &&
- git branch --set-upstream from-master follower &&
+ git branch --set-upstream-to follower from-master &&
git config branch.from-master.merge > expect &&
test_cmp expect2 actual2 &&
test_cmp expect actual
diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh
index 25110ea55d..1c0e8659d9 100755
--- a/t/t6120-describe.sh
+++ b/t/t6120-describe.sh
@@ -190,6 +190,33 @@ check_describe "test1-lightweight-*" --long --tags --match="test1-*" --match="te
check_describe "test1-lightweight-*" --long --tags --match="test3-*" --match="test1-*" HEAD
+test_expect_success 'set-up branches' '
+ git branch branch_A A &&
+ git branch branch_C c &&
+ git update-ref refs/remotes/origin/remote_branch_A "A^{commit}" &&
+ git update-ref refs/remotes/origin/remote_branch_C "c^{commit}" &&
+ git update-ref refs/original/original_branch_A test-annotated~2
+'
+
+check_describe "heads/branch_A*" --all --match="branch_*" --exclude="branch_C" HEAD
+
+check_describe "remotes/origin/remote_branch_A*" --all --match="origin/remote_branch_*" --exclude="origin/remote_branch_C" HEAD
+
+check_describe "original/original_branch_A*" --all test-annotated~1
+
+test_expect_success '--match does not work for other types' '
+ test_must_fail git describe --all --match="*original_branch_*" test-annotated~1
+'
+
+test_expect_success '--exclude does not work for other types' '
+ R=$(git describe --all --exclude="any_pattern_even_not_matching" test-annotated~1) &&
+ case "$R" in
+ *original_branch_A*) echo "fail: Found unknown reference $R with --exclude"
+ false;;
+ *) echo ok: Found some known type;;
+ esac
+'
+
test_expect_success 'name-rev with exact tags' '
echo A >expect &&
tag_object=$(git rev-parse refs/tags/A) &&
@@ -202,6 +229,31 @@ test_expect_success 'name-rev with exact tags' '
test_cmp expect actual
'
+test_expect_success 'name-rev --all' '
+ >expect.unsorted &&
+ for rev in $(git rev-list --all)
+ do
+ git name-rev $rev >>expect.unsorted
+ done &&
+ sort <expect.unsorted >expect &&
+ git name-rev --all >actual.unsorted &&
+ sort <actual.unsorted >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'name-rev --stdin' '
+ >expect.unsorted &&
+ for rev in $(git rev-list --all)
+ do
+ name=$(git name-rev --name-only $rev) &&
+ echo "$rev ($name)" >>expect.unsorted
+ done &&
+ sort <expect.unsorted >expect &&
+ git rev-list --all | git name-rev --stdin >actual.unsorted &&
+ sort <actual.unsorted >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'describe --contains with the exact tags' '
echo "A^0" >expect &&
tag_object=$(git rev-parse refs/tags/A) &&
@@ -254,7 +306,38 @@ test_expect_success 'describe chokes on severely broken submodules' '
'
test_expect_success 'describe ignoring a borken submodule' '
git describe --broken >out &&
+ test_when_finished "mv .git/modules/sub_moved .git/modules/sub1" &&
grep broken out
'
+test_expect_failure ULIMIT_STACK_SIZE 'name-rev works in a deep repo' '
+ i=1 &&
+ while test $i -lt 8000
+ do
+ echo "commit refs/heads/master
+committer A U Thor <author@example.com> $((1000000000 + $i * 100)) +0200
+data <<EOF
+commit #$i
+EOF"
+ test $i = 1 && echo "from refs/heads/master^0"
+ i=$(($i + 1))
+ done | git fast-import &&
+ git checkout master &&
+ git tag far-far-away HEAD^ &&
+ echo "HEAD~4000 tags/far-far-away~3999" >expect &&
+ git name-rev HEAD~4000 >actual &&
+ test_cmp expect actual &&
+ run_with_limited_stack git name-rev HEAD~4000 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success ULIMIT_STACK_SIZE 'describe works in a deep repo' '
+ git tag -f far-far-away HEAD~7999 &&
+ echo "far-far-away" >expect &&
+ git describe --tags --abbrev=0 HEAD~4000 >actual &&
+ test_cmp expect actual &&
+ run_with_limited_stack git describe --tags --abbrev=0 HEAD~4000 >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index d0ad902911..c128dfc579 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -610,18 +610,104 @@ test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' '
cat >trailers <<EOF
Reviewed-by: A U Thor <author@example.com>
Signed-off-by: A U Thor <author@example.com>
+[ v2 updated patch description ]
+Acked-by: A U Thor
+ <author@example.com>
EOF
-test_expect_success 'basic atom: head contents:trailers' '
+unfold () {
+ perl -0pe 's/\n\s+/ /g'
+}
+
+test_expect_success 'set up trailers for next test' '
echo "Some contents" > two &&
git add two &&
- git commit -F - <<-EOF &&
+ git commit -F - <<-EOF
trailers: this commit message has trailers
Some message contents
$(cat trailers)
EOF
+'
+
+test_expect_success '%(trailers:unfold) unfolds trailers' '
+ git for-each-ref --format="%(trailers:unfold)" refs/heads/master >actual &&
+ {
+ unfold <trailers
+ echo
+ } >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success '%(trailers:only) shows only "key: value" trailers' '
+ git for-each-ref --format="%(trailers:only)" refs/heads/master >actual &&
+ {
+ grep -v patch.description <trailers &&
+ echo
+ } >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success '%(trailers:only) and %(trailers:unfold) work together' '
+ git for-each-ref --format="%(trailers:only,unfold)" refs/heads/master >actual &&
+ git for-each-ref --format="%(trailers:unfold,only)" refs/heads/master >reverse &&
+ test_cmp actual reverse &&
+ {
+ grep -v patch.description <trailers | unfold &&
+ echo
+ } >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success '%(contents:trailers:unfold) unfolds trailers' '
+ git for-each-ref --format="%(contents:trailers:unfold)" refs/heads/master >actual &&
+ {
+ unfold <trailers
+ echo
+ } >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success '%(contents:trailers:only) shows only "key: value" trailers' '
+ git for-each-ref --format="%(contents:trailers:only)" refs/heads/master >actual &&
+ {
+ grep -v patch.description <trailers &&
+ echo
+ } >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success '%(contents:trailers:only) and %(contents:trailers:unfold) work together' '
+ git for-each-ref --format="%(contents:trailers:only,unfold)" refs/heads/master >actual &&
+ git for-each-ref --format="%(contents:trailers:unfold,only)" refs/heads/master >reverse &&
+ test_cmp actual reverse &&
+ {
+ grep -v patch.description <trailers | unfold &&
+ echo
+ } >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success '%(trailers) rejects unknown trailers arguments' '
+ # error message cannot be checked under i18n
+ cat >expect <<-EOF &&
+ fatal: unknown %(trailers) argument: unsupported
+ EOF
+ test_must_fail git for-each-ref --format="%(trailers:unsupported)" 2>actual &&
+ test_i18ncmp expect actual
+'
+
+test_expect_success '%(contents:trailers) rejects unknown trailers arguments' '
+ # error message cannot be checked under i18n
+ cat >expect <<-EOF &&
+ fatal: unknown %(trailers) argument: unsupported
+ EOF
+ test_must_fail git for-each-ref --format="%(contents:trailers:unsupported)" 2>actual &&
+ test_i18ncmp expect actual
+'
+
+test_expect_success 'basic atom: head contents:trailers' '
git for-each-ref --format="%(contents:trailers)" refs/heads/master >actual &&
sanitize_pgp <actual >actual.clean &&
# git for-each-ref ends with a blank line
@@ -680,4 +766,36 @@ test_expect_success 'Verify usage of %(symref:rstrip) atom' '
test_cmp expected actual
'
+test_expect_success ':remotename and :remoteref' '
+ git init remote-tests &&
+ (
+ cd remote-tests &&
+ test_commit initial &&
+ git remote add from fifth.coffee:blub &&
+ git config branch.master.remote from &&
+ git config branch.master.merge refs/heads/stable &&
+ git remote add to southridge.audio:repo &&
+ git config remote.to.push "refs/heads/*:refs/heads/pushed/*" &&
+ git config branch.master.pushRemote to &&
+ for pair in "%(upstream)=refs/remotes/from/stable" \
+ "%(upstream:remotename)=from" \
+ "%(upstream:remoteref)=refs/heads/stable" \
+ "%(push)=refs/remotes/to/pushed/master" \
+ "%(push:remotename)=to" \
+ "%(push:remoteref)=refs/heads/pushed/master"
+ do
+ echo "${pair#*=}" >expect &&
+ git for-each-ref --format="${pair%=*}" \
+ refs/heads/master >actual &&
+ test_cmp expect actual
+ done &&
+ git branch push-simple &&
+ git config branch.push-simple.pushRemote from &&
+ actual="$(git for-each-ref \
+ --format="%(push:remotename),%(push:remoteref)" \
+ refs/heads/push-simple)" &&
+ test from, = "$actual"
+ )
+'
+
test_done
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index e365d1ff77..6e5031f56f 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -452,7 +452,7 @@ test_expect_success 'checking out a commit before submodule moved needs manual u
git mv sub sub2 &&
git commit -m "moved sub to sub2" &&
git checkout -q HEAD^ 2>actual &&
- test_i18ngrep "^warning: unable to rmdir sub2:" actual &&
+ test_i18ngrep "^warning: unable to rmdir '\''sub2'\'':" actual &&
git status -s sub2 >actual &&
echo "?? sub2/" >expected &&
test_cmp expected actual &&
@@ -488,7 +488,32 @@ test_expect_success 'moving a submodule in nested directories' '
git config -f ../.gitmodules submodule.deep/directory/hierarchy/sub.path >../actual &&
echo "directory/hierarchy/sub" >../expect
) &&
- test_cmp actual expect
+ test_cmp expect actual
+'
+
+test_expect_failure 'moving nested submodules' '
+ git commit -am "cleanup commit" &&
+ mkdir sub_nested_nested &&
+ (cd sub_nested_nested &&
+ touch nested_level2 &&
+ git init &&
+ git add . &&
+ git commit -m "nested level 2"
+ ) &&
+ mkdir sub_nested &&
+ (cd sub_nested &&
+ touch nested_level1 &&
+ git init &&
+ git add . &&
+ git commit -m "nested level 1"
+ git submodule add ../sub_nested_nested &&
+ git commit -m "add nested level 2"
+ ) &&
+ git submodule add ./sub_nested nested_move &&
+ git commit -m "add nested_move" &&
+ git submodule update --init --recursive &&
+ git mv nested_move sub_nested_moved &&
+ git status
'
test_done
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index 62aa322846..a9af2de996 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -1863,13 +1863,6 @@ test_expect_success 'version sort with very long prerelease suffix' '
git tag -l --sort=version:refname
'
-run_with_limited_stack () {
- (ulimit -s 128 && "$@")
-}
-
-test_lazy_prereq ULIMIT_STACK_SIZE 'run_with_limited_stack true'
-
-# we require ulimit, this excludes Windows
test_expect_success ULIMIT_STACK_SIZE '--contains and --no-contains work in a deep repo' '
>expect &&
i=1 &&
diff --git a/t/t7005-editor.sh b/t/t7005-editor.sh
index 1b530b5022..29e5043b94 100755
--- a/t/t7005-editor.sh
+++ b/t/t7005-editor.sh
@@ -38,7 +38,7 @@ test_expect_success setup '
test_commit "$msg" &&
echo "$msg" >expect &&
git show -s --format=%s > actual &&
- test_cmp actual expect
+ test_cmp expect actual
'
@@ -85,7 +85,7 @@ do
git --exec-path=. commit --amend &&
git show -s --pretty=oneline |
sed -e "s/^[0-9a-f]* //" >actual &&
- test_cmp actual expect
+ test_cmp expect actual
'
done
@@ -107,7 +107,7 @@ do
git --exec-path=. commit --amend &&
git show -s --pretty=oneline |
sed -e "s/^[0-9a-f]* //" >actual &&
- test_cmp actual expect
+ test_cmp expect actual
'
done
diff --git a/t/t7006-pager.sh b/t/t7006-pager.sh
index f0f1abd1c2..f5f46a95b4 100755
--- a/t/t7006-pager.sh
+++ b/t/t7006-pager.sh
@@ -214,6 +214,44 @@ test_expect_success TTY 'git tag as alias respects pager.tag with -l' '
! test -e paginated.out
'
+test_expect_success TTY 'git branch defaults to paging' '
+ rm -f paginated.out &&
+ test_terminal git branch &&
+ test -e paginated.out
+'
+
+test_expect_success TTY 'git branch respects pager.branch' '
+ rm -f paginated.out &&
+ test_terminal git -c pager.branch=false branch &&
+ ! test -e paginated.out
+'
+
+test_expect_success TTY 'git branch respects --no-pager' '
+ rm -f paginated.out &&
+ test_terminal git --no-pager branch &&
+ ! test -e paginated.out
+'
+
+test_expect_success TTY 'git branch --edit-description ignores pager.branch' '
+ rm -f paginated.out editor.used &&
+ write_script editor <<-\EOF &&
+ echo "New description" >"$1"
+ touch editor.used
+ EOF
+ EDITOR=./editor test_terminal git -c pager.branch branch --edit-description &&
+ ! test -e paginated.out &&
+ test -e editor.used
+'
+
+test_expect_success TTY 'git branch --set-upstream-to ignores pager.branch' '
+ rm -f paginated.out &&
+ git branch other &&
+ test_when_finished "git branch -D other" &&
+ test_terminal git -c pager.branch branch --set-upstream-to=other &&
+ test_when_finished "git branch --unset-upstream" &&
+ ! test -e paginated.out
+'
+
# A colored commit log will begin with an appropriate ANSI escape
# for the first color; the text "commit" comes later.
colorful() {
@@ -570,4 +608,18 @@ test_expect_success 'command with underscores does not complain' '
test_cmp expect actual
'
+test_expect_success TTY 'git tag with auto-columns ' '
+ test_commit one &&
+ test_commit two &&
+ test_commit three &&
+ test_commit four &&
+ test_commit five &&
+ cat >expect <<-\EOF &&
+ initial one two three four five
+ EOF
+ test_terminal env PAGER="cat >actual" COLUMNS=80 \
+ git -c column.ui=auto tag --sort=authordate &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t7061-wtstatus-ignore.sh b/t/t7061-wtstatus-ignore.sh
index fc6013ba3c..0c394cf995 100755
--- a/t/t7061-wtstatus-ignore.sh
+++ b/t/t7061-wtstatus-ignore.sh
@@ -272,4 +272,15 @@ test_expect_success 'status ignored tracked directory with uncommitted file in t
test_cmp expected actual
'
+cat >expected <<\EOF
+!! tracked/submodule/
+EOF
+
+test_expect_success 'status ignores submodule in excluded directory' '
+ git init tracked/submodule &&
+ test_commit -C tracked/submodule initial &&
+ git status --porcelain --ignored -u tracked/submodule >actual &&
+ test_cmp expected actual
+'
+
test_done
diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh
index 86f23be34a..95653a08ca 100755
--- a/t/t7102-reset.sh
+++ b/t/t7102-reset.sh
@@ -428,9 +428,9 @@ test_expect_success 'test --mixed <paths>' '
git reset HEAD -- file1 file2 file3 &&
test_must_fail git diff --quiet &&
git diff > output &&
- test_cmp output expect &&
+ test_cmp expect output &&
git diff --cached > output &&
- test_cmp output cached_expect
+ test_cmp cached_expect output
'
test_expect_success 'test resetting the index at give paths' '
diff --git a/t/t7201-co.sh b/t/t7201-co.sh
index d4b217b0ee..76c223c967 100755
--- a/t/t7201-co.sh
+++ b/t/t7201-co.sh
@@ -187,7 +187,7 @@ test_expect_success 'format of merge conflict from checkout -m' '
d
>>>>>>> local
EOF
- test_cmp two expect
+ test_cmp expect two
'
test_expect_success 'checkout --merge --conflict=diff3 <branch>' '
@@ -213,7 +213,7 @@ test_expect_success 'checkout --merge --conflict=diff3 <branch>' '
d
>>>>>>> local
EOF
- test_cmp two expect
+ test_cmp expect two
'
test_expect_success 'switch to another branch while carrying a deletion' '
diff --git a/t/t7301-clean-interactive.sh b/t/t7301-clean-interactive.sh
index 3ae394e934..1bf9789c8a 100755
--- a/t/t7301-clean-interactive.sh
+++ b/t/t7301-clean-interactive.sh
@@ -3,6 +3,7 @@
test_description='git clean -i basic tests'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
test_expect_success 'setup' '
@@ -472,4 +473,14 @@ test_expect_success 'git clean -id with prefix and path (ask)' '
'
+test_expect_success TTY 'git clean -i paints the header in HEADER color' '
+ >a.out &&
+ echo q |
+ test_terminal git clean -i |
+ test_decode_color |
+ head -n 1 >header &&
+ # not i18ngrep
+ grep "^<BOLD>" header
+'
+
test_done
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index e9c3335b78..a39e69a3eb 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -46,16 +46,6 @@ test_expect_success 'submodule update aborts on missing gitmodules url' '
test_must_fail git submodule init
'
-test_expect_success 'configuration parsing' '
- test_when_finished "rm -f .gitmodules" &&
- cat >.gitmodules <<-\EOF &&
- [submodule "s"]
- path
- ignore
- EOF
- test_must_fail git status
-'
-
test_expect_success 'setup - repository in init subdirectory' '
mkdir init &&
(
@@ -1221,7 +1211,7 @@ test_expect_success 'clone --recurse-submodules with a pathspec works' '
git clone --recurse-submodules="sub0" multisuper multisuper_clone &&
git -C multisuper_clone submodule status |cut -c1,43- >actual &&
- test_cmp actual expected
+ test_cmp expected actual
'
test_expect_success 'clone with multiple --recurse-submodules options' '
diff --git a/t/t7405-submodule-merge.sh b/t/t7405-submodule-merge.sh
index 0d5b42a25b..7bfb2f498d 100755
--- a/t/t7405-submodule-merge.sh
+++ b/t/t7405-submodule-merge.sh
@@ -119,7 +119,7 @@ test_expect_success 'merge with one side as a fast-forward of the other' '
git ls-tree test-forward sub | cut -f1 | cut -f3 -d" " > actual &&
(cd sub &&
git rev-parse sub-d > ../expect) &&
- test_cmp actual expect)
+ test_cmp expect actual)
'
test_expect_success 'merging should conflict for non fast-forward' '
diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
index 034914a14f..6f083c4d68 100755
--- a/t/t7406-submodule-update.sh
+++ b/t/t7406-submodule-update.sh
@@ -406,6 +406,14 @@ test_expect_success 'submodule update - command in .git/config' '
)
'
+test_expect_success 'submodule update - command in .gitmodules is ignored' '
+ test_when_finished "git -C super reset --hard HEAD^" &&
+ git -C super config -f .gitmodules submodule.submodule.update "!false" &&
+ git -C super commit -a -m "add command to .gitmodules file" &&
+ git -C super/submodule reset --hard $submodulesha1^ &&
+ git -C super submodule update submodule
+'
+
cat << EOF >expect
Execution of 'false $submodulesha1' failed in submodule path 'submodule'
EOF
diff --git a/t/t7411-submodule-config.sh b/t/t7411-submodule-config.sh
index eea36f1dbe..46c09c7765 100755
--- a/t/t7411-submodule-config.sh
+++ b/t/t7411-submodule-config.sh
@@ -31,6 +31,21 @@ test_expect_success 'submodule config cache setup' '
)
'
+test_expect_success 'configuration parsing with error' '
+ test_when_finished "rm -rf repo" &&
+ test_create_repo repo &&
+ cat >repo/.gitmodules <<-\EOF &&
+ [submodule "s"]
+ path
+ ignore
+ EOF
+ (
+ cd repo &&
+ test_must_fail test-submodule-config "" s 2>actual &&
+ test_i18ngrep "bad config" actual
+ )
+'
+
cat >super/expect <<EOF
Submodule name: 'a' for path 'a'
Submodule name: 'a' for path 'b'
@@ -107,78 +122,6 @@ test_expect_success 'using different treeishs works' '
)
'
-cat >super/expect_url <<EOF
-Submodule url: 'git@somewhere.else.net:a.git' for path 'b'
-Submodule url: 'git@somewhere.else.net:submodule.git' for path 'submodule'
-EOF
-
-cat >super/expect_local_path <<EOF
-Submodule name: 'a' for path 'c'
-Submodule name: 'submodule' for path 'submodule'
-EOF
-
-test_expect_success 'reading of local configuration' '
- (cd super &&
- old_a=$(git config submodule.a.url) &&
- old_submodule=$(git config submodule.submodule.url) &&
- git config submodule.a.url git@somewhere.else.net:a.git &&
- git config submodule.submodule.url git@somewhere.else.net:submodule.git &&
- test-submodule-config --url \
- "" b \
- "" submodule \
- >actual &&
- test_cmp expect_url actual &&
- git config submodule.a.path c &&
- test-submodule-config \
- "" c \
- "" submodule \
- >actual &&
- test_cmp expect_local_path actual &&
- git config submodule.a.url "$old_a" &&
- git config submodule.submodule.url "$old_submodule" &&
- git config --unset submodule.a.path c
- )
-'
-
-cat >super/expect_url <<EOF
-Submodule url: '../submodule' for path 'b'
-Submodule url: 'git@somewhere.else.net:submodule.git' for path 'submodule'
-EOF
-
-test_expect_success 'reading of local configuration for uninitialized submodules' '
- (
- cd super &&
- git submodule deinit -f b &&
- old_submodule=$(git config submodule.submodule.url) &&
- git config submodule.submodule.url git@somewhere.else.net:submodule.git &&
- test-submodule-config --url \
- "" b \
- "" submodule \
- >actual &&
- test_cmp expect_url actual &&
- git config submodule.submodule.url "$old_submodule" &&
- git submodule init b
- )
-'
-
-cat >super/expect_fetchrecurse_die.err <<EOF
-fatal: bad submodule.submodule.fetchrecursesubmodules argument: blabla
-EOF
-
-test_expect_success 'local error in fetchrecursesubmodule dies early' '
- (cd super &&
- git config submodule.submodule.fetchrecursesubmodules blabla &&
- test_must_fail test-submodule-config \
- "" b \
- "" submodule \
- >actual.out 2>actual.err &&
- touch expect_fetchrecurse_die.out &&
- test_cmp expect_fetchrecurse_die.out actual.out &&
- test_cmp expect_fetchrecurse_die.err actual.err &&
- git config --unset submodule.submodule.fetchrecursesubmodules
- )
-'
-
test_expect_success 'error in history in fetchrecursesubmodule lets continue' '
(cd super &&
git config -f .gitmodules \
diff --git a/t/t7504-commit-msg-hook.sh b/t/t7504-commit-msg-hook.sh
index 88d4cda299..302a3a2082 100755
--- a/t/t7504-commit-msg-hook.sh
+++ b/t/t7504-commit-msg-hook.sh
@@ -101,6 +101,10 @@ cat > "$HOOK" <<EOF
exit 1
EOF
+commit_msg_is () {
+ test "$(git log --pretty=format:%s%b -1)" = "$1"
+}
+
test_expect_success 'with failing hook' '
echo "another" >> file &&
@@ -135,6 +139,32 @@ test_expect_success '--no-verify with failing hook (editor)' '
'
+test_expect_success 'merge fails with failing hook' '
+
+ test_when_finished "git branch -D newbranch" &&
+ test_when_finished "git checkout -f master" &&
+ git checkout --orphan newbranch &&
+ : >file2 &&
+ git add file2 &&
+ git commit --no-verify file2 -m in-side-branch &&
+ test_must_fail git merge --allow-unrelated-histories master &&
+ commit_msg_is "in-side-branch" # HEAD before merge
+
+'
+
+test_expect_success 'merge bypasses failing hook with --no-verify' '
+
+ test_when_finished "git branch -D newbranch" &&
+ test_when_finished "git checkout -f master" &&
+ git checkout --orphan newbranch &&
+ : >file2 &&
+ git add file2 &&
+ git commit --no-verify file2 -m in-side-branch &&
+ git merge --no-verify --allow-unrelated-histories master &&
+ commit_msg_is "Merge branch '\''master'\'' into newbranch"
+'
+
+
chmod -x "$HOOK"
test_expect_success POSIXPERM 'with non-executable hook' '
@@ -178,10 +208,6 @@ exit 0
EOF
chmod +x "$HOOK"
-commit_msg_is () {
- test "$(git log --pretty=format:%s%b -1)" = "$1"
-}
-
test_expect_success 'hook edits commit message' '
echo "additional" >> file &&
@@ -217,7 +243,36 @@ test_expect_success "hook doesn't edit commit message (editor)" '
echo "more plus" > FAKE_MSG &&
GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify &&
commit_msg_is "more plus"
+'
+test_expect_success 'hook called in git-merge picks up commit message' '
+ test_when_finished "git branch -D newbranch" &&
+ test_when_finished "git checkout -f master" &&
+ git checkout --orphan newbranch &&
+ : >file2 &&
+ git add file2 &&
+ git commit --no-verify file2 -m in-side-branch &&
+ git merge --allow-unrelated-histories master &&
+ commit_msg_is "new message"
+'
+
+test_expect_failure 'merge --continue remembers --no-verify' '
+ test_when_finished "git branch -D newbranch" &&
+ test_when_finished "git checkout -f master" &&
+ git checkout master &&
+ echo a >file2 &&
+ git add file2 &&
+ git commit --no-verify -m "add file2 to master" &&
+ git checkout -b newbranch master^ &&
+ echo b >file2 &&
+ git add file2 &&
+ git commit --no-verify file2 -m in-side-branch &&
+ git merge --no-verify -m not-rewritten-by-hook master &&
+ # resolve conflict:
+ echo c >file2 &&
+ git add file2 &&
+ git merge --continue &&
+ commit_msg_is not-rewritten-by-hook
'
# set up fake editor to replace `pick` by `reword`
@@ -237,4 +292,5 @@ test_expect_success 'hook is called for reword during `rebase -i`' '
'
+
test_done
diff --git a/t/t7506-status-submodule.sh b/t/t7506-status-submodule.sh
index 055c90736e..9edf6572ed 100755
--- a/t/t7506-status-submodule.sh
+++ b/t/t7506-status-submodule.sh
@@ -306,7 +306,7 @@ test_expect_success 'diff with merge conflict in .gitmodules' '
cd super &&
git diff >../diff_actual 2>&1
) &&
- test_cmp diff_actual diff_expect
+ test_cmp diff_expect diff_actual
'
test_expect_success 'diff --submodule with merge conflict in .gitmodules' '
@@ -314,7 +314,7 @@ test_expect_success 'diff --submodule with merge conflict in .gitmodules' '
cd super &&
git diff --submodule >../diff_submodule_actual 2>&1
) &&
- test_cmp diff_submodule_actual diff_submodule_expect
+ test_cmp diff_submodule_expect diff_submodule_actual
'
# We'll setup different cases for further testing:
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index a3d760e63a..50052e2872 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -1671,4 +1671,14 @@ test_expect_success '"Initial commit" should not be noted in commit template' '
test_i18ngrep ! "Initial commit" output
'
+test_expect_success '--no-optional-locks prevents index update' '
+ test-chmtime =1234567890 .git/index &&
+ git --no-optional-locks status &&
+ test-chmtime -v +0 .git/index >out &&
+ grep ^1234567890 out &&
+ git status &&
+ test-chmtime -v +0 .git/index >out &&
+ ! grep ^1234567890 out
+'
+
test_done
diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh
index 0c6f91c433..164719d1c9 100755
--- a/t/t7513-interpret-trailers.sh
+++ b/t/t7513-interpret-trailers.sh
@@ -681,6 +681,36 @@ test_expect_success 'using "where = before"' '
test_cmp expected actual
'
+test_expect_success 'overriding configuration with "--where after"' '
+ git config trailer.ack.where "before" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Peff
+ Reviewed-by: Z
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --where after --trailer "ack: Peff" \
+ complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "where = before" with "--no-where"' '
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Peff
+ Acked-by= Z
+ Reviewed-by: Z
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --where after --no-where --trailer "ack: Peff" \
+ --trailer "bug: 42" complex_message >actual &&
+ test_cmp expected actual
+'
+
test_expect_success 'using "where = after"' '
git config trailer.ack.where "after" &&
cat complex_message_body >expected &&
@@ -947,6 +977,23 @@ test_expect_success 'using "ifExists = add" with "where = after"' '
test_cmp expected actual
'
+test_expect_success 'overriding configuration with "--if-exists replace"' '
+ git config trailer.fix.key "Fixes: " &&
+ git config trailer.fix.ifExists "add" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Acked-by= Z
+ Reviewed-by:
+ Signed-off-by: Z
+ Fixes: 22
+ EOF
+ git interpret-trailers --if-exists replace --trailer "review:" \
+ --trailer "fix=53" --trailer "fix=22" --trailer "bug: 42" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
test_expect_success 'using "ifExists = replace"' '
git config trailer.fix.key "Fixes: " &&
git config trailer.fix.ifExists "replace" &&
@@ -1026,6 +1073,25 @@ test_expect_success 'the default is "ifMissing = add"' '
test_cmp expected actual
'
+test_expect_success 'overriding configuration with "--if-missing doNothing"' '
+ git config trailer.ifmissing "add" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Junio
+ Acked-by= Peff
+ Reviewed-by:
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --if-missing doNothing \
+ --trailer "review:" --trailer "fix=53" \
+ --trailer "cc=Linus" --trailer "ack: Junio" \
+ --trailer "fix=22" --trailer "bug: 42" --trailer "ack: Peff" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
test_expect_success 'when default "ifMissing" is "doNothing"' '
git config trailer.ifmissing "doNothing" &&
cat complex_message_body >expected &&
@@ -1275,4 +1341,80 @@ test_expect_success 'with cut line' '
test_cmp expected actual
'
+test_expect_success 'only trailers' '
+ git config trailer.sign.command "echo config-value" &&
+ cat >expected <<-\EOF &&
+ existing: existing-value
+ sign: config-value
+ added: added-value
+ EOF
+ git interpret-trailers \
+ --trailer added:added-value \
+ --only-trailers >actual <<-\EOF &&
+ my subject
+
+ my body
+
+ existing: existing-value
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'only-trailers omits non-trailer in middle of block' '
+ git config trailer.sign.command "echo config-value" &&
+ cat >expected <<-\EOF &&
+ Signed-off-by: nobody <nobody@nowhere>
+ Signed-off-by: somebody <somebody@somewhere>
+ sign: config-value
+ EOF
+ git interpret-trailers --only-trailers >actual <<-\EOF &&
+ subject
+
+ it is important that the trailers below are signed-off-by
+ so that they meet the "25% trailers Git knows about" heuristic
+
+ Signed-off-by: nobody <nobody@nowhere>
+ this is not a trailer
+ Signed-off-by: somebody <somebody@somewhere>
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'only input' '
+ git config trailer.sign.command "echo config-value" &&
+ cat >expected <<-\EOF &&
+ existing: existing-value
+ EOF
+ git interpret-trailers \
+ --only-trailers --only-input >actual <<-\EOF &&
+ my subject
+
+ my body
+
+ existing: existing-value
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'unfold' '
+ cat >expected <<-\EOF &&
+ foo: continued across several lines
+ EOF
+ # pass through tr to make leading and trailing whitespace more obvious
+ tr _ " " <<-\EOF |
+ my subject
+
+ my body
+
+ foo:_
+ __continued
+ ___across
+ ____several
+ _____lines
+ ___
+ EOF
+ git interpret-trailers --only-trailers --only-input --unfold >actual &&
+ test_cmp expected actual
+'
+
test_done
diff --git a/t/t7519-status-fsmonitor.sh b/t/t7519-status-fsmonitor.sh
new file mode 100755
index 0000000000..eb2d13bbcf
--- /dev/null
+++ b/t/t7519-status-fsmonitor.sh
@@ -0,0 +1,317 @@
+#!/bin/sh
+
+test_description='git status with file system watcher'
+
+. ./test-lib.sh
+
+#
+# To run the entire git test suite using fsmonitor:
+#
+# copy t/t7519/fsmonitor-all to a location in your path and then set
+# GIT_FSMONITOR_TEST=fsmonitor-all and run your tests.
+#
+
+# Note, after "git reset --hard HEAD" no extensions exist other than 'TREE'
+# "git update-index --fsmonitor" can be used to get the extension written
+# before testing the results.
+
+clean_repo () {
+ git reset --hard HEAD &&
+ git clean -fd
+}
+
+dirty_repo () {
+ : >untracked &&
+ : >dir1/untracked &&
+ : >dir2/untracked &&
+ echo 1 >modified &&
+ echo 2 >dir1/modified &&
+ echo 3 >dir2/modified &&
+ echo 4 >new &&
+ echo 5 >dir1/new &&
+ echo 6 >dir2/new
+}
+
+write_integration_script () {
+ write_script .git/hooks/fsmonitor-test<<-\EOF
+ if test "$#" -ne 2
+ then
+ echo "$0: exactly 2 arguments expected"
+ exit 2
+ fi
+ if test "$1" != 1
+ then
+ echo "Unsupported core.fsmonitor hook version." >&2
+ exit 1
+ fi
+ printf "untracked\0"
+ printf "dir1/untracked\0"
+ printf "dir2/untracked\0"
+ printf "modified\0"
+ printf "dir1/modified\0"
+ printf "dir2/modified\0"
+ printf "new\0"
+ printf "dir1/new\0"
+ printf "dir2/new\0"
+ EOF
+}
+
+test_lazy_prereq UNTRACKED_CACHE '
+ { git update-index --test-untracked-cache; ret=$?; } &&
+ test $ret -ne 1
+'
+
+test_expect_success 'setup' '
+ mkdir -p .git/hooks &&
+ : >tracked &&
+ : >modified &&
+ mkdir dir1 &&
+ : >dir1/tracked &&
+ : >dir1/modified &&
+ mkdir dir2 &&
+ : >dir2/tracked &&
+ : >dir2/modified &&
+ git -c core.fsmonitor= add . &&
+ git -c core.fsmonitor= commit -m initial &&
+ git config core.fsmonitor .git/hooks/fsmonitor-test &&
+ cat >.gitignore <<-\EOF
+ .gitignore
+ expect*
+ actual*
+ marker*
+ EOF
+'
+
+# test that the fsmonitor extension is off by default
+test_expect_success 'fsmonitor extension is off by default' '
+ test-dump-fsmonitor >actual &&
+ grep "^no fsmonitor" actual
+'
+
+# test that "update-index --fsmonitor" adds the fsmonitor extension
+test_expect_success 'update-index --fsmonitor" adds the fsmonitor extension' '
+ git update-index --fsmonitor &&
+ test-dump-fsmonitor >actual &&
+ grep "^fsmonitor last update" actual
+'
+
+# test that "update-index --no-fsmonitor" removes the fsmonitor extension
+test_expect_success 'update-index --no-fsmonitor" removes the fsmonitor extension' '
+ git update-index --no-fsmonitor &&
+ test-dump-fsmonitor >actual &&
+ grep "^no fsmonitor" actual
+'
+
+cat >expect <<EOF &&
+h dir1/modified
+H dir1/tracked
+h dir2/modified
+H dir2/tracked
+h modified
+H tracked
+EOF
+
+# test that "update-index --fsmonitor-valid" sets the fsmonitor valid bit
+test_expect_success 'update-index --fsmonitor-valid" sets the fsmonitor valid bit' '
+ git update-index --fsmonitor &&
+ git update-index --fsmonitor-valid dir1/modified &&
+ git update-index --fsmonitor-valid dir2/modified &&
+ git update-index --fsmonitor-valid modified &&
+ git ls-files -f >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<EOF &&
+H dir1/modified
+H dir1/tracked
+H dir2/modified
+H dir2/tracked
+H modified
+H tracked
+EOF
+
+# test that "update-index --no-fsmonitor-valid" clears the fsmonitor valid bit
+test_expect_success 'update-index --no-fsmonitor-valid" clears the fsmonitor valid bit' '
+ git update-index --no-fsmonitor-valid dir1/modified &&
+ git update-index --no-fsmonitor-valid dir2/modified &&
+ git update-index --no-fsmonitor-valid modified &&
+ git ls-files -f >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<EOF &&
+H dir1/modified
+H dir1/tracked
+H dir2/modified
+H dir2/tracked
+H modified
+H tracked
+EOF
+
+# test that all files returned by the script get flagged as invalid
+test_expect_success 'all files returned by integration script get flagged as invalid' '
+ write_integration_script &&
+ dirty_repo &&
+ git update-index --fsmonitor &&
+ git ls-files -f >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<EOF &&
+H dir1/modified
+h dir1/new
+H dir1/tracked
+H dir2/modified
+h dir2/new
+H dir2/tracked
+H modified
+h new
+H tracked
+EOF
+
+# test that newly added files are marked valid
+test_expect_success 'newly added files are marked valid' '
+ git add new &&
+ git add dir1/new &&
+ git add dir2/new &&
+ git ls-files -f >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<EOF &&
+H dir1/modified
+h dir1/new
+h dir1/tracked
+H dir2/modified
+h dir2/new
+h dir2/tracked
+H modified
+h new
+h tracked
+EOF
+
+# test that all unmodified files get marked valid
+test_expect_success 'all unmodified files get marked valid' '
+ # modified files result in update-index returning 1
+ test_must_fail git update-index --refresh --force-write-index &&
+ git ls-files -f >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<EOF &&
+H dir1/modified
+h dir1/tracked
+h dir2/modified
+h dir2/tracked
+h modified
+h tracked
+EOF
+
+# test that *only* files returned by the integration script get flagged as invalid
+test_expect_success '*only* files returned by the integration script get flagged as invalid' '
+ write_script .git/hooks/fsmonitor-test<<-\EOF &&
+ printf "dir1/modified\0"
+ EOF
+ clean_repo &&
+ git update-index --refresh --force-write-index &&
+ echo 1 >modified &&
+ echo 2 >dir1/modified &&
+ echo 3 >dir2/modified &&
+ test_must_fail git update-index --refresh --force-write-index &&
+ git ls-files -f >actual &&
+ test_cmp expect actual
+'
+
+# Ensure commands that call refresh_index() to move the index back in time
+# properly invalidate the fsmonitor cache
+test_expect_success 'refresh_index() invalidates fsmonitor cache' '
+ write_script .git/hooks/fsmonitor-test<<-\EOF &&
+ EOF
+ clean_repo &&
+ dirty_repo &&
+ git add . &&
+ git commit -m "to reset" &&
+ git reset HEAD~1 &&
+ git status >actual &&
+ git -c core.fsmonitor= status >expect &&
+ test_i18ncmp expect actual
+'
+
+# test fsmonitor with and without preloadIndex
+preload_values="false true"
+for preload_val in $preload_values
+do
+ test_expect_success "setup preloadIndex to $preload_val" '
+ git config core.preloadIndex $preload_val &&
+ if test $preload_val = true
+ then
+ GIT_FORCE_PRELOAD_TEST=$preload_val; export GIT_FORCE_PRELOAD_TEST
+ else
+ unset GIT_FORCE_PRELOAD_TEST
+ fi
+ '
+
+ # test fsmonitor with and without the untracked cache (if available)
+ uc_values="false"
+ test_have_prereq UNTRACKED_CACHE && uc_values="false true"
+ for uc_val in $uc_values
+ do
+ test_expect_success "setup untracked cache to $uc_val" '
+ git config core.untrackedcache $uc_val
+ '
+
+ # Status is well tested elsewhere so we'll just ensure that the results are
+ # the same when using core.fsmonitor.
+ test_expect_success 'compare status with and without fsmonitor' '
+ write_integration_script &&
+ clean_repo &&
+ dirty_repo &&
+ git add new &&
+ git add dir1/new &&
+ git add dir2/new &&
+ git status >actual &&
+ git -c core.fsmonitor= status >expect &&
+ test_i18ncmp expect actual
+ '
+
+ # Make sure it's actually skipping the check for modified and untracked
+ # (if enabled) files unless it is told about them.
+ test_expect_success "status doesn't detect unreported modifications" '
+ write_script .git/hooks/fsmonitor-test<<-\EOF &&
+ :>marker
+ EOF
+ clean_repo &&
+ git status &&
+ test_path_is_file marker &&
+ dirty_repo &&
+ rm -f marker &&
+ git status >actual &&
+ test_path_is_file marker &&
+ test_i18ngrep ! "Changes not staged for commit:" actual &&
+ if test $uc_val = true
+ then
+ test_i18ngrep ! "Untracked files:" actual
+ fi &&
+ if test $uc_val = false
+ then
+ test_i18ngrep "Untracked files:" actual
+ fi &&
+ rm -f marker
+ '
+ done
+done
+
+# test that splitting the index dosn't interfere
+test_expect_success 'splitting the index results in the same state' '
+ write_integration_script &&
+ dirty_repo &&
+ git update-index --fsmonitor &&
+ git ls-files -f >expect &&
+ test-dump-fsmonitor >&2 && echo &&
+ git update-index --fsmonitor --split-index &&
+ test-dump-fsmonitor >&2 && echo &&
+ git ls-files -f >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t7519/fsmonitor-all b/t/t7519/fsmonitor-all
new file mode 100755
index 0000000000..691bc94dc2
--- /dev/null
+++ b/t/t7519/fsmonitor-all
@@ -0,0 +1,24 @@
+#!/bin/sh
+#
+# An test hook script to integrate with git to test fsmonitor.
+#
+# The hook is passed a version (currently 1) and a time in nanoseconds
+# formatted as a string and outputs to stdout all files that have been
+# modified since the given time. Paths must be relative to the root of
+# the working tree and separated by a single NUL.
+#
+#echo "$0 $*" >&2
+
+if test "$#" -ne 2
+then
+ echo "$0: exactly 2 arguments expected" >&2
+ exit 2
+fi
+
+if test "$1" != 1
+then
+ echo "Unsupported core.fsmonitor hook version." >&2
+ exit 1
+fi
+
+echo "/"
diff --git a/t/t7519/fsmonitor-none b/t/t7519/fsmonitor-none
new file mode 100755
index 0000000000..ed9cf5a6a9
--- /dev/null
+++ b/t/t7519/fsmonitor-none
@@ -0,0 +1,22 @@
+#!/bin/sh
+#
+# An test hook script to integrate with git to test fsmonitor.
+#
+# The hook is passed a version (currently 1) and a time in nanoseconds
+# formatted as a string and outputs to stdout all files that have been
+# modified since the given time. Paths must be relative to the root of
+# the working tree and separated by a single NUL.
+#
+#echo "$0 $*" >&2
+
+if test "$#" -ne 2
+then
+ echo "$0: exactly 2 arguments expected" >&2
+ exit 2
+fi
+
+if test "$1" != 1
+then
+ echo "Unsupported core.fsmonitor hook version." >&2
+ exit 1
+fi
diff --git a/t/t7519/fsmonitor-watchman b/t/t7519/fsmonitor-watchman
new file mode 100755
index 0000000000..5514edcf68
--- /dev/null
+++ b/t/t7519/fsmonitor-watchman
@@ -0,0 +1,133 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use IPC::Open2;
+
+# An example hook script to integrate Watchman
+# (https://facebook.github.io/watchman/) with git to speed up detecting
+# new and modified files.
+#
+# The hook is passed a version (currently 1) and a time in nanoseconds
+# formatted as a string and outputs to stdout all files that have been
+# modified since the given time. Paths must be relative to the root of
+# the working tree and separated by a single NUL.
+#
+# To enable this hook, rename this file to "query-watchman" and set
+# 'git config core.fsmonitor .git/hooks/query-watchman'
+#
+my ($version, $time) = @ARGV;
+#print STDERR "$0 $version $time\n";
+
+# Check the hook interface version
+
+if ($version == 1) {
+ # convert nanoseconds to seconds
+ $time = int $time / 1000000000;
+} else {
+ die "Unsupported query-fsmonitor hook version '$version'.\n" .
+ "Falling back to scanning...\n";
+}
+
+my $git_work_tree;
+if ($^O =~ 'msys' || $^O =~ 'cygwin') {
+ $git_work_tree = Win32::GetCwd();
+ $git_work_tree =~ tr/\\/\//;
+} else {
+ require Cwd;
+ $git_work_tree = Cwd::cwd();
+}
+
+my $retry = 1;
+
+launch_watchman();
+
+sub launch_watchman {
+
+ my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'watchman -j')
+ or die "open2() failed: $!\n" .
+ "Falling back to scanning...\n";
+
+ # In the query expression below we're asking for names of files that
+ # changed since $time but were not transient (ie created after
+ # $time but no longer exist).
+ #
+ # To accomplish this, we're using the "since" generator to use the
+ # recency index to select candidate nodes and "fields" to limit the
+ # output to file names only. Then we're using the "expression" term to
+ # further constrain the results.
+ #
+ # The category of transient files that we want to ignore will have a
+ # creation clock (cclock) newer than $time_t value and will also not
+ # currently exist.
+
+ my $query = <<" END";
+ ["query", "$git_work_tree", {
+ "since": $time,
+ "fields": ["name"],
+ "expression": ["not", ["allof", ["since", $time, "cclock"], ["not", "exists"]]]
+ }]
+ END
+
+ open (my $fh, ">", ".git/watchman-query.json");
+ print $fh $query;
+ close $fh;
+
+ print CHLD_IN $query;
+ close CHLD_IN;
+ my $response = do {local $/; <CHLD_OUT>};
+
+ open ($fh, ">", ".git/watchman-response.json");
+ print $fh $response;
+ close $fh;
+
+ die "Watchman: command returned no output.\n" .
+ "Falling back to scanning...\n" if $response eq "";
+ die "Watchman: command returned invalid output: $response\n" .
+ "Falling back to scanning...\n" unless $response =~ /^\{/;
+
+ my $json_pkg;
+ eval {
+ require JSON::XS;
+ $json_pkg = "JSON::XS";
+ 1;
+ } or do {
+ require JSON::PP;
+ $json_pkg = "JSON::PP";
+ };
+
+ my $o = $json_pkg->new->utf8->decode($response);
+
+ if ($retry > 0 and $o->{error} and $o->{error} =~ m/unable to resolve root .* directory (.*) is not watched/) {
+ print STDERR "Adding '$git_work_tree' to watchman's watch list.\n";
+ $retry--;
+ qx/watchman watch "$git_work_tree"/;
+ die "Failed to make watchman watch '$git_work_tree'.\n" .
+ "Falling back to scanning...\n" if $? != 0;
+
+ # Watchman will always return all files on the first query so
+ # return the fast "everything is dirty" flag to git and do the
+ # Watchman query just to get it over with now so we won't pay
+ # the cost in git to look up each individual file.
+
+ open ($fh, ">", ".git/watchman-output.out");
+ print "/\0";
+ close $fh;
+
+ print "/\0";
+ eval { launch_watchman() };
+ exit 0;
+ }
+
+ die "Watchman: $o->{error}.\n" .
+ "Falling back to scanning...\n" if $o->{error};
+
+ open ($fh, ">", ".git/watchman-output.out");
+ binmode $fh, ":utf8";
+ print $fh @{$o->{files}};
+ close $fh;
+
+ binmode STDOUT, ":utf8";
+ local $, = "\0";
+ print @{$o->{files}};
+}
diff --git a/t/t7520-ignored-hook-warning.sh b/t/t7520-ignored-hook-warning.sh
new file mode 100755
index 0000000000..634fb7f23a
--- /dev/null
+++ b/t/t7520-ignored-hook-warning.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+test_description='ignored hook warning'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ hookdir="$(git rev-parse --git-dir)/hooks" &&
+ hook="$hookdir/pre-commit" &&
+ mkdir -p "$hookdir" &&
+ write_script "$hook" <<-\EOF
+ exit 0
+ EOF
+'
+
+test_expect_success 'no warning if hook is not ignored' '
+ git commit --allow-empty -m "more" 2>message &&
+ test_i18ngrep ! -e "hook was ignored" message
+'
+
+test_expect_success POSIXPERM 'warning if hook is ignored' '
+ chmod -x "$hook" &&
+ git commit --allow-empty -m "even more" 2>message &&
+ test_i18ngrep -e "hook was ignored" message
+'
+
+test_expect_success POSIXPERM 'no warning if advice.ignoredHook set to false' '
+ test_config advice.ignoredHook false &&
+ chmod -x "$hook" &&
+ git commit --allow-empty -m "even more" 2>message &&
+ test_i18ngrep ! -e "hook was ignored" message
+'
+
+test_expect_success 'no warning if unset advice.ignoredHook and hook removed' '
+ rm -f "$hook" &&
+ test_unconfig advice.ignoredHook &&
+ git commit --allow-empty -m "even more" 2>message &&
+ test_i18ngrep ! -e "hook was ignored" message
+'
+
+test_done
diff --git a/t/t7521-ignored-mode.sh b/t/t7521-ignored-mode.sh
new file mode 100755
index 0000000000..91790943c3
--- /dev/null
+++ b/t/t7521-ignored-mode.sh
@@ -0,0 +1,233 @@
+#!/bin/sh
+
+test_description='git status ignored modes'
+
+. ./test-lib.sh
+
+test_expect_success 'setup initial commit and ignore file' '
+ cat >.gitignore <<-\EOF &&
+ *.ign
+ ignored_dir/
+ !*.unignore
+ EOF
+ git add . &&
+ git commit -m "Initial commit"
+'
+
+test_expect_success 'Verify behavior of status on directories with ignored files' '
+ test_when_finished "git clean -fdx" &&
+ cat >expect <<-\EOF &&
+ ? expect
+ ? output
+ ! dir/ignored/ignored_1.ign
+ ! dir/ignored/ignored_2.ign
+ ! ignored/ignored_1.ign
+ ! ignored/ignored_2.ign
+ EOF
+
+ mkdir -p ignored dir/ignored &&
+ touch ignored/ignored_1.ign ignored/ignored_2.ign \
+ dir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&
+
+ git status --porcelain=v2 --ignored=matching --untracked-files=all >output &&
+ test_i18ncmp expect output
+'
+
+test_expect_success 'Verify status behavior on directory with tracked & ignored files' '
+ test_when_finished "git clean -fdx && git reset HEAD~1 --hard" &&
+ cat >expect <<-\EOF &&
+ ? expect
+ ? output
+ ! dir/tracked_ignored/ignored_1.ign
+ ! dir/tracked_ignored/ignored_2.ign
+ ! tracked_ignored/ignored_1.ign
+ ! tracked_ignored/ignored_2.ign
+ EOF
+
+ mkdir -p tracked_ignored dir/tracked_ignored &&
+ touch tracked_ignored/tracked_1 tracked_ignored/tracked_2 \
+ tracked_ignored/ignored_1.ign tracked_ignored/ignored_2.ign \
+ dir/tracked_ignored/tracked_1 dir/tracked_ignored/tracked_2 \
+ dir/tracked_ignored/ignored_1.ign dir/tracked_ignored/ignored_2.ign &&
+
+ git add tracked_ignored/tracked_1 tracked_ignored/tracked_2 \
+ dir/tracked_ignored/tracked_1 dir/tracked_ignored/tracked_2 &&
+ git commit -m "commit tracked files" &&
+
+ git status --porcelain=v2 --ignored=matching --untracked-files=all >output &&
+ test_i18ncmp expect output
+'
+
+test_expect_success 'Verify status behavior on directory with untracked and ignored files' '
+ test_when_finished "git clean -fdx" &&
+ cat >expect <<-\EOF &&
+ ? dir/untracked_ignored/untracked_1
+ ? dir/untracked_ignored/untracked_2
+ ? expect
+ ? output
+ ? untracked_ignored/untracked_1
+ ? untracked_ignored/untracked_2
+ ! dir/untracked_ignored/ignored_1.ign
+ ! dir/untracked_ignored/ignored_2.ign
+ ! untracked_ignored/ignored_1.ign
+ ! untracked_ignored/ignored_2.ign
+ EOF
+
+ mkdir -p untracked_ignored dir/untracked_ignored &&
+ touch untracked_ignored/untracked_1 untracked_ignored/untracked_2 \
+ untracked_ignored/ignored_1.ign untracked_ignored/ignored_2.ign \
+ dir/untracked_ignored/untracked_1 dir/untracked_ignored/untracked_2 \
+ dir/untracked_ignored/ignored_1.ign dir/untracked_ignored/ignored_2.ign &&
+
+ git status --porcelain=v2 --ignored=matching --untracked-files=all >output &&
+ test_i18ncmp expect output
+'
+
+test_expect_success 'Verify status matching ignored files on ignored directory' '
+ test_when_finished "git clean -fdx" &&
+ cat >expect <<-\EOF &&
+ ? expect
+ ? output
+ ! ignored_dir/
+ EOF
+
+ mkdir ignored_dir &&
+ touch ignored_dir/ignored_1 ignored_dir/ignored_2 \
+ ignored_dir/ignored_1.ign ignored_dir/ignored_2.ign &&
+
+ git status --porcelain=v2 --ignored=matching --untracked-files=all >output &&
+ test_i18ncmp expect output
+'
+
+test_expect_success 'Verify status behavior on ignored directory containing tracked file' '
+ test_when_finished "git clean -fdx && git reset HEAD~1 --hard" &&
+ cat >expect <<-\EOF &&
+ ? expect
+ ? output
+ ! ignored_dir/ignored_1
+ ! ignored_dir/ignored_1.ign
+ ! ignored_dir/ignored_2
+ ! ignored_dir/ignored_2.ign
+ EOF
+
+ mkdir ignored_dir &&
+ touch ignored_dir/ignored_1 ignored_dir/ignored_2 \
+ ignored_dir/ignored_1.ign ignored_dir/ignored_2.ign \
+ ignored_dir/tracked &&
+ git add -f ignored_dir/tracked &&
+ git commit -m "Force add file in ignored directory" &&
+ git status --porcelain=v2 --ignored=matching --untracked-files=all >output &&
+ test_i18ncmp expect output
+'
+
+test_expect_success 'Verify matching ignored files with --untracked-files=normal' '
+ test_when_finished "git clean -fdx" &&
+ cat >expect <<-\EOF &&
+ ? expect
+ ? output
+ ? untracked_dir/
+ ! ignored_dir/
+ ! ignored_files/ignored_1.ign
+ ! ignored_files/ignored_2.ign
+ EOF
+
+ mkdir ignored_dir ignored_files untracked_dir &&
+ touch ignored_dir/ignored_1 ignored_dir/ignored_2 \
+ ignored_files/ignored_1.ign ignored_files/ignored_2.ign \
+ untracked_dir/untracked &&
+ git status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&
+ test_i18ncmp expect output
+'
+
+test_expect_success 'Verify matching ignored files with --untracked-files=normal' '
+ test_when_finished "git clean -fdx" &&
+ cat >expect <<-\EOF &&
+ ? expect
+ ? output
+ ? untracked_dir/
+ ! ignored_dir/
+ ! ignored_files/ignored_1.ign
+ ! ignored_files/ignored_2.ign
+ EOF
+
+ mkdir ignored_dir ignored_files untracked_dir &&
+ touch ignored_dir/ignored_1 ignored_dir/ignored_2 \
+ ignored_files/ignored_1.ign ignored_files/ignored_2.ign \
+ untracked_dir/untracked &&
+ git status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&
+ test_i18ncmp expect output
+'
+
+test_expect_success 'Verify status behavior on ignored directory containing tracked file' '
+ test_when_finished "git clean -fdx && git reset HEAD~1 --hard" &&
+ cat >expect <<-\EOF &&
+ ? expect
+ ? output
+ ! ignored_dir/ignored_1
+ ! ignored_dir/ignored_1.ign
+ ! ignored_dir/ignored_2
+ ! ignored_dir/ignored_2.ign
+ EOF
+
+ mkdir ignored_dir &&
+ touch ignored_dir/ignored_1 ignored_dir/ignored_2 \
+ ignored_dir/ignored_1.ign ignored_dir/ignored_2.ign \
+ ignored_dir/tracked &&
+ git add -f ignored_dir/tracked &&
+ git commit -m "Force add file in ignored directory" &&
+ git status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&
+ test_i18ncmp expect output
+'
+
+test_expect_success 'Verify behavior of status with --ignored=no' '
+ test_when_finished "git clean -fdx" &&
+ cat >expect <<-\EOF &&
+ ? expect
+ ? output
+ EOF
+
+ mkdir -p ignored dir/ignored &&
+ touch ignored/ignored_1.ign ignored/ignored_2.ign \
+ dir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&
+
+ git status --porcelain=v2 --ignored=no --untracked-files=all >output &&
+ test_i18ncmp expect output
+'
+
+test_expect_success 'Verify behavior of status with --ignored=traditional and --untracked-files=all' '
+ test_when_finished "git clean -fdx" &&
+ cat >expect <<-\EOF &&
+ ? expect
+ ? output
+ ! dir/ignored/ignored_1.ign
+ ! dir/ignored/ignored_2.ign
+ ! ignored/ignored_1.ign
+ ! ignored/ignored_2.ign
+ EOF
+
+ mkdir -p ignored dir/ignored &&
+ touch ignored/ignored_1.ign ignored/ignored_2.ign \
+ dir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&
+
+ git status --porcelain=v2 --ignored=traditional --untracked-files=all >output &&
+ test_i18ncmp expect output
+'
+
+test_expect_success 'Verify behavior of status with --ignored=traditional and --untracked-files=normal' '
+ test_when_finished "git clean -fdx" &&
+ cat >expect <<-\EOF &&
+ ? expect
+ ? output
+ ! dir/
+ ! ignored/
+ EOF
+
+ mkdir -p ignored dir/ignored &&
+ touch ignored/ignored_1.ign ignored/ignored_2.ign \
+ dir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&
+
+ git status --porcelain=v2 --ignored=traditional --untracked-files=normal >output &&
+ test_i18ncmp expect output
+'
+
+test_done
diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh
index 80194b79f9..dfde6a675a 100755
--- a/t/t7600-merge.sh
+++ b/t/t7600-merge.sh
@@ -697,7 +697,7 @@ test_expect_success 'merge --no-ff --edit' '
git cat-file commit HEAD >raw &&
grep "work done on the side branch" raw &&
sed "1,/^$/d" >actual raw &&
- test_cmp actual expected
+ test_cmp expected actual
'
test_expect_success GPG 'merge --ff-only tag' '
@@ -709,7 +709,7 @@ test_expect_success GPG 'merge --ff-only tag' '
git merge --ff-only signed &&
git rev-parse signed^0 >expect &&
git rev-parse HEAD >actual &&
- test_cmp actual expect
+ test_cmp expect actual
'
test_expect_success GPG 'merge --no-edit tag should skip editor' '
@@ -721,7 +721,7 @@ test_expect_success GPG 'merge --no-edit tag should skip editor' '
EDITOR=false git merge --no-edit signed &&
git rev-parse signed^0 >expect &&
git rev-parse HEAD^2 >actual &&
- test_cmp actual expect
+ test_cmp expect actual
'
test_expect_success 'set up mod-256 conflict scenario' '
diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh
index 381b7df452..1a430b9c40 100755
--- a/t/t7610-mergetool.sh
+++ b/t/t7610-mergetool.sh
@@ -621,7 +621,7 @@ test_expect_success 'file with no base' '
test_must_fail git merge master &&
git mergetool --no-prompt --tool mybase -- both &&
>expected &&
- test_cmp both expected
+ test_cmp expected both
'
test_expect_success 'custom commands override built-ins' '
@@ -632,7 +632,7 @@ test_expect_success 'custom commands override built-ins' '
test_must_fail git merge master &&
git mergetool --no-prompt --tool defaults -- both &&
echo master both added >expected &&
- test_cmp both expected
+ test_cmp expected both
'
test_expect_success 'filenames seen by tools start with ./' '
diff --git a/t/t7614-merge-signoff.sh b/t/t7614-merge-signoff.sh
new file mode 100755
index 0000000000..c1b8446f49
--- /dev/null
+++ b/t/t7614-merge-signoff.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+test_description='git merge --signoff
+
+This test runs git merge --signoff and makes sure that it works.
+'
+
+. ./test-lib.sh
+
+# Setup test files
+test_setup() {
+ # Expected commit message after merge --signoff
+ cat >expected-signed <<EOF &&
+Merge branch 'master' into other-branch
+
+Signed-off-by: $(git var GIT_COMMITTER_IDENT | sed -e "s/>.*/>/")
+EOF
+
+ # Expected commit message after merge without --signoff (or with --no-signoff)
+ cat >expected-unsigned <<EOF &&
+Merge branch 'master' into other-branch
+EOF
+
+ # Initial commit and feature branch to merge master into it.
+ git commit --allow-empty -m "Initial empty commit" &&
+ git checkout -b other-branch &&
+ test_commit other-branch file1 1
+}
+
+# Setup repository, files & feature branch
+# This step must be run if You want to test 2,3 or 4
+# Order of 2,3,4 is not important, but 1 must be run before
+# For example `-r 1,4` or `-r 1,4,2 -v` etc
+# But not `-r 2` or `-r 4,3,2,1`
+test_expect_success 'setup' '
+ test_setup
+'
+
+# Test with --signoff flag
+test_expect_success 'git merge --signoff adds a sign-off line' '
+ git checkout master &&
+ test_commit master-branch-2 file2 2 &&
+ git checkout other-branch &&
+ git merge master --signoff --no-edit &&
+ git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
+ test_cmp expected-signed actual
+'
+
+# Test without --signoff flag
+test_expect_success 'git merge does not add a sign-off line' '
+ git checkout master &&
+ test_commit master-branch-3 file3 3 &&
+ git checkout other-branch &&
+ git merge master --no-edit &&
+ git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
+ test_cmp expected-unsigned actual
+'
+
+# Test for --no-signoff flag
+test_expect_success 'git merge --no-signoff flag cancels --signoff flag' '
+ git checkout master &&
+ test_commit master-branch-4 file4 4 &&
+ git checkout other-branch &&
+ git merge master --no-edit --signoff --no-signoff &&
+ git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
+ test_cmp expected-unsigned actual
+'
+
+test_done
diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh
index 2a6679c2f5..c02ca735b9 100755
--- a/t/t7810-grep.sh
+++ b/t/t7810-grep.sh
@@ -60,6 +60,18 @@ test_expect_success setup '
echo " line with leading space3"
echo "line without leading space2"
} >space &&
+ cat >hello.ps1 <<-\EOF &&
+ # No-op.
+ function dummy() {}
+
+ # Say hello.
+ function hello() {
+ echo "Hello world."
+ } # hello
+
+ # Still a no-op.
+ function dummy() {}
+ EOF
git add . &&
test_tick &&
git commit -m initial
@@ -766,18 +778,27 @@ test_expect_success 'grep -W shows no trailing empty lines' '
test_cmp expected actual
'
-cat >expected <<EOF
-hello.c= printf("Hello world.\n");
-hello.c: return 0;
-hello.c- /* char ?? */
-EOF
-
test_expect_success 'grep -W with userdiff' '
test_when_finished "rm -f .gitattributes" &&
- git config diff.custom.xfuncname "(printf.*|})$" &&
- echo "hello.c diff=custom" >.gitattributes &&
- git grep -W return >actual &&
- test_cmp expected actual
+ git config diff.custom.xfuncname "^function .*$" &&
+ echo "hello.ps1 diff=custom" >.gitattributes &&
+ git grep -W echo >function-context-userdiff-actual
+'
+
+test_expect_success ' includes preceding comment' '
+ grep "# Say hello" function-context-userdiff-actual
+'
+
+test_expect_success ' includes function line' '
+ grep "=function hello" function-context-userdiff-actual
+'
+
+test_expect_success ' includes matching line' '
+ grep ": echo" function-context-userdiff-actual
+'
+
+test_expect_success ' includes last line of the function' '
+ grep "} # hello" function-context-userdiff-actual
'
for threads in $(test_seq 0 10)
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index f30980895c..4d261c2a9c 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -1266,7 +1266,7 @@ test_expect_success $PREREQ 'asks about and fixes 8bit encodings' '
grep email-using-8bit stdout &&
grep "Which 8bit encoding" stdout &&
egrep "Content|MIME" msgtxt1 >actual &&
- test_cmp actual content-type-decl
+ test_cmp content-type-decl actual
'
test_expect_success $PREREQ 'sendemail.8bitEncoding works' '
@@ -1277,7 +1277,7 @@ test_expect_success $PREREQ 'sendemail.8bitEncoding works' '
--smtp-server="$(pwd)/fake.sendmail" \
email-using-8bit >stdout &&
egrep "Content|MIME" msgtxt1 >actual &&
- test_cmp actual content-type-decl
+ test_cmp content-type-decl actual
'
test_expect_success $PREREQ '--8bit-encoding overrides sendemail.8bitEncoding' '
@@ -1289,7 +1289,7 @@ test_expect_success $PREREQ '--8bit-encoding overrides sendemail.8bitEncoding' '
--8bit-encoding=UTF-8 \
email-using-8bit >stdout &&
egrep "Content|MIME" msgtxt1 >actual &&
- test_cmp actual content-type-decl
+ test_cmp content-type-decl actual
'
test_expect_success $PREREQ 'setup expect' '
diff --git a/t/t9010-svn-fe.sh b/t/t9010-svn-fe.sh
index 6dafe7e99a..8eaaca6f99 100755
--- a/t/t9010-svn-fe.sh
+++ b/t/t9010-svn-fe.sh
@@ -4,12 +4,13 @@ test_description='check svn dumpfile importer'
. ./test-lib.sh
+if test_have_prereq !PIPE
+then
+ skip_all="svn dumpfile importer testing requires the PIPE prerequisite"
+ test_done
+fi
+
reinit_git () {
- if ! test_declared_prereq PIPE
- then
- echo >&4 "reinit_git: need to declare PIPE prerequisite"
- return 127
- fi
rm -fr .git &&
rm -f stream backflow &&
git init &&
@@ -54,19 +55,19 @@ text_no_props () {
>empty
-test_expect_success PIPE 'empty dump' '
+test_expect_success 'empty dump' '
reinit_git &&
echo "SVN-fs-dump-format-version: 2" >input &&
try_dump input
'
-test_expect_success PIPE 'v4 dumps not supported' '
+test_expect_success 'v4 dumps not supported' '
reinit_git &&
echo "SVN-fs-dump-format-version: 4" >v4.dump &&
try_dump v4.dump must_fail
'
-test_expect_failure PIPE 'empty revision' '
+test_expect_failure 'empty revision' '
reinit_git &&
printf "rev <nobody, nobody@local>: %s\n" "" "" >expect &&
cat >emptyrev.dump <<-\EOF &&
@@ -86,7 +87,7 @@ test_expect_failure PIPE 'empty revision' '
test_cmp expect actual
'
-test_expect_success PIPE 'empty properties' '
+test_expect_success 'empty properties' '
reinit_git &&
printf "rev <nobody, nobody@local>: %s\n" "" "" >expect &&
cat >emptyprop.dump <<-\EOF &&
@@ -109,7 +110,7 @@ test_expect_success PIPE 'empty properties' '
test_cmp expect actual
'
-test_expect_success PIPE 'author name and commit message' '
+test_expect_success 'author name and commit message' '
reinit_git &&
echo "<author@example.com, author@example.com@local>" >expect.author &&
cat >message <<-\EOF &&
@@ -143,7 +144,7 @@ test_expect_success PIPE 'author name and commit message' '
test_cmp expect.author actual.author
'
-test_expect_success PIPE 'unsupported properties are ignored' '
+test_expect_success 'unsupported properties are ignored' '
reinit_git &&
echo author >expect &&
cat >extraprop.dump <<-\EOF &&
@@ -168,7 +169,7 @@ test_expect_success PIPE 'unsupported properties are ignored' '
test_cmp expect actual
'
-test_expect_failure PIPE 'timestamp and empty file' '
+test_expect_failure 'timestamp and empty file' '
echo author@example.com >expect.author &&
echo 1999-01-01 >expect.date &&
echo file >expect.files &&
@@ -210,7 +211,7 @@ test_expect_failure PIPE 'timestamp and empty file' '
test_cmp empty file
'
-test_expect_success PIPE 'directory with files' '
+test_expect_success 'directory with files' '
reinit_git &&
printf "%s\n" directory/file1 directory/file2 >expect.files &&
echo hi >hi &&
@@ -263,7 +264,7 @@ test_expect_success PIPE 'directory with files' '
test_cmp hi directory/file2
'
-test_expect_success PIPE 'branch name with backslash' '
+test_expect_success 'branch name with backslash' '
reinit_git &&
sort <<-\EOF >expect.branch-files &&
trunk/file1
@@ -362,7 +363,7 @@ test_expect_success PIPE 'branch name with backslash' '
test_cmp expect.branch-files actual.branch-files
'
-test_expect_success PIPE 'node without action' '
+test_expect_success 'node without action' '
reinit_git &&
cat >inaction.dump <<-\EOF &&
SVN-fs-dump-format-version: 3
@@ -383,7 +384,7 @@ test_expect_success PIPE 'node without action' '
try_dump inaction.dump must_fail
'
-test_expect_success PIPE 'action: add node without text' '
+test_expect_success 'action: add node without text' '
reinit_git &&
cat >textless.dump <<-\EOF &&
SVN-fs-dump-format-version: 3
@@ -405,7 +406,7 @@ test_expect_success PIPE 'action: add node without text' '
try_dump textless.dump must_fail
'
-test_expect_failure PIPE 'change file mode but keep old content' '
+test_expect_failure 'change file mode but keep old content' '
reinit_git &&
cat >expect <<-\EOF &&
OBJID
@@ -481,7 +482,7 @@ test_expect_failure PIPE 'change file mode but keep old content' '
test_cmp hello actual.target
'
-test_expect_success PIPE 'NUL in property value' '
+test_expect_success 'NUL in property value' '
reinit_git &&
echo "commit message" >expect.message &&
{
@@ -507,7 +508,7 @@ test_expect_success PIPE 'NUL in property value' '
test_cmp expect.message actual.message
'
-test_expect_success PIPE 'NUL in log message, file content, and property name' '
+test_expect_success 'NUL in log message, file content, and property name' '
# Caveat: svnadmin 1.6.16 (r1073529) truncates at \0 in the
# svn:specialQnotreally example.
reinit_git &&
@@ -587,7 +588,7 @@ test_expect_success PIPE 'NUL in log message, file content, and property name' '
test_cmp expect.hello2 actual.hello2
'
-test_expect_success PIPE 'change file mode and reiterate content' '
+test_expect_success 'change file mode and reiterate content' '
reinit_git &&
cat >expect <<-\EOF &&
OBJID
@@ -667,7 +668,7 @@ test_expect_success PIPE 'change file mode and reiterate content' '
test_cmp hello actual.target
'
-test_expect_success PIPE 'deltas supported' '
+test_expect_success 'deltas supported' '
reinit_git &&
{
# (old) h + (inline) ello + (old) \n
@@ -731,7 +732,7 @@ test_expect_success PIPE 'deltas supported' '
try_dump delta.dump
'
-test_expect_success PIPE 'property deltas supported' '
+test_expect_success 'property deltas supported' '
reinit_git &&
cat >expect <<-\EOF &&
OBJID
@@ -796,7 +797,7 @@ test_expect_success PIPE 'property deltas supported' '
test_cmp expect actual
'
-test_expect_success PIPE 'properties on /' '
+test_expect_success 'properties on /' '
reinit_git &&
cat <<-\EOF >expect &&
OBJID
@@ -850,7 +851,7 @@ test_expect_success PIPE 'properties on /' '
test_cmp expect actual
'
-test_expect_success PIPE 'deltas for typechange' '
+test_expect_success 'deltas for typechange' '
reinit_git &&
cat >expect <<-\EOF &&
OBJID
@@ -935,7 +936,7 @@ test_expect_success PIPE 'deltas for typechange' '
test_cmp expect actual
'
-test_expect_success PIPE 'deltas need not consume the whole preimage' '
+test_expect_success 'deltas need not consume the whole preimage' '
reinit_git &&
cat >expect <<-\EOF &&
OBJID
@@ -1040,7 +1041,7 @@ test_expect_success PIPE 'deltas need not consume the whole preimage' '
test_cmp expect.3 actual.3
'
-test_expect_success PIPE 'no hang for delta trying to read past end of preimage' '
+test_expect_success 'no hang for delta trying to read past end of preimage' '
reinit_git &&
{
# COPY 1
@@ -1087,7 +1088,7 @@ test_expect_success 'set up svn repo' '
fi
'
-test_expect_success SVNREPO,PIPE 't9135/svn.dump' '
+test_expect_success SVNREPO 't9135/svn.dump' '
mkdir -p simple-git &&
(
cd simple-git &&
diff --git a/t/t9114-git-svn-dcommit-merge.sh b/t/t9114-git-svn-dcommit-merge.sh
index a3d388228a..50bca62def 100755
--- a/t/t9114-git-svn-dcommit-merge.sh
+++ b/t/t9114-git-svn-dcommit-merge.sh
@@ -27,9 +27,7 @@ cat << EOF
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
-# MA 02111-1307 USA
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
#
EOF
}
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 2cb999ecfa..fc614dcbfa 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -1245,6 +1245,10 @@ test_expect_success 'double dash "git checkout"' '
--conflict=
--orphan Z
--patch Z
+ --detach Z
+ --ignore-skip-worktree-bits Z
+ --recurse-submodules Z
+ --no-recurse-submodules Z
EOF
'
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 5fbd8d4a90..116bd6a70c 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -44,6 +44,11 @@ GIT_BUILD_DIR="$TEST_DIRECTORY"/..
: ${ASAN_OPTIONS=detect_leaks=0:abort_on_error=1}
export ASAN_OPTIONS
+# If LSAN is in effect we _do_ want leak checking, but we still
+# want to abort so that we notice the problems.
+: ${LSAN_OPTIONS=abort_on_error=1}
+export LSAN_OPTIONS
+
################################################################
# It appears that people try to run tests without building...
"$GIT_BUILD_DIR/git" >/dev/null
@@ -170,9 +175,10 @@ esac
# Convenience
#
-# A regexp to match 5 and 40 hexdigits
+# A regexp to match 5, 35 and 40 hexdigits
_x05='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
-_x40="$_x05$_x05$_x05$_x05$_x05$_x05$_x05$_x05"
+_x35="$_x05$_x05$_x05$_x05$_x05$_x05$_x05"
+_x40="$_x35$_x05"
# Zero SHA-1
_z40=0000000000000000000000000000000000000000
@@ -188,7 +194,7 @@ LF='
# when case-folding filenames
u200c=$(printf '\342\200\214')
-export _x05 _x40 _z40 LF u200c EMPTY_TREE EMPTY_BLOB
+export _x05 _x35 _x40 _z40 LF u200c EMPTY_TREE EMPTY_BLOB
# Each test should start with something like this, after copyright notices:
#
@@ -274,7 +280,7 @@ then
test -z "$verbose" && verbose_only="$valgrind_only"
elif test -n "$valgrind"
then
- verbose=t
+ test -z "$verbose_log" && verbose=t
fi
if test -n "$color"
@@ -1062,14 +1068,8 @@ test_i18ngrep () {
test_lazy_prereq PIPE '
# test whether the filesystem supports FIFOs
- case $(uname -s) in
- CYGWIN*|MINGW*)
- false
- ;;
- *)
- rm -f testfifo && mkfifo testfifo
- ;;
- esac
+ test_have_prereq !MINGW,!CYGWIN &&
+ rm -f testfifo && mkfifo testfifo
'
test_lazy_prereq SYMLINKS '
@@ -1165,7 +1165,19 @@ run_with_limited_cmdline () {
(ulimit -s 128 && "$@")
}
-test_lazy_prereq CMDLINE_LIMIT 'run_with_limited_cmdline true'
+test_lazy_prereq CMDLINE_LIMIT '
+ test_have_prereq !MINGW,!CYGWIN &&
+ run_with_limited_cmdline true
+'
+
+run_with_limited_stack () {
+ (ulimit -s 128 && "$@")
+}
+
+test_lazy_prereq ULIMIT_STACK_SIZE '
+ test_have_prereq !MINGW,!CYGWIN &&
+ run_with_limited_stack true
+'
build_option () {
git version --build-options |