summaryrefslogtreecommitdiff
path: root/t
diff options
context:
space:
mode:
Diffstat (limited to 't')
-rw-r--r--t/Makefile6
-rw-r--r--t/README36
-rwxr-xr-xt/check-non-portable-shell.pl1
-rw-r--r--t/helper/.gitignore43
-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-example-decorate.c74
-rw-r--r--t/helper/test-lazy-init-name-hash.c2
-rw-r--r--t/helper/test-ref-store.c28
-rwxr-xr-xt/interop/i5700-protocol-transition.sh68
-rw-r--r--t/lib-git-daemon.sh41
-rw-r--r--t/lib-git-svn.sh24
-rw-r--r--t/lib-httpd/apache.conf5
-rwxr-xr-xt/lib-submodule-update.sh596
-rwxr-xr-xt/perf/aggregate.perl11
-rw-r--r--t/perf/lib-pack.sh25
-rwxr-xr-xt/perf/p4211-line-log.sh8
-rwxr-xr-xt/perf/p5550-fetch-tags.sh25
-rwxr-xr-xt/perf/p5551-fetch-rescan.sh55
-rwxr-xr-xt/perf/p7519-fsmonitor.sh183
-rwxr-xr-xt/perf/p7820-grep-engines.sh52
-rwxr-xr-xt/perf/p7821-grep-engines-fixed.sh55
-rw-r--r--t/perf/perf-lib.sh4
-rwxr-xr-xt/perf/run89
-rwxr-xr-xt/t0000-basic.sh25
-rw-r--r--t/t0021/rot13-filter.pl142
-rwxr-xr-xt/t0025-crlf-renormalize.sh30
-rwxr-xr-xt/t0027-auto-crlf.sh79
-rwxr-xr-xt/t0205-gettext-poison.sh4
-rwxr-xr-xt/t0302-credential-store.sh2
-rwxr-xr-xt/t1300-repo-config.sh30
-rwxr-xr-xt/t1430-bad-ref-name.sh43
-rwxr-xr-xt/t1700-split-index.sh39
-rwxr-xr-xt/t2020-checkout-detach.sh123
-rwxr-xr-xt/t2025-worktree-add.sh165
-rwxr-xr-xt/t2203-add-intent.sh72
-rwxr-xr-xt/t3030-merge-recursive.sh50
-rwxr-xr-xt/t3040-subprojects-basic.sh2
-rwxr-xr-xt/t3404-rebase-interactive.sh22
-rwxr-xr-xt/t3418-rebase-continue.sh14
-rwxr-xr-xt/t3600-rm.sh5
-rwxr-xr-xt/t3700-add.sh5
-rwxr-xr-xt/t3701-add-interactive.sh48
-rwxr-xr-xt/t3900-i18n-commit.sh8
-rwxr-xr-xt/t3903-stash.sh125
-rwxr-xr-xt/t4001-diff-rename.sh26
-rwxr-xr-xt/t4013-diff-various.sh45
-rw-r--r--t/t4013/diff.noellipses-diff-tree_--root_--abbrev_initial6
-rw-r--r--t/t4013/diff.noellipses-diff-tree_--root_-r_--abbrev=4_initial6
-rw-r--r--t/t4013/diff.noellipses-diff-tree_--root_-r_--abbrev_initial6
-rw-r--r--t/t4013/diff.noellipses-diff-tree_-c_--abbrev_master5
-rw-r--r--t/t4013/diff.noellipses-diff_--no-index_--raw_--abbrev=4_dir2_dir3
-rw-r--r--t/t4013/diff.noellipses-diff_--no-index_--raw_dir2_dir3
-rw-r--r--t/t4013/diff.noellipses-diff_--patch-with-raw_-r_initial..side36
-rw-r--r--t/t4013/diff.noellipses-diff_--patch-with-raw_initial..side36
-rw-r--r--t/t4013/diff.noellipses-diff_--raw_--abbrev=4_initial6
-rw-r--r--t/t4013/diff.noellipses-diff_--raw_initial6
-rw-r--r--t/t4013/diff.noellipses-show_--patch-with-raw_side42
-rw-r--r--t/t4013/diff.noellipses-whatchanged_--root_master42
-rw-r--r--t/t4013/diff.noellipses-whatchanged_-SF_master9
-rw-r--r--t/t4013/diff.noellipses-whatchanged_master32
-rwxr-xr-xt/t4015-diff-whitespace.sh45
-rwxr-xr-xt/t4045-diff-relative.sh110
-rwxr-xr-xt/t4051-diff-function-context.sh4
-rw-r--r--t/t4051/hello.c3
-rwxr-xr-xt/t4065-diff-anchored.sh94
-rwxr-xr-xt/t4202-log.sh101
-rwxr-xr-xt/t4208-log-magic-pathspec.sh19
-rwxr-xr-xt/t5317-pack-objects-filter-objects.sh375
-rwxr-xr-xt/t5510-fetch.sh9
-rwxr-xr-xt/t5521-pull-options.sh45
-rwxr-xr-xt/t5526-fetch-submodules.sh77
-rwxr-xr-xt/t5536-fetch-conflicts.sh2
-rwxr-xr-xt/t5541-http-push-smart.sh2
-rwxr-xr-xt/t5545-push-options.sh77
-rwxr-xr-xt/t5551-http-fetch-smart.sh33
-rwxr-xr-xt/t5570-git-daemon.sh37
-rwxr-xr-xt/t5573-pull-verify-signatures.sh81
-rwxr-xr-xt/t5600-clone-fail-cleanup.sh100
-rwxr-xr-xt/t5601-clone.sh95
-rwxr-xr-xt/t5603-clone-dirname.sh2
-rwxr-xr-xt/t5615-alternate-env.sh6
-rwxr-xr-xt/t5700-protocol-v1.sh294
-rwxr-xr-xt/t5812-proto-disable-http.sh5
-rwxr-xr-xt/t6015-rev-list-show-all-parents.sh31
-rwxr-xr-xt/t6022-merge-rename.sh6
-rwxr-xr-xt/t6030-bisect-porcelain.sh17
-rwxr-xr-xt/t6037-merge-ours-theirs.sh32
-rwxr-xr-xt/t6044-merge-unrelated-index-changes.sh26
-rwxr-xr-xt/t6100-rev-list-in-order.sh77
-rwxr-xr-xt/t6112-rev-list-filters-objects.sh225
-rwxr-xr-xt/t6120-describe.sh42
-rwxr-xr-xt/t6300-for-each-ref.sh32
-rwxr-xr-xt/t7001-mv.sh6
-rwxr-xr-xt/t7006-pager.sh38
-rwxr-xr-xt/t7409-submodule-detached-work-tree.sh2
-rwxr-xr-xt/t7500-commit.sh13
-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/t7612-merge-verify-signatures.sh45
-rwxr-xr-xt/t7810-grep.sh47
-rwxr-xr-xt/t9001-send-email.sh17
-rwxr-xr-xt/t9004-example.sh10
-rwxr-xr-xt/t9020-remote-svn.sh4
-rwxr-xr-xt/t9107-git-svn-migrate.sh2
-rwxr-xr-xt/t9169-git-svn-dcommit-crlf.sh27
-rwxr-xr-xt/t9300-fast-import.sh2
-rwxr-xr-xt/t9807-git-p4-submit.sh24
-rw-r--r--t/test-lib-functions.sh98
-rw-r--r--t/test-lib.sh75
115 files changed, 5351 insertions, 903 deletions
diff --git a/t/Makefile b/t/Makefile
index 1bb06c36f2..96317a35f4 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -8,6 +8,7 @@
#GIT_TEST_OPTS = --verbose --debug
SHELL_PATH ?= $(SHELL)
+TEST_SHELL_PATH ?= $(SHELL_PATH)
PERL_PATH ?= /usr/bin/perl
TAR ?= $(TAR)
RM ?= rm -f
@@ -23,6 +24,7 @@ endif
# Shell quote;
SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
+TEST_SHELL_PATH_SQ = $(subst ','\'',$(TEST_SHELL_PATH))
PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
TEST_RESULTS_DIRECTORY_SQ = $(subst ','\'',$(TEST_RESULTS_DIRECTORY))
@@ -42,11 +44,11 @@ failed:
test -z "$$failed" || $(MAKE) $$failed
prove: pre-clean $(TEST_LINT)
- @echo "*** prove ***"; $(PROVE) --exec '$(SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS)
+ @echo "*** prove ***"; $(PROVE) --exec '$(TEST_SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS)
$(MAKE) clean-except-prove-cache
$(T):
- @echo "*** $@ ***"; '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
+ @echo "*** $@ ***"; '$(TEST_SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
pre-clean:
$(RM) -r '$(TEST_RESULTS_DIRECTORY_SQ)'
diff --git a/t/README b/t/README
index 4b079e4494..1a1361a806 100644
--- a/t/README
+++ b/t/README
@@ -332,13 +332,10 @@ Writing Tests
-------------
The test script is written as a shell script. It should start
-with the standard "#!/bin/sh" with copyright notices, and an
+with the standard "#!/bin/sh", and an
assignment to variable 'test_description', like this:
#!/bin/sh
- #
- # Copyright (c) 2005 Junio C Hamano
- #
test_description='xxx test (option --frotz)
@@ -658,7 +655,7 @@ library for your script to use.
test_expect_code 1 git merge "merge msg" B master
'
- - test_must_fail <git-command>
+ - test_must_fail [<options>] <git-command>
Run a git command and ensure it fails in a controlled way. Use
this instead of "! <git-command>". When git-command dies due to a
@@ -666,17 +663,32 @@ library for your script to use.
treats it as just another expected failure, which would let such a
bug go unnoticed.
- - test_might_fail <git-command>
+ Accepts the following options:
+
+ ok=<signal-name>[,<...>]:
+ Don't treat an exit caused by the given signal as error.
+ Multiple signals can be specified as a comma separated list.
+ Currently recognized signal names are: sigpipe, success.
+ (Don't use 'success', use 'test_might_fail' instead.)
+
+ - test_might_fail [<options>] <git-command>
Similar to test_must_fail, but tolerate success, too. Use this
instead of "<git-command> || :" to catch failures due to segv.
+ Accepts the same options as test_must_fail.
+
- test_cmp <expected> <actual>
Check whether the content of the <actual> file matches the
<expected> file. This behaves like "cmp" but produces more
helpful output when the test is run with "-v" option.
+ - test_cmp_rev <expected> <actual>
+
+ Check whether the <expected> rev points to the same commit as the
+ <actual> rev.
+
- test_line_count (= | -lt | -ge | ...) <length> <file>
Check whether a file has the length it is expected to.
@@ -808,6 +820,18 @@ use these, and "test_set_prereq" for how to define your own.
Git was compiled with support for PCRE. Wrap any tests
that use git-grep --perl-regexp or git-grep -P in these.
+ - LIBPCRE1
+
+ Git was compiled with PCRE v1 support via
+ USE_LIBPCRE1=YesPlease. Wrap any PCRE using tests that for some
+ reason need v1 of the PCRE library instead of v2 in these.
+
+ - LIBPCRE2
+
+ Git was compiled with PCRE v2 support via
+ USE_LIBPCRE2=YesPlease. Wrap any PCRE using tests that for some
+ reason need v2 of the PCRE library instead of v1 in these.
+
- CASE_INSENSITIVE_FS
Test is run on a case insensitive file system.
diff --git a/t/check-non-portable-shell.pl b/t/check-non-portable-shell.pl
index 03dc9d2852..e07f028437 100755
--- a/t/check-non-portable-shell.pl
+++ b/t/check-non-portable-shell.pl
@@ -21,6 +21,7 @@ while (<>) {
/^\s*declare\s+/ and err 'arrays/declare not portable';
/^\s*[^#]\s*which\s/ and err 'which is not portable (please use type)';
/\btest\s+[^=]*==/ and err '"test a == b" is not portable (please use =)';
+ /\bwc -l.*"\s*=/ and err '`"$(wc -l)"` is not portable (please use test_line_count)';
/\bexport\s+[A-Za-z0-9_]*=/ and err '"export FOO=bar" is not portable (please use FOO=bar && export FOO)';
# this resets our $. for each file
close ARGV if eof;
diff --git a/t/helper/.gitignore b/t/helper/.gitignore
index 7c9d28a834..2bad28af92 100644
--- a/t/helper/.gitignore
+++ b/t/helper/.gitignore
@@ -1,38 +1,5 @@
-/test-chmtime
-/test-ctype
-/test-config
-/test-date
-/test-delta
-/test-dump-cache-tree
-/test-dump-split-index
-/test-dump-untracked-cache
-/test-fake-ssh
-/test-scrap-cache-tree
-/test-genrandom
-/test-hashmap
-/test-index-version
-/test-lazy-init-name-hash
-/test-line-buffer
-/test-match-trees
-/test-mergesort
-/test-mktemp
-/test-online-cpus
-/test-parse-options
-/test-path-utils
-/test-prio-queue
-/test-read-cache
-/test-ref-store
-/test-regex
-/test-revision-walking
-/test-run-command
-/test-sha1
-/test-sha1-array
-/test-sigchain
-/test-strcmp-offset
-/test-string-list
-/test-submodule-config
-/test-subprocess
-/test-svn-fe
-/test-urlmatch-normalization
-/test-wildmatch
-/test-write-cache
+*
+!*.sh
+!*.[ch]
+!*.gitignore
+
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-example-decorate.c b/t/helper/test-example-decorate.c
new file mode 100644
index 0000000000..90dc97a9d0
--- /dev/null
+++ b/t/helper/test-example-decorate.c
@@ -0,0 +1,74 @@
+#include "cache.h"
+#include "object.h"
+#include "decorate.h"
+
+int cmd_main(int argc, const char **argv)
+{
+ struct decoration n;
+ struct object_id one_oid = { {1} };
+ struct object_id two_oid = { {2} };
+ struct object_id three_oid = { {3} };
+ struct object *one, *two, *three;
+
+ int decoration_a, decoration_b;
+
+ void *ret;
+
+ int i, objects_noticed = 0;
+
+ /*
+ * The struct must be zero-initialized.
+ */
+ memset(&n, 0, sizeof(n));
+
+ /*
+ * Add 2 objects, one with a non-NULL decoration and one with a NULL
+ * decoration.
+ */
+ one = lookup_unknown_object(one_oid.hash);
+ two = lookup_unknown_object(two_oid.hash);
+ ret = add_decoration(&n, one, &decoration_a);
+ if (ret)
+ die("BUG: when adding a brand-new object, NULL should be returned");
+ ret = add_decoration(&n, two, NULL);
+ if (ret)
+ die("BUG: when adding a brand-new object, NULL should be returned");
+
+ /*
+ * When re-adding an already existing object, the old decoration is
+ * returned.
+ */
+ ret = add_decoration(&n, one, NULL);
+ if (ret != &decoration_a)
+ die("BUG: when readding an already existing object, existing decoration should be returned");
+ ret = add_decoration(&n, two, &decoration_b);
+ if (ret)
+ die("BUG: when readding an already existing object, existing decoration should be returned");
+
+ /*
+ * Lookup returns the added declarations, or NULL if the object was
+ * never added.
+ */
+ ret = lookup_decoration(&n, one);
+ if (ret)
+ die("BUG: lookup should return added declaration");
+ ret = lookup_decoration(&n, two);
+ if (ret != &decoration_b)
+ die("BUG: lookup should return added declaration");
+ three = lookup_unknown_object(three_oid.hash);
+ ret = lookup_decoration(&n, three);
+ if (ret)
+ die("BUG: lookup for unknown object should return NULL");
+
+ /*
+ * The user can also loop through all entries.
+ */
+ for (i = 0; i < n.size; i++) {
+ if (n.entries[i].base)
+ objects_noticed++;
+ }
+ if (objects_noticed != 2)
+ die("BUG: should have 2 objects");
+
+ return 0;
+}
diff --git a/t/helper/test-lazy-init-name-hash.c b/t/helper/test-lazy-init-name-hash.c
index 6368a89345..297fb01d61 100644
--- a/t/helper/test-lazy-init-name-hash.c
+++ b/t/helper/test-lazy-init-name-hash.c
@@ -112,7 +112,7 @@ static void analyze_run(void)
{
uint64_t t1s, t1m, t2s, t2m;
int cache_nr_limit;
- int nr_threads_used;
+ int nr_threads_used = 0;
int i;
int nr;
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
index 6ec2670044..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 ? ref : "(null)", 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/interop/i5700-protocol-transition.sh b/t/interop/i5700-protocol-transition.sh
new file mode 100755
index 0000000000..97e8e580ef
--- /dev/null
+++ b/t/interop/i5700-protocol-transition.sh
@@ -0,0 +1,68 @@
+#!/bin/sh
+
+VERSION_A=.
+VERSION_B=v2.0.0
+
+: ${LIB_GIT_DAEMON_PORT:=5700}
+LIB_GIT_DAEMON_COMMAND='git.b daemon'
+
+test_description='clone and fetch by client who is trying to use a new protocol'
+. ./interop-lib.sh
+. "$TEST_DIRECTORY"/lib-git-daemon.sh
+
+start_git_daemon --export-all
+
+repo=$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo
+
+test_expect_success "create repo served by $VERSION_B" '
+ git.b init "$repo" &&
+ git.b -C "$repo" commit --allow-empty -m one
+'
+
+test_expect_success "git:// clone with $VERSION_A and protocol v1" '
+ GIT_TRACE_PACKET=1 git.a -c protocol.version=1 clone "$GIT_DAEMON_URL/repo" child 2>log &&
+ git.a -C child log -1 --format=%s >actual &&
+ git.b -C "$repo" log -1 --format=%s >expect &&
+ test_cmp expect actual &&
+ grep "version=1" log
+'
+
+test_expect_success "git:// fetch with $VERSION_A and protocol v1" '
+ git.b -C "$repo" commit --allow-empty -m two &&
+ git.b -C "$repo" log -1 --format=%s >expect &&
+
+ GIT_TRACE_PACKET=1 git.a -C child -c protocol.version=1 fetch 2>log &&
+ git.a -C child log -1 --format=%s FETCH_HEAD >actual &&
+
+ test_cmp expect actual &&
+ grep "version=1" log &&
+ ! grep "version 1" log
+'
+
+stop_git_daemon
+
+test_expect_success "create repo served by $VERSION_B" '
+ git.b init parent &&
+ git.b -C parent commit --allow-empty -m one
+'
+
+test_expect_success "file:// clone with $VERSION_A and protocol v1" '
+ GIT_TRACE_PACKET=1 git.a -c protocol.version=1 clone --upload-pack="git.b upload-pack" parent child2 2>log &&
+ git.a -C child2 log -1 --format=%s >actual &&
+ git.b -C parent log -1 --format=%s >expect &&
+ test_cmp expect actual &&
+ ! grep "version 1" log
+'
+
+test_expect_success "file:// fetch with $VERSION_A and protocol v1" '
+ git.b -C parent commit --allow-empty -m two &&
+ git.b -C parent log -1 --format=%s >expect &&
+
+ GIT_TRACE_PACKET=1 git.a -C child2 -c protocol.version=1 fetch --upload-pack="git.b upload-pack" 2>log &&
+ git.a -C child2 log -1 --format=%s FETCH_HEAD >actual &&
+
+ test_cmp expect actual &&
+ ! grep "version 1" log
+'
+
+test_done
diff --git a/t/lib-git-daemon.sh b/t/lib-git-daemon.sh
index 987d40680b..edbea2d986 100644
--- a/t/lib-git-daemon.sh
+++ b/t/lib-git-daemon.sh
@@ -32,7 +32,8 @@ LIB_GIT_DAEMON_PORT=${LIB_GIT_DAEMON_PORT-${this_test#t}}
GIT_DAEMON_PID=
GIT_DAEMON_DOCUMENT_ROOT_PATH="$PWD"/repo
-GIT_DAEMON_URL=git://127.0.0.1:$LIB_GIT_DAEMON_PORT
+GIT_DAEMON_HOST_PORT=127.0.0.1:$LIB_GIT_DAEMON_PORT
+GIT_DAEMON_URL=git://$GIT_DAEMON_HOST_PORT
start_git_daemon() {
if test -n "$GIT_DAEMON_PID"
@@ -53,11 +54,19 @@ start_git_daemon() {
"$@" "$GIT_DAEMON_DOCUMENT_ROOT_PATH" \
>&3 2>git_daemon_output &
GIT_DAEMON_PID=$!
+ >daemon.log
{
- read line <&7
- echo >&4 "$line"
- cat <&7 >&4 &
- } 7<git_daemon_output &&
+ read -r line <&7
+ printf "%s\n" "$line"
+ printf >&4 "%s\n" "$line"
+ (
+ while read -r line <&7
+ do
+ printf "%s\n" "$line"
+ printf >&4 "%s\n" "$line"
+ done
+ ) &
+ } 7<git_daemon_output >>"$TRASH_DIRECTORY/daemon.log" &&
# Check expected output
if test x"$(expr "$line" : "\[[0-9]*\] \(.*\)")" != x"Ready to rumble"
@@ -90,3 +99,25 @@ stop_git_daemon() {
GIT_DAEMON_PID=
rm -f git_daemon_output
}
+
+# A stripped-down version of a netcat client, that connects to a "host:port"
+# given in $1, sends its stdin followed by EOF, then dumps the response (until
+# EOF) to stdout.
+fake_nc() {
+ if ! test_declared_prereq FAKENC
+ then
+ echo >&4 "fake_nc: need to declare FAKENC prerequisite"
+ return 127
+ fi
+ perl -Mstrict -MIO::Socket::INET -e '
+ my $s = IO::Socket::INET->new(shift)
+ or die "unable to open socket: $!";
+ print $s <STDIN>;
+ $s->shutdown(1);
+ print <$s>;
+ ' "$@"
+}
+
+test_lazy_prereq FAKENC '
+ perl -MIO::Socket::INET -e "exit 0"
+'
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
index 688313ed5c..4c1f81f167 100644
--- a/t/lib-git-svn.sh
+++ b/t/lib-git-svn.sh
@@ -17,8 +17,8 @@ SVN_TREE=$GIT_SVN_DIR/svn-tree
svn >/dev/null 2>&1
if test $? -ne 1
then
- skip_all='skipping git svn tests, svn not found'
- test_done
+ skip_all='skipping git svn tests, svn not found'
+ test_done
fi
svnrepo=$PWD/svnrepo
@@ -110,18 +110,20 @@ EOF
}
require_svnserve () {
- if test -z "$SVNSERVE_PORT"
- then
- skip_all='skipping svnserve test. (set $SVNSERVE_PORT to enable)'
- test_done
- fi
+ test_tristate GIT_TEST_SVNSERVE
+ if ! test "$GIT_TEST_SVNSERVE" = true
+ then
+ skip_all='skipping svnserve test. (set $GIT_TEST_SVNSERVE to enable)'
+ test_done
+ fi
}
start_svnserve () {
- svnserve --listen-port $SVNSERVE_PORT \
- --root "$rawsvnrepo" \
- --listen-once \
- --listen-host 127.0.0.1 &
+ SVNSERVE_PORT=${SVNSERVE_PORT-${this_test#t}}
+ svnserve --listen-port $SVNSERVE_PORT \
+ --root "$rawsvnrepo" \
+ --listen-once \
+ --listen-host 127.0.0.1 &
}
prepare_a_utf8_locale () {
diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf
index 0642ae7e6e..724d9ae462 100644
--- a/t/lib-httpd/apache.conf
+++ b/t/lib-httpd/apache.conf
@@ -25,6 +25,9 @@ ErrorLog error.log
<IfModule !mod_headers.c>
LoadModule headers_module modules/mod_headers.so
</IfModule>
+<IfModule !mod_setenvif.c>
+ LoadModule setenvif_module modules/mod_setenvif.so
+</IfModule>
<IfVersion < 2.4>
LockFile accept.lock
@@ -76,6 +79,8 @@ PassEnv ASAN_OPTIONS
PassEnv GIT_TRACE
PassEnv GIT_CONFIG_NOSYSTEM
+SetEnvIf Git-Protocol ".*" GIT_PROTOCOL=$0
+
Alias /dumb/ www/
Alias /auth/dumb/ www/auth/dumb/
diff --git a/t/lib-submodule-update.sh b/t/lib-submodule-update.sh
index 2d26f86800..1f38a85371 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 modify_sub1 origin/modify_sub1 &&
+ $command modify_sub1 &&
+ 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" &&
@@ -964,6 +885,7 @@ test_submodule_switch_recursing_with_args () {
(
cd submodule_update &&
git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
+ echo ignored >.git/modules/sub1/info/exclude &&
: >sub1/ignored &&
$command replace_sub1_with_file &&
test_superproject_content origin/replace_sub1_with_file &&
@@ -971,20 +893,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 +905,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 +920,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 +946,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 +973,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 +987,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 &&
@@ -1249,4 +1015,18 @@ test_submodule_forced_switch_recursing_with_args () {
test_submodule_content sub1 origin/modify_sub1
)
'
+
+ test_expect_success "$command: changed submodule worktree is reset" '
+ prolog &&
+ reset_work_tree_to_interested add_sub1 &&
+ (
+ cd submodule_update &&
+ rm sub1/file1 &&
+ : >sub1/new_file &&
+ git -C sub1 add new_file &&
+ $command HEAD &&
+ test_path_is_file sub1/file1 &&
+ test_path_is_missing sub1/new_file
+ )
+ '
}
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/p4211-line-log.sh b/t/perf/p4211-line-log.sh
index b7ff68d4fa..392bcc0e51 100755
--- a/t/perf/p4211-line-log.sh
+++ b/t/perf/p4211-line-log.sh
@@ -31,4 +31,12 @@ 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_perf 'git log --oneline --raw --parents -1000' '
+ git log --oneline --raw --parents -1000 >/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..65e145c02d
--- /dev/null
+++ b/t/perf/p7519-fsmonitor.sh
@@ -0,0 +1,183 @@
+#!/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
+'
+
+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/p7820-grep-engines.sh b/t/perf/p7820-grep-engines.sh
index 62aba19e76..8b09c5bf32 100755
--- a/t/perf/p7820-grep-engines.sh
+++ b/t/perf/p7820-grep-engines.sh
@@ -12,6 +12,9 @@ e.g. GIT_PERF_7820_GREP_OPTS=' -i'. Some options to try:
-vi
-vw
-viw
+
+If GIT_PERF_GREP_THREADS is set to a list of threads (e.g. '1 4 8'
+etc.) we will test the patterns under those numbers of threads.
"
. ./perf-lib.sh
@@ -19,6 +22,11 @@ e.g. GIT_PERF_7820_GREP_OPTS=' -i'. Some options to try:
test_perf_large_repo
test_checkout_worktree
+if test -n "$GIT_PERF_GREP_THREADS"
+then
+ test_set_prereq PERF_GREP_ENGINES_THREADS
+fi
+
for pattern in \
'how.to' \
'^how to' \
@@ -39,18 +47,42 @@ do
else
prereq=""
fi
- test_perf $prereq "$engine grep$GIT_PERF_7820_GREP_OPTS '$pattern'" "
- git -c grep.patternType=$engine grep$GIT_PERF_7820_GREP_OPTS -- '$pattern' >'out.$engine' || :
- "
- done
-
- test_expect_success "assert that all engines found the same for$GIT_PERF_7820_GREP_OPTS '$pattern'" '
- test_cmp out.basic out.extended &&
- if test_have_prereq PCRE
+ if ! test_have_prereq PERF_GREP_ENGINES_THREADS
then
- test_cmp out.basic out.perl
+ test_perf $prereq "$engine grep$GIT_PERF_7820_GREP_OPTS '$pattern'" "
+ git -c grep.patternType=$engine grep$GIT_PERF_7820_GREP_OPTS -- '$pattern' >'out.$engine' || :
+ "
+ else
+ for threads in $GIT_PERF_GREP_THREADS
+ do
+ test_perf PTHREADS,$prereq "$engine grep$GIT_PERF_7820_GREP_OPTS '$pattern' with $threads threads" "
+ git -c grep.patternType=$engine -c grep.threads=$threads grep$GIT_PERF_7820_GREP_OPTS -- '$pattern' >'out.$engine.$threads' || :
+ "
+ done
fi
- '
+ done
+
+ if ! test_have_prereq PERF_GREP_ENGINES_THREADS
+ then
+ test_expect_success "assert that all engines found the same for$GIT_PERF_7820_GREP_OPTS '$pattern'" '
+ test_cmp out.basic out.extended &&
+ if test_have_prereq PCRE
+ then
+ test_cmp out.basic out.perl
+ fi
+ '
+ else
+ for threads in $GIT_PERF_GREP_THREADS
+ do
+ test_expect_success PTHREADS "assert that all engines found the same for$GIT_PERF_7820_GREP_OPTS '$pattern' under threading" "
+ test_cmp out.basic.$threads out.extended.$threads &&
+ if test_have_prereq PCRE
+ then
+ test_cmp out.basic.$threads out.perl.$threads
+ fi
+ "
+ done
+ fi
done
test_done
diff --git a/t/perf/p7821-grep-engines-fixed.sh b/t/perf/p7821-grep-engines-fixed.sh
index c7ef1e198f..61e41b82cf 100755
--- a/t/perf/p7821-grep-engines-fixed.sh
+++ b/t/perf/p7821-grep-engines-fixed.sh
@@ -6,6 +6,9 @@ Set GIT_PERF_7821_GREP_OPTS in the environment to pass options to
git-grep. Make sure to include a leading space,
e.g. GIT_PERF_7821_GREP_OPTS=' -w'. See p7820-grep-engines.sh for more
options to try.
+
+If GIT_PERF_7821_THREADS is set to a list of threads (e.g. '1 4 8'
+etc.) we will test the patterns under those numbers of threads.
"
. ./perf-lib.sh
@@ -13,6 +16,11 @@ options to try.
test_perf_large_repo
test_checkout_worktree
+if test -n "$GIT_PERF_GREP_THREADS"
+then
+ test_set_prereq PERF_GREP_ENGINES_THREADS
+fi
+
for pattern in 'int' 'uncommon' 'æ'
do
for engine in fixed basic extended perl
@@ -23,19 +31,44 @@ do
else
prereq=""
fi
- test_perf $prereq "$engine grep$GIT_PERF_7821_GREP_OPTS $pattern" "
- git -c grep.patternType=$engine grep$GIT_PERF_7821_GREP_OPTS $pattern >'out.$engine' || :
- "
- done
-
- test_expect_success "assert that all engines found the same for$GIT_PERF_7821_GREP_OPTS $pattern" '
- test_cmp out.fixed out.basic &&
- test_cmp out.fixed out.extended &&
- if test_have_prereq PCRE
+ if ! test_have_prereq PERF_GREP_ENGINES_THREADS
then
- test_cmp out.fixed out.perl
+ test_perf $prereq "$engine grep$GIT_PERF_7821_GREP_OPTS $pattern" "
+ git -c grep.patternType=$engine grep$GIT_PERF_7821_GREP_OPTS $pattern >'out.$engine' || :
+ "
+ else
+ for threads in $GIT_PERF_GREP_THREADS
+ do
+ test_perf PTHREADS,$prereq "$engine grep$GIT_PERF_7821_GREP_OPTS $pattern with $threads threads" "
+ git -c grep.patternType=$engine -c grep.threads=$threads grep$GIT_PERF_7821_GREP_OPTS $pattern >'out.$engine.$threads' || :
+ "
+ done
fi
- '
+ done
+
+ if ! test_have_prereq PERF_GREP_ENGINES_THREADS
+ then
+ test_expect_success "assert that all engines found the same for$GIT_PERF_7821_GREP_OPTS $pattern" '
+ test_cmp out.fixed out.basic &&
+ test_cmp out.fixed out.extended &&
+ if test_have_prereq PCRE
+ then
+ test_cmp out.fixed out.perl
+ fi
+ '
+ else
+ for threads in $GIT_PERF_GREP_THREADS
+ do
+ test_expect_success PTHREADS "assert that all engines found the same for$GIT_PERF_7821_GREP_OPTS $pattern under threading" "
+ test_cmp out.fixed.$threads out.basic.$threads &&
+ test_cmp out.fixed.$threads out.extended.$threads &&
+ if test_have_prereq PCRE
+ then
+ test_cmp out.fixed.$threads out.perl.$threads
+ fi
+ "
+ done
+ fi
done
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/t0021/rot13-filter.pl b/t/t0021/rot13-filter.pl
index ad685d92f8..470107248e 100644
--- a/t/t0021/rot13-filter.pl
+++ b/t/t0021/rot13-filter.pl
@@ -30,9 +30,27 @@
# to the "list_available_blobs" response.
#
+use 5.008;
+sub gitperllib {
+ # Git assumes that all path lists are Unix-y colon-separated ones. But
+ # when the Git for Windows executes the test suite, its MSYS2 Bash
+ # calls git.exe, and colon-separated path lists are converted into
+ # Windows-y semicolon-separated lists of *Windows* paths (which
+ # naturally contain a colon after the drive letter, so splitting by
+ # colons simply does not cut it).
+ #
+ # Detect semicolon-separated path list and handle them appropriately.
+
+ if ($ENV{GITPERLLIB} =~ /;/) {
+ return split(/;/, $ENV{GITPERLLIB});
+ }
+ return split(/:/, $ENV{GITPERLLIB});
+}
+use lib (gitperllib());
use strict;
use warnings;
use IO::File;
+use Git::Packet;
my $MAX_PACKET_CONTENT_SIZE = 65516;
my $log_file = shift @ARGV;
@@ -55,89 +73,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_txt_write("git-filter-server");
-packet_txt_write("version=2");
-packet_flush();
+packet_initialize("git-filter", 2);
-( 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";
+my %remote_caps = packet_read_and_check_capabilities("clean", "smudge", "delay");
+packet_check_and_write_capabilities(\%remote_caps, @capabilities);
-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 +120,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 +141,9 @@ while (1) {
( $done, $buffer ) = packet_txt_read();
}
+ if ( $done == -1 ) {
+ die "unexpected EOF after pathname '$pathname'";
+ }
my $input = "";
{
@@ -194,6 +154,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 +164,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 +179,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 +213,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..beb5927f77 100755
--- a/t/t0027-auto-crlf.sh
+++ b/t/t0027-auto-crlf.sh
@@ -43,19 +43,31 @@ create_gitattributes () {
} >.gitattributes
}
-create_NNO_files () {
+# Create 2 sets of files:
+# The NNO files are "Not NOrmalized in the repo. We use CRLF_mix_LF and store
+# it under different names for the different test cases, see ${pfx}
+# Depending on .gitattributes they are normalized at the next commit (or not)
+# The MIX files have different contents in the repo.
+# Depending on its contents, the "new safer autocrlf" may kick in.
+create_NNO_MIX_files () {
for crlf in false true input
do
for attr in "" auto text -text
do
for aeol in "" lf crlf
do
- pfx=NNO_attr_${attr}_aeol_${aeol}_${crlf}
+ pfx=NNO_attr_${attr}_aeol_${aeol}_${crlf} &&
cp CRLF_mix_LF ${pfx}_LF.txt &&
cp CRLF_mix_LF ${pfx}_CRLF.txt &&
cp CRLF_mix_LF ${pfx}_CRLF_mix_LF.txt &&
cp CRLF_mix_LF ${pfx}_LF_mix_CR.txt &&
- cp CRLF_mix_LF ${pfx}_CRLF_nul.txt
+ cp CRLF_mix_LF ${pfx}_CRLF_nul.txt &&
+ pfx=MIX_attr_${attr}_aeol_${aeol}_${crlf} &&
+ cp LF ${pfx}_LF.txt &&
+ cp CRLF ${pfx}_CRLF.txt &&
+ cp CRLF_mix_LF ${pfx}_CRLF_mix_LF.txt &&
+ cp LF_mix_CR ${pfx}_LF_mix_CR.txt &&
+ cp CRLF_nul ${pfx}_CRLF_nul.txt
done
done
done
@@ -136,6 +148,49 @@ commit_chk_wrnNNO () {
'
}
+# Commit a file with mixed line endings on top of different files
+# in the index. Check for warnings
+commit_MIX_chkwrn () {
+ attr=$1 ; shift
+ aeol=$1 ; shift
+ crlf=$1 ; shift
+ lfwarn=$1 ; shift
+ crlfwarn=$1 ; shift
+ lfmixcrlf=$1 ; shift
+ lfmixcr=$1 ; shift
+ crlfnul=$1 ; shift
+ pfx=MIX_attr_${attr}_aeol_${aeol}_${crlf}
+ #Commit file with CLRF_mix_LF on top of existing file
+ create_gitattributes "$attr" $aeol &&
+ for f in LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+ do
+ fname=${pfx}_$f.txt &&
+ cp CRLF_mix_LF $fname &&
+ printf Z >>"$fname" &&
+ git -c core.autocrlf=$crlf add $fname 2>"${pfx}_$f.err"
+ done
+
+ test_expect_success "commit file with mixed EOL onto LF crlf=$crlf attr=$attr" '
+ check_warning "$lfwarn" ${pfx}_LF.err
+ '
+ test_expect_success "commit file with mixed EOL onto CLRF attr=$attr aeol=$aeol crlf=$crlf" '
+ check_warning "$crlfwarn" ${pfx}_CRLF.err
+ '
+
+ test_expect_success "commit file with mixed EOL onto CRLF_mix_LF attr=$attr aeol=$aeol crlf=$crlf" '
+ check_warning "$lfmixcrlf" ${pfx}_CRLF_mix_LF.err
+ '
+
+ test_expect_success "commit file with mixed EOL onto LF_mix_cr attr=$attr aeol=$aeol crlf=$crlf " '
+ check_warning "$lfmixcr" ${pfx}_LF_mix_CR.err
+ '
+
+ test_expect_success "commit file with mixed EOL onto CRLF_nul attr=$attr aeol=$aeol crlf=$crlf" '
+ check_warning "$crlfnul" ${pfx}_CRLF_nul.err
+ '
+}
+
+
stats_ascii () {
case "$1" in
LF)
@@ -315,7 +370,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 &&
@@ -323,8 +378,8 @@ test_expect_success 'setup master' '
printf "\$Id: 0000000000000000000000000000000000000000 \$\r\nLINEONE\r\nLINETWO\rLINETHREE" >CRLF_mix_CR &&
printf "\$Id: 0000000000000000000000000000000000000000 \$\r\nLINEONEQ\r\nLINETWO\r\nLINETHREE" | q_to_nul >CRLF_nul &&
printf "\$Id: 0000000000000000000000000000000000000000 \$\nLINEONEQ\nLINETWO\nLINETHREE" | q_to_nul >LF_nul &&
- create_NNO_files CRLF_mix_LF CRLF_mix_LF CRLF_mix_LF CRLF_mix_LF CRLF_mix_LF &&
- git -c core.autocrlf=false add NNO_*.txt &&
+ create_NNO_MIX_files &&
+ git -c core.autocrlf=false add NNO_*.txt MIX_*.txt &&
git commit -m "mixed line endings" &&
test_tick
'
@@ -385,6 +440,18 @@ test_expect_success 'commit files attr=crlf' '
commit_check_warn input "crlf" "LF_CRLF" "" "LF_CRLF" "LF_CRLF" ""
'
+# Commit "CRLFmixLF" on top of these files already in the repo:
+# mixed mixed mixed mixed mixed
+# onto onto onto onto onto
+# attr LF CRLF CRLFmixLF LF_mix_CR CRLFNUL
+commit_MIX_chkwrn "" "" false "" "" "" "" ""
+commit_MIX_chkwrn "" "" true "LF_CRLF" "" "" "LF_CRLF" "LF_CRLF"
+commit_MIX_chkwrn "" "" input "CRLF_LF" "" "" "CRLF_LF" "CRLF_LF"
+
+commit_MIX_chkwrn "auto" "" false "$WAMIX" "" "" "$WAMIX" "$WAMIX"
+commit_MIX_chkwrn "auto" "" true "LF_CRLF" "" "" "LF_CRLF" "LF_CRLF"
+commit_MIX_chkwrn "auto" "" input "CRLF_LF" "" "" "CRLF_LF" "CRLF_LF"
+
# attr LF CRLF CRLFmixLF LF_mix_CR CRLFNUL
commit_chk_wrnNNO "" "" false "" "" "" "" ""
commit_chk_wrnNNO "" "" true LF_CRLF "" "" "" ""
diff --git a/t/t0205-gettext-poison.sh b/t/t0205-gettext-poison.sh
index 2361590d54..438e778d6a 100755
--- a/t/t0205-gettext-poison.sh
+++ b/t/t0205-gettext-poison.sh
@@ -7,10 +7,6 @@ test_description='Gettext Shell poison'
. ./lib-gettext.sh
-test_expect_success GETTEXT_POISON "sanity: \$GIT_INTERNAL_GETTEXT_SH_SCHEME is set (to $GIT_INTERNAL_GETTEXT_SH_SCHEME)" '
- test -n "$GIT_INTERNAL_GETTEXT_SH_SCHEME"
-'
-
test_expect_success GETTEXT_POISON 'sanity: $GIT_INTERNAL_GETTEXT_SH_SCHEME" is poison' '
test "$GIT_INTERNAL_GETTEXT_SH_SCHEME" = "poison"
'
diff --git a/t/t0302-credential-store.sh b/t/t0302-credential-store.sh
index 1d8d1f210b..d6b54e8c65 100755
--- a/t/t0302-credential-store.sh
+++ b/t/t0302-credential-store.sh
@@ -37,7 +37,7 @@ helper_test store
unset XDG_CONFIG_HOME
test_expect_success 'if custom xdg file exists, home and xdg files not created' '
- test_when_finished "rm -f $HOME/xdg/git/credentials" &&
+ test_when_finished "rm -f \"$HOME/xdg/git/credentials\"" &&
test -s "$HOME/xdg/git/credentials" &&
test_path_is_missing "$HOME/.git-credentials" &&
test_path_is_missing "$HOME/.config/git/credentials"
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/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/t1700-split-index.sh b/t/t1700-split-index.sh
index 22f69a410b..a66936fe9b 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 &&
@@ -400,4 +401,42 @@ done <<\EOF
0642 -rw-r---w-
EOF
+test_expect_success POSIXPERM,SANITY 'graceful handling when splitting index is not allowed' '
+ test_create_repo ro &&
+ (
+ cd ro &&
+ test_commit initial &&
+ git update-index --split-index &&
+ test -f .git/sharedindex.*
+ ) &&
+ cp ro/.git/index new-index &&
+ test_when_finished "chmod u+w ro/.git" &&
+ chmod u-w ro/.git &&
+ GIT_INDEX_FILE="$(pwd)/new-index" git -C ro update-index --split-index &&
+ chmod u+w ro/.git &&
+ rm ro/.git/sharedindex.* &&
+ GIT_INDEX_FILE=new-index git ls-files >actual &&
+ echo initial.t >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'writing split index with null sha1 does not write cache tree' '
+ git config core.splitIndex true &&
+ git config splitIndex.maxPercentChange 0 &&
+ git commit -m "commit" &&
+ {
+ git ls-tree HEAD &&
+ printf "160000 commit $_z40\\tbroken\\n"
+ } >broken-tree &&
+ echo "add broken entry" >msg &&
+
+ tree=$(git mktree <broken-tree) &&
+ test_tick &&
+ commit=$(git commit-tree $tree -p HEAD <msg) &&
+ git update-ref HEAD "$commit" &&
+ GIT_ALLOW_NULL_SHA1=1 git reset --hard &&
+ (test-dump-cache-tree >cache-tree.out || true) &&
+ test_line_count = 0 cache-tree.out
+'
+
test_done
diff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh
index fbb4ee9bb4..bb4f2e0c63 100755
--- a/t/t2020-checkout-detach.sh
+++ b/t/t2020-checkout-detach.sh
@@ -186,4 +186,127 @@ test_expect_success 'no advice given for explicit detached head state' '
test_cmp expect.no-advice actual
'
+# Detached HEAD tests for GIT_PRINT_SHA1_ELLIPSIS (new format)
+test_expect_success 'describe_detached_head prints no SHA-1 ellipsis when not asked to' "
+
+ # The first detach operation is more chatty than the following ones.
+ cat >1st_detach <<-'EOF' &&
+ Note: checking out 'HEAD^'.
+
+ You are in 'detached HEAD' state. You can look around, make experimental
+ changes and commit them, and you can discard any commits you make in this
+ state without impacting any branches by performing another checkout.
+
+ If you want to create a new branch to retain commits you create, you may
+ do so (now or later) by using -b with the checkout command again. Example:
+
+ git checkout -b <new-branch-name>
+
+ HEAD is now at 7c7cd714e262 three
+ EOF
+
+ # The remaining ones just show info about previous and current HEADs.
+ cat >2nd_detach <<-'EOF' &&
+ Previous HEAD position was 7c7cd714e262 three
+ HEAD is now at 139b20d8e6c5 two
+ EOF
+
+ cat >3rd_detach <<-'EOF' &&
+ Previous HEAD position was 139b20d8e6c5 two
+ HEAD is now at d79ce1670bdc one
+ EOF
+
+ reset &&
+ check_not_detached &&
+
+ # Various ways of *not* asking for ellipses
+
+ sane_unset GIT_PRINT_SHA1_ELLIPSIS &&
+ git -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&
+ check_detached &&
+ test_i18ncmp 1st_detach actual &&
+
+ GIT_PRINT_SHA1_ELLIPSIS="no" git -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&
+ check_detached &&
+ test_i18ncmp 2nd_detach actual &&
+
+ GIT_PRINT_SHA1_ELLIPSIS= git -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&
+ check_detached &&
+ test_i18ncmp 3rd_detach actual &&
+
+ sane_unset GIT_PRINT_SHA1_ELLIPSIS &&
+
+ # We only have four commits, but we can re-use them
+ reset &&
+ check_not_detached &&
+
+ # Make no mention of the env var at all
+ git -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&
+ check_detached &&
+ test_i18ncmp 1st_detach actual &&
+
+ GIT_PRINT_SHA1_ELLIPSIS='nope' &&
+ git -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&
+ check_detached &&
+ test_i18ncmp 2nd_detach actual &&
+
+ GIT_PRINT_SHA1_ELLIPSIS=nein &&
+ git -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&
+ check_detached &&
+ test_i18ncmp 3rd_detach actual &&
+
+ true
+"
+
+# Detached HEAD tests for GIT_PRINT_SHA1_ELLIPSIS (old format)
+test_expect_success 'describe_detached_head does print SHA-1 ellipsis when asked to' "
+
+ # The first detach operation is more chatty than the following ones.
+ cat >1st_detach <<-'EOF' &&
+ Note: checking out 'HEAD^'.
+
+ You are in 'detached HEAD' state. You can look around, make experimental
+ changes and commit them, and you can discard any commits you make in this
+ state without impacting any branches by performing another checkout.
+
+ If you want to create a new branch to retain commits you create, you may
+ do so (now or later) by using -b with the checkout command again. Example:
+
+ git checkout -b <new-branch-name>
+
+ HEAD is now at 7c7cd714e262... three
+ EOF
+
+ # The remaining ones just show info about previous and current HEADs.
+ cat >2nd_detach <<-'EOF' &&
+ Previous HEAD position was 7c7cd714e262... three
+ HEAD is now at 139b20d8e6c5... two
+ EOF
+
+ cat >3rd_detach <<-'EOF' &&
+ Previous HEAD position was 139b20d8e6c5... two
+ HEAD is now at d79ce1670bdc... one
+ EOF
+
+ reset &&
+ check_not_detached &&
+
+ # Various ways of asking for ellipses...
+ # The user can just use any kind of quoting (including none).
+
+ GIT_PRINT_SHA1_ELLIPSIS=yes git -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&
+ check_detached &&
+ test_i18ncmp 1st_detach actual &&
+
+ GIT_PRINT_SHA1_ELLIPSIS=Yes git -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&
+ check_detached &&
+ test_i18ncmp 2nd_detach actual &&
+
+ GIT_PRINT_SHA1_ELLIPSIS=YES git -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&
+ check_detached &&
+ test_i18ncmp 3rd_detach actual &&
+
+ true
+"
+
test_done
diff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh
index b5c47ac602..2b95944973 100755
--- a/t/t2025-worktree-add.sh
+++ b/t/t2025-worktree-add.sh
@@ -245,6 +245,12 @@ test_expect_success 'local clone from linked checkout' '
( cd here-clone && git fsck )
'
+test_expect_success 'local clone --shared from linked checkout' '
+ git -C bare worktree add --detach ../baretree &&
+ git clone --local --shared baretree bare-clone &&
+ grep /bare/ bare-clone/.git/objects/info/alternates
+'
+
test_expect_success '"add" worktree with --no-checkout' '
git worktree add --no-checkout -b swamp swamp &&
! test -e swamp/init.t &&
@@ -313,5 +319,164 @@ test_expect_success 'checkout a branch under bisect' '
test_expect_success 'rename a branch under bisect not allowed' '
test_must_fail git branch -M under-bisect bisect-with-new-name
'
+# Is branch "refs/heads/$1" set to pull from "$2/$3"?
+test_branch_upstream () {
+ printf "%s\n" "$2" "refs/heads/$3" >expect.upstream &&
+ {
+ git config "branch.$1.remote" &&
+ git config "branch.$1.merge"
+ } >actual.upstream &&
+ test_cmp expect.upstream actual.upstream
+}
+
+test_expect_success '--track sets up tracking' '
+ test_when_finished rm -rf track &&
+ git worktree add --track -b track track master &&
+ test_branch_upstream track . master
+'
+
+# setup remote repository $1 and repository $2 with $1 set up as
+# remote. The remote has two branches, master and foo.
+setup_remote_repo () {
+ git init $1 &&
+ (
+ cd $1 &&
+ test_commit $1_master &&
+ git checkout -b foo &&
+ test_commit upstream_foo
+ ) &&
+ git init $2 &&
+ (
+ cd $2 &&
+ test_commit $2_master &&
+ git remote add $1 ../$1 &&
+ git config remote.$1.fetch \
+ "refs/heads/*:refs/remotes/$1/*" &&
+ git fetch --all
+ )
+}
+
+test_expect_success '--no-track avoids setting up tracking' '
+ test_when_finished rm -rf repo_upstream repo_local foo &&
+ setup_remote_repo repo_upstream repo_local &&
+ (
+ cd repo_local &&
+ git worktree add --no-track -b foo ../foo repo_upstream/foo
+ ) &&
+ (
+ cd foo &&
+ test_must_fail git config "branch.foo.remote" &&
+ test_must_fail git config "branch.foo.merge" &&
+ test_cmp_rev refs/remotes/repo_upstream/foo refs/heads/foo
+ )
+'
+
+test_expect_success '"add" <path> <non-existent-branch> fails' '
+ test_must_fail git worktree add foo non-existent
+'
+
+test_expect_success '"add" <path> <branch> dwims' '
+ test_when_finished rm -rf repo_upstream repo_dwim foo &&
+ setup_remote_repo repo_upstream repo_dwim &&
+ git init repo_dwim &&
+ (
+ cd repo_dwim &&
+ git worktree add ../foo foo
+ ) &&
+ (
+ cd foo &&
+ test_branch_upstream foo repo_upstream foo &&
+ test_cmp_rev refs/remotes/repo_upstream/foo refs/heads/foo
+ )
+'
+
+test_expect_success 'git worktree add does not match remote' '
+ test_when_finished rm -rf repo_a repo_b foo &&
+ setup_remote_repo repo_a repo_b &&
+ (
+ cd repo_b &&
+ git worktree add ../foo
+ ) &&
+ (
+ cd foo &&
+ test_must_fail git config "branch.foo.remote" &&
+ test_must_fail git config "branch.foo.merge" &&
+ ! test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo
+ )
+'
+
+test_expect_success 'git worktree add --guess-remote sets up tracking' '
+ test_when_finished rm -rf repo_a repo_b foo &&
+ setup_remote_repo repo_a repo_b &&
+ (
+ cd repo_b &&
+ git worktree add --guess-remote ../foo
+ ) &&
+ (
+ cd foo &&
+ test_branch_upstream foo repo_a foo &&
+ test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo
+ )
+'
+
+test_expect_success 'git worktree add with worktree.guessRemote sets up tracking' '
+ test_when_finished rm -rf repo_a repo_b foo &&
+ setup_remote_repo repo_a repo_b &&
+ (
+ cd repo_b &&
+ git config worktree.guessRemote true &&
+ git worktree add ../foo
+ ) &&
+ (
+ cd foo &&
+ test_branch_upstream foo repo_a foo &&
+ test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo
+ )
+'
+
+test_expect_success 'git worktree --no-guess-remote option overrides config' '
+ test_when_finished rm -rf repo_a repo_b foo &&
+ setup_remote_repo repo_a repo_b &&
+ (
+ cd repo_b &&
+ git config worktree.guessRemote true &&
+ git worktree add --no-guess-remote ../foo
+ ) &&
+ (
+ cd foo &&
+ test_must_fail git config "branch.foo.remote" &&
+ test_must_fail git config "branch.foo.merge" &&
+ ! test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo
+ )
+'
+
+post_checkout_hook () {
+ test_when_finished "rm -f .git/hooks/post-checkout" &&
+ mkdir -p .git/hooks &&
+ write_script .git/hooks/post-checkout <<-\EOF
+ echo $* >hook.actual
+ EOF
+}
+
+test_expect_success '"add" invokes post-checkout hook (branch)' '
+ post_checkout_hook &&
+ printf "%s %s 1\n" $_z40 $(git rev-parse HEAD) >hook.expect &&
+ git worktree add gumby &&
+ test_cmp hook.expect hook.actual
+'
+
+test_expect_success '"add" invokes post-checkout hook (detached)' '
+ post_checkout_hook &&
+ printf "%s %s 1\n" $_z40 $(git rev-parse HEAD) >hook.expect &&
+ git worktree add --detach grumpy &&
+ test_cmp hook.expect hook.actual
+'
+
+test_expect_success '"add --no-checkout" suppresses post-checkout hook' '
+ post_checkout_hook &&
+ rm -f hook.actual &&
+ git worktree add --no-checkout gloopy &&
+ test_path_is_missing hook.actual
+'
test_done
diff --git a/t/t2203-add-intent.sh b/t/t2203-add-intent.sh
index 1bdf38e80d..78236dc7d8 100755
--- a/t/t2203-add-intent.sh
+++ b/t/t2203-add-intent.sh
@@ -25,6 +25,18 @@ test_expect_success 'git status' '
test_cmp expect actual
'
+test_expect_success 'git status with porcelain v2' '
+ git status --porcelain=v2 | grep -v "^?" >actual &&
+ nam1=d00491fd7e5bb6fa28c517a0bb32b8b506539d4d &&
+ nam2=ce013625030ba8dba906f756967f9e9ca394464a &&
+ cat >expect <<-EOF &&
+ 1 DA N... 100644 000000 100644 $nam1 $_z40 1.t
+ 1 A. N... 000000 100644 100644 $_z40 $nam2 elif
+ 1 .A N... 000000 000000 100644 $_z40 $_z40 file
+ EOF
+ test_cmp expect actual
+'
+
test_expect_success 'check result of "add -N"' '
git ls-files -s file >actual &&
empty=$(git hash-object --stdin </dev/null) &&
@@ -150,5 +162,65 @@ test_expect_success 'commit: ita entries ignored in empty commit check' '
)
'
+test_expect_success 'rename detection finds the right names' '
+ git init rename-detection &&
+ (
+ cd rename-detection &&
+ echo contents >first &&
+ git add first &&
+ git commit -m first &&
+ mv first third &&
+ git add -N third &&
+
+ git status | grep -v "^?" >actual.1 &&
+ test_i18ngrep "renamed: *first -> third" actual.1 &&
+
+ git status --porcelain | grep -v "^?" >actual.2 &&
+ cat >expected.2 <<-\EOF &&
+ R first -> third
+ EOF
+ test_cmp expected.2 actual.2 &&
+
+ hash=12f00e90b6ef79117ce6e650416b8cf517099b78 &&
+ git status --porcelain=v2 | grep -v "^?" >actual.3 &&
+ cat >expected.3 <<-EOF &&
+ 2 .R N... 100644 100644 100644 $hash $hash R100 third first
+ EOF
+ test_cmp expected.3 actual.3
+ )
+'
+
+test_expect_success 'double rename detection in status' '
+ git init rename-detection-2 &&
+ (
+ cd rename-detection-2 &&
+ echo contents >first &&
+ git add first &&
+ git commit -m first &&
+ git mv first second &&
+ mv second third &&
+ git add -N third &&
+
+ git status | grep -v "^?" >actual.1 &&
+ test_i18ngrep "renamed: *first -> second" actual.1 &&
+ test_i18ngrep "renamed: *second -> third" actual.1 &&
+
+ git status --porcelain | grep -v "^?" >actual.2 &&
+ cat >expected.2 <<-\EOF &&
+ R first -> second
+ R second -> third
+ EOF
+ test_cmp expected.2 actual.2 &&
+
+ hash=12f00e90b6ef79117ce6e650416b8cf517099b78 &&
+ git status --porcelain=v2 | grep -v "^?" >actual.3 &&
+ cat >expected.3 <<-EOF &&
+ 2 R. N... 100644 100644 100644 $hash $hash R100 second first
+ 2 .R N... 100644 100644 100644 $hash $hash R100 third second
+ EOF
+ test_cmp expected.3 actual.3
+ )
+'
+
test_done
diff --git a/t/t3030-merge-recursive.sh b/t/t3030-merge-recursive.sh
index 9a893b5fe7..cdc38fe5d1 100755
--- a/t/t3030-merge-recursive.sh
+++ b/t/t3030-merge-recursive.sh
@@ -678,4 +678,54 @@ test_expect_success 'merge-recursive remembers the names of all base trees' '
test_cmp expect actual
'
+test_expect_success 'merge-recursive internal merge resolves to the sameness' '
+ git reset --hard HEAD &&
+
+ # We are going to create a history leading to two criss-cross
+ # branches A and B. The common ancestor at the bottom, O0,
+ # has two child commits O1 and O2, both of which will be merge
+ # base between A and B, like so:
+ #
+ # O1---A
+ # / \ /
+ # O0 .
+ # \ / \
+ # O2---B
+ #
+ # The recently added "check to see if the index is different from
+ # the tree into which something else is getting merged" check must
+ # NOT kick in when an inner merge between O1 and O2 is made. Both
+ # O1 and O2 happen to have the same tree as O0 in this test to
+ # trigger the bug---whether the inner merge is made by merging O2
+ # into O1 or O1 into O2, their common ancestor O0 and the branch
+ # being merged have the same tree. We should not trigger the "is
+ # the index dirty?" check in this case.
+
+ echo "zero" >file &&
+ git add file &&
+ test_tick &&
+ git commit -m "O0" &&
+ O0=$(git rev-parse HEAD) &&
+
+ test_tick &&
+ git commit --allow-empty -m "O1" &&
+ O1=$(git rev-parse HEAD) &&
+
+ git reset --hard $O0 &&
+ test_tick &&
+ git commit --allow-empty -m "O2" &&
+ O2=$(git rev-parse HEAD) &&
+
+ test_tick &&
+ git merge -s ours $O1 &&
+ B=$(git rev-parse HEAD) &&
+
+ git reset --hard $O1 &&
+ test_tick &&
+ git merge -s ours $O2 &&
+ A=$(git rev-parse HEAD) &&
+
+ git merge $B
+'
+
test_done
diff --git a/t/t3040-subprojects-basic.sh b/t/t3040-subprojects-basic.sh
index 0a4ff6d824..b81eb5fd6f 100755
--- a/t/t3040-subprojects-basic.sh
+++ b/t/t3040-subprojects-basic.sh
@@ -19,7 +19,7 @@ test_expect_success 'setup: create subprojects' '
git update-index --add sub1 &&
git add sub2 &&
git commit -q -m "subprojects added" &&
- git diff-tree --abbrev=5 HEAD^ HEAD |cut -d" " -f-3,5- >current &&
+ GIT_PRINT_SHA1_ELLIPSIS="yes" git diff-tree --abbrev=5 HEAD^ HEAD |cut -d" " -f-3,5- >current &&
git branch save HEAD &&
cat >expected <<-\EOF &&
:000000 160000 00000... A sub1
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 6a82d1ed87..481a350090 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -1260,6 +1260,28 @@ test_expect_success 'rebase -i respects rebase.missingCommitsCheck = error' '
test B = $(git cat-file commit HEAD^ | sed -ne \$p)
'
+test_expect_success 'respects rebase.abbreviateCommands with fixup, squash and exec' '
+ rebase_setup_and_clean abbrevcmd &&
+ test_commit "first" file1.txt "first line" first &&
+ test_commit "second" file1.txt "another line" second &&
+ test_commit "fixup! first" file2.txt "first line again" first_fixup &&
+ test_commit "squash! second" file1.txt "another line here" second_squash &&
+ cat >expected <<-EOF &&
+ p $(git rev-list --abbrev-commit -1 first) first
+ f $(git rev-list --abbrev-commit -1 first_fixup) fixup! first
+ x git show HEAD
+ p $(git rev-list --abbrev-commit -1 second) second
+ s $(git rev-list --abbrev-commit -1 second_squash) squash! second
+ x git show HEAD
+ EOF
+ git checkout abbrevcmd &&
+ set_cat_todo_editor &&
+ test_config rebase.abbreviateCommands true &&
+ test_must_fail git rebase -i --exec "git show HEAD" \
+ --autosquash master >actual &&
+ test_cmp expected actual
+'
+
test_expect_success 'static check of bad command' '
rebase_setup_and_clean bad-cmd &&
set_fake_editor &&
diff --git a/t/t3418-rebase-continue.sh b/t/t3418-rebase-continue.sh
index fcfdd197bd..7c91a85f43 100755
--- a/t/t3418-rebase-continue.sh
+++ b/t/t3418-rebase-continue.sh
@@ -74,6 +74,20 @@ test_expect_success 'rebase --continue remembers merge strategy and options' '
test -f funny.was.run
'
+test_expect_success 'rebase passes merge strategy options correctly' '
+ rm -fr .git/rebase-* &&
+ git reset --hard commit-new-file-F3-on-topic-branch &&
+ test_commit theirs-to-merge &&
+ git reset --hard HEAD^ &&
+ test_commit some-commit &&
+ test_tick &&
+ git merge --no-ff theirs-to-merge &&
+ FAKE_LINES="1 edit 2 3" git rebase -i -f -p -m \
+ -s recursive --strategy-option=theirs HEAD~2 &&
+ test_commit force-change &&
+ git rebase --continue
+'
+
test_expect_success 'setup rerere database' '
rm -fr .git/rebase-* &&
git reset --hard commit-new-file-F3-on-topic-branch &&
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index ab5500db44..46f15169f5 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -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/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index a49c12c79b..058698df6a 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -493,4 +493,52 @@ test_expect_success 'add -p works even with color.ui=always' '
test_cmp expect actual
'
+test_expect_success 'setup different kinds of dirty submodules' '
+ test_create_repo for-submodules &&
+ (
+ cd for-submodules &&
+ test_commit initial &&
+ test_create_repo dirty-head &&
+ (
+ cd dirty-head &&
+ test_commit initial
+ ) &&
+ cp -R dirty-head dirty-otherwise &&
+ cp -R dirty-head dirty-both-ways &&
+ git add dirty-head &&
+ git add dirty-otherwise dirty-both-ways &&
+ git commit -m initial &&
+
+ cd dirty-head &&
+ test_commit updated &&
+ cd ../dirty-both-ways &&
+ test_commit updated &&
+ echo dirty >>initial &&
+ : >untracked &&
+ cd ../dirty-otherwise &&
+ echo dirty >>initial &&
+ : >untracked
+ ) &&
+ git -C for-submodules diff-files --name-only >actual &&
+ cat >expected <<-\EOF &&
+ dirty-both-ways
+ dirty-head
+ dirty-otherwise
+ EOF
+ test_cmp expected actual &&
+ git -C for-submodules diff-files --name-only --ignore-submodules=dirty >actual &&
+ cat >expected <<-\EOF &&
+ dirty-both-ways
+ dirty-head
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'status ignores dirty submodules (except HEAD)' '
+ git -C for-submodules add -i </dev/null >output &&
+ grep dirty-head output &&
+ grep dirty-both-ways output &&
+ ! grep dirty-otherwise output
+'
+
test_done
diff --git a/t/t3900-i18n-commit.sh b/t/t3900-i18n-commit.sh
index 3b94283e35..b92ff95977 100755
--- a/t/t3900-i18n-commit.sh
+++ b/t/t3900-i18n-commit.sh
@@ -40,7 +40,7 @@ test_expect_success 'UTF-16 refused because of NULs' '
'
test_expect_success 'UTF-8 invalid characters refused' '
- test_when_finished "rm -f $HOME/stderr $HOME/invalid" &&
+ test_when_finished "rm -f \"\$HOME/stderr\" \"\$HOME/invalid\"" &&
echo "UTF-8 characters" >F &&
printf "Commit message\n\nInvalid surrogate:\355\240\200\n" \
>"$HOME/invalid" &&
@@ -49,7 +49,7 @@ test_expect_success 'UTF-8 invalid characters refused' '
'
test_expect_success 'UTF-8 overlong sequences rejected' '
- test_when_finished "rm -f $HOME/stderr $HOME/invalid" &&
+ test_when_finished "rm -f \"\$HOME/stderr\" \"\$HOME/invalid\"" &&
rm -f "$HOME/stderr" "$HOME/invalid" &&
echo "UTF-8 overlong" >F &&
printf "\340\202\251ommit message\n\nThis is not a space:\300\240\n" \
@@ -59,7 +59,7 @@ test_expect_success 'UTF-8 overlong sequences rejected' '
'
test_expect_success 'UTF-8 non-characters refused' '
- test_when_finished "rm -f $HOME/stderr $HOME/invalid" &&
+ test_when_finished "rm -f \"\$HOME/stderr\" \"\$HOME/invalid\"" &&
echo "UTF-8 non-character 1" >F &&
printf "Commit message\n\nNon-character:\364\217\277\276\n" \
>"$HOME/invalid" &&
@@ -68,7 +68,7 @@ test_expect_success 'UTF-8 non-characters refused' '
'
test_expect_success 'UTF-8 non-characters refused' '
- test_when_finished "rm -f $HOME/stderr $HOME/invalid" &&
+ test_when_finished "rm -f \"\$HOME/stderr\" \"\$HOME/invalid\"" &&
echo "UTF-8 non-character 2." >F &&
printf "Commit message\n\nNon-character:\357\267\220\n" \
>"$HOME/invalid" &&
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index 3b1ac1971a..aefde7b172 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -804,6 +804,99 @@ test_expect_success 'push -m shows right message' '
test_cmp expect actual
'
+test_expect_success 'push -m also works without space' '
+ >foo &&
+ git add foo &&
+ git stash push -m"unspaced test message" &&
+ echo "stash@{0}: On master: unspaced test message" >expect &&
+ git stash list -1 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'store -m foo shows right message' '
+ git stash clear &&
+ git reset --hard &&
+ echo quux >bazzy &&
+ git add bazzy &&
+ STASH_ID=$(git stash create) &&
+ git stash store -m "store m" $STASH_ID &&
+ echo "stash@{0}: store m" >expect &&
+ git stash list -1 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'store -mfoo shows right message' '
+ git stash clear &&
+ git reset --hard &&
+ echo quux >bazzy &&
+ git add bazzy &&
+ STASH_ID=$(git stash create) &&
+ git stash store -m"store mfoo" $STASH_ID &&
+ echo "stash@{0}: store mfoo" >expect &&
+ git stash list -1 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'store --message=foo shows right message' '
+ git stash clear &&
+ git reset --hard &&
+ echo quux >bazzy &&
+ git add bazzy &&
+ STASH_ID=$(git stash create) &&
+ git stash store --message="store message=foo" $STASH_ID &&
+ echo "stash@{0}: store message=foo" >expect &&
+ git stash list -1 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'store --message foo shows right message' '
+ git stash clear &&
+ git reset --hard &&
+ echo quux >bazzy &&
+ git add bazzy &&
+ STASH_ID=$(git stash create) &&
+ git stash store --message "store message foo" $STASH_ID &&
+ echo "stash@{0}: store message foo" >expect &&
+ git stash list -1 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'push -mfoo uses right message' '
+ >foo &&
+ git add foo &&
+ git stash push -m"test mfoo" &&
+ echo "stash@{0}: On master: test mfoo" >expect &&
+ git stash list -1 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'push --message foo is synonym for -mfoo' '
+ >foo &&
+ git add foo &&
+ git stash push --message "test message foo" &&
+ echo "stash@{0}: On master: test message foo" >expect &&
+ git stash list -1 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'push --message=foo is synonym for -mfoo' '
+ >foo &&
+ git add foo &&
+ git stash push --message="test message=foo" &&
+ echo "stash@{0}: On master: test message=foo" >expect &&
+ git stash list -1 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'push -m shows right message' '
+ >foo &&
+ git add foo &&
+ git stash push -m "test m foo" &&
+ echo "stash@{0}: On master: test m foo" >expect &&
+ git stash list -1 >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'create stores correct message' '
>foo &&
git add foo &&
@@ -971,4 +1064,36 @@ test_expect_success 'stash -k -- <pathspec> leaves unstaged files intact' '
test foo,bar = $(cat foo),$(cat bar)
'
+test_expect_success 'stash -- <subdir> leaves untracked files in subdir intact' '
+ git reset &&
+ >subdir/untracked &&
+ >subdir/tracked1 &&
+ >subdir/tracked2 &&
+ git add subdir/tracked* &&
+ git stash -- subdir/ &&
+ test_path_is_missing subdir/tracked1 &&
+ test_path_is_missing subdir/tracked2 &&
+ test_path_is_file subdir/untracked &&
+ git stash pop &&
+ test_path_is_file subdir/tracked1 &&
+ test_path_is_file subdir/tracked2 &&
+ test_path_is_file subdir/untracked
+'
+
+test_expect_success 'stash -- <subdir> works with binary files' '
+ git reset &&
+ >subdir/untracked &&
+ >subdir/tracked &&
+ cp "$TEST_DIRECTORY"/test-binary-1.png subdir/tracked-binary &&
+ git add subdir/tracked* &&
+ git stash -- subdir/ &&
+ test_path_is_missing subdir/tracked &&
+ test_path_is_missing subdir/tracked-binary &&
+ test_path_is_file subdir/untracked &&
+ git stash pop &&
+ test_path_is_file subdir/tracked &&
+ test_path_is_file subdir/tracked-binary &&
+ test_path_is_file subdir/untracked
+'
+
test_done
diff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh
index 0d1fa45d25..a07816d560 100755
--- a/t/t4001-diff-rename.sh
+++ b/t/t4001-diff-rename.sh
@@ -134,11 +134,15 @@ test_expect_success 'favour same basenames over different ones' '
git rm path1 &&
mkdir subdir &&
git mv another-path subdir/path1 &&
- git status | test_i18ngrep "renamed: .*path1 -> subdir/path1"'
+ git status >out &&
+ test_i18ngrep "renamed: .*path1 -> subdir/path1" out
+'
test_expect_success 'favour same basenames even with minor differences' '
git show HEAD:path1 | sed "s/15/16/" > subdir/path1 &&
- git status | test_i18ngrep "renamed: .*path1 -> subdir/path1"'
+ git status >out &&
+ test_i18ngrep "renamed: .*path1 -> subdir/path1" out
+'
test_expect_success 'two files with same basename and same content' '
git reset --hard &&
@@ -148,7 +152,8 @@ test_expect_success 'two files with same basename and same content' '
git add dir &&
git commit -m 2 &&
git mv dir other-dir &&
- git status | test_i18ngrep "renamed: .*dir/A/file -> other-dir/A/file"
+ git status >out &&
+ test_i18ngrep "renamed: .*dir/A/file -> other-dir/A/file" out
'
test_expect_success 'setup for many rename source candidates' '
@@ -230,4 +235,19 @@ test_expect_success 'rename pretty print common prefix and suffix overlap' '
test_i18ngrep " d/f/{ => f}/e " output
'
+test_expect_success 'diff-tree -l0 defaults to a big rename limit, not zero' '
+ test_write_lines line1 line2 line3 >myfile &&
+ git add myfile &&
+ git commit -m x &&
+
+ test_write_lines line1 line2 line4 >myotherfile &&
+ git rm myfile &&
+ git add myotherfile &&
+ git commit -m x &&
+
+ git diff-tree -M -l0 HEAD HEAD^ >actual &&
+ # Verify that a rename from myotherfile to myfile was detected
+ grep "myotherfile.*myfile" actual
+'
+
test_done
diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh
index c515e3e53f..f10798b2df 100755
--- a/t/t4013-diff-various.sh
+++ b/t/t4013-diff-various.sh
@@ -118,20 +118,37 @@ test_expect_success setup '
EOF
V=$(git version | sed -e 's/^git version //' -e 's/\./\\./g')
-while read cmd
+while read magic cmd
do
- case "$cmd" in
- '' | '#'*) continue ;;
+ case "$magic" in
+ '' | '#'*)
+ continue ;;
+ :*)
+ magic=${magic#:}
+ label="$magic-$cmd"
+ case "$magic" in
+ noellipses) ;;
+ *)
+ die "bug in t4103: unknown magic $magic" ;;
+ esac ;;
+ *)
+ cmd="$magic $cmd" magic=
+ label="$cmd" ;;
esac
- test=$(echo "$cmd" | sed -e 's|[/ ][/ ]*|_|g')
+ test=$(echo "$label" | sed -e 's|[/ ][/ ]*|_|g')
pfx=$(printf "%04d" $test_count)
expect="$TEST_DIRECTORY/t4013/diff.$test"
actual="$pfx-diff.$test"
- test_expect_success "git $cmd" '
+ test_expect_success "git $cmd # magic is ${magic:-"(not used)"}" '
{
- echo "\$ git $cmd"
- git $cmd |
+ echo "$ git $cmd"
+ case "$magic" in
+ "")
+ GIT_PRINT_SHA1_ELLIPSIS=yes git $cmd ;;
+ noellipses)
+ git $cmd ;;
+ esac |
sed -e "s/^\\(-*\\)$V\\(-*\\)\$/\\1g-i-t--v-e-r-s-i-o-n\2/" \
-e "s/^\\(.*mixed; boundary=\"-*\\)$V\\(-*\\)\"\$/\\1g-i-t--v-e-r-s-i-o-n\2\"/"
echo "\$"
@@ -158,9 +175,12 @@ diff-tree -r --abbrev initial
diff-tree -r --abbrev=4 initial
diff-tree --root initial
diff-tree --root --abbrev initial
+:noellipses diff-tree --root --abbrev initial
diff-tree --root -r initial
diff-tree --root -r --abbrev initial
+:noellipses diff-tree --root -r --abbrev initial
diff-tree --root -r --abbrev=4 initial
+:noellipses diff-tree --root -r --abbrev=4 initial
diff-tree -p initial
diff-tree --root -p initial
diff-tree --patch-with-stat initial
@@ -209,6 +229,7 @@ diff-tree -p master
diff-tree -p -m master
diff-tree -c master
diff-tree -c --abbrev master
+:noellipses diff-tree -c --abbrev master
diff-tree --cc master
# stat only should show the diffstat with the first parent
diff-tree -c --stat master
@@ -255,8 +276,10 @@ rev-list --parents HEAD
rev-list --children HEAD
whatchanged master
+:noellipses whatchanged master
whatchanged -p master
whatchanged --root master
+:noellipses whatchanged --root master
whatchanged --root -p master
whatchanged --patch-with-stat master
whatchanged --root --patch-with-stat master
@@ -266,6 +289,7 @@ whatchanged --root -c --patch-with-stat --summary master
# improved by Timo's patch
whatchanged --root --cc --patch-with-stat --summary master
whatchanged -SF master
+:noellipses whatchanged -SF master
whatchanged -SF -p master
log --patch-with-stat master -- dir/
@@ -284,6 +308,7 @@ show --stat side
show --stat --summary side
show --patch-with-stat side
show --patch-with-raw side
+:noellipses show --patch-with-raw side
show --patch-with-stat --summary side
format-patch --stdout initial..side
@@ -311,8 +336,10 @@ diff -r --stat initial..side
diff initial..side
diff --patch-with-stat initial..side
diff --patch-with-raw initial..side
+:noellipses diff --patch-with-raw initial..side
diff --patch-with-stat -r initial..side
diff --patch-with-raw -r initial..side
+:noellipses diff --patch-with-raw -r initial..side
diff --name-status dir2 dir
diff --no-index --name-status dir2 dir
diff --no-index --name-status -- dir2 dir
@@ -325,10 +352,14 @@ diff --dirstat initial rearrange
diff --dirstat-by-file initial rearrange
# No-index --abbrev and --no-abbrev
diff --raw initial
+:noellipses diff --raw initial
diff --raw --abbrev=4 initial
+:noellipses diff --raw --abbrev=4 initial
diff --raw --no-abbrev initial
diff --no-index --raw dir2 dir
+:noellipses diff --no-index --raw dir2 dir
diff --no-index --raw --abbrev=4 dir2 dir
+:noellipses diff --no-index --raw --abbrev=4 dir2 dir
diff --no-index --raw --no-abbrev dir2 dir
EOF
diff --git a/t/t4013/diff.noellipses-diff-tree_--root_--abbrev_initial b/t/t4013/diff.noellipses-diff-tree_--root_--abbrev_initial
new file mode 100644
index 0000000000..4bdad4072e
--- /dev/null
+++ b/t/t4013/diff.noellipses-diff-tree_--root_--abbrev_initial
@@ -0,0 +1,6 @@
+$ git diff-tree --root --abbrev initial
+444ac553ac7612cc88969031b02b3767fb8a353a
+:000000 040000 0000000 da7a33f A dir
+:000000 100644 0000000 01e79c3 A file0
+:000000 100644 0000000 01e79c3 A file2
+$
diff --git a/t/t4013/diff.noellipses-diff-tree_--root_-r_--abbrev=4_initial b/t/t4013/diff.noellipses-diff-tree_--root_-r_--abbrev=4_initial
new file mode 100644
index 0000000000..26fbfeb177
--- /dev/null
+++ b/t/t4013/diff.noellipses-diff-tree_--root_-r_--abbrev=4_initial
@@ -0,0 +1,6 @@
+$ git diff-tree --root -r --abbrev=4 initial
+444ac553ac7612cc88969031b02b3767fb8a353a
+:000000 100644 0000 35d2 A dir/sub
+:000000 100644 0000 01e7 A file0
+:000000 100644 0000 01e7 A file2
+$
diff --git a/t/t4013/diff.noellipses-diff-tree_--root_-r_--abbrev_initial b/t/t4013/diff.noellipses-diff-tree_--root_-r_--abbrev_initial
new file mode 100644
index 0000000000..2ac8561191
--- /dev/null
+++ b/t/t4013/diff.noellipses-diff-tree_--root_-r_--abbrev_initial
@@ -0,0 +1,6 @@
+$ git diff-tree --root -r --abbrev initial
+444ac553ac7612cc88969031b02b3767fb8a353a
+:000000 100644 0000000 35d242b A dir/sub
+:000000 100644 0000000 01e79c3 A file0
+:000000 100644 0000000 01e79c3 A file2
+$
diff --git a/t/t4013/diff.noellipses-diff-tree_-c_--abbrev_master b/t/t4013/diff.noellipses-diff-tree_-c_--abbrev_master
new file mode 100644
index 0000000000..bb80f013b3
--- /dev/null
+++ b/t/t4013/diff.noellipses-diff-tree_-c_--abbrev_master
@@ -0,0 +1,5 @@
+$ git diff-tree -c --abbrev master
+59d314ad6f356dd08601a4cd5e530381da3e3c64
+::100644 100644 100644 cead32e 7289e35 992913c MM dir/sub
+::100644 100644 100644 b414108 f4615da 10a8a9f MM file0
+$
diff --git a/t/t4013/diff.noellipses-diff_--no-index_--raw_--abbrev=4_dir2_dir b/t/t4013/diff.noellipses-diff_--no-index_--raw_--abbrev=4_dir2_dir
new file mode 100644
index 0000000000..41b7baf0a5
--- /dev/null
+++ b/t/t4013/diff.noellipses-diff_--no-index_--raw_--abbrev=4_dir2_dir
@@ -0,0 +1,3 @@
+$ git diff --no-index --raw --abbrev=4 dir2 dir
+:000000 100644 0000 0000 A dir/sub
+$
diff --git a/t/t4013/diff.noellipses-diff_--no-index_--raw_dir2_dir b/t/t4013/diff.noellipses-diff_--no-index_--raw_dir2_dir
new file mode 100644
index 0000000000..0cf3a3efea
--- /dev/null
+++ b/t/t4013/diff.noellipses-diff_--no-index_--raw_dir2_dir
@@ -0,0 +1,3 @@
+$ git diff --no-index --raw dir2 dir
+:000000 100644 0000000 0000000 A dir/sub
+$
diff --git a/t/t4013/diff.noellipses-diff_--patch-with-raw_-r_initial..side b/t/t4013/diff.noellipses-diff_--patch-with-raw_-r_initial..side
new file mode 100644
index 0000000000..8d1f1e3721
--- /dev/null
+++ b/t/t4013/diff.noellipses-diff_--patch-with-raw_-r_initial..side
@@ -0,0 +1,36 @@
+$ git diff --patch-with-raw -r initial..side
+:100644 100644 35d242b 7289e35 M dir/sub
+:100644 100644 01e79c3 f4615da M file0
+:000000 100644 0000000 7289e35 A file3
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+$
diff --git a/t/t4013/diff.noellipses-diff_--patch-with-raw_initial..side b/t/t4013/diff.noellipses-diff_--patch-with-raw_initial..side
new file mode 100644
index 0000000000..50d8aee4f7
--- /dev/null
+++ b/t/t4013/diff.noellipses-diff_--patch-with-raw_initial..side
@@ -0,0 +1,36 @@
+$ git diff --patch-with-raw initial..side
+:100644 100644 35d242b 7289e35 M dir/sub
+:100644 100644 01e79c3 f4615da M file0
+:000000 100644 0000000 7289e35 A file3
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+$
diff --git a/t/t4013/diff.noellipses-diff_--raw_--abbrev=4_initial b/t/t4013/diff.noellipses-diff_--raw_--abbrev=4_initial
new file mode 100644
index 0000000000..8ae44d6c83
--- /dev/null
+++ b/t/t4013/diff.noellipses-diff_--raw_--abbrev=4_initial
@@ -0,0 +1,6 @@
+$ git diff --raw --abbrev=4 initial
+:100644 100644 35d2 9929 M dir/sub
+:100644 100644 01e7 10a8 M file0
+:000000 100644 0000 b1e6 A file1
+:100644 000000 01e7 0000 D file2
+$
diff --git a/t/t4013/diff.noellipses-diff_--raw_initial b/t/t4013/diff.noellipses-diff_--raw_initial
new file mode 100644
index 0000000000..0175bfb281
--- /dev/null
+++ b/t/t4013/diff.noellipses-diff_--raw_initial
@@ -0,0 +1,6 @@
+$ git diff --raw initial
+:100644 100644 35d242b 992913c M dir/sub
+:100644 100644 01e79c3 10a8a9f M file0
+:000000 100644 0000000 b1e6722 A file1
+:100644 000000 01e79c3 0000000 D file2
+$
diff --git a/t/t4013/diff.noellipses-show_--patch-with-raw_side b/t/t4013/diff.noellipses-show_--patch-with-raw_side
new file mode 100644
index 0000000000..32fed3d576
--- /dev/null
+++ b/t/t4013/diff.noellipses-show_--patch-with-raw_side
@@ -0,0 +1,42 @@
+$ git show --patch-with-raw side
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+
+:100644 100644 35d242b 7289e35 M dir/sub
+:100644 100644 01e79c3 f4615da M file0
+:000000 100644 0000000 7289e35 A file3
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+$
diff --git a/t/t4013/diff.noellipses-whatchanged_--root_master b/t/t4013/diff.noellipses-whatchanged_--root_master
new file mode 100644
index 0000000000..c2cfd4e729
--- /dev/null
+++ b/t/t4013/diff.noellipses-whatchanged_--root_master
@@ -0,0 +1,42 @@
+$ git whatchanged --root master
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+
+:100644 100644 35d242b 7289e35 M dir/sub
+:100644 100644 01e79c3 f4615da M file0
+:000000 100644 0000000 7289e35 A file3
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+
+:100644 100644 8422d40 cead32e M dir/sub
+:000000 100644 0000000 b1e6722 A file1
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+
+:100644 100644 35d242b 8422d40 M dir/sub
+:100644 100644 01e79c3 b414108 M file0
+:100644 000000 01e79c3 0000000 D file2
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+
+:000000 100644 0000000 35d242b A dir/sub
+:000000 100644 0000000 01e79c3 A file0
+:000000 100644 0000000 01e79c3 A file2
+$
diff --git a/t/t4013/diff.noellipses-whatchanged_-SF_master b/t/t4013/diff.noellipses-whatchanged_-SF_master
new file mode 100644
index 0000000000..b36ce5886e
--- /dev/null
+++ b/t/t4013/diff.noellipses-whatchanged_-SF_master
@@ -0,0 +1,9 @@
+$ git whatchanged -SF master
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+
+:100644 100644 8422d40 cead32e M dir/sub
+$
diff --git a/t/t4013/diff.noellipses-whatchanged_master b/t/t4013/diff.noellipses-whatchanged_master
new file mode 100644
index 0000000000..55e500f2ed
--- /dev/null
+++ b/t/t4013/diff.noellipses-whatchanged_master
@@ -0,0 +1,32 @@
+$ git whatchanged master
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+
+:100644 100644 35d242b 7289e35 M dir/sub
+:100644 100644 01e79c3 f4615da M file0
+:000000 100644 0000000 7289e35 A file3
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+
+:100644 100644 8422d40 cead32e M dir/sub
+:000000 100644 0000000 b1e6722 A file1
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+
+:100644 100644 35d242b 8422d40 M dir/sub
+:100644 100644 01e79c3 b414108 M file0
+:100644 000000 01e79c3 0000000 D file2
+$
diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh
index 6c9a93b734..17df491a3a 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
'
@@ -608,6 +636,23 @@ test_expect_success 'check with space before tab in indent (diff-tree)' '
test_must_fail git diff-tree --check HEAD^ HEAD
'
+test_expect_success 'check with ignored trailing whitespace attr (diff-tree)' '
+ test_when_finished "git reset --hard HEAD^" &&
+
+ # create a whitespace error that should be ignored
+ echo "* -whitespace" >.gitattributes &&
+ git add .gitattributes &&
+ echo "foo(); " >x &&
+ git add x &&
+ git commit -m "add trailing space" &&
+
+ # with a worktree diff-tree ignores the whitespace error
+ git diff-tree --root --check HEAD &&
+
+ # without a worktree diff-tree still ignores the whitespace error
+ git -C .git diff-tree --root --check HEAD
+'
+
test_expect_success 'check trailing whitespace (trailing-space: off)' '
git config core.whitespace "-trailing-space" &&
echo "foo (); " >x &&
diff --git a/t/t4045-diff-relative.sh b/t/t4045-diff-relative.sh
index 3950f5034d..6471a68701 100755
--- a/t/t4045-diff-relative.sh
+++ b/t/t4045-diff-relative.sh
@@ -12,62 +12,76 @@ test_expect_success 'setup' '
git commit -m one
'
-check_diff() {
-expect=$1; shift
-cat >expected <<EOF
-diff --git a/$expect b/$expect
-new file mode 100644
-index 0000000..25c05ef
---- /dev/null
-+++ b/$expect
-@@ -0,0 +1 @@
-+other content
-EOF
-test_expect_success "-p $*" "
- git diff -p $* HEAD^ >actual &&
- test_cmp expected actual
-"
+check_diff () {
+ dir=$1
+ shift
+ expect=$1
+ shift
+ cat >expected <<-EOF
+ diff --git a/$expect b/$expect
+ new file mode 100644
+ index 0000000..25c05ef
+ --- /dev/null
+ +++ b/$expect
+ @@ -0,0 +1 @@
+ +other content
+ EOF
+ test_expect_success "-p $*" "
+ git -C '$dir' diff -p $* HEAD^ >actual &&
+ test_cmp expected actual
+ "
}
-check_numstat() {
-expect=$1; shift
-cat >expected <<EOF
-1 0 $expect
-EOF
-test_expect_success "--numstat $*" "
- echo '1 0 $expect' >expected &&
- git diff --numstat $* HEAD^ >actual &&
- test_cmp expected actual
-"
+check_numstat () {
+ dir=$1
+ shift
+ expect=$1
+ shift
+ cat >expected <<-EOF
+ 1 0 $expect
+ EOF
+ test_expect_success "--numstat $*" "
+ echo '1 0 $expect' >expected &&
+ git -C '$dir' diff --numstat $* HEAD^ >actual &&
+ test_cmp expected actual
+ "
}
-check_stat() {
-expect=$1; shift
-cat >expected <<EOF
- $expect | 1 +
- 1 file changed, 1 insertion(+)
-EOF
-test_expect_success "--stat $*" "
- git diff --stat $* HEAD^ >actual &&
- test_i18ncmp expected actual
-"
+check_stat () {
+ dir=$1
+ shift
+ expect=$1
+ shift
+ cat >expected <<-EOF
+ $expect | 1 +
+ 1 file changed, 1 insertion(+)
+ EOF
+ test_expect_success "--stat $*" "
+ git -C '$dir' diff --stat $* HEAD^ >actual &&
+ test_i18ncmp expected actual
+ "
}
-check_raw() {
-expect=$1; shift
-cat >expected <<EOF
-:000000 100644 0000000000000000000000000000000000000000 25c05ef3639d2d270e7fe765a67668f098092bc5 A $expect
-EOF
-test_expect_success "--raw $*" "
- git diff --no-abbrev --raw $* HEAD^ >actual &&
- test_cmp expected actual
-"
+check_raw () {
+ dir=$1
+ shift
+ expect=$1
+ shift
+ cat >expected <<-EOF
+ :000000 100644 0000000000000000000000000000000000000000 25c05ef3639d2d270e7fe765a67668f098092bc5 A $expect
+ EOF
+ test_expect_success "--raw $*" "
+ git -C '$dir' diff --no-abbrev --raw $* HEAD^ >actual &&
+ test_cmp expected actual
+ "
}
-for type in diff numstat stat raw; do
- check_$type file2 --relative=subdir/
- check_$type file2 --relative=subdir
- check_$type dir/file2 --relative=sub
+for type in diff numstat stat raw
+do
+ check_$type . file2 --relative=subdir/
+ check_$type . file2 --relative=subdir
+ check_$type subdir file2 --relative
+ check_$type . dir/file2 --relative=sub
done
test_done
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/t4065-diff-anchored.sh b/t/t4065-diff-anchored.sh
new file mode 100755
index 0000000000..b3f510f040
--- /dev/null
+++ b/t/t4065-diff-anchored.sh
@@ -0,0 +1,94 @@
+#!/bin/sh
+
+test_description='anchored diff algorithm'
+
+. ./test-lib.sh
+
+test_expect_success '--anchored' '
+ printf "a\nb\nc\n" >pre &&
+ printf "c\na\nb\n" >post &&
+
+ # normally, c is moved to produce the smallest diff
+ test_expect_code 1 git diff --no-index pre post >diff &&
+ grep "^+c" diff &&
+
+ # with anchor, a is moved
+ test_expect_code 1 git diff --no-index --anchored=c pre post >diff &&
+ grep "^+a" diff
+'
+
+test_expect_success '--anchored multiple' '
+ printf "a\nb\nc\nd\ne\nf\n" >pre &&
+ printf "c\na\nb\nf\nd\ne\n" >post &&
+
+ # with 1 anchor, c is not moved, but f is moved
+ test_expect_code 1 git diff --no-index --anchored=c pre post >diff &&
+ grep "^+a" diff && # a is moved instead of c
+ grep "^+f" diff &&
+
+ # with 2 anchors, c and f are not moved
+ test_expect_code 1 git diff --no-index --anchored=c --anchored=f pre post >diff &&
+ grep "^+a" diff &&
+ grep "^+d" diff # d is moved instead of f
+'
+
+test_expect_success '--anchored with nonexistent line has no effect' '
+ printf "a\nb\nc\n" >pre &&
+ printf "c\na\nb\n" >post &&
+
+ test_expect_code 1 git diff --no-index --anchored=x pre post >diff &&
+ grep "^+c" diff
+'
+
+test_expect_success '--anchored with non-unique line has no effect' '
+ printf "a\nb\nc\nd\ne\nc\n" >pre &&
+ printf "c\na\nb\nc\nd\ne\n" >post &&
+
+ test_expect_code 1 git diff --no-index --anchored=c pre post >diff &&
+ grep "^+c" diff
+'
+
+test_expect_success 'diff still produced with impossible multiple --anchored' '
+ printf "a\nb\nc\n" >pre &&
+ printf "c\na\nb\n" >post &&
+
+ test_expect_code 1 git diff --no-index --anchored=a --anchored=c pre post >diff &&
+ mv post expected_post &&
+
+ # Ensure that the diff is correct by applying it and then
+ # comparing the result with the original
+ git apply diff &&
+ diff expected_post post
+'
+
+test_expect_success 'later algorithm arguments override earlier ones' '
+ printf "a\nb\nc\n" >pre &&
+ printf "c\na\nb\n" >post &&
+
+ test_expect_code 1 git diff --no-index --patience --anchored=c pre post >diff &&
+ grep "^+a" diff &&
+
+ test_expect_code 1 git diff --no-index --anchored=c --patience pre post >diff &&
+ grep "^+c" diff &&
+
+ test_expect_code 1 git diff --no-index --histogram --anchored=c pre post >diff &&
+ grep "^+a" diff &&
+
+ test_expect_code 1 git diff --no-index --anchored=c --histogram pre post >diff &&
+ grep "^+c" diff
+'
+
+test_expect_success '--anchored works with other commands like "git show"' '
+ printf "a\nb\nc\n" >file &&
+ git add file &&
+ git commit -m foo &&
+ printf "c\na\nb\n" >file &&
+ git add file &&
+ git commit -m foo &&
+
+ # with anchor, a is moved
+ git show --patience --anchored=c >diff &&
+ grep "^+a" diff
+'
+
+test_done
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index 8f155da7a5..25b1f8cc73 100755
--- a/t/t4202-log.sh
+++ b/t/t4202-log.sh
@@ -737,6 +737,107 @@ test_expect_success 'log.decorate configuration' '
'
+test_expect_success 'decorate-refs with glob' '
+ cat >expect.decorate <<-\EOF &&
+ Merge-tag-reach
+ Merge-tags-octopus-a-and-octopus-b
+ seventh
+ octopus-b (octopus-b)
+ octopus-a (octopus-a)
+ reach
+ EOF
+ git log -n6 --decorate=short --pretty="tformat:%f%d" \
+ --decorate-refs="heads/octopus*" >actual &&
+ test_cmp expect.decorate actual
+'
+
+test_expect_success 'decorate-refs without globs' '
+ cat >expect.decorate <<-\EOF &&
+ Merge-tag-reach
+ Merge-tags-octopus-a-and-octopus-b
+ seventh
+ octopus-b
+ octopus-a
+ reach (tag: reach)
+ EOF
+ git log -n6 --decorate=short --pretty="tformat:%f%d" \
+ --decorate-refs="tags/reach" >actual &&
+ test_cmp expect.decorate actual
+'
+
+test_expect_success 'multiple decorate-refs' '
+ cat >expect.decorate <<-\EOF &&
+ Merge-tag-reach
+ Merge-tags-octopus-a-and-octopus-b
+ seventh
+ octopus-b (octopus-b)
+ octopus-a (octopus-a)
+ reach (tag: reach)
+ EOF
+ git log -n6 --decorate=short --pretty="tformat:%f%d" \
+ --decorate-refs="heads/octopus*" \
+ --decorate-refs="tags/reach" >actual &&
+ test_cmp expect.decorate actual
+'
+
+test_expect_success 'decorate-refs-exclude with glob' '
+ cat >expect.decorate <<-\EOF &&
+ Merge-tag-reach (HEAD -> master)
+ Merge-tags-octopus-a-and-octopus-b
+ seventh (tag: seventh)
+ octopus-b (tag: octopus-b)
+ octopus-a (tag: octopus-a)
+ reach (tag: reach, reach)
+ EOF
+ git log -n6 --decorate=short --pretty="tformat:%f%d" \
+ --decorate-refs-exclude="heads/octopus*" >actual &&
+ test_cmp expect.decorate actual
+'
+
+test_expect_success 'decorate-refs-exclude without globs' '
+ cat >expect.decorate <<-\EOF &&
+ Merge-tag-reach (HEAD -> master)
+ Merge-tags-octopus-a-and-octopus-b
+ seventh (tag: seventh)
+ octopus-b (tag: octopus-b, octopus-b)
+ octopus-a (tag: octopus-a, octopus-a)
+ reach (reach)
+ EOF
+ git log -n6 --decorate=short --pretty="tformat:%f%d" \
+ --decorate-refs-exclude="tags/reach" >actual &&
+ test_cmp expect.decorate actual
+'
+
+test_expect_success 'multiple decorate-refs-exclude' '
+ cat >expect.decorate <<-\EOF &&
+ Merge-tag-reach (HEAD -> master)
+ Merge-tags-octopus-a-and-octopus-b
+ seventh (tag: seventh)
+ octopus-b (tag: octopus-b)
+ octopus-a (tag: octopus-a)
+ reach (reach)
+ EOF
+ git log -n6 --decorate=short --pretty="tformat:%f%d" \
+ --decorate-refs-exclude="heads/octopus*" \
+ --decorate-refs-exclude="tags/reach" >actual &&
+ test_cmp expect.decorate actual
+'
+
+test_expect_success 'decorate-refs and decorate-refs-exclude' '
+ cat >expect.decorate <<-\EOF &&
+ Merge-tag-reach (master)
+ Merge-tags-octopus-a-and-octopus-b
+ seventh
+ octopus-b
+ octopus-a
+ reach (reach)
+ EOF
+ git log -n6 --decorate=short --pretty="tformat:%f%d" \
+ --decorate-refs="heads/*" \
+ --decorate-refs-exclude="heads/oc*" >actual &&
+ test_cmp expect.decorate actual
+'
+
test_expect_success 'log.decorate config parsing' '
git log --oneline --decorate=full >expect.full &&
git log --oneline --decorate=short >expect.short &&
diff --git a/t/t4208-log-magic-pathspec.sh b/t/t4208-log-magic-pathspec.sh
index 935df6a65c..a1705f70cf 100755
--- a/t/t4208-log-magic-pathspec.sh
+++ b/t/t4208-log-magic-pathspec.sh
@@ -93,4 +93,23 @@ test_expect_success 'command line pathspec parsing for "git log"' '
git log --merge -- a
'
+test_expect_success 'tree_entry_interesting does not match past submodule boundaries' '
+ test_when_finished "rm -rf repo submodule" &&
+ git init submodule &&
+ test_commit -C submodule initial &&
+ git init repo &&
+ >"repo/[bracket]" &&
+ git -C repo add "[bracket]" &&
+ test_tick &&
+ git -C repo commit -m bracket &&
+ git -C repo rev-list HEAD -- "[bracket]" >expect &&
+
+ git -C repo submodule add ../submodule &&
+ test_tick &&
+ git -C repo commit -m submodule &&
+
+ git -C repo rev-list HEAD -- "[bracket]" >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t5317-pack-objects-filter-objects.sh b/t/t5317-pack-objects-filter-objects.sh
new file mode 100755
index 0000000000..1b0acc383b
--- /dev/null
+++ b/t/t5317-pack-objects-filter-objects.sh
@@ -0,0 +1,375 @@
+#!/bin/sh
+
+test_description='git pack-objects using object filtering'
+
+. ./test-lib.sh
+
+# Test blob:none filter.
+
+test_expect_success 'setup r1' '
+ echo "{print \$1}" >print_1.awk &&
+ echo "{print \$2}" >print_2.awk &&
+
+ git init r1 &&
+ for n in 1 2 3 4 5
+ do
+ echo "This is file: $n" > r1/file.$n
+ git -C r1 add file.$n
+ git -C r1 commit -m "$n"
+ done
+'
+
+test_expect_success 'verify blob count in normal packfile' '
+ git -C r1 ls-files -s file.1 file.2 file.3 file.4 file.5 \
+ | awk -f print_2.awk \
+ | sort >expected &&
+ git -C r1 pack-objects --rev --stdout >all.pack <<-EOF &&
+ HEAD
+ EOF
+ git -C r1 index-pack ../all.pack &&
+ git -C r1 verify-pack -v ../all.pack \
+ | grep blob \
+ | awk -f print_1.awk \
+ | sort >observed &&
+ test_cmp observed expected
+'
+
+test_expect_success 'verify blob:none packfile has no blobs' '
+ git -C r1 pack-objects --rev --stdout --filter=blob:none >filter.pack <<-EOF &&
+ HEAD
+ EOF
+ git -C r1 index-pack ../filter.pack &&
+ git -C r1 verify-pack -v ../filter.pack \
+ | grep blob \
+ | awk -f print_1.awk \
+ | sort >observed &&
+ nr=$(wc -l <observed) &&
+ test 0 -eq $nr
+'
+
+test_expect_success 'verify normal and blob:none packfiles have same commits/trees' '
+ git -C r1 verify-pack -v ../all.pack \
+ | grep -E "commit|tree" \
+ | awk -f print_1.awk \
+ | sort >expected &&
+ git -C r1 verify-pack -v ../filter.pack \
+ | grep -E "commit|tree" \
+ | awk -f print_1.awk \
+ | sort >observed &&
+ test_cmp observed expected
+'
+
+# Test blob:limit=<n>[kmg] filter.
+# We boundary test around the size parameter. The filter is strictly less than
+# the value, so size 500 and 1000 should have the same results, but 1001 should
+# filter more.
+
+test_expect_success 'setup r2' '
+ git init r2 &&
+ for n in 1000 10000
+ do
+ printf "%"$n"s" X > r2/large.$n
+ git -C r2 add large.$n
+ git -C r2 commit -m "$n"
+ done
+'
+
+test_expect_success 'verify blob count in normal packfile' '
+ git -C r2 ls-files -s large.1000 large.10000 \
+ | awk -f print_2.awk \
+ | sort >expected &&
+ git -C r2 pack-objects --rev --stdout >all.pack <<-EOF &&
+ HEAD
+ EOF
+ git -C r2 index-pack ../all.pack &&
+ git -C r2 verify-pack -v ../all.pack \
+ | grep blob \
+ | awk -f print_1.awk \
+ | sort >observed &&
+ test_cmp observed expected
+'
+
+test_expect_success 'verify blob:limit=500 omits all blobs' '
+ git -C r2 pack-objects --rev --stdout --filter=blob:limit=500 >filter.pack <<-EOF &&
+ HEAD
+ EOF
+ git -C r2 index-pack ../filter.pack &&
+ git -C r2 verify-pack -v ../filter.pack \
+ | grep blob \
+ | awk -f print_1.awk \
+ | sort >observed &&
+ nr=$(wc -l <observed) &&
+ test 0 -eq $nr
+'
+
+test_expect_success 'verify blob:limit=1000' '
+ git -C r2 pack-objects --rev --stdout --filter=blob:limit=1000 >filter.pack <<-EOF &&
+ HEAD
+ EOF
+ git -C r2 index-pack ../filter.pack &&
+ git -C r2 verify-pack -v ../filter.pack \
+ | grep blob \
+ | awk -f print_1.awk \
+ | sort >observed &&
+ nr=$(wc -l <observed) &&
+ test 0 -eq $nr
+'
+
+test_expect_success 'verify blob:limit=1001' '
+ git -C r2 ls-files -s large.1000 \
+ | awk -f print_2.awk \
+ | sort >expected &&
+ git -C r2 pack-objects --rev --stdout --filter=blob:limit=1001 >filter.pack <<-EOF &&
+ HEAD
+ EOF
+ git -C r2 index-pack ../filter.pack &&
+ git -C r2 verify-pack -v ../filter.pack \
+ | grep blob \
+ | awk -f print_1.awk \
+ | sort >observed &&
+ test_cmp observed expected
+'
+
+test_expect_success 'verify blob:limit=10001' '
+ git -C r2 ls-files -s large.1000 large.10000 \
+ | awk -f print_2.awk \
+ | sort >expected &&
+ git -C r2 pack-objects --rev --stdout --filter=blob:limit=10001 >filter.pack <<-EOF &&
+ HEAD
+ EOF
+ git -C r2 index-pack ../filter.pack &&
+ git -C r2 verify-pack -v ../filter.pack \
+ | grep blob \
+ | awk -f print_1.awk \
+ | sort >observed &&
+ test_cmp observed expected
+'
+
+test_expect_success 'verify blob:limit=1k' '
+ git -C r2 ls-files -s large.1000 \
+ | awk -f print_2.awk \
+ | sort >expected &&
+ git -C r2 pack-objects --rev --stdout --filter=blob:limit=1k >filter.pack <<-EOF &&
+ HEAD
+ EOF
+ git -C r2 index-pack ../filter.pack &&
+ git -C r2 verify-pack -v ../filter.pack \
+ | grep blob \
+ | awk -f print_1.awk \
+ | sort >observed &&
+ test_cmp observed expected
+'
+
+test_expect_success 'verify blob:limit=1m' '
+ git -C r2 ls-files -s large.1000 large.10000 \
+ | awk -f print_2.awk \
+ | sort >expected &&
+ git -C r2 pack-objects --rev --stdout --filter=blob:limit=1m >filter.pack <<-EOF &&
+ HEAD
+ EOF
+ git -C r2 index-pack ../filter.pack &&
+ git -C r2 verify-pack -v ../filter.pack \
+ | grep blob \
+ | awk -f print_1.awk \
+ | sort >observed &&
+ test_cmp observed expected
+'
+
+test_expect_success 'verify normal and blob:limit packfiles have same commits/trees' '
+ git -C r2 verify-pack -v ../all.pack \
+ | grep -E "commit|tree" \
+ | awk -f print_1.awk \
+ | sort >expected &&
+ git -C r2 verify-pack -v ../filter.pack \
+ | grep -E "commit|tree" \
+ | awk -f print_1.awk \
+ | sort >observed &&
+ test_cmp observed expected
+'
+
+# Test sparse:path=<path> filter.
+# Use a local file containing a sparse-checkout specification to filter
+# out blobs not required for the corresponding sparse-checkout. We do not
+# require sparse-checkout to actually be enabled.
+
+test_expect_success 'setup r3' '
+ git init r3 &&
+ mkdir r3/dir1 &&
+ for n in sparse1 sparse2
+ do
+ echo "This is file: $n" > r3/$n
+ git -C r3 add $n
+ echo "This is file: dir1/$n" > r3/dir1/$n
+ git -C r3 add dir1/$n
+ done &&
+ git -C r3 commit -m "sparse" &&
+ echo dir1/ >pattern1 &&
+ echo sparse1 >pattern2
+'
+
+test_expect_success 'verify blob count in normal packfile' '
+ git -C r3 ls-files -s sparse1 sparse2 dir1/sparse1 dir1/sparse2 \
+ | awk -f print_2.awk \
+ | sort >expected &&
+ git -C r3 pack-objects --rev --stdout >all.pack <<-EOF &&
+ HEAD
+ EOF
+ git -C r3 index-pack ../all.pack &&
+ git -C r3 verify-pack -v ../all.pack \
+ | grep blob \
+ | awk -f print_1.awk \
+ | sort >observed &&
+ test_cmp observed expected
+'
+
+test_expect_success 'verify sparse:path=pattern1' '
+ git -C r3 ls-files -s dir1/sparse1 dir1/sparse2 \
+ | awk -f print_2.awk \
+ | sort >expected &&
+ git -C r3 pack-objects --rev --stdout --filter=sparse:path=../pattern1 >filter.pack <<-EOF &&
+ HEAD
+ EOF
+ git -C r3 index-pack ../filter.pack &&
+ git -C r3 verify-pack -v ../filter.pack \
+ | grep blob \
+ | awk -f print_1.awk \
+ | sort >observed &&
+ test_cmp observed expected
+'
+
+test_expect_success 'verify normal and sparse:path=pattern1 packfiles have same commits/trees' '
+ git -C r3 verify-pack -v ../all.pack \
+ | grep -E "commit|tree" \
+ | awk -f print_1.awk \
+ | sort >expected &&
+ git -C r3 verify-pack -v ../filter.pack \
+ | grep -E "commit|tree" \
+ | awk -f print_1.awk \
+ | sort >observed &&
+ test_cmp observed expected
+'
+
+test_expect_success 'verify sparse:path=pattern2' '
+ git -C r3 ls-files -s sparse1 dir1/sparse1 \
+ | awk -f print_2.awk \
+ | sort >expected &&
+ git -C r3 pack-objects --rev --stdout --filter=sparse:path=../pattern2 >filter.pack <<-EOF &&
+ HEAD
+ EOF
+ git -C r3 index-pack ../filter.pack &&
+ git -C r3 verify-pack -v ../filter.pack \
+ | grep blob \
+ | awk -f print_1.awk \
+ | sort >observed &&
+ test_cmp observed expected
+'
+
+test_expect_success 'verify normal and sparse:path=pattern2 packfiles have same commits/trees' '
+ git -C r3 verify-pack -v ../all.pack \
+ | grep -E "commit|tree" \
+ | awk -f print_1.awk \
+ | sort >expected &&
+ git -C r3 verify-pack -v ../filter.pack \
+ | grep -E "commit|tree" \
+ | awk -f print_1.awk \
+ | sort >observed &&
+ test_cmp observed expected
+'
+
+# Test sparse:oid=<oid-ish> filter.
+# Like sparse:path, but we get the sparse-checkout specification from
+# a blob rather than a file on disk.
+
+test_expect_success 'setup r4' '
+ git init r4 &&
+ mkdir r4/dir1 &&
+ for n in sparse1 sparse2
+ do
+ echo "This is file: $n" > r4/$n
+ git -C r4 add $n
+ echo "This is file: dir1/$n" > r4/dir1/$n
+ git -C r4 add dir1/$n
+ done &&
+ echo dir1/ >r4/pattern &&
+ git -C r4 add pattern &&
+ git -C r4 commit -m "pattern"
+'
+
+test_expect_success 'verify blob count in normal packfile' '
+ git -C r4 ls-files -s pattern sparse1 sparse2 dir1/sparse1 dir1/sparse2 \
+ | awk -f print_2.awk \
+ | sort >expected &&
+ git -C r4 pack-objects --rev --stdout >all.pack <<-EOF &&
+ HEAD
+ EOF
+ git -C r4 index-pack ../all.pack &&
+ git -C r4 verify-pack -v ../all.pack \
+ | grep blob \
+ | awk -f print_1.awk \
+ | sort >observed &&
+ test_cmp observed expected
+'
+
+test_expect_success 'verify sparse:oid=OID' '
+ git -C r4 ls-files -s dir1/sparse1 dir1/sparse2 \
+ | awk -f print_2.awk \
+ | sort >expected &&
+ oid=$(git -C r4 ls-files -s pattern | awk -f print_2.awk) &&
+ git -C r4 pack-objects --rev --stdout --filter=sparse:oid=$oid >filter.pack <<-EOF &&
+ HEAD
+ EOF
+ git -C r4 index-pack ../filter.pack &&
+ git -C r4 verify-pack -v ../filter.pack \
+ | grep blob \
+ | awk -f print_1.awk \
+ | sort >observed &&
+ test_cmp observed expected
+'
+
+test_expect_success 'verify sparse:oid=oid-ish' '
+ git -C r4 ls-files -s dir1/sparse1 dir1/sparse2 \
+ | awk -f print_2.awk \
+ | sort >expected &&
+ git -C r4 pack-objects --rev --stdout --filter=sparse:oid=master:pattern >filter.pack <<-EOF &&
+ HEAD
+ EOF
+ git -C r4 index-pack ../filter.pack &&
+ git -C r4 verify-pack -v ../filter.pack \
+ | grep blob \
+ | awk -f print_1.awk \
+ | sort >observed &&
+ test_cmp observed expected
+'
+
+# Delete some loose objects and use pack-objects, but WITHOUT any filtering.
+# This models previously omitted objects that we did not receive.
+
+test_expect_success 'setup r1 - delete loose blobs' '
+ git -C r1 ls-files -s file.1 file.2 file.3 file.4 file.5 \
+ | awk -f print_2.awk \
+ | sort >expected &&
+ for id in `cat expected | sed "s|..|&/|"`
+ do
+ rm r1/.git/objects/$id
+ done
+'
+
+test_expect_success 'verify pack-objects fails w/ missing objects' '
+ test_must_fail git -C r1 pack-objects --rev --stdout >miss.pack <<-EOF
+ HEAD
+ EOF
+'
+
+test_expect_success 'verify pack-objects fails w/ --missing=error' '
+ test_must_fail git -C r1 pack-objects --rev --stdout --missing=error >miss.pack <<-EOF
+ HEAD
+ EOF
+'
+
+test_expect_success 'verify pack-objects w/ --missing=allow-any' '
+ git -C r1 pack-objects --rev --stdout --missing=allow-any >miss.pack <<-EOF
+ HEAD
+ EOF
+'
+
+test_done
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index 668c54be41..3debc87d4a 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -222,12 +222,9 @@ test_expect_success 'fetch uses remote ref names to describe new refs' '
(
cd descriptive &&
git fetch o 2>actual &&
- grep " -> refs/crazyheads/descriptive-branch$" actual |
- test_i18ngrep "new branch" &&
- grep " -> descriptive-tag$" actual |
- test_i18ngrep "new tag" &&
- grep " -> crazy$" actual |
- test_i18ngrep "new ref"
+ test_i18ngrep "new branch.* -> refs/crazyheads/descriptive-branch$" actual &&
+ test_i18ngrep "new tag.* -> descriptive-tag$" actual &&
+ test_i18ngrep "new ref.* -> crazy$" actual
) &&
git checkout master
'
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..74486c73b0 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 .gitmodules 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/t5536-fetch-conflicts.sh b/t/t5536-fetch-conflicts.sh
index 2e42cf3316..644736b8a3 100755
--- a/t/t5536-fetch-conflicts.sh
+++ b/t/t5536-fetch-conflicts.sh
@@ -22,7 +22,7 @@ verify_stderr () {
cat >expected &&
# We're not interested in the error
# "fatal: The remote end hung up unexpectedly":
- test_i18ngrep -E '^(fatal|warning):' <error | grep -v 'hung up' >actual | sort &&
+ test_i18ngrep -E '^(fatal|warning):' error | grep -v 'hung up' >actual | sort &&
test_i18ncmp expected actual
}
diff --git a/t/t5541-http-push-smart.sh b/t/t5541-http-push-smart.sh
index d38bf32470..21340e89c9 100755
--- a/t/t5541-http-push-smart.sh
+++ b/t/t5541-http-push-smart.sh
@@ -234,7 +234,7 @@ test_expect_success TTY 'push --no-progress silences progress but not status' '
test_commit no-progress &&
test_terminal git push --no-progress >output 2>&1 &&
test_i18ngrep "^To http" output &&
- test_i18ngrep ! "^Writing objects"
+ test_i18ngrep ! "^Writing objects" output
'
test_expect_success 'push --progress shows progress to non-tty' '
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/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh
index a51b7e20d3..f5721b4a59 100755
--- a/t/t5551-http-fetch-smart.sh
+++ b/t/t5551-http-fetch-smart.sh
@@ -364,5 +364,38 @@ test_expect_success 'custom http headers' '
submodule update sub
'
+test_expect_success 'GIT_REDACT_COOKIES redacts cookies' '
+ rm -rf clone &&
+ echo "Set-Cookie: Foo=1" >cookies &&
+ echo "Set-Cookie: Bar=2" >>cookies &&
+ GIT_TRACE_CURL=true GIT_REDACT_COOKIES=Bar,Baz \
+ git -c "http.cookieFile=$(pwd)/cookies" clone \
+ $HTTPD_URL/smart/repo.git clone 2>err &&
+ grep "Cookie:.*Foo=1" err &&
+ grep "Cookie:.*Bar=<redacted>" err &&
+ ! grep "Cookie:.*Bar=2" err
+'
+
+test_expect_success 'GIT_REDACT_COOKIES handles empty values' '
+ rm -rf clone &&
+ echo "Set-Cookie: Foo=" >cookies &&
+ GIT_TRACE_CURL=true GIT_REDACT_COOKIES=Foo \
+ git -c "http.cookieFile=$(pwd)/cookies" clone \
+ $HTTPD_URL/smart/repo.git clone 2>err &&
+ grep "Cookie:.*Foo=<redacted>" err
+'
+
+test_expect_success 'GIT_TRACE_CURL_NO_DATA prevents data from being traced' '
+ rm -rf clone &&
+ GIT_TRACE_CURL=true \
+ git clone $HTTPD_URL/smart/repo.git clone 2>err &&
+ grep "=> Send data" err &&
+
+ rm -rf clone &&
+ GIT_TRACE_CURL=true GIT_TRACE_CURL_NO_DATA=1 \
+ git clone $HTTPD_URL/smart/repo.git clone 2>err &&
+ ! grep "=> Send data" err
+'
+
stop_httpd
test_done
diff --git a/t/t5570-git-daemon.sh b/t/t5570-git-daemon.sh
index 225a022e8a..755b05a8ae 100755
--- a/t/t5570-git-daemon.sh
+++ b/t/t5570-git-daemon.sh
@@ -167,23 +167,48 @@ test_expect_success 'access repo via interpolated hostname' '
git init --bare "$repo" &&
git push "$repo" HEAD &&
>"$repo"/git-daemon-export-ok &&
- rm -rf tmp.git &&
GIT_OVERRIDE_VIRTUAL_HOST=localhost \
- git clone --bare "$GIT_DAEMON_URL/interp.git" tmp.git &&
- rm -rf tmp.git &&
+ git ls-remote "$GIT_DAEMON_URL/interp.git" &&
GIT_OVERRIDE_VIRTUAL_HOST=LOCALHOST \
- git clone --bare "$GIT_DAEMON_URL/interp.git" tmp.git
+ git ls-remote "$GIT_DAEMON_URL/interp.git"
'
test_expect_success 'hostname cannot break out of directory' '
- rm -rf tmp.git &&
repo="$GIT_DAEMON_DOCUMENT_ROOT_PATH/../escape.git" &&
git init --bare "$repo" &&
git push "$repo" HEAD &&
>"$repo"/git-daemon-export-ok &&
test_must_fail \
env GIT_OVERRIDE_VIRTUAL_HOST=.. \
- git clone --bare "$GIT_DAEMON_URL/escape.git" tmp.git
+ git ls-remote "$GIT_DAEMON_URL/escape.git"
+'
+
+test_expect_success 'daemon log records all attributes' '
+ cat >expect <<-\EOF &&
+ Extended attribute "host": localhost
+ Extended attribute "protocol": version=1
+ EOF
+ >daemon.log &&
+ GIT_OVERRIDE_VIRTUAL_HOST=localhost \
+ git -c protocol.version=1 \
+ ls-remote "$GIT_DAEMON_URL/interp.git" &&
+ grep -i extended.attribute daemon.log | cut -d" " -f2- >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success FAKENC 'hostname interpolation works after LF-stripping' '
+ {
+ printf "git-upload-pack /interp.git\n\0host=localhost" | packetize
+ printf "0000"
+ } >input &&
+ fake_nc "$GIT_DAEMON_HOST_PORT" <input >output &&
+ depacketize <output >output.raw &&
+
+ # just pick out the value of master, which avoids any protocol
+ # particulars
+ perl -lne "print \$1 if m{^(\\S+) refs/heads/master}" <output.raw >actual &&
+ git -C "$repo" rev-parse master >expect &&
+ test_cmp expect actual
'
stop_git_daemon
diff --git a/t/t5573-pull-verify-signatures.sh b/t/t5573-pull-verify-signatures.sh
new file mode 100755
index 0000000000..9594e891f4
--- /dev/null
+++ b/t/t5573-pull-verify-signatures.sh
@@ -0,0 +1,81 @@
+#!/bin/sh
+
+test_description='pull signature verification tests'
+. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-gpg.sh"
+
+test_expect_success GPG 'create repositories with signed commits' '
+ echo 1 >a && git add a &&
+ test_tick && git commit -m initial &&
+ git tag initial &&
+
+ git clone . signed &&
+ (
+ cd signed &&
+ echo 2 >b && git add b &&
+ test_tick && git commit -S -m "signed"
+ ) &&
+
+ git clone . unsigned &&
+ (
+ cd unsigned &&
+ echo 3 >c && git add c &&
+ test_tick && git commit -m "unsigned"
+ ) &&
+
+ git clone . bad &&
+ (
+ cd bad &&
+ echo 4 >d && git add d &&
+ test_tick && git commit -S -m "bad" &&
+ git cat-file commit HEAD >raw &&
+ sed -e "s/bad/forged bad/" raw >forged &&
+ git hash-object -w -t commit forged >forged.commit &&
+ git checkout $(cat forged.commit)
+ ) &&
+
+ git clone . untrusted &&
+ (
+ cd untrusted &&
+ echo 5 >e && git add e &&
+ test_tick && git commit -SB7227189 -m "untrusted"
+ )
+'
+
+test_expect_success GPG 'pull unsigned commit with --verify-signatures' '
+ test_when_finished "git reset --hard && git checkout initial" &&
+ test_must_fail git pull --ff-only --verify-signatures unsigned 2>pullerror &&
+ test_i18ngrep "does not have a GPG signature" pullerror
+'
+
+test_expect_success GPG 'pull commit with bad signature with --verify-signatures' '
+ test_when_finished "git reset --hard && git checkout initial" &&
+ test_must_fail git pull --ff-only --verify-signatures bad 2>pullerror &&
+ test_i18ngrep "has a bad GPG signature" pullerror
+'
+
+test_expect_success GPG 'pull commit with untrusted signature with --verify-signatures' '
+ test_when_finished "git reset --hard && git checkout initial" &&
+ test_must_fail git pull --ff-only --verify-signatures untrusted 2>pullerror &&
+ test_i18ngrep "has an untrusted GPG signature" pullerror
+'
+
+test_expect_success GPG 'pull signed commit with --verify-signatures' '
+ test_when_finished "git reset --hard && git checkout initial" &&
+ git pull --verify-signatures signed >pulloutput &&
+ test_i18ngrep "has a good GPG signature" pulloutput
+'
+
+test_expect_success GPG 'pull commit with bad signature without verification' '
+ test_when_finished "git reset --hard && git checkout initial" &&
+ git pull --ff-only bad 2>pullerror
+'
+
+test_expect_success GPG 'pull commit with bad signature with --no-verify-signatures' '
+ test_when_finished "git reset --hard && git checkout initial" &&
+ test_config merge.verifySignatures true &&
+ test_config pull.verifySignatures true &&
+ git pull --ff-only --no-verify-signatures bad 2>pullerror
+'
+
+test_done
diff --git a/t/t5600-clone-fail-cleanup.sh b/t/t5600-clone-fail-cleanup.sh
index 4435693bb2..4a1a912e03 100755
--- a/t/t5600-clone-fail-cleanup.sh
+++ b/t/t5600-clone-fail-cleanup.sh
@@ -7,46 +7,94 @@ test_description='test git clone to cleanup after failure
This test covers the fact that if git clone fails, it should remove
the directory it created, to avoid the user having to manually
-remove the directory before attempting a clone again.'
+remove the directory before attempting a clone again.
+
+Unless the directory already exists, in which case we clean up only what we
+wrote.
+'
. ./test-lib.sh
-test_expect_success \
- 'clone of non-existent source should fail' \
- 'test_must_fail git clone foo bar'
+corrupt_repo () {
+ test_when_finished "rmdir foo/.git/objects.bak" &&
+ mkdir foo/.git/objects.bak/ &&
+ test_when_finished "mv foo/.git/objects.bak/* foo/.git/objects/" &&
+ mv foo/.git/objects/* foo/.git/objects.bak/
+}
-test_expect_success \
- 'failed clone should not leave a directory' \
- '! test -d bar'
+test_expect_success 'clone of non-existent source should fail' '
+ test_must_fail git clone foo bar
+'
-# Need a repo to clone
-test_create_repo foo
+test_expect_success 'failed clone should not leave a directory' '
+ test_path_is_missing bar
+'
-# clone doesn't like it if there is no HEAD. Is that a bug?
-(cd foo && touch file && git add file && git commit -m 'add file' >/dev/null 2>&1)
+test_expect_success 'create a repo to clone' '
+ test_create_repo foo
+'
+
+test_expect_success 'create objects in repo for later corruption' '
+ test_commit -C foo file
+'
# source repository given to git clone should be relative to the
# current path not to the target dir
-test_expect_success \
- 'clone of non-existent (relative to $PWD) source should fail' \
- 'test_must_fail git clone ../foo baz'
+test_expect_success 'clone of non-existent (relative to $PWD) source should fail' '
+ test_must_fail git clone ../foo baz
+'
-test_expect_success \
- 'clone should work now that source exists' \
- 'git clone foo bar'
+test_expect_success 'clone should work now that source exists' '
+ git clone foo bar
+'
-test_expect_success \
- 'successful clone must leave the directory' \
- 'test -d bar'
+test_expect_success 'successful clone must leave the directory' '
+ test_path_is_dir bar
+'
test_expect_success 'failed clone --separate-git-dir should not leave any directories' '
- mkdir foo/.git/objects.bak/ &&
- mv foo/.git/objects/* foo/.git/objects.bak/ &&
+ corrupt_repo &&
test_must_fail git clone --separate-git-dir gitdir foo worktree &&
- test_must_fail test -e gitdir &&
- test_must_fail test -e worktree &&
- mv foo/.git/objects.bak/* foo/.git/objects/ &&
- rmdir foo/.git/objects.bak
+ test_path_is_missing gitdir &&
+ test_path_is_missing worktree
+'
+
+test_expect_success 'failed clone into empty leaves directory (vanilla)' '
+ mkdir -p empty &&
+ corrupt_repo &&
+ test_must_fail git clone foo empty &&
+ test_dir_is_empty empty
+'
+
+test_expect_success 'failed clone into empty leaves directory (bare)' '
+ mkdir -p empty &&
+ corrupt_repo &&
+ test_must_fail git clone --bare foo empty &&
+ test_dir_is_empty empty
+'
+
+test_expect_success 'failed clone into empty leaves directory (separate)' '
+ mkdir -p empty-git empty-wt &&
+ corrupt_repo &&
+ test_must_fail git clone --separate-git-dir empty-git foo empty-wt &&
+ test_dir_is_empty empty-git &&
+ test_dir_is_empty empty-wt
+'
+
+test_expect_success 'failed clone into empty leaves directory (separate, git)' '
+ mkdir -p empty-git &&
+ corrupt_repo &&
+ test_must_fail git clone --separate-git-dir empty-git foo no-wt &&
+ test_dir_is_empty empty-git &&
+ test_path_is_missing no-wt
+'
+
+test_expect_success 'failed clone into empty leaves directory (separate, wt)' '
+ mkdir -p empty-wt &&
+ corrupt_repo &&
+ test_must_fail git clone --separate-git-dir no-git foo empty-wt &&
+ test_path_is_missing no-git &&
+ test_dir_is_empty empty-wt
'
test_done
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index 50e40abb11..8c437bf872 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -306,23 +306,21 @@ test_expect_success 'clone checking out a tag' '
test_cmp fetch.expected fetch.actual
'
-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" &&
- export GIT_SSH &&
- export TRASH_DIRECTORY &&
- >"$TRASH_DIRECTORY"/ssh-output
- '
-}
+test_expect_success 'set up ssh wrapper' '
+ cp "$GIT_BUILD_DIR/t/helper/test-fake-ssh$X" \
+ "$TRASH_DIRECTORY/ssh$X" &&
+ GIT_SSH="$TRASH_DIRECTORY/ssh$X" &&
+ export GIT_SSH &&
+ export TRASH_DIRECTORY &&
+ >"$TRASH_DIRECTORY"/ssh-output
+'
copy_ssh_wrapper_as () {
rm -f "${1%$X}$X" &&
- cp "$TRASH_DIRECTORY/ssh-wrapper$X" "${1%$X}$X" &&
+ cp "$TRASH_DIRECTORY/ssh$X" "${1%$X}$X" &&
+ test_when_finished "rm $(git rev-parse --sq-quote "${1%$X}$X")" &&
GIT_SSH="${1%$X}$X" &&
- export GIT_SSH
+ test_when_finished "GIT_SSH=\"\$TRASH_DIRECTORY/ssh\$X\""
}
expect_ssh () {
@@ -346,8 +344,6 @@ expect_ssh () {
(cd "$TRASH_DIRECTORY" && test_cmp ssh-expect ssh-output)
}
-setup_ssh_wrapper
-
test_expect_success 'clone myhost:src uses ssh' '
git clone myhost:src ssh-clone &&
expect_ssh myhost src
@@ -364,9 +360,52 @@ test_expect_success 'bracketed hostnames are still ssh' '
expect_ssh "-p 123" myhost src
'
-test_expect_success 'uplink is not treated as putty' '
+test_expect_success 'OpenSSH variant passes -4' '
+ git clone -4 "[myhost:123]:src" ssh-ipv4-clone &&
+ expect_ssh "-4 -p 123" myhost src
+'
+
+test_expect_success 'variant can be overridden' '
+ copy_ssh_wrapper_as "$TRASH_DIRECTORY/putty" &&
+ git -c ssh.variant=putty clone -4 "[myhost:123]:src" ssh-putty-clone &&
+ expect_ssh "-4 -P 123" myhost src
+'
+
+test_expect_success 'variant=auto picks based on basename' '
+ copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink" &&
+ git -c ssh.variant=auto clone -4 "[myhost:123]:src" ssh-auto-clone &&
+ expect_ssh "-4 -P 123" myhost src
+'
+
+test_expect_success 'simple does not support -4/-6' '
+ copy_ssh_wrapper_as "$TRASH_DIRECTORY/simple" &&
+ test_must_fail git clone -4 "myhost:src" ssh-4-clone-simple
+'
+
+test_expect_success 'simple does not support port' '
+ copy_ssh_wrapper_as "$TRASH_DIRECTORY/simple" &&
+ test_must_fail git clone "[myhost:123]:src" ssh-bracket-clone-simple
+'
+
+test_expect_success 'uplink is treated as simple' '
copy_ssh_wrapper_as "$TRASH_DIRECTORY/uplink" &&
- git clone "[myhost:123]:src" ssh-bracket-clone-uplink &&
+ test_must_fail git clone "[myhost:123]:src" ssh-bracket-clone-uplink &&
+ git clone "myhost:src" ssh-clone-uplink &&
+ expect_ssh myhost src
+'
+
+test_expect_success 'OpenSSH-like uplink is treated as ssh' '
+ write_script "$TRASH_DIRECTORY/uplink" <<-EOF &&
+ if test "\$1" = "-G"
+ then
+ exit 0
+ fi &&
+ exec "\$TRASH_DIRECTORY/ssh$X" "\$@"
+ EOF
+ test_when_finished "rm -f \"\$TRASH_DIRECTORY/uplink\"" &&
+ GIT_SSH="$TRASH_DIRECTORY/uplink" &&
+ test_when_finished "GIT_SSH=\"\$TRASH_DIRECTORY/ssh\$X\"" &&
+ git clone "[myhost:123]:src" ssh-bracket-clone-sshlike-uplink &&
expect_ssh "-p 123" myhost src
'
@@ -418,12 +457,14 @@ test_expect_success 'ssh.variant overrides plink detection' '
'
test_expect_success 'GIT_SSH_VARIANT overrides plink detection to plink' '
+ copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink" &&
GIT_SSH_VARIANT=plink \
git clone "[myhost:123]:src" ssh-bracket-clone-variant-3 &&
expect_ssh "-P 123" myhost src
'
test_expect_success 'GIT_SSH_VARIANT overrides plink to tortoiseplink' '
+ copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink" &&
GIT_SSH_VARIANT=tortoiseplink \
git clone "[myhost:123]:src" ssh-bracket-clone-variant-4 &&
expect_ssh "-batch -P 123" myhost src
@@ -435,9 +476,6 @@ test_expect_success 'clean failure on broken quoting' '
git clone "[myhost:123]:src" sq-failure
'
-# Reset the GIT_SSH environment variable for clone tests.
-setup_ssh_wrapper
-
counter=0
# $1 url
# $2 none|host
@@ -573,4 +611,21 @@ test_expect_success 'GIT_TRACE_PACKFILE produces a usable pack' '
git -C replay.git index-pack -v --stdin <tmp.pack
'
+hex2oct () {
+ perl -ne 'printf "\\%03o", hex for /../g'
+}
+
+test_expect_success 'clone on case-insensitive fs' '
+ git init icasefs &&
+ (
+ cd icasefs
+ o=$(git hash-object -w --stdin </dev/null | hex2oct) &&
+ t=$(printf "100644 X\0${o}100644 x\0${o}" |
+ git hash-object -w -t tree --stdin) &&
+ c=$(git commit-tree -m bogus $t) &&
+ git update-ref refs/heads/bogus $c &&
+ git clone -b bogus . bogus
+ )
+'
+
test_done
diff --git a/t/t5603-clone-dirname.sh b/t/t5603-clone-dirname.sh
index d5af758129..13b5e5eb9b 100755
--- a/t/t5603-clone-dirname.sh
+++ b/t/t5603-clone-dirname.sh
@@ -11,7 +11,9 @@ test_expect_success 'setup ssh wrapper' '
git upload-pack "$TRASH_DIRECTORY"
EOF
GIT_SSH="$TRASH_DIRECTORY/ssh-wrapper" &&
+ GIT_SSH_VARIANT=ssh &&
export GIT_SSH &&
+ export GIT_SSH_VARIANT &&
export TRASH_DIRECTORY
'
diff --git a/t/t5615-alternate-env.sh b/t/t5615-alternate-env.sh
index d2d883f3a1..b4905b822c 100755
--- a/t/t5615-alternate-env.sh
+++ b/t/t5615-alternate-env.sh
@@ -7,9 +7,9 @@ check_obj () {
alt=$1; shift
while read obj expect
do
- echo "$obj" >&3 &&
- echo "$obj $expect" >&4
- done 3>input 4>expect &&
+ echo "$obj" >&5 &&
+ echo "$obj $expect" >&6
+ done 5>input 6>expect &&
GIT_ALTERNATE_OBJECT_DIRECTORIES=$alt \
git "$@" cat-file --batch-check='%(objectname) %(objecttype)' \
<input >actual &&
diff --git a/t/t5700-protocol-v1.sh b/t/t5700-protocol-v1.sh
new file mode 100755
index 0000000000..ba86a44eb1
--- /dev/null
+++ b/t/t5700-protocol-v1.sh
@@ -0,0 +1,294 @@
+#!/bin/sh
+
+test_description='test git wire-protocol transition'
+
+TEST_NO_CREATE_REPO=1
+
+. ./test-lib.sh
+
+# Test protocol v1 with 'git://' transport
+#
+. "$TEST_DIRECTORY"/lib-git-daemon.sh
+start_git_daemon --export-all --enable=receive-pack
+daemon_parent=$GIT_DAEMON_DOCUMENT_ROOT_PATH/parent
+
+test_expect_success 'create repo to be served by git-daemon' '
+ git init "$daemon_parent" &&
+ test_commit -C "$daemon_parent" one
+'
+
+test_expect_success 'clone with git:// using protocol v1' '
+ GIT_TRACE_PACKET=1 git -c protocol.version=1 \
+ clone "$GIT_DAEMON_URL/parent" daemon_child 2>log &&
+
+ git -C daemon_child log -1 --format=%s >actual &&
+ git -C "$daemon_parent" log -1 --format=%s >expect &&
+ test_cmp expect actual &&
+
+ # Client requested to use protocol v1
+ grep "clone> .*\\\0\\\0version=1\\\0$" log &&
+ # Server responded using protocol v1
+ grep "clone< version 1" log
+'
+
+test_expect_success 'fetch with git:// using protocol v1' '
+ test_commit -C "$daemon_parent" two &&
+
+ GIT_TRACE_PACKET=1 git -C daemon_child -c protocol.version=1 \
+ fetch 2>log &&
+
+ git -C daemon_child log -1 --format=%s origin/master >actual &&
+ git -C "$daemon_parent" log -1 --format=%s >expect &&
+ test_cmp expect actual &&
+
+ # Client requested to use protocol v1
+ grep "fetch> .*\\\0\\\0version=1\\\0$" log &&
+ # Server responded using protocol v1
+ grep "fetch< version 1" log
+'
+
+test_expect_success 'pull with git:// using protocol v1' '
+ GIT_TRACE_PACKET=1 git -C daemon_child -c protocol.version=1 \
+ pull 2>log &&
+
+ git -C daemon_child log -1 --format=%s >actual &&
+ git -C "$daemon_parent" log -1 --format=%s >expect &&
+ test_cmp expect actual &&
+
+ # Client requested to use protocol v1
+ grep "fetch> .*\\\0\\\0version=1\\\0$" log &&
+ # Server responded using protocol v1
+ grep "fetch< version 1" log
+'
+
+test_expect_success 'push with git:// using protocol v1' '
+ test_commit -C daemon_child three &&
+
+ # Push to another branch, as the target repository has the
+ # master branch checked out and we cannot push into it.
+ GIT_TRACE_PACKET=1 git -C daemon_child -c protocol.version=1 \
+ push origin HEAD:client_branch 2>log &&
+
+ git -C daemon_child log -1 --format=%s >actual &&
+ git -C "$daemon_parent" log -1 --format=%s client_branch >expect &&
+ test_cmp expect actual &&
+
+ # Client requested to use protocol v1
+ grep "push> .*\\\0\\\0version=1\\\0$" log &&
+ # Server responded using protocol v1
+ grep "push< version 1" log
+'
+
+stop_git_daemon
+
+# Test protocol v1 with 'file://' transport
+#
+test_expect_success 'create repo to be served by file:// transport' '
+ git init file_parent &&
+ test_commit -C file_parent one
+'
+
+test_expect_success 'clone with file:// using protocol v1' '
+ GIT_TRACE_PACKET=1 git -c protocol.version=1 \
+ clone "file://$(pwd)/file_parent" file_child 2>log &&
+
+ git -C file_child log -1 --format=%s >actual &&
+ git -C file_parent log -1 --format=%s >expect &&
+ test_cmp expect actual &&
+
+ # Server responded using protocol v1
+ grep "clone< version 1" log
+'
+
+test_expect_success 'fetch with file:// using protocol v1' '
+ test_commit -C file_parent two &&
+
+ GIT_TRACE_PACKET=1 git -C file_child -c protocol.version=1 \
+ fetch 2>log &&
+
+ git -C file_child log -1 --format=%s origin/master >actual &&
+ git -C file_parent log -1 --format=%s >expect &&
+ test_cmp expect actual &&
+
+ # Server responded using protocol v1
+ grep "fetch< version 1" log
+'
+
+test_expect_success 'pull with file:// using protocol v1' '
+ GIT_TRACE_PACKET=1 git -C file_child -c protocol.version=1 \
+ pull 2>log &&
+
+ git -C file_child log -1 --format=%s >actual &&
+ git -C file_parent log -1 --format=%s >expect &&
+ test_cmp expect actual &&
+
+ # Server responded using protocol v1
+ grep "fetch< version 1" log
+'
+
+test_expect_success 'push with file:// using protocol v1' '
+ test_commit -C file_child three &&
+
+ # Push to another branch, as the target repository has the
+ # master branch checked out and we cannot push into it.
+ GIT_TRACE_PACKET=1 git -C file_child -c protocol.version=1 \
+ push origin HEAD:client_branch 2>log &&
+
+ git -C file_child log -1 --format=%s >actual &&
+ git -C file_parent log -1 --format=%s client_branch >expect &&
+ test_cmp expect actual &&
+
+ # Server responded using protocol v1
+ grep "push< version 1" log
+'
+
+# Test protocol v1 with 'ssh://' transport
+#
+test_expect_success 'setup ssh wrapper' '
+ GIT_SSH="$GIT_BUILD_DIR/t/helper/test-fake-ssh" &&
+ export GIT_SSH &&
+ GIT_SSH_VARIANT=ssh &&
+ export GIT_SSH_VARIANT &&
+ export TRASH_DIRECTORY &&
+ >"$TRASH_DIRECTORY"/ssh-output
+'
+
+expect_ssh () {
+ test_when_finished '(cd "$TRASH_DIRECTORY" && rm -f ssh-expect && >ssh-output)' &&
+ echo "ssh: -o SendEnv=GIT_PROTOCOL myhost $1 '$PWD/ssh_parent'" >"$TRASH_DIRECTORY/ssh-expect" &&
+ (cd "$TRASH_DIRECTORY" && test_cmp ssh-expect ssh-output)
+}
+
+test_expect_success 'create repo to be served by ssh:// transport' '
+ git init ssh_parent &&
+ test_commit -C ssh_parent one
+'
+
+test_expect_success 'clone with ssh:// using protocol v1' '
+ GIT_TRACE_PACKET=1 git -c protocol.version=1 \
+ clone "ssh://myhost:$(pwd)/ssh_parent" ssh_child 2>log &&
+ expect_ssh git-upload-pack &&
+
+ git -C ssh_child log -1 --format=%s >actual &&
+ git -C ssh_parent log -1 --format=%s >expect &&
+ test_cmp expect actual &&
+
+ # Server responded using protocol v1
+ grep "clone< version 1" log
+'
+
+test_expect_success 'fetch with ssh:// using protocol v1' '
+ test_commit -C ssh_parent two &&
+
+ GIT_TRACE_PACKET=1 git -C ssh_child -c protocol.version=1 \
+ fetch 2>log &&
+ expect_ssh git-upload-pack &&
+
+ git -C ssh_child log -1 --format=%s origin/master >actual &&
+ git -C ssh_parent log -1 --format=%s >expect &&
+ test_cmp expect actual &&
+
+ # Server responded using protocol v1
+ grep "fetch< version 1" log
+'
+
+test_expect_success 'pull with ssh:// using protocol v1' '
+ GIT_TRACE_PACKET=1 git -C ssh_child -c protocol.version=1 \
+ pull 2>log &&
+ expect_ssh git-upload-pack &&
+
+ git -C ssh_child log -1 --format=%s >actual &&
+ git -C ssh_parent log -1 --format=%s >expect &&
+ test_cmp expect actual &&
+
+ # Server responded using protocol v1
+ grep "fetch< version 1" log
+'
+
+test_expect_success 'push with ssh:// using protocol v1' '
+ test_commit -C ssh_child three &&
+
+ # Push to another branch, as the target repository has the
+ # master branch checked out and we cannot push into it.
+ GIT_TRACE_PACKET=1 git -C ssh_child -c protocol.version=1 \
+ push origin HEAD:client_branch 2>log &&
+ expect_ssh git-receive-pack &&
+
+ git -C ssh_child log -1 --format=%s >actual &&
+ git -C ssh_parent log -1 --format=%s client_branch >expect &&
+ test_cmp expect actual &&
+
+ # Server responded using protocol v1
+ grep "push< version 1" log
+'
+
+# Test protocol v1 with 'http://' transport
+#
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+test_expect_success 'create repo to be served by http:// transport' '
+ git init "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" &&
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" config http.receivepack true &&
+ test_commit -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" one
+'
+
+test_expect_success 'clone with http:// using protocol v1' '
+ GIT_TRACE_PACKET=1 GIT_TRACE_CURL=1 git -c protocol.version=1 \
+ clone "$HTTPD_URL/smart/http_parent" http_child 2>log &&
+
+ git -C http_child log -1 --format=%s >actual &&
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" log -1 --format=%s >expect &&
+ test_cmp expect actual &&
+
+ # Client requested to use protocol v1
+ grep "Git-Protocol: version=1" log &&
+ # Server responded using protocol v1
+ grep "git< version 1" log
+'
+
+test_expect_success 'fetch with http:// using protocol v1' '
+ test_commit -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" two &&
+
+ GIT_TRACE_PACKET=1 git -C http_child -c protocol.version=1 \
+ fetch 2>log &&
+
+ git -C http_child log -1 --format=%s origin/master >actual &&
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" log -1 --format=%s >expect &&
+ test_cmp expect actual &&
+
+ # Server responded using protocol v1
+ grep "git< version 1" log
+'
+
+test_expect_success 'pull with http:// using protocol v1' '
+ GIT_TRACE_PACKET=1 git -C http_child -c protocol.version=1 \
+ pull 2>log &&
+
+ git -C http_child log -1 --format=%s >actual &&
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" log -1 --format=%s >expect &&
+ test_cmp expect actual &&
+
+ # Server responded using protocol v1
+ grep "git< version 1" log
+'
+
+test_expect_success 'push with http:// using protocol v1' '
+ test_commit -C http_child three &&
+
+ # Push to another branch, as the target repository has the
+ # master branch checked out and we cannot push into it.
+ GIT_TRACE_PACKET=1 git -C http_child -c protocol.version=1 \
+ push origin HEAD:client_branch && #2>log &&
+
+ git -C http_child log -1 --format=%s >actual &&
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" log -1 --format=%s client_branch >expect &&
+ test_cmp expect actual &&
+
+ # Server responded using protocol v1
+ grep "git< version 1" log
+'
+
+stop_httpd
+
+test_done
diff --git a/t/t5812-proto-disable-http.sh b/t/t5812-proto-disable-http.sh
index d911afd24c..872788ac8c 100755
--- a/t/t5812-proto-disable-http.sh
+++ b/t/t5812-proto-disable-http.sh
@@ -20,10 +20,7 @@ test_expect_success 'curl redirects respect whitelist' '
test_must_fail env GIT_ALLOW_PROTOCOL=http:https \
GIT_SMART_HTTP=0 \
git clone "$HTTPD_URL/ftp-redir/repo.git" 2>stderr &&
- {
- test_i18ngrep "ftp.*disabled" stderr ||
- test_i18ngrep "your curl version is too old"
- }
+ test_i18ngrep -E "(ftp.*disabled|your curl version is too old)" stderr
'
test_expect_success 'curl limits redirects' '
diff --git a/t/t6015-rev-list-show-all-parents.sh b/t/t6015-rev-list-show-all-parents.sh
deleted file mode 100755
index 3c73c93ba6..0000000000
--- a/t/t6015-rev-list-show-all-parents.sh
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/bin/sh
-
-test_description='--show-all --parents does not rewrite TREESAME commits'
-
-. ./test-lib.sh
-
-test_expect_success 'set up --show-all --parents test' '
- test_commit one foo.txt &&
- commit1=$(git rev-list -1 HEAD) &&
- test_commit two bar.txt &&
- commit2=$(git rev-list -1 HEAD) &&
- test_commit three foo.txt &&
- commit3=$(git rev-list -1 HEAD)
- '
-
-test_expect_success '--parents rewrites TREESAME parents correctly' '
- echo $commit3 $commit1 > expected &&
- echo $commit1 >> expected &&
- git rev-list --parents HEAD -- foo.txt > actual &&
- test_cmp expected actual
- '
-
-test_expect_success '--parents --show-all does not rewrites TREESAME parents' '
- echo $commit3 $commit2 > expected &&
- echo $commit2 $commit1 >> expected &&
- echo $commit1 >> expected &&
- git rev-list --parents --show-all HEAD -- foo.txt > actual &&
- test_cmp expected actual
- '
-
-test_done
diff --git a/t/t6022-merge-rename.sh b/t/t6022-merge-rename.sh
index 05ebba7afa..c01f721f13 100755
--- a/t/t6022-merge-rename.sh
+++ b/t/t6022-merge-rename.sh
@@ -242,10 +242,12 @@ test_expect_success 'merge of identical changes in a renamed file' '
rm -f A M N &&
git reset --hard &&
git checkout change+rename &&
- GIT_MERGE_VERBOSITY=3 git merge change | test_i18ngrep "^Skipped B" &&
+ GIT_MERGE_VERBOSITY=3 git merge change >out &&
+ test_i18ngrep "^Skipped B" out &&
git reset --hard HEAD^ &&
git checkout change &&
- GIT_MERGE_VERBOSITY=3 git merge change+rename | test_i18ngrep "^Skipped B"
+ GIT_MERGE_VERBOSITY=3 git merge change+rename >out &&
+ test_i18ngrep "^Skipped B" out
'
test_expect_success 'setup for rename + d/f conflicts' '
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/t6037-merge-ours-theirs.sh b/t/t6037-merge-ours-theirs.sh
index 3889eca4ae..0aebc6c028 100755
--- a/t/t6037-merge-ours-theirs.sh
+++ b/t/t6037-merge-ours-theirs.sh
@@ -73,4 +73,36 @@ test_expect_success 'pull passes -X to underlying merge' '
git reset --hard master && test_must_fail git pull -s recursive -X bork . side
'
+test_expect_success SYMLINKS 'symlink with -Xours/-Xtheirs' '
+ git reset --hard master &&
+ git checkout -b two master &&
+ ln -s target-zero link &&
+ git add link &&
+ git commit -m "add link pointing to zero" &&
+
+ ln -f -s target-two link &&
+ git commit -m "add link pointing to two" link &&
+
+ git checkout -b one HEAD^ &&
+ ln -f -s target-one link &&
+ git commit -m "add link pointing to one" link &&
+
+ # we expect symbolic links not to resolve automatically, of course
+ git checkout one^0 &&
+ test_must_fail git merge -s recursive two &&
+
+ # favor theirs to resolve to target-two?
+ git reset --hard &&
+ git checkout one^0 &&
+ git merge -s recursive -X theirs two &&
+ git diff --exit-code two HEAD link &&
+
+ # favor ours to resolve to target-one?
+ git reset --hard &&
+ git checkout one^0 &&
+ git merge -s recursive -X ours two &&
+ git diff --exit-code one HEAD link
+
+'
+
test_done
diff --git a/t/t6044-merge-unrelated-index-changes.sh b/t/t6044-merge-unrelated-index-changes.sh
index 01023486c5..23b86fb977 100755
--- a/t/t6044-merge-unrelated-index-changes.sh
+++ b/t/t6044-merge-unrelated-index-changes.sh
@@ -6,18 +6,21 @@ test_description="merges with unrelated index changes"
# Testcase for some simple merges
# A
-# o-----o B
+# o-------o B
# \
-# \---o C
+# \-----o C
# \
-# \-o D
+# \---o D
# \
-# o E
+# \-o E
+# \
+# o F
# Commit A: some file a
# Commit B: adds file b, modifies end of a
# Commit C: adds file c
# Commit D: adds file d, modifies beginning of a
# Commit E: renames a->subdir/a, adds subdir/e
+# Commit F: empty commit
test_expect_success 'setup trivial merges' '
test_seq 1 10 >a &&
@@ -29,6 +32,7 @@ test_expect_success 'setup trivial merges' '
git branch C &&
git branch D &&
git branch E &&
+ git branch F &&
git checkout B &&
echo b >b &&
@@ -52,7 +56,10 @@ test_expect_success 'setup trivial merges' '
git mv a subdir/a &&
echo e >subdir/e &&
git add subdir &&
- test_tick && git commit -m E
+ test_tick && git commit -m E &&
+
+ git checkout F &&
+ test_tick && git commit --allow-empty -m F
'
test_expect_success 'ff update' '
@@ -105,6 +112,15 @@ test_expect_success 'recursive' '
test_must_fail git merge -s recursive C^0
'
+test_expect_success 'recursive, when merge branch matches merge base' '
+ git reset --hard &&
+ git checkout B^0 &&
+
+ touch random_file && git add random_file &&
+
+ test_must_fail git merge -s recursive F^0
+'
+
test_expect_success 'octopus, unrelated file touched' '
git reset --hard &&
git checkout B^0 &&
diff --git a/t/t6100-rev-list-in-order.sh b/t/t6100-rev-list-in-order.sh
new file mode 100755
index 0000000000..b2bb0a7f61
--- /dev/null
+++ b/t/t6100-rev-list-in-order.sh
@@ -0,0 +1,77 @@
+#!/bin/sh
+
+test_description='rev-list testing in-commit-order'
+
+. ./test-lib.sh
+
+test_expect_success 'setup a commit history with trees, blobs' '
+ for x in one two three four
+ do
+ echo $x >$x &&
+ git add $x &&
+ git commit -m "add file $x" ||
+ return 1
+ done &&
+ for x in four three
+ do
+ git rm $x &&
+ git commit -m "remove $x" ||
+ return 1
+ done
+'
+
+test_expect_success 'rev-list --in-commit-order' '
+ git rev-list --in-commit-order --objects HEAD >actual.raw &&
+ cut -c 1-40 >actual <actual.raw &&
+
+ git cat-file --batch-check="%(objectname)" >expect.raw <<-\EOF &&
+ HEAD^{commit}
+ HEAD^{tree}
+ HEAD^{tree}:one
+ HEAD^{tree}:two
+ HEAD~1^{commit}
+ HEAD~1^{tree}
+ HEAD~1^{tree}:three
+ HEAD~2^{commit}
+ HEAD~2^{tree}
+ HEAD~2^{tree}:four
+ HEAD~3^{commit}
+ # HEAD~3^{tree} skipped, same as HEAD~1^{tree}
+ HEAD~4^{commit}
+ # HEAD~4^{tree} skipped, same as HEAD^{tree}
+ HEAD~5^{commit}
+ HEAD~5^{tree}
+ EOF
+ grep -v "#" >expect <expect.raw &&
+
+ test_cmp expect actual
+'
+
+test_expect_success 'rev-list lists blobs and trees after commits' '
+ git rev-list --objects HEAD >actual.raw &&
+ cut -c 1-40 >actual <actual.raw &&
+
+ git cat-file --batch-check="%(objectname)" >expect.raw <<-\EOF &&
+ HEAD^{commit}
+ HEAD~1^{commit}
+ HEAD~2^{commit}
+ HEAD~3^{commit}
+ HEAD~4^{commit}
+ HEAD~5^{commit}
+ HEAD^{tree}
+ HEAD^{tree}:one
+ HEAD^{tree}:two
+ HEAD~1^{tree}
+ HEAD~1^{tree}:three
+ HEAD~2^{tree}
+ HEAD~2^{tree}:four
+ # HEAD~3^{tree} skipped, same as HEAD~1^{tree}
+ # HEAD~4^{tree} skipped, same as HEAD^{tree}
+ HEAD~5^{tree}
+ EOF
+ grep -v "#" >expect <expect.raw &&
+
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t6112-rev-list-filters-objects.sh b/t/t6112-rev-list-filters-objects.sh
new file mode 100755
index 0000000000..0a37dd5f97
--- /dev/null
+++ b/t/t6112-rev-list-filters-objects.sh
@@ -0,0 +1,225 @@
+#!/bin/sh
+
+test_description='git rev-list using object filtering'
+
+. ./test-lib.sh
+
+# Test the blob:none filter.
+
+test_expect_success 'setup r1' '
+ echo "{print \$1}" >print_1.awk &&
+ echo "{print \$2}" >print_2.awk &&
+
+ git init r1 &&
+ for n in 1 2 3 4 5
+ do
+ echo "This is file: $n" > r1/file.$n
+ git -C r1 add file.$n
+ git -C r1 commit -m "$n"
+ done
+'
+
+test_expect_success 'verify blob:none omits all 5 blobs' '
+ git -C r1 ls-files -s file.1 file.2 file.3 file.4 file.5 \
+ | awk -f print_2.awk \
+ | sort >expected &&
+ git -C r1 rev-list HEAD --quiet --objects --filter-print-omitted --filter=blob:none \
+ | awk -f print_1.awk \
+ | sed "s/~//" \
+ | sort >observed &&
+ test_cmp observed expected
+'
+
+test_expect_success 'verify emitted+omitted == all' '
+ git -C r1 rev-list HEAD --objects \
+ | awk -f print_1.awk \
+ | sort >expected &&
+ git -C r1 rev-list HEAD --objects --filter-print-omitted --filter=blob:none \
+ | awk -f print_1.awk \
+ | sed "s/~//" \
+ | sort >observed &&
+ test_cmp observed expected
+'
+
+
+# Test blob:limit=<n>[kmg] filter.
+# We boundary test around the size parameter. The filter is strictly less than
+# the value, so size 500 and 1000 should have the same results, but 1001 should
+# filter more.
+
+test_expect_success 'setup r2' '
+ git init r2 &&
+ for n in 1000 10000
+ do
+ printf "%"$n"s" X > r2/large.$n
+ git -C r2 add large.$n
+ git -C r2 commit -m "$n"
+ done
+'
+
+test_expect_success 'verify blob:limit=500 omits all blobs' '
+ git -C r2 ls-files -s large.1000 large.10000 \
+ | awk -f print_2.awk \
+ | sort >expected &&
+ git -C r2 rev-list HEAD --quiet --objects --filter-print-omitted --filter=blob:limit=500 \
+ | awk -f print_1.awk \
+ | sed "s/~//" \
+ | sort >observed &&
+ test_cmp observed expected
+'
+
+test_expect_success 'verify emitted+omitted == all' '
+ git -C r2 rev-list HEAD --objects \
+ | awk -f print_1.awk \
+ | sort >expected &&
+ git -C r2 rev-list HEAD --objects --filter-print-omitted --filter=blob:limit=500 \
+ | awk -f print_1.awk \
+ | sed "s/~//" \
+ | sort >observed &&
+ test_cmp observed expected
+'
+
+test_expect_success 'verify blob:limit=1000' '
+ git -C r2 ls-files -s large.1000 large.10000 \
+ | awk -f print_2.awk \
+ | sort >expected &&
+ git -C r2 rev-list HEAD --quiet --objects --filter-print-omitted --filter=blob:limit=1000 \
+ | awk -f print_1.awk \
+ | sed "s/~//" \
+ | sort >observed &&
+ test_cmp observed expected
+'
+
+test_expect_success 'verify blob:limit=1001' '
+ git -C r2 ls-files -s large.10000 \
+ | awk -f print_2.awk \
+ | sort >expected &&
+ git -C r2 rev-list HEAD --quiet --objects --filter-print-omitted --filter=blob:limit=1001 \
+ | awk -f print_1.awk \
+ | sed "s/~//" \
+ | sort >observed &&
+ test_cmp observed expected
+'
+
+test_expect_success 'verify blob:limit=1k' '
+ git -C r2 ls-files -s large.10000 \
+ | awk -f print_2.awk \
+ | sort >expected &&
+ git -C r2 rev-list HEAD --quiet --objects --filter-print-omitted --filter=blob:limit=1k \
+ | awk -f print_1.awk \
+ | sed "s/~//" \
+ | sort >observed &&
+ test_cmp observed expected
+'
+
+test_expect_success 'verify blob:limit=1m' '
+ cat </dev/null >expected &&
+ git -C r2 rev-list HEAD --quiet --objects --filter-print-omitted --filter=blob:limit=1m \
+ | awk -f print_1.awk \
+ | sed "s/~//" \
+ | sort >observed &&
+ test_cmp observed expected
+'
+
+# Test sparse:path=<path> filter.
+# Use a local file containing a sparse-checkout specification to filter
+# out blobs not required for the corresponding sparse-checkout. We do not
+# require sparse-checkout to actually be enabled.
+
+test_expect_success 'setup r3' '
+ git init r3 &&
+ mkdir r3/dir1 &&
+ for n in sparse1 sparse2
+ do
+ echo "This is file: $n" > r3/$n
+ git -C r3 add $n
+ echo "This is file: dir1/$n" > r3/dir1/$n
+ git -C r3 add dir1/$n
+ done &&
+ git -C r3 commit -m "sparse" &&
+ echo dir1/ >pattern1 &&
+ echo sparse1 >pattern2
+'
+
+test_expect_success 'verify sparse:path=pattern1 omits top-level files' '
+ git -C r3 ls-files -s sparse1 sparse2 \
+ | awk -f print_2.awk \
+ | sort >expected &&
+ git -C r3 rev-list HEAD --quiet --objects --filter-print-omitted --filter=sparse:path=../pattern1 \
+ | awk -f print_1.awk \
+ | sed "s/~//" \
+ | sort >observed &&
+ test_cmp observed expected
+'
+
+test_expect_success 'verify sparse:path=pattern2 omits both sparse2 files' '
+ git -C r3 ls-files -s sparse2 dir1/sparse2 \
+ | awk -f print_2.awk \
+ | sort >expected &&
+ git -C r3 rev-list HEAD --quiet --objects --filter-print-omitted --filter=sparse:path=../pattern2 \
+ | awk -f print_1.awk \
+ | sed "s/~//" \
+ | sort >observed &&
+ test_cmp observed expected
+'
+
+# Test sparse:oid=<oid-ish> filter.
+# Like sparse:path, but we get the sparse-checkout specification from
+# a blob rather than a file on disk.
+
+test_expect_success 'setup r3 part 2' '
+ echo dir1/ >r3/pattern &&
+ git -C r3 add pattern &&
+ git -C r3 commit -m "pattern"
+'
+
+test_expect_success 'verify sparse:oid=OID omits top-level files' '
+ git -C r3 ls-files -s pattern sparse1 sparse2 \
+ | awk -f print_2.awk \
+ | sort >expected &&
+ oid=$(git -C r3 ls-files -s pattern | awk -f print_2.awk) &&
+ git -C r3 rev-list HEAD --quiet --objects --filter-print-omitted --filter=sparse:oid=$oid \
+ | awk -f print_1.awk \
+ | sed "s/~//" \
+ | sort >observed &&
+ test_cmp observed expected
+'
+
+test_expect_success 'verify sparse:oid=oid-ish omits top-level files' '
+ git -C r3 ls-files -s pattern sparse1 sparse2 \
+ | awk -f print_2.awk \
+ | sort >expected &&
+ git -C r3 rev-list HEAD --quiet --objects --filter-print-omitted --filter=sparse:oid=master:pattern \
+ | awk -f print_1.awk \
+ | sed "s/~//" \
+ | sort >observed &&
+ test_cmp observed expected
+'
+
+# Delete some loose objects and use rev-list, but WITHOUT any filtering.
+# This models previously omitted objects that we did not receive.
+
+test_expect_success 'rev-list W/ --missing=print' '
+ git -C r1 ls-files -s file.1 file.2 file.3 file.4 file.5 \
+ | awk -f print_2.awk \
+ | sort >expected &&
+ for id in `cat expected | sed "s|..|&/|"`
+ do
+ rm r1/.git/objects/$id
+ done &&
+ git -C r1 rev-list --quiet HEAD --missing=print --objects \
+ | awk -f print_1.awk \
+ | sed "s/?//" \
+ | sort >observed &&
+ test_cmp observed expected
+'
+
+test_expect_success 'rev-list W/O --missing fails' '
+ test_must_fail git -C r1 rev-list --quiet --objects HEAD
+'
+
+test_expect_success 'rev-list W/ missing=allow-any' '
+ git -C r1 rev-list --quiet --missing=allow-any --objects HEAD
+'
+
+test_done
diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh
index 1c0e8659d9..a5d9015024 100755
--- a/t/t6120-describe.sh
+++ b/t/t6120-describe.sh
@@ -122,7 +122,7 @@ test_expect_success 'describe --contains defaults to HEAD without commit-ish' '
'
: >err.expect
-check_describe A --all A^0
+check_describe tags/A --all A^0
test_expect_success 'no warning was displayed for A' '
test_cmp err.expect err.actual
'
@@ -304,12 +304,46 @@ test_expect_success 'describe chokes on severely broken submodules' '
mv .git/modules/sub1/ .git/modules/sub_moved &&
test_must_fail git describe --dirty
'
-test_expect_success 'describe ignoring a borken submodule' '
+test_expect_success 'describe ignoring a broken submodule' '
git describe --broken >out &&
test_when_finished "mv .git/modules/sub_moved .git/modules/sub1" &&
grep broken out
'
+test_expect_success 'describe a blob at a directly tagged commit' '
+ echo "make it a unique blob" >file &&
+ git add file && git commit -m "content in file" &&
+ git tag -a -m "latest annotated tag" unique-file &&
+ git describe HEAD:file >actual &&
+ echo "unique-file:file" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'describe a blob with its first introduction' '
+ git commit --allow-empty -m "empty commit" &&
+ git rm file &&
+ git commit -m "delete blob" &&
+ git revert HEAD &&
+ git commit --allow-empty -m "empty commit" &&
+ git describe HEAD:file >actual &&
+ echo "unique-file:file" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'describe directly tagged blob' '
+ git tag test-blob unique-file:file &&
+ git describe test-blob >actual &&
+ echo "unique-file:file" >expect &&
+ # suboptimal: we rather want to see "test-blob"
+ test_cmp expect actual
+'
+
+test_expect_success 'describe tag object' '
+ git tag test-blob-1 -a -m msg unique-file:file &&
+ test_must_fail git describe test-blob-1 2>actual &&
+ test_i18ngrep "fatal: test-blob-1 is neither a commit nor blob" actual
+'
+
test_expect_failure ULIMIT_STACK_SIZE 'name-rev works in a deep repo' '
i=1 &&
while test $i -lt 8000
@@ -340,4 +374,8 @@ test_expect_success ULIMIT_STACK_SIZE 'describe works in a deep repo' '
test_cmp expect actual
'
+check_describe tags/A --all A
+check_describe tags/c --all c
+check_describe heads/branch_A --all --match='branch_*' branch_A
+
test_done
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index 3aa534933e..c128dfc579 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -766,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 6e5031f56f..d4e6485a26 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -39,6 +39,12 @@ test_expect_success \
grep "^R100..*path1/COPYING..*path0/COPYING"'
test_expect_success \
+ 'mv --dry-run does not move file' \
+ 'git mv -n path0/COPYING MOVED &&
+ test -f path0/COPYING &&
+ test ! -f MOVED'
+
+test_expect_success \
'checking -k on non-existing file' \
'git mv -k idontexist path0'
diff --git a/t/t7006-pager.sh b/t/t7006-pager.sh
index 865168ec6a..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() {
diff --git a/t/t7409-submodule-detached-work-tree.sh b/t/t7409-submodule-detached-work-tree.sh
index c20717181e..fc018e3638 100755
--- a/t/t7409-submodule-detached-work-tree.sh
+++ b/t/t7409-submodule-detached-work-tree.sh
@@ -6,7 +6,7 @@
test_description='Test submodules on detached working tree
This test verifies that "git submodule" initialization, update and addition works
-on detahced working trees
+on detached working trees
'
TEST_NO_CREATE_REPO=1
diff --git a/t/t7500-commit.sh b/t/t7500-commit.sh
index 5739d3ed23..170b4810e0 100755
--- a/t/t7500-commit.sh
+++ b/t/t7500-commit.sh
@@ -130,8 +130,8 @@ EOF
test_expect_success 'commit message from template with whitespace issue' '
echo "content galore" >>foo &&
git add foo &&
- GIT_EDITOR="$TEST_DIRECTORY"/t7500/add-whitespaced-content git commit \
- --template "$TEMPLATE" &&
+ GIT_EDITOR=\""$TEST_DIRECTORY"\"/t7500/add-whitespaced-content \
+ git commit --template "$TEMPLATE" &&
commit_msg_is "commit message"
'
@@ -272,6 +272,14 @@ test_expect_success 'commit --fixup provides correct one-line commit message' '
commit_msg_is "fixup! target message subject line"
'
+test_expect_success 'commit --fixup -m"something" -m"extra"' '
+ commit_for_rebase_autosquash_setup &&
+ git commit --fixup HEAD~1 -m"something" -m"extra" &&
+ commit_msg_is "fixup! target message subject linesomething
+
+extra"
+'
+
test_expect_success 'commit --squash works with -F' '
commit_for_rebase_autosquash_setup &&
echo "log message from file" >msgfile &&
@@ -325,7 +333,6 @@ test_expect_success 'invalid message options when using --fixup' '
test_must_fail git commit --fixup HEAD~1 --squash HEAD~2 &&
test_must_fail git commit --fixup HEAD~1 -C HEAD~2 &&
test_must_fail git commit --fixup HEAD~1 -c HEAD~2 &&
- test_must_fail git commit --fixup HEAD~1 -m "cmdline message" &&
test_must_fail git commit --fixup HEAD~1 -F log
'
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/t7612-merge-verify-signatures.sh b/t/t7612-merge-verify-signatures.sh
index 8ae69a61c3..e797c74112 100755
--- a/t/t7612-merge-verify-signatures.sh
+++ b/t/t7612-merge-verify-signatures.sh
@@ -35,27 +35,72 @@ test_expect_success GPG 'create signed commits' '
'
test_expect_success GPG 'merge unsigned commit with verification' '
+ test_when_finished "git reset --hard && git checkout initial" &&
test_must_fail git merge --ff-only --verify-signatures side-unsigned 2>mergeerror &&
test_i18ngrep "does not have a GPG signature" mergeerror
'
+test_expect_success GPG 'merge unsigned commit with merge.verifySignatures=true' '
+ test_when_finished "git reset --hard && git checkout initial" &&
+ test_config merge.verifySignatures true &&
+ test_must_fail git merge --ff-only side-unsigned 2>mergeerror &&
+ test_i18ngrep "does not have a GPG signature" mergeerror
+'
+
test_expect_success GPG 'merge commit with bad signature with verification' '
+ test_when_finished "git reset --hard && git checkout initial" &&
test_must_fail git merge --ff-only --verify-signatures $(cat forged.commit) 2>mergeerror &&
test_i18ngrep "has a bad GPG signature" mergeerror
'
+test_expect_success GPG 'merge commit with bad signature with merge.verifySignatures=true' '
+ test_when_finished "git reset --hard && git checkout initial" &&
+ test_config merge.verifySignatures true &&
+ test_must_fail git merge --ff-only $(cat forged.commit) 2>mergeerror &&
+ test_i18ngrep "has a bad GPG signature" mergeerror
+'
+
test_expect_success GPG 'merge commit with untrusted signature with verification' '
+ test_when_finished "git reset --hard && git checkout initial" &&
test_must_fail git merge --ff-only --verify-signatures side-untrusted 2>mergeerror &&
test_i18ngrep "has an untrusted GPG signature" mergeerror
'
+test_expect_success GPG 'merge commit with untrusted signature with merge.verifySignatures=true' '
+ test_when_finished "git reset --hard && git checkout initial" &&
+ test_config merge.verifySignatures true &&
+ test_must_fail git merge --ff-only side-untrusted 2>mergeerror &&
+ test_i18ngrep "has an untrusted GPG signature" mergeerror
+'
+
test_expect_success GPG 'merge signed commit with verification' '
+ test_when_finished "git reset --hard && git checkout initial" &&
git merge --verbose --ff-only --verify-signatures side-signed >mergeoutput &&
test_i18ngrep "has a good GPG signature" mergeoutput
'
+test_expect_success GPG 'merge signed commit with merge.verifySignatures=true' '
+ test_when_finished "git reset --hard && git checkout initial" &&
+ test_config merge.verifySignatures true &&
+ git merge --verbose --ff-only side-signed >mergeoutput &&
+ test_i18ngrep "has a good GPG signature" mergeoutput
+'
+
test_expect_success GPG 'merge commit with bad signature without verification' '
+ test_when_finished "git reset --hard && git checkout initial" &&
+ git merge $(cat forged.commit)
+'
+
+test_expect_success GPG 'merge commit with bad signature with merge.verifySignatures=false' '
+ test_when_finished "git reset --hard && git checkout initial" &&
+ test_config merge.verifySignatures false &&
git merge $(cat forged.commit)
'
+test_expect_success GPG 'merge commit with bad signature with merge.verifySignatures=true and --no-verify-signatures' '
+ test_when_finished "git reset --hard && git checkout initial" &&
+ test_config merge.verifySignatures true &&
+ git merge --no-verify-signatures $(cat forged.commit)
+'
+
test_done
diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh
index 2a6679c2f5..1797f632a3 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)
@@ -1110,6 +1131,12 @@ test_expect_success PCRE 'grep -P pattern' '
test_cmp expected actual
'
+test_expect_success LIBPCRE2 "grep -P with (*NO_JIT) doesn't error out" '
+ git grep -P "(*NO_JIT)\p{Ps}.*?\p{Pe}" hello.c >actual &&
+ test_cmp expected actual
+
+'
+
test_expect_success !PCRE 'grep -P pattern errors without PCRE' '
test_must_fail git grep -P "foo.*bar"
'
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index 4d261c2a9c..81869d8913 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -6,6 +6,12 @@ test_description='git send-email'
# May be altered later in the test
PREREQ="PERL"
+replace_variable_fields () {
+ sed -e "s/^\(Date:\).*/\1 DATE-STRING/" \
+ -e "s/^\(Message-Id:\).*/\1 MESSAGE-ID-STRING/" \
+ -e "s/^\(X-Mailer:\).*/\1 X-MAILER-STRING/"
+}
+
test_expect_success $PREREQ 'prepare reference tree' '
echo "1A quick brown fox jumps over the" >file &&
echo "lazy dog" >>file &&
@@ -296,10 +302,7 @@ test_expect_success $PREREQ 'Show all headers' '
--bcc=bcc@example.com \
--in-reply-to="<unique-message-id@example.com>" \
--smtp-server relay.example.com \
- $patches |
- sed -e "s/^\(Date:\).*/\1 DATE-STRING/" \
- -e "s/^\(Message-Id:\).*/\1 MESSAGE-ID-STRING/" \
- -e "s/^\(X-Mailer:\).*/\1 X-MAILER-STRING/" \
+ $patches | replace_variable_fields \
>actual-show-all-headers &&
test_cmp expected-show-all-headers actual-show-all-headers
'
@@ -554,12 +557,6 @@ Result: OK
EOF
"
-replace_variable_fields () {
- sed -e "s/^\(Date:\).*/\1 DATE-STRING/" \
- -e "s/^\(Message-Id:\).*/\1 MESSAGE-ID-STRING/" \
- -e "s/^\(X-Mailer:\).*/\1 X-MAILER-STRING/"
-}
-
test_suppression () {
git send-email \
--dry-run \
diff --git a/t/t9004-example.sh b/t/t9004-example.sh
new file mode 100755
index 0000000000..b28a028f55
--- /dev/null
+++ b/t/t9004-example.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+test_description='check that example code compiles and runs'
+. ./test-lib.sh
+
+test_expect_success 'decorate' '
+ test-example-decorate
+'
+
+test_done
diff --git a/t/t9020-remote-svn.sh b/t/t9020-remote-svn.sh
index 4d81ba1c2c..6fca08e5e3 100755
--- a/t/t9020-remote-svn.sh
+++ b/t/t9020-remote-svn.sh
@@ -25,8 +25,8 @@ init_git () {
git init &&
#git remote add svnsim testsvn::sim:///$TEST_DIRECTORY/t9020/example.svnrdump
# let's reuse an existing dump file!?
- git remote add svnsim testsvn::sim://$TEST_DIRECTORY/t9154/svn.dump
- git remote add svnfile testsvn::file://$TEST_DIRECTORY/t9154/svn.dump
+ git remote add svnsim "testsvn::sim://$TEST_DIRECTORY/t9154/svn.dump"
+ git remote add svnfile "testsvn::file://$TEST_DIRECTORY/t9154/svn.dump"
}
if test -e "$GIT_BUILD_DIR/git-remote-testsvn"
diff --git a/t/t9107-git-svn-migrate.sh b/t/t9107-git-svn-migrate.sh
index 9f3ef8f2ef..ceaa5bad10 100755
--- a/t/t9107-git-svn-migrate.sh
+++ b/t/t9107-git-svn-migrate.sh
@@ -28,7 +28,7 @@ test_expect_success 'git-svn-HEAD is a real HEAD' '
git rev-parse --verify refs/heads/git-svn-HEAD^0
'
-svnrepo_escaped=$(echo $svnrepo | sed 's/ /%20/')
+svnrepo_escaped=$(echo $svnrepo | sed 's/ /%20/g')
test_expect_success 'initialize old-style (v0) git svn layout' '
mkdir -p "$GIT_DIR"/git-svn/info "$GIT_DIR"/svn/info &&
diff --git a/t/t9169-git-svn-dcommit-crlf.sh b/t/t9169-git-svn-dcommit-crlf.sh
new file mode 100755
index 0000000000..54b1f61a2a
--- /dev/null
+++ b/t/t9169-git-svn-dcommit-crlf.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+test_description='git svn dcommit CRLF'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup commit repository' '
+ svn_cmd mkdir -m "$test_description" "$svnrepo/dir" &&
+ git svn clone "$svnrepo" work &&
+ (
+ cd work &&
+ echo foo >>foo &&
+ git update-index --add foo &&
+ printf "a\\r\\n\\r\\nb\\r\\nc\\r\\n" >cmt &&
+ p=$(git rev-parse HEAD) &&
+ t=$(git write-tree) &&
+ cmt=$(git commit-tree -p $p $t <cmt) &&
+ git update-ref refs/heads/master $cmt &&
+ git cat-file commit HEAD | tail -n4 >out &&
+ test_cmp cmt out &&
+ git svn dcommit &&
+ printf "a\\n\\nb\\nc\\n" >exp &&
+ git cat-file commit HEAD | sed -ne 6,9p >out &&
+ test_cmp exp out
+ )
+'
+
+test_done
diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
index d47560b634..e4d06accc4 100755
--- a/t/t9300-fast-import.sh
+++ b/t/t9300-fast-import.sh
@@ -876,7 +876,7 @@ test_expect_success 'L: verify internal tree sorting' '
EXPECT_END
git fast-import <input &&
- git diff-tree --abbrev --raw L^ L >output &&
+ GIT_PRINT_SHA1_ELLIPSIS="yes" git diff-tree --abbrev --raw L^ L >output &&
test_cmp expect output
'
diff --git a/t/t9807-git-p4-submit.sh b/t/t9807-git-p4-submit.sh
index 3457d5db64..71cae2874d 100755
--- a/t/t9807-git-p4-submit.sh
+++ b/t/t9807-git-p4-submit.sh
@@ -460,7 +460,13 @@ test_expect_success 'submit --shelve' '
)
'
-# Update an existing shelved changelist
+make_shelved_cl() {
+ test_commit "$1" >/dev/null &&
+ git p4 submit --origin HEAD^ --shelve >/dev/null &&
+ p4 -G changes -s shelved -m 1 | marshal_dump change
+}
+
+# Update existing shelved changelists
test_expect_success 'submit --update-shelve' '
test_when_finished cleanup_git &&
@@ -470,21 +476,19 @@ test_expect_success 'submit --update-shelve' '
p4 revert ... &&
cd "$git" &&
git config git-p4.skipSubmitEdit true &&
- test_commit "test-update-shelved-change" &&
- git p4 submit --origin=HEAD^ --shelve &&
+ shelved_cl0=$(make_shelved_cl "shelved-change-0") &&
+ echo shelved_cl0=$shelved_cl0 &&
+ shelved_cl1=$(make_shelved_cl "shelved-change-1") &&
- shelf_cl=$(p4 -G changes -s shelved -m 1 |\
- marshal_dump change) &&
- test -n $shelf_cl &&
- echo "updating shelved change list $shelf_cl" &&
+ echo "updating shelved change lists $shelved_cl0 and $shelved_cl1" &&
echo "updated-line" >>shelf.t &&
echo added-file.t >added-file.t &&
git add shelf.t added-file.t &&
- git rm -f test-update-shelved-change.t &&
+ git rm -f shelved-change-1.t &&
git commit --amend -C HEAD &&
git show --stat HEAD &&
- git p4 submit -v --origin HEAD^ --update-shelve $shelf_cl &&
+ git p4 submit -v --origin HEAD~2 --update-shelve $shelved_cl0 --update-shelve $shelved_cl1 &&
echo "done git p4 submit"
) &&
(
@@ -494,7 +498,7 @@ test_expect_success 'submit --update-shelve' '
p4 unshelve -c $change -s $change &&
grep -q updated-line shelf.t &&
p4 describe -S $change | grep added-file.t &&
- test_path_is_missing test-update-shelved-change.t
+ test_path_is_missing shelved-change-1.t
)
'
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 1701fe2a06..8a8a9329ee 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -610,6 +610,14 @@ list_contains () {
#
# Writing this as "! git checkout ../outerspace" is wrong, because
# the failure could be due to a segv. We want a controlled failure.
+#
+# Accepts the following options:
+#
+# ok=<signal-name>[,<...>]:
+# Don't treat an exit caused by the given signal as error.
+# Multiple signals can be specified as a comma separated list.
+# Currently recognized signal names are: sigpipe, success.
+# (Don't use 'success', use 'test_might_fail' instead.)
test_must_fail () {
case "$1" in
@@ -656,6 +664,8 @@ test_must_fail () {
#
# Writing "git config --unset all.configuration || :" would be wrong,
# because we want to notice if it fails due to segv.
+#
+# Accepts the same options as test_must_fail.
test_might_fail () {
test_must_fail ok=success "$@"
@@ -705,6 +715,60 @@ test_cmp_bin() {
cmp "$@"
}
+# Use this instead of test_cmp to compare files that contain expected and
+# actual output from git commands that can be translated. When running
+# under GETTEXT_POISON this pretends that the command produced expected
+# results.
+test_i18ncmp () {
+ test -n "$GETTEXT_POISON" || test_cmp "$@"
+}
+
+# Use this instead of "grep expected-string actual" to see if the
+# output from a git command that can be translated either contains an
+# expected string, or does not contain an unwanted one. When running
+# under GETTEXT_POISON this pretends that the command produced expected
+# results.
+test_i18ngrep () {
+ eval "last_arg=\${$#}"
+
+ test -f "$last_arg" ||
+ error "bug in the test script: test_i18ngrep requires a file" \
+ "to read as the last parameter"
+
+ if test $# -lt 2 ||
+ { test "x!" = "x$1" && test $# -lt 3 ; }
+ then
+ error "bug in the test script: too few parameters to test_i18ngrep"
+ fi
+
+ if test -n "$GETTEXT_POISON"
+ then
+ # pretend success
+ return 0
+ fi
+
+ if test "x!" = "x$1"
+ then
+ shift
+ ! grep "$@" && return 0
+
+ echo >&2 "error: '! grep $@' did find a match in:"
+ else
+ grep "$@" && return 0
+
+ echo >&2 "error: 'grep $@' didn't find a match in:"
+ fi
+
+ if test -s "$last_arg"
+ then
+ cat >&2 "$last_arg"
+ else
+ echo >&2 "<File '$last_arg' is empty>"
+ fi
+
+ return 1
+}
+
# Call any command "$@" but be more verbose about its
# failure. This is handy for commands like "test" which do
# not output anything when they fail.
@@ -1020,3 +1084,37 @@ nongit () {
"$@"
)
}
+
+# convert stdin to pktline representation; note that empty input becomes an
+# empty packet, not a flush packet (for that you can just print 0000 yourself).
+packetize() {
+ cat >packetize.tmp &&
+ len=$(wc -c <packetize.tmp) &&
+ printf '%04x%s' "$(($len + 4))" &&
+ cat packetize.tmp &&
+ rm -f packetize.tmp
+}
+
+# Parse the input as a series of pktlines, writing the result to stdout.
+# Sideband markers are removed automatically, and the output is routed to
+# stderr if appropriate.
+#
+# NUL bytes are converted to "\\0" for ease of parsing with text tools.
+depacketize () {
+ perl -e '
+ while (read(STDIN, $len, 4) == 4) {
+ if ($len eq "0000") {
+ print "FLUSH\n";
+ } else {
+ read(STDIN, $buf, hex($len) - 4);
+ $buf =~ s/\0/\\0/g;
+ if ($buf =~ s/^[\x2\x3]//) {
+ print STDERR $buf;
+ } else {
+ $buf =~ s/^\x1//;
+ print $buf;
+ }
+ }
+ }
+ '
+}
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 116bd6a70c..816e692391 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -80,7 +80,7 @@ done,*)
# from any previous runs.
>"$GIT_TEST_TEE_OUTPUT_FILE"
- (GIT_TEST_TEE_STARTED=done ${SHELL_PATH} "$0" "$@" 2>&1;
+ (GIT_TEST_TEE_STARTED=done ${TEST_SHELL_PATH} "$0" "$@" 2>&1;
echo $? >"$BASE.exit") | tee -a "$GIT_TEST_TEE_OUTPUT_FILE"
test "$(cat "$BASE.exit")" = 0
exit
@@ -116,6 +116,7 @@ unset VISUAL EMAIL LANGUAGE COLUMNS $("$PERL_PATH" -e '
my @vars = grep(/^GIT_/ && !/^GIT_($ok)/o, @env);
print join("\n", @vars);
')
+unset XDG_CACHE_HOME
unset XDG_CONFIG_HOME
unset GITPERLLIB
GIT_AUTHOR_EMAIL=author@example.com
@@ -264,7 +265,6 @@ do
shift ;;
-x)
trace=t
- verbose=t
shift ;;
--verbose-log)
verbose_log=t
@@ -283,6 +283,11 @@ then
test -z "$verbose_log" && verbose=t
fi
+if test -n "$trace" && test -z "$verbose_log"
+then
+ verbose=t
+fi
+
if test -n "$color"
then
# Save the color control sequences now rather than run tput
@@ -586,7 +591,9 @@ maybe_setup_valgrind () {
}
want_trace () {
- test "$trace" = t && test "$verbose" = t
+ test "$trace" = t && {
+ test "$verbose" = t || test "$verbose_log" = t
+ }
}
# This is a separate function because some tests use
@@ -601,26 +608,40 @@ test_eval_inner_ () {
}
test_eval_ () {
- # We run this block with stderr redirected to avoid extra cruft
- # during a "-x" trace. Once in "set -x" mode, we cannot prevent
+ # If "-x" tracing is in effect, then we want to avoid polluting stderr
+ # with non-test commands. But once in "set -x" mode, we cannot prevent
# the shell from printing the "set +x" to turn it off (nor the saving
# of $? before that). But we can make sure that the output goes to
# /dev/null.
#
- # The test itself is run with stderr put back to &4 (so either to
- # /dev/null, or to the original stderr if --verbose was used).
+ # There are a few subtleties here:
+ #
+ # - we have to redirect descriptor 4 in addition to 2, to cover
+ # BASH_XTRACEFD
+ #
+ # - the actual eval has to come before the redirection block (since
+ # it needs to see descriptor 4 to set up its stderr)
+ #
+ # - likewise, any error message we print must be outside the block to
+ # access descriptor 4
+ #
+ # - checking $? has to come immediately after the eval, but it must
+ # be _inside_ the block to avoid polluting the "set -x" output
+ #
+
+ test_eval_inner_ "$@" </dev/null >&3 2>&4
{
- test_eval_inner_ "$@" </dev/null >&3 2>&4
test_eval_ret_=$?
if want_trace
then
set +x
- if test "$test_eval_ret_" != 0
- then
- say_color error >&4 "error: last command exited with \$?=$test_eval_ret_"
- fi
fi
- } 2>/dev/null
+ } 2>/dev/null 4>&2
+
+ if test "$test_eval_ret_" != 0 && want_trace
+ then
+ say_color error >&4 "error: last command exited with \$?=$test_eval_ret_"
+ fi
return $test_eval_ret_
}
@@ -1028,6 +1049,8 @@ test -z "$NO_PERL" && test_set_prereq PERL
test -z "$NO_PTHREADS" && test_set_prereq PTHREADS
test -z "$NO_PYTHON" && test_set_prereq PYTHON
test -n "$USE_LIBPCRE1$USE_LIBPCRE2" && test_set_prereq PCRE
+test -n "$USE_LIBPCRE1" && test_set_prereq LIBPCRE1
+test -n "$USE_LIBPCRE2" && test_set_prereq LIBPCRE2
test -z "$NO_GETTEXT" && test_set_prereq GETTEXT
# Can we rely on git's output in the C locale?
@@ -1040,32 +1063,6 @@ else
test_set_prereq C_LOCALE_OUTPUT
fi
-# Use this instead of test_cmp to compare files that contain expected and
-# actual output from git commands that can be translated. When running
-# under GETTEXT_POISON this pretends that the command produced expected
-# results.
-test_i18ncmp () {
- test -n "$GETTEXT_POISON" || test_cmp "$@"
-}
-
-# Use this instead of "grep expected-string actual" to see if the
-# output from a git command that can be translated either contains an
-# expected string, or does not contain an unwanted one. When running
-# under GETTEXT_POISON this pretends that the command produced expected
-# results.
-test_i18ngrep () {
- if test -n "$GETTEXT_POISON"
- then
- : # pretend success
- elif test "x!" = "x$1"
- then
- shift
- ! grep "$@"
- else
- grep "$@"
- fi
-}
-
test_lazy_prereq PIPE '
# test whether the filesystem supports FIFOs
test_have_prereq !MINGW,!CYGWIN &&