diff options
Diffstat (limited to 'builtin/fast-export.c')
| -rw-r--r-- | builtin/fast-export.c | 242 |
1 files changed, 180 insertions, 62 deletions
diff --git a/builtin/fast-export.c b/builtin/fast-export.c index a5c82eef1d..c06ee0b213 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -9,12 +9,13 @@ #include "builtin.h" #include "config.h" +#include "environment.h" #include "gettext.h" #include "hex.h" #include "refs.h" #include "refspec.h" #include "object-file.h" -#include "object-store-ll.h" +#include "odb.h" #include "commit.h" #include "object.h" #include "tag.h" @@ -29,14 +30,18 @@ #include "quote.h" #include "remote.h" #include "blob.h" +#include "gpg-interface.h" -static const char *fast_export_usage[] = { +static const char *const fast_export_usage[] = { N_("git fast-export [<rev-list-opts>]"), NULL }; +enum sign_mode { SIGN_ABORT, SIGN_VERBATIM, SIGN_STRIP, SIGN_WARN_VERBATIM, SIGN_WARN_STRIP }; + static int progress; -static enum signed_tag_mode { SIGNED_TAG_ABORT, VERBATIM, WARN, WARN_STRIP, STRIP } signed_tag_mode = SIGNED_TAG_ABORT; +static enum sign_mode signed_tag_mode = SIGN_ABORT; +static enum sign_mode signed_commit_mode = SIGN_STRIP; static enum tag_of_filtered_mode { TAG_FILTERING_ABORT, DROP, REWRITE } tag_of_filtered_mode = TAG_FILTERING_ABORT; static enum reencode_mode { REENCODE_ABORT, REENCODE_YES, REENCODE_NO } reencode_mode = REENCODE_ABORT; static int fake_missing_tagger; @@ -53,23 +58,24 @@ static int anonymize; static struct hashmap anonymized_seeds; static struct revision_sources revision_sources; -static int parse_opt_signed_tag_mode(const struct option *opt, +static int parse_opt_sign_mode(const struct option *opt, const char *arg, int unset) { - enum signed_tag_mode *val = opt->value; - - if (unset || !strcmp(arg, "abort")) - *val = SIGNED_TAG_ABORT; + enum sign_mode *val = opt->value; + if (unset) + return 0; + else if (!strcmp(arg, "abort")) + *val = SIGN_ABORT; else if (!strcmp(arg, "verbatim") || !strcmp(arg, "ignore")) - *val = VERBATIM; - else if (!strcmp(arg, "warn")) - *val = WARN; + *val = SIGN_VERBATIM; + else if (!strcmp(arg, "warn-verbatim") || !strcmp(arg, "warn")) + *val = SIGN_WARN_VERBATIM; else if (!strcmp(arg, "warn-strip")) - *val = WARN_STRIP; + *val = SIGN_WARN_STRIP; else if (!strcmp(arg, "strip")) - *val = STRIP; + *val = SIGN_STRIP; else - return error("Unknown signed-tags mode: %s", arg); + return error("Unknown %s mode: %s", opt->long_name, arg); return 0; } @@ -319,7 +325,7 @@ static void export_blob(const struct object_id *oid) object = (struct object *)lookup_blob(the_repository, oid); eaten = 0; } else { - buf = repo_read_object_file(the_repository, oid, &type, &size); + buf = odb_read_object(the_repository->objects, oid, &type, &size); if (!buf) die("could not read blob %s", oid_to_hex(oid)); if (check_object_signature(the_repository, oid, buf, size, @@ -510,21 +516,6 @@ static void show_filemodify(struct diff_queue_struct *q, } } -static const char *find_encoding(const char *begin, const char *end) -{ - const char *needle = "\nencoding "; - char *bol, *eol; - - bol = memmem(begin, end ? end - begin : strlen(begin), - needle, strlen(needle)); - if (!bol) - return NULL; - bol += strlen(needle); - eol = strchrnul(bol, '\n'); - *eol = '\0'; - return bol; -} - static char *anonymize_ref_component(void) { static int counter; @@ -626,13 +617,85 @@ static void anonymize_ident_line(const char **beg, const char **end) *end = out->buf + out->len; } +/* + * find_commit_multiline_header is similar to find_commit_header, + * except that it handles multi-line headers, rather than simply + * returning the first line of the header. + * + * The returned string has had the ' ' line continuation markers + * removed, and points to allocated memory that must be free()d (not + * to memory within 'msg'). + * + * If the header is found, then *end is set to point at the '\n' in + * msg that immediately follows the header value. + */ +static const char *find_commit_multiline_header(const char *msg, + const char *key, + const char **end) +{ + struct strbuf val = STRBUF_INIT; + const char *bol, *eol; + size_t len; + + bol = find_commit_header(msg, key, &len); + if (!bol) + return NULL; + eol = bol + len; + strbuf_add(&val, bol, len); + + while (eol[0] == '\n' && eol[1] == ' ') { + bol = eol + 2; + eol = strchrnul(bol, '\n'); + strbuf_addch(&val, '\n'); + strbuf_add(&val, bol, eol - bol); + } + + *end = eol; + 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) { int saved_output_format = rev->diffopt.output_format; - const char *commit_buffer; + const char *commit_buffer, *commit_buffer_cursor; const char *author, *author_end, *committer, *committer_end; - const char *encoding, *message; + const char *encoding = NULL; + size_t encoding_len; + struct string_list signatures = STRING_LIST_INIT_DUP; + const char *message; char *reencoded = NULL; struct commit_list *p; const char *refname; @@ -641,21 +704,44 @@ static void handle_commit(struct commit *commit, struct rev_info *rev, rev->diffopt.output_format = DIFF_FORMAT_CALLBACK; parse_commit_or_die(commit); - commit_buffer = repo_get_commit_buffer(the_repository, commit, NULL); - author = strstr(commit_buffer, "\nauthor "); + commit_buffer_cursor = commit_buffer = repo_get_commit_buffer(the_repository, commit, NULL); + + author = strstr(commit_buffer_cursor, "\nauthor "); if (!author) die("could not find author in commit %s", oid_to_hex(&commit->object.oid)); author++; - author_end = strchrnul(author, '\n'); - committer = strstr(author_end, "\ncommitter "); + commit_buffer_cursor = author_end = strchrnul(author, '\n'); + + committer = strstr(commit_buffer_cursor, "\ncommitter "); if (!committer) die("could not find committer in commit %s", oid_to_hex(&commit->object.oid)); committer++; - committer_end = strchrnul(committer, '\n'); - message = strstr(committer_end, "\n\n"); - encoding = find_encoding(committer_end, message); + commit_buffer_cursor = committer_end = strchrnul(committer, '\n'); + + /* + * find_commit_header() and find_commit_multiline_header() get + * a `+ 1` because commit_buffer_cursor points at the trailing + * "\n" at the end of the previous line, but they want a + * pointer to the beginning of the next line. + */ + + if (*commit_buffer_cursor == '\n') { + encoding = find_commit_header(commit_buffer_cursor + 1, "encoding", &encoding_len); + if (encoding) + commit_buffer_cursor = encoding + encoding_len; + } + + if (*commit_buffer_cursor == '\n') { + 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"); if (message) message += 2; @@ -694,16 +780,20 @@ static void handle_commit(struct commit *commit, struct rev_info *rev, if (anonymize) { reencoded = anonymize_commit_message(); } else if (encoding) { - switch(reencode_mode) { + char *buf; + switch (reencode_mode) { case REENCODE_YES: - reencoded = reencode_string(message, "UTF-8", encoding); + buf = xstrfmt("%.*s", (int)encoding_len, encoding); + reencoded = reencode_string(message, "UTF-8", buf); + free(buf); break; case REENCODE_NO: break; case REENCODE_ABORT: - die("Encountered commit-specific encoding %s in commit " + die("Encountered commit-specific encoding %.*s in commit " "%s; use --reencode=[yes|no] to handle it", - encoding, oid_to_hex(&commit->object.oid)); + (int)encoding_len, encoding, + oid_to_hex(&commit->object.oid)); } } if (!commit->parents) @@ -714,8 +804,33 @@ 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 (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 %"PRIuMAX" signature(s) for commit %s", + (uintmax_t)signatures.nr, oid_to_hex(&commit->object.oid)); + /* fallthru */ + case SIGN_VERBATIM: + 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(s) from commit %s", + oid_to_hex(&commit->object.oid)); + /* fallthru */ + case SIGN_STRIP: + break; + } + string_list_clear(&signatures, 0); + } if (!reencoded && encoding) - printf("encoding %s\n", encoding); + printf("encoding %.*s\n", (int)encoding_len, encoding); printf("data %u\n%s", (unsigned)(reencoded ? strlen(reencoded) : message @@ -789,8 +904,8 @@ static void handle_tag(const char *name, struct tag *tag) return; } - buf = repo_read_object_file(the_repository, &tag->object.oid, &type, - &size); + buf = odb_read_object(the_repository->objects, &tag->object.oid, + &type, &size); if (!buf) die("could not read tag %s", oid_to_hex(&tag->object.oid)); message = memmem(buf, size, "\n\n", 2); @@ -828,22 +943,22 @@ static void handle_tag(const char *name, struct tag *tag) const char *signature = strstr(message, "\n-----BEGIN PGP SIGNATURE-----\n"); if (signature) - switch(signed_tag_mode) { - case SIGNED_TAG_ABORT: + switch (signed_tag_mode) { + case SIGN_ABORT: die("encountered signed tag %s; use " "--signed-tags=<mode> to handle it", oid_to_hex(&tag->object.oid)); - case WARN: + case SIGN_WARN_VERBATIM: warning("exporting signed tag %s", oid_to_hex(&tag->object.oid)); /* fallthru */ - case VERBATIM: + case SIGN_VERBATIM: break; - case WARN_STRIP: + case SIGN_WARN_STRIP: warning("stripping signature from tag %s", oid_to_hex(&tag->object.oid)); /* fallthru */ - case STRIP: + case SIGN_STRIP: message_size = signature + 1 - message; break; } @@ -853,7 +968,7 @@ static void handle_tag(const char *name, struct tag *tag) tagged = tag->tagged; tagged_mark = get_object_mark(tagged); if (!tagged_mark) { - switch(tag_of_filtered_mode) { + switch (tag_of_filtered_mode) { case TAG_FILTERING_ABORT: die("tag %s tags unexported object; use " "--tag-of-filtered-object=<mode> to handle it", @@ -869,7 +984,7 @@ static void handle_tag(const char *name, struct tag *tag) p = rewrite_commit((struct commit *)tagged); if (!p) { printf("reset %s\nfrom %s\n\n", - name, oid_to_hex(null_oid())); + name, oid_to_hex(null_oid(the_hash_algo))); free(buf); return; } @@ -883,7 +998,7 @@ static void handle_tag(const char *name, struct tag *tag) if (tagged->type == OBJ_TAG) { printf("reset %s\nfrom %s\n\n", - name, oid_to_hex(null_oid())); + name, oid_to_hex(null_oid(the_hash_algo))); } skip_prefix(name, "refs/tags/", &name); printf("tag %s\n", name); @@ -965,7 +1080,7 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info) continue; } - switch(commit->object.type) { + switch (commit->object.type) { case OBJ_COMMIT: break; case OBJ_BLOB: @@ -1023,7 +1138,7 @@ static void handle_tags_and_duplicates(struct string_list *extras) * it. */ printf("reset %s\nfrom %s\n\n", - name, oid_to_hex(null_oid())); + name, oid_to_hex(null_oid(the_hash_algo))); continue; } @@ -1042,7 +1157,7 @@ static void handle_tags_and_duplicates(struct string_list *extras) if (!reference_excluded_commits) { /* delete the ref */ printf("reset %s\nfrom %s\n\n", - name, oid_to_hex(null_oid())); + name, oid_to_hex(null_oid(the_hash_algo))); continue; } /* set ref to commit using oid, not mark */ @@ -1120,7 +1235,7 @@ static void import_marks(char *input_file, int check_exists) if (last_idnum < mark) last_idnum = mark; - type = oid_object_info(the_repository, &oid, NULL); + type = odb_read_object_info(the_repository->objects, &oid, NULL); if (type < 0) die("object not found: %s", oid_to_hex(&oid)); @@ -1153,7 +1268,7 @@ static void handle_deletes(void) continue; printf("reset %s\nfrom %s\n\n", - refspec->dst, oid_to_hex(null_oid())); + refspec->dst, oid_to_hex(null_oid(the_hash_algo))); } } @@ -1202,7 +1317,10 @@ int cmd_fast_export(int argc, N_("show progress after <n> objects")), OPT_CALLBACK(0, "signed-tags", &signed_tag_mode, N_("mode"), N_("select handling of signed tags"), - parse_opt_signed_tag_mode), + parse_opt_sign_mode), + OPT_CALLBACK(0, "signed-commits", &signed_commit_mode, N_("mode"), + N_("select handling of signed commits"), + parse_opt_sign_mode), OPT_CALLBACK(0, "tag-of-filtered-object", &tag_of_filtered_mode, N_("mode"), N_("select handling of tags that tag filtered objects"), parse_opt_tag_of_filtered_mode), @@ -1244,7 +1362,7 @@ int cmd_fast_export(int argc, usage_with_options (fast_export_usage, options); /* we handle encodings */ - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); repo_init_revisions(the_repository, &revs, prefix); init_revision_sources(&revision_sources); |
