git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v3 0/2] history: support signing rewritten commits

From
SSouma <git@5ouma.me>
Date
Sep 12, 2026, 16:00 UTC
Message-ID
<20260912160045.36064-1-git@5ouma.me>
In-Reply-To
<20260703145037.69832-1-git@5ouma.me>

The history commands create commits directly and via the replay machinery, but currently have no way to honor `commit.gpgSign` or an explicit signing request. This means users who require signed commits lose that property when rewriting history.

Teach the replay API to accept a signing key, then expose the standard `-S`/`--gpg-sign[=<key-id>]` and `--no-gpg-sign` interface across the `git history drop`, `git history fixup`, `git history reword`, and `git history split` subcommands. The selected policy applies to every new commit, including both halves of a split and replayed descendants.

The implementation follows the precedence used by rebase, cherry-pick, and revert: `commit.gpgSign` supplies the default, command-line options override it, and the last command-line option wins.

The signature records the attestation of the current committer to the rewritten commit while retaining the original author identity; it does not claim authorship of commits written by somebody else.

Changes since v2:
 - Shorten the commit messages based on review feedback
 - Rename the history implementation commit from `builtin/history` to
   `history`
 - Fix the continuation-backslash formatting in `OPT_HISTORY_GPG_SIGN`
Souma (2):
  replay: allow callers to sign commits
  history: sign rewritten commits
 Documentation/git-history.adoc | 16 +++++--
 builtin/history.c              | 84 ++++++++++++++++++++++++++--------
 replay.c                       | 13 ++++--
 replay.h                       |  6 +++
 t/t3451-history-reword.sh      | 63 +++++++++++++++++++++++++
 t/t3452-history-split.sh       | 44 ++++++++++++++++++
 t/t3453-history-fixup.sh       | 39 ++++++++++++++++
 t/t3454-history-drop.sh        | 50 ++++++++++++++++++++
 8 files changed, 286 insertions(+), 29 deletions(-)
Range-diff against v2:
1:  3f4dc0b982 ! 1:  ca35b0acaa replay: allow callers to sign commits
    @@ Metadata
      ## Commit message ##
         replay: allow callers to sign commits
    -    The replay machinery creates commits directly through
    -    `commit_tree_extended()`, but callers cannot currently request
    -    signatures. Commands that replay rewritten history consequently cannot
    -    carry their signing policy through to descendant commits.
    -
    -    Add `sign_commit` to `replay_revisions_options` and thread it through
    -    commit creation. `NULL` preserves the existing unsigned behavior, an
    -    empty string selects the default signing key, and a non-empty string
    -    selects an explicit key. Existing callers zero-initialize the options
    -    structure, so their behavior is unchanged.
    +    Add a signing-key option to replay_revisions_options and pass it to
    +    commit_tree_extended() when creating replayed commits.
         Signed-off-by: Souma <git@5ouma.me>
    @@ replay.c: static struct commit *pick_regular_commit(struct repository *repo,
     +					  enum replay_empty_commit_action empty,
     +					  const char *sign_commit)
      {
    - 	struct commit *base, *replayed_base;
      	struct tree *pickme_tree, *base_tree, *replayed_base_tree;
    +
     @@ replay.c: static struct commit *pick_regular_commit(struct repository *repo,
      		}
      	}
     -	return create_commit(repo, result->tree, pickme, replayed_base, mode);
     +	return create_commit(repo, result->tree, pickme, replayed_base, mode,
    -+			     sign_commit);
    ++					    sign_commit);
      }
      void replay_result_release(struct replay_result *result)
     @@ replay.c: int replay_revisions(struct rev_info *revs,
    - 		last_commit = pick_regular_commit(revs->repo, commit, replayed_commits,
    - 						  mode == REPLAY_MODE_REVERT ? last_commit : onto,
    --						  &merge_opt, &result, mode, opts->empty);
    -+						  &merge_opt, &result, mode, opts->empty,
    -+						  opts->sign_commit);
    - 		if (!last_commit)
    - 			break;
    + 			last_commit = pick_regular_commit(revs->repo, commit, base,
    + 							  &merge_opt, &result,
    +-							  mode, opts->empty);
    ++							  mode, opts->empty,
    ++							  opts->sign_commit);
    + 		}
    + 		if (!last_commit)
      ## replay.h ##
     @@ replay.h: struct replay_revisions_options {
2:  0e63c0b66a ! 2:  f0a1a88411 builtin/history: sign rewritten commits
    @@ Metadata
     Author: Souma <git@5ouma.me>
      ## Commit message ##
    -    builtin/history: sign rewritten commits
    +    history: sign rewritten commits
    -    The history commands create replacement commits directly instead of
    -    using the sequencer or the commit porcelain. As a result, rewritten
    -    commits ignore `commit.gpgSign` and cannot be signed on demand.
    +    Add --gpg-sign/--no-gpg-sign support to git history and honor
    +    commit.gpgSign when creating replacement commits. Thread the selected
    +    signing key through direct rewrites and replayed descendants while
    +    preserving the original author identity.
    -    Read the signing configuration before parsing options so that it
    -    establishes the default and later `-S`/`--gpg-sign` or `--no-gpg-sign`
    -    options override it. Pass the selected key through direct rewrites and
    -    the replay machinery.
    -
    -    Sign every newly created commit, including both halves of a split and
    -    replayed descendants. Dropping the tip creates no replacement commit,
    -    so there is nothing to sign. As with `rebase --gpg-sign`, the signature
    -    records the attestation of the current committer to the rewritten
    -    commit while retaining the original author identity; it does not claim
    -    authorship of commits written by somebody else.
    -
    -    Document the behavior and add GPG-gated coverage for configuration,
    -    command-line overrides, last-option-wins precedence, replayed
    -    descendants, split commits, an explicit signing key, and the
    -    no-new-commit drop case.
    +    Cover configuration, command-line precedence, explicit keys, split commits,
    +    and replayed descendants with GPG-gated tests.
         Signed-off-by: Souma <git@5ouma.me>
    @@ builtin/history.c: enum commit_tree_flags {
     +	return git_default_config(var, value, ctx, NULL);
     +}
     +
    -+#define OPT_HISTORY_GPG_SIGN(v) {                 \
    -+	.type = OPTION_STRING,                    \
    -+	.short_name = 'S',                        \
    -+	.long_name = "gpg-sign",                  \
    -+	.value = (v),                             \
    -+	.argh = N_("key-id"),                     \
    ++#define OPT_HISTORY_GPG_SIGN(v) { \
    ++	.type = OPTION_STRING, \
    ++	.short_name = 'S', \
    ++	.long_name = "gpg-sign", \
    ++	.value = (v), \
    ++	.argh = N_("key-id"), \
     +	.help = N_("GPG-sign rewritten commits"), \
    -+	.flags = PARSE_OPT_OPTARG,                \
    -+	.defval = (intptr_t)"",                   \
    ++	.flags = PARSE_OPT_OPTARG, \
    ++	.defval = (intptr_t)"", \
     +}
     +
      static int commit_tree_ext(struct repository *repo,
--
2.55.0
Previous: Patrick SteinhardtNext: Souma
Message 12 of 30 in “history: sign rewritten commits”
  1. 0/3 history: sign rewritten commitsSouma, Jul 3, 2026
  2. 1/3 builtin/history: sign rewritten commitsSouma, Jul 3, 2026
  3. Patrick SteinhardtJul 16, 2026
  4. 2/3 doc: document history signing optionsSouma, Jul 3, 2026
  5. Patrick SteinhardtJul 16, 2026
  6. 3/3 t345x: cover signed history rewritesSouma, Jul 3, 2026
  7. 0/2 history: support signing rewritten commitsSouma, Jul 17, 2026
  8. 1/2 replay: allow callers to sign commitsSouma, Jul 17, 2026
  9. Patrick SteinhardtSep 11, 2026
  10. 2/2 builtin/history: sign rewritten commitsSouma, Jul 17, 2026
  11. Patrick SteinhardtSep 11, 2026
  12. 0/2 history: support signing rewritten commitsSouma, Sep 12, 2026
  13. 1/2 replay: allow callers to sign commitsSouma, Sep 12, 2026
  14. Patrick SteinhardtSep 28, 2026
  15. 2/2 history: sign rewritten commitsSouma, Sep 12, 2026
  16. Patrick SteinhardtSep 28, 2026
  17. Junio C HamanoSep 28, 2026
  18. Junio C HamanoSep 28, 2026
  19. SoumaSep 28, 2026
  20. 0/2 history: sign rewritten commitsSouma, Oct 2, 2026
  21. Junio C HamanoOct 2, 2026
  22. SoumaOct 3, 2026
  23. 1/2 replay: allow callers to sign commitsSouma, Oct 2, 2026
  24. 2/2 history: sign rewritten commitsSouma, Oct 2, 2026
  25. 0/2 history: sign rewritten commitsSouma, Oct 3, 2026
  26. Junio C HamanoOct 4, 2026
  27. SoumaOct 5, 2026
  28. Junio C HamanoOct 6, 2026
  29. 1/2 replay: allow callers to sign commitsSouma, Oct 3, 2026
  30. 2/2 history: sign rewritten commitsSouma, Oct 3, 2026

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.