{"thread":{"id":"66393","subject":"[PATCH 0/2] replay: add signing support","startedAt":"2026-09-25T20:53:53Z","lastAt":"2026-10-02T21:23:18Z","messageCount":8,"participants":["Patrick Monette","Patrick Steinhardt","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"553326","messageId":"20260925205348.1210154-1-pmonette@google.com","threadId":"66393","inReplyTo":null,"subject":"[PATCH 0/2] replay: add signing support","fromName":"Patrick Monette","fromEmail":"pmonette@google.com","sentAt":"2026-09-25T20:53:46Z","receivedAt":"2026-09-25T20:53:53Z","isPatch":true,"body":"This pair of commits fixes a FIXME in replay.c. With this, it's possible\nto sign commits using `git replay`.\n\nTo follow the convention of git plumbing commands, where they must\nbehave the same regardless of user config, `commit.gpgSign` is\nintentionally ignored.\n\nThe first patch fixes pick_regular_commit() to ensure failures to create\ncommits are correctly handled, which can now happen more easily because\nof signing.\n\nPatrick Monette (2):\n  replay: handle failure to create commits\n  replay: add the -S option\n\n Documentation/git-replay.adoc |  10 +++-\n builtin/replay.c              |  12 +++-\n replay.c                      |  17 ++++--\n replay.h                      |   6 ++\n t/meson.build                 |   1 +\n t/t3651-replay-gpg-sign.sh    | 107 ++++++++++++++++++++++++++++++++++\n 6 files changed, 146 insertions(+), 7 deletions(-)\n create mode 100755 t/t3651-replay-gpg-sign.sh\n\n-- \n2.56.0.rc1.315.gc6ed9934b7-goog\n\n"},{"id":"553327","messageId":"20260925205348.1210154-2-pmonette@google.com","threadId":"66393","inReplyTo":"20260925205348.1210154-1-pmonette@google.com","subject":"[PATCH 1/2] replay: handle failure to create commits","fromName":"Patrick Monette","fromEmail":"pmonette@google.com","sentAt":"2026-09-25T20:53:47Z","receivedAt":"2026-09-25T20:53:56Z","isPatch":true,"body":"When pick_regular_commit() returns NULL, the caller relies on\n`result->clean` to figure out what happened. 1 means success, 0 means a\nconflict, and a negative value means an error.\n\nRight now, if the commit creation fails, `result->clean` stays at 1. The\ncaller doesn't expect the combination of NULL + clean == 1, so it breaks\nout of the loop, but the rest of the function treats this as a success.\n\nThe next commit will add a failure mode (signing) to the commit\ncreation, so this needs to be handled correctly. Set `result->clean`\nto -1 when the commit creation fails.\n\nSigned-off-by: Patrick Monette <pmonette@google.com>\n---\n replay.c | 7 ++++++-\n 1 file changed, 6 insertions(+), 1 deletion(-)\n\ndiff --git a/replay.c b/replay.c\nindex f415103023..ad87863565 100644\n--- a/replay.c\n+++ b/replay.c\n@@ -291,6 +291,7 @@ static struct commit *pick_regular_commit(struct repository *repo,\n \t\t\t\t\t  enum replay_empty_commit_action empty)\n {\n \tstruct tree *pickme_tree, *base_tree, *replayed_base_tree;\n+\tstruct commit *new_commit;\n \n \tif (pickme->parents)\n \t\tbase_tree = repo_get_commit_tree(repo, pickme->parents->item);\n@@ -361,7 +362,11 @@ static struct commit *pick_regular_commit(struct repository *repo,\n \t\t}\n \t}\n \n-\treturn create_commit(repo, result->tree, pickme, replayed_base, mode);\n+\tnew_commit = create_commit(repo, result->tree, pickme, replayed_base,\n+\t\t\t\t   mode);\n+\tif (!new_commit)\n+\t\tresult->clean = -1;\n+\treturn new_commit;\n }\n \n void replay_result_release(struct replay_result *result)\n-- \n2.56.0.rc1.315.gc6ed9934b7-goog\n\n"},{"id":"553328","messageId":"20260925205348.1210154-3-pmonette@google.com","threadId":"66393","inReplyTo":"20260925205348.1210154-1-pmonette@google.com","subject":"[PATCH 2/2] replay: add the -S option","fromName":"Patrick Monette","fromEmail":"pmonette@google.com","sentAt":"2026-09-25T20:53:48Z","receivedAt":"2026-09-25T20:53:58Z","isPatch":true,"body":"`git replay` currently doesn't support signing. In fact, there is a\nFIXME to address this.\n\nAdd the -S option and its related options --gpg-sign and --no-gpg-sign.\n\nSigned-off-by: Patrick Monette <pmonette@google.com>\n---\n Documentation/git-replay.adoc |  10 +++-\n builtin/replay.c              |  12 +++-\n replay.c                      |  12 ++--\n replay.h                      |   6 ++\n t/meson.build                 |   1 +\n t/t3651-replay-gpg-sign.sh    | 107 ++++++++++++++++++++++++++++++++++\n 6 files changed, 141 insertions(+), 7 deletions(-)\n create mode 100755 t/t3651-replay-gpg-sign.sh\n\ndiff --git a/Documentation/git-replay.adoc b/Documentation/git-replay.adoc\nindex 58b4c0c470..2e5caba0be 100644\n--- a/Documentation/git-replay.adoc\n+++ b/Documentation/git-replay.adoc\n@@ -10,7 +10,7 @@ SYNOPSIS\n --------\n [verse]\n (EXPERIMENTAL!) 'git replay' ([--contained] --onto=<newbase> | --advance=<branch> | --revert=<branch>)\n-\t\t\t     [--ref=<ref>] [--ref-action=<mode>] [--linearize] <revision-range>\n+\t\t\t     [--ref=<ref>] [--ref-action=<mode>] [--linearize] [-S[<keyid>]] <revision-range>\n \n DESCRIPTION\n -----------\n@@ -106,6 +106,14 @@ behavior of git-rebase(1)'s `--no-rebase-merges` option.)\n `--contained`. To linearize several branches, replay them in separate\n `git replay` invocations.\n \n+-S[<keyid>]::\n+--gpg-sign[=<keyid>]::\n+--no-gpg-sign::\n+\tGPG-sign commits. The `keyid` argument is optional and\n+\tdefaults to the committer identity; if specified, it must be\n+\tstuck to the option without a space. `--no-gpg-sign` is useful to\n+\tcountermand a `--gpg-sign` option given earlier on the command line.\n+\n <revision-range>::\n \tRange of commits to replay; see \"Specifying Ranges\" in\n \tlinkgit:git-rev-parse[1]. In `--advance=<branch>` or\ndiff --git a/builtin/replay.c b/builtin/replay.c\nindex d39626a37d..87c628e2ed 100644\n--- a/builtin/replay.c\n+++ b/builtin/replay.c\n@@ -85,7 +85,7 @@ int cmd_replay(int argc,\n \tconst char *const replay_usage[] = {\n \t\tN_(\"(EXPERIMENTAL!) git replay \"\n \t\t   \"([--contained] --onto=<newbase> | --advance=<branch> | --revert=<branch>)\\n\"\n-\t\t   \"[--ref=<ref>] [--ref-action=<mode>] [--linearize] <revision-range>\"),\n+\t\t   \"[--ref=<ref>] [--ref-action=<mode>] [--linearize] [-S[<keyid>]] <revision-range>\"),\n \t\tNULL\n \t};\n \tstruct option replay_options[] = {\n@@ -113,6 +113,16 @@ int cmd_replay(int argc,\n \t\t\t     PARSE_OPT_NONEG),\n \t\tOPT_BOOL(0, \"linearize\", &opts.linearize,\n \t\t\t N_(\"drop merge commits, replaying only non-merge commits\")),\n+\t\t{\n+\t\t\t.type = OPTION_STRING,\n+\t\t\t.short_name = 'S',\n+\t\t\t.long_name = \"gpg-sign\",\n+\t\t\t.value = &opts.sign_commit,\n+\t\t\t.argh = N_(\"key-id\"),\n+\t\t\t.help = N_(\"GPG-sign commits\"),\n+\t\t\t.flags = PARSE_OPT_OPTARG,\n+\t\t\t.defval = (intptr_t) \"\",\n+\t\t},\n \t\tOPT_END()\n \t};\n \ndiff --git a/replay.c b/replay.c\nindex ad87863565..9a84e297b1 100644\n--- a/replay.c\n+++ b/replay.c\n@@ -85,13 +85,13 @@ static struct commit *create_commit(struct repository *repo,\n \t\t\t\t    struct tree *tree,\n \t\t\t\t    struct commit *based_on,\n \t\t\t\t    struct commit *parent,\n-\t\t\t\t    enum replay_mode mode)\n+\t\t\t\t    enum replay_mode mode,\n+\t\t\t\t    const char *sign_commit)\n {\n \tstruct object_id ret;\n \tstruct object *obj = NULL;\n \tstruct commit_list *parents = NULL;\n \tchar *author = NULL;\n-\tchar *sign_commit = NULL; /* FIXME: cli users might want to sign again */\n \tstruct commit_extra_header *extra = NULL;\n \tstruct strbuf msg = STRBUF_INIT;\n \tconst char *out_enc = get_commit_output_encoding();\n@@ -288,7 +288,8 @@ static struct commit *pick_regular_commit(struct repository *repo,\n \t\t\t\t\t  struct merge_options *merge_opt,\n \t\t\t\t\t  struct merge_result *result,\n \t\t\t\t\t  enum replay_mode mode,\n-\t\t\t\t\t  enum replay_empty_commit_action empty)\n+\t\t\t\t\t  enum replay_empty_commit_action empty,\n+\t\t\t\t\t  const char *sign_commit)\n {\n \tstruct tree *pickme_tree, *base_tree, *replayed_base_tree;\n \tstruct commit *new_commit;\n@@ -363,7 +364,7 @@ static struct commit *pick_regular_commit(struct repository *repo,\n \t}\n \n \tnew_commit = create_commit(repo, result->tree, pickme, replayed_base,\n-\t\t\t\t   mode);\n+\t\t\t\t   mode, sign_commit);\n \tif (!new_commit)\n \t\tresult->clean = -1;\n \treturn new_commit;\n@@ -486,7 +487,8 @@ int replay_revisions(struct rev_info *revs,\n \n \t\t\tlast_commit = pick_regular_commit(revs->repo, commit, base,\n \t\t\t\t\t\t\t  &merge_opt, &result,\n-\t\t\t\t\t\t\t  mode, opts->empty);\n+\t\t\t\t\t\t\t  mode, opts->empty,\n+\t\t\t\t\t\t\t  opts->sign_commit);\n \t\t}\n \n \t\tif (!last_commit)\ndiff --git a/replay.h b/replay.h\nindex 2c71afbfde..7e93ab9565 100644\n--- a/replay.h\n+++ b/replay.h\n@@ -67,6 +67,12 @@ struct replay_revisions_options {\n \t * Whether to linearize the commits (i.e. drop merge commits).\n \t */\n \tint linearize;\n+\n+\t/*\n+\t * If non-NULL, GPG-sign the new commits. An empty string signs with\n+\t * the default key (the committer identity); otherwise, the key ID.\n+\t */\n+\tconst char *sign_commit;\n };\n \n /* This struct is used as an out-parameter by `replay_revisions()`. */\ndiff --git a/t/meson.build b/t/meson.build\nindex 3ca7b27104..93c01e26a2 100644\n--- a/t/meson.build\n+++ b/t/meson.build\n@@ -426,6 +426,7 @@ integration_tests = [\n   't3601-rm-pathspec-file.sh',\n   't3602-rm-sparse-checkout.sh',\n   't3650-replay-basics.sh',\n+  't3651-replay-gpg-sign.sh',\n   't3700-add.sh',\n   't3701-add-interactive.sh',\n   't3702-add-edit.sh',\ndiff --git a/t/t3651-replay-gpg-sign.sh b/t/t3651-replay-gpg-sign.sh\nnew file mode 100755\nindex 0000000000..61a3375b52\n--- /dev/null\n+++ b/t/t3651-replay-gpg-sign.sh\n@@ -0,0 +1,107 @@\n+#!/bin/sh\n+\n+test_description='git replay --[no-]gpg-sign'\n+\n+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n+\n+. ./test-lib.sh\n+. \"$TEST_DIRECTORY/lib-gpg.sh\"\n+\n+if ! test_have_prereq GPG\n+then\n+\tskip_all='skip all git replay --[no-]gpg-sign tests, gpg not available'\n+\ttest_done\n+fi\n+\n+# Checks that \"topic\" was replayed onto \"main\", and that the replayed\n+# commits are all signed (\"signed\") or all unsigned (\"unsigned\").\n+check_replayed () {\n+\tgit merge-base --is-ancestor main topic &&\n+\tgit rev-list main..topic >replayed &&\n+\ttest_line_count = 2 replayed &&\n+\tfor commit in $(cat replayed)\n+\tdo\n+\t\tcase \"$1\" in\n+\t\tsigned)\n+\t\t\tgit verify-commit $commit || return 1\n+\t\t\t;;\n+\t\tunsigned)\n+\t\t\ttest_must_fail git verify-commit $commit || return 1\n+\t\t\t;;\n+\t\tesac\n+\tdone\n+}\n+\n+test_expect_success 'setup' '\n+\ttest_commit A &&\n+\ttest_commit B &&\n+\tgit switch -c topic A &&\n+\techo C >C &&\n+\tgit add C &&\n+\tgit commit -S -m C &&\n+\tgit tag C &&\n+\techo D >D &&\n+\tgit add D &&\n+\tgit commit -S -m D &&\n+\tgit tag D &&\n+\tgit switch main\n+'\n+\n+test_expect_success 'replay without --gpg-sign does not sign' '\n+\tgit branch -f topic D &&\n+\tgit verify-commit C &&\n+\tgit verify-commit D &&\n+\tgit replay --onto main A..topic &&\n+\tcheck_replayed unsigned\n+'\n+\n+test_expect_success 'replay --gpg-sign signs with the default key' '\n+\tgit branch -f topic D &&\n+\tgit replay --gpg-sign --onto main A..topic &&\n+\tcheck_replayed signed &&\n+\techo \"C O Mitter <committer@example.com>\" >expect &&\n+\tgit log -1 --format=\"%GS\" topic >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'replay -S<keyid> signs with the given key' '\n+\tgit branch -f topic D &&\n+\tgit replay -SB7227189 --onto main A..topic &&\n+\tgit rev-list main..topic >replayed &&\n+\ttest_line_count = 2 replayed &&\n+\techo D4BE22311AD3131E5EDA29A461092E85B7227189 >expect &&\n+\tfor commit in $(cat replayed)\n+\tdo\n+\t\tgit log -1 --format=\"%GP\" $commit >actual &&\n+\t\ttest_cmp expect actual || return 1\n+\tdone\n+'\n+\n+test_expect_success 'replay --no-gpg-sign countermands --gpg-sign' '\n+\tgit branch -f topic D &&\n+\tgit replay --gpg-sign --no-gpg-sign --onto main A..topic &&\n+\tcheck_replayed unsigned\n+'\n+\n+test_expect_success 'replay ignores commit.gpgSign' '\n+\tgit branch -f topic D &&\n+\tgit -c commit.gpgSign=true replay --onto main A..topic &&\n+\tcheck_replayed unsigned\n+'\n+\n+test_expect_success 'replay fails and updates no ref when signing fails' '\n+\tgit branch -f topic D &&\n+\ttest_must_fail git replay -Snonexistent-key --onto main A..topic &&\n+\ttest_cmp_rev D topic\n+'\n+\n+test_expect_success 'replay --ref fails and updates no ref when signing fails' '\n+\tgit branch -f topic D &&\n+\ttest_must_fail git replay -Snonexistent-key --onto main \\\n+\t\t--ref refs/heads/other A..topic &&\n+\ttest_must_fail git rev-parse --verify refs/heads/other &&\n+\ttest_cmp_rev D topic\n+'\n+\n+test_done\n-- \n2.56.0.rc1.315.gc6ed9934b7-goog\n\n"},{"id":"553415","messageId":"aroaLu02NQ65Y2Ju@pks.im","threadId":"66393","inReplyTo":"20260925205348.1210154-2-pmonette@google.com","subject":"Re: [PATCH 1/2] replay: handle failure to create commits","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-09-28T07:41:34Z","receivedAt":"2026-09-28T07:41:40Z","isPatch":true,"body":"On Fri, Sep 25, 2026 at 04:53:47PM -0400, Patrick Monette wrote:\n> When pick_regular_commit() returns NULL, the caller relies on\n> `result->clean` to figure out what happened. 1 means success, 0 means a\n> conflict, and a negative value means an error.\n> \n> Right now, if the commit creation fails, `result->clean` stays at 1. The\n> caller doesn't expect the combination of NULL + clean == 1, so it breaks\n> out of the loop, but the rest of the function treats this as a success.\n> \n> The next commit will add a failure mode (signing) to the commit\n> creation, so this needs to be handled correctly. Set `result->clean`\n> to -1 when the commit creation fails.\n\nTrue, this is something we should fix indeed.\n\n> diff --git a/replay.c b/replay.c\n> index f415103023..ad87863565 100644\n> --- a/replay.c\n> +++ b/replay.c\n> @@ -361,7 +362,11 @@ static struct commit *pick_regular_commit(struct repository *repo,\n>  \t\t}\n>  \t}\n>  \n> -\treturn create_commit(repo, result->tree, pickme, replayed_base, mode);\n> +\tnew_commit = create_commit(repo, result->tree, pickme, replayed_base,\n> +\t\t\t\t   mode);\n> +\tif (!new_commit)\n> +\t\tresult->clean = -1;\n> +\treturn new_commit;\n>  }\n\nOther error paths end up printing an error message. But we don't have to\nbecause `create_commit()` already knows to do that for us.\n\nPatrick\n"},{"id":"553416","messageId":"aroaNcYqNIXcnQ-L@pks.im","threadId":"66393","inReplyTo":"20260925205348.1210154-3-pmonette@google.com","subject":"Re: [PATCH 2/2] replay: add the -S option","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-09-28T07:41:41Z","receivedAt":"2026-09-28T07:41:45Z","isPatch":true,"body":"On Fri, Sep 25, 2026 at 04:53:48PM -0400, Patrick Monette wrote:\n> diff --git a/builtin/replay.c b/builtin/replay.c\n> index d39626a37d..87c628e2ed 100644\n> --- a/builtin/replay.c\n> +++ b/builtin/replay.c\n> @@ -113,6 +113,16 @@ int cmd_replay(int argc,\n>  \t\t\t     PARSE_OPT_NONEG),\n>  \t\tOPT_BOOL(0, \"linearize\", &opts.linearize,\n>  \t\t\t N_(\"drop merge commits, replaying only non-merge commits\")),\n> +\t\t{\n> +\t\t\t.type = OPTION_STRING,\n> +\t\t\t.short_name = 'S',\n> +\t\t\t.long_name = \"gpg-sign\",\n> +\t\t\t.value = &opts.sign_commit,\n> +\t\t\t.argh = N_(\"key-id\"),\n> +\t\t\t.help = N_(\"GPG-sign commits\"),\n> +\t\t\t.flags = PARSE_OPT_OPTARG,\n> +\t\t\t.defval = (intptr_t) \"\",\n> +\t\t},\n>  \t\tOPT_END()\n>  \t};\n\nOkay. We can't use `OPT_STRING()` or `OPT_STRING_F()` here because we\nwant to set the default value.\n\nI notice that we don't start to honor \"commit.gpgSign\". Is there a\nreason for this?\n\nPatrick\n"},{"id":"553417","messageId":"aroaOsUFWt2lYOVS@pks.im","threadId":"66393","inReplyTo":"20260925205348.1210154-1-pmonette@google.com","subject":"Re: [PATCH 0/2] replay: add signing support","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-09-28T07:41:46Z","receivedAt":"2026-09-28T07:41:51Z","isPatch":true,"body":"Hi,\n\nOn Fri, Sep 25, 2026 at 04:53:46PM -0400, Patrick Monette wrote:\n> This pair of commits fixes a FIXME in replay.c. With this, it's possible\n> to sign commits using `git replay`.\n> \n> To follow the convention of git plumbing commands, where they must\n> behave the same regardless of user config, `commit.gpgSign` is\n> intentionally ignored.\n> \n> The first patch fixes pick_regular_commit() to ensure failures to create\n> commits are correctly handled, which can now happen more easily because\n> of signing.\n\nNote that there's already a patch series in flight that's adding the\ninfra to sign commits at [1]. Your patches will conflict with that\nseries, even though you're ultimately adapting git-replay(1) and not\ngit-history(1). So I'd suggest that once the series at [1] land, you can\nmaybe rebase your changes and then send a new version.\n\nPatrick\n\n[1]: <20260912160045.36064-1-git@5ouma.me>\n"},{"id":"553488","messageId":"xmqqcxtxo0vx.fsf@gitster.g","threadId":"66393","inReplyTo":"aroaOsUFWt2lYOVS@pks.im","subject":"Re: [PATCH 0/2] replay: add signing support","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-09-28T15:23:46Z","receivedAt":"2026-09-28T15:23:49Z","isPatch":true,"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> Hi,\n>\n> On Fri, Sep 25, 2026 at 04:53:46PM -0400, Patrick Monette wrote:\n>> This pair of commits fixes a FIXME in replay.c. With this, it's possible\n>> to sign commits using `git replay`.\n>> \n>> To follow the convention of git plumbing commands, where they must\n>> behave the same regardless of user config, `commit.gpgSign` is\n>> intentionally ignored.\n>> \n>> The first patch fixes pick_regular_commit() to ensure failures to create\n>> commits are correctly handled, which can now happen more easily because\n>> of signing.\n>\n> Note that there's already a patch series in flight that's adding the\n> infra to sign commits at [1]. Your patches will conflict with that\n> series, even though you're ultimately adapting git-replay(1) and not\n> git-history(1). So I'd suggest that once the series at [1] land, you can\n> maybe rebase your changes and then send a new version.\n\nAh, the other one says \"history\" but touches the same replay\nmachinery to update with the signature feature, hence this will need\nto take advantage of that.  The sequencing makes sense.\n\n> Patrick\n>\n> [1]: <20260912160045.36064-1-git@5ouma.me>\n"},{"id":"554011","messageId":"xmqqse2nvltp.fsf@gitster.g","threadId":"66393","inReplyTo":"20260925205348.1210154-3-pmonette@google.com","subject":"Re: [PATCH 2/2] replay: add the -S option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-10-02T21:23:14Z","receivedAt":"2026-10-02T21:23:18Z","isPatch":true,"body":"Patrick Monette <pmonette@google.com> writes:\n\n> `git replay` currently doesn't support signing. In fact, there is a\n> FIXME to address this.\n>\n> Add the -S option and its related options --gpg-sign and --no-gpg-sign.\n>\n> Signed-off-by: Patrick Monette <pmonette@google.com>\n> ---\n>  Documentation/git-replay.adoc |  10 +++-\n>  builtin/replay.c              |  12 +++-\n>  replay.c                      |  12 ++--\n>  replay.h                      |   6 ++\n>  t/meson.build                 |   1 +\n>  t/t3651-replay-gpg-sign.sh    | 107 ++++++++++++++++++++++++++++++++++\n>  6 files changed, 141 insertions(+), 7 deletions(-)\n>  create mode 100755 t/t3651-replay-gpg-sign.sh\n\nThe main part of the patch, which is the change to replay.[ch], has\nstriking similarity to another topic from mid July [*].\n\n  https://lore.kernel.org/git/20260717145142.39478-2-git@5ouma.me/\n\nThat topic has its latest reroll posted recently and it still looks\nvery similar.\n\n  https://lore.kernel.org/git/20261002132718.3830-2-git@5ouma.me/\n\nInstead of making duplicated effort, given that this community is\nlimited by reviewer bandwidth more than it is in need of new\npatches, it would be very much appreciated if you can give a review\nto the other topic to help another developer and move it forward.\n\nThere would be things your topic wanted to do that is different from\nwhat they wanted to achieve.  Theirs is about \"git history\", and\nthis topic is about \"git replay\".  So after their topic stabilized,\nyou can salvage the remainder of your topic and rebase them on top\nof their patch.\n\nThanks.\n\n\n[Footnote]\n\n * It shows us that there are only certain ways to implement a\n   thing, and it is hard to be \"original\" these days ;-)\n\n\n> diff --git a/replay.c b/replay.c\n> index ad87863565..9a84e297b1 100644\n> --- a/replay.c\n> +++ b/replay.c\n> @@ -85,13 +85,13 @@ static struct commit *create_commit(struct repository *repo,\n>  \t\t\t\t    struct tree *tree,\n>  \t\t\t\t    struct commit *based_on,\n>  \t\t\t\t    struct commit *parent,\n> -\t\t\t\t    enum replay_mode mode)\n> +\t\t\t\t    enum replay_mode mode,\n> +\t\t\t\t    const char *sign_commit)\n>  {\n>  \tstruct object_id ret;\n>  \tstruct object *obj = NULL;\n>  \tstruct commit_list *parents = NULL;\n>  \tchar *author = NULL;\n> -\tchar *sign_commit = NULL; /* FIXME: cli users might want to sign again */\n>  \tstruct commit_extra_header *extra = NULL;\n>  \tstruct strbuf msg = STRBUF_INIT;\n>  \tconst char *out_enc = get_commit_output_encoding();\n> @@ -288,7 +288,8 @@ static struct commit *pick_regular_commit(struct repository *repo,\n>  \t\t\t\t\t  struct merge_options *merge_opt,\n>  \t\t\t\t\t  struct merge_result *result,\n>  \t\t\t\t\t  enum replay_mode mode,\n> -\t\t\t\t\t  enum replay_empty_commit_action empty)\n> +\t\t\t\t\t  enum replay_empty_commit_action empty,\n> +\t\t\t\t\t  const char *sign_commit)\n>  {\n>  \tstruct tree *pickme_tree, *base_tree, *replayed_base_tree;\n>  \tstruct commit *new_commit;\n> @@ -363,7 +364,7 @@ static struct commit *pick_regular_commit(struct repository *repo,\n>  \t}\n>  \n>  \tnew_commit = create_commit(repo, result->tree, pickme, replayed_base,\n> -\t\t\t\t   mode);\n> +\t\t\t\t   mode, sign_commit);\n>  \tif (!new_commit)\n>  \t\tresult->clean = -1;\n>  \treturn new_commit;\n> @@ -486,7 +487,8 @@ int replay_revisions(struct rev_info *revs,\n>  \n>  \t\t\tlast_commit = pick_regular_commit(revs->repo, commit, base,\n>  \t\t\t\t\t\t\t  &merge_opt, &result,\n> -\t\t\t\t\t\t\t  mode, opts->empty);\n> +\t\t\t\t\t\t\t  mode, opts->empty,\n> +\t\t\t\t\t\t\t  opts->sign_commit);\n>  \t\t}\n>  \n>  \t\tif (!last_commit)\n> diff --git a/replay.h b/replay.h\n> index 2c71afbfde..7e93ab9565 100644\n> --- a/replay.h\n> +++ b/replay.h\n> @@ -67,6 +67,12 @@ struct replay_revisions_options {\n>  \t * Whether to linearize the commits (i.e. drop merge commits).\n>  \t */\n>  \tint linearize;\n> +\n> +\t/*\n> +\t * If non-NULL, GPG-sign the new commits. An empty string signs with\n> +\t * the default key (the committer identity); otherwise, the key ID.\n> +\t */\n> +\tconst char *sign_commit;\n>  };\n>  \n>  /* This struct is used as an out-parameter by `replay_revisions()`. */\n"}]}