Merge branch 'ps/refname-avail-check-optim' into kn/non-transactional-batch-updates

* ps/refname-avail-check-optim: (43 commits)
  refs: reuse iterators when determining refname availability
  refs/iterator: implement seeking for files iterators
  refs/iterator: implement seeking for packed-ref iterators
  refs/iterator: implement seeking for ref-cache iterators
  refs/iterator: implement seeking for reftable iterators
  refs/iterator: implement seeking for merged iterators
  refs/iterator: provide infrastructure to re-seek iterators
  refs/iterator: separate lifecycle from iteration
  refs: stop re-verifying common prefixes for availability
  refs/files: batch refname availability checks for initial transactions
  refs/files: batch refname availability checks for normal transactions
  refs/reftable: batch refname availability checks
  refs: introduce function to batch refname availability checks
  builtin/update-ref: skip ambiguity checks when parsing object IDs
  object-name: allow skipping ambiguity checks in `get_oid()` family
  object-name: introduce `repo_get_oid_with_flags()`
  Git 2.49-rc0
  The fourteenth batch
  mailmap: fix check-mailmap with full mailmap line
  The thirteenth batch
  ...
maint
Junio C Hamano 2025-03-12 11:55:05 -07:00
commit 7c42ab2c92
63 changed files with 765 additions and 449 deletions

View File

@ -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)


View File

@ -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).

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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`

View File

@ -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:


View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -1,6 +1,6 @@
#!/bin/sh

DEF_VER=v2.48.GIT
DEF_VER=v2.49.0-rc0

LF='
'

View File

@ -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


View File

@ -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);

View File

@ -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)

View File

@ -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);

View File

@ -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);
}


View File

@ -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);
}

View File

@ -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;

View File

@ -1,3 +1,6 @@
# The default target of this Makefile is...
all::

MAIN:=git-credential-libsecret
all:: $(MAIN)


View File

@ -1,3 +1,4 @@
# The default target of this Makefile is...
all:: git-credential-osxkeychain

CC = gcc

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

4
diff.c
View File

@ -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;
}


View File

@ -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;
}

View File

@ -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

View File

@ -1,3 +1,4 @@
# The default target of this Makefile is...
all::

# Define V=1 to have a more verbose compile.

View File

@ -1,3 +1,6 @@
# The default target of this Makefile is...
update-po::

PO_TEMPLATE = git-gui-glossary.pot

ALL_POFILES = $(wildcard *.po)

23
hash.h
View File

@ -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 | \

View File

@ -12,7 +12,7 @@
#define ITER_OK 0

/*
* The iterator is exhausted and has been freed.
* The iterator is exhausted.
*/
#define ITER_DONE -1


View File

@ -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', '')

View File

@ -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.

View File

@ -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, ...);

187
refs.c
View File

@ -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);

/*
* 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;
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;

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;
}
strbuf_reset(&dirname);

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;
}
}
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;

/*
* 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, '/');
/* Expand dirname to the new prefix, not including the trailing slash: */
strbuf_add(&dirname, refname + dirname.len, slash - refname - dirname.len);

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))
/*
* 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;

strbuf_addf(err, _("'%s' exists; cannot create '%s'"),
iter->refname, refname);
ref_iterator_abort(iter);
goto cleanup;
/*
* 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;
}
}

if (ok != ITER_DONE)
BUG("error while iterating over references");
/*
* 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) {
int ok;

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;
}

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);
goto cleanup;
}

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;

12
refs.h
View File

@ -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,

View File

@ -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 *

View File

@ -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;

View File

@ -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 ref_iterator_abort(ref_iterator);
return ITER_DONE;
}

static int empty_ref_iterator_seek(struct ref_iterator *ref_iterator UNUSED,
const char *prefix UNUSED)
{
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;
}

View File

@ -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;
}

View File

@ -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;

if (prefix && *prefix) {
iter->prefix = xstrdup(prefix);
level->prefix_state = PREFIX_WITHIN_DIR;
} else {
level->prefix_state = PREFIX_CONTAINS_DIR;
}

iter->repo = repo;
iter->cache = cache;
iter->prime_dir = prime_dir;

if (cache_ref_iterator_seek(&iter->base, prefix) < 0) {
ref_iterator_free(&iter->base);
return NULL;
}

return ref_iterator;
}

View File

@ -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;
@ -326,6 +327,22 @@ 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.
@ -333,12 +350,8 @@ int ref_iterator_advance(struct ref_iterator *ref_iterator);
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: */

/*
@ -455,6 +461,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.
*/
@ -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;
};

/*

View File

@ -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,

View File

@ -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;
}

View File

@ -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.

View File

@ -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);

View File

@ -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");

View File

@ -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)

View File

@ -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

View File

@ -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):

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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) {

View File

@ -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) {

View File

@ -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"

View File

@ -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;

View File

@ -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);