aboutsummaryrefslogtreecommitdiffstats
path: root/t
diff options
context:
space:
mode:
Diffstat (limited to 't')
-rw-r--r--t/helper/test-fast-rebase.c23
-rw-r--r--t/helper/test-fsmonitor-client.c106
-rw-r--r--t/helper/test-hexdump.c30
-rw-r--r--t/helper/test-pack-mtimes.c56
-rw-r--r--t/helper/test-revision-walking.c1
-rw-r--r--t/helper/test-run-command.c2
-rw-r--r--t/helper/test-tool.c2
-rw-r--r--t/helper/test-tool.h2
-rw-r--r--t/helper/test-trace2.c29
-rw-r--r--t/lib-git-svn.sh4
-rwxr-xr-xt/lib-unicode-nfc-nfd.sh162
-rw-r--r--t/lib-unique-files.sh34
-rwxr-xr-xt/perf/p0008-odb-fsync.sh82
-rwxr-xr-xt/perf/p2000-sparse-operations.sh1
-rwxr-xr-xt/perf/p4220-log-grep-engines.sh3
-rwxr-xr-xt/perf/p4221-log-grep-engines-fixed.sh3
-rwxr-xr-xt/perf/p5302-pack-index.sh15
-rwxr-xr-xt/perf/p7519-fsmonitor.sh18
-rwxr-xr-xt/perf/p7527-builtin-fsmonitor.sh257
-rwxr-xr-xt/perf/p7820-grep-engines.sh6
-rw-r--r--t/perf/perf-lib.sh63
-rwxr-xr-xt/t0056-git-C.sh1
-rwxr-xr-xt/t0062-revision-walking.sh1
-rwxr-xr-xt/t0100-previous.sh1
-rwxr-xr-xt/t0101-at-syntax.sh2
-rwxr-xr-xt/t0210-trace2-normal.sh76
-rwxr-xr-xt/t1001-read-tree-m-2way.sh1
-rwxr-xr-xt/t1002-read-tree-m-u-2way.sh1
-rwxr-xr-xt/t1060-object-corruption.sh2
-rwxr-xr-xt/t1091-sparse-checkout-builtin.sh15
-rwxr-xr-xt/t1092-sparse-checkout-compatibility.sh95
-rwxr-xr-xt/t1401-symbolic-ref.sh2
-rwxr-xr-xt/t1411-reflog-show.sh1
-rwxr-xr-xt/t1412-reflog-loop.sh2
-rwxr-xr-xt/t1415-worktree-refs.sh1
-rwxr-xr-xt/t2015-checkout-unborn.sh1
-rwxr-xr-xt/t2200-add-update.sh1
-rwxr-xr-xt/t3302-notes-index-expensive.sh1
-rwxr-xr-xt/t3303-notes-subtrees.sh1
-rwxr-xr-xt/t3305-notes-fanout.sh1
-rwxr-xr-xt/t3408-rebase-multi-line.sh1
-rwxr-xr-xt/t3602-rm-sparse-checkout.sh6
-rwxr-xr-xt/t3700-add.sh28
-rwxr-xr-xt/t3705-add-sparse-checkout.sh4
-rwxr-xr-xt/t3903-stash.sh20
-rwxr-xr-xt/t4021-format-patch-numbered.sh1
-rwxr-xr-xt/t4027-diff-submodule.sh1
-rwxr-xr-xt/t4028-format-patch-mime-headers.sh2
-rwxr-xr-xt/t4036-format-patch-signer-mime.sh1
-rwxr-xr-xt/t4039-diff-assume-unchanged.sh1
-rwxr-xr-xt/t4055-diff-context.sh1
-rwxr-xr-xt/t4066-diff-emit-delay.sh1
-rwxr-xr-xt/t4122-apply-symlink-inside.sh1
-rwxr-xr-xt/t4126-apply-empty.sh1
-rwxr-xr-xt/t4128-apply-root.sh1
-rwxr-xr-xt/t4206-log-follow-harder-copies.sh2
-rwxr-xr-xt/t4207-log-decoration-colors.sh1
-rwxr-xr-xt/t4212-log-corrupt.sh1
-rwxr-xr-xt/t5003-archive-zip.sh20
-rwxr-xr-xt/t5300-pack-object.sh41
-rwxr-xr-xt/t5301-sliding-window.sh2
-rwxr-xr-xt/t5313-pack-bounds-checks.sh2
-rwxr-xr-xt/t5316-pack-delta-depth.sh2
-rwxr-xr-xt/t5317-pack-objects-filter-objects.sh91
-rwxr-xr-xt/t5320-delta-islands.sh2
-rwxr-xr-xt/t5322-pack-objects-sparse.sh1
-rwxr-xr-xt/t5329-pack-objects-cruft.sh739
-rwxr-xr-xt/t5506-remote-groups.sh1
-rwxr-xr-xt/t5513-fetch-track.sh1
-rwxr-xr-xt/t5515-fetch-merge-logic.sh1
-rwxr-xr-xt/t5516-fetch-push.sh20
-rwxr-xr-xt/t5518-fetch-exit-status.sh1
-rwxr-xr-xt/t5532-fetch-proxy.sh2
-rwxr-xr-xt/t5600-clone-fail-cleanup.sh1
-rwxr-xr-xt/t5900-repo-selection.sh2
-rwxr-xr-xt/t6002-rev-list-bisect.sh1
-rwxr-xr-xt/t6003-rev-list-topo-order.sh1
-rwxr-xr-xt/t6005-rev-list-count.sh1
-rwxr-xr-xt/t6018-rev-list-glob.sh1
-rwxr-xr-xt/t6100-rev-list-in-order.sh1
-rwxr-xr-xt/t6101-rev-parse-parents.sh1
-rwxr-xr-xt/t6110-rev-list-sparse.sh1
-rwxr-xr-xt/t6114-keep-packs.sh2
-rwxr-xr-xt/t6131-pathspec-icase.sh2
-rwxr-xr-xt/t6132-pathspec-exclude.sh181
-rwxr-xr-xt/t6428-merge-conflicts-sparse.sh4
-rwxr-xr-xt/t7002-mv-sparse-checkout.sh2
-rwxr-xr-xt/t7008-filter-branch-null-sha1.sh1
-rwxr-xr-xt/t7012-skip-worktree-writing.sh2
-rwxr-xr-xt/t7519-status-fsmonitor.sh32
-rwxr-xr-xt/t7527-builtin-fsmonitor.sh401
-rwxr-xr-xt/t7702-repack-cyclic-alternate.sh2
-rwxr-xr-xt/t7703-repack-geometric.sh75
-rwxr-xr-xt/t9001-send-email.sh1
-rwxr-xr-xt/t9100-git-svn-basic.sh1
-rwxr-xr-xt/t9101-git-svn-props.sh2
-rwxr-xr-xt/t9104-git-svn-follow-parent.sh2
-rwxr-xr-xt/t9106-git-svn-commit-diff-clobber.sh2
-rwxr-xr-xt/t9115-git-svn-dcommit-funky-renames.sh1
-rwxr-xr-xt/t9116-git-svn-log.sh1
-rwxr-xr-xt/t9122-git-svn-author.sh2
-rwxr-xr-xt/t9127-git-svn-partial-rebuild.sh1
-rwxr-xr-xt/t9129-git-svn-i18n-commitencoding.sh1
-rwxr-xr-xt/t9132-git-svn-broken-symlink.sh1
-rwxr-xr-xt/t9139-git-svn-non-utf8-commitencoding.sh1
-rwxr-xr-xt/t9146-git-svn-empty-dirs.sh2
-rwxr-xr-xt/t9148-git-svn-propset.sh1
-rwxr-xr-xt/t9160-git-svn-preserve-empty-dirs.sh1
-rwxr-xr-xt/t9162-git-svn-dcommit-interactive.sh2
-rwxr-xr-xt/t9164-git-svn-dcommit-concurrent.sh2
-rwxr-xr-xt/t9501-gitweb-standalone-http-status.sh1
-rwxr-xr-xt/t9502-gitweb-standalone-parse-output.sh14
-rw-r--r--t/test-lib-functions.sh16
-rw-r--r--t/test-lib-github-workflow-markup.sh56
-rw-r--r--t/test-lib-junit.sh132
-rw-r--r--t/test-lib.sh128
116 files changed, 2935 insertions, 255 deletions
diff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c
index fc2d460904..4e5553e202 100644
--- a/t/helper/test-fast-rebase.c
+++ b/t/helper/test-fast-rebase.c
@@ -99,6 +99,7 @@ int cmd__fast_rebase(int argc, const char **argv)
struct merge_result result;
struct strbuf reflog_msg = STRBUF_INIT;
struct strbuf branch_name = STRBUF_INIT;
+ int ret = 0;
/*
* test-tool stuff doesn't set up the git directory by default; need to
@@ -137,13 +138,17 @@ int cmd__fast_rebase(int argc, const char **argv)
revs.topo_order = 1;
strvec_pushl(&rev_walk_args, "", argv[4], "--not", argv[3], NULL);
- if (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1)
- return error(_("unhandled options"));
+ if (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1) {
+ ret = error(_("unhandled options"));
+ goto cleanup;
+ }
strvec_clear(&rev_walk_args);
- if (prepare_revision_walk(&revs) < 0)
- return error(_("error preparing revisions"));
+ if (prepare_revision_walk(&revs) < 0) {
+ ret = error(_("error preparing revisions"));
+ goto cleanup;
+ }
init_merge_options(&merge_opt, the_repository);
memset(&result, 0, sizeof(result));
@@ -201,8 +206,6 @@ int cmd__fast_rebase(int argc, const char **argv)
}
if (create_symref("HEAD", branch_name.buf, reflog_msg.buf) < 0)
die(_("unable to update HEAD"));
- strbuf_release(&reflog_msg);
- strbuf_release(&branch_name);
prime_cache_tree(the_repository, the_repository->index,
result.tree);
@@ -221,5 +224,11 @@ int cmd__fast_rebase(int argc, const char **argv)
if (write_locked_index(&the_index, &lock,
COMMIT_LOCK | SKIP_IF_UNCHANGED))
die(_("unable to write %s"), get_index_file());
- return (result.clean == 0);
+
+ ret = (result.clean == 0);
+cleanup:
+ strbuf_release(&reflog_msg);
+ strbuf_release(&branch_name);
+ release_revisions(&revs);
+ return ret;
}
diff --git a/t/helper/test-fsmonitor-client.c b/t/helper/test-fsmonitor-client.c
index 3062c8a3c2..54a4856c48 100644
--- a/t/helper/test-fsmonitor-client.c
+++ b/t/helper/test-fsmonitor-client.c
@@ -7,6 +7,8 @@
#include "cache.h"
#include "parse-options.h"
#include "fsmonitor-ipc.h"
+#include "thread-utils.h"
+#include "trace2.h"
#ifndef HAVE_FSMONITOR_DAEMON_BACKEND
int cmd__fsmonitor_client(int argc, const char **argv)
@@ -79,20 +81,121 @@ static int do_send_flush(void)
return 0;
}
+struct hammer_thread_data
+{
+ pthread_t pthread_id;
+ int thread_nr;
+
+ int nr_requests;
+ const char *token;
+
+ int sum_successful;
+ int sum_errors;
+};
+
+static void *hammer_thread_proc(void *_hammer_thread_data)
+{
+ struct hammer_thread_data *data = _hammer_thread_data;
+ struct strbuf answer = STRBUF_INIT;
+ int k;
+ int ret;
+
+ trace2_thread_start("hammer");
+
+ for (k = 0; k < data->nr_requests; k++) {
+ strbuf_reset(&answer);
+
+ ret = fsmonitor_ipc__send_query(data->token, &answer);
+ if (ret < 0)
+ data->sum_errors++;
+ else
+ data->sum_successful++;
+ }
+
+ strbuf_release(&answer);
+ trace2_thread_exit();
+ return NULL;
+}
+
+/*
+ * Start a pool of client threads that will each send a series of
+ * commands to the daemon.
+ *
+ * The goal is to overload the daemon with a sustained series of
+ * concurrent requests.
+ */
+static int do_hammer(const char *token, int nr_threads, int nr_requests)
+{
+ struct hammer_thread_data *data = NULL;
+ int k;
+ int sum_join_errors = 0;
+ int sum_commands = 0;
+ int sum_errors = 0;
+
+ if (!token || !*token)
+ token = get_token_from_index();
+ if (nr_threads < 1)
+ nr_threads = 1;
+ if (nr_requests < 1)
+ nr_requests = 1;
+
+ CALLOC_ARRAY(data, nr_threads);
+
+ for (k = 0; k < nr_threads; k++) {
+ struct hammer_thread_data *p = &data[k];
+ p->thread_nr = k;
+ p->nr_requests = nr_requests;
+ p->token = token;
+
+ if (pthread_create(&p->pthread_id, NULL, hammer_thread_proc, p)) {
+ warning("failed to create thread[%d] skipping remainder", k);
+ nr_threads = k;
+ break;
+ }
+ }
+
+ for (k = 0; k < nr_threads; k++) {
+ struct hammer_thread_data *p = &data[k];
+
+ if (pthread_join(p->pthread_id, NULL))
+ sum_join_errors++;
+ sum_commands += p->sum_successful;
+ sum_errors += p->sum_errors;
+ }
+
+ fprintf(stderr, "HAMMER: [threads %d][requests %d] [ok %d][err %d][join %d]\n",
+ nr_threads, nr_requests, sum_commands, sum_errors, sum_join_errors);
+
+ free(data);
+
+ /*
+ * Return an error if any of the _send_query requests failed.
+ * We don't care about thread create/join errors.
+ */
+ return sum_errors > 0;
+}
+
int cmd__fsmonitor_client(int argc, const char **argv)
{
const char *subcmd;
const char *token = NULL;
+ int nr_threads = 1;
+ int nr_requests = 1;
const char * const fsmonitor_client_usage[] = {
"test-tool fsmonitor-client query [<token>]",
"test-tool fsmonitor-client flush",
+ "test-tool fsmonitor-client hammer [<token>] [<threads>] [<requests>]",
NULL,
};
struct option options[] = {
OPT_STRING(0, "token", &token, "token",
"command token to send to the server"),
+
+ OPT_INTEGER(0, "threads", &nr_threads, "number of client threads"),
+ OPT_INTEGER(0, "requests", &nr_requests, "number of requests per thread"),
+
OPT_END()
};
@@ -111,6 +214,9 @@ int cmd__fsmonitor_client(int argc, const char **argv)
if (!strcmp(subcmd, "flush"))
return !!do_send_flush();
+ if (!strcmp(subcmd, "hammer"))
+ return !!do_hammer(token, nr_threads, nr_requests);
+
die("Unhandled subcommand: '%s'", subcmd);
}
#endif
diff --git a/t/helper/test-hexdump.c b/t/helper/test-hexdump.c
new file mode 100644
index 0000000000..811e89c1bc
--- /dev/null
+++ b/t/helper/test-hexdump.c
@@ -0,0 +1,30 @@
+#include "test-tool.h"
+#include "git-compat-util.h"
+
+/*
+ * Read stdin and print a hexdump to stdout.
+ */
+int cmd__hexdump(int argc, const char **argv)
+{
+ char buf[1024];
+ ssize_t i, len;
+ int have_data = 0;
+
+ for (;;) {
+ len = xread(0, buf, sizeof(buf));
+ if (len < 0)
+ die_errno("failure reading stdin");
+ if (!len)
+ break;
+
+ have_data = 1;
+
+ for (i = 0; i < len; i++)
+ printf("%02x ", (unsigned char)buf[i]);
+ }
+
+ if (have_data)
+ putchar('\n');
+
+ return 0;
+}
diff --git a/t/helper/test-pack-mtimes.c b/t/helper/test-pack-mtimes.c
new file mode 100644
index 0000000000..f7b79daf4c
--- /dev/null
+++ b/t/helper/test-pack-mtimes.c
@@ -0,0 +1,56 @@
+#include "git-compat-util.h"
+#include "test-tool.h"
+#include "strbuf.h"
+#include "object-store.h"
+#include "packfile.h"
+#include "pack-mtimes.h"
+
+static void dump_mtimes(struct packed_git *p)
+{
+ uint32_t i;
+ if (load_pack_mtimes(p) < 0)
+ die("could not load pack .mtimes");
+
+ for (i = 0; i < p->num_objects; i++) {
+ struct object_id oid;
+ if (nth_packed_object_id(&oid, p, i) < 0)
+ die("could not load object id at position %"PRIu32, i);
+
+ printf("%s %"PRIu32"\n",
+ oid_to_hex(&oid), nth_packed_mtime(p, i));
+ }
+}
+
+static const char *pack_mtimes_usage = "\n"
+" test-tool pack-mtimes <pack-name.mtimes>";
+
+int cmd__pack_mtimes(int argc, const char **argv)
+{
+ struct strbuf buf = STRBUF_INIT;
+ struct packed_git *p;
+
+ setup_git_directory();
+
+ if (argc != 2)
+ usage(pack_mtimes_usage);
+
+ for (p = get_all_packs(the_repository); p; p = p->next) {
+ strbuf_addstr(&buf, basename(p->pack_name));
+ strbuf_strip_suffix(&buf, ".pack");
+ strbuf_addstr(&buf, ".mtimes");
+
+ if (!strcmp(buf.buf, argv[1]))
+ break;
+
+ strbuf_reset(&buf);
+ }
+
+ strbuf_release(&buf);
+
+ if (!p)
+ die("could not find pack '%s'", argv[1]);
+
+ dump_mtimes(p);
+
+ return 0;
+}
diff --git a/t/helper/test-revision-walking.c b/t/helper/test-revision-walking.c
index 625b2dbf82..4a45d5bac2 100644
--- a/t/helper/test-revision-walking.c
+++ b/t/helper/test-revision-walking.c
@@ -43,6 +43,7 @@ static int run_revision_walk(void)
}
reset_revision_walk();
+ release_revisions(&rev);
return got_revision;
}
diff --git a/t/helper/test-run-command.c b/t/helper/test-run-command.c
index f3b90aa834..9050e3113c 100644
--- a/t/helper/test-run-command.c
+++ b/t/helper/test-run-command.c
@@ -390,7 +390,7 @@ int cmd__run_command(int argc, const char **argv)
while (!strcmp(argv[1], "env")) {
if (!argv[2])
die("env specifier without a value");
- strvec_push(&proc.env_array, argv[2]);
+ strvec_push(&proc.env, argv[2]);
argv += 2;
argc -= 2;
}
diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c
index 0424f7adf5..318fdbab0c 100644
--- a/t/helper/test-tool.c
+++ b/t/helper/test-tool.c
@@ -38,6 +38,7 @@ static struct test_cmd cmds[] = {
{ "getcwd", cmd__getcwd },
{ "hashmap", cmd__hashmap },
{ "hash-speed", cmd__hash_speed },
+ { "hexdump", cmd__hexdump },
{ "index-version", cmd__index_version },
{ "json-writer", cmd__json_writer },
{ "lazy-init-name-hash", cmd__lazy_init_name_hash },
@@ -48,6 +49,7 @@ static struct test_cmd cmds[] = {
{ "oidmap", cmd__oidmap },
{ "oidtree", cmd__oidtree },
{ "online-cpus", cmd__online_cpus },
+ { "pack-mtimes", cmd__pack_mtimes },
{ "parse-options", cmd__parse_options },
{ "parse-pathspec-file", cmd__parse_pathspec_file },
{ "partial-clone", cmd__partial_clone },
diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h
index c876e8246f..bb79927163 100644
--- a/t/helper/test-tool.h
+++ b/t/helper/test-tool.h
@@ -29,6 +29,7 @@ int cmd__genzeros(int argc, const char **argv);
int cmd__getcwd(int argc, const char **argv);
int cmd__hashmap(int argc, const char **argv);
int cmd__hash_speed(int argc, const char **argv);
+int cmd__hexdump(int argc, const char **argv);
int cmd__index_version(int argc, const char **argv);
int cmd__json_writer(int argc, const char **argv);
int cmd__lazy_init_name_hash(int argc, const char **argv);
@@ -38,6 +39,7 @@ int cmd__mktemp(int argc, const char **argv);
int cmd__oidmap(int argc, const char **argv);
int cmd__oidtree(int argc, const char **argv);
int cmd__online_cpus(int argc, const char **argv);
+int cmd__pack_mtimes(int argc, const char **argv);
int cmd__parse_options(int argc, const char **argv);
int cmd__parse_pathspec_file(int argc, const char** argv);
int cmd__partial_clone(int argc, const char **argv);
diff --git a/t/helper/test-trace2.c b/t/helper/test-trace2.c
index 59b124bb5f..180c7f53f3 100644
--- a/t/helper/test-trace2.c
+++ b/t/helper/test-trace2.c
@@ -198,7 +198,7 @@ static int ut_006data(int argc, const char **argv)
return 0;
}
-static int ut_007bug(int argc, const char **argv)
+static int ut_007BUG(int argc, const char **argv)
{
/*
* Exercise BUG() to ensure that the message is printed to trace2.
@@ -206,6 +206,28 @@ static int ut_007bug(int argc, const char **argv)
BUG("the bug message");
}
+static int ut_008bug(int argc, const char **argv)
+{
+ bug("a bug message");
+ bug("another bug message");
+ BUG_if_bug("an explicit BUG_if_bug() following bug() call(s) is nice, but not required");
+ return 0;
+}
+
+static int ut_009bug_BUG(int argc, const char **argv)
+{
+ bug("a bug message");
+ bug("another bug message");
+ /* The BUG_if_bug(...) isn't here, but we'll spot bug() calls on exit()! */
+ return 0;
+}
+
+static int ut_010bug_BUG(int argc, const char **argv)
+{
+ bug("a bug message");
+ BUG("a BUG message");
+}
+
/*
* Usage:
* test-tool trace2 <ut_name_1> <ut_usage_1>
@@ -222,7 +244,10 @@ static struct unit_test ut_table[] = {
{ ut_004child, "004child", "[<child_command_line>]" },
{ ut_005exec, "005exec", "<git_command_args>" },
{ ut_006data, "006data", "[<category> <key> <value>]+" },
- { ut_007bug, "007bug", "" },
+ { ut_007BUG, "007bug", "" },
+ { ut_008bug, "008bug", "" },
+ { ut_009bug_BUG, "009bug_BUG","" },
+ { ut_010bug_BUG, "010bug_BUG","" },
};
/* clang-format on */
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
index 2fde2353fd..ea28971e8e 100644
--- a/t/lib-git-svn.sh
+++ b/t/lib-git-svn.sh
@@ -1,3 +1,7 @@
+if test -z "$TEST_FAILS_SANITIZE_LEAK"
+then
+ TEST_PASSES_SANITIZE_LEAK=true
+fi
. ./test-lib.sh
if test -n "$NO_SVN_TESTS"
diff --git a/t/lib-unicode-nfc-nfd.sh b/t/lib-unicode-nfc-nfd.sh
new file mode 100755
index 0000000000..22232247ef
--- /dev/null
+++ b/t/lib-unicode-nfc-nfd.sh
@@ -0,0 +1,162 @@
+# Help detect how Unicode NFC and NFD are handled on the filesystem.
+
+# A simple character that has a NFD form.
+#
+# NFC: U+00e9 LATIN SMALL LETTER E WITH ACUTE
+# UTF8(NFC): \xc3 \xa9
+#
+# NFD: U+0065 LATIN SMALL LETTER E
+# U+0301 COMBINING ACUTE ACCENT
+# UTF8(NFD): \x65 + \xcc \x81
+#
+utf8_nfc=$(printf "\xc3\xa9")
+utf8_nfd=$(printf "\x65\xcc\x81")
+
+# Is the OS or the filesystem "Unicode composition sensitive"?
+#
+# That is, does the OS or the filesystem allow files to exist with
+# both the NFC and NFD spellings? Or, does the OS/FS lie to us and
+# tell us that the NFC and NFD forms are equivalent.
+#
+# This is or may be independent of what type of filesystem we have,
+# since it might be handled by the OS at a layer above the FS.
+# Testing shows on MacOS using APFS, HFS+, and FAT32 reports a
+# collision, for example.
+#
+# This does not tell us how the Unicode pathname will be spelled
+# on disk, but rather only that the two spelling "collide". We
+# will examine the actual on disk spelling in a later prereq.
+#
+test_lazy_prereq UNICODE_COMPOSITION_SENSITIVE '
+ mkdir trial_${utf8_nfc} &&
+ mkdir trial_${utf8_nfd}
+'
+
+# Is the spelling of an NFC pathname preserved on disk?
+#
+# On MacOS with HFS+ and FAT32, NFC paths are converted into NFD
+# and on APFS, NFC paths are preserved. As we have established
+# above, this is independent of "composition sensitivity".
+#
+test_lazy_prereq UNICODE_NFC_PRESERVED '
+ mkdir c_${utf8_nfc} &&
+ ls | test-tool hexdump >dump &&
+ grep "63 5f c3 a9" dump
+'
+
+# Is the spelling of an NFD pathname preserved on disk?
+#
+test_lazy_prereq UNICODE_NFD_PRESERVED '
+ mkdir d_${utf8_nfd} &&
+ ls | test-tool hexdump >dump &&
+ grep "64 5f 65 cc 81" dump
+'
+
+# The following _DOUBLE_ forms are more for my curiosity,
+# but there may be quirks lurking when there are multiple
+# combining characters in non-canonical order.
+
+# Unicode also allows multiple combining characters
+# that can be decomposed in pieces.
+#
+# NFC: U+1f67 GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI
+# UTF8(NFC): \xe1 \xbd \xa7
+#
+# NFD1: U+1f61 GREEK SMALL LETTER OMEGA WITH DASIA
+# U+0342 COMBINING GREEK PERISPOMENI
+# UTF8(NFD1): \xe1 \xbd \xa1 + \xcd \x82
+#
+# But U+1f61 decomposes into
+# NFD2: U+03c9 GREEK SMALL LETTER OMEGA
+# U+0314 COMBINING REVERSED COMMA ABOVE
+# UTF8(NFD2): \xcf \x89 + \xcc \x94
+#
+# Yielding: \xcf \x89 + \xcc \x94 + \xcd \x82
+#
+# Note that I've used the canonical ordering of the
+# combinining characters. It is also possible to
+# swap them. My testing shows that that non-standard
+# ordering also causes a collision in mkdir. However,
+# the resulting names don't draw correctly on the
+# terminal (implying that the on-disk format also has
+# them out of order).
+#
+greek_nfc=$(printf "\xe1\xbd\xa7")
+greek_nfd1=$(printf "\xe1\xbd\xa1\xcd\x82")
+greek_nfd2=$(printf "\xcf\x89\xcc\x94\xcd\x82")
+
+# See if a double decomposition also collides.
+#
+test_lazy_prereq UNICODE_DOUBLE_COMPOSITION_SENSITIVE '
+ mkdir trial_${greek_nfc} &&
+ mkdir trial_${greek_nfd2}
+'
+
+# See if the NFC spelling appears on the disk.
+#
+test_lazy_prereq UNICODE_DOUBLE_NFC_PRESERVED '
+ mkdir c_${greek_nfc} &&
+ ls | test-tool hexdump >dump &&
+ grep "63 5f e1 bd a7" dump
+'
+
+# See if the NFD spelling appears on the disk.
+#
+test_lazy_prereq UNICODE_DOUBLE_NFD_PRESERVED '
+ mkdir d_${greek_nfd2} &&
+ ls | test-tool hexdump >dump &&
+ grep "64 5f cf 89 cc 94 cd 82" dump
+'
+
+# The following is for debugging. I found it useful when
+# trying to understand the various (OS, FS) quirks WRT
+# Unicode and how composition/decomposition is handled.
+# For example, when trying to understand how (macOS, APFS)
+# and (macOS, HFS) and (macOS, FAT32) compare.
+#
+# It is rather noisy, so it is disabled by default.
+#
+if test "$unicode_debug" = "true"
+then
+ if test_have_prereq UNICODE_COMPOSITION_SENSITIVE
+ then
+ echo NFC and NFD are distinct on this OS/filesystem.
+ else
+ echo NFC and NFD are aliases on this OS/filesystem.
+ fi
+
+ if test_have_prereq UNICODE_NFC_PRESERVED
+ then
+ echo NFC maintains original spelling.
+ else
+ echo NFC is modified.
+ fi
+
+ if test_have_prereq UNICODE_NFD_PRESERVED
+ then
+ echo NFD maintains original spelling.
+ else
+ echo NFD is modified.
+ fi
+
+ if test_have_prereq UNICODE_DOUBLE_COMPOSITION_SENSITIVE
+ then
+ echo DOUBLE NFC and NFD are distinct on this OS/filesystem.
+ else
+ echo DOUBLE NFC and NFD are aliases on this OS/filesystem.
+ fi
+
+ if test_have_prereq UNICODE_DOUBLE_NFC_PRESERVED
+ then
+ echo Double NFC maintains original spelling.
+ else
+ echo Double NFC is modified.
+ fi
+
+ if test_have_prereq UNICODE_DOUBLE_NFD_PRESERVED
+ then
+ echo Double NFD maintains original spelling.
+ else
+ echo Double NFD is modified.
+ fi
+fi
diff --git a/t/lib-unique-files.sh b/t/lib-unique-files.sh
new file mode 100644
index 0000000000..a14080fe79
--- /dev/null
+++ b/t/lib-unique-files.sh
@@ -0,0 +1,34 @@
+# Helper to create files with unique contents
+
+# Create multiple files with unique contents within this test run. Takes the
+# number of directories, the number of files in each directory, and the base
+# directory.
+#
+# test_create_unique_files 2 3 my_dir -- Creates 2 directories with 3 files
+# each in my_dir, all with contents
+# different from previous invocations
+# of this command in this run.
+
+test_create_unique_files () {
+ test "$#" -ne 3 && BUG "3 param"
+
+ local dirs="$1" &&
+ local files="$2" &&
+ local basedir="$3" &&
+ local counter="0" &&
+ local i &&
+ local j &&
+ test_tick &&
+ local basedata="$basedir$test_tick" &&
+ rm -rf "$basedir" &&
+ for i in $(test_seq $dirs)
+ do
+ local dir="$basedir/dir$i" &&
+ mkdir -p "$dir" &&
+ for j in $(test_seq $files)
+ do
+ counter=$((counter + 1)) &&
+ echo "$basedata.$counter">"$dir/file$j.txt"
+ done
+ done
+}
diff --git a/t/perf/p0008-odb-fsync.sh b/t/perf/p0008-odb-fsync.sh
new file mode 100755
index 0000000000..b3a90f30eb
--- /dev/null
+++ b/t/perf/p0008-odb-fsync.sh
@@ -0,0 +1,82 @@
+#!/bin/sh
+#
+# This test measures the performance of adding new files to the object
+# database. The test was originally added to measure the effect of the
+# core.fsyncMethod=batch mode, which is why we are testing different values of
+# that setting explicitly and creating a lot of unique objects.
+
+test_description="Tests performance of adding things to the object database"
+
+. ./perf-lib.sh
+
+. $TEST_DIRECTORY/lib-unique-files.sh
+
+test_perf_fresh_repo
+test_checkout_worktree
+
+dir_count=10
+files_per_dir=50
+total_files=$((dir_count * files_per_dir))
+
+populate_files () {
+ test_create_unique_files $dir_count $files_per_dir files
+}
+
+setup_repo () {
+ (rm -rf .git || 1) &&
+ git init &&
+ test_commit first &&
+ populate_files
+}
+
+test_perf_fsync_cfgs () {
+ local method &&
+ local cfg &&
+ for method in none fsync batch writeout-only
+ do
+ case $method in
+ none)
+ cfg="-c core.fsync=none"
+ ;;
+ *)
+ cfg="-c core.fsync=loose-object -c core.fsyncMethod=$method"
+ esac &&
+
+ # Set GIT_TEST_FSYNC=1 explicitly since fsync is normally
+ # disabled by t/test-lib.sh.
+ if ! test_perf "$1 (fsyncMethod=$method)" \
+ --setup "$2" \
+ "GIT_TEST_FSYNC=1 git $cfg $3"
+ then
+ break
+ fi
+ done
+}
+
+test_perf_fsync_cfgs "add $total_files files" \
+ "setup_repo" \
+ "add -- files"
+
+test_perf_fsync_cfgs "stash $total_files files" \
+ "setup_repo" \
+ "stash push -u -- files"
+
+test_perf_fsync_cfgs "unpack $total_files files" \
+ "
+ setup_repo &&
+ git -c core.fsync=none add -- files &&
+ git -c core.fsync=none commit -q -m second &&
+ echo HEAD | git pack-objects -q --stdout --revs >test_pack.pack &&
+ setup_repo
+ " \
+ "unpack-objects -q <test_pack.pack"
+
+test_perf_fsync_cfgs "commit $total_files files" \
+ "
+ setup_repo &&
+ git -c core.fsync=none add -- files &&
+ populate_files
+ " \
+ "commit -q -a -m test"
+
+test_done
diff --git a/t/perf/p2000-sparse-operations.sh b/t/perf/p2000-sparse-operations.sh
index 76710cbef3..c181110a43 100755
--- a/t/perf/p2000-sparse-operations.sh
+++ b/t/perf/p2000-sparse-operations.sh
@@ -112,6 +112,7 @@ test_perf_on_all git add -A
test_perf_on_all git add .
test_perf_on_all git commit -a -m A
test_perf_on_all git checkout -f -
+test_perf_on_all "git sparse-checkout add f2/f3/f1 && git sparse-checkout set $SPARSE_CONE"
test_perf_on_all git reset
test_perf_on_all git reset --hard
test_perf_on_all git reset -- does-not-exist
diff --git a/t/perf/p4220-log-grep-engines.sh b/t/perf/p4220-log-grep-engines.sh
index 2bc47ded4d..03fbfbb85d 100755
--- a/t/perf/p4220-log-grep-engines.sh
+++ b/t/perf/p4220-log-grep-engines.sh
@@ -36,7 +36,8 @@ do
else
prereq=""
fi
- test_perf $prereq "$engine log$GIT_PERF_4220_LOG_OPTS --grep='$pattern'" "
+ test_perf "$engine log$GIT_PERF_4220_LOG_OPTS --grep='$pattern'" \
+ --prereq "$prereq" "
git -c grep.patternType=$engine log --pretty=format:%h$GIT_PERF_4220_LOG_OPTS --grep='$pattern' >'out.$engine' || :
"
done
diff --git a/t/perf/p4221-log-grep-engines-fixed.sh b/t/perf/p4221-log-grep-engines-fixed.sh
index 060971265a..0a6d6dfc21 100755
--- a/t/perf/p4221-log-grep-engines-fixed.sh
+++ b/t/perf/p4221-log-grep-engines-fixed.sh
@@ -26,7 +26,8 @@ do
else
prereq=""
fi
- test_perf $prereq "$engine log$GIT_PERF_4221_LOG_OPTS --grep='$pattern'" "
+ test_perf "$engine log$GIT_PERF_4221_LOG_OPTS --grep='$pattern'" \
+ --prereq "$prereq" "
git -c grep.patternType=$engine log --pretty=format:%h$GIT_PERF_4221_LOG_OPTS --grep='$pattern' >'out.$engine' || :
"
done
diff --git a/t/perf/p5302-pack-index.sh b/t/perf/p5302-pack-index.sh
index c16f6a3ff6..14c601bbf8 100755
--- a/t/perf/p5302-pack-index.sh
+++ b/t/perf/p5302-pack-index.sh
@@ -26,9 +26,8 @@ test_expect_success 'set up thread-counting tests' '
done
'
-test_perf PERF_EXTRA 'index-pack 0 threads' '
- rm -rf repo.git &&
- git init --bare repo.git &&
+test_perf 'index-pack 0 threads' --prereq PERF_EXTRA \
+ --setup 'rm -rf repo.git && git init --bare repo.git' '
GIT_DIR=repo.git git index-pack --threads=1 --stdin < $PACK
'
@@ -36,17 +35,15 @@ for t in $threads
do
THREADS=$t
export THREADS
- test_perf PERF_EXTRA "index-pack $t threads" '
- rm -rf repo.git &&
- git init --bare repo.git &&
+ test_perf "index-pack $t threads" --prereq PERF_EXTRA \
+ --setup 'rm -rf repo.git && git init --bare repo.git' '
GIT_DIR=repo.git GIT_FORCE_THREADS=1 \
git index-pack --threads=$THREADS --stdin <$PACK
'
done
-test_perf 'index-pack default number of threads' '
- rm -rf repo.git &&
- git init --bare repo.git &&
+test_perf 'index-pack default number of threads' \
+ --setup 'rm -rf repo.git && git init --bare repo.git' '
GIT_DIR=repo.git git index-pack --stdin < $PACK
'
diff --git a/t/perf/p7519-fsmonitor.sh b/t/perf/p7519-fsmonitor.sh
index 0b9129ca7b..b1cb23880f 100755
--- a/t/perf/p7519-fsmonitor.sh
+++ b/t/perf/p7519-fsmonitor.sh
@@ -60,18 +60,6 @@ then
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
-
trace_start () {
if test -n "$GIT_PERF_7519_TRACE"
then
@@ -175,10 +163,10 @@ setup_for_fsmonitor_hook () {
test_perf_w_drop_caches () {
if test -n "$GIT_PERF_7519_DROP_CACHE"; then
- test-tool drop-caches
+ test_perf "$1" --setup "test-tool drop-caches" "$2"
+ else
+ test_perf "$@"
fi
-
- test_perf "$@"
}
test_fsmonitor_suite () {
diff --git a/t/perf/p7527-builtin-fsmonitor.sh b/t/perf/p7527-builtin-fsmonitor.sh
new file mode 100755
index 0000000000..9338b9ea00
--- /dev/null
+++ b/t/perf/p7527-builtin-fsmonitor.sh
@@ -0,0 +1,257 @@
+#!/bin/sh
+
+test_description="Perf test for the builtin FSMonitor"
+
+. ./perf-lib.sh
+
+if ! test_have_prereq FSMONITOR_DAEMON
+then
+ skip_all="fsmonitor--daemon is not supported on this platform"
+ test_done
+fi
+
+test_lazy_prereq UNTRACKED_CACHE '
+ { git update-index --test-untracked-cache; ret=$?; } &&
+ test $ret -ne 1
+'
+
+# Lie to perf-lib and ask for a new empty repo and avoid
+# the complaints about GIT_PERF_REPO not being big enough
+# the perf hit when GIT_PERF_LARGE_REPO is copied into
+# the trash directory.
+#
+# NEEDSWORK: It would be nice if perf-lib had an option to
+# "borrow" an existing large repo (especially for gigantic
+# monorepos) and use it in-place. For now, fake it here.
+#
+test_perf_fresh_repo
+
+
+# Use a generated synthetic monorepo. If it doesn't exist, we will
+# generate it. If it does exist, we will put it in a known state
+# before we start our timings.
+#
+PARAM_D=5
+PARAM_W=10
+PARAM_F=9
+
+PARAMS="$PARAM_D"."$PARAM_W"."$PARAM_F"
+
+BALLAST_BR=p0006-ballast
+export BALLAST_BR
+
+TMP_BR=tmp_br
+export TMP_BR
+
+REPO=../repos/gen-many-files-"$PARAMS".git
+export REPO
+
+if ! test -d $REPO
+then
+ (cd ../repos; ./many-files.sh -d $PARAM_D -w $PARAM_W -f $PARAM_F)
+fi
+
+
+enable_uc () {
+ git -C $REPO config core.untrackedcache true
+ git -C $REPO update-index --untracked-cache
+ git -C $REPO status >/dev/null 2>&1
+}
+
+disable_uc () {
+ git -C $REPO config core.untrackedcache false
+ git -C $REPO update-index --no-untracked-cache
+ git -C $REPO status >/dev/null 2>&1
+}
+
+start_fsm () {
+ git -C $REPO fsmonitor--daemon start
+ git -C $REPO fsmonitor--daemon status
+ git -C $REPO config core.fsmonitor true
+ git -C $REPO update-index --fsmonitor
+ git -C $REPO status >/dev/null 2>&1
+}
+
+stop_fsm () {
+ git -C $REPO config --unset core.fsmonitor
+ git -C $REPO update-index --no-fsmonitor
+ test_might_fail git -C $REPO fsmonitor--daemon stop 2>/dev/null
+ git -C $REPO status >/dev/null 2>&1
+}
+
+
+# Ensure that FSMonitor is turned off on the borrowed repo.
+#
+test_expect_success "Setup borrowed repo (fsm+uc)" "
+ stop_fsm &&
+ disable_uc
+"
+
+# Also ensure that it starts in a known state.
+#
+# Because we assume that $GIT_PERF_REPEAT_COUNT > 1, we are not going to time
+# the ballast checkout, since only the first invocation does any work and the
+# subsequent ones just print "already on branch" and quit, so the reported
+# time is not useful.
+#
+# Create a temp branch and do all work relative to it so that we don't
+# accidentially alter the real ballast branch.
+#
+test_expect_success "Setup borrowed repo (temp ballast branch)" "
+ test_might_fail git -C $REPO checkout $BALLAST_BR &&
+ test_might_fail git -C $REPO reset --hard &&
+ git -C $REPO clean -d -f &&
+ test_might_fail git -C $REPO branch -D $TMP_BR &&
+ git -C $REPO branch $TMP_BR $BALLAST_BR &&
+ git -C $REPO checkout $TMP_BR
+"
+
+
+echo Data >data.txt
+
+# NEEDSWORK: We assume that $GIT_PERF_REPEAT_COUNT > 1. With
+# FSMonitor enabled, we can get a skewed view of status times, since
+# the index MAY (or may not) be updated after the first invocation
+# which will update the FSMonitor Token, so the subsequent invocations
+# may get a smaller response from the daemon.
+#
+do_status () {
+ msg=$1
+
+ test_perf "$msg" "
+ git -C $REPO status >/dev/null 2>&1
+ "
+}
+
+do_matrix () {
+ uc=$1
+ fsm=$2
+
+ t="[uc $uc][fsm $fsm]"
+ MATRIX_BR="$TMP_BR-$uc-$fsm"
+
+ test_expect_success "$t Setup matrix branch" "
+ git -C $REPO clean -d -f &&
+ git -C $REPO checkout $TMP_BR &&
+ test_might_fail git -C $REPO branch -D $MATRIX_BR &&
+ git -C $REPO branch $MATRIX_BR $TMP_BR &&
+ git -C $REPO checkout $MATRIX_BR
+ "
+
+ if test $uc = true
+ then
+ enable_uc
+ else
+ disable_uc
+ fi
+
+ if test $fsm = true
+ then
+ start_fsm
+ else
+ stop_fsm
+ fi
+
+ do_status "$t status after checkout"
+
+ # Modify many files in the matrix branch.
+ # Stage them.
+ # Commit them.
+ # Rollback.
+ #
+ test_expect_success "$t modify tracked files" "
+ find $REPO -name file1 -exec cp data.txt {} \\;
+ "
+
+ do_status "$t status after big change"
+
+ # Don't bother timing the "add" because _REPEAT_COUNT
+ # issue described above.
+ #
+ test_expect_success "$t add all" "
+ git -C $REPO add -A
+ "
+
+ do_status "$t status after add all"
+
+ test_expect_success "$t add dot" "
+ git -C $REPO add .
+ "
+
+ do_status "$t status after add dot"
+
+ test_expect_success "$t commit staged" "
+ git -C $REPO commit -a -m data
+ "
+
+ do_status "$t status after commit"
+
+ test_expect_success "$t reset HEAD~1 hard" "
+ git -C $REPO reset --hard HEAD~1 >/dev/null 2>&1
+ "
+
+ do_status "$t status after reset hard"
+
+ # Create some untracked files.
+ #
+ test_expect_success "$t create untracked files" "
+ cp -R $REPO/ballast/dir1 $REPO/ballast/xxx1
+ "
+
+ do_status "$t status after create untracked files"
+
+ # Remove the new untracked files.
+ #
+ test_expect_success "$t clean -df" "
+ git -C $REPO clean -d -f
+ "
+
+ do_status "$t status after clean"
+
+ if test $fsm = true
+ then
+ stop_fsm
+ fi
+}
+
+# Begin testing each case in the matrix that we care about.
+#
+uc_values="false"
+test_have_prereq UNTRACKED_CACHE && uc_values="false true"
+
+fsm_values="false true"
+
+for uc_val in $uc_values
+do
+ for fsm_val in $fsm_values
+ do
+ do_matrix $uc_val $fsm_val
+ done
+done
+
+cleanup () {
+ uc=$1
+ fsm=$2
+
+ MATRIX_BR="$TMP_BR-$uc-$fsm"
+
+ test_might_fail git -C $REPO branch -D $MATRIX_BR
+}
+
+
+# We're borrowing this repo. We should leave it in a clean state.
+#
+test_expect_success "Cleanup temp and matrix branches" "
+ git -C $REPO clean -d -f &&
+ test_might_fail git -C $REPO checkout $BALLAST_BR &&
+ test_might_fail git -C $REPO branch -D $TMP_BR &&
+ for uc_val in $uc_values
+ do
+ for fsm_val in $fsm_values
+ do
+ cleanup $uc_val $fsm_val
+ done
+ done
+"
+
+test_done
diff --git a/t/perf/p7820-grep-engines.sh b/t/perf/p7820-grep-engines.sh
index 8b09c5bf32..9bfb86842a 100755
--- a/t/perf/p7820-grep-engines.sh
+++ b/t/perf/p7820-grep-engines.sh
@@ -49,13 +49,15 @@ do
fi
if ! test_have_prereq PERF_GREP_ENGINES_THREADS
then
- test_perf $prereq "$engine grep$GIT_PERF_7820_GREP_OPTS '$pattern'" "
+ test_perf "$engine grep$GIT_PERF_7820_GREP_OPTS '$pattern'" \
+ --prereq "$prereq" "
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" "
+ test_perf "$engine grep$GIT_PERF_7820_GREP_OPTS '$pattern' with $threads threads"
+ --prereq PTHREADS,$prereq "
git -c grep.patternType=$engine -c grep.threads=$threads grep$GIT_PERF_7820_GREP_OPTS -- '$pattern' >'out.$engine.$threads' || :
"
done
diff --git a/t/perf/perf-lib.sh b/t/perf/perf-lib.sh
index 932105cd12..ab3687c28d 100644
--- a/t/perf/perf-lib.sh
+++ b/t/perf/perf-lib.sh
@@ -189,19 +189,39 @@ exit $ret' >&3 2>&4
}
test_wrapper_ () {
- test_wrapper_func_=$1; shift
+ local test_wrapper_func_="$1"; shift
+ local test_title_="$1"; shift
test_start_
- test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
- test "$#" = 2 ||
- BUG "not 2 or 3 parameters to test-expect-success"
+ test_prereq=
+ test_perf_setup_=
+ while test $# != 0
+ do
+ case $1 in
+ --prereq)
+ test_prereq=$2
+ shift
+ ;;
+ --setup)
+ test_perf_setup_=$2
+ shift
+ ;;
+ *)
+ break
+ ;;
+ esac
+ shift
+ done
+ test "$#" = 1 || BUG "test_wrapper_ needs 2 positional parameters"
export test_prereq
- if ! test_skip "$@"
+ export test_perf_setup_
+
+ if ! test_skip "$test_title_" "$@"
then
base=$(basename "$0" .sh)
echo "$test_count" >>"$perf_results_dir"/$base.subtests
echo "$1" >"$perf_results_dir"/$base.$test_count.descr
base="$perf_results_dir"/"$PERF_RESULTS_PREFIX$(basename "$0" .sh)"."$test_count"
- "$test_wrapper_func_" "$@"
+ "$test_wrapper_func_" "$test_title_" "$@"
fi
test_finish_
@@ -214,6 +234,16 @@ test_perf_ () {
echo "perf $test_count - $1:"
fi
for i in $(test_seq 1 $GIT_PERF_REPEAT_COUNT); do
+ if test -n "$test_perf_setup_"
+ then
+ say >&3 "setup: $test_perf_setup_"
+ if ! test_eval_ $test_perf_setup_
+ then
+ test_failure_ "$test_perf_setup_"
+ break
+ fi
+
+ fi
say >&3 "running: $2"
if test_run_perf_ "$2"
then
@@ -237,11 +267,24 @@ test_perf_ () {
rm test_time.*
}
+# Usage: test_perf 'title' [options] 'perf-test'
+# Run the performance test script specified in perf-test with
+# optional prerequisite and setup steps.
+# Options:
+# --prereq prerequisites: Skip the test if prequisites aren't met
+# --setup "setup-steps": Run setup steps prior to each measured iteration
+#
test_perf () {
test_wrapper_ test_perf_ "$@"
}
test_size_ () {
+ if test -n "$test_perf_setup_"
+ then
+ say >&3 "setup: $test_perf_setup_"
+ test_eval_ $test_perf_setup_
+ fi
+
say >&3 "running: $2"
if test_eval_ "$2" 3>"$base".result; then
test_ok_ "$1"
@@ -250,6 +293,14 @@ test_size_ () {
fi
}
+# Usage: test_size 'title' [options] 'size-test'
+# Run the size test script specified in size-test with optional
+# prerequisites and setup steps. Returns the numeric value
+# returned by size-test.
+# Options:
+# --prereq prerequisites: Skip the test if prequisites aren't met
+# --setup "setup-steps": Run setup steps prior to the size measurement
+
test_size () {
test_wrapper_ test_size_ "$@"
}
diff --git a/t/t0056-git-C.sh b/t/t0056-git-C.sh
index 2630e756da..752aa8c945 100755
--- a/t/t0056-git-C.sh
+++ b/t/t0056-git-C.sh
@@ -2,6 +2,7 @@
test_description='"-C <path>" option and its effects on other path-related options'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success '"git -C <path>" runs git from the directory <path>' '
diff --git a/t/t0062-revision-walking.sh b/t/t0062-revision-walking.sh
index 8e215867b8..b9480c8178 100755
--- a/t/t0062-revision-walking.sh
+++ b/t/t0062-revision-walking.sh
@@ -5,6 +5,7 @@
test_description='Test revision walking api'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
cat >run_twice_expected <<-EOF
diff --git a/t/t0100-previous.sh b/t/t0100-previous.sh
index 69beb59f62..a16cc3d298 100755
--- a/t/t0100-previous.sh
+++ b/t/t0100-previous.sh
@@ -5,6 +5,7 @@ test_description='previous branch syntax @{-n}'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'branch -d @{-1}' '
diff --git a/t/t0101-at-syntax.sh b/t/t0101-at-syntax.sh
index a1998b558f..878aadd64c 100755
--- a/t/t0101-at-syntax.sh
+++ b/t/t0101-at-syntax.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='various @{whatever} syntax tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t0210-trace2-normal.sh b/t/t0210-trace2-normal.sh
index 37c359bd5a..80e76a4695 100755
--- a/t/t0210-trace2-normal.sh
+++ b/t/t0210-trace2-normal.sh
@@ -168,6 +168,82 @@ test_expect_success 'BUG messages are written to trace2' '
test_cmp expect actual
'
+test_expect_success 'bug messages with BUG_if_bug() are written to trace2' '
+ test_when_finished "rm trace.normal actual expect" &&
+ test_expect_code 99 env GIT_TRACE2="$(pwd)/trace.normal" \
+ test-tool trace2 008bug 2>err &&
+ cat >expect <<-\EOF &&
+ a bug message
+ another bug message
+ an explicit BUG_if_bug() following bug() call(s) is nice, but not required
+ EOF
+ sed "s/^.*: //" <err >actual &&
+ test_cmp expect actual &&
+
+ perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+ cat >expect <<-EOF &&
+ version $V
+ start _EXE_ trace2 008bug
+ cmd_name trace2 (trace2)
+ error a bug message
+ error another bug message
+ error an explicit BUG_if_bug() following bug() call(s) is nice, but not required
+ exit elapsed:_TIME_ code:99
+ atexit elapsed:_TIME_ code:99
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'bug messages without explicit BUG_if_bug() are written to trace2' '
+ test_when_finished "rm trace.normal actual expect" &&
+ test_expect_code 99 env GIT_TRACE2="$(pwd)/trace.normal" \
+ test-tool trace2 009bug_BUG 2>err &&
+ cat >expect <<-\EOF &&
+ a bug message
+ another bug message
+ had bug() call(s) in this process without explicit BUG_if_bug()
+ EOF
+ sed "s/^.*: //" <err >actual &&
+ test_cmp expect actual &&
+
+ perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+ cat >expect <<-EOF &&
+ version $V
+ start _EXE_ trace2 009bug_BUG
+ cmd_name trace2 (trace2)
+ error a bug message
+ error another bug message
+ error on exit(): had bug() call(s) in this process without explicit BUG_if_bug()
+ exit elapsed:_TIME_ code:99
+ atexit elapsed:_TIME_ code:99
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'bug messages followed by BUG() are written to trace2' '
+ test_when_finished "rm trace.normal actual expect" &&
+ test_expect_code 99 env GIT_TRACE2="$(pwd)/trace.normal" \
+ test-tool trace2 010bug_BUG 2>err &&
+ cat >expect <<-\EOF &&
+ a bug message
+ a BUG message
+ EOF
+ sed "s/^.*: //" <err >actual &&
+ test_cmp expect actual &&
+
+ perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+ cat >expect <<-EOF &&
+ version $V
+ start _EXE_ trace2 010bug_BUG
+ cmd_name trace2 (trace2)
+ error a bug message
+ error a BUG message
+ exit elapsed:_TIME_ code:99
+ atexit elapsed:_TIME_ code:99
+ EOF
+ test_cmp expect actual
+'
+
sane_unset GIT_TRACE2_BRIEF
# Now test without environment variables and get all Trace2 settings
diff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh
index 0710b1fb1e..516a6112fd 100755
--- a/t/t1001-read-tree-m-2way.sh
+++ b/t/t1001-read-tree-m-2way.sh
@@ -21,6 +21,7 @@ In the test, these paths are used:
yomin - not in H or M
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-read-tree.sh
diff --git a/t/t1002-read-tree-m-u-2way.sh b/t/t1002-read-tree-m-u-2way.sh
index 46cbd5514a..bd5313caec 100755
--- a/t/t1002-read-tree-m-u-2way.sh
+++ b/t/t1002-read-tree-m-u-2way.sh
@@ -9,6 +9,7 @@ This is identical to t1001, but uses -u to update the work tree as well.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-read-tree.sh
diff --git a/t/t1060-object-corruption.sh b/t/t1060-object-corruption.sh
index bc89371f53..5b8e47e346 100755
--- a/t/t1060-object-corruption.sh
+++ b/t/t1060-object-corruption.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='see how we handle various forms of corruption'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# convert "1234abcd" to ".git/objects/12/34abcd"
diff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh
index 9a90031018..de1ec89007 100755
--- a/t/t1091-sparse-checkout-builtin.sh
+++ b/t/t1091-sparse-checkout-builtin.sh
@@ -72,7 +72,7 @@ test_expect_success 'git sparse-checkout list (populated)' '
'
test_expect_success 'git sparse-checkout init' '
- git -C repo sparse-checkout init &&
+ git -C repo sparse-checkout init --no-cone &&
cat >expect <<-\EOF &&
/*
!/*/
@@ -111,6 +111,7 @@ test_expect_success 'init with existing sparse-checkout' '
test_expect_success 'clone --sparse' '
git clone --sparse "file://$(pwd)/repo" clone &&
+ git -C clone sparse-checkout reapply --no-cone &&
git -C clone sparse-checkout list >actual &&
cat >expect <<-\EOF &&
/*
@@ -124,7 +125,7 @@ test_expect_success 'switching to cone mode with non-cone mode patterns' '
git init bad-patterns &&
(
cd bad-patterns &&
- git sparse-checkout init &&
+ git sparse-checkout init --no-cone &&
git sparse-checkout add dir &&
git config --worktree core.sparseCheckoutCone true &&
test_must_fail git sparse-checkout add dir 2>err &&
@@ -402,7 +403,7 @@ test_expect_success 'revert to old sparse-checkout on empty update' '
git sparse-checkout set nothing 2>err &&
test_i18ngrep ! "Sparse checkout leaves no entry on working directory" err &&
test_i18ngrep ! ".git/index.lock" err &&
- git sparse-checkout set file
+ git sparse-checkout set --no-cone file
)
'
@@ -424,7 +425,7 @@ test_expect_success 'sparse-checkout (init|set|disable) warns with dirty status'
git clone repo dirty &&
echo dirty >dirty/folder1/a &&
- git -C dirty sparse-checkout init 2>err &&
+ git -C dirty sparse-checkout init --no-cone 2>err &&
test_i18ngrep "warning.*The following paths are not up to date" err &&
git -C dirty sparse-checkout set /folder2/* /deep/deeper1/* 2>err &&
@@ -435,7 +436,7 @@ test_expect_success 'sparse-checkout (init|set|disable) warns with dirty status'
test_must_be_empty err &&
git -C dirty reset --hard &&
- git -C dirty sparse-checkout init &&
+ git -C dirty sparse-checkout init --no-cone &&
git -C dirty sparse-checkout set /folder2/* /deep/deeper1/* &&
test_path_is_missing dirty/folder1/a &&
git -C dirty sparse-checkout disable &&
@@ -451,7 +452,7 @@ test_expect_success 'sparse-checkout (init|set|disable) warns with unmerged stat
EOF
git -C unmerged update-index --index-info <input &&
- git -C unmerged sparse-checkout init 2>err &&
+ git -C unmerged sparse-checkout init --no-cone 2>err &&
test_i18ngrep "warning.*The following paths are unmerged" err &&
git -C unmerged sparse-checkout set /folder2/* /deep/deeper1/* 2>err &&
@@ -462,7 +463,7 @@ test_expect_success 'sparse-checkout (init|set|disable) warns with unmerged stat
test_i18ngrep "warning.*The following paths are unmerged" err &&
git -C unmerged reset --hard &&
- git -C unmerged sparse-checkout init &&
+ git -C unmerged sparse-checkout init --no-cone &&
git -C unmerged sparse-checkout set /folder2/* /deep/deeper1/* &&
git -C unmerged sparse-checkout disable
'
diff --git a/t/t1092-sparse-checkout-compatibility.sh b/t/t1092-sparse-checkout-compatibility.sh
index 6f778cf28c..f9f8c988bb 100755
--- a/t/t1092-sparse-checkout-compatibility.sh
+++ b/t/t1092-sparse-checkout-compatibility.sh
@@ -205,36 +205,68 @@ test_sparse_unstaged () {
done
}
-test_expect_success 'sparse-index contents' '
- init_repos &&
-
+# Usage: test_sprase_checkout_set "<c1> ... <cN>" "<s1> ... <sM>"
+# Verifies that "git sparse-checkout set <c1> ... <cN>" succeeds and
+# leaves the sparse index in a state where <s1> ... <sM> are sparse
+# directories (and <c1> ... <cN> are not).
+test_sparse_checkout_set () {
+ CONE_DIRS=$1 &&
+ SPARSE_DIRS=$2 &&
+ git -C sparse-index sparse-checkout set --skip-checks $CONE_DIRS &&
git -C sparse-index ls-files --sparse --stage >cache &&
- for dir in folder1 folder2 x
+
+ # Check that the directories outside of the sparse-checkout cone
+ # have sparse directory entries.
+ for dir in $SPARSE_DIRS
do
TREE=$(git -C sparse-index rev-parse HEAD:$dir) &&
grep "040000 $TREE 0 $dir/" cache \
|| return 1
done &&
- git -C sparse-index sparse-checkout set folder1 &&
-
- git -C sparse-index ls-files --sparse --stage >cache &&
- for dir in deep folder2 x
+ # Check that the directories in the sparse-checkout cone
+ # are not sparse directory entries.
+ for dir in $CONE_DIRS
do
- TREE=$(git -C sparse-index rev-parse HEAD:$dir) &&
- grep "040000 $TREE 0 $dir/" cache \
+ # Allow TREE to not exist because
+ # $dir does not exist at HEAD.
+ TREE=$(git -C sparse-index rev-parse HEAD:$dir) ||
+ ! grep "040000 $TREE 0 $dir/" cache \
|| return 1
- done &&
+ done
+}
- git -C sparse-index sparse-checkout set deep/deeper1 &&
+test_expect_success 'sparse-index contents' '
+ init_repos &&
- git -C sparse-index ls-files --sparse --stage >cache &&
- for dir in deep/deeper2 folder1 folder2 x
- do
- TREE=$(git -C sparse-index rev-parse HEAD:$dir) &&
- grep "040000 $TREE 0 $dir/" cache \
- || return 1
- done &&
+ # Remove deep, add three other directories.
+ test_sparse_checkout_set \
+ "folder1 folder2 x" \
+ "before deep" &&
+
+ # Remove folder1, add deep
+ test_sparse_checkout_set \
+ "deep folder2 x" \
+ "before folder1" &&
+
+ # Replace deep with deep/deeper2 (dropping deep/deeper1)
+ # Add folder1
+ test_sparse_checkout_set \
+ "deep/deeper2 folder1 folder2 x" \
+ "before deep/deeper1" &&
+
+ # Replace deep/deeper2 with deep/deeper1
+ # Replace folder1 with folder1/0/0
+ # Replace folder2 with non-existent folder2/2/3
+ # Add non-existent "bogus"
+ test_sparse_checkout_set \
+ "bogus deep/deeper1 folder1/0/0 folder2/2/3 x" \
+ "before deep/deeper2 folder2/0" &&
+
+ # Drop down to only files at root
+ test_sparse_checkout_set \
+ "" \
+ "before deep folder1 folder2 x" &&
# Disabling the sparse-index replaces tree entries with full ones
git -C sparse-index sparse-checkout init --no-sparse-index &&
@@ -1628,6 +1660,31 @@ test_expect_success 'ls-files' '
ensure_not_expanded ls-files --sparse
'
+test_expect_success 'sparse index is not expanded: sparse-checkout' '
+ init_repos &&
+
+ ensure_not_expanded sparse-checkout set deep/deeper2 &&
+ ensure_not_expanded sparse-checkout set deep/deeper1 &&
+ ensure_not_expanded sparse-checkout set deep &&
+ ensure_not_expanded sparse-checkout add folder1 &&
+ ensure_not_expanded sparse-checkout set deep/deeper1 &&
+ ensure_not_expanded sparse-checkout set folder2 &&
+
+ # Demonstrate that the checks that "folder1/a" is a file
+ # do not cause a sparse-index expansion (since it is in the
+ # sparse-checkout cone).
+ echo >>sparse-index/folder2/a &&
+ git -C sparse-index add folder2/a &&
+
+ ensure_not_expanded sparse-checkout add folder1 &&
+
+ # Skip checks here, since deep/deeper1 is inside a sparse directory
+ # that must be expanded to check whether `deep/deeper1` is a file
+ # or not.
+ ensure_not_expanded sparse-checkout set --skip-checks deep/deeper1 &&
+ ensure_not_expanded sparse-checkout set
+'
+
# NEEDSWORK: a sparse-checkout behaves differently from a full checkout
# in this scenario, but it shouldn't.
test_expect_success 'reset mixed and checkout orphan' '
diff --git a/t/t1401-symbolic-ref.sh b/t/t1401-symbolic-ref.sh
index 132a1b885a..9fb0b90f25 100755
--- a/t/t1401-symbolic-ref.sh
+++ b/t/t1401-symbolic-ref.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='basic symbolic-ref tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# If the tests munging HEAD fail, they can break detection of
diff --git a/t/t1411-reflog-show.sh b/t/t1411-reflog-show.sh
index 975c4ea83a..da581ec19a 100755
--- a/t/t1411-reflog-show.sh
+++ b/t/t1411-reflog-show.sh
@@ -4,6 +4,7 @@ test_description='Test reflog display routines'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t1412-reflog-loop.sh b/t/t1412-reflog-loop.sh
index 977603f7f1..ff30874f94 100755
--- a/t/t1412-reflog-loop.sh
+++ b/t/t1412-reflog-loop.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='reflog walk shows repeated commits again'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup commits' '
diff --git a/t/t1415-worktree-refs.sh b/t/t1415-worktree-refs.sh
index a3e6ea0808..3b531842dd 100755
--- a/t/t1415-worktree-refs.sh
+++ b/t/t1415-worktree-refs.sh
@@ -2,6 +2,7 @@
test_description='per-worktree refs'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t2015-checkout-unborn.sh b/t/t2015-checkout-unborn.sh
index a9721215fa..9425aae639 100755
--- a/t/t2015-checkout-unborn.sh
+++ b/t/t2015-checkout-unborn.sh
@@ -4,6 +4,7 @@ test_description='checkout from unborn branch'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh
index 0c38f8e356..be394f1131 100755
--- a/t/t2200-add-update.sh
+++ b/t/t2200-add-update.sh
@@ -14,6 +14,7 @@ only the updates to dir/sub.
Also tested are "git add -u" without limiting, and "git add -u"
without contents changes, and other conditions'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t3302-notes-index-expensive.sh b/t/t3302-notes-index-expensive.sh
index bb5fea02a0..d0c4d38b4d 100755
--- a/t/t3302-notes-index-expensive.sh
+++ b/t/t3302-notes-index-expensive.sh
@@ -8,6 +8,7 @@ test_description='Test commit notes index (expensive!)'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
create_repo () {
diff --git a/t/t3303-notes-subtrees.sh b/t/t3303-notes-subtrees.sh
index eac193757b..bc9b791d1b 100755
--- a/t/t3303-notes-subtrees.sh
+++ b/t/t3303-notes-subtrees.sh
@@ -5,6 +5,7 @@ test_description='Test commit notes organized in subtrees'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
number_of_commits=100
diff --git a/t/t3305-notes-fanout.sh b/t/t3305-notes-fanout.sh
index 9976d787f4..64a9915761 100755
--- a/t/t3305-notes-fanout.sh
+++ b/t/t3305-notes-fanout.sh
@@ -2,6 +2,7 @@
test_description='Test that adding/removing many notes triggers automatic fanout restructuring'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
path_has_fanout() {
diff --git a/t/t3408-rebase-multi-line.sh b/t/t3408-rebase-multi-line.sh
index cde3562e3a..7b4607d72f 100755
--- a/t/t3408-rebase-multi-line.sh
+++ b/t/t3408-rebase-multi-line.sh
@@ -5,6 +5,7 @@ test_description='rebasing a commit with multi-line first paragraph.'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t3602-rm-sparse-checkout.sh b/t/t3602-rm-sparse-checkout.sh
index 034ec01091..08580fd3dc 100755
--- a/t/t3602-rm-sparse-checkout.sh
+++ b/t/t3602-rm-sparse-checkout.sh
@@ -30,7 +30,7 @@ test_expect_success 'setup' "
for opt in "" -f --dry-run
do
test_expect_success "rm${opt:+ $opt} does not remove sparse entries" '
- git sparse-checkout set a &&
+ git sparse-checkout set --no-cone a &&
test_must_fail git rm $opt b 2>stderr &&
test_cmp b_error_and_hint stderr &&
git ls-files --error-unmatch b
@@ -118,7 +118,7 @@ test_expect_success 'can remove files from non-sparse dir' '
test_commit w/f &&
test_commit x/y/f &&
- git sparse-checkout set w !/x y/ &&
+ git sparse-checkout set --no-cone w !/x y/ &&
git rm w/f.t x/y/f.t 2>stderr &&
test_must_be_empty stderr
'
@@ -128,7 +128,7 @@ test_expect_success 'refuse to remove non-skip-worktree file from sparse dir' '
git sparse-checkout disable &&
mkdir -p x/y/z &&
test_commit x/y/z/f &&
- git sparse-checkout set !/x y/ !x/y/z &&
+ git sparse-checkout set --no-cone !/x y/ !x/y/z &&
git update-index --no-skip-worktree x/y/z/f.t &&
test_must_fail git rm x/y/z/f.t 2>stderr &&
diff --git a/t/t3700-add.sh b/t/t3700-add.sh
index b1f90ba325..8979c8a5f0 100755
--- a/t/t3700-add.sh
+++ b/t/t3700-add.sh
@@ -8,6 +8,8 @@ test_description='Test of git add, including the -- option.'
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
+. $TEST_DIRECTORY/lib-unique-files.sh
+
# Test the file mode "$1" of the file "$2" in the index.
test_mode_in_index () {
case "$(git ls-files -s "$2")" in
@@ -34,6 +36,32 @@ test_expect_success \
'Test that "git add -- -q" works' \
'touch -- -q && git add -- -q'
+BATCH_CONFIGURATION='-c core.fsync=loose-object -c core.fsyncmethod=batch'
+
+test_expect_success 'git add: core.fsyncmethod=batch' "
+ test_create_unique_files 2 4 files_base_dir1 &&
+ GIT_TEST_FSYNC=1 git $BATCH_CONFIGURATION add -- ./files_base_dir1/ &&
+ git ls-files --stage files_base_dir1/ |
+ test_parse_ls_files_stage_oids >added_files_oids &&
+
+ # We created 2 subdirs with 4 files each (8 files total) above
+ test_line_count = 8 added_files_oids &&
+ git cat-file --batch-check='%(objectname)' <added_files_oids >added_files_actual &&
+ test_cmp added_files_oids added_files_actual
+"
+
+test_expect_success 'git update-index: core.fsyncmethod=batch' "
+ test_create_unique_files 2 4 files_base_dir2 &&
+ find files_base_dir2 ! -type d -print | xargs git $BATCH_CONFIGURATION update-index --add -- &&
+ git ls-files --stage files_base_dir2 |
+ test_parse_ls_files_stage_oids >added_files2_oids &&
+
+ # We created 2 subdirs with 4 files each (8 files total) above
+ test_line_count = 8 added_files2_oids &&
+ git cat-file --batch-check='%(objectname)' <added_files2_oids >added_files2_actual &&
+ test_cmp added_files2_oids added_files2_actual
+"
+
test_expect_success \
'git add: Test that executable bit is not used if core.filemode=0' \
'git config core.filemode 0 &&
diff --git a/t/t3705-add-sparse-checkout.sh b/t/t3705-add-sparse-checkout.sh
index 95609046c6..2bade9e804 100755
--- a/t/t3705-add-sparse-checkout.sh
+++ b/t/t3705-add-sparse-checkout.sh
@@ -166,7 +166,7 @@ test_expect_success 'do not warn when pathspec matches dense entries' '
test_expect_success 'git add fails outside of sparse-checkout definition' '
test_when_finished git sparse-checkout disable &&
test_commit a &&
- git sparse-checkout init &&
+ git sparse-checkout init --no-cone &&
git sparse-checkout set a &&
echo >>sparse_entry &&
@@ -208,7 +208,7 @@ test_expect_success 'add obeys advice.updateSparsePath' '
'
test_expect_success 'add allows sparse entries with --sparse' '
- git sparse-checkout set a &&
+ git sparse-checkout set --no-cone a &&
echo modified >sparse_entry &&
test_must_fail git add sparse_entry &&
test_sparse_entry_unchanged &&
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index 4abbc8fcca..20e9488196 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -9,6 +9,7 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
+. $TEST_DIRECTORY/lib-unique-files.sh
test_expect_success 'usage on cmd and subcommand invalid option' '
test_expect_code 129 git stash --invalid-option 2>usage &&
@@ -1410,6 +1411,25 @@ test_expect_success 'stash handles skip-worktree entries nicely' '
git rev-parse --verify refs/stash:A.t
'
+
+BATCH_CONFIGURATION='-c core.fsync=loose-object -c core.fsyncmethod=batch'
+
+test_expect_success 'stash with core.fsyncmethod=batch' "
+ test_create_unique_files 2 4 files_base_dir &&
+ GIT_TEST_FSYNC=1 git $BATCH_CONFIGURATION stash push -u -- ./files_base_dir/ &&
+
+ # The files were untracked, so use the third parent,
+ # which contains the untracked files
+ git ls-tree -r stash^3 -- ./files_base_dir/ |
+ test_parse_ls_tree_oids >stashed_files_oids &&
+
+ # We created 2 dirs with 4 files each (8 files total) above
+ test_line_count = 8 stashed_files_oids &&
+ git cat-file --batch-check='%(objectname)' <stashed_files_oids >stashed_files_actual &&
+ test_cmp stashed_files_oids stashed_files_actual
+"
+
+
test_expect_success 'git stash succeeds despite directory/file change' '
test_create_repo directory_file_switch_v1 &&
(
diff --git a/t/t4021-format-patch-numbered.sh b/t/t4021-format-patch-numbered.sh
index 9be65fd444..1219aa226d 100755
--- a/t/t4021-format-patch-numbered.sh
+++ b/t/t4021-format-patch-numbered.sh
@@ -5,6 +5,7 @@
test_description='Format-patch numbering options'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t4027-diff-submodule.sh b/t/t4027-diff-submodule.sh
index 295da987cc..40164ae07d 100755
--- a/t/t4027-diff-submodule.sh
+++ b/t/t4027-diff-submodule.sh
@@ -2,6 +2,7 @@
test_description='difference in submodules'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-diff.sh
diff --git a/t/t4028-format-patch-mime-headers.sh b/t/t4028-format-patch-mime-headers.sh
index 204ba673cb..60cb819c42 100755
--- a/t/t4028-format-patch-mime-headers.sh
+++ b/t/t4028-format-patch-mime-headers.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='format-patch mime headers and extra headers do not conflict'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'create commit with utf-8 body' '
diff --git a/t/t4036-format-patch-signer-mime.sh b/t/t4036-format-patch-signer-mime.sh
index 98d9713d8b..48655bcc78 100755
--- a/t/t4036-format-patch-signer-mime.sh
+++ b/t/t4036-format-patch-signer-mime.sh
@@ -2,6 +2,7 @@
test_description='format-patch -s should force MIME encoding as needed'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t4039-diff-assume-unchanged.sh b/t/t4039-diff-assume-unchanged.sh
index 0eb0314a8b..78090e6852 100755
--- a/t/t4039-diff-assume-unchanged.sh
+++ b/t/t4039-diff-assume-unchanged.sh
@@ -2,6 +2,7 @@
test_description='diff with assume-unchanged entries'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# external diff has been tested in t4020-diff-external.sh
diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh
index 741e0803c1..73048d0a52 100755
--- a/t/t4055-diff-context.sh
+++ b/t/t4055-diff-context.sh
@@ -5,6 +5,7 @@
test_description='diff.context configuration'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t4066-diff-emit-delay.sh b/t/t4066-diff-emit-delay.sh
index a1de63b77f..0ecb391541 100755
--- a/t/t4066-diff-emit-delay.sh
+++ b/t/t4066-diff-emit-delay.sh
@@ -4,6 +4,7 @@ test_description='test combined/stat/moved interaction'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# This test covers a weird 3-way interaction between "--cc -p", which will run
diff --git a/t/t4122-apply-symlink-inside.sh b/t/t4122-apply-symlink-inside.sh
index aa52de401b..9696537303 100755
--- a/t/t4122-apply-symlink-inside.sh
+++ b/t/t4122-apply-symlink-inside.sh
@@ -4,6 +4,7 @@ test_description='apply to deeper directory without getting fooled with symlink'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t4126-apply-empty.sh b/t/t4126-apply-empty.sh
index 33860d3829..ece9fae207 100755
--- a/t/t4126-apply-empty.sh
+++ b/t/t4126-apply-empty.sh
@@ -2,7 +2,6 @@
test_description='apply empty'
-
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
diff --git a/t/t4128-apply-root.sh b/t/t4128-apply-root.sh
index f6db5a79dd..ed94c90204 100755
--- a/t/t4128-apply-root.sh
+++ b/t/t4128-apply-root.sh
@@ -2,6 +2,7 @@
test_description='apply same filename'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t4206-log-follow-harder-copies.sh b/t/t4206-log-follow-harder-copies.sh
index 4871a5dc92..33ecf82c7f 100755
--- a/t/t4206-log-follow-harder-copies.sh
+++ b/t/t4206-log-follow-harder-copies.sh
@@ -6,6 +6,8 @@
test_description='Test --follow should always find copies hard in git log.
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-diff.sh
diff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh
index b870942498..36ac6aff1e 100755
--- a/t/t4207-log-decoration-colors.sh
+++ b/t/t4207-log-decoration-colors.sh
@@ -8,6 +8,7 @@ test_description='Test for "git log --decorate" colors'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t4212-log-corrupt.sh b/t/t4212-log-corrupt.sh
index 0244888a5a..30a219894b 100755
--- a/t/t4212-log-corrupt.sh
+++ b/t/t4212-log-corrupt.sh
@@ -2,6 +2,7 @@
test_description='git log with invalid commit headers'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t5003-archive-zip.sh b/t/t5003-archive-zip.sh
index d726964307..3992d08158 100755
--- a/t/t5003-archive-zip.sh
+++ b/t/t5003-archive-zip.sh
@@ -206,6 +206,26 @@ test_expect_success 'git archive --format=zip --add-file' '
check_zip with_untracked
check_added with_untracked untracked untracked
+test_expect_success UNZIP 'git archive --format=zip --add-virtual-file' '
+ if test_have_prereq FUNNYNAMES
+ then
+ PATHNAME="pathname with : colon"
+ else
+ PATHNAME="pathname without colon"
+ fi &&
+ git archive --format=zip >with_file_with_content.zip \
+ --add-virtual-file=\""$PATHNAME"\": \
+ --add-virtual-file=hello:world $EMPTY_TREE &&
+ test_when_finished "rm -rf tmp-unpack" &&
+ mkdir tmp-unpack && (
+ cd tmp-unpack &&
+ "$GIT_UNZIP" ../with_file_with_content.zip &&
+ test_path_is_file hello &&
+ test_path_is_file "$PATHNAME" &&
+ test world = $(cat hello)
+ )
+'
+
test_expect_success 'git archive --format=zip --add-file twice' '
echo untracked >untracked &&
git archive --format=zip --prefix=one/ --add-file=untracked \
diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh
index a11d61206a..f8a0f309e2 100755
--- a/t/t5300-pack-object.sh
+++ b/t/t5300-pack-object.sh
@@ -161,22 +161,27 @@ test_expect_success 'pack-objects with bogus arguments' '
'
check_unpack () {
+ local packname="$1" &&
+ local object_list="$2" &&
+ local git_config="$3" &&
test_when_finished "rm -rf git2" &&
- git init --bare git2 &&
- git -C git2 unpack-objects -n <"$1".pack &&
- git -C git2 unpack-objects <"$1".pack &&
- (cd .git && find objects -type f -print) |
- while read path
- do
- cmp git2/$path .git/$path || {
- echo $path differs.
- return 1
- }
- done
+ git $git_config init --bare git2 &&
+ (
+ git $git_config -C git2 unpack-objects -n <"$packname".pack &&
+ git $git_config -C git2 unpack-objects <"$packname".pack &&
+ git $git_config -C git2 cat-file --batch-check="%(objectname)"
+ ) <"$object_list" >current &&
+ cmp "$object_list" current
}
test_expect_success 'unpack without delta' '
- check_unpack test-1-${packname_1}
+ check_unpack test-1-${packname_1} obj-list
+'
+
+BATCH_CONFIGURATION='-c core.fsync=loose-object -c core.fsyncmethod=batch'
+
+test_expect_success 'unpack without delta (core.fsyncmethod=batch)' '
+ check_unpack test-1-${packname_1} obj-list "$BATCH_CONFIGURATION"
'
test_expect_success 'pack with REF_DELTA' '
@@ -185,7 +190,11 @@ test_expect_success 'pack with REF_DELTA' '
'
test_expect_success 'unpack with REF_DELTA' '
- check_unpack test-2-${packname_2}
+ check_unpack test-2-${packname_2} obj-list
+'
+
+test_expect_success 'unpack with REF_DELTA (core.fsyncmethod=batch)' '
+ check_unpack test-2-${packname_2} obj-list "$BATCH_CONFIGURATION"
'
test_expect_success 'pack with OFS_DELTA' '
@@ -195,7 +204,11 @@ test_expect_success 'pack with OFS_DELTA' '
'
test_expect_success 'unpack with OFS_DELTA' '
- check_unpack test-3-${packname_3}
+ check_unpack test-3-${packname_3} obj-list
+'
+
+test_expect_success 'unpack with OFS_DELTA (core.fsyncmethod=batch)' '
+ check_unpack test-3-${packname_3} obj-list "$BATCH_CONFIGURATION"
'
test_expect_success 'compare delta flavors' '
diff --git a/t/t5301-sliding-window.sh b/t/t5301-sliding-window.sh
index 76f9798ab9..3ccaaeb397 100755
--- a/t/t5301-sliding-window.sh
+++ b/t/t5301-sliding-window.sh
@@ -4,6 +4,8 @@
#
test_description='mmap sliding window tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success \
diff --git a/t/t5313-pack-bounds-checks.sh b/t/t5313-pack-bounds-checks.sh
index 535313e4dc..cc4cfaa9d3 100755
--- a/t/t5313-pack-bounds-checks.sh
+++ b/t/t5313-pack-bounds-checks.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='bounds-checking of access to mmapped on-disk file formats'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
clear_base () {
diff --git a/t/t5316-pack-delta-depth.sh b/t/t5316-pack-delta-depth.sh
index e9045009a1..eb4ef3dda4 100755
--- a/t/t5316-pack-delta-depth.sh
+++ b/t/t5316-pack-delta-depth.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='pack-objects breaks long cross-pack delta chains'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# This mirrors a repeated push setup:
diff --git a/t/t5317-pack-objects-filter-objects.sh b/t/t5317-pack-objects-filter-objects.sh
index 33b740ce62..bb633c9b09 100755
--- a/t/t5317-pack-objects-filter-objects.sh
+++ b/t/t5317-pack-objects-filter-objects.sh
@@ -10,9 +10,6 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
# 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
@@ -22,10 +19,13 @@ test_expect_success 'setup r1' '
done
'
+parse_verify_pack_blob_oid () {
+ awk '{print $1}' -
+}
+
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 \
- >ls_files_result &&
- awk -f print_2.awk ls_files_result |
+ git -C r1 ls-files -s file.1 file.2 file.3 file.4 file.5 |
+ test_parse_ls_files_stage_oids |
sort >expected &&
git -C r1 pack-objects --revs --stdout >all.pack <<-EOF &&
@@ -35,7 +35,7 @@ test_expect_success 'verify blob count in normal packfile' '
git -C r1 verify-pack -v ../all.pack >verify_result &&
grep blob verify_result |
- awk -f print_1.awk |
+ parse_verify_pack_blob_oid |
sort >observed &&
test_cmp expected observed
@@ -54,12 +54,12 @@ test_expect_success 'verify blob:none packfile has no blobs' '
test_expect_success 'verify normal and blob:none packfiles have same commits/trees' '
git -C r1 verify-pack -v ../all.pack >verify_result &&
grep -E "commit|tree" verify_result |
- awk -f print_1.awk |
+ parse_verify_pack_blob_oid |
sort >expected &&
git -C r1 verify-pack -v ../filter.pack >verify_result &&
grep -E "commit|tree" verify_result |
- awk -f print_1.awk |
+ parse_verify_pack_blob_oid |
sort >observed &&
test_cmp expected observed
@@ -123,8 +123,8 @@ test_expect_success 'setup r2' '
'
test_expect_success 'verify blob count in normal packfile' '
- git -C r2 ls-files -s large.1000 large.10000 >ls_files_result &&
- awk -f print_2.awk ls_files_result |
+ git -C r2 ls-files -s large.1000 large.10000 |
+ test_parse_ls_files_stage_oids |
sort >expected &&
git -C r2 pack-objects --revs --stdout >all.pack <<-EOF &&
@@ -134,7 +134,7 @@ test_expect_success 'verify blob count in normal packfile' '
git -C r2 verify-pack -v ../all.pack >verify_result &&
grep blob verify_result |
- awk -f print_1.awk |
+ parse_verify_pack_blob_oid |
sort >observed &&
test_cmp expected observed
@@ -161,8 +161,8 @@ test_expect_success 'verify blob:limit=1000' '
'
test_expect_success 'verify blob:limit=1001' '
- git -C r2 ls-files -s large.1000 >ls_files_result &&
- awk -f print_2.awk ls_files_result |
+ git -C r2 ls-files -s large.1000 |
+ test_parse_ls_files_stage_oids |
sort >expected &&
git -C r2 pack-objects --revs --stdout --filter=blob:limit=1001 >filter.pack <<-EOF &&
@@ -172,15 +172,15 @@ test_expect_success 'verify blob:limit=1001' '
git -C r2 verify-pack -v ../filter.pack >verify_result &&
grep blob verify_result |
- awk -f print_1.awk |
+ parse_verify_pack_blob_oid |
sort >observed &&
test_cmp expected observed
'
test_expect_success 'verify blob:limit=10001' '
- git -C r2 ls-files -s large.1000 large.10000 >ls_files_result &&
- awk -f print_2.awk ls_files_result |
+ git -C r2 ls-files -s large.1000 large.10000 |
+ test_parse_ls_files_stage_oids |
sort >expected &&
git -C r2 pack-objects --revs --stdout --filter=blob:limit=10001 >filter.pack <<-EOF &&
@@ -190,15 +190,15 @@ test_expect_success 'verify blob:limit=10001' '
git -C r2 verify-pack -v ../filter.pack >verify_result &&
grep blob verify_result |
- awk -f print_1.awk |
+ parse_verify_pack_blob_oid |
sort >observed &&
test_cmp expected observed
'
test_expect_success 'verify blob:limit=1k' '
- git -C r2 ls-files -s large.1000 >ls_files_result &&
- awk -f print_2.awk ls_files_result |
+ git -C r2 ls-files -s large.1000 |
+ test_parse_ls_files_stage_oids |
sort >expected &&
git -C r2 pack-objects --revs --stdout --filter=blob:limit=1k >filter.pack <<-EOF &&
@@ -208,15 +208,15 @@ test_expect_success 'verify blob:limit=1k' '
git -C r2 verify-pack -v ../filter.pack >verify_result &&
grep blob verify_result |
- awk -f print_1.awk |
+ parse_verify_pack_blob_oid |
sort >observed &&
test_cmp expected observed
'
test_expect_success 'verify explicitly specifying oversized blob in input' '
- git -C r2 ls-files -s large.1000 large.10000 >ls_files_result &&
- awk -f print_2.awk ls_files_result |
+ git -C r2 ls-files -s large.1000 large.10000 |
+ test_parse_ls_files_stage_oids |
sort >expected &&
echo HEAD >objects &&
@@ -226,15 +226,15 @@ test_expect_success 'verify explicitly specifying oversized blob in input' '
git -C r2 verify-pack -v ../filter.pack >verify_result &&
grep blob verify_result |
- awk -f print_1.awk |
+ parse_verify_pack_blob_oid |
sort >observed &&
test_cmp expected observed
'
test_expect_success 'verify blob:limit=1m' '
- git -C r2 ls-files -s large.1000 large.10000 >ls_files_result &&
- awk -f print_2.awk ls_files_result |
+ git -C r2 ls-files -s large.1000 large.10000 |
+ test_parse_ls_files_stage_oids |
sort >expected &&
git -C r2 pack-objects --revs --stdout --filter=blob:limit=1m >filter.pack <<-EOF &&
@@ -244,7 +244,7 @@ test_expect_success 'verify blob:limit=1m' '
git -C r2 verify-pack -v ../filter.pack >verify_result &&
grep blob verify_result |
- awk -f print_1.awk |
+ parse_verify_pack_blob_oid |
sort >observed &&
test_cmp expected observed
@@ -253,12 +253,12 @@ test_expect_success 'verify blob:limit=1m' '
test_expect_success 'verify normal and blob:limit packfiles have same commits/trees' '
git -C r2 verify-pack -v ../all.pack >verify_result &&
grep -E "commit|tree" verify_result |
- awk -f print_1.awk |
+ parse_verify_pack_blob_oid |
sort >expected &&
git -C r2 verify-pack -v ../filter.pack >verify_result &&
grep -E "commit|tree" verify_result |
- awk -f print_1.awk |
+ parse_verify_pack_blob_oid |
sort >observed &&
test_cmp expected observed
@@ -289,9 +289,8 @@ test_expect_success 'setup r3' '
'
test_expect_success 'verify blob count in normal packfile' '
- git -C r3 ls-files -s sparse1 sparse2 dir1/sparse1 dir1/sparse2 \
- >ls_files_result &&
- awk -f print_2.awk ls_files_result |
+ git -C r3 ls-files -s sparse1 sparse2 dir1/sparse1 dir1/sparse2 |
+ test_parse_ls_files_stage_oids |
sort >expected &&
git -C r3 pack-objects --revs --stdout >all.pack <<-EOF &&
@@ -301,7 +300,7 @@ test_expect_success 'verify blob count in normal packfile' '
git -C r3 verify-pack -v ../all.pack >verify_result &&
grep blob verify_result |
- awk -f print_1.awk |
+ parse_verify_pack_blob_oid |
sort >observed &&
test_cmp expected observed
@@ -342,9 +341,8 @@ test_expect_success 'setup r4' '
'
test_expect_success 'verify blob count in normal packfile' '
- git -C r4 ls-files -s pattern sparse1 sparse2 dir1/sparse1 dir1/sparse2 \
- >ls_files_result &&
- awk -f print_2.awk ls_files_result |
+ git -C r4 ls-files -s pattern sparse1 sparse2 dir1/sparse1 dir1/sparse2 |
+ test_parse_ls_files_stage_oids |
sort >expected &&
git -C r4 pack-objects --revs --stdout >all.pack <<-EOF &&
@@ -354,19 +352,19 @@ test_expect_success 'verify blob count in normal packfile' '
git -C r4 verify-pack -v ../all.pack >verify_result &&
grep blob verify_result |
- awk -f print_1.awk |
+ parse_verify_pack_blob_oid |
sort >observed &&
test_cmp expected observed
'
test_expect_success 'verify sparse:oid=OID' '
- git -C r4 ls-files -s dir1/sparse1 dir1/sparse2 >ls_files_result &&
- awk -f print_2.awk ls_files_result |
+ git -C r4 ls-files -s dir1/sparse1 dir1/sparse2 |
+ test_parse_ls_files_stage_oids |
sort >expected &&
git -C r4 ls-files -s pattern >staged &&
- oid=$(awk -f print_2.awk staged) &&
+ oid=$(test_parse_ls_files_stage_oids <staged) &&
git -C r4 pack-objects --revs --stdout --filter=sparse:oid=$oid >filter.pack <<-EOF &&
HEAD
EOF
@@ -374,15 +372,15 @@ test_expect_success 'verify sparse:oid=OID' '
git -C r4 verify-pack -v ../filter.pack >verify_result &&
grep blob verify_result |
- awk -f print_1.awk |
+ parse_verify_pack_blob_oid |
sort >observed &&
test_cmp expected observed
'
test_expect_success 'verify sparse:oid=oid-ish' '
- git -C r4 ls-files -s dir1/sparse1 dir1/sparse2 >ls_files_result &&
- awk -f print_2.awk ls_files_result |
+ git -C r4 ls-files -s dir1/sparse1 dir1/sparse2 |
+ test_parse_ls_files_stage_oids |
sort >expected &&
git -C r4 pack-objects --revs --stdout --filter=sparse:oid=main:pattern >filter.pack <<-EOF &&
@@ -392,7 +390,7 @@ test_expect_success 'verify sparse:oid=oid-ish' '
git -C r4 verify-pack -v ../filter.pack >verify_result &&
grep blob verify_result |
- awk -f print_1.awk |
+ parse_verify_pack_blob_oid |
sort >observed &&
test_cmp expected observed
@@ -402,9 +400,8 @@ test_expect_success 'verify sparse:oid=oid-ish' '
# 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 \
- >ls_files_result &&
- awk -f print_2.awk ls_files_result |
+ git -C r1 ls-files -s file.1 file.2 file.3 file.4 file.5 |
+ test_parse_ls_files_stage_oids |
sort >expected &&
for id in `cat expected | sed "s|..|&/|"`
diff --git a/t/t5320-delta-islands.sh b/t/t5320-delta-islands.sh
index fea92a5777..124d47603d 100755
--- a/t/t5320-delta-islands.sh
+++ b/t/t5320-delta-islands.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='exercise delta islands'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# returns true iff $1 is a delta based on $2
diff --git a/t/t5322-pack-objects-sparse.sh b/t/t5322-pack-objects-sparse.sh
index d39958c066..770695c927 100755
--- a/t/t5322-pack-objects-sparse.sh
+++ b/t/t5322-pack-objects-sparse.sh
@@ -4,6 +4,7 @@ test_description='pack-objects object selection using sparse algorithm'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup repo' '
diff --git a/t/t5329-pack-objects-cruft.sh b/t/t5329-pack-objects-cruft.sh
new file mode 100755
index 0000000000..b481224b93
--- /dev/null
+++ b/t/t5329-pack-objects-cruft.sh
@@ -0,0 +1,739 @@
+#!/bin/sh
+
+test_description='cruft pack related pack-objects tests'
+. ./test-lib.sh
+
+objdir=.git/objects
+packdir=$objdir/pack
+
+basic_cruft_pack_tests () {
+ expire="$1"
+
+ test_expect_success "unreachable loose objects are packed (expire $expire)" '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit base &&
+ git repack -Ad &&
+ test_commit loose &&
+
+ test-tool chmtime +2000 "$objdir/$(test_oid_to_path \
+ $(git rev-parse loose:loose.t))" &&
+ test-tool chmtime +1000 "$objdir/$(test_oid_to_path \
+ $(git rev-parse loose^{tree}))" &&
+
+ (
+ git rev-list --objects --no-object-names base..loose |
+ while read oid
+ do
+ path="$objdir/$(test_oid_to_path "$oid")" &&
+ printf "%s %d\n" "$oid" "$(test-tool chmtime --get "$path")"
+ done |
+ sort -k1
+ ) >expect &&
+
+ keep="$(basename "$(ls $packdir/pack-*.pack)")" &&
+ cruft="$(echo $keep | git pack-objects --cruft \
+ --cruft-expiration="$expire" $packdir/pack)" &&
+ test-tool pack-mtimes "pack-$cruft.mtimes" >actual &&
+
+ test_cmp expect actual
+ )
+ '
+
+ test_expect_success "unreachable packed objects are packed (expire $expire)" '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit packed &&
+ git repack -Ad &&
+ test_commit other &&
+
+ git rev-list --objects --no-object-names packed.. >objects &&
+ keep="$(basename "$(ls $packdir/pack-*.pack)")" &&
+ other="$(git pack-objects --delta-base-offset \
+ $packdir/pack <objects)" &&
+ git prune-packed &&
+
+ test-tool chmtime --get -100 "$packdir/pack-$other.pack" >expect &&
+
+ cruft="$(git pack-objects --cruft --cruft-expiration="$expire" $packdir/pack <<-EOF
+ $keep
+ -pack-$other.pack
+ EOF
+ )" &&
+ test-tool pack-mtimes "pack-$cruft.mtimes" >actual.raw &&
+
+ cut -d" " -f2 <actual.raw | sort -u >actual &&
+
+ test_cmp expect actual
+ )
+ '
+
+ test_expect_success "unreachable cruft objects are repacked (expire $expire)" '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit packed &&
+ git repack -Ad &&
+ test_commit other &&
+
+ git rev-list --objects --no-object-names packed.. >objects &&
+ keep="$(basename "$(ls $packdir/pack-*.pack)")" &&
+
+ cruft_a="$(echo $keep | git pack-objects --cruft --cruft-expiration="$expire" $packdir/pack)" &&
+ git prune-packed &&
+ cruft_b="$(git pack-objects --cruft --cruft-expiration="$expire" $packdir/pack <<-EOF
+ $keep
+ -pack-$cruft_a.pack
+ EOF
+ )" &&
+
+ test-tool pack-mtimes "pack-$cruft_a.mtimes" >expect.raw &&
+ test-tool pack-mtimes "pack-$cruft_b.mtimes" >actual.raw &&
+
+ sort <expect.raw >expect &&
+ sort <actual.raw >actual &&
+
+ test_cmp expect actual
+ )
+ '
+
+ test_expect_success "multiple cruft packs (expire $expire)" '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit reachable &&
+ git repack -Ad &&
+ keep="$(basename "$(ls $packdir/pack-*.pack)")" &&
+
+ test_commit cruft &&
+ loose="$objdir/$(test_oid_to_path $(git rev-parse cruft))" &&
+
+ # generate three copies of the cruft object in different
+ # cruft packs, each with a unique mtime:
+ # - one expired (1000 seconds ago)
+ # - two non-expired (one 1000 seconds in the future,
+ # one 1500 seconds in the future)
+ test-tool chmtime =-1000 "$loose" &&
+ git pack-objects --cruft $packdir/pack-A <<-EOF &&
+ $keep
+ EOF
+ test-tool chmtime =+1000 "$loose" &&
+ git pack-objects --cruft $packdir/pack-B <<-EOF &&
+ $keep
+ -$(basename $(ls $packdir/pack-A-*.pack))
+ EOF
+ test-tool chmtime =+1500 "$loose" &&
+ git pack-objects --cruft $packdir/pack-C <<-EOF &&
+ $keep
+ -$(basename $(ls $packdir/pack-A-*.pack))
+ -$(basename $(ls $packdir/pack-B-*.pack))
+ EOF
+
+ # ensure the resulting cruft pack takes the most recent
+ # mtime among all copies
+ cruft="$(git pack-objects --cruft \
+ --cruft-expiration="$expire" \
+ $packdir/pack <<-EOF
+ $keep
+ -$(basename $(ls $packdir/pack-A-*.pack))
+ -$(basename $(ls $packdir/pack-B-*.pack))
+ -$(basename $(ls $packdir/pack-C-*.pack))
+ EOF
+ )" &&
+
+ test-tool pack-mtimes "$(basename $(ls $packdir/pack-C-*.mtimes))" >expect.raw &&
+ test-tool pack-mtimes "pack-$cruft.mtimes" >actual.raw &&
+
+ sort expect.raw >expect &&
+ sort actual.raw >actual &&
+ test_cmp expect actual
+ )
+ '
+
+ test_expect_success "cruft packs tolerate missing trees (expire $expire)" '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit reachable &&
+ test_commit cruft &&
+
+ tree="$(git rev-parse cruft^{tree})" &&
+
+ git reset --hard reachable &&
+ git tag -d cruft &&
+ git reflog expire --all --expire=all &&
+
+ # remove the unreachable tree, but leave the commit
+ # which has it as its root tree intact
+ rm -fr "$objdir/$(test_oid_to_path "$tree")" &&
+
+ git repack -Ad &&
+ basename $(ls $packdir/pack-*.pack) >in &&
+ git pack-objects --cruft --cruft-expiration="$expire" \
+ $packdir/pack <in
+ )
+ '
+
+ test_expect_success "cruft packs tolerate missing blobs (expire $expire)" '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit reachable &&
+ test_commit cruft &&
+
+ blob="$(git rev-parse cruft:cruft.t)" &&
+
+ git reset --hard reachable &&
+ git tag -d cruft &&
+ git reflog expire --all --expire=all &&
+
+ # remove the unreachable blob, but leave the commit (and
+ # the root tree of that commit) intact
+ rm -fr "$objdir/$(test_oid_to_path "$blob")" &&
+
+ git repack -Ad &&
+ basename $(ls $packdir/pack-*.pack) >in &&
+ git pack-objects --cruft --cruft-expiration="$expire" \
+ $packdir/pack <in
+ )
+ '
+}
+
+basic_cruft_pack_tests never
+basic_cruft_pack_tests 2.weeks.ago
+
+test_expect_success 'cruft tags rescue tagged objects' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit packed &&
+ git repack -Ad &&
+
+ test_commit tagged &&
+ git tag -a annotated -m tag &&
+
+ git rev-list --objects --no-object-names packed.. >objects &&
+ while read oid
+ do
+ test-tool chmtime -1000 \
+ "$objdir/$(test_oid_to_path $oid)"
+ done <objects &&
+
+ test-tool chmtime -500 \
+ "$objdir/$(test_oid_to_path $(git rev-parse annotated))" &&
+
+ keep="$(basename "$(ls $packdir/pack-*.pack)")" &&
+ cruft="$(echo $keep | git pack-objects --cruft \
+ --cruft-expiration=750.seconds.ago \
+ $packdir/pack)" &&
+ test-tool pack-mtimes "pack-$cruft.mtimes" >actual.raw &&
+ cut -f1 -d" " <actual.raw | sort >actual &&
+
+ (
+ cat objects &&
+ git rev-parse annotated
+ ) >expect.raw &&
+ sort <expect.raw >expect &&
+
+ test_cmp expect actual &&
+ cat actual
+ )
+'
+
+test_expect_success 'cruft commits rescue parents, trees' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit packed &&
+ git repack -Ad &&
+
+ test_commit old &&
+ test_commit new &&
+
+ git rev-list --objects --no-object-names packed..new >objects &&
+ while read object
+ do
+ test-tool chmtime -1000 \
+ "$objdir/$(test_oid_to_path $object)"
+ done <objects &&
+ test-tool chmtime +500 "$objdir/$(test_oid_to_path \
+ $(git rev-parse HEAD))" &&
+
+ keep="$(basename "$(ls $packdir/pack-*.pack)")" &&
+ cruft="$(echo $keep | git pack-objects --cruft \
+ --cruft-expiration=750.seconds.ago \
+ $packdir/pack)" &&
+ test-tool pack-mtimes "pack-$cruft.mtimes" >actual.raw &&
+
+ cut -d" " -f1 <actual.raw | sort >actual &&
+ sort <objects >expect &&
+
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'cruft trees rescue sub-trees, blobs' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit packed &&
+ git repack -Ad &&
+
+ mkdir -p dir/sub &&
+ echo foo >foo &&
+ echo bar >dir/bar &&
+ echo baz >dir/sub/baz &&
+
+ test_tick &&
+ git add . &&
+ git commit -m "pruned" &&
+
+ test-tool chmtime -1000 "$objdir/$(test_oid_to_path $(git rev-parse HEAD))" &&
+ test-tool chmtime -1000 "$objdir/$(test_oid_to_path $(git rev-parse HEAD^{tree}))" &&
+ test-tool chmtime -1000 "$objdir/$(test_oid_to_path $(git rev-parse HEAD:foo))" &&
+ test-tool chmtime -500 "$objdir/$(test_oid_to_path $(git rev-parse HEAD:dir))" &&
+ test-tool chmtime -1000 "$objdir/$(test_oid_to_path $(git rev-parse HEAD:dir/bar))" &&
+ test-tool chmtime -1000 "$objdir/$(test_oid_to_path $(git rev-parse HEAD:dir/sub))" &&
+ test-tool chmtime -1000 "$objdir/$(test_oid_to_path $(git rev-parse HEAD:dir/sub/baz))" &&
+
+ keep="$(basename "$(ls $packdir/pack-*.pack)")" &&
+ cruft="$(echo $keep | git pack-objects --cruft \
+ --cruft-expiration=750.seconds.ago \
+ $packdir/pack)" &&
+ test-tool pack-mtimes "pack-$cruft.mtimes" >actual.raw &&
+ cut -f1 -d" " <actual.raw | sort >actual &&
+
+ git rev-parse HEAD:dir HEAD:dir/bar HEAD:dir/sub HEAD:dir/sub/baz >expect.raw &&
+ sort <expect.raw >expect &&
+
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'expired objects are pruned' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit packed &&
+ git repack -Ad &&
+
+ test_commit pruned &&
+
+ git rev-list --objects --no-object-names packed..pruned >objects &&
+ while read object
+ do
+ test-tool chmtime -1000 \
+ "$objdir/$(test_oid_to_path $object)"
+ done <objects &&
+
+ keep="$(basename "$(ls $packdir/pack-*.pack)")" &&
+ cruft="$(echo $keep | git pack-objects --cruft \
+ --cruft-expiration=750.seconds.ago \
+ $packdir/pack)" &&
+
+ test-tool pack-mtimes "pack-$cruft.mtimes" >actual &&
+ test_must_be_empty actual
+ )
+'
+
+test_expect_success 'repack --cruft generates a cruft pack' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit reachable &&
+ git branch -M main &&
+ git checkout --orphan other &&
+ test_commit unreachable &&
+
+ git checkout main &&
+ git branch -D other &&
+ git tag -d unreachable &&
+ # objects are not cruft if they are contained in the reflogs
+ git reflog expire --all --expire=all &&
+
+ git rev-list --objects --all --no-object-names >reachable.raw &&
+ git cat-file --batch-all-objects --batch-check="%(objectname)" >objects &&
+ sort <reachable.raw >reachable &&
+ comm -13 reachable objects >unreachable &&
+
+ git repack --cruft -d &&
+
+ cruft=$(basename $(ls $packdir/pack-*.mtimes) .mtimes) &&
+ pack=$(basename $(ls $packdir/pack-*.pack | grep -v $cruft) .pack) &&
+
+ git show-index <$packdir/$pack.idx >actual.raw &&
+ cut -f2 -d" " actual.raw | sort >actual &&
+ test_cmp reachable actual &&
+
+ git show-index <$packdir/$cruft.idx >actual.raw &&
+ cut -f2 -d" " actual.raw | sort >actual &&
+ test_cmp unreachable actual
+ )
+'
+
+test_expect_success 'loose objects mtimes upsert others' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit reachable &&
+ git repack -Ad &&
+ git branch -M main &&
+
+ git checkout --orphan other &&
+ test_commit cruft &&
+ # incremental repack, leaving existing objects loose (so
+ # they can be "freshened")
+ git repack &&
+
+ tip="$(git rev-parse cruft)" &&
+ path="$objdir/$(test_oid_to_path "$tip")" &&
+ test-tool chmtime --get +1000 "$path" >expect &&
+
+ git checkout main &&
+ git branch -D other &&
+ git tag -d cruft &&
+ git reflog expire --all --expire=all &&
+
+ git repack --cruft -d &&
+
+ mtimes="$(basename $(ls $packdir/pack-*.mtimes))" &&
+ test-tool pack-mtimes "$mtimes" >actual.raw &&
+ grep "$tip" actual.raw | cut -d" " -f2 >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'expiring cruft objects with git gc' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit reachable &&
+ git branch -M main &&
+ git checkout --orphan other &&
+ test_commit unreachable &&
+
+ git checkout main &&
+ git branch -D other &&
+ git tag -d unreachable &&
+ # objects are not cruft if they are contained in the reflogs
+ git reflog expire --all --expire=all &&
+
+ git rev-list --objects --all --no-object-names >reachable.raw &&
+ git cat-file --batch-all-objects --batch-check="%(objectname)" >objects &&
+ sort <reachable.raw >reachable &&
+ comm -13 reachable objects >unreachable &&
+
+ git repack --cruft -d &&
+
+ mtimes=$(ls .git/objects/pack/pack-*.mtimes) &&
+ test_path_is_file $mtimes &&
+
+ git gc --cruft --prune=now &&
+
+ git cat-file --batch-all-objects --batch-check="%(objectname)" >objects &&
+
+ comm -23 unreachable objects >removed &&
+ test_cmp unreachable removed &&
+ test_path_is_missing $mtimes
+ )
+'
+
+test_expect_success 'cruft packs are not included in geometric repack' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit reachable &&
+ git repack -Ad &&
+ git branch -M main &&
+
+ git checkout --orphan other &&
+ test_commit cruft &&
+ git repack -d &&
+
+ git checkout main &&
+ git branch -D other &&
+ git tag -d cruft &&
+ git reflog expire --all --expire=all &&
+
+ git repack --cruft &&
+
+ find $packdir -type f | sort >before &&
+ git repack --geometric=2 -d &&
+ find $packdir -type f | sort >after &&
+
+ test_cmp before after
+ )
+'
+
+test_expect_success 'repack --geometric collects once-cruft objects' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit reachable &&
+ git repack -Ad &&
+ git branch -M main &&
+
+ git checkout --orphan other &&
+ git rm -rf . &&
+ test_commit --no-tag cruft &&
+ cruft="$(git rev-parse HEAD)" &&
+
+ git checkout main &&
+ git branch -D other &&
+ git reflog expire --all --expire=all &&
+
+ # Pack the objects created in the previous step into a cruft
+ # pack. Intentionally leave loose copies of those objects
+ # around so we can pick them up in a subsequent --geometric
+ # reapack.
+ git repack --cruft &&
+
+ # Now make those objects reachable, and ensure that they are
+ # packed into the new pack created via a --geometric repack.
+ git update-ref refs/heads/other $cruft &&
+
+ # Without this object, the set of unpacked objects is exactly
+ # the set of objects already in the cruft pack. Tweak that set
+ # to ensure we do not overwrite the cruft pack entirely.
+ test_commit reachable2 &&
+
+ find $packdir -name "pack-*.idx" | sort >before &&
+ git repack --geometric=2 -d &&
+ find $packdir -name "pack-*.idx" | sort >after &&
+
+ {
+ git rev-list --objects --no-object-names $cruft &&
+ git rev-list --objects --no-object-names reachable..reachable2
+ } >want.raw &&
+ sort want.raw >want &&
+
+ pack=$(comm -13 before after) &&
+ git show-index <$pack >objects.raw &&
+
+ cut -d" " -f2 objects.raw | sort >got &&
+
+ test_cmp want got
+ )
+'
+
+test_expect_success 'cruft repack with no reachable objects' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit base &&
+ git repack -ad &&
+
+ base="$(git rev-parse base)" &&
+
+ git for-each-ref --format="delete %(refname)" >in &&
+ git update-ref --stdin <in &&
+ git reflog expire --all --expire=all &&
+ rm -fr .git/index &&
+
+ git repack --cruft -d &&
+
+ git cat-file -t $base
+ )
+'
+
+test_expect_success 'cruft repack ignores --max-pack-size' '
+ git init max-pack-size &&
+ (
+ cd max-pack-size &&
+ test_commit base &&
+ # two cruft objects which exceed the maximum pack size
+ test-tool genrandom foo 1048576 | git hash-object --stdin -w &&
+ test-tool genrandom bar 1048576 | git hash-object --stdin -w &&
+ git repack --cruft --max-pack-size=1M &&
+ find $packdir -name "*.mtimes" >cruft &&
+ test_line_count = 1 cruft &&
+ test-tool pack-mtimes "$(basename "$(cat cruft)")" >objects &&
+ test_line_count = 2 objects
+ )
+'
+
+test_expect_success 'cruft repack ignores pack.packSizeLimit' '
+ (
+ cd max-pack-size &&
+ # repack everything back together to remove the existing cruft
+ # pack (but to keep its objects)
+ git repack -adk &&
+ git -c pack.packSizeLimit=1M repack --cruft &&
+ # ensure the same post condition is met when --max-pack-size
+ # would otherwise be inferred from the configuration
+ find $packdir -name "*.mtimes" >cruft &&
+ test_line_count = 1 cruft &&
+ test-tool pack-mtimes "$(basename "$(cat cruft)")" >objects &&
+ test_line_count = 2 objects
+ )
+'
+
+test_expect_success 'cruft repack respects repack.cruftWindow' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit base &&
+
+ GIT_TRACE2_EVENT=$(pwd)/event.trace \
+ git -c pack.window=1 -c repack.cruftWindow=2 repack \
+ --cruft --window=3 &&
+
+ grep "pack-objects.*--window=2.*--cruft" event.trace
+ )
+'
+
+test_expect_success 'cruft repack respects --window by default' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit base &&
+
+ GIT_TRACE2_EVENT=$(pwd)/event.trace \
+ git -c pack.window=2 repack --cruft --window=3 &&
+
+ grep "pack-objects.*--window=3.*--cruft" event.trace
+ )
+'
+
+test_expect_success 'cruft repack respects --quiet' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit base &&
+ GIT_PROGRESS_DELAY=0 git repack --cruft --quiet 2>err &&
+ test_must_be_empty err
+ )
+'
+
+test_expect_success 'cruft --local drops unreachable objects' '
+ git init alternate &&
+ git init repo &&
+ test_when_finished "rm -fr alternate repo" &&
+
+ test_commit -C alternate base &&
+ # Pack all objects in alterate so that the cruft repack in "repo" sees
+ # the object it dropped due to `--local` as packed. Otherwise this
+ # object would not appear packed anywhere (since it is not packed in
+ # alternate and likewise not part of the cruft pack in the other repo
+ # because of `--local`).
+ git -C alternate repack -ad &&
+
+ (
+ cd repo &&
+
+ object="$(git -C ../alternate rev-parse HEAD:base.t)" &&
+ git -C ../alternate cat-file -p $object >contents &&
+
+ # Write some reachable objects and two unreachable ones: one
+ # that the alternate has and another that is unique.
+ test_commit other &&
+ git hash-object -w -t blob contents &&
+ cruft="$(echo cruft | git hash-object -w -t blob --stdin)" &&
+
+ ( cd ../alternate/.git/objects && pwd ) \
+ >.git/objects/info/alternates &&
+
+ test_path_is_file $objdir/$(test_oid_to_path $cruft) &&
+ test_path_is_file $objdir/$(test_oid_to_path $object) &&
+
+ git repack -d --cruft --local &&
+
+ test-tool pack-mtimes "$(basename $(ls $packdir/pack-*.mtimes))" \
+ >objects &&
+ ! grep $object objects &&
+ grep $cruft objects
+ )
+'
+
+test_expect_success 'MIDX bitmaps tolerate reachable cruft objects' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit reachable &&
+ test_commit cruft &&
+ unreachable="$(git rev-parse cruft)" &&
+
+ git reset --hard $unreachable^ &&
+ git tag -d cruft &&
+ git reflog expire --all --expire=all &&
+
+ git repack --cruft -d &&
+
+ # resurrect the unreachable object via a new commit. the
+ # new commit will get selected for a bitmap, but be
+ # missing one of its parents from the selected packs.
+ git reset --hard $unreachable &&
+ test_commit resurrect &&
+
+ git repack --write-midx --write-bitmap-index --geometric=2 -d
+ )
+'
+
+test_expect_success 'cruft objects are freshend via loose' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ echo "cruft" >contents &&
+ blob="$(git hash-object -w -t blob contents)" &&
+ loose="$objdir/$(test_oid_to_path $blob)" &&
+
+ test_commit base &&
+
+ git repack --cruft -d &&
+
+ test_path_is_missing "$loose" &&
+ test-tool pack-mtimes "$(basename "$(ls $packdir/pack-*.mtimes)")" >cruft &&
+ grep "$blob" cruft &&
+
+ # write the same object again
+ git hash-object -w -t blob contents &&
+
+ test_path_is_file "$loose"
+ )
+'
+
+test_done
diff --git a/t/t5506-remote-groups.sh b/t/t5506-remote-groups.sh
index 8f150c0793..5bac03ede8 100755
--- a/t/t5506-remote-groups.sh
+++ b/t/t5506-remote-groups.sh
@@ -4,6 +4,7 @@ test_description='git remote group handling'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
mark() {
diff --git a/t/t5513-fetch-track.sh b/t/t5513-fetch-track.sh
index 65d1e05bd6..c46c4dbaef 100755
--- a/t/t5513-fetch-track.sh
+++ b/t/t5513-fetch-track.sh
@@ -2,6 +2,7 @@
test_description='fetch follows remote-tracking branches correctly'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh
index 320d26796d..c100a809c5 100755
--- a/t/t5515-fetch-merge-logic.sh
+++ b/t/t5515-fetch-merge-logic.sh
@@ -14,6 +14,7 @@ export GIT_TEST_PROTOCOL_VERSION
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
build_script () {
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 4dfb080433..e99c31f8c3 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -598,6 +598,26 @@ test_expect_success 'branch.*.pushremote config order is irrelevant' '
check_push_result two_repo $the_commit heads/main
'
+test_expect_success 'push rejects empty branch name entries' '
+ mk_test one_repo heads/main &&
+ test_config remote.one.url one_repo &&
+ test_config branch..remote one &&
+ test_config branch..merge refs/heads/ &&
+ test_config branch.main.remote one &&
+ test_config branch.main.merge refs/heads/main &&
+ test_must_fail git push 2>err &&
+ grep "bad config variable .branch\.\." err
+'
+
+test_expect_success 'push ignores "branch." config without subsection' '
+ mk_test one_repo heads/main &&
+ test_config remote.one.url one_repo &&
+ test_config branch.autoSetupMerge true &&
+ test_config branch.main.remote one &&
+ test_config branch.main.merge refs/heads/main &&
+ git push
+'
+
test_expect_success 'push with dry-run' '
mk_test testrepo heads/main &&
diff --git a/t/t5518-fetch-exit-status.sh b/t/t5518-fetch-exit-status.sh
index 5c4ac2556e..c13120088f 100755
--- a/t/t5518-fetch-exit-status.sh
+++ b/t/t5518-fetch-exit-status.sh
@@ -8,6 +8,7 @@ test_description='fetch exit status test'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t5532-fetch-proxy.sh b/t/t5532-fetch-proxy.sh
index 9c2798603b..d664912799 100755
--- a/t/t5532-fetch-proxy.sh
+++ b/t/t5532-fetch-proxy.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='fetching via git:// using core.gitproxy'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup remote repo' '
diff --git a/t/t5600-clone-fail-cleanup.sh b/t/t5600-clone-fail-cleanup.sh
index 34b3df4027..c814afa565 100755
--- a/t/t5600-clone-fail-cleanup.sh
+++ b/t/t5600-clone-fail-cleanup.sh
@@ -13,6 +13,7 @@ Unless the directory already exists, in which case we clean up only what we
wrote.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
corrupt_repo () {
diff --git a/t/t5900-repo-selection.sh b/t/t5900-repo-selection.sh
index 14e59c5b3e..a84faac242 100755
--- a/t/t5900-repo-selection.sh
+++ b/t/t5900-repo-selection.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='selecting remote repo in ambiguous cases'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
reset() {
diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh
index b95a0212ad..162cf50778 100755
--- a/t/t6002-rev-list-bisect.sh
+++ b/t/t6002-rev-list-bisect.sh
@@ -4,6 +4,7 @@
#
test_description='Tests git rev-list --bisect functionality'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-t6000.sh # t6xxx specific functions
diff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh
index 24d1836f41..1f7d7dd20c 100755
--- a/t/t6003-rev-list-topo-order.sh
+++ b/t/t6003-rev-list-topo-order.sh
@@ -5,6 +5,7 @@
test_description='Tests git rev-list --topo-order functionality'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-t6000.sh # t6xxx specific functions
diff --git a/t/t6005-rev-list-count.sh b/t/t6005-rev-list-count.sh
index e960049f64..0729f800c3 100755
--- a/t/t6005-rev-list-count.sh
+++ b/t/t6005-rev-list-count.sh
@@ -2,6 +2,7 @@
test_description='git rev-list --max-count and --skip test'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh
index 24b34add83..e1abc5c2b3 100755
--- a/t/t6018-rev-list-glob.sh
+++ b/t/t6018-rev-list-glob.sh
@@ -5,6 +5,7 @@ test_description='rev-list/rev-parse --glob'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
commit () {
diff --git a/t/t6100-rev-list-in-order.sh b/t/t6100-rev-list-in-order.sh
index e934bc239c..88ed7bd75a 100755
--- a/t/t6100-rev-list-in-order.sh
+++ b/t/t6100-rev-list-in-order.sh
@@ -2,6 +2,7 @@
test_description='rev-list testing in-commit-order'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup a commit history with trees, blobs' '
diff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh
index c571fa5179..a3a41c7a3e 100755
--- a/t/t6101-rev-parse-parents.sh
+++ b/t/t6101-rev-parse-parents.sh
@@ -8,6 +8,7 @@ test_description='Test git rev-parse with different parent options'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_cmp_rev_output () {
diff --git a/t/t6110-rev-list-sparse.sh b/t/t6110-rev-list-sparse.sh
index 13c1da5352..ddefc7f24e 100755
--- a/t/t6110-rev-list-sparse.sh
+++ b/t/t6110-rev-list-sparse.sh
@@ -4,6 +4,7 @@ test_description='operations that cull histories in unusual ways'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t6114-keep-packs.sh b/t/t6114-keep-packs.sh
index 9239d8aa46..44246f8a63 100755
--- a/t/t6114-keep-packs.sh
+++ b/t/t6114-keep-packs.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='rev-list with .keep packs'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t6131-pathspec-icase.sh b/t/t6131-pathspec-icase.sh
index 39fc3f6769..770cce026c 100755
--- a/t/t6131-pathspec-icase.sh
+++ b/t/t6131-pathspec-icase.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test case insensitive pathspec limiting'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
if test_have_prereq CASE_INSENSITIVE_FS
diff --git a/t/t6132-pathspec-exclude.sh b/t/t6132-pathspec-exclude.sh
index 8ff1d76f79..9fdafeb1e9 100755
--- a/t/t6132-pathspec-exclude.sh
+++ b/t/t6132-pathspec-exclude.sh
@@ -195,6 +195,7 @@ test_expect_success 'multiple exclusions' '
'
test_expect_success 't_e_i() exclude case #8' '
+ test_when_finished "rm -fr case8" &&
git init case8 &&
(
cd case8 &&
@@ -244,4 +245,184 @@ test_expect_success 'grep --untracked PATTERN :(exclude)*FILE' '
test_cmp expect-grep actual-grep
'
+# Depending on the command, all negative pathspec needs to subtract
+# either from the full tree, or from the current directory.
+#
+# The sample tree checked out at this point has:
+# file
+# sub/file
+# sub/file2
+# sub/sub/file
+# sub/sub/sub/file
+# sub2/file
+#
+# but there may also be some cruft that interferes with "git clean"
+# and "git add" tests.
+
+test_expect_success 'archive with all negative' '
+ git reset --hard &&
+ git clean -f &&
+ git -C sub archive --format=tar HEAD -- ":!sub/" >archive &&
+ "$TAR" tf archive >actual &&
+ cat >expect <<-\EOF &&
+ file
+ file2
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'add with all negative' '
+ H=$(git rev-parse HEAD) &&
+ git reset --hard $H &&
+ git clean -f &&
+ test_when_finished "git reset --hard $H" &&
+ for path in file sub/file sub/sub/file sub2/file
+ do
+ echo smudge >>"$path" || return 1
+ done &&
+ git -C sub add -- ":!sub/" &&
+ git diff --name-only --no-renames --cached >actual &&
+ cat >expect <<-\EOF &&
+ file
+ sub/file
+ sub2/file
+ EOF
+ test_cmp expect actual &&
+ git diff --name-only --no-renames >actual &&
+ echo sub/sub/file >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'add -p with all negative' '
+ H=$(git rev-parse HEAD) &&
+ git reset --hard $H &&
+ git clean -f &&
+ test_when_finished "git reset --hard $H" &&
+ for path in file sub/file sub/sub/file sub2/file
+ do
+ echo smudge >>"$path" || return 1
+ done &&
+ yes | git -C sub add -p -- ":!sub/" &&
+ git diff --name-only --no-renames --cached >actual &&
+ cat >expect <<-\EOF &&
+ file
+ sub/file
+ sub2/file
+ EOF
+ test_cmp expect actual &&
+ git diff --name-only --no-renames >actual &&
+ echo sub/sub/file >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'clean with all negative' '
+ H=$(git rev-parse HEAD) &&
+ git reset --hard $H &&
+ test_when_finished "git reset --hard $H && git clean -f" &&
+ git clean -f &&
+ for path in file9 sub/file9 sub/sub/file9 sub2/file9
+ do
+ echo cruft >"$path" || return 1
+ done &&
+ git -C sub clean -f -- ":!sub" &&
+ test_path_is_file file9 &&
+ test_path_is_missing sub/file9 &&
+ test_path_is_file sub/sub/file9 &&
+ test_path_is_file sub2/file9
+'
+
+test_expect_success 'commit with all negative' '
+ H=$(git rev-parse HEAD) &&
+ git reset --hard $H &&
+ test_when_finished "git reset --hard $H" &&
+ for path in file sub/file sub/sub/file sub2/file
+ do
+ echo smudge >>"$path" || return 1
+ done &&
+ git -C sub commit -m sample -- ":!sub/" &&
+ git diff --name-only --no-renames HEAD^ HEAD >actual &&
+ cat >expect <<-\EOF &&
+ file
+ sub/file
+ sub2/file
+ EOF
+ test_cmp expect actual &&
+ git diff --name-only --no-renames HEAD >actual &&
+ echo sub/sub/file >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'reset with all negative' '
+ H=$(git rev-parse HEAD) &&
+ git reset --hard $H &&
+ test_when_finished "git reset --hard $H" &&
+ for path in file sub/file sub/sub/file sub2/file
+ do
+ echo smudge >>"$path" &&
+ git add "$path" || return 1
+ done &&
+ git -C sub reset --quiet -- ":!sub/" &&
+ git diff --name-only --no-renames --cached >actual &&
+ echo sub/sub/file >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'grep with all negative' '
+ H=$(git rev-parse HEAD) &&
+ git reset --hard $H &&
+ test_when_finished "git reset --hard $H" &&
+ for path in file sub/file sub/sub/file sub2/file
+ do
+ echo "needle $path" >>"$path" || return 1
+ done &&
+ git -C sub grep -h needle -- ":!sub/" >actual &&
+ cat >expect <<-\EOF &&
+ needle sub/file
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'ls-files with all negative' '
+ git reset --hard &&
+ git -C sub ls-files -- ":!sub/" >actual &&
+ cat >expect <<-\EOF &&
+ file
+ file2
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'rm with all negative' '
+ git reset --hard &&
+ test_when_finished "git reset --hard" &&
+ git -C sub rm -r --cached -- ":!sub/" >actual &&
+ git diff --name-only --no-renames --diff-filter=D --cached >actual &&
+ cat >expect <<-\EOF &&
+ sub/file
+ sub/file2
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'stash with all negative' '
+ H=$(git rev-parse HEAD) &&
+ git reset --hard $H &&
+ test_when_finished "git reset --hard $H" &&
+ for path in file sub/file sub/sub/file sub2/file
+ do
+ echo smudge >>"$path" || return 1
+ done &&
+ git -C sub stash push -m sample -- ":!sub/" &&
+ git diff --name-only --no-renames HEAD >actual &&
+ echo sub/sub/file >expect &&
+ test_cmp expect actual &&
+ git stash show --name-only >actual &&
+ cat >expect <<-\EOF &&
+ file
+ sub/file
+ sub2/file
+ EOF
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t6428-merge-conflicts-sparse.sh b/t/t6428-merge-conflicts-sparse.sh
index 142c9aaabc..064be1b629 100755
--- a/t/t6428-merge-conflicts-sparse.sh
+++ b/t/t6428-merge-conflicts-sparse.sh
@@ -87,7 +87,7 @@ test_expect_success 'conflicting entries written to worktree even if sparse' '
test_path_is_file numerals &&
git sparse-checkout init &&
- git sparse-checkout set README &&
+ git sparse-checkout set --no-cone README &&
test_path_is_file README &&
test_path_is_missing numerals &&
@@ -123,7 +123,7 @@ test_expect_success 'present-despite-SKIP_WORKTREE handled reasonably' '
test_path_is_file numerals &&
git sparse-checkout init &&
- git sparse-checkout set README &&
+ git sparse-checkout set --no-cone README &&
test_path_is_file README &&
test_path_is_missing numerals &&
diff --git a/t/t7002-mv-sparse-checkout.sh b/t/t7002-mv-sparse-checkout.sh
index 1d3d2aca21..f0f7cbfcdb 100755
--- a/t/t7002-mv-sparse-checkout.sh
+++ b/t/t7002-mv-sparse-checkout.sh
@@ -27,7 +27,7 @@ test_expect_success 'setup' "
test_expect_success 'mv refuses to move sparse-to-sparse' '
test_when_finished rm -f e &&
git reset --hard &&
- git sparse-checkout set a &&
+ git sparse-checkout set --no-cone a &&
touch b &&
test_must_fail git mv b e 2>stderr &&
cat sparse_error_header >expect &&
diff --git a/t/t7008-filter-branch-null-sha1.sh b/t/t7008-filter-branch-null-sha1.sh
index 9ba9f24ad2..93fbc92b8d 100755
--- a/t/t7008-filter-branch-null-sha1.sh
+++ b/t/t7008-filter-branch-null-sha1.sh
@@ -1,6 +1,7 @@
#!/bin/sh
test_description='filter-branch removal of trees with null sha1'
+
. ./test-lib.sh
test_expect_success 'setup: base commits' '
diff --git a/t/t7012-skip-worktree-writing.sh b/t/t7012-skip-worktree-writing.sh
index cb9f1a6981..cd5c20fe51 100755
--- a/t/t7012-skip-worktree-writing.sh
+++ b/t/t7012-skip-worktree-writing.sh
@@ -151,7 +151,7 @@ test_expect_success 'stash restore in sparse checkout' '
git stash push &&
- git sparse-checkout set subdir &&
+ git sparse-checkout set --no-cone subdir &&
# Ensure after sparse-checkout we only have expected files
cat >expect <<-EOF &&
diff --git a/t/t7519-status-fsmonitor.sh b/t/t7519-status-fsmonitor.sh
index d4f9c6a837..8348e3ae7d 100755
--- a/t/t7519-status-fsmonitor.sh
+++ b/t/t7519-status-fsmonitor.sh
@@ -55,6 +55,38 @@ test_lazy_prereq UNTRACKED_CACHE '
test $ret -ne 1
'
+# Test that we detect and disallow repos that are incompatible with FSMonitor.
+test_expect_success 'incompatible bare repo' '
+ test_when_finished "rm -rf ./bare-clone actual expect" &&
+ git init --bare bare-clone &&
+
+ test_must_fail \
+ git -C ./bare-clone -c core.fsmonitor=foo \
+ update-index --fsmonitor 2>actual &&
+ grep "bare repository .* is incompatible with fsmonitor" actual &&
+
+ test_must_fail \
+ git -C ./bare-clone -c core.fsmonitor=true \
+ update-index --fsmonitor 2>actual &&
+ grep "bare repository .* is incompatible with fsmonitor" actual
+'
+
+test_expect_success FSMONITOR_DAEMON 'run fsmonitor-daemon in bare repo' '
+ test_when_finished "rm -rf ./bare-clone actual" &&
+ git init --bare bare-clone &&
+ test_must_fail git -C ./bare-clone fsmonitor--daemon run 2>actual &&
+ grep "bare repository .* is incompatible with fsmonitor" actual
+'
+
+test_expect_success MINGW,FSMONITOR_DAEMON 'run fsmonitor-daemon in virtual repo' '
+ test_when_finished "rm -rf ./fake-virtual-clone actual" &&
+ git init fake-virtual-clone &&
+ test_must_fail git -C ./fake-virtual-clone \
+ -c core.virtualfilesystem=true \
+ fsmonitor--daemon run 2>actual &&
+ grep "virtual repository .* is incompatible with fsmonitor" actual
+'
+
test_expect_success 'setup' '
: >tracked &&
: >modified &&
diff --git a/t/t7527-builtin-fsmonitor.sh b/t/t7527-builtin-fsmonitor.sh
index bd0c952a11..56c0dfffea 100755
--- a/t/t7527-builtin-fsmonitor.sh
+++ b/t/t7527-builtin-fsmonitor.sh
@@ -124,6 +124,36 @@ test_expect_success 'implicit daemon start' '
test_must_fail git -C test_implicit fsmonitor--daemon status
'
+# Verify that the daemon has shutdown. Spin a few seconds to
+# make the test a little more robust during CI testing.
+#
+# We're looking for an implicit shutdown, such as when we delete or
+# rename the ".git" directory. Our delete/rename will cause a file
+# system event that the daemon will see and the daemon will
+# auto-shutdown as soon as it sees it. But this is racy with our `git
+# fsmonitor--daemon status` commands (and we cannot use a cookie file
+# here to help us). So spin a little and give the daemon a chance to
+# see the event. (This is primarily for underpowered CI build/test
+# machines (where it might take a moment to wake and reschedule the
+# daemon process) to avoid false alarms during test runs.)
+#
+IMPLICIT_TIMEOUT=5
+
+verify_implicit_shutdown () {
+ r=$1 &&
+
+ k=0 &&
+ while test "$k" -lt $IMPLICIT_TIMEOUT
+ do
+ git -C $r fsmonitor--daemon status || return 0
+
+ sleep 1
+ k=$(( $k + 1 ))
+ done &&
+
+ return 1
+}
+
test_expect_success 'implicit daemon stop (delete .git)' '
test_when_finished "stop_daemon_delete_repo test_implicit_1" &&
@@ -142,10 +172,9 @@ test_expect_success 'implicit daemon stop (delete .git)' '
# This would make the test result dependent upon whether we
# were using fsmonitor on our development worktree.
#
- sleep 1 &&
mkdir test_implicit_1/.git &&
- test_must_fail git -C test_implicit_1 fsmonitor--daemon status
+ verify_implicit_shutdown test_implicit_1
'
test_expect_success 'implicit daemon stop (rename .git)' '
@@ -160,10 +189,70 @@ test_expect_success 'implicit daemon stop (rename .git)' '
# See [1] above.
#
- sleep 1 &&
mkdir test_implicit_2/.git &&
- test_must_fail git -C test_implicit_2 fsmonitor--daemon status
+ verify_implicit_shutdown test_implicit_2
+'
+
+# File systems on Windows may or may not have shortnames.
+# This is a volume-specific setting on modern systems.
+# "C:/" drives are required to have them enabled. Other
+# hard drives default to disabled.
+#
+# This is a crude test to see if shortnames are enabled
+# on the volume containing the test directory. It is
+# crude, but it does not require elevation like `fsutil`.
+#
+test_lazy_prereq SHORTNAMES '
+ mkdir .foo &&
+ test -d "FOO~1"
+'
+
+# Here we assume that the shortname of ".git" is "GIT~1".
+test_expect_success MINGW,SHORTNAMES 'implicit daemon stop (rename GIT~1)' '
+ test_when_finished "stop_daemon_delete_repo test_implicit_1s" &&
+
+ git init test_implicit_1s &&
+
+ start_daemon -C test_implicit_1s &&
+
+ # renaming the .git directory will implicitly stop the daemon.
+ # this moves {.git, GIT~1} to {.gitxyz, GITXYZ~1}.
+ # the rename-from FS Event will contain the shortname.
+ #
+ mv test_implicit_1s/GIT~1 test_implicit_1s/.gitxyz &&
+
+ # See [1] above.
+ # this moves {.gitxyz, GITXYZ~1} to {.git, GIT~1}.
+ mv test_implicit_1s/.gitxyz test_implicit_1s/.git &&
+
+ verify_implicit_shutdown test_implicit_1s
+'
+
+# Here we first create a file with LONGNAME of "GIT~1" before
+# we create the repo. This will cause the shortname of ".git"
+# to be "GIT~2".
+test_expect_success MINGW,SHORTNAMES 'implicit daemon stop (rename GIT~2)' '
+ test_when_finished "stop_daemon_delete_repo test_implicit_1s2" &&
+
+ mkdir test_implicit_1s2 &&
+ echo HELLO >test_implicit_1s2/GIT~1 &&
+ git init test_implicit_1s2 &&
+
+ test_path_is_file test_implicit_1s2/GIT~1 &&
+ test_path_is_dir test_implicit_1s2/GIT~2 &&
+
+ start_daemon -C test_implicit_1s2 &&
+
+ # renaming the .git directory will implicitly stop the daemon.
+ # the rename-from FS Event will contain the shortname.
+ #
+ mv test_implicit_1s2/GIT~2 test_implicit_1s2/.gitxyz &&
+
+ # See [1] above.
+ mv test_implicit_1s2/.gitxyz test_implicit_1s2/.git &&
+
+ verify_implicit_shutdown test_implicit_1s2
'
test_expect_success 'cannot start multiple daemons' '
@@ -209,6 +298,16 @@ test_expect_success 'setup' '
trace*
EOF
+ mkdir -p T1/T2/T3/T4 &&
+ echo 1 >T1/F1 &&
+ echo 1 >T1/T2/F1 &&
+ echo 1 >T1/T2/T3/F1 &&
+ echo 1 >T1/T2/T3/T4/F1 &&
+ echo 2 >T1/F2 &&
+ echo 2 >T1/T2/F2 &&
+ echo 2 >T1/T2/T3/F2 &&
+ echo 2 >T1/T2/T3/T4/F2 &&
+
git -c core.fsmonitor=false add . &&
test_tick &&
git -c core.fsmonitor=false commit -m initial &&
@@ -291,6 +390,19 @@ directory_to_file () {
echo 1 >dir1
}
+move_directory_contents_deeper() {
+ mkdir T1/_new_ &&
+ mv T1/[A-Z]* T1/_new_
+}
+
+move_directory_up() {
+ mv T1/T2/T3 T1
+}
+
+move_directory() {
+ mv T1/T2/T3 T1/T2/NewT3
+}
+
# The next few test cases confirm that our fsmonitor daemon sees each type
# of OS filesystem notification that we care about. At this layer we just
# ensure we are getting the OS notifications and do not try to confirm what
@@ -595,6 +707,10 @@ do
matrix_try $uc_val $fsm_val file_to_directory
matrix_try $uc_val $fsm_val directory_to_file
+ matrix_try $uc_val $fsm_val move_directory_contents_deeper
+ matrix_try $uc_val $fsm_val move_directory_up
+ matrix_try $uc_val $fsm_val move_directory
+
if test $fsm_val = true
then
test_expect_success "Matrix[uc:$uc_val][fsm:$fsm_val] disable fsmonitor at end" '
@@ -606,4 +722,281 @@ do
done
done
+# Test Unicode UTF-8 characters in the pathname of the working
+# directory root. Use of "*A()" routines rather than "*W()" routines
+# on Windows can sometimes lead to odd failures.
+#
+u1=$(printf "u_c3_a6__\xC3\xA6")
+u2=$(printf "u_e2_99_ab__\xE2\x99\xAB")
+u_values="$u1 $u2"
+for u in $u_values
+do
+ test_expect_success "unicode in repo root path: $u" '
+ test_when_finished "stop_daemon_delete_repo $u" &&
+
+ git init "$u" &&
+ echo 1 >"$u"/file1 &&
+ git -C "$u" add file1 &&
+ git -C "$u" config core.fsmonitor true &&
+
+ start_daemon -C "$u" &&
+ git -C "$u" status >actual &&
+ grep "new file: file1" actual
+ '
+done
+
+# Test fsmonitor interaction with submodules.
+#
+# If we start the daemon in the super, it will see FS events for
+# everything in the working directory cone and this includes any
+# files/directories contained *within* the submodules.
+#
+# A `git status` at top level will get events for items within the
+# submodule and ignore them, since they aren't named in the index
+# of the super repo. This makes the fsmonitor response a little
+# noisy, but it doesn't alter the correctness of the state of the
+# super-proper.
+#
+# When we have submodules, `git status` normally does a recursive
+# status on each of the submodules and adds a summary row for any
+# dirty submodules. (See the "S..." bits in porcelain V2 output.)
+#
+# It is therefore important that the top level status not be tricked
+# by the FSMonitor response to skip those recursive calls. That is,
+# even if FSMonitor says that the mtime of the submodule directory
+# hasn't changed and it could be implicitly marked valid, we must
+# not take that shortcut. We need to force the recusion into the
+# submodule so that we get a summary of the status *within* the
+# submodule.
+
+create_super () {
+ super="$1" &&
+
+ git init "$super" &&
+ echo x >"$super/file_1" &&
+ echo y >"$super/file_2" &&
+ echo z >"$super/file_3" &&
+ mkdir "$super/dir_1" &&
+ echo a >"$super/dir_1/file_11" &&
+ echo b >"$super/dir_1/file_12" &&
+ mkdir "$super/dir_1/dir_2" &&
+ echo a >"$super/dir_1/dir_2/file_21" &&
+ echo b >"$super/dir_1/dir_2/file_22" &&
+ git -C "$super" add . &&
+ git -C "$super" commit -m "initial $super commit"
+}
+
+create_sub () {
+ sub="$1" &&
+
+ git init "$sub" &&
+ echo x >"$sub/file_x" &&
+ echo y >"$sub/file_y" &&
+ echo z >"$sub/file_z" &&
+ mkdir "$sub/dir_x" &&
+ echo a >"$sub/dir_x/file_a" &&
+ echo b >"$sub/dir_x/file_b" &&
+ mkdir "$sub/dir_x/dir_y" &&
+ echo a >"$sub/dir_x/dir_y/file_a" &&
+ echo b >"$sub/dir_x/dir_y/file_b" &&
+ git -C "$sub" add . &&
+ git -C "$sub" commit -m "initial $sub commit"
+}
+
+my_match_and_clean () {
+ git -C super --no-optional-locks status --porcelain=v2 >actual.with &&
+ git -C super --no-optional-locks -c core.fsmonitor=false \
+ status --porcelain=v2 >actual.without &&
+ test_cmp actual.with actual.without &&
+
+ git -C super/dir_1/dir_2/sub reset --hard &&
+ git -C super/dir_1/dir_2/sub clean -d -f
+}
+
+test_expect_success 'submodule always visited' '
+ test_when_finished "git -C super fsmonitor--daemon stop; \
+ rm -rf super; \
+ rm -rf sub" &&
+
+ create_super super &&
+ create_sub sub &&
+
+ git -C super submodule add ../sub ./dir_1/dir_2/sub &&
+ git -C super commit -m "add sub" &&
+
+ start_daemon -C super &&
+ git -C super config core.fsmonitor true &&
+ git -C super update-index --fsmonitor &&
+ git -C super status &&
+
+ # Now run pairs of commands w/ and w/o FSMonitor while we make
+ # some dirt in the submodule and confirm matching output.
+
+ # Completely clean status.
+ my_match_and_clean &&
+
+ # .M S..U
+ echo z >super/dir_1/dir_2/sub/dir_x/dir_y/foobar_u &&
+ my_match_and_clean &&
+
+ # .M S.M.
+ echo z >super/dir_1/dir_2/sub/dir_x/dir_y/foobar_m &&
+ git -C super/dir_1/dir_2/sub add . &&
+ my_match_and_clean &&
+
+ # .M S.M.
+ echo z >>super/dir_1/dir_2/sub/dir_x/dir_y/file_a &&
+ git -C super/dir_1/dir_2/sub add . &&
+ my_match_and_clean &&
+
+ # .M SC..
+ echo z >>super/dir_1/dir_2/sub/dir_x/dir_y/file_a &&
+ git -C super/dir_1/dir_2/sub add . &&
+ git -C super/dir_1/dir_2/sub commit -m "SC.." &&
+ my_match_and_clean
+'
+
+# If a submodule has a `sub/.git/` directory (rather than a file
+# pointing to the super's `.git/modules/sub`) and `core.fsmonitor`
+# turned on in the submodule and the daemon is not yet started in
+# the submodule, and someone does a `git submodule absorbgitdirs`
+# in the super, Git will recursively invoke `git submodule--helper`
+# to do the work and this may try to read the index. This will
+# try to start the daemon in the submodule *and* pass (either
+# directly or via inheritance) the `--super-prefix` arg to the
+# `git fsmonitor--daemon start` command inside the submodule.
+# This causes a warning because fsmonitor--daemon does take that
+# global arg (see the table in git.c)
+#
+# This causes a warning when trying to start the daemon that is
+# somewhat confusing. It does not seem to hurt anything because
+# the fsmonitor code maps the query failure into a trivial response
+# and does the work anyway.
+#
+# It would be nice to silence the warning, however.
+
+have_t2_error_event () {
+ log=$1
+ msg="fsmonitor--daemon doesnQt support --super-prefix" &&
+
+ tr '\047' Q <$1 | grep -e "$msg"
+}
+
+test_expect_success "stray submodule super-prefix warning" '
+ test_when_finished "rm -rf super; \
+ rm -rf sub; \
+ rm super-sub.trace" &&
+
+ create_super super &&
+ create_sub sub &&
+
+ # Copy rather than submodule add so that we get a .git dir.
+ cp -R ./sub ./super/dir_1/dir_2/sub &&
+
+ git -C super/dir_1/dir_2/sub config core.fsmonitor true &&
+
+ git -C super submodule add ../sub ./dir_1/dir_2/sub &&
+ git -C super commit -m "add sub" &&
+
+ test_path_is_dir super/dir_1/dir_2/sub/.git &&
+
+ GIT_TRACE2_EVENT="$PWD/super-sub.trace" \
+ git -C super submodule absorbgitdirs &&
+
+ ! have_t2_error_event super-sub.trace
+'
+
+# On a case-insensitive file system, confirm that the daemon
+# notices when the .git directory is moved/renamed/deleted
+# regardless of how it is spelled in the the FS event.
+# That is, does the FS event receive the spelling of the
+# operation or does it receive the spelling preserved with
+# the file/directory.
+#
+test_expect_success CASE_INSENSITIVE_FS 'case insensitive+preserving' '
+# test_when_finished "stop_daemon_delete_repo test_insensitive" &&
+
+ git init test_insensitive &&
+
+ start_daemon -C test_insensitive --tf "$PWD/insensitive.trace" &&
+
+ mkdir -p test_insensitive/abc/def &&
+ echo xyz >test_insensitive/ABC/DEF/xyz &&
+
+ test_path_is_dir test_insensitive/.git &&
+ test_path_is_dir test_insensitive/.GIT &&
+
+ # Rename .git using an alternate spelling to verify that that
+ # daemon detects it and automatically shuts down.
+ mv test_insensitive/.GIT test_insensitive/.FOO &&
+
+ # See [1] above.
+ mv test_insensitive/.FOO test_insensitive/.git &&
+
+ verify_implicit_shutdown test_insensitive &&
+
+ # Verify that events were reported using on-disk spellings of the
+ # directories and files that we touched. We may or may not get a
+ # trailing slash on modified directories.
+ #
+ egrep "^event: abc/?$" ./insensitive.trace &&
+ egrep "^event: abc/def/?$" ./insensitive.trace &&
+ egrep "^event: abc/def/xyz$" ./insensitive.trace
+'
+
+# The variable "unicode_debug" is defined in the following library
+# script to dump information about how the (OS, FS) handles Unicode
+# composition. Uncomment the following line if you want to enable it.
+#
+# unicode_debug=true
+
+. "$TEST_DIRECTORY/lib-unicode-nfc-nfd.sh"
+
+# See if the OS or filesystem does NFC/NFD aliasing/munging.
+#
+# The daemon should err on the side of caution and send BOTH the
+# NFC and NFD forms. It does not know the original spelling of
+# the pathname (how the user thinks it should be spelled), so
+# emit both and let the client decide (when necessary). This is
+# similar to "core.precomposeUnicode".
+#
+test_expect_success !UNICODE_COMPOSITION_SENSITIVE 'Unicode nfc/nfd' '
+ test_when_finished "stop_daemon_delete_repo test_unicode" &&
+
+ git init test_unicode &&
+
+ start_daemon -C test_unicode --tf "$PWD/unicode.trace" &&
+
+ # Create a directory using an NFC spelling.
+ #
+ mkdir test_unicode/nfc &&
+ mkdir test_unicode/nfc/c_${utf8_nfc} &&
+
+ # Create a directory using an NFD spelling.
+ #
+ mkdir test_unicode/nfd &&
+ mkdir test_unicode/nfd/d_${utf8_nfd} &&
+
+ git -C test_unicode fsmonitor--daemon stop &&
+
+ if test_have_prereq UNICODE_NFC_PRESERVED
+ then
+ # We should have seen NFC event from OS.
+ # We should not have synthesized an NFD event.
+ egrep "^event: nfc/c_${utf8_nfc}/?$" ./unicode.trace &&
+ egrep -v "^event: nfc/c_${utf8_nfd}/?$" ./unicode.trace
+ else
+ # We should have seen NFD event from OS.
+ # We should have synthesized an NFC event.
+ egrep "^event: nfc/c_${utf8_nfd}/?$" ./unicode.trace &&
+ egrep "^event: nfc/c_${utf8_nfc}/?$" ./unicode.trace
+ fi &&
+
+ # We assume UNICODE_NFD_PRESERVED.
+ # We should have seen explicit NFD from OS.
+ # We should have synthesized an NFC event.
+ egrep "^event: nfd/d_${utf8_nfd}/?$" ./unicode.trace &&
+ egrep "^event: nfd/d_${utf8_nfc}/?$" ./unicode.trace
+'
+
test_done
diff --git a/t/t7702-repack-cyclic-alternate.sh b/t/t7702-repack-cyclic-alternate.sh
index 93b74867ac..f3cdb98eec 100755
--- a/t/t7702-repack-cyclic-alternate.sh
+++ b/t/t7702-repack-cyclic-alternate.sh
@@ -4,6 +4,8 @@
#
test_description='repack involving cyclic alternate'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t7703-repack-geometric.sh b/t/t7703-repack-geometric.sh
index bdbbcbf1ec..da87f8b2d8 100755
--- a/t/t7703-repack-geometric.sh
+++ b/t/t7703-repack-geometric.sh
@@ -7,6 +7,7 @@ test_description='git repack --geometric works correctly'
GIT_TEST_MULTI_PACK_INDEX=0
objdir=.git/objects
+packdir=$objdir/pack
midx=$objdir/pack/multi-pack-index
test_expect_success '--geometric with no packs' '
@@ -180,6 +181,34 @@ test_expect_success '--geometric ignores kept packs' '
)
'
+test_expect_success '--geometric ignores --keep-pack packs' '
+ git init geometric &&
+ test_when_finished "rm -fr geometric" &&
+ (
+ cd geometric &&
+
+ # Create two equal-sized packs
+ test_commit kept && # 3 objects
+ git repack -d &&
+ test_commit pack && # 3 objects
+ git repack -d &&
+
+ find $objdir/pack -type f -name "*.pack" | sort >packs.before &&
+ git repack --geometric 2 -dm \
+ --keep-pack="$(basename "$(head -n 1 packs.before)")" >out &&
+ find $objdir/pack -type f -name "*.pack" | sort >packs.after &&
+
+ # Packs should not have changed (only one non-kept pack, no
+ # loose objects), but $midx should now exist.
+ grep "Nothing new to pack" out &&
+ test_path_is_file $midx &&
+
+ test_cmp packs.before packs.after &&
+
+ git fsck
+ )
+'
+
test_expect_success '--geometric chooses largest MIDX preferred pack' '
git init geometric &&
test_when_finished "rm -fr geometric" &&
@@ -202,4 +231,50 @@ test_expect_success '--geometric chooses largest MIDX preferred pack' '
)
'
+test_expect_success '--geometric with pack.packSizeLimit' '
+ git init pack-rewrite &&
+ test_when_finished "rm -fr pack-rewrite" &&
+ (
+ cd pack-rewrite &&
+
+ test-tool genrandom foo 1048576 >foo &&
+ test-tool genrandom bar 1048576 >bar &&
+
+ git add foo bar &&
+ test_tick &&
+ git commit -m base &&
+
+ git rev-parse HEAD:foo HEAD:bar >p1.objects &&
+ git rev-parse HEAD HEAD^{tree} >p2.objects &&
+
+ # These two packs each contain two objects, so the following
+ # `--geometric` repack will try to combine them.
+ p1="$(git pack-objects $packdir/pack <p1.objects)" &&
+ p2="$(git pack-objects $packdir/pack <p2.objects)" &&
+
+ # Remove any loose objects in packs, since we do not want extra
+ # copies around (which would mask over potential object
+ # corruption issues).
+ git prune-packed &&
+
+ # Both p1 and p2 will be rolled up, but pack-objects will write
+ # three packs:
+ #
+ # - one containing object "foo",
+ # - another containing object "bar",
+ # - a final pack containing the commit and tree objects
+ # (identical to p2 above)
+ git repack --geometric 2 -d --max-pack-size=1048576 &&
+
+ # Ensure `repack` can detect that the third pack it wrote
+ # (containing just the tree and commit objects) was identical to
+ # one that was below the geometric split, so that we can save it
+ # from deletion.
+ #
+ # If `repack` fails to do that, we will incorrectly delete p2,
+ # causing object corruption.
+ git fsck
+ )
+'
+
test_done
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index 42694fe584..01c74b8b07 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -4,6 +4,7 @@ test_description='git send-email'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# May be altered later in the test
diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh
index fea41b3c36..7c5b847f58 100755
--- a/t/t9100-git-svn-basic.sh
+++ b/t/t9100-git-svn-basic.sh
@@ -8,6 +8,7 @@ test_description='git svn basic tests'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_FAILS_SANITIZE_LEAK=true
. ./lib-git-svn.sh
prepare_utf8_locale
diff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh
index 8b5681dd68..d043e80fc3 100755
--- a/t/t9101-git-svn-props.sh
+++ b/t/t9101-git-svn-props.sh
@@ -4,6 +4,8 @@
#
test_description='git svn property tests'
+
+TEST_FAILS_SANITIZE_LEAK=true
. ./lib-git-svn.sh
mkdir import
diff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh
index c7d8e0bf00..5cf2ef4b8b 100755
--- a/t/t9104-git-svn-follow-parent.sh
+++ b/t/t9104-git-svn-follow-parent.sh
@@ -4,6 +4,8 @@
#
test_description='git svn fetching'
+
+TEST_FAILS_SANITIZE_LEAK=true
. ./lib-git-svn.sh
test_expect_success 'initialize repo' '
diff --git a/t/t9106-git-svn-commit-diff-clobber.sh b/t/t9106-git-svn-commit-diff-clobber.sh
index aec45bca3b..3cab0b9720 100755
--- a/t/t9106-git-svn-commit-diff-clobber.sh
+++ b/t/t9106-git-svn-commit-diff-clobber.sh
@@ -2,6 +2,8 @@
#
# Copyright (c) 2006 Eric Wong
test_description='git svn commit-diff clobber'
+
+TEST_FAILS_SANITIZE_LEAK=true
. ./lib-git-svn.sh
test_expect_success 'initialize repo' '
diff --git a/t/t9115-git-svn-dcommit-funky-renames.sh b/t/t9115-git-svn-dcommit-funky-renames.sh
index 743fbe1fe4..419f055721 100755
--- a/t/t9115-git-svn-dcommit-funky-renames.sh
+++ b/t/t9115-git-svn-dcommit-funky-renames.sh
@@ -5,6 +5,7 @@
test_description='git svn dcommit can commit renames of files with ugly names'
+TEST_FAILS_SANITIZE_LEAK=true
. ./lib-git-svn.sh
test_expect_success 'load repository with strange names' '
diff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh
index 0a9f1ef366..d74d7b2de6 100755
--- a/t/t9116-git-svn-log.sh
+++ b/t/t9116-git-svn-log.sh
@@ -4,6 +4,7 @@
#
test_description='git svn log tests'
+
. ./lib-git-svn.sh
test_expect_success 'setup repository and import' '
diff --git a/t/t9122-git-svn-author.sh b/t/t9122-git-svn-author.sh
index 9e8fe38e7e..527ba3d293 100755
--- a/t/t9122-git-svn-author.sh
+++ b/t/t9122-git-svn-author.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='git svn authorship'
+
+TEST_FAILS_SANITIZE_LEAK=true
. ./lib-git-svn.sh
test_expect_success 'setup svn repository' '
diff --git a/t/t9127-git-svn-partial-rebuild.sh b/t/t9127-git-svn-partial-rebuild.sh
index 2e4789d061..97f495bd49 100755
--- a/t/t9127-git-svn-partial-rebuild.sh
+++ b/t/t9127-git-svn-partial-rebuild.sh
@@ -4,6 +4,7 @@
#
test_description='git svn partial-rebuild tests'
+
. ./lib-git-svn.sh
test_expect_success 'initialize svnrepo' '
diff --git a/t/t9129-git-svn-i18n-commitencoding.sh b/t/t9129-git-svn-i18n-commitencoding.sh
index 01e1e8a8f7..185248a4cd 100755
--- a/t/t9129-git-svn-i18n-commitencoding.sh
+++ b/t/t9129-git-svn-i18n-commitencoding.sh
@@ -4,6 +4,7 @@
test_description='git svn honors i18n.commitEncoding in config'
+TEST_FAILS_SANITIZE_LEAK=true
. ./lib-git-svn.sh
compare_git_head_with () {
diff --git a/t/t9132-git-svn-broken-symlink.sh b/t/t9132-git-svn-broken-symlink.sh
index aeceffaf7b..4d8d0584b7 100755
--- a/t/t9132-git-svn-broken-symlink.sh
+++ b/t/t9132-git-svn-broken-symlink.sh
@@ -2,6 +2,7 @@
test_description='test that git handles an svn repository with empty symlinks'
+TEST_FAILS_SANITIZE_LEAK=true
. ./lib-git-svn.sh
test_expect_success 'load svn dumpfile' '
svnadmin load "$rawsvnrepo" <<EOF
diff --git a/t/t9139-git-svn-non-utf8-commitencoding.sh b/t/t9139-git-svn-non-utf8-commitencoding.sh
index 22d80b0be2..b7f756b2b7 100755
--- a/t/t9139-git-svn-non-utf8-commitencoding.sh
+++ b/t/t9139-git-svn-non-utf8-commitencoding.sh
@@ -4,6 +4,7 @@
test_description='git svn refuses to dcommit non-UTF8 messages'
+TEST_FAILS_SANITIZE_LEAK=true
. ./lib-git-svn.sh
# ISO-2022-JP can pass for valid UTF-8, so skipping that in this test
diff --git a/t/t9146-git-svn-empty-dirs.sh b/t/t9146-git-svn-empty-dirs.sh
index 80cb55fee7..79c26ed69c 100755
--- a/t/t9146-git-svn-empty-dirs.sh
+++ b/t/t9146-git-svn-empty-dirs.sh
@@ -3,6 +3,8 @@
# Copyright (c) 2009 Eric Wong
test_description='git svn creates empty directories'
+
+TEST_FAILS_SANITIZE_LEAK=true
. ./lib-git-svn.sh
test_expect_success 'initialize repo' '
diff --git a/t/t9148-git-svn-propset.sh b/t/t9148-git-svn-propset.sh
index aebb28995e..6cc76a07b3 100755
--- a/t/t9148-git-svn-propset.sh
+++ b/t/t9148-git-svn-propset.sh
@@ -5,6 +5,7 @@
test_description='git svn propset tests'
+TEST_FAILS_SANITIZE_LEAK=true
. ./lib-git-svn.sh
test_expect_success 'setup propset via import' '
diff --git a/t/t9160-git-svn-preserve-empty-dirs.sh b/t/t9160-git-svn-preserve-empty-dirs.sh
index 36c6b1a12f..9cf7a1427a 100755
--- a/t/t9160-git-svn-preserve-empty-dirs.sh
+++ b/t/t9160-git-svn-preserve-empty-dirs.sh
@@ -9,6 +9,7 @@ This test uses git to clone a Subversion repository that contains empty
directories, and checks that corresponding directories are created in the
local Git repository with placeholder files.'
+TEST_FAILS_SANITIZE_LEAK=true
. ./lib-git-svn.sh
GIT_REPO=git-svn-repo
diff --git a/t/t9162-git-svn-dcommit-interactive.sh b/t/t9162-git-svn-dcommit-interactive.sh
index e38d9fa37b..e2aa8ed88a 100755
--- a/t/t9162-git-svn-dcommit-interactive.sh
+++ b/t/t9162-git-svn-dcommit-interactive.sh
@@ -3,6 +3,8 @@
# Copyright (c) 2011 Frédéric Heitzmann
test_description='git svn dcommit --interactive series'
+
+TEST_FAILS_SANITIZE_LEAK=true
. ./lib-git-svn.sh
test_expect_success 'initialize repo' '
diff --git a/t/t9164-git-svn-dcommit-concurrent.sh b/t/t9164-git-svn-dcommit-concurrent.sh
index 8466269bf5..1465156072 100755
--- a/t/t9164-git-svn-dcommit-concurrent.sh
+++ b/t/t9164-git-svn-dcommit-concurrent.sh
@@ -4,6 +4,8 @@
#
test_description='concurrent git svn dcommit'
+
+TEST_FAILS_SANITIZE_LEAK=true
. ./lib-git-svn.sh
diff --git a/t/t9501-gitweb-standalone-http-status.sh b/t/t9501-gitweb-standalone-http-status.sh
index 32814e75df..c900231079 100755
--- a/t/t9501-gitweb-standalone-http-status.sh
+++ b/t/t9501-gitweb-standalone-http-status.sh
@@ -13,6 +13,7 @@ code and message.'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./lib-gitweb.sh
#
diff --git a/t/t9502-gitweb-standalone-parse-output.sh b/t/t9502-gitweb-standalone-parse-output.sh
index 8cb582f0e6..81d5625557 100755
--- a/t/t9502-gitweb-standalone-parse-output.sh
+++ b/t/t9502-gitweb-standalone-parse-output.sh
@@ -220,4 +220,18 @@ test_expect_success 'no http-equiv="content-type" in XHTML' '
no_http_equiv_content_type "p=.git;a=tree"
'
+proper_doctype() {
+ gitweb_run "$@" &&
+ grep -F "<!DOCTYPE html [" gitweb.body &&
+ grep "<!ENTITY nbsp" gitweb.body &&
+ grep "<!ENTITY sdot" gitweb.body
+}
+
+test_expect_success 'Proper DOCTYPE with entity declarations' '
+ proper_doctype &&
+ proper_doctype "p=.git" &&
+ proper_doctype "p=.git;a=log" &&
+ proper_doctype "p=.git;a=tree"
+'
+
test_done
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 93c03380d4..6da7273f1d 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -795,7 +795,7 @@ test_verify_prereq () {
}
test_expect_failure () {
- test_start_
+ test_start_ "$@"
test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
test "$#" = 2 ||
BUG "not 2 or 3 parameters to test-expect-failure"
@@ -803,6 +803,7 @@ test_expect_failure () {
export test_prereq
if ! test_skip "$@"
then
+ test -n "$test_skip_test_preamble" ||
say >&3 "checking known breakage of $TEST_NUMBER.$test_count '$1': $2"
if test_run_ "$2" expecting_failure
then
@@ -815,7 +816,7 @@ test_expect_failure () {
}
test_expect_success () {
- test_start_
+ test_start_ "$@"
test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
test "$#" = 2 ||
BUG "not 2 or 3 parameters to test-expect-success"
@@ -823,6 +824,7 @@ test_expect_success () {
export test_prereq
if ! test_skip "$@"
then
+ test -n "$test_skip_test_preamble" ||
say >&3 "expecting success of $TEST_NUMBER.$test_count '$1': $2"
if test_run_ "$2"
then
@@ -1782,6 +1784,16 @@ test_oid_to_path () {
echo "${1%$basename}/$basename"
}
+# Parse oids from git ls-files --staged output
+test_parse_ls_files_stage_oids () {
+ awk '{print $2}' -
+}
+
+# Parse oids from git ls-tree output
+test_parse_ls_tree_oids () {
+ awk '{print $3}' -
+}
+
# Choose a port number based on the test script's number and store it in
# the given variable name, unless that variable already contains a number.
test_set_port () {
diff --git a/t/test-lib-github-workflow-markup.sh b/t/test-lib-github-workflow-markup.sh
new file mode 100644
index 0000000000..9c5339c577
--- /dev/null
+++ b/t/test-lib-github-workflow-markup.sh
@@ -0,0 +1,56 @@
+# Library of functions to mark up test scripts' output suitable for
+# pretty-printing it in GitHub workflows.
+#
+# Copyright (c) 2022 Johannes Schindelin
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see http://www.gnu.org/licenses/ .
+#
+# The idea is for `test-lib.sh` to source this file when run in GitHub
+# workflows; these functions will then override (empty) functions
+# that are are called at the appropriate times during the test runs.
+
+test_skip_test_preamble=t
+
+start_test_output () {
+ test -n "$GIT_TEST_TEE_OUTPUT_FILE" ||
+ die "--github-workflow-markup requires --verbose-log"
+ github_markup_output="${GIT_TEST_TEE_OUTPUT_FILE%.out}.markup"
+ >$github_markup_output
+ GIT_TEST_TEE_OFFSET=0
+}
+
+# No need to override start_test_case_output
+
+finalize_test_case_output () {
+ test_case_result=$1
+ shift
+ case "$test_case_result" in
+ failure)
+ echo >>$github_markup_output "::error::failed: $this_test.$test_count $1"
+ ;;
+ fixed)
+ echo >>$github_markup_output "::notice::fixed: $this_test.$test_count $1"
+ ;;
+ ok)
+ # Exit without printing the "ok" tests
+ return
+ ;;
+ esac
+ echo >>$github_markup_output "::group::$test_case_result: $this_test.$test_count $*"
+ test-tool >>$github_markup_output path-utils skip-n-bytes \
+ "$GIT_TEST_TEE_OUTPUT_FILE" $GIT_TEST_TEE_OFFSET
+ echo >>$github_markup_output "::endgroup::"
+}
+
+# No need to override finalize_test_output
diff --git a/t/test-lib-junit.sh b/t/test-lib-junit.sh
new file mode 100644
index 0000000000..c959183c7e
--- /dev/null
+++ b/t/test-lib-junit.sh
@@ -0,0 +1,132 @@
+# Library of functions to format test scripts' output in JUnit XML
+# format, to support Git's test suite result to be presented in an
+# easily digestible way on Azure Pipelines.
+#
+# Copyright (c) 2022 Johannes Schindelin
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see http://www.gnu.org/licenses/ .
+#
+# The idea is for `test-lib.sh` to source this file when the user asks
+# for JUnit XML; these functions will then override (empty) functions
+# that are are called at the appropriate times during the test runs.
+
+start_test_output () {
+ junit_xml_dir="$TEST_OUTPUT_DIRECTORY/out"
+ mkdir -p "$junit_xml_dir"
+ junit_xml_base=${1##*/}
+ junit_xml_path="$junit_xml_dir/TEST-${junit_xml_base%.sh}.xml"
+ junit_attrs="name=\"${junit_xml_base%.sh}\""
+ junit_attrs="$junit_attrs timestamp=\"$(TZ=UTC \
+ date +%Y-%m-%dT%H:%M:%S)\""
+ write_junit_xml --truncate "<testsuites>" " <testsuite $junit_attrs>"
+ junit_suite_start=$(test-tool date getnanos)
+ if test -n "$GIT_TEST_TEE_OUTPUT_FILE"
+ then
+ GIT_TEST_TEE_OFFSET=0
+ fi
+}
+
+start_test_case_output () {
+ junit_start=$(test-tool date getnanos)
+}
+
+finalize_test_case_output () {
+ test_case_result=$1
+ shift
+ case "$test_case_result" in
+ ok)
+ set "$*"
+ ;;
+ failure)
+ junit_insert="<failure message=\"not ok $test_count -"
+ junit_insert="$junit_insert $(xml_attr_encode --no-lf "$1")\">"
+ junit_insert="$junit_insert $(xml_attr_encode \
+ "$(if test -n "$GIT_TEST_TEE_OUTPUT_FILE"
+ then
+ test-tool path-utils skip-n-bytes \
+ "$GIT_TEST_TEE_OUTPUT_FILE" $GIT_TEST_TEE_OFFSET
+ else
+ printf '%s\n' "$@" | sed 1d
+ fi)")"
+ junit_insert="$junit_insert</failure>"
+ if test -n "$GIT_TEST_TEE_OUTPUT_FILE"
+ then
+ junit_insert="$junit_insert<system-err>$(xml_attr_encode \
+ "$(cat "$GIT_TEST_TEE_OUTPUT_FILE")")</system-err>"
+ fi
+ set "$1" " $junit_insert"
+ ;;
+ fixed)
+ set "$* (breakage fixed)"
+ ;;
+ broken)
+ set "$* (known breakage)"
+ ;;
+ skip)
+ message="$(xml_attr_encode --no-lf "$skipped_reason")"
+ set "$1" " <skipped message=\"$message\" />"
+ ;;
+ esac
+
+ junit_attrs="name=\"$(xml_attr_encode --no-lf "$this_test.$test_count $1")\""
+ shift
+ junit_attrs="$junit_attrs classname=\"$this_test\""
+ junit_attrs="$junit_attrs time=\"$(test-tool \
+ date getnanos $junit_start)\""
+ write_junit_xml "$(printf '%s\n' \
+ " <testcase $junit_attrs>" "$@" " </testcase>")"
+ junit_have_testcase=t
+}
+
+finalize_test_output () {
+ if test -n "$junit_xml_path"
+ then
+ test -n "$junit_have_testcase" || {
+ junit_start=$(test-tool date getnanos)
+ write_junit_xml_testcase "all tests skipped"
+ }
+
+ # adjust the overall time
+ junit_time=$(test-tool date getnanos $junit_suite_start)
+ sed -e "s/\(<testsuite.*\) time=\"[^\"]*\"/\1/" \
+ -e "s/<testsuite [^>]*/& time=\"$junit_time\"/" \
+ -e '/^ *<\/testsuite/d' \
+ <"$junit_xml_path" >"$junit_xml_path.new"
+ mv "$junit_xml_path.new" "$junit_xml_path"
+
+ write_junit_xml " </testsuite>" "</testsuites>"
+ write_junit_xml=
+ fi
+}
+
+write_junit_xml () {
+ case "$1" in
+ --truncate)
+ >"$junit_xml_path"
+ junit_have_testcase=
+ shift
+ ;;
+ esac
+ printf '%s\n' "$@" >>"$junit_xml_path"
+}
+
+xml_attr_encode () {
+ if test "x$1" = "x--no-lf"
+ then
+ shift
+ printf '%s' "$*" | test-tool xml-encode
+ else
+ printf '%s\n' "$@" | test-tool xml-encode
+ fi
+}
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 8ba5ca1534..736c6447ec 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -137,6 +137,12 @@ mark_option_requires_arg () {
store_arg_to=$2
}
+# These functions can be overridden e.g. to output JUnit XML
+start_test_output () { :; }
+start_test_case_output () { :; }
+finalize_test_case_output () { :; }
+finalize_test_output () { :; }
+
parse_option () {
local opt="$1"
@@ -196,7 +202,10 @@ parse_option () {
tee=t
;;
--write-junit-xml)
- write_junit_xml=t
+ . "$TEST_DIRECTORY/test-lib-junit.sh"
+ ;;
+ --github-workflow-markup)
+ . "$TEST_DIRECTORY/test-lib-github-workflow-markup.sh"
;;
--stress)
stress=t ;;
@@ -664,7 +673,7 @@ exec 6<&0
exec 7>&2
_error_exit () {
- finalize_junit_xml
+ finalize_test_output
GIT_EXIT_OK=t
exit 1
}
@@ -774,35 +783,13 @@ trap '{ code=$?; set +x; } 2>/dev/null; exit $code' INT TERM HUP
# the test_expect_* functions instead.
test_ok_ () {
- if test -n "$write_junit_xml"
- then
- write_junit_xml_testcase "$*"
- fi
test_success=$(($test_success + 1))
say_color "" "ok $test_count - $@"
+ finalize_test_case_output ok "$@"
}
test_failure_ () {
- if test -n "$write_junit_xml"
- then
- junit_insert="<failure message=\"not ok $test_count -"
- junit_insert="$junit_insert $(xml_attr_encode "$1")\">"
- junit_insert="$junit_insert $(xml_attr_encode \
- "$(if test -n "$GIT_TEST_TEE_OUTPUT_FILE"
- then
- test-tool path-utils skip-n-bytes \
- "$GIT_TEST_TEE_OUTPUT_FILE" $GIT_TEST_TEE_OFFSET
- else
- printf '%s\n' "$@" | sed 1d
- fi)")"
- junit_insert="$junit_insert</failure>"
- if test -n "$GIT_TEST_TEE_OUTPUT_FILE"
- then
- junit_insert="$junit_insert<system-err>$(xml_attr_encode \
- "$(cat "$GIT_TEST_TEE_OUTPUT_FILE")")</system-err>"
- fi
- write_junit_xml_testcase "$1" " $junit_insert"
- fi
+ failure_label=$1
test_failure=$(($test_failure + 1))
say_color error "not ok $test_count - $1"
shift
@@ -812,24 +799,19 @@ test_failure_ () {
say_color error "1..$test_count"
_error_exit
fi
+ finalize_test_case_output failure "$failure_label" "$@"
}
test_known_broken_ok_ () {
- if test -n "$write_junit_xml"
- then
- write_junit_xml_testcase "$* (breakage fixed)"
- fi
test_fixed=$(($test_fixed+1))
say_color error "ok $test_count - $@ # TODO known breakage vanished"
+ finalize_test_case_output fixed "$@"
}
test_known_broken_failure_ () {
- if test -n "$write_junit_xml"
- then
- write_junit_xml_testcase "$* (known breakage)"
- fi
test_broken=$(($test_broken+1))
say_color warn "not ok $test_count - $@ # TODO known breakage"
+ finalize_test_case_output broken "$@"
}
test_debug () {
@@ -1104,10 +1086,7 @@ test_start_ () {
test_count=$(($test_count+1))
maybe_setup_verbose
maybe_setup_valgrind
- if test -n "$write_junit_xml"
- then
- junit_start=$(test-tool date getnanos)
- fi
+ start_test_case_output "$@"
}
test_finish_ () {
@@ -1158,15 +1137,10 @@ test_skip () {
case "$to_skip" in
t)
- if test -n "$write_junit_xml"
- then
- message="$(xml_attr_encode "$skipped_reason")"
- write_junit_xml_testcase "$1" \
- " <skipped message=\"$message\" />"
- fi
say_color skip "ok $test_count # skip $1 ($skipped_reason)"
: true
+ finalize_test_case_output skip "$@"
;;
*)
false
@@ -1179,53 +1153,6 @@ test_at_end_hook_ () {
:
}
-write_junit_xml () {
- case "$1" in
- --truncate)
- >"$junit_xml_path"
- junit_have_testcase=
- shift
- ;;
- esac
- printf '%s\n' "$@" >>"$junit_xml_path"
-}
-
-xml_attr_encode () {
- printf '%s\n' "$@" | test-tool xml-encode
-}
-
-write_junit_xml_testcase () {
- junit_attrs="name=\"$(xml_attr_encode "$this_test.$test_count $1")\""
- shift
- junit_attrs="$junit_attrs classname=\"$this_test\""
- junit_attrs="$junit_attrs time=\"$(test-tool \
- date getnanos $junit_start)\""
- write_junit_xml "$(printf '%s\n' \
- " <testcase $junit_attrs>" "$@" " </testcase>")"
- junit_have_testcase=t
-}
-
-finalize_junit_xml () {
- if test -n "$write_junit_xml" && test -n "$junit_xml_path"
- then
- test -n "$junit_have_testcase" || {
- junit_start=$(test-tool date getnanos)
- write_junit_xml_testcase "all tests skipped"
- }
-
- # adjust the overall time
- junit_time=$(test-tool date getnanos $junit_suite_start)
- sed -e "s/\(<testsuite.*\) time=\"[^\"]*\"/\1/" \
- -e "s/<testsuite [^>]*/& time=\"$junit_time\"/" \
- -e '/^ *<\/testsuite/d' \
- <"$junit_xml_path" >"$junit_xml_path.new"
- mv "$junit_xml_path.new" "$junit_xml_path"
-
- write_junit_xml " </testsuite>" "</testsuites>"
- write_junit_xml=
- fi
-}
-
test_atexit_cleanup=:
test_atexit_handler () {
# In a succeeding test script 'test_atexit_handler' is invoked
@@ -1248,7 +1175,7 @@ test_done () {
# removed, so the commands can access pidfiles and socket files.
test_atexit_handler
- finalize_junit_xml
+ finalize_test_output
if test -z "$HARNESS_ACTIVE"
then
@@ -1539,22 +1466,7 @@ fi
# in subprocesses like git equals our $PWD (for pathname comparisons).
cd -P "$TRASH_DIRECTORY" || exit 1
-if test -n "$write_junit_xml"
-then
- junit_xml_dir="$TEST_OUTPUT_DIRECTORY/out"
- mkdir -p "$junit_xml_dir"
- junit_xml_base=${0##*/}
- junit_xml_path="$junit_xml_dir/TEST-${junit_xml_base%.sh}.xml"
- junit_attrs="name=\"${junit_xml_base%.sh}\""
- junit_attrs="$junit_attrs timestamp=\"$(TZ=UTC \
- date +%Y-%m-%dT%H:%M:%S)\""
- write_junit_xml --truncate "<testsuites>" " <testsuite $junit_attrs>"
- junit_suite_start=$(test-tool date getnanos)
- if test -n "$GIT_TEST_TEE_OUTPUT_FILE"
- then
- GIT_TEST_TEE_OFFSET=0
- fi
-fi
+start_test_output "$0"
# Convenience
# A regexp to match 5 and 35 hexdigits