summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.mailmap1
-rw-r--r--Documentation/BreakingChanges.txt20
-rw-r--r--Documentation/CodingGuidelines19
-rw-r--r--Documentation/RelNotes/2.46.1.txt22
-rw-r--r--Documentation/RelNotes/2.47.0.txt74
-rw-r--r--Documentation/config/gc.txt3
-rw-r--r--Documentation/config/maintenance.txt11
-rw-r--r--Documentation/git-cat-file.txt6
-rw-r--r--Documentation/git-check-mailmap.txt18
-rw-r--r--Documentation/git-config.txt6
-rw-r--r--Documentation/git-for-each-ref.txt42
-rw-r--r--Documentation/git-gc.txt5
-rw-r--r--Documentation/git-send-email.txt7
-rw-r--r--Documentation/technical/api-trace2.txt17
-rw-r--r--Makefile14
-rw-r--r--archive.c10
-rw-r--r--builtin/archive.c7
-rw-r--r--builtin/bundle.c2
-rw-r--r--builtin/check-mailmap.c25
-rw-r--r--builtin/config.c2
-rw-r--r--builtin/diff-index.c4
-rw-r--r--builtin/fetch-pack.c20
-rw-r--r--builtin/fetch.c24
-rw-r--r--builtin/gc.c393
-rw-r--r--builtin/index-pack.c9
-rw-r--r--builtin/merge-tree.c13
-rw-r--r--builtin/pack-objects.c8
-rw-r--r--builtin/remote.c23
-rw-r--r--builtin/repack.c36
-rw-r--r--builtin/send-pack.c1
-rw-r--r--builtin/update-ref.c8
-rw-r--r--builtin/upload-archive.c8
-rw-r--r--bundle-uri.c4
-rw-r--r--bundle.c4
-rw-r--r--commit-reach.c126
-rw-r--r--commit-reach.h17
-rw-r--r--commit.c8
-rw-r--r--commit.h2
-rw-r--r--compat/mingw.c15
-rw-r--r--compat/mingw.h18
-rw-r--r--compat/nedmalloc/nedmalloc.c2
-rw-r--r--compat/regex/regcomp.c2
-rw-r--r--compat/stub/procinfo.c2
-rw-r--r--compat/terminal.c2
-rw-r--r--compat/win32/headless.c2
-rw-r--r--compat/win32/pthread.c2
-rw-r--r--compat/win32/pthread.h4
-rw-r--r--compat/win32/syslog.c2
-rw-r--r--compat/win32mmap.c2
-rw-r--r--compat/winansi.c2
-rw-r--r--config.c5
-rw-r--r--config.h2
-rw-r--r--config.mak.dev1
-rw-r--r--config.mak.uname1
-rw-r--r--contrib/completion/git-prompt.sh191
-rw-r--r--convert.c3
-rw-r--r--daemon.c6
-rw-r--r--diff-lib.c9
-rw-r--r--exec-cmd.c23
-rw-r--r--git-compat-util.h24
-rwxr-xr-xgit-send-email.perl41
-rw-r--r--grep.c4
-rw-r--r--imap-send.c2
-rw-r--r--mailinfo.c36
-rw-r--r--mailmap.c9
-rw-r--r--mailmap.h7
-rw-r--r--mergetools/vscode19
-rw-r--r--midx-write.c34
-rw-r--r--oss-fuzz/dummy-cmd-main.c2
-rw-r--r--pack-bitmap-write.c40
-rw-r--r--pack-bitmap.h11
-rw-r--r--pager.c2
-rw-r--r--pretty.c13
-rw-r--r--pseudo-merge.c21
-rw-r--r--pseudo-merge.h6
-rw-r--r--read-cache.c12
-rw-r--r--ref-filter.c82
-rw-r--r--ref-filter.h15
-rw-r--r--refs.c5
-rw-r--r--refs/files-backend.c15
-rw-r--r--refs/packed-backend.c4
-rw-r--r--refs/reftable-backend.c18
-rw-r--r--reftable/block_test.c123
-rw-r--r--reftable/blocksource.c26
-rw-r--r--reftable/blocksource.h2
-rw-r--r--reftable/dump.c111
-rw-r--r--reftable/generic.c229
-rw-r--r--reftable/generic.h37
-rw-r--r--reftable/iter.c129
-rw-r--r--reftable/iter.h30
-rw-r--r--reftable/merged.c72
-rw-r--r--reftable/merged.h4
-rw-r--r--reftable/reader.c219
-rw-r--r--reftable/reader.h9
-rw-r--r--reftable/record.c154
-rw-r--r--reftable/record.h1
-rw-r--r--reftable/reftable-generic.h47
-rw-r--r--reftable/reftable-merged.h26
-rw-r--r--reftable/reftable-reader.h28
-rw-r--r--reftable/reftable-record.h8
-rw-r--r--reftable/reftable-stack.h3
-rw-r--r--reftable/reftable-tests.h4
-rw-r--r--reftable/stack.c184
-rw-r--r--reftable/stack_test.c151
-rw-r--r--reftable/test_framework.c2
-rw-r--r--reftable/writer.c2
-rw-r--r--remote.c85
-rw-r--r--revision.c1
-rw-r--r--run-command.c12
-rw-r--r--scalar.c3
-rw-r--r--send-pack.c16
-rw-r--r--sequencer.c7
-rw-r--r--setup.c2
-rw-r--r--sideband.c15
-rw-r--r--t/helper/test-example-tap.c2
-rw-r--r--t/helper/test-hashmap.c2
-rw-r--r--t/helper/test-mergesort.c2
-rw-r--r--t/helper/test-reach.c2
-rw-r--r--t/helper/test-read-midx.c8
-rw-r--r--t/helper/test-reftable.c190
-rw-r--r--t/helper/test-tool.c1
-rw-r--r--t/helper/test-tool.h1
-rw-r--r--t/helper/test-urlmatch-normalization.c56
-rwxr-xr-xt/perf/p1500-graph-walks.sh31
-rwxr-xr-xt/t0110-urlmatch-normalization.sh182
-rw-r--r--t/t0110/README9
-rw-r--r--t/t0110/url-11
-rw-r--r--t/t0110/url-101
-rw-r--r--t/t0110/url-111
-rw-r--r--t/t0110/url-21
-rw-r--r--t/t0110/url-31
-rw-r--r--t/t0110/url-41
-rw-r--r--t/t0110/url-51
-rw-r--r--t/t0110/url-61
-rw-r--r--t/t0110/url-71
-rw-r--r--t/t0110/url-81
-rw-r--r--t/t0110/url-91
-rwxr-xr-xt/t1092-sparse-checkout-compatibility.sh15
-rwxr-xr-xt/t3400-rebase.sh6
-rwxr-xr-xt/t4150-am.sh1
-rwxr-xr-xt/t4203-mailmap.sh42
-rwxr-xr-xt/t4205-log-pretty-formats.sh2
-rwxr-xr-xt/t4301-merge-tree-write-tree.sh1
-rwxr-xr-xt/t5000-tar-tree.sh1
-rwxr-xr-xt/t5003-archive-zip.sh1
-rwxr-xr-xt/t5100-mailinfo.sh1
-rwxr-xr-xt/t5300-pack-object.sh39
-rwxr-xr-xt/t5304-prune.sh1
-rwxr-xr-xt/t5319-multi-pack-index.sh2
-rwxr-xr-xt/t5333-pseudo-merge-bitmaps.sh56
-rwxr-xr-xt/t5400-send-pack.sh1
-rwxr-xr-xt/t5401-update-hooks.sh2
-rwxr-xr-xt/t5408-send-pack-stdin.sh2
-rwxr-xr-xt/t5409-colorize-remote-messages.sh1
-rwxr-xr-xt/t5501-fetch-push-alternates.sh1
-rwxr-xr-xt/t5505-remote.sh1
-rwxr-xr-xt/t5510-fetch.sh1
-rwxr-xr-xt/t5519-push-alternates.sh1
-rwxr-xr-xt/t5536-fetch-conflicts.sh1
-rwxr-xr-xt/t5548-push-porcelain.sh1
-rwxr-xr-xt/t5553-set-upstream.sh1
-rwxr-xr-xt/t5574-fetch-output.sh1
-rwxr-xr-xt/t5616-partial-clone.sh6
-rwxr-xr-xt/t5703-upload-pack-ref-in-want.sh1
-rwxr-xr-xt/t5812-proto-disable-http.sh2
-rwxr-xr-xt/t6050-replace.sh1
-rwxr-xr-xt/t6300-for-each-ref.sh9
-rwxr-xr-xt/t6500-gc.sh45
-rwxr-xr-xt/t6600-test-reach.sh121
-rwxr-xr-xt/t7704-repack-cruft.sh1
-rwxr-xr-xt/t7900-maintenance.sh101
-rwxr-xr-xt/t9001-send-email.sh240
-rw-r--r--t/unit-tests/t-ctype.c2
-rw-r--r--t/unit-tests/t-hash.c2
-rw-r--r--t/unit-tests/t-hashmap.c4
-rw-r--r--t/unit-tests/t-mem-pool.c2
-rw-r--r--t/unit-tests/t-prio-queue.c2
-rw-r--r--t/unit-tests/t-reftable-basics.c2
-rw-r--r--t/unit-tests/t-reftable-block.c371
-rw-r--r--t/unit-tests/t-reftable-merged.c31
-rw-r--r--t/unit-tests/t-reftable-pq.c2
-rw-r--r--t/unit-tests/t-reftable-readwrite.c94
-rw-r--r--t/unit-tests/t-reftable-record.c2
-rw-r--r--t/unit-tests/t-reftable-tree.c2
-rw-r--r--t/unit-tests/t-strbuf.c2
-rw-r--r--t/unit-tests/t-strcmp-offset.c2
-rw-r--r--t/unit-tests/t-strvec.c2
-rw-r--r--t/unit-tests/t-trailer.c2
-rw-r--r--t/unit-tests/t-urlmatch-normalization.c271
-rw-r--r--trace2/tr2_tgt_event.c22
-rw-r--r--transport.c8
191 files changed, 3567 insertions, 1943 deletions
diff --git a/.mailmap b/.mailmap
index 18128a1250..96c2740fbb 100644
--- a/.mailmap
+++ b/.mailmap
@@ -257,6 +257,7 @@ Stefan Naewe <stefan.naewe@gmail.com> <stefan.naewe@googlemail.com>
Stefan Sperling <stsp@elego.de> <stsp@stsp.name>
Štěpán Němec <stepnem@gmail.com> <stepan.nemec@gmail.com>
Stephen Boyd <bebarino@gmail.com> <sboyd@codeaurora.org>
+Stephen P. Smith <ishchis2@gmail.com> <ischis2@cox.net>
Steven Drake <sdrake@xnet.co.nz> <sdrake@ihug.co.nz>
Steven Grimm <koreth@midwinter.com> <sgrimm@sgrimm-mbp.local>
Steven Grimm <koreth@midwinter.com> koreth@midwinter.com
diff --git a/Documentation/BreakingChanges.txt b/Documentation/BreakingChanges.txt
index 0532bfcf7f..2b64665694 100644
--- a/Documentation/BreakingChanges.txt
+++ b/Documentation/BreakingChanges.txt
@@ -115,6 +115,26 @@ info/grafts as outdated, 2014-03-05) and will be removed.
+
Cf. <20140304174806.GA11561@sigill.intra.peff.net>.
+* The git-pack-redundant(1) command can be used to remove redundant pack files.
+ The subcommand is unusably slow and the reason why nobody reports it as a
+ performance bug is suspected to be the absense of users. We have nominated
+ the command for removal and have started to emit a user-visible warning in
+ c3b58472be (pack-redundant: gauge the usage before proposing its removal,
+ 2020-08-25) whenever the command is executed.
++
+So far there was a single complaint about somebody still using the command, but
+that complaint did not cause us to reverse course. On the contrary, we have
+doubled down on the deprecation and starting with 4406522b76 (pack-redundant:
+escalate deprecation warning to an error, 2023-03-23), the command dies unless
+the user passes the `--i-still-use-this` option.
++
+There have not been any subsequent complaints, so this command will finally be
+removed.
++
+Cf. <xmqq1rjuz6n3.fsf_-_@gitster.c.googlers.com>,
+ <CAKvOHKAFXQwt4D8yUCCkf_TQL79mYaJ=KAKhtpDNTvHJFuX1NA@mail.gmail.com>,
+ <20230323204047.GA9290@coredump.intra.peff.net>,
+
== Superseded features that will not be deprecated
Some features have gained newer replacements that aim to improve the design in
diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines
index e4bd0abdcd..3263245b03 100644
--- a/Documentation/CodingGuidelines
+++ b/Documentation/CodingGuidelines
@@ -258,6 +258,14 @@ For C programs:
ensure your patch is clear of all compiler warnings we care about,
by e.g. "echo DEVELOPER=1 >>config.mak".
+ - When using DEVELOPER=1 mode, you may see warnings from the compiler
+ like "error: unused parameter 'foo' [-Werror=unused-parameter]",
+ which indicates that a function ignores its argument. If the unused
+ parameter can't be removed (e.g., because the function is used as a
+ callback and has to match a certain interface), you can annotate
+ the individual parameters with the UNUSED (or MAYBE_UNUSED)
+ keyword, like "int foo UNUSED".
+
- We try to support a wide range of C compilers to compile Git with,
including old ones. As of Git v2.35.0 Git requires C99 (we check
"__STDC_VERSION__"). You should not use features from a newer C
@@ -303,7 +311,9 @@ For C programs:
v12.01, 2022-03-28).
- Variables have to be declared at the beginning of the block, before
- the first statement (i.e. -Wdeclaration-after-statement).
+ the first statement (i.e. -Wdeclaration-after-statement). It is
+ encouraged to have a blank line between the end of the declarations
+ and the first statement in the block.
- NULL pointers shall be written as NULL, not as 0.
@@ -323,6 +333,13 @@ For C programs:
while( condition )
func (bar+1);
+ - A binary operator (other than ",") and ternary conditional "?:"
+ have a space on each side of the operator to separate it from its
+ operands. E.g. "A + 1", not "A+1".
+
+ - A unary operator (other than "." and "->") have no space between it
+ and its operand. E.g. "(char *)ptr", not "(char *) ptr".
+
- Do not explicitly compare an integral value with constant 0 or '\0',
or a pointer value with constant NULL. For instance, to validate that
counted array <ptr, cnt> is initialized but has no elements, write:
diff --git a/Documentation/RelNotes/2.46.1.txt b/Documentation/RelNotes/2.46.1.txt
index 52afb3556a..3b4c44305f 100644
--- a/Documentation/RelNotes/2.46.1.txt
+++ b/Documentation/RelNotes/2.46.1.txt
@@ -34,4 +34,26 @@ Fixes since Git 2.46
* Perforce tests have been updated.
+ * The credential helper to talk to OSX keychain sometimes sent
+ garbage bytes after the username, which has been corrected.
+
+ * A recent update broke "git ls-remote" used outside a repository,
+ which has been corrected.
+
+ * "git config --value=foo --fixed-value section.key newvalue" barfed
+ when the existing value in the configuration file used the
+ valueless true syntax, which has been corrected.
+
+ * "git reflog expire" failed to honor annotated tags when computing
+ reachable commits.
+
+ * A flakey test and incorrect calls to strtoX() functions have been
+ fixed.
+
+ * Follow-up on 2.45.1 regression fix.
+
+ * "git rev-list ... | git diff-tree -p --remerge-diff --stdin" should
+ behave more or less like "git log -p --remerge-diff" but instead it
+ crashed, forgetting to prepare a temporary object store needed.
+
Also contains minor documentation updates and code clean-ups.
diff --git a/Documentation/RelNotes/2.47.0.txt b/Documentation/RelNotes/2.47.0.txt
index 30bae56aa8..2aee05e09a 100644
--- a/Documentation/RelNotes/2.47.0.txt
+++ b/Documentation/RelNotes/2.47.0.txt
@@ -35,6 +35,26 @@ UI, Workflows & Features
environment variables, but now they can be configured in the user's
global and system wide configuration.
+ * "git send-email" learned "--translate-aliases" option that reads
+ addresses from the standard input and emits the result of applying
+ aliases on them to the standard output.
+
+ * 'git for-each-ref' learned a new "--format" atom to find the branch
+ that the history leading to a given commit "%(is-base:<commit>)" is
+ likely based on.
+
+ * The command line prompt support used to be littered with bash-isms,
+ which has been corrected to work with more shells.
+
+ * Support for the RUNTIME_PREFIX feature has been added to z/OS port.
+
+ * "git send-email" learned "--mailmap" option to allow rewriting the
+ recipient addresses.
+
+ * "git mergetool" learned to use VSCode as a merge backend.
+
+ * "git pack-redundant" has been marked for removal in Git 3.0.
+
Performance, Internal Implementation, Development Support etc.
--------------------------------------------------------------
@@ -85,6 +105,22 @@ Performance, Internal Implementation, Development Support etc.
object in the config subsystem has been rewritten to pass a
repository object through the callchain.
+ * Unused parameters have been either marked as UNUSED to squelch
+ -Wunused warnings or dropped from many functions..
+
+ * The code in the reftable library has been cleaned up by discarding
+ unused "generic" interface.
+
+ * The underlying machinery for "git diff-index" has long been made to
+ expand the sparse index as needed, but the command fully expanded
+ the sparse index upfront, which now has been taught not to do.
+
+ * More trace2 events at key points on push and fetch code paths have
+ been added.
+
+ * Make our codebase compilable with the -Werror=unused-parameter
+ option.
+
Fixes since v2.46
-----------------
@@ -118,11 +154,9 @@ Fixes since v2.46
* The credential helper to talk to OSX keychain sometimes sent
garbage bytes after the username, which has been corrected.
- (merge b201316835 jk/osxkeychain-username-is-nul-terminated later to maint).
* A recent update broke "git ls-remote" used outside a repository,
which has been corrected.
- (merge 9e89dcb66a ps/ls-remote-out-of-repo-fix later to maint).
* The patch parser in 'git apply' has been a bit more lenient against
unexpected mode bits, like 100664, recorded on extended header lines.
@@ -131,7 +165,6 @@ Fixes since v2.46
* "git config --value=foo --fixed-value section.key newvalue" barfed
when the existing value in the configuration file used the
valueless true syntax, which has been corrected.
- (merge 615d2de3b4 tb/config-fixed-value-with-valueless-true later to maint).
* The patch parser in "git patch-id" has been tightened to avoid
getting confused by lines that look like a patch header in the log
@@ -140,35 +173,46 @@ Fixes since v2.46
* "git reflog expire" failed to honor annotated tags when computing
reachable commits.
- (merge 5133ead528 jc/reflog-expire-lookup-commit-fix later to maint).
* A flakey test and incorrect calls to strtoX() functions have been
fixed.
- (merge ec60bb9fc4 kl/test-fixes later to maint).
* Follow-up on 2.45.1 regression fix.
- (merge ee0be850b0 jc/safe-directory later to maint).
* "git rev-list ... | git diff-tree -p --remerge-diff --stdin" should
behave more or less like "git log -p --remerge-diff" but instead it
crashed, forgetting to prepare a temporary object store needed.
- (merge a77554ea09 xx/diff-tree-remerge-diff-fix later to maint).
* "git bundle unbundle" outside a repository triggered a BUG()
unnecessarily, which has been corrected.
(merge 96a9a3e42e ps/bundle-outside-repo-fix later to maint).
+ * Maintenance tasks other than "gc" now properly go background when
+ "git maintenance" runs them.
+
+ * We created a useless pseudo-merge reachability bitmap that is about
+ 0 commits, and attempted to include commits that are not in packs,
+ which made no sense. These bugs have been corrected.
+ (merge a72dfab8b8 tb/pseudo-merge-bitmap-fixes later to maint).
+
+ * "git rebase -x --quiet" was not quiet, which was corrected.
+
+ * The code path for compacting reftable files saw some bugfixes
+ against concurrent operation.
+
+ * The code forgot to discard unnecessary in-core commit buffer data
+ for commits that "git log --skip=<number>" traversed but omitted
+ from the output, which has been corrected.
+ (merge 6bd2ae67a5 jk/free-commit-buffer-of-skipped-commits later to maint).
+
* Other code cleanup, docfix, build fix, etc.
(merge bb0498b1bb jc/how-to-maintain-updates later to maint).
- (merge 7c7516b8db jc/jl-git-no-advice-fix later to maint).
- (merge c3d034df16 jc/leakfix-hashfile later to maint).
- (merge d98d9c77e5 jc/leakfix-mailmap later to maint).
- (merge c199707496 jr/ls-files-expand-literal-doc later to maint).
- (merge e2e373ba82 ss/packed-ref-store-leakfix later to maint).
- (merge 0c4d5aa22d rs/use-decimal-width later to maint).
- (merge 67be8c4de5 jc/document-use-of-local later to maint).
- (merge 098be29f5b rs/t-example-simplify later to maint).
(merge 0d66f601a9 jc/tests-no-useless-tee later to maint).
(merge 170cdfc5a4 jc/grammo-fixes later to maint).
(merge 983555a1f2 jc/how-to-maintain-updates later to maint).
(merge e3209bd4df ps/stash-keep-untrack-empty-fix later to maint).
+ (merge 44db6f75cc jc/coding-style-c-operator-with-spaces later to maint).
+ (merge 596f4ff6ad cl/config-regexp-docfix later to maint).
+ (merge 4881328617 aa/cat-file-batch-output-doc later to maint).
+ (merge 1609470409 jc/config-doc-update later to maint).
+ (merge d4dc0efd7d rj/compat-terminal-unused-fix later to maint).
diff --git a/Documentation/config/gc.txt b/Documentation/config/gc.txt
index 664a3c2874..1d4f9470ea 100644
--- a/Documentation/config/gc.txt
+++ b/Documentation/config/gc.txt
@@ -40,7 +40,8 @@ use, it'll affect how the auto pack limit works.
gc.autoDetach::
Make `git gc --auto` return immediately and run in the background
- if the system supports it. Default is true.
+ if the system supports it. Default is true. This config variable acts
+ as a fallback in case `maintenance.autoDetach` is not set.
gc.bigPackThreshold::
If non-zero, all non-cruft packs larger than this limit are kept
diff --git a/Documentation/config/maintenance.txt b/Documentation/config/maintenance.txt
index 69a4f05153..72a9d6cf81 100644
--- a/Documentation/config/maintenance.txt
+++ b/Documentation/config/maintenance.txt
@@ -3,6 +3,17 @@ maintenance.auto::
`git maintenance run --auto` after doing their normal work. Defaults
to true.
+maintenance.autoDetach::
+ Many Git commands trigger automatic maintenance after they have
+ written data into the repository. This boolean config option
+ controls whether this automatic maintenance shall happen in the
+ foreground or whether the maintenance process shall detach and
+ continue to run in the background.
++
+If unset, the value of `gc.autoDetach` is used as a fallback. Defaults
+to true if both are unset, meaning that the maintenance process will
+detach.
+
maintenance.strategy::
This string config option provides a way to specify one of a few
recommended schedules for background maintenance. This only affects
diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt
index bd95a6c10a..d5890ae368 100644
--- a/Documentation/git-cat-file.txt
+++ b/Documentation/git-cat-file.txt
@@ -270,9 +270,9 @@ BATCH OUTPUT
------------
If `--batch` or `--batch-check` is given, `cat-file` will read objects
-from stdin, one per line, and print information about them. By default,
-the whole line is considered as an object, as if it were fed to
-linkgit:git-rev-parse[1].
+from stdin, one per line, and print information about them in the same
+order as they have been read. By default, the whole line is
+considered as an object, as if it were fed to linkgit:git-rev-parse[1].
When `--batch-command` is given, `cat-file` will read commands from stdin,
one per line, and print information based on the command given. With
diff --git a/Documentation/git-check-mailmap.txt b/Documentation/git-check-mailmap.txt
index 02f4418323..966c91c46a 100644
--- a/Documentation/git-check-mailmap.txt
+++ b/Documentation/git-check-mailmap.txt
@@ -15,10 +15,10 @@ SYNOPSIS
DESCRIPTION
-----------
-For each ``Name $$<user@host>$$'' or ``$$<user@host>$$'' from the command-line
-or standard input (when using `--stdin`), look up the person's canonical name
-and email address (see "Mapping Authors" below). If found, print them;
-otherwise print the input as-is.
+For each ``Name $$<user@host>$$'', ``$$<user@host>$$'', or ``$$user@host$$''
+from the command-line or standard input (when using `--stdin`), look up the
+person's canonical name and email address (see "Mapping Authors" below). If
+found, print them; otherwise print the input as-is.
OPTIONS
@@ -27,6 +27,16 @@ OPTIONS
Read contacts, one per line, from the standard input after exhausting
contacts provided on the command-line.
+--mailmap-file=<file>::
+ In addition to any configured mailmap files, read the specified
+ mailmap file. Entries in this file take precedence over entries in
+ either the default mailmap file or any configured mailmap file.
+
+--mailmap-blob=<blob>::
+ Like `--mailmap-file`, but consider the value as a reference to a
+ blob in the repository. If both `--mailmap-file` and
+ `--mailmap-blob` are specified, entries in `--mailmap-file` will
+ take precedence.
OUTPUT
------
diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
index 65c645d461..7f81fbbea8 100644
--- a/Documentation/git-config.txt
+++ b/Documentation/git-config.txt
@@ -10,7 +10,7 @@ SYNOPSIS
--------
[verse]
'git config list' [<file-option>] [<display-option>] [--includes]
-'git config get' [<file-option>] [<display-option>] [--includes] [--all] [--regexp=<regexp>] [--value=<value>] [--fixed-value] [--default=<default>] <name>
+'git config get' [<file-option>] [<display-option>] [--includes] [--all] [--regexp] [--value=<value>] [--fixed-value] [--default=<default>] <name>
'git config set' [<file-option>] [--type=<type>] [--all] [--value=<value>] [--fixed-value] <name> <value>
'git config unset' [<file-option>] [--all] [--value=<value>] [--fixed-value] <name> <value>
'git config rename-section' [<file-option>] <old-name> <new-name>
@@ -130,7 +130,7 @@ OPTIONS
--all::
With `get`, return all values for a multi-valued key.
----regexp::
+--regexp::
With `get`, interpret the name as a regular expression. Regular
expression matching is currently case-sensitive and done against a
canonicalized version of the key in which section and variable names
@@ -309,7 +309,7 @@ recommended to migrate to the new syntax.
Replaced by `git config get [--value=<pattern>] <name>`.
--get-all <name> [<value-pattern>]::
- Replaced by `git config get [--value=<pattern>] --all --show-names <name>`.
+ Replaced by `git config get [--value=<pattern>] --all <name>`.
--get-regexp <name-regexp>::
Replaced by `git config get --all --show-names --regexp <name-regexp>`.
diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index c1dd12b93c..d3764401a2 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -264,6 +264,48 @@ ahead-behind:<committish>::
commits ahead and behind, respectively, when comparing the output
ref to the `<committish>` specified in the format.
+is-base:<committish>::
+ In at most one row, `(<committish>)` will appear to indicate the ref
+ that is most likely the ref used as a starting point for the branch
+ that produced `<committish>`. This choice is made using a heuristic:
+ choose the ref that minimizes the number of commits in the
+ first-parent history of `<committish>` and not in the first-parent
+ history of the ref.
++
+For example, consider the following figure of first-parent histories of
+several refs:
++
+----
+*--*--*--*--*--* refs/heads/A
+\
+ \
+ *--*--*--* refs/heads/B
+ \ \
+ \ \
+ * * refs/heads/C
+ \
+ \
+ *--* refs/heads/D
+----
++
+Here, if `A`, `B`, and `C` are the filtered references, and the format
+string is `%(refname):%(is-base:D)`, then the output would be
++
+----
+refs/heads/A:
+refs/heads/B:(D)
+refs/heads/C:
+----
++
+This is because the first-parent history of `D` has its earliest
+intersection with the first-parent histories of the filtered refs at a
+common first-parent ancestor of `B` and `C` and ties are broken by the
+earliest ref in the sorted order.
++
+Note that this token will not appear if the first-parent history of
+`<committish>` does not intersect the first-parent histories of the
+filtered refs.
+
describe[:options]::
A human-readable name, like linkgit:git-describe[1];
empty string for undescribable commits. The `describe` string may
diff --git a/Documentation/git-gc.txt b/Documentation/git-gc.txt
index b5561c458a..370e22faae 100644
--- a/Documentation/git-gc.txt
+++ b/Documentation/git-gc.txt
@@ -9,7 +9,7 @@ git-gc - Cleanup unnecessary files and optimize the local repository
SYNOPSIS
--------
[verse]
-'git gc' [--aggressive] [--auto] [--quiet] [--prune=<date> | --no-prune] [--force] [--keep-largest-pack]
+'git gc' [--aggressive] [--auto] [--[no-]detach] [--quiet] [--prune=<date> | --no-prune] [--force] [--keep-largest-pack]
DESCRIPTION
-----------
@@ -53,6 +53,9 @@ configuration options such as `gc.auto` and `gc.autoPackLimit`, all
other housekeeping tasks (e.g. rerere, working trees, reflog...) will
be performed as well.
+--[no-]detach::
+ Run in the background if the system supports it. This option overrides
+ the `gc.autoDetach` config.
--[no-]cruft::
When expiring unreachable objects, pack them separately into a
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index c5d664f451..2e6f1d63ae 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -12,6 +12,7 @@ SYNOPSIS
'git send-email' [<options>] (<file>|<directory>)...
'git send-email' [<options>] <format-patch-options>
'git send-email' --dump-aliases
+'git send-email' --translate-aliases
DESCRIPTION
@@ -475,6 +476,12 @@ Information
that this only includes the alias name and not its expanded email addresses.
See 'sendemail.aliasesFile' for more information about aliases.
+--translate-aliases::
+ Instead of the normal operation, read from standard input and
+ interpret each line as an email alias. Translate it according to the
+ configured alias file(s). Output each translated name and email
+ address to standard output, one per line. See 'sendemail.aliasFile'
+ for more information about aliases.
CONFIGURATION
-------------
diff --git a/Documentation/technical/api-trace2.txt b/Documentation/technical/api-trace2.txt
index de5fc25059..5817b18310 100644
--- a/Documentation/technical/api-trace2.txt
+++ b/Documentation/technical/api-trace2.txt
@@ -128,7 +128,7 @@ yields
------------
$ cat ~/log.event
-{"event":"version","sid":"20190408T191610.507018Z-H9b68c35f-P000059a8","thread":"main","time":"2019-01-16T17:28:42.620713Z","file":"common-main.c","line":38,"evt":"3","exe":"2.20.1.155.g426c96fcdb"}
+{"event":"version","sid":"20190408T191610.507018Z-H9b68c35f-P000059a8","thread":"main","time":"2019-01-16T17:28:42.620713Z","file":"common-main.c","line":38,"evt":"4","exe":"2.20.1.155.g426c96fcdb"}
{"event":"start","sid":"20190408T191610.507018Z-H9b68c35f-P000059a8","thread":"main","time":"2019-01-16T17:28:42.621027Z","file":"common-main.c","line":39,"t_abs":0.001173,"argv":["git","version"]}
{"event":"cmd_name","sid":"20190408T191610.507018Z-H9b68c35f-P000059a8","thread":"main","time":"2019-01-16T17:28:42.621122Z","file":"git.c","line":432,"name":"version","hierarchy":"version"}
{"event":"exit","sid":"20190408T191610.507018Z-H9b68c35f-P000059a8","thread":"main","time":"2019-01-16T17:28:42.621236Z","file":"git.c","line":662,"t_abs":0.001227,"code":0}
@@ -344,7 +344,7 @@ only present on the "start" and "atexit" events.
{
"event":"version",
...
- "evt":"3", # EVENT format version
+ "evt":"4", # EVENT format version
"exe":"2.20.1.155.g426c96fcdb" # git version
}
------------
@@ -835,6 +835,19 @@ The "value" field may be an integer or a string.
}
------------
+`"printf"`::
+ This event logs a human-readable message with no particular formatting
+ guidelines.
++
+------------
+{
+ "event":"printf",
+ ...
+ "t_abs":0.015905, # elapsed time in seconds
+ "msg":"Hello world" # optional
+}
+------------
+
== Example Trace2 API Usage
diff --git a/Makefile b/Makefile
index 8813753d99..be643ef096 100644
--- a/Makefile
+++ b/Makefile
@@ -385,6 +385,10 @@ include shared.mak
# supports calling _NSGetExecutablePath to retrieve the path of the running
# executable.
#
+# When using RUNTIME_PREFIX, define HAVE_ZOS_GET_EXECUTABLE_PATH if your platform
+# supports calling __getprogramdir and getprogname to retrieve the path of the
+# running executable.
+#
# When using RUNTIME_PREFIX, define HAVE_WPGMPTR if your platform offers
# the global variable _wpgmptr containing the absolute path of the current
# executable (this is the case on Windows).
@@ -842,7 +846,6 @@ TEST_BUILTINS_OBJS += test-submodule.o
TEST_BUILTINS_OBJS += test-subprocess.o
TEST_BUILTINS_OBJS += test-trace2.o
TEST_BUILTINS_OBJS += test-truncate.o
-TEST_BUILTINS_OBJS += test-urlmatch-normalization.o
TEST_BUILTINS_OBJS += test-userdiff.o
TEST_BUILTINS_OBJS += test-wildmatch.o
TEST_BUILTINS_OBJS += test-windows-named-pipe.o
@@ -1341,6 +1344,7 @@ UNIT_TEST_PROGRAMS += t-oidmap
UNIT_TEST_PROGRAMS += t-oidtree
UNIT_TEST_PROGRAMS += t-prio-queue
UNIT_TEST_PROGRAMS += t-reftable-basics
+UNIT_TEST_PROGRAMS += t-reftable-block
UNIT_TEST_PROGRAMS += t-reftable-merged
UNIT_TEST_PROGRAMS += t-reftable-pq
UNIT_TEST_PROGRAMS += t-reftable-readwrite
@@ -1350,6 +1354,7 @@ UNIT_TEST_PROGRAMS += t-strbuf
UNIT_TEST_PROGRAMS += t-strcmp-offset
UNIT_TEST_PROGRAMS += t-strvec
UNIT_TEST_PROGRAMS += t-trailer
+UNIT_TEST_PROGRAMS += t-urlmatch-normalization
UNIT_TEST_PROGS = $(patsubst %,$(UNIT_TEST_BIN)/%$X,$(UNIT_TEST_PROGRAMS))
UNIT_TEST_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TEST_PROGRAMS))
UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/test-lib.o
@@ -2156,6 +2161,10 @@ ifdef HAVE_NS_GET_EXECUTABLE_PATH
BASIC_CFLAGS += -DHAVE_NS_GET_EXECUTABLE_PATH
endif
+ifdef HAVE_ZOS_GET_EXECUTABLE_PATH
+ BASIC_CFLAGS += -DHAVE_ZOS_GET_EXECUTABLE_PATH
+endif
+
ifdef HAVE_WPGMPTR
BASIC_CFLAGS += -DHAVE_WPGMPTR
endif
@@ -2678,13 +2687,10 @@ REFTABLE_OBJS += reftable/merged.o
REFTABLE_OBJS += reftable/pq.o
REFTABLE_OBJS += reftable/reader.o
REFTABLE_OBJS += reftable/record.o
-REFTABLE_OBJS += reftable/generic.o
REFTABLE_OBJS += reftable/stack.o
REFTABLE_OBJS += reftable/tree.o
REFTABLE_OBJS += reftable/writer.o
-REFTABLE_TEST_OBJS += reftable/block_test.o
-REFTABLE_TEST_OBJS += reftable/dump.o
REFTABLE_TEST_OBJS += reftable/stack_test.o
REFTABLE_TEST_OBJS += reftable/test_framework.o
diff --git a/archive.c b/archive.c
index 7bd60d0632..9ba96aae4f 100644
--- a/archive.c
+++ b/archive.c
@@ -736,6 +736,7 @@ int write_archive(int argc, const char **argv, const char *prefix,
struct pretty_print_describe_status describe_status = {0};
struct pretty_print_context ctx = {0};
struct archiver_args args;
+ const char **argv_copy;
int rc;
git_config_get_bool("uploadarchive.allowunreachable", &remote_allow_unreachable);
@@ -749,6 +750,14 @@ int write_archive(int argc, const char **argv, const char *prefix,
args.repo = repo;
args.prefix = prefix;
string_list_init_dup(&args.extra_files);
+
+ /*
+ * `parse_archive_args()` modifies contents of `argv`, which is what we
+ * want. Our callers may not want it though, so we create a copy here.
+ */
+ DUP_ARRAY(argv_copy, argv, argc);
+ argv = argv_copy;
+
argc = parse_archive_args(argc, argv, &ar, &args, name_hint, remote);
if (!startup_info->have_repository) {
/*
@@ -767,6 +776,7 @@ int write_archive(int argc, const char **argv, const char *prefix,
string_list_clear_func(&args.extra_files, extra_file_info_clear);
free(args.refname);
clear_pathspec(&args.pathspec);
+ free(argv_copy);
return rc;
}
diff --git a/builtin/archive.c b/builtin/archive.c
index b50981504f..63f02990d1 100644
--- a/builtin/archive.c
+++ b/builtin/archive.c
@@ -100,13 +100,16 @@ int cmd_archive(int argc, const char **argv, const char *prefix)
if (output)
create_output_file(output);
- if (remote)
- return run_remote_archiver(argc, argv, remote, exec, output);
+ if (remote) {
+ ret = run_remote_archiver(argc, argv, remote, exec, output);
+ goto out;
+ }
setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
ret = write_archive(argc, argv, prefix, the_repository, output, 0);
+out:
free(output);
return ret;
}
diff --git a/builtin/bundle.c b/builtin/bundle.c
index 86d0ed7049..b858552ee6 100644
--- a/builtin/bundle.c
+++ b/builtin/bundle.c
@@ -221,7 +221,9 @@ static int cmd_bundle_unbundle(int argc, const char **argv, const char *prefix)
&extra_index_pack_args, 0) ||
list_bundle_refs(&header, argc, argv);
bundle_header_release(&header);
+
cleanup:
+ strvec_clear(&extra_index_pack_args);
free(bundle_file);
return ret;
}
diff --git a/builtin/check-mailmap.c b/builtin/check-mailmap.c
index b8a05b8e07..2334b57222 100644
--- a/builtin/check-mailmap.c
+++ b/builtin/check-mailmap.c
@@ -9,6 +9,7 @@
#include "write-or-die.h"
static int use_stdin;
+static const char *mailmap_file, *mailmap_blob;
static const char * const check_mailmap_usage[] = {
N_("git check-mailmap [<options>] <contact>..."),
NULL
@@ -16,6 +17,8 @@ NULL
static const struct option check_mailmap_options[] = {
OPT_BOOL(0, "stdin", &use_stdin, N_("also read contacts from stdin")),
+ OPT_FILENAME(0, "mailmap-file", &mailmap_file, N_("read additional mailmap entries from file")),
+ OPT_STRING(0, "mailmap-blob", &mailmap_blob, N_("blob"), N_("read additional mailmap entries from blob")),
OPT_END()
};
@@ -25,13 +28,17 @@ static void check_mailmap(struct string_list *mailmap, const char *contact)
size_t namelen, maillen;
struct ident_split ident;
- if (split_ident_line(&ident, contact, strlen(contact)))
- die(_("unable to parse contact: %s"), contact);
-
- name = ident.name_begin;
- namelen = ident.name_end - ident.name_begin;
- mail = ident.mail_begin;
- maillen = ident.mail_end - ident.mail_begin;
+ if (!split_ident_line(&ident, contact, strlen(contact))) {
+ name = ident.name_begin;
+ namelen = ident.name_end - ident.name_begin;
+ mail = ident.mail_begin;
+ maillen = ident.mail_end - ident.mail_begin;
+ } else {
+ name = NULL;
+ namelen = 0;
+ mail = contact;
+ maillen = strlen(contact);
+ }
map_user(mailmap, &mail, &maillen, &name, &namelen);
@@ -52,6 +59,10 @@ int cmd_check_mailmap(int argc, const char **argv, const char *prefix)
die(_("no contacts specified"));
read_mailmap(&mailmap);
+ if (mailmap_blob)
+ read_mailmap_blob(&mailmap, mailmap_blob);
+ if (mailmap_file)
+ read_mailmap_file(&mailmap, mailmap_file, 0);
for (i = 0; i < argc; ++i)
check_mailmap(&mailmap, argv[i]);
diff --git a/builtin/config.c b/builtin/config.c
index e00d983596..95c8a00915 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -17,7 +17,7 @@
static const char *const builtin_config_usage[] = {
N_("git config list [<file-option>] [<display-option>] [--includes]"),
- N_("git config get [<file-option>] [<display-option>] [--includes] [--all] [--regexp=<regexp>] [--value=<value>] [--fixed-value] [--default=<default>] <name>"),
+ N_("git config get [<file-option>] [<display-option>] [--includes] [--all] [--regexp] [--value=<value>] [--fixed-value] [--default=<default>] <name>"),
N_("git config set [<file-option>] [--type=<type>] [--all] [--value=<value>] [--fixed-value] <name> <value>"),
N_("git config unset [<file-option>] [--all] [--value=<value>] [--fixed-value] <name> <value>"),
N_("git config rename-section [<file-option>] <old-name> <new-name>"),
diff --git a/builtin/diff-index.c b/builtin/diff-index.c
index 3e05260ac0..685b60284f 100644
--- a/builtin/diff-index.c
+++ b/builtin/diff-index.c
@@ -25,6 +25,10 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
usage(diff_cache_usage);
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
+
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
+
repo_init_revisions(the_repository, &rev, prefix);
rev.abbrev = 0;
prefix = precompose_argv_prefix(argc, argv, prefix);
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index af329e8d5c..fe404d1305 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -46,7 +46,7 @@ static void add_sought_entry(struct ref ***sought, int *nr, int *alloc,
int cmd_fetch_pack(int argc, const char **argv, const char *prefix UNUSED)
{
int i, ret;
- struct ref *ref = NULL;
+ struct ref *fetched_refs = NULL, *remote_refs = NULL;
const char *dest = NULL;
struct ref **sought = NULL;
int nr_sought = 0, alloc_sought = 0;
@@ -228,19 +228,20 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix UNUSED)
version = discover_version(&reader);
switch (version) {
case protocol_v2:
- get_remote_refs(fd[1], &reader, &ref, 0, NULL, NULL,
+ get_remote_refs(fd[1], &reader, &remote_refs, 0, NULL, NULL,
args.stateless_rpc);
break;
case protocol_v1:
case protocol_v0:
- get_remote_heads(&reader, &ref, 0, NULL, &shallow);
+ get_remote_heads(&reader, &remote_refs, 0, NULL, &shallow);
break;
case protocol_unknown_version:
BUG("unknown protocol version");
}
- ref = fetch_pack(&args, fd, ref, sought, nr_sought,
+ fetched_refs = fetch_pack(&args, fd, remote_refs, sought, nr_sought,
&shallow, pack_lockfiles_ptr, version);
+
if (pack_lockfiles.nr) {
int i;
@@ -260,7 +261,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix UNUSED)
if (finish_connect(conn))
return 1;
- ret = !ref;
+ ret = !fetched_refs;
/*
* If the heads to pull were given, we should have consumed
@@ -270,11 +271,14 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix UNUSED)
*/
ret |= report_unmatched_refs(sought, nr_sought);
- while (ref) {
+ for (struct ref *ref = fetched_refs; ref; ref = ref->next)
printf("%s %s\n",
oid_to_hex(&ref->old_oid), ref->name);
- ref = ref->next;
- }
+ for (size_t i = 0; i < nr_sought; i++)
+ free_one_ref(sought[i]);
+ free(sought);
+ free_refs(fetched_refs);
+ free_refs(remote_refs);
return ret;
}
diff --git a/builtin/fetch.c b/builtin/fetch.c
index c297569a47..b2b5aee5bf 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -1731,11 +1731,8 @@ static int do_fetch(struct transport *transport,
goto cleanup;
retcode = ref_transaction_commit(transaction, &err);
- if (retcode) {
- ref_transaction_free(transaction);
- transaction = NULL;
+ if (retcode)
goto cleanup;
- }
}
commit_fetch_head(&fetch_head);
@@ -1803,8 +1800,11 @@ cleanup:
if (transaction && ref_transaction_abort(transaction, &err) &&
err.len)
error("%s", err.buf);
+ transaction = NULL;
}
+ if (transaction)
+ ref_transaction_free(transaction);
display_state_release(&display_state);
close_fetch_head(&fetch_head);
strbuf_release(&err);
@@ -2408,6 +2408,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
struct oidset_iter iter;
const struct object_id *oid;
+ trace2_region_enter("fetch", "negotiate-only", the_repository);
if (!remote)
die(_("must supply remote when using --negotiate-only"));
gtransport = prepare_transport(remote, 1);
@@ -2416,6 +2417,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
} else {
warning(_("protocol does not support --negotiate-only, exiting"));
result = 1;
+ trace2_region_leave("fetch", "negotiate-only", the_repository);
goto cleanup;
}
if (server_options.nr)
@@ -2426,11 +2428,17 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
while ((oid = oidset_iter_next(&iter)))
printf("%s\n", oid_to_hex(oid));
oidset_clear(&acked_commits);
+ trace2_region_leave("fetch", "negotiate-only", the_repository);
} else if (remote) {
- if (filter_options.choice || repo_has_promisor_remote(the_repository))
+ if (filter_options.choice || repo_has_promisor_remote(the_repository)) {
+ trace2_region_enter("fetch", "setup-partial", the_repository);
fetch_one_setup_partial(remote);
+ trace2_region_leave("fetch", "setup-partial", the_repository);
+ }
+ trace2_region_enter("fetch", "fetch-one", the_repository);
result = fetch_one(remote, argc, argv, prune_tags_ok, stdin_refspecs,
&config);
+ trace2_region_leave("fetch", "fetch-one", the_repository);
} else {
int max_children = max_jobs;
@@ -2450,7 +2458,9 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
max_children = config.parallel;
/* TODO should this also die if we have a previous partial-clone? */
+ trace2_region_enter("fetch", "fetch-multiple", the_repository);
result = fetch_multiple(&list, max_children, &config);
+ trace2_region_leave("fetch", "fetch-multiple", the_repository);
}
/*
@@ -2472,6 +2482,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
max_children = config.parallel;
add_options_to_argv(&options, &config);
+ trace2_region_enter_printf("fetch", "recurse-submodule", the_repository, "%s", submodule_prefix);
result = fetch_submodules(the_repository,
&options,
submodule_prefix,
@@ -2479,6 +2490,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
recurse_submodules_default,
verbosity < 0,
max_children);
+ trace2_region_leave_printf("fetch", "recurse-submodule", the_repository, "%s", submodule_prefix);
strvec_clear(&options);
}
@@ -2502,9 +2514,11 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
if (progress)
commit_graph_flags |= COMMIT_GRAPH_WRITE_PROGRESS;
+ trace2_region_enter("fetch", "write-commit-graph", the_repository);
write_commit_graph_reachable(the_repository->objects->odb,
commit_graph_flags,
NULL);
+ trace2_region_leave("fetch", "write-commit-graph", the_repository);
}
if (enable_auto_gc) {
diff --git a/builtin/gc.c b/builtin/gc.c
index 45f14fb09b..7dac971405 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -49,23 +49,7 @@ static const char * const builtin_gc_usage[] = {
NULL
};
-static int pack_refs = 1;
-static int prune_reflogs = 1;
-static int cruft_packs = 1;
-static unsigned long max_cruft_size;
-static int aggressive_depth = 50;
-static int aggressive_window = 250;
-static int gc_auto_threshold = 6700;
-static int gc_auto_pack_limit = 50;
-static int detach_auto = 1;
static timestamp_t gc_log_expire_time;
-static const char *gc_log_expire = "1.day.ago";
-static const char *prune_expire = "2.weeks.ago";
-static const char *prune_worktrees_expire = "3.months.ago";
-static char *repack_filter;
-static char *repack_filter_to;
-static unsigned long big_pack_threshold;
-static unsigned long max_delta_cache_size = DEFAULT_DELTA_CACHE_SIZE;
static struct strvec reflog = STRVEC_INIT;
static struct strvec repack = STRVEC_INIT;
@@ -125,13 +109,6 @@ static void process_log_file_at_exit(void)
process_log_file();
}
-static void process_log_file_on_signal(int signo)
-{
- process_log_file();
- sigchain_pop(signo);
- raise(signo);
-}
-
static int gc_config_is_timestamp_never(const char *var)
{
const char *value;
@@ -145,37 +122,100 @@ static int gc_config_is_timestamp_never(const char *var)
return 0;
}
-static void gc_config(void)
+struct gc_config {
+ int pack_refs;
+ int prune_reflogs;
+ int cruft_packs;
+ unsigned long max_cruft_size;
+ int aggressive_depth;
+ int aggressive_window;
+ int gc_auto_threshold;
+ int gc_auto_pack_limit;
+ int detach_auto;
+ char *gc_log_expire;
+ char *prune_expire;
+ char *prune_worktrees_expire;
+ char *repack_filter;
+ char *repack_filter_to;
+ unsigned long big_pack_threshold;
+ unsigned long max_delta_cache_size;
+};
+
+#define GC_CONFIG_INIT { \
+ .pack_refs = 1, \
+ .prune_reflogs = 1, \
+ .cruft_packs = 1, \
+ .aggressive_depth = 50, \
+ .aggressive_window = 250, \
+ .gc_auto_threshold = 6700, \
+ .gc_auto_pack_limit = 50, \
+ .detach_auto = 1, \
+ .gc_log_expire = xstrdup("1.day.ago"), \
+ .prune_expire = xstrdup("2.weeks.ago"), \
+ .prune_worktrees_expire = xstrdup("3.months.ago"), \
+ .max_delta_cache_size = DEFAULT_DELTA_CACHE_SIZE, \
+}
+
+static void gc_config_release(struct gc_config *cfg)
+{
+ free(cfg->gc_log_expire);
+ free(cfg->prune_expire);
+ free(cfg->prune_worktrees_expire);
+ free(cfg->repack_filter);
+ free(cfg->repack_filter_to);
+}
+
+static void gc_config(struct gc_config *cfg)
{
const char *value;
+ char *owned = NULL;
if (!git_config_get_value("gc.packrefs", &value)) {
if (value && !strcmp(value, "notbare"))
- pack_refs = -1;
+ cfg->pack_refs = -1;
else
- pack_refs = git_config_bool("gc.packrefs", value);
+ cfg->pack_refs = git_config_bool("gc.packrefs", value);
}
if (gc_config_is_timestamp_never("gc.reflogexpire") &&
gc_config_is_timestamp_never("gc.reflogexpireunreachable"))
- prune_reflogs = 0;
+ cfg->prune_reflogs = 0;
+
+ git_config_get_int("gc.aggressivewindow", &cfg->aggressive_window);
+ git_config_get_int("gc.aggressivedepth", &cfg->aggressive_depth);
+ git_config_get_int("gc.auto", &cfg->gc_auto_threshold);
+ git_config_get_int("gc.autopacklimit", &cfg->gc_auto_pack_limit);
+ git_config_get_bool("gc.autodetach", &cfg->detach_auto);
+ git_config_get_bool("gc.cruftpacks", &cfg->cruft_packs);
+ git_config_get_ulong("gc.maxcruftsize", &cfg->max_cruft_size);
+
+ if (!repo_config_get_expiry(the_repository, "gc.pruneexpire", &owned)) {
+ free(cfg->prune_expire);
+ cfg->prune_expire = owned;
+ }
- git_config_get_int("gc.aggressivewindow", &aggressive_window);
- git_config_get_int("gc.aggressivedepth", &aggressive_depth);
- git_config_get_int("gc.auto", &gc_auto_threshold);
- git_config_get_int("gc.autopacklimit", &gc_auto_pack_limit);
- git_config_get_bool("gc.autodetach", &detach_auto);
- git_config_get_bool("gc.cruftpacks", &cruft_packs);
- git_config_get_ulong("gc.maxcruftsize", &max_cruft_size);
- repo_config_get_expiry(the_repository, "gc.pruneexpire", &prune_expire);
- repo_config_get_expiry(the_repository, "gc.worktreepruneexpire", &prune_worktrees_expire);
- repo_config_get_expiry(the_repository, "gc.logexpiry", &gc_log_expire);
+ if (!repo_config_get_expiry(the_repository, "gc.worktreepruneexpire", &owned)) {
+ free(cfg->prune_worktrees_expire);
+ cfg->prune_worktrees_expire = owned;
+ }
- git_config_get_ulong("gc.bigpackthreshold", &big_pack_threshold);
- git_config_get_ulong("pack.deltacachesize", &max_delta_cache_size);
+ if (!repo_config_get_expiry(the_repository, "gc.logexpiry", &owned)) {
+ free(cfg->gc_log_expire);
+ cfg->gc_log_expire = owned;
+ }
+
+ git_config_get_ulong("gc.bigpackthreshold", &cfg->big_pack_threshold);
+ git_config_get_ulong("pack.deltacachesize", &cfg->max_delta_cache_size);
+
+ if (!git_config_get_string("gc.repackfilter", &owned)) {
+ free(cfg->repack_filter);
+ cfg->repack_filter = owned;
+ }
- git_config_get_string("gc.repackfilter", &repack_filter);
- git_config_get_string("gc.repackfilterto", &repack_filter_to);
+ if (!git_config_get_string("gc.repackfilterto", &owned)) {
+ free(cfg->repack_filter_to);
+ cfg->repack_filter_to = owned;
+ }
git_config(git_default_config, NULL);
}
@@ -202,11 +242,15 @@ static enum schedule_priority parse_schedule(const char *value)
struct maintenance_run_opts {
int auto_flag;
+ int detach;
int quiet;
enum schedule_priority schedule;
};
+#define MAINTENANCE_RUN_OPTS_INIT { \
+ .detach = -1, \
+}
-static int pack_refs_condition(void)
+static int pack_refs_condition(UNUSED struct gc_config *cfg)
{
/*
* The auto-repacking logic for refs is handled by the ref backends and
@@ -216,7 +260,8 @@ static int pack_refs_condition(void)
return 1;
}
-static int maintenance_task_pack_refs(MAYBE_UNUSED struct maintenance_run_opts *opts)
+static int maintenance_task_pack_refs(struct maintenance_run_opts *opts,
+ UNUSED struct gc_config *cfg)
{
struct child_process cmd = CHILD_PROCESS_INIT;
@@ -228,7 +273,7 @@ static int maintenance_task_pack_refs(MAYBE_UNUSED struct maintenance_run_opts *
return run_command(&cmd);
}
-static int too_many_loose_objects(void)
+static int too_many_loose_objects(struct gc_config *cfg)
{
/*
* Quickly check if a "gc" is needed, by estimating how
@@ -247,7 +292,7 @@ static int too_many_loose_objects(void)
if (!dir)
return 0;
- auto_threshold = DIV_ROUND_UP(gc_auto_threshold, 256);
+ auto_threshold = DIV_ROUND_UP(cfg->gc_auto_threshold, 256);
while ((ent = readdir(dir)) != NULL) {
if (strspn(ent->d_name, "0123456789abcdef") != hexsz_loose ||
ent->d_name[hexsz_loose] != '\0')
@@ -283,12 +328,12 @@ static struct packed_git *find_base_packs(struct string_list *packs,
return base;
}
-static int too_many_packs(void)
+static int too_many_packs(struct gc_config *cfg)
{
struct packed_git *p;
int cnt;
- if (gc_auto_pack_limit <= 0)
+ if (cfg->gc_auto_pack_limit <= 0)
return 0;
for (cnt = 0, p = get_all_packs(the_repository); p; p = p->next) {
@@ -302,7 +347,7 @@ static int too_many_packs(void)
*/
cnt++;
}
- return gc_auto_pack_limit < cnt;
+ return cfg->gc_auto_pack_limit < cnt;
}
static uint64_t total_ram(void)
@@ -336,7 +381,8 @@ static uint64_t total_ram(void)
return 0;
}
-static uint64_t estimate_repack_memory(struct packed_git *pack)
+static uint64_t estimate_repack_memory(struct gc_config *cfg,
+ struct packed_git *pack)
{
unsigned long nr_objects = repo_approximate_object_count(the_repository);
size_t os_cache, heap;
@@ -373,7 +419,7 @@ static uint64_t estimate_repack_memory(struct packed_git *pack)
*/
heap += delta_base_cache_limit;
/* and of course pack-objects has its own delta cache */
- heap += max_delta_cache_size;
+ heap += cfg->max_delta_cache_size;
return os_cache + heap;
}
@@ -384,30 +430,31 @@ static int keep_one_pack(struct string_list_item *item, void *data UNUSED)
return 0;
}
-static void add_repack_all_option(struct string_list *keep_pack)
+static void add_repack_all_option(struct gc_config *cfg,
+ struct string_list *keep_pack)
{
- if (prune_expire && !strcmp(prune_expire, "now"))
+ if (cfg->prune_expire && !strcmp(cfg->prune_expire, "now"))
strvec_push(&repack, "-a");
- else if (cruft_packs) {
+ else if (cfg->cruft_packs) {
strvec_push(&repack, "--cruft");
- if (prune_expire)
- strvec_pushf(&repack, "--cruft-expiration=%s", prune_expire);
- if (max_cruft_size)
+ if (cfg->prune_expire)
+ strvec_pushf(&repack, "--cruft-expiration=%s", cfg->prune_expire);
+ if (cfg->max_cruft_size)
strvec_pushf(&repack, "--max-cruft-size=%lu",
- max_cruft_size);
+ cfg->max_cruft_size);
} else {
strvec_push(&repack, "-A");
- if (prune_expire)
- strvec_pushf(&repack, "--unpack-unreachable=%s", prune_expire);
+ if (cfg->prune_expire)
+ strvec_pushf(&repack, "--unpack-unreachable=%s", cfg->prune_expire);
}
if (keep_pack)
for_each_string_list(keep_pack, keep_one_pack, NULL);
- if (repack_filter && *repack_filter)
- strvec_pushf(&repack, "--filter=%s", repack_filter);
- if (repack_filter_to && *repack_filter_to)
- strvec_pushf(&repack, "--filter-to=%s", repack_filter_to);
+ if (cfg->repack_filter && *cfg->repack_filter)
+ strvec_pushf(&repack, "--filter=%s", cfg->repack_filter);
+ if (cfg->repack_filter_to && *cfg->repack_filter_to)
+ strvec_pushf(&repack, "--filter-to=%s", cfg->repack_filter_to);
}
static void add_repack_incremental_option(void)
@@ -415,13 +462,13 @@ static void add_repack_incremental_option(void)
strvec_push(&repack, "--no-write-bitmap-index");
}
-static int need_to_gc(void)
+static int need_to_gc(struct gc_config *cfg)
{
/*
* Setting gc.auto to 0 or negative can disable the
* automatic gc.
*/
- if (gc_auto_threshold <= 0)
+ if (cfg->gc_auto_threshold <= 0)
return 0;
/*
@@ -430,13 +477,13 @@ static int need_to_gc(void)
* we run "repack -A -d -l". Otherwise we tell the caller
* there is no need.
*/
- if (too_many_packs()) {
+ if (too_many_packs(cfg)) {
struct string_list keep_pack = STRING_LIST_INIT_NODUP;
- if (big_pack_threshold) {
- find_base_packs(&keep_pack, big_pack_threshold);
- if (keep_pack.nr >= gc_auto_pack_limit) {
- big_pack_threshold = 0;
+ if (cfg->big_pack_threshold) {
+ find_base_packs(&keep_pack, cfg->big_pack_threshold);
+ if (keep_pack.nr >= cfg->gc_auto_pack_limit) {
+ cfg->big_pack_threshold = 0;
string_list_clear(&keep_pack, 0);
find_base_packs(&keep_pack, 0);
}
@@ -445,7 +492,7 @@ static int need_to_gc(void)
uint64_t mem_have, mem_want;
mem_have = total_ram();
- mem_want = estimate_repack_memory(p);
+ mem_want = estimate_repack_memory(cfg, p);
/*
* Only allow 1/2 of memory for pack-objects, leave
@@ -456,9 +503,9 @@ static int need_to_gc(void)
string_list_clear(&keep_pack, 0);
}
- add_repack_all_option(&keep_pack);
+ add_repack_all_option(cfg, &keep_pack);
string_list_clear(&keep_pack, 0);
- } else if (too_many_loose_objects())
+ } else if (too_many_loose_objects(cfg))
add_repack_incremental_option();
else
return 0;
@@ -585,7 +632,8 @@ done:
return ret;
}
-static void gc_before_repack(struct maintenance_run_opts *opts)
+static void gc_before_repack(struct maintenance_run_opts *opts,
+ struct gc_config *cfg)
{
/*
* We may be called twice, as both the pre- and
@@ -596,10 +644,10 @@ static void gc_before_repack(struct maintenance_run_opts *opts)
if (done++)
return;
- if (pack_refs && maintenance_task_pack_refs(opts))
+ if (cfg->pack_refs && maintenance_task_pack_refs(opts, cfg))
die(FAILED_RUN, "pack-refs");
- if (prune_reflogs) {
+ if (cfg->prune_reflogs) {
struct child_process cmd = CHILD_PROCESS_INIT;
cmd.git_cmd = 1;
@@ -620,19 +668,25 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
int keep_largest_pack = -1;
timestamp_t dummy;
struct child_process rerere_cmd = CHILD_PROCESS_INIT;
- struct maintenance_run_opts opts = {0};
+ struct maintenance_run_opts opts = MAINTENANCE_RUN_OPTS_INIT;
+ struct gc_config cfg = GC_CONFIG_INIT;
+ const char *prune_expire_sentinel = "sentinel";
+ const char *prune_expire_arg = prune_expire_sentinel;
+ int ret;
struct option builtin_gc_options[] = {
OPT__QUIET(&quiet, N_("suppress progress reporting")),
- { OPTION_STRING, 0, "prune", &prune_expire, N_("date"),
+ { OPTION_STRING, 0, "prune", &prune_expire_arg, N_("date"),
N_("prune unreferenced objects"),
- PARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire },
- OPT_BOOL(0, "cruft", &cruft_packs, N_("pack unreferenced objects separately")),
- OPT_MAGNITUDE(0, "max-cruft-size", &max_cruft_size,
+ PARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire_arg },
+ OPT_BOOL(0, "cruft", &cfg.cruft_packs, N_("pack unreferenced objects separately")),
+ OPT_MAGNITUDE(0, "max-cruft-size", &cfg.max_cruft_size,
N_("with --cruft, limit the size of new cruft packs")),
OPT_BOOL(0, "aggressive", &aggressive, N_("be more thorough (increased runtime)")),
OPT_BOOL_F(0, "auto", &opts.auto_flag, N_("enable auto-gc mode"),
PARSE_OPT_NOCOMPLETE),
+ OPT_BOOL(0, "detach", &opts.detach,
+ N_("perform garbage collection in the background")),
OPT_BOOL_F(0, "force", &force,
N_("force running gc even if there may be another gc running"),
PARSE_OPT_NOCOMPLETE),
@@ -650,84 +704,103 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
strvec_pushl(&prune_worktrees, "worktree", "prune", "--expire", NULL);
strvec_pushl(&rerere, "rerere", "gc", NULL);
- /* default expiry time, overwritten in gc_config */
- gc_config();
- if (parse_expiry_date(gc_log_expire, &gc_log_expire_time))
- die(_("failed to parse gc.logExpiry value %s"), gc_log_expire);
+ gc_config(&cfg);
- if (pack_refs < 0)
- pack_refs = !is_bare_repository();
+ if (parse_expiry_date(cfg.gc_log_expire, &gc_log_expire_time))
+ die(_("failed to parse gc.logExpiry value %s"), cfg.gc_log_expire);
+
+ if (cfg.pack_refs < 0)
+ cfg.pack_refs = !is_bare_repository();
argc = parse_options(argc, argv, prefix, builtin_gc_options,
builtin_gc_usage, 0);
if (argc > 0)
usage_with_options(builtin_gc_usage, builtin_gc_options);
- if (prune_expire && parse_expiry_date(prune_expire, &dummy))
- die(_("failed to parse prune expiry value %s"), prune_expire);
+ if (prune_expire_arg != prune_expire_sentinel) {
+ free(cfg.prune_expire);
+ cfg.prune_expire = xstrdup_or_null(prune_expire_arg);
+ }
+ if (cfg.prune_expire && parse_expiry_date(cfg.prune_expire, &dummy))
+ die(_("failed to parse prune expiry value %s"), cfg.prune_expire);
if (aggressive) {
strvec_push(&repack, "-f");
- if (aggressive_depth > 0)
- strvec_pushf(&repack, "--depth=%d", aggressive_depth);
- if (aggressive_window > 0)
- strvec_pushf(&repack, "--window=%d", aggressive_window);
+ if (cfg.aggressive_depth > 0)
+ strvec_pushf(&repack, "--depth=%d", cfg.aggressive_depth);
+ if (cfg.aggressive_window > 0)
+ strvec_pushf(&repack, "--window=%d", cfg.aggressive_window);
}
if (quiet)
strvec_push(&repack, "-q");
if (opts.auto_flag) {
+ if (cfg.detach_auto && opts.detach < 0)
+ opts.detach = 1;
+
/*
* Auto-gc should be least intrusive as possible.
*/
- if (!need_to_gc())
- return 0;
+ if (!need_to_gc(&cfg)) {
+ ret = 0;
+ goto out;
+ }
+
if (!quiet) {
- if (detach_auto)
+ if (opts.detach > 0)
fprintf(stderr, _("Auto packing the repository in background for optimum performance.\n"));
else
fprintf(stderr, _("Auto packing the repository for optimum performance.\n"));
fprintf(stderr, _("See \"git help gc\" for manual housekeeping.\n"));
}
- if (detach_auto) {
- int ret = report_last_gc_error();
-
- if (ret == 1)
- /* Last gc --auto failed. Skip this one. */
- return 0;
- else if (ret)
- /* an I/O error occurred, already reported */
- return ret;
-
- if (lock_repo_for_gc(force, &pid))
- return 0;
- gc_before_repack(&opts); /* dies on failure */
- delete_tempfile(&pidfile);
-
- /*
- * failure to daemonize is ok, we'll continue
- * in foreground
- */
- daemonized = !daemonize();
- }
} else {
struct string_list keep_pack = STRING_LIST_INIT_NODUP;
if (keep_largest_pack != -1) {
if (keep_largest_pack)
find_base_packs(&keep_pack, 0);
- } else if (big_pack_threshold) {
- find_base_packs(&keep_pack, big_pack_threshold);
+ } else if (cfg.big_pack_threshold) {
+ find_base_packs(&keep_pack, cfg.big_pack_threshold);
}
- add_repack_all_option(&keep_pack);
+ add_repack_all_option(&cfg, &keep_pack);
string_list_clear(&keep_pack, 0);
}
+ if (opts.detach > 0) {
+ ret = report_last_gc_error();
+ if (ret == 1) {
+ /* Last gc --auto failed. Skip this one. */
+ ret = 0;
+ goto out;
+
+ } else if (ret) {
+ /* an I/O error occurred, already reported */
+ goto out;
+ }
+
+ if (lock_repo_for_gc(force, &pid)) {
+ ret = 0;
+ goto out;
+ }
+
+ gc_before_repack(&opts, &cfg); /* dies on failure */
+ delete_tempfile(&pidfile);
+
+ /*
+ * failure to daemonize is ok, we'll continue
+ * in foreground
+ */
+ daemonized = !daemonize();
+ }
+
name = lock_repo_for_gc(force, &pid);
if (name) {
- if (opts.auto_flag)
- return 0; /* be quiet on --auto */
+ if (opts.auto_flag) {
+ ret = 0;
+ goto out; /* be quiet on --auto */
+ }
+
die(_("gc is already running on machine '%s' pid %"PRIuMAX" (use --force if not)"),
name, (uintmax_t)pid);
}
@@ -737,11 +810,10 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
git_path("gc.log"),
LOCK_DIE_ON_ERROR);
dup2(get_lock_file_fd(&log_lock), 2);
- sigchain_push_common(process_log_file_on_signal);
atexit(process_log_file_at_exit);
}
- gc_before_repack(&opts);
+ gc_before_repack(&opts, &cfg);
if (!repository_format_precious_objects) {
struct child_process repack_cmd = CHILD_PROCESS_INIT;
@@ -752,11 +824,11 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
if (run_command(&repack_cmd))
die(FAILED_RUN, repack.v[0]);
- if (prune_expire) {
+ if (cfg.prune_expire) {
struct child_process prune_cmd = CHILD_PROCESS_INIT;
/* run `git prune` even if using cruft packs */
- strvec_push(&prune, prune_expire);
+ strvec_push(&prune, cfg.prune_expire);
if (quiet)
strvec_push(&prune, "--no-progress");
if (repo_has_promisor_remote(the_repository))
@@ -769,10 +841,10 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
}
}
- if (prune_worktrees_expire) {
+ if (cfg.prune_worktrees_expire) {
struct child_process prune_worktrees_cmd = CHILD_PROCESS_INIT;
- strvec_push(&prune_worktrees, prune_worktrees_expire);
+ strvec_push(&prune_worktrees, cfg.prune_worktrees_expire);
prune_worktrees_cmd.git_cmd = 1;
strvec_pushv(&prune_worktrees_cmd.args, prune_worktrees.v);
if (run_command(&prune_worktrees_cmd))
@@ -796,13 +868,15 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
!quiet && !daemonized ? COMMIT_GRAPH_WRITE_PROGRESS : 0,
NULL);
- if (opts.auto_flag && too_many_loose_objects())
+ if (opts.auto_flag && too_many_loose_objects(&cfg))
warning(_("There are too many unreachable loose objects; "
"run 'git prune' to remove them."));
if (!daemonized)
unlink(git_path("gc.log"));
+out:
+ gc_config_release(&cfg);
return 0;
}
@@ -893,7 +967,7 @@ static int dfs_on_ref(const char *refname UNUSED,
return result;
}
-static int should_write_commit_graph(void)
+static int should_write_commit_graph(struct gc_config *cfg UNUSED)
{
int result;
struct cg_auto_data data;
@@ -930,7 +1004,8 @@ static int run_write_commit_graph(struct maintenance_run_opts *opts)
return !!run_command(&child);
}
-static int maintenance_task_commit_graph(struct maintenance_run_opts *opts)
+static int maintenance_task_commit_graph(struct maintenance_run_opts *opts,
+ struct gc_config *cfg UNUSED)
{
prepare_repo_settings(the_repository);
if (!the_repository->settings.core_commit_graph)
@@ -964,7 +1039,8 @@ static int fetch_remote(struct remote *remote, void *cbdata)
return !!run_command(&child);
}
-static int maintenance_task_prefetch(struct maintenance_run_opts *opts)
+static int maintenance_task_prefetch(struct maintenance_run_opts *opts,
+ struct gc_config *cfg UNUSED)
{
if (for_each_remote(fetch_remote, opts)) {
error(_("failed to prefetch remotes"));
@@ -974,7 +1050,8 @@ static int maintenance_task_prefetch(struct maintenance_run_opts *opts)
return 0;
}
-static int maintenance_task_gc(struct maintenance_run_opts *opts)
+static int maintenance_task_gc(struct maintenance_run_opts *opts,
+ struct gc_config *cfg UNUSED)
{
struct child_process child = CHILD_PROCESS_INIT;
@@ -987,6 +1064,7 @@ static int maintenance_task_gc(struct maintenance_run_opts *opts)
strvec_push(&child.args, "--quiet");
else
strvec_push(&child.args, "--no-quiet");
+ strvec_push(&child.args, "--no-detach");
return run_command(&child);
}
@@ -1022,7 +1100,7 @@ static int loose_object_count(const struct object_id *oid UNUSED,
return 0;
}
-static int loose_object_auto_condition(void)
+static int loose_object_auto_condition(struct gc_config *cfg UNUSED)
{
int count = 0;
@@ -1082,6 +1160,12 @@ static int pack_loose(struct maintenance_run_opts *opts)
pack_proc.in = -1;
+ /*
+ * git-pack-objects(1) ends up writing the pack hash to stdout, which
+ * we do not care for.
+ */
+ pack_proc.out = -1;
+
if (start_command(&pack_proc)) {
error(_("failed to start 'git pack-objects' process"));
return 1;
@@ -1107,12 +1191,13 @@ static int pack_loose(struct maintenance_run_opts *opts)
return result;
}
-static int maintenance_task_loose_objects(struct maintenance_run_opts *opts)
+static int maintenance_task_loose_objects(struct maintenance_run_opts *opts,
+ struct gc_config *cfg UNUSED)
{
return prune_packed(opts) || pack_loose(opts);
}
-static int incremental_repack_auto_condition(void)
+static int incremental_repack_auto_condition(struct gc_config *cfg UNUSED)
{
struct packed_git *p;
int incremental_repack_auto_limit = 10;
@@ -1231,7 +1316,8 @@ static int multi_pack_index_repack(struct maintenance_run_opts *opts)
return 0;
}
-static int maintenance_task_incremental_repack(struct maintenance_run_opts *opts)
+static int maintenance_task_incremental_repack(struct maintenance_run_opts *opts,
+ struct gc_config *cfg UNUSED)
{
prepare_repo_settings(the_repository);
if (!the_repository->settings.core_multi_pack_index) {
@@ -1248,14 +1334,15 @@ static int maintenance_task_incremental_repack(struct maintenance_run_opts *opts
return 0;
}
-typedef int maintenance_task_fn(struct maintenance_run_opts *opts);
+typedef int maintenance_task_fn(struct maintenance_run_opts *opts,
+ struct gc_config *cfg);
/*
* An auto condition function returns 1 if the task should run
* and 0 if the task should NOT run. See needs_to_gc() for an
* example.
*/
-typedef int maintenance_auto_fn(void);
+typedef int maintenance_auto_fn(struct gc_config *cfg);
struct maintenance_task {
const char *name;
@@ -1322,7 +1409,8 @@ static int compare_tasks_by_selection(const void *a_, const void *b_)
return b->selected_order - a->selected_order;
}
-static int maintenance_run_tasks(struct maintenance_run_opts *opts)
+static int maintenance_run_tasks(struct maintenance_run_opts *opts,
+ struct gc_config *cfg)
{
int i, found_selected = 0;
int result = 0;
@@ -1346,6 +1434,13 @@ static int maintenance_run_tasks(struct maintenance_run_opts *opts)
}
free(lock_path);
+ /* Failure to daemonize is ok, we'll continue in foreground. */
+ if (opts->detach > 0) {
+ trace2_region_enter("maintenance", "detach", the_repository);
+ daemonize();
+ trace2_region_leave("maintenance", "detach", the_repository);
+ }
+
for (i = 0; !found_selected && i < TASK__COUNT; i++)
found_selected = tasks[i].selected_order >= 0;
@@ -1361,14 +1456,14 @@ static int maintenance_run_tasks(struct maintenance_run_opts *opts)
if (opts->auto_flag &&
(!tasks[i].auto_condition ||
- !tasks[i].auto_condition()))
+ !tasks[i].auto_condition(cfg)))
continue;
if (opts->schedule && tasks[i].schedule < opts->schedule)
continue;
trace2_region_enter("maintenance", tasks[i].name, r);
- if (tasks[i].fn(opts)) {
+ if (tasks[i].fn(opts, cfg)) {
error(_("task '%s' failed"), tasks[i].name);
result = 1;
}
@@ -1405,7 +1500,6 @@ static void initialize_task_config(int schedule)
{
int i;
struct strbuf config_name = STRBUF_INIT;
- gc_config();
if (schedule)
initialize_maintenance_strategy();
@@ -1468,10 +1562,13 @@ static int task_option_parse(const struct option *opt UNUSED,
static int maintenance_run(int argc, const char **argv, const char *prefix)
{
int i;
- struct maintenance_run_opts opts;
+ struct maintenance_run_opts opts = MAINTENANCE_RUN_OPTS_INIT;
+ struct gc_config cfg = GC_CONFIG_INIT;
struct option builtin_maintenance_run_options[] = {
OPT_BOOL(0, "auto", &opts.auto_flag,
N_("run tasks based on the state of the repository")),
+ OPT_BOOL(0, "detach", &opts.detach,
+ N_("perform maintenance in the background")),
OPT_CALLBACK(0, "schedule", &opts.schedule, N_("frequency"),
N_("run tasks based on frequency"),
maintenance_opt_schedule),
@@ -1482,7 +1579,7 @@ static int maintenance_run(int argc, const char **argv, const char *prefix)
PARSE_OPT_NONEG, task_option_parse),
OPT_END()
};
- memset(&opts, 0, sizeof(opts));
+ int ret;
opts.quiet = !isatty(2);
@@ -1497,12 +1594,16 @@ static int maintenance_run(int argc, const char **argv, const char *prefix)
if (opts.auto_flag && opts.schedule)
die(_("use at most one of --auto and --schedule=<frequency>"));
+ gc_config(&cfg);
initialize_task_config(opts.schedule);
if (argc != 0)
usage_with_options(builtin_maintenance_run_usage,
builtin_maintenance_run_options);
- return maintenance_run_tasks(&opts);
+
+ ret = maintenance_run_tasks(&opts, &cfg);
+ gc_config_release(&cfg);
+ return ret;
}
static char *get_maintpath(void)
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index fd968d673d..763b01372a 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -1868,6 +1868,15 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
if (!index_name && pack_name)
index_name = derive_filename(pack_name, "pack", "idx", &index_name_buf);
+ /*
+ * Packfiles and indices do not carry enough information to be able to
+ * identify their object hash. So when we are neither in a repository
+ * nor has the user told us which object hash to use we have no other
+ * choice but to guess the object hash.
+ */
+ if (!the_repository->hash_algo)
+ repo_set_hash_algo(the_repository, GIT_HASH_SHA1);
+
opts.flags &= ~(WRITE_REV | WRITE_REV_VERIFY);
if (rev_index) {
opts.flags |= verify ? WRITE_REV_VERIFY : WRITE_REV;
diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c
index 9bca9b5f33..c00469ed3d 100644
--- a/builtin/merge-tree.c
+++ b/builtin/merge-tree.c
@@ -533,6 +533,7 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix)
int expected_remaining_argc;
int original_argc;
const char *merge_base = NULL;
+ int ret;
const char * const merge_tree_usage[] = {
N_("git merge-tree [--write-tree] [<options>] <branch1> <branch2>"),
@@ -625,7 +626,9 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix)
strbuf_list_free(split);
}
strbuf_release(&buf);
- return 0;
+
+ ret = 0;
+ goto out;
}
/* Figure out which mode to use */
@@ -664,7 +667,11 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix)
/* Do the relevant type of merge */
if (o.mode == MODE_REAL)
- return real_merge(&o, merge_base, argv[0], argv[1], prefix);
+ ret = real_merge(&o, merge_base, argv[0], argv[1], prefix);
else
- return trivial_merge(argv[0], argv[1], argv[2]);
+ ret = trivial_merge(argv[0], argv[1], argv[2]);
+
+out:
+ strvec_clear(&xopts);
+ return ret;
}
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index c481feadbf..778be80f56 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -1342,10 +1342,10 @@ static void write_pack_file(void)
if (write_bitmap_index) {
bitmap_writer_init(&bitmap_writer,
- the_repository);
+ the_repository, &to_pack);
bitmap_writer_set_checksum(&bitmap_writer, hash);
bitmap_writer_build_type_index(&bitmap_writer,
- &to_pack, written_list, nr_written);
+ written_list);
}
if (cruft)
@@ -1367,10 +1367,10 @@ static void write_pack_file(void)
bitmap_writer_select_commits(&bitmap_writer,
indexed_commits,
indexed_commits_nr);
- if (bitmap_writer_build(&bitmap_writer, &to_pack) < 0)
+ if (bitmap_writer_build(&bitmap_writer) < 0)
die(_("failed to write bitmap index"));
bitmap_writer_finish(&bitmap_writer,
- written_list, nr_written,
+ written_list,
tmpname.buf, write_bitmap_options);
bitmap_writer_free(&bitmap_writer);
write_bitmap_index = 0;
diff --git a/builtin/remote.c b/builtin/remote.c
index d1f9292ed2..0acc547d69 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -164,6 +164,7 @@ static int add(int argc, const char **argv, const char *prefix)
struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT;
const char *name, *url;
int i;
+ int result = 0;
struct option options[] = {
OPT_BOOL('f', "fetch", &fetch, N_("fetch the remote branches")),
@@ -230,8 +231,10 @@ static int add(int argc, const char **argv, const char *prefix)
fetch_tags == TAGS_SET ? "--tags" : "--no-tags");
}
- if (fetch && fetch_remote(name))
- return 1;
+ if (fetch && fetch_remote(name)) {
+ result = 1;
+ goto out;
+ }
if (master) {
strbuf_reset(&buf);
@@ -241,14 +244,15 @@ static int add(int argc, const char **argv, const char *prefix)
strbuf_addf(&buf2, "refs/remotes/%s/%s", name, master);
if (refs_update_symref(get_main_ref_store(the_repository), buf.buf, buf2.buf, "remote add"))
- return error(_("Could not setup master '%s'"), master);
+ result = error(_("Could not setup master '%s'"), master);
}
+out:
strbuf_release(&buf);
strbuf_release(&buf2);
string_list_clear(&track, 0);
- return 0;
+ return result;
}
struct branch_info {
@@ -715,6 +719,7 @@ static int mv(int argc, const char **argv, const char *prefix)
struct rename_info rename;
int i, refs_renamed_nr = 0, refspec_updated = 0;
struct progress *progress = NULL;
+ int result = 0;
argc = parse_options(argc, argv, prefix, options,
builtin_remote_rename_usage, 0);
@@ -747,9 +752,11 @@ static int mv(int argc, const char **argv, const char *prefix)
strbuf_addf(&buf, "remote.%s", rename.old_name);
strbuf_addf(&buf2, "remote.%s", rename.new_name);
- if (repo_config_rename_section(the_repository, buf.buf, buf2.buf) < 1)
- return error(_("Could not rename config section '%s' to '%s'"),
- buf.buf, buf2.buf);
+ if (repo_config_rename_section(the_repository, buf.buf, buf2.buf) < 1) {
+ result = error(_("Could not rename config section '%s' to '%s'"),
+ buf.buf, buf2.buf);
+ goto out;
+ }
if (oldremote->fetch.raw_nr) {
strbuf_reset(&buf);
@@ -870,7 +877,7 @@ out:
strbuf_release(&buf);
strbuf_release(&buf2);
strbuf_release(&buf3);
- return 0;
+ return result;
}
static int rm(int argc, const char **argv, const char *prefix)
diff --git a/builtin/repack.c b/builtin/repack.c
index 62cfa50c50..8bb875532b 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -732,14 +732,23 @@ static void midx_included_packs(struct string_list *include,
struct pack_geometry *geometry)
{
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);
+ }
- for_each_string_list_item(item, &existing->kept_packs)
- string_list_insert(include, xstrfmt("%s.idx", item->string));
- for_each_string_list_item(item, names)
- string_list_insert(include, xstrfmt("pack-%s.idx", item->string));
if (geometry->split_factor) {
- struct strbuf buf = STRBUF_INIT;
uint32_t i;
+
for (i = geometry->split; i < geometry->pack_nr; i++) {
struct packed_git *p = geometry->pack[i];
@@ -754,17 +763,21 @@ static void midx_included_packs(struct string_list *include,
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, strbuf_detach(&buf, NULL));
+ string_list_insert(include, buf.buf);
}
} else {
for_each_string_list_item(item, &existing->non_kept_packs) {
if (pack_is_marked_for_deletion(item))
continue;
- string_list_insert(include, xstrfmt("%s.idx", item->string));
+
+ strbuf_reset(&buf);
+ strbuf_addf(&buf, "%s.idx", item->string);
+ string_list_insert(include, buf.buf);
}
}
@@ -784,8 +797,13 @@ static void midx_included_packs(struct string_list *include,
*/
if (pack_is_marked_for_deletion(item))
continue;
- string_list_insert(include, xstrfmt("%s.idx", item->string));
+
+ strbuf_reset(&buf);
+ strbuf_addf(&buf, "%s.idx", item->string);
+ string_list_insert(include, buf.buf);
}
+
+ strbuf_release(&buf);
}
static int write_midx_included_packs(struct string_list *include,
@@ -1476,7 +1494,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
mark_packs_for_deletion(&existing, &names);
if (write_midx) {
- struct string_list include = STRING_LIST_INIT_NODUP;
+ struct string_list include = STRING_LIST_INIT_DUP;
midx_included_packs(&include, &existing, &names, &geometry);
ret = write_midx_included_packs(&include, &geometry, &names,
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index 17cae6bbbd..ef0df80824 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -338,5 +338,6 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
free_refs(remote_refs);
free_refs(local_refs);
+ refspec_clear(&rs);
return ret;
}
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 6a6a2ff55d..8f31da9a4b 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -274,7 +274,7 @@ static void parse_cmd_update(struct ref_transaction *transaction,
}
static void parse_cmd_symref_update(struct ref_transaction *transaction,
- const char *next, const char *end)
+ const char *next, const char *end UNUSED)
{
char *refname, *new_target, *old_arg;
char *old_target = NULL;
@@ -360,7 +360,7 @@ static void parse_cmd_create(struct ref_transaction *transaction,
static void parse_cmd_symref_create(struct ref_transaction *transaction,
- const char *next, const char *end)
+ const char *next, const char *end UNUSED)
{
struct strbuf err = STRBUF_INIT;
char *refname, *new_target;
@@ -423,7 +423,7 @@ static void parse_cmd_delete(struct ref_transaction *transaction,
static void parse_cmd_symref_delete(struct ref_transaction *transaction,
- const char *next, const char *end)
+ const char *next, const char *end UNUSED)
{
struct strbuf err = STRBUF_INIT;
char *refname, *old_target;
@@ -479,7 +479,7 @@ static void parse_cmd_verify(struct ref_transaction *transaction,
}
static void parse_cmd_symref_verify(struct ref_transaction *transaction,
- const char *next, const char *end)
+ const char *next, const char *end UNUSED)
{
struct strbuf err = STRBUF_INIT;
struct object_id old_oid;
diff --git a/builtin/upload-archive.c b/builtin/upload-archive.c
index 1b09e5e1aa..313a8dfa81 100644
--- a/builtin/upload-archive.c
+++ b/builtin/upload-archive.c
@@ -22,6 +22,7 @@ int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix)
{
struct strvec sent_argv = STRVEC_INIT;
const char *arg_cmd = "argument ";
+ int ret;
if (argc != 2 || !strcmp(argv[1], "-h"))
usage(upload_archive_usage);
@@ -46,8 +47,11 @@ int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix)
}
/* parse all options sent by the client */
- return write_archive(sent_argv.nr, sent_argv.v, prefix,
- the_repository, NULL, 1);
+ ret = write_archive(sent_argv.nr, sent_argv.v, prefix,
+ the_repository, NULL, 1);
+
+ strvec_clear(&sent_argv);
+ return ret;
}
__attribute__((format (printf, 1, 2)))
diff --git a/bundle-uri.c b/bundle-uri.c
index 1e0ee156ba..dc0c96955b 100644
--- a/bundle-uri.c
+++ b/bundle-uri.c
@@ -13,6 +13,7 @@
#include "config.h"
#include "fetch-pack.h"
#include "remote.h"
+#include "trace2.h"
static struct {
enum bundle_list_heuristic heuristic;
@@ -799,6 +800,8 @@ int fetch_bundle_uri(struct repository *r, const char *uri,
.id = xstrdup(""),
};
+ trace2_region_enter("fetch", "fetch-bundle-uri", the_repository);
+
init_bundle_list(&list);
/*
@@ -824,6 +827,7 @@ cleanup:
for_all_bundles_in_list(&list, unlink_bundle, NULL);
clear_bundle_list(&list);
clear_remote_bundle_info(&bundle, NULL);
+ trace2_region_leave("fetch", "fetch-bundle-uri", the_repository);
return result;
}
diff --git a/bundle.c b/bundle.c
index b0a8a925cb..4773b51eb1 100644
--- a/bundle.c
+++ b/bundle.c
@@ -644,10 +644,8 @@ int unbundle(struct repository *r, struct bundle_header *header,
if (flags & VERIFY_BUNDLE_FSCK)
strvec_push(&ip.args, "--fsck-objects");
- if (extra_index_pack_args) {
+ if (extra_index_pack_args)
strvec_pushv(&ip.args, extra_index_pack_args->v);
- strvec_clear(extra_index_pack_args);
- }
ip.in = bundle_fd;
ip.no_stdout = 1;
diff --git a/commit-reach.c b/commit-reach.c
index 02f8218b8e..c3518aa360 100644
--- a/commit-reach.c
+++ b/commit-reach.c
@@ -1229,3 +1229,129 @@ done:
repo_clear_commit_marks(r, SEEN);
free_commit_list(stack);
}
+
+/*
+ * This slab initializes integers to zero, so use "-1" for "tip is best" and
+ * "i + 1" for "bases[i] is best".
+ */
+define_commit_slab(best_branch_base, int);
+static struct best_branch_base best_branch_base;
+#define get_best(c) (*best_branch_base_at(&best_branch_base, (c)))
+#define set_best(c,v) (*best_branch_base_at(&best_branch_base, (c)) = (v))
+
+int get_branch_base_for_tip(struct repository *r,
+ struct commit *tip,
+ struct commit **bases,
+ size_t bases_nr)
+{
+ int best_index = -1;
+ struct commit *branch_point = NULL;
+ struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
+ int found_missing_gen = 0;
+
+ if (!bases_nr)
+ return -1;
+
+ repo_parse_commit(r, tip);
+ if (commit_graph_generation(tip) == GENERATION_NUMBER_INFINITY)
+ found_missing_gen = 1;
+
+ /* Check for missing generation numbers. */
+ for (size_t i = 0; i < bases_nr; i++) {
+ struct commit *c = bases[i];
+ repo_parse_commit(r, c);
+ if (commit_graph_generation(c) == GENERATION_NUMBER_INFINITY)
+ found_missing_gen = 1;
+ }
+
+ if (found_missing_gen) {
+ struct commit **commits;
+ size_t commits_nr = bases_nr + 1;
+
+ CALLOC_ARRAY(commits, commits_nr);
+ COPY_ARRAY(commits, bases, bases_nr);
+ commits[bases_nr] = tip;
+ ensure_generations_valid(r, commits, commits_nr);
+ free(commits);
+ }
+
+ /* Initialize queue and slab now that generations are guaranteed. */
+ init_best_branch_base(&best_branch_base);
+ set_best(tip, -1);
+ prio_queue_put(&queue, tip);
+
+ for (size_t i = 0; i < bases_nr; i++) {
+ struct commit *c = bases[i];
+ int best = get_best(c);
+
+ /* Has this already been marked as best by another commit? */
+ if (best) {
+ if (best == -1) {
+ /* We agree at this position. Stop now. */
+ best_index = i + 1;
+ goto cleanup;
+ }
+ continue;
+ }
+
+ set_best(c, i + 1);
+ prio_queue_put(&queue, c);
+ }
+
+ while (queue.nr) {
+ struct commit *c = prio_queue_get(&queue);
+ int best_for_c = get_best(c);
+ int best_for_p, positive;
+ struct commit *parent;
+
+ /* Have we reached a known branch point? It's optimal. */
+ if (c == branch_point)
+ break;
+
+ repo_parse_commit(r, c);
+ if (!c->parents)
+ continue;
+
+ parent = c->parents->item;
+ repo_parse_commit(r, parent);
+ best_for_p = get_best(parent);
+
+ if (!best_for_p) {
+ /* 'parent' is new, so pass along best_for_c. */
+ set_best(parent, best_for_c);
+ prio_queue_put(&queue, parent);
+ continue;
+ }
+
+ if (best_for_p > 0 && best_for_c > 0) {
+ /* Collision among bases. Minimize. */
+ if (best_for_c < best_for_p)
+ set_best(parent, best_for_c);
+ continue;
+ }
+
+ /*
+ * At this point, we have reached a commit that is reachable
+ * from the tip, either from 'c' or from an earlier commit to
+ * have 'parent' as its first parent.
+ *
+ * Update 'best_index' to match the minimum of all base indices
+ * to reach 'parent'.
+ */
+
+ /* Exactly one is positive due to initial conditions. */
+ positive = (best_for_c < 0) ? best_for_p : best_for_c;
+
+ if (best_index < 0 || positive < best_index)
+ best_index = positive;
+
+ /* No matter what, track that the parent is reachable from tip. */
+ set_best(parent, -1);
+ branch_point = parent;
+ }
+
+cleanup:
+ clear_best_branch_base(&best_branch_base);
+ clear_prio_queue(&queue);
+ return best_index > 0 ? best_index - 1 : -1;
+}
diff --git a/commit-reach.h b/commit-reach.h
index bf63cc468f..9a745b7e17 100644
--- a/commit-reach.h
+++ b/commit-reach.h
@@ -139,4 +139,21 @@ void tips_reachable_from_bases(struct repository *r,
struct commit **tips, size_t tips_nr,
int mark);
+/*
+ * Given a 'tip' commit and a list potential 'bases', return the index 'i' that
+ * minimizes the number of commits in the first-parent history of 'tip' and not
+ * in the first-parent history of 'bases[i]'.
+ *
+ * Among a list of long-lived branches that are updated only by merges (with the
+ * first parent being the previous position of the branch), this would inform
+ * which branch was used to create the tip reference.
+ *
+ * Returns -1 if no common point is found in first-parent histories, which is
+ * rare, but possible with multiple root commits.
+ */
+int get_branch_base_for_tip(struct repository *r,
+ struct commit *tip,
+ struct commit **bases,
+ size_t bases_nr);
+
#endif
diff --git a/commit.c b/commit.c
index 24ab5c1b50..3238772f52 100644
--- a/commit.c
+++ b/commit.c
@@ -85,12 +85,18 @@ struct commit *lookup_commit(struct repository *r, const struct object_id *oid)
struct commit *lookup_commit_reference_by_name(const char *name)
{
+ return lookup_commit_reference_by_name_gently(name, 0);
+}
+
+struct commit *lookup_commit_reference_by_name_gently(const char *name,
+ int quiet)
+{
struct object_id oid;
struct commit *commit;
if (repo_get_oid_committish(the_repository, name, &oid))
return NULL;
- commit = lookup_commit_reference(the_repository, &oid);
+ commit = lookup_commit_reference_gently(the_repository, &oid, quiet);
if (repo_parse_commit(the_repository, commit))
return NULL;
return commit;
diff --git a/commit.h b/commit.h
index d62b1d93f9..0e5fce543c 100644
--- a/commit.h
+++ b/commit.h
@@ -81,6 +81,8 @@ struct commit *lookup_commit_reference_gently(struct repository *r,
const struct object_id *oid,
int quiet);
struct commit *lookup_commit_reference_by_name(const char *name);
+struct commit *lookup_commit_reference_by_name_gently(const char *name,
+ int quiet);
/*
* Look up object named by "oid", dereference tag as necessary,
diff --git a/compat/mingw.c b/compat/mingw.c
index 29d3f09768..eb13c02a76 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -243,7 +243,8 @@ static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
static char *unset_environment_variables;
int mingw_core_config(const char *var, const char *value,
- const struct config_context *ctx, void *cb)
+ const struct config_context *ctx UNUSED,
+ void *cb UNUSED)
{
if (!strcmp(var, "core.hidedotfiles")) {
if (value && !strcasecmp(value, "dotgitonly"))
@@ -453,7 +454,7 @@ static int set_hidden_flag(const wchar_t *path, int set)
return -1;
}
-int mingw_mkdir(const char *path, int mode)
+int mingw_mkdir(const char *path, int mode UNUSED)
{
int ret;
wchar_t wpath[MAX_PATH];
@@ -597,7 +598,7 @@ int mingw_open (const char *filename, int oflags, ...)
return fd;
}
-static BOOL WINAPI ctrl_ignore(DWORD type)
+static BOOL WINAPI ctrl_ignore(DWORD type UNUSED)
{
return TRUE;
}
@@ -1085,7 +1086,7 @@ int mkstemp(char *template)
return git_mkstemp_mode(template, 0600);
}
-int gettimeofday(struct timeval *tv, void *tz)
+int gettimeofday(struct timeval *tv, void *tz UNUSED)
{
FILETIME ft;
long long hnsec;
@@ -2252,7 +2253,7 @@ char *mingw_query_user_email(void)
return get_extended_user_info(NameUserPrincipal);
}
-struct passwd *getpwuid(int uid)
+struct passwd *getpwuid(int uid UNUSED)
{
static unsigned initialized;
static char user_name[100];
@@ -2304,7 +2305,7 @@ static sig_handler_t timer_fn = SIG_DFL, sigint_fn = SIG_DFL;
* length to call the signal handler.
*/
-static unsigned __stdcall ticktack(void *dummy)
+static unsigned __stdcall ticktack(void *dummy UNUSED)
{
while (WaitForSingleObject(timer_event, timer_interval) == WAIT_TIMEOUT) {
mingw_raise(SIGALRM);
@@ -2352,7 +2353,7 @@ static inline int is_timeval_eq(const struct timeval *i1, const struct timeval *
return i1->tv_sec == i2->tv_sec && i1->tv_usec == i2->tv_usec;
}
-int setitimer(int type, struct itimerval *in, struct itimerval *out)
+int setitimer(int type UNUSED, struct itimerval *in, struct itimerval *out)
{
static const struct timeval zero;
static int atexit_done;
diff --git a/compat/mingw.h b/compat/mingw.h
index 27b61284f4..ebfb8ba423 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -122,17 +122,17 @@ struct utsname {
* trivial stubs
*/
-static inline int readlink(const char *path, char *buf, size_t bufsiz)
+static inline int readlink(const char *path UNUSED, char *buf UNUSED, size_t bufsiz UNUSED)
{ errno = ENOSYS; return -1; }
-static inline int symlink(const char *oldpath, const char *newpath)
+static inline int symlink(const char *oldpath UNUSED, const char *newpath UNUSED)
{ errno = ENOSYS; return -1; }
-static inline int fchmod(int fildes, mode_t mode)
+static inline int fchmod(int fildes UNUSED, mode_t mode UNUSED)
{ errno = ENOSYS; return -1; }
#ifndef __MINGW64_VERSION_MAJOR
static inline pid_t fork(void)
{ errno = ENOSYS; return -1; }
#endif
-static inline unsigned int alarm(unsigned int seconds)
+static inline unsigned int alarm(unsigned int seconds UNUSED)
{ return 0; }
static inline int fsync(int fd)
{ return _commit(fd); }
@@ -140,9 +140,9 @@ static inline void sync(void)
{}
static inline uid_t getuid(void)
{ return 1; }
-static inline struct passwd *getpwnam(const char *name)
+static inline struct passwd *getpwnam(const char *name UNUSED)
{ return NULL; }
-static inline int fcntl(int fd, int cmd, ...)
+static inline int fcntl(int fd UNUSED, int cmd, ...)
{
if (cmd == F_GETFD || cmd == F_SETFD)
return 0;
@@ -151,17 +151,17 @@ static inline int fcntl(int fd, int cmd, ...)
}
#define sigemptyset(x) (void)0
-static inline int sigaddset(sigset_t *set, int signum)
+static inline int sigaddset(sigset_t *set UNUSED, int signum UNUSED)
{ return 0; }
#define SIG_BLOCK 0
#define SIG_UNBLOCK 0
-static inline int sigprocmask(int how, const sigset_t *set, sigset_t *oldset)
+static inline int sigprocmask(int how UNUSED, const sigset_t *set UNUSED, sigset_t *oldset UNUSED)
{ return 0; }
static inline pid_t getppid(void)
{ return 1; }
static inline pid_t getpgid(pid_t pid)
{ return pid == 0 ? getpid() : pid; }
-static inline pid_t tcgetpgrp(int fd)
+static inline pid_t tcgetpgrp(int fd UNUSED)
{ return getpid(); }
/*
diff --git a/compat/nedmalloc/nedmalloc.c b/compat/nedmalloc/nedmalloc.c
index 2c0ace7075..145255da43 100644
--- a/compat/nedmalloc/nedmalloc.c
+++ b/compat/nedmalloc/nedmalloc.c
@@ -31,6 +31,8 @@ DEALINGS IN THE SOFTWARE.
/*#pragma optimize("a", on)*/
#endif
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+
/*#define FULLSANITYCHECKS*/
#include "nedmalloc.h"
diff --git a/compat/regex/regcomp.c b/compat/regex/regcomp.c
index 6c5d455e92..8d93a9b93f 100644
--- a/compat/regex/regcomp.c
+++ b/compat/regex/regcomp.c
@@ -17,6 +17,8 @@
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+
#if defined __TANDEM
/* This is currently duplicated from git-compat-utils.h */
# ifdef NO_INTPTR_T
diff --git a/compat/stub/procinfo.c b/compat/stub/procinfo.c
index 12c0a23c9e..3168cd5714 100644
--- a/compat/stub/procinfo.c
+++ b/compat/stub/procinfo.c
@@ -6,6 +6,6 @@
* Stub. See sample implementations in compat/linux/procinfo.c and
* compat/win32/trace2_win32_process_info.c.
*/
-void trace2_collect_process_info(enum trace2_process_info_reason reason)
+void trace2_collect_process_info(enum trace2_process_info_reason reason UNUSED)
{
}
diff --git a/compat/terminal.c b/compat/terminal.c
index 0afda730f2..d54efa1c5d 100644
--- a/compat/terminal.c
+++ b/compat/terminal.c
@@ -594,7 +594,7 @@ void restore_term(void)
{
}
-char *git_terminal_prompt(const char *prompt, int echo)
+char *git_terminal_prompt(const char *prompt, int echo UNUSED)
{
return getpass(prompt);
}
diff --git a/compat/win32/headless.c b/compat/win32/headless.c
index 8b00dfe3bd..11392a0b9a 100644
--- a/compat/win32/headless.c
+++ b/compat/win32/headless.c
@@ -11,6 +11,8 @@
#include <stdlib.h>
#include <wchar.h>
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+
/*
* If `dir` contains the path to a Git exec directory, extend `PATH` to
* include the corresponding `bin/` directory (which is where all those
diff --git a/compat/win32/pthread.c b/compat/win32/pthread.c
index 85f8f7920c..58980a529c 100644
--- a/compat/win32/pthread.c
+++ b/compat/win32/pthread.c
@@ -21,7 +21,7 @@ static unsigned __stdcall win32_start_routine(void *arg)
return 0;
}
-int pthread_create(pthread_t *thread, const void *unused,
+int pthread_create(pthread_t *thread, const void *attr UNUSED,
void *(*start_routine)(void *), void *arg)
{
thread->arg = arg;
diff --git a/compat/win32/pthread.h b/compat/win32/pthread.h
index cc3221cb2c..e2b5c4f64c 100644
--- a/compat/win32/pthread.h
+++ b/compat/win32/pthread.h
@@ -18,7 +18,7 @@
*/
#define pthread_mutex_t CRITICAL_SECTION
-static inline int return_0(int i) {
+static inline int return_0(int i UNUSED) {
return 0;
}
#define pthread_mutex_init(a,b) return_0((InitializeCriticalSection((a)), 0))
@@ -70,7 +70,7 @@ static inline void NORETURN pthread_exit(void *ret)
}
typedef DWORD pthread_key_t;
-static inline int pthread_key_create(pthread_key_t *keyp, void (*destructor)(void *value))
+static inline int pthread_key_create(pthread_key_t *keyp, void (*destructor)(void *value) UNUSED)
{
return (*keyp = TlsAlloc()) == TLS_OUT_OF_INDEXES ? EAGAIN : 0;
}
diff --git a/compat/win32/syslog.c b/compat/win32/syslog.c
index 0af18d8882..4e4794743a 100644
--- a/compat/win32/syslog.c
+++ b/compat/win32/syslog.c
@@ -2,7 +2,7 @@
static HANDLE ms_eventlog;
-void openlog(const char *ident, int logopt, int facility)
+void openlog(const char *ident, int logopt UNUSED, int facility UNUSED)
{
if (ms_eventlog)
return;
diff --git a/compat/win32mmap.c b/compat/win32mmap.c
index 519d51f2b6..a4ab4cb939 100644
--- a/compat/win32mmap.c
+++ b/compat/win32mmap.c
@@ -40,7 +40,7 @@ void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t of
return MAP_FAILED;
}
-int git_munmap(void *start, size_t length)
+int git_munmap(void *start, size_t length UNUSED)
{
return !UnmapViewOfFile(start);
}
diff --git a/compat/winansi.c b/compat/winansi.c
index 575813bde8..1b3f916b9f 100644
--- a/compat/winansi.c
+++ b/compat/winansi.c
@@ -340,7 +340,7 @@ enum {
TEXT = 0, ESCAPE = 033, BRACKET = '['
};
-static DWORD WINAPI console_thread(LPVOID unused)
+static DWORD WINAPI console_thread(LPVOID data UNUSED)
{
unsigned char buffer[BUFFER_SIZE];
DWORD bytes;
diff --git a/config.c b/config.c
index 9850897524..56b5862e59 100644
--- a/config.c
+++ b/config.c
@@ -2694,9 +2694,10 @@ void git_protected_config(config_fn_t fn, void *data)
configset_iter(&protected_config, fn, data);
}
-int repo_config_get_expiry(struct repository *r, const char *key, const char **output)
+int repo_config_get_expiry(struct repository *r, const char *key, char **output)
{
- int ret = repo_config_get_string(r, key, (char **)output);
+ int ret = repo_config_get_string(r, key, output);
+
if (ret)
return ret;
if (strcmp(*output, "now")) {
diff --git a/config.h b/config.h
index 4d319a4193..d0497157c5 100644
--- a/config.h
+++ b/config.h
@@ -672,7 +672,7 @@ int repo_config_get_split_index(struct repository *r);
int repo_config_get_max_percent_split_change(struct repository *r);
/* This dies if the configured or default date is in the future */
-int repo_config_get_expiry(struct repository *r, const char *key, const char **output);
+int repo_config_get_expiry(struct repository *r, const char *key, char **output);
/* parse either "this many days" integer, or "5.days.ago" approxidate */
int repo_config_get_expiry_in_days(struct repository *r, const char *key,
diff --git a/config.mak.dev b/config.mak.dev
index 5229c35484..50026d1e0e 100644
--- a/config.mak.dev
+++ b/config.mak.dev
@@ -54,7 +54,6 @@ ifeq ($(filter extra-all,$(DEVOPTS)),)
DEVELOPER_CFLAGS += -Wno-empty-body
DEVELOPER_CFLAGS += -Wno-missing-field-initializers
DEVELOPER_CFLAGS += -Wno-sign-compare
-DEVELOPER_CFLAGS += -Wno-unused-parameter
endif
endif
diff --git a/config.mak.uname b/config.mak.uname
index aa0fd26bd5..904bcf3598 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -648,6 +648,7 @@ ifeq ($(uname_S),OS/390)
NO_GECOS_IN_PWENT = YesPlease
HAVE_STRINGS_H = YesPlease
NEEDS_MODE_TRANSLATION = YesPlease
+ HAVE_ZOS_GET_EXECUTABLE_PATH = YesPlease
endif
ifeq ($(uname_S),MINGW)
ifeq ($(shell expr "$(uname_R)" : '1\.'),2)
diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh
index 5330e769a7..6186c474ba 100644
--- a/contrib/completion/git-prompt.sh
+++ b/contrib/completion/git-prompt.sh
@@ -8,8 +8,8 @@
# To enable:
#
# 1) Copy this file to somewhere (e.g. ~/.git-prompt.sh).
-# 2) Add the following line to your .bashrc/.zshrc:
-# source ~/.git-prompt.sh
+# 2) Add the following line to your .bashrc/.zshrc/.profile:
+# . ~/.git-prompt.sh # dot path/to/this-file
# 3a) Change your PS1 to call __git_ps1 as
# command-substitution:
# Bash: PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '
@@ -30,6 +30,8 @@
# Optionally, you can supply a third argument with a printf
# format string to finetune the output of the branch status
#
+# See notes below about compatibility with other shells.
+#
# The repository status will be displayed only if you are currently in a
# git repository. The %s token is the placeholder for the shown status.
#
@@ -106,38 +108,78 @@
# directory is set up to be ignored by git, then set
# GIT_PS1_HIDE_IF_PWD_IGNORED to a nonempty value. Override this on the
# repository level by setting bash.hideIfPwdIgnored to "false".
+#
+# Compatibility with other shells (beyond bash/zsh):
+#
+# We require posix-ish shell plus "local" support, which is most
+# shells (even pdksh), but excluding ksh93 (because no "local").
+#
+# Prompt integration might differ between shells, but the gist is
+# to load it once on shell init with '. path/to/git-prompt.sh',
+# set GIT_PS1* vars once as needed, and either place $(__git_ps1..)
+# inside PS1 once (0/1 args), or, before each prompt is displayed,
+# call __git_ps1 (2/3 args) which sets PS1 with the status embedded.
+#
+# Many shells support the 1st method of command substitution,
+# though some might need to first enable cmd substitution in PS1.
+#
+# When using colors, each escape sequence is wrapped between byte
+# values 1 and 2 (control chars SOH, STX, respectively), which are
+# invisible at the output, but for bash/readline they mark 0-width
+# strings (SGR color sequences) when calculating the on-screen
+# prompt width, to maintain correct input editing at the prompt.
+#
+# To replace or disable the 0-width markers, set GIT_PS1_COLOR_PRE
+# and GIT_PS1_COLOR_POST to other markers, or empty (nul) to not
+# use markers. For instance, some shells support '\[' and '\]' as
+# start/end markers in PS1 - when invoking __git_ps1 with 3/4 args,
+# but it may or may not work in command substitution mode. YMMV.
+#
+# If the shell doesn't support 0-width markers and editing behaves
+# incorrectly when using colors in __git_ps1, then, other than
+# disabling color, it might be solved using multi-line prompt,
+# where the git status is not at the last line, e.g.:
+# PS1='\n\w \u@\h$(__git_ps1 " (%s)")\n\$ '
# check whether printf supports -v
__git_printf_supports_v=
printf -v __git_printf_supports_v -- '%s' yes >/dev/null 2>&1
+# like __git_SOH=$'\001' etc but works also in shells without $'...'
+eval "$(printf '
+ __git_SOH="\001" __git_STX="\002" __git_ESC="\033"
+ __git_LF="\n" __git_CRLF="\r\n"
+')"
+
# stores the divergence from upstream in $p
# used by GIT_PS1_SHOWUPSTREAM
__git_ps1_show_upstream ()
{
local key value
- local svn_remote svn_url_pattern count n
+ local svn_remotes="" svn_url_pattern="" count n
local upstream_type=git legacy="" verbose="" name=""
+ local LF="$__git_LF"
- svn_remote=()
# get some config options from git-config
local output="$(git config -z --get-regexp '^(svn-remote\..*\.url|bash\.showupstream)$' 2>/dev/null | tr '\0\n' '\n ')"
while read -r key value; do
case "$key" in
bash.showupstream)
GIT_PS1_SHOWUPSTREAM="$value"
- if [[ -z "${GIT_PS1_SHOWUPSTREAM}" ]]; then
+ if [ -z "${GIT_PS1_SHOWUPSTREAM}" ]; then
p=""
return
fi
;;
svn-remote.*.url)
- svn_remote[$((${#svn_remote[@]} + 1))]="$value"
+ svn_remotes=${svn_remotes}${value}${LF} # URI\nURI\n...
svn_url_pattern="$svn_url_pattern\\|$value"
upstream_type=svn+git # default upstream type is SVN if available, else git
;;
esac
- done <<< "$output"
+ done <<-OUTPUT
+ $output
+ OUTPUT
# parse configuration values
local option
@@ -154,33 +196,45 @@ __git_ps1_show_upstream ()
case "$upstream_type" in
git) upstream_type="@{upstream}" ;;
svn*)
- # get the upstream from the "git-svn-id: ..." in a commit message
- # (git-svn uses essentially the same procedure internally)
- local -a svn_upstream
- svn_upstream=($(git log --first-parent -1 \
- --grep="^git-svn-id: \(${svn_url_pattern#??}\)" 2>/dev/null))
- if [[ 0 -ne ${#svn_upstream[@]} ]]; then
- svn_upstream=${svn_upstream[${#svn_upstream[@]} - 2]}
- svn_upstream=${svn_upstream%@*}
- local n_stop="${#svn_remote[@]}"
- for ((n=1; n <= n_stop; n++)); do
- svn_upstream=${svn_upstream#${svn_remote[$n]}}
- done
+ # successful svn-upstream resolution:
+ # - get the list of configured svn-remotes ($svn_remotes set above)
+ # - get the last commit which seems from one of our svn-remotes
+ # - confirm that it is from one of the svn-remotes
+ # - use $GIT_SVN_ID if set, else "git-svn"
- if [[ -z "$svn_upstream" ]]; then
+ # get upstream from "git-svn-id: UPSTRM@N HASH" in a commit message
+ # (git-svn uses essentially the same procedure internally)
+ local svn_upstream="$(
+ git log --first-parent -1 \
+ --grep="^git-svn-id: \(${svn_url_pattern#??}\)" 2>/dev/null
+ )"
+
+ if [ -n "$svn_upstream" ]; then
+ # extract the URI, assuming --grep matched the last line
+ svn_upstream=${svn_upstream##*$LF} # last line
+ svn_upstream=${svn_upstream#*: } # UPSTRM@N HASH
+ svn_upstream=${svn_upstream%@*} # UPSTRM
+
+ case ${LF}${svn_remotes} in
+ *"${LF}${svn_upstream}${LF}"*)
+ # grep indeed matched the last line - it's our remote
# default branch name for checkouts with no layout:
upstream_type=${GIT_SVN_ID:-git-svn}
- else
+ ;;
+ *)
+ # the commit message includes one of our remotes, but
+ # it's not at the last line. is $svn_upstream junk?
upstream_type=${svn_upstream#/}
- fi
- elif [[ "svn+git" = "$upstream_type" ]]; then
+ ;;
+ esac
+ elif [ "svn+git" = "$upstream_type" ]; then
upstream_type="@{upstream}"
fi
;;
esac
# Find how many commits we are ahead/behind our upstream
- if [[ -z "$legacy" ]]; then
+ if [ -z "$legacy" ]; then
count="$(git rev-list --count --left-right \
"$upstream_type"...HEAD 2>/dev/null)"
else
@@ -192,8 +246,8 @@ __git_ps1_show_upstream ()
for commit in $commits
do
case "$commit" in
- "<"*) ((behind++)) ;;
- *) ((ahead++)) ;;
+ "<"*) behind=$((behind+1)) ;;
+ *) ahead=$((ahead+1)) ;;
esac
done
count="$behind $ahead"
@@ -203,7 +257,7 @@ __git_ps1_show_upstream ()
fi
# calculate the result
- if [[ -z "$verbose" ]]; then
+ if [ -z "$verbose" ]; then
case "$count" in
"") # no upstream
p="" ;;
@@ -229,10 +283,10 @@ __git_ps1_show_upstream ()
*) # diverged from upstream
upstream="|u+${count#* }-${count% *}" ;;
esac
- if [[ -n "$count" && -n "$name" ]]; then
+ if [ -n "$count" ] && [ -n "$name" ]; then
__git_ps1_upstream_name=$(git rev-parse \
--abbrev-ref "$upstream_type" 2>/dev/null)
- if [ $pcmode = yes ] && [ $ps1_expanded = yes ]; then
+ if [ "$pcmode" = yes ] && [ "$ps1_expanded" = yes ]; then
upstream="$upstream \${__git_ps1_upstream_name}"
else
upstream="$upstream ${__git_ps1_upstream_name}"
@@ -251,25 +305,29 @@ __git_ps1_show_upstream ()
# their own color.
__git_ps1_colorize_gitstring ()
{
- if [[ -n ${ZSH_VERSION-} ]]; then
+ if [ -n "${ZSH_VERSION-}" ]; then
local c_red='%F{red}'
local c_green='%F{green}'
local c_lblue='%F{blue}'
local c_clear='%f'
else
- # Using \001 and \002 around colors is necessary to prevent
- # issues with command line editing/browsing/completion!
- local c_red=$'\001\e[31m\002'
- local c_green=$'\001\e[32m\002'
- local c_lblue=$'\001\e[1;34m\002'
- local c_clear=$'\001\e[0m\002'
+ # \001 (SOH) and \002 (STX) are 0-width substring markers
+ # which bash/readline identify while calculating the prompt
+ # on-screen width - to exclude 0-screen-width esc sequences.
+ local c_pre="${GIT_PS1_COLOR_PRE-$__git_SOH}${__git_ESC}["
+ local c_post="m${GIT_PS1_COLOR_POST-$__git_STX}"
+
+ local c_red="${c_pre}31${c_post}"
+ local c_green="${c_pre}32${c_post}"
+ local c_lblue="${c_pre}1;34${c_post}"
+ local c_clear="${c_pre}0${c_post}"
fi
- local bad_color=$c_red
- local ok_color=$c_green
+ local bad_color="$c_red"
+ local ok_color="$c_green"
local flags_color="$c_lblue"
local branch_color=""
- if [ $detached = no ]; then
+ if [ "$detached" = no ]; then
branch_color="$ok_color"
else
branch_color="$bad_color"
@@ -298,7 +356,7 @@ __git_ps1_colorize_gitstring ()
# variable, in that order.
__git_eread ()
{
- test -r "$1" && IFS=$'\r\n' read -r "$2" <"$1"
+ test -r "$1" && IFS=$__git_CRLF read -r "$2" <"$1"
}
# see if a cherry-pick or revert is in progress, if the user has committed a
@@ -346,7 +404,7 @@ __git_sequencer_status ()
__git_ps1 ()
{
# preserve exit status
- local exit=$?
+ local exit="$?"
local pcmode=no
local detached=no
local ps1pc_start='\u@\h:\w '
@@ -365,7 +423,7 @@ __git_ps1 ()
;;
0|1) printf_format="${1:-$printf_format}"
;;
- *) return $exit
+ *) return "$exit"
;;
esac
@@ -403,7 +461,7 @@ __git_ps1 ()
# incorrect.)
#
local ps1_expanded=yes
- [ -z "${ZSH_VERSION-}" ] || [[ -o PROMPT_SUBST ]] || ps1_expanded=no
+ [ -z "${ZSH_VERSION-}" ] || eval '[[ -o PROMPT_SUBST ]]' || ps1_expanded=no
[ -z "${BASH_VERSION-}" ] || shopt -q promptvars || ps1_expanded=no
local repo_info rev_parse_exit_code
@@ -413,29 +471,30 @@ __git_ps1 ()
rev_parse_exit_code="$?"
if [ -z "$repo_info" ]; then
- return $exit
+ return "$exit"
fi
+ local LF="$__git_LF"
local short_sha=""
if [ "$rev_parse_exit_code" = "0" ]; then
- short_sha="${repo_info##*$'\n'}"
- repo_info="${repo_info%$'\n'*}"
+ short_sha="${repo_info##*$LF}"
+ repo_info="${repo_info%$LF*}"
fi
- local ref_format="${repo_info##*$'\n'}"
- repo_info="${repo_info%$'\n'*}"
- local inside_worktree="${repo_info##*$'\n'}"
- repo_info="${repo_info%$'\n'*}"
- local bare_repo="${repo_info##*$'\n'}"
- repo_info="${repo_info%$'\n'*}"
- local inside_gitdir="${repo_info##*$'\n'}"
- local g="${repo_info%$'\n'*}"
+ local ref_format="${repo_info##*$LF}"
+ repo_info="${repo_info%$LF*}"
+ local inside_worktree="${repo_info##*$LF}"
+ repo_info="${repo_info%$LF*}"
+ local bare_repo="${repo_info##*$LF}"
+ repo_info="${repo_info%$LF*}"
+ local inside_gitdir="${repo_info##*$LF}"
+ local g="${repo_info%$LF*}"
if [ "true" = "$inside_worktree" ] &&
[ -n "${GIT_PS1_HIDE_IF_PWD_IGNORED-}" ] &&
[ "$(git config --bool bash.hideIfPwdIgnored)" != "false" ] &&
git check-ignore -q .
then
- return $exit
+ return "$exit"
fi
local sparse=""
@@ -485,14 +544,16 @@ __git_ps1 ()
case "$ref_format" in
files)
if ! __git_eread "$g/HEAD" head; then
- return $exit
+ return "$exit"
fi
- if [[ $head == "ref: "* ]]; then
+ case $head in
+ "ref: "*)
head="${head#ref: }"
- else
+ ;;
+ *)
head=""
- fi
+ esac
;;
*)
head="$(git symbolic-ref HEAD 2>/dev/null)"
@@ -528,8 +589,8 @@ __git_ps1 ()
fi
local conflict="" # state indicator for unresolved conflicts
- if [[ "${GIT_PS1_SHOWCONFLICTSTATE-}" == "yes" ]] &&
- [[ $(git ls-files --unmerged 2>/dev/null) ]]; then
+ if [ "${GIT_PS1_SHOWCONFLICTSTATE-}" = "yes" ] &&
+ [ "$(git ls-files --unmerged 2>/dev/null)" ]; then
conflict="|CONFLICT"
fi
@@ -581,10 +642,10 @@ __git_ps1 ()
fi
fi
- local z="${GIT_PS1_STATESEPARATOR-" "}"
+ local z="${GIT_PS1_STATESEPARATOR- }"
b=${b##refs/heads/}
- if [ $pcmode = yes ] && [ $ps1_expanded = yes ]; then
+ if [ "$pcmode" = yes ] && [ "$ps1_expanded" = yes ]; then
__git_ps1_branch_name=$b
b="\${__git_ps1_branch_name}"
fi
@@ -596,7 +657,7 @@ __git_ps1 ()
local f="$h$w$i$s$u$p"
local gitstring="$c$b${f:+$z$f}${sparse}$r${upstream}${conflict}"
- if [ $pcmode = yes ]; then
+ if [ "$pcmode" = yes ]; then
if [ "${__git_printf_supports_v-}" != yes ]; then
gitstring=$(printf -- "$printf_format" "$gitstring")
else
@@ -607,5 +668,5 @@ __git_ps1 ()
printf -- "$printf_format" "$gitstring"
fi
- return $exit
+ return "$exit"
}
diff --git a/convert.c b/convert.c
index e6184d21f2..c9a31eb4f0 100644
--- a/convert.c
+++ b/convert.c
@@ -1371,6 +1371,9 @@ void reset_parsed_attributes(void)
for (drv = user_convert; drv; drv = next) {
next = drv->next;
free((void *)drv->name);
+ free((void *)drv->smudge);
+ free((void *)drv->clean);
+ free((void *)drv->process);
free(drv);
}
user_convert = NULL;
diff --git a/daemon.c b/daemon.c
index c65b068b76..cb946e3c95 100644
--- a/daemon.c
+++ b/daemon.c
@@ -1177,13 +1177,13 @@ static int service_loop(struct socketlist *socklist)
struct credentials;
-static void drop_privileges(struct credentials *cred)
+static void drop_privileges(struct credentials *cred UNUSED)
{
/* nothing */
}
-static struct credentials *prepare_credentials(const char *user_name,
- const char *group_name)
+static struct credentials *prepare_credentials(const char *user_name UNUSED,
+ const char *group_name UNUSED)
{
die("--user not supported on this platform");
}
diff --git a/diff-lib.c b/diff-lib.c
index 7a1eb63757..a680768ee7 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -308,8 +308,7 @@ static void diff_index_show_file(struct rev_info *revs,
oid, oid_valid, ce->name, dirty_submodule);
}
-static int get_stat_data(const struct index_state *istate,
- const struct cache_entry *ce,
+static int get_stat_data(const struct cache_entry *ce,
const struct object_id **oidp,
unsigned int *modep,
int cached, int match_missing,
@@ -352,7 +351,6 @@ static void show_new_file(struct rev_info *revs,
const struct object_id *oid;
unsigned int mode;
unsigned dirty_submodule = 0;
- struct index_state *istate = revs->diffopt.repo->index;
if (new_file && S_ISSPARSEDIR(new_file->ce_mode)) {
diff_tree_oid(NULL, &new_file->oid, new_file->name, &revs->diffopt);
@@ -363,7 +361,7 @@ static void show_new_file(struct rev_info *revs,
* New file in the index: it might actually be different in
* the working tree.
*/
- if (get_stat_data(istate, new_file, &oid, &mode, cached, match_missing,
+ if (get_stat_data(new_file, &oid, &mode, cached, match_missing,
&dirty_submodule, &revs->diffopt) < 0)
return;
@@ -379,7 +377,6 @@ static int show_modified(struct rev_info *revs,
unsigned int mode, oldmode;
const struct object_id *oid;
unsigned dirty_submodule = 0;
- struct index_state *istate = revs->diffopt.repo->index;
assert(S_ISSPARSEDIR(old_entry->ce_mode) ==
S_ISSPARSEDIR(new_entry->ce_mode));
@@ -395,7 +392,7 @@ static int show_modified(struct rev_info *revs,
return 0;
}
- if (get_stat_data(istate, new_entry, &oid, &mode, cached, match_missing,
+ if (get_stat_data(new_entry, &oid, &mode, cached, match_missing,
&dirty_submodule, &revs->diffopt) < 0) {
if (report_missing)
diff_index_show_file(revs, "-", old_entry,
diff --git a/exec-cmd.c b/exec-cmd.c
index 909777f61f..507e67d528 100644
--- a/exec-cmd.c
+++ b/exec-cmd.c
@@ -150,6 +150,25 @@ static int git_get_exec_path_darwin(struct strbuf *buf)
}
#endif /* HAVE_NS_GET_EXECUTABLE_PATH */
+#ifdef HAVE_ZOS_GET_EXECUTABLE_PATH
+/*
+ * Resolves the executable path from current program's directory and name.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+static int git_get_exec_path_zos(struct strbuf *buf)
+{
+ char *dir = __getprogramdir();
+ char *exe = getprogname();
+ if (dir && exe) {
+ strbuf_addf(buf, "%s/%s", dir, exe);
+ return 0;
+ }
+ return -1;
+}
+
+#endif /* HAVE_ZOS_GET_EXECUTABLE_PATH */
+
#ifdef HAVE_WPGMPTR
/*
* Resolves the executable path by using the global variable _wpgmptr.
@@ -206,6 +225,10 @@ static int git_get_exec_path(struct strbuf *buf, const char *argv0)
git_get_exec_path_wpgmptr(buf) &&
#endif /* HAVE_WPGMPTR */
+#ifdef HAVE_ZOS_GET_EXECUTABLE_PATH
+ git_get_exec_path_zos(buf) &&
+#endif /*HAVE_ZOS_GET_EXECUTABLE_PATH */
+
git_get_exec_path_from_argv0(buf, argv0)) {
return -1;
}
diff --git a/git-compat-util.h b/git-compat-util.h
index 71b4d23f03..e4a306dd56 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -195,6 +195,19 @@ struct strbuf;
#define _NETBSD_SOURCE 1
#define _SGI_SOURCE 1
+/*
+ * UNUSED marks a function parameter that is always unused. It also
+ * can be used to annotate a function, a variable, or a type that is
+ * always unused.
+ *
+ * A callback interface may dictate that a function accepts a
+ * parameter at that position, but the implementation of the function
+ * may not need to use the parameter. In such a case, mark the parameter
+ * with UNUSED.
+ *
+ * When a parameter may be used or unused, depending on conditional
+ * compilation, consider using MAYBE_UNUSED instead.
+ */
#if GIT_GNUC_PREREQ(4, 5)
#define UNUSED __attribute__((unused)) \
__attribute__((deprecated ("parameter declared as UNUSED")))
@@ -649,6 +662,17 @@ static inline int git_has_dir_sep(const char *path)
#define RESULT_MUST_BE_USED
#endif
+/*
+ * MAYBE_UNUSED marks a function parameter that may be unused, but
+ * whose use is not an error. It also can be used to annotate a
+ * function, a variable, or a type that may be unused.
+ *
+ * Depending on a configuration, all uses of such a thing may become
+ * #ifdef'ed away. Marking it with UNUSED would give a warning in a
+ * compilation where it is indeed used, and not marking it at all
+ * would give a warning in a compilation where it is unused. In such
+ * a case, MAYBE_UNUSED is the appropriate annotation to use.
+ */
#define MAYBE_UNUSED __attribute__((__unused__))
#include "compat/bswap.h"
diff --git a/git-send-email.perl b/git-send-email.perl
index 72044e5ef3..c835d4c11a 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -31,6 +31,7 @@ sub usage {
git send-email [<options>] <file|directory>
git send-email [<options>] <format-patch options>
git send-email --dump-aliases
+git send-email --translate-aliases
Composing:
--from <str> * Email From:
@@ -46,6 +47,8 @@ git send-email --dump-aliases
--compose-encoding <str> * Encoding to assume for introduction.
--8bit-encoding <str> * Encoding to assume 8bit mails if undeclared
--transfer-encoding <str> * Transfer encoding to use (quoted-printable, 8bit, base64)
+ --[no-]mailmap * Use mailmap file to map all email addresses to canonical
+ real names and email addresses.
Sending:
--envelope-sender <str> * Email envelope sender.
@@ -99,6 +102,10 @@ git send-email --dump-aliases
Information:
--dump-aliases * Dump configured aliases and exit.
+ --translate-aliases * Translate aliases read from standard
+ input according to the configured email
+ alias file(s), outputting the result to
+ standard output.
EOT
exit(1);
@@ -212,6 +219,7 @@ my $format_patch;
my $compose_filename;
my $force = 0;
my $dump_aliases = 0;
+my $translate_aliases = 0;
# Variables to prevent short format-patch options from being captured
# as abbreviated send-email options
@@ -272,12 +280,14 @@ my (@suppress_cc);
my ($auto_8bit_encoding);
my ($compose_encoding);
my ($sendmail_cmd);
+my ($mailmap_file, $mailmap_blob);
# Variables with corresponding config settings & hardcoded defaults
my ($debug_net_smtp) = 0; # Net::SMTP, see send_message()
my $thread = 1;
my $chain_reply_to = 0;
my $use_xmailer = 1;
my $validate = 1;
+my $mailmap = 0;
my $target_xfer_encoding = 'auto';
my $forbid_sendmail_variables = 1;
@@ -294,6 +304,7 @@ my %config_bool_settings = (
"annotate" => \$annotate,
"xmailer" => \$use_xmailer,
"forbidsendmailvariables" => \$forbid_sendmail_variables,
+ "mailmap" => \$mailmap,
);
my %config_settings = (
@@ -327,6 +338,8 @@ my %config_settings = (
my %config_path_settings = (
"aliasesfile" => \@alias_files,
"smtpsslcertpath" => \$smtp_ssl_cert_path,
+ "mailmap.file" => \$mailmap_file,
+ "mailmap.blob" => \$mailmap_blob,
);
# Handle Uncouth Termination
@@ -476,11 +489,14 @@ my $git_completion_helper;
my %dump_aliases_options = (
"h" => \$help,
"dump-aliases" => \$dump_aliases,
+ "translate-aliases" => \$translate_aliases,
);
$rc = GetOptions(%dump_aliases_options);
usage() unless $rc;
die __("--dump-aliases incompatible with other options\n")
- if !$help and $dump_aliases and @ARGV;
+ if !$help and ($dump_aliases or $translate_aliases) and @ARGV;
+die __("--dump-aliases and --translate-aliases are mutually exclusive\n")
+ if !$help and $dump_aliases and $translate_aliases;
my %options = (
"sender|from=s" => \$sender,
"in-reply-to=s" => \$initial_in_reply_to,
@@ -524,6 +540,8 @@ my %options = (
"thread!" => \$thread,
"validate!" => \$validate,
"transfer-encoding=s" => \$target_xfer_encoding,
+ "mailmap!" => \$mailmap,
+ "use-mailmap!" => \$mailmap,
"format-patch!" => \$format_patch,
"8bit-encoding=s" => \$auto_8bit_encoding,
"compose-encoding=s" => \$compose_encoding,
@@ -724,6 +742,16 @@ if ($dump_aliases) {
exit(0);
}
+if ($translate_aliases) {
+ while (<STDIN>) {
+ my @addr_list = parse_address_line($_);
+ @addr_list = expand_aliases(@addr_list);
+ @addr_list = sanitize_address_list(@addr_list);
+ print "$_\n" for @addr_list;
+ }
+ exit(0);
+}
+
# is_format_patch_arg($f) returns 0 if $f names a patch, or 1 if
# $f is a revision list specification to be passed to format-patch.
sub is_format_patch_arg {
@@ -1085,6 +1113,16 @@ if ($compose && $compose > 0) {
our ($message_id, %mail, $subject, $in_reply_to, $references, $message,
$needs_confirm, $message_num, $ask_default);
+sub mailmap_address_list {
+ return @_ unless @_ and $mailmap;
+ my @options = ();
+ push(@options, "--mailmap-file=$mailmap_file") if $mailmap_file;
+ push(@options, "--mailmap-blob=$mailmap_blob") if $mailmap_blob;
+ my @addr_list = Git::command('check-mailmap', @options, @_);
+ s/^<(.*)>$/$1/ for @addr_list;
+ return @addr_list;
+}
+
sub extract_valid_address {
my $address = shift;
my $local_part_regexp = qr/[^<>"\s@]+/;
@@ -1294,6 +1332,7 @@ sub process_address_list {
@addr_list = expand_aliases(@addr_list);
@addr_list = sanitize_address_list(@addr_list);
@addr_list = validate_address_list(@addr_list);
+ @addr_list = mailmap_address_list(@addr_list);
return @addr_list;
}
diff --git a/grep.c b/grep.c
index 2f8b9553df..e5761426e4 100644
--- a/grep.c
+++ b/grep.c
@@ -245,7 +245,7 @@ static int is_fixed(const char *s, size_t len)
#ifdef USE_LIBPCRE2
#define GREP_PCRE2_DEBUG_MALLOC 0
-static void *pcre2_malloc(PCRE2_SIZE size, MAYBE_UNUSED void *memory_data)
+static void *pcre2_malloc(PCRE2_SIZE size, void *memory_data UNUSED)
{
void *pointer = malloc(size);
#if GREP_PCRE2_DEBUG_MALLOC
@@ -255,7 +255,7 @@ static void *pcre2_malloc(PCRE2_SIZE size, MAYBE_UNUSED void *memory_data)
return pointer;
}
-static void pcre2_free(void *pointer, MAYBE_UNUSED void *memory_data)
+static void pcre2_free(void *pointer, void *memory_data UNUSED)
{
#if GREP_PCRE2_DEBUG_MALLOC
static int count = 1;
diff --git a/imap-send.c b/imap-send.c
index b2eb3d2dd0..2dd42807cd 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -192,7 +192,7 @@ static void socket_perror(const char *func, struct imap_socket *sock, int ret)
#ifdef NO_OPENSSL
static int ssl_socket_connect(struct imap_socket *sock UNUSED,
- const struct imap_server_conf *cfg,
+ const struct imap_server_conf *cfg UNUSED,
int use_tls_only UNUSED)
{
fprintf(stderr, "SSL requested but SSL support not compiled in\n");
diff --git a/mailinfo.c b/mailinfo.c
index 95228531a6..d1f42bd7e3 100644
--- a/mailinfo.c
+++ b/mailinfo.c
@@ -348,9 +348,8 @@ static void cleanup_subject(struct mailinfo *mi, struct strbuf *subject)
strbuf_trim(subject);
}
-#define MAX_HDR_PARSED 10
-static const char *header[MAX_HDR_PARSED] = {
- "From","Subject","Date",
+static const char * const header[] = {
+ "From", "Subject", "Date",
};
static inline int skip_header(const struct strbuf *line, const char *hdr,
@@ -585,7 +584,7 @@ static int check_header(struct mailinfo *mi,
struct strbuf sb = STRBUF_INIT;
/* search for the interesting parts */
- for (i = 0; header[i]; i++) {
+ for (i = 0; i < ARRAY_SIZE(header); i++) {
if ((!hdr_data[i] || overwrite) &&
parse_header(line, header[i], mi, &sb)) {
handle_header(&hdr_data[i], &sb);
@@ -627,7 +626,7 @@ static int is_inbody_header(const struct mailinfo *mi,
{
int i;
const char *val;
- for (i = 0; header[i]; i++)
+ for (i = 0; i < ARRAY_SIZE(header); i++)
if (!mi->s_hdr_data[i] && skip_header(line, header[i], &val))
return 1;
return 0;
@@ -774,7 +773,7 @@ static int check_inbody_header(struct mailinfo *mi, const struct strbuf *line)
return is_format_patch_separator(line->buf + 1, line->len - 1);
if (starts_with(line->buf, "[PATCH]") && isspace(line->buf[7])) {
int i;
- for (i = 0; header[i]; i++)
+ for (i = 0; i < ARRAY_SIZE(header); i++)
if (!strcmp("Subject", header[i])) {
handle_header(&mi->s_hdr_data[i], line);
return 1;
@@ -826,7 +825,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
* We may have already read "secondary headers"; purge
* them to give ourselves a clean restart.
*/
- for (i = 0; header[i]; i++) {
+ for (i = 0; i < ARRAY_SIZE(header); i++) {
if (mi->s_hdr_data[i])
strbuf_release(mi->s_hdr_data[i]);
FREE_AND_NULL(mi->s_hdr_data[i]);
@@ -1157,7 +1156,7 @@ static void handle_info(struct mailinfo *mi)
struct strbuf *hdr;
int i;
- for (i = 0; header[i]; i++) {
+ for (i = 0; i < ARRAY_SIZE(header); i++) {
/* only print inbody headers if we output a patch file */
if (mi->patch_lines && mi->s_hdr_data[i])
hdr = mi->s_hdr_data[i];
@@ -1208,8 +1207,8 @@ int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
return -1;
}
- mi->p_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*(mi->p_hdr_data)));
- mi->s_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*(mi->s_hdr_data)));
+ mi->p_hdr_data = xcalloc(ARRAY_SIZE(header), sizeof(*(mi->p_hdr_data)));
+ mi->s_hdr_data = xcalloc(ARRAY_SIZE(header), sizeof(*(mi->s_hdr_data)));
do {
peek = fgetc(mi->input);
@@ -1292,8 +1291,21 @@ void clear_mailinfo(struct mailinfo *mi)
strbuf_release(&mi->inbody_header_accum);
free(mi->message_id);
- strbuf_list_free(mi->p_hdr_data);
- strbuf_list_free(mi->s_hdr_data);
+ for (size_t i = 0; i < ARRAY_SIZE(header); i++) {
+ if (!mi->p_hdr_data[i])
+ continue;
+ strbuf_release(mi->p_hdr_data[i]);
+ free(mi->p_hdr_data[i]);
+ }
+ free(mi->p_hdr_data);
+
+ for (size_t i = 0; i < ARRAY_SIZE(header); i++) {
+ if (!mi->s_hdr_data[i])
+ continue;
+ strbuf_release(mi->s_hdr_data[i]);
+ free(mi->s_hdr_data[i]);
+ }
+ free(mi->s_hdr_data);
while (mi->content < mi->content_top) {
free(*(mi->content_top));
diff --git a/mailmap.c b/mailmap.c
index 2acf97f307..9f9fa3199a 100644
--- a/mailmap.c
+++ b/mailmap.c
@@ -142,11 +142,8 @@ static void read_mailmap_line(struct string_list *map, char *buffer)
add_mapping(map, name1, email1, name2, email2);
}
-/* Flags for read_mailmap_file() */
-#define MAILMAP_NOFOLLOW (1<<0)
-
-static int read_mailmap_file(struct string_list *map, const char *filename,
- unsigned flags)
+int read_mailmap_file(struct string_list *map, const char *filename,
+ unsigned flags)
{
char buffer[1024];
FILE *f;
@@ -186,7 +183,7 @@ static void read_mailmap_string(struct string_list *map, char *buf)
}
}
-static int read_mailmap_blob(struct string_list *map, const char *name)
+int read_mailmap_blob(struct string_list *map, const char *name)
{
struct object_id oid;
char *buf;
diff --git a/mailmap.h b/mailmap.h
index cbda9bc5e0..908365e1bf 100644
--- a/mailmap.h
+++ b/mailmap.h
@@ -6,6 +6,13 @@ struct string_list;
extern char *git_mailmap_file;
extern char *git_mailmap_blob;
+/* Flags for read_mailmap_file() */
+#define MAILMAP_NOFOLLOW (1<<0)
+
+int read_mailmap_file(struct string_list *map, const char *filename,
+ unsigned flags);
+int read_mailmap_blob(struct string_list *map, const char *name);
+
int read_mailmap(struct string_list *map);
void clear_mailmap(struct string_list *map);
diff --git a/mergetools/vscode b/mergetools/vscode
new file mode 100644
index 0000000000..3b39b458d6
--- /dev/null
+++ b/mergetools/vscode
@@ -0,0 +1,19 @@
+diff_cmd () {
+ "$merge_tool_path" --wait --diff "$LOCAL" "$REMOTE"
+}
+
+diff_cmd_help () {
+ echo "Use Visual Studio Code (requires a graphical session)"
+}
+
+merge_cmd () {
+ "$merge_tool_path" --wait --merge "$REMOTE" "$LOCAL" "$BASE" "$MERGED"
+}
+
+merge_cmd_help () {
+ echo "Use Visual Studio Code (requires a graphical session)"
+}
+
+translate_merge_tool_path () {
+ echo code
+}
diff --git a/midx-write.c b/midx-write.c
index e3fa33203f..1ef62c4f4b 100644
--- a/midx-write.c
+++ b/midx-write.c
@@ -858,10 +858,9 @@ static int write_midx_bitmap(const char *midx_name,
for (i = 0; i < pdata->nr_objects; i++)
index[i] = &pdata->objects[i].idx;
- bitmap_writer_init(&writer, the_repository);
+ bitmap_writer_init(&writer, the_repository, pdata);
bitmap_writer_show_progress(&writer, flags & MIDX_PROGRESS);
- bitmap_writer_build_type_index(&writer, pdata, index,
- pdata->nr_objects);
+ bitmap_writer_build_type_index(&writer, index);
/*
* bitmap_writer_finish expects objects in lex order, but pack_order
@@ -880,13 +879,12 @@ static int write_midx_bitmap(const char *midx_name,
index[pack_order[i]] = &pdata->objects[i].idx;
bitmap_writer_select_commits(&writer, commits, commits_nr);
- ret = bitmap_writer_build(&writer, pdata);
+ ret = bitmap_writer_build(&writer);
if (ret < 0)
goto cleanup;
bitmap_writer_set_checksum(&writer, midx_hash);
- bitmap_writer_finish(&writer, index, pdata->nr_objects, bitmap_name,
- options);
+ bitmap_writer_finish(&writer, index, bitmap_name, options);
cleanup:
free(index);
@@ -1308,6 +1306,18 @@ static int write_midx_internal(const char *object_dir,
pack_name_concat_len += MIDX_CHUNK_ALIGNMENT -
(pack_name_concat_len % MIDX_CHUNK_ALIGNMENT);
+ if (ctx.nr - dropped_packs == 0) {
+ error(_("no pack files to index."));
+ result = 1;
+ goto cleanup;
+ }
+
+ if (!ctx.entries_nr) {
+ if (flags & MIDX_WRITE_BITMAP)
+ warning(_("refusing to write multi-pack .bitmap without any objects"));
+ flags &= ~(MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP);
+ }
+
if (ctx.incremental) {
struct strbuf lock_name = STRBUF_INIT;
@@ -1333,18 +1343,6 @@ static int write_midx_internal(const char *object_dir,
f = hashfd(get_lock_file_fd(&lk), get_lock_file_path(&lk));
}
- if (ctx.nr - dropped_packs == 0) {
- error(_("no pack files to index."));
- result = 1;
- goto cleanup;
- }
-
- if (!ctx.entries_nr) {
- if (flags & MIDX_WRITE_BITMAP)
- warning(_("refusing to write multi-pack .bitmap without any objects"));
- flags &= ~(MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP);
- }
-
cf = init_chunkfile(f);
add_chunk(cf, MIDX_CHUNKID_PACKNAMES, pack_name_concat_len,
diff --git a/oss-fuzz/dummy-cmd-main.c b/oss-fuzz/dummy-cmd-main.c
index 071cb231ba..8ef776d06f 100644
--- a/oss-fuzz/dummy-cmd-main.c
+++ b/oss-fuzz/dummy-cmd-main.c
@@ -8,7 +8,7 @@
* executed.
*/
-int cmd_main(int argc, const char **argv) {
+int cmd_main(int argc UNUSED, const char **argv UNUSED) {
BUG("We should not execute cmd_main() from a fuzz target");
return 1;
}
diff --git a/pack-bitmap-write.c b/pack-bitmap-write.c
index bf96c80898..4dc0fe8e40 100644
--- a/pack-bitmap-write.c
+++ b/pack-bitmap-write.c
@@ -41,17 +41,19 @@ static inline int bitmap_writer_nr_selected_commits(struct bitmap_writer *writer
return writer->selected_nr - writer->pseudo_merges_nr;
}
-void bitmap_writer_init(struct bitmap_writer *writer, struct repository *r)
+void bitmap_writer_init(struct bitmap_writer *writer, struct repository *r,
+ struct packing_data *pdata)
{
memset(writer, 0, sizeof(struct bitmap_writer));
if (writer->bitmaps)
BUG("bitmap writer already initialized");
writer->bitmaps = kh_init_oid_map();
writer->pseudo_merge_commits = kh_init_oid_map();
+ writer->to_pack = pdata;
string_list_init_dup(&writer->pseudo_merge_groups);
- load_pseudo_merges_from_config(&writer->pseudo_merge_groups);
+ load_pseudo_merges_from_config(r, &writer->pseudo_merge_groups);
}
static void free_pseudo_merge_commit_idx(struct pseudo_merge_commit_idx *idx)
@@ -99,9 +101,7 @@ void bitmap_writer_show_progress(struct bitmap_writer *writer, int show)
* Build the initial type index for the packfile or multi-pack-index
*/
void bitmap_writer_build_type_index(struct bitmap_writer *writer,
- struct packing_data *to_pack,
- struct pack_idx_entry **index,
- uint32_t index_nr)
+ struct pack_idx_entry **index)
{
uint32_t i;
@@ -109,13 +109,13 @@ void bitmap_writer_build_type_index(struct bitmap_writer *writer,
writer->trees = ewah_new();
writer->blobs = ewah_new();
writer->tags = ewah_new();
- ALLOC_ARRAY(to_pack->in_pack_pos, to_pack->nr_objects);
+ ALLOC_ARRAY(writer->to_pack->in_pack_pos, writer->to_pack->nr_objects);
- for (i = 0; i < index_nr; ++i) {
+ for (i = 0; i < writer->to_pack->nr_objects; ++i) {
struct object_entry *entry = (struct object_entry *)index[i];
enum object_type real_type;
- oe_set_in_pack_pos(to_pack, entry, i);
+ oe_set_in_pack_pos(writer->to_pack, entry, i);
switch (oe_type(entry)) {
case OBJ_COMMIT:
@@ -126,7 +126,7 @@ void bitmap_writer_build_type_index(struct bitmap_writer *writer,
break;
default:
- real_type = oid_object_info(to_pack->repo,
+ real_type = oid_object_info(writer->to_pack->repo,
&entry->idx.oid, NULL);
break;
}
@@ -569,8 +569,7 @@ static void store_selected(struct bitmap_writer *writer,
kh_value(writer->bitmaps, hash_pos) = stored;
}
-int bitmap_writer_build(struct bitmap_writer *writer,
- struct packing_data *to_pack)
+int bitmap_writer_build(struct bitmap_writer *writer)
{
struct bitmap_builder bb;
size_t i;
@@ -581,17 +580,15 @@ int bitmap_writer_build(struct bitmap_writer *writer,
uint32_t *mapping;
int closed = 1; /* until proven otherwise */
- writer->to_pack = to_pack;
-
if (writer->show_progress)
writer->progress = start_progress("Building bitmaps",
writer->selected_nr);
trace2_region_enter("pack-bitmap-write", "building_bitmaps_total",
the_repository);
- old_bitmap = prepare_bitmap_git(to_pack->repo);
+ old_bitmap = prepare_bitmap_git(writer->to_pack->repo);
if (old_bitmap)
- mapping = create_bitmap_mapping(old_bitmap, to_pack);
+ mapping = create_bitmap_mapping(old_bitmap, writer->to_pack);
else
mapping = NULL;
@@ -697,6 +694,9 @@ void bitmap_writer_select_commits(struct bitmap_writer *writer,
if (indexed_commits_nr < 100) {
for (i = 0; i < indexed_commits_nr; ++i)
bitmap_writer_push_commit(writer, indexed_commits[i], 0);
+
+ select_pseudo_merges(writer);
+
return;
}
@@ -737,7 +737,7 @@ void bitmap_writer_select_commits(struct bitmap_writer *writer,
stop_progress(&writer->progress);
- select_pseudo_merges(writer, indexed_commits, indexed_commits_nr);
+ select_pseudo_merges(writer);
}
@@ -1001,7 +1001,6 @@ void bitmap_writer_set_checksum(struct bitmap_writer *writer,
void bitmap_writer_finish(struct bitmap_writer *writer,
struct pack_idx_entry **index,
- uint32_t index_nr,
const char *filename,
uint16_t options)
{
@@ -1034,12 +1033,13 @@ void bitmap_writer_finish(struct bitmap_writer *writer,
dump_bitmap(f, writer->tags);
if (options & BITMAP_OPT_LOOKUP_TABLE)
- CALLOC_ARRAY(offsets, index_nr);
+ CALLOC_ARRAY(offsets, writer->to_pack->nr_objects);
for (i = 0; i < bitmap_writer_nr_selected_commits(writer); i++) {
struct bitmapped_commit *stored = &writer->selected[i];
int commit_pos = oid_pos(&stored->commit->object.oid, index,
- index_nr, oid_access);
+ writer->to_pack->nr_objects,
+ oid_access);
if (commit_pos < 0)
BUG(_("trying to write commit not in index"));
@@ -1055,7 +1055,7 @@ void bitmap_writer_finish(struct bitmap_writer *writer,
write_lookup_table(writer, f, offsets);
if (options & BITMAP_OPT_HASH_CACHE)
- write_hash_cache(f, index, index_nr);
+ write_hash_cache(f, index, writer->to_pack->nr_objects);
finalize_hashfile(f, NULL, FSYNC_COMPONENT_PACK_METADATA,
CSUM_HASH_IN_STREAM | CSUM_FSYNC | CSUM_CLOSE);
diff --git a/pack-bitmap.h b/pack-bitmap.h
index 1171e6d989..ff0fd815b8 100644
--- a/pack-bitmap.h
+++ b/pack-bitmap.h
@@ -123,14 +123,13 @@ struct bitmap_writer {
unsigned char pack_checksum[GIT_MAX_RAWSZ];
};
-void bitmap_writer_init(struct bitmap_writer *writer, struct repository *r);
+void bitmap_writer_init(struct bitmap_writer *writer, struct repository *r,
+ struct packing_data *pdata);
void bitmap_writer_show_progress(struct bitmap_writer *writer, int show);
void bitmap_writer_set_checksum(struct bitmap_writer *writer,
const unsigned char *sha1);
void bitmap_writer_build_type_index(struct bitmap_writer *writer,
- struct packing_data *to_pack,
- struct pack_idx_entry **index,
- uint32_t index_nr);
+ struct pack_idx_entry **index);
int bitmap_writer_has_bitmapped_object_id(struct bitmap_writer *writer,
const struct object_id *oid);
void bitmap_writer_push_commit(struct bitmap_writer *writer,
@@ -147,11 +146,9 @@ struct ewah_bitmap *pseudo_merge_bitmap_for_commit(struct bitmap_index *bitmap_g
void bitmap_writer_select_commits(struct bitmap_writer *writer,
struct commit **indexed_commits,
unsigned int indexed_commits_nr);
-int bitmap_writer_build(struct bitmap_writer *writer,
- struct packing_data *to_pack);
+int bitmap_writer_build(struct bitmap_writer *writer);
void bitmap_writer_finish(struct bitmap_writer *writer,
struct pack_idx_entry **index,
- uint32_t index_nr,
const char *filename,
uint16_t options);
void bitmap_writer_free(struct bitmap_writer *writer);
diff --git a/pager.c b/pager.c
index 896f40fcd2..9c24ce6263 100644
--- a/pager.c
+++ b/pager.c
@@ -234,6 +234,8 @@ int term_columns(void)
*/
void term_clear_line(void)
{
+ if (!isatty(2))
+ return;
if (is_terminal_dumb())
/*
* Fall back to print a terminal width worth of space
diff --git a/pretty.c b/pretty.c
index 44222fb83c..5e5ae45253 100644
--- a/pretty.c
+++ b/pretty.c
@@ -63,7 +63,7 @@ static int git_pretty_formats_config(const char *var, const char *value,
void *cb UNUSED)
{
struct cmt_fmt_map *commit_format = NULL;
- const char *name;
+ const char *name, *stripped;
char *fmt;
int i;
@@ -90,15 +90,21 @@ static int git_pretty_formats_config(const char *var, const char *value,
commit_formats_len++;
}
+ free((char *)commit_format->name);
commit_format->name = xstrdup(name);
commit_format->format = CMIT_FMT_USERFORMAT;
if (git_config_string(&fmt, var, value))
return -1;
- if (skip_prefix(fmt, "format:", &commit_format->user_format)) {
+ free((char *)commit_format->user_format);
+ if (skip_prefix(fmt, "format:", &stripped)) {
commit_format->is_tformat = 0;
- } else if (skip_prefix(fmt, "tformat:", &commit_format->user_format)) {
+ commit_format->user_format = xstrdup(stripped);
+ free(fmt);
+ } else if (skip_prefix(fmt, "tformat:", &stripped)) {
commit_format->is_tformat = 1;
+ commit_format->user_format = xstrdup(stripped);
+ free(fmt);
} else if (strchr(fmt, '%')) {
commit_format->is_tformat = 1;
commit_format->user_format = fmt;
@@ -1770,6 +1776,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
}
trailer_out:
string_list_clear(&filter_list, 0);
+ strbuf_release(&kvsepbuf);
strbuf_release(&sepbuf);
return ret;
}
diff --git a/pseudo-merge.c b/pseudo-merge.c
index 77a83b9c5c..10ebd9a4e9 100644
--- a/pseudo-merge.c
+++ b/pseudo-merge.c
@@ -183,11 +183,12 @@ done:
return ret;
}
-void load_pseudo_merges_from_config(struct string_list *list)
+void load_pseudo_merges_from_config(struct repository *r,
+ struct string_list *list)
{
struct string_list_item *item;
- git_config(pseudo_merge_config, list);
+ repo_config(r, pseudo_merge_config, list);
for_each_string_list_item(item, list) {
struct pseudo_merge_group *group = item->util;
@@ -218,6 +219,8 @@ static int find_pseudo_merge_group_for_ref(const char *refname,
c = lookup_commit(the_repository, oid);
if (!c)
return 0;
+ if (!packlist_find(writer->to_pack, oid))
+ return 0;
has_bitmap = bitmap_writer_has_bitmapped_object_id(writer, oid);
@@ -358,8 +361,10 @@ static void select_pseudo_merges_1(struct bitmap_writer *writer,
p = commit_list_append(c, p);
} while (j % group->stable_size);
- bitmap_writer_push_commit(writer, merge, 1);
- writer->pseudo_merges_nr++;
+ if (merge->parents) {
+ bitmap_writer_push_commit(writer, merge, 1);
+ writer->pseudo_merges_nr++;
+ }
}
/* make up to group->max_merges pseudo merges for unstable commits */
@@ -399,8 +404,9 @@ static void select_pseudo_merges_1(struct bitmap_writer *writer,
p = commit_list_append(c, p);
}
- bitmap_writer_push_commit(writer, merge, 1);
- writer->pseudo_merges_nr++;
+ if (merge->parents) {
+ bitmap_writer_push_commit(writer, merge, 1);
+ writer->pseudo_merges_nr++; }
if (end >= matches->unstable_nr)
break;
}
@@ -424,8 +430,7 @@ static void sort_pseudo_merge_matches(struct pseudo_merge_matches *matches)
QSORT(matches->unstable, matches->unstable_nr, commit_date_cmp);
}
-void select_pseudo_merges(struct bitmap_writer *writer,
- struct commit **commits, size_t commits_nr)
+void select_pseudo_merges(struct bitmap_writer *writer)
{
struct progress *progress = NULL;
uint32_t i;
diff --git a/pseudo-merge.h b/pseudo-merge.h
index 2aca01d056..4b5febaa63 100644
--- a/pseudo-merge.h
+++ b/pseudo-merge.h
@@ -10,6 +10,7 @@ struct commit;
struct string_list;
struct bitmap_index;
struct bitmap_writer;
+struct repository;
/*
* A pseudo-merge group tracks the set of non-bitmapped reference tips
@@ -72,7 +73,7 @@ struct pseudo_merge_matches {
* entry keys are the pseudo-merge group names, and the values are
* pointers to the pseudo_merge_group structure itself.
*/
-void load_pseudo_merges_from_config(struct string_list *list);
+void load_pseudo_merges_from_config(struct repository *r, struct string_list *list);
/*
* A pseudo-merge commit index (pseudo_merge_commit_idx) maps a
@@ -94,8 +95,7 @@ struct pseudo_merge_commit_idx {
*
* Optionally shows a progress meter.
*/
-void select_pseudo_merges(struct bitmap_writer *writer,
- struct commit **commits, size_t commits_nr);
+void select_pseudo_merges(struct bitmap_writer *writer);
/*
* Represents a serialized view of a file containing pseudo-merge(s)
diff --git a/read-cache.c b/read-cache.c
index 5464398162..4e67dc182e 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -3195,18 +3195,24 @@ static int write_split_index(struct index_state *istate,
return ret;
}
-static const char *shared_index_expire = "2.weeks.ago";
-
static unsigned long get_shared_index_expire_date(void)
{
static unsigned long shared_index_expire_date;
static int shared_index_expire_date_prepared;
if (!shared_index_expire_date_prepared) {
+ const char *shared_index_expire = "2.weeks.ago";
+ char *value = NULL;
+
repo_config_get_expiry(the_repository, "splitindex.sharedindexexpire",
- &shared_index_expire);
+ &value);
+ if (value)
+ shared_index_expire = value;
+
shared_index_expire_date = approxidate(shared_index_expire);
shared_index_expire_date_prepared = 1;
+
+ free(value);
}
return shared_index_expire_date;
diff --git a/ref-filter.c b/ref-filter.c
index 6d8b591930..b6c6c10127 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -169,6 +169,7 @@ enum atom_type {
ATOM_ELSE,
ATOM_REST,
ATOM_AHEADBEHIND,
+ ATOM_ISBASE,
};
/*
@@ -742,8 +743,7 @@ static int person_name_atom_parser(struct ref_format *format UNUSED,
return 0;
}
-static int email_atom_option_parser(struct used_atom *atom,
- const char **arg, struct strbuf *err)
+static int email_atom_option_parser(const char **arg)
{
if (!*arg)
return EO_RAW;
@@ -761,7 +761,7 @@ static int person_email_atom_parser(struct ref_format *format UNUSED,
const char *arg, struct strbuf *err)
{
for (;;) {
- int opt = email_atom_option_parser(atom, &arg, err);
+ int opt = email_atom_option_parser(&arg);
const char *bad_arg = arg;
if (opt < 0)
@@ -891,6 +891,23 @@ static int ahead_behind_atom_parser(struct ref_format *format,
return 0;
}
+static int is_base_atom_parser(struct ref_format *format,
+ struct used_atom *atom UNUSED,
+ const char *arg, struct strbuf *err)
+{
+ struct string_list_item *item;
+
+ if (!arg)
+ return strbuf_addf_ret(err, -1, _("expected format: %%(is-base:<committish>)"));
+
+ item = string_list_append(&format->is_base_tips, arg);
+ item->util = lookup_commit_reference_by_name(arg);
+ if (!item->util)
+ die("failed to find '%s'", arg);
+
+ return 0;
+}
+
static int head_atom_parser(struct ref_format *format UNUSED,
struct used_atom *atom,
const char *arg, struct strbuf *err)
@@ -956,6 +973,7 @@ static struct {
[ATOM_ELSE] = { "else", SOURCE_NONE },
[ATOM_REST] = { "rest", SOURCE_NONE, FIELD_STR, rest_atom_parser },
[ATOM_AHEADBEHIND] = { "ahead-behind", SOURCE_OTHER, FIELD_STR, ahead_behind_atom_parser },
+ [ATOM_ISBASE] = { "is-base", SOURCE_OTHER, FIELD_STR, is_base_atom_parser },
/*
* Please update $__git_ref_fieldlist in git-completion.bash
* when you add new atoms
@@ -2341,6 +2359,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
int i;
struct object_info empty = OBJECT_INFO_INIT;
int ahead_behind_atoms = 0;
+ int is_base_atoms = 0;
CALLOC_ARRAY(ref->value, used_atom_cnt);
@@ -2490,6 +2509,15 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
v->s = xstrdup("");
}
continue;
+ } else if (atom_type == ATOM_ISBASE) {
+ if (ref->is_base && ref->is_base[is_base_atoms]) {
+ v->s = xstrfmt("(%s)", ref->is_base[is_base_atoms]);
+ free(ref->is_base[is_base_atoms]);
+ } else {
+ v->s = xstrdup("");
+ }
+ is_base_atoms++;
+ continue;
} else
continue;
@@ -2896,6 +2924,7 @@ static void free_array_item(struct ref_array_item *item)
free(item->value);
}
free(item->counts);
+ free(item->is_base);
free(item);
}
@@ -3060,6 +3089,49 @@ void filter_ahead_behind(struct repository *r,
free(commits);
}
+void filter_is_base(struct repository *r,
+ struct ref_format *format,
+ struct ref_array *array)
+{
+ struct commit **bases;
+ size_t bases_nr = 0;
+ struct ref_array_item **back_index;
+
+ if (!format->is_base_tips.nr || !array->nr)
+ return;
+
+ CALLOC_ARRAY(back_index, array->nr);
+ CALLOC_ARRAY(bases, array->nr);
+
+ for (size_t i = 0; i < array->nr; i++) {
+ const char *name = array->items[i]->refname;
+ struct commit *c = lookup_commit_reference_by_name_gently(name, 1);
+
+ CALLOC_ARRAY(array->items[i]->is_base, format->is_base_tips.nr);
+
+ if (!c)
+ continue;
+
+ back_index[bases_nr] = array->items[i];
+ bases[bases_nr] = c;
+ bases_nr++;
+ }
+
+ for (size_t i = 0; i < format->is_base_tips.nr; i++) {
+ struct commit *tip = format->is_base_tips.items[i].util;
+ int base_index = get_branch_base_for_tip(r, tip, bases, bases_nr);
+
+ if (base_index < 0)
+ continue;
+
+ /* Store the string for use in output later. */
+ back_index[base_index]->is_base[i] = xstrdup(format->is_base_tips.items[i].string);
+ }
+
+ free(back_index);
+ free(bases);
+}
+
static int do_filter_refs(struct ref_filter *filter, unsigned int type, each_ref_fn fn, void *cb_data)
{
int ret = 0;
@@ -3153,7 +3225,8 @@ static inline int can_do_iterative_format(struct ref_filter *filter,
return !(filter->reachable_from ||
filter->unreachable_from ||
sorting ||
- format->bases.nr);
+ format->bases.nr ||
+ format->is_base_tips.nr);
}
void filter_and_format_refs(struct ref_filter *filter, unsigned int type,
@@ -3177,6 +3250,7 @@ void filter_and_format_refs(struct ref_filter *filter, unsigned int type,
struct ref_array array = { 0 };
filter_refs(&array, filter, type);
filter_ahead_behind(the_repository, format, &array);
+ filter_is_base(the_repository, format, &array);
ref_array_sort(sorting, &array);
print_formatted_ref_array(&array, format);
ref_array_clear(&array);
diff --git a/ref-filter.h b/ref-filter.h
index 27ae1aa0d1..e794b8a676 100644
--- a/ref-filter.h
+++ b/ref-filter.h
@@ -48,6 +48,7 @@ struct ref_array_item {
struct commit *commit;
struct atom_value *value;
struct ahead_behind_count **counts;
+ char **is_base;
char refname[FLEX_ARRAY];
};
@@ -101,6 +102,9 @@ struct ref_format {
/* List of bases for ahead-behind counts. */
struct string_list bases;
+ /* List of bases for is-base indicators. */
+ struct string_list is_base_tips;
+
struct {
int max_count;
int omit_empty;
@@ -114,6 +118,7 @@ struct ref_format {
#define REF_FORMAT_INIT { \
.use_color = -1, \
.bases = STRING_LIST_INIT_DUP, \
+ .is_base_tips = STRING_LIST_INIT_DUP, \
}
/* Macros for checking --merged and --no-merged options */
@@ -203,6 +208,16 @@ void filter_ahead_behind(struct repository *r,
struct ref_format *format,
struct ref_array *array);
+/*
+ * If the provided format includes is-base atoms, then compute the base checks
+ * for those tips against all refs.
+ *
+ * If this is not called, then any is-base atoms will be blank.
+ */
+void filter_is_base(struct repository *r,
+ struct ref_format *format,
+ struct ref_array *array);
+
void ref_filter_init(struct ref_filter *filter);
void ref_filter_clear(struct ref_filter *filter);
diff --git a/refs.c b/refs.c
index 57ffd60be0..ceb72d4bd7 100644
--- a/refs.c
+++ b/refs.c
@@ -2392,9 +2392,10 @@ struct do_for_each_reflog_help {
void *cb_data;
};
-static int do_for_each_reflog_helper(const char *refname, const char *referent,
+static int do_for_each_reflog_helper(const char *refname,
+ const char *referent UNUSED,
const struct object_id *oid UNUSED,
- int flags,
+ int flags UNUSED,
void *cb_data)
{
struct do_for_each_reflog_help *hp = cb_data;
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 8d6ec9458d..4c5573b19c 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1946,10 +1946,13 @@ static int commit_ref_update(struct files_ref_store *refs,
return 0;
}
+#ifdef NO_SYMLINK_HEAD
+#define create_ref_symlink(a, b) (-1)
+#else
static int create_ref_symlink(struct ref_lock *lock, const char *target)
{
int ret = -1;
-#ifndef NO_SYMLINK_HEAD
+
char *ref_path = get_locked_file_path(&lock->lk);
unlink(ref_path);
ret = symlink(target, ref_path);
@@ -1957,13 +1960,12 @@ static int create_ref_symlink(struct ref_lock *lock, const char *target)
if (ret)
fprintf(stderr, "no symlink - falling back to symbolic ref\n");
-#endif
return ret;
}
+#endif
-static int create_symref_lock(struct files_ref_store *refs,
- struct ref_lock *lock, const char *refname,
- const char *target, struct strbuf *err)
+static int create_symref_lock(struct ref_lock *lock, const char *target,
+ struct strbuf *err)
{
if (!fdopen_lock_file(&lock->lk, "w")) {
strbuf_addf(err, "unable to fdopen %s: %s",
@@ -2579,8 +2581,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
}
if (update->new_target && !(update->flags & REF_LOG_ONLY)) {
- if (create_symref_lock(refs, lock, update->refname,
- update->new_target, err)) {
+ if (create_symref_lock(lock, update->new_target, err)) {
ret = TRANSACTION_GENERIC_ERROR;
goto out;
}
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index 3ab7e99a88..7a0a695ca2 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -1735,8 +1735,8 @@ static struct ref_iterator *packed_reflog_iterator_begin(struct ref_store *ref_s
return empty_ref_iterator_begin();
}
-static int packed_fsck(struct ref_store *ref_store,
- struct fsck_options *o)
+static int packed_fsck(struct ref_store *ref_store UNUSED,
+ struct fsck_options *o UNUSED)
{
return 0;
}
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index ef0f9b0d42..1c4b19e737 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -616,7 +616,7 @@ done:
static struct ref_iterator *reftable_be_iterator_begin(struct ref_store *ref_store,
const char *prefix,
- const char **exclude_patterns,
+ const char **exclude_patterns UNUSED,
unsigned int flags)
{
struct reftable_ref_iterator *main_iter, *worktree_iter;
@@ -1125,9 +1125,9 @@ done:
return ret;
}
-static int reftable_be_transaction_abort(struct ref_store *ref_store,
+static int reftable_be_transaction_abort(struct ref_store *ref_store UNUSED,
struct ref_transaction *transaction,
- struct strbuf *err)
+ struct strbuf *err UNUSED)
{
struct reftable_transaction_data *tx_data = transaction->backend_data;
free_transaction_data(tx_data);
@@ -1317,7 +1317,7 @@ done:
return ret;
}
-static int reftable_be_transaction_finish(struct ref_store *ref_store,
+static int reftable_be_transaction_finish(struct ref_store *ref_store UNUSED,
struct ref_transaction *transaction,
struct strbuf *err)
{
@@ -1728,8 +1728,8 @@ static int reftable_reflog_iterator_advance(struct ref_iterator *ref_iterator)
return ITER_OK;
}
-static int reftable_reflog_iterator_peel(struct ref_iterator *ref_iterator,
- struct object_id *peeled)
+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;
@@ -1990,7 +1990,7 @@ done:
static int reftable_be_create_reflog(struct ref_store *ref_store,
const char *refname,
- struct strbuf *errmsg)
+ struct strbuf *errmsg UNUSED)
{
struct reftable_ref_store *refs =
reftable_be_downcast(ref_store, REF_STORE_WRITE, "create_reflog");
@@ -2311,8 +2311,8 @@ done:
return ret;
}
-static int reftable_be_fsck(struct ref_store *ref_store,
- struct fsck_options *o)
+static int reftable_be_fsck(struct ref_store *ref_store UNUSED,
+ struct fsck_options *o UNUSED)
{
return 0;
}
diff --git a/reftable/block_test.c b/reftable/block_test.c
deleted file mode 100644
index 90aecd5a7c..0000000000
--- a/reftable/block_test.c
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
-Copyright 2020 Google LLC
-
-Use of this source code is governed by a BSD-style
-license that can be found in the LICENSE file or at
-https://developers.google.com/open-source/licenses/bsd
-*/
-
-#include "block.h"
-
-#include "system.h"
-#include "blocksource.h"
-#include "basics.h"
-#include "constants.h"
-#include "record.h"
-#include "test_framework.h"
-#include "reftable-tests.h"
-
-static void test_block_read_write(void)
-{
- const int header_off = 21; /* random */
- char *names[30];
- const int N = ARRAY_SIZE(names);
- const int block_size = 1024;
- struct reftable_block block = { NULL };
- struct block_writer bw = {
- .last_key = STRBUF_INIT,
- };
- struct reftable_record rec = {
- .type = BLOCK_TYPE_REF,
- };
- int i = 0;
- int n;
- struct block_reader br = { 0 };
- struct block_iter it = BLOCK_ITER_INIT;
- int j = 0;
- struct strbuf want = STRBUF_INIT;
-
- REFTABLE_CALLOC_ARRAY(block.data, block_size);
- block.len = block_size;
- block.source = malloc_block_source();
- block_writer_init(&bw, BLOCK_TYPE_REF, block.data, block_size,
- header_off, hash_size(GIT_SHA1_FORMAT_ID));
-
- rec.u.ref.refname = (char *) "";
- rec.u.ref.value_type = REFTABLE_REF_DELETION;
- n = block_writer_add(&bw, &rec);
- EXPECT(n == REFTABLE_API_ERROR);
-
- for (i = 0; i < N; i++) {
- char name[100];
- snprintf(name, sizeof(name), "branch%02d", i);
-
- rec.u.ref.refname = name;
- rec.u.ref.value_type = REFTABLE_REF_VAL1;
- memset(rec.u.ref.value.val1, i, GIT_SHA1_RAWSZ);
-
- names[i] = xstrdup(name);
- n = block_writer_add(&bw, &rec);
- rec.u.ref.refname = NULL;
- rec.u.ref.value_type = REFTABLE_REF_DELETION;
- EXPECT(n == 0);
- }
-
- n = block_writer_finish(&bw);
- EXPECT(n > 0);
-
- block_writer_release(&bw);
-
- block_reader_init(&br, &block, header_off, block_size, GIT_SHA1_RAWSZ);
-
- block_iter_seek_start(&it, &br);
-
- while (1) {
- int r = block_iter_next(&it, &rec);
- EXPECT(r >= 0);
- if (r > 0) {
- break;
- }
- EXPECT_STREQ(names[j], rec.u.ref.refname);
- j++;
- }
-
- reftable_record_release(&rec);
- block_iter_close(&it);
-
- for (i = 0; i < N; i++) {
- struct block_iter it = BLOCK_ITER_INIT;
- strbuf_reset(&want);
- strbuf_addstr(&want, names[i]);
-
- n = block_iter_seek_key(&it, &br, &want);
- EXPECT(n == 0);
-
- n = block_iter_next(&it, &rec);
- EXPECT(n == 0);
-
- EXPECT_STREQ(names[i], rec.u.ref.refname);
-
- want.len--;
- n = block_iter_seek_key(&it, &br, &want);
- EXPECT(n == 0);
-
- n = block_iter_next(&it, &rec);
- EXPECT(n == 0);
- EXPECT_STREQ(names[10 * (i / 10)], rec.u.ref.refname);
-
- block_iter_close(&it);
- }
-
- reftable_record_release(&rec);
- reftable_block_done(&br.block);
- strbuf_release(&want);
- for (i = 0; i < N; i++) {
- reftable_free(names[i]);
- }
-}
-
-int block_test_main(int argc, const char *argv[])
-{
- RUN_TEST(test_block_read_write);
- return 0;
-}
diff --git a/reftable/blocksource.c b/reftable/blocksource.c
index eeed254ba9..e93cac9bb6 100644
--- a/reftable/blocksource.c
+++ b/reftable/blocksource.c
@@ -13,14 +13,14 @@ https://developers.google.com/open-source/licenses/bsd
#include "reftable-blocksource.h"
#include "reftable-error.h"
-static void strbuf_return_block(void *b, struct reftable_block *dest)
+static void strbuf_return_block(void *b UNUSED, struct reftable_block *dest)
{
if (dest->len)
memset(dest->data, 0xff, dest->len);
reftable_free(dest->data);
}
-static void strbuf_close(void *b)
+static void strbuf_close(void *b UNUSED)
{
}
@@ -55,26 +55,6 @@ void block_source_from_strbuf(struct reftable_block_source *bs,
bs->arg = buf;
}
-static void malloc_return_block(void *b, struct reftable_block *dest)
-{
- if (dest->len)
- memset(dest->data, 0xff, dest->len);
- reftable_free(dest->data);
-}
-
-static struct reftable_block_source_vtable malloc_vtable = {
- .return_block = &malloc_return_block,
-};
-
-static struct reftable_block_source malloc_block_source_instance = {
- .ops = &malloc_vtable,
-};
-
-struct reftable_block_source malloc_block_source(void)
-{
- return malloc_block_source_instance;
-}
-
struct file_block_source {
uint64_t size;
unsigned char *data;
@@ -85,7 +65,7 @@ static uint64_t file_size(void *b)
return ((struct file_block_source *)b)->size;
}
-static void file_return_block(void *b, struct reftable_block *dest)
+static void file_return_block(void *b UNUSED, struct reftable_block *dest UNUSED)
{
}
diff --git a/reftable/blocksource.h b/reftable/blocksource.h
index 072e2727ad..659a27b406 100644
--- a/reftable/blocksource.h
+++ b/reftable/blocksource.h
@@ -17,6 +17,4 @@ struct reftable_block_source;
void block_source_from_strbuf(struct reftable_block_source *bs,
struct strbuf *buf);
-struct reftable_block_source malloc_block_source(void);
-
#endif
diff --git a/reftable/dump.c b/reftable/dump.c
deleted file mode 100644
index dd65d9e8bb..0000000000
--- a/reftable/dump.c
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
-Copyright 2020 Google LLC
-
-Use of this source code is governed by a BSD-style
-license that can be found in the LICENSE file or at
-https://developers.google.com/open-source/licenses/bsd
-*/
-
-#include "git-compat-util.h"
-#include "hash.h"
-
-#include "reftable-blocksource.h"
-#include "reftable-error.h"
-#include "reftable-record.h"
-#include "reftable-tests.h"
-#include "reftable-writer.h"
-#include "reftable-iterator.h"
-#include "reftable-reader.h"
-#include "reftable-stack.h"
-
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-
-static int compact_stack(const char *stackdir)
-{
- struct reftable_stack *stack = NULL;
- struct reftable_write_options opts = { 0 };
-
- int err = reftable_new_stack(&stack, stackdir, &opts);
- if (err < 0)
- goto done;
-
- err = reftable_stack_compact_all(stack, NULL);
- if (err < 0)
- goto done;
-done:
- if (stack) {
- reftable_stack_destroy(stack);
- }
- return err;
-}
-
-static void print_help(void)
-{
- printf("usage: dump [-cst] arg\n\n"
- "options: \n"
- " -c compact\n"
- " -b dump blocks\n"
- " -t dump table\n"
- " -s dump stack\n"
- " -6 sha256 hash format\n"
- " -h this help\n"
- "\n");
-}
-
-int reftable_dump_main(int argc, char *const *argv)
-{
- int err = 0;
- int opt_dump_blocks = 0;
- int opt_dump_table = 0;
- int opt_dump_stack = 0;
- int opt_compact = 0;
- uint32_t opt_hash_id = GIT_SHA1_FORMAT_ID;
- const char *arg = NULL, *argv0 = argv[0];
-
- for (; argc > 1; argv++, argc--)
- if (*argv[1] != '-')
- break;
- else if (!strcmp("-b", argv[1]))
- opt_dump_blocks = 1;
- else if (!strcmp("-t", argv[1]))
- opt_dump_table = 1;
- else if (!strcmp("-6", argv[1]))
- opt_hash_id = GIT_SHA256_FORMAT_ID;
- else if (!strcmp("-s", argv[1]))
- opt_dump_stack = 1;
- else if (!strcmp("-c", argv[1]))
- opt_compact = 1;
- else if (!strcmp("-?", argv[1]) || !strcmp("-h", argv[1])) {
- print_help();
- return 2;
- }
-
- if (argc != 2) {
- fprintf(stderr, "need argument\n");
- print_help();
- return 2;
- }
-
- arg = argv[1];
-
- if (opt_dump_blocks) {
- err = reftable_reader_print_blocks(arg);
- } else if (opt_dump_table) {
- err = reftable_reader_print_file(arg);
- } else if (opt_dump_stack) {
- err = reftable_stack_print_directory(arg, opt_hash_id);
- } else if (opt_compact) {
- err = compact_stack(arg);
- }
-
- if (err < 0) {
- fprintf(stderr, "%s: %s: %s\n", argv0, arg,
- reftable_error_str(err));
- return 1;
- }
- return 0;
-}
diff --git a/reftable/generic.c b/reftable/generic.c
deleted file mode 100644
index 28ae26145e..0000000000
--- a/reftable/generic.c
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
-Copyright 2020 Google LLC
-
-Use of this source code is governed by a BSD-style
-license that can be found in the LICENSE file or at
-https://developers.google.com/open-source/licenses/bsd
-*/
-
-#include "constants.h"
-#include "record.h"
-#include "generic.h"
-#include "reftable-iterator.h"
-#include "reftable-generic.h"
-
-void table_init_iter(struct reftable_table *tab,
- struct reftable_iterator *it,
- uint8_t typ)
-{
-
- tab->ops->init_iter(tab->table_arg, it, typ);
-}
-
-void reftable_table_init_ref_iter(struct reftable_table *tab,
- struct reftable_iterator *it)
-{
- table_init_iter(tab, it, BLOCK_TYPE_REF);
-}
-
-void reftable_table_init_log_iter(struct reftable_table *tab,
- struct reftable_iterator *it)
-{
- table_init_iter(tab, it, BLOCK_TYPE_LOG);
-}
-
-int reftable_iterator_seek_ref(struct reftable_iterator *it,
- const char *name)
-{
- struct reftable_record want = {
- .type = BLOCK_TYPE_REF,
- .u.ref = {
- .refname = (char *)name,
- },
- };
- return it->ops->seek(it->iter_arg, &want);
-}
-
-int reftable_iterator_seek_log_at(struct reftable_iterator *it,
- const char *name, uint64_t update_index)
-{
- struct reftable_record want = {
- .type = BLOCK_TYPE_LOG,
- .u.log = {
- .refname = (char *)name,
- .update_index = update_index,
- },
- };
- return it->ops->seek(it->iter_arg, &want);
-}
-
-int reftable_iterator_seek_log(struct reftable_iterator *it,
- const char *name)
-{
- return reftable_iterator_seek_log_at(it, name, ~((uint64_t) 0));
-}
-
-int reftable_table_read_ref(struct reftable_table *tab, const char *name,
- struct reftable_ref_record *ref)
-{
- struct reftable_iterator it = { NULL };
- int err;
-
- reftable_table_init_ref_iter(tab, &it);
-
- err = reftable_iterator_seek_ref(&it, name);
- if (err)
- goto done;
-
- err = reftable_iterator_next_ref(&it, ref);
- if (err)
- goto done;
-
- if (strcmp(ref->refname, name) ||
- reftable_ref_record_is_deletion(ref)) {
- reftable_ref_record_release(ref);
- err = 1;
- goto done;
- }
-
-done:
- reftable_iterator_destroy(&it);
- return err;
-}
-
-int reftable_table_print(struct reftable_table *tab) {
- struct reftable_iterator it = { NULL };
- struct reftable_ref_record ref = { NULL };
- struct reftable_log_record log = { NULL };
- uint32_t hash_id = reftable_table_hash_id(tab);
- int err;
-
- reftable_table_init_ref_iter(tab, &it);
-
- err = reftable_iterator_seek_ref(&it, "");
- if (err < 0)
- return err;
-
- while (1) {
- err = reftable_iterator_next_ref(&it, &ref);
- if (err > 0) {
- break;
- }
- if (err < 0) {
- return err;
- }
- reftable_ref_record_print(&ref, hash_id);
- }
- reftable_iterator_destroy(&it);
- reftable_ref_record_release(&ref);
-
- reftable_table_init_log_iter(tab, &it);
-
- err = reftable_iterator_seek_log(&it, "");
- if (err < 0)
- return err;
-
- while (1) {
- err = reftable_iterator_next_log(&it, &log);
- if (err > 0) {
- break;
- }
- if (err < 0) {
- return err;
- }
- reftable_log_record_print(&log, hash_id);
- }
- reftable_iterator_destroy(&it);
- reftable_log_record_release(&log);
- return 0;
-}
-
-uint64_t reftable_table_max_update_index(struct reftable_table *tab)
-{
- return tab->ops->max_update_index(tab->table_arg);
-}
-
-uint64_t reftable_table_min_update_index(struct reftable_table *tab)
-{
- return tab->ops->min_update_index(tab->table_arg);
-}
-
-uint32_t reftable_table_hash_id(struct reftable_table *tab)
-{
- return tab->ops->hash_id(tab->table_arg);
-}
-
-void reftable_iterator_destroy(struct reftable_iterator *it)
-{
- if (!it->ops) {
- return;
- }
- it->ops->close(it->iter_arg);
- it->ops = NULL;
- FREE_AND_NULL(it->iter_arg);
-}
-
-int reftable_iterator_next_ref(struct reftable_iterator *it,
- struct reftable_ref_record *ref)
-{
- struct reftable_record rec = {
- .type = BLOCK_TYPE_REF,
- .u = {
- .ref = *ref
- },
- };
- int err = iterator_next(it, &rec);
- *ref = rec.u.ref;
- return err;
-}
-
-int reftable_iterator_next_log(struct reftable_iterator *it,
- struct reftable_log_record *log)
-{
- struct reftable_record rec = {
- .type = BLOCK_TYPE_LOG,
- .u = {
- .log = *log,
- },
- };
- int err = iterator_next(it, &rec);
- *log = rec.u.log;
- return err;
-}
-
-int iterator_seek(struct reftable_iterator *it, struct reftable_record *want)
-{
- return it->ops->seek(it->iter_arg, want);
-}
-
-int iterator_next(struct reftable_iterator *it, struct reftable_record *rec)
-{
- return it->ops->next(it->iter_arg, rec);
-}
-
-static int empty_iterator_seek(void *arg, struct reftable_record *want)
-{
- return 0;
-}
-
-static int empty_iterator_next(void *arg, struct reftable_record *rec)
-{
- return 1;
-}
-
-static void empty_iterator_close(void *arg)
-{
-}
-
-static struct reftable_iterator_vtable empty_vtable = {
- .seek = &empty_iterator_seek,
- .next = &empty_iterator_next,
- .close = &empty_iterator_close,
-};
-
-void iterator_set_empty(struct reftable_iterator *it)
-{
- assert(!it->ops);
- it->iter_arg = NULL;
- it->ops = &empty_vtable;
-}
diff --git a/reftable/generic.h b/reftable/generic.h
deleted file mode 100644
index 8341fa570e..0000000000
--- a/reftable/generic.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
-Copyright 2020 Google LLC
-
-Use of this source code is governed by a BSD-style
-license that can be found in the LICENSE file or at
-https://developers.google.com/open-source/licenses/bsd
-*/
-
-#ifndef GENERIC_H
-#define GENERIC_H
-
-#include "record.h"
-#include "reftable-generic.h"
-
-/* generic interface to reftables */
-struct reftable_table_vtable {
- void (*init_iter)(void *tab, struct reftable_iterator *it, uint8_t typ);
- uint32_t (*hash_id)(void *tab);
- uint64_t (*min_update_index)(void *tab);
- uint64_t (*max_update_index)(void *tab);
-};
-
-void table_init_iter(struct reftable_table *tab,
- struct reftable_iterator *it,
- uint8_t typ);
-
-struct reftable_iterator_vtable {
- int (*seek)(void *iter_arg, struct reftable_record *want);
- int (*next)(void *iter_arg, struct reftable_record *rec);
- void (*close)(void *iter_arg);
-};
-
-void iterator_set_empty(struct reftable_iterator *it);
-int iterator_seek(struct reftable_iterator *it, struct reftable_record *want);
-int iterator_next(struct reftable_iterator *it, struct reftable_record *rec);
-
-#endif
diff --git a/reftable/iter.c b/reftable/iter.c
index fddea31e51..416a9f6996 100644
--- a/reftable/iter.c
+++ b/reftable/iter.c
@@ -11,11 +11,47 @@ https://developers.google.com/open-source/licenses/bsd
#include "system.h"
#include "block.h"
-#include "generic.h"
#include "constants.h"
#include "reader.h"
#include "reftable-error.h"
+int iterator_seek(struct reftable_iterator *it, struct reftable_record *want)
+{
+ return it->ops->seek(it->iter_arg, want);
+}
+
+int iterator_next(struct reftable_iterator *it, struct reftable_record *rec)
+{
+ return it->ops->next(it->iter_arg, rec);
+}
+
+static int empty_iterator_seek(void *arg UNUSED, struct reftable_record *want UNUSED)
+{
+ return 0;
+}
+
+static int empty_iterator_next(void *arg UNUSED, struct reftable_record *rec UNUSED)
+{
+ return 1;
+}
+
+static void empty_iterator_close(void *arg UNUSED)
+{
+}
+
+static struct reftable_iterator_vtable empty_vtable = {
+ .seek = &empty_iterator_seek,
+ .next = &empty_iterator_next,
+ .close = &empty_iterator_close,
+};
+
+void iterator_set_empty(struct reftable_iterator *it)
+{
+ assert(!it->ops);
+ it->iter_arg = NULL;
+ it->ops = &empty_vtable;
+}
+
static void filtering_ref_iterator_close(void *iter_arg)
{
struct filtering_ref_iterator *fri = iter_arg;
@@ -42,26 +78,6 @@ static int filtering_ref_iterator_next(void *iter_arg,
break;
}
- if (fri->double_check) {
- struct reftable_iterator it = { NULL };
-
- reftable_table_init_ref_iter(&fri->tab, &it);
-
- err = reftable_iterator_seek_ref(&it, ref->refname);
- if (err == 0)
- err = reftable_iterator_next_ref(&it, ref);
-
- reftable_iterator_destroy(&it);
-
- if (err < 0) {
- break;
- }
-
- if (err > 0) {
- continue;
- }
- }
-
if (ref->value_type == REFTABLE_REF_VAL2 &&
(!memcmp(fri->oid.buf, ref->value.val2.target_value,
fri->oid.len) ||
@@ -127,7 +143,8 @@ static int indexed_table_ref_iter_next_block(struct indexed_table_ref_iter *it)
return 0;
}
-static int indexed_table_ref_iter_seek(void *p, struct reftable_record *want)
+static int indexed_table_ref_iter_seek(void *p UNUSED,
+ struct reftable_record *want UNUSED)
{
BUG("seeking indexed table is not supported");
return -1;
@@ -201,3 +218,71 @@ void iterator_from_indexed_table_ref_iter(struct reftable_iterator *it,
it->iter_arg = itr;
it->ops = &indexed_table_ref_iter_vtable;
}
+
+void reftable_iterator_destroy(struct reftable_iterator *it)
+{
+ if (!it->ops)
+ return;
+ it->ops->close(it->iter_arg);
+ it->ops = NULL;
+ FREE_AND_NULL(it->iter_arg);
+}
+
+int reftable_iterator_seek_ref(struct reftable_iterator *it,
+ const char *name)
+{
+ struct reftable_record want = {
+ .type = BLOCK_TYPE_REF,
+ .u.ref = {
+ .refname = (char *)name,
+ },
+ };
+ return it->ops->seek(it->iter_arg, &want);
+}
+
+int reftable_iterator_next_ref(struct reftable_iterator *it,
+ struct reftable_ref_record *ref)
+{
+ struct reftable_record rec = {
+ .type = BLOCK_TYPE_REF,
+ .u = {
+ .ref = *ref
+ },
+ };
+ int err = iterator_next(it, &rec);
+ *ref = rec.u.ref;
+ return err;
+}
+
+int reftable_iterator_seek_log_at(struct reftable_iterator *it,
+ const char *name, uint64_t update_index)
+{
+ struct reftable_record want = {
+ .type = BLOCK_TYPE_LOG,
+ .u.log = {
+ .refname = (char *)name,
+ .update_index = update_index,
+ },
+ };
+ return it->ops->seek(it->iter_arg, &want);
+}
+
+int reftable_iterator_seek_log(struct reftable_iterator *it,
+ const char *name)
+{
+ return reftable_iterator_seek_log_at(it, name, ~((uint64_t) 0));
+}
+
+int reftable_iterator_next_log(struct reftable_iterator *it,
+ struct reftable_log_record *log)
+{
+ struct reftable_record rec = {
+ .type = BLOCK_TYPE_LOG,
+ .u = {
+ .log = *log,
+ },
+ };
+ int err = iterator_next(it, &rec);
+ *log = rec.u.log;
+ return err;
+}
diff --git a/reftable/iter.h b/reftable/iter.h
index 537431baba..befc4597df 100644
--- a/reftable/iter.h
+++ b/reftable/iter.h
@@ -14,12 +14,36 @@ https://developers.google.com/open-source/licenses/bsd
#include "record.h"
#include "reftable-iterator.h"
-#include "reftable-generic.h"
+
+/*
+ * The virtual function table for implementing generic reftable iterators.
+ */
+struct reftable_iterator_vtable {
+ int (*seek)(void *iter_arg, struct reftable_record *want);
+ int (*next)(void *iter_arg, struct reftable_record *rec);
+ void (*close)(void *iter_arg);
+};
+
+/*
+ * Position the iterator at the wanted record such that a call to
+ * `iterator_next()` would return that record, if it exists.
+ */
+int iterator_seek(struct reftable_iterator *it, struct reftable_record *want);
+
+/*
+ * Yield the next record and advance the iterator. Returns <0 on error, 0 when
+ * a record was yielded, and >0 when the iterator hit an error.
+ */
+int iterator_next(struct reftable_iterator *it, struct reftable_record *rec);
+
+/*
+ * Set up the iterator such that it behaves the same as an iterator with no
+ * entries.
+ */
+void iterator_set_empty(struct reftable_iterator *it);
/* iterator that produces only ref records that point to `oid` */
struct filtering_ref_iterator {
- int double_check;
- struct reftable_table tab;
struct strbuf oid;
struct reftable_iterator it;
};
diff --git a/reftable/merged.c b/reftable/merged.c
index 6adce44f4b..128a810c55 100644
--- a/reftable/merged.c
+++ b/reftable/merged.c
@@ -11,8 +11,8 @@ https://developers.google.com/open-source/licenses/bsd
#include "constants.h"
#include "iter.h"
#include "pq.h"
+#include "reader.h"
#include "record.h"
-#include "generic.h"
#include "reftable-merged.h"
#include "reftable-error.h"
#include "system.h"
@@ -25,7 +25,7 @@ struct merged_subiter {
struct merged_iter {
struct merged_subiter *subiters;
struct merged_iter_pqueue pq;
- size_t stack_len;
+ size_t subiters_len;
int suppress_deletions;
ssize_t advance_index;
};
@@ -38,12 +38,12 @@ static void merged_iter_init(struct merged_iter *mi,
mi->advance_index = -1;
mi->suppress_deletions = mt->suppress_deletions;
- REFTABLE_CALLOC_ARRAY(mi->subiters, mt->stack_len);
- for (size_t i = 0; i < mt->stack_len; i++) {
+ REFTABLE_CALLOC_ARRAY(mi->subiters, mt->readers_len);
+ for (size_t i = 0; i < mt->readers_len; i++) {
reftable_record_init(&mi->subiters[i].rec, typ);
- table_init_iter(&mt->stack[i], &mi->subiters[i].iter, typ);
+ reader_init_iter(mt->readers[i], &mi->subiters[i].iter, typ);
}
- mi->stack_len = mt->stack_len;
+ mi->subiters_len = mt->readers_len;
}
static void merged_iter_close(void *p)
@@ -51,7 +51,7 @@ static void merged_iter_close(void *p)
struct merged_iter *mi = p;
merged_iter_pqueue_release(&mi->pq);
- for (size_t i = 0; i < mi->stack_len; i++) {
+ for (size_t i = 0; i < mi->subiters_len; i++) {
reftable_iterator_destroy(&mi->subiters[i].iter);
reftable_record_release(&mi->subiters[i].rec);
}
@@ -80,7 +80,7 @@ static int merged_iter_seek(struct merged_iter *mi, struct reftable_record *want
mi->advance_index = -1;
- for (size_t i = 0; i < mi->stack_len; i++) {
+ for (size_t i = 0; i < mi->subiters_len; i++) {
err = iterator_seek(&mi->subiters[i].iter, want);
if (err < 0)
return err;
@@ -192,8 +192,8 @@ static void iterator_from_merged_iter(struct reftable_iterator *it,
it->ops = &merged_iter_vtable;
}
-int reftable_new_merged_table(struct reftable_merged_table **dest,
- struct reftable_table *stack, size_t n,
+int reftable_merged_table_new(struct reftable_merged_table **dest,
+ struct reftable_reader **readers, size_t n,
uint32_t hash_id)
{
struct reftable_merged_table *m = NULL;
@@ -201,10 +201,10 @@ int reftable_new_merged_table(struct reftable_merged_table **dest,
uint64_t first_min = 0;
for (size_t i = 0; i < n; i++) {
- uint64_t min = reftable_table_min_update_index(&stack[i]);
- uint64_t max = reftable_table_max_update_index(&stack[i]);
+ uint64_t min = reftable_reader_min_update_index(readers[i]);
+ uint64_t max = reftable_reader_max_update_index(readers[i]);
- if (reftable_table_hash_id(&stack[i]) != hash_id) {
+ if (reftable_reader_hash_id(readers[i]) != hash_id) {
return REFTABLE_FORMAT_ERROR;
}
if (i == 0 || min < first_min) {
@@ -216,8 +216,8 @@ int reftable_new_merged_table(struct reftable_merged_table **dest,
}
REFTABLE_CALLOC_ARRAY(m, 1);
- m->stack = stack;
- m->stack_len = n;
+ m->readers = readers;
+ m->readers_len = n;
m->min = first_min;
m->max = last_max;
m->hash_id = hash_id;
@@ -229,7 +229,6 @@ void reftable_merged_table_free(struct reftable_merged_table *mt)
{
if (!mt)
return;
- FREE_AND_NULL(mt->stack);
reftable_free(mt);
}
@@ -254,44 +253,19 @@ void merged_table_init_iter(struct reftable_merged_table *mt,
iterator_from_merged_iter(it, mi);
}
-uint32_t reftable_merged_table_hash_id(struct reftable_merged_table *mt)
-{
- return mt->hash_id;
-}
-
-static void reftable_merged_table_init_iter_void(void *tab,
- struct reftable_iterator *it,
- uint8_t typ)
-{
- merged_table_init_iter(tab, it, typ);
-}
-
-static uint32_t reftable_merged_table_hash_id_void(void *tab)
+void reftable_merged_table_init_ref_iterator(struct reftable_merged_table *mt,
+ struct reftable_iterator *it)
{
- return reftable_merged_table_hash_id(tab);
+ merged_table_init_iter(mt, it, BLOCK_TYPE_REF);
}
-static uint64_t reftable_merged_table_min_update_index_void(void *tab)
+void reftable_merged_table_init_log_iterator(struct reftable_merged_table *mt,
+ struct reftable_iterator *it)
{
- return reftable_merged_table_min_update_index(tab);
+ merged_table_init_iter(mt, it, BLOCK_TYPE_LOG);
}
-static uint64_t reftable_merged_table_max_update_index_void(void *tab)
-{
- return reftable_merged_table_max_update_index(tab);
-}
-
-static struct reftable_table_vtable merged_table_vtable = {
- .init_iter = reftable_merged_table_init_iter_void,
- .hash_id = reftable_merged_table_hash_id_void,
- .min_update_index = reftable_merged_table_min_update_index_void,
- .max_update_index = reftable_merged_table_max_update_index_void,
-};
-
-void reftable_table_from_merged_table(struct reftable_table *tab,
- struct reftable_merged_table *merged)
+uint32_t reftable_merged_table_hash_id(struct reftable_merged_table *mt)
{
- assert(!tab->ops);
- tab->ops = &merged_table_vtable;
- tab->table_arg = merged;
+ return mt->hash_id;
}
diff --git a/reftable/merged.h b/reftable/merged.h
index 2efe571da6..de5fd33f01 100644
--- a/reftable/merged.h
+++ b/reftable/merged.h
@@ -12,8 +12,8 @@ https://developers.google.com/open-source/licenses/bsd
#include "system.h"
struct reftable_merged_table {
- struct reftable_table *stack;
- size_t stack_len;
+ struct reftable_reader **readers;
+ size_t readers_len;
uint32_t hash_id;
/* If unset, produce deletions. This is useful for compaction. For the
diff --git a/reftable/reader.c b/reftable/reader.c
index 29c99e2269..f877099087 100644
--- a/reftable/reader.c
+++ b/reftable/reader.c
@@ -11,11 +11,9 @@ https://developers.google.com/open-source/licenses/bsd
#include "system.h"
#include "block.h"
#include "constants.h"
-#include "generic.h"
#include "iter.h"
#include "record.h"
#include "reftable-error.h"
-#include "reftable-generic.h"
uint64_t block_source_size(struct reftable_block_source *source)
{
@@ -164,58 +162,6 @@ done:
return err;
}
-int init_reader(struct reftable_reader *r, struct reftable_block_source *source,
- const char *name)
-{
- struct reftable_block footer = { NULL };
- struct reftable_block header = { NULL };
- int err = 0;
- uint64_t file_size = block_source_size(source);
-
- /* Need +1 to read type of first block. */
- uint32_t read_size = header_size(2) + 1; /* read v2 because it's larger. */
- memset(r, 0, sizeof(struct reftable_reader));
-
- if (read_size > file_size) {
- err = REFTABLE_FORMAT_ERROR;
- goto done;
- }
-
- err = block_source_read_block(source, &header, 0, read_size);
- if (err != read_size) {
- err = REFTABLE_IO_ERROR;
- goto done;
- }
-
- if (memcmp(header.data, "REFT", 4)) {
- err = REFTABLE_FORMAT_ERROR;
- goto done;
- }
- r->version = header.data[4];
- if (r->version != 1 && r->version != 2) {
- err = REFTABLE_FORMAT_ERROR;
- goto done;
- }
-
- r->size = file_size - footer_size(r->version);
- r->source = *source;
- r->name = xstrdup(name);
- r->hash_id = 0;
-
- err = block_source_read_block(source, &footer, r->size,
- footer_size(r->version));
- if (err != footer_size(r->version)) {
- err = REFTABLE_IO_ERROR;
- goto done;
- }
-
- err = parse_footer(r, footer.data, header.data);
-done:
- reftable_block_done(&footer);
- reftable_block_done(&header);
- return err;
-}
-
struct table_iter {
struct reftable_reader *r;
uint8_t typ;
@@ -229,6 +175,7 @@ static int table_iter_init(struct table_iter *ti, struct reftable_reader *r)
{
struct block_iter bi = BLOCK_ITER_INIT;
memset(ti, 0, sizeof(*ti));
+ reftable_reader_incref(r);
ti->r = r;
ti->bi = bi;
return 0;
@@ -316,6 +263,7 @@ static void table_iter_close(struct table_iter *ti)
{
table_iter_block_done(ti);
block_iter_close(&ti->bi);
+ reftable_reader_decref(ti->r);
}
static int table_iter_next_block(struct table_iter *ti)
@@ -605,9 +553,9 @@ static void iterator_from_table_iter(struct reftable_iterator *it,
it->ops = &table_iter_vtable;
}
-static void reader_init_iter(struct reftable_reader *r,
- struct reftable_iterator *it,
- uint8_t typ)
+void reader_init_iter(struct reftable_reader *r,
+ struct reftable_iterator *it,
+ uint8_t typ)
{
struct reftable_reader_offsets *offs = reader_offsets_for(r, typ);
@@ -633,31 +581,90 @@ void reftable_reader_init_log_iterator(struct reftable_reader *r,
reader_init_iter(r, it, BLOCK_TYPE_LOG);
}
-void reader_close(struct reftable_reader *r)
+int reftable_reader_new(struct reftable_reader **out,
+ struct reftable_block_source *source, char const *name)
{
- block_source_close(&r->source);
- FREE_AND_NULL(r->name);
-}
+ struct reftable_block footer = { 0 };
+ struct reftable_block header = { 0 };
+ struct reftable_reader *r;
+ uint64_t file_size = block_source_size(source);
+ uint32_t read_size;
+ int err;
-int reftable_new_reader(struct reftable_reader **p,
- struct reftable_block_source *src, char const *name)
-{
- struct reftable_reader *rd = reftable_calloc(1, sizeof(*rd));
- int err = init_reader(rd, src, name);
- if (err == 0) {
- *p = rd;
- } else {
- block_source_close(src);
- reftable_free(rd);
+ REFTABLE_CALLOC_ARRAY(r, 1);
+
+ /*
+ * We need one extra byte to read the type of first block. We also
+ * pretend to always be reading v2 of the format because it is larger.
+ */
+ read_size = header_size(2) + 1;
+ if (read_size > file_size) {
+ err = REFTABLE_FORMAT_ERROR;
+ goto done;
+ }
+
+ err = block_source_read_block(source, &header, 0, read_size);
+ if (err != read_size) {
+ err = REFTABLE_IO_ERROR;
+ goto done;
+ }
+
+ if (memcmp(header.data, "REFT", 4)) {
+ err = REFTABLE_FORMAT_ERROR;
+ goto done;
+ }
+ r->version = header.data[4];
+ if (r->version != 1 && r->version != 2) {
+ err = REFTABLE_FORMAT_ERROR;
+ goto done;
+ }
+
+ r->size = file_size - footer_size(r->version);
+ r->source = *source;
+ r->name = xstrdup(name);
+ r->hash_id = 0;
+ r->refcount = 1;
+
+ err = block_source_read_block(source, &footer, r->size,
+ footer_size(r->version));
+ if (err != footer_size(r->version)) {
+ err = REFTABLE_IO_ERROR;
+ goto done;
+ }
+
+ err = parse_footer(r, footer.data, header.data);
+ if (err)
+ goto done;
+
+ *out = r;
+
+done:
+ reftable_block_done(&footer);
+ reftable_block_done(&header);
+ if (err) {
+ reftable_free(r);
+ block_source_close(source);
}
return err;
}
-void reftable_reader_free(struct reftable_reader *r)
+void reftable_reader_incref(struct reftable_reader *r)
+{
+ if (!r->refcount)
+ BUG("cannot increment ref counter of dead reader");
+ r->refcount++;
+}
+
+void reftable_reader_decref(struct reftable_reader *r)
{
if (!r)
return;
- reader_close(r);
+ if (!r->refcount)
+ BUG("cannot decrement ref counter of dead reader");
+ if (--r->refcount)
+ return;
+ block_source_close(&r->source);
+ FREE_AND_NULL(r->name);
reftable_free(r);
}
@@ -735,8 +742,6 @@ static int reftable_reader_refs_for_unindexed(struct reftable_reader *r,
*filter = empty;
strbuf_add(&filter->oid, oid, oid_len);
- reftable_table_from_reader(&filter->tab, r);
- filter->double_check = 0;
iterator_from_table_iter(&filter->it, ti);
iterator_from_filtering_ref_iterator(it, filter);
@@ -761,66 +766,6 @@ uint64_t reftable_reader_min_update_index(struct reftable_reader *r)
return r->min_update_index;
}
-/* generic table interface. */
-
-static void reftable_reader_init_iter_void(void *tab,
- struct reftable_iterator *it,
- uint8_t typ)
-{
- reader_init_iter(tab, it, typ);
-}
-
-static uint32_t reftable_reader_hash_id_void(void *tab)
-{
- return reftable_reader_hash_id(tab);
-}
-
-static uint64_t reftable_reader_min_update_index_void(void *tab)
-{
- return reftable_reader_min_update_index(tab);
-}
-
-static uint64_t reftable_reader_max_update_index_void(void *tab)
-{
- return reftable_reader_max_update_index(tab);
-}
-
-static struct reftable_table_vtable reader_vtable = {
- .init_iter = reftable_reader_init_iter_void,
- .hash_id = reftable_reader_hash_id_void,
- .min_update_index = reftable_reader_min_update_index_void,
- .max_update_index = reftable_reader_max_update_index_void,
-};
-
-void reftable_table_from_reader(struct reftable_table *tab,
- struct reftable_reader *reader)
-{
- assert(!tab->ops);
- tab->ops = &reader_vtable;
- tab->table_arg = reader;
-}
-
-
-int reftable_reader_print_file(const char *tablename)
-{
- struct reftable_block_source src = { NULL };
- int err = reftable_block_source_from_file(&src, tablename);
- struct reftable_reader *r = NULL;
- struct reftable_table tab = { NULL };
- if (err < 0)
- goto done;
-
- err = reftable_new_reader(&r, &src, tablename);
- if (err < 0)
- goto done;
-
- reftable_table_from_reader(&tab, r);
- err = reftable_table_print(&tab);
-done:
- reftable_reader_free(r);
- return err;
-}
-
int reftable_reader_print_blocks(const char *tablename)
{
struct {
@@ -850,7 +795,7 @@ int reftable_reader_print_blocks(const char *tablename)
if (err < 0)
goto done;
- err = reftable_new_reader(&r, &src, tablename);
+ err = reftable_reader_new(&r, &src, tablename);
if (err < 0)
goto done;
@@ -881,7 +826,7 @@ int reftable_reader_print_blocks(const char *tablename)
}
done:
- reftable_reader_free(r);
+ reftable_reader_decref(r);
table_iter_close(&ti);
return err;
}
diff --git a/reftable/reader.h b/reftable/reader.h
index e869165f23..3710ee09b4 100644
--- a/reftable/reader.h
+++ b/reftable/reader.h
@@ -50,13 +50,16 @@ struct reftable_reader {
struct reftable_reader_offsets ref_offsets;
struct reftable_reader_offsets obj_offsets;
struct reftable_reader_offsets log_offsets;
+
+ uint64_t refcount;
};
-int init_reader(struct reftable_reader *r, struct reftable_block_source *source,
- const char *name);
-void reader_close(struct reftable_reader *r);
const char *reader_name(struct reftable_reader *r);
+void reader_init_iter(struct reftable_reader *r,
+ struct reftable_iterator *it,
+ uint8_t typ);
+
/* initialize a block reader to read from `r` */
int reader_init_block_reader(struct reftable_reader *r, struct block_reader *br,
uint64_t next_off, uint8_t want_typ);
diff --git a/reftable/record.c b/reftable/record.c
index a2cba5ef74..6b5a075b92 100644
--- a/reftable/record.c
+++ b/reftable/record.c
@@ -259,58 +259,6 @@ static void reftable_ref_record_copy_from(void *rec, const void *src_rec,
}
}
-static char hexdigit(int c)
-{
- if (c <= 9)
- return '0' + c;
- return 'a' + (c - 10);
-}
-
-static void hex_format(char *dest, const unsigned char *src, int hash_size)
-{
- assert(hash_size > 0);
- if (src) {
- int i = 0;
- for (i = 0; i < hash_size; i++) {
- dest[2 * i] = hexdigit(src[i] >> 4);
- dest[2 * i + 1] = hexdigit(src[i] & 0xf);
- }
- dest[2 * hash_size] = 0;
- }
-}
-
-static void reftable_ref_record_print_sz(const struct reftable_ref_record *ref,
- int hash_size)
-{
- char hex[GIT_MAX_HEXSZ + 1] = { 0 }; /* BUG */
- printf("ref{%s(%" PRIu64 ") ", ref->refname, ref->update_index);
- switch (ref->value_type) {
- case REFTABLE_REF_SYMREF:
- printf("=> %s", ref->value.symref);
- break;
- case REFTABLE_REF_VAL2:
- hex_format(hex, ref->value.val2.value, hash_size);
- printf("val 2 %s", hex);
- hex_format(hex, ref->value.val2.target_value,
- hash_size);
- printf("(T %s)", hex);
- break;
- case REFTABLE_REF_VAL1:
- hex_format(hex, ref->value.val1, hash_size);
- printf("val 1 %s", hex);
- break;
- case REFTABLE_REF_DELETION:
- printf("delete");
- break;
- }
- printf("}\n");
-}
-
-void reftable_ref_record_print(const struct reftable_ref_record *ref,
- uint32_t hash_id) {
- reftable_ref_record_print_sz(ref, hash_size(hash_id));
-}
-
static void reftable_ref_record_release_void(void *rec)
{
reftable_ref_record_release(rec);
@@ -480,12 +428,6 @@ static int reftable_ref_record_cmp_void(const void *_a, const void *_b)
return strcmp(a->refname, b->refname);
}
-static void reftable_ref_record_print_void(const void *rec,
- int hash_size)
-{
- reftable_ref_record_print_sz((struct reftable_ref_record *) rec, hash_size);
-}
-
static struct reftable_record_vtable reftable_ref_record_vtable = {
.key = &reftable_ref_record_key,
.type = BLOCK_TYPE_REF,
@@ -497,7 +439,6 @@ static struct reftable_record_vtable reftable_ref_record_vtable = {
.is_deletion = &reftable_ref_record_is_deletion_void,
.equal = &reftable_ref_record_equal_void,
.cmp = &reftable_ref_record_cmp_void,
- .print = &reftable_ref_record_print_void,
};
static void reftable_obj_record_key(const void *r, struct strbuf *dest)
@@ -516,23 +457,8 @@ static void reftable_obj_record_release(void *rec)
memset(obj, 0, sizeof(struct reftable_obj_record));
}
-static void reftable_obj_record_print(const void *rec, int hash_size)
-{
- const struct reftable_obj_record *obj = rec;
- char hex[GIT_MAX_HEXSZ + 1] = { 0 };
- struct strbuf offset_str = STRBUF_INIT;
- int i;
-
- for (i = 0; i < obj->offset_len; i++)
- strbuf_addf(&offset_str, "%" PRIu64 " ", obj->offsets[i]);
- hex_format(hex, obj->hash_prefix, obj->hash_prefix_len);
- printf("prefix %s (len %d), offsets [%s]\n",
- hex, obj->hash_prefix_len, offset_str.buf);
- strbuf_release(&offset_str);
-}
-
static void reftable_obj_record_copy_from(void *rec, const void *src_rec,
- int hash_size)
+ int hash_size UNUSED)
{
struct reftable_obj_record *obj = rec;
const struct reftable_obj_record *src =
@@ -559,7 +485,7 @@ static uint8_t reftable_obj_record_val_type(const void *rec)
}
static int reftable_obj_record_encode(const void *rec, struct string_view s,
- int hash_size)
+ int hash_size UNUSED)
{
const struct reftable_obj_record *r = rec;
struct string_view start = s;
@@ -594,7 +520,8 @@ static int reftable_obj_record_encode(const void *rec, struct string_view s,
static int reftable_obj_record_decode(void *rec, struct strbuf key,
uint8_t val_type, struct string_view in,
- int hash_size, struct strbuf *scratch UNUSED)
+ int hash_size UNUSED,
+ struct strbuf *scratch UNUSED)
{
struct string_view start = in;
struct reftable_obj_record *r = rec;
@@ -647,12 +574,13 @@ static int reftable_obj_record_decode(void *rec, struct strbuf key,
return start.len - in.len;
}
-static int not_a_deletion(const void *p)
+static int not_a_deletion(const void *p UNUSED)
{
return 0;
}
-static int reftable_obj_record_equal_void(const void *a, const void *b, int hash_size)
+static int reftable_obj_record_equal_void(const void *a, const void *b,
+ int hash_size UNUSED)
{
struct reftable_obj_record *ra = (struct reftable_obj_record *) a;
struct reftable_obj_record *rb = (struct reftable_obj_record *) b;
@@ -701,41 +629,8 @@ static struct reftable_record_vtable reftable_obj_record_vtable = {
.is_deletion = &not_a_deletion,
.equal = &reftable_obj_record_equal_void,
.cmp = &reftable_obj_record_cmp_void,
- .print = &reftable_obj_record_print,
};
-static void reftable_log_record_print_sz(struct reftable_log_record *log,
- int hash_size)
-{
- char hex[GIT_MAX_HEXSZ + 1] = { 0 };
-
- switch (log->value_type) {
- case REFTABLE_LOG_DELETION:
- printf("log{%s(%" PRIu64 ") delete\n", log->refname,
- log->update_index);
- break;
- case REFTABLE_LOG_UPDATE:
- printf("log{%s(%" PRIu64 ") %s <%s> %" PRIu64 " %04d\n",
- log->refname, log->update_index,
- log->value.update.name ? log->value.update.name : "",
- log->value.update.email ? log->value.update.email : "",
- log->value.update.time,
- log->value.update.tz_offset);
- hex_format(hex, log->value.update.old_hash, hash_size);
- printf("%s => ", hex);
- hex_format(hex, log->value.update.new_hash, hash_size);
- printf("%s\n\n%s\n}\n", hex,
- log->value.update.message ? log->value.update.message : "");
- break;
- }
-}
-
-void reftable_log_record_print(struct reftable_log_record *log,
- uint32_t hash_id)
-{
- reftable_log_record_print_sz(log, hash_size(hash_id));
-}
-
static void reftable_log_record_key(const void *r, struct strbuf *dest)
{
const struct reftable_log_record *rec =
@@ -1039,11 +934,6 @@ static int reftable_log_record_is_deletion_void(const void *p)
(const struct reftable_log_record *)p);
}
-static void reftable_log_record_print_void(const void *rec, int hash_size)
-{
- reftable_log_record_print_sz((struct reftable_log_record*)rec, hash_size);
-}
-
static struct reftable_record_vtable reftable_log_record_vtable = {
.key = &reftable_log_record_key,
.type = BLOCK_TYPE_LOG,
@@ -1055,7 +945,6 @@ static struct reftable_record_vtable reftable_log_record_vtable = {
.is_deletion = &reftable_log_record_is_deletion_void,
.equal = &reftable_log_record_equal_void,
.cmp = &reftable_log_record_cmp_void,
- .print = &reftable_log_record_print_void,
};
static void reftable_index_record_key(const void *r, struct strbuf *dest)
@@ -1066,7 +955,7 @@ static void reftable_index_record_key(const void *r, struct strbuf *dest)
}
static void reftable_index_record_copy_from(void *rec, const void *src_rec,
- int hash_size)
+ int hash_size UNUSED)
{
struct reftable_index_record *dst = rec;
const struct reftable_index_record *src = src_rec;
@@ -1082,13 +971,13 @@ static void reftable_index_record_release(void *rec)
strbuf_release(&idx->last_key);
}
-static uint8_t reftable_index_record_val_type(const void *rec)
+static uint8_t reftable_index_record_val_type(const void *rec UNUSED)
{
return 0;
}
static int reftable_index_record_encode(const void *rec, struct string_view out,
- int hash_size)
+ int hash_size UNUSED)
{
const struct reftable_index_record *r =
(const struct reftable_index_record *)rec;
@@ -1104,8 +993,10 @@ static int reftable_index_record_encode(const void *rec, struct string_view out,
}
static int reftable_index_record_decode(void *rec, struct strbuf key,
- uint8_t val_type, struct string_view in,
- int hash_size, struct strbuf *scratch UNUSED)
+ uint8_t val_type UNUSED,
+ struct string_view in,
+ int hash_size UNUSED,
+ struct strbuf *scratch UNUSED)
{
struct string_view start = in;
struct reftable_index_record *r = rec;
@@ -1122,7 +1013,8 @@ static int reftable_index_record_decode(void *rec, struct strbuf key,
return start.len - in.len;
}
-static int reftable_index_record_equal(const void *a, const void *b, int hash_size)
+static int reftable_index_record_equal(const void *a, const void *b,
+ int hash_size UNUSED)
{
struct reftable_index_record *ia = (struct reftable_index_record *) a;
struct reftable_index_record *ib = (struct reftable_index_record *) b;
@@ -1137,13 +1029,6 @@ static int reftable_index_record_cmp(const void *_a, const void *_b)
return strbuf_cmp(&a->last_key, &b->last_key);
}
-static void reftable_index_record_print(const void *rec, int hash_size)
-{
- const struct reftable_index_record *idx = rec;
- /* TODO: escape null chars? */
- printf("\"%s\" %" PRIu64 "\n", idx->last_key.buf, idx->offset);
-}
-
static struct reftable_record_vtable reftable_index_record_vtable = {
.key = &reftable_index_record_key,
.type = BLOCK_TYPE_INDEX,
@@ -1155,7 +1040,6 @@ static struct reftable_record_vtable reftable_index_record_vtable = {
.is_deletion = &not_a_deletion,
.equal = &reftable_index_record_equal,
.cmp = &reftable_index_record_cmp,
- .print = &reftable_index_record_print,
};
void reftable_record_key(struct reftable_record *rec, struct strbuf *dest)
@@ -1334,9 +1218,3 @@ void reftable_record_init(struct reftable_record *rec, uint8_t typ)
BUG("unhandled record type");
}
}
-
-void reftable_record_print(struct reftable_record *rec, int hash_size)
-{
- printf("'%c': ", rec->type);
- reftable_record_vtable(rec)->print(reftable_record_data(rec), hash_size);
-}
diff --git a/reftable/record.h b/reftable/record.h
index d778133e6e..5003bacdb0 100644
--- a/reftable/record.h
+++ b/reftable/record.h
@@ -136,7 +136,6 @@ void reftable_record_init(struct reftable_record *rec, uint8_t typ);
/* see struct record_vtable */
int reftable_record_cmp(struct reftable_record *a, struct reftable_record *b);
int reftable_record_equal(struct reftable_record *a, struct reftable_record *b, int hash_size);
-void reftable_record_print(struct reftable_record *rec, int hash_size);
void reftable_record_key(struct reftable_record *rec, struct strbuf *dest);
void reftable_record_copy_from(struct reftable_record *rec,
struct reftable_record *src, int hash_size);
diff --git a/reftable/reftable-generic.h b/reftable/reftable-generic.h
deleted file mode 100644
index 65670ea093..0000000000
--- a/reftable/reftable-generic.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
-Copyright 2020 Google LLC
-
-Use of this source code is governed by a BSD-style
-license that can be found in the LICENSE file or at
-https://developers.google.com/open-source/licenses/bsd
-*/
-
-#ifndef REFTABLE_GENERIC_H
-#define REFTABLE_GENERIC_H
-
-#include "reftable-iterator.h"
-
-struct reftable_table_vtable;
-
-/*
- * Provides a unified API for reading tables, either merged tables, or single
- * readers. */
-struct reftable_table {
- struct reftable_table_vtable *ops;
- void *table_arg;
-};
-
-void reftable_table_init_ref_iter(struct reftable_table *tab,
- struct reftable_iterator *it);
-
-void reftable_table_init_log_iter(struct reftable_table *tab,
- struct reftable_iterator *it);
-
-/* returns the hash ID from a generic reftable_table */
-uint32_t reftable_table_hash_id(struct reftable_table *tab);
-
-/* returns the max update_index covered by this table. */
-uint64_t reftable_table_max_update_index(struct reftable_table *tab);
-
-/* returns the min update_index covered by this table. */
-uint64_t reftable_table_min_update_index(struct reftable_table *tab);
-
-/* convenience function to read a single ref. Returns < 0 for error, 0
- for success, and 1 if ref not found. */
-int reftable_table_read_ref(struct reftable_table *tab, const char *name,
- struct reftable_ref_record *ref);
-
-/* dump table contents onto stdout for debugging */
-int reftable_table_print(struct reftable_table *tab);
-
-#endif
diff --git a/reftable/reftable-merged.h b/reftable/reftable-merged.h
index 14d5fc9f05..16d19f8df2 100644
--- a/reftable/reftable-merged.h
+++ b/reftable/reftable-merged.h
@@ -26,16 +26,24 @@ https://developers.google.com/open-source/licenses/bsd
/* A merged table is implements seeking/iterating over a stack of tables. */
struct reftable_merged_table;
-/* A generic reftable; see below. */
-struct reftable_table;
+struct reftable_reader;
-/* reftable_new_merged_table creates a new merged table. It takes ownership of
- the stack array.
-*/
-int reftable_new_merged_table(struct reftable_merged_table **dest,
- struct reftable_table *stack, size_t n,
+/*
+ * reftable_merged_table_new creates a new merged table. The readers must be
+ * kept alive as long as the merged table is still in use.
+ */
+int reftable_merged_table_new(struct reftable_merged_table **dest,
+ struct reftable_reader **readers, size_t n,
uint32_t hash_id);
+/* Initialize a merged table iterator for reading refs. */
+void reftable_merged_table_init_ref_iterator(struct reftable_merged_table *mt,
+ struct reftable_iterator *it);
+
+/* Initialize a merged table iterator for reading logs. */
+void reftable_merged_table_init_log_iterator(struct reftable_merged_table *mt,
+ struct reftable_iterator *it);
+
/* returns the max update_index covered by this merged table. */
uint64_t
reftable_merged_table_max_update_index(struct reftable_merged_table *mt);
@@ -50,8 +58,4 @@ void reftable_merged_table_free(struct reftable_merged_table *m);
/* return the hash ID of the merged table. */
uint32_t reftable_merged_table_hash_id(struct reftable_merged_table *m);
-/* create a generic table from reftable_merged_table */
-void reftable_table_from_merged_table(struct reftable_table *tab,
- struct reftable_merged_table *table);
-
#endif
diff --git a/reftable/reftable-reader.h b/reftable/reftable-reader.h
index a32f31d648..a600452b56 100644
--- a/reftable/reftable-reader.h
+++ b/reftable/reftable-reader.h
@@ -23,19 +23,28 @@
/* The reader struct is a handle to an open reftable file. */
struct reftable_reader;
-/* Generic table. */
-struct reftable_table;
-
-/* reftable_new_reader opens a reftable for reading. If successful,
+/* reftable_reader_new opens a reftable for reading. If successful,
* returns 0 code and sets pp. The name is used for creating a
* stack. Typically, it is the basename of the file. The block source
* `src` is owned by the reader, and is closed on calling
* reftable_reader_destroy(). On error, the block source `src` is
* closed as well.
*/
-int reftable_new_reader(struct reftable_reader **pp,
+int reftable_reader_new(struct reftable_reader **pp,
struct reftable_block_source *src, const char *name);
+/*
+ * Manage the reference count of the reftable reader. A newly initialized
+ * reader starts with a refcount of 1 and will be deleted once the refcount has
+ * reached 0.
+ *
+ * This is required because readers may have longer lifetimes than the stack
+ * they belong to. The stack may for example be reloaded while the old tables
+ * are still being accessed by an iterator.
+ */
+void reftable_reader_incref(struct reftable_reader *reader);
+void reftable_reader_decref(struct reftable_reader *reader);
+
/* Initialize a reftable iterator for reading refs. */
void reftable_reader_init_ref_iterator(struct reftable_reader *r,
struct reftable_iterator *it);
@@ -47,9 +56,6 @@ void reftable_reader_init_log_iterator(struct reftable_reader *r,
/* returns the hash ID used in this table. */
uint32_t reftable_reader_hash_id(struct reftable_reader *r);
-/* closes and deallocates a reader. */
-void reftable_reader_free(struct reftable_reader *);
-
/* return an iterator for the refs pointing to `oid`. */
int reftable_reader_refs_for(struct reftable_reader *r,
struct reftable_iterator *it, uint8_t *oid);
@@ -60,12 +66,6 @@ uint64_t reftable_reader_max_update_index(struct reftable_reader *r);
/* return the min_update_index for a table */
uint64_t reftable_reader_min_update_index(struct reftable_reader *r);
-/* creates a generic table from a file reader. */
-void reftable_table_from_reader(struct reftable_table *tab,
- struct reftable_reader *reader);
-
-/* print table onto stdout for debugging. */
-int reftable_reader_print_file(const char *tablename);
/* print blocks onto stdout for debugging. */
int reftable_reader_print_blocks(const char *tablename);
diff --git a/reftable/reftable-record.h b/reftable/reftable-record.h
index ff486eb1f7..2d42463c58 100644
--- a/reftable/reftable-record.h
+++ b/reftable/reftable-record.h
@@ -60,10 +60,6 @@ const unsigned char *reftable_ref_record_val2(const struct reftable_ref_record *
/* returns whether 'ref' represents a deletion */
int reftable_ref_record_is_deletion(const struct reftable_ref_record *ref);
-/* prints a reftable_ref_record onto stdout. Useful for debugging. */
-void reftable_ref_record_print(const struct reftable_ref_record *ref,
- uint32_t hash_id);
-
/* frees and nulls all pointer values inside `ref`. */
void reftable_ref_record_release(struct reftable_ref_record *ref);
@@ -111,8 +107,4 @@ void reftable_log_record_release(struct reftable_log_record *log);
int reftable_log_record_equal(const struct reftable_log_record *a,
const struct reftable_log_record *b, int hash_size);
-/* dumps a reftable_log_record on stdout, for debugging/testing. */
-void reftable_log_record_print(struct reftable_log_record *log,
- uint32_t hash_id);
-
#endif
diff --git a/reftable/reftable-stack.h b/reftable/reftable-stack.h
index 09e97c9991..f4f8cabc7f 100644
--- a/reftable/reftable-stack.h
+++ b/reftable/reftable-stack.h
@@ -140,7 +140,4 @@ struct reftable_compaction_stats {
struct reftable_compaction_stats *
reftable_stack_compaction_stats(struct reftable_stack *st);
-/* print the entire stack represented by the directory */
-int reftable_stack_print_directory(const char *stackdir, uint32_t hash_id);
-
#endif
diff --git a/reftable/reftable-tests.h b/reftable/reftable-tests.h
index f25034fca8..5d725c69c7 100644
--- a/reftable/reftable-tests.h
+++ b/reftable/reftable-tests.h
@@ -9,10 +9,6 @@ https://developers.google.com/open-source/licenses/bsd
#ifndef REFTABLE_TESTS_H
#define REFTABLE_TESTS_H
-int basics_test_main(int argc, const char **argv);
-int block_test_main(int argc, const char **argv);
-int record_test_main(int argc, const char **argv);
int stack_test_main(int argc, const char **argv);
-int reftable_dump_main(int argc, char *const *argv);
#endif
diff --git a/reftable/stack.c b/reftable/stack.c
index 2071e428a8..ce0a35216b 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -14,7 +14,6 @@ https://developers.google.com/open-source/licenses/bsd
#include "merged.h"
#include "reader.h"
#include "reftable-error.h"
-#include "reftable-generic.h"
#include "reftable-record.h"
#include "reftable-merged.h"
#include "writer.h"
@@ -187,7 +186,7 @@ void reftable_stack_destroy(struct reftable_stack *st)
if (names && !has_name(names, name)) {
stack_filename(&filename, st, name);
}
- reftable_reader_free(st->readers[i]);
+ reftable_reader_decref(st->readers[i]);
if (filename.len) {
/* On Windows, can only unlink after closing. */
@@ -225,13 +224,13 @@ static int reftable_stack_reload_once(struct reftable_stack *st,
const char **names,
int reuse_open)
{
- size_t cur_len = !st->merged ? 0 : st->merged->stack_len;
+ size_t cur_len = !st->merged ? 0 : st->merged->readers_len;
struct reftable_reader **cur = stack_copy_readers(st, cur_len);
+ struct reftable_reader **reused = NULL;
+ size_t reused_len = 0, reused_alloc = 0;
size_t names_len = names_length(names);
struct reftable_reader **new_readers =
reftable_calloc(names_len, sizeof(*new_readers));
- struct reftable_table *new_tables =
- reftable_calloc(names_len, sizeof(*new_tables));
size_t new_readers_len = 0;
struct reftable_merged_table *new_merged = NULL;
struct strbuf table_path = STRBUF_INIT;
@@ -248,6 +247,18 @@ static int reftable_stack_reload_once(struct reftable_stack *st,
if (cur[i] && 0 == strcmp(cur[i]->name, name)) {
rd = cur[i];
cur[i] = NULL;
+
+ /*
+ * When reloading the stack fails, we end up
+ * releasing all new readers. This also
+ * includes the reused readers, even though
+ * they are still in used by the old stack. We
+ * thus need to keep them alive here, which we
+ * do by bumping their refcount.
+ */
+ REFTABLE_ALLOC_GROW(reused, reused_len + 1, reused_alloc);
+ reused[reused_len++] = rd;
+ reftable_reader_incref(rd);
break;
}
}
@@ -261,55 +272,62 @@ static int reftable_stack_reload_once(struct reftable_stack *st,
if (err < 0)
goto done;
- err = reftable_new_reader(&rd, &src, name);
+ err = reftable_reader_new(&rd, &src, name);
if (err < 0)
goto done;
}
new_readers[new_readers_len] = rd;
- reftable_table_from_reader(&new_tables[new_readers_len], rd);
new_readers_len++;
}
/* success! */
- err = reftable_new_merged_table(&new_merged, new_tables,
+ err = reftable_merged_table_new(&new_merged, new_readers,
new_readers_len, st->opts.hash_id);
if (err < 0)
goto done;
- new_tables = NULL;
- st->readers_len = new_readers_len;
- if (st->merged)
- reftable_merged_table_free(st->merged);
- if (st->readers) {
- reftable_free(st->readers);
- }
- st->readers = new_readers;
- new_readers = NULL;
- new_readers_len = 0;
-
- new_merged->suppress_deletions = 1;
- st->merged = new_merged;
+ /*
+ * Close the old, non-reused readers and proactively try to unlink
+ * them. This is done for systems like Windows, where the underlying
+ * file of such an open reader wouldn't have been possible to be
+ * unlinked by the compacting process.
+ */
for (i = 0; i < cur_len; i++) {
if (cur[i]) {
const char *name = reader_name(cur[i]);
stack_filename(&table_path, st, name);
-
- reader_close(cur[i]);
- reftable_reader_free(cur[i]);
-
- /* On Windows, can only unlink after closing. */
+ reftable_reader_decref(cur[i]);
unlink(table_path.buf);
}
}
+ /* Update the stack to point to the new tables. */
+ if (st->merged)
+ reftable_merged_table_free(st->merged);
+ new_merged->suppress_deletions = 1;
+ st->merged = new_merged;
+
+ if (st->readers)
+ reftable_free(st->readers);
+ st->readers = new_readers;
+ st->readers_len = new_readers_len;
+ new_readers = NULL;
+ new_readers_len = 0;
+
+ /*
+ * Decrement the refcount of reused readers again. This only needs to
+ * happen on the successful case, because on the unsuccessful one we
+ * decrement their refcount via `new_readers`.
+ */
+ for (i = 0; i < reused_len; i++)
+ reftable_reader_decref(reused[i]);
+
done:
- for (i = 0; i < new_readers_len; i++) {
- reader_close(new_readers[i]);
- reftable_reader_free(new_readers[i]);
- }
+ for (i = 0; i < new_readers_len; i++)
+ reftable_reader_decref(new_readers[i]);
reftable_free(new_readers);
- reftable_free(new_tables);
+ reftable_free(reused);
reftable_free(cur);
strbuf_release(&table_path);
return err;
@@ -520,7 +538,7 @@ static int stack_uptodate(struct reftable_stack *st)
}
}
- if (names[st->merged->stack_len]) {
+ if (names[st->merged->readers_len]) {
err = 1;
goto done;
}
@@ -659,7 +677,7 @@ int reftable_addition_commit(struct reftable_addition *add)
if (add->new_tables_len == 0)
goto done;
- for (i = 0; i < add->stack->merged->stack_len; i++) {
+ for (i = 0; i < add->stack->merged->readers_len; i++) {
strbuf_addstr(&table_list, add->stack->readers[i]->name);
strbuf_addstr(&table_list, "\n");
}
@@ -839,7 +857,7 @@ done:
uint64_t reftable_stack_next_update_index(struct reftable_stack *st)
{
- int sz = st->merged->stack_len;
+ int sz = st->merged->readers_len;
if (sz > 0)
return reftable_reader_max_update_index(st->readers[sz - 1]) +
1;
@@ -906,30 +924,23 @@ static int stack_write_compact(struct reftable_stack *st,
size_t first, size_t last,
struct reftable_log_expiry_config *config)
{
- size_t subtabs_len = last - first + 1;
- struct reftable_table *subtabs = reftable_calloc(
- last - first + 1, sizeof(*subtabs));
struct reftable_merged_table *mt = NULL;
struct reftable_iterator it = { NULL };
struct reftable_ref_record ref = { NULL };
struct reftable_log_record log = { NULL };
+ size_t subtabs_len = last - first + 1;
uint64_t entries = 0;
int err = 0;
- for (size_t i = first, j = 0; i <= last; i++) {
- struct reftable_reader *t = st->readers[i];
- reftable_table_from_reader(&subtabs[j++], t);
- st->stats.bytes += t->size;
- }
+ for (size_t i = first; i <= last; i++)
+ st->stats.bytes += st->readers[i]->size;
reftable_writer_set_limits(wr, st->readers[first]->min_update_index,
st->readers[last]->max_update_index);
- err = reftable_new_merged_table(&mt, subtabs, subtabs_len,
+ err = reftable_merged_table_new(&mt, st->readers + first, subtabs_len,
st->opts.hash_id);
- if (err < 0) {
- reftable_free(subtabs);
+ if (err < 0)
goto done;
- }
merged_table_init_iter(mt, &it, BLOCK_TYPE_REF);
err = reftable_iterator_seek_ref(&it, "");
@@ -1207,7 +1218,7 @@ static int stack_compact_range(struct reftable_stack *st,
* have compacted them.
*/
for (size_t j = 1; j < last - first + 1; j++) {
- const char *old = first + j < st->merged->stack_len ?
+ const char *old = first + j < st->merged->readers_len ?
st->readers[first + j]->name : NULL;
const char *new = names[i + j];
@@ -1248,10 +1259,10 @@ static int stack_compact_range(struct reftable_stack *st,
* `fd_read_lines()` uses a `NULL` sentinel to indicate that
* the array is at its end. As we use `free_names()` to free
* the array, we need to include this sentinel value here and
- * thus have to allocate `stack_len + 1` many entries.
+ * thus have to allocate `readers_len + 1` many entries.
*/
- REFTABLE_CALLOC_ARRAY(names, st->merged->stack_len + 1);
- for (size_t i = 0; i < st->merged->stack_len; i++)
+ REFTABLE_CALLOC_ARRAY(names, st->merged->readers_len + 1);
+ for (size_t i = 0; i < st->merged->readers_len; i++)
names[i] = xstrdup(st->readers[i]->name);
first_to_replace = first;
last_to_replace = last;
@@ -1341,25 +1352,17 @@ done:
strbuf_release(&table_name);
free_names(names);
- return err;
-}
-
-static int stack_compact_range_stats(struct reftable_stack *st,
- size_t first, size_t last,
- struct reftable_log_expiry_config *config,
- unsigned int flags)
-{
- int err = stack_compact_range(st, first, last, config, flags);
if (err == REFTABLE_LOCK_ERROR)
st->stats.failures++;
+
return err;
}
int reftable_stack_compact_all(struct reftable_stack *st,
struct reftable_log_expiry_config *config)
{
- size_t last = st->merged->stack_len ? st->merged->stack_len - 1 : 0;
- return stack_compact_range_stats(st, 0, last, config, 0);
+ size_t last = st->merged->readers_len ? st->merged->readers_len - 1 : 0;
+ return stack_compact_range(st, 0, last, config, 0);
}
static int segment_size(struct segment *s)
@@ -1449,9 +1452,9 @@ static uint64_t *stack_table_sizes_for_compaction(struct reftable_stack *st)
int overhead = header_size(version) - 1;
uint64_t *sizes;
- REFTABLE_CALLOC_ARRAY(sizes, st->merged->stack_len);
+ REFTABLE_CALLOC_ARRAY(sizes, st->merged->readers_len);
- for (size_t i = 0; i < st->merged->stack_len; i++)
+ for (size_t i = 0; i < st->merged->readers_len; i++)
sizes[i] = st->readers[i]->size - overhead;
return sizes;
@@ -1461,12 +1464,12 @@ int reftable_stack_auto_compact(struct reftable_stack *st)
{
uint64_t *sizes = stack_table_sizes_for_compaction(st);
struct segment seg =
- suggest_compaction_segment(sizes, st->merged->stack_len,
+ suggest_compaction_segment(sizes, st->merged->readers_len,
st->opts.auto_compaction_factor);
reftable_free(sizes);
if (segment_size(&seg) > 0)
- return stack_compact_range_stats(st, seg.start, seg.end - 1,
- NULL, STACK_COMPACT_RANGE_BEST_EFFORT);
+ return stack_compact_range(st, seg.start, seg.end - 1,
+ NULL, STACK_COMPACT_RANGE_BEST_EFFORT);
return 0;
}
@@ -1480,9 +1483,28 @@ reftable_stack_compaction_stats(struct reftable_stack *st)
int reftable_stack_read_ref(struct reftable_stack *st, const char *refname,
struct reftable_ref_record *ref)
{
- struct reftable_table tab = { NULL };
- reftable_table_from_merged_table(&tab, reftable_stack_merged_table(st));
- return reftable_table_read_ref(&tab, refname, ref);
+ struct reftable_iterator it = { 0 };
+ int ret;
+
+ reftable_merged_table_init_ref_iterator(st->merged, &it);
+ ret = reftable_iterator_seek_ref(&it, refname);
+ if (ret)
+ goto out;
+
+ ret = reftable_iterator_next_ref(&it, ref);
+ if (ret)
+ goto out;
+
+ if (strcmp(ref->refname, refname) ||
+ reftable_ref_record_is_deletion(ref)) {
+ reftable_ref_record_release(ref);
+ ret = 1;
+ goto out;
+ }
+
+out:
+ reftable_iterator_destroy(&it);
+ return ret;
}
int reftable_stack_read_log(struct reftable_stack *st, const char *refname,
@@ -1534,12 +1556,12 @@ static void remove_maybe_stale_table(struct reftable_stack *st, uint64_t max,
if (err < 0)
goto done;
- err = reftable_new_reader(&rd, &src, name);
+ err = reftable_reader_new(&rd, &src, name);
if (err < 0)
goto done;
update_idx = reftable_reader_max_update_index(rd);
- reftable_reader_free(rd);
+ reftable_reader_decref(rd);
if (update_idx <= max) {
unlink(table_path.buf);
@@ -1596,23 +1618,3 @@ done:
reftable_addition_destroy(add);
return err;
}
-
-int reftable_stack_print_directory(const char *stackdir, uint32_t hash_id)
-{
- struct reftable_stack *stack = NULL;
- struct reftable_write_options opts = { .hash_id = hash_id };
- struct reftable_merged_table *merged = NULL;
- struct reftable_table table = { NULL };
-
- int err = reftable_new_stack(&stack, stackdir, &opts);
- if (err < 0)
- goto done;
-
- merged = reftable_stack_merged_table(stack);
- reftable_table_from_merged_table(&table, merged);
- err = reftable_table_print(&table);
-done:
- if (stack)
- reftable_stack_destroy(stack);
- return err;
-}
diff --git a/reftable/stack_test.c b/reftable/stack_test.c
index 8c36590ff0..89cb2be19f 100644
--- a/reftable/stack_test.c
+++ b/reftable/stack_test.c
@@ -10,6 +10,7 @@ https://developers.google.com/open-source/licenses/bsd
#include "system.h"
+#include "copy.h"
#include "reftable-reader.h"
#include "merged.h"
#include "basics.h"
@@ -125,6 +126,7 @@ static void write_n_ref_tables(struct reftable_stack *st,
.value_type = REFTABLE_REF_VAL1,
};
+ strbuf_reset(&buf);
strbuf_addf(&buf, "refs/heads/branch-%04u", (unsigned) i);
ref.refname = buf.buf;
set_test_hash(ref.value.val1, i);
@@ -179,13 +181,6 @@ static void test_reftable_stack_add_one(void)
EXPECT(0 == strcmp("master", dest.value.symref));
EXPECT(st->readers_len > 0);
- printf("testing print functionality:\n");
- err = reftable_stack_print_directory(dir, GIT_SHA1_FORMAT_ID);
- EXPECT_ERR(err);
-
- err = reftable_stack_print_directory(dir, GIT_SHA256_FORMAT_ID);
- EXPECT(err == REFTABLE_FORMAT_ERROR);
-
#ifndef GIT_WINDOWS_NATIVE
strbuf_addstr(&scratch, dir);
strbuf_addstr(&scratch, "/tables.list");
@@ -347,9 +342,9 @@ static void test_reftable_stack_transaction_api_performs_auto_compaction(void)
* all tables in the stack.
*/
if (i != n)
- EXPECT(st->merged->stack_len == i + 1);
+ EXPECT(st->merged->readers_len == i + 1);
else
- EXPECT(st->merged->stack_len == 1);
+ EXPECT(st->merged->readers_len == 1);
}
reftable_stack_destroy(st);
@@ -375,7 +370,7 @@ static void test_reftable_stack_auto_compaction_fails_gracefully(void)
err = reftable_stack_add(st, write_test_ref, &ref);
EXPECT_ERR(err);
- EXPECT(st->merged->stack_len == 1);
+ EXPECT(st->merged->readers_len == 1);
EXPECT(st->stats.attempts == 0);
EXPECT(st->stats.failures == 0);
@@ -390,7 +385,7 @@ static void test_reftable_stack_auto_compaction_fails_gracefully(void)
ref.update_index = 2;
err = reftable_stack_add(st, write_test_ref, &ref);
EXPECT_ERR(err);
- EXPECT(st->merged->stack_len == 2);
+ EXPECT(st->merged->readers_len == 2);
EXPECT(st->stats.attempts == 1);
EXPECT(st->stats.failures == 1);
@@ -399,7 +394,7 @@ static void test_reftable_stack_auto_compaction_fails_gracefully(void)
clear_dir(dir);
}
-static int write_error(struct reftable_writer *wr, void *arg)
+static int write_error(struct reftable_writer *wr UNUSED, void *arg)
{
return *((int *)arg);
}
@@ -816,7 +811,7 @@ static void test_reflog_expire(void)
reftable_log_record_release(&log);
}
-static int write_nothing(struct reftable_writer *wr, void *arg)
+static int write_nothing(struct reftable_writer *wr, void *arg UNUSED)
{
reftable_writer_set_limits(wr, 1, 1);
return 0;
@@ -881,7 +876,7 @@ static void test_reftable_stack_auto_compaction(void)
err = reftable_stack_auto_compact(st);
EXPECT_ERR(err);
- EXPECT(i < 3 || st->merged->stack_len < 2 * fastlog2(i));
+ EXPECT(i < 3 || st->merged->readers_len < 2 * fastlog2(i));
}
EXPECT(reftable_stack_compaction_stats(st)->entries_written <
@@ -905,7 +900,7 @@ static void test_reftable_stack_auto_compaction_with_locked_tables(void)
EXPECT_ERR(err);
write_n_ref_tables(st, 5);
- EXPECT(st->merged->stack_len == 5);
+ EXPECT(st->merged->readers_len == 5);
/*
* Given that all tables we have written should be roughly the same
@@ -925,7 +920,7 @@ static void test_reftable_stack_auto_compaction_with_locked_tables(void)
err = reftable_stack_auto_compact(st);
EXPECT_ERR(err);
EXPECT(st->stats.failures == 0);
- EXPECT(st->merged->stack_len == 4);
+ EXPECT(st->merged->readers_len == 4);
reftable_stack_destroy(st);
strbuf_release(&buf);
@@ -970,9 +965,9 @@ static void test_reftable_stack_add_performs_auto_compaction(void)
* all tables in the stack.
*/
if (i != n)
- EXPECT(st->merged->stack_len == i + 1);
+ EXPECT(st->merged->readers_len == i + 1);
else
- EXPECT(st->merged->stack_len == 1);
+ EXPECT(st->merged->readers_len == 1);
}
reftable_stack_destroy(st);
@@ -994,7 +989,7 @@ static void test_reftable_stack_compaction_with_locked_tables(void)
EXPECT_ERR(err);
write_n_ref_tables(st, 3);
- EXPECT(st->merged->stack_len == 3);
+ EXPECT(st->merged->readers_len == 3);
/* Lock one of the tables that we're about to compact. */
strbuf_reset(&buf);
@@ -1008,7 +1003,7 @@ static void test_reftable_stack_compaction_with_locked_tables(void)
err = reftable_stack_compact_all(st, NULL);
EXPECT(err == REFTABLE_LOCK_ERROR);
EXPECT(st->stats.failures == 1);
- EXPECT(st->merged->stack_len == 3);
+ EXPECT(st->merged->readers_len == 3);
reftable_stack_destroy(st);
strbuf_release(&buf);
@@ -1042,10 +1037,8 @@ static void test_reftable_stack_compaction_concurrent(void)
static void unclean_stack_close(struct reftable_stack *st)
{
/* break abstraction boundary to simulate unclean shutdown. */
- int i = 0;
- for (; i < st->readers_len; i++) {
- reftable_reader_free(st->readers[i]);
- }
+ for (size_t i = 0; i < st->readers_len; i++)
+ reftable_reader_decref(st->readers[i]);
st->readers_len = 0;
FREE_AND_NULL(st->readers);
}
@@ -1084,7 +1077,113 @@ static void test_reftable_stack_compaction_concurrent_clean(void)
clear_dir(dir);
}
-int stack_test_main(int argc, const char *argv[])
+static void test_reftable_stack_read_across_reload(void)
+{
+ struct reftable_write_options opts = { 0 };
+ struct reftable_stack *st1 = NULL, *st2 = NULL;
+ struct reftable_ref_record rec = { 0 };
+ struct reftable_iterator it = { 0 };
+ char *dir = get_tmp_dir(__LINE__);
+ int err;
+
+ /* Create a first stack and set up an iterator for it. */
+ err = reftable_new_stack(&st1, dir, &opts);
+ EXPECT_ERR(err);
+ write_n_ref_tables(st1, 2);
+ EXPECT(st1->merged->readers_len == 2);
+ reftable_stack_init_ref_iterator(st1, &it);
+ err = reftable_iterator_seek_ref(&it, "");
+ EXPECT_ERR(err);
+
+ /* Set up a second stack for the same directory and compact it. */
+ err = reftable_new_stack(&st2, dir, &opts);
+ EXPECT_ERR(err);
+ EXPECT(st2->merged->readers_len == 2);
+ err = reftable_stack_compact_all(st2, NULL);
+ EXPECT_ERR(err);
+ EXPECT(st2->merged->readers_len == 1);
+
+ /*
+ * Verify that we can continue to use the old iterator even after we
+ * have reloaded its stack.
+ */
+ err = reftable_stack_reload(st1);
+ EXPECT_ERR(err);
+ EXPECT(st1->merged->readers_len == 1);
+ err = reftable_iterator_next_ref(&it, &rec);
+ EXPECT_ERR(err);
+ EXPECT(!strcmp(rec.refname, "refs/heads/branch-0000"));
+ err = reftable_iterator_next_ref(&it, &rec);
+ EXPECT_ERR(err);
+ EXPECT(!strcmp(rec.refname, "refs/heads/branch-0001"));
+ err = reftable_iterator_next_ref(&it, &rec);
+ EXPECT(err > 0);
+
+ reftable_ref_record_release(&rec);
+ reftable_iterator_destroy(&it);
+ reftable_stack_destroy(st1);
+ reftable_stack_destroy(st2);
+ clear_dir(dir);
+}
+
+static void test_reftable_stack_reload_with_missing_table(void)
+{
+ struct reftable_write_options opts = { 0 };
+ struct reftable_stack *st = NULL;
+ struct reftable_ref_record rec = { 0 };
+ struct reftable_iterator it = { 0 };
+ struct strbuf table_path = STRBUF_INIT, content = STRBUF_INIT;
+ char *dir = get_tmp_dir(__LINE__);
+ int err;
+
+ /* Create a first stack and set up an iterator for it. */
+ err = reftable_new_stack(&st, dir, &opts);
+ EXPECT_ERR(err);
+ write_n_ref_tables(st, 2);
+ EXPECT(st->merged->readers_len == 2);
+ reftable_stack_init_ref_iterator(st, &it);
+ err = reftable_iterator_seek_ref(&it, "");
+ EXPECT_ERR(err);
+
+ /*
+ * Update the tables.list file with some garbage data, while reusing
+ * our old readers. This should trigger a partial reload of the stack,
+ * where we try to reuse our old readers.
+ */
+ strbuf_addf(&content, "%s\n", st->readers[0]->name);
+ strbuf_addf(&content, "%s\n", st->readers[1]->name);
+ strbuf_addstr(&content, "garbage\n");
+ strbuf_addf(&table_path, "%s.lock", st->list_file);
+ write_file_buf(table_path.buf, content.buf, content.len);
+ err = rename(table_path.buf, st->list_file);
+ EXPECT_ERR(err);
+
+ err = reftable_stack_reload(st);
+ EXPECT(err == -4);
+ EXPECT(st->merged->readers_len == 2);
+
+ /*
+ * Even though the reload has failed, we should be able to continue
+ * using the iterator.
+ */
+ err = reftable_iterator_next_ref(&it, &rec);
+ EXPECT_ERR(err);
+ EXPECT(!strcmp(rec.refname, "refs/heads/branch-0000"));
+ err = reftable_iterator_next_ref(&it, &rec);
+ EXPECT_ERR(err);
+ EXPECT(!strcmp(rec.refname, "refs/heads/branch-0001"));
+ err = reftable_iterator_next_ref(&it, &rec);
+ EXPECT(err > 0);
+
+ reftable_ref_record_release(&rec);
+ reftable_iterator_destroy(&it);
+ reftable_stack_destroy(st);
+ strbuf_release(&table_path);
+ strbuf_release(&content);
+ clear_dir(dir);
+}
+
+int stack_test_main(int argc UNUSED, const char *argv[] UNUSED)
{
RUN_TEST(test_empty_add);
RUN_TEST(test_read_file);
@@ -1106,6 +1205,8 @@ int stack_test_main(int argc, const char *argv[])
RUN_TEST(test_reftable_stack_auto_compaction_fails_gracefully);
RUN_TEST(test_reftable_stack_update_index_check);
RUN_TEST(test_reftable_stack_uptodate);
+ RUN_TEST(test_reftable_stack_read_across_reload);
+ RUN_TEST(test_reftable_stack_reload_with_missing_table);
RUN_TEST(test_suggest_compaction_segment);
RUN_TEST(test_suggest_compaction_segment_nothing);
return 0;
diff --git a/reftable/test_framework.c b/reftable/test_framework.c
index 4066924eee..a07fec5d84 100644
--- a/reftable/test_framework.c
+++ b/reftable/test_framework.c
@@ -21,7 +21,7 @@ ssize_t strbuf_add_void(void *b, const void *data, size_t sz)
return sz;
}
-int noop_flush(void *arg)
+int noop_flush(void *arg UNUSED)
{
return 0;
}
diff --git a/reftable/writer.c b/reftable/writer.c
index 45b3e9ce1f..9d5e6072bc 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -544,7 +544,7 @@ static void write_object_record(void *void_arg, void *key)
done:;
}
-static void object_record_free(void *void_arg, void *key)
+static void object_record_free(void *void_arg UNUSED, void *key)
{
struct obj_index_tree_node *entry = key;
diff --git a/remote.c b/remote.c
index 7d5b8f750d..8f3dee1318 100644
--- a/remote.c
+++ b/remote.c
@@ -243,6 +243,17 @@ static struct branch *make_branch(struct remote_state *remote_state,
return ret;
}
+static void branch_release(struct branch *branch)
+{
+ free((char *)branch->name);
+ free((char *)branch->refname);
+ free(branch->remote_name);
+ free(branch->pushremote_name);
+ for (int i = 0; i < branch->merge_nr; i++)
+ refspec_item_clear(branch->merge[i]);
+ free(branch->merge);
+}
+
static struct rewrite *make_rewrite(struct rewrites *r,
const char *base, size_t len)
{
@@ -263,6 +274,14 @@ static struct rewrite *make_rewrite(struct rewrites *r,
return ret;
}
+static void rewrites_release(struct rewrites *r)
+{
+ for (int i = 0; i < r->rewrite_nr; i++)
+ free((char *)r->rewrite[i]->base);
+ free(r->rewrite);
+ memset(r, 0, sizeof(*r));
+}
+
static void add_instead_of(struct rewrite *rewrite, const char *instead_of)
{
ALLOC_GROW(rewrite->instead_of, rewrite->instead_of_nr + 1, rewrite->instead_of_alloc);
@@ -373,8 +392,10 @@ static int handle_config(const char *key, const char *value,
return -1;
branch = make_branch(remote_state, name, namelen);
if (!strcmp(subkey, "remote")) {
+ FREE_AND_NULL(branch->remote_name);
return git_config_string(&branch->remote_name, key, value);
} else if (!strcmp(subkey, "pushremote")) {
+ FREE_AND_NULL(branch->pushremote_name);
return git_config_string(&branch->pushremote_name, key, value);
} else if (!strcmp(subkey, "merge")) {
if (!value)
@@ -406,9 +427,11 @@ static int handle_config(const char *key, const char *value,
return 0;
/* Handle remote.* variables */
- if (!name && !strcmp(subkey, "pushdefault"))
+ if (!name && !strcmp(subkey, "pushdefault")) {
+ FREE_AND_NULL(remote_state->pushremote_name);
return git_config_string(&remote_state->pushremote_name, key,
value);
+ }
if (!name)
return 0;
@@ -475,12 +498,15 @@ static int handle_config(const char *key, const char *value,
else if (!strcmp(value, "--tags"))
remote->fetch_tags = 2;
} else if (!strcmp(subkey, "proxy")) {
+ FREE_AND_NULL(remote->http_proxy);
return git_config_string(&remote->http_proxy,
key, value);
} else if (!strcmp(subkey, "proxyauthmethod")) {
+ FREE_AND_NULL(remote->http_proxy_authmethod);
return git_config_string(&remote->http_proxy_authmethod,
key, value);
} else if (!strcmp(subkey, "vcs")) {
+ FREE_AND_NULL(remote->foreign_vcs);
return git_config_string(&remote->foreign_vcs, key, value);
}
return 0;
@@ -1318,18 +1344,21 @@ static int match_explicit(struct ref *src, struct ref *dst,
struct ref ***dst_tail,
struct refspec_item *rs)
{
- struct ref *matched_src, *matched_dst;
- int allocated_src;
+ struct ref *matched_src = NULL, *matched_dst = NULL;
+ int allocated_src = 0, ret;
const char *dst_value = rs->dst;
char *dst_guess;
- if (rs->pattern || rs->matching || rs->negative)
- return 0;
+ if (rs->pattern || rs->matching || rs->negative) {
+ ret = 0;
+ goto out;
+ }
- matched_src = matched_dst = NULL;
- if (match_explicit_lhs(src, rs, &matched_src, &allocated_src) < 0)
- return -1;
+ if (match_explicit_lhs(src, rs, &matched_src, &allocated_src) < 0) {
+ ret = -1;
+ goto out;
+ }
if (!dst_value) {
int flag;
@@ -1368,18 +1397,30 @@ static int match_explicit(struct ref *src, struct ref *dst,
dst_value);
break;
}
- if (!matched_dst)
- return -1;
- if (matched_dst->peer_ref)
- return error(_("dst ref %s receives from more than one src"),
- matched_dst->name);
- else {
+
+ if (!matched_dst) {
+ ret = -1;
+ goto out;
+ }
+
+ if (matched_dst->peer_ref) {
+ ret = error(_("dst ref %s receives from more than one src"),
+ matched_dst->name);
+ goto out;
+ } else {
matched_dst->peer_ref = allocated_src ?
matched_src :
copy_ref(matched_src);
matched_dst->force = rs->force;
+ matched_src = NULL;
}
- return 0;
+
+ ret = 0;
+
+out:
+ if (allocated_src)
+ free_one_ref(matched_src);
+ return ret;
}
static int match_explicit_refs(struct ref *src, struct ref *dst,
@@ -2040,6 +2081,8 @@ static struct ref *get_expanded_map(const struct ref *remote_refs,
!ignore_symref_update(expn_name, &scratch)) {
struct ref *cpy = copy_ref(ref);
+ if (cpy->peer_ref)
+ free_one_ref(cpy->peer_ref);
cpy->peer_ref = alloc_ref(expn_name);
if (refspec->force)
cpy->peer_ref->force = 1;
@@ -2797,16 +2840,26 @@ struct remote_state *remote_state_new(void)
void remote_state_clear(struct remote_state *remote_state)
{
+ struct hashmap_iter iter;
+ struct branch *b;
int i;
for (i = 0; i < remote_state->remotes_nr; i++)
remote_clear(remote_state->remotes[i]);
FREE_AND_NULL(remote_state->remotes);
+ FREE_AND_NULL(remote_state->pushremote_name);
remote_state->remotes_alloc = 0;
remote_state->remotes_nr = 0;
+ rewrites_release(&remote_state->rewrites);
+ rewrites_release(&remote_state->rewrites_push);
+
hashmap_clear_and_free(&remote_state->remotes_hash, struct remote, ent);
- hashmap_clear_and_free(&remote_state->branches_hash, struct remote, ent);
+ hashmap_for_each_entry(&remote_state->branches_hash, &iter, b, ent) {
+ branch_release(b);
+ free(b);
+ }
+ hashmap_clear(&remote_state->branches_hash);
}
/*
diff --git a/revision.c b/revision.c
index ac94f8d429..2d7ad2bddf 100644
--- a/revision.c
+++ b/revision.c
@@ -4407,6 +4407,7 @@ static struct commit *get_revision_internal(struct rev_info *revs)
c = get_revision_1(revs);
if (!c)
break;
+ free_commit_buffer(revs->repo->parsed_objects, c);
}
}
diff --git a/run-command.c b/run-command.c
index 45ba544932..94f2f3079f 100644
--- a/run-command.c
+++ b/run-command.c
@@ -1808,16 +1808,26 @@ void run_processes_parallel(const struct run_process_parallel_opts *opts)
int prepare_auto_maintenance(int quiet, struct child_process *maint)
{
- int enabled;
+ int enabled, auto_detach;
if (!git_config_get_bool("maintenance.auto", &enabled) &&
!enabled)
return 0;
+ /*
+ * When `maintenance.autoDetach` isn't set, then we fall back to
+ * honoring `gc.autoDetach`. This is somewhat weird, but required to
+ * retain behaviour from when we used to run git-gc(1) here.
+ */
+ if (git_config_get_bool("maintenance.autodetach", &auto_detach) &&
+ git_config_get_bool("gc.autodetach", &auto_detach))
+ auto_detach = 1;
+
maint->git_cmd = 1;
maint->close_object_store = 1;
strvec_pushl(&maint->args, "maintenance", "run", "--auto", NULL);
strvec_push(&maint->args, quiet ? "--quiet" : "--no-quiet");
+ strvec_push(&maint->args, auto_detach ? "--detach" : "--no-detach");
return 1;
}
diff --git a/scalar.c b/scalar.c
index 1fe8a93e65..6166a8dd4c 100644
--- a/scalar.c
+++ b/scalar.c
@@ -400,7 +400,8 @@ static int delete_enlistment(struct strbuf *enlistment)
* Dummy implementation; Using `get_version_info()` would cause a link error
* without this.
*/
-void load_builtin_commands(const char *prefix, struct cmdnames *cmds)
+void load_builtin_commands(const char *prefix UNUSED,
+ struct cmdnames *cmds UNUSED)
{
die("not implemented");
}
diff --git a/send-pack.c b/send-pack.c
index fa2f5eec17..9666b2c995 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -75,6 +75,7 @@ static int pack_objects(int fd, struct ref *refs, struct oid_array *advertised,
int i;
int rc;
+ trace2_region_enter("send_pack", "pack_objects", the_repository);
strvec_push(&po.args, "pack-objects");
strvec_push(&po.args, "--all-progress-implied");
strvec_push(&po.args, "--revs");
@@ -146,8 +147,10 @@ static int pack_objects(int fd, struct ref *refs, struct oid_array *advertised,
*/
if (rc > 128 && rc != 141)
error("pack-objects died of signal %d", rc - 128);
+ trace2_region_leave("send_pack", "pack_objects", the_repository);
return -1;
}
+ trace2_region_leave("send_pack", "pack_objects", the_repository);
return 0;
}
@@ -170,6 +173,7 @@ static int receive_status(struct packet_reader *reader, struct ref *refs)
int new_report = 0;
int once = 0;
+ trace2_region_enter("send_pack", "receive_status", the_repository);
hint = NULL;
ret = receive_unpack_status(reader);
while (1) {
@@ -268,6 +272,7 @@ static int receive_status(struct packet_reader *reader, struct ref *refs)
new_report = 1;
}
}
+ trace2_region_leave("send_pack", "receive_status", the_repository);
return ret;
}
@@ -512,8 +517,11 @@ int send_pack(struct send_pack_args *args,
}
git_config_get_bool("push.negotiate", &push_negotiate);
- if (push_negotiate)
+ if (push_negotiate) {
+ trace2_region_enter("send_pack", "push_negotiate", the_repository);
get_commons_through_negotiation(args->url, remote_refs, &commons);
+ trace2_region_leave("send_pack", "push_negotiate", the_repository);
+ }
if (!git_config_get_bool("push.usebitmaps", &use_bitmaps))
args->disable_bitmaps = !use_bitmaps;
@@ -641,10 +649,11 @@ int send_pack(struct send_pack_args *args,
/*
* Finally, tell the other end!
*/
- if (!args->dry_run && push_cert_nonce)
+ if (!args->dry_run && push_cert_nonce) {
cmds_sent = generate_push_cert(&req_buf, remote_refs, args,
cap_buf.buf, push_cert_nonce);
- else if (!args->dry_run)
+ trace2_printf("Generated push certificate");
+ } else if (!args->dry_run) {
for (ref = remote_refs; ref; ref = ref->next) {
char *old_hex, *new_hex;
@@ -664,6 +673,7 @@ int send_pack(struct send_pack_args *args,
old_hex, new_hex, ref->name);
}
}
+ }
if (use_push_options) {
struct string_list_item *item;
diff --git a/sequencer.c b/sequencer.c
index e592df0f51..8d01cd50ac 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -3794,12 +3794,13 @@ static int error_failed_squash(struct repository *r,
return error_with_patch(r, commit, subject, subject_len, opts, 1, 0);
}
-static int do_exec(struct repository *r, const char *command_line)
+static int do_exec(struct repository *r, const char *command_line, int quiet)
{
struct child_process cmd = CHILD_PROCESS_INIT;
int dirty, status;
- fprintf(stderr, _("Executing: %s\n"), command_line);
+ if (!quiet)
+ fprintf(stderr, _("Executing: %s\n"), command_line);
cmd.use_shell = 1;
strvec_push(&cmd.args, command_line);
strvec_push(&cmd.env, "GIT_CHERRY_PICK_HELP");
@@ -5014,7 +5015,7 @@ static int pick_commits(struct repository *r,
if (!opts->verbose)
term_clear_line();
*end_of_arg = '\0';
- res = do_exec(r, arg);
+ res = do_exec(r, arg, opts->quiet);
*end_of_arg = saved;
if (res) {
diff --git a/setup.c b/setup.c
index 2e11542a1f..29f8673921 100644
--- a/setup.c
+++ b/setup.c
@@ -1907,7 +1907,7 @@ struct template_dir_cb_data {
};
static int template_dir_cb(const char *key, const char *value,
- const struct config_context *ctx, void *d)
+ const struct config_context *ctx UNUSED, void *d)
{
struct template_dir_cb_data *data = d;
diff --git a/sideband.c b/sideband.c
index 5b6b872a1c..4e816be4e9 100644
--- a/sideband.c
+++ b/sideband.c
@@ -32,28 +32,27 @@ static int use_sideband_colors(void)
const char *key = "color.remote";
struct strbuf sb = STRBUF_INIT;
- char *value;
+ const char *value;
int i;
if (use_sideband_colors_cached >= 0)
return use_sideband_colors_cached;
- if (!git_config_get_string(key, &value)) {
+ if (!git_config_get_string_tmp(key, &value))
use_sideband_colors_cached = git_config_colorbool(key, value);
- } else if (!git_config_get_string("color.ui", &value)) {
+ else if (!git_config_get_string_tmp("color.ui", &value))
use_sideband_colors_cached = git_config_colorbool("color.ui", value);
- } else {
+ else
use_sideband_colors_cached = GIT_COLOR_AUTO;
- }
for (i = 0; i < ARRAY_SIZE(keywords); i++) {
strbuf_reset(&sb);
strbuf_addf(&sb, "%s.%s", key, keywords[i].keyword);
- if (git_config_get_string(sb.buf, &value))
- continue;
- if (color_parse(value, keywords[i].color))
+ if (git_config_get_string_tmp(sb.buf, &value))
continue;
+ color_parse(value, keywords[i].color);
}
+
strbuf_release(&sb);
return use_sideband_colors_cached;
}
diff --git a/t/helper/test-example-tap.c b/t/helper/test-example-tap.c
index 914af88e0a..229d495ecf 100644
--- a/t/helper/test-example-tap.c
+++ b/t/helper/test-example-tap.c
@@ -70,7 +70,7 @@ static void t_empty(void)
; /* empty */
}
-int cmd__example_tap(int argc, const char **argv)
+int cmd__example_tap(int argc UNUSED, const char **argv UNUSED)
{
check(1);
diff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c
index 195e6278be..7782ae585e 100644
--- a/t/helper/test-hashmap.c
+++ b/t/helper/test-hashmap.c
@@ -138,7 +138,7 @@ static void perf_hashmap(unsigned int method, unsigned int rounds)
*
* perfhashmap method rounds -> test hashmap.[ch] performance
*/
-int cmd__hashmap(int argc, const char **argv)
+int cmd__hashmap(int argc UNUSED, const char **argv UNUSED)
{
struct string_list parts = STRING_LIST_INIT_NODUP;
struct strbuf line = STRBUF_INIT;
diff --git a/t/helper/test-mergesort.c b/t/helper/test-mergesort.c
index 42ccc87051..328bfe2977 100644
--- a/t/helper/test-mergesort.c
+++ b/t/helper/test-mergesort.c
@@ -122,7 +122,7 @@ static const struct dist *get_dist_by_name(const char *name)
return NULL;
}
-static void mode_copy(int *arr, int n)
+static void mode_copy(int *arr UNUSED, int n UNUSED)
{
/* nothing */
}
diff --git a/t/helper/test-reach.c b/t/helper/test-reach.c
index 5dd374379c..7314f6c0d8 100644
--- a/t/helper/test-reach.c
+++ b/t/helper/test-reach.c
@@ -116,6 +116,8 @@ int cmd__reach(int ac, const char **av)
repo_in_merge_bases_many(the_repository, A, X_nr, X_array, 0));
else if (!strcmp(av[1], "is_descendant_of"))
printf("%s(A,X):%d\n", av[1], repo_is_descendant_of(r, A, X));
+ else if (!strcmp(av[1], "get_branch_base_for_tip"))
+ printf("%s(A,X):%d\n", av[1], get_branch_base_for_tip(r, A, X_array, X_nr));
else if (!strcmp(av[1], "get_merge_bases_many")) {
struct commit_list *list = NULL;
if (repo_get_merge_bases_many(the_repository,
diff --git a/t/helper/test-read-midx.c b/t/helper/test-read-midx.c
index 69757e94fc..438fb9fc61 100644
--- a/t/helper/test-read-midx.c
+++ b/t/helper/test-read-midx.c
@@ -86,6 +86,8 @@ static int read_midx_checksum(const char *object_dir)
if (!m)
return 1;
printf("%s\n", hash_to_hex(get_midx_checksum(m)));
+
+ close_midx(m);
return 0;
}
@@ -102,10 +104,12 @@ static int read_midx_preferred_pack(const char *object_dir)
if (midx_preferred_pack(midx, &preferred_pack) < 0) {
warning(_("could not determine MIDX preferred pack"));
+ close_midx(midx);
return 1;
}
printf("%s\n", midx->pack_names[preferred_pack]);
+ close_midx(midx);
return 0;
}
@@ -122,8 +126,10 @@ static int read_midx_bitmapped_packs(const char *object_dir)
return 1;
for (i = 0; i < midx->num_packs + midx->num_packs_in_base; i++) {
- if (nth_bitmapped_pack(the_repository, midx, &pack, i) < 0)
+ if (nth_bitmapped_pack(the_repository, midx, &pack, i) < 0) {
+ close_midx(midx);
return 1;
+ }
printf("%s\n", pack_basename(pack.p));
printf(" bitmap_pos: %"PRIuMAX"\n", (uintmax_t)pack.bitmap_pos);
diff --git a/t/helper/test-reftable.c b/t/helper/test-reftable.c
index ded28ee5fb..ce5a94fbd3 100644
--- a/t/helper/test-reftable.c
+++ b/t/helper/test-reftable.c
@@ -1,16 +1,202 @@
+#include "git-compat-util.h"
+#include "hash.h"
+#include "hex.h"
#include "reftable/system.h"
+#include "reftable/reftable-error.h"
+#include "reftable/reftable-merged.h"
+#include "reftable/reftable-reader.h"
+#include "reftable/reftable-stack.h"
#include "reftable/reftable-tests.h"
#include "test-tool.h"
int cmd__reftable(int argc, const char **argv)
{
/* test from simple to complex. */
- block_test_main(argc, argv);
stack_test_main(argc, argv);
return 0;
}
+static void print_help(void)
+{
+ printf("usage: dump [-st] arg\n\n"
+ "options: \n"
+ " -b dump blocks\n"
+ " -t dump table\n"
+ " -s dump stack\n"
+ " -6 sha256 hash format\n"
+ " -h this help\n"
+ "\n");
+}
+
+static int dump_table(struct reftable_merged_table *mt)
+{
+ struct reftable_iterator it = { NULL };
+ struct reftable_ref_record ref = { NULL };
+ struct reftable_log_record log = { NULL };
+ const struct git_hash_algo *algop;
+ int err;
+
+ reftable_merged_table_init_ref_iterator(mt, &it);
+ err = reftable_iterator_seek_ref(&it, "");
+ if (err < 0)
+ return err;
+
+ algop = &hash_algos[hash_algo_by_id(reftable_merged_table_hash_id(mt))];
+
+ while (1) {
+ err = reftable_iterator_next_ref(&it, &ref);
+ if (err > 0)
+ break;
+ if (err < 0)
+ return err;
+
+ printf("ref{%s(%" PRIu64 ") ", ref.refname, ref.update_index);
+ switch (ref.value_type) {
+ case REFTABLE_REF_SYMREF:
+ printf("=> %s", ref.value.symref);
+ break;
+ case REFTABLE_REF_VAL2:
+ printf("val 2 %s", hash_to_hex_algop(ref.value.val2.value, algop));
+ printf("(T %s)", hash_to_hex_algop(ref.value.val2.target_value, algop));
+ break;
+ case REFTABLE_REF_VAL1:
+ printf("val 1 %s", hash_to_hex_algop(ref.value.val1, algop));
+ break;
+ case REFTABLE_REF_DELETION:
+ printf("delete");
+ break;
+ }
+ printf("}\n");
+ }
+ reftable_iterator_destroy(&it);
+ reftable_ref_record_release(&ref);
+
+ reftable_merged_table_init_log_iterator(mt, &it);
+ err = reftable_iterator_seek_log(&it, "");
+ if (err < 0)
+ return err;
+
+ while (1) {
+ err = reftable_iterator_next_log(&it, &log);
+ if (err > 0)
+ break;
+ if (err < 0)
+ return err;
+
+ switch (log.value_type) {
+ case REFTABLE_LOG_DELETION:
+ printf("log{%s(%" PRIu64 ") delete\n", log.refname,
+ log.update_index);
+ break;
+ case REFTABLE_LOG_UPDATE:
+ printf("log{%s(%" PRIu64 ") %s <%s> %" PRIu64 " %04d\n",
+ log.refname, log.update_index,
+ log.value.update.name ? log.value.update.name : "",
+ log.value.update.email ? log.value.update.email : "",
+ log.value.update.time,
+ log.value.update.tz_offset);
+ printf("%s => ", hash_to_hex_algop(log.value.update.old_hash, algop));
+ printf("%s\n\n%s\n}\n", hash_to_hex_algop(log.value.update.new_hash, algop),
+ log.value.update.message ? log.value.update.message : "");
+ break;
+ }
+ }
+ reftable_iterator_destroy(&it);
+ reftable_log_record_release(&log);
+ return 0;
+}
+
+static int dump_stack(const char *stackdir, uint32_t hash_id)
+{
+ struct reftable_stack *stack = NULL;
+ struct reftable_write_options opts = { .hash_id = hash_id };
+ struct reftable_merged_table *merged = NULL;
+
+ int err = reftable_new_stack(&stack, stackdir, &opts);
+ if (err < 0)
+ goto done;
+
+ merged = reftable_stack_merged_table(stack);
+ err = dump_table(merged);
+done:
+ if (stack)
+ reftable_stack_destroy(stack);
+ return err;
+}
+
+static int dump_reftable(const char *tablename)
+{
+ struct reftable_block_source src = { 0 };
+ struct reftable_merged_table *mt = NULL;
+ struct reftable_reader *r = NULL;
+ int err;
+
+ err = reftable_block_source_from_file(&src, tablename);
+ if (err < 0)
+ goto done;
+
+ err = reftable_reader_new(&r, &src, tablename);
+ if (err < 0)
+ goto done;
+
+ err = reftable_merged_table_new(&mt, &r, 1,
+ reftable_reader_hash_id(r));
+ if (err < 0)
+ goto done;
+
+ err = dump_table(mt);
+
+done:
+ reftable_merged_table_free(mt);
+ reftable_reader_decref(r);
+ return err;
+}
+
int cmd__dump_reftable(int argc, const char **argv)
{
- return reftable_dump_main(argc, (char *const *)argv);
+ int err = 0;
+ int opt_dump_blocks = 0;
+ int opt_dump_table = 0;
+ int opt_dump_stack = 0;
+ uint32_t opt_hash_id = GIT_SHA1_FORMAT_ID;
+ const char *arg = NULL, *argv0 = argv[0];
+
+ for (; argc > 1; argv++, argc--)
+ if (*argv[1] != '-')
+ break;
+ else if (!strcmp("-b", argv[1]))
+ opt_dump_blocks = 1;
+ else if (!strcmp("-t", argv[1]))
+ opt_dump_table = 1;
+ else if (!strcmp("-6", argv[1]))
+ opt_hash_id = GIT_SHA256_FORMAT_ID;
+ else if (!strcmp("-s", argv[1]))
+ opt_dump_stack = 1;
+ else if (!strcmp("-?", argv[1]) || !strcmp("-h", argv[1])) {
+ print_help();
+ return 2;
+ }
+
+ if (argc != 2) {
+ fprintf(stderr, "need argument\n");
+ print_help();
+ return 2;
+ }
+
+ arg = argv[1];
+
+ if (opt_dump_blocks) {
+ err = reftable_reader_print_blocks(arg);
+ } else if (opt_dump_table) {
+ err = dump_reftable(arg);
+ } else if (opt_dump_stack) {
+ err = dump_stack(arg, opt_hash_id);
+ }
+
+ if (err < 0) {
+ fprintf(stderr, "%s: %s: %s\n", argv0, arg,
+ reftable_error_str(err));
+ return 1;
+ }
+ return 0;
}
diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c
index 353d2aaaa4..7005b7d0bf 100644
--- a/t/helper/test-tool.c
+++ b/t/helper/test-tool.c
@@ -82,7 +82,6 @@ static struct test_cmd cmds[] = {
{ "trace2", cmd__trace2 },
{ "truncate", cmd__truncate },
{ "userdiff", cmd__userdiff },
- { "urlmatch-normalization", cmd__urlmatch_normalization },
{ "xml-encode", cmd__xml_encode },
{ "wildmatch", cmd__wildmatch },
#ifdef GIT_WINDOWS_NATIVE
diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h
index d3d8aa28e0..b51f79e774 100644
--- a/t/helper/test-tool.h
+++ b/t/helper/test-tool.h
@@ -75,7 +75,6 @@ int cmd__subprocess(int argc, const char **argv);
int cmd__trace2(int argc, const char **argv);
int cmd__truncate(int argc, const char **argv);
int cmd__userdiff(int argc, const char **argv);
-int cmd__urlmatch_normalization(int argc, const char **argv);
int cmd__xml_encode(int argc, const char **argv);
int cmd__wildmatch(int argc, const char **argv);
#ifdef GIT_WINDOWS_NATIVE
diff --git a/t/helper/test-urlmatch-normalization.c b/t/helper/test-urlmatch-normalization.c
deleted file mode 100644
index 86edd454f5..0000000000
--- a/t/helper/test-urlmatch-normalization.c
+++ /dev/null
@@ -1,56 +0,0 @@
-#include "test-tool.h"
-#include "git-compat-util.h"
-#include "urlmatch.h"
-
-int cmd__urlmatch_normalization(int argc, const char **argv)
-{
- const char usage[] = "test-tool urlmatch-normalization [-p | -l] <url1> | <url1> <url2>";
- char *url1 = NULL, *url2 = NULL;
- int opt_p = 0, opt_l = 0;
- int ret = 0;
-
- /*
- * For one url, succeed if url_normalize succeeds on it, fail otherwise.
- * For two urls, succeed only if url_normalize succeeds on both and
- * the results compare equal with strcmp. If -p is given (one url only)
- * and url_normalize succeeds, print the result followed by "\n". If
- * -l is given (one url only) and url_normalize succeeds, print the
- * returned length in decimal followed by "\n".
- */
-
- if (argc > 1 && !strcmp(argv[1], "-p")) {
- opt_p = 1;
- argc--;
- argv++;
- } else if (argc > 1 && !strcmp(argv[1], "-l")) {
- opt_l = 1;
- argc--;
- argv++;
- }
-
- if (argc < 2 || argc > 3)
- die("%s", usage);
-
- if (argc == 2) {
- struct url_info info;
- url1 = url_normalize(argv[1], &info);
- if (!url1)
- return 1;
- if (opt_p)
- printf("%s\n", url1);
- if (opt_l)
- printf("%u\n", (unsigned)info.url_len);
- goto cleanup;
- }
-
- if (opt_p || opt_l)
- die("%s", usage);
-
- url1 = url_normalize(argv[1], NULL);
- url2 = url_normalize(argv[2], NULL);
- ret = (url1 && url2 && !strcmp(url1, url2)) ? 0 : 1;
-cleanup:
- free(url1);
- free(url2);
- return ret;
-}
diff --git a/t/perf/p1500-graph-walks.sh b/t/perf/p1500-graph-walks.sh
index e14e7620cc..5b23ce5db9 100755
--- a/t/perf/p1500-graph-walks.sh
+++ b/t/perf/p1500-graph-walks.sh
@@ -20,6 +20,21 @@ test_expect_success 'setup' '
echo tag-$ref ||
return 1
done >tags &&
+
+ echo "A:HEAD" >test-tool-refs &&
+ for line in $(cat refs)
+ do
+ echo "X:$line" >>test-tool-refs || return 1
+ done &&
+ echo "A:HEAD" >test-tool-tags &&
+ for line in $(cat tags)
+ do
+ echo "X:$line" >>test-tool-tags || return 1
+ done &&
+
+ commit=$(git commit-tree $(git rev-parse HEAD^{tree})) &&
+ git update-ref refs/heads/disjoint-base $commit &&
+
git commit-graph write --reachable
'
@@ -47,4 +62,20 @@ test_perf 'contains: git tag --merged' '
xargs git tag --merged=HEAD <tags
'
+test_perf 'is-base check: test-tool reach (refs)' '
+ test-tool reach get_branch_base_for_tip <test-tool-refs
+'
+
+test_perf 'is-base check: test-tool reach (tags)' '
+ test-tool reach get_branch_base_for_tip <test-tool-tags
+'
+
+test_perf 'is-base check: git for-each-ref' '
+ git for-each-ref --format="%(is-base:HEAD)" --stdin <refs
+'
+
+test_perf 'is-base check: git for-each-ref (disjoint-base)' '
+ git for-each-ref --format="%(is-base:refs/heads/disjoint-base)" --stdin <refs
+'
+
test_done
diff --git a/t/t0110-urlmatch-normalization.sh b/t/t0110-urlmatch-normalization.sh
deleted file mode 100755
index 12d817fbd3..0000000000
--- a/t/t0110-urlmatch-normalization.sh
+++ /dev/null
@@ -1,182 +0,0 @@
-#!/bin/sh
-
-test_description='urlmatch URL normalization'
-
-TEST_PASSES_SANITIZE_LEAK=true
-. ./test-lib.sh
-
-# The base name of the test url files
-tu="$TEST_DIRECTORY/t0110/url"
-
-# Note that only file: URLs should be allowed without a host
-
-test_expect_success 'url scheme' '
- ! test-tool urlmatch-normalization "" &&
- ! test-tool urlmatch-normalization "_" &&
- ! test-tool urlmatch-normalization "scheme" &&
- ! test-tool urlmatch-normalization "scheme:" &&
- ! test-tool urlmatch-normalization "scheme:/" &&
- ! test-tool urlmatch-normalization "scheme://" &&
- ! test-tool urlmatch-normalization "file" &&
- ! test-tool urlmatch-normalization "file:" &&
- ! test-tool urlmatch-normalization "file:/" &&
- test-tool urlmatch-normalization "file://" &&
- ! test-tool urlmatch-normalization "://acme.co" &&
- ! test-tool urlmatch-normalization "x_test://acme.co" &&
- ! test-tool urlmatch-normalization "-test://acme.co" &&
- ! test-tool urlmatch-normalization "0test://acme.co" &&
- ! test-tool urlmatch-normalization "+test://acme.co" &&
- ! test-tool urlmatch-normalization ".test://acme.co" &&
- ! test-tool urlmatch-normalization "schem%6e://" &&
- test-tool urlmatch-normalization "x-Test+v1.0://acme.co" &&
- test "$(test-tool urlmatch-normalization -p "AbCdeF://x.Y")" = "abcdef://x.y/"
-'
-
-test_expect_success 'url authority' '
- ! test-tool urlmatch-normalization "scheme://user:pass@" &&
- ! test-tool urlmatch-normalization "scheme://?" &&
- ! test-tool urlmatch-normalization "scheme://#" &&
- ! test-tool urlmatch-normalization "scheme:///" &&
- ! test-tool urlmatch-normalization "scheme://:" &&
- ! test-tool urlmatch-normalization "scheme://:555" &&
- test-tool urlmatch-normalization "file://user:pass@" &&
- test-tool urlmatch-normalization "file://?" &&
- test-tool urlmatch-normalization "file://#" &&
- test-tool urlmatch-normalization "file:///" &&
- test-tool urlmatch-normalization "file://:" &&
- ! test-tool urlmatch-normalization "file://:555" &&
- test-tool urlmatch-normalization "scheme://user:pass@host" &&
- test-tool urlmatch-normalization "scheme://@host" &&
- test-tool urlmatch-normalization "scheme://%00@host" &&
- ! test-tool urlmatch-normalization "scheme://%%@host" &&
- test-tool urlmatch-normalization "scheme://host_" &&
- test-tool urlmatch-normalization "scheme://user:pass@host/" &&
- test-tool urlmatch-normalization "scheme://@host/" &&
- test-tool urlmatch-normalization "scheme://host/" &&
- test-tool urlmatch-normalization "scheme://host?x" &&
- test-tool urlmatch-normalization "scheme://host#x" &&
- test-tool urlmatch-normalization "scheme://host/@" &&
- test-tool urlmatch-normalization "scheme://host?@x" &&
- test-tool urlmatch-normalization "scheme://host#@x" &&
- test-tool urlmatch-normalization "scheme://[::1]" &&
- test-tool urlmatch-normalization "scheme://[::1]/" &&
- ! test-tool urlmatch-normalization "scheme://hos%41/" &&
- test-tool urlmatch-normalization "scheme://[invalid....:/" &&
- test-tool urlmatch-normalization "scheme://invalid....:]/" &&
- ! test-tool urlmatch-normalization "scheme://invalid....:[/" &&
- ! test-tool urlmatch-normalization "scheme://invalid....:["
-'
-
-test_expect_success 'url port checks' '
- test-tool urlmatch-normalization "xyz://q@some.host:" &&
- test-tool urlmatch-normalization "xyz://q@some.host:456/" &&
- ! test-tool urlmatch-normalization "xyz://q@some.host:0" &&
- ! test-tool urlmatch-normalization "xyz://q@some.host:0000000" &&
- test-tool urlmatch-normalization "xyz://q@some.host:0000001?" &&
- test-tool urlmatch-normalization "xyz://q@some.host:065535#" &&
- test-tool urlmatch-normalization "xyz://q@some.host:65535" &&
- ! test-tool urlmatch-normalization "xyz://q@some.host:65536" &&
- ! test-tool urlmatch-normalization "xyz://q@some.host:99999" &&
- ! test-tool urlmatch-normalization "xyz://q@some.host:100000" &&
- ! test-tool urlmatch-normalization "xyz://q@some.host:100001" &&
- test-tool urlmatch-normalization "http://q@some.host:80" &&
- test-tool urlmatch-normalization "https://q@some.host:443" &&
- test-tool urlmatch-normalization "http://q@some.host:80/" &&
- test-tool urlmatch-normalization "https://q@some.host:443?" &&
- ! test-tool urlmatch-normalization "http://q@:8008" &&
- ! test-tool urlmatch-normalization "http://:8080" &&
- ! test-tool urlmatch-normalization "http://:" &&
- test-tool urlmatch-normalization "xyz://q@some.host:456/" &&
- test-tool urlmatch-normalization "xyz://[::1]:456/" &&
- test-tool urlmatch-normalization "xyz://[::1]:/" &&
- ! test-tool urlmatch-normalization "xyz://[::1]:000/" &&
- ! test-tool urlmatch-normalization "xyz://[::1]:0%300/" &&
- ! test-tool urlmatch-normalization "xyz://[::1]:0x80/" &&
- ! test-tool urlmatch-normalization "xyz://[::1]:4294967297/" &&
- ! test-tool urlmatch-normalization "xyz://[::1]:030f/"
-'
-
-test_expect_success 'url port normalization' '
- test "$(test-tool urlmatch-normalization -p "http://x:800")" = "http://x:800/" &&
- test "$(test-tool urlmatch-normalization -p "http://x:0800")" = "http://x:800/" &&
- test "$(test-tool urlmatch-normalization -p "http://x:00000800")" = "http://x:800/" &&
- test "$(test-tool urlmatch-normalization -p "http://x:065535")" = "http://x:65535/" &&
- test "$(test-tool urlmatch-normalization -p "http://x:1")" = "http://x:1/" &&
- test "$(test-tool urlmatch-normalization -p "http://x:80")" = "http://x/" &&
- test "$(test-tool urlmatch-normalization -p "http://x:080")" = "http://x/" &&
- test "$(test-tool urlmatch-normalization -p "http://x:000000080")" = "http://x/" &&
- test "$(test-tool urlmatch-normalization -p "https://x:443")" = "https://x/" &&
- test "$(test-tool urlmatch-normalization -p "https://x:0443")" = "https://x/" &&
- test "$(test-tool urlmatch-normalization -p "https://x:000000443")" = "https://x/"
-'
-
-test_expect_success 'url general escapes' '
- ! test-tool urlmatch-normalization "http://x.y?%fg" &&
- test "$(test-tool urlmatch-normalization -p "X://W/%7e%41^%3a")" = "x://w/~A%5E%3A" &&
- test "$(test-tool urlmatch-normalization -p "X://W/:/?#[]@")" = "x://w/:/?#[]@" &&
- test "$(test-tool urlmatch-normalization -p "X://W/$&()*+,;=")" = "x://w/$&()*+,;=" &&
- test "$(test-tool urlmatch-normalization -p "X://W/'\''")" = "x://w/'\''" &&
- test "$(test-tool urlmatch-normalization -p "X://W?'\!'")" = "x://w/?'\!'"
-'
-
-test_expect_success !MINGW 'url high-bit escapes' '
- test "$(test-tool urlmatch-normalization -p "$(cat "$tu-1")")" = "x://q/%01%02%03%04%05%06%07%08%0E%0F%10%11%12" &&
- test "$(test-tool urlmatch-normalization -p "$(cat "$tu-2")")" = "x://q/%13%14%15%16%17%18%19%1B%1C%1D%1E%1F%7F" &&
- test "$(test-tool urlmatch-normalization -p "$(cat "$tu-3")")" = "x://q/%80%81%82%83%84%85%86%87%88%89%8A%8B%8C%8D%8E%8F" &&
- test "$(test-tool urlmatch-normalization -p "$(cat "$tu-4")")" = "x://q/%90%91%92%93%94%95%96%97%98%99%9A%9B%9C%9D%9E%9F" &&
- test "$(test-tool urlmatch-normalization -p "$(cat "$tu-5")")" = "x://q/%A0%A1%A2%A3%A4%A5%A6%A7%A8%A9%AA%AB%AC%AD%AE%AF" &&
- test "$(test-tool urlmatch-normalization -p "$(cat "$tu-6")")" = "x://q/%B0%B1%B2%B3%B4%B5%B6%B7%B8%B9%BA%BB%BC%BD%BE%BF" &&
- test "$(test-tool urlmatch-normalization -p "$(cat "$tu-7")")" = "x://q/%C0%C1%C2%C3%C4%C5%C6%C7%C8%C9%CA%CB%CC%CD%CE%CF" &&
- test "$(test-tool urlmatch-normalization -p "$(cat "$tu-8")")" = "x://q/%D0%D1%D2%D3%D4%D5%D6%D7%D8%D9%DA%DB%DC%DD%DE%DF" &&
- test "$(test-tool urlmatch-normalization -p "$(cat "$tu-9")")" = "x://q/%E0%E1%E2%E3%E4%E5%E6%E7%E8%E9%EA%EB%EC%ED%EE%EF" &&
- test "$(test-tool urlmatch-normalization -p "$(cat "$tu-10")")" = "x://q/%F0%F1%F2%F3%F4%F5%F6%F7%F8%F9%FA%FB%FC%FD%FE%FF"
-'
-
-test_expect_success 'url utf-8 escapes' '
- test "$(test-tool urlmatch-normalization -p "$(cat "$tu-11")")" = "x://q/%C2%80%DF%BF%E0%A0%80%EF%BF%BD%F0%90%80%80%F0%AF%BF%BD"
-'
-
-test_expect_success 'url username/password escapes' '
- test "$(test-tool urlmatch-normalization -p "x://%41%62(^):%70+d@foo")" = "x://Ab(%5E):p+d@foo/"
-'
-
-test_expect_success 'url normalized lengths' '
- test "$(test-tool urlmatch-normalization -l "Http://%4d%65:%4d^%70@The.Host")" = 25 &&
- test "$(test-tool urlmatch-normalization -l "http://%41:%42@x.y/%61/")" = 17 &&
- test "$(test-tool urlmatch-normalization -l "http://@x.y/^")" = 15
-'
-
-test_expect_success 'url . and .. segments' '
- test "$(test-tool urlmatch-normalization -p "x://y/.")" = "x://y/" &&
- test "$(test-tool urlmatch-normalization -p "x://y/./")" = "x://y/" &&
- test "$(test-tool urlmatch-normalization -p "x://y/a/.")" = "x://y/a" &&
- test "$(test-tool urlmatch-normalization -p "x://y/a/./")" = "x://y/a/" &&
- test "$(test-tool urlmatch-normalization -p "x://y/.?")" = "x://y/?" &&
- test "$(test-tool urlmatch-normalization -p "x://y/./?")" = "x://y/?" &&
- test "$(test-tool urlmatch-normalization -p "x://y/a/.?")" = "x://y/a?" &&
- test "$(test-tool urlmatch-normalization -p "x://y/a/./?")" = "x://y/a/?" &&
- test "$(test-tool urlmatch-normalization -p "x://y/a/./b/.././../c")" = "x://y/c" &&
- test "$(test-tool urlmatch-normalization -p "x://y/a/./b/../.././c/")" = "x://y/c/" &&
- test "$(test-tool urlmatch-normalization -p "x://y/a/./b/.././../c/././.././.")" = "x://y/" &&
- ! test-tool urlmatch-normalization "x://y/a/./b/.././../c/././.././.." &&
- test "$(test-tool urlmatch-normalization -p "x://y/a/./?/././..")" = "x://y/a/?/././.." &&
- test "$(test-tool urlmatch-normalization -p "x://y/%2e/")" = "x://y/" &&
- test "$(test-tool urlmatch-normalization -p "x://y/%2E/")" = "x://y/" &&
- test "$(test-tool urlmatch-normalization -p "x://y/a/%2e./")" = "x://y/" &&
- test "$(test-tool urlmatch-normalization -p "x://y/b/.%2E/")" = "x://y/" &&
- test "$(test-tool urlmatch-normalization -p "x://y/c/%2e%2E/")" = "x://y/"
-'
-
-# http://@foo specifies an empty user name but does not specify a password
-# http://foo specifies neither a user name nor a password
-# So they should not be equivalent
-test_expect_success 'url equivalents' '
- test-tool urlmatch-normalization "httP://x" "Http://X/" &&
- test-tool urlmatch-normalization "Http://%4d%65:%4d^%70@The.Host" "hTTP://Me:%4D^p@the.HOST:80/" &&
- ! test-tool urlmatch-normalization "https://@x.y/^" "httpS://x.y:443/^" &&
- test-tool urlmatch-normalization "https://@x.y/^" "httpS://@x.y:0443/^" &&
- test-tool urlmatch-normalization "https://@x.y/^/../abc" "httpS://@x.y:0443/abc" &&
- test-tool urlmatch-normalization "https://@x.y/^/.." "httpS://@x.y:0443/"
-'
-
-test_done
diff --git a/t/t0110/README b/t/t0110/README
deleted file mode 100644
index ad4a50ecd8..0000000000
--- a/t/t0110/README
+++ /dev/null
@@ -1,9 +0,0 @@
-The url data files in this directory contain URLs with characters
-in the range 0x01-0x1f and 0x7f-0xff to test the proper normalization
-of unprintable characters.
-
-A select few characters in the 0x01-0x1f range are skipped to help
-avoid problems running the test itself.
-
-The urls are in test files in this directory rather than being
-embedded in the test script for portability.
diff --git a/t/t0110/url-1 b/t/t0110/url-1
deleted file mode 100644
index 519019c5ce..0000000000
--- a/t/t0110/url-1
+++ /dev/null
@@ -1 +0,0 @@
-x://q/
diff --git a/t/t0110/url-10 b/t/t0110/url-10
deleted file mode 100644
index b9965de6a5..0000000000
--- a/t/t0110/url-10
+++ /dev/null
@@ -1 +0,0 @@
-x://q/ðñòóôõö÷øùúûüýþÿ
diff --git a/t/t0110/url-11 b/t/t0110/url-11
deleted file mode 100644
index f0a50f1009..0000000000
--- a/t/t0110/url-11
+++ /dev/null
@@ -1 +0,0 @@
-x://q/€߿ࠀ�ð€€ð¯¿½
diff --git a/t/t0110/url-2 b/t/t0110/url-2
deleted file mode 100644
index 43334b05b2..0000000000
--- a/t/t0110/url-2
+++ /dev/null
@@ -1 +0,0 @@
-x://q/
diff --git a/t/t0110/url-3 b/t/t0110/url-3
deleted file mode 100644
index 7378c7bec2..0000000000
--- a/t/t0110/url-3
+++ /dev/null
@@ -1 +0,0 @@
-x://q/€‚ƒ„…†‡ˆ‰Š‹ŒŽ
diff --git a/t/t0110/url-4 b/t/t0110/url-4
deleted file mode 100644
index 220b198c97..0000000000
--- a/t/t0110/url-4
+++ /dev/null
@@ -1 +0,0 @@
-x://q/‘’“”•–—˜™š›œžŸ
diff --git a/t/t0110/url-5 b/t/t0110/url-5
deleted file mode 100644
index 1ccd927779..0000000000
--- a/t/t0110/url-5
+++ /dev/null
@@ -1 +0,0 @@
-x://q/ ¡¢£¤¥¦§¨©ª«¬­®¯
diff --git a/t/t0110/url-6 b/t/t0110/url-6
deleted file mode 100644
index e8283aac6d..0000000000
--- a/t/t0110/url-6
+++ /dev/null
@@ -1 +0,0 @@
-x://q/°±²³´µ¶·¸¹º»¼½¾¿
diff --git a/t/t0110/url-7 b/t/t0110/url-7
deleted file mode 100644
index fa7c10b615..0000000000
--- a/t/t0110/url-7
+++ /dev/null
@@ -1 +0,0 @@
-x://q/ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ
diff --git a/t/t0110/url-8 b/t/t0110/url-8
deleted file mode 100644
index 79a0ba836f..0000000000
--- a/t/t0110/url-8
+++ /dev/null
@@ -1 +0,0 @@
-x://q/ÐÑÒÓÔÕÖרÙÚÛÜÝÞß
diff --git a/t/t0110/url-9 b/t/t0110/url-9
deleted file mode 100644
index 8b44bec48b..0000000000
--- a/t/t0110/url-9
+++ /dev/null
@@ -1 +0,0 @@
-x://q/àáâãäåæçèéêëìíîï
diff --git a/t/t1092-sparse-checkout-compatibility.sh b/t/t1092-sparse-checkout-compatibility.sh
index a2c0e1b4dc..6fa7f5e958 100755
--- a/t/t1092-sparse-checkout-compatibility.sh
+++ b/t/t1092-sparse-checkout-compatibility.sh
@@ -803,6 +803,8 @@ test_expect_success 'update-index --remove outside sparse definition' '
test_sparse_match git diff --cached --name-status &&
test_cmp expect sparse-checkout-out &&
+ test_sparse_match git diff-index --cached HEAD &&
+
# Reset the state
test_all_match git reset --hard &&
@@ -812,6 +814,8 @@ test_expect_success 'update-index --remove outside sparse definition' '
test_sparse_match git diff --cached --name-status &&
test_must_be_empty sparse-checkout-out &&
+ test_sparse_match git diff-index --cached HEAD &&
+
# Reset the state
test_all_match git reset --hard &&
@@ -823,7 +827,9 @@ test_expect_success 'update-index --remove outside sparse definition' '
D folder1/a
EOF
test_sparse_match git diff --cached --name-status &&
- test_cmp expect sparse-checkout-out
+ test_cmp expect sparse-checkout-out &&
+
+ test_sparse_match git diff-index --cached HEAD
'
test_expect_success 'update-index with directories' '
@@ -1551,7 +1557,7 @@ test_expect_success 'sparse-index is not expanded: describe' '
ensure_not_expanded describe
'
-test_expect_success 'sparse index is not expanded: diff' '
+test_expect_success 'sparse index is not expanded: diff and diff-index' '
init_repos &&
write_script edit-contents <<-\EOF &&
@@ -1568,6 +1574,7 @@ test_expect_success 'sparse index is not expanded: diff' '
test_all_match git diff --cached &&
ensure_not_expanded diff &&
ensure_not_expanded diff --cached &&
+ ensure_not_expanded diff-index --cached HEAD &&
# Add file outside cone
test_all_match git reset --hard &&
@@ -1582,6 +1589,7 @@ test_expect_success 'sparse index is not expanded: diff' '
test_all_match git diff --cached &&
ensure_not_expanded diff &&
ensure_not_expanded diff --cached &&
+ ensure_not_expanded diff-index --cached HEAD &&
# Merge conflict outside cone
# The sparse checkout will report a warning that is not in the
@@ -1594,7 +1602,8 @@ test_expect_success 'sparse index is not expanded: diff' '
test_all_match git diff &&
test_all_match git diff --cached &&
ensure_not_expanded diff &&
- ensure_not_expanded diff --cached
+ ensure_not_expanded diff --cached &&
+ ensure_not_expanded diff-index --cached HEAD
'
test_expect_success 'sparse index is not expanded: show and rev-parse' '
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index ae34bfad60..bd8bcc381a 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -235,6 +235,12 @@ test_expect_success 'rebase --merge -q is quiet' '
test_must_be_empty output.out
'
+test_expect_success 'rebase --exec -q is quiet' '
+ git checkout -B quiet topic &&
+ git rebase --exec true -q main >output.out 2>&1 &&
+ test_must_be_empty output.out
+'
+
test_expect_success 'Rebase a commit that sprinkles CRs in' '
(
echo "One" &&
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 5e2b6c80ea..232e1394e8 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -5,6 +5,7 @@ test_description='git am running'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup: messages' '
diff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh
index 79e5f42760..2265ff8872 100755
--- a/t/t4203-mailmap.sh
+++ b/t/t4203-mailmap.sh
@@ -72,12 +72,46 @@ test_expect_success 'check-mailmap --stdin arguments: mapping' '
test_cmp expect actual
'
-test_expect_success 'check-mailmap bogus contact' '
- test_must_fail git check-mailmap bogus
+test_expect_success 'check-mailmap simple address: mapping' '
+ test_when_finished "rm .mailmap" &&
+ cat >.mailmap <<-EOF &&
+ New Name <$GIT_AUTHOR_EMAIL>
+ EOF
+ cat .mailmap >expect &&
+ git check-mailmap "$GIT_AUTHOR_EMAIL" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'check-mailmap --stdin simple address: mapping' '
+ test_when_finished "rm .mailmap" &&
+ cat >.mailmap <<-EOF &&
+ New Name <$GIT_AUTHOR_EMAIL>
+ EOF
+ cat >stdin <<-EOF &&
+ $GIT_AUTHOR_EMAIL
+ EOF
+ cat .mailmap >expect &&
+ git check-mailmap --stdin <stdin >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'check-mailmap simple address: no mapping' '
+ cat >expect <<-EOF &&
+ <bugs@company.xx>
+ EOF
+ git check-mailmap "bugs@company.xx" >actual &&
+ test_cmp expect actual
'
-test_expect_success 'check-mailmap bogus contact --stdin' '
- test_must_fail git check-mailmap --stdin bogus </dev/null
+test_expect_success 'check-mailmap --stdin simple address: no mapping' '
+ cat >expect <<-EOF &&
+ <bugs@company.xx>
+ EOF
+ cat >stdin <<-EOF &&
+ bugs@company.xx
+ EOF
+ git check-mailmap --stdin <stdin >actual &&
+ test_cmp expect actual
'
test_expect_success 'No mailmap' '
diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh
index 158b49d4b6..eb63ce011f 100755
--- a/t/t4205-log-pretty-formats.sh
+++ b/t/t4205-log-pretty-formats.sh
@@ -5,6 +5,8 @@
#
test_description='Test pretty formats'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Tested non-UTF-8 encoding
diff --git a/t/t4301-merge-tree-write-tree.sh b/t/t4301-merge-tree-write-tree.sh
index eea19907b5..37f1cd7364 100755
--- a/t/t4301-merge-tree-write-tree.sh
+++ b/t/t4301-merge-tree-write-tree.sh
@@ -2,6 +2,7 @@
test_description='git merge-tree --write-tree'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# This test is ort-specific
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 72b8d0ff02..7abba8a4b2 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -25,6 +25,7 @@ commit id embedding:
'
TEST_CREATE_REPO_NO_TEMPLATE=1
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
SUBSTFORMAT=%H%n
diff --git a/t/t5003-archive-zip.sh b/t/t5003-archive-zip.sh
index 961c6aac25..01f591c99b 100755
--- a/t/t5003-archive-zip.sh
+++ b/t/t5003-archive-zip.sh
@@ -3,6 +3,7 @@
test_description='git archive --format=zip test'
TEST_CREATE_REPO_NO_TEMPLATE=1
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
SUBSTFORMAT=%H%n
diff --git a/t/t5100-mailinfo.sh b/t/t5100-mailinfo.sh
index c8d0655454..065156c1f3 100755
--- a/t/t5100-mailinfo.sh
+++ b/t/t5100-mailinfo.sh
@@ -5,6 +5,7 @@
test_description='git mailinfo and git mailsplit test'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
DATA="$TEST_DIRECTORY/t5100"
diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh
index 4ad023c846..3b9dae331a 100755
--- a/t/t5300-pack-object.sh
+++ b/t/t5300-pack-object.sh
@@ -635,4 +635,43 @@ test_expect_success 'negative window clamps to 0' '
check_deltas stderr = 0
'
+for hash in sha1 sha256
+do
+ test_expect_success "verify-pack with $hash packfile" '
+ test_when_finished "rm -rf repo" &&
+ git init --object-format=$hash repo &&
+ test_commit -C repo initial &&
+ git -C repo repack -ad &&
+ git -C repo verify-pack "$(pwd)"/repo/.git/objects/pack/*.idx &&
+ if test $hash = sha1
+ then
+ nongit git verify-pack "$(pwd)"/repo/.git/objects/pack/*.idx
+ else
+ # We have no way to identify the hash used by packfiles
+ # or indices, so we always fall back to SHA1.
+ nongit test_must_fail git verify-pack "$(pwd)"/repo/.git/objects/pack/*.idx &&
+ # But with an explicit object format we should succeed.
+ nongit git verify-pack --object-format=$hash "$(pwd)"/repo/.git/objects/pack/*.idx
+ fi
+ '
+
+ test_expect_success "index-pack outside of a $hash repository" '
+ test_when_finished "rm -rf repo" &&
+ git init --object-format=$hash repo &&
+ test_commit -C repo initial &&
+ git -C repo repack -ad &&
+ git -C repo index-pack --verify "$(pwd)"/repo/.git/objects/pack/*.pack &&
+ if test $hash = sha1
+ then
+ nongit git index-pack --verify "$(pwd)"/repo/.git/objects/pack/*.pack
+ else
+ # We have no way to identify the hash used by packfiles
+ # or indices, so we always fall back to SHA1.
+ nongit test_must_fail git index-pack --verify "$(pwd)"/repo/.git/objects/pack/*.pack 2>err &&
+ # But with an explicit object format we should succeed.
+ nongit git index-pack --object-format=$hash --verify "$(pwd)"/repo/.git/objects/pack/*.pack
+ fi
+ '
+done
+
test_done
diff --git a/t/t5304-prune.sh b/t/t5304-prune.sh
index 1f1f664871..e641df0116 100755
--- a/t/t5304-prune.sh
+++ b/t/t5304-prune.sh
@@ -7,6 +7,7 @@ test_description='prune'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
day=$((60*60*24))
diff --git a/t/t5319-multi-pack-index.sh b/t/t5319-multi-pack-index.sh
index ce1b58c732..fbbc218d04 100755
--- a/t/t5319-multi-pack-index.sh
+++ b/t/t5319-multi-pack-index.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='multi-pack-indexes'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-chunk.sh
. "$TEST_DIRECTORY"/lib-midx.sh
diff --git a/t/t5333-pseudo-merge-bitmaps.sh b/t/t5333-pseudo-merge-bitmaps.sh
index f052f395a7..1dd6284756 100755
--- a/t/t5333-pseudo-merge-bitmaps.sh
+++ b/t/t5333-pseudo-merge-bitmaps.sh
@@ -390,4 +390,60 @@ test_expect_success 'pseudo-merge reuse' '
)
'
+test_expect_success 'empty pseudo-merge group' '
+ git init pseudo-merge-empty-group &&
+ (
+ cd pseudo-merge-empty-group &&
+
+ # Ensure that a pseudo-merge group with no unstable
+ # commits does not generate an empty pseudo-merge
+ # bitmap.
+ git config bitmapPseudoMerge.empty.pattern refs/ &&
+
+ test_commit base &&
+ git repack -adb &&
+
+ test-tool bitmap dump-pseudo-merges >merges &&
+ test_line_count = 1 merges &&
+
+ test 0 -eq "$(grep -c commits=0 <merges)"
+ )
+'
+
+test_expect_success 'pseudo-merge closure' '
+ git init pseudo-merge-closure &&
+ (
+ cd pseudo-merge-closure &&
+
+ test_commit A &&
+ git repack -d &&
+
+ test_commit B &&
+
+ # Note that the contents of A is packed, but B is not. A
+ # (and the objects reachable from it) are thus visible
+ # to the MIDX, but the same is not true for B and its
+ # objects.
+ #
+ # Ensure that we do not attempt to create a pseudo-merge
+ # for B, depsite it matching the below pseudo-merge
+ # group pattern, as doing so would result in a failure
+ # to write a non-closed bitmap.
+ git config bitmapPseudoMerge.test.pattern refs/ &&
+ git config bitmapPseudoMerge.test.threshold now &&
+
+ git multi-pack-index write --bitmap &&
+
+ test-tool bitmap dump-pseudo-merges >pseudo-merges &&
+ test_line_count = 1 pseudo-merges &&
+
+ git rev-parse A >expect &&
+
+ test-tool bitmap list-commits >actual &&
+ test_cmp expect actual &&
+ test-tool bitmap dump-pseudo-merge-commits 0 >actual &&
+ test_cmp expect actual
+ )
+'
+
test_done
diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh
index 3f81f16e13..248c74d8ef 100755
--- a/t/t5400-send-pack.sh
+++ b/t/t5400-send-pack.sh
@@ -9,6 +9,7 @@ test_description='See why rewinding head breaks send-pack
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
cnt=64
diff --git a/t/t5401-update-hooks.sh b/t/t5401-update-hooks.sh
index d8cadeec73..3c1ea6086e 100755
--- a/t/t5401-update-hooks.sh
+++ b/t/t5401-update-hooks.sh
@@ -4,6 +4,8 @@
#
test_description='Test the update hook infrastructure.'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t5408-send-pack-stdin.sh b/t/t5408-send-pack-stdin.sh
index e8737df6f9..c3695a4d4e 100755
--- a/t/t5408-send-pack-stdin.sh
+++ b/t/t5408-send-pack-stdin.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='send-pack --stdin tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
create_ref () {
diff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh
index fa5de4500a..516b22fd96 100755
--- a/t/t5409-colorize-remote-messages.sh
+++ b/t/t5409-colorize-remote-messages.sh
@@ -2,6 +2,7 @@
test_description='remote messages are colorized on the client'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t5501-fetch-push-alternates.sh b/t/t5501-fetch-push-alternates.sh
index 66f19a4ef2..0c8668a1b8 100755
--- a/t/t5501-fetch-push-alternates.sh
+++ b/t/t5501-fetch-push-alternates.sh
@@ -4,6 +4,7 @@ test_description='fetch/push involving alternates'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
count_objects () {
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
index 08424e878e..532035933f 100755
--- a/t/t5505-remote.sh
+++ b/t/t5505-remote.sh
@@ -2,6 +2,7 @@
test_description='git remote porcelain-ish'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
setup_repository () {
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index 3b3991ab86..0890b9f61c 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -5,6 +5,7 @@ test_description='Per branch config variables affects "git fetch".
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-bundle.sh
diff --git a/t/t5519-push-alternates.sh b/t/t5519-push-alternates.sh
index 20ba604dfd..72e97b15fa 100755
--- a/t/t5519-push-alternates.sh
+++ b/t/t5519-push-alternates.sh
@@ -5,6 +5,7 @@ test_description='push to a repository that borrows from elsewhere'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t5536-fetch-conflicts.sh b/t/t5536-fetch-conflicts.sh
index 23bf696170..2dcbe79052 100755
--- a/t/t5536-fetch-conflicts.sh
+++ b/t/t5536-fetch-conflicts.sh
@@ -2,6 +2,7 @@
test_description='fetch handles conflicting refspecs correctly'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
D=$(pwd)
diff --git a/t/t5548-push-porcelain.sh b/t/t5548-push-porcelain.sh
index 6282728eaf..ecb3877aa4 100755
--- a/t/t5548-push-porcelain.sh
+++ b/t/t5548-push-porcelain.sh
@@ -4,6 +4,7 @@
#
test_description='Test git push porcelain output'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Create commits in <repo> and assign each commit's oid to shell variables
diff --git a/t/t5553-set-upstream.sh b/t/t5553-set-upstream.sh
index 70e3376d31..33e919a17e 100755
--- a/t/t5553-set-upstream.sh
+++ b/t/t5553-set-upstream.sh
@@ -4,6 +4,7 @@ test_description='"git fetch/pull --set-upstream" basic tests.'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
check_config () {
diff --git a/t/t5574-fetch-output.sh b/t/t5574-fetch-output.sh
index 5883839a04..f7707326ea 100755
--- a/t/t5574-fetch-output.sh
+++ b/t/t5574-fetch-output.sh
@@ -5,6 +5,7 @@ test_description='git fetch output format'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'fetch with invalid output format configuration' '
diff --git a/t/t5616-partial-clone.sh b/t/t5616-partial-clone.sh
index 2da7291e37..8415884754 100755
--- a/t/t5616-partial-clone.sh
+++ b/t/t5616-partial-clone.sh
@@ -229,7 +229,7 @@ test_expect_success 'fetch --refetch triggers repacking' '
GIT_TRACE2_EVENT="$PWD/trace1.event" \
git -C pc1 fetch --refetch origin &&
- test_subcommand git maintenance run --auto --no-quiet <trace1.event &&
+ test_subcommand git maintenance run --auto --no-quiet --detach <trace1.event &&
grep \"param\":\"gc.autopacklimit\",\"value\":\"1\" trace1.event &&
grep \"param\":\"maintenance.incremental-repack.auto\",\"value\":\"-1\" trace1.event &&
@@ -238,7 +238,7 @@ test_expect_success 'fetch --refetch triggers repacking' '
-c gc.autoPackLimit=0 \
-c maintenance.incremental-repack.auto=1234 \
-C pc1 fetch --refetch origin &&
- test_subcommand git maintenance run --auto --no-quiet <trace2.event &&
+ test_subcommand git maintenance run --auto --no-quiet --detach <trace2.event &&
grep \"param\":\"gc.autopacklimit\",\"value\":\"0\" trace2.event &&
grep \"param\":\"maintenance.incremental-repack.auto\",\"value\":\"-1\" trace2.event &&
@@ -247,7 +247,7 @@ test_expect_success 'fetch --refetch triggers repacking' '
-c gc.autoPackLimit=1234 \
-c maintenance.incremental-repack.auto=0 \
-C pc1 fetch --refetch origin &&
- test_subcommand git maintenance run --auto --no-quiet <trace3.event &&
+ test_subcommand git maintenance run --auto --no-quiet --detach <trace3.event &&
grep \"param\":\"gc.autopacklimit\",\"value\":\"1\" trace3.event &&
grep \"param\":\"maintenance.incremental-repack.auto\",\"value\":\"0\" trace3.event
'
diff --git a/t/t5703-upload-pack-ref-in-want.sh b/t/t5703-upload-pack-ref-in-want.sh
index 191097171b..f75fae52c8 100755
--- a/t/t5703-upload-pack-ref-in-want.sh
+++ b/t/t5703-upload-pack-ref-in-want.sh
@@ -2,6 +2,7 @@
test_description='upload-pack ref-in-want'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
get_actual_refs () {
diff --git a/t/t5812-proto-disable-http.sh b/t/t5812-proto-disable-http.sh
index 769c717e88..f69959c64c 100755
--- a/t/t5812-proto-disable-http.sh
+++ b/t/t5812-proto-disable-http.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test disabling of git-over-http in clone/fetch'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY/lib-proto-disable.sh"
. "$TEST_DIRECTORY/lib-httpd.sh"
diff --git a/t/t6050-replace.sh b/t/t6050-replace.sh
index c6e9b33e44..d7702fc756 100755
--- a/t/t6050-replace.sh
+++ b/t/t6050-replace.sh
@@ -7,6 +7,7 @@ test_description='Tests replace refs functionality'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY/lib-gpg.sh"
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index eb6c8204e8..8d15713cc6 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -1907,6 +1907,15 @@ test_expect_success 'git for-each-ref with nested tags' '
test_cmp expect actual
'
+test_expect_success 'is-base atom with non-commits' '
+ git for-each-ref --format="%(is-base:HEAD) %(refname)" >out 2>err &&
+ grep "(HEAD) refs/heads/main" out &&
+
+ test_line_count = 2 err &&
+ grep "error: object .* is a commit, not a blob" err &&
+ grep "error: bad tag pointer to" err
+'
+
GRADE_FORMAT="%(signature:grade)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)"
TRUSTLEVEL_FORMAT="%(signature:trustlevel)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)"
diff --git a/t/t6500-gc.sh b/t/t6500-gc.sh
index 1b5909d1b7..5378455968 100755
--- a/t/t6500-gc.sh
+++ b/t/t6500-gc.sh
@@ -338,14 +338,14 @@ test_expect_success 'gc.maxCruftSize sets appropriate repack options' '
test_subcommand $cruft_max_size_opts --max-cruft-size=3145728 <trace2.txt
'
-run_and_wait_for_auto_gc () {
+run_and_wait_for_gc () {
# We read stdout from gc for the side effect of waiting until the
# background gc process exits, closing its fd 9. Furthermore, the
# variable assignment from a command substitution preserves the
# exit status of the main gc process.
# Note: this fd trickery doesn't work on Windows, but there is no
# need to, because on Win the auto gc always runs in the foreground.
- doesnt_matter=$(git gc --auto 9>&1)
+ doesnt_matter=$(git gc "$@" 9>&1)
}
test_expect_success 'background auto gc does not run if gc.log is present and recent but does if it is old' '
@@ -361,7 +361,7 @@ test_expect_success 'background auto gc does not run if gc.log is present and re
test-tool chmtime =-345600 .git/gc.log &&
git gc --auto &&
test_config gc.logexpiry 2.days &&
- run_and_wait_for_auto_gc &&
+ run_and_wait_for_gc --auto &&
ls .git/objects/pack/pack-*.pack >packs &&
test_line_count = 1 packs
'
@@ -391,11 +391,48 @@ test_expect_success 'background auto gc respects lock for all operations' '
printf "%d %s" "$shell_pid" "$hostname" >.git/gc.pid &&
# our gc should exit zero without doing anything
- run_and_wait_for_auto_gc &&
+ run_and_wait_for_gc --auto &&
(ls -1 .git/refs/heads .git/reftable >actual || true) &&
test_cmp expect actual
'
+test_expect_success '--detach overrides gc.autoDetach=false' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+
+ # Prepare the repository such that git-gc(1) ends up repacking.
+ test_commit "$(test_oid blob17_1)" &&
+ test_commit "$(test_oid blob17_2)" &&
+ git config gc.autodetach false &&
+ git config gc.auto 2 &&
+
+ # Note that we cannot use `test_cmp` here to compare stderr
+ # because it may contain output from `set -x`.
+ run_and_wait_for_gc --auto --detach 2>actual &&
+ test_grep "Auto packing the repository in background for optimum performance." actual
+ )
+'
+
+test_expect_success '--no-detach overrides gc.autoDetach=true' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+
+ # Prepare the repository such that git-gc(1) ends up repacking.
+ test_commit "$(test_oid blob17_1)" &&
+ test_commit "$(test_oid blob17_2)" &&
+ git config gc.autodetach true &&
+ git config gc.auto 2 &&
+
+ GIT_PROGRESS_DELAY=0 git gc --auto --no-detach 2>output &&
+ test_grep "Auto packing the repository for optimum performance." output &&
+ test_grep "Collecting referenced commits: 2, done." output
+ )
+'
+
# DO NOT leave a detached auto gc process running near the end of the
# test script: it can run long enough in the background to racily
# interfere with the cleanup in 'test_done'.
diff --git a/t/t6600-test-reach.sh b/t/t6600-test-reach.sh
index b330945f49..2591f8b8b3 100755
--- a/t/t6600-test-reach.sh
+++ b/t/t6600-test-reach.sh
@@ -612,4 +612,125 @@ test_expect_success 'for-each-ref merged:none' '
--format="%(refname)" --stdin
'
+# For get_branch_base_for_tip, we only care about
+# first-parent history. Here is the test graph with
+# second parents removed:
+#
+# (10,10)
+# /
+# (10,9) (9,10)
+# / /
+# (10,8) (9,9) (8,10)
+# / / /
+# ( continued...)
+# \ / / /
+# (3,1) (2,2) (1,3)
+# \ / /
+# (2,1) (1,2)
+# \ /
+# (1,1)
+#
+# In short, for a commit (i,j), the first-parent history
+# walks all commits (i, k) with k from j to 1, then the
+# commits (l, 1) with l from i to 1.
+
+test_expect_success 'get_branch_base_for_tip: none reach' '
+ # (2,3) branched from the first tip (i,4) in X with i > 2
+ cat >input <<-\EOF &&
+ A:commit-2-3
+ X:commit-1-2
+ X:commit-1-4
+ X:commit-4-4
+ X:commit-8-4
+ X:commit-10-4
+ EOF
+ echo "get_branch_base_for_tip(A,X):2" >expect &&
+ test_all_modes get_branch_base_for_tip
+'
+
+test_expect_success 'get_branch_base_for_tip: equal to tip' '
+ # (2,3) branched from the first tip (i,4) in X with i > 2
+ cat >input <<-\EOF &&
+ A:commit-8-4
+ X:commit-1-2
+ X:commit-1-4
+ X:commit-4-4
+ X:commit-8-4
+ X:commit-10-4
+ EOF
+ echo "get_branch_base_for_tip(A,X):3" >expect &&
+ test_all_modes get_branch_base_for_tip
+'
+
+test_expect_success 'get_branch_base_for_tip: all reach tip' '
+ # (2,3) branched from the first tip (i,4) in X with i > 2
+ cat >input <<-\EOF &&
+ A:commit-4-1
+ X:commit-4-2
+ X:commit-5-1
+ EOF
+ echo "get_branch_base_for_tip(A,X):0" >expect &&
+ test_all_modes get_branch_base_for_tip
+'
+
+test_expect_success 'for-each-ref is-base: none reach' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-4-2
+ refs/heads/commit-4-4
+ refs/heads/commit-8-4
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-1-1:
+ refs/heads/commit-4-2:(commit-2-3)
+ refs/heads/commit-4-4:
+ refs/heads/commit-8-4:
+ EOF
+ run_all_modes git for-each-ref \
+ --format="%(refname):%(is-base:commit-2-3)" --stdin
+'
+
+test_expect_success 'for-each-ref is-base: all reach' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-4-2
+ refs/heads/commit-5-1
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-4-2:(commit-4-1)
+ refs/heads/commit-5-1:
+ EOF
+ run_all_modes git for-each-ref \
+ --format="%(refname):%(is-base:commit-4-1)" --stdin
+'
+
+test_expect_success 'for-each-ref is-base: equal to tip' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-4-2
+ refs/heads/commit-5-1
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-4-2:(commit-4-2)
+ refs/heads/commit-5-1:
+ EOF
+ run_all_modes git for-each-ref \
+ --format="%(refname):%(is-base:commit-4-2)" --stdin
+'
+
+test_expect_success 'for-each-ref is-base:multiple' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-4-2
+ refs/heads/commit-4-4
+ refs/heads/commit-8-4
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-1-1[-]
+ refs/heads/commit-4-2[(commit-2-3)-]
+ refs/heads/commit-4-4[-]
+ refs/heads/commit-8-4[-(commit-6-5)]
+ EOF
+ run_all_modes git for-each-ref \
+ --format="%(refname)[%(is-base:commit-2-3)-%(is-base:commit-6-5)]" --stdin
+'
+
test_done
diff --git a/t/t7704-repack-cruft.sh b/t/t7704-repack-cruft.sh
index 959e6e2648..5db9f4e10f 100755
--- a/t/t7704-repack-cruft.sh
+++ b/t/t7704-repack-cruft.sh
@@ -2,6 +2,7 @@
test_description='git repack works correctly'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
objdir=.git/objects
diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh
index 8595489ceb..abae7a9754 100755
--- a/t/t7900-maintenance.sh
+++ b/t/t7900-maintenance.sh
@@ -49,22 +49,47 @@ test_expect_success 'run [--auto|--quiet]' '
git maintenance run --auto 2>/dev/null &&
GIT_TRACE2_EVENT="$(pwd)/run-no-quiet.txt" \
git maintenance run --no-quiet 2>/dev/null &&
- test_subcommand git gc --quiet <run-no-auto.txt &&
- test_subcommand ! git gc --auto --quiet <run-auto.txt &&
- test_subcommand git gc --no-quiet <run-no-quiet.txt
+ test_subcommand git gc --quiet --no-detach <run-no-auto.txt &&
+ test_subcommand ! git gc --auto --quiet --no-detach <run-auto.txt &&
+ test_subcommand git gc --no-quiet --no-detach <run-no-quiet.txt
'
test_expect_success 'maintenance.auto config option' '
GIT_TRACE2_EVENT="$(pwd)/default" git commit --quiet --allow-empty -m 1 &&
- test_subcommand git maintenance run --auto --quiet <default &&
+ test_subcommand git maintenance run --auto --quiet --detach <default &&
GIT_TRACE2_EVENT="$(pwd)/true" \
git -c maintenance.auto=true \
commit --quiet --allow-empty -m 2 &&
- test_subcommand git maintenance run --auto --quiet <true &&
+ test_subcommand git maintenance run --auto --quiet --detach <true &&
GIT_TRACE2_EVENT="$(pwd)/false" \
git -c maintenance.auto=false \
commit --quiet --allow-empty -m 3 &&
- test_subcommand ! git maintenance run --auto --quiet <false
+ test_subcommand ! git maintenance run --auto --quiet --detach <false
+'
+
+for cfg in maintenance.autoDetach gc.autoDetach
+do
+ test_expect_success "$cfg=true config option" '
+ test_when_finished "rm -f trace" &&
+ test_config $cfg true &&
+ GIT_TRACE2_EVENT="$(pwd)/trace" git commit --quiet --allow-empty -m 1 &&
+ test_subcommand git maintenance run --auto --quiet --detach <trace
+ '
+
+ test_expect_success "$cfg=false config option" '
+ test_when_finished "rm -f trace" &&
+ test_config $cfg false &&
+ GIT_TRACE2_EVENT="$(pwd)/trace" git commit --quiet --allow-empty -m 1 &&
+ test_subcommand git maintenance run --auto --quiet --no-detach <trace
+ '
+done
+
+test_expect_success "maintenance.autoDetach overrides gc.autoDetach" '
+ test_when_finished "rm -f trace" &&
+ test_config maintenance.autoDetach false &&
+ test_config gc.autoDetach true &&
+ GIT_TRACE2_EVENT="$(pwd)/trace" git commit --quiet --allow-empty -m 1 &&
+ test_subcommand git maintenance run --auto --quiet --no-detach <trace
'
test_expect_success 'register uses XDG_CONFIG_HOME config if it exists' '
@@ -129,9 +154,9 @@ test_expect_success 'run --task=<task>' '
git maintenance run --task=commit-graph 2>/dev/null &&
GIT_TRACE2_EVENT="$(pwd)/run-both.txt" \
git maintenance run --task=commit-graph --task=gc 2>/dev/null &&
- test_subcommand ! git gc --quiet <run-commit-graph.txt &&
- test_subcommand git gc --quiet <run-gc.txt &&
- test_subcommand git gc --quiet <run-both.txt &&
+ test_subcommand ! git gc --quiet --no-detach <run-commit-graph.txt &&
+ test_subcommand git gc --quiet --no-detach <run-gc.txt &&
+ test_subcommand git gc --quiet --no-detach <run-both.txt &&
test_subcommand git commit-graph write --split --reachable --no-progress <run-commit-graph.txt &&
test_subcommand ! git commit-graph write --split --reachable --no-progress <run-gc.txt &&
test_subcommand git commit-graph write --split --reachable --no-progress <run-both.txt
@@ -908,4 +933,62 @@ test_expect_success 'failed schedule prevents config change' '
done
'
+test_expect_success '--no-detach causes maintenance to not run in background' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+
+ # Prepare the repository such that git-maintenance(1) ends up
+ # outputting something.
+ test_commit something &&
+ git config set maintenance.gc.enabled false &&
+ git config set maintenance.loose-objects.enabled true &&
+ git config set maintenance.loose-objects.auto 1 &&
+ git config set maintenance.incremental-repack.enabled true &&
+
+ GIT_TRACE2_EVENT="$(pwd)/trace.txt" \
+ git maintenance run --no-detach >out 2>&1 &&
+ ! test_region maintenance detach trace.txt
+ )
+'
+
+test_expect_success '--detach causes maintenance to run in background' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+
+ test_commit something &&
+ git config set maintenance.gc.enabled false &&
+ git config set maintenance.loose-objects.enabled true &&
+ git config set maintenance.loose-objects.auto 1 &&
+ git config set maintenance.incremental-repack.enabled true &&
+
+ # The extra file descriptor gets inherited to the child
+ # process, and by reading stdout we thus essentially wait for
+ # that descriptor to get closed, which indicates that the child
+ # is done, too.
+ does_not_matter=$(GIT_TRACE2_EVENT="$(pwd)/trace.txt" \
+ git maintenance run --detach 9>&1) &&
+ test_region maintenance detach trace.txt
+ )
+'
+
+test_expect_success 'repacking loose objects is quiet' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+
+ test_commit something &&
+ git config set maintenance.gc.enabled false &&
+ git config set maintenance.loose-objects.enabled true &&
+ git config set maintenance.loose-objects.auto 1 &&
+
+ git maintenance run --quiet >out 2>&1 &&
+ test_must_be_empty out
+ )
+'
+
test_done
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index 64a4ab3736..df5336bb7e 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -2084,22 +2084,24 @@ test_dump_aliases '--dump-aliases mailrc format' \
'bob' \
'chloe' \
'eve' <<-\EOF
- alias alice Alice W Land <awol@example.com>
- alias eve Eve <eve@example.com>
- alias bob Robert Bobbyton <bob@example.com>
+ alias alice "Alice W Land <awol@example.com>"
+ alias eve "Eve <eve@example.com>"
+ alias bob "Robert Bobbyton <bob@example.com>"
alias chloe chloe@example.com
EOF
test_dump_aliases '--dump-aliases pine format' \
'pine' \
'alice' \
+ 'bcgrp' \
'bob' \
'chloe' \
'eve' <<-\EOF
- alice Alice W Land <awol@example.com>
- eve Eve <eve@example.com>
- bob Robert Bobbyton <bob@example.com>
+ alice Alice W Land awol@example.com Friend
+ eve Eve eve@example.com
+ bob Robert Bobbyton bob@example.com
chloe chloe@example.com
+ bcgrp (bob, chloe, Other <o@example.com>)
EOF
test_dump_aliases '--dump-aliases gnus format' \
@@ -2118,6 +2120,110 @@ test_expect_success '--dump-aliases must be used alone' '
test_must_fail git send-email --dump-aliases --to=janice@example.com -1 refs/heads/accounting
'
+test_translate_aliases () {
+ msg="$1" && shift &&
+ filetype="$1" && shift &&
+ aliases="$1" && shift &&
+ printf '%s\n' "$@" >expect &&
+ cat >.tmp-email-aliases &&
+ printf '%s\n' "$aliases" >aliases &&
+
+ test_expect_success $PREREQ "$msg" '
+ clean_fake_sendmail && rm -fr outdir &&
+ git config --replace-all sendemail.aliasesfile \
+ "$(pwd)/.tmp-email-aliases" &&
+ git config sendemail.aliasfiletype "$filetype" &&
+ git send-email --translate-aliases <aliases 2>errors >actual &&
+ test_cmp expect actual
+ '
+}
+
+test_translate_aliases '--translate-aliases sendmail format' \
+ 'sendmail' \
+ 'alice bcgrp' \
+ 'Alice W Land <awol@example.com>' \
+ 'Robert Bobbyton <bob@example.com>' \
+ 'chloe@example.com' \
+ 'Other <o@example.com>' <<-\EOF
+ alice: Alice W Land <awol@example.com>
+ bob: Robert Bobbyton <bob@example.com>
+ chloe: chloe@example.com
+ abgroup: alice, bob
+ bcgrp: bob, chloe, Other <o@example.com>
+ EOF
+
+test_translate_aliases '--translate-aliases mutt format' \
+ 'mutt' \
+ 'donald bob' \
+ 'Donald C Carlton <donc@example.com>' \
+ 'Robert Bobbyton <bob@example.com>' <<-\EOF
+ alias alice Alice W Land <awol@example.com>
+ alias donald Donald C Carlton <donc@example.com>
+ alias bob Robert Bobbyton <bob@example.com>
+ alias chloe chloe@example.com
+ EOF
+
+test_translate_aliases '--translate-aliases mailrc format' \
+ 'mailrc' \
+ 'chloe eve alice' \
+ 'chloe@example.com' \
+ 'Eve <eve@example.com>' \
+ 'Alice W Land <awol@example.com>' <<-\EOF
+ alias alice "Alice W Land <awol@example.com>"
+ alias eve "Eve <eve@example.com>"
+ alias bob "Robert Bobbyton <bob@example.com>"
+ alias chloe chloe@example.com
+ EOF
+
+test_translate_aliases '--translate-aliases pine format' \
+ 'pine' \
+ 'eve bob bcgrp' \
+ 'eve@example.com' \
+ 'bob@example.com' \
+ 'bob@example.com' \
+ 'chloe@example.com' \
+ 'Other <o@example.com>' <<-\EOF
+ alice Alice W Land awol@example.com Friend
+ eve Eve eve@example.com
+ bob Robert Bobbyton bob@example.com
+ chloe chloe@example.com
+ bcgrp (bob, chloe, Other <o@example.com>)
+ EOF
+
+test_translate_aliases '--translate-aliases gnus format' \
+ 'gnus' \
+ 'alice chloe eve' \
+ 'awol@example.com' \
+ 'chloe@example.com' \
+ 'eve@example.com' <<-\EOF
+ (define-mail-alias "alice" "awol@example.com")
+ (define-mail-alias "eve" "eve@example.com")
+ (define-mail-alias "bob" "bob@example.com")
+ (define-mail-alias "chloe" "chloe@example.com")
+ EOF
+
+test_expect_success $PREREQ '--translate-aliases passes valid addresses through' '
+ cat >expect <<-\EOF &&
+ Other <o@example.com>
+ EOF
+ cat >aliases <<-\EOF &&
+ Other <o@example.com>
+ EOF
+ git send-email --translate-aliases <aliases >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success $PREREQ '--translate-aliases passes unknown aliases through' '
+ cat >expect <<-\EOF &&
+ blargh
+ EOF
+ cat >aliases <<-\EOF &&
+ blargh
+ EOF
+ git send-email --translate-aliases <aliases >actual &&
+ test_cmp expect actual
+'
+
test_expect_success $PREREQ 'aliases and sendemail.identity' '
test_must_fail git \
-c sendemail.identity=cloud \
@@ -2379,6 +2485,128 @@ test_expect_success $PREREQ 'leading and trailing whitespaces are removed' '
test_cmp expected-list actual-list
'
+test_expect_success $PREREQ 'mailmap support with --to' '
+ clean_fake_sendmail &&
+ test_config mailmap.file "mailmap.test" &&
+ cat >mailmap.test <<-EOF &&
+ Some Body <someone@example.com> <someone@example.org>
+ EOF
+ git format-patch --stdout -1 >a.patch &&
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ --to=someone@example.org \
+ --mailmap \
+ a.patch \
+ 2>errors >out &&
+ grep "^!someone@example\.com!$" commandline1
+'
+
+test_expect_success $PREREQ 'sendemail.mailmap configuration' '
+ clean_fake_sendmail &&
+ test_config mailmap.file "mailmap.test" &&
+ test_config sendemail.mailmap "true" &&
+ cat >mailmap.test <<-EOF &&
+ Some Body <someone@example.com> <someone@example.org>
+ EOF
+ git format-patch --stdout -1 >a.patch &&
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ --to=someone@example.org \
+ a.patch \
+ 2>errors >out &&
+ grep "^!someone@example\.com!$" commandline1
+'
+
+test_expect_success $PREREQ 'sendemail.mailmap.file configuration' '
+ clean_fake_sendmail &&
+ test_config sendemail.mailmap.file "mailmap.test" &&
+ test_config sendemail.mailmap "true" &&
+ cat >mailmap.test <<-EOF &&
+ Some Body <someone@example.com> <someone@example.org>
+ EOF
+ git format-patch --stdout -1 >a.patch &&
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ --to=someone@example.org \
+ a.patch \
+ 2>errors >out &&
+ grep "^!someone@example\.com!$" commandline1
+'
+
+test_expect_success $PREREQ 'sendemail.mailmap identity overrides configuration' '
+ clean_fake_sendmail &&
+ test_config sendemail.cloud.mailmap.file "mailmap.test" &&
+ test_config sendemail.mailmap "false" &&
+ test_config sendemail.cloud.mailmap "true" &&
+ cat >mailmap.test <<-EOF &&
+ Some Body <someone@example.com> <someone@example.org>
+ EOF
+ git format-patch --stdout -1 >a.patch &&
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ --identity=cloud \
+ --to=someone@example.org \
+ a.patch \
+ 2>errors >out &&
+ grep "^!someone@example\.com!$" commandline1
+'
+
+test_expect_success $PREREQ '--no-mailmap overrides configuration' '
+ clean_fake_sendmail &&
+ test_config sendemail.cloud.mailmap.file "mailmap.test" &&
+ test_config sendemail.mailmap "false" &&
+ test_config sendemail.cloud.mailmap "true" &&
+ cat >mailmap.test <<-EOF &&
+ Some Body <someone@example.com> <someone@example.org>
+ EOF
+ git format-patch --stdout -1 >a.patch &&
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ --identity=cloud \
+ --to=someone@example.org \
+ --no-mailmap \
+ a.patch \
+ 2>errors >out &&
+ grep "^!someone@example\.org!$" commandline1
+'
+
+test_expect_success $PREREQ 'mailmap support in To header' '
+ clean_fake_sendmail &&
+ test_config mailmap.file "mailmap.test" &&
+ cat >mailmap.test <<-EOF &&
+ <someone@example.com> <someone@example.org>
+ EOF
+ git format-patch --stdout -1 --to=someone@example.org >a.patch &&
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ --mailmap \
+ a.patch \
+ 2>errors >out &&
+ grep "^!someone@example\.com!$" commandline1
+'
+
+test_expect_success $PREREQ 'mailmap support in Cc header' '
+ clean_fake_sendmail &&
+ test_config mailmap.file "mailmap.test" &&
+ cat >mailmap.test <<-EOF &&
+ <someone@example.com> <someone@example.org>
+ EOF
+ git format-patch --stdout -1 --cc=someone@example.org >a.patch &&
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ --mailmap \
+ a.patch \
+ 2>errors >out &&
+ grep "^!someone@example\.com!$" commandline1
+'
+
test_expect_success $PREREQ 'test using command name with --sendmail-cmd' '
clean_fake_sendmail &&
PATH="$PWD:$PATH" \
diff --git a/t/unit-tests/t-ctype.c b/t/unit-tests/t-ctype.c
index e28a7f50f9..6043f0d9bc 100644
--- a/t/unit-tests/t-ctype.c
+++ b/t/unit-tests/t-ctype.c
@@ -31,7 +31,7 @@
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" \
"\x7f"
-int cmd_main(int argc, const char **argv) {
+int cmd_main(int argc UNUSED, const char **argv UNUSED) {
TEST_CHAR_CLASS(isspace, " \n\r\t");
TEST_CHAR_CLASS(isdigit, DIGIT);
TEST_CHAR_CLASS(isalpha, LOWER UPPER);
diff --git a/t/unit-tests/t-hash.c b/t/unit-tests/t-hash.c
index e9a78bf2c0..e62647019b 100644
--- a/t/unit-tests/t-hash.c
+++ b/t/unit-tests/t-hash.c
@@ -38,7 +38,7 @@ static void check_hash_data(const void *data, size_t data_length,
"SHA1 and SHA256 (%s) works", #literal); \
} while (0)
-int cmd_main(int argc, const char **argv)
+int cmd_main(int argc UNUSED, const char **argv UNUSED)
{
struct strbuf aaaaaaaaaa_100000 = STRBUF_INIT;
struct strbuf alphabet_100000 = STRBUF_INIT;
diff --git a/t/unit-tests/t-hashmap.c b/t/unit-tests/t-hashmap.c
index 09a48c2c4e..83b79dff39 100644
--- a/t/unit-tests/t-hashmap.c
+++ b/t/unit-tests/t-hashmap.c
@@ -322,7 +322,7 @@ static void t_alloc(struct hashmap *map, unsigned int ignore_case)
free(removed);
}
-static void t_intern(struct hashmap *map, unsigned int ignore_case)
+static void t_intern(void)
{
const char *values[] = { "value1", "Value1", "value2", "value2" };
@@ -356,6 +356,6 @@ int cmd_main(int argc UNUSED, const char **argv UNUSED)
TEST(setup(t_iterate, 0), "iterate works");
TEST(setup(t_iterate, 1), "iterate (case insensitive) works");
TEST(setup(t_alloc, 0), "grow / shrink works");
- TEST(setup(t_intern, 0), "string interning works");
+ TEST(t_intern(), "string interning works");
return test_done();
}
diff --git a/t/unit-tests/t-mem-pool.c b/t/unit-tests/t-mem-pool.c
index a0d57df761..fe500c704b 100644
--- a/t/unit-tests/t-mem-pool.c
+++ b/t/unit-tests/t-mem-pool.c
@@ -20,7 +20,7 @@ static void t_calloc_100(struct mem_pool *pool)
check(pool->mp_block->end != NULL);
}
-int cmd_main(int argc, const char **argv)
+int cmd_main(int argc UNUSED, const char **argv UNUSED)
{
TEST(setup_static(t_calloc_100, 1024 * 1024),
"mem_pool_calloc returns 100 zeroed bytes with big block");
diff --git a/t/unit-tests/t-prio-queue.c b/t/unit-tests/t-prio-queue.c
index 7a4e5780e1..fe6ae37935 100644
--- a/t/unit-tests/t-prio-queue.c
+++ b/t/unit-tests/t-prio-queue.c
@@ -69,7 +69,7 @@ static void test_prio_queue(int *input, size_t input_size,
#define TEST_INPUT(input, result) \
test_prio_queue(input, ARRAY_SIZE(input), result, ARRAY_SIZE(result))
-int cmd_main(int argc, const char **argv)
+int cmd_main(int argc UNUSED, const char **argv UNUSED)
{
TEST(TEST_INPUT(((int []){ 2, 6, 3, 10, 9, 5, 7, 4, 5, 8, 1, DUMP }),
((int []){ 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10 })),
diff --git a/t/unit-tests/t-reftable-basics.c b/t/unit-tests/t-reftable-basics.c
index 1dd60ab5f0..e5556ebf52 100644
--- a/t/unit-tests/t-reftable-basics.c
+++ b/t/unit-tests/t-reftable-basics.c
@@ -20,7 +20,7 @@ static int integer_needle_lesseq(size_t i, void *_args)
return args->needle <= args->haystack[i];
}
-int cmd_main(int argc, const char *argv[])
+int cmd_main(int argc UNUSED, const char *argv[] UNUSED)
{
if_test ("binary search with binsearch works") {
int haystack[] = { 2, 4, 6, 8, 10 };
diff --git a/t/unit-tests/t-reftable-block.c b/t/unit-tests/t-reftable-block.c
new file mode 100644
index 0000000000..f1a49485e2
--- /dev/null
+++ b/t/unit-tests/t-reftable-block.c
@@ -0,0 +1,371 @@
+/*
+Copyright 2020 Google LLC
+
+Use of this source code is governed by a BSD-style
+license that can be found in the LICENSE file or at
+https://developers.google.com/open-source/licenses/bsd
+*/
+
+#include "test-lib.h"
+#include "reftable/block.h"
+#include "reftable/blocksource.h"
+#include "reftable/constants.h"
+#include "reftable/reftable-error.h"
+
+static void t_ref_block_read_write(void)
+{
+ const int header_off = 21; /* random */
+ struct reftable_record recs[30];
+ const size_t N = ARRAY_SIZE(recs);
+ const size_t block_size = 1024;
+ struct reftable_block block = { 0 };
+ struct block_writer bw = {
+ .last_key = STRBUF_INIT,
+ };
+ struct reftable_record rec = {
+ .type = BLOCK_TYPE_REF,
+ };
+ size_t i = 0;
+ int ret;
+ struct block_reader br = { 0 };
+ struct block_iter it = BLOCK_ITER_INIT;
+ struct strbuf want = STRBUF_INIT, buf = STRBUF_INIT;
+
+ REFTABLE_CALLOC_ARRAY(block.data, block_size);
+ block.len = block_size;
+ block_source_from_strbuf(&block.source ,&buf);
+ block_writer_init(&bw, BLOCK_TYPE_REF, block.data, block_size,
+ header_off, hash_size(GIT_SHA1_FORMAT_ID));
+
+ rec.u.ref.refname = (char *) "";
+ rec.u.ref.value_type = REFTABLE_REF_DELETION;
+ ret = block_writer_add(&bw, &rec);
+ check_int(ret, ==, REFTABLE_API_ERROR);
+
+ for (i = 0; i < N; i++) {
+ rec.u.ref.refname = xstrfmt("branch%02"PRIuMAX, (uintmax_t)i);
+ rec.u.ref.value_type = REFTABLE_REF_VAL1;
+ memset(rec.u.ref.value.val1, i, GIT_SHA1_RAWSZ);
+
+ recs[i] = rec;
+ ret = block_writer_add(&bw, &rec);
+ rec.u.ref.refname = NULL;
+ rec.u.ref.value_type = REFTABLE_REF_DELETION;
+ check_int(ret, ==, 0);
+ }
+
+ ret = block_writer_finish(&bw);
+ check_int(ret, >, 0);
+
+ block_writer_release(&bw);
+
+ block_reader_init(&br, &block, header_off, block_size, GIT_SHA1_RAWSZ);
+
+ block_iter_seek_start(&it, &br);
+
+ for (i = 0; ; i++) {
+ ret = block_iter_next(&it, &rec);
+ check_int(ret, >=, 0);
+ if (ret > 0) {
+ check_int(i, ==, N);
+ break;
+ }
+ check(reftable_record_equal(&recs[i], &rec, GIT_SHA1_RAWSZ));
+ }
+
+ for (i = 0; i < N; i++) {
+ block_iter_reset(&it);
+ reftable_record_key(&recs[i], &want);
+
+ ret = block_iter_seek_key(&it, &br, &want);
+ check_int(ret, ==, 0);
+
+ ret = block_iter_next(&it, &rec);
+ check_int(ret, ==, 0);
+
+ check(reftable_record_equal(&recs[i], &rec, GIT_SHA1_RAWSZ));
+
+ want.len--;
+ ret = block_iter_seek_key(&it, &br, &want);
+ check_int(ret, ==, 0);
+
+ ret = block_iter_next(&it, &rec);
+ check_int(ret, ==, 0);
+ check(reftable_record_equal(&recs[10 * (i / 10)], &rec, GIT_SHA1_RAWSZ));
+ }
+
+ block_reader_release(&br);
+ block_iter_close(&it);
+ reftable_record_release(&rec);
+ reftable_block_done(&br.block);
+ strbuf_release(&want);
+ strbuf_release(&buf);
+ for (i = 0; i < N; i++)
+ reftable_record_release(&recs[i]);
+}
+
+static void t_log_block_read_write(void)
+{
+ const int header_off = 21;
+ struct reftable_record recs[30];
+ const size_t N = ARRAY_SIZE(recs);
+ const size_t block_size = 2048;
+ struct reftable_block block = { 0 };
+ struct block_writer bw = {
+ .last_key = STRBUF_INIT,
+ };
+ struct reftable_record rec = {
+ .type = BLOCK_TYPE_LOG,
+ };
+ size_t i = 0;
+ int ret;
+ struct block_reader br = { 0 };
+ struct block_iter it = BLOCK_ITER_INIT;
+ struct strbuf want = STRBUF_INIT, buf = STRBUF_INIT;
+
+ REFTABLE_CALLOC_ARRAY(block.data, block_size);
+ block.len = block_size;
+ block_source_from_strbuf(&block.source ,&buf);
+ block_writer_init(&bw, BLOCK_TYPE_LOG, block.data, block_size,
+ header_off, hash_size(GIT_SHA1_FORMAT_ID));
+
+ for (i = 0; i < N; i++) {
+ rec.u.log.refname = xstrfmt("branch%02"PRIuMAX , (uintmax_t)i);
+ rec.u.log.update_index = i;
+ rec.u.log.value_type = REFTABLE_LOG_UPDATE;
+
+ recs[i] = rec;
+ ret = block_writer_add(&bw, &rec);
+ rec.u.log.refname = NULL;
+ rec.u.log.value_type = REFTABLE_LOG_DELETION;
+ check_int(ret, ==, 0);
+ }
+
+ ret = block_writer_finish(&bw);
+ check_int(ret, >, 0);
+
+ block_writer_release(&bw);
+
+ block_reader_init(&br, &block, header_off, block_size, GIT_SHA1_RAWSZ);
+
+ block_iter_seek_start(&it, &br);
+
+ for (i = 0; ; i++) {
+ ret = block_iter_next(&it, &rec);
+ check_int(ret, >=, 0);
+ if (ret > 0) {
+ check_int(i, ==, N);
+ break;
+ }
+ check(reftable_record_equal(&recs[i], &rec, GIT_SHA1_RAWSZ));
+ }
+
+ for (i = 0; i < N; i++) {
+ block_iter_reset(&it);
+ strbuf_reset(&want);
+ strbuf_addstr(&want, recs[i].u.log.refname);
+
+ ret = block_iter_seek_key(&it, &br, &want);
+ check_int(ret, ==, 0);
+
+ ret = block_iter_next(&it, &rec);
+ check_int(ret, ==, 0);
+
+ check(reftable_record_equal(&recs[i], &rec, GIT_SHA1_RAWSZ));
+
+ want.len--;
+ ret = block_iter_seek_key(&it, &br, &want);
+ check_int(ret, ==, 0);
+
+ ret = block_iter_next(&it, &rec);
+ check_int(ret, ==, 0);
+ check(reftable_record_equal(&recs[10 * (i / 10)], &rec, GIT_SHA1_RAWSZ));
+ }
+
+ block_reader_release(&br);
+ block_iter_close(&it);
+ reftable_record_release(&rec);
+ reftable_block_done(&br.block);
+ strbuf_release(&want);
+ strbuf_release(&buf);
+ for (i = 0; i < N; i++)
+ reftable_record_release(&recs[i]);
+}
+
+static void t_obj_block_read_write(void)
+{
+ const int header_off = 21;
+ struct reftable_record recs[30];
+ const size_t N = ARRAY_SIZE(recs);
+ const size_t block_size = 1024;
+ struct reftable_block block = { 0 };
+ struct block_writer bw = {
+ .last_key = STRBUF_INIT,
+ };
+ struct reftable_record rec = {
+ .type = BLOCK_TYPE_OBJ,
+ };
+ size_t i = 0;
+ int ret;
+ struct block_reader br = { 0 };
+ struct block_iter it = BLOCK_ITER_INIT;
+ struct strbuf want = STRBUF_INIT, buf = STRBUF_INIT;
+
+ REFTABLE_CALLOC_ARRAY(block.data, block_size);
+ block.len = block_size;
+ block_source_from_strbuf(&block.source, &buf);
+ block_writer_init(&bw, BLOCK_TYPE_OBJ, block.data, block_size,
+ header_off, hash_size(GIT_SHA1_FORMAT_ID));
+
+ for (i = 0; i < N; i++) {
+ uint8_t bytes[] = { i, i + 1, i + 2, i + 3, i + 5 }, *allocated;
+ DUP_ARRAY(allocated, bytes, ARRAY_SIZE(bytes));
+
+ rec.u.obj.hash_prefix = allocated;
+ rec.u.obj.hash_prefix_len = 5;
+
+ recs[i] = rec;
+ ret = block_writer_add(&bw, &rec);
+ rec.u.obj.hash_prefix = NULL;
+ rec.u.obj.hash_prefix_len = 0;
+ check_int(ret, ==, 0);
+ }
+
+ ret = block_writer_finish(&bw);
+ check_int(ret, >, 0);
+
+ block_writer_release(&bw);
+
+ block_reader_init(&br, &block, header_off, block_size, GIT_SHA1_RAWSZ);
+
+ block_iter_seek_start(&it, &br);
+
+ for (i = 0; ; i++) {
+ ret = block_iter_next(&it, &rec);
+ check_int(ret, >=, 0);
+ if (ret > 0) {
+ check_int(i, ==, N);
+ break;
+ }
+ check(reftable_record_equal(&recs[i], &rec, GIT_SHA1_RAWSZ));
+ }
+
+ for (i = 0; i < N; i++) {
+ block_iter_reset(&it);
+ reftable_record_key(&recs[i], &want);
+
+ ret = block_iter_seek_key(&it, &br, &want);
+ check_int(ret, ==, 0);
+
+ ret = block_iter_next(&it, &rec);
+ check_int(ret, ==, 0);
+
+ check(reftable_record_equal(&recs[i], &rec, GIT_SHA1_RAWSZ));
+ }
+
+ block_reader_release(&br);
+ block_iter_close(&it);
+ reftable_record_release(&rec);
+ reftable_block_done(&br.block);
+ strbuf_release(&want);
+ strbuf_release(&buf);
+ for (i = 0; i < N; i++)
+ reftable_record_release(&recs[i]);
+}
+
+static void t_index_block_read_write(void)
+{
+ const int header_off = 21;
+ struct reftable_record recs[30];
+ const size_t N = ARRAY_SIZE(recs);
+ const size_t block_size = 1024;
+ struct reftable_block block = { 0 };
+ struct block_writer bw = {
+ .last_key = STRBUF_INIT,
+ };
+ struct reftable_record rec = {
+ .type = BLOCK_TYPE_INDEX,
+ .u.idx.last_key = STRBUF_INIT,
+ };
+ size_t i = 0;
+ int ret;
+ struct block_reader br = { 0 };
+ struct block_iter it = BLOCK_ITER_INIT;
+ struct strbuf want = STRBUF_INIT, buf = STRBUF_INIT;
+
+ REFTABLE_CALLOC_ARRAY(block.data, block_size);
+ block.len = block_size;
+ block_source_from_strbuf(&block.source, &buf);
+ block_writer_init(&bw, BLOCK_TYPE_INDEX, block.data, block_size,
+ header_off, hash_size(GIT_SHA1_FORMAT_ID));
+
+ for (i = 0; i < N; i++) {
+ strbuf_init(&recs[i].u.idx.last_key, 9);
+
+ recs[i].type = BLOCK_TYPE_INDEX;
+ strbuf_addf(&recs[i].u.idx.last_key, "branch%02"PRIuMAX, (uintmax_t)i);
+ recs[i].u.idx.offset = i;
+
+ ret = block_writer_add(&bw, &recs[i]);
+ check_int(ret, ==, 0);
+ }
+
+ ret = block_writer_finish(&bw);
+ check_int(ret, >, 0);
+
+ block_writer_release(&bw);
+
+ block_reader_init(&br, &block, header_off, block_size, GIT_SHA1_RAWSZ);
+
+ block_iter_seek_start(&it, &br);
+
+ for (i = 0; ; i++) {
+ ret = block_iter_next(&it, &rec);
+ check_int(ret, >=, 0);
+ if (ret > 0) {
+ check_int(i, ==, N);
+ break;
+ }
+ check(reftable_record_equal(&recs[i], &rec, GIT_SHA1_RAWSZ));
+ }
+
+ for (i = 0; i < N; i++) {
+ block_iter_reset(&it);
+ reftable_record_key(&recs[i], &want);
+
+ ret = block_iter_seek_key(&it, &br, &want);
+ check_int(ret, ==, 0);
+
+ ret = block_iter_next(&it, &rec);
+ check_int(ret, ==, 0);
+
+ check(reftable_record_equal(&recs[i], &rec, GIT_SHA1_RAWSZ));
+
+ want.len--;
+ ret = block_iter_seek_key(&it, &br, &want);
+ check_int(ret, ==, 0);
+
+ ret = block_iter_next(&it, &rec);
+ check_int(ret, ==, 0);
+ check(reftable_record_equal(&recs[10 * (i / 10)], &rec, GIT_SHA1_RAWSZ));
+ }
+
+ block_reader_release(&br);
+ block_iter_close(&it);
+ reftable_record_release(&rec);
+ reftable_block_done(&br.block);
+ strbuf_release(&want);
+ strbuf_release(&buf);
+ for (i = 0; i < N; i++)
+ reftable_record_release(&recs[i]);
+}
+
+int cmd_main(int argc UNUSED, const char *argv[] UNUSED)
+{
+ TEST(t_index_block_read_write(), "read-write operations on index blocks work");
+ TEST(t_log_block_read_write(), "read-write operations on log blocks work");
+ TEST(t_obj_block_read_write(), "read-write operations on obj blocks work");
+ TEST(t_ref_block_read_write(), "read-write operations on ref blocks work");
+
+ return test_done();
+}
diff --git a/t/unit-tests/t-reftable-merged.c b/t/unit-tests/t-reftable-merged.c
index b6263ee8b5..e9d100a01e 100644
--- a/t/unit-tests/t-reftable-merged.c
+++ b/t/unit-tests/t-reftable-merged.c
@@ -12,7 +12,6 @@ https://developers.google.com/open-source/licenses/bsd
#include "reftable/merged.h"
#include "reftable/reader.h"
#include "reftable/reftable-error.h"
-#include "reftable/reftable-generic.h"
#include "reftable/reftable-merged.h"
#include "reftable/reftable-writer.h"
@@ -22,7 +21,7 @@ static ssize_t strbuf_add_void(void *b, const void *data, const size_t sz)
return sz;
}
-static int noop_flush(void *arg)
+static int noop_flush(void *arg UNUSED)
{
return 0;
}
@@ -94,10 +93,8 @@ merged_table_from_records(struct reftable_ref_record **refs,
struct strbuf *buf, const size_t n)
{
struct reftable_merged_table *mt = NULL;
- struct reftable_table *tabs;
int err;
- REFTABLE_CALLOC_ARRAY(tabs, n);
REFTABLE_CALLOC_ARRAY(*readers, n);
REFTABLE_CALLOC_ARRAY(*source, n);
@@ -105,13 +102,12 @@ merged_table_from_records(struct reftable_ref_record **refs,
write_test_table(&buf[i], refs[i], sizes[i]);
block_source_from_strbuf(&(*source)[i], &buf[i]);
- err = reftable_new_reader(&(*readers)[i], &(*source)[i],
+ err = reftable_reader_new(&(*readers)[i], &(*source)[i],
"name");
check(!err);
- reftable_table_from_reader(&tabs[i], (*readers)[i]);
}
- err = reftable_new_merged_table(&mt, tabs, n, GIT_SHA1_FORMAT_ID);
+ err = reftable_merged_table_new(&mt, *readers, n, GIT_SHA1_FORMAT_ID);
check(!err);
return mt;
}
@@ -119,7 +115,7 @@ merged_table_from_records(struct reftable_ref_record **refs,
static void readers_destroy(struct reftable_reader **readers, const size_t n)
{
for (size_t i = 0; i < n; i++)
- reftable_reader_free(readers[i]);
+ reftable_reader_decref(readers[i]);
reftable_free(readers);
}
@@ -272,10 +268,8 @@ merged_table_from_log_records(struct reftable_log_record **logs,
struct strbuf *buf, const size_t n)
{
struct reftable_merged_table *mt = NULL;
- struct reftable_table *tabs;
int err;
- REFTABLE_CALLOC_ARRAY(tabs, n);
REFTABLE_CALLOC_ARRAY(*readers, n);
REFTABLE_CALLOC_ARRAY(*source, n);
@@ -283,13 +277,12 @@ merged_table_from_log_records(struct reftable_log_record **logs,
write_test_log_table(&buf[i], logs[i], sizes[i], i + 1);
block_source_from_strbuf(&(*source)[i], &buf[i]);
- err = reftable_new_reader(&(*readers)[i], &(*source)[i],
+ err = reftable_reader_new(&(*readers)[i], &(*source)[i],
"name");
check(!err);
- reftable_table_from_reader(&tabs[i], (*readers)[i]);
}
- err = reftable_new_merged_table(&mt, tabs, n, GIT_SHA1_FORMAT_ID);
+ err = reftable_merged_table_new(&mt, *readers, n, GIT_SHA1_FORMAT_ID);
check(!err);
return mt;
}
@@ -418,7 +411,6 @@ static void t_default_write_opts(void)
};
int err;
struct reftable_block_source source = { 0 };
- struct reftable_table *tab = reftable_calloc(1, sizeof(*tab));
uint32_t hash_id;
struct reftable_reader *rd = NULL;
struct reftable_merged_table *merged = NULL;
@@ -434,25 +426,24 @@ static void t_default_write_opts(void)
block_source_from_strbuf(&source, &buf);
- err = reftable_new_reader(&rd, &source, "filename");
+ err = reftable_reader_new(&rd, &source, "filename");
check(!err);
hash_id = reftable_reader_hash_id(rd);
check_int(hash_id, ==, GIT_SHA1_FORMAT_ID);
- reftable_table_from_reader(&tab[0], rd);
- err = reftable_new_merged_table(&merged, tab, 1, GIT_SHA256_FORMAT_ID);
+ err = reftable_merged_table_new(&merged, &rd, 1, GIT_SHA256_FORMAT_ID);
check_int(err, ==, REFTABLE_FORMAT_ERROR);
- err = reftable_new_merged_table(&merged, tab, 1, GIT_SHA1_FORMAT_ID);
+ err = reftable_merged_table_new(&merged, &rd, 1, GIT_SHA1_FORMAT_ID);
check(!err);
- reftable_reader_free(rd);
+ reftable_reader_decref(rd);
reftable_merged_table_free(merged);
strbuf_release(&buf);
}
-int cmd_main(int argc, const char *argv[])
+int cmd_main(int argc UNUSED, const char *argv[] UNUSED)
{
TEST(t_default_write_opts(), "merged table with default write opts");
TEST(t_merged_logs(), "merged table with multiple log updates for same ref");
diff --git a/t/unit-tests/t-reftable-pq.c b/t/unit-tests/t-reftable-pq.c
index 039bd0f1f9..ada4c19f18 100644
--- a/t/unit-tests/t-reftable-pq.c
+++ b/t/unit-tests/t-reftable-pq.c
@@ -142,7 +142,7 @@ static void t_merged_iter_pqueue_top(void)
merged_iter_pqueue_release(&pq);
}
-int cmd_main(int argc, const char *argv[])
+int cmd_main(int argc UNUSED, const char *argv[] UNUSED)
{
TEST(t_pq_record(), "pq works with record-based comparison");
TEST(t_pq_index(), "pq works with index-based comparison");
diff --git a/t/unit-tests/t-reftable-readwrite.c b/t/unit-tests/t-reftable-readwrite.c
index 2ce56a0523..82bfaf3287 100644
--- a/t/unit-tests/t-reftable-readwrite.c
+++ b/t/unit-tests/t-reftable-readwrite.c
@@ -26,7 +26,7 @@ static ssize_t strbuf_add_void(void *b, const void *data, size_t sz)
return sz;
}
-static int noop_flush(void *arg)
+static int noop_flush(void *arg UNUSED)
{
return 0;
}
@@ -205,7 +205,7 @@ static void t_log_write_read(void)
struct reftable_log_record log = { 0 };
int n;
struct reftable_iterator it = { 0 };
- struct reftable_reader rd = { 0 };
+ struct reftable_reader *reader;
struct reftable_block_source source = { 0 };
struct strbuf buf = STRBUF_INIT;
struct reftable_writer *w =
@@ -246,10 +246,10 @@ static void t_log_write_read(void)
block_source_from_strbuf(&source, &buf);
- err = init_reader(&rd, &source, "file.log");
+ err = reftable_reader_new(&reader, &source, "file.log");
check(!err);
- reftable_reader_init_ref_iterator(&rd, &it);
+ reftable_reader_init_ref_iterator(reader, &it);
err = reftable_iterator_seek_ref(&it, names[N - 1]);
check(!err);
@@ -264,7 +264,7 @@ static void t_log_write_read(void)
reftable_iterator_destroy(&it);
reftable_ref_record_release(&ref);
- reftable_reader_init_log_iterator(&rd, &it);
+ reftable_reader_init_log_iterator(reader, &it);
err = reftable_iterator_seek_log(&it, "");
check(!err);
@@ -285,7 +285,7 @@ static void t_log_write_read(void)
/* cleanup. */
strbuf_release(&buf);
free_names(names);
- reader_close(&rd);
+ reftable_reader_decref(reader);
}
static void t_log_zlib_corruption(void)
@@ -294,7 +294,7 @@ static void t_log_zlib_corruption(void)
.block_size = 256,
};
struct reftable_iterator it = { 0 };
- struct reftable_reader rd = { 0 };
+ struct reftable_reader *reader;
struct reftable_block_source source = { 0 };
struct strbuf buf = STRBUF_INIT;
struct reftable_writer *w =
@@ -337,18 +337,18 @@ static void t_log_zlib_corruption(void)
block_source_from_strbuf(&source, &buf);
- err = init_reader(&rd, &source, "file.log");
+ err = reftable_reader_new(&reader, &source, "file.log");
check(!err);
- reftable_reader_init_log_iterator(&rd, &it);
+ reftable_reader_init_log_iterator(reader, &it);
err = reftable_iterator_seek_log(&it, "refname");
check_int(err, ==, REFTABLE_ZLIB_ERROR);
reftable_iterator_destroy(&it);
/* cleanup. */
+ reftable_reader_decref(reader);
strbuf_release(&buf);
- reader_close(&rd);
}
static void t_table_read_write_sequential(void)
@@ -358,7 +358,7 @@ static void t_table_read_write_sequential(void)
int N = 50;
struct reftable_iterator it = { 0 };
struct reftable_block_source source = { 0 };
- struct reftable_reader rd = { 0 };
+ struct reftable_reader *reader;
int err = 0;
int j = 0;
@@ -366,10 +366,10 @@ static void t_table_read_write_sequential(void)
block_source_from_strbuf(&source, &buf);
- err = init_reader(&rd, &source, "file.ref");
+ err = reftable_reader_new(&reader, &source, "file.ref");
check(!err);
- reftable_reader_init_ref_iterator(&rd, &it);
+ reftable_reader_init_ref_iterator(reader, &it);
err = reftable_iterator_seek_ref(&it, "");
check(!err);
@@ -384,11 +384,11 @@ static void t_table_read_write_sequential(void)
reftable_ref_record_release(&ref);
}
check_int(j, ==, N);
+
reftable_iterator_destroy(&it);
+ reftable_reader_decref(reader);
strbuf_release(&buf);
free_names(names);
-
- reader_close(&rd);
}
static void t_table_write_small_table(void)
@@ -407,7 +407,7 @@ static void t_table_read_api(void)
char **names;
struct strbuf buf = STRBUF_INIT;
int N = 50;
- struct reftable_reader rd = { 0 };
+ struct reftable_reader *reader;
struct reftable_block_source source = { 0 };
int err;
struct reftable_log_record log = { 0 };
@@ -417,10 +417,10 @@ static void t_table_read_api(void)
block_source_from_strbuf(&source, &buf);
- err = init_reader(&rd, &source, "file.ref");
+ err = reftable_reader_new(&reader, &source, "file.ref");
check(!err);
- reftable_reader_init_ref_iterator(&rd, &it);
+ reftable_reader_init_ref_iterator(reader, &it);
err = reftable_iterator_seek_ref(&it, names[0]);
check(!err);
@@ -430,7 +430,7 @@ static void t_table_read_api(void)
strbuf_release(&buf);
free_names(names);
reftable_iterator_destroy(&it);
- reader_close(&rd);
+ reftable_reader_decref(reader);
strbuf_release(&buf);
}
@@ -439,7 +439,7 @@ static void t_table_read_write_seek(int index, int hash_id)
char **names;
struct strbuf buf = STRBUF_INIT;
int N = 50;
- struct reftable_reader rd = { 0 };
+ struct reftable_reader *reader;
struct reftable_block_source source = { 0 };
int err;
int i = 0;
@@ -452,17 +452,18 @@ static void t_table_read_write_seek(int index, int hash_id)
block_source_from_strbuf(&source, &buf);
- err = init_reader(&rd, &source, "file.ref");
+ err = reftable_reader_new(&reader, &source, "file.ref");
check(!err);
- check_int(hash_id, ==, reftable_reader_hash_id(&rd));
+ check_int(hash_id, ==, reftable_reader_hash_id(reader));
- if (!index)
- rd.ref_offsets.index_offset = 0;
- else
- check_int(rd.ref_offsets.index_offset, >, 0);
+ if (!index) {
+ reader->ref_offsets.index_offset = 0;
+ } else {
+ check_int(reader->ref_offsets.index_offset, >, 0);
+ }
for (i = 1; i < N; i++) {
- reftable_reader_init_ref_iterator(&rd, &it);
+ reftable_reader_init_ref_iterator(reader, &it);
err = reftable_iterator_seek_ref(&it, names[i]);
check(!err);
err = reftable_iterator_next_ref(&it, &ref);
@@ -478,7 +479,7 @@ static void t_table_read_write_seek(int index, int hash_id)
strbuf_addstr(&pastLast, names[N - 1]);
strbuf_addstr(&pastLast, "/");
- reftable_reader_init_ref_iterator(&rd, &it);
+ reftable_reader_init_ref_iterator(reader, &it);
err = reftable_iterator_seek_ref(&it, pastLast.buf);
if (err == 0) {
struct reftable_ref_record ref = { 0 };
@@ -493,7 +494,7 @@ static void t_table_read_write_seek(int index, int hash_id)
strbuf_release(&buf);
free_names(names);
- reader_close(&rd);
+ reftable_reader_decref(reader);
}
static void t_table_read_write_seek_linear(void)
@@ -525,7 +526,7 @@ static void t_table_refs_for(int indexed)
int i = 0;
int n;
int err;
- struct reftable_reader rd;
+ struct reftable_reader *reader;
struct reftable_block_source source = { 0 };
struct strbuf buf = STRBUF_INIT;
@@ -573,17 +574,17 @@ static void t_table_refs_for(int indexed)
block_source_from_strbuf(&source, &buf);
- err = init_reader(&rd, &source, "file.ref");
+ err = reftable_reader_new(&reader, &source, "file.ref");
check(!err);
if (!indexed)
- rd.obj_offsets.is_present = 0;
+ reader->obj_offsets.is_present = 0;
- reftable_reader_init_ref_iterator(&rd, &it);
+ reftable_reader_init_ref_iterator(reader, &it);
err = reftable_iterator_seek_ref(&it, "");
check(!err);
reftable_iterator_destroy(&it);
- err = reftable_reader_refs_for(&rd, &it, want_hash);
+ err = reftable_reader_refs_for(reader, &it, want_hash);
check(!err);
for (j = 0; ; j++) {
@@ -600,7 +601,7 @@ static void t_table_refs_for(int indexed)
strbuf_release(&buf);
free_names(want_names);
reftable_iterator_destroy(&it);
- reader_close(&rd);
+ reftable_reader_decref(reader);
}
static void t_table_refs_for_no_index(void)
@@ -635,7 +636,7 @@ static void t_write_empty_table(void)
block_source_from_strbuf(&source, &buf);
- err = reftable_new_reader(&rd, &source, "filename");
+ err = reftable_reader_new(&rd, &source, "filename");
check(!err);
reftable_reader_init_ref_iterator(rd, &it);
@@ -646,7 +647,7 @@ static void t_write_empty_table(void)
check_int(err, >, 0);
reftable_iterator_destroy(&it);
- reftable_reader_free(rd);
+ reftable_reader_decref(rd);
strbuf_release(&buf);
}
@@ -844,7 +845,7 @@ static void t_write_multiple_indices(void)
check_int(stats->log_stats.index_offset, >, 0);
block_source_from_strbuf(&source, &writer_buf);
- err = reftable_new_reader(&reader, &source, "filename");
+ err = reftable_reader_new(&reader, &source, "filename");
check(!err);
/*
@@ -857,7 +858,7 @@ static void t_write_multiple_indices(void)
reftable_iterator_destroy(&it);
reftable_writer_free(writer);
- reftable_reader_free(reader);
+ reftable_reader_decref(reader);
strbuf_release(&writer_buf);
strbuf_release(&buf);
}
@@ -901,7 +902,7 @@ static void t_write_multi_level_index(void)
check_int(stats->ref_stats.max_index_level, ==, 2);
block_source_from_strbuf(&source, &writer_buf);
- err = reftable_new_reader(&reader, &source, "filename");
+ err = reftable_reader_new(&reader, &source, "filename");
check(!err);
/*
@@ -913,7 +914,7 @@ static void t_write_multi_level_index(void)
reftable_iterator_destroy(&it);
reftable_writer_free(writer);
- reftable_reader_free(reader);
+ reftable_reader_decref(reader);
strbuf_release(&writer_buf);
strbuf_release(&buf);
}
@@ -922,11 +923,11 @@ static void t_corrupt_table_empty(void)
{
struct strbuf buf = STRBUF_INIT;
struct reftable_block_source source = { 0 };
- struct reftable_reader rd = { 0 };
+ struct reftable_reader *reader;
int err;
block_source_from_strbuf(&source, &buf);
- err = init_reader(&rd, &source, "file.log");
+ err = reftable_reader_new(&reader, &source, "file.log");
check_int(err, ==, REFTABLE_FORMAT_ERROR);
}
@@ -935,17 +936,18 @@ static void t_corrupt_table(void)
uint8_t zeros[1024] = { 0 };
struct strbuf buf = STRBUF_INIT;
struct reftable_block_source source = { 0 };
- struct reftable_reader rd = { 0 };
+ struct reftable_reader *reader;
int err;
strbuf_add(&buf, zeros, sizeof(zeros));
block_source_from_strbuf(&source, &buf);
- err = init_reader(&rd, &source, "file.log");
+ err = reftable_reader_new(&reader, &source, "file.log");
check_int(err, ==, REFTABLE_FORMAT_ERROR);
+
strbuf_release(&buf);
}
-int cmd_main(int argc, const char *argv[])
+int cmd_main(int argc UNUSED, const char *argv[] UNUSED)
{
TEST(t_buffer(), "strbuf works as blocksource");
TEST(t_corrupt_table(), "read-write on corrupted table");
diff --git a/t/unit-tests/t-reftable-record.c b/t/unit-tests/t-reftable-record.c
index cb649ee419..a7f67d4d9f 100644
--- a/t/unit-tests/t-reftable-record.c
+++ b/t/unit-tests/t-reftable-record.c
@@ -532,7 +532,7 @@ static void t_reftable_index_record_roundtrip(void)
strbuf_release(&in.u.idx.last_key);
}
-int cmd_main(int argc, const char *argv[])
+int cmd_main(int argc UNUSED, const char *argv[] UNUSED)
{
TEST(t_reftable_ref_record_comparison(), "comparison operations work on ref record");
TEST(t_reftable_log_record_comparison(), "comparison operations work on log record");
diff --git a/t/unit-tests/t-reftable-tree.c b/t/unit-tests/t-reftable-tree.c
index e7d774d774..700479d34b 100644
--- a/t/unit-tests/t-reftable-tree.c
+++ b/t/unit-tests/t-reftable-tree.c
@@ -75,7 +75,7 @@ static void t_infix_walk(void)
tree_free(root);
}
-int cmd_main(int argc, const char *argv[])
+int cmd_main(int argc UNUSED, const char *argv[] UNUSED)
{
TEST(t_tree_search(), "tree_search works");
TEST(t_infix_walk(), "infix_walk works");
diff --git a/t/unit-tests/t-strbuf.c b/t/unit-tests/t-strbuf.c
index 6027dafef7..3f4044d697 100644
--- a/t/unit-tests/t-strbuf.c
+++ b/t/unit-tests/t-strbuf.c
@@ -105,7 +105,7 @@ static void t_addstr(struct strbuf *buf, const void *data)
check_str(buf->buf + orig_len, text);
}
-int cmd_main(int argc, const char **argv)
+int cmd_main(int argc UNUSED, const char **argv UNUSED)
{
if (!TEST(t_static_init(), "static initialization works"))
test_skip_all("STRBUF_INIT is broken");
diff --git a/t/unit-tests/t-strcmp-offset.c b/t/unit-tests/t-strcmp-offset.c
index fe4c2706b1..6880f21161 100644
--- a/t/unit-tests/t-strcmp-offset.c
+++ b/t/unit-tests/t-strcmp-offset.c
@@ -24,7 +24,7 @@ static void check_strcmp_offset(const char *string1, const char *string2,
expect_offset), \
"strcmp_offset(%s, %s) works", #string1, #string2)
-int cmd_main(int argc, const char **argv)
+int cmd_main(int argc UNUSED, const char **argv UNUSED)
{
TEST_STRCMP_OFFSET("abc", "abc", 0, 3);
TEST_STRCMP_OFFSET("abc", "def", -1, 0);
diff --git a/t/unit-tests/t-strvec.c b/t/unit-tests/t-strvec.c
index c4bac8fc91..5c844cf0c9 100644
--- a/t/unit-tests/t-strvec.c
+++ b/t/unit-tests/t-strvec.c
@@ -19,7 +19,7 @@
} \
} while (0)
-int cmd_main(int argc, const char **argv)
+int cmd_main(int argc UNUSED, const char **argv UNUSED)
{
if_test ("static initialization") {
struct strvec vec = STRVEC_INIT;
diff --git a/t/unit-tests/t-trailer.c b/t/unit-tests/t-trailer.c
index 2ecca359d9..e1c6ad7461 100644
--- a/t/unit-tests/t-trailer.c
+++ b/t/unit-tests/t-trailer.c
@@ -308,7 +308,7 @@ static void run_t_trailer_iterator(void)
}
}
-int cmd_main(int argc, const char **argv)
+int cmd_main(int argc UNUSED, const char **argv UNUSED)
{
run_t_trailer_iterator();
return test_done();
diff --git a/t/unit-tests/t-urlmatch-normalization.c b/t/unit-tests/t-urlmatch-normalization.c
new file mode 100644
index 0000000000..1769c357b9
--- /dev/null
+++ b/t/unit-tests/t-urlmatch-normalization.c
@@ -0,0 +1,271 @@
+#include "test-lib.h"
+#include "urlmatch.h"
+
+static void check_url_normalizable(const char *url, unsigned int normalizable)
+{
+ char *url_norm = url_normalize(url, NULL);
+
+ if (!check_int(normalizable, ==, url_norm ? 1 : 0))
+ test_msg("input url: %s", url);
+ free(url_norm);
+}
+
+static void check_normalized_url(const char *url, const char *expect)
+{
+ char *url_norm = url_normalize(url, NULL);
+
+ if (!check_str(url_norm, expect))
+ test_msg("input url: %s", url);
+ free(url_norm);
+}
+
+static void compare_normalized_urls(const char *url1, const char *url2,
+ unsigned int equal)
+{
+ char *url1_norm = url_normalize(url1, NULL);
+ char *url2_norm = url_normalize(url2, NULL);
+
+ if (equal) {
+ if (!check_str(url1_norm, url2_norm))
+ test_msg("input url1: %s\n input url2: %s", url1,
+ url2);
+ } else if (!check_int(strcmp(url1_norm, url2_norm), !=, 0)) {
+ test_msg(" normalized url1: %s\n normalized url2: %s\n"
+ " input url1: %s\n input url2: %s",
+ url1_norm, url2_norm, url1, url2);
+ }
+ free(url1_norm);
+ free(url2_norm);
+}
+
+static void check_normalized_url_length(const char *url, size_t len)
+{
+ struct url_info info;
+ char *url_norm = url_normalize(url, &info);
+
+ if (!check_int(info.url_len, ==, len))
+ test_msg(" input url: %s\n normalized url: %s", url,
+ url_norm);
+ free(url_norm);
+}
+
+/* Note that only "file:" URLs should be allowed without a host */
+static void t_url_scheme(void)
+{
+ check_url_normalizable("", 0);
+ check_url_normalizable("_", 0);
+ check_url_normalizable("scheme", 0);
+ check_url_normalizable("scheme:", 0);
+ check_url_normalizable("scheme:/", 0);
+ check_url_normalizable("scheme://", 0);
+ check_url_normalizable("file", 0);
+ check_url_normalizable("file:", 0);
+ check_url_normalizable("file:/", 0);
+ check_url_normalizable("file://", 1);
+ check_url_normalizable("://acme.co", 0);
+ check_url_normalizable("x_test://acme.co", 0);
+ check_url_normalizable("-test://acme.co", 0);
+ check_url_normalizable("0test://acme.co", 0);
+ check_url_normalizable("+test://acme.co", 0);
+ check_url_normalizable(".test://acme.co", 0);
+ check_url_normalizable("schem%6e://", 0);
+ check_url_normalizable("x-Test+v1.0://acme.co", 1);
+ check_normalized_url("AbCdeF://x.Y", "abcdef://x.y/");
+}
+
+static void t_url_authority(void)
+{
+ check_url_normalizable("scheme://user:pass@", 0);
+ check_url_normalizable("scheme://?", 0);
+ check_url_normalizable("scheme://#", 0);
+ check_url_normalizable("scheme:///", 0);
+ check_url_normalizable("scheme://:", 0);
+ check_url_normalizable("scheme://:555", 0);
+ check_url_normalizable("file://user:pass@", 1);
+ check_url_normalizable("file://?", 1);
+ check_url_normalizable("file://#", 1);
+ check_url_normalizable("file:///", 1);
+ check_url_normalizable("file://:", 1);
+ check_url_normalizable("file://:555", 0);
+ check_url_normalizable("scheme://user:pass@host", 1);
+ check_url_normalizable("scheme://@host", 1);
+ check_url_normalizable("scheme://%00@host", 1);
+ check_url_normalizable("scheme://%%@host", 0);
+ check_url_normalizable("scheme://host_", 1);
+ check_url_normalizable("scheme://user:pass@host/", 1);
+ check_url_normalizable("scheme://@host/", 1);
+ check_url_normalizable("scheme://host/", 1);
+ check_url_normalizable("scheme://host?x", 1);
+ check_url_normalizable("scheme://host#x", 1);
+ check_url_normalizable("scheme://host/@", 1);
+ check_url_normalizable("scheme://host?@x", 1);
+ check_url_normalizable("scheme://host#@x", 1);
+ check_url_normalizable("scheme://[::1]", 1);
+ check_url_normalizable("scheme://[::1]/", 1);
+ check_url_normalizable("scheme://hos%41/", 0);
+ check_url_normalizable("scheme://[invalid....:/", 1);
+ check_url_normalizable("scheme://invalid....:]/", 1);
+ check_url_normalizable("scheme://invalid....:[/", 0);
+ check_url_normalizable("scheme://invalid....:[", 0);
+}
+
+static void t_url_port(void)
+{
+ check_url_normalizable("xyz://q@some.host:", 1);
+ check_url_normalizable("xyz://q@some.host:456/", 1);
+ check_url_normalizable("xyz://q@some.host:0", 0);
+ check_url_normalizable("xyz://q@some.host:0000000", 0);
+ check_url_normalizable("xyz://q@some.host:0000001?", 1);
+ check_url_normalizable("xyz://q@some.host:065535#", 1);
+ check_url_normalizable("xyz://q@some.host:65535", 1);
+ check_url_normalizable("xyz://q@some.host:65536", 0);
+ check_url_normalizable("xyz://q@some.host:99999", 0);
+ check_url_normalizable("xyz://q@some.host:100000", 0);
+ check_url_normalizable("xyz://q@some.host:100001", 0);
+ check_url_normalizable("http://q@some.host:80", 1);
+ check_url_normalizable("https://q@some.host:443", 1);
+ check_url_normalizable("http://q@some.host:80/", 1);
+ check_url_normalizable("https://q@some.host:443?", 1);
+ check_url_normalizable("http://q@:8008", 0);
+ check_url_normalizable("http://:8080", 0);
+ check_url_normalizable("http://:", 0);
+ check_url_normalizable("xyz://q@some.host:456/", 1);
+ check_url_normalizable("xyz://[::1]:456/", 1);
+ check_url_normalizable("xyz://[::1]:/", 1);
+ check_url_normalizable("xyz://[::1]:000/", 0);
+ check_url_normalizable("xyz://[::1]:0%300/", 0);
+ check_url_normalizable("xyz://[::1]:0x80/", 0);
+ check_url_normalizable("xyz://[::1]:4294967297/", 0);
+ check_url_normalizable("xyz://[::1]:030f/", 0);
+}
+
+static void t_url_port_normalization(void)
+{
+ check_normalized_url("http://x:800", "http://x:800/");
+ check_normalized_url("http://x:0800", "http://x:800/");
+ check_normalized_url("http://x:00000800", "http://x:800/");
+ check_normalized_url("http://x:065535", "http://x:65535/");
+ check_normalized_url("http://x:1", "http://x:1/");
+ check_normalized_url("http://x:80", "http://x/");
+ check_normalized_url("http://x:080", "http://x/");
+ check_normalized_url("http://x:000000080", "http://x/");
+ check_normalized_url("https://x:443", "https://x/");
+ check_normalized_url("https://x:0443", "https://x/");
+ check_normalized_url("https://x:000000443", "https://x/");
+}
+
+static void t_url_general_escape(void)
+{
+ check_url_normalizable("http://x.y?%fg", 0);
+ check_normalized_url("X://W/%7e%41^%3a", "x://w/~A%5E%3A");
+ check_normalized_url("X://W/:/?#[]@", "x://w/:/?#[]@");
+ check_normalized_url("X://W/$&()*+,;=", "x://w/$&()*+,;=");
+ check_normalized_url("X://W/'", "x://w/'");
+ check_normalized_url("X://W?!", "x://w/?!");
+}
+
+static void t_url_high_bit(void)
+{
+ check_normalized_url(
+ "x://q/\x01\x02\x03\x04\x05\x06\x07\x08\x0e\x0f\x10\x11\x12",
+ "x://q/%01%02%03%04%05%06%07%08%0E%0F%10%11%12");
+ check_normalized_url(
+ "x://q/\x13\x14\x15\x16\x17\x18\x19\x1b\x1c\x1d\x1e\x1f\x7f",
+ "x://q/%13%14%15%16%17%18%19%1B%1C%1D%1E%1F%7F");
+ check_normalized_url(
+ "x://q/\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f",
+ "x://q/%80%81%82%83%84%85%86%87%88%89%8A%8B%8C%8D%8E%8F");
+ check_normalized_url(
+ "x://q/\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f",
+ "x://q/%90%91%92%93%94%95%96%97%98%99%9A%9B%9C%9D%9E%9F");
+ check_normalized_url(
+ "x://q/\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf",
+ "x://q/%A0%A1%A2%A3%A4%A5%A6%A7%A8%A9%AA%AB%AC%AD%AE%AF");
+ check_normalized_url(
+ "x://q/\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf",
+ "x://q/%B0%B1%B2%B3%B4%B5%B6%B7%B8%B9%BA%BB%BC%BD%BE%BF");
+ check_normalized_url(
+ "x://q/\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf",
+ "x://q/%C0%C1%C2%C3%C4%C5%C6%C7%C8%C9%CA%CB%CC%CD%CE%CF");
+ check_normalized_url(
+ "x://q/\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf",
+ "x://q/%D0%D1%D2%D3%D4%D5%D6%D7%D8%D9%DA%DB%DC%DD%DE%DF");
+ check_normalized_url(
+ "x://q/\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef",
+ "x://q/%E0%E1%E2%E3%E4%E5%E6%E7%E8%E9%EA%EB%EC%ED%EE%EF");
+ check_normalized_url(
+ "x://q/\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
+ "x://q/%F0%F1%F2%F3%F4%F5%F6%F7%F8%F9%FA%FB%FC%FD%FE%FF");
+}
+
+static void t_url_utf8_escape(void)
+{
+ check_normalized_url(
+ "x://q/\xc2\x80\xdf\xbf\xe0\xa0\x80\xef\xbf\xbd\xf0\x90\x80\x80\xf0\xaf\xbf\xbd",
+ "x://q/%C2%80%DF%BF%E0%A0%80%EF%BF%BD%F0%90%80%80%F0%AF%BF%BD");
+}
+
+static void t_url_username_pass(void)
+{
+ check_normalized_url("x://%41%62(^):%70+d@foo", "x://Ab(%5E):p+d@foo/");
+}
+
+static void t_url_length(void)
+{
+ check_normalized_url_length("Http://%4d%65:%4d^%70@The.Host", 25);
+ check_normalized_url_length("http://%41:%42@x.y/%61/", 17);
+ check_normalized_url_length("http://@x.y/^", 15);
+}
+
+static void t_url_dots(void)
+{
+ check_normalized_url("x://y/.", "x://y/");
+ check_normalized_url("x://y/./", "x://y/");
+ check_normalized_url("x://y/a/.", "x://y/a");
+ check_normalized_url("x://y/a/./", "x://y/a/");
+ check_normalized_url("x://y/.?", "x://y/?");
+ check_normalized_url("x://y/./?", "x://y/?");
+ check_normalized_url("x://y/a/.?", "x://y/a?");
+ check_normalized_url("x://y/a/./?", "x://y/a/?");
+ check_normalized_url("x://y/a/./b/.././../c", "x://y/c");
+ check_normalized_url("x://y/a/./b/../.././c/", "x://y/c/");
+ check_normalized_url("x://y/a/./b/.././../c/././.././.", "x://y/");
+ check_url_normalizable("x://y/a/./b/.././../c/././.././..", 0);
+ check_normalized_url("x://y/a/./?/././..", "x://y/a/?/././..");
+ check_normalized_url("x://y/%2e/", "x://y/");
+ check_normalized_url("x://y/%2E/", "x://y/");
+ check_normalized_url("x://y/a/%2e./", "x://y/");
+ check_normalized_url("x://y/b/.%2E/", "x://y/");
+ check_normalized_url("x://y/c/%2e%2E/", "x://y/");
+}
+
+/*
+ * "http://@foo" specifies an empty user name but does not specify a password.
+ * "http://foo" specifies neither a user name nor a password.
+ * So they should not be equivalent.
+ */
+static void t_url_equivalents(void)
+{
+ compare_normalized_urls("httP://x", "Http://X/", 1);
+ compare_normalized_urls("Http://%4d%65:%4d^%70@The.Host", "hTTP://Me:%4D^p@the.HOST:80/", 1);
+ compare_normalized_urls("https://@x.y/^", "httpS://x.y:443/^", 0);
+ compare_normalized_urls("https://@x.y/^", "httpS://@x.y:0443/^", 1);
+ compare_normalized_urls("https://@x.y/^/../abc", "httpS://@x.y:0443/abc", 1);
+ compare_normalized_urls("https://@x.y/^/..", "httpS://@x.y:0443/", 1);
+}
+
+int cmd_main(int argc UNUSED, const char **argv UNUSED)
+{
+ TEST(t_url_scheme(), "url scheme");
+ TEST(t_url_authority(), "url authority");
+ TEST(t_url_port(), "url port checks");
+ TEST(t_url_port_normalization(), "url port normalization");
+ TEST(t_url_general_escape(), "url general escapes");
+ TEST(t_url_high_bit(), "url high-bit escapes");
+ TEST(t_url_utf8_escape(), "url utf8 escapes");
+ TEST(t_url_username_pass(), "url username/password escapes");
+ TEST(t_url_length(), "url normalized lengths");
+ TEST(t_url_dots(), "url . and .. segments");
+ TEST(t_url_equivalents(), "url equivalents");
+ return test_done();
+}
diff --git a/trace2/tr2_tgt_event.c b/trace2/tr2_tgt_event.c
index 59910a1a4f..45b0850a5e 100644
--- a/trace2/tr2_tgt_event.c
+++ b/trace2/tr2_tgt_event.c
@@ -24,7 +24,7 @@ static struct tr2_dst tr2dst_event = {
* a new field to an existing event, do not require an increment to the EVENT
* format version.
*/
-#define TR2_EVENT_VERSION "3"
+#define TR2_EVENT_VERSION "4"
/*
* Region nesting limit for messages written to the event target.
@@ -622,6 +622,24 @@ static void fn_data_json_fl(const char *file, int line,
}
}
+static void fn_printf_va_fl(const char *file, int line,
+ uint64_t us_elapsed_absolute,
+ const char *fmt, va_list ap)
+{
+ const char *event_name = "printf";
+ struct json_writer jw = JSON_WRITER_INIT;
+ double t_abs = (double)us_elapsed_absolute / 1000000.0;
+
+ jw_object_begin(&jw, 0);
+ event_fmt_prepare(event_name, file, line, NULL, &jw);
+ jw_object_double(&jw, "t_abs", 6, t_abs);
+ maybe_add_string_va(&jw, "msg", fmt, ap);
+ jw_end(&jw);
+
+ tr2_dst_write_line(&tr2dst_event, &jw.json);
+ jw_release(&jw);
+}
+
static void fn_timer(const struct tr2_timer_metadata *meta,
const struct tr2_timer *timer,
int is_final_data)
@@ -694,7 +712,7 @@ struct tr2_tgt tr2_tgt_event = {
.pfn_region_leave_printf_va_fl = fn_region_leave_printf_va_fl,
.pfn_data_fl = fn_data_fl,
.pfn_data_json_fl = fn_data_json_fl,
- .pfn_printf_va_fl = NULL,
+ .pfn_printf_va_fl = fn_printf_va_fl,
.pfn_timer = fn_timer,
.pfn_counter = fn_counter,
};
diff --git a/transport.c b/transport.c
index bab28965f9..3c4714581f 100644
--- a/transport.c
+++ b/transport.c
@@ -189,6 +189,8 @@ static int fetch_refs_from_bundle(struct transport *transport,
&extra_index_pack_args,
fetch_pack_fsck_objects() ? VERIFY_BUNDLE_FSCK : 0);
transport->hash_algo = data->header.hash_algo;
+
+ strvec_clear(&extra_index_pack_args);
return ret;
}
@@ -945,7 +947,13 @@ static int disconnect_git(struct transport *transport)
finish_connect(data->conn);
}
+ if (data->options.negotiation_tips) {
+ oid_array_clear(data->options.negotiation_tips);
+ free(data->options.negotiation_tips);
+ }
list_objects_filter_release(&data->options.filter_options);
+ oid_array_clear(&data->extra_have);
+ oid_array_clear(&data->shallow);
free(data);
return 0;
}