Merge branch 'jc/diff-relative'
authorJunio C Hamano <gitster@pobox.com>
Wed, 27 Feb 2008 19:55:28 +0000 (11:55 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 27 Feb 2008 19:55:28 +0000 (11:55 -0800)
* jc/diff-relative:
diff --relative: help working in a bare repository
diff --relative: output paths as relative to the current subdirectory

1  2 
builtin-diff.c
diff.c
diff.h
revision.c
diff --combined builtin-diff.c
index 8f53f52dcbe74c1fdb6b7a54a8b250537bc821ee,19fa6681858ee28f99bf6b3bad6adcda252fe3b7..444ff2fd92da38ab4002af6a1882839c1c3d930a
@@@ -4,7 -4,6 +4,7 @@@
   * Copyright (c) 2006 Junio C Hamano
   */
  #include "cache.h"
 +#include "color.h"
  #include "commit.h"
  #include "blob.h"
  #include "tag.h"
@@@ -44,12 -43,17 +44,17 @@@ static void stuff_change(struct diff_op
                tmp_u = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_u;
                tmp_c = old_name; old_name = new_name; new_name = tmp_c;
        }
+       if (opt->prefix &&
+           (strncmp(old_name, opt->prefix, opt->prefix_length) ||
+            strncmp(new_name, opt->prefix, opt->prefix_length)))
+               return;
        one = alloc_filespec(old_name);
        two = alloc_filespec(new_name);
        fill_filespec(one, old_sha1, old_mode);
        fill_filespec(two, new_sha1, new_mode);
  
-       /* NEEDSWORK: shouldn't this part of diffopt??? */
        diff_queue(&diff_queued_diff, one, two);
  }
  
@@@ -230,10 -234,6 +235,10 @@@ int cmd_diff(int argc, const char **arg
  
        prefix = setup_git_directory_gently(&nongit);
        git_config(git_diff_ui_config);
 +
 +      if (diff_use_color_default == -1)
 +              diff_use_color_default = git_use_color_default;
 +
        init_revisions(&rev, prefix);
        rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index;
  
                if (diff_setup_done(&rev.diffopt) < 0)
                        die("diff_setup_done failed");
        }
+       if (rev.diffopt.prefix && nongit) {
+               rev.diffopt.prefix = NULL;
+               rev.diffopt.prefix_length = 0;
+       }
        DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL);
        DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
  
diff --combined diff.c
index dff826b6347bfde01907c0c3b5f1873cd951c1fe,2b89b160078b4b0de9605100889f8cee110715d0..18859a70e02b84dabd3da66defba7df1111d18c1
--- 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);
  }
  
  /*
@@@ -155,16 -158,16 +155,16 @@@ int git_diff_ui_config(const char *var
                return 0;
        }
        if (!strcmp(var, "diff.external")) {
 +              if (!value)
 +                      return config_error_nonbool(var);
                external_diff_cmd_cfg = xstrdup(value);
                return 0;
        }
        if (!prefixcmp(var, "diff.")) {
                const char *ep = strrchr(var, '.');
  
 -              if (ep != var + 4) {
 -                      if (!strcmp(ep, ".command"))
 -                              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);
@@@ -174,8 -177,6 +174,8 @@@ int git_diff_basic_config(const char *v
  {
        if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) {
                int slot = parse_diff_color_slot(var, 11);
 +              if (!value)
 +                      return config_error_nonbool(var);
                color_parse(value, var, diff_colors[slot]);
                return 0;
        }
        if (!prefixcmp(var, "diff.")) {
                const char *ep = strrchr(var, '.');
                if (ep != var + 4) {
 -                      if (!strcmp(ep, ".funcname"))
 +                      if (!strcmp(ep, ".funcname")) {
 +                              if (!value)
 +                                      return config_error_nonbool(var);
                                return parse_funcname_pattern(var, ep, value);
 +                      }
                }
        }
  
 -      return git_default_config(var, value);
 +      return git_color_default_config(var, value);
  }
  
  static char *quote_two(const char *one, const char *two)
@@@ -272,8 -270,8 +272,8 @@@ static void print_line_count(int count
        }
  }
  
 -static void copy_file(int prefix, const char *data, int size,
 -              const char *set, const char *reset)
 +static void copy_file_with_prefix(int prefix, const char *data, int size,
 +                                const char *set, const char *reset)
  {
        int ch, nl_just_seen = 1;
        while (0 < size--) {
@@@ -331,9 -329,9 +331,9 @@@ static void emit_rewrite_diff(const cha
        print_line_count(lc_b);
        printf(" @@%s\n", reset);
        if (lc_a)
 -              copy_file('-', one->data, one->size, old, reset);
 +              copy_file_with_prefix('-', one->data, one->size, old, reset);
        if (lc_b)
 -              copy_file('+', two->data, two->size, new, reset);
 +              copy_file_with_prefix('+', two->data, two->size, new, reset);
  }
  
  static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
@@@ -982,90 -980,6 +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;
@@@ -1097,7 -1011,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");
        }
@@@ -1283,7 -1197,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{.*\\)$" },
  };
  
@@@ -1483,6 -1397,7 +1483,7 @@@ static void builtin_diffstat(const cha
  }
  
  static void builtin_checkdiff(const char *name_a, const char *name_b,
+                             const char *attr_path,
                             struct diff_filespec *one,
                             struct diff_filespec *two, struct diff_options *o)
  {
        data.filename = name_b ? name_b : name_a;
        data.lineno = 0;
        data.color_diff = DIFF_OPT_TST(o, COLOR_DIFF);
-       data.ws_rule = whitespace_rule(data.filename);
+       data.ws_rule = whitespace_rule(attr_path);
  
        if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
                die("unable to read files to diff");
@@@ -1596,22 -1511,17 +1597,22 @@@ static int reuse_worktree_file(const ch
        if (pos < 0)
                return 0;
        ce = active_cache[pos];
 -      if ((lstat(name, &st) < 0) ||
 -          !S_ISREG(st.st_mode) || /* careful! */
 -          ce_match_stat(ce, &st, 0) ||
 -          hashcmp(sha1, ce->sha1))
 +
 +      /*
 +       * This is not the sha1 we are looking for, or
 +       * unreusable because it is not a regular file.
 +       */
 +      if (hashcmp(sha1, ce->sha1) || !S_ISREG(ce->ce_mode))
                return 0;
 -      /* we return 1 only when we can stat, it is a regular file,
 -       * stat information matches, and sha1 recorded in the cache
 -       * matches.  I.e. we know the file in the work tree really is
 -       * the same as the <name, sha1> pair.
 +
 +      /*
 +       * If ce matches the file in the work tree, we can reuse it.
         */
 -      return 1;
 +      if (ce_uptodate(ce) ||
 +          (!lstat(name, &st) && !ce_match_stat(ce, &st, 0)))
 +              return 1;
 +
 +      return 0;
  }
  
  static int populate_from_stdin(struct diff_filespec *s)
@@@ -1715,7 -1625,7 +1716,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;
@@@ -1922,6 -1832,9 +1923,9 @@@ static const char *external_diff_attr(c
  {
        struct git_attr_check attr_diff_check;
  
+       if (!name)
+               return NULL;
        setup_diff_attr_check(&attr_diff_check);
        if (!git_checkattr(name, 1, &attr_diff_check)) {
                const char *value = attr_diff_check.value;
  static void run_diff_cmd(const char *pgm,
                         const char *name,
                         const char *other,
+                        const char *attr_path,
                         struct diff_filespec *one,
                         struct diff_filespec *two,
                         const char *xfrm_msg,
        if (!DIFF_OPT_TST(o, ALLOW_EXTERNAL))
                pgm = NULL;
        else {
-               const char *cmd = external_diff_attr(name);
+               const char *cmd = external_diff_attr(attr_path);
                if (cmd)
                        pgm = cmd;
        }
@@@ -1991,6 -1905,15 +1996,15 @@@ static int similarity_index(struct diff
        return p->score * 100 / MAX_SCORE;
  }
  
+ static void strip_prefix(int prefix_length, const char **namep, const char **otherp)
+ {
+       /* Strip the prefix but do not molest /dev/null and absolute paths */
+       if (*namep && **namep != '/')
+               *namep += prefix_length;
+       if (*otherp && **otherp != '/')
+               *otherp += prefix_length;
+ }
  static void run_diff(struct diff_filepair *p, struct diff_options *o)
  {
        const char *pgm = external_diff();
        struct diff_filespec *two = p->two;
        const char *name;
        const char *other;
+       const char *attr_path;
        int complete_rewrite = 0;
  
+       name  = p->one->path;
+       other = (strcmp(name, p->two->path) ? p->two->path : NULL);
+       attr_path = name;
+       if (o->prefix_length)
+               strip_prefix(o->prefix_length, &name, &other);
  
        if (DIFF_PAIR_UNMERGED(p)) {
-               run_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, o, 0);
+               run_diff_cmd(pgm, name, NULL, attr_path,
+                            NULL, NULL, NULL, o, 0);
                return;
        }
  
-       name  = p->one->path;
-       other = (strcmp(name, p->two->path) ? p->two->path : NULL);
        diff_fill_sha1_info(one);
        diff_fill_sha1_info(two);
  
                 * needs to be split into deletion and creation.
                 */
                struct diff_filespec *null = alloc_filespec(two->path);
-               run_diff_cmd(NULL, name, other, one, null, xfrm_msg, o, 0);
+               run_diff_cmd(NULL, name, other, attr_path,
+                            one, null, xfrm_msg, o, 0);
                free(null);
                null = alloc_filespec(one->path);
-               run_diff_cmd(NULL, name, other, null, two, xfrm_msg, o, 0);
+               run_diff_cmd(NULL, name, other, attr_path,
+                            null, two, xfrm_msg, o, 0);
                free(null);
        }
        else
-               run_diff_cmd(pgm, name, other, one, two, xfrm_msg, o,
-                            complete_rewrite);
+               run_diff_cmd(pgm, name, other, attr_path,
+                            one, two, xfrm_msg, o, complete_rewrite);
  
        strbuf_release(&msg);
  }
@@@ -2101,6 -2031,9 +2122,9 @@@ static void run_diffstat(struct diff_fi
        name = p->one->path;
        other = (strcmp(name, p->two->path) ? p->two->path : NULL);
  
+       if (o->prefix_length)
+               strip_prefix(o->prefix_length, &name, &other);
        diff_fill_sha1_info(p->one);
        diff_fill_sha1_info(p->two);
  
@@@ -2113,6 -2046,7 +2137,7 @@@ static void run_checkdiff(struct diff_f
  {
        const char *name;
        const char *other;
+       const char *attr_path;
  
        if (DIFF_PAIR_UNMERGED(p)) {
                /* unmerged */
  
        name = p->one->path;
        other = (strcmp(name, p->two->path) ? p->two->path : NULL);
+       attr_path = other ? other : name;
+       if (o->prefix_length)
+               strip_prefix(o->prefix_length, &name, &other);
  
        diff_fill_sha1_info(p->one);
        diff_fill_sha1_info(p->two);
  
-       builtin_checkdiff(name, other, p->one, p->two, o);
+       builtin_checkdiff(name, other, attr_path, p->one, p->two, o);
  }
  
  void diff_setup(struct diff_options *options)
        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);
@@@ -2168,6 -2105,13 +2197,13 @@@ int diff_setup_done(struct diff_option
        if (DIFF_OPT_TST(options, FIND_COPIES_HARDER))
                options->detect_rename = DIFF_DETECT_COPY;
  
+       if (!DIFF_OPT_TST(options, RELATIVE_NAME))
+               options->prefix = NULL;
+       if (options->prefix)
+               options->prefix_length = strlen(options->prefix);
+       else
+               options->prefix_length = 0;
        if (options->output_format & (DIFF_FORMAT_NAME |
                                      DIFF_FORMAT_NAME_STATUS |
                                      DIFF_FORMAT_CHECKDIFF |
                                            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);
@@@ -2299,10 -2241,6 +2335,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"))
        }
        else if (!strcmp(arg, "--no-renames"))
                options->detect_rename = 0;
+       else if (!strcmp(arg, "--relative"))
+               DIFF_OPT_SET(options, RELATIVE_NAME);
+       else if (!prefixcmp(arg, "--relative=")) {
+               DIFF_OPT_SET(options, RELATIVE_NAME);
+               options->prefix = arg + 11;
+       }
  
        /* xdiff options */
        else if (!strcmp(arg, "-w") || !strcmp(arg, "--ignore-all-space"))
@@@ -2573,12 -2517,20 +2615,20 @@@ static void diff_flush_raw(struct diff_
                printf("%c%c", p->status, inter_name_termination);
        }
  
-       if (p->status == DIFF_STATUS_COPIED || p->status == DIFF_STATUS_RENAMED) {
-               write_name_quoted(p->one->path, stdout, inter_name_termination);
-               write_name_quoted(p->two->path, stdout, line_termination);
+       if (p->status == DIFF_STATUS_COPIED ||
+           p->status == DIFF_STATUS_RENAMED) {
+               const char *name_a, *name_b;
+               name_a = p->one->path;
+               name_b = p->two->path;
+               strip_prefix(opt->prefix_length, &name_a, &name_b);
+               write_name_quoted(name_a, stdout, inter_name_termination);
+               write_name_quoted(name_b, stdout, line_termination);
        } else {
-               const char *path = p->one->mode ? p->one->path : p->two->path;
-               write_name_quoted(path, stdout, line_termination);
+               const char *name_a, *name_b;
+               name_a = p->one->mode ? p->one->path : p->two->path;
+               name_b = NULL;
+               strip_prefix(opt->prefix_length, &name_a, &name_b);
+               write_name_quoted(name_a, stdout, line_termination);
        }
  }
  
@@@ -2775,8 -2727,13 +2825,13 @@@ static void flush_one_pair(struct diff_
                diff_flush_checkdiff(p, opt);
        else if (fmt & (DIFF_FORMAT_RAW | DIFF_FORMAT_NAME_STATUS))
                diff_flush_raw(p, opt);
-       else if (fmt & DIFF_FORMAT_NAME)
-               write_name_quoted(p->two->path, stdout, opt->line_termination);
+       else if (fmt & DIFF_FORMAT_NAME) {
+               const char *name_a, *name_b;
+               name_a = p->two->path;
+               name_b = NULL;
+               strip_prefix(opt->prefix_length, &name_a, &name_b);
+               write_name_quoted(name_a, stdout, opt->line_termination);
+       }
  }
  
  static void show_file_mode_name(const char *newdelete, struct diff_filespec *fs)
@@@ -3021,7 -2978,7 +3076,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)
@@@ -3264,6 -3219,11 +3319,11 @@@ void diff_addremove(struct diff_option
  
        if (!path) path = "";
        sprintf(concatpath, "%s%s", base, path);
+       if (options->prefix &&
+           strncmp(concatpath, options->prefix, options->prefix_length))
+               return;
        one = alloc_filespec(concatpath);
        two = alloc_filespec(concatpath);
  
@@@ -3293,6 -3253,11 +3353,11 @@@ void diff_change(struct diff_options *o
        }
        if (!path) path = "";
        sprintf(concatpath, "%s%s", base, path);
+       if (options->prefix &&
+           strncmp(concatpath, options->prefix, options->prefix_length))
+               return;
        one = alloc_filespec(concatpath);
        two = alloc_filespec(concatpath);
        fill_filespec(one, old_sha1, old_mode);
@@@ -3307,6 -3272,11 +3372,11 @@@ void diff_unmerge(struct diff_options *
                  unsigned mode, const unsigned char *sha1)
  {
        struct diff_filespec *one, *two;
+       if (options->prefix &&
+           strncmp(path, options->prefix, options->prefix_length))
+               return;
        one = alloc_filespec(path);
        two = alloc_filespec(path);
        fill_filespec(one, sha1, mode);
diff --combined diff.h
index c5d3a4f965380fd56791fc99864fa27b758da7ca,fcd9653822effd07c0ebb34b4f2c65e55ebf5d2e..9a652e7f38e3c1aeb7c7eaf840dea7bcea285311
--- 1/diff.h
--- 2/diff.h
+++ b/diff.h
@@@ -30,8 -30,6 +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
@@@ -62,6 -60,7 +62,7 @@@
  #define DIFF_OPT_EXIT_WITH_STATUS    (1 << 14)
  #define DIFF_OPT_REVERSE_DIFF        (1 << 15)
  #define DIFF_OPT_CHECK_FAILED        (1 << 16)
+ #define DIFF_OPT_RELATIVE_NAME       (1 << 17)
  #define DIFF_OPT_TST(opts, flag)    ((opts)->flags & DIFF_OPT_##flag)
  #define DIFF_OPT_SET(opts, flag)    ((opts)->flags |= DIFF_OPT_##flag)
  #define DIFF_OPT_CLR(opts, flag)    ((opts)->flags &= ~DIFF_OPT_##flag)
@@@ -82,9 -81,10 +83,11 @@@ struct diff_options 
        int pickaxe_opts;
        int rename_score;
        int rename_limit;
 +      int dirstat_percent;
        int setup;
        int abbrev;
+       const char *prefix;
+       int prefix_length;
        const char *msg_sep;
        const char *stat_sep;
        long xdl_opts;
@@@ -177,7 -177,6 +180,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 *);
diff --combined revision.c
index 5df7961c501488a5393fd65de5de4bcf2eac1c25,4d6f57b64e077b3e3853a199daad7511b5ab2f4c..84fbdd3af47c40879874d50b173ac633d279d734
@@@ -46,8 -46,6 +46,8 @@@ void add_object(struct object *obj
  
  static void mark_blob_uninteresting(struct blob *blob)
  {
 +      if (!blob)
 +              return;
        if (blob->object.flags & UNINTERESTING)
                return;
        blob->object.flags |= UNINTERESTING;
@@@ -59,8 -57,6 +59,8 @@@ void mark_tree_uninteresting(struct tre
        struct name_entry entry;
        struct object *obj = &tree->object;
  
 +      if (!tree)
 +              return;
        if (obj->flags & UNINTERESTING)
                return;
        obj->flags |= UNINTERESTING;
@@@ -177,8 -173,6 +177,8 @@@ static struct commit *handle_commit(str
                struct tag *tag = (struct tag *) object;
                if (revs->tag_objects && !(flags & UNINTERESTING))
                        add_pending_object(revs, object, tag->tag);
 +              if (!tag->tagged)
 +                      die("bad tag");
                object = parse_object(tag->tagged->sha1);
                if (!object)
                        die("bad object %s", sha1_to_hex(tag->tagged->sha1));
@@@ -564,12 -558,6 +564,12 @@@ static void cherry_pick_list(struct com
        free_patch_ids(&ids);
  }
  
 +static void add_to_list(struct commit_list **p, struct commit *commit, struct commit_list *n)
 +{
 +      p = &commit_list_insert(commit, p)->next;
 +      *p = n;
 +}
 +
  static int limit_list(struct rev_info *revs)
  {
        struct commit_list *list = revs->commits;
                        return -1;
                if (obj->flags & UNINTERESTING) {
                        mark_parents_uninteresting(commit);
 -                      if (everybody_uninteresting(list))
 +                      if (everybody_uninteresting(list)) {
 +                              if (revs->show_all)
 +                                      add_to_list(p, commit, list);
                                break;
 -                      continue;
 +                      }
 +                      if (!revs->show_all)
 +                              continue;
                }
                if (revs->min_age != -1 && (commit->date > revs->min_age))
                        continue;
@@@ -701,8 -685,6 +701,8 @@@ static int add_parents_only(struct rev_
                it = get_reference(revs, arg, sha1, 0);
                if (it->type != OBJ_TAG)
                        break;
 +              if (!((struct tag*)it)->tagged)
 +                      return 0;
                hashcpy(sha1, ((struct tag*)it)->tagged->sha1);
        }
        if (it->type != OBJ_COMMIT)
@@@ -738,6 -720,10 +738,10 @@@ void init_revisions(struct rev_info *re
        revs->commit_format = CMIT_FMT_DEFAULT;
  
        diff_setup(&revs->diffopt);
+       if (prefix && !revs->diffopt.prefix) {
+               revs->diffopt.prefix = prefix;
+               revs->diffopt.prefix_length = strlen(prefix);
+       }
  }
  
  static void add_pending_commit_list(struct rev_info *revs,
@@@ -942,7 -928,6 +946,7 @@@ int setup_revisions(int argc, const cha
        int left = 1;
        int all_match = 0;
        int regflags = 0;
 +      int fixed = 0;
  
        /* First, search for "--" */
        seen_dashdash = 0;
                                revs->dense = 0;
                                continue;
                        }
 +                      if (!strcmp(arg, "--show-all")) {
 +                              revs->show_all = 1;
 +                              continue;
 +                      }
                        if (!strcmp(arg, "--remove-empty")) {
                                revs->remove_empty_trees = 1;
                                continue;
                                regflags |= REG_ICASE;
                                continue;
                        }
 +                      if (!strcmp(arg, "--fixed-strings") ||
 +                          !strcmp(arg, "-F")) {
 +                              fixed = 1;
 +                              continue;
 +                      }
                        if (!strcmp(arg, "--all-match")) {
                                all_match = 1;
                                continue;
                }
        }
  
 -      if (revs->grep_filter)
 +      if (revs->grep_filter) {
                revs->grep_filter->regflags |= regflags;
 +              revs->grep_filter->fixed = fixed;
 +      }
  
        if (show_merge)
                prepare_show_merge(revs);
@@@ -1468,8 -1442,6 +1472,8 @@@ enum commit_action simplify_commit(stru
                return commit_ignore;
        if (revs->unpacked && has_sha1_pack(commit->object.sha1, revs->ignore_packed))
                return commit_ignore;
 +      if (revs->show_all)
 +              return commit_show;
        if (commit->object.flags & UNINTERESTING)
                return commit_ignore;
        if (revs->min_age != -1 && (commit->date > revs->min_age))