Compare commits

...

489 Commits

Author SHA1 Message Date
Junio C Hamano
d529f3a197 The 16th batch
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-08 16:40:12 +09:00
Junio C Hamano
2db806d817 Merge branch 'en/ort-recursive-d-f-conflict-fix'
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.

* en/ort-recursive-d-f-conflict-fix:
  merge-ort: fix corner case recursive submodule/directory conflict handling
2026-01-08 16:40:12 +09:00
Junio C Hamano
512351f2a8 Merge branch 'dd/t5403-modernise'
Test micro-clean-up.

* dd/t5403-modernise:
  t5403: use test_path_is_file instead of test -f
2026-01-08 16:40:12 +09:00
Junio C Hamano
c0754dc423 Merge branch 'ds/diff-lazy-fetch-with-name-only-fix'
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.

* ds/diff-lazy-fetch-with-name-only-fix:
  diff: avoid segfault with freed entries
2026-01-08 16:40:11 +09:00
Junio C Hamano
d28d2be5f2 Merge branch 'rs/tag-wo-the-repository'
Code clean-up.

* rs/tag-wo-the-repository:
  tag: stop using the_repository
  tag: support arbitrary repositories in parse_tag()
  tag: support arbitrary repositories in gpg_verify_tag()
  tag: use algo of repo parameter in parse_tag_buffer()
2026-01-08 16:40:11 +09:00
Junio C Hamano
e0bfec3dfc The 15th batch
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-06 16:33:53 +09:00
Junio C Hamano
d39e3ed716 Merge branch 'rs/parse-config-expiry-simplify'
Code clean-up.

* rs/parse-config-expiry-simplify:
  config: use git_parse_int() in git_config_get_expiry_in_days()
2026-01-06 16:33:53 +09:00
Junio C Hamano
f406b89552 Merge branch 'ar/run-command-hook'
Use hook API to replace ad-hoc invocation of hook scripts with the
run_command() API.

* ar/run-command-hook:
  receive-pack: convert receive hooks to hook API
  receive-pack: convert update hooks to new API
  hooks: allow callers to capture output
  run-command: allow capturing of collated output
  hook: allow overriding the ungroup option
  reference-transaction: use hook API instead of run-command
  transport: convert pre-push to hook API
  hook: convert 'post-rewrite' hook in sequencer.c to hook API
  hook: provide stdin via callback
  run-command: add stdin callback for parallelization
  run-command: add first helper for pp child states
2026-01-06 16:33:53 +09:00
Junio C Hamano
1627809eef Merge branch 'rs/show-branch-prio-queue'
Code clean-up.

* rs/show-branch-prio-queue:
  show-branch: use prio_queue
2026-01-06 16:33:52 +09:00
Junio C Hamano
b39aad0b0d Merge branch 'rs/macos-iconv-workaround'
Workaround the "iconv" shipped as part of macOS, which is broken
handling stateful ISO/IEC 2022 encoded strings.

* rs/macos-iconv-workaround:
  macOS: use iconv from Homebrew if needed and present
  macOS: make Homebrew use configurable
2026-01-06 16:33:52 +09:00
Junio C Hamano
8fb86e1a42 Merge branch 'bc/checkout-error-message-fix'
Message fix.

* bc/checkout-error-message-fix:
  checkout: quote invalid treeish in error message
2026-01-06 16:33:52 +09:00
Junio C Hamano
68cb7f9e92 The 14th batch
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-30 12:58:22 +09:00
Junio C Hamano
a37bb2ae6c Merge branch 'jk/test-curl-updates'
Update HTTP tests to adjust for changes in curl 8.18.0

* jk/test-curl-updates:
  t5563: add missing end-of-line in HTTP header
  t5551: handle trailing slashes in expected cookies output
2025-12-30 12:58:22 +09:00
Junio C Hamano
e7b1925381 Merge branch 'jc/object-read-stream-fix'
Fix a performance regression in recently graduated topic.

* jc/object-read-stream-fix:
  odb: do not use "blank" substitute for NULL
2025-12-30 12:58:22 +09:00
Junio C Hamano
a194cdc8f3 Merge branch 'js/test-func-comment-fix'
Comment fix.

* js/test-func-comment-fix:
  test_detect_ref_format: fix comment
2025-12-30 12:58:21 +09:00
Junio C Hamano
68dce01807 Merge branch 'gf/clear-path-cache-cleanup'
Code clean-up.

* gf/clear-path-cache-cleanup:
  repository: remove duplicate free of cache->squash_msg
2025-12-30 12:58:21 +09:00
Junio C Hamano
2365d4f612 Merge branch 'gf/maintenance-is-needed-fix'
Brown-paper-bag fix to a recently graduated
'kn/maintenance-is-needed' topic.

* gf/maintenance-is-needed-fix:
  refs: dereference the value of the required pointer
2025-12-30 12:58:20 +09:00
Junio C Hamano
b006b84119 Merge branch 'dk/ci-rust-fix'
Build fix.

* dk/ci-rust-fix:
  rust: build correctly without GNU sed
2025-12-30 12:58:20 +09:00
Junio C Hamano
148c8f38ee Merge branch 'mh/doc-core-attributesfile'
Doc update.

* mh/doc-core-attributesfile:
  docs: note the type of core.attributesfile
2025-12-30 12:58:19 +09:00
Junio C Hamano
4a8ee50c77 Merge branch 'ps/repack-avoid-noop-midx-rewrite'
Even when there is no changes in the packfile and no need to
recompute bitmaps, "git repack" recomputed and updated the MIDX
file, which has been corrected.

* ps/repack-avoid-noop-midx-rewrite:
  midx-write: skip rewriting MIDX with `--stdin-packs` unless needed
  midx-write: extract function to test whether MIDX needs updating
  midx: fix `BUG()` when getting preferred pack without a reverse index
2025-12-30 12:58:19 +09:00
Junio C Hamano
d8e9716b91 Merge branch 'js/test-symlink-windows'
Prepare test suite for Git for Windows that supports symbolic
links.

* js/test-symlink-windows:
  t7800: work around the MSYS path conversion on Windows
  t6423: introduce Windows-specific handling for symlinking to /dev/null
  t1305: skip symlink tests that do not apply to Windows
  t1006: accommodate for symlink support in MSYS2
  t0600: fix incomplete prerequisite for a test case
  t0301: another fix for Windows compatibility
  t0001: handle `diff --no-index` gracefully
  mingw: special-case `open(symlink, O_CREAT | O_EXCL)`
  apply: symbolic links lack a "trustable executable bit"
  t9700: accommodate for Windows paths
2025-12-30 12:58:19 +09:00
Junio C Hamano
b1792f5116 Merge branch 'jt/doc-rev-list-filter-provided-objects'
Document "rev-list --filter-provided-objects" better.

* jt/doc-rev-list-filter-provided-objects:
  docs: clarify git-rev-list(1) --filter behavior
2025-12-30 12:58:19 +09:00
Junio C Hamano
02e9bc3392 Merge branch 'jt/repo-struct-more-objinfo'
More object database related information are shown in "git repo
structure" output.

* jt/repo-struct-more-objinfo:
  builtin/repo: add object disk size info to structure table
  builtin/repo: add disk size info to keyvalue stucture output
  builtin/repo: add inflated object info to structure table
  builtin/repo: add inflated object info to keyvalue structure output
  builtin/repo: humanise count values in structure output
  strbuf: split out logic to humanise byte values
  builtin/repo: group per-type object values into struct
2025-12-30 12:58:19 +09:00
Derrick Stolee
56d388e6ad diff: avoid segfault with freed entries
When computing a diff in a partial clone, there is a chance that we
could trigger a prefetch of missing objects at the same time as we are
freeing entries from the global diff queue. This is difficult to
reproduce, as we need to have some objects be freed from the queue
before triggering the prefetch of missing objects. There is a new test
in t4067 that does trigger the segmentation fault that results in this
case.

The fix is to set the queue pointer to NULL after it is freed, and then
to be careful about NULL values in the prefetch.

The more elaborate explanation is that within diffcore_std(), we may
skip the initial prefetch due to the output format (--name-only in the
test) and go straight to diffcore_skip_stat_unmatch(). In that method,
the index entries that have been invalidated by path changes show up as
entries but may be deleted because they are not actually content diffs
and only newer timestamps than expected. As those entries are deleted,
later entries are checked with diff_filespec_check_stat_unmatch(), which
uses diff_queued_diff_prefetch() as the missing_object_cb in its diff
options. That can trigger downloading missing objects if the appropriate
scenario occurs to trigger a call to diff_popoulate_filespec(). It's
finally within that callback to diff_queued_diff_prefetch() that the
segfault occurs.

The test was hard to find because it required some real differences,
some not-different files that had a newer modified time, and the order
of those files alphabetically was important to trigger the deletion
before the prefetch was triggered.

I briefly considered a "lock" member for the diff queue, but it was a
much larger diff and introduced many more possible error scenarios.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-30 10:53:47 +09:00
Deveshi Dwivedi
861dbb1586 t5403: use test_path_is_file instead of test -f
Replace 'test -f' with the test_path_is_file in
t5403-post-checkout-hook.sh. This helper provides better error
messages when tests fail, making it easier to debug issues.

Signed-off-by: Deveshi Dwivedi <deveshigurgaon@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-30 09:23:00 +09:00
Elijah Newren
979ee83e8a merge-ort: fix corner case recursive submodule/directory conflict handling
At GitHub, a few repositories were triggering errors of the form:

    git: merge-ort.c:3037: process_renames: Assertion `newinfo && !newinfo->merged.clean' failed.
    Aborted (core dumped)

While these may look similar to both
    a562d90a350d (merge-ort: fix failing merges in special corner case,
                  2025-11-03)
and
    f6ecb603ff8a (merge-ort: fix directory rename on top of source of other
                  rename/delete, 2025-08-06)
the cause is different and in this case the problem is not an
over-conservative assertion, but a bug before the assertion where we did
not update all relevant state appropriately.

It sadly took me a really long time to figure out how to get a simple
reproducer for this one.  It doesn't really have that many moving parts,
but there are multiple pieces of background information needed to
understand it.

First of all, when we have two files added at the same path, merge-ort
does a two-way merge of those files.  If we have two directories added
at the same path, we basically do the same thing (taking the union of
files, and two-way merging files with the same name).  But two-way
merging requires components of the same type.  We can't merge the
contents of a regular file with a directory, or with a symlink, or with
a submodule.  Nor can any of those other types be merged with each
other, e.g. merging a submodule with a directory is a bad idea.  When
two paths have the same name but their types do not match, merge-ort is
forced to move one of them to an alternate filename (using the
unique_path() function).

Second, if two commits being merged have more than one merge-base,
merge-ort will merge the merge-bases to create a virtual merge-base, and
use that as the base commit.

Third, one of the really important optimizations in merge-ort is trivial
tree-level resolution (roughly meaning merging trees without recursing
into them).  This optimization has some nuance to it that is important
to the current bug, and to understand it, it helps to first look at the
high-level overview of how merge-ort runs; there are basically three
high-level functions that the work is divided between:
    collect_merge_info() - walks the top-level trees getting individual
                           paths of interest
    detect_renames() - detect renames between paths in order to match up
                       paths for three-way merging
    process_entries() - does a few things of interest:
      * three-way merging of files,
      * other special handling (e.g. adjusting paths with conflicting
        types to avoid path collisions)
      * as it finishes handling all the files within a subdirectory,
        writes out a new tree object for that directory

If it were not for renames, we could just always do tree-level merging
whenever the tree on at least one side was unmodified.  Unfortunately,
we need to recurse into trees to determine whether there are renames.
However, we can also do tree-level merging so long as there aren't any
*relevant* renames (another merge-ort optimization), which we can
determine without recursing into trees.

We would also be able to do tree-level merging if we somehow apriori
knew what renames existed, by only recursing into the trees which we
could otherwise trivially merge if they contained files involved in
renames.  That might not seem useful, because we need to find out the
renames and we have to recurse into trees to do so, but when you find
out that the process_entries() step is more computationally expensive
than the collect_merge_info() step, it yields an interesting strategy:
   * run collect_merge_info()
   * run detect_renames()
   * cache the renames()
   * restart -- rerun collect_merge_info(), using the cached renames to
     only recurse into the needed trees
   * we already have the renames cached so no need to re-detect
   * run process_entries() on the reduced list of paths
which was implemented back in 7bee6c100431 (merge-ort: avoid recursing
into directories when we don't need to, 2021-07-16)  Crucially, this
restarting only occurs if the number of paths we could skip recursing
into exceeds the number we still need to recurse into by some safety
factor (wanted_factor in handle_deferred_entries()); forgetting this
fact is a great way to repeatedly fail to create a minimal testcase for
several days and go down alternate wrong paths).

Now, I earlier summarized this optimization as "merging trees without
recursing into them", but this optimization does not require that all
three sides of history has a directory at a given path.  So long as the
tree on one side matches the tree in the base version, we can decide to
resolve in favor of whatever the other side of history has at that path
-- be it a directory, a file, a submodule, or a symlink.  Unfortunately,
the code in question didn't fully realize this, and was written assuming
the base version and both sides would have a directory at the given
path, as can be seen by the "ci->filemask == 0" comment in
resolve_trivial_directory_merge() that was added as part of 7bee6c100431
(merge-ort: avoid recursing into directories when we don't need to,
2021-07-16).  A few additional lines of code are needed to handle cases
where we have something other than a directory on the other side of
history.

But, knowing that resolve_trivial_directory_merge() doesn't have
sufficient state updating logic doesn't show us how to trigger a bug
without combining with the other bits of information we provided above.
Here's a relevant testcase:
   * branches A & B
   * commit A1: adds "folder" as a directory with files tracked under it
   * commit B1: adds "folder" as a submodule
   * commit A2: merges B1 into A1, keeping "folder" as a directory
     (and in fact, with no changes to "folder" since A1), discarding the
     submodule
   * commit B2: merges A1 into B1, keeping "folder" as a submodule
     (and in fact, with no changes to "folder" since B1), discarding the
     directory
Here, if we try to merge A2 & B2, the logic proceeds as follows:
   * we have multiple merge-bases: A1 & B1.  So we have to merge those
     to get a virtual merge base.
   * due to "folder" as a directory and "folder" as a submodule, the
     path collision logic triggers and renames "folder" as a submodule
     to "folder~Temporary merge branch 2" so we can keep it alongside
     "folder" as a directory.
   * we now have a virtual merge base (containing both "folder"
     directory and a "folder~Temporary merge branch 2" submodule) and
     can now do the outer merge
   * in the first step of the outer merge, we attempt to defer recursing
     into folder/ as a directory, but find we need to for rename
     detection.
   * in rename detection, we note that "folder~Temporary merge branch 2"
     has the same hash as "folder" as a submodule in B2, which means we
     have an exact rename.
   * after rename detection, we discover no path in folder/ is needed
     for renames, and so we can cache renames and restart.
   * after restarting, we avoid recursing into "folder/" and realize we
     can resolve it trivially since it hasn't been modified.  The
     resolution removes "folder/", leaving us only "folder" as a
     submodule from commit B2.
   * After this point, we should have a rename/delete conflict on
     "folder~Temporary merge branch 2" -> "folder", but our marking of
     the merge of "folder" as clean broke our ability to handle that and
     in fact triggers an assertion in process_renames().

When there was a df_conflict (directory/"file" conflict, where "file"
could be submodule or regular file or symlink), ensure
resolve_trivial_directory_merge() handles it properly.  In particular:
  * do not pre-emptively mark the path as cleanly merged if the
    remaining path is a file; allow it to be processed in
    process_entries() later to determine if it was clean
  * clear the parts of dirmask or filemask corresponding to the matching
    sides of history, since we are resolving those away
  * clear the df_conflict bit afterwards; since we cleared away the two
    matching sides and only have one side left, that one side can't
    have a directory/file conflict with itself.

Also add the above minimal testcase showcasing this bug to t6422, **with
a sufficient number of paths under the folder/ directory to actually
trigger it**.  (I wish I could have all those days back from all the
wrong paths I went down due to not having enough files under that
directory...)

I know this commit has a very high ratio of lines in the commit message
to lines of comments, and a relatively high ratio of comments to actual
code, but given how long it took me to track down, on the off chance
that we ever need to further modify this logic, I wanted it thoroughly
documented for future me and for whatever other poor soul might end up
needing to read this commit message.

Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-30 08:59:52 +09:00
René Scharfe
009fceeda2 tag: stop using the_repository
gpg_verify_tag() shows the passed in object name on error.  Both callers
provide one.  It falls back to abbreviated hashes for future callers
that pass in a NULL name.  DEFAULT_ABBREV is default_abbrev, which in
turn is a global variable that's populated by git_default_config() and
only available with USE_THE_REPOSITORY_VARIABLE.

Don't let that hypothetical hold us back from getting rid of
the_repository in tag.c.  Fall back to full hashes, which are more
appropriate for error messages anyway.  This allows us to stop setting
USE_THE_REPOSITORY_VARIABLE.

Signed-off-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-29 22:02:54 +09:00
René Scharfe
b6e4cc8c32 tag: support arbitrary repositories in parse_tag()
Allow callers of parse_tag() pass in the repository to use.  Let most of
them pass in the_repository to get the same result as before.  One of
them has stopped using the_repository in ef9b0370da (sha1-name.c: store
and use repo in struct disambiguate_state, 2019-04-16); let it pass in
its stored repository.

Signed-off-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-29 22:02:54 +09:00
René Scharfe
154717b3b0 tag: support arbitrary repositories in gpg_verify_tag()
Allow callers of gpg_verify_tag() specify the repository to use by
providing a parameter for that.  One of the two has not been using
the_repository since 43a8391977 (builtin/verify-tag: stop using
`the_repository`, 2025-03-08); let it pass in the correct repository.
The other simply passes the_repository to get the same result as before.

Signed-off-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-29 22:02:53 +09:00
René Scharfe
e61f227d06 tag: use algo of repo parameter in parse_tag_buffer()
Stop using "the_hash_algo" explicitly and implictly via parse_oid_hex()
and instead use the "hash_algo" member of the passed in repository,
which is more correct.

Signed-off-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-29 22:02:53 +09:00
Junio C Hamano
7c7698a654 The 13th batch
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-28 17:36:17 +09:00
Junio C Hamano
d480fd08f8 Merge branch 'ap/packfile-promisor-object-optim'
The code path that enumerates promisor objects have been optimized
to skip pointlessly parsing blob objects.

* ap/packfile-promisor-object-optim:
  packfile: skip hash checks in add_promisor_object()
  object: apply skip_hash and discard_tree optimizations to unknown blobs too
2025-12-28 17:36:17 +09:00
Junio C Hamano
cb7c6f441e Merge branch 'ja/doc-misc-fixes'
Various documentation fixes.

* ja/doc-misc-fixes:
  doc: correct minor wording issues
  doc: fix asciidoc markup issues in several files
2025-12-28 17:36:16 +09:00
Junio C Hamano
86862bf287 Merge branch 'jc/doc-commit-signoff-config'
Documentation update.

* jc/doc-commit-signoff-config:
  signoff-option: linkify the reference to gitfaq
  commit: document that $command.signoff will not be added
2025-12-28 17:36:16 +09:00
Junio C Hamano
c744b2c16a Merge branch 'jc/c99-fam'
Require C99 style flexible array member support from all platforms.

* jc/c99-fam:
  FLEX_ARRAY: require platforms to support the C99 syntax
2025-12-28 17:36:16 +09:00
René Scharfe
06188ea5f3 config: use git_parse_int() in git_config_get_expiry_in_days()
git_config_get_expiry_in_days() calls git_parse_signed() with the
maximum value of int, which is equivalent to calling git_parse_int().
Do that instead, as its shorter and clearer.

This requires demoting "days" to int to match.  Promote "scale" to
intmax_t in turn to arrive at the same result when multiplying them.

Signed-off-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-28 14:04:15 +09:00
Emily Shaffer
c65f26fca4 receive-pack: convert receive hooks to hook API
This converts the last remaining hooks to the new hook API, for
the same benefits as the previous conversions (no need to toggle
signals, manage custom struct child_process, call find_hook(),
prepares for specifyinig hooks via configs, etc.).

I noticed a performance degradation when processing large amounts
of hook input with just 1 line per callback, due to run-command's
poll loop, therefore I batched 500 lines per callback, to ensure
similar pipe throughput as before and to avoid hook child waiting
on stdin.

Signed-off-by: Emily Shaffer <emilyshaffer@google.com>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-28 14:02:07 +09:00
Emily Shaffer
0bbaf3653f receive-pack: convert update hooks to new API
Use the new hook sideband API introduced in the previous commit.

The hook API avoids creating a custom struct child_process and other
internal hook plumbing (e.g. calling find_hook()) and prepares for
the specification of hooks via configs or running parallel hooks.

Execution is still sequential through the current hook.[ch] via the
run_process_parallel_opts.processes=1 arg.

Signed-off-by: Emily Shaffer <emilyshaffer@google.com>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-28 14:02:07 +09:00
Emily Shaffer
53254bfa1b hooks: allow callers to capture output
Some server-side hooks will require capturing output to send over
sideband instead of printing directly to stderr. Expose that capability.

Signed-off-by: Emily Shaffer <emilyshaffer@google.com>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-28 14:02:07 +09:00
Emily Shaffer
5ab5872a53 run-command: allow capturing of collated output
Some callers, for example server-side hooks which wish to relay hook
output to clients across a transport, want to capture what would
normally print to stderr and do something else with it. Allow that via a
callback.

By calling the callback regardless of whether there's output available,
we allow clients to send e.g. a keepalive if necessary.

Because we expose a strbuf, not a fd or FILE*, there's no need to create
a temporary pipe or similar - we can just skip the print to stderr and
instead hand it to the caller.

Signed-off-by: Emily Shaffer <emilyshaffer@google.com>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-28 14:02:07 +09:00
Adrian Ratiu
857f047e40 hook: allow overriding the ungroup option
When calling run_process_parallel() in run_hooks_opt(), the
ungroup option is currently hardcoded to .ungroup = 1.

This causes problems when ungrouping should be disabled, for
example when sideband-reading collated output from child hooks,
because sideband-reading and ungrouping are mutually exclusive.

Thus a new hook.h option is added to allow overriding.

The existing ungroup=1 behavior is preserved in the run_hooks()
API and the "hook run" command. We could modify these to take
an option if necessary, so I added two code comments there.

Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-28 14:02:07 +09:00
Adrian Ratiu
7a7717427e reference-transaction: use hook API instead of run-command
Convert the reference-transaction hook to the new hook API,
so it doesn't need to set up a struct child_process, call
find_hook or toggle the pipe signals.

The stdin feed callback is processing one ref update per
call. I haven't noticed any performance degradation due
to this, however we can batch as many we want in each call,
to ensure a good pipe throughtput (i.e. the child does not
wait after stdin).

Helped-by: Emily Shaffer <nasamuffin@google.com>
Signed-off-by: Emily Shaffer <emilyshaffer@google.com>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-28 14:02:06 +09:00
Emily Shaffer
3e2836a742 transport: convert pre-push to hook API
Move the pre-push hook from custom run-command invocations to
the new hook API which doesn't require a custom child_process
structure and signal toggling.

Signed-off-by: Emily Shaffer <emilyshaffer@google.com>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-28 14:02:06 +09:00
Emily Shaffer
05eccff8c7 hook: convert 'post-rewrite' hook in sequencer.c to hook API
Replace the custom run-command calls used by post-rewrite with
the newer and simpler hook_run_opt(), which does not need to
create a custom 'struct child_process' or call find_hook().

Another benefit of using the hook API is that hook_run_opt()
handles the SIGPIPE toggle logic.

Signed-off-by: Emily Shaffer <emilyshaffer@google.com>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-28 14:02:06 +09:00
Emily Shaffer
26238496a7 hook: provide stdin via callback
This adds a callback mechanism for feeding stdin to hooks alongside
the existing path_to_stdin (which slurps a file's content to stdin).

The advantage of this new callback is that it can feed stdin without
going through the FS layer. This helps when feeding large amount of
data and uses the run-command parallel stdin callback introduced in
the preceding commit.

Signed-off-by: Emily Shaffer <emilyshaffer@google.com>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-28 14:02:06 +09:00
Emily Shaffer
23a720e96b run-command: add stdin callback for parallelization
If a user of the run_processes_parallel() API wants to pipe a large
amount of information to the stdin of each parallel command, that
data could exceed the pipe buffer of the process's stdin and can be
too big to store in-memory via strbuf & friends or to slurp to a file.

Generally this is solved by repeatedly writing to child_process.in
between calls to start_command() and finish_command(). For a specific
pre-existing example of this, see transport.c:run_pre_push_hook().

This adds a generic callback API to run_processes_parallel() to do
exactly that in a unified manner, similar to the existing callback APIs,
which can then be used by hooks.h to convert the remaining hooks to the
new, simpler parallel interface.

Signed-off-by: Emily Shaffer <emilyshaffer@google.com>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-28 14:02:06 +09:00
Adrian Ratiu
56cef1e504 run-command: add first helper for pp child states
There is a recurring pattern of testing parallel process child states
and file descriptors to determine if a child is running, receiving any
input or if it's ready for cleanup.

Name the pp_child structure and introduce a first helper to make these
checks more readable. Next commits will add more helpers and checks.

Suggested-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-28 14:02:06 +09:00
René Scharfe
abf05d856f show-branch: use prio_queue
Building a list using commit_list_insert_by_date() has quadratic worst
case complexity.  Avoid it by using prio_queue.

Use prio_queue_peek()+prio_queue_replace() instead of prio_queue_get()+
prio_queue_put() if possible, as the former only rebalance the
prio_queue heap once instead of twice.

In sane repositories this won't make much of a difference because the
number of items in the list or queue won't be very high:

Benchmark 1: ./git_v2.52.0 show-branch origin/main origin/next origin/seen origin/todo
  Time (mean ± σ):     538.2 ms ±   0.8 ms    [User: 527.6 ms, System: 9.6 ms]
  Range (min … max):   537.0 ms … 539.2 ms    10 runs

Benchmark 2: ./git show-branch origin/main origin/next origin/seen origin/todo
  Time (mean ± σ):     530.6 ms ±   0.4 ms    [User: 519.8 ms, System: 9.8 ms]
  Range (min … max):   530.1 ms … 531.3 ms    10 runs

Summary
  ./git show-branch origin/main origin/next origin/seen origin/todo ran
    1.01 ± 0.00 times faster than ./git_v2.52.0 show-branch origin/main origin/next origin/seen origin/todo

That number is not limited, though, and in pathological cases like the
one in p6010 we see a sizable improvement:

Test                      v2.52.0           HEAD
------------------------------------------------------------------
6010.4: git show-branch   2.19(2.19+0.00)   0.03(0.02+0.00) -98.6%

Signed-off-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-28 14:01:23 +09:00
René Scharfe
cee341e9dd macOS: use iconv from Homebrew if needed and present
The library function iconv(3) supplied with macOS versions 15.7.2
(Sequoia) and 26.1 (Tahoe) is unreliable when doing conversions from
ISO-2022-JP to UTF-8 in multiple steps; t3900 reports this breakage:

  not ok 17 - ISO-2022-JP should be shown in UTF-8 now
  not ok 25 - ISO-2022-JP should be shown in UTF-8 now
  not ok 38 - commit --fixup into ISO-2022-JP from UTF-8

As a workaround, use libiconv from Homebrew, if available.  Search it in
its default locations: /opt/homebrew for Apple Silicon and /usr/local
for macOS Intel, with the former taking precedence.  Respect ICONVDIR if
already set by the user, though.

Helped-by: Koji Nakamaru <koji.nakamaru@gree.net>
Signed-off-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-25 16:43:10 +09:00
René Scharfe
363837afe7 macOS: make Homebrew use configurable
On macOS we opportunistically use Homebrew-installed versions of
gettext(3) and msgfmt(1).  Make that behavior configurable by providing
make variables to disable Homebrew usage (NO_HOMEBREW) and to allow
using a non-default installation location (HOMEBREW_PREFIX).

Include and link only the gettext keg via the symlink opt/gettext
pointing to its installed version instead of using the Homebrew prefix.
This is simpler and prevents accidentally including other libraries.

Suggested-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
Suggested-by: Torsten Bögershausen <tboegi@web.de>
Signed-off-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-25 16:43:09 +09:00
brian m. carlson
93f894c001 checkout: quote invalid treeish in error message
We received a report that invoking "git restore -source my_base_branch"
resulted in the confusing error message "fatal: could not resolve
ource".  This looked like a typo in our error message, but it is
actually because "-source" is missing its second dash and is being
resolved as "-s ource".  However, due to the lack of the quoting
recommended in CodingGuidelines, this is confusing to the reader and
we can do better.

Add the necessary quoting to this message.  With this change, we now get
this less confusing message:

    fatal: could not resolve 'ource'

Reported-by: Zhelyo Zhelev <zhelyo@gmail.com>
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-25 08:27:22 +09:00
Junio C Hamano
66ce5f8e88 The 12th batch
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-23 11:33:17 +09:00
Junio C Hamano
d8000781eb Merge branch 'kn/fix-fetch-backfill-tag-with-batched-ref-updates'
"git fetch" that involves fetching tags, when a tag being fetched
needs to overwrite existing one, failed to fetch other tags, which
has been corrected.

* kn/fix-fetch-backfill-tag-with-batched-ref-updates:
  fetch: fix failed batched updates skipping operations
  fetch: fix non-conflicting tags not being committed
  fetch: extract out reference committing logic
2025-12-23 11:33:17 +09:00
Junio C Hamano
5d2be7425c Merge branch 'rs/diff-files-r-find-copies-fix'
"git diff-files -R --find-copies-harder" has been taught to use
the potential copy sources from the index correctly.

* rs/diff-files-r-find-copies-fix:
  diff-files: fix copy detection
2025-12-23 11:33:16 +09:00
Junio C Hamano
86ebd83e6a Merge branch 'jc/memzero-array'
Further application of MEMZERO_ARRAY() macro to the rest of the
code base.

* jc/memzero-array:
  cocci: use MEMZERO_ARRAY() a bit more
  coccicheck: emit the contents of cocci patch
2025-12-23 11:33:16 +09:00
Junio C Hamano
396df67739 Merge branch 'tc/memzero-array'
MEMZERO_ARRAY() helper is introduced to avoid clearing only the
first N bytes of an N-element array whose elements are larger than
a byte.

* tc/memzero-array:
  contrib/coccinelle: pass include paths to spatch(1)
  git-compat-util: introduce MEMZERO_ARRAY() macro
2025-12-23 11:33:16 +09:00
Junio C Hamano
c77ba76807 Merge branch 'jc/completion-no-single-letter-options'
In-code comment update to clarify that single-letter options are
outside of the scope of command line completion script.

* jc/completion-no-single-letter-options:
  completion: clarify support for short options and arguments
2025-12-23 11:33:15 +09:00
Junio C Hamano
00bf98b16e Merge branch 'jc/submodule-add'
"git submodule add" to add a submodule under <name> segfaulted,
when a submodule.<name>.something is already in .gitmodules file
without defining where its submodule.<name>.path is, which has been
corrected.

* jc/submodule-add:
  submodule add: sanity check existing .gitmodules
2025-12-23 11:33:15 +09:00
Junio C Hamano
f0c063b67c Merge branch 'ds/doc-scalar-config'
Documentation updates.

* ds/doc-scalar-config:
  scalar: document config settings
  scalar: alphabetize and simplify config
  scalar: remove stale config values
  scalar: use index.skipHash=true for performance
  scalar: annotate config file with "set by scalar"
2025-12-23 11:33:15 +09:00
Junio C Hamano
c8d76f7325 The 11th batch
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-22 14:57:49 +09:00
Junio C Hamano
e72259073d Merge branch 'rs/t4014-git-version-string-fix'
Test fix.

* rs/t4014-git-version-string-fix:
  t4014: support Git version strings with spaces
2025-12-22 14:57:49 +09:00
Junio C Hamano
bcc20b8304 Merge branch 'kj/pull-options-decl-cleanup'
Code clean-up.

* kj/pull-options-decl-cleanup:
  pull: move options[] array into function scope
2025-12-22 14:57:49 +09:00
Junio C Hamano
448673412d Merge branch 'jc/macports-darwinports'
Makefile in-comment doc update.

* jc/macports-darwinports:
  Makefile: help macOS novices by mentioning MacPorts
2025-12-22 14:57:48 +09:00
Junio C Hamano
24a51fef5b Merge branch 'rs/replay-wrong-onto-fix'
"git replay --onto=<commit> ...", when <commit> is mistyped,
started to segfault with recent change, which has been corrected.

* rs/replay-wrong-onto-fix:
  replay: move onto NULL check before first use
2025-12-22 14:57:48 +09:00
Junio C Hamano
6a3051d3c2 Merge branch 'kh/doc-replay-updates'
"git replay" documentation updates.

* kh/doc-replay-updates:
  doc: replay: link section using markup
  replay: improve --contained and add to doc
  doc: replay: mention no output on conflicts
2025-12-22 14:57:48 +09:00
Junio C Hamano
5a8046ab33 Merge branch 'ps/odb-alternates-object-sources'
Code refactoring around alternate object store.

* ps/odb-alternates-object-sources:
  odb: write alternates via sources
  odb: read alternates via sources
  odb: drop forward declaration of `read_info_alternates()`
  odb: remove mutual recursion when parsing alternates
  odb: stop splitting alternate in `odb_add_to_alternates_file()`
  odb: move computation of normalized objdir into `alt_odb_usable()`
  odb: resolve relative alternative paths when parsing
  odb: refactor parsing of alternates to be self-contained
2025-12-22 14:57:48 +09:00
Jean-Noël Avila
8ee262985a doc: correct minor wording issues
* use imperative mood for consistency in options descriptions
* add missing parenthesis
* reword verbose phrase in git-repack.adoc

Signed-off-by: Jean-Noël Avila <jn.avila@free.fr>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-20 14:55:43 +09:00
Jean-Noël Avila
b2ff85e12c doc: fix asciidoc markup issues in several files
* fix incorrect use of backticks for markup in
  git-checkout.adoc, git-worktree.adoc
* switch tabs to spaces	in git-send-email.adoc list items

Signed-off-by: Jean-Noël Avila <jn.avila@free.fr>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-20 14:55:43 +09:00
Junio C Hamano
a0c813951a signoff-option: linkify the reference to gitfaq
The GitFAQ is a proper manual page in the section 7, so refer to it
using the usual linkgit:stuff[7] syntax.

Helped-by: Kristoffer Haugsbakk
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-19 21:51:01 +09:00
D. Ben Knoble
c469ca26c5 rust: build correctly without GNU sed
From e509b5b8be (rust: support for Windows, 2025-10-15), we check
cargo's information to decide which library to build. However, that
check mistakenly used "sed -s" ("consider files as separate rather than
as a single, continuous long stream"), which is a GNU extension. The
build thus fails on macOS with "meson -Drust=enabled", which comes with
BSD-derived sed.

Instead, use the intended "sed -n" and print the matching section of the
output. This failure mode likely went unnoticed on systems with GNU sed
(common for developer machines and CI) because, in those instances, the
output being matched by case is the full cargo output (which either
contains the string "-windows-" or doesn't).

Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Helped-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: D. Ben Knoble <ben.knoble+github@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-19 17:57:26 +09:00
Junio C Hamano
beb1789f08 Merge branch 'ps/ci-rust' into dk/ci-rust-fix
* ps/ci-rust:
  rust: support for Windows
  ci: verify minimum supported Rust version
  ci: check for common Rust mistakes via Clippy
  rust/varint: add safety comments
  ci: check formatting of our Rust code
  ci: deduplicate calls to `apt-get update`
  t8020: fix test failure due to indeterministic tag sorting
  gitlab-ci: upload Meson test logs as JUnit reports
  gitlab-ci: drop workaround for Python certificate store on Windows
  gitlab-ci: ignore failures to disable realtime monitoring
  gitlab-ci: dedup instructions to disable realtime monitoring
  ci: enable Rust for breaking-changes jobs
  ci: convert "pedantic" job into full build with breaking changes
  BreakingChanges: announce Rust becoming mandatory
  varint: reimplement as test balloon for Rust
  varint: use explicit width for integers
  help: report on whether or not Rust is enabled
  Makefile: introduce infrastructure to build internal Rust library
  Makefile: reorder sources after includes
  meson: add infrastructure to build internal Rust library
2025-12-19 17:57:16 +09:00
Greg Funni
46d0ee2d69 refs: dereference the value of the required pointer
Currently, this always prints yes because required is non-null.

This is the wrong behavior. The boolean must be
dereferenced.

Signed-off-by: Greg Funni <gfunni234@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-19 12:55:38 +09:00
Greg Funni
12f0be0857 repository: remove duplicate free of cache->squash_msg
Thankfully, it is set to NULL, so no security consequences.
However, this is still a mistake that must be rectified.

Signed-off-by: Greg Funni <gfunni234@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-19 12:51:44 +09:00
Johannes Schindelin
949df6ed6b test_detect_ref_format: fix comment
When 58aaf59133b (t: introduce GIT_TEST_DEFAULT_REF_FORMAT envvar,
2023-12-29) copy-edited the `test_detect_hash` function, the code
comment was accidentally left unchanged. Let's adjust it.

Noticed-by: Matthew John Cheetham <mjcheetham@outlook.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-19 12:49:51 +09:00
Jeff King
17f4b01da7 t5563: add missing end-of-line in HTTP header
In t5563, we test how various oddly-formatted WWW-Authenticate headers
are passed through curl to git's credential subsystem (and ultimately
out to credential helpers). One test, "access using basic auth with
wwwauth header mixed line-endings" does something odd. It does not mix
line endings at all (which must be CRLF according to the RFC anyway),
but omits the line ending entirely for the final header!

This means that the server produces an incomplete response. We send our
final header, and then the newline which is meant to mark the end of
headers (and the start of the body) becomes the line ending for that
header. And there is no header/body separator in the output at all.

Looking at strace, this is what the client reads:

  recvfrom(9, "WWW-Authenticate: FooBar param1=\"value1\"\r\n \r\n\tparam2=\"value2\"\r\nWWW-Authenticate: Basic realm=\"example.com\"", 16384, 0, NULL, NULL) = 106
  recvfrom(9, "\n", 16384, 0, NULL, NULL) = 1
  recvfrom(9, "", 16384, 0, NULL, NULL) = 0

The headers themselves are produced from the custom-auth.challenge file
we write in the test (which is missing the final CRLF), and then the
header/body separator comes from our lib-httpd/nph-custom-auth.sh CGI.
(Ignore for a moment that it is producing a bare newline, which I think
is a bug; it should be a CRLF but curl is happy with either).

Older versions of curl seemed to be OK with the truncated output, but
the upcoming 8.18.0 release seems to get confused. Specifically, since
67ae101666 (http: unfold response headers earlier, 2025-12-12) our
request to the server fails with insufficient credentials. I traced far
enough to see that curl does relay the header back to us, which we then
pass to a credential helper, which gives us the correct
username/password combination. But on our followup request, curl refuses
to send the Authorization header (and so gets an HTTP 401 again).

The change in curl's behavior is a bit unexpected, but since we are
sending it garbage, it is hard to complain too much. Let's add the
missing CRLF to the header. I _think_ this was just an oversight and not
the intent of the test. And that the "mixed line-endings" really meant
"mixed continuations", since we differ from the previous test in
continuing with both space and tab. So I've likewise updated the test
title to match that assumption.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-18 22:02:15 +09:00
Jeff King
2c6fc31e04 t5551: handle trailing slashes in expected cookies output
We check in t5551 that curl updates the expected list of cookies after
making a request. We do this by telling it to read and write cookies
from a particular text file, and then checking that after curl runs, the
file has the expected content.

However, in the upcoming curl 8.18.0, the output file has changed
slightly: curl will canonicalize the paths it writes, due to commit
a093c93994 (cookie: only keep and use the canonical cleaned up path,
2025-12-07). In particular, it strips trailing slashes from the paths we
see in the cookies.txt file.

This doesn't matter to Git, as the cookie handling is all internal to
curl. But our test is overly brittle and breaks as a result.

We can fix it by matching either format. We'll expect the new format
(without trailing slashes) and strip the slashes from curl's output
before comparing. That lets us pass with both old and new versions (I
tested against curl's 8_17_0 and rc-8_18_0-2 tags, which are
respectively before and after the curl change).

In theory it might be nice to try to future-proof this test more by
looking only for the bits we care about, rather than a byte-wise
comparison of the whole file. But after removing comments and blank
lines (which we already do), we care about most of what's there. So it's
not clear to me what a more liberal test would look like. Given that the
format doesn't change all that often, it's probably OK to stop here and
see if it ever breaks again.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-18 22:02:15 +09:00
Junio C Hamano
a650ad996d odb: do not use "blank" substitute for NULL
When various *object_info() functions are given an extended object
info structure as NULL by a caller that does not want any details,
the code uses a file-scope static blank_oi and passes it down to
the helper functions they use, to avoid handling NULL specifically.

The ps/object-read-stream topic graduated to 'master' recently
however had a bug that assumed that two identically named file-scope
static variables in two functions are the same, which of course is
not the case.  This made "git commit" take 0.38 seconds to 1508
seconds in some case, as reported by Aaron Plattner here:

  https://lore.kernel.org/git/f4ba7e89-4717-4b36-921f-56537131fd69@nvidia.com/

We _could_ move the blank_oi variable to the global scope in common
section to fix this regression, but explicitly handling the NULL is
a much safer fix.  It would also reduce the chance of errors that
somebody accidentally writes into blank_oi, making its contents
dirty, which potentially will make subsequent calls into the
function misbehave.  By explicitly handling NULL input, we no longer
have to worry about it.

Reported-by: Aaron Plattner <aplattner@nvidia.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-18 20:48:01 +09:00
Junio C Hamano
1da2a42c78 Merge branch 'ps/object-read-stream' into jc/object-read-stream-fix
* ps/object-read-stream: (32 commits)
  streaming: drop redundant type and size pointers
  streaming: move into object database subsystem
  streaming: refactor interface to be object-database-centric
  streaming: move logic to read packed objects streams into backend
  streaming: move logic to read loose objects streams into backend
  streaming: make the `odb_read_stream` definition public
  streaming: get rid of `the_repository`
  streaming: rely on object sources to create object stream
  packfile: introduce function to read object info from a store
  streaming: move zlib stream into backends
  streaming: create structure for filtered object streams
  streaming: create structure for packed object streams
  streaming: create structure for loose object streams
  streaming: create structure for in-core object streams
  streaming: allocate stream inside the backend-specific logic
  streaming: explicitly pass packfile info when streaming a packed object
  streaming: propagate final object type via the stream
  streaming: drop the `open()` callback function
  streaming: rename `git_istream` into `odb_read_stream`
  object-file: refactor writing objects via a stream
  ...
2025-12-18 12:21:21 +09:00
Matthew Hughes
1722c2244b docs: note the type of core.attributesfile
The previous wording:

> Path expansions are made the same way as for `core.excludesFile`.

required one to check the docs for 'core.excludesFile' and from there
the definition of the pathname variable type to understand the path
expansion behaviour of this variable. Instead, just link directly to the
pathname type.

This change is basically the same rewording as was done to
'core.excludesFile' in dca83abd (config: describe 'pathname' value
type, 2016-04-29).

Signed-off-by: Matthew Hughes <matthewhughes934@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-18 09:15:17 +09:00
Justin Tobler
df1b071fed builtin/repo: add object disk size info to structure table
Similar to a prior commit, update the table output format for the
git-repo(1) structure command to display the total object disk usage by
object type.

Signed-off-by: Justin Tobler <jltobler@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-18 09:02:32 +09:00
Justin Tobler
67cecc693f builtin/repo: add disk size info to keyvalue stucture output
Similar to a prior commit, extend the keyvalue and nul output formats of
the git-repo(1) structure command to additionally provide info regarding
total object disk sizes by object type.

Signed-off-by: Justin Tobler <jltobler@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-18 09:02:32 +09:00
Justin Tobler
4d279ae36b builtin/repo: add inflated object info to structure table
Update the table output format for the git-repo(1) structure command to
begin printing the total inflated object size info by object type. To be
more human-friendly, larger values are scaled down and displayed with
the appropriate unit prefix. Output for the keyvalue and nul formats
remains unchanged.

Signed-off-by: Justin Tobler <jltobler@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-18 09:02:31 +09:00
Justin Tobler
3e114496e4 builtin/repo: add inflated object info to keyvalue structure output
The structure subcommand for git-repo(1) outputs basic count information
for objects and references. Extend this output to also provide
information regarding total size of inflated objects by object type.

For now, object size by object type info is only added to the keyvalue
and nul output formats. In a subsequent commit, this info is also added
to the table format.

Signed-off-by: Justin Tobler <jltobler@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-18 09:02:31 +09:00
Justin Tobler
54731320cc builtin/repo: humanise count values in structure output
The table output format for the git-repo(1) structure subcommand is used
by default and intended to provide output to users in a human-friendly
manner. When the reference/object count values in a repository are
large, it becomes more cumbersome for users to read the values.

For larger values, update the table output format to instead produce
more human-friendly count values that are scaled down with the
appropriate unit prefix. Output for the keyvalue and nul formats remains
unchanged.

Signed-off-by: Justin Tobler <jltobler@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-18 09:02:31 +09:00
Justin Tobler
ce849b1851 strbuf: split out logic to humanise byte values
In a subsequent commit, byte size values displayed in table output for
the git-repo(1) "structure" subcommand will be shown in a more
human-readable format with the appropriate unit prefixes. For this
usecase, the downscaled values and unit strings must be handled
separately to ensure proper column alignment.

Split out logic from strbuf_humanise() to downscale byte values and
determine the corresponding unit prefix into a separate humanise_bytes()
function that provides seperate value and unit strings.

Note that the "byte" string in "t/helper/test-simple-ipc.c" is unmarked
for translation here so that it doesn't conflict with the newly defined
plural "byte/bytes" translation and instead uses it.

Signed-off-by: Justin Tobler <jltobler@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-18 09:02:31 +09:00
Justin Tobler
9faaf254ba builtin/repo: group per-type object values into struct
The `object_stats` structure stores object counts by type. In a
subsequent commit, additional per-type object measurements will also be
stored. Group per-type object values into a new struct to allow better
reuse.

Signed-off-by: Justin Tobler <jltobler@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-18 09:02:31 +09:00
Johannes Schindelin
ef6dd000ad t7800: work around the MSYS path conversion on Windows
Git's test suite's relies on Unix shell scripting, which is
understandable, of course, given Git's firm roots (and indeed, ongoing
focus) on Linux.

This fact, combined with Unix shell scripting's natural
habitat -- which is, naturally... *drumroll*... Unix --
often has unintended side effects, where developers expect the test
suite to run in a Unix environment, which is an incorrect assumption.

One instance of this problem can be observed in the 'difftool --dir-diff
handles modified symlinks' test case in `t7800-difftool.sh`, which
assumes that all absolute paths start with a forward slash. That
assumption is incorrect in general, e.g. on Windows, where absolute
paths have many shapes and forms, none of which starts with a forward
slash.

The only saving grace is that this test case is currently not run on
Windows because of the `SYMLINK` prerequisite. However, I am currently
working towards upstreaming symbolic link support from Git for Windows
to upstream Git, which will put a crack into that saving grace.

Let's change that test case so that it does not rely on absolute paths
(which are passed to the "external command" `ls` as parameters and are
therefore part of its output, and which the test case wants to filter
out before verifying that the output is as expected) starting with a
forward slash. Let's instead rely on the much more reliable fact that
`ls` will output the path in a line that ends in a colon, and simply
filter out those lines by matching said colon instead.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-18 08:18:14 +09:00
Johannes Schindelin
eae7c16c3d t6423: introduce Windows-specific handling for symlinking to /dev/null
The device `/dev/null` does not exist on Windows, it's called `NUL`
there. Calling `ln -s /dev/null my-symlink` in a symlink-enabled MSYS2
Bash will therefore literally link to a file or directory called `null`
that is supposed to be in the current drive's top-level `dev` directory.
Which typically does not exist.

The test, however, really wants the created symbolic link to point to
the NUL device. Let's instead use the `mklink` utility on Windows to
perform that job, and keep using `ln -s /dev/null <target>` on
non-Windows platforms.

While at it, add the missing `SYMLINKS` prereq because this test _still_
would not pass on Windows before support for symbolic links is
upstreamed from Git for Windows.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-18 08:18:14 +09:00
Johannes Schindelin
be6ac35107 t1305: skip symlink tests that do not apply to Windows
In Git for Windows, the gitdir is canonicalized so that even when the
gitdir is specified via a symbolic link, the `gitdir:` conditional
include will only match the real directory path.

Unfortunately, t1305 codifies a different behavior in two test cases,
which are hereby skipped on Windows.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-18 08:18:13 +09:00
Johannes Schindelin
dd47906923 t1006: accommodate for symlink support in MSYS2
The MSYS2 runtime (which inherits this trait from the Cygwin runtime,
and which is used by Git for Windows' Bash to emulate POSIX
functionality on Windows, the same Bash that is also used to run Git's
test suite on Windows) has a mode where it can create native symbolic
links on Windows.

Naturally, this is a bit of a strange feature, given that Cygwin goes
out of its way to support Unix-like paths even if no Win32 program
understands those, and the symbolic links have to use Win32 paths
instead (which Win32 programs understand very well).

As a consequence, the symbolic link targets get normalized before the
links are created.

This results in certain quirks that Git's test suite is ill equipped to
accommodate (because Git's test suite expects to be able to use
Unix-like paths even on Windows).

The test script t1006-cat-file.sh contains two prime examples, two test
cases that need to skip a couple assertions because they are simply
wrong in the context of Git for Windows.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-18 08:18:13 +09:00
Johannes Schindelin
bd6457cfa3 t0600: fix incomplete prerequisite for a test case
The 'symref transaction supports symlinks' test case is guarded by the
`SYMLINK` prerequisite because `core.prefersymlinkrefs = true` requires
symbolic links to be supported.

However, the `preferSymlinkRefs` feature is not supported on Windows,
therefore this test case needs the `MINGW` prerequisite, too.

There's a couple more cases where we set this config key:

  - In a subsequent test in t0600, but there we explicitly set it to
    "false". So this would naturally be supported by Windows.

  - In t7201 we set the value to `yes`, but we never verify that the
    written reference is a symbolic link in the first place. I guess
    that we could rather remove setting the configuration value here, as
    we are about to deprecate support for symrefs via symbolic links in
    the first place. But that's certainly outside of the scope of this
    patch.

  - In t9903 we do the same, but likewise, we don't check whether the
    written file is a symbolic link.

Therefore this seems to be the only instance where the tests actually
need to be adapted.

Helped-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-18 08:18:13 +09:00
Johannes Schindelin
492cc31b57 t0301: another fix for Windows compatibility
Just like 0fdcfa2f9f5 (t0301: fixes for windows compatibility,
2021-09-14) explained, we should not call `mkdir -m<mode>` in the test
suite because that would fail on Windows.

There was one forgotten instance of this which was hidden by a `SYMLINK`
prerequisite. Currently, this prevents this test case from being
executed on Windows, but with the upcoming support for symbolic links,
it would become a problem.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-18 08:18:13 +09:00
Johannes Schindelin
5e8e7e47e0 t0001: handle diff --no-index gracefully
The test case 're-init to move gitdir symlink' wants to compare the
contents of `newdir/.git`, which is a symbolic link pointing to a file.
However, `git diff --no-index`, which is used by `test_cmp` on Windows,
does not resolve symlinks; It shows the symlink _target_ instead (with a
file mode of 120000). That is totally unexpected by the test case, which
as a consequence fails, meaning that it's a bug in the test case itself.

Co-authored-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-18 08:18:13 +09:00
Johannes Schindelin
6fa50cc4a1 mingw: special-case open(symlink, O_CREAT | O_EXCL)
The `_wopen()` function would gladly follow a symbolic link to a
non-existent file and create it when given above-mentioned flags.

Git expects the `open()` call to fail, though. So let's add yet another
work-around to pretend that Windows behaves according to POSIX, see:
https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html#:~:text=If%20O_CREAT%20and%20O_EXCL%20are,set%2C%20the%20result%20is%20undefined.

This is required to let t4115.8(--reject removes .rej symlink if it
exists) pass on Windows when enabling the MSYS2 runtime's symbolic link
support.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-18 08:18:12 +09:00
Johannes Schindelin
b90a926371 apply: symbolic links lack a "trustable executable bit"
When 0482c32c334b (apply: ignore working tree filemode when
!core.filemode, 2023-12-26) fixed `git apply` to stop warning about
executable files, it inadvertently changed the code flow also for
symbolic links and directories.

Let's narrow the scope of the special `!trust_executable_git` code path
to apply only to regular files.

This is needed to let t4115.5(symlink escape when creating new files)
pass on Windows when symbolic link support is enabled in the MSYS2
runtime.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-18 08:18:12 +09:00
Johannes Schindelin
4ec7ac101b t9700: accommodate for Windows paths
Ever since fe53bbc9beb (Git.pm: Always set Repository to absolute path
if autodetecting, 2009-05-07), the t9700 test _must_ fail on Windows
because of that age-old Unix paths vs Windows paths problem.

The underlying root cause is that Git cannot run with a regular Win32
variant of Perl, the assumption that every path is a Unix path is just
too strong in Git's Perl code.

As a consequence, Git for Windows is basically stuck with using the
MSYS2 variant of Perl which uses a POSIX emulation layer (which is a
friendly fork of Cygwin) _and_ a best-effort Unix <-> Windows paths
conversion whenever crossing the boundary between MSYS2 and regular
Win32 processes. It is best effort only, though, using heuristics to
automagically convert correctly in most cases, but not in all cases.

In the context of this here patch, this means that asking `git.exe` for
the absolute path of the `.git/` directory will return a Win32 path
because `git.exe` is a regular Win32 executable that has no idea about
Unix-ish paths. But above-mentioned commit introduced a test that wants
to verify that this path is identical to the one that the Git Perl
module reports (which refuses to use Win32 paths and uses Unix-ish paths
instead). Obviously, this must fail because no heuristics can kick in at
that layer.

This test failure has not even been caught when Git introduced Windows
support in its CI definition in 2e90484eb4a (ci: add a Windows job to
the Azure Pipelines definition, 2019-01-29), as all tests relying on
Perl had to be disabled even from the start (because the CI runs would
otherwise have resulted in prohibitively long runtimes, not because
Windows is super slow per se, but because Git's test suite keeps
insisting on using technology that requires a POSIX emulation layer,
which _is_ super slow on Windows).

To work around this failure, let's use the `cygpath` utility to convert
the absolute `gitdir` path into the form that the Perl code expects.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-18 08:18:12 +09:00
Junio C Hamano
1129780f6a commit: document that $command.signoff will not be added
Every now and then we see this coming up on the list.  Let's help
new contributors who are not aware of past discussions by clearly
documenting our past consensus.

Helped-by: brian m. carlson <sandals@crustytoothpaste.net>
Helped-by: Elijah Newren <newren@gmail.com>
Helped-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-18 08:17:05 +09:00
Junio C Hamano
c4a0c8845e The 10th batch
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-17 14:11:53 +09:00
Junio C Hamano
f3951e3230 Merge branch 'kh/doc-send-email-paragraph-fix'
Docfix.

* kh/doc-send-email-paragraph-fix:
  doc: send-email: fix broken list continuation
2025-12-17 14:11:53 +09:00
Junio C Hamano
1c8a1b7bf9 Merge branch 'mh/doc-config-gui-gcwarning'
Docfix.

* mh/doc-config-gui-gcwarning:
  config: document 'gui.GCWarning'
2025-12-17 14:11:53 +09:00
Junio C Hamano
85964265a3 Merge branch 'kh/doc-pre-commit-fix'
Docfix.

* kh/doc-pre-commit-fix:
  doc: join default pre-commit paragraphs
2025-12-17 14:11:53 +09:00
Junio C Hamano
1c22dfde18 Merge branch 'jc/capability-leak'
Leakfix.

* jc/capability-leak:
  connect: plug protocol capability leak
2025-12-17 14:11:52 +09:00
Junio C Hamano
e7ef0ca622 The ninth batch
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-16 11:08:35 +09:00
Junio C Hamano
91bfbf49b6 Merge branch 'rs/ban-mktemp'
Rewrite the only use of "mktemp()" that is subject to TOCTOU race
and Stop using the insecure "mktemp()" function.

* rs/ban-mktemp:
  compat: remove gitmkdtemp()
  banned.h: ban mktemp(3)
  compat: remove mingw_mktemp()
  compat: use git_mkdtemp()
  wrapper: add git_mkdtemp()
2025-12-16 11:08:35 +09:00
Junio C Hamano
72154ce414 Merge branch 'gf/win32-pthread-cond-init'
Emulation code clean-up.

* gf/win32-pthread-cond-init:
  win32: pthread_cond_init should return a value
2025-12-16 11:08:34 +09:00
Junio C Hamano
dbe54273a7 Merge branch 'ps/object-read-stream'
The "git_istream" abstraction has been revamped to make it easier
to interface with pluggable object database design.

* ps/object-read-stream:
  streaming: drop redundant type and size pointers
  streaming: move into object database subsystem
  streaming: refactor interface to be object-database-centric
  streaming: move logic to read packed objects streams into backend
  streaming: move logic to read loose objects streams into backend
  streaming: make the `odb_read_stream` definition public
  streaming: get rid of `the_repository`
  streaming: rely on object sources to create object stream
  packfile: introduce function to read object info from a store
  streaming: move zlib stream into backends
  streaming: create structure for filtered object streams
  streaming: create structure for packed object streams
  streaming: create structure for loose object streams
  streaming: create structure for in-core object streams
  streaming: allocate stream inside the backend-specific logic
  streaming: explicitly pass packfile info when streaming a packed object
  streaming: propagate final object type via the stream
  streaming: drop the `open()` callback function
  streaming: rename `git_istream` into `odb_read_stream`
2025-12-16 11:08:34 +09:00
René Scharfe
f293bdcc29 diff-files: fix copy detection
Copy detection cannot work when comparing the index to the working tree
because Git ignores files that it is not explicitly told to track.  It
should work in the other direction, though, i.e. for a reverse diff of
the deletion of a copy from the index.

d1f2d7e8ca (Make run_diff_index() use unpack_trees(), not read_tree(),
2008-01-19) broke it with a seemingly stray change to run_diff_files().

We didn't notice because there's no test for that.  But even if we had
one, it might have gone unnoticed because the breakage only happens
with index preloading, which requires at least 1000 entries (more than
most test repos have) and is racy because it runs in parallel with the
actual command.

Fix copy detection by queuing up-to-date and skip-worktree entries using
diff_same().

While at it, use diff_same() also for queuing unchanged files not
flagged as up-to-date, i.e. clean submodules and entries where
preloading was not done at all or not quickly enough.  It uses less
memory than diff_change() and doesn't unnecessarily set the diff flag
has_changes.

Add two tests to cover running both without and with preloading.  The
first one passes reliably with the original code.  The second one
enables preloading and thus is racy.  It has a good chance to pass even
without the fix, but fails within seconds when running the test script
with --stress.  With the fix it runs fine for several minutes, until
my patience runs out.

Signed-off-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-16 10:23:26 +09:00
Junio C Hamano
c0c4dc0b70 Merge branch 'rs/diff-index-find-copies-harder-optim' into rs/diff-files-r-find-copies-fix
* rs/diff-index-find-copies-harder-optim:
  diff-index: don't queue unchanged filepairs with diff_change()
2025-12-16 10:22:56 +09:00
Justin Tobler
6d8dc99478 docs: clarify git-rev-list(1) --filter behavior
When using the --filter option for git-rev-list(1), objects that are
explicitly provided ignore filters and are always printed unless the
--filter-provided-objects option is also specified. Clarify this
behavior in the documentation.

Signed-off-by: Justin Tobler <jltobler@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-16 10:11:41 +09:00
Derrick Stolee
4ce170c522 scalar: document config settings
Add user-facing documentation that justifies the values being set by
'scalar clone', 'scalar register', and 'scalar reconfigure'.

Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-16 09:42:44 +09:00
Junio C Hamano
d8af7cadaa The eighth batch
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-14 17:04:38 +09:00
Junio C Hamano
f29e98755d Merge branch 'je/doc-data-model'
Docfix.

* je/doc-data-model:
  doc: remove stray text in Git data model
2025-12-14 17:04:38 +09:00
Junio C Hamano
affdbe41bd Merge branch 'lo/repo-struct-z'
"git repo struct" learned to take "-z" as a synonym to "--format=nul".

* lo/repo-struct-z:
  repo: add -z as an alias for --format=nul to git-repo-structure
  repo: use [--format=... | -z] instead of [-z] in git-repo-info synopsis
  repo: remove blank line from Documentation/git-repo.adoc
2025-12-14 17:04:37 +09:00
Junio C Hamano
2378ebcb58 Merge branch 'kh/advise-w-git-help-in-branch'
A help message from "git branch" now mentions "git help" instead of
"man" when suggesting to read some documentation.

* kh/advise-w-git-help-in-branch:
  branch: advice using git-help(1) instead of man(1)
2025-12-14 17:04:37 +09:00
Junio C Hamano
c382988d7b Merge branch 'je/doc-pull'
Doc fixup.

* je/doc-pull:
  doc: git-pull: fix 'git --rebase abort' typo
2025-12-14 17:04:37 +09:00
Junio C Hamano
25ce0883fe Merge branch 'tc/meson-cross-compile-fix'
Build fix.

* tc/meson-cross-compile-fix:
  meson: use is_cross_build() where possible
  meson: only detect ICONV_OMITS_BOM if possible
  meson: ignore subprojects/.wraplock
2025-12-14 17:04:37 +09:00
Junio C Hamano
21787077bf Merge branch 'js/last-modified-with-sparse-checkouts'
"git last-modified" used to mishandle "--" to mark the beginning of
pathspec, which has been corrected.

* js/last-modified-with-sparse-checkouts:
  last-modified: support sparse checkouts
2025-12-14 17:04:37 +09:00
Junio C Hamano
84ca5a2457 Merge branch 'rs/diff-index-find-copies-harder-optim'
Halve the memory consumed by artificial filepairs created during
"git diff --find-copioes-harder", also making the operation run
faster.

* rs/diff-index-find-copies-harder-optim:
  diff-index: don't queue unchanged filepairs with diff_change()
2025-12-14 17:04:36 +09:00
Junio C Hamano
794c979889 Merge branch 'tc/last-modified-active-paths-optimization'
Recent optimization to "last-modified" command introduced use of
uninitialized block of memory, which has been corrected.

* tc/last-modified-active-paths-optimization:
  last-modified: fix use of uninitialized memory
2025-12-14 17:04:36 +09:00
Kristoffer Haugsbakk
9ba08b30a1 doc: replay: link section using markup
Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-14 15:56:02 +09:00
Kristoffer Haugsbakk
03d7c9c457 replay: improve --contained and add to doc
There is no documentation for `--contained`.

Start by copying the text from `replay_options` in `builtin/
replay.c`. But some people think that the existing text is a
bit unclear; what does it mean for a branch to be contained
in a revision range? Let’s include the implied commits here:
the branches that point at commits in the range.

Also use “update” instead of “advance”. “Update” is the verb
commonly used in this context.

Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Helped-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-14 15:56:02 +09:00
Kristoffer Haugsbakk
8467c95419 doc: replay: mention no output on conflicts
Some commands will produce output on stderr if there are conflicts, but
git-replay(1) is completely silent. Explicitly spell that out.

Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-14 15:56:01 +09:00
René Scharfe
007b8994d4 t4014: support Git version strings with spaces
git --version reports its version with the prefix "git version ".
Remove precisely this string instead of everything up to and including
the rightmost space to avoid butchering version strings that contain
spaces.  This helps Apple's release of Git, which reports its version
like this: "git version 2.50.1 (Apple Git-155)".

Signed-off-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-14 15:50:44 +09:00
Junio C Hamano
8ea9492cf3 cocci: use MEMZERO_ARRAY() a bit more
Existing code in files that have been fairly stable trigger the
"make coccicheck" suggestions due to the new check.

Rewrite them to use MEMZERO_ARRAY()

Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-13 10:47:59 +09:00
Junio C Hamano
d2e4099968 coccicheck: emit the contents of cocci patch
Telling the user "you got some error messages" without showing what
the errors are is almost useless in CI environment, as the errors
cannot be examined without downloading build artifacts.

Arrange it to spew out the output when it fails.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-13 10:47:59 +09:00
Junio C Hamano
6362c9ce5e Merge branch 'tc/memzero-array' into jc/memzero-array
* tc/memzero-array:
  contrib/coccinelle: pass include paths to spatch(1)
  git-compat-util: introduce MEMZERO_ARRAY() macro
  last-modified: fix use of uninitialized memory
2025-12-13 10:39:23 +09:00
Derrick Stolee
e1588c270d scalar: alphabetize and simplify config
The config values set by Scalar went through an audit in the previous
changes, so now reorganize the settings and simplify their purpose.

First, alphabetize the config options, except put the platform-specific
options at the end. This groups two Windows-specific settings and only
one non-Windows setting.

Also, this removes the 'overwrite_on_reconfigure' setting for many of
these options. That setting made nearly all of these options "required"
for scalar enlistments, restricting use for users. Instead, now nearly
all options have removed this setting.

However, there is one setting that still has this, which is
index.skipHash, which was previously being set to _false_ when we
actually prefer the value of true. Keep the overwrite here to help
Scalar users upgrade to the new version. We may remove that overwrite in
the future once we belive that most of the users who have the false
value have upgraded to a version that overwrites that to 'true'.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-13 08:43:28 +09:00
Derrick Stolee
be667e40cb scalar: remove stale config values
These config values were added in the original Scalar contribution,
d0feac4e8c (scalar: 'register' sets recommended config and starts
maintenance, 2021-12-03), but were never fully checked for validity in
the upstream Git project. At the time, Scalar was only intended for the
contrib/ directory so did not have as rigorous of an investigation.

Each config option has its own justification for removal:

* core.preloadIndex: This value is true by default, now. Removing this
  causes some changes required to the tests that checked this config
  value. Use gui.gcwarning=false instead.

* core.fscache: This config does not exist in the core Git project, but
  is instead a config option for a Git for Windows feature.

* core.multiPackIndex: This config value is now enabled by default, so
  does not need to be called out specifically. It was originally
  included to make sure the background maintenance that created
  multi-pack-indexes would result in the expected performance
  improvements.

* credential.validate: This option is not something specific to Git but
  instead an older version of Git Credential Manager for Windows. That
  software was replaced several years ago by the cross-platform Git
  Credential Manger so this option is no longer needed to help users who
  were on that older software.

* pack.useSparse=true: This value is now Git's default as of de3a864114
  (config: set pack.useSparse=true by default, 2020-03-20) so we don't
  need it set by Scalar.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-13 08:43:28 +09:00
Derrick Stolee
05f28e4b3c scalar: use index.skipHash=true for performance
The index.skipHash config option has been set to 'false' by Scalar since
4933152cbb (scalar: enable path-walk during push via config, 2025-05-16)
but that commit message is trying to communicate the exact opposite:
that the 'true' value is what we want instead. This means that we've
been disabling this performance benefit for Scalar repos
unintentionally.

Fix this issue before we add justification for the config options set in
this list.

Oddly, enabling index.skipHash causes a test issue during 'test_commit'
in one of the Scalar tests when GIT_TEST_SPLIT_INDEX is enabled (as
caught by the linux-test-vars build). I'm fixing the test by disabling
the environment variable, but the issue should be resolved in a series
focused on the split index.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-13 08:43:27 +09:00
Derrick Stolee
48695fcde5 scalar: annotate config file with "set by scalar"
A repo may have config options set by 'scalar clone' or 'scalar
register' and then updated by 'scalar reconfigure'. It can be helpful to
point out which of those options were set by the latest scalar
recommendations.

Add "# set by scalar" to the end of each config option to assist users
in identifying why these config options were set in their repo. Use a new
helper method to simplify the two callsites.

Co-authored-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-13 08:43:27 +09:00
K Jayatheerth
bab391761d pull: move options[] array into function scope
Unless there are good reasons, it is customary to have the options[]
array used with the parse-options API declared in function scope rather
than at file scope.

Move builtin/pull.c:cmd_pull()’s options[] array into the function to
match that convention.

Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-12 22:08:02 +09:00
Junio C Hamano
4d75f2aea7 FLEX_ARRAY: require platforms to support the C99 syntax
Before C99 syntax to express that the final member in a struct is an
array of unknown number of elements, i.e.,

	struct {
		...
		T flexible_array[];
	};

came along, GNU introduced their own extension to declare such a
member with 0 size, i.e.,

		T flexible_array[0];

and the compilers that did not understand even that were given a way
to emulate it by wasting one element, i.e.,

		T flexible_array[1];

As we are using more and more C99 language features, let's see if
the platforms that still need to resort to the historical forms of
flexible array member support are still there, by forcing all the
flex array definitions to use the C99 syntax and see if anybody
screams (in which case reverting the changes is rather easy).

Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-12 22:05:19 +09:00
René Scharfe
a4a77e41fa replay: move onto NULL check before first use
cmd_replay() aborts if the pointer "onto" is NULL after argument
parsing, e.g. when specifying a non-existing commit with --onto.
15cd4ef1f4 (replay: make atomic ref updates the default behavior,
2025-11-06) added code that dereferences this pointer before the check.
Switch their places to avoid a segmentation fault.

Reported-by: Kristoffer Haugsbakk <kristofferhaugsbakk@fastmail.com>
Signed-off-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-12 12:41:26 +09:00
Junio C Hamano
8cb4a11438 Merge branch 'sa/replay-atomic-ref-updates' into rs/replay-wrong-onto-fix
* sa/replay-atomic-ref-updates:
  replay: add replay.refAction config option
  replay: make atomic ref updates the default behavior
  replay: use die_for_incompatible_opt2() for option validation
2025-12-12 12:41:17 +09:00
Junio C Hamano
d4b732899e Makefile: help macOS novices by mentioning MacPorts
Since Aug 2006, the DarwinPorts project renamed themselves as
MacPorts.  Those who are not intimately familiar with the Opensource
ecosystem around macOS from olden days, the name DarwinPorts may not
ring a bell, even when they are using MacPorts.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
Reviewed-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-12 11:19:43 +09:00
Patrick Steinhardt
221a877d47 odb: write alternates via sources
Refactor writing of alternates so that the actual business logic is
structured around the object database source we want to write the
alternate to. Same as with the preceding commit, this will eventually
allow us to have different logic for writing alternates depending on the
backend used.

Note that after the refactoring we start to call
`odb_add_alternate_recursively()` unconditionally. This is fine though
as we know to skip adding sources that are tracked already.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-11 18:39:37 +09:00
Patrick Steinhardt
f7dbd9fb2e odb: read alternates via sources
Adapt how we read alternates so that the interface is structured around
the object database source we're reading from. This will eventually
allow us to abstract away this behaviour with pluggable object databases
so that every format can have its own mechanism for listing alternates.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-11 18:39:37 +09:00
Patrick Steinhardt
3f42555322 odb: drop forward declaration of read_info_alternates()
Now that we have removed the mutual recursion in the preceding commit
it is not necessary anymore to have a forward declaration of the
`read_info_alternates()` function. Move the function and its
dependencies further up so that we can remove it.

Note that this commit also removes the function documentation of
`read_info_alternates()`. It's unclear what it's documenting, but it for
sure isn't documenting the modern behaviour of the function anymore.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-11 18:39:37 +09:00
Patrick Steinhardt
430e0e0f2e odb: remove mutual recursion when parsing alternates
When adding an alternative object database source we not only have to
consider the added source itself, but we also have to add _its_ sources
to our database. We implement this via mutual recursion:

  1. We first call `link_alt_odb_entries()`.

  2. `link_alt_odb_entries()` calls `parse_alternates()`.

  3. We then add each alternate via `odb_add_alternate_recursively()`.

  4. `odb_add_alternate_recursively()` calls `link_alt_odb_entries()`
     again.

This flow is somewhat hard to follow, but more importantly it means that
parsing of alternates is somewhat tied to the recursive behaviour.

Refactor the function to remove the mutual recursion between adding
sources and parsing alternates. The parsing step thus becomes completely
oblivious to the fact that there is recursive behaviour going on at all.
The recursion is handled by `odb_add_alternate_recursively()` instead,
which now recurses with itself.

This refactoring allows us to move parsing of alternates into object
database sources in a subsequent step.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-11 18:39:36 +09:00
Patrick Steinhardt
dccfb39cdb odb: stop splitting alternate in odb_add_to_alternates_file()
When calling `odb_add_to_alternates_file()` we know to add the newly
added source to the object database in case we have already loaded
alternates. This is done so that we can make its objects accessible
immediately without having to fully reload all alternates.

The way we do this though is to call `link_alt_odb_entries()`, which
adds _multiple_ sources to the object database source in case we have
newline-separated entries. This behaviour is not documented in the
function documentation of `odb_add_to_alternates_file()`, and all
callers only ever pass a single directory to it. It's thus entirely
surprising and a conceptual mismatch.

Fix this issue by directly calling `odb_add_alternate_recursively()`
instead.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-11 18:39:36 +09:00
Patrick Steinhardt
d17673ef42 odb: move computation of normalized objdir into alt_odb_usable()
The function `alt_odb_usable()` receives as input the object database,
the path it's supposed to determine usability for as well as the
normalized path of the main object directory of the repository. The last
part is derived by the function's caller from the object database. As we
already pass the object database to `alt_odb_usable()` it is redundant
information.

Drop the extra parameter and compute the normalized object directory in
the function itself.

While at it, rename the function to `odb_is_source_usable()` to align it
with modern terminology.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-11 18:39:35 +09:00
Patrick Steinhardt
84cec5276e odb: resolve relative alternative paths when parsing
Parsing alternates and resolving potential relative paths is currently
handled in two separate steps. This has the effect that the logic to
retrieve alternates is not entirely self-contained. We want it to be
just that though so that we can eventually move the logic to list
alternates into the `struct odb_source`.

Move the logic to resolve relative alternative paths into
`parse_alternates()`. Besides bringing us a step closer towards the
above goal, it also neatly separates concerns of generating the list of
alternatives and linking them into the object database.

Note that we ignore any errors when the relative path cannot be
resolved. This isn't really a change in behaviour though: if the path
cannot be resolved to a directory then `alt_odb_usable()` still knows to
bail out.

While at it, rename the function to `odb_add_alternate_recursively()` to
more clearly indicate what its intent is and to align it with modern
terminology.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-11 18:39:35 +09:00
Patrick Steinhardt
1660496fc4 odb: refactor parsing of alternates to be self-contained
Parsing of the alternates file and environment variable is currently
split up across multiple different functions and is entangled with
`link_alt_odb_entries()`, which is responsible for linking the parsed
object database sources. This results in two downsides:

  - We have mutual recursion between parsing alternates and linking them
    into the object database. This is because we also parse alternates
    that the newly added sources may have.

  - We mix up the actual logic to parse the data and to link them into
    place.

Refactor the logic so that parsing of the alternates file is entirely
self-contained. Note that this doesn't yet fix the above two issues, but
it is a necessary step to get there.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-11 18:39:34 +09:00
Toon Claes
467860bc0b contrib/coccinelle: pass include paths to spatch(1)
In the previous commit a new coccinelle rule is added. But neiter
`make coccicheck` nor `meson compile coccicheck` did detect a case in
builtin/last-modified.c.

This case involves the field `scratch` in `struct last_modified`. This
field is of type `struct bitmap` and that struct has a member
`eword_t *words`. Both are defined in `ewah/ewok.h`. Now, while
builtin/last-modified.c does include that header (with the subdir in the
#include directive), it seems coccinelle does not process it. So it's
unaware of the type of `words` in the bitmap, and it doesn't recognize
the rule from previous commit that uses:

    type T;
    T *ptr;

Fix coccicheck by passing all possible include paths inside the Git
project so spatch(1) can find the headers and can determine the types.

Signed-off-by: Toon Claes <toon@iotcl.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-11 14:44:43 +09:00
Toon Claes
a67b902c94 git-compat-util: introduce MEMZERO_ARRAY() macro
Introduce a new macro MEMZERO_ARRAY() that zeroes the memory allocated
by ALLOC_ARRAY() and friends. And add coccinelle rule to enforce the use
of this macro.

Signed-off-by: Toon Claes <toon@iotcl.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-11 14:44:43 +09:00
Junio C Hamano
af0ed97e10 Merge branch 'tc/last-modified-active-paths-optimization' into tc/memzero-array
* tc/last-modified-active-paths-optimization:
  last-modified: fix use of uninitialized memory
2025-12-11 14:44:28 +09:00
Patrick Steinhardt
6ce9d558ce midx-write: skip rewriting MIDX with --stdin-packs unless needed
In `write_midx_internal()` we know to skip rewriting the multi-pack
index in case the existing one already covers all packs. This logic does
not know to handle `git multi-pack-index write --stdin-packs` though, so
we end up always rewriting the MIDX in this case even if the MIDX would
not change.

With our default maintenance strategy this isn't really much of a
problem, as git-gc(1) does not use the "--stdin-packs" option. But that
is changing with geometric repacking, where "--stdin-packs" is used to
explicitly select the packfiles part of the geometric sequence.

This issue can be demonstrated trivially with a benchmark in the Git
repository: executing `git repack --geometric=2 --write-midx -d` in the
Git repository takes more than 3 seconds only to end up with the same
multi-pack index as we already had before.

The logic that decides if we need to rewrite the MIDX only checks
whether the number of packfiles covered will change. That check is of
course too lenient for "--stdin-packs", as it could happen that we want
to cover a different-but-same-size set of packfiles. But there is no
inherent reason why we cannot handle "--stdin-packs".

Improve the logic to not only check for the number of packs, but to also
verify that we are asked to generate a MIDX for the _same_ packs. This
allows us to also skip no-op rewrites for "--stdin-packs".

Helped-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-11 12:09:59 +09:00
Patrick Steinhardt
b3bab9d272 midx-write: extract function to test whether MIDX needs updating
In `write_midx_internal()` we know to skip writing the new multi-pack
index in case it would be the same as the existing one. This logic does
not handle the `--stdin-packs` option yet though, so we end up always
rewriting the MIDX if that option is passed to us.

Extract the logic to decide whether or not to rewrite the MIDX into a
separate function. This will allow us to extend that feature in the next
commit to address the above issue.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-11 12:09:58 +09:00
Patrick Steinhardt
665d19ec7b midx: fix BUG() when getting preferred pack without a reverse index
The function `midx_preferred_pack()` returns the preferred pack for a
given multi-pack index. To compute the preferred pack we:

  1. Take the first position indexed by the MIDX in pseudo-pack order.

  2. Convert this pseudo-pack position into the MIDX position.

  3. We then look up the pack that corresponds to this MIDX position.

This reliably returns the preferred pack given that all of its contained
objects will be up front in pseudo-pack order.

The second step that turns the pseudo-pack order into MIDX order
requires the reverse index though, which may not exist for example when
the MIDX does not have a bitmap. And in that case one may easily hit a
bug:

    BUG: ../pack-revindex.c:491: pack_pos_to_midx: reverse index not yet loaded

In theory, `midx_preferred_pack()` already knows to handle the case
where no reverse index exists, as it calls `load_midx_revindex()` before
calling into `midx_preferred_pack()`. But we only check for negative
return values there, even though the function returns a positive error
code in case the reverse index does not exist.

Fix the issue by testing for a non-zero return value instead, same as
all the other callers of this function already do. While at it, document
the return value of `load_midx_revindex()`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-11 12:09:58 +09:00
Karthik Nayak
b7b17ec8a6 fetch: fix failed batched updates skipping operations
Fix a regression introduced with batched updates in 0e358de64a (fetch:
use batched reference updates, 2025-05-19) when fetching references. In
the `do_fetch()` function, we jump to cleanup if committing the
transaction fails, regardless of whether using batched or atomic
updates. This skips three subsequent operations:

  - Update 'FETCH_HEAD' as part of `commit_fetch_head()`.

  - Add upstream tracking information via `set_upstream()`.

  - Setting remote 'HEAD' values when `do_set_head` is true.

For atomic updates, this is expected behavior. For batched updates,
we want to continue with these operations even if some refs fail to
update.

Skipping `commit_fetch_head()` isn't actually a regression because
'FETCH_HEAD' is already updated via `append_fetch_head()` when not
using '--atomic'. However, we add a test to validate this behavior.

Skipping the other two operations (upstream tracking and remote HEAD)
is a regression. Fix this by only jumping to cleanup when using
'--atomic', allowing batched updates to continue with post-fetch
operations. Add tests to prevent future regressions.

Helped-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-10 20:59:58 +09:00
Karthik Nayak
8ff2eef8ad fetch: fix non-conflicting tags not being committed
The commit 0e358de64a (fetch: use batched reference updates, 2025-05-19)
updated the 'git-fetch(1)' command to use batched updates. This batches
updates to gain performance improvements. When fetching references, each
update is added to the transaction. Finally, when committing, individual
updates are allowed to fail with reason, while the transaction itself
succeeds.

One scenario which was missed here, was fetching tags. When fetching
conflicting tags, the `fetch_and_consume_refs()` function returns '1',
which skipped committing the transaction and directly jumped to the
cleanup section. This mean that no updates were applied. This also
extends to backfilling tags which is done when fetching specific
refspecs which contains tags in their history.

Fix this by committing the transaction when we have an error code and
not using an atomic transaction. This ensures other references are
applied even when some updates fail.

The cleanup section is reached with `retcode` set in several scenarios:

   - `truncate_fetch_head()`, `open_fetch_head()` and `prune_refs()` set
     `retcode` before the transaction is created, so no commit is
     attempted.

   - `fetch_and_consume_refs()` and `backfill_tags()` are the primary
     cases this fix targets, both setting a positive `retcode` to
     trigger the committing of the transaction.

This simplifies error handling and ensures future modifications to
`do_fetch()` don't need special handling for batched updates.

Add tests to check for this regression. While here, add a missing
cleanup from previous test.

Reported-by: David Bohman <debohman@gmail.com>
Helped-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-10 20:59:58 +09:00
Aaron Plattner
3f5d1749e7 packfile: skip hash checks in add_promisor_object()
When is_promisor_object() is called for the first time, it lazily
initializes a set of all promisor objects by iterating through all
objects in promisor packs. For each object, add_promisor_object() calls
parse_object(), which decompresses and hashes the entire object.

For repositories with large pack files, this can take an extremely long
time. For example, on a production repository with a 176 GB promisor
pack:

 $ time ~/git/git/git-rev-list --objects --all --exclude-promisor-objects --quiet
 ________________________________________________________
 Executed in   76.10 mins    fish           external
    usr time   72.10 mins    1.83 millis   72.10 mins
    sys time    3.56 mins    0.17 millis    3.56 mins

add_promisor_object() just wants to construct the set of all promisor
objects, so it doesn't really need to verify the hash of every object.
Set PARSE_OBJECT_SKIP_HASH_CHECK to skip the hash check. This has the
side effect of skipping decompression of blob objects completely, saving
a significant amount of time:

 $ time ~/git/git/git-rev-list --objects --all --exclude-promisor-objects --quiet
 ________________________________________________________
 Executed in  124.70 secs    fish           external
    usr time   46.94 secs    0.00 millis   46.94 secs
    sys time   43.11 secs    1.03 millis   43.11 secs

Signed-off-by: Aaron Plattner <aplattner@nvidia.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-09 18:12:25 +09:00
Aaron Plattner
3c7c41d6b7 object: apply skip_hash and discard_tree optimizations to unknown blobs too
parse_object_with_flags() has an optimization to skip parsing blobs if
PARSE_OBJECT_SKIP_HASH_CHECK is set and the object hasn't been seen
before or might be a blob but hasn't been parsed yet. The latter can
happen, for example, if add_tree_entries() walks a path that references
a blob object that hasn't been seen before: lookup_blob() marks the
referenced oid as being a blob, but does not provide any additional
information about it until it is parsed.

It's possible for an object to be created without even a type, such as
when prepare_revision_walk() uses mark_uninteresting() to mark all
promisor objects as uninteresting. These objects have obj->parsed ==
false and obj->type == OBJ_NONE.

The skip_hash optimization does not consider this kind of object, so
parse_object_with_flags() proceeds to fully parse the object to
determine its type.

Improve the optimization by applying it to OBJ_NONE objects as well as
OBJ_BLOB ones. Apply a similar fix for trees.

Fixes: 8db2dad7a045 ("parse_object(): check on-disk type of suspected blob")
Signed-off-by: Aaron Plattner <aplattner@nvidia.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-09 18:12:24 +09:00
Junio C Hamano
e85ae279b0 The seventh batch
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-09 07:54:56 +09:00
Junio C Hamano
bbefa15ff5 Merge branch 'en/replay-doc-revision-range'
The use of "revision" (a connected set of commits) has been
clarified in the "git replay" documentation.

* en/replay-doc-revision-range:
  Documentation/git-replay.adoc: fix errors around revision range
2025-12-09 07:54:56 +09:00
Junio C Hamano
7fc0b33b5d Merge branch 'yc/xdiff-patience-optim'
The way patience diff finds LCS has been optimized.

* yc/xdiff-patience-optim:
  xdiff: optimize patience diff's LCS search
2025-12-09 07:54:55 +09:00
Junio C Hamano
fe0e6ffa19 Merge branch 'bc/zsh-testsuite'
A few tests have been updated to work under the shell compatible
mode of zsh.

* bc/zsh-testsuite:
  t5564: fix test hang under zsh's sh mode
  t0614: use numerical comparison with test_line_count
2025-12-09 07:54:54 +09:00
Junio C Hamano
c64b234a0b Merge branch 'pw/replay-exclude-gpgsig-fix'
"git replay" forgot to omit the "gpgsig-sha256" extended header
from the resulting commit the same way it omits "gpgsig", which has
been corrected.

* pw/replay-exclude-gpgsig-fix:
  replay: do not copy "gpgsign-sha256" header
2025-12-09 07:54:54 +09:00
Matthew Hughes
d4bc39a4d9 config: document 'gui.GCWarning'
While investigating the config options set by 'scalar' I noticed this
one wasn't documented.

Signed-off-by: Matthew Hughes <matthewhughes934@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-09 07:38:56 +09:00
Kristoffer Haugsbakk
41d425008a doc: send-email: fix broken list continuation
The list continuation has to be “immediately adjacent to the block
being attached”.[1]

[1]: https://web.archive.org/web/20251208172615/https://docs.asciidoctor.org/asciidoc/latest/lists/continuation/

Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-09 07:27:13 +09:00
Junio C Hamano
48176f953f connect: plug protocol capability leak
When pushing to a set of remotes using a nickname for the group, the
client initializes the connection to each remote, talks to the
remote and reads and parses capabilities line, and holds the
capabilities in a file-scope static variable server_capabilities_v1.

There are a few other such file-scope static variables, and these
connections cannot be parallelized until they are refactored to a
structure that keeps track of active connections.

Which is *not* the theme of this patch ;-)

For a single connection, the server_capabilities_v1 variable is
initialized to NULL (at the program initialization), populated when
we talk to the other side, used to look up capabilities of the other
side possibly multiple times, and the memory is held by the variable
until program exit, without leaking.  When talking to multiple remotes,
however, the server capabilities from the second connection overwrites
without freeing the one from the first connection, which leaks.

    ==1080970==ERROR: LeakSanitizer: detected memory leaks

    Direct leak of 421 byte(s) in 2 object(s) allocated from:
	#0 0x5615305f849e in strdup (/home/gitster/g/git-jch/bin/bin/git+0x2b349e) (BuildId: 54d149994c9e85374831958f694bd0aa3b8b1e26)
	#1 0x561530e76cc4 in xstrdup /home/gitster/w/build/wrapper.c:43:14
	#2 0x5615309cd7fa in process_capabilities /home/gitster/w/build/connect.c:243:27
	#3 0x5615309cd502 in get_remote_heads /home/gitster/w/build/connect.c:366:4
	#4 0x561530e2cb0b in handshake /home/gitster/w/build/transport.c:372:3
	#5 0x561530e29ed7 in get_refs_via_connect /home/gitster/w/build/transport.c:398:9
	#6 0x561530e26464 in transport_push /home/gitster/w/build/transport.c:1421:16
	#7 0x561530800bec in push_with_options /home/gitster/w/build/builtin/push.c:387:8
	#8 0x5615307ffb99 in do_push /home/gitster/w/build/builtin/push.c:442:7
	#9 0x5615307fe926 in cmd_push /home/gitster/w/build/builtin/push.c:664:7
	#10 0x56153065673f in run_builtin /home/gitster/w/build/git.c:506:11
	#11 0x56153065342f in handle_builtin /home/gitster/w/build/git.c:779:9
	#12 0x561530655b89 in run_argv /home/gitster/w/build/git.c:862:4
	#13 0x561530652cba in cmd_main /home/gitster/w/build/git.c:984:19
	#14 0x5615308dda0a in main /home/gitster/w/build/common-main.c:9:11
	#15 0x7f051651bca7 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16

    SUMMARY: AddressSanitizer: 421 byte(s) leaked in 2 allocation(s).

Free the capablities data for the previous server before overwriting
it with the next server to plug this leak.

The added test fails without the freeing with SANITIZE=leak; I
somehow couldn't get it fail reliably with SANITIZE=leak,address
though.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-09 07:11:42 +09:00
Kristoffer Haugsbakk
8cbbdc92f7 doc: join default pre-commit paragraphs
Join two paragraphs that start with the standard “The default <hook>,
when enabled” into one and put it at the end of the “pre-commit”
section.

The trailing whitespace paragraph was added in the first commit for the
doc, in 6d35cc76 (Document hooks., 2005-09-02). Then 3e14dd2c (mention
use of "hooks.allownonascii" in "man githooks", 2019-02-20) updated the
“pre-commit” section to mention the non-ASCII check that was added in
d00e364d.[1] But this paragraph was added one-past the original
“default” paragraph, after the env. variable paragraph, and starts
exactly the same. That causes the flow of this section to feel
off (paragraphs in order):

1. Invoked by <cmd> and what parameters it takes
2. The default 'pre-commit' hook catches introduction of trailing
   whitespace
3. `GIT_EDITOR=:`
4. The default pre-commit' hook catches introduction of non-ASCII
   filenames

Let’s instead join these two paragrahs and explain the whole behavior of
the default script.

† 1: Extend sample pre-commit hook to check for non ascii filenames,
     2009-05-19

Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-08 22:20:14 +09:00
Junio C Hamano
dc8a00fafe completion: clarify support for short options and arguments
The list of supported completions in the header of the file was
mostly written a long time ago when Shawn added the initial version
of this script in 2006.  The list explicitly states that we complete
"common --long-options", which implies that we do not complete
not-so-common ones and single letter options (this text dates back
to May 2007).

Update the description to explicitly state that single-letter
options are not completed.  Also, document that arguments to options
are completed, even for single-letter options (e.g., "git -c <TAB>"
offers configuration variables).

The reason why we do not complete single-letter options is because
it does not seem to help all that much to learn that the command
takes -c, -d, -e options when "git foo -<TAB>" offers these three,
unlike long options that is easier to guess what they are about.

Because this rationale is primarily for our developers, let's leave
it out of the completion script itself, whose messages are entirely
for end-users.  Our developers can run "git blame" to find this
commit as needed.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-07 10:05:49 +09:00
René Scharfe
10bba537c4 compat: remove gitmkdtemp()
gitmkdtemp() has become a trivial wrapper around git_mkdtemp().  Remove
this now unnecessary layer of indirection.

Signed-off-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-07 07:28:13 +09:00
René Scharfe
7bef658135 banned.h: ban mktemp(3)
Older versions of mktemp(3) generate easily guessable file names.  The
function checks if the generated name is used, which is unreliable, as
a file with that name might then be created by some other process before
we can do it ourselves.  The function was dropped from POSIX due to its
security problems.  Forbid its use.

Signed-off-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-07 07:28:13 +09:00
René Scharfe
47bf14750e compat: remove mingw_mktemp()
Remove the mktemp(3) compatibility function now that its last caller was
removed by the previous commit.

Signed-off-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-07 07:28:12 +09:00
René Scharfe
5ecd3590a3 compat: use git_mkdtemp()
A file might appear at the path returned by mktemp(3) before we call
mkdir(2).  Use the more robust git_mkdtemp() instead, which retries a
number of times and doesn't need to call lstat(2).

Signed-off-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-07 07:28:12 +09:00
René Scharfe
e1ecf0dd68 wrapper: add git_mkdtemp()
Extend git_mkstemps_mode() to optionally call mkdir(2) instead of
open(2), then use that ability to create a mkdtemp(3) replacement,
git_mkdtemp().  We'll start using it in the next commit.

Signed-off-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-07 07:28:11 +09:00
Junio C Hamano
bdc5341ff6 The sixth batch
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-05 14:49:59 +09:00
Junio C Hamano
644aed8921 Merge branch 'rs/config-set-multi-error-message-fix'
The error message given by "git config set", when the variable
being updated has more than one values defined, used old style "git
config" syntax with an incorrect option in its hint, both of which
have been corrected.

* rs/config-set-multi-error-message-fix:
  config: fix suggestion for failed set of multi-valued option
2025-12-05 14:49:59 +09:00
Junio C Hamano
e74a6e0cb9 Merge branch 'rs/config-unset-opthelp-fix'
The option help text given by "git config unset -h" described
the "--all" option to "replace", not "unset", multiple variables,
which has been corrected.

* rs/config-unset-opthelp-fix:
  config: fix short help of unset flags
2025-12-05 14:49:59 +09:00
Junio C Hamano
9d442ce2e2 Merge branch 'ps/object-source-management'
Code refactoring around object database sources.

* ps/object-source-management:
  odb: handle recreation of quarantine directories
  odb: handle changing a repository's commondir
  chdir-notify: add function to unregister listeners
  odb: handle initialization of sources in `odb_new()`
  http-push: stop setting up `the_repository` for each reference
  t/helper: stop setting up `the_repository` repeatedly
  builtin/index-pack: fix deferred fsck outside repos
  oidset: introduce `oidset_equal()`
  odb: move logic to disable ref updates into repo
  odb: refactor `odb_clear()` to `odb_free()`
  odb: adopt logic to close object databases
  setup: convert `set_git_dir()` to have file scope
  path: move `enter_repo()` into "setup.c"
2025-12-05 14:49:58 +09:00
Junio C Hamano
1b40ddc1a5 Merge branch 'cc/fast-import-strip-if-invalid'
"git fast-import" learns "--strip-if-invalid" option to drop
invalid cryptographic signature from objects.

* cc/fast-import-strip-if-invalid:
  fast-import: add 'strip-if-invalid' mode to --signed-commits=<mode>
  commit: refactor verify_commit_buffer()
  fast-import: refactor finalize_commit_buffer()
2025-12-05 14:49:58 +09:00
Junio C Hamano
85f99338e1 Merge branch 'js/ci-show-breakage-in-dockerized-jobs'
Dockerised jobs at the GitHub Actions CI have been taught to show
more details of failed tests.

* js/ci-show-breakage-in-dockerized-jobs:
  ci(dockerized): do show the result of failing tests again
2025-12-05 14:49:58 +09:00
Junio C Hamano
77f8d994a8 Merge branch 'kh/doc-committer-date-is-author-date'
The "--committer-date-is-author-date" option of "git am/rebase" is
a misguided one.  The documentation is updated to discourage its
use.

* kh/doc-committer-date-is-author-date:
  doc: warn against --committer-date-is-author-date
2025-12-05 14:49:57 +09:00
Junio C Hamano
0534b78576 Merge branch 'jc/optional-path'
"git config get --path" segfaulted on an ":(optional)path" that
does not exist, which has been corrected.

* jc/optional-path:
  config: really treat missing optional path as not configured
  config: really pretend missing :(optional) value is not there
  config: mark otherwise unused function as file-scope static
2025-12-05 14:49:56 +09:00
Junio C Hamano
5eadcbf815 Merge branch 'js/strip-scalar-too'
"make strip" has been taught to strip "scalar" as well as "git".

* js/strip-scalar-too:
  make strip: include `scalar`
2025-12-05 14:49:56 +09:00
Junio C Hamano
0c6707687f Merge branch 'en/xdiff-cleanup-2'
Code clean-up.

* en/xdiff-cleanup-2:
  xdiff: rename rindex -> reference_index
  xdiff: change rindex from long to size_t in xdfile_t
  xdiff: make xdfile_t.nreff a size_t instead of long
  xdiff: make xdfile_t.nrec a size_t instead of long
  xdiff: split xrecord_t.ha into line_hash and minimal_perfect_hash
  xdiff: use unambiguous types in xdl_hash_record()
  xdiff: use size_t for xrecord_t.size
  xdiff: make xrecord_t.ptr a uint8_t instead of char
  xdiff: use ptrdiff_t for dstart/dend
  doc: define unambiguous type mappings across C and Rust
2025-12-05 14:49:56 +09:00
Lucas Seiki Oshiro
76c0704bdf repo: add -z as an alias for --format=nul to git-repo-structure
Other Git commands that have nul-terminated output, such as git-config,
git-status, git-ls-files, and git-repo-info have a flag `-z` for using
the null character as the record separator.

Add the `-z` flag to git-repo-structure as an alias for `--format=nul`,
making it consistent with the behavior of the other commands.

Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-05 11:39:19 +09:00
Lucas Seiki Oshiro
768cf991ff repo: use [--format=... | -z] instead of [-z] in git-repo-info synopsis
The flag -z is only an alias for --format=null and even though --format
and -z can be used together and repeated, only the last one is
considered.

Replace `[-z]` in the synopsis of git-repo-info by
`[--format=... | -z]`, expliciting that the use of one of those flags
replace the other.

Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-05 11:39:19 +09:00
Lucas Seiki Oshiro
6fd44f55a7 repo: remove blank line from Documentation/git-repo.adoc
There was an extra blank line in git-repo-structure documentation, which
led to an unwawnted '+' character after generating an HTML or PDF from
that page. This can be seen, for example, in Git 2.52.0 online docs [1].

Remove that extra line.

[1] https://git-scm.com/docs/git-repo/2.52.0

Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-05 11:39:18 +09:00
Toon Claes
4061692ba4 meson: use is_cross_build() where possible
In previous commit the first use of meson.can_run_host_binaries() was
introduced. This is a guard around compiler.run() to ensure it's
actually possible to execute the provided.

In other places we've been having the same issue, but here `not
meson.is_cross_build()` is used as guard. This does the trick, but it
also prevents the code from running even when an exe_wrapper is
configured.

Switch to using meson.can_run_host_binaries() here as well.

There is another place left that still uses `not
meson.is_cross_build()`, but here it's a guard around fs.exists(). That
function will always run on the build machine, so checking for
cross-compilation is still in place here.

Signed-off-by: Toon Claes <toon@iotcl.com>
Acked-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-05 11:11:15 +09:00
Toon Claes
574ac61076 meson: only detect ICONV_OMITS_BOM if possible
In our Meson setup it automatically detects whether ICONV_OMITS_BOM
should be defined. To check this, a piece of code is compiled and ran.

When cross-compiling, it's not possible to run this piece of code. Guard
this test with a can_run_host_binaries() check to ensure it can run.

Signed-off-by: Toon Claes <toon@iotcl.com>
Acked-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-05 11:11:11 +09:00
Toon Claes
9ce3478410 meson: ignore subprojects/.wraplock
When asking Meson to wrap subprojects, it generates a .wraplock file in
the subprojects/ directory. Ignore this file.

See also https://github.com/mesonbuild/meson/issues/14948.

Signed-off-by: Toon Claes <toon@iotcl.com>
Acked-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-05 11:11:00 +09:00
Johannes Schindelin
05491b90ce last-modified: support sparse checkouts
In a sparse checkout, a user might want to run `last-modified` on a
directory outside the worktree.

And even in non-sparse checkouts, a user might need to run that command
on a directory that does not exist in the worktree.

These use cases should be supported via the `--` separator between
revision and file arguments, which is even advertised in the
documentation. This patch fixes a tiny bug that prevents that from
working.

This fixes https://github.com/git-for-windows/git/issues/5978

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Acked-by: Derrick Stolee <stolee@gmail.com>
Acked-by: Toon Claes <toon@iotcl.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-03 14:20:18 -08:00
Julia Evans
8ef7355a8f doc: git-pull: fix 'git --rebase abort' typo
An earlier commit e9d221b0 (doc: git-pull: clarify how to exit a
conflicted merge, 2025-10-15) misspelt `git rebase --abort` to
`git --rebase abort`.  Fix it.

Signed-off-by: Julia Evans <julia@jvns.ca>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-03 13:51:29 -08:00
Julia Evans
cfdce4afcc doc: remove stray text in Git data model
I meant to delete this sentence fragment when rewriting this paragraph,
but accidentally left it in. It's repetitive (since it was meant to be
deleted) and it's causing some formatting issues with the note.

Signed-off-by: Julia Evans <julia@jvns.ca>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-03 00:17:07 -08:00
Kristoffer Haugsbakk
b14f1df9f2 branch: advice using git-help(1) instead of man(1)
8fbd903e (branch: advise about ref syntax rules, 2024-03-05) added
an advice about checking git-check-ref-format(1) for the ref syntax
rules. The advice uses man(1). But git(1) is a multi-platform tool and
man(1) may not be available on some platforms. It might also be slightly
jarring to see a suggestion for running a command which is not from
the Git suite.

Let’s instead use git-help(1) in order to stay inside the land of
git(1). This also means that `help.format` (for `man`, `html` or other
formats) will be used if set.

Also change to using single quotes (') to quote the command since that
is more conventional.

While here let’s also update the test to use `{SQ}`, which is more
readable and easier to edit.

Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-03 00:16:05 -08:00
Junio C Hamano
f0ef5b6d9b The fifth batch
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-30 18:31:41 -08:00
Junio C Hamano
aea8cc3a10 Merge branch 'jk/asan-bonanza'
Various issues detected by Asan have been corrected.

* jk/asan-bonanza:
  t: enable ASan's strict_string_checks option
  fsck: avoid parse_timestamp() on buffer that isn't NUL-terminated
  fsck: remove redundant date timestamp check
  fsck: avoid strcspn() in fsck_ident()
  fsck: assert newline presence in fsck_ident()
  cache-tree: avoid strtol() on non-string buffer
  Makefile: turn on NO_MMAP when building with ASan
  pack-bitmap: handle name-hash lookups in incremental bitmaps
  compat/mmap: mark unused argument in git_munmap()
2025-11-30 18:31:41 -08:00
Junio C Hamano
6912d80f55 Merge branch 'je/doc-data-model'
Add a new manual that describes the data model.

* je/doc-data-model:
  doc: add an explanation of Git's data model
2025-11-30 18:31:40 -08:00
Junio C Hamano
3b212a83fe Merge branch 'jc/whitespace-incomplete-line'
Both "git apply" and "git diff" learn a new whitespace error class,
"incomplete-line".

* jc/whitespace-incomplete-line:
  attr: enable incomplete-line whitespace error for this project
  diff: highlight and error out on incomplete lines
  apply: check and fix incomplete lines
  whitespace: allocate a few more bits and define WS_INCOMPLETE_LINE
  apply: revamp the parsing of incomplete lines
  diff: update the way rewrite diff handles incomplete lines
  diff: call emit_callback ecbdata everywhere
  diff: refactor output of incomplete line
  diff: keep track of the type of the last line seen
  diff: correct suppress_blank_empty hack
  diff: emit_line_ws_markup() if/else style fix
  whitespace: correct bit assignment comments
2025-11-30 18:31:40 -08:00
Junio C Hamano
ffd9bb1bc7 Merge branch 'ja/doc-synopsis-style'
Doc mark-up updates.

* ja/doc-synopsis-style:
  doc: pull-fetch-param typofix
  doc: convert git push to synopsis style
  doc: convert git pull to synopsis style
  doc: convert git fetch to synopsis style
2025-11-30 18:31:39 -08:00
Junio C Hamano
0fec747d59 Merge branch 'lo/repo-info-all'
"git repo info" learned "--all" option.

* lo/repo-info-all:
  repo: add --all to git-repo-info
  repo: factor out field printing to dedicated function
2025-11-30 18:31:39 -08:00
René Scharfe
38f88051da diff-index: don't queue unchanged filepairs with diff_change()
diff_cache() queues unchanged filepairs if the flag find_copies_harder
is set, and uses diff_change() for that.  This function allocates a
filespec for each side, does a few other things that are unnecessary for
unchanged filepairs and always sets the diff_flag has_changes, which is
simply misleading in this case.

Add a new streamlined function for queuing unchanged filepairs and
use it in show_modified(), which is called by diff_cache() via
oneway_diff() and do_oneway_diff().  It allocates only a single filespec
for each filepair and uses it twice with reference counting.  This has a
measurable effect if there are a lot of them, like in the Linux repo:

Benchmark 1: ./git_v2.52.0 -C ../linux diff --cached --find-copies-harder
  Time (mean ± σ):      31.8 ms ±   0.2 ms    [User: 24.2 ms, System: 6.3 ms]
  Range (min … max):    31.5 ms …  32.3 ms    85 runs

Benchmark 2: ./git -C ../linux diff --cached --find-copies-harder
  Time (mean ± σ):      23.9 ms ±   0.2 ms    [User: 18.1 ms, System: 4.6 ms]
  Range (min … max):    23.5 ms …  24.4 ms    111 runs

Summary
  ./git -C ../linux diff --cached --find-copies-harder ran
    1.33 ± 0.01 times faster than ./git_v2.52.0 -C ../linux diff --cached --find-copies-harder

Signed-off-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-30 09:58:53 -08:00
Toon Claes
fe4e60759b last-modified: fix use of uninitialized memory
git-last-modified(1) uses a scratch bitmap to keep track of paths that
have been changed between commits. To avoid reallocating a bitmap on
each call of process_parent(), the scratch bitmap is kept and reused.
Although, it seems an incorrect length is passed to memset(3).

`struct bitmap` uses `eword_t` to for internal storage. This type is
typedef'd to uint64_t. To fully zero the memory used by the bitmap,
multiply the length (saved in `struct bitmap::word_alloc`) by the size
of `eword_t`.

Reported-by: Anders Kaseorg <andersk@mit.edu>
Helped-by: Jeff King <peff@peff.net>
Signed-off-by: Toon Claes <toon@iotcl.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-29 14:16:53 -08:00
Elijah Newren
136f86abc0 Documentation/git-replay.adoc: fix errors around revision range
There was significant confusion in the git-replay manual about what
constitutes a revision range.  As noted in f302c1e4aa09 (revisions(7):
clarify that most commands take a single revision range, 2021-05-18):

   Commands that are specifically designed to take two distinct ranges
   (e.g. "git range-diff R1 R2" to compare two ranges) do exist, but they
   are exceptions. Unless otherwise noted, all "git" commands that operate
   on a set of commits work on a single revision range.

`git replay` is not an exception, but a few places in the manual were
written as though it were.  These appear to have come in revisions to
the original series, between v3->v4 (see
https://lore.kernel.org/git/CAP8UFD3bpLrVW97DH7j=V9H2GsTSAkksC9L3QujQERFk_kLnZA@mail.gmail.com/
, "More than one <revision-range> can be passed") and between v6->v7
(https://lore.kernel.org/git/20231115143327.2441397-1-christian.couder@gmail.com/,
"Takes ranges of commits"), and I missed both of these revisions when
reviewing.  Fix them now.

There was also a reference to the "Commit Limiting options below", but
this page has no such section of options; strike the misleading
reference.

It is worth noting that we are documenting existing behavior, rather
than optimal behavior.  Junio has multiple times suggested introducing
alternative ways to walk revisions and use them in `git replay
--advance`, e.g. at
  * https://lore.kernel.org/git/xmqqy1mqo6kv.fsf@gitster.g/
  * https://lore.kernel.org/git/xmqq8rb3is8c.fsf@gitster.g/
  * https://lore.kernel.org/git/xmqqtsydj2zk.fsf@gitster.g/ (item (2))
If/when we introduce some new revision walking flag that implements one
of these alternate types of revision walks, we can update the --advance
option and this manual appropriately.

Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-28 23:06:46 -08:00
Yee Cheng Chin
c7e3b8085b xdiff: optimize patience diff's LCS search
The find_longest_common_sequence() function in patience diff is
inefficient as it calls binary_search() for every unique line it
encounters when deciding where to put it in the sequence. From
instrumentation (using xctrace) on popular repositories, binary_search()
takes up 50-60% of the run time within patience_diff() when performing a
diff.

To optimize this, add a boundary condition check before binary_search()
is called to see if the encountered unique line is located after the
entire currently tracked longest subsequence. If so, skip the
unnecessary binary search and simply append the entry to the end of
sequence. Given that most files compared in a diff are usually quite
similar to each other, this condition is very common, and should be hit
much more frequently than the binary search.

Below are some end-to-end performance results by timing `git log
--shortstat --oneline -500 --patience` on different repositories with
the old and new code. Generally speaking this seems to give at least
8-10% speed up. The "binary search hit %" column describes how often the
algorithm enters the binary search path instead of the new faster path.
Even in the WebKit case we can see that it's quite rare (1.46%).

| Repo     | Speed difference | binary search hit % |
|----------|------------------|---------------------|
| vim      | 1.27x            | 0.01%               |
| pytorch  | 1.16x            | 0.02%               |
| cpython  | 1.14x            | 0.06%               |
| ripgrep  | 1.14x            | 0.03%               |
| git      | 1.13x            | 0.12%               |
| vscode   | 1.09x            | 0.10%               |
| WebKit   | 1.08x            | 1.46%               |

The benchmarks were done using hyperfine, on an Apple M1 Max laptop,
with git compiled with `-O3 -flto`.

Signed-off-by: Yee Cheng Chin <ychin.git@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-27 19:11:41 -08:00
brian m. carlson
a92f243a94 t5564: fix test hang under zsh's sh mode
This test starts a SOCKS server in Perl in the background and then kills
it after the tests are done.  However, when using zsh (in sh mode) in
the tests, the start_socks function hangs until the background process
is killed.

Note that this does not reproduce in a simple shell script, so there is
likely some interaction between job handling, our heavy use of eval in
the test framework, and possibly other complexities of our test
framework.  What is clear, however, is that switching from a compound
statement to a subshell fixes the problem entirely and the test passes
with no problem, so do that.

Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-27 19:06:03 -08:00
brian m. carlson
bf25fca31c t0614: use numerical comparison with test_line_count
In this comparison, we want to know whether the number of lines is
greater than 1.  Our test_line_count function passes the first argument
as the comparison operator to test, so what we want is a numerical
comparison, not a string comparison.  While this does not produce a
functional problem now, it could very well if we expected two or more
items, in which case the value "10" would not match when it should.

Furthermore, the "<" and ">" comparisons are new in POSIX 1003.1-2024
and we don't want to require such a new version of POSIX since many
popular and supported operating systems were released before that
version of POSIX was released.

Finally, zsh's builtin test operator does not like the greater-than sign
in "test", since it is only supported in the double-bracket extension.
This has been reported and will be addressed in a future version, but
since our code is also technically incorrect, as well as not very
compatible, let's fix it by using a numeric comparison.

Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-27 19:06:01 -08:00
Junio C Hamano
b31ab939fe The fourth batch
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-26 10:32:43 -08:00
Junio C Hamano
54af646904 Merge branch 'gf/win32-pthread-cond-wait-err'
Emulation code clean-up.

* gf/win32-pthread-cond-wait-err:
  win32: return error if SleepConditionVariableCS fails
2025-11-26 10:32:43 -08:00
Junio C Hamano
536d284f3b Merge branch 'jk/ci-windows-meson-test-fix'
"Windows+meson" job at the GitHub Actions CI was hard to debug, as
it did not show and save failed test artifacts, which has been
corrected.

* jk/ci-windows-meson-test-fix:
  ci(windows-meson-test): handle options and output like other test jobs
  unit-test: ignore --no-chain-lint
2025-11-26 10:32:43 -08:00
Junio C Hamano
d65eab5d30 Merge branch 'pw/worktree-list-display-width-fix'
"git worktree list" attempts to show paths to worktrees while
aligning them, but miscounted display columns for the paths when
non-ASCII characters were involved, which has been corrected.

* pw/worktree-list-display-width-fix:
  worktree list: quote paths
  worktree list: fix column spacing
2025-11-26 10:32:42 -08:00
Junio C Hamano
e539545396 Merge branch 'js/wincred-get-credential-alloc-fix'
Under-allocation fix.

* js/wincred-get-credential-alloc-fix:
  wincred: avoid memory corruption
2025-11-26 10:32:42 -08:00
Junio C Hamano
35eaf96add Merge branch 'js/cmake-libgit-fix'
Makefile based build have recently been updated to build a
libgit.a that also has reftable and xdiff objects; CMake based
build procedure has been updated to match.

* js/cmake-libgit-fix:
  cmake: stop trying to build the reftable and xdiff libraries
2025-11-26 10:32:42 -08:00
Junio C Hamano
eb474aa7e6 Merge branch 'js/mingw-assign-comma-fix'
The "return errno = EFOO, -1" construct, which is heavily used in
compat/mingw.c and triggers warnings under "-Wcomma", has been
rewritten to avoid the warnings.

* js/mingw-assign-comma-fix:
  mingw: avoid the comma operator
2025-11-26 10:32:41 -08:00
Junio C Hamano
fa40522717 Merge branch 'js/ci-github-setup-go-update'
Update a version of action used at the GitHub Actrions CI.

* js/ci-github-setup-go-update:
  ci: bump actions/setup-go from 5 to 6
2025-11-26 10:32:41 -08:00
Junio C Hamano
24ddb3f1fc Merge branch 'jk/test-mktemp-leakfix'
Test leakfix.

* jk/test-mktemp-leakfix:
  test-mktemp: plug memory and descriptor leaks
2025-11-26 10:32:41 -08:00
Junio C Hamano
370470e240 Merge branch 'rs/xmkstemp-simplify'
Code simplification.

* rs/xmkstemp-simplify:
  wrapper: simplify xmkstemp()
2025-11-26 10:32:40 -08:00
Junio C Hamano
1b93acd13a Merge branch 'ad/blame-diff-algorithm'
"git blame" learns "--diff-algorithm=<algo>" option.

* ad/blame-diff-algorithm:
  blame: make diff algorithm configurable
  xdiff: add 'minimal' to XDF_DIFF_ALGORITHM_MASK
2025-11-26 10:32:40 -08:00
Junio C Hamano
716e871d50 Merge branch 'en/ort-rename-another-fix'
Yet another corner case fix around renames in the "ort" merge
strategy.

* en/ort-rename-another-fix:
  merge-ort: fix failing merges in special corner case
  merge-ort: remove debugging crud
  t6429: update comment to mention correct tool
2025-11-26 10:32:40 -08:00
Johannes Schindelin
0458e8b854 ci(dockerized): do show the result of failing tests again
The quality of tests and test suites is most apparent not when
everything passes, but in how quickly bugs can be identified,
analyzed, and resolved after test failures occur.

As such, it is an unfortunate side effect of 2a21098b98a (github: adapt
containerized jobs to be rootless, 2025-01-10) that the output of failed
test cases, which was shown before that change directly in the build
logs, is now no longer shown at all.

The reason is a side effect of trying to run the build and the tests
with permissions other than the `root` user, but without providing the
prerequisite permissions to signal what tests failed and whose output
hence needs to be included in the logs.

The way this signaling works is for the workflow to write into
special-purpose files whose path is specific to the current workflow
step and which can be accessed via the `$GITHUB_ENV` environment
variable, which differs between workflow steps. It is this file that is
missing write permission for the `builder` user that was introduced in
above-mentioned commit.

The solution is simple: make the file world-writable.

Technically, this write permission should be removed after the step has
completed, if proper security practices were to be upheld, but since
nothing uses that file again, it does not matter, and the fix is more
succinct this way.

This commit is best viewed with `--color-words`.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
[jc: squashed Elijah's rewrite of the first paragraph of the log message]
[jc: updated chmod to match "world-writable" in the log message]
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-26 10:17:44 -08:00
Junio C Hamano
42bf8a534b Merge branch 'master' of https://github.com/j6t/gitk
* 'master' of https://github.com/j6t/gitk:
  gitk: add external diff file rename detection
  gitk: show unescaped file names on 'rename' and 'copy' lines
  gitk: fix a 'continue' statement outside a loop to 'return'
  gitk: persist position and size of the Tags and Heads window
  Revert "gitk: Only restore window size from ~/.gitk, not position"
2025-11-26 09:35:09 -08:00
Phillip Wood
9f3a115087 replay: do not copy "gpgsign-sha256" header
When "git replay" replays a commit it copies the extended headers
across from the original commit. However, if the original commit
was signed, we do not want to copy the header associated with the
signature is it wont be valid for the new commit. The code already
knows to avoid coping the "gpgsig" header but does not know to avoid
copying the "gpgsig-sha256" header.  Add that header to the list of
exclusions to match what "git commit --amend" does.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-26 09:33:52 -08:00
Christian Couder
c20f112e51 fast-import: add 'strip-if-invalid' mode to --signed-commits=<mode>
Tools like `git filter-repo`[1] use `git fast-export` and
`git fast-import` to rewrite repository history. When rewriting
history using one such tool though, commit signatures might become
invalid because the commits they sign changed due to the changes
in the repository history made by the tool between the fast-export
and the fast-import steps.

Note that as far as signature handling goes:

  * Since fast-export doesn't know what changes filter-repo may make
to the stream, it can't know whether the signatures will still be
valid.

  * Since filter-repo doesn't know what history canonicalizations
fast-export performed (and it performs a few), it can't know whether
the signatures will still be valid.

  * Therefore, fast-import is the only process in the pipeline that
can know whether a specified signature remains valid.

Having invalid signatures in a rewritten repository could be
confusing, so users rewritting history might prefer to simply
discard signatures that are invalid at the fast-import step.

For example a common use case is to rewrite only "recent" history.
While specifying commit ranges corresponding to "recent" commits
could work, users worry about getting it wrong and want to just
automatically rewrite everything, expecting older commit signatures
to be untouched.

To let them do that, let's add a new 'strip-if-invalid' mode to the
`--signed-commits=<mode>` option of `git fast-import`.

It would be interesting for the `--signed-tags=<mode>` option to
have this mode too, but we leave that for a future improvement.

It might also be possible for `git fast-export` to have such a mode
in its `--signed-commits=<mode>` and `--signed-tags=<mode>`
options, but the use cases for it are much less clear, so we also
leave that for possible future improvements.

For now let's just die() if 'strip-if-invalid' is passed to these
options where it hasn't been implemented yet.

[1]: https://github.com/newren/git-filter-repo

Helped-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-26 08:43:44 -08:00
Johannes Sixt
776223c4d8 Merge branch 'tb/external-diff-renamed'
* tb/external-diff-renamed:
  gitk: add external diff file rename detection
2025-11-26 16:04:14 +01:00
Johannes Sixt
bd3fd7e77c Merge branch 'js/persist-ref-window-geometry'
* js/persist-ref-window-geometry:
  gitk: persist position and size of the Tags and Heads window
  Revert "gitk: Only restore window size from ~/.gitk, not position"
2025-11-26 16:02:23 +01:00
Patrick Steinhardt
ac65c70663 odb: handle recreation of quarantine directories
In the preceding commit we have moved the logic that reparents object
database sources on chdir(3p) from "setup.c" into "odb.c". Let's also do
the same for any temporary quarantine directories so that the complete
reparenting logic is self-contained in "odb.c".

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-25 12:16:00 -08:00
Patrick Steinhardt
2816b748e5 odb: handle changing a repository's commondir
The function `repo_set_gitdir()` is called in two situations:

  - To initialize the repository with its discovered location. As part
    of this we also set up the new object database.

  - To update the repository's discovered location in case the process
    changes its working directory so that we update relative paths. This
    means we also have to update any relative paths that are potentially
    used in the object database.

In the context of the object database we ideally wouldn't ever have to
worry about the second case: if all paths used by our object database
sources were absolute, then we wouldn't have to update them. But
unfortunately, the paths aren't only used to locate files owned by the
given source, but we also use them for reporting purposes. One such
example is `repo_get_object_directory()`, where we cannot just change
semantics to always return absolute paths, as that is likely to break
tooling out there.

One solution to this would be to have both a "display path" and an
"internal path". This would allow us to use internal paths for all
internal matters, but continue to use the potentially-relative display
paths so that we don't break compatibility. But converting the codebase
to honor this split is quite a messy endeavour, and it wouldn't even
help us with the goal to get rid of the need to update the display path
on chdir(3p).

Another solution would be to rework "setup.c" so that we never have to
update paths in the first place. In that case, we'd only initialize the
repository once we have figured out final locations for all directories.
This would be a significant simplification of that subsystem indeed, but
the current logic is so messy that it would take significant investments
to get there.

Meanwhile though, while object sources may still use relative paths, the
best thing we can do is to handle the reparenting of the object source
paths in the object database itself. This can be done by registering one
callback for each object database so that we get notified whenever the
current working directory changes, and we then perform the reparenting
ourselves.

Ideally, this wouldn't even happen on the object database level, but
instead handled by each object database source. But we don't yet have
proper pluggable object database sources, so this will need to be
handled at a later point in time.

The logic itself is rather simple:

  - We register the callback when creating the object database.

  - We unregister the callback when releasing it again.

  - We split up `set_git_dir_1()` so that it becomes possible to skip
    recreating the object database. This is required because the
    function is called both when the current working directory changes,
    but also when we set up the repository. Calling this function
    without skipping creation of the ODB will result in a bug in case
    it's already created.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-25 12:16:00 -08:00
Patrick Steinhardt
2574c61736 chdir-notify: add function to unregister listeners
While we (obviously) have a way to register new listeners that get
called whenever we chdir(3p), we don't have an equivalent that can be
used to unregister such a listener again.

Add one, as it will be required in a subsequent commit.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-25 12:16:00 -08:00
Patrick Steinhardt
35d9fc65ed odb: handle initialization of sources in odb_new()
The logic to set up a new object database is currently distributed
across two functions in "repository.c":

  - In `initialize_repository()` we initialize an empty object database.
    This object database is not fully initialized and doesn't have any
    sources attached to it.

  - The primary object database source is then created in
    `repo_set_gitdir()`.

Ideally though, the logic should be entirely self-contained so that we
can iterate more readily on how exactly the sources themselves get set
up.

Refactor `odb_new()` to handle both allocation and setup of the object
database. This ensures that the object database is always initialized
and ready for use, and it allows us to change how the sources get set up
eventually.

Note that `repo_set_gitdir()` still reaches into the sources when the
function gets called with an already-initialized object database. This
will be fixed in the next commit.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-25 12:16:00 -08:00
Patrick Steinhardt
c257bd5916 http-push: stop setting up the_repository for each reference
When pushing references via HTTP we call `repo_init_revisions()` in a
loop for each reference that we're about to push. As third argument we
pass the result of `setup_git_directory()`, which causes us to
reinitialize the repository every single time.

This is an obvious waste of compute, as the repository that we're
working in will never change across any of the initializations. The only
reason that we do this is to retrieve the directory of the repository.
Furthermore, this is about to create issues in a subsequent commit,
where reinitializing the repository will cause a `BUG()`.

Address this by storing the Git directory in a variable instead so that
we don't have to call the function repeatedly.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-25 12:16:00 -08:00
Patrick Steinhardt
eea83c010c t/helper: stop setting up the_repository repeatedly
The "repository" test helper sets up `the_repository` twice. In fact
though, we don't even have to set it up even once: all we need is to set
up its hash algorithm, because we still depend on some subsystems that
aren't free of `the_repository`.

Refactor the code accordingly. This prepares for a subsequent change,
where setting up the repository repeatedly will lead to a `BUG()`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-25 12:16:00 -08:00
Patrick Steinhardt
8dc22e87f0 builtin/index-pack: fix deferred fsck outside repos
When asked to perform object consistency checks via the `--fsck-objects`
flag we verify that each object part of the pack is valid. In general,
this check can even be performed outside of a Git repository: we don't
need an initialized object database as we simply read the object from
the packfile directly.

But there's one exception: a subset of the object checks may be deferred
to a later point in time. For now, this only concerns ".gitmodules" and
".gitattributes" files: whenever we see a tree referencing these files
we queue them for a deferred check. This is done because we need to do
some extra checks for those files to ensure that they are well-formed,
and these checks need to be done regardless of whether the corresponding
blobs are part of the packfile or not.

This works inside a repository, but unfortunately the logic leads to a
segfault when running outside of one. This is because we eventually call
`odb_read_object()`, which will crash because the object database has
not been initialized.

There's multiple options here:

  - We could in theory create a purely in-memory database with only a
    packfile store that contains the single packfile. We don't really
    have the infrastructure for this yet though, and it would end up
    being quite hacky.

  - We could refuse to perform consistency checks outside of a
    repository. But most of the checks work alright, so this would be a
    regression.

  - We can skip the finalizing consistency checks when running outside
    of a repository. This is not as invasive as skipping all checks,
    but it's not great to randomly skip a subset of tests, either.

None of these options really feel perfect. The first one would be the
obvious choice if easily possible.

There's another option though: instead of skipping the final object
checks, we can die if there are any queued object checks. With this
change we now die exactly if and only if we would have previously
segfaulted. Like this we ensure that objects that _may_ fail the
consistency checks won't be silently skipped, and at the same time we
give users a much better error message.

Refactor the code accordingly and add a test that would have triggered
the segfault. Note that we also move down the logic to add the packfile
to the store. There is no point doing this any earlier than right before
we execute `fsck_finish()`, and it ensures that the logic to set up and
perform the consistency check is self-contained.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-25 12:15:59 -08:00
Patrick Steinhardt
5d795b34dc oidset: introduce oidset_equal()
Introduce a new function that allows the caller to verify whether two
oidsets contain the exact same object IDs.

Note that this change requires us to change `oidset_iter_init()` to
accept a `const struct oidset`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-25 12:15:59 -08:00
Patrick Steinhardt
b67b2d9fb7 odb: move logic to disable ref updates into repo
Our object database sources have a field `disable_ref_updates`. This
field can obviously be set to disable reference updates, but it is
somewhat curious that this logic is hosted by the object database.

The reason for this is that it was primarily added to keep us from
accidentally updating references while an ODB transaction is ongoing.
Any objects part of the transaction have not yet been committed to disk,
so new references that point to them might get corrupted in case we
never end up committing the transaction. As such, whenever we create a
new transaction we set up a new temporary ODB source and mark it as
disabling reference updates.

This has one (and only one?) upside: once we have committed the
transaction, the temporary source will be dropped and thus we clean up
the disabled reference updates automatically. But other than that, it's
somewhat misdesigned:

  - We can have multiple ODB sources, but only the currently active
    source inhibits reference updates.

  - We're mixing concerns of the refdb with the ODB.

Arguably, the decision of whether we can update references or not should
be handled by the refdb. But that wouldn't be a great fit either, as
there can be one refdb per worktree. So we'd again have the same problem
that a "global" intent becomes localized to a specific instance.

Instead, move the setting into the repository. While at it, convert it
into a boolean.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-25 12:15:59 -08:00
Junio C Hamano
dd8e8c786e submodule add: sanity check existing .gitmodules
"git submodule add" tries to find if a submodule with the same name
already exists at a different path, by looking up an entry in the
.gitmodules file.  If the entry in the file is incomplete, e.g.,
when the submodule.<name>.something variable is defined but there is
no definition of submodule.<name>.path variable, it accesses the
missing .path member of the submodule structure and triggers a
segfault.

A brief audit was done to make sure that the code does not assume
members other than those that are absolutely certain to exist: a
submodule obtained by submodule_from_name() should have .name
member, while a submodule obtained by submodule_from_path() should
also have .path as well as .name member, and we cannot assume
anything else.  Luckily, the module_add() codepath was the only
problematic one.  It is fairly recent code that comes from 1fa06ced
(submodule: prevent overwriting .gitmodules on path reuse,
2025-07-24).

A helper used by update_submodule() seems to assume that its call to
submodule_from_path() always yields a submodule object without a
failure, which seems to rely on the caller making sure it is the
case.  Leave an assert() with a NEEDSWORK comment there for future
developers to make sure the assumption actually holds.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-25 08:43:20 -08:00
Junio C Hamano
0bd16856ff config: really treat missing optional path as not configured
These callers expect that git_config_pathname() that returns 0 is a
signal that the variable they passed has a string they need to act
on.  But with the introduction of ":(optional)path" earlier, that is
no longer the case.  If the path specified by the configuration
variable is missing, their variable will get a NULL in it, and they
need to act on it (often, just refraining from copying it elsewhere).

Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-24 17:00:47 -08:00
Junio C Hamano
ce1a5a22a5 config: really pretend missing :(optional) value is not there
Earlier we added support for a value spelled as ":(optional)path"
for configuration variables whose values are of type "path", with
the documented semantics "if the path is missing, behave as if such
a variable definition is not even there."

This has worked OK for code paths that reads configuration files and
stores the configured value as a string, where NULL in such a string
is treated as if the setting is not there, left as the default.

However, there are other code paths that do not _ignore_ such NULL
values and misbehave.  "git config get --path" is one of them.

When git_config_pathname() helper function finds that the value of
the variable is an optional path *and* the path is missing, it
leaves the destination pointer intact (which usually is left to
NULL) and returns 0 to signal a success.  format_config() helper
however assumed that the destination pointer always gets a string,
which no longer is the case, and segfaulted.

Make sure that git_config_pathname() clears the destination pointer
in such a case, and teach format_config() to react to the condition
by returning 1 (which is different from 0 that is a normal success
and negative that is an error) to its callers.  Adjust the callers
to react to this new return value that tells them to pretend as if
they did not even see this partcular <key, value> pair.

Reported-by: Han Jiang <jhcarl0814@gmail.com>
Helped-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-24 17:00:47 -08:00
Junio C Hamano
6ab38b7e9c The third batch
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-24 15:46:41 -08:00
Junio C Hamano
a5d5c50160 Merge branch 'jx/repo-struct-utf8width-fix'
The "git repo structure" subcommand tried to align its output but
mixed up byte count and display column width, which has been
corrected.

* jx/repo-struct-utf8width-fix:
  builtin/repo: fix table alignment for UTF-8 characters
  t/unit-tests: add UTF-8 width tests for CJK chars
2025-11-24 15:46:41 -08:00
Junio C Hamano
861312b51d Merge branch 'kn/osxkeychain-idempotent-store-fix'
An earlier check added to osx keychain credential helper to avoid
storing the credential itself supplied was overeager and rejected
credential material supplied by other helper backends that it would
have wanted to store, which has been corrected.

* kn/osxkeychain-idempotent-store-fix:
  osxkeychain: avoid incorrectly skipping store operation
2025-11-24 15:46:41 -08:00
Junio C Hamano
aa934e0950 Merge branch 'kh/doc-commit-extra-references'
Doc update.

* kh/doc-commit-extra-references:
  doc: commit: link to git-status(1) on all format options
2025-11-24 15:46:41 -08:00
Junio C Hamano
a545103244 Merge branch 'ps/object-source-loose'
A part of code paths that deals with loose objects has been cleaned
up.

* ps/object-source-loose:
  object-file: refactor writing objects via a stream
  object-file: rename `write_object_file()`
  object-file: refactor freshening of objects
  object-file: rename `has_loose_object()`
  object-file: read objects via the loose object source
  object-file: move loose object map into loose source
  object-file: hide internals when we need to reprepare loose sources
  object-file: move loose object cache into loose source
  object-file: introduce `struct odb_source_loose`
  object-file: move `fetch_if_missing`
  odb: adjust naming to free object sources
  odb: introduce `odb_source_new()`
  odb: fix subtle logic to check whether an alternate is usable
2025-11-24 15:46:41 -08:00
Junio C Hamano
05ce3ab2c6 Merge branch 'qj/doc-http-bad-want-response'
Doc update.

* qj/doc-http-bad-want-response:
  doc: clarify server behavior for invalid 'want' lines in HTTP protocol
2025-11-24 15:46:40 -08:00
Junio C Hamano
9370a6be79 Merge branch 'sa/replay-atomic-ref-updates'
"git replay" (experimental) learned to perform ref updates itself
in a transaction by default, instead of emitting where each refs
should point at and leaving the actual update to another command.

* sa/replay-atomic-ref-updates:
  replay: add replay.refAction config option
  replay: make atomic ref updates the default behavior
  replay: use die_for_incompatible_opt2() for option validation
2025-11-24 15:46:40 -08:00
Junio C Hamano
d91d79f26d Merge branch 'bc/submodule-force-same-hash'
Adding a repository that uses a different hash function is a no-no,
but "git submodule add" did nt prevent it, which has been corrected.

* bc/submodule-force-same-hash:
  read-cache: drop submodule check from add_to_cache()
  object-file: disallow adding submodules of different hash algo
2025-11-24 15:46:40 -08:00
Junio C Hamano
54f7817456 Merge branch 'jk/attr-macroexpand-wo-recursion'
The code to expand attribute macros has been rewritten to avoid
recursion to avoid running out of stack space in an uncontrolled
way.

* jk/attr-macroexpand-wo-recursion:
  attr: avoid recursion when expanding attribute macros
2025-11-24 15:46:39 -08:00
René Scharfe
18bf67b753 config: fix short help of unset flags
The flags --all and --value of "git config unset" don't make the command
"replace" or "show" anything, they are about selecting what to unset.
Change their help text accordingly.

Signed-off-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-24 15:00:46 -08:00
René Scharfe
df963f0df4 config: fix suggestion for failed set of multi-valued option
The command "git config set <name> <value>" fails for an option that has
multiple values.  List the "git config set" flags that can be used,
instead of old-style "git config" actions.

Reported-by: Paul Wintz <pwintz@ucsc.edu>
Signed-off-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-24 14:59:02 -08:00
Jean-Noël Avila via GitGitGadget
fddba8f737 doc: pull-fetch-param typofix
An earier patch had a typo discovered after it has been merged to
'next'.  Fix it.

Signed-off-by: Jean-Noël Avila <jn.avila@free.fr>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-24 10:55:48 -08:00
Patrick Steinhardt
7b94028652 streaming: drop redundant type and size pointers
In the preceding commits we have turned `struct odb_read_stream` into a
publicly visible structure. Furthermore, this structure now contains the
type and size of the object that we are about to stream. Consequently,
the out-pointers that we used before to propagate the type and size of
the streamed object are now somewhat redundant with the data contained
in the structure itself.

Drop these out-pointers and adapt callers accordingly.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-23 12:56:46 -08:00
Patrick Steinhardt
1599b68d5e streaming: move into object database subsystem
The "streaming" terminology is somewhat generic, so it may not be
immediately obvious that "streaming.{c,h}" is specific to the object
database. Rectify this by moving it into the "odb/" directory so that it
can be immediately attributed to the object subsystem.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-23 12:56:46 -08:00
Patrick Steinhardt
378ec56beb streaming: refactor interface to be object-database-centric
Refactor the streaming interface to be centered around object databases
instead of centered around the repository. Rename the functions
accordingly.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-23 12:56:45 -08:00
Patrick Steinhardt
8c1b84bc97 streaming: move logic to read packed objects streams into backend
Move the logic to read packed object streams into the respective
subsystem.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-23 12:56:45 -08:00
Patrick Steinhardt
bc30a2f5df streaming: move logic to read loose objects streams into backend
Move the logic to read loose object streams into the respective
subsystem. This allows us to make a couple of function declarations
private.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-23 12:56:45 -08:00
Patrick Steinhardt
ffc9a34485 streaming: make the odb_read_stream definition public
Subsequent commits will move the backend-specific logic of setting up an
object read stream into the specific subsystems. As the backends are now
the ones that are responsible for allocating the stream they'll need to
have the stream definition available to them.

Make the stream definition public to prepare for this.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-23 12:56:45 -08:00
Patrick Steinhardt
c26da3446e streaming: get rid of the_repository
Subsequent commits will move the backend-specific logic of object
streaming into their respective subsystems. These subsystems have gotten
rid of `the_repository` already, but we still use it in two locations in
the streaming subsystem.

Prepare for the move by fixing those two cases. Converting the logic in
`open_istream_pack_non_delta()` is trivial as we already got the object
database as input.

But for `stream_blob_to_fd()` we have to add a new parameter to make it
accessible. So, as we already have to adjust all callers anyway, rename
the function to `odb_stream_blob_to_fd()` to indicate it's part of the
object subsystem.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-23 12:56:45 -08:00
Patrick Steinhardt
4c89d31494 streaming: rely on object sources to create object stream
When creating an object stream we first look up the object info and, if
it's present, we call into the respective backend that contains the
object to create a new stream for it.

This has the consequence that, for loose object source, we basically
iterate through the object sources twice: we first discover that the
file exists as a loose object in the first place by iterating through
all sources. And, once we have discovered it, we again walk through all
sources to try and map the object. The same issue will eventually also
surface once the packfile store becomes per-object-source.

Furthermore, it feels rather pointless to first look up the object only
to then try and read it.

Refactor the logic to be centered around sources instead. Instead of
first reading the object, we immediately ask the source to create the
object stream for us. If the object exists we get stream, otherwise
we'll try the next source.

Like this we only have to iterate through sources once. But even more
importantly, this change also helps us to make the whole logic
pluggable. The object read stream subsystem does not need to be aware of
the different source backends anymore, but eventually it'll only have to
call the source's callback function.

Note that at the current point in time we aren't fully there yet:

  - The packfile store still sits on the object database level and is
    thus agnostic of the sources.

  - We still have to call into both the packfile store and the loose
    object source.

But both of these issues will soon be addressed.

This refactoring results in a slight change to semantics: previously, it
was `odb_read_object_info_extended()` that picked the source for us, and
it would have favored packed (non-deltified) objects over loose objects.
And while we still favor packed over loose objects for a single source
with the new logic, we'll now favor a loose object from an earlier
source over a packed object from a later source.

Ultimately this shouldn't matter though: the stream doesn't indicate to
the caller which source it is from and whether it was created from a
packed or loose object, so such details are opaque to the caller. And
other than that we should be able to assume that two objects with the
same object ID should refer to the same content, so the streamed data
would be the same, too.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-23 12:56:45 -08:00
Patrick Steinhardt
385e18810f packfile: introduce function to read object info from a store
Extract the logic to read object info for a packed object from
`do_oid_object_into_extended()` into a standalone function that operates
on the packfile store. This function will be used in a subsequent
commit.

Note that this change allows us to make `find_pack_entry()` an internal
implementation detail. As a consequence though we have to move around
`packfile_store_freshen_object()` so that it is defined after that
function.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-23 12:56:45 -08:00
Patrick Steinhardt
eb5abbb4e6 streaming: move zlib stream into backends
While all backend-specific data is now contained in a backend-specific
structure, we still share the zlib stream across the loose and packed
objects.

Refactor the code and move it into the specific structures so that we
fully detangle the different backends from one another.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-23 12:56:45 -08:00
Patrick Steinhardt
1154b2d2e5 streaming: create structure for filtered object streams
As explained in a preceding commit, we want to get rid of the union of
stream-type specific data in `struct odb_read_stream`. Create a new
structure for filtered object streams to move towards this design.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-23 12:56:45 -08:00
Patrick Steinhardt
5f0d8d2e8d streaming: create structure for packed object streams
As explained in a preceding commit, we want to get rid of the union of
stream-type specific data in `struct odb_read_stream`. Create a new
structure for packed object streams to move towards this design.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-23 12:56:45 -08:00
Patrick Steinhardt
b7774c0f0d streaming: create structure for loose object streams
As explained in a preceding commit, we want to get rid of the union of
stream-type specific data in `struct odb_read_stream`. Create a new
structure for loose object streams to move towards this design.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-23 12:56:45 -08:00
Patrick Steinhardt
e030d0aeb5 streaming: create structure for in-core object streams
As explained in a preceding commit, we want to get rid of the union of
stream-type specific data in `struct odb_read_stream`. Create a new
structure for in-core object streams to move towards this design.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-23 12:56:44 -08:00
Patrick Steinhardt
595296e124 streaming: allocate stream inside the backend-specific logic
When creating a new stream we first allocate it and then call into
backend-specific logic to populate the stream. This design requires that
the stream itself contains a `union` with backend-specific members that
then ultimately get populated by the backend-specific logic.

This works, but it's awkward in the context of pluggable object
databases. Each backend will need its own member in that union, and as
the structure itself is completely opaque (it's only defined in
"streaming.c") it also has the consequence that we must have the logic
that is specific to backends in "streaming.c".

Ideally though, the infrastructure would be reversed: we have a generic
`struct odb_read_stream` and some helper functions in "streaming.c",
whereas the backend-specific logic sits in the backend's subsystem
itself.

This can be realized by using a design that is similar to how we handle
reference databases: instead of having a union of members, we instead
have backend-specific structures with a `struct odb_read_stream base`
as its first member. The backends would thus hand out the pointer to the
base, but internally they know to cast back to the backend-specific
type.

This means though that we need to allocate different structures
depending on the backend. To prepare for this, move allocation of the
structure into the backend-specific functions that open a new stream.
Subsequent commits will then create those new backend-specific structs.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-23 12:56:44 -08:00
Patrick Steinhardt
3c7722dd4d streaming: explicitly pass packfile info when streaming a packed object
When streaming a packed object we first populate the stream with
information about the pack that contains the object before calling
`open_istream_pack_non_delta()`. This is done because we have already
looked up both the pack and the object's offset, so it would be a waste
of time to look up this information again.

But the way this is done makes for a somewhat awkward calling interface,
as the caller now needs to be aware of how exactly the function itself
behaves.

Refactor the code so that we instead explicitly pass the packfile info
into `open_istream_pack_non_delta()`. This makes the calling convention
explicit, but more importantly this allows us to refactor the function
so that it becomes its responsibility to allocate the stream itself in a
subsequent patch.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-23 12:56:44 -08:00
Patrick Steinhardt
3f64deabdf streaming: propagate final object type via the stream
When opening the read stream for a specific object the caller is also
expected to pass in a pointer to the object type. This type is passed
down via multiple levels and will eventually be populated with the type
of the looked-up object.

The way we propagate down the pointer though is somewhat non-obvious.
While `istream_source()` still expects the pointer and looks it up via
`odb_read_object_info_extended()`, we also pass it down even further
into the format-specific callbacks that perform another lookup. This is
quite confusing overall.

Refactor the code so that the responsibility to populate the object type
rests solely with the format-specific callbacks. This will allow us to
drop the call to `odb_read_object_info_extended()` in `istream_source()`
entirely in a subsequent patch.

Furthermore, instead of propagating the type via an in-pointer, we now
propagate the type via a new field in the object stream. It already has
a `size` field, so it's only natural to have a second field that
contains the object type.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-23 12:56:44 -08:00
Patrick Steinhardt
70c8b5f545 streaming: drop the open() callback function
When creating a read stream we first populate the structure with the
open callback function and then subsequently call the function. This
layout is somewhat weird though:

  - The structure needs to be allocated and partially populated with the
    open function before we can properly initialize it.

  - We only ever call the `open()` callback function right after having
    populated the `struct odb_read_stream::open` member, and it's never
    called thereafter again. So it is somewhat pointless to store the
    callback in the first place.

Especially the first point creates a problem for us. In subsequent
commits we'll want to fully move construction of the read source into
the respective object sources. E.g., the loose object source will be the
one that is responsible for creating the structure. But this creates a
problem: if we first need to create the structure so that we can call
the source-specific callback we cannot fully handle creation of the
structure in the source itself.

We could of course work around that and have the loose object source
create the structure and populate its `open()` callback, only. But
this doesn't really buy us anything due to the second bullet point
above.

Instead, drop the callback entirely and refactor `istream_source()` so
that we open the streams immediately. This unblocks a subsequent step,
where we'll also start to allocate the structure in the source-specific
logic.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-23 12:56:44 -08:00
Patrick Steinhardt
6bdda3a3b0 streaming: rename git_istream into odb_read_stream
In the following patches we are about to make the `git_istream` more
generic so that it becomes fully controlled by the specific object
source that wants to create it. As part of these refactorings we'll
fully move the structure into the object database subsystem.

Prepare for this change by renaming the structure from `git_istream`
to `odb_read_stream`. This mirrors the `odb_write_stream` structure that
we already have.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-23 12:56:44 -08:00
Junio C Hamano
debbc87557 The second batch
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-21 09:14:18 -08:00
Junio C Hamano
7895a60969 Merge branch 'jc/gitattributes-whitespace-no-indent-fix'
Ever since we added whitespace rules for this project, we misspelt
an entry, which has been corrected.

* jc/gitattributes-whitespace-no-indent-fix:
  .gitattributes: remove misspelled no-op whitespace attribute
2025-11-21 09:14:18 -08:00
Junio C Hamano
c62d2d3810 Merge branch 'kn/maintenance-is-needed'
"git maintenance" command learned "is-needed" subcommand to tell if
it is necessary to perform various maintenance tasks.

* kn/maintenance-is-needed:
  maintenance: add 'is-needed' subcommand
  maintenance: add checking logic in `pack_refs_condition()`
  refs: add a `optimize_required` field to `struct ref_storage_be`
  reftable/stack: add function to check if optimization is required
  reftable/stack: return stack segments directly
2025-11-21 09:14:17 -08:00
Junio C Hamano
3176576a56 Merge branch 'rs/diff-quiet-no-rename'
As "git diff --quiet" only cares about the existence of any
changes, disable rename/copy detection to skip more expensive
processing whose result will be discarded anyway.

* rs/diff-quiet-no-rename:
  diff: disable rename detection with --quiet
2025-11-21 09:14:15 -08:00
Karthik Nayak
c3cf8e5907 fetch: extract out reference committing logic
The `do_fetch()` function contains the core of the `git-fetch(1)` logic.
Part of this is to fetch and store references. This is done by

  1. Creating a reference transaction (non-atomic mode uses batched
     updates).
  2. Adding individual reference updates to the transaction.
  3. Committing the transaction.
  4. When using batched updates, handling the rejected updates.

The following commit, will fix a bug wherein fetching tags with
conflicts was causing other reference updates to fail. Fixing this
requires utilizing this logic in different regions of the function.

In preparation of the follow up commit, extract the committing and
rejection handling logic into a separate function called
`commit_ref_transaction()`.

Helped-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-21 08:40:18 -08:00
Junio C Hamano
770afe4437 config: mark otherwise unused function as file-scope static
git_configset_get_pathname() is only used once inside config.c; we do
not have to expose it as a public function.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-20 15:03:40 -08:00
Greg Funni
42aa7603aa win32: pthread_cond_init should return a value
This value is not checked, but it must return to match POSIX

Signed-off-by: Greg Funni <gfunni234@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-20 14:46:05 -08:00
Greg Funni
2367c6bcd6 win32: return error if SleepConditionVariableCS fails
If it fails, return an error.

Signed-off-by: Greg Funni <gfunni234@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-20 14:45:26 -08:00
Kristoffer Haugsbakk
fbf3d0669f doc: warn against --committer-date-is-author-date
This option could create a commit history which violates the assumption
that commits have non-decreasing commit timestamps. Warn against that in
both git-am(1) and git-rebase(1).

The genesis of this option is from git-am(1) and was added in
3f01ad66 (am: Add --committer-date-is-author-date option,
2009-01-22). The commit message doesn’t give us an example
of a use case, but the thread starter does:[1]

    I've a big set of patches in a mbox file: there's sufficient info
    inside for git-am to work.

    Yet, each time I do import these, my sha1sums are changing because of
    different commit dates.

    I'd like to force the commit date to match the info/date from the time
    I received the email (and therefore always get back the right
    sha1sums).

[1]: https://lore.kernel.org/git/46d6db660901221441q60eb90bdge601a7a250c3a247@mail.gmail.com/

So the motivation was to treat git-am(1) as an import command that
creates the same commit IDs.

Putting aside the question of whether you should be using git-am(1) for
importing commits, this approach is problematic:

• you still need to apply the commits to the same base if you want the
  same hashes; and
• you need the same committer.

And if you expect the same committer, why is this person applying the
same patches multiple times with the goal of making *identical* commits?

That was all for git-am(1).

It was added to git-rebase(1) in 570ccad3 (rebase: add options passed to
git-am, 2009-03-18)[2] in order to plug options that could not be sent
on to git-am(1). At this point the utility of the option graduated to
making no sense; a use case for `git rebase --committer-date-is-author-
date` is still yet to be found.

Just warn against using this option on both commands and remind the user
to consider whether they really need it.

† 2: See also 7573cec5 (rebase -i: support
     --committer-date-is-author-date, 2020-08-17) for the commit for the
     merge backend

Suggested-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>
Acked-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-20 10:03:31 -08:00
Patrick Steinhardt
f8bdf3127a odb: refactor odb_clear() to odb_free()
The function `odb_clear()` releases all resources allocated to an object
database and ensures that all fields become zero'd out. Despite its
naming though it doesn't really clear the object database so that it
becomes ready for reuse afterwards again -- the caller would first have
to reinitialize it, and that contradicts the terminology of "clearing"
as we have defined it in our coding guidelines.

There isn't really only a reason to have "clearing" semantics, either.
There's only a single caller of `odb_clear()`, and that caller also ends
up freeing the object database structure itself.

Refactor the function to have "freeing" semantics instead, so that the
structure itself is also freed, which allows us to drop some useless
boilerplate to zero out the structure's members.

This refactoring reveals that we're trying to close the commit graph
multiple times: once directly via `free_commit_graph()`, and once via
`odb_close()`. Drop the former call.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-19 17:41:03 -08:00
Patrick Steinhardt
9aaba57993 odb: adopt logic to close object databases
The logic to close an object database is currently contained in the
packfile subsystem. That choice is somewhat relatable, as most of the
logic really is to close resources associated with the packfile store
itself. But we also end up handling object sources and commit graphs,
which certainly is not related to packfiles.

Move the function into the object database subsystem and rename it to
`odb_close()`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-19 17:41:03 -08:00
Patrick Steinhardt
7c188a9e45 setup: convert set_git_dir() to have file scope
We don't have any external callers of `set_git_dir()` anymore now that
`enter_repo()` has been moved into "setup.c". Remove the declaration and
mark the function as static.

Note that this change requires us to move the implementation around so
that we can avoid adding any new forward declarations.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-19 17:41:03 -08:00
Patrick Steinhardt
831e02340b path: move enter_repo() into "setup.c"
The function `enter_repo()` is used to enter a repository at a given
path. As such it sits way closer to setting up a repository than it does
with handling paths, but regardless of that it's located in "path.c"
instead of in "setup.c".

Move the function into "setup.c".

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-19 17:41:03 -08:00
Junio C Hamano
c6def6a055 Merge branch 'ps/object-source-loose' into ps/object-source-management
A part of code paths that deals with loose objects has been cleaned
up.

* ps/object-source-loose:
  object-file: refactor writing objects via a stream
  object-file: rename `write_object_file()`
  object-file: refactor freshening of objects
  object-file: rename `has_loose_object()`
  object-file: read objects via the loose object source
  object-file: move loose object map into loose source
  object-file: hide internals when we need to reprepare loose sources
  object-file: move loose object cache into loose source
  object-file: introduce `struct odb_source_loose`
  object-file: move `fetch_if_missing`
  odb: adjust naming to free object sources
  odb: introduce `odb_source_new()`
  odb: fix subtle logic to check whether an alternate is usable
2025-11-19 17:40:24 -08:00
Junio C Hamano
01f9010cc7 Merge branch 'ps/object-source-loose' into ps/object-read-stream
A part of code paths that deals with loose objects has been cleaned
up.

* ps/object-source-loose:
  object-file: refactor writing objects via a stream
  object-file: rename `write_object_file()`
  object-file: refactor freshening of objects
  object-file: rename `has_loose_object()`
  object-file: read objects via the loose object source
  object-file: move loose object map into loose source
  object-file: hide internals when we need to reprepare loose sources
  object-file: move loose object cache into loose source
  object-file: introduce `struct odb_source_loose`
  object-file: move `fetch_if_missing`
  odb: adjust naming to free object sources
  odb: introduce `odb_source_new()`
  odb: fix subtle logic to check whether an alternate is usable
2025-11-19 17:39:12 -08:00
Jean-Noël Avila
f7316a66d3 doc: convert git push to synopsis style
- Switch the synopsis to a synopsis block which will automatically
  format placeholders in italics and keywords in monospace
- Use _<placeholder>_ instead of <placeholder> in the description
- Use `backticks` for keywords and more complex option
descriptions. The new rendering engine will apply synopsis rules to
these spans.

Signed-off-by: Jean-Noël Avila <jn.avila@free.fr>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-19 15:00:45 -08:00
Jean-Noël Avila
c80a5ebce0 doc: convert git pull to synopsis style
- Switch the synopsis to a synopsis block which will automatically
  format placeholders in italics and keywords in monospace
- Use _<placeholder>_ instead of <placeholder> in the description
- Use `backticks` for keywords and more complex option
descriptions. The new rendering engine will apply synopsis rules to
these spans.

Signed-off-by: Jean-Noël Avila <jn.avila@free.fr>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-19 15:00:42 -08:00
Jean-Noël Avila
903b04a3e7 doc: convert git fetch to synopsis style
- Switch the synopsis to a synopsis block which will automatically
  format placeholders in italics and keywords in monospace
- Use _<placeholder>_ instead of <placeholder> in the description
- Use `backticks` for keywords and more complex option
descriptions. The new rendering engine will apply synopsis rules to
these spans.

Signed-off-by: Jean-Noël Avila <jn.avila@free.fr>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-19 15:00:37 -08:00
Junio C Hamano
5e6e4854e0 Start 2.53 cycle
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-19 10:55:42 -08:00
Junio C Hamano
ee27005905 Merge branch 'ps/ref-peeled-tags-fixes'
Another fix-up to "peeled-tags" topic.

* ps/ref-peeled-tags-fixes:
  object: fix performance regression when peeling tags
2025-11-19 10:55:42 -08:00
Junio C Hamano
7ccfc262d7 Merge branch 'kn/refs-optim-cleanup'
Code clean-up.

* kn/refs-optim-cleanup:
  t/pack-refs-tests: move the 'test_done' to callees
  refs: rename 'pack_refs_opts' to 'refs_optimize_opts'
  refs: move to using the '.optimize' functions
2025-11-19 10:55:40 -08:00
Junio C Hamano
13134cecb0 Merge branch 'ps/ref-peeled-tags'
Some ref backend storage can hold not just the object name of an
annotated tag, but the object name of the object the tag points at.
The code to handle this information has been streamlined.

* ps/ref-peeled-tags:
  t7004: do not chdir around in the main process
  ref-filter: fix stale parsed objects
  ref-filter: parse objects on demand
  ref-filter: detect broken tags when dereferencing them
  refs: don't store peeled object IDs for invalid tags
  object: add flag to `peel_object()` to verify object type
  refs: drop infrastructure to peel via iterators
  refs: drop `current_ref_iter` hack
  builtin/show-ref: convert to use `reference_get_peeled_oid()`
  ref-filter: propagate peeled object ID
  upload-pack: convert to use `reference_get_peeled_oid()`
  refs: expose peeled object ID via the iterator
  refs: refactor reference status flags
  refs: fully reset `struct ref_iterator::ref` on iteration
  refs: introduce `.ref` field for the base iterator
  refs: introduce wrapper struct for `each_ref_fn`
2025-11-19 10:55:39 -08:00
Junio C Hamano
7a75e549b2 Merge branch 'ps/packed-git-in-object-store'
The list of packfiles used in a running Git process is moved from
the packed_git structure into the packfile store.

* ps/packed-git-in-object-store:
  packfile: track packs via the MRU list exclusively
  packfile: always add packfiles to MRU when adding a pack
  packfile: move list of packs into the packfile store
  builtin/pack-objects: simplify logic to find kept or nonlocal objects
  packfile: fix approximation of object counts
  http: refactor subsystem to use `packfile_list`s
  packfile: move the MRU list into the packfile store
  packfile: use a `strmap` to store packs by name
2025-11-19 10:55:37 -08:00
Ezekiel Newren
22ce0cb639 xdiff: rename rindex -> reference_index
The classic diff adds only the lines that it's going to consider,
during the diff, to an array. A mapping between the compacted
array, and the lines of the file that they reference, is
facilitated by this array.

Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-18 14:53:11 -08:00
Ezekiel Newren
5004a8da14 xdiff: change rindex from long to size_t in xdfile_t
The field rindex describes an index offset for other arrays. Change it
to size_t.

Changing the type of rindex from long to size_t has no cascading
refactor impact because it is only ever used to directly index other
arrays.

Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-18 14:53:11 -08:00
Ezekiel Newren
e35877eadb xdiff: make xdfile_t.nreff a size_t instead of long
size_t is used because nreff describes the number of elements in memory
for rindex.

Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-18 14:53:11 -08:00
Ezekiel Newren
016538780e xdiff: make xdfile_t.nrec a size_t instead of long
size_t is used because nrec describes the number of elements for both
recs, and for 'changed' + 2.

Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-18 14:53:10 -08:00
Ezekiel Newren
6a26019c81 xdiff: split xrecord_t.ha into line_hash and minimal_perfect_hash
The ha field is serving two different purposes, which makes the code
harder to read. At first glance, it looks like many places assume
there could never be hash collisions between lines of the two input
files. In reality, line_hash is used together with xdl_recmatch() to
ensure correct comparisons of lines, even when collisions occur.

To make this clearer, the old ha field has been split:
  * line_hash: a straightforward hash of a line, independent of any
    external context. Its type is uint64_t, as it comes from a fixed
    width hash function.
  * minimal_perfect_hash: Not a new concept, but now a separate
    field. It comes from the classifier's general-purpose hash table,
    which assigns each line a unique and minimal hash across the two
    files. A size_t is used here because it's meant to be used to
    index an array. This also avoids ` as usize` casts on the Rust
    side when using it to index a slice.

Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-18 14:53:10 -08:00
Ezekiel Newren
b0d4ae30f5 xdiff: use unambiguous types in xdl_hash_record()
Convert the function signature and body to use unambiguous types. char
is changed to uint8_t because this function processes bytes in memory.
unsigned long to uint64_t so that the hash output is consistent across
platforms. `flags` was changed from long to uint64_t to ensure the
high order bits are not dropped on platforms that treat long as 32
bits.

Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-18 14:53:10 -08:00
Ezekiel Newren
9bd193253c xdiff: use size_t for xrecord_t.size
size_t is the appropriate type because size is describing the number of
elements, bytes in this case, in memory.

Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-18 14:53:10 -08:00
Ezekiel Newren
10f97d6aff xdiff: make xrecord_t.ptr a uint8_t instead of char
Make xrecord_t.ptr uint8_t because it's referring to bytes in memory.

In order to avoid a refactor avalanche, many uses of this field were
cast to char* or similar.

Places where casting was unnecessary:
xemit.c:156
xmerge.c:124
xmerge.c:127
xmerge.c:164
xmerge.c:169
xmerge.c:172
xmerge.c:178

Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-18 14:53:10 -08:00
Ezekiel Newren
f007f4f4b4 xdiff: use ptrdiff_t for dstart/dend
ptrdiff_t is appropriate for dstart and dend because they both describe
positive or negative offsets relative to a pointer.

Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-18 14:53:10 -08:00
Ezekiel Newren
6971934d9b doc: define unambiguous type mappings across C and Rust
Document other nuances when crossing the FFI boundary. Other language
mappings may be added in the future.

Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-18 14:53:09 -08:00
Lucas Seiki Oshiro
155caac7d1 repo: add --all to git-repo-info
Add a new flag `--all` to git-repo-info for requesting values for all
the available keys. By using this flag, the user can retrieve all the
values instead of searching what are the desired keys for what they
wants.

Helped-by: Karthik Nayak <karthik.188@gmail.com>
Helped-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-18 13:29:10 -08:00
Lucas Seiki Oshiro
fd7d79d068 repo: factor out field printing to dedicated function
Move the field printing in git-repo-info to a new function called
`print_field`, allowing it to be called by functions other than
`print_fields`.

Also change its use of quote_c_style() helper to output directly to
the standard output stream, instead of taking a result in a strbuf
and then printing it outselves.

Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-18 13:29:10 -08:00
Phillip Wood
08dfa59835 worktree list: quote paths
If a worktree path contains newlines or other control characters
it messes up the output of "git worktree list". Fix this by using
quote_path() to display the worktree path. The output of "git worktree
list" is designed for human consumption, scripts should be using the
"--porcelain" option so this change should not break them.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-18 10:11:29 -08:00
Phillip Wood
a6238ee163 worktree list: fix column spacing
The output of "git worktree list" displays a table containing the
worktree path, HEAD OID and branch name for each worktree. The code
aligns the columns by measuring the visual width of the worktree path
when it is printed. Unfortunately it fails to use the visual width
when calculating the width of the column so, if any of the paths
contain a multibyte character, we can end up with excess padding
between columns. The simplest fix would be to replace strlen() with
utf8_strwidth() in measure_widths(). However that leaves us measuring
the visual width twice and the byte length once. By caching the visual
width and printing the padding separately to the worktree path, we only
need to calculate the visual width once and do not need the byte length
at all. The visual widths are stored in an arrays of structs rather
than an array of ints as the next commit will add more struct members.

Even if there are no multibyte characters in any of the paths we still
print an extra space between the path and the object id as the field
width is calculated as one plus the length of the path and we print an
explicit space as well. This is fixed by not printing the extra space.

The tests are updated to include multibyte characters in one of the
worktree paths and to check the spacing of the columns.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-18 10:11:19 -08:00
Jeff King
14b561e768 test-mktemp: plug memory and descriptor leaks
We test xmkstemp() in our helper by just calling:

  xmkstemp(xstrdup(argv[1]));

This leaks both the copied string as well as the descriptor returned by
the function. In practice this isn't a big deal, since we immediately
exit the program, but:

  1. LSan will complain about the memory leak. The only reason we did
     not notice this in our leak-checking builds is that both of the
     callers in the test suite (both in t0070) pass a broken template
     (and expect failure). So the function calls die() before we can
     actually leak.

     But it's an accident waiting to happen if anybody adds a call which
     succeeds.

  2. Coverity complains about the descriptor leak. There's a long list
     of uninteresting or false positives in Coverity's results, but
     since we're here we might as well fix it, too.

I didn't bother adding a new test that triggers the leak. It's not even
in real production code, but just in the test-helper itself.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-18 10:05:14 -08:00
Jeff King
17bd1108ea ci(windows-meson-test): handle options and output like other test jobs
The GitHub windows-meson-test jobs directly run "meson test" with the
--slice option. This means they skip all of the ci/lib.sh
infrastructure, and in particular:

  1. They do not actually set any GIT_TEST_OPTS like --verbose-log or
     -x.

  2. They do not do the usual handle_failed_tests() magic to print test
     failures or tar up failed directories.

As a result, you get almost no feedback at all when a test fails in this
job, making debugging rather tricky.

Let's try to make this behave more like the other CI jobs. Because we're
on Windows, we can't just use the normal run-build-and-tests.sh script.
Our build runs as a separate job (like the non-meson Windows job), and
then we parallelize the tests across several job slices. So we need
something like the run-test-slice.sh script that the "windows-test" job
uses.

In theory we could just swap out the "make" invocation there for
"meson". But it doesn't quite work, because "make" knows how to pull
GIT_TEST_OPTS out of GIT-BUILD-OPTIONS automatically. But for meson, we
have to extract them into the --test-args option ourselves. I tried
making the logic in run-test-slice.sh conditional, but there ended up
being hardly any common code at all (and there are some tricky ordering
constraints). So I added up with a new meson-specific test-slice runner.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-18 09:45:29 -08:00
Jeff King
e96105aa17 unit-test: ignore --no-chain-lint
In the same spirit as 9faf3963b6 (t: introduce compatibility options to
clar-based tests, 2024-12-13), we should ignore --no-chain-lint passed
to our clar tests, since it may appear in GIT_TEST_OPTS to be used with
other tests.

This is particularly important on Windows CI, where --no-chain-lint is
added to the test options by default, and the meson build will pass all
options to the unit tests. The only reason our meson Windows CI job does
not run into this currently is that it is not respecting GIT_TEST_OPTS
at all! So ignoring this option is a prerequisite to fixing that
situation.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-18 09:45:28 -08:00
Jeff King
a031b6181a t: enable ASan's strict_string_checks option
ASan has an option to enable strict string checking, where any pointer
passed to a function that expects a NUL-terminated string will be
checked for that NUL termination. This can sometimes produce false
positives. E.g., it is not wrong to pass a buffer with { '1', '2', '\n' }
into strtoul(). Even though it is not NUL-terminated, it will stop at
the newline.

But in trying it out, it identified two problematic spots in our test
suite (which have now been adjusted):

  1. The strtol() parsing in cache-tree.c was a real potential problem,
     which would have been very hard to find otherwise (since it
     required constructing a very specific broken index file).

  2. The use of string functions in fsck_ident() were false positives,
     because we knew that there was always a trailing newline which
     would stop the functions from reading off the end of the buffer.
     But the reasoning behind that is somewhat fragile, and silencing
     those complaints made the code easier to reason about.

So even though this did not find any earth-shattering bugs, and even had
a few false positives, I'm sufficiently convinced that its complaints
are more helpful than hurtful. Let's turn it on by default (since the
test suite now runs cleanly with it) and see if it ever turns up any
other instances.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-18 09:36:12 -08:00
Jeff King
5a993593b2 fsck: avoid parse_timestamp() on buffer that isn't NUL-terminated
In fsck_ident(), we parse the timestamp with parse_timestamp(), which is
really an alias for strtoumax(). But since our buffer may not be
NUL-terminated, this can trigger a complaint from ASan's
strict_string_checks mode. This is a false positive, since we know that
the buffer contains a trailing newline (which we checked earlier in the
function), and that strtoumax() would stop there.

But it is worth working around ASan's complaint. One is because that
will let us turn on strict_string_checks by default, which has helped
catch other real problems. And two is that the safety of the current
code is very hard to reason about (it subtly depends on distant code
which could change).

One option here is to just parse the number left-to-right ourselves. But
we care about the size of a timestamp_t and detecting overflow, since
that's part of the point of these checks. And doing that correctly is
tricky. So we'll instead just pull the digits into a separate,
NUL-terminated buffer, and use that to call parse_timestamp().

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-18 09:36:12 -08:00
Jeff King
f05df7ffca fsck: remove redundant date timestamp check
After calling "parse_timestamp(p, &end, 10)", we complain if "p == end",
which would imply that we did not see any digits at all. But we know
this cannot be the case, since we would have bailed already if we did
not see any digits, courtesy of extra checks added by 8e4309038f (fsck:
do not assume NUL-termination of buffers, 2023-01-19). Since then,
checking "p == end" is redundant and we can drop it.

This will make our lives a little easier as we refactor further.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-18 09:36:11 -08:00
Jeff King
830424def4 fsck: avoid strcspn() in fsck_ident()
We may be operating on a buffer that is not NUL-terminated, but we use
strcspn() to parse it. This is OK in practice, as discussed in
8e4309038f (fsck: do not assume NUL-termination of buffers, 2023-01-19),
because we know there is at least a trailing newline in our buffer, and
we always pass "\n" to strcspn(). So we know it will stop before running
off the end of the buffer.

But this is a subtle point to hang our memory safety hat on. And it
confuses ASan's strict_string_checks mode, even though it is technically
a false positive (that mode complains that we have no NUL, which is
true, but it does not know that we have verified the presence of the
newline already).

Let's instead open-code the loop. As a bonus, this makes the logic more
obvious (to my mind, anyway). The current code skips forward with
strcspn until it hits "<", ">", or "\n". But then it must check which it
saw to decide if that was what we expected or not, duplicating some
logic between what's in the strcspn() and what's in the domain logic.
Instead, we can just check each character as we loop and act on it
immediately.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-18 09:36:11 -08:00
Jeff King
0b6ec075df fsck: assert newline presence in fsck_ident()
The fsck code purports to handle buffers that are not NUL-terminated,
but fsck_ident() uses some string functions. This works OK in practice,
as explained in 8e4309038f (fsck: do not assume NUL-termination of
buffers, 2023-01-19). Before calling fsck_ident() we'll have called
verify_headers(), which makes sure we have at least a trailing newline.
And none of our string-like functions will walk past that newline.

However, that makes this code at the top of fsck_ident() very confusing:

    *ident = strchrnul(*ident, '\n');
    if (**ident == '\n')
            (*ident)++;

We should always see that newline, or our memory safety assumptions have
been violated! Further, using strchrnul() is weird, since the whole
point is that if the newline is not there, we don't necessarily have a
NUL at all, and might read off the end of the buffer.

So let's have callers pass in the boundary of our buffer, which lets us
safely find the newline with memchr(). And if it is not there, this is a
BUG(), because it means our caller did not validate the input with
verify_headers() as it was supposed to (and we are better off bailing
rather than having memory-safety problems).

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-18 09:36:11 -08:00
Jeff King
c4c9089584 cache-tree: avoid strtol() on non-string buffer
A cache-tree extension entry in the index looks like this:

  <name> NUL <entry_nr> SPACE <subtree_nr> NEWLINE <binary_oid>

where the "_nr" items are human-readable base-10 ASCII. We parse them
with strtol(), even though we do not have a NUL-terminated string (we'd
generally have an mmap() of the on-disk index file). For a well-formed
entry, this is not a problem; strtol() will stop when it sees the
newline. But there are two problems:

  1. A corrupted entry could omit the newline, causing us to read
     further. You'd mostly get stopped by seeing non-digits in the oid
     field (and if it is likewise truncated, there will still be 20 or
     more bytes of the index checksum). So it's possible, though
     unlikely, to read off the end of the mmap'd buffer. Of course a
     malicious index file can fake the oid and the index checksum to all
     (ASCII) 0's.

     This is further complicated by the fact that mmap'd buffers tend to
     be zero-padded up to the page boundary. So to run off the end, the
     index size also has to be a multiple of the page size. This is also
     unlikely, though you can construct a malicious index file that
     matches this.

     The security implications aren't too interesting. The index file is
     a local file anyway (so you can't attack somebody by cloning, but
     only if you convince them to operate in a .git directory you made,
     at which point attacking .git/config is much easier). And it's just
     a read overflow via strtol(), which is unlikely to buy you much
     beyond a crash.

  2. ASan has a strict_string_checks option, which tells it to make sure
     that options to string functions (like strtol) have some eventual
     NUL, without regard to what the function would actually do (like
     stopping at a newline here). This option sometimes has false
     positives, but it can point to sketchy areas (like this one) where
     the input we use doesn't exhibit a problem, but different input
     _could_ cause us to misbehave.

Let's fix it by just parsing the values ourselves with a helper function
that is careful not to go past the end of the buffer. There are a few
behavior changes here that should not matter:

  - We do not consider overflow, as strtol() would. But nor did the
    original code. However, we don't trust the value we get from the
    on-disk file, and if it says to read 2^30 entries, we would notice
    that we do not have that many and bail before reading off the end of
    the buffer.

  - Our helper does not skip past extra leading whitespace as strtol()
    would, but according to gitformat-index(5) there should not be any.

  - The original quit parsing at a newline or a NUL byte, but now we
    insist on a newline (which is what the documentation says, and what
    Git has always produced).

Since we are providing our own helper function, we can tweak the
interface a bit to make our lives easier. The original code does not use
strtol's "end" pointer to find the end of the parsed data, but rather
uses a separate loop to advance our "buf" pointer to the trailing
newline. We can instead provide a helper that advances "buf" as it
parses, letting us read strictly left-to-right through the buffer.

I didn't add a new test here. It's surprisingly difficult to construct
an index of exactly the right size due to the way we pad entries. But it
is easy to trigger the problem in existing tests when using ASan's
strict string checking, coupled with a recent change to use NO_MMAP with
ASan builds. So:

  make SANITIZE=address
  cd t
  ASAN_OPTIONS=strict_string_checks=1 ./t0090-cache-tree.sh

triggers it reliably. Technically it is not deterministic because there
is ~8% chance (it's 1-(255/256)^20, or ^32 for sha256) that the trailing
checksum hash has a NUL byte in it. But we compute enough cache-trees in
the course of that script that we are very likely to hit the problem in
one of them.

We can look at making strict_string_checks the default for ASan builds,
but there are some other cases we'd want to fix first.

Reported-by: correctmost <cmlists@sent.com>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-18 09:36:06 -08:00
Jeff King
a9990f8ec0 Makefile: turn on NO_MMAP when building with ASan
Git often uses mmap() to access on-disk files. This leaves a blind spot
in our SANITIZE=address builds, since ASan does not seem to handle mmap
at all. Nor does the OS notice most out-of-bounds access, since it tends
to round up to the nearest page size (so depending on how big the map
is, you might have to overrun it by up to 4095 bytes to trigger a
segfault).

The previous commit demonstrates a memory bug that we missed. We could
have made a new test where the out-of-bounds access was much larger, or
where the mapped file ended closer to a page boundary. But the point of
running the test suite with sanitizers is to catch these problems
without having to construct specific tests.

Let's enable NO_MMAP for our ASan builds by default, which should give
us better coverage. This does increase the memory usage of Git, since
we're copying from the filesystem into heap. But the repositories in the
test suite tend to be small, so the overhead isn't really noticeable
(and ASan already has quite a performance penalty).

There are a few other known bugs that this patch will help flush out.
However, they aren't directly triggered in the test suite (yet). So
it's safe to turn this on now without breaking the test suite, which
will help us add new tests to demonstrate those other bugs as we fix
them.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-18 09:36:06 -08:00
Jeff King
4deb882e54 pack-bitmap: handle name-hash lookups in incremental bitmaps
If a bitmap has a name-hash cache, it is an array of 32-bit integers,
one per entry in the bitmap, which we've mmap'd from the .bitmap file.
We access it directly like this:

    if (bitmap_git->hashes)
            hash = get_be32(bitmap_git->hashes + index_pos);

That works for both regular pack bitmaps and for non-incremental midx
bitmaps. There is one bitmap_index with one "hashes" array, and
index_pos is within its bounds (we do the bounds-checking when we load
the bitmap).

But for an incremental midx bitmap, we have a linked list of
bitmap_index structs, and each one has only its own small slice of the
name-hash array. If index_pos refers to an object that is not in the
first bitmap_git of the chain, then we'll access memory outside of the
bounds of its "hashes" array, and often outside of the mmap.

Instead, we should walk through the list until we find the bitmap_index
which serves our index_pos, and use its hash (after adjusting index_pos
to make it relative to the slice we found). This is exactly what we do
elsewhere for incremental midx lookups (like the pack_pos_to_midx() call
a few lines above). But we can't use existing helpers like
midx_for_object() here, because we're walking through the chain of
bitmap_index structs (each of which refers to a midx), not the chain of
incremental multi_pack_index structs themselves.

The problem is triggered in the test suite, but we don't get a segfault
because the out-of-bounds index is too small. The OS typically rounds
our mmap up to the nearest page size, so we just end up accessing some
extra zero'd memory. Nor do we catch it with ASan, since it doesn't seem
to instrument mmaps at all. But if we build with NO_MMAP, then our maps
are replaced with heap allocations, which ASan does check. And so:

  make NO_MMAP=1 SANITIZE=address
  cd t
  ./t5334-incremental-multi-pack-index.sh

does show the problem (and this patch makes it go away).

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-18 09:36:06 -08:00
Jeff King
65e8141f05 compat/mmap: mark unused argument in git_munmap()
Our mmap compat code emulates mapping by using malloc/free. Our
git_munmap() must take a "length" parameter to match the interface of
munmap(), but we don't use it (it is up to the allocator to know how big
the block is in free()).

Let's mark it as UNUSED to avoid complaints from -Wunused-parameter.
Otherwise you cannot build with "make DEVELOPER=1 NO_MMAP=1".

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-18 09:36:05 -08:00
Johannes Schindelin
cd99203f86 ci: bump actions/setup-go from 5 to 6
Bumps actions/setup-go from 5 to 6. This upgrade includes dependency
updates that incorporate a fix for a critical vulnerability.
[Originally opened at https://github.com/git-for-windows/git/pull/5811]

- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v5...v6)

Originally-authored-by: dependabot[bot] <support@github.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-18 09:34:16 -08:00
Johannes Schindelin
af3919816f mingw: avoid the comma operator
The pattern `return errno = ..., -1;` is observed several times in
`compat/mingw.c`. It has served us well over the years, but now clang
starts complaining:

  compat/mingw.c:723:24: error: possible misuse of comma operator here [-Werror,-Wcomma]
    723 |                 return errno = ENOSYS, -1;
        |                                      ^

See for example this failing workflow run:
https://github.com/git-for-windows/git-sdk-arm64/actions/runs/15457893907/job/43513458823#step:8:201

Let's appease clang (and also reduce the use of the no longer common
comma operator).

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-17 14:19:21 -08:00
Johannes Schindelin
b0d5c88cca cmake: stop trying to build the reftable and xdiff libraries
In the `en/make-libgit-a` topic branch, more precisely in the commits
f3b4c89d59f1 (make: delete REFTABLE_LIB, add reftable to LIB_OBJS,
2025-10-02) and cf680cdb9543 (make: delete XDIFF_LIB, add xdiff to
LIB_OBJS, 2025-10-02), the strategy to build three static libraries was
rethought, and instead only one static library is now built.

This is good.

However, the CMake definition was not changed accordingly, and now
CMake-based builds fail thusly:

  [...]
  Generating hook-list.h
  CMake Error at CMakeLists.txt:122 (string):
    string sub-command REPLACE requires at least four arguments.
  Call Stack (most recent call first):
    CMakeLists.txt:711 (parse_makefile_for_sources)

  CMake Error at CMakeLists.txt:122 (string):
    string sub-command REPLACE requires at least four arguments.
  Call Stack (most recent call first):
    CMakeLists.txt:717 (parse_makefile_for_sources)

  -- Configuring incomplete, errors occurred!

Fix that by removing the parts that expect the reftable and xdiff
objects to be defined separately in the Makefile, still.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-17 14:18:28 -08:00
David Macek
d22a488482 wincred: avoid memory corruption
`wcsncpy_s()` wants to write the terminating null character so we need
to allocate one more space for it in the target memory block.

This should fix crashes when trying to read passwords.  When this
happened, the password/token wouldn't print out and Git would therefore
ask for a new password every time.

Signed-off-by: David Macek <david.macek.0@gmail.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-17 14:17:42 -08:00
Elijah Newren
a562d90a35 merge-ort: fix failing merges in special corner case
At GitHub, we had a repository that was triggering
  git: merge-ort.c:3032: process_renames: Assertion `newinfo && !newinfo->merged.clean` failed.
during git replay.

This sounds similar to the somewhat recent f6ecb603ff8a (merge-ort: fix
directory rename on top of source of other rename/delete, 2025-08-06),
but the cause is different.  Unlike that case, there are no
rename-to-self situations arising in this case, and new to this case it
can only be triggered during a replay operation on the 2nd or later
commit being replayed, never on the first merge in the sequence.

To trigger, the repository needs:
  * an upstream which:
    * renames a file to a different directory, e.g.
        old/file -> new/file
    * leaves other files remaining in the original directory (so that
      e.g. "old/" still exists upstream even though file has been
      removed from it and placed elsewhere)
  * a topic branch being rebased where:
    * a commit in the sequence:
      * modifies old/file
    * a subsequent commit in the sequence being replayed:
      * does NOT touch *anything* under new/
      * does NOT touch old/file
      * DOES modify other paths under old/
      * does NOT have any relevant renames that we need to detect
        _anywhere_ elsewhere in the tree (meaning this interacts
        interestingly with both directory renames and cached renames)

In such a case, the assertion will trigger.  The fix turns out to be
surprisingly simple.  I have a very vague recollection that I actually
considered whether to add such an if-check years ago when I added the
very similar one for oldinfo in 1b6b902d95a5 (merge-ort:
process_renames() now needs more defensiveness, 2021-01-19), but I think
I couldn't figure out a possible way to trigger it and was worried at
the time that if I didn't know how to trigger it then I wasn't so sure
that simply skipping it was correct.  Waiting did give me a chance to
put more thorough tests and checks into place for the rename-to-self
cases a few months back, which I might not have found as easily
otherwise.  Anyway, put the check in place now and add a test that
demonstrates the fix.

Note that this bug, as demonstrated by the conditions listed above,
runs at the intersection of relevant renames, trivial directory
resolutions, and cached renames.  All three of those optimizations are
ones that unfortunately make the code (and testcases!) a bit more
complex, and threading all three makes it a bit more so.  However, the
testcase isn't crazy enough that I'd expect no one to ever hit it in
practice, and was confused why we didn't see it before.  After some
digging, I discovered that merge.directoryRenames=false is a workaround
to this bug, and GitHub used that setting until recently (it was a
"temporary" match-what-libgit2-does piece of code that lasted years
longer than intended).  Since the conditions I gave above for triggering
this bug rule out the possibility of there being directory renames, one
might assume that it shouldn't matter whether you try to detect such
renames if there aren't any.  However, due to commit a16e8efe5c2b
(merge-ort: fix merge.directoryRenames=false, 2025-03-13), the heavy
hammer used there means that merge.directoryRenames=false ALSO turns off
rename caching, which is critical to triggering the bug.  This becomes
a bit more than an aside since...

Re-reading that old commit, a16e8efe5c2b (merge-ort: fix
merge.directoryRenames=false, 2025-03-13), it appears that the solution
to this latest bug might have been at least a partial alternative
solution to that old commit.  And it may have been an improved
alternative (or at least help implement one), since it may be able to
avoid the heavy-handed disabling of rename cache.  That might be an
interesting future thing to investigate, but is not critical for the
current fix.  However, since I spent time digging it all up, at least
leave a small comment tweak breadcrumb to help some future reader
(myself or others) who wants to dig further to connect the dots a little
quicker.

Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-17 14:08:09 -08:00
Elijah Newren
d5663a4b05 merge-ort: remove debugging crud
While developing commit a16e8efe5c2b (merge-ort: fix
merge.directoryRenames=false, 2025-03-13), I was testing things out and
had an extra condition on one of the if-blocks that I occasionally
swapped between '&& 0' and '&& 1' to see the effects of the changes.  I
forgot to remove it before submitting and it wasn't caught in review.
Remove it now.

Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-17 14:08:08 -08:00
Elijah Newren
ffe702b3ed t6429: update comment to mention correct tool
A comment at the top of t6429 mentions why the test doesn't exercise git
rebase or git cherry-pick.  However, it claims that it uses `test-tool
fast-rebase`.  That was true when the comment was written, but commit
f920b0289ba3 (replay: introduce new builtin, 2023-11-24) changed it to
use git replay without updating this comment.

We could potentially just strike this second comment, since git replay
is a bona fide built-in, but perhaps the explanation about why it focuses
on git replay is still useful.  Update the comment to make it accurate
again.

Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-17 14:08:08 -08:00
Johannes Schindelin
c64eb849b1 make strip: include scalar
When Scalar was made a canonical part of Git in 7b5c93c6c68 (scalar:
include in standard Git build & installation, 2022-09-02), it was added
to all relevant Makefile targets except for the `strip` target.

Let's correct that.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-17 14:05:05 -08:00
René Scharfe
f18aa68861 wrapper: simplify xmkstemp()
Call xmkstemp_mode() instead of duplicating its error handling code.
This switches the implementation from the system's mkstemp(3) to our own
git_mkstemp_mode(), which works just as well.

Signed-off-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-17 13:53:09 -08:00
Antonin Delpeuch
ffffb987fc blame: make diff algorithm configurable
The diff algorithm used in 'git-blame(1)' is set to 'myers',
without the possibility to change it aside from the `--minimal` option.

There has been long-standing interest in changing the default diff
algorithm to "histogram", and Git 3.0 was floated as a possible occasion
for taking some steps towards that:

https://lore.kernel.org/git/xmqqed873vgn.fsf@gitster.g/

As a preparation for this move, it is worth making sure that the diff
algorithm is configurable where useful.

Make it configurable in the `git-blame(1)` command by introducing the
`--diff-algorithm` option and make honor the `diff.algorithm` config
variable. Keep Myers diff as the default.

Signed-off-by: Antonin Delpeuch <antonin@delpeuch.eu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-17 09:31:59 -08:00
Antonin Delpeuch
881793c4f7 xdiff: add 'minimal' to XDF_DIFF_ALGORITHM_MASK
The XDF_DIFF_ALGORITHM_MASK bit mask only includes bits for the patience
and histogram diffs, not for the minimal one. This means that when
reseting the diff algorithm to the default one, one needs to separately
clear the bit for the minimal diff. There are places in the code that fail
to do that: merge-ort.c and builtin/merge-file.c.

Add the XDF_NEED_MINIMAL bit to the bit mask, and remove the separate
clearing of this bit in the places where it hasn't been forgotten.

Signed-off-by: Antonin Delpeuch <antonin@delpeuch.eu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-17 09:31:59 -08:00
Junio C Hamano
9a2fb147f2 Git 2.52
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-17 07:35:33 -08:00
Junio C Hamano
7f79dc3562 Merge branch 'jc/ci-use-arm64-p4-on-macos'
We replaced deprecated macos-13 with macos-14 image in GitHub
Actions CI, but we forgot that the image is for arm64.  We have
been seeing a lot of test failures ever since.  Switch to arm64
binary for Perforce tests.

* jc/ci-use-arm64-p4-on-macos:
  Use Perforce arm64 binary on macOS CI jobs
2025-11-17 07:00:12 -08:00
Christian Couder
cb034c020a commit: refactor verify_commit_buffer()
In a following commit, we are going to check commit signatures, but we
won't have a commit yet, only a commit buffer, and we are going to
discard this commit buffer if the signature is invalid. So it would be
wasteful to create a commit that we might discard, just to be able to
check a commit signature.

It would be simpler instead to be able to check commit signatures
using only a commit buffer instead of a commit.

To be able to do that, let's extract some code from the
check_commit_signature() function into a new verify_commit_buffer()
function, and then let's make check_commit_signature() call
verify_commit_buffer().

Note that this doesn't fundamentally change how
check_commit_signature() works. It used to call parse_signed_commit()
which calls repo_get_commit_buffer(), parse_buffer_signed_by_header()
and repo_unuse_commit_buffer(). Now these 3 functions are called
directly by verify_commit_buffer().

Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-16 20:56:35 -08:00
Christian Couder
388517c14c fast-import: refactor finalize_commit_buffer()
In a following commit we are going to finalize commit buffers with or
without signatures in order to check the signatures and possibly drop
them.

To do so easily and without duplication, let's refactor the current
code that finalizes commit buffers into a new finalize_commit_buffer()
function.

Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-16 20:56:35 -08:00
Jiang Xin
7a03a10a3a builtin/repo: fix table alignment for UTF-8 characters
The output table from "git repo structure" is misaligned when displaying
UTF-8 characters (e.g., non-ASCII glyphs). E.g.:

    | 仓库结构   | 值  |
    | -------------- | ---- |
    | * 引用       |      |
    |   * 计数     |   67 |

The previous implementation used simple width formatting with printf()
which didn't properly handle multi-byte UTF-8 characters, causing
misaligned table columns when displaying repository structure
information.

This change modifies the stats_table_print_structure function to use
strbuf_utf8_align() instead of basic printf width specifiers. This
ensures proper column alignment regardless of the character encoding of
the content being displayed.

Also add test cases for strbuf_utf8_align(), a function newly introduced
in "builtin/repo.c".

Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-16 16:04:24 -08:00
Jiang Xin
878fef8ebf t/unit-tests: add UTF-8 width tests for CJK chars
The file "builtin/repo.c" uses utf8_strwidth() to calculate the display
width of UTF-8 characters in a table, but the resulting output is still
misaligned. Add test cases for both utf8_strwidth and utf8_strnwidth to
verify that they correctly compute the display width for UTF-8
characters.

Also updated the build configuration in Makefile and meson.build to
include the new test suite in the build process.

Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-16 16:04:24 -08:00
Junio C Hamano
ffff0bb0da Use Perforce arm64 binary on macOS CI jobs
The previous step replaced deprecated macos-13 image with macos-14
image on GitHub Actions CI.  While x86-64 binaries can work there,
because macos-14 images are arm64 based (we could replace it with
macos-14-large that is x86-64), it makes more sense to use arm64
binary there.  Without this change, we have been getting unusually
higher rate of failures from random macOS CI jobs railing to run
t98xx series of tests.

Helped-by: Koji Nakamaru <koji.nakamaru@gree.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-16 15:11:41 -08:00
Junio C Hamano
c93f1a0fa3 l10n-2.52.0-v1
-----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEE37vMEzKDqYvVxs51k24VDd1FMtUFAmkZmdgACgkQk24VDd1F
 MtUSmQ/8DB8gG0gNDhqI3C824auF3Wa6fJFl8UBiM9uJ2QwphKtkJHSeekBnQ4FW
 18UmN7VkuRPMcMKXYRUS6SSzHebT9Atxmmsm2NuUb8fqq5Oe3v87Shv07On3+b7l
 GtSG9pkbjQeiR14Us+9G3pFm9IJ5Uh8gojZiXwZNKEzBCiae4sgxJ0YGKKpKDYao
 lGb1T/gibYA16uVMXceKEH009RN0CKL64LyY9wmNtIoVzSjOFCOiisSyD5gbG/NB
 pDZzZg7eODHSNseNaLO25tlAqKiFkNvjVOfwBNVm1ubV1zNQR0zonWKm6I3f3dyE
 lCw7swVWBoME0U4EsQdePuhccRyFNYG0RjPk/KNsygLYgLXhAIvXRRY+B/AHbyjj
 gDaFCQLywHpq/cL007fYaCaEWNPiLgb+w7/kHu/qf5cEV7kucT7BQ07X8AsnGXlk
 T5fjw/ZP7JpP6CCsL4xUfx+W8zM0+V0Yp/GdyblIxePhhDEYD8X28WRADRKuae/r
 dWgASTDOuojsJ8F2pmKUVfaXKT8aenBSsSaS9wxAeYZ8qmAA9BBwmhSXqBfm2VgZ
 xWEtbG5i4r6PiXy8uqpLBnl7nUie5EuDblv7Fs3WCLj0tLF7DWw9SNkmm3oipDXq
 iNChQeDFYX9rT0a0lLDJoWmhk8+TGdUdOfF7OgQAv47q0skP+Hg=
 =xW7s
 -----END PGP SIGNATURE-----

Merge tag 'l10n-2.52.0-v1' of https://github.com/git-l10n/git-po

l10n-2.52.0-v1

* tag 'l10n-2.52.0-v1' of https://github.com/git-l10n/git-po:
  l10n: zh_CN: updated translation for 2.52
  l10n: uk: add 2.52 translation
  l10n: zh_TW.po: update Git 2.52 translation
  l10n: Updated translation for vi-2.52
  l10n: tr: Update Turkish translations
  l10n: po-id for 2.52
  l10n: ga.po: Update Irish translation for Git 2.52
  l10n: bg.po: Updated Bulgarian translation (6065t)
  l10n: fr: version 2.52
  l10n: sv.po: Update Swedish translation
2025-11-16 10:36:50 -08:00
Teng Long
ad892a61d6 l10n: zh_CN: updated translation for 2.52
Reviewed-by: 依云 <lilydjwg@gmail.com>
Signed-off-by: Teng Long <dyroneteng@gmail.com>
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
2025-11-16 17:27:10 +08:00
Jeff King
6fe288bfbc read-cache: drop submodule check from add_to_cache()
In add_to_cache(), we treat any directories as submodules, and complain
if we can't resolve their HEAD. This call to resolve_gitlink_ref() was
added by f937bc2f86 (add: error appropriately on repository with no
commits, 2019-04-09), with the goal of improving the error message for
empty repositories.

But we already resolve the submodule HEAD in index_path(), which is
where we find the actual oid we're going to use. Resolving it again here
introduces some downsides:

  1. It's more work, since we have to open up the submodule repository's
     files twice.

  2. There are call paths that get to index_path() without going through
     add_to_cache(). For instance, we'd want a similar informative
     message if "git diff empty" finds that it can't resolve the
     submodule's HEAD. (In theory we can also get there through
     update-index, but AFAICT it refuses to consider directories as
     submodules at all, and just complains about them).

  3. The resolution in index_path() catches more errors that we don't
     handle here. In particular, it will validate that the object format
     for the submodule matches that of the superproject. This isn't a
     bug, since our call in add_to_cache() throws away the oid it gets
     without looking at it. But it certainly caused confusion for me
     when looking at where the object-format check should go.

So instead of resolving the submodule HEAD in add_to_cache(), let's just
teach the call in index_path() to actually produce an error message
(which it already does for other cases). That's probably what f937bc2f86
should have done in the first place, and it gives us a single point of
resolution when adding a submodule to the index.

The resulting output is slightly more verbose, as we propagate the error
up the call stack, but I think that's OK (and again, matches many other
errors we get when indexing fails).

I've left the text of the error message as-is, though it is perhaps
overly specific.  There are many reasons that resolving the submodule
HEAD might fail, though outside of corruption or system errors it is
probably most likely that the submodule HEAD is simply on an unborn
branch.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-15 21:18:49 -08:00
Jiang Xin
900094616b Merge branch '2.52-uk' of github.com:arkid15r/git-ukrainian-l10n
* '2.52-uk' of github.com:arkid15r/git-ukrainian-l10n:
  l10n: uk: add 2.52 translation
2025-11-16 10:16:45 +08:00
brian m. carlson
66c78e0653 object-file: disallow adding submodules of different hash algo
The design of the hash algorithm transition plan is that objects stored
must be entirely in one algorithm since we lack any way to indicate a
mix of algorithms.  This also includes submodules, but we have
traditionally not enforced this, which leads to various problems when
trying to clone or check out the the submodule from the remote.

Since this cannot work in the general case, restrict adding a submodule
of a different algorithm to the index.  Add tests for git add and git
submodule add that these are rejected.

Note that we cannot check this in git fsck because the malformed
submodule is stored in the tree as an object ID which is either
truncated (when a SHA-256 submodule is added to a SHA-1 repository) or
padded with zeros (when a SHA-1 submodule is added to a SHA-256
repository).  We cannot detect even the latter case because someone
could have an actual submodule that actually ends in 24 zeros, which
would be a false positive.

Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-15 11:51:37 -08:00
Arkadii Yakovets
1480c3907b
l10n: uk: add 2.52 translation
Co-authored-by: Kate Golovanova <kate@kgthreads.com>
Signed-off-by: Arkadii Yakovets <ark@cho.red>
Signed-off-by: Kate Golovanova <kate@kgthreads.com>
2025-11-15 10:02:21 -08:00
Jiang Xin
d3849c4a55 Merge branch 'vi-2.52' of github.com:Nekosha/git-po
* 'vi-2.52' of github.com:Nekosha/git-po:
  l10n: Updated translation for vi-2.52
2025-11-15 22:16:10 +08:00
Jiang Xin
4adfdf39e7 Merge branch 'l10n/zh-TW/git-2-52' of github.com:l10n-tw/git-po
* 'l10n/zh-TW/git-2-52' of github.com:l10n-tw/git-po:
  l10n: zh_TW.po: update Git 2.52 translation
2025-11-15 22:14:55 +08:00
Jiang Xin
b8fee03310 Merge branch 'po-id' of github.com:bagasme/git-po
* 'po-id' of github.com:bagasme/git-po:
  l10n: po-id for 2.52
2025-11-15 22:10:16 +08:00
Jiang Xin
4ef1a07de7 Merge branch 'master' of github.com:alshopov/git-po
* 'master' of github.com:alshopov/git-po:
  l10n: bg.po: Updated Bulgarian translation (6065t)
2025-11-15 22:08:47 +08:00
Jiang Xin
5eab3a7a11 Merge branch 'fr_v2.52' of github.com:jnavila/git
* 'fr_v2.52' of github.com:jnavila/git:
  l10n: fr: version 2.52
2025-11-15 22:07:53 +08:00
Jiang Xin
fc2961a95d Merge branch 'l10n-ga-2.52' of github.com:aindriu80/git-po
* 'l10n-ga-2.52' of github.com:aindriu80/git-po:
  l10n: ga.po: Update Irish translation for Git 2.52
2025-11-15 22:06:01 +08:00
Jiang Xin
466b4c0bf3 Merge branch 'master' of github.com:nafmo/git-l10n-sv
* 'master' of github.com:nafmo/git-l10n-sv:
  l10n: sv.po: Update Swedish translation
2025-11-15 22:03:30 +08:00
Yi-Jyun Pan
c35d202dcd
l10n: zh_TW.po: update Git 2.52 translation
Reviewed-by: hms5232 <hms5232@hhming.moe>
Co-authored-by: Lumynous <lumynou5.tw@gmail.com>
Signed-off-by: Yi-Jyun Pan <pan93412@gmail.com>
2025-11-15 19:10:36 +08:00
Vũ Tiến Hưng
c7b5e0e58e l10n: Updated translation for vi-2.52
Signed-off-by: Vũ Tiến Hưng <newcomerminecraft@gmail.com>
2025-11-15 12:56:31 +07:00
Emir SARI
8b26798b42
l10n: tr: Update Turkish translations
Signed-off-by: Emir SARI <emir_sari@icloud.com>
2025-11-15 02:31:02 +03:00
Kristoffer Haugsbakk
df90eccd93 doc: commit: link to git-status(1) on all format options
`--branch` and `--long` refer to git-status(1) options but they don’t tell us
what `short-format` and `long-format` are, respectively. And `--null`
mentions “status” but does not link to the command.

Refer to git-config(1) on `--branch` like `--short` does.

`long-format` is the git-status(1) output. So we can just say that
directly.

Replace “status” with a `linkgit` on `--null`.

Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-14 08:56:35 -08:00
Koji Nakamaru
4580bcd235 osxkeychain: avoid incorrectly skipping store operation
git-credential-osxkeychain skips storing a credential if its "get"
action sets "state[]=osxkeychain:seen=1". This behavior was introduced
in e1ab45b2 (osxkeychain: state to skip unnecessary store operations,
2024-05-15), which appeared in v2.46.

However, this state[] persists even if a credential returned by
"git-credential-osxkeychain get" is invalid and a subsequent helper's
"get" operation returns a valid credential. Another subsequent helper
(such as [1]) may expect git-credential-osxkeychain to store the valid
credential, but the "store" operation is incorrectly skipped because it
only checks "state[]=osxkeychain:seen=1".

To solve this issue, "state[]=osxkeychain:seen" needs to contain enough
information to identify whether the current "store" input matches the
output from the previous "get" operation (and not a credential from
another helper).

Set "state[]=osxkeychain:seen" to a value encoding the credential output
by "get", and compare it with a value encoding the credential input by
"store".

[1]: https://github.com/hickford/git-credential-oauth

Reported-by: Petter Sælen <petter@saelen.eu>
Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Koji Nakamaru <koji.nakamaru@gree.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-14 08:47:54 -08:00
Junio C Hamano
51358a1ede attr: enable incomplete-line whitespace error for this project
Now "git diff --check" and "git apply --whitespace=warn/fix" learned
incomplete line is a whitespace error, enable them for this project
to prevent patches to add new incomplete lines to our source to both
code and documentation files.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-14 08:26:46 -08:00
Taylor Blau
fd372d9b1a RelNotes: fix typo in release notes for 2.52.0
Introduced via aea86cf00f (The nineteenth batch, 2025-10-14).

Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-13 09:34:53 -08:00
Bagas Sanjaya
773b840da1 l10n: po-id for 2.52
Update following components:

  - add-patch.c
  - builtin/bisect.c
  - builtin/describe.c
  - builtin/fast-export.c
  - builtin/fast-import.c
  - builtin/fetch.c
  - builtin/for-each-ref.c
  - builtin/gc.c
  - builtin/log.c
  - builtin/pack-refs.c
  - builtin/range-diff.c
  - builtin/reflog.c
  - builtin/refs.c
  - builtin/remote.c
  - builtin/repo.c
  - builtin/sparse-checkout.c
  - command-list.h
  - config.c
  - diff-lib.c
  - diff.c
  - gpg-interface.c
  - midx-write.c
  - promisor-remote.c
  - range-diff.c
  - refs.c
  - refs/files-backend.c
  - refs/reftable-backend.c
  - remote.c
  - usage.c
  - git-send-email.perl

Translate following new components:

  - builtin/last-modified.c
  - http.h

Signed-off-by: Bagas Sanjaya <bagasdotme@gmail.com>
2025-11-13 09:00:02 +07:00
Junio C Hamano
ab2693cb52 diff: highlight and error out on incomplete lines
Teach "git diff" to highlight "\ No newline at end of file" message
as a whitespace error when incomplete-line whitespace error class is
in effect.  Thanks to the previous refactoring of complete rewrite
code path, we can do this at a single place.

Unlike whitespace errors in the payload where we need to annotate in
line, possibly using colors, the line that has whitespace problems,
we have a dedicated line already that can serve as the error
message, so paint it as a whitespace error message.

Also teach "git diff --check" to notice incomplete lines as
whitespace errors and report when incomplete-line whitespace error
class is in effect.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-12 14:04:05 -08:00
Junio C Hamano
9fb15a8e14 apply: check and fix incomplete lines
The final line of a file that lacks the terminating newline at its
end is called an incomplete line.  In general they are frowned upon
for many reasons (imagine concatenating two files with "cat A B" and
what happens when A ends in an incomplete line, for example), and
text-oriented tools often mishandle such a line.

Implement checks in "git apply" for incomplete lines, which is off
by default for backward compatibility's sake, so that "git apply
--whitespace={fix,warn,error}" can notice, warn against, and fix
them.

As one of the new test shows, if you modify contents on an
incomplete line in the original and leave the resulting line
incomplete, it is still considered a whitespace error, the reasoning
being that "you'd better fix it while at it if you are making a
change on an incomplete line anyway", which may controversial.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-12 14:04:04 -08:00
Junio C Hamano
a675104c39 whitespace: allocate a few more bits and define WS_INCOMPLETE_LINE
Reserve a few more bits in the diff flags word to be used for future
whitespace rules.  Add WS_INCOMPLETE_LINE without implementing the
behaviour (yet).

Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-12 14:04:04 -08:00
Junio C Hamano
3a4eb5ad2e apply: revamp the parsing of incomplete lines
A patch file represents the incomplete line at the end of the file
with two lines, one that is the usual "context" with " " as the
first letter, "added" with "+" as the first letter, or "removed"
with "-" as the first letter that shows the content of the line,
plus an extra "\ No newline at the end of file" line that comes
immediately after it.

Ever since the apply machinery was written, the "git apply"
machinery parses "\ No newline at the end of file" line
independently, without even knowing what line the incomplete-ness
applies to, simply because it does not even remember what the
previous line was.

This poses a problem if we want to check and warn on an incomplete
line.  Revamp the code that parses a fragment, to actually drop the
'\n' at the end of the incoming patch file that terminates a line,
so that check_whitespace() calls made from the code path actually
sees an incomplete as incomplete.

Note that the result of this parsing is not directly used by the
code path that applies the patch.  apply_one_fragment() function
already checks if each of the patch text it handles is followed by a
line that begins with a backslash to drop the newline at the end of
the current line it is looking at.  In a sense, this patch harmonizes
the behaviour of the parsing side to what is already done in the
application side.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-12 14:04:04 -08:00
Junio C Hamano
8d8e3c6187 diff: update the way rewrite diff handles incomplete lines
The diff_symbol based output framework uses one DIFF_SYMBOL_* enum
value per the kind of output lines of "git diff", which corresponds
to one output line from the xdiff machinery used internally.  Most
notably, DIFF_SYMBOL_PLUS and DIFF_SYMBOL_MINUS that correspond to
"+" and "-" lines are designed to always take a complete line, even
if the output from xdiff machinery may produce "\ No newline at the
end of file" immediately after them.

But this is not true in the rewrite-diff codepath, which completely
bypasses the xdiff machinery.  Since the code path feeds the bytes
directly from the payload to the output routines, the output layer
has to deal with an incomplete line with DIFF_SYMBOL_PLUS and
DIFF_SYMBOL_MINUS, which never would see an incomplete line in the
normal code paths.  This lack of final newline is compensated by an
ugly hack for a fabricated DIFF_SYMBOL_NO_LF_EOF token to inject an
extra newline to the output to simulate output coming from the xdiff
machinery.

Revamp the way the complete-rewrite code path feeds the lines to the
output layer by treating the last line of the pre/post image when it
is an incomplete line specially.

This lets us remove the DIFF_SYMBOL_NO_LF_EOF hack and use the usual
DIFF_SYMBOL_CONTEXT_INCOMPLETE code path, which will later learn how
to handle whitespace errors.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-12 14:04:04 -08:00
Junio C Hamano
35925f1832 diff: call emit_callback ecbdata everywhere
Everybody else, except for emit_rewrite_lines(), calls the
emit_callback data ecbdata.  Make sure we call the same thing by
the same name for consistency.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-12 14:04:04 -08:00
Junio C Hamano
29228cbdc5 diff: refactor output of incomplete line
Create a helper function that reacts to "\ No newline at the end of
file" in preparation for unifying the incomplete line handling in
the code path that handles xdiff output and the code path that
bypasses xdiff and produces a complete-rewrite patch.

Currently the output from the DIFF_SYMBOL_CONTEXT_INCOMPLETE case
still (ab)uses the same code as what is used for context lines, but
that would change in a later step where we introduce support to treat
an incomplete line as a whitespace error.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-12 14:04:04 -08:00
Junio C Hamano
ced0561828 diff: keep track of the type of the last line seen
The "\ No newline at the end of the file" can come after any of the
"-" (deleted preimage line), " " (unchanged line), or "+" (added
postimage line).  In later steps in this series, we will start
treating a change that makes a file to end in an incomplete line
as a whitespace error, and we would need to know what the previous
line was when we react to "\ No newline" in the diff output.  If
the previous line was a context (i.e., unchanged) line, the file
lacked the final newline before the change, and the change did not
touch that line and left it still incomplete, so we do not want to
warn in such a case.

Teach fn_out_consume() function to keep track of what the previous
line was, and prepare an otherwise empty switch statement to let us
react differently to "\ No newline" based on that.

Note that there is an existing curiosity (read: likely to be a bug)
in the code that increments line number in the preimage file every
time it sees a line with "\ No newline" on it, regardless of what
the previous line was.  I left it as-is, because it does not affect
the main theme of this series, and more importantly, I do not think
it matters, as these numbers are used only to compare them with
blank_at_eof_in_{pre,post}image to issue a warning when we see more
empty line was added at the end, but by definition, after we see
"\ No newline at the end of the file" for an added line, we will not
see an added line for the file.

An independent audit to ensure that this curious increment can be
safely removed would make a good #leftoverbits clean-up (we may even
find some code that decrements this counter or over-increments the
other quantity this counter is compared with that compensates the
effect of this curious increment that hides a bug, in which case we
may also need to remove them).

Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-12 14:04:04 -08:00
Junio C Hamano
fc7abcd9d5 diff: correct suppress_blank_empty hack
The suppress-blank-empty feature abused the CONTEXT_INCOMPLETE
symbol that was meant to be used only for "\ No newline at the end
of file" code path.

The intent of the feature was to turn a context line we receive from
xdiff machinery (which always uses ' ' for context lines, even an
empty one) and spit it out as a truly empty line.

Perform such a conversion very locally at where a line from xdiff
that begins with ' ' is handled for output; there are many checks
before the control reaches such place that checks the first letter
of the diff output line to see if it is a context line, and having
to check for '\n' and treat it as a special case is error prone.

In order to catch similar hacks in the future, make sure the code
path that is meant for "\ No newline" case checks the first byte is
indeed a backslash.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-12 14:04:04 -08:00
Junio C Hamano
f83d1afafb diff: emit_line_ws_markup() if/else style fix
Apply the simple rule: if you need {} in one arm of the if/else
if/else... cascade, have {} in all of them.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-12 14:04:04 -08:00
Junio C Hamano
8d4725e48e whitespace: correct bit assignment comments
A comment in diff.c claimed that bits up to 12th (counting from 0th)
are whitespace rules, and 13th thru 15th are for new/old/context,
but it turns out it was miscounting.  Correct them, and clarify
where the whitespace rule bits come from in the comment.  Extend bit
assignment comments to cover bits used for color-moved, which
weren't described.

Also update the way these bit constants are defined to use (1 << N)
notation, instead of octal constants, as it tends to make it easier
to notice a breakage like this.

Sprinkle a few blank lines between logically distinct groups of CPP
macro definitions to make them easier to read.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-12 14:04:04 -08:00
Julia Evans
dee80940b1 doc: add an explanation of Git's data model
Git very often uses the terms "object", "reference", or "index" in its
documentation.

However, it's hard to find a clear explanation of these terms and how
they relate to each other in the documentation. The closest candidates
currently are:

1. `gitglossary`. This makes a good effort, but it's an alphabetically
    ordered dictionary and a dictionary is not a good way to learn
    concepts. You have to jump around too much and it's not possible to
    present the concepts in the order that they should be explained.
2. `gitcore-tutorial`. This explains how to use the "core" Git commands.
   This is a nice document to have, but it's not necessary to learn how
   `update-index` works to understand Git's data model, and we should
   not be requiring users to learn how to use the "plumbing" commands
   if they want to learn what the term "index" or "object" means.
3. `gitrepository-layout`. This is a great resource, but it includes a
   lot of information about configuration and internal implementation
   details which are not related to the data model. It also does
   not explain how commits work.

The result of this is that Git users (even users who have been using
Git for 15+ years) struggle to read the documentation because they don't
know what the core terms mean, and it's not possible to add links
to help them learn more.

Add an explanation of Git's data model. Some choices I've made in
deciding what "core data model" means:

1. Omit pseudorefs like `FETCH_HEAD`, because it's not clear to me
   if those are intended to be user facing or if they're more like
   internal implementation details.
2. Don't talk about submodules other than by mentioning how they
   relate to trees. This is because Git has a lot of special features,
   and explaining how they all work exhaustively could quickly go
   down a rabbit hole which would make this document less useful for
   understanding Git's core behaviour.
3. Don't discuss the structure of a commit message
   (first line, trailers etc).
4. Don't mention configuration.
5. Don't mention the `.git` directory, to avoid getting too much into
   implementation details

Signed-off-by: Julia Evans <julia@jvns.ca>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-12 12:21:15 -08:00
Junio C Hamano
99bd5a5c9f Merge branch 'tc/last-modified-active-paths-optimization'
"git last-modified" was optimized by narrowing the set of paths to
follow as it dug deeper in the history.

* tc/last-modified-active-paths-optimization:
  last-modified: implement faster algorithm
2025-11-12 11:45:24 -08:00
Jeff King
42ed046866 attr: avoid recursion when expanding attribute macros
Given a set of attribute macros like:

   [attr]a1 a2
   [attr]a2 a3
   ...
   [attr]a300000 -text
   file a1

expanding the attributes for "file" requires expanding "a1" to "a2",
"a2" to "a3", and so on until hitting a non-macro expansion ("-text", in
this case). We implement this via recursion: fill_one() calls
macroexpand_one(), which then recurses back to fill_one(). As a result,
very deep macro chains like the one above can run out of stack space and
cause us to segfault.

The required stack space is fairly small; I needed on the order of
200,000 entries to get a segfault on Linux. So it's unlikely anybody
would hit this accidentally, leaving only malicious inputs. There you
can easily construct a repo which will segfault on clone (we look at
attributes during the checkout step, but you'd see the same trying to do
other operations, like diff in a bare repo). It's mostly harmless, since
anybody constructing such a repo is only preventing victims from cloning
their evil garbage, but it could be a nuisance for hosting sites.

One option to prevent this is to limit the depth of recursion we'll
allow. This is conceptually easy to implement, but it raises other
questions: what should the limit be, and do we need a configuration knob
for it?

The recursion here is simple enough that we can avoid those questions by
just converting it to iteration instead. Rather than iterate over the
states of a match_attr in fill_one(), we'll put them all in a queue, and
the expansion of each can add to the queue rather than recursing. Note
that this is a LIFO queue in order to keep the same depth-first order we
did with the recursive implementation. I've avoided using the word
"stack" in the code because the term is already heavily used to refer to
the stack of .gitattribute files that matches the tree structure of the
repository.

The test uses a limited stack size so we can trigger the problem with a
much smaller input than the one shown above. The value here (3000) is
enough to trigger the issue on my x86_64 Linux machine.

Reported-by: Ben Stav <benstav@miggo.io>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-12 10:30:04 -08:00
Junio C Hamano
621415c8b5 Git 2.52-rc2
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-12 08:17:31 -08:00
Junio C Hamano
e65e955c03 Merge branch 'dk/make-git-contacts-executable'
Building "git contacts" script (in contrib/) left the resulting
file unexecutable, which has been corrected.

* dk/make-git-contacts-executable:
  perl: also mark git-contacts executable
2025-11-12 08:17:31 -08:00
Junio C Hamano
da5841b45c Merge branch 'dk/meson-html-dir'
The build procedure based on meson learned to allow builders to
specify the directory to install HTML documents.

* dk/meson-html-dir:
  meson: make GIT_HTML_PATH configurable
2025-11-12 08:17:31 -08:00
Junio C Hamano
cb9036aca1 Merge branch 'tu/credential-wincred-makefile-update'
Build procedure for Wincred credential helper has been updated.

* tu/credential-wincred-makefile-update:
  wincred: align Makefile with other Makefiles in contrib
2025-11-12 08:17:31 -08:00
Junio C Hamano
358e94dc70 .gitattributes: remove misspelled no-op whitespace attribute
Ever since 14f9e128 (Define the project whitespace policy,
2008-02-10) added the whitespace rules to .gitattributes, we spelled
the most general rule like so:

    * whitespace=!indent,trail,space

in the top-level .gitattributes file.  The intent of this line was
described in the commit log message:

     - Unless otherwise specified, indent with SP that could be
       replaced with HT are not "bad".  But SP before HT in the
       indent is "bad", and trailing whitespaces are "bad".

It clearly wanted to disable indent-with-non-tab, so !indent is most
likely a misspelt form of '-indent'.  Because indent-with-non-tab
has never been enabled by default, by luck this was not causing any
ill effect.

We could either remove "!indent", or spell it "-indent".  The
immediate effect would be the same.  It would only start to make a
difference when/if we enable indent-with-non-tab by default in
future versions of Git.

Let's take the former option to remove "!indent" from the list.  We
would feel the effect first-hand ourselves before anybody else if we
ever decide to change the built-in default whitespace rules, which
would be hidden from us if we decide to rewrite it to "-indent"
instead.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-11 10:53:37 -08:00
René Scharfe
fa052367ef diff: disable rename detection with --quiet
Detecting renames and copies improves diff's output.  This effort is
wasted if we don't show any.  Disable detection in that case.

This actually fixes the error code when using the options --cached,
--find-copies-harder, --no-ext-diff and --quiet together:
run_diff_index() indirectly calls diff-lib.c::show_modified(), which
queues even non-modified entries using diff_change() because we need
them for copy detection.  diff_change() sets flags.has_changes, though,
which causes diff_can_quit_early() to declare we're done after seeing
only the very first entry -- way too soon.

Using --cached, --find-copies-harder and --quiet together without
--no-ext-diff was not affected even before, as it causes the flag
flags.diff_from_contents to be set, which disables the optimization
in a different way.

Reported-by: D. Ben Knoble <ben.knoble@gmail.com>
Suggested-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-10 11:23:57 -08:00
Karthik Nayak
28b83e6f08 maintenance: add 'is-needed' subcommand
The 'git-maintenance(1)' command provides tooling to run maintenance
tasks over Git repositories. The 'run' subcommand, as the name suggests,
runs the maintenance tasks. When used with the '--auto' flag, it uses
heuristics to determine if the required thresholds are met for running
said maintenance tasks.

There is however a lack of insight into these heuristics. Meaning, the
checks are linked to the execution.

Add a new 'is-needed' subcommand to 'git-maintenance(1)' which allows
users to simply check if it is needed to run maintenance without
performing it.

This subcommand can check if it is needed to run maintenance without
actually running it. Ideally it should be used with the '--auto' flag,
which would allow users to check if the thresholds required are met. The
subcommand also supports the '--task' flag which can be used to check
specific maintenance tasks.

While adding the respective tests in 't/t7900-maintenance.sh', remove a
duplicate of the test: 'worktree-prune task with --auto honors
maintenance.worktree-prune.auto'.

Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
Acked-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-10 09:28:48 -08:00
Karthik Nayak
8c1ce2204c maintenance: add checking logic in pack_refs_condition()
The 'git-maintenance(1)' command supports an '--auto' flag. Usage of the
flag ensures to run maintenance tasks only if certain thresholds are
met. The heuristic is defined on a task level, wherein each task defines
an 'auto_condition', which states if the task should be run.

The 'pack-refs' task is hard-coded to return 1 as:
1. There was never a way to check if the reference backend needs to be
optimized without actually performing the optimization.
2. We can pass in the '--auto' flag to 'git-pack-refs(1)' which would
optimize based on heuristics.

The previous commit added a `refs_optimize_required()` function, which
can be used to check if a reference backend required optimization. Use
this within `pack_refs_condition()`.

This allows us to add a 'git maintenance is-needed' subcommand which can
notify the user if maintenance is needed without actually performing the
optimization. Without this change, the reference backend would always
state that optimization is needed.

Since we import 'revision.h', we need to remove the definition for
'SEEN' which is duplicated in the included header.

Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
Acked-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-10 09:28:48 -08:00
Karthik Nayak
f6c5ca387a refs: add a optimize_required field to struct ref_storage_be
To allow users of the refs namespace to check if the reference backend
requires optimization, add a new field `optimize_required` field to
`struct ref_storage_be`. This field is of type `optimize_required_fn`
which is also introduced in this commit.

Modify the debug, files, packed and reftable backend to implement this
field. A following commit will expose this via 'git pack-refs' and 'git
refs optimize'.

Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
Acked-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-10 09:28:48 -08:00
Karthik Nayak
e35155588a reftable/stack: add function to check if optimization is required
The reftable backend performs auto-compaction as part of its regular
flow, which is required to keep the number of tables part of a stack at
bay. This allows it to stay optimized.

Compaction can also be triggered voluntarily by the user via the 'git
pack-refs' or the 'git refs optimize' command. However, currently there
is no way for the user to check if optimization is required without
actually performing it.

Extract out the heuristics logic from 'reftable_stack_auto_compact()'
into an internal function 'update_segment_if_compaction_required()'.
Then use this to add and expose `reftable_stack_compaction_required()`
which will allow users to check if the reftable backend can be
optimized.

Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
Acked-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-10 09:28:47 -08:00
Karthik Nayak
135f491f83 reftable/stack: return stack segments directly
The `stack_table_sizes_for_compaction()` function returns individual
sizes of each reftable table. This function is only called by
`reftable_stack_auto_compact()` to decide which tables need to be
compacted, if any.

Modify the function to directly return the segments, which avoids the
extra step of receiving the sizes only to pass it to
`suggest_compaction_segment()`.

A future commit will also add functionality for checking whether
auto-compaction is necessary without performing it. This change allows
code re-usability in that context.

Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
Acked-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-10 09:28:47 -08:00
Aindriú Mac Giolla Eoin
2c8999027c l10n: ga.po: Update Irish translation for Git 2.52
Refreshes the Irish translation for Git 2.52, including new strings and
consistency improvements. Verified with `git-po-helper check`.

Signed-off-by: Aindriú Mac Giolla Eoin <aindriu80@gmail.com>
2025-11-10 10:39:35 +00:00
Alexander Shopov
44030a90b2 l10n: bg.po: Updated Bulgarian translation (6065t)
Signed-off-by: Alexander Shopov <ash@kambanaria.org>
2025-11-09 18:28:21 +01:00
Jean-Noël Avila
95bc4ee7c3 l10n: fr: version 2.52
Signed-off-by: Jean-Noël Avila <jn.avila@free.fr>
2025-11-09 14:58:27 +01:00
Peter Krefting
b095b7d159 l10n: sv.po: Update Swedish translation
Signed-off-by: Peter Krefting <peter@softwolves.pp.se>
2025-11-07 15:54:20 +01:00
Junio C Hamano
4badef0c35 Merge branch 'dk/parseopt-optional-filename-fixes'
A recently added configuration variable and command line option
syntax ":(optional)" for values that are of filename type
inconsistently behaved on an empty file (configuration took it
happily, while the command line option pretended as if it did not
exist), which has been corrected.

* dk/parseopt-optional-filename-fixes:
  parseopt: remove unreachable code
  parseopt: restore const qualifier to parsed filename
  config: use boolean type for a simple flag
  parseopt: use boolean type for a simple flag
  doc: clarify command equivalence comment
  parseopt: fix :(optional) at command line to only ignore missing files
2025-11-06 15:17:01 -08:00
Junio C Hamano
e569dced68 Merge branch 'cc/fast-import-export-i18n-cleanup'
Messages from fast-import/export are now marked for i18n.

* cc/fast-import-export-i18n-cleanup:
  gpg-interface: mark a string for translation
  fast-import: mark strings for translation
  fast-export: mark strings for translation
  gpg-interface: use left shift to define GPG_VERIFY_*
  gpg-interface: simplify ssh fingerprint parsing
2025-11-06 15:17:01 -08:00
Junio C Hamano
5db9d35a28 Merge branch 'js/ci-github-actions-update'
CI updates.

* js/ci-github-actions-update:
  ci: update {download,upload}-artifact Action versions
2025-11-06 14:52:57 -08:00
Junio C Hamano
f58ea683b5 Merge branch 'pk/reflog-migrate-message-fix'
Message fix.

* pk/reflog-migrate-message-fix:
  refs: add missing space in messages
2025-11-06 14:52:57 -08:00
Patrick Steinhardt
7048e74609 object: fix performance regression when peeling tags
Our Bencher dashboards [1] have recently alerted us about a bunch of
performance regressions when writing references, specifically with the
reftable backend. There is a 3x regression when writing many refs with
preexisting refs in the reftable format, and a 10x regression when
migrating refs between backends in either of the formats.

Bisecting the issue lands us at 6ec4c0b45b (refs: don't store peeled
object IDs for invalid tags, 2025-10-23). The gist of the commit is that
we may end up storing peeled objects in both reftables and packed-refs
for corrupted tags, where the claimed tagged object type is different
than the actual tagged object type. This will then cause us to create
the `struct object *` with a wrong type, as well, and obviously nothing
good comes out of that.

The fix for this issue was to introduce a new flag to `peel_object()`
that causes us to verify the tagged object's type before writing it into
the refdb -- if the tag is corrupt, we skip writing the peeled value.
To verify whether the peeled value is correct we have to look up the
object type via the ODB and compare the actual type with the claimed
type, and that additional object lookup is costly.

This also explains why we see the regression only when writing refs with
the reftable backend, but we see the regression with both backends when
migrating refs:

  - The reftable backend knows to store peeled values in the new table
    immediately, so it has to try and peel each ref it's about to write
    to the transaction. So the performance regression is visible for all
    writes.

  - The files backend only stores peeled values when writing the
    packed-refs file, so it wouldn't hit the performance regression for
    normal writes. But on ref migrations we know to write all new values
    into the packed-refs file immediately, and that's why we see the
    regression for both backends there.

Taking a step back though reveals an oddity in the new verification
logic: we not only verify the _tagged_ object's type, but we also verify
the type of the tag itself. But this isn't really needed, as we wouldn't
hit the bug in such a case anyway, as we only hit the issue with corrupt
tags claiming an invalid type for the tagged object.

The consequence of this is that we now started to look up the target
object of every single reference we're about to write, regardless of
whether it even is a tag or not. And that is of course quite costly.

Fix the issue by only verifying the type of the tagged objects. This
means that we of course still have a performance hit for actual tags.
But this only happens for writes anyway, and I'd claim it's preferable
to not store corrupted data in the refdb than to be fast here. Rename
the flag accordingly to clarify that we only verify the tagged object's
type.

This fix brings performance back to previous levels:

    Benchmark 1: baseline
      Time (mean ± σ):      46.0 ms ±   0.4 ms    [User: 40.0 ms, System: 5.7 ms]
      Range (min … max):    45.0 ms …  47.1 ms    54 runs

    Benchmark 2: regression
      Time (mean ± σ):     140.2 ms ±   1.3 ms    [User: 77.5 ms, System: 60.5 ms]
      Range (min … max):   138.0 ms … 142.7 ms    20 runs

    Benchmark 3: fix
      Time (mean ± σ):      46.2 ms ±   0.4 ms    [User: 40.2 ms, System: 5.7 ms]
      Range (min … max):    45.0 ms …  47.3 ms    55 runs

    Summary
      update-ref: baseline
        1.00 ± 0.01 times faster than fix
        3.05 ± 0.04 times faster than regression

[1]: https://bencher.dev/perf/git/plots

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-06 10:54:34 -08:00
Junio C Hamano
994869e2b5 Merge branch 'ps/ref-peeled-tags' into ps/ref-peeled-tags-fixes
* ps/ref-peeled-tags:
  t7004: do not chdir around in the main process
  ref-filter: fix stale parsed objects
  ref-filter: parse objects on demand
  ref-filter: detect broken tags when dereferencing them
  refs: don't store peeled object IDs for invalid tags
  object: add flag to `peel_object()` to verify object type
  refs: drop infrastructure to peel via iterators
  refs: drop `current_ref_iter` hack
  builtin/show-ref: convert to use `reference_get_peeled_oid()`
  ref-filter: propagate peeled object ID
  upload-pack: convert to use `reference_get_peeled_oid()`
  refs: expose peeled object ID via the iterator
  refs: refactor reference status flags
  refs: fully reset `struct ref_iterator::ref` on iteration
  refs: introduce `.ref` field for the base iterator
  refs: introduce wrapper struct for `each_ref_fn`
2025-11-06 10:54:28 -08:00
Johannes Schindelin
8d71696686 ci: update {download,upload}-artifact Action versions
Bumps `actions/upload-artifact` from 4 to 5.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v5)

Bumps `actions/download-artifact` from 5 to 6.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v5...v6)

Originally-authored-by: dependabot[bot] <support@github.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-06 10:37:45 -08:00
Tobias Boesch
bdb1cf8312 gitk: add external diff file rename detection
If a file is renamed between commits and an external diff is started
through gitk on the original or the renamed file name,
gitk is unable to open the renamed file in the external diff editor.
It fails to fetch the renamed file from git, because it fetches it
using its original path in contrast to using the renamed path of the
file.
Detect the rename and open the external diff with the original and
the renamed file instead of no file (fetch the renamed file path and
name from git) no matter if the original or the renamed file is
selected in gitk.

Signed-off-by: Tobias Boesch <tobias.boesch@miele.com>
Signed-off-by: Johannes Sixt <j6t@kdbg.org>
2025-11-06 19:03:26 +01:00
D. Ben Knoble
d63417e3ad meson: make GIT_HTML_PATH configurable
Makefile-based builds can configure Git's internal HTML_PATH by defining
htmldir, which is useful for packagers that put documentation in
different locations. Gentoo, for example, uses version-suffixed
directories like ${prefix}/share/doc/git-2.51 and puts the HTML
documentation in an 'html' subdirectory of the same.

Propagate the same configuration knob to Meson-based builds so that
"git --html-path" on such systems can be configured to output the
correct directory.

Signed-off-by: D. Ben Knoble <ben.knoble+github@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-06 09:58:56 -08:00
D. Ben Knoble
38419bdd45 perl: also mark git-contacts executable
When installing git-contacts with Meson via -Dcontrib=contacts, the default
Perl generation fails to mark it executable. As a result, "git contacts"
reports "'contacts' is not a git command."

Unlike generate-script.sh, we aren't testing the basename here; so, glob
the script name in the case arm to match wherever the input comes from.

Signed-off-by: D. Ben Knoble <ben.knoble+github@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-06 09:57:47 -08:00
Thomas Uhle
fade8f074e wincred: align Makefile with other Makefiles in contrib
* Replace $(LOADLIBES) because it is deprecated since long and it is
  used nowhere else in the git project.
* Use $(gitexecdir) instead of $(libexecdir) because config.mak defines
  $(libexecdir) as $(prefix)/libexec, not as $(prefix)/libexec/git-core.
* Similar to other Makefiles, let install target rule create
  $(gitexecdir) to make sure the directory exists before copying the
  executable and also let it respect $(DESTDIR).
* Shuffle the lines for the default settings to align them with the
  other Makefiles in contrib/credential.
* Define .PHONY for all special targets (all, install, clean).

Signed-off-by: Thomas Uhle <thomas.uhle@mailbox.tu-dresden.de>
Acked-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-06 09:56:30 -08:00
Queen Ediri Jessa
46207a54cc doc: clarify server behavior for invalid 'want' lines in HTTP protocol
Update the documentation to clearly describe how the server responds when a
client sends an invalid or malformed `want` line during the HTTP protocol
exchange. The server includes the offending object name in its error message.

Signed-off-by: Queen Ediri Jessa <qjessa662@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-06 09:45:38 -08:00
Johannes Sixt
d445a78873 gitk: show unescaped file names on 'rename' and 'copy' lines
When a file is selected in the file list, the diff window scrolls to the
corresponding section. The administrative data needed for this purpose
is extracted from the 'rename from', 'rename to', and 'copy to' lines.
Escaped file names are unescaped for this purpose. However, the lines
shown in the diff window are left in the escaped form. This is not very
pleasing. Replace the escaped form by the unescaped form.

Add a section to treat the 'copy from' case.

Signed-off-by: Johannes Sixt <j6t@kdbg.org>
2025-11-06 10:59:02 +01:00
Johannes Sixt
77e7aab693 gitk: fix a 'continue' statement outside a loop to 'return'
When 5de460a2cfdd (gitk: Refactor per-line part of getblobdiffline and
its support) moved the body of a loop into a separate function, several
'continue' statements were changed to 'return'. But one instance was
missed. Fix it now.

Signed-off-by: Johannes Sixt <j6t@kdbg.org>
2025-11-06 10:59:02 +01:00
Peter Krefting
d9988b063f refs: add missing space in messages
Signed-off-by: Peter Krefting <peter@softwolves.pp.se>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-05 15:04:26 -08:00
Junio C Hamano
77b7284cca Git 2.52-rc1
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-05 13:41:52 -08:00
Junio C Hamano
9a18a7449d Merge branch 'jc/ci-use-macos-14'
The version of macos image used in GitHub CI has been updated to
macos-14, as the macos-13 that we have been using got deprecated.

* jc/ci-use-macos-14:
  GitHub CI: macos-13 images are no more
2025-11-05 13:41:51 -08:00
Junio C Hamano
c8a641c590 Merge branch 'rz/t0450-bisect-doc-update'
The help text and manual page of "git bisect" command have been
made consistent with each other.

* rz/t0450-bisect-doc-update:
  bisect: update usage and docs to match each other
2025-11-05 13:41:51 -08:00
Siddharth Asthana
336ac90c06 replay: add replay.refAction config option
Add a configuration variable to control the default behavior of git replay
for updating references. This allows users who prefer the traditional
pipeline output to set it once in their config instead of passing
--ref-action=print with every command.

The config variable uses string values that mirror the behavior modes:
  * replay.refAction = update (default): atomic ref updates
  * replay.refAction = print: output commands for pipeline

Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Elijah Newren <newren@gmail.com>
Helped-by: Christian Couder <christian.couder@gmail.com>
Helped-by: Phillip Wood <phillip.wood123@gmail.com>
Signed-off-by: Siddharth Asthana <siddharthasthana31@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-05 13:34:55 -08:00
Siddharth Asthana
15cd4ef1f4 replay: make atomic ref updates the default behavior
The git replay command currently outputs update commands that can be
piped to update-ref to achieve a rebase, e.g.

  git replay --onto main topic1..topic2 | git update-ref --stdin

This separation had advantages for three special cases:
  * it made testing easy (when state isn't modified from one step to
    the next, you don't need to make temporary branches or have undo
    commands, or try to track the changes)
  * it provided a natural can-it-rebase-cleanly (and what would it
    rebase to) capability without automatically updating refs, similar
    to a --dry-run
  * it provided a natural low-level tool for the suite of hash-object,
    mktree, commit-tree, mktag, merge-tree, and update-ref, allowing
    users to have another building block for experimentation and making
    new tools

However, it should be noted that all three of these are somewhat
special cases; users, whether on the client or server side, would
almost certainly find it more ergonomic to simply have the updating
of refs be the default.

For server-side operations in particular, the pipeline architecture
creates process coordination overhead. Server implementations that need
to perform rebases atomically must maintain additional code to:

  1. Spawn and manage a pipeline between git-replay and git-update-ref
  2. Coordinate stdout/stderr streams across the pipe boundary
  3. Handle partial failure states if the pipeline breaks mid-execution
  4. Parse and validate the update-ref command output

Change the default behavior to update refs directly, and atomically (at
least to the extent supported by the refs backend in use). This
eliminates the process coordination overhead for the common case.

For users needing the traditional pipeline workflow, add a new
--ref-action=<mode> option that preserves the original behavior:

  git replay --ref-action=print --onto main topic1..topic2 | git update-ref --stdin

The mode can be:
  * update (default): Update refs directly using an atomic transaction
  * print: Output update-ref commands for pipeline use

Test suite changes:

All existing tests that expected command output now use
--ref-action=print to preserve their original behavior. This keeps
the tests valid while allowing them to verify that the pipeline workflow
still works correctly.

New tests were added to verify:
  - Default atomic behavior (no output, refs updated directly)
  - Bare repository support (server-side use case)
  - Equivalence between traditional pipeline and atomic updates
  - Real atomicity using a lock file to verify all-or-nothing guarantee
  - Test isolation using test_when_finished to clean up state
  - Reflog messages include replay mode and target

A following commit will add a replay.refAction configuration
option for users who prefer the traditional pipeline output as their
default behavior.

Helped-by: Elijah Newren <newren@gmail.com>
Helped-by: Patrick Steinhardt <ps@pks.im>
Helped-by: Christian Couder <christian.couder@gmail.com>
Helped-by: Phillip Wood <phillip.wood123@gmail.com>
Signed-off-by: Siddharth Asthana <siddharthasthana31@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-05 13:34:55 -08:00
Siddharth Asthana
e031fa1006 replay: use die_for_incompatible_opt2() for option validation
In preparation for adding the --ref-action option, convert option
validation to use die_for_incompatible_opt2(). This helper provides
standardized error messages for mutually exclusive options.

The following commit introduces --ref-action which will be incompatible
with certain other options. Using die_for_incompatible_opt2() now means
that commit can cleanly add its validation using the same pattern,
keeping the validation logic consistent and maintainable.

This also aligns git-replay's option handling with how other Git commands
manage option conflicts, using the established die_for_incompatible_opt*()
helper family.

Signed-off-by: Siddharth Asthana <siddharthasthana31@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-05 13:34:55 -08:00
Junio C Hamano
73b9cdb7c4 GitHub CI: macos-13 images are no more
As this image was deprecated on Sep 22nd, and will be dropped on Dec
4th, replace these jobs to use macos-14 images instead.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-04 19:50:10 -08:00
Junio C Hamano
a2584d0434 parseopt: remove unreachable code
At this point in the code after running skip_prefix() on the
variable and receiving the result in the same variable, the contents
of the variable can never be NULL.  The function either (1) updates
the variable to point at a later part of the string it originally
pointed at, or (2) leaves it intact if the string does not have the
prefix.  (1) will never make the variable NULL, and (2) cannot be
the source of NULL, because the variable cannot be NULL before
calling skip_prefix(), which would die immediately by dereferencing
the NULL pointer in that case.

Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-04 09:36:10 -08:00
D. Ben Knoble
383e5e1c4b parseopt: restore const qualifier to parsed filename
This was unintentionally dropped in ccfcaf399f (parseopt: values of
pathname type can be prefixed with :(optional), 2025-09-28). Notably,
continue dropping the const qualifier when free'ing value; see
4049b9cfc0 (fix const issues with some functions, 2007-10-16) or
83838d5c1b (cast variable in call to free() in builtin/diff.c and
submodule.c, 2011-11-06) for more details on why.

Suggested-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: D. Ben Knoble <ben.knoble+github@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-04 09:25:52 -08:00
D. Ben Knoble
4dbb7f4f82 config: use boolean type for a simple flag
Suggested-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: D. Ben Knoble <ben.knoble+github@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-04 09:25:51 -08:00
D. Ben Knoble
4da5bebc17 parseopt: use boolean type for a simple flag
Suggested-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: D. Ben Knoble <ben.knoble+github@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-04 09:25:51 -08:00
D. Ben Knoble
2fd151af13 doc: clarify command equivalence comment
Documentation of command parsing for :(optional) includes a terse
comment; expand it to be clearer to readers.

Suggested-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: D. Ben Knoble <ben.knoble+github@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-04 09:25:51 -08:00
D. Ben Knoble
aece3bc266 parseopt: fix :(optional) at command line to only ignore missing files
Unlike the configuration option magic, the parseopt code also ignores
empty files: compare implementations from ccfcaf399f (parseopt: values
of pathname type can be prefixed with :(optional), 2025-09-28) and
749d6d166d (config: values of pathname type can be prefixed with
:(optional), 2025-09-28).

Unify the 2 by not ignoring empty files, which is less surprising and
the intended semantics from the first patch for config.

Suggested-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: D. Ben Knoble <ben.knoble+github@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-04 09:25:51 -08:00
Junio C Hamano
4cf919bd7b A bit more before rc1
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-04 07:48:10 -08:00
Junio C Hamano
5931b6b2fb Merge branch 'jk/doc-backslash-in-exclude'
The patterns used in the .gitignore files use backslash in the way
documented for fnmatch(3); document as such to reduce confusion.

* jk/doc-backslash-in-exclude:
  doc: document backslash in gitignore patterns
2025-11-04 07:48:10 -08:00
Junio C Hamano
377e8e2848 Merge branch 'jk/test-delete-gpgsig-leakfix'
Leakfix.

* jk/test-delete-gpgsig-leakfix:
  test-tool: fix leak in delete-gpgsig command
2025-11-04 07:48:09 -08:00
Junio C Hamano
55e8615d18 Merge branch 'eb/t1016-hash-transition-fix'
Test fix.

* eb/t1016-hash-transition-fix:
  t1016-compatObjectFormat: really freeze time for reproduciblity
2025-11-04 07:48:09 -08:00
Junio C Hamano
a82fd5067c Merge branch 'kh/doc-checkout-markup-fix'
Doc mark-up fix.

* kh/doc-checkout-markup-fix:
  doc: git-checkout: fix placeholder markup
2025-11-04 07:48:08 -08:00
Junio C Hamano
517964205c Merge branch 'xr/ref-debug-remove-on-disk'
The "debug" ref-backend was missing a method implementation, which
has been corrected.

* xr/ref-debug-remove-on-disk:
  refs: add missing remove_on_disk implementation for debug backend
2025-11-04 07:48:08 -08:00
Junio C Hamano
aa61d1f40f Merge branch 'qj/doc-my1stcontrib-email-verify'
The "MyFirstContribution" tutorial tells the reader how to send out
their patches; the section gained a hint to verify the message
reached the mailing list.

* qj/doc-my1stcontrib-email-verify:
  MyFirstContribution: add note on confirming patches
2025-11-04 07:48:08 -08:00
Junio C Hamano
8f0d663eac Merge branch 'tz/test-prepare-gnupghome'
Tests did not set up GNUPGHOME correctly, which is fixed but some
flaky tests are exposed in t1016, which needs to be addressed
before this topic can move forward.

* tz/test-prepare-gnupghome:
  t/lib-gpg: call prepare_gnupghome() in GPG2 prereq
  t/lib-gpg: add prepare_gnupghome() to create GNUPGHOME dir
2025-11-04 07:48:07 -08:00
Junio C Hamano
a9db6c66f5 Merge branch 'jt/repo-structure'
"git repo structure", a new command.

* jt/repo-structure:
  builtin/repo: add progress meter for structure stats
  builtin/repo: add keyvalue and nul format for structure stats
  builtin/repo: add object counts in structure output
  builtin/repo: introduce structure subcommand
  ref-filter: export ref_kind_from_refname()
  ref-filter: allow NULL filter pattern
  builtin/repo: rename repo_info() to cmd_repo_info()
2025-11-04 07:48:07 -08:00
Junio C Hamano
175048344f Merge branch 'tu/credential-install'
Contributed credential helpers (obviously in contrib/) now have "cd
$there && make install" target.

* tu/credential-install:
  contrib/credential: add install target
2025-11-04 07:48:06 -08:00
Junio C Hamano
3012e5b650 Merge branch 'cc/doc-submitting-patches-with-ai'
AI guidelines.

* cc/doc-submitting-patches-with-ai:
  SubmittingPatches: add section about AI
2025-11-04 07:48:06 -08:00
Junio C Hamano
31177a8bb6 Merge branch 'kn/refs-optim-cleanup' into kn/maintenance-is-needed
* kn/refs-optim-cleanup:
  t/pack-refs-tests: move the 'test_done' to callees
  refs: rename 'pack_refs_opts' to 'refs_optimize_opts'
  refs: move to using the '.optimize' functions
2025-11-04 07:38:48 -08:00
Junio C Hamano
4a1442a336 Merge branch 'ps/ref-peeled-tags' into kn/maintenance-is-needed
* ps/ref-peeled-tags: (23 commits)
  t7004: do not chdir around in the main process
  ref-filter: fix stale parsed objects
  ref-filter: parse objects on demand
  ref-filter: detect broken tags when dereferencing them
  refs: don't store peeled object IDs for invalid tags
  object: add flag to `peel_object()` to verify object type
  refs: drop infrastructure to peel via iterators
  refs: drop `current_ref_iter` hack
  builtin/show-ref: convert to use `reference_get_peeled_oid()`
  ref-filter: propagate peeled object ID
  upload-pack: convert to use `reference_get_peeled_oid()`
  refs: expose peeled object ID via the iterator
  refs: refactor reference status flags
  refs: fully reset `struct ref_iterator::ref` on iteration
  refs: introduce `.ref` field for the base iterator
  refs: introduce wrapper struct for `each_ref_fn`
  builtin/repo: add progress meter for structure stats
  builtin/repo: add keyvalue and nul format for structure stats
  builtin/repo: add object counts in structure output
  builtin/repo: introduce structure subcommand
  ...
2025-11-04 07:38:27 -08:00
Karthik Nayak
c113f4ca4d t/pack-refs-tests: move the 'test_done' to callees
In ac0bad0af4 (t0601: refactor tests to be shareable, 2025-09-19), we
refactored 't/t0601-reffiles-pack-refs.sh' to move all of the tests to
't/pack-refs-tests.sh', which became a common test suite which was also
used by 't/t1463-refs-optimize.sh'.

This also moved the 'test_done' directive to 't/pack-refs-tests.sh'.
Which inhibits additional tests from being added to either of the tests.
Let's move the directive out to both the tests, so that we can add
additional specific tests to them. Also the test flow logic shouldn't be
part of tests which can be embedded in other test scripts.

Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-04 07:35:12 -08:00
Karthik Nayak
2cd99d9841 refs: rename 'pack_refs_opts' to 'refs_optimize_opts'
The previous commit removed all references to 'pack_refs()' within
the refs subsystem. Continue this cleanup by also renaming
'pack_refs_opts' to 'refs_optimize_opts' and the respective flags
accordingly. Keeping the naming consistent will make the code easier to
maintain.

Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-04 07:35:12 -08:00
Karthik Nayak
9b93ab8a9c refs: move to using the '.optimize' functions
The `struct ref_store` variable exposes two ways to optimize a reftable
backend:

  1. pack_refs
  2. optimize

The former was specific to the 'files' + 'packed' refs backend. The
latter is more generic and covers all backends. While the naming is
different, both of these functions perform the same functionality.

Consolidate this code to only maintain the 'optimize' functions. Do this
by modifying the backends so that they exclusively implement the
`optimize` callback, only. All users of the refs subsystem already use
the 'optimize' function so there is no changes needed on the callee
side. Finally, cleanup all references to the 'pack_refs' field of the
structure and code around it.

Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-04 07:35:12 -08:00
Junio C Hamano
aec5adb4b7 Merge branch 'ps/ref-peeled-tags' into kn/refs-optim-cleanup
* ps/ref-peeled-tags: (92 commits)
  t7004: do not chdir around in the main process
  ref-filter: fix stale parsed objects
  ref-filter: parse objects on demand
  ref-filter: detect broken tags when dereferencing them
  refs: don't store peeled object IDs for invalid tags
  object: add flag to `peel_object()` to verify object type
  refs: drop infrastructure to peel via iterators
  refs: drop `current_ref_iter` hack
  builtin/show-ref: convert to use `reference_get_peeled_oid()`
  ref-filter: propagate peeled object ID
  upload-pack: convert to use `reference_get_peeled_oid()`
  refs: expose peeled object ID via the iterator
  refs: refactor reference status flags
  refs: fully reset `struct ref_iterator::ref` on iteration
  refs: introduce `.ref` field for the base iterator
  refs: introduce wrapper struct for `each_ref_fn`
  builtin/repo: add progress meter for structure stats
  builtin/repo: add keyvalue and nul format for structure stats
  builtin/repo: add object counts in structure output
  builtin/repo: introduce structure subcommand
  ...
2025-11-04 07:33:41 -08:00
Junio C Hamano
61ac8ba0f0 t7004: do not chdir around in the main process
Move down to no-contains subdirectory inside a subshell, just like
the previous step that created and used it does.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-04 07:32:25 -08:00
Patrick Steinhardt
bea37f1d64 ref-filter: fix stale parsed objects
In 054f5f457e (ref-filter: parse objects on demand, 2025-10-23) we have
started to skip parsing some objects in case we don't need to access
their values in the first place. This was done by introducing a new
member `struct expand_data::maybe_object` that gets populated on demand
via `get_or_parse_object()`.

This has led to a regression though where the object now gets reused
because we don't reset it properly. The `oi` structure is declared in
global scope, and there is no single place where we reset it before
invoking `get_object()`. The consequence is that the `maybe_object`
member doesn't get reset across calls, so subsequent calls will end up
reusing the same object.

This is only an issue for a subset of retrieved values, as not all of
the infrastructure ends up calling `get_or_parse_object()`. So the
effect is limited, which is probably why the issue wasn't detected
earlier.

Fix the issue by resetting `maybe_object` in `get_object()`.

Reported-by: Junio C Hamano <gitster@pobox.com>
Based-on-patch-by: Jeff King <peff@peff.net>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-04 07:32:25 -08:00
Patrick Steinhardt
a29e2e8fe7 ref-filter: parse objects on demand
When formatting an arbitrary object we parse that object regardless of
whether or not we actually need any parsed data. In fact, many of the
atoms we have don't require any.

Refactor the code so that we parse the data on demand when we see an
atom that wants to access the objects. This leads to a small speedup,
for example in the Chromium repository with around 40000 refs:

    Benchmark 1: for-each-ref --format='%(raw)' (HEAD~)
      Time (mean ± σ):     388.7 ms ±   1.1 ms    [User: 322.2 ms, System: 65.0 ms]
      Range (min … max):   387.3 ms … 390.8 ms    10 runs

    Benchmark 2: for-each-ref --format='%(raw)' (HEAD)
      Time (mean ± σ):     344.7 ms ±   0.7 ms    [User: 287.8 ms, System: 55.1 ms]
      Range (min … max):   343.9 ms … 345.7 ms    10 runs

    Summary
      for-each-ref --format='%(raw)' (HEAD) ran
        1.13 ± 0.00 times faster than for-each-ref --format='%(raw)' (HEAD~)

With this change, we now spend ~90% of the time decompressing objects,
which is almost as good as it gets regarding git-for-each-ref(1)'s own
infrastructure.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-04 07:32:25 -08:00
Patrick Steinhardt
e66077ae45 ref-filter: detect broken tags when dereferencing them
Users can ask git-for-each-ref(1) to peel tags and return information of
the tagged object by adding an asterisk to the format, like for example
"%(*$objectname)". If so, git-for-each-ref(1) peels that object to the
first non-tag object and then returns its values.

As mentioned in preceding commits, it can happen that the tagged object
type and the claimed object type differ, effectively resulting in a
corrupt tag. git-for-each-ref(1) would notice this mismatch, print an
error and then bail out when trying to peel the tag.

But we only notice this corruption in some very specific edge cases!
While we have a test in "t/for-each-ref-tests.sh" that verifies the
above scenario, this test is specifically crafted to detect the issue at
hand. Namely, we create two tags:

  - One tag points to a specific object with the correct type.

  - The other tag points to the *same* object with a different type.

The fact that both tags point to the same object is important here:
`peel_object()` wouldn't notice the corruption if the tagged objects
were different.

The root cause is that `peel_object()` calls `lookup_${type}()`
eventually, where the type is the same type declared in the tag object.
Consequently, when we have two tags pointing to the same object but with
different declared types we'll call two different lookup functions. The
first lookup will store the object with an unverified type A, whereas
the second lookup will try to look up the object with a different
unverified type B. And it is only now that we notice the discrepancy in
object types, even though type A could've already been the wrong type.

Fix the issue by verifying the object type in `populate_value()`. With
this change we'll also notice type mismatches when only dereferencing a
tag once.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-04 07:32:25 -08:00
Patrick Steinhardt
6ec4c0b45b refs: don't store peeled object IDs for invalid tags
Both the "files" and "reftable" backend store peeled object IDs for
references that point to tags:

  - The "files" backend stores the value when packing refs, where each
    peeled object ID is prefixed with "^".

  - The "reftable" backend stores the value whenever writing a new
    reference that points to a tag via a special ref record type.

Both of these backends use `peel_object()` to find the peeled object ID.
But as explained in the preceding commit, that function does not detect
the case where the tag's tagged object and its claimed type mismatch.

The consequence of storing these bogus peeled object IDs is that we're
less likely to detect such corruption in other parts of Git.
git-for-each-ref(1) for example does not notice anymore that the tag is
broken when using "--format=%(*objectname)" to dereference tags.

One could claim that this is good, because it still allows us to mostly
use the tag as intended. But the biggest problem here is that we now
have different behaviour for such a broken tag depending on whether or
not we have its peeled value in the refdb.

Fix the issue by verifying the object type when peeling the object. If
that verification fails we simply skip storing the peeled value in
either of the reference formats.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-04 07:32:25 -08:00
Patrick Steinhardt
7ec85185b1 object: add flag to peel_object() to verify object type
When peeling a tag to a non-tag object we repeatedly call
`parse_object()` on the tagged object until we find the first object
that isn't a tag. While this feels sensible at first, there is a big
catch here: `parse_object()` doesn't actually verify the type of the
tagged object.

The relevant code path here eventually ends up in `parse_tag_buffer()`.
Here, we parse the various fields of the tag, including the "type". Once
we've figured out the type and the tagged object ID, we call one of the
`lookup_${type}()` functions for whatever type we have found. There is
two possible outcomes in the successful case:

  1. The object is already part of our cached objects. In that case we
     double-check whether the type we're trying to look up matches the
     type that was cached.

  2. The object is _not_ part of our cached objects. In that case, we
     simply create a new object with the expected type, but we don't
     parse that object.

In the first case we might notice type mismatches, but only in the case
where our cache has the object with the correct type. In the second
case, we'll blindly assume that the type is correct and then go with it.
We'll only notice that the type might be wrong when we try to parse the
object at a later point.

Now arguably, we could change `parse_tag_buffer()` to verify the tagged
object's type for us. But that would have the effect that such a tag
cannot be parsed at all anymore, and we have a small bunch of tests for
exactly this case that assert we still can open such tags. So this
change does not feel like something we can retroactively tighten, even
though one shouldn't ever hit such corrupted tags.

Instead, add a new `flags` field to `peel_object()` that allows the
caller to opt in to strict object verification. This will be wired up at
a subset of callsites over the next few commits.

Note that this change also inlines `deref_tag_noverify()`. There's only
been two callsites of that function, the one we're changing and one in
our test helpers. The latter callsite can trivially use `deref_tag()`
instead, so by inlining the function we avoid having to pass down the
flag.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-04 07:32:25 -08:00
Patrick Steinhardt
705114772e refs: drop infrastructure to peel via iterators
Now that the peeled object ID gets propagated via the `struct reference`
there is no need anymore to call into the reference iterator itself to
dereference an object. Remove this infrastructure.

Most of the changes are straight-forward deletions of code. There is one
exception though in `refs/packed-backend.c::write_with_updates()`. Here
we stop peeling the iterator and instead just pass the peeled object ID
of that iterator directly.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-04 07:32:25 -08:00
Patrick Steinhardt
5a5c7359f7 refs: drop current_ref_iter hack
In preceding commits we have refactored all callers of
`peel_iterated_oid()` to instead use `reference_get_peeled_oid()`. This
allows us to thus get rid of the former function.

Getting rid of that function is nice, but even nicer is that this also
allows us to get rid of the `current_ref_iter` hack. This global
variable tracked the currently-active ref iterator so that we can use it
to peel an object ID. Now that the peeled object ID is propagated via
`struct reference` though we don't have to depend on this hack anymore,
which makes for a more robust and easier-to-understand infrastructure.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-04 07:32:25 -08:00
Patrick Steinhardt
feaaea4c12 builtin/show-ref: convert to use reference_get_peeled_oid()
The git-show-ref(1) command has multiple different modes:

  - It knows to show all references matching a pattern.

  - It knows to list all references that are an exact match to whatever
    the user has provided.

  - It knows to check for reference existence.

The first two commands use mostly the same infrastructure to print the
references via `show_one()`. But while the former mode uses a proper
iterator and thus has a `struct reference` available in its context, the
latter calls `refs_read_ref()` and thus doesn't. Consequently, we cannot
easily use `reference_get_peeled_oid()` to print the peeled value.

Adapt the code so that we manually construct a `struct reference` when
verifying refs. We wouldn't ever have the peeled value available anyway
as we're not using an iterator here, so we can simply plug in the values
we _do_ have.

With this change we now have a `struct reference` available at both
callsites of `show_one()` and can thus pass it, which allows us to use
`reference_get_peeled_oid()` instead of `peel_iterated_oid()`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-04 07:32:25 -08:00
Patrick Steinhardt
70b783c3a1 ref-filter: propagate peeled object ID
When queueing a reference in the "ref-filter" subsystem we end up
creating a new ref array item that contains the reference's info. One
bit of info that we always discard though is the peeled object ID, and
because of that we are forced to use `peel_iterated_oid()`.

Refactor the code to propagate the peeled object ID via the ref array,
if available. This allows us to manually peel tags without having to go
through the object database.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-04 07:32:25 -08:00
Patrick Steinhardt
adecd5f0b6 upload-pack: convert to use reference_get_peeled_oid()
The `write_v0_ref()` callback is invoked from two callsites:

  - Once via `send_ref()` which is a callback passed to
    `for_each_namespaced_ref_1()` and `refs_head_ref_namespaced()`.

  - Once manually to announce capabilities.

When sending references to the client we also send the peeled value of
tags. As we don't have a `struct reference` available in the second
case, we cannot easily peel by calling `reference_get_peeled_oid()`, but
we instead have to depend on on global state via `peel_iterated_oid()`.

We do have a reference available though in the first case, it's only the
second case that keeps us from using `reference_get_peeled_oid()`. But
that second case only announces capabilities anyway, so we're not really
handling a reference at all here.

Adapt that case to construct a reference manually and pass that to
`write_v0_ref()`. Start to use `reference_get_peeled_oid()` now that we
always have a `struct reference` available.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-04 07:32:25 -08:00
Patrick Steinhardt
f898661637 refs: expose peeled object ID via the iterator
Both the "files" and "reftable" backend are able to store peeled values
for tags in the respective formats. This allows for a more efficient
lookup of the target object of such a tag without having to manually
peel via the object database.

The infrastructure to access these peeled object IDs is somewhat funky
though. When iterating through objects, we store a pointer reference to
the current iterator in a global variable. The callbacks invoked by that
iterator are then expected to call `peel_iterated_oid()`, which checks
whether the globally-stored iterator's current reference refers to the
one handed into that function. If so, we ask the iterator to peel the
object, otherwise we manually peel the object via the object database.
Depending on global state like this is somewhat weird and also quite
fragile.

Introduce a new `struct reference::peeled_oid` field that can be
populated by the reference backends. This field can be accessed via a
new function `reference_get_peeled_oid()` that either uses that value,
if set, or alternatively peels via the ODB. With this change we don't
have to rely on global state anymore, but make the peeled object ID
available to the callback functions directly.

Adjust trivial callers that already have a `struct reference` available.
Remaining callers will be adjusted in subsequent commits.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-04 07:32:25 -08:00
Patrick Steinhardt
eb2934d94b refs: refactor reference status flags
The reference flags encode information like whether or not a reference
is a symbolic reference or whether it may be broken. This information is
stored in a `int flags` bitfield, which is in conflict with our modern
best practices; we tend to use an unsigned integer to store flags.

Change the type of the field to be `unsigned`. While at it, refactor the
individual flags to be part of an `enum` instead of using preprocessor
defines.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-04 07:32:25 -08:00
Patrick Steinhardt
4cea042287 refs: fully reset struct ref_iterator::ref on iteration
With the introduction of the `struct ref_iterator::ref` field it now is
a whole lot easier to introduce new fields that become accessible to the
caller without having to adapt every single callsite. But there's a
downside: when a new field is introduced we always have to adapt all
backends to set that field.

This isn't something we can avoid in the general case: when the new
field is expected to be populated by all backends we of course cannot
avoid doing so. But new fields may be entirely optional, in which case
we'd still have such churn. And furthermore, it is very easy right now
to leak state from a previous iteration into the next iteration.

Address this issue by ensuring that the reference backends all fully
reset the field on every single iteration. This ensures that no state
from previous iterations can leak into the next one. And it ensures that
any newly introduced fields will be zeroed out by default.

Note that we don't have to explicitly adapt the "files" backend, as it
uses the `cache_ref_iterator` internally. Furthermore, other "wrapping"
iterators like for example the `prefix_ref_iterator` copy around the
whole reference, so these don't need to be adapted either.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-04 07:32:25 -08:00
Patrick Steinhardt
89baa52da6 refs: introduce .ref field for the base iterator
The base iterator has a couple of fields that tracks the name, target,
object ID and flags for the current reference. Due to this design we
have to create a new `struct reference` whenever we want to hand over
that reference to the callback function, which is tedious and not very
efficient.

Convert the structure to instead contain a `struct reference` as member.
This member is expected to be populated by the implementations of the
iterator and is handed over to the callback directly.

While at it, simplify `should_pack_ref()` to take a `struct reference`
directly instead of passing its respective fields.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-04 07:32:25 -08:00
Patrick Steinhardt
bdbebe5714 refs: introduce wrapper struct for each_ref_fn
The `each_ref_fn` callback function type is used across our code base
for several different functions that iterate through reference. There's
a bunch of callbacks implementing this type, which makes any changes to
the callback signature extremely noisy. An example of the required churn
is e8207717f1 (refs: add referent to each_ref_fn, 2024-08-09): adding a
single argument required us to change 48 files.

It was already proposed back then [1] that we might want to introduce a
wrapper structure to alleviate the pain going forward. While this of
course requires the same kind of global refactoring as just introducing
a new parameter, it at least allows us to more change the callback type
afterwards by just extending the wrapper structure.

One counterargument to this refactoring is that it makes the structure
more opaque. While it is obvious which callsites need to be fixed up
when we change the function type, it's not obvious anymore once we use
a structure. That being said, we only have a handful of sites that
actually need to populate this wrapper structure: our ref backends,
"refs/iterator.c" as well as very few sites that invoke the iterator
callback functions directly.

Introduce this wrapper structure so that we can adapt the iterator
interfaces more readily.

[1]: <ZmarVcF5JjsZx0dl@tanuki>

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-04 07:32:24 -08:00
Patrick Steinhardt
3e5e360888 object-file: refactor writing objects via a stream
We have two different ways to write an object into the database:

  - We either provide the full buffer and write the object all at once.

  - Or we provide an input stream that has a `read()` function so that
    we can chunk the object.

The latter is especially used for large objects, where it may be too
expensive to hold the complete object in memory all at once.

While we already have `odb_write_object()` at the ODB-layer, we don't
have an equivalent for streaming an object. Introduce a new function
`odb_write_object_stream()` to address this gap so that callers don't
have to be aware of the inner workings of how to stream an object to
disk with a specific object source.

Rename `stream_loose_object()` to `odb_source_loose_write_stream()` to
clarify its scope. This matches our modern best practices around how to
name functions.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-03 12:18:48 -08:00
Patrick Steinhardt
bfb1b2b4ac object-file: rename write_object_file()
Rename `write_object_file()` to `odb_source_loose_write_object()` so
that it becomes clear that this is tied to a specific loose object
source. This matches our modern naming schema for functions.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-03 12:18:47 -08:00
Patrick Steinhardt
f2bd88a308 object-file: refactor freshening of objects
When writing an object that already exists in our object database we
skip the write and instead only update mtimes of the object, either in
its packed or loose object format. This logic is wholly contained in
"object-file.c", but that file is really only concerned with loose
objects. So it does not really make sense that it also contains the
logic to freshen a packed object.

Introduce a new `odb_freshen_object()` function that sits on the object
database level and two functions `packfile_store_freshen_object()` and
`odb_source_loose_freshen_object()`. Like this, the format-specific
functions can be part of their respective subsystems, while the backend
agnostic function to freshen an object sits at the object database
layer.

Note that this change also moves the logic that iterates through object
sources from the object source layer into the object database layer.
This change is intentional: object sources should ideally only have to
worry about themselves, and coordination of different sources should be
handled on the object database level.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-03 12:18:47 -08:00
Patrick Steinhardt
05130c6c9e object-file: rename has_loose_object()
Rename `has_loose_object()` to `odb_source_loose_has_object()` so that
it becomes clear that this is tied to a specific loose object source.
This matches our modern naming schema for functions.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-03 12:18:47 -08:00
Patrick Steinhardt
ff7ad5cb39 object-file: read objects via the loose object source
When reading an object via `loose_object_info()` or `map_loose_object()`
we hand in the whole repository. We then iterate through each of the
object sources to figure out whether that source has the object in
question.

This logic is reversing responsibility though: a specific backend should
only care about one specific source, where the object sources themselves
are then managed by the object database.

Refactor the code accordingly by passing an object source to both of
these functions instead. The different sources are then handled by
either `do_oid_object_info_extended()`, which sits on the object
database level, and by `open_istream_loose()`. The latter function
arguably is still at the wrong level, but this will be cleaned up at a
later point in time.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-03 12:18:47 -08:00
Patrick Steinhardt
376016ec71 object-file: move loose object map into loose source
The loose object map is used to map from the repository's canonical
object hash to the compatibility hash. As the name indicates, this map
is only used for loose objects, and as such it is tied to a specific
loose object source.

Same as with preceding commits, move this map into the loose object
source accordingly.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-03 12:18:47 -08:00
Patrick Steinhardt
be659c97ea object-file: hide internals when we need to reprepare loose sources
There are two different situations where we have to clear the cache of
loose objects:

  - When freeing the loose object source itself to avoid memory leaks.

  - When repreparing the loose object source so that any potentially-
    stale data is getting evicted from the cache.

The former is already handled by `odb_source_loose_free()`. But the
latter case is still done manually by in `odb_reprepare()`, so we are
leaking internals into that code.

Introduce a new `odb_source_loose_reprepare()` function as an equivalent
to `packfile_store_prepare()` to hide these implementation details.
Furthermore, while at it, rename the function `odb_clear_loose_cache()`
to `odb_source_loose_clear()`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-03 12:18:46 -08:00
Patrick Steinhardt
90a93f9dea object-file: move loose object cache into loose source
Our loose objects use a cache that (optionally) stores all objects for
each of the opened sharding directories. This cache is located in the
`struct odb_source`, but now that we have `struct odb_source_loose` it
makes sense to move it into the latter structure so that all state that
relates to loose objects is entirely self-contained.

Do so. While at it, rename corresponding functions to have a prefix that
relates to `struct odb_source_loose`.

Note that despite this prefix, the functions still accept a `struct
odb_source` as input. This is done intentionally: once we introduce
pluggable object databases, we will continue to accept this struct but
then do a cast inside these functions to `struct odb_source_loose`. This
design is similar to how we do it for our ref backends.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-03 12:18:46 -08:00
Patrick Steinhardt
ece43d9dc7 object-file: introduce struct odb_source_loose
Currently, all state that relates to loose objects is held directly by
the `struct odb_source`. Introduce a new `struct odb_source_loose` to
hold the state instead so that it is entirely self-contained.

This structure will eventually morph into the backend for accessing
loose objects. As such, this is part of the refactorings to introduce
pluggable object databases.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-03 12:18:46 -08:00
Patrick Steinhardt
0cc12dedef object-file: move fetch_if_missing
The `fetch_if_missing` global variable is declared in "object-file.h"
but defined in "odb.c". The variable relates to the whole object
database instead of only loose objects, so move the declaration into
"odb.h" accordingly.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-03 12:18:46 -08:00
Patrick Steinhardt
c2da110411 odb: adjust naming to free object sources
The functions `free_object_directory()` and `free_object_directories()`
are responsible for freeing a single object source or all object sources
connected to an object database, respectively. The associated structure
has been renamed from `struct object_directory` to `struct odb_source`
in a1e2581a1e (object-store: rename `object_directory` to `odb_source`,
2025-07-01) though, so the names are somewhat stale nowadays.

Rename them to mention the new struct name instead. Furthermore, while
at it, adapt them to our modern naming schema where we first have the
subject followed by a verb.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-03 12:18:46 -08:00
Patrick Steinhardt
0820a4b120 odb: introduce odb_source_new()
We have three different locations where we create a new ODB source.
Deduplicate the logic via a new `odb_source_new()` function.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-03 12:18:45 -08:00
Patrick Steinhardt
f82e430b4e odb: fix subtle logic to check whether an alternate is usable
When adding an alternate to the object database we first check whether
or not the path is usable. A path is usable if:

  - It actually exists.

  - We don't have it in our object sources yet.

While the former check is trivial enough, the latter part is somewhat
subtle and prone for bugs. This is because the function doesn't only
check whether or not the given path is usable. But if it _is_ usable, we
also store that path in the map of object sources immediately.

The tricky part here is that the path that gets stored in the map is
_not_ copied. Instead, we rely on the fact that subsequent code uses
`strbuf_detach()` to store the exact same allocated memory in the
created object source. Consequently, the memory is owned by the source
but _also_ stored in the map. This subtlety is easy to miss, so if one
decides to refactor this code one can easily end up breaking this
mechanism.

Make the relationship more explicit by not storing the path as part of
`alt_odb_usable()`. Instead, store the path after we have created the
source so that we can use the source's path pointer directly.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-03 12:18:45 -08:00
Toon Claes
2a04e8c293 last-modified: implement faster algorithm
The current implementation of git-last-modified(1) works by doing a
revision walk, and inspecting the diff at each level of that walk to
annotate entries remaining in the hashmap of paths. In other words, if
the diff at some level touches a path which has not yet been associated
with a commit, then that commit becomes associated with the path.

While a perfectly reasonable implementation, it can perform poorly in
either one of two scenarios:

  1. There are many entries of interest, in which case there is simply
     a lot of work to do.

  2. Or, there are (even a few) entries which have not been updated in a
     long time, and so we must walk through a lot of history in order to
     find a commit that touches that path.

This patch rewrites the last-modified implementation that addresses the
second point. The idea behind the algorithm is to propagate a set of
'active' paths (a path is 'active' if it does not yet belong to a
commit) up to parents and do a truncated revision walk.

The walk is truncated because it does not produce a revision for every
change in the original pathspec, but rather only for active paths.

More specifically, consider a priority queue of commits sorted by
generation number. First, enqueue the set of boundary commits with all
paths in the original spec marked as interesting.

Then, while the queue is not empty, do the following:

  1. Pop an element, say, 'c', off of the queue, making sure that 'c'
     isn't reachable by anything in the '--not' set.

  2. For each parent 'p' (with index 'parent_i') of 'c', do the
     following:

     a. Compute the diff between 'c' and 'p'.
     b. Pass any active paths that are TREESAME from 'c' to 'p'.
     c. If 'p' has any active paths, push it onto the queue.

  3. Any path that remains active on 'c' is associated to that commit.

This ends up being equivalent to doing something like 'git log -1 --
$path' for each path simultaneously. But, it allows us to go much faster
than the original implementation by limiting the number of diffs we
compute, since we can avoid parts of history that would have been
considered by the revision walk in the original implementation, but are
known to be uninteresting to us because we have already marked all paths
in that area to be inactive.

To avoid computing many first-parent diffs, add another trick on top of
this and check if all paths active in 'c' are DEFINITELY NOT in c's
Bloom filter. Since the commit-graph only stores first-parent diffs in
the Bloom filters, we can only apply this trick to first-parent diffs.

Comparing the performance of this new algorithm shows about a 2.5x
improvement on git.git:

    Benchmark 1: master   no bloom
      Time (mean ± σ):      2.868 s ±  0.023 s    [User: 2.811 s, System: 0.051 s]
      Range (min … max):    2.847 s …  2.926 s    10 runs

    Benchmark 2: master with bloom
      Time (mean ± σ):     949.9 ms ±  15.2 ms    [User: 907.6 ms, System: 39.5 ms]
      Range (min … max):   933.3 ms … 971.2 ms    10 runs

    Benchmark 3: HEAD     no bloom
      Time (mean ± σ):     782.0 ms ±   6.3 ms    [User: 740.7 ms, System: 39.2 ms]
      Range (min … max):   776.4 ms … 798.2 ms    10 runs

    Benchmark 4: HEAD   with bloom
      Time (mean ± σ):     307.1 ms ±   1.7 ms    [User: 276.4 ms, System: 29.9 ms]
      Range (min … max):   303.7 ms … 309.5 ms    10 runs

    Summary
      HEAD   with bloom ran
        2.55 ± 0.02 times faster than HEAD     no bloom
        3.09 ± 0.05 times faster than master with bloom
        9.34 ± 0.09 times faster than master   no bloom

In short, the existing implementation is comparably fast *with* Bloom
filters as the new implementation is *without* Bloom filters. So, most
repositories should get a dramatic speed-up by just deploying this (even
without computing Bloom filters), and all repositories should get faster
still when computing Bloom filters.

When comparing a more extreme example of
`git last-modified -- COPYING t`, the difference is even 5 times better:

    Benchmark 1: master
      Time (mean ± σ):      4.372 s ±  0.057 s    [User: 4.286 s, System: 0.062 s]
      Range (min … max):    4.308 s …  4.509 s    10 runs

    Benchmark 2: HEAD
      Time (mean ± σ):     826.3 ms ±  22.3 ms    [User: 784.1 ms, System: 39.2 ms]
      Range (min … max):   810.6 ms … 881.2 ms    10 runs

    Summary
      HEAD ran
        5.29 ± 0.16 times faster than master

As an added benefit, results are more consistent now. For example
implementation in 'master' gives:

    $ git log --max-count=1 --format=%H -- pkt-line.h
    15df15fe07ef66b51302bb77e393f3c5502629de

    $ git last-modified -- pkt-line.h
    15df15fe07ef66b51302bb77e393f3c5502629de	pkt-line.h

    $ git last-modified | grep pkt-line.h
    5b49c1af03e600c286f63d9d9c9fb01403230b9f	pkt-line.h

With the changes in this patch the results of git-last-modified(1)
always match those of `git log --max-count=1`.

One thing to note though, the results might be outputted in a different
order than before. This is not considerd to be an issue because nowhere
is documented the order is guaranteed.

Based-on-patches-by: Derrick Stolee <stolee@gmail.com>
Based-on-patches-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Toon Claes <toon@iotcl.com>
Acked-by: Taylor Blau <me@ttaylorr.com>
[jc: tweaked use of xcalloc() to unbreak coccicheck]
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-03 07:25:41 -08:00
Patrick Steinhardt
c31bad4f7d packfile: track packs via the MRU list exclusively
We track packfiles via two different lists:

  - `struct packfile_store::packs` is a list that sorts local packs
    first. In addition, these packs are sorted so that younger packs are
    sorted towards the front.

  - `struct packfile_store::mru` is a list that sorts packs so that
    most-recently used packs are at the front.

The reasoning behind the ordering in the `packs` list is that younger
objects stored in the local object store tend to be accessed more
frequently, and that is certainly true for some cases. But there are
going to be lots of cases where that isn't true. Especially when
traversing history it is likely that one needs to access many older
objects, and due to our housekeeping it is very likely that almost all
of those older objects will be contained in one large pack that is
oldest.

So whether or not the ordering makes sense really depends on the use
case at hand. A flexible approach like our MRU list addresses that need,
as it will sort packs towards the front that are accessed all the time.
Intuitively, this approach is thus able to satisfy more use cases more
efficiently.

This reasoning casts some doubt on whether or not it really makes sense
to track packs via two different lists. It causes confusion, and it is
not clear whether there are use cases where the `packs` list really is
such an obvious choice.

Merge these two lists into one most-recently-used list.

Note that there is one important edge case: `for_each_packed_object()`
uses the MRU list to iterate through packs, and then it lists each
object in those packs. This would have the effect that we now sort the
current pack towards the front, thus modifying the list of packfiles we
are iterating over, with the consequence that we'll see an infinite
loop. This edge case is worked around by introducing a new field that
allows us to skip updating the MRU.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-10-30 07:09:53 -07:00
Patrick Steinhardt
6aff1f25a0 packfile: always add packfiles to MRU when adding a pack
When preparing the packfile store we know to also prepare the MRU list
of packfiles with all packs that are currently loaded in the store via
`packfile_store_prepare_mru()`. So we know that the list of packs in the
MRU list should match the list of packs in the non-MRU list.

But there are some direct or indirect callsites that add a packfile to
the store via `packfile_store_add_pack()` without adding the pack to the
MRU. And while functions that access the MRU (e.g. `find_pack_entry()`)
know to call `packfile_store_prepare()`, which knows to prepare the MRU
via `packfile_store_prepare_mru()`, that operation will be turned into a
no-op because the packfile store is already prepared. So this will not
cause us to add the packfile to the MRU, and consequently we won't be
able to find the packfile in our MRU list.

There are only a handful of callers outside of "packfile.c" that add a
packfile to the store:

  - "builtin/fast-import.c" adds multiple packs of imported objects, but
    it knows to look up objects via `packfile_store_get_packs()`. This
    function does not use the MRU, so we're good.

  - "builtin/index-pack.c" adds the indexed pack to the store in case it
    needs to perform consistency checks on its objects.

  - "http.c" adds the fetched pack to the store so that we can access
    its objects.

In all of these cases we actually want to access the contained objects.
And luckily, reading these objects works as expected:

  1. We eventually end up in `do_oid_object_info_extended()`.

  2. Calling `find_pack_entry()` fails because the MRU list doesn't
     contain the newly added packfile.

  3. The callers don't pass `OBJECT_INFO_QUICK`, so we end up
     repreparing the object database. This will also cause us to
     reprepare the MRU list.

  4. We now retry reading the object via `find_pack_entry()`, and now we
     succeed because the MRU list got populated.

This logic feels quite fragile: we intentionally add the packfile to the
store, but we then ultimately rely on repreparing the entire store only
to make the packfile accessible. While we do the correct thing in
`do_oid_object_info_extended()`, other sites that access the MRU may not
know to reprepare.

But besides being fragile it's also a waste of resources: repreparing
the object database requires us to re-read the alternates file and
discard any caches.

Refactor the code so that we unconditionally add packfiles to the MRU
when adding them to a packfile store. This makes the logic less fragile
and ensures that we don't have to reprepare the store to make the pack
accessible.

Note that this does not allow us to drop `packfile_store_prepare_mru()`
just yet: while the MRU list is already populated with all packs now,
the order in which we add these packs is indeterministic for most of the
part. So by first calling `sort_pack()` on the other packfile list and
then re-preparing the MRU list we inherit its sorting.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-10-30 07:09:53 -07:00
Patrick Steinhardt
589127caa7 packfile: move list of packs into the packfile store
Move the list of packs into the packfile store. This follows the same
logic as in a previous commit, where we moved the most-recently-used
list of packs, as well.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-10-30 07:09:53 -07:00
Patrick Steinhardt
0d0e4b5954 builtin/pack-objects: simplify logic to find kept or nonlocal objects
The function `has_sha1_pack_kept_or_nonlocal()` takes an object ID and
then searches through packed objects to figure out whether the object
exists in a kept or non-local pack. As a performance optimization we
remember the packfile that contains a given object ID so that the next
call to the function first checks that same packfile again.

The way this is written is rather hard to follow though, as the caching
mechanism is intertwined with the loop that iterates through the packs.
Consequently, we need to do some gymnastics to re-start the iteration if
the cached pack does not contain the objects.

Refactor this so that we check the cached packfile at the beginning. We
don't have to re-verify whether the packfile meets the properties as we
have already verified those when storing the pack in `last_found` in the
first place. So all we need to do is to use `find_pack_entry_one()` to
check whether the pack contains the object ID, and to skip the cached
pack in the loop so that we don't search it twice.

Furthermore, stop using the `(void *)1` sentinel value and instead use a
simple `NULL` pointer to indicate that we don't have a last-found pack
yet.

This refactoring significantly simplifies the logic and makes it much
easier to follow.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-10-30 07:09:53 -07:00
Patrick Steinhardt
02a7f6ffab packfile: fix approximation of object counts
When approximating the number of objects in a repository we only take
into account two data sources, the multi-pack index and the packfile
indices, as both of these data structures allow us to easily figure out
how many objects they contain.

But the way we currently approximate the number of objects is broken in
presence of a multi-pack index. This is due to two separate reasons:

  - We have recently introduced initial infrastructure for incremental
    multi-pack indices. Starting with that series, `num_objects` only
    counts the number of objects of a specific layer of the MIDX chain,
    so we do not take into account objects from parent layers.

    This issue is fixed by adding `num_objects_in_base`, which contains
    the sum of all objects in previous layers.

  - When using the multi-pack index we may count objects contained in
    packfiles twice: once via the multi-pack index, but then we again
    count them via the packfile itself.

    This issue is fixed by skipping any packfiles that have an MIDX.

Overall, given that we _always_ count the packs, we can only end up
overestimating the number of objects, and the overestimation is limited
to a factor of two at most.

The consequences of those issues are very limited though, as we only
approximate object counts in a small number of cases:

  - When writing a commit-graph we use the approximate object count to
    display the upper limit of a progress display.

  - In `repo_find_unique_abbrev_r()` we use it to specify a lower limit
    of how many hex digits we want to abbreviate to. Given that we use
    power-of-two here to derive the lower limit we may end up with an
    abbreviated hash that is one digit longer than required.

  - In `estimate_repack_memory()` we may end up overestimating how much
    memory a repack needs to pack objects. Conseuqently, we may end up
    dropping some packfiles from a repack.

None of these are really game-changing. But it's nice to fix those
issues regardless.

While at it, convert the code to use `repo_for_each_pack()`.
Furthermore, use `odb_prepare_alternates()` instead of explicitly
preparing the packfile store. We really only want to prepare the object
database sources, and `get_multi_pack_index()` already knows to prepare
the packfile store for us.

Helped-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-10-30 07:09:52 -07:00
Patrick Steinhardt
89219bc0cd http: refactor subsystem to use packfile_lists
The dumb HTTP protocol directly fetches packfiles from the remote server
and temporarily stores them in a list of packfiles. Those packfiles are
not yet added to the repository's packfile store until we finalize the
whole fetch.

Refactor the code to instead use a `struct packfile_list` to store those
packs. This prepares us for a subsequent change where the `->next`
pointer of `struct packed_git` will go away.

Note that this refactoring creates some temporary duplication of code,
as we now have both `packfile_list_find_oid()` and `find_oid_pack()`.
The latter function will be removed in a subsequent commit though.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-10-30 07:09:52 -07:00
Patrick Steinhardt
f905a855b1 packfile: move the MRU list into the packfile store
Packfiles have two lists associated to them:

  - A list that keeps track of packfiles in the order that they were
    added to a packfile store.

  - A list that keeps track of packfiles in most-recently-used order so
    that packfiles that are more likely to contain a specific object are
    ordered towards the front.

Both of these lists are hosted by `struct packed_git` itself, So to
identify all packfiles in a repository you simply need to grab the first
packfile and then iterate the `->next` pointers or the MRU list. This
pattern has the problem that all packfiles are part of the same list,
regardless of whether or not they belong to the same object source.

With the upcoming pluggable object database effort this needs to change:
packfiles should be contained by a single object source, and reading an
object from any such packfile should use that source to look up the
object. Consequently, we need to break up the global lists of packfiles
into per-object-source lists.

A first step towards this goal is to move those lists out of `struct
packed_git` and into the packfile store. While the packfile store is
currently sitting on the `struct object_database` level, the intent is
to push it down one level into the `struct odb_source` in a subsequent
patch series.

Introduce a new `struct packfile_list` that is used to manage lists of
packfiles and use it to store the list of most-recently-used packfiles
in `struct packfile_store`. For now, the new list type is only used in a
single spot, but we'll expand its usage in subsequent patches.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-10-30 07:09:52 -07:00
Patrick Steinhardt
e78ab37054 packfile: use a strmap to store packs by name
To allow fast lookups of a packfile by name we use a hashmap that has
the packfile name as key and the pack itself as value. But while this is
the perfect use case for a `strmap`, we instead use `struct hashmap` and
store the hashmap entry in the packfile itself.

Simplify the code by using a `strmap` instead.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-10-30 07:09:52 -07:00
Christian Couder
93cef5bda5 gpg-interface: mark a string for translation
Previous commits have marked a number of error or warning messages in
"builtin/fast-export.c" and "builtin/fast-import.c" for translation.

As "gpg-interface.c" code is used by the fast-export and fast-import
code, we should make sure that error or warning messages are also all
marked for translation in "gpg-interface.c".

To ensure that, let's mark for translation an error message in a
die() function.

With this, all the error and warning messages emitted by fast-export
and fast-import can be properly translated.

Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-10-30 07:06:58 -07:00
Christian Couder
c295115ec6 fast-import: mark strings for translation
Some error or warning messages in "builtin/fast-import.c" are marked
for translation, but many are not.

To be more consistent and provide a better experience to people using a
translated version, let's mark all the remaining error or warning
messages for translation.

While at it, let's make the following small changes:

  - replace "GIT" or "git" in a few error messages to just "Git",
  - replace "Expected from command, got %s" to "expected 'from'
    command, got '%s'", which makes it clearer that "from" is a command
    and should not be translated,
  - downcase error and warning messages that start with an uppercase,
  - fix test cases in "t9300-fast-import.sh" that broke because an
    error or warning message was downcased,
  - split error and warning messages that are too long,
  - adjust the indentation of some arguments of the error functions.

Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-10-30 07:06:58 -07:00
Christian Couder
d53287b734 fast-export: mark strings for translation
Some error or warning messages in "builtin/fast-export.c" are marked
for translation, but many are not.

To be more consistent and provide a better experience to people using a
translated version, let's mark all the remaining error or warning
messages for translation.

While at it:

  - improve how some arguments to some error functions are indented,
  - remove "Error:" at the start of an error message,
  - downcase error and warning messages that start with an uppercase.

Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-10-30 07:06:58 -07:00
Christian Couder
2d7cc86b3b gpg-interface: use left shift to define GPG_VERIFY_*
In "gpg-interface.h", the definitions of the GPG_VERIFY_* boolean flags
are currently using 1, 2 and 4 while we often prefer the bitwise left
shift operator, `<<`, for that purpose to make it clearer that they are
boolean.

Let's use the left shift operator here too. Let's also fix an indent
issue with "4" while at it.

Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-10-30 07:06:58 -07:00
Christian Couder
ee74c5b167 gpg-interface: simplify ssh fingerprint parsing
In "gpg-interface.c", the 'parse_ssh_output()' function takes a
'struct signature_check *sigc' argument and populates many members of
this 'sigc' using information parsed from 'sigc->output' which
contains the ouput of an `ssh-keygen -Y ...` command that was used to
verify an SSH signature.

When it populates 'sigc->fingerprint' though, it uses
`xstrdup(strstr(line, "key ") + 4)` while `strstr(line, "key ")` has
already been computed a few lines above and is already available in
the `key` variable.

Let's simplify this.

Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-10-30 07:06:58 -07:00
Jeff King
85333aa1af test-tool: fix leak in delete-gpgsig command
We read the input into a strbuf, so we must free it. Without this, t1016
complains in SANITIZE=leak mode.

The bug was introduced in 7673ecd2dc (t1016-compatObjectFormat: add
tests to verify the conversion between objects, 2023-10-01). But nobody
seems to have noticed, probably because CI did not run these tests until
the fix in 6cd8369ef3 (t/lib-gpg: call prepare_gnupghome() in GPG2
prereq, 2024-07-03).

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-10-29 12:36:10 -07:00
Jeff King
8a6d158a1d doc: document backslash in gitignore patterns
Because gitignore patterns are passed to fnmatch, the handling of
backslashes is the same as it is there: it can be used to escape
metacharacters. We do reference fnmatch(3) for more details, but it may
be friendlier to point out this implication explicitly (especially for
people who want to know about backslash handling and search the
documentation for that word). There are also two cases that I've seen
some other backslash-escaping systems handle differently, so let's
describe those:

  1. A backslash before any character treats that character literally,
     even if it's not otherwise a meta-character. As opposed to
     including the backslash itself (like "foo\bar" in shell expands to
     "foo\bar") or forbidding it ("foo\zar" is required to produce a
     diagnostic in C).

  2. A backslash at the end of the string is an invalid pattern (and not
     a literal backslash).

This second one in particular was a point of confusion between our
implementation and the one in JGit. Our wildmatch behavior matches what
POSIX specifies for fnmatch, so the code and documentation are in line.
But let's add a test to cover this case. Note that the behavior here
differs between wildmatch itself (which is what gitignore will use) and
pathspec matching (which will only turn to wildmatch if a literal match
fails). So we match "foo\" to "foo\" in pathspecs, but not via
gitignore.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-10-29 09:17:21 -07:00
Eric W. Biederman
f711f37b05 t1016-compatObjectFormat: really freeze time for reproduciblity
The strategy in t1016-compatObjectFormat is to build two trees with
identical commits, one tree encoded in sha1 the other tree encoded
in sha256 and to use the compatibility code to test and see if
the two trees are identical.

GPG signatures include the current time as part of the signature.

To make gpg deterministic I forced the use of gpg --faked-system-time.
Unfortunately I did not look closely enough.

By default gpg still allows time to move forward with --faked-system-time.
So in those rare instances when the system is heavily loaded and gpg runs
slower than other times, signatures over the exact same data differ
due to timestamps with a minuscule difference.

Reading through the gpg documentation with a close eye, time can be
frozen by including an exclamation point at the end of the argument to
--faked-system-time.

Add the exclamation point so gpg really runs with a fixed notion of time,
resulting in the exact same data having identical gpg signatures.

That is enough that I can run "t1016-compatObjectFormat.sh --stress"
and I don't see any failures.

It is possible a future change to gpg will make replay protection more
robust and not provide a way to allow two separate runs of gpg to
produce exactly the same signature for exactly the same data.  If that
happens a deeper comparison of the two repositories will need to be
performed.  A comparison that simply verifies the signatures and
compares the data for equality.  For now that is a lot of work
for no gain so I am just documenting the possibility.

Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-10-28 20:10:15 -07:00
Ruoyu Zhong
bb42dc9710 bisect: update usage and docs to match each other
Update the usage string of `git bisect` and documentation to match each
other. While at it, also:

1. Move the synopsis of `git bisect` subcommands to the synopsis
   section, so that the test `t0450-txt-doc-vs-help.sh` can pass.

2. Document the `git bisect next` subcommand, which exists in the code
   but is missing from the documentation.

See also: [1].

[1]: https://lore.kernel.org/git/3DA38465-7636-4EEF-B074-53E4628F5355@gmail.com/

Suggested-by: Ben Knoble <ben.knoble@gmail.com>
Signed-off-by: Ruoyu Zhong <zhongruoyu@outlook.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-10-28 15:41:42 -07:00
Kristoffer Haugsbakk
d24220b9e8 doc: git-checkout: fix placeholder markup
The placeholder markup is underscore (_), not backtick (`) as well.

The inline-verbatim markup (backticks) handle interior formatting. This
means in this case that it applies HTML `<code>` to the underscores and
`<em>` to the placeholder.

That is the effect, anyway; we can see from the rest of 042d6f34 (doc:
git-checkout: clarify `-b` and `-B`, 2025-09-10) that this was probably
an unintended mix-up.

Acked-by: Julia Evans <julia@jvns.ca>
Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-10-28 12:01:23 -07:00
Junio C Hamano
ed3305fff7 Merge branch 'ps/remove-packfile-store-get-packs' into ps/packed-git-in-object-store
* ps/remove-packfile-store-get-packs: (55 commits)
  packfile: rename `packfile_store_get_all_packs()`
  packfile: introduce macro to iterate through packs
  packfile: drop `packfile_store_get_packs()`
  builtin/grep: simplify how we preload packs
  builtin/gc: convert to use `packfile_store_get_all_packs()`
  object-name: convert to use `packfile_store_get_all_packs()`
  builtin/repack.c: clean up unused `#include`s
  repack: move `write_cruft_pack()` out of the builtin
  repack: move `write_filtered_pack()` out of the builtin
  repack: move `pack_kept_objects` to `struct pack_objects_args`
  repack: move `finish_pack_objects_cmd()` out of the builtin
  builtin/repack.c: pass `write_pack_opts` to `finish_pack_objects_cmd()`
  repack: extract `write_pack_opts_is_local()`
  repack: move `find_pack_prefix()` out of the builtin
  builtin/repack.c: use `write_pack_opts` within `write_cruft_pack()`
  builtin/repack.c: introduce `struct write_pack_opts`
  repack: 'write_midx_included_packs' API from the builtin
  builtin/repack.c: inline packs within `write_midx_included_packs()`
  builtin/repack.c: pass `repack_write_midx_opts` to `midx_included_packs`
  builtin/repack.c: inline `remove_redundant_bitmaps()`
  ...
2025-10-28 10:00:56 -07:00
Queen Ediri Jessa
29181abead MyFirstContribution: add note on confirming patches
Add a note after the `git send-email` section explaining how
contributors can confirm that their patches reached the mailing
list by checking https://lore.kernel.org/git/. This helps
contributors verify that their emails were successfully delivered.

Signed-off-by: Queen Ediri Jessa <qjessa662@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-10-27 09:09:53 -07:00
Xinyu Ruan
6661cde2be refs: add missing remove_on_disk implementation for debug backend
The debug ref backend (refs_be_debug) was missing the remove_on_disk
function pointer, which caused a segmentation fault when running
'GIT_TRACE_REFS=1 git refs migrate --ref-format=reftable' commands.

Signed-off-by: Xinyu Ruan <r200981113@gmail.com>
Acked-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-10-27 08:57:47 -07:00
Thomas Uhle
595be20d22 contrib/credential: add install target
Add an install target rule to the Makefiles in contrib/credential in the
same manner as in other Makefiles in contrib such as for contacts or
subtree.

Signed-off-by: Thomas Uhle <thomas.uhle@mailbox.tu-dresden.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-10-25 18:27:56 -07:00
Junio C Hamano
f2bf477c7e Merge branch 'jt/repo-structure' into ps/ref-peeled-tags
* jt/repo-structure:
  builtin/repo: add progress meter for structure stats
  builtin/repo: add keyvalue and nul format for structure stats
  builtin/repo: add object counts in structure output
  builtin/repo: introduce structure subcommand
  ref-filter: export ref_kind_from_refname()
  ref-filter: allow NULL filter pattern
  builtin/repo: rename repo_info() to cmd_repo_info()
2025-10-22 07:47:24 -07:00
Junio C Hamano
6131a76399 Merge branch 'tb/incremental-midx-part-3.1' into ps/ref-peeled-tags
* tb/incremental-midx-part-3.1: (49 commits)
  builtin/repack.c: clean up unused `#include`s
  repack: move `write_cruft_pack()` out of the builtin
  repack: move `write_filtered_pack()` out of the builtin
  repack: move `pack_kept_objects` to `struct pack_objects_args`
  repack: move `finish_pack_objects_cmd()` out of the builtin
  builtin/repack.c: pass `write_pack_opts` to `finish_pack_objects_cmd()`
  repack: extract `write_pack_opts_is_local()`
  repack: move `find_pack_prefix()` out of the builtin
  builtin/repack.c: use `write_pack_opts` within `write_cruft_pack()`
  builtin/repack.c: introduce `struct write_pack_opts`
  repack: 'write_midx_included_packs' API from the builtin
  builtin/repack.c: inline packs within `write_midx_included_packs()`
  builtin/repack.c: pass `repack_write_midx_opts` to `midx_included_packs`
  builtin/repack.c: inline `remove_redundant_bitmaps()`
  builtin/repack.c: reorder `remove_redundant_bitmaps()`
  repack: keep track of MIDX pack names using existing_packs
  builtin/repack.c: use a string_list for 'midx_pack_names'
  builtin/repack.c: extract opts struct for 'write_midx_included_packs()'
  builtin/repack.c: remove ref snapshotting from builtin
  repack: remove pack_geometry API from the builtin
  ...
2025-10-22 07:47:01 -07:00
Justin Tobler
16a93c03c7 builtin/repo: add progress meter for structure stats
When using the structure subcommand for git-repo(1), evaluating a
repository may take some time depending on its shape. Add a progress
meter to provide feedback to the user about what is happening. The
progress meter is enabled by default when the command is executed from a
tty. It can also be explicitly enabled/disabled via the --[no-]progress
option.

Signed-off-by: Justin Tobler <jltobler@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-10-21 14:40:38 -07:00
Justin Tobler
17215675b5 builtin/repo: add keyvalue and nul format for structure stats
All repository structure stats are outputted in a human-friendly table
form. This format is not suitable for machine parsing. Add a --format
option that supports three output modes: `table`, `keyvalue`, and `nul`.
The `table` mode is the default format and prints the same table output
as before.

With the `keyvalue` mode, each line of output contains a key-value pair
of a repository stat. The '=' character is used to delimit between keys
and values. The `nul` mode is similar to `keyvalue`, but key-values are
delimited by a NUL character instead of a newline. Also, instead of a
'=' character to delimit between keys and values, a newline character is
used. This allows stat values to support special characters without
having to cquote them. These two new modes provides output that is more
machine-friendly.

Signed-off-by: Justin Tobler <jltobler@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-10-21 14:40:38 -07:00
Justin Tobler
eb5cf58ffc builtin/repo: add object counts in structure output
The amount of objects in a repository can provide insight regarding its
shape. To surface this information, use the path-walk API to count the
number of reachable objects in the repository by object type. All
regular references are used to determine the reachable set of objects.
The object counts are appended to the same table containing the
reference information.

Signed-off-by: Justin Tobler <jltobler@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-10-21 14:40:38 -07:00
Justin Tobler
bbb2b93348 builtin/repo: introduce structure subcommand
The structure of a repository's history can have huge impacts on the
performance and health of the repository itself. Currently, Git lacks a
means to surface repository metrics regarding its structure/shape via a
single command. Acquiring this information requires users to be familiar
with the relevant data points and the various Git commands required to
surface them. To fill this gap, supplemental tools such as git-sizer(1)
have been developed.

To allow users to more readily identify repository structure related
information, introduce the "structure" subcommand in git-repo(1). The
goal of this subcommand is to eventually provide similar functionality
to git-sizer(1), but natively in Git.

The initial version of this command only iterates through all references
in the repository and tracks the count of branches, tags, remote refs,
and other reference types. The corresponding information is displayed in
a human-friendly table formatted in a very similar manner to
git-sizer(1). The width of each table column is adjusted automatically
to satisfy the requirements of the widest row contained.

Subsequent commits will surface additional relevant data points to
output and also provide other more machine-friendly output formats.

Based-on-patch-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Justin Tobler <jltobler@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-10-21 14:40:37 -07:00
Justin Tobler
6d1997f6cb ref-filter: export ref_kind_from_refname()
When filtering refs, `ref_kind_from_refname()` is used to determine the
ref type. In a subsequent commit, this same logic is reused when
counting refs by type. Export the function to prepare for this change.

Signed-off-by: Justin Tobler <jltobler@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-10-21 14:40:37 -07:00
Justin Tobler
eafc03dbe3 ref-filter: allow NULL filter pattern
When setting up `struct ref_filter` for filter_refs(), the
`name_patterns` field must point to an array of pattern strings even if
no patterns are required. To improve this interface, treat a NULL
`name_patterns` field the same as when it points to an empty array.

Signed-off-by: Justin Tobler <jltobler@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-10-21 14:40:37 -07:00
Justin Tobler
026ad60160 builtin/repo: rename repo_info() to cmd_repo_info()
Subcommand functions are often prefixed with `cmd_` to denote that they
are an entrypoint. Rename repo_info() to cmd_repo_info() accordingly.

Signed-off-by: Justin Tobler <jltobler@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-10-21 14:40:37 -07:00
Johannes Sixt
bf5a55ac5e gitk: persist position and size of the Tags and Heads window
The Tags and Heads window always opens at a default position and size,
requiring users to reposition it each time. Remember its geometry
between sessions in the config file as `geometry(showrefs)`.

Note that the existing configuration is sourced in proc savestuff
right before new settings are written. This makes the old settings
available as local variables(!) and does not overwrite the current
settings. Since we need access to the global geometry(showrefs), it
is necessary to unset the local variable.

Helped-by: Michael Rappazzo <rappazzo@gmail.com>
Signed-off-by: Johannes Sixt <j6t@kdbg.org>
2025-10-17 18:38:11 +02:00
Johannes Sixt
8b5636a57f Revert "gitk: Only restore window size from ~/.gitk, not position"
This reverts commit b9bee11526ec (gitk: Only restore window size from
~/.gitk, not position, 2008-03-10).

The earlier commit e9937d2a03a4 (Make gitk work reasonably well on
Cygwin, 2007-02-01) reworked the window layout considerably. Much of
this became irrelevant around 2011 after Cygwin gained an X11 server
and switched to a supportable port of the Unix/X11 Tcl/Tk (it is now
on the current 8.6 code base).

Part of the necessary change was to restore the window size across
sessions, but the position was also restored. This raised complaints
on the mailing list[*], because Gitk was opened on the wrong monitor.
b9bee11526ec was the compromise, because it was only the size that
mattered for the Cygwin layout engine to work.

I personally, find it annoying when Gitk pops up on a random location
on the screen, in particular, since many other applications restore
the window positions across sessions, so why not Gitk as well? (I do
not operate multi-monitor setups, so I cannot test the case.)

[*] https://lore.kernel.org/git/47AAA254.2020008@thorn.ws/

Helped-by: Mark Levedahl <mlevedahl@gmail.com>
Signed-off-by: Johannes Sixt <j6t@kdbg.org>
2025-10-17 18:37:52 +02:00
Todd Zullinger
6cd8369ef3 t/lib-gpg: call prepare_gnupghome() in GPG2 prereq
The GPG2 prereq added in 2f36339fa8 (t/lib-gpg: introduce new prereq
GPG2, 2023-06-04) does not create the $GNUPGHOME directory.

Tests which use the GPG2 prereq without previously using the GPG prereq
fail because of the missing directory.  This currently affects
t1016-compatObjectFormat.

Ensure $GNUPGHOME is created in the GPG2 prereq.

Signed-off-by: Todd Zullinger <tmz@pobox.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-10-09 17:58:35 -07:00
Todd Zullinger
a35952b493 t/lib-gpg: add prepare_gnupghome() to create GNUPGHOME dir
We create the $GNUPGHOME directory in both the GPG and GPGSSH prereqs.
Replace the redundancy with a function.

Use `mkdir -p` to ensure we do not fail if a test includes more than one
of these prereqs.

Signed-off-by: Todd Zullinger <tmz@pobox.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-10-09 17:58:35 -07:00
Christian Couder
7b0c37953d SubmittingPatches: add section about AI
As more and more developer tools use AI, we are facing two main risks
related to AI generated content:

  - its situation regarding copyright and license is not clear,
    and:

  - more and more bad quality content could be submitted for review to
    the mailing list.

To mitigate both risks, let's add an "Use of Artificial Intelligence"
section to "Documentation/SubmittingPatches" with the goal of
discouraging its blind use to generate content that is submitted to
the project, while still allowing us to benefit from its help in some
innovative, useful and less risky ways.

Helped-by: Rick Sanders <rick@sfconservancy.org>
Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-10-01 14:27:04 -07:00
321 changed files with 23387 additions and 13602 deletions

8
.gitattributes vendored
View File

@ -1,13 +1,13 @@
* whitespace=!indent,trail,space
*.[ch] whitespace=indent,trail,space diff=cpp
*.sh whitespace=indent,trail,space text eol=lf
* whitespace=trail,space
*.[ch] whitespace=indent,trail,space,incomplete diff=cpp
*.sh whitespace=indent,trail,space,incomplete text eol=lf
*.perl text eol=lf diff=perl
*.pl text eof=lf diff=perl
*.pm text eol=lf diff=perl
*.py text eol=lf diff=python
*.bat text eol=crlf
CODE_OF_CONDUCT.md -whitespace
/Documentation/**/*.adoc text eol=lf
/Documentation/**/*.adoc text eol=lf whitespace=trail,space,incomplete
/command-list.txt text eol=lf
/GIT-VERSION-GEN text eol=lf
/mergetools/* text eol=lf

View File

@ -63,7 +63,7 @@ jobs:
origin \
${{ github.ref }} \
$args
- uses: actions/setup-go@v5
- uses: actions/setup-go@v6
with:
go-version: '>=1.16'
cache: false

View File

@ -123,7 +123,7 @@ jobs:
- name: zip up tracked files
run: git archive -o artifacts/tracked.tar.gz HEAD
- name: upload tracked files and build artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: windows-artifacts
path: artifacts
@ -140,7 +140,7 @@ jobs:
cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
steps:
- name: download tracked files and build artifacts
uses: actions/download-artifact@v5
uses: actions/download-artifact@v6
with:
name: windows-artifacts
path: ${{github.workspace}}
@ -157,7 +157,7 @@ jobs:
run: ci/print-test-failures.sh
- name: Upload failed tests' directories
if: failure() && env.FAILED_TEST_ARTIFACTS != ''
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: failed-tests-windows-${{ matrix.nr }}
path: ${{env.FAILED_TEST_ARTIFACTS}}
@ -208,7 +208,7 @@ jobs:
- name: zip up tracked files
run: git archive -o artifacts/tracked.tar.gz HEAD
- name: upload tracked files and build artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: vs-artifacts
path: artifacts
@ -226,7 +226,7 @@ jobs:
steps:
- uses: git-for-windows/setup-git-for-windows-sdk@v1
- name: download tracked files and build artifacts
uses: actions/download-artifact@v5
uses: actions/download-artifact@v6
with:
name: vs-artifacts
path: ${{github.workspace}}
@ -244,7 +244,7 @@ jobs:
run: ci/print-test-failures.sh
- name: Upload failed tests' directories
if: failure() && env.FAILED_TEST_ARTIFACTS != ''
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: failed-tests-windows-vs-${{ matrix.nr }}
path: ${{env.FAILED_TEST_ARTIFACTS}}
@ -270,7 +270,7 @@ jobs:
shell: pwsh
run: meson compile -C build
- name: Upload build artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: windows-meson-artifacts
path: build
@ -292,13 +292,23 @@ jobs:
shell: pwsh
run: pip install meson ninja
- name: Download build artifacts
uses: actions/download-artifact@v5
uses: actions/download-artifact@v6
with:
name: windows-meson-artifacts
path: build
- name: Test
shell: pwsh
run: meson test -C build --no-rebuild --print-errorlogs --slice "$(1+${{ matrix.nr }})/10"
run: ci/run-test-slice-meson.sh build ${{matrix.nr}} 10
- name: print test failures
if: failure() && env.FAILED_TEST_ARTIFACTS != ''
shell: bash
run: ci/print-test-failures.sh
- name: Upload failed tests' directories
if: failure() && env.FAILED_TEST_ARTIFACTS != ''
uses: actions/upload-artifact@v4
with:
name: failed-tests-windows-meson-${{ matrix.nr }}
path: ${{env.FAILED_TEST_ARTIFACTS}}
regular:
name: ${{matrix.vector.jobname}} (${{matrix.vector.pool}})
@ -313,16 +323,16 @@ jobs:
vector:
- jobname: osx-clang
cc: clang
pool: macos-13
pool: macos-14
- jobname: osx-reftable
cc: clang
pool: macos-13
pool: macos-14
- jobname: osx-gcc
cc: gcc-13
pool: macos-13
pool: macos-14
- jobname: osx-meson
cc: clang
pool: macos-13
pool: macos-14
env:
CC: ${{matrix.vector.cc}}
CC_PACKAGE: ${{matrix.vector.cc_package}}
@ -339,7 +349,7 @@ jobs:
run: ci/print-test-failures.sh
- name: Upload failed tests' directories
if: failure() && env.FAILED_TEST_ARTIFACTS != ''
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: failed-tests-${{matrix.vector.jobname}}
path: ${{env.FAILED_TEST_ARTIFACTS}}
@ -433,13 +443,13 @@ jobs:
- run: ci/install-dependencies.sh
- run: useradd builder --create-home
- run: chown -R builder .
- run: sudo --preserve-env --set-home --user=builder ci/run-build-and-tests.sh
- run: chmod a+w $GITHUB_ENV && sudo --preserve-env --set-home --user=builder ci/run-build-and-tests.sh
- name: print test failures
if: failure() && env.FAILED_TEST_ARTIFACTS != ''
run: sudo --preserve-env --set-home --user=builder ci/print-test-failures.sh
- name: Upload failed tests' directories
if: failure() && env.FAILED_TEST_ARTIFACTS != ''
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: failed-tests-${{matrix.vector.jobname}}
path: ${{env.FAILED_TEST_ARTIFACTS}}

View File

@ -53,6 +53,7 @@ MAN7_TXT += gitcli.adoc
MAN7_TXT += gitcore-tutorial.adoc
MAN7_TXT += gitcredentials.adoc
MAN7_TXT += gitcvs-migration.adoc
MAN7_TXT += gitdatamodel.adoc
MAN7_TXT += gitdiffcore.adoc
MAN7_TXT += giteveryday.adoc
MAN7_TXT += gitfaq.adoc
@ -142,6 +143,7 @@ TECH_DOCS += technical/shallow
TECH_DOCS += technical/sparse-checkout
TECH_DOCS += technical/sparse-index
TECH_DOCS += technical/trivial-merge
TECH_DOCS += technical/unambiguous-types
TECH_DOCS += technical/unit-tests
SP_ARTICLES += $(TECH_DOCS)
SP_ARTICLES += technical/api-index

View File

@ -1153,6 +1153,11 @@ NOTE: When you are sending a real patch, it will go to git@vger.kernel.org - but
please don't send your patchset from the tutorial to the real mailing list! For
now, you can send it to yourself, to make sure you understand how it will look.
NOTE: After sending your patches, you can confirm that they reached the mailing
list by visiting https://lore.kernel.org/git/. Use the search bar to find your
name or the subject of your patch. If it appears, your email was successfully
delivered.
After you run the command above, you will be presented with an interactive
prompt for each patch that's about to go out. This gives you one last chance to
edit or quit sending something (but again, don't edit code this way). Once you

View File

@ -17,10 +17,10 @@ UI, Workflows & Features
* A new command "git last-modified" has been added to show the closest
ancestor commit that touched each path.
* "git refs exists" that works like "git show-ref --exists" has been
added.
* The "git refs exists" command that works like "git show-ref --exists"
has been added.
* "repo info" learns a short-hand option "-z" that is the same as
* "git repo info" learns the short-hand option "-z" that is the same as
"--format=nul", and learns to report the objects format used in the
repository.
@ -53,7 +53,7 @@ UI, Workflows & Features
* Configuration variables that take a pathname as a value
(e.g. blame.ignorerevsfile) can be marked as optional by prefixing
":(optoinal)" before its value.
":(optional)" before its value.
* Show 'P'ipe command in "git add -p".
@ -74,6 +74,11 @@ UI, Workflows & Features
avoids doing maintenance tasks that rebuilds everything from
scratch.
* "git repo structure", a new command.
* The help text and manual page of "git bisect" command have been
made consistent with each other.
Performance, Internal Implementation, Development Support etc.
--------------------------------------------------------------
@ -165,6 +170,26 @@ Performance, Internal Implementation, Development Support etc.
* The code to walk revision graph to compute merge base has been
optimized.
* AI guidelines has been added to our documentation set.
* Contributed credential helpers (obviously in contrib/) now have "cd
$there && make install" target.
* The "MyFirstContribution" tutorial tells the reader how to send out
their patches; the section gained a hint to verify the message
reached the mailing list.
* The "debug" ref-backend was missing a method implementation, which
has been corrected.
* Build procedure for Wincred credential helper has been updated.
* The build procedure based on meson learned to allow builders to
specify the directory to install HTML documents.
* Building "git contacts" script (in contrib/) left the resulting
file unexecutable, which has been corrected.
Fixes since v2.51
-----------------
@ -397,6 +422,22 @@ including security updates, are included in this release.
"foo**/bar" match with "foobar", which has been corrected.
(merge 1940a02dc1 jk/match-pathname-fix later to maint).
* Tests did not set up GNUPGHOME correctly, which is fixed but some
flaky tests are exposed in t1016, which needs to be addressed
before this topic can move forward.
(merge 6cd8369ef3 tz/test-prepare-gnupghome later to maint).
* The patterns used in the .gitignore files use backslash in the way
documented for fnmatch(3); document as such to reduce confusion.
(merge 8a6d158a1d jk/doc-backslash-in-exclude later to maint).
* The version of macos image used in GitHub CI has been updated to
macos-14, as the macos-13 that we have been using got deprecated.
Perforce binary used there has been changed to arm64 version to
match.
(merge 73b9cdb7c4 jc/ci-use-macos-14 later to maint).
(merge ffff0bb0da jc/ci-use-arm64-p4-on-macos later to maint).
* Other code cleanup, docfix, build fix, etc.
(merge 529a60a885 ua/t1517-short-help-tests later to maint).
(merge 22d421fed9 ac/deglobal-fmt-merge-log-config later to maint).
@ -409,3 +450,5 @@ including security updates, are included in this release.
(merge 15b8abde07 js/mingw-includes-cleanup later to maint).
(merge 2cebca0582 tb/cat-file-objectmode-update later to maint).
(merge 8f487db07a kh/doc-patch-id-1 later to maint).
(merge f711f37b05 eb/t1016-hash-transition-fix later to maint).
(merge 85333aa1af jk/test-delete-gpgsig-leakfix later to maint).

View File

@ -0,0 +1,265 @@
Git v2.53 Release Notes
=======================
UI, Workflows & Features
------------------------
* "git maintenance" command learned "is-needed" subcommand to tell if
it is necessary to perform various maintenance tasks.
* "git replay" (experimental) learned to perform ref updates itself
in a transaction by default, instead of emitting where each refs
should point at and leaving the actual update to another command.
* "git blame" learns "--diff-algorithm=<algo>" option.
* "git repo info" learned "--all" option.
* Both "git apply" and "git diff" learn a new whitespace error class,
"incomplete-line".
* Add a new manual that describes the data model.
* "git fast-import" learns "--strip-if-invalid" option to drop
invalid cryptographic signature from objects.
* The use of "revision" (a connected set of commits) has been
clarified in the "git replay" documentation.
* A help message from "git branch" now mentions "git help" instead of
"man" when suggesting to read some documentation.
* "git repo struct" learned to take "-z" as a synonym to "--format=nul".
* More object database related information are shown in "git repo
structure" output.
Performance, Internal Implementation, Development Support etc.
--------------------------------------------------------------
* The list of packfiles used in a running Git process is moved from
the packed_git structure into the packfile store.
* Some ref backend storage can hold not just the object name of an
annotated tag, but the object name of the object the tag points at.
The code to handle this information has been streamlined.
* As "git diff --quiet" only cares about the existence of any
changes, disable rename/copy detection to skip more expensive
processing whose result will be discarded anyway.
* A part of code paths that deals with loose objects has been cleaned
up.
* "make strip" has been taught to strip "scalar" as well as "git".
* Dockerised jobs at the GitHub Actions CI have been taught to show
more details of failed tests.
* Code refactoring around object database sources.
* Halve the memory consumed by artificial filepairs created during
"git diff --find-copioes-harder", also making the operation run
faster.
* The "git_istream" abstraction has been revamped to make it easier
to interface with pluggable object database design.
* Rewrite the only use of "mktemp()" that is subject to TOCTOU race
and Stop using the insecure "mktemp()" function.
(merge 10bba537c4 rs/ban-mktemp later to maint).
* In-code comment update to clarify that single-letter options are
outside of the scope of command line completion script.
(merge dc8a00fafe jc/completion-no-single-letter-options later to maint).
* MEMZERO_ARRAY() helper is introduced to avoid clearing only the
first N bytes of an N-element array whose elements are larger than
a byte.
* "git diff-files -R --find-copies-harder" has been taught to use
the potential copy sources from the index correctly.
* Require C99 style flexible array member support from all platforms.
* The code path that enumerates promisor objects have been optimized
to skip pointlessly parsing blob objects.
* Prepare test suite for Git for Windows that supports symbolic
links.
* Use hook API to replace ad-hoc invocation of hook scripts with the
run_command() API.
Fixes since v2.52
-----------------
* Ever since we added whitespace rules for this project, we misspelt
an entry, which has been corrected.
(merge 358e94dc70 jc/gitattributes-whitespace-no-indent-fix later to maint).
* The code to expand attribute macros has been rewritten to avoid
recursion to avoid running out of stack space in an uncontrolled
way.
(merge 42ed046866 jk/attr-macroexpand-wo-recursion later to maint).
* Adding a repository that uses a different hash function is a no-no,
but "git submodule add" did nt prevent it, which has been corrected.
(merge 6fe288bfbc bc/submodule-force-same-hash later to maint).
* An earlier check added to osx keychain credential helper to avoid
storing the credential itself supplied was overeager and rejected
credential material supplied by other helper backends that it would
have wanted to store, which has been corrected.
(merge 4580bcd235 kn/osxkeychain-idempotent-store-fix later to maint).
* The "git repo structure" subcommand tried to align its output but
mixed up byte count and display column width, which has been
corrected.
(merge 7a03a10a3a jx/repo-struct-utf8width-fix later to maint).
* Yet another corner case fix around renames in the "ort" merge
strategy.
(merge a562d90a35 en/ort-rename-another-fix later to maint).
* Test leakfix.
(merge 14b561e768 jk/test-mktemp-leakfix later to maint).
* Update a version of action used at the GitHub Actrions CI.
(merge cd99203f86 js/ci-github-setup-go-update later to maint).
* The "return errno = EFOO, -1" construct, which is heavily used in
compat/mingw.c and triggers warnings under "-Wcomma", has been
rewritten to avoid the warnings.
(merge af3919816f js/mingw-assign-comma-fix later to maint).
* Makefile based build have recently been updated to build a
libgit.a that also has reftable and xdiff objects; CMake based
build procedure has been updated to match.
(merge b0d5c88cca js/cmake-libgit-fix later to maint).
* Under-allocation fix.
(merge d22a488482 js/wincred-get-credential-alloc-fix later to maint).
* "git worktree list" attempts to show paths to worktrees while
aligning them, but miscounted display columns for the paths when
non-ASCII characters were involved, which has been corrected.
(merge 08dfa59835 pw/worktree-list-display-width-fix later to maint).
* "Windows+meson" job at the GitHub Actions CI was hard to debug, as
it did not show and save failed test artifacts, which has been
corrected.
(merge 17bd1108ea jk/ci-windows-meson-test-fix later to maint).
* Emulation code clean-up.
(merge 2367c6bcd6 gf/win32-pthread-cond-wait-err later to maint).
* Various issues detected by Asan have been corrected.
(merge a031b6181a jk/asan-bonanza later to maint).
* "git config get --path" segfaulted on an ":(optional)path" that
does not exist, which has been corrected.
(merge 0bd16856ff jc/optional-path later to maint).
* The "--committer-date-is-author-date" option of "git am/rebase" is
a misguided one. The documentation is updated to discourage its
use.
(merge fbf3d0669f kh/doc-committer-date-is-author-date later to maint).
* The option help text given by "git config unset -h" described
the "--all" option to "replace", not "unset", multiple variables,
which has been corrected.
(merge 18bf67b753 rs/config-unset-opthelp-fix later to maint).
* The error message given by "git config set", when the variable
being updated has more than one values defined, used old style "git
config" syntax with an incorrect option in its hint, both of which
have been corrected.
(merge df963f0df4 rs/config-set-multi-error-message-fix later to maint).
* "git replay" forgot to omit the "gpgsig-sha256" extended header
from the resulting commit the same way it omits "gpgsig", which has
been corrected.
(merge 9f3a115087 pw/replay-exclude-gpgsig-fix later to maint).
* A few tests have been updated to work under the shell compatible
mode of zsh.
(merge a92f243a94 bc/zsh-testsuite later to maint).
* The way patience diff finds LCS has been optimized.
(merge c7e3b8085b yc/xdiff-patience-optim later to maint).
* Recent optimization to "last-modified" command introduced use of
uninitialized block of memory, which has been corrected.
(merge fe4e60759b tc/last-modified-active-paths-optimization later to maint).
* "git last-modified" used to mishandle "--" to mark the beginning of
pathspec, which has been corrected.
(merge 05491b90ce js/last-modified-with-sparse-checkouts later to maint).
* Emulation code clean-up.
(merge 42aa7603aa gf/win32-pthread-cond-init later to maint).
* "git submodule add" to add a submodule under <name> segfaulted,
when a submodule.<name>.something is already in .gitmodules file
without defining where its submodule.<name>.path is, which has been
corrected.
(merge dd8e8c786e jc/submodule-add later to maint).
* "git fetch" that involves fetching tags, when a tag being fetched
needs to overwrite existing one, failed to fetch other tags, which
has been corrected.
(merge b7b17ec8a6 kn/fix-fetch-backfill-tag-with-batched-ref-updates later to maint).
* Document "rev-list --filter-provided-objects" better.
(merge 6d8dc99478 jt/doc-rev-list-filter-provided-objects later to maint).
* Even when there is no changes in the packfile and no need to
recompute bitmaps, "git repack" recomputed and updated the MIDX
file, which has been corrected.
(merge 6ce9d558ce ps/repack-avoid-noop-midx-rewrite later to maint).
* 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).
* 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).
(merge f18aa68861 rs/xmkstemp-simplify later to maint).
(merge fddba8f737 ja/doc-synopsis-style later to maint).
(merge 22ce0cb639 en/xdiff-cleanup-2 later to maint).
(merge 8ef7355a8f je/doc-pull later to maint).
(merge 48176f953f jc/capability-leak later to maint).
(merge 8cbbdc92f7 kh/doc-pre-commit-fix later to maint).
(merge d4bc39a4d9 mh/doc-config-gui-gcwarning later to maint).
(merge 41d425008a kh/doc-send-email-paragraph-fix later to maint).
(merge d4b732899e jc/macports-darwinports later to maint).
(merge bab391761d kj/pull-options-decl-cleanup later to maint).
(merge 007b8994d4 rs/t4014-git-version-string-fix later to maint).
(merge 4ce170c522 ds/doc-scalar-config later to maint).
(merge a0c813951a jc/doc-commit-signoff-config later to maint).
(merge 8ee262985a ja/doc-misc-fixes later to maint).
(merge 1722c2244b mh/doc-core-attributesfile later to maint).
(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).

View File

@ -446,6 +446,34 @@ highlighted above.
Only capitalize the very first letter of the trailer, i.e. favor
"Signed-off-by" over "Signed-Off-By" and "Acked-by:" over "Acked-By".
[[ai]]
=== Use of Artificial Intelligence (AI)
The Developer's Certificate of Origin requires contributors to certify
that they know the origin of their contributions to the project and
that they have the right to submit it under the project's license.
It's not yet clear that this can be legally satisfied when submitting
significant amount of content that has been generated by AI tools.
Another issue with AI generated content is that AIs still often
hallucinate or just produce bad code, commit messages, documentation
or output, even when you point out their mistakes.
To avoid these issues, we will reject anything that looks AI
generated, that sounds overly formal or bloated, that looks like AI
slop, that looks good on the surface but makes no sense, or that
senders dont understand or cannot explain.
We strongly recommend using AI tools carefully and responsibly.
Contributors would often benefit more from AI by using it to guide and
help them step by step towards producing a solution by themselves
rather than by asking for a full solution that they would then mostly
copy-paste. They can also use AI to help with debugging, or with
checking for obvious mistakes, things that can be improved, things
that dont match our style, guidelines or our feedback, before sending
it to us.
[[git-tools]]
=== Generate your patch using Git tools out of your commits.

View File

@ -492,10 +492,9 @@ core.askPass::
command-line argument and write the password on its STDOUT.
core.attributesFile::
In addition to `.gitattributes` (per-directory) and
`.git/info/attributes`, Git looks into this file for attributes
(see linkgit:gitattributes[5]). Path expansions are made the same
way as for `core.excludesFile`. Its default value is
Specifies the pathname to the file that contains attributes (see
linkgit:gitattributes[5]), in addition to `.gitattributes` (per-directory)
and `.git/info/attributes`. Its default value is
`$XDG_CONFIG_HOME/git/attributes`. If `$XDG_CONFIG_HOME` is either not
set or empty, `$HOME/.config/git/attributes` is used instead.
@ -629,6 +628,8 @@ core.whitespace::
part of the line terminator, i.e. with it, `trailing-space`
does not trigger if the character before such a carriage-return
is not a whitespace (not enabled by default).
* `incomplete-line` treats the last line of a file that is missing the
newline at the end as an error (not enabled by default).
* `tabwidth=<n>` tells how many character positions a tab occupies; this
is relevant for `indent-with-non-tab` and when Git fixes `tab-in-indent`
errors. The default tab width is 8. Allowed values are 1 to 63.

View File

@ -1,32 +1,32 @@
fetch.recurseSubmodules::
`fetch.recurseSubmodules`::
This option controls whether `git fetch` (and the underlying fetch
in `git pull`) will recursively fetch into populated submodules.
This option can be set either to a boolean value or to 'on-demand'.
This option can be set either to a boolean value or to `on-demand`.
Setting it to a boolean changes the behavior of fetch and pull to
recurse unconditionally into submodules when set to true or to not
recurse at all when set to false. When set to 'on-demand', fetch and
recurse at all when set to false. When set to `on-demand`, fetch and
pull will only recurse into a populated submodule when its
superproject retrieves a commit that updates the submodule's
reference.
Defaults to 'on-demand', or to the value of 'submodule.recurse' if set.
Defaults to `on-demand`, or to the value of `submodule.recurse` if set.
fetch.fsckObjects::
`fetch.fsckObjects`::
If it is set to true, git-fetch-pack will check all fetched
objects. See `transfer.fsckObjects` for what's
checked. Defaults to false. If not set, the value of
checked. Defaults to `false`. If not set, the value of
`transfer.fsckObjects` is used instead.
fetch.fsck.<msg-id>::
`fetch.fsck.<msg-id>`::
Acts like `fsck.<msg-id>`, but is used by
linkgit:git-fetch-pack[1] instead of linkgit:git-fsck[1]. See
the `fsck.<msg-id>` documentation for details.
fetch.fsck.skipList::
`fetch.fsck.skipList`::
Acts like `fsck.skipList`, but is used by
linkgit:git-fetch-pack[1] instead of linkgit:git-fsck[1]. See
the `fsck.skipList` documentation for details.
fetch.unpackLimit::
`fetch.unpackLimit`::
If the number of objects fetched over the Git native
transfer is below this
limit, then the objects will be unpacked into loose object
@ -37,12 +37,12 @@ fetch.unpackLimit::
especially on slow filesystems. If not set, the value of
`transfer.unpackLimit` is used instead.
fetch.prune::
`fetch.prune`::
If true, fetch will automatically behave as if the `--prune`
option was given on the command line. See also `remote.<name>.prune`
and the PRUNING section of linkgit:git-fetch[1].
fetch.pruneTags::
`fetch.pruneTags`::
If true, fetch will automatically behave as if the
`refs/tags/*:refs/tags/*` refspec was provided when pruning,
if not set already. This allows for setting both this option
@ -50,41 +50,41 @@ fetch.pruneTags::
refs. See also `remote.<name>.pruneTags` and the PRUNING
section of linkgit:git-fetch[1].
fetch.all::
`fetch.all`::
If true, fetch will attempt to update all available remotes.
This behavior can be overridden by passing `--no-all` or by
explicitly specifying one or more remote(s) to fetch from.
Defaults to false.
Defaults to `false`.
fetch.output::
`fetch.output`::
Control how ref update status is printed. Valid values are
`full` and `compact`. Default value is `full`. See the
OUTPUT section in linkgit:git-fetch[1] for details.
fetch.negotiationAlgorithm::
`fetch.negotiationAlgorithm`::
Control how information about the commits in the local repository
is sent when negotiating the contents of the packfile to be sent by
the server. Set to "consecutive" to use an algorithm that walks
over consecutive commits checking each one. Set to "skipping" to
the server. Set to `consecutive` to use an algorithm that walks
over consecutive commits checking each one. Set to `skipping` to
use an algorithm that skips commits in an effort to converge
faster, but may result in a larger-than-necessary packfile; or set
to "noop" to not send any information at all, which will almost
to `noop` to not send any information at all, which will almost
certainly result in a larger-than-necessary packfile, but will skip
the negotiation step. Set to "default" to override settings made
the negotiation step. Set to `default` to override settings made
previously and use the default behaviour. The default is normally
"consecutive", but if `feature.experimental` is true, then the
default is "skipping". Unknown values will cause 'git fetch' to
`consecutive`, but if `feature.experimental` is `true`, then the
default is `skipping`. Unknown values will cause `git fetch` to
error out.
+
See also the `--negotiate-only` and `--negotiation-tip` options to
linkgit:git-fetch[1].
fetch.showForcedUpdates::
Set to false to enable `--no-show-forced-updates` in
`fetch.showForcedUpdates`::
Set to `false` to enable `--no-show-forced-updates` in
linkgit:git-fetch[1] and linkgit:git-pull[1] commands.
Defaults to true.
Defaults to `true`.
fetch.parallel::
`fetch.parallel`::
Specifies the maximal number of fetch operations to be run in parallel
at a time (submodules, or remotes when the `--multiple` option of
linkgit:git-fetch[1] is in effect).
@ -94,16 +94,16 @@ A value of 0 will give some reasonable default. If unset, it defaults to 1.
For submodules, this setting can be overridden using the `submodule.fetchJobs`
config setting.
fetch.writeCommitGraph::
`fetch.writeCommitGraph`::
Set to true to write a commit-graph after every `git fetch` command
that downloads a pack-file from a remote. Using the `--split` option,
most executions will create a very small commit-graph file on top of
the existing commit-graph file(s). Occasionally, these files will
merge and the write may take longer. Having an updated commit-graph
file helps performance of many Git commands, including `git merge-base`,
`git push -f`, and `git log --graph`. Defaults to false.
`git push -f`, and `git log --graph`. Defaults to `false`.
fetch.bundleURI::
`fetch.bundleURI`::
This value stores a URI for downloading Git object data from a bundle
URI before performing an incremental fetch from the origin Git server.
This is similar to how the `--bundle-uri` option behaves in
@ -115,9 +115,9 @@ If you modify this value and your repository has a `fetch.bundleCreationToken`
value, then remove that `fetch.bundleCreationToken` value before fetching from
the new bundle URI.
fetch.bundleCreationToken::
`fetch.bundleCreationToken`::
When using `fetch.bundleURI` to fetch incrementally from a bundle
list that uses the "creationToken" heuristic, this config value
list that uses the "`creationToken`" heuristic, this config value
stores the maximum `creationToken` value of the downloaded bundles.
This value is used to prevent downloading bundles in the future
if the advertised `creationToken` is not strictly larger than this

View File

@ -55,3 +55,8 @@ gui.blamehistoryctx::
linkgit:gitk[1] for the selected commit, when the `Show History
Context` menu item is invoked from 'git gui blame'. If this
variable is set to zero, the whole history is shown.
gui.GCWarning::
Determines whether linkgit:git-gui[1] should prompt for garbage
collection when git detects a large number of loose objects in
the repository. The default value is "true".

View File

@ -1,15 +1,15 @@
push.autoSetupRemote::
If set to "true" assume `--set-upstream` on default push when no
`push.autoSetupRemote`::
If set to `true` assume `--set-upstream` on default push when no
upstream tracking exists for the current branch; this option
takes effect with push.default options 'simple', 'upstream',
and 'current'. It is useful if by default you want new branches
takes effect with `push.default` options `simple`, `upstream`,
and `current`. It is useful if by default you want new branches
to be pushed to the default remote (like the behavior of
'push.default=current') and you also want the upstream tracking
`push.default=current`) and you also want the upstream tracking
to be set. Workflows most likely to benefit from this option are
'simple' central workflows where all branches are expected to
`simple` central workflows where all branches are expected to
have the same name on the remote.
push.default::
`push.default`::
Defines the action `git push` should take if no refspec is
given (whether from the command-line, config, or elsewhere).
Different values are well-suited for
@ -18,24 +18,28 @@ push.default::
`upstream` is probably what you want. Possible values are:
+
--
`nothing`;;
do not push anything (error out) unless a refspec is
given. This is primarily meant for people who want to
avoid mistakes by always being explicit.
* `nothing` - do not push anything (error out) unless a refspec is
given. This is primarily meant for people who want to
avoid mistakes by always being explicit.
`current`;;
push the current branch to update a branch with the same
name on the receiving end. Works in both central and non-central
workflows.
* `current` - push the current branch to update a branch with the same
name on the receiving end. Works in both central and non-central
workflows.
`upstream`;;
push the current branch back to the branch whose
changes are usually integrated into the current branch (which is
called `@{upstream}`). This mode only makes sense if you are
pushing to the same repository you would normally pull from
(i.e. central workflow).
* `upstream` - push the current branch back to the branch whose
changes are usually integrated into the current branch (which is
called `@{upstream}`). This mode only makes sense if you are
pushing to the same repository you would normally pull from
(i.e. central workflow).
`tracking`;;
this is a deprecated synonym for `upstream`.
* `tracking` - This is a deprecated synonym for `upstream`.
* `simple` - push the current branch with the same name on the remote.
`simple`;;
push the current branch with the same name on the remote.
+
If you are working on a centralized workflow (pushing to the same repository you
pull from, which is typically `origin`), then you need to configure an upstream
@ -44,16 +48,17 @@ branch with the same name.
This mode is the default since Git 2.0, and is the safest option suited for
beginners.
* `matching` - push all branches having the same name on both ends.
This makes the repository you are pushing to remember the set of
branches that will be pushed out (e.g. if you always push 'maint'
and 'master' there and no other branches, the repository you push
to will have these two branches, and your local 'maint' and
'master' will be pushed there).
`matching`;;
push all branches having the same name on both ends.
This makes the repository you are pushing to remember the set of
branches that will be pushed out (e.g. if you always push `maint`
and `master` there and no other branches, the repository you push
to will have these two branches, and your local `maint` and
`master` will be pushed there).
+
To use this mode effectively, you have to make sure _all_ the
branches you would push out are ready to be pushed out before
running 'git push', as the whole point of this mode is to allow you
running `git push`, as the whole point of this mode is to allow you
to push all of the branches in one go. If you usually finish work
on only one branch and push out the result, while other branches are
unfinished, this mode is not for you. Also this mode is not
@ -66,24 +71,24 @@ new default).
--
push.followTags::
`push.followTags`::
If set to true, enable `--follow-tags` option by default. You
may override this configuration at time of push by specifying
`--no-follow-tags`.
push.gpgSign::
May be set to a boolean value, or the string 'if-asked'. A true
`push.gpgSign`::
May be set to a boolean value, or the string `if-asked`. A true
value causes all pushes to be GPG signed, as if `--signed` is
passed to linkgit:git-push[1]. The string 'if-asked' causes
passed to linkgit:git-push[1]. The string `if-asked` causes
pushes to be signed if the server supports it, as if
`--signed=if-asked` is passed to 'git push'. A false value may
`--signed=if-asked` is passed to `git push`. A false value may
override a value from a lower-priority config file. An explicit
command-line flag always overrides this config option.
push.pushOption::
`push.pushOption`::
When no `--push-option=<option>` argument is given from the
command line, `git push` behaves as if each <value> of
this variable is given as `--push-option=<value>`.
command line, `git push` behaves as if each _<option>_ of
this variable is given as `--push-option=<option>`.
+
This is a multi-valued variable, and an empty value can be used in a
higher priority configuration file (e.g. `.git/config` in a
@ -109,26 +114,26 @@ This will result in only b (a and c are cleared).
----
push.recurseSubmodules::
May be "check", "on-demand", "only", or "no", with the same behavior
as that of "push --recurse-submodules".
If not set, 'no' is used by default, unless 'submodule.recurse' is
set (in which case a 'true' value means 'on-demand').
`push.recurseSubmodules`::
May be `check`, `on-demand`, `only`, or `no`, with the same behavior
as that of `push --recurse-submodules`.
If not set, `no` is used by default, unless `submodule.recurse` is
set (in which case a `true` value means `on-demand`).
push.useForceIfIncludes::
If set to "true", it is equivalent to specifying
`push.useForceIfIncludes`::
If set to `true`, it is equivalent to specifying
`--force-if-includes` as an option to linkgit:git-push[1]
in the command line. Adding `--no-force-if-includes` at the
time of push overrides this configuration setting.
push.negotiate::
If set to "true", attempt to reduce the size of the packfile
`push.negotiate`::
If set to `true`, attempt to reduce the size of the packfile
sent by rounds of negotiation in which the client and the
server attempt to find commits in common. If "false", Git will
server attempt to find commits in common. If `false`, Git will
rely solely on the server's ref advertisement to find commits
in common.
push.useBitmaps::
If set to "false", disable use of bitmaps for "git push" even if
`pack.useBitmaps` is "true", without preventing other git operations
from using bitmaps. Default is true.
`push.useBitmaps`::
If set to `false`, disable use of bitmaps for `git push` even if
`pack.useBitmaps` is `true`, without preventing other git operations
from using bitmaps. Default is `true`.

View File

@ -0,0 +1,11 @@
replay.refAction::
Specifies the default mode for handling reference updates in
`git replay`. The value can be:
+
--
* `update`: Update refs directly using an atomic transaction (default behavior).
* `print`: Output update-ref commands for pipeline use.
--
+
This setting can be overridden with the `--ref-action` command-line option.
When not configured, `git replay` defaults to `update` mode.

View File

@ -0,0 +1,20 @@
`--diff-algorithm=(patience|minimal|histogram|myers)`::
Choose a diff algorithm. The variants are as follows:
+
--
`default`;;
`myers`;;
The basic greedy diff algorithm. Currently, this is the default.
`minimal`;;
Spend extra time to make sure the smallest possible diff is
produced.
`patience`;;
Use "patience diff" algorithm when generating patches.
`histogram`;;
This algorithm extends the patience algorithm to "support
low-occurrence common elements".
--
+
For instance, if you configured the `diff.algorithm` variable to a
non-default value and want to use the default one, then you
have to use `--diff-algorithm=default` option.

View File

@ -197,26 +197,7 @@ and starts with _<text>_, this algorithm attempts to prevent it from
appearing as a deletion or addition in the output. It uses the "patience
diff" algorithm internally.
`--diff-algorithm=(patience|minimal|histogram|myers)`::
Choose a diff algorithm. The variants are as follows:
+
--
`default`;;
`myers`;;
The basic greedy diff algorithm. Currently, this is the default.
`minimal`;;
Spend extra time to make sure the smallest possible diff is
produced.
`patience`;;
Use "patience diff" algorithm when generating patches.
`histogram`;;
This algorithm extends the patience algorithm to "support
low-occurrence common elements".
--
+
For instance, if you configured the `diff.algorithm` variable to a
non-default value and want to use the default one, then you
have to use `--diff-algorithm=default` option.
include::diff-algorithm-option.adoc[]
`--stat[=<width>[,<name-width>[,<count>]]]`::
Generate a diffstat. By default, as much space as necessary

View File

@ -1,41 +1,41 @@
--all::
--no-all::
`--all`::
`--no-all`::
Fetch all remotes, except for the ones that has the
`remote.<name>.skipFetchAll` configuration variable set.
This overrides the configuration variable `fetch.all`.
-a::
--append::
`-a`::
`--append`::
Append ref names and object names of fetched refs to the
existing contents of `.git/FETCH_HEAD`. Without this
option old data in `.git/FETCH_HEAD` will be overwritten.
--atomic::
`--atomic`::
Use an atomic transaction to update local refs. Either all refs are
updated, or on error, no refs are updated.
--depth=<depth>::
`--depth=<depth>`::
Limit fetching to the specified number of commits from the tip of
each remote branch history. If fetching to a 'shallow' repository
created by `git clone` with `--depth=<depth>` option (see
linkgit:git-clone[1]), deepen or shorten the history to the specified
number of commits. Tags for the deepened commits are not fetched.
--deepen=<depth>::
Similar to --depth, except it specifies the number of commits
`--deepen=<depth>`::
Similar to `--depth`, except it specifies the number of commits
from the current shallow boundary instead of from the tip of
each remote branch history.
--shallow-since=<date>::
`--shallow-since=<date>`::
Deepen or shorten the history of a shallow repository to
include all reachable commits after <date>.
include all reachable commits after _<date>_.
--shallow-exclude=<ref>::
`--shallow-exclude=<ref>`::
Deepen or shorten the history of a shallow repository to
exclude commits reachable from a specified remote branch or tag.
This option can be specified multiple times.
--unshallow::
`--unshallow`::
If the source repository is complete, convert a shallow
repository to a complete one, removing all the limitations
imposed by shallow repositories.
@ -43,13 +43,13 @@
If the source repository is shallow, fetch as much as possible so that
the current repository has the same history as the source repository.
--update-shallow::
`--update-shallow`::
By default when fetching from a shallow repository,
`git fetch` refuses refs that require updating
.git/shallow. This option updates .git/shallow and accepts such
`.git/shallow`. This option updates `.git/shallow` and accepts such
refs.
--negotiation-tip=<commit|glob>::
`--negotiation-tip=(<commit>|<glob>)`::
By default, Git will report, to the server, commits reachable
from all local refs to find common commits in an attempt to
reduce the size of the to-be-received packfile. If specified,
@ -69,28 +69,28 @@ See also the `fetch.negotiationAlgorithm` and `push.negotiate`
configuration variables documented in linkgit:git-config[1], and the
`--negotiate-only` option below.
--negotiate-only::
`--negotiate-only`::
Do not fetch anything from the server, and instead print the
ancestors of the provided `--negotiation-tip=*` arguments,
ancestors of the provided `--negotiation-tip=` arguments,
which we have in common with the server.
+
This is incompatible with `--recurse-submodules=[yes|on-demand]`.
This is incompatible with `--recurse-submodules=(yes|on-demand)`.
Internally this is used to implement the `push.negotiate` option, see
linkgit:git-config[1].
--dry-run::
`--dry-run`::
Show what would be done, without making any changes.
--porcelain::
`--porcelain`::
Print the output to standard output in an easy-to-parse format for
scripts. See section OUTPUT in linkgit:git-fetch[1] for details.
+
This is incompatible with `--recurse-submodules=[yes|on-demand]` and takes
This is incompatible with `--recurse-submodules=(yes|on-demand)` and takes
precedence over the `fetch.output` config option.
ifndef::git-pull[]
--write-fetch-head::
--no-write-fetch-head::
`--write-fetch-head`::
`--no-write-fetch-head`::
Write the list of remote refs fetched in the `FETCH_HEAD`
file directly under `$GIT_DIR`. This is the default.
Passing `--no-write-fetch-head` from the command line tells
@ -98,64 +98,65 @@ ifndef::git-pull[]
file is never written.
endif::git-pull[]
-f::
--force::
When 'git fetch' is used with `<src>:<dst>` refspec, it may
refuse to update the local branch as discussed
`-f`::
`--force`::
ifdef::git-pull[]
in the `<refspec>` part of the linkgit:git-fetch[1]
documentation.
When `git fetch` is used with `<src>:<dst>` refspec, it may
refuse to update the local branch as discussed
in the _<refspec>_ part of the linkgit:git-fetch[1]
documentation.
endif::git-pull[]
ifndef::git-pull[]
in the `<refspec>` part below.
When `git fetch` is used with `<src>:<dst>` refspec, it may
refuse to update the local branch as discussed in the _<refspec>_ part below.
endif::git-pull[]
This option overrides that check.
This option overrides that check.
-k::
--keep::
`-k`::
`--keep`::
Keep downloaded pack.
ifndef::git-pull[]
--multiple::
Allow several <repository> and <group> arguments to be
specified. No <refspec>s may be specified.
`--multiple`::
Allow several _<repository>_ and _<group>_ arguments to be
specified. No __<refspec>__s may be specified.
--auto-maintenance::
--no-auto-maintenance::
--auto-gc::
--no-auto-gc::
`--auto-maintenance`::
`--no-auto-maintenance`::
`--auto-gc`::
`--no-auto-gc`::
Run `git maintenance run --auto` at the end to perform automatic
repository maintenance if needed. (`--[no-]auto-gc` is a synonym.)
repository maintenance if needed.
This is enabled by default.
--write-commit-graph::
--no-write-commit-graph::
`--write-commit-graph`::
`--no-write-commit-graph`::
Write a commit-graph after fetching. This overrides the config
setting `fetch.writeCommitGraph`.
endif::git-pull[]
--prefetch::
`--prefetch`::
Modify the configured refspec to place all refs into the
`refs/prefetch/` namespace. See the `prefetch` task in
linkgit:git-maintenance[1].
-p::
--prune::
`-p`::
`--prune`::
Before fetching, remove any remote-tracking references that no
longer exist on the remote. Tags are not subject to pruning
if they are fetched only because of the default tag
auto-following or due to a --tags option. However, if tags
auto-following or due to a `--tags` option. However, if tags
are fetched due to an explicit refspec (either on the command
line or in the remote configuration, for example if the remote
was cloned with the --mirror option), then they are also
was cloned with the `--mirror` option), then they are also
subject to pruning. Supplying `--prune-tags` is a shorthand for
providing the tag refspec.
ifndef::git-pull[]
+
See the PRUNING section below for more details.
-P::
--prune-tags::
`-P`::
`--prune-tags`::
Before fetching, remove any local tags that no longer exist on
the remote if `--prune` is enabled. This option should be used
more carefully, unlike `--prune` it will remove any local
@ -168,17 +169,17 @@ See the PRUNING section below for more details.
endif::git-pull[]
ifndef::git-pull[]
-n::
`-n`::
endif::git-pull[]
--no-tags::
`--no-tags`::
By default, tags that point at objects that are downloaded
from the remote repository are fetched and stored locally.
This option disables this automatic tag following. The default
behavior for a remote may be specified with the remote.<name>.tagOpt
behavior for a remote may be specified with the `remote.<name>.tagOpt`
setting. See linkgit:git-config[1].
ifndef::git-pull[]
--refetch::
`--refetch`::
Instead of negotiating with the server to avoid transferring commits and
associated objects that are already present locally, this option fetches
all objects as a fresh clone would. Use this to reapply a partial clone
@ -187,29 +188,29 @@ ifndef::git-pull[]
object database pack consolidation to remove any duplicate objects.
endif::git-pull[]
--refmap=<refspec>::
`--refmap=<refspec>`::
When fetching refs listed on the command line, use the
specified refspec (can be given more than once) to map the
refs to remote-tracking branches, instead of the values of
`remote.*.fetch` configuration variables for the remote
repository. Providing an empty `<refspec>` to the
`remote.<name>.fetch` configuration variables for the remote
repository. Providing an empty _<refspec>_ to the
`--refmap` option causes Git to ignore the configured
refspecs and rely entirely on the refspecs supplied as
command-line arguments. See section on "Configured Remote-tracking
Branches" for details.
-t::
--tags::
`-t`::
`--tags`::
Fetch all tags from the remote (i.e., fetch remote tags
`refs/tags/*` into local tags with the same name), in addition
to whatever else would otherwise be fetched. Using this
option alone does not subject tags to pruning, even if --prune
option alone does not subject tags to pruning, even if `--prune`
is used (though tags may be pruned anyway if they are also the
destination of an explicit refspec; see `--prune`).
ifndef::git-pull[]
--recurse-submodules[=(yes|on-demand|no)]::
This option controls if and under what conditions new commits of
`--recurse-submodules[=(yes|on-demand|no)]`::
Control if and under what conditions new commits of
submodules should be fetched too. When recursing through submodules,
`git fetch` always attempts to fetch "changed" submodules, that is, a
submodule that has commits that are referenced by a newly fetched
@ -219,19 +220,19 @@ ifndef::git-pull[]
adds a new submodule, that submodule cannot be fetched until it is
cloned e.g. by `git submodule update`.
+
When set to 'on-demand', only changed submodules are fetched. When set
to 'yes', all populated submodules are fetched and submodules that are
both unpopulated and changed are fetched. When set to 'no', submodules
When set to `on-demand`, only changed submodules are fetched. When set
to `yes`, all populated submodules are fetched and submodules that are
both unpopulated and changed are fetched. When set to `no`, submodules
are never fetched.
+
When unspecified, this uses the value of `fetch.recurseSubmodules` if it
is set (see linkgit:git-config[1]), defaulting to 'on-demand' if unset.
When this option is used without any value, it defaults to 'yes'.
is set (see linkgit:git-config[1]), defaulting to `on-demand` if unset.
When this option is used without any value, it defaults to `yes`.
endif::git-pull[]
-j::
--jobs=<n>::
Number of parallel children to be used for all forms of fetching.
`-j <n>`::
`--jobs=<n>`::
Parallelize all forms of fetching up to _<n>_ jobs at a time.
+
If the `--multiple` option was specified, the different remotes will be fetched
in parallel. If multiple submodules are fetched, they will be fetched in
@ -242,12 +243,12 @@ Typically, parallel recursive and multi-remote fetches will be faster. By
default fetches are performed sequentially, not in parallel.
ifndef::git-pull[]
--no-recurse-submodules::
`--no-recurse-submodules`::
Disable recursive fetching of submodules (this has the same effect as
using the `--recurse-submodules=no` option).
endif::git-pull[]
--set-upstream::
`--set-upstream`::
If the remote is fetched successfully, add upstream
(tracking) reference, used by argument-less
linkgit:git-pull[1] and other commands. For more information,
@ -255,57 +256,57 @@ endif::git-pull[]
linkgit:git-config[1].
ifndef::git-pull[]
--submodule-prefix=<path>::
Prepend <path> to paths printed in informative messages
`--submodule-prefix=<path>`::
Prepend _<path>_ to paths printed in informative messages
such as "Fetching submodule foo". This option is used
internally when recursing over submodules.
--recurse-submodules-default=[yes|on-demand]::
`--recurse-submodules-default=(yes|on-demand)`::
This option is used internally to temporarily provide a
non-negative default value for the --recurse-submodules
non-negative default value for the `--recurse-submodules`
option. All other methods of configuring fetch's submodule
recursion (such as settings in linkgit:gitmodules[5] and
linkgit:git-config[1]) override this option, as does
specifying --[no-]recurse-submodules directly.
specifying `--[no-]recurse-submodules` directly.
-u::
--update-head-ok::
By default 'git fetch' refuses to update the head which
`-u`::
`--update-head-ok`::
By default `git fetch` refuses to update the head which
corresponds to the current branch. This flag disables the
check. This is purely for the internal use for 'git pull'
to communicate with 'git fetch', and unless you are
check. This is purely for the internal use for `git pull`
to communicate with `git fetch`, and unless you are
implementing your own Porcelain you are not supposed to
use it.
endif::git-pull[]
--upload-pack <upload-pack>::
`--upload-pack <upload-pack>`::
When given, and the repository to fetch from is handled
by 'git fetch-pack', `--exec=<upload-pack>` is passed to
by `git fetch-pack`, `--exec=<upload-pack>` is passed to
the command to specify non-default path for the command
run on the other end.
ifndef::git-pull[]
-q::
--quiet::
Pass --quiet to git-fetch-pack and silence any other internally
`-q`::
`--quiet`::
Pass `--quiet` to `git-fetch-pack` and silence any other internally
used git commands. Progress is not reported to the standard error
stream.
-v::
--verbose::
`-v`::
`--verbose`::
Be verbose.
endif::git-pull[]
--progress::
`--progress`::
Progress status is reported on the standard error stream
by default when it is attached to a terminal, unless -q
by default when it is attached to a terminal, unless `-q`
is specified. This flag forces progress status even if the
standard error stream is not directed to a terminal.
-o <option>::
--server-option=<option>::
`-o <option>`::
`--server-option=<option>`::
Transmit the given string to the server when communicating using
protocol version 2. The given string must not contain a NUL or LF
protocol version 2. The given string must not contain a _NUL_ or _LF_
character. The server's handling of server options, including
unknown ones, is server-specific.
When multiple `--server-option=<option>` are given, they are all
@ -314,23 +315,23 @@ endif::git-pull[]
the values of configuration variable `remote.<name>.serverOption`
are used instead.
--show-forced-updates::
`--show-forced-updates`::
By default, git checks if a branch is force-updated during
fetch. This can be disabled through fetch.showForcedUpdates, but
the --show-forced-updates option guarantees this check occurs.
fetch. This can be disabled through `fetch.showForcedUpdates`, but
the `--show-forced-updates` option guarantees this check occurs.
See linkgit:git-config[1].
--no-show-forced-updates::
`--no-show-forced-updates`::
By default, git checks if a branch is force-updated during
fetch. Pass --no-show-forced-updates or set fetch.showForcedUpdates
fetch. Pass `--no-show-forced-updates` or set `fetch.showForcedUpdates`
to false to skip this check for performance reasons. If used during
'git-pull' the --ff-only option will still check for forced updates
`git-pull` the `--ff-only` option will still check for forced updates
before attempting a fast-forward update. See linkgit:git-config[1].
-4::
--ipv4::
`-4`::
`--ipv4`::
Use IPv4 addresses only, ignoring IPv6 addresses.
-6::
--ipv6::
`-6`::
`--ipv6`::
Use IPv6 addresses only, ignoring IPv4 addresses.

View File

@ -162,6 +162,13 @@ Valid <action> for the `--whitespace` option are:
commit creation as the committer date. This allows the
user to lie about the committer date by using the same
value as the author date.
+
WARNING: The history walking machinery assumes that commits have
non-decreasing commit timestamps. You should consider if you really need
to use this option. Then you should only use this option to override the
committer date when applying commits on top of a base which commit is
older (in terms of the commit date) than the oldest patch you are
applying.
--ignore-date::
By default the command records the date from the e-mail

View File

@ -9,26 +9,22 @@ git-bisect - Use binary search to find the commit that introduced a bug
SYNOPSIS
--------
[verse]
'git bisect' <subcommand> <options>
'git bisect' start [--term-(bad|new)=<term-new> --term-(good|old)=<term-old>]
[--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<pathspec>...]
'git bisect' (bad|new|<term-new>) [<rev>]
'git bisect' (good|old|<term-old>) [<rev>...]
'git bisect' terms [--term-(good|old) | --term-(bad|new)]
'git bisect' skip [(<rev>|<range>)...]
'git bisect' next
'git bisect' reset [<commit>]
'git bisect' (visualize|view)
'git bisect' replay <logfile>
'git bisect' log
'git bisect' run <cmd> [<arg>...]
'git bisect' help
DESCRIPTION
-----------
The command takes various subcommands, and different options depending
on the subcommand:
git bisect start [--term-(bad|new)=<term-new> --term-(good|old)=<term-old>]
[--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<pathspec>...]
git bisect (bad|new|<term-new>) [<rev>]
git bisect (good|old|<term-old>) [<rev>...]
git bisect terms [--term-(good|old) | --term-(bad|new)]
git bisect skip [(<rev>|<range>)...]
git bisect reset [<commit>]
git bisect (visualize|view)
git bisect replay <logfile>
git bisect log
git bisect run <cmd> [<arg>...]
git bisect help
This command uses a binary search algorithm to find which commit in
your project's history introduced a bug. You use it by first telling
it a "bad" commit that is known to contain the bug, and a "good"
@ -295,6 +291,19 @@ $ git bisect skip v2.5 v2.5..v2.6
This tells the bisect process that the commits between `v2.5` and
`v2.6` (inclusive) should be skipped.
Bisect next
~~~~~~~~~~~
Normally, after marking a revision as good or bad, Git automatically
computes and checks out the next revision to test. However, if you need to
explicitly request the next bisection step, you can use:
------------
$ git bisect next
------------
You might use this to resume the bisection process after interrupting it
by checking out a different revision.
Cutting down bisection by giving more parameters to bisect start
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -85,6 +85,8 @@ include::blame-options.adoc[]
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

View File

@ -61,7 +61,7 @@ uncommitted changes.
`git checkout -B <branch> [<start-point>]`::
The same as `-b`, except that if the branch already exists it
resets `_<branch>_` to the start point instead of failing.
resets _<branch>_ to the start point instead of failing.
`git checkout --detach [<branch>]`::
`git checkout [--detach] <commit>`::
@ -155,7 +155,7 @@ of it").
`-B <new-branch>`::
The same as `-b`, except that if the branch already exists it
resets `_<branch>_` to the start point instead of failing.
resets _<branch>_ to the start point instead of failing.
`-t`::
`--track[=(direct|inherit)]`::
@ -509,7 +509,7 @@ ARGUMENT DISAMBIGUATION
-----------------------
When you run `git checkout <something>`, Git tries to guess whether
`<something>` is intended to be a branch, a commit, or a set of file(s),
_<something>_ is intended to be a branch, a commit, or a set of file(s),
and then either switches to that branch or commit, or restores the
specified files.

View File

@ -146,7 +146,8 @@ See linkgit:git-rebase[1] for details.
linkgit:git-status[1] for details. Implies `--dry-run`.
`--branch`::
Show the branch and tracking info even in short-format.
Show the branch and tracking info even in short-format. See
linkgit:git-status[1] for details.
`--porcelain`::
When doing a dry-run, give the output in a porcelain-ready
@ -154,12 +155,13 @@ See linkgit:git-rebase[1] for details.
`--dry-run`.
`--long`::
When doing a dry-run, give the output in the long-format.
Implies `--dry-run`.
When doing a dry-run, give the output in the long-format. This
is the default output of linkgit:git-status[1]. Implies
`--dry-run`.
`-z`::
`--null`::
When showing `short` or `porcelain` status output, print the
When showing `short` or `porcelain` linkgit:git-status[1] output, print the
filename verbatim and terminate the entries with _NUL_, instead of _LF_.
If no format is given, implies the `--porcelain` output format.
Without the `-z` option, filenames with "unusual" characters are

View File

@ -66,15 +66,26 @@ fast-import stream! This option is enabled automatically for
remote-helpers that use the `import` capability, as they are
already trusted to run their own code.
--signed-tags=(verbatim|warn-verbatim|warn-strip|strip|abort)::
Specify how to handle signed tags. Behaves in the same way
as the same option in linkgit:git-fast-export[1], except that
default is 'verbatim' (instead of 'abort').
`--signed-tags=(verbatim|warn-verbatim|warn-strip|strip|abort)`::
Specify how to handle signed tags. Behaves in the same way as
the `--signed-commits=<mode>` below, except that the
`strip-if-invalid` mode is not yet supported. Like for signed
commits, the default mode is `verbatim`.
--signed-commits=(verbatim|warn-verbatim|warn-strip|strip|abort)::
Specify how to handle signed commits. Behaves in the same way
as the same option in linkgit:git-fast-export[1], except that
default is 'verbatim' (instead of 'abort').
`--signed-commits=<mode>`::
Specify how to handle signed commits. The following <mode>s
are supported:
+
* `verbatim`, which is the default, will silently import commit
signatures.
* `warn-verbatim` will import them, but will display a warning.
* `abort` will make this program die when encountering a signed
commit.
* `strip` will silently make the commits unsigned.
* `warn-strip` will make them unsigned, but will display a warning.
* `strip-if-invalid` will check signatures and, if they are invalid,
will strip them and display a warning. The validation is performed
in the same way as linkgit:git-verify-commit[1] does it.
Options for Frontends
~~~~~~~~~~~~~~~~~~~~~

View File

@ -8,11 +8,11 @@ git-fetch - Download objects and refs from another repository
SYNOPSIS
--------
[verse]
'git fetch' [<options>] [<repository> [<refspec>...]]
'git fetch' [<options>] <group>
'git fetch' --multiple [<options>] [(<repository> | <group>)...]
'git fetch' --all [<options>]
[synopsis]
git fetch [<options>] [<repository> [<refspec>...]]
git fetch [<options>] <group>
git fetch --multiple [<options>] [(<repository>|<group>)...]
git fetch --all [<options>]
DESCRIPTION
@ -20,19 +20,19 @@ DESCRIPTION
Fetch branches and/or tags (collectively, "refs") from one or more
other repositories, along with the objects necessary to complete their
histories. Remote-tracking branches are updated (see the description
of <refspec> below for ways to control this behavior).
of _<refspec>_ below for ways to control this behavior).
By default, any tag that points into the histories being fetched is
also fetched; the effect is to fetch tags that
point at branches that you are interested in. This default behavior
can be changed by using the --tags or --no-tags options or by
configuring remote.<name>.tagOpt. By using a refspec that fetches tags
can be changed by using the `--tags` or `--no-tags` options or by
configuring `remote.<name>.tagOpt`. By using a refspec that fetches tags
explicitly, you can fetch tags that do not point into branches you
are interested in as well.
'git fetch' can fetch from either a single named repository or URL,
or from several repositories at once if <group> is given and
there is a remotes.<group> entry in the configuration file.
`git fetch` can fetch from either a single named repository or URL,
or from several repositories at once if _<group>_ is given and
there is a `remotes.<group>` entry in the configuration file.
(See linkgit:git-config[1]).
When no remote is specified, by default the `origin` remote will be used,
@ -48,15 +48,15 @@ include::fetch-options.adoc[]
include::pull-fetch-param.adoc[]
--stdin::
`--stdin`::
Read refspecs, one per line, from stdin in addition to those provided
as arguments. The "tag <name>" format is not supported.
as arguments. The "tag _<name>_" format is not supported.
include::urls-remotes.adoc[]
CONFIGURED REMOTE-TRACKING BRANCHES[[CRTB]]
-------------------------------------------
[[CRTB]]
CONFIGURED REMOTE-TRACKING BRANCHES
-----------------------------------
You often interact with the same remote repository by
regularly and repeatedly fetching from it. In order to keep track
@ -84,13 +84,13 @@ This configuration is used in two ways:
* When `git fetch` is run with explicit branches and/or tags
to fetch on the command line, e.g. `git fetch origin master`, the
<refspec>s given on the command line determine what are to be
_<refspec>s_ given on the command line determine what are to be
fetched (e.g. `master` in the example,
which is a short-hand for `master:`, which in turn means
"fetch the 'master' branch but I do not explicitly say what
"fetch the `master` branch but I do not explicitly say what
remote-tracking branch to update with it from the command line"),
and the example command will
fetch _only_ the 'master' branch. The `remote.<repository>.fetch`
fetch _only_ the `master` branch. The `remote.<repository>.fetch`
values determine which
remote-tracking branch, if any, is updated. When used in this
way, the `remote.<repository>.fetch` values do not have any
@ -144,9 +144,9 @@ tracking branches that are deleted, but any local tag that doesn't
exist on the remote.
This might not be what you expect, i.e. you want to prune remote
`<name>`, but also explicitly fetch tags from it, so when you fetch
_<name>_, but also explicitly fetch tags from it, so when you fetch
from it you delete all your local tags, most of which may not have
come from the `<name>` remote in the first place.
come from the _<name>_ remote in the first place.
So be careful when using this with a refspec like
`refs/tags/*:refs/tags/*`, or any other refspec which might map
@ -213,11 +213,11 @@ of the form:
<flag> <old-object-id> <new-object-id> <local-reference>
-------------------------------
The status of up-to-date refs is shown only if the --verbose option is
The status of up-to-date refs is shown only if the `--verbose` option is
used.
In compact output mode, specified with configuration variable
fetch.output, if either entire `<from>` or `<to>` is found in the
fetch.output, if either entire _<from>_ or _<to>_ is found in the
other string, it will be substituted with `*` in the other string. For
example, `master -> origin/master` becomes `master -> origin/*`.
@ -303,7 +303,7 @@ include::config/fetch.adoc[]
BUGS
----
Using --recurse-submodules can only fetch new commits in submodules that are
Using `--recurse-submodules` can only fetch new commits in submodules that are
present locally e.g. in `$GIT_DIR/modules/`. If the upstream adds a new
submodule, that submodule cannot be fetched until it is cloned e.g. by `git
submodule update`. This is expected to be fixed in a future Git version.

View File

@ -12,6 +12,7 @@ SYNOPSIS
'git maintenance' run [<options>]
'git maintenance' start [--scheduler=<scheduler>]
'git maintenance' (stop|register|unregister) [<options>]
'git maintenance' is-needed [<options>]
DESCRIPTION
@ -84,6 +85,16 @@ The `unregister` subcommand will report an error if the current repository
is not already registered. Use the `--force` option to return success even
when the current repository is not registered.
is-needed::
Check whether maintenance needs to be run without actually running it.
Exits with a 0 status code if maintenance needs to be run, 1 otherwise.
Ideally used with the '--auto' flag.
+
If one or more `--task` options are specified, then those tasks are checked
in that order. Otherwise, the tasks are determined by which
`maintenance.<task>.enabled` config options are true. By default, only
`maintenance.gc.enabled` is true.
TASKS
-----
@ -183,6 +194,8 @@ OPTIONS
in the `gc.auto` config setting, or when the number of pack-files
exceeds the `gc.autoPackLimit` config setting. Not compatible with
the `--schedule` option.
When combined with the `is-needed` subcommand, check if the required
thresholds are met without actually running maintenance.
--schedule::
When combined with the `run` subcommand, run maintenance tasks

View File

@ -8,8 +8,8 @@ git-pull - Fetch from and integrate with another repository or a local branch
SYNOPSIS
--------
[verse]
'git pull' [<options>] [<repository> [<refspec>...]]
[synopsis]
git pull [<options>] [<repository> [<refspec>...]]
DESCRIPTION
@ -37,13 +37,13 @@ You can also set the configuration options `pull.rebase`, `pull.squash`,
or `pull.ff` with your preferred behaviour.
If there's a merge conflict during the merge or rebase that you don't
want to handle, you can safely abort it with `git merge --abort` or `git
--rebase abort`.
want to handle, you can safely abort it with `git merge --abort` or
`git rebase --abort`.
OPTIONS
-------
<repository>::
_<repository>_::
The "remote" repository to pull from. This can be either
a URL (see the section <<URLS,GIT URLS>> below) or the name
of a remote (see the section <<REMOTES,REMOTES>> below).
@ -52,29 +52,29 @@ Defaults to the configured upstream for the current branch, or `origin`.
See <<UPSTREAM-BRANCHES,UPSTREAM BRANCHES>> below for more on how to
configure upstreams.
<refspec>::
_<refspec>_::
Which branch or other reference(s) to fetch and integrate into the
current branch, for example `main` in `git pull origin main`.
Defaults to the configured upstream for the current branch.
+
This can be a branch, tag, or other collection of reference(s).
See <<fetch-refspec,<refspec>>> below under "Options related to fetching"
See <<fetch-refspec,_<refspec>_>> below under "Options related to fetching"
for the full syntax, and <<DEFAULT-BEHAVIOUR,DEFAULT BEHAVIOUR>> below
for how `git pull` uses this argument to determine which remote branch
to integrate.
-q::
--quiet::
`-q`::
`--quiet`::
This is passed to both underlying git-fetch to squelch reporting of
during transfer, and underlying git-merge to squelch output during
merging.
-v::
--verbose::
Pass --verbose to git-fetch and git-merge.
`-v`::
`--verbose`::
Pass `--verbose` to git-fetch and git-merge.
--recurse-submodules[=(yes|on-demand|no)]::
--no-recurse-submodules::
`--recurse-submodules[=(yes|on-demand|no)]`::
`--no-recurse-submodules`::
This option controls if new commits of populated submodules should
be fetched, and if the working trees of active submodules should be
updated, too (see linkgit:git-fetch[1], linkgit:git-config[1] and
@ -91,21 +91,20 @@ Options related to merging
include::merge-options.adoc[]
-r::
--rebase[=(false|true|merges|interactive)]::
When true, rebase the current branch on top of the upstream
`-r`::
`--rebase[=(true|merges|false|interactive)]`::
`true`;; rebase the current branch on top of the upstream
branch after fetching. If there is a remote-tracking branch
corresponding to the upstream branch and the upstream branch
was rebased since last fetched, the rebase uses that information
to avoid rebasing non-local changes.
+
When set to `merges`, rebase using `git rebase --rebase-merges` so that
to avoid rebasing non-local changes. This is the default.
`merges`;; rebase using `git rebase --rebase-merges` so that
the local merge commits are included in the rebase (see
linkgit:git-rebase[1] for details).
+
When false, merge the upstream branch into the current branch.
+
When `interactive`, enable the interactive mode of rebase.
`false`;; merge the upstream branch into the current branch.
`interactive`;; enable the interactive mode of rebase.
+
See `pull.rebase`, `branch.<name>.rebase` and `branch.autoSetupRebase` in
linkgit:git-config[1] if you want to make `git pull` always use
@ -117,8 +116,8 @@ It rewrites history, which does not bode well when you
published that history already. Do *not* use this option
unless you have read linkgit:git-rebase[1] carefully.
--no-rebase::
This is shorthand for --rebase=false.
`--no-rebase`::
This is shorthand for `--rebase=false`.
Options related to fetching
~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -178,7 +177,7 @@ uses the refspec from the configuration or
rules apply:
. If `branch.<name>.merge` configuration for the current
branch `<name>` exists, that is the name of the branch at the
branch _<name>_ exists, that is the name of the branch at the
remote site that is merged.
. If the refspec is a globbing one, nothing is merged.
@ -198,9 +197,9 @@ $ git pull
$ git pull origin
------------------------------------------------
+
Normally the branch merged in is the HEAD of the remote repository,
but the choice is determined by the branch.<name>.remote and
branch.<name>.merge options; see linkgit:git-config[1] for details.
Normally the branch merged in is the `HEAD` of the remote repository,
but the choice is determined by the `branch.<name>.remote` and
`branch.<name>.merge` options; see linkgit:git-config[1] for details.
* Merge into the current branch the remote branch `next`:
+
@ -208,7 +207,7 @@ branch.<name>.merge options; see linkgit:git-config[1] for details.
$ git pull origin next
------------------------------------------------
+
This leaves a copy of `next` temporarily in FETCH_HEAD, and
This leaves a copy of `next` temporarily in `FETCH_HEAD`, and
updates the remote-tracking branch `origin/next`.
The same can be done by invoking fetch and merge:
+
@ -219,14 +218,14 @@ $ git merge origin/next
If you tried a pull which resulted in complex conflicts and
would want to start over, you can recover with 'git reset'.
would want to start over, you can recover with `git reset`.
include::transfer-data-leaks.adoc[]
BUGS
----
Using --recurse-submodules can only fetch new commits in already checked
Using `--recurse-submodules` can only fetch new commits in already checked
out submodules right now. When e.g. upstream added a new submodule in the
just fetched commits of the superproject the submodule itself cannot be
fetched, making it impossible to check out that submodule later without

View File

@ -8,13 +8,13 @@ git-push - Update remote refs along with associated objects
SYNOPSIS
--------
[verse]
'git push' [--all | --branches | --mirror | --tags] [--follow-tags] [--atomic] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
[--repo=<repository>] [-f | --force] [-d | --delete] [--prune] [-q | --quiet] [-v | --verbose]
[-u | --set-upstream] [-o <string> | --push-option=<string>]
[--[no-]signed|--signed=(true|false|if-asked)]
[--force-with-lease[=<refname>[:<expect>]] [--force-if-includes]]
[--no-verify] [<repository> [<refspec>...]]
[synopsis]
git push [--all | --branches | --mirror | --tags] [--follow-tags] [--atomic] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
[--repo=<repository>] [-f | --force] [-d | --delete] [--prune] [-q | --quiet] [-v | --verbose]
[-u | --set-upstream] [-o <string> | --push-option=<string>]
[--[no-]signed | --signed=(true|false|if-asked)]
[--force-with-lease[=<refname>[:<expect>]] [--force-if-includes]]
[--no-verify] [<repository> [<refspec>...]]
DESCRIPTION
-----------
@ -35,7 +35,7 @@ To decide which branches, tags, or other refs to push, Git uses
1. The `<refspec>` argument(s) (for example `main` in `git push origin main`)
or the `--all`, `--mirror`, or `--tags` options
2. The `remote.*.push` configuration for the repository being pushed to
2. The `remote.<name>.push` configuration for the repository being pushed to
3. The `push.default` configuration. The default is `push.default=simple`,
which will push to a branch with the same name as the current branch.
See the <<CONFIGURATION,CONFIGURATION>> section below for more on `push.default`.
@ -49,25 +49,25 @@ You can make interesting things happen to a repository
every time you push into it, by setting up 'hooks' there. See
documentation for linkgit:git-receive-pack[1].
OPTIONS[[OPTIONS]]
------------------
<repository>::
[[OPTIONS]]
OPTIONS
-------
_<repository>_::
The "remote" repository that is the destination of a push
operation. This parameter can be either a URL
(see the section <<URLS,GIT URLS>> below) or the name
of a remote (see the section <<REMOTES,REMOTES>> below).
<refspec>...::
`<refspec>...`::
Specify what destination ref to update with what source object.
+
The format for a refspec is [+]<src>[:<dst>], for example `main`,
The format for a refspec is `[+]<src>[:<dst>]`, for example `main`,
`main:other`, or `HEAD^:refs/heads/main`.
+
The `<src>` is often the name of the local branch to push, but it can be
The _<src>_ is often the name of the local branch to push, but it can be
any arbitrary "SHA-1 expression" (see linkgit:gitrevisions[7]).
+
The `<dst>` determines what ref to update on the remote side. It must be the
The _<dst>_ determines what ref to update on the remote side. It must be the
name of a branch, tag, or other ref, not an arbitrary expression.
+
The `+` is optional and does the same thing as `--force`.
@ -78,23 +78,23 @@ and destination, or with a shorter form (for example `main` or
`main:other`). Here are the rules for how refspecs are expanded,
as well as various other special refspec forms:
+
* `<src>` without a `:<dst>` means to update the same ref as the
`<src>`, unless the `remote.<repository>.push` configuration specifies a
different <dst>. For example, if `main` is a branch, then the refspec
* _<src>_ without a `:<dst>` means to update the same ref as the
_<src>_, unless the `remote.<repository>.push` configuration specifies a
different _<dst>_. For example, if `main` is a branch, then the refspec
`main` expands to `main:refs/heads/main`.
* If `<dst>` unambiguously refers to a ref on the <repository> remote,
* If _<dst>_ unambiguously refers to a ref on the <repository> remote,
then expand it to that ref. For example, if `v1.0` is a tag on the
remote, then `HEAD:v1.0` expands to `HEAD:refs/tags/v1.0`.
* If `<src>` resolves to a ref starting with `refs/heads/` or `refs/tags/`,
* If _<src>_ resolves to a ref starting with `refs/heads/` or `refs/tags/`,
then prepend that to <dst>. For example, if `main` is a branch, then
`main:other` expands to `main:refs/heads/other`
* The special refspec `:` (or `+:` to allow non-fast-forward updates)
directs Git to push "matching" branches: for every branch that exists on
the local side, the remote side is updated if a branch of the same name
already exists on the remote side.
* <src> may contain a * to indicate a simple pattern match.
* _<src>_ may contain a `*` to indicate a simple pattern match.
This works like a glob that matches any ref matching the pattern.
There must be only one * in both the `<src>` and `<dst>`.
There must be only one `*` in both the `<src>` and `<dst>`.
It will map refs to the destination by replacing the * with the
contents matched from the source. For example, `refs/heads/*:refs/heads/*`
will push all branches.
@ -102,11 +102,11 @@ as well as various other special refspec forms:
This specifies refs to exclude. A ref will be considered to
match if it matches at least one positive refspec, and does not
match any negative refspec. Negative refspecs can be pattern refspecs.
They must only contain a `<src>`.
They must only contain a _<src>_.
Fully spelled out hex object names are also not supported.
For example, `git push origin 'refs/heads/*' '^refs/heads/dev-*'`
will push all branches except for those starting with `dev-`
* If `<src>` is empty, it deletes the `<dst>` ref from the remote
* If _<src>_ is empty, it deletes the _<dst>_ ref from the remote
repository. For example, `git push origin :dev` will
delete the `dev` branch.
* `tag <tag>` expands to `refs/tags/<tag>:refs/tags/<tag>`.
@ -121,12 +121,12 @@ as well as various other special refspec forms:
Not all updates are allowed: see PUSH RULES below for the details.
--all::
--branches::
`--all`::
`--branches`::
Push all branches (i.e. refs under `refs/heads/`); cannot be
used with other <refspec>.
--prune::
`--prune`::
Remove remote branches that don't have a local counterpart. For example
a remote branch `tmp` will be removed if a local branch with the same
name doesn't exist any more. This also respects refspecs, e.g.
@ -134,7 +134,7 @@ Not all updates are allowed: see PUSH RULES below for the details.
make sure that remote `refs/tmp/foo` will be removed if `refs/heads/foo`
doesn't exist.
--mirror::
`--mirror`::
Instead of naming each ref to push, specifies that all
refs under `refs/` (which includes but is not
limited to `refs/heads/`, `refs/remotes/`, and `refs/tags/`)
@ -145,26 +145,26 @@ Not all updates are allowed: see PUSH RULES below for the details.
if the configuration option `remote.<remote>.mirror` is
set.
-n::
--dry-run::
`-n`::
`--dry-run`::
Do everything except actually send the updates.
--porcelain::
`--porcelain`::
Produce machine-readable output. The output status line for each ref
will be tab-separated and sent to stdout instead of stderr. The full
symbolic names of the refs will be given.
-d::
--delete::
`-d`::
`--delete`::
All listed refs are deleted from the remote repository. This is
the same as prefixing all refs with a colon.
--tags::
`--tags`::
All refs under `refs/tags` are pushed, in
addition to refspecs explicitly listed on the command
line.
--follow-tags::
`--follow-tags`::
Push all the refs that would be pushed without this option,
and also push annotated tags in `refs/tags` that are missing
from the remote but are pointing at commit-ish that are
@ -172,29 +172,34 @@ Not all updates are allowed: see PUSH RULES below for the details.
with configuration variable `push.followTags`. For more
information, see `push.followTags` in linkgit:git-config[1].
--signed::
--no-signed::
--signed=(true|false|if-asked)::
`--signed`::
`--no-signed`::
`--signed=(true|false|if-asked)`::
GPG-sign the push request to update refs on the receiving
side, to allow it to be checked by the hooks and/or be
logged. If `false` or `--no-signed`, no signing will be
attempted. If `true` or `--signed`, the push will fail if the
server does not support signed pushes. If set to `if-asked`,
sign if and only if the server supports signed pushes. The push
will also fail if the actual call to `gpg --sign` fails. See
linkgit:git-receive-pack[1] for the details on the receiving end.
logged. Possible values are:
`false`;;
`--no-signed`;;
no signing will be attempted.
`true`;;
`--signed`;;
the push will fail if the server does not support signed pushes.
`if-asked`;;
sign if and only if the server supports signed pushes. The push
will also fail if the actual call to `gpg --sign` fails. See
linkgit:git-receive-pack[1] for the details on the receiving end.
--atomic::
--no-atomic::
`--atomic`::
`--no-atomic`::
Use an atomic transaction on the remote side if available.
Either all refs are updated, or on error, no refs are updated.
If the server does not support atomic pushes the push will fail.
-o <option>::
--push-option=<option>::
`-o <option>`::
`--push-option=<option>`::
Transmit the given string to the server, which passes them to
the pre-receive as well as the post-receive hook. The given string
must not contain a NUL or LF character.
must not contain a _NUL_ or _LF_ character.
When multiple `--push-option=<option>` are given, they are
all sent to the other side in the order listed on the
command line.
@ -202,22 +207,22 @@ Not all updates are allowed: see PUSH RULES below for the details.
line, the values of configuration variable `push.pushOption`
are used instead.
--receive-pack=<git-receive-pack>::
--exec=<git-receive-pack>::
`--receive-pack=<git-receive-pack>`::
`--exec=<git-receive-pack>`::
Path to the 'git-receive-pack' program on the remote
end. Sometimes useful when pushing to a remote
repository over ssh, and you do not have the program in
a directory on the default $PATH.
a directory on the default `$PATH`.
--force-with-lease::
--no-force-with-lease::
--force-with-lease=<refname>::
--force-with-lease=<refname>:<expect>::
Usually, "git push" refuses to update a remote ref that is
`--force-with-lease`::
`--no-force-with-lease`::
`--force-with-lease=<refname>`::
`--force-with-lease=<refname>:<expect>`::
Usually, `git push` refuses to update a remote ref that is
not an ancestor of the local ref used to overwrite it.
+
This option overrides this restriction if the current value of the
remote ref is the expected value. "git push" fails otherwise.
remote ref is the expected value. `git push` fails otherwise.
+
Imagine that you have to rebase what you have already published.
You will have to bypass the "must fast-forward" rule in order to
@ -239,16 +244,16 @@ current value to be the same as the remote-tracking branch we have
for them.
+
`--force-with-lease=<refname>`, without specifying the expected value, will
protect the named ref (alone), if it is going to be updated, by
protect _<refname>_ (alone), if it is going to be updated, by
requiring its current value to be the same as the remote-tracking
branch we have for it.
+
`--force-with-lease=<refname>:<expect>` will protect the named ref (alone),
`--force-with-lease=<refname>:<expect>` will protect _<refname>_ (alone),
if it is going to be updated, by requiring its current value to be
the same as the specified value `<expect>` (which is allowed to be
the same as the specified value _<expect>_ (which is allowed to be
different from the remote-tracking branch we have for the refname,
or we do not even have to have such a remote-tracking branch when
this form is used). If `<expect>` is the empty string, then the named ref
this form is used). If _<expect>_ is the empty string, then the named ref
must not already exist.
+
Note that all forms other than `--force-with-lease=<refname>:<expect>`
@ -256,7 +261,7 @@ that specifies the expected current value of the ref explicitly are
still experimental and their semantics may change as we gain experience
with this feature.
+
"--no-force-with-lease" will cancel all the previous --force-with-lease on the
`--no-force-with-lease` will cancel all the previous `--force-with-lease` on the
command line.
+
A general note on safety: supplying this option without an expected
@ -276,23 +281,29 @@ If your editor or some other system is running `git fetch` in the
background for you a way to mitigate this is to simply set up another
remote:
+
git remote add origin-push $(git config remote.origin.url)
git fetch origin-push
----
git remote add origin-push $(git config remote.origin.url)
git fetch origin-push
----
+
Now when the background process runs `git fetch origin` the references
on `origin-push` won't be updated, and thus commands like:
+
git push --force-with-lease origin-push
----
git push --force-with-lease origin-push
----
+
Will fail unless you manually run `git fetch origin-push`. This method
is of course entirely defeated by something that runs `git fetch
--all`, in that case you'd need to either disable it or do something
more tedious like:
+
git fetch # update 'master' from remote
git tag base master # mark our base point
git rebase -i master # rewrite some commits
git push --force-with-lease=master:base master:master
----
git fetch # update 'master' from remote
git tag base master # mark our base point
git rebase -i master # rewrite some commits
git push --force-with-lease=master:base master:master
----
+
I.e. create a `base` tag for versions of the upstream code that you've
seen and are willing to overwrite, then rewrite history, and finally
@ -308,26 +319,26 @@ verify if updates from the remote-tracking refs that may have been
implicitly updated in the background are integrated locally before
allowing a forced update.
-f::
--force::
`-f`::
`--force`::
Usually, `git push` will refuse to update a branch that is not an
ancestor of the commit being pushed.
+
This flag disables that check, the other safety checks in PUSH RULES
below, and the checks in --force-with-lease. It can cause the remote
below, and the checks in `--force-with-lease`. It can cause the remote
repository to lose commits; use it with care.
+
Note that `--force` applies to all the refs that are pushed, hence
using it with `push.default` set to `matching` or with multiple push
destinations configured with `remote.*.push` may overwrite refs
destinations configured with `remote.<name>.push` may overwrite refs
other than the current branch (including local refs that are
strictly behind their remote counterpart). To force a push to only
one branch, use a `+` in front of the refspec to push (e.g `git push
origin +master` to force a push to the `master` branch). See the
`<refspec>...` section above for details.
--force-if-includes::
--no-force-if-includes::
`--force-if-includes`::
`--no-force-if-includes`::
Force an update only if the tip of the remote-tracking ref
has been integrated locally.
+
@ -343,72 +354,78 @@ a "no-op".
+
Specifying `--no-force-if-includes` disables this behavior.
--repo=<repository>::
This option is equivalent to the <repository> argument. If both
`--repo=<repository>`::
This option is equivalent to the _<repository>_ argument. If both
are specified, the command-line argument takes precedence.
-u::
--set-upstream::
`-u`::
`--set-upstream`::
For every branch that is up to date or successfully pushed, add
upstream (tracking) reference, used by argument-less
linkgit:git-pull[1] and other commands. For more information,
see `branch.<name>.merge` in linkgit:git-config[1].
--thin::
--no-thin::
`--thin`::
`--no-thin`::
These options are passed to linkgit:git-send-pack[1]. A thin transfer
significantly reduces the amount of sent data when the sender and
receiver share many of the same objects in common. The default is
`--thin`.
-q::
--quiet::
`-q`::
`--quiet`::
Suppress all output, including the listing of updated refs,
unless an error occurs. Progress is not reported to the standard
error stream.
-v::
--verbose::
`-v`::
`--verbose`::
Run verbosely.
--progress::
`--progress`::
Progress status is reported on the standard error stream
by default when it is attached to a terminal, unless -q
by default when it is attached to a terminal, unless `-q`
is specified. This flag forces progress status even if the
standard error stream is not directed to a terminal.
--no-recurse-submodules::
--recurse-submodules=check|on-demand|only|no::
`--no-recurse-submodules`::
`--recurse-submodules=(check|on-demand|only|no)`::
May be used to make sure all submodule commits used by the
revisions to be pushed are available on a remote-tracking branch.
If 'check' is used Git will verify that all submodule commits that
Possible values are:
`check`;;
Git will verify that all submodule commits that
changed in the revisions to be pushed are available on at least one
remote of the submodule. If any commits are missing the push will
be aborted and exit with non-zero status. If 'on-demand' is used
be aborted and exit with non-zero status.
`on-demand`;;
all submodules that changed in the revisions to be pushed will be
pushed. If on-demand was not able to push all necessary revisions it will
also be aborted and exit with non-zero status. If 'only' is used all
submodules will be pushed while the superproject is left
unpushed. A value of 'no' or using `--no-recurse-submodules` can be used
to override the push.recurseSubmodules configuration variable when no
submodule recursion is required.
pushed. If `on-demand` was not able to push all necessary revisions it will
also be aborted and exit with non-zero status.
`only`;;
all submodules will be pushed while the superproject is left
unpushed.
`no`;;
override the `push.recurseSubmodules` configuration variable when no
submodule recursion is required. Similar to using `--no-recurse-submodules`.
+
When using 'on-demand' or 'only', if a submodule has a
"push.recurseSubmodules={on-demand,only}" or "submodule.recurse" configuration,
further recursion will occur. In this case, "only" is treated as "on-demand".
When using `on-demand` or `only`, if a submodule has a
`push.recurseSubmodules=(on-demand|only)` or `submodule.recurse` configuration,
further recursion will occur. In this case, `only` is treated as `on-demand`.
--verify::
--no-verify::
`--verify`::
`--no-verify`::
Toggle the pre-push hook (see linkgit:githooks[5]). The
default is --verify, giving the hook a chance to prevent the
push. With --no-verify, the hook is bypassed completely.
default is `--verify`, giving the hook a chance to prevent the
push. With `--no-verify`, the hook is bypassed completely.
-4::
--ipv4::
`-4`::
`--ipv4`::
Use IPv4 addresses only, ignoring IPv6 addresses.
-6::
--ipv6::
`-6`::
`--ipv6`::
Use IPv6 addresses only, ignoring IPv4 addresses.
include::urls-remotes.adoc[]
@ -427,16 +444,16 @@ representing the status of a single ref. Each line is of the form:
<flag> <summary> <from> -> <to> (<reason>)
-------------------------------
If --porcelain is used, then each line of the output is of the form:
If `--porcelain` is used, then each line of the output is of the form:
-------------------------------
<flag> \t <from>:<to> \t <summary> (<reason>)
-------------------------------
The status of up-to-date refs is shown only if --porcelain or --verbose
The status of up-to-date refs is shown only if `--porcelain` or `--verbose`
option is used.
flag::
_<flag>_::
A single character indicating the status of the ref:
(space);; for a successfully pushed fast-forward;
`+`;; for a successful forced update;
@ -445,7 +462,7 @@ flag::
`!`;; for a ref that was rejected or failed to push; and
`=`;; for a ref that was up to date and did not need pushing.
summary::
_<summary>_::
For a successfully pushed ref, the summary shows the old and new
values of the ref in a form suitable for using as an argument to
`git log` (this is `<old>..<new>` in most cases, and
@ -586,7 +603,7 @@ Updating A with the resulting merge commit will fast-forward and your
push will be accepted.
Alternatively, you can rebase your change between X and B on top of A,
with "git pull --rebase", and push the result back. The rebase will
with `git pull --rebase`, and push the result back. The rebase will
create a new commit D that builds the change between X and B on top of
A.
@ -604,12 +621,12 @@ accepted.
There is another common situation where you may encounter non-fast-forward
rejection when you try to push, and it is possible even when you are
pushing into a repository nobody else pushes into. After you push commit
A yourself (in the first picture in this section), replace it with "git
commit --amend" to produce commit B, and you try to push it out, because
A yourself (in the first picture in this section), replace it with `git
commit --amend` to produce commit B, and you try to push it out, because
forgot that you have pushed A out already. In such a case, and only if
you are certain that nobody in the meantime fetched your earlier commit A
(and started building on top of it), you can run "git push --force" to
overwrite it. In other words, "git push --force" is a method reserved for
(and started building on top of it), you can run `git push --force` to
overwrite it. In other words, `git push --force` is a method reserved for
a case where you do mean to lose history.
@ -627,18 +644,18 @@ EXAMPLES
variable) if it has the same name as the current branch, and
errors out without pushing otherwise.
+
The default behavior of this command when no <refspec> is given can be
The default behavior of this command when no _<refspec>_ is given can be
configured by setting the `push` option of the remote, or the `push.default`
configuration variable.
+
For example, to default to pushing only the current branch to `origin`
use `git config remote.origin.push HEAD`. Any valid <refspec> (like
use `git config remote.origin.push HEAD`. Any valid _<refspec>_ (like
the ones in the examples below) can be configured as the default for
`git push origin`.
`git push origin :`::
Push "matching" branches to `origin`. See
<refspec> in the <<OPTIONS,OPTIONS>> section above for a
_<refspec>_ in the <<OPTIONS,OPTIONS>> section above for a
description of "matching" branches.
`git push origin master`::

View File

@ -87,7 +87,7 @@ of the to-be-rebased branch. However, `ORIG_HEAD` is not guaranteed to still
point to that commit at the end of the rebase if other commands that change
`ORIG_HEAD` (like `git reset`) are used during the rebase. The previous branch
tip, however, is accessible using the reflog of the current branch (i.e. `@{1}`,
see linkgit:gitrevisions[7].
see linkgit:gitrevisions[7]).
TRANSPLANTING A TOPIC BRANCH WITH --ONTO
----------------------------------------
@ -474,6 +474,13 @@ See also INCOMPATIBLE OPTIONS below.
Instead of using the current time as the committer date, use
the author date of the commit being rebased as the committer
date. This option implies `--force-rebase`.
+
WARNING: The history walking machinery assumes that commits have
non-decreasing commit timestamps. You should consider if you really need
to use this option. Then you should only use this option to override the
committer date when rebasing commits on top of a base which commit is
older (in terms of the commit date) than the oldest commit you are
applying (in terms of the author date).
--ignore-date::
--reset-author-date::

View File

@ -77,14 +77,14 @@ to the new separate pack will be written.
Only useful with `--cruft -d`.
--max-cruft-size=<n>::
Overrides `--max-pack-size` for cruft packs. Inherits the value of
Override `--max-pack-size` for cruft packs. Inherits the value of
`--max-pack-size` (if any) by default. See the documentation for
`--max-pack-size` for more details.
--combine-cruft-below-size=<n>::
When generating cruft packs without pruning, only repack
existing cruft packs whose size is strictly less than `<n>`,
where `<n>` represents a number of bytes, which can optionally
existing cruft packs whose size is strictly less than `<n>`
bytes, which can optionally
be suffixed with "k", "m", or "g". Cruft packs whose size is
greater than or equal to `<n>` are left as-is and not repacked.
Useful when you want to avoid repacking large cruft pack(s) in

View File

@ -9,16 +9,17 @@ git-replay - EXPERIMENTAL: Replay commits on a new base, works with bare repos t
SYNOPSIS
--------
[verse]
(EXPERIMENTAL!) 'git replay' ([--contained] --onto <newbase> | --advance <branch>) <revision-range>...
(EXPERIMENTAL!) 'git replay' ([--contained] --onto <newbase> | --advance <branch>) [--ref-action[=<mode>]] <revision-range>
DESCRIPTION
-----------
Takes ranges of commits and replays them onto a new location. Leaves
the working tree and the index untouched, and updates no references.
The output of this command is meant to be used as input to
`git update-ref --stdin`, which would update the relevant branches
(see the OUTPUT section below).
Takes a range of commits and replays them onto a new location. Leaves
the working tree and the index untouched. By default, updates the
relevant references using an atomic transaction (all refs update or
none). Use `--ref-action=print` to avoid automatic ref updates and
instead get update commands that can be piped to `git update-ref --stdin`
(see the <<output,OUTPUT>> section below).
THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.
@ -29,33 +30,51 @@ OPTIONS
Starting point at which to create the new commits. May be any
valid commit, and not just an existing branch name.
+
When `--onto` is specified, the update-ref command(s) in the output will
update the branch(es) in the revision range to point at the new
commits, similar to the way how `git rebase --update-refs` updates
multiple branches in the affected range.
When `--onto` is specified, the branch(es) in the revision range will be
updated to point at the new commits, similar to the way `git rebase --update-refs`
updates multiple branches in the affected range.
--advance <branch>::
Starting point at which to create the new commits; must be a
branch name.
+
When `--advance` is specified, the update-ref command(s) in the output
will update the branch passed as an argument to `--advance` to point at
the new commits (in other words, this mimics a cherry-pick operation).
The history is replayed on top of the <branch> and <branch> is updated to
point at the tip of the resulting history. This is different from `--onto`,
which uses the target only as a starting point without updating it.
--contained::
Update all branches that point at commits in
<revision-range>. Requires `--onto`.
--ref-action[=<mode>]::
Control how references are updated. The mode can be:
+
--
* `update` (default): Update refs directly using an atomic transaction.
All refs are updated or none are (all-or-nothing behavior).
* `print`: Output update-ref commands for pipeline use. This is the
traditional behavior where output can be piped to `git update-ref --stdin`.
--
+
The default mode can be configured via the `replay.refAction` configuration variable.
<revision-range>::
Range of commits to replay. More than one <revision-range> can
be passed, but in `--advance <branch>` mode, they should have
a single tip, so that it's clear where <branch> should point
to. See "Specifying Ranges" in linkgit:git-rev-parse[1] and the
"Commit Limiting" options below.
Range of commits to replay; see "Specifying Ranges" in
linkgit:git-rev-parse[1]. In `--advance <branch>` mode, the
range should have a single tip, so that it's clear to which tip the
advanced <branch> should point.
include::rev-list-options.adoc[]
[[output]]
OUTPUT
------
When there are no conflicts, the output of this command is usable as
input to `git update-ref --stdin`. It is of the form:
By default, or with `--ref-action=update`, this command produces no output on
success, as refs are updated directly using an atomic transaction.
When using `--ref-action=print`, the output is usable as input to
`git update-ref --stdin`. It is of the form:
update refs/heads/branch1 ${NEW_branch1_HASH} ${OLD_branch1_HASH}
update refs/heads/branch2 ${NEW_branch2_HASH} ${OLD_branch2_HASH}
@ -66,6 +85,10 @@ the shape of the history being replayed. When using `--advance`, the
number of refs updated is always one, but for `--onto`, it can be one
or more (rebasing multiple branches simultaneously is supported).
There is no stderr output on conflicts; see the <<exit-status,EXIT
STATUS>> section below.
[[exit-status]]
EXIT STATUS
-----------
@ -81,6 +104,14 @@ To simply rebase `mybranch` onto `target`:
------------
$ git replay --onto target origin/main..mybranch
------------
The refs are updated atomically and no output is produced on success.
To see what would be updated without actually updating:
------------
$ git replay --ref-action=print --onto target origin/main..mybranch
update refs/heads/mybranch ${NEW_mybranch_HASH} ${OLD_mybranch_HASH}
------------
@ -88,33 +119,29 @@ To cherry-pick the commits from mybranch onto target:
------------
$ git replay --advance target origin/main..mybranch
update refs/heads/target ${NEW_target_HASH} ${OLD_target_HASH}
------------
Note that the first two examples replay the exact same commits and on
top of the exact same new base, they only differ in that the first
provides instructions to make mybranch point at the new commits and
the second provides instructions to make target point at them.
updates mybranch to point at the new commits and the second updates
target to point at them.
What if you have a stack of branches, one depending upon another, and
you'd really like to rebase the whole set?
------------
$ git replay --contained --onto origin/main origin/main..tipbranch
update refs/heads/branch1 ${NEW_branch1_HASH} ${OLD_branch1_HASH}
update refs/heads/branch2 ${NEW_branch2_HASH} ${OLD_branch2_HASH}
update refs/heads/tipbranch ${NEW_tipbranch_HASH} ${OLD_tipbranch_HASH}
------------
All three branches (`branch1`, `branch2`, and `tipbranch`) are updated
atomically.
When calling `git replay`, one does not need to specify a range of
commits to replay using the syntax `A..B`; any range expression will
do:
------------
$ git replay --onto origin/main ^base branch1 branch2 branch3
update refs/heads/branch1 ${NEW_branch1_HASH} ${OLD_branch1_HASH}
update refs/heads/branch2 ${NEW_branch2_HASH} ${OLD_branch2_HASH}
update refs/heads/branch3 ${NEW_branch3_HASH} ${OLD_branch3_HASH}
------------
This will simultaneously rebase `branch1`, `branch2`, and `branch3`,

View File

@ -8,7 +8,8 @@ git-repo - Retrieve information about the repository
SYNOPSIS
--------
[synopsis]
git repo info [--format=(keyvalue|nul)] [-z] [<key>...]
git repo info [--format=(keyvalue|nul) | -z] [--all | <key>...]
git repo structure [--format=(table|keyvalue|nul) | -z]
DESCRIPTION
-----------
@ -18,13 +19,13 @@ THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.
COMMANDS
--------
`info [--format=(keyvalue|nul)] [-z] [<key>...]`::
`info [--format=(keyvalue|nul) | -z] [--all | <key>...]`::
Retrieve metadata-related information about the current repository. Only
the requested data will be returned based on their keys (see "INFO KEYS"
section below).
+
The values are returned in the same order in which their respective keys were
requested.
requested. The `--all` flag requests the values for all the available keys.
+
The output format can be chosen through the flag `--format`. Two formats are
supported:
@ -43,6 +44,38 @@ supported:
+
`-z` is an alias for `--format=nul`.
`structure [--format=(table|keyvalue|nul) | -z]`::
Retrieve statistics about the current repository structure. The
following kinds of information are reported:
+
* Reference counts categorized by type
* Reachable object counts categorized by type
* Total inflated size of reachable objects by type
* Total disk size of reachable objects by type
+
The output format can be chosen through the flag `--format`. Three formats are
supported:
+
`table`:::
Outputs repository stats in a human-friendly table. This format may
change and is not intended for machine parsing. This is the default
format.
`keyvalue`:::
Each line of output contains a key-value pair for a repository stat.
The '=' character is used to delimit between the key and the value.
Values containing "unusual" characters are quoted as explained for the
configuration variable `core.quotePath` (see linkgit:git-config[1]).
`nul`:::
Similar to `keyvalue`, but uses a NUL character to delimit between
key-value pairs instead of a newline. Also uses a newline character as
the delimiter between the key and value instead of '='. Unlike the
`keyvalue` format, values containing "unusual" characters are never
quoted.
+
`-z` is an alias for `--format=nul`.
INFO KEYS
---------
In order to obtain a set of values from `git repo info`, you should provide

View File

@ -208,7 +208,7 @@ Sending
for your own case. Default is the value of `sendemail.smtpEncryption`.
--smtp-domain=<FQDN>::
Specifies the Fully Qualified Domain Name (FQDN) used in the
Specify the Fully Qualified Domain Name (FQDN) used in the
HELO/EHLO command to the SMTP server. Some servers require the
FQDN to match your IP address. If not set, `git send-email` attempts
to determine your FQDN automatically. Default is the value of
@ -245,7 +245,7 @@ a password is obtained using linkgit:git-credential[1].
Disable SMTP authentication. Short hand for `--smtp-auth=none`.
--smtp-server=<host>::
If set, specifies the outgoing SMTP server to use (e.g.
Specify the outgoing SMTP server to use (e.g.
`smtp.example.com` or a raw IP address). If unspecified, and if
`--sendmail-cmd` is also unspecified, the default is to search
for `sendmail` in `/usr/sbin`, `/usr/lib` and `$PATH` if such a
@ -258,7 +258,7 @@ command names. For those use cases, consider using `--sendmail-cmd`
instead.
--smtp-server-port=<port>::
Specifies a port different from the default port (SMTP
Specify a port different from the default port (SMTP
servers typically listen to smtp port 25, but may also listen to
submission port 587, or the common SSL smtp port 465);
symbolic port names (e.g. `submission` instead of 587)
@ -266,7 +266,7 @@ instead.
`sendemail.smtpServerPort` configuration variable.
--smtp-server-option=<option>::
If set, specifies the outgoing SMTP server option to use.
Specify the outgoing SMTP server option to use.
Default value can be specified by the `sendemail.smtpServerOption`
configuration option.
+
@ -277,7 +277,7 @@ must be used for each option.
--smtp-ssl::
Legacy alias for `--smtp-encryption ssl`.
--smtp-ssl-cert-path::
--smtp-ssl-cert-path <path>::
Path to a store of trusted CA certificates for SMTP SSL/TLS
certificate validation (either a directory that has been processed
by `c_rehash`, or a single file containing one or more PEM format
@ -321,7 +321,6 @@ for instructions.
If disabled with `--no-use-imap-only`, the emails will be sent like usual.
Disabled by default, but the `sendemail.useImapOnly` configuration
variable can be used to enable it.
+
This feature requires setting up `git imap-send`. See linkgit:git-imap-send[1]
for instructions.
@ -347,11 +346,11 @@ Automating
--no-to::
--no-cc::
--no-bcc::
Clears any list of `To:`, `Cc:`, `Bcc:` addresses previously
Clear any list of `To:`, `Cc:`, `Bcc:` addresses previously
set via config.
--no-identity::
Clears the previously read value of `sendemail.identity` set
Clear the previously read value of `sendemail.identity` set
via config, if any.
--to-cmd=<command>::
@ -510,12 +509,12 @@ have been specified, in which case default to `compose`.
Currently, validation means the following:
+
--
* Invoke the sendemail-validate hook if present (see linkgit:githooks[5]).
* Warn of patches that contain lines longer than
998 characters unless a suitable transfer encoding
(`auto`, `base64`, or `quoted-printable`) is used;
this is due to SMTP limits as described by
https://www.ietf.org/rfc/rfc5322.txt.
* Invoke the sendemail-validate hook if present (see linkgit:githooks[5]).
* Warn of patches that contain lines longer than
998 characters unless a suitable transfer encoding
(`auto`, `base64`, or `quoted-printable`) is used;
this is due to SMTP limits as described by
https://www.ietf.org/rfc/rfc5322.txt.
--
+
Default is the value of `sendemail.validate`; if this is not set,

View File

@ -104,7 +104,7 @@ associated with a new unborn branch named _<branch>_ (after
passed to the command. In the event the repository has a remote and
`--guess-remote` is used, but no remote or local branches exist, then the
command fails with a warning reminding the user to fetch from their remote
first (or override by using `-f/--force`).
first (or override by using `-f`/`--force`).
`list`::

View File

@ -223,7 +223,7 @@ Options that take a filename allow a prefix `:(optional)`. For example:
----------------------------
git commit -F :(optional)COMMIT_EDITMSG
# if COMMIT_EDITMSG does not exist, equivalent to
# if COMMIT_EDITMSG does not exist, the above is equivalent to
git commit
----------------------------

View File

@ -0,0 +1,305 @@
gitdatamodel(7)
===============
NAME
----
gitdatamodel - Git's core data model
SYNOPSIS
--------
gitdatamodel
DESCRIPTION
-----------
It's not necessary to understand Git's data model to use Git, but it's
very helpful when reading Git's documentation so that you know what it
means when the documentation says "object", "reference" or "index".
Git's core operations use 4 kinds of data:
1. <<objects,Objects>>: commits, trees, blobs, and tag objects
2. <<references,References>>: branches, tags,
remote-tracking branches, etc
3. <<index,The index>>, also known as the staging area
4. <<reflogs,Reflogs>>: logs of changes to references ("ref log")
[[objects]]
OBJECTS
-------
All of the commits and files in a Git repository are stored as "Git objects".
Git objects never change after they're created, and every object has an ID,
like `1b61de420a21a2f1aaef93e38ecd0e45e8bc9f0a`.
This means that if you have an object's ID, you can always recover its
exact contents as long as the object hasn't been deleted.
Every object has:
[[object-id]]
1. an *ID* (aka "object name"), which is a cryptographic hash of its
type and contents.
It's fast to look up a Git object using its ID.
This is usually represented in hexadecimal, like
`1b61de420a21a2f1aaef93e38ecd0e45e8bc9f0a`.
2. a *type*. There are 4 types of objects:
<<commit,commits>>, <<tree,trees>>, <<blob,blobs>>,
and <<tag-object,tag objects>>.
3. *contents*. The structure of the contents depends on the type.
Here's how each type of object is structured:
[[commit]]
commit::
A commit contains these required fields
(though there are other optional fields):
+
1. The full directory structure of all the files in that version of the
repository and each file's contents, stored as the *<<tree,tree>>* ID
of the commit's top-level directory
2. Its *parent commit ID(s)*. The first commit in a repository has 0 parents,
regular commits have 1 parent, merge commits have 2 or more parents
3. An *author* and the time the commit was authored
4. A *committer* and the time the commit was committed
5. A *commit message*
+
Here's how an example commit is stored:
+
----
tree 1b61de420a21a2f1aaef93e38ecd0e45e8bc9f0a
parent 4ccb6d7b8869a86aae2e84c56523f8705b50c647
author Maya <maya@example.com> 1759173425 -0400
committer Maya <maya@example.com> 1759173425 -0400
Add README
----
+
Like all other objects, commits can never be changed after they're created.
For example, "amending" a commit with `git commit --amend` creates a new
commit with the same parent.
+
Git does not store the diff for a commit: when you ask Git to show
the commit with linkgit:git-show[1], it calculates the diff from its
parent on the fly.
[[tree]]
tree::
A tree is how Git represents a directory.
It can contain files or other trees (which are subdirectories).
It lists, for each item in the tree:
+
1. The *filename*, for example `hello.py`
2. The *file type*, which must be one of these five types:
- *regular file*
- *executable file*
- *symbolic link*
- *directory*
- *gitlink* (for use with submodules)
3. The <<object-id,*object ID*>> with the contents of the file, directory,
or gitlink.
+
For example, this is how a tree containing one directory (`src`) and one file
(`README.md`) is stored:
+
----
100644 blob 8728a858d9d21a8c78488c8b4e70e531b659141f README.md
040000 tree 89b1d2e0495f66d6929f4ff76ff1bb07fc41947d src
----
NOTE: In the output above, Git displays the file type of each tree entry
using a format that's loosely modelled on Unix file modes (`100644` is
"regular file", `100755` is "executable file", `120000` is "symbolic
link", `040000` is "directory", and `160000` is "gitlink"). It also
displays the object's type: `blob` for files and symlinks, `tree` for
directories, and `commit` for gitlinks.
[[blob]]
blob::
A blob object contains a file's contents.
+
When you make a commit, Git stores the full contents of each file that
you changed as a blob.
For example, if you have a commit that changes 2 files in a repository
with 1000 files, that commit will create 2 new blobs, and use the
previous blob ID for the other 998 files.
This means that commits can use relatively little disk space even in a
very large repository.
[[tag-object]]
tag object::
Tag objects contain these required fields
(though there are other optional fields):
+
1. The *ID* of the object it references
2. The *type* of the object it references
3. The *tagger* and tag date
4. A *tag message*, similar to a commit message
Here's how an example tag object is stored:
----
object 750b4ead9c87ceb3ddb7a390e6c7074521797fb3
type commit
tag v1.0.0
tagger Maya <maya@example.com> 1759927359 -0400
Release version 1.0.0
----
NOTE: All of the examples in this section were generated with
`git cat-file -p <object-id>`.
[[references]]
REFERENCES
----------
References are a way to give a name to a commit.
It's easier to remember "the changes I'm working on are on the `turtle`
branch" than "the changes are in commit bb69721404348e".
Git often uses "ref" as shorthand for "reference".
References can either refer to:
1. An object ID, usually a <<commit,commit>> ID
2. Another reference. This is called a "symbolic reference"
References are stored in a hierarchy, and Git handles references
differently based on where they are in the hierarchy.
Most references are under `refs/`. Here are the main types:
[[branch]]
branches: `refs/heads/<name>`::
A branch refers to a commit ID.
That commit is the latest commit on the branch.
+
To get the history of commits on a branch, Git will start at the commit
ID the branch references, and then look at the commit's parent(s),
the parent's parent, etc.
[[tag]]
tags: `refs/tags/<name>`::
A tag refers to a commit ID, tag object ID, or other object ID.
There are two types of tags:
1. "Annotated tags", which reference a <<tag-object,tag object>> ID
which contains a tag message
2. "Lightweight tags", which reference a commit, blob, or tree ID
directly
+
Even though branches and tags both refer to a commit ID, Git
treats them very differently.
Branches are expected to change over time: when you make a commit, Git
will update your <<HEAD,current branch>> to point to the new commit.
Tags are usually not changed after they're created.
[[HEAD]]
HEAD: `HEAD`::
`HEAD` is where Git stores your current <<branch,branch>>,
if there is a current branch. `HEAD` can either be:
+
1. A symbolic reference to your current branch, for example `ref:
refs/heads/main` if your current branch is `main`.
2. A direct reference to a commit ID. In this case there is no current branch.
This is called "detached HEAD state", see the DETACHED HEAD section
of linkgit:git-checkout[1] for more.
[[remote-tracking-branch]]
remote-tracking branches: `refs/remotes/<remote>/<branch>`::
A remote-tracking branch refers to a commit ID.
It's how Git stores the last-known state of a branch in a remote
repository. `git fetch` updates remote-tracking branches. When
`git status` says "you're up to date with origin/main", it's looking at
this.
+
`refs/remotes/<remote>/HEAD` is a symbolic reference to the remote's
default branch. This is the branch that `git clone` checks out by default.
[[other-refs]]
Other references::
Git tools may create references anywhere under `refs/`.
For example, linkgit:git-stash[1], linkgit:git-bisect[1],
and linkgit:git-notes[1] all create their own references
in `refs/stash`, `refs/bisect`, etc.
Third-party Git tools may also create their own references.
+
Git may also create references other than `HEAD` at the base of the
hierarchy, like `ORIG_HEAD`.
NOTE: Git may delete objects that aren't "reachable" from any reference
or <<reflogs,reflog>>.
An object is "reachable" if we can find it by following tags to whatever
they tag, commits to their parents or trees, and trees to the trees or
blobs that they contain.
For example, if you amend a commit with `git commit --amend`,
there will no longer be a branch that points at the old commit.
The old commit is recorded in the current branch's <<reflogs,reflog>>,
so it is still "reachable", but when the reflog entry expires it may
become unreachable and get deleted.
Reachable objects will never be deleted.
[[index]]
THE INDEX
---------
The index, also known as the "staging area", is a list of files and
the contents of each file, stored as a <<blob,blob>>.
You can add files to the index or update the contents of a file in the
index with linkgit:git-add[1]. This is called "staging" the file for commit.
Unlike a <<tree,tree>>, the index is a flat list of files.
When you commit, Git converts the list of files in the index to a
directory <<tree,tree>> and uses that tree in the new <<commit,commit>>.
Each index entry has 4 fields:
1. The *file type*, which must be one of:
- *regular file*
- *executable file*
- *symbolic link*
- *gitlink* (for use with submodules)
2. The *<<blob,blob>>* ID of the file,
or (rarely) the *<<commit,commit>>* ID of the submodule
3. The *stage number*, either 0, 1, 2, or 3. This is normally 0, but if
there's a merge conflict there can be multiple versions of the same
filename in the index.
4. The *file path*, for example `src/hello.py`
It's extremely uncommon to look at the index directly: normally you'd
run `git status` to see a list of changes between the index and <<HEAD,HEAD>>.
But you can use `git ls-files --stage` to see the index.
Here's the output of `git ls-files --stage` in a repository with 2 files:
----
100644 8728a858d9d21a8c78488c8b4e70e531b659141f 0 README.md
100644 665c637a360874ce43bf74018768a96d2d4d219a 0 src/hello.py
----
[[reflogs]]
REFLOGS
-------
Every time a branch, remote-tracking branch, or HEAD is updated, Git
updates a log called a "reflog" for that <<references,reference>>.
This means that if you make a mistake and "lose" a commit, you can
generally recover the commit ID by running `git reflog <reference>`.
A reflog is a list of log entries. Each entry has:
1. The *commit ID*
2. *Timestamp* when the change was made
3. *Log message*, for example `pull: Fast-forward`
Reflogs only log changes made in your local repository.
They are not shared with remotes.
You can view a reflog with `git reflog <reference>`.
For example, here's the reflog for a `main` branch which has changed twice:
----
$ git reflog main --date=iso --no-decorate
750b4ea main@{2025-09-29 15:17:05 -0400}: commit: Add README
4ccb6d7 main@{2025-09-29 15:16:48 -0400}: commit (initial): Initial commit
----
GIT
---
Part of the linkgit:git[1] suite

View File

@ -83,6 +83,25 @@ Windows would be the configuration `"C:\Program Files\Vim\gvim.exe" --nofork`,
which quotes the filename with spaces and specifies the `--nofork` option to
avoid backgrounding the process.
[[sign-off]]
Why not have `commit.signoff` and other configuration variables?::
Git intentionally does not (and will not) provide a
configuration variable, such as `commit.signoff`, to
automatically add `--signoff` by default. The reason is to
protect the legal and intentional significance of a sign-off.
If there were more automated and widely publicized ways for
sign-offs to be appended, it would become easier for someone
to argue later that a "Signed-off-by" trailer was just added
out of habit or by automation, without the committer's full
awareness or intent to certify their agreement with the
Developer Certificate of Origin (DCO) or a similar statement.
This could undermine the sign-offs credibility in legal or
contractual situations.
+
There exists `format.signoff`, but that is a historical mistake, and
it is not an excuse to add more mistakes of the same kind on top.
Credentials
-----------

View File

@ -103,17 +103,14 @@ invoked before obtaining the proposed commit log message and
making a commit. Exiting with a non-zero status from this script
causes the `git commit` command to abort before creating a commit.
The default 'pre-commit' hook, when enabled, catches introduction
of lines with trailing whitespaces and aborts the commit when
such a line is found.
All the `git commit` hooks are invoked with the environment
variable `GIT_EDITOR=:` if the command will not bring up an editor
to modify the commit message.
The default 'pre-commit' hook, when enabled--and with the
`hooks.allownonascii` config option unset or set to false--prevents
the use of non-ASCII filenames.
The default 'pre-commit' hook, when enabled, prevents the introduction
of non-ASCII filenames and lines with trailing whitespace. The non-ASCII
check can be turned off by setting the `hooks.allownonascii` config
option to `true`.
pre-merge-commit
~~~~~~~~~~~~~~~~

View File

@ -111,6 +111,11 @@ PATTERN FORMAT
one of the characters in a range. See fnmatch(3) and the
FNM_PATHNAME flag for a more detailed description.
- A backslash ("`\`") can be used to escape any character. E.g., "`\*`"
matches a literal asterisk (and "`\a`" matches "`a`", even though
there is no need for escaping there). As with fnmatch(3), a backslash
at the end of a pattern is an invalid pattern that never matches.
Two consecutive asterisks ("`**`") in patterns matched against
full pathname may have special meaning:

View File

@ -443,7 +443,8 @@ If no "want" objects are received, send an error:
TODO: Define error if no "want" lines are requested.
If any "want" object is not reachable, send an error:
TODO: Define error if an invalid "want" is requested.
When a Git server receives an invalid or malformed `want` line, it
responds with an error message that includes the offending object name.
Create an empty list, `s_common`.

View File

@ -297,8 +297,8 @@ This commit is referred to as a "merge commit", or sometimes just a
identified by its <<def_object_name,object name>>. The objects usually
live in `$GIT_DIR/objects/`.
[[def_object_identifier]]object identifier (oid)::
Synonym for <<def_object_name,object name>>.
[[def_object_identifier]]object identifier, object ID, oid::
Synonyms for <<def_object_name,object name>>.
[[def_object_name]]object name::
The unique identifier of an <<def_object,object>>. The

View File

@ -35,7 +35,7 @@ doc_targets += custom_target(
output: 'howto-index.html',
depends: documentation_deps,
install: true,
install_dir: get_option('datadir') / 'doc/git-doc',
install_dir: htmldir,
)
foreach howto : howto_sources
@ -57,6 +57,6 @@ foreach howto : howto_sources
output: fs.stem(howto_stripped.full_path()) + '.html',
depends: documentation_deps,
install: true,
install_dir: get_option('datadir') / 'doc/git-doc/howto',
install_dir: htmldir / 'howto',
)
endforeach

View File

@ -56,7 +56,7 @@ ifdef::git-pull[]
`--ff-only`::
Only update to the new history if there is no divergent local
history. This is the default when no method for reconciling
divergent histories is provided (via the --rebase=* flags).
divergent histories is provided (via the `--rebase` flags).
`--ff`::
`--no-ff`::

View File

@ -193,6 +193,7 @@ manpages = {
'gitcore-tutorial.adoc' : 7,
'gitcredentials.adoc' : 7,
'gitcvs-migration.adoc' : 7,
'gitdatamodel.adoc' : 7,
'gitdiffcore.adoc' : 7,
'giteveryday.adoc' : 7,
'gitfaq.adoc' : 7,
@ -412,7 +413,7 @@ foreach manpage, category : manpages
input: manpage,
output: fs.stem(manpage) + '.html',
install: true,
install_dir: get_option('datadir') / 'doc/git-doc',
install_dir: htmldir,
)
endif
endforeach
@ -423,7 +424,7 @@ if get_option('docs').contains('html')
output: 'docinfo.html',
copy: true,
install: true,
install_dir: get_option('datadir') / 'doc/git-doc',
install_dir: htmldir,
)
configure_file(
@ -431,11 +432,11 @@ if get_option('docs').contains('html')
output: 'docbook-xsl.css',
copy: true,
install: true,
install_dir: get_option('datadir') / 'doc/git-doc',
install_dir: htmldir,
)
install_symlink('index.html',
install_dir: get_option('datadir') / 'doc/git-doc',
install_dir: htmldir,
pointing_to: 'git.html',
)
@ -466,7 +467,7 @@ if get_option('docs').contains('html')
input: 'docbook.xsl',
output: 'user-manual.html',
install: true,
install_dir: get_option('datadir') / 'doc/git-doc',
install_dir: htmldir,
)
articles = [
@ -492,7 +493,7 @@ if get_option('docs').contains('html')
output: fs.stem(article) + '.html',
depends: documentation_deps,
install: true,
install_dir: get_option('datadir') / 'doc/git-doc',
install_dir: htmldir,
)
endforeach

View File

@ -1,20 +1,20 @@
<repository>::
_<repository>_::
The "remote" repository that is the source of a fetch
or pull operation. This parameter can be either a URL
(see the section <<URLS,GIT URLS>> below) or the name
of a remote (see the section <<REMOTES,REMOTES>> below).
ifndef::git-pull[]
<group>::
_<group>_::
A name referring to a list of repositories as the value
of remotes.<group> in the configuration file.
of `remotes.<group>` in the configuration file.
(See linkgit:git-config[1]).
endif::git-pull[]
[[fetch-refspec]]
<refspec>::
_<refspec>_::
Specifies which refs to fetch and which local refs to update.
When no <refspec>s appear on the command line, the refs to fetch
When no __<refspec>__s appear on the command line, the refs to fetch
are read from `remote.<repository>.fetch` variables instead
ifndef::git-pull[]
(see <<CRTB,CONFIGURED REMOTE-TRACKING BRANCHES>> below).
@ -24,18 +24,18 @@ ifdef::git-pull[]
in linkgit:git-fetch[1]).
endif::git-pull[]
+
The format of a <refspec> parameter is an optional plus
`+`, followed by the source <src>, followed
by a colon `:`, followed by the destination <dst>.
The colon can be omitted when <dst> is empty. <src> is
The format of a _<refspec>_ parameter is an optional plus
`+`, followed by the source _<src>_, followed
by a colon `:`, followed by the destination _<dst>_.
The colon can be omitted when _<dst>_ is empty. _<src>_ is
typically a ref, or a glob pattern with a single `*` that is used
to match a set of refs, but it can also be a fully spelled hex object
name.
+
A <refspec> may contain a `*` in its <src> to indicate a simple pattern
A _<refspec>_ may contain a `*` in its _<src>_ to indicate a simple pattern
match. Such a refspec functions like a glob that matches any ref with the
pattern. A pattern <refspec> must have one and only one `*` in both the <src> and
<dst>. It will map refs to the destination by replacing the `*` with the
pattern. A pattern _<refspec>_ must have one and only one `*` in both the _<src>_ and
_<dst>_. It will map refs to the destination by replacing the `*` with the
contents matched from the source.
+
If a refspec is prefixed by `^`, it will be interpreted as a negative
@ -45,14 +45,14 @@ considered to match if it matches at least one positive refspec, and does
not match any negative refspec. Negative refspecs can be useful to restrict
the scope of a pattern refspec so that it will not include specific refs.
Negative refspecs can themselves be pattern refspecs. However, they may only
contain a <src> and do not specify a <dst>. Fully spelled out hex object
contain a _<src>_ and do not specify a _<dst>_. Fully spelled out hex object
names are also not supported.
+
`tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`;
it requests fetching everything up to the given tag.
+
The remote ref that matches <src>
is fetched, and if <dst> is not an empty string, an attempt
The remote ref that matches _<src>_
is fetched, and if _<dst>_ is not an empty string, an attempt
is made to update the local ref that matches it.
+
Whether that update is allowed without `--force` depends on the ref
@ -60,7 +60,7 @@ namespace it's being fetched to, the type of object being fetched, and
whether the update is considered to be a fast-forward. Generally, the
same rules apply for fetching as when pushing, see the `<refspec>...`
section of linkgit:git-push[1] for what those are. Exceptions to those
rules particular to 'git fetch' are noted below.
rules particular to `git fetch` are noted below.
+
Until Git version 2.20, and unlike when pushing with
linkgit:git-push[1], any updates to `refs/tags/*` would be accepted
@ -101,19 +101,19 @@ must know this is the expected usage pattern for a branch.
ifdef::git-pull[]
+
[NOTE]
There is a difference between listing multiple <refspec>
directly on 'git pull' command line and having multiple
There is a difference between listing multiple _<refspec>_
directly on `git pull` command line and having multiple
`remote.<repository>.fetch` entries in your configuration
for a <repository> and running a
'git pull' command without any explicit <refspec> parameters.
<refspec>s listed explicitly on the command line are always
for a _<repository>_ and running a
`git pull` command without any explicit _<refspec>_ parameters.
__<refspec>__s listed explicitly on the command line are always
merged into the current branch after fetching. In other words,
if you list more than one remote ref, 'git pull' will create
if you list more than one remote ref, `git pull` will create
an Octopus merge. On the other hand, if you do not list any
explicit <refspec> parameter on the command line, 'git pull'
will fetch all the <refspec>s it finds in the
explicit _<refspec>_ parameter on the command line, `git pull`
will fetch all the __<refspec>__s it finds in the
`remote.<repository>.fetch` configuration and merge
only the first <refspec> found into the current branch.
only the first _<refspec>_ found into the current branch.
This is because making an
Octopus from remote refs is rarely done, while keeping track
of multiple remote heads in one-go by fetching more than one

View File

@ -983,7 +983,9 @@ to name units in KiB, MiB, or GiB. For example, `blob:limit=1k`
is the same as 'blob:limit=1024'.
+
The form `--filter=object:type=(tag|commit|tree|blob)` omits all objects
which are not of the requested type.
which are not of the requested type. Note that explicitly provided objects
ignore filters and are always printed unless `--filter-provided-objects` is
also specified.
+
The form `--filter=sparse:oid=<blob-ish>` uses a sparse-checkout
specification contained in the blob (or blob-expression) _<blob-ish>_

View File

@ -197,6 +197,170 @@ delete <enlistment>::
This subcommand lets you delete an existing Scalar enlistment from your
local file system, unregistering the repository.
RECOMMENDED CONFIG VALUES
-------------------------
As part of both `scalar clone` and `scalar register`, certain Git config
values are set to optimize for large repositories or cross-platform support.
These options are updated in new Git versions according to the best known
advice for large repositories, and users can get the latest recommendations
by running `scalar reconfigure [--all]`.
This section lists justifications for the config values that are set in the
latest version.
am.keepCR=true::
This setting is important for cross-platform development across Windows
and non-Windows platforms and keeping carriage return (`\r`) characters
in certain workflows.
commitGraph.changedPaths=true::
This setting helps the background maintenance steps that compute the
serialized commit-graph to also store changed-path Bloom filters. This
accelerates file history commands and allows users to automatically
benefit without running a foreground command.
commitGraph.generationVersion=1::
While the preferred version is 2 for performance reasons, existing users
that had version 1 by default will need special care in upgrading to
version 2. This is likely to change in the future as the upgrade story
solidifies.
core.autoCRLF=false::
This removes the transformation of worktree files to add CRLF line
endings when only LF line endings exist. This is removed for performance
reasons. Repositories that use tools that care about CRLF line endings
should commit the necessary files with those line endings instead.
core.logAllRefUpdates=true::
This enables the reflog on all branches. While this is a performance
cost for large repositories, it is frequently an important data source
for users to get out of bad situations or to seek support from experts.
core.safeCRLF=false::
Similar to `core.autoCRLF=false`, this disables checks around whether
the CRLF conversion is reversible. This is a performance improvement,
but can be dangerous if `core.autoCRLF` is reenabled by the user.
credential.https://dev.azure.com.useHttpPath=true::
This setting enables the `credential.useHttpPath` feature only for web
URLs for Azure DevOps. This is important for users interacting with that
service using multiple organizations and thus multiple credential
tokens.
feature.experimental=false::
This disables the "experimental" optimizations grouped under this
feature config. The expectation is that all valuable optimizations are
also set explicitly by Scalar config, and any differences are
intentional. Notable differences include several bitmap-related config
options which are disabled for client-focused Scalar repos.
feature.manyFiles=false::
This disables the "many files" optimizations grouped under this feature
config. The expectation is that all valuable optimizations are also set
explicitly by Scalar config, and any differences are intentional.
fetch.showForcedUpdates=false::
This disables the check at the end of `git fetch` that notifies the user
if the ref update was a forced update (one where the previous position
is not reachable from the latest position). This check can be very
expensive in large repositories, so is disabled and replaced with an
advice message. Set `advice.fetchShowForcedUpdates=false` to disable
this advice message.
fetch.unpackLimit=1::
This setting prevents Git from unpacking packfiles into loose objects
as they are downloaded from the server. The default limit of 100 was
intended as a way to prevent performance issues from too many packfiles,
but Scalar uses background maintenance to group packfiles and cover them
with a multi-pack-index, removing this issue.
fetch.writeCommitGraph=false::
This config setting was created to help users automatically update their
commit-graph files as they perform fetches. However, this takes time
from foreground fetches and pulls and Scalar uses background maintenance
for this function instead.
gc.auto=0::
This disables automatic garbage collection, since Scalar uses background
maintenance to keep the repository data in good shape.
gui.GCWarning=false::
Since Scalar disables garbage collection by setting `gc.auto=0`, the
`git-gui` tool may start to warn about this setting. Disable this
warning as Scalar's background maintenance configuration makes the
warning irrelevant.
index.skipHash=true::
Disable computing the hash of the index contents as it is being written.
This assists with performance, especially for large index files.
index.threads=true::
This tells Git to automatically detect how many threads it should use
when reading the index due to the default value of `core.preloadIndex`,
which enables parallel index reads. This explicit setting also enables
`index.recordOffsetTable=true` to speed up parallel index reads.
index.version=4::
This index version adds compression to the path names, reducing the size
of the index in a significant way for large repos. This is an important
performance boost.
log.excludeDecoration=refs/prefetch/*::
Since Scalar enables background maintenance with the `incremental`
strategy, this setting avoids polluting `git log` output with refs
stored by the background prefetch operations.
merge.renames=true::
When computing merges in large repos, it is particularly important to
detect renames to maximize the potential for a result that will validate
correctly. Users performing merges locally are more likely to be doing
so because a server-side merge (via pull request or similar) resulted in
conflicts. While this is the default setting, it is set specifically to
override a potential change to `diff.renames` which a user may set for
performance reasons.
merge.stat=false::
This disables a diff output after computing a merge. This improves
performance of `git merge` for large repos while reducing noisy output.
pack.useBitmaps=false::
This disables the use of `.bitmap` files attached to packfiles. Bitmap
files are optimized for server-side use, not client-side use. Scalar
disables this to avoid some performance issues that can occur if a user
accidentally creates `.bitmap` files.
pack.usePathWalk=true::
This enables the `--path-walk` option to `git pack-objects` by default.
This can accelerate the computation and compression of packfiles created
by `git push` and other repack operations.
receive.autoGC=false::
Similar to `gc.auto`, this setting is disabled in preference of
background maintenance.
status.aheadBehind=false::
This disables the ahead/behind calculation that would normally happen
during a `git status` command. This information is frequently ignored by
users but can be expensive to calculate in large repos that receive
thousands of commits per day. The calculation is replaced with an advice
message that can be disabled by disabling the `advice.statusAheadBehind`
config.
The following settings are different based on which platform is in use:
core.untrackedCache=(true|false)::
The untracked cache feature is important for performance benefits on
large repositories, but has demonstrated some bugs on Windows
filesystems. Thus, this is set for other platforms but disabled on
Windows.
http.sslBackend=schannel::
On Windows, the `openssl` backend has some issues with certain types of
remote providers and certificate types. Override the default setting to
avoid these common problems.
SEE ALSO
--------
linkgit:git-clone[1], linkgit:git-maintenance[1].

View File

@ -16,3 +16,7 @@ endif::git-commit[]
+
The `--no-signoff` option can be used to countermand an earlier `--signoff`
option on the command line.
+
Git does not (and will not) have a configuration variable to enable
the `--signoff` command line option by default; see the
`commit.signoff` entry in linkgit:gitfaq[7] for more details.

View File

@ -32,6 +32,7 @@ articles = [
'sparse-checkout.adoc',
'sparse-index.adoc',
'trivial-merge.adoc',
'unambiguous-types.adoc',
'unit-tests.adoc',
]
@ -53,7 +54,7 @@ doc_targets += custom_target(
output: 'api-index.html',
depends: documentation_deps,
install: true,
install_dir: get_option('datadir') / 'doc/git-doc/technical',
install_dir: htmldir / 'technical',
)
foreach article : api_docs + articles
@ -63,6 +64,6 @@ foreach article : api_docs + articles
output: fs.stem(article) + '.html',
depends: documentation_deps,
install: true,
install_dir: get_option('datadir') / 'doc/git-doc/technical',
install_dir: htmldir / 'technical',
)
endforeach

View File

@ -0,0 +1,224 @@
= Unambiguous types
Most of these mappings are obvious, but there are some nuances and gotchas with
Rust FFI (Foreign Function Interface).
This document defines clear, one-to-one mappings between primitive types in C,
Rust (and possible other languages in the future). Its purpose is to eliminate
ambiguity in type widths, signedness, and binary representation across
platforms and languages.
For Git, the only header required to use these unambiguous types in C is
`git-compat-util.h`.
== Boolean types
[cols="1,1", options="header"]
|===
| C Type | Rust Type
| bool^1^ | bool
|===
== Integer types
In C, `<stdint.h>` (or an equivalent) must be included.
[cols="1,1", options="header"]
|===
| C Type | Rust Type
| uint8_t | u8
| uint16_t | u16
| uint32_t | u32
| uint64_t | u64
| int8_t | i8
| int16_t | i16
| int32_t | i32
| int64_t | i64
|===
== Floating-point types
Rust requires IEEE-754 semantics.
In C, that is typically true, but not guaranteed by the standard.
[cols="1,1", options="header"]
|===
| C Type | Rust Type
| float^2^ | f32
| double^2^ | f64
|===
== Size types
These types represent pointer-sized integers and are typically defined in
`<stddef.h>` or an equivalent header.
Size types should be used any time pointer arithmetic is performed e.g.
indexing an array, describing the number of elements in memory, etc...
[cols="1,1", options="header"]
|===
| C Type | Rust Type
| size_t^3^ | usize
| ptrdiff_t^3^ | isize
|===
== Character types
This is where C and Rust don't have a clean one-to-one mapping.
A C `char` and a Rust `u8` share the same bit width, so any C struct containing
a `char` will have the same size as the corresponding Rust struct using `u8`.
In that sense, such structs are safe to pass over the FFI boundary, because
their fields will be laid out identically. However, beyond bit width, C `char`
has additional semantics and platform-dependent behavior that can cause
problems, as discussed below.
The C language leaves the signedness of `char` implementation defined. Because
our developer build enables -Wsign-compare, comparison of a value of `char`
type with either signed or unsigned integers may trigger warnings from the
compiler.
Note: Rust's `char` type is an unsigned 32-bit integer that is used to describe
Unicode code points.
=== Notes
^1^ This is only true if stdbool.h (or equivalent) is used. +
^2^ C does not enforce IEEE-754 compatibility, but Rust expects it. If the
platform/arch for C does not follow IEEE-754 then this equivalence does not
hold. Also, it's assumed that `float` is 32 bits and `double` is 64, but
there may be a strange platform/arch where even this isn't true. +
^3^ C also defines uintptr_t, ssize_t and intptr_t, but these types are
discouraged for FFI purposes. For functions like `read()` and `write()` ssize_t
should be cast to a different, and unambiguous, type before being passed over
the FFI boundary. +
== Problems with std::ffi::c_* types in Rust
TL;DR: In practice, Rust's `c_*` types aren't guaranteed to match C types for
all possible C compilers, platforms, or architectures, because Rust only
ensures correctness of C types on officially supported targets. These
definitions have changed over time to match more targets which means that the
c_* definitions will differ based on which Rust version Git chooses to use.
Current list of safe, Rust side, FFI types in Git: +
* `c_void`
* `CStr`
* `CString`
Even then, they should be used sparingly, and only where the semantics match
exactly.
The std::os::raw::c_* directly inherits the problems of core::ffi, which
changes over time and seems to make a best guess at the correct definition for
a given platform/target. This probably isn't a problem for all other platforms
that Rust supports currently, but can anyone say that Rust got it right for all
C compilers of all platforms/targets?
To give an example: c_long is defined in
footnote:[https://doc.rust-lang.org/1.63.0/src/core/ffi/mod.rs.html#175-189[c_long in 1.63.0]]
footnote:[https://doc.rust-lang.org/1.89.0/src/core/ffi/primitives.rs.html#135-151[c_long in 1.89.0]]
=== Rust version 1.63.0
```
mod c_long_definition {
cfg_if! {
if #[cfg(all(target_pointer_width = "64", not(windows)))] {
pub type c_long = i64;
pub type NonZero_c_long = crate::num::NonZeroI64;
pub type c_ulong = u64;
pub type NonZero_c_ulong = crate::num::NonZeroU64;
} else {
// The minimal size of `long` in the C standard is 32 bits
pub type c_long = i32;
pub type NonZero_c_long = crate::num::NonZeroI32;
pub type c_ulong = u32;
pub type NonZero_c_ulong = crate::num::NonZeroU32;
}
}
}
```
=== Rust version 1.89.0
```
mod c_long_definition {
crate::cfg_select! {
any(
all(target_pointer_width = "64", not(windows)),
// wasm32 Linux ABI uses 64-bit long
all(target_arch = "wasm32", target_os = "linux")
) => {
pub(super) type c_long = i64;
pub(super) type c_ulong = u64;
}
_ => {
// The minimal size of `long` in the C standard is 32 bits
pub(super) type c_long = i32;
pub(super) type c_ulong = u32;
}
}
}
```
Even for the cases where C types are correctly mapped to Rust types via
std::ffi::c_* there are still problems. Let's take c_char for example. On some
platforms it's u8 on others it's i8.
=== Subtraction underflow in debug mode
The following code will panic in debug on platforms that define c_char as u8,
but won't if it's an i8.
```
let mut x: std::ffi::c_char = 0;
x -= 1;
```
=== Inconsistent shift behavior
`x` will be 0xC0 for platforms that use i8, but will be 0x40 where it's u8.
```
let mut x: std::ffi::c_char = 0x80;
x >>= 1;
```
=== Equality fails to compile on some platforms
The following will not compile on platforms that define c_char as i8, but will
if it's u8. You can cast x e.g. `assert_eq!(x as u8, b'a');`, but then you get
a warning on platforms that use u8 and a clean compilation where i8 is used.
```
let mut x: std::ffi::c_char = 0x61;
assert_eq!(x, b'a');
```
== Enum types
Rust enum types should not be used as FFI types. Rust enum types are more like
C union types than C enum's. For something like:
```
#[repr(C, u8)]
enum Fruit {
Apple,
Banana,
Cherry,
}
```
It's easy enough to make sure the Rust enum matches what C would expect, but a
more complex type like.
```
enum HashResult {
SHA1([u8; 20]),
SHA256([u8; 32]),
}
```
The Rust compiler has to add a discriminant to the enum to distinguish between
the variants. The width, location, and values for that discriminant is up to
the Rust compiler and is not ABI stable.

View File

@ -4,7 +4,7 @@ REMOTES[[REMOTES]]
------------------
The name of one of the following can be used instead
of a URL as `<repository>` argument:
of a URL as _<repository>_ argument:
* a remote in the Git configuration file: `$GIT_DIR/config`,
* a file in the `$GIT_DIR/remotes` directory, or
@ -32,8 +32,8 @@ config file would appear like this:
fetch = <refspec>
------------
The `<pushurl>` is used for pushes only. It is optional and defaults
to `<URL>`. Pushing to a remote affects all defined pushurls or all
The _<pushurl>_ is used for pushes only. It is optional and defaults
to _<URL>_. Pushing to a remote affects all defined pushurls or all
defined urls if no pushurls are defined. Fetch, however, will only
fetch from the first defined url if multiple urls are defined.
@ -54,8 +54,8 @@ following format:
------------
`Push:` lines are used by 'git push' and
`Pull:` lines are used by 'git pull' and 'git fetch'.
`Push:` lines are used by `git push` and
`Pull:` lines are used by `git pull` and `git fetch`.
Multiple `Push:` and `Pull:` lines may
be specified for additional branch mappings.
@ -72,12 +72,12 @@ This file should have the following format:
<URL>#<head>
------------
`<URL>` is required; `#<head>` is optional.
_<URL>_ is required; `#<head>` is optional.
Depending on the operation, git will use one of the following
refspecs, if you don't provide one on the command line.
`<branch>` is the name of this file in `$GIT_DIR/branches` and
`<head>` defaults to `master`.
_<branch>_ is the name of this file in `$GIT_DIR/branches` and
_<head>_ defaults to `master`.
git fetch uses:
@ -111,7 +111,7 @@ Git defaults to using the upstream branch for remote operations, for example:
'origin/main' have diverged, and have 2 and 3 different commits each
respectively".
The upstream is stored in `.git/config`, in the "remote" and "merge"
The upstream is stored in `.git/config`, in the "`remote`" and "`merge`"
fields. For example, if `main`'s upstream is `origin/main`:
------------

View File

@ -1,6 +1,6 @@
#!/bin/sh
DEF_VER=v2.52.0-rc0
DEF_VER=v2.52.GIT
LF='
'

View File

@ -95,11 +95,21 @@ include shared.mak
# and LDFLAGS appropriately.
#
# Define NO_DARWIN_PORTS if you are building on Darwin/Mac OS X,
# have DarwinPorts installed in /opt/local, but don't want GIT to
# have DarwinPorts (which is an old name for MacPorts) installed
# in /opt/local, but don't want GIT to
# link against any libraries installed there. If defined you may
# 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.
@ -981,7 +991,7 @@ SANITIZE_LEAK =
SANITIZE_ADDRESS =
# For the 'coccicheck' target
SPATCH_INCLUDE_FLAGS = --all-includes
SPATCH_INCLUDE_FLAGS = --all-includes $(addprefix -I ,compat ewah refs sha256 trace2 win32 xdiff)
SPATCH_FLAGS =
SPATCH_TEST_FLAGS =
@ -1201,6 +1211,7 @@ LIB_OBJS += object-file.o
LIB_OBJS += object-name.o
LIB_OBJS += object.o
LIB_OBJS += odb.o
LIB_OBJS += odb/streaming.o
LIB_OBJS += oid-array.o
LIB_OBJS += oidmap.o
LIB_OBJS += oidset.o
@ -1294,7 +1305,6 @@ LIB_OBJS += split-index.o
LIB_OBJS += stable-qsort.o
LIB_OBJS += statinfo.o
LIB_OBJS += strbuf.o
LIB_OBJS += streaming.o
LIB_OBJS += string-list.o
LIB_OBJS += strmap.o
LIB_OBJS += strvec.o
@ -1525,6 +1535,7 @@ CLAR_TEST_SUITES += u-string-list
CLAR_TEST_SUITES += u-strvec
CLAR_TEST_SUITES += u-trailer
CLAR_TEST_SUITES += u-urlmatch-normalization
CLAR_TEST_SUITES += u-utf8-width
CLAR_TEST_PROG = $(UNIT_TEST_BIN)/unit-tests$(X)
CLAR_TEST_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(CLAR_TEST_SUITES))
CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/clar/clar.o
@ -1587,6 +1598,7 @@ SANITIZE_LEAK = YesCompiledWithIt
endif
ifneq ($(filter address,$(SANITIZERS)),)
NO_REGEX = NeededForASAN
NO_MMAP = NeededForASAN
SANITIZE_ADDRESS = YesCompiledWithIt
endif
endif
@ -1690,6 +1702,23 @@ 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
endif
endif
endif
endif
ifdef NO_LIBGEN_H
COMPAT_CFLAGS += -DNO_LIBGEN_H
COMPAT_OBJS += compat/basename.o
@ -1917,7 +1946,6 @@ ifdef NO_SETENV
endif
ifdef NO_MKDTEMP
COMPAT_CFLAGS += -DNO_MKDTEMP
COMPAT_OBJS += compat/mkdtemp.o
endif
ifdef MKDIR_WO_TRAILING_SLASH
COMPAT_CFLAGS += -DMKDIR_WO_TRAILING_SLASH
@ -2565,7 +2593,7 @@ please_set_SHELL_PATH_to_a_more_modern_shell:
shell_compatibility_test: please_set_SHELL_PATH_to_a_more_modern_shell
strip: $(PROGRAMS) git$X
strip: $(PROGRAMS) git$X scalar$X
$(STRIP) $(STRIP_OPTS) $^
### Target-specific flags and dependencies
@ -3521,7 +3549,7 @@ else
COCCICHECK_PATCH_MUST_BE_EMPTY_FILES = $(COCCICHECK_PATCHES_INTREE)
endif
coccicheck: $(COCCICHECK_PATCH_MUST_BE_EMPTY_FILES)
! grep -q ^ $(COCCICHECK_PATCH_MUST_BE_EMPTY_FILES) /dev/null
! grep ^ $(COCCICHECK_PATCH_MUST_BE_EMPTY_FILES) /dev/null
# See contrib/coccinelle/README
coccicheck-pending: coccicheck-test

View File

@ -1 +1 @@
Documentation/RelNotes/2.52.0.adoc
Documentation/RelNotes/2.53.0.adoc

81
apply.c
View File

@ -1640,6 +1640,14 @@ static void record_ws_error(struct apply_state *state,
state->squelch_whitespace_errors < state->whitespace_error)
return;
/*
* line[len] for an incomplete line points at the "\n" at the end
* of patch input line, so "%.*s" would drop the last letter on line;
* compensate for it.
*/
if (result & WS_INCOMPLETE_LINE)
len++;
err = whitespace_error_string(result);
if (state->apply_verbosity > verbosity_silent)
fprintf(stderr, "%s:%d: %s.\n%.*s\n",
@ -1670,6 +1678,35 @@ static void check_old_for_crlf(struct patch *patch, const char *line, int len)
}
/*
* Just saw a single line in a fragment. If it is a part of this hunk
* that is a context " ", an added "+", or a removed "-" line, it may
* be followed by "\\ No newline..." to signal that the last "\n" on
* this line needs to be dropped. Depending on locale settings when
* the patch was produced we don't know what this line would exactly
* say. The only thing we do know is that it begins with "\ ".
* Checking for 12 is just for sanity check; "\ No newline..." would
* be at least that long in any l10n.
*
* Return 0 if the line we saw is not followed by "\ No newline...",
* or length of that line. The caller will use it to skip over the
* "\ No newline..." line.
*/
static int adjust_incomplete(const char *line, int len,
unsigned long size)
{
int nextlen;
if (*line != '\n' && *line != ' ' && *line != '+' && *line != '-')
return 0;
if (size - len < 12 || memcmp(line + len, "\\ ", 2))
return 0;
nextlen = linelen(line + len, size - len);
if (nextlen < 12)
return 0;
return nextlen;
}
/*
* Parse a unified diff. Note that this really needs to parse each
* fragment separately, since the only way to know the difference
@ -1684,6 +1721,7 @@ static int parse_fragment(struct apply_state *state,
{
int added, deleted;
int len = linelen(line, size), offset;
int skip_len = 0;
unsigned long oldlines, newlines;
unsigned long leading, trailing;
@ -1710,6 +1748,22 @@ static int parse_fragment(struct apply_state *state,
len = linelen(line, size);
if (!len || line[len-1] != '\n')
return -1;
/*
* For an incomplete line, skip_len counts the bytes
* on "\\ No newline..." marker line that comes next
* to the current line.
*
* Reduce "len" to drop the newline at the end of
* line[], but add one to "skip_len", which will be
* added back to "len" for the next iteration, to
* compensate.
*/
skip_len = adjust_incomplete(line, len, size);
if (skip_len) {
len--;
skip_len++;
}
switch (*line) {
default:
return -1;
@ -1745,19 +1799,12 @@ static int parse_fragment(struct apply_state *state,
newlines--;
trailing = 0;
break;
}
/*
* We allow "\ No newline at end of file". Depending
* on locale settings when the patch was produced we
* don't know what this line looks like. The only
* thing we do know is that it begins with "\ ".
* Checking for 12 is just for sanity check -- any
* l10n of "\ No newline..." is at least that long.
*/
case '\\':
if (len < 12 || memcmp(line, "\\ ", 2))
return -1;
break;
/* eat the "\\ No newline..." as well, if exists */
if (skip_len) {
len += skip_len;
state->linenr++;
}
}
if (oldlines || newlines)
@ -1768,14 +1815,6 @@ static int parse_fragment(struct apply_state *state,
fragment->leading = leading;
fragment->trailing = trailing;
/*
* If a fragment ends with an incomplete line, we failed to include
* it in the above loop because we hit oldlines == newlines == 0
* before seeing it.
*/
if (12 < size && !memcmp(line, "\\ ", 2))
offset += linelen(line, size);
patch->lines_added += added;
patch->lines_deleted += deleted;
@ -3779,7 +3818,7 @@ static int check_preimage(struct apply_state *state,
if (*ce && !(*ce)->ce_mode)
BUG("ce_mode == 0 for path '%s'", old_name);
if (trust_executable_bit)
if (trust_executable_bit || !S_ISREG(st->st_mode))
st_mode = ce_mode_from_stat(*ce, st->st_mode);
else if (*ce)
st_mode = (*ce)->ce_mode;

View File

@ -12,8 +12,8 @@
#include "tar.h"
#include "archive.h"
#include "odb.h"
#include "odb/streaming.h"
#include "strbuf.h"
#include "streaming.h"
#include "run-command.h"
#include "write-or-die.h"
@ -129,22 +129,20 @@ static void write_trailer(void)
*/
static int stream_blocked(struct repository *r, const struct object_id *oid)
{
struct git_istream *st;
enum object_type type;
unsigned long sz;
struct odb_read_stream *st;
char buf[BLOCKSIZE];
ssize_t readlen;
st = open_istream(r, oid, &type, &sz, NULL);
st = odb_read_stream_open(r->objects, oid, NULL);
if (!st)
return error(_("cannot stream blob %s"), oid_to_hex(oid));
for (;;) {
readlen = read_istream(st, buf, sizeof(buf));
readlen = odb_read_stream_read(st, buf, sizeof(buf));
if (readlen <= 0)
break;
do_write_blocked(buf, readlen);
}
close_istream(st);
odb_read_stream_close(st);
if (!readlen)
finish_record();
return readlen;

View File

@ -10,9 +10,9 @@
#include "gettext.h"
#include "git-zlib.h"
#include "hex.h"
#include "streaming.h"
#include "utf8.h"
#include "odb.h"
#include "odb/streaming.h"
#include "strbuf.h"
#include "userdiff.h"
#include "write-or-die.h"
@ -309,7 +309,7 @@ static int write_zip_entry(struct archiver_args *args,
enum zip_method method;
unsigned char *out;
void *deflated = NULL;
struct git_istream *stream = NULL;
struct odb_read_stream *stream = NULL;
unsigned long flags = 0;
int is_binary = -1;
const char *path_without_prefix = path + args->baselen;
@ -347,12 +347,11 @@ static int write_zip_entry(struct archiver_args *args,
method = ZIP_METHOD_DEFLATE;
if (!buffer) {
enum object_type type;
stream = open_istream(args->repo, oid, &type, &size,
NULL);
stream = odb_read_stream_open(args->repo->objects, oid, NULL);
if (!stream)
return error(_("cannot stream blob %s"),
oid_to_hex(oid));
size = stream->size;
flags |= ZIP_STREAM;
out = NULL;
} else {
@ -429,7 +428,7 @@ static int write_zip_entry(struct archiver_args *args,
ssize_t readlen;
for (;;) {
readlen = read_istream(stream, buf, sizeof(buf));
readlen = odb_read_stream_read(stream, buf, sizeof(buf));
if (readlen <= 0)
break;
crc = crc32(crc, buf, readlen);
@ -439,7 +438,7 @@ static int write_zip_entry(struct archiver_args *args,
buf, readlen);
write_or_die(1, buf, readlen);
}
close_istream(stream);
odb_read_stream_close(stream);
if (readlen)
return readlen;
@ -462,7 +461,7 @@ static int write_zip_entry(struct archiver_args *args,
zstream.avail_out = sizeof(compressed);
for (;;) {
readlen = read_istream(stream, buf, sizeof(buf));
readlen = odb_read_stream_read(stream, buf, sizeof(buf));
if (readlen <= 0)
break;
crc = crc32(crc, buf, readlen);
@ -486,7 +485,7 @@ static int write_zip_entry(struct archiver_args *args,
}
}
close_istream(stream);
odb_read_stream_close(stream);
if (readlen)
return readlen;

50
attr.c
View File

@ -1064,24 +1064,52 @@ static int path_matches(const char *pathname, int pathlen,
pattern, prefix, pat->patternlen);
}
static int macroexpand_one(struct all_attrs_item *all_attrs, int nr, int rem);
struct attr_state_queue {
const struct attr_state **items;
size_t alloc, nr;
};
static void attr_state_queue_push(struct attr_state_queue *t,
const struct match_attr *a)
{
for (size_t i = 0; i < a->num_attr; i++) {
ALLOC_GROW(t->items, t->nr + 1, t->alloc);
t->items[t->nr++] = &a->state[i];
}
}
static const struct attr_state *attr_state_queue_pop(struct attr_state_queue *t)
{
return t->nr ? t->items[--t->nr] : NULL;
}
static void attr_state_queue_release(struct attr_state_queue *t)
{
free(t->items);
}
static int fill_one(struct all_attrs_item *all_attrs,
const struct match_attr *a, int rem)
{
size_t i;
struct attr_state_queue todo = { 0 };
const struct attr_state *state;
for (i = a->num_attr; rem > 0 && i > 0; i--) {
const struct git_attr *attr = a->state[i - 1].attr;
attr_state_queue_push(&todo, a);
while (rem > 0 && (state = attr_state_queue_pop(&todo))) {
const struct git_attr *attr = state->attr;
const char **n = &(all_attrs[attr->attr_nr].value);
const char *v = a->state[i - 1].setto;
const char *v = state->setto;
if (*n == ATTR__UNKNOWN) {
const struct all_attrs_item *item =
&all_attrs[attr->attr_nr];
*n = v;
rem--;
rem = macroexpand_one(all_attrs, attr->attr_nr, rem);
if (item->macro && item->value == ATTR__TRUE)
attr_state_queue_push(&todo, item->macro);
}
}
attr_state_queue_release(&todo);
return rem;
}
@ -1106,16 +1134,6 @@ static int fill(const char *path, int pathlen, int basename_offset,
return rem;
}
static int macroexpand_one(struct all_attrs_item *all_attrs, int nr, int rem)
{
const struct all_attrs_item *item = &all_attrs[nr];
if (item->macro && item->value == ATTR__TRUE)
return fill_one(all_attrs, item->macro, rem);
else
return rem;
}
/*
* Marks the attributes which are macros based on the attribute stack.
* This prevents having to search through the attribute stack each time

View File

@ -41,4 +41,7 @@
#undef asctime_r
#define asctime_r(t, buf) BANNED(asctime_r)
#undef mktemp
#define mktemp(x) BANNED(mktemp)
#endif /* BANNED_H */

View File

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

View File

@ -375,7 +375,7 @@ int validate_branchname(const char *name, struct strbuf *ref)
if (check_branch_ref(ref, name)) {
int code = die_message(_("'%s' is not a valid branch name"), name);
advise_if_enabled(ADVICE_REF_SYNTAX,
_("See `man git check-ref-format`"));
_("See 'git help check-ref-format'"));
exit(code);
}

View File

@ -27,13 +27,14 @@ static GIT_PATH_FUNC(git_path_bisect_first_parent, "BISECT_FIRST_PARENT")
static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN")
#define BUILTIN_GIT_BISECT_START_USAGE \
N_("git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>]" \
" [--no-checkout] [--first-parent] [<bad> [<good>...]] [--]" \
" [<pathspec>...]")
#define BUILTIN_GIT_BISECT_STATE_USAGE \
N_("git bisect (good|bad) [<rev>...]")
N_("git bisect start [--term-(bad|new)=<term-new> --term-(good|old)=<term-old>]\n" \
" [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<pathspec>...]")
#define BUILTIN_GIT_BISECT_BAD_USAGE \
N_("git bisect (bad|new|<term-new>) [<rev>]")
#define BUILTIN_GIT_BISECT_GOOD_USAGE \
N_("git bisect (good|old|<term-old>) [<rev>...]")
#define BUILTIN_GIT_BISECT_TERMS_USAGE \
"git bisect terms [--term-good | --term-bad]"
"git bisect terms [--term-(good|old) | --term-(bad|new)]"
#define BUILTIN_GIT_BISECT_SKIP_USAGE \
N_("git bisect skip [(<rev>|<range>)...]")
#define BUILTIN_GIT_BISECT_NEXT_USAGE \
@ -41,17 +42,20 @@ static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN")
#define BUILTIN_GIT_BISECT_RESET_USAGE \
N_("git bisect reset [<commit>]")
#define BUILTIN_GIT_BISECT_VISUALIZE_USAGE \
"git bisect visualize"
"git bisect (visualize|view)"
#define BUILTIN_GIT_BISECT_REPLAY_USAGE \
N_("git bisect replay <logfile>")
#define BUILTIN_GIT_BISECT_LOG_USAGE \
"git bisect log"
#define BUILTIN_GIT_BISECT_RUN_USAGE \
N_("git bisect run <cmd> [<arg>...]")
#define BUILTIN_GIT_BISECT_HELP_USAGE \
"git bisect help"
static const char * const git_bisect_usage[] = {
BUILTIN_GIT_BISECT_START_USAGE,
BUILTIN_GIT_BISECT_STATE_USAGE,
BUILTIN_GIT_BISECT_BAD_USAGE,
BUILTIN_GIT_BISECT_GOOD_USAGE,
BUILTIN_GIT_BISECT_TERMS_USAGE,
BUILTIN_GIT_BISECT_SKIP_USAGE,
BUILTIN_GIT_BISECT_NEXT_USAGE,
@ -60,6 +64,7 @@ static const char * const git_bisect_usage[] = {
BUILTIN_GIT_BISECT_REPLAY_USAGE,
BUILTIN_GIT_BISECT_LOG_USAGE,
BUILTIN_GIT_BISECT_RUN_USAGE,
BUILTIN_GIT_BISECT_HELP_USAGE,
NULL
};
@ -358,10 +363,7 @@ static int check_and_set_terms(struct bisect_terms *terms, const char *cmd)
return 0;
}
static int inc_nr(const char *refname UNUSED,
const char *referent UNUSED,
const struct object_id *oid UNUSED,
int flag UNUSED, void *cb_data)
static int inc_nr(const struct reference *ref UNUSED, void *cb_data)
{
unsigned int *nr = (unsigned int *)cb_data;
(*nr)++;
@ -549,12 +551,11 @@ finish:
return res;
}
static int add_bisect_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
int flags UNUSED, void *cb)
static int add_bisect_ref(const struct reference *ref, void *cb)
{
struct add_bisect_ref_data *data = cb;
add_pending_oid(data->revs, refname, oid, data->object_flags);
add_pending_oid(data->revs, ref->name, ref->oid, data->object_flags);
return 0;
}
@ -1165,12 +1166,9 @@ static int bisect_visualize(struct bisect_terms *terms, int argc,
return run_command(&cmd);
}
static int get_first_good(const char *refname UNUSED,
const char *referent UNUSED,
const struct object_id *oid,
int flag UNUSED, void *cb_data)
static int get_first_good(const struct reference *ref, void *cb_data)
{
oidcpy(cb_data, oid);
oidcpy(cb_data, ref->oid);
return 1;
}

View File

@ -739,7 +739,8 @@ static int git_blame_config(const char *var, const char *value,
ret = git_config_pathname(&str, var, value);
if (ret)
return ret;
string_list_insert(&ignore_revs_file_list, str);
if (str)
string_list_insert(&ignore_revs_file_list, str);
free(str);
return 0;
}
@ -779,6 +780,19 @@ static int git_blame_config(const char *var, const char *value,
}
}
if (!strcmp(var, "diff.algorithm")) {
long diff_algorithm;
if (!value)
return config_error_nonbool(var);
diff_algorithm = parse_algorithm_value(value);
if (diff_algorithm < 0)
return error(_("unknown value for config '%s': %s"),
var, value);
xdl_opts &= ~XDF_DIFF_ALGORITHM_MASK;
xdl_opts |= diff_algorithm;
return 0;
}
if (git_diff_heuristic_config(var, value, cb) < 0)
return -1;
if (userdiff_config(var, value) < 0)
@ -824,6 +838,38 @@ static int blame_move_callback(const struct option *option, const char *arg, int
return 0;
}
static int blame_diff_algorithm_minimal(const struct option *option,
const char *arg, int unset)
{
int *opt = option->value;
BUG_ON_OPT_ARG(arg);
*opt &= ~XDF_DIFF_ALGORITHM_MASK;
if (!unset)
*opt |= XDF_NEED_MINIMAL;
return 0;
}
static int blame_diff_algorithm_callback(const struct option *option,
const char *arg, int unset)
{
int *opt = option->value;
long value = parse_algorithm_value(arg);
BUG_ON_OPT_NEG(unset);
if (value < 0)
return error(_("option diff-algorithm accepts \"myers\", "
"\"minimal\", \"patience\" and \"histogram\""));
*opt &= ~XDF_DIFF_ALGORITHM_MASK;
*opt |= value;
return 0;
}
static int is_a_rev(const char *name)
{
struct object_id oid;
@ -915,11 +961,16 @@ int cmd_blame(int argc,
OPT_BIT('s', NULL, &output_option, N_("suppress author name and timestamp (Default: off)"), OUTPUT_NO_AUTHOR),
OPT_BIT('e', "show-email", &output_option, N_("show author email instead of name (Default: off)"), OUTPUT_SHOW_EMAIL),
OPT_BIT('w', NULL, &xdl_opts, N_("ignore whitespace differences"), XDF_IGNORE_WHITESPACE),
OPT_CALLBACK_F(0, "diff-algorithm", &xdl_opts, N_("<algorithm>"),
N_("choose a diff algorithm"),
PARSE_OPT_NONEG, blame_diff_algorithm_callback),
OPT_STRING_LIST(0, "ignore-rev", &ignore_rev_list, N_("rev"), N_("ignore <rev> when blaming")),
OPT_STRING_LIST(0, "ignore-revs-file", &ignore_revs_file_list, N_("file"), N_("ignore revisions from <file>")),
OPT_BIT(0, "color-lines", &output_option, N_("color redundant metadata from previous line differently"), OUTPUT_COLOR_LINE),
OPT_BIT(0, "color-by-age", &output_option, N_("color lines by age"), OUTPUT_SHOW_AGE_WITH_COLOR),
OPT_BIT(0, "minimal", &xdl_opts, N_("spend extra cycles to find better match"), XDF_NEED_MINIMAL),
OPT_CALLBACK_F(0, "minimal", &xdl_opts, NULL,
N_("spend extra cycles to find a better match"),
PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, blame_diff_algorithm_minimal),
OPT_STRING('S', NULL, &revs_file, N_("file"), N_("use revisions from <file> instead of calling git-rev-list")),
OPT_STRING(0, "contents", &contents_from, N_("file"), N_("use <file>'s contents as the final image")),
OPT_CALLBACK_F('C', NULL, &opt, N_("score"), N_("find line copies within and across files"), PARSE_OPT_OPTARG, blame_copy_callback),

View File

@ -591,7 +591,7 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
else {
int code = die_message(_("invalid branch name: '%s'"), oldname);
advise_if_enabled(ADVICE_REF_SYNTAX,
_("See `man git check-ref-format`"));
_("See 'git help check-ref-format'"));
exit(code);
}
}

View File

@ -18,13 +18,13 @@
#include "list-objects-filter-options.h"
#include "parse-options.h"
#include "userdiff.h"
#include "streaming.h"
#include "oid-array.h"
#include "packfile.h"
#include "pack-bitmap.h"
#include "object-file.h"
#include "object-name.h"
#include "odb.h"
#include "odb/streaming.h"
#include "replace-object.h"
#include "promisor-remote.h"
#include "mailmap.h"
@ -95,7 +95,7 @@ static int filter_object(const char *path, unsigned mode,
static int stream_blob(const struct object_id *oid)
{
if (stream_blob_to_fd(1, oid, NULL, 0))
if (odb_stream_blob_to_fd(the_repository->objects, 1, oid, NULL, 0))
die("unable to stream %s to stdout", oid_to_hex(oid));
return 0;
}

View File

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

View File

@ -1617,7 +1617,7 @@ int cmd_clone(int argc,
transport_disconnect(transport);
if (option_dissociate) {
close_object_store(the_repository->objects);
odb_close(the_repository->objects);
dissociate_from_references();
}

View File

@ -261,6 +261,12 @@ struct strbuf_list {
int alloc;
};
/*
* Format the configuration key-value pair (`key_`, `value_`) and
* append it into strbuf `buf`. Returns a negative value on failure,
* 0 on success, 1 on a missing optional value (i.e., telling the
* caller to pretend that <key_,value_> did not exist).
*/
static int format_config(const struct config_display_options *opts,
struct strbuf *buf, const char *key_,
const char *value_, const struct key_value_info *kvi)
@ -299,7 +305,10 @@ static int format_config(const struct config_display_options *opts,
char *v;
if (git_config_pathname(&v, key_, value_) < 0)
return -1;
strbuf_addstr(buf, v);
if (v)
strbuf_addstr(buf, v);
else
return 1; /* :(optional)no-such-file */
free((char *)v);
} else if (opts->type == TYPE_EXPIRY_DATE) {
timestamp_t t;
@ -344,6 +353,7 @@ static int collect_config(const char *key_, const char *value_,
struct collect_config_data *data = cb;
struct strbuf_list *values = data->values;
const struct key_value_info *kvi = ctx->kvi;
int status;
if (!(data->get_value_flags & GET_VALUE_KEY_REGEXP) &&
strcmp(key_, data->key))
@ -361,8 +371,15 @@ static int collect_config(const char *key_, const char *value_,
ALLOC_GROW(values->items, values->nr + 1, values->alloc);
strbuf_init(&values->items[values->nr], 0);
return format_config(data->display_opts, &values->items[values->nr++],
key_, value_, kvi);
status = format_config(data->display_opts, &values->items[values->nr++],
key_, value_, kvi);
if (status < 0)
return status;
if (status) {
strbuf_release(&values->items[--values->nr]);
status = 0;
}
return status;
}
static int get_value(const struct config_location_options *opts,
@ -438,15 +455,23 @@ static int get_value(const struct config_location_options *opts,
if (!values.nr && display_opts->default_value) {
struct key_value_info kvi = KVI_INIT;
struct strbuf *item;
int status;
kvi_from_param(&kvi);
ALLOC_GROW(values.items, values.nr + 1, values.alloc);
item = &values.items[values.nr++];
strbuf_init(item, 0);
if (format_config(display_opts, item, key_,
display_opts->default_value, &kvi) < 0)
status = format_config(display_opts, item, key_,
display_opts->default_value, &kvi);
if (status < 0)
die(_("failed to format default config value: %s"),
display_opts->default_value);
if (status) {
/* default was a missing optional value */
values.nr--;
strbuf_release(item);
}
}
ret = !values.nr;
@ -714,11 +739,13 @@ static int get_urlmatch(const struct config_location_options *opts,
for_each_string_list_item(item, &values) {
struct urlmatch_current_candidate_value *matched = item->util;
struct strbuf buf = STRBUF_INIT;
int status;
format_config(&display_opts, &buf, item->string,
matched->value_is_null ? NULL : matched->value.buf,
&matched->kvi);
fwrite(buf.buf, 1, buf.len, stdout);
status = format_config(&display_opts, &buf, item->string,
matched->value_is_null ? NULL : matched->value.buf,
&matched->kvi);
if (!status)
fwrite(buf.buf, 1, buf.len, stdout);
strbuf_release(&buf);
strbuf_release(&matched->value);
@ -985,7 +1012,7 @@ static int cmd_config_set(int argc, const char **argv, const char *prefix,
argv[0], comment, value);
if (ret == CONFIG_NOTHING_SET)
error(_("cannot overwrite multiple values with a single value\n"
" Use a regexp, --add or --replace-all to change %s."), argv[0]);
" Use --value=<pattern>, --append or --all to change %s."), argv[0]);
}
location_options_release(&location_opts);
@ -1003,8 +1030,8 @@ static int cmd_config_unset(int argc, const char **argv, const char *prefix,
struct option opts[] = {
CONFIG_LOCATION_OPTIONS(location_opts),
OPT_GROUP(N_("Filter")),
OPT_BIT(0, "all", &flags, N_("replace multi-valued config option with new value"), CONFIG_FLAGS_MULTI_REPLACE),
OPT_STRING(0, "value", &value_pattern, N_("pattern"), N_("show config with values matching the pattern")),
OPT_BIT(0, "all", &flags, N_("unset all multi-valued config options"), CONFIG_FLAGS_MULTI_REPLACE),
OPT_STRING(0, "value", &value_pattern, N_("pattern"), N_("unset multi-valued config options with matching values")),
OPT_BIT(0, "fixed-value", &flags, N_("use string equality when comparing values to value pattern"), CONFIG_FLAGS_FIXED_VALUE),
OPT_END(),
};

View File

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

View File

@ -65,7 +65,7 @@ static int parse_opt_sign_mode(const struct option *opt,
return 0;
if (parse_sign_mode(arg, val))
return error("Unknown %s mode: %s", opt->long_name, arg);
return error(_("unknown %s mode: %s"), opt->long_name, arg);
return 0;
}
@ -82,7 +82,7 @@ static int parse_opt_tag_of_filtered_mode(const struct option *opt,
else if (!strcmp(arg, "rewrite"))
*val = REWRITE;
else
return error("Unknown tag-of-filtered mode: %s", arg);
return error(_("unknown tag-of-filtered mode: %s"), arg);
return 0;
}
@ -107,7 +107,7 @@ static int parse_opt_reencode_mode(const struct option *opt,
if (!strcasecmp(arg, "abort"))
*val = REENCODE_ABORT;
else
return error("Unknown reencoding mode: %s", arg);
return error(_("unknown reencoding mode: %s"), arg);
}
return 0;
@ -318,16 +318,16 @@ static void export_blob(const struct object_id *oid)
} else {
buf = odb_read_object(the_repository->objects, oid, &type, &size);
if (!buf)
die("could not read blob %s", oid_to_hex(oid));
die(_("could not read blob %s"), oid_to_hex(oid));
if (check_object_signature(the_repository, oid, buf, size,
type) < 0)
die("oid mismatch in blob %s", oid_to_hex(oid));
die(_("oid mismatch in blob %s"), oid_to_hex(oid));
object = parse_object_buffer(the_repository, oid, type,
size, buf, &eaten);
}
if (!object)
die("Could not read blob %s", oid_to_hex(oid));
die(_("could not read blob %s"), oid_to_hex(oid));
mark_next_object(object);
@ -336,7 +336,7 @@ static void export_blob(const struct object_id *oid)
printf("original-oid %s\n", oid_to_hex(oid));
printf("data %"PRIuMAX"\n", (uintmax_t)size);
if (size && fwrite(buf, size, 1, stdout) != 1)
die_errno("could not write blob '%s'", oid_to_hex(oid));
die_errno(_("could not write blob '%s'"), oid_to_hex(oid));
printf("\n");
show_progress();
@ -499,10 +499,10 @@ static void show_filemodify(struct diff_queue_struct *q,
break;
default:
die("Unexpected comparison status '%c' for %s, %s",
q->queue[i]->status,
ospec->path ? ospec->path : "none",
spec->path ? spec->path : "none");
die(_("unexpected comparison status '%c' for %s, %s"),
q->queue[i]->status,
ospec->path ? ospec->path : _("none"),
spec->path ? spec->path : _("none"));
}
}
}
@ -699,14 +699,14 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
author = strstr(commit_buffer_cursor, "\nauthor ");
if (!author)
die("could not find author in commit %s",
die(_("could not find author in commit %s"),
oid_to_hex(&commit->object.oid));
author++;
commit_buffer_cursor = author_end = strchrnul(author, '\n');
committer = strstr(commit_buffer_cursor, "\ncommitter ");
if (!committer)
die("could not find committer in commit %s",
die(_("could not find committer in commit %s"),
oid_to_hex(&commit->object.oid));
committer++;
commit_buffer_cursor = committer_end = strchrnul(committer, '\n');
@ -781,8 +781,8 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
case REENCODE_NO:
break;
case REENCODE_ABORT:
die("Encountered commit-specific encoding %.*s in commit "
"%s; use --reencode=[yes|no] to handle it",
die(_("encountered commit-specific encoding %.*s in commit "
"%s; use --reencode=[yes|no] to handle it"),
(int)encoding_len, encoding,
oid_to_hex(&commit->object.oid));
}
@ -797,12 +797,9 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
(int)(committer_end - committer), committer);
if (signatures.nr) {
switch (signed_commit_mode) {
case SIGN_ABORT:
die("encountered signed commit %s; use "
"--signed-commits=<mode> to handle it",
oid_to_hex(&commit->object.oid));
/* Exporting modes */
case SIGN_WARN_VERBATIM:
warning("exporting %"PRIuMAX" signature(s) for commit %s",
warning(_("exporting %"PRIuMAX" signature(s) for commit %s"),
(uintmax_t)signatures.nr, oid_to_hex(&commit->object.oid));
/* fallthru */
case SIGN_VERBATIM:
@ -811,12 +808,25 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
print_signature(item->string, item->util);
}
break;
/* Stripping modes */
case SIGN_WARN_STRIP:
warning("stripping signature(s) from commit %s",
warning(_("stripping signature(s) from commit %s"),
oid_to_hex(&commit->object.oid));
/* fallthru */
case SIGN_STRIP:
break;
/* Aborting modes */
case SIGN_ABORT:
die(_("encountered signed commit %s; use "
"--signed-commits=<mode> to handle it"),
oid_to_hex(&commit->object.oid));
case SIGN_STRIP_IF_INVALID:
die(_("'strip-if-invalid' is not a valid mode for "
"git fast-export with --signed-commits=<mode>"));
default:
BUG("invalid signed_commit_mode value %d", signed_commit_mode);
}
string_list_clear(&signatures, 0);
}
@ -890,7 +900,8 @@ static void handle_tag(const char *name, struct tag *tag)
tagged = ((struct tag *)tagged)->tagged;
}
if (tagged->type == OBJ_TREE) {
warning("Omitting tag %s,\nsince tags of trees (or tags of tags of trees, etc.) are not supported.",
warning(_("omitting tag %s,\nsince tags of trees (or tags "
"of tags of trees, etc.) are not supported."),
oid_to_hex(&tag->object.oid));
return;
}
@ -898,7 +909,7 @@ static void handle_tag(const char *name, struct tag *tag)
buf = odb_read_object(the_repository->objects, &tag->object.oid,
&type, &size);
if (!buf)
die("could not read tag %s", oid_to_hex(&tag->object.oid));
die(_("could not read tag %s"), oid_to_hex(&tag->object.oid));
message = memmem(buf, size, "\n\n", 2);
if (message) {
message += 2;
@ -934,23 +945,33 @@ static void handle_tag(const char *name, struct tag *tag)
size_t sig_offset = parse_signed_buffer(message, message_size);
if (sig_offset < message_size)
switch (signed_tag_mode) {
case SIGN_ABORT:
die("encountered signed tag %s; use "
"--signed-tags=<mode> to handle it",
oid_to_hex(&tag->object.oid));
/* Exporting modes */
case SIGN_WARN_VERBATIM:
warning("exporting signed tag %s",
warning(_("exporting signed tag %s"),
oid_to_hex(&tag->object.oid));
/* fallthru */
case SIGN_VERBATIM:
break;
/* Stripping modes */
case SIGN_WARN_STRIP:
warning("stripping signature from tag %s",
warning(_("stripping signature from tag %s"),
oid_to_hex(&tag->object.oid));
/* fallthru */
case SIGN_STRIP:
message_size = sig_offset;
break;
/* Aborting modes */
case SIGN_ABORT:
die(_("encountered signed tag %s; use "
"--signed-tags=<mode> to handle it"),
oid_to_hex(&tag->object.oid));
case SIGN_STRIP_IF_INVALID:
die(_("'strip-if-invalid' is not a valid mode for "
"git fast-export with --signed-tags=<mode>"));
default:
BUG("invalid signed_commit_mode value %d", signed_commit_mode);
}
}
@ -960,8 +981,8 @@ static void handle_tag(const char *name, struct tag *tag)
if (!tagged_mark) {
switch (tag_of_filtered_mode) {
case TAG_FILTERING_ABORT:
die("tag %s tags unexported object; use "
"--tag-of-filtered-object=<mode> to handle it",
die(_("tag %s tags unexported object; use "
"--tag-of-filtered-object=<mode> to handle it"),
oid_to_hex(&tag->object.oid));
case DROP:
/* Ignore this tag altogether */
@ -969,7 +990,7 @@ static void handle_tag(const char *name, struct tag *tag)
return;
case REWRITE:
if (tagged->type == OBJ_TAG && !mark_tags) {
die(_("Error: Cannot export nested tags unless --mark-tags is specified."));
die(_("cannot export nested tags unless --mark-tags is specified."));
} else if (tagged->type == OBJ_COMMIT) {
p = rewrite_commit((struct commit *)tagged);
if (!p) {
@ -1025,7 +1046,7 @@ static struct commit *get_commit(struct rev_cmdline_entry *e, const char *full_n
tag = (struct tag *)tag->tagged;
}
if (!tag)
die("Tag %s points nowhere?", e->name);
die(_("tag %s points nowhere?"), e->name);
return (struct commit *)tag;
}
default:
@ -1063,7 +1084,7 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)
commit = get_commit(e, full_name);
if (!commit) {
warning("%s: Unexpected object of type %s, skipping.",
warning(_("%s: unexpected object of type %s, skipping."),
e->name,
type_name(e->item->type));
free(full_name);
@ -1078,7 +1099,7 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)
free(full_name);
continue;
default: /* OBJ_TAG (nested tags) is already handled */
warning("Tag points to object of unexpected type %s, skipping.",
warning(_("tag points to object of unexpected type %s, skipping."),
type_name(commit->object.type));
free(full_name);
continue;
@ -1174,7 +1195,7 @@ static void export_marks(char *file)
f = fopen_for_writing(file);
if (!f)
die_errno("Unable to open marks file %s for writing.", file);
die_errno(_("unable to open marks file %s for writing."), file);
for (i = 0; i < idnums.size; i++) {
if (deco->base && deco->base->type == 1) {
@ -1191,7 +1212,7 @@ static void export_marks(char *file)
e |= ferror(f);
e |= fclose(f);
if (e)
error("Unable to write marks file %s.", file);
error(_("unable to write marks file %s."), file);
}
static void import_marks(char *input_file, int check_exists)
@ -1214,20 +1235,20 @@ static void import_marks(char *input_file, int check_exists)
line_end = strchr(line, '\n');
if (line[0] != ':' || !line_end)
die("corrupt mark line: %s", line);
die(_("corrupt mark line: %s"), line);
*line_end = '\0';
mark = strtoumax(line + 1, &mark_end, 10);
if (!mark || mark_end == line + 1
|| *mark_end != ' ' || get_oid_hex(mark_end + 1, &oid))
die("corrupt mark line: %s", line);
die(_("corrupt mark line: %s"), line);
if (last_idnum < mark)
last_idnum = mark;
type = odb_read_object_info(the_repository->objects, &oid, NULL);
if (type < 0)
die("object not found: %s", oid_to_hex(&oid));
die(_("object not found: %s"), oid_to_hex(&oid));
if (type != OBJ_COMMIT)
/* only commits */
@ -1235,12 +1256,12 @@ static void import_marks(char *input_file, int check_exists)
commit = lookup_commit(the_repository, &oid);
if (!commit)
die("not a commit? can't happen: %s", oid_to_hex(&oid));
die(_("not a commit? can't happen: %s"), oid_to_hex(&oid));
object = &commit->object;
if (object->flags & SHOWN)
error("Object %s already has a mark", oid_to_hex(&oid));
error(_("object %s already has a mark"), oid_to_hex(&oid));
mark_object(object, mark);
@ -1394,7 +1415,7 @@ int cmd_fast_export(int argc,
get_tags_and_duplicates(&revs.cmdline);
if (prepare_revision_walk(&revs))
die("revision walk setup failed");
die(_("revision walk setup failed"));
revs.reverse = 1;
revs.diffopt.format_callback = show_filemodify;

File diff suppressed because it is too large Load Diff

View File

@ -47,7 +47,7 @@
static const char * const builtin_fetch_usage[] = {
N_("git fetch [<options>] [<repository> [<refspec>...]]"),
N_("git fetch [<options>] <group>"),
N_("git fetch --multiple [<options>] [(<repository> | <group>)...]"),
N_("git fetch --multiple [<options>] [(<repository>|<group>)...]"),
N_("git fetch --all [<options>]"),
NULL
};
@ -289,13 +289,11 @@ static struct refname_hash_entry *refname_hash_add(struct hashmap *map,
return ent;
}
static int add_one_refname(const char *refname, const char *referent UNUSED,
const struct object_id *oid,
int flag UNUSED, void *cbdata)
static int add_one_refname(const struct reference *ref, void *cbdata)
{
struct hashmap *refname_map = cbdata;
(void) refname_hash_add(refname_map, refname, oid);
(void) refname_hash_add(refname_map, ref->name, ref->oid);
return 0;
}
@ -1416,14 +1414,11 @@ static void set_option(struct transport *transport, const char *name, const char
}
static int add_oid(const char *refname UNUSED,
const char *referent UNUSED,
const struct object_id *oid,
int flags UNUSED, void *cb_data)
static int add_oid(const struct reference *ref, void *cb_data)
{
struct oid_array *oids = cb_data;
oid_array_append(oids, oid);
oid_array_append(oids, ref->oid);
return 0;
}
@ -1686,6 +1681,36 @@ static void ref_transaction_rejection_handler(const char *refname,
*data->retcode = 1;
}
/*
* Commit the reference transaction. If it isn't an atomic transaction, handle
* rejected updates as part of using batched updates.
*/
static int commit_ref_transaction(struct ref_transaction **transaction,
bool is_atomic, const char *remote_name,
struct strbuf *err)
{
int retcode = ref_transaction_commit(*transaction, err);
if (retcode)
goto out;
if (!is_atomic) {
struct ref_rejection_data data = {
.conflict_msg_shown = 0,
.remote_name = remote_name,
.retcode = &retcode,
};
ref_transaction_for_each_rejected_update(*transaction,
ref_transaction_rejection_handler,
&data);
}
out:
ref_transaction_free(*transaction);
*transaction = NULL;
return retcode;
}
static int do_fetch(struct transport *transport,
struct refspec *rs,
const struct fetch_config *config)
@ -1858,33 +1883,14 @@ static int do_fetch(struct transport *transport,
if (retcode)
goto cleanup;
retcode = ref_transaction_commit(transaction, &err);
if (retcode) {
/*
* Explicitly handle transaction cleanup to avoid
* aborting an already closed transaction.
*/
ref_transaction_free(transaction);
transaction = NULL;
retcode = commit_ref_transaction(&transaction, atomic_fetch,
transport->remote->name, &err);
/*
* With '--atomic', bail out if the transaction fails. Without '--atomic',
* continue to fetch head and perform other post-fetch operations.
*/
if (retcode && atomic_fetch)
goto cleanup;
}
if (!atomic_fetch) {
struct ref_rejection_data data = {
.retcode = &retcode,
.conflict_msg_shown = 0,
.remote_name = transport->remote->name,
};
ref_transaction_for_each_rejected_update(transaction,
ref_transaction_rejection_handler,
&data);
if (retcode) {
ref_transaction_free(transaction);
transaction = NULL;
goto cleanup;
}
}
commit_fetch_head(&fetch_head);
@ -1950,6 +1956,14 @@ static int do_fetch(struct transport *transport,
}
cleanup:
/*
* When using batched updates, we want to commit the non-rejected
* updates and also handle the rejections.
*/
if (retcode && !atomic_fetch && transaction)
commit_ref_transaction(&transaction, false,
transport->remote->name, &err);
if (retcode) {
if (err.len) {
error("%s", err.buf);

View File

@ -13,11 +13,11 @@
#include "fsck.h"
#include "parse-options.h"
#include "progress.h"
#include "streaming.h"
#include "packfile.h"
#include "object-file.h"
#include "object-name.h"
#include "odb.h"
#include "odb/streaming.h"
#include "path.h"
#include "read-cache-ll.h"
#include "replace-object.h"
@ -340,7 +340,8 @@ static void check_unreachable_object(struct object *obj)
}
f = xfopen(filename, "w");
if (obj->type == OBJ_BLOB) {
if (stream_blob_to_fd(fileno(f), &obj->oid, NULL, 1))
if (odb_stream_blob_to_fd(the_repository->objects, fileno(f),
&obj->oid, NULL, 1))
die_errno(_("could not write '%s'"), filename);
} else
fprintf(f, "%s\n", describe_object(&obj->oid));
@ -530,14 +531,13 @@ static int fsck_handle_reflog(const char *logname, void *cb_data)
return 0;
}
static int fsck_handle_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
int flag UNUSED, void *cb_data UNUSED)
static int fsck_handle_ref(const struct reference *ref, void *cb_data UNUSED)
{
struct object *obj;
obj = parse_object(the_repository, oid);
obj = parse_object(the_repository, ref->oid);
if (!obj) {
if (is_promisor_object(the_repository, oid)) {
if (is_promisor_object(the_repository, ref->oid)) {
/*
* Increment default_refs anyway, because this is a
* valid ref.
@ -546,19 +546,19 @@ static int fsck_handle_ref(const char *refname, const char *referent UNUSED, con
return 0;
}
error(_("%s: invalid sha1 pointer %s"),
refname, oid_to_hex(oid));
ref->name, oid_to_hex(ref->oid));
errors_found |= ERROR_REACHABLE;
/* We'll continue with the rest despite the error.. */
return 0;
}
if (obj->type != OBJ_COMMIT && is_branch(refname)) {
error(_("%s: not a commit"), refname);
if (obj->type != OBJ_COMMIT && is_branch(ref->name)) {
error(_("%s: not a commit"), ref->name);
errors_found |= ERROR_REFS;
}
default_refs++;
obj->flags |= USED;
fsck_put_object_name(&fsck_walk_options,
oid, "%s", refname);
ref->oid, "%s", ref->name);
mark_object_reachable(obj);
return 0;
@ -580,13 +580,19 @@ static void get_default_heads(void)
worktrees = get_worktrees();
for (p = worktrees; *p; p++) {
struct worktree *wt = *p;
struct strbuf ref = STRBUF_INIT;
struct strbuf refname = STRBUF_INIT;
strbuf_worktree_ref(wt, &ref, "HEAD");
fsck_head_link(ref.buf, &head_points_at, &head_oid);
if (head_points_at && !is_null_oid(&head_oid))
fsck_handle_ref(ref.buf, NULL, &head_oid, 0, NULL);
strbuf_release(&ref);
strbuf_worktree_ref(wt, &refname, "HEAD");
fsck_head_link(refname.buf, &head_points_at, &head_oid);
if (head_points_at && !is_null_oid(&head_oid)) {
struct reference ref = {
.name = refname.buf,
.oid = &head_oid,
};
fsck_handle_ref(&ref, NULL);
}
strbuf_release(&refname);
if (include_reflogs)
refs_for_each_reflog(get_worktree_ref_store(wt),

View File

@ -36,6 +36,7 @@
#include "reflog.h"
#include "repack.h"
#include "rerere.h"
#include "revision.h"
#include "blob.h"
#include "tree.h"
#include "promisor-remote.h"
@ -286,12 +287,26 @@ static void maintenance_run_opts_release(struct maintenance_run_opts *opts)
static int pack_refs_condition(UNUSED struct gc_config *cfg)
{
/*
* The auto-repacking logic for refs is handled by the ref backends and
* exposed via `git pack-refs --auto`. We thus always return truish
* here and let the backend decide for us.
*/
return 1;
struct string_list included_refs = STRING_LIST_INIT_NODUP;
struct ref_exclusions excludes = REF_EXCLUSIONS_INIT;
struct refs_optimize_opts optimize_opts = {
.exclusions = &excludes,
.includes = &included_refs,
.flags = REFS_OPTIMIZE_PRUNE | REFS_OPTIMIZE_AUTO,
};
bool required;
/* Check for all refs, similar to 'git refs optimize --all'. */
string_list_append(optimize_opts.includes, "*");
if (refs_optimize_required(get_main_ref_store(the_repository),
&optimize_opts, &required))
return 0;
clear_ref_exclusions(&excludes);
string_list_clear(&included_refs, 0);
return required;
}
static int maintenance_task_pack_refs(struct maintenance_run_opts *opts,
@ -1048,7 +1063,7 @@ int cmd_gc(int argc,
report_garbage = report_pack_garbage;
odb_reprepare(the_repository->objects);
if (pack_garbage.nr > 0) {
close_object_store(the_repository->objects);
odb_close(the_repository->objects);
clean_pack_garbage();
}
@ -1095,32 +1110,26 @@ static int maintenance_opt_schedule(const struct option *opt, const char *arg,
return 0;
}
/* Remember to update object flag allocation in object.h */
#define SEEN (1u<<0)
struct cg_auto_data {
int num_not_in_graph;
int limit;
};
static int dfs_on_ref(const char *refname UNUSED,
const char *referent UNUSED,
const struct object_id *oid,
int flags UNUSED,
void *cb_data)
static int dfs_on_ref(const struct reference *ref, void *cb_data)
{
struct cg_auto_data *data = (struct cg_auto_data *)cb_data;
int result = 0;
const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
struct commit_list *stack = NULL;
struct commit *commit;
if (!peel_iterated_oid(the_repository, oid, &peeled))
oid = &peeled;
if (odb_read_object_info(the_repository->objects, oid, NULL) != OBJ_COMMIT)
if (!reference_get_peeled_oid(the_repository, ref, &peeled))
maybe_peeled = &peeled;
if (odb_read_object_info(the_repository->objects, maybe_peeled, NULL) != OBJ_COMMIT)
return 0;
commit = lookup_commit(the_repository, oid);
commit = lookup_commit(the_repository, maybe_peeled);
if (!commit)
return 0;
if (repo_parse_commit(the_repository, commit) ||
@ -3447,7 +3456,67 @@ static int maintenance_stop(int argc, const char **argv, const char *prefix,
return update_background_schedule(NULL, 0);
}
static const char * const builtin_maintenance_usage[] = {
static const char *const builtin_maintenance_is_needed_usage[] = {
"git maintenance is-needed [--task=<task>] [--schedule]",
NULL
};
static int maintenance_is_needed(int argc, const char **argv, const char *prefix,
struct repository *repo UNUSED)
{
struct maintenance_run_opts opts = MAINTENANCE_RUN_OPTS_INIT;
struct string_list selected_tasks = STRING_LIST_INIT_DUP;
struct gc_config cfg = GC_CONFIG_INIT;
struct option options[] = {
OPT_BOOL(0, "auto", &opts.auto_flag,
N_("run tasks based on the state of the repository")),
OPT_CALLBACK_F(0, "task", &selected_tasks, N_("task"),
N_("check a specific task"),
PARSE_OPT_NONEG, task_option_parse),
OPT_END()
};
bool is_needed = false;
argc = parse_options(argc, argv, prefix, options,
builtin_maintenance_is_needed_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
if (argc)
usage_with_options(builtin_maintenance_is_needed_usage, options);
gc_config(&cfg);
initialize_task_config(&opts, &selected_tasks);
if (opts.auto_flag) {
for (size_t i = 0; i < opts.tasks_nr; i++) {
if (tasks[opts.tasks[i]].auto_condition &&
tasks[opts.tasks[i]].auto_condition(&cfg)) {
is_needed = true;
break;
}
}
} else {
/*
* When not using --auto we always require maintenance right now.
*
* TODO: this certainly is too eager, as some maintenance tasks may
* decide to not do anything because the data structures are already
* fully optimized. We may eventually want to extend the auto
* condition to also cover non-auto runs so that we can detect such
* cases.
*/
is_needed = true;
}
string_list_clear(&selected_tasks, 0);
maintenance_run_opts_release(&opts);
gc_config_release(&cfg);
if (is_needed)
return 0;
return 1;
}
static const char *const builtin_maintenance_usage[] = {
N_("git maintenance <subcommand> [<options>]"),
NULL,
};
@ -3464,6 +3533,7 @@ int cmd_maintenance(int argc,
OPT_SUBCOMMAND("stop", &fn, maintenance_stop),
OPT_SUBCOMMAND("register", &fn, maintenance_register),
OPT_SUBCOMMAND("unregister", &fn, maintenance_unregister),
OPT_SUBCOMMAND("is-needed", &fn, maintenance_is_needed),
OPT_END(),
};

View File

@ -43,6 +43,12 @@ static int run(int argc, const char **argv, const char *prefix,
if (!argc)
goto usage;
/*
* All current "hook run" use-cases require ungrouped child output.
* If this changes, a hook run argument can be added to toggle it.
*/
opt.ungroup = 1;
/*
* Having a -- for "run" when providing <hook-args> is
* mandatory.

View File

@ -16,12 +16,12 @@
#include "progress.h"
#include "fsck.h"
#include "strbuf.h"
#include "streaming.h"
#include "thread-utils.h"
#include "packfile.h"
#include "pack-revindex.h"
#include "object-file.h"
#include "odb.h"
#include "odb/streaming.h"
#include "oid-array.h"
#include "oidset.h"
#include "path.h"
@ -762,7 +762,7 @@ static void find_ref_delta_children(const struct object_id *oid,
struct compare_data {
struct object_entry *entry;
struct git_istream *st;
struct odb_read_stream *st;
unsigned char *buf;
unsigned long buf_size;
};
@ -779,7 +779,7 @@ static int compare_objects(const unsigned char *buf, unsigned long size,
}
while (size) {
ssize_t len = read_istream(data->st, data->buf, size);
ssize_t len = odb_read_stream_read(data->st, data->buf, size);
if (len == 0)
die(_("SHA1 COLLISION FOUND WITH %s !"),
oid_to_hex(&data->entry->idx.oid));
@ -798,8 +798,6 @@ static int compare_objects(const unsigned char *buf, unsigned long size,
static int check_collison(struct object_entry *entry)
{
struct compare_data data;
enum object_type type;
unsigned long size;
if (entry->size <= repo_settings_get_big_file_threshold(the_repository) ||
entry->type != OBJ_BLOB)
@ -807,15 +805,14 @@ static int check_collison(struct object_entry *entry)
memset(&data, 0, sizeof(data));
data.entry = entry;
data.st = open_istream(the_repository, &entry->idx.oid, &type, &size,
NULL);
data.st = odb_read_stream_open(the_repository->objects, &entry->idx.oid, NULL);
if (!data.st)
return -1;
if (size != entry->size || type != entry->type)
if (data.st->size != entry->size || data.st->type != entry->type)
die(_("SHA1 COLLISION FOUND WITH %s !"),
oid_to_hex(&entry->idx.oid));
unpack_data(entry, compare_objects, &data);
close_istream(data.st);
odb_read_stream_close(data.st);
free(data.buf);
return 0;
}
@ -1640,7 +1637,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
rename_tmp_packfile(&final_index_name, curr_index_name, &index_name,
hash, "idx", 1);
if (do_fsck_object)
if (do_fsck_object && startup_info->have_repository)
packfile_store_load_pack(the_repository->objects->packfiles,
final_index_name, 0);
@ -2110,8 +2107,23 @@ int cmd_index_pack(int argc,
else
close(input_fd);
if (do_fsck_object && fsck_finish(&fsck_options))
die(_("fsck error in pack objects"));
if (do_fsck_object) {
/*
* We cannot perform queued consistency checks when running
* outside of a repository because those require us to read
* from the object database, which is uninitialized.
*
* TODO: we may eventually set up an in-memory object database,
* which would allow us to perform these queued checks.
*/
if (!startup_info->have_repository &&
fsck_has_queued_checks(&fsck_options))
die(_("cannot perform queued object checks outside "
"of a repository"));
if (fsck_finish(&fsck_options))
die(_("fsck error in pack objects"));
}
free(opts.anomaly);
free(objects);

View File

@ -2,26 +2,32 @@
#include "bloom.h"
#include "builtin.h"
#include "commit-graph.h"
#include "commit-slab.h"
#include "commit.h"
#include "config.h"
#include "environment.h"
#include "diff.h"
#include "diffcore.h"
#include "environment.h"
#include "ewah/ewok.h"
#include "hashmap.h"
#include "hex.h"
#include "log-tree.h"
#include "object-name.h"
#include "object.h"
#include "parse-options.h"
#include "prio-queue.h"
#include "quote.h"
#include "repository.h"
#include "revision.h"
/* Remember to update object flag allocation in object.h */
#define PARENT1 (1u<<16) /* used instead of SEEN */
#define PARENT2 (1u<<17) /* used instead of BOTTOM, BOUNDARY */
struct last_modified_entry {
struct hashmap_entry hashent;
struct object_id oid;
struct bloom_key key;
size_t diff_idx;
const char path[FLEX_ARRAY];
};
@ -37,13 +43,45 @@ static int last_modified_entry_hashcmp(const void *unused UNUSED,
return strcmp(ent1->path, path ? path : ent2->path);
}
/*
* Hold a bitmap for each commit we're working with. In the bitmap, each bit
* represents a path in `lm->all_paths`. An active bit indicates the path still
* needs to be associated to a commit.
*/
define_commit_slab(active_paths_for_commit, struct bitmap *);
struct last_modified {
struct hashmap paths;
struct rev_info rev;
bool recursive;
bool show_trees;
const char **all_paths;
size_t all_paths_nr;
struct active_paths_for_commit active_paths;
/* 'scratch' to avoid allocating a bitmap every process_parent() */
struct bitmap *scratch;
};
static struct bitmap *active_paths_for(struct last_modified *lm, struct commit *c)
{
struct bitmap **bitmap = active_paths_for_commit_at(&lm->active_paths, c);
if (!*bitmap)
*bitmap = bitmap_word_alloc(lm->all_paths_nr / BITS_IN_EWORD + 1);
return *bitmap;
}
static void active_paths_free(struct last_modified *lm, struct commit *c)
{
struct bitmap **bitmap = active_paths_for_commit_at(&lm->active_paths, c);
if (*bitmap) {
bitmap_free(*bitmap);
*bitmap = NULL;
}
}
static void last_modified_release(struct last_modified *lm)
{
struct hashmap_iter iter;
@ -54,6 +92,8 @@ static void last_modified_release(struct last_modified *lm)
hashmap_clear_and_free(&lm->paths, struct last_modified_entry, hashent);
release_revisions(&lm->rev);
free(lm->all_paths);
}
struct last_modified_callback_data {
@ -146,7 +186,7 @@ static void mark_path(const char *path, const struct object_id *oid,
* Is it arriving at a version of interest, or is it from a side branch
* which did not contribute to the final state?
*/
if (!oideq(oid, &ent->oid))
if (oid && !oideq(oid, &ent->oid))
return;
last_modified_emit(data->lm, path, data->commit);
@ -196,7 +236,17 @@ static void last_modified_diff(struct diff_queue_struct *q,
}
}
static bool maybe_changed_path(struct last_modified *lm, struct commit *origin)
static void pass_to_parent(struct bitmap *c,
struct bitmap *p,
size_t pos)
{
bitmap_unset(c, pos);
bitmap_set(p, pos);
}
static bool maybe_changed_path(struct last_modified *lm,
struct commit *origin,
struct bitmap *active)
{
struct bloom_filter *filter;
struct last_modified_entry *ent;
@ -213,6 +263,9 @@ static bool maybe_changed_path(struct last_modified *lm, struct commit *origin)
return true;
hashmap_for_each_entry(&lm->paths, &iter, ent, hashent) {
if (active && !bitmap_get(active, ent->diff_idx))
continue;
if (bloom_filter_contains(filter, &ent->key,
lm->rev.bloom_filter_settings))
return true;
@ -220,42 +273,202 @@ static bool maybe_changed_path(struct last_modified *lm, struct commit *origin)
return false;
}
static void process_parent(struct last_modified *lm,
struct prio_queue *queue,
struct commit *c, struct bitmap *active_c,
struct commit *parent, int parent_i)
{
struct bitmap *active_p;
repo_parse_commit(lm->rev.repo, parent);
active_p = active_paths_for(lm, parent);
/*
* The first time entering this function for this commit (i.e. first parent)
* see if Bloom filters will tell us it's worth to do the diff.
*/
if (parent_i || maybe_changed_path(lm, c, active_c)) {
diff_tree_oid(&parent->object.oid,
&c->object.oid, "", &lm->rev.diffopt);
diffcore_std(&lm->rev.diffopt);
}
/*
* Test each path for TREESAME-ness against the parent. If a path is
* TREESAME, pass it on to this parent.
*
* First, collect all paths that are *not* TREESAME in 'scratch'.
* Then, pass paths that *are* TREESAME and active to the parent.
*/
for (int i = 0; i < diff_queued_diff.nr; i++) {
struct diff_filepair *fp = diff_queued_diff.queue[i];
const char *path = fp->two->path;
struct last_modified_entry *ent =
hashmap_get_entry_from_hash(&lm->paths, strhash(path), path,
struct last_modified_entry, hashent);
if (ent) {
size_t k = ent->diff_idx;
if (bitmap_get(active_c, k))
bitmap_set(lm->scratch, k);
}
}
for (size_t i = 0; i < lm->all_paths_nr; i++) {
if (bitmap_get(active_c, i) && !bitmap_get(lm->scratch, i))
pass_to_parent(active_c, active_p, i);
}
/*
* If parent has any active paths, put it on the queue (if not already).
*/
if (!bitmap_is_empty(active_p) && !(parent->object.flags & PARENT1)) {
parent->object.flags |= PARENT1;
prio_queue_put(queue, parent);
}
if (!(parent->object.flags & PARENT1))
active_paths_free(lm, parent);
MEMZERO_ARRAY(lm->scratch->words, lm->scratch->word_alloc);
diff_queue_clear(&diff_queued_diff);
}
static int last_modified_run(struct last_modified *lm)
{
int max_count, queue_popped = 0;
struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
struct prio_queue not_queue = { compare_commits_by_gen_then_commit_date };
struct commit_list *list;
struct last_modified_callback_data data = { .lm = lm };
lm->rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
lm->rev.diffopt.format_callback = last_modified_diff;
lm->rev.diffopt.format_callback_data = &data;
lm->rev.no_walk = 1;
prepare_revision_walk(&lm->rev);
while (hashmap_get_size(&lm->paths)) {
data.commit = get_revision(&lm->rev);
if (!data.commit)
BUG("paths remaining beyond boundary in last-modified");
max_count = lm->rev.max_count;
if (data.commit->object.flags & BOUNDARY) {
init_active_paths_for_commit(&lm->active_paths);
lm->scratch = bitmap_word_alloc(lm->all_paths_nr);
/*
* lm->rev.commits holds the set of boundary commits for our walk.
*
* Loop through each such commit, and place it in the appropriate queue.
*/
for (list = lm->rev.commits; list; list = list->next) {
struct commit *c = list->item;
if (c->object.flags & BOTTOM) {
prio_queue_put(&not_queue, c);
c->object.flags |= PARENT2;
} else if (!(c->object.flags & PARENT1)) {
/*
* If the commit is a starting point (and hasn't been
* seen yet), then initialize the set of interesting
* paths, too.
*/
struct bitmap *active;
prio_queue_put(&queue, c);
c->object.flags |= PARENT1;
active = active_paths_for(lm, c);
for (size_t i = 0; i < lm->all_paths_nr; i++)
bitmap_set(active, i);
}
}
while (queue.nr) {
int parent_i;
struct commit_list *p;
struct commit *c = prio_queue_get(&queue);
struct bitmap *active_c = active_paths_for(lm, c);
if ((0 <= max_count && max_count < ++queue_popped) ||
(c->object.flags & PARENT2)) {
/*
* Either a boundary commit, or we have already seen too
* many others. Either way, stop here.
*/
c->object.flags |= PARENT2 | BOUNDARY;
data.commit = c;
diff_tree_oid(lm->rev.repo->hash_algo->empty_tree,
&data.commit->object.oid, "",
&lm->rev.diffopt);
&c->object.oid,
"", &lm->rev.diffopt);
diff_flush(&lm->rev.diffopt);
break;
goto cleanup;
}
if (!maybe_changed_path(lm, data.commit))
continue;
/*
* Otherwise, make sure that 'c' isn't reachable from anything
* in the '--not' queue.
*/
repo_parse_commit(lm->rev.repo, c);
log_tree_commit(&lm->rev, data.commit);
while (not_queue.nr) {
struct commit_list *np;
struct commit *n = prio_queue_get(&not_queue);
repo_parse_commit(lm->rev.repo, n);
for (np = n->parents; np; np = np->next) {
if (!(np->item->object.flags & PARENT2)) {
prio_queue_put(&not_queue, np->item);
np->item->object.flags |= PARENT2;
}
}
if (commit_graph_generation(n) < commit_graph_generation(c))
break;
}
/*
* Look at each parent and pass on each path that's TREESAME
* with that parent. Stop early when no active paths remain.
*/
for (p = c->parents, parent_i = 0; p; p = p->next, parent_i++) {
process_parent(lm, &queue,
c, active_c,
p->item, parent_i);
if (bitmap_is_empty(active_c))
break;
}
/*
* Paths that remain active, or not TREESAME with any parent,
* were changed by 'c'.
*/
if (!bitmap_is_empty(active_c)) {
data.commit = c;
for (size_t i = 0; i < lm->all_paths_nr; i++) {
if (bitmap_get(active_c, i))
mark_path(lm->all_paths[i], NULL, &data);
}
}
cleanup:
active_paths_free(lm, c);
}
if (hashmap_get_size(&lm->paths))
BUG("paths remaining beyond boundary in last-modified");
clear_prio_queue(&not_queue);
clear_prio_queue(&queue);
clear_active_paths_for_commit(&lm->active_paths);
bitmap_free(lm->scratch);
return 0;
}
static int last_modified_init(struct last_modified *lm, struct repository *r,
const char *prefix, int argc, const char **argv)
{
struct hashmap_iter iter;
struct last_modified_entry *ent;
hashmap_init(&lm->paths, last_modified_entry_hashcmp, NULL, 0);
repo_init_revisions(r, &lm->rev, prefix);
@ -280,6 +493,13 @@ static int last_modified_init(struct last_modified *lm, struct repository *r,
if (populate_paths_from_revs(lm) < 0)
return error(_("unable to setup last-modified"));
CALLOC_ARRAY(lm->all_paths, hashmap_get_size(&lm->paths));
lm->all_paths_nr = 0;
hashmap_for_each_entry(&lm->paths, &iter, ent, hashent) {
ent->diff_idx = lm->all_paths_nr++;
lm->all_paths[ent->diff_idx] = ent->path;
}
return 0;
}
@ -305,7 +525,8 @@ int cmd_last_modified(int argc, const char **argv, const char *prefix,
argc = parse_options(argc, argv, prefix, last_modified_options,
last_modified_usage,
PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT);
PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT |
PARSE_OPT_KEEP_DASHDASH);
repo_config(repo, git_default_config, NULL);

View File

@ -16,6 +16,7 @@
#include "refs.h"
#include "object-name.h"
#include "odb.h"
#include "odb/streaming.h"
#include "pager.h"
#include "color.h"
#include "commit.h"
@ -35,7 +36,6 @@
#include "parse-options.h"
#include "line-log.h"
#include "branch.h"
#include "streaming.h"
#include "version.h"
#include "mailmap.h"
#include "progress.h"
@ -584,7 +584,7 @@ static int show_blob_object(const struct object_id *oid, struct rev_info *rev, c
fflush(rev->diffopt.file);
if (!rev->diffopt.flags.textconv_set_via_cmdline ||
!rev->diffopt.flags.allow_textconv)
return stream_blob_to_fd(1, oid, NULL, 0);
return odb_stream_blob_to_fd(the_repository->objects, 1, oid, NULL, 0);
if (get_oid_with_context(the_repository, obj_name,
GET_OID_RECORD_PATH,
@ -594,7 +594,7 @@ static int show_blob_object(const struct object_id *oid, struct rev_info *rev, c
!textconv_object(the_repository, obj_context.path,
obj_context.mode, &oidc, 1, &buf, &size)) {
object_context_release(&obj_context);
return stream_blob_to_fd(1, oid, NULL, 0);
return odb_stream_blob_to_fd(the_repository->objects, 1, oid, NULL, 0);
}
if (!buf)

View File

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

View File

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

View File

@ -22,7 +22,6 @@
#include "pack-objects.h"
#include "progress.h"
#include "refs.h"
#include "streaming.h"
#include "thread-utils.h"
#include "pack-bitmap.h"
#include "delta-islands.h"
@ -33,6 +32,7 @@
#include "packfile.h"
#include "object-file.h"
#include "odb.h"
#include "odb/streaming.h"
#include "replace-object.h"
#include "dir.h"
#include "midx.h"
@ -404,7 +404,7 @@ static unsigned long do_compress(void **pptr, unsigned long size)
return stream.total_out;
}
static unsigned long write_large_blob_data(struct git_istream *st, struct hashfile *f,
static unsigned long write_large_blob_data(struct odb_read_stream *st, struct hashfile *f,
const struct object_id *oid)
{
git_zstream stream;
@ -417,7 +417,7 @@ static unsigned long write_large_blob_data(struct git_istream *st, struct hashfi
for (;;) {
ssize_t readlen;
int zret = Z_OK;
readlen = read_istream(st, ibuf, sizeof(ibuf));
readlen = odb_read_stream_read(st, ibuf, sizeof(ibuf));
if (readlen == -1)
die(_("unable to read %s"), oid_to_hex(oid));
@ -513,17 +513,19 @@ static unsigned long write_no_reuse_object(struct hashfile *f, struct object_ent
unsigned hdrlen;
enum object_type type;
void *buf;
struct git_istream *st = NULL;
struct odb_read_stream *st = NULL;
const unsigned hashsz = the_hash_algo->rawsz;
if (!usable_delta) {
if (oe_type(entry) == OBJ_BLOB &&
oe_size_greater_than(&to_pack, entry,
repo_settings_get_big_file_threshold(the_repository)) &&
(st = open_istream(the_repository, &entry->idx.oid, &type,
&size, NULL)) != NULL)
(st = odb_read_stream_open(the_repository->objects, &entry->idx.oid,
NULL)) != NULL) {
buf = NULL;
else {
type = st->type;
size = st->size;
} else {
buf = odb_read_object(the_repository->objects,
&entry->idx.oid, &type,
&size);
@ -577,7 +579,7 @@ static unsigned long write_no_reuse_object(struct hashfile *f, struct object_ent
dheader[--pos] = 128 | (--ofs & 127);
if (limit && hdrlen + sizeof(dheader) - pos + datalen + hashsz >= limit) {
if (st)
close_istream(st);
odb_read_stream_close(st);
free(buf);
return 0;
}
@ -591,7 +593,7 @@ static unsigned long write_no_reuse_object(struct hashfile *f, struct object_ent
*/
if (limit && hdrlen + hashsz + datalen + hashsz >= limit) {
if (st)
close_istream(st);
odb_read_stream_close(st);
free(buf);
return 0;
}
@ -601,7 +603,7 @@ static unsigned long write_no_reuse_object(struct hashfile *f, struct object_ent
} else {
if (limit && hdrlen + datalen + hashsz >= limit) {
if (st)
close_istream(st);
odb_read_stream_close(st);
free(buf);
return 0;
}
@ -609,7 +611,7 @@ static unsigned long write_no_reuse_object(struct hashfile *f, struct object_ent
}
if (st) {
datalen = write_large_blob_data(st, f, &entry->idx.oid);
close_istream(st);
odb_read_stream_close(st);
} else {
hashwrite(f, buf, datalen);
free(buf);
@ -831,15 +833,14 @@ static enum write_one_status write_one(struct hashfile *f,
return WRITE_ONE_WRITTEN;
}
static int mark_tagged(const char *path UNUSED, const char *referent UNUSED, const struct object_id *oid,
int flag UNUSED, void *cb_data UNUSED)
static int mark_tagged(const struct reference *ref, void *cb_data UNUSED)
{
struct object_id peeled;
struct object_entry *entry = packlist_find(&to_pack, oid);
struct object_entry *entry = packlist_find(&to_pack, ref->oid);
if (entry)
entry->tagged = 1;
if (!peel_iterated_oid(the_repository, oid, &peeled)) {
if (!reference_get_peeled_oid(the_repository, ref, &peeled)) {
entry = packlist_find(&to_pack, &peeled);
if (entry)
entry->tagged = 1;
@ -1706,8 +1707,8 @@ static int want_object_in_pack_mtime(const struct object_id *oid,
uint32_t found_mtime)
{
int want;
struct packfile_list_entry *e;
struct odb_source *source;
struct list_head *pos;
if (!exclude && local) {
/*
@ -1716,7 +1717,7 @@ static int want_object_in_pack_mtime(const struct object_id *oid,
*/
struct odb_source *source = the_repository->objects->sources->next;
for (; source; source = source->next)
if (has_loose_object(source, oid))
if (odb_source_loose_has_object(source, oid))
return 0;
}
@ -1748,12 +1749,11 @@ static int want_object_in_pack_mtime(const struct object_id *oid,
}
}
list_for_each(pos, packfile_store_get_packs_mru(the_repository->objects->packfiles)) {
struct packed_git *p = list_entry(pos, struct packed_git, mru);
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)
list_move(&p->mru,
packfile_store_get_packs_mru(the_repository->objects->packfiles));
packfile_list_prepend(&the_repository->objects->packfiles->packs, p);
if (want != -1)
return want;
}
@ -3293,7 +3293,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));
@ -3306,13 +3306,13 @@ static void add_tag_chain(const struct object_id *oid)
}
}
static int add_ref_tag(const char *tag UNUSED, const char *referent UNUSED, const struct object_id *oid,
int flag UNUSED, void *cb_data UNUSED)
static int add_ref_tag(const struct reference *ref, void *cb_data UNUSED)
{
struct object_id peeled;
if (!peel_iterated_oid(the_repository, oid, &peeled) && obj_is_packed(&peeled))
add_tag_chain(oid);
if (!reference_get_peeled_oid(the_repository, ref, &peeled) &&
obj_is_packed(&peeled))
add_tag_chain(ref->oid);
return 0;
}
@ -3978,7 +3978,7 @@ static void add_cruft_object_entry(const struct object_id *oid, enum object_type
int found = 0;
for (; !found && source; source = source->next)
if (has_loose_object(source, oid))
if (odb_source_loose_has_object(source, oid))
found = 1;
/*
@ -4389,27 +4389,27 @@ static void add_unreachable_loose_objects(struct rev_info *revs)
static int has_sha1_pack_kept_or_nonlocal(const struct object_id *oid)
{
struct packfile_store *packs = the_repository->objects->packfiles;
static struct packed_git *last_found = (void *)1;
static struct packed_git *last_found = NULL;
struct packed_git *p;
p = (last_found != (void *)1) ? last_found :
packfile_store_get_packs(packs);
if (last_found && find_pack_entry_one(oid, last_found))
return 1;
while (p) {
if ((!p->pack_local || p->pack_keep ||
p->pack_keep_in_core) &&
find_pack_entry_one(oid, p)) {
repo_for_each_pack(the_repository, p) {
/*
* We have already checked `last_found`, so there is no need to
* re-check here.
*/
if (p == last_found)
continue;
if ((!p->pack_local || p->pack_keep || p->pack_keep_in_core) &&
find_pack_entry_one(oid, p)) {
last_found = p;
return 1;
}
if (p == last_found)
p = packfile_store_get_packs(packs);
else
p = p->next;
if (p == last_found)
p = p->next;
}
return 0;
}
@ -4528,19 +4528,16 @@ static void record_recent_commit(struct commit *commit, void *data UNUSED)
oid_array_append(&recent_objects, &commit->object.oid);
}
static int mark_bitmap_preferred_tip(const char *refname,
const char *referent UNUSED,
const struct object_id *oid,
int flags UNUSED,
void *data UNUSED)
static int mark_bitmap_preferred_tip(const struct reference *ref, void *data UNUSED)
{
const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
struct object *object;
if (!peel_iterated_oid(the_repository, oid, &peeled))
oid = &peeled;
if (!reference_get_peeled_oid(the_repository, ref, &peeled))
maybe_peeled = &peeled;
object = parse_object_or_die(the_repository, oid, refname);
object = parse_object_or_die(the_repository, maybe_peeled, ref->name);
if (object->type == OBJ_COMMIT)
object->flags |= NEEDS_BITMAP;

View File

@ -119,148 +119,6 @@ static int opt_show_forced_updates = -1;
static const char *set_upstream;
static struct strvec opt_fetch = STRVEC_INIT;
static struct option pull_options[] = {
/* Shared options */
OPT__VERBOSITY(&opt_verbosity),
OPT_PASSTHRU(0, "progress", &opt_progress, NULL,
N_("force progress reporting"),
PARSE_OPT_NOARG),
OPT_CALLBACK_F(0, "recurse-submodules",
&recurse_submodules_cli, N_("on-demand"),
N_("control for recursive fetching of submodules"),
PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules),
/* Options passed to git-merge or git-rebase */
OPT_GROUP(N_("Options related to merging")),
OPT_CALLBACK_F('r', "rebase", &opt_rebase,
"(false|true|merges|interactive)",
N_("incorporate changes by rebasing rather than merging"),
PARSE_OPT_OPTARG, parse_opt_rebase),
OPT_PASSTHRU('n', NULL, &opt_diffstat, NULL,
N_("do not show a diffstat at the end of the merge"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG),
OPT_PASSTHRU(0, "stat", &opt_diffstat, NULL,
N_("show a diffstat at the end of the merge"),
PARSE_OPT_NOARG),
OPT_PASSTHRU(0, "summary", &opt_diffstat, NULL,
N_("(synonym to --stat)"),
PARSE_OPT_NOARG | PARSE_OPT_HIDDEN),
OPT_PASSTHRU(0, "compact-summary", &opt_diffstat, NULL,
N_("show a compact-summary at the end of the merge"),
PARSE_OPT_NOARG),
OPT_PASSTHRU(0, "log", &opt_log, N_("n"),
N_("add (at most <n>) entries from shortlog to merge commit message"),
PARSE_OPT_OPTARG),
OPT_PASSTHRU(0, "signoff", &opt_signoff, NULL,
N_("add a Signed-off-by trailer"),
PARSE_OPT_OPTARG),
OPT_PASSTHRU(0, "squash", &opt_squash, NULL,
N_("create a single commit instead of doing a merge"),
PARSE_OPT_NOARG),
OPT_PASSTHRU(0, "commit", &opt_commit, NULL,
N_("perform a commit if the merge succeeds (default)"),
PARSE_OPT_NOARG),
OPT_PASSTHRU(0, "edit", &opt_edit, NULL,
N_("edit message before committing"),
PARSE_OPT_NOARG),
OPT_CLEANUP(&cleanup_arg),
OPT_PASSTHRU(0, "ff", &opt_ff, NULL,
N_("allow fast-forward"),
PARSE_OPT_NOARG),
OPT_PASSTHRU(0, "ff-only", &opt_ff, NULL,
N_("abort if fast-forward is not possible"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG),
OPT_PASSTHRU(0, "verify", &opt_verify, NULL,
N_("control use of pre-merge-commit and commit-msg hooks"),
PARSE_OPT_NOARG),
OPT_PASSTHRU(0, "verify-signatures", &opt_verify_signatures, NULL,
N_("verify that the named commit has a valid GPG signature"),
PARSE_OPT_NOARG),
OPT_BOOL(0, "autostash", &opt_autostash,
N_("automatically stash/stash pop before and after")),
OPT_PASSTHRU_ARGV('s', "strategy", &opt_strategies, N_("strategy"),
N_("merge strategy to use"),
0),
OPT_PASSTHRU_ARGV('X', "strategy-option", &opt_strategy_opts,
N_("option=value"),
N_("option for selected merge strategy"),
0),
OPT_PASSTHRU('S', "gpg-sign", &opt_gpg_sign, N_("key-id"),
N_("GPG sign commit"),
PARSE_OPT_OPTARG),
OPT_SET_INT(0, "allow-unrelated-histories",
&opt_allow_unrelated_histories,
N_("allow merging unrelated histories"), 1),
/* Options passed to git-fetch */
OPT_GROUP(N_("Options related to fetching")),
OPT_PASSTHRU(0, "all", &opt_all, NULL,
N_("fetch from all remotes"),
PARSE_OPT_NOARG),
OPT_PASSTHRU('a', "append", &opt_append, NULL,
N_("append to .git/FETCH_HEAD instead of overwriting"),
PARSE_OPT_NOARG),
OPT_PASSTHRU(0, "upload-pack", &opt_upload_pack, N_("path"),
N_("path to upload pack on remote end"),
0),
OPT__FORCE(&opt_force, N_("force overwrite of local branch"), 0),
OPT_PASSTHRU('t', "tags", &opt_tags, NULL,
N_("fetch all tags and associated objects"),
PARSE_OPT_NOARG),
OPT_PASSTHRU('p', "prune", &opt_prune, NULL,
N_("prune remote-tracking branches no longer on remote"),
PARSE_OPT_NOARG),
OPT_PASSTHRU('j', "jobs", &max_children, N_("n"),
N_("number of submodules pulled in parallel"),
PARSE_OPT_OPTARG),
OPT_BOOL(0, "dry-run", &opt_dry_run,
N_("dry run")),
OPT_PASSTHRU('k', "keep", &opt_keep, NULL,
N_("keep downloaded pack"),
PARSE_OPT_NOARG),
OPT_PASSTHRU(0, "depth", &opt_depth, N_("depth"),
N_("deepen history of shallow clone"),
0),
OPT_PASSTHRU_ARGV(0, "shallow-since", &opt_fetch, N_("time"),
N_("deepen history of shallow repository based on time"),
0),
OPT_PASSTHRU_ARGV(0, "shallow-exclude", &opt_fetch, N_("ref"),
N_("deepen history of shallow clone, excluding ref"),
0),
OPT_PASSTHRU_ARGV(0, "deepen", &opt_fetch, N_("n"),
N_("deepen history of shallow clone"),
0),
OPT_PASSTHRU(0, "unshallow", &opt_unshallow, NULL,
N_("convert to a complete repository"),
PARSE_OPT_NONEG | PARSE_OPT_NOARG),
OPT_PASSTHRU(0, "update-shallow", &opt_update_shallow, NULL,
N_("accept refs that update .git/shallow"),
PARSE_OPT_NOARG),
OPT_PASSTHRU(0, "refmap", &opt_refmap, N_("refmap"),
N_("specify fetch refmap"),
PARSE_OPT_NONEG),
OPT_PASSTHRU_ARGV('o', "server-option", &opt_fetch,
N_("server-specific"),
N_("option to transmit"),
0),
OPT_PASSTHRU('4', "ipv4", &opt_ipv4, NULL,
N_("use IPv4 addresses only"),
PARSE_OPT_NOARG),
OPT_PASSTHRU('6', "ipv6", &opt_ipv6, NULL,
N_("use IPv6 addresses only"),
PARSE_OPT_NOARG),
OPT_PASSTHRU_ARGV(0, "negotiation-tip", &opt_fetch, N_("revision"),
N_("report that we have only objects reachable from this object"),
0),
OPT_BOOL(0, "show-forced-updates", &opt_show_forced_updates,
N_("check for forced-updates on all updated branches")),
OPT_PASSTHRU(0, "set-upstream", &set_upstream, NULL,
N_("set upstream for git pull/fetch"),
PARSE_OPT_NOARG),
OPT_END()
};
/**
* Pushes "-q" or "-v" switches into arr to match the opt_verbosity level.
*/
@ -1008,6 +866,147 @@ int cmd_pull(int argc,
int can_ff;
int divergent;
int ret;
static struct option pull_options[] = {
/* Shared options */
OPT__VERBOSITY(&opt_verbosity),
OPT_PASSTHRU(0, "progress", &opt_progress, NULL,
N_("force progress reporting"),
PARSE_OPT_NOARG),
OPT_CALLBACK_F(0, "recurse-submodules",
&recurse_submodules_cli, N_("on-demand"),
N_("control for recursive fetching of submodules"),
PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules),
/* Options passed to git-merge or git-rebase */
OPT_GROUP(N_("Options related to merging")),
OPT_CALLBACK_F('r', "rebase", &opt_rebase,
"(false|true|merges|interactive)",
N_("incorporate changes by rebasing rather than merging"),
PARSE_OPT_OPTARG, parse_opt_rebase),
OPT_PASSTHRU('n', NULL, &opt_diffstat, NULL,
N_("do not show a diffstat at the end of the merge"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG),
OPT_PASSTHRU(0, "stat", &opt_diffstat, NULL,
N_("show a diffstat at the end of the merge"),
PARSE_OPT_NOARG),
OPT_PASSTHRU(0, "summary", &opt_diffstat, NULL,
N_("(synonym to --stat)"),
PARSE_OPT_NOARG | PARSE_OPT_HIDDEN),
OPT_PASSTHRU(0, "compact-summary", &opt_diffstat, NULL,
N_("show a compact-summary at the end of the merge"),
PARSE_OPT_NOARG),
OPT_PASSTHRU(0, "log", &opt_log, N_("n"),
N_("add (at most <n>) entries from shortlog to merge commit message"),
PARSE_OPT_OPTARG),
OPT_PASSTHRU(0, "signoff", &opt_signoff, NULL,
N_("add a Signed-off-by trailer"),
PARSE_OPT_OPTARG),
OPT_PASSTHRU(0, "squash", &opt_squash, NULL,
N_("create a single commit instead of doing a merge"),
PARSE_OPT_NOARG),
OPT_PASSTHRU(0, "commit", &opt_commit, NULL,
N_("perform a commit if the merge succeeds (default)"),
PARSE_OPT_NOARG),
OPT_PASSTHRU(0, "edit", &opt_edit, NULL,
N_("edit message before committing"),
PARSE_OPT_NOARG),
OPT_CLEANUP(&cleanup_arg),
OPT_PASSTHRU(0, "ff", &opt_ff, NULL,
N_("allow fast-forward"),
PARSE_OPT_NOARG),
OPT_PASSTHRU(0, "ff-only", &opt_ff, NULL,
N_("abort if fast-forward is not possible"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG),
OPT_PASSTHRU(0, "verify", &opt_verify, NULL,
N_("control use of pre-merge-commit and commit-msg hooks"),
PARSE_OPT_NOARG),
OPT_PASSTHRU(0, "verify-signatures", &opt_verify_signatures, NULL,
N_("verify that the named commit has a valid GPG signature"),
PARSE_OPT_NOARG),
OPT_BOOL(0, "autostash", &opt_autostash,
N_("automatically stash/stash pop before and after")),
OPT_PASSTHRU_ARGV('s', "strategy", &opt_strategies, N_("strategy"),
N_("merge strategy to use"),
0),
OPT_PASSTHRU_ARGV('X', "strategy-option", &opt_strategy_opts,
N_("option=value"),
N_("option for selected merge strategy"),
0),
OPT_PASSTHRU('S', "gpg-sign", &opt_gpg_sign, N_("key-id"),
N_("GPG sign commit"),
PARSE_OPT_OPTARG),
OPT_SET_INT(0, "allow-unrelated-histories",
&opt_allow_unrelated_histories,
N_("allow merging unrelated histories"), 1),
/* Options passed to git-fetch */
OPT_GROUP(N_("Options related to fetching")),
OPT_PASSTHRU(0, "all", &opt_all, NULL,
N_("fetch from all remotes"),
PARSE_OPT_NOARG),
OPT_PASSTHRU('a', "append", &opt_append, NULL,
N_("append to .git/FETCH_HEAD instead of overwriting"),
PARSE_OPT_NOARG),
OPT_PASSTHRU(0, "upload-pack", &opt_upload_pack, N_("path"),
N_("path to upload pack on remote end"),
0),
OPT__FORCE(&opt_force, N_("force overwrite of local branch"), 0),
OPT_PASSTHRU('t', "tags", &opt_tags, NULL,
N_("fetch all tags and associated objects"),
PARSE_OPT_NOARG),
OPT_PASSTHRU('p', "prune", &opt_prune, NULL,
N_("prune remote-tracking branches no longer on remote"),
PARSE_OPT_NOARG),
OPT_PASSTHRU('j', "jobs", &max_children, N_("n"),
N_("number of submodules pulled in parallel"),
PARSE_OPT_OPTARG),
OPT_BOOL(0, "dry-run", &opt_dry_run,
N_("dry run")),
OPT_PASSTHRU('k', "keep", &opt_keep, NULL,
N_("keep downloaded pack"),
PARSE_OPT_NOARG),
OPT_PASSTHRU(0, "depth", &opt_depth, N_("depth"),
N_("deepen history of shallow clone"),
0),
OPT_PASSTHRU_ARGV(0, "shallow-since", &opt_fetch, N_("time"),
N_("deepen history of shallow repository based on time"),
0),
OPT_PASSTHRU_ARGV(0, "shallow-exclude", &opt_fetch, N_("ref"),
N_("deepen history of shallow clone, excluding ref"),
0),
OPT_PASSTHRU_ARGV(0, "deepen", &opt_fetch, N_("n"),
N_("deepen history of shallow clone"),
0),
OPT_PASSTHRU(0, "unshallow", &opt_unshallow, NULL,
N_("convert to a complete repository"),
PARSE_OPT_NONEG | PARSE_OPT_NOARG),
OPT_PASSTHRU(0, "update-shallow", &opt_update_shallow, NULL,
N_("accept refs that update .git/shallow"),
PARSE_OPT_NOARG),
OPT_PASSTHRU(0, "refmap", &opt_refmap, N_("refmap"),
N_("specify fetch refmap"),
PARSE_OPT_NONEG),
OPT_PASSTHRU_ARGV('o', "server-option", &opt_fetch,
N_("server-specific"),
N_("option to transmit"),
0),
OPT_PASSTHRU('4', "ipv4", &opt_ipv4, NULL,
N_("use IPv4 addresses only"),
PARSE_OPT_NOARG),
OPT_PASSTHRU('6', "ipv6", &opt_ipv6, NULL,
N_("use IPv6 addresses only"),
PARSE_OPT_NOARG),
OPT_PASSTHRU_ARGV(0, "negotiation-tip", &opt_fetch, N_("revision"),
N_("report that we have only objects reachable from this object"),
0),
OPT_BOOL(0, "show-forced-updates", &opt_show_forced_updates,
N_("check for forced-updates on all updated branches")),
OPT_PASSTHRU(0, "set-upstream", &set_upstream, NULL,
N_("set upstream for git pull/fetch"),
PARSE_OPT_NOARG),
OPT_END()
};
if (!getenv("GIT_REFLOG_ACTION"))
set_reflog_message(argc, argv);

View File

@ -34,7 +34,6 @@
#include "object-file.h"
#include "object-name.h"
#include "odb.h"
#include "path.h"
#include "protocol.h"
#include "commit-reach.h"
#include "server-info.h"
@ -42,6 +41,7 @@
#include "trace2.h"
#include "worktree.h"
#include "shallow.h"
#include "setup.h"
#include "parse-options.h"
static const char * const receive_pack_usage[] = {
@ -177,8 +177,9 @@ static int receive_pack_config(const char *var, const char *value,
if (git_config_pathname(&path, var, value))
return -1;
strbuf_addf(&fsck_msg_types, "%cskiplist=%s",
fsck_msg_types.len ? ',' : '=', path);
if (path)
strbuf_addf(&fsck_msg_types, "%cskiplist=%s",
fsck_msg_types.len ? ',' : '=', path);
free(path);
return 0;
}
@ -305,13 +306,12 @@ static void show_ref(const char *path, const struct object_id *oid)
}
}
static int show_ref_cb(const char *path_full, const char *referent UNUSED, const struct object_id *oid,
int flag UNUSED, void *data)
static int show_ref_cb(const struct reference *ref, void *data)
{
struct oidset *seen = data;
const char *path = strip_namespace(path_full);
const char *path = strip_namespace(ref->name);
if (ref_is_hidden(path, path_full, &hidden_refs))
if (ref_is_hidden(path, ref->name, &hidden_refs))
return 0;
/*
@ -320,13 +320,13 @@ static int show_ref_cb(const char *path_full, const char *referent UNUSED, const
* transfer but will otherwise ignore them.
*/
if (!path) {
if (oidset_insert(seen, oid))
if (oidset_insert(seen, ref->oid))
return 0;
path = ".have";
} else {
oidset_insert(seen, oid);
oidset_insert(seen, ref->oid);
}
show_ref(path, oid);
show_ref(path, ref->oid);
return 0;
}
@ -749,7 +749,7 @@ static int check_cert_push_options(const struct string_list *push_options)
return retval;
}
static void prepare_push_cert_sha1(struct child_process *proc)
static void prepare_push_cert_sha1(struct run_hooks_opt *opt)
{
static int already_done;
@ -775,23 +775,23 @@ static void prepare_push_cert_sha1(struct child_process *proc)
nonce_status = check_nonce(sigcheck.payload);
}
if (!is_null_oid(&push_cert_oid)) {
strvec_pushf(&proc->env, "GIT_PUSH_CERT=%s",
strvec_pushf(&opt->env, "GIT_PUSH_CERT=%s",
oid_to_hex(&push_cert_oid));
strvec_pushf(&proc->env, "GIT_PUSH_CERT_SIGNER=%s",
strvec_pushf(&opt->env, "GIT_PUSH_CERT_SIGNER=%s",
sigcheck.signer ? sigcheck.signer : "");
strvec_pushf(&proc->env, "GIT_PUSH_CERT_KEY=%s",
strvec_pushf(&opt->env, "GIT_PUSH_CERT_KEY=%s",
sigcheck.key ? sigcheck.key : "");
strvec_pushf(&proc->env, "GIT_PUSH_CERT_STATUS=%c",
strvec_pushf(&opt->env, "GIT_PUSH_CERT_STATUS=%c",
sigcheck.result);
if (push_cert_nonce) {
strvec_pushf(&proc->env,
strvec_pushf(&opt->env,
"GIT_PUSH_CERT_NONCE=%s",
push_cert_nonce);
strvec_pushf(&proc->env,
strvec_pushf(&opt->env,
"GIT_PUSH_CERT_NONCE_STATUS=%s",
nonce_status);
if (nonce_status == NONCE_SLOP)
strvec_pushf(&proc->env,
strvec_pushf(&opt->env,
"GIT_PUSH_CERT_NONCE_SLOP=%ld",
nonce_stamp_slop);
}
@ -803,119 +803,74 @@ struct receive_hook_feed_state {
struct ref_push_report *report;
int skip_broken;
struct strbuf buf;
const struct string_list *push_options;
};
typedef int (*feed_fn)(void *, const char **, size_t *);
static int run_and_feed_hook(const char *hook_name, feed_fn feed,
struct receive_hook_feed_state *feed_state)
static int feed_receive_hook_cb(int hook_stdin_fd, void *pp_cb UNUSED, void *pp_task_cb)
{
struct child_process proc = CHILD_PROCESS_INIT;
struct async muxer;
int code;
const char *hook_path = find_hook(the_repository, hook_name);
struct receive_hook_feed_state *state = pp_task_cb;
struct command *cmd = state->cmd;
unsigned int lines_batch_size = 500;
if (!hook_path)
return 0;
strbuf_reset(&state->buf);
strvec_push(&proc.args, hook_path);
proc.in = -1;
proc.stdout_to_stderr = 1;
proc.trace2_hook_name = hook_name;
/* batch lines to avoid going through run-command's poll loop for each line */
for (unsigned int i = 0; i < lines_batch_size; i++) {
while (cmd &&
state->skip_broken && (cmd->error_string || cmd->did_not_exist))
cmd = cmd->next;
if (feed_state->push_options) {
size_t i;
for (i = 0; i < feed_state->push_options->nr; i++)
strvec_pushf(&proc.env,
"GIT_PUSH_OPTION_%"PRIuMAX"=%s",
(uintmax_t)i,
feed_state->push_options->items[i].string);
strvec_pushf(&proc.env, "GIT_PUSH_OPTION_COUNT=%"PRIuMAX"",
(uintmax_t)feed_state->push_options->nr);
} else
strvec_pushf(&proc.env, "GIT_PUSH_OPTION_COUNT");
if (!cmd)
break; /* no more commands left */
if (tmp_objdir)
strvec_pushv(&proc.env, tmp_objdir_env(tmp_objdir));
if (!state->report)
state->report = cmd->report;
if (use_sideband) {
memset(&muxer, 0, sizeof(muxer));
muxer.proc = copy_to_sideband;
muxer.in = -1;
code = start_async(&muxer);
if (code)
return code;
proc.err = muxer.in;
if (state->report) {
struct object_id *old_oid;
struct object_id *new_oid;
const char *ref_name;
old_oid = state->report->old_oid ? state->report->old_oid : &cmd->old_oid;
new_oid = state->report->new_oid ? state->report->new_oid : &cmd->new_oid;
ref_name = state->report->ref_name ? state->report->ref_name : cmd->ref_name;
strbuf_addf(&state->buf, "%s %s %s\n",
oid_to_hex(old_oid), oid_to_hex(new_oid),
ref_name);
state->report = state->report->next;
if (!state->report)
cmd = cmd->next;
} else {
strbuf_addf(&state->buf, "%s %s %s\n",
oid_to_hex(&cmd->old_oid), oid_to_hex(&cmd->new_oid),
cmd->ref_name);
cmd = cmd->next;
}
}
prepare_push_cert_sha1(&proc);
state->cmd = cmd;
code = start_command(&proc);
if (code) {
if (use_sideband)
finish_async(&muxer);
return code;
if (state->buf.len > 0) {
int ret = write_in_full(hook_stdin_fd, state->buf.buf, state->buf.len);
if (ret < 0) {
if (errno == EPIPE)
return 1; /* child closed pipe */
return ret;
}
}
sigchain_push(SIGPIPE, SIG_IGN);
while (1) {
const char *buf;
size_t n;
if (feed(feed_state, &buf, &n))
break;
if (write_in_full(proc.in, buf, n) < 0)
break;
}
close(proc.in);
if (use_sideband)
finish_async(&muxer);
sigchain_pop(SIGPIPE);
return finish_command(&proc);
return state->cmd ? 0 : 1; /* 0 = more to come, 1 = EOF */
}
static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep)
static void hook_output_to_sideband(struct strbuf *output, void *cb_data UNUSED)
{
struct receive_hook_feed_state *state = state_;
struct command *cmd = state->cmd;
if (!output)
BUG("output must be non-NULL");
while (cmd &&
state->skip_broken && (cmd->error_string || cmd->did_not_exist))
cmd = cmd->next;
if (!cmd)
return -1; /* EOF */
if (!bufp)
return 0; /* OK, can feed something. */
strbuf_reset(&state->buf);
if (!state->report)
state->report = cmd->report;
if (state->report) {
struct object_id *old_oid;
struct object_id *new_oid;
const char *ref_name;
old_oid = state->report->old_oid ? state->report->old_oid : &cmd->old_oid;
new_oid = state->report->new_oid ? state->report->new_oid : &cmd->new_oid;
ref_name = state->report->ref_name ? state->report->ref_name : cmd->ref_name;
strbuf_addf(&state->buf, "%s %s %s\n",
oid_to_hex(old_oid), oid_to_hex(new_oid),
ref_name);
state->report = state->report->next;
if (!state->report)
state->cmd = cmd->next;
} else {
strbuf_addf(&state->buf, "%s %s %s\n",
oid_to_hex(&cmd->old_oid), oid_to_hex(&cmd->new_oid),
cmd->ref_name);
state->cmd = cmd->next;
}
if (bufp) {
*bufp = state->buf.buf;
*sizep = state->buf.len;
}
return 0;
/* buffer might be empty for keepalives */
if (output->len)
send_sideband(1, 2, output->buf, output->len, use_sideband);
}
static int run_receive_hook(struct command *commands,
@ -923,47 +878,65 @@ static int run_receive_hook(struct command *commands,
int skip_broken,
const struct string_list *push_options)
{
struct receive_hook_feed_state state;
int status;
struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
struct command *iter = commands;
struct receive_hook_feed_state feed_state;
int ret;
strbuf_init(&state.buf, 0);
state.cmd = commands;
state.skip_broken = skip_broken;
state.report = NULL;
if (feed_receive_hook(&state, NULL, NULL))
/* if there are no valid commands, don't invoke the hook at all. */
while (iter && skip_broken && (iter->error_string || iter->did_not_exist))
iter = iter->next;
if (!iter)
return 0;
state.cmd = commands;
state.push_options = push_options;
status = run_and_feed_hook(hook_name, feed_receive_hook, &state);
strbuf_release(&state.buf);
return status;
if (push_options) {
for (int i = 0; i < push_options->nr; i++)
strvec_pushf(&opt.env, "GIT_PUSH_OPTION_%d=%s", i,
push_options->items[i].string);
strvec_pushf(&opt.env, "GIT_PUSH_OPTION_COUNT=%"PRIuMAX"",
(uintmax_t)push_options->nr);
} else {
strvec_push(&opt.env, "GIT_PUSH_OPTION_COUNT");
}
if (tmp_objdir)
strvec_pushv(&opt.env, tmp_objdir_env(tmp_objdir));
prepare_push_cert_sha1(&opt);
/* set up sideband printer */
if (use_sideband)
opt.consume_output = hook_output_to_sideband;
/* set up stdin callback */
feed_state.cmd = commands;
feed_state.skip_broken = skip_broken;
feed_state.report = NULL;
strbuf_init(&feed_state.buf, 0);
opt.feed_pipe_cb_data = &feed_state;
opt.feed_pipe = feed_receive_hook_cb;
ret = run_hooks_opt(the_repository, hook_name, &opt);
strbuf_release(&feed_state.buf);
return ret;
}
static int run_update_hook(struct command *cmd)
{
struct child_process proc = CHILD_PROCESS_INIT;
int code;
const char *hook_path = find_hook(the_repository, "update");
struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
if (!hook_path)
return 0;
strvec_pushl(&opt.args,
cmd->ref_name,
oid_to_hex(&cmd->old_oid),
oid_to_hex(&cmd->new_oid),
NULL);
strvec_push(&proc.args, hook_path);
strvec_push(&proc.args, cmd->ref_name);
strvec_push(&proc.args, oid_to_hex(&cmd->old_oid));
strvec_push(&proc.args, oid_to_hex(&cmd->new_oid));
proc.no_stdin = 1;
proc.stdout_to_stderr = 1;
proc.err = use_sideband ? -1 : 0;
proc.trace2_hook_name = "update";
code = start_command(&proc);
if (code)
return code;
if (use_sideband)
copy_to_sideband(proc.err, -1, NULL);
return finish_command(&proc);
opt.consume_output = hook_output_to_sideband;
return run_hooks_opt(the_repository, "update", &opt);
}
static struct command *find_command_by_refname(struct command *list,
@ -1640,33 +1613,20 @@ out:
static void run_update_post_hook(struct command *commands)
{
struct command *cmd;
struct child_process proc = CHILD_PROCESS_INIT;
const char *hook;
hook = find_hook(the_repository, "post-update");
if (!hook)
return;
struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
for (cmd = commands; cmd; cmd = cmd->next) {
if (cmd->error_string || cmd->did_not_exist)
continue;
if (!proc.args.nr)
strvec_push(&proc.args, hook);
strvec_push(&proc.args, cmd->ref_name);
strvec_push(&opt.args, cmd->ref_name);
}
if (!proc.args.nr)
if (!opt.args.nr)
return;
proc.no_stdin = 1;
proc.stdout_to_stderr = 1;
proc.err = use_sideband ? -1 : 0;
proc.trace2_hook_name = "post-update";
if (use_sideband)
opt.consume_output = hook_output_to_sideband;
if (!start_command(&proc)) {
if (use_sideband)
copy_to_sideband(proc.err, -1, NULL);
finish_command(&proc);
}
run_hooks_opt(the_repository, "post-update", &opt);
}
static void check_aliased_update_internal(struct command *cmd,

View File

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

View File

@ -488,7 +488,7 @@ int cmd_repack(int argc,
string_list_sort(&names);
close_object_store(repo->objects);
odb_close(repo->objects);
/*
* Ok we have prepared all new packfiles.

View File

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

View File

@ -8,6 +8,7 @@
#include "git-compat-util.h"
#include "builtin.h"
#include "config.h"
#include "environment.h"
#include "hex.h"
#include "lockfile.h"
@ -20,6 +21,11 @@
#include <oidset.h>
#include <tree.h>
enum ref_action_mode {
REF_ACTION_UPDATE,
REF_ACTION_PRINT,
};
static const char *short_commit_name(struct repository *repo,
struct commit *commit)
{
@ -67,7 +73,7 @@ static struct commit *create_commit(struct repository *repo,
const char *message = repo_logmsg_reencode(repo, based_on,
NULL, out_enc);
const char *orig_message = NULL;
const char *exclude_gpgsig[] = { "gpgsig", NULL };
const char *exclude_gpgsig[] = { "gpgsig", "gpgsig-sha256", NULL };
commit_list_insert(parent, &parents);
extra = read_commit_extra_headers(based_on, exclude_gpgsig);
@ -284,6 +290,54 @@ static struct commit *pick_regular_commit(struct repository *repo,
return create_commit(repo, result->tree, pickme, replayed_base);
}
static enum ref_action_mode parse_ref_action_mode(const char *ref_action, const char *source)
{
if (!ref_action || !strcmp(ref_action, "update"))
return REF_ACTION_UPDATE;
if (!strcmp(ref_action, "print"))
return REF_ACTION_PRINT;
die(_("invalid %s value: '%s'"), source, ref_action);
}
static enum ref_action_mode get_ref_action_mode(struct repository *repo, const char *ref_action)
{
const char *config_value = NULL;
/* Command line option takes precedence */
if (ref_action)
return parse_ref_action_mode(ref_action, "--ref-action");
/* Check config value */
if (!repo_config_get_string_tmp(repo, "replay.refAction", &config_value))
return parse_ref_action_mode(config_value, "replay.refAction");
/* Default to update mode */
return REF_ACTION_UPDATE;
}
static int handle_ref_update(enum ref_action_mode mode,
struct ref_transaction *transaction,
const char *refname,
const struct object_id *new_oid,
const struct object_id *old_oid,
const char *reflog_msg,
struct strbuf *err)
{
switch (mode) {
case REF_ACTION_PRINT:
printf("update %s %s %s\n",
refname,
oid_to_hex(new_oid),
oid_to_hex(old_oid));
return 0;
case REF_ACTION_UPDATE:
return ref_transaction_update(transaction, refname, new_oid, old_oid,
NULL, NULL, 0, reflog_msg, err);
default:
BUG("unknown ref_action_mode %d", mode);
}
}
int cmd_replay(int argc,
const char **argv,
const char *prefix,
@ -294,6 +348,8 @@ int cmd_replay(int argc,
struct commit *onto = NULL;
const char *onto_name = NULL;
int contained = 0;
const char *ref_action = NULL;
enum ref_action_mode ref_mode;
struct rev_info revs;
struct commit *last_commit = NULL;
@ -302,12 +358,15 @@ int cmd_replay(int argc,
struct merge_result result;
struct strset *update_refs = NULL;
kh_oid_map_t *replayed_commits;
struct ref_transaction *transaction = NULL;
struct strbuf transaction_err = STRBUF_INIT;
struct strbuf reflog_msg = STRBUF_INIT;
int ret = 0;
const char * const replay_usage[] = {
const char *const replay_usage[] = {
N_("(EXPERIMENTAL!) git replay "
"([--contained] --onto <newbase> | --advance <branch>) "
"<revision-range>..."),
"[--ref-action[=<mode>]] <revision-range>"),
NULL
};
struct option replay_options[] = {
@ -318,7 +377,10 @@ int cmd_replay(int argc,
N_("revision"),
N_("replay onto given commit")),
OPT_BOOL(0, "contained", &contained,
N_("advance all branches contained in revision-range")),
N_("update all branches that point at commits in <revision-range>")),
OPT_STRING(0, "ref-action", &ref_action,
N_("mode"),
N_("control ref update behavior (update|print)")),
OPT_END()
};
@ -330,9 +392,12 @@ int cmd_replay(int argc,
usage_with_options(replay_usage, replay_options);
}
if (advance_name_opt && contained)
die(_("options '%s' and '%s' cannot be used together"),
"--advance", "--contained");
die_for_incompatible_opt2(!!advance_name_opt, "--advance",
contained, "--contained");
/* Parse ref action mode from command line or config */
ref_mode = get_ref_action_mode(repo, ref_action);
advance_name = xstrdup_or_null(advance_name_opt);
repo_init_revisions(repo, &revs, prefix);
@ -392,6 +457,24 @@ int cmd_replay(int argc,
if (!onto) /* FIXME: Should handle replaying down to root commit */
die("Replaying down to root commit is not supported yet!");
/* Build reflog message */
if (advance_name_opt)
strbuf_addf(&reflog_msg, "replay --advance %s", advance_name_opt);
else
strbuf_addf(&reflog_msg, "replay --onto %s",
oid_to_hex(&onto->object.oid));
/* Initialize ref transaction if using update mode */
if (ref_mode == REF_ACTION_UPDATE) {
transaction = ref_store_transaction_begin(get_main_ref_store(repo),
0, &transaction_err);
if (!transaction) {
ret = error(_("failed to begin ref transaction: %s"),
transaction_err.buf);
goto cleanup;
}
}
if (prepare_revision_walk(&revs) < 0) {
ret = error(_("error preparing revisions"));
goto cleanup;
@ -434,10 +517,16 @@ int cmd_replay(int argc,
if (decoration->type == DECORATION_REF_LOCAL &&
(contained || strset_contains(update_refs,
decoration->name))) {
printf("update %s %s %s\n",
decoration->name,
oid_to_hex(&last_commit->object.oid),
oid_to_hex(&commit->object.oid));
if (handle_ref_update(ref_mode, transaction,
decoration->name,
&last_commit->object.oid,
&commit->object.oid,
reflog_msg.buf,
&transaction_err) < 0) {
ret = error(_("failed to update ref '%s': %s"),
decoration->name, transaction_err.buf);
goto cleanup;
}
}
decoration = decoration->next;
}
@ -445,10 +534,24 @@ int cmd_replay(int argc,
/* In --advance mode, advance the target ref */
if (result.clean == 1 && advance_name) {
printf("update %s %s %s\n",
advance_name,
oid_to_hex(&last_commit->object.oid),
oid_to_hex(&onto->object.oid));
if (handle_ref_update(ref_mode, transaction, advance_name,
&last_commit->object.oid,
&onto->object.oid,
reflog_msg.buf,
&transaction_err) < 0) {
ret = error(_("failed to update ref '%s': %s"),
advance_name, transaction_err.buf);
goto cleanup;
}
}
/* Commit the ref transaction if we have one */
if (transaction && result.clean == 1) {
if (ref_transaction_commit(transaction, &transaction_err)) {
ret = error(_("failed to commit ref transaction: %s"),
transaction_err.buf);
goto cleanup;
}
}
merge_finalize(&merge_opt, &result);
@ -460,6 +563,10 @@ int cmd_replay(int argc,
ret = result.clean;
cleanup:
if (transaction)
ref_transaction_free(transaction);
strbuf_release(&transaction_err);
strbuf_release(&reflog_msg);
release_revisions(&revs);
free(advance_name);

View File

@ -2,20 +2,30 @@
#include "builtin.h"
#include "environment.h"
#include "hex.h"
#include "odb.h"
#include "parse-options.h"
#include "path-walk.h"
#include "progress.h"
#include "quote.h"
#include "ref-filter.h"
#include "refs.h"
#include "revision.h"
#include "strbuf.h"
#include "string-list.h"
#include "shallow.h"
#include "utf8.h"
static const char *const repo_usage[] = {
"git repo info [--format=(keyvalue|nul)] [-z] [<key>...]",
"git repo info [--format=(keyvalue|nul) | -z] [--all | <key>...]",
"git repo structure [--format=(table|keyvalue|nul) | -z]",
NULL
};
typedef int get_value_fn(struct repository *repo, struct strbuf *buf);
enum output_format {
FORMAT_TABLE,
FORMAT_KEYVALUE,
FORMAT_NUL_TERMINATED,
};
@ -77,13 +87,29 @@ static get_value_fn *get_value_fn_for_key(const char *key)
return found ? found->get_value : NULL;
}
static void print_field(enum output_format format, const char *key,
const char *value)
{
switch (format) {
case FORMAT_KEYVALUE:
printf("%s=", key);
quote_c_style(value, NULL, stdout, 0);
putchar('\n');
break;
case FORMAT_NUL_TERMINATED:
printf("%s\n%s%c", key, value, '\0');
break;
default:
BUG("not a valid output format: %d", format);
}
}
static int print_fields(int argc, const char **argv,
struct repository *repo,
enum output_format format)
{
int ret = 0;
struct strbuf valbuf = STRBUF_INIT;
struct strbuf quotbuf = STRBUF_INIT;
for (int i = 0; i < argc; i++) {
get_value_fn *get_value;
@ -97,28 +123,31 @@ static int print_fields(int argc, const char **argv,
}
strbuf_reset(&valbuf);
strbuf_reset(&quotbuf);
get_value(repo, &valbuf);
switch (format) {
case FORMAT_KEYVALUE:
quote_c_style(valbuf.buf, &quotbuf, NULL, 0);
printf("%s=%s\n", key, quotbuf.buf);
break;
case FORMAT_NUL_TERMINATED:
printf("%s\n%s%c", key, valbuf.buf, '\0');
break;
default:
BUG("not a valid output format: %d", format);
}
print_field(format, key, valbuf.buf);
}
strbuf_release(&valbuf);
strbuf_release(&quotbuf);
return ret;
}
static int print_all_fields(struct repository *repo,
enum output_format format)
{
struct strbuf valbuf = STRBUF_INIT;
for (size_t i = 0; i < ARRAY_SIZE(repo_info_fields); i++) {
const struct field *field = &repo_info_fields[i];
strbuf_reset(&valbuf);
field->get_value(repo, &valbuf);
print_field(format, field->key, valbuf.buf);
}
strbuf_release(&valbuf);
return 0;
}
static int parse_format_cb(const struct option *opt,
const char *arg, int unset UNUSED)
{
@ -130,16 +159,19 @@ static int parse_format_cb(const struct option *opt,
*format = FORMAT_NUL_TERMINATED;
else if (!strcmp(arg, "keyvalue"))
*format = FORMAT_KEYVALUE;
else if (!strcmp(arg, "table"))
*format = FORMAT_TABLE;
else
die(_("invalid format '%s'"), arg);
return 0;
}
static int repo_info(int argc, const char **argv, const char *prefix,
struct repository *repo)
static int cmd_repo_info(int argc, const char **argv, const char *prefix,
struct repository *repo)
{
enum output_format format = FORMAT_KEYVALUE;
int all_keys = 0;
struct option options[] = {
OPT_CALLBACK_F(0, "format", &format, N_("format"),
N_("output format"),
@ -148,12 +180,511 @@ static int repo_info(int argc, const char **argv, const char *prefix,
N_("synonym for --format=nul"),
PARSE_OPT_NONEG | PARSE_OPT_NOARG,
parse_format_cb),
OPT_BOOL(0, "all", &all_keys, N_("print all keys/values")),
OPT_END()
};
argc = parse_options(argc, argv, prefix, options, repo_usage, 0);
if (format != FORMAT_KEYVALUE && format != FORMAT_NUL_TERMINATED)
die(_("unsupported output format"));
return print_fields(argc, argv, repo, format);
if (all_keys && argc)
die(_("--all and <key> cannot be used together"));
if (all_keys)
return print_all_fields(repo, format);
else
return print_fields(argc, argv, repo, format);
}
struct ref_stats {
size_t branches;
size_t remotes;
size_t tags;
size_t others;
};
struct object_values {
size_t tags;
size_t commits;
size_t trees;
size_t blobs;
};
struct object_stats {
struct object_values type_counts;
struct object_values inflated_sizes;
struct object_values disk_sizes;
};
struct repo_structure {
struct ref_stats refs;
struct object_stats objects;
};
struct stats_table {
struct string_list rows;
int name_col_width;
int value_col_width;
int unit_col_width;
};
/*
* Holds column data that gets stored for each row.
*/
struct stats_table_entry {
char *value;
const char *unit;
};
static void stats_table_vaddf(struct stats_table *table,
struct stats_table_entry *entry,
const char *format, va_list ap)
{
struct strbuf buf = STRBUF_INIT;
struct string_list_item *item;
char *formatted_name;
int name_width;
strbuf_vaddf(&buf, format, ap);
formatted_name = strbuf_detach(&buf, NULL);
name_width = utf8_strwidth(formatted_name);
item = string_list_append_nodup(&table->rows, formatted_name);
item->util = entry;
if (name_width > table->name_col_width)
table->name_col_width = name_width;
if (!entry)
return;
if (entry->value) {
int value_width = utf8_strwidth(entry->value);
if (value_width > table->value_col_width)
table->value_col_width = value_width;
}
if (entry->unit) {
int unit_width = utf8_strwidth(entry->unit);
if (unit_width > table->unit_col_width)
table->unit_col_width = unit_width;
}
}
static void stats_table_addf(struct stats_table *table, const char *format, ...)
{
va_list ap;
va_start(ap, format);
stats_table_vaddf(table, NULL, format, ap);
va_end(ap);
}
static void stats_table_count_addf(struct stats_table *table, size_t value,
const char *format, ...)
{
struct stats_table_entry *entry;
va_list ap;
CALLOC_ARRAY(entry, 1);
humanise_count(value, &entry->value, &entry->unit);
va_start(ap, format);
stats_table_vaddf(table, entry, format, ap);
va_end(ap);
}
static void stats_table_size_addf(struct stats_table *table, size_t value,
const char *format, ...)
{
struct stats_table_entry *entry;
va_list ap;
CALLOC_ARRAY(entry, 1);
humanise_bytes(value, &entry->value, &entry->unit, HUMANISE_COMPACT);
va_start(ap, format);
stats_table_vaddf(table, entry, format, ap);
va_end(ap);
}
static inline size_t get_total_reference_count(struct ref_stats *stats)
{
return stats->branches + stats->remotes + stats->tags + stats->others;
}
static inline size_t get_total_object_values(struct object_values *values)
{
return values->tags + values->commits + values->trees + values->blobs;
}
static void stats_table_setup_structure(struct stats_table *table,
struct repo_structure *stats)
{
struct object_stats *objects = &stats->objects;
struct ref_stats *refs = &stats->refs;
size_t inflated_object_total;
size_t object_count_total;
size_t disk_object_total;
size_t ref_total;
ref_total = get_total_reference_count(refs);
stats_table_addf(table, "* %s", _("References"));
stats_table_count_addf(table, ref_total, " * %s", _("Count"));
stats_table_count_addf(table, refs->branches, " * %s", _("Branches"));
stats_table_count_addf(table, refs->tags, " * %s", _("Tags"));
stats_table_count_addf(table, refs->remotes, " * %s", _("Remotes"));
stats_table_count_addf(table, refs->others, " * %s", _("Others"));
object_count_total = get_total_object_values(&objects->type_counts);
stats_table_addf(table, "");
stats_table_addf(table, "* %s", _("Reachable objects"));
stats_table_count_addf(table, object_count_total, " * %s", _("Count"));
stats_table_count_addf(table, objects->type_counts.commits,
" * %s", _("Commits"));
stats_table_count_addf(table, objects->type_counts.trees,
" * %s", _("Trees"));
stats_table_count_addf(table, objects->type_counts.blobs,
" * %s", _("Blobs"));
stats_table_count_addf(table, objects->type_counts.tags,
" * %s", _("Tags"));
inflated_object_total = get_total_object_values(&objects->inflated_sizes);
stats_table_size_addf(table, inflated_object_total,
" * %s", _("Inflated size"));
stats_table_size_addf(table, objects->inflated_sizes.commits,
" * %s", _("Commits"));
stats_table_size_addf(table, objects->inflated_sizes.trees,
" * %s", _("Trees"));
stats_table_size_addf(table, objects->inflated_sizes.blobs,
" * %s", _("Blobs"));
stats_table_size_addf(table, objects->inflated_sizes.tags,
" * %s", _("Tags"));
disk_object_total = get_total_object_values(&objects->disk_sizes);
stats_table_size_addf(table, disk_object_total,
" * %s", _("Disk size"));
stats_table_size_addf(table, objects->disk_sizes.commits,
" * %s", _("Commits"));
stats_table_size_addf(table, objects->disk_sizes.trees,
" * %s", _("Trees"));
stats_table_size_addf(table, objects->disk_sizes.blobs,
" * %s", _("Blobs"));
stats_table_size_addf(table, objects->disk_sizes.tags,
" * %s", _("Tags"));
}
static void stats_table_print_structure(const struct stats_table *table)
{
const char *name_col_title = _("Repository structure");
const char *value_col_title = _("Value");
int title_name_width = utf8_strwidth(name_col_title);
int title_value_width = utf8_strwidth(value_col_title);
int name_col_width = table->name_col_width;
int value_col_width = table->value_col_width;
int unit_col_width = table->unit_col_width;
struct string_list_item *item;
struct strbuf buf = STRBUF_INIT;
if (title_name_width > name_col_width)
name_col_width = title_name_width;
if (title_value_width > value_col_width + unit_col_width + 1)
value_col_width = title_value_width - unit_col_width;
strbuf_addstr(&buf, "| ");
strbuf_utf8_align(&buf, ALIGN_LEFT, name_col_width, name_col_title);
strbuf_addstr(&buf, " | ");
strbuf_utf8_align(&buf, ALIGN_LEFT,
value_col_width + unit_col_width + 1, value_col_title);
strbuf_addstr(&buf, " |");
printf("%s\n", buf.buf);
printf("| ");
for (int i = 0; i < name_col_width; i++)
putchar('-');
printf(" | ");
for (int i = 0; i < value_col_width + unit_col_width + 1; i++)
putchar('-');
printf(" |\n");
for_each_string_list_item(item, &table->rows) {
struct stats_table_entry *entry = item->util;
const char *value = "";
const char *unit = "";
if (entry) {
struct stats_table_entry *entry = item->util;
value = entry->value;
if (entry->unit)
unit = entry->unit;
}
strbuf_reset(&buf);
strbuf_addstr(&buf, "| ");
strbuf_utf8_align(&buf, ALIGN_LEFT, name_col_width, item->string);
strbuf_addstr(&buf, " | ");
strbuf_utf8_align(&buf, ALIGN_RIGHT, value_col_width, value);
strbuf_addch(&buf, ' ');
strbuf_utf8_align(&buf, ALIGN_LEFT, unit_col_width, unit);
strbuf_addstr(&buf, " |");
printf("%s\n", buf.buf);
}
strbuf_release(&buf);
}
static void stats_table_clear(struct stats_table *table)
{
struct stats_table_entry *entry;
struct string_list_item *item;
for_each_string_list_item(item, &table->rows) {
entry = item->util;
if (entry)
free(entry->value);
}
string_list_clear(&table->rows, 1);
}
static void structure_keyvalue_print(struct repo_structure *stats,
char key_delim, char value_delim)
{
printf("references.branches.count%c%" PRIuMAX "%c", key_delim,
(uintmax_t)stats->refs.branches, value_delim);
printf("references.tags.count%c%" PRIuMAX "%c", key_delim,
(uintmax_t)stats->refs.tags, value_delim);
printf("references.remotes.count%c%" PRIuMAX "%c", key_delim,
(uintmax_t)stats->refs.remotes, value_delim);
printf("references.others.count%c%" PRIuMAX "%c", key_delim,
(uintmax_t)stats->refs.others, value_delim);
printf("objects.commits.count%c%" PRIuMAX "%c", key_delim,
(uintmax_t)stats->objects.type_counts.commits, value_delim);
printf("objects.trees.count%c%" PRIuMAX "%c", key_delim,
(uintmax_t)stats->objects.type_counts.trees, value_delim);
printf("objects.blobs.count%c%" PRIuMAX "%c", key_delim,
(uintmax_t)stats->objects.type_counts.blobs, value_delim);
printf("objects.tags.count%c%" PRIuMAX "%c", key_delim,
(uintmax_t)stats->objects.type_counts.tags, value_delim);
printf("objects.commits.inflated_size%c%" PRIuMAX "%c", key_delim,
(uintmax_t)stats->objects.inflated_sizes.commits, value_delim);
printf("objects.trees.inflated_size%c%" PRIuMAX "%c", key_delim,
(uintmax_t)stats->objects.inflated_sizes.trees, value_delim);
printf("objects.blobs.inflated_size%c%" PRIuMAX "%c", key_delim,
(uintmax_t)stats->objects.inflated_sizes.blobs, value_delim);
printf("objects.tags.inflated_size%c%" PRIuMAX "%c", key_delim,
(uintmax_t)stats->objects.inflated_sizes.tags, value_delim);
printf("objects.commits.disk_size%c%" PRIuMAX "%c", key_delim,
(uintmax_t)stats->objects.disk_sizes.commits, value_delim);
printf("objects.trees.disk_size%c%" PRIuMAX "%c", key_delim,
(uintmax_t)stats->objects.disk_sizes.trees, value_delim);
printf("objects.blobs.disk_size%c%" PRIuMAX "%c", key_delim,
(uintmax_t)stats->objects.disk_sizes.blobs, value_delim);
printf("objects.tags.disk_size%c%" PRIuMAX "%c", key_delim,
(uintmax_t)stats->objects.disk_sizes.tags, value_delim);
fflush(stdout);
}
struct count_references_data {
struct ref_stats *stats;
struct rev_info *revs;
struct progress *progress;
};
static int count_references(const struct reference *ref, void *cb_data)
{
struct count_references_data *data = cb_data;
struct ref_stats *stats = data->stats;
size_t ref_count;
switch (ref_kind_from_refname(ref->name)) {
case FILTER_REFS_BRANCHES:
stats->branches++;
break;
case FILTER_REFS_REMOTES:
stats->remotes++;
break;
case FILTER_REFS_TAGS:
stats->tags++;
break;
case FILTER_REFS_OTHERS:
stats->others++;
break;
default:
BUG("unexpected reference type");
}
/*
* While iterating through references for counting, also add OIDs in
* preparation for the path walk.
*/
add_pending_oid(data->revs, NULL, ref->oid, 0);
ref_count = get_total_reference_count(stats);
display_progress(data->progress, ref_count);
return 0;
}
static void structure_count_references(struct ref_stats *stats,
struct rev_info *revs,
struct repository *repo,
int show_progress)
{
struct count_references_data data = {
.stats = stats,
.revs = revs,
};
if (show_progress)
data.progress = start_delayed_progress(repo,
_("Counting references"), 0);
refs_for_each_ref(get_main_ref_store(repo), count_references, &data);
stop_progress(&data.progress);
}
struct count_objects_data {
struct object_database *odb;
struct object_stats *stats;
struct progress *progress;
};
static int count_objects(const char *path UNUSED, struct oid_array *oids,
enum object_type type, void *cb_data)
{
struct count_objects_data *data = cb_data;
struct object_stats *stats = data->stats;
size_t inflated_total = 0;
size_t disk_total = 0;
size_t object_count;
for (size_t i = 0; i < oids->nr; i++) {
struct object_info oi = OBJECT_INFO_INIT;
unsigned long inflated;
off_t disk;
oi.sizep = &inflated;
oi.disk_sizep = &disk;
if (odb_read_object_info_extended(data->odb, &oids->oid[i], &oi,
OBJECT_INFO_SKIP_FETCH_OBJECT |
OBJECT_INFO_QUICK) < 0)
continue;
inflated_total += inflated;
disk_total += disk;
}
switch (type) {
case OBJ_TAG:
stats->type_counts.tags += oids->nr;
stats->inflated_sizes.tags += inflated_total;
stats->disk_sizes.tags += disk_total;
break;
case OBJ_COMMIT:
stats->type_counts.commits += oids->nr;
stats->inflated_sizes.commits += inflated_total;
stats->disk_sizes.commits += disk_total;
break;
case OBJ_TREE:
stats->type_counts.trees += oids->nr;
stats->inflated_sizes.trees += inflated_total;
stats->disk_sizes.trees += disk_total;
break;
case OBJ_BLOB:
stats->type_counts.blobs += oids->nr;
stats->inflated_sizes.blobs += inflated_total;
stats->disk_sizes.blobs += disk_total;
break;
default:
BUG("invalid object type");
}
object_count = get_total_object_values(&stats->type_counts);
display_progress(data->progress, object_count);
return 0;
}
static void structure_count_objects(struct object_stats *stats,
struct rev_info *revs,
struct repository *repo, int show_progress)
{
struct path_walk_info info = PATH_WALK_INFO_INIT;
struct count_objects_data data = {
.odb = repo->objects,
.stats = stats,
};
info.revs = revs;
info.path_fn = count_objects;
info.path_fn_data = &data;
if (show_progress)
data.progress = start_delayed_progress(repo, _("Counting objects"), 0);
walk_objects_by_path(&info);
path_walk_info_clear(&info);
stop_progress(&data.progress);
}
static int cmd_repo_structure(int argc, const char **argv, const char *prefix,
struct repository *repo)
{
struct stats_table table = {
.rows = STRING_LIST_INIT_DUP,
};
enum output_format format = FORMAT_TABLE;
struct repo_structure stats = { 0 };
struct rev_info revs;
int show_progress = -1;
struct option options[] = {
OPT_CALLBACK_F(0, "format", &format, N_("format"),
N_("output format"),
PARSE_OPT_NONEG, parse_format_cb),
OPT_CALLBACK_F('z', NULL, &format, NULL,
N_("synonym for --format=nul"),
PARSE_OPT_NONEG | PARSE_OPT_NOARG,
parse_format_cb),
OPT_BOOL(0, "progress", &show_progress, N_("show progress")),
OPT_END()
};
argc = parse_options(argc, argv, prefix, options, repo_usage, 0);
if (argc)
usage(_("too many arguments"));
repo_init_revisions(repo, &revs, prefix);
if (show_progress < 0)
show_progress = isatty(2);
structure_count_references(&stats.refs, &revs, repo, show_progress);
structure_count_objects(&stats.objects, &revs, repo, show_progress);
switch (format) {
case FORMAT_TABLE:
stats_table_setup_structure(&table, &stats);
stats_table_print_structure(&table);
break;
case FORMAT_KEYVALUE:
structure_keyvalue_print(&stats, '=', '\n');
break;
case FORMAT_NUL_TERMINATED:
structure_keyvalue_print(&stats, '\n', '\0');
break;
default:
BUG("invalid output format");
}
stats_table_clear(&table);
release_revisions(&revs);
return 0;
}
int cmd_repo(int argc, const char **argv, const char *prefix,
@ -161,7 +692,8 @@ int cmd_repo(int argc, const char **argv, const char *prefix,
{
parse_opt_subcommand_fn *fn = NULL;
struct option options[] = {
OPT_SUBCOMMAND("info", &fn, repo_info),
OPT_SUBCOMMAND("info", &fn, cmd_repo_info),
OPT_SUBCOMMAND("structure", &fn, cmd_repo_structure),
OPT_END()
};

View File

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

View File

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

View File

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

View File

@ -593,16 +593,12 @@ static void print_status(unsigned int flags, char state, const char *path,
printf("\n");
}
static int handle_submodule_head_ref(const char *refname UNUSED,
const char *referent UNUSED,
const struct object_id *oid,
int flags UNUSED,
void *cb_data)
static int handle_submodule_head_ref(const struct reference *ref, void *cb_data)
{
struct object_id *output = cb_data;
if (oid)
oidcpy(output, oid);
if (ref->oid)
oidcpy(output, ref->oid);
return 0;
}
@ -1907,6 +1903,13 @@ static int determine_submodule_update_strategy(struct repository *r,
const char *val;
int ret;
/*
* NEEDSWORK: audit and ensure that update_submodule() has right
* to assume that submodule_from_path() above will always succeed.
*/
if (!sub)
BUG("update_submodule assumes a submodule exists at path (%s)",
path);
key = xstrfmt("submodule.%s.update", sub->name);
if (update) {
@ -3531,14 +3534,15 @@ static int module_add(int argc, const char **argv, const char *prefix,
}
}
if(!add_data.sm_name)
if (!add_data.sm_name)
add_data.sm_name = add_data.sm_path;
existing = submodule_from_name(the_repository,
null_oid(the_hash_algo),
add_data.sm_name);
if (existing && strcmp(existing->path, add_data.sm_path)) {
if (existing && existing->path &&
strcmp(existing->path, add_data.sm_path)) {
if (!force) {
die(_("submodule name '%s' already used for path '%s'"),
add_data.sm_name, existing->path);

View File

@ -149,11 +149,11 @@ 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)
pretty_print_ref(name, oid, format);
pretty_print_ref(name, oid, NULL, format);
return 0;
}

View File

@ -363,7 +363,7 @@ struct input_zstream_data {
int status;
};
static const void *feed_input_zstream(struct input_stream *in_stream,
static const void *feed_input_zstream(struct odb_write_stream *in_stream,
unsigned long *readlen)
{
struct input_zstream_data *data = in_stream->data;
@ -393,7 +393,7 @@ static void stream_blob(unsigned long size, unsigned nr)
{
git_zstream zstream = { 0 };
struct input_zstream_data data = { 0 };
struct input_stream in_stream = {
struct odb_write_stream in_stream = {
.read = feed_input_zstream,
.data = &data,
};
@ -402,8 +402,7 @@ static void stream_blob(unsigned long size, unsigned nr)
data.zstream = &zstream;
git_inflate_init(&zstream);
if (stream_loose_object(the_repository->objects->sources,
&in_stream, size, &info->oid))
if (odb_write_object_stream(the_repository->objects, &in_stream, size, &info->oid))
die(_("failed to write object in stream"));
if (data.status != Z_STREAM_END)

View File

@ -4,8 +4,8 @@
#define USE_THE_REPOSITORY_VARIABLE
#include "builtin.h"
#include "archive.h"
#include "path.h"
#include "pkt-line.h"
#include "setup.h"
#include "sideband.h"
#include "run-command.h"
#include "strvec.h"

View File

@ -5,11 +5,11 @@
#include "gettext.h"
#include "pkt-line.h"
#include "parse-options.h"
#include "path.h"
#include "protocol.h"
#include "replace-object.h"
#include "upload-pack.h"
#include "serve.h"
#include "setup.h"
#include "commit.h"
#include "environment.h"

View File

@ -61,13 +61,13 @@ 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;
}
if (format.format)
pretty_print_ref(name, &oid, &format);
pretty_print_ref(name, &oid, NULL, &format);
}
return had_error;
}

View File

@ -635,11 +635,7 @@ static void print_preparing_worktree_line(int detach,
*
* Returns 0 on failure and non-zero on success.
*/
static int first_valid_ref(const char *refname UNUSED,
const char *referent UNUSED,
const struct object_id *oid UNUSED,
int flags UNUSED,
void *cb_data UNUSED)
static int first_valid_ref(const struct reference *ref UNUSED, void *cb_data UNUSED)
{
return 1;
}
@ -979,14 +975,18 @@ static void show_worktree_porcelain(struct worktree *wt, int line_terminator)
fputc(line_terminator, stdout);
}
static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len)
struct worktree_display {
char *path;
int width;
};
static void show_worktree(struct worktree *wt, struct worktree_display *display,
int path_maxwidth, int abbrev_len)
{
struct strbuf sb = STRBUF_INIT;
int cur_path_len = strlen(wt->path);
int path_adj = cur_path_len - utf8_strwidth(wt->path);
const char *reason;
strbuf_addf(&sb, "%-*s ", 1 + path_maxlen + path_adj, wt->path);
strbuf_addf(&sb, "%s%*s", display->path, 1 + path_maxwidth - display->width, "");
if (wt->is_bare)
strbuf_addstr(&sb, "(bare)");
else {
@ -1020,20 +1020,27 @@ static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len)
strbuf_release(&sb);
}
static void measure_widths(struct worktree **wt, int *abbrev, int *maxlen)
static void measure_widths(struct worktree **wt, int *abbrev,
struct worktree_display **d, int *maxwidth)
{
int i;
int i, display_alloc = 0;
struct worktree_display *display = NULL;
struct strbuf buf = STRBUF_INIT;
for (i = 0; wt[i]; i++) {
int sha1_len;
int path_len = strlen(wt[i]->path);
ALLOC_GROW(display, i + 1, display_alloc);
quote_path(wt[i]->path, NULL, &buf, 0);
display[i].width = utf8_strwidth(buf.buf);
display[i].path = strbuf_detach(&buf, NULL);
if (path_len > *maxlen)
*maxlen = path_len;
if (display[i].width > *maxwidth)
*maxwidth = display[i].width;
sha1_len = strlen(repo_find_unique_abbrev(the_repository, &wt[i]->head_oid, *abbrev));
if (sha1_len > *abbrev)
*abbrev = sha1_len;
}
*d = display;
}
static int pathcmp(const void *a_, const void *b_)
@ -1079,21 +1086,27 @@ static int list(int ac, const char **av, const char *prefix,
die(_("the option '%s' requires '%s'"), "-z", "--porcelain");
else {
struct worktree **worktrees = get_worktrees();
int path_maxlen = 0, abbrev = DEFAULT_ABBREV, i;
int path_maxwidth = 0, abbrev = DEFAULT_ABBREV, i;
struct worktree_display *display = NULL;
/* sort worktrees by path but keep main worktree at top */
pathsort(worktrees + 1);
if (!porcelain)
measure_widths(worktrees, &abbrev, &path_maxlen);
measure_widths(worktrees, &abbrev,
&display, &path_maxwidth);
for (i = 0; worktrees[i]; i++) {
if (porcelain)
show_worktree_porcelain(worktrees[i],
line_terminator);
else
show_worktree(worktrees[i], path_maxlen, abbrev);
show_worktree(worktrees[i],
&display[i], path_maxwidth, abbrev);
}
for (i = 0; display && worktrees[i]; i++)
free(display[i].path);
free(display);
free_worktrees(worktrees);
}
return 0;

View File

@ -548,12 +548,41 @@ void cache_tree_write(struct strbuf *sb, struct cache_tree *root)
trace2_region_leave("cache_tree", "write", the_repository);
}
static int parse_int(const char **ptr, unsigned long *len_p, int *out)
{
const char *s = *ptr;
unsigned long len = *len_p;
int ret = 0;
int sign = 1;
while (len && *s == '-') {
sign *= -1;
s++;
len--;
}
while (len) {
if (!isdigit(*s))
break;
ret *= 10;
ret += *s - '0';
s++;
len--;
}
if (s == *ptr)
return -1;
*ptr = s;
*len_p = len;
*out = sign * ret;
return 0;
}
static struct cache_tree *read_one(const char **buffer, unsigned long *size_p)
{
const char *buf = *buffer;
unsigned long size = *size_p;
const char *cp;
char *ep;
struct cache_tree *it;
int i, subtree_nr;
const unsigned rawsz = the_hash_algo->rawsz;
@ -569,19 +598,14 @@ static struct cache_tree *read_one(const char **buffer, unsigned long *size_p)
buf++; size--;
it = cache_tree();
cp = buf;
it->entry_count = strtol(cp, &ep, 10);
if (cp == ep)
if (parse_int(&buf, &size, &it->entry_count) < 0)
goto free_return;
cp = ep;
subtree_nr = strtol(cp, &ep, 10);
if (cp == ep)
if (!size || *buf != ' ')
goto free_return;
while (size && *buf && *buf != '\n') {
size--;
buf++;
}
if (!size)
buf++; size--;
if (parse_int(&buf, &size, &subtree_nr) < 0)
goto free_return;
if (!size || *buf != '\n')
goto free_return;
buf++; size--;
if (0 <= it->entry_count) {

View File

@ -25,6 +25,24 @@ void chdir_notify_register(const char *name,
list_add_tail(&e->list, &chdir_notify_entries);
}
void chdir_notify_unregister(const char *name, chdir_notify_callback cb,
void *data)
{
struct list_head *pos, *p;
list_for_each_safe(pos, p, &chdir_notify_entries) {
struct chdir_notify_entry *e =
list_entry(pos, struct chdir_notify_entry, list);
if (e->cb != cb || e->data != data || !e->name != !name ||
(e->name && strcmp(e->name, name)))
continue;
list_del(pos);
free(e);
}
}
static void reparent_cb(const char *name,
const char *old_cwd,
const char *new_cwd,

View File

@ -41,6 +41,8 @@ typedef void (*chdir_notify_callback)(const char *name,
const char *new_cwd,
void *data);
void chdir_notify_register(const char *name, chdir_notify_callback cb, void *data);
void chdir_notify_unregister(const char *name, chdir_notify_callback cb,
void *data);
void chdir_notify_reparent(const char *name, char **path);
/*

View File

@ -109,7 +109,7 @@ macos-*)
brew link --force gettext
mkdir -p "$CUSTOM_PATH"
wget -q "$P4WHENCE/bin.macosx1015x86_64/helix-core-server.tgz" &&
wget -q "$P4WHENCE/bin.macosx12arm64/helix-core-server.tgz" &&
tar -xf helix-core-server.tgz -C "$CUSTOM_PATH" p4 p4d &&
sudo xattr -d com.apple.quarantine "$CUSTOM_PATH/p4" "$CUSTOM_PATH/p4d" 2>/dev/null || true
rm helix-core-server.tgz

Some files were not shown because too many files have changed in this diff Show More