format-patch: add --interdiff option to embed diff in cover letter
When submitting a revised version of a patch series, it can be helpful (to reviewers) to include a summary of changes since the previous attempt in the form of an interdiff, however, doing so involves manually copy/pasting the diff into the cover letter. Add an --interdiff option to automate this process. The argument to --interdiff specifies the tip of the previous attempt against which to generate the interdiff. For example: git format-patch --cover-letter --interdiff=v1 -3 v2 The previous attempt and the patch series being formatted must share a common base. 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
126facf8219177bc265be725a8579c86ebdf99a5
7 files changed
+78
-2
Documentation/git-format-patch.txt
+9
@@ -23,6 +23,7 @@ SYNOPSIS
23
[(--reroll-count|-v) <n>]
24
[--to=<email>] [--cc=<email>]
25
[--[no-]cover-letter] [--quiet] [--notes[=<ref>]]
26
+ [--interdiff=<previous>]
27
[--progress]
28
[<common diff options>]
29
[ <since> | <revision range> ]
@@ -228,6 +229,14 @@ feeding the result to `git send-email`.
229
containing the branch description, shortlog and the overall diffstat. You can
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
234
+ the differences between the previous version of the patch series and
235
+ the series currently being formatted. `previous` is a single revision
236
+ naming the tip of the previous series which shares a common base with
237
+ the series being formatted (for example `git format-patch
238
+ --cover-letter --interdiff=feature/v1 -3 feature/v2`).
239
+
240
--notes[=<ref>]::
241
Append the notes (see linkgit:git-notes[1]) for the commit
242
after the three-dash line.
Makefile
+1
@@ -871,6 +871,7 @@ LIB_OBJS += hashmap.o
871
LIB_OBJS += help.o
872
LIB_OBJS += hex.o
873
LIB_OBJS += ident.o
874
+LIB_OBJS += interdiff.o
875
LIB_OBJS += kwset.o
876
LIB_OBJS += levenshtein.o
877
LIB_OBJS += line-log.o
builtin/log.c
+22
-2
@@ -30,6 +30,7 @@
30
#include "gpg-interface.h"
31
#include "progress.h"
32
#include "commit-slab.h"
33
+#include "interdiff.h"
34
35
#define MAIL_DEFAULT_WRAP 72
36
@@ -1082,6 +1083,11 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
1083
/* We can only do diffstat with a unique reference point */
1084
if (origin)
1085
show_diffstat(rev, origin, head);
1086
+
1087
+ if (rev->idiff_oid1) {
1088
+ fprintf_ln(rev->diffopt.file, "%s", _("Interdiff:"));
1089
+ show_interdiff(rev);
1090
+ }
1091
}
1092
1093
static const char *clean_message_id(const char *msg_id)
@@ -1448,6 +1454,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
1454
struct base_tree_info bases;
1455
int show_progress = 0;
1456
struct progress *progress = NULL;
1457
+ struct oid_array idiff_prev = OID_ARRAY_INIT;
1458
1459
const struct option builtin_format_patch_options[] = {
1460
{ OPTION_CALLBACK, 'n', "numbered", &numbered, NULL,
@@ -1521,6 +1528,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
1528
OPT__QUIET(&quiet, N_("don't print the patch filenames")),
1529
OPT_BOOL(0, "progress", &show_progress,
1530
N_("show progress while generating patches")),
1531
+ OPT_CALLBACK(0, "interdiff", &idiff_prev, N_("rev"),
1532
+ N_("show changes against <rev> in cover letter"),
1533
+ parse_opt_object_name),
1534
OPT_END()
1535
};
1536
@@ -1706,7 +1716,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
1716
if (rev.pending.nr == 2) {
1717
struct object_array_entry *o = rev.pending.objects;
1718
if (oidcmp(&o[0].item->oid, &o[1].item->oid) == 0)
1709
- return 0;
1719
+ goto done;
1720
}
1721
get_patch_ids(&rev, &ids);
1722
}
@@ -1730,7 +1740,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
1740
}
1741
if (nr == 0)
1742
/* nothing to do */
1733
- return 0;
1743
+ goto done;
1744
total = nr;
1745
if (cover_letter == -1) {
1746
if (config_cover_letter == COVER_AUTO)
@@ -1743,6 +1753,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
1753
if (numbered)
1754
rev.total = total + start_number - 1;
1755
1756
+ if (idiff_prev.nr) {
1757
+ if (!cover_letter)
1758
+ die(_("--interdiff requires --cover-letter"));
1759
+ rev.idiff_oid1 = &idiff_prev.oid[idiff_prev.nr - 1];
1760
+ rev.idiff_oid2 = get_commit_tree_oid(list[0]);
1761
+ }
1762
+
1763
if (!signature) {
1764
; /* --no-signature inhibits all signatures */
1765
} else if (signature && signature != git_version_string) {
@@ -1860,6 +1877,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
1877
string_list_clear(&extra_hdr, 0);
1878
if (ignore_if_in_upstream)
1879
free_patch_ids(&ids);
1880
+
1881
+done:
1882
+ oid_array_clear(&idiff_prev);
1883
return 0;
1884
}
1885
interdiff.c
new
+17
@@ -0,0 +1,17 @@
1
+#include "cache.h"
2
+#include "commit.h"
3
+#include "revision.h"
4
+#include "interdiff.h"
5
+
6
+void show_interdiff(struct rev_info *rev)
7
+{
8
+ struct diff_options opts;
9
+
10
+ memcpy(&opts, &rev->diffopt, sizeof(opts));
11
+ opts.output_format = DIFF_FORMAT_PATCH;
12
+ diff_setup_done(&opts);
13
+
14
+ diff_tree_oid(rev->idiff_oid1, rev->idiff_oid2, "", &opts);
15
+ diffcore_std(&opts);
16
+ diff_flush(&opts);
17
+}
interdiff.h
new
+8
@@ -0,0 +1,8 @@
1
+#ifndef INTERDIFF_H
2
+#define INTERDIFF_H
3
+
4
+struct rev_info;
5
+
6
+void show_interdiff(struct rev_info *);
7
+
8
+#endif
revision.h
+4
@@ -212,6 +212,10 @@ struct rev_info {
212
/* notes-specific options: which refs to show */
213
struct display_notes_opt notes_opt;
214
215
+ /* interdiff */
216
+ const struct object_id *idiff_oid1;
217
+ const struct object_id *idiff_oid2;
218
+
219
/* commit counts */
220
int count_left;
221
int count_right;
t/t4014-format-patch.sh
+17
@@ -1717,4 +1717,21 @@ test_expect_success 'format-patch --pretty=mboxrd' '
1717
test_cmp expect actual
1718
'
1719
1720
+test_expect_success 'interdiff: setup' '
1721
+ git checkout -b boop master &&
1722
+ test_commit fnorp blorp &&
1723
+ test_commit fleep blorp
1724
+'
1725
+
1726
+test_expect_success 'interdiff: cover-letter' '
1727
+ sed "y/q/ /" >expect <<-\EOF &&
1728
+ +fleep
1729
+ --q
1730
+ EOF
1731
+ git format-patch --cover-letter --interdiff=boop~2 -1 boop &&
1732
+ test_i18ngrep "^Interdiff:$" 0000-cover-letter.patch &&
1733
+ sed "1,/^@@ /d; /^-- $/q" <0000-cover-letter.patch >actual &&
1734
+ test_cmp expect actual
1735
+'
1736
+
1737
test_done