Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
9ccd9e6
pretty.c: add %(count) and %(total) placeholders
Mroik Mar 6, 2026
2af59cb
format-patch: move cover letter summary generation
Mroik Mar 6, 2026
6005932
format-patch: add ability to use alt cover format
Mroik Mar 6, 2026
be0ef6f
format-patch: add commitListFormat config
Mroik Mar 6, 2026
51ed9f7
docs: add usage for the cover-letter fmt feature
Mroik Mar 6, 2026
8367733
Merge branch 'ar/config-hooks' into ar/config-hook-cleanups
gitster Mar 9, 2026
b3c222e
Merge branch 'mf/format-patch-cover-letter-format' into mf/format-pat…
gitster Mar 16, 2026
60cb27e
pretty.c: better die message %(count) and %(total)
Mroik Mar 23, 2026
3482b42
format-patch: refactor generate_commit_list_cover
Mroik Mar 23, 2026
67ea2ad
format-patch: rename --cover-letter-format option
Mroik Mar 23, 2026
344f00e
docs/pretty-formats: add %(count) and %(total)
Mroik Mar 23, 2026
24d174f
format.commitListFormat: strip meaning from empty
Mroik Mar 23, 2026
617db87
format-patch: wrap generate_commit_list_cover()
Mroik Mar 23, 2026
d022dc7
format-patch: add preset for --commit-list-format
Mroik Mar 23, 2026
36c16a5
format-patch: --commit-list-format without prefix
Mroik Mar 23, 2026
add3564
hook: move unsorted_string_list_remove() to string-list.[ch]
10ne1 Mar 25, 2026
6b9f9e2
builtin/receive-pack: properly init receive_hook strbuf
10ne1 Mar 25, 2026
b06770e
hook: fix minor style issues
10ne1 Mar 25, 2026
8f7db6f
hook: rename cb_data_free/alloc -> hook_data_free/alloc
10ne1 Mar 25, 2026
4d10f4a
hook: detect & emit two more bugs
10ne1 Mar 25, 2026
a8b1ba8
hook: replace hook_list_clear() -> string_list_clear_func()
10ne1 Mar 25, 2026
2e5dbaf
hook: make consistent use of friendly-name in docs
10ne1 Mar 25, 2026
e0fceec
t1800: add test to verify hook execution ordering
10ne1 Mar 25, 2026
d8513bc
hook: introduce hook_config_cache_entry for per-hook data
10ne1 Mar 25, 2026
b66efad
hook: show config scope in git hook list
10ne1 Mar 25, 2026
e17bd99
hook: show disabled hooks in "git hook list"
10ne1 Mar 25, 2026
5c58dbc
hook: reject unknown hook names in git-hook(1)
10ne1 Mar 25, 2026
1e6434e
sequencer: extract revert message formatting into shared function
edith007 Mar 25, 2026
2760ee4
replay: add --revert mode to reverse commit changes
edith007 Mar 25, 2026
0f77914
worktree: remove "the_repository" from is_current_worktree()
phillipwood Mar 26, 2026
8bad0e0
worktree add: stop reading ".git/HEAD"
phillipwood Mar 26, 2026
7580868
worktree: reject NULL worktree in get_worktree_git_dir()
phillipwood Mar 26, 2026
50da232
revision: include object-name.h
derrickstolee Mar 26, 2026
9b474a6
t5620: prepare branched repo for revision tests
derrickstolee Mar 26, 2026
302aff0
backfill: accept revision arguments
derrickstolee Mar 26, 2026
7be1820
backfill: work with prefix pathspecs
derrickstolee Mar 26, 2026
3f20c21
path-walk: support wildcard pathspecs for blob filtering
derrickstolee Mar 26, 2026
46d1f4c
t5620: test backfill's unknown argument handling
derrickstolee Mar 26, 2026
f54477a
Merge branch 'mf/format-patch-commit-list-format' into mf/format-patc…
gitster Mar 26, 2026
acee42d
docs: fix --commit-list-format related entries
Mroik Mar 27, 2026
0284046
format-patch: removing unconditional wrapping
Mroik Mar 27, 2026
206ca04
t8003: avoid suppressing git's exit code
trieu1162000 Mar 28, 2026
699248d
t8003: modernise style
trieu1162000 Mar 28, 2026
6390178
Merge branch 'mf/format-patch-cover-letter-format'
gitster Apr 3, 2026
aafabe2
Merge branch 'mf/format-patch-commit-list-format'
gitster Apr 3, 2026
cd79c76
Merge branch 'mf/format-patch-commit-list-format-doc'
gitster Apr 3, 2026
4e58217
Merge branch 'ds/backfill-revs'
gitster Apr 3, 2026
0cd4fb9
Merge branch 'ar/config-hook-cleanups'
gitster Apr 3, 2026
05ddb9e
Merge branch 'pw/worktree-reduce-the-repository'
gitster Apr 3, 2026
e0613d2
Merge branch 'sa/replay-revert'
gitster Apr 3, 2026
fed877c
Merge branch 'th/t8003-unhide-git-failures'
gitster Apr 3, 2026
8de2f1b
A bit more on top of 2.54-rc0
gitster Apr 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Documentation/RelNotes/2.54.0.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,16 @@ UI, Workflows & Features
* "git repo info -h" and "git repo structure -h" limit their help output
to the part that is specific to the subcommand.

* "git format-patch --cover-letter" learns to use a simpler format
instead of the traditional shortlog format to list its commits with
a new --commit-list-format option and format.commitListFormat
configuration variable.

* `git backfill` learned to accept revision and pathspec arguments.

* "git replay" (experimental) learns, in addition to "pick" and
"replay", a new operating mode "revert".


Performance, Internal Implementation, Development Support etc.
--------------------------------------------------------------
Expand Down Expand Up @@ -495,3 +505,4 @@ Fixes since v2.53
(merge fc8a4f15e7 gi/doc-boolean-config-typofix later to maint).
(merge 37182267a0 kh/doc-interpret-trailers-1 later to maint).
(merge f64c50e768 jc/rerere-modern-strbuf-handling later to maint).
(merge 699248d89e th/t8003-unhide-git-failures later to maint).
5 changes: 5 additions & 0 deletions Documentation/config/format.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ format.coverLetter::
generate a cover-letter only when there's more than one patch.
Default is false.

format.commitListFormat::
When the `--cover-letter-format` option is not given, `format-patch`
uses the value of this variable to decide how to format the entry of
each commit. Defaults to `shortlog`.

format.outputDirectory::
Set a custom directory to store the resulting files instead of the
current working directory. All directory components will be created.
Expand Down
30 changes: 15 additions & 15 deletions Documentation/config/hook.adoc
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
hook.<name>.command::
The command to execute for `hook.<name>`. `<name>` is a unique
"friendly" name that identifies this hook. (The hook events that
trigger the command are configured with `hook.<name>.event`.) The
value can be an executable path or a shell oneliner. If more than
one value is specified for the same `<name>`, only the last value
parsed is used. See linkgit:git-hook[1].
hook.<friendly-name>.command::
The command to execute for `hook.<friendly-name>`. `<friendly-name>`
is a unique name that identifies this hook. The hook events that
trigger the command are configured with `hook.<friendly-name>.event`.
The value can be an executable path or a shell oneliner. If more than
one value is specified for the same `<friendly-name>`, only the last
value parsed is used. See linkgit:git-hook[1].

hook.<name>.event::
The hook events that trigger `hook.<name>`. The value is the name
of a hook event, like "pre-commit" or "update". (See
hook.<friendly-name>.event::
The hook events that trigger `hook.<friendly-name>`. The value is the
name of a hook event, like "pre-commit" or "update". (See
linkgit:githooks[5] for a complete list of hook events.) On the
specified event, the associated `hook.<name>.command` is executed.
This is a multi-valued key. To run `hook.<name>` on multiple
specified event, the associated `hook.<friendly-name>.command` is executed.
This is a multi-valued key. To run `hook.<friendly-name>` on multiple
events, specify the key more than once. An empty value resets
the list of events, clearing any previously defined events for
`hook.<name>`. See linkgit:git-hook[1].
`hook.<friendly-name>`. See linkgit:git-hook[1].

hook.<name>.enabled::
Whether the hook `hook.<name>` is enabled. Defaults to `true`.
hook.<friendly-name>.enabled::
Whether the hook `hook.<friendly-name>` is enabled. Defaults to `true`.
Set to `false` to disable the hook without removing its
configuration. This is particularly useful when a hook is defined
in a system or global config file and needs to be disabled for a
Expand Down
5 changes: 4 additions & 1 deletion Documentation/git-backfill.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,12 @@ OPTIONS
current sparse-checkout. If the sparse-checkout feature is enabled,
then `--sparse` is assumed and can be disabled with `--no-sparse`.

You may also specify the commit limiting options from linkgit:git-rev-list[1].

SEE ALSO
--------
linkgit:git-clone[1].
linkgit:git-clone[1],
linkgit:git-rev-list[1]

GIT
---
Expand Down
20 changes: 17 additions & 3 deletions Documentation/git-format-patch.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ SYNOPSIS
[(--reroll-count|-v) <n>]
[--to=<email>] [--cc=<email>]
[--[no-]cover-letter] [--quiet]
[--commit-list-format=<format-spec>]
[--[no-]encode-email-headers]
[--no-notes | --notes[=<ref>]]
[--interdiff=<previous>]
Expand Down Expand Up @@ -318,9 +319,21 @@ feeding the result to `git send-email`.

--cover-letter::
--no-cover-letter::
In addition to the patches, generate a cover letter file
containing the branch description, shortlog and the overall diffstat. You can
fill in a description in the file before sending it out.
In addition to the patches, generate a cover letter file containing the
branch description, commit list and the overall diffstat. You can fill
in a description in the file before sending it out.

--commit-list-format=<format-spec>::
Specify the format in which to generate the commit list of the patch
series. The accepted values for format-spec are `shortlog`, `modern` or
a format-string prefixed with `log:`. E.g. `log: %s (%an)`.
`modern` is the same as `log:%w(72)[%(count)/%(total)] %s`.
The `log:` prefix can be omitted if the format-string has a `%` in it
(expecting that it is part of `%<placeholder>`).
Defaults to the `format.commitListFormat` configuration variable, if
set, or `shortlog`.
This option given from the command-line implies the use of
`--cover-letter` unless `--no-cover-letter` is given.

--encode-email-headers::
--no-encode-email-headers::
Expand Down Expand Up @@ -453,6 +466,7 @@ with configuration variables.
signOff = true
outputDirectory = <directory>
coverLetter = auto
commitListFormat = shortlog
coverFromDescription = auto
------------

Expand Down
27 changes: 20 additions & 7 deletions Documentation/git-hook.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ git-hook - Run git hooks
SYNOPSIS
--------
[verse]
'git hook' run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-args>]
'git hook' list [-z] <hook-name>
'git hook' run [--allow-unknown-hook-name] [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-args>]
'git hook' list [--allow-unknown-hook-name] [-z] [--show-scope] <hook-name>

DESCRIPTION
-----------
Expand Down Expand Up @@ -44,7 +44,7 @@ event`), and then `~/bin/spellchecker` will have a chance to check your commit
message (during the `commit-msg` hook event).

Commands are run in the order Git encounters their associated
`hook.<name>.event` configs during the configuration parse (see
`hook.<friendly-name>.event` configs during the configuration parse (see
linkgit:git-config[1]). Although multiple `hook.linter.event` configs can be
added, only one `hook.linter.command` event is valid - Git uses "last-one-wins"
to determine which command to run.
Expand Down Expand Up @@ -76,10 +76,10 @@ first start `~/bin/linter --cpp20` and second start `~/bin/leak-detector`. It
would evaluate the output of each when deciding whether to proceed with the
commit.

For a full list of hook events which you can set your `hook.<name>.event` to,
For a full list of hook events which you can set your `hook.<friendly-name>.event` to,
and how hooks are invoked during those events, see linkgit:githooks[5].

Git will ignore any `hook.<name>.event` that specifies an event it doesn't
Git will ignore any `hook.<friendly-name>.event` that specifies an event it doesn't
recognize. This is intended so that tools which wrap Git can use the hook
infrastructure to run their own hooks; see "WRAPPERS" for more guidance.

Expand Down Expand Up @@ -113,14 +113,21 @@ Any positional arguments to the hook should be passed after a
mandatory `--` (or `--end-of-options`, see linkgit:gitcli[7]). See
linkgit:githooks[5] for arguments hooks might expect (if any).

list [-z]::
list [-z] [--show-scope]::
Print a list of hooks which will be run on `<hook-name>` event. If no
hooks are configured for that event, print a warning and return 1.
Use `-z` to terminate output lines with NUL instead of newlines.

OPTIONS
-------

--allow-unknown-hook-name::
By default `git hook run` and `git hook list` will bail out when
`<hook-name>` is not a hook event known to Git (see linkgit:githooks[5]
for the list of known hooks). This is meant to help catch typos
such as `prereceive` when `pre-receive` was intended. Pass this
flag to allow unknown hook names.

--to-stdin::
For "run"; specify a file which will be streamed into the
hook's stdin. The hook will receive the entire file from
Expand All @@ -134,6 +141,12 @@ OPTIONS
-z::
Terminate "list" output lines with NUL instead of newlines.

--show-scope::
For "list"; prefix each configured hook's friendly name with a
tab-separated config scope (e.g. `local`, `global`, `system`),
mirroring the output style of `git config --show-scope`. Traditional
hooks from the hookdir are unaffected.

WRAPPERS
--------

Expand All @@ -153,7 +166,7 @@ Then, in your 'mywrapper' tool, you can invoke any users' configured hooks by
running:

----
git hook run mywrapper-start-tests \
git hook run --allow-unknown-hook-name mywrapper-start-tests \
# providing something to stdin
--stdin some-tempfile-123 \
# execute hooks in serial
Expand Down
52 changes: 44 additions & 8 deletions Documentation/git-replay.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ git-replay - EXPERIMENTAL: Replay commits on a new base, works with bare repos t
SYNOPSIS
--------
[verse]
(EXPERIMENTAL!) 'git replay' ([--contained] --onto <newbase> | --advance <branch>) [--ref-action[=<mode>]] <revision-range>
(EXPERIMENTAL!) 'git replay' ([--contained] --onto <newbase> | --advance <branch> | --revert <branch>) [--ref-action[=<mode>]] <revision-range>

DESCRIPTION
-----------
Expand Down Expand Up @@ -42,6 +42,25 @@ The history is replayed on top of the <branch> and <branch> is updated to
point at the tip of the resulting history. This is different from `--onto`,
which uses the target only as a starting point without updating it.

--revert <branch>::
Starting point at which to create the reverted commits; must be a
branch name.
+
When `--revert` is specified, the commits in the revision range are reverted
(their changes are undone) and the reverted commits are created on top of
<branch>. The <branch> is then updated to point at the new commits. This is
the same as running `git revert <revision-range>` but does not update the
working tree.
+
The commit messages follow `git revert` conventions: they are prefixed with
"Revert" and include "This reverts commit <hash>." When reverting a commit
whose message starts with "Revert", the new message uses "Reapply" instead.
Unlike cherry-pick which preserves the original author, revert commits use
the current user as the author, matching the behavior of `git revert`.
+
This option is mutually exclusive with `--onto` and `--advance`. It is also
incompatible with `--contained` (which is a modifier for `--onto` only).

--contained::
Update all branches that point at commits in
<revision-range>. Requires `--onto`.
Expand All @@ -60,10 +79,11 @@ The default mode can be configured via the `replay.refAction` configuration vari

<revision-range>::
Range of commits to replay; see "Specifying Ranges" in
linkgit:git-rev-parse[1]. In `--advance <branch>` mode, the
range should have a single tip, so that it's clear to which tip the
advanced <branch> should point. Any commits in the range whose
changes are already present in the branch the commits are being
linkgit:git-rev-parse[1]. In `--advance <branch>` or
`--revert <branch>` mode, the range should have a single tip,
so that it's clear to which tip the advanced or reverted
<branch> should point. Any commits in the range whose changes
are already present in the branch the commits are being
replayed onto will be dropped.

:git-replay: 1
Expand All @@ -84,9 +104,10 @@ When using `--ref-action=print`, the output is usable as input to
update refs/heads/branch3 ${NEW_branch3_HASH} ${OLD_branch3_HASH}

where the number of refs updated depends on the arguments passed and
the shape of the history being replayed. When using `--advance`, the
number of refs updated is always one, but for `--onto`, it can be one
or more (rebasing multiple branches simultaneously is supported).
the shape of the history being replayed. When using `--advance` or
`--revert`, the number of refs updated is always one, but for `--onto`,
it can be one or more (rebasing multiple branches simultaneously is
supported).

There is no stderr output on conflicts; see the <<exit-status,EXIT
STATUS>> section below.
Expand Down Expand Up @@ -152,6 +173,21 @@ all commits they have since `base`, playing them on top of
`origin/main`. These three branches may have commits on top of `base`
that they have in common, but that does not need to be the case.

To revert commits on a branch:

------------
$ git replay --revert main topic~2..topic
------------

This reverts the last two commits from `topic`, creating revert commits on
top of `main`, and updates `main` to point at the result. This is useful when
commits from `topic` were previously merged or cherry-picked into `main` and
need to be undone.

NOTE: For reverting an entire merge request as a single commit (rather than
commit-by-commit), consider using `git merge-tree --merge-base $TIP HEAD $BASE`
which can avoid unnecessary merge conflicts.

GIT
---
Part of the linkgit:git[1] suite
4 changes: 4 additions & 0 deletions Documentation/pretty-formats.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,10 @@ The placeholders are:
linkgit:git-rev-list[1])
+%d+:: ref names, like the --decorate option of linkgit:git-log[1]
+%D+:: ref names without the " (", ")" wrapping.
+%(count)+:: the number of a patch within a patch series. Used only in
`--commit-list-format` in `format-patch`
+%(total)+:: the total number of patches in a patch series. Used only in
`--commit-list-format` in `format-patch`
++%(decorate++`[:<option>,...]`++)++::
ref names with custom decorations. The `decorate` string may be followed by a
colon and zero or more comma-separated options. Option values may contain
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2675,6 +2675,7 @@ git$X: git.o GIT-LDFLAGS $(BUILTIN_OBJS) $(GITLIBS)

help.sp help.s help.o: command-list.h
builtin/bugreport.sp builtin/bugreport.s builtin/bugreport.o: hook-list.h
builtin/hook.sp builtin/hook.s builtin/hook.o: hook-list.h

builtin/help.sp builtin/help.s builtin/help.o: config-list.h GIT-PREFIX
builtin/help.sp builtin/help.s builtin/help.o: EXTRA_CPPFLAGS = \
Expand Down
22 changes: 16 additions & 6 deletions builtin/backfill.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ struct backfill_context {
struct oid_array current_batch;
size_t min_batch_size;
int sparse;
struct rev_info revs;
};

static void backfill_context_clear(struct backfill_context *ctx)
Expand Down Expand Up @@ -79,7 +80,6 @@ static int fill_missing_blobs(const char *path UNUSED,

static int do_backfill(struct backfill_context *ctx)
{
struct rev_info revs;
struct path_walk_info info = PATH_WALK_INFO_INIT;
int ret;

Expand All @@ -91,13 +91,14 @@ static int do_backfill(struct backfill_context *ctx)
}
}

repo_init_revisions(ctx->repo, &revs, "");
handle_revision_arg("HEAD", &revs, 0, 0);
/* Walk from HEAD if otherwise unspecified. */
if (!ctx->revs.pending.nr)
add_head_to_pending(&ctx->revs);

info.blobs = 1;
info.tags = info.commits = info.trees = 0;

info.revs = &revs;
info.revs = &ctx->revs;
info.path_fn = fill_missing_blobs;
info.path_fn_data = ctx;

Expand All @@ -108,7 +109,6 @@ static int do_backfill(struct backfill_context *ctx)
download_batch(ctx);

path_walk_info_clear(&info);
release_revisions(&revs);
return ret;
}

Expand All @@ -120,6 +120,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit
.current_batch = OID_ARRAY_INIT,
.min_batch_size = 50000,
.sparse = 0,
.revs = REV_INFO_INIT,
};
struct option options[] = {
OPT_UNSIGNED(0, "min-batch-size", &ctx.min_batch_size,
Expand All @@ -134,7 +135,15 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit
builtin_backfill_usage, options);

argc = parse_options(argc, argv, prefix, options, builtin_backfill_usage,
0);
PARSE_OPT_KEEP_UNKNOWN_OPT |
PARSE_OPT_KEEP_ARGV0 |
PARSE_OPT_KEEP_DASHDASH);

repo_init_revisions(repo, &ctx.revs, prefix);
argc = setup_revisions(argc, argv, &ctx.revs, NULL);

if (argc > 1)
die(_("unrecognized argument: %s"), argv[1]);

repo_config(repo, git_default_config, NULL);

Expand All @@ -143,5 +152,6 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit

result = do_backfill(&ctx);
backfill_context_clear(&ctx);
release_revisions(&ctx.revs);
return result;
}
Loading