Merge branch 'mh/remote-hg-mode-bits-fix'
authorJunio C Hamano <gitster@pobox.com>
Tue, 22 Jan 2013 04:15:40 +0000 (20:15 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 22 Jan 2013 04:15:40 +0000 (20:15 -0800)
Update to the Hg remote helper (in contrib/).

* mh/remote-hg-mode-bits-fix:
remote-hg: fix handling of file perms when pushing

36 files changed:
Documentation/RelNotes/1.8.1.2.txt [new file with mode: 0644]
Documentation/RelNotes/1.8.2.txt
Documentation/config.txt
Documentation/git-commit-tree.txt
Documentation/git-commit.txt
Documentation/git-for-each-ref.txt
Documentation/git-log.txt
Documentation/git-svn.txt
attr.c
builtin/blame.c
builtin/clean.c
builtin/clone.c
builtin/commit.c
builtin/log.c
builtin/shortlog.c
commit.h
contrib/completion/git-completion.bash
contrib/completion/git-completion.tcsh
contrib/remote-helpers/git-remote-hg
contrib/vim/README
git-rebase--interactive.sh
git-svn.perl
log-tree.c
mailmap.c
mailmap.h
perl/Git/SVN/Editor.pm
perl/Git/SVN/Utils.pm
pretty.c
revision.c
revision.h
string-list.c
string-list.h
t/t4203-mailmap.sh
t/t7500/add-content-and-comment [new file with mode: 0755]
t/t7502-commit.sh
t/test-lib.sh
diff --git a/Documentation/RelNotes/1.8.1.2.txt b/Documentation/RelNotes/1.8.1.2.txt
new file mode 100644 (file)
index 0000000..76ad0b3
--- /dev/null
@@ -0,0 +1,13 @@
+Git 1.8.1.2 Release Notes
+=========================
+
+Fixes since v1.8.1.1
+--------------------
+
+ * "git archive" did not record uncompressed size in the header when
+   streaming a zip archive, which confused some implementations of unzip.
+
+ * When users spelled "cc:" in lowercase in the fake "header" in the
+   trailer part, "git send-email" failed to pick up the addresses from
+   there. As e-mail headers field names are case insensitive, this
+   script should follow suit and treat "cc:" and "Cc:" the same way.
index ff5869d960bd35742ba77abf8684db3e7f856b08..f6d15059dc71c22ace37732b5b215c39c4cec944 100644 (file)
@@ -46,6 +46,9 @@ UI, Workflows & Features
  * "git cherry-pick" can be used to replay a root commit to an unborn
    branch.
 
+ * "git commit" can be told to use --cleanup=whitespace by setting the
+   configuration variable commit.cleanup to 'whitespace'.
+
  * "git fetch --mirror" and fetch that uses other forms of refspec
    with wildcard used to attempt to update a symbolic ref that match
    the wildcard on the receiving end, which made little sense (the
@@ -62,6 +65,10 @@ UI, Workflows & Features
    allows patches from rerolled series to be stored under different
    names and makes it easier to reuse cover letter messsages.
 
+ * "git log" and friends can be told with --use-mailmap option to
+   rewrite the names and email addresses of people using the mailmap
+   mechanism.
+
  * "git push" now requires "-f" to update a tag, even if it is a
    fast-forward, as tags are meant to be fixed points.
 
@@ -213,6 +220,14 @@ details).
    to add a newline after such a line.
    (merge e0db176 sp/shortlog-missing-lf later to maint).
 
+ * Command line completion leaked an unnecessary error message while
+   looking for possible matches with paths in <tree-ish>.
+   (merge ca87dd6 ds/completion-silence-in-tree-path-probe later to maint).
+
+ * Command line completion for "tcsh" emitted an unwanted space
+   after completing a single directory name.
+   (merge 92f1c04 mk/complete-tcsh later to maint).
+
  * Some shells do not behave correctly when IFS is unset; work it
    around by explicitly setting it to the default value.
    (merge 393050c jc/maint-fbsd-sh-ifs-workaround later to maint).
index d5809e0e8c4dca414a863cfd019a6f8860c46c4d..b87f7446436ea73ffeb062317ea9869051d5be78 100644 (file)
@@ -923,6 +923,15 @@ column.tag::
        Specify whether to output tag listing in `git tag` in columns.
        See `column.ui` for details.
 
+commit.cleanup::
+       This setting overrides the default of the `--cleanup` option in
+       `git commit`. See linkgit:git-commit[1] for details. Changing the
+       default can be useful when you always want to keep lines that begin
+       with comment character `#` in your log message, in which case you
+       would do `git config commit.cleanup whitespace` (note that you will
+       have to remove the help lines that begin with `#` in the commit log
+       template yourself, if you do this).
+
 commit.status::
        A boolean to enable/disable inclusion of status information in the
        commit message template when using an editor to prepare the commit
@@ -1361,6 +1370,12 @@ help.autocorrect::
        value is 0 - the command will be just shown but not executed.
        This is the default.
 
+help.htmlpath::
+       Specify the path where the HTML documentation resides. File system paths
+       and URLs are supported. HTML pages will be prefixed with this path when
+       help is displayed in the 'web' format. This defaults to the documentation
+       path of your Git installation.
+
 http.proxy::
        Override the HTTP proxy, normally configured using the 'http_proxy',
        'https_proxy', and 'all_proxy' environment variables (see
@@ -1519,6 +1534,10 @@ log.showroot::
        Tools like linkgit:git-log[1] or linkgit:git-whatchanged[1], which
        normally hide the root commit will now show it. True by default.
 
+log.mailmap::
+       If true, makes linkgit:git-log[1], linkgit:git-show[1], and
+       linkgit:git-whatchanged[1] assume `--use-mailmap`.
+
 mailmap.file::
        The location of an augmenting mailmap file. The default
        mailmap, located in the root of the repository, is loaded
index 6d5a04c83b6461396b44b3972a539eaae6368a19..a22116951527e4181d85ed23acb029f6920ee1ff 100644 (file)
@@ -72,13 +72,13 @@ if set:
        GIT_COMMITTER_NAME
        GIT_COMMITTER_EMAIL
        GIT_COMMITTER_DATE
-       EMAIL
 
 (nb "<", ">" and "\n"s are stripped)
 
 In case (some of) these environment variables are not set, the information
 is taken from the configuration items user.name and user.email, or, if not
-present, system user name and the hostname used for outgoing mail (taken
+present, the environment variable EMAIL, or, if that is not set,
+system user name and the hostname used for outgoing mail (taken
 from `/etc/mailname` and falling back to the fully qualified hostname when
 that file does not exist).
 
index 7bdb039d5ee9c6ed6c19c70173c733860de2d5b7..41b27da325053a9a817160364ac50e5140f5891c 100644 (file)
@@ -179,7 +179,9 @@ OPTIONS
        only if the message is to be edited. Otherwise only whitespace
        removed. The 'verbatim' mode does not change message at all,
        'whitespace' removes just leading/trailing whitespace lines
-       and 'strip' removes both whitespace and commentary.
+       and 'strip' removes both whitespace and commentary. The default
+       can be changed by the 'commit.cleanup' configuration variable
+       (see linkgit:git-config[1]).
 
 -e::
 --edit::
index db55a4e0bbc524c20f08e6862daaa97dd1785b71..f2e08d11c19cf127c5f7d484b0c3d66fc475d547 100644 (file)
@@ -117,7 +117,7 @@ returns an empty string instead.
 
 As a special case for the date-type fields, you may specify a format for
 the date by adding one of `:default`, `:relative`, `:short`, `:local`,
-`:iso8601` or `:rfc2822` to the end of the fieldname; e.g.
+`:iso8601`, `:rfc2822` or `:raw` to the end of the fieldname; e.g.
 `%(taggerdate:relative)`.
 
 
index 08a185db7fb7192b4e94c83ae6781893e119c6cf..22c0d6e4b1074b71468ac1c9b16d7eeb0434e225 100644 (file)
@@ -47,6 +47,11 @@ OPTIONS
        Print out the ref name given on the command line by which each
        commit was reached.
 
+--use-mailmap::
+       Use mailmap file to map author and committer names and email
+       to canonical real names and email addresses. See
+       linkgit:git-shortlog[1].
+
 --full-diff::
        Without this flag, "git log -p <path>..." shows commits that
        touch the specified paths, and diffs about the same specified
index 69decb13b0ec672cd9ccc83a67a08f1725473324..34d438b0abd583ca8b2e2156a085e322e8f827eb 100644 (file)
@@ -346,6 +346,16 @@ Any other arguments are passed directly to 'git log'
        corresponding git commit hash (this can optionally be followed by a
        tree-ish to specify which branch should be searched).  When given a
        tree-ish, returns the corresponding SVN revision number.
++
+--before;;
+       Don't require an exact match if given an SVN revision, instead find
+       the commit corresponding to the state of the SVN repository (on the
+       current branch) at the specified revision.
++
+--after;;
+       Don't require an exact match if given an SVN revision; if there is
+       not an exact match return the closest match searching forward in the
+       history.
 
 'set-tree'::
        You should consider using 'dcommit' instead of this command.
diff --git a/attr.c b/attr.c
index d6d71901b24855a1d33ef2edeb8fbc6ce8e969dc..233539969af6dc17e8953e4a553cae76fd9601da 100644 (file)
--- a/attr.c
+++ b/attr.c
@@ -704,7 +704,7 @@ static int fill_one(const char *what, struct match_attr *a, int rem)
 
                if (*n == ATTR__UNKNOWN) {
                        debug_set(what,
-                                 a->is_macro ? a->u.attr->name : a->u.pattern,
+                                 a->is_macro ? a->u.attr->name : a->u.pat.pattern,
                                  attr, v);
                        *n = v;
                        rem--;
index bc6c899d302d3027a0531b613d4c2dbf706d558d..b431ba320997918324a76c29b81dd01613cfa9db 100644 (file)
@@ -1322,30 +1322,31 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
  * Information on commits, used for output.
  */
 struct commit_info {
-       const char *author;
-       const char *author_mail;
+       struct strbuf author;
+       struct strbuf author_mail;
        unsigned long author_time;
-       const char *author_tz;
+       struct strbuf author_tz;
 
        /* filled only when asked for details */
-       const char *committer;
-       const char *committer_mail;
+       struct strbuf committer;
+       struct strbuf committer_mail;
        unsigned long committer_time;
-       const char *committer_tz;
+       struct strbuf committer_tz;
 
-       const char *summary;
+       struct strbuf summary;
 };
 
 /*
  * Parse author/committer line in the commit object buffer
  */
 static void get_ac_line(const char *inbuf, const char *what,
-                       int person_len, char *person,
-                       int mail_len, char *mail,
-                       unsigned long *time, const char **tz)
+       struct strbuf *name, struct strbuf *mail,
+       unsigned long *time, struct strbuf *tz)
 {
-       int len, tzlen, maillen;
-       char *tmp, *endp, *timepos, *mailpos;
+       struct ident_split ident;
+       size_t len, maillen, namelen;
+       char *tmp, *endp;
+       const char *namebuf, *mailbuf;
 
        tmp = strstr(inbuf, what);
        if (!tmp)
@@ -1356,69 +1357,61 @@ static void get_ac_line(const char *inbuf, const char *what,
                len = strlen(tmp);
        else
                len = endp - tmp;
-       if (person_len <= len) {
+
+       if (split_ident_line(&ident, tmp, len)) {
        error_out:
                /* Ugh */
-               *tz = "(unknown)";
-               strcpy(person, *tz);
-               strcpy(mail, *tz);
+               tmp = "(unknown)";
+               strbuf_addstr(name, tmp);
+               strbuf_addstr(mail, tmp);
+               strbuf_addstr(tz, tmp);
                *time = 0;
                return;
        }
-       memcpy(person, tmp, len);
 
-       tmp = person;
-       tmp += len;
-       *tmp = 0;
-       while (person < tmp && *tmp != ' ')
-               tmp--;
-       if (tmp <= person)
-               goto error_out;
-       *tz = tmp+1;
-       tzlen = (person+len)-(tmp+1);
+       namelen = ident.name_end - ident.name_begin;
+       namebuf = ident.name_begin;
 
-       *tmp = 0;
-       while (person < tmp && *tmp != ' ')
-               tmp--;
-       if (tmp <= person)
-               goto error_out;
-       *time = strtoul(tmp, NULL, 10);
-       timepos = tmp;
+       maillen = ident.mail_end - ident.mail_begin;
+       mailbuf = ident.mail_begin;
 
-       *tmp = 0;
-       while (person < tmp && !(*tmp == ' ' && tmp[1] == '<'))
-               tmp--;
-       if (tmp <= person)
-               return;
-       mailpos = tmp + 1;
-       *tmp = 0;
-       maillen = timepos - tmp;
-       memcpy(mail, mailpos, maillen);
+       *time = strtoul(ident.date_begin, NULL, 10);
 
-       if (!mailmap.nr)
-               return;
-
-       /*
-        * mailmap expansion may make the name longer.
-        * make room by pushing stuff down.
-        */
-       tmp = person + person_len - (tzlen + 1);
-       memmove(tmp, *tz, tzlen);
-       tmp[tzlen] = 0;
-       *tz = tmp;
+       len = ident.tz_end - ident.tz_begin;
+       strbuf_add(tz, ident.tz_begin, len);
 
        /*
         * Now, convert both name and e-mail using mailmap
         */
-       if (map_user(&mailmap, mail+1, mail_len-1, person, tmp-person-1)) {
-               /* Add a trailing '>' to email, since map_user returns plain emails
-                  Note: It already has '<', since we replace from mail+1 */
-               mailpos = memchr(mail, '\0', mail_len);
-               if (mailpos && mailpos-mail < mail_len - 1) {
-                       *mailpos = '>';
-                       *(mailpos+1) = '\0';
-               }
-       }
+       map_user(&mailmap, &mailbuf, &maillen,
+                &namebuf, &namelen);
+
+       strbuf_addf(mail, "<%.*s>", (int)maillen, mailbuf);
+       strbuf_add(name, namebuf, namelen);
+}
+
+static void commit_info_init(struct commit_info *ci)
+{
+
+       strbuf_init(&ci->author, 0);
+       strbuf_init(&ci->author_mail, 0);
+       strbuf_init(&ci->author_tz, 0);
+       strbuf_init(&ci->committer, 0);
+       strbuf_init(&ci->committer_mail, 0);
+       strbuf_init(&ci->committer_tz, 0);
+       strbuf_init(&ci->summary, 0);
+}
+
+static void commit_info_destroy(struct commit_info *ci)
+{
+
+       strbuf_release(&ci->author);
+       strbuf_release(&ci->author_mail);
+       strbuf_release(&ci->author_tz);
+       strbuf_release(&ci->committer);
+       strbuf_release(&ci->committer_mail);
+       strbuf_release(&ci->committer_tz);
+       strbuf_release(&ci->summary);
 }
 
 static void get_commit_info(struct commit *commit,
@@ -1428,11 +1421,8 @@ static void get_commit_info(struct commit *commit,
        int len;
        const char *subject, *encoding;
        char *reencoded, *message;
-       static char author_name[1024];
-       static char author_mail[1024];
-       static char committer_name[1024];
-       static char committer_mail[1024];
-       static char summary_buf[1024];
+
+       commit_info_init(ret);
 
        /*
         * We've operated without save_commit_buffer, so
@@ -1450,11 +1440,8 @@ static void get_commit_info(struct commit *commit,
        encoding = get_log_output_encoding();
        reencoded = logmsg_reencode(commit, encoding);
        message   = reencoded ? reencoded : commit->buffer;
-       ret->author = author_name;
-       ret->author_mail = author_mail;
        get_ac_line(message, "\nauthor ",
-                   sizeof(author_name), author_name,
-                   sizeof(author_mail), author_mail,
+                   &ret->author, &ret->author_mail,
                    &ret->author_time, &ret->author_tz);
 
        if (!detailed) {
@@ -1462,21 +1449,16 @@ static void get_commit_info(struct commit *commit,
                return;
        }
 
-       ret->committer = committer_name;
-       ret->committer_mail = committer_mail;
        get_ac_line(message, "\ncommitter ",
-                   sizeof(committer_name), committer_name,
-                   sizeof(committer_mail), committer_mail,
+                   &ret->committer, &ret->committer_mail,
                    &ret->committer_time, &ret->committer_tz);
 
-       ret->summary = summary_buf;
        len = find_commit_subject(message, &subject);
-       if (len && len < sizeof(summary_buf)) {
-               memcpy(summary_buf, subject, len);
-               summary_buf[len] = 0;
-       } else {
-               sprintf(summary_buf, "(%s)", sha1_to_hex(commit->object.sha1));
-       }
+       if (len)
+               strbuf_add(&ret->summary, subject, len);
+       else
+               strbuf_addf(&ret->summary, "(%s)", sha1_to_hex(commit->object.sha1));
+
        free(reencoded);
 }
 
@@ -1505,15 +1487,15 @@ static int emit_one_suspect_detail(struct origin *suspect, int repeat)
 
        suspect->commit->object.flags |= METAINFO_SHOWN;
        get_commit_info(suspect->commit, &ci, 1);
-       printf("author %s\n", ci.author);
-       printf("author-mail %s\n", ci.author_mail);
+       printf("author %s\n", ci.author.buf);
+       printf("author-mail %s\n", ci.author_mail.buf);
        printf("author-time %lu\n", ci.author_time);
-       printf("author-tz %s\n", ci.author_tz);
-       printf("committer %s\n", ci.committer);
-       printf("committer-mail %s\n", ci.committer_mail);
+       printf("author-tz %s\n", ci.author_tz.buf);
+       printf("committer %s\n", ci.committer.buf);
+       printf("committer-mail %s\n", ci.committer_mail.buf);
        printf("committer-time %lu\n", ci.committer_time);
-       printf("committer-tz %s\n", ci.committer_tz);
-       printf("summary %s\n", ci.summary);
+       printf("committer-tz %s\n", ci.committer_tz.buf);
+       printf("summary %s\n", ci.summary.buf);
        if (suspect->commit->object.flags & UNINTERESTING)
                printf("boundary\n");
        if (suspect->previous) {
@@ -1521,6 +1503,9 @@ static int emit_one_suspect_detail(struct origin *suspect, int repeat)
                printf("previous %s ", sha1_to_hex(prev->commit->object.sha1));
                write_name_quoted(prev->path, stdout, '\n');
        }
+
+       commit_info_destroy(&ci);
+
        return 1;
 }
 
@@ -1707,11 +1692,11 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
                if (opt & OUTPUT_ANNOTATE_COMPAT) {
                        const char *name;
                        if (opt & OUTPUT_SHOW_EMAIL)
-                               name = ci.author_mail;
+                               name = ci.author_mail.buf;
                        else
-                               name = ci.author;
+                               name = ci.author.buf;
                        printf("\t(%10s\t%10s\t%d)", name,
-                              format_time(ci.author_time, ci.author_tz,
+                              format_time(ci.author_time, ci.author_tz.buf,
                                           show_raw_time),
                               ent->lno + 1 + cnt);
                } else {
@@ -1730,14 +1715,14 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
                                const char *name;
                                int pad;
                                if (opt & OUTPUT_SHOW_EMAIL)
-                                       name = ci.author_mail;
+                                       name = ci.author_mail.buf;
                                else
-                                       name = ci.author;
+                                       name = ci.author.buf;
                                pad = longest_author - utf8_strwidth(name);
                                printf(" (%s%*s %10s",
                                       name, pad, "",
                                       format_time(ci.author_time,
-                                                  ci.author_tz,
+                                                  ci.author_tz.buf,
                                                   show_raw_time));
                        }
                        printf(" %*d) ",
@@ -1752,6 +1737,8 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
 
        if (sb->final_buf_size && cp[-1] != '\n')
                putchar('\n');
+
+       commit_info_destroy(&ci);
 }
 
 static void output(struct scoreboard *sb, int option)
@@ -1876,9 +1863,9 @@ static void find_alignment(struct scoreboard *sb, int *option)
                        suspect->commit->object.flags |= METAINFO_SHOWN;
                        get_commit_info(suspect->commit, &ci, 1);
                        if (*option & OUTPUT_SHOW_EMAIL)
-                               num = utf8_strwidth(ci.author_mail);
+                               num = utf8_strwidth(ci.author_mail.buf);
                        else
-                               num = utf8_strwidth(ci.author);
+                               num = utf8_strwidth(ci.author.buf);
                        if (longest_author < num)
                                longest_author = num;
                }
@@ -1890,6 +1877,8 @@ static void find_alignment(struct scoreboard *sb, int *option)
                        longest_dst_lines = num;
                if (largest_score < ent_score(sb, e))
                        largest_score = ent_score(sb, e);
+
+               commit_info_destroy(&ci);
        }
        max_orig_digits = decimal_width(longest_src_lines);
        max_digits = decimal_width(longest_dst_lines);
index 69c1cda9061f248479377d2841a1ddf703ea59ba..f4b760bf3dcb2371da6ae9d77f2f8fb1c0fa9eb9 100644 (file)
@@ -10,6 +10,7 @@
 #include "cache.h"
 #include "dir.h"
 #include "parse-options.h"
+#include "refs.h"
 #include "string-list.h"
 #include "quote.h"
 
@@ -20,6 +21,12 @@ static const char *const builtin_clean_usage[] = {
        NULL
 };
 
+static const char *msg_remove = N_("Removing %s\n");
+static const char *msg_would_remove = N_("Would remove %s\n");
+static const char *msg_skip_git_dir = N_("Skipping repository %s\n");
+static const char *msg_would_skip_git_dir = N_("Would skip repository %s\n");
+static const char *msg_warn_remove_failed = N_("failed to remove %s");
+
 static int git_clean_config(const char *var, const char *value, void *cb)
 {
        if (!strcmp(var, "clean.requireforce"))
@@ -34,11 +41,112 @@ static int exclude_cb(const struct option *opt, const char *arg, int unset)
        return 0;
 }
 
+static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
+               int dry_run, int quiet, int *dir_gone)
+{
+       DIR *dir;
+       struct strbuf quoted = STRBUF_INIT;
+       struct dirent *e;
+       int res = 0, ret = 0, gone = 1, original_len = path->len, len, i;
+       unsigned char submodule_head[20];
+       struct string_list dels = STRING_LIST_INIT_DUP;
+
+       *dir_gone = 1;
+
+       if ((force_flag & REMOVE_DIR_KEEP_NESTED_GIT) &&
+                       !resolve_gitlink_ref(path->buf, "HEAD", submodule_head)) {
+               if (!quiet) {
+                       quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+                       printf(dry_run ?  _(msg_would_skip_git_dir) : _(msg_skip_git_dir),
+                                       quoted.buf);
+               }
+
+               *dir_gone = 0;
+               return 0;
+       }
+
+       dir = opendir(path->buf);
+       if (!dir) {
+               /* an empty dir could be removed even if it is unreadble */
+               res = dry_run ? 0 : rmdir(path->buf);
+               if (res) {
+                       quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+                       warning(_(msg_warn_remove_failed), quoted.buf);
+                       *dir_gone = 0;
+               }
+               return res;
+       }
+
+       if (path->buf[original_len - 1] != '/')
+               strbuf_addch(path, '/');
+
+       len = path->len;
+       while ((e = readdir(dir)) != NULL) {
+               struct stat st;
+               if (is_dot_or_dotdot(e->d_name))
+                       continue;
+
+               strbuf_setlen(path, len);
+               strbuf_addstr(path, e->d_name);
+               if (lstat(path->buf, &st))
+                       ; /* fall thru */
+               else if (S_ISDIR(st.st_mode)) {
+                       if (remove_dirs(path, prefix, force_flag, dry_run, quiet, &gone))
+                               ret = 1;
+                       if (gone) {
+                               quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+                               string_list_append(&dels, quoted.buf);
+                       } else
+                               *dir_gone = 0;
+                       continue;
+               } else {
+                       res = dry_run ? 0 : unlink(path->buf);
+                       if (!res) {
+                               quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+                               string_list_append(&dels, quoted.buf);
+                       } else {
+                               quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+                               warning(_(msg_warn_remove_failed), quoted.buf);
+                               *dir_gone = 0;
+                               ret = 1;
+                       }
+                       continue;
+               }
+
+               /* path too long, stat fails, or non-directory still exists */
+               *dir_gone = 0;
+               ret = 1;
+               break;
+       }
+       closedir(dir);
+
+       strbuf_setlen(path, original_len);
+
+       if (*dir_gone) {
+               res = dry_run ? 0 : rmdir(path->buf);
+               if (!res)
+                       *dir_gone = 1;
+               else {
+                       quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+                       warning(_(msg_warn_remove_failed), quoted.buf);
+                       *dir_gone = 0;
+                       ret = 1;
+               }
+       }
+
+       if (!*dir_gone && !quiet) {
+               for (i = 0; i < dels.nr; i++)
+                       printf(dry_run ?  _(msg_would_remove) : _(msg_remove), dels.items[i].string);
+       }
+       string_list_clear(&dels, 0);
+       return ret;
+}
+
 int cmd_clean(int argc, const char **argv, const char *prefix)
 {
-       int i;
-       int show_only = 0, remove_directories = 0, quiet = 0, ignored = 0;
-       int ignored_only = 0, config_set = 0, errors = 0;
+       int i, res;
+       int dry_run = 0, remove_directories = 0, quiet = 0, ignored = 0;
+       int ignored_only = 0, config_set = 0, errors = 0, gone = 1;
        int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT;
        struct strbuf directory = STRBUF_INIT;
        struct dir_struct dir;
@@ -49,7 +157,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
        char *seen = NULL;
        struct option options[] = {
                OPT__QUIET(&quiet, N_("do not print names of files removed")),
-               OPT__DRY_RUN(&show_only, N_("dry run")),
+               OPT__DRY_RUN(&dry_run, N_("dry run")),
                OPT__FORCE(&force, N_("force")),
                OPT_BOOLEAN('d', NULL, &remove_directories,
                                N_("remove whole directories")),
@@ -77,7 +185,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
        if (ignored && ignored_only)
                die(_("-x and -X cannot be used together"));
 
-       if (!show_only && !force) {
+       if (!dry_run && !force) {
                if (config_set)
                        die(_("clean.requireForce set to true and neither -n nor -f given; "
                                  "refusing to clean"));
@@ -149,38 +257,26 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 
                if (S_ISDIR(st.st_mode)) {
                        strbuf_addstr(&directory, ent->name);
-                       qname = quote_path_relative(directory.buf, directory.len, &buf, prefix);
-                       if (show_only && (remove_directories ||
-                           (matches == MATCHED_EXACTLY))) {
-                               printf(_("Would remove %s\n"), qname);
-                       } else if (remove_directories ||
-                                  (matches == MATCHED_EXACTLY)) {
-                               if (!quiet)
-                                       printf(_("Removing %s\n"), qname);
-                               if (remove_dir_recursively(&directory,
-                                                          rm_flags) != 0) {
-                                       warning(_("failed to remove %s"), qname);
+                       if (remove_directories || (matches == MATCHED_EXACTLY)) {
+                               if (remove_dirs(&directory, prefix, rm_flags, dry_run, quiet, &gone))
                                        errors++;
+                               if (gone && !quiet) {
+                                       qname = quote_path_relative(directory.buf, directory.len, &buf, prefix);
+                                       printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
                                }
-                       } else if (show_only) {
-                               printf(_("Would not remove %s\n"), qname);
-                       } else {
-                               printf(_("Not removing %s\n"), qname);
                        }
                        strbuf_reset(&directory);
                } else {
                        if (pathspec && !matches)
                                continue;
-                       qname = quote_path_relative(ent->name, -1, &buf, prefix);
-                       if (show_only) {
-                               printf(_("Would remove %s\n"), qname);
-                               continue;
-                       } else if (!quiet) {
-                               printf(_("Removing %s\n"), qname);
-                       }
-                       if (unlink(ent->name) != 0) {
-                               warning(_("failed to remove %s"), qname);
+                       res = dry_run ? 0 : unlink(ent->name);
+                       if (res) {
+                               qname = quote_path_relative(ent->name, -1, &buf, prefix);
+                               warning(_(msg_warn_remove_failed), qname);
                                errors++;
+                       } else if (!quiet) {
+                               qname = quote_path_relative(ent->name, -1, &buf, prefix);
+                               printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
                        }
                }
        }
index 8d23a62e8a1b875f1415f78a4c283f4cdefac011..36ec99db3f5a7dbd59705aa68bcd592c08c773cd 100644 (file)
@@ -704,6 +704,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                if (option_origin)
                        die(_("--bare and --origin %s options are incompatible."),
                            option_origin);
+               if (real_git_dir)
+                       die(_("--bare and --separate-git-dir are incompatible."));
                option_no_checkout = 1;
        }
 
index d6dd3df8b1e1f449e7781bdb033f5cfa11753436..7c2a3d48b4ac6bf6f55539946adacb433b51dabb 100644 (file)
@@ -103,7 +103,7 @@ static enum {
        CLEANUP_NONE,
        CLEANUP_ALL
 } cleanup_mode;
-static char *cleanup_arg;
+static const char *cleanup_arg;
 
 static enum commit_whence whence;
 static int use_editor = 1, include_status = 1;
@@ -1320,6 +1320,8 @@ static int git_commit_config(const char *k, const char *v, void *cb)
                include_status = git_config_bool(k, v);
                return 0;
        }
+       if (!strcmp(k, "commit.cleanup"))
+               return git_config_string(&cleanup_arg, k, v);
 
        status = git_gpg_config(k, v, NULL);
        if (status)
index 5a4055eb0b0d4965785b6f69dbe92efe251c54dd..8f0b2e84fef5d1b9c07ea8846c9fbc1318d8d51b 100644 (file)
@@ -22,6 +22,7 @@
 #include "branch.h"
 #include "streaming.h"
 #include "version.h"
+#include "mailmap.h"
 
 /* Set a default date-time format for git log ("log.date" config variable) */
 static const char *default_date_mode = NULL;
@@ -30,6 +31,7 @@ static int default_abbrev_commit;
 static int default_show_root = 1;
 static int decoration_style;
 static int decoration_given;
+static int use_mailmap_config;
 static const char *fmt_patch_subject_prefix = "PATCH";
 static const char *fmt_pretty;
 
@@ -94,16 +96,18 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
                         struct rev_info *rev, struct setup_revision_opt *opt)
 {
        struct userformat_want w;
-       int quiet = 0, source = 0;
+       int quiet = 0, source = 0, mailmap = 0;
 
        const struct option builtin_log_options[] = {
                OPT_BOOLEAN(0, "quiet", &quiet, N_("suppress diff output")),
                OPT_BOOLEAN(0, "source", &source, N_("show source")),
+               OPT_BOOLEAN(0, "use-mailmap", &mailmap, N_("Use mail map file")),
                { OPTION_CALLBACK, 0, "decorate", NULL, NULL, N_("decorate options"),
                  PARSE_OPT_OPTARG, decorate_callback},
                OPT_END()
        };
 
+       mailmap = use_mailmap_config;
        argc = parse_options(argc, argv, prefix,
                             builtin_log_options, builtin_log_usage,
                             PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN |
@@ -136,6 +140,11 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
        if (source)
                rev->show_source = 1;
 
+       if (mailmap) {
+               rev->mailmap = xcalloc(1, sizeof(struct string_list));
+               read_mailmap(rev->mailmap, NULL);
+       }
+
        if (rev->pretty_given && rev->commit_format == CMIT_FMT_RAW) {
                /*
                 * "log --pretty=raw" is special; ignore UI oriented
@@ -351,6 +360,11 @@ static int git_log_config(const char *var, const char *value, void *cb)
        }
        if (!prefixcmp(var, "color.decorate."))
                return parse_decorate_color_config(var, 15, value);
+       if (!strcmp(var, "log.mailmap")) {
+               use_mailmap_config = git_config_bool(var, value);
+               return 0;
+       }
+
        if (grep_config(var, value, cb) < 0)
                return -1;
        return git_diff_ui_config(var, value, cb);
index 83605143ac0cea68b421dd9a3c056d3a27f66be9..240bff3efa80b3af087fe1e6c16a75108ffee4de 100644 (file)
@@ -36,52 +36,28 @@ static void insert_one_record(struct shortlog *log,
        const char *dot3 = log->common_repo_prefix;
        char *buffer, *p;
        struct string_list_item *item;
-       char namebuf[1024];
-       char emailbuf[1024];
-       size_t len;
+       const char *mailbuf, *namebuf;
+       size_t namelen, maillen;
        const char *eol;
-       const char *boemail, *eoemail;
        struct strbuf subject = STRBUF_INIT;
+       struct strbuf namemailbuf = STRBUF_INIT;
+       struct ident_split ident;
 
-       boemail = strchr(author, '<');
-       if (!boemail)
-               return;
-       eoemail = strchr(boemail, '>');
-       if (!eoemail)
+       if (split_ident_line(&ident, author, strlen(author)))
                return;
 
-       /* copy author name to namebuf, to support matching on both name and email */
-       memcpy(namebuf, author, boemail - author);
-       len = boemail - author;
-       while (len > 0 && isspace(namebuf[len-1]))
-               len--;
-       namebuf[len] = 0;
-
-       /* copy email name to emailbuf, to allow email replacement as well */
-       memcpy(emailbuf, boemail+1, eoemail - boemail);
-       emailbuf[eoemail - boemail - 1] = 0;
-
-       if (!map_user(&log->mailmap, emailbuf, sizeof(emailbuf), namebuf, sizeof(namebuf))) {
-               while (author < boemail && isspace(*author))
-                       author++;
-               for (len = 0;
-                    len < sizeof(namebuf) - 1 && author + len < boemail;
-                    len++)
-                       namebuf[len] = author[len];
-               while (0 < len && isspace(namebuf[len-1]))
-                       len--;
-               namebuf[len] = '\0';
-       }
-       else
-               len = strlen(namebuf);
+       namebuf = ident.name_begin;
+       mailbuf = ident.mail_begin;
+       namelen = ident.name_end - ident.name_begin;
+       maillen = ident.mail_end - ident.mail_begin;
 
-       if (log->email) {
-               size_t room = sizeof(namebuf) - len - 1;
-               int maillen = strlen(emailbuf);
-               snprintf(namebuf + len, room, " <%.*s>", maillen, emailbuf);
-       }
+       map_user(&log->mailmap, &mailbuf, &maillen, &namebuf, &namelen);
+       strbuf_add(&namemailbuf, namebuf, namelen);
+
+       if (log->email)
+               strbuf_addf(&namemailbuf, " <%.*s>", (int)maillen, mailbuf);
 
-       item = string_list_insert(&log->list, namebuf);
+       item = string_list_insert(&log->list, namemailbuf.buf);
        if (item->util == NULL)
                item->util = xcalloc(1, sizeof(struct string_list));
 
index 0f469e507db7b8517e4f37107d855b7877176085..c16c8a75349f2ef4dba1f3c05528d04750a925da 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -89,6 +89,7 @@ struct pretty_print_context {
        char *notes_message;
        struct reflog_walk_info *reflog_info;
        const char *output_encoding;
+       struct string_list *mailmap;
        int color;
 };
 
index a4c48e179eb778077462a812b491e762e284486c..14dd5e7ca272350b3fa13d6fc8da3ccb46e12227 100644 (file)
@@ -397,7 +397,7 @@ __git_complete_revlist_file ()
                *)   pfx="$ref:$pfx" ;;
                esac
 
-               __gitcomp_nl "$(git --git-dir="$(__gitdir)" ls-tree "$ls" \
+               __gitcomp_nl "$(git --git-dir="$(__gitdir)" ls-tree "$ls" 2>/dev/null \
                                | sed '/^100... blob /{
                                           s,^.*        ,,
                                           s,$, ,
index 8aafb63315c52a08e6f581e0ef13aa9c239b03a8..3e3889f2b4fd3acb441e56284750cb22fef80547 100644 (file)
@@ -13,6 +13,7 @@
 #
 # To use this completion script:
 #
+#    0) You need tcsh 6.16.00 or newer.
 #    1) Copy both this file and the bash completion script to ${HOME}.
 #       You _must_ use the name ${HOME}/.git-completion.bash for the
 #       bash script.
 #        set autolist=ambiguous
 #       It will tell tcsh to list the possible completion choices.
 
+set __git_tcsh_completion_version = `\echo ${tcsh} | \sed 's/\./ /g'`
+if ( ${__git_tcsh_completion_version[1]} < 6 || \
+     ( ${__git_tcsh_completion_version[1]} == 6 && \
+       ${__git_tcsh_completion_version[2]} < 16 ) ) then
+       echo "git-completion.tcsh: Your version of tcsh is too old, you need version 6.16.00 or newer.  Git completion will not work."
+       exit
+endif
+unset __git_tcsh_completion_version
+
 set __git_tcsh_completion_original_script = ${HOME}/.git-completion.bash
 set __git_tcsh_completion_script = ${HOME}/.git-completion.tcsh.bash
 
@@ -64,9 +74,7 @@ fi
 _\${1}
 
 IFS=\$'\n'
-if [ \${#COMPREPLY[*]} -gt 0 ]; then
-       echo "\${COMPREPLY[*]}" | sort | uniq
-else
+if [ \${#COMPREPLY[*]} -eq 0 ]; then
        # No completions suggested.  In this case, we want tcsh to perform
        # standard file completion.  However, there does not seem to be way
        # to tell tcsh to do that.  To help the user, we try to simulate
@@ -85,19 +93,20 @@ else
                # We don't support ~ expansion: too tricky.
                if [ "\${TO_COMPLETE:0:1}" != "~" ]; then
                        # Use ls so as to add the '/' at the end of directories.
-                       RESULT=(\`ls -dp \${TO_COMPLETE}* 2> /dev/null\`)
-                       echo \${RESULT[*]}
-
-                       # If there is a single completion and it is a directory,
-                       # we output it a second time to trick tcsh into not adding a space
-                       # after it.
-                       if [ \${#RESULT[*]} -eq 1 ] && [ "\${RESULT[0]: -1}" == "/" ]; then
-                               echo \${RESULT[*]}
-                       fi
+                       COMPREPLY=(\`ls -dp \${TO_COMPLETE}* 2> /dev/null\`)
                fi
        fi
 fi
 
+# tcsh does not automatically remove duplicates, so we do it ourselves
+echo "\${COMPREPLY[*]}" | sort | uniq
+
+# If there is a single completion and it is a directory, we output it
+# a second time to trick tcsh into not adding a space after it.
+if [ \${#COMPREPLY[*]} -eq 1 ] && [ "\${COMPREPLY[0]: -1}" == "/" ]; then
+       echo "\${COMPREPLY[*]}"
+fi
+
 EOF
 
 # Don't need this variable anymore, so don't pollute the users environment
index 5974976f028d6d27ee5c69b1afb6823ed0509b1a..328c2dc76dd33e47cfe69064757631e9717771e6 100755 (executable)
@@ -720,6 +720,14 @@ def do_export(parser):
     if peer:
         parser.repo.push(peer, force=False)
 
+def fix_path(alias, repo, orig_url):
+    repo_url = util.url(repo.url())
+    url = util.url(orig_url)
+    if str(url) == str(repo_url):
+        return
+    cmd = ['git', 'config', 'remote.%s.url' % alias, "hg::%s" % repo_url]
+    subprocess.call(cmd)
+
 def main(args):
     global prefix, dirname, branches, bmarks
     global marks, blob_marks, parsed_refs
@@ -766,6 +774,9 @@ def main(args):
     repo = get_repo(url, alias)
     prefix = 'refs/hg/%s' % alias
 
+    if not is_tmp:
+        fix_path(alias, peer or repo, url)
+
     if not os.path.exists(dirname):
         os.makedirs(dirname)
 
index fca1e17251f1f414a1d97bf6e6240bdf20daa648..8f16d069721c5b9885b2e700981618878b54191e 100644 (file)
@@ -17,16 +17,6 @@ To install:
 
   1. Copy these files to vim's syntax directory $HOME/.vim/syntax
   2. To auto-detect the editing of various git-related filetypes:
-       $ cat >>$HOME/.vim/filetype.vim <<'EOF'
-       autocmd BufNewFile,BufRead *.git/COMMIT_EDITMSG    setf gitcommit
-       autocmd BufNewFile,BufRead *.git/config,.gitconfig setf gitconfig
-       autocmd BufNewFile,BufRead git-rebase-todo         setf gitrebase
-       autocmd BufNewFile,BufRead .msg.[0-9]*
-               \ if getline(1) =~ '^From.*# This line is ignored.$' |
-               \   setf gitsendemail |
-               \ endif
-       autocmd BufNewFile,BufRead *.git/**
-               \ if getline(1) =~ '^\x\{40\}\>\|^ref: ' |
-               \   setf git |
-               \ endif
-       EOF
+
+       $ curl http://ftp.vim.org/pub/vim/runtime/filetype.vim |
+               sed -ne '/^" Git$/, /^$/ p' >>$HOME/.vim/filetype.vim
index 44901d53c43d972e03a71bfbe0b769f2e8f22d7b..8ed7fccc18507b6f3e2508bb117ce056a764de77 100644 (file)
@@ -190,6 +190,11 @@ is_empty_commit() {
        test "$tree" = "$ptree"
 }
 
+is_merge_commit()
+{
+       git rev-parse --verify --quiet "$1"^2 >/dev/null 2>&1
+}
+
 # Run command with GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and
 # GIT_AUTHOR_DATE exported from the current environment.
 do_with_author () {
@@ -874,7 +879,7 @@ git rev-list $merges_option --pretty=oneline --abbrev-commit \
 while read -r shortsha1 rest
 do
 
-       if test -z "$keep_empty" && is_empty_commit $shortsha1
+       if test -z "$keep_empty" && is_empty_commit $shortsha1 && ! is_merge_commit $shortsha1
        then
                comment_out="# "
        else
index bd5266c86b299bbc75878b7529820798d194fc72..d0866946cee0076dd167cf6234dfec192d8d194e 100755 (executable)
@@ -114,6 +114,7 @@ sub _req_svn {
        $_message, $_file, $_branch_dest,
        $_template, $_shared,
        $_version, $_fetch_all, $_no_rebase, $_fetch_parent,
+       $_before, $_after,
        $_merge, $_strategy, $_preserve_merges, $_dry_run, $_local,
        $_prefix, $_no_checkout, $_url, $_verbose,
        $_commit_url, $_tag, $_merge_info, $_interactive);
@@ -258,7 +259,8 @@ sub _req_svn {
                        } ],
        'find-rev' => [ \&cmd_find_rev,
                        "Translate between SVN revision numbers and tree-ish",
-                       {} ],
+                       { 'before' => \$_before,
+                         'after' => \$_after } ],
        'rebase' => [ \&cmd_rebase, "Fetch and rebase your working directory",
                        { 'merge|m|M' => \$_merge,
                          'verbose|v' => \$_verbose,
@@ -1191,7 +1193,13 @@ sub cmd_find_rev {
                            "$head history\n";
                }
                my $desired_revision = substr($revision_or_hash, 1);
-               $result = $gs->rev_map_get($desired_revision, $uuid);
+               if ($_before) {
+                       $result = $gs->find_rev_before($desired_revision, 1);
+               } elsif ($_after) {
+                       $result = $gs->find_rev_after($desired_revision, 1);
+               } else {
+                       $result = $gs->rev_map_get($desired_revision, $uuid);
+               }
        } else {
                my (undef, $rev, undef) = cmt_metadata($revision_or_hash);
                $result = $rev;
index f8487f8a8a7694ce09f1b079c701bceabf688b20..5dc45c4812bdfd0d7a6b71d529eaa37df8178186 100644 (file)
@@ -681,6 +681,7 @@ void show_log(struct rev_info *opt)
        ctx.preserve_subject = opt->preserve_subject;
        ctx.reflog_info = opt->reflog_info;
        ctx.fmt = opt->commit_format;
+       ctx.mailmap = opt->mailmap;
        ctx.color = opt->diffopt.use_color;
        pretty_print_commit(&ctx, commit, &msgbuf);
 
index b16542febec14ed86fd7ef21ba19899d08c95a64..2a7b36628cb5623eb0ffb95bfb54e52a6dd300f4 100644 (file)
--- a/mailmap.c
+++ b/mailmap.c
@@ -235,6 +235,7 @@ int read_mailmap(struct string_list *map, char **repo_abbrev)
        int err = 0;
 
        map->strdup_strings = 1;
+       map->cmp = strcasecmp;
 
        if (!git_mailmap_blob && is_bare_repository())
                git_mailmap_blob = "HEAD:.mailmap";
@@ -253,60 +254,95 @@ void clear_mailmap(struct string_list *map)
        debug_mm("mailmap: cleared\n");
 }
 
+/*
+ * Look for an entry in map that match string[0:len]; string[len]
+ * does not have to be NUL (but it could be).
+ */
+static struct string_list_item *lookup_prefix(struct string_list *map,
+                                             const char *string, size_t len)
+{
+       int i = string_list_find_insert_index(map, string, 1);
+       if (i < 0) {
+               /* exact match */
+               i = -1 - i;
+               if (!string[len])
+                       return &map->items[i];
+               /*
+                * that map entry matches exactly to the string, including
+                * the cruft at the end beyond "len".  That is not a match
+                * with string[0:len] that we are looking for.
+                */
+       } else if (!string[len]) {
+               /*
+                * asked with the whole string, and got nothing.  No
+                * matching entry can exist in the map.
+                */
+               return NULL;
+       }
+
+       /*
+        * i is at the exact match to an overlong key, or location the
+        * overlong key would be inserted, which must come after the
+        * real location of the key if one exists.
+        */
+       while (0 <= --i && i < map->nr) {
+               int cmp = strncasecmp(map->items[i].string, string, len);
+               if (cmp < 0)
+                       /*
+                        * "i" points at a key definitely below the prefix;
+                        * the map does not have string[0:len] in it.
+                        */
+                       break;
+               else if (!cmp && !map->items[i].string[len])
+                       /* found it */
+                       return &map->items[i];
+               /*
+                * otherwise, the string at "i" may be string[0:len]
+                * followed by a string that sorts later than string[len:];
+                * keep trying.
+                */
+       }
+       return NULL;
+}
+
 int map_user(struct string_list *map,
-            char *email, int maxlen_email, char *name, int maxlen_name)
+                        const char **email, size_t *emaillen,
+                        const char **name, size_t *namelen)
 {
-       char *end_of_email;
        struct string_list_item *item;
        struct mailmap_entry *me;
-       char buf[1024], *mailbuf;
-       int i;
-
-       /* figure out space requirement for email */
-       end_of_email = strchr(email, '>');
-       if (!end_of_email) {
-               /* email passed in might not be wrapped in <>, but end with a \0 */
-               end_of_email = memchr(email, '\0', maxlen_email);
-               if (!end_of_email)
-                       return 0;
-       }
-       if (end_of_email - email + 1 < sizeof(buf))
-               mailbuf = buf;
-       else
-               mailbuf = xmalloc(end_of_email - email + 1);
-
-       /* downcase the email address */
-       for (i = 0; i < end_of_email - email; i++)
-               mailbuf[i] = tolower(email[i]);
-       mailbuf[i] = 0;
-
-       debug_mm("map_user: map '%s' <%s>\n", name, mailbuf);
-       item = string_list_lookup(map, mailbuf);
+
+       debug_mm("map_user: map '%.*s' <%.*s>\n",
+                *name, *namelen, *emaillen, *email);
+
+       item = lookup_prefix(map, *email, *emaillen);
        if (item != NULL) {
                me = (struct mailmap_entry *)item->util;
                if (me->namemap.nr) {
                        /* The item has multiple items, so we'll look up on name too */
                        /* If the name is not found, we choose the simple entry      */
-                       struct string_list_item *subitem = string_list_lookup(&me->namemap, name);
+                       struct string_list_item *subitem;
+                       subitem = lookup_prefix(&me->namemap, *name, *namelen);
                        if (subitem)
                                item = subitem;
                }
        }
-       if (mailbuf != buf)
-               free(mailbuf);
        if (item != NULL) {
                struct mailmap_info *mi = (struct mailmap_info *)item->util;
-               if (mi->name == NULL && (mi->email == NULL || maxlen_email == 0)) {
+               if (mi->name == NULL && mi->email == NULL) {
                        debug_mm("map_user:  -- (no simple mapping)\n");
                        return 0;
                }
-               if (maxlen_email && mi->email)
-                       strlcpy(email, mi->email, maxlen_email);
-               else
-                       *end_of_email = '\0';
-               if (maxlen_name && mi->name)
-                       strlcpy(name, mi->name, maxlen_name);
-               debug_mm("map_user:  to '%s' <%s>\n", name, mi->email ? mi->email : "");
+               if (mi->email) {
+                               *email = mi->email;
+                               *emaillen = strlen(*email);
+               }
+               if (mi->name) {
+                               *name = mi->name;
+                               *namelen = strlen(*name);
+               }
+               debug_mm("map_user:  to '%.*s' <.*%s>\n", *namelen, *name,
+                                *emaillen, *email);
                return 1;
        }
        debug_mm("map_user:  --\n");
index d5c3664322023d6552cb24006d833aa08224ff6f..ed7c93b05c3cc904c3052bae199346a698eec1e6 100644 (file)
--- a/mailmap.h
+++ b/mailmap.h
@@ -4,7 +4,7 @@
 int read_mailmap(struct string_list *map, char **repo_abbrev);
 void clear_mailmap(struct string_list *map);
 
-int map_user(struct string_list *mailmap,
-            char *email, int maxlen_email, char *name, int maxlen_name);
+int map_user(struct string_list *map,
+                        const char **email, size_t *emaillen, const char **name, size_t *namelen);
 
 #endif
index 3bbc20a054576b75e77a15faffd82c46e553fef8..3db152155c62fb43b6243c10ce25cc193895afe3 100644 (file)
@@ -145,7 +145,8 @@ sub repo_path {
 sub url_path {
        my ($self, $path) = @_;
        if ($self->{url} =~ m#^https?://#) {
-               $path =~ s!([^~a-zA-Z0-9_./-])!uc sprintf("%%%02x",ord($1))!eg;
+               # characters are taken from subversion/libsvn_subr/path.c
+               $path =~ s#([^~a-zA-Z0-9_./!$&'()*+,-])#uc sprintf("%%%02x",ord($1))#eg;
        }
        $self->{url} . '/' . $self->repo_path($path);
 }
@@ -358,12 +359,12 @@ sub T {
                        mode_a => $m->{mode_a}, mode_b => '000000',
                        sha1_a => $m->{sha1_a}, sha1_b => '0' x 40,
                        chg => 'D', file_b => $m->{file_b}
-               });
+               }, $deletions);
                $self->A({
                        mode_a => '000000', mode_b => $m->{mode_b},
                        sha1_a => '0' x 40, sha1_b => $m->{sha1_b},
                        chg => 'A', file_b => $m->{file_b}
-               });
+               }, $deletions);
                return;
        }
 
index 8b8cf3755c268e03ef334534092bcd7959070cd2..3d1a0933a2eb27dc123730c67470be503e91bf93 100644 (file)
@@ -155,7 +155,7 @@ sub _canonicalize_url_path {
 
        my @parts;
        foreach my $part (split m{/+}, $uri_path) {
-               $part =~ s/([^~\w.%+-]|%(?![a-fA-F0-9]{2}))/sprintf("%%%02X",ord($1))/eg;
+               $part =~ s/([^!\$%&'()*+,.\/\w:=\@_`~-]|%(?![a-fA-F0-9]{2}))/sprintf("%%%02X",ord($1))/eg;
                push @parts, $part;
        }
 
index 92c839fe641da15d05c2afe2b498a9fe390624c8..07fc0628656c5e37eaf04c969a7273455a391f8d 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -387,56 +387,79 @@ void pp_user_info(const struct pretty_print_context *pp,
                  const char *what, struct strbuf *sb,
                  const char *line, const char *encoding)
 {
+       struct strbuf name;
+       struct strbuf mail;
+       struct ident_split ident;
+       int linelen;
+       char *line_end, *date;
+       const char *mailbuf, *namebuf;
+       size_t namelen, maillen;
        int max_length = 78; /* per rfc2822 */
-       char *date;
-       int namelen;
        unsigned long time;
        int tz;
 
        if (pp->fmt == CMIT_FMT_ONELINE)
                return;
-       date = strchr(line, '>');
-       if (!date)
+
+       line_end = strchr(line, '\n');
+       if (!line_end) {
+               line_end = strchr(line, '\0');
+               if (!line_end)
+                       return;
+       }
+
+       linelen = ++line_end - line;
+       if (split_ident_line(&ident, line, linelen))
                return;
-       namelen = ++date - line;
-       time = strtoul(date, &date, 10);
+
+
+       mailbuf = ident.mail_begin;
+       maillen = ident.mail_end - ident.mail_begin;
+       namebuf = ident.name_begin;
+       namelen = ident.name_end - ident.name_begin;
+
+       if (pp->mailmap)
+               map_user(pp->mailmap, &mailbuf, &maillen, &namebuf, &namelen);
+
+       strbuf_init(&mail, 0);
+       strbuf_init(&name, 0);
+
+       strbuf_add(&mail, mailbuf, maillen);
+       strbuf_add(&name, namebuf, namelen);
+
+       namelen = name.len + mail.len + 3; /* ' ' + '<' + '>' */
+       time = strtoul(ident.date_begin, &date, 10);
        tz = strtol(date, NULL, 10);
 
        if (pp->fmt == CMIT_FMT_EMAIL) {
-               char *name_tail = strchr(line, '<');
-               int display_name_length;
-               if (!name_tail)
-                       return;
-               while (line < name_tail && isspace(name_tail[-1]))
-                       name_tail--;
-               display_name_length = name_tail - line;
                strbuf_addstr(sb, "From: ");
-               if (needs_rfc2047_encoding(line, display_name_length, RFC2047_ADDRESS)) {
-                       add_rfc2047(sb, line, display_name_length,
-                                               encoding, RFC2047_ADDRESS);
+               if (needs_rfc2047_encoding(name.buf, name.len, RFC2047_ADDRESS)) {
+                       add_rfc2047(sb, name.buf, name.len,
+                                   encoding, RFC2047_ADDRESS);
                        max_length = 76; /* per rfc2047 */
-               } else if (needs_rfc822_quoting(line, display_name_length)) {
+               } else if (needs_rfc822_quoting(name.buf, name.len)) {
                        struct strbuf quoted = STRBUF_INIT;
-                       add_rfc822_quoted(&quoted, line, display_name_length);
+                       add_rfc822_quoted(&quoted, name.buf, name.len);
                        strbuf_add_wrapped_bytes(sb, quoted.buf, quoted.len,
                                                        -6, 1, max_length);
                        strbuf_release(&quoted);
                } else {
-                       strbuf_add_wrapped_bytes(sb, line, display_name_length,
-                                                       -6, 1, max_length);
+                       strbuf_add_wrapped_bytes(sb, name.buf, name.len,
+                                                -6, 1, max_length);
                }
-               if (namelen - display_name_length + last_line_length(sb) > max_length) {
+               if (namelen - name.len + last_line_length(sb) > max_length)
                        strbuf_addch(sb, '\n');
-                       if (!isspace(name_tail[0]))
-                               strbuf_addch(sb, ' ');
-               }
-               strbuf_add(sb, name_tail, namelen - display_name_length);
-               strbuf_addch(sb, '\n');
+
+               strbuf_addf(sb, " <%s>\n", mail.buf);
        } else {
-               strbuf_addf(sb, "%s: %.*s%.*s\n", what,
+               strbuf_addf(sb, "%s: %.*s%s <%s>\n", what,
                              (pp->fmt == CMIT_FMT_FULLER) ? 4 : 0,
-                             "    ", namelen, line);
+                             "    ", name.buf, mail.buf);
        }
+
+       strbuf_release(&mail);
+       strbuf_release(&name);
+
        switch (pp->fmt) {
        case CMIT_FMT_MEDIUM:
                strbuf_addf(sb, "Date:   %s\n", show_date(time, tz, pp->date_mode));
@@ -586,7 +609,8 @@ char *logmsg_reencode(const struct commit *commit,
        return out;
 }
 
-static int mailmap_name(char *email, int email_len, char *name, int name_len)
+static int mailmap_name(const char **email, size_t *email_len,
+                       const char **name, size_t *name_len)
 {
        static struct string_list *mail_map;
        if (!mail_map) {
@@ -603,36 +627,26 @@ static size_t format_person_part(struct strbuf *sb, char part,
        const int placeholder_len = 2;
        int tz;
        unsigned long date = 0;
-       char person_name[1024];
-       char person_mail[1024];
        struct ident_split s;
-       const char *name_start, *name_end, *mail_start, *mail_end;
+       const char *name, *mail;
+       size_t maillen, namelen;
 
        if (split_ident_line(&s, msg, len) < 0)
                goto skip;
 
-       name_start = s.name_begin;
-       name_end = s.name_end;
-       mail_start = s.mail_begin;
-       mail_end = s.mail_end;
-
-       if (part == 'N' || part == 'E') { /* mailmap lookup */
-               snprintf(person_name, sizeof(person_name), "%.*s",
-                        (int)(name_end - name_start), name_start);
-               snprintf(person_mail, sizeof(person_mail), "%.*s",
-                        (int)(mail_end - mail_start), mail_start);
-               mailmap_name(person_mail, sizeof(person_mail), person_name, sizeof(person_name));
-               name_start = person_name;
-               name_end = name_start + strlen(person_name);
-               mail_start = person_mail;
-               mail_end = mail_start +  strlen(person_mail);
-       }
+       name = s.name_begin;
+       namelen = s.name_end - s.name_begin;
+       mail = s.mail_begin;
+       maillen = s.mail_end - s.mail_begin;
+
+       if (part == 'N' || part == 'E') /* mailmap lookup */
+               mailmap_name(&mail, &maillen, &name, &namelen);
        if (part == 'n' || part == 'N') {       /* name */
-               strbuf_add(sb, name_start, name_end-name_start);
+               strbuf_add(sb, name, namelen);
                return placeholder_len;
        }
        if (part == 'e' || part == 'E') {       /* email */
-               strbuf_add(sb, mail_start, mail_end-mail_start);
+               strbuf_add(sb, mail, maillen);
                return placeholder_len;
        }
 
@@ -966,7 +980,7 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,
 
                        if (!end)
                                return 0;
-                       if (!memcmp(begin, "auto,", 5)) {
+                       if (!prefixcmp(begin, "auto,")) {
                                if (!want_color(c->pretty_ctx->color))
                                        return end - placeholder + 1;
                                begin += 5;
@@ -1301,7 +1315,7 @@ static void pp_header(const struct pretty_print_context *pp,
                        continue;
                }
 
-               if (!memcmp(line, "parent ", 7)) {
+               if (!prefixcmp(line, "parent ")) {
                        if (linelen != 48)
                                die("bad parent line in commit");
                        continue;
@@ -1325,11 +1339,11 @@ static void pp_header(const struct pretty_print_context *pp,
                 * FULL shows both authors but not dates.
                 * FULLER shows both authors and dates.
                 */
-               if (!memcmp(line, "author ", 7)) {
+               if (!prefixcmp(line, "author ")) {
                        strbuf_grow(sb, linelen + 80);
                        pp_user_info(pp, "Author", sb, line + 7, encoding);
                }
-               if (!memcmp(line, "committer ", 10) &&
+               if (!prefixcmp(line, "committer ") &&
                    (pp->fmt == CMIT_FMT_FULL || pp->fmt == CMIT_FMT_FULLER)) {
                        strbuf_grow(sb, linelen + 80);
                        pp_user_info(pp, "Commit", sb, line + 10, encoding);
index 95d21e6472921ab993373ef1848357379440d46e..d7562ee5004379774c6fe1a74b0c503475a34ab5 100644 (file)
@@ -13,6 +13,7 @@
 #include "decorate.h"
 #include "log-tree.h"
 #include "string-list.h"
+#include "mailmap.h"
 
 volatile show_early_output_fn_t show_early_output;
 
@@ -2219,6 +2220,51 @@ static int rewrite_parents(struct rev_info *revs, struct commit *commit)
        return 0;
 }
 
+static int commit_rewrite_person(struct strbuf *buf, const char *what, struct string_list *mailmap)
+{
+       char *person, *endp;
+       size_t len, namelen, maillen;
+       const char *name;
+       const char *mail;
+       struct ident_split ident;
+
+       person = strstr(buf->buf, what);
+       if (!person)
+               return 0;
+
+       person += strlen(what);
+       endp = strchr(person, '\n');
+       if (!endp)
+               return 0;
+
+       len = endp - person;
+
+       if (split_ident_line(&ident, person, len))
+               return 0;
+
+       mail = ident.mail_begin;
+       maillen = ident.mail_end - ident.mail_begin;
+       name = ident.name_begin;
+       namelen = ident.name_end - ident.name_begin;
+
+       if (map_user(mailmap, &mail, &maillen, &name, &namelen)) {
+               struct strbuf namemail = STRBUF_INIT;
+
+               strbuf_addf(&namemail, "%.*s <%.*s>",
+                           (int)namelen, name, (int)maillen, mail);
+
+               strbuf_splice(buf, ident.name_begin - buf->buf,
+                             ident.mail_end - ident.name_begin + 1,
+                             namemail.buf, namemail.len);
+
+               strbuf_release(&namemail);
+
+               return 1;
+       }
+
+       return 0;
+}
+
 static int commit_match(struct commit *commit, struct rev_info *opt)
 {
        int retval;
@@ -2237,6 +2283,14 @@ static int commit_match(struct commit *commit, struct rev_info *opt)
        if (buf.len)
                strbuf_addstr(&buf, commit->buffer);
 
+       if (opt->grep_filter.header_list && opt->mailmap) {
+               if (!buf.len)
+                       strbuf_addstr(&buf, commit->buffer);
+
+               commit_rewrite_person(&buf, "\nauthor ", opt->mailmap);
+               commit_rewrite_person(&buf, "\ncommitter ", opt->mailmap);
+       }
+
        /* Append "fake" message parts as needed */
        if (opt->show_notes) {
                if (!buf.len)
index a395c3639a30d4d0d55036a5fec316e4cdb9546f..5da09ee3efa976b503cba5d13e347aad0f6c764c 100644 (file)
@@ -144,6 +144,7 @@ struct rev_info {
        const char      *subject_prefix;
        int             no_inline;
        int             show_log_size;
+       struct string_list *mailmap;
 
        /* Filter by commit log message */
        struct grep_opt grep_filter;
index 480173fe6dbee5428e3b135ff27ba7551c527ad8..aabb25ef4c1040dde015d6ac37b8213d9bc958ea 100644 (file)
@@ -7,10 +7,11 @@ static int get_entry_index(const struct string_list *list, const char *string,
                int *exact_match)
 {
        int left = -1, right = list->nr;
+       compare_strings_fn cmp = list->cmp ? list->cmp : strcmp;
 
        while (left + 1 < right) {
                int middle = (left + right) / 2;
-               int compare = strcmp(string, list->items[middle].string);
+               int compare = cmp(string, list->items[middle].string);
                if (compare < 0)
                        right = middle;
                else if (compare > 0)
@@ -96,8 +97,9 @@ void string_list_remove_duplicates(struct string_list *list, int free_util)
 {
        if (list->nr > 1) {
                int src, dst;
+               compare_strings_fn cmp = list->cmp ? list->cmp : strcmp;
                for (src = dst = 1; src < list->nr; src++) {
-                       if (!strcmp(list->items[dst - 1].string, list->items[src].string)) {
+                       if (!cmp(list->items[dst - 1].string, list->items[src].string)) {
                                if (list->strdup_strings)
                                        free(list->items[src].string);
                                if (free_util)
@@ -210,15 +212,20 @@ struct string_list_item *string_list_append(struct string_list *list,
                        list->strdup_strings ? xstrdup(string) : (char *)string);
 }
 
+/* Yuck */
+static compare_strings_fn compare_for_qsort;
+
+/* Only call this from inside sort_string_list! */
 static int cmp_items(const void *a, const void *b)
 {
        const struct string_list_item *one = a;
        const struct string_list_item *two = b;
-       return strcmp(one->string, two->string);
+       return compare_for_qsort(one->string, two->string);
 }
 
 void sort_string_list(struct string_list *list)
 {
+       compare_for_qsort = list->cmp ? list->cmp : strcmp;
        qsort(list->items, list->nr, sizeof(*list->items), cmp_items);
 }
 
@@ -226,8 +233,10 @@ struct string_list_item *unsorted_string_list_lookup(struct string_list *list,
                                                     const char *string)
 {
        int i;
+       compare_strings_fn cmp = list->cmp ? list->cmp : strcmp;
+
        for (i = 0; i < list->nr; i++)
-               if (!strcmp(string, list->items[i].string))
+               if (!cmp(string, list->items[i].string))
                        return list->items + i;
        return NULL;
 }
index db1284861adb707b675714edc1ecacf9357f6661..de6769c92dd109791c872b8d320c2caeee9f2b8d 100644 (file)
@@ -5,10 +5,14 @@ struct string_list_item {
        char *string;
        void *util;
 };
+
+typedef int (*compare_strings_fn)(const char *, const char *);
+
 struct string_list {
        struct string_list_item *items;
        unsigned int nr, alloc;
        unsigned int strdup_strings:1;
+       compare_strings_fn cmp; /* NULL uses strcmp() */
 };
 
 #define STRING_LIST_INIT_NODUP { NULL, 0, 0, 0 }
index aae30d97b1a0b95f9cae5c2659f77cf2cb9c3947..842b7549ec3ed6b1fa268a96d085048d2f0b7793 100755 (executable)
@@ -337,6 +337,62 @@ test_expect_success 'Log output (complex mapping)' '
        test_cmp expect actual
 '
 
+cat >expect <<\EOF
+Author: CTO <cto@company.xx>
+Author: Santa Claus <santa.claus@northpole.xx>
+Author: Santa Claus <santa.claus@northpole.xx>
+Author: Other Author <other@author.xx>
+Author: Other Author <other@author.xx>
+Author: Some Dude <some@dude.xx>
+Author: A U Thor <author@example.com>
+EOF
+
+test_expect_success 'Log output with --use-mailmap' '
+       git log --use-mailmap | grep Author >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<\EOF
+Author: CTO <cto@company.xx>
+Author: Santa Claus <santa.claus@northpole.xx>
+Author: Santa Claus <santa.claus@northpole.xx>
+Author: Other Author <other@author.xx>
+Author: Other Author <other@author.xx>
+Author: Some Dude <some@dude.xx>
+Author: A U Thor <author@example.com>
+EOF
+
+test_expect_success 'Log output with log.mailmap' '
+       git -c log.mailmap=True log | grep Author >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<\EOF
+Author: Santa Claus <santa.claus@northpole.xx>
+Author: Santa Claus <santa.claus@northpole.xx>
+EOF
+
+test_expect_success 'Grep author with --use-mailmap' '
+       git log --use-mailmap --author Santa | grep Author >actual &&
+       test_cmp expect actual
+'
+cat >expect <<\EOF
+Author: Santa Claus <santa.claus@northpole.xx>
+Author: Santa Claus <santa.claus@northpole.xx>
+EOF
+
+test_expect_success 'Grep author with log.mailmap' '
+       git -c log.mailmap=True log --author Santa | grep Author >actual &&
+       test_cmp expect actual
+'
+
+>expect
+
+test_expect_success 'Only grep replaced author with --use-mailmap' '
+       git log --use-mailmap --author "<cto@coompany.xx>" >actual &&
+       test_cmp expect actual
+'
+
 # git blame
 cat >expect <<\EOF
 ^OBJI (A U Thor     DATE 1) one
diff --git a/t/t7500/add-content-and-comment b/t/t7500/add-content-and-comment
new file mode 100755 (executable)
index 0000000..c4dccff
--- /dev/null
@@ -0,0 +1,5 @@
+#!/bin/sh
+echo "commit message" >> "$1"
+echo "# comment" >> "$1"
+exit 0
+
index 1a5cb6983c527b6d3e1ad6fad427d3513e8704b1..b1c76483866f55b4429fc76e7035e65984e7b4c2 100755 (executable)
@@ -4,6 +4,15 @@ test_description='git commit porcelain-ish'
 
 . ./test-lib.sh
 
+commit_msg_is () {
+       expect=commit_msg_is.expect
+       actual=commit_msg_is.actual
+
+       printf "%s" "$(git log --pretty=format:%s%b -1)" >$actual &&
+       printf "%s" "$1" >$expect &&
+       test_i18ncmp $expect $actual
+}
+
 # Arguments: [<prefix] [<commit message>] [<commit options>]
 check_summary_oneline() {
        test_tick &&
@@ -168,7 +177,7 @@ test_expect_success 'verbose respects diff config' '
        git config --unset color.diff
 '
 
-test_expect_success 'cleanup commit messages (verbatim,-t)' '
+test_expect_success 'cleanup commit messages (verbatim option,-t)' '
 
        echo >>negative &&
        { echo;echo "# text";echo; } >expect &&
@@ -178,7 +187,7 @@ test_expect_success 'cleanup commit messages (verbatim,-t)' '
 
 '
 
-test_expect_success 'cleanup commit messages (verbatim,-F)' '
+test_expect_success 'cleanup commit messages (verbatim option,-F)' '
 
        echo >>negative &&
        git commit --cleanup=verbatim -F expect -a &&
@@ -187,7 +196,7 @@ test_expect_success 'cleanup commit messages (verbatim,-F)' '
 
 '
 
-test_expect_success 'cleanup commit messages (verbatim,-m)' '
+test_expect_success 'cleanup commit messages (verbatim option,-m)' '
 
        echo >>negative &&
        git commit --cleanup=verbatim -m "$(cat expect)" -a &&
@@ -196,7 +205,7 @@ test_expect_success 'cleanup commit messages (verbatim,-m)' '
 
 '
 
-test_expect_success 'cleanup commit messages (whitespace,-F)' '
+test_expect_success 'cleanup commit messages (whitespace option,-F)' '
 
        echo >>negative &&
        { echo;echo "# text";echo; } >text &&
@@ -207,7 +216,7 @@ test_expect_success 'cleanup commit messages (whitespace,-F)' '
 
 '
 
-test_expect_success 'cleanup commit messages (strip,-F)' '
+test_expect_success 'cleanup commit messages (strip option,-F)' '
 
        echo >>negative &&
        { echo;echo "# text";echo sample;echo; } >text &&
@@ -218,7 +227,7 @@ test_expect_success 'cleanup commit messages (strip,-F)' '
 
 '
 
-test_expect_success 'cleanup commit messages (strip,-F,-e)' '
+test_expect_success 'cleanup commit messages (strip option,-F,-e)' '
 
        echo >>negative &&
        { echo;echo sample;echo; } >text &&
@@ -231,10 +240,71 @@ echo "sample
 # Please enter the commit message for your changes. Lines starting
 # with '#' will be ignored, and an empty message aborts the commit." >expect
 
-test_expect_success 'cleanup commit messages (strip,-F,-e): output' '
+test_expect_success 'cleanup commit messages (strip option,-F,-e): output' '
        test_i18ncmp expect actual
 '
 
+test_expect_success 'cleanup commit message (fail on invalid cleanup mode option)' '
+       test_must_fail git commit --cleanup=non-existent
+'
+
+test_expect_success 'cleanup commit message (fail on invalid cleanup mode configuration)' '
+       test_must_fail git -c commit.cleanup=non-existent commit
+'
+
+test_expect_success 'cleanup commit message (no config and no option uses default)' '
+       echo content >>file &&
+       git add file &&
+       test_set_editor "$TEST_DIRECTORY"/t7500/add-content-and-comment &&
+       git commit --no-status &&
+       commit_msg_is "commit message"
+'
+
+test_expect_success 'cleanup commit message (option overrides default)' '
+       echo content >>file &&
+       git add file &&
+       test_set_editor "$TEST_DIRECTORY"/t7500/add-content-and-comment &&
+       git commit --cleanup=whitespace --no-status &&
+       commit_msg_is "commit message # comment"
+'
+
+test_expect_success 'cleanup commit message (config overrides default)' '
+       echo content >>file &&
+       git add file &&
+       test_set_editor "$TEST_DIRECTORY"/t7500/add-content-and-comment &&
+       git -c commit.cleanup=whitespace commit --no-status &&
+       commit_msg_is "commit message # comment"
+'
+
+test_expect_success 'cleanup commit message (option overrides config)' '
+       echo content >>file &&
+       git add file &&
+       test_set_editor "$TEST_DIRECTORY"/t7500/add-content-and-comment &&
+       git -c commit.cleanup=whitespace commit --cleanup=default &&
+       commit_msg_is "commit message"
+'
+
+test_expect_success 'cleanup commit message (default, -m)' '
+       echo content >>file &&
+       git add file &&
+       git commit -m "message #comment " &&
+       commit_msg_is "message #comment"
+'
+
+test_expect_success 'cleanup commit message (whitespace option, -m)' '
+       echo content >>file &&
+       git add file &&
+       git commit --cleanup=whitespace --no-status -m "message #comment " &&
+       commit_msg_is "message #comment"
+'
+
+test_expect_success 'cleanup commit message (whitespace config, -m)' '
+       echo content >>file &&
+       git add file &&
+       git -c commit.cleanup=whitespace commit --no-status -m "message #comment " &&
+       commit_msg_is "message #comment"
+'
+
 test_expect_success 'message shows author when it is not equal to committer' '
        echo >>negative &&
        git commit -e -m "sample" -a &&
index d8ec408dfd8d3a78fd14aed75c5c0c2f28e66c3f..1a6c4ab08cd5adb56869855913272ec42eceb45f 100644 (file)
@@ -86,7 +86,7 @@ unset VISUAL EMAIL LANGUAGE COLUMNS $("$PERL_PATH" -e '
                PROVE
                VALGRIND
                UNZIP
-               PERF_AGGREGATING_LATER
+               PERF_
        ));
        my @vars = grep(/^GIT_/ && !/^GIT_($ok)/o, @env);
        print join("\n", @vars);