Merge branch 'wp/pretty-enhancement'
authorJunio C Hamano <gitster@pobox.com>
Sun, 13 Jun 2010 18:21:00 +0000 (11:21 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sun, 13 Jun 2010 18:21:00 +0000 (11:21 -0700)
* wp/pretty-enhancement:
pretty: initialize new cmt_fmt_map to 0
pretty: add aliases for pretty formats
pretty: add infrastructure for commit format aliases
pretty: make it easier to add new formats

1  2 
Documentation/config.txt
Documentation/pretty-formats.txt
pretty.c
diff --combined Documentation/config.txt
index 85f763c4d0dc6656a45fa876b453d2cb34a72cb3,b41f39de7ce77e288e21676c3f15e832f36800c7..7e2ae254b0b25b65d8589d7ca568b9902ca3baa7
@@@ -481,8 -481,6 +481,8 @@@ core.whitespace:
    error (enabled by default).
  * `indent-with-non-tab` treats a line that is indented with 8 or more
    space characters as an error (not enabled by default).
 +* `tab-in-indent` treats a tab character in the initial indent part of
 +  the line as an error (not enabled by default).
  * `blank-at-eof` treats blank lines added at the end of file as an error
    (enabled by default).
  * `trailing-space` is a short-hand to cover both `blank-at-eol` and
@@@ -946,19 -944,13 +946,19 @@@ gc.pruneexpire:
        unreachable objects immediately.
  
  gc.reflogexpire::
 +gc.<pattern>.reflogexpire::
        'git reflog expire' removes reflog entries older than
 -      this time; defaults to 90 days.
 +      this time; defaults to 90 days.  With "<pattern>" (e.g.
 +      "refs/stash") in the middle the setting applies only to
 +      the refs that match the <pattern>.
  
  gc.reflogexpireunreachable::
 +gc.<ref>.reflogexpireunreachable::
        'git reflog expire' removes reflog entries older than
        this time and are not reachable from the current tip;
 -      defaults to 30 days.
 +      defaults to 30 days.  With "<pattern>" (e.g. "refs/stash")
 +      in the middle, the setting applies only to the refs that
 +      match the <pattern>.
  
  gc.rerereresolved::
        Records of conflicted merge you resolved earlier are
@@@ -1276,13 -1268,6 +1276,13 @@@ log.date:
        following alternatives: {relative,local,default,iso,rfc,short}.
        See linkgit:git-log[1].
  
 +log.decorate::
 +      Print out the ref names of any commits that are shown by the log
 +      command. If 'short' is specified, the ref name prefixes 'refs/heads/',
 +      'refs/tags/' and 'refs/remotes/' will not be printed. If 'full' is
 +      specified, the full ref name (including prefix) will be printed.
 +      This is the same as the log commands '--decorate' option.
 +
  log.showroot::
        If true, the initial commit will be shown as a big creation event.
        This is equivalent to a diff against an empty tree.
@@@ -1374,6 -1359,10 +1374,6 @@@ notes.rewrite.<command>:
        automatically copies your notes from the original to the
        rewritten commit.  Defaults to `true`, but see
        "notes.rewriteRef" below.
 -+
 -This setting can be overridden with the `GIT_NOTES_REWRITE_REF`
 -environment variable, which must be a colon separated list of refs or
 -globs.
  
  notes.rewriteMode::
        When copying notes during a rewrite (see the
@@@ -1393,10 -1382,6 +1393,10 @@@ notes.rewriteRef:
  +
  Does not have a default value; you must configure this variable to
  enable note rewriting.
 ++
 +This setting can be overridden with the `GIT_NOTES_REWRITE_REF`
 +environment variable, which must be a colon separated list of refs or
 +globs.
  
  pack.window::
        The size of the window used by linkgit:git-pack-objects[1] when no
@@@ -1481,6 -1466,16 +1481,16 @@@ pager.<cmd>:
        it takes precedence over this option.  To disable pagination for
        all commands, set `core.pager` or `GIT_PAGER` to `cat`.
  
+ pretty.<name>::
+       Alias for a --pretty= format string, as specified in
+       linkgit:git-log[1]. Any aliases defined here can be used just
+       as the built-in pretty formats could. For example,
+       running `git config pretty.changelog "format:{asterisk} %H %s"`
+       would cause the invocation `git log --pretty=changelog`
+       to be equivalent to running `git log "--pretty=format:{asterisk} %H %s"`.
+       Note that an alias with the same name as a built-in format
+       will be silently ignored.
  pull.octopus::
        The default merge strategy to use when pulling multiple branches
        at once.
@@@ -1531,7 -1526,7 +1541,7 @@@ receive.denyDeletes:
        the ref. Use this to prevent such a ref deletion via a push.
  
  receive.denyCurrentBranch::
 -      If set to true or "refuse", receive-pack will deny a ref update
 +      If set to true or "refuse", git-receive-pack will deny a ref update
        to the currently checked out branch of a non-bare repository.
        Such a push is potentially dangerous because it brings the HEAD
        out of sync with the index and working tree. If set to "warn",
@@@ -1593,9 -1588,7 +1603,9 @@@ remote.<name>.uploadpack:
  
  remote.<name>.tagopt::
        Setting this value to \--no-tags disables automatic tag following when
 -      fetching from remote <name>
 +      fetching from remote <name>. Setting it to \--tags will fetch every
 +      tag from remote <name>, even if they are not reachable from remote
 +      branch heads.
  
  remote.<name>.vcs::
        Setting this to a value <vcs> will cause git to interact with
@@@ -1659,7 -1652,6 +1669,7 @@@ sendemail.smtppass:
  sendemail.suppresscc::
  sendemail.suppressfrom::
  sendemail.to::
 +sendemail.smtpdomain::
  sendemail.smtpserver::
  sendemail.smtpserverport::
  sendemail.smtpuser::
@@@ -1699,13 -1691,6 +1709,13 @@@ If this variable is not specified, it d
  This variable can be overridden with the -u|--untracked-files option
  of linkgit:git-status[1] and linkgit:git-commit[1].
  
 +status.submodulesummary::
 +      Defaults to false.
 +      If this is set to a non zero number or true (identical to -1 or an
 +      unlimited number), the submodule summary will be enabled and a
 +      summary of commits for modified submodules will be shown (see
 +      --summary-limit option of linkgit:git-submodule[1]).
 +
  tar.umask::
        This variable can be used to restrict the permission bits of
        tar archive entries.  The default is 0002, which turns off the
index b055a673d83bcf4c106f3f703ca7f4db4ca1d60f,5e95df6ba34290be88b76282ec7fb09864dd8134..8c68ce94f990b0c7f3bc909cc3dc0c4e2a37cf3b
@@@ -11,7 -11,12 +11,12 @@@ have limited your view of history: for 
  only interested in changes related to a certain directory or
  file.
  
- Here are some additional details for each format:
+ There are several built-in formats, and you can define
+ additional formats by setting a pretty.<name>
+ config option to either another format name, or a
+ 'format:' string, as described below (see
+ linkgit:git-config[1]). Here are the details of the
+ built-in formats:
  
  * 'oneline'
  
@@@ -76,9 -81,9 +81,9 @@@ displayed in full, regardless of whethe
  true parent commits, without taking grafts nor history
  simplification into account.
  
 -* 'format:'
 +* 'format:<string>'
  +
 -The 'format:' format allows you to specify which information
 +The 'format:<string>' format allows you to specify which information
  you want to show. It works a little bit like printf format,
  with the notable exception that you get a newline with '%n'
  instead of '\n'.
@@@ -123,7 -128,6 +128,7 @@@ The placeholders are
  - '%s': subject
  - '%f': sanitized subject line, suitable for a filename
  - '%b': body
 +- '%B': raw body (unwrapped subject and body)
  - '%N': commit notes
  - '%gD': reflog selector, e.g., `refs/stash@\{1\}`
  - '%gd': shortened reflog selector, e.g., `stash@\{1\}`
diff --combined pretty.c
index 74cda1b44e37f414d9d61326bbc5c548d56ba780,4784f676faeea6365be76fb8b8f122c8bb69fd04..8b18efda9cd6eb615900687c0205daa832794c92
+++ b/pretty.c
  #include "reflog-walk.h"
  
  static char *user_format;
+ static struct cmt_fmt_map {
+       const char *name;
+       enum cmit_fmt format;
+       int is_tformat;
+       int is_alias;
+       const char *user_format;
+ } *commit_formats;
+ static size_t builtin_formats_len;
+ static size_t commit_formats_len;
+ static size_t commit_formats_alloc;
+ static struct cmt_fmt_map *find_commit_format(const char *sought);
  
  static void save_user_format(struct rev_info *rev, const char *cp, int is_tformat)
  {
        rev->commit_format = CMIT_FMT_USERFORMAT;
  }
  
void get_commit_format(const char *arg, struct rev_info *rev)
static int git_pretty_formats_config(const char *var, const char *value, void *cb)
  {
+       struct cmt_fmt_map *commit_format = NULL;
+       const char *name;
+       const char *fmt;
        int i;
-       static struct cmt_fmt_map {
-               const char *n;
-               size_t cmp_len;
-               enum cmit_fmt v;
-       } cmt_fmts[] = {
-               { "raw",        1,      CMIT_FMT_RAW },
-               { "medium",     1,      CMIT_FMT_MEDIUM },
-               { "short",      1,      CMIT_FMT_SHORT },
-               { "email",      1,      CMIT_FMT_EMAIL },
-               { "full",       5,      CMIT_FMT_FULL },
-               { "fuller",     5,      CMIT_FMT_FULLER },
-               { "oneline",    1,      CMIT_FMT_ONELINE },
+       if (prefixcmp(var, "pretty."))
+               return 0;
+       name = var + strlen("pretty.");
+       for (i = 0; i < builtin_formats_len; i++) {
+               if (!strcmp(commit_formats[i].name, name))
+                       return 0;
+       }
+       for (i = builtin_formats_len; i < commit_formats_len; i++) {
+               if (!strcmp(commit_formats[i].name, name)) {
+                       commit_format = &commit_formats[i];
+                       break;
+               }
+       }
+       if (!commit_format) {
+               ALLOC_GROW(commit_formats, commit_formats_len+1,
+                          commit_formats_alloc);
+               commit_format = &commit_formats[commit_formats_len];
+               memset(commit_format, 0, sizeof(*commit_format));
+               commit_formats_len++;
+       }
+       commit_format->name = xstrdup(name);
+       commit_format->format = CMIT_FMT_USERFORMAT;
+       git_config_string(&fmt, var, value);
+       if (!prefixcmp(fmt, "format:") || !prefixcmp(fmt, "tformat:")) {
+               commit_format->is_tformat = fmt[0] == 't';
+               fmt = strchr(fmt, ':') + 1;
+       } else if (strchr(fmt, '%'))
+               commit_format->is_tformat = 1;
+       else
+               commit_format->is_alias = 1;
+       commit_format->user_format = fmt;
+       return 0;
+ }
+ static void setup_commit_formats(void)
+ {
+       struct cmt_fmt_map builtin_formats[] = {
+               { "raw",        CMIT_FMT_RAW,           0 },
+               { "medium",     CMIT_FMT_MEDIUM,        0 },
+               { "short",      CMIT_FMT_SHORT,         0 },
+               { "email",      CMIT_FMT_EMAIL,         0 },
+               { "fuller",     CMIT_FMT_FULLER,        0 },
+               { "full",       CMIT_FMT_FULL,          0 },
+               { "oneline",    CMIT_FMT_ONELINE,       1 }
        };
+       commit_formats_len = ARRAY_SIZE(builtin_formats);
+       builtin_formats_len = commit_formats_len;
+       ALLOC_GROW(commit_formats, commit_formats_len, commit_formats_alloc);
+       memcpy(commit_formats, builtin_formats,
+              sizeof(*builtin_formats)*ARRAY_SIZE(builtin_formats));
+       git_config(git_pretty_formats_config, NULL);
+ }
+ static struct cmt_fmt_map *find_commit_format_recursive(const char *sought,
+                                                       const char *original,
+                                                       int num_redirections)
+ {
+       struct cmt_fmt_map *found = NULL;
+       size_t found_match_len = 0;
+       int i;
+       if (num_redirections >= commit_formats_len)
+               die("invalid --pretty format: "
+                   "'%s' references an alias which points to itself",
+                   original);
+       for (i = 0; i < commit_formats_len; i++) {
+               size_t match_len;
+               if (prefixcmp(commit_formats[i].name, sought))
+                       continue;
+               match_len = strlen(commit_formats[i].name);
+               if (found == NULL || found_match_len > match_len) {
+                       found = &commit_formats[i];
+                       found_match_len = match_len;
+               }
+       }
+       if (found && found->is_alias) {
+               found = find_commit_format_recursive(found->user_format,
+                                                    original,
+                                                    num_redirections+1);
+       }
+       return found;
+ }
+ static struct cmt_fmt_map *find_commit_format(const char *sought)
+ {
+       if (!commit_formats)
+               setup_commit_formats();
+       return find_commit_format_recursive(sought, sought, 0);
+ }
+ void get_commit_format(const char *arg, struct rev_info *rev)
+ {
+       struct cmt_fmt_map *commit_format;
  
        rev->use_terminator = 0;
        if (!arg || !*arg) {
                save_user_format(rev, strchr(arg, ':') + 1, arg[0] == 't');
                return;
        }
-       for (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) {
-               if (!strncmp(arg, cmt_fmts[i].n, cmt_fmts[i].cmp_len) &&
-                   !strncmp(arg, cmt_fmts[i].n, strlen(arg))) {
-                       if (cmt_fmts[i].v == CMIT_FMT_ONELINE)
-                               rev->use_terminator = 1;
-                       rev->commit_format = cmt_fmts[i].v;
-                       return;
-               }
-       }
        if (strchr(arg, '%')) {
                save_user_format(rev, arg, 1);
                return;
        }
  
-       die("invalid --pretty format: %s", arg);
+       commit_format = find_commit_format(arg);
+       if (!commit_format)
+               die("invalid --pretty format: %s", arg);
+       rev->commit_format = commit_format->format;
+       rev->use_terminator = commit_format->is_tformat;
+       if (commit_format->format == CMIT_FMT_USERFORMAT) {
+               save_user_format(rev, commit_format->user_format,
+                                commit_format->is_tformat);
+       }
  }
  
  /*
@@@ -716,7 -824,7 +824,7 @@@ static size_t format_commit_one(struct 
                if (add_again(sb, &c->abbrev_commit_hash))
                        return 1;
                strbuf_addstr(sb, find_unique_abbrev(commit->object.sha1,
 -                                                   DEFAULT_ABBREV));
 +                                                   c->pretty_ctx->abbrev));
                c->abbrev_commit_hash.len = sb->len - c->abbrev_commit_hash.off;
                return 1;
        case 'T':               /* tree hash */
                if (add_again(sb, &c->abbrev_tree_hash))
                        return 1;
                strbuf_addstr(sb, find_unique_abbrev(commit->tree->object.sha1,
 -                                                   DEFAULT_ABBREV));
 +                                                   c->pretty_ctx->abbrev));
                c->abbrev_tree_hash.len = sb->len - c->abbrev_tree_hash.off;
                return 1;
        case 'P':               /* parent hashes */
                        if (p != commit->parents)
                                strbuf_addch(sb, ' ');
                        strbuf_addstr(sb, find_unique_abbrev(
 -                                      p->item->object.sha1, DEFAULT_ABBREV));
 +                                      p->item->object.sha1,
 +                                      c->pretty_ctx->abbrev));
                }
                c->abbrev_parent_hashes.len = sb->len -
                                              c->abbrev_parent_hashes.off;
        case 'e':       /* encoding */
                strbuf_add(sb, msg + c->encoding.off, c->encoding.len);
                return 1;
 +      case 'B':       /* raw body */
 +              /* message_off is always left at the initial newline */
 +              strbuf_addstr(sb, msg + c->message_off + 1);
 +              return 1;
        }
  
        /* Now we need to parse the commit message. */