}
/* format-patch */
-#define FORMAT_PATCH_NAME_MAX 64
-
-static int istitlechar(char c)
-{
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
- (c >= '0' && c <= '9') || c == '.' || c == '_';
-}
static const char *fmt_patch_suffix = ".patch";
static int numbered = 0;
#define THREAD_SHALLOW 1
#define THREAD_DEEP 2
static int thread = 0;
+static int do_signoff = 0;
static int git_format_config(const char *var, const char *value, void *cb)
{
thread = git_config_bool(var, value) && THREAD_SHALLOW;
return 0;
}
+ if (!strcmp(var, "format.signoff")) {
+ do_signoff = git_config_bool(var, value);
+ return 0;
+ }
return git_log_config(var, value, cb);
}
-
-static const char *get_oneline_for_filename(struct commit *commit,
- int keep_subject)
-{
- static char filename[PATH_MAX];
- char *sol;
- int len = 0;
- int suffix_len = strlen(fmt_patch_suffix) + 1;
-
- sol = strstr(commit->buffer, "\n\n");
- if (!sol)
- filename[0] = '\0';
- else {
- int j, space = 0;
-
- sol += 2;
- /* strip [PATCH] or [PATCH blabla] */
- if (!keep_subject && !prefixcmp(sol, "[PATCH")) {
- char *eos = strchr(sol + 6, ']');
- if (eos) {
- while (isspace(*eos))
- eos++;
- sol = eos;
- }
- }
-
- for (j = 0;
- j < FORMAT_PATCH_NAME_MAX - suffix_len - 5 &&
- len < sizeof(filename) - suffix_len &&
- sol[j] && sol[j] != '\n';
- j++) {
- if (istitlechar(sol[j])) {
- if (space) {
- filename[len++] = '-';
- space = 0;
- }
- filename[len++] = sol[j];
- if (sol[j] == '.')
- while (sol[j + 1] == '.')
- j++;
- } else
- space = 1;
- }
- while (filename[len - 1] == '.'
- || filename[len - 1] == '-')
- len--;
- filename[len] = '\0';
- }
- return filename;
-}
-
static FILE *realstdout = NULL;
static const char *output_directory = NULL;
static int outdir_offset;
-static int reopen_stdout(const char *oneline, int nr, struct rev_info *rev)
+static int reopen_stdout(struct commit *commit, struct rev_info *rev)
{
- char filename[PATH_MAX];
- int len = 0;
+ struct strbuf filename = STRBUF_INIT;
int suffix_len = strlen(fmt_patch_suffix) + 1;
if (output_directory) {
- len = snprintf(filename, sizeof(filename), "%s",
- output_directory);
- if (len >=
- sizeof(filename) - FORMAT_PATCH_NAME_MAX - suffix_len)
+ strbuf_addstr(&filename, output_directory);
+ if (filename.len >=
+ PATH_MAX - FORMAT_PATCH_NAME_MAX - suffix_len)
return error("name of output directory is too long");
- if (filename[len - 1] != '/')
- filename[len++] = '/';
+ if (filename.buf[filename.len - 1] != '/')
+ strbuf_addch(&filename, '/');
}
- if (!oneline)
- len += sprintf(filename + len, "%d", nr);
- else {
- len += sprintf(filename + len, "%04d-", nr);
- len += snprintf(filename + len, sizeof(filename) - len - 1
- - suffix_len, "%s", oneline);
- strcpy(filename + len, fmt_patch_suffix);
- }
+ get_patch_filename(commit, rev->nr, fmt_patch_suffix, &filename);
if (!DIFF_OPT_TST(&rev->diffopt, QUIET))
- fprintf(realstdout, "%s\n", filename + outdir_offset);
+ fprintf(realstdout, "%s\n", filename.buf + outdir_offset);
- if (freopen(filename, "w", stdout) == NULL)
- return error("Cannot open patch file %s",filename);
+ if (freopen(filename.buf, "w", stdout) == NULL)
+ return error("Cannot open patch file %s", filename.buf);
+ strbuf_release(&filename);
return 0;
}
int nr, struct commit **list, struct commit *head)
{
const char *committer;
- char *head_sha1;
const char *subject_start = NULL;
const char *body = "*** SUBJECT HERE ***\n\n*** BLURB HERE ***\n";
const char *msg;
const char *encoding = "utf-8";
struct diff_options opts;
int need_8bit_cte = 0;
+ struct commit *commit = NULL;
if (rev->commit_format != CMIT_FMT_EMAIL)
die("Cover letter needs email format");
- if (!use_stdout && reopen_stdout(numbered_files ?
- NULL : "cover-letter", 0, rev))
+ committer = git_committer_info(0);
+
+ if (!numbered_files) {
+ /*
+ * We fake a commit for the cover letter so we get the filename
+ * desired.
+ */
+ commit = xcalloc(1, sizeof(*commit));
+ commit->buffer = xmalloc(400);
+ snprintf(commit->buffer, 400,
+ "tree 0000000000000000000000000000000000000000\n"
+ "parent %s\n"
+ "author %s\n"
+ "committer %s\n\n"
+ "cover letter\n",
+ sha1_to_hex(head->object.sha1), committer, committer);
+ }
+
+ if (!use_stdout && reopen_stdout(commit, rev))
return;
- head_sha1 = sha1_to_hex(head->object.sha1);
+ if (commit) {
- log_write_email_headers(rev, head_sha1, &subject_start, &extra_headers,
- &need_8bit_cte);
+ free(commit->buffer);
+ free(commit);
+ }
- committer = git_committer_info(0);
+ log_write_email_headers(rev, head, &subject_start, &extra_headers,
+ &need_8bit_cte);
msg = body;
pp_user_info(NULL, CMIT_FMT_EMAIL, &sb, committer, DATE_RFC2822,
int cover_letter = 0;
int boundary_count = 0;
int no_binary_diff = 0;
+ int numbered_cmdline_opt = 0;
struct commit *origin = NULL, *head = NULL;
const char *in_reply_to = NULL;
struct patch_ids ids;
if (!strcmp(argv[i], "--stdout"))
use_stdout = 1;
else if (!strcmp(argv[i], "-n") ||
- !strcmp(argv[i], "--numbered"))
+ !strcmp(argv[i], "--numbered")) {
numbered = 1;
+ numbered_cmdline_opt = 1;
+ }
else if (!strcmp(argv[i], "-N") ||
!strcmp(argv[i], "--no-numbered")) {
numbered = 0;
}
else if (!strcmp(argv[i], "--signoff") ||
!strcmp(argv[i], "-s")) {
- const char *committer;
- const char *endpos;
- committer = git_committer_info(IDENT_ERROR_ON_NO_NAME);
- endpos = strchr(committer, '>');
- if (!endpos)
- die("bogus committer info %s", committer);
- add_signoff = xmemdupz(committer, endpos - committer + 1);
+ do_signoff = 1;
}
else if (!strcmp(argv[i], "--attach")) {
rev.mime_boundary = git_version_string;
}
argc = j;
+ if (do_signoff) {
+ const char *committer;
+ const char *endpos;
+ committer = git_committer_info(IDENT_ERROR_ON_NO_NAME);
+ endpos = strchr(committer, '>');
+ if (!endpos)
+ die("bogus committer info %s", committer);
+ add_signoff = xmemdupz(committer, endpos - committer + 1);
+ }
+
for (i = 0; i < extra_hdr_nr; i++) {
strbuf_addstr(&buf, extra_hdr[i]);
strbuf_addch(&buf, '\n');
if (start_number < 0)
start_number = 1;
+
+ /*
+ * If numbered is set solely due to format.numbered in config,
+ * and it would conflict with --keep-subject (-k) from the
+ * command line, reset "numbered".
+ */
+ if (numbered && keep_subject && !numbered_cmdline_opt)
+ numbered = 0;
+
if (numbered && keep_subject)
die ("-n and -k are mutually exclusive.");
if (keep_subject && subject_prefix)
const char *msgid = clean_message_id(in_reply_to);
string_list_append(msgid, rev.ref_message_ids);
}
+ rev.numbered_files = numbered_files;
+ rev.patch_suffix = fmt_patch_suffix;
if (cover_letter) {
if (thread)
gen_message_id(&rev, "cover");
}
gen_message_id(&rev, sha1_to_hex(commit->object.sha1));
}
- if (!use_stdout && reopen_stdout(numbered_files ? NULL :
- get_oneline_for_filename(commit, keep_subject),
- rev.nr, &rev))
+
+ if (!use_stdout && reopen_stdout(numbered_files ? NULL : commit,
+ &rev))
die("Failed to create output files");
shown = log_tree_commit(&rev, commit);
free(commit->buffer);