summaryrefslogtreecommitdiff
path: root/t
diff options
context:
space:
mode:
Diffstat (limited to 't')
-rw-r--r--t/README12
-rw-r--r--t/annotate-tests.sh34
-rw-r--r--t/helper/test-bitmap.c24
-rw-r--r--t/helper/test-bloom.c2
-rw-r--r--t/helper/test-example-decorate.c6
-rw-r--r--t/helper/test-fast-rebase.c54
-rw-r--r--t/helper/test-path-utils.c46
-rw-r--r--t/helper/test-read-cache.c66
-rw-r--r--t/helper/test-submodule-nested-repo-config.c2
-rw-r--r--t/helper/test-tool.c2
-rw-r--r--t/helper/test-tool.h2
-rw-r--r--t/helper/test-userdiff.c46
-rw-r--r--t/lib-encoding.sh25
-rw-r--r--t/lib-parallel-checkout.sh45
-rwxr-xr-xt/perf/p2000-sparse-operations.sh101
-rwxr-xr-xt/perf/p5600-partial-clone.sh16
-rwxr-xr-xt/t0001-init.sh28
-rwxr-xr-xt/t0027-auto-crlf.sh7
-rwxr-xr-xt/t0028-working-tree-encoding.sh25
-rwxr-xr-xt/t0060-path-utils.sh30
-rwxr-xr-xt/t1091-sparse-checkout-builtin.sh13
-rwxr-xr-xt/t1092-sparse-checkout-compatibility.sh143
-rwxr-xr-xt/t1300-config.sh100
-rwxr-xr-xt/t1306-xdg-files.sh2
-rwxr-xr-xt/t1400-update-ref.sh9
-rwxr-xr-xt/t1500-rev-parse.sh4
-rwxr-xr-xt/t2080-parallel-checkout-basics.sh229
-rwxr-xr-xt/t2081-parallel-checkout-collisions.sh162
-rwxr-xr-xt/t2082-parallel-checkout-attributes.sh194
-rwxr-xr-xt/t3001-ls-files-others-exclude.sh5
-rwxr-xr-xt/t3003-ls-files-exclude.sh4
-rwxr-xr-xt/t3070-wildmatch.sh5
-rwxr-xr-xt/t3418-rebase-continue.sh27
-rwxr-xr-xt/t3437-rebase-fixup-options.sh6
-rwxr-xr-xt/t3512-cherry-pick-submodule.sh7
-rwxr-xr-xt/t3513-revert-submodule.sh9
-rwxr-xr-xt/t3602-rm-sparse-checkout.sh78
-rwxr-xr-xt/t3700-add.sh10
-rwxr-xr-xt/t3705-add-sparse-checkout.sh155
-rwxr-xr-xt/t3903-stash.sh2
-rwxr-xr-xt/t3905-stash-include-untracked.sh19
-rwxr-xr-xt/t4013-diff-various.sh55
-rw-r--r--t/t4013/diff.diff-tree_-m_master11
-rw-r--r--t/t4013/diff.log_-m_--raw_master61
-rw-r--r--t/t4013/diff.log_-m_--stat_master66
-rwxr-xr-xt/t4018-diff-funcname.sh53
-rw-r--r--t/t4018/README3
-rw-r--r--t/t4018/scheme-class7
-rw-r--r--t/t4018/scheme-def4
-rw-r--r--t/t4018/scheme-def-variant4
-rw-r--r--t/t4018/scheme-define-slash-public7
-rw-r--r--t/t4018/scheme-define-syntax8
-rw-r--r--t/t4018/scheme-define-variant4
-rw-r--r--t/t4018/scheme-library11
-rw-r--r--t/t4018/scheme-local-define4
-rw-r--r--t/t4018/scheme-module6
-rw-r--r--t/t4018/scheme-top-level-define4
-rw-r--r--t/t4018/scheme-user-defined-define6
-rwxr-xr-xt/t4034-diff-words.sh6
-rw-r--r--t/t4034/scheme/expect11
-rw-r--r--t/t4034/scheme/post6
-rw-r--r--t/t4034/scheme/pre6
-rwxr-xr-xt/t4108-apply-threeway.sh70
-rwxr-xr-xt/t4205-log-pretty-formats.sh15
-rwxr-xr-xt/t4258-am-quoted-cr.sh37
-rw-r--r--t/t4258/mbox12
-rwxr-xr-xt/t5100-mailinfo.sh40
-rw-r--r--t/t5100/quoted-cr-info5
-rw-r--r--t/t5100/quoted-cr-msg2
-rw-r--r--t/t5100/quoted-cr-patch22
-rw-r--r--t/t5100/quoted-cr.mbox47
-rwxr-xr-xt/t5300-pack-object.sh265
-rwxr-xr-xt/t5304-prune.sh16
-rwxr-xr-xt/t5310-pack-bitmaps.sh61
-rwxr-xr-xt/t5316-pack-delta-depth.sh15
-rwxr-xr-xt/t5516-fetch-push.sh35
-rwxr-xr-xt/t5523-push-upstream.sh7
-rwxr-xr-xt/t5551-http-fetch-smart.sh41
-rwxr-xr-xt/t5572-pull-submodule.sh7
-rwxr-xr-xt/t5582-fetch-negative-refspec.sh43
-rwxr-xr-xt/t5600-clone-fail-cleanup.sh7
-rwxr-xr-xt/t5601-clone.sh2
-rwxr-xr-xt/t5616-partial-clone.sh8
-rwxr-xr-xt/t5701-git-serve.sh28
-rwxr-xr-xt/t5702-protocol-v2.sh89
-rwxr-xr-xt/t6030-bisect-porcelain.sh11
-rwxr-xr-xt/t6041-bisect-submodule.sh4
-rwxr-xr-xt/t6112-rev-list-filters-objects.sh72
-rwxr-xr-xt/t6113-rev-list-bitmap-filters.sh68
-rwxr-xr-xt/t6300-for-each-ref.sh16
-rwxr-xr-xt/t6302-for-each-ref-filter.sh19
-rwxr-xr-xt/t6423-merge-rename-directories.sh60
-rwxr-xr-xt/t6428-merge-conflicts-sparse.sh158
-rwxr-xr-xt/t6429-merge-sequence-rename-caching.sh700
-rwxr-xr-xt/t6437-submodule-merge.sh5
-rwxr-xr-xt/t6438-submodule-directory-file-conflicts.sh7
-rwxr-xr-xt/t6501-freshen-objects.sh22
-rwxr-xr-xt/t7011-skip-worktree-reading.sh5
-rwxr-xr-xt/t7012-skip-worktree-writing.sh19
-rwxr-xr-xt/t7063-status-untracked-cache.sh206
-rwxr-xr-xt/t7300-clean.sh42
-rwxr-xr-xt/t7406-submodule-update.sh24
-rwxr-xr-xt/t7450-bad-git-dotfiles.sh (renamed from t/t7415-submodule-names.sh)129
-rwxr-xr-xt/t7500-commit-template-squash-signoff.sh2
-rwxr-xr-xt/t7513-interpret-trailers.sh84
-rwxr-xr-xt/t7519-status-fsmonitor.sh8
-rwxr-xr-xt/t7900-maintenance.sh22
-rwxr-xr-xt/t9001-send-email.sh89
-rwxr-xr-xt/t9117-git-svn-init-clone.sh6
-rwxr-xr-xt/t9148-git-svn-propset.sh27
-rwxr-xr-xt/t9801-git-p4-branch.sh2
-rwxr-xr-xt/t9902-completion.sh22
-rw-r--r--t/test-lib-functions.sh7
-rw-r--r--t/test-lib.sh11
114 files changed, 4283 insertions, 537 deletions
diff --git a/t/README b/t/README
index fd9375b..1a2072b 100644
--- a/t/README
+++ b/t/README
@@ -196,6 +196,11 @@ appropriately before running "make". Short options can be bundled, i.e.
this feature by setting the GIT_TEST_CHAIN_LINT environment
variable to "1" or "0", respectively.
+ A few test scripts disable some of the more advanced
+ chain-linting detection in the name of efficiency. You can
+ override this by setting the GIT_TEST_CHAIN_LINT_HARDER
+ environment variable to "1".
+
--stress::
Run the test script repeatedly in multiple parallel jobs until
one of them fails. Useful for reproducing rare failures in
@@ -436,6 +441,13 @@ and "sha256".
GIT_TEST_WRITE_REV_INDEX=<boolean>, when true enables the
'pack.writeReverseIndex' setting.
+GIT_TEST_SPARSE_INDEX=<boolean>, when true enables index writes to use the
+sparse-index format by default.
+
+GIT_TEST_CHECKOUT_WORKERS=<n> overrides the 'checkout.workers' setting
+to <n> and 'checkout.thresholdForParallelism' to 0, forcing the
+execution of the parallel-checkout code.
+
Naming Tests
------------
diff --git a/t/annotate-tests.sh b/t/annotate-tests.sh
index 29ce890..d3b299e 100644
--- a/t/annotate-tests.sh
+++ b/t/annotate-tests.sh
@@ -479,22 +479,26 @@ test_expect_success 'blame -L ^:RE (absolute: end-of-file)' '
check_count -f hello.c -L$n -L^:ma.. F 4 G 1 H 1
'
-test_expect_success 'setup -L :funcname with userdiff driver' '
- echo "fortran-* diff=fortran" >.gitattributes &&
- fortran_file=fortran-external-function &&
- orig_file="$TEST_DIRECTORY/t4018/$fortran_file" &&
- cp "$orig_file" . &&
- git add "$fortran_file" &&
- GIT_AUTHOR_NAME="A" GIT_AUTHOR_EMAIL="A@test.git" \
- git commit -m "add fortran file" &&
- sed -e "s/ChangeMe/IWasChanged/" <"$orig_file" >"$fortran_file" &&
- git add "$fortran_file" &&
- GIT_AUTHOR_NAME="B" GIT_AUTHOR_EMAIL="B@test.git" \
- git commit -m "change fortran file"
-'
-
test_expect_success 'blame -L :funcname with userdiff driver' '
- check_count -f fortran-external-function -L:RIGHT A 7 B 1
+ cat >file.template <<-\EOF &&
+ DO NOT MATCH THIS LINE
+ function RIGHT(a, b) result(c)
+ AS THE DEFAULT DRIVER WOULD
+
+ integer, intent(in) :: ChangeMe
+ EOF
+
+ fortran_file=file.f03 &&
+ test_when_finished "rm .gitattributes" &&
+ echo "$fortran_file diff=fortran" >.gitattributes &&
+
+ test_commit --author "A <A@test.git>" \
+ "add" "$fortran_file" \
+ "$(cat file.template)" &&
+ test_commit --author "B <B@test.git>" \
+ "change" "$fortran_file" \
+ "$(cat file.template | sed -e s/ChangeMe/IWasChanged/)" &&
+ check_count -f "$fortran_file" -L:RIGHT A 3 B 1
'
test_expect_success 'setup incremental' '
diff --git a/t/helper/test-bitmap.c b/t/helper/test-bitmap.c
new file mode 100644
index 0000000..134a1e9
--- /dev/null
+++ b/t/helper/test-bitmap.c
@@ -0,0 +1,24 @@
+#include "test-tool.h"
+#include "cache.h"
+#include "pack-bitmap.h"
+
+static int bitmap_list_commits(void)
+{
+ return test_bitmap_commits(the_repository);
+}
+
+int cmd__bitmap(int argc, const char **argv)
+{
+ setup_git_directory();
+
+ if (argc != 2)
+ goto usage;
+
+ if (!strcmp(argv[1], "list-commits"))
+ return bitmap_list_commits();
+
+usage:
+ usage("\ttest-tool bitmap list-commits");
+
+ return -1;
+}
diff --git a/t/helper/test-bloom.c b/t/helper/test-bloom.c
index 2a1ae3d..ad3ef1c 100644
--- a/t/helper/test-bloom.c
+++ b/t/helper/test-bloom.c
@@ -48,7 +48,7 @@ static void get_bloom_filter_for_commit(const struct object_id *commit_oid)
static const char *bloom_usage = "\n"
" test-tool bloom get_murmur3 <string>\n"
" test-tool bloom generate_filter <string> [<string>...]\n"
-" test-tool get_filter_for_commit <commit-hex>\n";
+" test-tool bloom get_filter_for_commit <commit-hex>\n";
int cmd__bloom(int argc, const char **argv)
{
diff --git a/t/helper/test-example-decorate.c b/t/helper/test-example-decorate.c
index c8a1cde..b9d1200 100644
--- a/t/helper/test-example-decorate.c
+++ b/t/helper/test-example-decorate.c
@@ -26,8 +26,8 @@ int cmd__example_decorate(int argc, const char **argv)
* Add 2 objects, one with a non-NULL decoration and one with a NULL
* decoration.
*/
- one = lookup_unknown_object(&one_oid);
- two = lookup_unknown_object(&two_oid);
+ one = lookup_unknown_object(the_repository, &one_oid);
+ two = lookup_unknown_object(the_repository, &two_oid);
ret = add_decoration(&n, one, &decoration_a);
if (ret)
BUG("when adding a brand-new object, NULL should be returned");
@@ -56,7 +56,7 @@ int cmd__example_decorate(int argc, const char **argv)
ret = lookup_decoration(&n, two);
if (ret != &decoration_b)
BUG("lookup should return added declaration");
- three = lookup_unknown_object(&three_oid);
+ three = lookup_unknown_object(the_repository, &three_oid);
ret = lookup_decoration(&n, three);
if (ret)
BUG("lookup for unknown object should return NULL");
diff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c
index 3732122..fc2d460 100644
--- a/t/helper/test-fast-rebase.c
+++ b/t/helper/test-fast-rebase.c
@@ -91,7 +91,6 @@ int cmd__fast_rebase(int argc, const char **argv)
struct commit *last_commit = NULL, *last_picked_commit = NULL;
struct object_id head;
struct lock_file lock = LOCK_INIT;
- int clean = 1;
struct strvec rev_walk_args = STRVEC_INIT;
struct rev_info revs;
struct commit *commit;
@@ -124,7 +123,8 @@ int cmd__fast_rebase(int argc, const char **argv)
assert(oideq(&onto->object.oid, &head));
hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
- assert(repo_read_index(the_repository) >= 0);
+ if (repo_read_index(the_repository) < 0)
+ BUG("Could not read index");
repo_init_revisions(the_repository, &revs, NULL);
revs.verbose_header = 1;
@@ -175,11 +175,10 @@ int cmd__fast_rebase(int argc, const char **argv)
free((char*)merge_opt.ancestor);
merge_opt.ancestor = NULL;
if (!result.clean)
- die("Aborting: Hit a conflict and restarting is not implemented.");
+ break;
last_picked_commit = commit;
last_commit = create_commit(result.tree, commit, last_commit);
}
- fprintf(stderr, "\nDone.\n");
/* TODO: There should be some kind of rev_info_free(&revs) call... */
memset(&revs, 0, sizeof(revs));
@@ -188,24 +187,39 @@ int cmd__fast_rebase(int argc, const char **argv)
if (result.clean < 0)
exit(128);
- strbuf_addf(&reflog_msg, "finish rebase %s onto %s",
- oid_to_hex(&last_picked_commit->object.oid),
- oid_to_hex(&last_commit->object.oid));
- if (update_ref(reflog_msg.buf, branch_name.buf,
- &last_commit->object.oid,
- &last_picked_commit->object.oid,
- REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR)) {
- error(_("could not update %s"), argv[4]);
- die("Failed to update %s", argv[4]);
+ if (result.clean) {
+ fprintf(stderr, "\nDone.\n");
+ strbuf_addf(&reflog_msg, "finish rebase %s onto %s",
+ oid_to_hex(&last_picked_commit->object.oid),
+ oid_to_hex(&last_commit->object.oid));
+ if (update_ref(reflog_msg.buf, branch_name.buf,
+ &last_commit->object.oid,
+ &last_picked_commit->object.oid,
+ REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR)) {
+ error(_("could not update %s"), argv[4]);
+ die("Failed to update %s", argv[4]);
+ }
+ 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);
+ } else {
+ fprintf(stderr, "\nAborting: Hit a conflict.\n");
+ strbuf_addf(&reflog_msg, "rebase progress up to %s",
+ oid_to_hex(&last_picked_commit->object.oid));
+ if (update_ref(reflog_msg.buf, "HEAD",
+ &last_commit->object.oid,
+ &head,
+ REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR)) {
+ error(_("could not update %s"), argv[4]);
+ die("Failed to update %s", argv[4]);
+ }
}
- 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);
if (write_locked_index(&the_index, &lock,
COMMIT_LOCK | SKIP_IF_UNCHANGED))
die(_("unable to write %s"), get_index_file());
- return (clean == 0);
+ return (result.clean == 0);
}
diff --git a/t/helper/test-path-utils.c b/t/helper/test-path-utils.c
index 313a153..229ed41 100644
--- a/t/helper/test-path-utils.c
+++ b/t/helper/test-path-utils.c
@@ -172,9 +172,22 @@ static struct test_data dirname_data[] = {
{ NULL, NULL }
};
-static int is_dotgitmodules(const char *path)
+static int check_dotfile(const char *x, const char **argv,
+ int (*is_hfs)(const char *),
+ int (*is_ntfs)(const char *))
{
- return is_hfs_dotgitmodules(path) || is_ntfs_dotgitmodules(path);
+ int res = 0, expect = 1;
+ for (; *argv; argv++) {
+ if (!strcmp("--not", *argv))
+ expect = !expect;
+ else if (expect != (is_hfs(*argv) || is_ntfs(*argv)))
+ res = error("'%s' is %s.git%s", *argv,
+ expect ? "not " : "", x);
+ else
+ fprintf(stderr, "ok: '%s' is %s.git%s\n",
+ *argv, expect ? "" : "not ", x);
+ }
+ return !!res;
}
static int cmp_by_st_size(const void *a, const void *b)
@@ -382,17 +395,24 @@ int cmd__path_utils(int argc, const char **argv)
return test_function(dirname_data, posix_dirname, argv[1]);
if (argc > 2 && !strcmp(argv[1], "is_dotgitmodules")) {
- int res = 0, expect = 1, i;
- for (i = 2; i < argc; i++)
- if (!strcmp("--not", argv[i]))
- expect = !expect;
- else if (expect != is_dotgitmodules(argv[i]))
- res = error("'%s' is %s.gitmodules", argv[i],
- expect ? "not " : "");
- else
- fprintf(stderr, "ok: '%s' is %s.gitmodules\n",
- argv[i], expect ? "" : "not ");
- return !!res;
+ return check_dotfile("modules", argv + 2,
+ is_hfs_dotgitmodules,
+ is_ntfs_dotgitmodules);
+ }
+ if (argc > 2 && !strcmp(argv[1], "is_dotgitignore")) {
+ return check_dotfile("ignore", argv + 2,
+ is_hfs_dotgitignore,
+ is_ntfs_dotgitignore);
+ }
+ if (argc > 2 && !strcmp(argv[1], "is_dotgitattributes")) {
+ return check_dotfile("attributes", argv + 2,
+ is_hfs_dotgitattributes,
+ is_ntfs_dotgitattributes);
+ }
+ if (argc > 2 && !strcmp(argv[1], "is_dotmailmap")) {
+ return check_dotfile("mailmap", argv + 2,
+ is_hfs_dotmailmap,
+ is_ntfs_dotmailmap);
}
if (argc > 2 && !strcmp(argv[1], "file-size")) {
diff --git a/t/helper/test-read-cache.c b/t/helper/test-read-cache.c
index 244977a..b52c174 100644
--- a/t/helper/test-read-cache.c
+++ b/t/helper/test-read-cache.c
@@ -1,36 +1,82 @@
#include "test-tool.h"
#include "cache.h"
#include "config.h"
+#include "blob.h"
+#include "commit.h"
+#include "tree.h"
+#include "sparse-index.h"
+
+static void print_cache_entry(struct cache_entry *ce)
+{
+ const char *type;
+ printf("%06o ", ce->ce_mode & 0177777);
+
+ if (S_ISSPARSEDIR(ce->ce_mode))
+ type = tree_type;
+ else if (S_ISGITLINK(ce->ce_mode))
+ type = commit_type;
+ else
+ type = blob_type;
+
+ printf("%s %s\t%s\n",
+ type,
+ oid_to_hex(&ce->oid),
+ ce->name);
+}
+
+static void print_cache(struct index_state *istate)
+{
+ int i;
+ for (i = 0; i < istate->cache_nr; i++)
+ print_cache_entry(istate->cache[i]);
+}
int cmd__read_cache(int argc, const char **argv)
{
+ struct repository *r = the_repository;
int i, cnt = 1;
const char *name = NULL;
+ int table = 0, expand = 0;
+
+ initialize_the_repository();
+ prepare_repo_settings(r);
+ r->settings.command_requires_full_index = 0;
- if (argc > 1 && skip_prefix(argv[1], "--print-and-refresh=", &name)) {
- argc--;
- argv++;
+ for (++argv, --argc; *argv && starts_with(*argv, "--"); ++argv, --argc) {
+ if (skip_prefix(*argv, "--print-and-refresh=", &name))
+ continue;
+ if (!strcmp(*argv, "--table"))
+ table = 1;
+ else if (!strcmp(*argv, "--expand"))
+ expand = 1;
}
- if (argc == 2)
- cnt = strtol(argv[1], NULL, 0);
+ if (argc == 1)
+ cnt = strtol(argv[0], NULL, 0);
setup_git_directory();
git_config(git_default_config, NULL);
+
for (i = 0; i < cnt; i++) {
- read_cache();
+ repo_read_index(r);
+
+ if (expand)
+ ensure_full_index(r->index);
+
if (name) {
int pos;
- refresh_index(&the_index, REFRESH_QUIET,
+ refresh_index(r->index, REFRESH_QUIET,
NULL, NULL, NULL);
- pos = index_name_pos(&the_index, name, strlen(name));
+ pos = index_name_pos(r->index, name, strlen(name));
if (pos < 0)
die("%s not in index", name);
printf("%s is%s up to date\n", name,
- ce_uptodate(the_index.cache[pos]) ? "" : " not");
+ ce_uptodate(r->index->cache[pos]) ? "" : " not");
write_file(name, "%d\n", i);
}
- discard_cache();
+ if (table)
+ print_cache(r->index);
+ discard_index(r->index);
}
return 0;
}
diff --git a/t/helper/test-submodule-nested-repo-config.c b/t/helper/test-submodule-nested-repo-config.c
index c5fd452..e3f11ff 100644
--- a/t/helper/test-submodule-nested-repo-config.c
+++ b/t/helper/test-submodule-nested-repo-config.c
@@ -18,7 +18,7 @@ int cmd__submodule_nested_repo_config(int argc, const char **argv)
setup_git_directory();
- sub = submodule_from_path(the_repository, &null_oid, argv[1]);
+ sub = submodule_from_path(the_repository, null_oid(), argv[1]);
if (repo_submodule_init(&subrepo, the_repository, sub)) {
die_usage(argv, "Submodule not found.");
}
diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c
index 287aa60..c5bd0c6 100644
--- a/t/helper/test-tool.c
+++ b/t/helper/test-tool.c
@@ -15,6 +15,7 @@ struct test_cmd {
static struct test_cmd cmds[] = {
{ "advise", cmd__advise_if_enabled },
+ { "bitmap", cmd__bitmap },
{ "bloom", cmd__bloom },
{ "chmtime", cmd__chmtime },
{ "config", cmd__config },
@@ -72,6 +73,7 @@ static struct test_cmd cmds[] = {
{ "submodule-nested-repo-config", cmd__submodule_nested_repo_config },
{ "subprocess", cmd__subprocess },
{ "trace2", cmd__trace2 },
+ { "userdiff", cmd__userdiff },
{ "urlmatch-normalization", cmd__urlmatch_normalization },
{ "xml-encode", cmd__xml_encode },
{ "wildmatch", cmd__wildmatch },
diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h
index 9ea4b31..e8069a3 100644
--- a/t/helper/test-tool.h
+++ b/t/helper/test-tool.h
@@ -5,6 +5,7 @@
#include "git-compat-util.h"
int cmd__advise_if_enabled(int argc, const char **argv);
+int cmd__bitmap(int argc, const char **argv);
int cmd__bloom(int argc, const char **argv);
int cmd__chmtime(int argc, const char **argv);
int cmd__config(int argc, const char **argv);
@@ -62,6 +63,7 @@ int cmd__submodule_config(int argc, const char **argv);
int cmd__submodule_nested_repo_config(int argc, const char **argv);
int cmd__subprocess(int argc, const char **argv);
int cmd__trace2(int argc, const char **argv);
+int cmd__userdiff(int argc, const char **argv);
int cmd__urlmatch_normalization(int argc, const char **argv);
int cmd__xml_encode(int argc, const char **argv);
int cmd__wildmatch(int argc, const char **argv);
diff --git a/t/helper/test-userdiff.c b/t/helper/test-userdiff.c
new file mode 100644
index 0000000..f013f8a
--- /dev/null
+++ b/t/helper/test-userdiff.c
@@ -0,0 +1,46 @@
+#include "test-tool.h"
+#include "cache.h"
+#include "userdiff.h"
+#include "config.h"
+
+static int driver_cb(struct userdiff_driver *driver,
+ enum userdiff_driver_type type, void *priv)
+{
+ enum userdiff_driver_type *want_type = priv;
+ if (type & *want_type && driver->funcname.pattern)
+ puts(driver->name);
+ return 0;
+}
+
+static int cmd__userdiff_config(const char *var, const char *value, void *cb)
+{
+ if (userdiff_config(var, value) < 0)
+ return -1;
+ return 0;
+}
+
+int cmd__userdiff(int argc, const char **argv)
+{
+ enum userdiff_driver_type want = 0;
+ if (argc != 2)
+ return 1;
+
+ if (!strcmp(argv[1], "list-drivers"))
+ want = (USERDIFF_DRIVER_TYPE_BUILTIN |
+ USERDIFF_DRIVER_TYPE_CUSTOM);
+ else if (!strcmp(argv[1], "list-builtin-drivers"))
+ want = USERDIFF_DRIVER_TYPE_BUILTIN;
+ else if (!strcmp(argv[1], "list-custom-drivers"))
+ want = USERDIFF_DRIVER_TYPE_CUSTOM;
+ else
+ return error("unknown argument %s", argv[1]);
+
+ if (want & USERDIFF_DRIVER_TYPE_CUSTOM) {
+ setup_git_directory();
+ git_config(cmd__userdiff_config, NULL);
+ }
+
+ for_each_userdiff_driver(driver_cb, &want);
+
+ return 0;
+}
diff --git a/t/lib-encoding.sh b/t/lib-encoding.sh
new file mode 100644
index 0000000..2dabc8c
--- /dev/null
+++ b/t/lib-encoding.sh
@@ -0,0 +1,25 @@
+# Encoding helpers
+
+test_lazy_prereq NO_UTF16_BOM '
+ test $(printf abc | iconv -f UTF-8 -t UTF-16 | wc -c) = 6
+'
+
+test_lazy_prereq NO_UTF32_BOM '
+ test $(printf abc | iconv -f UTF-8 -t UTF-32 | wc -c) = 12
+'
+
+write_utf16 () {
+ if test_have_prereq NO_UTF16_BOM
+ then
+ printf '\376\377'
+ fi &&
+ iconv -f UTF-8 -t UTF-16
+}
+
+write_utf32 () {
+ if test_have_prereq NO_UTF32_BOM
+ then
+ printf '\0\0\376\377'
+ fi &&
+ iconv -f UTF-8 -t UTF-32
+}
diff --git a/t/lib-parallel-checkout.sh b/t/lib-parallel-checkout.sh
new file mode 100644
index 0000000..83b279a
--- /dev/null
+++ b/t/lib-parallel-checkout.sh
@@ -0,0 +1,45 @@
+# Helpers for tests invoking parallel-checkout
+
+# Parallel checkout tests need full control of the number of workers
+unset GIT_TEST_CHECKOUT_WORKERS
+
+set_checkout_config () {
+ if test $# -ne 2
+ then
+ BUG "usage: set_checkout_config <workers> <threshold>"
+ fi &&
+
+ test_config_global checkout.workers $1 &&
+ test_config_global checkout.thresholdForParallelism $2
+}
+
+# Run "${@:2}" and check that $1 checkout workers were used
+test_checkout_workers () {
+ if test $# -lt 2
+ then
+ BUG "too few arguments to test_checkout_workers"
+ fi &&
+
+ local expected_workers=$1 &&
+ shift &&
+
+ local trace_file=trace-test-checkout-workers &&
+ rm -f "$trace_file" &&
+ GIT_TRACE2="$(pwd)/$trace_file" "$@" 2>&8 &&
+
+ local workers="$(grep "child_start\[..*\] git checkout--worker" "$trace_file" | wc -l)" &&
+ test $workers -eq $expected_workers &&
+ rm "$trace_file"
+} 8>&2 2>&4
+
+# Verify that both the working tree and the index were created correctly
+verify_checkout () {
+ if test $# -ne 1
+ then
+ BUG "usage: verify_checkout <repository path>"
+ fi &&
+
+ git -C "$1" diff-index --ignore-submodules=none --exit-code HEAD -- &&
+ git -C "$1" status --porcelain >"$1".status &&
+ test_must_be_empty "$1".status
+}
diff --git a/t/perf/p2000-sparse-operations.sh b/t/perf/p2000-sparse-operations.sh
new file mode 100755
index 0000000..94513c9
--- /dev/null
+++ b/t/perf/p2000-sparse-operations.sh
@@ -0,0 +1,101 @@
+#!/bin/sh
+
+test_description="test performance of Git operations using the index"
+
+. ./perf-lib.sh
+
+test_perf_default_repo
+
+SPARSE_CONE=f2/f4/f1
+
+test_expect_success 'setup repo and indexes' '
+ git reset --hard HEAD &&
+
+ # Remove submodules from the example repo, because our
+ # duplication of the entire repo creates an unlikely data shape.
+ if git config --file .gitmodules --get-regexp "submodule.*.path" >modules
+ then
+ git rm $(awk "{print \$2}" modules) &&
+ git commit -m "remove submodules" || return 1
+ fi &&
+
+ echo bogus >a &&
+ cp a b &&
+ git add a b &&
+ git commit -m "level 0" &&
+ BLOB=$(git rev-parse HEAD:a) &&
+ OLD_COMMIT=$(git rev-parse HEAD) &&
+ OLD_TREE=$(git rev-parse HEAD^{tree}) &&
+
+ for i in $(test_seq 1 4)
+ do
+ cat >in <<-EOF &&
+ 100755 blob $BLOB a
+ 040000 tree $OLD_TREE f1
+ 040000 tree $OLD_TREE f2
+ 040000 tree $OLD_TREE f3
+ 040000 tree $OLD_TREE f4
+ EOF
+ NEW_TREE=$(git mktree <in) &&
+ NEW_COMMIT=$(git commit-tree $NEW_TREE -p $OLD_COMMIT -m "level $i") &&
+ OLD_TREE=$NEW_TREE &&
+ OLD_COMMIT=$NEW_COMMIT || return 1
+ done &&
+
+ git sparse-checkout init --cone &&
+ git branch -f wide $OLD_COMMIT &&
+ git -c core.sparseCheckoutCone=true clone --branch=wide --sparse . full-index-v3 &&
+ (
+ cd full-index-v3 &&
+ git sparse-checkout init --cone &&
+ git sparse-checkout set $SPARSE_CONE &&
+ git config index.version 3 &&
+ git update-index --index-version=3
+ ) &&
+ git -c core.sparseCheckoutCone=true clone --branch=wide --sparse . full-index-v4 &&
+ (
+ cd full-index-v4 &&
+ git sparse-checkout init --cone &&
+ git sparse-checkout set $SPARSE_CONE &&
+ git config index.version 4 &&
+ git update-index --index-version=4
+ ) &&
+ git -c core.sparseCheckoutCone=true clone --branch=wide --sparse . sparse-index-v3 &&
+ (
+ cd sparse-index-v3 &&
+ git sparse-checkout init --cone --sparse-index &&
+ git sparse-checkout set $SPARSE_CONE &&
+ git config index.version 3 &&
+ git update-index --index-version=3
+ ) &&
+ git -c core.sparseCheckoutCone=true clone --branch=wide --sparse . sparse-index-v4 &&
+ (
+ cd sparse-index-v4 &&
+ git sparse-checkout init --cone --sparse-index &&
+ git sparse-checkout set $SPARSE_CONE &&
+ git config index.version 4 &&
+ git update-index --index-version=4
+ )
+'
+
+test_perf_on_all () {
+ command="$@"
+ for repo in full-index-v3 full-index-v4 \
+ sparse-index-v3 sparse-index-v4
+ do
+ test_perf "$command ($repo)" "
+ (
+ cd $repo &&
+ echo >>$SPARSE_CONE/a &&
+ $command
+ )
+ "
+ done
+}
+
+test_perf_on_all git status
+test_perf_on_all git add -A
+test_perf_on_all git add .
+test_perf_on_all git commit -a -m A
+
+test_done
diff --git a/t/perf/p5600-partial-clone.sh b/t/perf/p5600-partial-clone.sh
index 3e04bd2..a965f2c 100755
--- a/t/perf/p5600-partial-clone.sh
+++ b/t/perf/p5600-partial-clone.sh
@@ -23,4 +23,20 @@ test_perf 'checkout of result' '
git -C worktree checkout -f
'
+test_perf 'fsck' '
+ git -C bare.git fsck
+'
+
+test_perf 'count commits' '
+ git -C bare.git rev-list --all --count
+'
+
+test_perf 'count non-promisor commits' '
+ git -C bare.git rev-list --all --count --exclude-promisor-objects
+'
+
+test_perf 'gc' '
+ git -C bare.git gc
+'
+
test_done
diff --git a/t/t0001-init.sh b/t/t0001-init.sh
index 0803994..acd662e 100755
--- a/t/t0001-init.sh
+++ b/t/t0001-init.sh
@@ -186,21 +186,33 @@ test_expect_success 'init with --template (blank)' '
test_path_is_missing template-blank/.git/info/exclude
'
-test_expect_success 'init with init.templatedir set' '
- mkdir templatedir-source &&
- echo Content >templatedir-source/file &&
- test_config_global init.templatedir "${HOME}/templatedir-source" &&
+init_no_templatedir_env () {
(
- mkdir templatedir-set &&
- cd templatedir-set &&
sane_unset GIT_TEMPLATE_DIR &&
NO_SET_GIT_TEMPLATE_DIR=t &&
export NO_SET_GIT_TEMPLATE_DIR &&
- git init
- ) &&
+ git init "$1"
+ )
+}
+
+test_expect_success 'init with init.templatedir set' '
+ mkdir templatedir-source &&
+ echo Content >templatedir-source/file &&
+ test_config_global init.templatedir "${HOME}/templatedir-source" &&
+
+ init_no_templatedir_env templatedir-set &&
test_cmp templatedir-source/file templatedir-set/.git/file
'
+test_expect_success 'init with init.templatedir using ~ expansion' '
+ mkdir -p templatedir-source &&
+ echo Content >templatedir-source/file &&
+ test_config_global init.templatedir "~/templatedir-source" &&
+
+ init_no_templatedir_env templatedir-expansion &&
+ test_cmp templatedir-source/file templatedir-expansion/.git/file
+'
+
test_expect_success 'init --bare/--shared overrides system/global config' '
test_config_global core.bare false &&
test_config_global core.sharedRepository 0640 &&
diff --git a/t/t0027-auto-crlf.sh b/t/t0027-auto-crlf.sh
index d24d5ac..4a5c5c6 100755
--- a/t/t0027-auto-crlf.sh
+++ b/t/t0027-auto-crlf.sh
@@ -386,7 +386,9 @@ test_expect_success 'setup main' '
test_tick
'
-
+# Disable extra chain-linting for the next set of tests. There are many
+# auto-generated ones that are not worth checking over and over.
+GIT_TEST_CHAIN_LINT_HARDER_DEFAULT=0
warn_LF_CRLF="LF will be replaced by CRLF"
warn_CRLF_LF="CRLF will be replaced by LF"
@@ -597,6 +599,9 @@ do
checkout_files auto "$id" "" false native $NL CRLF CRLF_mix_LF LF_mix_CR LF_nul
done
+# The rest of the tests are unique; do the usual linting.
+unset GIT_TEST_CHAIN_LINT_HARDER_DEFAULT
+
# Should be the last test case: remove some files from the worktree
test_expect_success 'ls-files --eol -d -z' '
rm crlf_false_attr__CRLF.txt crlf_false_attr__CRLF_mix_LF.txt crlf_false_attr__LF.txt .gitattributes &&
diff --git a/t/t0028-working-tree-encoding.sh b/t/t0028-working-tree-encoding.sh
index f970a98..82905a2 100755
--- a/t/t0028-working-tree-encoding.sh
+++ b/t/t0028-working-tree-encoding.sh
@@ -6,33 +6,10 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-encoding.sh"
GIT_TRACE_WORKING_TREE_ENCODING=1 && export GIT_TRACE_WORKING_TREE_ENCODING
-test_lazy_prereq NO_UTF16_BOM '
- test $(printf abc | iconv -f UTF-8 -t UTF-16 | wc -c) = 6
-'
-
-test_lazy_prereq NO_UTF32_BOM '
- test $(printf abc | iconv -f UTF-8 -t UTF-32 | wc -c) = 12
-'
-
-write_utf16 () {
- if test_have_prereq NO_UTF16_BOM
- then
- printf '\376\377'
- fi &&
- iconv -f UTF-8 -t UTF-16
-}
-
-write_utf32 () {
- if test_have_prereq NO_UTF32_BOM
- then
- printf '\0\0\376\377'
- fi &&
- iconv -f UTF-8 -t UTF-32
-}
-
test_expect_success 'setup test files' '
git config core.eol lf &&
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 0ff06b5..de49607 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -468,6 +468,36 @@ test_expect_success 'match .gitmodules' '
.gitmodules,:\$DATA
'
+test_expect_success 'match .gitattributes' '
+ test-tool path-utils is_dotgitattributes \
+ .gitattributes \
+ .git${u200c}attributes \
+ .Gitattributes \
+ .gitattributeS \
+ GITATT~1 \
+ GI7D29~1
+'
+
+test_expect_success 'match .gitignore' '
+ test-tool path-utils is_dotgitignore \
+ .gitignore \
+ .git${u200c}ignore \
+ .Gitignore \
+ .gitignorE \
+ GITIGN~1 \
+ GI250A~1
+'
+
+test_expect_success 'match .mailmap' '
+ test-tool path-utils is_dotmailmap \
+ .mailmap \
+ .mail${u200c}map \
+ .Mailmap \
+ .mailmaP \
+ MAILMA~1 \
+ MABA30~1
+'
+
test_expect_success MINGW 'is_valid_path() on Windows' '
test-tool path-utils is_valid_path \
win32 \
diff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh
index fc64e9e..38fc834 100755
--- a/t/t1091-sparse-checkout-builtin.sh
+++ b/t/t1091-sparse-checkout-builtin.sh
@@ -205,6 +205,19 @@ test_expect_success 'sparse-checkout disable' '
check_files repo a deep folder1 folder2
'
+test_expect_success 'sparse-index enabled and disabled' '
+ git -C repo sparse-checkout init --cone --sparse-index &&
+ test_cmp_config -C repo true index.sparse &&
+ test-tool -C repo read-cache --table >cache &&
+ grep " tree " cache &&
+
+ git -C repo sparse-checkout disable &&
+ test-tool -C repo read-cache --table >cache &&
+ ! grep " tree " cache &&
+ git -C repo config --list >config &&
+ ! grep index.sparse config
+'
+
test_expect_success 'cone mode: init and set' '
git -C repo sparse-checkout init --cone &&
git -C repo config --list >config &&
diff --git a/t/t1092-sparse-checkout-compatibility.sh b/t/t1092-sparse-checkout-compatibility.sh
index 8cd3e5a..e9a815c 100755
--- a/t/t1092-sparse-checkout-compatibility.sh
+++ b/t/t1092-sparse-checkout-compatibility.sh
@@ -2,11 +2,15 @@
test_description='compare full workdir to sparse workdir'
+GIT_TEST_SPLIT_INDEX=0
+GIT_TEST_SPARSE_INDEX=
+
. ./test-lib.sh
test_expect_success 'setup' '
git init initial-repo &&
(
+ GIT_TEST_SPARSE_INDEX=0 &&
cd initial-repo &&
echo a >a &&
echo "after deep" >e &&
@@ -87,39 +91,102 @@ init_repos () {
cp -r initial-repo sparse-checkout &&
git -C sparse-checkout reset --hard &&
- git -C sparse-checkout sparse-checkout init --cone &&
+
+ cp -r initial-repo sparse-index &&
+ git -C sparse-index reset --hard &&
# initialize sparse-checkout definitions
- git -C sparse-checkout sparse-checkout set deep
+ git -C sparse-checkout sparse-checkout init --cone &&
+ git -C sparse-checkout sparse-checkout set deep &&
+ git -C sparse-index sparse-checkout init --cone --sparse-index &&
+ test_cmp_config -C sparse-index true index.sparse &&
+ git -C sparse-index sparse-checkout set deep
}
run_on_sparse () {
(
cd sparse-checkout &&
- $* >../sparse-checkout-out 2>../sparse-checkout-err
+ GIT_PROGRESS_DELAY=100000 "$@" >../sparse-checkout-out 2>../sparse-checkout-err
+ ) &&
+ (
+ cd sparse-index &&
+ GIT_PROGRESS_DELAY=100000 "$@" >../sparse-index-out 2>../sparse-index-err
)
}
run_on_all () {
(
cd full-checkout &&
- $* >../full-checkout-out 2>../full-checkout-err
+ GIT_PROGRESS_DELAY=100000 "$@" >../full-checkout-out 2>../full-checkout-err
) &&
- run_on_sparse $*
+ run_on_sparse "$@"
}
test_all_match () {
- run_on_all $* &&
+ run_on_all "$@" &&
test_cmp full-checkout-out sparse-checkout-out &&
- test_cmp full-checkout-err sparse-checkout-err
+ test_cmp full-checkout-out sparse-index-out &&
+ test_cmp full-checkout-err sparse-checkout-err &&
+ test_cmp full-checkout-err sparse-index-err
+}
+
+test_sparse_match () {
+ run_on_sparse "$@" &&
+ test_cmp sparse-checkout-out sparse-index-out &&
+ test_cmp sparse-checkout-err sparse-index-err
}
+test_expect_success 'sparse-index contents' '
+ init_repos &&
+
+ test-tool -C sparse-index read-cache --table >cache &&
+ for dir in folder1 folder2 x
+ do
+ TREE=$(git -C sparse-index rev-parse HEAD:$dir) &&
+ grep "040000 tree $TREE $dir/" cache \
+ || return 1
+ done &&
+
+ git -C sparse-index sparse-checkout set folder1 &&
+
+ test-tool -C sparse-index read-cache --table >cache &&
+ for dir in deep folder2 x
+ do
+ TREE=$(git -C sparse-index rev-parse HEAD:$dir) &&
+ grep "040000 tree $TREE $dir/" cache \
+ || return 1
+ done &&
+
+ git -C sparse-index sparse-checkout set deep/deeper1 &&
+
+ test-tool -C sparse-index read-cache --table >cache &&
+ for dir in deep/deeper2 folder1 folder2 x
+ do
+ TREE=$(git -C sparse-index rev-parse HEAD:$dir) &&
+ grep "040000 tree $TREE $dir/" cache \
+ || return 1
+ done &&
+
+ # Disabling the sparse-index removes tree entries with full ones
+ git -C sparse-index sparse-checkout init --no-sparse-index &&
+
+ test-tool -C sparse-index read-cache --table >cache &&
+ ! grep "040000 tree" cache &&
+ test_sparse_match test-tool read-cache --table
+'
+
+test_expect_success 'expanded in-memory index matches full index' '
+ init_repos &&
+ test_sparse_match test-tool read-cache --expand --table
+'
+
test_expect_success 'status with options' '
init_repos &&
+ test_sparse_match ls &&
test_all_match git status --porcelain=v2 &&
test_all_match git status --porcelain=v2 -z -u &&
test_all_match git status --porcelain=v2 -uno &&
- run_on_all "touch README.md" &&
+ run_on_all touch README.md &&
test_all_match git status --porcelain=v2 &&
test_all_match git status --porcelain=v2 -z -u &&
test_all_match git status --porcelain=v2 -uno &&
@@ -135,7 +202,7 @@ test_expect_success 'add, commit, checkout' '
write_script edit-contents <<-\EOF &&
echo text >>$1
EOF
- run_on_all "../edit-contents README.md" &&
+ run_on_all ../edit-contents README.md &&
test_all_match git add README.md &&
test_all_match git status --porcelain=v2 &&
@@ -144,7 +211,7 @@ test_expect_success 'add, commit, checkout' '
test_all_match git checkout HEAD~1 &&
test_all_match git checkout - &&
- run_on_all "../edit-contents README.md" &&
+ run_on_all ../edit-contents README.md &&
test_all_match git add -A &&
test_all_match git status --porcelain=v2 &&
@@ -153,7 +220,7 @@ test_expect_success 'add, commit, checkout' '
test_all_match git checkout HEAD~1 &&
test_all_match git checkout - &&
- run_on_all "../edit-contents deep/newfile" &&
+ run_on_all ../edit-contents deep/newfile &&
test_all_match git status --porcelain=v2 -uno &&
test_all_match git status --porcelain=v2 &&
@@ -186,7 +253,7 @@ test_expect_success 'diff --staged' '
write_script edit-contents <<-\EOF &&
echo text >>README.md
EOF
- run_on_all "../edit-contents" &&
+ run_on_all ../edit-contents &&
test_all_match git diff &&
test_all_match git diff --staged &&
@@ -252,6 +319,17 @@ test_expect_failure 'checkout and reset (mixed)' '
test_all_match git reset update-folder2
'
+# Ensure that sparse-index behaves identically to
+# sparse-checkout with a full index.
+test_expect_success 'checkout and reset (mixed) [sparse]' '
+ init_repos &&
+
+ test_sparse_match git checkout -b reset-test update-deep &&
+ test_sparse_match git reset deepest &&
+ test_sparse_match git reset update-folder1 &&
+ test_sparse_match git reset update-folder2
+'
+
test_expect_success 'merge' '
init_repos &&
@@ -280,7 +358,7 @@ test_expect_success 'clean' '
echo bogus >>.gitignore &&
run_on_all cp ../.gitignore . &&
test_all_match git add .gitignore &&
- test_all_match git commit -m ignore-bogus-files &&
+ test_all_match git commit -m "ignore bogus files" &&
run_on_sparse mkdir folder1 &&
run_on_all touch folder1/bogus &&
@@ -288,14 +366,51 @@ test_expect_success 'clean' '
test_all_match git status --porcelain=v2 &&
test_all_match git clean -f &&
test_all_match git status --porcelain=v2 &&
+ test_sparse_match ls &&
+ test_sparse_match ls folder1 &&
test_all_match git clean -xf &&
test_all_match git status --porcelain=v2 &&
+ test_sparse_match ls &&
+ test_sparse_match ls folder1 &&
test_all_match git clean -xdf &&
test_all_match git status --porcelain=v2 &&
+ test_sparse_match ls &&
+ test_sparse_match ls folder1 &&
+
+ test_sparse_match test_path_is_dir folder1
+'
+
+test_expect_success 'submodule handling' '
+ init_repos &&
+
+ test_all_match mkdir modules &&
+ test_all_match touch modules/a &&
+ test_all_match git add modules &&
+ test_all_match git commit -m "add modules directory" &&
+
+ run_on_all git submodule add "$(pwd)/initial-repo" modules/sub &&
+ test_all_match git commit -m "add submodule" &&
+
+ # having a submodule prevents "modules" from collapse
+ test-tool -C sparse-index read-cache --table >cache &&
+ grep "100644 blob .* modules/a" cache &&
+ grep "160000 commit $(git -C initial-repo rev-parse HEAD) modules/sub" cache
+'
+
+test_expect_success 'sparse-index is expanded and converted back' '
+ init_repos &&
+
+ GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \
+ git -C sparse-index -c core.fsmonitor="" reset --hard &&
+ test_region index convert_to_sparse trace2.txt &&
+ test_region index ensure_full_index trace2.txt &&
- test_path_is_dir sparse-checkout/folder1
+ rm trace2.txt &&
+ GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \
+ git -C sparse-index -c core.fsmonitor="" status -uno &&
+ test_region index ensure_full_index trace2.txt
'
test_done
diff --git a/t/t1300-config.sh b/t/t1300-config.sh
index e0dd5d6..9ff46f3 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -1374,16 +1374,29 @@ test_expect_success 'git --config-env=key=envvar support' '
cat >expect <<-\EOF &&
value
value
+ value
+ value
+ false
false
EOF
{
ENVVAR=value git --config-env=core.name=ENVVAR config core.name &&
+ ENVVAR=value git --config-env core.name=ENVVAR config core.name &&
ENVVAR=value git --config-env=foo.CamelCase=ENVVAR config foo.camelcase &&
- ENVVAR= git --config-env=foo.flag=ENVVAR config --bool foo.flag
+ ENVVAR=value git --config-env foo.CamelCase=ENVVAR config foo.camelcase &&
+ ENVVAR= git --config-env=foo.flag=ENVVAR config --bool foo.flag &&
+ ENVVAR= git --config-env foo.flag=ENVVAR config --bool foo.flag
} >actual &&
test_cmp expect actual
'
+test_expect_success 'git --config-env with missing value' '
+ test_must_fail env ENVVAR=value git --config-env 2>error &&
+ grep "no config key given for --config-env" error &&
+ test_must_fail env ENVVAR=value git --config-env config core.name 2>error &&
+ grep "invalid config format: config" error
+'
+
test_expect_success 'git --config-env fails with invalid parameters' '
test_must_fail git --config-env=foo.flag config --bool foo.flag 2>error &&
test_i18ngrep "invalid config format: foo.flag" error &&
@@ -2059,6 +2072,91 @@ test_expect_success '--show-scope with --show-origin' '
test_cmp expect output
'
+test_expect_success 'override global and system config' '
+ test_when_finished rm -f "$HOME"/.config/git &&
+
+ cat >"$HOME"/.gitconfig <<-EOF &&
+ [home]
+ config = true
+ EOF
+ mkdir -p "$HOME"/.config/git &&
+ cat >"$HOME"/.config/git/config <<-EOF &&
+ [xdg]
+ config = true
+ EOF
+ cat >.git/config <<-EOF &&
+ [local]
+ config = true
+ EOF
+ cat >custom-global-config <<-EOF &&
+ [global]
+ config = true
+ EOF
+ cat >custom-system-config <<-EOF &&
+ [system]
+ config = true
+ EOF
+
+ cat >expect <<-EOF &&
+ global xdg.config=true
+ global home.config=true
+ local local.config=true
+ EOF
+ git config --show-scope --list >output &&
+ test_cmp expect output &&
+
+ cat >expect <<-EOF &&
+ system system.config=true
+ global global.config=true
+ local local.config=true
+ EOF
+ GIT_CONFIG_NOSYSTEM=false GIT_CONFIG_SYSTEM=custom-system-config GIT_CONFIG_GLOBAL=custom-global-config \
+ git config --show-scope --list >output &&
+ test_cmp expect output &&
+
+ cat >expect <<-EOF &&
+ local local.config=true
+ EOF
+ GIT_CONFIG_NOSYSTEM=false GIT_CONFIG_SYSTEM=/dev/null GIT_CONFIG_GLOBAL=/dev/null \
+ git config --show-scope --list >output &&
+ test_cmp expect output
+'
+
+test_expect_success 'override global and system config with missing file' '
+ test_must_fail env GIT_CONFIG_GLOBAL=does-not-exist GIT_CONFIG_SYSTEM=/dev/null git config --global --list &&
+ test_must_fail env GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_SYSTEM=does-not-exist git config --system --list &&
+ GIT_CONFIG_GLOBAL=does-not-exist GIT_CONFIG_SYSTEM=does-not-exist git version
+'
+
+test_expect_success 'system override has no effect with GIT_CONFIG_NOSYSTEM' '
+ # `git config --system` has different semantics compared to other
+ # commands as it ignores GIT_CONFIG_NOSYSTEM. We thus test whether the
+ # variable has an effect via a different proxy.
+ cat >alias-config <<-EOF &&
+ [alias]
+ hello-world = !echo "hello world"
+ EOF
+ test_must_fail env GIT_CONFIG_NOSYSTEM=true GIT_CONFIG_SYSTEM=alias-config \
+ git hello-world &&
+ GIT_CONFIG_NOSYSTEM=false GIT_CONFIG_SYSTEM=alias-config \
+ git hello-world >actual &&
+ echo "hello world" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'write to overridden global and system config' '
+ cat >expect <<EOF &&
+[config]
+ key = value
+EOF
+
+ GIT_CONFIG_GLOBAL=write-to-global git config --global config.key value &&
+ test_cmp expect write-to-global &&
+
+ GIT_CONFIG_SYSTEM=write-to-system git config --system config.key value &&
+ test_cmp expect write-to-system
+'
+
for opt in --local --worktree
do
test_expect_success "$opt requires a repo" '
diff --git a/t/t1306-xdg-files.sh b/t/t1306-xdg-files.sh
index dd87b43..40d3c42 100755
--- a/t/t1306-xdg-files.sh
+++ b/t/t1306-xdg-files.sh
@@ -116,7 +116,7 @@ test_expect_success 'Exclusion in a non-XDG global ignore file' '
test_expect_success 'Checking XDG ignore file when HOME is unset' '
(sane_unset HOME &&
git config --unset core.excludesfile &&
- git ls-files --exclude-standard --ignored >actual) &&
+ git ls-files --exclude-standard --ignored --others >actual) &&
test_must_be_empty actual
'
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index e31f65f..4506cd4 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -1598,4 +1598,13 @@ test_expect_success 'transaction cannot restart ongoing transaction' '
test_must_fail git show-ref --verify refs/heads/restart
'
+test_expect_success 'directory not created deleting packed ref' '
+ git branch d1/d2/r1 HEAD &&
+ git pack-refs --all &&
+ test_path_is_missing .git/refs/heads/d1/d2 &&
+ git update-ref -d refs/heads/d1/d2/r1 &&
+ test_path_is_missing .git/refs/heads/d1/d2 &&
+ test_path_is_missing .git/refs/heads/d1
+'
+
test_done
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
index deae916..1c2df08 100755
--- a/t/t1500-rev-parse.sh
+++ b/t/t1500-rev-parse.sh
@@ -146,6 +146,10 @@ test_expect_success '--path-format can change in the middle of the command line'
test_cmp expect actual
'
+test_expect_success '--path-format does not segfault without an argument' '
+ test_must_fail git rev-parse --path-format
+'
+
test_expect_success 'git-common-dir from worktree root' '
echo .git >expect &&
git rev-parse --git-common-dir >actual &&
diff --git a/t/t2080-parallel-checkout-basics.sh b/t/t2080-parallel-checkout-basics.sh
new file mode 100755
index 0000000..3e0f8c6
--- /dev/null
+++ b/t/t2080-parallel-checkout-basics.sh
@@ -0,0 +1,229 @@
+#!/bin/sh
+
+test_description='parallel-checkout basics
+
+Ensure that parallel-checkout basically works on clone and checkout, spawning
+the required number of workers and correctly populating both the index and the
+working tree.
+'
+
+TEST_NO_CREATE_REPO=1
+. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-parallel-checkout.sh"
+
+# Test parallel-checkout with a branch switch containing a variety of file
+# creations, deletions, and modifications, involving different entry types.
+# The branches B1 and B2 have the following paths:
+#
+# B1 B2
+# a/a (file) a (file)
+# b (file) b/b (file)
+#
+# c/c (file) c (symlink)
+# d (symlink) d/d (file)
+#
+# e/e (file) e (submodule)
+# f (submodule) f/f (file)
+#
+# g (submodule) g (symlink)
+# h (symlink) h (submodule)
+#
+# Additionally, the following paths are present on both branches, but with
+# different contents:
+#
+# i (file) i (file)
+# j (symlink) j (symlink)
+# k (submodule) k (submodule)
+#
+# And the following paths are only present in one of the branches:
+#
+# l/l (file) -
+# - m/m (file)
+#
+test_expect_success 'setup repo for checkout with various types of changes' '
+ git init sub &&
+ (
+ cd sub &&
+ git checkout -b B2 &&
+ echo B2 >file &&
+ git add file &&
+ git commit -m file &&
+
+ git checkout -b B1 &&
+ echo B1 >file &&
+ git add file &&
+ git commit -m file
+ ) &&
+
+ git init various &&
+ (
+ cd various &&
+
+ git checkout -b B1 &&
+ mkdir a c e &&
+ echo a/a >a/a &&
+ echo b >b &&
+ echo c/c >c/c &&
+ test_ln_s_add c d &&
+ echo e/e >e/e &&
+ git submodule add ../sub f &&
+ git submodule add ../sub g &&
+ test_ln_s_add c h &&
+
+ echo "B1 i" >i &&
+ test_ln_s_add c j &&
+ git submodule add -b B1 ../sub k &&
+ mkdir l &&
+ echo l/l >l/l &&
+
+ git add . &&
+ git commit -m B1 &&
+
+ git checkout -b B2 &&
+ git rm -rf :^.gitmodules :^k &&
+ mkdir b d f &&
+ echo a >a &&
+ echo b/b >b/b &&
+ test_ln_s_add b c &&
+ echo d/d >d/d &&
+ git submodule add ../sub e &&
+ echo f/f >f/f &&
+ test_ln_s_add b g &&
+ git submodule add ../sub h &&
+
+ echo "B2 i" >i &&
+ test_ln_s_add b j &&
+ git -C k checkout B2 &&
+ mkdir m &&
+ echo m/m >m/m &&
+
+ git add . &&
+ git commit -m B2 &&
+
+ git checkout --recurse-submodules B1
+ )
+'
+
+for mode in sequential parallel sequential-fallback
+do
+ case $mode in
+ sequential) workers=1 threshold=0 expected_workers=0 ;;
+ parallel) workers=2 threshold=0 expected_workers=2 ;;
+ sequential-fallback) workers=2 threshold=100 expected_workers=0 ;;
+ esac
+
+ test_expect_success "$mode checkout" '
+ repo=various_$mode &&
+ cp -R -P various $repo &&
+
+ # The just copied files have more recent timestamps than their
+ # associated index entries. So refresh the cached timestamps
+ # to avoid an "entry not up-to-date" error from `git checkout`.
+ # We only have to do this for the submodules as `git checkout`
+ # will already refresh the superproject index before performing
+ # the up-to-date check.
+ #
+ git -C $repo submodule foreach "git update-index --refresh" &&
+
+ set_checkout_config $workers $threshold &&
+ test_checkout_workers $expected_workers \
+ git -C $repo checkout --recurse-submodules B2 &&
+ verify_checkout $repo
+ '
+done
+
+for mode in parallel sequential-fallback
+do
+ case $mode in
+ parallel) workers=2 threshold=0 expected_workers=2 ;;
+ sequential-fallback) workers=2 threshold=100 expected_workers=0 ;;
+ esac
+
+ test_expect_success "$mode checkout on clone" '
+ repo=various_${mode}_clone &&
+ set_checkout_config $workers $threshold &&
+ test_checkout_workers $expected_workers \
+ git clone --recurse-submodules --branch B2 various $repo &&
+ verify_checkout $repo
+ '
+done
+
+# Just to be paranoid, actually compare the working trees' contents directly.
+test_expect_success 'compare the working trees' '
+ rm -rf various_*/.git &&
+ rm -rf various_*/*/.git &&
+
+ # We use `git diff` instead of `diff -r` because the latter would
+ # follow symlinks, and not all `diff` implementations support the
+ # `--no-dereference` option.
+ #
+ git diff --no-index various_sequential various_parallel &&
+ git diff --no-index various_sequential various_parallel_clone &&
+ git diff --no-index various_sequential various_sequential-fallback &&
+ git diff --no-index various_sequential various_sequential-fallback_clone
+'
+
+# Currently, each submodule is checked out in a separated child process, but
+# these subprocesses must also be able to use parallel checkout workers to
+# write the submodules' entries.
+test_expect_success 'submodules can use parallel checkout' '
+ set_checkout_config 2 0 &&
+ git init super &&
+ (
+ cd super &&
+ git init sub &&
+ test_commit -C sub A &&
+ test_commit -C sub B &&
+ git submodule add ./sub &&
+ git commit -m sub &&
+ rm sub/* &&
+ test_checkout_workers 2 git checkout --recurse-submodules .
+ )
+'
+
+test_expect_success 'parallel checkout respects --[no]-force' '
+ set_checkout_config 2 0 &&
+ git init dirty &&
+ (
+ cd dirty &&
+ mkdir D &&
+ test_commit D/F &&
+ test_commit F &&
+
+ rm -rf D &&
+ echo changed >D &&
+ echo changed >F.t &&
+
+ # We expect 0 workers because there is nothing to be done
+ test_checkout_workers 0 git checkout HEAD &&
+ test_path_is_file D &&
+ grep changed D &&
+ grep changed F.t &&
+
+ test_checkout_workers 2 git checkout --force HEAD &&
+ test_path_is_dir D &&
+ grep D/F D/F.t &&
+ grep F F.t
+ )
+'
+
+test_expect_success SYMLINKS 'parallel checkout checks for symlinks in leading dirs' '
+ set_checkout_config 2 0 &&
+ git init symlinks &&
+ (
+ cd symlinks &&
+ mkdir D untracked &&
+ # Commit 2 files to have enough work for 2 parallel workers
+ test_commit D/A &&
+ test_commit D/B &&
+ rm -rf D &&
+ ln -s untracked D &&
+
+ test_checkout_workers 2 git checkout --force HEAD &&
+ ! test -h D &&
+ grep D/A D/A.t &&
+ grep D/B D/B.t
+ )
+'
+
+test_done
diff --git a/t/t2081-parallel-checkout-collisions.sh b/t/t2081-parallel-checkout-collisions.sh
new file mode 100755
index 0000000..f6fcfc0
--- /dev/null
+++ b/t/t2081-parallel-checkout-collisions.sh
@@ -0,0 +1,162 @@
+#!/bin/sh
+
+test_description="path collisions during parallel checkout
+
+Parallel checkout must detect path collisions to:
+
+1) Avoid racily writing to different paths that represent the same file on disk.
+2) Report the colliding entries on clone.
+
+The tests in this file exercise parallel checkout's collision detection code in
+both these mechanics.
+"
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-parallel-checkout.sh"
+
+TEST_ROOT="$PWD"
+
+test_expect_success CASE_INSENSITIVE_FS 'setup' '
+ empty_oid=$(git hash-object -w --stdin </dev/null) &&
+ cat >objs <<-EOF &&
+ 100644 $empty_oid FILE_X
+ 100644 $empty_oid FILE_x
+ 100644 $empty_oid file_X
+ 100644 $empty_oid file_x
+ EOF
+ git update-index --index-info <objs &&
+ git commit -m "colliding files" &&
+ git tag basename_collision &&
+
+ write_script "$TEST_ROOT"/logger_script <<-\EOF
+ echo "$@" >>filter.log
+ EOF
+'
+
+test_workers_in_event_trace ()
+{
+ test $1 -eq $(grep ".event.:.child_start..*checkout--worker" $2 | wc -l)
+}
+
+test_expect_success CASE_INSENSITIVE_FS 'worker detects basename collision' '
+ GIT_TRACE2_EVENT="$(pwd)/trace" git \
+ -c checkout.workers=2 -c checkout.thresholdForParallelism=0 \
+ checkout . &&
+
+ test_workers_in_event_trace 2 trace &&
+ collisions=$(grep -i "category.:.pcheckout.,.key.:.collision/basename.,.value.:.file_x.}" trace | wc -l) &&
+ test $collisions -eq 3
+'
+
+test_expect_success CASE_INSENSITIVE_FS 'worker detects dirname collision' '
+ test_config filter.logger.smudge "\"$TEST_ROOT/logger_script\" %f" &&
+ empty_oid=$(git hash-object -w --stdin </dev/null) &&
+
+ # By setting a filter command to "a", we make it ineligible for parallel
+ # checkout, and thus it is checked out *first*. This way we can ensure
+ # that "A/B" and "A/C" will both collide with the regular file "a".
+ #
+ attr_oid=$(echo "a filter=logger" | git hash-object -w --stdin) &&
+
+ cat >objs <<-EOF &&
+ 100644 $empty_oid A/B
+ 100644 $empty_oid A/C
+ 100644 $empty_oid a
+ 100644 $attr_oid .gitattributes
+ EOF
+ git rm -rf . &&
+ git update-index --index-info <objs &&
+
+ rm -f trace filter.log &&
+ GIT_TRACE2_EVENT="$(pwd)/trace" git \
+ -c checkout.workers=2 -c checkout.thresholdForParallelism=0 \
+ checkout . &&
+
+ # Check that "a" (and only "a") was filtered
+ echo a >expected.log &&
+ test_cmp filter.log expected.log &&
+
+ # Check that it used the right number of workers and detected the collisions
+ test_workers_in_event_trace 2 trace &&
+ grep "category.:.pcheckout.,.key.:.collision/dirname.,.value.:.A/B.}" trace &&
+ grep "category.:.pcheckout.,.key.:.collision/dirname.,.value.:.A/C.}" trace
+'
+
+test_expect_success SYMLINKS,CASE_INSENSITIVE_FS 'do not follow symlinks colliding with leading dir' '
+ empty_oid=$(git hash-object -w --stdin </dev/null) &&
+ symlink_oid=$(echo "./e" | git hash-object -w --stdin) &&
+ mkdir e &&
+
+ cat >objs <<-EOF &&
+ 120000 $symlink_oid D
+ 100644 $empty_oid d/x
+ 100644 $empty_oid e/y
+ EOF
+ git rm -rf . &&
+ git update-index --index-info <objs &&
+
+ set_checkout_config 2 0 &&
+ test_checkout_workers 2 git checkout . &&
+ test_path_is_dir e &&
+ test_path_is_missing e/x
+'
+
+# The two following tests check that parallel checkout correctly reports
+# colliding entries on clone. The sequential code detects a collision by
+# calling lstat() before trying to open(O_CREAT) a file. (Note that this only
+# works for clone.) Then, to find the pair of a colliding item k, it searches
+# cache_entry[0, k-1]. This is not sufficient in parallel checkout because:
+#
+# - A colliding file may be created between the lstat() and open() calls;
+# - A colliding entry might appear in the second half of the cache_entry array.
+#
+test_expect_success CASE_INSENSITIVE_FS 'collision report on clone (w/ racy file creation)' '
+ git reset --hard basename_collision &&
+ set_checkout_config 2 0 &&
+ test_checkout_workers 2 git clone . clone-repo 2>stderr &&
+
+ grep FILE_X stderr &&
+ grep FILE_x stderr &&
+ grep file_X stderr &&
+ grep file_x stderr &&
+ grep "the following paths have collided" stderr
+'
+
+# This test ensures that the collision report code is correctly looking for
+# colliding peers in the second half of the cache_entry array. This is done by
+# defining a smudge command for the *last* array entry, which makes it
+# non-eligible for parallel-checkout. Thus, it is checked out *first*, before
+# spawning the workers.
+#
+# Note: this test doesn't work on Windows because, on this system, the
+# collision report code uses strcmp() to find the colliding pairs when
+# core.ignoreCase is false. And we need this setting for this test so that only
+# 'file_x' matches the pattern of the filter attribute. But the test works on
+# OSX, where the colliding pairs are found using inode.
+#
+test_expect_success CASE_INSENSITIVE_FS,!MINGW,!CYGWIN \
+ 'collision report on clone (w/ colliding peer after the detected entry)' '
+
+ test_config_global filter.logger.smudge "\"$TEST_ROOT/logger_script\" %f" &&
+ git reset --hard basename_collision &&
+ echo "file_x filter=logger" >.gitattributes &&
+ git add .gitattributes &&
+ git commit -m "filter for file_x" &&
+
+ rm -rf clone-repo &&
+ set_checkout_config 2 0 &&
+ test_checkout_workers 2 \
+ git -c core.ignoreCase=false clone . clone-repo 2>stderr &&
+
+ grep FILE_X stderr &&
+ grep FILE_x stderr &&
+ grep file_X stderr &&
+ grep file_x stderr &&
+ grep "the following paths have collided" stderr &&
+
+ # Check that only "file_x" was filtered
+ echo file_x >expected.log &&
+ test_cmp clone-repo/filter.log expected.log
+'
+
+test_done
diff --git a/t/t2082-parallel-checkout-attributes.sh b/t/t2082-parallel-checkout-attributes.sh
new file mode 100755
index 0000000..2525457
--- /dev/null
+++ b/t/t2082-parallel-checkout-attributes.sh
@@ -0,0 +1,194 @@
+#!/bin/sh
+
+test_description='parallel-checkout: attributes
+
+Verify that parallel-checkout correctly creates files that require
+conversions, as specified in .gitattributes. The main point here is
+to check that the conv_attr data is correctly sent to the workers
+and that it contains sufficient information to smudge files
+properly (without access to the index or attribute stack).
+'
+
+TEST_NO_CREATE_REPO=1
+. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-parallel-checkout.sh"
+. "$TEST_DIRECTORY/lib-encoding.sh"
+
+test_expect_success 'parallel-checkout with ident' '
+ set_checkout_config 2 0 &&
+ git init ident &&
+ (
+ cd ident &&
+ echo "A ident" >.gitattributes &&
+ echo "\$Id\$" >A &&
+ echo "\$Id\$" >B &&
+ git add -A &&
+ git commit -m id &&
+
+ rm A B &&
+ test_checkout_workers 2 git reset --hard &&
+ hexsz=$(test_oid hexsz) &&
+ grep -E "\\\$Id: [0-9a-f]{$hexsz} \\\$" A &&
+ grep "\\\$Id\\\$" B
+ )
+'
+
+test_expect_success 'parallel-checkout with re-encoding' '
+ set_checkout_config 2 0 &&
+ git init encoding &&
+ (
+ cd encoding &&
+ echo text >utf8-text &&
+ write_utf16 <utf8-text >utf16-text &&
+
+ echo "A working-tree-encoding=UTF-16" >.gitattributes &&
+ cp utf16-text A &&
+ cp utf8-text B &&
+ git add A B .gitattributes &&
+ git commit -m encoding &&
+
+ # Check that A is stored in UTF-8
+ git cat-file -p :A >A.internal &&
+ test_cmp_bin utf8-text A.internal &&
+
+ rm A B &&
+ test_checkout_workers 2 git checkout A B &&
+
+ # Check that A (and only A) is re-encoded during checkout
+ test_cmp_bin utf16-text A &&
+ test_cmp_bin utf8-text B
+ )
+'
+
+test_expect_success 'parallel-checkout with eol conversions' '
+ set_checkout_config 2 0 &&
+ git init eol &&
+ (
+ cd eol &&
+ printf "multi\r\nline\r\ntext" >crlf-text &&
+ printf "multi\nline\ntext" >lf-text &&
+
+ git config core.autocrlf false &&
+ echo "A eol=crlf" >.gitattributes &&
+ cp crlf-text A &&
+ cp lf-text B &&
+ git add A B .gitattributes &&
+ git commit -m eol &&
+
+ # Check that A is stored with LF format
+ git cat-file -p :A >A.internal &&
+ test_cmp_bin lf-text A.internal &&
+
+ rm A B &&
+ test_checkout_workers 2 git checkout A B &&
+
+ # Check that A (and only A) is converted to CRLF during checkout
+ test_cmp_bin crlf-text A &&
+ test_cmp_bin lf-text B
+ )
+'
+
+# Entries that require an external filter are not eligible for parallel
+# checkout. Check that both the parallel-eligible and non-eligible entries are
+# properly writen in a single checkout operation.
+#
+test_expect_success 'parallel-checkout and external filter' '
+ set_checkout_config 2 0 &&
+ git init filter &&
+ (
+ cd filter &&
+ write_script <<-\EOF rot13.sh &&
+ tr \
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" \
+ "nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM"
+ EOF
+
+ git config filter.rot13.clean "\"$(pwd)/rot13.sh\"" &&
+ git config filter.rot13.smudge "\"$(pwd)/rot13.sh\"" &&
+ git config filter.rot13.required true &&
+
+ echo abcd >original &&
+ echo nopq >rot13 &&
+
+ echo "A filter=rot13" >.gitattributes &&
+ cp original A &&
+ cp original B &&
+ cp original C &&
+ git add A B C .gitattributes &&
+ git commit -m filter &&
+
+ # Check that A (and only A) was cleaned
+ git cat-file -p :A >A.internal &&
+ test_cmp rot13 A.internal &&
+ git cat-file -p :B >B.internal &&
+ test_cmp original B.internal &&
+ git cat-file -p :C >C.internal &&
+ test_cmp original C.internal &&
+
+ rm A B C *.internal &&
+ test_checkout_workers 2 git checkout A B C &&
+
+ # Check that A (and only A) was smudged during checkout
+ test_cmp original A &&
+ test_cmp original B &&
+ test_cmp original C
+ )
+'
+
+# The delayed queue is independent from the parallel queue, and they should be
+# able to work together in the same checkout process.
+#
+test_expect_success PERL 'parallel-checkout and delayed checkout' '
+ write_script rot13-filter.pl "$PERL_PATH" \
+ <"$TEST_DIRECTORY"/t0021/rot13-filter.pl &&
+
+ test_config_global filter.delay.process \
+ "\"$(pwd)/rot13-filter.pl\" --always-delay \"$(pwd)/delayed.log\" clean smudge delay" &&
+ test_config_global filter.delay.required true &&
+
+ echo "abcd" >original &&
+ echo "nopq" >rot13 &&
+
+ git init delayed &&
+ (
+ cd delayed &&
+ echo "*.d filter=delay" >.gitattributes &&
+ cp ../original W.d &&
+ cp ../original X.d &&
+ cp ../original Y &&
+ cp ../original Z &&
+ git add -A &&
+ git commit -m delayed &&
+
+ # Check that *.d files were cleaned
+ git cat-file -p :W.d >W.d.internal &&
+ test_cmp W.d.internal ../rot13 &&
+ git cat-file -p :X.d >X.d.internal &&
+ test_cmp X.d.internal ../rot13 &&
+ git cat-file -p :Y >Y.internal &&
+ test_cmp Y.internal ../original &&
+ git cat-file -p :Z >Z.internal &&
+ test_cmp Z.internal ../original &&
+
+ rm *
+ ) &&
+
+ set_checkout_config 2 0 &&
+ test_checkout_workers 2 git -C delayed checkout -f &&
+ verify_checkout delayed &&
+
+ # Check that the *.d files got to the delay queue and were filtered
+ grep "smudge W.d .* \[DELAYED\]" delayed.log &&
+ grep "smudge X.d .* \[DELAYED\]" delayed.log &&
+ test_cmp delayed/W.d original &&
+ test_cmp delayed/X.d original &&
+
+ # Check that the parallel-eligible entries went to the right queue and
+ # were not filtered
+ ! grep "smudge Y .* \[DELAYED\]" delayed.log &&
+ ! grep "smudge Z .* \[DELAYED\]" delayed.log &&
+ test_cmp delayed/Y original &&
+ test_cmp delayed/Z original
+'
+
+test_done
diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh
index 1ec7cb5..516c95e 100755
--- a/t/t3001-ls-files-others-exclude.sh
+++ b/t/t3001-ls-files-others-exclude.sh
@@ -292,6 +292,11 @@ EOF
test_cmp expect actual
'
+test_expect_success 'ls-files with "**" patterns and --directory' '
+ # Expectation same as previous test
+ git ls-files --directory -o -i --exclude "**/a.1" >actual &&
+ test_cmp expect actual
+'
test_expect_success 'ls-files with "**" patterns and no slashes' '
git ls-files -o -i --exclude "one**a.1" >actual &&
diff --git a/t/t3003-ls-files-exclude.sh b/t/t3003-ls-files-exclude.sh
index d5ec333..c41c4f0 100755
--- a/t/t3003-ls-files-exclude.sh
+++ b/t/t3003-ls-files-exclude.sh
@@ -29,11 +29,11 @@ test_expect_success 'add file to gitignore' '
'
check_all_output
-test_expect_success 'ls-files -i lists only tracked-but-ignored files' '
+test_expect_success 'ls-files -i -c lists only tracked-but-ignored files' '
echo content >other-file &&
git add other-file &&
echo file >expect &&
- git ls-files -i --exclude-standard >output &&
+ git ls-files -i -c --exclude-standard >output &&
test_cmp expect output
'
diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh
index 891d4d7..56ea4bd 100755
--- a/t/t3070-wildmatch.sh
+++ b/t/t3070-wildmatch.sh
@@ -4,6 +4,11 @@ test_description='wildmatch tests'
. ./test-lib.sh
+# Disable expensive chain-lint tests; all of the tests in this script
+# are variants of a few trivial test-tool invocations, and there are a lot of
+# them.
+GIT_TEST_CHAIN_LINT_HARDER_DEFAULT=0
+
should_create_test_file() {
file=$1
diff --git a/t/t3418-rebase-continue.sh b/t/t3418-rebase-continue.sh
index 0838f4e..f4c2ee0 100755
--- a/t/t3418-rebase-continue.sh
+++ b/t/t3418-rebase-continue.sh
@@ -282,12 +282,35 @@ test_expect_success '--reschedule-failed-exec' '
test_i18ngrep "has been rescheduled" err
'
-test_expect_success 'rebase.reschedulefailedexec only affects `rebase -i`' '
- test_config rebase.reschedulefailedexec true &&
+test_expect_success 'rebase.rescheduleFailedExec only affects `rebase -i`' '
+ test_config rebase.rescheduleFailedExec true &&
test_must_fail git rebase -x false HEAD^ &&
grep "^exec false" .git/rebase-merge/git-rebase-todo &&
git rebase --abort &&
git rebase HEAD^
'
+test_expect_success 'rebase.rescheduleFailedExec=true & --no-reschedule-failed-exec' '
+ test_when_finished "git rebase --abort" &&
+ test_config rebase.rescheduleFailedExec true &&
+ test_must_fail git rebase -x false --no-reschedule-failed-exec HEAD~2 &&
+ test_must_fail git rebase --continue 2>err &&
+ ! grep "has been rescheduled" err
+'
+
+test_expect_success 'new rebase.rescheduleFailedExec=true setting in an ongoing rebase is ignored' '
+ test_when_finished "git rebase --abort" &&
+ test_must_fail git rebase -x false HEAD~2 &&
+ test_config rebase.rescheduleFailedExec true &&
+ test_must_fail git rebase --continue 2>err &&
+ ! grep "has been rescheduled" err
+'
+
+test_expect_success 'there is no --no-reschedule-failed-exec in an ongoing rebase' '
+ test_when_finished "git rebase --abort" &&
+ test_must_fail git rebase -x false HEAD~2 &&
+ test_expect_code 129 git rebase --continue --no-reschedule-failed-exec &&
+ test_expect_code 129 git rebase --edit-todo --no-reschedule-failed-exec
+'
+
test_done
diff --git a/t/t3437-rebase-fixup-options.sh b/t/t3437-rebase-fixup-options.sh
index d0bdc7e..c023fef 100755
--- a/t/t3437-rebase-fixup-options.sh
+++ b/t/t3437-rebase-fixup-options.sh
@@ -157,7 +157,7 @@ test_expect_success 'sequence of fixup, fixup -C & squash --signoff works' '
git -c commit.status=false rebase -ik --signoff A &&
git diff-tree --exit-code --patch HEAD B3 -- &&
test_cmp_rev HEAD^ A &&
- test_i18ncmp "$TEST_DIRECTORY/t3437/expected-squash-message" \
+ test_cmp "$TEST_DIRECTORY/t3437/expected-squash-message" \
actual-squash-message
'
@@ -191,7 +191,7 @@ test_expect_success 'sequence squash, fixup & fixup -c gives combined message' '
FAKE_LINES="1 squash 2 fixup 3 fixup_-c 4" \
FAKE_MESSAGE_COPY=actual-combined-message \
git -c commit.status=false rebase -i A &&
- test_i18ncmp "$TEST_DIRECTORY/t3437/expected-combined-message" \
+ test_cmp "$TEST_DIRECTORY/t3437/expected-combined-message" \
actual-combined-message &&
test_cmp_rev HEAD^ A
'
@@ -204,7 +204,7 @@ test_expect_success 'fixup -C works upon --autosquash with amend!' '
--signoff A &&
git diff-tree --exit-code --patch HEAD B3 -- &&
test_cmp_rev HEAD^ A &&
- test_i18ncmp "$TEST_DIRECTORY/t3437/expected-squash-message" \
+ test_cmp "$TEST_DIRECTORY/t3437/expected-squash-message" \
actual-squash-message
'
diff --git a/t/t3512-cherry-pick-submodule.sh b/t/t3512-cherry-pick-submodule.sh
index 822f2d4..c657840 100755
--- a/t/t3512-cherry-pick-submodule.sh
+++ b/t/t3512-cherry-pick-submodule.sh
@@ -8,8 +8,11 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-submodule-update.sh
-KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
-KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES=1
+if test "$GIT_TEST_MERGE_ALGORITHM" != ort
+then
+ KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
+ KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES=1
+fi
test_submodule_switch "cherry-pick"
test_expect_success 'unrelated submodule/file conflict is ignored' '
diff --git a/t/t3513-revert-submodule.sh b/t/t3513-revert-submodule.sh
index a759f12..8bfe3ed 100755
--- a/t/t3513-revert-submodule.sh
+++ b/t/t3513-revert-submodule.sh
@@ -14,7 +14,7 @@ test_description='revert can handle submodules'
git_revert () {
git status -su >expect &&
ls -1pR * >>expect &&
- tar cf "$TRASH_DIRECTORY/tmp.tar" * &&
+ "$TAR" cf "$TRASH_DIRECTORY/tmp.tar" * &&
may_only_be_test_must_fail "$2" &&
$2 git checkout "$1" &&
if test -n "$2"
@@ -23,14 +23,17 @@ git_revert () {
fi &&
git revert HEAD &&
rm -rf * &&
- tar xf "$TRASH_DIRECTORY/tmp.tar" &&
+ "$TAR" xf "$TRASH_DIRECTORY/tmp.tar" &&
git status -su >actual &&
ls -1pR * >>actual &&
test_cmp expect actual &&
git revert HEAD
}
-KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
+if test "$GIT_TEST_MERGE_ALGORITHM" != ort
+then
+ KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
+fi
test_submodule_switch_func "git_revert"
test_done
diff --git a/t/t3602-rm-sparse-checkout.sh b/t/t3602-rm-sparse-checkout.sh
new file mode 100755
index 0000000..e9e9a15
--- /dev/null
+++ b/t/t3602-rm-sparse-checkout.sh
@@ -0,0 +1,78 @@
+#!/bin/sh
+
+test_description='git rm in sparse checked out working trees'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' "
+ mkdir -p sub/dir &&
+ touch a b c sub/d sub/dir/e &&
+ git add -A &&
+ git commit -m files &&
+
+ cat >sparse_error_header <<-EOF &&
+ The following pathspecs didn't match any eligible path, but they do match index
+ entries outside the current sparse checkout:
+ EOF
+
+ cat >sparse_hint <<-EOF &&
+ hint: Disable or modify the sparsity rules if you intend to update such entries.
+ hint: Disable this message with \"git config advice.updateSparsePath false\"
+ EOF
+
+ echo b | cat sparse_error_header - >sparse_entry_b_error &&
+ cat sparse_entry_b_error sparse_hint >b_error_and_hint
+"
+
+for opt in "" -f --dry-run
+do
+ test_expect_success "rm${opt:+ $opt} does not remove sparse entries" '
+ git sparse-checkout set a &&
+ test_must_fail git rm $opt b 2>stderr &&
+ test_cmp b_error_and_hint stderr &&
+ git ls-files --error-unmatch b
+ '
+done
+
+test_expect_success 'recursive rm does not remove sparse entries' '
+ git reset --hard &&
+ git sparse-checkout set sub/dir &&
+ git rm -r sub &&
+ git status --porcelain -uno >actual &&
+ echo "D sub/dir/e" >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'rm obeys advice.updateSparsePath' '
+ git reset --hard &&
+ git sparse-checkout set a &&
+ test_must_fail git -c advice.updateSparsePath=false rm b 2>stderr &&
+ test_cmp sparse_entry_b_error stderr
+'
+
+test_expect_success 'do not advice about sparse entries when they do not match the pathspec' '
+ git reset --hard &&
+ git sparse-checkout set a &&
+ test_must_fail git rm nonexistent 2>stderr &&
+ grep "fatal: pathspec .nonexistent. did not match any files" stderr &&
+ ! grep -F -f sparse_error_header stderr
+'
+
+test_expect_success 'do not warn about sparse entries when pathspec matches dense entries' '
+ git reset --hard &&
+ git sparse-checkout set a &&
+ git rm "[ba]" 2>stderr &&
+ test_must_be_empty stderr &&
+ git ls-files --error-unmatch b &&
+ test_must_fail git ls-files --error-unmatch a
+'
+
+test_expect_success 'do not warn about sparse entries with --ignore-unmatch' '
+ git reset --hard &&
+ git sparse-checkout set a &&
+ git rm --ignore-unmatch b 2>stderr &&
+ test_must_be_empty stderr &&
+ git ls-files --error-unmatch b
+'
+
+test_done
diff --git a/t/t3700-add.sh b/t/t3700-add.sh
index b3b122f..4086e1e 100755
--- a/t/t3700-add.sh
+++ b/t/t3700-add.sh
@@ -196,6 +196,12 @@ test_expect_success 'git add --refresh with pathspec' '
grep baz actual
'
+test_expect_success 'git add --refresh correctly reports no match error' "
+ echo \"fatal: pathspec ':(icase)nonexistent' did not match any files\" >expect &&
+ test_must_fail git add --refresh ':(icase)nonexistent' 2>actual &&
+ test_cmp expect actual
+"
+
test_expect_success POSIXPERM,SANITY 'git add should fail atomically upon an unreadable file' '
git reset --hard &&
date >foo1 &&
@@ -343,6 +349,10 @@ test_expect_success 'git add --dry-run --ignore-missing of non-existing file out
test_cmp expect.err actual.err
'
+test_expect_success 'git add --dry-run --interactive should fail' '
+ test_must_fail git add --dry-run --interactive
+'
+
test_expect_success 'git add empty string should fail' '
test_must_fail git add ""
'
diff --git a/t/t3705-add-sparse-checkout.sh b/t/t3705-add-sparse-checkout.sh
new file mode 100755
index 0000000..2b1fd0d
--- /dev/null
+++ b/t/t3705-add-sparse-checkout.sh
@@ -0,0 +1,155 @@
+#!/bin/sh
+
+test_description='git add in sparse checked out working trees'
+
+. ./test-lib.sh
+
+SPARSE_ENTRY_BLOB=""
+
+# Optionally take a printf format string to write to the sparse_entry file
+setup_sparse_entry () {
+ # 'sparse_entry' might already be in the index with the skip-worktree
+ # bit set. Remove it so that the subsequent git add can update it.
+ git update-index --force-remove sparse_entry &&
+ if test $# -eq 1
+ then
+ printf "$1" >sparse_entry
+ else
+ >sparse_entry
+ fi &&
+ git add sparse_entry &&
+ git update-index --skip-worktree sparse_entry &&
+ SPARSE_ENTRY_BLOB=$(git rev-parse :sparse_entry)
+}
+
+test_sparse_entry_unchanged () {
+ echo "100644 $SPARSE_ENTRY_BLOB 0 sparse_entry" >expected &&
+ git ls-files --stage sparse_entry >actual &&
+ test_cmp expected actual
+}
+
+setup_gitignore () {
+ test_when_finished rm -f .gitignore &&
+ cat >.gitignore <<-EOF
+ *
+ !/sparse_entry
+ EOF
+}
+
+test_expect_success 'setup' "
+ cat >sparse_error_header <<-EOF &&
+ The following pathspecs didn't match any eligible path, but they do match index
+ entries outside the current sparse checkout:
+ EOF
+
+ cat >sparse_hint <<-EOF &&
+ hint: Disable or modify the sparsity rules if you intend to update such entries.
+ hint: Disable this message with \"git config advice.updateSparsePath false\"
+ EOF
+
+ echo sparse_entry | cat sparse_error_header - >sparse_entry_error &&
+ cat sparse_entry_error sparse_hint >error_and_hint
+"
+
+test_expect_success 'git add does not remove sparse entries' '
+ setup_sparse_entry &&
+ rm sparse_entry &&
+ test_must_fail git add sparse_entry 2>stderr &&
+ test_cmp error_and_hint stderr &&
+ test_sparse_entry_unchanged
+'
+
+test_expect_success 'git add -A does not remove sparse entries' '
+ setup_sparse_entry &&
+ rm sparse_entry &&
+ setup_gitignore &&
+ git add -A 2>stderr &&
+ test_must_be_empty stderr &&
+ test_sparse_entry_unchanged
+'
+
+test_expect_success 'git add . does not remove sparse entries' '
+ setup_sparse_entry &&
+ rm sparse_entry &&
+ setup_gitignore &&
+ test_must_fail git add . 2>stderr &&
+
+ cat sparse_error_header >expect &&
+ echo . >>expect &&
+ cat sparse_hint >>expect &&
+
+ test_cmp expect stderr &&
+ test_sparse_entry_unchanged
+'
+
+for opt in "" -f -u --ignore-removal --dry-run
+do
+ test_expect_success "git add${opt:+ $opt} does not update sparse entries" '
+ setup_sparse_entry &&
+ echo modified >sparse_entry &&
+ test_must_fail git add $opt sparse_entry 2>stderr &&
+ test_cmp error_and_hint stderr &&
+ test_sparse_entry_unchanged
+ '
+done
+
+test_expect_success 'git add --refresh does not update sparse entries' '
+ setup_sparse_entry &&
+ git ls-files --debug sparse_entry | grep mtime >before &&
+ test-tool chmtime -60 sparse_entry &&
+ test_must_fail git add --refresh sparse_entry 2>stderr &&
+ test_cmp error_and_hint stderr &&
+ git ls-files --debug sparse_entry | grep mtime >after &&
+ test_cmp before after
+'
+
+test_expect_success 'git add --chmod does not update sparse entries' '
+ setup_sparse_entry &&
+ test_must_fail git add --chmod=+x sparse_entry 2>stderr &&
+ test_cmp error_and_hint stderr &&
+ test_sparse_entry_unchanged &&
+ ! test -x sparse_entry
+'
+
+test_expect_success 'git add --renormalize does not update sparse entries' '
+ test_config core.autocrlf false &&
+ setup_sparse_entry "LINEONE\r\nLINETWO\r\n" &&
+ echo "sparse_entry text=auto" >.gitattributes &&
+ test_must_fail git add --renormalize sparse_entry 2>stderr &&
+ test_cmp error_and_hint stderr &&
+ test_sparse_entry_unchanged
+'
+
+test_expect_success 'git add --dry-run --ignore-missing warn on sparse path' '
+ setup_sparse_entry &&
+ rm sparse_entry &&
+ test_must_fail git add --dry-run --ignore-missing sparse_entry 2>stderr &&
+ test_cmp error_and_hint stderr &&
+ test_sparse_entry_unchanged
+'
+
+test_expect_success 'do not advice about sparse entries when they do not match the pathspec' '
+ setup_sparse_entry &&
+ test_must_fail git add nonexistent 2>stderr &&
+ grep "fatal: pathspec .nonexistent. did not match any files" stderr &&
+ ! grep -F -f sparse_error_header stderr
+'
+
+test_expect_success 'do not warn when pathspec matches dense entries' '
+ setup_sparse_entry &&
+ echo modified >sparse_entry &&
+ >dense_entry &&
+ git add "*_entry" 2>stderr &&
+ test_must_be_empty stderr &&
+ test_sparse_entry_unchanged &&
+ git ls-files --error-unmatch dense_entry
+'
+
+test_expect_success 'add obeys advice.updateSparsePath' '
+ setup_sparse_entry &&
+ test_must_fail git -c advice.updateSparsePath=false add sparse_entry 2>stderr &&
+ test_cmp sparse_entry_error stderr
+
+'
+
+test_done
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index 5f282ec..873aa56 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -859,7 +859,7 @@ test_expect_success 'setup stash with index and worktree changes' '
git stash
'
-test_expect_success 'stash list implies --first-parent -m' '
+test_expect_success 'stash list -p shows simple diff' '
cat >expect <<-EOF &&
stash@{0}
diff --git a/t/t3905-stash-include-untracked.sh b/t/t3905-stash-include-untracked.sh
index 8314ab2..dd2cdcc 100755
--- a/t/t3905-stash-include-untracked.sh
+++ b/t/t3905-stash-include-untracked.sh
@@ -333,6 +333,8 @@ test_expect_success 'stash show --include-untracked shows untracked files' '
git stash show -p --include-untracked >actual &&
test_cmp expect actual &&
git stash show --include-untracked -p >actual &&
+ test_cmp expect actual &&
+ git -c stash.showIncludeUntracked=true stash show -p >actual &&
test_cmp expect actual
'
@@ -367,7 +369,7 @@ test_expect_success 'stash show --only-untracked only shows untracked files' '
test_cmp expect actual
'
-test_expect_success 'stash show --no-include-untracked cancels --{include,show}-untracked' '
+test_expect_success 'stash show --no-include-untracked cancels --{include,only}-untracked' '
git reset --hard &&
git clean -xf &&
>untracked &&
@@ -405,4 +407,19 @@ test_expect_success 'stash show --include-untracked errors on duplicate files' '
test_i18ngrep "worktree and untracked commit have duplicate entries: tracked" err
'
+test_expect_success 'stash show --{include,only}-untracked on stashes without untracked entries' '
+ git reset --hard &&
+ git clean -xf &&
+ >tracked &&
+ git add tracked &&
+ git stash &&
+
+ git stash show >expect &&
+ git stash show --include-untracked >actual &&
+ test_cmp expect actual &&
+
+ git stash show --only-untracked >actual &&
+ test_must_be_empty actual
+'
+
test_done
diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh
index 6cca8b8..7fadc98 100755
--- a/t/t4013-diff-various.sh
+++ b/t/t4013-diff-various.sh
@@ -293,6 +293,7 @@ diff-tree --stat initial mode
diff-tree --summary initial mode
diff-tree master
+diff-tree -m master
diff-tree -p master
diff-tree -p -m master
diff-tree -c master
@@ -337,6 +338,8 @@ log -m -p --first-parent master
log -m -p master
log --cc -m -p master
log -c -m -p master
+log -m --raw master
+log -m --stat master
log -SF master
log -S F master
log -SF -p master
@@ -452,6 +455,58 @@ diff-tree --stat --compact-summary initial mode
diff-tree -R --stat --compact-summary initial mode
EOF
+test_expect_success 'log -m matches log -m -p' '
+ git log -m -p master >result &&
+ process_diffs result >expected &&
+ git log -m >result &&
+ process_diffs result >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'log --diff-merges=on matches --diff-merges=separate' '
+ git log -p --diff-merges=separate master >result &&
+ process_diffs result >expected &&
+ git log -p --diff-merges=on master >result &&
+ process_diffs result >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'deny wrong log.diffMerges config' '
+ test_config log.diffMerges wrong-value &&
+ test_expect_code 128 git log
+'
+
+test_expect_success 'git config log.diffMerges first-parent' '
+ git log -p --diff-merges=first-parent master >result &&
+ process_diffs result >expected &&
+ test_config log.diffMerges first-parent &&
+ git log -p --diff-merges=on master >result &&
+ process_diffs result >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'git config log.diffMerges first-parent vs -m' '
+ git log -p --diff-merges=first-parent master >result &&
+ process_diffs result >expected &&
+ test_config log.diffMerges first-parent &&
+ git log -p -m master >result &&
+ process_diffs result >actual &&
+ test_cmp expected actual
+'
+
+# -m in "git diff-index" means "match missing", that differs
+# from its meaning in "git diff". Let's check it in diff-index.
+# The line in the output for removed file should disappear when
+# we provide -m in diff-index.
+test_expect_success 'git diff-index -m' '
+ rm -f file1 &&
+ git diff-index HEAD >without-m &&
+ lines_count=$(wc -l <without-m) &&
+ git diff-index -m HEAD >with-m &&
+ git restore file1 &&
+ test_line_count = $((lines_count - 1)) with-m
+'
+
test_expect_success 'log -S requires an argument' '
test_must_fail git log -S
'
diff --git a/t/t4013/diff.diff-tree_-m_master b/t/t4013/diff.diff-tree_-m_master
new file mode 100644
index 0000000..6d0a220
--- /dev/null
+++ b/t/t4013/diff.diff-tree_-m_master
@@ -0,0 +1,11 @@
+$ git diff-tree -m master
+59d314ad6f356dd08601a4cd5e530381da3e3c64
+:040000 040000 65f5c9dd60ce3b2b3324b618ac7accf8d912c113 0564e026437809817a64fff393079714b6dd4628 M dir
+:100644 100644 b414108e81e5091fe0974a1858b4d0d22b107f70 10a8a9f3657f91a156b9f0184ed79a20adef9f7f M file0
+59d314ad6f356dd08601a4cd5e530381da3e3c64
+:040000 040000 f977ed46ae6873c1c30ab878e15a4accedc3618b 0564e026437809817a64fff393079714b6dd4628 M dir
+:100644 100644 f4615da674c09df322d6ba8d6b21ecfb1b1ba510 10a8a9f3657f91a156b9f0184ed79a20adef9f7f M file0
+:000000 100644 0000000000000000000000000000000000000000 b1e67221afe8461efd244b487afca22d46b95eb8 A file1
+:100644 000000 01e79c32a8c99c557f0757da7cb6d65b3414466d 0000000000000000000000000000000000000000 D file2
+:100644 000000 7289e35bff32727c08dda207511bec138fdb9ea5 0000000000000000000000000000000000000000 D file3
+$
diff --git a/t/t4013/diff.log_-m_--raw_master b/t/t4013/diff.log_-m_--raw_master
new file mode 100644
index 0000000..cd2ecc4
--- /dev/null
+++ b/t/t4013/diff.log_-m_--raw_master
@@ -0,0 +1,61 @@
+$ git log -m --raw master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+:100644 100644 cead32e... 992913c... M dir/sub
+:100644 100644 b414108... 10a8a9f... M file0
+
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+:100644 100644 7289e35... 992913c... M dir/sub
+:100644 100644 f4615da... 10a8a9f... M file0
+:000000 100644 0000000... b1e6722... A file1
+:100644 000000 01e79c3... 0000000... D file2
+:100644 000000 7289e35... 0000000... D file3
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+
+:100644 100644 35d242b... 7289e35... M dir/sub
+:100644 100644 01e79c3... f4615da... M file0
+:000000 100644 0000000... 7289e35... A file3
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+
+:100644 100644 8422d40... cead32e... M dir/sub
+:000000 100644 0000000... b1e6722... A file1
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+
+:100644 100644 35d242b... 8422d40... M dir/sub
+:100644 100644 01e79c3... b414108... M file0
+:100644 000000 01e79c3... 0000000... D file2
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+$
diff --git a/t/t4013/diff.log_-m_--stat_master b/t/t4013/diff.log_-m_--stat_master
new file mode 100644
index 0000000..c7db084
--- /dev/null
+++ b/t/t4013/diff.log_-m_--stat_master
@@ -0,0 +1,66 @@
+$ git log -m --stat master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+ dir/sub | 2 ++
+ file0 | 3 +++
+ 2 files changed, 5 insertions(+)
+
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+ dir/sub | 4 ++++
+ file0 | 3 +++
+ file1 | 3 +++
+ file2 | 3 ---
+ file3 | 4 ----
+ 5 files changed, 10 insertions(+), 7 deletions(-)
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+)
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+)
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+$
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index 9675bc1..740696c 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -25,33 +25,26 @@ test_expect_success 'setup' '
echo B >B.java
'
+test_expect_success 'setup: test-tool userdiff' '
+ # Make sure additions to builtin_drivers are sorted
+ test_when_finished "rm builtin-drivers.sorted" &&
+ test-tool userdiff list-builtin-drivers >builtin-drivers &&
+ test_file_not_empty builtin-drivers &&
+ sort <builtin-drivers >builtin-drivers.sorted &&
+ test_cmp builtin-drivers.sorted builtin-drivers &&
+
+ # Ditto, but "custom" requires the .git directory and config
+ # to be setup and read.
+ test_when_finished "rm custom-drivers.sorted" &&
+ test-tool userdiff list-custom-drivers >custom-drivers &&
+ test_file_not_empty custom-drivers &&
+ sort <custom-drivers >custom-drivers.sorted &&
+ test_cmp custom-drivers.sorted custom-drivers
+'
+
diffpatterns="
- ada
- bash
- bibtex
- cpp
- csharp
- css
- dts
- elixir
- fortran
- fountain
- golang
- html
- java
- markdown
- matlab
- objc
- pascal
- perl
- php
- python
- ruby
- rust
- tex
- custom1
- custom2
- custom3
+ $(cat builtin-drivers)
+ $(cat custom-drivers)
"
for p in $diffpatterns
@@ -101,13 +94,7 @@ test_expect_success 'setup hunk header tests' '
# check each individual file
for i in $(git ls-files)
do
- if grep broken "$i" >/dev/null 2>&1
- then
- result=failure
- else
- result=success
- fi
- test_expect_$result "hunk header: $i" "
+ test_expect_success "hunk header: $i" "
git diff -U1 $i >actual &&
grep '@@ .* @@.*RIGHT' actual
"
diff --git a/t/t4018/README b/t/t4018/README
index 283e01cc..2d25b2b 100644
--- a/t/t4018/README
+++ b/t/t4018/README
@@ -7,9 +7,6 @@ at least two lines from the line that must appear in the hunk header.
The text that must appear in the hunk header must contain the word
"right", but in all upper-case, like in the title above.
-To mark a test case that highlights a malfunction, insert the word
-BROKEN in all lower-case somewhere in the file.
-
This text is a bit twisted and out of order, but it is itself a
test case for the default hunk header pattern. Know what you are doing
if you change it.
diff --git a/t/t4018/scheme-class b/t/t4018/scheme-class
new file mode 100644
index 0000000..e5e07b4
--- /dev/null
+++ b/t/t4018/scheme-class
@@ -0,0 +1,7 @@
+(define book-class%
+ (class* () object% RIGHT
+ (field (pages 5))
+ (field (ChangeMe 5))
+ (define/public (letters)
+ (* pages 500))
+ (super-new)))
diff --git a/t/t4018/scheme-def b/t/t4018/scheme-def
new file mode 100644
index 0000000..1e2673d
--- /dev/null
+++ b/t/t4018/scheme-def
@@ -0,0 +1,4 @@
+(def (some-func x y z) RIGHT
+ (let ((a x)
+ (b y))
+ (ChangeMe a b)))
diff --git a/t/t4018/scheme-def-variant b/t/t4018/scheme-def-variant
new file mode 100644
index 0000000..d857a61
--- /dev/null
+++ b/t/t4018/scheme-def-variant
@@ -0,0 +1,4 @@
+(defmethod {print point} RIGHT
+ (lambda (self)
+ (with ((point x y) self)
+ (printf "{ChangeMe x:~a y:~a}~n" x y))))
diff --git a/t/t4018/scheme-define-slash-public b/t/t4018/scheme-define-slash-public
new file mode 100644
index 0000000..39a93a1
--- /dev/null
+++ b/t/t4018/scheme-define-slash-public
@@ -0,0 +1,7 @@
+(define bar-class%
+ (class object%
+ (field (info 5))
+ (define/public (foo) RIGHT
+ (+ info 42)
+ (* info ChangeMe))
+ (super-new)))
diff --git a/t/t4018/scheme-define-syntax b/t/t4018/scheme-define-syntax
new file mode 100644
index 0000000..7d5e99e
--- /dev/null
+++ b/t/t4018/scheme-define-syntax
@@ -0,0 +1,8 @@
+(define-syntax define-test-suite RIGHT
+ (syntax-rules ()
+ ((_ suite-name (name test) ChangeMe ...)
+ (define suite-name
+ (let ((tests
+ `((name . ,test) ...)))
+ (lambda ()
+ (run-suite 'suite-name tests)))))))
diff --git a/t/t4018/scheme-define-variant b/t/t4018/scheme-define-variant
new file mode 100644
index 0000000..9117088
--- /dev/null
+++ b/t/t4018/scheme-define-variant
@@ -0,0 +1,4 @@
+(define* (some-func x y z) RIGHT
+ (let ((a x)
+ (b y))
+ (ChangeMe a b)))
diff --git a/t/t4018/scheme-library b/t/t4018/scheme-library
new file mode 100644
index 0000000..82ea3df
--- /dev/null
+++ b/t/t4018/scheme-library
@@ -0,0 +1,11 @@
+(library (my-helpers id-stuff) RIGHT
+ (export find-dup)
+ (import (ChangeMe))
+ (define (find-dup l)
+ (and (pair? l)
+ (let loop ((rest (cdr l)))
+ (cond
+ [(null? rest) (find-dup (cdr l))]
+ [(bound-identifier=? (car l) (car rest))
+ (car rest)]
+ [else (loop (cdr rest))])))))
diff --git a/t/t4018/scheme-local-define b/t/t4018/scheme-local-define
new file mode 100644
index 0000000..bc6d8ae
--- /dev/null
+++ b/t/t4018/scheme-local-define
@@ -0,0 +1,4 @@
+(define (higher-order)
+ (define local-function RIGHT
+ (lambda (x)
+ (car "this is" "ChangeMe"))))
diff --git a/t/t4018/scheme-module b/t/t4018/scheme-module
new file mode 100644
index 0000000..edfae0e
--- /dev/null
+++ b/t/t4018/scheme-module
@@ -0,0 +1,6 @@
+(module A RIGHT
+ (export with-display-exception)
+ (extern (display-exception display-exception ChangeMe))
+ (def (with-display-exception thunk)
+ (with-catch (lambda (e) (display-exception e (current-error-port)) e)
+ thunk)))
diff --git a/t/t4018/scheme-top-level-define b/t/t4018/scheme-top-level-define
new file mode 100644
index 0000000..624743c
--- /dev/null
+++ b/t/t4018/scheme-top-level-define
@@ -0,0 +1,4 @@
+(define (some-func x y z) RIGHT
+ (let ((a x)
+ (b y))
+ (ChangeMe a b)))
diff --git a/t/t4018/scheme-user-defined-define b/t/t4018/scheme-user-defined-define
new file mode 100644
index 0000000..35fe7cc
--- /dev/null
+++ b/t/t4018/scheme-user-defined-define
@@ -0,0 +1,6 @@
+(define-test-suite record\ case-tests RIGHT
+ (record-case-1 (lambda (fail)
+ (let ((a (make-foo 1 2)))
+ (record-case a
+ ((bar x) (ChangeMe))
+ ((foo a b) (+ a b)))))))
diff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh
index 56f1e62..561c582 100755
--- a/t/t4034-diff-words.sh
+++ b/t/t4034-diff-words.sh
@@ -184,6 +184,11 @@ test_expect_success 'word diff with a regular expression' '
word_diff --color-words="[a-z]+"
'
+test_expect_success 'word diff with zero length matches' '
+ cp expect.letter-runs-are-words expect &&
+ word_diff --color-words="[a-z${LF}]*"
+'
+
test_expect_success 'set up a diff driver' '
git config diff.testdriver.wordRegex "[^[:space:]]" &&
cat <<-\EOF >.gitattributes
@@ -325,6 +330,7 @@ test_language_driver perl
test_language_driver php
test_language_driver python
test_language_driver ruby
+test_language_driver scheme
test_language_driver tex
test_expect_success 'word-diff with diff.sbe' '
diff --git a/t/t4034/scheme/expect b/t/t4034/scheme/expect
new file mode 100644
index 0000000..496cd5d
--- /dev/null
+++ b/t/t4034/scheme/expect
@@ -0,0 +1,11 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 74b6605..63b6ac4 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,6 +1,6 @@<RESET>
+(define (<RED>myfunc a b<RESET><GREEN>my-func first second<RESET>)
+ ; This is a <RED>really<RESET><GREEN>(moderately)<RESET> cool function.
+ (<RED>this\place<RESET><GREEN>that\place<RESET> (+ 3 4))
+ (define <RED>some-text<RESET><GREEN>|a greeting|<RESET> "hello")
+ (let ((c (<RED>+ a b<RESET><GREEN>add1 first<RESET>)))
+ (format "one more than the total is %d" (<RED>add1<RESET><GREEN>+<RESET> c <GREEN>second<RESET>))))
diff --git a/t/t4034/scheme/post b/t/t4034/scheme/post
new file mode 100644
index 0000000..63b6ac4
--- /dev/null
+++ b/t/t4034/scheme/post
@@ -0,0 +1,6 @@
+(define (my-func first second)
+ ; This is a (moderately) cool function.
+ (that\place (+ 3 4))
+ (define |a greeting| "hello")
+ (let ((c (add1 first)))
+ (format "one more than the total is %d" (+ c second))))
diff --git a/t/t4034/scheme/pre b/t/t4034/scheme/pre
new file mode 100644
index 0000000..74b6605
--- /dev/null
+++ b/t/t4034/scheme/pre
@@ -0,0 +1,6 @@
+(define (myfunc a b)
+ ; This is a really cool function.
+ (this\place (+ 3 4))
+ (define some-text "hello")
+ (let ((c (+ a b)))
+ (format "one more than the total is %d" (add1 c))))
diff --git a/t/t4108-apply-threeway.sh b/t/t4108-apply-threeway.sh
index d62db3f..65147ef 100755
--- a/t/t4108-apply-threeway.sh
+++ b/t/t4108-apply-threeway.sh
@@ -160,4 +160,74 @@ test_expect_success 'apply -3 with add/add conflict (dirty working tree)' '
test_cmp three.save three
'
+test_expect_success 'apply -3 with ambiguous repeating file' '
+ git reset --hard &&
+ test_write_lines 1 2 1 2 1 2 1 2 1 2 1 >one_two_repeat &&
+ git add one_two_repeat &&
+ git commit -m "init one" &&
+ test_write_lines 1 2 1 2 1 2 1 2 one 2 1 >one_two_repeat &&
+ git commit -a -m "change one" &&
+
+ git diff HEAD~ >Repeat.diff &&
+ git reset --hard HEAD~ &&
+
+ test_write_lines 1 2 1 2 1 2 one 2 1 2 one >one_two_repeat &&
+ git commit -a -m "change surrounding one" &&
+
+ git apply --index --3way Repeat.diff &&
+ test_write_lines 1 2 1 2 1 2 one 2 one 2 one >expect &&
+
+ test_cmp expect one_two_repeat
+'
+
+test_expect_success 'apply with --3way --cached clean apply' '
+ # Merging side should be similar to applying this patch
+ git diff ...side >P.diff &&
+
+ # The corresponding cleanly applied merge
+ git reset --hard &&
+ git checkout main~ &&
+ git merge --no-commit side &&
+ git ls-files -s >expect.ls &&
+
+ # should succeed
+ git reset --hard &&
+ git checkout main~ &&
+ git apply --cached --3way P.diff &&
+ git ls-files -s >actual.ls &&
+ print_sanitized_conflicted_diff >actual.diff &&
+
+ # The cache should resemble the corresponding merge
+ # (both files at stage #0)
+ test_cmp expect.ls actual.ls &&
+ # However the working directory should not change
+ >expect.diff &&
+ test_cmp expect.diff actual.diff
+'
+
+test_expect_success 'apply with --3way --cached and conflicts' '
+ # Merging side should be similar to applying this patch
+ git diff ...side >P.diff &&
+
+ # The corresponding conflicted merge
+ git reset --hard &&
+ git checkout main^0 &&
+ test_must_fail git merge --no-commit side &&
+ git ls-files -s >expect.ls &&
+
+ # should fail to apply
+ git reset --hard &&
+ git checkout main^0 &&
+ test_must_fail git apply --cached --3way P.diff &&
+ git ls-files -s >actual.ls &&
+ print_sanitized_conflicted_diff >actual.diff &&
+
+ # The cache should resemble the corresponding merge
+ # (one file at stage #0, one file at stages #1 #2 #3)
+ test_cmp expect.ls actual.ls &&
+ # However the working directory should not change
+ >expect.diff &&
+ test_cmp expect.diff actual.diff
+'
+
test_done
diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh
index cabdf7d..8272d94 100755
--- a/t/t4205-log-pretty-formats.sh
+++ b/t/t4205-log-pretty-formats.sh
@@ -525,20 +525,25 @@ test_expect_success 'strbuf_utf8_replace() not producing NUL' '
! grep Q actual
'
-# ISO strict date format
-test_expect_success 'ISO and ISO-strict date formats display the same values' '
- git log --format=%ai%n%ci |
- sed -e "s/ /T/; s/ //; s/..\$/:&/" >expected &&
+# --date=[XXX] and corresponding %a[X] %c[X] format equivalency
+test_expect_success '--date=iso-strict %ad%cd is the same as %aI%cI' '
+ git log --format=%ad%n%cd --date=iso-strict >expected &&
git log --format=%aI%n%cI >actual &&
test_cmp expected actual
'
-test_expect_success 'short date' '
+test_expect_success '--date=short %ad%cd is the same as %as%cs' '
git log --format=%ad%n%cd --date=short >expected &&
git log --format=%as%n%cs >actual &&
test_cmp expected actual
'
+test_expect_success '--date=human %ad%cd is the same as %ah%ch' '
+ git log --format=%ad%n%cd --date=human >expected &&
+ git log --format=%ah%n%ch >actual &&
+ test_cmp expected actual
+'
+
# get new digests (with no abbreviations)
test_expect_success 'set up log decoration tests' '
head1=$(git rev-parse --verify HEAD~0) &&
diff --git a/t/t4258-am-quoted-cr.sh b/t/t4258-am-quoted-cr.sh
new file mode 100755
index 0000000..fb5071f
--- /dev/null
+++ b/t/t4258-am-quoted-cr.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+test_description='test am --quoted-cr=<action>'
+
+. ./test-lib.sh
+
+DATA="$TEST_DIRECTORY/t4258"
+
+test_expect_success 'setup' '
+ test_write_lines one two three >text &&
+ test_commit one text &&
+ test_write_lines one owt three >text &&
+ test_commit two text
+'
+
+test_expect_success 'am warn if quoted-cr is found' '
+ git reset --hard one &&
+ test_must_fail git am "$DATA/mbox" 2>err &&
+ grep "quoted CRLF detected" err
+'
+
+test_expect_success 'am --quoted-cr=strip' '
+ test_might_fail git am --abort &&
+ git reset --hard one &&
+ git am --quoted-cr=strip "$DATA/mbox" &&
+ git diff --exit-code HEAD two
+'
+
+test_expect_success 'am with config mailinfo.quotecr=strip' '
+ test_might_fail git am --abort &&
+ git reset --hard one &&
+ test_config mailinfo.quotedCr strip &&
+ git am "$DATA/mbox" &&
+ git diff --exit-code HEAD two
+'
+
+test_done
diff --git a/t/t4258/mbox b/t/t4258/mbox
new file mode 100644
index 0000000..c62819f
--- /dev/null
+++ b/t/t4258/mbox
@@ -0,0 +1,12 @@
+From: A U Thor <mail@example.com>
+To: list@example.org
+Subject: [PATCH v2] sample
+Date: Mon, 3 Aug 2020 22:40:55 +0700
+Message-Id: <msg-id@example.com>
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: base64
+
+VGhpcyBpcyBjb21taXQgbWVzc2FnZS4NCi0tLQ0KIHRleHQgfCAyICstDQogMSBmaWxlIGNoYW5n
+ZWQsIDEgaW5zZXJ0aW9uKCspLCAxIGRlbGV0aW9uKC0pDQoNCmRpZmYgLS1naXQgYS90ZXh0IGIv
+dGV4dA0KaW5kZXggNTYyNmFiZi4uZjcxOWVmZCAxMDA2NDQNCi0tLSBhL3RleHQNCisrKyBiL3Rl
+eHQNCkBAIC0xICsxIEBADQotb25lDQordHdvDQotLSANCjIuMzEuMQoK
diff --git a/t/t5100-mailinfo.sh b/t/t5100-mailinfo.sh
index 147e616..141b29f 100755
--- a/t/t5100-mailinfo.sh
+++ b/t/t5100-mailinfo.sh
@@ -228,4 +228,44 @@ test_expect_success 'mailinfo handles unusual header whitespace' '
test_cmp expect actual
'
+check_quoted_cr_mail () {
+ mail="$1" && shift &&
+ git mailinfo -u "$@" "$mail.msg" "$mail.patch" \
+ <"$mail" >"$mail.info" 2>"$mail.err" &&
+ test_cmp "$mail-expected.msg" "$mail.msg" &&
+ test_cmp "$mail-expected.patch" "$mail.patch" &&
+ test_cmp "$DATA/quoted-cr-info" "$mail.info"
+}
+
+test_expect_success 'split base64 email with quoted-cr' '
+ mkdir quoted-cr &&
+ git mailsplit -oquoted-cr "$DATA/quoted-cr.mbox" >quoted-cr/last &&
+ test $(cat quoted-cr/last) = 2
+'
+
+test_expect_success 'mailinfo warn CR in base64 encoded email' '
+ sed -e "s/%%$//" -e "s/%%/$(printf \\015)/g" "$DATA/quoted-cr-msg" \
+ >quoted-cr/0001-expected.msg &&
+ sed "s/%%/$(printf \\015)/g" "$DATA/quoted-cr-msg" \
+ >quoted-cr/0002-expected.msg &&
+ sed -e "s/%%$//" -e "s/%%/$(printf \\015)/g" "$DATA/quoted-cr-patch" \
+ >quoted-cr/0001-expected.patch &&
+ sed "s/%%/$(printf \\015)/g" "$DATA/quoted-cr-patch" \
+ >quoted-cr/0002-expected.patch &&
+ check_quoted_cr_mail quoted-cr/0001 &&
+ test_must_be_empty quoted-cr/0001.err &&
+ check_quoted_cr_mail quoted-cr/0002 &&
+ grep "quoted CRLF detected" quoted-cr/0002.err &&
+ check_quoted_cr_mail quoted-cr/0001 --quoted-cr=nowarn &&
+ test_must_be_empty quoted-cr/0001.err &&
+ check_quoted_cr_mail quoted-cr/0002 --quoted-cr=nowarn &&
+ test_must_be_empty quoted-cr/0002.err &&
+ cp quoted-cr/0001-expected.msg quoted-cr/0002-expected.msg &&
+ cp quoted-cr/0001-expected.patch quoted-cr/0002-expected.patch &&
+ check_quoted_cr_mail quoted-cr/0001 --quoted-cr=strip &&
+ test_must_be_empty quoted-cr/0001.err &&
+ check_quoted_cr_mail quoted-cr/0002 --quoted-cr=strip &&
+ test_must_be_empty quoted-cr/0002.err
+'
+
test_done
diff --git a/t/t5100/quoted-cr-info b/t/t5100/quoted-cr-info
new file mode 100644
index 0000000..dab2228
--- /dev/null
+++ b/t/t5100/quoted-cr-info
@@ -0,0 +1,5 @@
+Author: A U Thor
+Email: mail@example.com
+Subject: sample
+Date: Mon, 3 Aug 2020 22:40:55 +0700
+
diff --git a/t/t5100/quoted-cr-msg b/t/t5100/quoted-cr-msg
new file mode 100644
index 0000000..89b05a0
--- /dev/null
+++ b/t/t5100/quoted-cr-msg
@@ -0,0 +1,2 @@
+On different distro, %%pytest is suffixed with different patterns.%%
+%%
diff --git a/t/t5100/quoted-cr-patch b/t/t5100/quoted-cr-patch
new file mode 100644
index 0000000..65b13ee
--- /dev/null
+++ b/t/t5100/quoted-cr-patch
@@ -0,0 +1,22 @@
+---%%
+ configure | 2 +-%%
+ 1 file changed, 1 insertion(+), 1 deletion(-)%%
+%%
+diff --git a/configure b/configure%%
+index db3538b3..f7c1c095 100755%%
+--- a/configure%%
++++ b/configure%%
+@@ -814,7 +814,7 @@ if [ $have_python3 -eq 1 ]; then%%
+ printf "%%Checking for python3 pytest (>= 3.0)... "%%
+ conf=$(mktemp)%%
+ printf "[pytest]\nminversion=3.0\n" > $conf%%
+- if pytest-3 -c $conf --version >/dev/null 2>&1; then%%
++ if "$python" -m pytest -c $conf --version >/dev/null 2>&1; then%%
+ printf "Yes.\n"%%
+ have_python3_pytest=1%%
+ else%%
+-- %%
+2.28.0%%
+_______________________________________________
+example mailing list -- list@example.org
+To unsubscribe send an email to list-leave@example.org
diff --git a/t/t5100/quoted-cr.mbox b/t/t5100/quoted-cr.mbox
new file mode 100644
index 0000000..909021b
--- /dev/null
+++ b/t/t5100/quoted-cr.mbox
@@ -0,0 +1,47 @@
+From nobody Mon Sep 17 00:00:00 2001
+From: A U Thor <mail@example.com>
+To: list@example.org
+Subject: [PATCH v2] sample
+Date: Mon, 3 Aug 2020 22:40:55 +0700
+Message-Id: <msg-id@example.com>
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: base64
+
+T24gZGlmZmVyZW50IGRpc3RybywgDXB5dGVzdCBpcyBzdWZmaXhlZCB3aXRoIGRpZmZlcmVudCBw
+YXR0ZXJucy4KCi0tLQogY29uZmlndXJlIHwgMiArLQogMSBmaWxlIGNoYW5nZWQsIDEgaW5zZXJ0
+aW9uKCspLCAxIGRlbGV0aW9uKC0pCgpkaWZmIC0tZ2l0IGEvY29uZmlndXJlIGIvY29uZmlndXJl
+CmluZGV4IGRiMzUzOGIzLi5mN2MxYzA5NSAxMDA3NTUKLS0tIGEvY29uZmlndXJlCisrKyBiL2Nv
+bmZpZ3VyZQpAQCAtODE0LDcgKzgxNCw3IEBAIGlmIFsgJGhhdmVfcHl0aG9uMyAtZXEgMSBdOyB0
+aGVuCiAgICAgcHJpbnRmICINQ2hlY2tpbmcgZm9yIHB5dGhvbjMgcHl0ZXN0ICg+PSAzLjApLi4u
+ICIKICAgICBjb25mPSQobWt0ZW1wKQogICAgIHByaW50ZiAiW3B5dGVzdF1cbm1pbnZlcnNpb249
+My4wXG4iID4gJGNvbmYKLSAgICBpZiBweXRlc3QtMyAtYyAkY29uZiAtLXZlcnNpb24gPi9kZXYv
+bnVsbCAyPiYxOyB0aGVuCisgICAgaWYgIiRweXRob24iIC1tIHB5dGVzdCAtYyAkY29uZiAtLXZl
+cnNpb24gPi9kZXYvbnVsbCAyPiYxOyB0aGVuCiAgICAgICAgIHByaW50ZiAiWWVzLlxuIgogICAg
+ICAgICBoYXZlX3B5dGhvbjNfcHl0ZXN0PTEKICAgICBlbHNlCi0tIAoyLjI4LjAKX19fX19fX19f
+X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX18KZXhhbXBsZSBtYWlsaW5nIGxp
+c3QgLS0gbGlzdEBleGFtcGxlLm9yZwpUbyB1bnN1YnNjcmliZSBzZW5kIGFuIGVtYWlsIHRvIGxp
+c3QtbGVhdmVAZXhhbXBsZS5vcmcK
+
+From nobody Mon Sep 17 00:00:00 2001
+From: A U Thor <mail@example.com>
+To: list@example.org
+Subject: [PATCH v2] sample
+Date: Mon, 3 Aug 2020 22:40:55 +0700
+Message-Id: <msg-id2@example.com>
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: base64
+
+T24gZGlmZmVyZW50IGRpc3RybywgDXB5dGVzdCBpcyBzdWZmaXhlZCB3aXRoIGRpZmZlcmVudCBw
+YXR0ZXJucy4NCg0KLS0tDQogY29uZmlndXJlIHwgMiArLQ0KIDEgZmlsZSBjaGFuZ2VkLCAxIGlu
+c2VydGlvbigrKSwgMSBkZWxldGlvbigtKQ0KDQpkaWZmIC0tZ2l0IGEvY29uZmlndXJlIGIvY29u
+ZmlndXJlDQppbmRleCBkYjM1MzhiMy4uZjdjMWMwOTUgMTAwNzU1DQotLS0gYS9jb25maWd1cmUN
+CisrKyBiL2NvbmZpZ3VyZQ0KQEAgLTgxNCw3ICs4MTQsNyBAQCBpZiBbICRoYXZlX3B5dGhvbjMg
+LWVxIDEgXTsgdGhlbg0KICAgICBwcmludGYgIg1DaGVja2luZyBmb3IgcHl0aG9uMyBweXRlc3Qg
+KD49IDMuMCkuLi4gIg0KICAgICBjb25mPSQobWt0ZW1wKQ0KICAgICBwcmludGYgIltweXRlc3Rd
+XG5taW52ZXJzaW9uPTMuMFxuIiA+ICRjb25mDQotICAgIGlmIHB5dGVzdC0zIC1jICRjb25mIC0t
+dmVyc2lvbiA+L2Rldi9udWxsIDI+JjE7IHRoZW4NCisgICAgaWYgIiRweXRob24iIC1tIHB5dGVz
+dCAtYyAkY29uZiAtLXZlcnNpb24gPi9kZXYvbnVsbCAyPiYxOyB0aGVuDQogICAgICAgICBwcmlu
+dGYgIlllcy5cbiINCiAgICAgICAgIGhhdmVfcHl0aG9uM19weXRlc3Q9MQ0KICAgICBlbHNlDQot
+LSANCjIuMjguMA0KX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19f
+X18KZXhhbXBsZSBtYWlsaW5nIGxpc3QgLS0gbGlzdEBleGFtcGxlLm9yZwpUbyB1bnN1YnNjcmli
+ZSBzZW5kIGFuIGVtYWlsIHRvIGxpc3QtbGVhdmVAZXhhbXBsZS5vcmcK
diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh
index 2fc5e68..5c5e53f 100755
--- a/t/t5300-pack-object.sh
+++ b/t/t5300-pack-object.sh
@@ -8,125 +8,91 @@ test_description='git pack-object
'
. ./test-lib.sh
-TRASH=$(pwd)
-
-test_expect_success \
- 'setup' \
- 'rm -f .git/index* &&
- perl -e "print \"a\" x 4096;" > a &&
- perl -e "print \"b\" x 4096;" > b &&
- perl -e "print \"c\" x 4096;" > c &&
- test-tool genrandom "seed a" 2097152 > a_big &&
- test-tool genrandom "seed b" 2097152 > b_big &&
- git update-index --add a a_big b b_big c &&
- cat c >d && echo foo >>d && git update-index --add d &&
- tree=$(git write-tree) &&
- commit=$(git commit-tree $tree </dev/null) && {
- echo $tree &&
- echo $commit &&
- git ls-tree $tree | sed -e "s/.* \\([0-9a-f]*\\) .*/\\1/"
- } >obj-list && {
- git diff-tree --root -p $commit &&
- while read object
- do
- t=$(git cat-file -t $object) &&
- git cat-file $t $object || return 1
- done <obj-list
- } >expect'
-
-test_expect_success \
- 'pack without delta' \
- 'packname_1=$(git pack-objects --window=0 test-1 <obj-list)'
-
-test_expect_success \
- 'pack-objects with bogus arguments' \
- 'test_must_fail git pack-objects --window=0 test-1 blah blah <obj-list'
-
-rm -fr .git2
-mkdir .git2
-
-test_expect_success \
- 'unpack without delta' \
- "GIT_OBJECT_DIRECTORY=.git2/objects &&
- export GIT_OBJECT_DIRECTORY &&
- git init &&
- git unpack-objects -n <test-1-${packname_1}.pack &&
- git unpack-objects <test-1-${packname_1}.pack"
-
-unset GIT_OBJECT_DIRECTORY
-cd "$TRASH/.git2"
+test_expect_success 'setup' '
+ rm -f .git/index* &&
+ perl -e "print \"a\" x 4096;" >a &&
+ perl -e "print \"b\" x 4096;" >b &&
+ perl -e "print \"c\" x 4096;" >c &&
+ test-tool genrandom "seed a" 2097152 >a_big &&
+ test-tool genrandom "seed b" 2097152 >b_big &&
+ git update-index --add a a_big b b_big c &&
+ cat c >d && echo foo >>d && git update-index --add d &&
+ tree=$(git write-tree) &&
+ commit=$(git commit-tree $tree </dev/null) &&
+ {
+ echo $tree &&
+ echo $commit &&
+ git ls-tree $tree | sed -e "s/.* \\([0-9a-f]*\\) .*/\\1/"
+ } >obj-list &&
+ {
+ git diff-tree --root -p $commit &&
+ while read object
+ do
+ t=$(git cat-file -t $object) &&
+ git cat-file $t $object || return 1
+ done <obj-list
+ } >expect
+'
-test_expect_success \
- 'check unpack without delta' \
- '(cd ../.git && find objects -type f -print) |
- while read path
- do
- cmp $path ../.git/$path || {
- echo $path differs.
- return 1
- }
- done'
-cd "$TRASH"
+# usage: check_deltas <stderr_from_pack_objects> <cmp_op> <nr_deltas>
+# e.g.: check_deltas stderr -gt 0
+check_deltas() {
+ deltas=$(perl -lne '/delta (\d+)/ and print $1' "$1") &&
+ shift &&
+ if ! test "$deltas" "$@"
+ then
+ echo >&2 "unexpected number of deltas (compared $delta $*)"
+ return 1
+ fi
+}
+
+test_expect_success 'pack without delta' '
+ packname_1=$(git pack-objects --progress --window=0 test-1 \
+ <obj-list 2>stderr) &&
+ check_deltas stderr = 0
+'
-test_expect_success \
- 'pack with REF_DELTA' \
- 'pwd &&
- packname_2=$(git pack-objects test-2 <obj-list)'
+test_expect_success 'pack-objects with bogus arguments' '
+ test_must_fail git pack-objects --window=0 test-1 blah blah <obj-list
+'
-rm -fr .git2
-mkdir .git2
+check_unpack () {
+ 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
+}
+
+test_expect_success 'unpack without delta' '
+ check_unpack test-1-${packname_1}
+'
-test_expect_success \
- 'unpack with REF_DELTA' \
- 'GIT_OBJECT_DIRECTORY=.git2/objects &&
- export GIT_OBJECT_DIRECTORY &&
- git init &&
- git unpack-objects -n <test-2-${packname_2}.pack &&
- git unpack-objects <test-2-${packname_2}.pack'
-
-unset GIT_OBJECT_DIRECTORY
-cd "$TRASH/.git2"
-test_expect_success \
- 'check unpack with REF_DELTA' \
- '(cd ../.git && find objects -type f -print) |
- while read path
- do
- cmp $path ../.git/$path || {
- echo $path differs.
- return 1
- }
- done'
-cd "$TRASH"
+test_expect_success 'pack with REF_DELTA' '
+ packname_2=$(git pack-objects --progress test-2 <obj-list 2>stderr) &&
+ check_deltas stderr -gt 0
+'
-test_expect_success \
- 'pack with OFS_DELTA' \
- 'pwd &&
- packname_3=$(git pack-objects --delta-base-offset test-3 <obj-list)'
+test_expect_success 'unpack with REF_DELTA' '
+ check_unpack test-2-${packname_2}
+'
-rm -fr .git2
-mkdir .git2
+test_expect_success 'pack with OFS_DELTA' '
+ packname_3=$(git pack-objects --progress --delta-base-offset test-3 \
+ <obj-list 2>stderr) &&
+ check_deltas stderr -gt 0
+'
-test_expect_success \
- 'unpack with OFS_DELTA' \
- 'GIT_OBJECT_DIRECTORY=.git2/objects &&
- export GIT_OBJECT_DIRECTORY &&
- git init &&
- git unpack-objects -n <test-3-${packname_3}.pack &&
- git unpack-objects <test-3-${packname_3}.pack'
-
-unset GIT_OBJECT_DIRECTORY
-cd "$TRASH/.git2"
-test_expect_success \
- 'check unpack with OFS_DELTA' \
- '(cd ../.git && find objects -type f -print) |
- while read path
- do
- cmp $path ../.git/$path || {
- echo $path differs.
- return 1
- }
- done'
-cd "$TRASH"
+test_expect_success 'unpack with OFS_DELTA' '
+ check_unpack test-3-${packname_3}
+'
test_expect_success 'compare delta flavors' '
perl -e '\''
@@ -135,55 +101,33 @@ test_expect_success 'compare delta flavors' '
'\'' test-2-$packname_2.pack test-3-$packname_3.pack
'
-rm -fr .git2
-mkdir .git2
+check_use_objects () {
+ test_when_finished "rm -rf git2" &&
+ git init --bare git2 &&
+ cp "$1".pack "$1".idx git2/objects/pack &&
+ (
+ cd git2 &&
+ git diff-tree --root -p $commit &&
+ while read object
+ do
+ t=$(git cat-file -t $object) &&
+ git cat-file $t $object || exit 1
+ done
+ ) <obj-list >current &&
+ cmp expect current
+}
-test_expect_success \
- 'use packed objects' \
- 'GIT_OBJECT_DIRECTORY=.git2/objects &&
- export GIT_OBJECT_DIRECTORY &&
- git init &&
- cp test-1-${packname_1}.pack test-1-${packname_1}.idx .git2/objects/pack && {
- git diff-tree --root -p $commit &&
- while read object
- do
- t=$(git cat-file -t $object) &&
- git cat-file $t $object || return 1
- done <obj-list
- } >current &&
- cmp expect current'
+test_expect_success 'use packed objects' '
+ check_use_objects test-1-${packname_1}
+'
-test_expect_success \
- 'use packed deltified (REF_DELTA) objects' \
- 'GIT_OBJECT_DIRECTORY=.git2/objects &&
- export GIT_OBJECT_DIRECTORY &&
- rm -f .git2/objects/pack/test-* &&
- cp test-2-${packname_2}.pack test-2-${packname_2}.idx .git2/objects/pack && {
- git diff-tree --root -p $commit &&
- while read object
- do
- t=$(git cat-file -t $object) &&
- git cat-file $t $object || return 1
- done <obj-list
- } >current &&
- cmp expect current'
+test_expect_success 'use packed deltified (REF_DELTA) objects' '
+ check_use_objects test-2-${packname_2}
+'
-test_expect_success \
- 'use packed deltified (OFS_DELTA) objects' \
- 'GIT_OBJECT_DIRECTORY=.git2/objects &&
- export GIT_OBJECT_DIRECTORY &&
- rm -f .git2/objects/pack/test-* &&
- cp test-3-${packname_3}.pack test-3-${packname_3}.idx .git2/objects/pack && {
- git diff-tree --root -p $commit &&
- while read object
- do
- t=$(git cat-file -t $object) &&
- git cat-file $t $object || return 1
- done <obj-list
- } >current &&
- cmp expect current'
-
-unset GIT_OBJECT_DIRECTORY
+test_expect_success 'use packed deltified (OFS_DELTA) objects' '
+ check_use_objects test-3-${packname_3}
+'
test_expect_success 'survive missing objects/pack directory' '
(
@@ -669,4 +613,9 @@ test_expect_success '--stdin-packs with broken links' '
)
'
+test_expect_success 'negative window clamps to 0' '
+ git pack-objects --progress --window=-1 neg-window <obj-list 2>stderr &&
+ check_deltas stderr = 0
+'
+
test_done
diff --git a/t/t5304-prune.sh b/t/t5304-prune.sh
index b447ce5..3475b06 100755
--- a/t/t5304-prune.sh
+++ b/t/t5304-prune.sh
@@ -352,4 +352,20 @@ test_expect_success 'trivial prune with bitmaps enabled' '
test_must_fail git cat-file -e $blob
'
+test_expect_success 'old reachable-from-recent retained with bitmaps' '
+ git repack -adb &&
+ to_drop=$(echo bitmap-from-recent-1 | git hash-object -w --stdin) &&
+ test-tool chmtime -86400 .git/objects/$(test_oid_to_path $to_drop) &&
+ to_save=$(echo bitmap-from-recent-2 | git hash-object -w --stdin) &&
+ test-tool chmtime -86400 .git/objects/$(test_oid_to_path $to_save) &&
+ tree=$(printf "100644 blob $to_save\tfile\n" | git mktree) &&
+ test-tool chmtime -86400 .git/objects/$(test_oid_to_path $tree) &&
+ commit=$(echo foo | git commit-tree $tree) &&
+ git prune --expire=12.hours.ago &&
+ git cat-file -e $commit &&
+ git cat-file -e $tree &&
+ git cat-file -e $to_save &&
+ test_must_fail git cat-file -e $to_drop
+'
+
test_done
diff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh
index 40b9f63..b028387 100755
--- a/t/t5310-pack-bitmaps.sh
+++ b/t/t5310-pack-bitmaps.sh
@@ -461,6 +461,29 @@ test_expect_success 'truncated bitmap fails gracefully (cache)' '
test_i18ngrep corrupted.bitmap.index stderr
'
+test_expect_success 'enumerating progress counts pack-reused objects' '
+ count=$(git rev-list --objects --all --count) &&
+ git repack -adb &&
+
+ # check first with only reused objects; confirm that our progress
+ # showed the right number, and also that we did pack-reuse as expected.
+ # Check only the final "done" line of the meter (there may be an
+ # arbitrary number of intermediate lines ending with CR).
+ GIT_PROGRESS_DELAY=0 \
+ git pack-objects --all --stdout --progress \
+ </dev/null >/dev/null 2>stderr &&
+ grep "Enumerating objects: $count, done" stderr &&
+ grep "pack-reused $count" stderr &&
+
+ # now the same but with one non-reused object
+ git commit --allow-empty -m "an extra commit object" &&
+ GIT_PROGRESS_DELAY=0 \
+ git pack-objects --all --stdout --progress \
+ </dev/null >/dev/null 2>stderr &&
+ grep "Enumerating objects: $((count+1)), done" stderr &&
+ grep "pack-reused $count" stderr
+'
+
# have_delta <obj> <expected_base>
#
# Note that because this relies on cat-file, it might find _any_ copy of an
@@ -554,4 +577,42 @@ test_expect_success 'fetch with bitmaps can reuse old base' '
)
'
+test_expect_success 'pack.preferBitmapTips' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ # create enough commits that not all are receive bitmap
+ # coverage even if they are all at the tip of some reference.
+ test_commit_bulk --message="%s" 103 &&
+
+ git rev-list HEAD >commits.raw &&
+ sort <commits.raw >commits &&
+
+ git log --format="create refs/tags/%s %H" HEAD >refs &&
+ git update-ref --stdin <refs &&
+
+ git repack -adb &&
+ test-tool bitmap list-commits | sort >bitmaps &&
+
+ # remember which commits did not receive bitmaps
+ comm -13 bitmaps commits >before &&
+ test_file_not_empty before &&
+
+ # mark the commits which did not receive bitmaps as preferred,
+ # and generate the bitmap again
+ perl -pe "s{^}{create refs/tags/include/$. }" <before |
+ git update-ref --stdin &&
+ git -c pack.preferBitmapTips=refs/tags/include repack -adb &&
+
+ # finally, check that the commit(s) without bitmap coverage
+ # are not the same ones as before
+ test-tool bitmap list-commits | sort >bitmaps &&
+ comm -13 bitmaps commits >after &&
+
+ ! test_cmp before after
+ )
+'
+
test_done
diff --git a/t/t5316-pack-delta-depth.sh b/t/t5316-pack-delta-depth.sh
index a8c1bc0..759169d 100755
--- a/t/t5316-pack-delta-depth.sh
+++ b/t/t5316-pack-delta-depth.sh
@@ -69,6 +69,7 @@ test_expect_success 'create series of packs' '
max_chain() {
git index-pack --verify-stat-only "$1" >output &&
perl -lne '
+ BEGIN { $len = 0 }
/chain length = (\d+)/ and $len = $1;
END { print $len }
' output
@@ -94,4 +95,18 @@ test_expect_success '--depth limits depth' '
test_cmp expect actual
'
+test_expect_success '--depth=0 disables deltas' '
+ pack=$(git pack-objects --all --depth=0 </dev/null pack) &&
+ echo 0 >expect &&
+ max_chain pack-$pack.pack >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'negative depth disables deltas' '
+ pack=$(git pack-objects --all --depth=-1 </dev/null pack) &&
+ echo 0 >expect &&
+ max_chain pack-$pack.pack >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index f11742e..0916f76 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -191,6 +191,41 @@ test_expect_success 'fetch with pushInsteadOf (should not rewrite)' '
)
'
+grep_wrote () {
+ object_count=$1
+ file_name=$2
+ grep 'write_pack_file/wrote.*"value":"'$1'"' $2
+}
+
+test_expect_success 'push with negotiation' '
+ # Without negotiation
+ mk_empty testrepo &&
+ git push testrepo $the_first_commit:refs/remotes/origin/first_commit &&
+ git -C testrepo config receive.hideRefs refs/remotes/origin/first_commit &&
+ echo now pushing without negotiation &&
+ GIT_TRACE2_EVENT="$(pwd)/event" git -c protocol.version=2 push testrepo refs/heads/main:refs/remotes/origin/main &&
+ grep_wrote 5 event && # 2 commits, 2 trees, 1 blob
+
+ # Same commands, but with negotiation
+ rm event &&
+ mk_empty testrepo &&
+ git push testrepo $the_first_commit:refs/remotes/origin/first_commit &&
+ git -C testrepo config receive.hideRefs refs/remotes/origin/first_commit &&
+ GIT_TRACE2_EVENT="$(pwd)/event" git -c protocol.version=2 -c push.negotiate=1 push testrepo refs/heads/main:refs/remotes/origin/main &&
+ grep_wrote 2 event # 1 commit, 1 tree
+'
+
+test_expect_success 'push with negotiation proceeds anyway even if negotiation fails' '
+ rm event &&
+ mk_empty testrepo &&
+ git push testrepo $the_first_commit:refs/remotes/origin/first_commit &&
+ git -C testrepo config receive.hideRefs refs/remotes/origin/first_commit &&
+ GIT_TEST_PROTOCOL_VERSION=0 GIT_TRACE2_EVENT="$(pwd)/event" \
+ git -c push.negotiate=1 push testrepo refs/heads/main:refs/remotes/origin/main 2>err &&
+ grep_wrote 5 event && # 2 commits, 2 trees, 1 blob
+ test_i18ngrep "push negotiation failed" err
+'
+
test_expect_success 'push without wildcard' '
mk_empty testrepo &&
diff --git a/t/t5523-push-upstream.sh b/t/t5523-push-upstream.sh
index 9fbe7f7..fdb4292 100755
--- a/t/t5523-push-upstream.sh
+++ b/t/t5523-push-upstream.sh
@@ -119,4 +119,11 @@ test_expect_success TTY 'quiet push' '
test_must_be_empty output
'
+test_expect_success TTY 'quiet push -u' '
+ ensure_fresh_upstream &&
+
+ test_terminal git push --quiet -u --no-progress upstream main 2>&1 | tee output &&
+ test_must_be_empty output
+'
+
test_done
diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh
index 984dba2..4f87d90 100755
--- a/t/t5551-http-fetch-smart.sh
+++ b/t/t5551-http-fetch-smart.sh
@@ -517,4 +517,45 @@ test_expect_success 'server-side error detected' '
test_i18ngrep "server-side error" actual
'
+test_expect_success 'http auth remembers successful credentials' '
+ rm -f .git-credentials &&
+ test_config credential.helper store &&
+
+ # the first request prompts the user...
+ set_askpass user@host pass@host &&
+ git ls-remote "$HTTPD_URL/auth/smart/repo.git" >/dev/null &&
+ expect_askpass both user@host &&
+
+ # ...and the second one uses the stored value rather than
+ # prompting the user.
+ set_askpass bogus-user bogus-pass &&
+ git ls-remote "$HTTPD_URL/auth/smart/repo.git" >/dev/null &&
+ expect_askpass none
+'
+
+test_expect_success 'http auth forgets bogus credentials' '
+ # seed credential store with bogus values. In real life,
+ # this would probably come from a password which worked
+ # for a previous request.
+ rm -f .git-credentials &&
+ test_config credential.helper store &&
+ {
+ echo "url=$HTTPD_URL" &&
+ echo "username=bogus" &&
+ echo "password=bogus"
+ } | git credential approve &&
+
+ # we expect this to use the bogus values and fail, never even
+ # prompting the user...
+ set_askpass user@host pass@host &&
+ test_must_fail git ls-remote "$HTTPD_URL/auth/smart/repo.git" >/dev/null &&
+ expect_askpass none &&
+
+ # ...but now we should have forgotten the bad value, causing
+ # us to prompt the user again.
+ set_askpass user@host pass@host &&
+ git ls-remote "$HTTPD_URL/auth/smart/repo.git" >/dev/null &&
+ expect_askpass both user@host
+'
+
test_done
diff --git a/t/t5572-pull-submodule.sh b/t/t5572-pull-submodule.sh
index 29537f4..4f92a11 100755
--- a/t/t5572-pull-submodule.sh
+++ b/t/t5572-pull-submodule.sh
@@ -42,8 +42,11 @@ git_pull_noff () {
$2 git pull --no-ff
}
-KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
-KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES=1
+if test "$GIT_TEST_MERGE_ALGORITHM" != ort
+then
+ KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
+ KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES=1
+fi
test_submodule_switch_func "git_pull_noff"
test_expect_success 'pull --recurse-submodule setup' '
diff --git a/t/t5582-fetch-negative-refspec.sh b/t/t5582-fetch-negative-refspec.sh
index f345097..e5d2e79 100755
--- a/t/t5582-fetch-negative-refspec.sh
+++ b/t/t5582-fetch-negative-refspec.sh
@@ -240,4 +240,47 @@ test_expect_success "push with matching +: and negative refspec" '
git -C two push -v one
'
+test_expect_success '--prefetch correctly modifies refspecs' '
+ git -C one config --unset-all remote.origin.fetch &&
+ git -C one config --add remote.origin.fetch ^refs/heads/bogus/ignore &&
+ git -C one config --add remote.origin.fetch "refs/tags/*:refs/tags/*" &&
+ git -C one config --add remote.origin.fetch "refs/heads/bogus/*:bogus/*" &&
+
+ git tag -a -m never never-fetch-tag HEAD &&
+
+ git branch bogus/fetched HEAD~1 &&
+ git branch bogus/ignore HEAD &&
+
+ git -C one fetch --prefetch --no-tags &&
+ test_must_fail git -C one rev-parse never-fetch-tag &&
+ git -C one rev-parse refs/prefetch/bogus/fetched &&
+ test_must_fail git -C one rev-parse refs/prefetch/bogus/ignore &&
+
+ # correctly handle when refspec set becomes empty
+ # after removing the refs/tags/* refspec.
+ git -C one config --unset-all remote.origin.fetch &&
+ git -C one config --add remote.origin.fetch "refs/tags/*:refs/tags/*" &&
+
+ git -C one fetch --prefetch --no-tags &&
+ test_must_fail git -C one rev-parse never-fetch-tag &&
+
+ # The refspec for refs that are not fully qualified
+ # are filtered multiple times.
+ git -C one rev-parse refs/prefetch/bogus/fetched &&
+ test_must_fail git -C one rev-parse refs/prefetch/bogus/ignore
+'
+
+test_expect_success '--prefetch succeeds when refspec becomes empty' '
+ git checkout bogus/fetched &&
+ test_commit extra &&
+
+ git -C one config --unset-all remote.origin.fetch &&
+ git -C one config --unset branch.main.remote &&
+ git -C one config remote.origin.fetch "+refs/tags/extra" &&
+ git -C one config remote.origin.skipfetchall true &&
+ git -C one config remote.origin.tagopt "--no-tags" &&
+
+ git -C one fetch --prefetch
+'
+
test_done
diff --git a/t/t5600-clone-fail-cleanup.sh b/t/t5600-clone-fail-cleanup.sh
index 4a1a912..5bf1026 100755
--- a/t/t5600-clone-fail-cleanup.sh
+++ b/t/t5600-clone-fail-cleanup.sh
@@ -97,4 +97,11 @@ test_expect_success 'failed clone into empty leaves directory (separate, wt)' '
test_dir_is_empty empty-wt
'
+test_expect_success 'transport failure cleans up directory' '
+ test_must_fail git clone --no-local \
+ -u "f() { git-upload-pack \"\$@\"; return 1; }; f" \
+ foo broken-clone &&
+ test_path_is_missing broken-clone
+'
+
test_done
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index 329ae59..c068846 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -762,7 +762,7 @@ test_expect_success 'partial clone using HTTP' '
test_expect_success 'reject cloning shallow repository using HTTP' '
test_when_finished "rm -rf repo" &&
git clone --bare --no-local --depth=1 src "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
- test_must_fail git clone --reject-shallow $HTTPD_URL/smart/repo.git repo 2>err &&
+ test_must_fail git -c protocol.version=2 clone --reject-shallow $HTTPD_URL/smart/repo.git repo 2>err &&
test_i18ngrep -e "source repository is shallow, reject to clone." err &&
git clone --no-reject-shallow $HTTPD_URL/smart/repo.git repo
diff --git a/t/t5616-partial-clone.sh b/t/t5616-partial-clone.sh
index 5cb4153..cf3e82b 100755
--- a/t/t5616-partial-clone.sh
+++ b/t/t5616-partial-clone.sh
@@ -548,6 +548,14 @@ test_expect_success 'fetch from a partial clone, protocol v2' '
grep "version 2" trace
'
+test_expect_success 'repack does not loosen promisor objects' '
+ rm -rf client trace &&
+ git clone --bare --filter=blob:none "file://$(pwd)/srv.bare" client &&
+ test_when_finished "rm -rf client trace" &&
+ GIT_TRACE2_PERF="$(pwd)/trace" git -C client repack -A -d &&
+ grep "loosen_unused_packed_objects/loosened:0" trace
+'
+
. "$TEST_DIRECTORY"/lib-httpd.sh
start_httpd
diff --git a/t/t5701-git-serve.sh b/t/t5701-git-serve.sh
index 509f379..930721f 100755
--- a/t/t5701-git-serve.sh
+++ b/t/t5701-git-serve.sh
@@ -16,9 +16,10 @@ test_expect_success 'test capability advertisement' '
version 2
agent=git/$(git version | cut -d" " -f3)
ls-refs=unborn
- fetch=shallow
+ fetch=shallow wait-for-done
server-option
object-format=$(test_oid algo)
+ object-info
0000
EOF
@@ -240,4 +241,29 @@ test_expect_success 'unexpected lines are not allowed in fetch request' '
grep "unexpected line: .this-is-not-a-command." err
'
+# Test the basics of object-info
+#
+test_expect_success 'basics of object-info' '
+ test-tool pkt-line pack >in <<-EOF &&
+ command=object-info
+ object-format=$(test_oid algo)
+ 0001
+ size
+ oid $(git rev-parse two:two.t)
+ oid $(git rev-parse two:two.t)
+ 0000
+ EOF
+
+ cat >expect <<-EOF &&
+ size
+ $(git rev-parse two:two.t) $(wc -c <two.t | xargs)
+ $(git rev-parse two:two.t) $(wc -c <two.t | xargs)
+ 0000
+ EOF
+
+ test-tool serve-v2 --stateless-rpc <in >out &&
+ test-tool pkt-line unpack <out >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t5702-protocol-v2.sh b/t/t5702-protocol-v2.sh
index 2e1243c..66af411 100755
--- a/t/t5702-protocol-v2.sh
+++ b/t/t5702-protocol-v2.sh
@@ -585,6 +585,49 @@ test_expect_success 'deepen-relative' '
test_cmp expected actual
'
+setup_negotiate_only () {
+ SERVER="$1"
+ URI="$2"
+
+ rm -rf "$SERVER" client
+
+ git init "$SERVER"
+ test_commit -C "$SERVER" one
+ test_commit -C "$SERVER" two
+
+ git clone "$URI" client
+ test_commit -C client three
+}
+
+test_expect_success 'file:// --negotiate-only' '
+ SERVER="server" &&
+ URI="file://$(pwd)/server" &&
+
+ setup_negotiate_only "$SERVER" "$URI" &&
+
+ git -c protocol.version=2 -C client fetch \
+ --no-tags \
+ --negotiate-only \
+ --negotiation-tip=$(git -C client rev-parse HEAD) \
+ origin >out &&
+ COMMON=$(git -C "$SERVER" rev-parse two) &&
+ grep "$COMMON" out
+'
+
+test_expect_success 'file:// --negotiate-only with protocol v0' '
+ SERVER="server" &&
+ URI="file://$(pwd)/server" &&
+
+ setup_negotiate_only "$SERVER" "$URI" &&
+
+ test_must_fail git -c protocol.version=0 -C client fetch \
+ --no-tags \
+ --negotiate-only \
+ --negotiation-tip=$(git -C client rev-parse HEAD) \
+ origin 2>err &&
+ test_i18ngrep "negotiate-only requires protocol v2" err
+'
+
# Test protocol v2 with 'http://' transport
#
. "$TEST_DIRECTORY"/lib-httpd.sh
@@ -1035,6 +1078,52 @@ test_expect_success 'packfile-uri with transfer.fsckobjects fails when .gitmodul
test_i18ngrep "disallowed submodule name" err
'
+test_expect_success 'http:// --negotiate-only' '
+ SERVER="$HTTPD_DOCUMENT_ROOT_PATH/server" &&
+ URI="$HTTPD_URL/smart/server" &&
+
+ setup_negotiate_only "$SERVER" "$URI" &&
+
+ git -c protocol.version=2 -C client fetch \
+ --no-tags \
+ --negotiate-only \
+ --negotiation-tip=$(git -C client rev-parse HEAD) \
+ origin >out &&
+ COMMON=$(git -C "$SERVER" rev-parse two) &&
+ grep "$COMMON" out
+'
+
+test_expect_success 'http:// --negotiate-only without wait-for-done support' '
+ SERVER="server" &&
+ URI="$HTTPD_URL/one_time_perl/server" &&
+
+ setup_negotiate_only "$SERVER" "$URI" &&
+
+ echo "s/ wait-for-done/ xxxx-xxx-xxxx/" \
+ >"$HTTPD_ROOT_PATH/one-time-perl" &&
+
+ test_must_fail git -c protocol.version=2 -C client fetch \
+ --no-tags \
+ --negotiate-only \
+ --negotiation-tip=$(git -C client rev-parse HEAD) \
+ origin 2>err &&
+ test_i18ngrep "server does not support wait-for-done" err
+'
+
+test_expect_success 'http:// --negotiate-only with protocol v0' '
+ SERVER="$HTTPD_DOCUMENT_ROOT_PATH/server" &&
+ URI="$HTTPD_URL/smart/server" &&
+
+ setup_negotiate_only "$SERVER" "$URI" &&
+
+ test_must_fail git -c protocol.version=0 -C client fetch \
+ --no-tags \
+ --negotiate-only \
+ --negotiation-tip=$(git -C client rev-parse HEAD) \
+ origin 2>err &&
+ test_i18ngrep "negotiate-only requires protocol v2" err
+'
+
# DO NOT add non-httpd-specific tests here, because the last part of this
# test script is only executed when httpd is available and enabled.
diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh
index 32bb66e..a1baf4e 100755
--- a/t/t6030-bisect-porcelain.sh
+++ b/t/t6030-bisect-porcelain.sh
@@ -922,6 +922,17 @@ test_expect_success 'bisect start takes options and revs in any order' '
test_cmp expected actual
'
+# Bisect is started with --term-new and --term-old arguments,
+# then skip. The HEAD should be changed.
+test_expect_success 'bisect skip works with --term*' '
+ git bisect reset &&
+ git bisect start --term-new=fixed --term-old=unfixed HEAD $HASH1 &&
+ hash_skipped_from=$(git rev-parse --verify HEAD) &&
+ git bisect skip &&
+ hash_skipped_to=$(git rev-parse --verify HEAD) &&
+ test "$hash_skipped_from" != "$hash_skipped_to"
+'
+
test_expect_success 'git bisect reset cleans bisection state properly' '
git bisect reset &&
git bisect start &&
diff --git a/t/t6041-bisect-submodule.sh b/t/t6041-bisect-submodule.sh
index df1eff0..82013fc 100755
--- a/t/t6041-bisect-submodule.sh
+++ b/t/t6041-bisect-submodule.sh
@@ -8,7 +8,7 @@ test_description='bisect can handle submodules'
git_bisect () {
git status -su >expect &&
ls -1pR * >>expect &&
- tar cf "$TRASH_DIRECTORY/tmp.tar" * &&
+ "$TAR" cf "$TRASH_DIRECTORY/tmp.tar" * &&
GOOD=$(git rev-parse --verify HEAD) &&
may_only_be_test_must_fail "$2" &&
$2 git checkout "$1" &&
@@ -25,7 +25,7 @@ git_bisect () {
git bisect start &&
git bisect good $GOOD &&
rm -rf * &&
- tar xf "$TRASH_DIRECTORY/tmp.tar" &&
+ "$TAR" xf "$TRASH_DIRECTORY/tmp.tar" &&
git status -su >actual &&
ls -1pR * >>actual &&
test_cmp expect actual &&
diff --git a/t/t6112-rev-list-filters-objects.sh b/t/t6112-rev-list-filters-objects.sh
index 31457d1..4ade105 100755
--- a/t/t6112-rev-list-filters-objects.sh
+++ b/t/t6112-rev-list-filters-objects.sh
@@ -159,6 +159,78 @@ test_expect_success 'verify blob:limit=1m' '
test_must_be_empty observed
'
+# Test object:type=<type> filter.
+
+test_expect_success 'setup object-type' '
+ test_create_repo object-type &&
+ test_commit --no-tag -C object-type message blob &&
+ git -C object-type tag tag -m tag-message
+'
+
+test_expect_success 'verify object:type= fails with invalid type' '
+ test_must_fail git -C object-type rev-list --objects --filter=object:type= HEAD &&
+ test_must_fail git -C object-type rev-list --objects --filter=object:type=invalid HEAD
+'
+
+test_expect_success 'verify object:type=blob prints blob and commit' '
+ git -C object-type rev-parse HEAD >expected &&
+ printf "%s blob\n" $(git -C object-type rev-parse HEAD:blob) >>expected &&
+ git -C object-type rev-list --objects --filter=object:type=blob HEAD >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'verify object:type=tree prints tree and commit' '
+ (
+ git -C object-type rev-parse HEAD &&
+ printf "%s \n" $(git -C object-type rev-parse HEAD^{tree})
+ ) >expected &&
+ git -C object-type rev-list --objects --filter=object:type=tree HEAD >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'verify object:type=commit prints commit' '
+ git -C object-type rev-parse HEAD >expected &&
+ git -C object-type rev-list --objects --filter=object:type=commit HEAD >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'verify object:type=tag prints tag' '
+ (
+ git -C object-type rev-parse HEAD &&
+ printf "%s tag\n" $(git -C object-type rev-parse tag)
+ ) >expected &&
+ git -C object-type rev-list --objects --filter=object:type=tag tag >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'verify object:type=blob prints only blob with --filter-provided-objects' '
+ printf "%s blob\n" $(git -C object-type rev-parse HEAD:blob) >expected &&
+ git -C object-type rev-list --objects \
+ --filter=object:type=blob --filter-provided-objects HEAD >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'verify object:type=tree prints only tree with --filter-provided-objects' '
+ printf "%s \n" $(git -C object-type rev-parse HEAD^{tree}) >expected &&
+ git -C object-type rev-list --objects \
+ --filter=object:type=tree HEAD --filter-provided-objects >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'verify object:type=commit prints only commit with --filter-provided-objects' '
+ git -C object-type rev-parse HEAD >expected &&
+ git -C object-type rev-list --objects \
+ --filter=object:type=commit --filter-provided-objects HEAD >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'verify object:type=tag prints only tag with --filter-provided-objects' '
+ printf "%s tag\n" $(git -C object-type rev-parse tag) >expected &&
+ git -C object-type rev-list --objects \
+ --filter=object:type=tag --filter-provided-objects tag >actual &&
+ test_cmp expected actual
+'
+
# Test sparse:path=<path> filter.
# !!!!
# NOTE: sparse:path filter support has been dropped for security reasons,
diff --git a/t/t6113-rev-list-bitmap-filters.sh b/t/t6113-rev-list-bitmap-filters.sh
index 3f88994..4d8e091 100755
--- a/t/t6113-rev-list-bitmap-filters.sh
+++ b/t/t6113-rev-list-bitmap-filters.sh
@@ -10,7 +10,8 @@ test_expect_success 'set up bitmapped repo' '
test_commit much-larger-blob-one &&
git repack -adb &&
test_commit two &&
- test_commit much-larger-blob-two
+ test_commit much-larger-blob-two &&
+ git tag tag
'
test_expect_success 'filters fallback to non-bitmap traversal' '
@@ -75,4 +76,69 @@ test_expect_success 'tree:1 filter' '
test_cmp expect actual
'
+test_expect_success 'object:type filter' '
+ git rev-list --objects --filter=object:type=tag tag >expect &&
+ git rev-list --use-bitmap-index \
+ --objects --filter=object:type=tag tag >actual &&
+ test_cmp expect actual &&
+
+ git rev-list --objects --filter=object:type=commit tag >expect &&
+ git rev-list --use-bitmap-index \
+ --objects --filter=object:type=commit tag >actual &&
+ test_bitmap_traversal expect actual &&
+
+ git rev-list --objects --filter=object:type=tree tag >expect &&
+ git rev-list --use-bitmap-index \
+ --objects --filter=object:type=tree tag >actual &&
+ test_bitmap_traversal expect actual &&
+
+ git rev-list --objects --filter=object:type=blob tag >expect &&
+ git rev-list --use-bitmap-index \
+ --objects --filter=object:type=blob tag >actual &&
+ test_bitmap_traversal expect actual
+'
+
+test_expect_success 'object:type filter with --filter-provided-objects' '
+ git rev-list --objects --filter-provided-objects --filter=object:type=tag tag >expect &&
+ git rev-list --use-bitmap-index \
+ --objects --filter-provided-objects --filter=object:type=tag tag >actual &&
+ test_cmp expect actual &&
+
+ git rev-list --objects --filter-provided-objects --filter=object:type=commit tag >expect &&
+ git rev-list --use-bitmap-index \
+ --objects --filter-provided-objects --filter=object:type=commit tag >actual &&
+ test_bitmap_traversal expect actual &&
+
+ git rev-list --objects --filter-provided-objects --filter=object:type=tree tag >expect &&
+ git rev-list --use-bitmap-index \
+ --objects --filter-provided-objects --filter=object:type=tree tag >actual &&
+ test_bitmap_traversal expect actual &&
+
+ git rev-list --objects --filter-provided-objects --filter=object:type=blob tag >expect &&
+ git rev-list --use-bitmap-index \
+ --objects --filter-provided-objects --filter=object:type=blob tag >actual &&
+ test_bitmap_traversal expect actual
+'
+
+test_expect_success 'combine filter' '
+ git rev-list --objects --filter=blob:limit=1000 --filter=object:type=blob tag >expect &&
+ git rev-list --use-bitmap-index \
+ --objects --filter=blob:limit=1000 --filter=object:type=blob tag >actual &&
+ test_bitmap_traversal expect actual
+'
+
+test_expect_success 'combine filter with --filter-provided-objects' '
+ git rev-list --objects --filter-provided-objects --filter=blob:limit=1000 --filter=object:type=blob tag >expect &&
+ git rev-list --use-bitmap-index \
+ --objects --filter-provided-objects --filter=blob:limit=1000 --filter=object:type=blob tag >actual &&
+ test_bitmap_traversal expect actual &&
+
+ git cat-file --batch-check="%(objecttype) %(objectsize)" <actual >objects &&
+ while read objecttype objectsize
+ do
+ test "$objecttype" = blob || return 1
+ test "$objectsize" -le 1000 || return 1
+ done <objects
+'
+
test_done
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index cac7f44..9e02140 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -945,9 +945,9 @@ test_failing_trailer_option () {
test_expect_success "$title" '
# error message cannot be checked under i18n
test_must_fail git for-each-ref --format="%($option)" refs/heads/main 2>actual &&
- test_i18ncmp expect actual &&
+ test_cmp expect actual &&
test_must_fail git for-each-ref --format="%(contents:$option)" refs/heads/main 2>actual &&
- test_i18ncmp expect actual
+ test_cmp expect actual
'
}
@@ -966,7 +966,7 @@ test_expect_success 'if arguments, %(contents:trailers) shows error if colon is
fatal: unrecognized %(contents) argument: trailersonly
EOF
test_must_fail git for-each-ref --format="%(contents:trailersonly)" 2>actual &&
- test_i18ncmp expect actual
+ test_cmp expect actual
'
test_expect_success 'basic atom: head contents:trailers' '
@@ -1134,4 +1134,14 @@ test_expect_success 'for-each-ref --ignore-case works on multiple sort keys' '
test_cmp expect actual
'
+test_expect_success 'for-each-ref reports broken tags' '
+ git tag -m "good tag" broken-tag-good HEAD &&
+ git cat-file tag broken-tag-good >good &&
+ sed s/commit/blob/ <good >bad &&
+ bad=$(git hash-object -w -t tag bad) &&
+ git update-ref refs/tags/broken-tag-bad $bad &&
+ test_must_fail git for-each-ref --format="%(*objectname)" \
+ refs/tags/broken-tag-*
+'
+
test_done
diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh
index 9866b1b..1537aa2 100755
--- a/t/t6302-for-each-ref-filter.sh
+++ b/t/t6302-for-each-ref-filter.sh
@@ -117,6 +117,25 @@ test_expect_success '%(color) must fail' '
test_must_fail git for-each-ref --format="%(color)%(refname)"
'
+test_expect_success '%(color:#aa22ac) must succeed' '
+ test_when_finished rm -rf test &&
+ git init test &&
+ (
+ cd test &&
+ test_commit initial &&
+ git branch -M main &&
+ cat >expect <<-\EOF &&
+ refs/heads/main
+ refs/tags/initial
+ EOF
+ git remote add origin nowhere &&
+ git config branch.main.remote origin &&
+ git config branch.main.merge refs/heads/main &&
+ git for-each-ref --format="%(color:#aa22ac)%(refname)" >actual &&
+ test_cmp expect actual
+ )
+'
+
test_expect_success 'left alignment is default' '
cat >expect <<-\EOF &&
refname is refs/heads/main |refs/heads/main
diff --git a/t/t6423-merge-rename-directories.sh b/t/t6423-merge-rename-directories.sh
index 379aac0..be84d22 100755
--- a/t/t6423-merge-rename-directories.sh
+++ b/t/t6423-merge-rename-directories.sh
@@ -4797,7 +4797,7 @@ test_setup_12f () {
)
}
-test_expect_merge_algorithm failure success '12f: Trivial directory resolve, caching, all kinds of fun' '
+test_expect_merge_algorithm failure failure '12f: Trivial directory resolve, caching, all kinds of fun' '
test_setup_12f &&
(
cd 12f &&
@@ -4966,6 +4966,64 @@ test_expect_success '12g: Testcase with two kinds of "relevant" renames' '
)
'
+# Testcase 12h, Testcase with two kinds of "relevant" renames
+# Commit O: olddir/{a_1, b}
+# Commit A: newdir/{a_2, b}
+# Commit B: olddir/{alpha_1, b}
+# Expected: newdir/{alpha_2, b}
+
+test_setup_12h () {
+ test_create_repo 12h &&
+ (
+ cd 12h &&
+
+ mkdir olddir &&
+ test_seq 3 8 >olddir/a &&
+ >olddir/b &&
+ git add olddir &&
+ git commit -m orig &&
+
+ git branch O &&
+ git branch A &&
+ git branch B &&
+
+ git switch A &&
+ test_seq 3 10 >olddir/a &&
+ git add olddir/a &&
+ git mv olddir newdir &&
+ git commit -m A &&
+
+ git switch B &&
+
+ git mv olddir/a olddir/alpha &&
+ git commit -m B
+ )
+}
+
+test_expect_failure '12h: renaming a file within a renamed directory' '
+ test_setup_12h &&
+ (
+ cd 12h &&
+
+ git checkout A^0 &&
+
+ test_might_fail git -c merge.directoryRenames=true merge -s recursive B^0 &&
+
+ git ls-files >tracked &&
+ test_line_count = 2 tracked &&
+
+ test_path_is_missing olddir/a &&
+ test_path_is_file newdir/alpha &&
+ test_path_is_file newdir/b &&
+
+ git rev-parse >actual \
+ HEAD:newdir/alpha HEAD:newdir/b &&
+ git rev-parse >expect \
+ A:newdir/a O:oldir/b &&
+ test_cmp expect actual
+ )
+'
+
###########################################################################
# SECTION 13: Checking informational and conflict messages
#
diff --git a/t/t6428-merge-conflicts-sparse.sh b/t/t6428-merge-conflicts-sparse.sh
new file mode 100755
index 0000000..7e8bf49
--- /dev/null
+++ b/t/t6428-merge-conflicts-sparse.sh
@@ -0,0 +1,158 @@
+#!/bin/sh
+
+test_description="merge cases"
+
+# The setup for all of them, pictorially, is:
+#
+# A
+# o
+# / \
+# O o ?
+# \ /
+# o
+# B
+#
+# To help make it easier to follow the flow of tests, they have been
+# divided into sections and each test will start with a quick explanation
+# of what commits O, A, and B contain.
+#
+# Notation:
+# z/{b,c} means files z/b and z/c both exist
+# x/d_1 means file x/d exists with content d1. (Purpose of the
+# underscore notation is to differentiate different
+# files that might be renamed into each other's paths.)
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-merge.sh
+
+
+# Testcase basic, conflicting changes in 'numerals'
+
+test_setup_numerals () {
+ test_create_repo numerals_$1 &&
+ (
+ cd numerals_$1 &&
+
+ >README &&
+ test_write_lines I II III >numerals &&
+ git add README numerals &&
+ test_tick &&
+ git commit -m "O" &&
+
+ git branch O &&
+ git branch A &&
+ git branch B &&
+
+ git checkout A &&
+ test_write_lines I II III IIII >numerals &&
+ git add numerals &&
+ test_tick &&
+ git commit -m "A" &&
+
+ git checkout B &&
+ test_write_lines I II III IV >numerals &&
+ git add numerals &&
+ test_tick &&
+ git commit -m "B" &&
+
+ cat <<-EOF >expected-index &&
+ H README
+ M numerals
+ M numerals
+ M numerals
+ EOF
+
+ cat <<-EOF >expected-merge
+ I
+ II
+ III
+ <<<<<<< HEAD
+ IIII
+ =======
+ IV
+ >>>>>>> B^0
+ EOF
+
+ )
+}
+
+test_expect_success 'conflicting entries written to worktree even if sparse' '
+ test_setup_numerals plain &&
+ (
+ cd numerals_plain &&
+
+ git checkout A^0 &&
+
+ test_path_is_file README &&
+ test_path_is_file numerals &&
+
+ git sparse-checkout init &&
+ git sparse-checkout set README &&
+
+ test_path_is_file README &&
+ test_path_is_missing numerals &&
+
+ test_must_fail git merge -s recursive B^0 &&
+
+ git ls-files -t >index_files &&
+ test_cmp expected-index index_files &&
+
+ test_path_is_file README &&
+ test_path_is_file numerals &&
+
+ test_cmp expected-merge numerals &&
+
+ # 4 other files:
+ # * expected-merge
+ # * expected-index
+ # * index_files
+ # * others
+ git ls-files -o >others &&
+ test_line_count = 4 others
+ )
+'
+
+test_expect_merge_algorithm failure success 'present-despite-SKIP_WORKTREE handled reasonably' '
+ test_setup_numerals in_the_way &&
+ (
+ cd numerals_in_the_way &&
+
+ git checkout A^0 &&
+
+ test_path_is_file README &&
+ test_path_is_file numerals &&
+
+ git sparse-checkout init &&
+ git sparse-checkout set README &&
+
+ test_path_is_file README &&
+ test_path_is_missing numerals &&
+
+ echo foobar >numerals &&
+
+ test_must_fail git merge -s recursive B^0 &&
+
+ git ls-files -t >index_files &&
+ test_cmp expected-index index_files &&
+
+ test_path_is_file README &&
+ test_path_is_file numerals &&
+
+ test_cmp expected-merge numerals &&
+
+ # There should still be a file with "foobar" in it
+ grep foobar * &&
+
+ # 5 other files:
+ # * expected-merge
+ # * expected-index
+ # * index_files
+ # * others
+ # * whatever name was given to the numerals file that had
+ # "foobar" in it
+ git ls-files -o >others &&
+ test_line_count = 5 others
+ )
+'
+
+test_done
diff --git a/t/t6429-merge-sequence-rename-caching.sh b/t/t6429-merge-sequence-rename-caching.sh
new file mode 100755
index 0000000..035edc4
--- /dev/null
+++ b/t/t6429-merge-sequence-rename-caching.sh
@@ -0,0 +1,700 @@
+#!/bin/sh
+
+test_description="remember regular & dir renames in sequence of merges"
+
+. ./test-lib.sh
+
+#
+# NOTE 1: this testfile tends to not only rename files, but modify on both
+# sides; without modifying on both sides, optimizations can kick in
+# which make rename detection irrelevant or trivial. We want to make
+# sure that we are triggering rename caching rather than rename
+# bypassing.
+#
+# NOTE 2: this testfile uses 'test-tool fast-rebase' instead of either
+# cherry-pick or rebase. sequencer.c is only superficially
+# integrated with merge-ort; it calls merge_switch_to_result()
+# after EACH merge, which updates the index and working copy AND
+# throws away the cached results (because merge_switch_to_result()
+# is only supposed to be called at the end of the sequence).
+# Integrating them more deeply is a big task, so for now the tests
+# use 'test-tool fast-rebase'.
+#
+
+
+#
+# In the following simple testcase:
+# Base: numbers_1, values_1
+# Upstream: numbers_2, values_2
+# Topic_1: sequence_3
+# Topic_2: scruples_3
+# or, in english, rename numbers -> sequence in the first commit, and rename
+# values -> scruples in the second commit.
+#
+# This shouldn't be a challenge, it's just verifying that cached renames isn't
+# preventing us from finding new renames.
+#
+test_expect_success 'caching renames does not preclude finding new ones' '
+ test_create_repo caching-renames-and-new-renames &&
+ (
+ cd caching-renames-and-new-renames &&
+
+ test_seq 2 10 >numbers &&
+ test_seq 2 10 >values &&
+ git add numbers values &&
+ git commit -m orig &&
+
+ git branch upstream &&
+ git branch topic &&
+
+ git switch upstream &&
+ test_seq 1 10 >numbers &&
+ test_seq 1 10 >values &&
+ git add numbers values &&
+ git commit -m "Tweaked both files" &&
+
+ git switch topic &&
+
+ test_seq 2 12 >numbers &&
+ git add numbers &&
+ git mv numbers sequence &&
+ git commit -m A &&
+
+ test_seq 2 12 >values &&
+ git add values &&
+ git mv values scruples &&
+ git commit -m B &&
+
+ #
+ # Actual testing
+ #
+
+ git switch upstream &&
+
+ test-tool fast-rebase --onto HEAD upstream~1 topic &&
+ #git cherry-pick upstream~1..topic
+
+ git ls-files >tracked-files &&
+ test_line_count = 2 tracked-files &&
+ test_seq 1 12 >expect &&
+ test_cmp expect sequence &&
+ test_cmp expect scruples
+ )
+'
+
+#
+# In the following testcase:
+# Base: numbers_1
+# Upstream: rename numbers_1 -> sequence_2
+# Topic_1: numbers_3
+# Topic_2: numbers_1
+# or, in english, the first commit on the topic branch modifies numbers by
+# shrinking it (dramatically) and the second commit on topic reverts its
+# parent.
+#
+# Can git apply both patches?
+#
+# Traditional cherry-pick/rebase will fail to apply the second commit, the
+# one that reverted its parent, because despite detecting the rename from
+# 'numbers' to 'sequence' for the first commit, it fails to detect that
+# rename when picking the second commit. That's "reasonable" given the
+# dramatic change in size of the file, but remembering the rename and
+# reusing it is reasonable too.
+#
+# We do test here that we expect rename detection to only be run once total
+# (the topic side of history doesn't need renames, and with caching we
+# should be able to only run rename detection on the upstream side one
+# time.)
+test_expect_success 'cherry-pick both a commit and its immediate revert' '
+ test_create_repo pick-commit-and-its-immediate-revert &&
+ (
+ cd pick-commit-and-its-immediate-revert &&
+
+ test_seq 11 30 >numbers &&
+ git add numbers &&
+ git commit -m orig &&
+
+ git branch upstream &&
+ git branch topic &&
+
+ git switch upstream &&
+ test_seq 1 30 >numbers &&
+ git add numbers &&
+ git mv numbers sequence &&
+ git commit -m "Renamed (and modified) numbers -> sequence" &&
+
+ git switch topic &&
+
+ test_seq 11 13 >numbers &&
+ git add numbers &&
+ git commit -m A &&
+
+ git revert HEAD &&
+
+ #
+ # Actual testing
+ #
+
+ git switch upstream &&
+
+ GIT_TRACE2_PERF="$(pwd)/trace.output" &&
+ export GIT_TRACE2_PERF &&
+
+ test-tool fast-rebase --onto HEAD upstream~1 topic &&
+ #git cherry-pick upstream~1..topic &&
+
+ grep region_enter.*diffcore_rename trace.output >calls &&
+ test_line_count = 1 calls
+ )
+'
+
+#
+# In the following testcase:
+# Base: sequence_1
+# Upstream: rename sequence_1 -> values_2
+# Topic_1: rename sequence_1 -> values_3
+# Topic_2: add unrelated sequence_4
+# or, in english, both sides rename sequence -> values, and then the second
+# commit on the topic branch adds an unrelated file called sequence.
+#
+# This testcase presents no problems for git traditionally, but having both
+# sides do the same rename in effect "uses it up" and if it remains cached,
+# could cause a spurious rename/add conflict.
+#
+test_expect_success 'rename same file identically, then reintroduce it' '
+ test_create_repo rename-rename-1to1-then-add-old-filename &&
+ (
+ cd rename-rename-1to1-then-add-old-filename &&
+
+ test_seq 3 8 >sequence &&
+ git add sequence &&
+ git commit -m orig &&
+
+ git branch upstream &&
+ git branch topic &&
+
+ git switch upstream &&
+ test_seq 1 8 >sequence &&
+ git add sequence &&
+ git mv sequence values &&
+ git commit -m "Renamed (and modified) sequence -> values" &&
+
+ git switch topic &&
+
+ test_seq 3 10 >sequence &&
+ git add sequence &&
+ git mv sequence values &&
+ git commit -m A &&
+
+ test_write_lines A B C D E F G H I J >sequence &&
+ git add sequence &&
+ git commit -m B &&
+
+ #
+ # Actual testing
+ #
+
+ git switch upstream &&
+
+ GIT_TRACE2_PERF="$(pwd)/trace.output" &&
+ export GIT_TRACE2_PERF &&
+
+ test-tool fast-rebase --onto HEAD upstream~1 topic &&
+ #git cherry-pick upstream~1..topic &&
+
+ git ls-files >tracked &&
+ test_line_count = 2 tracked &&
+ test_path_is_file values &&
+ test_path_is_file sequence &&
+
+ grep region_enter.*diffcore_rename trace.output >calls &&
+ test_line_count = 2 calls
+ )
+'
+
+#
+# In the following testcase:
+# Base: olddir/{valuesZ_1, valuesY_1, valuesX_1}
+# Upstream: rename olddir/valuesZ_1 -> dirA/valuesZ_2
+# rename olddir/valuesY_1 -> dirA/valuesY_2
+# rename olddir/valuesX_1 -> dirB/valuesX_2
+# Topic_1: rename olddir/valuesZ_1 -> dirA/valuesZ_3
+# rename olddir/valuesY_1 -> dirA/valuesY_3
+# Topic_2: add olddir/newfile
+# Expected Pick1: dirA/{valuesZ, valuesY}, dirB/valuesX
+# Expected Pick2: dirA/{valuesZ, valuesY}, dirB/{valuesX, newfile}
+#
+# This testcase presents no problems for git traditionally, but having both
+# sides do the same renames in effect "use it up" but if the renames remain
+# cached, the directory rename could put newfile in the wrong directory.
+#
+test_expect_success 'rename same file identically, then add file to old dir' '
+ test_create_repo rename-rename-1to1-then-add-file-to-old-dir &&
+ (
+ cd rename-rename-1to1-then-add-file-to-old-dir &&
+
+ mkdir olddir/ &&
+ test_seq 3 8 >olddir/valuesZ &&
+ test_seq 3 8 >olddir/valuesY &&
+ test_seq 3 8 >olddir/valuesX &&
+ git add olddir &&
+ git commit -m orig &&
+
+ git branch upstream &&
+ git branch topic &&
+
+ git switch upstream &&
+ test_seq 1 8 >olddir/valuesZ &&
+ test_seq 1 8 >olddir/valuesY &&
+ test_seq 1 8 >olddir/valuesX &&
+ git add olddir &&
+ mkdir dirA &&
+ git mv olddir/valuesZ olddir/valuesY dirA &&
+ git mv olddir/ dirB/ &&
+ git commit -m "Renamed (and modified) values*" &&
+
+ git switch topic &&
+
+ test_seq 3 10 >olddir/valuesZ &&
+ test_seq 3 10 >olddir/valuesY &&
+ git add olddir &&
+ mkdir dirA &&
+ git mv olddir/valuesZ olddir/valuesY dirA &&
+ git commit -m A &&
+
+ >olddir/newfile &&
+ git add olddir/newfile &&
+ git commit -m B &&
+
+ #
+ # Actual testing
+ #
+
+ git switch upstream &&
+ git config merge.directoryRenames true &&
+
+ GIT_TRACE2_PERF="$(pwd)/trace.output" &&
+ export GIT_TRACE2_PERF &&
+
+ test-tool fast-rebase --onto HEAD upstream~1 topic &&
+ #git cherry-pick upstream~1..topic &&
+
+ git ls-files >tracked &&
+ test_line_count = 4 tracked &&
+ test_path_is_file dirA/valuesZ &&
+ test_path_is_file dirA/valuesY &&
+ test_path_is_file dirB/valuesX &&
+ test_path_is_file dirB/newfile &&
+
+ grep region_enter.*diffcore_rename trace.output >calls &&
+ test_line_count = 3 calls
+ )
+'
+
+#
+# In the following testcase, upstream renames a directory, and the topic branch
+# first adds a file to the directory, then later renames the directory
+# differently:
+# Base: olddir/a
+# olddir/b
+# Upstream: rename olddir/ -> newdir/
+# Topic_1: add olddir/newfile
+# Topic_2: rename olddir/ -> otherdir/
+#
+# Here we are just concerned that cached renames might prevent us from seeing
+# the rename conflict, and we want to ensure that we do get a conflict.
+#
+# While at it, though, we do test that we only try to detect renames 2
+# times and not three. (The first merge needs to detect renames on the
+# upstream side. Traditionally, the second merge would need to detect
+# renames on both sides of history, but our caching of upstream renames
+# should avoid the need to re-detect upstream renames.)
+#
+test_expect_success 'cached dir rename does not prevent noticing later conflict' '
+ test_create_repo dir-rename-cache-not-occluding-later-conflict &&
+ (
+ cd dir-rename-cache-not-occluding-later-conflict &&
+
+ mkdir olddir &&
+ test_seq 3 10 >olddir/a &&
+ test_seq 3 10 >olddir/b &&
+ git add olddir &&
+ git commit -m orig &&
+
+ git branch upstream &&
+ git branch topic &&
+
+ git switch upstream &&
+ test_seq 3 10 >olddir/a &&
+ test_seq 3 10 >olddir/b &&
+ git add olddir &&
+ git mv olddir newdir &&
+ git commit -m "Dir renamed" &&
+
+ git switch topic &&
+
+ >olddir/newfile &&
+ git add olddir/newfile &&
+ git commit -m A &&
+
+ test_seq 1 8 >olddir/a &&
+ test_seq 1 8 >olddir/b &&
+ git add olddir &&
+ git mv olddir otherdir &&
+ git commit -m B &&
+
+ #
+ # Actual testing
+ #
+
+ git switch upstream &&
+ git config merge.directoryRenames true &&
+
+ GIT_TRACE2_PERF="$(pwd)/trace.output" &&
+ export GIT_TRACE2_PERF &&
+
+ test_must_fail test-tool fast-rebase --onto HEAD upstream~1 topic >output &&
+ #git cherry-pick upstream..topic &&
+
+ grep CONFLICT..rename/rename output &&
+
+ grep region_enter.*diffcore_rename trace.output >calls &&
+ test_line_count = 2 calls
+ )
+'
+
+# Helper for the next two tests
+test_setup_upstream_rename () {
+ test_create_repo $1 &&
+ (
+ cd $1 &&
+
+ test_seq 3 8 >somefile &&
+ test_seq 3 8 >relevant-rename &&
+ git add somefile relevant-rename &&
+ mkdir olddir &&
+ test_write_lines a b c d e f g >olddir/a &&
+ test_write_lines z y x w v u t >olddir/b &&
+ git add olddir &&
+ git commit -m orig &&
+
+ git branch upstream &&
+ git branch topic &&
+
+ git switch upstream &&
+ test_seq 1 8 >somefile &&
+ test_seq 1 8 >relevant-rename &&
+ git add somefile relevant-rename &&
+ git mv relevant-rename renamed &&
+ echo h >>olddir/a &&
+ echo s >>olddir/b &&
+ git add olddir &&
+ git mv olddir newdir &&
+ git commit -m "Dir renamed"
+ )
+}
+
+#
+# In the following testcase, upstream renames a file in the toplevel directory
+# as well as its only directory:
+# Base: relevant-rename_1
+# somefile
+# olddir/a
+# olddir/b
+# Upstream: rename relevant-rename_1 -> renamed_2
+# rename olddir/ -> newdir/
+# Topic_1: relevant-rename_3
+# Topic_2: olddir/newfile_1
+# Topic_3: olddir/newfile_2
+#
+# In this testcase, since the first commit being picked only modifies a
+# file in the toplevel directory, the directory rename is irrelevant for
+# that first merge. However, we need to notice the directory rename for
+# the merge that picks the second commit, and we don't want the third
+# commit to mess up its location either. We want to make sure that
+# olddir/newfile doesn't exist in the result and that newdir/newfile does.
+#
+# We also test that we only do rename detection twice. We never need
+# rename detection on the topic side of history, but we do need it twice on
+# the upstream side of history. For the first topic commit, we only need
+# the
+# relevant-rename -> renamed
+# rename, because olddir is unmodified by Topic_1. For Topic_2, however,
+# the new file being added to olddir means files that were previously
+# irrelevant for rename detection are now relevant, forcing us to repeat
+# rename detection for the paths we don't already have cached. Topic_3 also
+# tweaks olddir/newfile, but the renames in olddir/ will have been cached
+# from the second rename detection run.
+#
+test_expect_success 'dir rename unneeded, then add new file to old dir' '
+ test_setup_upstream_rename dir-rename-unneeded-until-new-file &&
+ (
+ cd dir-rename-unneeded-until-new-file &&
+
+ git switch topic &&
+
+ test_seq 3 10 >relevant-rename &&
+ git add relevant-rename &&
+ git commit -m A &&
+
+ echo foo >olddir/newfile &&
+ git add olddir/newfile &&
+ git commit -m B &&
+
+ echo bar >>olddir/newfile &&
+ git add olddir/newfile &&
+ git commit -m C &&
+
+ #
+ # Actual testing
+ #
+
+ git switch upstream &&
+ git config merge.directoryRenames true &&
+
+ GIT_TRACE2_PERF="$(pwd)/trace.output" &&
+ export GIT_TRACE2_PERF &&
+
+ test-tool fast-rebase --onto HEAD upstream~1 topic &&
+ #git cherry-pick upstream..topic &&
+
+ grep region_enter.*diffcore_rename trace.output >calls &&
+ test_line_count = 2 calls &&
+
+ git ls-files >tracked &&
+ test_line_count = 5 tracked &&
+ test_path_is_missing olddir/newfile &&
+ test_path_is_file newdir/newfile
+ )
+'
+
+#
+# The following testcase is *very* similar to the last one, but instead of
+# adding a new olddir/newfile, it renames somefile -> olddir/newfile:
+# Base: relevant-rename_1
+# somefile_1
+# olddir/a
+# olddir/b
+# Upstream: rename relevant-rename_1 -> renamed_2
+# rename olddir/ -> newdir/
+# Topic_1: relevant-rename_3
+# Topic_2: rename somefile -> olddir/newfile_2
+# Topic_3: modify olddir/newfile_3
+#
+# In this testcase, since the first commit being picked only modifies a
+# file in the toplevel directory, the directory rename is irrelevant for
+# that first merge. However, we need to notice the directory rename for
+# the merge that picks the second commit, and we don't want the third
+# commit to mess up its location either. We want to make sure that
+# neither somefile or olddir/newfile exists in the result and that
+# newdir/newfile does.
+#
+# This testcase needs one more call to rename detection than the last
+# testcase, because of the somefile -> olddir/newfile rename in Topic_2.
+test_expect_success 'dir rename unneeded, then rename existing file into old dir' '
+ test_setup_upstream_rename dir-rename-unneeded-until-file-moved-inside &&
+ (
+ cd dir-rename-unneeded-until-file-moved-inside &&
+
+ git switch topic &&
+
+ test_seq 3 10 >relevant-rename &&
+ git add relevant-rename &&
+ git commit -m A &&
+
+ test_seq 1 10 >somefile &&
+ git add somefile &&
+ git mv somefile olddir/newfile &&
+ git commit -m B &&
+
+ test_seq 1 12 >olddir/newfile &&
+ git add olddir/newfile &&
+ git commit -m C &&
+
+ #
+ # Actual testing
+ #
+
+ git switch upstream &&
+ git config merge.directoryRenames true &&
+
+ GIT_TRACE2_PERF="$(pwd)/trace.output" &&
+ export GIT_TRACE2_PERF &&
+
+ test-tool fast-rebase --onto HEAD upstream~1 topic &&
+ #git cherry-pick upstream..topic &&
+
+ grep region_enter.*diffcore_rename trace.output >calls &&
+ test_line_count = 3 calls &&
+
+ test_path_is_missing somefile &&
+ test_path_is_missing olddir/newfile &&
+ test_path_is_file newdir/newfile &&
+ git ls-files >tracked &&
+ test_line_count = 4 tracked
+ )
+'
+
+# Helper for the next two tests
+test_setup_topic_rename () {
+ test_create_repo $1 &&
+ (
+ cd $1 &&
+
+ test_seq 3 8 >somefile &&
+ mkdir olddir &&
+ test_seq 3 8 >olddir/a &&
+ echo b >olddir/b &&
+ git add olddir somefile &&
+ git commit -m orig &&
+
+ git branch upstream &&
+ git branch topic &&
+
+ git switch topic &&
+ test_seq 1 8 >somefile &&
+ test_seq 1 8 >olddir/a &&
+ git add somefile olddir/a &&
+ git mv olddir newdir &&
+ git commit -m "Dir renamed" &&
+
+ test_seq 1 10 >somefile &&
+ git add somefile &&
+ mkdir olddir &&
+ >olddir/unrelated-file &&
+ git add olddir &&
+ git commit -m "Unrelated file in recreated old dir"
+ )
+}
+
+#
+# In the following testcase, the first commit on the topic branch renames
+# a directory, while the second recreates the old directory and places a
+# file into it:
+# Base: somefile
+# olddir/a
+# olddir/b
+# Upstream: olddir/newfile
+# Topic_1: somefile_2
+# rename olddir/ -> newdir/
+# Topic_2: olddir/unrelated-file
+#
+# Note that the first pick should merge:
+# Base: somefile
+# olddir/{a,b}
+# Upstream: olddir/newfile
+# Topic_1: rename olddir/ -> newdir/
+# For which the expected result (assuming merge.directoryRenames=true) is
+# clearly:
+# Result: somefile
+# newdir/{a, b, newfile}
+#
+# While the second pick does the following three-way merge:
+# Base (Topic_1): somefile
+# newdir/{a,b}
+# Upstream (Result from 1): same files as base, but adds newdir/newfile
+# Topic_2: same files as base, but adds olddir/unrelated-file
+#
+# The second merge is pretty trivial; upstream adds newdir/newfile, and
+# topic_2 adds olddir/unrelated-file. We're just testing that we don't
+# accidentally cache directory renames somehow and rename
+# olddir/unrelated-file to newdir/unrelated-file.
+#
+# This testcase should only need one call to diffcore_rename_extended().
+test_expect_success 'caching renames only on upstream side, part 1' '
+ test_setup_topic_rename cache-renames-only-upstream-add-file &&
+ (
+ cd cache-renames-only-upstream-add-file &&
+
+ git switch upstream &&
+
+ >olddir/newfile &&
+ git add olddir/newfile &&
+ git commit -m "Add newfile" &&
+
+ #
+ # Actual testing
+ #
+
+ git switch upstream &&
+
+ git config merge.directoryRenames true &&
+
+ GIT_TRACE2_PERF="$(pwd)/trace.output" &&
+ export GIT_TRACE2_PERF &&
+
+ test-tool fast-rebase --onto HEAD upstream~1 topic &&
+ #git cherry-pick upstream..topic &&
+
+ grep region_enter.*diffcore_rename trace.output >calls &&
+ test_line_count = 1 calls &&
+
+ git ls-files >tracked &&
+ test_line_count = 5 tracked &&
+ test_path_is_missing newdir/unrelated-file &&
+ test_path_is_file olddir/unrelated-file &&
+ test_path_is_file newdir/newfile &&
+ test_path_is_file newdir/b &&
+ test_path_is_file newdir/a &&
+ test_path_is_file somefile
+ )
+'
+
+#
+# The following testcase is *very* similar to the last one, but instead of
+# adding a new olddir/newfile, it renames somefile -> olddir/newfile:
+# Base: somefile
+# olddir/a
+# olddir/b
+# Upstream: somefile_1 -> olddir/newfile
+# Topic_1: rename olddir/ -> newdir/
+# somefile_2
+# Topic_2: olddir/unrelated-file
+# somefile_3
+#
+# Much like the previous test, this case is actually trivial and we are just
+# making sure there isn't some spurious directory rename caching going on
+# for the wrong side of history.
+#
+#
+# This testcase should only need two calls to diffcore_rename_extended(),
+# both for the first merge, one for each side of history.
+#
+test_expect_success 'caching renames only on upstream side, part 2' '
+ test_setup_topic_rename cache-renames-only-upstream-rename-file &&
+ (
+ cd cache-renames-only-upstream-rename-file &&
+
+ git switch upstream &&
+
+ git mv somefile olddir/newfile &&
+ git commit -m "Add newfile" &&
+
+ #
+ # Actual testing
+ #
+
+ git switch upstream &&
+
+ git config merge.directoryRenames true &&
+
+ GIT_TRACE2_PERF="$(pwd)/trace.output" &&
+ export GIT_TRACE2_PERF &&
+
+ test-tool fast-rebase --onto HEAD upstream~1 topic &&
+ #git cherry-pick upstream..topic &&
+
+ grep region_enter.*diffcore_rename trace.output >calls &&
+ test_line_count = 2 calls &&
+
+ git ls-files >tracked &&
+ test_line_count = 4 tracked &&
+ test_path_is_missing newdir/unrelated-file &&
+ test_path_is_file olddir/unrelated-file &&
+ test_path_is_file newdir/newfile &&
+ test_path_is_file newdir/b &&
+ test_path_is_file newdir/a
+ )
+'
+
+test_done
diff --git a/t/t6437-submodule-merge.sh b/t/t6437-submodule-merge.sh
index 0f92bcf..e5e89c2 100755
--- a/t/t6437-submodule-merge.sh
+++ b/t/t6437-submodule-merge.sh
@@ -6,6 +6,7 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-merge.sh
#
# history
@@ -328,7 +329,7 @@ test_expect_success 'setup file/submodule conflict' '
)
'
-test_expect_failure 'file/submodule conflict' '
+test_expect_merge_algorithm failure success 'file/submodule conflict' '
test_when_finished "git -C file-submodule reset --hard" &&
(
cd file-submodule &&
@@ -437,7 +438,7 @@ test_expect_failure 'directory/submodule conflict; keep submodule clean' '
)
'
-test_expect_failure !FAIL_PREREQS 'directory/submodule conflict; should not treat submodule files as untracked or in the way' '
+test_expect_merge_algorithm failure success !FAIL_PREREQS 'directory/submodule conflict; should not treat submodule files as untracked or in the way' '
test_when_finished "git -C directory-submodule/path reset --hard" &&
test_when_finished "git -C directory-submodule reset --hard" &&
(
diff --git a/t/t6438-submodule-directory-file-conflicts.sh b/t/t6438-submodule-directory-file-conflicts.sh
index 04bf4be..8df67a0 100755
--- a/t/t6438-submodule-directory-file-conflicts.sh
+++ b/t/t6438-submodule-directory-file-conflicts.sh
@@ -12,8 +12,11 @@ test_submodule_switch "merge --ff"
test_submodule_switch "merge --ff-only"
-KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
-KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES=1
+if test "$GIT_TEST_MERGE_ALGORITHM" != ort
+then
+ KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
+ KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES=1
+fi
test_submodule_switch "merge --no-ff"
test_done
diff --git a/t/t6501-freshen-objects.sh b/t/t6501-freshen-objects.sh
index 75210f0..1066245 100755
--- a/t/t6501-freshen-objects.sh
+++ b/t/t6501-freshen-objects.sh
@@ -43,15 +43,25 @@ commit () {
}
maybe_repack () {
- if test -n "$repack"; then
+ case "$title" in
+ loose)
+ : skip repack
+ ;;
+ repack)
git repack -ad
- fi
+ ;;
+ bitmap)
+ git repack -adb
+ ;;
+ *)
+ echo >&2 "unknown test type in maybe_repack"
+ return 1
+ ;;
+ esac
}
-for repack in '' true; do
- title=${repack:+repack}
- title=${title:-loose}
-
+for title in loose repack bitmap
+do
test_expect_success "make repo completely empty ($title)" '
rm -rf .git &&
git init
diff --git a/t/t7011-skip-worktree-reading.sh b/t/t7011-skip-worktree-reading.sh
index 2685258..1761a2b 100755
--- a/t/t7011-skip-worktree-reading.sh
+++ b/t/t7011-skip-worktree-reading.sh
@@ -132,11 +132,6 @@ test_expect_success 'diff-files does not examine skip-worktree dirty entries' '
test -z "$(git diff-files -- one)"
'
-test_expect_success 'git-rm succeeds on skip-worktree absent entries' '
- setup_absent &&
- git rm 1
-'
-
test_expect_success 'commit on skip-worktree absent entries' '
git reset &&
setup_absent &&
diff --git a/t/t7012-skip-worktree-writing.sh b/t/t7012-skip-worktree-writing.sh
index f2a8e76..a1080b9 100755
--- a/t/t7012-skip-worktree-writing.sh
+++ b/t/t7012-skip-worktree-writing.sh
@@ -60,13 +60,6 @@ setup_absent() {
git update-index --skip-worktree 1
}
-test_absent() {
- echo "100644 $EMPTY_BLOB 0 1" > expected &&
- git ls-files --stage 1 > result &&
- test_cmp expected result &&
- test ! -f 1
-}
-
setup_dirty() {
git update-index --force-remove 1 &&
echo dirty > 1 &&
@@ -100,18 +93,6 @@ test_expect_success 'index setup' '
test_cmp expected result
'
-test_expect_success 'git-add ignores worktree content' '
- setup_absent &&
- git add 1 &&
- test_absent
-'
-
-test_expect_success 'git-add ignores worktree content' '
- setup_dirty &&
- git add 1 &&
- test_dirty
-'
-
test_expect_success 'git-rm fails if worktree is dirty' '
setup_dirty &&
test_must_fail git rm 1 &&
diff --git a/t/t7063-status-untracked-cache.sh b/t/t7063-status-untracked-cache.sh
index accefde..a0c123b 100755
--- a/t/t7063-status-untracked-cache.sh
+++ b/t/t7063-status-untracked-cache.sh
@@ -57,6 +57,20 @@ iuc () {
return $ret
}
+get_relevant_traces () {
+ # From the GIT_TRACE2_PERF data of the form
+ # $TIME $FILE:$LINE | d0 | main | data | r1 | ? | ? | read_directo | $RELEVANT_STAT
+ # extract the $RELEVANT_STAT fields. We don't care about region_enter
+ # or region_leave, or stats for things outside read_directory.
+ INPUT_FILE=$1
+ OUTPUT_FILE=$2
+ grep data.*read_directo $INPUT_FILE |
+ cut -d "|" -f 9 |
+ grep -v visited \
+ >"$OUTPUT_FILE"
+}
+
+
test_lazy_prereq UNTRACKED_CACHE '
{ git update-index --test-untracked-cache; ret=$?; } &&
test $ret -ne 1
@@ -129,19 +143,21 @@ EOF
test_expect_success 'status first time (empty cache)' '
avoid_racy &&
- : >../trace &&
- GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
+ : >../trace.output &&
+ GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
git status --porcelain >../actual &&
iuc status --porcelain >../status.iuc &&
test_cmp ../status.expect ../status.iuc &&
test_cmp ../status.expect ../actual &&
+ get_relevant_traces ../trace.output ../trace.relevant &&
cat >../trace.expect <<EOF &&
-node creation: 3
-gitignore invalidation: 1
-directory invalidation: 0
-opendir: 4
+ ....path:
+ ....node-creation:3
+ ....gitignore-invalidation:1
+ ....directory-invalidation:0
+ ....opendir:4
EOF
- test_cmp ../trace.expect ../trace
+ test_cmp ../trace.expect ../trace.relevant
'
test_expect_success 'untracked cache after first status' '
@@ -151,19 +167,21 @@ test_expect_success 'untracked cache after first status' '
test_expect_success 'status second time (fully populated cache)' '
avoid_racy &&
- : >../trace &&
- GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
+ : >../trace.output &&
+ GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
git status --porcelain >../actual &&
iuc status --porcelain >../status.iuc &&
test_cmp ../status.expect ../status.iuc &&
test_cmp ../status.expect ../actual &&
+ get_relevant_traces ../trace.output ../trace.relevant &&
cat >../trace.expect <<EOF &&
-node creation: 0
-gitignore invalidation: 0
-directory invalidation: 0
-opendir: 0
+ ....path:
+ ....node-creation:0
+ ....gitignore-invalidation:0
+ ....directory-invalidation:0
+ ....opendir:0
EOF
- test_cmp ../trace.expect ../trace
+ test_cmp ../trace.expect ../trace.relevant
'
test_expect_success 'untracked cache after second status' '
@@ -174,8 +192,8 @@ test_expect_success 'untracked cache after second status' '
test_expect_success 'modify in root directory, one dir invalidation' '
avoid_racy &&
: >four &&
- : >../trace &&
- GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
+ : >../trace.output &&
+ GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
git status --porcelain >../actual &&
iuc status --porcelain >../status.iuc &&
cat >../status.expect <<EOF &&
@@ -189,13 +207,15 @@ A two
EOF
test_cmp ../status.expect ../status.iuc &&
test_cmp ../status.expect ../actual &&
+ get_relevant_traces ../trace.output ../trace.relevant &&
cat >../trace.expect <<EOF &&
-node creation: 0
-gitignore invalidation: 0
-directory invalidation: 1
-opendir: 1
+ ....path:
+ ....node-creation:0
+ ....gitignore-invalidation:0
+ ....directory-invalidation:1
+ ....opendir:1
EOF
- test_cmp ../trace.expect ../trace
+ test_cmp ../trace.expect ../trace.relevant
'
@@ -223,8 +243,8 @@ EOF
test_expect_success 'new .gitignore invalidates recursively' '
avoid_racy &&
echo four >.gitignore &&
- : >../trace &&
- GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
+ : >../trace.output &&
+ GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
git status --porcelain >../actual &&
iuc status --porcelain >../status.iuc &&
cat >../status.expect <<EOF &&
@@ -238,13 +258,15 @@ A two
EOF
test_cmp ../status.expect ../status.iuc &&
test_cmp ../status.expect ../actual &&
+ get_relevant_traces ../trace.output ../trace.relevant &&
cat >../trace.expect <<EOF &&
-node creation: 0
-gitignore invalidation: 1
-directory invalidation: 1
-opendir: 4
+ ....path:
+ ....node-creation:0
+ ....gitignore-invalidation:1
+ ....directory-invalidation:1
+ ....opendir:4
EOF
- test_cmp ../trace.expect ../trace
+ test_cmp ../trace.expect ../trace.relevant
'
@@ -272,8 +294,8 @@ EOF
test_expect_success 'new info/exclude invalidates everything' '
avoid_racy &&
echo three >>.git/info/exclude &&
- : >../trace &&
- GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
+ : >../trace.output &&
+ GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
git status --porcelain >../actual &&
iuc status --porcelain >../status.iuc &&
cat >../status.expect <<EOF &&
@@ -285,13 +307,15 @@ A two
EOF
test_cmp ../status.expect ../status.iuc &&
test_cmp ../status.expect ../actual &&
+ get_relevant_traces ../trace.output ../trace.relevant &&
cat >../trace.expect <<EOF &&
-node creation: 0
-gitignore invalidation: 1
-directory invalidation: 0
-opendir: 4
+ ....path:
+ ....node-creation:0
+ ....gitignore-invalidation:1
+ ....directory-invalidation:0
+ ....opendir:4
EOF
- test_cmp ../trace.expect ../trace
+ test_cmp ../trace.expect ../trace.relevant
'
test_expect_success 'verify untracked cache dump' '
@@ -330,8 +354,8 @@ EOF
'
test_expect_success 'status after the move' '
- : >../trace &&
- GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
+ : >../trace.output &&
+ GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
git status --porcelain >../actual &&
iuc status --porcelain >../status.iuc &&
cat >../status.expect <<EOF &&
@@ -343,13 +367,15 @@ A one
EOF
test_cmp ../status.expect ../status.iuc &&
test_cmp ../status.expect ../actual &&
+ get_relevant_traces ../trace.output ../trace.relevant &&
cat >../trace.expect <<EOF &&
-node creation: 0
-gitignore invalidation: 0
-directory invalidation: 0
-opendir: 1
+ ....path:
+ ....node-creation:0
+ ....gitignore-invalidation:0
+ ....directory-invalidation:0
+ ....opendir:1
EOF
- test_cmp ../trace.expect ../trace
+ test_cmp ../trace.expect ../trace.relevant
'
test_expect_success 'verify untracked cache dump' '
@@ -389,8 +415,8 @@ EOF
'
test_expect_success 'status after the move' '
- : >../trace &&
- GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
+ : >../trace.output &&
+ GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
git status --porcelain >../actual &&
iuc status --porcelain >../status.iuc &&
cat >../status.expect <<EOF &&
@@ -402,13 +428,15 @@ A two
EOF
test_cmp ../status.expect ../status.iuc &&
test_cmp ../status.expect ../actual &&
+ get_relevant_traces ../trace.output ../trace.relevant &&
cat >../trace.expect <<EOF &&
-node creation: 0
-gitignore invalidation: 0
-directory invalidation: 0
-opendir: 1
+ ....path:
+ ....node-creation:0
+ ....gitignore-invalidation:0
+ ....directory-invalidation:0
+ ....opendir:1
EOF
- test_cmp ../trace.expect ../trace
+ test_cmp ../trace.expect ../trace.relevant
'
test_expect_success 'verify untracked cache dump' '
@@ -438,8 +466,8 @@ test_expect_success 'set up for sparse checkout testing' '
'
test_expect_success 'status after commit' '
- : >../trace &&
- GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
+ : >../trace.output &&
+ GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
git status --porcelain >../actual &&
iuc status --porcelain >../status.iuc &&
cat >../status.expect <<EOF &&
@@ -448,13 +476,15 @@ test_expect_success 'status after commit' '
EOF
test_cmp ../status.expect ../status.iuc &&
test_cmp ../status.expect ../actual &&
+ get_relevant_traces ../trace.output ../trace.relevant &&
cat >../trace.expect <<EOF &&
-node creation: 0
-gitignore invalidation: 0
-directory invalidation: 0
-opendir: 2
+ ....path:
+ ....node-creation:0
+ ....gitignore-invalidation:0
+ ....directory-invalidation:0
+ ....opendir:2
EOF
- test_cmp ../trace.expect ../trace
+ test_cmp ../trace.expect ../trace.relevant
'
test_expect_success 'untracked cache correct after commit' '
@@ -496,9 +526,9 @@ test_expect_success 'create/modify files, some of which are gitignored' '
'
test_expect_success 'test sparse status with untracked cache' '
- : >../trace &&
+ : >../trace.output &&
avoid_racy &&
- GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
+ GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
git status --porcelain >../status.actual &&
iuc status --porcelain >../status.iuc &&
cat >../status.expect <<EOF &&
@@ -509,13 +539,15 @@ test_expect_success 'test sparse status with untracked cache' '
EOF
test_cmp ../status.expect ../status.iuc &&
test_cmp ../status.expect ../status.actual &&
+ get_relevant_traces ../trace.output ../trace.relevant &&
cat >../trace.expect <<EOF &&
-node creation: 0
-gitignore invalidation: 1
-directory invalidation: 2
-opendir: 2
+ ....path:
+ ....node-creation:0
+ ....gitignore-invalidation:1
+ ....directory-invalidation:2
+ ....opendir:2
EOF
- test_cmp ../trace.expect ../trace
+ test_cmp ../trace.expect ../trace.relevant
'
test_expect_success 'untracked cache correct after status' '
@@ -539,8 +571,8 @@ EOF
test_expect_success 'test sparse status again with untracked cache' '
avoid_racy &&
- : >../trace &&
- GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
+ : >../trace.output &&
+ GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
git status --porcelain >../status.actual &&
iuc status --porcelain >../status.iuc &&
cat >../status.expect <<EOF &&
@@ -551,13 +583,15 @@ test_expect_success 'test sparse status again with untracked cache' '
EOF
test_cmp ../status.expect ../status.iuc &&
test_cmp ../status.expect ../status.actual &&
+ get_relevant_traces ../trace.output ../trace.relevant &&
cat >../trace.expect <<EOF &&
-node creation: 0
-gitignore invalidation: 0
-directory invalidation: 0
-opendir: 0
+ ....path:
+ ....node-creation:0
+ ....gitignore-invalidation:0
+ ....directory-invalidation:0
+ ....opendir:0
EOF
- test_cmp ../trace.expect ../trace
+ test_cmp ../trace.expect ../trace.relevant
'
test_expect_success 'set up for test of subdir and sparse checkouts' '
@@ -568,8 +602,8 @@ test_expect_success 'set up for test of subdir and sparse checkouts' '
test_expect_success 'test sparse status with untracked cache and subdir' '
avoid_racy &&
- : >../trace &&
- GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
+ : >../trace.output &&
+ GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
git status --porcelain >../status.actual &&
iuc status --porcelain >../status.iuc &&
cat >../status.expect <<EOF &&
@@ -581,13 +615,15 @@ test_expect_success 'test sparse status with untracked cache and subdir' '
EOF
test_cmp ../status.expect ../status.iuc &&
test_cmp ../status.expect ../status.actual &&
+ get_relevant_traces ../trace.output ../trace.relevant &&
cat >../trace.expect <<EOF &&
-node creation: 2
-gitignore invalidation: 0
-directory invalidation: 1
-opendir: 3
+ ....path:
+ ....node-creation:2
+ ....gitignore-invalidation:0
+ ....directory-invalidation:1
+ ....opendir:3
EOF
- test_cmp ../trace.expect ../trace
+ test_cmp ../trace.expect ../trace.relevant
'
test_expect_success 'verify untracked cache dump (sparse/subdirs)' '
@@ -616,19 +652,21 @@ EOF
test_expect_success 'test sparse status again with untracked cache and subdir' '
avoid_racy &&
- : >../trace &&
- GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
+ : >../trace.output &&
+ GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
git status --porcelain >../status.actual &&
iuc status --porcelain >../status.iuc &&
test_cmp ../status.expect ../status.iuc &&
test_cmp ../status.expect ../status.actual &&
+ get_relevant_traces ../trace.output ../trace.relevant &&
cat >../trace.expect <<EOF &&
-node creation: 0
-gitignore invalidation: 0
-directory invalidation: 0
-opendir: 0
+ ....path:
+ ....node-creation:0
+ ....gitignore-invalidation:0
+ ....directory-invalidation:0
+ ....opendir:0
EOF
- test_cmp ../trace.expect ../trace
+ test_cmp ../trace.expect ../trace.relevant
'
test_expect_success 'move entry in subdir from untracked to cached' '
diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh
index a74816c..0399701 100755
--- a/t/t7300-clean.sh
+++ b/t/t7300-clean.sh
@@ -746,4 +746,46 @@ test_expect_success 'clean untracked paths by pathspec' '
test_must_be_empty actual
'
+test_expect_success 'avoid traversing into ignored directories' '
+ test_when_finished rm -f output error trace.* &&
+ test_create_repo avoid-traversing-deep-hierarchy &&
+ (
+ cd avoid-traversing-deep-hierarchy &&
+
+ mkdir -p untracked/subdir/with/a &&
+ >untracked/subdir/with/a/random-file.txt &&
+
+ GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
+ git clean -ffdxn -e untracked
+ ) &&
+
+ # Make sure we only visited into the top-level directory, and did
+ # not traverse into the "untracked" subdirectory since it was excluded
+ grep data.*read_directo.*directories-visited trace.output |
+ cut -d "|" -f 9 >trace.relevant &&
+ cat >trace.expect <<-EOF &&
+ ..directories-visited:1
+ EOF
+ test_cmp trace.expect trace.relevant
+'
+
+test_expect_success 'traverse into directories that may have ignored entries' '
+ test_when_finished rm -f output &&
+ test_create_repo need-to-traverse-into-hierarchy &&
+ (
+ cd need-to-traverse-into-hierarchy &&
+ mkdir -p modules/foobar/src/generated &&
+ > modules/foobar/src/generated/code.c &&
+ > modules/foobar/Makefile &&
+ echo "/modules/**/src/generated/" >.gitignore &&
+
+ git clean -fX modules/foobar >../output &&
+
+ grep Removing ../output &&
+
+ test_path_is_missing modules/foobar/src/generated/code.c &&
+ test_path_is_file modules/foobar/Makefile
+ )
+'
+
test_done
diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
index ff3ba54..f4f61fe 100755
--- a/t/t7406-submodule-update.sh
+++ b/t/t7406-submodule-update.sh
@@ -1037,4 +1037,28 @@ test_expect_success 'submodule update --quiet passes quietness to merge/rebase'
)
'
+test_expect_success 'submodule update --quiet passes quietness to fetch with a shallow clone' '
+ test_when_finished "rm -rf super4 super5 super6" &&
+ git clone . super4 &&
+ (cd super4 &&
+ git submodule add --quiet file://"$TRASH_DIRECTORY"/submodule submodule3 &&
+ git commit -am "setup submodule3"
+ ) &&
+ (cd submodule &&
+ test_commit line6 file
+ ) &&
+ git clone super4 super5 &&
+ (cd super5 &&
+ git submodule update --quiet --init --depth=1 submodule3 >out 2>err &&
+ test_must_be_empty out &&
+ test_must_be_empty err
+ ) &&
+ git clone super4 super6 &&
+ (cd super6 &&
+ git submodule update --init --depth=1 submodule3 >out 2>err &&
+ test_file_not_empty out &&
+ test_file_not_empty err
+ )
+'
+
test_done
diff --git a/t/t7415-submodule-names.sh b/t/t7450-bad-git-dotfiles.sh
index f70368b..41706c1 100755
--- a/t/t7415-submodule-names.sh
+++ b/t/t7450-bad-git-dotfiles.sh
@@ -1,9 +1,16 @@
#!/bin/sh
-test_description='check handling of .. in submodule names
+test_description='check broken or malicious patterns in .git* files
-Exercise the name-checking function on a variety of names, and then give a
-real-world setup that confirms we catch this in practice.
+Such as:
+
+ - presence of .. in submodule names;
+ Exercise the name-checking function on a variety of names, and then give a
+ real-world setup that confirms we catch this in practice.
+
+ - nested submodule names
+
+ - symlinked .gitmodules, etc
'
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-pack.sh
@@ -132,31 +139,84 @@ test_expect_success 'index-pack --strict works for non-repo pack' '
grep gitmodulesName output
'
-test_expect_success 'fsck detects symlinked .gitmodules file' '
- git init symlink &&
- (
- cd symlink &&
-
- # Make the tree directly to avoid index restrictions.
- #
- # Because symlinks store the target as a blob, choose
- # a pathname that could be parsed as a .gitmodules file
- # to trick naive non-symlink-aware checking.
- tricky="[foo]bar=true" &&
- content=$(git hash-object -w ../.gitmodules) &&
- target=$(printf "$tricky" | git hash-object -w --stdin) &&
- {
- printf "100644 blob $content\t$tricky\n" &&
- printf "120000 blob $target\t.gitmodules\n"
- } | git mktree &&
-
- # Check not only that we fail, but that it is due to the
- # symlink detector; this grep string comes from the config
- # variable name and will not be translated.
- test_must_fail git fsck 2>output &&
- test_i18ngrep gitmodulesSymlink output
- )
-'
+check_dotx_symlink () {
+ fsck_must_fail=test_must_fail
+ fsck_prefix=error
+ refuse_index=t
+ case "$1" in
+ --warning)
+ fsck_must_fail=
+ fsck_prefix=warning
+ refuse_index=
+ shift
+ ;;
+ esac
+
+ name=$1
+ type=$2
+ path=$3
+ dir=symlink-$name-$type
+
+ test_expect_success "set up repo with symlinked $name ($type)" '
+ git init $dir &&
+ (
+ cd $dir &&
+
+ # Make the tree directly to avoid index restrictions.
+ #
+ # Because symlinks store the target as a blob, choose
+ # a pathname that could be parsed as a .gitmodules file
+ # to trick naive non-symlink-aware checking.
+ tricky="[foo]bar=true" &&
+ content=$(git hash-object -w ../.gitmodules) &&
+ target=$(printf "$tricky" | git hash-object -w --stdin) &&
+ {
+ printf "100644 blob $content\t$tricky\n" &&
+ printf "120000 blob $target\t$path\n"
+ } >bad-tree
+ ) &&
+ tree=$(git -C $dir mktree <$dir/bad-tree)
+ '
+
+ test_expect_success "fsck detects symlinked $name ($type)" '
+ (
+ cd $dir &&
+
+ # Check not only that we fail, but that it is due to the
+ # symlink detector
+ $fsck_must_fail git fsck 2>output &&
+ grep "$fsck_prefix.*tree $tree: ${name}Symlink" output
+ )
+ '
+
+ test -n "$refuse_index" &&
+ test_expect_success "refuse to load symlinked $name into index ($type)" '
+ test_must_fail \
+ git -C $dir \
+ -c core.protectntfs \
+ -c core.protecthfs \
+ read-tree $tree 2>err &&
+ grep "invalid path.*$name" err &&
+ git -C $dir ls-files -s >out &&
+ test_must_be_empty out
+ '
+}
+
+check_dotx_symlink gitmodules vanilla .gitmodules
+check_dotx_symlink gitmodules ntfs ".gitmodules ."
+check_dotx_symlink gitmodules hfs ".${u200c}gitmodules"
+
+check_dotx_symlink --warning gitattributes vanilla .gitattributes
+check_dotx_symlink --warning gitattributes ntfs ".gitattributes ."
+check_dotx_symlink --warning gitattributes hfs ".${u200c}gitattributes"
+
+check_dotx_symlink --warning gitignore vanilla .gitignore
+check_dotx_symlink --warning gitignore ntfs ".gitignore ."
+check_dotx_symlink --warning gitignore hfs ".${u200c}gitignore"
+
+check_dotx_symlink --warning mailmap vanilla .mailmap
+check_dotx_symlink --warning mailmap ntfs ".mailmap ."
+check_dotx_symlink --warning mailmap hfs ".${u200c}mailmap"
test_expect_success 'fsck detects non-blob .gitmodules' '
git init non-blob &&
@@ -191,7 +251,7 @@ test_expect_success 'fsck detects corrupt .gitmodules' '
)
'
-test_expect_success MINGW 'prevent git~1 squatting on Windows' '
+test_expect_success WINDOWS 'prevent git~1 squatting on Windows' '
git init squatting &&
(
cd squatting &&
@@ -219,10 +279,13 @@ test_expect_success MINGW 'prevent git~1 squatting on Windows' '
test_tick &&
git -c core.protectNTFS=false commit -m "module"
) &&
- test_must_fail git -c core.protectNTFS=false \
- clone --recurse-submodules squatting squatting-clone 2>err &&
- test_i18ngrep -e "directory not empty" -e "not an empty directory" err &&
- ! grep gitdir squatting-clone/d/a/git~2
+ if test_have_prereq MINGW
+ then
+ test_must_fail git -c core.protectNTFS=false \
+ clone --recurse-submodules squatting squatting-clone 2>err &&
+ test_i18ngrep -e "directory not empty" -e "not an empty directory" err &&
+ ! grep gitdir squatting-clone/d/a/git~2
+ fi
'
test_expect_success 'git dirs of sibling submodules must not be nested' '
diff --git a/t/t7500-commit-template-squash-signoff.sh b/t/t7500-commit-template-squash-signoff.sh
index 9092db5..7d02f79 100755
--- a/t/t7500-commit-template-squash-signoff.sh
+++ b/t/t7500-commit-template-squash-signoff.sh
@@ -413,7 +413,7 @@ test_expect_success 'amend! commit allows empty commit msg body with --allow-emp
'
test_fixup_reword_opt () {
- test_expect_success C_LOCALE_OUTPUT "--fixup=reword: incompatible with $1" "
+ test_expect_success "--fixup=reword: incompatible with $1" "
echo 'fatal: reword option of --fixup is mutually exclusive with'\
'--patch/--interactive/--all/--include/--only' >expect &&
test_must_fail git commit --fixup=reword:HEAD~ $1 2>actual &&
diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh
index 6602790..04885d0 100755
--- a/t/t7513-interpret-trailers.sh
+++ b/t/t7513-interpret-trailers.sh
@@ -51,6 +51,69 @@ test_expect_success 'setup' '
EOF
'
+test_expect_success 'with cmd' '
+ test_when_finished "git config --remove-section trailer.bug" &&
+ git config trailer.bug.key "Bug-maker: " &&
+ git config trailer.bug.ifExists "add" &&
+ git config trailer.bug.cmd "echo \"maybe is\"" &&
+ cat >expected2 <<-EOF &&
+
+ Bug-maker: maybe is him
+ Bug-maker: maybe is me
+ EOF
+ git interpret-trailers --trailer "bug: him" --trailer "bug:me" \
+ >actual2 &&
+ test_cmp expected2 actual2
+'
+
+test_expect_success 'with cmd and $1' '
+ test_when_finished "git config --remove-section trailer.bug" &&
+ git config trailer.bug.key "Bug-maker: " &&
+ git config trailer.bug.ifExists "add" &&
+ git config trailer.bug.cmd "echo \"\$1\" is" &&
+ cat >expected2 <<-EOF &&
+
+ Bug-maker: him is him
+ Bug-maker: me is me
+ EOF
+ git interpret-trailers --trailer "bug: him" --trailer "bug:me" \
+ >actual2 &&
+ test_cmp expected2 actual2
+'
+
+test_expect_success 'with cmd and $1 with sh -c' '
+ test_when_finished "git config --remove-section trailer.bug" &&
+ git config trailer.bug.key "Bug-maker: " &&
+ git config trailer.bug.ifExists "replace" &&
+ git config trailer.bug.cmd "sh -c \"echo who is \"\$1\"\"" &&
+ cat >expected2 <<-EOF &&
+
+ Bug-maker: who is me
+ EOF
+ git interpret-trailers --trailer "bug: him" --trailer "bug:me" \
+ >actual2 &&
+ test_cmp expected2 actual2
+'
+
+test_expect_success 'with cmd and $1 with shell script' '
+ test_when_finished "git config --remove-section trailer.bug" &&
+ git config trailer.bug.key "Bug-maker: " &&
+ git config trailer.bug.ifExists "replace" &&
+ git config trailer.bug.cmd "./echoscript" &&
+ cat >expected2 <<-EOF &&
+
+ Bug-maker: who is me
+ EOF
+ cat >echoscript <<-EOF &&
+ #!/bin/sh
+ echo who is "\$1"
+ EOF
+ chmod +x echoscript &&
+ git interpret-trailers --trailer "bug: him" --trailer "bug:me" \
+ >actual2 &&
+ test_cmp expected2 actual2
+'
+
test_expect_success 'without config' '
sed -e "s/ Z\$/ /" >expected <<-\EOF &&
@@ -1274,6 +1337,27 @@ test_expect_success 'setup a commit' '
git commit -m "Add file a.txt"
'
+test_expect_success 'cmd takes precedence over command' '
+ test_when_finished "git config --unset trailer.fix.cmd" &&
+ git config trailer.fix.ifExists "replace" &&
+ git config trailer.fix.cmd "test -n \"\$1\" && git log -1 --oneline --format=\"%h (%aN)\" \
+ --abbrev-commit --abbrev=14 \"\$1\" || true" &&
+ git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" \
+ --abbrev-commit --abbrev=14 \$ARG" &&
+ FIXED=$(git log -1 --oneline --format="%h (%aN)" --abbrev-commit --abbrev=14 HEAD) &&
+ cat complex_message_body >expected2 &&
+ sed -e "s/ Z\$/ /" >>expected2 <<-EOF &&
+ Fixes: $FIXED
+ Acked-by= Z
+ Reviewed-by:
+ Signed-off-by: Z
+ Signed-off-by: A U Thor <author@example.com>
+ EOF
+ git interpret-trailers --trailer "review:" --trailer "fix=HEAD" \
+ <complex_message >actual2 &&
+ test_cmp expected2 actual2
+'
+
test_expect_success 'with command using $ARG' '
git config trailer.fix.ifExists "replace" &&
git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG" &&
diff --git a/t/t7519-status-fsmonitor.sh b/t/t7519-status-fsmonitor.sh
index 45d025f..637391c 100755
--- a/t/t7519-status-fsmonitor.sh
+++ b/t/t7519-status-fsmonitor.sh
@@ -334,7 +334,7 @@ test_expect_success UNTRACKED_CACHE 'ignore .git changes when invalidating UNTR'
git config core.fsmonitor .git/hooks/fsmonitor-test &&
git update-index --untracked-cache &&
git update-index --fsmonitor &&
- GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace-before" \
+ GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace-before" \
git status &&
test-tool dump-untracked-cache >../before
) &&
@@ -346,12 +346,12 @@ test_expect_success UNTRACKED_CACHE 'ignore .git changes when invalidating UNTR'
EOF
(
cd dot-git &&
- GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace-after" \
+ GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace-after" \
git status &&
test-tool dump-untracked-cache >../after
) &&
- grep "directory invalidation" trace-before >>before &&
- grep "directory invalidation" trace-after >>after &&
+ grep "directory-invalidation" trace-before | cut -d"|" -f 9 >>before &&
+ grep "directory-invalidation" trace-after | cut -d"|" -f 9 >>after &&
# UNTR extension unchanged, dir invalidation count unchanged
test_cmp before after
'
diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh
index 2412d8c..b93ae01 100755
--- a/t/t7900-maintenance.sh
+++ b/t/t7900-maintenance.sh
@@ -141,19 +141,25 @@ test_expect_success 'prefetch multiple remotes' '
test_commit -C clone1 one &&
test_commit -C clone2 two &&
GIT_TRACE2_EVENT="$(pwd)/run-prefetch.txt" git maintenance run --task=prefetch 2>/dev/null &&
- fetchargs="--prune --no-tags --no-write-fetch-head --recurse-submodules=no --refmap= --quiet" &&
- test_subcommand git fetch remote1 $fetchargs +refs/heads/\\*:refs/prefetch/remote1/\\* <run-prefetch.txt &&
- test_subcommand git fetch remote2 $fetchargs +refs/heads/\\*:refs/prefetch/remote2/\\* <run-prefetch.txt &&
+ fetchargs="--prefetch --prune --no-tags --no-write-fetch-head --recurse-submodules=no --quiet" &&
+ test_subcommand git fetch remote1 $fetchargs <run-prefetch.txt &&
+ test_subcommand git fetch remote2 $fetchargs <run-prefetch.txt &&
test_path_is_missing .git/refs/remotes &&
- git log prefetch/remote1/one &&
- git log prefetch/remote2/two &&
+ git log prefetch/remotes/remote1/one &&
+ git log prefetch/remotes/remote2/two &&
git fetch --all &&
- test_cmp_rev refs/remotes/remote1/one refs/prefetch/remote1/one &&
- test_cmp_rev refs/remotes/remote2/two refs/prefetch/remote2/two &&
+ test_cmp_rev refs/remotes/remote1/one refs/prefetch/remotes/remote1/one &&
+ test_cmp_rev refs/remotes/remote2/two refs/prefetch/remotes/remote2/two &&
test_cmp_config refs/prefetch/ log.excludedecoration &&
git log --oneline --decorate --all >log &&
- ! grep "prefetch" log
+ ! grep "prefetch" log &&
+
+ test_when_finished git config --unset remote.remote1.skipFetchAll &&
+ git config remote.remote1.skipFetchAll true &&
+ GIT_TRACE2_EVENT="$(pwd)/skip-remote1.txt" git maintenance run --task=prefetch 2>/dev/null &&
+ test_subcommand ! git fetch remote1 $fetchargs <skip-remote1.txt &&
+ test_subcommand git fetch remote2 $fetchargs <skip-remote1.txt
'
test_expect_success 'prefetch and existing log.excludeDecoration values' '
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index 1a1caf8..30eff72 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -415,15 +415,23 @@ test_expect_success $PREREQ 'reject long lines' '
z512=$z64$z64$z64$z64$z64$z64$z64$z64 &&
clean_fake_sendmail &&
cp $patches longline.patch &&
- echo $z512$z512 >>longline.patch &&
+ cat >>longline.patch <<-EOF &&
+ $z512$z512
+ not a long line
+ $z512$z512
+ EOF
test_must_fail git send-email \
--from="Example <nobody@example.com>" \
--to=nobody@example.com \
--smtp-server="$(pwd)/fake.sendmail" \
--transfer-encoding=8bit \
$patches longline.patch \
- 2>errors &&
- grep longline.patch errors
+ 2>actual &&
+ cat >expect <<-\EOF &&
+ fatal: longline.patch:35 is longer than 998 characters
+ warning: no patches were sent
+ EOF
+ test_cmp expect actual
'
test_expect_success $PREREQ 'no patch was sent' '
@@ -527,22 +535,33 @@ test_expect_success $PREREQ "--validate respects relative core.hooksPath path" '
--to=nobody@example.com \
--smtp-server="$(pwd)/fake.sendmail" \
--validate \
- longline.patch 2>err &&
+ longline.patch 2>actual &&
test_path_is_file my-hooks.ran &&
- grep "rejected by sendemail-validate" err
+ cat >expect <<-EOF &&
+ fatal: longline.patch: rejected by sendemail-validate hook
+ fatal: command '"'"'my-hooks/sendemail-validate'"'"' died with exit code 1
+ warning: no patches were sent
+ EOF
+ test_cmp expect actual
'
test_expect_success $PREREQ "--validate respects absolute core.hooksPath path" '
- test_config core.hooksPath "$(pwd)/my-hooks" &&
+ hooks_path="$(pwd)/my-hooks" &&
+ test_config core.hooksPath "$hooks_path" &&
test_when_finished "rm my-hooks.ran" &&
test_must_fail git send-email \
--from="Example <nobody@example.com>" \
--to=nobody@example.com \
--smtp-server="$(pwd)/fake.sendmail" \
--validate \
- longline.patch 2>err &&
+ longline.patch 2>actual &&
test_path_is_file my-hooks.ran &&
- grep "rejected by sendemail-validate" err
+ cat >expect <<-EOF &&
+ fatal: longline.patch: rejected by sendemail-validate hook
+ fatal: command '"'"'$hooks_path/sendemail-validate'"'"' died with exit code 1
+ warning: no patches were sent
+ EOF
+ test_cmp expect actual
'
for enc in 7bit 8bit quoted-printable base64
@@ -625,14 +644,33 @@ test_expect_success $PREREQ 'In-Reply-To with --chain-reply-to' '
test_cmp expect actual
'
+test_set_editor "$(pwd)/fake-editor"
+
+test_expect_success $PREREQ 'setup erroring fake editor' '
+ write_script fake-editor <<-\EOF
+ echo >&2 "I am about to error"
+ exit 1
+ EOF
+'
+
+test_expect_success $PREREQ 'fake editor dies with error' '
+ clean_fake_sendmail &&
+ test_must_fail git send-email \
+ --compose --subject foo \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ $patches 2>err &&
+ grep "I am about to error" err &&
+ grep "the editor exited uncleanly, aborting everything" err
+'
+
test_expect_success $PREREQ 'setup fake editor' '
write_script fake-editor <<-\EOF
echo fake edit >>"$1"
EOF
'
-test_set_editor "$(pwd)/fake-editor"
-
test_expect_success $PREREQ '--compose works' '
clean_fake_sendmail &&
git send-email \
@@ -2129,6 +2167,37 @@ test_expect_success $PREREQ 'leading and trailing whitespaces are removed' '
test_cmp expected-list actual-list
'
+test_expect_success $PREREQ 'test using command name with --sendmail-cmd' '
+ clean_fake_sendmail &&
+ PATH="$(pwd):$PATH" \
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --sendmail-cmd="fake.sendmail" \
+ HEAD^ &&
+ test_path_is_file commandline1
+'
+
+test_expect_success $PREREQ 'test using arguments with --sendmail-cmd' '
+ clean_fake_sendmail &&
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --sendmail-cmd='\''"$(pwd)/fake.sendmail" -f nobody@example.com'\'' \
+ HEAD^ &&
+ test_path_is_file commandline1
+'
+
+test_expect_success $PREREQ 'test shell expression with --sendmail-cmd' '
+ clean_fake_sendmail &&
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --sendmail-cmd='\''f() { "$(pwd)/fake.sendmail" "$@"; };f'\'' \
+ HEAD^ &&
+ test_path_is_file commandline1
+'
+
test_expect_success $PREREQ 'invoke hook' '
mkdir -p .git/hooks &&
diff --git a/t/t9117-git-svn-init-clone.sh b/t/t9117-git-svn-init-clone.sh
index 044f65e..62de819 100755
--- a/t/t9117-git-svn-init-clone.sh
+++ b/t/t9117-git-svn-init-clone.sh
@@ -7,12 +7,6 @@ test_description='git svn init/clone tests'
. ./lib-git-svn.sh
-# setup, run inside tmp so we don't have any conflicts with $svnrepo
-set -e
-rm -r .git
-mkdir tmp
-cd tmp
-
test_expect_success 'setup svnrepo' '
mkdir project project/trunk project/branches project/tags &&
echo foo > project/trunk/foo &&
diff --git a/t/t9148-git-svn-propset.sh b/t/t9148-git-svn-propset.sh
index 1026390..aebb289 100755
--- a/t/t9148-git-svn-propset.sh
+++ b/t/t9148-git-svn-propset.sh
@@ -7,19 +7,22 @@ test_description='git svn propset tests'
. ./lib-git-svn.sh
-foo_subdir2="subdir/subdir2/foo_subdir2"
+test_expect_success 'setup propset via import' '
+ test_when_finished "rm -rf import" &&
-set -e
-mkdir import &&
-(set -e ; cd import
- mkdir subdir
- mkdir subdir/subdir2
- touch foo # for 'add props top level'
- touch subdir/foo_subdir # for 'add props relative'
- touch "$foo_subdir2" # for 'add props subdir'
- svn_cmd import -m 'import for git svn' . "$svnrepo" >/dev/null
-)
-rm -rf import
+ foo_subdir2="subdir/subdir2/foo_subdir2" &&
+ mkdir -p import/subdir/subdir2 &&
+ (
+ cd import &&
+ # for "add props top level"
+ >foo &&
+ # for "add props relative"
+ >subdir/foo_subdir &&
+ # for "add props subdir"
+ >"$foo_subdir2" &&
+ svn_cmd import -m "import for git svn" . "$svnrepo"
+ )
+'
test_expect_success 'initialize git svn' '
git svn init "$svnrepo"
diff --git a/t/t9801-git-p4-branch.sh b/t/t9801-git-p4-branch.sh
index ff94c3f..50a6f8b 100755
--- a/t/t9801-git-p4-branch.sh
+++ b/t/t9801-git-p4-branch.sh
@@ -294,11 +294,13 @@ test_expect_success 'git p4 clone complex branches' '
test_path_is_file file3 &&
grep update file2 &&
git reset --hard p4/depot/branch4 &&
+ git diff-tree --quiet HEAD &&
test_path_is_file file1 &&
test_path_is_file file2 &&
test_path_is_missing file3 &&
! grep update file2 &&
git reset --hard p4/depot/branch5 &&
+ git diff-tree --quiet HEAD &&
test_path_is_file file1 &&
test_path_is_file file2 &&
test_path_is_file file3 &&
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 04ce884..cb057ef 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -1879,6 +1879,7 @@ test_expect_success '__git_find_on_cmdline - single match' '
(
words=(git command --opt list) &&
cword=${#words[@]} &&
+ __git_cmd_idx=1 &&
__git_find_on_cmdline "add list remove" >actual
) &&
test_cmp expect actual
@@ -1889,6 +1890,7 @@ test_expect_success '__git_find_on_cmdline - multiple matches' '
(
words=(git command -o --opt remove list add) &&
cword=${#words[@]} &&
+ __git_cmd_idx=1 &&
__git_find_on_cmdline "add list remove" >actual
) &&
test_cmp expect actual
@@ -1898,6 +1900,7 @@ test_expect_success '__git_find_on_cmdline - no match' '
(
words=(git command --opt branch) &&
cword=${#words[@]} &&
+ __git_cmd_idx=1 &&
__git_find_on_cmdline "add list remove" >actual
) &&
test_must_be_empty actual
@@ -1908,6 +1911,7 @@ test_expect_success '__git_find_on_cmdline - single match with index' '
(
words=(git command --opt list) &&
cword=${#words[@]} &&
+ __git_cmd_idx=1 &&
__git_find_on_cmdline --show-idx "add list remove" >actual
) &&
test_cmp expect actual
@@ -1918,6 +1922,7 @@ test_expect_success '__git_find_on_cmdline - multiple matches with index' '
(
words=(git command -o --opt remove list add) &&
cword=${#words[@]} &&
+ __git_cmd_idx=1 &&
__git_find_on_cmdline --show-idx "add list remove" >actual
) &&
test_cmp expect actual
@@ -1927,11 +1932,23 @@ test_expect_success '__git_find_on_cmdline - no match with index' '
(
words=(git command --opt branch) &&
cword=${#words[@]} &&
+ __git_cmd_idx=1 &&
__git_find_on_cmdline --show-idx "add list remove" >actual
) &&
test_must_be_empty actual
'
+test_expect_success '__git_find_on_cmdline - ignores matches before command with index' '
+ echo "6 remove" >expect &&
+ (
+ words=(git -C remove command -o --opt remove list add) &&
+ cword=${#words[@]} &&
+ __git_cmd_idx=3 &&
+ __git_find_on_cmdline --show-idx "add list remove" >actual
+ ) &&
+ test_cmp expect actual
+'
+
test_expect_success '__git_get_config_variables' '
cat >expect <<-EOF &&
name-1
@@ -2275,6 +2292,7 @@ do
(
words=(git push '$flag' other ma) &&
cword=${#words[@]} cur=${words[cword-1]} &&
+ __git_cmd_idx=1 &&
__git_complete_remote_or_refspec &&
print_comp
) &&
@@ -2288,6 +2306,7 @@ do
(
words=(git push other '$flag' ma) &&
cword=${#words[@]} cur=${words[cword-1]} &&
+ __git_cmd_idx=1 &&
__git_complete_remote_or_refspec &&
print_comp
) &&
@@ -2306,6 +2325,7 @@ test_expect_success 'git config - variable name' '
test_completion "git config log.d" <<-\EOF
log.date Z
log.decorate Z
+ log.diffMerges Z
EOF
'
@@ -2327,6 +2347,7 @@ test_expect_success 'git -c - variable name' '
test_completion "git -c log.d" <<-\EOF
log.date=Z
log.decorate=Z
+ log.diffMerges=Z
EOF
'
@@ -2348,6 +2369,7 @@ test_expect_success 'git clone --config= - variable name' '
test_completion "git clone --config=log.d" <<-\EOF
log.date=Z
log.decorate=Z
+ log.diffMerges=Z
EOF
'
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 93a3fca..f0448da 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -1053,13 +1053,6 @@ test_cmp_bin () {
cmp "$@"
}
-# Wrapper for test_cmp which used to be used for
-# GIT_TEST_GETTEXT_POISON=false. Only here as a shim for other
-# in-flight changes. Should not be used and will be removed soon.
-test_i18ncmp () {
- test_cmp "$@"
-}
-
# Wrapper for grep which used to be used for
# GIT_TEST_GETTEXT_POISON=false. Only here as a shim for other
# in-flight changes. Should not be used and will be removed soon.
diff --git a/t/test-lib.sh b/t/test-lib.sh
index e986c58..54938c6 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -453,6 +453,8 @@ export EDITOR
GIT_DEFAULT_HASH="${GIT_TEST_DEFAULT_HASH:-sha1}"
export GIT_DEFAULT_HASH
+GIT_TEST_MERGE_ALGORITHM="${GIT_TEST_MERGE_ALGORITHM:-ort}"
+export GIT_TEST_MERGE_ALGORITHM
# Tests using GIT_TRACE typically don't want <timestamp> <file>:<line> output
GIT_TRACE_BARE=1
@@ -950,8 +952,11 @@ test_run_ () {
trace=
# 117 is magic because it is unlikely to match the exit
# code of other programs
- if $(printf '%s\n' "$1" | sed -f "$GIT_BUILD_DIR/t/chainlint.sed" | grep -q '?![A-Z][A-Z]*?!') ||
- test "OK-117" != "$(test_eval_ "(exit 117) && $1${LF}${LF}echo OK-\$?" 3>&1)"
+ if test "OK-117" != "$(test_eval_ "(exit 117) && $1${LF}${LF}echo OK-\$?" 3>&1)" ||
+ {
+ test "${GIT_TEST_CHAIN_LINT_HARDER:-${GIT_TEST_CHAIN_LINT_HARDER_DEFAULT:-1}}" != 0 &&
+ $(printf '%s\n' "$1" | sed -f "$GIT_BUILD_DIR/t/chainlint.sed" | grep -q '?![A-Z][A-Z]*?!')
+ }
then
BUG "broken &&-chain or run-away HERE-DOC: $1"
fi
@@ -1467,6 +1472,7 @@ case $uname_s in
test_set_prereq NATIVE_CRLF
test_set_prereq SED_STRIPS_CR
test_set_prereq GREP_STRIPS_CR
+ test_set_prereq WINDOWS
GIT_TEST_CMP=mingw_test_cmp
;;
*CYGWIN*)
@@ -1475,6 +1481,7 @@ case $uname_s in
test_set_prereq CYGWIN
test_set_prereq SED_STRIPS_CR
test_set_prereq GREP_STRIPS_CR
+ test_set_prereq WINDOWS
;;
*)
test_set_prereq POSIXPERM