Skip to content

Commit 87d2062

Browse files
committed
Merge branch 'sb/format-patch-patchname'
* sb/format-patch-patchname: format_sanitized_subject: Don't trim past initial length of strbuf log-tree: fix patch filename computation in "git format-patch" format-patch: --numbered-files and --stdout aren't mutually exclusive format-patch: --attach/inline uses filename instead of SHA1 format-patch: move get_patch_filename() into log-tree format-patch: pass a commit to reopen_stdout() format-patch: construct patch filename in one function pretty.c: add %f format specifier to format_commit_message()
2 parents a6e5ef7 + 871d21d commit 87d2062

17 files changed

+381
-129
lines changed

Documentation/pretty-formats.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ The placeholders are:
121121
- '%d': ref names, like the --decorate option of linkgit:git-log[1]
122122
- '%e': encoding
123123
- '%s': subject
124+
- '%f': sanitized subject line, suitable for a filename
124125
- '%b': body
125126
- '%Cred': switch color to red
126127
- '%Cgreen': switch color to green

builtin-log.c

Lines changed: 43 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -417,13 +417,6 @@ int cmd_log(int argc, const char **argv, const char *prefix)
417417
}
418418

419419
/* format-patch */
420-
#define FORMAT_PATCH_NAME_MAX 64
421-
422-
static int istitlechar(char c)
423-
{
424-
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
425-
(c >= '0' && c <= '9') || c == '.' || c == '_';
426-
}
427420

428421
static const char *fmt_patch_suffix = ".patch";
429422
static int numbered = 0;
@@ -523,92 +516,33 @@ static int git_format_config(const char *var, const char *value, void *cb)
523516
return git_log_config(var, value, cb);
524517
}
525518

526-
527-
static const char *get_oneline_for_filename(struct commit *commit,
528-
int keep_subject)
529-
{
530-
static char filename[PATH_MAX];
531-
char *sol;
532-
int len = 0;
533-
int suffix_len = strlen(fmt_patch_suffix) + 1;
534-
535-
sol = strstr(commit->buffer, "\n\n");
536-
if (!sol)
537-
filename[0] = '\0';
538-
else {
539-
int j, space = 0;
540-
541-
sol += 2;
542-
/* strip [PATCH] or [PATCH blabla] */
543-
if (!keep_subject && !prefixcmp(sol, "[PATCH")) {
544-
char *eos = strchr(sol + 6, ']');
545-
if (eos) {
546-
while (isspace(*eos))
547-
eos++;
548-
sol = eos;
549-
}
550-
}
551-
552-
for (j = 0;
553-
j < FORMAT_PATCH_NAME_MAX - suffix_len - 5 &&
554-
len < sizeof(filename) - suffix_len &&
555-
sol[j] && sol[j] != '\n';
556-
j++) {
557-
if (istitlechar(sol[j])) {
558-
if (space) {
559-
filename[len++] = '-';
560-
space = 0;
561-
}
562-
filename[len++] = sol[j];
563-
if (sol[j] == '.')
564-
while (sol[j + 1] == '.')
565-
j++;
566-
} else
567-
space = 1;
568-
}
569-
while (filename[len - 1] == '.'
570-
|| filename[len - 1] == '-')
571-
len--;
572-
filename[len] = '\0';
573-
}
574-
return filename;
575-
}
576-
577519
static FILE *realstdout = NULL;
578520
static const char *output_directory = NULL;
579521
static int outdir_offset;
580522

581-
static int reopen_stdout(const char *oneline, int nr, struct rev_info *rev)
523+
static int reopen_stdout(struct commit *commit, struct rev_info *rev)
582524
{
583-
char filename[PATH_MAX];
584-
int len = 0;
525+
struct strbuf filename = STRBUF_INIT;
585526
int suffix_len = strlen(fmt_patch_suffix) + 1;
586527

587528
if (output_directory) {
588-
len = snprintf(filename, sizeof(filename), "%s",
589-
output_directory);
590-
if (len >=
591-
sizeof(filename) - FORMAT_PATCH_NAME_MAX - suffix_len)
529+
strbuf_addstr(&filename, output_directory);
530+
if (filename.len >=
531+
PATH_MAX - FORMAT_PATCH_NAME_MAX - suffix_len)
592532
return error("name of output directory is too long");
593-
if (filename[len - 1] != '/')
594-
filename[len++] = '/';
533+
if (filename.buf[filename.len - 1] != '/')
534+
strbuf_addch(&filename, '/');
595535
}
596536

597-
if (!oneline)
598-
len += sprintf(filename + len, "%d", nr);
599-
else {
600-
len += sprintf(filename + len, "%04d-", nr);
601-
len += snprintf(filename + len, sizeof(filename) - len - 1
602-
- suffix_len, "%s", oneline);
603-
strcpy(filename + len, fmt_patch_suffix);
604-
}
537+
get_patch_filename(commit, rev->nr, fmt_patch_suffix, &filename);
605538

606539
if (!DIFF_OPT_TST(&rev->diffopt, QUIET))
607-
fprintf(realstdout, "%s\n", filename + outdir_offset);
540+
fprintf(realstdout, "%s\n", filename.buf + outdir_offset);
608541

609-
if (freopen(filename, "w", stdout) == NULL)
610-
return error("Cannot open patch file %s",filename);
542+
if (freopen(filename.buf, "w", stdout) == NULL)
543+
return error("Cannot open patch file %s", filename.buf);
611544

545+
strbuf_release(&filename);
612546
return 0;
613547
}
614548

@@ -678,7 +612,6 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
678612
int nr, struct commit **list, struct commit *head)
679613
{
680614
const char *committer;
681-
char *head_sha1;
682615
const char *subject_start = NULL;
683616
const char *body = "*** SUBJECT HERE ***\n\n*** BLURB HERE ***\n";
684617
const char *msg;
@@ -689,20 +622,40 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
689622
const char *encoding = "utf-8";
690623
struct diff_options opts;
691624
int need_8bit_cte = 0;
625+
struct commit *commit = NULL;
692626

693627
if (rev->commit_format != CMIT_FMT_EMAIL)
694628
die("Cover letter needs email format");
695629

696-
if (!use_stdout && reopen_stdout(numbered_files ?
697-
NULL : "cover-letter", 0, rev))
630+
committer = git_committer_info(0);
631+
632+
if (!numbered_files) {
633+
/*
634+
* We fake a commit for the cover letter so we get the filename
635+
* desired.
636+
*/
637+
commit = xcalloc(1, sizeof(*commit));
638+
commit->buffer = xmalloc(400);
639+
snprintf(commit->buffer, 400,
640+
"tree 0000000000000000000000000000000000000000\n"
641+
"parent %s\n"
642+
"author %s\n"
643+
"committer %s\n\n"
644+
"cover letter\n",
645+
sha1_to_hex(head->object.sha1), committer, committer);
646+
}
647+
648+
if (!use_stdout && reopen_stdout(commit, rev))
698649
return;
699650

700-
head_sha1 = sha1_to_hex(head->object.sha1);
651+
if (commit) {
701652

702-
log_write_email_headers(rev, head_sha1, &subject_start, &extra_headers,
703-
&need_8bit_cte);
653+
free(commit->buffer);
654+
free(commit);
655+
}
704656

705-
committer = git_committer_info(0);
657+
log_write_email_headers(rev, head, &subject_start, &extra_headers,
658+
&need_8bit_cte);
706659

707660
msg = body;
708661
pp_user_info(NULL, CMIT_FMT_EMAIL, &sb, committer, DATE_RFC2822,
@@ -1067,6 +1020,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
10671020
const char *msgid = clean_message_id(in_reply_to);
10681021
string_list_append(msgid, rev.ref_message_ids);
10691022
}
1023+
rev.numbered_files = numbered_files;
1024+
rev.patch_suffix = fmt_patch_suffix;
10701025
if (cover_letter) {
10711026
if (thread)
10721027
gen_message_id(&rev, "cover");
@@ -1115,9 +1070,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
11151070
}
11161071
gen_message_id(&rev, sha1_to_hex(commit->object.sha1));
11171072
}
1118-
if (!use_stdout && reopen_stdout(numbered_files ? NULL :
1119-
get_oneline_for_filename(commit, keep_subject),
1120-
rev.nr, &rev))
1073+
1074+
if (!use_stdout && reopen_stdout(numbered_files ? NULL : commit,
1075+
&rev))
11211076
die("Failed to create output files");
11221077
shown = log_tree_commit(&rev, commit);
11231078
free(commit->buffer);

log-tree.c

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -179,13 +179,31 @@ static int has_non_ascii(const char *s)
179179
return 0;
180180
}
181181

182-
void log_write_email_headers(struct rev_info *opt, const char *name,
182+
void get_patch_filename(struct commit *commit, int nr, const char *suffix,
183+
struct strbuf *buf)
184+
{
185+
int suffix_len = strlen(suffix) + 1;
186+
int start_len = buf->len;
187+
188+
strbuf_addf(buf, commit ? "%04d-" : "%d", nr);
189+
if (commit) {
190+
int max_len = start_len + FORMAT_PATCH_NAME_MAX - suffix_len;
191+
192+
format_commit_message(commit, "%f", buf, DATE_NORMAL);
193+
if (max_len < buf->len)
194+
strbuf_setlen(buf, max_len);
195+
strbuf_addstr(buf, suffix);
196+
}
197+
}
198+
199+
void log_write_email_headers(struct rev_info *opt, struct commit *commit,
183200
const char **subject_p,
184201
const char **extra_headers_p,
185202
int *need_8bit_cte_p)
186203
{
187204
const char *subject = NULL;
188205
const char *extra_headers = opt->extra_headers;
206+
const char *name = sha1_to_hex(commit->object.sha1);
189207

190208
*need_8bit_cte_p = 0; /* unknown */
191209
if (opt->total > 0) {
@@ -224,6 +242,7 @@ void log_write_email_headers(struct rev_info *opt, const char *name,
224242
if (opt->mime_boundary) {
225243
static char subject_buffer[1024];
226244
static char buffer[1024];
245+
struct strbuf filename = STRBUF_INIT;
227246
*need_8bit_cte_p = -1; /* NEVER */
228247
snprintf(subject_buffer, sizeof(subject_buffer) - 1,
229248
"%s"
@@ -242,18 +261,21 @@ void log_write_email_headers(struct rev_info *opt, const char *name,
242261
mime_boundary_leader, opt->mime_boundary);
243262
extra_headers = subject_buffer;
244263

264+
get_patch_filename(opt->numbered_files ? NULL : commit, opt->nr,
265+
opt->patch_suffix, &filename);
245266
snprintf(buffer, sizeof(buffer) - 1,
246267
"\n--%s%s\n"
247268
"Content-Type: text/x-patch;"
248-
" name=\"%s.diff\"\n"
269+
" name=\"%s\"\n"
249270
"Content-Transfer-Encoding: 8bit\n"
250271
"Content-Disposition: %s;"
251-
" filename=\"%s.diff\"\n\n",
272+
" filename=\"%s\"\n\n",
252273
mime_boundary_leader, opt->mime_boundary,
253-
name,
274+
filename.buf,
254275
opt->no_inline ? "attachment" : "inline",
255-
name);
276+
filename.buf);
256277
opt->diffopt.stat_sep = buffer;
278+
strbuf_release(&filename);
257279
}
258280
*subject_p = subject;
259281
*extra_headers_p = extra_headers;
@@ -333,8 +355,7 @@ void show_log(struct rev_info *opt)
333355
*/
334356

335357
if (opt->commit_format == CMIT_FMT_EMAIL) {
336-
log_write_email_headers(opt, sha1_to_hex(commit->object.sha1),
337-
&subject, &extra_headers,
358+
log_write_email_headers(opt, commit, &subject, &extra_headers,
338359
&need_8bit_cte);
339360
} else if (opt->commit_format != CMIT_FMT_USERFORMAT) {
340361
fputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), stdout);

log-tree.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,14 @@ int log_tree_commit(struct rev_info *, struct commit *);
1313
int log_tree_opt_parse(struct rev_info *, const char **, int);
1414
void show_log(struct rev_info *opt);
1515
void show_decorations(struct rev_info *opt, struct commit *commit);
16-
void log_write_email_headers(struct rev_info *opt, const char *name,
16+
void log_write_email_headers(struct rev_info *opt, struct commit *commit,
1717
const char **subject_p,
1818
const char **extra_headers_p,
1919
int *need_8bit_cte_p);
2020
void load_ref_decorations(void);
2121

22+
#define FORMAT_PATCH_NAME_MAX 64
23+
void get_patch_filename(struct commit *commit, int nr, const char *suffix,
24+
struct strbuf *buf);
25+
2226
#endif

pretty.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,40 @@ static void parse_commit_header(struct format_commit_context *context)
493493
context->commit_header_parsed = 1;
494494
}
495495

496+
static int istitlechar(char c)
497+
{
498+
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
499+
(c >= '0' && c <= '9') || c == '.' || c == '_';
500+
}
501+
502+
static void format_sanitized_subject(struct strbuf *sb, const char *msg)
503+
{
504+
size_t trimlen;
505+
size_t start_len = sb->len;
506+
int space = 2;
507+
508+
for (; *msg && *msg != '\n'; msg++) {
509+
if (istitlechar(*msg)) {
510+
if (space == 1)
511+
strbuf_addch(sb, '-');
512+
space = 0;
513+
strbuf_addch(sb, *msg);
514+
if (*msg == '.')
515+
while (*(msg+1) == '.')
516+
msg++;
517+
} else
518+
space |= 1;
519+
}
520+
521+
/* trim any trailing '.' or '-' characters */
522+
trimlen = 0;
523+
while (sb->len - trimlen > start_len &&
524+
(sb->buf[sb->len - 1 - trimlen] == '.'
525+
|| sb->buf[sb->len - 1 - trimlen] == '-'))
526+
trimlen++;
527+
strbuf_remove(sb, sb->len - trimlen, trimlen);
528+
}
529+
496530
const char *format_subject(struct strbuf *sb, const char *msg,
497531
const char *line_separator)
498532
{
@@ -683,6 +717,9 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder,
683717
case 's': /* subject */
684718
format_subject(sb, msg + c->subject_off, " ");
685719
return 1;
720+
case 'f': /* sanitized subject */
721+
format_sanitized_subject(sb, msg + c->subject_off);
722+
return 1;
686723
case 'b': /* body */
687724
strbuf_addstr(sb, msg + c->body_off);
688725
return 1;

revision.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ struct rev_info {
8585
struct log_info *loginfo;
8686
int nr, total;
8787
const char *mime_boundary;
88+
const char *patch_suffix;
89+
int numbered_files;
8890
char *message_id;
8991
struct string_list *ref_message_ids;
9092
const char *add_signoff;

t/t4013-diff-various.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,11 +246,12 @@ format-patch --stdout initial..master
246246
format-patch --stdout --no-numbered initial..master
247247
format-patch --stdout --numbered initial..master
248248
format-patch --attach --stdout initial..side
249+
format-patch --attach --stdout --suffix=.diff initial..side
249250
format-patch --attach --stdout initial..master^
250251
format-patch --attach --stdout initial..master
251252
format-patch --inline --stdout initial..side
252253
format-patch --inline --stdout initial..master^
253-
format-patch --inline --stdout initial..master
254+
format-patch --inline --stdout --numbered-files initial..master
254255
format-patch --inline --stdout initial..master
255256
format-patch --inline --stdout --subject-prefix=TESTCASE initial..master
256257
config format.subjectprefix DIFFERENT_PREFIX

0 commit comments

Comments
 (0)