提交 159c88e5 编写于 作者: J Junio C Hamano

Merge branch 'maint'

* maint:
  git-send-email.txt: move --format-patch paragraph to a proper location
  git-shortlog.txt: improve documentation about .mailmap files
  pretty: support multiline subjects with format:
  pretty: factor out format_subject()
  pretty: factor out skip_empty_lines()
  merge-file: handle freopen() failure
  daemon: cleanup: factor out xstrdup_tolower()
  daemon: cleanup: replace loop with if
  daemon: handle freopen() failure
  describe: Avoid unnecessary warning when using --all
......@@ -197,12 +197,6 @@ Administering
--[no-]validate::
Perform sanity checks on patches.
Currently, validation means the following:
--[no-]format-patch::
When an argument may be understood either as a reference or as a file name,
choose to understand it as a format-patch argument ('--format-patch')
or as a file name ('--no-format-patch'). By default, when such a conflict
occurs, git send-email will fail.
+
--
* Warn of patches that contain lines longer than 998 characters; this
......@@ -212,6 +206,12 @@ Administering
Default is the value of 'sendemail.validate'; if this is not set,
default to '--validate'.
--[no-]format-patch::
When an argument may be understood either as a reference or as a file name,
choose to understand it as a format-patch argument ('--format-patch')
or as a file name ('--no-format-patch'). By default, when such a conflict
occurs, git send-email will fail.
CONFIGURATION
-------------
......
......@@ -48,15 +48,41 @@ OPTIONS
FILES
-----
If the file `.mailmap` exists, it will be used for mapping author
email addresses to a real author name. One mapping per line, first
the author name followed by the email address enclosed by
'<' and '>'. Use hash '#' for comments. Example:
If a file `.mailmap` exists at the toplevel of the repository,
it is used to map an author email address to a canonical real name. This
can be used to coalesce together commits by the same person where their
name was spelled differently (whether with the same email address or
not).
Each line in the file consists, in this order, of the canonical real name
of an author, whitespace, and an email address (enclosed by '<' and '>')
to map to the name. Use hash '#' for comments, either on their own line,
or after the email address.
A canonical name may appear in more than one line, associated with
different email addresses, but it doesn't make sense for a given address
to appear more than once (if that happens, a later line overrides the
earlier ones).
So, for example, if your history contains commits by two authors, Jane
and Joe, whose names appear in the repository under several forms:
------------
Joe Developer <joe@example.com>
Joe R. Developer <joe@example.com>
Jane Doe <jane@example.com>
Jane Doe <jane@laptop.(none)>
Jane D. <jane@desktop.(none)>
------------
Then, supposing Joe wants his middle name initial used, and Jane prefers
her family name fully spelled out, a proper `.mailmap` file would look like:
------------
# Keep alphabetized
Adam Morrow <adam@localhost.localdomain>
Eve Jones <eve@laptop.(none)>
# Note how we don't need an entry for <jane@laptop.(none)>, because the
# real name of that author is correct already, and coalesced directly.
Jane Doe <jane@desktop.(none)>
Joe R. Developer <joe@random.com>
------------
Author
......
......@@ -158,7 +158,7 @@ static void display_name(struct commit_name *n)
n->tag = lookup_tag(n->sha1);
if (!n->tag || parse_tag(n->tag) || !n->tag->tag)
die("annotated tag %s not available", n->path);
if (strcmp(n->tag->tag, n->path))
if (strcmp(n->tag->tag, all ? n->path + 5 : n->path))
warning("tag '%s' is really '%s' here", n->tag->tag, n->path);
}
......
......@@ -51,8 +51,11 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, options, merge_file_usage, 0);
if (argc != 3)
usage_with_options(merge_file_usage, options);
if (quiet)
freopen("/dev/null", "w", stderr);
if (quiet) {
if (!freopen("/dev/null", "w", stderr))
return error("failed to redirect stderr to /dev/null: "
"%s\n", strerror(errno));
}
for (i = 0; i < 3; i++) {
if (!names[i])
......
......@@ -150,7 +150,6 @@ static char *path_ok(char *directory)
{
static char rpath[PATH_MAX];
static char interp_path[PATH_MAX];
int retried_path = 0;
char *path;
char *dir;
......@@ -219,22 +218,15 @@ static char *path_ok(char *directory)
dir = rpath;
}
do {
path = enter_repo(dir, strict_paths);
if (path)
break;
path = enter_repo(dir, strict_paths);
if (!path && base_path && base_path_relaxed) {
/*
* if we fail and base_path_relaxed is enabled, try without
* prefixing the base path
*/
if (base_path && base_path_relaxed && !retried_path) {
dir = directory;
retried_path = 1;
continue;
}
break;
} while (1);
dir = directory;
path = enter_repo(dir, strict_paths);
}
if (!path) {
logerror("'%s': unable to chdir or not a git archive", dir);
......@@ -405,6 +397,14 @@ static void make_service_overridable(const char *name, int ena)
die("No such service %s", name);
}
static char *xstrdup_tolower(const char *str)
{
char *p, *dup = xstrdup(str);
for (p = dup; *p; p++)
*p = tolower(*p);
return dup;
}
/*
* Separate the "extra args" information as supplied by the client connection.
*/
......@@ -413,7 +413,6 @@ static void parse_extra_args(char *extra_args, int buflen)
char *val;
int vallen;
char *end = extra_args + buflen;
char *hp;
while (extra_args < end && *extra_args) {
saw_extended_args = 1;
......@@ -431,7 +430,7 @@ static void parse_extra_args(char *extra_args, int buflen)
tcp_port = xstrdup(port);
}
free(hostname);
hostname = xstrdup(host);
hostname = xstrdup_tolower(host);
}
/* On to the next one */
......@@ -439,20 +438,11 @@ static void parse_extra_args(char *extra_args, int buflen)
}
}
/*
* Replace literal host with lowercase-ized hostname.
*/
hp = hostname;
if (!hp)
return;
for ( ; *hp; hp++)
*hp = tolower(*hp);
/*
* Locate canonical hostname and its IP address.
*/
if (hostname) {
#ifndef NO_IPV6
{
struct addrinfo hints;
struct addrinfo *ai, *ai0;
int gai;
......@@ -476,9 +466,7 @@ static void parse_extra_args(char *extra_args, int buflen)
}
freeaddrinfo(ai0);
}
}
#else
{
struct hostent *hent;
struct sockaddr_in sa;
char **ap;
......@@ -499,8 +487,8 @@ static void parse_extra_args(char *extra_args, int buflen)
canon_hostname = xstrdup(hent->h_name);
free(ip_address);
ip_address = xstrdup(addrbuf);
}
#endif
}
}
......@@ -953,12 +941,8 @@ int main(int argc, char **argv)
char *arg = argv[i];
if (!prefixcmp(arg, "--listen=")) {
char *p = arg + 9;
char *ph = listen_addr = xmalloc(strlen(arg + 9) + 1);
while (*p)
*ph++ = tolower(*p++);
*ph = 0;
continue;
listen_addr = xstrdup_tolower(arg + 9);
continue;
}
if (!prefixcmp(arg, "--port=")) {
char *end;
......@@ -1118,7 +1102,9 @@ int main(int argc, char **argv)
struct sockaddr *peer = (struct sockaddr *)&ss;
socklen_t slen = sizeof(ss);
freopen("/dev/null", "w", stderr);
if (!freopen("/dev/null", "w", stderr))
die("failed to redirect stderr to /dev/null: %s",
strerror(errno));
if (getpeername(0, peer, &slen))
peer = NULL;
......
......@@ -181,6 +181,20 @@ static int is_empty_line(const char *line, int *len_p)
return !len;
}
static const char *skip_empty_lines(const char *msg)
{
for (;;) {
int linelen = get_one_line(msg);
int ll = linelen;
if (!linelen)
break;
if (!is_empty_line(msg, &ll))
break;
msg += linelen;
}
return msg;
}
static void add_merge_info(enum cmit_fmt fmt, struct strbuf *sb,
const struct commit *commit, int abbrev)
{
......@@ -410,13 +424,15 @@ struct chunk {
struct format_commit_context {
const struct commit *commit;
enum date_mode dmode;
unsigned commit_header_parsed:1;
unsigned commit_message_parsed:1;
/* These offsets are relative to the start of the commit message. */
int commit_header_parsed;
struct chunk subject;
struct chunk author;
struct chunk committer;
struct chunk encoding;
size_t message_off;
size_t subject_off;
size_t body_off;
/* The following ones are relative to the result struct strbuf. */
......@@ -446,23 +462,14 @@ static void parse_commit_header(struct format_commit_context *context)
{
const char *msg = context->commit->buffer;
int i;
enum { HEADER, SUBJECT, BODY } state;
for (i = 0, state = HEADER; msg[i] && state < BODY; i++) {
for (i = 0; msg[i]; i++) {
int eol;
for (eol = i; msg[eol] && msg[eol] != '\n'; eol++)
; /* do nothing */
if (state == SUBJECT) {
context->subject.off = i;
context->subject.len = eol - i;
i = eol;
}
if (i == eol) {
state++;
/* strip empty lines */
while (msg[eol] == '\n' && msg[eol + 1] == '\n')
eol++;
break;
} else if (!prefixcmp(msg + i, "author ")) {
context->author.off = i + 7;
context->author.len = eol - i - 7;
......@@ -474,13 +481,50 @@ static void parse_commit_header(struct format_commit_context *context)
context->encoding.len = eol - i - 9;
}
i = eol;
if (!msg[i])
break;
}
context->body_off = i;
context->message_off = i;
context->commit_header_parsed = 1;
}
static const char *format_subject(struct strbuf *sb, const char *msg,
const char *line_separator)
{
int first = 1;
for (;;) {
const char *line = msg;
int linelen = get_one_line(line);
msg += linelen;
if (!linelen || is_empty_line(line, &linelen))
break;
if (!sb)
continue;
strbuf_grow(sb, linelen + 2);
if (!first)
strbuf_addstr(sb, line_separator);
strbuf_add(sb, line, linelen);
first = 0;
}
return msg;
}
static void parse_commit_message(struct format_commit_context *c)
{
const char *msg = c->commit->buffer + c->message_off;
const char *start = c->commit->buffer;
msg = skip_empty_lines(msg);
c->subject_off = msg - start;
msg = format_subject(NULL, msg, NULL);
msg = skip_empty_lines(msg);
c->body_off = msg - start;
c->commit_message_parsed = 1;
}
static void format_decoration(struct strbuf *sb, const struct commit *commit)
{
struct name_decoration *d;
......@@ -600,9 +644,6 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder,
parse_commit_header(c);
switch (placeholder[0]) {
case 's': /* subject */
strbuf_add(sb, msg + c->subject.off, c->subject.len);
return 1;
case 'a': /* author ... */
return format_person_part(sb, placeholder[1],
msg + c->author.off, c->author.len,
......@@ -614,6 +655,16 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder,
case 'e': /* encoding */
strbuf_add(sb, msg + c->encoding.off, c->encoding.len);
return 1;
}
/* Now we need to parse the commit message. */
if (!c->commit_message_parsed)
parse_commit_message(c);
switch (placeholder[0]) {
case 's': /* subject */
format_subject(sb, msg + c->subject_off, " ");
return 1;
case 'b': /* body */
strbuf_addstr(sb, msg + c->body_off);
return 1;
......@@ -704,27 +755,11 @@ void pp_title_line(enum cmit_fmt fmt,
const char *encoding,
int need_8bit_cte)
{
const char *line_separator = (fmt == CMIT_FMT_EMAIL) ? "\n " : " ";
struct strbuf title;
strbuf_init(&title, 80);
for (;;) {
const char *line = *msg_p;
int linelen = get_one_line(line);
*msg_p += linelen;
if (!linelen || is_empty_line(line, &linelen))
break;
strbuf_grow(&title, linelen + 2);
if (title.len) {
if (fmt == CMIT_FMT_EMAIL) {
strbuf_addch(&title, '\n');
}
strbuf_addch(&title, ' ');
}
strbuf_add(&title, line, linelen);
}
*msg_p = format_subject(&title, *msg_p, line_separator);
strbuf_grow(sb, title.len + 1024);
if (subject) {
......@@ -850,15 +885,7 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
}
/* Skip excess blank lines at the beginning of body, if any... */
for (;;) {
int linelen = get_one_line(msg);
int ll = linelen;
if (!linelen)
break;
if (!is_empty_line(msg, &ll))
break;
msg += linelen;
}
msg = skip_empty_lines(msg);
/* These formats treat the title line specially. */
if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
......
......@@ -100,6 +100,12 @@ check_describe B --tags HEAD^^2^
check_describe B-0-* --long HEAD^^2^
check_describe A-3-* --long HEAD^^2
: >err.expect
check_describe A --all A^0
test_expect_success 'no warning was displayed for A' '
test_cmp err.expect err.actual
'
test_expect_success 'rename tag A to Q locally' '
mv .git/refs/tags/A .git/refs/tags/Q
'
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册