Merge branch 'lt/pretty-expand-tabs'
authorJunio C Hamano <gitster@pobox.com>
Wed, 13 Apr 2016 21:12:36 +0000 (14:12 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 13 Apr 2016 21:12:36 +0000 (14:12 -0700)
When "git log" shows the log message indented by 4-spaces, the
remainder of a line after a HT does not align in the way the author
originally intended. The command now expands tabs by default in
such a case, and allows the users to override it with a new option,
'--no-expand-tabs'.

* lt/pretty-expand-tabs:
pretty: test --expand-tabs
pretty: allow tweaking tabwidth in --expand-tabs
pretty: enable --expand-tabs by default for selected pretty formats
pretty: expand tabs in indented logs to make things line up properly

1  2 
Documentation/pretty-options.txt
builtin/log.c
revision.c
revision.h
t/t4201-shortlog.sh
index 54b88b6dcaaa5df06fc8dad2e5f5172682269bc9,93ad1cdc404a812228e78b85c225e6354359ac7f..6c67182728c08309c6b6281c59d67264e0c69c49
@@@ -42,8 -42,22 +42,22 @@@ people using 80-column terminals
        verbatim; this means that invalid sequences in the original
        commit may be copied to the output.
  
+ --expand-tabs=<n>::
+ --expand-tabs::
+ --no-expand-tabs::
+       Perform a tab expansion (replace each tab with enough spaces
+       to fill to the next display column that is multiple of '<n>')
+       in the log message before showing it in the output.
+       `--expand-tabs` is a short-hand for `--expand-tabs=8`, and
+       `--no-expand-tabs` is a short-hand for `--expand-tabs=0`,
+       which disables tab expansion.
+ +
+ By default, tabs are expanded in pretty formats that indent the log
+ message by 4 spaces (i.e.  'medium', which is the default, 'full',
+ and 'fuller').
  ifndef::git-rev-list[]
 ---notes[=<ref>]::
 +--notes[=<treeish>]::
        Show the notes (see linkgit:git-notes[1]) that annotate the
        commit, when showing the commit log message.  This is the default
        for `git log`, `git show` and `git whatchanged` commands when
@@@ -54,8 -68,8 +68,8 @@@ By default, the notes shown are from th
  'core.notesRef' and 'notes.displayRef' variables (or corresponding
  environment overrides). See linkgit:git-config[1] for more details.
  +
 -With an optional '<ref>' argument, show this notes ref instead of the
 -default notes ref(s). The ref specifies the full refname when it begins
 +With an optional '<treeish>' argument, use the treeish to find the notes
 +to display.  The treeish can specify the full refname when it begins
  with `refs/notes/`; when it begins with `notes/`, `refs/` and otherwise
  `refs/notes/` is prefixed to form a full name of the ref.
  +
@@@ -71,7 -85,7 +85,7 @@@ being displayed. Examples: "--notes=foo
        "--notes --notes=foo --no-notes --notes=bar" will only show notes
        from "refs/notes/bar".
  
 ---show-notes[=<ref>]::
 +--show-notes[=<treeish>]::
  --[no-]standard-notes::
        These options are deprecated. Use the above --notes/--no-notes
        options instead.
diff --combined builtin/log.c
index 9430b80e5e18c99bec41d4b4813a24def5bfe07a,e5775ae4d18ecc21baac79490a7ae1c5341eb8df..dff3fbbb437c462e51544dbf1d4781c550a14d1a
@@@ -100,12 -100,6 +100,12 @@@ static int log_line_range_callback(cons
        return 0;
  }
  
 +static void init_log_defaults(void)
 +{
 +      init_grep_defaults();
 +      init_diff_ui_defaults();
 +}
 +
  static void cmd_log_init_defaults(struct rev_info *rev)
  {
        if (fmt_pretty)
@@@ -422,7 -416,7 +422,7 @@@ int cmd_whatchanged(int argc, const cha
        struct rev_info rev;
        struct setup_revision_opt opt;
  
 -      init_grep_defaults();
 +      init_log_defaults();
        git_config(git_log_config, NULL);
  
        init_revisions(&rev, prefix);
@@@ -533,7 -527,7 +533,7 @@@ int cmd_show(int argc, const char **arg
        struct pathspec match_all;
        int i, count, ret = 0;
  
 -      init_grep_defaults();
 +      init_log_defaults();
        git_config(git_log_config, NULL);
  
        memset(&match_all, 0, sizeof(match_all));
@@@ -614,7 -608,7 +614,7 @@@ int cmd_log_reflog(int argc, const cha
        struct rev_info rev;
        struct setup_revision_opt opt;
  
 -      init_grep_defaults();
 +      init_log_defaults();
        git_config(git_log_config, NULL);
  
        init_revisions(&rev, prefix);
@@@ -653,7 -647,7 +653,7 @@@ int cmd_log(int argc, const char **argv
        struct rev_info rev;
        struct setup_revision_opt opt;
  
 -      init_grep_defaults();
 +      init_log_defaults();
        git_config(git_log_config, NULL);
  
        init_revisions(&rev, prefix);
@@@ -705,7 -699,6 +705,7 @@@ static int do_signoff
  static const char *signature = git_version_string;
  static const char *signature_file;
  static int config_cover_letter;
 +static const char *config_output_directory;
  
  enum {
        COVER_UNSET,
@@@ -784,8 -777,6 +784,8 @@@ static int git_format_config(const cha
                config_cover_letter = git_config_bool(var, value) ? COVER_ON : COVER_OFF;
                return 0;
        }
 +      if (!strcmp(var, "format.outputdirectory"))
 +              return git_config_string(&config_output_directory, var, value);
  
        return git_log_config(var, value, cb);
  }
@@@ -1286,10 -1277,11 +1286,11 @@@ int cmd_format_patch(int argc, const ch
        extra_hdr.strdup_strings = 1;
        extra_to.strdup_strings = 1;
        extra_cc.strdup_strings = 1;
 -      init_grep_defaults();
 +      init_log_defaults();
        git_config(git_format_config, NULL);
        init_revisions(&rev, prefix);
        rev.commit_format = CMIT_FMT_EMAIL;
+       rev.expand_tabs_in_log_default = 0;
        rev.verbose_header = 1;
        rev.diff = 1;
        rev.max_parents = 1;
        if (rev.show_notes)
                init_display_notes(&rev.notes_opt);
  
 +      if (!output_directory && !use_stdout)
 +              output_directory = config_output_directory;
 +
        if (!use_stdout)
                output_directory = set_outdir(prefix, output_directory);
        else
diff --combined revision.c
index 8b2dfe3160784f9780cf541a674fe89100d38a93,47e9ee7a14060b7848436ee0049b12ccf2378180..b683476b9c44b90a6dc2acd5cf1f725f741fbaa3
@@@ -25,13 -25,69 +25,13 @@@ volatile show_early_output_fn_t show_ea
  static const char *term_bad;
  static const char *term_good;
  
 -char *path_name(const struct name_path *path, const char *name)
 +void show_object_with_name(FILE *out, struct object *obj, const char *name)
  {
 -      const struct name_path *p;
 -      char *n, *m;
 -      int nlen = strlen(name);
 -      int len = nlen + 1;
 -
 -      for (p = path; p; p = p->up) {
 -              if (p->elem_len)
 -                      len += p->elem_len + 1;
 -      }
 -      n = xmalloc(len);
 -      m = n + len - (nlen + 1);
 -      memcpy(m, name, nlen + 1);
 -      for (p = path; p; p = p->up) {
 -              if (p->elem_len) {
 -                      m -= p->elem_len + 1;
 -                      memcpy(m, p->elem, p->elem_len);
 -                      m[p->elem_len] = '/';
 -              }
 -      }
 -      return n;
 -}
 -
 -static int show_path_component_truncated(FILE *out, const char *name, int len)
 -{
 -      int cnt;
 -      for (cnt = 0; cnt < len; cnt++) {
 -              int ch = name[cnt];
 -              if (!ch || ch == '\n')
 -                      return -1;
 -              fputc(ch, out);
 -      }
 -      return len;
 -}
 -
 -static int show_path_truncated(FILE *out, const struct name_path *path)
 -{
 -      int emitted, ours;
 -
 -      if (!path)
 -              return 0;
 -      emitted = show_path_truncated(out, path->up);
 -      if (emitted < 0)
 -              return emitted;
 -      if (emitted)
 -              fputc('/', out);
 -      ours = show_path_component_truncated(out, path->elem, path->elem_len);
 -      if (ours < 0)
 -              return ours;
 -      return ours || emitted;
 -}
 -
 -void show_object_with_name(FILE *out, struct object *obj,
 -                         const struct name_path *path, const char *component)
 -{
 -      struct name_path leaf;
 -      leaf.up = (struct name_path *)path;
 -      leaf.elem = component;
 -      leaf.elem_len = strlen(component);
 +      const char *p;
  
        fprintf(out, "%s ", oid_to_hex(&obj->oid));
 -      show_path_truncated(out, &leaf);
 +      for (p = name; *p && *p != '\n'; p++)
 +              fputc(*p, out);
        fputc('\n', out);
  }
  
@@@ -1356,8 -1412,10 +1356,10 @@@ void init_revisions(struct rev_info *re
        revs->skip_count = -1;
        revs->max_count = -1;
        revs->max_parents = -1;
+       revs->expand_tabs_in_log = -1;
  
        revs->commit_format = CMIT_FMT_DEFAULT;
+       revs->expand_tabs_in_log_default = 8;
  
        init_grep_defaults();
        grep_init(&revs->grep_filter, prefix);
@@@ -1579,7 -1637,10 +1581,7 @@@ static void append_prune_data(struct cm
  static void read_pathspec_from_stdin(struct rev_info *revs, struct strbuf *sb,
                                     struct cmdline_pathspec *prune)
  {
 -      while (strbuf_getwholeline(sb, stdin, '\n') != EOF) {
 -              int len = sb->len;
 -              if (len && sb->buf[len - 1] == '\n')
 -                      sb->buf[--len] = '\0';
 +      while (strbuf_getline(sb, stdin) != EOF) {
                ALLOC_GROW(prune->path, prune->nr + 1, prune->alloc);
                prune->path[prune->nr++] = xstrdup(sb->buf);
        }
@@@ -1596,8 -1657,10 +1598,8 @@@ static void read_revisions_from_stdin(s
        warn_on_object_refname_ambiguity = 0;
  
        strbuf_init(&sb, 1000);
 -      while (strbuf_getwholeline(&sb, stdin, '\n') != EOF) {
 +      while (strbuf_getline(&sb, stdin) != EOF) {
                int len = sb.len;
 -              if (len && sb.buf[len - 1] == '\n')
 -                      sb.buf[--len] = '\0';
                if (!len)
                        break;
                if (sb.buf[0] == '-') {
@@@ -1854,6 -1917,15 +1856,15 @@@ static int handle_revision_opt(struct r
                revs->verbose_header = 1;
                revs->pretty_given = 1;
                get_commit_format(arg+9, revs);
+       } else if (!strcmp(arg, "--expand-tabs")) {
+               revs->expand_tabs_in_log = 8;
+       } else if (!strcmp(arg, "--no-expand-tabs")) {
+               revs->expand_tabs_in_log = 0;
+       } else if (skip_prefix(arg, "--expand-tabs=", &arg)) {
+               int val;
+               if (strtol_i(arg, 10, &val) < 0 || val < 0)
+                       die("'%s': not a non-negative integer", arg);
+               revs->expand_tabs_in_log = val;
        } else if (!strcmp(arg, "--show-notes") || !strcmp(arg, "--notes")) {
                revs->show_notes = 1;
                revs->show_notes_given = 1;
@@@ -2327,6 -2399,9 +2338,9 @@@ int setup_revisions(int argc, const cha
        if (revs->first_parent_only && revs->bisect)
                die(_("--first-parent is incompatible with --bisect"));
  
+       if (revs->expand_tabs_in_log < 0)
+               revs->expand_tabs_in_log = revs->expand_tabs_in_log_default;
        return left;
  }
  
diff --combined revision.h
index dca0d381715cf5bc6888587feaec989012f51252,6cc36b49c59cc2265040eaa033e4f0e5c607ea48..9fac1a607de6470ab0c9b7e5079e8d36c8d68c75
@@@ -148,6 -148,8 +148,8 @@@ struct rev_info 
                        linear:1;
  
        struct date_mode date_mode;
+       int             expand_tabs_in_log; /* unset if negative */
+       int             expand_tabs_in_log_default;
  
        unsigned int    abbrev;
        enum cmit_fmt   commit_format;
@@@ -257,9 -259,16 +259,9 @@@ extern void put_revision_mark(const str
  extern void mark_parents_uninteresting(struct commit *commit);
  extern void mark_tree_uninteresting(struct tree *tree);
  
 -struct name_path {
 -      struct name_path *up;
 -      int elem_len;
 -      const char *elem;
 -};
 -
 -char *path_name(const struct name_path *path, const char *name);
 +char *path_name(struct strbuf *path, const char *name);
  
 -extern void show_object_with_name(FILE *, struct object *,
 -                                const struct name_path *, const char *);
 +extern void show_object_with_name(FILE *, struct object *, const char *);
  
  extern void add_pending_object(struct rev_info *revs,
                               struct object *obj, const char *name);
diff --combined t/t4201-shortlog.sh
index f5e63670faa0c5acc38adc17d32ae66b59d66b64,2fec94886af7cfad22d1f7f9eaa43c63a57e827b..a9773658f09e92578b489b1b5b65fc87599cb8ae
@@@ -115,17 -115,11 +115,17 @@@ EO
  '
  
  test_expect_success !MINGW 'shortlog from non-git directory' '
-       git log HEAD >log &&
+       git log --no-expand-tabs HEAD >log &&
        GIT_DIR=non-existing git shortlog -w <log >out &&
        test_cmp expect out
  '
  
 +test_expect_success !MINGW 'shortlog can read --format=raw output' '
 +      git log --format=raw HEAD >log &&
 +      GIT_DIR=non-existing git shortlog -w <log >out &&
 +      test_cmp expect out
 +'
 +
  test_expect_success 'shortlog should add newline when input line matches wraplen' '
        cat >expect <<\EOF &&
  A U Thor (2):
@@@ -178,6 -172,22 +178,6 @@@ test_expect_success !MINGW 'shortlog en
        git shortlog HEAD~2.. > out &&
  test_cmp expect out'
  
 -test_expect_success 'shortlog ignores commits with missing authors' '
 -      git commit --allow-empty -m normal &&
 -      git commit --allow-empty -m soon-to-be-broken &&
 -      git cat-file commit HEAD >commit.tmp &&
 -      sed "/^author/d" commit.tmp >broken.tmp &&
 -      commit=$(git hash-object -w -t commit --stdin <broken.tmp) &&
 -      git update-ref HEAD $commit &&
 -      cat >expect <<-\EOF &&
 -      A U Thor (1):
 -            normal
 -
 -      EOF
 -      git shortlog HEAD~2.. >actual &&
 -      test_cmp expect actual
 -'
 -
  test_expect_success 'shortlog with revision pseudo options' '
        git shortlog --all &&
        git shortlog --branches &&