diff options
| author | Christian Couder <christian.couder@gmail.com> | 2025-07-09 16:12:53 +0200 |
|---|---|---|
| committer | Junio C Hamano <gitster@pobox.com> | 2025-07-09 16:08:29 -0700 |
| commit | b5b3ddbe5c56c7ded95e7c47c985dc6d61f73ea0 (patch) | |
| tree | ea629a5202795d1d72e257ed8a2a421e1c809a7a /builtin/fast-export.c | |
| parent | The second batch (diff) | |
| download | git-b5b3ddbe5c56c7ded95e7c47c985dc6d61f73ea0.tar.gz git-b5b3ddbe5c56c7ded95e7c47c985dc6d61f73ea0.zip | |
fast-(import|export): improve on commit signature output format
A recent commit, d9cb0e6ff8 (fast-export, fast-import: add support for
signed-commits, 2025-03-10), added support for signed commits to
fast-export and fast-import.
When a signed commit is processed, fast-export can output either
"gpgsig sha1" or "gpgsig sha256" depending on whether the signed
commit uses the SHA-1 or SHA-256 Git object format.
However, this implementation has a number of limitations:
- the output format was not properly described in the documentation,
- the output format is not very informative as it doesn't even say
if the signature is an OpenPGP, an SSH, or an X509 signature,
- the implementation doesn't support having both one signature on
the SHA-1 object and one on the SHA-256 object.
Let's improve on these limitations by improving fast-export and
fast-import so that:
- all the signatures are exported,
- at most one signature on the SHA-1 object and one on the SHA-256
are imported,
- if there is more than one signature on the SHA-1 object or on
the SHA-256 object, fast-import emits a warning for each
additional signature,
- the output format is "gpgsig <git-hash-algo> <signature-format>",
where <git-hash-algo> is the Git object format as before, and
<signature-format> is the signature type ("openpgp", "x509",
"ssh" or "unknown"),
- the output is properly documented.
About the output format:
- <git-hash-algo> allows to know which representation of the commit
was signed (the SHA-1 or the SHA-256 version) which helps with
both signature verification and interoperability between repos
with different hash functions,
- <signature-format> helps tools that process the fast-export
stream, so they don't have to parse the ASCII armor to identify
the signature type.
It could be even better to be able to import more than one signature
on the SHA-1 object and on the SHA-256 object, but other parts of
Git don't handle that well for now, so this is left for future
improvements.
Helped-by: brian m. carlson <sandals@crustytoothpaste.net>
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-export.c')
| -rw-r--r-- | builtin/fast-export.c | 62 |
1 files changed, 48 insertions, 14 deletions
diff --git a/builtin/fast-export.c b/builtin/fast-export.c index fcf6b00d5f..7b4e6a6e41 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -29,6 +29,7 @@ #include "quote.h" #include "remote.h" #include "blob.h" +#include "gpg-interface.h" static const char *const fast_export_usage[] = { N_("git fast-export [<rev-list-opts>]"), @@ -652,6 +653,38 @@ static const char *find_commit_multiline_header(const char *msg, return strbuf_detach(&val, NULL); } +static void print_signature(const char *signature, const char *object_hash) +{ + if (!signature) + return; + + printf("gpgsig %s %s\ndata %u\n%s\n", + object_hash, + get_signature_format(signature), + (unsigned)strlen(signature), + signature); +} + +static const char *append_signatures_for_header(struct string_list *signatures, + const char *pos, + const char *header, + const char *object_hash) +{ + const char *signature; + const char *start = pos; + const char *end = pos; + + while ((signature = find_commit_multiline_header(start + 1, + header, + &end))) { + string_list_append(signatures, signature)->util = (void *)object_hash; + free((char *)signature); + start = end; + } + + return end; +} + static void handle_commit(struct commit *commit, struct rev_info *rev, struct string_list *paths_of_changed_objects) { @@ -660,7 +693,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev, const char *author, *author_end, *committer, *committer_end; const char *encoding = NULL; size_t encoding_len; - const char *signature_alg = NULL, *signature = NULL; + struct string_list signatures = STRING_LIST_INIT_DUP; const char *message; char *reencoded = NULL; struct commit_list *p; @@ -700,10 +733,11 @@ static void handle_commit(struct commit *commit, struct rev_info *rev, } if (*commit_buffer_cursor == '\n') { - if ((signature = find_commit_multiline_header(commit_buffer_cursor + 1, "gpgsig", &commit_buffer_cursor))) - signature_alg = "sha1"; - else if ((signature = find_commit_multiline_header(commit_buffer_cursor + 1, "gpgsig-sha256", &commit_buffer_cursor))) - signature_alg = "sha256"; + const char *after_sha1 = append_signatures_for_header(&signatures, commit_buffer_cursor, + "gpgsig", "sha1"); + const char *after_sha256 = append_signatures_for_header(&signatures, commit_buffer_cursor, + "gpgsig-sha256", "sha256"); + commit_buffer_cursor = (after_sha1 > after_sha256) ? after_sha1 : after_sha256; } message = strstr(commit_buffer_cursor, "\n\n"); @@ -769,30 +803,30 @@ static void handle_commit(struct commit *commit, struct rev_info *rev, printf("%.*s\n%.*s\n", (int)(author_end - author), author, (int)(committer_end - committer), committer); - if (signature) { + if (signatures.nr) { switch (signed_commit_mode) { case SIGN_ABORT: die("encountered signed commit %s; use " "--signed-commits=<mode> to handle it", oid_to_hex(&commit->object.oid)); case SIGN_WARN_VERBATIM: - warning("exporting signed commit %s", - oid_to_hex(&commit->object.oid)); + warning("exporting %"PRIuMAX" signature(s) for commit %s", + (uintmax_t)signatures.nr, oid_to_hex(&commit->object.oid)); /* fallthru */ case SIGN_VERBATIM: - printf("gpgsig %s\ndata %u\n%s", - signature_alg, - (unsigned)strlen(signature), - signature); + for (size_t i = 0; i < signatures.nr; i++) { + struct string_list_item *item = &signatures.items[i]; + print_signature(item->string, item->util); + } break; case SIGN_WARN_STRIP: - warning("stripping signature from commit %s", + warning("stripping signature(s) from commit %s", oid_to_hex(&commit->object.oid)); /* fallthru */ case SIGN_STRIP: break; } - free((char *)signature); + string_list_clear(&signatures, 0); } if (!reencoded && encoding) printf("encoding %.*s\n", (int)encoding_len, encoding); |
