From 48513e05e2f226c85a9b88893630c8ae28409772 Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Fri, 22 May 2026 23:12:25 +0000 Subject: [PATCH 01/16] stash: reuse cached index entries in --patch temporary index `git stash -p` prepares the interactive selection by creating a temporary index at HEAD, switching `GIT_INDEX_FILE` to it, and then running the `add -p` machinery. That temporary index was created by running `git read-tree HEAD`. The resulting index had no useful cached stat data or fsmonitor-valid bits from the real index. When `run_add_p()` refreshed that temporary index before showing the first prompt, it could end up lstat(2)-ing every tracked file, even in a repository where `git diff` and `git restore -p` can use fsmonitor to avoid that work. Create the temporary index in-process instead. Use `unpack_trees()` to reset the real index contents to HEAD while writing the result to the temporary index path. For paths whose index entries already match HEAD, `oneway_merge()` reuses the existing cache entries, preserving their cached stat data and `CE_FSMONITOR_VALID` state. This makes the refresh performed by `run_add_p()` behave like the one used by `git restore -p`: unchanged paths can be skipped via fsmonitor instead of being scanned again. In a 206k file repository with `core.fsmonitor` enabled and a one-line change in one file, time to first prompt dropped from 34.774 seconds to 0.659 seconds. The new perf test file demonstrates similar improvements, with maen times for without- and with-fsmonitor cases dropping from 6.90 and 6.83 seconds to 0.55 and 0.28 seconds, respectively. Signed-off-by: Adam Johnson Signed-off-by: Junio C Hamano --- builtin/stash.c | 70 +++++++++++++++++++++++++++++++++---- t/perf/p3904-stash-patch.sh | 43 +++++++++++++++++++++++ 2 files changed, 107 insertions(+), 6 deletions(-) create mode 100755 t/perf/p3904-stash-patch.sh diff --git a/builtin/stash.c b/builtin/stash.c index 0d27b2fb1fcb67..640be770c5d02a 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -372,6 +372,56 @@ static int reset_tree(struct object_id *i_tree, int update, int reset) return 0; } +static int create_index_from_tree(const struct object_id *tree_id, + const char *index_path) +{ + int nr_trees = 1; + int ret = 0; + struct unpack_trees_options opts; + struct tree_desc t[MAX_UNPACK_TREES]; + struct tree *tree; + struct index_state dst_istate = INDEX_STATE_INIT(the_repository); + struct lock_file lock_file = LOCK_INIT; + + repo_read_index_preload(the_repository, NULL, 0); + refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL); + + hold_lock_file_for_update(&lock_file, index_path, LOCK_DIE_ON_ERROR); + + memset(&opts, 0, sizeof(opts)); + + tree = repo_parse_tree_indirect(the_repository, tree_id); + if (!tree || repo_parse_tree(the_repository, tree)) { + ret = -1; + goto done; + } + + init_tree_desc(t, &tree->object.oid, tree->buffer, tree->size); + + opts.head_idx = 1; + opts.src_index = the_repository->index; + opts.dst_index = &dst_istate; + opts.merge = 1; + opts.reset = UNPACK_RESET_PROTECT_UNTRACKED; + opts.fn = oneway_merge; + + if (unpack_trees(nr_trees, t, &opts)) { + ret = -1; + goto done; + } + + if (write_locked_index(&dst_istate, &lock_file, COMMIT_LOCK)) { + ret = error(_("unable to write new index file")); + goto done; + } + +done: + release_index(&dst_istate); + if (ret) + rollback_lock_file(&lock_file); + return ret; +} + static int diff_tree_binary(struct strbuf *out, struct object_id *w_commit) { struct child_process cp = CHILD_PROCESS_INIT; @@ -1309,18 +1359,26 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps, struct interactive_options *interactive_opts) { int ret = 0; - struct child_process cp_read_tree = CHILD_PROCESS_INIT; struct child_process cp_diff_tree = CHILD_PROCESS_INIT; + struct commit *head_commit; + const struct object_id *head_tree; struct index_state istate = INDEX_STATE_INIT(the_repository); char *old_index_env = NULL, *old_repo_index_file; remove_path(stash_index_path.buf); - cp_read_tree.git_cmd = 1; - strvec_pushl(&cp_read_tree.args, "read-tree", "HEAD", NULL); - strvec_pushf(&cp_read_tree.env, "GIT_INDEX_FILE=%s", - stash_index_path.buf); - if (run_command(&cp_read_tree)) { + head_commit = lookup_commit(the_repository, &info->b_commit); + if (!head_commit || repo_parse_commit(the_repository, head_commit)) { + ret = -1; + goto done; + } + head_tree = get_commit_tree_oid(head_commit); + if (!head_tree) { + ret = -1; + goto done; + } + + if (create_index_from_tree(head_tree, stash_index_path.buf)) { ret = -1; goto done; } diff --git a/t/perf/p3904-stash-patch.sh b/t/perf/p3904-stash-patch.sh new file mode 100755 index 00000000000000..4cfce638bed6f4 --- /dev/null +++ b/t/perf/p3904-stash-patch.sh @@ -0,0 +1,43 @@ +#!/bin/sh + +test_description="Performance tests for git stash -p" + +. ./perf-lib.sh + +test_perf_fresh_repo + +test_expect_success "setup" ' + mkdir files && + test_seq 1 100000 | while read i; do + echo "content $i" >files/$i.txt || return 1 + done && + git add files/ && + git commit -q -m "add tracked files" && + echo modified >files/1.txt +' + +test_perf "stash -p, no fsmonitor" \ + --setup 'echo modified >files/1.txt' ' + printf "q\n" | git stash -p >/dev/null 2>&1 || true +' + +if test_have_prereq FSMONITOR_DAEMON +then + test_expect_success "enable builtin fsmonitor" ' + git config core.fsmonitor true && + git fsmonitor--daemon start && + git update-index --fsmonitor && + git status >/dev/null 2>&1 + ' + + test_perf "stash -p, builtin fsmonitor" \ + --setup 'echo modified >files/1.txt && git status >/dev/null 2>&1' ' + printf "q\n" | git stash -p >/dev/null 2>&1 || true + ' + + test_expect_success "stop builtin fsmonitor" ' + git fsmonitor--daemon stop + ' +fi + +test_done From 50cd5219d2b63f4896a3d142f83fadf8e47a6c3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= Date: Mon, 25 May 2026 10:28:22 +0000 Subject: [PATCH 02/16] doc: convert git-bisect to synopsis style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert Documentation/git-bisect.adoc to the modern synopsis style. - Replace [verse] with [synopsis] in the SYNOPSIS block - Remove single quotes around command names in the synopsis - Use backticks for inline commands, options, refs, and special values - Apply [synopsis] attribute to in-body command-form code blocks - Format OPTIONS entries with backtick-quoted terms and direct - Add synopsis-style formatting to listing blocks - Format man page references as `command`(N) Signed-off-by: Jean-Noël Avila Signed-off-by: Junio C Hamano --- Documentation/asciidoc.conf.in | 6 +++ Documentation/git-bisect.adoc | 90 ++++++++++++++++------------------ 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/Documentation/asciidoc.conf.in b/Documentation/asciidoc.conf.in index 31b883a72c5739..93c63b284a9669 100644 --- a/Documentation/asciidoc.conf.in +++ b/Documentation/asciidoc.conf.in @@ -84,6 +84,9 @@ ifdef::doctype-manpage[] [blockdef-open] synopsis-style=template="verseparagraph",filter="sed 's!…\\(\\]\\|$\\)!\\0!g;s!\\([\\[ |()]\\|^\\|\\]\\|>\\)\\([-=a-zA-Z0-9:+@,\\/_^\\$.\\\\\\*]\\+\\|…\\)!\\1\\2!g;s!<[-a-zA-Z0-9.]\\+>!\\0!g'" +[blockdef-listing] +synopsis-style=template="verseparagraph",filter="sed 's!…\\(\\]\\|$\\)!\\0!g;s!\\([\\[ |()]\\|^\\|\\]\\|>\\)\\([-=a-zA-Z0-9:+@,\\/_^\\$.\\\\\\*]\\+\\|…\\)!\\1\\2!g;s!<[-a-zA-Z0-9.]\\+>!\\0!g'" + [paradef-default] synopsis-style=template="verseparagraph",filter="sed 's!…\\(\\]\\|$\\)!\\0!g;s!\\([\\[ |()]\\|^\\|\\]\\|>\\)\\([-=a-zA-Z0-9:+@,\\/_^\\$.\\\\\\*]\\+\\|…\\)!\\1\\2!g;s!<[-a-zA-Z0-9.]\\+>!\\0!g'" endif::doctype-manpage[] @@ -93,6 +96,9 @@ ifdef::backend-xhtml11[] [blockdef-open] synopsis-style=template="verseparagraph",filter="sed 's!…\\(\\]\\|$\\)!\\0!g;s!\\([\\[ |()]\\|^\\|\\]\\|>\\)\\([-=a-zA-Z0-9:+@,\\/_^\\$.\\\\\\*]\\+\\|…\\)!\\1\\2!g;s!<[-a-zA-Z0-9.]\\+>!\\0!g'" +[blockdef-listing] +synopsis-style=template="verseparagraph",filter="sed 's!…\\(\\]\\|$\\)!\\0!g;s!\\([\\[ |()]\\|^\\|\\]\\|>\\)\\([-=a-zA-Z0-9:+@,\\/_^\\$.\\\\\\*]\\+\\|…\\)!\\1\\2!g;s!<[-a-zA-Z0-9.]\\+>!\\0!g'" + [paradef-default] synopsis-style=template="verseparagraph",filter="sed 's!…\\(\\]\\|$\\)!\\0!g;s!\\([\\[ |()]\\|^\\|\\]\\|>\\)\\([-=a-zA-Z0-9:+@,\\/_^\\$.\\\\\\*]\\+\\|…\\)!\\1\\2!g;s!<[-a-zA-Z0-9.]\\+>!\\0!g'" endif::backend-xhtml11[] diff --git a/Documentation/git-bisect.adoc b/Documentation/git-bisect.adoc index b0078dda0eb050..4765d3b9691a10 100644 --- a/Documentation/git-bisect.adoc +++ b/Documentation/git-bisect.adoc @@ -8,20 +8,20 @@ git-bisect - Use binary search to find the commit that introduced a bug SYNOPSIS -------- -[verse] -'git bisect' start [--term-(bad|new)= --term-(good|old)=] - [--no-checkout] [--first-parent] [ [...]] [--] [...] -'git bisect' (bad|new|) [] -'git bisect' (good|old|) [...] -'git bisect' terms [--term-(good|old) | --term-(bad|new)] -'git bisect' skip [(|)...] -'git bisect' next -'git bisect' reset [] -'git bisect' (visualize|view) -'git bisect' replay -'git bisect' log -'git bisect' run [...] -'git bisect' help +[synopsis] +git bisect start [--term-(bad|new)= --term-(good|old)=] + [--no-checkout] [--first-parent] [ [...]] [--] [...] +git bisect (bad|new|) [] +git bisect (good|old|) [...] +git bisect terms [--term-(good|old) | --term-(bad|new)] +git bisect skip [(|)...] +git bisect next +git bisect reset [] +git bisect (visualize|view) +git bisect replay +git bisect log +git bisect run [...] +git bisect help DESCRIPTION ----------- @@ -94,7 +94,7 @@ Bisect reset ~~~~~~~~~~~~ After a bisect session, to clean up the bisection state and return to -the original HEAD, issue the following command: +the original `HEAD`, issue the following command: ------------------------------------------------ $ git bisect reset @@ -107,9 +107,8 @@ that, as it cleans up the old bisection state.) With an optional argument, you can return to a different commit instead: ------------------------------------------------- +[synopsis] $ git bisect reset ------------------------------------------------- For example, `git bisect reset bisect/bad` will check out the first bad revision, while `git bisect reset HEAD` will leave you on the @@ -143,23 +142,20 @@ To use "old" and "new" instead of "good" and bad, you must run `git bisect start` without commits as argument and then run the following commands to add the commits: ------------------------------------------------- +[synopsis] git bisect old [] ------------------------------------------------- to indicate that a commit was before the sought change, or ------------------------------------------------- +[synopsis] git bisect new [...] ------------------------------------------------- to indicate that it was after. To get a reminder of the currently used terms, use ------------------------------------------------- +[synopsis] git bisect terms ------------------------------------------------- You can get just the old term with `git bisect terms --term-old` or `git bisect terms --term-good`; `git bisect terms --term-new` @@ -171,9 +167,8 @@ If you would like to use your own terms instead of "bad"/"good" or subcommands like `reset`, `start`, ...) by starting the bisection using ------------------------------------------------- +[synopsis] git bisect start --term-old --term-new ------------------------------------------------- For example, if you are looking for a commit that introduced a performance regression, you might use @@ -194,7 +189,7 @@ of `git bisect good` and `git bisect bad` to mark commits. Bisect visualize/view ~~~~~~~~~~~~~~~~~~~~~ -To see the currently remaining suspects in 'gitk', issue the following +To see the currently remaining suspects in `gitk`, issue the following command during the bisection process (the subcommand `view` can be used as an alternative to `visualize`): @@ -203,12 +198,13 @@ $ git bisect visualize ------------ Git detects a graphical environment through various environment variables: -`DISPLAY`, which is set in X Window System environments on Unix systems. -`SESSIONNAME`, which is set under Cygwin in interactive desktop sessions. -`MSYSTEM`, which is set under Msys2 and Git for Windows. -`SECURITYSESSIONID`, which may be set on macOS in interactive desktop sessions. -If none of these environment variables is set, 'git log' is used instead. +`DISPLAY`:: which is set in X Window System environments on Unix systems. +`SESSIONNAME`:: which is set under Cygwin in interactive desktop sessions. +`MSYSTEM`:: which is set under Msys2 and Git for Windows. +`SECURITYSESSIONID`:: which may be set on macOS in interactive desktop sessions. + +If none of these environment variables is set, `git log` is used instead. You can also give command-line options such as `-p` and `--stat`. ------------ @@ -342,8 +338,8 @@ code between 1 and 127 (inclusive), except 125, if the current source code is bad/new. Any other exit code will abort the bisect process. It should be noted -that a program that terminates via `exit(-1)` leaves $? = 255, (see the -exit(3) manual page), as the value is chopped with `& 0377`. +that a program that terminates via `exit(-1)` leaves `$?` = 255, (see the +`exit`(3) manual page), as the value is chopped with `& 0377`. The special exit code 125 should be used when the current source code cannot be tested. If the script exits with this code, the current @@ -355,12 +351,12 @@ details do not matter, as they are normal errors in the script, as far as `bisect run` is concerned). You may often find that during a bisect session you want to have -temporary modifications (e.g. s/#define DEBUG 0/#define DEBUG 1/ in a +temporary modifications (e.g. `s/#define DEBUG 0/#define DEBUG 1/` in a header file, or "revision that does not have this commit needs this patch applied to work around another problem this bisection is not interested in") applied to the revision being tested. -To cope with such a situation, after the inner 'git bisect' finds the +To cope with such a situation, after the inner `git bisect` finds the next revision to test, the script can apply the patch before compiling, run the real test, and afterwards decide if the revision (possibly with the needed patch) passed the test and then @@ -370,20 +366,18 @@ determine the eventual outcome of the bisect session. OPTIONS ------- ---no-checkout:: -+ -Do not checkout the new working tree at each iteration of the bisection -process. Instead just update the reference named `BISECT_HEAD` to make -it point to the commit that should be tested. +`--no-checkout`:: + Do not checkout the new working tree at each iteration of the bisection + process. Instead just update the reference named `BISECT_HEAD` to make + it point to the commit that should be tested. + This option may be useful when the test you would perform in each step does not require a checked out tree. + If the repository is bare, `--no-checkout` is assumed. ---first-parent:: -+ -Follow only the first parent commit upon seeing a merge commit. +`--first-parent`:: + Follow only the first parent commit upon seeing a merge commit. + In detecting regressions introduced through the merging of a branch, the merge commit will be identified as introduction of the bug and its ancestors will be @@ -395,7 +389,7 @@ branch contained broken or non-buildable commits, but the merge itself was OK. EXAMPLES -------- -* Automatically bisect a broken build between v1.2 and HEAD: +* Automatically bisect a broken build between v1.2 and `HEAD`: + ------------ $ git bisect start HEAD v1.2 -- # HEAD is bad, v1.2 is good @@ -403,7 +397,7 @@ $ git bisect run make # "make" builds the app $ git bisect reset # quit the bisect session ------------ -* Automatically bisect a test failure between origin and HEAD: +* Automatically bisect a test failure between origin and `HEAD`: + ------------ $ git bisect start HEAD origin -- # HEAD is bad, origin is good @@ -430,7 +424,7 @@ and `exit 1` otherwise. + It is safer if both `test.sh` and `check_test_case.sh` are outside the repository to prevent interactions between the bisect, -make and test processes and the scripts. +`make` and test processes and the scripts. * Automatically bisect with temporary modifications (hot-fix): + @@ -491,9 +485,9 @@ $ git bisect run sh -c ' $ git bisect reset # quit the bisect session ------------ + -In this case, when 'git bisect run' finishes, bisect/bad will refer to a commit that +In this case, when `git bisect run` finishes, `bisect/bad` will refer to a commit that has at least one parent whose reachable graph is fully traversable in the sense -required by 'git pack objects'. +required by `git pack-objects`. * Look for a fix instead of a regression in the code + From ed31e2872a7306f52de1e2b9ab0065b9d91f3338 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= Date: Mon, 25 May 2026 10:28:23 +0000 Subject: [PATCH 03/16] doc: git bisect: clarify the usage of the synopsis vs actual command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The difference between a synopsis and an actual command is that the synopsis is a more abstract representation of the command, which may include placeholders for arguments and options. The actual command is the specific instance of the command with all the arguments and options filled in. The formatting of an actual command is a code block, with the command prefixed by a dollar sign ($) to indicate that it is a command to be run in the terminal. It can also include comments with a hash sign (#) to explain the command or provide additional information, just like in a regular terminal session. Signed-off-by: Jean-Noël Avila Signed-off-by: Junio C Hamano --- Documentation/git-bisect.adoc | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/Documentation/git-bisect.adoc b/Documentation/git-bisect.adoc index 4765d3b9691a10..d2115b29905f41 100644 --- a/Documentation/git-bisect.adoc +++ b/Documentation/git-bisect.adoc @@ -96,9 +96,8 @@ Bisect reset After a bisect session, to clean up the bisection state and return to the original `HEAD`, issue the following command: ------------------------------------------------- -$ git bisect reset ------------------------------------------------- +[synopsis] +git bisect reset By default, this will return your tree to the commit that was checked out before `git bisect start`. (A new `git bisect start` will also do @@ -108,7 +107,8 @@ With an optional argument, you can return to a different commit instead: [synopsis] -$ git bisect reset +git bisect reset + For example, `git bisect reset bisect/bad` will check out the first bad revision, while `git bisect reset HEAD` will leave you on the @@ -174,13 +174,13 @@ For example, if you are looking for a commit that introduced a performance regression, you might use ------------------------------------------------ -git bisect start --term-old fast --term-new slow +$ git bisect start --term-old fast --term-new slow ------------------------------------------------ Or if you are looking for the commit that fixed a bug, you might use ------------------------------------------------ -git bisect start --term-new fixed --term-old broken +$ git bisect start --term-new fixed --term-old broken ------------------------------------------------ Then, use `git bisect ` and `git bisect ` instead @@ -328,11 +328,10 @@ Bisect run If you have a script that can tell if the current source code is good or bad, you can bisect by issuing the command: ------------- -$ git bisect run my_script arguments ------------- +[synopsis] +git bisect run [...] -Note that the script (`my_script` in the above example) should exit +Note that __ run with __ should exit with code 0 if the current source code is good/old, and exit with a code between 1 and 127 (inclusive), except 125, if the current source code is bad/new. From 25d5d60958f1486cb8439863062f66ffef605f18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= Date: Mon, 25 May 2026 10:28:24 +0000 Subject: [PATCH 04/16] doc: convert git-grep synopsis and options to new style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert git-grep.adoc from [verse]/single-quote style to the modern synopsis-block style: - Replace [verse] with [synopsis] in SYNOPSIS block - Change 'git grep' to git grep (no single quotes) - Backtick-quote all OPTIONS terms - Convert inline man page refs: grep(1) -> `grep`(1) - Convert inline command refs: 'git diff' -> `git diff` - Convert prose placeholders: -> __ Signed-off-by: Jean-Noël Avila Signed-off-by: Junio C Hamano --- Documentation/config/grep.adoc | 36 +++--- Documentation/git-grep.adoc | 196 ++++++++++++++++----------------- 2 files changed, 116 insertions(+), 116 deletions(-) diff --git a/Documentation/config/grep.adoc b/Documentation/config/grep.adoc index 10041f27b0c8e2..83d4b76dd351e1 100644 --- a/Documentation/config/grep.adoc +++ b/Documentation/config/grep.adoc @@ -1,28 +1,28 @@ -grep.lineNumber:: - If set to true, enable `-n` option by default. +`grep.lineNumber`:: + If set to `true`, enable `-n` option by default. -grep.column:: - If set to true, enable the `--column` option by default. +`grep.column`:: + If set to `true`, enable the `--column` option by default. -grep.patternType:: - Set the default matching behavior. Using a value of 'basic', 'extended', - 'fixed', or 'perl' will enable the `--basic-regexp`, `--extended-regexp`, +`grep.patternType`:: + Set the default matching behavior. Using a value of `basic`, `extended`, + `fixed`, or `perl` will enable the `--basic-regexp`, `--extended-regexp`, `--fixed-strings`, or `--perl-regexp` option accordingly, while the - value 'default' will use the `grep.extendedRegexp` option to choose - between 'basic' and 'extended'. + value `default` will use the `grep.extendedRegexp` option to choose + between `basic` and `extended`. -grep.extendedRegexp:: - If set to true, enable `--extended-regexp` option by default. This +`grep.extendedRegexp`:: + If set to `true`, enable `--extended-regexp` option by default. This option is ignored when the `grep.patternType` option is set to a value - other than 'default'. + other than `default`. -grep.threads:: +`grep.threads`:: Number of grep worker threads to use. If unset (or set to 0), Git will use as many threads as the number of logical cores available. -grep.fullName:: - If set to true, enable `--full-name` option by default. +`grep.fullName`:: + If set to `true`, enable `--full-name` option by default. -grep.fallbackToNoIndex:: - If set to true, fall back to `git grep --no-index` if `git grep` - is executed outside of a git repository. Defaults to false. +`grep.fallbackToNoIndex`:: + If set to `true`, fall back to `git grep --no-index` if `git grep` + is executed outside of a git repository. Defaults to `false`. diff --git a/Documentation/git-grep.adoc b/Documentation/git-grep.adoc index a548585d4cbad5..19b3ade16dd51a 100644 --- a/Documentation/git-grep.adoc +++ b/Documentation/git-grep.adoc @@ -8,8 +8,8 @@ git-grep - Print lines matching a pattern SYNOPSIS -------- -[verse] -'git grep' [-a | --text] [-I] [--textconv] [-i | --ignore-case] [-w | --word-regexp] +[synopsis] +git grep [-a | --text] [-I] [--textconv] [-i | --ignore-case] [-w | --word-regexp] [-v | --invert-match] [-h|-H] [--full-name] [-E | --extended-regexp] [-G | --basic-regexp] [-P | --perl-regexp] @@ -41,139 +41,139 @@ characters. An empty string as search expression matches all lines. OPTIONS ------- ---cached:: +`--cached`:: Instead of searching tracked files in the working tree, search blobs registered in the index file. ---untracked:: +`--untracked`:: In addition to searching in the tracked files in the working tree, search also in untracked files. ---no-index:: +`--no-index`:: Search files in the current directory that is not managed by Git, or by ignoring that the current directory is managed by Git. This - is rather similar to running the regular `grep(1)` utility with its + is rather similar to running the regular `grep`(1) utility with its `-r` option specified, but with some additional benefits, such as - using pathspec patterns to limit paths; see the 'pathspec' entry + using pathspec patterns to limit paths; see the `pathspec` entry in linkgit:gitglossary[7] for more information. + This option cannot be used together with `--cached` or `--untracked`. See also `grep.fallbackToNoIndex` in 'CONFIGURATION' below. ---no-exclude-standard:: +`--no-exclude-standard`:: Also search in ignored files by not honoring the `.gitignore` mechanism. Only useful with `--untracked`. ---exclude-standard:: +`--exclude-standard`:: Do not pay attention to ignored files specified via the `.gitignore` mechanism. Only useful when searching files in the current directory with `--no-index`. ---recurse-submodules:: +`--recurse-submodules`:: Recursively search in each submodule that is active and checked out in the repository. When used in combination with the __ option the prefix of all submodule output will be the name of the parent project's __ object. This option cannot be used together with `--untracked`, and it has no effect if `--no-index` is specified. --a:: ---text:: +`-a`:: +`--text`:: Process binary files as if they were text. ---textconv:: +`--textconv`:: Honor textconv filter settings. ---no-textconv:: +`--no-textconv`:: Do not honor textconv filter settings. This is the default. --i:: ---ignore-case:: +`-i`:: +`--ignore-case`:: Ignore case differences between the patterns and the files. --I:: +`-I`:: Don't match the pattern in binary files. ---max-depth :: - For each given on command line, descend at most +`--max-depth `:: + For each __ given on command line, descend at most __ levels of directories. A value of -1 means no limit. - This option is ignored if contains active wildcards. + This option is ignored if __ contains active wildcards. In other words if "a*" matches a directory named "a*", - "*" is matched literally so --max-depth is still effective. + "*" is matched literally so `--max-depth` is still effective. --r:: ---recursive:: +`-r`:: +`--recursive`:: Same as `--max-depth=-1`; this is the default. ---no-recursive:: +`--no-recursive`:: Same as `--max-depth=0`. --w:: ---word-regexp:: +`-w`:: +`--word-regexp`:: Match the pattern only at word boundary (either begin at the beginning of a line, or preceded by a non-word character; end at the end of a line or followed by a non-word character). --v:: ---invert-match:: +`-v`:: +`--invert-match`:: Select non-matching lines. --h:: --H:: +`-h`:: +`-H`:: By default, the command shows the filename for each match. `-h` option is used to suppress this output. `-H` is there for completeness and does not do anything except it overrides `-h` given earlier on the command line. ---full-name:: +`--full-name`:: When run from a subdirectory, the command usually outputs paths relative to the current directory. This option forces paths to be output relative to the project top directory. --E:: ---extended-regexp:: --G:: ---basic-regexp:: +`-E`:: +`--extended-regexp`:: +`-G`:: +`--basic-regexp`:: Use POSIX extended/basic regexp for patterns. Default is to use basic regexp. --P:: ---perl-regexp:: +`-P`:: +`--perl-regexp`:: Use Perl-compatible regular expressions for patterns. + Support for these types of regular expressions is an optional compile-time dependency. If Git wasn't compiled with support for them providing this option will cause it to die. --F:: ---fixed-strings:: +`-F`:: +`--fixed-strings`:: Use fixed strings for patterns (don't interpret pattern as a regex). --n:: ---line-number:: +`-n`:: +`--line-number`:: Prefix the line number to matching lines. ---column:: +`--column`:: Prefix the 1-indexed byte-offset of the first match from the start of the matching line. --l:: ---files-with-matches:: ---name-only:: --L:: ---files-without-match:: +`-l`:: +`--files-with-matches`:: +`--name-only`:: +`-L`:: +`--files-without-match`:: Instead of showing every matched line, show only the names of files that contain (or do not contain) matches. - For better compatibility with 'git diff', `--name-only` is a + For better compatibility with `git diff`, `--name-only` is a synonym for `--files-with-matches`. --O[]:: ---open-files-in-pager[=]:: - Open the matching files in the pager (not the output of 'grep'). +`-O[]`:: +`--open-files-in-pager[=]`:: + Open the matching files in the pager (not the output of `grep`). If the pager happens to be "less" or "vi", and the user specified only one pattern, the first file is positioned at the first match automatically. The `pager` argument is @@ -181,65 +181,65 @@ providing this option will cause it to die. without a space. If `pager` is unspecified, the default pager will be used (see `core.pager` in linkgit:git-config[1]). --z:: ---null:: +`-z`:: +`--null`:: Use \0 as the delimiter for pathnames in the output, and print them verbatim. Without this option, pathnames with "unusual" characters are quoted as explained for the configuration variable `core.quotePath` (see linkgit:git-config[1]). --o:: ---only-matching:: +`-o`:: +`--only-matching`:: Print only the matched (non-empty) parts of a matching line, with each such part on a separate output line. --c:: ---count:: +`-c`:: +`--count`:: Instead of showing every matched line, show the number of lines that match. ---color[=]:: +`--color[=]`:: Show colored matches. - The value must be always (the default), never, or auto. + The value must be `always` (the default), `never`, or `auto`. ---no-color:: +`--no-color`:: Turn off match highlighting, even when the configuration file gives the default to color output. Same as `--color=never`. ---break:: +`--break`:: Print an empty line between matches from different files. ---heading:: +`--heading`:: Show the filename above the matches in that file instead of at the start of each shown line. --p:: ---show-function:: +`-p`:: +`--show-function`:: Show the preceding line that contains the function name of the match, unless the matching line is a function name itself. The name is determined in the same way as `git diff` works out patch hunk headers (see 'Defining a custom hunk-header' in linkgit:gitattributes[5]). --:: --C :: ---context :: - Show leading and trailing lines, and place a line +`-`:: +`-C `:: +`--context `:: + Show __ leading and trailing lines, and place a line containing `--` between contiguous groups of matches. --A :: ---after-context :: - Show trailing lines, and place a line containing +`-A `:: +`--after-context `:: + Show __ trailing lines, and place a line containing `--` between contiguous groups of matches. --B :: ---before-context :: - Show leading lines, and place a line containing +`-B `:: +`--before-context `:: + Show __ leading lines, and place a line containing `--` between contiguous groups of matches. --W:: ---function-context:: +`-W`:: +`--function-context`:: Show the surrounding text from the previous line containing a function name up to the one before the next function name, effectively showing the whole function in which the match was @@ -247,22 +247,22 @@ providing this option will cause it to die. `git diff` works out patch hunk headers (see 'Defining a custom hunk-header' in linkgit:gitattributes[5]). --m :: ---max-count :: +`-m `:: +`--max-count `:: Limit the amount of matches per file. When using the `-v` or `--invert-match` option, the search stops after the specified number of non-matches. A value of -1 will return unlimited results (the default). A value of 0 will exit immediately with a non-zero status. ---threads :: - Number of `grep` worker threads to use. See 'NOTES ON THREADS' +`--threads `:: + Number of `grep` worker threads to use. See `NOTES ON THREADS` and `grep.threads` in 'CONFIGURATION' for more information. --f :: - Read patterns from , one per line. +`-f `:: + Read patterns from __, one per line. + -Passing the pattern via allows for providing a search pattern +Passing the pattern via __ allows for providing a search pattern containing a \0. + Not all pattern types support patterns containing \0. Git will error @@ -279,44 +279,44 @@ In future versions we may learn to support patterns containing \0 for more search backends, until then we'll die when the pattern type in question doesn't support them. --e:: +`-e`:: The next parameter is the pattern. This option has to be used for patterns starting with `-` and should be used in scripts passing user input to grep. Multiple patterns are - combined by 'or'. + combined by `or`. ---and:: ---or:: ---not:: -( ... ):: +`--and`:: +`--or`:: +`--not`:: +`( ... )`:: Specify how multiple patterns are combined using Boolean expressions. `--or` is the default operator. `--and` has higher precedence than `--or`. `-e` has to be used for all patterns. ---all-match:: +`--all-match`:: When giving multiple pattern expressions combined with `--or`, this flag is specified to limit the match to files that have lines to match all of them. --q:: ---quiet:: +`-q`:: +`--quiet`:: Do not output matched lines; instead, exit with status 0 when there is a match and with non-zero status when there isn't. -...:: +`...`:: Instead of searching tracked files in the working tree, search blobs in the given trees. -\--:: +`--`:: Signals the end of options; the rest of the parameters - are limiters. + are __ limiters. -...:: +`...`:: If given, limit the search to paths matching at least one pattern. - Both leading paths match and glob(7) patterns are supported. + Both leading paths match and `glob`(7) patterns are supported. + -For more details about the syntax, see the 'pathspec' entry +For more details about the __ syntax, see the `pathspec` entry in linkgit:gitglossary[7]. EXAMPLES From 242d3aa317ccaa3e7c5f6bf2218ccbdd8a0a26e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= Date: Mon, 25 May 2026 10:28:25 +0000 Subject: [PATCH 05/16] doc: convert git-am synopsis and options to new style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert git-am from [verse]/single-quote style to the modern synopsis-block style: - Replace [verse] with [synopsis] in SYNOPSIS block - Backtick-quote all OPTIONS terms - Convert inline man page refs - Convert inline command refs - Convert prose placeholders: Signed-off-by: Jean-Noël Avila Signed-off-by: Junio C Hamano --- Documentation/config/am.adoc | 6 +- Documentation/format-patch-caveats.adoc | 2 +- .../format-patch-end-of-commit-message.adoc | 4 +- Documentation/git-am.adoc | 132 +++++++++--------- 4 files changed, 72 insertions(+), 72 deletions(-) diff --git a/Documentation/config/am.adoc b/Documentation/config/am.adoc index e9561e12d7467e..250e6b5047365d 100644 --- a/Documentation/config/am.adoc +++ b/Documentation/config/am.adoc @@ -1,11 +1,11 @@ -am.keepcr:: +`am.keepcr`:: If true, linkgit:git-am[1] will call linkgit:git-mailsplit[1] for patches in mbox format with parameter `--keep-cr`. In this case linkgit:git-mailsplit[1] will not remove `\r` from lines ending with `\r\n`. Can be overridden by giving `--no-keep-cr` from the command line. -am.threeWay:: +`am.threeWay`:: By default, linkgit:git-am[1] will fail if the patch does not apply cleanly. When set to true, this setting tells linkgit:git-am[1] to fall back on 3-way merge if the patch @@ -13,7 +13,7 @@ am.threeWay:: have those blobs available locally (equivalent to giving the `--3way` option from the command line). Defaults to `false`. -am.messageId:: +`am.messageId`:: Add a `Message-ID` trailer based on the email header to the commit when using linkgit:git-am[1] (see linkgit:git-interpret-trailers[1]). See also the `--message-id` diff --git a/Documentation/format-patch-caveats.adoc b/Documentation/format-patch-caveats.adoc index 807a65b885b09a..133e4757e7946c 100644 --- a/Documentation/format-patch-caveats.adoc +++ b/Documentation/format-patch-caveats.adoc @@ -28,6 +28,6 @@ repositories. This goes to show that this behavior does not only impact email workflows. Given these limitations, one might be tempted to use a general-purpose -utility like patch(1) instead. However, patch(1) will not only look for +utility like `patch`(1) instead. However, `patch`(1) will not only look for unindented diffs (like linkgit:git-am[1]) but will try to apply indented diffs as well. diff --git a/Documentation/format-patch-end-of-commit-message.adoc b/Documentation/format-patch-end-of-commit-message.adoc index ec1ef79f5e3308..a1a624d2ac5bc2 100644 --- a/Documentation/format-patch-end-of-commit-message.adoc +++ b/Documentation/format-patch-end-of-commit-message.adoc @@ -1,8 +1,8 @@ Any line that is of the form: * three-dashes and end-of-line, or -* a line that begins with "diff -", or -* a line that begins with "Index: " +* a line that begins with `diff -`, or +* a line that begins with `Index: ` is taken as the beginning of a patch, and the commit log message is terminated before the first occurrence of such a line. diff --git a/Documentation/git-am.adoc b/Documentation/git-am.adoc index ac65852918f3ee..28adf4cf656651 100644 --- a/Documentation/git-am.adoc +++ b/Documentation/git-am.adoc @@ -8,17 +8,17 @@ git-am - Apply a series of patches from a mailbox SYNOPSIS -------- -[verse] -'git am' [--signoff] [--keep] [--[no-]keep-cr] [--[no-]utf8] [--[no-]verify] +[synopsis] +git am [--signoff] [--keep] [--[no-]keep-cr] [--[no-]utf8] [--[no-]verify] [--[no-]3way] [--interactive] [--committer-date-is-author-date] [--ignore-date] [--ignore-space-change | --ignore-whitespace] [--whitespace=] [-C] [-p] [--directory=] [--exclude=] [--include=] [--reject] [-q | --quiet] - [--[no-]scissors] [-S[]] [--patch-format=] + [--[no-]scissors] [-S[]] [--patch-format=] [--quoted-cr=] [--empty=(stop|drop|keep)] [( | )...] -'git am' (--continue | --skip | --abort | --quit | --retry | --show-current-patch[=(diff|raw)] | --allow-empty) +git am (--continue | --skip | --abort | --quit | --retry | --show-current-patch[=(diff|raw)] | --allow-empty) DESCRIPTION ----------- @@ -30,45 +30,45 @@ history without merges. OPTIONS ------- -(|)...:: +`(|)...`:: The list of mailbox files to read patches from. If you do not supply this argument, the command reads from the standard input. If you supply directories, they will be treated as Maildirs. --s:: ---signoff:: +`-s`:: +`--signoff`:: Add a `Signed-off-by` trailer to the commit message (see linkgit:git-interpret-trailers[1]), using the committer identity of yourself. See the signoff option in linkgit:git-commit[1] for more information. --k:: ---keep:: +`-k`:: +`--keep`:: Pass `-k` flag to linkgit:git-mailinfo[1]. ---keep-non-patch:: +`--keep-non-patch`:: Pass `-b` flag to linkgit:git-mailinfo[1]. ---keep-cr:: ---no-keep-cr:: +`--keep-cr`:: +`--no-keep-cr`:: With `--keep-cr`, call linkgit:git-mailsplit[1] with the same option, to prevent it from stripping CR at the end of lines. `am.keepcr` configuration variable can be used to specify the default behaviour. `--no-keep-cr` is useful to override `am.keepcr`. --c:: ---scissors:: +`-c`:: +`--scissors`:: Remove everything in body before a scissors line (see linkgit:git-mailinfo[1]). Can be activated by default using the `mailinfo.scissors` configuration variable. ---no-scissors:: +`--no-scissors`:: Ignore scissors lines (see linkgit:git-mailinfo[1]). ---quoted-cr=:: +`--quoted-cr=`:: This flag will be passed down to linkgit:git-mailinfo[1]. ---empty=(drop|keep|stop):: +`--empty=(drop|keep|stop)`:: How to handle an e-mail message lacking a patch: + -- @@ -82,23 +82,23 @@ OPTIONS session. This is the default behavior. -- --m:: ---message-id:: +`-m`:: +`--message-id`:: Pass the `-m` flag to linkgit:git-mailinfo[1], so that the `Message-ID` header is added to the commit message. The `am.messageid` configuration variable can be used to specify the default behaviour. ---no-message-id:: +`--no-message-id`:: Do not add the Message-ID header to the commit message. `--no-message-id` is useful to override `am.messageid`. --q:: ---quiet:: +`-q`:: +`--quiet`:: Be quiet. Only print error messages. --u:: ---utf8:: +`-u`:: +`--utf8`:: Pass `-u` flag to linkgit:git-mailinfo[1]. The proposed commit log message taken from the e-mail is re-coded into UTF-8 encoding (configuration variable @@ -108,57 +108,57 @@ OPTIONS This was optional in prior versions of git, but now it is the default. You can use `--no-utf8` to override this. ---no-utf8:: +`--no-utf8`:: Pass `-n` flag to linkgit:git-mailinfo[1]. --3:: ---3way:: ---no-3way:: +`-3`:: +`--3way`:: +`--no-3way`:: When the patch does not apply cleanly, fall back on 3-way merge if the patch records the identity of blobs it is supposed to apply to and we have those blobs available locally. `--no-3way` can be used to override - am.threeWay configuration variable. For more information, - see am.threeWay in linkgit:git-config[1]. + `am.threeWay` configuration variable. For more information, + see `am.threeWay` in linkgit:git-config[1]. include::rerere-options.adoc[] ---ignore-space-change:: ---ignore-whitespace:: ---whitespace=:: --C:: --p:: ---directory=:: ---exclude=:: ---include=:: ---reject:: +`--ignore-space-change`:: +`--ignore-whitespace`:: +`--whitespace=`:: +`-C`:: +`-p`:: +`--directory=`:: +`--exclude=`:: +`--include=`:: +`--reject`:: These flags are passed to the linkgit:git-apply[1] program that applies the patch. + -Valid for the `--whitespace` option are: +Valid __ for the `--whitespace` option are: `nowarn`, `warn`, `fix`, `error`, and `error-all`. ---patch-format:: +`--patch-format`:: By default the command will try to detect the patch format automatically. This option allows the user to bypass the automatic detection and specify the patch format that the patch(es) should be interpreted as. Valid formats are mbox, mboxrd, stgit, stgit-series, and hg. --i:: ---interactive:: +`-i`:: +`--interactive`:: Run interactively. ---verify:: --n:: ---no-verify:: +`--verify`:: +`-n`:: +`--no-verify`:: Run the `pre-applypatch` and `applypatch-msg` hooks. This is the default. Skip these hooks with `-n` or `--no-verify`. See also linkgit:githooks[5]. + Note that `post-applypatch` cannot be skipped. ---committer-date-is-author-date:: +`--committer-date-is-author-date`:: By default the command records the date from the e-mail message as the commit author date, and uses the time of commit creation as the committer date. This allows the @@ -172,29 +172,29 @@ committer date when applying commits on top of a base which commit is older (in terms of the commit date) than the oldest patch you are applying. ---ignore-date:: +`--ignore-date`:: By default the command records the date from the e-mail message as the commit author date, and uses the time of commit creation as the committer date. This allows the user to lie about the author date by using the same value as the committer date. ---skip:: +`--skip`:: Skip the current patch. This is only meaningful when restarting an aborted patch. --S[]:: ---gpg-sign[=]:: ---no-gpg-sign:: - GPG-sign commits. The `keyid` argument is optional and +`-S[]`:: +`--gpg-sign[=]`:: +`--no-gpg-sign`:: + GPG-sign commits. The __ is optional and defaults to the committer identity; if specified, it must be stuck to the option without a space. `--no-gpg-sign` is useful to countermand both `commit.gpgSign` configuration variable, and earlier `--gpg-sign`. ---continue:: --r:: ---resolved:: +`--continue`:: +`-r`:: +`--resolved`:: After a patch failure (e.g. attempting to apply conflicting patch), the user has applied it by hand and the index file stores the result of the application. @@ -202,36 +202,36 @@ applying. extracted from the e-mail message and the current index file, and continue. ---resolvemsg=:: - When a patch failure occurs, will be printed +`--resolvemsg=`:: + When a patch failure occurs, __ will be printed to the screen before exiting. This overrides the standard message informing you to use `--continue` or `--skip` to handle the failure. This is solely for internal use between linkgit:git-rebase[1] and linkgit:git-am[1]. ---abort:: +`--abort`:: Restore the original branch and abort the patching operation. Revert the contents of files involved in the am operation to their pre-am state. ---quit:: - Abort the patching operation but keep HEAD and the index +`--quit`:: + Abort the patching operation but keep `HEAD` and the index untouched. ---retry:: +`--retry`:: Try to apply the last conflicting patch again. This is generally only useful for passing extra options to the retry attempt (e.g., `--3way`), since otherwise you'll just see the same failure again. ---show-current-patch[=(diff|raw)]:: +`--show-current-patch[=(diff|raw)]`:: Show the message at which linkgit:git-am[1] has stopped due to conflicts. If `raw` is specified, show the raw contents of the e-mail message; if `diff`, show the diff portion only. Defaults to `raw`. ---allow-empty:: +`--allow-empty`:: After a patch failure on an input e-mail message lacking a patch, create an empty commit with the contents of the e-mail message as its log message. @@ -278,11 +278,11 @@ operation is finished, so if you decide to start over from scratch, run `git am --abort` before running the command with mailbox names. -Before any patches are applied, ORIG_HEAD is set to the tip of the +Before any patches are applied, `ORIG_HEAD` is set to the tip of the current branch. This is useful if you have problems with multiple commits, like running linkgit:git-am[1] on the wrong branch or an error in the commits that is more easily fixed by changing the mailbox (e.g. -errors in the "From:" lines). +errors in the `From:` lines). [[caveats]] CAVEATS From ba1c516edabccd3e195746a8be842a70cdea6502 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= Date: Mon, 25 May 2026 10:28:26 +0000 Subject: [PATCH 06/16] doc: convert git-apply synopsis and options to new style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert git-apply from [verse]/single-quote style to the modern synopsis-block style: - Replace [verse] with [synopsis] in SYNOPSIS block - Backtick-quote all OPTIONS terms and config keys in config/apply.adoc - Convert single-quoted inline commands ('git apply', 'diff', etc.) - Wrap standalone placeholders in underscores (, , ) - Backtick-quote `*.rej` and GNU `patch` tool references Signed-off-by: Jean-Noël Avila Signed-off-by: Junio C Hamano --- Documentation/config/apply.adoc | 17 +++-- Documentation/git-apply.adoc | 125 ++++++++++++++++---------------- 2 files changed, 74 insertions(+), 68 deletions(-) diff --git a/Documentation/config/apply.adoc b/Documentation/config/apply.adoc index f9908e210a838d..36fcea62914ae6 100644 --- a/Documentation/config/apply.adoc +++ b/Documentation/config/apply.adoc @@ -1,11 +1,16 @@ -apply.ignoreWhitespace:: - When set to 'change', tells 'git apply' to ignore changes in +`apply.ignoreWhitespace`:: + When set to `change`, tells `git apply` to ignore changes in whitespace, in the same way as the `--ignore-space-change` option. - When set to one of: no, none, never, false, it tells 'git apply' to + When set to one of: `no`, `none`, `never`, `false`, it tells `git apply` to respect all whitespace differences. +ifndef::git-apply[] See linkgit:git-apply[1]. +endif::git-apply[] -apply.whitespace:: - Tells 'git apply' how to handle whitespace, in the same way - as the `--whitespace` option. See linkgit:git-apply[1]. +`apply.whitespace`:: + Tells `git apply` how to handle whitespace, in the same way + as the `--whitespace` option. +ifndef::git-apply[] + See linkgit:git-apply[1]. +endif::git-apply[] diff --git a/Documentation/git-apply.adoc b/Documentation/git-apply.adoc index 6c71ee69da977d..3f22dac1ce8edd 100644 --- a/Documentation/git-apply.adoc +++ b/Documentation/git-apply.adoc @@ -8,8 +8,8 @@ git-apply - Apply a patch to files and/or to the index SYNOPSIS -------- -[verse] -'git apply' [--stat] [--numstat] [--summary] [--check] +[synopsis] +git apply [--stat] [--numstat] [--summary] [--check] [--index | --intent-to-add] [--3way] [--ours | --theirs | --union] [--apply] [--no-add] [--build-fake-ancestor=] [-R | --reverse] [--allow-binary-replacement | --binary] [--reject] [-z] @@ -35,33 +35,33 @@ linkgit:git-format-patch[1] and/or received by email. OPTIONS ------- -...:: - The files to read the patch from. '-' can be used to read +`...`:: + The files to read the patch from. `-` can be used to read from the standard input. ---stat:: +`--stat`:: Instead of applying the patch, output diffstat for the input. Turns off "apply". ---numstat:: +`--numstat`:: Similar to `--stat`, but shows the number of added and deleted lines in decimal notation and the pathname without abbreviation, to make it more machine friendly. For binary files, outputs two `-` instead of saying `0 0`. Turns off "apply". ---summary:: +`--summary`:: Instead of applying the patch, output a condensed summary of information obtained from git diff extended headers, such as creations, renames, and mode changes. Turns off "apply". ---check:: +`--check`:: Instead of applying the patch, see if the patch is applicable to the current working tree and/or the index file and detects errors. Turns off "apply". ---index:: +`--index`:: Apply the patch to both the index and the working tree (or merely check that it would apply cleanly to both if `--check` is in effect). Note that `--index` expects index entries and @@ -70,13 +70,13 @@ OPTIONS raise an error if they are not, even if the patch would apply cleanly to both the index and the working tree in isolation. ---cached:: +`--cached`:: Apply the patch to just the index, without touching the working tree. If `--check` is in effect, merely check that it would apply cleanly to the index entry. --N:: ---intent-to-add:: +`-N`:: +`--intent-to-add`:: When applying the patch only to the working tree, mark new files to be added to the index later (see `--intent-to-add` option in linkgit:git-add[1]). This option is ignored if @@ -84,8 +84,8 @@ OPTIONS repository. Note that `--index` could be implied by other options such as `--3way`. --3:: ---3way:: +`-3`:: +`--3way`:: Attempt 3-way merge if the patch records the identity of blobs it is supposed to apply to and we have those blobs available locally, possibly leaving the conflict markers in the files in the working tree for the user to @@ -94,14 +94,14 @@ OPTIONS When used with the `--cached` option, any conflicts are left at higher stages in the cache. ---ours:: ---theirs:: ---union:: +`--ours`:: +`--theirs`:: +`--union`:: Instead of leaving conflicts in the file, resolve conflicts favouring - our (or their or both) side of the lines. Requires --3way. + our (or their or both) side of the lines. Requires `--3way`. ---build-fake-ancestor=:: - Newer 'git diff' output has embedded 'index information' +`--build-fake-ancestor=`:: + Newer `git diff` output has embedded 'index information' for each blob to help identify the original version that the patch applies to. When this flag is given, and if the original versions of the blobs are available locally, @@ -110,18 +110,18 @@ OPTIONS When a pure mode change is encountered (which has no index information), the information is read from the current index instead. --R:: ---reverse:: +`-R`:: +`--reverse`:: Apply the patch in reverse. ---reject:: - For atomicity, 'git apply' by default fails the whole patch and +`--reject`:: + For atomicity, `git apply` by default fails the whole patch and does not touch the working tree when some of the hunks do not apply. This option makes it apply the parts of the patch that are applicable, and leave the - rejected hunks in corresponding *.rej files. + rejected hunks in corresponding `*.rej` files. --z:: +`-z`:: When `--numstat` has been given, do not munge pathnames, but use a NUL-terminated machine-readable format. + @@ -129,20 +129,20 @@ Without this option, pathnames with "unusual" characters are quoted as explained for the configuration variable `core.quotePath` (see linkgit:git-config[1]). --p:: - Remove leading path components (separated by slashes) from +`-p`:: + Remove __ leading path components (separated by slashes) from traditional diff paths. E.g., with `-p2`, a patch against `a/dir/file` will be applied directly to `file`. The default is 1. --C:: - Ensure at least lines of surrounding context match before +`-C`:: + Ensure at least __ lines of surrounding context match before and after each change. When fewer lines of surrounding context exist they all must match. By default no context is ever ignored. ---unidiff-zero:: - By default, 'git apply' expects that the patch being +`--unidiff-zero`:: + By default, `git apply` expects that the patch being applied is a unified diff with at least one line of context. This provides good safety measures, but breaks down when applying a diff generated with `--unified=0`. To bypass these @@ -151,34 +151,34 @@ linkgit:git-config[1]). Note, for the reasons stated above, the usage of context-free patches is discouraged. ---apply:: +`--apply`:: If you use any of the options marked "Turns off - 'apply'" above, 'git apply' reads and outputs the + 'apply'" above, `git apply` reads and outputs the requested information without actually applying the patch. Give this flag after those flags to also apply the patch. ---no-add:: +`--no-add`:: When applying a patch, ignore additions made by the patch. This can be used to extract the common part between - two files by first running 'diff' on them and applying + two files by first running `diff` on them and applying the result with this option, which would apply the deletion part but not the addition part. ---allow-binary-replacement:: ---binary:: +`--allow-binary-replacement`:: +`--binary`:: Historically we did not allow binary patch application without an explicit permission from the user, and this flag was the way to do so. Currently, we always allow binary patch application, so this is a no-op. ---exclude=:: - Don't apply changes to files matching the given path pattern. This can +`--exclude=`:: + Don't apply changes to files matching __. This can be useful when importing patchsets, where you want to exclude certain files or directories. ---include=:: - Apply changes to files matching the given path pattern. This can +`--include=`:: + Apply changes to files matching the __. This can be useful when importing patchsets, where you want to include certain files or directories. + @@ -188,15 +188,15 @@ patch to each path is used. A patch to a path that does not match any include/exclude pattern is used by default if there is no include pattern on the command line, and ignored if there is any include pattern. ---ignore-space-change:: ---ignore-whitespace:: +`--ignore-space-change`:: +`--ignore-whitespace`:: When applying a patch, ignore changes in whitespace in context lines if necessary. Context lines will preserve their whitespace, and they will not undergo whitespace fixing regardless of the value of the `--whitespace` option. New lines will still be fixed, though. ---whitespace=:: +`--whitespace=`:: When applying a patch, detect a new or modified line that has whitespace errors. What are considered whitespace errors is controlled by `core.whitespace` configuration. By default, @@ -209,7 +209,7 @@ By default, the command outputs warning messages but applies the patch. When `git-apply` is used for statistics and not applying a patch, it defaults to `nowarn`. + -You can use different `` values to control this +You can use different __ values to control this behavior: + * `nowarn` turns off the trailing whitespace warning. @@ -223,48 +223,48 @@ behavior: to apply the patch. * `error-all` is similar to `error` but shows all errors. ---inaccurate-eof:: - Under certain circumstances, some versions of 'diff' do not correctly +`--inaccurate-eof`:: + Under certain circumstances, some versions of `diff` do not correctly detect a missing new-line at the end of the file. As a result, patches - created by such 'diff' programs do not record incomplete lines + created by such `diff` programs do not record incomplete lines correctly. This option adds support for applying such patches by working around this bug. --v:: ---verbose:: +`-v`:: +`--verbose`:: Report progress to stderr. By default, only a message about the current patch being applied will be printed. This option will cause additional information to be reported. --q:: ---quiet:: +`-q`:: +`--quiet`:: Suppress stderr output. Messages about patch status and progress will not be printed. ---recount:: +`--recount`:: Do not trust the line counts in the hunk headers, but infer them by inspecting the patch (e.g. after editing the patch without adjusting the hunk headers appropriately). ---directory=:: - Prepend to all filenames. If a "-p" argument was also passed, +`--directory=`:: + Prepend __ to all filenames. If a `-p` argument was also passed, it is applied before prepending the new root. + For example, a patch that talks about updating `a/git-gui.sh` to `b/git-gui.sh` can be applied to the file in the working tree `modules/git-gui/git-gui.sh` by running `git apply --directory=modules/git-gui`. ---unsafe-paths:: +`--unsafe-paths`:: By default, a patch that affects outside the working area (either a Git controlled working tree, or the current working - directory when "git apply" is used as a replacement of GNU - patch) is rejected as a mistake (or a mischief). + directory when `git apply` is used as a replacement of GNU + `patch`) is rejected as a mistake (or a mischief). + -When `git apply` is used as a "better GNU patch", the user can pass +When `git apply` is used as a "better GNU `patch`", the user can pass the `--unsafe-paths` option to override this safety check. This option has no effect when `--index` or `--cached` is in use. ---allow-empty:: +`--allow-empty`:: Don't return an error for patches containing no diff. This includes empty patches and patches with commit text only. @@ -273,11 +273,12 @@ CONFIGURATION include::includes/cmd-config-section-all.adoc[] +:git-apply: 1 include::config/apply.adoc[] SUBMODULES ---------- -If the patch contains any changes to submodules then 'git apply' +If the patch contains any changes to submodules then `git apply` treats these changes as follows. If `--index` is specified (explicitly or implicitly), then the submodule From 2ef248ae45bdcfbb027108bf87fcc3375cb5daba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= Date: Mon, 25 May 2026 10:28:27 +0000 Subject: [PATCH 07/16] doc: convert git-imap-send synopsis and options to new style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert git-imap-send from [verse]/single-quote style to the modern synopsis-block style: - Replace [verse] with [synopsis] in SYNOPSIS block - Backtick-quote all OPTIONS terms - Backtick-quote all config keys in config/imap.adoc - Backtick-quote bare config key references in prose Signed-off-by: Jean-Noël Avila Signed-off-by: Junio C Hamano --- Documentation/config/imap.adoc | 30 +++++++++++++++--------------- Documentation/git-imap-send.adoc | 24 ++++++++++++------------ 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc index 4682a6bd039755..cb8f5e2700ae13 100644 --- a/Documentation/config/imap.adoc +++ b/Documentation/config/imap.adoc @@ -1,44 +1,44 @@ -imap.folder:: +`imap.folder`:: The folder to drop the mails into, which is typically the Drafts folder. For example: `INBOX.Drafts`, `INBOX/Drafts` or `[Gmail]/Drafts`. The IMAP folder to interact with MUST be specified; the value of this configuration variable is used as the fallback default value when the `--folder` option is not given. -imap.tunnel:: +`imap.tunnel`:: Command used to set up a tunnel to the IMAP server through which commands will be piped instead of using a direct network connection - to the server. Required when imap.host is not set. + to the server. Required when `imap.host` is not set. -imap.host:: +`imap.host`:: A URL identifying the server. Use an `imap://` prefix for non-secure connections and an `imaps://` prefix for secure connections. - Ignored when imap.tunnel is set, but required otherwise. + Ignored when `imap.tunnel` is set, but required otherwise. -imap.user:: +`imap.user`:: The username to use when logging in to the server. -imap.pass:: +`imap.pass`:: The password to use when logging in to the server. -imap.port:: +`imap.port`:: An integer port number to connect to on the server. - Defaults to 143 for imap:// hosts and 993 for imaps:// hosts. - Ignored when imap.tunnel is set. + Defaults to 143 for `imap://` hosts and 993 for `imaps://` hosts. + Ignored when `imap.tunnel` is set. -imap.sslverify:: +`imap.sslverify`:: A boolean to enable/disable verification of the server certificate used by the SSL/TLS connection. Default is `true`. Ignored when - imap.tunnel is set. + `imap.tunnel` is set. -imap.preformattedHTML:: +`imap.preformattedHTML`:: A boolean to enable/disable the use of html encoding when sending - a patch. An html encoded patch will be bracketed with
+	a patch.  An html encoded patch will be bracketed with `
`
 	and have a content type of text/html.  Ironically, enabling this
 	option causes Thunderbird to send the patch as a plain/text,
 	format=fixed email.  Default is `false`.
 
-imap.authMethod::
+`imap.authMethod`::
 	Specify the authentication method for authenticating with the IMAP server.
 	If Git was built with the NO_CURL option, or if your curl version is older
 	than 7.34.0, or if you're running git-imap-send with the `--no-curl`
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 278e5ccd36b9fa..538b91afc06dde 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -8,9 +8,9 @@ git-imap-send - Send a collection of patches from stdin to an IMAP folder
 
 SYNOPSIS
 --------
-[verse]
-'git imap-send' [-v] [-q] [--[no-]curl] [(--folder|-f) ]
-'git imap-send' --list
+[synopsis]
+git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) ]
+git imap-send --list
 
 
 DESCRIPTION
@@ -32,30 +32,30 @@ $ git format-patch --signoff --stdout --attach origin | git imap-send
 OPTIONS
 -------
 
--v::
---verbose::
+`-v`::
+`--verbose`::
 	Be verbose.
 
--q::
---quiet::
+`-q`::
+`--quiet`::
 	Be quiet.
 
--f ::
---folder=::
+`-f `::
+`--folder=`::
 	Specify the folder in which the emails have to saved.
 	For example: `--folder=[Gmail]/Drafts` or `-f INBOX/Drafts`.
 
---curl::
+`--curl`::
 	Use libcurl to communicate with the IMAP server, unless tunneling
 	into it.  Ignored if Git was built without the USE_CURL_FOR_IMAP_SEND
 	option set.
 
---no-curl::
+`--no-curl`::
 	Talk to the IMAP server using git's own IMAP routines instead of
 	using libcurl.  Ignored if Git was built with the NO_OPENSSL option
 	set.
 
---list::
+`--list`::
 	Run the IMAP LIST command to output a list of all the folders present.
 
 CONFIGURATION

From 85a30b5b26c2953aa836c554af5fc58eed707e4a Mon Sep 17 00:00:00 2001
From: Kristofer Karlsson 
Date: Mon, 25 May 2026 14:28:03 +0000
Subject: [PATCH 08/16] object.h: fix stale entries in object flag allocation
 table

Update three stale entries found during an audit of the flag
allocation table:

 - sha1-name.c was renamed to object-name.c
 - builtin/show-branch.c uses bits 0 and 2-28, not 0-26
   (REV_SHIFT=2, MAX_REVS=FLAG_BITS-REV_SHIFT=27)
 - negotiator/skipping.c uses bits 2-5 like negotiator/default.c
   (ADVERTISED on bit 3 instead of COMMON_REF)

Signed-off-by: Kristofer Karlsson 
Signed-off-by: Junio C Hamano 
---
 object.h | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/object.h b/object.h
index d814647ebe6c18..2b26de3044846b 100644
--- a/object.h
+++ b/object.h
@@ -67,6 +67,7 @@ void object_array_init(struct object_array *array);
  * revision.h:               0---------10         15               23--------28
  * fetch-pack.c:             01    67
  * negotiator/default.c:       2--5
+ * negotiator/skipping.c:      2--5
  * walker.c:                 0-2
  * upload-pack.c:                4       11-----14  16-----19
  * builtin/blame.c:                        12-13
@@ -76,13 +77,13 @@ void object_array_init(struct object_array *array);
  * commit-graph.c:                                15
  * commit-reach.c:                                  16-----19
  * builtin/last-modified.c:                         1617
- * sha1-name.c:                                              20
+ * object-name.c:                                            20
  * list-objects-filter.c:                                      21
  * bloom.c:                                                    2122
  * builtin/fsck.c:           0--3
  * builtin/index-pack.c:                                     2021
  * reflog.c:                           10--12
- * builtin/show-branch.c:    0-------------------------------------------26
+ * builtin/show-branch.c:    0-----------------------------------------------28
  * builtin/unpack-objects.c:                                 2021
  * pack-bitmap.h:                                              2122
  */

From f767dae3e6c8359128d0ec83acd009751e92e419 Mon Sep 17 00:00:00 2001
From: Kristofer Karlsson 
Date: Mon, 25 May 2026 14:28:04 +0000
Subject: [PATCH 09/16] commit-reach: deduplicate queue entries in
 paint_down_to_common

paint_down_to_common() can enqueue the same commit multiple times
when it is reached through different parents with different flag
combinations. Add an ENQUEUED flag to track whether a commit is
currently in the priority queue, and skip it if already present.

Introduce prio_queue_put_dedup() and prio_queue_get_dedup()
wrappers that manage the ENQUEUED flag on enqueue and dequeue.

This change is performance-neutral on its own: the O(n)
queue_has_nonstale() scan still dominates the per-iteration cost.
However, the deduplication guarantee (each commit appears in the
queue at most once) is a prerequisite for the next commit, which
replaces that scan with O(1) tracking.

Signed-off-by: Kristofer Karlsson 
Signed-off-by: Junio C Hamano 
---
 commit-reach.c | 27 ++++++++++++++++++++++-----
 object.h       |  2 +-
 2 files changed, 23 insertions(+), 6 deletions(-)

diff --git a/commit-reach.c b/commit-reach.c
index d3a9b3ed6fe561..31e6110b138d8f 100644
--- a/commit-reach.c
+++ b/commit-reach.c
@@ -17,8 +17,9 @@
 #define PARENT2		(1u<<17)
 #define STALE		(1u<<18)
 #define RESULT		(1u<<19)
+#define ENQUEUED	(1u<<20)
 
-static const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT);
+static const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT | ENQUEUED);
 
 static int compare_commits_by_gen(const void *_a, const void *_b)
 {
@@ -39,6 +40,22 @@ static int compare_commits_by_gen(const void *_a, const void *_b)
 	return 0;
 }
 
+static void prio_queue_put_dedup(struct prio_queue *queue, struct commit *c)
+{
+	if (c->object.flags & ENQUEUED)
+		return;
+	c->object.flags |= ENQUEUED;
+	prio_queue_put(queue, c);
+}
+
+static struct commit *prio_queue_get_dedup(struct prio_queue *queue)
+{
+	struct commit *commit = prio_queue_get(queue);
+	if (commit)
+		commit->object.flags &= ~ENQUEUED;
+	return commit;
+}
+
 static int queue_has_nonstale(struct prio_queue *queue)
 {
 	for (size_t i = 0; i < queue->nr; i++) {
@@ -70,15 +87,15 @@ static int paint_down_to_common(struct repository *r,
 		commit_list_append(one, result);
 		return 0;
 	}
-	prio_queue_put(&queue, one);
+	prio_queue_put_dedup(&queue, one);
 
 	for (i = 0; i < n; i++) {
 		twos[i]->object.flags |= PARENT2;
-		prio_queue_put(&queue, twos[i]);
+		prio_queue_put_dedup(&queue, twos[i]);
 	}
 
 	while (queue_has_nonstale(&queue)) {
-		struct commit *commit = prio_queue_get(&queue);
+		struct commit *commit = prio_queue_get_dedup(&queue);
 		struct commit_list *parents;
 		int flags;
 		timestamp_t generation = commit_graph_generation(commit);
@@ -124,7 +141,7 @@ static int paint_down_to_common(struct repository *r,
 					     oid_to_hex(&p->object.oid));
 			}
 			p->object.flags |= flags;
-			prio_queue_put(&queue, p);
+			prio_queue_put_dedup(&queue, p);
 		}
 	}
 
diff --git a/object.h b/object.h
index 2b26de3044846b..8fb03ff90a3e56 100644
--- a/object.h
+++ b/object.h
@@ -75,7 +75,7 @@ void object_array_init(struct object_array *array);
  * bundle.c:                                        16
  * http-push.c:                          11-----14
  * commit-graph.c:                                15
- * commit-reach.c:                                  16-----19
+ * commit-reach.c:                                  16-------20
  * builtin/last-modified.c:                         1617
  * object-name.c:                                            20
  * list-objects-filter.c:                                      21

From a186b7797a8bd4b9ca09b9cb326a2dccee00f90e Mon Sep 17 00:00:00 2001
From: Kristofer Karlsson 
Date: Mon, 25 May 2026 14:28:05 +0000
Subject: [PATCH 10/16] commit-reach: replace queue_has_nonstale() scan with
 O(1) tracking
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

paint_down_to_common() and ahead_behind() call queue_has_nonstale()
on every iteration to decide whether to continue the walk.
queue_has_nonstale() performs a linear scan of the priority queue,
making the overall walk O(n*m) where n is the number of commits
walked and m is the queue size.

Introduce 'struct nonstale_queue', a thin wrapper around prio_queue
that maintains a 'max_nonstale' pointer — the lowest-priority
(oldest) non-stale commit seen so far. When this commit is popped,
every remaining queue entry is known to be stale, so the walk can
stop. This reduces the per-iteration termination check from O(m)
to O(1).

Uses <= 0 (not < 0) when comparing priorities so that among distinct
commits with equal priority (same generation and timestamp) the
last-enqueued one is tracked. Since prio_queue breaks ties by
insertion order, this ensures max_nonstale is always the last in its
priority class to be popped, making pointer equality on pop
sufficient for correctness.

The previous commit's ENQUEUED deduplication guarantees each commit
appears at most once in the queue, which is required for the pointer
equality check to be unambiguous.

On a large monorepo (3.7M commits), this yields ~2x end-to-end
speedup for merge-base calculations on deep import branches.
Profiling shows paint_down_to_common() drops from 50% to 4% of
total runtime (~27x faster), with the remaining time in commit
graph lookups and heap operations:

  Before: 8536ms / 5757ms / 4743ms  (three test cases)
  After:  3956ms / 4383ms / 1927ms

Suggested-by: Jeff King 
Signed-off-by: Kristofer Karlsson 
Signed-off-by: Junio C Hamano 
---
 commit-reach.c | 96 ++++++++++++++++++++++++++++++++++----------------
 1 file changed, 65 insertions(+), 31 deletions(-)

diff --git a/commit-reach.c b/commit-reach.c
index 31e6110b138d8f..2e86e1e80316a9 100644
--- a/commit-reach.c
+++ b/commit-reach.c
@@ -40,32 +40,62 @@ static int compare_commits_by_gen(const void *_a, const void *_b)
 	return 0;
 }
 
-static void prio_queue_put_dedup(struct prio_queue *queue, struct commit *c)
+/*
+ * A prio_queue with O(1) termination check.  'max_nonstale' tracks
+ * the lowest-priority non-stale commit enqueued so far; once it is
+ * popped, every remaining entry is known to be STALE.
+ */
+struct nonstale_queue {
+	struct prio_queue pq;
+	struct commit *max_nonstale;
+};
+
+static void nonstale_queue_put(struct nonstale_queue *queue,
+			       struct commit *c)
+{
+	struct commit *old = queue->max_nonstale;
+
+	prio_queue_put(&queue->pq, c);
+	if (c->object.flags & STALE)
+		return;
+	if (!old || queue->pq.compare(old, c, queue->pq.cb_data) <= 0)
+		queue->max_nonstale = c;
+}
+
+static struct commit *nonstale_queue_get(struct nonstale_queue *queue)
+{
+	struct commit *commit = prio_queue_get(&queue->pq);
+
+	if (commit == queue->max_nonstale)
+		queue->max_nonstale = NULL;
+
+	return commit;
+}
+
+static void clear_nonstale_queue(struct nonstale_queue *queue)
+{
+	clear_prio_queue(&queue->pq);
+	queue->max_nonstale = NULL;
+}
+
+static void nonstale_queue_put_dedup(struct nonstale_queue *queue,
+				     struct commit *c)
 {
 	if (c->object.flags & ENQUEUED)
 		return;
 	c->object.flags |= ENQUEUED;
-	prio_queue_put(queue, c);
+	nonstale_queue_put(queue, c);
 }
 
-static struct commit *prio_queue_get_dedup(struct prio_queue *queue)
+static struct commit *nonstale_queue_get_dedup(struct nonstale_queue *queue)
 {
-	struct commit *commit = prio_queue_get(queue);
+	struct commit *commit = nonstale_queue_get(queue);
+
 	if (commit)
 		commit->object.flags &= ~ENQUEUED;
 	return commit;
 }
 
-static int queue_has_nonstale(struct prio_queue *queue)
-{
-	for (size_t i = 0; i < queue->nr; i++) {
-		struct commit *commit = queue->array[i].data;
-		if (!(commit->object.flags & STALE))
-			return 1;
-	}
-	return 0;
-}
-
 /* all input commits in one and twos[] must have been parsed! */
 static int paint_down_to_common(struct repository *r,
 				struct commit *one, int n,
@@ -74,28 +104,30 @@ static int paint_down_to_common(struct repository *r,
 				int ignore_missing_commits,
 				struct commit_list **result)
 {
-	struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
+	struct nonstale_queue queue = {
+		{ compare_commits_by_gen_then_commit_date }
+	};
 	int i;
 	timestamp_t last_gen = GENERATION_NUMBER_INFINITY;
 	struct commit_list **tail = result;
 
 	if (!min_generation && !corrected_commit_dates_enabled(r))
-		queue.compare = compare_commits_by_commit_date;
+		queue.pq.compare = compare_commits_by_commit_date;
 
 	one->object.flags |= PARENT1;
 	if (!n) {
 		commit_list_append(one, result);
 		return 0;
 	}
-	prio_queue_put_dedup(&queue, one);
+	nonstale_queue_put_dedup(&queue, one);
 
 	for (i = 0; i < n; i++) {
 		twos[i]->object.flags |= PARENT2;
-		prio_queue_put_dedup(&queue, twos[i]);
+		nonstale_queue_put_dedup(&queue, twos[i]);
 	}
 
-	while (queue_has_nonstale(&queue)) {
-		struct commit *commit = prio_queue_get_dedup(&queue);
+	while (queue.max_nonstale) {
+		struct commit *commit = nonstale_queue_get_dedup(&queue);
 		struct commit_list *parents;
 		int flags;
 		timestamp_t generation = commit_graph_generation(commit);
@@ -125,7 +157,7 @@ static int paint_down_to_common(struct repository *r,
 			if ((p->object.flags & flags) == flags)
 				continue;
 			if (repo_parse_commit(r, p)) {
-				clear_prio_queue(&queue);
+				clear_nonstale_queue(&queue);
 				commit_list_free(*result);
 				*result = NULL;
 				/*
@@ -141,11 +173,11 @@ static int paint_down_to_common(struct repository *r,
 					     oid_to_hex(&p->object.oid));
 			}
 			p->object.flags |= flags;
-			prio_queue_put_dedup(&queue, p);
+			nonstale_queue_put_dedup(&queue, p);
 		}
 	}
 
-	clear_prio_queue(&queue);
+	clear_nonstale_queue(&queue);
 	commit_list_sort_by_date(result);
 	return 0;
 }
@@ -1039,11 +1071,11 @@ struct commit_list *get_reachable_subset(struct commit **from, size_t nr_from,
 define_commit_slab(bit_arrays, struct bitmap *);
 static struct bit_arrays bit_arrays;
 
-static void insert_no_dup(struct prio_queue *queue, struct commit *c)
+static void insert_no_dup(struct nonstale_queue *queue, struct commit *c)
 {
 	if (c->object.flags & PARENT2)
 		return;
-	prio_queue_put(queue, c);
+	nonstale_queue_put(queue, c);
 	c->object.flags |= PARENT2;
 }
 
@@ -1068,7 +1100,9 @@ void ahead_behind(struct repository *r,
 		  struct commit **commits, size_t commits_nr,
 		  struct ahead_behind_count *counts, size_t counts_nr)
 {
-	struct prio_queue queue = { .compare = compare_commits_by_gen_then_commit_date };
+	struct nonstale_queue queue = {
+		{ .compare = compare_commits_by_gen_then_commit_date }
+	};
 	size_t width = DIV_ROUND_UP(commits_nr, BITS_IN_EWORD);
 
 	if (!commits_nr || !counts_nr)
@@ -1091,8 +1125,8 @@ void ahead_behind(struct repository *r,
 		insert_no_dup(&queue, c);
 	}
 
-	while (queue_has_nonstale(&queue)) {
-		struct commit *c = prio_queue_get(&queue);
+	while (queue.max_nonstale) {
+		struct commit *c = nonstale_queue_get(&queue);
 		struct commit_list *p;
 		struct bitmap *bitmap_c = get_bit_array(c, width);
 
@@ -1134,10 +1168,10 @@ void ahead_behind(struct repository *r,
 
 	/* STALE is used here, PARENT2 is used by insert_no_dup(). */
 	repo_clear_commit_marks(r, PARENT2 | STALE);
-	for (size_t i = 0; i < queue.nr; i++)
-		free_bit_array(queue.array[i].data);
+	for (size_t i = 0; i < queue.pq.nr; i++)
+		free_bit_array(queue.pq.array[i].data);
 	clear_bit_arrays(&bit_arrays);
-	clear_prio_queue(&queue);
+	clear_nonstale_queue(&queue);
 }
 
 struct commit_and_index {

From 44d04e442683b53ed618b74861f056f93fcbd783 Mon Sep 17 00:00:00 2001
From: Alyssa Ross 
Date: Mon, 25 May 2026 18:23:12 +0200
Subject: [PATCH 11/16] receive-pack: fix updateInstead with core.worktree

Before a8cc594333 (hooks: fix an obscure TOCTOU "did we just run a
hook?" race, 2022-03-07), when receive.denyCurrentBranch is set to
updateInstead, only one of push_to_checkout() or push_to_deploy()
was called.  That commit changed to always call push_to_checkout(),
and then to call push_to_deploy() if push_to_checkout() didn't run
anything.

This change didn't take into account that push_to_checkout() had a
side effect of modifying env, and that modified env broke updating
the worktree in push_to_deploy() if core.worktree was configured.
To fix this, only mutate the environment used inside
push_to_commit(), rather than the environment that might later be
passed to push_to_deploy().

Signed-off-by: Alyssa Ross 
Signed-off-by: Junio C Hamano 
---
 builtin/receive-pack.c |  2 +-
 t/t5516-fetch-push.sh  | 11 +++++++++++
 2 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 9c491746168a6f..9fbfa15a516077 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -1427,8 +1427,8 @@ static const char *push_to_checkout(unsigned char *hash,
 	struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
 	opt.invoked_hook = invoked_hook;
 
-	strvec_pushf(env, "GIT_WORK_TREE=%s", absolute_path(work_tree));
 	strvec_pushv(&opt.env, env->v);
+	strvec_pushf(&opt.env, "GIT_WORK_TREE=%s", absolute_path(work_tree));
 	strvec_push(&opt.args, hash_to_hex(hash));
 	if (run_hooks_opt(the_repository, push_to_checkout_hook, &opt))
 		return "push-to-checkout hook declined";
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 46926e7bbd3a9a..a85b1fbf8b5d47 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -1791,6 +1791,17 @@ test_expect_success 'updateInstead with push-to-checkout hook' '
 	)
 '
 
+test_expect_success 'denyCurrentBranch and core.worktree' '
+	test_when_finished "rm -fr cloned cloned.git" &&
+	git clone --separate-git-dir cloned.git . cloned &&
+	git --git-dir cloned.git config receive.denyCurrentBranch updateInstead &&
+	git --git-dir cloned.git config core.worktree "$PWD/cloned" &&
+	test_commit raspberry &&
+	git push cloned.git HEAD:main &&
+	test_path_exists cloned/raspberry.t &&
+	test_must_fail git push --delete cloned.git main
+'
+
 test_expect_success 'denyCurrentBranch and worktrees' '
 	git worktree add new-wt &&
 	git clone . cloned &&

From ca7b9ae3403b1a46fffbdae312092c4029470eee Mon Sep 17 00:00:00 2001
From: Derrick Stolee 
Date: Tue, 26 May 2026 20:26:33 +0000
Subject: [PATCH 12/16] t1092: test 'git restore' with sparse index

A user reported that 'git restore --staged .' causes the sparse index to
expand. This is somewhat natural because the '.' pathspec means 'check
every path'. However, the restore will not update paths marked with the
SKIP_WORKTREE bit, so we shouldn't need to process such entries.

For now, establish the current behavior, including the sparse index
expansion, in the t1092 test case as a baseline.

Signed-off-by: Derrick Stolee 
Signed-off-by: Junio C Hamano 
---
 t/t1092-sparse-checkout-compatibility.sh | 50 ++++++++++++++++++++++++
 1 file changed, 50 insertions(+)

diff --git a/t/t1092-sparse-checkout-compatibility.sh b/t/t1092-sparse-checkout-compatibility.sh
index d98cb4ac113c67..d69434e7ab6ce0 100755
--- a/t/t1092-sparse-checkout-compatibility.sh
+++ b/t/t1092-sparse-checkout-compatibility.sh
@@ -2573,4 +2573,54 @@ test_expect_success 'sparse-index is not expanded: merge-ours' '
 	ensure_not_expanded merge -s ours merge-right
 '
 
+test_expect_success 'restore --staged with sparse definition' '
+	init_repos &&
+
+	# Stage changes within the sparse definition
+	test_all_match git checkout -b restore-staged-1 base &&
+	test_all_match git reset --soft update-deep &&
+	test_all_match git restore --staged . &&
+	test_all_match git status --porcelain=v2 &&
+	test_all_match git diff --cached
+'
+
+test_expect_success 'restore --staged with outside sparse definition' '
+	init_repos &&
+
+	# Stage changes that include paths outside the sparse definition.
+	# Although the working tree differs between full and sparse checkouts
+	# after restore, the state of the index should be the same.
+	test_all_match git checkout -b restore-staged-2 base &&
+	test_all_match git reset --soft update-folder1 &&
+	test_sparse_match git restore --staged . &&
+	git -C full-checkout restore --staged . &&
+	test_all_match git ls-files -s -- folder1 &&
+	test_all_match git diff --cached -- folder1
+'
+
+test_expect_success 'restore --staged with wildcards' '
+	init_repos &&
+
+	test_all_match git checkout -b restore-staged-3 base &&
+	test_all_match git reset --soft update-deep &&
+	test_all_match git restore --staged "deep/*" &&
+	test_all_match git status --porcelain=v2 &&
+	test_all_match git diff --cached
+'
+
+test_expect_success 'sparse-index is expanded: restore --staged' '
+	init_repos &&
+
+	git -C sparse-index checkout -b restore-staged-exp base &&
+	git -C sparse-index reset --soft update-folder1 &&
+	ensure_expanded restore --staged .
+'
+
+test_expect_success 'sparse-index is expanded: restore --source --staged' '
+	init_repos &&
+
+	git -C sparse-index checkout -b restore-source-staged base &&
+	ensure_expanded restore --source update-folder1 --staged .
+'
+
 test_done

From 105aacd072e41c55948d016b46f86f75db2487b3 Mon Sep 17 00:00:00 2001
From: Derrick Stolee 
Date: Tue, 26 May 2026 20:26:34 +0000
Subject: [PATCH 13/16] restore: avoid sparse index expansion

Teach update_some() to handle sparse directory entries at the tree
level rather than expanding the entire sparse index. When iterating a
source tree during checkout/restore operations:

 - If a directory matches a sparse directory entry with the same OID,
   skip it entirely (no change needed).

 - If the OID differs and we are in non-overlay mode (e.g., restore
   --staged), update the sparse directory entry's OID in place. This
   is semantically correct because non-overlay mode removes paths not
   in the source tree anyway.

 - In overlay mode (e.g., checkout  -- .), fall through to
   recursive descent so individual file entries are preserved
   correctly.

Also switch from index_name_pos() to index_name_pos_sparse() for
individual file lookups to avoid triggering ensure_full_index() when
the file is already individually tracked in the index.

Update the test expectation in t1092 to assert that 'restore --staged'
no longer expands the sparse index.

Signed-off-by: Derrick Stolee 
Signed-off-by: Junio C Hamano 
---
 builtin/checkout.c                       | 65 +++++++++++++++++++++---
 t/t1092-sparse-checkout-compatibility.sh |  8 +--
 2 files changed, 63 insertions(+), 10 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 1345e8574a79c8..86e23a07b11695 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -31,6 +31,7 @@
 #include "revision.h"
 #include "sequencer.h"
 #include "setup.h"
+#include "sparse-index.h"
 #include "strvec.h"
 #include "submodule.h"
 #include "symlinks.h"
@@ -141,15 +142,65 @@ static int post_checkout_hook(struct commit *old_commit, struct commit *new_comm
 	return run_hooks_opt(the_repository, "post-checkout", &opt);
 }
 
+/*
+ * Handle a tree object and determine if we need to recurse into the
+ * tree (READ_TREE_RECURSIVE) or skip it (0).
+ */
+static int try_update_sparse_directory(const struct object_id *oid,
+				       struct strbuf *base,
+				       const char *pathname,
+				       int overlay_mode)
+{
+	struct strbuf dirpath = STRBUF_INIT;
+	struct cache_entry *old;
+	int pos, result = READ_TREE_RECURSIVE;
+
+	if (!the_repository->index->sparse_index)
+		return result;
+
+	strbuf_addbuf(&dirpath, base);
+	strbuf_addstr(&dirpath, pathname);
+	strbuf_addch(&dirpath, '/');
+
+	pos = index_name_pos_sparse(the_repository->index,
+				    dirpath.buf, dirpath.len);
+	if (pos < 0)
+		goto cleanup;
+
+	old = the_repository->index->cache[pos];
+	if (!S_ISSPARSEDIR(old->ce_mode))
+		goto cleanup;
+
+	if (oideq(oid, &old->oid)) {
+		/* Tree content already matches; no need to descend. */
+		result = 0;
+	} else if (!overlay_mode) {
+		/*
+		 * In non-overlay mode (e.g., restore --staged), replace the
+		 * sparse directory OID directly since files not present in
+		 * the source tree should be removed anyway.
+		 */
+		oidcpy(&old->oid, oid);
+		old->ce_flags |= CE_UPDATE;
+		result = 0;
+	}
+
+cleanup:
+	strbuf_release(&dirpath);
+	return result;
+}
+
 static int update_some(const struct object_id *oid, struct strbuf *base,
-		       const char *pathname, unsigned mode, void *context UNUSED)
+		       const char *pathname, unsigned mode, void *context)
 {
 	int len;
 	struct cache_entry *ce;
 	int pos;
+	int overlay_mode = context ? *((int *)context) : 1;
 
 	if (S_ISDIR(mode))
-		return READ_TREE_RECURSIVE;
+		return try_update_sparse_directory(oid, base, pathname,
+						   overlay_mode);
 
 	len = base->len + strlen(pathname);
 	ce = make_empty_cache_entry(the_repository->index, len);
@@ -165,7 +216,7 @@ static int update_some(const struct object_id *oid, struct strbuf *base,
 	 * entry in place. Whether it is UPTODATE or not, checkout_entry will
 	 * do the right thing.
 	 */
-	pos = index_name_pos(the_repository->index, ce->name, ce->ce_namelen);
+	pos = index_name_pos_sparse(the_repository->index, ce->name, ce->ce_namelen);
 	if (pos >= 0) {
 		struct cache_entry *old = the_repository->index->cache[pos];
 		if (ce->ce_mode == old->ce_mode &&
@@ -182,10 +233,11 @@ static int update_some(const struct object_id *oid, struct strbuf *base,
 	return 0;
 }
 
-static int read_tree_some(struct tree *tree, const struct pathspec *pathspec)
+static int read_tree_some(struct tree *tree, const struct pathspec *pathspec,
+			  int overlay_mode)
 {
 	read_tree(the_repository, tree,
-		  pathspec, update_some, NULL);
+		  pathspec, update_some, &overlay_mode);
 
 	/* update the index with the given tree's info
 	 * for all args, expanding wildcards, and exit
@@ -580,7 +632,8 @@ static int checkout_paths(const struct checkout_opts *opts,
 		return error(_("index file corrupt"));
 
 	if (opts->source_tree)
-		read_tree_some(opts->source_tree, &opts->pathspec);
+		read_tree_some(opts->source_tree, &opts->pathspec,
+			       opts->overlay_mode);
 	if (opts->merge)
 		unmerge_index(the_repository->index, &opts->pathspec, CE_MATCHED);
 
diff --git a/t/t1092-sparse-checkout-compatibility.sh b/t/t1092-sparse-checkout-compatibility.sh
index d69434e7ab6ce0..8186da5c887c56 100755
--- a/t/t1092-sparse-checkout-compatibility.sh
+++ b/t/t1092-sparse-checkout-compatibility.sh
@@ -2608,19 +2608,19 @@ test_expect_success 'restore --staged with wildcards' '
 	test_all_match git diff --cached
 '
 
-test_expect_success 'sparse-index is expanded: restore --staged' '
+test_expect_success 'sparse-index is not expanded: restore --staged' '
 	init_repos &&
 
 	git -C sparse-index checkout -b restore-staged-exp base &&
 	git -C sparse-index reset --soft update-folder1 &&
-	ensure_expanded restore --staged .
+	ensure_not_expanded restore --staged .
 '
 
-test_expect_success 'sparse-index is expanded: restore --source --staged' '
+test_expect_success 'sparse-index is not expanded: restore --source --staged' '
 	init_repos &&
 
 	git -C sparse-index checkout -b restore-source-staged base &&
-	ensure_expanded restore --source update-folder1 --staged .
+	ensure_not_expanded restore --source update-folder1 --staged .
 '
 
 test_done

From 7dd898a92ddc2318c3516c06fb6769c3e64216ed Mon Sep 17 00:00:00 2001
From: Kristoffer Haugsbakk 
Date: Thu, 28 May 2026 09:00:10 +0200
Subject: [PATCH 14/16] *: replace deprecated free_commit_list
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Replace `free_commit_list` with `commit_list_free`. The former was
deprecated in 9f18d089 (commit: rename `free_commit_list()` to conform
to coding guidelines, 2026-01-15).

This allows us to remove all the deprecated functions in the
next commit:

• `copy_commit_list`
• `reverse_commit_list`
• `free_commit_list`

Acked-by: Patrick Steinhardt 
Signed-off-by: Kristoffer Haugsbakk 
Signed-off-by: Junio C Hamano 
---
 builtin/history.c | 4 ++--
 replay.c          | 2 +-
 upload-pack.c     | 4 ++--
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/builtin/history.c b/builtin/history.c
index 952693808574b7..4c47ce494689ac 100644
--- a/builtin/history.c
+++ b/builtin/history.c
@@ -272,7 +272,7 @@ static int setup_revwalk(struct repository *repo,
 
 		commit_list_insert(original, &from_list);
 		ret = repo_is_descendant_of(repo, head, from_list);
-		free_commit_list(from_list);
+		commit_list_free(from_list);
 
 		if (ret < 0) {
 			ret = error(_("cannot determine descendance"));
@@ -649,7 +649,7 @@ static int split_commit(struct repository *repo,
 	if (index_file.len)
 		unlink(index_file.buf);
 	strbuf_release(&index_file);
-	free_commit_list(parents);
+	commit_list_free(parents);
 	release_index(&index);
 	return ret;
 }
diff --git a/replay.c b/replay.c
index f96f1f6551ae63..4f1996485fdd9a 100644
--- a/replay.c
+++ b/replay.c
@@ -120,7 +120,7 @@ static struct commit *create_commit(struct repository *repo,
 out:
 	repo_unuse_commit_buffer(repo, based_on, message);
 	free_commit_extra_headers(extra);
-	free_commit_list(parents);
+	commit_list_free(parents);
 	strbuf_release(&msg);
 	free(author);
 	return (struct commit *)obj;
diff --git a/upload-pack.c b/upload-pack.c
index 9f6d6fe48c8c58..2bf450ab2880c7 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -886,7 +886,7 @@ static void deepen(struct upload_pack_data *data, int depth)
 					     data->deepen_relative, depth,
 					     SHALLOW, NOT_SHALLOW);
 		send_shallow(data, result);
-		free_commit_list(result);
+		commit_list_free(result);
 	}
 
 	send_unshallow(data);
@@ -900,7 +900,7 @@ static void deepen_by_rev_list(struct upload_pack_data *data,
 	disable_commit_graph(the_repository);
 	result = get_shallow_commits_by_rev_list(argv, SHALLOW, NOT_SHALLOW);
 	send_shallow(data, result);
-	free_commit_list(result);
+	commit_list_free(result);
 	send_unshallow(data);
 }
 

From 83e7f3bd2bac934c21f39175b965c37810a41ea5 Mon Sep 17 00:00:00 2001
From: Kristoffer Haugsbakk 
Date: Thu, 28 May 2026 09:00:11 +0200
Subject: [PATCH 15/16] commit: remove deprecated functions

These functions were deprecated in a series of commits merged in
52882024 (Merge branch 'ps/commit-list-functions-renamed', 2026-02-13).

The compatibility was for in-flight topics at the time.

Acked-by: Patrick Steinhardt 
Signed-off-by: Kristoffer Haugsbakk 
Signed-off-by: Junio C Hamano 
---
 commit.h | 19 -------------------
 1 file changed, 19 deletions(-)

diff --git a/commit.h b/commit.h
index 58150045afafed..5352056f87abfa 100644
--- a/commit.h
+++ b/commit.h
@@ -203,25 +203,6 @@ struct commit_list *commit_list_reverse(struct commit_list *list);
 
 void commit_list_free(struct commit_list *list);
 
-/*
- * Deprecated compatibility functions for `struct commit_list`, to be removed
- * once Git 2.53 is released.
- */
-static inline struct commit_list *copy_commit_list(struct commit_list *l)
-{
-	return commit_list_copy(l);
-}
-
-static inline struct commit_list *reverse_commit_list(struct commit_list *l)
-{
-	return commit_list_reverse(l);
-}
-
-static inline void free_commit_list(struct commit_list *l)
-{
-	commit_list_free(l);
-}
-
 struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */
 
 const char *repo_logmsg_reencode(struct repository *r,

From 600fe743028cbfb640855f659e9851522214bc0b Mon Sep 17 00:00:00 2001
From: Junio C Hamano 
Date: Sun, 7 Jun 2026 23:58:12 +0900
Subject: [PATCH 16/16] The 12th batch

Signed-off-by: Junio C Hamano 
---
 Documentation/RelNotes/2.55.0.adoc | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/Documentation/RelNotes/2.55.0.adoc b/Documentation/RelNotes/2.55.0.adoc
index 2b588a5b45fa37..937ac48e64bdec 100644
--- a/Documentation/RelNotes/2.55.0.adoc
+++ b/Documentation/RelNotes/2.55.0.adoc
@@ -122,6 +122,19 @@ Performance, Internal Implementation, Development Support etc.
    converted to a "service" enum to improve type safety and clarify its
    purpose.
 
+ * 'git restore --staged' has been optimized to avoid unnecessarily expanding
+   the sparse index when operating on paths within the sparse checkout
+   definition, by handling sparse directory entries at the tree level.
+
+ * "git stash -p" has been optimized by reusing cached index
+   entries in its temporary index, avoiding unnecessary lstat()
+   calls on unchanged files.
+
+ * The check for non-stale commits in the priority queue used by
+   `paint_down_to_common` and `ahead_behind` has been optimized by
+   replacing an O(N) scan with an O(1) counter, yielding performance
+   improvements in repositories with wide histories.
+
 
 Fixes since v2.54
 -----------------
@@ -241,6 +254,16 @@ Fixes since v2.54
    day before the specified time, which has been corrected.
    (merge b809304101 ta/approxidate-noon-fix later to maint).
 
+ * The GIT_WORK_TREE variable prepared to invoke the push-to-checkout
+   hook was leaking into the environment even when there was no hook
+   used and broke the default push-to-deploy (i.e., let "git checkout"
+   update the working tree only when the working tree is clean).
+   (merge 44d04e4426 ar/receive-pack-worktree-env later to maint).
+
+ * A batch of documentation pages has been updated to use the modern
+   synopsis style.
+   (merge 2ef248ae45 ja/doc-synopsis-style-again later to maint).
+
  * Other code cleanup, docfix, build fix, etc.
    (merge 80f4b802e9 ja/doc-difftool-synopsis-style later to maint).
    (merge b96490241e jc/doc-timestamps-in-stat later to maint).
@@ -262,3 +285,4 @@ Fixes since v2.54
    (merge d9982e8290 ed/check-connected-close-err-fd-2.53 later to maint).
    (merge 1740cc35d0 ed/check-connected-close-err-fd later to maint).
    (merge f4d7eb3d1c sp/doc-range-diff-takes-notes later to maint).
+   (merge 83e7f3bd2b kh/free-commit-list later to maint).