aboutsummaryrefslogtreecommitdiffstats
AgeCommit message (Collapse)AuthorFilesLines
2025-08-25doc/format-patch: adjust Thunderbird MUA hint to new add-onJohannes Sixt1-3/+9
There are three tips how to compose a non-line-wrapped patch with Thunderbird. The first one suggests use of an add-on. The one referenced has long been superseded by a different one. Update the link to the new one. Mention that additional configuration is required to make the add-on work. Signed-off-by: Johannes Sixt <j6t@kdbg.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-25bulk-checkin: use repository variable from transactionJustin Tobler1-31/+36
The bulk-checkin subsystem depends on `the_repository`. Adapt functions and call sites to access the repository through `struct odb_transaction` instead. The `USE_THE_REPOSITORY_VARIBALE` is still required as the `pack_compression_level` and `pack_size_limit_cfg` globals are still used. Also adapt functions using packfile state to instead access it through the transaction. This makes some function parameters redundant and go away. Signed-off-by: Justin Tobler <jltobler@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-25bulk-checkin: require transaction for index_blob_bulk_checkin()Justin Tobler3-32/+23
The bulk-checkin subsystem provides a mechanism to write blobs directly to a packfile via `index_blob_bulk_checkin()`. If there is an ongoing transaction when invoked, objects written via this function are stored in the same packfile. The packfile is not flushed until the transaction itself is flushed. If there is no transaction, the single object is written to a packfile and immediately flushed. This complicates `index_blob_bulk_checkin()` as it cannot reliably use the provided transaction to get the associated repository. Update `index_blob_bulk_checkin()` to assume that a valid transaction is always provided. Callers are now expected to ensure a transaction is set up beforehand. With this simplification, `deflate_blob_bulk_checkin()` is no longer needed as a standalone internal function and is combined with `index_blob_bulk_checkin()`. The single call site in `object-file.c:index_fd()` is updated accordingly. Due to how `{begin,end}_odb_transaction()` handles nested transactions, a new transaction is only created and committed if there is not already an ongoing transaction. Signed-off-by: Justin Tobler <jltobler@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-25bulk-checkin: remove global transaction stateJustin Tobler9-52/+94
Object database transactions in the bulk-checkin subsystem rely on global state to track transaction status. Stop relying on global state and instead store the transaction in the `struct object_database`. Functions that operate on transactions are updated to now wire transaction state. Signed-off-by: Justin Tobler <jltobler@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-25bulk-checkin: introduce object database transaction structureJustin Tobler1-22/+24
Object database transaction state is stored across several global variables in the bulk-checkin subsystem. Consolidate this state into a single `struct odb_transaction` global. In a subsequent commit, the transactional interfaces will be updated to wire this structure instead of relying on a global variable. Signed-off-by: Justin Tobler <jltobler@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-25doc: clarify which remotes can be used with GitGitGadgetDaniele Sassoli1-4/+7
The docs mostly point to using git/git as one's remote, however, when it comes to Sending a PR to GitGitGadget section, the reader is told to use gitgitgadget/git, with no mention of git/git, potentially leading to some confusion. Clarify that both gitgitgadget/git and git/git can be used, albeit with some differences. Signed-off-by: Daniele Sassoli <danielesassoli@gmail.com> Acked-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-25path-walk: create initializer for path listsDerrick Stolee1-32/+25
The previous change fixed a bug in 'git repack -adf --path-walk' that was due to an update to how path lists are initialized and missing some important cases when processing the pending objects. This change takes the three critical places where path lists are initialized and combines them into a static method. This simplifies the callers somewhat while also helping to avoid a missed update in the future. The other places where a path list (struct type_and_oid_list) is initialized is for the following "fixed" lists: * Tag objects. * Commit objects. * Root trees. * Tagged trees. * Tagged blobs. These lists are created and consumed in different ways, with only the root trees being passed into the logic that cares about the "maybe_interesting" bit. It is appropriate to keep these uses separate. Signed-off-by: Derrick Stolee <stolee@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-25path-walk: fix setup of pending objectsDerrick Stolee2-0/+65
Users reported an issue where objects were missing from their local repositories after a full repack using 'git repack -adf --path-walk'. This was alarming and took a while to create a reproducer. Here, we fix the bug and include a test case that would fail without this fix. The root cause is that certain objects existed in the index and had no second versions. These objects are usually blobs, though trees can be included if a cache-tree exists. The issue is that the revision walk adds these objects to the "pending" list and the path-walk API forgets to mark the lists it creates at this point as "maybe_interesting". If these paths only ever have a single version in the history of the repo (including the current staged version) then the parent directory never tries to add a new object to the list and mark the list as "maybe_interesting". Thus, when walking the list later, the group is skipped as it is expected that no objects are interesting. This happens even when there are actually no UNINTERESTING objects at all! This is based on the optimization enabled by the pack.useSparse=true config option, which is the default. Thus, we create a test case that demonstrates the many cases of this issue for reproducibility: 1. File a/b/c has only one committed version. 2. Files a/i and x/y only exist as staged changes. 3. Tree x/ only exists in the cache-tree. After performing a non-path-walk repack to force all loose objects into packfiles, run a --path-walk repack followed by 'git fsck'. This fsck is what fails with the following errors: error: invalid object 100644 f2e41136... for 'a/b/c' This is the dropped instance of the single-versioned a/b/c file. broken link from tree cfda31d8... to tree 3f725fcd... This is the missing tree for the single-versioned a/b/ directory. missing blob 0ddf2bae... (a/i) missing blob 975fbec8... (x/y) missing blob a60d869d... (file) missing blob f2e41136... (a/b/c) missing tree 3f725fcd... (a/b/) dangling tree 5896d7e... (staged root tree) Note that since the staged root tree is missing, the fsck output cannot even report that the staged x/ tree is missing as well. The core problem here is that the "maybe_interesting" member of 'struct type_and_oid_list' is not initialized to '1'. This member was added in 6333e7ae0b (path-walk: mark trees and blobs as UNINTERESTING, 2024-12-20) in a way to help when creating packfiles for a small commit range using the sparse path algorithm (enabled by pack.useSparse=true). The idea here is that the list is marked as "maybe_interesting" if an object is added that does not have the UNINTERESTING flag on it. Later, this is checked again in case all objects in the list were marked UNINTERESTING after that point in time. In this case, the algorithm skips the list as there is no reason to visit it. This leads to the problem where the "maybe_interesting" member was not appropriately initialized when the list is created from pending objects. Initializing this in the correct places fixes the bug. To reduce risk of similar bugs around initializing this structure, a follow-up change will make initializing lists use a shared method. Signed-off-by: Derrick Stolee <stolee@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-25line-log: simplify condition checking for merge commitsSZEDER Gábor1-3/+3
In process_ranges_arbitrary_commit() the condition deciding whether the given commit is not a merge, i.e. that it doesn't have more than one parent, is head-scratchingly backwards, flip it. Signed-off-by: SZEDER Gábor <szeder.dev@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-25line-log: initialize diff queue in process_ranges_ordinary_commit()SZEDER Gábor1-1/+1
process_ranges_ordinary_commit() uses a local diff queue variable, which it leaves uninitialized before passing its address to queue_diffs(). This is not an issue, because at the end of that function the contents of an other diff queue is moved into it by simply overwriting whatever is in there, i.e. without reading any uninitialized memory. Still, seeing the uninitialized diff queue being passed around scared me more than once, so out of caution let's make sure that it's initialized. Signed-off-by: SZEDER Gábor <szeder.dev@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-25line-log: get rid of the parents array in process_ranges_merge_commit()SZEDER Gábor1-12/+12
We can easily iterate through the parents of a merge commit without turning the list of parents into a dynamically allocated array of parents, so let's do so. This way we can avoid a memory allocation for each processed merge commit, though its effect on runtime seems to be unmeasurable. Signed-off-by: SZEDER Gábor <szeder.dev@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-25line-log: avoid unnecessary tree diffs when processing merge commitsSZEDER Gábor1-15/+5
In process_ranges_merge_commit(), the line-level log first creates an array of diff queues by iterating over all parents of a merge commit and computing a tree diff for each. Then in a second loop it iterates over those diff queues, and if it finds that none of the interesting paths were modified in one of them, then it will return early. This means that when none of the interesting paths were modified between a merge and its first parent, then the tree diff between the merge and its second (Nth...) parent was computed in vain. Unify these two loops, so when it iterates over all parents of a merge commit, then it first computes the tree diff between the merge and that particular parent and then processes the resulting diff queue right away. This way we can spare some tree diff computing, thereby speeding up line-level log in repositories with mergy history: # git.git, 25.8% of commits are merges: Benchmark 1: ./git_v2.51.0 -C ~/src/git log -L:'lookup_commit(':commit.c v2.51.0 Time (mean ± σ): 1.001 s ± 0.009 s [User: 0.906 s, System: 0.095 s] Range (min … max): 0.991 s … 1.023 s 10 runs Benchmark 2: ./git -C ~/src/git log -L:'lookup_commit(':commit.c v2.51.0 Time (mean ± σ): 445.5 ms ± 3.4 ms [User: 358.8 ms, System: 84.3 ms] Range (min … max): 440.1 ms … 450.3 ms 10 runs Summary './git -C ~/src/git log -L:'lookup_commit(':commit.c v2.51.0' ran 2.25 ± 0.03 times faster than './git_v2.51.0 -C ~/src/git log -L:'lookup_commit(':commit.c v2.51.0' # linux.git, 7.5% of commits are merges: Benchmark 1: ./git_v2.51.0 -C ~/src/linux.git log -L:build_restore_work_registers:arch/mips/mm/tlbex.c v6.16 Time (mean ± σ): 3.246 s ± 0.007 s [User: 2.835 s, System: 0.409 s] Range (min … max): 3.232 s … 3.255 s 10 runs Benchmark 2: ./git -C ~/src/linux.git log -L:build_restore_work_registers:arch/mips/mm/tlbex.c v6.16 Time (mean ± σ): 2.467 s ± 0.014 s [User: 2.113 s, System: 0.353 s] Range (min … max): 2.455 s … 2.505 s 10 runs Summary './git -C ~/src/linux.git log -L:build_restore_work_registers:arch/mips/mm/tlbex.c v6.16' ran 1.32 ± 0.01 times faster than './git_v2.51.0 -C ~/src/linux.git log -L:build_restore_work_registers:arch/mips/mm/tlbex.c v6.16' And since now each iteration computes a tree diff and processes its result, there is no reason to store the diff queues for each merge parent anymore, so replace that diff queue array with a loop-local diff queue variable. With this change the static free_diffqueues() helper function in 'line-log.c' has no more callers left, remove it. Signed-off-by: SZEDER Gábor <szeder.dev@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-24gitk: use <Button-3> for ctx menus on macOS with Tcl 8.7+Ruoyu Zhong1-1/+1
Commit d277e89f87fda01daa1e1a35fc1f7118678faa1f added special handling on macOS (OS X) that makes button 2 the right mouse button. As per TIP 474 [1], Tcl 8.7 has swapped buttons 2 and 3 such that button 3 is made the right mouse button as in other platforms. Therefore, the logic should be updated to use button 3 on macOS with Tcl 8.7+. [1]: https://core.tcl-lang.org/tips/doc/main/tip/474.md Signed-off-by: Ruoyu Zhong <zhongruoyu@outlook.com>
2025-08-23doc: git-rebase: update discussion of internalsJulia Evans1-43/+18
- make it clearer that we're talking about a multistep process - give a more technically accurate description how rebase works with the merge backend. - condense the explanation of how git rebase skips commits with the same textual changes into a single bullet point and remove the explanatory diagram. Lots of things which are more complicated are already being explained without a diagram. - remove the explanation of how exactly `--fork-point` and `--root` work since that information is in the OPTIONS section - put all discussion of `ORIG_HEAD` inside the note Signed-off-by: Julia Evans <julia@jvns.ca> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-23doc: git-rebase: move --onto explanation downJulia Evans1-0/+5
There's a very clear explanation with examples of using --onto which is currently buried in the very long DESCRIPTION section. This moves it to its own section, so that we can reference the explanation from the `--onto` option by name. Signed-off-by: Julia Evans <julia@jvns.ca> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-23doc: git rebase: clarify arguments syntaxJulia Evans1-5/+1
Remove duplicate explanation of `git rebase <upstream> <branch>` which is already explained above. Signed-off-by: Julia Evans <julia@jvns.ca> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-23doc: git rebase: dedup merge conflict discussionJulia Evans1-29/+20
Previously there were two explanations, this combines them both into a single explanation. Signed-off-by: Julia Evans <julia@jvns.ca> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-23doc: git-rebase: start with an exampleJulia Evans1-26/+26
- Start with an example that mirrors the example in the `git-merge` man page, to make it easier for folks to understand the difference between a rebase and a merge. - Mention that rebase can combine or reorder commits Signed-off-by: Julia Evans <julia@jvns.ca> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-22The second batchJunio C Hamano1-0/+23
2025-08-22Merge branch 'ly/diff-name-only-with-diff-from-content'Junio C Hamano5-23/+70
Various options to "git diff" that makes comparison ignore certain aspects of the differences (like "space changes are ignored", "differences in lines that match these regular expressions are ignored") did not work well with "--name-only" and friends. * ly/diff-name-only-with-diff-from-content: diff: ensure consistent diff behavior with ignore options
2025-08-22Merge branch 'kr/clone-synopsis-fix'Junio C Hamano1-1/+1
Doc fix. * kr/clone-synopsis-fix: docs: remove stray bracket from git-clone synopsis
2025-08-22Merge branch 'ac/deglobal-fmt-merge-log-config'Junio C Hamano6-10/+18
Code clean-up. * ac/deglobal-fmt-merge-log-config: builtin/fmt-merge-msg: stop depending on 'the_repository' environment: remove the global variable 'merge_log_config'
2025-08-22Merge branch 'jc/diff-no-index-in-subdir'Junio C Hamano2-0/+32
"git diff --no-index" run inside a subdirectory under control of a Git repository operated at the top of the working tree and stripped the prefix from the output, and oddballs like "-" (stdin) did not work correctly because of it. Correct the set-up by undoing what the set-up sequence did to cwd and prefix. * jc/diff-no-index-in-subdir: diff: --no-index should ignore the worktree
2025-08-22Merge branch 'gh/git-jump-pathname-with-sp'Junio C Hamano1-1/+1
"git jump" (in contrib/) fails to parse the diff header correctly when a file has a space in its name, which has been corrected. * gh/git-jump-pathname-with-sp: git-jump: make `diff` work with filenames containing spaces
2025-08-22Merge branch 'ms/refs-list'Junio C Hamano11-2244/+2316
The "list" subcommand of "git refs" acts as a front-end for "git for-each-ref". * ms/refs-list: t: add test for git refs list subcommand t6300: refactor tests to be shareable builtin/refs: add list subcommand builtin/for-each-ref: factor out core logic into a helper builtin/for-each-ref: align usage string with the man page doc: factor out common option
2025-08-22doc: interpret-trailers: close all pairs of single quotesKristoffer Haugsbakk1-4/+4
Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-21Start 2.52 cycle, the first batchJunio C Hamano3-2/+60
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-21Merge branch 'ly/changed-path-traversal-with-magic-pathspec'Junio C Hamano2-17/+56
Revision traversal limited with pathspec, like "git log dir/*", used to ignore changed-paths Bloom filter when the pathspec contained wildcards; now they take advantage of the filter when they can. * ly/changed-path-traversal-with-magic-pathspec: bloom: enable bloom filter with wildcard pathspec in revision traversal
2025-08-21Merge branch 'en/ort-rename-fixes'Junio C Hamano2-30/+544
Various bugs about rename handling in "ort" merge strategy have been fixed. * en/ort-rename-fixes: merge-ort: fix directory rename on top of source of other rename/delete merge-ort: fix incorrect file handling merge-ort: clarify the interning of strings in opt->priv->path t6423: fix missed staging of file in testcases 12i,12j,12k t6423: document two bugs with rename-to-self testcases merge-ort: drop unnecessary temporary in check_for_directory_rename() merge-ort: update comments to modern testfile location
2025-08-21Merge branch 'ua/t1517-short-help-tests'Junio C Hamano3-13/+37
Test shuffling. * ua/t1517-short-help-tests: t5304: move `prune -h` test from t1517 t5200: move `update-server-info -h` test from t1517 t/t1517: automate `git subcmd -h` tests outside a repository
2025-08-21Merge branch 'rj/t6137-cygwin-fix'Junio C Hamano1-6/+6
Test fix for breakage introduced in Git 2.50. * rj/t6137-cygwin-fix: t6137-*.sh: fix test failure on cygwin
2025-08-21Merge branch 'kh/doc-git-log-markup-fix'Junio C Hamano1-2/+2
Doc update. * kh/doc-git-log-markup-fix: doc: git-log: fix description list
2025-08-21Merge branch 'dl/push-missing-object-error'Junio C Hamano2-59/+19
"git push" had a code path that led to BUG() but it should have been a die(), as it is a response to a usual but invalid end-user action to attempt pushing an object that does not exist. * dl/push-missing-object-error: remote.c: convert if-else ladder to switch remote.c: remove BUG in show_push_unqualified_ref_name_error() t5516: remove surrounding empty lines in test bodies
2025-08-21Merge branch 'jc/strbuf-split'Junio C Hamano8-166/+129
Arrays of strbuf is often a wrong data structure to use, and strbuf_split*() family of functions that create them often have better alternatives. Update several code paths and replace strbuf_split*(). * jc/strbuf-split: trace2: do not use strbuf_split*() trace2: trim_trailing_newline followed by trim is a no-op sub-process: do not use strbuf_split*() environment: do not use strbuf_split*() config: do not use strbuf_split() notes: do not use strbuf_split*() merge-tree: do not use strbuf_split*() clean: do not use strbuf_split*() [part 2] clean: do not pass the whole structure when it is not necessary clean: do not use strbuf_split*() [part 1] clean: do not pass strbuf by value wt-status: avoid strbuf_split*()
2025-08-21Merge branch 'jc/string-list-split'Junio C Hamano21-90/+225
string_list_split*() family of functions have been extended to simplify common use cases. * jc/string-list-split: string-list: split-then-remove-empty can be done while splitting string-list: optionally omit empty string pieces in string_list_split*() diff: simplify parsing of diff.colormovedws string-list: optionally trim string pieces split by string_list_split*() string-list: unify string_list_split* functions string-list: align string_list_split() with its _in_place() counterpart string-list: report programming error with BUG
2025-08-21Merge branch 'rs/describe-with-prio-queue'Junio C Hamano1-24/+63
"git describe" has been optimized by using better data structure. * rs/describe-with-prio-queue: describe: use prio_queue_replace() describe: use prio_queue
2025-08-21Merge branch 'ps/remote-rename-fix'Junio C Hamano19-200/+372
"git remote rename origin upstream" failed to move origin/HEAD to upstream/HEAD when origin/HEAD is unborn and performed other renames extremely inefficiently, which has been corrected. * ps/remote-rename-fix: builtin/remote: only iterate through refs that are to be renamed builtin/remote: rework how remote refs get renamed builtin/remote: determine whether refs need renaming early on builtin/remote: fix sign comparison warnings refs: simplify logic when migrating reflog entries refs: pass refname when invoking reflog entry callback
2025-08-21Merge branch 'ps/reflog-migrate-fixes'Junio C Hamano12-117/+413
"git refs migrate" to migrate the reflog entries from a refs backend to another had a handful of bugs squashed. * ps/reflog-migrate-fixes: refs: fix invalid old object IDs when migrating reflogs refs: stop unsetting REF_HAVE_OLD for log-only updates refs/files: detect race when generating reflog entry for HEAD refs: fix identity for migrated reflogs ident: fix type of string length parameter builtin/reflog: implement subcommand to write new entries refs: export `ref_transaction_update_reflog()` builtin/reflog: improve grouping of subcommands Documentation/git-reflog: convert to use synopsis type
2025-08-21Merge branch 'js/rebase-i-allow-drop-on-a-merge'Junio C Hamano2-0/+2
During interactive rebase, using 'drop' on a merge commit lead to an error, which was incorrect. * js/rebase-i-allow-drop-on-a-merge: rebase -i: permit 'drop' of a merge commit
2025-08-21git-gui: simplify using nice(1)Mark Levedahl1-23/+7
git-gui invokes some long running commands using "nice git $cmd" if nice is found and works, otherwise just "git $cmd". The current code is more complex than needed; let's simplify it. Signed-off-by: Mark Levedahl <mlevedahl@gmail.com> Signed-off-by: Johannes Sixt <j6t@kdbg.org>
2025-08-21git-gui: simplify PATH de-duplicationMark Levedahl1-5/+1
git-gui since 8fe7861c51 ("git-gui: assure PATH has only absolute elements.", 2025-04-11) uses a list to maintain order and a dict to detect duplicated elements without quadratic complexity. But, Tcl's dict explicitly maintains keys in the order first added, thus the list is not needed. Simplify the code. Signed-off-by: Mark Levedahl <mlevedahl@gmail.com> Signed-off-by: Johannes Sixt <j6t@kdbg.org>
2025-08-21config: document includeIf conditions consistentlyJunio C Hamano1-5/+4
When 399b1984 (config: include file if remote URL matches a glob, 2022-01-18) added the 'hasconfig:remote.*.url:<URL>' condition to be used in the "includeIf.<condition>.path" configuration, the keyword was added with an extra colon in the documentation. The section that documents these condition begins with this preamble: The condition starts with a keyword followed by a colon and some data whose format and meaning depends on the keyword. Supported keywords are: which makes it clear that the colon that comes between the condition keyword (e.g. "gitdir") and the parameter (aka "some data") is not a part of the keyword. Lose the extra colon. Also rewrite description of all keywords to clarify that "some data" does not directly follow "keyword", and the colon is not a part of keyword. Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-20Merge branch 'lo/repo-info' into lo/repo-info-step-2Junio C Hamano11-0/+337
* lo/repo-info: repo: add the --format flag repo: add the field layout.shallow repo: add the field layout.bare repo: add the field references.format repo: declare the repo command
2025-08-20doc: fix asciidoc format compatibility in pretty-formats.adocJean-Noël Avila1-2/+2
Asciidoc.py and Asciidoctor do not process the '+' verbatim the same way. A span is detected when the format sign (here '+')is preceded by a non-word character. It seems that '{nbsp}' is considered a non-word sign by Asciidoc.py, but not by Asciidoctor. Using a double format-sign opens 'unconstrained' span, independent on the preceding character in both engines. The '+' sign is used instead of the backtick '`' because it is not processed as synopsis in asciidoc.py. Unfortunately, the post-processing of verbatim synopsis in asciidoctor cannot be bypassed and formatting of the parentheses is forced in syntax sign instead of keywords, unless a proper grammar analyzer is used. Signed-off-by: Jean-Noël Avila <jn.avila@free.fr> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-20line-log: show all line ranges touched by the same diff rangeSZEDER Gábor5-0/+33
When line-level log is invoked with more than one disjoint line range in the same file, and one of the commits happens to change that file such that one diff range modifies more than one line range, then changes to all modified line ranges should be shown, but only the changes in the first modified line range are: $ git log --oneline -p 80ca903 (HEAD -> master) Initial diff --git a/file b/file new file mode 100644 index 0000000..00935f1 --- /dev/null +++ b/file @@ -0,0 +1,10 @@ +Line 1 +Line 2 +Line 3 +Line 4 +Line 5 +Line 6 +Line 7 +Line 8 +Line 9 +Line 10 $ git log --oneline -L1,2:file -L4,5:file -L7,8:file 80ca903 (HEAD -> master) Initial diff --git a/file b/file --- /dev/null +++ b/file @@ -0,0 +1,2 @@ +Line 1 +Line 2 The line-log-specific diff printer is already clever enough to handle the case when one line range covers multiple diff ranges, but the possibility of one diff range touching multiple disjoint line ranges was apparently overlooked. Add the necessary condition to dump_diff_hacky_one() to handle this case as well, and show all modified line ranges: $ git log --oneline -L1,2:file -L4,5:file -L7,8:file 0f9a5b4 (HEAD -> master) Initial diff --git a/file b/file --- /dev/null +++ b/file @@ -0,0 +1,2 @@ +Line 1 +Line 2 @@ -0,0 +4,2 @@ +Line 4 +Line 5 @@ -0,0 +7,2 @@ +Line 7 +Line 8 This bug was already present in the initial line-log implementation added in 2da1d1f6f (Implement line-history search (git log -L), 2013-03-28). Interestingly, that commit already contained a canned test case covering a similar scenario: "-L '/long f/',/^}/:a.c -L /main/,/^}/:a.c simple" This test case looks for two line ranges in the same file, and both trace back disjointly to the test repository's inital commit, therefore changes to both line ranges should have been shown for the initial commit, but only changes for the first line range are shown. So this test case should have failed from the very beginning, but it never did, because, unfortunately, the canned expected result is incorrect, as it doesn't include changes for the second line range. A similar test with a similarly incorrect canned expected result was added later in 209618860c (log -L: fix overlapping input ranges, 2013-04-05). Correct these two canned expected results to contain the changes for the second line range for the initial commit as well. Signed-off-by: SZEDER Gábor <szeder.dev@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-20line-log: fix assertion errorSZEDER Gábor4-3/+185
When line-level log is invoked with more than one disjoint line range in the same file, and one of the commits happens to change that file such that: - the last line of a line range R(n) immediately preceeds the first line modified or added by a hunk H, and - subtracting the number of lines added by hunk H from the start and end of the subsequent line range R(n+1) would result in a range overlapping with line range R(n), then git aborts with an assertion error, because those overlapping line ranges violate the invariants: $ git log --oneline -p 73e4e2f (HEAD -> master) Add lines 6 7 8 9 10 diff --git a/file b/file index 572d5d9..00935f1 100644 --- a/file +++ b/file @@ -3,3 +3,8 @@ Line 2 Line 3 Line 4 Line 5 +Line 6 +Line 7 +Line 8 +Line 9 +Line 10 66e3561 Add lines 1 2 3 4 5 diff --git a/file b/file new file mode 100644 index 0000000..572d5d9 --- /dev/null +++ b/file @@ -0,0 +1,5 @@ +Line 1 +Line 2 +Line 3 +Line 4 +Line 5 $ git log --oneline -L3,5:file -L7,8:file git: line-log.c:73: range_set_append: Assertion `rs->nr == 0 || rs->ranges[rs->nr-1].end <= a' failed. Aborted (core dumped) The line-log machinery encodes line and diff ranges internally as [start, end) pairs, i.e. include 'start' but exclude 'end', and line numbering starts at 0 (as opposed to the -LX,Y option, where it starts at 1, IOW the parameter -L3,5 is represented internally as { start = 2, end = 5 }). The reason for this assertion error and some related issues is that there are a couple of places where 'end' is mistakenly considered to be part of the range: - When a commit modifies an interesting path, the line-log machinery first checks which diff range (i.e. hunk) modify any line ranges. This is done in diff_ranges_filter_touched(), where the outer loop iterates over the diff ranges, and in each iteration the inner loop advances the line ranges supposedly until the current line range ends at or after the current diff range starts, and then the current diff and line ranges are checked for overlap. For HEAD in the above example the first line range [2, 5) ends just before the diff range [5, 10) starts, so the inner loop should advance, and then the second line range [6, 8) and the diff range should be checked for overlap. Unfortunately, the condition of the inner loop mistakenly considers 'end' as part of the line range, and, seeing the diff range starting at 5 and the line range ending at 5, it doesn't skip the first range. Consequently, the diff range and the first line range are checked for overlap, and after that the outer loop runs out of diff ranges, and then the processing goes on in the false belief that this commit didn't touch any of the interesting line ranges. The line-log machinery later shifts the line ranges to account for any added/removed lines in the diff ranges preceeding each line range. This leaves the first line range intact, but attempts to shift the second line range [6, 8) by 5 lines towards the beginning of the file, resulting in [1, 3), triggering the assertion error, because the two overlapping line ranges violate the invariants. Fix that loop condition in diff_ranges_filter_touched() to not treat 'end' as part of the line range. - With the above fix the assertion error is gone... but, alas, we now get stuck in an endless loop! This happens in range_set_difference(), where a couple of nested loops iterate over the line and diff ranges, and a condition is supposed to break the middle loop when the current line range ends before the current diff range, so processing could continue with the next line range. For HEAD in the above example the first line range [2, 5) ends just before the diff range [5, 10) starts, so this condition should trigger and break the middle loop. Unfortunately, just like in the case of the assertion error, this conditions mistakenly considers 'end' as part of the line range, and, seeing the line range ending at 5 and the diff range starting at 5, it doesn't break the loop, which will then go on and on. Fix this condition in range_set_difference() to not treat 'end' as part of the line range. - With the above fix the endless loop is gone... but, alas, the output is now wrong, as it shows both line ranges for HEAD, even though the first line range is not modified by that commit: $ git log --oneline -L3,5:file -L7,8:file 73e4e2f (HEAD -> master) Add lines 6 7 8 9 10 diff --git a/file b/file --- a/file +++ b/file @@ -3,3 +3,3 @@ Line 3 Line 4 Line 5 @@ -6,0 +7,2 @@ +Line 7 +Line 8 66e3561 Add lines 1 2 3 4 5 diff --git a/file b/file --- /dev/null +++ b/file @@ -0,0 +3,3 @@ +Line 3 +Line 4 +Line 5 In dump_diff_hacky_one() a couple of nested loops are responsible for finding and printing the modified line ranges: the big outer loop iterates over all line ranges, and the first inner loop skips over the diff ranges that end before the start of the current line range. This is followed by a condition checking whether the current diff range starts after the end of the current line range, which, when fulfilled, continues and advances the outer loop to the next line range. For HEAD in the above example the first line range [2, 5) ends just before the diff range [5, 10), so this condition should trigger, and the outer loop should advance to the second line range. Unfortunately, just like in the previous cases, this condition mistakenly considers 'end' as part of the line range, and, seeing the first line range ending at 5 and the diff range starting at 5, it doesn't continue to advance the outher loop, but goes on to show the (unmodified) first line range. Fix this condition to not treat 'end' as part of the line range, just like in the previous cases. After all this the command in the above example finally finishes and produces the right output: $ git log --oneline -L3,5:file -L7,8:file 73e4e2f (HEAD -> master) Add lines 6 7 8 9 10 diff --git a/file b/file --- a/file +++ b/file @@ -6,0 +7,2 @@ +Line 7 +Line 8 66e3561 Add lines 1 2 3 4 5 diff --git a/file b/file --- /dev/null +++ b/file @@ -0,0 +3,3 @@ +Line 3 +Line 4 +Line 5 Add a canned test similar to the above example, with the line ranges adjusted to the test repository's history. Reported-by: Evgeni Chasnovski <evgeni.chasnovski@gmail.com> Signed-off-by: SZEDER Gábor <szeder.dev@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-20doc: add discord to ways of getting helpDaniele Sassoli1-0/+9
Discord is a great way of receiving help for members of the community that are not on the mailing list or not familiar with Libera. Adding it to the official documentation will aid discoverability of it. The link is the same as the one at https://git-scm.com/community. Signed-off-by: Daniele Sassoli <danielesassoli@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-20describe: pass commit to describe_commit()Jeff King1-11/+9
There's a call in describe_commit() to lookup_commit_reference(), but we don't check the return value. If it returns NULL, we'll segfault as we immediately dereference the result. In practice this can never happen, since all callers pass an oid which came from a "struct commit" already. So we can make this more obvious by just taking that commit struct in the first place. Reported-by: Cheng <prophecheng@stu.pku.edu.cn> Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-20describe: handle blob traversal with no commitsJeff King2-2/+20
When describing a blob, we traverse from HEAD, remembering each commit we saw, and then checking each blob to report the containing commit. But if we haven't seen any commits at all, we'll segfault (we store the "current" commit as an oid initialized to the null oid, causing lookup_commit_reference() to return NULL). This shouldn't be able to happen normally. We always start our traversal at HEAD, which must be a commit (a property which is enforced by the refs code). But you can trigger the segfault like this: blob=$(echo foo | git hash-object -w --stdin) echo $blob >.git/HEAD git describe $blob We can instead catch this case and return an empty result, which hits the usual "we didn't find $blob while traversing HEAD" error. This is a minor lie in that we did "find" the blob. And this even hints at a bigger problem in this code: what if the traversal pointed to the blob as _not_ part of a commit at all, but we had previously filled in the recorded "current commit"? One could imagine this happening due to a tag pointing directly to the blob in question. But that can't happen, because we only traverse from HEAD, never from any other refs. And the intent of the blob-describing code is to find blobs within commits. So I think this matches the original intent as closely as we can (and again, this segfault cannot be triggered without corrupting your repository!). The test here does not use the formula above, which works only for the files backend (and not reftables). Instead we use another loophole to create the bogus state using only Git commands. See the comment in the test for details. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-20doc/gitk: update reference to the external projectJohannes Sixt1-4/+4
Gitk is now maintained by Johannes Sixt and the repository can be cloned from a new URL. b59358100c20 (Update the official repo of gitk, 2024-12-24) could have updated this instance in the manual, too, but the opportunity was missed. Update it now. Do give credit to Paul Mackerras as the inventor of the program. Signed-off-by: Johannes Sixt <j6t@kdbg.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-19refs: do not clobber dangling symrefsJeff King4-7/+87
When given an expected "before" state, the ref-writing code will avoid overwriting any ref that does not match that expected state. We use the null oid as a sentinel value for "nothing should exist", and likewise that is the sentinel value we get when trying to read a ref that does not exist. But there's one corner case where this is ambiguous: dangling symrefs. Trying to read them will yield the null oid, but there is potentially something of value there: the dangling symref itself. For a normal recursive write, this is OK. Imagine we have a symref "FOO_HEAD" that points to a ref "refs/heads/bar" that does not exist, and we try to write to it with a create operation like: oid=$(git rev-parse HEAD) ;# or whatever git symbolic-ref FOO_HEAD refs/heads/bar echo "create FOO_HEAD $oid" | git update-ref --stdin The attempt to resolve FOO_HEAD will actually resolve "bar", yielding the null oid. That matches our expectation, and the write proceeds. This is correct, because we are not writing FOO_HEAD at all, but writing its destination "bar", which in fact does not exist. But what if the operation asked not to dereference symrefs? Like this: echo "create FOO_HEAD $oid" | git update-ref --no-deref --stdin Resolving FOO_HEAD would still result in a null oid, and the write will proceed. But it will overwrite FOO_HEAD itself, removing the fact that it ever pointed to "bar". This case is a little esoteric; we are clobbering a symref with a no-deref write of a regular ref value. But the same problem occurs when writing symrefs. For example: echo "symref-create FOO_HEAD refs/heads/other" | git update-ref --no-deref --stdin The "create" operation asked us to create FOO_HEAD only if it did not exist. But we silently overwrite the existing value. You can trigger this without using update-ref via the fetch followRemoteHEAD code. In "create" mode, it should not overwrite an existing value. But if you manually create a symref pointing to a value that does not yet exist (either via symbolic-ref or with "remote add -m"), create mode will happily overwrite it. Instead, we should detect this case and refuse to write. The correct specification to overwrite FOO_HEAD in this case is to provide an expected target ref value, like: echo "symref-update FOO_HEAD refs/heads/other ref refs/heads/bar" | git update-ref --no-deref --stdin Note that the non-symref "update" directive does not allow you to do this (you can only specify an oid). This is a weakness in the update-ref interface, and you'd have to overwrite unconditionally, like: echo "update FOO_HEAD $oid" | git update-ref --no-deref --stdin Likewise other symref operations like symref-delete do not accept the "ref" keyword. You should be able to do: echo "symref-delete FOO_HEAD ref refs/heads/bar" but cannot (and can only delete unconditionally). This patch doesn't address those gaps. We may want to do so in a future patch for completeness, but it's not clear if anybody actually wants to perform those operations. The symref update case (specifically, via followRemoteHEAD) is what I ran into in the wild. The code for the fix is relatively straight-forward given the discussion above. But note that we have to implement it independently for the files and reftable backends. The "old oid" checks happen as part of the locking process, which is implemented separately for each system. We may want to factor this out somehow, but it's beyond the scope of this patch. (Another curiosity is that the messages in the reftable code are marked for translation, but the ones in the files backend are not. I followed local convention in each case, but we may want to harmonize this at some point). Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-19t5510: prefer "git -C" to subshell for followRemoteHEAD testsJeff King1-119/+83
These tests set config within a sub-repo using (cd two && git config), and then a separate test_when_finished outside the subshell to clean it up. We can't use test_config to do this, because the cleanup command it registers inside the subshell would be lost. Nor can we do it before entering the subshell, because the config has to be set after some other commands are run. Let's switch these tests to use "git -C" for each command instead of a subshell. That lets us use test_config (with -C also) at the appropriate part of the test. And we no longer need the manual cleanup command. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-19t5510: stop changing top-level working directoryJeff King1-195/+161
Several tests in t5510 do a bare "cd subrepo", not in a subshell. This changes the working directory for subsequent tests. As a result, almost every test has to start with "cd $D" to go back to the top-level. Our usual style is to do per-test environment changes like this in a subshell, so that tests can assume they are starting at the top-level $TRASH_DIRECTORY. Let's switch to that style, which lets us drop all of that extra path-handling. Most cases can switch to using a subshell, but in a few spots we can simplify by doing "git init foo && git -C foo ...". We do have to make sure that we weren't intentionally touching the environment in any code which was moved into a subshell (e.g., with a test_when_finished), but that isn't the case for any of these tests. All of the references to the $D variable can go away, replaced generally with $PWD or $TRASH_DIRECTORY (if we use it inside a chdir'd subshell). Note in one test, "fetch --prune prints the remotes url", we make sure to use $(pwd) to get the Windows-style path on that platform (for the other tests, the exact form doesn't matter). Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-19t5510: make confusing config cleanup more explicitJeff King1-9/+9
Several tests set a config variable in a sub-repo we chdir into via a subshell, like this: ( cd "$D" && cd two && git config foo.bar baz ) But they also clean up the variable with a when_finished directive outside of the subshell, like this: test_when_finished "git config unset foo.bar" At first glance, this shouldn't work! The cleanup clause cannot be run from the subshell (since environment changes there are lost by the time the test snippet finishes). But since the cleanup command runs outside the subshell, our working directory will not have been switched into "two". But it does work. Why? The answer is that an earlier test does a "cd two" that moves the whole test's working directory out of $TRASH_DIRECTORY and into "two". So the subshell is a bit of a red herring; we are already in the right directory! That's why we need the "cd $D" at the top of the shell, to put us back to a known spot. Let's make this cleanup code more explicitly specify where we expect the config command to run. That makes the script more robust against running a subset of the tests, and ultimately will make it easier to refactor the script to avoid these top-level chdirs. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-19doc: git-add: simplify discussion of ignored filesJulia Evans1-6/+4
- Mention the --force option earlier - Remove the explanation of shell globbing vs git's internal glob system, since users are confused by it and there's a clearer discussion in the EXAMPLES section. Signed-off-by: Julia Evans <julia@jvns.ca> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-19doc: git-add: clarify intro & add an exampleJulia Evans1-12/+12
- Add a basic example of how "git add" is normally used - It's not technically true that you *must* use the `add` command to add changes before running `git commit`, because `git commit -a` exists. Instead say that you *can* use the `add` command. - Mention early on that "index" is another word for "staging area", since Git very rarely uses the word "index" in its output (`git status`) uses the term "staged", and many Git users are unfamiliar with the term "index" - Remove "It typically adds" (it's not clear what "typically" means), and instead mention that `git add -p` can be used to add partial contents - Currently the introduction is somewhat repetitive ("to prepare the content staged for the next commit" ... "this snapshot that is taken as the contents of the next commit."), replace with a single sentence ("The "index" [...] is where Git stores the contents of the next commit.") Signed-off-by: Julia Evans <julia@jvns.ca> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-19t/t1517: mark tests that fail with GIT_TEST_INSTALLEDAdam Dinwoodie1-1/+4
The changes added by 39fc408562 (t/t1517: automate `git subcmd -h` tests outside a repository, 2025-08-08) to automatically loop over all "main" Git commands will, when run against an installed build using GIT_TEST_INSTALLED rather than the build in the build directory, include some extra git-gui commands that are installed by `make install`, or credential helpers that might be installed manually from the contrib directories. These fail the test, so record them as such. Signed-off-by: Adam Dinwoodie <adam@dinwoodie.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-18describe: catch unborn branch in describe_blob()Jeff King2-1/+15
When describing a blob, we search for it by traversing from HEAD. We do this by feeding the name HEAD to setup_revisions(). But if we are on an unborn branch, this will fail with a confusing message: $ git describe $blob fatal: ambiguous argument 'HEAD': unknown revision or path not in the working tree. Use '--' to separate paths from revisions, like this: 'git <command> [<revision>...] -- [<file>...]' It is OK for this to be an error (we cannot find $blob in an empty traversal, so we'd eventually complain about that). But the error message could be more helpful. Let's resolve HEAD ourselves and pass the resolved object id to setup_revisions(). If resolving fails, then we can print a more useful message. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-18describe: error if blob not foundJeff King2-0/+9
If describe_blob() does not find the blob in question, it returns an empty strbuf, and we print an empty line. This differs from describe_commit(), which always either returns an answer or calls die() itself. As the blob function was bolted onto the command afterwards, I think its behavior is not intentional, and it is just a bug that it does not report an error. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-18describe: pass oid struct by const pointerJeff King1-4/+4
We pass a "struct object_id" to describe_blob() by value. This isn't wrong, as an oid is composed only of copy-able values. But it's unusual; typically we pass structs by const pointer, including object_ids. Let's do so. It similarly makes sense for us to hold that pointer in the callback data (rather than yet another copy of the oid). Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-18xdiff: optimize xdl_hash_record_verbatimAlexander Monakov1-4/+55
xdl_hash_record_verbatim uses modified djb2 hash with XOR instead of ADD for combining. The ADD-based variant is used as the basis of the modern ("GNU") symbol lookup scheme in ELF. Glibc dynamic loader received an optimized version of this hash function thanks to Noah Goldstein [1]. Switch xdl_hash_record_verbatim to additive hashing and implement an optimized loop following the scheme suggested by Noah. Timing 'git log --oneline --shortstat v2.0.0..v2.5.0' under perf, I got version | cycles, bn | instructions, bn --------------------------------------- A 6.38 11.3 B 6.21 10.89 C 5.80 9.95 D 5.83 8.74 --------------------------------------- A: baseline (git master at e4ef0485fd78) B: plus 'xdiff: refactor xdl_hash_record()' C: and plus this patch D: with 'xdiff: use xxhash' by Phillip Wood The resulting speedup for xdl_hash_record_verbatim itself is about 1.5x. [1] https://inbox.sourceware.org/libc-alpha/20220519221803.57957-6-goldstein.w.n@gmail.com/ Signed-off-by: Alexander Monakov <amonakov@ispras.ru> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-17Git 2.51v2.51.0Junio C Hamano1-1/+1
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-17Merge tag 'l10n-2.51.0-2' of https://github.com/git-l10n/git-poJunio C Hamano10-4029/+6223
l10n-2.51.0-2 * tag 'l10n-2.51.0-2' of https://github.com/git-l10n/git-po: l10n: Update Catalan Translation for Git 2.51-rc2 l10n: zh_CN: updated translation for 2.51 l10n: uk: add 2.51 translation l10n: zh_TW: Git 2.51 l10n: po-id for 2.51 l10n: fr translation update for v2.51.0 l10n: tr: Update Turkish translations for 2.51.0 l10n: Updated translation for vi-2.51 l10n: sv.po: Update Swedish translation l10n: bg.po: Updated Bulgarian translation (5856t)
2025-08-17repo: add the --format flagLucas Seiki Oshiro3-16/+88
Add the --format flag to git-repo-info. By using this flag, the users can choose the format for obtaining the data they requested. Given that this command can be used for generating input for other applications and for being read by end users, it requires at least two formats: one for being read by humans and other for being read by machines. Some other Git commands also have two output formats, notably git-config which was the inspiration for the two formats that were chosen here: - keyvalue, where the retrieved data is printed one per line, using = for delimiting the key and the value. This is the default format, targeted for end users. - nul, where the retrieved data is separated by NUL characters, using the newline character for delimiting the key and the value. This format is targeted for being read by machines. Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk> Helped-by: Junio C Hamano <gitster@pobox.com> Helped-by: Justin Tobler <jltobler@gmail.com> Helped-by: Eric Sunshine <sunshine@sunshineco.com> Mentored-by: Karthik Nayak <karthik.188@gmail.com> Mentored-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-08-17repo: add the field layout.shallowLucas Seiki Oshiro3-0/+25
This commit is part of the series that introduces the new subcommand git-repo-info. The flag `--is-shallow-repository` from git-rev-parse is used for retrieving whether the repository is shallow. This way, it is used for querying repository metadata, fitting in the purpose of git-repo-info. Then, add a new field `layout.shallow` to the git-repo-info subcommand containing that information. Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk> Helped-by: Junio C Hamano <gitster@pobox.com> Helped-by: Justin Tobler <jltobler@gmail.com> Helped-by: Eric Sunshine <sunshine@sunshineco.com> Mentored-by: Karthik Nayak <karthik.188@gmail.com> Mentored-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-08-17repo: add the field layout.bareLucas Seiki Oshiro3-0/+30
This commit is part of the series that introduces the new subcommand git-repo-info. The flag --is-bare-repository from git-rev-parse is used for retrieving whether the current repository is bare. This way, it is used for querying repository metadata, fitting in the purpose of git-repo-info. Then, add a new field layout.bare to the git-repo-info subcommand containing that information. Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk> Helped-by: Junio C Hamano <gitster@pobox.com> Helped-by: Justin Tobler <jltobler@gmail.com> Helped-by: Eric Sunshine <sunshine@sunshineco.com> Mentored-by: Karthik Nayak <karthik.188@gmail.com> Mentored-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-08-17repo: add the field references.formatLucas Seiki Oshiro4-2/+146
This commit is part of the series that introduces the new subcommand git-repo-info. The flag `--show-ref-format` from git-rev-parse is used for retrieving the reference format (i.e. `files` or `reftable`). This way, it is used for querying repository metadata, fitting in the purpose of git-repo-info. Add a new field `references.format` to the repo-info subcommand containing that information. Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk> Helped-by: Junio C Hamano <gitster@pobox.com> Helped-by: Justin Tobler <jltobler@gmail.com> Helped-by: Eric Sunshine <sunshine@sunshineco.com> Mentored-by: Karthik Nayak <karthik.188@gmail.com> Mentored-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-08-17repo: declare the repo commandLucas Seiki Oshiro9-0/+66
Currently, `git rev-parse` covers a wide range of functionality not directly related to parsing revisions, as its name suggests. Over time, many features like parsing datestrings, options, paths, and others were added to it because there wasn't a more appropriate command to place them. Create a new Git command called `repo`. `git repo` will be the main command for obtaining the information about a repository (such as metadata and metrics). Also declare a subcommand for `repo` called `info`. `git repo info` will bring the functionality of retrieving repository-related information currently returned by `rev-parse`. Add the required documentation and build changes to enable usage of this subcommand. Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk> Helped-by: Junio C Hamano <gitster@pobox.com> Helped-by: Justin Tobler <jltobler@gmail.com> Helped-by: Eric Sunshine <sunshine@sunshineco.com> Mentored-by: Karthik Nayak <karthik.188@gmail.com> Mentored-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-08-17cmake: accommodate for `UNIT_TEST_SOURCES`Johannes Schindelin1-1/+3
As part of 9bbc981c6f2 (t/unit-tests: finalize migration of reftable-related tests, 2025-07-24), the explicit list of `UNIT_TEST_PROGRAMS` was turned into a wildcard pattern-derived list. Let's do the same in the CMake definition. This fixes build errors with symptoms like this: CMake Error at CMakeLists.txt:132 (string): string sub-command REPLACE requires at least four arguments. Call Stack (most recent call first): CMakeLists.txt:1037 (parse_makefile_for_scripts) Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-17l10n: Update Catalan Translation for Git 2.51-rc2Mikel Forcada1-993/+1803
Edit: We are continuing to follow the existing PO file convention, which includes filenames but strips out line numbers from the file-location comments. This standard was set by our former lead, Jordi Mas, and we are maintaining it for project-wide consistency. Signed-off-by: Mikel Forcada <mikel.forcada@gmail.com> Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
2025-08-17Merge branch 'jx/zh_CN-2.51' of github.com:jiangxin/gitJiang Xin1-248/+444
* 'jx/zh_CN-2.51' of github.com:jiangxin/git: l10n: zh_CN: updated translation for 2.51
2025-08-17l10n: zh_CN: updated translation for 2.51Teng Long1-248/+444
Signed-off-by: Teng Long <dyroneteng@gmail.com> Reviewed-by: Fangyi Zhou <me@fangyi.io> Reviewed-by: 依云 <lilydjwg@gmail.com> Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
2025-08-17Merge branch '2.51-uk-update' of github.com:arkid15r/git-ukrainian-l10nJiang Xin1-181/+347
* '2.51-uk-update' of github.com:arkid15r/git-ukrainian-l10n: l10n: uk: add 2.51 translation
2025-08-16l10n: uk: add 2.51 translationArkadii Yakovets1-181/+347
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-08-16Merge branch 'fr_v2.51.0' of github.com:jnavila/gitJiang Xin1-463/+345
* 'fr_v2.51.0' of github.com:jnavila/git: l10n: fr translation update for v2.51.0
2025-08-16Merge branch 'po-id' of github.com:bagasme/git-poJiang Xin1-235/+431
* 'po-id' of github.com:bagasme/git-po: l10n: po-id for 2.51
2025-08-16Merge branch 'tr-l10n' of github.com:bitigchi/git-poJiang Xin1-173/+332
* 'tr-l10n' of github.com:bitigchi/git-po: l10n: tr: Update Turkish translations for 2.51.0
2025-08-16Merge branch 'l10n/zh-TW/2025-08-08' of github.com:l10n-tw/git-poJiang Xin1-393/+617
* 'l10n/zh-TW/2025-08-08' of github.com:l10n-tw/git-po: l10n: zh_TW: Git 2.51
2025-08-16Merge branch 'master' of github.com:alshopov/git-poJiang Xin1-210/+387
* 'master' of github.com:alshopov/git-po: l10n: bg.po: Updated Bulgarian translation (5856t)
2025-08-16Merge branch 'master' of github.com:nafmo/git-l10n-svJiang Xin1-524/+560
* 'master' of github.com:nafmo/git-l10n-sv: l10n: sv.po: Update Swedish translation
2025-08-16Merge branch 'vi-2.51' of github.com:Nekosha/git-poJiang Xin1-609/+957
* 'vi-2.51' of github.com:Nekosha/git-po: l10n: Updated translation for vi-2.51
2025-08-16l10n: zh_TW: Git 2.51Yi-Jyun Pan1-393/+617
Co-authored-by: Lumynous <lumynou5.tw@gmail.com> Co-authored-by: hms5232 <hms5232@hhming.moe> Signed-off-by: Yi-Jyun Pan <pan93412@gmail.com>
2025-08-15commit-graph: stop passing in redundant repositoryPatrick Steinhardt4-81/+59
Many of the commit-graph related functions take in both a repository and the object database source (directly or via `struct commit_graph`) for which we are supposed to load such a commit-graph. In the best case this information is simply redundant as the source already contains a reference to its owning object database, which in turn has a reference to its repository. In the worst case this information could even mismatch when passing in a source that doesn't belong to the same repository. Refactor the code so that we only pass in the object database source in those cases. There is one exception though, namely `load_commit_graph_chain_fd_st()`, which is responsible for loading a commit-graph chain. It is expected that parts of the commit-graph chain aren't located in the same object source as the chain file itself, but in a different one. Consequently, this function doesn't work on the source level but on the database level instead. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-15commit-graph: stop using `the_repository`Patrick Steinhardt4-40/+42
There's still a bunch of uses of `the_repository` in "commit-graph.c", which we want to stop using due to it being a global variable. Refactor the code to stop using `the_repository` in favor of the repository provided via the calling context. This allows us to drop the `USE_THE_REPOSITORY_VARIABLE` macro. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-15commit-graph: stop using `the_hash_algo`Patrick Steinhardt3-15/+18
Stop using `the_hash_algo` as it implicitly relies on `the_repository`. Instead, we either use the hash algo provided via the context or, if there is no such hash algo, we use `the_repository` explicitly. Such uses will be removed in subsequent commits. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-15commit-graph: refactor `parse_commit_graph()` to take a repositoryPatrick Steinhardt3-16/+15
Refactor `parse_commit_graph()` so that it takes a repository instead of taking repository settings. On the one hand this allows us to get rid of instances where we access `the_hash_algo` by using the repository's hash algorithm instead. On the other hand it also allows us to move the call of `prepare_repo_settings()` into the function itself. Note that there's one small catch, as the commit-graph fuzzer calls this function directly without having a fully functional repository at hand. And while the fuzzer already initializes `the_repository` with relevant info, the call to `prepare_repo_settings()` would fail because we don't have a fully-initialized repository. Work around the issue by also settings `settings.initialized` to pretend that we've already read the settings. While at it, remove the redundant `parse_commit_graph()` declaration in the fuzzer. It was added together with aa658574bf (commit-graph, fuzz: add fuzzer for commit-graph, 2019-01-15), but as we also declared the same function in "commit-graph.h" it wasn't ever needed. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-15commit-graph: store the hash algorithm instead of its lengthPatrick Steinhardt2-19/+19
The commit-graph stores the length of the hash algorithm it uses. In subsequent commits we'll need to pass the whole hash algorithm around though, which we currently don't have access to. Refactor the code so that we store the hash algorithm instead of only its size. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-15commit-graph: stop using `the_hash_algo` via macrosPatrick Steinhardt1-9/+16
We have two macros `GRAPH_DATA_WIDTH` and `GRAPH_MIN_SIZE` that compute hash-dependent sizes. They do so by using the global `the_hash_algo` variable though, which we want to get rid of over time. Convert these macros into functions that accept the hash algorithm as input parameter. Adapt callers accordingly. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-15l10n: po-id for 2.51Bagas Sanjaya1-235/+431
Update following components: * add-interactive.c * builtin/add.c * builtin/config.c * builtin/fetch.c * builtin/for-each-ref.c * builtin/gc.c * builtin/merge.c * builtin/pack-objects.c * builtin/remote.c * builtin/repack.c * builtin/stash.c * builtin/submodule--helper.c * diff-no-index.c * git-send-email.perl * imap-send.c * parse-options.c * refs.c * t/helper/test-path-walk.c * usage.c Signed-off-by: Bagas Sanjaya <bagasdotme@gmail.com>
2025-08-14l10n: fr translation update for v2.51.0Jean-Noël Avila1-463/+345
Signed-off-by: Jean-Noël Avila <jn.avila@free.fr>
2025-08-14count-objects: document count-objects packDaniele Sassoli1-0/+2
0bdaa12169b (git-count-objects.txt: describe each line in -v output, 2013-02-08) forgot to include `packs`. Signed-off-by: Daniele Sassoli <danielesassoli@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-14abbrev: allow extending beyond 32 chars to disambiguateJunio C Hamano1-1/+1
When you have two or more objects with object names that share more than 32 letters in an SHA-1 repository, find_unique_abbrev() fails to show disambiguation. To see how many leading letters of a given full object name is sufficiently unambiguous, the algorithm starts from a initial length, guessed based on the estimated number of objects in the repository, and see if another object that shares the prefix, and keeps extending the abbreviation. The loop stops at GIT_MAX_RAWSZ, which is counted as the number of bytes, since 5b20ace6 (sha1_name: unroll len loop in find_unique_abbrev_r(), 2017-10-08); before that change, it extended up to GIT_SHA1_HEXSZ, which meant to stop at the end of hexadecimal SHA-1 object name. Because the hexadecimal object name passed to the function is NUL-terminated, and this fact is used to correctly terminate the loop that scans for the first difference earlier in the function, use it to make sure we do not increment the .cur_len member beyond the end of the string. Noticed-by: Jon Forrest <nobozo@gmail.com> Helped-by: René Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-14l10n: tr: Update Turkish translations for 2.51.0Emir SARI1-173/+332
Signed-off-by: Emir SARI <emir_sari@icloud.com>
2025-08-14l10n: Updated translation for vi-2.51Vũ Tiến Hưng1-609/+957
Signed-off-by: Vũ Tiến Hưng <newcomerminecraft@gmail.com>
2025-08-14l10n: sv.po: Update Swedish translationPeter Krefting1-524/+560
Also fix typo reported by Tuomas Ahola <taahol@utu.fi>. Signed-off-by: Peter Krefting <peter@softwolves.pp.se>
2025-08-13l10n: bg.po: Updated Bulgarian translation (5856t)Alexander Shopov1-210/+387
Signed-off-by: Alexander Shopov <ash@kambanaria.org>
2025-08-13t7005: sanitize test environment for subsequent testsD. Ben Knoble1-44/+39
Some of the editor tests manipulate the environment or config in ways that affect future tests, but those modifications are visible to future tests and create a footgun for them. Use test_config, subshells, single-command environment overrides, and test helpers to automatically undo environment and config modifications once finished. Best-viewed-with: --ignore-all-space Signed-off-by: D. Ben Knoble <ben.knoble+github@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-13t7005: stop abusing --exec-pathD. Ben Knoble1-2/+2
We want the editors in this test on PATH, so put them there. Signed-off-by: D. Ben Knoble <ben.knoble+github@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-13t7005: use modern test styleD. Ben Knoble1-46/+22
Tests in t7005 mask Git error codes and do not use our nice test helpers. Improve that, move some code into the setup test, and drop a few old-style blank lines while at it. Best-viewed-with: --ignore-all-space Signed-off-by: D. Ben Knoble <ben.knoble+github@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-13Git 2.51-rc2v2.51.0-rc2Junio C Hamano1-1/+1
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-12send-email: enable copying emails to an IMAP folder without actually sending ↵Aditya Garg3-1/+23
them `git imap-send` was built on the idea of copying emails to an IMAP folder like drafts, and sending them later using an email client. Currently the only way to do it is by piping output of `git format-patch` to IMAP send. Add another way to do it by using `git send-email` with the `--use-imap-only` or `sendmail.useImapOnly` option. This allows users to use the advanced features of `git send-email` like tweaking Cc: list programmatically, compose the cover letter, etc. and then send the well formatted emails to an IMAP folder using `git imap-send`. While at it, use `` instead of '' for --smtp-encryption ssl in help section of `git send-email`. Signed-off-by: Aditya Garg <gargaditya08@live.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-12send-email: add ability to send a copy of sent emails to an IMAP folderAditya Garg4-9/+61
Some email providers like Apple iCloud Mail do not support sending a copy of sent emails to the "Sent" folder if SMTP server is used. As a workaround, various email clients like Thunderbird which rely on SMTP, use IMAP to send a copy of sent emails to the "Sent" folder. Something similar can be done if sending emails via `git send-email`, by using the `git imap-send` command to send a copy of the sent email to an IMAP folder specified by the user. Add this functionality to `git send-email` by introducing a new configuration variable `sendemail.imapfolder` and command line option `--imap-folder` which specifies the IMAP folder to send a copy of the sent emails to. If specified, a copy of the sent emails will be sent by piping the emails to `git imap-send` command, after all emails are sent via SMTP and the SMTP server has been closed. Signed-off-by: Aditya Garg <gargaditya08@live.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-12docs: remove stray bracket from git-clone synopsisKnut Harald Ryager1-1/+1
The synopsis section has an extra closing bracket, like this: [--filter=<filter>] [--also-filter-submodules]] The extra one is not the one at the end of this line; it is the one after "...=<filter>". The "--also-filter-submodules" option was added by f05da2b4 (clone, submodule: pass partial clone filters to submodules, 2022-02-04). Because it makes sense only when used with the "--filter=<filter>" option, these two options are enclosed in a pair of brackets. The extra one was added by 76880f05 (doc: git-clone: apply new documentation formatting guidelines, 2024-03-29) by mistake. Remove the extra and incorrect closing bracket, so that the line reads: [--filter=<filter> [--also-filter-submodules]] Signed-off-by: Knut Harald Ryager <e-k-nut@hotmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-12refs/reftable: always reload stacks when creating lockPatrick Steinhardt1-11/+12
When creating a new addition via either `reftable_stack_new_addition()` or its convenince wrapper `reftable_stack_add()` we: 1. Create the "tables.list.lock" file. 2. Verify that the current version of the "tables.list" file is up-to-date. 3. Write the new table records if so. By default, the second step would cause us to bail out if we see that there has been a concurrent write to the stack that made our in-memory copy of the stack out-of-date. This is a safety mechanism to not write records to the stack based on outdated information. The downside though is that concurrent writes may now cause us to bail out, which is not a good user experience. In addition, this isn't even necessary for us, as Git knows to perform all checks for the old state of references under the lock. (Well, in all except one case: when we expire the reflog we first create the log iterator before we create the lock, but this ordering is fixed as part of this commit.) Consequently, most writers pass the `REFTABLE_STACK_NEW_ADDITION_RELOAD` flag. The effect of this flag is that we reload the stack after having acquired the lock in case the stack is out-of-date. This plugs the race with concurrent writers, but we continue performing the verifications of the expected old state to catch actual conflicts in the references we are about to write. Adapt the remaining callsites that don't yet pass this flag to do so. While at it, drop a needless manual reload. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-12reftable: don't second-guess errors from flock interfacePatrick Steinhardt3-31/+12
The `flock` interface is implemented as part of "reftable/system.c" and thus needs to be implemented by the integrator between the reftable library and its parent code base. As such, we cannot rely on any specific implementation thereof. Regardless of that, users of the `flock` subsystem rely on `errno` being set to specific values. This is fragile and not documented anywhere and doesn't really make for a good interface. Refactor the code so that the implementations themselves are expected to return reftable-specific error codes. Our implementation of the `flock` subsystem already knows to do this for all error paths except one. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-12reftable/stack: handle outdated stacks when compactingPatrick Steinhardt1-6/+26
When we compact the reftable stack we first acquire the lock for the "tables.list" file and then reload the stack to check that it is still up-to-date. This is done by calling `stack_uptodate()`, which knows to return zero in case the stack is up-to-date, a positive value if it is not and a negative error code on unexpected conditions. We don't do proper error checking though, but instead we only check whether the returned error code is non-zero. If so, we simply bubble it up the calling stack, which means that callers may see an unexpected positive value. Fix this issue by translating to `REFTABLE_OUTDATED_ERROR` instead. Handle this situation in `reftable_addition_commit()`, where we perform a best-effort auto-compaction. All other callsites of `stack_uptodate()` know to handle a positive return value and thus don't need to be fixed. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-12reftable/stack: allow passing flags to `reftable_stack_add()`Patrick Steinhardt4-36/+39
The `reftable_stack_add()` function is a simple wrapper to lock the stack, add records to it via a callback and then commit the result. One problem with it though is that it doesn't accept any flags for creating the addition. This makes it impossible to automatically reload the stack in case it was modified before we managed to lock the stack. Add a `flags` field to plug this gap and pass it through accordingly. For now this new flag won't be used by us, but it will be used by libgit2. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-12reftable/stack: fix compiler warning due to missing bracesPatrick Steinhardt1-7/+7
While perfectly legal, older compiler toolchains complain when zero-initializing structs that contain nested structs with `{0}`: /home/libgit2/source/deps/reftable/stack.c:862:35: error: suggest braces around initialization of subobject [-Werror,-Wmissing-braces] struct reftable_addition empty = REFTABLE_ADDITION_INIT; ^~~~~~~~~~~~~~~~~~~~~~ /home/libgit2/source/deps/reftable/stack.c:707:33: note: expanded from macro 'REFTABLE_ADDITION_INIT' #define REFTABLE_ADDITION_INIT {0} ^ We had the discussion around whether or not we want to handle such bogus compiler errors in the past already [1]. Back then we basically decided that we do not care about such old-and-buggy compilers, so while we could fix the issue by using `{{0}}` instead this is not the preferred way to handle this in the Git codebase. We have an easier fix though: we can just drop the macro altogether and handle initialization of the struct in `reftable_stack_addition_init()`. Callers are expected to call this function already, so this change even simplifies the calling convention. [1]: https://lore.kernel.org/git/20220710081135.74964-1-sunshine@sunshineco.com/T/ Suggested-by: Carlo Arenas <carenas@gmail.com> Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-12reftable/stack: reorder code to avoid forward declarationsPatrick Steinhardt1-188/+176
We have a couple of forward declarations in the stack-related code of the reftable library. These declarations aren't really required, but are simply caused by unfortunate ordering. Reorder the code and remove the forward declarations. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-12reftable/writer: drop Git-specific `QSORT()` macroPatrick Steinhardt1-2/+4
The reftable writer accidentally uses the Git-specific `QSORT()` macro. This macro removes the need for the caller to provide the element size, but other than that it's mostly equivalent to `qsort()`. Replace the macro accordingly to make the library usable outside of Git. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-12reftable/writer: fix type used for number of recordsPatrick Steinhardt2-10/+11
Both `reftable_writer_add_refs()` and `reftable_writer_add_logs()` accept an array of records that should be added to the new table. Callers of this function are expected to also pass the number of such records to the function to tell it how many such records it is supposed to write. But while all callers pass in a `size_t`, which is a sensible choice, the function in fact accepts an `int` as argument, which is less so. Fix this. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-11A few hotfixes before -rc2Junio C Hamano0-0/+0
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-11Merge branch 'rs/merge-compact-summary'Junio C Hamano1-1/+1
Hotfix. * rs/merge-compact-summary: merge: don't document non-existing --compact-summary argument
2025-08-11Merge branch 'rs/for-each-ref-start-after-marker-fix'Junio C Hamano1-1/+1
Hotfix. * rs/for-each-ref-start-after-marker-fix: for-each-ref: call --start-after argument "marker"
2025-08-11doc lint: check that synopsis manpages have synopsis inlinesJean-Noël Avila3-11/+17
When switching manpages to the synopsis style, the description lists of options need to be switched to inline synopsis for proper formatting. This is done by enclosing the option name in double backticks, e.g. `--option`. Signed-off-by: Jean-Noël Avila <jn.avila@free.fr> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-11doc:git-for-each-ref: fix styling and typosJean-Noël Avila1-132/+132
This commit fixes the synopsis syntax and changes the wording of a few descriptions to be more consistent with the rest of the documentation. It is a prepartion for the next commit that checks that synopsis style is applied consistently across a manual page. Signed-off-by: Jean-Noël Avila <jn.avila@free.fr> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-11doc: check for absence of the form --[no-]parameterJean-Noël Avila36-78/+159
For better searchability, this commit adds a check to ensure that parameters expressed in the form of `--[no-]parameter` are not used in the documentation. In the place of such parameters, the documentation should list two separate parameters: `--parameter` and `--no-parameter`. Signed-off-by: Jean-Noël Avila <jn.avila@free.fr> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-11doc: check for absence of multiple terms in each entry of desc listJean-Noël Avila7-6/+51
For simplifying automated translation of the documentation, it is better to only present one term in each entry of a description list of options. This is because most of these terms can automatically be marked as notranslatable. Also, due to portability issues, the script generate-configlist.sh can no longer insert newlines in the output. However, the result is that it no longer correctly handles multiple terms in a single entry of definition lists. As a result, we now check that these entries do not exist in the documentation. Reviewed-by: Collin Funk <collin.funk1@gmail.com> Signed-off-by: Jean-Noël Avila <jn.avila@free.fr> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-11doc: check well-formedness of delimited sectionsJean-Noël Avila14-4/+80
Having an empty line before each delimited sections is not required by asciidoc, but it is a safety measure that prevents generating malformed asciidoc when generating translated documentation. When a delimited section appears just after a paragraph, the asciidoc processor checks that the length of the delimited section header is different from the length of the paragraph. If it is not, the asciidoc processor will generate a title. In the original English documentation, this is not a problem because the authors always check the output of the asciidoc processor and fix the length of the delimited section header if it turns out to be the same as the paragraph length. However, this is not the case for translations, where the authors have no way to check the length of the delimited section header or the output of the asciidoc processor. This can lead to a section title that is not intended. Indeed, this test also checks that titles are correctly formed, that is, the length of the underline is equal to the length of the title (otherwise it would not be a title but a section header). Finally, this test checks that the delimited section are terminated within the same file. Signed-off-by: Jean-Noël Avila <jn.avila@free.fr> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-11doc: test linkgit macros for well-formednessJean-Noël Avila2-1/+8
Some readers of man pages have reported that they found malformed linkgit macros in the documentation (absence or bad spelling). Signed-off-by: Jean-Noël Avila <jn.avila@free.fr> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-11midx: compute paths via their sourcePatrick Steinhardt5-75/+62
With the preceding commits we started to always have the object database source available when we load, write or access multi-pack indices. With this in place we can change how MIDX paths are computed so that we don't have to pass in the combination of a hash algorithm and object directory anymore, but only the object database source. Refactor the code accordingly. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-11midx: stop duplicating info redundant with its owning sourcePatrick Steinhardt7-35/+36
Multi-pack indices store some information that is redundant with their owning source: - The locality bit that tracks whether the source is the primary object source or an alternate. - The object directory path the multi-pack index is located in. - The pointer to the owning parent directory. All of this information is already contained in `struct odb_source`. So now that we always have that struct available when loading a multi-pack index we have it readily accessible. Drop the redundant information and instead store a pointer to the object source. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-11midx: write multi-pack indices via their sourcePatrick Steinhardt4-49/+47
Similar to the preceding commit, refactor the writing side of multi-pack indices so that we pass in the object database source where the index should be written to. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-11midx: load multi-pack indices via their sourcePatrick Steinhardt5-52/+62
To load a multi-pack index the caller is expected to pass both the repository and the object directory where the multi-pack index is located. While this works, this layout has a couple of downsides: - We need to pass in information reduntant with the owning source, namely its object directory and whether the source is local or not. - We don't have access to the source when loading the multi-pack index. If we had that access, we could store a pointer to the owning source in the MIDX and thus deduplicate some information. - Multi-pack indices are inherently specific to the object source and its format. With the goal of pluggable object backends in mind we will eventually want the backends to own the logic of reading and writing multi-pack indices. Making the logic work on top of object sources is a step into that direction. Refactor loading of multi-pack indices accordingly. This surfaces one small problem though: git-multi-pack-index(1) and our MIDX test helper both know to read and write multi-pack-indices located in a different object directory. This issue is addressed by adding the user-provided object directory as an in-memory alternate. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-11midx: drop redundant `struct repository` parameterPatrick Steinhardt7-28/+26
There are a couple of functions that take both a `struct repository` and a `struct multi_pack_index`. This provides redundant information though without much benefit given that the multi-pack index already has a pointer to its owning repository. Drop the `struct repository` parameter from such functions. While at it, reorder the list of parameters of `fill_midx_entry()` so that the MIDX comes first to better align with our coding guidelines. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-11odb: simplify calling `link_alt_odb_entry()`Patrick Steinhardt1-18/+7
Callers of `link_alt_odb_entry()` are expected to pass in three different paths: - The (potentially relative) path of the object directory that we're about to add. - The base that should be used to resolve a relative object directory path. - The resolved path to the object database's objects directory. Juggling those three paths makes the calling convention somewhat hard to grok at first. As it turns out, the third parameter is redundant: we always pass in the resolved path of the object database's primary source, and we already pass in the database itself. So instead, we can resolve that path in the function itself. One downside of this is that one caller of `link_alt_odb_entry()` calls this function in a loop, so we were able to resolve the directory a single time, only. But ultimately, we only ever end up with a rather limited number of alternates anyway, so the extra couple of cycles we save feels more like a micro optimization. Refactor the code accordingly. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-11odb: return newly created in-memory sourcesPatrick Steinhardt2-14/+20
Callers have no trivial way to obtain the newly created object database source when adding it to the in-memory list of alternates. While not yet needed anywhere, a subsequent commit will want to obtain that pointer. Refactor the function to return the source to make it easily accessible. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-11odb: consistently use "dir" to refer to alternate's directoryPatrick Steinhardt1-16/+14
The functions that add an alternate object directory to the object database are somewhat inconsistent in how they call the paramater that refers to the directory path: in our headers we refer to it as "dir", whereas in the implementation we often call it "reference" or "entry". Unify this and consistently call the parameter "dir". While at it, refactor `link_alt_odb_entry()` to accept a C string instead of a `struct strbuf` as parameter to clarify that we really only need the path and nothing else. Suggested-by: Taylor Blau <me@ttaylorr.com> Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-11odb: allow `odb_find_source()` to failPatrick Steinhardt4-5/+14
When trying to locate a source for an unknown object directory we will die right away. In subsequent patches we will add new callsites though that want to handle this situation gracefully instead. Refactor the function to return a `NULL` pointer if the source could not be found and adapt the callsites to die instead. Introduce a new wrapper `odb_find_source_or_die()` that continues to die in case the source could not be found. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-11odb: store locality in object database sourcesPatrick Steinhardt6-8/+18
Object database sources are classified either as: - Local, which means that the source is the repository's primary source. This is typically ".git/objects". - Non-local, which is everything else. Most importantly this includes alternates and quarantine directories. This locality is often computed ad-hoc by checking whether a given object source is the first one. This works, but it is quite roundabout. Refactor the code so that we store locality when creating the sources in the first place. This makes it both more accessible and robust. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-11builtin/fmt-merge-msg: stop depending on 'the_repository'Ayush Chandekar2-3/+9
Refactor builtin/fmt-merge-msg.c to remove the dependancy on the global 'the_repository'. Remove the 'UNUSED' macro from the 'struct repository' parameter and replace 'git_config()' with 'repo_config()' so that configuration is read from the passed repository. Also, add a test to make sure that "git fmt-merge-msg -h" can be called outside a repository. Mentored-by: Christian Couder <christian.couder@gmail.com> Mentored-by: Ghanshyam Thakkar <shyamthakkar001@gmail.com> Signed-off-by: Ayush Chandekar <ayu.chandekar@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-11environment: remove the global variable 'merge_log_config'Ayush Chandekar5-8/+10
The global variable 'merge_log_config', set via the "merge.log" or "merge.summary" settings, is only used in 'cmd_fmt_merge_msg()' and 'cmd_merge()' to adjust the 'shortlog_len' variable. Remove 'merge_log_config' globally and localize it in 'cmd_fmt_merge_msg()' and 'cmd_merge()'. Set its value by passing it in 'fmt_merge_msg_config()' by passing its pointer to the function via the callback parameter. This change is part of an ongoing effort to eliminate global variables, improve modularity and help libify the codebase. Mentored-by: Christian Couder <christian.couder@gmail.com> Mentored-by: Ghanshyam Thakkar <shyamthakkar001@gmail.com> Signed-off-by: Ayush Chandekar <ayu.chandekar@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-11git-jump: make `diff` work with filenames containing spacesGreg Hurrell1-1/+1
In diff.c, we output a trailing "\t" at the end of any filename that contains a space: case DIFF_SYMBOL_FILEPAIR_PLUS: meta = diff_get_color_opt(o, DIFF_METAINFO); reset = diff_get_color_opt(o, DIFF_RESET); fprintf(o->file, "%s%s+++ %s%s%s\n", diff_line_prefix(o), meta, line, reset, strchr(line, ' ') ? "\t" : ""); break; That is, for a file "foo.txt", `git diff --no-prefix` will emit: +++ foo.txt but for "foo bar.txt" it will emit: +++ foo bar.txt\t This in turn leads `git-jump` to produce a quickfix format like this: foo bar.txt\t:1:1:contents Because no "foo bar.txt\t" file actually exists on disk, opening it in Vim will just land the user in an empty buffer. This commit takes the simple approach of unconditionally stripping any trailing tab. Consider the following three examples: 1. For file "foo", Git will emit "foo". 2. For file "foo bar", Git will emit "foo bar\t". 3. For file "foo\t", Git will emit "\"foo\t\"". 4. For file "foo bar\t", Git will emit "\"foo bar\t\"". Before this commit, `git-jump` correctly handled only case "1". After this commit, `git-jump` correctly handles cases "1" and "2". In reality, these are the only cases people are going to run into with any regularity, and the other two are rare edge cases, which probably aren't worth the effort to support unless somebody actually complains about them. Signed-off-by: Greg Hurrell <greg.hurrell@datadoghq.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-11bloom: enable bloom filter with wildcard pathspec in revision traversalLidong Yan2-17/+56
When traversing commits, a pathspec item can be used to limit the traversal to commits that modify the specified paths. And the commit-graph includes a Bloom filter to exclude commits that definitely did not modify a given pathspec item. During commit traversal, the Bloom filter can significantly improve performance. However, it is disabled if the specified pathspec item contains wildcard characters or magic signatures. For performance reason, enable Bloom filter even if a pathspec item contains wildcard characters by filtering only the non-wildcard part of the pathspec item. The function of pathspec magic signature is generally to narrow down the path specified by the pathspecs. So, enable Bloom filter when the magic signature is "top", "glob", "attr", "--depth" or "literal". "exclude" is used to select paths other than the specified path, rather than serving as a filtering function, so it cannot be used together with the Bloom filter. Since Bloom filter is not case insensitive even in case insensitive system (e.g. MacOS), it cannot be used together with "icase" magic. With this optimization, we get some improvements for pathspecs with wildcards or magic signatures. First, in the Git repository we see these modest results: git log -100 -- "t/*" Benchmark 1: new Time (mean ± σ): 20.4 ms ± 0.6 ms Range (min … max): 19.3 ms … 24.4 ms Benchmark 2: old Time (mean ± σ): 23.4 ms ± 0.5 ms Range (min … max): 22.5 ms … 24.7 ms git log -100 -- ":(top)t" Benchmark 1: new Time (mean ± σ): 16.2 ms ± 0.4 ms Range (min … max): 15.3 ms … 17.2 ms Benchmark 2: old Time (mean ± σ): 18.6 ms ± 0.5 ms Range (min … max): 17.6 ms … 20.4 ms But in a larger repo, such as the LLVM project repo below, we get even better results: git log -100 -- "libc/*" Benchmark 1: new Time (mean ± σ): 16.0 ms ± 0.6 ms Range (min … max): 14.7 ms … 17.8 ms Benchmark 2: old Time (mean ± σ): 26.7 ms ± 0.5 ms Range (min … max): 25.4 ms … 27.8 ms git log -100 -- ":(top)libc" Benchmark 1: new Time (mean ± σ): 15.6 ms ± 0.6 ms Range (min … max): 14.4 ms … 17.7 ms Benchmark 2: old Time (mean ± σ): 19.6 ms ± 0.5 ms Range (min … max): 18.6 ms … 20.6 ms Helped-by: Junio C Hamano <gitster@pobox.com> Signed-off-by: Lidong Yan <yldhome2d2@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-09diff: --no-index should ignore the worktreeJunio C Hamano2-0/+32
The act of giving "--no-index" tells Git to pretend that the current directory is not under control of any Git index or repository, so even when you happen to be in a Git controlled working tree, where in that working tree should not matter. But the start-up sequence tries to discover the top of the working tree and chdir(2)'s there, even before Git passes control to the subcommand being run. When diff_no_index() starts running, it starts at a wrong (from the end-user's point of view who thinks "git diff --no-index" is merely a better version of GNU diff) directory, and the original directory the user started the command is at "prefix". Because the paths given from argv[] have already been adjusted to account for this path shuffling by prepending the prefix, and showing the resulting path by stripping the prefix, the effect of these nonsense operations (nonsense in the context of "--no-index", that is) is usually not observable. Except for special cases like "-", where it is not preprocessed by prepending the prefix. Instead of papering over by adding more special cases only to cater to the no-index codepath in the generic code, drive the diff machinery more faithfully to what is going on. If the user started "git diff --no-index" in directory X/Y/Z in a working tree controlled by Git, and the start up sequence of Git chdir(2)'ed up to directory X and left Y/Z in the prefix, revert the effect of the start up sequence by chdir'ing back to Y/Z and emptying the prefix. Reported-by: Gregoire Geis <opensource@gregoirege.is> Helped-by: Ramsay Jones <ramsay@ramsayjones.plus.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-09merge: don't document non-existing --compact-summary argumentRené Scharfe1-1/+1
3a54f5bd5d (merge/pull: add the "--compact-summary" option, 2025-06-12) added the option --compact-summary to both merge and pull. It takes no no argument, but for merge it got an argument help string. Remove it, since it is unnecessary. Signed-off-by: René Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-09for-each-ref: call --start-after argument "marker"René Scharfe1-1/+1
dabecb9db2 (for-each-ref: introduce a '--start-after' option, 2025-07-15) added the option --start-after and referred to its argument as "marker" in documentation and usage string, but not in the option's short help. Use "marker" there as well for consistency and brevity. Signed-off-by: René Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-08t6137-*.sh: fix test failure on cygwinRamsay Jones1-6/+6
Commit 6fd1106aa4 ("t3700: Skip a test with backslashes in pathspec", 2009-03-13) introduced the BSLASHPSPEC prerequisite. This prerequisite allows tests to check for systems that can use backslashes in pathspecs (e.g. to escape glob special characters). On windows (and cygwin), this does not work because backslashes are used as directory separators, and git eagerly converts them to forward slashes. This test file uses the FUNNYNAMES prerequisite to skip this test file on windows, despite not really being appropriate for this test, which does not hold on cygwin. The FUNNYNAMES prerequisite is set when the system can create files with embedded quotes ("), tabs or newlines in the name. Since cygwin can satisfy FUNNYNAMES, but not BSLASHPSPEC, this leads to test failures on cygwin. In order to skip these tests on cygwin, replace the FUNNYNAMES prerequisite with BSLASHPSPEC, so that this test file is skipped on both windows and cygwin. While here, fix a few test titles as well. Signed-off-by: Ramsay Jones <ramsay@ramsayjones.plus.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-08builtin: also setup gently for --help-allD. Ben Knoble5-7/+19
Git experts often check the help summary of a command to make sure they spell options right when suggesting advice to colleagues. Further, they might check hidden options when responding to queries about deprecated options like git-rebase(1)'s "preserve merges" option. But some commands don't support "--help-all" outside of a git directory. Running (for example) git rebase --help-all outside a directory fails in "setup_git_directory", erroring with the localized form of fatal: not a git repository (or any of the parent directories): .git Like 99caeed05d (Let 'git <command> -h' show usage without a git dir, 2009-11-09), we want to show the "--help-all" output even without a git dir. Make "--help-all" where we expect "-h" to mean "setup_git_directory_gently", and interpose early in the natural place ("show_usage_with_options_if_asked"). Do the same for usage callers with show_usage_if_asked. The exception is merge-recursive, whose help block doesn't use newer APIs. Best-viewed-with: --ignore-space-change Signed-off-by: D. Ben Knoble <ben.knoble+github@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-08parse-options: refactor flags for usage_with_options_internalD. Ben Knoble1-5/+15
When reading or editing calls to usage_with_options_internal, it is difficult to tell what trailing "0, 0", "0, 1", "1, 0" arguments mean (NB there is never a "1, 1" case). Give the flags readable names to improve call-sites without changing any behavior. Signed-off-by: D. Ben Knoble <ben.knoble+github@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-08Merge branch 'ua/t1517-short-help-tests' into dk/help-allJunio C Hamano3-13/+37
* ua/t1517-short-help-tests: t5304: move `prune -h` test from t1517 t5200: move `update-server-info -h` test from t1517 t/t1517: automate `git subcmd -h` tests outside a repository
2025-08-08doc: git-log: fix description listKristoffer Haugsbakk1-2/+2
b27be108c89 (doc: git-log: convert log config to new doc format, 2025-07-07) intended to convert a paragraph describing the different options for `log.decorate` into a description list. But the literal block syntax was used by mistake. Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-08t0450: fix test for out-of-tree buildsToon Claes1-1/+1
When using Meson, builds are out-of-tree and $GIT_BUILD_DIR gets set to the path where the build output is landing. To locate the Documentation sources, test 't0450' was using that path. Modify test 't0450' to use `$GIT_SOURCE_DIR/Documentation` to find the documentation sources. Signed-off-by: Toon Claes <toon@iotcl.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-08remote.c: convert if-else ladder to switchDenton Liu1-7/+12
For better readability, convert the if-else ladder into a switch statement. Suggested-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Denton Liu <liu.denton@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-08remote.c: remove BUG in show_push_unqualified_ref_name_error()Denton Liu2-2/+8
When "git push <remote> <src>:<dst>" does not spell out the destination side of the ref fully, and when <src> is not given as a reference but an object name, the code tries to give advice messages based on the type of that object. The type is determined by calling odb_read_object_info() and signalled by its return value. The code however reported a programming error with BUG() when this function said that there is no such object, which happens when the object name is given as a full hexadecimal (if the object name is given as a partial hexadecimal or an non-existing ref, the function would have died without returning, so this BUG() wouldn't have triggered). This is wrong. It is an ordinary end-user mistake to give an object name that does not exist and treated as such. An example of the error message produced is as follows: error: The destination you provided is not a full refname (i.e., starting with "refs/"). We tried to guess what you meant by: - Looking for a ref that matches 'branch' on the remote side. - Checking if the <src> being pushed ('0000000000000000000000000000000000000001') is a ref in "refs/{heads,tags}/". If so we add a corresponding refs/{heads,tags}/ prefix on the remote side. Neither worked, so we gave up. You must fully qualify the ref. BUG: remote.c:1221: '0000000000000000000000000000000000000001' should be commit/tag/tree/blob, is '-1' fatal: the remote end hung up unexpectedly Aborted (core dumped) Helped-by: Junio C Hamano <gitster@pobox.com> Signed-off-by: Denton Liu <liu.denton@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-08t5516: remove surrounding empty lines in test bodiesDenton Liu1-51/+0
This style with the empty lines in test bodies was from when the test suite was being developed. Remove the empty lines to match the modern test style. Signed-off-by: Denton Liu <liu.denton@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-08diff: ensure consistent diff behavior with ignore optionsLidong Yan5-23/+70
In git-diff, options like `-w` and `-I<regex>`, two files are considered equivalent under the specified "ignore" rules, even when they are not bit-for-bit identical. For options like `--raw`, `--name-status`, and `--name-only`, git-diff deliberately compares only the SHA values to determine whether two files are equivalent, for performance reasons. As a result, a file shown in `git diff --name-status` may not appear in `git diff --patch`. To quickly determine whether two files are equivalent, add a helper function diff_flush_patch_quietly() in diff.c. Add `.dry_run` field in `struct diff_options`. When `.dry_run` is true, builtin_diff() returns immediately upon finding any change. Call diff_flush_patch_quietly() to determine if we should flush `--raw`, `--name-only` or `--name-status` output. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Lidong Yan <yldhome2d2@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-08t5304: move `prune -h` test from t1517Usman Akinyemi2-7/+5
t1517 is now focused on testing subcommands outside a repository. Move the in-repo `-h` test for `prune` to t5304, which covers this command. Suggested-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Usman Akinyemi <usmanakinyemi202@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-08t5200: move `update-server-info -h` test from t1517Usman Akinyemi2-5/+5
t1517 is now focused on testing subcommands outside a repository. Move the in-repo `-h` test for `update-server-info` to t5200, which covers this command. Suggested-by: Patrick Steinhardt <ps@pks.im> Helped-by: Junio C Hamano <gitster@pobox.com> Signed-off-by: Usman Akinyemi <usmanakinyemi202@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-08t/t1517: automate `git subcmd -h` tests outside a repositoryUsman Akinyemi1-2/+28
Replace manual `-h` tests with a loop over all subcommands using `git --list-cmds=main`. This ensures consistent coverage of `-h` behavior outside a repo and future-proofs the test by covering new commands automatically. Known exceptions are skipped or marked as expected failures. Suggested-by: Patrick Steinhardt <ps@pks.im> Helped-by: Junio C Hamano <gitster@pobox.com> Helped-by: D. Ben Knoble <ben.knoble+github@gmail.com> Signed-off-by: Usman Akinyemi <usmanakinyemi202@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-07diff: teach tree-diff a max-depth parameterJeff King7-3/+257
When you are doing a tree-diff, there are basically two options: do not recurse into subtrees at all, or recurse indefinitely. While most callers would want to always recurse and see full pathnames, some may want the efficiency of looking only at a particular level of the tree. This is currently easy to do for the top-level (just turn off recursion), but you cannot say "show me what changed in subdir/, but do not recurse". This patch adds a max-depth parameter which is measured from the closest pathspec match, so that you can do: git log --raw --max-depth=1 -- a/b/c and see the raw output for a/b/c/, but not those of a/b/c/d/ (instead of the raw output you would see for a/b/c/d). Co-authored-by: Toon Claes <toon@iotcl.com> Signed-off-by: Toon Claes <toon@iotcl.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-07within_depth: fix return for empty pathToon Claes4-1/+50
The within_depth() function is used to check whether pathspecs limited by a max-depth parameter are acceptable. It takes a path to check, a maximum depth, and a "base" depth. It counts the components in the path (by counting slashes), adds them to the base, and compares them to the maximum. However, if the base does not have any slashes at all, we always return `true`. If the base depth is 0, then this is correct; no matter what the maximum is, we are always within it. However, if the base depth is greater than 0, then we might return an erroneous result. This ends up not causing any user-visible bugs in the current code. The call sites in dir.c always pass a base depth of 0, so are unaffected. But tree_entry_interesting() uses this function differently: it will pass the prefix of the current entry, along with a `1` if the entry is a directory, in essence checking whether items inside the entry would be of interest. It turns out not to make a difference in behavior, but the reasoning is complex. Given a tree like: file a/file a/b/file walking the tree and calling tree_entry_interesting() will yield the following results: (with max_depth=0): file: yes a: yes a/file: no a/b: no (with max_depth=1): file: yes a: yes a/file: yes a/b: no So we have inconsistent behavior in considering directories interesting. If they are at the edge of our depth but at the root, we will recurse into them, but then find all of their entries uninteresting (e.g., in the first case, we will look at "a" but find "a/*" uninteresting). But if they are at the edge of our depth and not at the root, then we will not recurse (in the second example, we do not even bother entering "a/b"). This turns out not to matter because the only caller which uses max-depth pathspecs is cmd_grep(), which only cares about blob entries. From its perspective, it is exactly the same to not recurse into a subtree, or to recurse and find that it contains no matching entries. Not recursing is merely an optimization. It is debatable whether tree_entry_interesting() should consider such an entry interesting. The only caller does not care if it sees the tree itself, and can benefit from the optimization. But if we add a "max-depth" limiter to regular diffs, then a diff with DIFF_OPT_TREE_IN_RECURSIVE would probably want to show the tree itself, but not what it contains. This patch just fixes within_depth(), which means we consider such entries uninteresting (and makes the current caller happy). If we want to change that in the future, then this fix is still the correct first step, as the current behavior is simply inconsistent. This has the effect the function tree_entry_interesting() now behaves like following on the first example: (with max_depth=0): file: yes a: no a/file: no a/b: no Meaning we won't step in "a/" no more to realize all "a/*" entries are uninterested, but we stop at the tree entry itself. Based-on-patch-by: Jeff King <peff@peff.net> Signed-off-by: Toon Claes <toon@iotcl.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-07combine-diff: zero memory used for callback filepairsJeff King1-1/+1
In commit 25e5e2bf85 (combine-diff: support format_callback, 2011-08-19), the combined-diff code learned how to make a multi-sourced `diff_filepair` to pass to a diff callback. When we create each filepair, we do not bother to fill in many of the fields, because they would make no sense (e.g. there can be no rename score or broken_pair flag because we do not go through the diffcore filters). However, we did not even bother to zero them, leading to random values. Let's make sure everything is blank with xcalloc(), just as the regular diff code does. We would potentially want to set the `status` flag to something non-zero, but it is not clear to what. Possibly a new DIFF_STATUS_COMBINED would make sense, as this is not strictly a modification, nor does it fit any other category. Since it is not yet clear what callers would want, this patch simply leaves it as `0`, the same empty flag that is seen when `diffcore_std` is not used at all. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Toon Claes <toon@iotcl.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-07merge-ort: fix directory rename on top of source of other rename/deleteElijah Newren2-20/+355
At GitHub, we've got a real-world repository that has been triggering failures of the form: git: merge-ort.c:3007: process_renames: Assertion `newinfo && !newinfo->merged.clean' failed. which comes from the line: VERIFY_CI(newinfo); Unfortunately, this one has been quite complex to unravel, and is a bit complex to explain. So, I'm going to carefully try to explain each relevant piece needed to understand the fix, then carefully build up from a simple testcase to some of the relevant testcases. == New special case we need to consider == Rename pairs in the diffcore machinery connect the source path of a rename with the destination path of a rename. Since we have rename pairs to consider on both sides of history since the merge base, merging has to consider a few special cases of possible overlap: A) two rename pairs having the same target path B) two rename pairs having the same source path C) the source path of one rename pair being the target path of a different rename pair Some of these came up often enough that we gave them names: A) a rename/rename(2to1) conflict (looks similar to an add/add conflict) B) a rename/rename(1to2) conflict, which represents the same path being renamed differently on the two sides of history C) not yet named merge-ort is well-prepared to handle cases (A) and (B), as was merge-recursive (which was merge-ort's predecessor). Case (C) was briefly considered during the years of merge-recursive maintenance, but the full extent of support it got was a few FIXME/TODO comments littered around the code highlighting some of the places that would probably need to be fixed to support it. When I wrote merge-ort I ignored case (C) entirely, since I believed that case (C) was only possible if we were to support break detection during merges. Not only had break detection never been supported by any merge algorithm, I thought break detection wasn't worth the effort to support in a merge algorithm. However, it turns out that case (C) can be triggered without break detection, if there's enough moving pieces. Before I dive into how to trigger case (C) with directory renames plus other renames, it might be helpful to use a simpler example with break detection first. And before we get to that it may help to explain some more basics of handling renames in the merge algorithm. So, let me first backup and provide a quick refresher on each of * handling renames * what break detection would mean, if supported in merging * handling directory renames From there, I'll build up from a basic directory rename detection case to one that triggers a failure currently. == Handling renames == In the merge machinery when we have a rename of a path A -> B, processing that rename needs to remove path A, and make sure that path B has the relevant information. Note that if the content was also modified on both sides, this may mean that we have 3 different stages that need to be stored at path B instead of having some stored at path A. Having all stages stored at path B makes it much easier for users to investigate and resolve the content conflict associated with a renamed path. For example: * "git status" doesn't have to figure out how to list paths A & B and attempt to connect them for users; it can just list path B. * Users can use "git ls-files -u B" (instead of trying to find the previous name of the file so they can list both, i.e. "git ls-files -u A B") * Users can resolve via "git add B" (without needing to "git rm A") == What break detection would mean == If break detection were supported, we might have cases where A -> B *and* C -> A, meaning that both rename pairs might believe they need to update A. In particular, the processing of A -> B would need to be careful to not clear out all stages of A and mark it resolved, while both renames would need to figure out which stages of A belong with A and which belong with B, so that both paths have the right stages associated with them. merge-ort (like merge-recursive before it) makes no attempt to handle break detection; it runs with break detection turned off. It would need to be retrofitted to handle such cases. == Directory rename detection == If one side of history renames directory D/ -> E/, and the other side of history adds new files to D/, then directory rename detection notices and suggests moving those new files to E/. A similar thing is done for paths renamed into D/, causing them to be transitively renamed into E/. The default in the merge machinery is to report a conflict whenever a directory rename might modify the location of a path, so that users can decide whether they wanted the original path or the directory-rename-induced location. However, that means the default codepath still runs through all the directory rename detection logic, it just supplements it with providing conflict notices when it is done. == Building up increasingly complex testcases == I'll start with a really simple directory rename example, and then slowly add twists that explain new pieces until we get to the problematic cases: === Testcase 1 === Let's start with a concrete example, where particular files/directories of interest that exist or are changed on each side are called out: Original: <nothing of note> our side: rename B/file -> C/file their side: rename C/ -> A/ For this case, we'd expect to see the original B/file appear not at C/file but at A/file. (We would also expect a conflict notice that the user will want to choose between C/file and A/file, but I'm going to ignore conflict notices from here on by assuming merge.directoryRenames is set to `true` rather than `conflict`; the only difference that assumption makes is whether that makes the merge be considered to be conflicted and whether it prints a conflict notice; what is written to the index or working directory is unchanged.) === Testcase 2 === Modify testcase 1 by having A/file exist from the start: Original: A/file exists our side: rename B/file -> C/file their side: rename C/ -> A/ In such a case, to avoid user confusion at what looks kind of like an add/add conflict (even though the original path at A/file was not added by either side of the merge), we turn off directory rename detection for this path and print a "in the way" warning to the user: CONFLICT (implicit dir rename): Existing file/dir ... in the way ... The testcases in section 5 of t6423 explore these in more detail. === Testcase 3 === Let's modify testcase 1 in a slightly different way: have A/file be added by their side rather than it already existing. Original: <nothing of note> our side: rename B/file -> C/file their side: rename C/ -> A/ add A/file In this case, the directory rename detection basically transforms our side's original B/file -> C/file into a B/file -> A/file, and so we get a rename/add conflict, with one version of A/file coming from the renamed file, and another coming from the new A/file, each stored as stages 2 and 3 in conflicts. This kind of add/add conflict is perhaps slightly more complex than a regular add/add conflict, but with the printed messages it makes sense where it came from and we have different stages of the file to work with to resolve the conflict. === Testcase 4 === Let's do something similar to testcase 3, but have the opposite side of history add A/file: Original: <nothing of note> our side: rename B/file -> C/file add A/file their side: rename C/ -> A/ Now if we allow directory rename detection to modify C/file to A/file, then we also get a rename/add conflict, but in this case we'd need both higher order stages being recorded on side 2, which makes no sense. The index can't store multiple stage 2 entries, and even if we could, it would probably be confusing for users to work with. So, similar to what we do when there was an A/file in the original version, we simply turn off directory rename detection for cases like this and provide the "in the way" CONFLICT notice to the user. === Testcase 5 === We're slowly getting closer. Let's mix it up by having A/file exist at the beginning but not exist on their side: original: A/file exists our side: rename B/file -> C/file their side: rename C/ -> A/ rename A/file -> D/file For this case, you could say that since A/file -> D/file, it's no longer in the way of C/file being moved by directory rename detection to A/file. But that would give us a case where A/file is both the source and the target of a rename, similar to break detection, which the code isn't currently equipped to handle. This is not yet the case that causes current failures; to the current code, this kind of looks like testcase 4 in that A/file is in the way on our side (since A/file was in the original and was umodified by our side). So, it results in a "in the way" notification with directory rename detection being turned off for A/file so that B/file ends up at C/file. Perhaps the resolution could be improved in the future, but our "in the way" checks prevented such problems by noticing that A/file exists on our side and thus turns off directory rename detection from affecting C/file's location. So, while the merge result could be perhaps improved, the fact that this is currently handled by giving the user an "in the way" message gives the user a chance to resolve and prevents the code from tripping itself up. === Testcase 6 === Let's modify testcase 5 a bit more, to also delete A/file on our side: original: A/file exists our side: rename B/file -> C/file delete A/file their side: rename C/ -> A/ rename A/file -> D/file Now the "in the way" logic doesn't detect that there's an A/file in the way (neither side has an A/file anymore), so it's fine to transitively rename C/file further to A/file...except that we end up with A/file being both the source of one rename, and the target of a different rename. Each rename pair tries to handle the resolution of the source and target paths of its own rename. But when we go to process the second rename pair in process_renames(), we do not expect either the source or the destination to be marked as handled already; so, when we hit the sanity checks that these are not handled: VERIFY_CI(oldinfo); VERIFY_CI(newinfo); then one of these is going to throw an assertion failure since the previous rename pair already marked both of its paths as handled. This will give us an error of the form: git: merge-ort.c:3007: process_renames: Assertion `newinfo && !newinfo->merged.clean' failed. This is the failure we're currently triggering, and it fundamentally depends on: * a path existing in the original * that original path being removed or renamed on *both* sides * some kind of directory rename moving some *other* path into that original path This was added as testcase 12q in t6423. === Testcase 7 === Bonus bug found while investigating! Let's go back to the comparison between testcases 2 & 3, and set up a file present on their side that we need to consider: Original: A/file exists our side: rename B/file -> C/file rename A/file -> D/file their side: rename C/ -> A/ Here, there is no A/file in the way on our side like testcase 4. There is an A/file present on their side like testcase 3, which was an add/add conflict, but that's associated with the file be renamed to D/file. So, that really shouldn't be an add/add conflict because we instead want all modes of the original A/file to be transported to D/file. Unfortunately, the current code kind of treats it like an add/add conflict instead...but even worse. There is also a valid mode for A/file in the original, which normally goes to stage 1. However, an add/add conflict should be represented in the index with no mode at stage 1 (for the original side), only modes at stages 2 and 3 (for our and their side), so for an add/add we'd expect that mode for A/file in the original version to be cleared out (or be transported to D/file). Unfortunately, the code currently leaves not only the stage 3 entry for A/file intact, it also leaves the stage 1 entry for A/file. This results in `git ls-files -u A/file` output of the form: 100644 d00491fd7e5bb6fa28c517a0bb32b8b506539d4d 1 A/file 100644 0cfbf08886fca9a91cb753ec8734c84fcbe52c9f 2 A/file 100644 d00491fd7e5bb6fa28c517a0bb32b8b506539d4d 3 A/file This would likely cause users to believe this isn't an add/add conflict; rather, this would lead them to believe that A/file was only modified on our side and that therefore it should not have been a conflict in the first place. And while resolving the conflict in favor of our side is the correct resolution (because stages 1 and 3 should have been cleared out in the first place), this is certainly likely to cause confusion for anyone attempting to investigate why this path was marked as conflicted. This was added as testcase 12p in t6423. == Attempted solutions that I discarded == 1) For each side of history, create a strset of the sources of each rename on the other side of history. Then when using directory renames to modify existing renames, verify that we aren't renaming to a source of another rename. Unfortunately, the "relevant renames" optimization in merge-ort means we often don't detect renames -- we just see a delete and an add -- which is easy to forget and makes debugging testcases harder, but it also turns out that this solution in insufficient to solve the related problems in the area (more on that below). 2) Modify the code to be aware of the possibility of renaming to the source of another side's rename, and make all the conflict resolution logic for each case (including existing rename/rename(2to1) and rename/rename(1to2) cases) handle the additional complexity. It turns out there was much more code to audit than I wanted, for a really niche case. I didn't like how many changes were needed, and aborted. == Solution == We do not want the stages of unrelated files appearing at the same path in the index except when dealing with an add/add conflict. While we previously handled this for stages 2 & 3, we also need to worry about stage 1. So check for a stage 1 index entry being in the way of a directory rename. However, if we can detect that the stage 1 index entry is actually from a related file due to a directory-rename-causes-rename-to-self situation, then we can allow the stage 1 entry to remain. From this wording, you may note that it's not just rename cases that are a problem; bugs could be triggered with directory renames vs simple adds. That leads us to... == Testcases 8+ == Another bonus bug, found via understanding our final solutions (and the failure of our first attempted solution)! Let's tweak testcase 7 a bit: Original: A/file exists our side: delete A/file add -> C/file their side: delete A/file rename C/ -> A/ Here, there doesn't seem to be a big problem. Sure C/file gets modified via the directory rename of C/ -> A/ so that it becomes A/file, but there's no file in the way, right? Actually, here we have a problem that the stage 1 entry of A/file would be combined with the stage 2 entry of C/file, and make it look like a modify/delete conflict. Perhaps there is some extra checking that could be added to the code to make it attempt to clear out the stage 1 entry of A/file, but the various rename-to-self-via-directory-rename testcases make that a bit more difficult. For now, it's easier to just treat this as a path-in-the-way situation and not allow the directory rename to modify C/file. That sounds all well and good, but it does have an interesting side effect. Due to the "relevant renames" optimizations in merge-ort (i.e. only detect the renames you need), 100% renames whose files weren't modified on the other side often go undetected. This means that if we modify this testcase slightly to: Original: A/file exists our side: A/file -> C/file their side: rename C/ -> A/ Then although this looks like where the directory rename just moves C/file back to A/file and there's no problem, we may not detect the A/file -> C/file rename. Instead it will look like a deletion of A/file and an addition of C/file. The directory rename then appears to be moving C/file to A/file, which is on top of an "unrelated" file (or at least a file it doesn't know is related). So, we will report path-in-the-way conflicts now in cases where we didn't before. That's better than silently and accidentally combining stages of unrelated files and making them look like a modify/delete; users can investigate the reported conflict and simply resolve it. This means we tweak the expected solution for testcases 12i, 12j, and 12k. (Those three tests are basically the same test repeated three times, but I was worried when I added those that subtle differences in parent/child, sibling/sibling, and toplevel directories might mess up how rename-to-self testcases actually get handled.) Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-07merge-ort: fix incorrect file handlingElijah Newren2-3/+80
We have multiple bugs here -- accidental silent file deletion, accidental silent file retention for files that should be deleted, and incorrect number of entries left in the index. The series merged at commit d3b88be1b450 (Merge branch 'en/merge-dir-rename-corner-case-fix', 2021-07-16) introduced testcase 12i-12k in t6423 which checked for rename-to-self cases, and fixed bugs that merge-ort and merge-recursive had with these testcases. At the time, I noted that merge-ort had one bug for these cases, while merge-recursive had two. It turns out that merge-ort did in fact have another bug, but the "relevant renames" optimizations were masking it. If we modify testcase 12i from t6423 to modify the file in the commit that renames it (but only modify it enough that it can still be detected as a rename), then we can trigger silent deletion of the file. Tweak testcase 12i slightly to make the file in question have more than one line in it. This leaves the testcase intact other than changing the initial contents of this one file. The purpose of this tweak is to minimize the changes between this testcase and a new one that we want to add. Then duplicate testcase 12i as 12i2, changing it so that it adds a single line to the file in question when it is renamed; testcase 12i2 then serves as a testcase for this merge-ort bug that I previously overlooked. Further, commit 98a1a00d5301 (t6423: add a testcase causing a failed assertion in process_renames, 2025-03-06), fixed an issue with rename-to-self but added a new testcase, 12n, that only checked for whether the merge ran to completion. A few commits ago, we modified this test to check for the number of entries in the index -- but noted that the number was wrong. And we also noted a silently-keep-instead-of-delete bug at the same time in the new testcase 12n2. In summary, we have the following bugs with rename-to-self cases: * silent deletion of file expected to be kept (t6423 testcase 12i2) * silent retention of file expected to be removed (t6423 testcase 12n2) * wrong number of extries left in the index (t6423 testcase 12n) All of these bugs arise because in a rename-to-self case, when we have a rename A->B, both A and B name the same file. The code in process_renames() assumes A & B are different, and tries to move the higher order stages and file contents so that they are associated just with the new path, but the assumptions of A & B being different can cause A to be deleted when it's not supposed to be or mark B as resolved and kept in place when it's supposed to be deleted. Since A & B are already the same path in the rename-to-self case, simply skip the steps in process_renames() for such files to fix these bugs. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-07merge-ort: clarify the interning of strings in opt->priv->pathElijah Newren1-3/+8
Because merge-ort is dealing with potentially all the pathnames in the repository, it sometimes needs to do an awful lot of string comparisons. Because of this, struct merge_options_internal's path member was envisioned from the beginning to contain an interned value for every path in order to allow us to compare strings via pointer comparison instead of using strcmp. See * 5b59c3db059d (merge-ort: setup basic internal data structures, 2020-12-13) * f591c4724615 (merge-ort: copy and adapt merge_3way() from merge-recursive.c, 2021-01-01) for some of the early comments. However, the original comment was slightly misleading when it switched from mentioning paths to only mentioning directories. Fix that, and while at it also point to an example in the code which applies the extra needed care to permit the pointer comparison optimization. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-07t6423: fix missed staging of file in testcases 12i,12j,12kElijah Newren1-3/+6
Commit 806f83287f8d (t6423: test directory renames causing rename-to-self, 2021-06-30) introduced testcase 12i-12k but omitted staging one of the files and copy-pasted that mistake to the other tests. This means the merge runs with an unstaged change, even though that isn't related to what is being tested and makes the test look more complicated than it is. The cover letter for the series associated with the above commit (see Message-ID: pull.1039.git.git.1624727121.gitgitgadget@gmail.com) noted that these testcases triggered two bugs in merge-recursive but only one in merge-ort; in merge-recursive these testcases also triggered a silent deletion of the file in question when it shouldn't be deleted. What I didn't realize at the time was that the deletion bug in merge-ort was merely being sidestepped by the "relevant renames" optimization but can actually be triggered. A subsequent commit will deal with that additional bug, but it was complicated by the mistaken forgotten staging, so this commit first fixes that issue. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-07t6423: document two bugs with rename-to-self testcasesElijah Newren1-2/+98
When commit 98a1a00d5301 (t6423: add a testcase causing a failed assertion in process_renames, 2025-03-06) was added, I tweaked the commit message, and moved the test into t6423. However, that still left two other things missing that made this test unlike the others in the same testfile: * It didn't have an English description of the test setup like all other tests in t6423 * It didn't check that the right number of files were present at the end The former issue is a minor detail that isn't that critical, but the latter feels more important. If it had been done, I might have noticed another bug. In particular, this testcase involves Side A: rename world -> tools/world and Side B: rename tools/ -> <the toplevel> Side B: remove world The tools/ -> <toplevel> rename turns the world -> tools/world rename into world -> world, i.e. a rename-to-self case. But, it's a path conflict because merge.directoryRenames defaults to false. There's no content conflict because Side A didn't modify world, so we should just take the content of world from Side B -- i.e. delete it. So, we have a conflict on the path, but not on its content. We could consider letting the content trump since it is unconflicted, but if we are going to leave a conflict, it should certainly represent that 'world' existed both in the base version and on Side A. Currently it doesn't. Add a description of this test, add some checking of the number of entries in the index at the end of the merge, and mark the test as expecting to fail for now. A subsequent commit will fix this bug. While at it, I found another related bug from a nearly identical setup but setting merge.directoryRenames=true. Copy testcase 12n into 12n2, changing it to use merge instead of cherry-pick, and turn on directory renames for this test. In this case, since there is no content conflict and no path conflict, it should be okay to delete the file. Unfortunately, the code resolves without conflict but silently leaves world despite the fact it should be deleted. It might also be okay if the code spuriously thought there was a modify/delete conflict here; that would at least notify users to look closer and then when they notice there was no change since the base version, they can easily resolve. A conflict notice is much better than silently providing the wrong resolution. Cover this with the 12n2 testcase, which for now is marked as expecting to fail as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-07merge-ort: drop unnecessary temporary in check_for_directory_rename()Elijah Newren1-4/+2
check_for_directory_rename() had a weirdly coded check for whether a strmap contained a certain key. Replace the temporary variable and call to strmap_get_entry() with the more natural strmap_contains() call. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-07merge-ort: update comments to modern testfile locationElijah Newren1-3/+3
In commit 919df3195553 (Collect merge-related tests to t64xx, 2020-08-10), merge related tests were moved from t60xx to t64xx. Some comments in merge-ort relating to some tricky code referenced specific testcases within certain testfiles for additional information, but referred to their historical testfile names; update the testfile names to mention their modern location. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-07Git 2.51-rc1v2.51.0-rc1Junio C Hamano2-1/+7
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-07Documentation/RelNotes/2.51.0: improve wording for a couple entriesPatrick Steinhardt1-5/+5
Improve wording and fix typos for a couple entries part of the Git 2.51 release notes. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-07Merge branch 'jt/archive-zip-deflate-fix'Junio C Hamano1-6/+14
The deflate codepath in "git archive --format=zip" had a longstanding bug coming from misuse of zlib API, which has been corrected. * jt/archive-zip-deflate-fix: archive: flush deflate stream until Z_STREAM_END
2025-08-07Merge branch 'dl/squelch-maybe-uninitialized'Junio C Hamano2-5/+8
Squelch false-positive compiler warning. * dl/squelch-maybe-uninitialized: t/unit-tests/clar: fix -Wmaybe-uninitialized with -Og remote: bail early from set_head() if missing remote name
2025-08-07Merge branch 'jk/revert-squelch-compiler-warning'Junio C Hamano1-1/+1
Squelch false-positive compiler warning. * jk/revert-squelch-compiler-warning: revert: initialize const value
2025-08-06builtin/remote: only iterate through refs that are to be renamedPatrick Steinhardt3-10/+13
When renaming a remote we also need to rename all references accordingly. But while we only need to rename references that are contained in the "refs/remotes/$OLDNAME/" namespace, we end up using `refs_for_each_rawref()` that iterates through _all_ references. We know to exit early in the callback in case we see an irrelevant reference, but ultimately this is still a waste of compute as we knowingly iterate through references that we won't ever care about. Improve this by using `refs_for_each_rawref_in()`, which knows to only iterate through (potentially broken) references in a given prefix. The following benchmark renames a remote with a single reference in a repository that has 100k unrelated references. This shows a sizeable improvement with the "files" backend: Benchmark 1: rename remote (refformat = files, revision = HEAD~) Time (mean ± σ): 42.6 ms ± 0.9 ms [User: 29.1 ms, System: 8.4 ms] Range (min … max): 40.1 ms … 43.3 ms 10 runs Benchmark 2: rename remote (refformat = files, revision = HEAD) Time (mean ± σ): 31.7 ms ± 4.0 ms [User: 19.6 ms, System: 6.9 ms] Range (min … max): 27.1 ms … 36.0 ms 10 runs Summary rename remote (refformat = files, revision = HEAD) ran 1.35 ± 0.17 times faster than rename remote (refformat = files, revision = HEAD~) The "reftable" backend shows roughly the same absolute improvement, but given that it's already significantly faster than the "files" backend this translates to a much larger relative improvement: Benchmark 1: rename remote (refformat = reftable, revision = HEAD~) Time (mean ± σ): 18.2 ms ± 0.5 ms [User: 12.7 ms, System: 3.0 ms] Range (min … max): 17.3 ms … 21.4 ms 110 runs Benchmark 2: rename remote (refformat = reftable, revision = HEAD) Time (mean ± σ): 8.8 ms ± 0.5 ms [User: 3.8 ms, System: 2.9 ms] Range (min … max): 7.5 ms … 9.9 ms 167 runs Summary rename remote (refformat = reftable, revision = HEAD) ran 2.07 ± 0.12 times faster than rename remote (refformat = reftable, revision = HEAD~) Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-06builtin/remote: rework how remote refs get renamedPatrick Steinhardt2-99/+270
It was recently reported [1] that renaming a remote that has dangling symrefs is broken. This issue can be trivially reproduced: $ git init repo Initialized empty Git repository in /tmp/repo/.git/ $ cd repo/ $ git remote add origin /dev/null $ git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/master $ git remote rename origin renamed $ git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/master $ git symbolic-ref refs/remotes/renamed/HEAD fatal: ref refs/remotes/renamed/HEAD is not a symbolic ref As one can see, the "HEAD" reference did not get renamed but stays in the same place. There are two issues here: - We use `refs_resolve_ref_unsafe()` to resolve references, but we don't pass the `RESOLVE_REF_NO_RECURSE` flag. Consequently, if the reference does not resolve, the function will fail and we thus ignore this branch. - We use `refs_for_each_ref()` to iterate through the old remote's references, but that function ignores broken references. Both of these issues are easy to fix. But having a closer look at the logic that renames remote references surfaces that it leaves a lot to be desired overall. The problem is that we're using O(|refs| + |symrefs| * 2) many reference transactions to perform the renames. We first delete all symrefs, then individually rename every direct reference and finally we recreate the symrefs. On the one hand this isn't even remotely an atomic operation, so if we hit any error we'll already have deleted all references. But more importantly it is also extremely inefficient. The number of transactions for symrefs doesn't really bother us too much, as there should generally only be a single symref anyway ("HEAD"). But the renames are very expensive: - For the "reftable" backend we perform auto-compaction after every single rename, which does add up. - For the "files" backend we potentially have to rewrite the "packed-refs" file on every single rename in case they are packed. The consequence here is quadratic runtime performance. Renaming a 100k references takes hours to complete. Refactor the code to use a single transaction to perform all the reference updates atomically, which speeds up the transaction quite significantly: Benchmark 1: rename remote (refformat = files, revision = HEAD~) Time (mean ± σ): 238.770 s ± 13.857 s [User: 91.473 s, System: 143.793 s] Range (min … max): 204.863 s … 247.699 s 10 runs Benchmark 2: rename remote (refformat = files, revision = HEAD) Time (mean ± σ): 2.103 s ± 0.036 s [User: 0.360 s, System: 1.313 s] Range (min … max): 2.011 s … 2.141 s 10 runs Summary rename remote (refformat = files, revision = HEAD) ran 113.53 ± 6.87 times faster than rename remote (refformat = files, revision = HEAD~) For the "reftable" backend we see a significant speedup, as well, but given that we don't have quadratic runtime behaviour there it's way less extreme: Benchmark 1: rename remote (refformat = reftable, revision = HEAD~) Time (mean ± σ): 8.604 s ± 0.539 s [User: 4.985 s, System: 2.368 s] Range (min … max): 7.880 s … 9.556 s 10 runs Benchmark 2: rename remote (refformat = reftable, revision = HEAD) Time (mean ± σ): 1.177 s ± 0.103 s [User: 0.446 s, System: 0.270 s] Range (min … max): 1.023 s … 1.410 s 10 runs Summary rename remote (refformat = reftable, revision = HEAD) ran 7.31 ± 0.79 times faster than rename remote (refformat = reftable, revision = HEAD~) There is one issue though with using atomic transactions: when nesting a remote into itself it can happen that renamed references conflict with the old referencse. For example, when we have a reference "refs/remotes/origin/foo" and we rename "origin" to "origin/foo", then we'll end up with an F/D conflict when we try to create the renamed reference "refs/remotes/origin/foo/foo". This situation is overall quite unlikely to happen: people tend to not use nested remotes, and if they do they must at the same time also have a conflicting refname. But the end result would be that the old remote references stay intact whereas all the other parts of the repository have been adjusted for the new remote name. Address this by queueing and preparing the reference update before we touch any other part of the repository. Like this we can make sure that the reference update will go through before rewriting the configuration. Otherwise, if the transaction fails to prepare we can gracefully abort the whole operation without any changes having been performed in the repository yet. Furthermore, we can detect the conflict and print some helpful advice for how the user can resolve this situation. So overall, the tradeoff is that: - Reference transactions are now all-or-nothing. This is a significant improvement over the previous state where we may have ended up with partially-renamed references. - Rewriting references is now significantly faster. - We only rewrite the configuration in case we know that all references can be updated. - But we may refuse to rename a remote in case references conflict. Overall this seems like an acceptable tradeoff. While at it, fix the handling of symbolic/broken references by using `refs_for_each_rawref()`. Add tests that cover both this reported issue and tests that exercise nesting of remotes. One thing to note: with this change we cannot provide a proper progress monitor anymore as we queue the references into the transactions as we iterate through them. Consequently, as we don't know yet how many refs there are in total, we cannot report how many percent of the operation is done anymore. But that's a small price to pay considering that you now shouldn't need the progress monitor in most situations at all anymore. [1]: <CANrWfmQWa=RJnm7d3C7ogRX6Tth2eeuGwvwrNmzS2gr+eP0OpA@mail.gmail.com> Reported-by: Han Jiang <jhcarl0814@gmail.com> Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-06builtin/remote: determine whether refs need renaming early onPatrick Steinhardt1-4/+8
When renaming a remote we may have to also rename remote refs in case the refspec changes. Pull out this computation into a separate loop. While that seems nonsensical right now, it'll help us in a subsequent commit where we will prepare the reference transaction before we rewrite the configuration. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-06builtin/remote: fix sign comparison warningsPatrick Steinhardt1-31/+23
Fix -Wsign-comparison warnings. All of the warnings we have are about mismatches in signedness for loop counters. These are trivially fixable by using the correct integer type. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-06refs: simplify logic when migrating reflog entriesPatrick Steinhardt1-26/+10
When migrating reflog entries between two storage formats we have to do so via two callback-driven functions: - `migrate_one_reflog()` gets invoked via `refs_for_each_reflog()` to first list all available reflogs. - `migrate_one_reflog_entry()` gets invoked via `refs_for_each_reflog_ent()` in `migrate_one_reflog()`. Before the preceding commit we didn't have the refname available in `migrate_one_reflog_entry()`, which made it necessary to have a separate structure that we pass to the second callback so that we can propagate the refname. Now that `refs_for_each_reflog_ent()` knows to pass the refname to the callback though that indirection isn't necessary anymore. There's one catch though: we do have an update index that is also stored in the entry-specific callback data. This update index is required so that we can tell the ref backend in which order it should persist the reflog entries to disk. But that purpose can be trivially achieved by just converting it into a global counter that is used for all reflog entries, regardless of which reference they are for. The ordering will remain the same as both the update index and the refname is considered when sorting the entries. Move the index into `struct migration_data` and drop the now-unused `struct reflog_migration_data` to simplify the code a bit. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-06refs: pass refname when invoking reflog entry callbackPatrick Steinhardt17-45/+63
With `refs_for_each_reflog_ent()` callers can iterate through all the reflog entries for a given reference. The callback that is being invoked for each such entry does not receive the name of the reference that we are currently iterating through. This isn't really a limiting factor, as callers can simply pass the name via the callback data. But this layout sometimes does make for a bit of an awkward calling pattern. One example: when iterating through all reflogs, and for each reflog we iterate through all refnames, we have to do some extra book keeping to track which reference name we are currently yielding reflog entries for. Change the signature of the callback function so that the reference name of the reflog gets passed through to it. Adapt callers accordingly and start using the new parameter in trivial cases. The next commit will refactor the reference migration logic to make use of this parameter so that we can simplify its logic a bit. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-06Merge branch 'ps/reflog-migrate-fixes' into ps/remote-rename-fixJunio C Hamano12-117/+413
* ps/reflog-migrate-fixes: refs: fix invalid old object IDs when migrating reflogs refs: stop unsetting REF_HAVE_OLD for log-only updates refs/files: detect race when generating reflog entry for HEAD refs: fix identity for migrated reflogs ident: fix type of string length parameter builtin/reflog: implement subcommand to write new entries refs: export `ref_transaction_update_reflog()` builtin/reflog: improve grouping of subcommands Documentation/git-reflog: convert to use synopsis type
2025-08-06rebase -i: permit 'drop' of a merge commitJohannes Sixt2-0/+2
4c063c82e9 (rebase -i: improve error message when picking merge, 2024-05-30) added advice texts for cases when a merge commit is passed as argument of sequencer command that cannot operate with a merge commit. However, it forgot about the 'drop' command, so that in this case the BUG() in the default branch is reached. Handle 'drop' like 'merge', i.e., permit it without a message. Signed-off-by: Johannes Sixt <j6t@kdbg.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-06refs: fix invalid old object IDs when migrating reflogsPatrick Steinhardt6-13/+55
When migrating reflog entries between different storage formats we end up with invalid old object IDs for the migrated entries: instead of writing the old object ID of the to-be-migrated entry, we end up with the all-zeroes object ID. The root cause of this issue is that we don't know to use the old object ID provided by the caller. Instead, we manually resolve the old object ID by resolving the current value of its matching reference. But as that reference does not yet exist in the target ref storage we always end up resolving it to all-zeroes. This issue got unnoticed as there is no user-facing command that would even show the old object ID. While `git log -g` knows to show the new object ID, we don't have any formatting directive to show the old object ID. Fix the bug by introducing a new flag `REF_LOG_USE_PROVIDED_OIDS`. If set, backends are instructed to use the old and new object IDs provided by the caller, without doing any manual resolving. Set this flag in `ref_transaction_update_reflog()`. Amend our tests in t1460-refs-migrate to use our test tool to read reflog entries. This test tool prints out both old and new object ID of each reflog entry, which fixes the test gap. Furthermore it also prints the full identity used to write the reflog, which provides test coverage for the previous commit in this patch series that fixed the identity for migrated reflogs. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-06refs: stop unsetting REF_HAVE_OLD for log-only updatesPatrick Steinhardt4-19/+13
The `REF_HAVE_OLD` flag indicates whether a given ref update has its old object ID set. If so, the value of that field is used to verify whether the current state of the reference matches this expected state. It is thus an important part of mitigating races with a concurrent process that updates the same set of references. When writing reflogs though we explicitly unset that flag. This is a sensible thing to do: the old state of reflog entry updates may not necessarily match the current on-disk state of its accompanying ref, but it's only intended to signal what old object ID we want to write into the new reflog entry. For example when migrating refs we end up writing many reflog entries for a single reference, and most likely those reflog entries will have many different old object IDs. But unsetting this flag also removes a useful signal, namely that the caller _did_ provide an old object ID for a given reflog entry. This signal will become useful in a subsequent commit, where we add a new flag that tells the transaction to use the provided old and new object IDs to write a reflog entry. The `REF_HAVE_OLD` flag is then used as a signal to verify that the caller really did provide an old object ID. Stop unsetting the flag so that we can use it as this described signal in a subsequent commit. Skip checking the old object ID for log-only updates so that we don't expect it to match the current on-disk state. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-06refs/files: detect race when generating reflog entry for HEADPatrick Steinhardt1-2/+38
When updating a reference that is being pointed to HEAD we don't only write a reflog message for that particular reference, but also generate one for HEAD. This logic is handled by `split_head_update()`, where we: 1. Verify that the condition actually triggered. This is done by reading HEAD at the start of the transaction so that we can then check whether a given reference update refers to its target. 2. Queue a new log-only update for HEAD in case it did. But the logic is unfortunately not free of races, as we do not lock the HEAD reference after we have read its target. This can lead to the following two scenarios: - HEAD gets concurrently updated to point to one of the references we have already processed. This causes us not writing a reflog message even though we should have done so. - HEAD gets concurrently updated to no longer point to a reference anymore that we have already processed. This causes us to write a reflog message even though we should _not_ have done so. Improve the situation by introducing a new `REF_LOG_VIA_SPLIT` flag that is specific to the "files" backend. If set, we will double check that the HEAD reference still points to the reference that we are creating the reflog entry for after we have locked HEAD. Furthermore, instead of manually resolving the old object ID of that entry, we now use the same old state as for the parent update. If we detect such a racy update we abort the transaction. This is a bit heavy-handed: the user didn't even ask us to write a reflog update for "HEAD", so it might be surprising if we abort the transaction. That being said: - Normal users wouldn't typically hit this case as we only hit the relevant code when committing to a branch that is being pointed to by "HEAD" directly. Commands like git-commit(1) typically commit to "HEAD" itself though. - Scripted users that use git-update-ref(1) and related plumbing commands are unlikely to hit this case either, as they would have to update the pointed-to-branch at the same as "HEAD" is being updated, which is an exceedingly rare event. The alternative would be to instead drop the log-only update completely, but that would require more logic that is hard to verify without adding infrastructure specific for such a test. So we rather do the pragmatic thing and don't worry too much about an edge case that is very unlikely to happen. Unfortunately, this change only helps with the second race. We cannot reliably plug the first race without locking the HEAD reference at the start of the transaction. Locking HEAD unconditionally would effectively serialize all writes though, and that doesn't seem like an option. Also, double checking its value at the end of the transaction is not an option either, as its target may have flip-flopped during the transaction. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-06refs: fix identity for migrated reflogsPatrick Steinhardt1-4/+18
When migrating reflog entries between different storage formats we must reconstruct the identity of reflog entries. This is done by passing the committer passed to the `migrate_one_reflog_entry()` callback function to `fmt_ident()`. This results in an invalid identity though: `fmt_ident()` expects the caller to provide both name and mail of the author, but we pass the full identity as mail. This leads to an identity like: pks <Patrick Steinhardt ps@pks.im> Fix the bug by splitting the identity line first. This allows us to extract both the name and mail so that we can pass them to `fmt_ident()` separately. This commit does not yet add any tests as there is another bug in the reflog migration that will be fixed in a subsequent commit. Once that bug is fixed we'll make the reflog verification in t1450 stricter, and that will catch both this bug here and the other bug. Note that we also add two new `name` and `mail` string buffers to the callback structures and splice them through to the callbacks. This is done so that we can avoid allocating a new buffer every time we compute the committer information. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-06ident: fix type of string length parameterPatrick Steinhardt2-2/+2
The last parameter in `split_ident_line()` is the length of the line passed in by the caller. As such, most callers pass in either the result of `strlen()`, `struct strbuf::len` or a pointer diff, all of which are expected to be positive numbers. Regardless of that, the function accepts a signed integer, which is somewhat confusing. Fix the function signature to instead accept a `size_t`. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-06builtin/reflog: implement subcommand to write new entriesPatrick Steinhardt4-0/+201
While we provide a couple of subcommands in git-reflog(1) to remove reflog entries, we don't provide any to write new entries. Obviously this is not an operation that really would be needed for many use cases out there, or otherwise people would have complained that such a command does not exist yet. But the introduction of the "reftable" backend changes the picture a bit, as it is now basically impossible to manually append a reflog entry if one wanted to do so due to the binary format. Plug this gap by introducing a simple "write" subcommand. For now, all this command does is to append a single new reflog entry with the given object IDs and message to the reflog. More specifically, it is not yet possible to: - Write multiple reflog entries at once. - Insert reflog entries at arbitrary indices. - Specify the date of the reflog entry. - Insert reflog entries that refer to nonexistent objects. If required, those features can be added at a future point in time. For now though, the new command aims to fulfill the most basic use cases while being as strict as possible when it comes to verifying parameters. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-06refs: export `ref_transaction_update_reflog()`Patrick Steinhardt2-18/+26
In a subsequent commit we'll add another user that wants to write reflog entries. This requires them to call `ref_transaction_update_reflog()`, but that function is local to "refs.c". Export the function to prepare for the change. While at it, drop the `flags` field, as all callers are for now expected to use the same flags anyway. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-06builtin/reflog: improve grouping of subcommandsPatrick Steinhardt2-49/+50
The way subcommands of git-reflog(1) are laid out does not make any immediate sense. Reorder them such that read-only subcommands precede writing commands for a bit more structure. Furthermore, move the "expire" subcommand last. This prepares for a subsequent change where we are about to introduce a new "write" command to append reflog entries. Like this, the writing subcommands are ordered such that those affecting a single reflog come before those spanning across all reflogs. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-06Documentation/git-reflog: convert to use synopsis typePatrick Steinhardt1-19/+19
With 974cdca345c (doc: introduce a synopsis typesetting, 2024-09-24) we have introduced a new synopsis type that simplifies the rules for typesetting a command's synopsis. Convert the git-reflog(1) documentation to use it. While at it, convert the list of options to use backticks. This is done to appease an upcoming new linter that mandates the use of backticks when using the synopsis type. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-05A bit more after -rc0Junio C Hamano1-0/+13
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-05Merge branch 'kj/renamed-submodule'Junio C Hamano4-6/+89
The case where a new submodule takes a path where used to be a completely different subproject is now dealt a bit better than before. * kj/renamed-submodule: fixup! submodule: skip redundant active entries when pattern covers path fixup! submodule: prevent overwriting .gitmodules on path reuse submodule: skip redundant active entries when pattern covers path submodule: prevent overwriting .gitmodules on path reuse
2025-08-05Merge branch 'rs/tighten-alias-help'Junio C Hamano1-1/+1
"git -c alias.foo=bar foo -h baz" reported "'foo' is aliased to 'bar'" and then went on to run "git foo -h baz", which was unexpected. Tighten the rule so that alias expansion is reported only when "-h" is the sole option. * rs/tighten-alias-help: git: show alias info only with lone -h
2025-08-05Merge branch 'ps/object-file-wo-the-repository'Junio C Hamano39-293/+332
Reduce implicit assumption and dependence on the_repository in the object-file subsystem. * ps/object-file-wo-the-repository: object-file: get rid of `the_repository` in index-related functions object-file: get rid of `the_repository` in `force_object_loose()` object-file: get rid of `the_repository` in `read_loose_object()` object-file: get rid of `the_repository` in loose object iterators object-file: remove declaration for `for_each_file_in_obj_subdir()` object-file: inline `for_each_loose_file_in_objdir_buf()` object-file: get rid of `the_repository` when writing objects odb: introduce `odb_write_object()` loose: write loose objects map via their source object-file: get rid of `the_repository` in `finalize_object_file()` object-file: get rid of `the_repository` in `loose_object_info()` object-file: get rid of `the_repository` when freshening objects object-file: inline `check_and_freshen()` functions object-file: get rid of `the_repository` in `has_loose_object()` object-file: stop using `the_hash_algo` object-file: fix -Wsign-compare warnings
2025-08-05t: add test for git refs list subcommandMeet Soni2-0/+9
Add a test script, `t/t1461-refs-list.sh`, for the new `git refs list` command. This script acts as a simple driver, leveraging the shared test library created in the preceding commit. It works by overriding the `$git_for_each_ref` variable to "git refs list" and then sourcing the shared library (`t/for-each-ref-tests.sh`). This approach ensures that `git refs list` is tested against the entire comprehensive test suite of `git for-each-ref`, verifying that it acts as a compatible drop-in replacement. Mentored-by: Patrick Steinhardt <ps@pks.im> Mentored-by: shejialuo <shejialuo@gmail.com> Mentored-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: Meet Soni <meetsoni3017@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-05t6300: refactor tests to be shareableMeet Soni2-2138/+2143
In preparation for adding tests for the new `git refs list` command, refactor the existing t6300 test suite to make its logic shareable. Move the core test logic from `t6300-for-each-ref.sh` into a new `for-each-ref-tests.sh` file. Inside this new script, replace hardcoded calls to "git for-each-ref" with the `$git_for_each_ref` variable. The original `t6300-for-each-ref.sh` script now becomes a simple "driver". It is responsible for setting the default value of the variable and then sourcing the test library. This new structure follows the established pattern used for sharing tests between `git-blame` and `git-annotate` and prepares the test suite for the `refs list` tests to be added in a subsequent commit. Mentored-by: Patrick Steinhardt <ps@pks.im> Mentored-by: shejialuo <shejialuo@gmail.com> Mentored-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: Meet Soni <meetsoni3017@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-05builtin/refs: add list subcommandMeet Soni2-0/+30
Git's reference management is distributed across multiple commands. As part of an ongoing effort to consolidate and modernize reference handling, introduce a `list` subcommand under the `git refs` umbrella as a replacement for `git for-each-ref`. Implement `cmd_refs_list` by having it call the `for_each_ref_core()` helper function. This helper was factored out of the original `cmd_for_each_ref` in a preceding commit, allowing both commands to share the same core logic as independent peers. Add documentation for the new command. The man page leverages the shared options file, created in a previous commit, by using the AsciiDoc `include::` macro to ensure consistency with git-for-each-ref(1). Mentored-by: Patrick Steinhardt <ps@pks.im> Mentored-by: shejialuo <shejialuo@gmail.com> Mentored-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: Meet Soni <meetsoni3017@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-05builtin/for-each-ref: factor out core logic into a helperMeet Soni2-22/+45
The implementation of `git for-each-ref` is monolithic within `cmd_for_each_ref()`, making it impossible to share its logic with other commands. To enable code reuse for the upcoming `git refs list` subcommand, refactor the core logic into a shared helper function. Introduce a new `for-each-ref.h` header to define the public interface for this shared logic. It contains the declaration for a new helper function, `for_each_ref_core()`, and a macro for the common usage options. Move the option parsing, filtering, and formatting logic from `cmd_for_each_ref()` into a new helper function named `for_each_ref_core()`. This helper is made generic by accepting the command's usage string as a parameter. The original `cmd_for_each_ref()` is simplified to a thin wrapper that is only responsible for defining its specific usage array and calling the shared helper. Mentored-by: Patrick Steinhardt <ps@pks.im> Mentored-by: shejialuo <shejialuo@gmail.com> Mentored-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: Meet Soni <meetsoni3017@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-05builtin/for-each-ref: align usage string with the man pageMeet Soni2-6/+10
Usage string for `git for-each-ref` was out of sync with its official documentation. The test `t0450-txt-doc-vs-help.sh` was marked as broken due to this. Update the usage string to match the documentation. This allows the test to pass, so remove the corresponding 'known breakage' marker from the test file. Mentored-by: Patrick Steinhardt <ps@pks.im> Mentored-by: shejialuo <shejialuo@gmail.com> Mentored-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: Meet Soni <meetsoni3017@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-05doc: factor out common optionMeet Soni2-88/+89
In preparation for adding documentation for `git refs list`, factor out the common options from the `git-for-each-ref` man page into a shareable file `for-each-ref-options.adoc` and update `git-for-each-ref.adoc` to use an `include::` macro. This change is a pure refactoring and results in no change to the final rendered documentation for `for-each-ref`. Mentored-by: Patrick Steinhardt <ps@pks.im> Mentored-by: shejialuo <shejialuo@gmail.com> Mentored-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: Meet Soni <meetsoni3017@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-05t/unit-tests/clar: fix -Wmaybe-uninitialized with -OgDenton Liu1-1/+1
When building with -Og on gcc 15.1.1, the build produces a warning. In practice, though, this cannot be hit because `exact` acts as a guard and that variable can only be set after `matchlen` is already initialized Assign a default value to `matchlen` so that the warning is silenced. Signed-off-by: Denton Liu <liu.denton@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-05remote: bail early from set_head() if missing remote nameJeff King1-4/+7
In "git remote set-head", we can take varying numbers of arguments depending on whether we saw the "-d" or "-a" options. But the first argument is always the remote name. The current code is somewhat awkward in that it conditionally handles the remote name up-front like this: if (argc) remote = ...from argv[0]... and then only later decides to bail if we do not have the right number of arguments for the options we saw. This makes it hard to figure out if "remote" is always set when it needs to be. Both for humans, but also for compilers; with -Og, gcc complains that "remote" can be accessed without being initialized (although this is not true, as we'd always die with a usage message in that case). Let's instead enforce the presence of the remote argument up front, which fixes the compiler warning and is easier to understand. It does mean duplicating the code to print a usage message, but it's a single line. Noticed-by: Denton Liu <liu.denton@gmail.com> Signed-off-by: Jeff King <peff@peff.net> Tested-by: Denton Liu <liu.denton@gmail.com> Signed-off-by: Denton Liu <liu.denton@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-04archive: flush deflate stream until Z_STREAM_ENDJustin Tobler1-6/+14
In `archive-zip.c:write_zip_entry()` when using a stream as input for deflating a file, the call to `git_deflate()` with Z_FINISH always expects Z_STREAM_END to be returned. Per zlib documentation[1]: If the parameter flush is set to Z_FINISH, pending input is processed, pending output is flushed and deflate returns with Z_STREAM_END if there was enough output space. If deflate returns with Z_OK or Z_BUF_ERROR, this function must be called again with Z_FINISH and more output space (updated avail_out) but no more input data, until it returns with Z_STREAM_END or an error. After deflate has returned Z_STREAM_END, the only possible operations on the stream are deflateReset or deflateEnd. In scenarios where the output buffer is not large enough to write all the compressed data, it is perfectly valid for the underlying `deflate()` to return Z_OK. Thus, expecting a single pass of `deflate()` here to always return Z_STREAM_END is a bug. Update the code to flush the deflate stream until Z_STREAM_END is returned. [1]: https://zlib.net/manual.html Helped-by: Toon Claes <toon@iotcl.com> Signed-off-by: Justin Tobler <jltobler@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-08-04Merge branch 'master' of https://github.com/j6t/git-guiJunio C Hamano27-1052/+197
* 'master' of https://github.com/j6t/git-gui: (21 commits) git-gui: ensure own version of git-gui--askpass is used git-gui: Allow Tcl 9.0 git-gui: use -profile tcl8 on encoding conversions git-gui: use -profile tcl8 for file input with Tcl 9 git-gui: themed.tcl: use full namespace for color git-gui: remove EOL translation for gets git-gui: honor TCLTK_PATH in git-gui--askpass git-gui: retire Git Gui.app git-gui: fix dependency of GITGUI_MAIN on generator git-gui: remove uname_O in Makefile git-gui i18n: Remove the locations within the Bulgarian translation git-gui i18n: Update Bulgarian translation (557t) git-gui: do not mix -translation binary and -encoding git-gui: replace encoding binary with iso8859-1 git-gui: translation binary defines iso8859-1 git-gui: assure -eofchar {} on all channels git-gui: use /cmd/git-gui.exe for shortcut git-gui: Windows tk_getSaveFile is not useful for shortcuts git-gui: let nice work on Windows git-gui: do not add directories to PATH on Windows ...
2025-08-04Merge branch 'master' of https://github.com/j6t/gitkJunio C Hamano2-414/+146
* 'master' of https://github.com/j6t/gitk: gitk: Mention globs in description of preference to hide custom refs gitk: filter invisible upstream refs from reference list gitk: avoid duplicated upstream refs gitk i18n: Remove the locations within the Bulgarian translation gitk i18n: Update Bulgarian translation (322t) gitk: allow Tcl/Tk 9.0+ gitk: use -profile tcl8 on encoding conversions gitk: use -profile tcl8 for file input with Tcl 9 gitk: Tcl9 doesn't expand ~, use $env(HOME) gitk: switch to -translation binary gitk: update scrolling for TclTk 8.7+ / TIP 474 gitk: restore ui colors after cancelling config dialog gitk: set config dialog color swatches in one place gitk: Add user preference to hide specific references
2025-08-04Merge branch 'cb/no-tcl86-on-macos'Johannes Sixt12-247/+46
* cb/no-tcl86-on-macos: git-gui: ensure own version of git-gui--askpass is used git-gui: honor TCLTK_PATH in git-gui--askpass git-gui: retire Git Gui.app git-gui: fix dependency of GITGUI_MAIN on generator git-gui: remove uname_O in Makefile
2025-08-04git-gui: ensure own version of git-gui--askpass is usedCarlo Marcelo Arenas Belón1-1/+3
When finding a location for the askpass helper, git will be asked for its exec path, but if that git is not the same that called git-gui then we might mistakenly point to its helper instead. Assume that git-gui and the helper are colocated to derive its path instead. This is specially useful in macOS where a broken version of that helper is provided by the system git. [j6t: move directory to variable to help in-flight topics] Suggested-by: Mark Levedahl <mlevedahl@gmail.com> Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com> Signed-off-by: Johannes Sixt <j6t@kdbg.org>
2025-08-04Merge branch 'docglobs' of github.com:ilyagr/gitkJohannes Sixt1-1/+1
* 'docglobs' of github.com:ilyagr/gitk: gitk: Mention globs in description of preference to hide custom refs Signed-off-by: Johannes Sixt <j6t@kdbg.org>