summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/Makefile5
-rw-r--r--Documentation/RelNotes/2.49.0.adoc27
-rw-r--r--Documentation/config/http.adoc15
-rw-r--r--Documentation/config/merge.adoc3
-rw-r--r--Documentation/git-commit.adoc8
-rw-r--r--Documentation/git-config.adoc4
-rw-r--r--Documentation/git-merge-tree.adoc11
-rw-r--r--Documentation/git-rebase.adoc8
-rw-r--r--Documentation/git.adoc5
-rw-r--r--Documentation/gitattributes.adoc4
-rw-r--r--Documentation/merge-strategies.adoc2
-rw-r--r--Documentation/pretty-formats.adoc8
-rwxr-xr-xGIT-VERSION-GEN2
-rw-r--r--Makefile8
-rw-r--r--builtin/check-mailmap.c2
-rw-r--r--builtin/clone.c2
-rw-r--r--builtin/merge-tree.c11
-rw-r--r--builtin/update-ref.c15
-rw-r--r--builtin/update-server-info.c8
-rw-r--r--compat/mingw.c4
-rw-r--r--contrib/credential/libsecret/Makefile3
-rw-r--r--contrib/credential/osxkeychain/Makefile1
-rw-r--r--contrib/credential/wincred/Makefile3
-rw-r--r--contrib/diff-highlight/Makefile3
-rw-r--r--contrib/diff-highlight/t/Makefile5
-rw-r--r--contrib/mw-to-git/Makefile5
-rw-r--r--contrib/mw-to-git/t/Makefile3
-rw-r--r--contrib/persistent-https/Makefile5
-rw-r--r--contrib/subtree/t/Makefile5
-rw-r--r--diff.c4
-rw-r--r--dir-iterator.c24
-rw-r--r--dir-iterator.h11
-rw-r--r--git-gui/Makefile1
-rw-r--r--git-gui/po/glossary/Makefile3
-rw-r--r--hash.h23
-rw-r--r--iterator.h2
-rw-r--r--meson.build17
-rw-r--r--object-name.c18
-rw-r--r--object-name.h6
-rw-r--r--refs.c187
-rw-r--r--refs.h12
-rw-r--r--refs/debug.c20
-rw-r--r--refs/files-backend.c117
-rw-r--r--refs/iterator.c150
-rw-r--r--refs/packed-backend.c92
-rw-r--r--refs/ref-cache.c88
-rw-r--r--refs/refs-internal.h53
-rw-r--r--refs/reftable-backend.c84
-rw-r--r--refspec.c34
-rw-r--r--refspec.h9
-rw-r--r--remote.c6
-rw-r--r--t/helper/test-dir-iterator.c1
-rw-r--r--t/interop/Makefile5
-rw-r--r--t/perf/Makefile5
-rwxr-xr-xt/t4203-mailmap.sh12
-rwxr-xr-xt/t4209-log-pickaxe.sh16
-rw-r--r--templates/Makefile5
-rw-r--r--xdiff/xdiffi.c3
-rw-r--r--xdiff/xemit.c2
-rw-r--r--xdiff/xhistogram.c8
-rw-r--r--xdiff/xinclude.h2
-rw-r--r--xdiff/xpatience.c3
-rw-r--r--xdiff/xutils.c4
63 files changed, 764 insertions, 448 deletions
diff --git a/Documentation/Makefile b/Documentation/Makefile
index a734c6d624..c9a7cf662f 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -1,3 +1,6 @@
+# The default target of this Makefile is...
+all::
+
# Import tree-wide shared Makefile behavior and libraries
include ../shared.mak
@@ -238,7 +241,7 @@ DEFAULT_EDITOR_SQ = $(subst ','\'',$(DEFAULT_EDITOR))
ASCIIDOC_EXTRA += -a 'git-default-editor=$(DEFAULT_EDITOR_SQ)'
endif
-all: html man
+all:: html man
html: $(DOC_HTML)
diff --git a/Documentation/RelNotes/2.49.0.adoc b/Documentation/RelNotes/2.49.0.adoc
index 64323ec4a9..2e9aa0d69f 100644
--- a/Documentation/RelNotes/2.49.0.adoc
+++ b/Documentation/RelNotes/2.49.0.adoc
@@ -33,6 +33,11 @@ UI, Workflows & Features
* "git rev-list --missing=" learned to accept "print-info" that gives
known details expected of the missing objects, like path and type.
+ * Comes with an updated "gitk".
+
+ * The documentation of "git commit" and "git rebase" now refer to
+ commit titles as such, not "subject".
+
Performance, Internal Implementation, Development Support etc.
--------------------------------------------------------------
@@ -202,6 +207,25 @@ Fixes since v2.48
* A thunderbird helper script lost its bashism.
(merge 59d26bd961 bc/contrib-thunderbird-patch-inline-fix later to maint).
+ * The -G/-S options to the "diff" family of commands caused us to hit
+ a BUG() when they get no values; they have been corrected.
+ (merge a620046b29 bc/diff-reject-empty-arg-to-pickaxe later to maint).
+
+ * "git merge-tree --stdin" has been improved (including a workaround
+ for a deadlock).
+ (merge 6a9ae81015 pw/merge-tree-stdin-deadlock-fix later to maint).
+
+ * Correct the default target in Documentation/Makefile, and
+ future-proof all Makefiles from similar breakages by declaring the
+ default target (which happens to be "all") upfront.
+ (merge 5309c1e9fb ad/set-default-target-in-makefiles later to maint).
+
+ * "git check-mailmap" used to segfault when queried without human
+ readable name.
+ (merge bb60c52131 jk/check-mailmap-wo-name-fix later to maint).
+
+ * Support for renaming of symbolic links on Windows has been improved.
+
* Other code cleanup, docfix, build fix, etc.
(merge ddb5287894 jk/t7407-use-test-grep later to maint).
(merge 21e1b44865 aj/difftool-config-doc-fix later to maint).
@@ -217,3 +241,6 @@ Fixes since v2.48
(merge 087740d65a ps/leakfixes-0129 later to maint).
(merge 6bba6f604b jp/doc-trailer-config later to maint).
(merge f1cc562b77 lo/t7603-path-is-file-update later to maint).
+ (merge 45761988ac en/doc-renormalize later to maint).
+ (merge 832f56f06a jc/doc-boolean-synonyms later to maint).
+ (merge 3eeed876a9 ac/doc-http-ssl-type-config later to maint).
diff --git a/Documentation/config/http.adoc b/Documentation/config/http.adoc
index a14371b5c9..22a8803dea 100644
--- a/Documentation/config/http.adoc
+++ b/Documentation/config/http.adoc
@@ -216,6 +216,21 @@ http.sslBackend::
This option is ignored if cURL lacks support for choosing the SSL
backend at runtime.
+http.sslCertType::
+ Type of client certificate used when fetching or pushing over HTTPS.
+ "PEM", "DER" are supported when using openssl or gnutls backends. "P12"
+ is supported on "openssl", "schannel", "securetransport", and gnutls 8.11+.
+ See also libcurl `CURLOPT_SSLCERTTYPE`. Can be overridden by the
+ `GIT_SSL_CERT_TYPE` environment variable.
+
+http.sslKeyType::
+ Type of client private key used when fetching or pushing over HTTPS. (e.g.
+ "PEM", "DER", or "ENG"). Only applicable when using "openssl" backend. "DER"
+ is not supported with openssl. Particularly useful when set to "ENG" for
+ authenticating with PKCS#11 tokens, with a PKCS#11 URL in sslCert option.
+ See also libcurl `CURLOPT_SSLKEYTYPE`. Can be overridden by the
+ `GIT_SSL_KEY_TYPE` environment variable.
+
http.schannelCheckRevoke::
Used to enforce or disable certificate revocation checks in cURL
when http.sslBackend is set to "schannel". Defaults to `true` if
diff --git a/Documentation/config/merge.adoc b/Documentation/config/merge.adoc
index 857de5b40b..d2d0f21a71 100644
--- a/Documentation/config/merge.adoc
+++ b/Documentation/config/merge.adoc
@@ -69,7 +69,8 @@ merge.renormalize::
Tell Git that canonical representation of files in the
repository has changed over time (e.g. earlier commits record
text files with CRLF line endings, but recent ones use LF line
- endings). In such a repository, Git can convert the data
+ endings). In such a repository, for each file where a
+ three-way content merge is needed, Git can convert the data
recorded in commits to a canonical form before performing a
merge to reduce unnecessary conflicts. For more information,
see section "Merging branches with differing checkin/checkout
diff --git a/Documentation/git-commit.adoc b/Documentation/git-commit.adoc
index dfb78169cb..dc219025f1 100644
--- a/Documentation/git-commit.adoc
+++ b/Documentation/git-commit.adoc
@@ -98,8 +98,8 @@ OPTIONS
replaces the log message of _<commit>_ with its own log message
but makes no changes to the content of _<commit>_.
+
-The commit created by plain `--fixup=<commit>` has a subject
-composed of "fixup!" followed by the subject line from _<commit>_,
+The commit created by plain `--fixup=<commit>` has a title
+composed of "fixup!" followed by the title of _<commit>_,
and is recognized specially by `git rebase --autosquash`. The `-m`
option may be used to supplement the log message of the created
commit, but the additional commentary will be thrown away once the
@@ -107,7 +107,7 @@ commit, but the additional commentary will be thrown away once the
`git rebase --autosquash`.
+
The commit created by `--fixup=amend:<commit>` is similar but its
-subject is instead prefixed with "amend!". The log message of
+title is instead prefixed with "amend!". The log message of
_<commit>_ is copied into the log message of the "amend!" commit and
opened in an editor so it can be refined. When `git rebase
--autosquash` squashes the "amend!" commit into _<commit>_, the
@@ -128,7 +128,7 @@ See linkgit:git-rebase[1] for details.
`--squash=<commit>`::
Construct a commit message for use with `git rebase --autosquash`.
- The commit message subject line is taken from the specified
+ The commit message title is taken from the specified
commit with a prefix of "squash! ". Can be used with additional
commit message options (`-m`/`-c`/`-C`/`-F`). See
linkgit:git-rebase[1] for details.
diff --git a/Documentation/git-config.adoc b/Documentation/git-config.adoc
index 888f8ba54b..936e0c5130 100644
--- a/Documentation/git-config.adoc
+++ b/Documentation/git-config.adoc
@@ -213,7 +213,9 @@ See also <<FILES>>.
+
Valid `<type>`'s include:
+
-- 'bool': canonicalize values as either "true" or "false".
+- 'bool': canonicalize values `true`, `yes`,`on`, and positive
+ numbers as "true", and values `false`, `no`, `off` and `0` as
+ "false".
- 'int': canonicalize values as simple decimal numbers. An optional suffix of
'k', 'm', or 'g' will cause the value to be multiplied by 1024, 1048576, or
1073741824 upon input.
diff --git a/Documentation/git-merge-tree.adoc b/Documentation/git-merge-tree.adoc
index 0b6a8a19b1..cf0578f9b5 100644
--- a/Documentation/git-merge-tree.adoc
+++ b/Documentation/git-merge-tree.adoc
@@ -40,11 +40,17 @@ After the merge completes, a new toplevel tree object is created. See
OPTIONS
-------
+--stdin::
+ Read the commits to merge from the standard input rather than
+ the command-line. See <<INPUT,INPUT FORMAT>> below for more
+ information. Implies `-z`.
+
-z::
Do not quote filenames in the <Conflicted file info> section,
and end each filename with a NUL character rather than
newline. Also begin the messages section with a NUL character
- instead of a newline. See <<OUTPUT>> below for more information.
+ instead of a newline. See <<OUTPUT,OUTPUT>> below for more
+ information.
--name-only::
In the Conflicted file info section, instead of writing a list
@@ -116,8 +122,6 @@ This is an integer status followed by a NUL character. The integer status is:
0: merge had conflicts
1: merge was clean
- <0: something prevented the merge from running (e.g. access to repository
- objects denied by filesystem)
[[OIDTLT]]
OID of toplevel tree
@@ -235,6 +239,7 @@ with linkgit:git-merge[1]:
* any messages that would have been printed to stdout (the
<<IM,Informational messages>>)
+[[INPUT]]
INPUT FORMAT
------------
'git merge-tree --stdin' input format is fully text based. Each line
diff --git a/Documentation/git-rebase.adoc b/Documentation/git-rebase.adoc
index 133fe8c5e6..153cb69a4f 100644
--- a/Documentation/git-rebase.adoc
+++ b/Documentation/git-rebase.adoc
@@ -599,11 +599,11 @@ See also INCOMPATIBLE OPTIONS below.
--no-autosquash::
Automatically squash commits with specially formatted messages into
previous commits being rebased. If a commit message starts with
- "squash! ", "fixup! " or "amend! ", the remainder of the subject line
+ "squash! ", "fixup! " or "amend! ", the remainder of the title
is taken as a commit specifier, which matches a previous commit if it
- matches the subject line or the hash of that commit. If no commit
+ matches the title or the hash of that commit. If no commit
matches fully, matches of the specifier with the start of commit
- subjects are considered.
+ titles are considered.
+
In the rebase todo list, the actions of squash, fixup and amend commits are
changed from `pick` to `squash`, `fixup` or `fixup -C`, respectively, and they
@@ -613,7 +613,7 @@ be used to review and edit the todo list before proceeding.
The recommended way to create commits with squash markers is by using the
`--squash`, `--fixup`, `--fixup=amend:` or `--fixup=reword:` options of
linkgit:git-commit[1], which take the target commit as an argument and
-automatically fill in the subject line of the new commit from that.
+automatically fill in the title of the new commit from that.
+
Setting configuration variable `rebase.autoSquash` to true enables
auto-squashing by default for interactive rebase. The `--no-autosquash`
diff --git a/Documentation/git.adoc b/Documentation/git.adoc
index a9c1183318..743b7b00e4 100644
--- a/Documentation/git.adoc
+++ b/Documentation/git.adoc
@@ -472,8 +472,9 @@ Environment Variables
---------------------
Various Git commands pay attention to environment variables and change
their behavior. The environment variables marked as "Boolean" take
-their values the same way as Boolean valued configuration variables, e.g.
-"true", "yes", "on" and positive numbers are taken as "yes".
+their values the same way as Boolean valued configuration variables, i.e.,
+"true", "yes", "on" and positive numbers are taken as "yes", while "false",
+"no", "off", and "0" are taken as "no".
Here are the variables:
diff --git a/Documentation/gitattributes.adoc b/Documentation/gitattributes.adoc
index 5d12b78549..7eaca89972 100644
--- a/Documentation/gitattributes.adoc
+++ b/Documentation/gitattributes.adoc
@@ -701,8 +701,8 @@ where the attribute is not in place would normally cause merge
conflicts.
To prevent these unnecessary merge conflicts, Git can be told to run a
-virtual check-out and check-in of all three stages of a file when
-resolving a three-way merge by setting the `merge.renormalize`
+virtual check-out and check-in of all three stages of each file that
+needs a three-way content merge, by setting the `merge.renormalize`
configuration variable. This prevents changes caused by check-in
conversion from causing spurious merge conflicts when a converted file
is merged with an unconverted file.
diff --git a/Documentation/merge-strategies.adoc b/Documentation/merge-strategies.adoc
index 5fc54ec060..a5dc95a378 100644
--- a/Documentation/merge-strategies.adoc
+++ b/Documentation/merge-strategies.adoc
@@ -56,7 +56,7 @@ ignore-cr-at-eol;;
renormalize;;
This runs a virtual check-out and check-in of all three stages
- of a file when resolving a three-way merge. This option is
+ of any file which needs a three-way merge. This option is
meant to be used when merging branches with different clean
filters or end-of-line normalization rules. See "Merging
branches with differing checkin/checkout attributes" in
diff --git a/Documentation/pretty-formats.adoc b/Documentation/pretty-formats.adoc
index 8ee940b6a4..07475de8c3 100644
--- a/Documentation/pretty-formats.adoc
+++ b/Documentation/pretty-formats.adoc
@@ -339,10 +339,10 @@ insert an empty string unless we are traversing reflog entries (e.g., by
decoration format if `--decorate` was not already provided on the command
line.
-The boolean options accept an optional value `[=<bool-value>]`. The values
-`true`, `false`, `on`, `off` etc. are all accepted. See the "boolean"
-sub-section in "EXAMPLES" in linkgit:git-config[1]. If a boolean
-option is given with no value, it's enabled.
+The boolean options accept an optional value `[=<bool-value>]`. The
+values taken by `--type=bool` git-config[1], like `yes` and `off`,
+are all accepted. Giving a boolean option without `=<value>` is
+equivalent to giving it with `=true`.
If you add a `+` (plus sign) after '%' of a placeholder, a line-feed
is inserted immediately before the expansion if and only if the
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 56b45333c1..67fa0401cf 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,6 +1,6 @@
#!/bin/sh
-DEF_VER=v2.48.GIT
+DEF_VER=v2.49.0-rc0
LF='
'
diff --git a/Makefile b/Makefile
index bcf5ed3f85..6d45093089 100644
--- a/Makefile
+++ b/Makefile
@@ -1704,16 +1704,16 @@ IMAP_SEND_LDFLAGS += $(OPENSSL_LINK) $(OPENSSL_LIBSSL) $(LIB_4_CRYPTO)
ifdef ZLIB_NG
BASIC_CFLAGS += -DHAVE_ZLIB_NG
- ifdef ZLIB_NG_PATH
+ ifdef ZLIB_NG_PATH
BASIC_CFLAGS += -I$(ZLIB_NG_PATH)/include
EXTLIBS += $(call libpath_template,$(ZLIB_NG_PATH)/$(lib))
- endif
+ endif
EXTLIBS += -lz-ng
else
- ifdef ZLIB_PATH
+ ifdef ZLIB_PATH
BASIC_CFLAGS += -I$(ZLIB_PATH)/include
EXTLIBS += $(call libpath_template,$(ZLIB_PATH)/$(lib))
- endif
+ endif
EXTLIBS += -lz
endif
diff --git a/builtin/check-mailmap.c b/builtin/check-mailmap.c
index df00b5ee13..be2cebe121 100644
--- a/builtin/check-mailmap.c
+++ b/builtin/check-mailmap.c
@@ -35,7 +35,7 @@ static void check_mailmap(struct string_list *mailmap, const char *contact)
mail = ident.mail_begin;
maillen = ident.mail_end - ident.mail_begin;
} else {
- name = NULL;
+ name = "";
namelen = 0;
mail = contact;
maillen = strlen(contact);
diff --git a/builtin/clone.c b/builtin/clone.c
index f9a2ecbe9c..add9d8600c 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -342,6 +342,8 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
strbuf_setlen(src, src_len);
die(_("failed to iterate over '%s'"), src->buf);
}
+
+ dir_iterator_free(iter);
}
static void clone_local(const char *src_repo, const char *dest_repo)
diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c
index 9a6c8b4e4c..3ec7127b3a 100644
--- a/builtin/merge-tree.c
+++ b/builtin/merge-tree.c
@@ -18,6 +18,7 @@
#include "tree.h"
#include "config.h"
#include "strvec.h"
+#include "write-or-die.h"
static int line_termination = '\n';
@@ -575,7 +576,7 @@ int cmd_merge_tree(int argc,
};
/* Init merge options */
- init_ui_merge_options(&o.merge_options, the_repository);
+ init_basic_merge_options(&o.merge_options, the_repository);
/* Parse arguments */
original_argc = argc - 1; /* ignoring argv[0] */
@@ -600,7 +601,6 @@ int cmd_merge_tree(int argc,
line_termination = '\0';
while (strbuf_getline_lf(&buf, stdin) != EOF) {
struct strbuf **split;
- int result;
const char *input_merge_base = NULL;
split = strbuf_split(&buf, ' ');
@@ -617,15 +617,14 @@ int cmd_merge_tree(int argc,
if (input_merge_base && split[2] && split[3] && !split[4]) {
strbuf_rtrim(split[2]);
strbuf_rtrim(split[3]);
- result = real_merge(&o, input_merge_base, split[2]->buf, split[3]->buf, prefix);
+ real_merge(&o, input_merge_base, split[2]->buf, split[3]->buf, prefix);
} else if (!input_merge_base && !split[2]) {
- result = real_merge(&o, NULL, split[0]->buf, split[1]->buf, prefix);
+ real_merge(&o, NULL, split[0]->buf, split[1]->buf, prefix);
} else {
die(_("malformed input line: '%s'."), buf.buf);
}
+ maybe_flush_or_die(stdout, "stdout");
- if (result < 0)
- die(_("merging cannot continue; got unclean result of %d"), result);
strbuf_list_free(split);
}
strbuf_release(&buf);
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 4d35bdc4b4..1d541e13ad 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -179,7 +179,8 @@ static int parse_next_oid(const char **next, const char *end,
(*next)++;
*next = parse_arg(*next, &arg);
if (arg.len) {
- if (repo_get_oid(the_repository, arg.buf, oid))
+ if (repo_get_oid_with_flags(the_repository, arg.buf, oid,
+ GET_OID_SKIP_AMBIGUITY_CHECK))
goto invalid;
} else {
/* Without -z, an empty value means all zeros: */
@@ -197,7 +198,8 @@ static int parse_next_oid(const char **next, const char *end,
*next += arg.len;
if (arg.len) {
- if (repo_get_oid(the_repository, arg.buf, oid))
+ if (repo_get_oid_with_flags(the_repository, arg.buf, oid,
+ GET_OID_SKIP_AMBIGUITY_CHECK))
goto invalid;
} else if (flags & PARSE_SHA1_ALLOW_EMPTY) {
/* With -z, treat an empty value as all zeros: */
@@ -299,7 +301,8 @@ static void parse_cmd_symref_update(struct ref_transaction *transaction,
die("symref-update %s: expected old value", refname);
if (!strcmp(old_arg, "oid")) {
- if (repo_get_oid(the_repository, old_target, &old_oid))
+ if (repo_get_oid_with_flags(the_repository, old_target, &old_oid,
+ GET_OID_SKIP_AMBIGUITY_CHECK))
die("symref-update %s: invalid oid: %s", refname, old_target);
have_old_oid = 1;
@@ -772,7 +775,8 @@ int cmd_update_ref(int argc,
refname = argv[0];
value = argv[1];
oldval = argv[2];
- if (repo_get_oid(the_repository, value, &oid))
+ if (repo_get_oid_with_flags(the_repository, value, &oid,
+ GET_OID_SKIP_AMBIGUITY_CHECK))
die("%s: not a valid SHA1", value);
}
@@ -783,7 +787,8 @@ int cmd_update_ref(int argc,
* must not already exist:
*/
oidclr(&oldoid, the_repository->hash_algo);
- else if (repo_get_oid(the_repository, oldval, &oldoid))
+ else if (repo_get_oid_with_flags(the_repository, oldval, &oldoid,
+ GET_OID_SKIP_AMBIGUITY_CHECK))
die("%s: not a valid old SHA1", oldval);
}
diff --git a/builtin/update-server-info.c b/builtin/update-server-info.c
index 47a3f0bdd9..d7467290a8 100644
--- a/builtin/update-server-info.c
+++ b/builtin/update-server-info.c
@@ -1,4 +1,3 @@
-#define USE_THE_REPOSITORY_VARIABLE
#include "builtin.h"
#include "config.h"
#include "gettext.h"
@@ -13,7 +12,7 @@ static const char * const update_server_info_usage[] = {
int cmd_update_server_info(int argc,
const char **argv,
const char *prefix,
- struct repository *repo UNUSED)
+ struct repository *repo)
{
int force = 0;
struct option options[] = {
@@ -21,11 +20,12 @@ int cmd_update_server_info(int argc,
OPT_END()
};
- git_config(git_default_config, NULL);
+ if (repo)
+ repo_config(repo, git_default_config, NULL);
argc = parse_options(argc, argv, prefix, options,
update_server_info_usage, 0);
if (argc > 0)
usage_with_options(update_server_info_usage, options);
- return !!update_server_info(the_repository, force);
+ return !!update_server_info(repo, force);
}
diff --git a/compat/mingw.c b/compat/mingw.c
index 1d5b211b54..f524c54d06 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -2278,7 +2278,9 @@ repeat:
old_handle = CreateFileW(wpold, DELETE,
FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
- NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ NULL, OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
+ NULL);
if (old_handle == INVALID_HANDLE_VALUE) {
errno = err_win_to_posix(GetLastError());
return -1;
diff --git a/contrib/credential/libsecret/Makefile b/contrib/credential/libsecret/Makefile
index 3e67552cc5..97ce9c92fb 100644
--- a/contrib/credential/libsecret/Makefile
+++ b/contrib/credential/libsecret/Makefile
@@ -1,3 +1,6 @@
+# The default target of this Makefile is...
+all::
+
MAIN:=git-credential-libsecret
all:: $(MAIN)
diff --git a/contrib/credential/osxkeychain/Makefile b/contrib/credential/osxkeychain/Makefile
index 238f5f8c36..0948297e20 100644
--- a/contrib/credential/osxkeychain/Makefile
+++ b/contrib/credential/osxkeychain/Makefile
@@ -1,3 +1,4 @@
+# The default target of this Makefile is...
all:: git-credential-osxkeychain
CC = gcc
diff --git a/contrib/credential/wincred/Makefile b/contrib/credential/wincred/Makefile
index 6e992c0866..5b795fc9fe 100644
--- a/contrib/credential/wincred/Makefile
+++ b/contrib/credential/wincred/Makefile
@@ -1,4 +1,5 @@
-all: git-credential-wincred.exe
+# The default target of this Makefile is...
+all:: git-credential-wincred.exe
-include ../../../config.mak.autogen
-include ../../../config.mak
diff --git a/contrib/diff-highlight/Makefile b/contrib/diff-highlight/Makefile
index f2be7cc924..33c2ccc9f7 100644
--- a/contrib/diff-highlight/Makefile
+++ b/contrib/diff-highlight/Makefile
@@ -1,4 +1,5 @@
-all: diff-highlight
+# The default target of this Makefile is...
+all:: diff-highlight
PERL_PATH = /usr/bin/perl
-include ../../config.mak
diff --git a/contrib/diff-highlight/t/Makefile b/contrib/diff-highlight/t/Makefile
index 5ff5275496..2a98541477 100644
--- a/contrib/diff-highlight/t/Makefile
+++ b/contrib/diff-highlight/t/Makefile
@@ -1,3 +1,6 @@
+# The default target of this Makefile is...
+all::
+
-include ../../../config.mak.autogen
-include ../../../config.mak
@@ -6,7 +9,7 @@ SHELL_PATH ?= $(SHELL)
SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
-all: test
+all:: test
test: $(T)
.PHONY: help clean all test $(T)
diff --git a/contrib/mw-to-git/Makefile b/contrib/mw-to-git/Makefile
index 4e603512a3..497ac434d6 100644
--- a/contrib/mw-to-git/Makefile
+++ b/contrib/mw-to-git/Makefile
@@ -12,6 +12,9 @@
#
# make install
+# The default target of this Makefile is...
+all::
+
GIT_MEDIAWIKI_PM=Git/Mediawiki.pm
SCRIPT_PERL=git-remote-mediawiki.perl
SCRIPT_PERL+=git-mw.perl
@@ -27,7 +30,7 @@ INSTLIBDIR=$(shell $(MAKE) -C $(GIT_ROOT_DIR)/ \
DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
INSTLIBDIR_SQ = $(subst ','\'',$(INSTLIBDIR))
-all: build
+all:: build
test: all
$(MAKE) -C t
diff --git a/contrib/mw-to-git/t/Makefile b/contrib/mw-to-git/t/Makefile
index f422203fa0..6c9f377caa 100644
--- a/contrib/mw-to-git/t/Makefile
+++ b/contrib/mw-to-git/t/Makefile
@@ -8,7 +8,8 @@
#
## Test git-remote-mediawiki
-all: test
+# The default target of this Makefile is...
+all:: test
-include ../../../config.mak.autogen
-include ../../../config.mak
diff --git a/contrib/persistent-https/Makefile b/contrib/persistent-https/Makefile
index 52b84ba3d4..691737e76b 100644
--- a/contrib/persistent-https/Makefile
+++ b/contrib/persistent-https/Makefile
@@ -12,10 +12,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+# The default target of this Makefile is...
+all::
+
BUILD_LABEL=$(shell cut -d" " -f3 ../../GIT-VERSION-FILE)
TAR_OUT=$(shell go env GOOS)_$(shell go env GOARCH).tar.gz
-all: git-remote-persistent-https git-remote-persistent-https--proxy \
+all:: git-remote-persistent-https git-remote-persistent-https--proxy \
git-remote-persistent-http
git-remote-persistent-https--proxy: git-remote-persistent-https
diff --git a/contrib/subtree/t/Makefile b/contrib/subtree/t/Makefile
index 093399c788..2a85f5ee84 100644
--- a/contrib/subtree/t/Makefile
+++ b/contrib/subtree/t/Makefile
@@ -3,6 +3,9 @@
# Copyright (c) 2005 Junio C Hamano
#
+# The default target of this Makefile is...
+all::
+
-include ../../../config.mak.autogen
-include ../../../config.mak
@@ -31,7 +34,7 @@ TSVN = $(sort $(wildcard t91[0-9][0-9]-*.sh))
TGITWEB = $(sort $(wildcard t95[0-9][0-9]-*.sh))
THELPERS = $(sort $(filter-out $(T),$(wildcard *.sh)))
-all: $(DEFAULT_TEST_TARGET)
+all:: $(DEFAULT_TEST_TARGET)
test: pre-clean $(TEST_LINT)
$(MAKE) aggregate-results-and-cleanup
diff --git a/diff.c b/diff.c
index 019fb893a7..c89c15d98e 100644
--- a/diff.c
+++ b/diff.c
@@ -5493,6 +5493,8 @@ static int diff_opt_pickaxe_regex(const struct option *opt,
BUG_ON_OPT_NEG(unset);
options->pickaxe = arg;
options->pickaxe_opts |= DIFF_PICKAXE_KIND_G;
+ if (arg && !*arg)
+ return error(_("-G requires a non-empty argument"));
return 0;
}
@@ -5504,6 +5506,8 @@ static int diff_opt_pickaxe_string(const struct option *opt,
BUG_ON_OPT_NEG(unset);
options->pickaxe = arg;
options->pickaxe_opts |= DIFF_PICKAXE_KIND_S;
+ if (arg && !*arg)
+ return error(_("-S requires a non-empty argument"));
return 0;
}
diff --git a/dir-iterator.c b/dir-iterator.c
index de619846f2..857e1d9bda 100644
--- a/dir-iterator.c
+++ b/dir-iterator.c
@@ -193,9 +193,9 @@ int dir_iterator_advance(struct dir_iterator *dir_iterator)
if (S_ISDIR(iter->base.st.st_mode) && push_level(iter)) {
if (errno != ENOENT && iter->flags & DIR_ITERATOR_PEDANTIC)
- goto error_out;
+ return ITER_ERROR;
if (iter->levels_nr == 0)
- goto error_out;
+ return ITER_ERROR;
}
/* Loop until we find an entry that we can give back to the caller. */
@@ -211,11 +211,11 @@ int dir_iterator_advance(struct dir_iterator *dir_iterator)
int ret = next_directory_entry(level->dir, iter->base.path.buf, &de);
if (ret < 0) {
if (iter->flags & DIR_ITERATOR_PEDANTIC)
- goto error_out;
+ return ITER_ERROR;
continue;
} else if (ret > 0) {
if (pop_level(iter) == 0)
- return dir_iterator_abort(dir_iterator);
+ return ITER_DONE;
continue;
}
@@ -223,7 +223,7 @@ int dir_iterator_advance(struct dir_iterator *dir_iterator)
} else {
if (level->entries_idx >= level->entries.nr) {
if (pop_level(iter) == 0)
- return dir_iterator_abort(dir_iterator);
+ return ITER_DONE;
continue;
}
@@ -232,22 +232,21 @@ int dir_iterator_advance(struct dir_iterator *dir_iterator)
if (prepare_next_entry_data(iter, name)) {
if (errno != ENOENT && iter->flags & DIR_ITERATOR_PEDANTIC)
- goto error_out;
+ return ITER_ERROR;
continue;
}
return ITER_OK;
}
-
-error_out:
- dir_iterator_abort(dir_iterator);
- return ITER_ERROR;
}
-int dir_iterator_abort(struct dir_iterator *dir_iterator)
+void dir_iterator_free(struct dir_iterator *dir_iterator)
{
struct dir_iterator_int *iter = (struct dir_iterator_int *)dir_iterator;
+ if (!iter)
+ return;
+
for (; iter->levels_nr; iter->levels_nr--) {
struct dir_iterator_level *level =
&iter->levels[iter->levels_nr - 1];
@@ -266,7 +265,6 @@ int dir_iterator_abort(struct dir_iterator *dir_iterator)
free(iter->levels);
strbuf_release(&iter->base.path);
free(iter);
- return ITER_DONE;
}
struct dir_iterator *dir_iterator_begin(const char *path, unsigned int flags)
@@ -301,7 +299,7 @@ struct dir_iterator *dir_iterator_begin(const char *path, unsigned int flags)
return dir_iterator;
error_out:
- dir_iterator_abort(dir_iterator);
+ dir_iterator_free(dir_iterator);
errno = saved_errno;
return NULL;
}
diff --git a/dir-iterator.h b/dir-iterator.h
index 6d438809b6..ccd6a19734 100644
--- a/dir-iterator.h
+++ b/dir-iterator.h
@@ -28,7 +28,7 @@
*
* while ((ok = dir_iterator_advance(iter)) == ITER_OK) {
* if (want_to_stop_iteration()) {
- * ok = dir_iterator_abort(iter);
+ * ok = ITER_DONE;
* break;
* }
*
@@ -39,6 +39,7 @@
*
* if (ok != ITER_DONE)
* handle_error();
+ * dir_iterator_free(iter);
*
* Callers are allowed to modify iter->path while they are working,
* but they must restore it to its original contents before calling
@@ -107,11 +108,7 @@ struct dir_iterator *dir_iterator_begin(const char *path, unsigned int flags);
*/
int dir_iterator_advance(struct dir_iterator *iterator);
-/*
- * End the iteration before it has been exhausted. Free the
- * dir_iterator and any associated resources and return ITER_DONE. On
- * error, free the dir_iterator and return ITER_ERROR.
- */
-int dir_iterator_abort(struct dir_iterator *iterator);
+/* Free the dir_iterator and any associated resources. */
+void dir_iterator_free(struct dir_iterator *iterator);
#endif
diff --git a/git-gui/Makefile b/git-gui/Makefile
index 667c39ed56..6c5a12bc32 100644
--- a/git-gui/Makefile
+++ b/git-gui/Makefile
@@ -1,3 +1,4 @@
+# The default target of this Makefile is...
all::
# Define V=1 to have a more verbose compile.
diff --git a/git-gui/po/glossary/Makefile b/git-gui/po/glossary/Makefile
index 749aa2e7ec..e656b0d2b0 100644
--- a/git-gui/po/glossary/Makefile
+++ b/git-gui/po/glossary/Makefile
@@ -1,3 +1,6 @@
+# The default target of this Makefile is...
+update-po::
+
PO_TEMPLATE = git-gui-glossary.pot
ALL_POFILES = $(wildcard *.po)
diff --git a/hash.h b/hash.h
index 4367acfec5..5e3c462dc5 100644
--- a/hash.h
+++ b/hash.h
@@ -193,17 +193,18 @@ struct object_id {
int algo; /* XXX requires 4-byte alignment */
};
-#define GET_OID_QUIETLY 01
-#define GET_OID_COMMIT 02
-#define GET_OID_COMMITTISH 04
-#define GET_OID_TREE 010
-#define GET_OID_TREEISH 020
-#define GET_OID_BLOB 040
-#define GET_OID_FOLLOW_SYMLINKS 0100
-#define GET_OID_RECORD_PATH 0200
-#define GET_OID_ONLY_TO_DIE 04000
-#define GET_OID_REQUIRE_PATH 010000
-#define GET_OID_HASH_ANY 020000
+#define GET_OID_QUIETLY 01
+#define GET_OID_COMMIT 02
+#define GET_OID_COMMITTISH 04
+#define GET_OID_TREE 010
+#define GET_OID_TREEISH 020
+#define GET_OID_BLOB 040
+#define GET_OID_FOLLOW_SYMLINKS 0100
+#define GET_OID_RECORD_PATH 0200
+#define GET_OID_ONLY_TO_DIE 04000
+#define GET_OID_REQUIRE_PATH 010000
+#define GET_OID_HASH_ANY 020000
+#define GET_OID_SKIP_AMBIGUITY_CHECK 040000
#define GET_OID_DISAMBIGUATORS \
(GET_OID_COMMIT | GET_OID_COMMITTISH | \
diff --git a/iterator.h b/iterator.h
index 0f6900e43a..6b77dcc262 100644
--- a/iterator.h
+++ b/iterator.h
@@ -12,7 +12,7 @@
#define ITER_OK 0
/*
- * The iterator is exhausted and has been freed.
+ * The iterator is exhausted.
*/
#define ITER_DONE -1
diff --git a/meson.build b/meson.build
index bf95576f83..021a182135 100644
--- a/meson.build
+++ b/meson.build
@@ -778,7 +778,22 @@ endif
# Note that we only set NO_PERL if the Perl features were disabled by the user.
# It may not be set when we have found Perl, but only use it to run tests.
-perl = find_program('perl', version: '>=5.8.1', dirs: program_path, required: perl_required)
+#
+# At the time of writing, executing `perl --version` results in a string
+# similar to the following output:
+#
+# This is perl 5, version 40, subversion 0 (v5.40.0) built for x86_64-linux-thread-multi
+#
+# Meson picks up the "40" as version number instead of using "v5.40.0"
+# due to the regular expression it uses. This got fixed in Meson 1.7.0,
+# but meanwhile we have to either use `-V:version` instead of `--version`,
+# which we can do starting with Meson 1.5.0 and newer, or we have to
+# match against the minor version.
+if meson.version().version_compare('>=1.5.0')
+ perl = find_program('perl', dirs: program_path, required: perl_required, version: '>=5.26.0', version_argument: '-V:version')
+else
+ perl = find_program('perl', dirs: program_path, required: perl_required, version: '>=26')
+endif
perl_features_enabled = perl.found() and get_option('perl').allowed()
if perl_features_enabled
build_options_config.set('NO_PERL', '')
diff --git a/object-name.c b/object-name.c
index 945d5bdef2..85444dbb15 100644
--- a/object-name.c
+++ b/object-name.c
@@ -961,7 +961,9 @@ static int get_oid_basic(struct repository *r, const char *str, int len,
int fatal = !(flags & GET_OID_QUIETLY);
if (len == r->hash_algo->hexsz && !get_oid_hex(str, oid)) {
- if (repo_settings_get_warn_ambiguous_refs(r) && warn_on_object_refname_ambiguity) {
+ if (!(flags & GET_OID_SKIP_AMBIGUITY_CHECK) &&
+ repo_settings_get_warn_ambiguous_refs(r) &&
+ warn_on_object_refname_ambiguity) {
refs_found = repo_dwim_ref(r, str, len, &tmp_oid, &real_ref, 0);
if (refs_found > 0) {
warning(warn_msg, len, str);
@@ -1794,18 +1796,20 @@ void object_context_release(struct object_context *ctx)
strbuf_release(&ctx->symlink_path);
}
-/*
- * This is like "get_oid_basic()", except it allows "object ID expressions",
- * notably "xyz^" for "parent of xyz"
- */
-int repo_get_oid(struct repository *r, const char *name, struct object_id *oid)
+int repo_get_oid_with_flags(struct repository *r, const char *name,
+ struct object_id *oid, unsigned flags)
{
struct object_context unused;
- int ret = get_oid_with_context(r, name, 0, oid, &unused);
+ int ret = get_oid_with_context(r, name, flags, oid, &unused);
object_context_release(&unused);
return ret;
}
+int repo_get_oid(struct repository *r, const char *name, struct object_id *oid)
+{
+ return repo_get_oid_with_flags(r, name, oid, 0);
+}
+
/*
* This returns a non-zero value if the string (built using printf
* format and the given arguments) is not a valid object.
diff --git a/object-name.h b/object-name.h
index 8dba4a47a4..cda4934cd5 100644
--- a/object-name.h
+++ b/object-name.h
@@ -51,6 +51,12 @@ void strbuf_repo_add_unique_abbrev(struct strbuf *sb, struct repository *repo,
void strbuf_add_unique_abbrev(struct strbuf *sb, const struct object_id *oid,
int abbrev_len);
+/*
+ * This is like "get_oid_basic()", except it allows "object ID expressions",
+ * notably "xyz^" for "parent of xyz". Accepts GET_OID_* flags.
+ */
+int repo_get_oid_with_flags(struct repository *r, const char *str,
+ struct object_id *oid, unsigned flags);
int repo_get_oid(struct repository *r, const char *str, struct object_id *oid);
__attribute__((format (printf, 2, 3)))
int get_oidf(struct object_id *oid, const char *fmt, ...);
diff --git a/refs.c b/refs.c
index e1a6a2d189..79d5a8b8d4 100644
--- a/refs.c
+++ b/refs.c
@@ -2475,19 +2475,18 @@ int ref_transaction_commit(struct ref_transaction *transaction,
return ret;
}
-int refs_verify_refname_available(struct ref_store *refs,
- const char *refname,
- const struct string_list *extras,
- const struct string_list *skip,
- unsigned int initial_transaction,
- struct strbuf *err)
+int refs_verify_refnames_available(struct ref_store *refs,
+ const struct string_list *refnames,
+ const struct string_list *extras,
+ const struct string_list *skip,
+ unsigned int initial_transaction,
+ struct strbuf *err)
{
- const char *slash;
- const char *extra_refname;
struct strbuf dirname = STRBUF_INIT;
struct strbuf referent = STRBUF_INIT;
- struct object_id oid;
- unsigned int type;
+ struct string_list_item *item;
+ struct ref_iterator *iter = NULL;
+ struct strset dirnames;
int ret = -1;
/*
@@ -2497,86 +2496,130 @@ int refs_verify_refname_available(struct ref_store *refs,
assert(err);
- strbuf_grow(&dirname, strlen(refname) + 1);
- for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) {
- /*
- * Just saying "Is a directory" when we e.g. can't
- * lock some multi-level ref isn't very informative,
- * the user won't be told *what* is a directory, so
- * let's not use strerror() below.
- */
- int ignore_errno;
- /* Expand dirname to the new prefix, not including the trailing slash: */
- strbuf_add(&dirname, refname + dirname.len, slash - refname - dirname.len);
+ strset_init(&dirnames);
+
+ for_each_string_list_item(item, refnames) {
+ const char *refname = item->string;
+ const char *extra_refname;
+ struct object_id oid;
+ unsigned int type;
+ const char *slash;
+
+ strbuf_reset(&dirname);
+
+ for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) {
+ /*
+ * Just saying "Is a directory" when we e.g. can't
+ * lock some multi-level ref isn't very informative,
+ * the user won't be told *what* is a directory, so
+ * let's not use strerror() below.
+ */
+ int ignore_errno;
+
+ /* Expand dirname to the new prefix, not including the trailing slash: */
+ strbuf_add(&dirname, refname + dirname.len, slash - refname - dirname.len);
+
+ /*
+ * We are still at a leading dir of the refname (e.g.,
+ * "refs/foo"; if there is a reference with that name,
+ * it is a conflict, *unless* it is in skip.
+ */
+ if (skip && string_list_has_string(skip, dirname.buf))
+ continue;
+
+ /*
+ * If we've already seen the directory we don't need to
+ * process it again. Skip it to avoid checking checking
+ * common prefixes like "refs/heads/" repeatedly.
+ */
+ if (!strset_add(&dirnames, dirname.buf))
+ continue;
+
+ if (!initial_transaction &&
+ !refs_read_raw_ref(refs, dirname.buf, &oid, &referent,
+ &type, &ignore_errno)) {
+ strbuf_addf(err, _("'%s' exists; cannot create '%s'"),
+ dirname.buf, refname);
+ goto cleanup;
+ }
+
+ if (extras && string_list_has_string(extras, dirname.buf)) {
+ strbuf_addf(err, _("cannot process '%s' and '%s' at the same time"),
+ refname, dirname.buf);
+ goto cleanup;
+ }
+ }
/*
- * We are still at a leading dir of the refname (e.g.,
- * "refs/foo"; if there is a reference with that name,
- * it is a conflict, *unless* it is in skip.
+ * We are at the leaf of our refname (e.g., "refs/foo/bar").
+ * There is no point in searching for a reference with that
+ * name, because a refname isn't considered to conflict with
+ * itself. But we still need to check for references whose
+ * names are in the "refs/foo/bar/" namespace, because they
+ * *do* conflict.
*/
- if (skip && string_list_has_string(skip, dirname.buf))
- continue;
+ strbuf_addstr(&dirname, refname + dirname.len);
+ strbuf_addch(&dirname, '/');
- if (!initial_transaction &&
- !refs_read_raw_ref(refs, dirname.buf, &oid, &referent,
- &type, &ignore_errno)) {
- strbuf_addf(err, _("'%s' exists; cannot create '%s'"),
- dirname.buf, refname);
- goto cleanup;
- }
+ if (!initial_transaction) {
+ int ok;
- if (extras && string_list_has_string(extras, dirname.buf)) {
- strbuf_addf(err, _("cannot process '%s' and '%s' at the same time"),
- refname, dirname.buf);
- goto cleanup;
- }
- }
+ if (!iter) {
+ iter = refs_ref_iterator_begin(refs, dirname.buf, NULL, 0,
+ DO_FOR_EACH_INCLUDE_BROKEN);
+ } else if (ref_iterator_seek(iter, dirname.buf) < 0) {
+ goto cleanup;
+ }
- /*
- * We are at the leaf of our refname (e.g., "refs/foo/bar").
- * There is no point in searching for a reference with that
- * name, because a refname isn't considered to conflict with
- * itself. But we still need to check for references whose
- * names are in the "refs/foo/bar/" namespace, because they
- * *do* conflict.
- */
- strbuf_addstr(&dirname, refname + dirname.len);
- strbuf_addch(&dirname, '/');
-
- if (!initial_transaction) {
- struct ref_iterator *iter;
- int ok;
-
- iter = refs_ref_iterator_begin(refs, dirname.buf, NULL, 0,
- DO_FOR_EACH_INCLUDE_BROKEN);
- while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
- if (skip &&
- string_list_has_string(skip, iter->refname))
- continue;
+ while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
+ if (skip &&
+ string_list_has_string(skip, iter->refname))
+ continue;
- strbuf_addf(err, _("'%s' exists; cannot create '%s'"),
- iter->refname, refname);
- ref_iterator_abort(iter);
- goto cleanup;
+ strbuf_addf(err, _("'%s' exists; cannot create '%s'"),
+ iter->refname, refname);
+ goto cleanup;
+ }
+
+ if (ok != ITER_DONE)
+ BUG("error while iterating over references");
}
- if (ok != ITER_DONE)
- BUG("error while iterating over references");
+ extra_refname = find_descendant_ref(dirname.buf, extras, skip);
+ if (extra_refname) {
+ strbuf_addf(err, _("cannot process '%s' and '%s' at the same time"),
+ refname, extra_refname);
+ goto cleanup;
+ }
}
- extra_refname = find_descendant_ref(dirname.buf, extras, skip);
- if (extra_refname)
- strbuf_addf(err, _("cannot process '%s' and '%s' at the same time"),
- refname, extra_refname);
- else
- ret = 0;
+ ret = 0;
cleanup:
strbuf_release(&referent);
strbuf_release(&dirname);
+ strset_clear(&dirnames);
+ ref_iterator_free(iter);
return ret;
}
+int refs_verify_refname_available(struct ref_store *refs,
+ const char *refname,
+ const struct string_list *extras,
+ const struct string_list *skip,
+ unsigned int initial_transaction,
+ struct strbuf *err)
+{
+ struct string_list_item item = { .string = (char *) refname };
+ struct string_list refnames = {
+ .items = &item,
+ .nr = 1,
+ };
+
+ return refs_verify_refnames_available(refs, &refnames, extras, skip,
+ initial_transaction, err);
+}
+
struct do_for_each_reflog_help {
each_reflog_fn *fn;
void *cb_data;
diff --git a/refs.h b/refs.h
index 09be47afbe..b14ba1f9ff 100644
--- a/refs.h
+++ b/refs.h
@@ -124,6 +124,18 @@ int refs_verify_refname_available(struct ref_store *refs,
unsigned int initial_transaction,
struct strbuf *err);
+/*
+ * Same as `refs_verify_refname_available()`, but checking for a list of
+ * refnames instead of only a single item. This is more efficient in the case
+ * where one needs to check multiple refnames.
+ */
+int refs_verify_refnames_available(struct ref_store *refs,
+ const struct string_list *refnames,
+ const struct string_list *extras,
+ const struct string_list *skip,
+ unsigned int initial_transaction,
+ struct strbuf *err);
+
int refs_ref_exists(struct ref_store *refs, const char *refname);
int should_autocreate_reflog(enum log_refs_config log_all_ref_updates,
diff --git a/refs/debug.c b/refs/debug.c
index fbc4df08b4..5390fa9c18 100644
--- a/refs/debug.c
+++ b/refs/debug.c
@@ -169,6 +169,16 @@ static int debug_ref_iterator_advance(struct ref_iterator *ref_iterator)
return res;
}
+static int debug_ref_iterator_seek(struct ref_iterator *ref_iterator,
+ const char *prefix)
+{
+ struct debug_ref_iterator *diter =
+ (struct debug_ref_iterator *)ref_iterator;
+ int res = diter->iter->vtable->seek(diter->iter, prefix);
+ trace_printf_key(&trace_refs, "iterator_seek: %s: %d\n", prefix ? prefix : "", res);
+ return res;
+}
+
static int debug_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
@@ -179,19 +189,19 @@ static int debug_ref_iterator_peel(struct ref_iterator *ref_iterator,
return res;
}
-static int debug_ref_iterator_abort(struct ref_iterator *ref_iterator)
+static void debug_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct debug_ref_iterator *diter =
(struct debug_ref_iterator *)ref_iterator;
- int res = diter->iter->vtable->abort(diter->iter);
- trace_printf_key(&trace_refs, "iterator_abort: %d\n", res);
- return res;
+ diter->iter->vtable->release(diter->iter);
+ trace_printf_key(&trace_refs, "iterator_abort\n");
}
static struct ref_iterator_vtable debug_ref_iterator_vtable = {
.advance = debug_ref_iterator_advance,
+ .seek = debug_ref_iterator_seek,
.peel = debug_ref_iterator_peel,
- .abort = debug_ref_iterator_abort,
+ .release = debug_ref_iterator_release,
};
static struct ref_iterator *
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 29f08dced4..5f921e85eb 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -678,6 +678,7 @@ static void unlock_ref(struct ref_lock *lock)
*/
static int lock_raw_ref(struct files_ref_store *refs,
const char *refname, int mustexist,
+ struct string_list *refnames_to_check,
const struct string_list *extras,
struct ref_lock **lock_p,
struct strbuf *referent,
@@ -855,16 +856,11 @@ retry:
}
/*
- * If the ref did not exist and we are creating it,
- * make sure there is no existing packed ref that
- * conflicts with refname:
+ * If the ref did not exist and we are creating it, we have to
+ * make sure there is no existing packed ref that conflicts
+ * with refname. This check is deferred so that we can batch it.
*/
- if (refs_verify_refname_available(
- refs->packed_ref_store, refname,
- extras, NULL, 0, err)) {
- ret = TRANSACTION_NAME_CONFLICT;
- goto error_return;
- }
+ string_list_append(refnames_to_check, refname);
}
ret = 0;
@@ -919,13 +915,17 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator)
return ITER_OK;
}
- iter->iter0 = NULL;
- if (ref_iterator_abort(ref_iterator) != ITER_DONE)
- ok = ITER_ERROR;
-
return ok;
}
+static int files_ref_iterator_seek(struct ref_iterator *ref_iterator,
+ const char *prefix)
+{
+ struct files_ref_iterator *iter =
+ (struct files_ref_iterator *)ref_iterator;
+ return ref_iterator_seek(iter->iter0, prefix);
+}
+
static int files_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
@@ -935,23 +935,18 @@ static int files_ref_iterator_peel(struct ref_iterator *ref_iterator,
return ref_iterator_peel(iter->iter0, peeled);
}
-static int files_ref_iterator_abort(struct ref_iterator *ref_iterator)
+static void files_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct files_ref_iterator *iter =
(struct files_ref_iterator *)ref_iterator;
- int ok = ITER_DONE;
-
- if (iter->iter0)
- ok = ref_iterator_abort(iter->iter0);
-
- base_ref_iterator_free(ref_iterator);
- return ok;
+ ref_iterator_free(iter->iter0);
}
static struct ref_iterator_vtable files_ref_iterator_vtable = {
.advance = files_ref_iterator_advance,
+ .seek = files_ref_iterator_seek,
.peel = files_ref_iterator_peel,
- .abort = files_ref_iterator_abort,
+ .release = files_ref_iterator_release,
};
static struct ref_iterator *files_ref_iterator_begin(
@@ -1382,7 +1377,7 @@ static int should_pack_refs(struct files_ref_store *refs,
iter->flags, opts))
refcount++;
if (refcount >= limit) {
- ref_iterator_abort(iter);
+ ref_iterator_free(iter);
return 1;
}
}
@@ -1390,6 +1385,7 @@ static int should_pack_refs(struct files_ref_store *refs,
if (ret != ITER_DONE)
die("error while iterating over references");
+ ref_iterator_free(iter);
return 0;
}
@@ -1456,6 +1452,7 @@ static int files_pack_refs(struct ref_store *ref_store,
packed_refs_unlock(refs->packed_ref_store);
prune_refs(refs, &refs_to_prune);
+ ref_iterator_free(iter);
strbuf_release(&err);
return 0;
}
@@ -2303,35 +2300,33 @@ static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator)
return ITER_OK;
}
- iter->dir_iterator = NULL;
- if (ref_iterator_abort(ref_iterator) == ITER_ERROR)
- ok = ITER_ERROR;
return ok;
}
+static int files_reflog_iterator_seek(struct ref_iterator *ref_iterator UNUSED,
+ const char *prefix UNUSED)
+{
+ BUG("ref_iterator_seek() called for reflog_iterator");
+}
+
static int files_reflog_iterator_peel(struct ref_iterator *ref_iterator UNUSED,
struct object_id *peeled UNUSED)
{
BUG("ref_iterator_peel() called for reflog_iterator");
}
-static int files_reflog_iterator_abort(struct ref_iterator *ref_iterator)
+static void files_reflog_iterator_release(struct ref_iterator *ref_iterator)
{
struct files_reflog_iterator *iter =
(struct files_reflog_iterator *)ref_iterator;
- int ok = ITER_DONE;
-
- if (iter->dir_iterator)
- ok = dir_iterator_abort(iter->dir_iterator);
-
- base_ref_iterator_free(ref_iterator);
- return ok;
+ dir_iterator_free(iter->dir_iterator);
}
static struct ref_iterator_vtable files_reflog_iterator_vtable = {
.advance = files_reflog_iterator_advance,
+ .seek = files_reflog_iterator_seek,
.peel = files_reflog_iterator_peel,
- .abort = files_reflog_iterator_abort,
+ .release = files_reflog_iterator_release,
};
static struct ref_iterator *reflog_iterator_begin(struct ref_store *ref_store,
@@ -2569,6 +2564,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
struct ref_update *update,
struct ref_transaction *transaction,
const char *head_ref,
+ struct string_list *refnames_to_check,
struct string_list *affected_refnames,
struct strbuf *err)
{
@@ -2597,7 +2593,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
lock->count++;
} else {
ret = lock_raw_ref(refs, update->refname, mustexist,
- affected_refnames,
+ refnames_to_check, affected_refnames,
&lock, &referent,
&update->type, err);
if (ret) {
@@ -2811,6 +2807,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
size_t i;
int ret = 0;
struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
+ struct string_list refnames_to_check = STRING_LIST_INIT_NODUP;
char *head_ref = NULL;
int head_type;
struct files_transaction_backend_data *backend_data;
@@ -2898,7 +2895,8 @@ static int files_transaction_prepare(struct ref_store *ref_store,
struct ref_update *update = transaction->updates[i];
ret = lock_ref_for_update(refs, update, transaction,
- head_ref, &affected_refnames, err);
+ head_ref, &refnames_to_check,
+ &affected_refnames, err);
if (ret)
goto cleanup;
@@ -2930,6 +2928,26 @@ static int files_transaction_prepare(struct ref_store *ref_store,
}
}
+ /*
+ * Verify that none of the loose reference that we're about to write
+ * conflict with any existing packed references. Ideally, we'd do this
+ * check after the packed-refs are locked so that the file cannot
+ * change underneath our feet. But introducing such a lock now would
+ * probably do more harm than good as users rely on there not being a
+ * global lock with the "files" backend.
+ *
+ * Another alternative would be to do the check after the (optional)
+ * lock, but that would extend the time we spend in the globally-locked
+ * state.
+ *
+ * So instead, we accept the race for now.
+ */
+ if (refs_verify_refnames_available(refs->packed_ref_store, &refnames_to_check,
+ &affected_refnames, NULL, 0, err)) {
+ ret = TRANSACTION_NAME_CONFLICT;
+ goto cleanup;
+ }
+
if (packed_transaction) {
if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
ret = TRANSACTION_GENERIC_ERROR;
@@ -2972,6 +2990,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
cleanup:
free(head_ref);
string_list_clear(&affected_refnames, 0);
+ string_list_clear(&refnames_to_check, 0);
if (ret)
files_transaction_cleanup(refs, transaction);
@@ -3036,6 +3055,7 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
size_t i;
int ret = 0;
struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
+ struct string_list refnames_to_check = STRING_LIST_INIT_NODUP;
struct ref_transaction *packed_transaction = NULL;
struct ref_transaction *loose_transaction = NULL;
@@ -3085,11 +3105,7 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
!is_null_oid(&update->old_oid))
BUG("initial ref transaction with old_sha1 set");
- if (refs_verify_refname_available(&refs->base, update->refname,
- &affected_refnames, NULL, 1, err)) {
- ret = TRANSACTION_NAME_CONFLICT;
- goto cleanup;
- }
+ string_list_append(&refnames_to_check, update->refname);
/*
* packed-refs don't support symbolic refs, root refs and reflogs,
@@ -3125,8 +3141,19 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
}
}
- if (packed_refs_lock(refs->packed_ref_store, 0, err) ||
- ref_transaction_commit(packed_transaction, err)) {
+ if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
+ ret = TRANSACTION_GENERIC_ERROR;
+ goto cleanup;
+ }
+
+ if (refs_verify_refnames_available(&refs->base, &refnames_to_check,
+ &affected_refnames, NULL, 1, err)) {
+ packed_refs_unlock(refs->packed_ref_store);
+ ret = TRANSACTION_NAME_CONFLICT;
+ goto cleanup;
+ }
+
+ if (ref_transaction_commit(packed_transaction, err)) {
ret = TRANSACTION_GENERIC_ERROR;
goto cleanup;
}
@@ -3147,6 +3174,7 @@ cleanup:
ref_transaction_free(packed_transaction);
transaction->state = REF_TRANSACTION_CLOSED;
string_list_clear(&affected_refnames, 0);
+ string_list_clear(&refnames_to_check, 0);
return ret;
}
@@ -3808,6 +3836,7 @@ static int files_fsck_refs_dir(struct ref_store *ref_store,
ret = error(_("failed to iterate over '%s'"), sb.buf);
out:
+ dir_iterator_free(iter);
strbuf_release(&sb);
strbuf_release(&refname);
return ret;
diff --git a/refs/iterator.c b/refs/iterator.c
index d25e568bf0..766d96e795 100644
--- a/refs/iterator.c
+++ b/refs/iterator.c
@@ -15,15 +15,26 @@ int ref_iterator_advance(struct ref_iterator *ref_iterator)
return ref_iterator->vtable->advance(ref_iterator);
}
+int ref_iterator_seek(struct ref_iterator *ref_iterator,
+ const char *prefix)
+{
+ return ref_iterator->vtable->seek(ref_iterator, prefix);
+}
+
int ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
return ref_iterator->vtable->peel(ref_iterator, peeled);
}
-int ref_iterator_abort(struct ref_iterator *ref_iterator)
+void ref_iterator_free(struct ref_iterator *ref_iterator)
{
- return ref_iterator->vtable->abort(ref_iterator);
+ if (ref_iterator) {
+ ref_iterator->vtable->release(ref_iterator);
+ /* Help make use-after-free bugs fail quickly: */
+ ref_iterator->vtable = NULL;
+ free(ref_iterator);
+ }
}
void base_ref_iterator_init(struct ref_iterator *iter,
@@ -36,20 +47,19 @@ void base_ref_iterator_init(struct ref_iterator *iter,
iter->flags = 0;
}
-void base_ref_iterator_free(struct ref_iterator *iter)
-{
- /* Help make use-after-free bugs fail quickly: */
- iter->vtable = NULL;
- free(iter);
-}
-
struct empty_ref_iterator {
struct ref_iterator base;
};
-static int empty_ref_iterator_advance(struct ref_iterator *ref_iterator)
+static int empty_ref_iterator_advance(struct ref_iterator *ref_iterator UNUSED)
+{
+ return ITER_DONE;
+}
+
+static int empty_ref_iterator_seek(struct ref_iterator *ref_iterator UNUSED,
+ const char *prefix UNUSED)
{
- return ref_iterator_abort(ref_iterator);
+ return 0;
}
static int empty_ref_iterator_peel(struct ref_iterator *ref_iterator UNUSED,
@@ -58,16 +68,15 @@ static int empty_ref_iterator_peel(struct ref_iterator *ref_iterator UNUSED,
BUG("peel called for empty iterator");
}
-static int empty_ref_iterator_abort(struct ref_iterator *ref_iterator)
+static void empty_ref_iterator_release(struct ref_iterator *ref_iterator UNUSED)
{
- base_ref_iterator_free(ref_iterator);
- return ITER_DONE;
}
static struct ref_iterator_vtable empty_ref_iterator_vtable = {
.advance = empty_ref_iterator_advance,
+ .seek = empty_ref_iterator_seek,
.peel = empty_ref_iterator_peel,
- .abort = empty_ref_iterator_abort,
+ .release = empty_ref_iterator_release,
};
struct ref_iterator *empty_ref_iterator_begin(void)
@@ -87,7 +96,8 @@ int is_empty_ref_iterator(struct ref_iterator *ref_iterator)
struct merge_ref_iterator {
struct ref_iterator base;
- struct ref_iterator *iter0, *iter1;
+ struct ref_iterator *iter0, *iter0_owned;
+ struct ref_iterator *iter1, *iter1_owned;
ref_iterator_select_fn *select;
void *cb_data;
@@ -179,9 +189,8 @@ static int merge_ref_iterator_advance(struct ref_iterator *ref_iterator)
iter->select(iter->iter0, iter->iter1, iter->cb_data);
if (selection == ITER_SELECT_DONE) {
- return ref_iterator_abort(ref_iterator);
+ return ITER_DONE;
} else if (selection == ITER_SELECT_ERROR) {
- ref_iterator_abort(ref_iterator);
return ITER_ERROR;
}
@@ -211,10 +220,31 @@ static int merge_ref_iterator_advance(struct ref_iterator *ref_iterator)
}
error:
- ref_iterator_abort(ref_iterator);
return ITER_ERROR;
}
+static int merge_ref_iterator_seek(struct ref_iterator *ref_iterator,
+ const char *prefix)
+{
+ struct merge_ref_iterator *iter =
+ (struct merge_ref_iterator *)ref_iterator;
+ int ret;
+
+ iter->current = NULL;
+ iter->iter0 = iter->iter0_owned;
+ iter->iter1 = iter->iter1_owned;
+
+ ret = ref_iterator_seek(iter->iter0, prefix);
+ if (ret < 0)
+ return ret;
+
+ ret = ref_iterator_seek(iter->iter1, prefix);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
static int merge_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
@@ -227,28 +257,19 @@ static int merge_ref_iterator_peel(struct ref_iterator *ref_iterator,
return ref_iterator_peel(*iter->current, peeled);
}
-static int merge_ref_iterator_abort(struct ref_iterator *ref_iterator)
+static void merge_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct merge_ref_iterator *iter =
(struct merge_ref_iterator *)ref_iterator;
- int ok = ITER_DONE;
-
- if (iter->iter0) {
- if (ref_iterator_abort(iter->iter0) != ITER_DONE)
- ok = ITER_ERROR;
- }
- if (iter->iter1) {
- if (ref_iterator_abort(iter->iter1) != ITER_DONE)
- ok = ITER_ERROR;
- }
- base_ref_iterator_free(ref_iterator);
- return ok;
+ ref_iterator_free(iter->iter0_owned);
+ ref_iterator_free(iter->iter1_owned);
}
static struct ref_iterator_vtable merge_ref_iterator_vtable = {
.advance = merge_ref_iterator_advance,
+ .seek = merge_ref_iterator_seek,
.peel = merge_ref_iterator_peel,
- .abort = merge_ref_iterator_abort,
+ .release = merge_ref_iterator_release,
};
struct ref_iterator *merge_ref_iterator_begin(
@@ -267,8 +288,8 @@ struct ref_iterator *merge_ref_iterator_begin(
*/
base_ref_iterator_init(ref_iterator, &merge_ref_iterator_vtable);
- iter->iter0 = iter0;
- iter->iter1 = iter1;
+ iter->iter0 = iter->iter0_owned = iter0;
+ iter->iter1 = iter->iter1_owned = iter1;
iter->select = select;
iter->cb_data = cb_data;
iter->current = NULL;
@@ -310,10 +331,10 @@ struct ref_iterator *overlay_ref_iterator_begin(
* them.
*/
if (is_empty_ref_iterator(front)) {
- ref_iterator_abort(front);
+ ref_iterator_free(front);
return back;
} else if (is_empty_ref_iterator(back)) {
- ref_iterator_abort(back);
+ ref_iterator_free(back);
return front;
}
@@ -350,19 +371,15 @@ static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator)
while ((ok = ref_iterator_advance(iter->iter0)) == ITER_OK) {
int cmp = compare_prefix(iter->iter0->refname, iter->prefix);
-
if (cmp < 0)
continue;
-
- if (cmp > 0) {
- /*
- * As the source iterator is ordered, we
- * can stop the iteration as soon as we see a
- * refname that comes after the prefix:
- */
- ok = ref_iterator_abort(iter->iter0);
- break;
- }
+ /*
+ * As the source iterator is ordered, we
+ * can stop the iteration as soon as we see a
+ * refname that comes after the prefix:
+ */
+ if (cmp > 0)
+ return ITER_DONE;
if (iter->trim) {
/*
@@ -386,12 +403,19 @@ static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator)
return ITER_OK;
}
- iter->iter0 = NULL;
- if (ref_iterator_abort(ref_iterator) != ITER_DONE)
- return ITER_ERROR;
return ok;
}
+static int prefix_ref_iterator_seek(struct ref_iterator *ref_iterator,
+ const char *prefix)
+{
+ struct prefix_ref_iterator *iter =
+ (struct prefix_ref_iterator *)ref_iterator;
+ free(iter->prefix);
+ iter->prefix = xstrdup_or_null(prefix);
+ return ref_iterator_seek(iter->iter0, prefix);
+}
+
static int prefix_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
@@ -401,23 +425,19 @@ static int prefix_ref_iterator_peel(struct ref_iterator *ref_iterator,
return ref_iterator_peel(iter->iter0, peeled);
}
-static int prefix_ref_iterator_abort(struct ref_iterator *ref_iterator)
+static void prefix_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct prefix_ref_iterator *iter =
(struct prefix_ref_iterator *)ref_iterator;
- int ok = ITER_DONE;
-
- if (iter->iter0)
- ok = ref_iterator_abort(iter->iter0);
+ ref_iterator_free(iter->iter0);
free(iter->prefix);
- base_ref_iterator_free(ref_iterator);
- return ok;
}
static struct ref_iterator_vtable prefix_ref_iterator_vtable = {
.advance = prefix_ref_iterator_advance,
+ .seek = prefix_ref_iterator_seek,
.peel = prefix_ref_iterator_peel,
- .abort = prefix_ref_iterator_abort,
+ .release = prefix_ref_iterator_release,
};
struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
@@ -453,20 +473,14 @@ int do_for_each_ref_iterator(struct ref_iterator *iter,
current_ref_iter = iter;
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
retval = fn(iter->refname, iter->referent, iter->oid, iter->flags, cb_data);
- if (retval) {
- /*
- * If ref_iterator_abort() returns ITER_ERROR,
- * we ignore that error in deference to the
- * callback function's return value.
- */
- ref_iterator_abort(iter);
+ if (retval)
goto out;
- }
}
out:
current_ref_iter = old_ref_iter;
if (ok == ITER_ERROR)
- return -1;
+ retval = -1;
+ ref_iterator_free(iter);
return retval;
}
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index a7b6f74b6e..f4c82ba2c7 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -819,6 +819,8 @@ struct packed_ref_iterator {
struct snapshot *snapshot;
+ char *prefix;
+
/* The current position in the snapshot's buffer: */
const char *pos;
@@ -841,11 +843,9 @@ struct packed_ref_iterator {
};
/*
- * Move the iterator to the next record in the snapshot, without
- * respect for whether the record is actually required by the current
- * iteration. Adjust the fields in `iter` and return `ITER_OK` or
- * `ITER_DONE`. This function does not free the iterator in the case
- * of `ITER_DONE`.
+ * Move the iterator to the next record in the snapshot. Adjust the fields in
+ * `iter` and return `ITER_OK` or `ITER_DONE`. This function does not free the
+ * iterator in the case of `ITER_DONE`.
*/
static int next_record(struct packed_ref_iterator *iter)
{
@@ -942,6 +942,9 @@ static int packed_ref_iterator_advance(struct ref_iterator *ref_iterator)
int ok;
while ((ok = next_record(iter)) == ITER_OK) {
+ const char *refname = iter->base.refname;
+ const char *prefix = iter->prefix;
+
if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY &&
!is_per_worktree_ref(iter->base.refname))
continue;
@@ -951,15 +954,41 @@ static int packed_ref_iterator_advance(struct ref_iterator *ref_iterator)
&iter->oid, iter->flags))
continue;
+ while (prefix && *prefix) {
+ if (*refname < *prefix)
+ BUG("packed-refs backend yielded reference preceding its prefix");
+ else if (*refname > *prefix)
+ return ITER_DONE;
+ prefix++;
+ refname++;
+ }
+
return ITER_OK;
}
- if (ref_iterator_abort(ref_iterator) != ITER_DONE)
- ok = ITER_ERROR;
-
return ok;
}
+static int packed_ref_iterator_seek(struct ref_iterator *ref_iterator,
+ const char *prefix)
+{
+ struct packed_ref_iterator *iter =
+ (struct packed_ref_iterator *)ref_iterator;
+ const char *start;
+
+ if (prefix && *prefix)
+ start = find_reference_location(iter->snapshot, prefix, 0);
+ else
+ start = iter->snapshot->start;
+
+ free(iter->prefix);
+ iter->prefix = xstrdup_or_null(prefix);
+ iter->pos = start;
+ iter->eof = iter->snapshot->eof;
+
+ return 0;
+}
+
static int packed_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
@@ -976,23 +1005,21 @@ static int packed_ref_iterator_peel(struct ref_iterator *ref_iterator,
}
}
-static int packed_ref_iterator_abort(struct ref_iterator *ref_iterator)
+static void packed_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct packed_ref_iterator *iter =
(struct packed_ref_iterator *)ref_iterator;
- int ok = ITER_DONE;
-
strbuf_release(&iter->refname_buf);
free(iter->jump);
+ free(iter->prefix);
release_snapshot(iter->snapshot);
- base_ref_iterator_free(ref_iterator);
- return ok;
}
static struct ref_iterator_vtable packed_ref_iterator_vtable = {
.advance = packed_ref_iterator_advance,
+ .seek = packed_ref_iterator_seek,
.peel = packed_ref_iterator_peel,
- .abort = packed_ref_iterator_abort
+ .release = packed_ref_iterator_release,
};
static int jump_list_entry_cmp(const void *va, const void *vb)
@@ -1104,7 +1131,6 @@ static struct ref_iterator *packed_ref_iterator_begin(
{
struct packed_ref_store *refs;
struct snapshot *snapshot;
- const char *start;
struct packed_ref_iterator *iter;
struct ref_iterator *ref_iterator;
unsigned int required_flags = REF_STORE_READ;
@@ -1120,14 +1146,6 @@ static struct ref_iterator *packed_ref_iterator_begin(
*/
snapshot = get_snapshot(refs);
- if (prefix && *prefix)
- start = find_reference_location(snapshot, prefix, 0);
- else
- start = snapshot->start;
-
- if (start == snapshot->eof)
- return empty_ref_iterator_begin();
-
CALLOC_ARRAY(iter, 1);
ref_iterator = &iter->base;
base_ref_iterator_init(ref_iterator, &packed_ref_iterator_vtable);
@@ -1137,19 +1155,15 @@ static struct ref_iterator *packed_ref_iterator_begin(
iter->snapshot = snapshot;
acquire_snapshot(snapshot);
-
- iter->pos = start;
- iter->eof = snapshot->eof;
strbuf_init(&iter->refname_buf, 0);
-
iter->base.oid = &iter->oid;
-
iter->repo = ref_store->repo;
iter->flags = flags;
- if (prefix && *prefix)
- /* Stop iteration after we've gone *past* prefix: */
- ref_iterator = prefix_ref_iterator_begin(ref_iterator, prefix, 0);
+ if (packed_ref_iterator_seek(&iter->base, prefix) < 0) {
+ ref_iterator_free(&iter->base);
+ return NULL;
+ }
return ref_iterator;
}
@@ -1362,8 +1376,10 @@ static int write_with_updates(struct packed_ref_store *refs,
*/
iter = packed_ref_iterator_begin(&refs->base, "", NULL,
DO_FOR_EACH_INCLUDE_BROKEN);
- if ((ok = ref_iterator_advance(iter)) != ITER_OK)
+ if ((ok = ref_iterator_advance(iter)) != ITER_OK) {
+ ref_iterator_free(iter);
iter = NULL;
+ }
i = 0;
@@ -1411,8 +1427,10 @@ static int write_with_updates(struct packed_ref_store *refs,
* the iterator over the unneeded
* value.
*/
- if ((ok = ref_iterator_advance(iter)) != ITER_OK)
+ if ((ok = ref_iterator_advance(iter)) != ITER_OK) {
+ ref_iterator_free(iter);
iter = NULL;
+ }
cmp = +1;
} else {
/*
@@ -1449,8 +1467,10 @@ static int write_with_updates(struct packed_ref_store *refs,
peel_error ? NULL : &peeled))
goto write_error;
- if ((ok = ref_iterator_advance(iter)) != ITER_OK)
+ if ((ok = ref_iterator_advance(iter)) != ITER_OK) {
+ ref_iterator_free(iter);
iter = NULL;
+ }
} else if (is_null_oid(&update->new_oid)) {
/*
* The update wants to delete the reference,
@@ -1499,9 +1519,7 @@ write_error:
get_tempfile_path(refs->tempfile), strerror(errno));
error:
- if (iter)
- ref_iterator_abort(iter);
-
+ ref_iterator_free(iter);
delete_tempfile(&refs->tempfile);
return -1;
}
diff --git a/refs/ref-cache.c b/refs/ref-cache.c
index 02f09e4df8..c1f1bab1d5 100644
--- a/refs/ref-cache.c
+++ b/refs/ref-cache.c
@@ -362,9 +362,7 @@ struct cache_ref_iterator {
struct ref_iterator base;
/*
- * The number of levels currently on the stack. This is always
- * at least 1, because when it becomes zero the iteration is
- * ended and this struct is freed.
+ * The number of levels currently on the stack.
*/
size_t levels_nr;
@@ -376,7 +374,7 @@ struct cache_ref_iterator {
* The prefix is matched textually, without regard for path
* component boundaries.
*/
- const char *prefix;
+ char *prefix;
/*
* A stack of levels. levels[0] is the uppermost level that is
@@ -389,6 +387,9 @@ struct cache_ref_iterator {
struct cache_ref_iterator_level *levels;
struct repository *repo;
+ struct ref_cache *cache;
+
+ int prime_dir;
};
static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
@@ -396,6 +397,9 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
struct cache_ref_iterator *iter =
(struct cache_ref_iterator *)ref_iterator;
+ if (!iter->levels_nr)
+ return ITER_DONE;
+
while (1) {
struct cache_ref_iterator_level *level =
&iter->levels[iter->levels_nr - 1];
@@ -409,7 +413,7 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
if (++level->index == level->dir->nr) {
/* This level is exhausted; pop up a level */
if (--iter->levels_nr == 0)
- return ref_iterator_abort(ref_iterator);
+ return ITER_DONE;
continue;
}
@@ -444,6 +448,41 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
}
}
+static int cache_ref_iterator_seek(struct ref_iterator *ref_iterator,
+ const char *prefix)
+{
+ struct cache_ref_iterator *iter =
+ (struct cache_ref_iterator *)ref_iterator;
+ struct cache_ref_iterator_level *level;
+ struct ref_dir *dir;
+
+ dir = get_ref_dir(iter->cache->root);
+ if (prefix && *prefix)
+ dir = find_containing_dir(dir, prefix);
+ if (!dir) {
+ iter->levels_nr = 0;
+ return 0;
+ }
+
+ if (iter->prime_dir)
+ prime_ref_dir(dir, prefix);
+ iter->levels_nr = 1;
+ level = &iter->levels[0];
+ level->index = -1;
+ level->dir = dir;
+
+ if (prefix && *prefix) {
+ free(iter->prefix);
+ iter->prefix = xstrdup(prefix);
+ level->prefix_state = PREFIX_WITHIN_DIR;
+ } else {
+ FREE_AND_NULL(iter->prefix);
+ level->prefix_state = PREFIX_CONTAINS_DIR;
+ }
+
+ return 0;
+}
+
static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
@@ -452,21 +491,19 @@ static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator,
return peel_object(iter->repo, ref_iterator->oid, peeled) ? -1 : 0;
}
-static int cache_ref_iterator_abort(struct ref_iterator *ref_iterator)
+static void cache_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct cache_ref_iterator *iter =
(struct cache_ref_iterator *)ref_iterator;
-
- free((char *)iter->prefix);
+ free(iter->prefix);
free(iter->levels);
- base_ref_iterator_free(ref_iterator);
- return ITER_DONE;
}
static struct ref_iterator_vtable cache_ref_iterator_vtable = {
.advance = cache_ref_iterator_advance,
+ .seek = cache_ref_iterator_seek,
.peel = cache_ref_iterator_peel,
- .abort = cache_ref_iterator_abort
+ .release = cache_ref_iterator_release,
};
struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache,
@@ -474,39 +511,22 @@ struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache,
struct repository *repo,
int prime_dir)
{
- struct ref_dir *dir;
struct cache_ref_iterator *iter;
struct ref_iterator *ref_iterator;
- struct cache_ref_iterator_level *level;
-
- dir = get_ref_dir(cache->root);
- if (prefix && *prefix)
- dir = find_containing_dir(dir, prefix);
- if (!dir)
- /* There's nothing to iterate over. */
- return empty_ref_iterator_begin();
-
- if (prime_dir)
- prime_ref_dir(dir, prefix);
CALLOC_ARRAY(iter, 1);
ref_iterator = &iter->base;
base_ref_iterator_init(ref_iterator, &cache_ref_iterator_vtable);
ALLOC_GROW(iter->levels, 10, iter->levels_alloc);
- iter->levels_nr = 1;
- level = &iter->levels[0];
- level->index = -1;
- level->dir = dir;
+ iter->repo = repo;
+ iter->cache = cache;
+ iter->prime_dir = prime_dir;
- if (prefix && *prefix) {
- iter->prefix = xstrdup(prefix);
- level->prefix_state = PREFIX_WITHIN_DIR;
- } else {
- level->prefix_state = PREFIX_CONTAINS_DIR;
+ if (cache_ref_iterator_seek(&iter->base, prefix) < 0) {
+ ref_iterator_free(&iter->base);
+ return NULL;
}
- iter->repo = repo;
-
return ref_iterator;
}
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 8894b43d1d..e5862757a7 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -273,11 +273,11 @@ enum do_for_each_ref_flags {
* the next reference and returns ITER_OK. The data pointed at by
* refname and oid belong to the iterator; if you want to retain them
* after calling ref_iterator_advance() again or calling
- * ref_iterator_abort(), you must make a copy. When the iteration has
+ * ref_iterator_free(), you must make a copy. When the iteration has
* been exhausted, ref_iterator_advance() releases any resources
* associated with the iteration, frees the ref_iterator object, and
* returns ITER_DONE. If you want to abort the iteration early, call
- * ref_iterator_abort(), which also frees the ref_iterator object and
+ * ref_iterator_free(), which also frees the ref_iterator object and
* any associated resources. If there was an internal error advancing
* to the next entry, ref_iterator_advance() aborts the iteration,
* frees the ref_iterator, and returns ITER_ERROR.
@@ -293,7 +293,7 @@ enum do_for_each_ref_flags {
*
* while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
* if (want_to_stop_iteration()) {
- * ok = ref_iterator_abort(iter);
+ * ok = ITER_DONE;
* break;
* }
*
@@ -307,6 +307,7 @@ enum do_for_each_ref_flags {
*
* if (ok != ITER_DONE)
* handle_error();
+ * ref_iterator_free(iter);
*/
struct ref_iterator {
struct ref_iterator_vtable *vtable;
@@ -327,18 +328,30 @@ struct ref_iterator {
int ref_iterator_advance(struct ref_iterator *ref_iterator);
/*
+ * Seek the iterator to the first reference with the given prefix.
+ * The prefix is matched as a literal string, without regard for path
+ * separators. If prefix is NULL or the empty string, seek the iterator to the
+ * first reference again.
+ *
+ * This function is expected to behave as if a new ref iterator with the same
+ * prefix had been created, but allows reuse of iterators and thus may allow
+ * the backend to optimize. Parameters other than the prefix that have been
+ * passed when creating the iterator will remain unchanged.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+int ref_iterator_seek(struct ref_iterator *ref_iterator,
+ const char *prefix);
+
+/*
* If possible, peel the reference currently being viewed by the
* iterator. Return 0 on success.
*/
int ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled);
-/*
- * End the iteration before it has been exhausted, freeing the
- * reference iterator and any associated resources and returning
- * ITER_DONE. If the abort itself failed, return ITER_ERROR.
- */
-int ref_iterator_abort(struct ref_iterator *ref_iterator);
+/* Free the reference iterator and any associated resources. */
+void ref_iterator_free(struct ref_iterator *ref_iterator);
/*
* An iterator over nothing (its first ref_iterator_advance() call
@@ -438,13 +451,6 @@ struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
void base_ref_iterator_init(struct ref_iterator *iter,
struct ref_iterator_vtable *vtable);
-/*
- * Base class destructor for ref_iterators. Destroy the ref_iterator
- * part of iter and shallow-free the object. This is meant to be
- * called only by the destructors of derived classes.
- */
-void base_ref_iterator_free(struct ref_iterator *iter);
-
/* Virtual function declarations for ref_iterators: */
/*
@@ -456,6 +462,13 @@ void base_ref_iterator_free(struct ref_iterator *iter);
typedef int ref_iterator_advance_fn(struct ref_iterator *ref_iterator);
/*
+ * Seek the iterator to the first reference matching the given prefix. Should
+ * behave the same as if a new iterator was created with the same prefix.
+ */
+typedef int ref_iterator_seek_fn(struct ref_iterator *ref_iterator,
+ const char *prefix);
+
+/*
* Peels the current ref, returning 0 for success or -1 for failure.
*/
typedef int ref_iterator_peel_fn(struct ref_iterator *ref_iterator,
@@ -463,15 +476,15 @@ typedef int ref_iterator_peel_fn(struct ref_iterator *ref_iterator,
/*
* Implementations of this function should free any resources specific
- * to the derived class, then call base_ref_iterator_free() to clean
- * up and free the ref_iterator object.
+ * to the derived class.
*/
-typedef int ref_iterator_abort_fn(struct ref_iterator *ref_iterator);
+typedef void ref_iterator_release_fn(struct ref_iterator *ref_iterator);
struct ref_iterator_vtable {
ref_iterator_advance_fn *advance;
+ ref_iterator_seek_fn *seek;
ref_iterator_peel_fn *peel;
- ref_iterator_abort_fn *abort;
+ ref_iterator_release_fn *release;
};
/*
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 7e90e13f74..c8f86da731 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -547,7 +547,7 @@ struct reftable_ref_iterator {
struct reftable_ref_record ref;
struct object_id oid;
- const char *prefix;
+ char *prefix;
size_t prefix_len;
char **exclude_patterns;
size_t exclude_patterns_index;
@@ -711,20 +711,27 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
break;
}
- if (iter->err > 0) {
- if (ref_iterator_abort(ref_iterator) != ITER_DONE)
- return ITER_ERROR;
+ if (iter->err > 0)
return ITER_DONE;
- }
-
- if (iter->err < 0) {
- ref_iterator_abort(ref_iterator);
+ if (iter->err < 0)
return ITER_ERROR;
- }
-
return ITER_OK;
}
+static int reftable_ref_iterator_seek(struct ref_iterator *ref_iterator,
+ const char *prefix)
+{
+ struct reftable_ref_iterator *iter =
+ (struct reftable_ref_iterator *)ref_iterator;
+
+ free(iter->prefix);
+ iter->prefix = xstrdup_or_null(prefix);
+ iter->prefix_len = prefix ? strlen(prefix) : 0;
+ iter->err = reftable_iterator_seek_ref(&iter->iter, prefix);
+
+ return iter->err;
+}
+
static int reftable_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
@@ -740,7 +747,7 @@ static int reftable_ref_iterator_peel(struct ref_iterator *ref_iterator,
return -1;
}
-static int reftable_ref_iterator_abort(struct ref_iterator *ref_iterator)
+static void reftable_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct reftable_ref_iterator *iter =
(struct reftable_ref_iterator *)ref_iterator;
@@ -751,14 +758,14 @@ static int reftable_ref_iterator_abort(struct ref_iterator *ref_iterator)
free(iter->exclude_patterns[i]);
free(iter->exclude_patterns);
}
- free(iter);
- return ITER_DONE;
+ free(iter->prefix);
}
static struct ref_iterator_vtable reftable_ref_iterator_vtable = {
.advance = reftable_ref_iterator_advance,
+ .seek = reftable_ref_iterator_seek,
.peel = reftable_ref_iterator_peel,
- .abort = reftable_ref_iterator_abort
+ .release = reftable_ref_iterator_release,
};
static int qsort_strcmp(const void *va, const void *vb)
@@ -815,8 +822,6 @@ static struct reftable_ref_iterator *ref_iterator_for_stack(struct reftable_ref_
iter = xcalloc(1, sizeof(*iter));
base_ref_iterator_init(&iter->base, &reftable_ref_iterator_vtable);
- iter->prefix = prefix;
- iter->prefix_len = prefix ? strlen(prefix) : 0;
iter->base.oid = &iter->oid;
iter->flags = flags;
iter->refs = refs;
@@ -830,8 +835,11 @@ static struct reftable_ref_iterator *ref_iterator_for_stack(struct reftable_ref_
if (ret)
goto done;
- reftable_stack_init_ref_iterator(stack, &iter->iter);
- ret = reftable_iterator_seek_ref(&iter->iter, prefix);
+ ret = reftable_stack_init_ref_iterator(stack, &iter->iter);
+ if (ret)
+ goto done;
+
+ ret = reftable_ref_iterator_seek(&iter->base, prefix);
if (ret)
goto done;
@@ -1069,6 +1077,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
reftable_be_downcast(ref_store, REF_STORE_WRITE|REF_STORE_MAIN, "ref_transaction_prepare");
struct strbuf referent = STRBUF_INIT, head_referent = STRBUF_INIT;
struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
+ struct string_list refnames_to_check = STRING_LIST_INIT_NODUP;
struct reftable_transaction_data *tx_data = NULL;
struct reftable_backend *be;
struct object_id head_oid;
@@ -1224,12 +1233,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
* can output a proper error message instead of failing
* at a later point.
*/
- ret = refs_verify_refname_available(ref_store, u->refname,
- &affected_refnames, NULL,
- transaction->flags & REF_TRANSACTION_FLAG_INITIAL,
- err);
- if (ret < 0)
- goto done;
+ string_list_append(&refnames_to_check, u->refname);
/*
* There is no need to write the reference deletion
@@ -1379,6 +1383,12 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
}
}
+ ret = refs_verify_refnames_available(ref_store, &refnames_to_check, &affected_refnames, NULL,
+ transaction->flags & REF_TRANSACTION_FLAG_INITIAL,
+ err);
+ if (ret < 0)
+ goto done;
+
transaction->backend_data = tx_data;
transaction->state = REF_TRANSACTION_PREPARED;
@@ -1394,6 +1404,7 @@ done:
string_list_clear(&affected_refnames, 0);
strbuf_release(&referent);
strbuf_release(&head_referent);
+ string_list_clear(&refnames_to_check, 0);
return ret;
}
@@ -2017,20 +2028,20 @@ static int reftable_reflog_iterator_advance(struct ref_iterator *ref_iterator)
break;
}
- if (iter->err > 0) {
- if (ref_iterator_abort(ref_iterator) != ITER_DONE)
- return ITER_ERROR;
+ if (iter->err > 0)
return ITER_DONE;
- }
-
- if (iter->err < 0) {
- ref_iterator_abort(ref_iterator);
+ if (iter->err < 0)
return ITER_ERROR;
- }
-
return ITER_OK;
}
+static int reftable_reflog_iterator_seek(struct ref_iterator *ref_iterator UNUSED,
+ const char *prefix UNUSED)
+{
+ BUG("reftable reflog iterator cannot be seeked");
+ return -1;
+}
+
static int reftable_reflog_iterator_peel(struct ref_iterator *ref_iterator UNUSED,
struct object_id *peeled UNUSED)
{
@@ -2038,21 +2049,20 @@ static int reftable_reflog_iterator_peel(struct ref_iterator *ref_iterator UNUSE
return -1;
}
-static int reftable_reflog_iterator_abort(struct ref_iterator *ref_iterator)
+static void reftable_reflog_iterator_release(struct ref_iterator *ref_iterator)
{
struct reftable_reflog_iterator *iter =
(struct reftable_reflog_iterator *)ref_iterator;
reftable_log_record_release(&iter->log);
reftable_iterator_destroy(&iter->iter);
strbuf_release(&iter->last_name);
- free(iter);
- return ITER_DONE;
}
static struct ref_iterator_vtable reftable_reflog_iterator_vtable = {
.advance = reftable_reflog_iterator_advance,
+ .seek = reftable_reflog_iterator_seek,
.peel = reftable_reflog_iterator_peel,
- .abort = reftable_reflog_iterator_abort
+ .release = reftable_reflog_iterator_release,
};
static struct reftable_reflog_iterator *reflog_iterator_for_stack(struct reftable_ref_store *refs,
diff --git a/refspec.c b/refspec.c
index 3d6cf4dc92..4cb80b5208 100644
--- a/refspec.c
+++ b/refspec.c
@@ -269,28 +269,28 @@ void refspec_ref_prefixes(const struct refspec *rs,
}
}
-int match_name_with_pattern(const char *key, const char *name,
- const char *value, char **result)
+int match_refname_with_pattern(const char *pattern, const char *refname,
+ const char *replacement, char **result)
{
- const char *kstar = strchr(key, '*');
+ const char *kstar = strchr(pattern, '*');
size_t klen;
size_t ksuffixlen;
size_t namelen;
int ret;
if (!kstar)
- die(_("key '%s' of pattern had no '*'"), key);
- klen = kstar - key;
+ die(_("pattern '%s' has no '*'"), pattern);
+ klen = kstar - pattern;
ksuffixlen = strlen(kstar + 1);
- namelen = strlen(name);
- ret = !strncmp(name, key, klen) && namelen >= klen + ksuffixlen &&
- !memcmp(name + namelen - ksuffixlen, kstar + 1, ksuffixlen);
- if (ret && value) {
+ namelen = strlen(refname);
+ ret = !strncmp(refname, pattern, klen) && namelen >= klen + ksuffixlen &&
+ !memcmp(refname + namelen - ksuffixlen, kstar + 1, ksuffixlen);
+ if (ret && replacement) {
struct strbuf sb = STRBUF_INIT;
- const char *vstar = strchr(value, '*');
+ const char *vstar = strchr(replacement, '*');
if (!vstar)
- die(_("value '%s' of pattern has no '*'"), value);
- strbuf_add(&sb, value, vstar - value);
- strbuf_add(&sb, name + klen, namelen - klen - ksuffixlen);
+ die(_("replacement '%s' has no '*'"), replacement);
+ strbuf_add(&sb, replacement, vstar - replacement);
+ strbuf_add(&sb, refname + klen, namelen - klen - ksuffixlen);
strbuf_addstr(&sb, vstar + 1);
*result = strbuf_detach(&sb, NULL);
}
@@ -301,7 +301,7 @@ static int refspec_match(const struct refspec_item *refspec,
const char *name)
{
if (refspec->pattern)
- return match_name_with_pattern(refspec->src, name, NULL, NULL);
+ return match_refname_with_pattern(refspec->src, name, NULL, NULL);
return !strcmp(refspec->src, name);
}
@@ -352,7 +352,7 @@ static int refspec_find_negative_match(struct refspec *rs, struct refspec_item *
const char *key = refspec->dst ? refspec->dst : refspec->src;
const char *value = refspec->src;
- if (match_name_with_pattern(key, needle, value, &expn_name))
+ if (match_refname_with_pattern(key, needle, value, &expn_name))
string_list_append_nodup(&reversed, expn_name);
} else if (refspec->matching) {
/* For the special matching refspec, any query should match */
@@ -397,7 +397,7 @@ void refspec_find_all_matches(struct refspec *rs,
if (!refspec->dst || refspec->negative)
continue;
if (refspec->pattern) {
- if (match_name_with_pattern(key, needle, value, result))
+ if (match_refname_with_pattern(key, needle, value, result))
string_list_append_nodup(results, *result);
} else if (!strcmp(needle, key)) {
string_list_append(results, value);
@@ -426,7 +426,7 @@ int refspec_find_match(struct refspec *rs, struct refspec_item *query)
if (!refspec->dst || refspec->negative)
continue;
if (refspec->pattern) {
- if (match_name_with_pattern(key, needle, value, result)) {
+ if (match_refname_with_pattern(key, needle, value, result)) {
query->force = refspec->force;
return 0;
}
diff --git a/refspec.h b/refspec.h
index f62f83a7ee..e2b5cc54ef 100644
--- a/refspec.h
+++ b/refspec.h
@@ -75,11 +75,12 @@ void refspec_ref_prefixes(const struct refspec *rs,
int refname_matches_negative_refspec_item(const char *refname, struct refspec *rs);
/*
- * Checks whether a name matches a pattern and optionally generates a result.
- * Returns 1 if the name matches the pattern, 0 otherwise.
+ * Checks if a refname matches a globbing refspec pattern.
+ * If replacement is provided, computes the corresponding mapped refname.
+ * Returns 1 if refname matches pattern, 0 otherwise.
*/
-int match_name_with_pattern(const char *key, const char *name,
- const char *value, char **result);
+int match_refname_with_pattern(const char *pattern, const char *refname,
+ const char *replacement, char **result);
/*
* Queries a refspec for a match and updates the query item.
diff --git a/remote.c b/remote.c
index c1cf363cca..c9c1384c1d 100644
--- a/remote.c
+++ b/remote.c
@@ -1322,9 +1322,9 @@ static char *get_ref_match(const struct refspec *rs, const struct ref *ref,
const char *dst_side = item->dst ? item->dst : item->src;
int match;
if (direction == FROM_SRC)
- match = match_name_with_pattern(item->src, ref->name, dst_side, &name);
+ match = match_refname_with_pattern(item->src, ref->name, dst_side, &name);
else
- match = match_name_with_pattern(dst_side, ref->name, item->src, &name);
+ match = match_refname_with_pattern(dst_side, ref->name, item->src, &name);
if (match) {
matching_refs = i;
break;
@@ -1942,7 +1942,7 @@ static struct ref *get_expanded_map(const struct ref *remote_refs,
if (strchr(ref->name, '^'))
continue; /* a dereference item */
- if (match_name_with_pattern(refspec->src, ref->name,
+ if (match_refname_with_pattern(refspec->src, ref->name,
refspec->dst, &expn_name) &&
!ignore_symref_update(expn_name, &scratch)) {
struct ref *cpy = copy_ref(ref);
diff --git a/t/helper/test-dir-iterator.c b/t/helper/test-dir-iterator.c
index 6b297bd753..8d46e8ba40 100644
--- a/t/helper/test-dir-iterator.c
+++ b/t/helper/test-dir-iterator.c
@@ -53,6 +53,7 @@ int cmd__dir_iterator(int argc, const char **argv)
printf("(%s) [%s] %s\n", diter->relative_path, diter->basename,
diter->path.buf);
}
+ dir_iterator_free(diter);
if (iter_status != ITER_DONE) {
printf("dir_iterator_advance failure\n");
diff --git a/t/interop/Makefile b/t/interop/Makefile
index 6911c2915a..4ff4ed0616 100644
--- a/t/interop/Makefile
+++ b/t/interop/Makefile
@@ -1,3 +1,6 @@
+# The default target of this Makefile is...
+all::
+
# Import tree-wide shared Makefile behavior and libraries
include ../../shared.mak
@@ -8,7 +11,7 @@ SHELL_PATH ?= $(SHELL)
SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
T = $(sort $(wildcard i[0-9][0-9][0-9][0-9]-*.sh))
-all: $(T)
+all:: $(T)
$(T):
@echo "*** $@ ***"; '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
diff --git a/t/perf/Makefile b/t/perf/Makefile
index e4808aebed..9b3090c4ed 100644
--- a/t/perf/Makefile
+++ b/t/perf/Makefile
@@ -1,10 +1,13 @@
+# The default target of this Makefile is...
+all::
+
# Import tree-wide shared Makefile behavior and libraries
include ../../shared.mak
-include ../../config.mak
export GIT_TEST_OPTIONS
-all: test-lint perf
+all:: test-lint perf
perf: pre-clean
./run
diff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh
index 2421491931..4a6242ff99 100755
--- a/t/t4203-mailmap.sh
+++ b/t/t4203-mailmap.sh
@@ -113,6 +113,18 @@ test_expect_success 'check-mailmap --stdin simple address: no mapping' '
test_cmp expect actual
'
+test_expect_success 'check-mailmap name and address: mapping' '
+ test_when_finished "rm .mailmap" &&
+ cat >.mailmap <<-EOF &&
+ Bug Reports <bugs-new@company.xx> Bugs <bugs@company.xx>
+ EOF
+ cat >expect <<-EOF &&
+ <bugs@company.xx>
+ EOF
+ git check-mailmap "bugs@company.xx" >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'No mailmap' '
cat >expect <<-EOF &&
$GIT_AUTHOR_NAME (1):
diff --git a/t/t4209-log-pickaxe.sh b/t/t4209-log-pickaxe.sh
index a675ace081..0e2f80a268 100755
--- a/t/t4209-log-pickaxe.sh
+++ b/t/t4209-log-pickaxe.sh
@@ -93,6 +93,22 @@ test_expect_success 'usage: --no-pickaxe-regex' '
test_cmp expect actual
'
+test_expect_success 'usage: -G and -S with empty argument' '
+ cat >expect <<-\EOF &&
+ error: -S requires a non-empty argument
+ EOF
+
+ test_expect_code 129 git log -S "" 2>actual &&
+ test_cmp expect actual &&
+
+ cat >expect <<-\EOF &&
+ error: -G requires a non-empty argument
+ EOF
+
+ test_expect_code 129 git log -G "" 2>actual &&
+ test_cmp expect actual
+'
+
test_log expect_initial --grep initial
test_log expect_nomatch --grep InItial
test_log_icase expect_initial --grep InItial
diff --git a/templates/Makefile b/templates/Makefile
index bd1e9e30c1..722755338d 100644
--- a/templates/Makefile
+++ b/templates/Makefile
@@ -1,3 +1,6 @@
+# The default target of this Makefile is...
+all::
+
# Import tree-wide shared Makefile behavior and libraries
include ../shared.mak
@@ -23,7 +26,7 @@ PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
template_instdir_SQ = $(subst ','\'',$(template_instdir))
-all: boilerplates.made custom
+all:: boilerplates.made custom
# Put templates that can be copied straight from the source
# in a file direc--tory--file in the source. They will be
diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c
index 4685ba6137..8889b8b62a 100644
--- a/xdiff/xdiffi.c
+++ b/xdiff/xdiffi.c
@@ -19,7 +19,6 @@
* Davide Libenzi <davidel@xmailserver.org>
*
*/
-#define DISABLE_SIGN_COMPARE_WARNINGS
#include "xinclude.h"
@@ -1014,7 +1013,7 @@ static void xdl_mark_ignorable_lines(xdchange_t *xscr, xdfenv_t *xe, long flags)
static int record_matches_regex(xrecord_t *rec, xpparam_t const *xpp) {
regmatch_t regmatch;
- int i;
+ size_t i;
for (i = 0; i < xpp->ignore_regex_nr; i++)
if (!regexec_buf(xpp->ignore_regex[i], rec->ptr, rec->size, 1,
diff --git a/xdiff/xemit.c b/xdiff/xemit.c
index 75f0fe4986..f8e3f25b03 100644
--- a/xdiff/xemit.c
+++ b/xdiff/xemit.c
@@ -54,7 +54,7 @@ xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg)
xdchange_t *xch, *xchp, *lxch;
long max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen;
long max_ignorable = xecfg->ctxlen;
- unsigned long ignored = 0; /* number of ignored blank lines */
+ long ignored = 0; /* number of ignored blank lines */
/* remove ignorable changes that are too far before other changes */
for (xchp = *xscr; xchp && xchp->ignore; xchp = xchp->next) {
diff --git a/xdiff/xhistogram.c b/xdiff/xhistogram.c
index 16a8fe2f3f..040d81e0bc 100644
--- a/xdiff/xhistogram.c
+++ b/xdiff/xhistogram.c
@@ -106,7 +106,7 @@ static int scanA(struct histindex *index, int line1, int count1)
unsigned int chain_len;
struct record **rec_chain, *rec;
- for (ptr = LINE_END(1); line1 <= ptr; ptr--) {
+ for (ptr = LINE_END(1); (unsigned int)line1 <= ptr; ptr--) {
tbl_idx = TABLE_HASH(index, 1, ptr);
rec_chain = index->records + tbl_idx;
rec = *rec_chain;
@@ -181,14 +181,14 @@ static int try_lcs(struct histindex *index, struct region *lcs, int b_ptr,
be = bs;
rc = rec->cnt;
- while (line1 < as && line2 < bs
+ while ((unsigned int)line1 < as && (unsigned int)line2 < bs
&& CMP(index, 1, as - 1, 2, bs - 1)) {
as--;
bs--;
if (1 < rc)
rc = XDL_MIN(rc, CNT(index, as));
}
- while (ae < LINE_END(1) && be < LINE_END(2)
+ while (ae < (unsigned int)LINE_END(1) && be < (unsigned int)LINE_END(2)
&& CMP(index, 1, ae + 1, 2, be + 1)) {
ae++;
be++;
@@ -313,7 +313,7 @@ redo:
if (count1 <= 0 && count2 <= 0)
return 0;
- if (LINE_END(1) >= MAX_PTR)
+ if ((unsigned int)LINE_END(1) >= MAX_PTR)
return -1;
if (!count1) {
diff --git a/xdiff/xinclude.h b/xdiff/xinclude.h
index 7e56542526..a4285ac0eb 100644
--- a/xdiff/xinclude.h
+++ b/xdiff/xinclude.h
@@ -23,8 +23,6 @@
#if !defined(XINCLUDE_H)
#define XINCLUDE_H
-#define DISABLE_SIGN_COMPARE_WARNINGS
-
#include "git-compat-util.h"
#include "xmacros.h"
#include "xdiff.h"
diff --git a/xdiff/xpatience.c b/xdiff/xpatience.c
index a2d8955537..82f663004e 100644
--- a/xdiff/xpatience.c
+++ b/xdiff/xpatience.c
@@ -19,6 +19,7 @@
* Davide Libenzi <davidel@xmailserver.org>
*
*/
+
#include "xinclude.h"
/*
@@ -75,7 +76,7 @@ struct hashmap {
static int is_anchor(xpparam_t const *xpp, const char *line)
{
- int i;
+ size_t i;
for (i = 0; i < xpp->anchors_nr; i++) {
if (!strncmp(line, xpp->anchors[i], strlen(xpp->anchors[i])))
return 1;
diff --git a/xdiff/xutils.c b/xdiff/xutils.c
index 9e36f24875..444a108f87 100644
--- a/xdiff/xutils.c
+++ b/xdiff/xutils.c
@@ -375,7 +375,7 @@ static int xdl_format_hunk_hdr(long s1, long c1, long s2, long c2,
nb += 3;
if (func && funclen) {
buf[nb++] = ' ';
- if (funclen > sizeof(buf) - nb - 1)
+ if ((size_t)funclen > sizeof(buf) - nb - 1)
funclen = sizeof(buf) - nb - 1;
memcpy(buf + nb, func, funclen);
nb += funclen;
@@ -437,7 +437,7 @@ void* xdl_alloc_grow_helper(void *p, long nr, long *alloc, size_t size)
{
void *tmp = NULL;
size_t n = ((LONG_MAX - 16) / 2 >= *alloc) ? 2 * *alloc + 16 : LONG_MAX;
- if (nr > n)
+ if ((size_t)nr > n)
n = nr;
if (SIZE_MAX / size >= n)
tmp = xdl_realloc(p, n * size);