This option causes the raw commit data to be inserted between the changelog and the diffstat when you run git-format-patch. With a following patch to 'git am', this will allow the exact reconstruction of the commit to the point where the sha1 will be the same. There is also a new config option controlling the default behaviour: format.complete Signed-off-by: Vegard Nossum <vegard.nossum@xxxxxxxxxx> Previous-version: 622a0469a4970c5daac0c0323e2d6a77b3bebbdb --- Documentation/config/format.txt | 7 ++++++ Documentation/git-format-patch.txt | 9 ++++++++ builtin/log.c | 35 ++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/Documentation/config/format.txt b/Documentation/config/format.txt index 40cad9278f..3a38679837 100644 --- a/Documentation/config/format.txt +++ b/Documentation/config/format.txt @@ -87,6 +87,13 @@ format.useAutoBase:: A boolean value which lets you enable the `--base=auto` option of format-patch by default. +format.complete:: + Provides the default value for the `--complete` option to + format-patch. If true, the raw commit metadata (including the + SHA1) is included at the bottom of the diff, before the signature. + This allows a recipient who has all the parent commits and/or the + tree to reconstruct the commit with the same SHA1. + format.notes:: Provides the default value for the `--notes` option to format-patch. Accepts a boolean value, or a ref which specifies diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt index 2035d4d5d5..74fc6d8a8c 100644 --- a/Documentation/git-format-patch.txt +++ b/Documentation/git-format-patch.txt @@ -26,6 +26,7 @@ SYNOPSIS [--no-notes | --notes[=<ref>]] [--interdiff=<previous>] [--range-diff=<previous> [--creation-factor=<percent>]] + [--[no-]complete] [--progress] [<common diff options>] [ <since> | <revision range> ] @@ -325,6 +326,14 @@ you can use `--suffix=-patch` to get `0001-description-of-my-change-patch`. range are always formatted as creation patches, independently of this flag. +--[no-]complete:: + Include the raw commit metadata (including the SHA1) at the + bottom of the diff, before the signature. This allows a + recipient who has all the parent commits and/or the tree to + reconstruct the commit with the same SHA1. The default is + `--no-complete`, unless the `format.complete` configuration + option is set. + --progress:: Show progress reports on stderr as patches are generated. diff --git a/builtin/log.c b/builtin/log.c index 89873d2dc2..822a0838b6 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -783,6 +783,8 @@ enum { COVER_AUTO }; +static int fmt_complete; + static int git_format_config(const char *var, const char *value, void *cb) { struct rev_info *rev = cb; @@ -888,6 +890,10 @@ static int git_format_config(const char *var, const char *value, void *cb) } return 0; } + if (!strcmp(var, "format.complete")) { + fmt_complete = git_config_bool(var, value); + return 0; + } return git_log_config(var, value, cb); } @@ -1490,6 +1496,23 @@ static void print_bases(struct base_tree_info *bases, FILE *file) oidclr(&bases->base_commit); } +static void print_meta(struct rev_info *opt, struct commit *commit) +{ + const char *buffer = get_commit_buffer(commit, NULL); + const char *subject; + + fprintf(opt->diffopt.file, "--\n"); + fprintf(opt->diffopt.file, "commit %s\n", oid_to_hex(&commit->object.oid)); + + /* + * TODO: hex-encode to avoid mailer mangling? + */ + if (find_commit_subject(buffer, &subject)) + fprintf(opt->diffopt.file, "%.*s", (int) (subject - buffer), buffer); + else + fprintf(opt->diffopt.file, "%s", buffer); +} + static const char *diff_title(struct strbuf *sb, int reroll_count, const char *generic, const char *rerolled) { @@ -1622,6 +1645,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) N_("add a signature")), OPT_STRING(0, "base", &base_commit, N_("base-commit"), N_("add prerequisite tree info to the patch series")), + OPT_BOOL(0, "complete", &fmt_complete, + N_("include all the information necessary to reconstruct commit exactly")), OPT_FILENAME(0, "signature-file", &signature_file, N_("add a signature from a file")), OPT__QUIET(&quiet, N_("don't print the patch filenames")), @@ -1921,6 +1946,14 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) prepare_bases(&bases, base, list, nr); } + if (fmt_complete) { + /* + * We need the commit buffer so that we can output the exact + * sequence of bytes that gets hashed as part of a commit. + */ + save_commit_buffer = 1; + } + if (in_reply_to || thread || cover_letter) rev.ref_message_ids = xcalloc(1, sizeof(struct string_list)); if (in_reply_to) { @@ -2004,6 +2037,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) rev.shown_one = 0; if (shown) { print_bases(&bases, rev.diffopt.file); + if (fmt_complete) + print_meta(&rev, commit); if (rev.mime_boundary) fprintf(rev.diffopt.file, "\n--%s%s--\n\n\n", mime_boundary_leader, -- commit 7fa59e79ef5474fb4c657fb2ff6a8341cc17c897 tree 891d334e23f950afbaaafc182384309fd8c73e48 parent d966095db01190a2196e31195ea6fa0c722aa732 author Vegard Nossum <vegard.nossum@xxxxxxxxxx> 1570284959 +0200 committer Vegard Nossum <vegard.nossum@xxxxxxxxxx> 1571666151 +0200 -- 2.24.0.rc0.3.g4ba423c3c2