aboutsummaryrefslogtreecommitdiff
path: root/builtin/fast-import.c
diff options
context:
space:
mode:
authorChristian Couder <christian.couder@gmail.com>2025-11-17 05:34:50 +0100
committerJunio C Hamano <gitster@pobox.com>2025-11-26 08:43:44 -0800
commitc20f112e5149d1bd0d4741c4b28a65f81318309a (patch)
tree68ab2d63f36af2854b3e49c563bd213fea1357a6 /builtin/fast-import.c
parentcb034c020aba54360e7c19faf82021399bf131e7 (diff)
downloadgit-c20f112e5149d1bd0d4741c4b28a65f81318309a.tar.xz
fast-import: add 'strip-if-invalid' mode to --signed-commits=<mode>
Tools like `git filter-repo`[1] use `git fast-export` and `git fast-import` to rewrite repository history. When rewriting history using one such tool though, commit signatures might become invalid because the commits they sign changed due to the changes in the repository history made by the tool between the fast-export and the fast-import steps. Note that as far as signature handling goes: * Since fast-export doesn't know what changes filter-repo may make to the stream, it can't know whether the signatures will still be valid. * Since filter-repo doesn't know what history canonicalizations fast-export performed (and it performs a few), it can't know whether the signatures will still be valid. * Therefore, fast-import is the only process in the pipeline that can know whether a specified signature remains valid. Having invalid signatures in a rewritten repository could be confusing, so users rewritting history might prefer to simply discard signatures that are invalid at the fast-import step. For example a common use case is to rewrite only "recent" history. While specifying commit ranges corresponding to "recent" commits could work, users worry about getting it wrong and want to just automatically rewrite everything, expecting older commit signatures to be untouched. To let them do that, let's add a new 'strip-if-invalid' mode to the `--signed-commits=<mode>` option of `git fast-import`. It would be interesting for the `--signed-tags=<mode>` option to have this mode too, but we leave that for a future improvement. It might also be possible for `git fast-export` to have such a mode in its `--signed-commits=<mode>` and `--signed-tags=<mode>` options, but the use cases for it are much less clear, so we also leave that for possible future improvements. For now let's just die() if 'strip-if-invalid' is passed to these options where it hasn't been implemented yet. [1]: https://github.com/newren/git-filter-repo Helped-by: Elijah Newren <newren@gmail.com> Signed-off-by: Christian Couder <chriscool@tuxfamily.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Diffstat (limited to 'builtin/fast-import.c')
-rw-r--r--builtin/fast-import.c59
1 files changed, 53 insertions, 6 deletions
diff --git a/builtin/fast-import.c b/builtin/fast-import.c
index 493de57ef6..e2c6894461 100644
--- a/builtin/fast-import.c
+++ b/builtin/fast-import.c
@@ -2772,7 +2772,7 @@ static void add_gpgsig_to_commit(struct strbuf *commit_data,
{
struct string_list siglines = STRING_LIST_INIT_NODUP;
- if (!sig->hash_algo)
+ if (!sig || !sig->hash_algo)
return;
strbuf_addstr(commit_data, header);
@@ -2827,6 +2827,45 @@ static void finalize_commit_buffer(struct strbuf *new_data,
strbuf_addbuf(new_data, msg);
}
+static void handle_strip_if_invalid(struct strbuf *new_data,
+ struct signature_data *sig_sha1,
+ struct signature_data *sig_sha256,
+ struct strbuf *msg)
+{
+ struct strbuf tmp_buf = STRBUF_INIT;
+ struct signature_check signature_check = { 0 };
+ int ret;
+
+ /* Check signature in a temporary commit buffer */
+ strbuf_addbuf(&tmp_buf, new_data);
+ finalize_commit_buffer(&tmp_buf, sig_sha1, sig_sha256, msg);
+ ret = verify_commit_buffer(tmp_buf.buf, tmp_buf.len, &signature_check);
+
+ if (ret) {
+ const char *signer = signature_check.signer ?
+ signature_check.signer : _("unknown");
+ const char *subject;
+ int subject_len = find_commit_subject(msg->buf, &subject);
+
+ if (subject_len > 100)
+ warning(_("stripping invalid signature for commit '%.100s...'\n"
+ " allegedly by %s"), subject, signer);
+ else if (subject_len > 0)
+ warning(_("stripping invalid signature for commit '%.*s'\n"
+ " allegedly by %s"), subject_len, subject, signer);
+ else
+ warning(_("stripping invalid signature for commit\n"
+ " allegedly by %s"), signer);
+
+ finalize_commit_buffer(new_data, NULL, NULL, msg);
+ } else {
+ strbuf_swap(new_data, &tmp_buf);
+ }
+
+ signature_check_clear(&signature_check);
+ strbuf_release(&tmp_buf);
+}
+
static void parse_new_commit(const char *arg)
{
static struct strbuf msg = STRBUF_INIT;
@@ -2878,6 +2917,7 @@ static void parse_new_commit(const char *arg)
warning(_("importing a commit signature verbatim"));
/* fallthru */
case SIGN_VERBATIM:
+ case SIGN_STRIP_IF_INVALID:
import_one_signature(&sig_sha1, &sig_sha256, v);
break;
@@ -2962,7 +3002,11 @@ static void parse_new_commit(const char *arg)
"encoding %s\n",
encoding);
- finalize_commit_buffer(&new_data, &sig_sha1, &sig_sha256, &msg);
+ if (signed_commit_mode == SIGN_STRIP_IF_INVALID &&
+ (sig_sha1.hash_algo || sig_sha256.hash_algo))
+ handle_strip_if_invalid(&new_data, &sig_sha1, &sig_sha256, &msg);
+ else
+ finalize_commit_buffer(&new_data, &sig_sha1, &sig_sha256, &msg);
free(author);
free(committer);
@@ -2984,9 +3028,6 @@ static void handle_tag_signature(struct strbuf *msg, const char *name)
switch (signed_tag_mode) {
/* First, modes that don't change anything */
- case SIGN_ABORT:
- die(_("encountered signed tag; use "
- "--signed-tags=<mode> to handle it"));
case SIGN_WARN_VERBATIM:
warning(_("importing a tag signature verbatim for tag '%s'"), name);
/* fallthru */
@@ -3003,7 +3044,13 @@ static void handle_tag_signature(struct strbuf *msg, const char *name)
strbuf_setlen(msg, sig_offset);
break;
- /* Third, BUG */
+ /* Third, aborting modes */
+ case SIGN_ABORT:
+ die(_("encountered signed tag; use "
+ "--signed-tags=<mode> to handle it"));
+ case SIGN_STRIP_IF_INVALID:
+ die(_("'strip-if-invalid' is not a valid mode for "
+ "git fast-import with --signed-tags=<mode>"));
default:
BUG("invalid signed_tag_mode value %d from tag '%s'",
signed_tag_mode, name);