Skip to content

Commit 895680f

Browse files
committed
fmt-merge-msg: Add contents of merged tag in the merge message
When a contributor asks the integrator to merge her history, a signed tag can be a good vehicle to communicate the authenticity of the request while conveying other information such as the purpose of the topic. E.g. a signed tag "for-linus" can be created, and the integrator can run: $ git pull git://example.com/work.git/ for-linus This would allow the integrator to run "git verify-tag FETCH_HEAD" to validate the signed tag. Update fmt-merge-msg so that it pre-fills the merge message template with the body (but not signature) of the tag object to help the integrator write a better merge message, in the same spirit as the existing merge.log summary lines. The message that comes from GPG signature validation is also included in the merge message template to help the integrator verify it, but they are prefixed with "#" to make them comments. Signed-off-by: Junio C Hamano <[email protected]>
1 parent cbda121 commit 895680f

File tree

3 files changed

+92
-2
lines changed

3 files changed

+92
-2
lines changed

builtin/fmt-merge-msg.c

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "revision.h"
66
#include "tag.h"
77
#include "string-list.h"
8+
#include "gpg-interface.h"
89

910
static const char * const fmt_merge_msg_usage[] = {
1011
"git fmt-merge-msg [-m <message>] [--log[=<n>]|--no-log] [--file <file>]",
@@ -262,6 +263,70 @@ static void fmt_merge_msg_title(struct strbuf *out,
262263
strbuf_addf(out, " into %s\n", current_branch);
263264
}
264265

266+
static void fmt_tag_signature(struct strbuf *tagbuf,
267+
struct strbuf *sig,
268+
const char *buf,
269+
unsigned long len)
270+
{
271+
const char *tag_body = strstr(buf, "\n\n");
272+
if (tag_body) {
273+
tag_body += 2;
274+
strbuf_add(tagbuf, tag_body, buf + len - tag_body);
275+
}
276+
strbuf_complete_line(tagbuf);
277+
strbuf_add_lines(tagbuf, "# ", sig->buf, sig->len);
278+
}
279+
280+
static void fmt_merge_msg_sigs(struct strbuf *out)
281+
{
282+
int i, tag_number = 0, first_tag = 0;
283+
struct strbuf tagbuf = STRBUF_INIT;
284+
285+
for (i = 0; i < origins.nr; i++) {
286+
unsigned char *sha1 = origins.items[i].util;
287+
enum object_type type;
288+
unsigned long size, len;
289+
char *buf = read_sha1_file(sha1, &type, &size);
290+
struct strbuf sig = STRBUF_INIT;
291+
292+
if (!buf || type != OBJ_TAG)
293+
goto next;
294+
len = parse_signature(buf, size);
295+
296+
if (size == len)
297+
; /* merely annotated */
298+
else if (verify_signed_buffer(buf, len, buf + len, size - len, &sig)) {
299+
if (!sig.len)
300+
strbuf_addstr(&sig, "gpg verification failed.\n");
301+
}
302+
303+
if (!tag_number++) {
304+
fmt_tag_signature(&tagbuf, &sig, buf, len);
305+
first_tag = i;
306+
} else {
307+
if (tag_number == 2) {
308+
struct strbuf tagline = STRBUF_INIT;
309+
strbuf_addf(&tagline, "\n# %s\n",
310+
origins.items[first_tag].string);
311+
strbuf_insert(&tagbuf, 0, tagline.buf,
312+
tagline.len);
313+
strbuf_release(&tagline);
314+
}
315+
strbuf_addf(&tagbuf, "\n# %s\n",
316+
origins.items[i].string);
317+
fmt_tag_signature(&tagbuf, &sig, buf, len);
318+
}
319+
strbuf_release(&sig);
320+
next:
321+
free(buf);
322+
}
323+
if (tagbuf.len) {
324+
strbuf_addch(out, '\n');
325+
strbuf_addbuf(out, &tagbuf);
326+
}
327+
strbuf_release(&tagbuf);
328+
}
329+
265330
int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
266331
struct fmt_merge_msg_opts *opts)
267332
{
@@ -293,6 +358,9 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
293358
if (opts->add_title && srcs.nr)
294359
fmt_merge_msg_title(out, current_branch);
295360

361+
if (origins.nr)
362+
fmt_merge_msg_sigs(out);
363+
296364
if (opts->shortlog_len) {
297365
struct commit *head;
298366
struct rev_info rev;
@@ -310,8 +378,8 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
310378
shortlog(origins.items[i].string, origins.items[i].util,
311379
head, &rev, opts->shortlog_len, out);
312380
}
313-
if (out->len && out->buf[out->len-1] != '\n')
314-
strbuf_addch(out, '\n');
381+
382+
strbuf_complete_line(out);
315383
return 0;
316384
}
317385

strbuf.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,3 +397,17 @@ int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint)
397397

398398
return len;
399399
}
400+
401+
void strbuf_add_lines(struct strbuf *out, const char *prefix,
402+
const char *buf, size_t size)
403+
{
404+
while (size) {
405+
const char *next = memchr(buf, '\n', size);
406+
next = next ? (next + 1) : (buf + size);
407+
strbuf_addstr(out, prefix);
408+
strbuf_add(out, buf, next - buf);
409+
size -= next - buf;
410+
buf = next;
411+
}
412+
strbuf_complete_line(out);
413+
}

strbuf.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,14 @@ extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
100100
__attribute__((format (printf,2,0)))
101101
extern void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap);
102102

103+
extern void strbuf_add_lines(struct strbuf *sb, const char *prefix, const char *buf, size_t size);
104+
105+
static inline void strbuf_complete_line(struct strbuf *sb)
106+
{
107+
if (sb->len && sb->buf[sb->len - 1] != '\n')
108+
strbuf_addch(sb, '\n');
109+
}
110+
103111
extern size_t strbuf_fread(struct strbuf *, size_t, FILE *);
104112
/* XXX: if read fails, any partial read is undone */
105113
extern ssize_t strbuf_read(struct strbuf *, int fd, size_t hint);

0 commit comments

Comments
 (0)