format-patch: allow --interdiff to apply to a lone-patch

When submitting a revised version of a patch or series, it can be helpful (to reviewers) to include a summary of changes since the previous attempt in the form of an interdiff, typically in the cover letter. However, it is occasionally useful, despite making for a noisy read, to insert an interdiff into the commentary section of the lone patch of a 1-patch series. Therefore, extend "git format-patch --interdiff=<prev>" to insert an interdiff into the commentary section of a lone patch rather than requiring a cover letter. The interdiff is indented to avoid confusing git-am and human readers into considering it part of the patch proper. Implementation note: Generating an interdiff for insertion into the commentary section of a patch which itself is currently being generated requires invoking the diffing machinery recursively. However, the machinery does not (presently) support this since it uses global state. Consequently, we need to take care to stash away the state of the in-progress operation while generating the interdiff, and restore it after. Signed-off-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Eric Sunshine committed Jul 22, 2018 at 05:57 UTC ee6cbf712edcbd1dc14993ab2452fbe882dc524a
4 files changed +33 -4
Documentation/git-format-patch.txt
+2 -1
@@ -230,7 +230,8 @@ feeding the result to `git send-email`.
230 fill in a description in the file before sending it out.
231
232 --interdiff=<previous>::
233 - As a reviewer aid, insert an interdiff into the cover letter showing
233 + As a reviewer aid, insert an interdiff into the cover letter,
234 + or as commentary of the lone patch of a 1-patch series, showing
235 the differences between the previous version of the patch series and
236 the series currently being formatted. `previous` is a single revision
237 naming the tip of the previous series which shares a common base with
builtin/log.c
+5 -3
@@ -1540,7 +1540,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
1540 OPT_BOOL(0, "progress", &show_progress,
1541 N_("show progress while generating patches")),
1542 OPT_CALLBACK(0, "interdiff", &idiff_prev, N_("rev"),
1543 - N_("show changes against <rev> in cover letter"),
1543 + N_("show changes against <rev> in cover letter or single patch"),
1544 parse_opt_object_name),
1545 OPT_END()
1546 };
@@ -1765,8 +1765,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
1765 rev.total = total + start_number - 1;
1766
1767 if (idiff_prev.nr) {
1768 - if (!cover_letter)
1769 - die(_("--interdiff requires --cover-letter"));
1768 + if (!cover_letter && total != 1)
1769 + die(_("--interdiff requires --cover-letter or single patch"));
1770 rev.idiff_oid1 = &idiff_prev.oid[idiff_prev.nr - 1];
1771 rev.idiff_oid2 = get_commit_tree_oid(list[0]);
1772 rev.idiff_title = diff_title(&idiff_title, reroll_count,
@@ -1811,6 +1811,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
1811 print_signature(rev.diffopt.file);
1812 total++;
1813 start_number--;
1814 + /* interdiff in cover-letter; omit from patches */
1815 + rev.idiff_oid1 = NULL;
1816 }
1817 rev.add_signoff = do_signoff;
1818
log-tree.c
+14
@@ -14,6 +14,7 @@
14 #include "sequencer.h"
15 #include "line-log.h"
16 #include "help.h"
17 +#include "interdiff.h"
18
19 static struct decoration name_decoration = { "object names" };
20 static int decoration_loaded;
@@ -736,6 +737,19 @@ void show_log(struct rev_info *opt)
737
738 strbuf_release(&msgbuf);
739 free(ctx.notes_message);
740 +
741 + if (cmit_fmt_is_mail(ctx.fmt) && opt->idiff_oid1) {
742 + struct diff_queue_struct dq;
743 +
744 + memcpy(&dq, &diff_queued_diff, sizeof(diff_queued_diff));
745 + DIFF_QUEUE_CLEAR(&diff_queued_diff);
746 +
747 + next_commentary_block(opt, NULL);
748 + fprintf_ln(opt->diffopt.file, "%s", opt->idiff_title);
749 + show_interdiff(opt, 2);
750 +
751 + memcpy(&diff_queued_diff, &dq, sizeof(diff_queued_diff));
752 + }
753 }
754
755 int log_tree_diff_flush(struct rev_info *opt)
t/t4014-format-patch.sh
+12
@@ -1730,6 +1730,7 @@ test_expect_success 'interdiff: cover-letter' '
1730 EOF
1731 git format-patch --cover-letter --interdiff=boop~2 -1 boop &&
1732 test_i18ngrep "^Interdiff:$" 0000-cover-letter.patch &&
1733 + test_i18ngrep ! "^Interdiff:$" 0001-fleep.patch &&
1734 sed "1,/^@@ /d; /^-- $/q" <0000-cover-letter.patch >actual &&
1735 test_cmp expect actual
1736 '
@@ -1739,4 +1740,15 @@ test_expect_success 'interdiff: reroll-count' '
1740 test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
1741 '
1742
1743 +test_expect_success 'interdiff: solo-patch' '
1744 + cat >expect <<-\EOF &&
1745 + +fleep
1746 +
1747 + EOF
1748 + git format-patch --interdiff=boop~2 -1 boop &&
1749 + test_i18ngrep "^Interdiff:$" 0001-fleep.patch &&
1750 + sed "1,/^ @@ /d; /^$/q" <0001-fleep.patch >actual &&
1751 + test_cmp expect actual
1752 +'
1753 +
1754 test_done