aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/CodingGuidelines15
-rw-r--r--Documentation/RelNotes/2.46.1.txt29
-rw-r--r--Documentation/git-cat-file.txt6
-rw-r--r--Documentation/git-config.txt2
-rw-r--r--Documentation/git-diff-tree.txt2
-rw-r--r--Documentation/git-ls-files.txt6
-rw-r--r--Documentation/gittutorial.txt2
-rw-r--r--Documentation/howto/maintain-git.txt4
-rw-r--r--apply.c1
-rw-r--r--builtin/bundle.c5
-rw-r--r--builtin/diff-tree.c13
-rw-r--r--builtin/get-tar-commit-id.c1
-rw-r--r--builtin/index-pack.c9
-rw-r--r--builtin/ls-remote.c15
-rw-r--r--builtin/patch-id.c93
-rw-r--r--builtin/stash.c23
-rw-r--r--bundle.c7
-rw-r--r--config.c2
-rw-r--r--contrib/credential/osxkeychain/git-credential-osxkeychain.c2
-rw-r--r--csum-file.c9
-rw-r--r--csum-file.h1
-rw-r--r--log-tree.c13
-rw-r--r--mailmap.c4
-rw-r--r--read-cache.c99
-rw-r--r--ref-filter.c1
-rw-r--r--reflog.c3
-rw-r--r--refs/files-backend.c1
-rw-r--r--revision.c1
-rw-r--r--setup.c58
-rw-r--r--t/helper/test-json-writer.c2
-rw-r--r--t/helper/test-trace2.c1
-rwxr-xr-xt/t0018-advice.sh1
-rwxr-xr-xt/t0033-safe-directory.sh178
-rwxr-xr-xt/t1001-read-tree-m-2way.sh2
-rwxr-xr-xt/t1300-config.sh9
-rwxr-xr-xt/t1410-reflog.sh8
-rwxr-xr-xt/t2107-update-index-basic.sh1
-rwxr-xr-xt/t3903-stash.sh15
-rwxr-xr-xt/t4069-remerge-diff.sh35
-rwxr-xr-xt/t4129-apply-samemode.sh62
-rwxr-xr-xt/t4203-mailmap.sh1
-rwxr-xr-xt/t4204-patch-id.sh40
-rwxr-xr-xt/t5300-pack-object.sh39
-rwxr-xr-xt/t5512-ls-remote.sh13
-rwxr-xr-xt/t5523-push-upstream.sh4
-rwxr-xr-xt/t6020-bundle-misc.sh32
-rwxr-xr-xt/t6421-merge-partial-clone.sh15
-rw-r--r--t/unit-tests/t-example-decorate.c24
48 files changed, 799 insertions, 110 deletions
diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines
index 52afb2725f..5edd3a0b9d 100644
--- a/Documentation/CodingGuidelines
+++ b/Documentation/CodingGuidelines
@@ -185,8 +185,8 @@ For shell scripts specifically (not exhaustive):
- Even though "local" is not part of POSIX, we make heavy use of it
in our test suite. We do not use it in scripted Porcelains, and
- hopefully nobody starts using "local" before they are reimplemented
- in C ;-)
+ hopefully nobody starts using "local" before all shells that matter
+ support it (notably, ksh from AT&T Research does not support it yet).
- Some versions of shell do not understand "export variable=value",
so we write "variable=value" and then "export variable" on two
@@ -293,7 +293,9 @@ For C programs:
v12.01, 2022-03-28).
- Variables have to be declared at the beginning of the block, before
- the first statement (i.e. -Wdeclaration-after-statement).
+ the first statement (i.e. -Wdeclaration-after-statement). It is
+ encouraged to have a blank line between the end of the declarations
+ and the first statement in the block.
- NULL pointers shall be written as NULL, not as 0.
@@ -313,6 +315,13 @@ For C programs:
while( condition )
func (bar+1);
+ - A binary operator (other than ",") and ternary conditional "?:"
+ have a space on each side of the operator to separate it from its
+ operands. E.g. "A + 1", not "A+1".
+
+ - A unary operator (other than "." and "->") have no space between it
+ and its operand. E.g. "(char *)ptr", not "(char *) ptr".
+
- Do not explicitly compare an integral value with constant 0 or '\0',
or a pointer value with constant NULL. For instance, to validate that
counted array <ptr, cnt> is initialized but has no elements, write:
diff --git a/Documentation/RelNotes/2.46.1.txt b/Documentation/RelNotes/2.46.1.txt
index 52afb3556a..07dc76d030 100644
--- a/Documentation/RelNotes/2.46.1.txt
+++ b/Documentation/RelNotes/2.46.1.txt
@@ -34,4 +34,33 @@ Fixes since Git 2.46
* Perforce tests have been updated.
+ * The credential helper to talk to OSX keychain sometimes sent
+ garbage bytes after the username, which has been corrected.
+
+ * A recent update broke "git ls-remote" used outside a repository,
+ which has been corrected.
+
+ * "git config --value=foo --fixed-value section.key newvalue" barfed
+ when the existing value in the configuration file used the
+ valueless true syntax, which has been corrected.
+
+ * "git reflog expire" failed to honor annotated tags when computing
+ reachable commits.
+
+ * A flakey test and incorrect calls to strtoX() functions have been
+ fixed.
+
+ * Follow-up on 2.45.1 regression fix.
+
+ * "git rev-list ... | git diff-tree -p --remerge-diff --stdin" should
+ behave more or less like "git log -p --remerge-diff" but instead it
+ crashed, forgetting to prepare a temporary object store needed.
+
+ * The patch parser in "git patch-id" has been tightened to avoid
+ getting confused by lines that look like a patch header in the log
+ message.
+
+ * "git bundle unbundle" outside a repository triggered a BUG()
+ unnecessarily, which has been corrected.
+
Also contains minor documentation updates and code clean-ups.
diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt
index bd95a6c10a..d5890ae368 100644
--- a/Documentation/git-cat-file.txt
+++ b/Documentation/git-cat-file.txt
@@ -270,9 +270,9 @@ BATCH OUTPUT
------------
If `--batch` or `--batch-check` is given, `cat-file` will read objects
-from stdin, one per line, and print information about them. By default,
-the whole line is considered as an object, as if it were fed to
-linkgit:git-rev-parse[1].
+from stdin, one per line, and print information about them in the same
+order as they have been read. By default, the whole line is
+considered as an object, as if it were fed to linkgit:git-rev-parse[1].
When `--batch-command` is given, `cat-file` will read commands from stdin,
one per line, and print information based on the command given. With
diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
index 1ee5c89ba2..7f81fbbea8 100644
--- a/Documentation/git-config.txt
+++ b/Documentation/git-config.txt
@@ -130,7 +130,7 @@ OPTIONS
--all::
With `get`, return all values for a multi-valued key.
----regexp::
+--regexp::
With `get`, interpret the name as a regular expression. Regular
expression matching is currently case-sensitive and done against a
canonicalized version of the key in which section and variable names
diff --git a/Documentation/git-diff-tree.txt b/Documentation/git-diff-tree.txt
index 143318c411..09286a85eb 100644
--- a/Documentation/git-diff-tree.txt
+++ b/Documentation/git-diff-tree.txt
@@ -88,7 +88,7 @@ include::pretty-options.txt[]
--no-commit-id::
'git diff-tree' outputs a line with the commit ID when
- applicable. This flag suppressed the commit ID output.
+ applicable. This flag suppresses the commit ID output.
-c::
This flag changes the way a merge commit is displayed
diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt
index d08c7da8f4..58c529afbe 100644
--- a/Documentation/git-ls-files.txt
+++ b/Documentation/git-ls-files.txt
@@ -219,9 +219,9 @@ followed by the ("attr/<eolattr>").
--format=<format>::
A string that interpolates `%(fieldname)` from the result being shown.
- It also interpolates `%%` to `%`, and `%xx` where `xx` are hex digits
- interpolates to character with hex code `xx`; for example `%00`
- interpolates to `\0` (NUL), `%09` to `\t` (TAB) and %0a to `\n` (LF).
+ It also interpolates `%%` to `%`, and `%xXX` where `XX` are hex digits
+ interpolates to character with hex code `XX`; for example `%x00`
+ interpolates to `\0` (NUL), `%x09` to `\t` (TAB) and %x0a to `\n` (LF).
--format cannot be combined with `-s`, `-o`, `-k`, `-t`, `--resolve-undo`
and `--eol`.
\--::
diff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt
index 4759408788..f89ad30cf6 100644
--- a/Documentation/gittutorial.txt
+++ b/Documentation/gittutorial.txt
@@ -360,7 +360,7 @@ $ gitk HEAD...FETCH_HEAD
This means "show everything that is reachable from either one, but
exclude anything that is reachable from both of them".
-Please note that these range notation can be used with both `gitk`
+Please note that these range notations can be used with both `gitk`
and `git log`.
After inspecting what Bob did, if there is nothing urgent, Alice may
diff --git a/Documentation/howto/maintain-git.txt b/Documentation/howto/maintain-git.txt
index 41f54050f8..da31332f11 100644
--- a/Documentation/howto/maintain-git.txt
+++ b/Documentation/howto/maintain-git.txt
@@ -181,6 +181,10 @@ by doing the following:
$ git diff ORIG_HEAD.. ;# final review
$ make test ;# final review
+ If the tip of 'master' is updated, also generate the preformatted
+ documentation and push the out result to git-htmldocs and
+ git-manpages repositories.
+
- Handle the remaining patches:
- Anything unobvious that is applicable to 'master' (in other
diff --git a/apply.c b/apply.c
index 0f2f5dabe3..6e1060a952 100644
--- a/apply.c
+++ b/apply.c
@@ -995,6 +995,7 @@ static int parse_mode_line(const char *line, int linenr, unsigned int *mode)
*mode = strtoul(line, &end, 8);
if (end == line || !isspace(*end))
return error(_("invalid mode on line %d: %s"), linenr, line);
+ *mode = canon_mode(*mode);
return 0;
}
diff --git a/builtin/bundle.c b/builtin/bundle.c
index d5d41a8f67..86d0ed7049 100644
--- a/builtin/bundle.c
+++ b/builtin/bundle.c
@@ -207,12 +207,13 @@ static int cmd_bundle_unbundle(int argc, const char **argv, const char *prefix)
builtin_bundle_unbundle_usage, options, &bundle_file);
/* bundle internals use argv[1] as further parameters */
+ if (!startup_info->have_repository)
+ die(_("Need a repository to unbundle."));
+
if ((bundle_fd = open_bundle(bundle_file, &header, NULL)) < 0) {
ret = 1;
goto cleanup;
}
- if (!startup_info->have_repository)
- die(_("Need a repository to unbundle."));
if (progress)
strvec_pushl(&extra_index_pack_args, "-v", "--progress-title",
_("Unbundling objects"), NULL);
diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c
index 0d3c611aac..b8df1d4b79 100644
--- a/builtin/diff-tree.c
+++ b/builtin/diff-tree.c
@@ -8,6 +8,7 @@
#include "read-cache-ll.h"
#include "repository.h"
#include "revision.h"
+#include "tmp-objdir.h"
#include "tree.h"
static struct rev_info log_tree_opt;
@@ -166,6 +167,13 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
opt->diffopt.rotate_to_strict = 1;
+ if (opt->remerge_diff) {
+ opt->remerge_objdir = tmp_objdir_create("remerge-diff");
+ if (!opt->remerge_objdir)
+ die(_("unable to create temporary object directory"));
+ tmp_objdir_replace_primary_odb(opt->remerge_objdir, 1);
+ }
+
/*
* NOTE! We expect "a..b" to expand to "^a b" but it is
* perfectly valid for revision range parser to yield "b ^a",
@@ -230,5 +238,10 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
diff_free(&opt->diffopt);
}
+ if (opt->remerge_diff) {
+ tmp_objdir_destroy(opt->remerge_objdir);
+ opt->remerge_objdir = NULL;
+ }
+
return diff_result_code(&opt->diffopt);
}
diff --git a/builtin/get-tar-commit-id.c b/builtin/get-tar-commit-id.c
index 66a7389f9f..7195a072ed 100644
--- a/builtin/get-tar-commit-id.c
+++ b/builtin/get-tar-commit-id.c
@@ -35,6 +35,7 @@ int cmd_get_tar_commit_id(int argc, const char **argv UNUSED, const char *prefix
if (header->typeflag[0] != TYPEFLAG_GLOBAL_HEADER)
return 1;
+ errno = 0;
len = strtol(content, &end, 10);
if (errno == ERANGE || end == content || len < 0)
return 1;
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index fd968d673d..763b01372a 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -1868,6 +1868,15 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
if (!index_name && pack_name)
index_name = derive_filename(pack_name, "pack", "idx", &index_name_buf);
+ /*
+ * Packfiles and indices do not carry enough information to be able to
+ * identify their object hash. So when we are neither in a repository
+ * nor has the user told us which object hash to use we have no other
+ * choice but to guess the object hash.
+ */
+ if (!the_repository->hash_algo)
+ repo_set_hash_algo(the_repository, GIT_HASH_SHA1);
+
opts.flags &= ~(WRITE_REV | WRITE_REV_VERIFY);
if (rev_index) {
opts.flags |= verify ? WRITE_REV_VERIFY : WRITE_REV;
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index debf2d4f88..6da63a67f5 100644
--- a/builtin/ls-remote.c
+++ b/builtin/ls-remote.c
@@ -91,6 +91,21 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
PARSE_OPT_STOP_AT_NON_OPTION);
dest = argv[0];
+ /*
+ * TODO: This is buggy, but required for transport helpers. When a
+ * transport helper advertises a "refspec", then we'd add that to a
+ * list of refspecs via `refspec_append()`, which transitively depends
+ * on `the_hash_algo`. Thus, when the hash algorithm isn't properly set
+ * up, this would lead to a segfault.
+ *
+ * We really should fix this in the transport helper logic such that we
+ * lazily parse refspec capabilities _after_ we have learned about the
+ * remote's object format. Otherwise, we may end up misparsing refspecs
+ * depending on what object hash the remote uses.
+ */
+ if (!the_repository->hash_algo)
+ repo_set_hash_algo(the_repository, GIT_HASH_SHA1);
+
packet_trace_identity("ls-remote");
if (argc > 1) {
diff --git a/builtin/patch-id.c b/builtin/patch-id.c
index d790ae6354..35c1179f7e 100644
--- a/builtin/patch-id.c
+++ b/builtin/patch-id.c
@@ -7,10 +7,9 @@
#include "parse-options.h"
#include "setup.h"
-static void flush_current_id(int patchlen, struct object_id *id, struct object_id *result)
+static void flush_current_id(struct object_id *id, struct object_id *result)
{
- if (patchlen)
- printf("%s %s\n", oid_to_hex(result), oid_to_hex(id));
+ printf("%s %s\n", oid_to_hex(result), oid_to_hex(id));
}
static int remove_space(char *line)
@@ -60,9 +59,27 @@ static int scan_hunk_header(const char *p, int *p_before, int *p_after)
return 1;
}
+/*
+ * flag bits to control get_one_patchid()'s behaviour.
+ *
+ * STABLE/VERBATIM are given from the command line option as
+ * --stable/--verbatim. FIND_HEADER conveys the internal state
+ * maintained by the caller to allow the function to avoid mistaking
+ * lines of log message before seeing the "diff" part as the beginning
+ * of the next patch.
+ */
+enum {
+ GOPID_STABLE = (1<<0), /* --stable */
+ GOPID_VERBATIM = (1<<1), /* --verbatim */
+ GOPID_FIND_HEADER = (1<<2), /* stop at the beginning of patch message */
+};
+
static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
- struct strbuf *line_buf, int stable, int verbatim)
+ struct strbuf *line_buf, unsigned flags)
{
+ int stable = flags & GOPID_STABLE;
+ int verbatim = flags & GOPID_VERBATIM;
+ int find_header = flags & GOPID_FIND_HEADER;
int patchlen = 0, found_next = 0;
int before = -1, after = -1;
int diff_is_binary = 0;
@@ -77,24 +94,40 @@ static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
const char *p = line;
int len;
- /* Possibly skip over the prefix added by "log" or "format-patch" */
- if (!skip_prefix(line, "commit ", &p) &&
- !skip_prefix(line, "From ", &p) &&
- starts_with(line, "\\ ") && 12 < strlen(line)) {
- if (verbatim)
- the_hash_algo->update_fn(&ctx, line, strlen(line));
- continue;
- }
-
- if (!get_oid_hex(p, next_oid)) {
- found_next = 1;
- break;
+ /*
+ * The caller hasn't seen us find a patch header and
+ * return to it, or we have started processing patch
+ * and may encounter the beginning of the next patch.
+ */
+ if (find_header) {
+ /*
+ * If we see a line that begins with "<object name>",
+ * "commit <object name>" or "From <object name>", it is
+ * the beginning of a patch. Return to the caller, as
+ * we are done with the one we have been processing.
+ */
+ if (skip_prefix(line, "commit ", &p))
+ ;
+ else if (skip_prefix(line, "From ", &p))
+ ;
+ if (!get_oid_hex(p, next_oid)) {
+ if (verbatim)
+ the_hash_algo->update_fn(&ctx, line, strlen(line));
+ found_next = 1;
+ break;
+ }
}
/* Ignore commit comments */
if (!patchlen && !starts_with(line, "diff "))
continue;
+ /*
+ * We are past the commit log message. Prepare to
+ * stop at the beginning of the next patch header.
+ */
+ find_header = 1;
+
/* Parsing diff header? */
if (before == -1) {
if (starts_with(line, "GIT binary patch") ||
@@ -127,6 +160,16 @@ static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
break;
}
+ /*
+ * A hunk about an incomplete line may have this
+ * marker at the end, which should just be ignored.
+ */
+ if (starts_with(line, "\\ ") && 12 < strlen(line)) {
+ if (verbatim)
+ the_hash_algo->update_fn(&ctx, line, strlen(line));
+ continue;
+ }
+
if (diff_is_binary) {
if (starts_with(line, "diff ")) {
diff_is_binary = 0;
@@ -173,17 +216,20 @@ static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
return patchlen;
}
-static void generate_id_list(int stable, int verbatim)
+static void generate_id_list(unsigned flags)
{
struct object_id oid, n, result;
int patchlen;
struct strbuf line_buf = STRBUF_INIT;
oidclr(&oid, the_repository->hash_algo);
+ flags |= GOPID_FIND_HEADER;
while (!feof(stdin)) {
- patchlen = get_one_patchid(&n, &result, &line_buf, stable, verbatim);
- flush_current_id(patchlen, &oid, &result);
+ patchlen = get_one_patchid(&n, &result, &line_buf, flags);
+ if (patchlen)
+ flush_current_id(&oid, &result);
oidcpy(&oid, &n);
+ flags &= ~GOPID_FIND_HEADER;
}
strbuf_release(&line_buf);
}
@@ -219,6 +265,7 @@ int cmd_patch_id(int argc, const char **argv, const char *prefix)
/* if nothing is set, default to unstable */
struct patch_id_opts config = {0, 0};
int opts = 0;
+ unsigned flags = 0;
struct option builtin_patch_id_options[] = {
OPT_CMDMODE(0, "unstable", &opts,
N_("use the unstable patch-id algorithm"), 1),
@@ -250,7 +297,11 @@ int cmd_patch_id(int argc, const char **argv, const char *prefix)
if (!the_hash_algo)
repo_set_hash_algo(the_repository, GIT_HASH_SHA1);
- generate_id_list(opts ? opts > 1 : config.stable,
- opts ? opts == 3 : config.verbatim);
+ if (opts ? opts > 1 : config.stable)
+ flags |= GOPID_STABLE;
+ if (opts ? opts == 3 : config.verbatim)
+ flags |= GOPID_VERBATIM;
+ generate_id_list(flags);
+
return 0;
}
diff --git a/builtin/stash.c b/builtin/stash.c
index 46b981c4dd..80ccfc7a08 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -1671,7 +1671,28 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
}
}
- if (keep_index == 1 && !is_null_oid(&info.i_tree)) {
+ /*
+ * When keeping staged entries, we need to reset the working
+ * directory to match the state of our index. This can be
+ * skipped when the index is the empty tree, because there is
+ * nothing to reset in that case:
+ *
+ * - When the index has any file, regardless of whether
+ * staged or not, the tree cannot be empty by definition
+ * and thus we enter the condition.
+ *
+ * - When the index has no files, the only thing we need to
+ * care about is untracked files when `--include-untracked`
+ * is given. But as we already execute git-clean(1) further
+ * up to delete such untracked files we don't have to do
+ * anything here, either.
+ *
+ * We thus skip calling git-checkout(1) in this case, also
+ * because running it on an empty tree will cause it to fail
+ * due to the pathspec not matching anything.
+ */
+ if (keep_index == 1 && !is_null_oid(&info.i_tree) &&
+ !is_empty_tree_oid(&info.i_tree, the_repository->hash_algo)) {
struct child_process cp = CHILD_PROCESS_INIT;
cp.git_cmd = 1;
diff --git a/bundle.c b/bundle.c
index ce164c37bc..b0a8a925cb 100644
--- a/bundle.c
+++ b/bundle.c
@@ -89,7 +89,12 @@ int read_bundle_header_fd(int fd, struct bundle_header *header,
goto abort;
}
- header->hash_algo = the_hash_algo;
+ /*
+ * The default hash format for bundles is SHA1, unless told otherwise
+ * by an "object-format=" capability, which is being handled in
+ * `parse_capability()`.
+ */
+ header->hash_algo = &hash_algos[GIT_HASH_SHA1];
/* The bundle header ends with an empty line */
while (!strbuf_getwholeline_fd(&buf, fd, '\n') &&
diff --git a/config.c b/config.c
index 6421894614..05f369ec0d 100644
--- a/config.c
+++ b/config.c
@@ -2914,7 +2914,7 @@ static int matches(const char *key, const char *value,
{
if (strcmp(key, store->key))
return 0; /* not ours */
- if (store->fixed_value)
+ if (store->fixed_value && value)
return !strcmp(store->fixed_value, value);
if (!store->value_pattern)
return 1; /* always matches */
diff --git a/contrib/credential/osxkeychain/git-credential-osxkeychain.c b/contrib/credential/osxkeychain/git-credential-osxkeychain.c
index 6ce22a28ed..1c8310d7fe 100644
--- a/contrib/credential/osxkeychain/git-credential-osxkeychain.c
+++ b/contrib/credential/osxkeychain/git-credential-osxkeychain.c
@@ -141,7 +141,7 @@ static void find_username_in_item(CFDictionaryRef item)
username_buf,
buffer_len,
ENCODING)) {
- write_item("username", username_buf, buffer_len - 1);
+ write_item("username", username_buf, strlen(username_buf));
}
free(username_buf);
}
diff --git a/csum-file.c b/csum-file.c
index 8abbf01325..2131ee6b12 100644
--- a/csum-file.c
+++ b/csum-file.c
@@ -102,6 +102,15 @@ int finalize_hashfile(struct hashfile *f, unsigned char *result,
return fd;
}
+void discard_hashfile(struct hashfile *f)
+{
+ if (0 <= f->check_fd)
+ close(f->check_fd);
+ if (0 <= f->fd)
+ close(f->fd);
+ free_hashfile(f);
+}
+
void hashwrite(struct hashfile *f, const void *buf, unsigned int count)
{
while (count) {
diff --git a/csum-file.h b/csum-file.h
index 566e05cbd2..36c7c5585f 100644
--- a/csum-file.h
+++ b/csum-file.h
@@ -47,6 +47,7 @@ struct hashfile *hashfd(int fd, const char *name);
struct hashfile *hashfd_check(const char *name);
struct hashfile *hashfd_throughput(int fd, const char *name, struct progress *tp);
int finalize_hashfile(struct hashfile *, unsigned char *, enum fsync_component, unsigned int);
+void discard_hashfile(struct hashfile *);
void hashwrite(struct hashfile *, const void *, unsigned int);
void hashflush(struct hashfile *f);
void crc32_begin(struct hashfile *);
diff --git a/log-tree.c b/log-tree.c
index 52feec4356..13524bc888 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -31,6 +31,7 @@
#include "tree.h"
#include "wildmatch.h"
#include "write-or-die.h"
+#include "pager.h"
static struct decoration name_decoration = { "object names" };
static int decoration_loaded;
@@ -411,16 +412,6 @@ void show_decorations(struct rev_info *opt, struct commit *commit)
strbuf_release(&sb);
}
-static unsigned int digits_in_number(unsigned int number)
-{
- unsigned int i = 10, result = 1;
- while (i <= number) {
- i *= 10;
- result++;
- }
- return result;
-}
-
void fmt_output_subject(struct strbuf *filename,
const char *subject,
struct rev_info *info)
@@ -464,7 +455,7 @@ void fmt_output_email_subject(struct strbuf *sb, struct rev_info *opt)
strbuf_addf(sb, "Subject: [%s%s%0*d/%d] ",
opt->subject_prefix,
*opt->subject_prefix ? " " : "",
- digits_in_number(opt->total),
+ decimal_width(opt->total),
opt->nr, opt->total);
} else if (opt->total == 0 && opt->subject_prefix && *opt->subject_prefix) {
strbuf_addf(sb, "Subject: [%s] ",
diff --git a/mailmap.c b/mailmap.c
index 2d0212f444..2acf97f307 100644
--- a/mailmap.c
+++ b/mailmap.c
@@ -201,8 +201,10 @@ static int read_mailmap_blob(struct string_list *map, const char *name)
buf = repo_read_object_file(the_repository, &oid, &type, &size);
if (!buf)
return error("unable to read mailmap object at %s", name);
- if (type != OBJ_BLOB)
+ if (type != OBJ_BLOB) {
+ free(buf);
return error("mailmap is not a blob: %s", name);
+ }
read_mailmap_string(map, buf);
diff --git a/read-cache.c b/read-cache.c
index 48bf24f87c..1f67bb755b 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -2963,7 +2963,7 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
if (err) {
free(ieot);
- return err;
+ goto cleanup;
}
offset = hashfile_total(f);
@@ -2992,8 +2992,14 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
hashwrite(f, sb.buf, sb.len);
strbuf_release(&sb);
free(ieot);
- if (err)
- return -1;
+ /*
+ * NEEDSWORK: write_index_ext_header() never returns a failure,
+ * and this part may want to be simplified.
+ */
+ if (err) {
+ err = -1;
+ goto cleanup;
+ }
}
if (write_extensions & WRITE_SPLIT_INDEX_EXTENSION &&
@@ -3008,8 +3014,14 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
sb.len) < 0;
hashwrite(f, sb.buf, sb.len);
strbuf_release(&sb);
- if (err)
- return -1;
+ /*
+ * NEEDSWORK: write_link_extension() never returns a failure,
+ * and this part may want to be simplified.
+ */
+ if (err) {
+ err = -1;
+ goto cleanup;
+ }
}
if (write_extensions & WRITE_CACHE_TREE_EXTENSION &&
!drop_cache_tree && istate->cache_tree) {
@@ -3019,8 +3031,14 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
err = write_index_ext_header(f, eoie_c, CACHE_EXT_TREE, sb.len) < 0;
hashwrite(f, sb.buf, sb.len);
strbuf_release(&sb);
- if (err)
- return -1;
+ /*
+ * NEEDSWORK: write_index_ext_header() never returns a failure,
+ * and this part may want to be simplified.
+ */
+ if (err) {
+ err = -1;
+ goto cleanup;
+ }
}
if (write_extensions & WRITE_RESOLVE_UNDO_EXTENSION &&
istate->resolve_undo) {
@@ -3031,8 +3049,14 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
sb.len) < 0;
hashwrite(f, sb.buf, sb.len);
strbuf_release(&sb);
- if (err)
- return -1;
+ /*
+ * NEEDSWORK: write_index_ext_header() never returns a failure,
+ * and this part may want to be simplified.
+ */
+ if (err) {
+ err = -1;
+ goto cleanup;
+ }
}
if (write_extensions & WRITE_UNTRACKED_CACHE_EXTENSION &&
istate->untracked) {
@@ -3043,8 +3067,14 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
sb.len) < 0;
hashwrite(f, sb.buf, sb.len);
strbuf_release(&sb);
- if (err)
- return -1;
+ /*
+ * NEEDSWORK: write_index_ext_header() never returns a failure,
+ * and this part may want to be simplified.
+ */
+ if (err) {
+ err = -1;
+ goto cleanup;
+ }
}
if (write_extensions & WRITE_FSMONITOR_EXTENSION &&
istate->fsmonitor_last_update) {
@@ -3054,12 +3084,25 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
err = write_index_ext_header(f, eoie_c, CACHE_EXT_FSMONITOR, sb.len) < 0;
hashwrite(f, sb.buf, sb.len);
strbuf_release(&sb);
- if (err)
- return -1;
+ /*
+ * NEEDSWORK: write_index_ext_header() never returns a failure,
+ * and this part may want to be simplified.
+ */
+ if (err) {
+ err = -1;
+ goto cleanup;
+ }
}
if (istate->sparse_index) {
- if (write_index_ext_header(f, eoie_c, CACHE_EXT_SPARSE_DIRECTORIES, 0) < 0)
- return -1;
+ err = write_index_ext_header(f, eoie_c, CACHE_EXT_SPARSE_DIRECTORIES, 0);
+ /*
+ * NEEDSWORK: write_index_ext_header() never returns a failure,
+ * and this part may want to be simplified.
+ */
+ if (err) {
+ err = -1;
+ goto cleanup;
+ }
}
/*
@@ -3075,8 +3118,14 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
err = write_index_ext_header(f, NULL, CACHE_EXT_ENDOFINDEXENTRIES, sb.len) < 0;
hashwrite(f, sb.buf, sb.len);
strbuf_release(&sb);
- if (err)
- return -1;
+ /*
+ * NEEDSWORK: write_index_ext_header() never returns a failure,
+ * and this part may want to be simplified.
+ */
+ if (err) {
+ err = -1;
+ goto cleanup;
+ }
}
csum_fsync_flag = 0;
@@ -3085,13 +3134,16 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
finalize_hashfile(f, istate->oid.hash, FSYNC_COMPONENT_INDEX,
CSUM_HASH_IN_STREAM | csum_fsync_flag);
+ f = NULL;
if (close_tempfile_gently(tempfile)) {
- error(_("could not close '%s'"), get_tempfile_path(tempfile));
- return -1;
+ err = error(_("could not close '%s'"), get_tempfile_path(tempfile));
+ goto cleanup;
+ }
+ if (stat(get_tempfile_path(tempfile), &st)) {
+ err = error_errno(_("could not stat '%s'"), get_tempfile_path(tempfile));
+ goto cleanup;
}
- if (stat(get_tempfile_path(tempfile), &st))
- return -1;
istate->timestamp.sec = (unsigned int)st.st_mtime;
istate->timestamp.nsec = ST_MTIME_NSEC(st);
trace_performance_since(start, "write index, changed mask = %x", istate->cache_changed);
@@ -3106,6 +3158,11 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
istate->cache_nr);
return 0;
+
+cleanup:
+ if (f)
+ discard_hashfile(f);
+ return err;
}
void set_alternate_index_output(const char *name)
diff --git a/ref-filter.c b/ref-filter.c
index 8c5e673fc0..54880a2497 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -1628,6 +1628,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
timestamp = parse_timestamp(eoemail + 2, &zone, 10);
if (timestamp == TIME_MAX)
goto bad;
+ errno = 0;
tz = strtol(zone, NULL, 10);
if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)
goto bad;
diff --git a/reflog.c b/reflog.c
index 5ca944529b..eeccd5fab4 100644
--- a/reflog.c
+++ b/reflog.c
@@ -332,7 +332,8 @@ void reflog_expiry_prepare(const char *refname,
if (!cb->cmd.expire_unreachable || is_head(refname)) {
cb->unreachable_expire_kind = UE_HEAD;
} else {
- commit = lookup_commit(the_repository, oid);
+ commit = lookup_commit_reference_gently(the_repository,
+ oid, 1);
if (commit && is_null_oid(&commit->object.oid))
commit = NULL;
cb->unreachable_expire_kind = commit ? UE_NORMAL : UE_ALWAYS;
diff --git a/refs/files-backend.c b/refs/files-backend.c
index aa52d9be7c..11551de8f8 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -157,6 +157,7 @@ static void files_ref_store_release(struct ref_store *ref_store)
free_ref_cache(refs->loose);
free(refs->gitcommondir);
ref_store_release(refs->packed_ref_store);
+ free(refs->packed_ref_store);
}
static void files_reflog_path(struct files_ref_store *refs,
diff --git a/revision.c b/revision.c
index 1c0192f522..5fecd7e0d7 100644
--- a/revision.c
+++ b/revision.c
@@ -4407,6 +4407,7 @@ static struct commit *get_revision_internal(struct rev_info *revs)
c = get_revision_1(revs);
if (!c)
break;
+ free_commit_buffer(revs->repo->parsed_objects, c);
}
}
diff --git a/setup.c b/setup.c
index d458edcc02..5f81d9fac0 100644
--- a/setup.c
+++ b/setup.c
@@ -1215,7 +1215,7 @@ static int canonicalize_ceiling_entry(struct string_list_item *item,
}
struct safe_directory_data {
- const char *path;
+ char *path;
int is_safe;
};
@@ -1235,17 +1235,45 @@ static int safe_directory_cb(const char *key, const char *value,
char *allowed = NULL;
if (!git_config_pathname(&allowed, key, value)) {
- const char *check = allowed ? allowed : value;
- if (ends_with(check, "/*")) {
- size_t len = strlen(check);
- if (!fspathncmp(check, data->path, len - 1))
+ char *normalized = NULL;
+
+ /*
+ * Setting safe.directory to a non-absolute path
+ * makes little sense---it won't be relative to
+ * the configuration file the item is defined in.
+ * Except for ".", which means "if we are at the top
+ * level of a repository, then it is OK", which is
+ * slightly tighter than "*" that allows discovery.
+ */
+ if (!is_absolute_path(allowed) && strcmp(allowed, ".")) {
+ warning(_("safe.directory '%s' not absolute"),
+ allowed);
+ goto next;
+ }
+
+ /*
+ * A .gitconfig in $HOME may be shared across
+ * different machines and safe.directory entries
+ * may or may not exist as paths on all of these
+ * machines. In other words, it is not a warning
+ * worthy event when there is no such path on this
+ * machine---the entry may be useful elsewhere.
+ */
+ normalized = real_pathdup(allowed, 0);
+ if (!normalized)
+ goto next;
+
+ if (ends_with(normalized, "/*")) {
+ size_t len = strlen(normalized);
+ if (!fspathncmp(normalized, data->path, len - 1))
data->is_safe = 1;
- } else if (!fspathcmp(data->path, check)) {
+ } else if (!fspathcmp(data->path, normalized)) {
data->is_safe = 1;
}
- }
- if (allowed != value)
+ next:
+ free(normalized);
free(allowed);
+ }
}
return 0;
@@ -1263,9 +1291,7 @@ static int ensure_valid_ownership(const char *gitfile,
const char *worktree, const char *gitdir,
struct strbuf *report)
{
- struct safe_directory_data data = {
- .path = worktree ? worktree : gitdir
- };
+ struct safe_directory_data data = { 0 };
if (!git_env_bool("GIT_TEST_ASSUME_DIFFERENT_OWNER", 0) &&
(!gitfile || is_path_owned_by_current_user(gitfile, report)) &&
@@ -1274,12 +1300,22 @@ static int ensure_valid_ownership(const char *gitfile,
return 1;
/*
+ * normalize the data.path for comparison with normalized paths
+ * that come from the configuration file. The path is unsafe
+ * if it cannot be normalized.
+ */
+ data.path = real_pathdup(worktree ? worktree : gitdir, 0);
+ if (!data.path)
+ return 0;
+
+ /*
* data.path is the "path" that identifies the repository and it is
* constant regardless of what failed above. data.is_safe should be
* initialized to false, and might be changed by the callback.
*/
git_protected_config(safe_directory_cb, &data);
+ free(data.path);
return data.is_safe;
}
diff --git a/t/helper/test-json-writer.c b/t/helper/test-json-writer.c
index ed52eb76bf..a288069b04 100644
--- a/t/helper/test-json-writer.c
+++ b/t/helper/test-json-writer.c
@@ -415,6 +415,7 @@ static void get_i(struct line *line, intmax_t *s_in)
get_s(line, &s);
+ errno = 0;
*s_in = strtol(s, &endptr, 10);
if (*endptr || errno == ERANGE)
die("line[%d]: invalid integer value", line->nr);
@@ -427,6 +428,7 @@ static void get_d(struct line *line, double *s_in)
get_s(line, &s);
+ errno = 0;
*s_in = strtod(s, &endptr);
if (*endptr || errno == ERANGE)
die("line[%d]: invalid float value", line->nr);
diff --git a/t/helper/test-trace2.c b/t/helper/test-trace2.c
index cd955ec63e..c588c273ce 100644
--- a/t/helper/test-trace2.c
+++ b/t/helper/test-trace2.c
@@ -26,6 +26,7 @@ static int get_i(int *p_value, const char *data)
if (!data || !*data)
return MyError;
+ errno = 0;
*p_value = strtol(data, &endptr, 10);
if (*endptr || errno == ERANGE)
return MyError;
diff --git a/t/t0018-advice.sh b/t/t0018-advice.sh
index 29306b367c..fac52322a7 100755
--- a/t/t0018-advice.sh
+++ b/t/t0018-advice.sh
@@ -96,7 +96,6 @@ test_expect_success 'advice should be printed when GIT_ADVICE is set to true' '
>README &&
GIT_ADVICE=true git status
) >actual &&
- cat actual > /tmp/actual &&
test_cmp expect actual
'
diff --git a/t/t0033-safe-directory.sh b/t/t0033-safe-directory.sh
index 5fe61f1291..e97a84764f 100755
--- a/t/t0033-safe-directory.sh
+++ b/t/t0033-safe-directory.sh
@@ -119,4 +119,182 @@ test_expect_success 'local clone of unowned repo accepted in safe directory' '
test_path_is_dir target
'
+test_expect_success SYMLINKS 'checked paths are normalized' '
+ test_when_finished "rm -rf repository; rm -f repo" &&
+ (
+ sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER &&
+ git config --global --unset-all safe.directory
+ ) &&
+ git init repository &&
+ ln -s repository repo &&
+ (
+ cd repository &&
+ sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER &&
+ test_commit sample
+ ) &&
+
+ (
+ sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER &&
+ git config --global safe.directory "$(pwd)/repository"
+ ) &&
+ git -C repository for-each-ref &&
+ git -C repository/ for-each-ref &&
+ git -C repo for-each-ref &&
+ git -C repo/ for-each-ref &&
+ test_must_fail git -C repository/.git for-each-ref &&
+ test_must_fail git -C repository/.git/ for-each-ref &&
+ test_must_fail git -C repo/.git for-each-ref &&
+ test_must_fail git -C repo/.git/ for-each-ref
+'
+
+test_expect_success SYMLINKS 'checked leading paths are normalized' '
+ test_when_finished "rm -rf repository; rm -f repo" &&
+ (
+ sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER &&
+ git config --global --unset-all safe.directory
+ ) &&
+ mkdir -p repository &&
+ git init repository/s &&
+ ln -s repository repo &&
+ (
+ cd repository/s &&
+ sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER &&
+ test_commit sample
+ ) &&
+
+ (
+ sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER &&
+ git config --global safe.directory "$(pwd)/repository/*"
+ ) &&
+ git -C repository/s for-each-ref &&
+ git -C repository/s/ for-each-ref &&
+ git -C repo/s for-each-ref &&
+ git -C repo/s/ for-each-ref &&
+ git -C repository/s/.git for-each-ref &&
+ git -C repository/s/.git/ for-each-ref &&
+ git -C repo/s/.git for-each-ref &&
+ git -C repo/s/.git/ for-each-ref
+'
+
+test_expect_success SYMLINKS 'configured paths are normalized' '
+ test_when_finished "rm -rf repository; rm -f repo" &&
+ (
+ sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER &&
+ git config --global --unset-all safe.directory
+ ) &&
+ git init repository &&
+ ln -s repository repo &&
+ (
+ cd repository &&
+ sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER &&
+ test_commit sample
+ ) &&
+
+ (
+ sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER &&
+ git config --global safe.directory "$(pwd)/repo"
+ ) &&
+ git -C repository for-each-ref &&
+ git -C repository/ for-each-ref &&
+ git -C repo for-each-ref &&
+ git -C repo/ for-each-ref &&
+ test_must_fail git -C repository/.git for-each-ref &&
+ test_must_fail git -C repository/.git/ for-each-ref &&
+ test_must_fail git -C repo/.git for-each-ref &&
+ test_must_fail git -C repo/.git/ for-each-ref
+'
+
+test_expect_success SYMLINKS 'configured leading paths are normalized' '
+ test_when_finished "rm -rf repository; rm -f repo" &&
+ (
+ sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER &&
+ git config --global --unset-all safe.directory
+ ) &&
+ mkdir -p repository &&
+ git init repository/s &&
+ ln -s repository repo &&
+ (
+ cd repository/s &&
+ sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER &&
+ test_commit sample
+ ) &&
+
+ (
+ sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER &&
+ git config --global safe.directory "$(pwd)/repo/*"
+ ) &&
+ git -C repository/s for-each-ref &&
+ git -C repository/s/ for-each-ref &&
+ git -C repository/s/.git for-each-ref &&
+ git -C repository/s/.git/ for-each-ref &&
+ git -C repo/s for-each-ref &&
+ git -C repo/s/ for-each-ref &&
+ git -C repo/s/.git for-each-ref &&
+ git -C repo/s/.git/ for-each-ref
+'
+
+test_expect_success 'safe.directory set to a dot' '
+ test_when_finished "rm -rf repository" &&
+ (
+ sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER &&
+ git config --global --unset-all safe.directory
+ ) &&
+ mkdir -p repository/subdir &&
+ git init repository &&
+ (
+ cd repository &&
+ sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER &&
+ test_commit sample
+ ) &&
+
+ (
+ sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER &&
+ git config --global safe.directory "."
+ ) &&
+ git -C repository for-each-ref &&
+ git -C repository/ for-each-ref &&
+ git -C repository/.git for-each-ref &&
+ git -C repository/.git/ for-each-ref &&
+
+ # What is allowed is repository/subdir but the repository
+ # path is repository.
+ test_must_fail git -C repository/subdir for-each-ref &&
+
+ # Likewise, repository .git/refs is allowed with "." but
+ # repository/.git that is accessed is not allowed.
+ test_must_fail git -C repository/.git/refs for-each-ref
+'
+
+test_expect_success 'safe.directory set to asterisk' '
+ test_when_finished "rm -rf repository" &&
+ (
+ sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER &&
+ git config --global --unset-all safe.directory
+ ) &&
+ mkdir -p repository/subdir &&
+ git init repository &&
+ (
+ cd repository &&
+ sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER &&
+ test_commit sample
+ ) &&
+
+ (
+ sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER &&
+ git config --global safe.directory "*"
+ ) &&
+ # these are trivial
+ git -C repository for-each-ref &&
+ git -C repository/ for-each-ref &&
+ git -C repository/.git for-each-ref &&
+ git -C repository/.git/ for-each-ref &&
+
+ # With "*", everything is allowed, and the repository is
+ # discovered, which is different behaviour from "." above.
+ git -C repository/subdir for-each-ref &&
+
+ # Likewise.
+ git -C repository/.git/refs for-each-ref
+'
+
test_done
diff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh
index 88c524f655..48a1550371 100755
--- a/t/t1001-read-tree-m-2way.sh
+++ b/t/t1001-read-tree-m-2way.sh
@@ -397,7 +397,7 @@ test_expect_success 'a/b vs a, plus c/d case setup.' '
test_expect_success 'a/b vs a, plus c/d case test.' '
read_tree_u_must_succeed -u -m "$treeH" "$treeM" &&
- git ls-files --stage | tee >treeMcheck.out &&
+ git ls-files --stage >treeMcheck.out &&
test_cmp treeM.out treeMcheck.out
'
diff --git a/t/t1300-config.sh b/t/t1300-config.sh
index 9de2d95f06..f13277c8f3 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -2704,6 +2704,15 @@ test_expect_success '--get and --get-all with --fixed-value' '
test_must_fail git config --file=config --get-regexp --fixed-value fixed+ non-existent
'
+test_expect_success '--fixed-value with value-less configuration' '
+ test_when_finished rm -f config &&
+ cat >config <<-\EOF &&
+ [section]
+ key
+ EOF
+ git config --file=config --fixed-value section.key value pattern
+'
+
test_expect_success 'includeIf.hasconfig:remote.*.url' '
git init hasremoteurlTest &&
test_when_finished "rm -rf hasremoteurlTest" &&
diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh
index 5bf883f1e3..246a3f46ab 100755
--- a/t/t1410-reflog.sh
+++ b/t/t1410-reflog.sh
@@ -146,6 +146,14 @@ test_expect_success rewind '
test_line_count = 5 output
'
+test_expect_success 'reflog expire should not barf on an annotated tag' '
+ test_when_finished "git tag -d v0.tag || :" &&
+ git -c core.logAllRefUpdates=always \
+ tag -a -m "tag name" v0.tag main &&
+ git reflog expire --dry-run refs/tags/v0.tag 2>err &&
+ test_grep ! "error: [Oo]bject .* not a commit" err
+'
+
test_expect_success 'corrupt and check' '
corrupt $F &&
diff --git a/t/t2107-update-index-basic.sh b/t/t2107-update-index-basic.sh
index cc72ead79f..f0eab13f96 100755
--- a/t/t2107-update-index-basic.sh
+++ b/t/t2107-update-index-basic.sh
@@ -5,6 +5,7 @@ test_description='basic update-index tests
Tests for command-line parsing and basic operation.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'update-index --nonsense fails' '
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index a7f71f8126..74666ff3e4 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -1397,6 +1397,21 @@ test_expect_success 'stash --keep-index with file deleted in index does not resu
test_path_is_missing to-remove
'
+test_expect_success 'stash --keep-index --include-untracked with empty tree' '
+ test_when_finished "rm -rf empty" &&
+ git init empty &&
+ (
+ cd empty &&
+ git commit --allow-empty --message "empty" &&
+ echo content >file &&
+ git stash push --keep-index --include-untracked &&
+ test_path_is_missing file &&
+ git stash pop &&
+ echo content >expect &&
+ test_cmp expect file
+ )
+'
+
test_expect_success 'stash apply should succeed with unmodified file' '
echo base >file &&
git add file &&
diff --git a/t/t4069-remerge-diff.sh b/t/t4069-remerge-diff.sh
index 07323ebafe..ca8f999cab 100755
--- a/t/t4069-remerge-diff.sh
+++ b/t/t4069-remerge-diff.sh
@@ -110,6 +110,41 @@ test_expect_success 'can filter out additional headers with pickaxe' '
test_must_be_empty actual
'
+test_expect_success 'remerge-diff also works for git-diff-tree' '
+ # With a clean merge
+ git diff-tree -r -p --remerge-diff --no-commit-id bc_resolution >actual &&
+ test_must_be_empty actual &&
+
+ # With both a resolved conflict and an unrelated change
+ cat <<-EOF >tmp &&
+ diff --git a/numbers b/numbers
+ remerge CONFLICT (content): Merge conflict in numbers
+ index a1fb731..6875544 100644
+ --- a/numbers
+ +++ b/numbers
+ @@ -1,13 +1,9 @@
+ 1
+ 2
+ -<<<<<<< b0ed5cb (change_a)
+ -three
+ -=======
+ -tres
+ ->>>>>>> 6cd3f82 (change_b)
+ +drei
+ 4
+ 5
+ 6
+ 7
+ -eight
+ +acht
+ 9
+ EOF
+ sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >expect &&
+ git diff-tree -r -p --remerge-diff --no-commit-id ab_resolution >tmp &&
+ sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'setup non-content conflicts' '
git switch --orphan base &&
diff --git a/t/t4129-apply-samemode.sh b/t/t4129-apply-samemode.sh
index 4eb8444029..87ffd2b8e1 100755
--- a/t/t4129-apply-samemode.sh
+++ b/t/t4129-apply-samemode.sh
@@ -130,4 +130,66 @@ test_expect_success 'git apply respects core.fileMode' '
test_grep ! "has type 100644, expected 100755" err
'
+test_expect_success POSIXPERM 'patch mode for new file is canonicalized' '
+ cat >patch <<-\EOF &&
+ diff --git a/non-canon b/non-canon
+ new file mode 100660
+ --- /dev/null
+ +++ b/non-canon
+ +content
+ EOF
+ test_when_finished "git reset --hard" &&
+ (
+ umask 0 &&
+ git apply --index patch 2>err
+ ) &&
+ test_must_be_empty err &&
+ git ls-files -s -- non-canon >staged &&
+ test_grep "^100644" staged &&
+ ls -l non-canon >worktree &&
+ test_grep "^-rw-rw-rw" worktree
+'
+
+test_expect_success POSIXPERM 'patch mode for deleted file is canonicalized' '
+ test_when_finished "git reset --hard" &&
+ echo content >non-canon &&
+ chmod 666 non-canon &&
+ git add non-canon &&
+
+ cat >patch <<-\EOF &&
+ diff --git a/non-canon b/non-canon
+ deleted file mode 100660
+ --- a/non-canon
+ +++ /dev/null
+ @@ -1 +0,0 @@
+ -content
+ EOF
+ git apply --index patch 2>err &&
+ test_must_be_empty err &&
+ git ls-files -- non-canon >staged &&
+ test_must_be_empty staged &&
+ test_path_is_missing non-canon
+'
+
+test_expect_success POSIXPERM 'patch mode for mode change is canonicalized' '
+ test_when_finished "git reset --hard" &&
+ echo content >non-canon &&
+ git add non-canon &&
+
+ cat >patch <<-\EOF &&
+ diff --git a/non-canon b/non-canon
+ old mode 100660
+ new mode 100770
+ EOF
+ (
+ umask 0 &&
+ git apply --index patch 2>err
+ ) &&
+ test_must_be_empty err &&
+ git ls-files -s -- non-canon >staged &&
+ test_grep "^100755" staged &&
+ ls -l non-canon >worktree &&
+ test_grep "^-rwxrwxrwx" worktree
+'
+
test_done
diff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh
index 8a88dd7900..79e5f42760 100755
--- a/t/t4203-mailmap.sh
+++ b/t/t4203-mailmap.sh
@@ -5,6 +5,7 @@ test_description='.mailmap configurations'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup commits and contacts file' '
diff --git a/t/t4204-patch-id.sh b/t/t4204-patch-id.sh
index 605faea0c7..dc8ddb10af 100755
--- a/t/t4204-patch-id.sh
+++ b/t/t4204-patch-id.sh
@@ -114,6 +114,46 @@ test_expect_success 'patch-id supports git-format-patch output' '
test "$2" = $(git rev-parse HEAD)
'
+test_expect_success 'patch-id computes the same for various formats' '
+ # This test happens to consider "git log -p -1" output
+ # the canonical input format, so use it as the norm.
+ git log -1 -p same >log-p.output &&
+ git patch-id <log-p.output >expect &&
+
+ # format-patch begins with "From <commit object name>"
+ git format-patch -1 --stdout same >format-patch.output &&
+ git patch-id <format-patch.output >actual &&
+ test_cmp actual expect &&
+
+ # "diff-tree --stdin -p" begins with "<commit object name>"
+ same=$(git rev-parse same) &&
+ echo $same | git diff-tree --stdin -p >diff-tree.output &&
+ git patch-id <diff-tree.output >actual &&
+ test_cmp actual expect &&
+
+ # "diff-tree --stdin -v -p" begins with "commit <commit object name>"
+ echo $same | git diff-tree --stdin -p -v >diff-tree-v.output &&
+ git patch-id <diff-tree-v.output >actual &&
+ test_cmp actual expect
+'
+
+hash=$(git rev-parse same:)
+for cruft in "$hash" "commit $hash is bad" "From $hash status"
+do
+ test_expect_success "patch-id with <$cruft> in log message" '
+ git format-patch -1 --stdout same >patch-0 &&
+ git patch-id <patch-0 >expect &&
+
+ {
+ sed -e "/^$/q" patch-0 &&
+ printf "random message\n%s\n\n" "$cruft" &&
+ sed -e "1,/^$/d" patch-0
+ } >patch-cruft &&
+ git patch-id <patch-cruft >actual &&
+ test_cmp actual expect
+ '
+done
+
test_expect_success 'whitespace is irrelevant in footer' '
get_patch_id main &&
git checkout same &&
diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh
index 4ad023c846..3b9dae331a 100755
--- a/t/t5300-pack-object.sh
+++ b/t/t5300-pack-object.sh
@@ -635,4 +635,43 @@ test_expect_success 'negative window clamps to 0' '
check_deltas stderr = 0
'
+for hash in sha1 sha256
+do
+ test_expect_success "verify-pack with $hash packfile" '
+ test_when_finished "rm -rf repo" &&
+ git init --object-format=$hash repo &&
+ test_commit -C repo initial &&
+ git -C repo repack -ad &&
+ git -C repo verify-pack "$(pwd)"/repo/.git/objects/pack/*.idx &&
+ if test $hash = sha1
+ then
+ nongit git verify-pack "$(pwd)"/repo/.git/objects/pack/*.idx
+ else
+ # We have no way to identify the hash used by packfiles
+ # or indices, so we always fall back to SHA1.
+ nongit test_must_fail git verify-pack "$(pwd)"/repo/.git/objects/pack/*.idx &&
+ # But with an explicit object format we should succeed.
+ nongit git verify-pack --object-format=$hash "$(pwd)"/repo/.git/objects/pack/*.idx
+ fi
+ '
+
+ test_expect_success "index-pack outside of a $hash repository" '
+ test_when_finished "rm -rf repo" &&
+ git init --object-format=$hash repo &&
+ test_commit -C repo initial &&
+ git -C repo repack -ad &&
+ git -C repo index-pack --verify "$(pwd)"/repo/.git/objects/pack/*.pack &&
+ if test $hash = sha1
+ then
+ nongit git index-pack --verify "$(pwd)"/repo/.git/objects/pack/*.pack
+ else
+ # We have no way to identify the hash used by packfiles
+ # or indices, so we always fall back to SHA1.
+ nongit test_must_fail git index-pack --verify "$(pwd)"/repo/.git/objects/pack/*.pack 2>err &&
+ # But with an explicit object format we should succeed.
+ nongit git index-pack --object-format=$hash --verify "$(pwd)"/repo/.git/objects/pack/*.pack
+ fi
+ '
+done
+
test_done
diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh
index 42e77eb5a9..bc442ec221 100755
--- a/t/t5512-ls-remote.sh
+++ b/t/t5512-ls-remote.sh
@@ -402,4 +402,17 @@ test_expect_success 'v0 clients can handle multiple symrefs' '
test_cmp expect actual
'
+test_expect_success 'helper with refspec capability fails gracefully' '
+ mkdir test-bin &&
+ write_script test-bin/git-remote-foo <<-EOF &&
+ echo import
+ echo refspec ${SQ}*:*${SQ}
+ EOF
+ (
+ PATH="$PWD/test-bin:$PATH" &&
+ export PATH &&
+ test_must_fail nongit git ls-remote foo::bar
+ )
+'
+
test_done
diff --git a/t/t5523-push-upstream.sh b/t/t5523-push-upstream.sh
index 1f859ade16..4ad36a31e1 100755
--- a/t/t5523-push-upstream.sh
+++ b/t/t5523-push-upstream.sh
@@ -124,14 +124,14 @@ test_expect_success TTY 'push --no-progress suppresses progress' '
test_expect_success TTY 'quiet push' '
ensure_fresh_upstream &&
- test_terminal git push --quiet --no-progress upstream main 2>&1 | tee output &&
+ test_terminal git push --quiet --no-progress upstream main >output 2>&1 &&
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_terminal git push --quiet -u --no-progress upstream main >output 2>&1 &&
test_must_be_empty output
'
diff --git a/t/t6020-bundle-misc.sh b/t/t6020-bundle-misc.sh
index fe75a06572..34b5cd62c2 100755
--- a/t/t6020-bundle-misc.sh
+++ b/t/t6020-bundle-misc.sh
@@ -652,4 +652,36 @@ test_expect_success 'send a bundle to standard output' '
test_cmp expect actual
'
+test_expect_success 'unbundle outside of a repository' '
+ git bundle create some.bundle HEAD &&
+ echo "fatal: Need a repository to unbundle." >expect &&
+ nongit test_must_fail git bundle unbundle "$(pwd)/some.bundle" 2>err &&
+ test_cmp expect err
+'
+
+test_expect_success 'list-heads outside of a repository' '
+ git bundle create some.bundle HEAD &&
+ cat >expect <<-EOF &&
+ $(git rev-parse HEAD) HEAD
+ EOF
+ nongit git bundle list-heads "$(pwd)/some.bundle" >actual &&
+ test_cmp expect actual
+'
+
+for hash in sha1 sha256
+do
+ test_expect_success "list-heads with bundle using $hash" '
+ test_when_finished "rm -rf hash" &&
+ git init --object-format=$hash hash &&
+ test_commit -C hash initial &&
+ git -C hash bundle create hash.bundle HEAD &&
+
+ cat >expect <<-EOF &&
+ $(git -C hash rev-parse HEAD) HEAD
+ EOF
+ git bundle list-heads hash/hash.bundle >actual &&
+ test_cmp expect actual
+ '
+done
+
test_done
diff --git a/t/t6421-merge-partial-clone.sh b/t/t6421-merge-partial-clone.sh
index 711b709e75..b99f29ef9b 100755
--- a/t/t6421-merge-partial-clone.sh
+++ b/t/t6421-merge-partial-clone.sh
@@ -230,8 +230,9 @@ test_expect_merge_algorithm failure success 'Objects downloaded for single relev
grep fetch_count trace.output | cut -d "|" -f 9 | tr -d " ." >actual &&
test_cmp expect actual &&
- # Check the number of fetch commands exec-ed
- grep d0.*fetch.negotiationAlgorithm trace.output >fetches &&
+ # Check the number of fetch commands exec-ed by filtering trace to
+ # child_start events by the top-level program (2nd field == d0)
+ grep " d0 .* child_start .*fetch.negotiationAlgorithm" trace.output >fetches &&
test_line_count = 2 fetches &&
git rev-list --objects --all --missing=print |
@@ -318,8 +319,9 @@ test_expect_merge_algorithm failure success 'Objects downloaded when a directory
grep fetch_count trace.output | cut -d "|" -f 9 | tr -d " ." >actual &&
test_cmp expect actual &&
- # Check the number of fetch commands exec-ed
- grep d0.*fetch.negotiationAlgorithm trace.output >fetches &&
+ # Check the number of fetch commands exec-ed by filtering trace to
+ # child_start events by the top-level program (2nd field == d0)
+ grep " d0 .* child_start .*fetch.negotiationAlgorithm" trace.output >fetches &&
test_line_count = 1 fetches &&
git rev-list --objects --all --missing=print |
@@ -422,8 +424,9 @@ test_expect_merge_algorithm failure success 'Objects downloaded with lots of ren
grep fetch_count trace.output | cut -d "|" -f 9 | tr -d " ." >actual &&
test_cmp expect actual &&
- # Check the number of fetch commands exec-ed
- grep d0.*fetch.negotiationAlgorithm trace.output >fetches &&
+ # Check the number of fetch commands exec-ed by filtering trace to
+ # child_start events by the top-level program (2nd field == d0)
+ grep " d0 .* child_start .*fetch.negotiationAlgorithm" trace.output >fetches &&
test_line_count = 4 fetches &&
git rev-list --objects --all --missing=print |
diff --git a/t/unit-tests/t-example-decorate.c b/t/unit-tests/t-example-decorate.c
index a4a75db735..8bf0709c41 100644
--- a/t/unit-tests/t-example-decorate.c
+++ b/t/unit-tests/t-example-decorate.c
@@ -15,36 +15,29 @@ static void t_add(struct test_vars *vars)
{
void *ret = add_decoration(&vars->n, vars->one, &vars->decoration_a);
- if (!check(ret == NULL))
- test_msg("when adding a brand-new object, NULL should be returned");
+ check(ret == NULL);
ret = add_decoration(&vars->n, vars->two, NULL);
- if (!check(ret == NULL))
- test_msg("when adding a brand-new object, NULL should be returned");
+ check(ret == NULL);
}
static void t_readd(struct test_vars *vars)
{
void *ret = add_decoration(&vars->n, vars->one, NULL);
- if (!check(ret == &vars->decoration_a))
- test_msg("when readding an already existing object, existing decoration should be returned");
+ check(ret == &vars->decoration_a);
ret = add_decoration(&vars->n, vars->two, &vars->decoration_b);
- if (!check(ret == NULL))
- test_msg("when readding an already existing object, existing decoration should be returned");
+ check(ret == NULL);
}
static void t_lookup(struct test_vars *vars)
{
void *ret = lookup_decoration(&vars->n, vars->one);
- if (!check(ret == NULL))
- test_msg("lookup should return added declaration");
+ check(ret == NULL);
ret = lookup_decoration(&vars->n, vars->two);
- if (!check(ret == &vars->decoration_b))
- test_msg("lookup should return added declaration");
+ check(ret == &vars->decoration_b);
ret = lookup_decoration(&vars->n, vars->three);
- if (!check(ret == NULL))
- test_msg("lookup for unknown object should return NULL");
+ check(ret == NULL);
}
static void t_loop(struct test_vars *vars)
@@ -55,8 +48,7 @@ static void t_loop(struct test_vars *vars)
if (vars->n.entries[i].base)
objects_noticed++;
}
- if (!check_int(objects_noticed, ==, 2))
- test_msg("should have 2 objects");
+ check_int(objects_noticed, ==, 2);
}
int cmd_main(int argc UNUSED, const char **argv UNUSED)