summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitattributes1
-rw-r--r--.mailmap7
-rw-r--r--Documentation/RelNotes/2.53.0.adoc80
-rw-r--r--Documentation/blame-options.adoc120
-rw-r--r--Documentation/fsck-msgids.adoc6
-rw-r--r--Documentation/git-blame.adoc72
-rw-r--r--Documentation/git-config.adoc2
-rw-r--r--Documentation/git-patch-id.adoc20
-rw-r--r--Documentation/git-remote.adoc106
-rw-r--r--Documentation/git-reset.adoc111
-rw-r--r--Documentation/git-stage.adoc4
-rw-r--r--Documentation/git-status.adoc352
-rw-r--r--Documentation/git-update-ref.adoc2
-rw-r--r--Documentation/gitfaq.adoc39
-rwxr-xr-xGIT-VERSION-GEN2
-rw-r--r--Makefile42
-rw-r--r--add-interactive.c2
-rw-r--r--archive.c2
-rw-r--r--bloom.c2
-rw-r--r--builtin.h26
-rw-r--r--builtin/am.c10
-rw-r--r--builtin/cat-file.c11
-rw-r--r--builtin/checkout.c14
-rw-r--r--builtin/clone.c4
-rw-r--r--builtin/commit.c5
-rw-r--r--builtin/describe.c6
-rw-r--r--builtin/diff-tree.c2
-rw-r--r--builtin/fast-import.c37
-rw-r--r--builtin/fsck.c208
-rw-r--r--builtin/gc.c8
-rw-r--r--builtin/grep.c10
-rw-r--r--builtin/index-pack.c2
-rw-r--r--builtin/log.c31
-rw-r--r--builtin/ls-tree.c2
-rw-r--r--builtin/merge-tree.c9
-rw-r--r--builtin/merge.c8
-rw-r--r--builtin/name-rev.c17
-rw-r--r--builtin/pack-objects.c124
-rw-r--r--builtin/patch-id.c4
-rw-r--r--builtin/read-tree.c4
-rw-r--r--builtin/repack.c3
-rw-r--r--builtin/replay.c87
-rw-r--r--builtin/reset.c4
-rw-r--r--builtin/show-branch.c34
-rw-r--r--builtin/stash.c8
-rw-r--r--builtin/tag.c2
-rw-r--r--builtin/verify-tag.c2
-rw-r--r--bundle-uri.c24
-rw-r--r--cache-tree.c2
-rw-r--r--commit-graph.c88
-rw-r--r--commit-reach.c25
-rw-r--r--commit.c28
-rw-r--r--commit.h12
-rw-r--r--compat/mingw.c18
-rw-r--r--config.c6
-rw-r--r--config.mak.uname31
-rw-r--r--contrib/coccinelle/the_repository.cocci121
-rwxr-xr-xcontrib/subtree/git-subtree.sh141
-rwxr-xr-xcontrib/subtree/t/t7900-subtree.sh83
-rw-r--r--delta-islands.c2
-rw-r--r--diff-lib.c2
-rw-r--r--diff.c5
-rw-r--r--environment.c33
-rw-r--r--environment.h3
-rw-r--r--fsck.c9
-rw-r--r--fsck.h4
-rw-r--r--git-compat-util.h24
-rw-r--r--http-backend.c8
-rw-r--r--http-push.c2
-rw-r--r--http.c2
-rw-r--r--list-objects.c4
-rw-r--r--lockfile.c4
-rw-r--r--merge-ort.c48
-rw-r--r--merge.c6
-rw-r--r--midx-write.c49
-rw-r--r--midx.c19
-rw-r--r--object-file.c115
-rw-r--r--object-name.c2
-rw-r--r--odb.c46
-rw-r--r--odb.h19
-rw-r--r--odb/streaming.c9
-rw-r--r--pack-bitmap-write.c20
-rw-r--r--pack-bitmap.c3
-rw-r--r--packfile.c288
-rw-r--r--packfile.h109
-rw-r--r--path-walk.c2
-rw-r--r--reachable.c2
-rw-r--r--read-cache.c2
-rw-r--r--ref-filter.c2
-rw-r--r--refs.c43
-rw-r--r--refs.h18
-rw-r--r--refs/files-backend.c228
-rw-r--r--refs/reftable-backend.c167
-rw-r--r--remote.c74
-rw-r--r--repack-geometry.c87
-rw-r--r--repack-promisor.c97
-rw-r--r--repack.h10
-rw-r--r--repo-settings.c3
-rw-r--r--repo-settings.h3
-rw-r--r--reset.c2
-rw-r--r--revision.c35
-rw-r--r--sequencer.c4
-rw-r--r--setup.c2
-rw-r--r--shallow.c44
-rw-r--r--shallow.h4
-rw-r--r--strbuf.c8
-rw-r--r--t/helper/test-cache-tree.c2
-rw-r--r--t/helper/test-match-trees.c4
-rw-r--r--t/helper/test-reach.c34
-rwxr-xr-xt/perf/p1006-cat-file.sh14
-rwxr-xr-xt/perf/p6010-merge-base.sh8
-rw-r--r--t/perf/perf-lib.sh3
-rwxr-xr-xt/perf/run10
-rwxr-xr-xt/t0450-txt-doc-vs-help.sh2
-rwxr-xr-xt/t0602-reffiles-fsck.sh30
-rwxr-xr-xt/t0614-reftable-fsck.sh44
-rwxr-xr-xt/t1300-config.sh8
-rwxr-xr-xt/t1410-reflog.sh8
-rwxr-xr-xt/t1420-lost-found.sh9
-rwxr-xr-xt/t1450-fsck.sh10
-rwxr-xr-xt/t2021-checkout-overwrite.sh4
-rwxr-xr-xt/t3650-replay-basics.sh54
-rwxr-xr-xt/t4067-diff-partial-clone.sh35
-rwxr-xr-xt/t5003-archive-zip.sh34
-rwxr-xr-xt/t5319-multi-pack-index.sh2
-rwxr-xr-xt/t5331-pack-objects-stdin.sh39
-rwxr-xr-xt/t5403-post-checkout-hook.sh2
-rwxr-xr-xt/t5750-bundle-uri-parse.sh26
-rwxr-xr-xt/t6422-merge-rename-corner-cases.sh86
-rwxr-xr-xt/t7101-reset-empty-subdirs.sh18
-rwxr-xr-xt/t7703-repack-geometric.sh61
-rwxr-xr-xt/t7800-difftool.sh6
-rwxr-xr-xt/t7900-maintenance.sh25
-rw-r--r--t/unit-tests/clar/.github/workflows/ci.yml2
-rw-r--r--t/unit-tests/clar/clar.c146
-rw-r--r--t/unit-tests/clar/clar.h82
-rw-r--r--t/unit-tests/clar/clar/print.h2
-rw-r--r--t/unit-tests/clar/test/expected/quiet40
-rw-r--r--t/unit-tests/clar/test/expected/summary_with_filename42
-rw-r--r--t/unit-tests/clar/test/expected/summary_without_filename42
-rw-r--r--t/unit-tests/clar/test/expected/tap88
-rw-r--r--t/unit-tests/clar/test/expected/without_arguments42
-rw-r--r--t/unit-tests/clar/test/selftest.c10
-rw-r--r--t/unit-tests/clar/test/suites/combined.c65
-rw-r--r--t/unit-tests/u-reftable-record.c22
-rw-r--r--t/unit-tests/unit-test.h6
-rw-r--r--tag.c27
-rw-r--r--tag.h4
-rw-r--r--tree-diff.c2
-rw-r--r--tree-walk.c4
-rw-r--r--tree.c16
-rw-r--r--tree.h13
-rw-r--r--utf8.c13
-rw-r--r--walker.c4
154 files changed, 3352 insertions, 1803 deletions
diff --git a/.gitattributes b/.gitattributes
index 700743c3f5..38b1c52fe0 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -17,3 +17,4 @@ CODE_OF_CONDUCT.md -whitespace
/Documentation/gitk.adoc conflict-marker-size=32
/Documentation/user-manual.adoc conflict-marker-size=32
/t/t????-*.sh conflict-marker-size=32
+/t/unit-tests/clar/test/expected/* whitespace=-blank-at-eof
diff --git a/.mailmap b/.mailmap
index 7b3198171f..799734821b 100644
--- a/.mailmap
+++ b/.mailmap
@@ -107,6 +107,9 @@ Jason Riedy <ejr@eecs.berkeley.edu> <ejr@cs.berkeley.edu>
Jay Soffian <jaysoffian@gmail.com> <jaysoffian+git@gmail.com>
Jean-Noël Avila <jn.avila@free.fr> Jean-Noel Avila
Jean-Noël Avila <jn.avila@free.fr> Jean-Noël AVILA
+Jean-Noël Avila <jn.avila@free.fr> Jean-Noel Avila <jean-noel.avila@scantech.fr>
+Jean-Noël Avila <jn.avila@free.fr> Jean-Noël AVILA <avila.jn@gmail.com>
+Jean-Noël Avila <jn.avila@free.fr> Jean-Noël Avila via GitGitGadget <gitgitgadget@gmail.com>
Jeff King <peff@peff.net> <peff@github.com>
Jeff Muizelaar <jmuizelaar@mozilla.com> <jeff@infidigm.net>
Jens Axboe <axboe@kernel.dk> <axboe@suse.de>
@@ -140,8 +143,8 @@ Junio C Hamano <gitster@pobox.com> <junkio@twinsun.com>
Kaartic Sivaraam <kaartic.sivaraam@gmail.com> <kaarticsivaraam91196@gmail.com>
Karl Wiberg <kha@treskal.com> Karl Hasselström
Karl Wiberg <kha@treskal.com> <kha@yoghurt.hemma.treskal.com>
-Karsten Blees <blees@dcon.de> <karsten.blees@dcon.de>
-Karsten Blees <blees@dcon.de> <karsten.blees@gmail.com>
+Karsten Blees <karsten.blees@gmail.com> <karsten.blees@dcon.de>
+Karsten Blees <karsten.blees@gmail.com> <blees@dcon.de>
Kay Sievers <kay.sievers@vrfy.org> <kay.sievers@suse.de>
Kay Sievers <kay.sievers@vrfy.org> <kay@mam.(none)>
Kazuki Saitoh <ksaitoh560@gmail.com> kazuki saitoh <ksaitoh560@gmail.com>
diff --git a/Documentation/RelNotes/2.53.0.adoc b/Documentation/RelNotes/2.53.0.adoc
index 91cfb7adfa..be8df25477 100644
--- a/Documentation/RelNotes/2.53.0.adoc
+++ b/Documentation/RelNotes/2.53.0.adoc
@@ -34,6 +34,17 @@ UI, Workflows & Features
* More object database related information are shown in "git repo
structure" output.
+ * Improve the error message when a bad argument is given to the
+ `--onto` option of "git replay". Test coverage of "git replay" has
+ been improved.
+
+ * The split command in "git subtree" (in contrib/) has been taught to
+ deal better with rebased history.
+
+ * The iconv library on macOS fails to correctly handle stateful
+ ISO/IEC 2022 encoded strings. Work it around instead of replacing
+ it wholesale from homebrew.
+
Performance, Internal Implementation, Development Support etc.
--------------------------------------------------------------
@@ -89,6 +100,20 @@ Performance, Internal Implementation, Development Support etc.
* Prepare test suite for Git for Windows that supports symbolic
links.
+ * Import newer version of "clar", unit testing framework.
+ (merge 84071a6dea ps/clar-integers later to maint).
+
+ * The packfile_store data structure is moved from object store to odb
+ source.
+
+ * The object-info API has been cleaned up.
+
+ * Further preparation to upstream symbolic link support on Windows.
+
+ * Remove implicit reliance on the_repository global in the APIs
+ around tree objects and make it explicit which repository to work
+ in.
+
Fixes since v2.52
-----------------
@@ -221,6 +246,50 @@ Fixes since v2.52
* Update HTTP tests to adjust for changes in curl 8.18.0
(merge 17f4b01da7 jk/test-curl-updates later to maint).
+ * Workaround the "iconv" shipped as part of macOS, which is broken
+ handling stateful ISO/IEC 2022 encoded strings.
+ (merge cee341e9dd rs/macos-iconv-workaround later to maint).
+
+ * Running "git diff" with "--name-only" and other options that allows
+ us not to look at the blob contents, while objects that are lazily
+ fetched from a promisor remote, caused use-after-free, which has
+ been corrected.
+
+ * The ort merge machinery hit an assertion failure in a history with
+ criss-cross merges renamed a directory and a non-directory, which
+ has been corrected.
+ (merge 979ee83e8a en/ort-recursive-d-f-conflict-fix later to maint).
+
+ * Diagnose invalid bundle-URI that lack the URI entry, instead of
+ crashing.
+ (merge 7796c14a1a sb/bundle-uri-without-uri later to maint).
+
+ * Mailmap update for Karsten
+ (merge e97678c4ef js/mailmap-karsten-blees later to maint).
+
+ * Perf-test fixes.
+ (merge 79d301c767 jk/t-perf-fixes later to maint).
+
+ * Fix for a performance regression in "git cat-file".
+ (merge 9e8b448dd8 jk/cat-file-avoid-bitmap-when-unneeded later to maint).
+
+ * Update a FAQ entry on synching two separate repositories using the
+ "git stash export/import" recently introduced.
+ (merge 02fc44a989 bc/doc-stash-import-export later to maint).
+
+ * "git fsck" used inconsistent set of refs to show a confused
+ warning, which has been corrected.
+
+ * Some error messages from the http transport layer lacked the
+ terminating newline, which has been corrected.
+ (merge a8227ae8d5 kt/http-backend-errors later to maint).
+
+ * "git repack --geometric" did not work with promisor packs, which
+ has been corrected.
+
+ * The logic that avoids reusing MIDX files with a wrong checksum was
+ broken, which has been corrected.
+
* Other code cleanup, docfix, build fix, etc.
(merge 46207a54cc qj/doc-http-bad-want-response later to maint).
(merge df90eccd93 kh/doc-commit-extra-references later to maint).
@@ -242,3 +311,14 @@ Fixes since v2.52
(merge c469ca26c5 dk/ci-rust-fix later to maint).
(merge 12f0be0857 gf/clear-path-cache-cleanup later to maint).
(merge 949df6ed6b js/test-func-comment-fix later to maint).
+ (merge 93f894c001 bc/checkout-error-message-fix later to maint).
+ (merge abf05d856f rs/show-branch-prio-queue later to maint).
+ (merge 06188ea5f3 rs/parse-config-expiry-simplify later to maint).
+ (merge 861dbb1586 dd/t5403-modernise later to maint).
+ (merge acffc5e9e5 ja/doc-synopsis-style-more later to maint).
+ (merge 6c5c7e7071 ac/t1420-use-more-direct-check later to maint).
+ (merge 2ac93bfcbc ds/builtin-doc-update later to maint).
+ (merge 3f051fc9c9 kh/doc-patch-id later to maint).
+ (merge 555c8464e5 je/doc-reset later to maint).
+ (merge 220f888d7e ps/t1410-cleanup later to maint).
+ (merge 5814b04c02 ps/config-doc-get-urlmatch-fix later to maint).
diff --git a/Documentation/blame-options.adoc b/Documentation/blame-options.adoc
index 1fb948fc76..1ae1222b6b 100644
--- a/Documentation/blame-options.adoc
+++ b/Documentation/blame-options.adoc
@@ -1,105 +1,105 @@
--b::
+`-b`::
Show blank SHA-1 for boundary commits. This can also
be controlled via the `blame.blankBoundary` config option.
---root::
+`--root`::
Do not treat root commits as boundaries. This can also be
controlled via the `blame.showRoot` config option.
---show-stats::
+`--show-stats`::
Include additional statistics at the end of blame output.
--L <start>,<end>::
--L :<funcname>::
- Annotate only the line range given by '<start>,<end>',
- or by the function name regex '<funcname>'.
+`-L <start>,<end>`::
+`-L :<funcname>`::
+ Annotate only the line range given by `<start>,<end>`,
+ or by the function name regex _<funcname>_.
May be specified multiple times. Overlapping ranges are allowed.
+
-'<start>' and '<end>' are optional. `-L <start>` or `-L <start>,` spans from
-'<start>' to end of file. `-L ,<end>` spans from start of file to '<end>'.
+_<start>_ and _<end>_ are optional. `-L <start>` or `-L <start>,` spans from
+_<start>_ to end of file. `-L ,<end>` spans from start of file to _<end>_.
+
include::line-range-format.adoc[]
--l::
+`-l`::
Show long rev (Default: off).
--t::
+`-t`::
Show raw timestamp (Default: off).
--S <revs-file>::
- Use revisions from revs-file instead of calling linkgit:git-rev-list[1].
+`-S <revs-file>`::
+ Use revisions from _<revs-file>_ instead of calling
+ linkgit:git-rev-list[1].
---reverse <rev>..<rev>::
+`--reverse <start>..<end>`::
Walk history forward instead of backward. Instead of showing
the revision in which a line appeared, this shows the last
revision in which a line has existed. This requires a range of
- revision like START..END where the path to blame exists in
- START. `git blame --reverse START` is taken as `git blame
- --reverse START..HEAD` for convenience.
+ revision like `<start>..<end>` where the path to blame exists in
+ _<start>_. `git blame --reverse <start>` is taken as `git blame
+ --reverse <start>..HEAD` for convenience.
---first-parent::
+`--first-parent`::
Follow only the first parent commit upon seeing a merge
commit. This option can be used to determine when a line
was introduced to a particular integration branch, rather
than when it was introduced to the history overall.
--p::
---porcelain::
+`-p`::
+`--porcelain`::
Show in a format designed for machine consumption.
---line-porcelain::
+`--line-porcelain`::
Show the porcelain format, but output commit information for
each line, not just the first time a commit is referenced.
- Implies --porcelain.
+ Implies `--porcelain`.
---incremental::
+`--incremental`::
Show the result incrementally in a format designed for
machine consumption.
---encoding=<encoding>::
- Specifies the encoding used to output author names
+`--encoding=<encoding>`::
+ Specify the encoding used to output author names
and commit summaries. Setting it to `none` makes blame
output unconverted data. For more information see the
discussion about encoding in the linkgit:git-log[1]
manual page.
---contents <file>::
- Annotate using the contents from the named file, starting from <rev>
- if it is specified, and HEAD otherwise. You may specify '-' to make
+`--contents <file>`::
+ Annotate using the contents from _<file>_, starting from _<rev>_
+ if it is specified, and `HEAD` otherwise. You may specify `-` to make
the command read from the standard input for the file contents.
---date <format>::
- Specifies the format used to output dates. If --date is not
- provided, the value of the blame.date config variable is
- used. If the blame.date config variable is also not set, the
+`--date <format>`::
+ Specify the format used to output dates. If `--date` is not
+ provided, the value of the `blame.date` config variable is
+ used. If the `blame.date` config variable is also not set, the
iso format is used. For supported values, see the discussion
- of the --date option at linkgit:git-log[1].
+ of the `--date` option at linkgit:git-log[1].
---progress::
---no-progress::
- Progress status is reported on the standard error stream
- by default when it is attached to a terminal. This flag
- enables progress reporting even if not attached to a
- terminal. Can't use `--progress` together with `--porcelain`
- or `--incremental`.
+`--progress`::
+`--no-progress`::
+ Enable progress reporting on the standard error stream even if
+ not attached to a terminal. By default, progress status is
+ reported only when it is attached. You can't use `--progress`
+ together with `--porcelain` or `--incremental`.
--M[<num>]::
+`-M[<num>]`::
Detect moved or copied lines within a file. When a commit
moves or copies a block of lines (e.g. the original file
- has A and then B, and the commit changes it to B and then
- A), the traditional 'blame' algorithm notices only half of
+ has _A_ and then _B_, and the commit changes it to _B_ and then
+ _A_), the traditional `blame` algorithm notices only half of
the movement and typically blames the lines that were moved
- up (i.e. B) to the parent and assigns blame to the lines that
- were moved down (i.e. A) to the child commit. With this
+ up (i.e. _B_) to the parent and assigns blame to the lines that
+ were moved down (i.e. _A_) to the child commit. With this
option, both groups of lines are blamed on the parent by
running extra passes of inspection.
+
-<num> is optional but it is the lower bound on the number of
+_<num>_ is optional, but it is the lower bound on the number of
alphanumeric characters that Git must detect as moving/copying
within a file for it to associate those lines with the parent
commit. The default value is 20.
--C[<num>]::
+`-C[<num>]`::
In addition to `-M`, detect lines moved or copied from other
files that were modified in the same commit. This is
useful when you reorganize your program and move code
@@ -109,14 +109,14 @@ commit. The default value is 20.
option is given three times, the command additionally
looks for copies from other files in any commit.
+
-<num> is optional but it is the lower bound on the number of
+_<num>_ is optional, but it is the lower bound on the number of
alphanumeric characters that Git must detect as moving/copying
between files for it to associate those lines with the parent
commit. And the default value is 40. If there are more than one
-`-C` options given, the <num> argument of the last `-C` will
+`-C` options given, the _<num>_ argument of the last `-C` will
take effect.
---ignore-rev <rev>::
+`--ignore-rev <rev>`::
Ignore changes made by the revision when assigning blame, as if the
change never happened. Lines that were changed or added by an ignored
commit will be blamed on the previous commit that changed that line or
@@ -126,26 +126,26 @@ take effect.
another commit will be marked with a `?` in the blame output. If the
`blame.markUnblamableLines` config option is set, then those lines touched
by an ignored commit that we could not attribute to another revision are
- marked with a '*'. In the porcelain modes, we print 'ignored' and
- 'unblamable' on a newline respectively.
+ marked with a `*`. In the porcelain modes, we print `ignored` and
+ `unblamable` on a newline respectively.
---ignore-revs-file <file>::
- Ignore revisions listed in `file`, which must be in the same format as an
+`--ignore-revs-file <file>`::
+ Ignore revisions listed in _<file>_, which must be in the same format as an
`fsck.skipList`. This option may be repeated, and these files will be
processed after any files specified with the `blame.ignoreRevsFile` config
option. An empty file name, `""`, will clear the list of revs from
previously processed files.
---color-lines::
+`--color-lines`::
Color line annotations in the default format differently if they come from
the same commit as the preceding line. This makes it easier to distinguish
code blocks introduced by different commits. The color defaults to cyan and
can be adjusted using the `color.blame.repeatedLines` config option.
---color-by-age::
- Color line annotations depending on the age of the line in the default format.
- The `color.blame.highlightRecent` config option controls what color is used for
- each range of age.
+`--color-by-age`::
+ Color line annotations depending on the age of the line in
+ the default format. The `color.blame.highlightRecent` config
+ option controls what color is used for each range of age.
--h::
+`-h`::
Show help message.
diff --git a/Documentation/fsck-msgids.adoc b/Documentation/fsck-msgids.adoc
index acac9683af..6a4db3a991 100644
--- a/Documentation/fsck-msgids.adoc
+++ b/Documentation/fsck-msgids.adoc
@@ -13,6 +13,9 @@
`badGpgsig`::
(ERROR) A tag contains a bad (truncated) signature (e.g., `gpgsig`) header.
+`badHeadTarget`::
+ (ERROR) The `HEAD` ref is a symref that does not refer to a branch.
+
`badHeaderContinuation`::
(ERROR) A continuation header (such as for `gpgsig`) is unexpectedly truncated.
@@ -41,6 +44,9 @@
`badRefName`::
(ERROR) A ref has an invalid format.
+`badRefOid`::
+ (ERROR) A ref points to an invalid object ID.
+
`badReferentName`::
(ERROR) The referent name of a symref is invalid.
diff --git a/Documentation/git-blame.adoc b/Documentation/git-blame.adoc
index adcbb6f5dc..8808009e87 100644
--- a/Documentation/git-blame.adoc
+++ b/Documentation/git-blame.adoc
@@ -7,12 +7,12 @@ git-blame - Show what revision and author last modified each line of a file
SYNOPSIS
--------
-[verse]
-'git blame' [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-e] [-p] [-w] [--incremental]
- [-L <range>] [-S <revs-file>] [-M] [-C] [-C] [-C] [--since=<date>]
- [--ignore-rev <rev>] [--ignore-revs-file <file>]
- [--color-lines] [--color-by-age] [--progress] [--abbrev=<n>]
- [ --contents <file> ] [<rev> | --reverse <rev>..<rev>] [--] <file>
+[synopsis]
+git blame [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-e] [-p] [-w] [--incremental]
+ [-L <range>] [-S <revs-file>] [-M] [-C] [-C] [-C] [--since=<date>]
+ [--ignore-rev <rev>] [--ignore-revs-file <file>]
+ [--color-lines] [--color-by-age] [--progress] [--abbrev=<n>]
+ [ --contents <file> ] [<rev> | --reverse <rev>..<rev>] [--] <file>
DESCRIPTION
-----------
@@ -30,7 +30,7 @@ lines that were copied and pasted from another file, etc., see the
`-C` and `-M` options.
The report does not tell you anything about lines which have been deleted or
-replaced; you need to use a tool such as 'git diff' or the "pickaxe"
+replaced; you need to use a tool such as `git diff` or the "pickaxe"
interface briefly mentioned in the following paragraph.
Apart from supporting file annotation, Git also supports searching the
@@ -50,47 +50,47 @@ OPTIONS
-------
include::blame-options.adoc[]
--c::
+`-c`::
Use the same output mode as linkgit:git-annotate[1] (Default: off).
---score-debug::
+`--score-debug`::
Include debugging information related to the movement of
lines between files (see `-C`) and lines moved within a
file (see `-M`). The first number listed is the score.
This is the number of alphanumeric characters detected
as having been moved between or within files. This must be above
- a certain threshold for 'git blame' to consider those lines
+ a certain threshold for `git blame` to consider those lines
of code to have been moved.
--f::
---show-name::
+`-f`::
+`--show-name`::
Show the filename in the original commit. By default
the filename is shown if there is any line that came from a
file with a different name, due to rename detection.
--n::
---show-number::
+`-n`::
+`--show-number`::
Show the line number in the original commit (Default: off).
--s::
+`-s`::
Suppress the author name and timestamp from the output.
--e::
---show-email::
+`-e`::
+`--show-email`::
Show the author email instead of the author name (Default: off).
This can also be controlled via the `blame.showEmail` config
option.
--w::
+`-w`::
Ignore whitespace when comparing the parent's version and
the child's to find where the lines came from.
include::diff-algorithm-option.adoc[]
---abbrev=<n>::
- Instead of using the default 7+1 hexadecimal digits as the
- abbreviated object name, use <m>+1 digits, where <m> is at
- least <n> but ensures the commit object names are unique.
+`--abbrev=<n>`::
+ Instead of using the default _7+1_ hexadecimal digits as the
+ abbreviated object name, use _<m>+1_ digits, where _<m>_ is at
+ least _<n>_ but ensures the commit object names are unique.
Note that 1 column
is used for a caret to mark the boundary commit.
@@ -124,21 +124,21 @@ header at the minimum has the first line which has:
This header line is followed by the following information
at least once for each commit:
-- the author name ("author"), email ("author-mail"), time
- ("author-time"), and time zone ("author-tz"); similarly
+- the author name (`author`), email (`author-mail`), time
+ (`author-time`), and time zone (`author-tz`); similarly
for committer.
- the filename in the commit that the line is attributed to.
-- the first line of the commit log message ("summary").
+- the first line of the commit log message (`summary`).
The contents of the actual line are output after the above
-header, prefixed by a TAB. This is to allow adding more
+header, prefixed by a _TAB_. This is to allow adding more
header elements later.
The porcelain format generally suppresses commit information that has
already been seen. For example, two lines that are blamed to the same
commit will both be shown, but the details for that commit will be shown
only once. Information which is specific to individual lines will not be
-grouped together, like revs to be marked 'ignored' or 'unblamable'. This
+grouped together, like revs to be marked `ignored` or `unblamable`. This
is more efficient, but may require more state be kept by the reader. The
`--line-porcelain` option can be used to output full commit information
for each line, allowing simpler (but less efficient) usage like:
@@ -152,7 +152,7 @@ for each line, allowing simpler (but less efficient) usage like:
SPECIFYING RANGES
-----------------
-Unlike 'git blame' and 'git annotate' in older versions of git, the extent
+Unlike `git blame` and `git annotate` in older versions of git, the extent
of the annotation can be limited to both line ranges and revision
ranges. The `-L` option, which limits annotation to a range of lines, may be
specified multiple times.
@@ -173,7 +173,7 @@ which limits the annotation to the body of the `hello` subroutine.
When you are not interested in changes older than version
v2.6.18, or changes older than 3 weeks, you can use revision
-range specifiers similar to 'git rev-list':
+range specifiers similar to `git rev-list`:
git blame v2.6.18.. -- foo
git blame --since=3.weeks -- foo
@@ -212,8 +212,9 @@ does not contain the actual lines from the file that is being
annotated.
. Each blame entry always starts with a line of:
-
- <40-byte-hex-sha1> <sourceline> <resultline> <num-lines>
++
+[synopsis]
+<40-byte-hex-sha1> <sourceline> <resultline> <num-lines>
+
Line numbers count from 1.
@@ -224,16 +225,17 @@ Line numbers count from 1.
. Unlike the Porcelain format, the filename information is always
given and terminates the entry:
-
- "filename" <whitespace-quoted-filename-goes-here>
++
+[synopsis]
+filename <whitespace-quoted-filename-goes-here>
+
and thus it is really quite easy to parse for some line- and word-oriented
parser (which should be quite natural for most scripting languages).
+
[NOTE]
For people who do parsing: to make it more robust, just ignore any
-lines between the first and last one ("<sha1>" and "filename" lines)
-where you do not recognize the tag words (or care about that particular
+lines between the first and last one (_<40-byte-hex-sha1>_ and `filename`
+lines) where you do not recognize the tag words (or care about that particular
one) at the beginning of the "extended information" lines. That way, if
there is ever added information (like the commit encoding or extended
commit commentary), a blame viewer will not care.
diff --git a/Documentation/git-config.adoc b/Documentation/git-config.adoc
index cc054fa7e1..ac3b536a15 100644
--- a/Documentation/git-config.adoc
+++ b/Documentation/git-config.adoc
@@ -332,7 +332,7 @@ recommended to migrate to the new syntax.
Replaced by `git config get --all --show-names --regexp <name-regexp>`.
--get-urlmatch <name> <URL>::
- Replaced by `git config get --all --show-names --url=<URL> <name>`.
+ Replaced by `git config get --url=<URL> <name>`.
--get-color <name> [<default>]::
Replaced by `git config get --type=color [--default=<default>] <name>`.
diff --git a/Documentation/git-patch-id.adoc b/Documentation/git-patch-id.adoc
index 92a1af36a2..013e1a6190 100644
--- a/Documentation/git-patch-id.adoc
+++ b/Documentation/git-patch-id.adoc
@@ -21,7 +21,7 @@ the same time also reasonably unique, i.e., two patches that have the same
The main usecase for this command is to look for likely duplicate commits.
-When dealing with `git diff-tree` output, it takes advantage of
+When dealing with `git diff-tree --patch` output, it takes advantage of
the fact that the patch is prefixed with the object name of the
commit, and outputs two 40-byte hexadecimal strings. The first
string is the patch ID, and the second string is the commit ID.
@@ -31,8 +31,8 @@ OPTIONS
-------
`--verbatim`::
- Calculate the patch-id of the input as it is given, do not strip
- any whitespace.
+ Calculate the patch ID of the input as it is given, do not strip
+ any whitespace. Implies `--stable` and forbids `--unstable`.
+
This is the default if `patchid.verbatim` is `true`.
@@ -45,24 +45,24 @@ This is the default if `patchid.verbatim` is `true`.
with two different settings for `-O<orderfile>` result in the same
patch ID signature, thereby allowing the computed result to be used
as a key to index some meta-information about the change between
- the two trees;
+ the two trees.
-- Result is different from the value produced by git 1.9 and older
+- The result is different from the value produced by Git 1.9 and older
or produced when an "unstable" hash (see `--unstable` below) is
configured - even when used on a diff output taken without any use
of `-O<orderfile>`, thereby making existing databases storing such
- "unstable" or historical patch-ids unusable.
+ "unstable" or historical patch IDs unusable.
-- All whitespace within the patch is ignored and does not affect the id.
+- All whitespace within the patch is ignored and does not affect the ID.
--
+
This is the default if `patchid.stable` is set to `true`.
`--unstable`::
Use an "unstable" hash as the patch ID. With this option,
- the result produced is compatible with the patch-id value produced
- by git 1.9 and older and whitespace is ignored. Users with pre-existing
- databases storing patch-ids produced by git 1.9 and older (who do not deal
+ the result produced is compatible with the patch ID value produced
+ by Git 1.9 and older and whitespace is ignored. Users with pre-existing
+ databases storing patch IDs produced by Git 1.9 and older (who do not deal
with reordered patches) may want to use this option.
+
This is the default.
diff --git a/Documentation/git-remote.adoc b/Documentation/git-remote.adoc
index 932a5c3ea4..eaae30aa88 100644
--- a/Documentation/git-remote.adoc
+++ b/Documentation/git-remote.adoc
@@ -8,20 +8,20 @@ git-remote - Manage set of tracked repositories
SYNOPSIS
--------
-[verse]
-'git remote' [-v | --verbose]
-'git remote add' [-t <branch>] [-m <master>] [-f] [--[no-]tags] [--mirror=(fetch|push)] <name> <URL>
-'git remote rename' [--[no-]progress] <old> <new>
-'git remote remove' <name>
-'git remote set-head' <name> (-a | --auto | -d | --delete | <branch>)
-'git remote set-branches' [--add] <name> <branch>...
-'git remote get-url' [--push] [--all] <name>
-'git remote set-url' [--push] <name> <newurl> [<oldurl>]
-'git remote set-url --add' [--push] <name> <newurl>
-'git remote set-url --delete' [--push] <name> <URL>
-'git remote' [-v | --verbose] 'show' [-n] <name>...
-'git remote prune' [-n | --dry-run] <name>...
-'git remote' [-v | --verbose] 'update' [-p | --prune] [(<group> | <remote>)...]
+[synopsis]
+git remote [-v | --verbose]
+git remote add [-t <branch>] [-m <master>] [-f] [--[no-]tags] [--mirror=(fetch|push)] <name> <URL>
+git remote rename [--[no-]progress] <old> <new>
+git remote remove <name>
+git remote set-head <name> (-a | --auto | -d | --delete | <branch>)
+git remote set-branches [--add] <name> <branch>...
+git remote get-url [--push] [--all] <name>
+git remote set-url [--push] <name> <newurl> [<oldurl>]
+git remote set-url --add [--push] <name> <newurl>
+git remote set-url --delete [--push] <name> <URL>
+git remote [-v | --verbose] show [-n] <name>...
+git remote prune [-n | --dry-run] <name>...
+git remote [-v | --verbose] update [-p | --prune] [(<group> | <remote>)...]
DESCRIPTION
-----------
@@ -32,8 +32,8 @@ Manage the set of repositories ("remotes") whose branches you track.
OPTIONS
-------
--v::
---verbose::
+`-v`::
+`--verbose`::
Be a little more verbose and show remote url after name.
For promisor remotes, also show which filters (`blob:none` etc.)
are configured.
@@ -43,14 +43,14 @@ OPTIONS
COMMANDS
--------
-With no arguments, shows a list of existing remotes. Several
+With no arguments, show a list of existing remotes. Several
subcommands are available to perform operations on the remotes.
-'add'::
+`add`::
-Add a remote named <name> for the repository at
-<URL>. The command `git fetch <name>` can then be used to create and
-update remote-tracking branches <name>/<branch>.
+Add a remote named _<name>_ for the repository at
+_<URL>_. The command `git fetch <name>` can then be used to create and
+update remote-tracking branches `<name>/<branch>`.
+
With `-f` option, `git fetch <name>` is run immediately after
the remote information is set up.
@@ -66,40 +66,40 @@ By default, only tags on fetched branches are imported
+
With `-t <branch>` option, instead of the default glob
refspec for the remote to track all branches under
-the `refs/remotes/<name>/` namespace, a refspec to track only `<branch>`
+the `refs/remotes/<name>/` namespace, a refspec to track only _<branch>_
is created. You can give more than one `-t <branch>` to track
multiple branches without grabbing all branches.
+
With `-m <master>` option, a symbolic-ref `refs/remotes/<name>/HEAD` is set
-up to point at remote's `<master>` branch. See also the set-head command.
+up to point at remote's _<master>_ branch. See also the set-head command.
+
When a fetch mirror is created with `--mirror=fetch`, the refs will not
-be stored in the 'refs/remotes/' namespace, but rather everything in
-'refs/' on the remote will be directly mirrored into 'refs/' in the
+be stored in the `refs/remotes/` namespace, but rather everything in
+`refs/` on the remote will be directly mirrored into `refs/` in the
local repository. This option only makes sense in bare repositories,
because a fetch would overwrite any local commits.
+
When a push mirror is created with `--mirror=push`, then `git push`
will always behave as if `--mirror` was passed.
-'rename'::
+`rename`::
-Rename the remote named <old> to <new>. All remote-tracking branches and
+Rename the remote named _<old>_ to _<new>_. All remote-tracking branches and
configuration settings for the remote are updated.
+
-In case <old> and <new> are the same, and <old> is a file under
+In case _<old>_ and _<new>_ are the same, and _<old>_ is a file under
`$GIT_DIR/remotes` or `$GIT_DIR/branches`, the remote is converted to
the configuration file format.
-'remove'::
-'rm'::
+`remove`::
+`rm`::
-Remove the remote named <name>. All remote-tracking branches and
+Remove the remote named _<name>_. All remote-tracking branches and
configuration settings for the remote are removed.
-'set-head'::
+`set-head`::
-Sets or deletes the default branch (i.e. the target of the
+Set or delete the default branch (i.e. the target of the
symbolic-ref `refs/remotes/<name>/HEAD`) for
the named remote. Having a default branch for a remote is not required,
but allows the name of the remote to be specified in lieu of a specific
@@ -116,15 +116,15 @@ the symbolic-ref `refs/remotes/origin/HEAD` to `refs/remotes/origin/next`. This
only work if `refs/remotes/origin/next` already exists; if not it must be
fetched first.
+
-Use `<branch>` to set the symbolic-ref `refs/remotes/<name>/HEAD` explicitly. e.g., `git
+Use _<branch>_ to set the symbolic-ref `refs/remotes/<name>/HEAD` explicitly. e.g., `git
remote set-head origin master` will set the symbolic-ref `refs/remotes/origin/HEAD` to
`refs/remotes/origin/master`. This will only work if
`refs/remotes/origin/master` already exists; if not it must be fetched first.
+
-'set-branches'::
+`set-branches`::
-Changes the list of branches tracked by the named remote.
+Change the list of branches tracked by the named remote.
This can be used to track a subset of the available remote branches
after the initial setup for a remote.
+
@@ -134,7 +134,7 @@ The named branches will be interpreted as if specified with the
With `--add`, instead of replacing the list of currently tracked
branches, adds to that list.
-'get-url'::
+`get-url`::
Retrieves the URLs for a remote. Configurations for `insteadOf` and
`pushInsteadOf` are expanded here. By default, only the first URL is listed.
@@ -143,18 +143,18 @@ With `--push`, push URLs are queried rather than fetch URLs.
+
With `--all`, all URLs for the remote will be listed.
-'set-url'::
+`set-url`::
-Changes URLs for the remote. Sets first URL for remote <name> that matches
-regex <oldurl> (first URL if no <oldurl> is given) to <newurl>. If
-<oldurl> doesn't match any URL, an error occurs and nothing is changed.
+Change URLs for the remote. Sets first URL for remote _<name>_ that matches
+regex _<oldurl>_ (first URL if no _<oldurl>_ is given) to _<newurl>_. If
+_<oldurl>_ doesn't match any URL, an error occurs and nothing is changed.
+
With `--push`, push URLs are manipulated instead of fetch URLs.
+
With `--add`, instead of changing existing URLs, new URL is added.
+
With `--delete`, instead of changing existing URLs, all URLs matching
-regex <URL> are deleted for remote <name>. Trying to delete all
+regex _<URL>_ are deleted for remote _<name>_. Trying to delete all
non-push URLs is an error.
+
Note that the push URL and the fetch URL, even though they can
@@ -165,17 +165,17 @@ fetch from one place (e.g. your upstream) and push to another (e.g.
your publishing repository), use two separate remotes.
-'show'::
+`show`::
-Gives some information about the remote <name>.
+Give some information about the remote _<name>_.
+
With `-n` option, the remote heads are not queried first with
`git ls-remote <name>`; cached information is used instead.
-'prune'::
+`prune`::
-Deletes stale references associated with <name>. By default, stale
-remote-tracking branches under <name> are deleted, but depending on
+Delete stale references associated with _<name>_. By default, stale
+remote-tracking branches under _<name>_ are deleted, but depending on
global configuration and the configuration of the remote we might even
prune local tags that haven't been pushed there. Equivalent to `git
fetch --prune <name>`, except that no new references will be fetched.
@@ -186,13 +186,13 @@ depending on various configuration.
With `--dry-run` option, report what branches would be pruned, but do not
actually prune them.
-'update'::
+`update`::
Fetch updates for remotes or remote groups in the repository as defined by
`remotes.<group>`. If neither group nor remote is specified on the command line,
-the configuration parameter remotes.default will be used; if
-remotes.default is not defined, all remotes which do not have the
-configuration parameter `remote.<name>.skipDefaultUpdate` set to true will
+the configuration parameter `remotes.default` will be used; if
+`remotes.default` is not defined, all remotes which do not have the
+configuration parameter `remote.<name>.skipDefaultUpdate` set to `true` will
be updated. (See linkgit:git-config[1]).
+
With `--prune` option, run pruning against all the remotes that are updated.
@@ -210,7 +210,7 @@ EXIT STATUS
On success, the exit status is `0`.
-When subcommands such as 'add', 'rename', and 'remove' can't find the
+When subcommands such as `add`, `rename`, and `remove` can't find the
remote in question, the exit status is `2`. When the remote already
exists, the exit status is `3`.
@@ -247,7 +247,7 @@ $ git switch -c staging staging/master
...
------------
-* Imitate 'git clone' but track only selected branches
+* Imitate `git clone` but track only selected branches
+
------------
$ mkdir project.git
diff --git a/Documentation/git-reset.adoc b/Documentation/git-reset.adoc
index 3b9ba9aee9..5023b50699 100644
--- a/Documentation/git-reset.adoc
+++ b/Documentation/git-reset.adoc
@@ -3,86 +3,67 @@ git-reset(1)
NAME
----
-git-reset - Reset current HEAD to the specified state
+git-reset - Set `HEAD` or the index to a known state
SYNOPSIS
--------
[synopsis]
+git reset [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]
git reset [-q] [<tree-ish>] [--] <pathspec>...
git reset [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>]
git reset (--patch | -p) [<tree-ish>] [--] [<pathspec>...]
-git reset [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]
DESCRIPTION
-----------
-In the first three forms, copy entries from _<tree-ish>_ to the index.
-In the last form, set the current branch head (`HEAD`) to _<commit>_,
-optionally modifying index and working tree to match.
-The _<tree-ish>_/_<commit>_ defaults to `HEAD` in all forms.
-
-`git reset [-q] [<tree-ish>] [--] <pathspec>...`::
-`git reset [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>]`::
- These forms reset the index entries for all paths that match the
- _<pathspec>_ to their state at _<tree-ish>_. (It does not affect
- the working tree or the current branch.)
-+
-This means that `git reset <pathspec>` is the opposite of `git add
-<pathspec>`. This command is equivalent to
-`git restore [--source=<tree-ish>] --staged <pathspec>...`.
-+
-After running `git reset <pathspec>` to update the index entry, you can
-use linkgit:git-restore[1] to check the contents out of the index to
-the working tree. Alternatively, using linkgit:git-restore[1]
-and specifying a commit with `--source`, you
-can copy the contents of a path out of a commit to the index and to the
-working tree in one go.
+`git reset` does either of the following:
-`git reset (--patch | -p) [<tree-ish>] [--] [<pathspec>...]`::
- Interactively select hunks in the difference between the index
- and _<tree-ish>_ (defaults to `HEAD`). The chosen hunks are applied
- in reverse to the index.
-+
-This means that `git reset -p` is the opposite of `git add -p`, i.e.
-you can use it to selectively reset hunks. See the "Interactive Mode"
-section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
+1. `git reset [<mode>] <commit>` changes which commit `HEAD` points to. This
+ makes it possible to undo various Git operations, for example commit, merge,
+ rebase, and pull.
+2. When you specify files or directories or pass `--patch`, `git reset` updates
+ the staged version of the specified files.
`git reset [<mode>] [<commit>]`::
- This form resets the current branch head to _<commit>_ and
- possibly updates the index (resetting it to the tree of _<commit>_) and
- the working tree depending on _<mode>_. Before the operation, `ORIG_HEAD`
- is set to the tip of the current branch. If _<mode>_ is omitted,
- defaults to `--mixed`. The _<mode>_ must be one of the following:
+ Set the current branch head (`HEAD`) to point at _<commit>_.
+ Depending on _<mode>_, also update the working directory and/or index
+ to match the contents of _<commit>_.
+ _<commit>_ defaults to `HEAD`.
+ Before the operation, `ORIG_HEAD` is set to the tip of the current branch.
++
+The _<mode>_ must be one of the following (default `--mixed`):
+
---
-`--soft`::
- Does not touch the index file or the working tree at all (but
- resets the head to _<commit>_, just like all modes do). This leaves
- all your changed files "Changes to be committed", as `git status`
- would put it.
+--
`--mixed`::
- Resets the index but not the working tree (i.e., the changed files
- are preserved but not marked for commit) and reports what has not
- been updated. This is the default action.
+ Leave your working directory unchanged.
+ Update the index to match the new `HEAD`, so nothing will be staged.
+
-If `-N` is specified, removed paths are marked as intent-to-add (see
+If `-N` is specified, mark removed paths as intent-to-add (see
linkgit:git-add[1]).
+`--soft`::
+ Leave your working tree files and the index unchanged.
+ For example, if you have no staged changes, you can use
+ `git reset --soft HEAD~5; git commit`
+ to combine the last 5 commits into 1 commit. This works even with
+ changes in the working tree, which are left untouched, but such usage
+ can lead to confusion.
+
`--hard`::
- Resets the index and working tree. Any changes to tracked files in the
- working tree since _<commit>_ are discarded. Any untracked files or
- directories in the way of writing any tracked files are simply deleted.
+ Overwrite all files and directories with the version from _<commit>_,
+ and may overwrite untracked files. Tracked files not in _<commit>_ are
+ removed so that the working tree matches _<commit>_.
+ Update the index to match the new `HEAD`, so nothing will be staged.
`--merge`::
- Resets the index and updates the files in the working tree that are
- different between _<commit>_ and `HEAD`, but keeps those which are
+ Reset the index and update the files in the working tree that are
+ different between _<commit>_ and `HEAD`, but keep those which are
different between the index and working tree (i.e. which have changes
which have not been added).
+ Mainly exists to reset unmerged index entries, like those left behind by
+ `git am -3` or `git switch -m` in certain situations.
If a file that is different between _<commit>_ and the index has
unstaged changes, reset is aborted.
-+
-In other words, `--merge` does something like a `git read-tree -u -m <commit>`,
-but carries forward unmerged index entries.
`--keep`::
Resets index entries and updates files in the working tree that are
@@ -98,6 +79,28 @@ but carries forward unmerged index entries.
the submodules' `HEAD` to be detached at that commit.
--
+`git reset [-q] [<tree-ish>] [--] <pathspec>...`::
+`git reset [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>]`::
+ For all specified files or directories, set the staged version to
+ the version from the given commit or tree (which defaults to `HEAD`).
++
+This means that `git reset <pathspec>` is the opposite of `git add
+<pathspec>`: it unstages all changes to the specified file(s) or
+directories. This is equivalent to `git restore --staged <pathspec>...`.
++
+In this mode, `git reset` updates only the index (without updating the `HEAD` or
+working tree files). If you want to update the files as well as the index
+entries, use linkgit:git-restore[1].
+
+`git reset (--patch | -p) [<tree-ish>] [--] [<pathspec>...]`::
+ Interactively select changes from the difference between the index
+ and the specified commit or tree (which defaults to `HEAD`).
+ The index is modified using the chosen changes.
++
+This means that `git reset -p` is the opposite of `git add -p`, i.e.
+you can use it to selectively unstage changes. See the "Interactive Mode"
+section of linkgit:git-add[1] to learn how to use the `--patch` option.
+
See "Reset, restore and revert" in linkgit:git[1] for the differences
between the three commands.
diff --git a/Documentation/git-stage.adoc b/Documentation/git-stage.adoc
index 2f6aaa75b9..753a817639 100644
--- a/Documentation/git-stage.adoc
+++ b/Documentation/git-stage.adoc
@@ -8,8 +8,8 @@ git-stage - Add file contents to the staging area
SYNOPSIS
--------
-[verse]
-'git stage' <arg>...
+[synopsis]
+git stage <arg>...
DESCRIPTION
diff --git a/Documentation/git-status.adoc b/Documentation/git-status.adoc
index 9a376886a5..9acca52bfb 100644
--- a/Documentation/git-status.adoc
+++ b/Documentation/git-status.adoc
@@ -8,8 +8,9 @@ git-status - Show the working tree status
SYNOPSIS
--------
-[verse]
-'git status' [<options>] [--] [<pathspec>...]
+
+[synopsis]
+git status [<options>] [--] [<pathspec>...]
DESCRIPTION
-----------
@@ -18,57 +19,57 @@ current HEAD commit, paths that have differences between the working
tree and the index file, and paths in the working tree that are not
tracked by Git (and are not ignored by linkgit:gitignore[5]). The first
are what you _would_ commit by running `git commit`; the second and
-third are what you _could_ commit by running 'git add' before running
+third are what you _could_ commit by running `git add` before running
`git commit`.
OPTIONS
-------
--s::
---short::
+`-s`::
+`--short`::
Give the output in the short-format.
--b::
---branch::
+`-b`::
+`--branch`::
Show the branch and tracking info even in short-format.
---show-stash::
+`--show-stash`::
Show the number of entries currently stashed away.
---porcelain[=<version>]::
+`--porcelain[=<version>]`::
Give the output in an easy-to-parse format for scripts.
This is similar to the short output, but will remain stable
across Git versions and regardless of user configuration. See
below for details.
+
-The version parameter is used to specify the format version.
-This is optional and defaults to the original version 'v1' format.
+The _<version>_ parameter is used to specify the format version.
+This is optional and defaults to the original version `v1` format.
---long::
+`--long`::
Give the output in the long-format. This is the default.
--v::
---verbose::
+`-v`::
+`--verbose`::
In addition to the names of files that have been changed, also
show the textual changes that are staged to be committed
(i.e., like the output of `git diff --cached`). If `-v` is specified
twice, then also show the changes in the working tree that
have not yet been staged (i.e., like the output of `git diff`).
--u[<mode>]::
---untracked-files[=<mode>]::
+`-u[<mode>]`::
+`--untracked-files[=<mode>]`::
Show untracked files.
+
--
The mode parameter is used to specify the handling of untracked files.
-It is optional: it defaults to 'all', and if specified, it must be
+It is optional: it defaults to `all`, and if specified, it must be
stuck to the option (e.g. `-uno`, but not `-u no`).
The possible options are:
- - 'no' - Show no untracked files.
- - 'normal' - Shows untracked files and directories.
- - 'all' - Also shows individual files in untracked directories.
+`no`:: Show no untracked files.
+`normal`:: Show untracked files and directories.
+`all`:: Also show individual files in untracked directories.
When `-u` option is not used, untracked files and directories are
shown (i.e. the same as specifying `normal`), to help you avoid
@@ -82,76 +83,78 @@ return more quickly without showing untracked files.
All usual spellings for Boolean value `true` are taken as `normal`
and `false` as `no`.
-The default can be changed using the status.showUntrackedFiles
+The default can be changed using the `status.showUntrackedFiles`
configuration variable documented in linkgit:git-config[1].
--
---ignore-submodules[=<when>]::
- Ignore changes to submodules when looking for changes. <when> can be
- either "none", "untracked", "dirty" or "all", which is the default.
- Using "none" will consider the submodule modified when it either contains
+`--ignore-submodules[=<when>]`::
+ Ignore changes to submodules when looking for changes. _<when>_ can be
+ either `none`, `untracked`, `dirty` or `all`, which is the default.
+`none`;; will consider the submodule modified when it either contains
untracked or modified files or its HEAD differs from the commit recorded
in the superproject and can be used to override any settings of the
- 'ignore' option in linkgit:git-config[1] or linkgit:gitmodules[5]. When
- "untracked" is used submodules are not considered dirty when they only
+ `ignore` option in linkgit:git-config[1] or linkgit:gitmodules[5].
+`untracked`;; submodules are not considered dirty when they only
contain untracked content (but they are still scanned for modified
- content). Using "dirty" ignores all changes to the work tree of submodules,
+ content).
+`dirty`;; ignore all changes to the work tree of submodules,
only changes to the commits stored in the superproject are shown (this was
- the behavior before 1.7.0). Using "all" hides all changes to submodules
+ the behavior before 1.7.0).
+`all`;; hide all changes to submodules
(and suppresses the output of submodule summaries when the config option
`status.submoduleSummary` is set).
---ignored[=<mode>]::
+`--ignored[=<mode>]`::
Show ignored files as well.
+
--
The mode parameter is used to specify the handling of ignored files.
-It is optional: it defaults to 'traditional'.
+It is optional: it defaults to `traditional`.
The possible options are:
- - 'traditional' - Shows ignored files and directories, unless
- --untracked-files=all is specified, in which case
- individual files in ignored directories are
- displayed.
- - 'no' - Show no ignored files.
- - 'matching' - Shows ignored files and directories matching an
- ignore pattern.
-
-When 'matching' mode is specified, paths that explicitly match an
+`traditional`:: Show ignored files and directories, unless
+`--untracked-files=all` is specified, in which case
+ individual files in ignored directories are
+ displayed.
+`no`:: Show no ignored files.
+`matching`:: Show ignored files and directories matching an
+ignore pattern.
++
+Paths that explicitly match an
ignored pattern are shown. If a directory matches an ignore pattern,
then it is shown, but not paths contained in the ignored directory. If
a directory does not match an ignore pattern, but all contents are
ignored, then the directory is not shown, but all contents are shown.
--
--z::
- Terminate entries with NUL, instead of LF. This implies
+`-z`::
+ Terminate entries with _NUL_, instead of _LF_. This implies
the `--porcelain=v1` output format if no other format is given.
---column[=<options>]::
---no-column::
+`--column[=<options>]`::
+`--no-column`::
Display untracked files in columns. See configuration variable
`column.status` for option syntax. `--column` and `--no-column`
- without options are equivalent to 'always' and 'never'
+ without options are equivalent to `always` and `never`
respectively.
---ahead-behind::
---no-ahead-behind::
+`--ahead-behind`::
+`--no-ahead-behind`::
Display or do not display detailed ahead/behind counts for the
- branch relative to its upstream branch. Defaults to true.
+ branch relative to its upstream branch. Defaults to `true`.
---renames::
---no-renames::
+`--renames`::
+`--no-renames`::
Turn on/off rename detection regardless of user configuration.
See also linkgit:git-diff[1] `--no-renames`.
---find-renames[=<n>]::
+`--find-renames[=<n>]`::
Turn on rename detection, optionally setting the similarity
threshold.
See also linkgit:git-diff[1] `--find-renames`.
-<pathspec>...::
+`<pathspec>...`::
See the 'pathspec' entry in linkgit:gitglossary[7].
OUTPUT
@@ -173,12 +176,12 @@ Short Format
In the short-format, the status of each path is shown as one of these
forms
- XY PATH
- XY ORIG_PATH -> PATH
+ <xy> <path>
+ <xy> <orig-path> -> <path>
-where `ORIG_PATH` is where the renamed/copied contents came
-from. `ORIG_PATH` is only shown when the entry is renamed or
-copied. The `XY` is a two-letter status code.
+where _<orig-path>_ is where the renamed/copied contents came
+from. _<orig-path>_ is only shown when the entry is renamed or
+copied. The _<xy>_ is a two-letter status code `XY`.
The fields (including the `->`) are separated from each other by a
single space. If a filename contains whitespace or other nonprintable
@@ -187,7 +190,7 @@ literal: surrounded by ASCII double quote (34) characters, and with
interior special characters backslash-escaped.
There are three different types of states that are shown using this format, and
-each one uses the `XY` syntax differently:
+each one uses the _<xy>_ syntax differently:
* When a merge is occurring and the merge was successful, or outside of a merge
situation, `X` shows the status of the index and `Y` shows the status of the
@@ -207,60 +210,59 @@ In the following table, these three classes are shown in separate sections, and
these characters are used for `X` and `Y` fields for the first two sections that
show tracked paths:
-* ' ' = unmodified
-* 'M' = modified
-* 'T' = file type changed (regular file, symbolic link or submodule)
-* 'A' = added
-* 'D' = deleted
-* 'R' = renamed
-* 'C' = copied (if config option status.renames is set to "copies")
-* 'U' = updated but unmerged
+' ':: unmodified
+`M`:: modified
+`T`:: file type changed (regular file, symbolic link or submodule)
+`A`:: added
+`D`:: deleted
+`R`:: renamed
+`C`:: copied (if config option status.renames is set to "copies")
+`U`:: updated but unmerged
-....
-X Y Meaning
--------------------------------------------------
- [AMD] not updated
-M [ MTD] updated in index
-T [ MTD] type changed in index
-A [ MTD] added to index
-D deleted from index
-R [ MTD] renamed in index
-C [ MTD] copied in index
-[MTARC] index and work tree matches
-[ MTARC] M work tree changed since index
-[ MTARC] T type changed in work tree since index
-[ MTARC] D deleted in work tree
- R renamed in work tree
- C copied in work tree
--------------------------------------------------
-D D unmerged, both deleted
-A U unmerged, added by us
-U D unmerged, deleted by them
-U A unmerged, added by them
-D U unmerged, deleted by us
-A A unmerged, both added
-U U unmerged, both modified
--------------------------------------------------
-? ? untracked
-! ! ignored
--------------------------------------------------
-....
+[cols="^1m,^1m,<2",options="header"]
+|===
+|X | Y |Meaning
+| |[AMD] |not updated
+|M |[ MTD] |updated in index
+|T |[ MTD] |type changed in index
+|A |[ MTD] |added to index
+|D | |deleted from index
+|R |[ MTD] |renamed in index
+|C |[ MTD] |copied in index
+|[MTARC] | |index and work tree matches
+|[ MTARC] |M |work tree changed since index
+|[ MTARC] |T |type changed in work tree since index
+|[ MTARC] |D |deleted in work tree
+| |R |renamed in work tree
+| |C |copied in work tree
+|D |D |unmerged, both deleted
+|A |U |unmerged, added by us
+|U |D |unmerged, deleted by them
+|U |A |unmerged, added by them
+|D |U |unmerged, deleted by us
+|A |A |unmerged, both added
+|U |U |unmerged, both modified
+|? |? |untracked
+|! |! |ignored
+|===
Submodules have more state and instead report
-* 'M' = the submodule has a different HEAD than recorded in the index
-* 'm' = the submodule has modified content
-* '?' = the submodule has untracked files
+`M`:: the submodule has a different HEAD than recorded in the index
+`m`:: the submodule has modified content
+`?`:: the submodule has untracked files
This is since modified content or untracked files in a submodule cannot be added
via `git add` in the superproject to prepare a commit.
-'m' and '?' are applied recursively. For example if a nested submodule
-in a submodule contains an untracked file, this is reported as '?' as well.
+`m` and `?` are applied recursively. For example if a nested submodule
+in a submodule contains an untracked file, this is reported as `?` as well.
-If -b is used the short-format status is preceded by a line
+If `-b` is used the short-format status is preceded by a line
+
+[synopsis]
+{empty}## <branchname> <tracking-info>
- ## branchname tracking info
Porcelain Format Version 1
~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -271,16 +273,16 @@ based on user configuration. This makes it ideal for parsing by scripts.
The description of the short format above also describes the porcelain
format, with a few exceptions:
-1. The user's color.status configuration is not respected; color will
+1. The user's `color.status` configuration is not respected; color will
always be off.
-2. The user's status.relativePaths configuration is not respected; paths
+2. The user's `status.relativePaths` configuration is not respected; paths
shown will always be relative to the repository root.
-There is also an alternate -z format recommended for machine parsing. In
+There is also an alternate `-z` format recommended for machine parsing. In
that format, the status field is the same, but some other things
-change. First, the '\->' is omitted from rename entries and the field
-order is reversed (e.g 'from \-> to' becomes 'to from'). Second, a NUL
+change. First, the `->` is omitted from rename entries and the field
+order is reversed (e.g `from -> to` becomes `to from`). Second, a _NUL_
(ASCII 0) follows each filename, replacing space as a field separator
and the terminating newline (but a space still separates the status
field from the first filename). Third, filenames containing special
@@ -296,7 +298,7 @@ Version 2 format adds more detailed information about the state of
the worktree and changed items. Version 2 also defines an extensible
set of easy to parse optional headers.
-Header lines start with "#" and are added in response to specific
+Header lines start with `#` and are added in response to specific
command line arguments. Parsers should ignore headers they
don't recognize.
@@ -306,16 +308,15 @@ Branch Headers
If `--branch` is given, a series of header lines are printed with
information about the current branch.
-....
-Line Notes
-------------------------------------------------------------
-# branch.oid <commit> | (initial) Current commit.
-# branch.head <branch> | (detached) Current branch.
-# branch.upstream <upstream-branch> If upstream is set.
-# branch.ab +<ahead> -<behind> If upstream is set and
- the commit is present.
-------------------------------------------------------------
-....
+[cols="<1,<1",options="header"]
+|===
+|Line |Notes
+|`# branch.oid <commit> \| (initial)` |Current commit.
+|`# branch.head <branch> \| (detached)` |Current branch.
+|`# branch.upstream <upstream-branch>` |If upstream is set.
+|`# branch.ab +<ahead> -<behind>` |If upstream is set and
+ the commit is present.
+|===
Stash Information
^^^^^^^^^^^^^^^^^
@@ -336,66 +337,73 @@ line types in any order.
Ordinary changed entries have the following format:
- 1 <XY> <sub> <mH> <mI> <mW> <hH> <hI> <path>
+[synopsis]
+1 <XY> <sub> <mH> <mI> <mW> <hH> <hI> <path>
Renamed or copied entries have the following format:
- 2 <XY> <sub> <mH> <mI> <mW> <hH> <hI> <X><score> <path><sep><origPath>
+[synopsis]
+2 <XY> <sub> <mH> <mI> <mW> <hH> <hI> <X><score> <path><sep><origPath>
+
+[cols="<1,<1a",options="header"]
+|===
+|Field | Meaning
+
+|_<XY>_
+|A 2 character field containing the staged and
+unstaged XY values described in the short format,
+with unchanged indicated by a "." rather than
+a space.
+|_<sub>_
+|A 4 character field describing the submodule state.
+"N..." when the entry is not a submodule.
+`S<c><m><u>` when the entry is a submodule.
-....
-Field Meaning
---------------------------------------------------------
-<XY> A 2 character field containing the staged and
- unstaged XY values described in the short format,
- with unchanged indicated by a "." rather than
- a space.
-<sub> A 4 character field describing the submodule state.
- "N..." when the entry is not a submodule.
- "S<c><m><u>" when the entry is a submodule.
- <c> is "C" if the commit changed; otherwise ".".
- <m> is "M" if it has tracked changes; otherwise ".".
- <u> is "U" if there are untracked changes; otherwise ".".
-<mH> The octal file mode in HEAD.
-<mI> The octal file mode in the index.
-<mW> The octal file mode in the worktree.
-<hH> The object name in HEAD.
-<hI> The object name in the index.
-<X><score> The rename or copy score (denoting the percentage
- of similarity between the source and target of the
- move or copy). For example "R100" or "C75".
-<path> The pathname. In a renamed/copied entry, this
- is the target path.
-<sep> When the `-z` option is used, the 2 pathnames are separated
- with a NUL (ASCII 0x00) byte; otherwise, a tab (ASCII 0x09)
- byte separates them.
-<origPath> The pathname in the commit at HEAD or in the index.
- This is only present in a renamed/copied entry, and
- tells where the renamed/copied contents came from.
---------------------------------------------------------
-....
+* _<c>_ is "C" if the commit changed; otherwise ".".
+* _<m>_ is "M" if it has tracked changes; otherwise ".".
+* _<u>_ is "U" if there are untracked changes; otherwise ".".
+|_<mH>_ |The octal file mode in HEAD.
+|_<mI>_ |The octal file mode in the index.
+|_<mW>_ |The octal file mode in the worktree.
+|_<hH>_ |The object name in HEAD.
+|_<hI>_ |The object name in the index.
+|_<X><score>_ |The rename or copy score (denoting the percentage
+of similarity between the source and target of the
+move or copy). For example "R100" or "C75".
+|_<path>_
+|The pathname. In a renamed/copied entry, this is the target path.
+|_<sep>_
+|When the `-z` option is used, the 2 pathnames are separated
+with a _NUL_ (ASCII 0x00) byte; otherwise, a _TAB_ (ASCII 0x09)
+byte separates them.
+|_<origPath>_
+|The pathname in the commit at HEAD or in the index.
+This is only present in a renamed/copied entry, and
+tells where the renamed/copied contents came from.
+|===
Unmerged entries have the following format; the first character is
a "u" to distinguish from ordinary changed entries.
- u <XY> <sub> <m1> <m2> <m3> <mW> <h1> <h2> <h3> <path>
+[synopsis]
+u <XY> <sub> <m1> <m2> <m3> <mW> <h1> <h2> <h3> <path>
-....
-Field Meaning
---------------------------------------------------------
-<XY> A 2 character field describing the conflict type
+[cols="<1,<1a",options="header"]
+|===
+|Field |Meaning
+|_<XY>_ |A 2 character field describing the conflict type
as described in the short format.
-<sub> A 4 character field describing the submodule state
+|_<sub>_ |A 4 character field describing the submodule state
as described above.
-<m1> The octal file mode in stage 1.
-<m2> The octal file mode in stage 2.
-<m3> The octal file mode in stage 3.
-<mW> The octal file mode in the worktree.
-<h1> The object name in stage 1.
-<h2> The object name in stage 2.
-<h3> The object name in stage 3.
-<path> The pathname.
---------------------------------------------------------
-....
+|_<m1>_ |The octal file mode in stage 1.
+|_<m2>_ |The octal file mode in stage 2.
+|_<m3>_ |The octal file mode in stage 3.
+|_<mW>_ |The octal file mode in the worktree.
+|_<h1>_ |The object name in stage 1.
+|_<h2>_ |The object name in stage 2.
+|_<h3>_ |The object name in stage 3.
+|_<path>_ |The pathname.
+|===
Other Items
^^^^^^^^^^^
@@ -416,7 +424,7 @@ Pathname Format Notes and -z
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When the `-z` option is given, pathnames are printed as is and
-without any quoting and lines are terminated with a NUL (ASCII 0x00)
+without any quoting and lines are terminated with a _NUL_ (ASCII 0x00)
byte.
Without the `-z` option, pathnames with "unusual" characters are
@@ -439,11 +447,11 @@ directory.
If `status.submoduleSummary` is set to a non zero number or true (identical
to -1 or an unlimited number), the submodule summary will be enabled for
the long format and a summary of commits for modified submodules will be
-shown (see --summary-limit option of linkgit:git-submodule[1]). Please note
+shown (see `--summary-limit` option of linkgit:git-submodule[1]). Please note
that the summary output from the status command will be suppressed for all
-submodules when `diff.ignoreSubmodules` is set to 'all' or only for those
+submodules when `diff.ignoreSubmodules` is set to `all` or only for those
submodules where `submodule.<name>.ignore=all`. To also view the summary for
-ignored submodules you can either use the --ignore-submodules=dirty command
+ignored submodules you can either use the `--ignore-submodules=dirty` command
line option or the 'git submodule summary' command, which shows a similar
output but does not honor these settings.
@@ -484,7 +492,7 @@ results, so it could be faster on subsequent runs.
setting this variable to `false` disables the warning message
given when enumerating untracked files takes more than 2
seconds. In a large project, it may take longer and the user
- may have already accepted the trade off (e.g. using "-uno" may
+ may have already accepted the trade off (e.g. using `-uno` may
not be an acceptable option for the user), in which case, there
is no point issuing the warning message, and in such a case,
disabling the warning may be the best.
diff --git a/Documentation/git-update-ref.adoc b/Documentation/git-update-ref.adoc
index 9310ce9768..37a5019a8b 100644
--- a/Documentation/git-update-ref.adoc
+++ b/Documentation/git-update-ref.adoc
@@ -119,7 +119,7 @@ verify::
Verify <ref> against <old-oid> but do not change it. If
<old-oid> is zero or missing, the ref must not exist.
-symref-create:
+symref-create::
Create symbolic ref <ref> with <new-target> after verifying that
it does not exist.
diff --git a/Documentation/gitfaq.adoc b/Documentation/gitfaq.adoc
index 8d3647d359..f6c9b9d9f7 100644
--- a/Documentation/gitfaq.adoc
+++ b/Documentation/gitfaq.adoc
@@ -233,14 +233,30 @@ of refs, such that both sides end up with different commits on a branch that
the other doesn't have. This can result in important objects becoming
unreferenced and possibly pruned by `git gc`, causing data loss.
+
-Therefore, it's better to push your work to either the other system or a central
-server using the normal push and pull mechanism. However, this doesn't always
-preserve important data, like stashes, so some people prefer to share a working
-tree across systems.
+Therefore, it's better to push your work to either the other system or a
+central server using the normal push and pull mechanism. In Git 2.51, Git
+learned to import and export stashes, so it's possible to synchronize the state
+of the working tree by stashing it with `git stash`, then exporting either all
+stashes with `git stash export --to-ref refs/heads/stashes` (assuming you want
+to export to the `stashes` branch) or selecting stashes by adding their numbers
+to the end of that command. It's also possible to include untracked files by
+using the `--include-untracked` argument when stashing the data in the first
+place, but be careful not to do this if any of these contain sensitive
+information.
+
-If you do this, the recommended approach is to use `rsync -a --delete-after`
-(ideally with an encrypted connection such as with `ssh`) on the root of
-repository. You should ensure several things when you do this:
+You can then push the `stashes` branch (or whatever branch you've exported to),
+fetch them to the local system (such as with `git fetch origin
++stashes:stashes`), and import the stashes on the other system with `git stash
+import stashes` (again, changing the name as necessary). Applying the changes
+to the working tree can be done with `git stash pop` or `git stash apply`.
+This is the approach that is most robust and most likely to avoid unintended
+problems.
++
+Having said that, there are some cases where people nevertheless prefer to
+share a working tree across systems. If you do this, the recommended approach
+is to use `rsync -a --delete-after` (ideally with an encrypted connection such
+as with `ssh`) on the root of repository. You should ensure several things
+when you do this:
+
* If you have additional worktrees or a separate Git directory, they must be
synced at the same time as the main working tree and repository.
@@ -251,10 +267,11 @@ repository. You should ensure several things when you do this:
any sort are taking place on it, including background operations like `git
gc` and operations invoked by your editor).
+
-Be aware that even with these recommendations, syncing in this way has some risk
-since it bypasses Git's normal integrity checking for repositories, so having
-backups is advised. You may also wish to do a `git fsck` to verify the
-integrity of your data on the destination system after syncing.
+Be aware that even with these recommendations, syncing working trees in this
+way has some risk since it bypasses Git's normal integrity checking for
+repositories, so having backups is advised. You may also wish to do a `git
+fsck` to verify the integrity of your data on the destination system after
+syncing.
Common Issues
-------------
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 1f7af0328a..5adc4afd67 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,6 +1,6 @@
#!/bin/sh
-DEF_VER=v2.52.GIT
+DEF_VER=v2.53.0-rc0
LF='
'
diff --git a/Makefile b/Makefile
index 89d8d73ec0..8aa489f3b6 100644
--- a/Makefile
+++ b/Makefile
@@ -101,6 +101,15 @@ include shared.mak
# specify your own (or DarwinPort's) include directories and
# library directories by defining CFLAGS and LDFLAGS appropriately.
#
+# Define NO_HOMEBREW if you don't want to use gettext, libiconv and
+# msgfmt installed by Homebrew.
+#
+# Define HOMEBREW_PREFIX if you have Homebrew installed in a non-default
+# location on macOS or on Linux and want to use it.
+#
+# Define USE_HOMEBREW_LIBICONV to link against libiconv installed by
+# Homebrew, if present.
+#
# Define NO_APPLE_COMMON_CRYPTO if you are building on Darwin/Mac OS X
# and do not want to use Apple's CommonCrypto library. This allows you
# to provide your own OpenSSL library, for example from MacPorts.
@@ -1678,11 +1687,21 @@ ifeq ($(uname_S),Darwin)
BASIC_CFLAGS += -I/sw/include
BASIC_LDFLAGS += -L/sw/lib
endif
+ ifeq ($(shell test -d /opt/sw/lib && echo y),y)
+ BASIC_CFLAGS += -I/opt/sw/include
+ BASIC_LDFLAGS += -L/opt/sw/lib
+ ifeq ($(shell test -e /opt/sw/lib/libiconv.dylib && echo y),y)
+ HAS_GOOD_LIBICONV = Yes
+ endif
+ endif
endif
ifndef NO_DARWIN_PORTS
ifeq ($(shell test -d /opt/local/lib && echo y),y)
BASIC_CFLAGS += -I/opt/local/include
BASIC_LDFLAGS += -L/opt/local/lib
+ ifeq ($(shell test -e /opt/local/lib/libiconv.dylib && echo y),y)
+ HAS_GOOD_LIBICONV = Yes
+ endif
endif
endif
ifndef NO_APPLE_COMMON_CRYPTO
@@ -1693,6 +1712,24 @@ ifeq ($(uname_S),Darwin)
PTHREAD_LIBS =
endif
+ifndef NO_HOMEBREW
+ifdef HOMEBREW_PREFIX
+ifeq ($(shell test -d $(HOMEBREW_PREFIX)/opt/gettext && echo y),y)
+ BASIC_CFLAGS += -I$(HOMEBREW_PREFIX)/opt/gettext/include
+ BASIC_LDFLAGS += -L$(HOMEBREW_PREFIX)/opt/gettext/lib
+endif
+ifeq ($(shell test -x $(HOMEBREW_PREFIX)/opt/gettext/msgfmt && echo y),y)
+ MSGFMT = $(HOMEBREW_PREFIX)/opt/gettext/msgfmt
+endif
+ifdef USE_HOMEBREW_LIBICONV
+ifeq ($(shell test -d $(HOMEBREW_PREFIX)/opt/libiconv && echo y),y)
+ ICONVDIR ?= $(HOMEBREW_PREFIX)/opt/libiconv
+ HAS_GOOD_LIBICONV = Yes
+endif
+endif
+endif
+endif
+
ifdef NO_LIBGEN_H
COMPAT_CFLAGS += -DNO_LIBGEN_H
COMPAT_OBJS += compat/basename.o
@@ -1833,6 +1870,11 @@ ifndef NO_ICONV
endif
EXTLIBS += $(ICONV_LINK) -liconv
endif
+ ifdef NEEDS_GOOD_LIBICONV
+ ifndef HAS_GOOD_LIBICONV
+ BASIC_CFLAGS += -DICONV_RESTART_RESET
+ endif
+ endif
endif
ifdef ICONV_OMITS_BOM
BASIC_CFLAGS += -DICONV_OMITS_BOM
diff --git a/add-interactive.c b/add-interactive.c
index 68fc09547d..95ec5a89f8 100644
--- a/add-interactive.c
+++ b/add-interactive.c
@@ -840,7 +840,7 @@ static int run_revert(struct add_i_state *s, const struct pathspec *ps,
if (is_initial)
oidcpy(&oid, s->r->hash_algo->empty_tree);
else {
- tree = parse_tree_indirect(&oid);
+ tree = repo_parse_tree_indirect(s->r, &oid);
if (!tree) {
res = error(_("Could not parse HEAD^{tree}"));
goto finish_revert;
diff --git a/archive.c b/archive.c
index 310672b479..fcd474c682 100644
--- a/archive.c
+++ b/archive.c
@@ -519,7 +519,7 @@ static void parse_treeish_arg(const char **argv,
if (ar_args->mtime_option)
archive_time = approxidate(ar_args->mtime_option);
- tree = parse_tree_indirect(&oid);
+ tree = repo_parse_tree_indirect(the_repository, &oid);
if (!tree)
die(_("not a tree object: %s"), oid_to_hex(&oid));
diff --git a/bloom.c b/bloom.c
index 2d7b951e5b..77a6fddf72 100644
--- a/bloom.c
+++ b/bloom.c
@@ -354,7 +354,7 @@ static void init_truncated_large_filter(struct bloom_filter *filter,
static int has_entries_with_high_bit(struct repository *r, struct tree *t)
{
- if (parse_tree(t))
+ if (repo_parse_tree(r, t))
return 1;
if (!(t->object.flags & VISITED)) {
diff --git a/builtin.h b/builtin.h
index 1b35565fbd..e5e16ecaa6 100644
--- a/builtin.h
+++ b/builtin.h
@@ -17,7 +17,8 @@
* . Define the implementation of the built-in command `foo` with
* signature:
*
- * int cmd_foo(int argc, const char **argv, const char *prefix);
+ * int cmd_foo(int argc, const char **argv,
+ * const char *prefix, struct repository *repo);
*
* . Add the external declaration for the function to `builtin.h`.
*
@@ -29,12 +30,14 @@
* where options is the bitwise-or of:
*
* `RUN_SETUP`:
+ *
* If there is not a Git directory to work on, abort. If there
* is a work tree, chdir to the top of it if the command was
* invoked in a subdirectory. If there is no work tree, no
* chdir() is done.
*
* `RUN_SETUP_GENTLY`:
+ *
* If there is a Git directory, chdir as per RUN_SETUP, otherwise,
* don't chdir anywhere.
*
@@ -57,6 +60,12 @@
* more informed decision, e.g., by ignoring `pager.<cmd>` for
* certain subcommands.
*
+ * `NO_PARSEOPT`:
+ *
+ * Most Git builtins use the parseopt library for parsing options.
+ * This flag indicates that a custom parser is used and thus the
+ * builtin would not appear in 'git --list-cmds=parseopt'.
+ *
* . Add `builtin/foo.o` to `BUILTIN_OBJS` in `Makefile`.
*
* Additionally, if `foo` is a new command, there are 4 more things to do:
@@ -69,6 +78,21 @@
*
* . Add an entry for `/git-foo` to `.gitignore`.
*
+ * As you work on implementing your builtin, be mindful that the
+ * following tests will check different aspects of the builtin's
+ * readiness and adherence to matching the documentation:
+ *
+ * * t0012-help.sh checks that the builtin can handle -h, which comes
+ * automatically with the parseopt API.
+ *
+ * * t0450-txt-doc-vs-help.sh checks that the -h help output matches the
+ * SYNOPSIS in the documentation for the builtin.
+ *
+ * * t1517-outside-repo.sh checks that the builtin can handle -h when
+ * run outside of the context of a repository. Note that this test
+ * requires that the usage has a space after the builtin name, so some
+ * minimum description of options is required.
+ *
*
* How a built-in is called
* ------------------------
diff --git a/builtin/am.c b/builtin/am.c
index 277c2e7937..b66a33d8a8 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1998,7 +1998,7 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
struct unpack_trees_options opts;
struct tree_desc t[2];
- if (parse_tree(head) || parse_tree(remote))
+ if (repo_parse_tree(the_repository, head) || repo_parse_tree(the_repository, remote))
return -1;
repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
@@ -2038,7 +2038,7 @@ static int merge_tree(struct tree *tree)
struct unpack_trees_options opts;
struct tree_desc t[1];
- if (parse_tree(tree))
+ if (repo_parse_tree(the_repository, tree))
return -1;
repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
@@ -2071,11 +2071,11 @@ static int clean_index(const struct object_id *head, const struct object_id *rem
struct tree *head_tree, *remote_tree, *index_tree;
struct object_id index;
- head_tree = parse_tree_indirect(head);
+ head_tree = repo_parse_tree_indirect(the_repository, head);
if (!head_tree)
return error(_("Could not parse object '%s'."), oid_to_hex(head));
- remote_tree = parse_tree_indirect(remote);
+ remote_tree = repo_parse_tree_indirect(the_repository, remote);
if (!remote_tree)
return error(_("Could not parse object '%s'."), oid_to_hex(remote));
@@ -2089,7 +2089,7 @@ static int clean_index(const struct object_id *head, const struct object_id *rem
0, NULL))
return -1;
- index_tree = parse_tree_indirect(&index);
+ index_tree = repo_parse_tree_indirect(the_repository, &index);
if (!index_tree)
return error(_("Could not parse object '%s'."), oid_to_hex(&index));
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index 505ddaa12f..df8e87a81f 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -487,8 +487,7 @@ static void batch_object_write(const char *obj_name,
data->info.sizep = &data->size;
if (pack)
- ret = packed_object_info(the_repository, pack,
- offset, &data->info);
+ ret = packed_object_info(pack, offset, &data->info);
else
ret = odb_read_object_info_extended(the_repository->objects,
&data->oid, &data->info,
@@ -846,12 +845,14 @@ static void batch_each_object(struct batch_options *opt,
.callback = callback,
.payload = _payload,
};
- struct bitmap_index *bitmap = prepare_bitmap_git(the_repository);
+ struct bitmap_index *bitmap = NULL;
for_each_loose_object(the_repository->objects, batch_one_object_loose, &payload, 0);
- if (bitmap && !for_each_bitmapped_object(bitmap, &opt->objects_filter,
- batch_one_object_bitmapped, &payload)) {
+ if (opt->objects_filter.choice != LOFC_DISABLED &&
+ (bitmap = prepare_bitmap_git(the_repository)) &&
+ !for_each_bitmapped_object(bitmap, &opt->objects_filter,
+ batch_one_object_bitmapped, &payload)) {
struct packed_git *pack;
repo_for_each_pack(the_repository, pack) {
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 66b69df6e6..0ba4f03f2e 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -724,7 +724,7 @@ static int reset_tree(struct tree *tree, const struct checkout_opts *o,
init_checkout_metadata(&opts.meta, info->refname,
info->commit ? &info->commit->object.oid : null_oid(the_hash_algo),
NULL);
- if (parse_tree(tree) < 0)
+ if (repo_parse_tree(the_repository, tree) < 0)
return 128;
init_tree_desc(&tree_desc, &tree->object.oid, tree->buffer, tree->size);
switch (unpack_trees(1, &tree_desc, &opts)) {
@@ -803,7 +803,8 @@ static int merge_working_tree(const struct checkout_opts *opts,
if (opts->new_orphan_branch && opts->orphan_from_empty_tree) {
if (new_branch_info->commit)
BUG("'switch --orphan' should never accept a commit as starting point");
- new_tree = parse_tree_indirect(the_hash_algo->empty_tree);
+ new_tree = repo_parse_tree_indirect(the_repository,
+ the_hash_algo->empty_tree);
if (!new_tree)
BUG("unable to read empty tree");
} else {
@@ -841,14 +842,15 @@ static int merge_working_tree(const struct checkout_opts *opts,
old_commit_oid = old_branch_info->commit ?
&old_branch_info->commit->object.oid :
the_hash_algo->empty_tree;
- tree = parse_tree_indirect(old_commit_oid);
+ tree = repo_parse_tree_indirect(the_repository,
+ old_commit_oid);
if (!tree)
die(_("unable to parse commit %s"),
oid_to_hex(old_commit_oid));
init_tree_desc(&trees[0], &tree->object.oid,
tree->buffer, tree->size);
- if (parse_tree(new_tree) < 0)
+ if (repo_parse_tree(the_repository, new_tree) < 0)
die(NULL);
tree = new_tree;
init_tree_desc(&trees[1], &tree->object.oid,
@@ -1278,7 +1280,7 @@ static void setup_new_branch_info_and_source_tree(
new_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);
if (!new_branch_info->commit) {
/* not a commit */
- *source_tree = parse_tree_indirect(rev);
+ *source_tree = repo_parse_tree_indirect(the_repository, rev);
if (!*source_tree)
die(_("unable to read tree (%s)"), oid_to_hex(rev));
} else {
@@ -1899,7 +1901,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
struct object_id rev;
if (repo_get_oid_mb(the_repository, opts->from_treeish, &rev))
- die(_("could not resolve %s"), opts->from_treeish);
+ die(_("could not resolve '%s'"), opts->from_treeish);
setup_new_branch_info_and_source_tree(&new_branch_info,
opts, &rev,
diff --git a/builtin/clone.c b/builtin/clone.c
index b19b302b06..b40cee5968 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -680,10 +680,10 @@ static int checkout(int submodule_progress, int filter_submodules,
opts.dst_index = the_repository->index;
init_checkout_metadata(&opts.meta, head, &oid, NULL);
- tree = parse_tree_indirect(&oid);
+ tree = repo_parse_tree_indirect(the_repository, &oid);
if (!tree)
die(_("unable to parse commit %s"), oid_to_hex(&oid));
- if (parse_tree(tree) < 0)
+ if (repo_parse_tree(the_repository, tree) < 0)
exit(128);
init_tree_desc(&t, &tree->object.oid, tree->buffer, tree->size);
if (unpack_trees(1, &t, &opts) < 0)
diff --git a/builtin/commit.c b/builtin/commit.c
index 0243f17d53..8e901fe8db 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -327,10 +327,11 @@ static void create_base_index(const struct commit *current_head)
opts.dst_index = the_repository->index;
opts.fn = oneway_merge;
- tree = parse_tree_indirect(&current_head->object.oid);
+ tree = repo_parse_tree_indirect(the_repository,
+ &current_head->object.oid);
if (!tree)
die(_("failed to unpack HEAD tree object"));
- if (parse_tree(tree) < 0)
+ if (repo_parse_tree(the_repository, tree) < 0)
exit(128);
init_tree_desc(&t, &tree->object.oid, tree->buffer, tree->size);
if (unpack_trees(1, &t, &opts))
diff --git a/builtin/describe.c b/builtin/describe.c
index 443546aaac..989a78d715 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -112,13 +112,13 @@ static int replace_name(struct commit_name *e,
if (!e->tag) {
t = lookup_tag(the_repository, &e->oid);
- if (!t || parse_tag(t))
+ if (!t || parse_tag(the_repository, t))
return 1;
e->tag = t;
}
t = lookup_tag(the_repository, oid);
- if (!t || parse_tag(t))
+ if (!t || parse_tag(the_repository, t))
return 0;
*tag = t;
@@ -335,7 +335,7 @@ static void append_name(struct commit_name *n, struct strbuf *dst)
{
if (n->prio == 2 && !n->tag) {
n->tag = lookup_tag(the_repository, &n->oid);
- if (!n->tag || parse_tag(n->tag))
+ if (!n->tag || parse_tag(the_repository, n->tag))
die(_("annotated tag %s not available"), n->path);
}
if (n->tag && !n->name_checked) {
diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c
index 49dd4d00eb..740d9a791c 100644
--- a/builtin/diff-tree.c
+++ b/builtin/diff-tree.c
@@ -52,7 +52,7 @@ static int stdin_diff_trees(struct tree *tree1, const char *p)
if (!isspace(*p++) || parse_oid_hex(p, &oid, &p) || *p)
return error("Need exactly two trees, separated by a space");
tree2 = lookup_tree(the_repository, &oid);
- if (!tree2 || parse_tree(tree2))
+ if (!tree2 || repo_parse_tree(the_repository, tree2))
return -1;
printf("%s %s\n", oid_to_hex(&tree1->object.oid),
oid_to_hex(&tree2->object.oid));
diff --git a/builtin/fast-import.c b/builtin/fast-import.c
index 7849005ccb..b8a7757cfd 100644
--- a/builtin/fast-import.c
+++ b/builtin/fast-import.c
@@ -900,7 +900,7 @@ static void end_packfile(void)
idx_name = keep_pack(create_index());
/* Register the packfile with core git's machinery. */
- new_p = packfile_store_load_pack(pack_data->repo->objects->packfiles,
+ new_p = packfile_store_load_pack(pack_data->repo->objects->sources->packfiles,
idx_name, 1);
if (!new_p)
die(_("core Git rejected index %s"), idx_name);
@@ -955,7 +955,7 @@ static int store_object(
struct object_id *oidout,
uintmax_t mark)
{
- struct packfile_store *packs = the_repository->objects->packfiles;
+ struct odb_source *source;
void *out, *delta;
struct object_entry *e;
unsigned char hdr[96];
@@ -979,7 +979,11 @@ static int store_object(
if (e->idx.offset) {
duplicate_count_by_type[type]++;
return 1;
- } else if (packfile_list_find_oid(packfile_store_get_packs(packs), &oid)) {
+ }
+
+ for (source = the_repository->objects->sources; source; source = source->next) {
+ if (!packfile_list_find_oid(packfile_store_get_packs(source->packfiles), &oid))
+ continue;
e->type = type;
e->pack_id = MAX_PACK_ID;
e->idx.offset = 1; /* just not zero! */
@@ -1096,10 +1100,10 @@ static void truncate_pack(struct hashfile_checkpoint *checkpoint)
static void stream_blob(uintmax_t len, struct object_id *oidout, uintmax_t mark)
{
- struct packfile_store *packs = the_repository->objects->packfiles;
size_t in_sz = 64 * 1024, out_sz = 64 * 1024;
unsigned char *in_buf = xmalloc(in_sz);
unsigned char *out_buf = xmalloc(out_sz);
+ struct odb_source *source;
struct object_entry *e;
struct object_id oid;
unsigned long hdrlen;
@@ -1179,24 +1183,29 @@ static void stream_blob(uintmax_t len, struct object_id *oidout, uintmax_t mark)
if (e->idx.offset) {
duplicate_count_by_type[OBJ_BLOB]++;
truncate_pack(&checkpoint);
+ goto out;
+ }
- } else if (packfile_list_find_oid(packfile_store_get_packs(packs), &oid)) {
+ for (source = the_repository->objects->sources; source; source = source->next) {
+ if (!packfile_list_find_oid(packfile_store_get_packs(source->packfiles), &oid))
+ continue;
e->type = OBJ_BLOB;
e->pack_id = MAX_PACK_ID;
e->idx.offset = 1; /* just not zero! */
duplicate_count_by_type[OBJ_BLOB]++;
truncate_pack(&checkpoint);
-
- } else {
- e->depth = 0;
- e->type = OBJ_BLOB;
- e->pack_id = pack_id;
- e->idx.offset = offset;
- e->idx.crc32 = crc32_end(pack_file);
- object_count++;
- object_count_by_type[OBJ_BLOB]++;
+ goto out;
}
+ e->depth = 0;
+ e->type = OBJ_BLOB;
+ e->pack_id = pack_id;
+ e->idx.offset = offset;
+ e->idx.crc32 = crc32_end(pack_file);
+ object_count++;
+ object_count_by_type[OBJ_BLOB]++;
+
+out:
free(in_buf);
free(out_buf);
}
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 4979bc795e..0512f78a87 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -51,6 +51,7 @@ static int show_progress = -1;
static int show_dangling = 1;
static int name_objects;
static int check_references = 1;
+static timestamp_t now;
#define ERROR_OBJECT 01
#define ERROR_REACHABLE 02
#define ERROR_PACK 04
@@ -510,6 +511,9 @@ static int fsck_handle_reflog_ent(const char *refname,
timestamp_t timestamp, int tz UNUSED,
const char *message UNUSED, void *cb_data UNUSED)
{
+ if (now && timestamp > now)
+ return 0;
+
if (verbose)
fprintf_ln(stderr, _("Checking reflog %s->%s"),
oid_to_hex(ooid), oid_to_hex(noid));
@@ -531,8 +535,22 @@ static int fsck_handle_reflog(const char *logname, void *cb_data)
return 0;
}
-static int fsck_handle_ref(const struct reference *ref, void *cb_data UNUSED)
+struct ref_snapshot {
+ char *refname;
+ struct object_id oid;
+ /* TODO: Maybe supplement with latest reflog entry info too? */
+};
+
+struct snapshot {
+ size_t nr;
+ size_t alloc;
+ struct ref_snapshot *ref;
+ /* TODO: Consider also snapshotting the index of each worktree. */
+};
+
+static int snapshot_ref(const struct reference *ref, void *cb_data)
{
+ struct snapshot *snap = cb_data;
struct object *obj;
obj = parse_object(the_repository, ref->oid);
@@ -556,6 +574,20 @@ static int fsck_handle_ref(const struct reference *ref, void *cb_data UNUSED)
errors_found |= ERROR_REFS;
}
default_refs++;
+
+ ALLOC_GROW(snap->ref, snap->nr + 1, snap->alloc);
+ snap->ref[snap->nr].refname = xstrdup(ref->name);
+ oidcpy(&snap->ref[snap->nr].oid, ref->oid);
+ snap->nr++;
+
+ return 0;
+}
+
+static int fsck_handle_ref(const struct reference *ref, void *cb_data UNUSED)
+{
+ struct object *obj;
+
+ obj = parse_object(the_repository, ref->oid);
obj->flags |= USED;
fsck_put_object_name(&fsck_walk_options,
ref->oid, "%s", ref->name);
@@ -564,18 +596,35 @@ static int fsck_handle_ref(const struct reference *ref, void *cb_data UNUSED)
return 0;
}
-static int fsck_head_link(const char *head_ref_name,
- const char **head_points_at,
- struct object_id *head_oid);
-
-static void get_default_heads(void)
+static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)
{
struct worktree **worktrees, **p;
const char *head_points_at;
struct object_id head_oid;
+ for (int i = 0; i < argc; i++) {
+ const char *arg = argv[i];
+ struct object_id oid;
+ if (!repo_get_oid(the_repository, arg, &oid)) {
+ struct reference ref = {
+ .name = arg,
+ .oid = &oid,
+ };
+
+ snapshot_ref(&ref, snap);
+ continue;
+ }
+ error(_("invalid parameter: expected sha1, got '%s'"), arg);
+ errors_found |= ERROR_OBJECT;
+ }
+
+ if (argc) {
+ include_reflogs = 0;
+ return;
+ }
+
refs_for_each_rawref(get_main_ref_store(the_repository),
- fsck_handle_ref, NULL);
+ snapshot_ref, snap);
worktrees = get_worktrees();
for (p = worktrees; *p; p++) {
@@ -583,22 +632,62 @@ static void get_default_heads(void)
struct strbuf refname = STRBUF_INIT;
strbuf_worktree_ref(wt, &refname, "HEAD");
- fsck_head_link(refname.buf, &head_points_at, &head_oid);
+
+ head_points_at = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ refname.buf, 0, &head_oid, NULL);
+
if (head_points_at && !is_null_oid(&head_oid)) {
struct reference ref = {
.name = refname.buf,
.oid = &head_oid,
};
- fsck_handle_ref(&ref, NULL);
+ snapshot_ref(&ref, snap);
}
strbuf_release(&refname);
- if (include_reflogs)
+ /*
+ * TODO: Could use refs_for_each_reflog(...) to find
+ * latest entry instead of using a global 'now' for that
+ * purpose.
+ */
+ }
+ free_worktrees(worktrees);
+
+ /* Ignore reflogs newer than now */
+ now = time(NULL);
+}
+
+
+static void free_snapshot_refs(struct snapshot *snap)
+{
+ for (size_t i = 0; i < snap->nr; i++)
+ free(snap->ref[i].refname);
+ free(snap->ref);
+}
+
+static void process_refs(struct snapshot *snap)
+{
+ struct worktree **worktrees, **p;
+
+ for (size_t i = 0; i < snap->nr; i++) {
+ struct reference ref = {
+ .name = snap->ref[i].refname,
+ .oid = &snap->ref[i].oid,
+ };
+ fsck_handle_ref(&ref, NULL);
+ }
+
+ if (include_reflogs) {
+ worktrees = get_worktrees();
+ for (p = worktrees; *p; p++) {
+ struct worktree *wt = *p;
+
refs_for_each_reflog(get_worktree_ref_store(wt),
fsck_handle_reflog, wt);
+ }
+ free_worktrees(worktrees);
}
- free_worktrees(worktrees);
/*
* Not having any default heads isn't really fatal, but
@@ -713,43 +802,6 @@ static void fsck_source(struct odb_source *source)
stop_progress(&progress);
}
-static int fsck_head_link(const char *head_ref_name,
- const char **head_points_at,
- struct object_id *head_oid)
-{
- int null_is_error = 0;
-
- if (verbose)
- fprintf_ln(stderr, _("Checking %s link"), head_ref_name);
-
- *head_points_at = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
- head_ref_name, 0, head_oid,
- NULL);
- if (!*head_points_at) {
- errors_found |= ERROR_REFS;
- return error(_("invalid %s"), head_ref_name);
- }
- if (!strcmp(*head_points_at, head_ref_name))
- /* detached HEAD */
- null_is_error = 1;
- else if (!starts_with(*head_points_at, "refs/heads/")) {
- errors_found |= ERROR_REFS;
- return error(_("%s points to something strange (%s)"),
- head_ref_name, *head_points_at);
- }
- if (is_null_oid(head_oid)) {
- if (null_is_error) {
- errors_found |= ERROR_REFS;
- return error(_("%s: detached HEAD points at nothing"),
- head_ref_name);
- }
- fprintf_ln(stderr,
- _("notice: %s points to an unborn branch (%s)"),
- head_ref_name, *head_points_at + 11);
- }
- return 0;
-}
-
static int fsck_cache_tree(struct cache_tree *it, const char *index_path)
{
int i;
@@ -963,8 +1015,12 @@ int cmd_fsck(int argc,
const char *prefix,
struct repository *repo UNUSED)
{
- int i;
struct odb_source *source;
+ struct snapshot snap = {
+ .nr = 0,
+ .alloc = 0,
+ .ref = NULL
+ };
/* fsck knows how to handle missing promisor objects */
fetch_if_missing = 0;
@@ -1000,6 +1056,17 @@ int cmd_fsck(int argc,
if (check_references)
fsck_refs(the_repository);
+ /*
+ * Take a snapshot of the refs before walking objects to avoid looking
+ * at a set of refs that may be changed by the user while we are walking
+ * objects. We can still walk over new objects that are added during the
+ * execution of fsck but won't miss any objects that were reachable.
+ */
+ snapshot_refs(&snap, argc, argv);
+
+ /* Ensure we get a "fresh" view of the odb */
+ odb_reprepare(the_repository->objects);
+
if (connectivity_only) {
for_each_loose_object(the_repository->objects,
mark_loose_for_connectivity, NULL, 0);
@@ -1041,42 +1108,18 @@ int cmd_fsck(int argc,
errors_found |= ERROR_OBJECT;
}
- for (i = 0; i < argc; i++) {
- const char *arg = argv[i];
- struct object_id oid;
- if (!repo_get_oid(the_repository, arg, &oid)) {
- struct object *obj = lookup_object(the_repository,
- &oid);
-
- if (!obj || !(obj->flags & HAS_OBJ)) {
- if (is_promisor_object(the_repository, &oid))
- continue;
- error(_("%s: object missing"), oid_to_hex(&oid));
- errors_found |= ERROR_OBJECT;
- continue;
- }
-
- obj->flags |= USED;
- fsck_put_object_name(&fsck_walk_options, &oid,
- "%s", arg);
- mark_object_reachable(obj);
- continue;
- }
- error(_("invalid parameter: expected sha1, got '%s'"), arg);
- errors_found |= ERROR_OBJECT;
- }
+ /* Process the snapshotted refs and the reflogs. */
+ process_refs(&snap);
- /*
- * If we've not been given any explicit head information, do the
- * default ones from .git/refs. We also consider the index file
- * in this case (ie this implies --cache).
- */
- if (!argc) {
- get_default_heads();
+ /* If not given any explicit objects, process index files too. */
+ if (!argc)
keep_cache_objects = 1;
- }
-
if (keep_cache_objects) {
+ /*
+ * TODO: Consider first walking these indexes in snapshot_refs,
+ * to snapshot where the index entries used to point, and then
+ * check those snapshotted locations here.
+ */
struct worktree **worktrees, **p;
verify_index_checksum = 1;
@@ -1149,5 +1192,6 @@ int cmd_fsck(int argc,
}
}
+ free_snapshot_refs(&snap);
return errors_found;
}
diff --git a/builtin/gc.c b/builtin/gc.c
index 92c6e7b954..17ff68cbd9 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -1130,8 +1130,10 @@ static int dfs_on_ref(const struct reference *ref, void *cb_data)
return 0;
commit = lookup_commit(the_repository, maybe_peeled);
- if (!commit)
+ if (!commit || commit->object.flags & SEEN)
return 0;
+ commit->object.flags |= SEEN;
+
if (repo_parse_commit(the_repository, commit) ||
commit_graph_position(commit) != COMMIT_NOT_FROM_GRAPH)
return 0;
@@ -1141,7 +1143,7 @@ static int dfs_on_ref(const struct reference *ref, void *cb_data)
if (data->num_not_in_graph >= data->limit)
return 1;
- commit_list_append(commit, &stack);
+ commit_list_insert(commit, &stack);
while (!result && stack) {
struct commit_list *parent;
@@ -1162,7 +1164,7 @@ static int dfs_on_ref(const struct reference *ref, void *cb_data)
break;
}
- commit_list_append(parent->item, &stack);
+ commit_list_insert(parent->item, &stack);
}
}
diff --git a/builtin/grep.c b/builtin/grep.c
index 53cccf2d25..5b8b87b1ac 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -1213,8 +1213,14 @@ int cmd_grep(int argc,
*/
if (recurse_submodules)
repo_read_gitmodules(the_repository, 1);
- if (startup_info->have_repository)
- packfile_store_prepare(the_repository->objects->packfiles);
+
+ if (startup_info->have_repository) {
+ struct odb_source *source;
+
+ odb_prepare_alternates(the_repository->objects);
+ for (source = the_repository->objects->sources; source; source = source->next)
+ packfile_store_prepare(source->packfiles);
+ }
start_threads(&opt);
} else {
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index a7e901e49c..b67fb0256c 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -1638,7 +1638,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
hash, "idx", 1);
if (do_fsck_object && startup_info->have_repository)
- packfile_store_load_pack(the_repository->objects->packfiles,
+ packfile_store_load_pack(the_repository->objects->sources->packfiles,
final_index_name, 0);
if (!from_stdin) {
diff --git a/builtin/log.c b/builtin/log.c
index d4cf9c59c8..5c9a8ef363 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1896,11 +1896,11 @@ int cmd_format_patch(int argc,
{
struct format_config cfg;
struct commit *commit;
- struct commit **list = NULL;
+ struct commit_stack list = COMMIT_STACK_INIT;
struct rev_info rev;
char *to_free = NULL;
struct setup_revision_opt s_r_opt;
- size_t nr = 0, total, i;
+ size_t total, i;
int use_stdout = 0;
int start_number = -1;
int just_numbers = 0;
@@ -2283,14 +2283,12 @@ int cmd_format_patch(int argc,
if (ignore_if_in_upstream && has_commit_patch_id(commit, &ids))
continue;
- nr++;
- REALLOC_ARRAY(list, nr);
- list[nr - 1] = commit;
+ commit_stack_push(&list, commit);
}
- if (nr == 0)
+ if (!list.nr)
/* nothing to do */
goto done;
- total = nr;
+ total = list.nr;
if (cover_letter == -1) {
if (cfg.config_cover_letter == COVER_AUTO)
cover_letter = (total > 1);
@@ -2308,7 +2306,7 @@ int cmd_format_patch(int argc,
if (!cover_letter && total != 1)
die(_("--interdiff requires --cover-letter or single patch"));
rev.idiff_oid1 = &idiff_prev.oid[idiff_prev.nr - 1];
- rev.idiff_oid2 = get_commit_tree_oid(list[0]);
+ rev.idiff_oid2 = get_commit_tree_oid(list.items[0]);
rev.idiff_title = diff_title(&idiff_title, reroll_count,
_("Interdiff:"),
_("Interdiff against v%d:"));
@@ -2324,7 +2322,7 @@ int cmd_format_patch(int argc,
die(_("--range-diff requires --cover-letter or single patch"));
infer_range_diff_ranges(&rdiff1, &rdiff2, rdiff_prev,
- origin, list[0]);
+ origin, list.items[0]);
rev.rdiff1 = rdiff1.buf;
rev.rdiff2 = rdiff2.buf;
rev.creation_factor = creation_factor;
@@ -2360,11 +2358,11 @@ int cmd_format_patch(int argc,
}
memset(&bases, 0, sizeof(bases));
- base = get_base_commit(&cfg, list, nr);
+ base = get_base_commit(&cfg, list.items, list.nr);
if (base) {
reset_revision_walk();
clear_object_flags(the_repository, UNINTERESTING);
- prepare_bases(&bases, base, list, nr);
+ prepare_bases(&bases, base, list.items, list.nr);
}
if (in_reply_to || cfg.thread || cover_letter) {
@@ -2381,7 +2379,8 @@ int cmd_format_patch(int argc,
if (cfg.thread)
gen_message_id(&rev, "cover");
make_cover_letter(&rev, !!output_directory,
- origin, nr, list, description_file, branch_name, quiet, &cfg);
+ origin, list.nr, list.items,
+ description_file, branch_name, quiet, &cfg);
print_bases(&bases, rev.diffopt.file);
print_signature(signature, rev.diffopt.file);
total++;
@@ -2395,12 +2394,12 @@ int cmd_format_patch(int argc,
if (show_progress)
progress = start_delayed_progress(the_repository,
_("Generating patches"), total);
- for (i = 0; i < nr; i++) {
- size_t idx = nr - i - 1;
+ while (list.nr) {
+ size_t idx = list.nr - 1;
int shown;
display_progress(progress, total - idx);
- commit = list[idx];
+ commit = commit_stack_pop(&list);
rev.nr = total - idx + (start_number - 1);
/* Make the second and subsequent mails replies to the first */
@@ -2469,7 +2468,7 @@ int cmd_format_patch(int argc,
}
}
stop_progress(&progress);
- free(list);
+ commit_stack_clear(&list);
if (ignore_if_in_upstream)
free_patch_ids(&ids);
diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c
index ec6940fc7c..113e4a960d 100644
--- a/builtin/ls-tree.c
+++ b/builtin/ls-tree.c
@@ -421,7 +421,7 @@ int cmd_ls_tree(int argc,
for (i = 0; i < options.pathspec.nr; i++)
options.pathspec.items[i].nowildcard_len = options.pathspec.items[i].len;
options.pathspec.has_wildcard = 0;
- tree = parse_tree_indirect(&oid);
+ tree = repo_parse_tree_indirect(the_repository, &oid);
if (!tree)
die("not a tree object");
/*
diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c
index 1c063d9a41..a6e6d5b555 100644
--- a/builtin/merge-tree.c
+++ b/builtin/merge-tree.c
@@ -447,17 +447,20 @@ static int real_merge(struct merge_tree_options *o,
if (repo_get_oid_treeish(the_repository, merge_base, &base_oid))
die(_("could not parse as tree '%s'"), merge_base);
- base_tree = parse_tree_indirect(&base_oid);
+ base_tree = repo_parse_tree_indirect(the_repository,
+ &base_oid);
if (!base_tree)
die(_("unable to read tree (%s)"), oid_to_hex(&base_oid));
if (repo_get_oid_treeish(the_repository, branch1, &head_oid))
die(_("could not parse as tree '%s'"), branch1);
- parent1_tree = parse_tree_indirect(&head_oid);
+ parent1_tree = repo_parse_tree_indirect(the_repository,
+ &head_oid);
if (!parent1_tree)
die(_("unable to read tree (%s)"), oid_to_hex(&head_oid));
if (repo_get_oid_treeish(the_repository, branch2, &merge_oid))
die(_("could not parse as tree '%s'"), branch2);
- parent2_tree = parse_tree_indirect(&merge_oid);
+ parent2_tree = repo_parse_tree_indirect(the_repository,
+ &merge_oid);
if (!parent2_tree)
die(_("unable to read tree (%s)"), oid_to_hex(&merge_oid));
diff --git a/builtin/merge.c b/builtin/merge.c
index c421a11b0b..50001b4c59 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -756,19 +756,19 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head,
opts.trivial_merges_only = 1;
opts.merge = 1;
opts.preserve_ignored = 0; /* FIXME: !overwrite_ignore */
- trees[nr_trees] = parse_tree_indirect(common);
+ trees[nr_trees] = repo_parse_tree_indirect(the_repository, common);
if (!trees[nr_trees++])
return -1;
- trees[nr_trees] = parse_tree_indirect(head);
+ trees[nr_trees] = repo_parse_tree_indirect(the_repository, head);
if (!trees[nr_trees++])
return -1;
- trees[nr_trees] = parse_tree_indirect(one);
+ trees[nr_trees] = repo_parse_tree_indirect(the_repository, one);
if (!trees[nr_trees++])
return -1;
opts.fn = threeway_merge;
cache_tree_free(&the_repository->index->cache_tree);
for (i = 0; i < nr_trees; i++) {
- parse_tree(trees[i]);
+ repo_parse_tree(the_repository, trees[i]);
init_tree_desc(t+i, &trees[i]->object.oid,
trees[i]->buffer, trees[i]->size);
}
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 615f7d1aae..6188cf98ce 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -180,8 +180,7 @@ static void name_rev(struct commit *start_commit,
{
struct prio_queue queue;
struct commit *commit;
- struct commit **parents_to_queue = NULL;
- size_t parents_to_queue_nr, parents_to_queue_alloc = 0;
+ struct commit_stack parents_to_queue = COMMIT_STACK_INIT;
struct rev_name *start_name;
repo_parse_commit(the_repository, start_commit);
@@ -206,7 +205,7 @@ static void name_rev(struct commit *start_commit,
struct commit_list *parents;
int parent_number = 1;
- parents_to_queue_nr = 0;
+ parents_to_queue.nr = 0;
for (parents = commit->parents;
parents;
@@ -238,22 +237,18 @@ static void name_rev(struct commit *start_commit,
string_pool);
else
parent_name->tip_name = name->tip_name;
- ALLOC_GROW(parents_to_queue,
- parents_to_queue_nr + 1,
- parents_to_queue_alloc);
- parents_to_queue[parents_to_queue_nr] = parent;
- parents_to_queue_nr++;
+ commit_stack_push(&parents_to_queue, parent);
}
}
/* The first parent must come out first from the prio_queue */
- while (parents_to_queue_nr)
+ while (parents_to_queue.nr)
prio_queue_put(&queue,
- parents_to_queue[--parents_to_queue_nr]);
+ commit_stack_pop(&parents_to_queue));
}
clear_prio_queue(&queue);
- free(parents_to_queue);
+ commit_stack_clear(&parents_to_queue);
}
static int subpath_matches(const char *path, const char *filter)
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 1ce8d6ee21..5846b6a293 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -1529,49 +1529,53 @@ static int want_cruft_object_mtime(struct repository *r,
const struct object_id *oid,
unsigned flags, uint32_t mtime)
{
- struct packed_git **cache;
+ struct odb_source *source;
- for (cache = kept_pack_cache(r, flags); *cache; cache++) {
- struct packed_git *p = *cache;
- off_t ofs;
- uint32_t candidate_mtime;
+ for (source = r->objects->sources; source; source = source->next) {
+ struct packed_git **cache = packfile_store_get_kept_pack_cache(source->packfiles, flags);
- ofs = find_pack_entry_one(oid, p);
- if (!ofs)
- continue;
-
- /*
- * We have a copy of the object 'oid' in a non-cruft
- * pack. We can avoid packing an additional copy
- * regardless of what the existing copy's mtime is since
- * it is outside of a cruft pack.
- */
- if (!p->is_cruft)
- return 0;
+ for (; *cache; cache++) {
+ struct packed_git *p = *cache;
+ off_t ofs;
+ uint32_t candidate_mtime;
- /*
- * If we have a copy of the object 'oid' in a cruft
- * pack, then either read the cruft pack's mtime for
- * that object, or, if that can't be loaded, assume the
- * pack's mtime itself.
- */
- if (!load_pack_mtimes(p)) {
- uint32_t pos;
- if (offset_to_pack_pos(p, ofs, &pos) < 0)
+ ofs = find_pack_entry_one(oid, p);
+ if (!ofs)
continue;
- candidate_mtime = nth_packed_mtime(p, pos);
- } else {
- candidate_mtime = p->mtime;
- }
- /*
- * We have a surviving copy of the object in a cruft
- * pack whose mtime is greater than or equal to the one
- * we are considering. We can thus avoid packing an
- * additional copy of that object.
- */
- if (mtime <= candidate_mtime)
- return 0;
+ /*
+ * We have a copy of the object 'oid' in a non-cruft
+ * pack. We can avoid packing an additional copy
+ * regardless of what the existing copy's mtime is since
+ * it is outside of a cruft pack.
+ */
+ if (!p->is_cruft)
+ return 0;
+
+ /*
+ * If we have a copy of the object 'oid' in a cruft
+ * pack, then either read the cruft pack's mtime for
+ * that object, or, if that can't be loaded, assume the
+ * pack's mtime itself.
+ */
+ if (!load_pack_mtimes(p)) {
+ uint32_t pos;
+ if (offset_to_pack_pos(p, ofs, &pos) < 0)
+ continue;
+ candidate_mtime = nth_packed_mtime(p, pos);
+ } else {
+ candidate_mtime = p->mtime;
+ }
+
+ /*
+ * We have a surviving copy of the object in a cruft
+ * pack whose mtime is greater than or equal to the one
+ * we are considering. We can thus avoid packing an
+ * additional copy of that object.
+ */
+ if (mtime <= candidate_mtime)
+ return 0;
+ }
}
return -1;
@@ -1624,9 +1628,9 @@ static int want_found_object(const struct object_id *oid, int exclude,
*/
unsigned flags = 0;
if (ignore_packed_keep_on_disk)
- flags |= ON_DISK_KEEP_PACKS;
+ flags |= KEPT_PACK_ON_DISK;
if (ignore_packed_keep_in_core)
- flags |= IN_CORE_KEEP_PACKS;
+ flags |= KEPT_PACK_IN_CORE;
/*
* If the object is in a pack that we want to ignore, *and* we
@@ -1749,13 +1753,15 @@ static int want_object_in_pack_mtime(const struct object_id *oid,
}
}
- for (e = the_repository->objects->packfiles->packs.head; e; e = e->next) {
- struct packed_git *p = e->pack;
- want = want_object_in_pack_one(p, oid, exclude, found_pack, found_offset, found_mtime);
- if (!exclude && want > 0)
- packfile_list_prepend(&the_repository->objects->packfiles->packs, p);
- if (want != -1)
- return want;
+ for (source = the_repository->objects->sources; source; source = source->next) {
+ for (e = source->packfiles->packs.head; e; e = e->next) {
+ struct packed_git *p = e->pack;
+ want = want_object_in_pack_one(p, oid, exclude, found_pack, found_offset, found_mtime);
+ if (!exclude && want > 0)
+ packfile_list_prepend(&source->packfiles->packs, p);
+ if (want != -1)
+ return want;
+ }
}
if (uri_protocols.nr) {
@@ -2411,7 +2417,7 @@ static void drop_reused_delta(struct object_entry *entry)
oi.sizep = &size;
oi.typep = &type;
- if (packed_object_info(the_repository, IN_PACK(entry), entry->in_pack_offset, &oi) < 0) {
+ if (packed_object_info(IN_PACK(entry), entry->in_pack_offset, &oi) < 0) {
/*
* We failed to get the info from this pack for some reason;
* fall back to odb_read_object_info, which may find another copy.
@@ -3293,7 +3299,7 @@ static void add_tag_chain(const struct object_id *oid)
tag = lookup_tag(the_repository, oid);
while (1) {
- if (!tag || parse_tag(tag) || !tag->tagged)
+ if (!tag || parse_tag(the_repository, tag) || !tag->tagged)
die(_("unable to pack objects reachable from tag %s"),
oid_to_hex(oid));
@@ -3748,7 +3754,7 @@ static int add_object_entry_from_pack(const struct object_id *oid,
struct object_info oi = OBJECT_INFO_INIT;
oi.typep = &type;
- if (packed_object_info(the_repository, p, ofs, &oi) < 0) {
+ if (packed_object_info(p, ofs, &oi) < 0) {
die(_("could not get type of object %s in pack %s"),
oid_to_hex(oid), p->pack_name);
} else if (type == OBJ_COMMIT) {
@@ -3857,8 +3863,11 @@ static void read_packs_list_from_stdin(struct rev_info *revs)
repo_for_each_pack(the_repository, p) {
const char *pack_name = pack_basename(p);
- if ((item = string_list_lookup(&include_packs, pack_name)))
+ if ((item = string_list_lookup(&include_packs, pack_name))) {
+ if (exclude_promisor_objects && p->pack_promisor)
+ die(_("packfile %s is a promisor but --exclude-promisor-objects was given"), p->pack_name);
item->util = p;
+ }
if ((item = string_list_lookup(&exclude_packs, pack_name)))
item->util = p;
}
@@ -3931,11 +3940,12 @@ static void read_stdin_packs(enum stdin_packs_mode mode, int rev_list_unpacked)
* an optimization during delta selection.
*/
revs.no_kept_objects = 1;
- revs.keep_pack_cache_flags |= IN_CORE_KEEP_PACKS;
+ revs.keep_pack_cache_flags |= KEPT_PACK_IN_CORE;
revs.blob_objects = 1;
revs.tree_objects = 1;
revs.tag_objects = 1;
revs.ignore_missing_links = 1;
+ revs.exclude_promisor_objects = exclude_promisor_objects;
/* avoids adding objects in excluded packs */
ignore_packed_keep_in_core = 1;
@@ -4030,7 +4040,7 @@ static void show_cruft_commit(struct commit *commit, void *data)
static int cruft_include_check_obj(struct object *obj, void *data UNUSED)
{
- return !has_object_kept_pack(to_pack.repo, &obj->oid, IN_CORE_KEEP_PACKS);
+ return !has_object_kept_pack(to_pack.repo, &obj->oid, KEPT_PACK_IN_CORE);
}
static int cruft_include_check(struct commit *commit, void *data)
@@ -5092,9 +5102,13 @@ int cmd_pack_objects(int argc,
exclude_promisor_objects_best_effort,
"--exclude-promisor-objects-best-effort");
if (exclude_promisor_objects) {
- use_internal_rev_list = 1;
fetch_if_missing = 0;
- strvec_push(&rp, "--exclude-promisor-objects");
+
+ /* --stdin-packs handles promisor objects separately. */
+ if (!stdin_packs) {
+ use_internal_rev_list = 1;
+ strvec_push(&rp, "--exclude-promisor-objects");
+ }
} else if (exclude_promisor_objects_best_effort) {
use_internal_rev_list = 1;
fetch_if_missing = 0;
diff --git a/builtin/patch-id.c b/builtin/patch-id.c
index d26e9d0c1e..2781598ede 100644
--- a/builtin/patch-id.c
+++ b/builtin/patch-id.c
@@ -228,9 +228,9 @@ int cmd_patch_id(int argc,
int opts = 0;
struct option builtin_patch_id_options[] = {
OPT_CMDMODE(0, "unstable", &opts,
- N_("use the unstable patch-id algorithm"), 1),
+ N_("use the unstable patch ID algorithm"), 1),
OPT_CMDMODE(0, "stable", &opts,
- N_("use the stable patch-id algorithm"), 2),
+ N_("use the stable patch ID algorithm"), 2),
OPT_CMDMODE(0, "verbatim", &opts,
N_("don't strip whitespace from the patch"), 3),
OPT_END()
diff --git a/builtin/read-tree.c b/builtin/read-tree.c
index 34f7a59f38..460b21e40a 100644
--- a/builtin/read-tree.c
+++ b/builtin/read-tree.c
@@ -32,7 +32,7 @@ static int list_tree(struct object_id *oid)
if (nr_trees >= MAX_UNPACK_TREES)
die("I cannot read more than %d trees", MAX_UNPACK_TREES);
- tree = parse_tree_indirect(oid);
+ tree = repo_parse_tree_indirect(the_repository, oid);
if (!tree)
return -1;
trees[nr_trees++] = tree;
@@ -268,7 +268,7 @@ int cmd_read_tree(int argc,
cache_tree_free(&the_repository->index->cache_tree);
for (i = 0; i < nr_trees; i++) {
struct tree *tree = trees[i];
- if (parse_tree(tree) < 0)
+ if (repo_parse_tree(the_repository, tree) < 0)
return 128;
init_tree_desc(t+i, &tree->object.oid, tree->buffer, tree->size);
}
diff --git a/builtin/repack.c b/builtin/repack.c
index d9012141f6..f6bb04bef7 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -332,6 +332,9 @@ int cmd_repack(int argc,
!(pack_everything & PACK_CRUFT))
strvec_push(&cmd.args, "--pack-loose-unreachable");
} else if (geometry.split_factor) {
+ pack_geometry_repack_promisors(repo, &po_args, &geometry,
+ &names, packtmp);
+
if (midx_must_contain_cruft)
strvec_push(&cmd.args, "--stdin-packs");
else
diff --git a/builtin/replay.c b/builtin/replay.c
index 69c4c55129..1960bbbee8 100644
--- a/builtin/replay.c
+++ b/builtin/replay.c
@@ -33,14 +33,16 @@ static const char *short_commit_name(struct repository *repo,
DEFAULT_ABBREV);
}
-static struct commit *peel_committish(struct repository *repo, const char *name)
+static struct commit *peel_committish(struct repository *repo,
+ const char *name,
+ const char *mode)
{
struct object *obj;
struct object_id oid;
if (repo_get_oid(repo, name, &oid))
- return NULL;
- obj = parse_object(repo, &oid);
+ die(_("'%s' is not a valid commit-ish for %s"), name, mode);
+ obj = parse_object_or_die(repo, &oid, name);
return (struct commit *)repo_peel_to_type(repo, name, 0, obj,
OBJ_COMMIT);
}
@@ -162,12 +164,12 @@ static void get_ref_information(struct repository *repo,
}
}
-static void determine_replay_mode(struct repository *repo,
- struct rev_cmdline_info *cmd_info,
- const char *onto_name,
- char **advance_name,
- struct commit **onto,
- struct strset **update_refs)
+static void set_up_replay_mode(struct repository *repo,
+ struct rev_cmdline_info *cmd_info,
+ const char *onto_name,
+ char **advance_name,
+ struct commit **onto,
+ struct strset **update_refs)
{
struct ref_info rinfo;
@@ -178,15 +180,20 @@ static void determine_replay_mode(struct repository *repo,
die_for_incompatible_opt2(!!onto_name, "--onto",
!!*advance_name, "--advance");
if (onto_name) {
- *onto = peel_committish(repo, onto_name);
+ *onto = peel_committish(repo, onto_name, "--onto");
if (rinfo.positive_refexprs <
strset_get_size(&rinfo.positive_refs))
die(_("all positive revisions given must be references"));
- } else if (*advance_name) {
+ *update_refs = xcalloc(1, sizeof(**update_refs));
+ **update_refs = rinfo.positive_refs;
+ memset(&rinfo.positive_refs, 0, sizeof(**update_refs));
+ } else {
struct object_id oid;
char *fullname = NULL;
- *onto = peel_committish(repo, *advance_name);
+ if (!*advance_name)
+ BUG("expected either onto_name or *advance_name in this function");
+
if (repo_dwim_ref(repo, *advance_name, strlen(*advance_name),
&oid, &fullname, 0) == 1) {
free(*advance_name);
@@ -194,53 +201,9 @@ static void determine_replay_mode(struct repository *repo,
} else {
die(_("argument to --advance must be a reference"));
}
+ *onto = peel_committish(repo, *advance_name, "--advance");
if (rinfo.positive_refexprs > 1)
die(_("cannot advance target with multiple sources because ordering would be ill-defined"));
- } else {
- int positive_refs_complete = (
- rinfo.positive_refexprs ==
- strset_get_size(&rinfo.positive_refs));
- int negative_refs_complete = (
- rinfo.negative_refexprs ==
- strset_get_size(&rinfo.negative_refs));
- /*
- * We need either positive_refs_complete or
- * negative_refs_complete, but not both.
- */
- if (rinfo.negative_refexprs > 0 &&
- positive_refs_complete == negative_refs_complete)
- die(_("cannot implicitly determine whether this is an --advance or --onto operation"));
- if (negative_refs_complete) {
- struct hashmap_iter iter;
- struct strmap_entry *entry;
- const char *last_key = NULL;
-
- if (rinfo.negative_refexprs == 0)
- die(_("all positive revisions given must be references"));
- else if (rinfo.negative_refexprs > 1)
- die(_("cannot implicitly determine whether this is an --advance or --onto operation"));
- else if (rinfo.positive_refexprs > 1)
- die(_("cannot advance target with multiple source branches because ordering would be ill-defined"));
-
- /* Only one entry, but we have to loop to get it */
- strset_for_each_entry(&rinfo.negative_refs,
- &iter, entry) {
- last_key = entry->key;
- }
-
- free(*advance_name);
- *advance_name = xstrdup_or_null(last_key);
- } else { /* positive_refs_complete */
- if (rinfo.negative_refexprs > 1)
- die(_("cannot implicitly determine correct base for --onto"));
- if (rinfo.negative_refexprs == 1)
- *onto = rinfo.onto;
- }
- }
- if (!*advance_name) {
- *update_refs = xcalloc(1, sizeof(**update_refs));
- **update_refs = rinfo.positive_refs;
- memset(&rinfo.positive_refs, 0, sizeof(**update_refs));
}
strset_clear(&rinfo.negative_refs);
strset_clear(&rinfo.positive_refs);
@@ -451,11 +414,11 @@ int cmd_replay(int argc,
revs.simplify_history = 0;
}
- determine_replay_mode(repo, &revs.cmdline, onto_name, &advance_name,
- &onto, &update_refs);
+ set_up_replay_mode(repo, &revs.cmdline,
+ onto_name, &advance_name,
+ &onto, &update_refs);
- if (!onto) /* FIXME: Should handle replaying down to root commit */
- die("Replaying down to root commit is not supported yet!");
+ /* FIXME: Should allow replaying commits with the first as a root commit */
/* Build reflog message */
if (advance_name_opt)
@@ -491,7 +454,7 @@ int cmd_replay(int argc,
int hr;
if (!commit->parents)
- die(_("replaying down to root commit is not supported yet!"));
+ die(_("replaying down from root commit is not supported yet!"));
if (commit->parents->next)
die(_("replaying merge commits is not supported yet!"));
diff --git a/builtin/reset.c b/builtin/reset.c
index ed35802af1..c48d9845f8 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -118,7 +118,7 @@ static int reset_index(const char *ref, const struct object_id *oid, int reset_t
goto out;
if (reset_type == MIXED || reset_type == HARD) {
- tree = parse_tree_indirect(oid);
+ tree = repo_parse_tree_indirect(the_repository, oid);
if (!tree) {
error(_("unable to read tree (%s)"), oid_to_hex(oid));
goto out;
@@ -417,7 +417,7 @@ int cmd_reset(int argc,
struct tree *tree;
if (repo_get_oid_treeish(the_repository, rev, &oid))
die(_("Failed to resolve '%s' as a valid tree."), rev);
- tree = parse_tree_indirect(&oid);
+ tree = repo_parse_tree_indirect(the_repository, &oid);
if (!tree)
die(_("Could not parse object '%s'."), rev);
oidcpy(&oid, &tree->object.oid);
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 10475a6b5e..f3ebc1d4ea 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -18,6 +18,7 @@
#include "commit-slab.h"
#include "date.h"
#include "wildmatch.h"
+#include "prio-queue.h"
static const char*const show_branch_usage[] = {
N_("git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
@@ -59,11 +60,10 @@ static const char *get_color_reset_code(void)
return "";
}
-static struct commit *interesting(struct commit_list *list)
+static struct commit *interesting(struct prio_queue *queue)
{
- while (list) {
- struct commit *commit = list->item;
- list = list->next;
+ for (size_t i = 0; i < queue->nr; i++) {
+ struct commit *commit = queue->array[i].data;
if (commit->object.flags & UNINTERESTING)
continue;
return commit;
@@ -222,17 +222,18 @@ static int mark_seen(struct commit *commit, struct commit_list **seen_p)
return 0;
}
-static void join_revs(struct commit_list **list_p,
+static void join_revs(struct prio_queue *queue,
struct commit_list **seen_p,
int num_rev, int extra)
{
int all_mask = ((1u << (REV_SHIFT + num_rev)) - 1);
int all_revs = all_mask & ~((1u << REV_SHIFT) - 1);
- while (*list_p) {
+ while (queue->nr) {
struct commit_list *parents;
- int still_interesting = !!interesting(*list_p);
- struct commit *commit = pop_commit(list_p);
+ int still_interesting = !!interesting(queue);
+ struct commit *commit = prio_queue_peek(queue);
+ bool get_pending = true;
int flags = commit->object.flags & all_mask;
if (!still_interesting && extra <= 0)
@@ -253,8 +254,14 @@ static void join_revs(struct commit_list **list_p,
if (mark_seen(p, seen_p) && !still_interesting)
extra--;
p->object.flags |= flags;
- commit_list_insert_by_date(p, list_p);
+ if (get_pending)
+ prio_queue_replace(queue, p);
+ else
+ prio_queue_put(queue, p);
+ get_pending = false;
}
+ if (get_pending)
+ prio_queue_get(queue);
}
/*
@@ -639,7 +646,8 @@ int cmd_show_branch(int ac,
{
struct commit *rev[MAX_REVS], *commit;
char *reflog_msg[MAX_REVS] = {0};
- struct commit_list *list = NULL, *seen = NULL;
+ struct commit_list *seen = NULL;
+ struct prio_queue queue = { compare_commits_by_commit_date };
unsigned int rev_mask[MAX_REVS];
int num_rev, i, extra = 0;
int all_heads = 0, all_remotes = 0;
@@ -883,14 +891,14 @@ int cmd_show_branch(int ac,
*/
commit->object.flags |= flag;
if (commit->object.flags == flag)
- commit_list_insert_by_date(commit, &list);
+ prio_queue_put(&queue, commit);
rev[num_rev] = commit;
}
for (i = 0; i < num_rev; i++)
rev_mask[i] = rev[i]->object.flags;
if (0 <= extra)
- join_revs(&list, &seen, num_rev, extra);
+ join_revs(&queue, &seen, num_rev, extra);
commit_list_sort_by_date(&seen);
@@ -1001,7 +1009,7 @@ out:
for (size_t i = 0; i < ARRAY_SIZE(reflog_msg); i++)
free(reflog_msg[i]);
free_commit_list(seen);
- free_commit_list(list);
+ clear_prio_queue(&queue);
free(args_copy);
free(head);
return ret;
diff --git a/builtin/stash.c b/builtin/stash.c
index 948eba06fb..193e3ea47a 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -347,8 +347,8 @@ static int reset_tree(struct object_id *i_tree, int update, int reset)
memset(&opts, 0, sizeof(opts));
- tree = parse_tree_indirect(i_tree);
- if (parse_tree(tree))
+ tree = repo_parse_tree_indirect(the_repository, i_tree);
+ if (repo_parse_tree(the_repository, tree))
return -1;
init_tree_desc(t, &tree->object.oid, tree->buffer, tree->size);
@@ -940,8 +940,8 @@ static void diff_include_untracked(const struct stash_info *info, struct diff_op
struct unpack_trees_options unpack_tree_opt = { 0 };
for (size_t i = 0; i < ARRAY_SIZE(oid); i++) {
- tree[i] = parse_tree_indirect(oid[i]);
- if (parse_tree(tree[i]) < 0)
+ tree[i] = repo_parse_tree_indirect(the_repository, oid[i]);
+ if (repo_parse_tree(the_repository, tree[i]) < 0)
die(_("failed to parse tree"));
init_tree_desc(&tree_desc[i], &tree[i]->object.oid,
tree[i]->buffer, tree[i]->size);
diff --git a/builtin/tag.c b/builtin/tag.c
index 01eba90c5c..aeb04c487f 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -149,7 +149,7 @@ static int verify_tag(const char *name, const char *ref UNUSED,
if (format->format)
flags = GPG_VERIFY_OMIT_STATUS;
- if (gpg_verify_tag(oid, name, flags))
+ if (gpg_verify_tag(the_repository, oid, name, flags))
return -1;
if (format->format)
diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c
index 558121eaa1..4a261b2369 100644
--- a/builtin/verify-tag.c
+++ b/builtin/verify-tag.c
@@ -61,7 +61,7 @@ int cmd_verify_tag(int argc,
continue;
}
- if (gpg_verify_tag(&oid, name, flags)) {
+ if (gpg_verify_tag(repo, &oid, name, flags)) {
had_error = 1;
continue;
}
diff --git a/bundle-uri.c b/bundle-uri.c
index 57cccfc6b8..3b2e347288 100644
--- a/bundle-uri.c
+++ b/bundle-uri.c
@@ -89,7 +89,10 @@ static int summarize_bundle(struct remote_bundle_info *info, void *data)
{
FILE *fp = data;
fprintf(fp, "[bundle \"%s\"]\n", info->id);
- fprintf(fp, "\turi = %s\n", info->uri);
+ if (info->uri)
+ fprintf(fp, "\turi = %s\n", info->uri);
+ else
+ fprintf(fp, "\t# uri = (missing)\n");
if (info->creationToken)
fprintf(fp, "\tcreationToken = %"PRIu64"\n", info->creationToken);
@@ -267,6 +270,19 @@ int bundle_uri_parse_config_format(const char *uri,
result = 1;
}
+ if (!result) {
+ struct hashmap_iter iter;
+ struct remote_bundle_info *bundle;
+
+ hashmap_for_each_entry(&list->bundles, &iter, bundle, ent) {
+ if (!bundle->uri) {
+ error(_("bundle list at '%s': bundle '%s' has no uri"),
+ uri, bundle->id ? bundle->id : "<unknown>");
+ result = 1;
+ }
+ }
+ }
+
return result;
}
@@ -751,6 +767,12 @@ static int fetch_bundle_uri_internal(struct repository *r,
return -1;
}
+ if (!bundle->uri) {
+ error(_("bundle '%s' has no uri"),
+ bundle->id ? bundle->id : "<unknown>");
+ return -1;
+ }
+
if (!bundle->file &&
!(bundle->file = find_temp_filename())) {
result = -1;
diff --git a/cache-tree.c b/cache-tree.c
index 2d8947b518..16c3a36b48 100644
--- a/cache-tree.c
+++ b/cache-tree.c
@@ -813,7 +813,7 @@ static void prime_cache_tree_rec(struct repository *r,
struct cache_tree_sub *sub;
struct tree *subtree = lookup_tree(r, &entry.oid);
- if (parse_tree(subtree) < 0)
+ if (repo_parse_tree(the_repository, subtree) < 0)
exit(128);
sub = cache_tree_sub(it, entry.path);
sub->cache_tree = cache_tree();
diff --git a/commit-graph.c b/commit-graph.c
index 80be2ff2c3..6b1f02e179 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -1127,18 +1127,12 @@ struct tree *get_commit_tree_in_graph(struct repository *r, const struct commit
return get_commit_tree_in_graph_one(r->objects->commit_graph, c);
}
-struct packed_commit_list {
- struct commit **list;
- size_t nr;
- size_t alloc;
-};
-
struct write_commit_graph_context {
struct repository *r;
struct odb_source *odb_source;
char *graph_name;
struct oid_array oids;
- struct packed_commit_list commits;
+ struct commit_stack commits;
int num_extra_edges;
int num_generation_data_overflows;
unsigned long approx_nr_objects;
@@ -1180,7 +1174,7 @@ static int write_graph_chunk_fanout(struct hashfile *f,
{
struct write_commit_graph_context *ctx = data;
int i, count = 0;
- struct commit **list = ctx->commits.list;
+ struct commit **list = ctx->commits.items;
/*
* Write the first-level table (the list is sorted,
@@ -1206,7 +1200,7 @@ static int write_graph_chunk_oids(struct hashfile *f,
void *data)
{
struct write_commit_graph_context *ctx = data;
- struct commit **list = ctx->commits.list;
+ struct commit **list = ctx->commits.items;
int count;
for (count = 0; count < ctx->commits.nr; count++, list++) {
display_progress(ctx->progress, ++ctx->progress_cnt);
@@ -1226,8 +1220,8 @@ static int write_graph_chunk_data(struct hashfile *f,
void *data)
{
struct write_commit_graph_context *ctx = data;
- struct commit **list = ctx->commits.list;
- struct commit **last = ctx->commits.list + ctx->commits.nr;
+ struct commit **list = ctx->commits.items;
+ struct commit **last = ctx->commits.items + ctx->commits.nr;
uint32_t num_extra_edges = 0;
while (list < last) {
@@ -1249,7 +1243,7 @@ static int write_graph_chunk_data(struct hashfile *f,
edge_value = GRAPH_PARENT_NONE;
else {
edge_value = oid_pos(&parent->item->object.oid,
- ctx->commits.list,
+ ctx->commits.items,
ctx->commits.nr,
commit_to_oid);
@@ -1280,7 +1274,7 @@ static int write_graph_chunk_data(struct hashfile *f,
edge_value = GRAPH_EXTRA_EDGES_NEEDED | num_extra_edges;
else {
edge_value = oid_pos(&parent->item->object.oid,
- ctx->commits.list,
+ ctx->commits.items,
ctx->commits.nr,
commit_to_oid);
@@ -1332,7 +1326,7 @@ static int write_graph_chunk_generation_data(struct hashfile *f,
int i, num_generation_data_overflows = 0;
for (i = 0; i < ctx->commits.nr; i++) {
- struct commit *c = ctx->commits.list[i];
+ struct commit *c = ctx->commits.items[i];
timestamp_t offset;
repo_parse_commit(ctx->r, c);
offset = commit_graph_data_at(c)->generation - c->date;
@@ -1355,7 +1349,7 @@ static int write_graph_chunk_generation_data_overflow(struct hashfile *f,
struct write_commit_graph_context *ctx = data;
int i;
for (i = 0; i < ctx->commits.nr; i++) {
- struct commit *c = ctx->commits.list[i];
+ struct commit *c = ctx->commits.items[i];
timestamp_t offset = commit_graph_data_at(c)->generation - c->date;
display_progress(ctx->progress, ++ctx->progress_cnt);
@@ -1372,8 +1366,8 @@ static int write_graph_chunk_extra_edges(struct hashfile *f,
void *data)
{
struct write_commit_graph_context *ctx = data;
- struct commit **list = ctx->commits.list;
- struct commit **last = ctx->commits.list + ctx->commits.nr;
+ struct commit **list = ctx->commits.items;
+ struct commit **last = ctx->commits.items + ctx->commits.nr;
struct commit_list *parent;
while (list < last) {
@@ -1393,7 +1387,7 @@ static int write_graph_chunk_extra_edges(struct hashfile *f,
/* Since num_parents > 2, this initializer is safe. */
for (parent = (*list)->parents->next; parent; parent = parent->next) {
int edge_value = oid_pos(&parent->item->object.oid,
- ctx->commits.list,
+ ctx->commits.items,
ctx->commits.nr,
commit_to_oid);
@@ -1427,8 +1421,8 @@ static int write_graph_chunk_bloom_indexes(struct hashfile *f,
void *data)
{
struct write_commit_graph_context *ctx = data;
- struct commit **list = ctx->commits.list;
- struct commit **last = ctx->commits.list + ctx->commits.nr;
+ struct commit **list = ctx->commits.items;
+ struct commit **last = ctx->commits.items + ctx->commits.nr;
uint32_t cur_pos = 0;
while (list < last) {
@@ -1463,8 +1457,8 @@ static int write_graph_chunk_bloom_data(struct hashfile *f,
void *data)
{
struct write_commit_graph_context *ctx = data;
- struct commit **list = ctx->commits.list;
- struct commit **last = ctx->commits.list + ctx->commits.nr;
+ struct commit **list = ctx->commits.items;
+ struct commit **last = ctx->commits.items + ctx->commits.nr;
trace2_bloom_filter_settings(ctx);
@@ -1499,7 +1493,7 @@ static int add_packed_commits(const struct object_id *oid,
display_progress(ctx->progress, ++ctx->progress_done);
oi.typep = &type;
- if (packed_object_info(ctx->r, pack, offset, &oi) < 0)
+ if (packed_object_info(pack, offset, &oi) < 0)
die(_("unable to get type of object %s"), oid_to_hex(oid));
if (type != OBJ_COMMIT)
@@ -1585,7 +1579,7 @@ static void close_reachable(struct write_commit_graph_context *ctx)
struct compute_generation_info {
struct repository *r;
- struct packed_commit_list *commits;
+ struct commit_stack *commits;
struct progress *progress;
int progress_cnt;
@@ -1622,7 +1616,7 @@ static void compute_reachable_generation_numbers(
struct commit_list *list = NULL;
for (i = 0; i < info->commits->nr; i++) {
- struct commit *c = info->commits->list[i];
+ struct commit *c = info->commits->items[i];
timestamp_t gen;
repo_parse_commit(info->r, c);
gen = info->get_generation(c, info->data);
@@ -1729,7 +1723,7 @@ static void compute_generation_numbers(struct write_commit_graph_context *ctx)
if (!ctx->trust_generation_numbers) {
for (i = 0; i < ctx->commits.nr; i++) {
- struct commit *c = ctx->commits.list[i];
+ struct commit *c = ctx->commits.items[i];
repo_parse_commit(ctx->r, c);
commit_graph_data_at(c)->generation = GENERATION_NUMBER_ZERO;
}
@@ -1738,7 +1732,7 @@ static void compute_generation_numbers(struct write_commit_graph_context *ctx)
compute_reachable_generation_numbers(&info, 2);
for (i = 0; i < ctx->commits.nr; i++) {
- struct commit *c = ctx->commits.list[i];
+ struct commit *c = ctx->commits.items[i];
timestamp_t offset = commit_graph_data_at(c)->generation - c->date;
if (offset > GENERATION_NUMBER_V2_OFFSET_MAX)
ctx->num_generation_data_overflows++;
@@ -1760,8 +1754,8 @@ void ensure_generations_valid(struct repository *r,
struct commit **commits, size_t nr)
{
int generation_version = get_configured_generation_version(r);
- struct packed_commit_list list = {
- .list = commits,
+ struct commit_stack list = {
+ .items = commits,
.alloc = nr,
.nr = nr,
};
@@ -1804,7 +1798,7 @@ static void compute_bloom_filters(struct write_commit_graph_context *ctx)
_("Computing commit changed paths Bloom filters"),
ctx->commits.nr);
- DUP_ARRAY(sorted_commits, ctx->commits.list, ctx->commits.nr);
+ DUP_ARRAY(sorted_commits, ctx->commits.items, ctx->commits.nr);
if (ctx->order_by_pack)
QSORT(sorted_commits, ctx->commits.nr, commit_pos_cmp);
@@ -1992,26 +1986,26 @@ static void copy_oids_to_commits(struct write_commit_graph_context *ctx)
oid_array_sort(&ctx->oids);
for (i = 0; i < ctx->oids.nr; i = oid_array_next_unique(&ctx->oids, i)) {
unsigned int num_parents;
+ struct commit *commit;
display_progress(ctx->progress, i + 1);
- ALLOC_GROW(ctx->commits.list, ctx->commits.nr + 1, ctx->commits.alloc);
- ctx->commits.list[ctx->commits.nr] = lookup_commit(ctx->r, &ctx->oids.oid[i]);
+ commit = lookup_commit(ctx->r, &ctx->oids.oid[i]);
if (ctx->split && flags != COMMIT_GRAPH_SPLIT_REPLACE &&
- commit_graph_position(ctx->commits.list[ctx->commits.nr]) != COMMIT_NOT_FROM_GRAPH)
+ commit_graph_position(commit) != COMMIT_NOT_FROM_GRAPH)
continue;
if (ctx->split && flags == COMMIT_GRAPH_SPLIT_REPLACE)
- repo_parse_commit(ctx->r, ctx->commits.list[ctx->commits.nr]);
+ repo_parse_commit(ctx->r, commit);
else
- repo_parse_commit_no_graph(ctx->r, ctx->commits.list[ctx->commits.nr]);
+ repo_parse_commit_no_graph(ctx->r, commit);
- num_parents = commit_list_count(ctx->commits.list[ctx->commits.nr]->parents);
+ num_parents = commit_list_count(commit->parents);
if (num_parents > 2)
ctx->num_extra_edges += num_parents - 1;
- ctx->commits.nr++;
+ commit_stack_push(&ctx->commits, commit);
}
stop_progress(&ctx->progress);
}
@@ -2330,7 +2324,7 @@ static void merge_commit_graph(struct write_commit_graph_context *ctx,
oid_to_hex(&g->oid),
(uintmax_t)st_add(ctx->commits.nr, g->num_commits));
- ALLOC_GROW(ctx->commits.list, ctx->commits.nr + g->num_commits, ctx->commits.alloc);
+ commit_stack_grow(&ctx->commits, g->num_commits);
for (i = 0; i < g->num_commits; i++) {
struct object_id oid;
@@ -2343,10 +2337,8 @@ static void merge_commit_graph(struct write_commit_graph_context *ctx,
/* only add commits if they still exist in the repo */
result = lookup_commit_reference_gently(ctx->r, &oid, 1);
- if (result) {
- ctx->commits.list[ctx->commits.nr] = result;
- ctx->commits.nr++;
- }
+ if (result)
+ commit_stack_push(&ctx->commits, result);
}
}
@@ -2367,14 +2359,14 @@ static void sort_and_scan_merged_commits(struct write_commit_graph_context *ctx)
_("Scanning merged commits"),
ctx->commits.nr);
- QSORT(ctx->commits.list, ctx->commits.nr, commit_compare);
+ QSORT(ctx->commits.items, ctx->commits.nr, commit_compare);
ctx->num_extra_edges = 0;
for (i = 0; i < ctx->commits.nr; i++) {
display_progress(ctx->progress, i + 1);
- if (i && oideq(&ctx->commits.list[i - 1]->object.oid,
- &ctx->commits.list[i]->object.oid)) {
+ if (i && oideq(&ctx->commits.items[i - 1]->object.oid,
+ &ctx->commits.items[i]->object.oid)) {
/*
* Silently ignore duplicates. These were likely
* created due to a commit appearing in multiple
@@ -2385,10 +2377,10 @@ static void sort_and_scan_merged_commits(struct write_commit_graph_context *ctx)
} else {
unsigned int num_parents;
- ctx->commits.list[dedup_i] = ctx->commits.list[i];
+ ctx->commits.items[dedup_i] = ctx->commits.items[i];
dedup_i++;
- num_parents = commit_list_count(ctx->commits.list[i]->parents);
+ num_parents = commit_list_count(ctx->commits.items[i]->parents);
if (num_parents > 2)
ctx->num_extra_edges += num_parents - 1;
}
@@ -2666,7 +2658,7 @@ int write_commit_graph(struct odb_source *source,
cleanup:
free(ctx.graph_name);
free(ctx.base_graph_name);
- free(ctx.commits.list);
+ commit_stack_clear(&ctx.commits);
oid_array_clear(&ctx.oids);
clear_topo_level_slab(&topo_levels);
diff --git a/commit-reach.c b/commit-reach.c
index cc18c86d3b..e7d9b3208f 100644
--- a/commit-reach.c
+++ b/commit-reach.c
@@ -283,8 +283,8 @@ static int remove_redundant_with_gen(struct repository *r,
{
size_t i, count_non_stale = 0, count_still_independent = cnt;
timestamp_t min_generation = GENERATION_NUMBER_INFINITY;
- struct commit **walk_start, **sorted;
- size_t walk_start_nr = 0, walk_start_alloc = cnt;
+ struct commit **sorted;
+ struct commit_stack walk_start = COMMIT_STACK_INIT;
size_t min_gen_pos = 0;
/*
@@ -298,7 +298,7 @@ static int remove_redundant_with_gen(struct repository *r,
QSORT(sorted, cnt, compare_commits_by_gen);
min_generation = commit_graph_generation(sorted[0]);
- ALLOC_ARRAY(walk_start, walk_start_alloc);
+ commit_stack_grow(&walk_start, cnt);
/* Mark all parents of the input as STALE */
for (i = 0; i < cnt; i++) {
@@ -312,18 +312,17 @@ static int remove_redundant_with_gen(struct repository *r,
repo_parse_commit(r, parents->item);
if (!(parents->item->object.flags & STALE)) {
parents->item->object.flags |= STALE;
- ALLOC_GROW(walk_start, walk_start_nr + 1, walk_start_alloc);
- walk_start[walk_start_nr++] = parents->item;
+ commit_stack_push(&walk_start, parents->item);
}
parents = parents->next;
}
}
- QSORT(walk_start, walk_start_nr, compare_commits_by_gen);
+ QSORT(walk_start.items, walk_start.nr, compare_commits_by_gen);
/* remove STALE bit for now to allow walking through parents */
- for (i = 0; i < walk_start_nr; i++)
- walk_start[i]->object.flags &= ~STALE;
+ for (i = 0; i < walk_start.nr; i++)
+ walk_start.items[i]->object.flags &= ~STALE;
/*
* Start walking from the highest generation. Hopefully, it will
@@ -331,12 +330,12 @@ static int remove_redundant_with_gen(struct repository *r,
* terminate early. Otherwise, we will do the same amount of work
* as before.
*/
- for (i = walk_start_nr; i && count_still_independent > 1; i--) {
+ for (i = walk_start.nr; i && count_still_independent > 1; i--) {
/* push the STALE bits up to min generation */
struct commit_list *stack = NULL;
- commit_list_insert(walk_start[i - 1], &stack);
- walk_start[i - 1]->object.flags |= STALE;
+ commit_list_insert(walk_start.items[i - 1], &stack);
+ walk_start.items[i - 1]->object.flags |= STALE;
while (stack) {
struct commit_list *parents;
@@ -390,8 +389,8 @@ static int remove_redundant_with_gen(struct repository *r,
}
/* clear marks */
- clear_commit_marks_many(walk_start_nr, walk_start, STALE);
- free(walk_start);
+ clear_commit_marks_many(walk_start.nr, walk_start.items, STALE);
+ commit_stack_clear(&walk_start);
*dedup_cnt = count_non_stale;
return 0;
diff --git a/commit.c b/commit.c
index 709c9eed58..28bb5ce029 100644
--- a/commit.c
+++ b/commit.c
@@ -1981,3 +1981,31 @@ int run_commit_hook(int editor_is_used, const char *index_file,
opt.invoked_hook = invoked_hook;
return run_hooks_opt(the_repository, name, &opt);
}
+
+void commit_stack_init(struct commit_stack *stack)
+{
+ stack->items = NULL;
+ stack->nr = stack->alloc = 0;
+}
+
+void commit_stack_grow(struct commit_stack *stack, size_t extra)
+{
+ ALLOC_GROW(stack->items, st_add(stack->nr, extra), stack->alloc);
+}
+
+void commit_stack_push(struct commit_stack *stack, struct commit *commit)
+{
+ commit_stack_grow(stack, 1);
+ stack->items[stack->nr++] = commit;
+}
+
+struct commit *commit_stack_pop(struct commit_stack *stack)
+{
+ return stack->nr ? stack->items[--stack->nr] : NULL;
+}
+
+void commit_stack_clear(struct commit_stack *stack)
+{
+ free(stack->items);
+ commit_stack_init(stack);
+}
diff --git a/commit.h b/commit.h
index 5406dd2663..79a761c37d 100644
--- a/commit.h
+++ b/commit.h
@@ -381,4 +381,16 @@ int parse_buffer_signed_by_header(const char *buffer,
const struct git_hash_algo *algop);
int add_header_signature(struct strbuf *buf, struct strbuf *sig, const struct git_hash_algo *algo);
+struct commit_stack {
+ struct commit **items;
+ size_t nr, alloc;
+};
+#define COMMIT_STACK_INIT { 0 }
+
+void commit_stack_init(struct commit_stack *);
+void commit_stack_grow(struct commit_stack *, size_t);
+void commit_stack_push(struct commit_stack *, struct commit *);
+struct commit *commit_stack_pop(struct commit_stack *);
+void commit_stack_clear(struct commit_stack *);
+
#endif /* COMMIT_H */
diff --git a/compat/mingw.c b/compat/mingw.c
index f09b49ff21..cf4f3c92e7 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -1239,18 +1239,16 @@ char *mingw_getcwd(char *pointer, int len)
{
wchar_t cwd[MAX_PATH], wpointer[MAX_PATH];
DWORD ret = GetCurrentDirectoryW(ARRAY_SIZE(cwd), cwd);
+ HANDLE hnd;
if (!ret || ret >= ARRAY_SIZE(cwd)) {
errno = ret ? ENAMETOOLONG : err_win_to_posix(GetLastError());
return NULL;
}
- ret = GetLongPathNameW(cwd, wpointer, ARRAY_SIZE(wpointer));
- if (!ret && GetLastError() == ERROR_ACCESS_DENIED) {
- HANDLE hnd = CreateFileW(cwd, 0,
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
- OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
- if (hnd == INVALID_HANDLE_VALUE)
- return NULL;
+ hnd = CreateFileW(cwd, 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+ OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ if (hnd != INVALID_HANDLE_VALUE) {
ret = GetFinalPathNameByHandleW(hnd, wpointer, ARRAY_SIZE(wpointer), 0);
CloseHandle(hnd);
if (!ret || ret >= ARRAY_SIZE(wpointer))
@@ -1259,13 +1257,11 @@ char *mingw_getcwd(char *pointer, int len)
return NULL;
return pointer;
}
- if (!ret || ret >= ARRAY_SIZE(wpointer))
- return NULL;
- if (GetFileAttributesW(wpointer) == INVALID_FILE_ATTRIBUTES) {
+ if (GetFileAttributesW(cwd) == INVALID_FILE_ATTRIBUTES) {
errno = ENOENT;
return NULL;
}
- if (xwcstoutf(pointer, wpointer, len) < 0)
+ if (xwcstoutf(pointer, cwd, len) < 0)
return NULL;
convert_slashes(pointer);
return pointer;
diff --git a/config.c b/config.c
index 1738c0cb0d..7f6d53b473 100644
--- a/config.c
+++ b/config.c
@@ -2435,14 +2435,14 @@ int repo_config_get_expiry_in_days(struct repository *r, const char *key,
timestamp_t *expiry, timestamp_t now)
{
const char *expiry_string;
- intmax_t days;
+ int days;
timestamp_t when;
if (repo_config_get_string_tmp(r, key, &expiry_string))
return 1; /* no such thing */
- if (git_parse_signed(expiry_string, &days, maximum_signed_value_of_type(int))) {
- const int scale = 86400;
+ if (git_parse_int(expiry_string, &days)) {
+ const intmax_t scale = 86400;
*expiry = now - days * scale;
return 0;
}
diff --git a/config.mak.uname b/config.mak.uname
index 1691c6ae6e..3c35ae33a3 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -124,6 +124,7 @@ ifeq ($(uname_S),Darwin)
# - MacOS 10.0.* and MacOS 10.1.0 = Darwin 1.*
# - MacOS 10.x.* = Darwin (x+4).* for (1 <= x)
# i.e. "begins with [15678] and a dot" means "10.4.* or older".
+ DARWIN_MAJOR_VERSION = $(shell expr "$(uname_R)" : '\([0-9]*\)\.')
ifeq ($(shell expr "$(uname_R)" : '[15678]\.'),2)
OLD_ICONV = UnfortunatelyYes
NO_APPLE_COMMON_CRYPTO = YesPlease
@@ -149,28 +150,14 @@ ifeq ($(uname_S),Darwin)
CSPRNG_METHOD = arc4random
USE_ENHANCED_BASIC_REGULAR_EXPRESSIONS = YesPlease
- # Workaround for `gettext` being keg-only and not even being linked via
- # `brew link --force gettext`, should be obsolete as of
- # https://github.com/Homebrew/homebrew-core/pull/53489
- ifeq ($(shell test -d /usr/local/opt/gettext/ && echo y),y)
- BASIC_CFLAGS += -I/usr/local/include -I/usr/local/opt/gettext/include
- BASIC_LDFLAGS += -L/usr/local/lib -L/usr/local/opt/gettext/lib
- ifeq ($(shell test -x /usr/local/opt/gettext/bin/msgfmt && echo y),y)
- MSGFMT = /usr/local/opt/gettext/bin/msgfmt
- endif
- # On newer ARM-based machines the default installation path has changed to
- # /opt/homebrew. Include it in our search paths so that the user does not
- # have to configure this manually.
- #
- # Note that we do not employ the same workaround as above where we manually
- # add gettext. The issue was fixed more than three years ago by now, and at
- # that point there haven't been any ARM-based Macs yet.
- else ifeq ($(shell test -d /opt/homebrew/ && echo y),y)
- BASIC_CFLAGS += -I/opt/homebrew/include
- BASIC_LDFLAGS += -L/opt/homebrew/lib
- ifeq ($(shell test -x /opt/homebrew/bin/msgfmt && echo y),y)
- MSGFMT = /opt/homebrew/bin/msgfmt
- endif
+ ifeq ($(uname_M),arm64)
+ HOMEBREW_PREFIX = /opt/homebrew
+ else
+ HOMEBREW_PREFIX = /usr/local
+ endif
+ ifeq ($(shell test "$(DARWIN_MAJOR_VERSION)" -ge 24 && echo 1),1)
+ USE_HOMEBREW_LIBICONV = UnfortunatelyYes
+ NEEDS_GOOD_LIBICONV = UnfortunatelyYes
endif
# The builtin FSMonitor on MacOS builds upon Simple-IPC. Both require
diff --git a/contrib/coccinelle/the_repository.cocci b/contrib/coccinelle/the_repository.cocci
index ea7fe1c8db..f1129f7985 100644
--- a/contrib/coccinelle/the_repository.cocci
+++ b/contrib/coccinelle/the_repository.cocci
@@ -2,121 +2,16 @@
@@
@@
(
-// cache.h
-- get_oid
-+ repo_get_oid
+// TODO: remove the rules below and the macros from tree.h after the
+// next Git release.
+- parse_tree
++ repo_parse_tree
|
-- get_oid_commit
-+ repo_get_oid_commit
+- parse_tree_gently
++ repo_parse_tree_gently
|
-- get_oid_committish
-+ repo_get_oid_committish
-|
-- get_oid_tree
-+ repo_get_oid_tree
-|
-- get_oid_treeish
-+ repo_get_oid_treeish
-|
-- get_oid_blob
-+ repo_get_oid_blob
-|
-- get_oid_mb
-+ repo_get_oid_mb
-|
-- find_unique_abbrev
-+ repo_find_unique_abbrev
-|
-- find_unique_abbrev_r
-+ repo_find_unique_abbrev_r
-|
-- for_each_abbrev
-+ repo_for_each_abbrev
-|
-- interpret_branch_name
-+ repo_interpret_branch_name
-|
-- peel_to_type
-+ repo_peel_to_type
-// commit-reach.h
-|
-- get_merge_bases
-+ repo_get_merge_bases
-|
-- get_merge_bases_many
-+ repo_get_merge_bases_many
-|
-- get_merge_bases_many_dirty
-+ repo_get_merge_bases_many_dirty
-|
-- in_merge_bases
-+ repo_in_merge_bases
-|
-- in_merge_bases_many
-+ repo_in_merge_bases_many
-// commit.h
-|
-- parse_commit_internal
-+ repo_parse_commit_internal
-|
-- parse_commit
-+ repo_parse_commit
-|
-- get_commit_buffer
-+ repo_get_commit_buffer
-|
-- unuse_commit_buffer
-+ repo_unuse_commit_buffer
-|
-- logmsg_reencode
-+ repo_logmsg_reencode
-|
-- get_commit_tree
-+ repo_get_commit_tree
-// diff.h
-|
-- diff_setup
-+ repo_diff_setup
-// odb.h
-|
-- read_object_file
-+ repo_read_object_file
-|
-- has_object_file
-+ repo_has_object_file
-|
-- has_object_file_with_flags
-+ repo_has_object_file_with_flags
-// pretty.h
-|
-- format_commit_message
-+ repo_format_commit_message
-// packfile.h
-|
-- approximate_object_count
-+ repo_approximate_object_count
-// promisor-remote.h
-|
-- promisor_remote_reinit
-+ repo_promisor_remote_reinit
-|
-- promisor_remote_find
-+ repo_promisor_remote_find
-|
-- has_promisor_remote
-+ repo_has_promisor_remote
-// refs.h
-|
-- dwim_ref
-+ repo_dwim_ref
-// rerere.h
-|
-- rerere
-+ repo_rerere
-// revision.h
-|
-- init_revisions
-+ repo_init_revisions
+- parse_tree_indirect
++ repo_parse_tree_indirect
)
(
+ the_repository,
diff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh
index 17106d1a72..3ebe88cbea 100755
--- a/contrib/subtree/git-subtree.sh
+++ b/contrib/subtree/git-subtree.sh
@@ -325,6 +325,12 @@ check_parents () {
done
}
+# Usage: get_notree REV
+get_notree () {
+ assert test $# = 1
+ test -r "$cachedir/notree/$1"
+}
+
# Usage: set_notree REV
set_notree () {
assert test $# = 1
@@ -511,6 +517,71 @@ find_existing_splits () {
done || exit $?
}
+# Usage: find_other_splits DIR REV UNREVS...
+#
+# Scan history in REV UNREVS for other `git subtree split --rejoin`
+# merge commits belonging to prefixes outside of DIR. These
+# "other splits" don't contribute to DIR and can be ignored.
+#
+# If any such rejoins are found,
+#
+# * emit their second-parent as an UNREV, avoiding a
+# potentially costly history traversal
+#
+# * mark the merge commit as "notree" to ignore it
+find_other_splits () {
+ assert test $# -ge 2
+ dir="${1%/}"
+ rev="$2"
+ shift 2
+ debug "Looking for other splits with dir != $dir..."
+
+ git log \
+ --grep '^git-subtree-mainline:' \
+ --no-patch \
+ --no-show-signature \
+ --format='hash: %H%nparents: %P%n%(trailers:key=git-subtree-dir,key=git-subtree-mainline,key=git-subtree-split)%nEND' \
+ "$rev" ${@:+"$@"} |
+ while read -r key val
+ do
+ case "$key" in
+ hash:)
+ commit_hash="${val}"
+ commit_parents=
+ subtree_dir=
+ subtree_mainline=
+ subtree_split=
+ ;;
+ parents:)
+ commit_parents="${val}" ;;
+ git-subtree-dir:)
+ subtree_dir="${val%/}/" ;;
+ git-subtree-mainline:)
+ subtree_mainline="${val}" ;;
+ git-subtree-split:)
+ subtree_split="${val}" ;;
+ END)
+ # verify:
+ # * all git-subtree-* trailers are present
+ # * this subtree is outside of $dir
+ # * the first parent is the git-subtree-mainline:
+ # * the commit has at least two parents
+ if test -n "${subtree_dir}" &&
+ test -n "${subtree_split}" &&
+ test -n "${subtree_mainline}" &&
+ test "${subtree_dir}" = "${subtree_dir#"${dir}/"}" &&
+ test "${commit_parents}" != "${commit_parents#"$subtree_mainline "}" &&
+ rev_exists "${commit_hash}^2"
+ then
+ debug "find_other_splits excluding dir=$subtree_dir merged in ${commit_hash}"
+ echo "^${commit_hash}^2"
+ set_notree "${commit_hash}"
+ fi
+ ;;
+ esac
+ done
+}
+
# Usage: copy_commit REV TREE FLAGS_STR
copy_commit () {
assert test $# = 3
@@ -785,42 +856,6 @@ ensure_valid_ref_format () {
die "fatal: '$1' does not look like a ref"
}
-# Usage: should_ignore_subtree_split_commit REV
-#
-# Check if REV is a commit from another subtree and should be
-# ignored from processing for splits
-should_ignore_subtree_split_commit () {
- assert test $# = 1
-
- git show \
- --no-patch \
- --no-show-signature \
- --format='%(trailers:key=git-subtree-dir,key=git-subtree-mainline)' \
- "$1" |
- (
- have_mainline=
- subtree_dir=
-
- while read -r trailer val
- do
- case "$trailer" in
- git-subtree-dir:)
- subtree_dir="${val%/}" ;;
- git-subtree-mainline:)
- have_mainline=y ;;
- esac
- done
-
- if test -n "${subtree_dir}" &&
- test -z "${have_mainline}" &&
- test "${subtree_dir}" != "$arg_prefix"
- then
- return 0
- fi
- return 1
- )
-}
-
# Usage: process_split_commit REV PARENTS
process_split_commit () {
assert test $# = 2
@@ -994,31 +1029,39 @@ cmd_split () {
fi
unrevs="$(find_existing_splits "$dir" "$rev" "$repository")" || exit $?
+ (find_other_splits >"$cachedir/prune" "$dir" "$rev" $unrevs) || exit $?
# We can't restrict rev-list to only $dir here, because some of our
# parents have the $dir contents the root, and those won't match.
# (and rev-list --follow doesn't seem to solve this)
- grl='git rev-list --topo-order --reverse --parents $rev $unrevs'
- revmax=$(eval "$grl" | wc -l)
+ revmax="$(git rev-list \
+ <"$cachedir/prune" \
+ --topo-order \
+ --reverse \
+ --parents \
+ --stdin \
+ --count \
+ "$rev" \
+ $unrevs
+ )"
revcount=0
createcount=0
extracount=0
- eval "$grl" |
+ git rev-list \
+ <"$cachedir/prune" \
+ --topo-order \
+ --reverse \
+ --parents \
+ --stdin \
+ "$rev" \
+ $unrevs |
while read rev parents
do
- if should_ignore_subtree_split_commit "$rev"
+ if get_notree "$rev"
then
continue
fi
- parsedparents=''
- for parent in $parents
- do
- if ! should_ignore_subtree_split_commit "$parent"
- then
- parsedparents="$parsedparents$parent "
- fi
- done
- process_split_commit "$rev" "$parsedparents"
+ process_split_commit "$rev" "$parents"
done || exit $?
latest_new=$(cache_get latest_new) || exit $?
diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh
index 316dc5269e..4db3a6eff3 100755
--- a/contrib/subtree/t/t7900-subtree.sh
+++ b/contrib/subtree/t/t7900-subtree.sh
@@ -411,8 +411,9 @@ test_expect_success 'split sub dir/ with --rejoin' '
git fetch ./"sub proj" HEAD &&
git subtree merge --prefix="sub dir" FETCH_HEAD &&
split_hash=$(git subtree split --prefix="sub dir" --annotate="*") &&
- git subtree split --prefix="sub dir" --annotate="*" --rejoin &&
- test "$(last_commit_subject)" = "Split '\''sub dir/'\'' into commit '\''$split_hash'\''"
+ git subtree split --prefix="sub dir" --annotate="*" -b spl --rejoin &&
+ test "$(last_commit_subject)" = "Split '\''sub dir/'\'' into commit '\''$split_hash'\''" &&
+ test "$(git rev-list --count spl)" -eq 5
)
'
@@ -442,18 +443,25 @@ test_expect_success 'split with multiple subtrees' '
git -C "$test_count" subtree add --prefix=subADir FETCH_HEAD &&
git -C "$test_count" fetch ./subB HEAD &&
git -C "$test_count" subtree add --prefix=subBDir FETCH_HEAD &&
+ test "$(git -C "$test_count" rev-list --count main)" -eq 7 &&
test_create_commit "$test_count" subADir/main-subA1 &&
test_create_commit "$test_count" subBDir/main-subB1 &&
git -C "$test_count" subtree split --prefix=subADir \
- --squash --rejoin -m "Sub A Split 1" &&
+ --squash --rejoin -m "Sub A Split 1" -b a1 &&
+ test "$(git -C "$test_count" rev-list --count main..a1)" -eq 1 &&
git -C "$test_count" subtree split --prefix=subBDir \
- --squash --rejoin -m "Sub B Split 1" &&
+ --squash --rejoin -m "Sub B Split 1" -b b1 &&
+ test "$(git -C "$test_count" rev-list --count main..b1)" -eq 1 &&
test_create_commit "$test_count" subADir/main-subA2 &&
test_create_commit "$test_count" subBDir/main-subB2 &&
git -C "$test_count" subtree split --prefix=subADir \
- --squash --rejoin -m "Sub A Split 2" &&
+ --squash --rejoin -m "Sub A Split 2" -b a2 &&
+ test "$(git -C "$test_count" rev-list --count main..a2)" -eq 2 &&
+ test "$(git -C "$test_count" rev-list --count a1..a2)" -eq 1 &&
test "$(git -C "$test_count" subtree split --prefix=subBDir \
- --squash --rejoin -d -m "Sub B Split 1" 2>&1 | grep -w "\[1\]")" = ""
+ --squash --rejoin -d -m "Sub B Split 1" -b b2 2>&1 | grep -w "\[1\]")" = "" &&
+ test "$(git -C "$test_count" rev-list --count main..b2)" -eq 2 &&
+ test "$(git -C "$test_count" rev-list --count b1..b2)" -eq 1
'
# When subtree split-ing a directory that has other subtree
@@ -477,6 +485,7 @@ do
test_path_is_file subA/file1.t &&
test_path_is_file subA/subB/file2.t &&
git subtree split --prefix=subA --branch=bsplit &&
+ test "$(git rev-list --count bsplit)" -eq 2 &&
git checkout bsplit &&
test_path_is_file file1.t &&
test_path_is_file subB/file2.t &&
@@ -489,6 +498,7 @@ do
--prefix=subA/subB mksubtree &&
test_path_is_file subA/subB/file3.t &&
git subtree split --prefix=subA --branch=bsplit &&
+ test "$(git rev-list --count bsplit)" -eq 3 &&
git checkout bsplit &&
test_path_is_file file1.t &&
test_path_is_file subB/file2.t &&
@@ -497,6 +507,67 @@ do
'
done
+# Usually,
+#
+# git subtree merge -P subA --squash f00...
+#
+# makes two commits, in this order:
+#
+# 1. Squashed 'subA/' content from commit f00...
+# 2. Merge commit (1) as 'subA'
+#
+# Commit 1 updates the subtree but does *not* rewrite paths.
+# Commit 2 rewrites all trees to start with `subA/`
+#
+# Commit 1 either has no parents or depends only on other
+# "Squashed 'subA/' content" commits.
+#
+# For merge without --squash, subtree produces just one commit:
+# a merge commit with git-subtree trailers.
+#
+# In either case, if the user rebases these commits, they will
+# still have the git-subtree-* trailers… but will NOT have
+# the layout described above.
+#
+# Test that subsequent `git subtree split` are not confused by this.
+test_expect_success 'split with rebased subtree commit' '
+ subtree_test_create_repo "$test_count" &&
+ (
+ cd "$test_count" &&
+ test_commit file0 &&
+ test_create_subtree_add \
+ . mksubtree subA file1 --squash &&
+ test_path_is_file subA/file1.t &&
+ mkdir subB &&
+ test_commit subB/bfile &&
+ git commit --amend -F - <<'EOF' &&
+Squashed '\''subB/'\'' content from commit '\''badf00da911bbe895347b4b236f5461d55dc9877'\''
+
+Simulate a cherry-picked or rebased subtree commit.
+
+git-subtree-dir: subB
+git-subtree-split: badf00da911bbe895347b4b236f5461d55dc9877
+EOF
+ test_commit subA/file2 &&
+ test_commit subB/bfile2 &&
+ git commit --amend -F - <<'EOF' &&
+Split '\''subB/'\'' into commit '\''badf00da911bbe895347b4b236f5461d55dc9877'\''
+
+Simulate a cherry-picked or rebased subtree commit.
+
+git-subtree-dir: subB
+git-subtree-mainline: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+git-subtree-split: badf00da911bbe895347b4b236f5461d55dc9877
+EOF
+ git subtree split --prefix=subA --branch=bsplit &&
+ git checkout bsplit &&
+ test_path_is_file file1.t &&
+ test_path_is_file file2.t &&
+ test "$(last_commit_subject)" = "subA/file2" &&
+ test "$(git rev-list --count bsplit)" -eq 2
+ )
+'
+
test_expect_success 'split sub dir/ with --rejoin from scratch' '
subtree_test_create_repo "$test_count" &&
test_create_commit "$test_count" main1 &&
diff --git a/delta-islands.c b/delta-islands.c
index 7cfebc4162..f4d2468790 100644
--- a/delta-islands.c
+++ b/delta-islands.c
@@ -283,7 +283,7 @@ void resolve_tree_islands(struct repository *r,
root_marks = kh_value(island_marks, pos);
tree = lookup_tree(r, &ent->idx.oid);
- if (!tree || parse_tree(tree) < 0)
+ if (!tree || repo_parse_tree(r, tree) < 0)
die(_("bad tree object %s"), oid_to_hex(&ent->idx.oid));
init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
diff --git a/diff-lib.c b/diff-lib.c
index 5307390ff3..506000761d 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -552,7 +552,7 @@ static int diff_cache(struct rev_info *revs,
struct tree_desc t;
struct unpack_trees_options opts;
- tree = parse_tree_indirect(tree_oid);
+ tree = repo_parse_tree_indirect(the_repository, tree_oid);
if (!tree)
return error("bad tree object %s",
tree_name ? tree_name : oid_to_hex(tree_oid));
diff --git a/diff.c b/diff.c
index 436da250eb..a68ddd2168 100644
--- a/diff.c
+++ b/diff.c
@@ -7098,6 +7098,7 @@ static void diffcore_skip_stat_unmatch(struct diff_options *diffopt)
if (!diffopt->flags.no_index)
diffopt->skip_stat_unmatch++;
diff_free_filepair(p);
+ q->queue[i] = NULL;
}
}
free(q->queue);
@@ -7141,6 +7142,10 @@ void diff_queued_diff_prefetch(void *repository)
for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];
+
+ if (!p)
+ continue;
+
diff_add_if_missing(repo, &to_fetch, p->one);
diff_add_if_missing(repo, &to_fetch, p->two);
}
diff --git a/environment.c b/environment.c
index a770b5921d..8ffbf92d50 100644
--- a/environment.c
+++ b/environment.c
@@ -80,30 +80,6 @@ int core_sparse_checkout_cone;
int sparse_expect_files_outside_of_patterns;
int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
unsigned long pack_size_limit_cfg;
-int max_allowed_tree_depth =
-#ifdef _MSC_VER
- /*
- * When traversing into too-deep trees, Visual C-compiled Git seems to
- * run into some internal stack overflow detection in the
- * `RtlpAllocateHeap()` function that is called from within
- * `git_inflate_init()`'s call tree. The following value seems to be
- * low enough to avoid that by letting Git exit with an error before
- * the stack overflow can occur.
- */
- 512;
-#elif defined(GIT_WINDOWS_NATIVE) && defined(__clang__) && defined(__aarch64__)
- /*
- * Similar to Visual C, it seems that on Windows/ARM64 the clang-based
- * builds have a smaller stack space available. When running out of
- * that stack space, a `STATUS_STACK_OVERFLOW` is produced. When the
- * Git command was run from an MSYS2 Bash, this unfortunately results
- * in an exit code 127. Let's prevent that by lowering the maximal
- * tree depth; This value seems to be low enough.
- */
- 1280;
-#else
- 2048;
-#endif
#ifndef PROTECT_HFS_DEFAULT
#define PROTECT_HFS_DEFAULT 0
@@ -324,8 +300,8 @@ next_name:
return (current & ~negative) | positive;
}
-static int git_default_core_config(const char *var, const char *value,
- const struct config_context *ctx, void *cb)
+int git_default_core_config(const char *var, const char *value,
+ const struct config_context *ctx, void *cb)
{
/* This needs a better name */
if (!strcmp(var, "core.filemode")) {
@@ -569,11 +545,6 @@ static int git_default_core_config(const char *var, const char *value,
return 0;
}
- if (!strcmp(var, "core.maxtreedepth")) {
- max_allowed_tree_depth = git_config_int(var, value, ctx->kvi);
- return 0;
- }
-
/* Add other config variables here and to Documentation/config.adoc. */
return platform_core_config(var, value, ctx, cb);
}
diff --git a/environment.h b/environment.h
index 51898c99cd..27f657af04 100644
--- a/environment.h
+++ b/environment.h
@@ -106,6 +106,8 @@ const char *strip_namespace(const char *namespaced_ref);
int git_default_config(const char *, const char *,
const struct config_context *, void *);
+int git_default_core_config(const char *var, const char *value,
+ const struct config_context *ctx, void *cb);
/*
* TODO: All the below state either explicitly or implicitly relies on
@@ -156,7 +158,6 @@ extern char *git_attributes_file;
extern int zlib_compression_level;
extern int pack_compression_level;
extern unsigned long pack_size_limit_cfg;
-extern int max_allowed_tree_depth;
extern int precomposed_unicode;
extern int protect_hfs;
diff --git a/fsck.c b/fsck.c
index 138fffded9..3afec0d0d3 100644
--- a/fsck.c
+++ b/fsck.c
@@ -360,7 +360,7 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op
int res = 0;
const char *name;
- if (parse_tree(tree))
+ if (repo_parse_tree(the_repository, tree))
return -1;
name = fsck_get_object_name(options, &tree->object.oid);
@@ -474,7 +474,7 @@ static int fsck_walk_tag(struct tag *tag, void *data, struct fsck_options *optio
{
const char *name = fsck_get_object_name(options, &tag->object.oid);
- if (parse_tag(tag))
+ if (parse_tag(the_repository, tag))
return -1;
if (name)
fsck_put_object_name(options, &tag->tagged->oid, "%s", name);
@@ -1310,11 +1310,6 @@ int fsck_refs_error_function(struct fsck_options *options UNUSED,
strbuf_addstr(&sb, report->path);
- if (report->oid)
- strbuf_addf(&sb, " -> (%s)", oid_to_hex(report->oid));
- else if (report->referent)
- strbuf_addf(&sb, " -> (%s)", report->referent);
-
if (msg_type == FSCK_WARN)
warning("%s: %s", sb.buf, message);
else
diff --git a/fsck.h b/fsck.h
index 336917c045..65ecbb7fe1 100644
--- a/fsck.h
+++ b/fsck.h
@@ -30,6 +30,7 @@ enum fsck_msg_type {
FUNC(BAD_DATE_OVERFLOW, ERROR) \
FUNC(BAD_EMAIL, ERROR) \
FUNC(BAD_GPGSIG, ERROR) \
+ FUNC(BAD_HEAD_TARGET, ERROR) \
FUNC(BAD_NAME, ERROR) \
FUNC(BAD_OBJECT_SHA1, ERROR) \
FUNC(BAD_PACKED_REF_ENTRY, ERROR) \
@@ -39,6 +40,7 @@ enum fsck_msg_type {
FUNC(BAD_REF_CONTENT, ERROR) \
FUNC(BAD_REF_FILETYPE, ERROR) \
FUNC(BAD_REF_NAME, ERROR) \
+ FUNC(BAD_REF_OID, ERROR) \
FUNC(BAD_TIMEZONE, ERROR) \
FUNC(BAD_TREE, ERROR) \
FUNC(BAD_TREE_SHA1, ERROR) \
@@ -162,8 +164,6 @@ struct fsck_object_report {
struct fsck_ref_report {
const char *path;
- const struct object_id *oid;
- const char *referent;
};
struct fsck_options {
diff --git a/git-compat-util.h b/git-compat-util.h
index b0673d1a45..bebcf9f698 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -578,6 +578,30 @@ static inline bool strip_suffix(const char *str, const char *suffix,
#define DEFAULT_PACKED_GIT_LIMIT \
((1024L * 1024L) * (size_t)(sizeof(void*) >= 8 ? (32 * 1024L * 1024L) : 256))
+#ifdef _MSC_VER
+ /*
+ * When traversing into too-deep trees, Visual C-compiled Git seems to
+ * run into some internal stack overflow detection in the
+ * `RtlpAllocateHeap()` function that is called from within
+ * `git_inflate_init()`'s call tree. The following value seems to be
+ * low enough to avoid that by letting Git exit with an error before
+ * the stack overflow can occur.
+ */
+#define DEFAULT_MAX_ALLOWED_TREE_DEPTH 512
+#elif defined(GIT_WINDOWS_NATIVE) && defined(__clang__) && defined(__aarch64__)
+ /*
+ * Similar to Visual C, it seems that on Windows/ARM64 the clang-based
+ * builds have a smaller stack space available. When running out of
+ * that stack space, a `STATUS_STACK_OVERFLOW` is produced. When the
+ * Git command was run from an MSYS2 Bash, this unfortunately results
+ * in an exit code 127. Let's prevent that by lowering the maximal
+ * tree depth; This value seems to be low enough.
+ */
+#define DEFAULT_MAX_ALLOWED_TREE_DEPTH 1280
+#else
+#define DEFAULT_MAX_ALLOWED_TREE_DEPTH 2048
+#endif
+
int git_open_cloexec(const char *name, int flags);
#define git_open(name) git_open_cloexec(name, O_RDONLY)
diff --git a/http-backend.c b/http-backend.c
index 24f0dc119a..0122146df6 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -144,8 +144,10 @@ static NORETURN void not_found(struct strbuf *hdr, const char *err, ...)
end_headers(hdr);
va_start(params, err);
- if (err && *err)
+ if (err && *err) {
vfprintf(stderr, err, params);
+ putc('\n', stderr);
+ }
va_end(params);
exit(0);
}
@@ -160,8 +162,10 @@ static NORETURN void forbidden(struct strbuf *hdr, const char *err, ...)
end_headers(hdr);
va_start(params, err);
- if (err && *err)
+ if (err && *err) {
vfprintf(stderr, err, params);
+ putc('\n', stderr);
+ }
va_end(params);
exit(0);
}
diff --git a/http-push.c b/http-push.c
index 60a9b75620..cc0f809346 100644
--- a/http-push.c
+++ b/http-push.c
@@ -1311,7 +1311,7 @@ static struct object_list **process_tree(struct tree *tree,
if (obj->flags & (UNINTERESTING | SEEN))
return p;
- if (parse_tree(tree) < 0)
+ if (repo_parse_tree(the_repository, tree) < 0)
die("bad tree object %s", oid_to_hex(&obj->oid));
obj->flags |= SEEN;
diff --git a/http.c b/http.c
index 41f850db16..7815f144de 100644
--- a/http.c
+++ b/http.c
@@ -2544,7 +2544,7 @@ void http_install_packfile(struct packed_git *p,
struct packfile_list *list_to_remove_from)
{
packfile_list_remove(list_to_remove_from, p);
- packfile_store_add_pack(the_repository->objects->packfiles, p);
+ packfile_store_add_pack(the_repository->objects->sources->packfiles, p);
}
struct http_pack_request *new_http_pack_request(
diff --git a/list-objects.c b/list-objects.c
index 42c17d9573..91b23e22f7 100644
--- a/list-objects.c
+++ b/list-objects.c
@@ -167,10 +167,10 @@ static void process_tree(struct traversal_context *ctx,
!revs->include_check_obj(&tree->object, revs->include_check_data))
return;
- if (ctx->depth > max_allowed_tree_depth)
+ if (ctx->depth > revs->repo->settings.max_allowed_tree_depth)
die("exceeded maximum allowed tree depth");
- failed_parse = parse_tree_gently(tree, 1);
+ failed_parse = repo_parse_tree_gently(the_repository, tree, 1);
if (failed_parse) {
if (revs->ignore_missing_links)
return;
diff --git a/lockfile.c b/lockfile.c
index 1d5ed01682..67082a9caa 100644
--- a/lockfile.c
+++ b/lockfile.c
@@ -19,14 +19,14 @@ static void trim_last_path_component(struct strbuf *path)
int i = path->len;
/* back up past trailing slashes, if any */
- while (i && path->buf[i - 1] == '/')
+ while (i && is_dir_sep(path->buf[i - 1]))
i--;
/*
* then go backwards until a slash, or the beginning of the
* string
*/
- while (i && path->buf[i - 1] != '/')
+ while (i && !is_dir_sep(path->buf[i - 1]))
i--;
strbuf_setlen(path, i);
diff --git a/merge-ort.c b/merge-ort.c
index 9e85a5e60a..e80e4f735a 100644
--- a/merge-ort.c
+++ b/merge-ort.c
@@ -1502,11 +1502,44 @@ static void resolve_trivial_directory_merge(struct conflict_info *ci, int side)
VERIFY_CI(ci);
assert((side == 1 && ci->match_mask == 5) ||
(side == 2 && ci->match_mask == 3));
+
+ /*
+ * Since ci->stages[0] matches ci->stages[3-side], resolve merge in
+ * favor of ci->stages[side].
+ */
oidcpy(&ci->merged.result.oid, &ci->stages[side].oid);
ci->merged.result.mode = ci->stages[side].mode;
ci->merged.is_null = is_null_oid(&ci->stages[side].oid);
+
+ /*
+ * Because we resolved in favor of "side", we are no longer
+ * considering the paths which matched (i.e. had the same hash) any
+ * more. Strip the matching paths from both dirmask & filemask.
+ * Another consequence of merging in favor of side is that we can no
+ * longer have a directory/file conflict either..but there's a slight
+ * nuance we consider before clearing it.
+ *
+ * In most cases, resolving in favor of the other side means there's
+ * no conflict at all, but if we had a directory/file conflict to
+ * start, and the directory is resolved away, the remaining file could
+ * still be part of a rename. If the remaining file is part of a
+ * rename, then it may also be part of a rename conflict (e.g.
+ * rename/delete or rename/rename(1to2)), so we can't
+ * mark it as a clean merge if we started with a directory/file
+ * conflict and still have a file left.
+ *
+ * In contrast, if we started with a directory/file conflict and
+ * still have a directory left, no file under that directory can be
+ * part of a rename, otherwise we would have had to recurse into the
+ * directory and would have never ended up within
+ * resolve_trivial_directory_merge() for that directory.
+ */
+ ci->dirmask &= (~ci->match_mask);
+ ci->filemask &= (~ci->match_mask);
+ assert(!ci->filemask || !ci->dirmask);
ci->match_mask = 0;
- ci->merged.clean = 1; /* (ci->filemask == 0); */
+ ci->merged.clean = !ci->df_conflict || ci->dirmask;
+ ci->df_conflict = 0;
}
static int handle_deferred_entries(struct merge_options *opt,
@@ -1699,9 +1732,9 @@ static int collect_merge_info(struct merge_options *opt,
info.data = opt;
info.show_all_errors = 1;
- if (parse_tree(merge_base) < 0 ||
- parse_tree(side1) < 0 ||
- parse_tree(side2) < 0)
+ if (repo_parse_tree(the_repository, merge_base) < 0 ||
+ repo_parse_tree(the_repository, side1) < 0 ||
+ repo_parse_tree(the_repository, side2) < 0)
return -1;
init_tree_desc(t + 0, &merge_base->object.oid,
merge_base->buffer, merge_base->size);
@@ -4586,10 +4619,10 @@ static int checkout(struct merge_options *opt,
unpack_opts.verbose_update = (opt->verbosity > 2);
unpack_opts.fn = twoway_merge;
unpack_opts.preserve_ignored = 0; /* FIXME: !opts->overwrite_ignore */
- if (parse_tree(prev) < 0)
+ if (repo_parse_tree(the_repository, prev) < 0)
return -1;
init_tree_desc(&trees[0], &prev->object.oid, prev->buffer, prev->size);
- if (parse_tree(next) < 0)
+ if (repo_parse_tree(the_repository, next) < 0)
return -1;
init_tree_desc(&trees[1], &next->object.oid, next->buffer, next->size);
@@ -5247,7 +5280,8 @@ redo:
if (result->clean >= 0) {
if (!opt->mergeability_only) {
- result->tree = parse_tree_indirect(&working_tree_oid);
+ result->tree = repo_parse_tree_indirect(the_repository,
+ &working_tree_oid);
if (!result->tree)
die(_("unable to read tree (%s)"),
oid_to_hex(&working_tree_oid));
diff --git a/merge.c b/merge.c
index 5ecaf508e4..0f5e823e63 100644
--- a/merge.c
+++ b/merge.c
@@ -68,18 +68,18 @@ int checkout_fast_forward(struct repository *r,
memset(&trees, 0, sizeof(trees));
memset(&t, 0, sizeof(t));
- trees[nr_trees] = parse_tree_indirect(head);
+ trees[nr_trees] = repo_parse_tree_indirect(the_repository, head);
if (!trees[nr_trees++]) {
rollback_lock_file(&lock_file);
return -1;
}
- trees[nr_trees] = parse_tree_indirect(remote);
+ trees[nr_trees] = repo_parse_tree_indirect(the_repository, remote);
if (!trees[nr_trees++]) {
rollback_lock_file(&lock_file);
return -1;
}
for (i = 0; i < nr_trees; i++) {
- if (parse_tree(trees[i]) < 0) {
+ if (repo_parse_tree(the_repository, trees[i]) < 0) {
rollback_lock_file(&lock_file);
return -1;
}
diff --git a/midx-write.c b/midx-write.c
index ce459b02c3..6485cb6706 100644
--- a/midx-write.c
+++ b/midx-write.c
@@ -723,9 +723,7 @@ static int add_ref_to_pending(const struct reference *ref, void *cb_data)
}
struct bitmap_commit_cb {
- struct commit **commits;
- size_t commits_nr, commits_alloc;
-
+ struct commit_stack *commits;
struct write_midx_context *ctx;
};
@@ -745,8 +743,7 @@ static void bitmap_show_commit(struct commit *commit, void *_data)
if (pos < 0)
return;
- ALLOC_GROW(data->commits, data->commits_nr + 1, data->commits_alloc);
- data->commits[data->commits_nr++] = commit;
+ commit_stack_push(data->commits, commit);
}
static int read_refs_snapshot(const char *refs_snapshot,
@@ -784,17 +781,15 @@ static int read_refs_snapshot(const char *refs_snapshot,
return 0;
}
-static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr_p,
- const char *refs_snapshot,
- struct write_midx_context *ctx)
+static void find_commits_for_midx_bitmap(struct commit_stack *commits,
+ const char *refs_snapshot,
+ struct write_midx_context *ctx)
{
struct rev_info revs;
- struct bitmap_commit_cb cb = {0};
+ struct bitmap_commit_cb cb = { .commits = commits, .ctx = ctx };
trace2_region_enter("midx", "find_commits_for_midx_bitmap", ctx->repo);
- cb.ctx = ctx;
-
repo_init_revisions(ctx->repo, &revs, NULL);
if (refs_snapshot) {
read_refs_snapshot(refs_snapshot, &revs);
@@ -823,14 +818,10 @@ static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr
die(_("revision walk setup failed"));
traverse_commit_list(&revs, bitmap_show_commit, NULL, &cb);
- if (indexed_commits_nr_p)
- *indexed_commits_nr_p = cb.commits_nr;
release_revisions(&revs);
trace2_region_leave("midx", "find_commits_for_midx_bitmap", ctx->repo);
-
- return cb.commits;
}
static int write_midx_bitmap(struct write_midx_context *ctx,
@@ -1021,6 +1012,20 @@ static bool midx_needs_update(struct multi_pack_index *midx, struct write_midx_c
bool needed = true;
/*
+ * Ensure that we have a valid checksum before consulting the
+ * exisiting MIDX in order to determine if we can avoid an
+ * update.
+ *
+ * This is necessary because the given MIDX is loaded directly
+ * from the object store (because we still compare our proposed
+ * update to any on-disk MIDX regardless of whether or not we
+ * have assigned "ctx.m") and is thus not guaranteed to have a
+ * valid checksum.
+ */
+ if (!midx_checksum_valid(midx))
+ goto out;
+
+ /*
* Ignore incremental updates for now. The assumption is that any
* incremental update would be either empty (in which case we will bail
* out later) or it would actually cover at least one new pack.
@@ -1447,15 +1452,14 @@ static int write_midx_internal(struct odb_source *source,
if (flags & MIDX_WRITE_BITMAP) {
struct packing_data pdata;
- struct commit **commits;
- uint32_t commits_nr;
+ struct commit_stack commits = COMMIT_STACK_INIT;
if (!ctx.entries_nr)
BUG("cannot write a bitmap without any objects");
prepare_midx_packing_data(&pdata, &ctx);
- commits = find_commits_for_midx_bitmap(&commits_nr, refs_snapshot, &ctx);
+ find_commits_for_midx_bitmap(&commits, refs_snapshot, &ctx);
/*
* The previous steps translated the information from
@@ -1466,17 +1470,16 @@ static int write_midx_internal(struct odb_source *source,
FREE_AND_NULL(ctx.entries);
ctx.entries_nr = 0;
- if (write_midx_bitmap(&ctx,
- midx_hash, &pdata, commits, commits_nr,
- flags) < 0) {
+ if (write_midx_bitmap(&ctx, midx_hash, &pdata,
+ commits.items, commits.nr, flags) < 0) {
error(_("could not write multi-pack bitmap"));
clear_packing_data(&pdata);
- free(commits);
+ commit_stack_clear(&commits);
goto cleanup;
}
clear_packing_data(&pdata);
- free(commits);
+ commit_stack_clear(&commits);
}
/*
* NOTE: Do not use ctx.entries beyond this point, since it might
diff --git a/midx.c b/midx.c
index b681b18fc1..a75ea99a0d 100644
--- a/midx.c
+++ b/midx.c
@@ -95,8 +95,8 @@ static int midx_read_object_offsets(const unsigned char *chunk_start,
struct multi_pack_index *get_multi_pack_index(struct odb_source *source)
{
- packfile_store_prepare(source->odb->packfiles);
- return source->midx;
+ packfile_store_prepare(source->packfiles);
+ return source->packfiles->midx;
}
static struct multi_pack_index *load_multi_pack_index_one(struct odb_source *source,
@@ -447,7 +447,6 @@ static uint32_t midx_for_pack(struct multi_pack_index **_m,
int prepare_midx_pack(struct multi_pack_index *m,
uint32_t pack_int_id)
{
- struct repository *r = m->source->odb->repo;
struct strbuf pack_name = STRBUF_INIT;
struct packed_git *p;
@@ -460,7 +459,7 @@ int prepare_midx_pack(struct multi_pack_index *m,
strbuf_addf(&pack_name, "%s/pack/%s", m->source->path,
m->pack_names[pack_int_id]);
- p = packfile_store_load_pack(r->objects->packfiles,
+ p = packfile_store_load_pack(m->source->packfiles,
pack_name.buf, m->source->local);
strbuf_release(&pack_name);
@@ -710,12 +709,12 @@ int prepare_multi_pack_index_one(struct odb_source *source)
if (!r->settings.core_multi_pack_index)
return 0;
- if (source->midx)
+ if (source->packfiles->midx)
return 1;
- source->midx = load_multi_pack_index(source);
+ source->packfiles->midx = load_multi_pack_index(source);
- return !!source->midx;
+ return !!source->packfiles->midx;
}
int midx_checksum_valid(struct multi_pack_index *m)
@@ -804,9 +803,9 @@ void clear_midx_file(struct repository *r)
struct odb_source *source;
for (source = r->objects->sources; source; source = source->next) {
- if (source->midx)
- close_midx(source->midx);
- source->midx = NULL;
+ if (source->packfiles->midx)
+ close_midx(source->packfiles->midx);
+ source->packfiles->midx = NULL;
}
}
diff --git a/object-file.c b/object-file.c
index 6280e42f34..e7e4c3348f 100644
--- a/object-file.c
+++ b/object-file.c
@@ -416,19 +416,16 @@ int odb_source_loose_read_object_info(struct odb_source *source,
const struct object_id *oid,
struct object_info *oi, int flags)
{
- int status = 0;
+ int ret;
int fd;
unsigned long mapsize;
const char *path;
- void *map;
- git_zstream stream;
+ void *map = NULL;
+ git_zstream stream, *stream_to_end = NULL;
char hdr[MAX_HEADER_LEN];
unsigned long size_scratch;
enum object_type type_scratch;
- if (oi && oi->delta_base_oid)
- oidclr(oi->delta_base_oid, source->odb->repo->hash_algo);
-
/*
* If we don't care about type or size, then we don't
* need to look inside the object at all. Note that we
@@ -439,71 +436,101 @@ int odb_source_loose_read_object_info(struct odb_source *source,
*/
if (!oi || (!oi->typep && !oi->sizep && !oi->contentp)) {
struct stat st;
- if ((!oi || !oi->disk_sizep) && (flags & OBJECT_INFO_QUICK))
- return quick_has_loose(source->loose, oid) ? 0 : -1;
- if (stat_loose_object(source->loose, oid, &st, &path) < 0)
- return -1;
+
+ if ((!oi || !oi->disk_sizep) && (flags & OBJECT_INFO_QUICK)) {
+ ret = quick_has_loose(source->loose, oid) ? 0 : -1;
+ goto out;
+ }
+
+ if (stat_loose_object(source->loose, oid, &st, &path) < 0) {
+ ret = -1;
+ goto out;
+ }
+
if (oi && oi->disk_sizep)
*oi->disk_sizep = st.st_size;
- return 0;
+
+ ret = 0;
+ goto out;
}
fd = open_loose_object(source->loose, oid, &path);
if (fd < 0) {
if (errno != ENOENT)
error_errno(_("unable to open loose object %s"), oid_to_hex(oid));
- return -1;
+ ret = -1;
+ goto out;
}
- map = map_fd(fd, path, &mapsize);
- if (!map)
- return -1;
- if (!oi->sizep)
- oi->sizep = &size_scratch;
- if (!oi->typep)
- oi->typep = &type_scratch;
+ map = map_fd(fd, path, &mapsize);
+ if (!map) {
+ ret = -1;
+ goto out;
+ }
if (oi->disk_sizep)
*oi->disk_sizep = mapsize;
+ stream_to_end = &stream;
+
switch (unpack_loose_header(&stream, map, mapsize, hdr, sizeof(hdr))) {
case ULHR_OK:
- if (parse_loose_header(hdr, oi) < 0)
- status = error(_("unable to parse %s header"), oid_to_hex(oid));
- else if (*oi->typep < 0)
+ if (!oi->sizep)
+ oi->sizep = &size_scratch;
+ if (!oi->typep)
+ oi->typep = &type_scratch;
+
+ if (parse_loose_header(hdr, oi) < 0) {
+ ret = error(_("unable to parse %s header"), oid_to_hex(oid));
+ goto corrupt;
+ }
+
+ if (*oi->typep < 0)
die(_("invalid object type"));
- if (!oi->contentp)
- break;
- *oi->contentp = unpack_loose_rest(&stream, hdr, *oi->sizep, oid);
- if (*oi->contentp)
- goto cleanup;
+ if (oi->contentp) {
+ *oi->contentp = unpack_loose_rest(&stream, hdr, *oi->sizep, oid);
+ if (!*oi->contentp) {
+ ret = -1;
+ goto corrupt;
+ }
+ }
- status = -1;
break;
case ULHR_BAD:
- status = error(_("unable to unpack %s header"),
- oid_to_hex(oid));
- break;
+ ret = error(_("unable to unpack %s header"),
+ oid_to_hex(oid));
+ goto corrupt;
case ULHR_TOO_LONG:
- status = error(_("header for %s too long, exceeds %d bytes"),
- oid_to_hex(oid), MAX_HEADER_LEN);
- break;
+ ret = error(_("header for %s too long, exceeds %d bytes"),
+ oid_to_hex(oid), MAX_HEADER_LEN);
+ goto corrupt;
}
- if (status && (flags & OBJECT_INFO_DIE_IF_CORRUPT))
+ ret = 0;
+
+corrupt:
+ if (ret && (flags & OBJECT_INFO_DIE_IF_CORRUPT))
die(_("loose object %s (stored in %s) is corrupt"),
oid_to_hex(oid), path);
-cleanup:
- git_inflate_end(&stream);
- munmap(map, mapsize);
- if (oi->sizep == &size_scratch)
- oi->sizep = NULL;
- if (oi->typep == &type_scratch)
- oi->typep = NULL;
- oi->whence = OI_LOOSE;
- return status;
+out:
+ if (stream_to_end)
+ git_inflate_end(stream_to_end);
+ if (map)
+ munmap(map, mapsize);
+ if (oi) {
+ if (oi->sizep == &size_scratch)
+ oi->sizep = NULL;
+ if (oi->typep == &type_scratch)
+ oi->typep = NULL;
+ if (oi->delta_base_oid)
+ oidclr(oi->delta_base_oid, source->odb->repo->hash_algo);
+ if (!ret)
+ oi->whence = OI_LOOSE;
+ }
+
+ return ret;
}
static void hash_object_body(const struct git_hash_algo *algo, struct git_hash_ctx *c,
diff --git a/object-name.c b/object-name.c
index fed5de5153..8b862c124e 100644
--- a/object-name.c
+++ b/object-name.c
@@ -449,7 +449,7 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)
} else if (type == OBJ_TAG) {
struct tag *tag = lookup_tag(ds->repo, oid);
- if (!parse_tag(tag) && tag->tag) {
+ if (!parse_tag(ds->repo, tag) && tag->tag) {
/*
* TRANSLATORS: This is a line of ambiguous
* tag object output. E.g.:
diff --git a/odb.c b/odb.c
index ffd78e1c46..ac70b6a099 100644
--- a/odb.c
+++ b/odb.c
@@ -229,6 +229,7 @@ static struct odb_source *odb_source_new(struct object_database *odb,
source->local = local;
source->path = xstrdup(path);
source->loose = odb_source_loose_new(source);
+ source->packfiles = packfile_store_new(source);
return source;
}
@@ -376,6 +377,7 @@ static void odb_source_free(struct odb_source *source)
{
free(source->path);
odb_source_loose_free(source->loose);
+ packfile_store_free(source->packfiles);
free(source);
}
@@ -710,19 +712,19 @@ static int do_oid_object_info_extended(struct object_database *odb,
while (1) {
struct odb_source *source;
- if (!packfile_store_read_object_info(odb->packfiles, real, oi, flags))
- return 0;
-
/* Most likely it's a loose object. */
- for (source = odb->sources; source; source = source->next)
- if (!odb_source_loose_read_object_info(source, real, oi, flags))
+ for (source = odb->sources; source; source = source->next) {
+ if (!packfile_store_read_object_info(source->packfiles, real, oi, flags) ||
+ !odb_source_loose_read_object_info(source, real, oi, flags))
return 0;
+ }
/* Not a loose object; someone else may have just packed it. */
if (!(flags & OBJECT_INFO_QUICK)) {
odb_reprepare(odb->repo->objects);
- if (!packfile_store_read_object_info(odb->packfiles, real, oi, flags))
- return 0;
+ for (source = odb->sources; source; source = source->next)
+ if (!packfile_store_read_object_info(source->packfiles, real, oi, flags))
+ return 0;
}
/*
@@ -981,13 +983,14 @@ int odb_freshen_object(struct object_database *odb,
{
struct odb_source *source;
- if (packfile_store_freshen_object(odb->packfiles, oid))
- return 1;
-
odb_prepare_alternates(odb);
- for (source = odb->sources; source; source = source->next)
+ for (source = odb->sources; source; source = source->next) {
+ if (packfile_store_freshen_object(source->packfiles, oid))
+ return 1;
+
if (odb_source_loose_freshen_object(source, oid))
return 1;
+ }
return 0;
}
@@ -1062,7 +1065,6 @@ struct object_database *odb_new(struct repository *repo,
memset(o, 0, sizeof(*o));
o->repo = repo;
- o->packfiles = packfile_store_new(o);
pthread_mutex_init(&o->replace_mutex, NULL);
string_list_init_dup(&o->submodule_source_paths);
@@ -1082,15 +1084,8 @@ struct object_database *odb_new(struct repository *repo,
void odb_close(struct object_database *o)
{
struct odb_source *source;
-
- packfile_store_close(o->packfiles);
-
- for (source = o->sources; source; source = source->next) {
- if (source->midx)
- close_midx(source->midx);
- source->midx = NULL;
- }
-
+ for (source = o->sources; source; source = source->next)
+ packfile_store_close(source->packfiles);
close_commit_graph(o);
}
@@ -1117,14 +1112,13 @@ void odb_free(struct object_database *o)
oidmap_clear(&o->replace_map, 1);
pthread_mutex_destroy(&o->replace_mutex);
+ odb_close(o);
odb_free_sources(o);
for (size_t i = 0; i < o->cached_object_nr; i++)
free((char *) o->cached_objects[i].value.buf);
free(o->cached_objects);
- odb_close(o);
- packfile_store_free(o->packfiles);
string_list_clear(&o->submodule_source_paths, 0);
chdir_notify_unregister(NULL, odb_update_commondir, o);
@@ -1147,13 +1141,13 @@ void odb_reprepare(struct object_database *o)
o->loaded_alternates = 0;
odb_prepare_alternates(o);
- for (source = o->sources; source; source = source->next)
+ for (source = o->sources; source; source = source->next) {
odb_source_loose_reprepare(source);
+ packfile_store_reprepare(source->packfiles);
+ }
o->approximate_object_count_valid = 0;
- packfile_store_reprepare(o->packfiles);
-
obj_read_unlock();
}
diff --git a/odb.h b/odb.h
index 014cd9585a..bab07755f4 100644
--- a/odb.h
+++ b/odb.h
@@ -51,12 +51,8 @@ struct odb_source {
/* Private state for loose objects. */
struct odb_source_loose *loose;
- /*
- * private data
- *
- * should only be accessed directly by packfile.c and midx.c
- */
- struct multi_pack_index *midx;
+ /* Should only be accessed directly by packfile.c and midx.c. */
+ struct packfile_store *packfiles;
/*
* Figure out whether this is the local source of the owning
@@ -128,9 +124,6 @@ struct object_database {
struct commit_graph *commit_graph;
unsigned commit_graph_attempted : 1; /* if loading has been attempted */
- /* Should only be accessed directly by packfile.c and midx.c. */
- struct packfile_store *packfiles;
-
/*
* This is meant to hold a *small* number of objects that you would
* want odb_read_object() to be able to return, but yet you do not want
@@ -330,7 +323,6 @@ struct object_info {
OI_CACHED,
OI_LOOSE,
OI_PACKED,
- OI_DBCACHED
} whence;
union {
/*
@@ -344,7 +336,12 @@ struct object_info {
struct {
struct packed_git *pack;
off_t offset;
- unsigned int is_delta;
+ enum packed_object_type {
+ PACKED_OBJECT_TYPE_UNKNOWN,
+ PACKED_OBJECT_TYPE_FULL,
+ PACKED_OBJECT_TYPE_OFS_DELTA,
+ PACKED_OBJECT_TYPE_REF_DELTA,
+ } type;
} packed;
} u;
};
diff --git a/odb/streaming.c b/odb/streaming.c
index 745cd486fb..4a4474f891 100644
--- a/odb/streaming.c
+++ b/odb/streaming.c
@@ -185,13 +185,12 @@ static int istream_source(struct odb_read_stream **out,
{
struct odb_source *source;
- if (!packfile_store_read_object_stream(out, odb->packfiles, oid))
- return 0;
-
odb_prepare_alternates(odb);
- for (source = odb->sources; source; source = source->next)
- if (!odb_source_loose_read_object_stream(out, source, oid))
+ for (source = odb->sources; source; source = source->next) {
+ if (!packfile_store_read_object_stream(out, source->packfiles, oid) ||
+ !odb_source_loose_read_object_stream(out, source, oid))
return 0;
+ }
return open_istream_incore(out, odb, oid);
}
diff --git a/pack-bitmap-write.c b/pack-bitmap-write.c
index 4404921521..625fa92b2f 100644
--- a/pack-bitmap-write.c
+++ b/pack-bitmap-write.c
@@ -315,8 +315,7 @@ define_commit_slab(bb_data, struct bb_commit);
struct bitmap_builder {
struct bb_data data;
- struct commit **commits;
- size_t commits_nr, commits_alloc;
+ struct commit_stack commits;
};
static void bitmap_builder_init(struct bitmap_builder *bb,
@@ -329,8 +328,8 @@ static void bitmap_builder_init(struct bitmap_builder *bb,
struct commit_list *r;
unsigned int i, num_maximal = 0;
- memset(bb, 0, sizeof(*bb));
init_bb_data(&bb->data);
+ commit_stack_init(&bb->commits);
reset_revision_walk();
repo_init_revisions(writer->to_pack->repo, &revs, NULL);
@@ -390,8 +389,7 @@ static void bitmap_builder_init(struct bitmap_builder *bb,
if (c_ent->maximal) {
num_maximal++;
- ALLOC_GROW(bb->commits, bb->commits_nr + 1, bb->commits_alloc);
- bb->commits[bb->commits_nr++] = commit;
+ commit_stack_push(&bb->commits, commit);
}
if (p) {
@@ -438,8 +436,7 @@ next:
}
for (r = reusable; r; r = r->next) {
- ALLOC_GROW(bb->commits, bb->commits_nr + 1, bb->commits_alloc);
- bb->commits[bb->commits_nr++] = r->item;
+ commit_stack_push(&bb->commits, r->item);
}
trace2_data_intmax("pack-bitmap-write", writer->repo,
@@ -454,8 +451,7 @@ next:
static void bitmap_builder_clear(struct bitmap_builder *bb)
{
deep_clear_bb_data(&bb->data, clear_bb_commit);
- free(bb->commits);
- bb->commits_nr = bb->commits_alloc = 0;
+ commit_stack_clear(&bb->commits);
}
static int fill_bitmap_tree(struct bitmap_writer *writer,
@@ -478,7 +474,7 @@ static int fill_bitmap_tree(struct bitmap_writer *writer,
return 0;
bitmap_set(bitmap, pos);
- if (parse_tree(tree) < 0)
+ if (repo_parse_tree(writer->repo, tree) < 0)
die("unable to load tree object %s",
oid_to_hex(&tree->object.oid));
init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
@@ -630,8 +626,8 @@ int bitmap_writer_build(struct bitmap_writer *writer)
mapping = NULL;
bitmap_builder_init(&bb, writer, old_bitmap);
- for (i = bb.commits_nr; i > 0; i--) {
- struct commit *commit = bb.commits[i-1];
+ for (i = bb.commits.nr; i > 0; i--) {
+ struct commit *commit = bb.commits.items[i-1];
struct bb_commit *ent = bb_data_at(&bb.data, commit);
struct commit *child;
int reused = 0;
diff --git a/pack-bitmap.c b/pack-bitmap.c
index 8ca79725b1..972203f12b 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -1876,8 +1876,7 @@ static unsigned long get_size_by_pos(struct bitmap_index *bitmap_git,
ofs = pack_pos_to_offset(pack, pos);
}
- if (packed_object_info(bitmap_repo(bitmap_git), pack, ofs,
- &oi) < 0) {
+ if (packed_object_info(pack, ofs, &oi) < 0) {
struct object_id oid;
nth_bitmap_object_oid(bitmap_git, &oid,
pack_pos_to_index(pack, pos));
diff --git a/packfile.c b/packfile.c
index 23a7f8a191..402c3b5dc7 100644
--- a/packfile.c
+++ b/packfile.c
@@ -355,16 +355,17 @@ static void scan_windows(struct packed_git *p,
}
}
-static int unuse_one_window(struct packed_git *current)
+static int unuse_one_window(struct object_database *odb)
{
+ struct odb_source *source;
struct packfile_list_entry *e;
struct packed_git *lru_p = NULL;
struct pack_window *lru_w = NULL, *lru_l = NULL;
- if (current)
- scan_windows(current, &lru_p, &lru_w, &lru_l);
- for (e = current->repo->objects->packfiles->packs.head; e; e = e->next)
- scan_windows(e->pack, &lru_p, &lru_w, &lru_l);
+ for (source = odb->sources; source; source = source->next)
+ for (e = source->packfiles->packs.head; e; e = e->next)
+ scan_windows(e->pack, &lru_p, &lru_w, &lru_l);
+
if (lru_p) {
munmap(lru_w->base, lru_w->len);
pack_mapped -= lru_w->len;
@@ -529,15 +530,18 @@ static void find_lru_pack(struct packed_git *p, struct packed_git **lru_p, struc
static int close_one_pack(struct repository *r)
{
+ struct odb_source *source;
struct packfile_list_entry *e;
struct packed_git *lru_p = NULL;
struct pack_window *mru_w = NULL;
int accept_windows_inuse = 1;
- for (e = r->objects->packfiles->packs.head; e; e = e->next) {
- if (e->pack->pack_fd == -1)
- continue;
- find_lru_pack(e->pack, &lru_p, &mru_w, &accept_windows_inuse);
+ for (source = r->objects->sources; source; source = source->next) {
+ for (e = source->packfiles->packs.head; e; e = e->next) {
+ if (e->pack->pack_fd == -1)
+ continue;
+ find_lru_pack(e->pack, &lru_p, &mru_w, &accept_windows_inuse);
+ }
}
if (lru_p)
@@ -740,8 +744,8 @@ unsigned char *use_pack(struct packed_git *p,
win->len = (size_t)len;
pack_mapped += win->len;
- while (settings->packed_git_limit < pack_mapped
- && unuse_one_window(p))
+ while (settings->packed_git_limit < pack_mapped &&
+ unuse_one_window(p->repo->objects))
; /* nothing */
win->base = xmmap_gently(NULL, win->len,
PROT_READ, MAP_PRIVATE,
@@ -876,7 +880,7 @@ struct packed_git *packfile_store_load_pack(struct packfile_store *store,
p = strmap_get(&store->packs_by_path, key.buf);
if (!p) {
- p = add_packed_git(store->odb->repo, idx_path,
+ p = add_packed_git(store->source->odb->repo, idx_path,
strlen(idx_path), local);
if (p)
packfile_store_add_pack(store, p);
@@ -975,10 +979,8 @@ void for_each_file_in_pack_dir(const char *objdir,
}
struct prepare_pack_data {
- struct repository *r;
+ struct odb_source *source;
struct string_list *garbage;
- int local;
- struct multi_pack_index *m;
};
static void prepare_pack(const char *full_name, size_t full_name_len,
@@ -988,10 +990,11 @@ static void prepare_pack(const char *full_name, size_t full_name_len,
size_t base_len = full_name_len;
if (strip_suffix_mem(full_name, &base_len, ".idx") &&
- !(data->m && midx_contains_pack(data->m, file_name))) {
+ !(data->source->packfiles->midx &&
+ midx_contains_pack(data->source->packfiles->midx, file_name))) {
char *trimmed_path = xstrndup(full_name, full_name_len);
- packfile_store_load_pack(data->r->objects->packfiles,
- trimmed_path, data->local);
+ packfile_store_load_pack(data->source->packfiles,
+ trimmed_path, data->source->local);
free(trimmed_path);
}
@@ -1020,10 +1023,8 @@ static void prepare_packed_git_one(struct odb_source *source)
{
struct string_list garbage = STRING_LIST_INIT_DUP;
struct prepare_pack_data data = {
- .m = source->midx,
- .r = source->odb->repo,
+ .source = source,
.garbage = &garbage,
- .local = source->local,
};
for_each_file_in_pack_dir(source->path, prepare_pack, &data);
@@ -1063,16 +1064,11 @@ static int sort_pack(const struct packfile_list_entry *a,
void packfile_store_prepare(struct packfile_store *store)
{
- struct odb_source *source;
-
if (store->initialized)
return;
- odb_prepare_alternates(store->odb);
- for (source = store->odb->sources; source; source = source->next) {
- prepare_multi_pack_index_one(source);
- prepare_packed_git_one(source);
- }
+ prepare_multi_pack_index_one(store->source);
+ prepare_packed_git_one(store->source);
sort_packs(&store->packs.head, sort_pack);
for (struct packfile_list_entry *e = store->packs.head; e; e = e->next)
@@ -1092,10 +1088,8 @@ struct packfile_list_entry *packfile_store_get_packs(struct packfile_store *stor
{
packfile_store_prepare(store);
- for (struct odb_source *source = store->odb->sources; source; source = source->next) {
- struct multi_pack_index *m = source->midx;
- if (!m)
- continue;
+ if (store->midx) {
+ struct multi_pack_index *m = store->midx;
for (uint32_t i = 0; i < m->num_packs + m->num_packs_in_base; i++)
prepare_midx_pack(m, i);
}
@@ -1250,11 +1244,15 @@ void mark_bad_packed_object(struct packed_git *p, const struct object_id *oid)
const struct packed_git *has_packed_and_bad(struct repository *r,
const struct object_id *oid)
{
- struct packfile_list_entry *e;
+ struct odb_source *source;
+
+ for (source = r->objects->sources; source; source = source->next) {
+ struct packfile_list_entry *e;
+ for (e = source->packfiles->packs.head; e; e = e->next)
+ if (oidset_contains(&e->pack->bad_objects, oid))
+ return e->pack;
+ }
- for (e = r->objects->packfiles->packs.head; e; e = e->next)
- if (oidset_contains(&e->pack->bad_objects, oid))
- return e->pack;
return NULL;
}
@@ -1580,24 +1578,25 @@ static void add_delta_base_cache(struct packed_git *p, off_t base_offset,
hashmap_add(&delta_base_cache, &ent->ent);
}
-int packed_object_info(struct repository *r, struct packed_git *p,
+int packed_object_info(struct packed_git *p,
off_t obj_offset, struct object_info *oi)
{
struct pack_window *w_curs = NULL;
unsigned long size;
off_t curpos = obj_offset;
- enum object_type type;
+ enum object_type type = OBJ_NONE;
+ int ret;
/*
* We always get the representation type, but only convert it to
* a "real" type later if the caller is interested.
*/
if (oi->contentp) {
- *oi->contentp = cache_or_unpack_entry(r, p, obj_offset, oi->sizep,
+ *oi->contentp = cache_or_unpack_entry(p->repo, p, obj_offset, oi->sizep,
&type);
if (!*oi->contentp)
type = OBJ_BAD;
- } else {
+ } else if (oi->sizep || oi->typep || oi->delta_base_oid) {
type = unpack_object_header(p, &w_curs, &curpos, &size);
}
@@ -1607,12 +1606,12 @@ int packed_object_info(struct repository *r, struct packed_git *p,
off_t base_offset = get_delta_base(p, &w_curs, &tmp_pos,
type, obj_offset);
if (!base_offset) {
- type = OBJ_BAD;
+ ret = -1;
goto out;
}
*oi->sizep = get_size_from_delta(p, &w_curs, tmp_pos);
if (*oi->sizep == 0) {
- type = OBJ_BAD;
+ ret = -1;
goto out;
}
} else {
@@ -1625,7 +1624,7 @@ int packed_object_info(struct repository *r, struct packed_git *p,
if (offset_to_pack_pos(p, obj_offset, &pos) < 0) {
error("could not find object at offset %"PRIuMAX" "
"in pack %s", (uintmax_t)obj_offset, p->pack_name);
- type = OBJ_BAD;
+ ret = -1;
goto out;
}
@@ -1634,12 +1633,12 @@ int packed_object_info(struct repository *r, struct packed_git *p,
if (oi->typep) {
enum object_type ptot;
- ptot = packed_to_object_type(r, p, obj_offset,
+ ptot = packed_to_object_type(p->repo, p, obj_offset,
type, &w_curs, curpos);
if (oi->typep)
*oi->typep = ptot;
if (ptot < 0) {
- type = OBJ_BAD;
+ ret = -1;
goto out;
}
}
@@ -1649,19 +1648,37 @@ int packed_object_info(struct repository *r, struct packed_git *p,
if (get_delta_base_oid(p, &w_curs, curpos,
oi->delta_base_oid,
type, obj_offset) < 0) {
- type = OBJ_BAD;
+ ret = -1;
goto out;
}
} else
oidclr(oi->delta_base_oid, p->repo->hash_algo);
}
- oi->whence = in_delta_base_cache(p, obj_offset) ? OI_DBCACHED :
- OI_PACKED;
+ oi->whence = OI_PACKED;
+ oi->u.packed.offset = obj_offset;
+ oi->u.packed.pack = p;
+
+ switch (type) {
+ case OBJ_NONE:
+ oi->u.packed.type = PACKED_OBJECT_TYPE_UNKNOWN;
+ break;
+ case OBJ_REF_DELTA:
+ oi->u.packed.type = PACKED_OBJECT_TYPE_REF_DELTA;
+ break;
+ case OBJ_OFS_DELTA:
+ oi->u.packed.type = PACKED_OBJECT_TYPE_OFS_DELTA;
+ break;
+ default:
+ oi->u.packed.type = PACKED_OBJECT_TYPE_FULL;
+ break;
+ }
+
+ ret = 0;
out:
unuse_pack(&w_curs);
- return type;
+ return ret;
}
static void *unpack_compressed_entry(struct packed_git *p,
@@ -2090,30 +2107,26 @@ static int fill_pack_entry(const struct object_id *oid,
return 1;
}
-static int find_pack_entry(struct repository *r,
+static int find_pack_entry(struct packfile_store *store,
const struct object_id *oid,
struct pack_entry *e)
{
struct packfile_list_entry *l;
- packfile_store_prepare(r->objects->packfiles);
-
- for (struct odb_source *source = r->objects->sources; source; source = source->next)
- if (source->midx && fill_midx_entry(source->midx, oid, e))
- return 1;
-
- if (!r->objects->packfiles->packs.head)
- return 0;
+ packfile_store_prepare(store);
+ if (store->midx && fill_midx_entry(store->midx, oid, e))
+ return 1;
- for (l = r->objects->packfiles->packs.head; l; l = l->next) {
+ for (l = store->packs.head; l; l = l->next) {
struct packed_git *p = l->pack;
if (!p->multi_pack_index && fill_pack_entry(oid, e, p)) {
- if (!r->objects->packfiles->skip_mru_updates)
- packfile_list_prepend(&r->objects->packfiles->packs, p);
+ if (!store->skip_mru_updates)
+ packfile_list_prepend(&store->packs, p);
return 1;
}
}
+
return 0;
}
@@ -2121,7 +2134,7 @@ int packfile_store_freshen_object(struct packfile_store *store,
const struct object_id *oid)
{
struct pack_entry e;
- if (!find_pack_entry(store->odb->repo, oid, &e))
+ if (!find_pack_entry(store, oid, &e))
return 0;
if (e.p->is_cruft)
return 0;
@@ -2139,9 +2152,9 @@ int packfile_store_read_object_info(struct packfile_store *store,
unsigned flags UNUSED)
{
struct pack_entry e;
- int rtype;
+ int ret;
- if (!find_pack_entry(store->odb->repo, oid, &e))
+ if (!find_pack_entry(store, oid, &e))
return 1;
/*
@@ -2151,41 +2164,35 @@ int packfile_store_read_object_info(struct packfile_store *store,
if (!oi)
return 0;
- rtype = packed_object_info(store->odb->repo, e.p, e.offset, oi);
- if (rtype < 0) {
+ ret = packed_object_info(e.p, e.offset, oi);
+ if (ret < 0) {
mark_bad_packed_object(e.p, oid);
return -1;
}
- if (oi->whence == OI_PACKED) {
- oi->u.packed.offset = e.offset;
- oi->u.packed.pack = e.p;
- oi->u.packed.is_delta = (rtype == OBJ_REF_DELTA ||
- rtype == OBJ_OFS_DELTA);
- }
-
return 0;
}
-static void maybe_invalidate_kept_pack_cache(struct repository *r,
+static void maybe_invalidate_kept_pack_cache(struct packfile_store *store,
unsigned flags)
{
- if (!r->objects->packfiles->kept_cache.packs)
+ if (!store->kept_cache.packs)
return;
- if (r->objects->packfiles->kept_cache.flags == flags)
+ if (store->kept_cache.flags == flags)
return;
- FREE_AND_NULL(r->objects->packfiles->kept_cache.packs);
- r->objects->packfiles->kept_cache.flags = 0;
+ FREE_AND_NULL(store->kept_cache.packs);
+ store->kept_cache.flags = 0;
}
-struct packed_git **kept_pack_cache(struct repository *r, unsigned flags)
+struct packed_git **packfile_store_get_kept_pack_cache(struct packfile_store *store,
+ unsigned flags)
{
- maybe_invalidate_kept_pack_cache(r, flags);
+ maybe_invalidate_kept_pack_cache(store, flags);
- if (!r->objects->packfiles->kept_cache.packs) {
+ if (!store->kept_cache.packs) {
struct packed_git **packs = NULL;
+ struct packfile_list_entry *e;
size_t nr = 0, alloc = 0;
- struct packed_git *p;
/*
* We want "all" packs here, because we need to cover ones that
@@ -2195,9 +2202,11 @@ struct packed_git **kept_pack_cache(struct repository *r, unsigned flags)
* covers, one kept and one not kept, but the midx returns only
* the non-kept version.
*/
- repo_for_each_pack(r, p) {
- if ((p->pack_keep && (flags & ON_DISK_KEEP_PACKS)) ||
- (p->pack_keep_in_core && (flags & IN_CORE_KEEP_PACKS))) {
+ for (e = packfile_store_get_packs(store); e; e = e->next) {
+ struct packed_git *p = e->pack;
+
+ if ((p->pack_keep && (flags & KEPT_PACK_ON_DISK)) ||
+ (p->pack_keep_in_core && (flags & KEPT_PACK_IN_CORE))) {
ALLOC_GROW(packs, nr + 1, alloc);
packs[nr++] = p;
}
@@ -2205,40 +2214,47 @@ struct packed_git **kept_pack_cache(struct repository *r, unsigned flags)
ALLOC_GROW(packs, nr + 1, alloc);
packs[nr] = NULL;
- r->objects->packfiles->kept_cache.packs = packs;
- r->objects->packfiles->kept_cache.flags = flags;
+ store->kept_cache.packs = packs;
+ store->kept_cache.flags = flags;
}
- return r->objects->packfiles->kept_cache.packs;
+ return store->kept_cache.packs;
}
-int find_kept_pack_entry(struct repository *r,
- const struct object_id *oid,
- unsigned flags,
- struct pack_entry *e)
+int has_object_pack(struct repository *r, const struct object_id *oid)
{
- struct packed_git **cache;
+ struct odb_source *source;
+ struct pack_entry e;
- for (cache = kept_pack_cache(r, flags); *cache; cache++) {
- struct packed_git *p = *cache;
- if (fill_pack_entry(oid, e, p))
- return 1;
+ odb_prepare_alternates(r->objects);
+ for (source = r->objects->sources; source; source = source->next) {
+ int ret = find_pack_entry(source->packfiles, oid, &e);
+ if (ret)
+ return ret;
}
return 0;
}
-int has_object_pack(struct repository *r, const struct object_id *oid)
-{
- struct pack_entry e;
- return find_pack_entry(r, oid, &e);
-}
-
int has_object_kept_pack(struct repository *r, const struct object_id *oid,
unsigned flags)
{
+ struct odb_source *source;
struct pack_entry e;
- return find_kept_pack_entry(r, oid, flags, &e);
+
+ for (source = r->objects->sources; source; source = source->next) {
+ struct packed_git **cache;
+
+ cache = packfile_store_get_kept_pack_cache(source->packfiles, flags);
+
+ for (; *cache; cache++) {
+ struct packed_git *p = *cache;
+ if (fill_pack_entry(oid, &e, p))
+ return 1;
+ }
+ }
+
+ return 0;
}
int for_each_object_in_pack(struct packed_git *p,
@@ -2288,32 +2304,46 @@ int for_each_object_in_pack(struct packed_git *p,
int for_each_packed_object(struct repository *repo, each_packed_object_fn cb,
void *data, enum for_each_object_flags flags)
{
- struct packed_git *p;
+ struct odb_source *source;
int r = 0;
int pack_errors = 0;
- repo->objects->packfiles->skip_mru_updates = true;
- repo_for_each_pack(repo, p) {
- if ((flags & FOR_EACH_OBJECT_LOCAL_ONLY) && !p->pack_local)
- continue;
- if ((flags & FOR_EACH_OBJECT_PROMISOR_ONLY) &&
- !p->pack_promisor)
- continue;
- if ((flags & FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS) &&
- p->pack_keep_in_core)
- continue;
- if ((flags & FOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS) &&
- p->pack_keep)
- continue;
- if (open_pack_index(p)) {
- pack_errors = 1;
- continue;
+ odb_prepare_alternates(repo->objects);
+
+ for (source = repo->objects->sources; source; source = source->next) {
+ struct packfile_list_entry *e;
+
+ source->packfiles->skip_mru_updates = true;
+
+ for (e = packfile_store_get_packs(source->packfiles); e; e = e->next) {
+ struct packed_git *p = e->pack;
+
+ if ((flags & FOR_EACH_OBJECT_LOCAL_ONLY) && !p->pack_local)
+ continue;
+ if ((flags & FOR_EACH_OBJECT_PROMISOR_ONLY) &&
+ !p->pack_promisor)
+ continue;
+ if ((flags & FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS) &&
+ p->pack_keep_in_core)
+ continue;
+ if ((flags & FOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS) &&
+ p->pack_keep)
+ continue;
+ if (open_pack_index(p)) {
+ pack_errors = 1;
+ continue;
+ }
+
+ r = for_each_object_in_pack(p, cb, data, flags);
+ if (r)
+ break;
}
- r = for_each_object_in_pack(p, cb, data, flags);
+
+ source->packfiles->skip_mru_updates = false;
+
if (r)
break;
}
- repo->objects->packfiles->skip_mru_updates = false;
return r ? r : pack_errors;
}
@@ -2411,11 +2441,11 @@ int parse_pack_header_option(const char *in, unsigned char *out, unsigned int *l
return 0;
}
-struct packfile_store *packfile_store_new(struct object_database *odb)
+struct packfile_store *packfile_store_new(struct odb_source *source)
{
struct packfile_store *store;
CALLOC_ARRAY(store, 1);
- store->odb = odb;
+ store->source = source;
strmap_init(&store->packs_by_path);
return store;
}
@@ -2437,6 +2467,9 @@ void packfile_store_close(struct packfile_store *store)
BUG("want to close pack marked 'do-not-close'");
close_pack(e->pack);
}
+ if (store->midx)
+ close_midx(store->midx);
+ store->midx = NULL;
}
struct odb_packed_read_stream {
@@ -2533,8 +2566,9 @@ int packfile_store_read_object_stream(struct odb_read_stream **out,
oi.sizep = &size;
if (packfile_store_read_object_info(store, oid, &oi, 0) ||
- oi.u.packed.is_delta ||
- repo_settings_get_big_file_threshold(store->odb->repo) >= size)
+ oi.u.packed.type == PACKED_OBJECT_TYPE_REF_DELTA ||
+ oi.u.packed.type == PACKED_OBJECT_TYPE_OFS_DELTA ||
+ repo_settings_get_big_file_threshold(store->source->odb->repo) >= size)
return -1;
in_pack_type = unpack_object_header(oi.u.packed.pack,
diff --git a/packfile.h b/packfile.h
index 59d162a3f4..acc5c55ad5 100644
--- a/packfile.h
+++ b/packfile.h
@@ -5,6 +5,7 @@
#include "object.h"
#include "odb.h"
#include "oidset.h"
+#include "repository.h"
#include "strmap.h"
/* in odb.h */
@@ -77,7 +78,7 @@ struct packed_git *packfile_list_find_oid(struct packfile_list_entry *packs,
* A store that manages packfiles for a given object database.
*/
struct packfile_store {
- struct object_database *odb;
+ struct odb_source *source;
/*
* The list of packfiles in the order in which they have been most
@@ -90,15 +91,19 @@ struct packfile_store {
* is an on-disk ".keep" file or because they are marked as "kept" in
* memory.
*
- * Should not be accessed directly, but via `kept_pack_cache()`. The
- * list of packs gets invalidated when the stored flags and the flags
- * passed to `kept_pack_cache()` mismatch.
+ * Should not be accessed directly, but via
+ * `packfile_store_get_kept_pack_cache()`. The list of packs gets
+ * invalidated when the stored flags and the flags passed to
+ * `packfile_store_get_kept_pack_cache()` mismatch.
*/
struct {
struct packed_git **packs;
unsigned flags;
} kept_cache;
+ /* The multi-pack index that belongs to this specific packfile store. */
+ struct multi_pack_index *midx;
+
/*
* A map of packfile names to packed_git structs for tracking which
* packs have been loaded already.
@@ -129,9 +134,9 @@ struct packfile_store {
/*
* Allocate and initialize a new empty packfile store for the given object
- * database.
+ * database source.
*/
-struct packfile_store *packfile_store_new(struct object_database *odb);
+struct packfile_store *packfile_store_new(struct odb_source *source);
/*
* Free the packfile store and all its associated state. All packfiles
@@ -170,13 +175,64 @@ void packfile_store_add_pack(struct packfile_store *store,
struct packed_git *pack);
/*
+ * Get all packs managed by the given store, including packfiles that are
+ * referenced by multi-pack indices.
+ */
+struct packfile_list_entry *packfile_store_get_packs(struct packfile_store *store);
+
+struct repo_for_each_pack_data {
+ struct odb_source *source;
+ struct packfile_list_entry *entry;
+};
+
+static inline struct repo_for_each_pack_data repo_for_eack_pack_data_init(struct repository *repo)
+{
+ struct repo_for_each_pack_data data = { 0 };
+
+ odb_prepare_alternates(repo->objects);
+
+ for (struct odb_source *source = repo->objects->sources; source; source = source->next) {
+ struct packfile_list_entry *entry = packfile_store_get_packs(source->packfiles);
+ if (!entry)
+ continue;
+ data.source = source;
+ data.entry = entry;
+ break;
+ }
+
+ return data;
+}
+
+static inline void repo_for_each_pack_data_next(struct repo_for_each_pack_data *data)
+{
+ struct odb_source *source;
+
+ data->entry = data->entry->next;
+ if (data->entry)
+ return;
+
+ for (source = data->source->next; source; source = source->next) {
+ struct packfile_list_entry *entry = packfile_store_get_packs(source->packfiles);
+ if (!entry)
+ continue;
+ data->source = source;
+ data->entry = entry;
+ return;
+ }
+
+ data->source = NULL;
+ data->entry = NULL;
+}
+
+/*
* Load and iterate through all packs of the given repository. This helper
* function will yield packfiles from all object sources connected to the
* repository.
*/
#define repo_for_each_pack(repo, p) \
- for (struct packfile_list_entry *e = packfile_store_get_packs(repo->objects->packfiles); \
- ((p) = (e ? e->pack : NULL)); e = e->next)
+ for (struct repo_for_each_pack_data eack_pack_data = repo_for_eack_pack_data_init(repo); \
+ ((p) = (eack_pack_data.entry ? eack_pack_data.entry->pack : NULL)); \
+ repo_for_each_pack_data_next(&eack_pack_data))
int packfile_store_read_object_stream(struct odb_read_stream **out,
struct packfile_store *store,
@@ -194,12 +250,6 @@ int packfile_store_read_object_info(struct packfile_store *store,
unsigned flags);
/*
- * Get all packs managed by the given store, including packfiles that are
- * referenced by multi-pack indices.
- */
-struct packfile_list_entry *packfile_store_get_packs(struct packfile_store *store);
-
-/*
* Open the packfile and add it to the store if it isn't yet known. Returns
* either the newly opened packfile or the preexisting packfile. Returns a
* `NULL` pointer in case the packfile could not be opened.
@@ -210,6 +260,19 @@ struct packed_git *packfile_store_load_pack(struct packfile_store *store,
int packfile_store_freshen_object(struct packfile_store *store,
const struct object_id *oid);
+enum kept_pack_type {
+ KEPT_PACK_ON_DISK = (1 << 0),
+ KEPT_PACK_IN_CORE = (1 << 1),
+};
+
+/*
+ * Retrieve the cache of kept packs from the given packfile store. Accepts a
+ * combination of `kept_pack_type` flags. The cache is computed on demand and
+ * will be recomputed whenever the flags change.
+ */
+struct packed_git **packfile_store_get_kept_pack_cache(struct packfile_store *store,
+ unsigned flags);
+
struct pack_window {
struct pack_window *next;
unsigned char *base;
@@ -378,28 +441,20 @@ void release_pack_memory(size_t);
/* global flag to enable extra checks when accessing packed objects */
extern int do_check_packed_object_crc;
-int packed_object_info(struct repository *r,
- struct packed_git *pack,
+/*
+ * Look up the object info for a specific offset in the packfile.
+ * Returns zero on success, a negative error code otherwise.
+ */
+int packed_object_info(struct packed_git *pack,
off_t offset, struct object_info *);
void mark_bad_packed_object(struct packed_git *, const struct object_id *);
const struct packed_git *has_packed_and_bad(struct repository *, const struct object_id *);
-#define ON_DISK_KEEP_PACKS 1
-#define IN_CORE_KEEP_PACKS 2
-
-/*
- * Iff a pack file in the given repository contains the object named by sha1,
- * return true and store its location to e.
- */
-int find_kept_pack_entry(struct repository *r, const struct object_id *oid, unsigned flags, struct pack_entry *e);
-
int has_object_pack(struct repository *r, const struct object_id *oid);
int has_object_kept_pack(struct repository *r, const struct object_id *oid,
unsigned flags);
-struct packed_git **kept_pack_cache(struct repository *r, unsigned flags);
-
/*
* Return 1 if an object in a promisor packfile is or refers to the given
* object, 0 otherwise.
diff --git a/path-walk.c b/path-walk.c
index f1ceed99e9..364e4cfa19 100644
--- a/path-walk.c
+++ b/path-walk.c
@@ -137,7 +137,7 @@ static int add_tree_entries(struct path_walk_context *ctx,
error(_("failed to walk children of tree %s: not found"),
oid_to_hex(oid));
return -1;
- } else if (parse_tree_gently(tree, 1)) {
+ } else if (repo_parse_tree_gently(ctx->repo, tree, 1)) {
error("bad tree object %s", oid_to_hex(oid));
return -1;
}
diff --git a/reachable.c b/reachable.c
index b753c39553..4b532039d5 100644
--- a/reachable.c
+++ b/reachable.c
@@ -242,7 +242,7 @@ static int want_recent_object(struct recent_data *data,
const struct object_id *oid)
{
if (data->ignore_in_core_kept_packs &&
- has_object_kept_pack(data->revs->repo, oid, IN_CORE_KEEP_PACKS))
+ has_object_kept_pack(data->revs->repo, oid, KEPT_PACK_IN_CORE))
return 0;
return 1;
}
diff --git a/read-cache.c b/read-cache.c
index 990d4ead0d..e9c1b23e48 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -3807,7 +3807,7 @@ void overlay_tree_on_index(struct index_state *istate,
if (repo_get_oid(the_repository, tree_name, &oid))
die("tree-ish %s not found.", tree_name);
- tree = parse_tree_indirect(&oid);
+ tree = repo_parse_tree_indirect(the_repository, &oid);
if (!tree)
die("bad tree-ish %s", tree_name);
diff --git a/ref-filter.c b/ref-filter.c
index d7454269e8..c318f9ca0e 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -2866,7 +2866,7 @@ static int match_points_at(struct oid_array *points_at,
while (obj && obj->type == OBJ_TAG) {
struct tag *tag = (struct tag *)obj;
- if (parse_tag(tag) < 0) {
+ if (parse_tag(the_repository, tag) < 0) {
obj = NULL;
break;
}
diff --git a/refs.c b/refs.c
index 046b695bb2..627b7f8698 100644
--- a/refs.c
+++ b/refs.c
@@ -320,6 +320,49 @@ int check_refname_format(const char *refname, int flags)
return check_or_sanitize_refname(refname, flags, NULL);
}
+int refs_fsck_ref(struct ref_store *refs UNUSED, struct fsck_options *o,
+ struct fsck_ref_report *report,
+ const char *refname UNUSED, const struct object_id *oid)
+{
+ if (is_null_oid(oid))
+ return fsck_report_ref(o, report, FSCK_MSG_BAD_REF_OID,
+ "points to invalid object ID '%s'",
+ oid_to_hex(oid));
+
+ return 0;
+}
+
+int refs_fsck_symref(struct ref_store *refs UNUSED, struct fsck_options *o,
+ struct fsck_ref_report *report,
+ const char *refname, const char *target)
+{
+ const char *stripped_refname;
+
+ parse_worktree_ref(refname, NULL, NULL, &stripped_refname);
+
+ if (!strcmp(stripped_refname, "HEAD") &&
+ !starts_with(target, "refs/heads/") &&
+ fsck_report_ref(o, report, FSCK_MSG_BAD_HEAD_TARGET,
+ "HEAD points to non-branch '%s'", target))
+ return -1;
+
+ if (is_root_ref(target))
+ return 0;
+
+ if (check_refname_format(target, 0) &&
+ fsck_report_ref(o, report, FSCK_MSG_BAD_REFERENT_NAME,
+ "points to invalid refname '%s'", target))
+ return -1;
+
+ if (!starts_with(target, "refs/") &&
+ !starts_with(target, "worktrees/") &&
+ fsck_report_ref(o, report, FSCK_MSG_SYMREF_TARGET_IS_NOT_A_REF,
+ "points to non-ref target '%s'", target))
+ return -1;
+
+ return 0;
+}
+
int refs_fsck(struct ref_store *refs, struct fsck_options *o,
struct worktree *wt)
{
diff --git a/refs.h b/refs.h
index d9051bbb04..f0abfa1d93 100644
--- a/refs.h
+++ b/refs.h
@@ -653,6 +653,24 @@ int refs_for_each_reflog(struct ref_store *refs, each_reflog_fn fn, void *cb_dat
*/
int check_refname_format(const char *refname, int flags);
+struct fsck_ref_report;
+
+/*
+ * Perform generic checks for a specific direct ref. This function is
+ * expected to be called by the ref backends for every symbolic ref.
+ */
+int refs_fsck_ref(struct ref_store *refs, struct fsck_options *o,
+ struct fsck_ref_report *report,
+ const char *refname, const struct object_id *oid);
+
+/*
+ * Perform generic checks for a specific symref target. This function is
+ * expected to be called by the ref backends for every symbolic ref.
+ */
+int refs_fsck_symref(struct ref_store *refs, struct fsck_options *o,
+ struct fsck_ref_report *report,
+ const char *refname, const char *target);
+
/*
* Check the reference database for consistency. Return 0 if refs and
* reflogs are consistent, and non-zero otherwise. The errors will be
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 6f6f76a8d8..240d3c3b26 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -354,13 +354,11 @@ static int for_each_root_ref(struct files_ref_store *refs,
void *cb_data)
{
struct strbuf path = STRBUF_INIT, refname = STRBUF_INIT;
- const char *dirname = refs->loose->root->name;
struct dirent *de;
- size_t dirnamelen;
int ret;
DIR *d;
- files_ref_path(refs, &path, dirname);
+ files_ref_path(refs, &path, "");
d = opendir(path.buf);
if (!d) {
@@ -368,9 +366,6 @@ static int for_each_root_ref(struct files_ref_store *refs,
return -1;
}
- strbuf_addstr(&refname, dirname);
- dirnamelen = refname.len;
-
while ((de = readdir(d)) != NULL) {
unsigned char dtype;
@@ -378,6 +373,8 @@ static int for_each_root_ref(struct files_ref_store *refs,
continue;
if (ends_with(de->d_name, ".lock"))
continue;
+
+ strbuf_reset(&refname);
strbuf_addstr(&refname, de->d_name);
dtype = get_dtype(de, &path, 1);
@@ -386,8 +383,6 @@ static int for_each_root_ref(struct files_ref_store *refs,
if (ret)
goto done;
}
-
- strbuf_setlen(&refname, dirnamelen);
}
ret = 0;
@@ -3720,64 +3715,50 @@ static int files_ref_store_remove_on_disk(struct ref_store *ref_store,
typedef int (*files_fsck_refs_fn)(struct ref_store *ref_store,
struct fsck_options *o,
const char *refname,
- struct dir_iterator *iter);
+ const char *path,
+ int mode);
-static int files_fsck_symref_target(struct fsck_options *o,
+static int files_fsck_symref_target(struct ref_store *ref_store,
+ struct fsck_options *o,
struct fsck_ref_report *report,
+ const char *refname,
struct strbuf *referent,
unsigned int symbolic_link)
{
- int is_referent_root;
char orig_last_byte;
size_t orig_len;
int ret = 0;
orig_len = referent->len;
orig_last_byte = referent->buf[orig_len - 1];
- if (!symbolic_link)
- strbuf_rtrim(referent);
-
- is_referent_root = is_root_ref(referent->buf);
- if (!is_referent_root &&
- !starts_with(referent->buf, "refs/") &&
- !starts_with(referent->buf, "worktrees/")) {
- ret = fsck_report_ref(o, report,
- FSCK_MSG_SYMREF_TARGET_IS_NOT_A_REF,
- "points to non-ref target '%s'", referent->buf);
-
- }
- if (!is_referent_root && check_refname_format(referent->buf, 0)) {
- ret = fsck_report_ref(o, report,
- FSCK_MSG_BAD_REFERENT_NAME,
- "points to invalid refname '%s'", referent->buf);
- goto out;
- }
+ if (!symbolic_link) {
+ strbuf_rtrim(referent);
- if (symbolic_link)
- goto out;
+ if (referent->len == orig_len ||
+ (referent->len < orig_len && orig_last_byte != '\n')) {
+ ret |= fsck_report_ref(o, report,
+ FSCK_MSG_REF_MISSING_NEWLINE,
+ "misses LF at the end");
+ }
- if (referent->len == orig_len ||
- (referent->len < orig_len && orig_last_byte != '\n')) {
- ret = fsck_report_ref(o, report,
- FSCK_MSG_REF_MISSING_NEWLINE,
- "misses LF at the end");
+ if (referent->len != orig_len && referent->len != orig_len - 1) {
+ ret |= fsck_report_ref(o, report,
+ FSCK_MSG_TRAILING_REF_CONTENT,
+ "has trailing whitespaces or newlines");
+ }
}
- if (referent->len != orig_len && referent->len != orig_len - 1) {
- ret = fsck_report_ref(o, report,
- FSCK_MSG_TRAILING_REF_CONTENT,
- "has trailing whitespaces or newlines");
- }
+ ret |= refs_fsck_symref(ref_store, o, report, refname, referent->buf);
-out:
- return ret;
+ return ret ? -1 : 0;
}
static int files_fsck_refs_content(struct ref_store *ref_store,
struct fsck_options *o,
const char *target_name,
- struct dir_iterator *iter)
+ const char *path,
+ int mode)
{
struct strbuf ref_content = STRBUF_INIT;
struct strbuf abs_gitdir = STRBUF_INIT;
@@ -3791,7 +3772,7 @@ static int files_fsck_refs_content(struct ref_store *ref_store,
report.path = target_name;
- if (S_ISLNK(iter->st.st_mode)) {
+ if (S_ISLNK(mode)) {
const char *relative_referent_path = NULL;
ret = fsck_report_ref(o, &report,
@@ -3803,7 +3784,7 @@ static int files_fsck_refs_content(struct ref_store *ref_store,
if (!is_dir_sep(abs_gitdir.buf[abs_gitdir.len - 1]))
strbuf_addch(&abs_gitdir, '/');
- strbuf_add_real_path(&ref_content, iter->path.buf);
+ strbuf_add_real_path(&ref_content, path);
skip_prefix(ref_content.buf, abs_gitdir.buf,
&relative_referent_path);
@@ -3812,11 +3793,12 @@ static int files_fsck_refs_content(struct ref_store *ref_store,
else
strbuf_addbuf(&referent, &ref_content);
- ret |= files_fsck_symref_target(o, &report, &referent, 1);
+ ret |= files_fsck_symref_target(ref_store, o, &report,
+ target_name, &referent, 1);
goto cleanup;
}
- if (strbuf_read_file(&ref_content, iter->path.buf, 0) < 0) {
+ if (strbuf_read_file(&ref_content, path, 0) < 0) {
/*
* Ref file could be removed by another concurrent process. We should
* ignore this error and continue to the next ref.
@@ -3824,7 +3806,7 @@ static int files_fsck_refs_content(struct ref_store *ref_store,
if (errno == ENOENT)
goto cleanup;
- ret = error_errno(_("cannot read ref file '%s'"), iter->path.buf);
+ ret = error_errno(_("cannot read ref file '%s'"), path);
goto cleanup;
}
@@ -3851,8 +3833,11 @@ static int files_fsck_refs_content(struct ref_store *ref_store,
"has trailing garbage: '%s'", trailing);
goto cleanup;
}
+
+ ret = refs_fsck_ref(ref_store, o, &report, target_name, &oid);
} else {
- ret = files_fsck_symref_target(o, &report, &referent, 0);
+ ret = files_fsck_symref_target(ref_store, o, &report,
+ target_name, &referent, 0);
goto cleanup;
}
@@ -3866,21 +3851,25 @@ cleanup:
static int files_fsck_refs_name(struct ref_store *ref_store UNUSED,
struct fsck_options *o,
const char *refname,
- struct dir_iterator *iter)
+ const char *path,
+ int mode UNUSED)
{
struct strbuf sb = STRBUF_INIT;
+ const char *filename;
int ret = 0;
+ filename = basename((char *) path);
+
/*
* Ignore the files ending with ".lock" as they may be lock files
* However, do not allow bare ".lock" files.
*/
- if (iter->basename[0] != '.' && ends_with(iter->basename, ".lock"))
+ if (filename[0] != '.' && ends_with(filename, ".lock"))
+ goto cleanup;
+
+ if (is_root_ref(refname))
goto cleanup;
- /*
- * This works right now because we never check the root refs.
- */
if (check_refname_format(refname, 0)) {
struct fsck_ref_report report = { 0 };
@@ -3895,11 +3884,44 @@ cleanup:
return ret;
}
+static const files_fsck_refs_fn fsck_refs_fn[]= {
+ files_fsck_refs_name,
+ files_fsck_refs_content,
+ NULL,
+};
+
+static int files_fsck_ref(struct ref_store *ref_store,
+ struct fsck_options *o,
+ const char *refname,
+ const char *path,
+ int mode)
+{
+ int ret = 0;
+
+ if (o->verbose)
+ fprintf_ln(stderr, "Checking %s", refname);
+
+ if (!S_ISREG(mode) && !S_ISLNK(mode)) {
+ struct fsck_ref_report report = { .path = refname };
+
+ if (fsck_report_ref(o, &report,
+ FSCK_MSG_BAD_REF_FILETYPE,
+ "unexpected file type"))
+ ret = -1;
+ goto out;
+ }
+
+ for (size_t i = 0; fsck_refs_fn[i]; i++)
+ if (fsck_refs_fn[i](ref_store, o, refname, path, mode))
+ ret = -1;
+
+out:
+ return ret;
+}
+
static int files_fsck_refs_dir(struct ref_store *ref_store,
struct fsck_options *o,
- const char *refs_check_dir,
- struct worktree *wt,
- files_fsck_refs_fn *fsck_refs_fn)
+ struct worktree *wt)
{
struct strbuf refname = STRBUF_INIT;
struct strbuf sb = STRBUF_INIT;
@@ -3907,7 +3929,7 @@ static int files_fsck_refs_dir(struct ref_store *ref_store,
int iter_status;
int ret = 0;
- strbuf_addf(&sb, "%s/%s", ref_store->gitdir, refs_check_dir);
+ strbuf_addf(&sb, "%s/refs", ref_store->gitdir);
iter = dir_iterator_begin(sb.buf, 0);
if (!iter) {
@@ -3919,31 +3941,17 @@ static int files_fsck_refs_dir(struct ref_store *ref_store,
}
while ((iter_status = dir_iterator_advance(iter)) == ITER_OK) {
- if (S_ISDIR(iter->st.st_mode)) {
+ if (S_ISDIR(iter->st.st_mode))
continue;
- } else if (S_ISREG(iter->st.st_mode) ||
- S_ISLNK(iter->st.st_mode)) {
- strbuf_reset(&refname);
- if (!is_main_worktree(wt))
- strbuf_addf(&refname, "worktrees/%s/", wt->id);
- strbuf_addf(&refname, "%s/%s", refs_check_dir,
- iter->relative_path);
+ strbuf_reset(&refname);
+ if (!is_main_worktree(wt))
+ strbuf_addf(&refname, "worktrees/%s/", wt->id);
+ strbuf_addf(&refname, "refs/%s", iter->relative_path);
- if (o->verbose)
- fprintf_ln(stderr, "Checking %s", refname.buf);
-
- for (size_t i = 0; fsck_refs_fn[i]; i++) {
- if (fsck_refs_fn[i](ref_store, o, refname.buf, iter))
- ret = -1;
- }
- } else {
- struct fsck_ref_report report = { .path = iter->basename };
- if (fsck_report_ref(o, &report,
- FSCK_MSG_BAD_REF_FILETYPE,
- "unexpected file type"))
- ret = -1;
- }
+ if (files_fsck_ref(ref_store, o, refname.buf,
+ iter->path.buf, iter->st.st_mode) < 0)
+ ret = -1;
}
if (iter_status != ITER_DONE)
@@ -3956,17 +3964,35 @@ out:
return ret;
}
-static int files_fsck_refs(struct ref_store *ref_store,
- struct fsck_options *o,
- struct worktree *wt)
+struct files_fsck_root_ref_data {
+ struct files_ref_store *refs;
+ struct fsck_options *o;
+ struct worktree *wt;
+ struct strbuf refname;
+ struct strbuf path;
+};
+
+static int files_fsck_root_ref(const char *refname, void *cb_data)
{
- files_fsck_refs_fn fsck_refs_fn[]= {
- files_fsck_refs_name,
- files_fsck_refs_content,
- NULL,
- };
+ struct files_fsck_root_ref_data *data = cb_data;
+ struct stat st;
- return files_fsck_refs_dir(ref_store, o, "refs", wt, fsck_refs_fn);
+ strbuf_reset(&data->refname);
+ if (!is_main_worktree(data->wt))
+ strbuf_addf(&data->refname, "worktrees/%s/", data->wt->id);
+ strbuf_addstr(&data->refname, refname);
+
+ strbuf_reset(&data->path);
+ strbuf_addf(&data->path, "%s/%s", data->refs->gitcommondir, data->refname.buf);
+
+ if (stat(data->path.buf, &st)) {
+ if (errno == ENOENT)
+ return 0;
+ return error_errno("failed to read ref: '%s'", data->path.buf);
+ }
+
+ return files_fsck_ref(&data->refs->base, data->o, data->refname.buf,
+ data->path.buf, st.st_mode);
}
static int files_fsck(struct ref_store *ref_store,
@@ -3975,9 +4001,27 @@ static int files_fsck(struct ref_store *ref_store,
{
struct files_ref_store *refs =
files_downcast(ref_store, REF_STORE_READ, "fsck");
+ struct files_fsck_root_ref_data data = {
+ .refs = refs,
+ .o = o,
+ .wt = wt,
+ .refname = STRBUF_INIT,
+ .path = STRBUF_INIT,
+ };
+ int ret = 0;
+
+ if (files_fsck_refs_dir(ref_store, o, wt) < 0)
+ ret = -1;
+
+ if (for_each_root_ref(refs, files_fsck_root_ref, &data) < 0)
+ ret = -1;
+
+ if (refs->packed_ref_store->be->fsck(refs->packed_ref_store, o, wt) < 0)
+ ret = -1;
- return files_fsck_refs(ref_store, o, wt) |
- refs->packed_ref_store->be->fsck(refs->packed_ref_store, o, wt);
+ strbuf_release(&data.refname);
+ strbuf_release(&data.path);
+ return ret;
}
struct ref_storage_be refs_be_files = {
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 4319a4eacb..fe74af73af 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -10,9 +10,10 @@
#include "../gettext.h"
#include "../hash.h"
#include "../hex.h"
-#include "../iterator.h"
#include "../ident.h"
+#include "../iterator.h"
#include "../object.h"
+#include "../parse.h"
#include "../path.h"
#include "../refs.h"
#include "../reftable/reftable-basics.h"
@@ -25,8 +26,8 @@
#include "../setup.h"
#include "../strmap.h"
#include "../trace2.h"
+#include "../worktree.h"
#include "../write-or-die.h"
-#include "parse.h"
#include "refs-internal.h"
/*
@@ -172,6 +173,37 @@ static struct reftable_ref_store *reftable_be_downcast(struct ref_store *ref_sto
return refs;
}
+static int backend_for_worktree(struct reftable_backend **out,
+ struct reftable_ref_store *store,
+ const char *worktree_name)
+{
+ struct strbuf worktree_dir = STRBUF_INIT;
+ int ret;
+
+ *out = strmap_get(&store->worktree_backends, worktree_name);
+ if (*out) {
+ ret = 0;
+ goto out;
+ }
+
+ strbuf_addf(&worktree_dir, "%s/worktrees/%s/reftable",
+ store->base.repo->commondir, worktree_name);
+
+ CALLOC_ARRAY(*out, 1);
+ store->err = ret = reftable_backend_init(*out, worktree_dir.buf,
+ &store->write_options);
+ if (ret < 0) {
+ free(*out);
+ goto out;
+ }
+
+ strmap_put(&store->worktree_backends, worktree_name, *out);
+
+out:
+ strbuf_release(&worktree_dir);
+ return ret;
+}
+
/*
* Some refs are global to the repository (refs/heads/{*}), while others are
* local to the worktree (eg. HEAD, refs/bisect/{*}). We solve this by having
@@ -191,19 +223,19 @@ static int backend_for(struct reftable_backend **out,
const char **rewritten_ref,
int reload)
{
- struct reftable_backend *be;
const char *wtname;
int wtname_len;
+ int ret;
if (!refname) {
- be = &store->main_backend;
+ *out = &store->main_backend;
+ ret = 0;
goto out;
}
switch (parse_worktree_ref(refname, &wtname, &wtname_len, rewritten_ref)) {
case REF_WORKTREE_OTHER: {
static struct strbuf wtname_buf = STRBUF_INIT;
- struct strbuf wt_dir = STRBUF_INIT;
/*
* We're using a static buffer here so that we don't need to
@@ -223,20 +255,8 @@ static int backend_for(struct reftable_backend **out,
* already and error out when trying to write a reference via
* both stacks.
*/
- be = strmap_get(&store->worktree_backends, wtname_buf.buf);
- if (!be) {
- strbuf_addf(&wt_dir, "%s/worktrees/%s/reftable",
- store->base.repo->commondir, wtname_buf.buf);
-
- CALLOC_ARRAY(be, 1);
- store->err = reftable_backend_init(be, wt_dir.buf,
- &store->write_options);
- assert(store->err != REFTABLE_API_ERROR);
-
- strmap_put(&store->worktree_backends, wtname_buf.buf, be);
- }
+ ret = backend_for_worktree(out, store, wtname_buf.buf);
- strbuf_release(&wt_dir);
goto out;
}
case REF_WORKTREE_CURRENT:
@@ -245,27 +265,24 @@ static int backend_for(struct reftable_backend **out,
* main worktree. We thus return the main stack in that case.
*/
if (!store->worktree_backend.stack)
- be = &store->main_backend;
+ *out = &store->main_backend;
else
- be = &store->worktree_backend;
+ *out = &store->worktree_backend;
+ ret = 0;
goto out;
case REF_WORKTREE_MAIN:
case REF_WORKTREE_SHARED:
- be = &store->main_backend;
+ *out = &store->main_backend;
+ ret = 0;
goto out;
default:
BUG("unhandled worktree reference type");
}
out:
- if (reload) {
- int ret = reftable_stack_reload(be->stack);
- if (ret)
- return ret;
- }
- *out = be;
-
- return 0;
+ if (reload && !ret)
+ ret = reftable_stack_reload((*out)->stack);
+ return ret;
}
static int should_write_log(struct reftable_ref_store *refs, const char *refname)
@@ -2746,24 +2763,92 @@ static int reftable_fsck_error_handler(struct reftable_fsck_info *info,
}
static int reftable_be_fsck(struct ref_store *ref_store, struct fsck_options *o,
- struct worktree *wt UNUSED)
+ struct worktree *wt)
{
- struct reftable_ref_store *refs;
- struct strmap_entry *entry;
- struct hashmap_iter iter;
- int ret = 0;
+ struct reftable_ref_store *refs =
+ reftable_be_downcast(ref_store, REF_STORE_READ, "fsck");
+ struct reftable_ref_iterator *iter = NULL;
+ struct reftable_ref_record ref = { 0 };
+ struct fsck_ref_report report = { 0 };
+ struct strbuf refname = STRBUF_INIT;
+ struct reftable_backend *backend;
+ int ret, errors = 0;
- refs = reftable_be_downcast(ref_store, REF_STORE_READ, "fsck");
+ if (is_main_worktree(wt)) {
+ backend = &refs->main_backend;
+ } else {
+ ret = backend_for_worktree(&backend, refs, wt->id);
+ if (ret < 0) {
+ ret = error(_("reftable stack for worktree '%s' is broken"),
+ wt->id);
+ goto out;
+ }
+ }
- ret |= reftable_fsck_check(refs->main_backend.stack, reftable_fsck_error_handler,
- reftable_fsck_verbose_handler, o);
+ errors |= reftable_fsck_check(backend->stack, reftable_fsck_error_handler,
+ reftable_fsck_verbose_handler, o);
- strmap_for_each_entry(&refs->worktree_backends, &iter, entry) {
- struct reftable_backend *b = (struct reftable_backend *)entry->value;
- ret |= reftable_fsck_check(b->stack, reftable_fsck_error_handler,
- reftable_fsck_verbose_handler, o);
+ iter = ref_iterator_for_stack(refs, backend->stack, "", NULL, 0);
+ if (!iter) {
+ ret = error(_("could not create iterator for worktree '%s'"), wt->id);
+ goto out;
}
+ while (1) {
+ ret = reftable_iterator_next_ref(&iter->iter, &ref);
+ if (ret > 0)
+ break;
+ if (ret < 0) {
+ ret = error(_("could not read record for worktree '%s'"), wt->id);
+ goto out;
+ }
+
+ strbuf_reset(&refname);
+ if (!is_main_worktree(wt))
+ strbuf_addf(&refname, "worktrees/%s/", wt->id);
+ strbuf_addstr(&refname, ref.refname);
+ report.path = refname.buf;
+
+ switch (ref.value_type) {
+ case REFTABLE_REF_VAL1:
+ case REFTABLE_REF_VAL2: {
+ struct object_id oid;
+ unsigned hash_id;
+
+ switch (reftable_stack_hash_id(backend->stack)) {
+ case REFTABLE_HASH_SHA1:
+ hash_id = GIT_HASH_SHA1;
+ break;
+ case REFTABLE_HASH_SHA256:
+ hash_id = GIT_HASH_SHA256;
+ break;
+ default:
+ BUG("unhandled hash ID %d",
+ reftable_stack_hash_id(backend->stack));
+ }
+
+ oidread(&oid, reftable_ref_record_val1(&ref),
+ &hash_algos[hash_id]);
+
+ errors |= refs_fsck_ref(ref_store, o, &report, ref.refname, &oid);
+ break;
+ }
+ case REFTABLE_REF_SYMREF:
+ errors |= refs_fsck_symref(ref_store, o, &report, ref.refname,
+ ref.value.symref);
+ break;
+ default:
+ BUG("unhandled reference value type %d", ref.value_type);
+ }
+ }
+
+ ret = errors ? -1 : 0;
+
+out:
+ if (iter)
+ ref_iterator_free(&iter->base);
+ reftable_ref_record_release(&ref);
+ strbuf_release(&refname);
return ret;
}
diff --git a/remote.c b/remote.c
index 59b3715120..b756ff6f15 100644
--- a/remote.c
+++ b/remote.c
@@ -1381,12 +1381,7 @@ static struct ref **tail_ref(struct ref **head)
return tail;
}
-struct tips {
- struct commit **tip;
- size_t nr, alloc;
-};
-
-static void add_to_tips(struct tips *tips, const struct object_id *oid)
+static void add_to_tips(struct commit_stack *tips, const struct object_id *oid)
{
struct commit *commit;
@@ -1396,8 +1391,7 @@ static void add_to_tips(struct tips *tips, const struct object_id *oid)
if (!commit || (commit->object.flags & TMP_MARK))
return;
commit->object.flags |= TMP_MARK;
- ALLOC_GROW(tips->tip, tips->nr + 1, tips->alloc);
- tips->tip[tips->nr++] = commit;
+ commit_stack_push(tips, commit);
}
static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***dst_tail)
@@ -1406,13 +1400,12 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds
struct string_list src_tag = STRING_LIST_INIT_NODUP;
struct string_list_item *item;
struct ref *ref;
- struct tips sent_tips;
+ struct commit_stack sent_tips = COMMIT_STACK_INIT;
/*
* Collect everything we know they would have at the end of
* this push, and collect all tags they have.
*/
- memset(&sent_tips, 0, sizeof(sent_tips));
for (ref = *dst; ref; ref = ref->next) {
if (ref->peer_ref &&
!is_null_oid(&ref->peer_ref->new_oid))
@@ -1422,7 +1415,7 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds
if (starts_with(ref->name, "refs/tags/"))
string_list_append(&dst_tag, ref->name);
}
- clear_commit_marks_many(sent_tips.nr, sent_tips.tip, TMP_MARK);
+ clear_commit_marks_many(sent_tips.nr, sent_tips.items, TMP_MARK);
string_list_sort(&dst_tag);
@@ -1450,9 +1443,7 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds
if (sent_tips.nr) {
const int reachable_flag = 1;
struct commit_list *found_commits;
- struct commit **src_commits;
- size_t nr_src_commits = 0, alloc_src_commits = 16;
- ALLOC_ARRAY(src_commits, alloc_src_commits);
+ struct commit_stack src_commits = COMMIT_STACK_INIT;
for_each_string_list_item(item, &src_tag) {
struct ref *ref = item->util;
@@ -1467,12 +1458,13 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds
/* not pushing a commit, which is not an error */
continue;
- ALLOC_GROW(src_commits, nr_src_commits + 1, alloc_src_commits);
- src_commits[nr_src_commits++] = commit;
+ commit_stack_push(&src_commits, commit);
}
- found_commits = get_reachable_subset(sent_tips.tip, sent_tips.nr,
- src_commits, nr_src_commits,
+ found_commits = get_reachable_subset(sent_tips.items,
+ sent_tips.nr,
+ src_commits.items,
+ src_commits.nr,
reachable_flag);
for_each_string_list_item(item, &src_tag) {
@@ -1502,13 +1494,14 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds
dst_ref->peer_ref = copy_ref(ref);
}
- clear_commit_marks_many(nr_src_commits, src_commits, reachable_flag);
- free(src_commits);
+ clear_commit_marks_many(src_commits.nr, src_commits.items,
+ reachable_flag);
+ commit_stack_clear(&src_commits);
free_commit_list(found_commits);
}
string_list_clear(&src_tag, 0);
- free(sent_tips.tip);
+ commit_stack_clear(&sent_tips);
}
struct ref *find_ref_by_name(const struct ref *list, const char *name)
@@ -2544,36 +2537,9 @@ static int remote_tracking(struct remote *remote, const char *refname,
return 0;
}
-/*
- * The struct "reflog_commit_array" and related helper functions
- * are used for collecting commits into an array during reflog
- * traversals in "check_and_collect_until()".
- */
-struct reflog_commit_array {
- struct commit **item;
- size_t nr, alloc;
-};
-
-#define REFLOG_COMMIT_ARRAY_INIT { 0 }
-
-/* Append a commit to the array. */
-static void append_commit(struct reflog_commit_array *arr,
- struct commit *commit)
-{
- ALLOC_GROW(arr->item, arr->nr + 1, arr->alloc);
- arr->item[arr->nr++] = commit;
-}
-
-/* Free and reset the array. */
-static void free_commit_array(struct reflog_commit_array *arr)
-{
- FREE_AND_NULL(arr->item);
- arr->nr = arr->alloc = 0;
-}
-
struct check_and_collect_until_cb_data {
struct commit *remote_commit;
- struct reflog_commit_array *local_commits;
+ struct commit_stack *local_commits;
timestamp_t remote_reflog_timestamp;
};
@@ -2605,7 +2571,7 @@ static int check_and_collect_until(const char *refname UNUSED,
return 1;
if ((commit = lookup_commit_reference(the_repository, n_oid)))
- append_commit(cb->local_commits, commit);
+ commit_stack_push(cb->local_commits, commit);
/*
* If the reflog entry timestamp is older than the remote ref's
@@ -2633,7 +2599,7 @@ static int is_reachable_in_reflog(const char *local, const struct ref *remote)
struct commit *commit;
struct commit **chunk;
struct check_and_collect_until_cb_data cb;
- struct reflog_commit_array arr = REFLOG_COMMIT_ARRAY_INIT;
+ struct commit_stack arr = COMMIT_STACK_INIT;
size_t size = 0;
int ret = 0;
@@ -2664,8 +2630,8 @@ static int is_reachable_in_reflog(const char *local, const struct ref *remote)
* Check if the remote commit is reachable from any
* of the commits in the collected array, in batches.
*/
- for (chunk = arr.item; chunk < arr.item + arr.nr; chunk += size) {
- size = arr.item + arr.nr - chunk;
+ for (chunk = arr.items; chunk < arr.items + arr.nr; chunk += size) {
+ size = arr.items + arr.nr - chunk;
if (MERGE_BASES_BATCH_SIZE < size)
size = MERGE_BASES_BATCH_SIZE;
@@ -2674,7 +2640,7 @@ static int is_reachable_in_reflog(const char *local, const struct ref *remote)
}
cleanup_return:
- free_commit_array(&arr);
+ commit_stack_clear(&arr);
return ret;
}
diff --git a/repack-geometry.c b/repack-geometry.c
index b3e32cd07e..7cebd0cb45 100644
--- a/repack-geometry.c
+++ b/repack-geometry.c
@@ -66,45 +66,54 @@ void pack_geometry_init(struct pack_geometry *geometry,
if (p->is_cruft)
continue;
- ALLOC_GROW(geometry->pack,
- geometry->pack_nr + 1,
- geometry->pack_alloc);
+ if (p->pack_promisor) {
+ ALLOC_GROW(geometry->promisor_pack,
+ geometry->promisor_pack_nr + 1,
+ geometry->promisor_pack_alloc);
- geometry->pack[geometry->pack_nr] = p;
- geometry->pack_nr++;
+ geometry->promisor_pack[geometry->promisor_pack_nr] = p;
+ geometry->promisor_pack_nr++;
+ } else {
+ ALLOC_GROW(geometry->pack,
+ geometry->pack_nr + 1,
+ geometry->pack_alloc);
+
+ geometry->pack[geometry->pack_nr] = p;
+ geometry->pack_nr++;
+ }
}
QSORT(geometry->pack, geometry->pack_nr, pack_geometry_cmp);
+ QSORT(geometry->promisor_pack, geometry->promisor_pack_nr, pack_geometry_cmp);
strbuf_release(&buf);
}
-void pack_geometry_split(struct pack_geometry *geometry)
+static uint32_t compute_pack_geometry_split(struct packed_git **pack, size_t pack_nr,
+ int split_factor)
{
uint32_t i;
uint32_t split;
off_t total_size = 0;
- if (!geometry->pack_nr) {
- geometry->split = geometry->pack_nr;
- return;
- }
+ if (!pack_nr)
+ return 0;
/*
* First, count the number of packs (in descending order of size) which
* already form a geometric progression.
*/
- for (i = geometry->pack_nr - 1; i > 0; i--) {
- struct packed_git *ours = geometry->pack[i];
- struct packed_git *prev = geometry->pack[i - 1];
+ for (i = pack_nr - 1; i > 0; i--) {
+ struct packed_git *ours = pack[i];
+ struct packed_git *prev = pack[i - 1];
- if (unsigned_mult_overflows(geometry->split_factor,
+ if (unsigned_mult_overflows(split_factor,
pack_geometry_weight(prev)))
die(_("pack %s too large to consider in geometric "
"progression"),
prev->pack_name);
if (pack_geometry_weight(ours) <
- geometry->split_factor * pack_geometry_weight(prev))
+ split_factor * pack_geometry_weight(prev))
break;
}
@@ -130,21 +139,19 @@ void pack_geometry_split(struct pack_geometry *geometry)
* the geometric progression.
*/
for (i = 0; i < split; i++) {
- struct packed_git *p = geometry->pack[i];
+ struct packed_git *p = pack[i];
if (unsigned_add_overflows(total_size, pack_geometry_weight(p)))
die(_("pack %s too large to roll up"), p->pack_name);
total_size += pack_geometry_weight(p);
}
- for (i = split; i < geometry->pack_nr; i++) {
- struct packed_git *ours = geometry->pack[i];
+ for (i = split; i < pack_nr; i++) {
+ struct packed_git *ours = pack[i];
- if (unsigned_mult_overflows(geometry->split_factor,
- total_size))
+ if (unsigned_mult_overflows(split_factor, total_size))
die(_("pack %s too large to roll up"), ours->pack_name);
- if (pack_geometry_weight(ours) <
- geometry->split_factor * total_size) {
+ if (pack_geometry_weight(ours) < split_factor * total_size) {
if (unsigned_add_overflows(total_size,
pack_geometry_weight(ours)))
die(_("pack %s too large to roll up"),
@@ -156,7 +163,16 @@ void pack_geometry_split(struct pack_geometry *geometry)
break;
}
- geometry->split = split;
+ return split;
+}
+
+void pack_geometry_split(struct pack_geometry *geometry)
+{
+ geometry->split = compute_pack_geometry_split(geometry->pack, geometry->pack_nr,
+ geometry->split_factor);
+ geometry->promisor_split = compute_pack_geometry_split(geometry->promisor_pack,
+ geometry->promisor_pack_nr,
+ geometry->split_factor);
}
struct packed_git *pack_geometry_preferred_pack(struct pack_geometry *geometry)
@@ -194,17 +210,18 @@ struct packed_git *pack_geometry_preferred_pack(struct pack_geometry *geometry)
return NULL;
}
-void pack_geometry_remove_redundant(struct pack_geometry *geometry,
- struct string_list *names,
- struct existing_packs *existing,
- const char *packdir)
+static void remove_redundant_packs(struct packed_git **pack,
+ uint32_t pack_nr,
+ struct string_list *names,
+ struct existing_packs *existing,
+ const char *packdir)
{
const struct git_hash_algo *algop = existing->repo->hash_algo;
struct strbuf buf = STRBUF_INIT;
uint32_t i;
- for (i = 0; i < geometry->split; i++) {
- struct packed_git *p = geometry->pack[i];
+ for (i = 0; i < pack_nr; i++) {
+ struct packed_git *p = pack[i];
if (string_list_has_string(names, hash_to_hex_algop(p->hash,
algop)))
continue;
@@ -223,10 +240,22 @@ void pack_geometry_remove_redundant(struct pack_geometry *geometry,
strbuf_release(&buf);
}
+void pack_geometry_remove_redundant(struct pack_geometry *geometry,
+ struct string_list *names,
+ struct existing_packs *existing,
+ const char *packdir)
+{
+ remove_redundant_packs(geometry->pack, geometry->split,
+ names, existing, packdir);
+ remove_redundant_packs(geometry->promisor_pack, geometry->promisor_split,
+ names, existing, packdir);
+}
+
void pack_geometry_release(struct pack_geometry *geometry)
{
if (!geometry)
return;
free(geometry->pack);
+ free(geometry->promisor_pack);
}
diff --git a/repack-promisor.c b/repack-promisor.c
index ee6e0669f6..73af57bce3 100644
--- a/repack-promisor.c
+++ b/repack-promisor.c
@@ -34,39 +34,17 @@ static int write_oid(const struct object_id *oid,
return 0;
}
-void repack_promisor_objects(struct repository *repo,
- const struct pack_objects_args *args,
- struct string_list *names, const char *packtmp)
+static void finish_repacking_promisor_objects(struct repository *repo,
+ struct child_process *cmd,
+ struct string_list *names,
+ const char *packtmp)
{
- struct write_oid_context ctx;
- struct child_process cmd = CHILD_PROCESS_INIT;
- FILE *out;
struct strbuf line = STRBUF_INIT;
+ FILE *out;
- prepare_pack_objects(&cmd, args, packtmp);
- cmd.in = -1;
-
- /*
- * NEEDSWORK: Giving pack-objects only the OIDs without any ordering
- * hints may result in suboptimal deltas in the resulting pack. See if
- * the OIDs can be sent with fake paths such that pack-objects can use a
- * {type -> existing pack order} ordering when computing deltas instead
- * of a {type -> size} ordering, which may produce better deltas.
- */
- ctx.cmd = &cmd;
- ctx.algop = repo->hash_algo;
- for_each_packed_object(repo, write_oid, &ctx,
- FOR_EACH_OBJECT_PROMISOR_ONLY);
-
- if (cmd.in == -1) {
- /* No packed objects; cmd was never started */
- child_process_clear(&cmd);
- return;
- }
-
- close(cmd.in);
+ close(cmd->in);
- out = xfdopen(cmd.out, "r");
+ out = xfdopen(cmd->out, "r");
while (strbuf_getline_lf(&line, out) != EOF) {
struct string_list_item *item;
char *promisor_name;
@@ -96,7 +74,66 @@ void repack_promisor_objects(struct repository *repo,
}
fclose(out);
- if (finish_command(&cmd))
+ if (finish_command(cmd))
die(_("could not finish pack-objects to repack promisor objects"));
strbuf_release(&line);
}
+
+void repack_promisor_objects(struct repository *repo,
+ const struct pack_objects_args *args,
+ struct string_list *names, const char *packtmp)
+{
+ struct write_oid_context ctx;
+ struct child_process cmd = CHILD_PROCESS_INIT;
+
+ prepare_pack_objects(&cmd, args, packtmp);
+ cmd.in = -1;
+
+ /*
+ * NEEDSWORK: Giving pack-objects only the OIDs without any ordering
+ * hints may result in suboptimal deltas in the resulting pack. See if
+ * the OIDs can be sent with fake paths such that pack-objects can use a
+ * {type -> existing pack order} ordering when computing deltas instead
+ * of a {type -> size} ordering, which may produce better deltas.
+ */
+ ctx.cmd = &cmd;
+ ctx.algop = repo->hash_algo;
+ for_each_packed_object(repo, write_oid, &ctx,
+ FOR_EACH_OBJECT_PROMISOR_ONLY);
+
+ if (cmd.in == -1) {
+ /* No packed objects; cmd was never started */
+ child_process_clear(&cmd);
+ return;
+ }
+
+ finish_repacking_promisor_objects(repo, &cmd, names, packtmp);
+}
+
+void pack_geometry_repack_promisors(struct repository *repo,
+ const struct pack_objects_args *args,
+ const struct pack_geometry *geometry,
+ struct string_list *names,
+ const char *packtmp)
+{
+ struct child_process cmd = CHILD_PROCESS_INIT;
+ FILE *in;
+
+ if (!geometry->promisor_split)
+ return;
+
+ prepare_pack_objects(&cmd, args, packtmp);
+ strvec_push(&cmd.args, "--stdin-packs");
+ cmd.in = -1;
+ if (start_command(&cmd))
+ die(_("could not start pack-objects to repack promisor packs"));
+
+ in = xfdopen(cmd.in, "w");
+ for (size_t i = 0; i < geometry->promisor_split; i++)
+ fprintf(in, "%s\n", pack_basename(geometry->promisor_pack[i]));
+ for (size_t i = geometry->promisor_split; i < geometry->promisor_pack_nr; i++)
+ fprintf(in, "^%s\n", pack_basename(geometry->promisor_pack[i]));
+ fclose(in);
+
+ finish_repacking_promisor_objects(repo, &cmd, names, packtmp);
+}
diff --git a/repack.h b/repack.h
index 3a688a12ee..bc9f2e1a5d 100644
--- a/repack.h
+++ b/repack.h
@@ -103,9 +103,19 @@ struct pack_geometry {
uint32_t pack_nr, pack_alloc;
uint32_t split;
+ struct packed_git **promisor_pack;
+ uint32_t promisor_pack_nr, promisor_pack_alloc;
+ uint32_t promisor_split;
+
int split_factor;
};
+void pack_geometry_repack_promisors(struct repository *repo,
+ const struct pack_objects_args *args,
+ const struct pack_geometry *geometry,
+ struct string_list *names,
+ const char *packtmp);
+
void pack_geometry_init(struct pack_geometry *geometry,
struct existing_packs *existing,
const struct pack_objects_args *args);
diff --git a/repo-settings.c b/repo-settings.c
index 195c24e9c0..208e09ff17 100644
--- a/repo-settings.c
+++ b/repo-settings.c
@@ -100,6 +100,9 @@ void prepare_repo_settings(struct repository *r)
*/
if (!repo_config_get_int(r, "index.version", &value))
r->settings.index_version = value;
+ repo_cfg_int(r, "core.maxtreedepth",
+ &r->settings.max_allowed_tree_depth,
+ DEFAULT_MAX_ALLOWED_TREE_DEPTH);
if (!repo_config_get_string_tmp(r, "core.untrackedcache", &strval)) {
int v = git_parse_maybe_bool(strval);
diff --git a/repo-settings.h b/repo-settings.h
index d477885561..cad9c3f0cc 100644
--- a/repo-settings.h
+++ b/repo-settings.h
@@ -67,6 +67,8 @@ struct repo_settings {
size_t packed_git_limit;
unsigned long big_file_threshold;
+ int max_allowed_tree_depth;
+
char *hooks_path;
};
#define REPO_SETTINGS_INIT { \
@@ -78,6 +80,7 @@ struct repo_settings {
.delta_base_cache_limit = DEFAULT_DELTA_BASE_CACHE_LIMIT, \
.packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE, \
.packed_git_limit = DEFAULT_PACKED_GIT_LIMIT, \
+ .max_allowed_tree_depth = DEFAULT_MAX_ALLOWED_TREE_DEPTH, \
}
void prepare_repo_settings(struct repository *r);
diff --git a/reset.c b/reset.c
index bb59027181..46e30e6394 100644
--- a/reset.c
+++ b/reset.c
@@ -163,7 +163,7 @@ int reset_head(struct repository *r, const struct reset_head_opts *opts)
goto leave_reset_head;
}
- tree = parse_tree_indirect(oid);
+ tree = repo_parse_tree_indirect(the_repository, oid);
if (!tree) {
ret = error(_("unable to read tree (%s)"), oid_to_hex(oid));
goto leave_reset_head;
diff --git a/revision.c b/revision.c
index 5f0850ae5c..9b131670f7 100644
--- a/revision.c
+++ b/revision.c
@@ -72,7 +72,7 @@ static void mark_tree_contents_uninteresting(struct repository *r,
struct tree_desc desc;
struct name_entry entry;
- if (parse_tree_gently(tree, 1) < 0)
+ if (repo_parse_tree_gently(the_repository, tree, 1) < 0)
return;
init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
@@ -179,7 +179,7 @@ static void add_children_by_path(struct repository *r,
if (!tree)
return;
- if (parse_tree_gently(tree, 1) < 0)
+ if (repo_parse_tree_gently(the_repository, tree, 1) < 0)
return;
init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
@@ -250,29 +250,6 @@ void mark_trees_uninteresting_sparse(struct repository *r,
paths_and_oids_clear(&map);
}
-struct commit_stack {
- struct commit **items;
- size_t nr, alloc;
-};
-#define COMMIT_STACK_INIT { 0 }
-
-static void commit_stack_push(struct commit_stack *stack, struct commit *commit)
-{
- ALLOC_GROW(stack->items, stack->nr + 1, stack->alloc);
- stack->items[stack->nr++] = commit;
-}
-
-static struct commit *commit_stack_pop(struct commit_stack *stack)
-{
- return stack->nr ? stack->items[--stack->nr] : NULL;
-}
-
-static void commit_stack_clear(struct commit_stack *stack)
-{
- FREE_AND_NULL(stack->items);
- stack->nr = stack->alloc = 0;
-}
-
static void mark_one_parent_uninteresting(struct rev_info *revs, struct commit *commit,
struct commit_stack *pending)
{
@@ -2541,14 +2518,14 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
die(_("--unpacked=<packfile> no longer supported"));
} else if (!strcmp(arg, "--no-kept-objects")) {
revs->no_kept_objects = 1;
- revs->keep_pack_cache_flags |= IN_CORE_KEEP_PACKS;
- revs->keep_pack_cache_flags |= ON_DISK_KEEP_PACKS;
+ revs->keep_pack_cache_flags |= KEPT_PACK_IN_CORE;
+ revs->keep_pack_cache_flags |= KEPT_PACK_ON_DISK;
} else if (skip_prefix(arg, "--no-kept-objects=", &optarg)) {
revs->no_kept_objects = 1;
if (!strcmp(optarg, "in-core"))
- revs->keep_pack_cache_flags |= IN_CORE_KEEP_PACKS;
+ revs->keep_pack_cache_flags |= KEPT_PACK_IN_CORE;
if (!strcmp(optarg, "on-disk"))
- revs->keep_pack_cache_flags |= ON_DISK_KEEP_PACKS;
+ revs->keep_pack_cache_flags |= KEPT_PACK_ON_DISK;
} else if (!strcmp(arg, "-r")) {
revs->diff = 1;
revs->diffopt.flags.recursive = 1;
diff --git a/sequencer.c b/sequencer.c
index 5476d39ba9..1f492f8460 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -767,7 +767,7 @@ static int do_recursive_merge(struct repository *r,
o.buffer_output = 2;
o.show_rename_progress = 1;
- head_tree = parse_tree_indirect(head);
+ head_tree = repo_parse_tree_indirect(the_repository, head);
if (!head_tree)
return error(_("unable to read tree (%s)"), oid_to_hex(head));
next_tree = next ? repo_get_commit_tree(r, next) : empty_tree(r);
@@ -4044,7 +4044,7 @@ static int do_reset(struct repository *r,
goto cleanup;
}
- tree = parse_tree_indirect(&oid);
+ tree = repo_parse_tree_indirect(the_repository, &oid);
if (!tree)
return error(_("unable to read tree (%s)"), oid_to_hex(&oid));
prime_cache_tree(r, r->index, tree);
diff --git a/setup.c b/setup.c
index 3a6a048620..b723f8b339 100644
--- a/setup.c
+++ b/setup.c
@@ -2693,7 +2693,7 @@ int init_db(const char *git_dir, const char *real_git_dir,
* have set up the repository format such that we can evaluate
* includeIf conditions correctly in the case of re-initialization.
*/
- repo_config(the_repository, platform_core_config, NULL);
+ repo_config(the_repository, git_default_core_config, NULL);
safe_create_dir(the_repository, git_dir, 0);
diff --git a/shallow.c b/shallow.c
index 186e9178f3..c870efcefc 100644
--- a/shallow.c
+++ b/shallow.c
@@ -471,6 +471,7 @@ void prepare_shallow_info(struct shallow_info *info, struct oid_array *sa)
{
trace_printf_key(&trace_shallow, "shallow: prepare_shallow_info\n");
memset(info, 0, sizeof(*info));
+ commit_stack_init(&info->commits);
info->shallow = sa;
if (!sa)
return;
@@ -503,6 +504,7 @@ void clear_shallow_info(struct shallow_info *info)
free(info->shallow_ref);
free(info->ours);
free(info->theirs);
+ commit_stack_clear(&info->commits);
}
/* Step 4, remove non-existent ones in "theirs" after getting the pack */
@@ -733,19 +735,13 @@ void assign_shallow_commits_to_refs(struct shallow_info *info,
free(shallow);
}
-struct commit_array {
- struct commit **commits;
- size_t nr, alloc;
-};
-
static int add_ref(const struct reference *ref, void *cb_data)
{
- struct commit_array *ca = cb_data;
- ALLOC_GROW(ca->commits, ca->nr + 1, ca->alloc);
- ca->commits[ca->nr] = lookup_commit_reference_gently(the_repository,
- ref->oid, 1);
- if (ca->commits[ca->nr])
- ca->nr++;
+ struct commit_stack *cs = cb_data;
+ struct commit *commit = lookup_commit_reference_gently(the_repository,
+ ref->oid, 1);
+ if (commit)
+ commit_stack_push(cs, commit);
return 0;
}
@@ -770,7 +766,7 @@ static void post_assign_shallow(struct shallow_info *info,
uint32_t **bitmap;
size_t dst, i, j;
size_t bitmap_nr = DIV_ROUND_UP(info->ref->nr, 32);
- struct commit_array ca;
+ struct commit_stack cs = COMMIT_STACK_INIT;
trace_printf_key(&trace_shallow, "shallow: post_assign_shallow\n");
if (ref_status)
@@ -793,9 +789,8 @@ static void post_assign_shallow(struct shallow_info *info,
}
info->nr_theirs = dst;
- memset(&ca, 0, sizeof(ca));
- refs_head_ref(get_main_ref_store(the_repository), add_ref, &ca);
- refs_for_each_ref(get_main_ref_store(the_repository), add_ref, &ca);
+ refs_head_ref(get_main_ref_store(the_repository), add_ref, &cs);
+ refs_for_each_ref(get_main_ref_store(the_repository), add_ref, &cs);
/* Remove unreachable shallow commits from "ours" */
for (i = dst = 0; i < info->nr_ours; i++) {
@@ -808,7 +803,7 @@ static void post_assign_shallow(struct shallow_info *info,
for (j = 0; j < bitmap_nr; j++)
if (bitmap[0][j]) {
/* Step 7, reachability test at commit level */
- int ret = repo_in_merge_bases_many(the_repository, c, ca.nr, ca.commits, 1);
+ int ret = repo_in_merge_bases_many(the_repository, c, cs.nr, cs.items, 1);
if (ret < 0)
exit(128);
if (!ret) {
@@ -820,7 +815,7 @@ static void post_assign_shallow(struct shallow_info *info,
}
info->nr_ours = dst;
- free(ca.commits);
+ commit_stack_clear(&cs);
}
/* (Delayed) step 7, reachability test at commit level */
@@ -830,22 +825,17 @@ int delayed_reachability_test(struct shallow_info *si, int c)
struct commit *commit = lookup_commit(the_repository,
&si->shallow->oid[c]);
- if (!si->commits) {
- struct commit_array ca;
-
- memset(&ca, 0, sizeof(ca));
+ if (!si->commits.nr) {
refs_head_ref(get_main_ref_store(the_repository),
- add_ref, &ca);
+ add_ref, &si->commits);
refs_for_each_ref(get_main_ref_store(the_repository),
- add_ref, &ca);
- si->commits = ca.commits;
- si->nr_commits = ca.nr;
+ add_ref, &si->commits);
}
si->reachable[c] = repo_in_merge_bases_many(the_repository,
commit,
- si->nr_commits,
- si->commits,
+ si->commits.nr,
+ si->commits.items,
1);
if (si->reachable[c] < 0)
exit(128);
diff --git a/shallow.h b/shallow.h
index ad591bd139..1c0787de1d 100644
--- a/shallow.h
+++ b/shallow.h
@@ -1,6 +1,7 @@
#ifndef SHALLOW_H
#define SHALLOW_H
+#include "commit.h"
#include "lockfile.h"
#include "object.h"
#include "repository.h"
@@ -69,8 +70,7 @@ struct shallow_info {
int *need_reachability_test;
int *reachable;
int *shallow_ref;
- struct commit **commits;
- size_t nr_commits;
+ struct commit_stack commits;
};
void prepare_shallow_info(struct shallow_info *, struct oid_array *);
diff --git a/strbuf.c b/strbuf.c
index 7fb7d12ac0..59678bf5b0 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -566,7 +566,7 @@ ssize_t strbuf_write(struct strbuf *sb, FILE *f)
return sb->len ? fwrite(sb->buf, 1, sb->len, f) : 0;
}
-#define STRBUF_MAXLINK (2*PATH_MAX)
+#define STRBUF_MAXLINK (32767)
int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)
{
@@ -578,12 +578,12 @@ int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)
while (hint < STRBUF_MAXLINK) {
ssize_t len;
- strbuf_grow(sb, hint);
- len = readlink(path, sb->buf, hint);
+ strbuf_grow(sb, hint + 1);
+ len = readlink(path, sb->buf, hint + 1);
if (len < 0) {
if (errno != ERANGE)
break;
- } else if (len < hint) {
+ } else if (len <= hint) {
strbuf_setlen(sb, len);
return 0;
}
diff --git a/t/helper/test-cache-tree.c b/t/helper/test-cache-tree.c
index 3ae45cec3b..ff61d0ca7e 100644
--- a/t/helper/test-cache-tree.c
+++ b/t/helper/test-cache-tree.c
@@ -41,7 +41,7 @@ int cmd__cache_tree(int argc, const char **argv)
die(_("unable to read index file"));
oidcpy(&oid, &the_repository->index->cache_tree->oid);
- tree = parse_tree_indirect(&oid);
+ tree = repo_parse_tree_indirect(the_repository, &oid);
if (!tree)
die(_("not a tree object: %s"), oid_to_hex(&oid));
diff --git a/t/helper/test-match-trees.c b/t/helper/test-match-trees.c
index e0e2048320..2ed064b971 100644
--- a/t/helper/test-match-trees.c
+++ b/t/helper/test-match-trees.c
@@ -19,10 +19,10 @@ int cmd__match_trees(int ac UNUSED, const char **av)
die("cannot parse %s as an object name", av[1]);
if (repo_get_oid(the_repository, av[2], &hash2))
die("cannot parse %s as an object name", av[2]);
- one = parse_tree_indirect(&hash1);
+ one = repo_parse_tree_indirect(the_repository, &hash1);
if (!one)
die("not a tree-ish %s", av[1]);
- two = parse_tree_indirect(&hash2);
+ two = repo_parse_tree_indirect(the_repository, &hash2);
if (!two)
die("not a tree-ish %s", av[2]);
diff --git a/t/helper/test-reach.c b/t/helper/test-reach.c
index c58c93800f..feabeb29c2 100644
--- a/t/helper/test-reach.c
+++ b/t/helper/test-reach.c
@@ -34,8 +34,8 @@ int cmd__reach(int ac, const char **av)
struct commit *A, *B;
struct commit_list *X, *Y;
struct object_array X_obj = OBJECT_ARRAY_INIT;
- struct commit **X_array, **Y_array;
- size_t X_nr, X_alloc, Y_nr, Y_alloc;
+ struct commit_stack X_stack = COMMIT_STACK_INIT;
+ struct commit_stack Y_stack = COMMIT_STACK_INIT;
struct strbuf buf = STRBUF_INIT;
struct repository *r = the_repository;
@@ -46,10 +46,6 @@ int cmd__reach(int ac, const char **av)
A = B = NULL;
X = Y = NULL;
- X_nr = Y_nr = 0;
- X_alloc = Y_alloc = 16;
- ALLOC_ARRAY(X_array, X_alloc);
- ALLOC_ARRAY(Y_array, Y_alloc);
while (strbuf_getline(&buf, stdin) != EOF) {
struct object_id oid;
@@ -88,15 +84,13 @@ int cmd__reach(int ac, const char **av)
case 'X':
commit_list_insert(c, &X);
- ALLOC_GROW(X_array, X_nr + 1, X_alloc);
- X_array[X_nr++] = c;
+ commit_stack_push(&X_stack, c);
add_object_array(orig, NULL, &X_obj);
break;
case 'Y':
commit_list_insert(c, &Y);
- ALLOC_GROW(Y_array, Y_nr + 1, Y_alloc);
- Y_array[Y_nr++] = c;
+ commit_stack_push(&Y_stack, c);
break;
default:
@@ -112,16 +106,16 @@ int cmd__reach(int ac, const char **av)
repo_in_merge_bases(the_repository, A, B));
else if (!strcmp(av[1], "in_merge_bases_many"))
printf("%s(A,X):%d\n", av[1],
- repo_in_merge_bases_many(the_repository, A, X_nr, X_array, 0));
+ repo_in_merge_bases_many(the_repository, A, X_stack.nr, X_stack.items, 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));
+ printf("%s(A,X):%d\n", av[1], get_branch_base_for_tip(r, A, X_stack.items, X_stack.nr));
else if (!strcmp(av[1], "get_merge_bases_many")) {
struct commit_list *list = NULL;
if (repo_get_merge_bases_many(the_repository,
- A, X_nr,
- X_array,
+ A, X_stack.nr,
+ X_stack.items,
&list) < 0)
exit(128);
printf("%s(A,X):\n", av[1]);
@@ -159,8 +153,8 @@ int cmd__reach(int ac, const char **av)
const int reachable_flag = 1;
int count = 0;
struct commit_list *current;
- struct commit_list *list = get_reachable_subset(X_array, X_nr,
- Y_array, Y_nr,
+ struct commit_list *list = get_reachable_subset(X_stack.items, X_stack.nr,
+ Y_stack.items, Y_stack.nr,
reachable_flag);
printf("get_reachable_subset(X,Y)\n");
for (current = list; current; current = current->next) {
@@ -169,8 +163,8 @@ int cmd__reach(int ac, const char **av)
oid_to_hex(&list->item->object.oid));
count++;
}
- for (size_t i = 0; i < Y_nr; i++) {
- if (Y_array[i]->object.flags & reachable_flag)
+ for (size_t i = 0; i < Y_stack.nr; i++) {
+ if (Y_stack.items[i]->object.flags & reachable_flag)
count--;
}
@@ -185,7 +179,7 @@ int cmd__reach(int ac, const char **av)
strbuf_release(&buf);
free_commit_list(X);
free_commit_list(Y);
- free(X_array);
- free(Y_array);
+ commit_stack_clear(&X_stack);
+ commit_stack_clear(&Y_stack);
return 0;
}
diff --git a/t/perf/p1006-cat-file.sh b/t/perf/p1006-cat-file.sh
index dcd8015379..da34ece242 100755
--- a/t/perf/p1006-cat-file.sh
+++ b/t/perf/p1006-cat-file.sh
@@ -9,4 +9,18 @@ test_perf 'cat-file --batch-check' '
git cat-file --batch-all-objects --batch-check
'
+test_perf 'list all objects (sorted)' '
+ git cat-file --batch-all-objects --batch-check="%(objectname)"
+'
+
+test_perf 'list all objects (unsorted)' '
+ git cat-file --batch-all-objects --batch-check="%(objectname)" \
+ --unordered
+'
+
+test_perf 'list blobs' '
+ git cat-file --batch-all-objects --batch-check="%(objectname)" \
+ --unordered --filter=object:type=blob
+'
+
test_done
diff --git a/t/perf/p6010-merge-base.sh b/t/perf/p6010-merge-base.sh
index 54f52fa23e..08212dd037 100755
--- a/t/perf/p6010-merge-base.sh
+++ b/t/perf/p6010-merge-base.sh
@@ -83,9 +83,9 @@ build_history2 () {
test_expect_success 'setup' '
max_level=15 &&
build_history $max_level | git fast-import --export-marks=marks &&
- git tag one &&
+ git branch one &&
build_history2 $max_level | git fast-import --import-marks=marks --force &&
- git tag two &&
+ git branch two &&
git gc &&
git log --format=%H --no-merges >expect
'
@@ -98,4 +98,8 @@ test_expect_success 'verify result' '
test_cmp expect actual
'
+test_perf 'git show-branch' '
+ git show-branch one two
+'
+
test_done
diff --git a/t/perf/perf-lib.sh b/t/perf/perf-lib.sh
index b15c74d6f1..2ac007888e 100644
--- a/t/perf/perf-lib.sh
+++ b/t/perf/perf-lib.sh
@@ -20,7 +20,7 @@
# These variables must be set before the inclusion of test-lib.sh below,
# because it will change our working directory.
TEST_DIRECTORY=$(pwd)/..
-TEST_OUTPUT_DIRECTORY=$(pwd)
+perf_dir=$(pwd)
TEST_NO_CREATE_REPO=t
TEST_NO_MALLOC_CHECK=t
@@ -58,6 +58,7 @@ then
fi
. "$GIT_BUILD_DIR"/GIT-BUILD-OPTIONS
+: ${TEST_OUTPUT_DIRECTORY:=$perf_dir}
. "$GIT_SOURCE_DIR"/t/test-lib.sh
# Then restore GIT_PERF_* settings.
diff --git a/t/perf/run b/t/perf/run
index 073bcb2aff..13913db4a3 100755
--- a/t/perf/run
+++ b/t/perf/run
@@ -204,8 +204,18 @@ run_subsection () {
get_var_from_env_or_config "GIT_PERF_CODESPEED_OUTPUT" "perf" "codespeedOutput" "--bool"
get_var_from_env_or_config "GIT_PERF_SEND_TO_CODESPEED" "perf" "sendToCodespeed"
+# Preserve GIT_PERF settings from the environment when loading
+# GIT-BUILD-OPTIONS; see the similar hack in perf-lib.sh.
+git_perf_settings="$(env |
+ sed -n "/^GIT_PERF_/{
+ # escape all single-quotes in the value
+ s/'/'\\\\''/g
+ # turn this into an eval-able assignment
+ s/^\\([^=]*=\\)\\(.*\\)/\\1'\\2'/p
+ }")"
cd "$(dirname $0)"
. ../../GIT-BUILD-OPTIONS
+eval "$git_perf_settings"
if test -n "$TEST_OUTPUT_DIRECTORY"
then
diff --git a/t/t0450-txt-doc-vs-help.sh b/t/t0450-txt-doc-vs-help.sh
index e12e18f97f..822b0d55a5 100755
--- a/t/t0450-txt-doc-vs-help.sh
+++ b/t/t0450-txt-doc-vs-help.sh
@@ -56,7 +56,7 @@ adoc_to_synopsis () {
b2t="$(builtin_to_adoc "$builtin")" &&
sed -n \
-E '/^\[(verse|synopsis)\]$/,/^$/ {
- /^$/d;
+ /^$/q;
/^\[(verse|synopsis)\]$/d;
s/\{litdd\}/--/g;
s/'\''(git[ a-z-]*)'\''/\1/g;
diff --git a/t/t0602-reffiles-fsck.sh b/t/t0602-reffiles-fsck.sh
index 0ef483659d..3c1f553b81 100755
--- a/t/t0602-reffiles-fsck.sh
+++ b/t/t0602-reffiles-fsck.sh
@@ -905,4 +905,34 @@ test_expect_success '--[no-]references option should apply to fsck' '
)
'
+test_expect_success 'complains about broken root ref' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ echo "ref: refs/heads/../HEAD" >.git/HEAD &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: HEAD: badReferentName: points to invalid refname ${SQ}refs/heads/../HEAD${SQ}
+ EOF
+ test_cmp expect err
+ )
+'
+
+test_expect_success 'complains about broken root ref in worktree' '
+ test_when_finished "rm -rf repo worktree" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit initial &&
+ git worktree add ../worktree &&
+ echo "ref: refs/heads/../HEAD" >.git/worktrees/worktree/HEAD &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: worktrees/worktree/HEAD: badReferentName: points to invalid refname ${SQ}refs/heads/../HEAD${SQ}
+ EOF
+ test_cmp expect err
+ )
+'
+
test_done
diff --git a/t/t0614-reftable-fsck.sh b/t/t0614-reftable-fsck.sh
index 677eb9143c..d24b87f961 100755
--- a/t/t0614-reftable-fsck.sh
+++ b/t/t0614-reftable-fsck.sh
@@ -55,4 +55,48 @@ for TABLE_NAME in "foo-bar-e4d12d59.ref" \
'
done
+test_expect_success 'worktree stacks can be verified' '
+ test_when_finished "rm -rf repo worktree" &&
+ git init repo &&
+ test_commit -C repo initial &&
+ git -C repo worktree add ../worktree &&
+
+ git -C worktree refs verify 2>err &&
+ test_must_be_empty err &&
+
+ REFTABLE_DIR=$(git -C worktree rev-parse --git-dir)/reftable &&
+ EXISTING_TABLE=$(head -n1 "$REFTABLE_DIR/tables.list") &&
+ mv "$REFTABLE_DIR/$EXISTING_TABLE" "$REFTABLE_DIR/broken.ref" &&
+
+ for d in repo worktree
+ do
+ echo "broken.ref" >"$REFTABLE_DIR/tables.list" &&
+ git -C "$d" refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: broken.ref: badReftableTableName: invalid reftable table name
+ EOF
+ test_cmp expect err &&
+
+ echo garbage >"$REFTABLE_DIR/tables.list" &&
+ test_must_fail git -C "$d" refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: reftable stack for worktree ${SQ}worktree${SQ} is broken
+ EOF
+ test_cmp expect err || return 1
+
+ done
+'
+
+test_expect_success 'invalid symref gets reported' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_commit -C repo initial &&
+ git -C repo symbolic-ref refs/heads/symref garbage &&
+ test_must_fail git -C repo refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: refs/heads/symref: badReferentName: points to invalid refname ${SQ}garbage${SQ}
+ EOF
+ test_cmp expect err
+'
+
test_done
diff --git a/t/t1300-config.sh b/t/t1300-config.sh
index 358d636379..9850fcd5b5 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -1232,12 +1232,12 @@ test_expect_success SYMLINKS 'symlinked configuration' '
test_when_finished "rm myconfig" &&
ln -s notyet myconfig &&
git config --file=myconfig test.frotz nitfol &&
- test -h myconfig &&
- test -f notyet &&
+ test_path_is_symlink myconfig &&
+ test_path_is_file notyet &&
test "z$(git config --file=notyet test.frotz)" = znitfol &&
git config --file=myconfig test.xyzzy rezrov &&
- test -h myconfig &&
- test -f notyet &&
+ test_path_is_symlink myconfig &&
+ test_path_is_file notyet &&
cat >expect <<-\EOF &&
nitfol
rezrov
diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh
index e30f87a358..ce71f9a30a 100755
--- a/t/t1410-reflog.sh
+++ b/t/t1410-reflog.sh
@@ -130,10 +130,10 @@ test_expect_success 'pass through -- to sub-command' '
test_expect_success rewind '
test_tick && git reset --hard HEAD~2 &&
- test -f C &&
- test -f A/B/E &&
- ! test -f F &&
- ! test -f A/G &&
+ test_path_is_file C &&
+ test_path_is_file A/B/E &&
+ test_path_is_missing F &&
+ test_path_is_missing A/G &&
check_have A B C D E F G H I J K L &&
diff --git a/t/t1420-lost-found.sh b/t/t1420-lost-found.sh
index 2fb2f44f02..926c6d63e3 100755
--- a/t/t1420-lost-found.sh
+++ b/t/t1420-lost-found.sh
@@ -28,9 +28,12 @@ test_expect_success 'lost and found something' '
test_tick &&
git reset --hard HEAD^ &&
git fsck --lost-found &&
- test 2 = $(ls .git/lost-found/*/* | wc -l) &&
- test -f .git/lost-found/commit/$(cat lost-commit) &&
- test -f .git/lost-found/other/$(cat lost-other)
+ ls .git/lost-found/*/* >actual &&
+ cat >expect <<-EOF &&
+ .git/lost-found/commit/$(cat lost-commit)
+ .git/lost-found/other/$(cat lost-other)
+ EOF
+ test_cmp expect actual
'
test_done
diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
index c4b651c2dc..3fae05f9d9 100755
--- a/t/t1450-fsck.sh
+++ b/t/t1450-fsck.sh
@@ -105,7 +105,7 @@ test_expect_success REFFILES 'HEAD link pointing at a funny object' '
echo $ZERO_OID >.git/HEAD &&
# avoid corrupt/broken HEAD from interfering with repo discovery
test_must_fail env GIT_DIR=.git git fsck 2>out &&
- test_grep "detached HEAD points" out
+ test_grep "HEAD: badRefOid: points to invalid object ID ${SQ}$ZERO_OID${SQ}" out
'
test_expect_success 'HEAD link pointing at a funny place' '
@@ -113,7 +113,7 @@ test_expect_success 'HEAD link pointing at a funny place' '
test-tool ref-store main create-symref HEAD refs/funny/place &&
# avoid corrupt/broken HEAD from interfering with repo discovery
test_must_fail env GIT_DIR=.git git fsck 2>out &&
- test_grep "HEAD points to something strange" out
+ test_grep "HEAD: badHeadTarget: HEAD points to non-branch ${SQ}refs/funny/place${SQ}" out
'
test_expect_success REFFILES 'HEAD link pointing at a funny object (from different wt)' '
@@ -123,7 +123,7 @@ test_expect_success REFFILES 'HEAD link pointing at a funny object (from differe
echo $ZERO_OID >.git/HEAD &&
# avoid corrupt/broken HEAD from interfering with repo discovery
test_must_fail git -C wt fsck 2>out &&
- test_grep "main-worktree/HEAD: detached HEAD points" out
+ test_grep "HEAD: badRefOid: points to invalid object ID ${SQ}$ZERO_OID${SQ}" out
'
test_expect_success REFFILES 'other worktree HEAD link pointing at a funny object' '
@@ -131,7 +131,7 @@ test_expect_success REFFILES 'other worktree HEAD link pointing at a funny objec
git worktree add other &&
echo $ZERO_OID >.git/worktrees/other/HEAD &&
test_must_fail git fsck 2>out &&
- test_grep "worktrees/other/HEAD: detached HEAD points" out
+ test_grep "worktrees/other/HEAD: badRefOid: points to invalid object ID ${SQ}$ZERO_OID${SQ}" out
'
test_expect_success 'other worktree HEAD link pointing at missing object' '
@@ -148,7 +148,7 @@ test_expect_success 'other worktree HEAD link pointing at a funny place' '
git worktree add other &&
git -C other symbolic-ref HEAD refs/funny/place &&
test_must_fail git fsck 2>out &&
- test_grep "worktrees/other/HEAD points to something strange" out
+ test_grep "worktrees/other/HEAD: badHeadTarget: HEAD points to non-branch ${SQ}refs/funny/place${SQ}" out
'
test_expect_success 'commit with multiple signatures is okay' '
diff --git a/t/t2021-checkout-overwrite.sh b/t/t2021-checkout-overwrite.sh
index a5c03d5d4a..38c41ae373 100755
--- a/t/t2021-checkout-overwrite.sh
+++ b/t/t2021-checkout-overwrite.sh
@@ -27,7 +27,7 @@ test_expect_success 'checkout commit with dir must not remove untracked a/b' '
git rm --cached a/b &&
git commit -m "un-track the file" &&
test_must_fail git checkout start &&
- test -f a/b
+ test_path_is_file a/b
'
test_expect_success 'create a commit where dir a/b changed to symlink' '
@@ -49,7 +49,7 @@ test_expect_success 'checkout commit with dir must not remove untracked a/b' '
test_expect_success SYMLINKS 'the symlink remained' '
- test -h a/b
+ test_path_is_symlink a/b
'
test_expect_success 'cleanup after previous symlink tests' '
diff --git a/t/t3650-replay-basics.sh b/t/t3650-replay-basics.sh
index cf3aacf355..307101eeb9 100755
--- a/t/t3650-replay-basics.sh
+++ b/t/t3650-replay-basics.sh
@@ -43,6 +43,13 @@ test_expect_success 'setup' '
test_commit L &&
test_commit M &&
+ git switch --detach topic4 &&
+ test_commit N &&
+ test_commit O &&
+ git switch -c topic-with-merge topic4 &&
+ test_merge P O --no-ff &&
+ git switch main &&
+
git switch -c conflict B &&
test_commit C.conflict C.t conflict
'
@@ -51,6 +58,53 @@ test_expect_success 'setup bare' '
git clone --bare . bare
'
+test_expect_success 'argument to --advance must be a reference' '
+ echo "fatal: argument to --advance must be a reference" >expect &&
+ oid=$(git rev-parse main) &&
+ test_must_fail git replay --advance=$oid topic1..topic2 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--onto with invalid commit-ish' '
+ printf "fatal: ${SQ}refs/not-valid${SQ} is not " >expect &&
+ printf "a valid commit-ish for --onto\n" >>expect &&
+ test_must_fail git replay --onto=refs/not-valid topic1..topic2 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'option --onto or --advance is mandatory' '
+ echo "error: option --onto or --advance is mandatory" >expect &&
+ test_might_fail git replay -h >>expect &&
+ test_must_fail git replay topic1..topic2 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'no base or negative ref gives no-replaying down to root error' '
+ echo "fatal: replaying down from root commit is not supported yet!" >expect &&
+ test_must_fail git replay --onto=topic1 topic2 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'options --advance and --contained cannot be used together' '
+ printf "fatal: options ${SQ}--advance${SQ} " >expect &&
+ printf "and ${SQ}--contained${SQ} cannot be used together\n" >>expect &&
+ test_must_fail git replay --advance=main --contained \
+ topic1..topic2 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'cannot advance target ... ordering would be ill-defined' '
+ echo "fatal: cannot advance target with multiple sources because ordering would be ill-defined" >expect &&
+ test_must_fail git replay --advance=main main topic1 topic2 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'replaying merge commits is not supported yet' '
+ echo "fatal: replaying merge commits is not supported yet!" >expect &&
+ test_must_fail git replay --advance=main main..topic-with-merge 2>actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'using replay to rebase two branches, one on top of other' '
git replay --ref-action=print --onto main topic1..topic2 >result &&
diff --git a/t/t4067-diff-partial-clone.sh b/t/t4067-diff-partial-clone.sh
index 581250dd2d..72f25de449 100755
--- a/t/t4067-diff-partial-clone.sh
+++ b/t/t4067-diff-partial-clone.sh
@@ -132,6 +132,41 @@ test_expect_success 'diff with rename detection batches blobs' '
test_line_count = 1 done_lines
'
+test_expect_success 'diff succeeds even if entries are removed from queue' '
+ test_when_finished "rm -rf server client trace" &&
+
+ test_create_repo server &&
+ for l in a c e g i p
+ do
+ echo $l >server/$l &&
+ git -C server add $l || return 1
+ done &&
+ git -C server commit -m x &&
+
+ for l in a e i
+ do
+ git -C server rm $l || return 1
+ done &&
+
+ for l in b d f i
+ do
+ echo $l$l >server/$l &&
+ git -C server add $l || return 1
+ done &&
+ git -C server commit -a -m x &&
+
+ test_config -C server uploadpack.allowfilter 1 &&
+ test_config -C server uploadpack.allowanysha1inwant 1 &&
+ git clone --filter=blob:limit=0 "file://$(pwd)/server" client &&
+
+ for file in $(ls client)
+ do
+ cat client/$file >$file &&
+ mv $file client/$file || return 1
+ done &&
+ git -C client diff --name-only --relative HEAD^
+'
+
test_expect_success 'diff does not fetch anything if inexact rename detection is not needed' '
test_when_finished "rm -rf server client trace" &&
diff --git a/t/t5003-archive-zip.sh b/t/t5003-archive-zip.sh
index 961c6aac25..c8c1c5c06b 100755
--- a/t/t5003-archive-zip.sh
+++ b/t/t5003-archive-zip.sh
@@ -239,6 +239,40 @@ check_zip with_untracked2
check_added with_untracked2 untracked one/untracked
check_added with_untracked2 untracked two/untracked
+test_expect_success 'git-archive --format=zip with bigFile delta chains' '
+ test_when_finished rm -rf repo &&
+ git init repo &&
+ (
+ cd repo &&
+ test-tool genrandom foo 100000 >base &&
+ {
+ cat base &&
+ echo "trailing data"
+ } >delta-1 &&
+ {
+ cat delta-1 &&
+ echo "trailing data"
+ } >delta-2 &&
+ git add . &&
+ git commit -m "blobs" &&
+ git repack -Ad &&
+ git verify-pack -v .git/objects/pack/pack-*.idx >stats &&
+ test_grep "chain length = 1: 1 object" stats &&
+ test_grep "chain length = 2: 1 object" stats &&
+
+ git -c core.bigFileThreshold=1k archive --format=zip HEAD >archive.zip &&
+ if test_have_prereq UNZIP
+ then
+ mkdir unpack &&
+ cd unpack &&
+ "$GIT_UNZIP" ../archive.zip &&
+ test_cmp base ../base &&
+ test_cmp delta-1 ../delta-1 &&
+ test_cmp delta-2 ../delta-2
+ fi
+ )
+'
+
# Test remote archive over HTTP protocol.
#
# Note: this should be the last part of this test suite, because
diff --git a/t/t5319-multi-pack-index.sh b/t/t5319-multi-pack-index.sh
index 794f8b5ab4..faae98c7e7 100755
--- a/t/t5319-multi-pack-index.sh
+++ b/t/t5319-multi-pack-index.sh
@@ -415,8 +415,6 @@ test_expect_success 'up-to-date multi-pack-index is retained' '
)
'
-test_done
-
test_expect_success 'verify multi-pack-index success' '
git multi-pack-index verify --object-dir=$objdir
'
diff --git a/t/t5331-pack-objects-stdin.sh b/t/t5331-pack-objects-stdin.sh
index 4a8df5a389..cd949025b9 100755
--- a/t/t5331-pack-objects-stdin.sh
+++ b/t/t5331-pack-objects-stdin.sh
@@ -319,6 +319,45 @@ test_expect_success '--stdin-packs=follow walks into unknown packs' '
)
'
+test_expect_success '--stdin-packs with promisors' '
+ test_when_finished "rm -fr repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ git config set maintenance.auto false &&
+ git remote add promisor garbage &&
+ git config set remote.promisor.promisor true &&
+
+ for c in A B C D
+ do
+ echo "$c" >file &&
+ git add file &&
+ git commit --message "$c" &&
+ git tag "$c" || return 1
+ done &&
+
+ A="$(echo A | git pack-objects --revs $packdir/pack)" &&
+ B="$(echo A..B | git pack-objects --revs $packdir/pack --filter=blob:none)" &&
+ C="$(echo B..C | git pack-objects --revs $packdir/pack)" &&
+ D="$(echo C..D | git pack-objects --revs $packdir/pack)" &&
+ touch $packdir/pack-$B.promisor &&
+
+ test_must_fail git pack-objects --stdin-packs --exclude-promisor-objects pack- 2>err <<-EOF &&
+ pack-$B.pack
+ EOF
+ test_grep "is a promisor but --exclude-promisor-objects was given" err &&
+
+ PACK=$(git pack-objects --stdin-packs=follow --exclude-promisor-objects $packdir/pack <<-EOF
+ pack-$D.pack
+ EOF
+ ) &&
+ objects_in_packs $C $D >expect &&
+ objects_in_packs $PACK >actual &&
+ test_cmp expect actual &&
+ rm -f $packdir/pack-$PACK.*
+ )
+'
+
stdin_packs__follow_with_only () {
rm -fr stdin_packs__follow_with_only &&
git init stdin_packs__follow_with_only &&
diff --git a/t/t5403-post-checkout-hook.sh b/t/t5403-post-checkout-hook.sh
index 978f240cda..1462e3365b 100755
--- a/t/t5403-post-checkout-hook.sh
+++ b/t/t5403-post-checkout-hook.sh
@@ -109,7 +109,7 @@ test_expect_success 'post-checkout hook is triggered by clone' '
echo "$@" >"$GIT_DIR/post-checkout.args"
EOF
git clone --template=templates . clone3 &&
- test -f clone3/.git/post-checkout.args
+ test_path_is_file clone3/.git/post-checkout.args
'
test_done
diff --git a/t/t5750-bundle-uri-parse.sh b/t/t5750-bundle-uri-parse.sh
index 80a3f83ffb..294f9d9c64 100755
--- a/t/t5750-bundle-uri-parse.sh
+++ b/t/t5750-bundle-uri-parse.sh
@@ -286,4 +286,30 @@ test_expect_success 'parse config format edge cases: creationToken heuristic' '
grep "could not parse bundle list key creationToken with value '\''bogus'\''" err
'
+test_expect_success 'parse config format: bundle with missing uri' '
+ cat >input <<-\EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ [bundle "missing-uri"]
+ creationToken = 1
+ EOF
+
+ test_must_fail test-tool bundle-uri parse-config input 2>err &&
+ grep "bundle '\''missing-uri'\'' has no uri" err
+'
+
+test_expect_success 'parse config format: bundle with url instead of uri' '
+ cat >input <<-\EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ [bundle "typo"]
+ url = https://example.com/bundle.bdl
+ EOF
+
+ test_must_fail test-tool bundle-uri parse-config input 2>err &&
+ grep "bundle '\''typo'\'' has no uri" err
+'
+
test_done
diff --git a/t/t6422-merge-rename-corner-cases.sh b/t/t6422-merge-rename-corner-cases.sh
index f14c0fb30e..e18d5a227d 100755
--- a/t/t6422-merge-rename-corner-cases.sh
+++ b/t/t6422-merge-rename-corner-cases.sh
@@ -1439,4 +1439,90 @@ test_expect_success 'rename/rename(1to2) with a binary file' '
)
'
+# Testcase preliminary submodule/directory conflict and submodule rename
+# Commit O: <empty, or additional irrelevant stuff>
+# Commit A1: introduce "folder" (as a tree)
+# Commit B1: introduce "folder" (as a submodule)
+# Commit A2: merge B1 into A1, but keep folder as a tree
+# Commit B2: merge A1 into B1, but keep folder as a submodule
+# Merge A2 & B2
+test_setup_submodule_directory_preliminary_conflict () {
+ git init submodule_directory_preliminary_conflict &&
+ (
+ cd submodule_directory_preliminary_conflict &&
+
+ # Trying to do the A2 and B2 merges above is slightly more
+ # challenging with a local submodule (because checking out
+ # another commit has the submodule in the way). Instead,
+ # first create the commits with the wrong parents but right
+ # trees, in the order A1, A2, B1, B2...
+ #
+ # Then go back and create new A2 & B2 with the correct
+ # parents and the same trees.
+
+ git commit --allow-empty -m orig &&
+
+ git branch A &&
+ git branch B &&
+
+ git checkout B &&
+ mkdir folder &&
+ echo A>folder/A &&
+ echo B>folder/B &&
+ echo C>folder/C &&
+ echo D>folder/D &&
+ echo E>folder/E &&
+ git add folder &&
+ git commit -m B1 &&
+
+ git commit --allow-empty -m B2 &&
+
+ git checkout A &&
+ git init folder &&
+ (
+ cd folder &&
+ >Z &&
+ >Y &&
+ git add Z Y &&
+ git commit -m "original submodule commit"
+ ) &&
+ git add folder &&
+ git commit -m A1 &&
+
+ git commit --allow-empty -m A2 &&
+
+ NewA2=$(git commit-tree -p A^ -p B^ -m "Merge B into A" A^{tree}) &&
+ NewB2=$(git commit-tree -p B^ -p A^ -m "Merge A into B" B^{tree}) &&
+ git update-ref refs/heads/A $NewA2 &&
+ git update-ref refs/heads/B $NewB2
+ )
+}
+
+test_expect_success 'submodule/directory preliminary conflict' '
+ test_setup_submodule_directory_preliminary_conflict &&
+ (
+ cd submodule_directory_preliminary_conflict &&
+
+ git checkout A^0 &&
+
+ test_expect_code 1 git merge B^0 &&
+
+ # Make sure the index has the right number of entries
+ git ls-files -s >actual &&
+ test_line_count = 2 actual &&
+
+ # The "folder" as directory should have been resolved away
+ # as part of the merge. The "folder" as submodule got
+ # renamed to "folder~Temporary merge branch 2" in the
+ # virtual merge base, resulting in a
+ # "folder~Temporary merge branch 2" -> "folder"
+ # rename in the outermerge for the submodule, which then
+ # becomes part of a rename/delete conflict (because "folder"
+ # as a submodule was deleted in A2).
+ submod=$(git rev-parse A:folder) &&
+ printf "160000 $submod 1\tfolder\n160000 $submod 2\tfolder\n" >expect &&
+ test_cmp expect actual
+ )
+'
+
test_done
diff --git a/t/t7101-reset-empty-subdirs.sh b/t/t7101-reset-empty-subdirs.sh
index 33d5d5b76e..d1d3e231fc 100755
--- a/t/t7101-reset-empty-subdirs.sh
+++ b/t/t7101-reset-empty-subdirs.sh
@@ -34,32 +34,32 @@ test_expect_success 'resetting tree HEAD^' '
'
test_expect_success 'checking initial files exist after rewind' '
- test -d path0 &&
- test -f path0/COPYING
+ test_path_is_dir path0 &&
+ test_path_is_file path0/COPYING
'
test_expect_success 'checking lack of path1/path2/COPYING' '
- ! test -f path1/path2/COPYING
+ test_path_is_missing path1/path2/COPYING
'
test_expect_success 'checking lack of path1/COPYING' '
- ! test -f path1/COPYING
+ test_path_is_missing path1/COPYING
'
test_expect_success 'checking lack of COPYING' '
- ! test -f COPYING
+ test_path_is_missing COPYING
'
-test_expect_success 'checking checking lack of path1/COPYING-TOO' '
- ! test -f path0/COPYING-TOO
+test_expect_success 'checking lack of path0/COPYING-TOO' '
+ test_path_is_missing path0/COPYING-TOO
'
test_expect_success 'checking lack of path1/path2' '
- ! test -d path1/path2
+ test_path_is_missing path1/path2
'
test_expect_success 'checking lack of path1' '
- ! test -d path1
+ test_path_is_missing path1
'
test_done
diff --git a/t/t7703-repack-geometric.sh b/t/t7703-repack-geometric.sh
index 98806cdb6f..04d5d8fc33 100755
--- a/t/t7703-repack-geometric.sh
+++ b/t/t7703-repack-geometric.sh
@@ -480,4 +480,65 @@ test_expect_success '--geometric -l disables writing bitmaps with non-local pack
test_path_is_file member/.git/objects/pack/multi-pack-index-*.bitmap
'
+write_packfile () {
+ NR="$1"
+ PREFIX="$2"
+
+ printf "blob\ndata <<EOB\n$PREFIX %s\nEOB\n" $(test_seq $NR) |
+ git fast-import &&
+ git pack-objects --pack-loose-unreachable .git/objects/pack/pack &&
+ git prune-packed
+}
+
+write_promisor_packfile () {
+ PACKFILE=$(write_packfile "$@") &&
+ touch .git/objects/pack/pack-$PACKFILE.promisor &&
+ echo "$PACKFILE"
+}
+
+test_expect_success 'geometric repack works with promisor packs' '
+ test_when_finished "rm -fr repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ git config set maintenance.auto false &&
+ git remote add promisor garbage &&
+ git config set remote.promisor.promisor true &&
+
+ # Packs A and B need to be merged.
+ NORMAL_A=$(write_packfile 2 normal-a) &&
+ NORMAL_B=$(write_packfile 2 normal-b) &&
+ NORMAL_C=$(write_packfile 14 normal-c) &&
+
+ # Packs A, B and C need to be merged.
+ PROMISOR_A=$(write_promisor_packfile 1 promisor-a) &&
+ PROMISOR_B=$(write_promisor_packfile 3 promisor-b) &&
+ PROMISOR_C=$(write_promisor_packfile 3 promisor-c) &&
+ PROMISOR_D=$(write_promisor_packfile 20 promisor-d) &&
+ PROMISOR_E=$(write_promisor_packfile 40 promisor-e) &&
+
+ git cat-file --batch-all-objects --batch-check="%(objectname)" >objects-expect &&
+
+ ls .git/objects/pack/*.pack >packs-before &&
+ test_line_count = 8 packs-before &&
+ git repack --geometric=2 -d &&
+ ls .git/objects/pack/*.pack >packs-after &&
+ test_line_count = 5 packs-after &&
+ test_grep ! "$NORMAL_A" packs-after &&
+ test_grep ! "$NORMAL_B" packs-after &&
+ test_grep "$NORMAL_C" packs-after &&
+ test_grep ! "$PROMISOR_A" packs-after &&
+ test_grep ! "$PROMISOR_B" packs-after &&
+ test_grep ! "$PROMISOR_C" packs-after &&
+ test_grep "$PROMISOR_D" packs-after &&
+ test_grep "$PROMISOR_E" packs-after &&
+
+ ls .git/objects/pack/*.promisor >promisors &&
+ test_line_count = 3 promisors &&
+
+ git cat-file --batch-all-objects --batch-check="%(objectname)" >objects-actual &&
+ test_cmp objects-expect objects-actual
+ )
+'
+
test_done
diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh
index bf0f67378d..8a91ff3603 100755
--- a/t/t7800-difftool.sh
+++ b/t/t7800-difftool.sh
@@ -647,21 +647,21 @@ test_expect_success SYMLINKS 'difftool --dir-diff --symlinks without unstaged ch
'
write_script modify-right-file <<\EOF
-echo "new content" >"$2/file"
+echo "modified content" >"$2/file"
EOF
run_dir_diff_test 'difftool --dir-diff syncs worktree with unstaged change' '
test_when_finished git reset --hard &&
echo "orig content" >file &&
git difftool -d $symlinks --extcmd "$PWD/modify-right-file" branch &&
- echo "new content" >expect &&
+ echo "modified content" >expect &&
test_cmp expect file
'
run_dir_diff_test 'difftool --dir-diff syncs worktree without unstaged change' '
test_when_finished git reset --hard &&
git difftool -d $symlinks --extcmd "$PWD/modify-right-file" branch &&
- echo "new content" >expect &&
+ echo "modified content" >expect &&
test_cmp expect file
'
diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh
index 6b36f52df7..7cc0ce57f8 100755
--- a/t/t7900-maintenance.sh
+++ b/t/t7900-maintenance.sh
@@ -206,6 +206,31 @@ test_expect_success 'commit-graph auto condition' '
test_subcommand $COMMIT_GRAPH_WRITE <cg-two-satisfied.txt
'
+test_expect_success 'commit-graph auto condition with merges' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ git config set maintenance.auto false &&
+ test_commit initial &&
+ git switch --create feature &&
+ test_commit feature-1 &&
+ test_commit feature-2 &&
+ git switch - &&
+ test_commit main-1 &&
+ test_commit main-2 &&
+ git merge feature &&
+
+ # We have 6 commits, none of which are covered by a commit
+ # graph. So this must be the boundary at which we start to
+ # perform maintenance.
+ test_must_fail git -c maintenance.commit-graph.auto=7 \
+ maintenance is-needed --auto --task=commit-graph &&
+ git -c maintenance.commit-graph.auto=6 \
+ maintenance is-needed --auto --task=commit-graph
+ )
+'
+
test_expect_success 'run --task=bogus' '
test_must_fail git maintenance run --task=bogus 2>err &&
test_grep "is not a valid task" err
diff --git a/t/unit-tests/clar/.github/workflows/ci.yml b/t/unit-tests/clar/.github/workflows/ci.yml
index 4d4724222c..14cb4ed1d4 100644
--- a/t/unit-tests/clar/.github/workflows/ci.yml
+++ b/t/unit-tests/clar/.github/workflows/ci.yml
@@ -53,7 +53,7 @@ jobs:
if: matrix.platform.image == 'i386/debian:latest'
run: apt -q update && apt -q -y install cmake gcc libc6-amd64 lib64stdc++6 make python3
- name: Check out
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Build
shell: bash
run: |
diff --git a/t/unit-tests/clar/clar.c b/t/unit-tests/clar/clar.c
index d6176e50b2..e959a5ae02 100644
--- a/t/unit-tests/clar/clar.c
+++ b/t/unit-tests/clar/clar.c
@@ -24,6 +24,14 @@
#include <sys/types.h>
#include <sys/stat.h>
+#ifndef va_copy
+# ifdef __va_copy
+# define va_copy(dst, src) __va_copy(dst, src)
+# else
+# define va_copy(dst, src) ((dst) = (src))
+# endif
+#endif
+
#if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_WCHAR__)
/*
* uClibc can optionally be built without wchar support, in which case
@@ -76,8 +84,10 @@
# define S_ISDIR(x) ((x & _S_IFDIR) != 0)
# endif
# define p_snprintf(buf,sz,fmt,...) _snprintf_s(buf,sz,_TRUNCATE,fmt,__VA_ARGS__)
+# define p_vsnprintf _vsnprintf
# else
# define p_snprintf snprintf
+# define p_vsnprintf vsnprintf
# endif
# define localtime_r(timer, buf) (localtime_s(buf, timer) == 0 ? buf : NULL)
@@ -86,6 +96,7 @@
# include <unistd.h>
# define _MAIN_CC
# define p_snprintf snprintf
+# define p_vsnprintf vsnprintf
typedef struct stat STAT_T;
#endif
@@ -699,13 +710,14 @@ void clar__skip(void)
abort_test();
}
-void clar__fail(
+static void clar__failv(
const char *file,
const char *function,
size_t line,
+ int should_abort,
const char *error_msg,
const char *description,
- int should_abort)
+ va_list args)
{
struct clar_error *error;
@@ -725,9 +737,19 @@ void clar__fail(
error->line_number = _clar.invoke_line ? _clar.invoke_line : line;
error->error_msg = error_msg;
- if (description != NULL &&
- (error->description = strdup(description)) == NULL)
- clar_abort("Failed to allocate description.\n");
+ if (description != NULL) {
+ va_list args_copy;
+ int len;
+
+ va_copy(args_copy, args);
+ if ((len = p_vsnprintf(NULL, 0, description, args_copy)) < 0)
+ clar_abort("Failed to compute description.");
+ va_end(args_copy);
+
+ if ((error->description = calloc(1, len + 1)) == NULL)
+ clar_abort("Failed to allocate buffer.");
+ p_vsnprintf(error->description, len + 1, description, args);
+ }
_clar.total_errors++;
_clar.last_report->status = CL_TEST_FAILURE;
@@ -736,6 +758,34 @@ void clar__fail(
abort_test();
}
+void clar__failf(
+ const char *file,
+ const char *function,
+ size_t line,
+ int should_abort,
+ const char *error_msg,
+ const char *description,
+ ...)
+{
+ va_list args;
+ va_start(args, description);
+ clar__failv(file, function, line, should_abort, error_msg,
+ description, args);
+ va_end(args);
+}
+
+void clar__fail(
+ const char *file,
+ const char *function,
+ size_t line,
+ const char *error_msg,
+ const char *description,
+ int should_abort)
+{
+ clar__failf(file, function, line, should_abort, error_msg,
+ description ? "%s" : NULL, description);
+}
+
void clar__assert(
int condition,
const char *file,
@@ -889,6 +939,92 @@ void clar__assert_equal(
clar__fail(file, function, line, err, buf, should_abort);
}
+void clar__assert_compare_i(
+ const char *file,
+ const char *func,
+ size_t line,
+ int should_abort,
+ enum clar_comparison cmp,
+ intmax_t value1,
+ intmax_t value2,
+ const char *error,
+ const char *description,
+ ...)
+{
+ int fulfilled;
+ switch (cmp) {
+ case CLAR_COMPARISON_EQ:
+ fulfilled = value1 == value2;
+ break;
+ case CLAR_COMPARISON_LT:
+ fulfilled = value1 < value2;
+ break;
+ case CLAR_COMPARISON_LE:
+ fulfilled = value1 <= value2;
+ break;
+ case CLAR_COMPARISON_GT:
+ fulfilled = value1 > value2;
+ break;
+ case CLAR_COMPARISON_GE:
+ fulfilled = value1 >= value2;
+ break;
+ default:
+ cl_assert(0);
+ return;
+ }
+
+ if (!fulfilled) {
+ va_list args;
+ va_start(args, description);
+ clar__failv(file, func, line, should_abort, error,
+ description, args);
+ va_end(args);
+ }
+}
+
+void clar__assert_compare_u(
+ const char *file,
+ const char *func,
+ size_t line,
+ int should_abort,
+ enum clar_comparison cmp,
+ uintmax_t value1,
+ uintmax_t value2,
+ const char *error,
+ const char *description,
+ ...)
+{
+ int fulfilled;
+ switch (cmp) {
+ case CLAR_COMPARISON_EQ:
+ fulfilled = value1 == value2;
+ break;
+ case CLAR_COMPARISON_LT:
+ fulfilled = value1 < value2;
+ break;
+ case CLAR_COMPARISON_LE:
+ fulfilled = value1 <= value2;
+ break;
+ case CLAR_COMPARISON_GT:
+ fulfilled = value1 > value2;
+ break;
+ case CLAR_COMPARISON_GE:
+ fulfilled = value1 >= value2;
+ break;
+ default:
+ cl_assert(0);
+ return;
+ }
+
+ if (!fulfilled) {
+ va_list args;
+ va_start(args, description);
+ clar__failv(file, func, line, should_abort, error,
+ description, args);
+ va_end(args);
+ }
+}
+
void cl_set_cleanup(void (*cleanup)(void *), void *opaque)
{
_clar.local_cleanup = cleanup;
diff --git a/t/unit-tests/clar/clar.h b/t/unit-tests/clar/clar.h
index ca72292ae9..f7e4363022 100644
--- a/t/unit-tests/clar/clar.h
+++ b/t/unit-tests/clar/clar.h
@@ -7,6 +7,7 @@
#ifndef __CLAR_TEST_H__
#define __CLAR_TEST_H__
+#include <inttypes.h>
#include <stdlib.h>
#include <limits.h>
@@ -149,6 +150,7 @@ const char *cl_fixture_basename(const char *fixture_name);
* Forced failure/warning
*/
#define cl_fail(desc) clar__fail(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Test failed.", desc, 1)
+#define cl_failf(desc,...) clar__failf(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, "Test failed.", desc, __VA_ARGS__)
#define cl_warning(desc) clar__fail(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Warning during test execution:", desc, 0)
#define cl_skip() clar__skip()
@@ -168,9 +170,42 @@ const char *cl_fixture_basename(const char *fixture_name);
#define cl_assert_equal_wcsn(wcs1,wcs2,len) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #wcs1 " != " #wcs2, 1, "%.*ls", (wcs1), (wcs2), (int)(len))
#define cl_assert_equal_wcsn_(wcs1,wcs2,len,note) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #wcs1 " != " #wcs2 " (" #note ")", 1, "%.*ls", (wcs1), (wcs2), (int)(len))
-#define cl_assert_equal_i(i1,i2) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,#i1 " != " #i2, 1, "%d", (int)(i1), (int)(i2))
-#define cl_assert_equal_i_(i1,i2,note) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,#i1 " != " #i2 " (" #note ")", 1, "%d", (i1), (i2))
-#define cl_assert_equal_i_fmt(i1,i2,fmt) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,#i1 " != " #i2, 1, (fmt), (int)(i1), (int)(i2))
+#define cl_assert_compare_i_(i1, i2, cmp, error, ...) clar__assert_compare_i(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, cmp, \
+ (i1), (i2), "Expected comparison to hold: " error, __VA_ARGS__)
+#define cl_assert_compare_i(i1, i2, cmp, error, fmt) do { \
+ intmax_t v1 = (i1), v2 = (i2); \
+ clar__assert_compare_i(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, cmp, \
+ v1, v2, "Expected comparison to hold: " error, fmt, v1, v2); \
+} while (0)
+#define cl_assert_equal_i_(i1, i2, ...) cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_EQ, #i1 " == " #i2, __VA_ARGS__)
+#define cl_assert_equal_i(i1, i2) cl_assert_compare_i (i1, i2, CLAR_COMPARISON_EQ, #i1 " == " #i2, "%"PRIdMAX " != %"PRIdMAX)
+#define cl_assert_equal_i_fmt(i1, i2, fmt) cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_EQ, #i1 " == " #i2, fmt " != " fmt, (int)(i1), (int)(i2))
+#define cl_assert_lt_i_(i1, i2, ...) cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_LT, #i1 " < " #i2, __VA_ARGS__)
+#define cl_assert_lt_i(i1, i2) cl_assert_compare_i (i1, i2, CLAR_COMPARISON_LT, #i1 " < " #i2, "%"PRIdMAX " >= %"PRIdMAX)
+#define cl_assert_le_i_(i1, i2, ...) cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_LE, #i1 " <= " #i2, __VA_ARGS__)
+#define cl_assert_le_i(i1, i2) cl_assert_compare_i (i1, i2, CLAR_COMPARISON_LE, #i1 " <= " #i2, "%"PRIdMAX " > %"PRIdMAX)
+#define cl_assert_gt_i_(i1, i2, ...) cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_GT, #i1 " > " #i2, __VA_ARGS__)
+#define cl_assert_gt_i(i1, i2) cl_assert_compare_i (i1, i2, CLAR_COMPARISON_GT, #i1 " > " #i2, "%"PRIdMAX " <= %"PRIdMAX)
+#define cl_assert_ge_i_(i1, i2, ...) cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_GE, #i1 " >= " #i2, __VA_ARGS__)
+#define cl_assert_ge_i(i1, i2) cl_assert_compare_i (i1, i2, CLAR_COMPARISON_GE, #i1 " >= " #i2, "%"PRIdMAX " < %"PRIdMAX)
+
+#define cl_assert_compare_u_(u1, u2, cmp, error, ...) clar__assert_compare_u(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, cmp, \
+ (u1), (u2), "Expected comparison to hold: " error, __VA_ARGS__)
+#define cl_assert_compare_u(u1, u2, cmp, error, fmt) do { \
+ uintmax_t v1 = (u1), v2 = (u2); \
+ clar__assert_compare_u(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, cmp, \
+ v1, v2, "Expected comparison to hold: " error, fmt, v1, v2); \
+} while (0)
+#define cl_assert_equal_u_(u1, u2, ...) cl_assert_compare_u_(u1, u2, CLAR_COMPARISON_EQ, #u1 " == " #u2, __VA_ARGS__)
+#define cl_assert_equal_u(u1, u2) cl_assert_compare_u (u1, u2, CLAR_COMPARISON_EQ, #u1 " == " #u2, "%"PRIuMAX " != %"PRIuMAX)
+#define cl_assert_lt_u_(u1, u2, ...) cl_assert_compare_u_(u1, u2, CLAR_COMPARISON_LT, #u1 " < " #u2, __VA_ARGS__)
+#define cl_assert_lt_u(u1, u2) cl_assert_compare_u (u1, u2, CLAR_COMPARISON_LT, #u1 " < " #u2, "%"PRIuMAX " >= %"PRIuMAX)
+#define cl_assert_le_u_(u1, u2, ...) cl_assert_compare_u_(u1, u2, CLAR_COMPARISON_LE, #u1 " <= " #u2, __VA_ARGS__)
+#define cl_assert_le_u(u1, u2) cl_assert_compare_u (u1, u2, CLAR_COMPARISON_LE, #u1 " <= " #u2, "%"PRIuMAX " > %"PRIuMAX)
+#define cl_assert_gt_u_(u1, u2, ...) cl_assert_compare_u_(u1, u2, CLAR_COMPARISON_GT, #u1 " > " #u2, __VA_ARGS__)
+#define cl_assert_gt_u(u1, u2) cl_assert_compare_u (u1, u2, CLAR_COMPARISON_GT, #u1 " > " #u2, "%"PRIuMAX " <= %"PRIuMAX)
+#define cl_assert_ge_u_(u1, u2, ...) cl_assert_compare_u_(u1, u2, CLAR_COMPARISON_GE, #u1 " >= " #u2, __VA_ARGS__)
+#define cl_assert_ge_u(u1, u2) cl_assert_compare_u (u1, u2, CLAR_COMPARISON_GE, #u1 " >= " #u2, "%"PRIuMAX " < %"PRIuMAX)
#define cl_assert_equal_b(b1,b2) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,#b1 " != " #b2, 1, "%d", (int)((b1) != 0),(int)((b2) != 0))
@@ -186,6 +221,15 @@ void clar__fail(
const char *description,
int should_abort);
+void clar__failf(
+ const char *file,
+ const char *func,
+ size_t line,
+ int should_abort,
+ const char *error,
+ const char *description,
+ ...);
+
void clar__assert(
int condition,
const char *file,
@@ -204,6 +248,38 @@ void clar__assert_equal(
const char *fmt,
...);
+enum clar_comparison {
+ CLAR_COMPARISON_EQ,
+ CLAR_COMPARISON_LT,
+ CLAR_COMPARISON_LE,
+ CLAR_COMPARISON_GT,
+ CLAR_COMPARISON_GE,
+};
+
+void clar__assert_compare_i(
+ const char *file,
+ const char *func,
+ size_t line,
+ int should_abort,
+ enum clar_comparison cmp,
+ intmax_t value1,
+ intmax_t value2,
+ const char *error,
+ const char *description,
+ ...);
+
+void clar__assert_compare_u(
+ const char *file,
+ const char *func,
+ size_t line,
+ int should_abort,
+ enum clar_comparison cmp,
+ uintmax_t value1,
+ uintmax_t value2,
+ const char *error,
+ const char *description,
+ ...);
+
void clar__set_invokepoint(
const char *file,
const char *func,
diff --git a/t/unit-tests/clar/clar/print.h b/t/unit-tests/clar/clar/print.h
index 89b66591d7..6a2321b399 100644
--- a/t/unit-tests/clar/clar/print.h
+++ b/t/unit-tests/clar/clar/print.h
@@ -164,7 +164,7 @@ static void clar_print_tap_ontest(const char *suite_name, const char *test_name,
printf(" file: '"); print_escaped(error->file); printf("'\n");
printf(" line: %" PRIuMAX "\n", error->line_number);
printf(" function: '%s'\n", error->function);
- printf(" ---\n");
+ printf(" ...\n");
}
break;
diff --git a/t/unit-tests/clar/test/expected/quiet b/t/unit-tests/clar/test/expected/quiet
index 280c99d8ad..a93273b5a2 100644
--- a/t/unit-tests/clar/test/expected/quiet
+++ b/t/unit-tests/clar/test/expected/quiet
@@ -18,27 +18,57 @@ combined::strings_with_length [file:42]
5) Failure:
combined::int [file:42]
- 101 != value ("extra note on failing test")
+ Expected comparison to hold: 101 == value
101 != 100
6) Failure:
+combined::int_note [file:42]
+ Expected comparison to hold: 101 == value
+ extra note on failing test
+
+ 7) Failure:
combined::int_fmt [file:42]
- 022 != value
+ Expected comparison to hold: 022 == value
0022 != 0144
- 7) Failure:
+ 8) Failure:
combined::bool [file:42]
0 != value
0 != 1
- 8) Failure:
+ 9) Failure:
combined::multiline_description [file:42]
Function call failed: -1
description line 1
description line 2
- 9) Failure:
+ 10) Failure:
combined::null_string [file:42]
String mismatch: "expected" != actual ("this one fails")
'expected' != NULL
+ 11) Failure:
+combined::failf [file:42]
+ Test failed.
+ some reason: foo
+
+ 12) Failure:
+combined::compare_i [file:42]
+ Expected comparison to hold: two < 1
+ 2 >= 1
+
+ 13) Failure:
+combined::compare_i_with_format [file:42]
+ Expected comparison to hold: two < 1
+ foo: bar
+
+ 14) Failure:
+combined::compare_u [file:42]
+ Expected comparison to hold: two < 1
+ 2 >= 1
+
+ 15) Failure:
+combined::compare_u_with_format [file:42]
+ Expected comparison to hold: two < 1
+ foo: bar
+
diff --git a/t/unit-tests/clar/test/expected/summary_with_filename b/t/unit-tests/clar/test/expected/summary_with_filename
index 460160791d..a9471cc7d5 100644
--- a/t/unit-tests/clar/test/expected/summary_with_filename
+++ b/t/unit-tests/clar/test/expected/summary_with_filename
@@ -1,6 +1,6 @@
Loaded 1 suites:
Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')
-FFFFFFFFF
+FFFFFFFFFFFFFFF
1) Failure:
combined::1 [file:42]
@@ -22,28 +22,58 @@ combined::strings_with_length [file:42]
5) Failure:
combined::int [file:42]
- 101 != value ("extra note on failing test")
+ Expected comparison to hold: 101 == value
101 != 100
6) Failure:
+combined::int_note [file:42]
+ Expected comparison to hold: 101 == value
+ extra note on failing test
+
+ 7) Failure:
combined::int_fmt [file:42]
- 022 != value
+ Expected comparison to hold: 022 == value
0022 != 0144
- 7) Failure:
+ 8) Failure:
combined::bool [file:42]
0 != value
0 != 1
- 8) Failure:
+ 9) Failure:
combined::multiline_description [file:42]
Function call failed: -1
description line 1
description line 2
- 9) Failure:
+ 10) Failure:
combined::null_string [file:42]
String mismatch: "expected" != actual ("this one fails")
'expected' != NULL
+ 11) Failure:
+combined::failf [file:42]
+ Test failed.
+ some reason: foo
+
+ 12) Failure:
+combined::compare_i [file:42]
+ Expected comparison to hold: two < 1
+ 2 >= 1
+
+ 13) Failure:
+combined::compare_i_with_format [file:42]
+ Expected comparison to hold: two < 1
+ foo: bar
+
+ 14) Failure:
+combined::compare_u [file:42]
+ Expected comparison to hold: two < 1
+ 2 >= 1
+
+ 15) Failure:
+combined::compare_u_with_format [file:42]
+ Expected comparison to hold: two < 1
+ foo: bar
+
written summary file to different.xml
diff --git a/t/unit-tests/clar/test/expected/summary_without_filename b/t/unit-tests/clar/test/expected/summary_without_filename
index 7874c1d98b..83ba770d00 100644
--- a/t/unit-tests/clar/test/expected/summary_without_filename
+++ b/t/unit-tests/clar/test/expected/summary_without_filename
@@ -1,6 +1,6 @@
Loaded 1 suites:
Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')
-FFFFFFFFF
+FFFFFFFFFFFFFFF
1) Failure:
combined::1 [file:42]
@@ -22,28 +22,58 @@ combined::strings_with_length [file:42]
5) Failure:
combined::int [file:42]
- 101 != value ("extra note on failing test")
+ Expected comparison to hold: 101 == value
101 != 100
6) Failure:
+combined::int_note [file:42]
+ Expected comparison to hold: 101 == value
+ extra note on failing test
+
+ 7) Failure:
combined::int_fmt [file:42]
- 022 != value
+ Expected comparison to hold: 022 == value
0022 != 0144
- 7) Failure:
+ 8) Failure:
combined::bool [file:42]
0 != value
0 != 1
- 8) Failure:
+ 9) Failure:
combined::multiline_description [file:42]
Function call failed: -1
description line 1
description line 2
- 9) Failure:
+ 10) Failure:
combined::null_string [file:42]
String mismatch: "expected" != actual ("this one fails")
'expected' != NULL
+ 11) Failure:
+combined::failf [file:42]
+ Test failed.
+ some reason: foo
+
+ 12) Failure:
+combined::compare_i [file:42]
+ Expected comparison to hold: two < 1
+ 2 >= 1
+
+ 13) Failure:
+combined::compare_i_with_format [file:42]
+ Expected comparison to hold: two < 1
+ foo: bar
+
+ 14) Failure:
+combined::compare_u [file:42]
+ Expected comparison to hold: two < 1
+ 2 >= 1
+
+ 15) Failure:
+combined::compare_u_with_format [file:42]
+ Expected comparison to hold: two < 1
+ foo: bar
+
written summary file to summary.xml
diff --git a/t/unit-tests/clar/test/expected/tap b/t/unit-tests/clar/test/expected/tap
index bddbd5dfe9..e67118d3ae 100644
--- a/t/unit-tests/clar/test/expected/tap
+++ b/t/unit-tests/clar/test/expected/tap
@@ -8,7 +8,7 @@ not ok 1 - combined::1
file: 'file'
line: 42
function: 'func'
- ---
+ ...
not ok 2 - combined::2
---
reason: |
@@ -17,7 +17,7 @@ not ok 2 - combined::2
file: 'file'
line: 42
function: 'func'
- ---
+ ...
not ok 3 - combined::strings
---
reason: |
@@ -27,7 +27,7 @@ not ok 3 - combined::strings
file: 'file'
line: 42
function: 'func'
- ---
+ ...
not ok 4 - combined::strings_with_length
---
reason: |
@@ -37,28 +37,38 @@ not ok 4 - combined::strings_with_length
file: 'file'
line: 42
function: 'func'
- ---
+ ...
not ok 5 - combined::int
---
reason: |
- 101 != value ("extra note on failing test")
+ Expected comparison to hold: 101 == value
101 != 100
at:
file: 'file'
line: 42
function: 'func'
+ ...
+not ok 6 - combined::int_note
---
-not ok 6 - combined::int_fmt
+ reason: |
+ Expected comparison to hold: 101 == value
+ extra note on failing test
+ at:
+ file: 'file'
+ line: 42
+ function: 'func'
+ ...
+not ok 7 - combined::int_fmt
---
reason: |
- 022 != value
+ Expected comparison to hold: 022 == value
0022 != 0144
at:
file: 'file'
line: 42
function: 'func'
- ---
-not ok 7 - combined::bool
+ ...
+not ok 8 - combined::bool
---
reason: |
0 != value
@@ -67,8 +77,8 @@ not ok 7 - combined::bool
file: 'file'
line: 42
function: 'func'
- ---
-not ok 8 - combined::multiline_description
+ ...
+not ok 9 - combined::multiline_description
---
reason: |
Function call failed: -1
@@ -78,8 +88,8 @@ not ok 8 - combined::multiline_description
file: 'file'
line: 42
function: 'func'
- ---
-not ok 9 - combined::null_string
+ ...
+not ok 10 - combined::null_string
---
reason: |
String mismatch: "expected" != actual ("this one fails")
@@ -88,5 +98,55 @@ not ok 9 - combined::null_string
file: 'file'
line: 42
function: 'func'
+ ...
+not ok 11 - combined::failf
+ ---
+ reason: |
+ Test failed.
+ some reason: foo
+ at:
+ file: 'file'
+ line: 42
+ function: 'func'
+ ...
+not ok 12 - combined::compare_i
---
-1..9
+ reason: |
+ Expected comparison to hold: two < 1
+ 2 >= 1
+ at:
+ file: 'file'
+ line: 42
+ function: 'func'
+ ...
+not ok 13 - combined::compare_i_with_format
+ ---
+ reason: |
+ Expected comparison to hold: two < 1
+ foo: bar
+ at:
+ file: 'file'
+ line: 42
+ function: 'func'
+ ...
+not ok 14 - combined::compare_u
+ ---
+ reason: |
+ Expected comparison to hold: two < 1
+ 2 >= 1
+ at:
+ file: 'file'
+ line: 42
+ function: 'func'
+ ...
+not ok 15 - combined::compare_u_with_format
+ ---
+ reason: |
+ Expected comparison to hold: two < 1
+ foo: bar
+ at:
+ file: 'file'
+ line: 42
+ function: 'func'
+ ...
+1..15
diff --git a/t/unit-tests/clar/test/expected/without_arguments b/t/unit-tests/clar/test/expected/without_arguments
index 1111d418a0..9891f45a70 100644
--- a/t/unit-tests/clar/test/expected/without_arguments
+++ b/t/unit-tests/clar/test/expected/without_arguments
@@ -1,6 +1,6 @@
Loaded 1 suites:
Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')
-FFFFFFFFF
+FFFFFFFFFFFFFFF
1) Failure:
combined::1 [file:42]
@@ -22,27 +22,57 @@ combined::strings_with_length [file:42]
5) Failure:
combined::int [file:42]
- 101 != value ("extra note on failing test")
+ Expected comparison to hold: 101 == value
101 != 100
6) Failure:
+combined::int_note [file:42]
+ Expected comparison to hold: 101 == value
+ extra note on failing test
+
+ 7) Failure:
combined::int_fmt [file:42]
- 022 != value
+ Expected comparison to hold: 022 == value
0022 != 0144
- 7) Failure:
+ 8) Failure:
combined::bool [file:42]
0 != value
0 != 1
- 8) Failure:
+ 9) Failure:
combined::multiline_description [file:42]
Function call failed: -1
description line 1
description line 2
- 9) Failure:
+ 10) Failure:
combined::null_string [file:42]
String mismatch: "expected" != actual ("this one fails")
'expected' != NULL
+ 11) Failure:
+combined::failf [file:42]
+ Test failed.
+ some reason: foo
+
+ 12) Failure:
+combined::compare_i [file:42]
+ Expected comparison to hold: two < 1
+ 2 >= 1
+
+ 13) Failure:
+combined::compare_i_with_format [file:42]
+ Expected comparison to hold: two < 1
+ foo: bar
+
+ 14) Failure:
+combined::compare_u [file:42]
+ Expected comparison to hold: two < 1
+ 2 >= 1
+
+ 15) Failure:
+combined::compare_u_with_format [file:42]
+ Expected comparison to hold: two < 1
+ foo: bar
+
diff --git a/t/unit-tests/clar/test/selftest.c b/t/unit-tests/clar/test/selftest.c
index eed83e4512..6eadc64c48 100644
--- a/t/unit-tests/clar/test/selftest.c
+++ b/t/unit-tests/clar/test/selftest.c
@@ -298,7 +298,7 @@ void test_selftest__help(void)
void test_selftest__without_arguments(void)
{
- cl_invoke(assert_output("combined", "without_arguments", 9, NULL));
+ cl_invoke(assert_output("combined", "without_arguments", 15, NULL));
}
void test_selftest__specific_test(void)
@@ -313,12 +313,12 @@ void test_selftest__stop_on_failure(void)
void test_selftest__quiet(void)
{
- cl_invoke(assert_output("combined", "quiet", 9, "-q", NULL));
+ cl_invoke(assert_output("combined", "quiet", 15, "-q", NULL));
}
void test_selftest__tap(void)
{
- cl_invoke(assert_output("combined", "tap", 9, "-t", NULL));
+ cl_invoke(assert_output("combined", "tap", 15, "-t", NULL));
}
void test_selftest__suite_names(void)
@@ -329,7 +329,7 @@ void test_selftest__suite_names(void)
void test_selftest__summary_without_filename(void)
{
struct stat st;
- cl_invoke(assert_output("combined", "summary_without_filename", 9, "-r", NULL));
+ cl_invoke(assert_output("combined", "summary_without_filename", 15, "-r", NULL));
/* The summary contains timestamps, so we cannot verify its contents. */
cl_must_pass(stat("summary.xml", &st));
}
@@ -337,7 +337,7 @@ void test_selftest__summary_without_filename(void)
void test_selftest__summary_with_filename(void)
{
struct stat st;
- cl_invoke(assert_output("combined", "summary_with_filename", 9, "-rdifferent.xml", NULL));
+ cl_invoke(assert_output("combined", "summary_with_filename", 15, "-rdifferent.xml", NULL));
/* The summary contains timestamps, so we cannot verify its contents. */
cl_must_pass(stat("different.xml", &st));
}
diff --git a/t/unit-tests/clar/test/suites/combined.c b/t/unit-tests/clar/test/suites/combined.c
index e8b41c98c3..9e9dbc2fb1 100644
--- a/t/unit-tests/clar/test/suites/combined.c
+++ b/t/unit-tests/clar/test/suites/combined.c
@@ -55,7 +55,12 @@ void test_combined__strings_with_length(void)
void test_combined__int(void)
{
int value = 100;
- cl_assert_equal_i(100, value);
+ cl_assert_equal_i(101, value);
+}
+
+void test_combined__int_note(void)
+{
+ int value = 100;
cl_assert_equal_i_(101, value, "extra note on failing test");
}
@@ -83,3 +88,61 @@ void test_combined__null_string(void)
cl_assert_equal_s(actual, actual);
cl_assert_equal_s_("expected", actual, "this one fails");
}
+
+void test_combined__failf(void)
+{
+ cl_failf("some reason: %s", "foo");
+}
+
+void test_combined__compare_i(void)
+{
+ int one = 1, two = 2;
+
+ cl_assert_equal_i(one, 1);
+ cl_assert_equal_i(one, 1);
+ cl_assert_equal_i_(one, 1, "format");
+ cl_assert_lt_i(one, 2);
+ cl_assert_lt_i_(one, 2, "format");
+ cl_assert_le_i(one, 2);
+ cl_assert_le_i(two, 2);
+ cl_assert_le_i_(two, 2, "format");
+ cl_assert_gt_i(two, 1);
+ cl_assert_gt_i_(two, 1, "format");
+ cl_assert_ge_i(two, 2);
+ cl_assert_ge_i(3, two);
+ cl_assert_ge_i_(3, two, "format");
+
+ cl_assert_lt_i(two, 1); /* this one fails */
+}
+
+void test_combined__compare_i_with_format(void)
+{
+ int two = 2;
+ cl_assert_lt_i_(two, 1, "foo: %s", "bar");
+}
+
+void test_combined__compare_u(void)
+{
+ unsigned one = 1, two = 2;
+
+ cl_assert_equal_u(one, 1);
+ cl_assert_equal_u_(one, 1, "format");
+ cl_assert_lt_u(one, 2);
+ cl_assert_lt_u_(one, 2, "format");
+ cl_assert_le_u(one, 2);
+ cl_assert_le_u(two, 2);
+ cl_assert_le_u_(two, 2, "format");
+ cl_assert_gt_u(two, 1);
+ cl_assert_gt_u_(two, 1, "format");
+ cl_assert_ge_u(two, 2);
+ cl_assert_ge_u(3, two);
+ cl_assert_ge_u_(3, two, "format");
+
+ cl_assert_lt_u(two, 1); /* this one fails */
+}
+
+void test_combined__compare_u_with_format(void)
+{
+ unsigned two = 2;
+ cl_assert_lt_u_(two, 1, "foo: %s", "bar");
+}
diff --git a/t/unit-tests/u-reftable-record.c b/t/unit-tests/u-reftable-record.c
index 6c8c0d5374..1bf2e170dc 100644
--- a/t/unit-tests/u-reftable-record.c
+++ b/t/unit-tests/u-reftable-record.c
@@ -51,10 +51,10 @@ void test_reftable_record__varint_roundtrip(void)
int n = put_var_int(&out, in);
uint64_t got = 0;
- cl_assert(n > 0);
+ cl_assert_gt_i(n, 0);
out.len = n;
n = get_var_int(&got, &out);
- cl_assert(n > 0);
+ cl_assert_gt_i(n, 0);
cl_assert_equal_i(got, in);
}
@@ -110,7 +110,7 @@ void test_reftable_record__ref_record_comparison(void)
cl_assert(reftable_record_equal(&in[1], &in[2],
REFTABLE_HASH_SIZE_SHA1) == 0);
cl_assert_equal_i(reftable_record_cmp(&in[1], &in[2], &cmp), 0);
- cl_assert(cmp > 0);
+ cl_assert_gt_i(cmp, 0);
in[1].u.ref.value_type = in[0].u.ref.value_type;
cl_assert(reftable_record_equal(&in[0], &in[1],
@@ -184,7 +184,7 @@ void test_reftable_record__ref_record_roundtrip(void)
reftable_record_key(&in, &key);
n = reftable_record_encode(&in, dest, REFTABLE_HASH_SIZE_SHA1);
- cl_assert(n > 0);
+ cl_assert_gt_i(n, 0);
/* decode into a non-zero reftable_record to test for leaks. */
m = reftable_record_decode(&out, key, i, dest, REFTABLE_HASH_SIZE_SHA1, &scratch);
@@ -228,11 +228,11 @@ void test_reftable_record__log_record_comparison(void)
cl_assert_equal_i(reftable_record_equal(&in[1], &in[2],
REFTABLE_HASH_SIZE_SHA1), 0);
cl_assert_equal_i(reftable_record_cmp(&in[1], &in[2], &cmp), 0);
- cl_assert(cmp > 0);
+ cl_assert_gt_i(cmp, 0);
/* comparison should be reversed for equal keys, because
* comparison is now performed on the basis of update indices */
cl_assert_equal_i(reftable_record_cmp(&in[0], &in[1], &cmp), 0);
- cl_assert(cmp < 0);
+ cl_assert_lt_i(cmp, 0);
in[1].u.log.update_index = in[0].u.log.update_index;
cl_assert(reftable_record_equal(&in[0], &in[1],
@@ -344,7 +344,7 @@ void test_reftable_record__log_record_roundtrip(void)
reftable_record_key(&rec, &key);
n = reftable_record_encode(&rec, dest, REFTABLE_HASH_SIZE_SHA1);
- cl_assert(n >= 0);
+ cl_assert_ge_i(n, 0);
valtype = reftable_record_val_type(&rec);
m = reftable_record_decode(&out, key, valtype, dest,
REFTABLE_HASH_SIZE_SHA1, &scratch);
@@ -382,7 +382,7 @@ void test_reftable_record__key_roundtrip(void)
extra = 6;
n = reftable_encode_key(&restart, dest, last_key, key, extra);
cl_assert(!restart);
- cl_assert(n > 0);
+ cl_assert_gt_i(n, 0);
cl_assert_equal_i(reftable_buf_addstr(&roundtrip,
"refs/heads/master"), 0);
@@ -432,7 +432,7 @@ void test_reftable_record__obj_record_comparison(void)
cl_assert_equal_i(reftable_record_equal(&in[1], &in[2],
REFTABLE_HASH_SIZE_SHA1), 0);
cl_assert_equal_i(reftable_record_cmp(&in[1], &in[2], &cmp), 0);
- cl_assert(cmp > 0);
+ cl_assert_gt_i(cmp, 0);
in[1].u.obj.offset_len = in[0].u.obj.offset_len;
cl_assert(reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1) != 0);
@@ -485,7 +485,7 @@ void test_reftable_record__obj_record_roundtrip(void)
t_copy(&in);
reftable_record_key(&in, &key);
n = reftable_record_encode(&in, dest, REFTABLE_HASH_SIZE_SHA1);
- cl_assert(n > 0);
+ cl_assert_gt_i(n, 0);
extra = reftable_record_val_type(&in);
m = reftable_record_decode(&out, key, extra, dest,
REFTABLE_HASH_SIZE_SHA1, &scratch);
@@ -535,7 +535,7 @@ void test_reftable_record__index_record_comparison(void)
cl_assert_equal_i(reftable_record_equal(&in[1], &in[2],
REFTABLE_HASH_SIZE_SHA1), 0);
cl_assert_equal_i(reftable_record_cmp(&in[1], &in[2], &cmp), 0);
- cl_assert(cmp > 0);
+ cl_assert_gt_i(cmp, 0);
in[1].u.idx.offset = in[0].u.idx.offset;
cl_assert(reftable_record_equal(&in[0], &in[1],
diff --git a/t/unit-tests/unit-test.h b/t/unit-tests/unit-test.h
index 39a0b72a05..5398b44917 100644
--- a/t/unit-tests/unit-test.h
+++ b/t/unit-tests/unit-test.h
@@ -7,9 +7,3 @@
#else
# include GIT_CLAR_DECLS_H
#endif
-
-#define cl_failf(fmt, ...) do { \
- char desc[4096]; \
- snprintf(desc, sizeof(desc), fmt, __VA_ARGS__); \
- clar__fail(__FILE__, __func__, __LINE__, "Test failed.", desc, 1); \
-} while (0)
diff --git a/tag.c b/tag.c
index f5c232d2f1..2f12e51024 100644
--- a/tag.c
+++ b/tag.c
@@ -1,4 +1,3 @@
-#define USE_THE_REPOSITORY_VARIABLE
#define DISABLE_SIGN_COMPARE_WARNINGS
#include "git-compat-util.h"
@@ -13,6 +12,7 @@
#include "gpg-interface.h"
#include "hex.h"
#include "packfile.h"
+#include "repository.h"
const char *tag_type = "tag";
@@ -44,28 +44,28 @@ static int run_gpg_verify(const char *buf, unsigned long size, unsigned flags)
return ret;
}
-int gpg_verify_tag(const struct object_id *oid, const char *name_to_report,
- unsigned flags)
+int gpg_verify_tag(struct repository *r, const struct object_id *oid,
+ const char *name_to_report, unsigned flags)
{
enum object_type type;
char *buf;
unsigned long size;
int ret;
- type = odb_read_object_info(the_repository->objects, oid, NULL);
+ type = odb_read_object_info(r->objects, oid, NULL);
if (type != OBJ_TAG)
return error("%s: cannot verify a non-tag object of type %s.",
name_to_report ?
name_to_report :
- repo_find_unique_abbrev(the_repository, oid, DEFAULT_ABBREV),
+ oid_to_hex(oid),
type_name(type));
- buf = odb_read_object(the_repository->objects, oid, &type, &size);
+ buf = odb_read_object(r->objects, oid, &type, &size);
if (!buf)
return error("%s: unable to read file.",
name_to_report ?
name_to_report :
- repo_find_unique_abbrev(the_repository, oid, DEFAULT_ABBREV));
+ oid_to_hex(oid));
ret = run_gpg_verify(buf, size, flags);
@@ -148,9 +148,11 @@ int parse_tag_buffer(struct repository *r, struct tag *item, const void *data, u
FREE_AND_NULL(item->tag);
}
- if (size < the_hash_algo->hexsz + 24)
+ if (size < r->hash_algo->hexsz + 24)
return -1;
- if (memcmp("object ", bufptr, 7) || parse_oid_hex(bufptr + 7, &oid, &bufptr) || *bufptr++ != '\n')
+ if (memcmp("object ", bufptr, 7) ||
+ parse_oid_hex_algop(bufptr + 7, &oid, &bufptr, r->hash_algo) ||
+ *bufptr++ != '\n')
return -1;
if (!starts_with(bufptr, "type "))
@@ -201,7 +203,7 @@ int parse_tag_buffer(struct repository *r, struct tag *item, const void *data, u
return 0;
}
-int parse_tag(struct tag *item)
+int parse_tag(struct repository *r, struct tag *item)
{
enum object_type type;
void *data;
@@ -210,8 +212,7 @@ int parse_tag(struct tag *item)
if (item->object.parsed)
return 0;
- data = odb_read_object(the_repository->objects, &item->object.oid,
- &type, &size);
+ data = odb_read_object(r->objects, &item->object.oid, &type, &size);
if (!data)
return error("Could not read %s",
oid_to_hex(&item->object.oid));
@@ -220,7 +221,7 @@ int parse_tag(struct tag *item)
return error("Object %s not a tag",
oid_to_hex(&item->object.oid));
}
- ret = parse_tag_buffer(the_repository, item, data, size);
+ ret = parse_tag_buffer(r, item, data, size);
free(data);
return ret;
}
diff --git a/tag.h b/tag.h
index ef12a61037..534687c4ca 100644
--- a/tag.h
+++ b/tag.h
@@ -13,10 +13,10 @@ struct tag {
};
struct tag *lookup_tag(struct repository *r, const struct object_id *oid);
int parse_tag_buffer(struct repository *r, struct tag *item, const void *data, unsigned long size);
-int parse_tag(struct tag *item);
+int parse_tag(struct repository *r, struct tag *item);
void release_tag_memory(struct tag *t);
struct object *deref_tag(struct repository *r, struct object *, const char *, int);
-int gpg_verify_tag(const struct object_id *oid,
+int gpg_verify_tag(struct repository *r, const struct object_id *oid,
const char *name_to_report, unsigned flags);
struct object_id *get_tagged_oid(struct tag *tag);
diff --git a/tree-diff.c b/tree-diff.c
index 5988148b60..631ea86812 100644
--- a/tree-diff.c
+++ b/tree-diff.c
@@ -439,7 +439,7 @@ static void ll_diff_tree_paths(
void *ttree, **tptree;
int i;
- if (depth > max_allowed_tree_depth)
+ if (depth > opt->repo->settings.max_allowed_tree_depth)
die("exceeded maximum allowed tree depth");
FAST_ARRAY_ALLOC(tp, nparent);
diff --git a/tree-walk.c b/tree-walk.c
index e449a1320e..7e1b956f27 100644
--- a/tree-walk.c
+++ b/tree-walk.c
@@ -12,6 +12,7 @@
#include "pathspec.h"
#include "json-writer.h"
#include "environment.h"
+#include "read-cache-ll.h"
static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned long size, struct strbuf *err)
{
@@ -441,8 +442,9 @@ int traverse_trees(struct index_state *istate,
struct strbuf base = STRBUF_INIT;
int interesting = 1;
char *traverse_path;
+ struct repository *r = istate ? istate->repo : the_repository;
- if (traverse_trees_cur_depth > max_allowed_tree_depth)
+ if (traverse_trees_cur_depth > r->settings.max_allowed_tree_depth)
return error("exceeded maximum allowed tree depth");
traverse_trees_count++;
diff --git a/tree.c b/tree.c
index 1ef743d90f..d703ab97c8 100644
--- a/tree.c
+++ b/tree.c
@@ -1,5 +1,3 @@
-#define USE_THE_REPOSITORY_VARIABLE
-
#include "git-compat-util.h"
#include "hex.h"
#include "tree.h"
@@ -25,10 +23,10 @@ int read_tree_at(struct repository *r,
int len, oldlen = base->len;
enum interesting retval = entry_not_interesting;
- if (depth > max_allowed_tree_depth)
+ if (depth > r->settings.max_allowed_tree_depth)
return error("exceeded maximum allowed tree depth");
- if (parse_tree(tree))
+ if (repo_parse_tree(r, tree))
return -1;
init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
@@ -185,7 +183,8 @@ int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size)
return 0;
}
-int parse_tree_gently(struct tree *item, int quiet_on_missing)
+int repo_parse_tree_gently(struct repository *r, struct tree *item,
+ int quiet_on_missing)
{
enum object_type type;
void *buffer;
@@ -193,8 +192,7 @@ int parse_tree_gently(struct tree *item, int quiet_on_missing)
if (item->object.parsed)
return 0;
- buffer = odb_read_object(the_repository->objects, &item->object.oid,
- &type, &size);
+ buffer = odb_read_object(r->objects, &item->object.oid, &type, &size);
if (!buffer)
return quiet_on_missing ? -1 :
error("Could not read %s",
@@ -214,9 +212,9 @@ void free_tree_buffer(struct tree *tree)
tree->object.parsed = 0;
}
-struct tree *parse_tree_indirect(const struct object_id *oid)
+struct tree *repo_parse_tree_indirect(struct repository *r,
+ const struct object_id *oid)
{
- struct repository *r = the_repository;
struct object *obj = parse_object(r, oid);
return (struct tree *)repo_peel_to_type(r, NULL, 0, obj, OBJ_TREE);
}
diff --git a/tree.h b/tree.h
index cc6ddf51b3..677382eed8 100644
--- a/tree.h
+++ b/tree.h
@@ -19,15 +19,20 @@ struct tree *lookup_tree(struct repository *r, const struct object_id *oid);
int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size);
-int parse_tree_gently(struct tree *tree, int quiet_on_missing);
-static inline int parse_tree(struct tree *tree)
+#define parse_tree_gently(t, q) repo_parse_tree_gently(the_repository, t, q)
+int repo_parse_tree_gently(struct repository *r, struct tree *item,
+ int quiet_on_missing);
+#define parse_tree(t) repo_parse_tree(the_repository, t)
+static inline int repo_parse_tree(struct repository *r, struct tree *item)
{
- return parse_tree_gently(tree, 0);
+ return repo_parse_tree_gently(r, item, 0);
}
void free_tree_buffer(struct tree *tree);
/* Parses and returns the tree in the given ent, chasing tags and commits. */
-struct tree *parse_tree_indirect(const struct object_id *oid);
+#define parse_tree_indirect(o) repo_parse_tree_indirect(the_repository, o)
+struct tree *repo_parse_tree_indirect(struct repository *r,
+ const struct object_id *oid);
/*
* Functions for comparing pathnames
diff --git a/utf8.c b/utf8.c
index 35a0251939..96460cc414 100644
--- a/utf8.c
+++ b/utf8.c
@@ -515,6 +515,19 @@ char *reencode_string_iconv(const char *in, size_t insz, iconv_t conv,
out = xrealloc(out, outalloc);
outpos = out + sofar;
outsz = outalloc - sofar - 1;
+#ifdef ICONV_RESTART_RESET
+ /*
+ * If iconv(3) messes up piecemeal conversions
+ * then restore the original pointers, sizes,
+ * and converter state, then retry converting
+ * the full string using the reallocated buffer.
+ */
+ insz += cp - (iconv_ibp)in; /* Restore insz */
+ cp = (iconv_ibp)in; /* original start value */
+ outpos = out + bom_len; /* original start value */
+ outsz = outalloc - bom_len - 1; /* new len */
+ iconv(conv, NULL, NULL, NULL, NULL); /* reset iconv machinery */
+#endif
}
else {
*outpos = '\0';
diff --git a/walker.c b/walker.c
index 409b646578..91332539d3 100644
--- a/walker.c
+++ b/walker.c
@@ -45,7 +45,7 @@ static int process_tree(struct walker *walker, struct tree *tree)
struct tree_desc desc;
struct name_entry entry;
- if (parse_tree(tree))
+ if (repo_parse_tree(the_repository, tree))
return -1;
init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
@@ -115,7 +115,7 @@ static int process_commit(struct walker *walker, struct commit *commit)
static int process_tag(struct walker *walker, struct tag *tag)
{
- if (parse_tag(tag))
+ if (parse_tag(the_repository, tag))
return -1;
return process(walker, tag->tagged);
}