Skip to content

Commit 3baf58b

Browse files
committed
format-patch: make output filename configurable
For the past 15 years, we've used the hardcoded 64 as the length limit of the filename of the output from the "git format-patch" command. Since the value is shorter than the 80-column terminal, it could grow without line wrapping a bit. At the same time, since the value is longer than half of the 80-column terminal, we could fit two or more of them in "ls" output on such a terminal if we allowed to lower it. Introduce a new command line option --filename-max-length=<n> and a new configuration variable format.filenameMaxLength to override the hardcoded default. While we are at it, remove a check that the name of output directory does not exceed PATH_MAX---this check is pointless in that by the time control reaches the function, the caller would already have done an equivalent of "mkdir -p", so if the system does not like an overly long directory name, the control wouldn't have reached here, and otherwise, we know that the system allowed the output directory to exist. In the worst case, we will get an error when we try to open the output file and handle the error correctly anyway. Signed-off-by: Junio C Hamano <[email protected]>
1 parent 898f807 commit 3baf58b

File tree

7 files changed

+83
-8
lines changed

7 files changed

+83
-8
lines changed

Documentation/config/format.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ format.outputDirectory::
9494
Set a custom directory to store the resulting files instead of the
9595
current working directory. All directory components will be created.
9696

97+
format.filenameMaxLength::
98+
The maximum length of the output filenames generated by the
99+
`format-patch` command; defaults to 64. Can be overridden
100+
by the `--filename-max-length=<n>` command line option.
101+
97102
format.useAutoBase::
98103
A boolean value which lets you enable the `--base=auto` option of
99104
format-patch by default. Can also be set to "whenAble" to allow

Documentation/git-format-patch.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ SYNOPSIS
2828
[--no-notes | --notes[=<ref>]]
2929
[--interdiff=<previous>]
3030
[--range-diff=<previous> [--creation-factor=<percent>]]
31+
[--filename-max-length=<n>]
3132
[--progress]
3233
[<common diff options>]
3334
[ <since> | <revision range> ]
@@ -200,6 +201,13 @@ populated with placeholder text.
200201
allows for useful naming of a patch series, and can be
201202
combined with the `--numbered` option.
202203

204+
--filename-max-length=<n>::
205+
Instead of the standard 64 bytes, chomp the generated output
206+
filenames at around '<n>' bytes (too short a value will be
207+
silently raised to a reasonable length). Defaults to the
208+
value of the `format.filenameMaxLength` configuration
209+
variable, or 64 if unconfigured.
210+
203211
--rfc::
204212
Alias for `--subject-prefix="RFC PATCH"`. RFC means "Request For
205213
Comments"; use this when sending an experimental patch for

builtin/log.c

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737

3838
#define MAIL_DEFAULT_WRAP 72
3939
#define COVER_FROM_AUTO_MAX_SUBJECT_LEN 100
40+
#define FORMAT_PATCH_NAME_MAX_DEFAULT 64
4041

4142
/* Set a default date-time format for git log ("log.date" config variable) */
4243
static const char *default_date_mode = NULL;
@@ -50,6 +51,7 @@ static int decoration_style;
5051
static int decoration_given;
5152
static int use_mailmap_config = 1;
5253
static const char *fmt_patch_subject_prefix = "PATCH";
54+
static int fmt_patch_name_max = FORMAT_PATCH_NAME_MAX_DEFAULT;
5355
static const char *fmt_pretty;
5456

5557
static const char * const builtin_log_usage[] = {
@@ -150,6 +152,7 @@ static void cmd_log_init_defaults(struct rev_info *rev)
150152
rev->abbrev_commit = default_abbrev_commit;
151153
rev->show_root_diff = default_show_root;
152154
rev->subject_prefix = fmt_patch_subject_prefix;
155+
rev->patch_name_max = fmt_patch_name_max;
153156
rev->show_signature = default_show_signature;
154157
rev->encode_email_headers = default_encode_email_headers;
155158
rev->diffopt.flags.allow_textconv = 1;
@@ -454,6 +457,10 @@ static int git_log_config(const char *var, const char *value, void *cb)
454457
return git_config_string(&fmt_pretty, var, value);
455458
if (!strcmp(var, "format.subjectprefix"))
456459
return git_config_string(&fmt_patch_subject_prefix, var, value);
460+
if (!strcmp(var, "format.filenamemaxlength")) {
461+
fmt_patch_name_max = git_config_int(var, value);
462+
return 0;
463+
}
457464
if (!strcmp(var, "format.encodeemailheaders")) {
458465
default_encode_email_headers = git_config_bool(var, value);
459466
return 0;
@@ -955,15 +962,9 @@ static int open_next_file(struct commit *commit, const char *subject,
955962
struct rev_info *rev, int quiet)
956963
{
957964
struct strbuf filename = STRBUF_INIT;
958-
int suffix_len = strlen(rev->patch_suffix) + 1;
959965

960966
if (output_directory) {
961967
strbuf_addstr(&filename, output_directory);
962-
if (filename.len >=
963-
PATH_MAX - FORMAT_PATCH_NAME_MAX - suffix_len) {
964-
strbuf_release(&filename);
965-
return error(_("name of output directory is too long"));
966-
}
967968
strbuf_complete(&filename, '/');
968969
}
969970

@@ -1751,6 +1752,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
17511752
N_("start numbering patches at <n> instead of 1")),
17521753
OPT_INTEGER('v', "reroll-count", &reroll_count,
17531754
N_("mark the series as Nth re-roll")),
1755+
OPT_INTEGER(0, "filename-max-length", &fmt_patch_name_max,
1756+
N_("max length of output filename")),
17541757
OPT_CALLBACK_F(0, "rfc", &rev, NULL,
17551758
N_("Use [RFC PATCH] instead of [PATCH]"),
17561759
PARSE_OPT_NOARG | PARSE_OPT_NONEG, rfc_callback),
@@ -1851,6 +1854,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
18511854
PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN |
18521855
PARSE_OPT_KEEP_DASHDASH);
18531856

1857+
/* Make sure "0000-$sub.patch" gives non-negative length for $sub */
1858+
if (fmt_patch_name_max <= strlen("0000-") + strlen(fmt_patch_suffix))
1859+
fmt_patch_name_max = strlen("0000-") + strlen(fmt_patch_suffix);
1860+
18541861
if (cover_from_description_arg)
18551862
cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
18561863

@@ -1935,6 +1942,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
19351942
rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
19361943

19371944
rev.zero_commit = zero_commit;
1945+
rev.patch_name_max = fmt_patch_name_max;
19381946

19391947
if (!rev.diffopt.flags.text && !no_binary_diff)
19401948
rev.diffopt.flags.binary = 1;

log-tree.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ void fmt_output_subject(struct strbuf *filename,
367367
const char *suffix = info->patch_suffix;
368368
int nr = info->nr;
369369
int start_len = filename->len;
370-
int max_len = start_len + FORMAT_PATCH_NAME_MAX - (strlen(suffix) + 1);
370+
int max_len = start_len + info->patch_name_max - (strlen(suffix) + 1);
371371

372372
if (0 < info->reroll_count)
373373
strbuf_addf(filename, "v%d-", info->reroll_count);

log-tree.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit,
3333
int maybe_multipart);
3434
void load_ref_decorations(struct decoration_filter *filter, int flags);
3535

36-
#define FORMAT_PATCH_NAME_MAX 64
3736
void fmt_output_commit(struct strbuf *, struct commit *, struct rev_info *);
3837
void fmt_output_subject(struct strbuf *, const char *subject, struct rev_info *);
3938
void fmt_output_email_subject(struct strbuf *, struct rev_info *);

revision.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ struct rev_info {
238238
const char *extra_headers;
239239
const char *log_reencode;
240240
const char *subject_prefix;
241+
int patch_name_max;
241242
int no_inline;
242243
int show_log_size;
243244
struct string_list *mailmap;

t/t4014-format-patch.sh

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,60 @@ test_expect_success 'multiple files' '
313313
ls patches/0001-Side-changes-1.patch patches/0002-Side-changes-2.patch patches/0003-Side-changes-3-with-n-backslash-n-in-it.patch
314314
'
315315

316+
test_expect_success 'filename length limit' '
317+
test_when_finished "rm -f 000*" &&
318+
rm -rf 000[1-9]-*.patch &&
319+
for len in 15 25 35
320+
do
321+
git format-patch --filename-max-length=$len -3 side &&
322+
max=$(
323+
for patch in 000[1-9]-*.patch
324+
do
325+
echo "$patch" | wc -c
326+
done |
327+
sort -nr |
328+
head -n 1
329+
) &&
330+
test $max -le $len || return 1
331+
done
332+
'
333+
334+
test_expect_success 'filename length limit from config' '
335+
test_when_finished "rm -f 000*" &&
336+
rm -rf 000[1-9]-*.patch &&
337+
for len in 15 25 35
338+
do
339+
git -c format.filenameMaxLength=$len format-patch -3 side &&
340+
max=$(
341+
for patch in 000[1-9]-*.patch
342+
do
343+
echo "$patch" | wc -c
344+
done |
345+
sort -nr |
346+
head -n 1
347+
) &&
348+
test $max -le $len || return 1
349+
done
350+
'
351+
352+
test_expect_success 'filename limit applies only to basename' '
353+
test_when_finished "rm -rf patches/" &&
354+
rm -rf patches/ &&
355+
for len in 15 25 35
356+
do
357+
git format-patch -o patches --filename-max-length=$len -3 side &&
358+
max=$(
359+
for patch in patches/000[1-9]-*.patch
360+
do
361+
echo "${patch#patches/}" | wc -c
362+
done |
363+
sort -nr |
364+
head -n 1
365+
) &&
366+
test $max -le $len || return 1
367+
done
368+
'
369+
316370
test_expect_success 'reroll count' '
317371
rm -fr patches &&
318372
git format-patch -o patches --cover-letter --reroll-count 4 master..side >list &&

0 commit comments

Comments
 (0)