Merge branch 'ps/ref-peeled-tags' into kn/refs-optim-cleanup

* ps/ref-peeled-tags: (92 commits)
  t7004: do not chdir around in the main process
  ref-filter: fix stale parsed objects
  ref-filter: parse objects on demand
  ref-filter: detect broken tags when dereferencing them
  refs: don't store peeled object IDs for invalid tags
  object: add flag to `peel_object()` to verify object type
  refs: drop infrastructure to peel via iterators
  refs: drop `current_ref_iter` hack
  builtin/show-ref: convert to use `reference_get_peeled_oid()`
  ref-filter: propagate peeled object ID
  upload-pack: convert to use `reference_get_peeled_oid()`
  refs: expose peeled object ID via the iterator
  refs: refactor reference status flags
  refs: fully reset `struct ref_iterator::ref` on iteration
  refs: introduce `.ref` field for the base iterator
  refs: introduce wrapper struct for `each_ref_fn`
  builtin/repo: add progress meter for structure stats
  builtin/repo: add keyvalue and nul format for structure stats
  builtin/repo: add object counts in structure output
  builtin/repo: introduce structure subcommand
  ...
This commit is contained in:
Junio C Hamano
2025-11-04 07:33:41 -08:00
95 changed files with 3556 additions and 2297 deletions

View File

@@ -100,7 +100,7 @@ Performance, Internal Implementation, Development Support etc.
* CodingGuidelines now spells out how bitfields are to be written.
* Adjust to the way newer versions of cURL selectivel enables tracing
* Adjust to the way newer versions of cURL selectively enable tracing
options, so that our tests can continue to work.
(merge 1b5a6bfff3 jk/curl-global-trace-components later to maint).
@@ -127,6 +127,10 @@ Performance, Internal Implementation, Development Support etc.
* Documentation for "git log --pretty" options has been updated
to make it easier to translate.
* Instead of three library archives (one for git, one for reftable,
and one for xdiff), roll everything into a single libgit.a archive.
This would help later effort to FFI into Rust.
Fixes since v2.51
-----------------
@@ -212,13 +216,13 @@ including security updates, are included in this release.
name.
(merge bcb20dda83 js/doc-gitk-history later to maint).
* Update the instruction to use of GGG in the MyFirstContribution
* Update the instructions for using GGG in the MyFirstContribution
document to say that a GitHub PR could be made against `git/git`
instead of `gitgitgadget/git`.
(merge 37001cdbc4 ds/doc-ggg-pr-fork-clarify later to maint).
* Makefile tried to run multiple "cargo build" which would not work
very well; serialize their execution to work it around.
very well; serialize their execution to work around this problem.
(merge 0eeacde50e da/cargo-serialize later to maint).
* "git repack --path-walk" lost objects in some corner cases, which
@@ -294,12 +298,12 @@ including security updates, are included in this release.
updated.
(merge 54a60e5b38 kh/you-still-use-whatchanged-fix later to maint).
* Clang-format update to let our control macros formatted the way we
* Clang-format update to let our control macros be formatted the way we
had them traditionally, e.g., "for_each_string_list_item()" without
space before the parentheses.
(merge 3721541d35 jt/clang-format-foreach-wo-space-before-parenthesis later to maint).
* A few places where an size_t value was cast to curl_off_t without
* A few places where a size_t value was cast to curl_off_t without
checking has been updated to use the existing helper function.
(merge ecc5749578 js/curl-off-t-fixes later to maint).
@@ -329,6 +333,19 @@ including security updates, are included in this release.
you would get from "git format-patch --notes=..." for a singleton
patch.
* The code in "git add -p" and friends to iterate over hunks was
riddled with bugs, which has been corrected.
* A few more things that patch authors can do to help maintainer to
keep track of their topics better.
(merge 1a41698841 tb/doc-submitting-patches later to maint).
* An earlier addition to "git diff --no-index A B" to limit the
output with pathspec after the two directories misbehaved when
these directories were given with a trailing slash, which has been
corrected.
(merge c0bec06cfe jk/diff-no-index-with-pathspec-fix later to maint).
* Other code cleanup, docfix, build fix, etc.
(merge 823d537fa7 kh/doc-git-log-markup-fix later to maint).
(merge cf7efa4f33 rj/t6137-cygwin-fix later to maint).
@@ -359,3 +376,7 @@ including security updates, are included in this release.
(merge 1c573a3451 en/doc-merge-tree-describe-merge-base later to maint).
(merge 84a6bf7965 ja/doc-markup-attached-paragraph-fix later to maint).
(merge 399694384b kh/doc-patch-id-markup-fix later to maint).
(merge 15b8abde07 js/mingw-includes-cleanup later to maint).
(merge 3860985105 js/unreachable-workaround-for-no-symlink-head later to maint).
(merge b3ac6e737d kh/doc-continued-paragraph-fix later to maint).
(merge 2cebca0582 tb/cat-file-objectmode-update later to maint).

View File

@@ -579,14 +579,27 @@ line via `git format-patch --notes`.
[[the-topic-summary]]
*This is EXPERIMENTAL*.
When sending a topic, you can propose a one-paragraph summary that
should appear in the "What's cooking" report when it is picked up to
explain the topic. If you choose to do so, please write a 2-5 line
paragraph that will fit well in our release notes (see many bulleted
entries in the Documentation/RelNotes/* files for examples), and make
it the first paragraph of the cover letter. For a single-patch
series, use the space between the three-dash line and the diffstat, as
described earlier.
When sending a topic, you can optionally propose a topic name and/or a
one-paragraph summary that should appear in the "What's cooking"
report when it is picked up to explain the topic. If you choose to do
so, please write a 2-5 line paragraph that will fit well in our
release notes (see many bulleted entries in the
Documentation/RelNotes/* files for examples), and make it the first
(or second, if including a suggested topic name) paragraph of the
cover letter. If suggesting a topic name, use the format
"XX/your-topic-name", where "XX" is a stand-in for the primary
author's initials, and "your-topic-name" is a brief, dash-delimited
description of what your topic does. For a single-patch series, use
the space between the three-dash line and the diffstat, as described
earlier.
[[multi-series-efforts]]
If your patch series is part of a larger effort spanning multiple
patch series, briefly describe the broader goal, and state where the
current series fits into that goal. If you are suggesting a topic
name as in <<the-topic-summary, section above>>, consider
"XX/the-broader-goal-part-one", "XX/the-broader-goal-part-two", and so
on.
[[attachment]]
Do not attach the patch as a MIME attachment, compressed or not.

View File

@@ -75,8 +75,8 @@ The built-in file system monitor is currently available only on a
limited set of supported platforms. Currently, this includes Windows
and MacOS.
+
Otherwise, this variable contains the pathname of the "fsmonitor"
hook command.
Otherwise, this variable contains the pathname of the "fsmonitor"
hook command.
+
This hook command is used to identify all files that may have changed
since the requested date/time. This information is used to speed up

View File

@@ -11,6 +11,10 @@ endif::git-stash[]
behave as if `--index` was supplied. Defaults to false.
ifndef::git-stash[]
See the descriptions in linkgit:git-stash[1].
+
This also affects invocations of linkgit:git-stash[1] via `--autostash` from
commands like linkgit:git-merge[1], linkgit:git-rebase[1], and
linkgit:git-pull[1].
endif::git-stash[]
`stash.showIncludeUntracked`::

View File

@@ -342,10 +342,10 @@ patch::
d - do not stage this hunk or any of the later hunks in the file
g - select a hunk to go to
/ - search for a hunk matching the given regex
j - leave this hunk undecided, see next undecided hunk
J - leave this hunk undecided, see next hunk
k - leave this hunk undecided, see previous undecided hunk
K - leave this hunk undecided, see previous hunk
j - go to the next undecided hunk, roll over at the bottom
J - go to the next hunk, roll over at the bottom
k - go to the previous undecided hunk, roll over at the top
K - go to the previous hunk, roll over at the top
s - split the current hunk into smaller hunks
e - manually edit the current hunk
p - print the current hunk

View File

@@ -117,15 +117,15 @@ OPTIONS
--comment <message>::
Append a comment at the end of new or modified lines.
If _<message>_ begins with one or more whitespaces followed
by "#", it is used as-is. If it begins with "#", a space is
prepended before it is used. Otherwise, a string " # " (a
space followed by a hash followed by a space) is prepended
to it. And the resulting string is placed immediately after
the value defined for the variable. The _<message>_ must
not contain linefeed characters (no multi-line comments are
permitted).
+
If _<message>_ begins with one or more whitespaces followed
by "#", it is used as-is. If it begins with "#", a space is
prepended before it is used. Otherwise, a string " # " (a
space followed by a hash followed by a space) is prepended
to it. And the resulting string is placed immediately after
the value defined for the variable. The _<message>_ must
not contain linefeed characters (no multi-line comments are
permitted).
--all::
With `get`, return all values for a multi-valued key.

View File

@@ -9,6 +9,7 @@ SYNOPSIS
--------
[synopsis]
git repo info [--format=(keyvalue|nul)] [-z] [<key>...]
git repo structure [--format=(table|keyvalue|nul)]
DESCRIPTION
-----------
@@ -43,6 +44,35 @@ supported:
+
`-z` is an alias for `--format=nul`.
`structure [--format=(table|keyvalue|nul)]`::
Retrieve statistics about the current repository structure. The
following kinds of information are reported:
+
* Reference counts categorized by type
* Reachable object counts categorized by type
+
The output format can be chosen through the flag `--format`. Three formats are
supported:
+
`table`:::
Outputs repository stats in a human-friendly table. This format may
change and is not intended for machine parsing. This is the default
format.
`keyvalue`:::
Each line of output contains a key-value pair for a repository stat.
The '=' character is used to delimit between the key and the value.
Values containing "unusual" characters are quoted as explained for the
configuration variable `core.quotePath` (see linkgit:git-config[1]).
`nul`:::
Similar to `keyvalue`, but uses a NUL character to delimit between
key-value pairs instead of a newline. Also uses a newline character as
the delimiter between the key and value instead of '='. Unlike the
`keyvalue` format, values containing "unusual" characters are never
quoted.
INFO KEYS
---------
In order to obtain a set of values from `git repo info`, you should provide

View File

@@ -174,13 +174,13 @@ for another option.
Allow oids to be input from any object format that the current
repository supports.
Specifying "sha1" translates if necessary and returns a sha1 oid.
Specifying "sha256" translates if necessary and returns a sha256 oid.
Specifying "storage" translates if necessary and returns an oid in
encoded in the storage hash algorithm.
+
Specifying "sha1" translates if necessary and returns a sha1 oid.
+
Specifying "sha256" translates if necessary and returns a sha256 oid.
+
Specifying "storage" translates if necessary and returns an oid in
encoded in the storage hash algorithm.
Options for Objects
~~~~~~~~~~~~~~~~~~~

View File

@@ -44,8 +44,8 @@ OPTIONS
describe each commit. '<format>' can be any string accepted
by the `--format` option of 'git log', such as '* [%h] %s'.
(See the "PRETTY FORMATS" section of linkgit:git-log[1].)
Each pretty-printed commit will be rewrapped before it is shown.
+
Each pretty-printed commit will be rewrapped before it is shown.
--date=<format>::
Show dates formatted according to the given date string. (See

View File

@@ -264,34 +264,50 @@ patterns in non-cone mode has a number of shortcomings:
inconsistent.
* It has edge cases where the "right" behavior is unclear. Two examples:
First, two users are in a subdirectory, and the first runs
git sparse-checkout set '/toplevel-dir/*.c'
while the second runs
git sparse-checkout set relative-dir
Should those arguments be transliterated into
current/subdirectory/toplevel-dir/*.c
and
current/subdirectory/relative-dir
before inserting into the sparse-checkout file? The user who typed
the first command is probably aware that arguments to set/add are
supposed to be patterns in non-cone mode, and probably would not be
happy with such a transliteration. However, many gitignore-style
patterns are just paths, which might be what the user who typed the
second command was thinking, and they'd be upset if their argument
wasn't transliterated.
Second, what should bash-completion complete on for set/add commands
for non-cone users? If it suggests paths, is it exacerbating the
problem above? Also, if it suggests paths, what if the user has a
file or directory that begins with either a '!' or '#' or has a '*',
'\', '?', '[', or ']' in its name? And if it suggests paths, will
it complete "/pro" to "/proc" (in the root filesystem) rather than to
"/progress.txt" in the current directory? (Note that users are
likely to want to start paths with a leading '/' in non-cone mode,
for the same reason that .gitignore files often have one.)
Completing on files or directories might give nasty surprises in
all these cases.
+
First, two users are in a subdirectory, and the first runs
+
----
git sparse-checkout set '/toplevel-dir/*.c'
----
+
while the second runs
+
----
git sparse-checkout set relative-dir
----
+
Should those arguments be transliterated into
+
----
current/subdirectory/toplevel-dir/*.c
----
+
and
+
----
current/subdirectory/relative-dir
----
+
before inserting into the sparse-checkout file? The user who typed
the first command is probably aware that arguments to set/add are
supposed to be patterns in non-cone mode, and probably would not be
happy with such a transliteration. However, many gitignore-style
patterns are just paths, which might be what the user who typed the
second command was thinking, and they'd be upset if their argument
wasn't transliterated.
+
Second, what should bash-completion complete on for set/add commands
for non-cone users? If it suggests paths, is it exacerbating the
problem above? Also, if it suggests paths, what if the user has a
file or directory that begins with either a '!' or '#' or has a '*',
'\', '?', '[', or ']' in its name? And if it suggests paths, will
it complete "/pro" to "/proc" (in the root filesystem) rather than to
"/progress.txt" in the current directory? (Note that users are
likely to want to start paths with a leading '/' in non-cone mode,
for the same reason that .gitignore files often have one.)
Completing on files or directories might give nasty surprises in
all these cases.
* The excessive flexibility made other extensions essentially
impractical. `--sparse-index` is likely impossible in non-cone

View File

@@ -927,16 +927,13 @@ export PYTHON_PATH
TEST_SHELL_PATH = $(SHELL_PATH)
LIB_FILE = libgit.a
XDIFF_LIB = xdiff/lib.a
REFTABLE_LIB = reftable/libreftable.a
ifdef DEBUG
RUST_LIB = target/debug/libgitcore.a
else
RUST_LIB = target/release/libgitcore.a
endif
# xdiff and reftable libs may in turn depend on what is in libgit.a
GITLIBS = common-main.o $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB) $(LIB_FILE)
GITLIBS = common-main.o $(LIB_FILE)
EXTLIBS =
GIT_USER_AGENT = git/$(GIT_VERSION)
@@ -1248,7 +1245,27 @@ LIB_OBJS += refs/iterator.o
LIB_OBJS += refs/packed-backend.o
LIB_OBJS += refs/ref-cache.o
LIB_OBJS += refspec.o
LIB_OBJS += reftable/basics.o
LIB_OBJS += reftable/block.o
LIB_OBJS += reftable/blocksource.o
LIB_OBJS += reftable/error.o
LIB_OBJS += reftable/fsck.o
LIB_OBJS += reftable/iter.o
LIB_OBJS += reftable/merged.o
LIB_OBJS += reftable/pq.o
LIB_OBJS += reftable/record.o
LIB_OBJS += reftable/stack.o
LIB_OBJS += reftable/system.o
LIB_OBJS += reftable/table.o
LIB_OBJS += reftable/tree.o
LIB_OBJS += reftable/writer.o
LIB_OBJS += remote.o
LIB_OBJS += repack.o
LIB_OBJS += repack-cruft.o
LIB_OBJS += repack-filtered.o
LIB_OBJS += repack-geometry.o
LIB_OBJS += repack-midx.o
LIB_OBJS += repack-promisor.o
LIB_OBJS += replace-object.o
LIB_OBJS += repo-settings.o
LIB_OBJS += repository.o
@@ -1322,6 +1339,13 @@ LIB_OBJS += write-or-die.o
LIB_OBJS += ws.o
LIB_OBJS += wt-status.o
LIB_OBJS += xdiff-interface.o
LIB_OBJS += xdiff/xdiffi.o
LIB_OBJS += xdiff/xemit.o
LIB_OBJS += xdiff/xhistogram.o
LIB_OBJS += xdiff/xmerge.o
LIB_OBJS += xdiff/xpatience.o
LIB_OBJS += xdiff/xprepare.o
LIB_OBJS += xdiff/xutils.o
BUILTIN_OBJS += builtin/add.o
BUILTIN_OBJS += builtin/am.o
@@ -2756,31 +2780,6 @@ reconfigure config.mak.autogen: config.status
.PHONY: reconfigure # This is a convenience target.
endif
XDIFF_OBJS += xdiff/xdiffi.o
XDIFF_OBJS += xdiff/xemit.o
XDIFF_OBJS += xdiff/xhistogram.o
XDIFF_OBJS += xdiff/xmerge.o
XDIFF_OBJS += xdiff/xpatience.o
XDIFF_OBJS += xdiff/xprepare.o
XDIFF_OBJS += xdiff/xutils.o
.PHONY: xdiff-objs
xdiff-objs: $(XDIFF_OBJS)
REFTABLE_OBJS += reftable/basics.o
REFTABLE_OBJS += reftable/block.o
REFTABLE_OBJS += reftable/blocksource.o
REFTABLE_OBJS += reftable/error.o
REFTABLE_OBJS += reftable/fsck.o
REFTABLE_OBJS += reftable/iter.o
REFTABLE_OBJS += reftable/merged.o
REFTABLE_OBJS += reftable/pq.o
REFTABLE_OBJS += reftable/record.o
REFTABLE_OBJS += reftable/stack.o
REFTABLE_OBJS += reftable/system.o
REFTABLE_OBJS += reftable/table.o
REFTABLE_OBJS += reftable/tree.o
REFTABLE_OBJS += reftable/writer.o
TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS)) $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS))
.PHONY: test-objs
@@ -2801,9 +2800,8 @@ OBJECTS += $(GIT_OBJS)
OBJECTS += $(SCALAR_OBJS)
OBJECTS += $(PROGRAM_OBJS)
OBJECTS += $(TEST_OBJS)
OBJECTS += $(XDIFF_OBJS)
OBJECTS += $(FUZZ_OBJS)
OBJECTS += $(REFTABLE_OBJS) $(REFTABLE_TEST_OBJS)
OBJECTS += $(REFTABLE_TEST_OBJS)
OBJECTS += $(UNIT_TEST_OBJS)
OBJECTS += $(CLAR_TEST_OBJS)
OBJECTS += $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TEST_PROGRAMS))
@@ -2961,12 +2959,6 @@ $(RUST_LIB): Cargo.toml $(RUST_SOURCES)
.PHONY: rust
rust: $(RUST_LIB)
$(XDIFF_LIB): $(XDIFF_OBJS)
$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^
$(REFTABLE_LIB): $(REFTABLE_OBJS)
$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^
export DEFAULT_EDITOR DEFAULT_PAGER
Documentation/GIT-EXCLUDED-PROGRAMS: FORCE
@@ -3805,7 +3797,7 @@ clean: profile-clean coverage-clean cocciclean
$(RM) git.rc git.res
$(RM) $(OBJECTS)
$(RM) headless-git.o
$(RM) $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB)
$(RM) $(LIB_FILE)
$(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) $(OTHER_PROGRAMS)
$(RM) $(TEST_PROGRAMS)
$(RM) $(FUZZ_PROGRAMS)
@@ -3999,8 +3991,6 @@ endif
LIBGIT_PUB_OBJS += contrib/libgit-sys/public_symbol_export.o
LIBGIT_PUB_OBJS += libgit.a
LIBGIT_PUB_OBJS += reftable/libreftable.a
LIBGIT_PUB_OBJS += xdiff/lib.a
LIBGIT_PARTIAL_EXPORT = contrib/libgit-sys/partial_symbol_export.o

View File

@@ -1408,10 +1408,10 @@ static size_t display_hunks(struct add_p_state *s,
}
static const char help_patch_remainder[] =
N_("j - leave this hunk undecided, see next undecided hunk\n"
"J - leave this hunk undecided, see next hunk\n"
"k - leave this hunk undecided, see previous undecided hunk\n"
"K - leave this hunk undecided, see previous hunk\n"
N_("j - go to the next undecided hunk, roll over at the bottom\n"
"J - go to the next hunk, roll over at the bottom\n"
"k - go to the previous undecided hunk, roll over at the top\n"
"K - go to the previous hunk, roll over at the top\n"
"g - select a hunk to go to\n"
"/ - search for a hunk matching the given regex\n"
"s - split the current hunk into smaller hunks\n"
@@ -1419,6 +1419,27 @@ N_("j - leave this hunk undecided, see next undecided hunk\n"
"p - print the current hunk, 'P' to use the pager\n"
"? - print help\n");
static size_t dec_mod(size_t a, size_t m)
{
return a > 0 ? a - 1 : m - 1;
}
static size_t inc_mod(size_t a, size_t m)
{
return a < m - 1 ? a + 1 : 0;
}
static bool get_first_undecided(const struct file_diff *file_diff, size_t *idx)
{
for (size_t i = 0; i < file_diff->hunk_nr; i++) {
if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
*idx = i;
return true;
}
}
return false;
}
static int patch_update_file(struct add_p_state *s,
struct file_diff *file_diff)
{
@@ -1429,15 +1450,6 @@ static int patch_update_file(struct add_p_state *s,
struct child_process cp = CHILD_PROCESS_INIT;
int colored = !!s->colored.len, quit = 0, use_pager = 0;
enum prompt_mode_type prompt_mode_type;
enum {
ALLOW_GOTO_PREVIOUS_HUNK = 1 << 0,
ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK = 1 << 1,
ALLOW_GOTO_NEXT_HUNK = 1 << 2,
ALLOW_GOTO_NEXT_UNDECIDED_HUNK = 1 << 3,
ALLOW_SEARCH_AND_GOTO = 1 << 4,
ALLOW_SPLIT = 1 << 5,
ALLOW_EDIT = 1 << 6
} permitted = 0;
/* Empty added files have no hunks */
if (!file_diff->hunk_nr && !file_diff->added)
@@ -1447,6 +1459,16 @@ static int patch_update_file(struct add_p_state *s,
render_diff_header(s, file_diff, colored, &s->buf);
fputs(s->buf.buf, stdout);
for (;;) {
enum {
ALLOW_GOTO_PREVIOUS_HUNK = 1 << 0,
ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK = 1 << 1,
ALLOW_GOTO_NEXT_HUNK = 1 << 2,
ALLOW_GOTO_NEXT_UNDECIDED_HUNK = 1 << 3,
ALLOW_SEARCH_AND_GOTO = 1 << 4,
ALLOW_SPLIT = 1 << 5,
ALLOW_EDIT = 1 << 6
} permitted = 0;
if (hunk_index >= file_diff->hunk_nr)
hunk_index = 0;
hunk = file_diff->hunk_nr
@@ -1456,13 +1478,17 @@ static int patch_update_file(struct add_p_state *s,
undecided_next = -1;
if (file_diff->hunk_nr) {
for (i = hunk_index - 1; i >= 0; i--)
for (i = dec_mod(hunk_index, file_diff->hunk_nr);
i != hunk_index;
i = dec_mod(i, file_diff->hunk_nr))
if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
undecided_previous = i;
break;
}
for (i = hunk_index + 1; i < file_diff->hunk_nr; i++)
for (i = inc_mod(hunk_index, file_diff->hunk_nr);
i != hunk_index;
i = inc_mod(i, file_diff->hunk_nr))
if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
undecided_next = i;
break;
@@ -1496,7 +1522,7 @@ static int patch_update_file(struct add_p_state *s,
permitted |= ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK;
strbuf_addstr(&s->buf, ",k");
}
if (hunk_index) {
if (file_diff->hunk_nr > 1) {
permitted |= ALLOW_GOTO_PREVIOUS_HUNK;
strbuf_addstr(&s->buf, ",K");
}
@@ -1504,7 +1530,7 @@ static int patch_update_file(struct add_p_state *s,
permitted |= ALLOW_GOTO_NEXT_UNDECIDED_HUNK;
strbuf_addstr(&s->buf, ",j");
}
if (hunk_index + 1 < file_diff->hunk_nr) {
if (file_diff->hunk_nr > 1) {
permitted |= ALLOW_GOTO_NEXT_HUNK;
strbuf_addstr(&s->buf, ",J");
}
@@ -1569,6 +1595,8 @@ soft_increment:
if (hunk->use == UNDECIDED_HUNK)
hunk->use = USE_HUNK;
}
if (!get_first_undecided(file_diff, &hunk_index))
hunk_index = 0;
} else if (hunk->use == UNDECIDED_HUNK) {
hunk->use = USE_HUNK;
}
@@ -1579,6 +1607,8 @@ soft_increment:
if (hunk->use == UNDECIDED_HUNK)
hunk->use = SKIP_HUNK;
}
if (!get_first_undecided(file_diff, &hunk_index))
hunk_index = 0;
} else if (hunk->use == UNDECIDED_HUNK) {
hunk->use = SKIP_HUNK;
}
@@ -1588,24 +1618,25 @@ soft_increment:
}
} else if (s->answer.buf[0] == 'K') {
if (permitted & ALLOW_GOTO_PREVIOUS_HUNK)
hunk_index--;
hunk_index = dec_mod(hunk_index,
file_diff->hunk_nr);
else
err(s, _("No previous hunk"));
err(s, _("No other hunk"));
} else if (s->answer.buf[0] == 'J') {
if (permitted & ALLOW_GOTO_NEXT_HUNK)
hunk_index++;
else
err(s, _("No next hunk"));
err(s, _("No other hunk"));
} else if (s->answer.buf[0] == 'k') {
if (permitted & ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK)
hunk_index = undecided_previous;
else
err(s, _("No previous hunk"));
err(s, _("No other undecided hunk"));
} else if (s->answer.buf[0] == 'j') {
if (permitted & ALLOW_GOTO_NEXT_UNDECIDED_HUNK)
hunk_index = undecided_next;
else
err(s, _("No next hunk"));
err(s, _("No other undecided hunk"));
} else if (s->answer.buf[0] == 'g') {
char *pend;
unsigned long response;

View File

@@ -450,21 +450,20 @@ void find_bisection(struct commit_list **commit_list, int *reaches,
clear_commit_weight(&commit_weight);
}
static int register_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
int flags UNUSED, void *cb_data UNUSED)
static int register_ref(const struct reference *ref, void *cb_data UNUSED)
{
struct strbuf good_prefix = STRBUF_INIT;
strbuf_addstr(&good_prefix, term_good);
strbuf_addstr(&good_prefix, "-");
if (!strcmp(refname, term_bad)) {
if (!strcmp(ref->name, term_bad)) {
free(current_bad_oid);
current_bad_oid = xmalloc(sizeof(*current_bad_oid));
oidcpy(current_bad_oid, oid);
} else if (starts_with(refname, good_prefix.buf)) {
oid_array_append(&good_revs, oid);
} else if (starts_with(refname, "skip-")) {
oid_array_append(&skipped_revs, oid);
oidcpy(current_bad_oid, ref->oid);
} else if (starts_with(ref->name, good_prefix.buf)) {
oid_array_append(&good_revs, ref->oid);
} else if (starts_with(ref->name, "skip-")) {
oid_array_append(&skipped_revs, ref->oid);
}
strbuf_release(&good_prefix);
@@ -1178,14 +1177,11 @@ int estimate_bisect_steps(int all)
return (e < 3 * x) ? n : n - 1;
}
static int mark_for_removal(const char *refname,
const char *referent UNUSED,
const struct object_id *oid UNUSED,
int flag UNUSED, void *cb_data)
static int mark_for_removal(const struct reference *ref, void *cb_data)
{
struct string_list *refs = cb_data;
char *ref = xstrfmt("refs/bisect%s", refname);
string_list_append(refs, ref);
char *bisect_ref = xstrfmt("refs/bisect%s", ref->name);
string_list_append(refs, bisect_ref);
return 0;
}

View File

@@ -358,10 +358,7 @@ static int check_and_set_terms(struct bisect_terms *terms, const char *cmd)
return 0;
}
static int inc_nr(const char *refname UNUSED,
const char *referent UNUSED,
const struct object_id *oid UNUSED,
int flag UNUSED, void *cb_data)
static int inc_nr(const struct reference *ref UNUSED, void *cb_data)
{
unsigned int *nr = (unsigned int *)cb_data;
(*nr)++;
@@ -549,12 +546,11 @@ finish:
return res;
}
static int add_bisect_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
int flags UNUSED, void *cb)
static int add_bisect_ref(const struct reference *ref, void *cb)
{
struct add_bisect_ref_data *data = cb;
add_pending_oid(data->revs, refname, oid, data->object_flags);
add_pending_oid(data->revs, ref->name, ref->oid, data->object_flags);
return 0;
}
@@ -1165,12 +1161,9 @@ static int bisect_visualize(struct bisect_terms *terms, int argc,
return run_command(&cmd);
}
static int get_first_good(const char *refname UNUSED,
const char *referent UNUSED,
const struct object_id *oid,
int flag UNUSED, void *cb_data)
static int get_first_good(const struct reference *ref, void *cb_data)
{
oidcpy(cb_data, oid);
oidcpy(cb_data, ref->oid);
return 1;
}

View File

@@ -495,7 +495,7 @@ static void batch_object_write(const char *obj_name,
OBJECT_INFO_LOOKUP_REPLACE);
if (ret < 0) {
if (data->mode == S_IFGITLINK)
report_object_status(opt, oid_to_hex(&data->oid), &data->oid, "submodule");
report_object_status(opt, NULL, &data->oid, "submodule");
else
report_object_status(opt, obj_name, &data->oid, "missing");
return;

View File

@@ -1063,11 +1063,9 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
report_tracking(new_branch_info);
}
static int add_pending_uninteresting_ref(const char *refname, const char *referent UNUSED,
const struct object_id *oid,
int flags UNUSED, void *cb_data)
static int add_pending_uninteresting_ref(const struct reference *ref, void *cb_data)
{
add_pending_oid(cb_data, refname, oid, UNINTERESTING);
add_pending_oid(cb_data, ref->name, ref->oid, UNINTERESTING);
return 0;
}

View File

@@ -154,20 +154,19 @@ static void add_to_known_names(const char *path,
}
}
static int get_name(const char *path, const char *referent UNUSED, const struct object_id *oid,
int flag UNUSED, void *cb_data UNUSED)
static int get_name(const struct reference *ref, void *cb_data UNUSED)
{
int is_tag = 0;
struct object_id peeled;
int is_annotated, prio;
const char *path_to_match = NULL;
if (skip_prefix(path, "refs/tags/", &path_to_match)) {
if (skip_prefix(ref->name, "refs/tags/", &path_to_match)) {
is_tag = 1;
} else if (all) {
if ((exclude_patterns.nr || patterns.nr) &&
!skip_prefix(path, "refs/heads/", &path_to_match) &&
!skip_prefix(path, "refs/remotes/", &path_to_match)) {
!skip_prefix(ref->name, "refs/heads/", &path_to_match) &&
!skip_prefix(ref->name, "refs/remotes/", &path_to_match)) {
/* Only accept reference of known type if there are match/exclude patterns */
return 0;
}
@@ -209,10 +208,10 @@ static int get_name(const char *path, const char *referent UNUSED, const struct
}
/* Is it annotated? */
if (!peel_iterated_oid(the_repository, oid, &peeled)) {
is_annotated = !oideq(oid, &peeled);
if (!reference_get_peeled_oid(the_repository, ref, &peeled)) {
is_annotated = !oideq(ref->oid, &peeled);
} else {
oidcpy(&peeled, oid);
oidcpy(&peeled, ref->oid);
is_annotated = 0;
}
@@ -229,7 +228,8 @@ static int get_name(const char *path, const char *referent UNUSED, const struct
else
prio = 0;
add_to_known_names(all ? path + 5 : path + 10, &peeled, prio, oid);
add_to_known_names(all ? ref->name + 5 : ref->name + 10,
&peeled, prio, ref->oid);
return 0;
}

View File

@@ -289,13 +289,11 @@ static struct refname_hash_entry *refname_hash_add(struct hashmap *map,
return ent;
}
static int add_one_refname(const char *refname, const char *referent UNUSED,
const struct object_id *oid,
int flag UNUSED, void *cbdata)
static int add_one_refname(const struct reference *ref, void *cbdata)
{
struct hashmap *refname_map = cbdata;
(void) refname_hash_add(refname_map, refname, oid);
(void) refname_hash_add(refname_map, ref->name, ref->oid);
return 0;
}
@@ -1416,14 +1414,11 @@ static void set_option(struct transport *transport, const char *name, const char
}
static int add_oid(const char *refname UNUSED,
const char *referent UNUSED,
const struct object_id *oid,
int flags UNUSED, void *cb_data)
static int add_oid(const struct reference *ref, void *cb_data)
{
struct oid_array *oids = cb_data;
oid_array_append(oids, oid);
oid_array_append(oids, ref->oid);
return 0;
}

View File

@@ -530,14 +530,13 @@ static int fsck_handle_reflog(const char *logname, void *cb_data)
return 0;
}
static int fsck_handle_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
int flag UNUSED, void *cb_data UNUSED)
static int fsck_handle_ref(const struct reference *ref, void *cb_data UNUSED)
{
struct object *obj;
obj = parse_object(the_repository, oid);
obj = parse_object(the_repository, ref->oid);
if (!obj) {
if (is_promisor_object(the_repository, oid)) {
if (is_promisor_object(the_repository, ref->oid)) {
/*
* Increment default_refs anyway, because this is a
* valid ref.
@@ -546,19 +545,19 @@ static int fsck_handle_ref(const char *refname, const char *referent UNUSED, con
return 0;
}
error(_("%s: invalid sha1 pointer %s"),
refname, oid_to_hex(oid));
ref->name, oid_to_hex(ref->oid));
errors_found |= ERROR_REACHABLE;
/* We'll continue with the rest despite the error.. */
return 0;
}
if (obj->type != OBJ_COMMIT && is_branch(refname)) {
error(_("%s: not a commit"), refname);
if (obj->type != OBJ_COMMIT && is_branch(ref->name)) {
error(_("%s: not a commit"), ref->name);
errors_found |= ERROR_REFS;
}
default_refs++;
obj->flags |= USED;
fsck_put_object_name(&fsck_walk_options,
oid, "%s", refname);
ref->oid, "%s", ref->name);
mark_object_reachable(obj);
return 0;
@@ -580,13 +579,19 @@ static void get_default_heads(void)
worktrees = get_worktrees();
for (p = worktrees; *p; p++) {
struct worktree *wt = *p;
struct strbuf ref = STRBUF_INIT;
struct strbuf refname = STRBUF_INIT;
strbuf_worktree_ref(wt, &ref, "HEAD");
fsck_head_link(ref.buf, &head_points_at, &head_oid);
if (head_points_at && !is_null_oid(&head_oid))
fsck_handle_ref(ref.buf, NULL, &head_oid, 0, NULL);
strbuf_release(&ref);
strbuf_worktree_ref(wt, &refname, "HEAD");
fsck_head_link(refname.buf, &head_points_at, &head_oid);
if (head_points_at && !is_null_oid(&head_oid)) {
struct reference ref = {
.name = refname.buf,
.oid = &head_oid,
};
fsck_handle_ref(&ref, NULL);
}
strbuf_release(&refname);
if (include_reflogs)
refs_for_each_reflog(get_worktree_ref_store(wt),

View File

@@ -1100,24 +1100,21 @@ struct cg_auto_data {
int limit;
};
static int dfs_on_ref(const char *refname UNUSED,
const char *referent UNUSED,
const struct object_id *oid,
int flags UNUSED,
void *cb_data)
static int dfs_on_ref(const struct reference *ref, void *cb_data)
{
struct cg_auto_data *data = (struct cg_auto_data *)cb_data;
int result = 0;
const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
struct commit_list *stack = NULL;
struct commit *commit;
if (!peel_iterated_oid(the_repository, oid, &peeled))
oid = &peeled;
if (odb_read_object_info(the_repository->objects, oid, NULL) != OBJ_COMMIT)
if (!reference_get_peeled_oid(the_repository, ref, &peeled))
maybe_peeled = &peeled;
if (odb_read_object_info(the_repository->objects, maybe_peeled, NULL) != OBJ_COMMIT)
return 0;
commit = lookup_commit(the_repository, oid);
commit = lookup_commit(the_repository, maybe_peeled);
if (!commit)
return 0;
if (repo_parse_commit(the_repository, commit) ||

View File

@@ -156,7 +156,7 @@ int cmd_ls_remote(int argc,
continue;
if (!tail_match(&pattern, ref->name))
continue;
item = ref_array_push(&ref_array, ref->name, &ref->old_oid);
item = ref_array_push(&ref_array, ref->name, &ref->old_oid, NULL);
item->symref = xstrdup_or_null(ref->symref);
}

View File

@@ -339,10 +339,9 @@ static int cmp_by_tag_and_age(const void *a_, const void *b_)
return a->taggerdate != b->taggerdate;
}
static int name_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,
int flags UNUSED, void *cb_data)
static int name_ref(const struct reference *ref, void *cb_data)
{
struct object *o = parse_object(the_repository, oid);
struct object *o = parse_object(the_repository, ref->oid);
struct name_ref_data *data = cb_data;
int can_abbreviate_output = data->tags_only && data->name_only;
int deref = 0;
@@ -350,14 +349,14 @@ static int name_ref(const char *path, const char *referent UNUSED, const struct
struct commit *commit = NULL;
timestamp_t taggerdate = TIME_MAX;
if (data->tags_only && !starts_with(path, "refs/tags/"))
if (data->tags_only && !starts_with(ref->name, "refs/tags/"))
return 0;
if (data->exclude_filters.nr) {
struct string_list_item *item;
for_each_string_list_item(item, &data->exclude_filters) {
if (subpath_matches(path, item->string) >= 0)
if (subpath_matches(ref->name, item->string) >= 0)
return 0;
}
}
@@ -378,7 +377,7 @@ static int name_ref(const char *path, const char *referent UNUSED, const struct
* shouldn't stop when seeing 'refs/tags/v1.4' matches
* 'refs/tags/v*'. We should show it as 'v1.4'.
*/
switch (subpath_matches(path, item->string)) {
switch (subpath_matches(ref->name, item->string)) {
case -1: /* did not match */
break;
case 0: /* matched fully */
@@ -406,13 +405,13 @@ static int name_ref(const char *path, const char *referent UNUSED, const struct
}
if (o && o->type == OBJ_COMMIT) {
commit = (struct commit *)o;
from_tag = starts_with(path, "refs/tags/");
from_tag = starts_with(ref->name, "refs/tags/");
if (taggerdate == TIME_MAX)
taggerdate = commit->date;
}
add_to_tip_table(oid, path, can_abbreviate_output, commit, taggerdate,
from_tag, deref);
add_to_tip_table(ref->oid, ref->name, can_abbreviate_output,
commit, taggerdate, from_tag, deref);
return 0;
}

View File

@@ -831,15 +831,14 @@ static enum write_one_status write_one(struct hashfile *f,
return WRITE_ONE_WRITTEN;
}
static int mark_tagged(const char *path UNUSED, const char *referent UNUSED, const struct object_id *oid,
int flag UNUSED, void *cb_data UNUSED)
static int mark_tagged(const struct reference *ref, void *cb_data UNUSED)
{
struct object_id peeled;
struct object_entry *entry = packlist_find(&to_pack, oid);
struct object_entry *entry = packlist_find(&to_pack, ref->oid);
if (entry)
entry->tagged = 1;
if (!peel_iterated_oid(the_repository, oid, &peeled)) {
if (!reference_get_peeled_oid(the_repository, ref, &peeled)) {
entry = packlist_find(&to_pack, &peeled);
if (entry)
entry->tagged = 1;
@@ -3306,13 +3305,13 @@ static void add_tag_chain(const struct object_id *oid)
}
}
static int add_ref_tag(const char *tag UNUSED, const char *referent UNUSED, const struct object_id *oid,
int flag UNUSED, void *cb_data UNUSED)
static int add_ref_tag(const struct reference *ref, void *cb_data UNUSED)
{
struct object_id peeled;
if (!peel_iterated_oid(the_repository, oid, &peeled) && obj_is_packed(&peeled))
add_tag_chain(oid);
if (!reference_get_peeled_oid(the_repository, ref, &peeled) &&
obj_is_packed(&peeled))
add_tag_chain(ref->oid);
return 0;
}
@@ -4533,19 +4532,16 @@ static void record_recent_commit(struct commit *commit, void *data UNUSED)
oid_array_append(&recent_objects, &commit->object.oid);
}
static int mark_bitmap_preferred_tip(const char *refname,
const char *referent UNUSED,
const struct object_id *oid,
int flags UNUSED,
void *data UNUSED)
static int mark_bitmap_preferred_tip(const struct reference *ref, void *data UNUSED)
{
const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
struct object *object;
if (!peel_iterated_oid(the_repository, oid, &peeled))
oid = &peeled;
if (!reference_get_peeled_oid(the_repository, ref, &peeled))
maybe_peeled = &peeled;
object = parse_object_or_die(the_repository, oid, refname);
object = parse_object_or_die(the_repository, maybe_peeled, ref->name);
if (object->type == OBJ_COMMIT)
object->flags |= NEEDS_BITMAP;

View File

@@ -305,13 +305,12 @@ static void show_ref(const char *path, const struct object_id *oid)
}
}
static int show_ref_cb(const char *path_full, const char *referent UNUSED, const struct object_id *oid,
int flag UNUSED, void *data)
static int show_ref_cb(const struct reference *ref, void *data)
{
struct oidset *seen = data;
const char *path = strip_namespace(path_full);
const char *path = strip_namespace(ref->name);
if (ref_is_hidden(path, path_full, &hidden_refs))
if (ref_is_hidden(path, ref->name, &hidden_refs))
return 0;
/*
@@ -320,13 +319,13 @@ static int show_ref_cb(const char *path_full, const char *referent UNUSED, const
* transfer but will otherwise ignore them.
*/
if (!path) {
if (oidset_insert(seen, oid))
if (oidset_insert(seen, ref->oid))
return 0;
path = ".have";
} else {
oidset_insert(seen, oid);
oidset_insert(seen, ref->oid);
}
show_ref(path, oid);
show_ref(path, ref->oid);
return 0;
}

View File

@@ -570,17 +570,14 @@ struct branches_for_remote {
struct known_remotes *keep;
};
static int add_branch_for_removal(const char *refname,
const char *referent UNUSED,
const struct object_id *oid UNUSED,
int flags UNUSED, void *cb_data)
static int add_branch_for_removal(const struct reference *ref, void *cb_data)
{
struct branches_for_remote *branches = cb_data;
struct refspec_item refspec;
struct known_remote *kr;
memset(&refspec, 0, sizeof(refspec));
refspec.dst = (char *)refname;
refspec.dst = (char *)ref->name;
if (remote_find_tracking(branches->remote, &refspec))
return 0;
free(refspec.src);
@@ -588,7 +585,7 @@ static int add_branch_for_removal(const char *refname,
/* don't delete a branch if another remote also uses it */
for (kr = branches->keep->list; kr; kr = kr->next) {
memset(&refspec, 0, sizeof(refspec));
refspec.dst = (char *)refname;
refspec.dst = (char *)ref->name;
if (!remote_find_tracking(kr->remote, &refspec)) {
free(refspec.src);
return 0;
@@ -596,16 +593,16 @@ static int add_branch_for_removal(const char *refname,
}
/* don't delete non-remote-tracking refs */
if (!starts_with(refname, "refs/remotes/")) {
if (!starts_with(ref->name, "refs/remotes/")) {
/* advise user how to delete local branches */
if (starts_with(refname, "refs/heads/"))
if (starts_with(ref->name, "refs/heads/"))
string_list_append(branches->skipped,
abbrev_branch(refname));
abbrev_branch(ref->name));
/* silently skip over other non-remote refs */
return 0;
}
string_list_append(branches->branches, refname);
string_list_append(branches->branches, ref->name);
return 0;
}
@@ -713,18 +710,18 @@ out:
return error;
}
static int rename_one_ref(const char *old_refname, const char *referent,
const struct object_id *oid,
int flags, void *cb_data)
static int rename_one_ref(const struct reference *ref, void *cb_data)
{
struct strbuf new_referent = STRBUF_INIT;
struct strbuf new_refname = STRBUF_INIT;
struct rename_info *rename = cb_data;
const struct object_id *oid = ref->oid;
const char *referent = ref->target;
int error;
compute_renamed_ref(rename, old_refname, &new_refname);
compute_renamed_ref(rename, ref->name, &new_refname);
if (flags & REF_ISSYMREF) {
if (ref->flags & REF_ISSYMREF) {
/*
* Stupidly enough `referent` is not pointing to the immediate
* target of a symref, but it's the recursively resolved value.
@@ -732,25 +729,25 @@ static int rename_one_ref(const char *old_refname, const char *referent,
* unborn symrefs don't have any value for the `referent` at all.
*/
referent = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
old_refname, RESOLVE_REF_NO_RECURSE,
ref->name, RESOLVE_REF_NO_RECURSE,
NULL, NULL);
compute_renamed_ref(rename, referent, &new_referent);
oid = NULL;
}
error = ref_transaction_delete(rename->transaction, old_refname,
error = ref_transaction_delete(rename->transaction, ref->name,
oid, referent, REF_NO_DEREF, NULL, rename->err);
if (error < 0)
goto out;
error = ref_transaction_update(rename->transaction, new_refname.buf, oid, null_oid(the_hash_algo),
(flags & REF_ISSYMREF) ? new_referent.buf : NULL, NULL,
(ref->flags & REF_ISSYMREF) ? new_referent.buf : NULL, NULL,
REF_SKIP_CREATE_REFLOG | REF_NO_DEREF | REF_SKIP_OID_VERIFICATION,
NULL, rename->err);
if (error < 0)
goto out;
error = rename_one_reflog(old_refname, oid, rename);
error = rename_one_reflog(ref->name, oid, rename);
if (error < 0)
goto out;
@@ -1125,19 +1122,16 @@ static void free_remote_ref_states(struct ref_states *states)
string_list_clear_func(&states->push, clear_push_info);
}
static int append_ref_to_tracked_list(const char *refname,
const char *referent UNUSED,
const struct object_id *oid UNUSED,
int flags, void *cb_data)
static int append_ref_to_tracked_list(const struct reference *ref, void *cb_data)
{
struct ref_states *states = cb_data;
struct refspec_item refspec;
if (flags & REF_ISSYMREF)
if (ref->flags & REF_ISSYMREF)
return 0;
memset(&refspec, 0, sizeof(refspec));
refspec.dst = (char *)refname;
refspec.dst = (char *)ref->name;
if (!remote_find_tracking(states->remote, &refspec)) {
string_list_append(&states->tracked, abbrev_branch(refspec.src));
free(refspec.src);

File diff suppressed because it is too large Load Diff

View File

@@ -47,30 +47,27 @@ struct show_data {
enum replace_format format;
};
static int show_reference(const char *refname,
const char *referent UNUSED,
const struct object_id *oid,
int flag UNUSED, void *cb_data)
static int show_reference(const struct reference *ref, void *cb_data)
{
struct show_data *data = cb_data;
if (!wildmatch(data->pattern, refname, 0)) {
if (!wildmatch(data->pattern, ref->name, 0)) {
if (data->format == REPLACE_FORMAT_SHORT)
printf("%s\n", refname);
printf("%s\n", ref->name);
else if (data->format == REPLACE_FORMAT_MEDIUM)
printf("%s -> %s\n", refname, oid_to_hex(oid));
printf("%s -> %s\n", ref->name, oid_to_hex(ref->oid));
else { /* data->format == REPLACE_FORMAT_LONG */
struct object_id object;
enum object_type obj_type, repl_type;
if (repo_get_oid(data->repo, refname, &object))
return error(_("failed to resolve '%s' as a valid ref"), refname);
if (repo_get_oid(data->repo, ref->name, &object))
return error(_("failed to resolve '%s' as a valid ref"), ref->name);
obj_type = odb_read_object_info(data->repo->objects, &object, NULL);
repl_type = odb_read_object_info(data->repo->objects, oid, NULL);
repl_type = odb_read_object_info(data->repo->objects, ref->oid, NULL);
printf("%s (%s) -> %s (%s)\n", refname, type_name(obj_type),
oid_to_hex(oid), type_name(repl_type));
printf("%s (%s) -> %s (%s)\n", ref->name, type_name(obj_type),
oid_to_hex(ref->oid), type_name(repl_type));
}
}

View File

@@ -3,19 +3,27 @@
#include "builtin.h"
#include "environment.h"
#include "parse-options.h"
#include "path-walk.h"
#include "progress.h"
#include "quote.h"
#include "ref-filter.h"
#include "refs.h"
#include "revision.h"
#include "strbuf.h"
#include "string-list.h"
#include "shallow.h"
#include "utf8.h"
static const char *const repo_usage[] = {
"git repo info [--format=(keyvalue|nul)] [-z] [<key>...]",
"git repo structure [--format=(table|keyvalue|nul)]",
NULL
};
typedef int get_value_fn(struct repository *repo, struct strbuf *buf);
enum output_format {
FORMAT_TABLE,
FORMAT_KEYVALUE,
FORMAT_NUL_TERMINATED,
};
@@ -130,14 +138,16 @@ static int parse_format_cb(const struct option *opt,
*format = FORMAT_NUL_TERMINATED;
else if (!strcmp(arg, "keyvalue"))
*format = FORMAT_KEYVALUE;
else if (!strcmp(arg, "table"))
*format = FORMAT_TABLE;
else
die(_("invalid format '%s'"), arg);
return 0;
}
static int repo_info(int argc, const char **argv, const char *prefix,
struct repository *repo)
static int cmd_repo_info(int argc, const char **argv, const char *prefix,
struct repository *repo)
{
enum output_format format = FORMAT_KEYVALUE;
struct option options[] = {
@@ -152,16 +162,377 @@ static int repo_info(int argc, const char **argv, const char *prefix,
};
argc = parse_options(argc, argv, prefix, options, repo_usage, 0);
if (format != FORMAT_KEYVALUE && format != FORMAT_NUL_TERMINATED)
die(_("unsupported output format"));
return print_fields(argc, argv, repo, format);
}
struct ref_stats {
size_t branches;
size_t remotes;
size_t tags;
size_t others;
};
struct object_stats {
size_t tags;
size_t commits;
size_t trees;
size_t blobs;
};
struct repo_structure {
struct ref_stats refs;
struct object_stats objects;
};
struct stats_table {
struct string_list rows;
int name_col_width;
int value_col_width;
};
/*
* Holds column data that gets stored for each row.
*/
struct stats_table_entry {
char *value;
};
static void stats_table_vaddf(struct stats_table *table,
struct stats_table_entry *entry,
const char *format, va_list ap)
{
struct strbuf buf = STRBUF_INIT;
struct string_list_item *item;
char *formatted_name;
int name_width;
strbuf_vaddf(&buf, format, ap);
formatted_name = strbuf_detach(&buf, NULL);
name_width = utf8_strwidth(formatted_name);
item = string_list_append_nodup(&table->rows, formatted_name);
item->util = entry;
if (name_width > table->name_col_width)
table->name_col_width = name_width;
if (entry) {
int value_width = utf8_strwidth(entry->value);
if (value_width > table->value_col_width)
table->value_col_width = value_width;
}
}
static void stats_table_addf(struct stats_table *table, const char *format, ...)
{
va_list ap;
va_start(ap, format);
stats_table_vaddf(table, NULL, format, ap);
va_end(ap);
}
static void stats_table_count_addf(struct stats_table *table, size_t value,
const char *format, ...)
{
struct stats_table_entry *entry;
va_list ap;
CALLOC_ARRAY(entry, 1);
entry->value = xstrfmt("%" PRIuMAX, (uintmax_t)value);
va_start(ap, format);
stats_table_vaddf(table, entry, format, ap);
va_end(ap);
}
static inline size_t get_total_reference_count(struct ref_stats *stats)
{
return stats->branches + stats->remotes + stats->tags + stats->others;
}
static inline size_t get_total_object_count(struct object_stats *stats)
{
return stats->tags + stats->commits + stats->trees + stats->blobs;
}
static void stats_table_setup_structure(struct stats_table *table,
struct repo_structure *stats)
{
struct object_stats *objects = &stats->objects;
struct ref_stats *refs = &stats->refs;
size_t object_total;
size_t ref_total;
ref_total = get_total_reference_count(refs);
stats_table_addf(table, "* %s", _("References"));
stats_table_count_addf(table, ref_total, " * %s", _("Count"));
stats_table_count_addf(table, refs->branches, " * %s", _("Branches"));
stats_table_count_addf(table, refs->tags, " * %s", _("Tags"));
stats_table_count_addf(table, refs->remotes, " * %s", _("Remotes"));
stats_table_count_addf(table, refs->others, " * %s", _("Others"));
object_total = get_total_object_count(objects);
stats_table_addf(table, "");
stats_table_addf(table, "* %s", _("Reachable objects"));
stats_table_count_addf(table, object_total, " * %s", _("Count"));
stats_table_count_addf(table, objects->commits, " * %s", _("Commits"));
stats_table_count_addf(table, objects->trees, " * %s", _("Trees"));
stats_table_count_addf(table, objects->blobs, " * %s", _("Blobs"));
stats_table_count_addf(table, objects->tags, " * %s", _("Tags"));
}
static void stats_table_print_structure(const struct stats_table *table)
{
const char *name_col_title = _("Repository structure");
const char *value_col_title = _("Value");
int name_col_width = utf8_strwidth(name_col_title);
int value_col_width = utf8_strwidth(value_col_title);
struct string_list_item *item;
if (table->name_col_width > name_col_width)
name_col_width = table->name_col_width;
if (table->value_col_width > value_col_width)
value_col_width = table->value_col_width;
printf("| %-*s | %-*s |\n", name_col_width, name_col_title,
value_col_width, value_col_title);
printf("| ");
for (int i = 0; i < name_col_width; i++)
putchar('-');
printf(" | ");
for (int i = 0; i < value_col_width; i++)
putchar('-');
printf(" |\n");
for_each_string_list_item(item, &table->rows) {
struct stats_table_entry *entry = item->util;
const char *value = "";
if (entry) {
struct stats_table_entry *entry = item->util;
value = entry->value;
}
printf("| %-*s | %*s |\n", name_col_width, item->string,
value_col_width, value);
}
}
static void stats_table_clear(struct stats_table *table)
{
struct stats_table_entry *entry;
struct string_list_item *item;
for_each_string_list_item(item, &table->rows) {
entry = item->util;
if (entry)
free(entry->value);
}
string_list_clear(&table->rows, 1);
}
static void structure_keyvalue_print(struct repo_structure *stats,
char key_delim, char value_delim)
{
printf("references.branches.count%c%" PRIuMAX "%c", key_delim,
(uintmax_t)stats->refs.branches, value_delim);
printf("references.tags.count%c%" PRIuMAX "%c", key_delim,
(uintmax_t)stats->refs.tags, value_delim);
printf("references.remotes.count%c%" PRIuMAX "%c", key_delim,
(uintmax_t)stats->refs.remotes, value_delim);
printf("references.others.count%c%" PRIuMAX "%c", key_delim,
(uintmax_t)stats->refs.others, value_delim);
printf("objects.commits.count%c%" PRIuMAX "%c", key_delim,
(uintmax_t)stats->objects.commits, value_delim);
printf("objects.trees.count%c%" PRIuMAX "%c", key_delim,
(uintmax_t)stats->objects.trees, value_delim);
printf("objects.blobs.count%c%" PRIuMAX "%c", key_delim,
(uintmax_t)stats->objects.blobs, value_delim);
printf("objects.tags.count%c%" PRIuMAX "%c", key_delim,
(uintmax_t)stats->objects.tags, value_delim);
fflush(stdout);
}
struct count_references_data {
struct ref_stats *stats;
struct rev_info *revs;
struct progress *progress;
};
static int count_references(const struct reference *ref, void *cb_data)
{
struct count_references_data *data = cb_data;
struct ref_stats *stats = data->stats;
size_t ref_count;
switch (ref_kind_from_refname(ref->name)) {
case FILTER_REFS_BRANCHES:
stats->branches++;
break;
case FILTER_REFS_REMOTES:
stats->remotes++;
break;
case FILTER_REFS_TAGS:
stats->tags++;
break;
case FILTER_REFS_OTHERS:
stats->others++;
break;
default:
BUG("unexpected reference type");
}
/*
* While iterating through references for counting, also add OIDs in
* preparation for the path walk.
*/
add_pending_oid(data->revs, NULL, ref->oid, 0);
ref_count = get_total_reference_count(stats);
display_progress(data->progress, ref_count);
return 0;
}
static void structure_count_references(struct ref_stats *stats,
struct rev_info *revs,
struct repository *repo,
int show_progress)
{
struct count_references_data data = {
.stats = stats,
.revs = revs,
};
if (show_progress)
data.progress = start_delayed_progress(repo,
_("Counting references"), 0);
refs_for_each_ref(get_main_ref_store(repo), count_references, &data);
stop_progress(&data.progress);
}
struct count_objects_data {
struct object_stats *stats;
struct progress *progress;
};
static int count_objects(const char *path UNUSED, struct oid_array *oids,
enum object_type type, void *cb_data)
{
struct count_objects_data *data = cb_data;
struct object_stats *stats = data->stats;
size_t object_count;
switch (type) {
case OBJ_TAG:
stats->tags += oids->nr;
break;
case OBJ_COMMIT:
stats->commits += oids->nr;
break;
case OBJ_TREE:
stats->trees += oids->nr;
break;
case OBJ_BLOB:
stats->blobs += oids->nr;
break;
default:
BUG("invalid object type");
}
object_count = get_total_object_count(stats);
display_progress(data->progress, object_count);
return 0;
}
static void structure_count_objects(struct object_stats *stats,
struct rev_info *revs,
struct repository *repo, int show_progress)
{
struct path_walk_info info = PATH_WALK_INFO_INIT;
struct count_objects_data data = {
.stats = stats,
};
info.revs = revs;
info.path_fn = count_objects;
info.path_fn_data = &data;
if (show_progress)
data.progress = start_delayed_progress(repo, _("Counting objects"), 0);
walk_objects_by_path(&info);
path_walk_info_clear(&info);
stop_progress(&data.progress);
}
static int cmd_repo_structure(int argc, const char **argv, const char *prefix,
struct repository *repo)
{
struct stats_table table = {
.rows = STRING_LIST_INIT_DUP,
};
enum output_format format = FORMAT_TABLE;
struct repo_structure stats = { 0 };
struct rev_info revs;
int show_progress = -1;
struct option options[] = {
OPT_CALLBACK_F(0, "format", &format, N_("format"),
N_("output format"),
PARSE_OPT_NONEG, parse_format_cb),
OPT_BOOL(0, "progress", &show_progress, N_("show progress")),
OPT_END()
};
argc = parse_options(argc, argv, prefix, options, repo_usage, 0);
if (argc)
usage(_("too many arguments"));
repo_init_revisions(repo, &revs, prefix);
if (show_progress < 0)
show_progress = isatty(2);
structure_count_references(&stats.refs, &revs, repo, show_progress);
structure_count_objects(&stats.objects, &revs, repo, show_progress);
switch (format) {
case FORMAT_TABLE:
stats_table_setup_structure(&table, &stats);
stats_table_print_structure(&table);
break;
case FORMAT_KEYVALUE:
structure_keyvalue_print(&stats, '=', '\n');
break;
case FORMAT_NUL_TERMINATED:
structure_keyvalue_print(&stats, '\n', '\0');
break;
default:
BUG("invalid output format");
}
stats_table_clear(&table);
release_revisions(&revs);
return 0;
}
int cmd_repo(int argc, const char **argv, const char *prefix,
struct repository *repo)
{
parse_opt_subcommand_fn *fn = NULL;
struct option options[] = {
OPT_SUBCOMMAND("info", &fn, repo_info),
OPT_SUBCOMMAND("info", &fn, cmd_repo_info),
OPT_SUBCOMMAND("structure", &fn, cmd_repo_structure),
OPT_END()
};

View File

@@ -217,19 +217,17 @@ static int show_default(void)
return 0;
}
static int show_reference(const char *refname, const char *referent UNUSED, const struct object_id *oid,
int flag UNUSED, void *cb_data UNUSED)
static int show_reference(const struct reference *ref, void *cb_data UNUSED)
{
if (ref_excluded(&ref_excludes, refname))
if (ref_excluded(&ref_excludes, ref->name))
return 0;
show_rev(NORMAL, oid, refname);
show_rev(NORMAL, ref->oid, ref->name);
return 0;
}
static int anti_reference(const char *refname, const char *referent UNUSED, const struct object_id *oid,
int flag UNUSED, void *cb_data UNUSED)
static int anti_reference(const struct reference *ref, void *cb_data UNUSED)
{
show_rev(REVERSED, oid, refname);
show_rev(REVERSED, ref->oid, ref->name);
return 0;
}

View File

@@ -413,34 +413,32 @@ static int append_ref(const char *refname, const struct object_id *oid,
return 0;
}
static int append_head_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
int flag UNUSED, void *cb_data UNUSED)
static int append_head_ref(const struct reference *ref, void *cb_data UNUSED)
{
struct object_id tmp;
int ofs = 11;
if (!starts_with(refname, "refs/heads/"))
if (!starts_with(ref->name, "refs/heads/"))
return 0;
/* If both heads/foo and tags/foo exists, get_sha1 would
* get confused.
*/
if (repo_get_oid(the_repository, refname + ofs, &tmp) || !oideq(&tmp, oid))
if (repo_get_oid(the_repository, ref->name + ofs, &tmp) || !oideq(&tmp, ref->oid))
ofs = 5;
return append_ref(refname + ofs, oid, 0);
return append_ref(ref->name + ofs, ref->oid, 0);
}
static int append_remote_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
int flag UNUSED, void *cb_data UNUSED)
static int append_remote_ref(const struct reference *ref, void *cb_data UNUSED)
{
struct object_id tmp;
int ofs = 13;
if (!starts_with(refname, "refs/remotes/"))
if (!starts_with(ref->name, "refs/remotes/"))
return 0;
/* If both heads/foo and tags/foo exists, get_sha1 would
* get confused.
*/
if (repo_get_oid(the_repository, refname + ofs, &tmp) || !oideq(&tmp, oid))
if (repo_get_oid(the_repository, ref->name + ofs, &tmp) || !oideq(&tmp, ref->oid))
ofs = 5;
return append_ref(refname + ofs, oid, 0);
return append_ref(ref->name + ofs, ref->oid, 0);
}
static int append_tag_ref(const char *refname, const struct object_id *oid,
@@ -454,27 +452,26 @@ static int append_tag_ref(const char *refname, const struct object_id *oid,
static const char *match_ref_pattern = NULL;
static int match_ref_slash = 0;
static int append_matching_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
int flag, void *cb_data)
static int append_matching_ref(const struct reference *ref, void *cb_data)
{
/* we want to allow pattern hold/<asterisk> to show all
* branches under refs/heads/hold/, and v0.99.9? to show
* refs/tags/v0.99.9a and friends.
*/
const char *tail;
int slash = count_slashes(refname);
for (tail = refname; *tail && match_ref_slash < slash; )
int slash = count_slashes(ref->name);
for (tail = ref->name; *tail && match_ref_slash < slash; )
if (*tail++ == '/')
slash--;
if (!*tail)
return 0;
if (wildmatch(match_ref_pattern, tail, 0))
return 0;
if (starts_with(refname, "refs/heads/"))
return append_head_ref(refname, NULL, oid, flag, cb_data);
if (starts_with(refname, "refs/tags/"))
return append_tag_ref(refname, oid, flag, cb_data);
return append_ref(refname, oid, 0);
if (starts_with(ref->name, "refs/heads/"))
return append_head_ref(ref, cb_data);
if (starts_with(ref->name, "refs/tags/"))
return append_tag_ref(ref->name, ref->oid, ref->flags, cb_data);
return append_ref(ref->name, ref->oid, 0);
}
static void snarf_refs(int head, int remotes)

View File

@@ -31,31 +31,31 @@ struct show_one_options {
};
static void show_one(const struct show_one_options *opts,
const char *refname, const struct object_id *oid)
const struct reference *ref)
{
const char *hex;
struct object_id peeled;
if (!odb_has_object(the_repository->objects, oid,
if (!odb_has_object(the_repository->objects, ref->oid,
HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
die("git show-ref: bad ref %s (%s)", refname,
oid_to_hex(oid));
die("git show-ref: bad ref %s (%s)", ref->name,
oid_to_hex(ref->oid));
if (opts->quiet)
return;
hex = repo_find_unique_abbrev(the_repository, oid, opts->abbrev);
hex = repo_find_unique_abbrev(the_repository, ref->oid, opts->abbrev);
if (opts->hash_only)
printf("%s\n", hex);
else
printf("%s %s\n", hex, refname);
printf("%s %s\n", hex, ref->name);
if (!opts->deref_tags)
return;
if (!peel_iterated_oid(the_repository, oid, &peeled)) {
if (!reference_get_peeled_oid(the_repository, ref, &peeled)) {
hex = repo_find_unique_abbrev(the_repository, &peeled, opts->abbrev);
printf("%s %s^{}\n", hex, refname);
printf("%s %s^{}\n", hex, ref->name);
}
}
@@ -66,26 +66,25 @@ struct show_ref_data {
int show_head;
};
static int show_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
int flag UNUSED, void *cbdata)
static int show_ref(const struct reference *ref, void *cbdata)
{
struct show_ref_data *data = cbdata;
if (data->show_head && !strcmp(refname, "HEAD"))
if (data->show_head && !strcmp(ref->name, "HEAD"))
goto match;
if (data->patterns) {
int reflen = strlen(refname);
int reflen = strlen(ref->name);
const char **p = data->patterns, *m;
while ((m = *p++) != NULL) {
int len = strlen(m);
if (len > reflen)
continue;
if (memcmp(m, refname + reflen - len, len))
if (memcmp(m, ref->name + reflen - len, len))
continue;
if (len == reflen)
goto match;
if (refname[reflen - len - 1] == '/')
if (ref->name[reflen - len - 1] == '/')
goto match;
}
return 0;
@@ -94,18 +93,15 @@ static int show_ref(const char *refname, const char *referent UNUSED, const stru
match:
data->found_match++;
show_one(data->show_one_opts, refname, oid);
show_one(data->show_one_opts, ref);
return 0;
}
static int add_existing(const char *refname,
const char *referent UNUSED,
const struct object_id *oid UNUSED,
int flag UNUSED, void *cbdata)
static int add_existing(const struct reference *ref, void *cbdata)
{
struct string_list *list = (struct string_list *)cbdata;
string_list_insert(list, refname);
string_list_insert(list, ref->name);
return 0;
}
@@ -179,12 +175,18 @@ static int cmd_show_ref__verify(const struct show_one_options *show_one_opts,
if ((starts_with(*refs, "refs/") || refname_is_safe(*refs)) &&
!refs_read_ref(get_main_ref_store(the_repository), *refs, &oid)) {
show_one(show_one_opts, *refs, &oid);
}
else if (!show_one_opts->quiet)
struct reference ref = {
.name = *refs,
.oid = &oid,
};
show_one(show_one_opts, &ref);
} else if (!show_one_opts->quiet) {
die("'%s' - not a valid ref", *refs);
else
} else {
return 1;
}
refs++;
}

View File

@@ -593,16 +593,12 @@ static void print_status(unsigned int flags, char state, const char *path,
printf("\n");
}
static int handle_submodule_head_ref(const char *refname UNUSED,
const char *referent UNUSED,
const struct object_id *oid,
int flags UNUSED,
void *cb_data)
static int handle_submodule_head_ref(const struct reference *ref, void *cb_data)
{
struct object_id *output = cb_data;
if (oid)
oidcpy(output, oid);
if (ref->oid)
oidcpy(output, ref->oid);
return 0;
}

View File

@@ -153,7 +153,7 @@ static int verify_tag(const char *name, const char *ref UNUSED,
return -1;
if (format->format)
pretty_print_ref(name, oid, format);
pretty_print_ref(name, oid, NULL, format);
return 0;
}

View File

@@ -67,7 +67,7 @@ int cmd_verify_tag(int argc,
}
if (format.format)
pretty_print_ref(name, &oid, &format);
pretty_print_ref(name, &oid, NULL, &format);
}
return had_error;
}

View File

@@ -635,11 +635,7 @@ static void print_preparing_worktree_line(int detach,
*
* Returns 0 on failure and non-zero on success.
*/
static int first_valid_ref(const char *refname UNUSED,
const char *referent UNUSED,
const struct object_id *oid UNUSED,
int flags UNUSED,
void *cb_data UNUSED)
static int first_valid_ref(const struct reference *ref UNUSED, void *cb_data UNUSED)
{
return 1;
}

403
bulk-checkin.c Normal file
View File

@@ -0,0 +1,403 @@
/*
* Copyright (c) 2011, Google Inc.
*/
#define USE_THE_REPOSITORY_VARIABLE
#include "git-compat-util.h"
#include "bulk-checkin.h"
#include "environment.h"
#include "gettext.h"
#include "hex.h"
#include "lockfile.h"
#include "repository.h"
#include "csum-file.h"
#include "pack.h"
#include "strbuf.h"
#include "tmp-objdir.h"
#include "packfile.h"
#include "object-file.h"
#include "odb.h"
struct bulk_checkin_packfile {
char *pack_tmp_name;
struct hashfile *f;
off_t offset;
struct pack_idx_option pack_idx_opts;
struct pack_idx_entry **written;
uint32_t alloc_written;
uint32_t nr_written;
};
struct odb_transaction {
struct object_database *odb;
int nesting;
struct tmp_objdir *objdir;
struct bulk_checkin_packfile packfile;
};
static void finish_tmp_packfile(struct odb_transaction *transaction,
struct strbuf *basename,
unsigned char hash[])
{
struct bulk_checkin_packfile *state = &transaction->packfile;
struct repository *repo = transaction->odb->repo;
char *idx_tmp_name = NULL;
stage_tmp_packfiles(repo, basename, state->pack_tmp_name,
state->written, state->nr_written, NULL,
&state->pack_idx_opts, hash, &idx_tmp_name);
rename_tmp_packfile_idx(repo, basename, &idx_tmp_name);
free(idx_tmp_name);
}
static void flush_bulk_checkin_packfile(struct odb_transaction *transaction)
{
struct bulk_checkin_packfile *state = &transaction->packfile;
struct repository *repo = transaction->odb->repo;
unsigned char hash[GIT_MAX_RAWSZ];
struct strbuf packname = STRBUF_INIT;
if (!state->f)
return;
if (state->nr_written == 0) {
close(state->f->fd);
free_hashfile(state->f);
unlink(state->pack_tmp_name);
goto clear_exit;
} else if (state->nr_written == 1) {
finalize_hashfile(state->f, hash, FSYNC_COMPONENT_PACK,
CSUM_HASH_IN_STREAM | CSUM_FSYNC | CSUM_CLOSE);
} else {
int fd = finalize_hashfile(state->f, hash, FSYNC_COMPONENT_PACK, 0);
fixup_pack_header_footer(repo->hash_algo, fd, hash, state->pack_tmp_name,
state->nr_written, hash,
state->offset);
close(fd);
}
strbuf_addf(&packname, "%s/pack/pack-%s.",
repo_get_object_directory(transaction->odb->repo),
hash_to_hex_algop(hash, repo->hash_algo));
finish_tmp_packfile(transaction, &packname, hash);
for (uint32_t i = 0; i < state->nr_written; i++)
free(state->written[i]);
clear_exit:
free(state->pack_tmp_name);
free(state->written);
memset(state, 0, sizeof(*state));
strbuf_release(&packname);
/* Make objects we just wrote available to ourselves */
odb_reprepare(repo->objects);
}
/*
* Cleanup after batch-mode fsync_object_files.
*/
static void flush_batch_fsync(struct odb_transaction *transaction)
{
struct strbuf temp_path = STRBUF_INIT;
struct tempfile *temp;
if (!transaction->objdir)
return;
/*
* Issue a full hardware flush against a temporary file to ensure
* that all objects are durable before any renames occur. The code in
* fsync_loose_object_bulk_checkin has already issued a writeout
* request, but it has not flushed any writeback cache in the storage
* hardware or any filesystem logs. This fsync call acts as a barrier
* to ensure that the data in each new object file is durable before
* the final name is visible.
*/
strbuf_addf(&temp_path, "%s/bulk_fsync_XXXXXX",
repo_get_object_directory(transaction->odb->repo));
temp = xmks_tempfile(temp_path.buf);
fsync_or_die(get_tempfile_fd(temp), get_tempfile_path(temp));
delete_tempfile(&temp);
strbuf_release(&temp_path);
/*
* Make the object files visible in the primary ODB after their data is
* fully durable.
*/
tmp_objdir_migrate(transaction->objdir);
transaction->objdir = NULL;
}
static int already_written(struct odb_transaction *transaction,
struct object_id *oid)
{
/* The object may already exist in the repository */
if (odb_has_object(transaction->odb, oid,
HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
return 1;
/* Might want to keep the list sorted */
for (uint32_t i = 0; i < transaction->packfile.nr_written; i++)
if (oideq(&transaction->packfile.written[i]->oid, oid))
return 1;
/* This is a new object we need to keep */
return 0;
}
/*
* Read the contents from fd for size bytes, streaming it to the
* packfile in state while updating the hash in ctx. Signal a failure
* by returning a negative value when the resulting pack would exceed
* the pack size limit and this is not the first object in the pack,
* so that the caller can discard what we wrote from the current pack
* by truncating it and opening a new one. The caller will then call
* us again after rewinding the input fd.
*
* The already_hashed_to pointer is kept untouched by the caller to
* make sure we do not hash the same byte when we are called
* again. This way, the caller does not have to checkpoint its hash
* status before calling us just in case we ask it to call us again
* with a new pack.
*/
static int stream_blob_to_pack(struct bulk_checkin_packfile *state,
struct git_hash_ctx *ctx, off_t *already_hashed_to,
int fd, size_t size, const char *path,
unsigned flags)
{
git_zstream s;
unsigned char ibuf[16384];
unsigned char obuf[16384];
unsigned hdrlen;
int status = Z_OK;
int write_object = (flags & INDEX_WRITE_OBJECT);
off_t offset = 0;
git_deflate_init(&s, pack_compression_level);
hdrlen = encode_in_pack_object_header(obuf, sizeof(obuf), OBJ_BLOB, size);
s.next_out = obuf + hdrlen;
s.avail_out = sizeof(obuf) - hdrlen;
while (status != Z_STREAM_END) {
if (size && !s.avail_in) {
size_t rsize = size < sizeof(ibuf) ? size : sizeof(ibuf);
ssize_t read_result = read_in_full(fd, ibuf, rsize);
if (read_result < 0)
die_errno("failed to read from '%s'", path);
if ((size_t)read_result != rsize)
die("failed to read %u bytes from '%s'",
(unsigned)rsize, path);
offset += rsize;
if (*already_hashed_to < offset) {
size_t hsize = offset - *already_hashed_to;
if (rsize < hsize)
hsize = rsize;
if (hsize)
git_hash_update(ctx, ibuf, hsize);
*already_hashed_to = offset;
}
s.next_in = ibuf;
s.avail_in = rsize;
size -= rsize;
}
status = git_deflate(&s, size ? 0 : Z_FINISH);
if (!s.avail_out || status == Z_STREAM_END) {
if (write_object) {
size_t written = s.next_out - obuf;
/* would we bust the size limit? */
if (state->nr_written &&
pack_size_limit_cfg &&
pack_size_limit_cfg < state->offset + written) {
git_deflate_abort(&s);
return -1;
}
hashwrite(state->f, obuf, written);
state->offset += written;
}
s.next_out = obuf;
s.avail_out = sizeof(obuf);
}
switch (status) {
case Z_OK:
case Z_BUF_ERROR:
case Z_STREAM_END:
continue;
default:
die("unexpected deflate failure: %d", status);
}
}
git_deflate_end(&s);
return 0;
}
/* Lazily create backing packfile for the state */
static void prepare_to_stream(struct odb_transaction *transaction,
unsigned flags)
{
struct bulk_checkin_packfile *state = &transaction->packfile;
if (!(flags & INDEX_WRITE_OBJECT) || state->f)
return;
state->f = create_tmp_packfile(transaction->odb->repo,
&state->pack_tmp_name);
reset_pack_idx_option(&state->pack_idx_opts);
/* Pretend we are going to write only one object */
state->offset = write_pack_header(state->f, 1);
if (!state->offset)
die_errno("unable to write pack header");
}
int index_blob_bulk_checkin(struct odb_transaction *transaction,
struct object_id *result_oid, int fd, size_t size,
const char *path, unsigned flags)
{
struct bulk_checkin_packfile *state = &transaction->packfile;
off_t seekback, already_hashed_to;
struct git_hash_ctx ctx;
unsigned char obuf[16384];
unsigned header_len;
struct hashfile_checkpoint checkpoint;
struct pack_idx_entry *idx = NULL;
seekback = lseek(fd, 0, SEEK_CUR);
if (seekback == (off_t) -1)
return error("cannot find the current offset");
header_len = format_object_header((char *)obuf, sizeof(obuf),
OBJ_BLOB, size);
transaction->odb->repo->hash_algo->init_fn(&ctx);
git_hash_update(&ctx, obuf, header_len);
/* Note: idx is non-NULL when we are writing */
if ((flags & INDEX_WRITE_OBJECT) != 0) {
CALLOC_ARRAY(idx, 1);
prepare_to_stream(transaction, flags);
hashfile_checkpoint_init(state->f, &checkpoint);
}
already_hashed_to = 0;
while (1) {
prepare_to_stream(transaction, flags);
if (idx) {
hashfile_checkpoint(state->f, &checkpoint);
idx->offset = state->offset;
crc32_begin(state->f);
}
if (!stream_blob_to_pack(state, &ctx, &already_hashed_to,
fd, size, path, flags))
break;
/*
* Writing this object to the current pack will make
* it too big; we need to truncate it, start a new
* pack, and write into it.
*/
if (!idx)
BUG("should not happen");
hashfile_truncate(state->f, &checkpoint);
state->offset = checkpoint.offset;
flush_bulk_checkin_packfile(transaction);
if (lseek(fd, seekback, SEEK_SET) == (off_t) -1)
return error("cannot seek back");
}
git_hash_final_oid(result_oid, &ctx);
if (!idx)
return 0;
idx->crc32 = crc32_end(state->f);
if (already_written(transaction, result_oid)) {
hashfile_truncate(state->f, &checkpoint);
state->offset = checkpoint.offset;
free(idx);
} else {
oidcpy(&idx->oid, result_oid);
ALLOC_GROW(state->written,
state->nr_written + 1,
state->alloc_written);
state->written[state->nr_written++] = idx;
}
return 0;
}
void prepare_loose_object_bulk_checkin(struct odb_transaction *transaction)
{
/*
* We lazily create the temporary object directory
* the first time an object might be added, since
* callers may not know whether any objects will be
* added at the time they call begin_odb_transaction.
*/
if (!transaction || transaction->objdir)
return;
transaction->objdir = tmp_objdir_create(transaction->odb->repo, "bulk-fsync");
if (transaction->objdir)
tmp_objdir_replace_primary_odb(transaction->objdir, 0);
}
void fsync_loose_object_bulk_checkin(struct odb_transaction *transaction,
int fd, const char *filename)
{
/*
* If we have an active ODB transaction, we issue a call that
* cleans the filesystem page cache but avoids a hardware flush
* command. Later on we will issue a single hardware flush
* before renaming the objects to their final names as part of
* flush_batch_fsync.
*/
if (!transaction || !transaction->objdir ||
git_fsync(fd, FSYNC_WRITEOUT_ONLY) < 0) {
if (errno == ENOSYS)
warning(_("core.fsyncMethod = batch is unsupported on this platform"));
fsync_or_die(fd, filename);
}
}
struct odb_transaction *begin_odb_transaction(struct object_database *odb)
{
if (!odb->transaction) {
CALLOC_ARRAY(odb->transaction, 1);
odb->transaction->odb = odb;
}
odb->transaction->nesting += 1;
return odb->transaction;
}
void flush_odb_transaction(struct odb_transaction *transaction)
{
if (!transaction)
return;
flush_batch_fsync(transaction);
flush_bulk_checkin_packfile(transaction);
}
void end_odb_transaction(struct odb_transaction *transaction)
{
if (!transaction || transaction->nesting == 0)
BUG("Unbalanced ODB transaction nesting");
transaction->nesting -= 1;
if (transaction->nesting)
return;
flush_odb_transaction(transaction);
transaction->odb->transaction = NULL;
free(transaction);
}

View File

@@ -1851,18 +1851,16 @@ struct refs_cb_data {
struct progress *progress;
};
static int add_ref_to_set(const char *refname UNUSED,
const char *referent UNUSED,
const struct object_id *oid,
int flags UNUSED, void *cb_data)
static int add_ref_to_set(const struct reference *ref, void *cb_data)
{
const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
struct refs_cb_data *data = (struct refs_cb_data *)cb_data;
if (!peel_iterated_oid(data->repo, oid, &peeled))
oid = &peeled;
if (odb_read_object_info(data->repo->objects, oid, NULL) == OBJ_COMMIT)
oidset_insert(data->commits, oid);
if (!reference_get_peeled_oid(data->repo, ref, &peeled))
maybe_peeled = &peeled;
if (odb_read_object_info(data->repo->objects, maybe_peeled, NULL) == OBJ_COMMIT)
oidset_insert(data->commits, maybe_peeled);
display_progress(data->progress, oidset_size(data->commits));

View File

@@ -1,26 +1,26 @@
#define USE_THE_REPOSITORY_VARIABLE
#define DISABLE_SIGN_COMPARE_WARNINGS
#include "../git-compat-util.h"
#include "win32.h"
#include <aclapi.h>
#include <sddl.h>
#include <conio.h>
#include <wchar.h>
#include "../strbuf.h"
#include "../run-command.h"
#include "../abspath.h"
#include "../alloc.h"
#include "win32/lazyload.h"
#include "../config.h"
#include "../environment.h"
#include "../trace2.h"
#include "../symlinks.h"
#include "../wrapper.h"
#include "git-compat-util.h"
#include "abspath.h"
#include "alloc.h"
#include "config.h"
#include "dir.h"
#include "environment.h"
#include "gettext.h"
#include "run-command.h"
#include "strbuf.h"
#include "symlinks.h"
#include "trace2.h"
#include "win32.h"
#include "win32/lazyload.h"
#include "wrapper.h"
#include <aclapi.h>
#include <conio.h>
#include <sddl.h>
#define SECURITY_WIN32
#include <sspi.h>
#include <wchar.h>
#include <winternl.h>
#define STATUS_DELETE_PENDING ((NTSTATUS) 0xC0000056)

View File

@@ -390,8 +390,7 @@ static void add_ref_to_island(kh_str_t *remote_islands, const char *island_name,
rl->hash += sha_core;
}
static int find_island_for_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
int flags UNUSED, void *cb)
static int find_island_for_ref(const struct reference *ref, void *cb)
{
struct island_load_data *ild = cb;
@@ -406,7 +405,7 @@ static int find_island_for_ref(const char *refname, const char *referent UNUSED,
/* walk backwards to get last-one-wins ordering */
for (i = ild->nr - 1; i >= 0; i--) {
if (!regexec(&ild->rx[i], refname,
if (!regexec(&ild->rx[i], ref->name,
ARRAY_SIZE(matches), matches, 0))
break;
}
@@ -428,10 +427,10 @@ static int find_island_for_ref(const char *refname, const char *referent UNUSED,
if (island_name.len)
strbuf_addch(&island_name, '-');
strbuf_add(&island_name, refname + match->rm_so, match->rm_eo - match->rm_so);
strbuf_add(&island_name, ref->name + match->rm_so, match->rm_eo - match->rm_so);
}
add_ref_to_island(ild->remote_islands, island_name.buf, oid);
add_ref_to_island(ild->remote_islands, island_name.buf, ref->oid);
strbuf_release(&island_name);
return 0;
}

View File

@@ -21,30 +21,21 @@
static int read_directory_contents(const char *path, struct string_list *list,
const struct pathspec *pathspec,
int skip)
struct strbuf *match)
{
struct strbuf match = STRBUF_INIT;
int len;
int len = match->len;
DIR *dir;
struct dirent *e;
if (!(dir = opendir(path)))
return error("Could not open directory %s", path);
if (pathspec) {
strbuf_addstr(&match, path);
strbuf_complete(&match, '/');
strbuf_remove(&match, 0, skip);
len = match.len;
}
while ((e = readdir_skip_dot_and_dotdot(dir))) {
if (pathspec) {
int is_dir = 0;
strbuf_setlen(&match, len);
strbuf_addstr(&match, e->d_name);
strbuf_setlen(match, len);
strbuf_addstr(match, e->d_name);
if (NOT_CONSTANT(DTYPE(e)) != DT_UNKNOWN) {
is_dir = (DTYPE(e) == DT_DIR);
} else {
@@ -57,7 +48,7 @@ static int read_directory_contents(const char *path, struct string_list *list,
}
if (!match_leading_pathspec(NULL, pathspec,
match.buf, match.len,
match->buf, match->len,
0, NULL, is_dir))
continue;
}
@@ -65,7 +56,7 @@ static int read_directory_contents(const char *path, struct string_list *list,
string_list_insert(list, e->d_name);
}
strbuf_release(&match);
strbuf_setlen(match, len);
closedir(dir);
return 0;
}
@@ -169,7 +160,8 @@ static struct diff_filespec *noindex_filespec(const struct git_hash_algo *algop,
static int queue_diff(struct diff_options *o, const struct git_hash_algo *algop,
const char *name1, const char *name2, int recursing,
const struct pathspec *ps, int skip1, int skip2)
const struct pathspec *ps,
struct strbuf *ps_match1, struct strbuf *ps_match2)
{
int mode1 = 0, mode2 = 0;
enum special special1 = SPECIAL_NONE, special2 = SPECIAL_NONE;
@@ -208,10 +200,12 @@ static int queue_diff(struct diff_options *o, const struct git_hash_algo *algop,
struct string_list p2 = STRING_LIST_INIT_DUP;
int i1, i2, ret = 0;
size_t len1 = 0, len2 = 0;
size_t match1_len = ps_match1->len;
size_t match2_len = ps_match2->len;
if (name1 && read_directory_contents(name1, &p1, ps, skip1))
if (name1 && read_directory_contents(name1, &p1, ps, ps_match1))
return -1;
if (name2 && read_directory_contents(name2, &p2, ps, skip2)) {
if (name2 && read_directory_contents(name2, &p2, ps, ps_match2)) {
string_list_clear(&p1, 0);
return -1;
}
@@ -235,6 +229,11 @@ static int queue_diff(struct diff_options *o, const struct git_hash_algo *algop,
strbuf_setlen(&buffer1, len1);
strbuf_setlen(&buffer2, len2);
if (ps) {
strbuf_setlen(ps_match1, match1_len);
strbuf_setlen(ps_match2, match2_len);
}
if (i1 == p1.nr)
comp = 1;
else if (i2 == p2.nr)
@@ -245,18 +244,28 @@ static int queue_diff(struct diff_options *o, const struct git_hash_algo *algop,
if (comp > 0)
n1 = NULL;
else {
strbuf_addstr(&buffer1, p1.items[i1++].string);
strbuf_addstr(&buffer1, p1.items[i1].string);
if (ps) {
strbuf_addstr(ps_match1, p1.items[i1].string);
strbuf_complete(ps_match1, '/');
}
n1 = buffer1.buf;
i1++;
}
if (comp < 0)
n2 = NULL;
else {
strbuf_addstr(&buffer2, p2.items[i2++].string);
strbuf_addstr(&buffer2, p2.items[i2].string);
if (ps) {
strbuf_addstr(ps_match2, p2.items[i2].string);
strbuf_complete(ps_match2, '/');
}
n2 = buffer2.buf;
i2++;
}
ret = queue_diff(o, algop, n1, n2, 1, ps, skip1, skip2);
ret = queue_diff(o, algop, n1, n2, 1, ps, ps_match1, ps_match2);
}
string_list_clear(&p1, 0);
string_list_clear(&p2, 0);
@@ -346,7 +355,8 @@ int diff_no_index(struct rev_info *revs, const struct git_hash_algo *algop,
int implicit_no_index, int argc, const char **argv)
{
struct pathspec pathspec, *ps = NULL;
int i, no_index, skip1 = 0, skip2 = 0;
struct strbuf ps_match1 = STRBUF_INIT, ps_match2 = STRBUF_INIT;
int i, no_index;
int ret = 1;
const char *paths[2];
char *to_free[ARRAY_SIZE(paths)] = { 0 };
@@ -387,11 +397,6 @@ int diff_no_index(struct rev_info *revs, const struct git_hash_algo *algop,
NULL, &argv[2]);
if (pathspec.nr)
ps = &pathspec;
skip1 = strlen(paths[0]);
skip1 += paths[0][skip1] == '/' ? 0 : 1;
skip2 = strlen(paths[1]);
skip2 += paths[1][skip2] == '/' ? 0 : 1;
} else if (argc > 2) {
warning(_("Limiting comparison with pathspecs is only "
"supported if both paths are directories."));
@@ -415,7 +420,7 @@ int diff_no_index(struct rev_info *revs, const struct git_hash_algo *algop,
revs->diffopt.flags.exit_with_status = 1;
if (queue_diff(&revs->diffopt, algop, paths[0], paths[1], 0, ps,
skip1, skip2))
&ps_match1, &ps_match2))
goto out;
diff_set_mnemonic_prefix(&revs->diffopt, "1/", "2/");
diffcore_std(&revs->diffopt);
@@ -431,6 +436,8 @@ out:
for (i = 0; i < ARRAY_SIZE(to_free); i++)
free(to_free[i]);
strbuf_release(&replacement);
strbuf_release(&ps_match1);
strbuf_release(&ps_match2);
if (ps)
clear_pathspec(ps);
return ret;

View File

@@ -188,13 +188,9 @@ static int rev_list_insert_ref(struct fetch_negotiator *negotiator,
return 0;
}
static int rev_list_insert_ref_oid(const char *refname UNUSED,
const char *referent UNUSED,
const struct object_id *oid,
int flag UNUSED,
void *cb_data)
static int rev_list_insert_ref_oid(const struct reference *ref, void *cb_data)
{
return rev_list_insert_ref(cb_data, oid);
return rev_list_insert_ref(cb_data, ref->oid);
}
enum ack_type {
@@ -616,13 +612,9 @@ static int mark_complete(const struct object_id *oid)
return 0;
}
static int mark_complete_oid(const char *refname UNUSED,
const char *referent UNUSED,
const struct object_id *oid,
int flag UNUSED,
void *cb_data UNUSED)
static int mark_complete_oid(const struct reference *ref, void *cb_data UNUSED)
{
return mark_complete(oid);
return mark_complete(ref->oid);
}
static void mark_recent_complete_commits(struct fetch_pack_args *args,

10
help.c
View File

@@ -851,18 +851,16 @@ struct similar_ref_cb {
struct string_list *similar_refs;
};
static int append_similar_ref(const char *refname, const char *referent UNUSED,
const struct object_id *oid UNUSED,
int flags UNUSED, void *cb_data)
static int append_similar_ref(const struct reference *ref, void *cb_data)
{
struct similar_ref_cb *cb = (struct similar_ref_cb *)(cb_data);
char *branch = strrchr(refname, '/') + 1;
char *branch = strrchr(ref->name, '/') + 1;
/* A remote branch of the same name is deemed similar */
if (starts_with(refname, "refs/remotes/") &&
if (starts_with(ref->name, "refs/remotes/") &&
!strcmp(branch, cb->base_ref))
string_list_append_nodup(cb->similar_refs,
refs_shorten_unambiguous_ref(get_main_ref_store(the_repository), refname, 1));
refs_shorten_unambiguous_ref(get_main_ref_store(the_repository), ref->name, 1));
return 0;
}

View File

@@ -513,18 +513,17 @@ static void run_service(const char **argv, int buffer_input)
exit(1);
}
static int show_text_ref(const char *name, const char *referent UNUSED, const struct object_id *oid,
int flag UNUSED, void *cb_data)
static int show_text_ref(const struct reference *ref, void *cb_data)
{
const char *name_nons = strip_namespace(name);
const char *name_nons = strip_namespace(ref->name);
struct strbuf *buf = cb_data;
struct object *o = parse_object(the_repository, oid);
struct object *o = parse_object(the_repository, ref->oid);
if (!o)
return 0;
strbuf_addf(buf, "%s\t%s\n", oid_to_hex(oid), name_nons);
strbuf_addf(buf, "%s\t%s\n", oid_to_hex(ref->oid), name_nons);
if (o->type == OBJ_TAG) {
o = deref_tag(the_repository, o, name, 0);
o = deref_tag(the_repository, o, ref->name, 0);
if (!o)
return 0;
strbuf_addf(buf, "%s\t%s^{}\n", oid_to_hex(&o->oid),
@@ -569,21 +568,20 @@ static void get_info_refs(struct strbuf *hdr, char *arg UNUSED)
strbuf_release(&buf);
}
static int show_head_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
int flag, void *cb_data)
static int show_head_ref(const struct reference *ref, void *cb_data)
{
struct strbuf *buf = cb_data;
if (flag & REF_ISSYMREF) {
if (ref->flags & REF_ISSYMREF) {
const char *target = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
refname,
ref->name,
RESOLVE_REF_READING,
NULL, NULL);
if (target)
strbuf_addf(buf, "ref: %s\n", strip_namespace(target));
} else {
strbuf_addf(buf, "%s\n", oid_to_hex(oid));
strbuf_addf(buf, "%s\n", oid_to_hex(ref->oid));
}
return 0;

View File

@@ -147,9 +147,7 @@ static int ref_filter_match(const char *refname,
return 1;
}
static int add_ref_decoration(const char *refname, const char *referent UNUSED, const struct object_id *oid,
int flags UNUSED,
void *cb_data)
static int add_ref_decoration(const struct reference *ref, void *cb_data)
{
int i;
struct object *obj;
@@ -158,16 +156,16 @@ static int add_ref_decoration(const char *refname, const char *referent UNUSED,
struct decoration_filter *filter = (struct decoration_filter *)cb_data;
const char *git_replace_ref_base = ref_namespace[NAMESPACE_REPLACE].ref;
if (filter && !ref_filter_match(refname, filter))
if (filter && !ref_filter_match(ref->name, filter))
return 0;
if (starts_with(refname, git_replace_ref_base)) {
if (starts_with(ref->name, git_replace_ref_base)) {
struct object_id original_oid;
if (!replace_refs_enabled(the_repository))
return 0;
if (get_oid_hex(refname + strlen(git_replace_ref_base),
if (get_oid_hex(ref->name + strlen(git_replace_ref_base),
&original_oid)) {
warning("invalid replace ref %s", refname);
warning("invalid replace ref %s", ref->name);
return 0;
}
obj = parse_object(the_repository, &original_oid);
@@ -176,10 +174,10 @@ static int add_ref_decoration(const char *refname, const char *referent UNUSED,
return 0;
}
objtype = odb_read_object_info(the_repository->objects, oid, NULL);
objtype = odb_read_object_info(the_repository->objects, ref->oid, NULL);
if (objtype < 0)
return 0;
obj = lookup_object_by_type(the_repository, oid, objtype);
obj = lookup_object_by_type(the_repository, ref->oid, objtype);
for (i = 0; i < ARRAY_SIZE(ref_namespace); i++) {
struct ref_namespace_info *info = &ref_namespace[i];
@@ -187,24 +185,24 @@ static int add_ref_decoration(const char *refname, const char *referent UNUSED,
if (!info->decoration)
continue;
if (info->exact) {
if (!strcmp(refname, info->ref)) {
if (!strcmp(ref->name, info->ref)) {
deco_type = info->decoration;
break;
}
} else if (starts_with(refname, info->ref)) {
} else if (starts_with(ref->name, info->ref)) {
deco_type = info->decoration;
break;
}
}
add_name_decoration(deco_type, refname, obj);
add_name_decoration(deco_type, ref->name, obj);
while (obj->type == OBJ_TAG) {
if (!obj->parsed)
parse_object(the_repository, &obj->oid);
obj = ((struct tag *)obj)->tagged;
if (!obj)
break;
add_name_decoration(DECORATION_REF_TAG, refname, obj);
add_name_decoration(DECORATION_REF_TAG, ref->name, obj);
}
return 0;
}

View File

@@ -75,42 +75,42 @@ struct ls_refs_data {
unsigned unborn : 1;
};
static int send_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
int flag, void *cb_data)
static int send_ref(const struct reference *ref, void *cb_data)
{
struct ls_refs_data *data = cb_data;
const char *refname_nons = strip_namespace(refname);
const char *refname_nons = strip_namespace(ref->name);
strbuf_reset(&data->buf);
if (ref_is_hidden(refname_nons, refname, &data->hidden_refs))
if (ref_is_hidden(refname_nons, ref->name, &data->hidden_refs))
return 0;
if (!ref_match(&data->prefixes, refname_nons))
return 0;
if (oid)
strbuf_addf(&data->buf, "%s %s", oid_to_hex(oid), refname_nons);
if (ref->oid)
strbuf_addf(&data->buf, "%s %s", oid_to_hex(ref->oid), refname_nons);
else
strbuf_addf(&data->buf, "unborn %s", refname_nons);
if (data->symrefs && flag & REF_ISSYMREF) {
if (data->symrefs && ref->flags & REF_ISSYMREF) {
int unused_flag;
struct object_id unused;
const char *symref_target = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
refname,
ref->name,
0,
&unused,
&flag);
&unused_flag);
if (!symref_target)
die("'%s' is a symref but it is not?", refname);
die("'%s' is a symref but it is not?", ref->name);
strbuf_addf(&data->buf, " symref-target:%s",
strip_namespace(symref_target));
}
if (data->peel && oid) {
if (data->peel && ref->oid) {
struct object_id peeled;
if (!peel_iterated_oid(the_repository, oid, &peeled))
if (!reference_get_peeled_oid(the_repository, ref, &peeled))
strbuf_addf(&data->buf, " peeled:%s", oid_to_hex(&peeled));
}
@@ -131,9 +131,17 @@ static void send_possibly_unborn_head(struct ls_refs_data *data)
if (!refs_resolve_ref_unsafe(get_main_ref_store(the_repository), namespaced.buf, 0, &oid, &flag))
return; /* bad ref */
oid_is_null = is_null_oid(&oid);
if (!oid_is_null ||
(data->unborn && data->symrefs && (flag & REF_ISSYMREF)))
send_ref(namespaced.buf, NULL, oid_is_null ? NULL : &oid, flag, data);
(data->unborn && data->symrefs && (flag & REF_ISSYMREF))) {
struct reference ref = {
.name = namespaced.buf,
.oid = oid_is_null ? NULL : &oid,
.flags = flag,
};
send_ref(&ref, data);
}
strbuf_release(&namespaced);
}

View File

@@ -463,6 +463,12 @@ libgit_sources = [
'reftable/tree.c',
'reftable/writer.c',
'remote.c',
'repack.c',
'repack-cruft.c',
'repack-filtered.c',
'repack-geometry.c',
'repack-midx.c',
'repack-promisor.c',
'replace-object.c',
'repo-settings.c',
'repository.c',

View File

@@ -697,28 +697,27 @@ static void prepare_midx_packing_data(struct packing_data *pdata,
trace2_region_leave("midx", "prepare_midx_packing_data", ctx->repo);
}
static int add_ref_to_pending(const char *refname, const char *referent UNUSED,
const struct object_id *oid,
int flag, void *cb_data)
static int add_ref_to_pending(const struct reference *ref, void *cb_data)
{
struct rev_info *revs = (struct rev_info*)cb_data;
const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
struct object *object;
if ((flag & REF_ISSYMREF) && (flag & REF_ISBROKEN)) {
warning("symbolic ref is dangling: %s", refname);
if ((ref->flags & REF_ISSYMREF) && (ref->flags & REF_ISBROKEN)) {
warning("symbolic ref is dangling: %s", ref->name);
return 0;
}
if (!peel_iterated_oid(revs->repo, oid, &peeled))
oid = &peeled;
if (!reference_get_peeled_oid(revs->repo, ref, &peeled))
maybe_peeled = &peeled;
object = parse_object_or_die(revs->repo, oid, refname);
object = parse_object_or_die(revs->repo, maybe_peeled, ref->name);
if (object->type != OBJ_COMMIT)
return 0;
add_pending_object(revs, object, "");
if (bitmap_is_preferred_refname(revs->repo, refname))
if (bitmap_is_preferred_refname(revs->repo, ref->name))
object->flags |= NEEDS_BITMAP;
return 0;
}

View File

@@ -38,11 +38,10 @@ static void rev_list_push(struct negotiation_state *ns,
}
}
static int clear_marks(const char *refname, const char *referent UNUSED, const struct object_id *oid,
int flag UNUSED,
void *cb_data UNUSED)
static int clear_marks(const struct reference *ref, void *cb_data UNUSED)
{
struct object *o = deref_tag(the_repository, parse_object(the_repository, oid), refname, 0);
struct object *o = deref_tag(the_repository, parse_object(the_repository, ref->oid),
ref->name, 0);
if (o && o->type == OBJ_COMMIT)
clear_commit_marks((struct commit *)o,

View File

@@ -75,11 +75,10 @@ static struct entry *rev_list_push(struct data *data, struct commit *commit, int
return entry;
}
static int clear_marks(const char *refname, const char *referent UNUSED, const struct object_id *oid,
int flag UNUSED,
void *cb_data UNUSED)
static int clear_marks(const struct reference *ref, void *cb_data UNUSED)
{
struct object *o = deref_tag(the_repository, parse_object(the_repository, oid), refname, 0);
struct object *o = deref_tag(the_repository, parse_object(the_repository, ref->oid),
ref->name, 0);
if (o && o->type == OBJ_COMMIT)
clear_commit_marks((struct commit *)o,

View File

@@ -938,13 +938,11 @@ out:
return ret;
}
static int string_list_add_one_ref(const char *refname, const char *referent UNUSED,
const struct object_id *oid UNUSED,
int flag UNUSED, void *cb)
static int string_list_add_one_ref(const struct reference *ref, void *cb)
{
struct string_list *refs = cb;
if (!unsorted_string_list_has_string(refs, refname))
string_list_append(refs, refname);
if (!unsorted_string_list_has_string(refs, ref->name))
string_list_append(refs, ref->name);
return 0;
}

View File

@@ -1444,18 +1444,16 @@ struct handle_one_ref_cb {
struct commit_list **list;
};
static int handle_one_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,
int flag UNUSED,
void *cb_data)
static int handle_one_ref(const struct reference *ref, void *cb_data)
{
struct handle_one_ref_cb *cb = cb_data;
struct commit_list **list = cb->list;
struct object *object = parse_object(cb->repo, oid);
struct object *object = parse_object(cb->repo, ref->oid);
if (!object)
return 0;
if (object->type == OBJ_TAG) {
object = deref_tag(cb->repo, object, path,
strlen(path));
object = deref_tag(cb->repo, object, ref->name,
strlen(ref->name));
if (!object)
return 0;
}

View File

@@ -209,11 +209,12 @@ struct object *lookup_object_by_type(struct repository *r,
enum peel_status peel_object(struct repository *r,
const struct object_id *name,
struct object_id *oid)
struct object_id *oid,
unsigned flags)
{
struct object *o = lookup_unknown_object(r, name);
if (o->type == OBJ_NONE) {
if (o->type == OBJ_NONE || flags & PEEL_OBJECT_VERIFY_OBJECT_TYPE) {
int type = odb_read_object_info(r->objects, name, NULL);
if (type < 0 || !object_as_type(o, type, 0))
return PEEL_INVALID;
@@ -222,7 +223,20 @@ enum peel_status peel_object(struct repository *r,
if (o->type != OBJ_TAG)
return PEEL_NON_TAG;
o = deref_tag_noverify(r, o);
while (o && o->type == OBJ_TAG) {
o = parse_object(r, &o->oid);
if (o && o->type == OBJ_TAG && ((struct tag *)o)->tagged) {
o = ((struct tag *)o)->tagged;
if (flags & PEEL_OBJECT_VERIFY_OBJECT_TYPE) {
int type = odb_read_object_info(r->objects, &o->oid, NULL);
if (type < 0 || !object_as_type(o, type, 0))
return PEEL_INVALID;
}
} else {
o = NULL;
}
}
if (!o)
return PEEL_INVALID;

View File

@@ -287,6 +287,17 @@ enum peel_status {
PEEL_BROKEN = -4
};
enum peel_object_flags {
/*
* Always verify the object type, even in the case where the looked-up
* object already has an object type. This can be useful when the
* stored object type may be invalid. One such case is when looking up
* objects via tags, where we blindly trust the object type declared by
* the tag.
*/
PEEL_OBJECT_VERIFY_OBJECT_TYPE = (1 << 0),
};
/*
* Peel the named object; i.e., if the object is a tag, resolve the
* tag recursively until a non-tag is found. If successful, store the
@@ -295,7 +306,9 @@ enum peel_status {
* and leave oid unchanged.
*/
enum peel_status peel_object(struct repository *r,
const struct object_id *name, struct object_id *oid);
const struct object_id *name,
struct object_id *oid,
unsigned flags);
struct object_list *object_list_insert(struct object *item,
struct object_list **list_p);

View File

@@ -221,28 +221,25 @@ void load_pseudo_merges_from_config(struct repository *r,
}
}
static int find_pseudo_merge_group_for_ref(const char *refname,
const char *referent UNUSED,
const struct object_id *oid,
int flags UNUSED,
void *_data)
static int find_pseudo_merge_group_for_ref(const struct reference *ref, void *_data)
{
struct bitmap_writer *writer = _data;
const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
struct commit *c;
uint32_t i;
int has_bitmap;
if (!peel_iterated_oid(the_repository, oid, &peeled))
oid = &peeled;
if (!reference_get_peeled_oid(the_repository, ref, &peeled))
maybe_peeled = &peeled;
c = lookup_commit(the_repository, oid);
c = lookup_commit(the_repository, maybe_peeled);
if (!c)
return 0;
if (!packlist_find(writer->to_pack, oid))
if (!packlist_find(writer->to_pack, maybe_peeled))
return 0;
has_bitmap = bitmap_writer_has_bitmapped_object_id(writer, oid);
has_bitmap = bitmap_writer_has_bitmapped_object_id(writer, maybe_peeled);
for (i = 0; i < writer->pseudo_merge_groups.nr; i++) {
struct pseudo_merge_group *group;
@@ -252,7 +249,7 @@ static int find_pseudo_merge_group_for_ref(const char *refname,
size_t j;
group = writer->pseudo_merge_groups.items[i].util;
if (regexec(group->pattern, refname, ARRAY_SIZE(captures),
if (regexec(group->pattern, ref->name, ARRAY_SIZE(captures),
captures, 0))
continue;
@@ -269,7 +266,7 @@ static int find_pseudo_merge_group_for_ref(const char *refname,
if (group_name.len)
strbuf_addch(&group_name, '-');
strbuf_add(&group_name, refname + match->rm_so,
strbuf_add(&group_name, ref->name + match->rm_so,
match->rm_eo - match->rm_so);
}

View File

@@ -83,18 +83,17 @@ static void add_rebase_files(struct rev_info *revs)
free_worktrees(worktrees);
}
static int add_one_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,
int flag, void *cb_data)
static int add_one_ref(const struct reference *ref, void *cb_data)
{
struct rev_info *revs = (struct rev_info *)cb_data;
struct object *object;
if ((flag & REF_ISSYMREF) && (flag & REF_ISBROKEN)) {
warning("symbolic ref is dangling: %s", path);
if ((ref->flags & REF_ISSYMREF) && (ref->flags & REF_ISBROKEN)) {
warning("symbolic ref is dangling: %s", ref->name);
return 0;
}
object = parse_object_or_die(the_repository, oid, path);
object = parse_object_or_die(the_repository, ref->oid, ref->name);
add_pending_object(revs, object, "");
return 0;

View File

@@ -91,6 +91,7 @@ static struct expand_data {
struct object_id delta_base_oid;
void *content;
struct object *maybe_object;
struct object_info info;
} oi, oi_deref;
@@ -1475,11 +1476,29 @@ static void grab_common_values(struct atom_value *val, int deref, struct expand_
}
}
/* See grab_values */
static void grab_tag_values(struct atom_value *val, int deref, struct object *obj)
static struct object *get_or_parse_object(struct expand_data *data, const char *refname,
struct strbuf *err, int *eaten)
{
if (!data->maybe_object) {
data->maybe_object = parse_object_buffer(the_repository, &data->oid, data->type,
data->size, data->content, eaten);
if (!data->maybe_object) {
strbuf_addf(err, _("parse_object_buffer failed on %s for %s"),
oid_to_hex(&data->oid), refname);
return NULL;
}
}
return data->maybe_object;
}
/* See grab_values */
static int grab_tag_values(struct atom_value *val, int deref,
struct expand_data *data, const char *refname,
struct strbuf *err, int *eaten)
{
struct tag *tag = NULL;
int i;
struct tag *tag = (struct tag *) obj;
for (i = 0; i < used_atom_cnt; i++) {
const char *name = used_atom[i].name;
@@ -1487,6 +1506,14 @@ static void grab_tag_values(struct atom_value *val, int deref, struct object *ob
struct atom_value *v = &val[i];
if (!!deref != (*name == '*'))
continue;
if (!tag) {
tag = (struct tag *) get_or_parse_object(data, refname,
err, eaten);
if (!tag)
return -1;
}
if (deref)
name++;
if (atom_type == ATOM_TAG)
@@ -1496,22 +1523,35 @@ static void grab_tag_values(struct atom_value *val, int deref, struct object *ob
else if (atom_type == ATOM_OBJECT && tag->tagged)
v->s = xstrdup(oid_to_hex(&tag->tagged->oid));
}
return 0;
}
/* See grab_values */
static void grab_commit_values(struct atom_value *val, int deref, struct object *obj)
static int grab_commit_values(struct atom_value *val, int deref,
struct expand_data *data, const char *refname,
struct strbuf *err, int *eaten)
{
int i;
struct commit *commit = (struct commit *) obj;
struct commit *commit = NULL;
for (i = 0; i < used_atom_cnt; i++) {
const char *name = used_atom[i].name;
enum atom_type atom_type = used_atom[i].atom_type;
struct atom_value *v = &val[i];
if (!!deref != (*name == '*'))
continue;
if (deref)
name++;
if (!commit) {
commit = (struct commit *) get_or_parse_object(data, refname,
err, eaten);
if (!commit)
return -1;
}
if (atom_type == ATOM_TREE &&
grab_oid(name, "tree", get_commit_tree_oid(commit), v, &used_atom[i]))
continue;
@@ -1531,6 +1571,8 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object
v->s = strbuf_detach(&s, NULL);
}
}
return 0;
}
static const char *find_wholine(const char *who, int wholen, const char *buf)
@@ -1759,10 +1801,12 @@ static void grab_person(const char *who, struct atom_value *val, int deref, void
}
}
static void grab_signature(struct atom_value *val, int deref, struct object *obj)
static int grab_signature(struct atom_value *val, int deref,
struct expand_data *data, const char *refname,
struct strbuf *err, int *eaten)
{
int i;
struct commit *commit = (struct commit *) obj;
struct commit *commit = NULL;
struct signature_check sigc = { 0 };
int signature_checked = 0;
@@ -1790,6 +1834,13 @@ static void grab_signature(struct atom_value *val, int deref, struct object *obj
continue;
if (!signature_checked) {
if (!commit) {
commit = (struct commit *) get_or_parse_object(data, refname,
err, eaten);
if (!commit)
return -1;
}
check_commit_signature(commit, &sigc);
signature_checked = 1;
}
@@ -1843,6 +1894,8 @@ static void grab_signature(struct atom_value *val, int deref, struct object *obj
if (signature_checked)
signature_check_clear(&sigc);
return 0;
}
static void find_subpos(const char *buf,
@@ -1920,9 +1973,8 @@ static void append_lines(struct strbuf *out, const char *buf, unsigned long size
}
static void grab_describe_values(struct atom_value *val, int deref,
struct object *obj)
struct expand_data *data)
{
struct commit *commit = (struct commit *)obj;
int i;
for (i = 0; i < used_atom_cnt; i++) {
@@ -1944,7 +1996,7 @@ static void grab_describe_values(struct atom_value *val, int deref,
cmd.git_cmd = 1;
strvec_push(&cmd.args, "describe");
strvec_pushv(&cmd.args, atom->u.describe_args.v);
strvec_push(&cmd.args, oid_to_hex(&commit->object.oid));
strvec_push(&cmd.args, oid_to_hex(&data->oid));
if (pipe_command(&cmd, NULL, 0, &out, 0, &err, 0) < 0) {
error(_("failed to run 'describe'"));
v->s = xstrdup("");
@@ -2066,24 +2118,36 @@ static void fill_missing_values(struct atom_value *val)
* pointed at by the ref itself; otherwise it is the object the
* ref (which is a tag) refers to.
*/
static void grab_values(struct atom_value *val, int deref, struct object *obj, struct expand_data *data)
static int grab_values(struct atom_value *val, int deref, struct expand_data *data,
const char *refname, struct strbuf *err, int *eaten)
{
void *buf = data->content;
int ret;
switch (obj->type) {
switch (data->type) {
case OBJ_TAG:
grab_tag_values(val, deref, obj);
ret = grab_tag_values(val, deref, data, refname, err, eaten);
if (ret < 0)
goto out;
grab_sub_body_contents(val, deref, data);
grab_person("tagger", val, deref, buf);
grab_describe_values(val, deref, obj);
grab_describe_values(val, deref, data);
break;
case OBJ_COMMIT:
grab_commit_values(val, deref, obj);
ret = grab_commit_values(val, deref, data, refname, err, eaten);
if (ret < 0)
goto out;
grab_sub_body_contents(val, deref, data);
grab_person("author", val, deref, buf);
grab_person("committer", val, deref, buf);
grab_signature(val, deref, obj);
grab_describe_values(val, deref, obj);
ret = grab_signature(val, deref, data, refname, err, eaten);
if (ret < 0)
goto out;
grab_describe_values(val, deref, data);
break;
case OBJ_TREE:
/* grab_tree_values(val, deref, obj, buf, sz); */
@@ -2094,8 +2158,12 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, s
grab_sub_body_contents(val, deref, data);
break;
default:
die("Eh? Object of type %d?", obj->type);
die("Eh? Object of type %d?", data->type);
}
ret = 0;
out:
return ret;
}
static inline char *copy_advance(char *dst, const char *src)
@@ -2292,38 +2360,43 @@ static const char *get_refname(struct used_atom *atom, struct ref_array_item *re
return show_ref(&atom->u.refname, ref->refname);
}
static int get_object(struct ref_array_item *ref, int deref, struct object **obj,
static int get_object(struct ref_array_item *ref, int deref,
struct expand_data *oi, struct strbuf *err)
{
/* parse_object_buffer() will set eaten to 0 if free() will be needed */
int eaten = 1;
/* parse_object_buffer() will set eaten to 1 if free() will be needed */
int eaten = 0;
int ret;
oi->maybe_object = NULL;
if (oi->info.contentp) {
/* We need to know that to use parse_object_buffer properly */
oi->info.sizep = &oi->size;
oi->info.typep = &oi->type;
}
if (odb_read_object_info_extended(the_repository->objects, &oi->oid, &oi->info,
OBJECT_INFO_LOOKUP_REPLACE))
return strbuf_addf_ret(err, -1, _("missing object %s for %s"),
oid_to_hex(&oi->oid), ref->refname);
OBJECT_INFO_LOOKUP_REPLACE)) {
ret = strbuf_addf_ret(err, -1, _("missing object %s for %s"),
oid_to_hex(&oi->oid), ref->refname);
goto out;
}
if (oi->info.disk_sizep && oi->disk_size < 0)
BUG("Object size is less than zero.");
if (oi->info.contentp) {
*obj = parse_object_buffer(the_repository, &oi->oid, oi->type, oi->size, oi->content, &eaten);
if (!*obj) {
if (!eaten)
free(oi->content);
return strbuf_addf_ret(err, -1, _("parse_object_buffer failed on %s for %s"),
oid_to_hex(&oi->oid), ref->refname);
}
grab_values(ref->value, deref, *obj, oi);
ret = grab_values(ref->value, deref, oi, ref->refname, err, &eaten);
if (ret < 0)
goto out;
}
grab_common_values(ref->value, deref, oi);
ret = 0;
out:
if (!eaten)
free(oi->content);
return 0;
return ret;
}
static void populate_worktree_map(struct hashmap *map, struct worktree **worktrees)
@@ -2376,7 +2449,6 @@ static char *get_worktree_path(const struct ref_array_item *ref)
*/
static int populate_value(struct ref_array_item *ref, struct strbuf *err)
{
struct object *obj;
int i;
struct object_info empty = OBJECT_INFO_INIT;
int ahead_behind_atoms = 0;
@@ -2564,24 +2636,32 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
oi.oid = ref->objectname;
if (get_object(ref, 0, &obj, &oi, err))
if (get_object(ref, 0, &oi, err))
return -1;
/*
* If there is no atom that wants to know about tagged
* object, we are done.
*/
if (!need_tagged || (obj->type != OBJ_TAG))
if (!need_tagged || (oi.type != OBJ_TAG))
return 0;
/*
* If it is a tag object, see if we use the peeled value. If we do,
* grab the peeled OID.
*/
if (need_tagged && peel_iterated_oid(the_repository, &obj->oid, &oi_deref.oid))
die("bad tag");
if (need_tagged) {
if (!is_null_oid(&ref->peeled_oid)) {
oidcpy(&oi_deref.oid, &ref->peeled_oid);
} else if (!peel_object(the_repository, &oi.oid, &oi_deref.oid,
PEEL_OBJECT_VERIFY_OBJECT_TYPE)) {
/* We managed to peel the object ourselves. */
} else {
die("bad tag");
}
}
return get_object(ref, 1, &obj, &oi_deref, err);
return get_object(ref, 1, &oi_deref, err);
}
/*
@@ -2664,7 +2744,7 @@ static int match_name_as_path(const char **pattern, const char *refname,
/* Return 1 if the refname matches one of the patterns, otherwise 0. */
static int filter_pattern_match(struct ref_filter *filter, const char *refname)
{
if (!*filter->name_patterns)
if (!filter->name_patterns || !*filter->name_patterns)
return 1; /* No pattern always matches */
if (filter->match_as_path)
return match_name_as_path(filter->name_patterns, refname,
@@ -2751,7 +2831,7 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
return for_each_fullref_with_seek(filter, cb, cb_data, 0);
}
if (!filter->name_patterns[0]) {
if (!filter->name_patterns || !filter->name_patterns[0]) {
/* no patterns; we have to look at everything */
return for_each_fullref_with_seek(filter, cb, cb_data, 0);
}
@@ -2807,12 +2887,15 @@ static int match_points_at(struct oid_array *points_at,
* Callers can then fill in other struct members at their leisure.
*/
static struct ref_array_item *new_ref_array_item(const char *refname,
const struct object_id *oid)
const struct object_id *oid,
const struct object_id *peeled_oid)
{
struct ref_array_item *ref;
FLEX_ALLOC_STR(ref, refname, refname);
oidcpy(&ref->objectname, oid);
if (peeled_oid)
oidcpy(&ref->peeled_oid, peeled_oid);
ref->rest = NULL;
return ref;
@@ -2826,14 +2909,15 @@ static void ref_array_append(struct ref_array *array, struct ref_array_item *ref
struct ref_array_item *ref_array_push(struct ref_array *array,
const char *refname,
const struct object_id *oid)
const struct object_id *oid,
const struct object_id *peeled_oid)
{
struct ref_array_item *ref = new_ref_array_item(refname, oid);
struct ref_array_item *ref = new_ref_array_item(refname, oid, peeled_oid);
ref_array_append(array, ref);
return ref;
}
static int ref_kind_from_refname(const char *refname)
int ref_kind_from_refname(const char *refname)
{
unsigned int i;
@@ -2871,25 +2955,25 @@ static int filter_ref_kind(struct ref_filter *filter, const char *refname)
return ref_kind_from_refname(refname);
}
static struct ref_array_item *apply_ref_filter(const char *refname, const char *referent, const struct object_id *oid,
int flag, struct ref_filter *filter)
static struct ref_array_item *apply_ref_filter(const struct reference *ref,
struct ref_filter *filter)
{
struct ref_array_item *ref;
struct ref_array_item *item;
struct commit *commit = NULL;
unsigned int kind;
if (flag & REF_BAD_NAME) {
warning(_("ignoring ref with broken name %s"), refname);
if (ref->flags & REF_BAD_NAME) {
warning(_("ignoring ref with broken name %s"), ref->name);
return NULL;
}
if (flag & REF_ISBROKEN) {
warning(_("ignoring broken ref %s"), refname);
if (ref->flags & REF_ISBROKEN) {
warning(_("ignoring broken ref %s"), ref->name);
return NULL;
}
/* Obtain the current ref kind from filter_ref_kind() and ignore unwanted refs. */
kind = filter_ref_kind(filter, refname);
kind = filter_ref_kind(filter, ref->name);
/*
* Generally HEAD refs are printed with special description denoting a rebase,
@@ -2902,13 +2986,13 @@ static struct ref_array_item *apply_ref_filter(const char *refname, const char *
else if (!(kind & filter->kind))
return NULL;
if (!filter_pattern_match(filter, refname))
if (!filter_pattern_match(filter, ref->name))
return NULL;
if (filter_exclude_match(filter, refname))
if (filter_exclude_match(filter, ref->name))
return NULL;
if (filter->points_at.nr && !match_points_at(&filter->points_at, oid, refname))
if (filter->points_at.nr && !match_points_at(&filter->points_at, ref->oid, ref->name))
return NULL;
/*
@@ -2918,7 +3002,7 @@ static struct ref_array_item *apply_ref_filter(const char *refname, const char *
*/
if (filter->reachable_from || filter->unreachable_from ||
filter->with_commit || filter->no_commit || filter->verbose) {
commit = lookup_commit_reference_gently(the_repository, oid, 1);
commit = lookup_commit_reference_gently(the_repository, ref->oid, 1);
if (!commit)
return NULL;
/* We perform the filtering for the '--contains' option... */
@@ -2936,13 +3020,13 @@ static struct ref_array_item *apply_ref_filter(const char *refname, const char *
* to do its job and the resulting list may yet to be pruned
* by maxcount logic.
*/
ref = new_ref_array_item(refname, oid);
ref->commit = commit;
ref->flag = flag;
ref->kind = kind;
ref->symref = xstrdup_or_null(referent);
item = new_ref_array_item(ref->name, ref->oid, ref->peeled_oid);
item->commit = commit;
item->flag = ref->flags;
item->kind = kind;
item->symref = xstrdup_or_null(ref->target);
return ref;
return item;
}
struct ref_filter_cbdata {
@@ -2954,14 +3038,14 @@ struct ref_filter_cbdata {
* A call-back given to for_each_ref(). Filter refs and keep them for
* later object processing.
*/
static int filter_one(const char *refname, const char *referent, const struct object_id *oid, int flag, void *cb_data)
static int filter_one(const struct reference *ref, void *cb_data)
{
struct ref_filter_cbdata *ref_cbdata = cb_data;
struct ref_array_item *ref;
struct ref_array_item *item;
ref = apply_ref_filter(refname, referent, oid, flag, ref_cbdata->filter);
if (ref)
ref_array_append(ref_cbdata->array, ref);
item = apply_ref_filter(ref, ref_cbdata->filter);
if (item)
ref_array_append(ref_cbdata->array, item);
return 0;
}
@@ -2990,17 +3074,17 @@ struct ref_filter_and_format_cbdata {
} internal;
};
static int filter_and_format_one(const char *refname, const char *referent, const struct object_id *oid, int flag, void *cb_data)
static int filter_and_format_one(const struct reference *ref, void *cb_data)
{
struct ref_filter_and_format_cbdata *ref_cbdata = cb_data;
struct ref_array_item *ref;
struct ref_array_item *item;
struct strbuf output = STRBUF_INIT, err = STRBUF_INIT;
ref = apply_ref_filter(refname, referent, oid, flag, ref_cbdata->filter);
if (!ref)
item = apply_ref_filter(ref, ref_cbdata->filter);
if (!item)
return 0;
if (format_ref_array_item(ref, ref_cbdata->format, &output, &err))
if (format_ref_array_item(item, ref_cbdata->format, &output, &err))
die("%s", err.buf);
if (output.len || !ref_cbdata->format->array_opts.omit_empty) {
@@ -3010,7 +3094,7 @@ static int filter_and_format_one(const char *refname, const char *referent, cons
strbuf_release(&output);
strbuf_release(&err);
free_array_item(ref);
free_array_item(item);
/*
* Increment the running count of refs that match the filter. If
@@ -3583,13 +3667,14 @@ void print_formatted_ref_array(struct ref_array *array, struct ref_format *forma
}
void pretty_print_ref(const char *name, const struct object_id *oid,
const struct object_id *peeled_oid,
struct ref_format *format)
{
struct ref_array_item *ref_item;
struct strbuf output = STRBUF_INIT;
struct strbuf err = STRBUF_INIT;
ref_item = new_ref_array_item(name, oid);
ref_item = new_ref_array_item(name, oid, peeled_oid);
ref_item->kind = ref_kind_from_refname(name);
if (format_ref_array_item(ref_item, format, &output, &err))
die("%s", err.buf);

View File

@@ -41,6 +41,7 @@ enum ref_sorting_order {
struct ref_array_item {
struct object_id objectname;
struct object_id peeled_oid;
const char *rest;
int flag;
unsigned int kind;
@@ -135,6 +136,8 @@ struct ref_format {
OPT_STRVEC(0, "exclude", &(var)->exclude, \
N_("pattern"), N_("exclude refs which match pattern"))
/* Get the reference kind from the provided reference name. */
int ref_kind_from_refname(const char *refname);
/*
* API for filtering a set of refs. Based on the type of refs the user
* has requested, we iterate through those refs and apply filters
@@ -185,6 +188,7 @@ void print_formatted_ref_array(struct ref_array *array, struct ref_format *forma
* name must be a fully qualified refname.
*/
void pretty_print_ref(const char *name, const struct object_id *oid,
const struct object_id *peeled_oid,
struct ref_format *format);
/*
@@ -193,7 +197,8 @@ void pretty_print_ref(const char *name, const struct object_id *oid,
*/
struct ref_array_item *ref_array_push(struct ref_array *array,
const char *refname,
const struct object_id *oid);
const struct object_id *oid,
const struct object_id *peeled_oid);
/*
* If the provided format includes ahead-behind atoms, then compute the

View File

@@ -423,16 +423,13 @@ int should_expire_reflog_ent_verbose(struct object_id *ooid,
return expire;
}
static int push_tip_to_list(const char *refname UNUSED,
const char *referent UNUSED,
const struct object_id *oid,
int flags, void *cb_data)
static int push_tip_to_list(const struct reference *ref, void *cb_data)
{
struct commit_list **list = cb_data;
struct commit *tip_commit;
if (flags & REF_ISSYMREF)
if (ref->flags & REF_ISSYMREF)
return 0;
tip_commit = lookup_commit_reference_gently(the_repository, oid, 1);
tip_commit = lookup_commit_reference_gently(the_repository, ref->oid, 1);
if (!tip_commit)
return 0;
commit_list_insert(tip_commit, list);

85
refs.c
View File

@@ -426,17 +426,19 @@ int refs_ref_exists(struct ref_store *refs, const char *refname)
NULL, NULL);
}
static int for_each_filter_refs(const char *refname, const char *referent,
const struct object_id *oid,
int flags, void *data)
static int for_each_filter_refs(const struct reference *ref, void *data)
{
struct for_each_ref_filter *filter = data;
if (wildmatch(filter->pattern, refname, 0))
if (wildmatch(filter->pattern, ref->name, 0))
return 0;
if (filter->prefix)
skip_prefix(refname, filter->prefix, &refname);
return filter->fn(refname, referent, oid, flags, filter->cb_data);
if (filter->prefix) {
struct reference skipped = *ref;
skip_prefix(skipped.name, filter->prefix, &skipped.name);
return filter->fn(&skipped, filter->cb_data);
} else {
return filter->fn(ref, filter->cb_data);
}
}
struct warn_if_dangling_data {
@@ -447,17 +449,15 @@ struct warn_if_dangling_data {
int dry_run;
};
static int warn_if_dangling_symref(const char *refname, const char *referent UNUSED,
const struct object_id *oid UNUSED,
int flags, void *cb_data)
static int warn_if_dangling_symref(const struct reference *ref, void *cb_data)
{
struct warn_if_dangling_data *d = cb_data;
const char *resolves_to, *msg;
if (!(flags & REF_ISSYMREF))
if (!(ref->flags & REF_ISSYMREF))
return 0;
resolves_to = refs_resolve_ref_unsafe(d->refs, refname, 0, NULL, NULL);
resolves_to = refs_resolve_ref_unsafe(d->refs, ref->name, 0, NULL, NULL);
if (!resolves_to
|| !string_list_has_string(d->refnames, resolves_to)) {
return 0;
@@ -466,7 +466,7 @@ static int warn_if_dangling_symref(const char *refname, const char *referent UNU
msg = d->dry_run
? _("%s%s will become dangling after %s is deleted\n")
: _("%s%s has become dangling after %s was deleted\n");
fprintf(d->fp, msg, d->indent, refname, resolves_to);
fprintf(d->fp, msg, d->indent, ref->name, resolves_to);
return 0;
}
@@ -507,8 +507,15 @@ int refs_head_ref_namespaced(struct ref_store *refs, each_ref_fn fn, void *cb_da
int flag;
strbuf_addf(&buf, "%sHEAD", get_git_namespace());
if (!refs_read_ref_full(refs, buf.buf, RESOLVE_REF_READING, &oid, &flag))
ret = fn(buf.buf, NULL, &oid, flag, cb_data);
if (!refs_read_ref_full(refs, buf.buf, RESOLVE_REF_READING, &oid, &flag)) {
struct reference ref = {
.name = buf.buf,
.oid = &oid,
.flags = flag,
};
ret = fn(&ref, cb_data);
}
strbuf_release(&buf);
return ret;
@@ -1741,8 +1748,15 @@ int refs_head_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
int flag;
if (refs_resolve_ref_unsafe(refs, "HEAD", RESOLVE_REF_READING,
&oid, &flag))
return fn("HEAD", NULL, &oid, flag, cb_data);
&oid, &flag)) {
struct reference ref = {
.name = "HEAD",
.oid = &oid,
.flags = flag,
};
return fn(&ref, cb_data);
}
return 0;
}
@@ -2310,14 +2324,16 @@ int refs_optimize(struct ref_store *refs, struct pack_refs_opts *opts)
return refs->be->optimize(refs, opts);
}
int peel_iterated_oid(struct repository *r, const struct object_id *base, struct object_id *peeled)
int reference_get_peeled_oid(struct repository *repo,
const struct reference *ref,
struct object_id *peeled_oid)
{
if (current_ref_iter &&
(current_ref_iter->oid == base ||
oideq(current_ref_iter->oid, base)))
return ref_iterator_peel(current_ref_iter, peeled);
if (ref->peeled_oid) {
oidcpy(peeled_oid, ref->peeled_oid);
return 0;
}
return peel_object(r, base, peeled) ? -1 : 0;
return peel_object(repo, ref->oid, peeled_oid, 0) ? -1 : 0;
}
int refs_update_symref(struct ref_store *refs, const char *ref,
@@ -2689,7 +2705,7 @@ enum ref_transaction_error refs_verify_refnames_available(struct ref_store *refs
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
if (skip &&
string_list_has_string(skip, iter->refname))
string_list_has_string(skip, iter->ref.name))
continue;
if (transaction && ref_transaction_maybe_set_rejected(
@@ -2698,7 +2714,7 @@ enum ref_transaction_error refs_verify_refnames_available(struct ref_store *refs
continue;
strbuf_addf(err, _("'%s' exists; cannot create '%s'"),
iter->refname, refname);
iter->ref.name, refname);
goto cleanup;
}
@@ -2753,14 +2769,10 @@ struct do_for_each_reflog_help {
void *cb_data;
};
static int do_for_each_reflog_helper(const char *refname,
const char *referent UNUSED,
const struct object_id *oid UNUSED,
int flags UNUSED,
void *cb_data)
static int do_for_each_reflog_helper(const struct reference *ref, void *cb_data)
{
struct do_for_each_reflog_help *hp = cb_data;
return hp->fn(refname, hp->cb_data);
return hp->fn(ref->name, hp->cb_data);
}
int refs_for_each_reflog(struct ref_store *refs, each_reflog_fn fn, void *cb_data)
@@ -2976,25 +2988,24 @@ struct migration_data {
uint64_t index;
};
static int migrate_one_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
int flags, void *cb_data)
static int migrate_one_ref(const struct reference *ref, void *cb_data)
{
struct migration_data *data = cb_data;
struct strbuf symref_target = STRBUF_INIT;
int ret;
if (flags & REF_ISSYMREF) {
ret = refs_read_symbolic_ref(data->old_refs, refname, &symref_target);
if (ref->flags & REF_ISSYMREF) {
ret = refs_read_symbolic_ref(data->old_refs, ref->name, &symref_target);
if (ret < 0)
goto done;
ret = ref_transaction_update(data->transaction, refname, NULL, null_oid(the_hash_algo),
ret = ref_transaction_update(data->transaction, ref->name, NULL, null_oid(the_hash_algo),
symref_target.buf, NULL,
REF_SKIP_CREATE_REFLOG | REF_NO_DEREF, NULL, data->errbuf);
if (ret < 0)
goto done;
} else {
ret = ref_transaction_create(data->transaction, refname, oid, NULL,
ret = ref_transaction_create(data->transaction, ref->name, ref->oid, NULL,
REF_SKIP_CREATE_REFLOG | REF_SKIP_OID_VERIFICATION,
NULL, data->errbuf);
if (ret < 0)

90
refs.h
View File

@@ -333,36 +333,74 @@ struct ref_transaction;
* stored in ref_iterator::flags. Other bits are for internal use
* only:
*/
enum reference_status {
/* Reference is a symbolic reference. */
REF_ISSYMREF = (1 << 0),
/* Reference is a symbolic reference. */
#define REF_ISSYMREF 0x01
/* Reference is a packed reference. */
REF_ISPACKED = (1 << 1),
/* Reference is a packed reference. */
#define REF_ISPACKED 0x02
/*
* Reference cannot be resolved to an object name: dangling symbolic
* reference (directly or indirectly), corrupt reference file,
* reference exists but name is bad, or symbolic reference refers to
* ill-formatted reference name.
*/
REF_ISBROKEN = (1 << 2),
/*
* Reference name is not well formed.
*
* See git-check-ref-format(1) for the definition of well formed ref names.
*/
REF_BAD_NAME = (1 << 3),
};
/* A reference passed to `for_each_ref()`-style callbacks. */
struct reference {
/* The fully-qualified name of the reference. */
const char *name;
/* The target of a symbolic ref. `NULL` for direct references. */
const char *target;
/*
* The object ID of a reference. Either the direct object ID or the
* resolved object ID in the case of a symbolic ref. May be the zero
* object ID in case the symbolic ref cannot be resolved.
*/
const struct object_id *oid;
/*
* An optional peeled object ID. This field _may_ be set for tags in
* case the peeled value is present in the backend. Please refer to
* `reference_get_peeled_oid()`.
*/
const struct object_id *peeled_oid;
/* A bitfield of `enum reference_status` flags. */
unsigned flags;
};
/*
* Reference cannot be resolved to an object name: dangling symbolic
* reference (directly or indirectly), corrupt reference file,
* reference exists but name is bad, or symbolic reference refers to
* ill-formatted reference name.
*/
#define REF_ISBROKEN 0x04
/*
* Reference name is not well formed.
* Peel the tag to a non-tag commit. If present, this uses the peeled object ID
* exposed by the reference backend. Otherwise, the object is peeled via the
* object database, which is less efficient.
*
* See git-check-ref-format(1) for the definition of well formed ref names.
* Return `0` if the reference could be peeled, a negative error code
* otherwise.
*/
#define REF_BAD_NAME 0x08
int reference_get_peeled_oid(struct repository *repo,
const struct reference *ref,
struct object_id *peeled_oid);
/*
* The signature for the callback function for the for_each_*()
* functions below. The memory pointed to by the refname and oid
* arguments is only guaranteed to be valid for the duration of a
* functions below. The memory pointed to by the `struct reference`
* argument is only guaranteed to be valid for the duration of a
* single callback invocation.
*/
typedef int each_ref_fn(const char *refname, const char *referent,
const struct object_id *oid, int flags, void *cb_data);
typedef int each_ref_fn(const struct reference *ref, void *cb_data);
/*
* The following functions invoke the specified callback function for
@@ -1251,10 +1289,6 @@ int repo_migrate_ref_storage_format(struct repository *repo,
* to the next entry, ref_iterator_advance() aborts the iteration,
* frees the ref_iterator, and returns ITER_ERROR.
*
* The reference currently being looked at can be peeled by calling
* ref_iterator_peel(). This function is often faster than peel_ref(),
* so it should be preferred when iterating over references.
*
* Putting it all together, a typical iteration looks like this:
*
* int ok;
@@ -1269,9 +1303,6 @@ int repo_migrate_ref_storage_format(struct repository *repo,
* // Access information about the current reference:
* if (!(iter->flags & REF_ISSYMREF))
* printf("%s is %s\n", iter->refname, oid_to_hex(iter->oid));
*
* // If you need to peel the reference:
* ref_iterator_peel(iter, &oid);
* }
*
* if (ok != ITER_DONE)
@@ -1362,13 +1393,6 @@ enum ref_iterator_seek_flag {
int ref_iterator_seek(struct ref_iterator *ref_iterator, const char *refname,
unsigned int flags);
/*
* 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);
/* Free the reference iterator and any associated resources. */
void ref_iterator_free(struct ref_iterator *ref_iterator);

View File

@@ -160,11 +160,9 @@ static int debug_ref_iterator_advance(struct ref_iterator *ref_iterator)
trace_printf_key(&trace_refs, "iterator_advance: (%d)\n", res);
else
trace_printf_key(&trace_refs, "iterator_advance: %s (0)\n",
diter->iter->refname);
diter->iter->ref.name);
diter->base.refname = diter->iter->refname;
diter->base.oid = diter->iter->oid;
diter->base.flags = diter->iter->flags;
diter->base.ref = diter->iter->ref;
return res;
}
@@ -179,16 +177,6 @@ static int debug_ref_iterator_seek(struct ref_iterator *ref_iterator,
return res;
}
static int debug_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
struct debug_ref_iterator *diter =
(struct debug_ref_iterator *)ref_iterator;
int res = diter->iter->vtable->peel(diter->iter, peeled);
trace_printf_key(&trace_refs, "iterator_peel: %s: %d\n", diter->iter->refname, res);
return res;
}
static void debug_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct debug_ref_iterator *diter =
@@ -200,7 +188,6 @@ static void debug_ref_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable debug_ref_iterator_vtable = {
.advance = debug_ref_iterator_advance,
.seek = debug_ref_iterator_seek,
.peel = debug_ref_iterator_peel,
.release = debug_ref_iterator_release,
};

View File

@@ -961,26 +961,23 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator)
while ((ok = ref_iterator_advance(iter->iter0)) == ITER_OK) {
if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY &&
parse_worktree_ref(iter->iter0->refname, NULL, NULL,
parse_worktree_ref(iter->iter0->ref.name, NULL, NULL,
NULL) != REF_WORKTREE_CURRENT)
continue;
if ((iter->flags & DO_FOR_EACH_OMIT_DANGLING_SYMREFS) &&
(iter->iter0->flags & REF_ISSYMREF) &&
(iter->iter0->flags & REF_ISBROKEN))
(iter->iter0->ref.flags & REF_ISSYMREF) &&
(iter->iter0->ref.flags & REF_ISBROKEN))
continue;
if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
!ref_resolves_to_object(iter->iter0->refname,
!ref_resolves_to_object(iter->iter0->ref.name,
iter->repo,
iter->iter0->oid,
iter->iter0->flags))
iter->iter0->ref.oid,
iter->iter0->ref.flags))
continue;
iter->base.refname = iter->iter0->refname;
iter->base.oid = iter->iter0->oid;
iter->base.flags = iter->iter0->flags;
iter->base.referent = iter->iter0->referent;
iter->base.ref = iter->iter0->ref;
return ITER_OK;
}
@@ -996,15 +993,6 @@ static int files_ref_iterator_seek(struct ref_iterator *ref_iterator,
return ref_iterator_seek(iter->iter0, refname, flags);
}
static int files_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
struct files_ref_iterator *iter =
(struct files_ref_iterator *)ref_iterator;
return ref_iterator_peel(iter->iter0, peeled);
}
static void files_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct files_ref_iterator *iter =
@@ -1015,7 +1003,6 @@ static void files_ref_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable files_ref_iterator_vtable = {
.advance = files_ref_iterator_advance,
.seek = files_ref_iterator_seek,
.peel = files_ref_iterator_peel,
.release = files_ref_iterator_release,
};
@@ -1367,30 +1354,29 @@ static void prune_refs(struct files_ref_store *refs, struct ref_to_prune **refs_
* Return true if the specified reference should be packed.
*/
static int should_pack_ref(struct files_ref_store *refs,
const char *refname,
const struct object_id *oid, unsigned int ref_flags,
const struct reference *ref,
struct pack_refs_opts *opts)
{
struct string_list_item *item;
/* Do not pack per-worktree refs: */
if (parse_worktree_ref(refname, NULL, NULL, NULL) !=
if (parse_worktree_ref(ref->name, NULL, NULL, NULL) !=
REF_WORKTREE_SHARED)
return 0;
/* Do not pack symbolic refs: */
if (ref_flags & REF_ISSYMREF)
if (ref->flags & REF_ISSYMREF)
return 0;
/* Do not pack broken refs: */
if (!ref_resolves_to_object(refname, refs->base.repo, oid, ref_flags))
if (!ref_resolves_to_object(ref->name, refs->base.repo, ref->oid, ref->flags))
return 0;
if (ref_excluded(opts->exclusions, refname))
if (ref_excluded(opts->exclusions, ref->name))
return 0;
for_each_string_list_item(item, opts->includes)
if (!wildmatch(item->string, refname, 0))
if (!wildmatch(item->string, ref->name, 0))
return 1;
return 0;
@@ -1443,8 +1429,7 @@ static int should_pack_refs(struct files_ref_store *refs,
iter = cache_ref_iterator_begin(get_loose_ref_cache(refs, 0), NULL,
refs->base.repo, 0);
while ((ret = ref_iterator_advance(iter)) == ITER_OK) {
if (should_pack_ref(refs, iter->refname, iter->oid,
iter->flags, opts))
if (should_pack_ref(refs, &iter->ref, opts))
refcount++;
if (refcount >= limit) {
ref_iterator_free(iter);
@@ -1489,24 +1474,24 @@ static int files_pack_refs(struct ref_store *ref_store,
* in the packed ref cache. If the reference should be
* pruned, also add it to refs_to_prune.
*/
if (!should_pack_ref(refs, iter->refname, iter->oid, iter->flags, opts))
if (!should_pack_ref(refs, &iter->ref, opts))
continue;
/*
* Add a reference creation for this reference to the
* packed-refs transaction:
*/
if (ref_transaction_update(transaction, iter->refname,
iter->oid, NULL, NULL, NULL,
if (ref_transaction_update(transaction, iter->ref.name,
iter->ref.oid, NULL, NULL, NULL,
REF_NO_DEREF, NULL, &err))
die("failure preparing to create packed reference %s: %s",
iter->refname, err.buf);
iter->ref.name, err.buf);
/* Schedule the loose reference for pruning if requested. */
if ((opts->flags & PACK_REFS_PRUNE)) {
struct ref_to_prune *n;
FLEX_ALLOC_STR(n, name, iter->refname);
oidcpy(&n->oid, iter->oid);
FLEX_ALLOC_STR(n, name, iter->ref.name);
oidcpy(&n->oid, iter->ref.oid);
n->next = refs_to_prune;
refs_to_prune = n;
}
@@ -2379,7 +2364,7 @@ static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator)
REFNAME_ALLOW_ONELEVEL))
continue;
iter->base.refname = diter->relative_path;
iter->base.ref.name = diter->relative_path;
return ITER_OK;
}
@@ -2393,12 +2378,6 @@ static int files_reflog_iterator_seek(struct ref_iterator *ref_iterator 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 void files_reflog_iterator_release(struct ref_iterator *ref_iterator)
{
struct files_reflog_iterator *iter =
@@ -2409,7 +2388,6 @@ static void files_reflog_iterator_release(struct ref_iterator *ref_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,
.release = files_reflog_iterator_release,
};
@@ -3150,14 +3128,11 @@ static int parse_and_write_reflog(struct files_ref_store *refs,
return 0;
}
static int ref_present(const char *refname, const char *referent UNUSED,
const struct object_id *oid UNUSED,
int flags UNUSED,
void *cb_data)
static int ref_present(const struct reference *ref, void *cb_data)
{
struct string_list *affected_refnames = cb_data;
return string_list_has_string(affected_refnames, refname);
return string_list_has_string(affected_refnames, ref->name);
}
static int files_transaction_finish_initial(struct files_ref_store *refs,
@@ -3327,7 +3302,13 @@ static int files_transaction_finish(struct ref_store *ref_store,
* next update. If not, we try and create a regular symref.
*/
if (update->new_target && refs->prefer_symlink_refs)
if (!create_ref_symlink(lock, update->new_target))
/*
* By using the `NOT_CONSTANT()` trick, we can avoid
* errors by `clang`'s `-Wunreachable` logic that would
* report that the `continue` statement is not reachable
* when `NO_SYMLINK_HEAD` is `#define`d.
*/
if (NOT_CONSTANT(!create_ref_symlink(lock, update->new_target)))
continue;
if (update->flags & REF_NEEDS_COMMIT) {

View File

@@ -21,12 +21,6 @@ int ref_iterator_seek(struct ref_iterator *ref_iterator, const char *refname,
return ref_iterator->vtable->seek(ref_iterator, refname, flags);
}
int ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
return ref_iterator->vtable->peel(ref_iterator, peeled);
}
void ref_iterator_free(struct ref_iterator *ref_iterator)
{
if (ref_iterator) {
@@ -41,10 +35,7 @@ void base_ref_iterator_init(struct ref_iterator *iter,
struct ref_iterator_vtable *vtable)
{
iter->vtable = vtable;
iter->refname = NULL;
iter->referent = NULL;
iter->oid = NULL;
iter->flags = 0;
memset(&iter->ref, 0, sizeof(iter->ref));
}
struct empty_ref_iterator {
@@ -63,12 +54,6 @@ static int empty_ref_iterator_seek(struct ref_iterator *ref_iterator UNUSED,
return 0;
}
static int empty_ref_iterator_peel(struct ref_iterator *ref_iterator UNUSED,
struct object_id *peeled UNUSED)
{
BUG("peel called for empty iterator");
}
static void empty_ref_iterator_release(struct ref_iterator *ref_iterator UNUSED)
{
}
@@ -76,7 +61,6 @@ static void empty_ref_iterator_release(struct ref_iterator *ref_iterator UNUSED)
static struct ref_iterator_vtable empty_ref_iterator_vtable = {
.advance = empty_ref_iterator_advance,
.seek = empty_ref_iterator_seek,
.peel = empty_ref_iterator_peel,
.release = empty_ref_iterator_release,
};
@@ -127,8 +111,8 @@ enum iterator_selection ref_iterator_select(struct ref_iterator *iter_worktree,
* latter.
*/
if (iter_worktree) {
int cmp = strcmp(iter_worktree->refname,
iter_common->refname);
int cmp = strcmp(iter_worktree->ref.name,
iter_common->ref.name);
if (cmp < 0)
return ITER_SELECT_0;
else if (!cmp)
@@ -139,7 +123,7 @@ enum iterator_selection ref_iterator_select(struct ref_iterator *iter_worktree,
* We now know that the lexicographically-next ref is a common
* ref. When the common ref is a shared one we return it.
*/
if (parse_worktree_ref(iter_common->refname, NULL, NULL,
if (parse_worktree_ref(iter_common->ref.name, NULL, NULL,
NULL) == REF_WORKTREE_SHARED)
return ITER_SELECT_1;
@@ -212,10 +196,7 @@ static int merge_ref_iterator_advance(struct ref_iterator *ref_iterator)
}
if (selection & ITER_YIELD_CURRENT) {
iter->base.referent = (*iter->current)->referent;
iter->base.refname = (*iter->current)->refname;
iter->base.oid = (*iter->current)->oid;
iter->base.flags = (*iter->current)->flags;
iter->base.ref = (*iter->current)->ref;
return ITER_OK;
}
}
@@ -246,18 +227,6 @@ static int merge_ref_iterator_seek(struct ref_iterator *ref_iterator,
return 0;
}
static int merge_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
struct merge_ref_iterator *iter =
(struct merge_ref_iterator *)ref_iterator;
if (!iter->current) {
BUG("peel called before advance for merge iterator");
}
return ref_iterator_peel(*iter->current, peeled);
}
static void merge_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct merge_ref_iterator *iter =
@@ -269,7 +238,6 @@ static void merge_ref_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable merge_ref_iterator_vtable = {
.advance = merge_ref_iterator_advance,
.seek = merge_ref_iterator_seek,
.peel = merge_ref_iterator_peel,
.release = merge_ref_iterator_release,
};
@@ -313,7 +281,7 @@ static enum iterator_selection overlay_iterator_select(
else if (!front)
return ITER_SELECT_1;
cmp = strcmp(front->refname, back->refname);
cmp = strcmp(front->ref.name, back->ref.name);
if (cmp < 0)
return ITER_SELECT_0;
@@ -371,7 +339,7 @@ static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator)
int ok;
while ((ok = ref_iterator_advance(iter->iter0)) == ITER_OK) {
int cmp = compare_prefix(iter->iter0->refname, iter->prefix);
int cmp = compare_prefix(iter->iter0->ref.name, iter->prefix);
if (cmp < 0)
continue;
/*
@@ -382,6 +350,8 @@ static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator)
if (cmp > 0)
return ITER_DONE;
iter->base.ref = iter->iter0->ref;
if (iter->trim) {
/*
* It is nonsense to trim off characters that
@@ -392,15 +362,11 @@ static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator)
* one character left in the refname after
* trimming, report it as a bug:
*/
if (strlen(iter->iter0->refname) <= iter->trim)
if (strlen(iter->base.ref.name) <= iter->trim)
BUG("attempt to trim too many characters");
iter->base.refname = iter->iter0->refname + iter->trim;
} else {
iter->base.refname = iter->iter0->refname;
iter->base.ref.name += iter->trim;
}
iter->base.oid = iter->iter0->oid;
iter->base.flags = iter->iter0->flags;
return ITER_OK;
}
@@ -420,15 +386,6 @@ static int prefix_ref_iterator_seek(struct ref_iterator *ref_iterator,
return ref_iterator_seek(iter->iter0, refname, flags);
}
static int prefix_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
struct prefix_ref_iterator *iter =
(struct prefix_ref_iterator *)ref_iterator;
return ref_iterator_peel(iter->iter0, peeled);
}
static void prefix_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct prefix_ref_iterator *iter =
@@ -440,7 +397,6 @@ static void prefix_ref_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable prefix_ref_iterator_vtable = {
.advance = prefix_ref_iterator_advance,
.seek = prefix_ref_iterator_seek,
.peel = prefix_ref_iterator_peel,
.release = prefix_ref_iterator_release,
};
@@ -466,23 +422,18 @@ struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
return ref_iterator;
}
struct ref_iterator *current_ref_iter = NULL;
int do_for_each_ref_iterator(struct ref_iterator *iter,
each_ref_fn fn, void *cb_data)
{
int retval = 0, ok;
struct ref_iterator *old_ref_iter = current_ref_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);
retval = fn(&iter->ref, cb_data);
if (retval)
goto out;
}
out:
current_ref_iter = old_ref_iter;
if (ok == ITER_ERROR)
retval = -1;
ref_iterator_free(iter);

View File

@@ -882,6 +882,7 @@ static int next_record(struct packed_ref_iterator *iter)
{
const char *p, *eol;
memset(&iter->base.ref, 0, sizeof(iter->base.ref));
strbuf_reset(&iter->refname_buf);
/*
@@ -908,7 +909,7 @@ static int next_record(struct packed_ref_iterator *iter)
if (iter->pos == iter->eof)
return ITER_DONE;
iter->base.flags = REF_ISPACKED;
iter->base.ref.flags = REF_ISPACKED;
p = iter->pos;
if (iter->eof - p < snapshot_hexsz(iter->snapshot) + 2 ||
@@ -916,6 +917,7 @@ static int next_record(struct packed_ref_iterator *iter)
!isspace(*p++))
die_invalid_line(iter->snapshot->refs->path,
iter->pos, iter->eof - iter->pos);
iter->base.ref.oid = &iter->oid;
eol = memchr(p, '\n', iter->eof - p);
if (!eol)
@@ -923,22 +925,22 @@ static int next_record(struct packed_ref_iterator *iter)
iter->pos, iter->eof - iter->pos);
strbuf_add(&iter->refname_buf, p, eol - p);
iter->base.refname = iter->refname_buf.buf;
iter->base.ref.name = iter->refname_buf.buf;
if (refname_contains_nul(&iter->refname_buf))
die("packed refname contains embedded NULL: %s", iter->base.refname);
die("packed refname contains embedded NULL: %s", iter->base.ref.name);
if (check_refname_format(iter->base.refname, REFNAME_ALLOW_ONELEVEL)) {
if (!refname_is_safe(iter->base.refname))
if (check_refname_format(iter->base.ref.name, REFNAME_ALLOW_ONELEVEL)) {
if (!refname_is_safe(iter->base.ref.name))
die("packed refname is dangerous: %s",
iter->base.refname);
iter->base.ref.name);
oidclr(&iter->oid, iter->repo->hash_algo);
iter->base.flags |= REF_BAD_NAME | REF_ISBROKEN;
iter->base.ref.flags |= REF_BAD_NAME | REF_ISBROKEN;
}
if (iter->snapshot->peeled == PEELED_FULLY ||
(iter->snapshot->peeled == PEELED_TAGS &&
starts_with(iter->base.refname, "refs/tags/")))
iter->base.flags |= REF_KNOWS_PEELED;
starts_with(iter->base.ref.name, "refs/tags/")))
iter->base.ref.flags |= REF_KNOWS_PEELED;
iter->pos = eol + 1;
@@ -956,11 +958,12 @@ static int next_record(struct packed_ref_iterator *iter)
* definitely know the value of *this* reference. But
* we suppress it if the reference is broken:
*/
if ((iter->base.flags & REF_ISBROKEN)) {
if ((iter->base.ref.flags & REF_ISBROKEN)) {
oidclr(&iter->peeled, iter->repo->hash_algo);
iter->base.flags &= ~REF_KNOWS_PEELED;
iter->base.ref.flags &= ~REF_KNOWS_PEELED;
} else {
iter->base.flags |= REF_KNOWS_PEELED;
iter->base.ref.flags |= REF_KNOWS_PEELED;
iter->base.ref.peeled_oid = &iter->peeled;
}
} else {
oidclr(&iter->peeled, iter->repo->hash_algo);
@@ -976,15 +979,15 @@ 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 *refname = iter->base.ref.name;
const char *prefix = iter->prefix;
if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY &&
!is_per_worktree_ref(iter->base.refname))
!is_per_worktree_ref(iter->base.ref.name))
continue;
if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
!ref_resolves_to_object(iter->base.refname, iter->repo,
!ref_resolves_to_object(iter->base.ref.name, iter->repo,
&iter->oid, iter->flags))
continue;
@@ -1027,22 +1030,6 @@ static int packed_ref_iterator_seek(struct ref_iterator *ref_iterator,
return 0;
}
static int packed_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
struct packed_ref_iterator *iter =
(struct packed_ref_iterator *)ref_iterator;
if ((iter->base.flags & REF_KNOWS_PEELED)) {
oidcpy(peeled, &iter->peeled);
return is_null_oid(&iter->peeled) ? -1 : 0;
} else if ((iter->base.flags & (REF_ISBROKEN | REF_ISSYMREF))) {
return -1;
} else {
return peel_object(iter->repo, &iter->oid, peeled) ? -1 : 0;
}
}
static void packed_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct packed_ref_iterator *iter =
@@ -1056,7 +1043,6 @@ static void packed_ref_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable packed_ref_iterator_vtable = {
.advance = packed_ref_iterator_advance,
.seek = packed_ref_iterator_seek,
.peel = packed_ref_iterator_peel,
.release = packed_ref_iterator_release,
};
@@ -1194,7 +1180,6 @@ static struct ref_iterator *packed_ref_iterator_begin(
iter->snapshot = snapshot;
acquire_snapshot(snapshot);
strbuf_init(&iter->refname_buf, 0);
iter->base.oid = &iter->oid;
iter->repo = ref_store->repo;
iter->flags = flags;
@@ -1436,7 +1421,7 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
if (!iter)
cmp = +1;
else
cmp = strcmp(iter->refname, update->refname);
cmp = strcmp(iter->ref.name, update->refname);
}
if (!cmp) {
@@ -1459,11 +1444,11 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
}
goto error;
} else if (!oideq(&update->old_oid, iter->oid)) {
} else if (!oideq(&update->old_oid, iter->ref.oid)) {
strbuf_addf(err, "cannot update ref '%s': "
"is at %s but expected %s",
update->refname,
oid_to_hex(iter->oid),
oid_to_hex(iter->ref.oid),
oid_to_hex(&update->old_oid));
ret = REF_TRANSACTION_ERROR_INCORRECT_OLD_VALUE;
@@ -1523,13 +1508,8 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
if (cmp < 0) {
/* Pass the old reference through. */
struct object_id peeled;
int peel_error = ref_iterator_peel(iter, &peeled);
if (write_packed_entry(out, iter->refname,
iter->oid,
peel_error ? NULL : &peeled))
if (write_packed_entry(out, iter->ref.name,
iter->ref.oid, iter->ref.peeled_oid))
goto write_error;
if ((ok = ref_iterator_advance(iter)) != ITER_OK) {
@@ -1547,9 +1527,8 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
i++;
} else {
struct object_id peeled;
int peel_error = peel_object(refs->base.repo,
&update->new_oid,
&peeled);
int peel_error = peel_object(refs->base.repo, &update->new_oid,
&peeled, PEEL_OBJECT_VERIFY_OBJECT_TYPE);
if (write_packed_entry(out, update->refname,
&update->new_oid,

View File

@@ -425,10 +425,11 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
level->prefix_state = entry_prefix_state;
level->index = -1;
} else {
iter->base.refname = entry->name;
iter->base.referent = entry->u.value.referent;
iter->base.oid = &entry->u.value.oid;
iter->base.flags = entry->flag;
memset(&iter->base.ref, 0, sizeof(iter->base.ref));
iter->base.ref.name = entry->name;
iter->base.ref.target = entry->u.value.referent;
iter->base.ref.oid = &entry->u.value.oid;
iter->base.ref.flags = entry->flag;
return ITER_OK;
}
}
@@ -545,14 +546,6 @@ static int cache_ref_iterator_seek(struct ref_iterator *ref_iterator,
return 0;
}
static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
struct cache_ref_iterator *iter =
(struct cache_ref_iterator *)ref_iterator;
return peel_object(iter->repo, ref_iterator->oid, peeled) ? -1 : 0;
}
static void cache_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct cache_ref_iterator *iter =
@@ -564,7 +557,6 @@ static void cache_ref_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable cache_ref_iterator_vtable = {
.advance = cache_ref_iterator_advance,
.seek = cache_ref_iterator_seek,
.peel = cache_ref_iterator_peel,
.release = cache_ref_iterator_release,
};

View File

@@ -249,10 +249,7 @@ const char *find_descendant_ref(const char *dirname,
*/
struct ref_iterator {
struct ref_iterator_vtable *vtable;
const char *refname;
const char *referent;
const struct object_id *oid;
unsigned int flags;
struct reference ref;
};
/*
@@ -360,12 +357,6 @@ typedef int ref_iterator_advance_fn(struct ref_iterator *ref_iterator);
typedef int ref_iterator_seek_fn(struct ref_iterator *ref_iterator,
const char *refname, unsigned int flags);
/*
* Peels the current ref, returning 0 for success or -1 for failure.
*/
typedef int ref_iterator_peel_fn(struct ref_iterator *ref_iterator,
struct object_id *peeled);
/*
* Implementations of this function should free any resources specific
* to the derived class.
@@ -375,23 +366,9 @@ 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_release_fn *release;
};
/*
* current_ref_iter is a performance hack: when iterating over
* references using the for_each_ref*() functions, current_ref_iter is
* set to the reference iterator before calling the callback function.
* If the callback function calls peel_ref(), then peel_ref() first
* checks whether the reference to be peeled is the one referred to by
* the iterator (it usually is) and if so, asks the iterator for the
* peeled version of the reference if it is available. This avoids a
* refname lookup in a common case. current_ref_iter is set to NULL
* when the iteration is over.
*/
extern struct ref_iterator *current_ref_iter;
struct ref_store;
/* refs backends */

View File

@@ -547,6 +547,7 @@ struct reftable_ref_iterator {
struct reftable_iterator iter;
struct reftable_ref_record ref;
struct object_id oid;
struct object_id peeled_oid;
char *prefix;
size_t prefix_len;
@@ -671,6 +672,8 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
case REFTABLE_REF_VAL2:
oidread(&iter->oid, iter->ref.value.val2.value,
refs->base.repo->hash_algo);
oidread(&iter->peeled_oid, iter->ref.value.val2.target_value,
refs->base.repo->hash_algo);
break;
case REFTABLE_REF_SYMREF:
referent = refs_resolve_ref_unsafe(&iter->refs->base,
@@ -704,10 +707,13 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
&iter->oid, flags))
continue;
iter->base.refname = iter->ref.refname;
iter->base.referent = referent;
iter->base.oid = &iter->oid;
iter->base.flags = flags;
memset(&iter->base.ref, 0, sizeof(iter->base.ref));
iter->base.ref.name = iter->ref.refname;
iter->base.ref.target = referent;
iter->base.ref.oid = &iter->oid;
if (iter->ref.value_type == REFTABLE_REF_VAL2)
iter->base.ref.peeled_oid = &iter->peeled_oid;
iter->base.ref.flags = flags;
break;
}
@@ -738,21 +744,6 @@ static int reftable_ref_iterator_seek(struct ref_iterator *ref_iterator,
return iter->err;
}
static int reftable_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
struct reftable_ref_iterator *iter =
(struct reftable_ref_iterator *)ref_iterator;
if (iter->ref.value_type == REFTABLE_REF_VAL2) {
oidread(peeled, iter->ref.value.val2.target_value,
iter->refs->base.repo->hash_algo);
return 0;
}
return -1;
}
static void reftable_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct reftable_ref_iterator *iter =
@@ -770,7 +761,6 @@ static void reftable_ref_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable reftable_ref_iterator_vtable = {
.advance = reftable_ref_iterator_advance,
.seek = reftable_ref_iterator_seek,
.peel = reftable_ref_iterator_peel,
.release = reftable_ref_iterator_release,
};
@@ -828,7 +818,7 @@ 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->base.oid = &iter->oid;
iter->base.ref.oid = &iter->oid;
iter->flags = flags;
iter->refs = refs;
iter->exclude_patterns = filter_exclude_patterns(exclude_patterns);
@@ -1642,7 +1632,8 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
ref.refname = (char *)u->refname;
ref.update_index = ts;
peel_error = peel_object(arg->refs->base.repo, &u->new_oid, &peeled);
peel_error = peel_object(arg->refs->base.repo, &u->new_oid, &peeled,
PEEL_OBJECT_VERIFY_OBJECT_TYPE);
if (!peel_error) {
ref.value_type = REFTABLE_REF_VAL2;
memcpy(ref.value.val2.target_value, peeled.hash, GIT_MAX_RAWSZ);
@@ -2072,7 +2063,7 @@ static int reftable_reflog_iterator_advance(struct ref_iterator *ref_iterator)
strbuf_reset(&iter->last_name);
strbuf_addstr(&iter->last_name, iter->log.refname);
iter->base.refname = iter->log.refname;
iter->base.ref.name = iter->log.refname;
break;
}
@@ -2092,13 +2083,6 @@ static int reftable_reflog_iterator_seek(struct ref_iterator *ref_iterator UNUSE
return -1;
}
static int reftable_reflog_iterator_peel(struct ref_iterator *ref_iterator UNUSED,
struct object_id *peeled UNUSED)
{
BUG("reftable reflog iterator cannot be peeled");
return -1;
}
static void reftable_reflog_iterator_release(struct ref_iterator *ref_iterator)
{
struct reftable_reflog_iterator *iter =
@@ -2111,7 +2095,6 @@ static void reftable_reflog_iterator_release(struct ref_iterator *ref_iterator)
static struct ref_iterator_vtable reftable_reflog_iterator_vtable = {
.advance = reftable_reflog_iterator_advance,
.seek = reftable_reflog_iterator_seek,
.peel = reftable_reflog_iterator_peel,
.release = reftable_reflog_iterator_release,
};
@@ -2515,7 +2498,7 @@ static int write_reflog_expiry_table(struct reftable_writer *writer, void *cb_da
ref.refname = (char *)arg->refname;
ref.update_index = ts;
if (!peel_object(arg->refs->base.repo, &arg->update_oid, &peeled)) {
if (!peel_object(arg->refs->base.repo, &arg->update_oid, &peeled, 0)) {
ref.value_type = REFTABLE_REF_VAL2;
memcpy(ref.value.val2.target_value, peeled.hash, GIT_MAX_RAWSZ);
memcpy(ref.value.val2.value, arg->update_oid.hash, GIT_MAX_RAWSZ);

View File

@@ -2315,21 +2315,19 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
return 1;
}
static int one_local_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
int flag UNUSED,
void *cb_data)
static int one_local_ref(const struct reference *ref, void *cb_data)
{
struct ref ***local_tail = cb_data;
struct ref *ref;
struct ref *local_ref;
/* we already know it starts with refs/ to get here */
if (check_refname_format(refname + 5, 0))
if (check_refname_format(ref->name + 5, 0))
return 0;
ref = alloc_ref(refname);
oidcpy(&ref->new_oid, oid);
**local_tail = ref;
*local_tail = &ref->next;
local_ref = alloc_ref(ref->name);
oidcpy(&local_ref->new_oid, ref->oid);
**local_tail = local_ref;
*local_tail = &local_ref->next;
return 0;
}
@@ -2402,15 +2400,14 @@ struct stale_heads_info {
struct refspec *rs;
};
static int get_stale_heads_cb(const char *refname, const char *referent UNUSED, const struct object_id *oid,
int flags, void *cb_data)
static int get_stale_heads_cb(const struct reference *ref, void *cb_data)
{
struct stale_heads_info *info = cb_data;
struct string_list matches = STRING_LIST_INIT_DUP;
struct refspec_item query;
int i, stale = 1;
memset(&query, 0, sizeof(struct refspec_item));
query.dst = (char *)refname;
query.dst = (char *)ref->name;
refspec_find_all_matches(info->rs, &query, &matches);
if (matches.nr == 0)
@@ -2423,7 +2420,7 @@ static int get_stale_heads_cb(const char *refname, const char *referent UNUSED,
* overlapping refspecs, we need to go over all of the
* matching refs.
*/
if (flags & REF_ISSYMREF)
if (ref->flags & REF_ISSYMREF)
goto clean_exit;
for (i = 0; stale && i < matches.nr; i++)
@@ -2431,8 +2428,8 @@ static int get_stale_heads_cb(const char *refname, const char *referent UNUSED,
stale = 0;
if (stale) {
struct ref *ref = make_linked_ref(refname, &info->stale_refs_tail);
oidcpy(&ref->new_oid, oid);
struct ref *linked_ref = make_linked_ref(ref->name, &info->stale_refs_tail);
oidcpy(&linked_ref->new_oid, ref->oid);
}
clean_exit:

99
repack-cruft.c Normal file
View File

@@ -0,0 +1,99 @@
#include "git-compat-util.h"
#include "repack.h"
#include "packfile.h"
#include "repository.h"
#include "run-command.h"
static void combine_small_cruft_packs(FILE *in, off_t combine_cruft_below_size,
struct existing_packs *existing)
{
struct packfile_store *packs = existing->repo->objects->packfiles;
struct packed_git *p;
struct strbuf buf = STRBUF_INIT;
size_t i;
for (p = packfile_store_get_all_packs(packs); p; p = p->next) {
if (!(p->is_cruft && p->pack_local))
continue;
strbuf_reset(&buf);
strbuf_addstr(&buf, pack_basename(p));
strbuf_strip_suffix(&buf, ".pack");
if (!string_list_has_string(&existing->cruft_packs, buf.buf))
continue;
if (p->pack_size < combine_cruft_below_size) {
fprintf(in, "-%s\n", pack_basename(p));
} else {
existing_packs_retain_cruft(existing, p);
fprintf(in, "%s\n", pack_basename(p));
}
}
for (i = 0; i < existing->non_kept_packs.nr; i++)
fprintf(in, "-%s.pack\n",
existing->non_kept_packs.items[i].string);
strbuf_release(&buf);
}
int write_cruft_pack(const struct write_pack_opts *opts,
const char *cruft_expiration,
unsigned long combine_cruft_below_size,
struct string_list *names,
struct existing_packs *existing)
{
struct child_process cmd = CHILD_PROCESS_INIT;
struct string_list_item *item;
FILE *in;
int ret;
const char *pack_prefix = write_pack_opts_pack_prefix(opts);
prepare_pack_objects(&cmd, opts->po_args, opts->destination);
strvec_push(&cmd.args, "--cruft");
if (cruft_expiration)
strvec_pushf(&cmd.args, "--cruft-expiration=%s",
cruft_expiration);
strvec_push(&cmd.args, "--non-empty");
cmd.in = -1;
ret = start_command(&cmd);
if (ret)
return ret;
/*
* names has a confusing double use: it both provides the list
* of just-written new packs, and accepts the name of the cruft
* pack we are writing.
*
* By the time it is read here, it contains only the pack(s)
* that were just written, which is exactly the set of packs we
* want to consider kept.
*
* If `--expire-to` is given, the double-use served by `names`
* ensures that the pack written to `--expire-to` excludes any
* objects contained in the cruft pack.
*/
in = xfdopen(cmd.in, "w");
for_each_string_list_item(item, names)
fprintf(in, "%s-%s.pack\n", pack_prefix, item->string);
if (combine_cruft_below_size && !cruft_expiration) {
combine_small_cruft_packs(in, combine_cruft_below_size,
existing);
} else {
for_each_string_list_item(item, &existing->non_kept_packs)
fprintf(in, "-%s.pack\n", item->string);
for_each_string_list_item(item, &existing->cruft_packs)
fprintf(in, "-%s.pack\n", item->string);
}
for_each_string_list_item(item, &existing->kept_packs)
fprintf(in, "%s.pack\n", item->string);
fclose(in);
return finish_pack_objects_cmd(existing->repo->hash_algo, opts, &cmd,
names);
}

51
repack-filtered.c Normal file
View File

@@ -0,0 +1,51 @@
#include "git-compat-util.h"
#include "repack.h"
#include "repository.h"
#include "run-command.h"
#include "string-list.h"
int write_filtered_pack(const struct write_pack_opts *opts,
struct existing_packs *existing,
struct string_list *names)
{
struct child_process cmd = CHILD_PROCESS_INIT;
struct string_list_item *item;
FILE *in;
int ret;
const char *caret;
const char *pack_prefix = write_pack_opts_pack_prefix(opts);
prepare_pack_objects(&cmd, opts->po_args, opts->destination);
strvec_push(&cmd.args, "--stdin-packs");
for_each_string_list_item(item, &existing->kept_packs)
strvec_pushf(&cmd.args, "--keep-pack=%s", item->string);
cmd.in = -1;
ret = start_command(&cmd);
if (ret)
return ret;
/*
* Here 'names' contains only the pack(s) that were just
* written, which is exactly the packs we want to keep. Also
* 'existing_kept_packs' already contains the packs in
* 'keep_pack_list'.
*/
in = xfdopen(cmd.in, "w");
for_each_string_list_item(item, names)
fprintf(in, "^%s-%s.pack\n", pack_prefix, item->string);
for_each_string_list_item(item, &existing->non_kept_packs)
fprintf(in, "%s.pack\n", item->string);
for_each_string_list_item(item, &existing->cruft_packs)
fprintf(in, "%s.pack\n", item->string);
caret = opts->po_args->pack_kept_objects ? "" : "^";
for_each_string_list_item(item, &existing->kept_packs)
fprintf(in, "%s%s.pack\n", caret, item->string);
fclose(in);
return finish_pack_objects_cmd(existing->repo->hash_algo, opts, &cmd,
names);
}

233
repack-geometry.c Normal file
View File

@@ -0,0 +1,233 @@
#define DISABLE_SIGN_COMPARE_WARNINGS
#include "git-compat-util.h"
#include "repack.h"
#include "repository.h"
#include "hex.h"
#include "packfile.h"
static uint32_t pack_geometry_weight(struct packed_git *p)
{
if (open_pack_index(p))
die(_("cannot open index for %s"), p->pack_name);
return p->num_objects;
}
static int pack_geometry_cmp(const void *va, const void *vb)
{
uint32_t aw = pack_geometry_weight(*(struct packed_git **)va),
bw = pack_geometry_weight(*(struct packed_git **)vb);
if (aw < bw)
return -1;
if (aw > bw)
return 1;
return 0;
}
void pack_geometry_init(struct pack_geometry *geometry,
struct existing_packs *existing,
const struct pack_objects_args *args)
{
struct packfile_store *packs = existing->repo->objects->packfiles;
struct packed_git *p;
struct strbuf buf = STRBUF_INIT;
for (p = packfile_store_get_all_packs(packs); p; p = p->next) {
if (args->local && !p->pack_local)
/*
* When asked to only repack local packfiles we skip
* over any packfiles that are borrowed from alternate
* object directories.
*/
continue;
if (!args->pack_kept_objects) {
/*
* Any pack that has its pack_keep bit set will
* appear in existing->kept_packs below, but
* this saves us from doing a more expensive
* check.
*/
if (p->pack_keep)
continue;
/*
* The pack may be kept via the --keep-pack
* option; check 'existing->kept_packs' to
* determine whether to ignore it.
*/
strbuf_reset(&buf);
strbuf_addstr(&buf, pack_basename(p));
strbuf_strip_suffix(&buf, ".pack");
if (string_list_has_string(&existing->kept_packs, buf.buf))
continue;
}
if (p->is_cruft)
continue;
ALLOC_GROW(geometry->pack,
geometry->pack_nr + 1,
geometry->pack_alloc);
geometry->pack[geometry->pack_nr] = p;
geometry->pack_nr++;
}
QSORT(geometry->pack, geometry->pack_nr, pack_geometry_cmp);
strbuf_release(&buf);
}
void pack_geometry_split(struct pack_geometry *geometry)
{
uint32_t i;
uint32_t split;
off_t total_size = 0;
if (!geometry->pack_nr) {
geometry->split = geometry->pack_nr;
return;
}
/*
* First, count the number of packs (in descending order of size) which
* already form a geometric progression.
*/
for (i = geometry->pack_nr - 1; i > 0; i--) {
struct packed_git *ours = geometry->pack[i];
struct packed_git *prev = geometry->pack[i - 1];
if (unsigned_mult_overflows(geometry->split_factor,
pack_geometry_weight(prev)))
die(_("pack %s too large to consider in geometric "
"progression"),
prev->pack_name);
if (pack_geometry_weight(ours) <
geometry->split_factor * pack_geometry_weight(prev))
break;
}
split = i;
if (split) {
/*
* Move the split one to the right, since the top element in the
* last-compared pair can't be in the progression. Only do this
* when we split in the middle of the array (otherwise if we got
* to the end, then the split is in the right place).
*/
split++;
}
/*
* Then, anything to the left of 'split' must be in a new pack. But,
* creating that new pack may cause packs in the heavy half to no longer
* form a geometric progression.
*
* Compute an expected size of the new pack, and then determine how many
* packs in the heavy half need to be joined into it (if any) to restore
* the geometric progression.
*/
for (i = 0; i < split; i++) {
struct packed_git *p = geometry->pack[i];
if (unsigned_add_overflows(total_size, pack_geometry_weight(p)))
die(_("pack %s too large to roll up"), p->pack_name);
total_size += pack_geometry_weight(p);
}
for (i = split; i < geometry->pack_nr; i++) {
struct packed_git *ours = geometry->pack[i];
if (unsigned_mult_overflows(geometry->split_factor,
total_size))
die(_("pack %s too large to roll up"), ours->pack_name);
if (pack_geometry_weight(ours) <
geometry->split_factor * total_size) {
if (unsigned_add_overflows(total_size,
pack_geometry_weight(ours)))
die(_("pack %s too large to roll up"),
ours->pack_name);
split++;
total_size += pack_geometry_weight(ours);
} else
break;
}
geometry->split = split;
}
struct packed_git *pack_geometry_preferred_pack(struct pack_geometry *geometry)
{
uint32_t i;
if (!geometry) {
/*
* No geometry means either an all-into-one repack (in which
* case there is only one pack left and it is the largest) or an
* incremental one.
*
* If repacking incrementally, then we could check the size of
* all packs to determine which should be preferred, but leave
* this for later.
*/
return NULL;
}
if (geometry->split == geometry->pack_nr)
return NULL;
/*
* The preferred pack is the largest pack above the split line. In
* other words, it is the largest pack that does not get rolled up in
* the geometric repack.
*/
for (i = geometry->pack_nr; i > geometry->split; i--)
/*
* A pack that is not local would never be included in a
* multi-pack index. We thus skip over any non-local packs.
*/
if (geometry->pack[i - 1]->pack_local)
return geometry->pack[i - 1];
return NULL;
}
void pack_geometry_remove_redundant(struct pack_geometry *geometry,
struct string_list *names,
struct existing_packs *existing,
const char *packdir)
{
const struct git_hash_algo *algop = existing->repo->hash_algo;
struct strbuf buf = STRBUF_INIT;
uint32_t i;
for (i = 0; i < geometry->split; i++) {
struct packed_git *p = geometry->pack[i];
if (string_list_has_string(names, hash_to_hex_algop(p->hash,
algop)))
continue;
strbuf_reset(&buf);
strbuf_addstr(&buf, pack_basename(p));
strbuf_strip_suffix(&buf, ".pack");
if ((p->pack_keep) ||
(string_list_has_string(&existing->kept_packs, buf.buf)))
continue;
repack_remove_redundant_pack(existing->repo, packdir, buf.buf);
}
strbuf_release(&buf);
}
void pack_geometry_release(struct pack_geometry *geometry)
{
if (!geometry)
return;
free(geometry->pack);
}

370
repack-midx.c Normal file
View File

@@ -0,0 +1,370 @@
#include "git-compat-util.h"
#include "repack.h"
#include "hash.h"
#include "hex.h"
#include "odb.h"
#include "oidset.h"
#include "pack-bitmap.h"
#include "refs.h"
#include "run-command.h"
#include "tempfile.h"
struct midx_snapshot_ref_data {
struct repository *repo;
struct tempfile *f;
struct oidset seen;
int preferred;
};
static int midx_snapshot_ref_one(const struct reference *ref, void *_data)
{
struct midx_snapshot_ref_data *data = _data;
const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
if (!reference_get_peeled_oid(data->repo, ref, &peeled))
maybe_peeled = &peeled;
if (oidset_insert(&data->seen, maybe_peeled))
return 0; /* already seen */
if (odb_read_object_info(data->repo->objects, maybe_peeled, NULL) != OBJ_COMMIT)
return 0;
fprintf(data->f->fp, "%s%s\n", data->preferred ? "+" : "",
oid_to_hex(maybe_peeled));
return 0;
}
void midx_snapshot_refs(struct repository *repo, struct tempfile *f)
{
struct midx_snapshot_ref_data data;
const struct string_list *preferred = bitmap_preferred_tips(repo);
data.repo = repo;
data.f = f;
data.preferred = 0;
oidset_init(&data.seen, 0);
if (!fdopen_tempfile(f, "w"))
die(_("could not open tempfile %s for writing"),
get_tempfile_path(f));
if (preferred) {
struct string_list_item *item;
data.preferred = 1;
for_each_string_list_item(item, preferred)
refs_for_each_ref_in(get_main_ref_store(repo),
item->string,
midx_snapshot_ref_one, &data);
data.preferred = 0;
}
refs_for_each_ref(get_main_ref_store(repo),
midx_snapshot_ref_one, &data);
if (close_tempfile_gently(f)) {
int save_errno = errno;
delete_tempfile(&f);
errno = save_errno;
die_errno(_("could not close refs snapshot tempfile"));
}
oidset_clear(&data.seen);
}
static int midx_has_unknown_packs(struct string_list *include,
struct pack_geometry *geometry,
struct existing_packs *existing)
{
struct string_list_item *item;
string_list_sort(include);
for_each_string_list_item(item, &existing->midx_packs) {
const char *pack_name = item->string;
/*
* Determine whether or not each MIDX'd pack from the existing
* MIDX (if any) is represented in the new MIDX. For each pack
* in the MIDX, it must either be:
*
* - In the "include" list of packs to be included in the new
* MIDX. Note this function is called before the include
* list is populated with any cruft pack(s).
*
* - Below the geometric split line (if using pack geometry),
* indicating that the pack won't be included in the new
* MIDX, but its contents were rolled up as part of the
* geometric repack.
*
* - In the existing non-kept packs list (if not using pack
* geometry), and marked as non-deleted.
*/
if (string_list_has_string(include, pack_name)) {
continue;
} else if (geometry) {
struct strbuf buf = STRBUF_INIT;
uint32_t j;
for (j = 0; j < geometry->split; j++) {
strbuf_reset(&buf);
strbuf_addstr(&buf, pack_basename(geometry->pack[j]));
strbuf_strip_suffix(&buf, ".pack");
strbuf_addstr(&buf, ".idx");
if (!strcmp(pack_name, buf.buf)) {
strbuf_release(&buf);
break;
}
}
strbuf_release(&buf);
if (j < geometry->split)
continue;
} else {
struct string_list_item *item;
item = string_list_lookup(&existing->non_kept_packs,
pack_name);
if (item && !existing_pack_is_marked_for_deletion(item))
continue;
}
/*
* If we got to this point, the MIDX includes some pack that we
* don't know about.
*/
return 1;
}
return 0;
}
static void midx_included_packs(struct string_list *include,
struct repack_write_midx_opts *opts)
{
struct existing_packs *existing = opts->existing;
struct pack_geometry *geometry = opts->geometry;
struct string_list *names = opts->names;
struct string_list_item *item;
struct strbuf buf = STRBUF_INIT;
for_each_string_list_item(item, &existing->kept_packs) {
strbuf_reset(&buf);
strbuf_addf(&buf, "%s.idx", item->string);
string_list_insert(include, buf.buf);
}
for_each_string_list_item(item, names) {
strbuf_reset(&buf);
strbuf_addf(&buf, "pack-%s.idx", item->string);
string_list_insert(include, buf.buf);
}
if (geometry->split_factor) {
uint32_t i;
for (i = geometry->split; i < geometry->pack_nr; i++) {
struct packed_git *p = geometry->pack[i];
/*
* The multi-pack index never refers to packfiles part
* of an alternate object database, so we skip these.
* While git-multi-pack-index(1) would silently ignore
* them anyway, this allows us to skip executing the
* command completely when we have only non-local
* packfiles.
*/
if (!p->pack_local)
continue;
strbuf_reset(&buf);
strbuf_addstr(&buf, pack_basename(p));
strbuf_strip_suffix(&buf, ".pack");
strbuf_addstr(&buf, ".idx");
string_list_insert(include, buf.buf);
}
} else {
for_each_string_list_item(item, &existing->non_kept_packs) {
if (existing_pack_is_marked_for_deletion(item))
continue;
strbuf_reset(&buf);
strbuf_addf(&buf, "%s.idx", item->string);
string_list_insert(include, buf.buf);
}
}
if (opts->midx_must_contain_cruft ||
midx_has_unknown_packs(include, geometry, existing)) {
/*
* If there are one or more unknown pack(s) present (see
* midx_has_unknown_packs() for what makes a pack
* "unknown") in the MIDX before the repack, keep them
* as they may be required to form a reachability
* closure if the MIDX is bitmapped.
*
* For example, a cruft pack can be required to form a
* reachability closure if the MIDX is bitmapped and one
* or more of the bitmap's selected commits reaches a
* once-cruft object that was later made reachable.
*/
for_each_string_list_item(item, &existing->cruft_packs) {
/*
* When doing a --geometric repack, there is no
* need to check for deleted packs, since we're
* by definition not doing an ALL_INTO_ONE
* repack (hence no packs will be deleted).
* Otherwise we must check for and exclude any
* packs which are enqueued for deletion.
*
* So we could omit the conditional below in the
* --geometric case, but doing so is unnecessary
* since no packs are marked as pending
* deletion (since we only call
* `existing_packs_mark_for_deletion()` when
* doing an all-into-one repack).
*/
if (existing_pack_is_marked_for_deletion(item))
continue;
strbuf_reset(&buf);
strbuf_addf(&buf, "%s.idx", item->string);
string_list_insert(include, buf.buf);
}
} else {
/*
* Modern versions of Git (with the appropriate
* configuration setting) will write new copies of
* once-cruft objects when doing a --geometric repack.
*
* If the MIDX has no cruft pack, new packs written
* during a --geometric repack will not rely on the
* cruft pack to form a reachability closure, so we can
* avoid including them in the MIDX in that case.
*/
;
}
strbuf_release(&buf);
}
static void remove_redundant_bitmaps(struct string_list *include,
const char *packdir)
{
struct strbuf path = STRBUF_INIT;
struct string_list_item *item;
size_t packdir_len;
strbuf_addstr(&path, packdir);
strbuf_addch(&path, '/');
packdir_len = path.len;
/*
* Remove any pack bitmaps corresponding to packs which are now
* included in the MIDX.
*/
for_each_string_list_item(item, include) {
strbuf_addstr(&path, item->string);
strbuf_strip_suffix(&path, ".idx");
strbuf_addstr(&path, ".bitmap");
if (unlink(path.buf) && errno != ENOENT)
warning_errno(_("could not remove stale bitmap: %s"),
path.buf);
strbuf_setlen(&path, packdir_len);
}
strbuf_release(&path);
}
int write_midx_included_packs(struct repack_write_midx_opts *opts)
{
struct child_process cmd = CHILD_PROCESS_INIT;
struct string_list include = STRING_LIST_INIT_DUP;
struct string_list_item *item;
struct packed_git *preferred = pack_geometry_preferred_pack(opts->geometry);
FILE *in;
int ret = 0;
midx_included_packs(&include, opts);
if (!include.nr)
goto done;
cmd.in = -1;
cmd.git_cmd = 1;
strvec_push(&cmd.args, "multi-pack-index");
strvec_pushl(&cmd.args, "write", "--stdin-packs", NULL);
if (opts->show_progress)
strvec_push(&cmd.args, "--progress");
else
strvec_push(&cmd.args, "--no-progress");
if (opts->write_bitmaps)
strvec_push(&cmd.args, "--bitmap");
if (preferred)
strvec_pushf(&cmd.args, "--preferred-pack=%s",
pack_basename(preferred));
else if (opts->names->nr) {
/* The largest pack was repacked, meaning that either
* one or two packs exist depending on whether the
* repository has a cruft pack or not.
*
* Select the non-cruft one as preferred to encourage
* pack-reuse among packs containing reachable objects
* over unreachable ones.
*
* (Note we could write multiple packs here if
* `--max-pack-size` was given, but any one of them
* will suffice, so pick the first one.)
*/
for_each_string_list_item(item, opts->names) {
struct generated_pack *pack = item->util;
if (generated_pack_has_ext(pack, ".mtimes"))
continue;
strvec_pushf(&cmd.args, "--preferred-pack=pack-%s.pack",
item->string);
break;
}
} else {
/*
* No packs were kept, and no packs were written. The
* only thing remaining are .keep packs (unless
* --pack-kept-objects was given).
*
* Set the `--preferred-pack` arbitrarily here.
*/
;
}
if (opts->refs_snapshot)
strvec_pushf(&cmd.args, "--refs-snapshot=%s",
opts->refs_snapshot);
ret = start_command(&cmd);
if (ret)
goto done;
in = xfdopen(cmd.in, "w");
for_each_string_list_item(item, &include)
fprintf(in, "%s\n", item->string);
fclose(in);
ret = finish_command(&cmd);
done:
if (!ret && opts->write_bitmaps)
remove_redundant_bitmaps(&include, opts->packdir);
string_list_clear(&include, 0);
return ret;
}

102
repack-promisor.c Normal file
View File

@@ -0,0 +1,102 @@
#include "git-compat-util.h"
#include "repack.h"
#include "hex.h"
#include "pack.h"
#include "packfile.h"
#include "path.h"
#include "repository.h"
#include "run-command.h"
struct write_oid_context {
struct child_process *cmd;
const struct git_hash_algo *algop;
};
/*
* Write oid to the given struct child_process's stdin, starting it first if
* necessary.
*/
static int write_oid(const struct object_id *oid,
struct packed_git *pack UNUSED,
uint32_t pos UNUSED, void *data)
{
struct write_oid_context *ctx = data;
struct child_process *cmd = ctx->cmd;
if (cmd->in == -1) {
if (start_command(cmd))
die(_("could not start pack-objects to repack promisor objects"));
}
if (write_in_full(cmd->in, oid_to_hex(oid), ctx->algop->hexsz) < 0 ||
write_in_full(cmd->in, "\n", 1) < 0)
die(_("failed to feed promisor objects to pack-objects"));
return 0;
}
void repack_promisor_objects(struct repository *repo,
const struct pack_objects_args *args,
struct string_list *names, const char *packtmp)
{
struct write_oid_context ctx;
struct child_process cmd = CHILD_PROCESS_INIT;
FILE *out;
struct strbuf line = STRBUF_INIT;
prepare_pack_objects(&cmd, args, packtmp);
cmd.in = -1;
/*
* NEEDSWORK: Giving pack-objects only the OIDs without any ordering
* hints may result in suboptimal deltas in the resulting pack. See if
* the OIDs can be sent with fake paths such that pack-objects can use a
* {type -> existing pack order} ordering when computing deltas instead
* of a {type -> size} ordering, which may produce better deltas.
*/
ctx.cmd = &cmd;
ctx.algop = repo->hash_algo;
for_each_packed_object(repo, write_oid, &ctx,
FOR_EACH_OBJECT_PROMISOR_ONLY);
if (cmd.in == -1) {
/* No packed objects; cmd was never started */
child_process_clear(&cmd);
return;
}
close(cmd.in);
out = xfdopen(cmd.out, "r");
while (strbuf_getline_lf(&line, out) != EOF) {
struct string_list_item *item;
char *promisor_name;
if (line.len != repo->hash_algo->hexsz)
die(_("repack: Expecting full hex object ID lines only from pack-objects."));
item = string_list_append(names, line.buf);
/*
* pack-objects creates the .pack and .idx files, but not the
* .promisor file. Create the .promisor file, which is empty.
*
* NEEDSWORK: fetch-pack sometimes generates non-empty
* .promisor files containing the ref names and associated
* hashes at the point of generation of the corresponding
* packfile, but this would not preserve their contents. Maybe
* concatenate the contents of all .promisor files instead of
* just creating a new empty file.
*/
promisor_name = mkpathdup("%s-%s.promisor", packtmp,
line.buf);
write_promisor_file(promisor_name, NULL, 0);
item->util = generated_pack_populate(item->string, packtmp);
free(promisor_name);
}
fclose(out);
if (finish_command(&cmd))
die(_("could not finish pack-objects to repack promisor objects"));
strbuf_release(&line);
}

360
repack.c Normal file
View File

@@ -0,0 +1,360 @@
#include "git-compat-util.h"
#include "dir.h"
#include "midx.h"
#include "odb.h"
#include "packfile.h"
#include "path.h"
#include "repack.h"
#include "repository.h"
#include "run-command.h"
#include "tempfile.h"
void prepare_pack_objects(struct child_process *cmd,
const struct pack_objects_args *args,
const char *out)
{
strvec_push(&cmd->args, "pack-objects");
if (args->window)
strvec_pushf(&cmd->args, "--window=%s", args->window);
if (args->window_memory)
strvec_pushf(&cmd->args, "--window-memory=%s", args->window_memory);
if (args->depth)
strvec_pushf(&cmd->args, "--depth=%s", args->depth);
if (args->threads)
strvec_pushf(&cmd->args, "--threads=%s", args->threads);
if (args->max_pack_size)
strvec_pushf(&cmd->args, "--max-pack-size=%lu", args->max_pack_size);
if (args->no_reuse_delta)
strvec_pushf(&cmd->args, "--no-reuse-delta");
if (args->no_reuse_object)
strvec_pushf(&cmd->args, "--no-reuse-object");
if (args->name_hash_version)
strvec_pushf(&cmd->args, "--name-hash-version=%d", args->name_hash_version);
if (args->path_walk)
strvec_pushf(&cmd->args, "--path-walk");
if (args->local)
strvec_push(&cmd->args, "--local");
if (args->quiet)
strvec_push(&cmd->args, "--quiet");
if (args->delta_base_offset)
strvec_push(&cmd->args, "--delta-base-offset");
if (!args->pack_kept_objects)
strvec_push(&cmd->args, "--honor-pack-keep");
strvec_push(&cmd->args, out);
cmd->git_cmd = 1;
cmd->out = -1;
}
void pack_objects_args_release(struct pack_objects_args *args)
{
free(args->window);
free(args->window_memory);
free(args->depth);
free(args->threads);
list_objects_filter_release(&args->filter_options);
}
void repack_remove_redundant_pack(struct repository *repo, const char *dir_name,
const char *base_name)
{
struct strbuf buf = STRBUF_INIT;
struct odb_source *source = repo->objects->sources;
struct multi_pack_index *m = get_multi_pack_index(source);
strbuf_addf(&buf, "%s.pack", base_name);
if (m && source->local && midx_contains_pack(m, buf.buf))
clear_midx_file(repo);
strbuf_insertf(&buf, 0, "%s/", dir_name);
unlink_pack_path(buf.buf, 1);
strbuf_release(&buf);
}
const char *write_pack_opts_pack_prefix(const struct write_pack_opts *opts)
{
const char *pack_prefix;
if (!skip_prefix(opts->packtmp, opts->packdir, &pack_prefix))
die(_("pack prefix %s does not begin with objdir %s"),
opts->packtmp, opts->packdir);
if (*pack_prefix == '/')
pack_prefix++;
return pack_prefix;
}
bool write_pack_opts_is_local(const struct write_pack_opts *opts)
{
return starts_with(opts->destination, opts->packdir);
}
int finish_pack_objects_cmd(const struct git_hash_algo *algop,
const struct write_pack_opts *opts,
struct child_process *cmd,
struct string_list *names)
{
FILE *out;
bool local = write_pack_opts_is_local(opts);
struct strbuf line = STRBUF_INIT;
out = xfdopen(cmd->out, "r");
while (strbuf_getline_lf(&line, out) != EOF) {
struct string_list_item *item;
if (line.len != algop->hexsz)
die(_("repack: Expecting full hex object ID lines only "
"from pack-objects."));
/*
* Avoid putting packs written outside of the repository in the
* list of names.
*/
if (local) {
item = string_list_append(names, line.buf);
item->util = generated_pack_populate(line.buf,
opts->packtmp);
}
}
fclose(out);
strbuf_release(&line);
return finish_command(cmd);
}
#define DELETE_PACK 1
#define RETAIN_PACK 2
void existing_packs_collect(struct existing_packs *existing,
const struct string_list *extra_keep)
{
struct packfile_store *packs = existing->repo->objects->packfiles;
struct packed_git *p;
struct strbuf buf = STRBUF_INIT;
for (p = packfile_store_get_all_packs(packs); p; p = p->next) {
size_t i;
const char *base;
if (p->multi_pack_index)
string_list_append(&existing->midx_packs,
pack_basename(p));
if (!p->pack_local)
continue;
base = pack_basename(p);
for (i = 0; i < extra_keep->nr; i++)
if (!fspathcmp(base, extra_keep->items[i].string))
break;
strbuf_reset(&buf);
strbuf_addstr(&buf, base);
strbuf_strip_suffix(&buf, ".pack");
if ((extra_keep->nr > 0 && i < extra_keep->nr) || p->pack_keep)
string_list_append(&existing->kept_packs, buf.buf);
else if (p->is_cruft)
string_list_append(&existing->cruft_packs, buf.buf);
else
string_list_append(&existing->non_kept_packs, buf.buf);
}
string_list_sort(&existing->kept_packs);
string_list_sort(&existing->non_kept_packs);
string_list_sort(&existing->cruft_packs);
string_list_sort(&existing->midx_packs);
strbuf_release(&buf);
}
int existing_packs_has_non_kept(const struct existing_packs *existing)
{
return existing->non_kept_packs.nr || existing->cruft_packs.nr;
}
static void existing_pack_mark_for_deletion(struct string_list_item *item)
{
item->util = (void*)((uintptr_t)item->util | DELETE_PACK);
}
static void existing_pack_unmark_for_deletion(struct string_list_item *item)
{
item->util = (void*)((uintptr_t)item->util & ~DELETE_PACK);
}
int existing_pack_is_marked_for_deletion(struct string_list_item *item)
{
return (uintptr_t)item->util & DELETE_PACK;
}
static void existing_packs_mark_retained(struct string_list_item *item)
{
item->util = (void*)((uintptr_t)item->util | RETAIN_PACK);
}
static int existing_pack_is_retained(struct string_list_item *item)
{
return (uintptr_t)item->util & RETAIN_PACK;
}
static void existing_packs_mark_for_deletion_1(const struct git_hash_algo *algop,
struct string_list *names,
struct string_list *list)
{
struct string_list_item *item;
const size_t hexsz = algop->hexsz;
for_each_string_list_item(item, list) {
char *sha1;
size_t len = strlen(item->string);
if (len < hexsz)
continue;
sha1 = item->string + len - hexsz;
if (existing_pack_is_retained(item)) {
existing_pack_unmark_for_deletion(item);
} else if (!string_list_has_string(names, sha1)) {
/*
* Mark this pack for deletion, which ensures
* that this pack won't be included in a MIDX
* (if `--write-midx` was given) and that we
* will actually delete this pack (if `-d` was
* given).
*/
existing_pack_mark_for_deletion(item);
}
}
}
void existing_packs_retain_cruft(struct existing_packs *existing,
struct packed_git *cruft)
{
struct strbuf buf = STRBUF_INIT;
struct string_list_item *item;
strbuf_addstr(&buf, pack_basename(cruft));
strbuf_strip_suffix(&buf, ".pack");
item = string_list_lookup(&existing->cruft_packs, buf.buf);
if (!item)
BUG("could not find cruft pack '%s'", pack_basename(cruft));
existing_packs_mark_retained(item);
strbuf_release(&buf);
}
void existing_packs_mark_for_deletion(struct existing_packs *existing,
struct string_list *names)
{
const struct git_hash_algo *algop = existing->repo->hash_algo;
existing_packs_mark_for_deletion_1(algop, names,
&existing->non_kept_packs);
existing_packs_mark_for_deletion_1(algop, names,
&existing->cruft_packs);
}
static void remove_redundant_packs_1(struct repository *repo,
struct string_list *packs,
const char *packdir)
{
struct string_list_item *item;
for_each_string_list_item(item, packs) {
if (!existing_pack_is_marked_for_deletion(item))
continue;
repack_remove_redundant_pack(repo, packdir, item->string);
}
}
void existing_packs_remove_redundant(struct existing_packs *existing,
const char *packdir)
{
remove_redundant_packs_1(existing->repo, &existing->non_kept_packs,
packdir);
remove_redundant_packs_1(existing->repo, &existing->cruft_packs,
packdir);
}
void existing_packs_release(struct existing_packs *existing)
{
string_list_clear(&existing->kept_packs, 0);
string_list_clear(&existing->non_kept_packs, 0);
string_list_clear(&existing->cruft_packs, 0);
string_list_clear(&existing->midx_packs, 0);
}
static struct {
const char *name;
unsigned optional:1;
} exts[] = {
{".pack"},
{".rev", 1},
{".mtimes", 1},
{".bitmap", 1},
{".promisor", 1},
{".idx"},
};
struct generated_pack {
struct tempfile *tempfiles[ARRAY_SIZE(exts)];
};
struct generated_pack *generated_pack_populate(const char *name,
const char *packtmp)
{
struct stat statbuf;
struct strbuf path = STRBUF_INIT;
struct generated_pack *pack = xcalloc(1, sizeof(*pack));
size_t i;
for (i = 0; i < ARRAY_SIZE(exts); i++) {
strbuf_reset(&path);
strbuf_addf(&path, "%s-%s%s", packtmp, name, exts[i].name);
if (stat(path.buf, &statbuf))
continue;
pack->tempfiles[i] = register_tempfile(path.buf);
}
strbuf_release(&path);
return pack;
}
int generated_pack_has_ext(const struct generated_pack *pack, const char *ext)
{
size_t i;
for (i = 0; i < ARRAY_SIZE(exts); i++) {
if (strcmp(exts[i].name, ext))
continue;
return !!pack->tempfiles[i];
}
BUG("unknown pack extension: '%s'", ext);
}
void generated_pack_install(struct generated_pack *pack, const char *name,
const char *packdir, const char *packtmp)
{
size_t ext;
for (ext = 0; ext < ARRAY_SIZE(exts); ext++) {
char *fname;
fname = mkpathdup("%s/pack-%s%s", packdir, name,
exts[ext].name);
if (pack->tempfiles[ext]) {
const char *fname_old = get_tempfile_path(pack->tempfiles[ext]);
struct stat statbuffer;
if (!stat(fname_old, &statbuffer)) {
statbuffer.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
chmod(fname_old, statbuffer.st_mode);
}
if (rename_tempfile(&pack->tempfiles[ext], fname))
die_errno(_("renaming pack to '%s' failed"),
fname);
} else if (!exts[ext].optional)
die(_("pack-objects did not write a '%s' file for pack %s-%s"),
exts[ext].name, packtmp, name);
else if (unlink(fname) < 0 && errno != ENOENT)
die_errno(_("could not unlink: %s"), fname);
free(fname);
}
}

146
repack.h Normal file
View File

@@ -0,0 +1,146 @@
#ifndef REPACK_H
#define REPACK_H
#include "list-objects-filter-options.h"
#include "string-list.h"
struct pack_objects_args {
char *window;
char *window_memory;
char *depth;
char *threads;
unsigned long max_pack_size;
int no_reuse_delta;
int no_reuse_object;
int quiet;
int local;
int name_hash_version;
int path_walk;
int delta_base_offset;
int pack_kept_objects;
struct list_objects_filter_options filter_options;
};
#define PACK_OBJECTS_ARGS_INIT { \
.delta_base_offset = 1, \
.pack_kept_objects = -1, \
}
struct child_process;
void prepare_pack_objects(struct child_process *cmd,
const struct pack_objects_args *args,
const char *out);
void pack_objects_args_release(struct pack_objects_args *args);
void repack_remove_redundant_pack(struct repository *repo, const char *dir_name,
const char *base_name);
struct write_pack_opts {
struct pack_objects_args *po_args;
const char *destination;
const char *packdir;
const char *packtmp;
};
const char *write_pack_opts_pack_prefix(const struct write_pack_opts *opts);
bool write_pack_opts_is_local(const struct write_pack_opts *opts);
int finish_pack_objects_cmd(const struct git_hash_algo *algop,
const struct write_pack_opts *opts,
struct child_process *cmd,
struct string_list *names);
struct repository;
struct packed_git;
struct existing_packs {
struct repository *repo;
struct string_list kept_packs;
struct string_list non_kept_packs;
struct string_list cruft_packs;
struct string_list midx_packs;
};
#define EXISTING_PACKS_INIT { \
.kept_packs = STRING_LIST_INIT_DUP, \
.non_kept_packs = STRING_LIST_INIT_DUP, \
.cruft_packs = STRING_LIST_INIT_DUP, \
}
/*
* Adds all packs hex strings (pack-$HASH) to either packs->non_kept
* or packs->kept based on whether each pack has a corresponding
* .keep file or not. Packs without a .keep file are not to be kept
* if we are going to pack everything into one file.
*/
void existing_packs_collect(struct existing_packs *existing,
const struct string_list *extra_keep);
int existing_packs_has_non_kept(const struct existing_packs *existing);
int existing_pack_is_marked_for_deletion(struct string_list_item *item);
void existing_packs_retain_cruft(struct existing_packs *existing,
struct packed_git *cruft);
void existing_packs_mark_for_deletion(struct existing_packs *existing,
struct string_list *names);
void existing_packs_remove_redundant(struct existing_packs *existing,
const char *packdir);
void existing_packs_release(struct existing_packs *existing);
struct generated_pack;
struct generated_pack *generated_pack_populate(const char *name,
const char *packtmp);
int generated_pack_has_ext(const struct generated_pack *pack, const char *ext);
void generated_pack_install(struct generated_pack *pack, const char *name,
const char *packdir, const char *packtmp);
void repack_promisor_objects(struct repository *repo,
const struct pack_objects_args *args,
struct string_list *names, const char *packtmp);
struct pack_geometry {
struct packed_git **pack;
uint32_t pack_nr, pack_alloc;
uint32_t split;
int split_factor;
};
void pack_geometry_init(struct pack_geometry *geometry,
struct existing_packs *existing,
const struct pack_objects_args *args);
void pack_geometry_split(struct pack_geometry *geometry);
struct packed_git *pack_geometry_preferred_pack(struct pack_geometry *geometry);
void pack_geometry_remove_redundant(struct pack_geometry *geometry,
struct string_list *names,
struct existing_packs *existing,
const char *packdir);
void pack_geometry_release(struct pack_geometry *geometry);
struct tempfile;
struct repack_write_midx_opts {
struct existing_packs *existing;
struct pack_geometry *geometry;
struct string_list *names;
const char *refs_snapshot;
const char *packdir;
int show_progress;
int write_bitmaps;
int midx_must_contain_cruft;
};
void midx_snapshot_refs(struct repository *repo, struct tempfile *f);
int write_midx_included_packs(struct repack_write_midx_opts *opts);
int write_filtered_pack(const struct write_pack_opts *opts,
struct existing_packs *existing,
struct string_list *names);
int write_cruft_pack(const struct write_pack_opts *opts,
const char *cruft_expiration,
unsigned long combine_cruft_below_size,
struct string_list *names,
struct existing_packs *existing);
#endif /* REPACK_H */

View File

@@ -8,31 +8,27 @@
#include "repository.h"
#include "commit.h"
static int register_replace_ref(const char *refname,
const char *referent UNUSED,
const struct object_id *oid,
int flag UNUSED,
void *cb_data)
static int register_replace_ref(const struct reference *ref, void *cb_data)
{
struct repository *r = cb_data;
/* Get sha1 from refname */
const char *slash = strrchr(refname, '/');
const char *hash = slash ? slash + 1 : refname;
const char *slash = strrchr(ref->name, '/');
const char *hash = slash ? slash + 1 : ref->name;
struct replace_object *repl_obj = xmalloc(sizeof(*repl_obj));
if (get_oid_hex_algop(hash, &repl_obj->original.oid, r->hash_algo)) {
free(repl_obj);
warning(_("bad replace ref name: %s"), refname);
warning(_("bad replace ref name: %s"), ref->name);
return 0;
}
/* Copy sha1 from the read ref */
oidcpy(&repl_obj->replacement, oid);
oidcpy(&repl_obj->replacement, ref->oid);
/* Register new object */
if (oidmap_put(&r->objects->replace_map, repl_obj))
die(_("duplicate replace ref: %s"), refname);
die(_("duplicate replace ref: %s"), ref->name);
return 0;
}

View File

@@ -1644,19 +1644,17 @@ struct all_refs_cb {
struct worktree *wt;
};
static int handle_one_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,
int flag UNUSED,
void *cb_data)
static int handle_one_ref(const struct reference *ref, void *cb_data)
{
struct all_refs_cb *cb = cb_data;
struct object *object;
if (ref_excluded(&cb->all_revs->ref_excludes, path))
if (ref_excluded(&cb->all_revs->ref_excludes, ref->name))
return 0;
object = get_reference(cb->all_revs, path, oid, cb->all_flags);
add_rev_cmdline(cb->all_revs, object, path, REV_CMD_REF, cb->all_flags);
add_pending_object(cb->all_revs, object, path);
object = get_reference(cb->all_revs, ref->name, ref->oid, cb->all_flags);
add_rev_cmdline(cb->all_revs, object, ref->name, REV_CMD_REF, cb->all_flags);
add_pending_object(cb->all_revs, object, ref->name);
return 0;
}

View File

@@ -148,23 +148,21 @@ out:
return ret;
}
static int add_info_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,
int flag UNUSED,
void *cb_data)
static int add_info_ref(const struct reference *ref, void *cb_data)
{
struct update_info_ctx *uic = cb_data;
struct object *o = parse_object(uic->repo, oid);
struct object *o = parse_object(uic->repo, ref->oid);
if (!o)
return -1;
if (uic_printf(uic, "%s %s\n", oid_to_hex(oid), path) < 0)
if (uic_printf(uic, "%s %s\n", oid_to_hex(ref->oid), ref->name) < 0)
return -1;
if (o->type == OBJ_TAG) {
o = deref_tag(uic->repo, o, path, 0);
o = deref_tag(uic->repo, o, ref->name, 0);
if (o)
if (uic_printf(uic, "%s %s^{}\n",
oid_to_hex(&o->oid), path) < 0)
oid_to_hex(&o->oid), ref->name) < 0)
return -1;
}
return 0;

View File

@@ -626,14 +626,10 @@ static void paint_down(struct paint_info *info, const struct object_id *oid,
free(tmp);
}
static int mark_uninteresting(const char *refname UNUSED,
const char *referent UNUSED,
const struct object_id *oid,
int flags UNUSED,
void *cb_data UNUSED)
static int mark_uninteresting(const struct reference *ref, void *cb_data UNUSED)
{
struct commit *commit = lookup_commit_reference_gently(the_repository,
oid, 1);
ref->oid, 1);
if (!commit)
return 0;
commit->object.flags |= UNINTERESTING;
@@ -742,16 +738,12 @@ struct commit_array {
size_t nr, alloc;
};
static int add_ref(const char *refname UNUSED,
const char *referent UNUSED,
const struct object_id *oid,
int flags UNUSED,
void *cb_data)
static int add_ref(const struct reference *ref, void *cb_data)
{
struct commit_array *ca = cb_data;
ALLOC_GROW(ca->commits, ca->nr + 1, ca->alloc);
ca->commits[ca->nr] = lookup_commit_reference_gently(the_repository,
oid, 1);
ref->oid, 1);
if (ca->commits[ca->nr])
ca->nr++;
return 0;

View File

@@ -934,10 +934,7 @@ static void free_submodules_data(struct string_list *submodules)
string_list_clear(submodules, 1);
}
static int has_remote(const char *refname UNUSED,
const char *referent UNUSED,
const struct object_id *oid UNUSED,
int flags UNUSED, void *cb_data UNUSED)
static int has_remote(const struct reference *ref UNUSED, void *cb_data UNUSED)
{
return 1;
}
@@ -1255,13 +1252,10 @@ int push_unpushed_submodules(struct repository *r,
return ret;
}
static int append_oid_to_array(const char *ref UNUSED,
const char *referent UNUSED,
const struct object_id *oid,
int flags UNUSED, void *data)
static int append_oid_to_array(const struct reference *ref, void *data)
{
struct oid_array *array = data;
oid_array_append(array, oid);
oid_array_append(array, ref->oid);
return 0;
}

View File

@@ -1809,7 +1809,9 @@ test_expect_success "${git_for_each_ref} reports broken tags" '
bad=$(git hash-object -w -t tag bad) &&
git update-ref refs/tags/broken-tag-bad $bad &&
test_must_fail ${git_for_each_ref} --format="%(*objectname)" \
refs/tags/broken-tag-*
refs/tags/broken-tag-* &&
test_must_fail ${git_for_each_ref} --format="%(*objectname)" \
refs/tags/broken-tag-bad
'
test_expect_success 'set up tag with signature and no blank lines' '

View File

@@ -63,7 +63,7 @@ int cmd__reach(int ac, const char **av)
die("failed to resolve %s", buf.buf + 2);
orig = parse_object(r, &oid);
peeled = deref_tag_noverify(the_repository, orig);
peeled = deref_tag(the_repository, orig, NULL, 0);
if (!peeled)
die("failed to load commit for input %s resulting in oid %s",

View File

@@ -154,10 +154,9 @@ static int cmd_rename_ref(struct ref_store *refs, const char **argv)
return refs_rename_ref(refs, oldref, newref, logmsg);
}
static int each_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
int flags, void *cb_data UNUSED)
static int each_ref(const struct reference *ref, void *cb_data UNUSED)
{
printf("%s %s 0x%x\n", oid_to_hex(oid), refname, flags);
printf("%s %s 0x%x\n", oid_to_hex(ref->oid), ref->name, ref->flags);
return 0;
}

View File

@@ -238,6 +238,7 @@ integration_tests = [
't1701-racy-split-index.sh',
't1800-hook.sh',
't1900-repo.sh',
't1901-repo-structure.sh',
't2000-conflict-when-checking-files-out.sh',
't2002-checkout-cache-u.sh',
't2003-checkout-cache-mkdir.sh',

View File

@@ -428,4 +428,36 @@ do
'
done
test_expect_success 'pack-refs does not store invalid peeled tag value' '
test_when_finished rm -rf repo &&
git init repo &&
(
cd repo &&
git commit --allow-empty --message initial &&
echo garbage >blob-content &&
blob_id=$(git hash-object -w -t blob blob-content) &&
# Write an invalid tag into the object database. The tag itself
# is well-formed, but the tagged object is a blob while we
# claim that it is a commit.
cat >tag-content <<-EOF &&
object $blob_id
type commit
tag bad-tag
tagger C O Mitter <committer@example.com> 1112354055 +0200
annotated
EOF
tag_id=$(git hash-object -w -t tag tag-content) &&
git update-ref refs/tags/bad-tag "$tag_id" &&
# The packed-refs file should not contain the peeled object ID.
# If it did this would cause commands that use the peeled value
# to not notice this corrupted tag.
git pack-refs --all &&
test_grep ! "^\^" .git/packed-refs
)
'
test_done

View File

@@ -1135,4 +1135,32 @@ test_expect_success 'fetch: accessing FETCH_HEAD special ref works' '
test_cmp expect actual
'
test_expect_success 'writes do not persist peeled value for invalid tags' '
test_when_finished rm -rf repo &&
git init repo &&
(
cd repo &&
git commit --allow-empty --message initial &&
# We cannot easily verify that the peeled value is not stored
# in the tables. Instead, we test this indirectly: we create
# two tags that both point to the same object, but they claim
# different object types. If we parse both tags we notice that
# the parsed tagged object has a mismatch between the two tags
# and bail out.
#
# If we instead use the persisted peeled value we would not
# even parse the tags. As such, we would not notice the
# discrepancy either and thus listing these tags would succeed.
git tag tag-1 -m "tag 1" &&
git cat-file tag tag-1 >raw-tag &&
sed "s/^type commit$/type blob/" <raw-tag >broken-tag &&
broken_tag_id=$(git hash-object -w -t tag broken-tag) &&
git update-ref refs/tags/tag-2 $broken_tag_id &&
test_must_fail git for-each-ref --format="%(*objectname)" refs/tags/ 2>err &&
test_grep "bad tag pointer" err
)
'
test_done

129
t/t1901-repo-structure.sh Executable file
View File

@@ -0,0 +1,129 @@
#!/bin/sh
test_description='test git repo structure'
. ./test-lib.sh
test_expect_success 'empty repository' '
test_when_finished "rm -rf repo" &&
git init repo &&
(
cd repo &&
cat >expect <<-\EOF &&
| Repository structure | Value |
| -------------------- | ----- |
| * References | |
| * Count | 0 |
| * Branches | 0 |
| * Tags | 0 |
| * Remotes | 0 |
| * Others | 0 |
| | |
| * Reachable objects | |
| * Count | 0 |
| * Commits | 0 |
| * Trees | 0 |
| * Blobs | 0 |
| * Tags | 0 |
EOF
git repo structure >out 2>err &&
test_cmp expect out &&
test_line_count = 0 err
)
'
test_expect_success 'repository with references and objects' '
test_when_finished "rm -rf repo" &&
git init repo &&
(
cd repo &&
test_commit_bulk 42 &&
git tag -a foo -m bar &&
oid="$(git rev-parse HEAD)" &&
git update-ref refs/remotes/origin/foo "$oid" &&
# Also creates a commit, tree, and blob.
git notes add -m foo &&
cat >expect <<-\EOF &&
| Repository structure | Value |
| -------------------- | ----- |
| * References | |
| * Count | 4 |
| * Branches | 1 |
| * Tags | 1 |
| * Remotes | 1 |
| * Others | 1 |
| | |
| * Reachable objects | |
| * Count | 130 |
| * Commits | 43 |
| * Trees | 43 |
| * Blobs | 43 |
| * Tags | 1 |
EOF
git repo structure >out 2>err &&
test_cmp expect out &&
test_line_count = 0 err
)
'
test_expect_success 'keyvalue and nul format' '
test_when_finished "rm -rf repo" &&
git init repo &&
(
cd repo &&
test_commit_bulk 42 &&
git tag -a foo -m bar &&
cat >expect <<-\EOF &&
references.branches.count=1
references.tags.count=1
references.remotes.count=0
references.others.count=0
objects.commits.count=42
objects.trees.count=42
objects.blobs.count=42
objects.tags.count=1
EOF
git repo structure --format=keyvalue >out 2>err &&
test_cmp expect out &&
test_line_count = 0 err &&
# Replace key and value delimiters for nul format.
tr "\n=" "\0\n" <expect >expect_nul &&
git repo structure --format=nul >out 2>err &&
test_cmp expect_nul out &&
test_line_count = 0 err
)
'
test_expect_success 'progress meter option' '
test_when_finished "rm -rf repo" &&
git init repo &&
(
cd repo &&
test_commit foo &&
GIT_PROGRESS_DELAY=0 git repo structure --progress >out 2>err &&
test_file_not_empty out &&
test_grep "Counting references: 2, done." err &&
test_grep "Counting objects: 3, done." err &&
GIT_PROGRESS_DELAY=0 git repo structure --no-progress >out 2>err &&
test_file_not_empty out &&
test_line_count = 0 err
)
'
test_done

View File

@@ -333,8 +333,8 @@ test_expect_success 'different prompts for mode change/deleted' '
sed -n "s/^\(([0-9/]*) Stage .*?\).*/\1/p" actual >actual.filtered &&
cat >expect <<-\EOF &&
(1/1) Stage deletion [y,n,q,a,d,p,?]?
(1/2) Stage mode change [y,n,q,a,d,j,J,g,/,p,?]?
(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]?
(1/2) Stage mode change [y,n,q,a,d,k,K,j,J,g,/,p,?]?
(2/2) Stage this hunk [y,n,q,a,d,K,J,g,/,e,p,?]?
EOF
test_cmp expect actual.filtered
'
@@ -521,13 +521,13 @@ test_expect_success 'split hunk setup' '
test_expect_success 'goto hunk 1 with "g 1"' '
test_when_finished "git reset" &&
tr _ " " >expect <<-EOF &&
(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? + 1: -1,2 +1,3 +15
(2/2) Stage this hunk [y,n,q,a,d,K,J,g,/,e,p,?]? + 1: -1,2 +1,3 +15
_ 2: -2,4 +3,8 +21
go to which hunk? @@ -1,2 +1,3 @@
_10
+15
_20
(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_
(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,?]?_
EOF
test_write_lines s y g 1 | git add -p >actual &&
tail -n 7 <actual >actual.trimmed &&
@@ -540,7 +540,7 @@ test_expect_success 'goto hunk 1 with "g1"' '
_10
+15
_20
(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_
(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,?]?_
EOF
test_write_lines s y g1 | git add -p >actual &&
tail -n 4 <actual >actual.trimmed &&
@@ -550,11 +550,11 @@ test_expect_success 'goto hunk 1 with "g1"' '
test_expect_success 'navigate to hunk via regex /pattern' '
test_when_finished "git reset" &&
tr _ " " >expect <<-EOF &&
(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? @@ -1,2 +1,3 @@
(2/2) Stage this hunk [y,n,q,a,d,K,J,g,/,e,p,?]? @@ -1,2 +1,3 @@
_10
+15
_20
(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_
(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,?]?_
EOF
test_write_lines s y /1,2 | git add -p >actual &&
tail -n 5 <actual >actual.trimmed &&
@@ -567,7 +567,7 @@ test_expect_success 'navigate to hunk via regex / pattern' '
_10
+15
_20
(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_
(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,?]?_
EOF
test_write_lines s y / 1,2 | git add -p >actual &&
tail -n 4 <actual >actual.trimmed &&
@@ -579,11 +579,11 @@ test_expect_success 'print again the hunk' '
tr _ " " >expect <<-EOF &&
+15
20
(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? @@ -1,2 +1,3 @@
(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,?]? @@ -1,2 +1,3 @@
10
+15
20
(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_
(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,?]?_
EOF
test_write_lines s y g 1 p | git add -p >actual &&
tail -n 7 <actual >actual.trimmed &&
@@ -595,11 +595,11 @@ test_expect_success TTY 'print again the hunk (PAGER)' '
cat >expect <<-EOF &&
<GREEN>+<RESET><GREEN>15<RESET>
20<RESET>
<BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET>PAGER <CYAN>@@ -1,2 +1,3 @@<RESET>
<BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,?]? <RESET>PAGER <CYAN>@@ -1,2 +1,3 @@<RESET>
PAGER 10<RESET>
PAGER <GREEN>+<RESET><GREEN>15<RESET>
PAGER 20<RESET>
<BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET>
<BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,?]? <RESET>
EOF
test_write_lines s y g 1 P |
(
@@ -802,15 +802,15 @@ test_expect_success 'colors can be overridden' '
<BOLD>-old<RESET>
<BLUE>+<RESET><BLUE>new<RESET>
<CYAN> more-context<RESET>
<YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET><MAGENTA>@@ -3 +3,2 @@<RESET>
<YELLOW>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,?]? <RESET><MAGENTA>@@ -3 +3,2 @@<RESET>
<CYAN> more-context<RESET>
<BLUE>+<RESET><BLUE>another-one<RESET>
<YELLOW>(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? <RESET><MAGENTA>@@ -1,3 +1,3 @@<RESET>
<YELLOW>(2/2) Stage this hunk [y,n,q,a,d,K,J,g,/,e,p,?]? <RESET><MAGENTA>@@ -1,3 +1,3 @@<RESET>
<CYAN> context<RESET>
<BOLD>-old<RESET>
<BLUE>+new<RESET>
<CYAN> more-context<RESET>
<YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET>
<YELLOW>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,?]? <RESET>
EOF
test_cmp expect actual
'
@@ -1385,4 +1385,50 @@ test_expect_success 'splitting edited hunk' '
test_cmp expect actual
'
test_expect_success 'options J, K roll over' '
test_write_lines a b c d e f g h i >file &&
git add file &&
test_write_lines X b c d e f g h X >file &&
test_write_lines J J K q | git add -p >out &&
test_write_lines 1 2 1 2 >expect &&
sed -n -e "s-/.*--" -e "s/^(//p" <out >actual &&
test_cmp expect actual
'
test_expect_success 'options y, n, a, d, j, k, e roll over to next undecided (1)' '
test_write_lines a b c d e f g h i j k l m n o p q >file &&
git add file &&
test_write_lines X b c d e f g h X j k l m n o p X >file &&
test_set_editor : &&
test_write_lines g3 y g3 n g3 a g3 d g3 j g3 e k q | git add -p >out &&
test_write_lines 1 3 1 3 1 3 1 3 1 3 1 3 1 2 >expect &&
sed -n -e "s-/.*--" -e "s/^(//p" <out >actual &&
test_cmp expect actual
'
test_expect_success 'options y, n, a, d, j, k, e roll over to next undecided (2)' '
test_write_lines a b c d e f g h i j k l m n o p q >file &&
git add file &&
test_write_lines X b c d e f g h X j k l m n o p X >file &&
test_set_editor : &&
test_write_lines y g3 y g3 n g3 a g3 d g3 j g3 e g1 k q | git add -p >out &&
test_write_lines 1 2 3 2 3 2 3 2 3 2 3 2 3 2 1 2 >expect &&
sed -n -e "s-/.*--" -e "s/^(//p" <out >actual &&
test_cmp expect actual
'
test_expect_success 'invalid option s is rejected' '
test_write_lines a b c d e f g h i j k >file &&
git add file &&
test_write_lines X b X d e f g h i j X >file &&
test_write_lines j s q | git add -p >out &&
sed -ne "s/ @@.*//" -e "s/ \$//" -e "/^(/p" <out >actual &&
cat >expect <<-EOF &&
(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,s,e,p,?]?
(2/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,?]? Sorry, cannot split this hunk
(2/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,?]?
EOF
test_cmp expect actual
'
test_done

View File

@@ -339,6 +339,22 @@ test_expect_success 'diff --no-index with pathspec' '
test_cmp expect actual
'
test_expect_success 'diff --no-index first path ending in slash with pathspec' '
test_expect_code 1 git diff --name-status --no-index a/ b 1 >actual &&
cat >expect <<-EOF &&
D a/1
EOF
test_cmp expect actual
'
test_expect_success 'diff --no-index second path ending in slash with pathspec' '
test_expect_code 1 git diff --name-status --no-index a b/ 1 >actual &&
cat >expect <<-EOF &&
D a/1
EOF
test_cmp expect actual
'
test_expect_success 'diff --no-index with pathspec no matches' '
test_expect_code 0 git diff --name-status --no-index a b missing
'

View File

@@ -2293,24 +2293,26 @@ test_expect_success '--contains combined with --no-contains' '
# don't recurse down to tags for trees or blobs pointed to by *those*
# commits.
test_expect_success 'Does --[no-]contains stop at commits? Yes!' '
cd no-contains &&
blob=$(git rev-parse v0.3:v0.3.t) &&
tree=$(git rev-parse v0.3^{tree}) &&
git tag tag-blob $blob &&
git tag tag-tree $tree &&
git tag --contains v0.3 >actual &&
cat >expected <<-\EOF &&
v0.3
v0.4
v0.5
EOF
test_cmp expected actual &&
git tag --no-contains v0.3 >actual &&
cat >expected <<-\EOF &&
v0.1
v0.2
EOF
test_cmp expected actual
(
cd no-contains &&
blob=$(git rev-parse v0.3:v0.3.t) &&
tree=$(git rev-parse v0.3^{tree}) &&
git tag tag-blob $blob &&
git tag tag-tree $tree &&
git tag --contains v0.3 >actual &&
cat >expected <<-\EOF &&
v0.3
v0.4
v0.5
EOF
test_cmp expected actual &&
git tag --no-contains v0.3 >actual &&
cat >expected <<-\EOF &&
v0.1
v0.2
EOF
test_cmp expected actual
)
'
test_expect_success 'If tag is created then tag message file is unlinked' '
@@ -2332,4 +2334,24 @@ test_expect_success 'If tag cannot be created then tag message file is not unlin
test_path_exists .git/TAG_EDITMSG
'
test_expect_success 'annotated tag version sort' '
git tag -a -m "sample 1.0" vsample-1.0 &&
git tag -a -m "sample 2.0" vsample-2.0 &&
git tag -a -m "sample 10.0" vsample-10.0 &&
cat >expect <<-EOF &&
vsample-1.0
vsample-2.0
vsample-10.0
EOF
git tag --list --sort=version:tag vsample-\* >actual &&
test_cmp expect actual &&
# Ensure that we also handle this case alright in the case we have the
# peeled values cached e.g. via the packed-refs file.
git pack-refs --all &&
git tag --list --sort=version:tag vsample-\* &&
test_cmp expect actual
'
test_done

12
tag.c
View File

@@ -94,18 +94,6 @@ struct object *deref_tag(struct repository *r, struct object *o, const char *war
return o;
}
struct object *deref_tag_noverify(struct repository *r, struct object *o)
{
while (o && o->type == OBJ_TAG) {
o = parse_object(r, &o->oid);
if (o && o->type == OBJ_TAG && ((struct tag *)o)->tagged)
o = ((struct tag *)o)->tagged;
else
o = NULL;
}
return o;
}
struct tag *lookup_tag(struct repository *r, const struct object_id *oid)
{
struct object *obj = lookup_object(r, oid);

1
tag.h
View File

@@ -16,7 +16,6 @@ int parse_tag_buffer(struct repository *r, struct tag *item, const void *data, u
int parse_tag(struct tag *item);
void release_tag_memory(struct tag *t);
struct object *deref_tag(struct repository *r, struct object *, const char *, int);
struct object *deref_tag_noverify(struct repository *r, struct object *);
int gpg_verify_tag(const struct object_id *oid,
const char *name_to_report, unsigned flags);
struct object_id *get_tagged_oid(struct tag *tag);

View File

@@ -870,8 +870,8 @@ static void send_unshallow(struct upload_pack_data *data)
}
}
static int check_ref(const char *refname_full, const char *referent UNUSED, const struct object_id *oid,
int flag, void *cb_data);
static int check_ref(const struct reference *ref, void *cb_data);
static void deepen(struct upload_pack_data *data, int depth)
{
if (depth == INFINITE_DEPTH && !is_repository_shallow(the_repository)) {
@@ -1224,13 +1224,12 @@ static int mark_our_ref(const char *refname, const char *refname_full,
return 0;
}
static int check_ref(const char *refname_full, const char *referent UNUSED,const struct object_id *oid,
int flag UNUSED, void *cb_data)
static int check_ref(const struct reference *ref, void *cb_data)
{
const char *refname = strip_namespace(refname_full);
const char *refname = strip_namespace(ref->name);
struct upload_pack_data *data = cb_data;
mark_our_ref(refname, refname_full, oid, &data->hidden_refs);
mark_our_ref(refname, ref->name, ref->oid, &data->hidden_refs);
return 0;
}
@@ -1250,15 +1249,15 @@ static void format_session_id(struct strbuf *buf, struct upload_pack_data *d) {
}
static void write_v0_ref(struct upload_pack_data *data,
const char *refname, const char *refname_nons,
const struct object_id *oid)
const struct reference *ref,
const char *refname_nons)
{
static const char *capabilities = "multi_ack thin-pack side-band"
" side-band-64k ofs-delta shallow deepen-since deepen-not"
" deepen-relative no-progress include-tag multi_ack_detailed";
struct object_id peeled;
if (mark_our_ref(refname_nons, refname, oid, &data->hidden_refs))
if (mark_our_ref(refname_nons, ref->name, ref->oid, &data->hidden_refs))
return;
if (capabilities) {
@@ -1268,7 +1267,7 @@ static void write_v0_ref(struct upload_pack_data *data,
format_symref_info(&symref_info, &data->symref);
format_session_id(&session_id, data);
packet_fwrite_fmt(stdout, "%s %s%c%s%s%s%s%s%s%s object-format=%s agent=%s\n",
oid_to_hex(oid), refname_nons,
oid_to_hex(ref->oid), refname_nons,
0, capabilities,
(data->allow_uor & ALLOW_TIP_SHA1) ?
" allow-tip-sha1-in-want" : "",
@@ -1284,35 +1283,33 @@ static void write_v0_ref(struct upload_pack_data *data,
strbuf_release(&session_id);
data->sent_capabilities = 1;
} else {
packet_fwrite_fmt(stdout, "%s %s\n", oid_to_hex(oid), refname_nons);
packet_fwrite_fmt(stdout, "%s %s\n", oid_to_hex(ref->oid), refname_nons);
}
capabilities = NULL;
if (!peel_iterated_oid(the_repository, oid, &peeled))
if (!reference_get_peeled_oid(the_repository, ref, &peeled))
packet_fwrite_fmt(stdout, "%s %s^{}\n", oid_to_hex(&peeled), refname_nons);
return;
}
static int send_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
int flag UNUSED, void *cb_data)
static int send_ref(const struct reference *ref, void *cb_data)
{
write_v0_ref(cb_data, refname, strip_namespace(refname), oid);
write_v0_ref(cb_data, ref, strip_namespace(ref->name));
return 0;
}
static int find_symref(const char *refname, const char *referent UNUSED,
const struct object_id *oid UNUSED,
int flag, void *cb_data)
static int find_symref(const struct reference *ref, void *cb_data)
{
const char *symref_target;
struct string_list_item *item;
int flag;
if ((flag & REF_ISSYMREF) == 0)
if ((ref->flags & REF_ISSYMREF) == 0)
return 0;
symref_target = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
refname, 0, NULL, &flag);
ref->name, 0, NULL, &flag);
if (!symref_target || (flag & REF_ISSYMREF) == 0)
die("'%s' is a symref but it is not?", refname);
item = string_list_append(cb_data, strip_namespace(refname));
die("'%s' is a symref but it is not?", ref->name);
item = string_list_append(cb_data, strip_namespace(ref->name));
item->util = xstrdup(strip_namespace(symref_target));
return 0;
}
@@ -1445,8 +1442,12 @@ void upload_pack(const int advertise_refs, const int stateless_rpc,
send_ref, &data);
for_each_namespaced_ref_1(send_ref, &data);
if (!data.sent_capabilities) {
const char *refname = "capabilities^{}";
write_v0_ref(&data, refname, refname, null_oid(the_hash_algo));
struct reference ref = {
.name = "capabilities^{}",
.oid = null_oid(the_hash_algo),
};
write_v0_ref(&data, &ref, ref.name);
}
/*
* fflush stdout before calling advertise_shallow_grafts because send_ref

View File

@@ -226,14 +226,10 @@ static int interpret_target(struct walker *walker, char *target, struct object_i
return -1;
}
static int mark_complete(const char *path UNUSED,
const char *referent UNUSED,
const struct object_id *oid,
int flag UNUSED,
void *cb_data UNUSED)
static int mark_complete(const struct reference *ref, void *cb_data UNUSED)
{
struct commit *commit = lookup_commit_reference_gently(the_repository,
oid, 1);
ref->oid, 1);
if (commit) {
commit->object.flags |= COMPLETE;

View File

@@ -595,8 +595,15 @@ int other_head_refs(each_ref_fn fn, void *cb_data)
if (refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
refname.buf,
RESOLVE_REF_READING,
&oid, &flag))
ret = fn(refname.buf, NULL, &oid, flag, cb_data);
&oid, &flag)) {
struct reference ref = {
.name = refname.buf,
.oid = &oid,
.flags = flag,
};
ret = fn(&ref, cb_data);
}
if (ret)
break;
}