Merge branch 'db/cover-letter'
* db/cover-letter: Improve collection of information for format-patch --cover-letter Add API access to shortlog t4014: Replace sed's non-standard 'Q' by standard 'q' Support a --cc=<email> option in format-patch Combine To: and Cc: headers Fix format.headers not ending with a newline Add tests for extra headers in format-patch Add a --cover-letter option to format-patch Export some email and pretty-printing functions Improve message-id generation flow control for format-patch Add more tests for format-patch Conflicts: builtin-log.c builtin-shortlog.c pretty.c
This commit is contained in:
commit
992221d05e
@ -10,13 +10,15 @@ SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git-format-patch' [-k] [-o <dir> | --stdout] [--thread]
|
||||
[--attach[=<boundary>] | --inline[=<boundary>]]
|
||||
[-s | --signoff] [<common diff options>]
|
||||
[-n | --numbered | -N | --no-numbered]
|
||||
[--start-number <n>] [--numbered-files]
|
||||
[--in-reply-to=Message-Id] [--suffix=.<sfx>]
|
||||
[--ignore-if-in-upstream]
|
||||
[--subject-prefix=Subject-Prefix]
|
||||
[--attach[=<boundary>] | --inline[=<boundary>]]
|
||||
[-s | --signoff] [<common diff options>]
|
||||
[-n | --numbered | -N | --no-numbered]
|
||||
[--start-number <n>] [--numbered-files]
|
||||
[--in-reply-to=Message-Id] [--suffix=.<sfx>]
|
||||
[--ignore-if-in-upstream]
|
||||
[--subject-prefix=Subject-Prefix]
|
||||
[--cc=<email>]
|
||||
[--cover-letter]
|
||||
[ <since> | <revision range> ]
|
||||
|
||||
DESCRIPTION
|
||||
@ -135,6 +137,15 @@ include::diff-options.txt[]
|
||||
allows for useful naming of a patch series, and can be
|
||||
combined with the --numbered option.
|
||||
|
||||
--cc=<email>::
|
||||
Add a "Cc:" header to the email headers. This is in addition
|
||||
to any configured headers, and may be used multiple times.
|
||||
|
||||
--cover-letter::
|
||||
Generate a cover letter template. You still have to fill in
|
||||
a description, but the shortlog and the diffstat will be
|
||||
generated for you.
|
||||
|
||||
--suffix=.<sfx>::
|
||||
Instead of using `.patch` as the suffix for generated
|
||||
filenames, use specified suffix. A common alternative is
|
||||
|
348
builtin-log.c
348
builtin-log.c
@ -15,6 +15,8 @@
|
||||
#include "reflog-walk.h"
|
||||
#include "patch-ids.h"
|
||||
#include "refs.h"
|
||||
#include "run-command.h"
|
||||
#include "shortlog.h"
|
||||
|
||||
static int default_show_root = 1;
|
||||
static const char *fmt_patch_subject_prefix = "PATCH";
|
||||
@ -428,24 +430,47 @@ static int istitlechar(char c)
|
||||
(c >= '0' && c <= '9') || c == '.' || c == '_';
|
||||
}
|
||||
|
||||
static char *extra_headers = NULL;
|
||||
static int extra_headers_size = 0;
|
||||
static const char *fmt_patch_suffix = ".patch";
|
||||
static int numbered = 0;
|
||||
static int auto_number = 0;
|
||||
|
||||
static char **extra_hdr;
|
||||
static int extra_hdr_nr;
|
||||
static int extra_hdr_alloc;
|
||||
|
||||
static char **extra_to;
|
||||
static int extra_to_nr;
|
||||
static int extra_to_alloc;
|
||||
|
||||
static char **extra_cc;
|
||||
static int extra_cc_nr;
|
||||
static int extra_cc_alloc;
|
||||
|
||||
static void add_header(const char *value)
|
||||
{
|
||||
int len = strlen(value);
|
||||
while (value[len - 1] == '\n')
|
||||
len--;
|
||||
if (!strncasecmp(value, "to: ", 4)) {
|
||||
ALLOC_GROW(extra_to, extra_to_nr + 1, extra_to_alloc);
|
||||
extra_to[extra_to_nr++] = xstrndup(value + 4, len - 4);
|
||||
return;
|
||||
}
|
||||
if (!strncasecmp(value, "cc: ", 4)) {
|
||||
ALLOC_GROW(extra_cc, extra_cc_nr + 1, extra_cc_alloc);
|
||||
extra_cc[extra_cc_nr++] = xstrndup(value + 4, len - 4);
|
||||
return;
|
||||
}
|
||||
ALLOC_GROW(extra_hdr, extra_hdr_nr + 1, extra_hdr_alloc);
|
||||
extra_hdr[extra_hdr_nr++] = xstrndup(value, len);
|
||||
}
|
||||
|
||||
static int git_format_config(const char *var, const char *value)
|
||||
{
|
||||
if (!strcmp(var, "format.headers")) {
|
||||
int len;
|
||||
|
||||
if (!value)
|
||||
die("format.headers without value");
|
||||
len = strlen(value);
|
||||
extra_headers_size += len + 1;
|
||||
extra_headers = xrealloc(extra_headers, extra_headers_size);
|
||||
extra_headers[extra_headers_size - len - 1] = 0;
|
||||
strcat(extra_headers, value);
|
||||
add_header(value);
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(var, "format.suffix")) {
|
||||
@ -470,74 +495,81 @@ static int git_format_config(const char *var, const char *value)
|
||||
}
|
||||
|
||||
|
||||
static FILE *realstdout = NULL;
|
||||
static const char *output_directory = NULL;
|
||||
|
||||
static int reopen_stdout(struct commit *commit, int nr, int keep_subject,
|
||||
int numbered_files)
|
||||
static const char *get_oneline_for_filename(struct commit *commit,
|
||||
int keep_subject)
|
||||
{
|
||||
char filename[PATH_MAX];
|
||||
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 reopen_stdout(const char *oneline, int nr, int total)
|
||||
{
|
||||
char filename[PATH_MAX];
|
||||
int len = 0;
|
||||
int suffix_len = strlen(fmt_patch_suffix) + 1;
|
||||
|
||||
if (output_directory) {
|
||||
if (strlen(output_directory) >=
|
||||
len = snprintf(filename, sizeof(filename), "%s",
|
||||
output_directory);
|
||||
if (len >=
|
||||
sizeof(filename) - FORMAT_PATCH_NAME_MAX - suffix_len)
|
||||
return error("name of output directory is too long");
|
||||
strlcpy(filename, output_directory, sizeof(filename) - suffix_len);
|
||||
len = strlen(filename);
|
||||
if (filename[len - 1] != '/')
|
||||
filename[len++] = '/';
|
||||
}
|
||||
|
||||
if (numbered_files) {
|
||||
sprintf(filename + len, "%d", nr);
|
||||
len = strlen(filename);
|
||||
|
||||
} else {
|
||||
sprintf(filename + len, "%04d", nr);
|
||||
len = strlen(filename);
|
||||
|
||||
sol = strstr(commit->buffer, "\n\n");
|
||||
if (sol) {
|
||||
int j, space = 1;
|
||||
|
||||
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;
|
||||
}
|
||||
if (len + suffix_len >= sizeof(filename))
|
||||
return error("Patch pathname too long");
|
||||
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);
|
||||
}
|
||||
|
||||
@ -594,16 +626,89 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const cha
|
||||
o2->flags = flags2;
|
||||
}
|
||||
|
||||
static void gen_message_id(char *dest, unsigned int length, char *base)
|
||||
static void gen_message_id(struct rev_info *info, char *base)
|
||||
{
|
||||
const char *committer = git_committer_info(IDENT_WARN_ON_NO_NAME);
|
||||
const char *email_start = strrchr(committer, '<');
|
||||
const char *email_end = strrchr(committer, '>');
|
||||
if(!email_start || !email_end || email_start > email_end - 1)
|
||||
struct strbuf buf;
|
||||
if (!email_start || !email_end || email_start > email_end - 1)
|
||||
die("Could not extract email from committer identity.");
|
||||
snprintf(dest, length, "%s.%lu.git.%.*s", base,
|
||||
(unsigned long) time(NULL),
|
||||
(int)(email_end - email_start - 1), email_start + 1);
|
||||
strbuf_init(&buf, 0);
|
||||
strbuf_addf(&buf, "%s.%lu.git.%.*s", base,
|
||||
(unsigned long) time(NULL),
|
||||
(int)(email_end - email_start - 1), email_start + 1);
|
||||
info->message_id = strbuf_detach(&buf, NULL);
|
||||
}
|
||||
|
||||
static void make_cover_letter(struct rev_info *rev, int use_stdout,
|
||||
int numbered, int numbered_files,
|
||||
struct commit *origin,
|
||||
int nr, struct commit **list, struct commit *head)
|
||||
{
|
||||
const char *committer;
|
||||
const char *origin_sha1, *head_sha1;
|
||||
const char *argv[7];
|
||||
const char *subject_start = NULL;
|
||||
const char *body = "*** SUBJECT HERE ***\n\n*** BLURB HERE ***\n";
|
||||
const char *msg;
|
||||
const char *extra_headers = rev->extra_headers;
|
||||
struct shortlog log;
|
||||
struct strbuf sb;
|
||||
int i;
|
||||
const char *encoding = "utf-8";
|
||||
|
||||
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->total))
|
||||
return;
|
||||
|
||||
head_sha1 = sha1_to_hex(head->object.sha1);
|
||||
|
||||
log_write_email_headers(rev, head_sha1, &subject_start, &extra_headers);
|
||||
|
||||
committer = git_committer_info(0);
|
||||
|
||||
msg = body;
|
||||
strbuf_init(&sb, 0);
|
||||
pp_user_info(NULL, CMIT_FMT_EMAIL, &sb, committer, DATE_RFC2822,
|
||||
encoding);
|
||||
pp_title_line(CMIT_FMT_EMAIL, &msg, &sb, subject_start, extra_headers,
|
||||
encoding, 0);
|
||||
pp_remainder(CMIT_FMT_EMAIL, &msg, &sb, 0);
|
||||
printf("%s\n", sb.buf);
|
||||
|
||||
strbuf_release(&sb);
|
||||
|
||||
shortlog_init(&log);
|
||||
for (i = 0; i < nr; i++)
|
||||
shortlog_add_commit(&log, list[i]);
|
||||
|
||||
shortlog_output(&log);
|
||||
|
||||
/*
|
||||
* We can only do diffstat with a unique reference point
|
||||
*/
|
||||
if (!origin)
|
||||
return;
|
||||
|
||||
origin_sha1 = sha1_to_hex(origin->object.sha1);
|
||||
|
||||
argv[0] = "diff";
|
||||
argv[1] = "--stat";
|
||||
argv[2] = "--summary";
|
||||
argv[3] = head_sha1;
|
||||
argv[4] = "--not";
|
||||
argv[5] = origin_sha1;
|
||||
argv[6] = "--";
|
||||
argv[7] = NULL;
|
||||
fflush(stdout);
|
||||
run_command_v_opt(argv, RUN_GIT_CMD);
|
||||
|
||||
fflush(stdout);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static const char *clean_message_id(const char *msg_id)
|
||||
@ -641,11 +746,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
int subject_prefix = 0;
|
||||
int ignore_if_in_upstream = 0;
|
||||
int thread = 0;
|
||||
int cover_letter = 0;
|
||||
int boundary_count = 0;
|
||||
struct commit *origin = NULL, *head = NULL;
|
||||
const char *in_reply_to = NULL;
|
||||
struct patch_ids ids;
|
||||
char *add_signoff = NULL;
|
||||
char message_id[1024];
|
||||
char ref_message_id[1024];
|
||||
struct strbuf buf;
|
||||
|
||||
git_config(git_format_config);
|
||||
init_revisions(&rev, prefix);
|
||||
@ -658,7 +765,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
|
||||
|
||||
rev.subject_prefix = fmt_patch_subject_prefix;
|
||||
rev.extra_headers = extra_headers;
|
||||
|
||||
/*
|
||||
* Parse the arguments before setup_revisions(), or something
|
||||
@ -686,6 +792,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
die("Need a number for --start-number");
|
||||
start_number = strtol(argv[i], NULL, 10);
|
||||
}
|
||||
else if (!prefixcmp(argv[i], "--cc=")) {
|
||||
ALLOC_GROW(extra_cc, extra_cc_nr + 1, extra_cc_alloc);
|
||||
extra_cc[extra_cc_nr++] = xstrdup(argv[i] + 5);
|
||||
}
|
||||
else if (!strcmp(argv[i], "-k") ||
|
||||
!strcmp(argv[i], "--keep-subject")) {
|
||||
keep_subject = 1;
|
||||
@ -742,11 +852,44 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
rev.subject_prefix = argv[i] + 17;
|
||||
} else if (!prefixcmp(argv[i], "--suffix="))
|
||||
fmt_patch_suffix = argv[i] + 9;
|
||||
else if (!strcmp(argv[i], "--cover-letter"))
|
||||
cover_letter = 1;
|
||||
else
|
||||
argv[j++] = argv[i];
|
||||
}
|
||||
argc = j;
|
||||
|
||||
strbuf_init(&buf, 0);
|
||||
|
||||
for (i = 0; i < extra_hdr_nr; i++) {
|
||||
strbuf_addstr(&buf, extra_hdr[i]);
|
||||
strbuf_addch(&buf, '\n');
|
||||
}
|
||||
|
||||
if (extra_to_nr)
|
||||
strbuf_addstr(&buf, "To: ");
|
||||
for (i = 0; i < extra_to_nr; i++) {
|
||||
if (i)
|
||||
strbuf_addstr(&buf, " ");
|
||||
strbuf_addstr(&buf, extra_to[i]);
|
||||
if (i + 1 < extra_to_nr)
|
||||
strbuf_addch(&buf, ',');
|
||||
strbuf_addch(&buf, '\n');
|
||||
}
|
||||
|
||||
if (extra_cc_nr)
|
||||
strbuf_addstr(&buf, "Cc: ");
|
||||
for (i = 0; i < extra_cc_nr; i++) {
|
||||
if (i)
|
||||
strbuf_addstr(&buf, " ");
|
||||
strbuf_addstr(&buf, extra_cc[i]);
|
||||
if (i + 1 < extra_cc_nr)
|
||||
strbuf_addch(&buf, ',');
|
||||
strbuf_addch(&buf, '\n');
|
||||
}
|
||||
|
||||
rev.extra_headers = strbuf_detach(&buf, 0);
|
||||
|
||||
if (start_number < 0)
|
||||
start_number = 1;
|
||||
if (numbered && keep_subject)
|
||||
@ -793,6 +936,18 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
* get_revision() to do the usual traversal.
|
||||
*/
|
||||
}
|
||||
if (cover_letter) {
|
||||
/* remember the range */
|
||||
int i;
|
||||
for (i = 0; i < rev.pending.nr; i++) {
|
||||
struct object *o = rev.pending.objects[i].item;
|
||||
if (!(o->flags & UNINTERESTING))
|
||||
head = (struct commit *)o;
|
||||
}
|
||||
/* We can't generate a cover letter without any patches */
|
||||
if (!head)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ignore_if_in_upstream)
|
||||
get_patch_ids(&rev, &ids, prefix);
|
||||
@ -802,7 +957,15 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
|
||||
if (prepare_revision_walk(&rev))
|
||||
die("revision walk setup failed");
|
||||
rev.boundary = 1;
|
||||
while ((commit = get_revision(&rev)) != NULL) {
|
||||
if (commit->object.flags & BOUNDARY) {
|
||||
fprintf(stderr, "Boundary %s\n", sha1_to_hex(commit->object.sha1));
|
||||
boundary_count++;
|
||||
origin = (boundary_count == 1) ? commit : NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* ignore merges */
|
||||
if (commit->parents && commit->parents->next)
|
||||
continue;
|
||||
@ -820,29 +983,42 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
numbered = 1;
|
||||
if (numbered)
|
||||
rev.total = total + start_number - 1;
|
||||
rev.add_signoff = add_signoff;
|
||||
if (in_reply_to)
|
||||
rev.ref_message_id = clean_message_id(in_reply_to);
|
||||
if (cover_letter) {
|
||||
if (thread)
|
||||
gen_message_id(&rev, "cover");
|
||||
make_cover_letter(&rev, use_stdout, numbered, numbered_files,
|
||||
origin, nr, list, head);
|
||||
total++;
|
||||
start_number--;
|
||||
}
|
||||
rev.add_signoff = add_signoff;
|
||||
while (0 <= --nr) {
|
||||
int shown;
|
||||
commit = list[nr];
|
||||
rev.nr = total - nr + (start_number - 1);
|
||||
/* Make the second and subsequent mails replies to the first */
|
||||
if (thread) {
|
||||
if (nr == (total - 2)) {
|
||||
strncpy(ref_message_id, message_id,
|
||||
sizeof(ref_message_id));
|
||||
ref_message_id[sizeof(ref_message_id)-1]='\0';
|
||||
rev.ref_message_id = ref_message_id;
|
||||
/* Have we already had a message ID? */
|
||||
if (rev.message_id) {
|
||||
/*
|
||||
* If we've got the ID to be a reply
|
||||
* to, discard the current ID;
|
||||
* otherwise, make everything a reply
|
||||
* to that.
|
||||
*/
|
||||
if (rev.ref_message_id)
|
||||
free(rev.message_id);
|
||||
else
|
||||
rev.ref_message_id = rev.message_id;
|
||||
}
|
||||
gen_message_id(message_id, sizeof(message_id),
|
||||
sha1_to_hex(commit->object.sha1));
|
||||
rev.message_id = message_id;
|
||||
gen_message_id(&rev, sha1_to_hex(commit->object.sha1));
|
||||
}
|
||||
if (!use_stdout)
|
||||
if (reopen_stdout(commit, rev.nr, keep_subject,
|
||||
numbered_files))
|
||||
die("Failed to create output files");
|
||||
if (!use_stdout && reopen_stdout(numbered_files ? NULL :
|
||||
get_oneline_for_filename(commit, keep_subject),
|
||||
rev.nr, rev.total))
|
||||
die("Failed to create output files");
|
||||
shown = log_tree_commit(&rev, commit);
|
||||
free(commit->buffer);
|
||||
commit->buffer = NULL;
|
||||
|
@ -6,13 +6,11 @@
|
||||
#include "revision.h"
|
||||
#include "utf8.h"
|
||||
#include "mailmap.h"
|
||||
#include "shortlog.h"
|
||||
|
||||
static const char shortlog_usage[] =
|
||||
"git-shortlog [-n] [-s] [-e] [<commit-id>... ]";
|
||||
|
||||
static char *common_repo_prefix;
|
||||
static int email;
|
||||
|
||||
static int compare_by_number(const void *a1, const void *a2)
|
||||
{
|
||||
const struct path_list_item *i1 = a1, *i2 = a2;
|
||||
@ -26,13 +24,11 @@ static int compare_by_number(const void *a1, const void *a2)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static struct path_list mailmap = {NULL, 0, 0, 0};
|
||||
|
||||
static void insert_one_record(struct path_list *list,
|
||||
static void insert_one_record(struct shortlog *log,
|
||||
const char *author,
|
||||
const char *oneline)
|
||||
{
|
||||
const char *dot3 = common_repo_prefix;
|
||||
const char *dot3 = log->common_repo_prefix;
|
||||
char *buffer, *p;
|
||||
struct path_list_item *item;
|
||||
struct path_list *onelines;
|
||||
@ -47,7 +43,7 @@ static void insert_one_record(struct path_list *list,
|
||||
eoemail = strchr(boemail, '>');
|
||||
if (!eoemail)
|
||||
return;
|
||||
if (!map_email(&mailmap, boemail+1, namebuf, sizeof(namebuf))) {
|
||||
if (!map_email(&log->mailmap, boemail+1, namebuf, sizeof(namebuf))) {
|
||||
while (author < boemail && isspace(*author))
|
||||
author++;
|
||||
for (len = 0;
|
||||
@ -61,14 +57,14 @@ static void insert_one_record(struct path_list *list,
|
||||
else
|
||||
len = strlen(namebuf);
|
||||
|
||||
if (email) {
|
||||
if (log->email) {
|
||||
size_t room = sizeof(namebuf) - len - 1;
|
||||
int maillen = eoemail - boemail + 1;
|
||||
snprintf(namebuf + len, room, " %.*s", maillen, boemail);
|
||||
}
|
||||
|
||||
buffer = xstrdup(namebuf);
|
||||
item = path_list_insert(buffer, list);
|
||||
item = path_list_insert(buffer, &log->list);
|
||||
if (item->util == NULL)
|
||||
item->util = xcalloc(1, sizeof(struct path_list));
|
||||
else
|
||||
@ -114,7 +110,7 @@ static void insert_one_record(struct path_list *list,
|
||||
onelines->items[onelines->nr++].path = buffer;
|
||||
}
|
||||
|
||||
static void read_from_stdin(struct path_list *list)
|
||||
static void read_from_stdin(struct shortlog *log)
|
||||
{
|
||||
char author[1024], oneline[1024];
|
||||
|
||||
@ -128,39 +124,43 @@ static void read_from_stdin(struct path_list *list)
|
||||
while (fgets(oneline, sizeof(oneline), stdin) &&
|
||||
oneline[0] == '\n')
|
||||
; /* discard blanks */
|
||||
insert_one_record(list, author + 8, oneline);
|
||||
insert_one_record(log, author + 8, oneline);
|
||||
}
|
||||
}
|
||||
|
||||
static void get_from_rev(struct rev_info *rev, struct path_list *list)
|
||||
void shortlog_add_commit(struct shortlog *log, struct commit *commit)
|
||||
{
|
||||
const char *author = NULL, *buffer;
|
||||
|
||||
buffer = commit->buffer;
|
||||
while (*buffer && *buffer != '\n') {
|
||||
const char *eol = strchr(buffer, '\n');
|
||||
|
||||
if (eol == NULL)
|
||||
eol = buffer + strlen(buffer);
|
||||
else
|
||||
eol++;
|
||||
|
||||
if (!prefixcmp(buffer, "author "))
|
||||
author = buffer + 7;
|
||||
buffer = eol;
|
||||
}
|
||||
if (!author)
|
||||
die("Missing author: %s",
|
||||
sha1_to_hex(commit->object.sha1));
|
||||
if (*buffer)
|
||||
buffer++;
|
||||
insert_one_record(log, author, !*buffer ? "<none>" : buffer);
|
||||
}
|
||||
|
||||
static void get_from_rev(struct rev_info *rev, struct shortlog *log)
|
||||
{
|
||||
struct commit *commit;
|
||||
|
||||
if (prepare_revision_walk(rev))
|
||||
die("revision walk setup failed");
|
||||
while ((commit = get_revision(rev)) != NULL) {
|
||||
const char *author = NULL, *buffer;
|
||||
|
||||
buffer = commit->buffer;
|
||||
while (*buffer && *buffer != '\n') {
|
||||
const char *eol = strchr(buffer, '\n');
|
||||
|
||||
if (eol == NULL)
|
||||
eol = buffer + strlen(buffer);
|
||||
else
|
||||
eol++;
|
||||
|
||||
if (!prefixcmp(buffer, "author "))
|
||||
author = buffer + 7;
|
||||
buffer = eol;
|
||||
}
|
||||
if (!author)
|
||||
die("Missing author: %s",
|
||||
sha1_to_hex(commit->object.sha1));
|
||||
if (*buffer)
|
||||
buffer++;
|
||||
insert_one_record(list, author, !*buffer ? "<none>" : buffer);
|
||||
}
|
||||
while ((commit = get_revision(rev)) != NULL)
|
||||
shortlog_add_commit(log, commit);
|
||||
}
|
||||
|
||||
static int parse_uint(char const **arg, int comma)
|
||||
@ -212,29 +212,38 @@ static void parse_wrap_args(const char *arg, int *in1, int *in2, int *wrap)
|
||||
die(wrap_arg_usage);
|
||||
}
|
||||
|
||||
void shortlog_init(struct shortlog *log)
|
||||
{
|
||||
memset(log, 0, sizeof(*log));
|
||||
|
||||
read_mailmap(&log->mailmap, ".mailmap", &log->common_repo_prefix);
|
||||
|
||||
log->list.strdup_paths = 1;
|
||||
log->wrap = DEFAULT_WRAPLEN;
|
||||
log->in1 = DEFAULT_INDENT1;
|
||||
log->in2 = DEFAULT_INDENT2;
|
||||
}
|
||||
|
||||
int cmd_shortlog(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct shortlog log;
|
||||
struct rev_info rev;
|
||||
struct path_list list = { NULL, 0, 0, 1 };
|
||||
int i, j, sort_by_number = 0, summary = 0;
|
||||
int wrap_lines = 0;
|
||||
int wrap = DEFAULT_WRAPLEN;
|
||||
int in1 = DEFAULT_INDENT1;
|
||||
int in2 = DEFAULT_INDENT2;
|
||||
|
||||
shortlog_init(&log);
|
||||
|
||||
/* since -n is a shadowed rev argument, parse our args first */
|
||||
while (argc > 1) {
|
||||
if (!strcmp(argv[1], "-n") || !strcmp(argv[1], "--numbered"))
|
||||
sort_by_number = 1;
|
||||
log.sort_by_number = 1;
|
||||
else if (!strcmp(argv[1], "-s") ||
|
||||
!strcmp(argv[1], "--summary"))
|
||||
summary = 1;
|
||||
log.summary = 1;
|
||||
else if (!strcmp(argv[1], "-e") ||
|
||||
!strcmp(argv[1], "--email"))
|
||||
email = 1;
|
||||
log.email = 1;
|
||||
else if (!prefixcmp(argv[1], "-w")) {
|
||||
wrap_lines = 1;
|
||||
parse_wrap_args(argv[1], &in1, &in2, &wrap);
|
||||
log.wrap_lines = 1;
|
||||
parse_wrap_args(argv[1], &log.in1, &log.in2, &log.wrap);
|
||||
}
|
||||
else if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
|
||||
usage(shortlog_usage);
|
||||
@ -248,34 +257,38 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
|
||||
if (argc > 1)
|
||||
die ("unrecognized argument: %s", argv[1]);
|
||||
|
||||
read_mailmap(&mailmap, ".mailmap", &common_repo_prefix);
|
||||
|
||||
/* assume HEAD if from a tty */
|
||||
if (!rev.pending.nr && isatty(0))
|
||||
add_head_to_pending(&rev);
|
||||
if (rev.pending.nr == 0) {
|
||||
read_from_stdin(&list);
|
||||
read_from_stdin(&log);
|
||||
}
|
||||
else
|
||||
get_from_rev(&rev, &list);
|
||||
get_from_rev(&rev, &log);
|
||||
|
||||
if (sort_by_number)
|
||||
qsort(list.items, list.nr, sizeof(struct path_list_item),
|
||||
shortlog_output(&log);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void shortlog_output(struct shortlog *log)
|
||||
{
|
||||
int i, j;
|
||||
if (log->sort_by_number)
|
||||
qsort(log->list.items, log->list.nr, sizeof(struct path_list_item),
|
||||
compare_by_number);
|
||||
for (i = 0; i < log->list.nr; i++) {
|
||||
struct path_list *onelines = log->list.items[i].util;
|
||||
|
||||
for (i = 0; i < list.nr; i++) {
|
||||
struct path_list *onelines = list.items[i].util;
|
||||
|
||||
if (summary) {
|
||||
printf("%6d\t%s\n", onelines->nr, list.items[i].path);
|
||||
if (log->summary) {
|
||||
printf("%6d\t%s\n", onelines->nr, log->list.items[i].path);
|
||||
} else {
|
||||
printf("%s (%d):\n", list.items[i].path, onelines->nr);
|
||||
printf("%s (%d):\n", log->list.items[i].path, onelines->nr);
|
||||
for (j = onelines->nr - 1; j >= 0; j--) {
|
||||
const char *msg = onelines->items[j].path;
|
||||
|
||||
if (wrap_lines) {
|
||||
int col = print_wrapped_text(msg, in1, in2, wrap);
|
||||
if (col != wrap)
|
||||
if (log->wrap_lines) {
|
||||
int col = print_wrapped_text(msg, log->in1, log->in2, log->wrap);
|
||||
if (col != log->wrap)
|
||||
putchar('\n');
|
||||
}
|
||||
else
|
||||
@ -287,13 +300,11 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
|
||||
onelines->strdup_paths = 1;
|
||||
path_list_clear(onelines, 1);
|
||||
free(onelines);
|
||||
list.items[i].util = NULL;
|
||||
log->list.items[i].util = NULL;
|
||||
}
|
||||
|
||||
list.strdup_paths = 1;
|
||||
path_list_clear(&list, 1);
|
||||
mailmap.strdup_paths = 1;
|
||||
path_list_clear(&mailmap, 1);
|
||||
|
||||
return 0;
|
||||
log->list.strdup_paths = 1;
|
||||
path_list_clear(&log->list, 1);
|
||||
log->mailmap.strdup_paths = 1;
|
||||
path_list_clear(&log->mailmap, 1);
|
||||
}
|
||||
|
15
commit.h
15
commit.h
@ -71,6 +71,21 @@ extern void pretty_print_commit(enum cmit_fmt fmt, const struct commit*,
|
||||
int abbrev, const char *subject,
|
||||
const char *after_subject, enum date_mode,
|
||||
int non_ascii_present);
|
||||
void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
|
||||
const char *line, enum date_mode dmode,
|
||||
const char *encoding);
|
||||
void pp_title_line(enum cmit_fmt fmt,
|
||||
const char **msg_p,
|
||||
struct strbuf *sb,
|
||||
const char *subject,
|
||||
const char *after_subject,
|
||||
const char *encoding,
|
||||
int plain_non_ascii);
|
||||
void pp_remainder(enum cmit_fmt fmt,
|
||||
const char **msg_p,
|
||||
struct strbuf *sb,
|
||||
int indent);
|
||||
|
||||
|
||||
/** Removes the first commit from a list sorted by date, and adds all
|
||||
* of its parents.
|
||||
|
126
log-tree.c
126
log-tree.c
@ -137,6 +137,72 @@ static int has_non_ascii(const char *s)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void log_write_email_headers(struct rev_info *opt, const char *name,
|
||||
const char **subject_p, const char **extra_headers_p)
|
||||
{
|
||||
const char *subject = NULL;
|
||||
const char *extra_headers = opt->extra_headers;
|
||||
if (opt->total > 0) {
|
||||
static char buffer[64];
|
||||
snprintf(buffer, sizeof(buffer),
|
||||
"Subject: [%s %0*d/%d] ",
|
||||
opt->subject_prefix,
|
||||
digits_in_number(opt->total),
|
||||
opt->nr, opt->total);
|
||||
subject = buffer;
|
||||
} else if (opt->total == 0 && opt->subject_prefix && *opt->subject_prefix) {
|
||||
static char buffer[256];
|
||||
snprintf(buffer, sizeof(buffer),
|
||||
"Subject: [%s] ",
|
||||
opt->subject_prefix);
|
||||
subject = buffer;
|
||||
} else {
|
||||
subject = "Subject: ";
|
||||
}
|
||||
|
||||
printf("From %s Mon Sep 17 00:00:00 2001\n", name);
|
||||
if (opt->message_id)
|
||||
printf("Message-Id: <%s>\n", opt->message_id);
|
||||
if (opt->ref_message_id)
|
||||
printf("In-Reply-To: <%s>\nReferences: <%s>\n",
|
||||
opt->ref_message_id, opt->ref_message_id);
|
||||
if (opt->mime_boundary) {
|
||||
static char subject_buffer[1024];
|
||||
static char buffer[1024];
|
||||
snprintf(subject_buffer, sizeof(subject_buffer) - 1,
|
||||
"%s"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: multipart/mixed;"
|
||||
" boundary=\"%s%s\"\n"
|
||||
"\n"
|
||||
"This is a multi-part message in MIME "
|
||||
"format.\n"
|
||||
"--%s%s\n"
|
||||
"Content-Type: text/plain; "
|
||||
"charset=UTF-8; format=fixed\n"
|
||||
"Content-Transfer-Encoding: 8bit\n\n",
|
||||
extra_headers ? extra_headers : "",
|
||||
mime_boundary_leader, opt->mime_boundary,
|
||||
mime_boundary_leader, opt->mime_boundary);
|
||||
extra_headers = subject_buffer;
|
||||
|
||||
snprintf(buffer, sizeof(buffer) - 1,
|
||||
"--%s%s\n"
|
||||
"Content-Type: text/x-patch;"
|
||||
" name=\"%s.diff\"\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Content-Disposition: %s;"
|
||||
" filename=\"%s.diff\"\n\n",
|
||||
mime_boundary_leader, opt->mime_boundary,
|
||||
name,
|
||||
opt->no_inline ? "attachment" : "inline",
|
||||
name);
|
||||
opt->diffopt.stat_sep = buffer;
|
||||
}
|
||||
*subject_p = subject;
|
||||
*extra_headers_p = extra_headers;
|
||||
}
|
||||
|
||||
void show_log(struct rev_info *opt, const char *sep)
|
||||
{
|
||||
struct strbuf msgbuf;
|
||||
@ -188,64 +254,8 @@ void show_log(struct rev_info *opt, const char *sep)
|
||||
*/
|
||||
|
||||
if (opt->commit_format == CMIT_FMT_EMAIL) {
|
||||
char *sha1 = sha1_to_hex(commit->object.sha1);
|
||||
if (opt->total > 0) {
|
||||
static char buffer[64];
|
||||
snprintf(buffer, sizeof(buffer),
|
||||
"Subject: [%s %0*d/%d] ",
|
||||
opt->subject_prefix,
|
||||
digits_in_number(opt->total),
|
||||
opt->nr, opt->total);
|
||||
subject = buffer;
|
||||
} else if (opt->total == 0 && opt->subject_prefix && *opt->subject_prefix) {
|
||||
static char buffer[256];
|
||||
snprintf(buffer, sizeof(buffer),
|
||||
"Subject: [%s] ",
|
||||
opt->subject_prefix);
|
||||
subject = buffer;
|
||||
} else {
|
||||
subject = "Subject: ";
|
||||
}
|
||||
|
||||
printf("From %s Mon Sep 17 00:00:00 2001\n", sha1);
|
||||
if (opt->message_id)
|
||||
printf("Message-Id: <%s>\n", opt->message_id);
|
||||
if (opt->ref_message_id)
|
||||
printf("In-Reply-To: <%s>\nReferences: <%s>\n",
|
||||
opt->ref_message_id, opt->ref_message_id);
|
||||
if (opt->mime_boundary) {
|
||||
static char subject_buffer[1024];
|
||||
static char buffer[1024];
|
||||
snprintf(subject_buffer, sizeof(subject_buffer) - 1,
|
||||
"%s"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: multipart/mixed;"
|
||||
" boundary=\"%s%s\"\n"
|
||||
"\n"
|
||||
"This is a multi-part message in MIME "
|
||||
"format.\n"
|
||||
"--%s%s\n"
|
||||
"Content-Type: text/plain; "
|
||||
"charset=UTF-8; format=fixed\n"
|
||||
"Content-Transfer-Encoding: 8bit\n\n",
|
||||
extra_headers ? extra_headers : "",
|
||||
mime_boundary_leader, opt->mime_boundary,
|
||||
mime_boundary_leader, opt->mime_boundary);
|
||||
extra_headers = subject_buffer;
|
||||
|
||||
snprintf(buffer, sizeof(buffer) - 1,
|
||||
"--%s%s\n"
|
||||
"Content-Type: text/x-patch;"
|
||||
" name=\"%s.diff\"\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Content-Disposition: %s;"
|
||||
" filename=\"%s.diff\"\n\n",
|
||||
mime_boundary_leader, opt->mime_boundary,
|
||||
sha1,
|
||||
opt->no_inline ? "attachment" : "inline",
|
||||
sha1);
|
||||
opt->diffopt.stat_sep = buffer;
|
||||
}
|
||||
log_write_email_headers(opt, sha1_to_hex(commit->object.sha1),
|
||||
&subject, &extra_headers);
|
||||
} else if (opt->commit_format != CMIT_FMT_USERFORMAT) {
|
||||
fputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), stdout);
|
||||
if (opt->commit_format != CMIT_FMT_ONELINE)
|
||||
|
@ -13,5 +13,7 @@ int log_tree_commit(struct rev_info *, struct commit *);
|
||||
int log_tree_opt_parse(struct rev_info *, const char **, int);
|
||||
void show_log(struct rev_info *opt, const char *sep);
|
||||
void show_decorations(struct commit *commit);
|
||||
void log_write_email_headers(struct rev_info *opt, const char *name,
|
||||
const char **subject_p, const char **extra_headers_p);
|
||||
|
||||
#endif
|
||||
|
32
pretty.c
32
pretty.c
@ -110,9 +110,9 @@ needquote:
|
||||
strbuf_addstr(sb, "?=");
|
||||
}
|
||||
|
||||
static void add_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
|
||||
const char *line, enum date_mode dmode,
|
||||
const char *encoding)
|
||||
void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
|
||||
const char *line, enum date_mode dmode,
|
||||
const char *encoding)
|
||||
{
|
||||
char *date;
|
||||
int namelen;
|
||||
@ -621,23 +621,23 @@ static void pp_header(enum cmit_fmt fmt,
|
||||
*/
|
||||
if (!memcmp(line, "author ", 7)) {
|
||||
strbuf_grow(sb, linelen + 80);
|
||||
add_user_info("Author", fmt, sb, line + 7, dmode, encoding);
|
||||
pp_user_info("Author", fmt, sb, line + 7, dmode, encoding);
|
||||
}
|
||||
if (!memcmp(line, "committer ", 10) &&
|
||||
(fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) {
|
||||
strbuf_grow(sb, linelen + 80);
|
||||
add_user_info("Commit", fmt, sb, line + 10, dmode, encoding);
|
||||
pp_user_info("Commit", fmt, sb, line + 10, dmode, encoding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void pp_title_line(enum cmit_fmt fmt,
|
||||
const char **msg_p,
|
||||
struct strbuf *sb,
|
||||
const char *subject,
|
||||
const char *after_subject,
|
||||
const char *encoding,
|
||||
int plain_non_ascii)
|
||||
void pp_title_line(enum cmit_fmt fmt,
|
||||
const char **msg_p,
|
||||
struct strbuf *sb,
|
||||
const char *subject,
|
||||
const char *after_subject,
|
||||
const char *encoding,
|
||||
int plain_non_ascii)
|
||||
{
|
||||
struct strbuf title;
|
||||
|
||||
@ -686,10 +686,10 @@ static void pp_title_line(enum cmit_fmt fmt,
|
||||
strbuf_release(&title);
|
||||
}
|
||||
|
||||
static void pp_remainder(enum cmit_fmt fmt,
|
||||
const char **msg_p,
|
||||
struct strbuf *sb,
|
||||
int indent)
|
||||
void pp_remainder(enum cmit_fmt fmt,
|
||||
const char **msg_p,
|
||||
struct strbuf *sb,
|
||||
int indent)
|
||||
{
|
||||
int first = 1;
|
||||
for (;;) {
|
||||
|
@ -75,7 +75,7 @@ struct rev_info {
|
||||
struct log_info *loginfo;
|
||||
int nr, total;
|
||||
const char *mime_boundary;
|
||||
const char *message_id;
|
||||
char *message_id;
|
||||
const char *ref_message_id;
|
||||
const char *add_signoff;
|
||||
const char *extra_headers;
|
||||
|
26
shortlog.h
Normal file
26
shortlog.h
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef SHORTLOG_H
|
||||
#define SHORTLOG_H
|
||||
|
||||
#include "path-list.h"
|
||||
|
||||
struct shortlog {
|
||||
struct path_list list;
|
||||
int summary;
|
||||
int wrap_lines;
|
||||
int sort_by_number;
|
||||
int wrap;
|
||||
int in1;
|
||||
int in2;
|
||||
|
||||
char *common_repo_prefix;
|
||||
int email;
|
||||
struct path_list mailmap;
|
||||
};
|
||||
|
||||
void shortlog_init(struct shortlog *log);
|
||||
|
||||
void shortlog_add_commit(struct shortlog *log, struct commit *commit);
|
||||
|
||||
void shortlog_output(struct shortlog *log);
|
||||
|
||||
#endif
|
@ -245,6 +245,7 @@ format-patch --inline --stdout initial..master
|
||||
format-patch --inline --stdout --subject-prefix=TESTCASE initial..master
|
||||
config format.subjectprefix DIFFERENT_PREFIX
|
||||
format-patch --inline --stdout initial..master^^
|
||||
format-patch --stdout --cover-letter -n initial..master^
|
||||
|
||||
diff --abbrev initial..side
|
||||
diff -r initial..side
|
||||
|
@ -0,0 +1,100 @@
|
||||
$ git format-patch --stdout --cover-letter -n initial..master^
|
||||
From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
|
||||
From: C O Mitter <committer@example.com>
|
||||
Date: Mon, 26 Jun 2006 00:05:00 +0000
|
||||
Subject: [DIFFERENT_PREFIX 0/2] *** SUBJECT HERE ***
|
||||
|
||||
*** BLURB HERE ***
|
||||
|
||||
A U Thor (2):
|
||||
Second
|
||||
Third
|
||||
|
||||
dir/sub | 4 ++++
|
||||
file0 | 3 +++
|
||||
file1 | 3 +++
|
||||
file2 | 3 ---
|
||||
4 files changed, 10 insertions(+), 3 deletions(-)
|
||||
create mode 100644 file1
|
||||
delete mode 100644 file2
|
||||
|
||||
From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
|
||||
From: A U Thor <author@example.com>
|
||||
Date: Mon, 26 Jun 2006 00:01:00 +0000
|
||||
Subject: [DIFFERENT_PREFIX 1/2] Second
|
||||
|
||||
This is the second commit.
|
||||
---
|
||||
dir/sub | 2 ++
|
||||
file0 | 3 +++
|
||||
file2 | 3 ---
|
||||
3 files changed, 5 insertions(+), 3 deletions(-)
|
||||
delete mode 100644 file2
|
||||
|
||||
diff --git a/dir/sub b/dir/sub
|
||||
index 35d242b..8422d40 100644
|
||||
--- a/dir/sub
|
||||
+++ b/dir/sub
|
||||
@@ -1,2 +1,4 @@
|
||||
A
|
||||
B
|
||||
+C
|
||||
+D
|
||||
diff --git a/file0 b/file0
|
||||
index 01e79c3..b414108 100644
|
||||
--- a/file0
|
||||
+++ b/file0
|
||||
@@ -1,3 +1,6 @@
|
||||
1
|
||||
2
|
||||
3
|
||||
+4
|
||||
+5
|
||||
+6
|
||||
diff --git a/file2 b/file2
|
||||
deleted file mode 100644
|
||||
index 01e79c3..0000000
|
||||
--- a/file2
|
||||
+++ /dev/null
|
||||
@@ -1,3 +0,0 @@
|
||||
-1
|
||||
-2
|
||||
-3
|
||||
--
|
||||
g-i-t--v-e-r-s-i-o-n
|
||||
|
||||
|
||||
From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
|
||||
From: A U Thor <author@example.com>
|
||||
Date: Mon, 26 Jun 2006 00:02:00 +0000
|
||||
Subject: [DIFFERENT_PREFIX 2/2] Third
|
||||
|
||||
---
|
||||
dir/sub | 2 ++
|
||||
file1 | 3 +++
|
||||
2 files changed, 5 insertions(+), 0 deletions(-)
|
||||
create mode 100644 file1
|
||||
|
||||
diff --git a/dir/sub b/dir/sub
|
||||
index 8422d40..cead32e 100644
|
||||
--- a/dir/sub
|
||||
+++ b/dir/sub
|
||||
@@ -2,3 +2,5 @@ A
|
||||
B
|
||||
C
|
||||
D
|
||||
+E
|
||||
+F
|
||||
diff --git a/file1 b/file1
|
||||
new file mode 100644
|
||||
index 0000000..b1e6722
|
||||
--- /dev/null
|
||||
+++ b/file1
|
||||
@@ -0,0 +1,3 @@
|
||||
+A
|
||||
+B
|
||||
+C
|
||||
--
|
||||
g-i-t--v-e-r-s-i-o-n
|
||||
|
||||
$
|
@ -88,4 +88,117 @@ test_expect_success 'replay did not screw up the log message' '
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'extra headers' '
|
||||
|
||||
git config format.headers "To: R. E. Cipient <rcipient@example.com>
|
||||
" &&
|
||||
git config --add format.headers "Cc: S. E. Cipient <scipient@example.com>
|
||||
" &&
|
||||
git format-patch --stdout master..side > patch2 &&
|
||||
sed -e "/^$/q" patch2 > hdrs2 &&
|
||||
grep "^To: R. E. Cipient <rcipient@example.com>$" hdrs2 &&
|
||||
grep "^Cc: S. E. Cipient <scipient@example.com>$" hdrs2
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'extra headers without newlines' '
|
||||
|
||||
git config --replace-all format.headers "To: R. E. Cipient <rcipient@example.com>" &&
|
||||
git config --add format.headers "Cc: S. E. Cipient <scipient@example.com>" &&
|
||||
git format-patch --stdout master..side >patch3 &&
|
||||
sed -e "/^$/q" patch3 > hdrs3 &&
|
||||
grep "^To: R. E. Cipient <rcipient@example.com>$" hdrs3 &&
|
||||
grep "^Cc: S. E. Cipient <scipient@example.com>$" hdrs3
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'extra headers with multiple To:s' '
|
||||
|
||||
git config --replace-all format.headers "To: R. E. Cipient <rcipient@example.com>" &&
|
||||
git config --add format.headers "To: S. E. Cipient <scipient@example.com>" &&
|
||||
git format-patch --stdout master..side > patch4 &&
|
||||
sed -e "/^$/q" patch4 > hdrs4 &&
|
||||
grep "^To: R. E. Cipient <rcipient@example.com>,$" hdrs4 &&
|
||||
grep "^ *S. E. Cipient <scipient@example.com>$" hdrs4
|
||||
'
|
||||
|
||||
test_expect_success 'additional command line cc' '
|
||||
|
||||
git config --replace-all format.headers "Cc: R. E. Cipient <rcipient@example.com>" &&
|
||||
git format-patch --cc="S. E. Cipient <scipient@example.com>" --stdout master..side | sed -e "/^$/q" >patch5 &&
|
||||
grep "^Cc: R. E. Cipient <rcipient@example.com>,$" patch5 &&
|
||||
grep "^ *S. E. Cipient <scipient@example.com>$" patch5
|
||||
'
|
||||
|
||||
test_expect_success 'multiple files' '
|
||||
|
||||
rm -rf patches/ &&
|
||||
git checkout side &&
|
||||
git format-patch -o patches/ master &&
|
||||
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
|
||||
'
|
||||
|
||||
test_expect_success 'thread' '
|
||||
|
||||
rm -rf patches/ &&
|
||||
git checkout side &&
|
||||
git format-patch --thread -o patches/ master &&
|
||||
FIRST_MID=$(grep "Message-Id:" patches/0001-* | sed "s/^[^<]*\(<[^>]*>\).*$/\1/") &&
|
||||
for i in patches/0002-* patches/0003-*
|
||||
do
|
||||
grep "References: $FIRST_MID" $i &&
|
||||
grep "In-Reply-To: $FIRST_MID" $i
|
||||
done
|
||||
'
|
||||
|
||||
test_expect_success 'thread in-reply-to' '
|
||||
|
||||
rm -rf patches/ &&
|
||||
git checkout side &&
|
||||
git format-patch --in-reply-to="<test.message>" --thread -o patches/ master &&
|
||||
FIRST_MID="<test.message>" &&
|
||||
for i in patches/*
|
||||
do
|
||||
grep "References: $FIRST_MID" $i &&
|
||||
grep "In-Reply-To: $FIRST_MID" $i
|
||||
done
|
||||
'
|
||||
|
||||
test_expect_success 'thread cover-letter' '
|
||||
|
||||
rm -rf patches/ &&
|
||||
git checkout side &&
|
||||
git format-patch --cover-letter --thread -o patches/ master &&
|
||||
FIRST_MID=$(grep "Message-Id:" patches/0000-* | sed "s/^[^<]*\(<[^>]*>\).*$/\1/") &&
|
||||
for i in patches/0001-* patches/0002-* patches/0003-*
|
||||
do
|
||||
grep "References: $FIRST_MID" $i &&
|
||||
grep "In-Reply-To: $FIRST_MID" $i
|
||||
done
|
||||
'
|
||||
|
||||
test_expect_success 'thread cover-letter in-reply-to' '
|
||||
|
||||
rm -rf patches/ &&
|
||||
git checkout side &&
|
||||
git format-patch --cover-letter --in-reply-to="<test.message>" --thread -o patches/ master &&
|
||||
FIRST_MID="<test.message>" &&
|
||||
for i in patches/*
|
||||
do
|
||||
grep "References: $FIRST_MID" $i &&
|
||||
grep "In-Reply-To: $FIRST_MID" $i
|
||||
done
|
||||
'
|
||||
|
||||
test_expect_success 'excessive subject' '
|
||||
|
||||
rm -rf patches/ &&
|
||||
git checkout side &&
|
||||
for i in 5 6 1 2 3 A 4 B C 7 8 9 10 D E F; do echo "$i"; done >>file &&
|
||||
git update-index file &&
|
||||
git commit -m "This is an excessively long subject line for a message due to the habit some projects have of not having a short, one-line subject at the start of the commit message, but rather sticking a whole paragraph right at the start as the only thing in the commit message. It had better not become the filename for the patch." &&
|
||||
git format-patch -o patches/ master..side &&
|
||||
ls patches/0004-This-is-an-excessively-long-subject-line-for-a-messa.patch
|
||||
'
|
||||
|
||||
test_done
|
||||
|
Loading…
Reference in New Issue
Block a user