Code

Merge branch 'lt/dirstat'
authorJunio C Hamano <gitster@pobox.com>
Mon, 25 Feb 2008 02:14:53 +0000 (18:14 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 25 Feb 2008 02:14:53 +0000 (18:14 -0800)
* lt/dirstat:
  diff --dirstat: saner handling of binary and unmerged files
  Add "--dirstat" for some directory statistics

1  2 
diff.c
diff.h

diff --combined diff.c
index 699b21f4e347b38498e23ad5258a94cf4289bae9,bcc323f19fd5878776fecde7074e230c61667de6..99daca6103d0235a851ed6de8c16bd86df194158
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -20,7 -20,7 +20,7 @@@
  
  static int diff_detect_rename_default;
  static int diff_rename_limit_default = 100;
 -static int diff_use_color_default;
 +int diff_use_color_default = -1;
  static const char *external_diff_cmd_cfg;
  int diff_auto_refresh_index = 1;
  
@@@ -57,7 -57,7 +57,7 @@@ static int parse_diff_color_slot(const 
  static struct ll_diff_driver {
        const char *name;
        struct ll_diff_driver *next;
 -      char *cmd;
 +      const char *cmd;
  } *user_diff, **user_diff_tail;
  
  /*
@@@ -86,7 -86,10 +86,7 @@@ static int parse_lldiff_command(const c
                user_diff_tail = &(drv->next);
        }
  
 -      if (!value)
 -              return error("%s: lacks value", var);
 -      drv->cmd = strdup(value);
 -      return 0;
 +      return git_config_string(&(drv->cmd), var, value);
  }
  
  /*
@@@ -163,8 -166,13 +163,8 @@@ int git_diff_ui_config(const char *var
        if (!prefixcmp(var, "diff.")) {
                const char *ep = strrchr(var, '.');
  
 -              if (ep != var + 4) {
 -                      if (!strcmp(ep, ".command")) {
 -                              if (!value)
 -                                      return config_error_nonbool(var);
 -                              return parse_lldiff_command(var, ep, value);
 -                      }
 -              }
 +              if (ep != var + 4 && !strcmp(ep, ".command"))
 +                      return parse_lldiff_command(var, ep, value);
        }
  
        return git_diff_basic_config(var, value);
@@@ -191,7 -199,7 +191,7 @@@ int git_diff_basic_config(const char *v
                }
        }
  
 -      return git_default_config(var, value);
 +      return git_color_default_config(var, value);
  }
  
  static char *quote_two(const char *one, const char *two)
@@@ -982,6 -990,90 +982,90 @@@ static void show_numstat(struct diffsta
        }
  }
  
+ struct diffstat_dir {
+       struct diffstat_file **files;
+       int nr, percent, cumulative;
+ };
+ static long gather_dirstat(struct diffstat_dir *dir, unsigned long changed, const char *base, int baselen)
+ {
+       unsigned long this_dir = 0;
+       unsigned int sources = 0;
+       while (dir->nr) {
+               struct diffstat_file *f = *dir->files;
+               int namelen = strlen(f->name);
+               unsigned long this;
+               char *slash;
+               if (namelen < baselen)
+                       break;
+               if (memcmp(f->name, base, baselen))
+                       break;
+               slash = strchr(f->name + baselen, '/');
+               if (slash) {
+                       int newbaselen = slash + 1 - f->name;
+                       this = gather_dirstat(dir, changed, f->name, newbaselen);
+                       sources++;
+               } else {
+                       if (f->is_unmerged || f->is_binary)
+                               this = 0;
+                       else
+                               this = f->added + f->deleted;
+                       dir->files++;
+                       dir->nr--;
+                       sources += 2;
+               }
+               this_dir += this;
+       }
+       /*
+        * We don't report dirstat's for
+        *  - the top level
+        *  - or cases where everything came from a single directory
+        *    under this directory (sources == 1).
+        */
+       if (baselen && sources != 1) {
+               int permille = this_dir * 1000 / changed;
+               if (permille) {
+                       int percent = permille / 10;
+                       if (percent >= dir->percent) {
+                               printf("%4d.%01d%% %.*s\n", percent, permille % 10, baselen, base);
+                               if (!dir->cumulative)
+                                       return 0;
+                       }
+               }
+       }
+       return this_dir;
+ }
+ static void show_dirstat(struct diffstat_t *data, struct diff_options *options)
+ {
+       int i;
+       unsigned long changed;
+       struct diffstat_dir dir;
+       /* Calculate total changes */
+       changed = 0;
+       for (i = 0; i < data->nr; i++) {
+               if (data->files[i]->is_binary || data->files[i]->is_unmerged)
+                       continue;
+               changed += data->files[i]->added;
+               changed += data->files[i]->deleted;
+       }
+       /* This can happen even with many files, if everything was renames */
+       if (!changed)
+               return;
+       /* Show all directories with more than x% of the changes */
+       dir.files = data->files;
+       dir.nr = data->nr;
+       dir.percent = options->dirstat_percent;
+       dir.cumulative = options->output_format & DIFF_FORMAT_CUMULATIVE;
+       gather_dirstat(&dir, changed, "", 0);
+ }
  static void free_diffstat_info(struct diffstat_t *diffstat)
  {
        int i;
@@@ -1013,7 -1105,6 +1097,7 @@@ static void checkdiff_consume(void *pri
        char *err;
  
        if (line[0] == '+') {
 +              data->lineno++;
                data->status = check_and_emit_line(line + 1, len - 1,
                    data->ws_rule, NULL, NULL, NULL, NULL);
                if (!data->status)
                emit_line(set, reset, line, 1);
                (void)check_and_emit_line(line + 1, len - 1, data->ws_rule,
                    stdout, set, reset, ws);
 -              data->lineno++;
        } else if (line[0] == ' ')
                data->lineno++;
        else if (line[0] == '@') {
                char *plus = strchr(line, '+');
                if (plus)
 -                      data->lineno = strtol(plus, NULL, 10);
 +                      data->lineno = strtol(plus, NULL, 10) - 1;
                else
                        die("invalid diff");
        }
@@@ -1199,7 -1291,7 +1283,7 @@@ static struct builtin_funcname_pattern 
                        "new\\|return\\|switch\\|throw\\|while\\)\n"
                        "^[     ]*\\(\\([       ]*"
                        "[A-Za-z_][A-Za-z_0-9]*\\)\\{2,\\}"
 -                      "[      ]*([^;]*$\\)" },
 +                      "[      ]*([^;]*\\)$" },
        { "tex", "^\\(\\\\\\(sub\\)*section{.*\\)$" },
  };
  
@@@ -1631,7 -1723,7 +1715,7 @@@ int diff_populate_filespec(struct diff_
                 * Convert from working tree format to canonical git format
                 */
                strbuf_init(&buf, 0);
 -              if (convert_to_git(s->path, s->data, s->size, &buf)) {
 +              if (convert_to_git(s->path, s->data, s->size, &buf, safe_crlf)) {
                        size_t size = 0;
                        munmap(s->data, s->size);
                        s->should_munmap = 0;
@@@ -2050,12 -2142,13 +2134,13 @@@ void diff_setup(struct diff_options *op
        options->line_termination = '\n';
        options->break_opt = -1;
        options->rename_limit = -1;
+       options->dirstat_percent = 3;
        options->context = 3;
        options->msg_sep = "";
  
        options->change = diff_change;
        options->add_remove = diff_addremove;
 -      if (diff_use_color_default)
 +      if (diff_use_color_default > 0)
                DIFF_OPT_SET(options, COLOR_DIFF);
        else
                DIFF_OPT_CLR(options, COLOR_DIFF);
@@@ -2091,6 -2184,7 +2176,7 @@@ int diff_setup_done(struct diff_option
                                            DIFF_FORMAT_NUMSTAT |
                                            DIFF_FORMAT_DIFFSTAT |
                                            DIFF_FORMAT_SHORTSTAT |
+                                           DIFF_FORMAT_DIRSTAT |
                                            DIFF_FORMAT_SUMMARY |
                                            DIFF_FORMAT_PATCH);
  
                                      DIFF_FORMAT_NUMSTAT |
                                      DIFF_FORMAT_DIFFSTAT |
                                      DIFF_FORMAT_SHORTSTAT |
+                                     DIFF_FORMAT_DIRSTAT |
                                      DIFF_FORMAT_SUMMARY |
                                      DIFF_FORMAT_CHECKDIFF))
                DIFF_OPT_SET(options, RECURSIVE);
@@@ -2212,6 -2307,10 +2299,10 @@@ int diff_opt_parse(struct diff_options 
                options->output_format |= DIFF_FORMAT_NUMSTAT;
        else if (!strcmp(arg, "--shortstat"))
                options->output_format |= DIFF_FORMAT_SHORTSTAT;
+       else if (opt_arg(arg, 'X', "dirstat", &options->dirstat_percent))
+               options->output_format |= DIFF_FORMAT_DIRSTAT;
+       else if (!strcmp(arg, "--cumulative"))
+               options->output_format |= DIFF_FORMAT_CUMULATIVE;
        else if (!strcmp(arg, "--check"))
                options->output_format |= DIFF_FORMAT_CHECKDIFF;
        else if (!strcmp(arg, "--summary"))
@@@ -2930,7 -3029,7 +3021,7 @@@ void diff_flush(struct diff_options *op
                separator++;
        }
  
-       if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT)) {
+       if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIRSTAT)) {
                struct diffstat_t diffstat;
  
                memset(&diffstat, 0, sizeof(struct diffstat_t));
                        if (check_pair_status(p))
                                diff_flush_stat(p, options, &diffstat);
                }
+               if (output_format & DIFF_FORMAT_DIRSTAT)
+                       show_dirstat(&diffstat, options);
                if (output_format & DIFF_FORMAT_NUMSTAT)
                        show_numstat(&diffstat, options);
                if (output_format & DIFF_FORMAT_DIFFSTAT)
diff --combined diff.h
index 8e73f07d7e35cd414c0f5cf347516e39921a7dd1,8c6bb54119507633b5823757d7485925b346a07b..c5d3a4f965380fd56791fc99864fa27b758da7ca
--- 1/diff.h
--- 2/diff.h
+++ b/diff.h
@@@ -30,6 -30,8 +30,8 @@@ typedef void (*diff_format_fn_t)(struc
  #define DIFF_FORMAT_SUMMARY   0x0008
  #define DIFF_FORMAT_PATCH     0x0010
  #define DIFF_FORMAT_SHORTSTAT 0x0020
+ #define DIFF_FORMAT_DIRSTAT   0x0040
+ #define DIFF_FORMAT_CUMULATIVE        0x0080
  
  /* These override all above */
  #define DIFF_FORMAT_NAME      0x0100
@@@ -80,6 -82,7 +82,7 @@@ struct diff_options 
        int pickaxe_opts;
        int rename_score;
        int rename_limit;
+       int dirstat_percent;
        int setup;
        int abbrev;
        const char *msg_sep;
@@@ -174,7 -177,6 +177,7 @@@ extern void diff_unmerge(struct diff_op
  
  extern int git_diff_basic_config(const char *var, const char *value);
  extern int git_diff_ui_config(const char *var, const char *value);
 +extern int diff_use_color_default;
  extern void diff_setup(struct diff_options *);
  extern int diff_opt_parse(struct diff_options *, const char **, int);
  extern int diff_setup_done(struct diff_options *);