Merge branch 'th/diff-extra'
authorJunio C Hamano <junkio@cox.net>
Sun, 13 Aug 2006 02:34:41 +0000 (19:34 -0700)
committerJunio C Hamano <junkio@cox.net>
Sun, 13 Aug 2006 02:34:41 +0000 (19:34 -0700)
1  2 
builtin-diff-files.c
builtin-diff.c
diff.c
diff --combined builtin-diff-files.c
index ac13db70ff741240f1f27f98dfe09614da4ea87c,3361898ada6f6606293e9bf8e1ed1539dff41a8d..5d4a5c5828d1202b6d6b2eadffc51fc131a37dc5
@@@ -13,13 -13,13 +13,13 @@@ static const char diff_files_usage[] 
  "git-diff-files [-q] [-0/-1/2/3 |-c|--cc] [<common diff options>] [<path>...]"
  COMMON_DIFF_OPTIONS_HELP;
  
 -int cmd_diff_files(int argc, const char **argv, char **envp)
 +int cmd_diff_files(int argc, const char **argv, const char *prefix)
  {
        struct rev_info rev;
        int silent = 0;
  
 -      git_config(git_diff_config);
 -      init_revisions(&rev);
 +      init_revisions(&rev, prefix);
 +      git_config(git_default_config); /* no "diff" UI options */
        rev.abbrev = 0;
  
        argc = setup_revisions(argc, argv, &rev, NULL);
        if (rev.pending.nr ||
            rev.min_age != -1 || rev.max_age != -1)
                usage(diff_files_usage);
-       /*
-        * Backward compatibility wart - "diff-files -s" used to
-        * defeat the common diff option "-s" which asked for
-        * DIFF_FORMAT_NO_OUTPUT.
-        */
-       if (rev.diffopt.output_format == DIFF_FORMAT_NO_OUTPUT)
-               rev.diffopt.output_format = DIFF_FORMAT_RAW;
        return run_diff_files(&rev, silent);
  }
diff --combined builtin-diff.c
index a090e298a55ef110a6fdd0e0dde54ea744a52f76,059eb6de4932ad7f67a09559da5c08144f14de3f..82afce782d88a1f773599332910982ef804aea12
@@@ -23,7 -23,7 +23,7 @@@ struct blobinfo 
  };
  
  static const char builtin_diff_usage[] =
 -"diff <options> <rev>{0,2} -- <path>*";
 +"git-diff <options> <rev>{0,2} -- <path>*";
  
  static int builtin_diff_files(struct rev_info *revs,
                              int argc, const char **argv)
        if (revs->max_count < 0 &&
            (revs->diffopt.output_format & DIFF_FORMAT_PATCH))
                revs->combine_merges = revs->dense_combined_merges = 1;
-       /*
-        * Backward compatibility wart - "diff-files -s" used to
-        * defeat the common diff option "-s" which asked for
-        * DIFF_FORMAT_NO_OUTPUT.
-        */
-       if (revs->diffopt.output_format == DIFF_FORMAT_NO_OUTPUT)
-               revs->diffopt.output_format = DIFF_FORMAT_RAW;
        return run_diff_files(revs, silent);
  }
  
@@@ -125,6 -118,9 +118,6 @@@ static int builtin_diff_blobs(struct re
                              int argc, const char **argv,
                              struct blobinfo *blob)
  {
 -      /* Blobs: the arguments are reversed when setup_revisions()
 -       * picked them up.
 -       */
        unsigned mode = canon_mode(S_IFREG | 0644);
  
        if (argc > 1)
  
        stuff_change(&revs->diffopt,
                     mode, mode,
 -                   blob[1].sha1, blob[0].sha1,
 -                   blob[0].name, blob[0].name);
 +                   blob[0].sha1, blob[1].sha1,
 +                   blob[0].name, blob[1].name);
        diffcore_std(&revs->diffopt);
        diff_flush(&revs->diffopt);
        return 0;
@@@ -173,7 -169,7 +166,7 @@@ static int builtin_diff_tree(struct rev
                usage(builtin_diff_usage);
  
        /* We saw two trees, ent[0] and ent[1].
 -       * if ent[1] is unintesting, they are swapped
 +       * if ent[1] is uninteresting, they are swapped
         */
        if (ent[1].item->flags & UNINTERESTING)
                swap = 1;
@@@ -218,7 -214,7 +211,7 @@@ void add_head(struct rev_info *revs
        add_pending_object(revs, obj, "HEAD");
  }
  
 -int cmd_diff(int argc, const char **argv, char **envp)
 +int cmd_diff(int argc, const char **argv, const char *prefix)
  {
        int i;
        struct rev_info rev;
         * Other cases are errors.
         */
  
 -      git_config(git_diff_config);
 -      init_revisions(&rev);
 +      git_config(git_diff_ui_config);
 +      init_revisions(&rev, prefix);
  
        argc = setup_revisions(argc, argv, &rev, NULL);
        if (!rev.diffopt.output_format) {
                rev.diffopt.output_format = DIFF_FORMAT_PATCH;
 -              diff_setup_done(&rev.diffopt);
 +              if (diff_setup_done(&rev.diffopt) < 0)
 +                      die("diff_setup_done failed");
        }
  
        /* Do we have --cached and not have a pending object, then
                obj = deref_tag(obj, NULL, 0);
                if (!obj)
                        die("invalid object '%s' given.", name);
 -              if (obj->type == TYPE_COMMIT)
 +              if (obj->type == OBJ_COMMIT)
                        obj = &((struct commit *)obj)->tree->object;
 -              if (obj->type == TYPE_TREE) {
 +              if (obj->type == OBJ_TREE) {
                        if (ARRAY_SIZE(ent) <= ents)
                                die("more than %d trees given: '%s'",
                                    (int) ARRAY_SIZE(ent), name);
                        ents++;
                        continue;
                }
 -              if (obj->type == TYPE_BLOB) {
 +              if (obj->type == OBJ_BLOB) {
                        if (2 <= blobs)
                                die("more than two blobs given: '%s'", name);
                        memcpy(blob[blobs].sha1, obj->sha1, 20);
                return builtin_diff_index(&rev, argc, argv);
        else if (ents == 2)
                return builtin_diff_tree(&rev, argc, argv, ent);
 +      else if ((ents == 3) && (ent[0].item->flags & UNINTERESTING)) {
 +              /* diff A...B where there is one sane merge base between
 +               * A and B.  We have ent[0] == merge-base, ent[1] == A,
 +               * and ent[2] == B.  Show diff between the base and B.
 +               */
 +              ent[1] = ent[2];
 +              return builtin_diff_tree(&rev, argc, argv, ent);
 +      }
        else
 -              return builtin_diff_combined(&rev, argc, argv, ent, ents);
 +              return builtin_diff_combined(&rev, argc, argv,
 +                                           ent, ents);
        usage(builtin_diff_usage);
  }
diff --combined diff.c
index a8710cb9e2388ce915fabc99cb86caacd1020961,5358fd0c14a249c2d1a3fe417016264aba6fc745..8861b853e70ab511fdcab16d97687aafc99ec000
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
  
  static int use_size_cache;
  
 +static int diff_detect_rename_default = 0;
  static int diff_rename_limit_default = -1;
  static int diff_use_color_default = 0;
  
 -enum color_diff {
 -      DIFF_RESET = 0,
 -      DIFF_PLAIN = 1,
 -      DIFF_METAINFO = 2,
 -      DIFF_FRAGINFO = 3,
 -      DIFF_FILE_OLD = 4,
 -      DIFF_FILE_NEW = 5,
 -};
 -
 -#define COLOR_NORMAL  ""
 -#define COLOR_BOLD    "\033[1m"
 -#define COLOR_DIM     "\033[2m"
 -#define COLOR_UL      "\033[4m"
 -#define COLOR_BLINK   "\033[5m"
 -#define COLOR_REVERSE "\033[7m"
 -#define COLOR_RESET   "\033[m"
 -
 -#define COLOR_BLACK   "\033[30m"
 -#define COLOR_RED     "\033[31m"
 -#define COLOR_GREEN   "\033[32m"
 -#define COLOR_YELLOW  "\033[33m"
 -#define COLOR_BLUE    "\033[34m"
 -#define COLOR_MAGENTA "\033[35m"
 -#define COLOR_CYAN    "\033[36m"
 -#define COLOR_WHITE   "\033[37m"
 -
 -static const char *diff_colors[] = {
 -      [DIFF_RESET]    = COLOR_RESET,
 -      [DIFF_PLAIN]    = COLOR_NORMAL,
 -      [DIFF_METAINFO] = COLOR_BOLD,
 -      [DIFF_FRAGINFO] = COLOR_CYAN,
 -      [DIFF_FILE_OLD] = COLOR_RED,
 -      [DIFF_FILE_NEW] = COLOR_GREEN,
 +/* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */
 +static char diff_colors[][24] = {
 +      "\033[m",       /* reset */
 +      "",             /* normal */
 +      "\033[1m",      /* bold */
 +      "\033[36m",     /* cyan */
 +      "\033[31m",     /* red */
 +      "\033[32m",     /* green */
 +      "\033[33m"      /* yellow */
  };
  
  static int parse_diff_color_slot(const char *var, int ofs)
                return DIFF_FILE_OLD;
        if (!strcasecmp(var+ofs, "new"))
                return DIFF_FILE_NEW;
 +      if (!strcasecmp(var+ofs, "commit"))
 +              return DIFF_COMMIT;
        die("bad config variable '%s'", var);
  }
  
 -static const char *parse_diff_color_value(const char *value, const char *var)
 -{
 -      if (!strcasecmp(value, "normal"))
 -              return COLOR_NORMAL;
 -      if (!strcasecmp(value, "bold"))
 -              return COLOR_BOLD;
 -      if (!strcasecmp(value, "dim"))
 -              return COLOR_DIM;
 -      if (!strcasecmp(value, "ul"))
 -              return COLOR_UL;
 -      if (!strcasecmp(value, "blink"))
 -              return COLOR_BLINK;
 -      if (!strcasecmp(value, "reverse"))
 -              return COLOR_REVERSE;
 -      if (!strcasecmp(value, "reset"))
 -              return COLOR_RESET;
 -      if (!strcasecmp(value, "black"))
 -              return COLOR_BLACK;
 -      if (!strcasecmp(value, "red"))
 -              return COLOR_RED;
 -      if (!strcasecmp(value, "green"))
 -              return COLOR_GREEN;
 -      if (!strcasecmp(value, "yellow"))
 -              return COLOR_YELLOW;
 -      if (!strcasecmp(value, "blue"))
 -              return COLOR_BLUE;
 -      if (!strcasecmp(value, "magenta"))
 -              return COLOR_MAGENTA;
 -      if (!strcasecmp(value, "cyan"))
 -              return COLOR_CYAN;
 -      if (!strcasecmp(value, "white"))
 -              return COLOR_WHITE;
 +static int parse_color(const char *name, int len)
 +{
 +      static const char * const color_names[] = {
 +              "normal", "black", "red", "green", "yellow",
 +              "blue", "magenta", "cyan", "white"
 +      };
 +      char *end;
 +      int i;
 +      for (i = 0; i < ARRAY_SIZE(color_names); i++) {
 +              const char *str = color_names[i];
 +              if (!strncasecmp(name, str, len) && !str[len])
 +                      return i - 1;
 +      }
 +      i = strtol(name, &end, 10);
 +      if (*name && !*end && i >= -1 && i <= 255)
 +              return i;
 +      return -2;
 +}
 +
 +static int parse_attr(const char *name, int len)
 +{
 +      static const int attr_values[] = { 1, 2, 4, 5, 7 };
 +      static const char * const attr_names[] = {
 +              "bold", "dim", "ul", "blink", "reverse"
 +      };
 +      int i;
 +      for (i = 0; i < ARRAY_SIZE(attr_names); i++) {
 +              const char *str = attr_names[i];
 +              if (!strncasecmp(name, str, len) && !str[len])
 +                      return attr_values[i];
 +      }
 +      return -1;
 +}
 +
 +static void parse_diff_color_value(const char *value, const char *var, char *dst)
 +{
 +      const char *ptr = value;
 +      int attr = -1;
 +      int fg = -2;
 +      int bg = -2;
 +
 +      if (!strcasecmp(value, "reset")) {
 +              strcpy(dst, "\033[m");
 +              return;
 +      }
 +
 +      /* [fg [bg]] [attr] */
 +      while (*ptr) {
 +              const char *word = ptr;
 +              int val, len = 0;
 +
 +              while (word[len] && !isspace(word[len]))
 +                      len++;
 +
 +              ptr = word + len;
 +              while (*ptr && isspace(*ptr))
 +                      ptr++;
 +
 +              val = parse_color(word, len);
 +              if (val >= -1) {
 +                      if (fg == -2) {
 +                              fg = val;
 +                              continue;
 +                      }
 +                      if (bg == -2) {
 +                              bg = val;
 +                              continue;
 +                      }
 +                      goto bad;
 +              }
 +              val = parse_attr(word, len);
 +              if (val < 0 || attr != -1)
 +                      goto bad;
 +              attr = val;
 +      }
 +
 +      if (attr >= 0 || fg >= 0 || bg >= 0) {
 +              int sep = 0;
 +
 +              *dst++ = '\033';
 +              *dst++ = '[';
 +              if (attr >= 0) {
 +                      *dst++ = '0' + attr;
 +                      sep++;
 +              }
 +              if (fg >= 0) {
 +                      if (sep++)
 +                              *dst++ = ';';
 +                      if (fg < 8) {
 +                              *dst++ = '3';
 +                              *dst++ = '0' + fg;
 +                      } else {
 +                              dst += sprintf(dst, "38;5;%d", fg);
 +                      }
 +              }
 +              if (bg >= 0) {
 +                      if (sep++)
 +                              *dst++ = ';';
 +                      if (bg < 8) {
 +                              *dst++ = '4';
 +                              *dst++ = '0' + bg;
 +                      } else {
 +                              dst += sprintf(dst, "48;5;%d", bg);
 +                      }
 +              }
 +              *dst++ = 'm';
 +      }
 +      *dst = 0;
 +      return;
 +bad:
        die("bad config value '%s' for variable '%s'", value, var);
  }
  
 -int git_diff_config(const char *var, const char *value)
 +/*
 + * These are to give UI layer defaults.
 + * The core-level commands such as git-diff-files should
 + * never be affected by the setting of diff.renames
 + * the user happens to have in the configuration file.
 + */
 +int git_diff_ui_config(const char *var, const char *value)
  {
        if (!strcmp(var, "diff.renamelimit")) {
                diff_rename_limit_default = git_config_int(var, value);
        if (!strcmp(var, "diff.color")) {
                if (!value)
                        diff_use_color_default = 1; /* bool */
 -              else if (!strcasecmp(value, "auto"))
 -                      diff_use_color_default = isatty(1);
 +              else if (!strcasecmp(value, "auto")) {
 +                      diff_use_color_default = 0;
 +                      if (isatty(1) || (pager_in_use && pager_use_color)) {
 +                              char *term = getenv("TERM");
 +                              if (term && strcmp(term, "dumb"))
 +                                      diff_use_color_default = 1;
 +                      }
 +              }
                else if (!strcasecmp(value, "never"))
                        diff_use_color_default = 0;
                else if (!strcasecmp(value, "always"))
                        diff_use_color_default = git_config_bool(var, value);
                return 0;
        }
 +      if (!strcmp(var, "diff.renames")) {
 +              if (!value)
 +                      diff_detect_rename_default = DIFF_DETECT_RENAME;
 +              else if (!strcasecmp(value, "copies") ||
 +                       !strcasecmp(value, "copy"))
 +                      diff_detect_rename_default = DIFF_DETECT_COPY;
 +              else if (git_config_bool(var,value))
 +                      diff_detect_rename_default = DIFF_DETECT_RENAME;
 +              return 0;
 +      }
        if (!strncmp(var, "diff.color.", 11)) {
                int slot = parse_diff_color_slot(var, 11);
 -              diff_colors[slot] = parse_diff_color_value(value, var);
 +              parse_diff_color_value(value, var, diff_colors[slot]);
                return 0;
        }
        return git_default_config(var, value);
@@@ -358,153 -279,13 +358,153 @@@ static int fill_mmfile(mmfile_t *mf, st
        return 0;
  }
  
 +struct diff_words_buffer {
 +      mmfile_t text;
 +      long alloc;
 +      long current; /* output pointer */
 +      int suppressed_newline;
 +};
 +
 +static void diff_words_append(char *line, unsigned long len,
 +              struct diff_words_buffer *buffer)
 +{
 +      if (buffer->text.size + len > buffer->alloc) {
 +              buffer->alloc = (buffer->text.size + len) * 3 / 2;
 +              buffer->text.ptr = xrealloc(buffer->text.ptr, buffer->alloc);
 +      }
 +      line++;
 +      len--;
 +      memcpy(buffer->text.ptr + buffer->text.size, line, len);
 +      buffer->text.size += len;
 +}
 +
 +struct diff_words_data {
 +      struct xdiff_emit_state xm;
 +      struct diff_words_buffer minus, plus;
 +};
 +
 +static void print_word(struct diff_words_buffer *buffer, int len, int color,
 +              int suppress_newline)
 +{
 +      const char *ptr;
 +      int eol = 0;
 +
 +      if (len == 0)
 +              return;
 +
 +      ptr  = buffer->text.ptr + buffer->current;
 +      buffer->current += len;
 +
 +      if (ptr[len - 1] == '\n') {
 +              eol = 1;
 +              len--;
 +      }
 +
 +      fputs(diff_get_color(1, color), stdout);
 +      fwrite(ptr, len, 1, stdout);
 +      fputs(diff_get_color(1, DIFF_RESET), stdout);
 +
 +      if (eol) {
 +              if (suppress_newline)
 +                      buffer->suppressed_newline = 1;
 +              else
 +                      putchar('\n');
 +      }
 +}
 +
 +static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)
 +{
 +      struct diff_words_data *diff_words = priv;
 +
 +      if (diff_words->minus.suppressed_newline) {
 +              if (line[0] != '+')
 +                      putchar('\n');
 +              diff_words->minus.suppressed_newline = 0;
 +      }
 +
 +      len--;
 +      switch (line[0]) {
 +              case '-':
 +                      print_word(&diff_words->minus, len, DIFF_FILE_OLD, 1);
 +                      break;
 +              case '+':
 +                      print_word(&diff_words->plus, len, DIFF_FILE_NEW, 0);
 +                      break;
 +              case ' ':
 +                      print_word(&diff_words->plus, len, DIFF_PLAIN, 0);
 +                      diff_words->minus.current += len;
 +                      break;
 +      }
 +}
 +
 +/* this executes the word diff on the accumulated buffers */
 +static void diff_words_show(struct diff_words_data *diff_words)
 +{
 +      xpparam_t xpp;
 +      xdemitconf_t xecfg;
 +      xdemitcb_t ecb;
 +      mmfile_t minus, plus;
 +      int i;
 +
 +      minus.size = diff_words->minus.text.size;
 +      minus.ptr = xmalloc(minus.size);
 +      memcpy(minus.ptr, diff_words->minus.text.ptr, minus.size);
 +      for (i = 0; i < minus.size; i++)
 +              if (isspace(minus.ptr[i]))
 +                      minus.ptr[i] = '\n';
 +      diff_words->minus.current = 0;
 +
 +      plus.size = diff_words->plus.text.size;
 +      plus.ptr = xmalloc(plus.size);
 +      memcpy(plus.ptr, diff_words->plus.text.ptr, plus.size);
 +      for (i = 0; i < plus.size; i++)
 +              if (isspace(plus.ptr[i]))
 +                      plus.ptr[i] = '\n';
 +      diff_words->plus.current = 0;
 +
 +      xpp.flags = XDF_NEED_MINIMAL;
 +      xecfg.ctxlen = diff_words->minus.alloc + diff_words->plus.alloc;
 +      xecfg.flags = 0;
 +      ecb.outf = xdiff_outf;
 +      ecb.priv = diff_words;
 +      diff_words->xm.consume = fn_out_diff_words_aux;
 +      xdl_diff(&minus, &plus, &xpp, &xecfg, &ecb);
 +
 +      free(minus.ptr);
 +      free(plus.ptr);
 +      diff_words->minus.text.size = diff_words->plus.text.size = 0;
 +
 +      if (diff_words->minus.suppressed_newline) {
 +              putchar('\n');
 +              diff_words->minus.suppressed_newline = 0;
 +      }
 +}
 +
  struct emit_callback {
        struct xdiff_emit_state xm;
        int nparents, color_diff;
        const char **label_path;
 +      struct diff_words_data *diff_words;
  };
  
 -static inline const char *get_color(int diff_use_color, enum color_diff ix)
 +static void free_diff_words_data(struct emit_callback *ecbdata)
 +{
 +      if (ecbdata->diff_words) {
 +              /* flush buffers */
 +              if (ecbdata->diff_words->minus.text.size ||
 +                              ecbdata->diff_words->plus.text.size)
 +                      diff_words_show(ecbdata->diff_words);
 +
 +              if (ecbdata->diff_words->minus.text.ptr)
 +                      free (ecbdata->diff_words->minus.text.ptr);
 +              if (ecbdata->diff_words->plus.text.ptr)
 +                      free (ecbdata->diff_words->plus.text.ptr);
 +              free(ecbdata->diff_words);
 +              ecbdata->diff_words = NULL;
 +      }
 +}
 +
 +const char *diff_get_color(int diff_use_color, enum color_diff ix)
  {
        if (diff_use_color)
                return diff_colors[ix];
@@@ -515,8 -296,8 +515,8 @@@ static void fn_out_consume(void *priv, 
  {
        int i;
        struct emit_callback *ecbdata = priv;
 -      const char *set = get_color(ecbdata->color_diff, DIFF_METAINFO);
 -      const char *reset = get_color(ecbdata->color_diff, DIFF_RESET);
 +      const char *set = diff_get_color(ecbdata->color_diff, DIFF_METAINFO);
 +      const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
  
        if (ecbdata->label_path[0]) {
                printf("%s--- %s%s\n", set, ecbdata->label_path[0], reset);
                ;
        if (2 <= i && i < len && line[i] == ' ') {
                ecbdata->nparents = i - 1;
 -              set = get_color(ecbdata->color_diff, DIFF_FRAGINFO);
 +              set = diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO);
        }
        else if (len < ecbdata->nparents)
                set = reset;
        else {
                int nparents = ecbdata->nparents;
                int color = DIFF_PLAIN;
 -              for (i = 0; i < nparents && len; i++) {
 -                      if (line[i] == '-')
 -                              color = DIFF_FILE_OLD;
 -                      else if (line[i] == '+')
 -                              color = DIFF_FILE_NEW;
 -              }
 -              set = get_color(ecbdata->color_diff, color);
 +              if (ecbdata->diff_words && nparents != 1)
 +                      /* fall back to normal diff */
 +                      free_diff_words_data(ecbdata);
 +              if (ecbdata->diff_words) {
 +                      if (line[0] == '-') {
 +                              diff_words_append(line, len,
 +                                              &ecbdata->diff_words->minus);
 +                              return;
 +                      } else if (line[0] == '+') {
 +                              diff_words_append(line, len,
 +                                              &ecbdata->diff_words->plus);
 +                              return;
 +                      }
 +                      if (ecbdata->diff_words->minus.text.size ||
 +                                      ecbdata->diff_words->plus.text.size)
 +                              diff_words_show(ecbdata->diff_words);
 +                      line++;
 +                      len--;
 +              } else
 +                      for (i = 0; i < nparents && len; i++) {
 +                              if (line[i] == '-')
 +                                      color = DIFF_FILE_OLD;
 +                              else if (line[i] == '+')
 +                                      color = DIFF_FILE_NEW;
 +                      }
 +              set = diff_get_color(ecbdata->color_diff, color);
        }
        if (len > 0 && line[len-1] == '\n')
                len--;
 -      printf("%s%.*s%s\n", set, (int) len, line, reset);
 +      fputs (set, stdout);
 +      fwrite (line, len, 1, stdout);
 +      puts (reset);
  }
  
  static char *pprint_rename(const char *a, const char *b)
@@@ -823,7 -583,7 +823,7 @@@ static unsigned char *deflate_it(char *
        z_stream stream;
  
        memset(&stream, 0, sizeof(stream));
 -      deflateInit(&stream, Z_BEST_COMPRESSION);
 +      deflateInit(&stream, zlib_compression_level);
        bound = deflateBound(&stream, size);
        deflated = xmalloc(bound);
        stream.next_out = deflated;
@@@ -920,8 -680,8 +920,8 @@@ static void builtin_diff(const char *na
        mmfile_t mf1, mf2;
        const char *lbl[2];
        char *a_one, *b_two;
 -      const char *set = get_color(o->color_diff, DIFF_METAINFO);
 -      const char *reset = get_color(o->color_diff, DIFF_RESET);
 +      const char *set = diff_get_color(o->color_diff, DIFF_METAINFO);
 +      const char *reset = diff_get_color(o->color_diff, DIFF_RESET);
  
        a_one = quote_two("a/", name_a);
        b_two = quote_two("b/", name_b);
        if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
                die("unable to read files to diff");
  
 -      if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2)) {
 +      if (!o->text && (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2))) {
                /* Quite common confusing case */
                if (mf1.size == mf2.size &&
                    !memcmp(mf1.ptr, mf2.ptr, mf1.size))
                ecb.outf = xdiff_outf;
                ecb.priv = &ecbdata;
                ecbdata.xm.consume = fn_out_consume;
 +              if (o->color_diff_words)
 +                      ecbdata.diff_words =
 +                              xcalloc(1, sizeof(struct diff_words_data));
                xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
 +              if (o->color_diff_words)
 +                      free_diff_words_data(&ecbdata);
        }
  
   free_ab_and_return:
@@@ -1674,13 -1429,27 +1674,26 @@@ void diff_setup(struct diff_options *op
        options->change = diff_change;
        options->add_remove = diff_addremove;
        options->color_diff = diff_use_color_default;
 +      options->detect_rename = diff_detect_rename_default;
  }
  
  int diff_setup_done(struct diff_options *options)
  {
 -      if ((options->find_copies_harder &&
 -           options->detect_rename != DIFF_DETECT_COPY) ||
 -          (0 <= options->rename_limit && !options->detect_rename))
 -              return -1;
+       int count = 0;
+       if (options->output_format & DIFF_FORMAT_NAME)
+               count++;
+       if (options->output_format & DIFF_FORMAT_NAME_STATUS)
+               count++;
+       if (options->output_format & DIFF_FORMAT_CHECKDIFF)
+               count++;
+       if (options->output_format & DIFF_FORMAT_NO_OUTPUT)
+               count++;
+       if (count > 1)
+               die("--name-only, --name-status, --check and -s are mutually exclusive");
 +      if (options->find_copies_harder)
 +              options->detect_rename = DIFF_DETECT_COPY;
  
        if (options->output_format & (DIFF_FORMAT_NAME |
                                      DIFF_FORMAT_NAME_STATUS |
@@@ -1803,9 -1572,6 +1816,9 @@@ int diff_opt_parse(struct diff_options 
                options->output_format |= DIFF_FORMAT_PATCH;
                options->full_index = options->binary = 1;
        }
 +      else if (!strcmp(arg, "-a") || !strcmp(arg, "--text")) {
 +              options->text = 1;
 +      }
        else if (!strcmp(arg, "--name-only"))
                options->output_format |= DIFF_FORMAT_NAME;
        else if (!strcmp(arg, "--name-status"))
        }
        else if (!strcmp(arg, "--color"))
                options->color_diff = 1;
 +      else if (!strcmp(arg, "--no-color"))
 +              options->color_diff = 0;
        else if (!strcmp(arg, "-w") || !strcmp(arg, "--ignore-all-space"))
                options->xdl_opts |= XDF_IGNORE_WHITESPACE;
        else if (!strcmp(arg, "-b") || !strcmp(arg, "--ignore-space-change"))
                options->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE;
 +      else if (!strcmp(arg, "--color-words"))
 +              options->color_diff = options->color_diff_words = 1;
 +      else if (!strcmp(arg, "--no-renames"))
 +              options->detect_rename = 0;
        else
                return 0;
        return 1;
@@@ -1950,9 -1710,13 +1963,9 @@@ struct diff_filepair *diff_queue(struc
                                 struct diff_filespec *one,
                                 struct diff_filespec *two)
  {
 -      struct diff_filepair *dp = xmalloc(sizeof(*dp));
 +      struct diff_filepair *dp = xcalloc(1, sizeof(*dp));
        dp->one = one;
        dp->two = two;
 -      dp->score = 0;
 -      dp->status = 0;
 -      dp->source_stays = 0;
 -      dp->broken_pair = 0;
        if (queue)
                diff_q(queue, dp);
        return dp;
@@@ -2344,145 -2108,6 +2357,145 @@@ static void diff_summary(struct diff_fi
        }
  }
  
 +struct patch_id_t {
 +      struct xdiff_emit_state xm;
 +      SHA_CTX *ctx;
 +      int patchlen;
 +};
 +
 +static int remove_space(char *line, int len)
 +{
 +      int i;
 +        char *dst = line;
 +        unsigned char c;
 +
 +        for (i = 0; i < len; i++)
 +                if (!isspace((c = line[i])))
 +                        *dst++ = c;
 +
 +        return dst - line;
 +}
 +
 +static void patch_id_consume(void *priv, char *line, unsigned long len)
 +{
 +      struct patch_id_t *data = priv;
 +      int new_len;
 +
 +      /* Ignore line numbers when computing the SHA1 of the patch */
 +      if (!strncmp(line, "@@ -", 4))
 +              return;
 +
 +      new_len = remove_space(line, len);
 +
 +      SHA1_Update(data->ctx, line, new_len);
 +      data->patchlen += new_len;
 +}
 +
 +/* returns 0 upon success, and writes result into sha1 */
 +static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1)
 +{
 +      struct diff_queue_struct *q = &diff_queued_diff;
 +      int i;
 +      SHA_CTX ctx;
 +      struct patch_id_t data;
 +      char buffer[PATH_MAX * 4 + 20];
 +
 +      SHA1_Init(&ctx);
 +      memset(&data, 0, sizeof(struct patch_id_t));
 +      data.ctx = &ctx;
 +      data.xm.consume = patch_id_consume;
 +
 +      for (i = 0; i < q->nr; i++) {
 +              xpparam_t xpp;
 +              xdemitconf_t xecfg;
 +              xdemitcb_t ecb;
 +              mmfile_t mf1, mf2;
 +              struct diff_filepair *p = q->queue[i];
 +              int len1, len2;
 +
 +              if (p->status == 0)
 +                      return error("internal diff status error");
 +              if (p->status == DIFF_STATUS_UNKNOWN)
 +                      continue;
 +              if (diff_unmodified_pair(p))
 +                      continue;
 +              if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||
 +                  (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))
 +                      continue;
 +              if (DIFF_PAIR_UNMERGED(p))
 +                      continue;
 +
 +              diff_fill_sha1_info(p->one);
 +              diff_fill_sha1_info(p->two);
 +              if (fill_mmfile(&mf1, p->one) < 0 ||
 +                              fill_mmfile(&mf2, p->two) < 0)
 +                      return error("unable to read files to diff");
 +
 +              /* Maybe hash p->two? into the patch id? */
 +              if (mmfile_is_binary(&mf2))
 +                      continue;
 +
 +              len1 = remove_space(p->one->path, strlen(p->one->path));
 +              len2 = remove_space(p->two->path, strlen(p->two->path));
 +              if (p->one->mode == 0)
 +                      len1 = snprintf(buffer, sizeof(buffer),
 +                                      "diff--gita/%.*sb/%.*s"
 +                                      "newfilemode%06o"
 +                                      "---/dev/null"
 +                                      "+++b/%.*s",
 +                                      len1, p->one->path,
 +                                      len2, p->two->path,
 +                                      p->two->mode,
 +                                      len2, p->two->path);
 +              else if (p->two->mode == 0)
 +                      len1 = snprintf(buffer, sizeof(buffer),
 +                                      "diff--gita/%.*sb/%.*s"
 +                                      "deletedfilemode%06o"
 +                                      "---a/%.*s"
 +                                      "+++/dev/null",
 +                                      len1, p->one->path,
 +                                      len2, p->two->path,
 +                                      p->one->mode,
 +                                      len1, p->one->path);
 +              else
 +                      len1 = snprintf(buffer, sizeof(buffer),
 +                                      "diff--gita/%.*sb/%.*s"
 +                                      "---a/%.*s"
 +                                      "+++b/%.*s",
 +                                      len1, p->one->path,
 +                                      len2, p->two->path,
 +                                      len1, p->one->path,
 +                                      len2, p->two->path);
 +              SHA1_Update(&ctx, buffer, len1);
 +
 +              xpp.flags = XDF_NEED_MINIMAL;
 +              xecfg.ctxlen = 3;
 +              xecfg.flags = XDL_EMIT_FUNCNAMES;
 +              ecb.outf = xdiff_outf;
 +              ecb.priv = &data;
 +              xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
 +      }
 +
 +      SHA1_Final(sha1, &ctx);
 +      return 0;
 +}
 +
 +int diff_flush_patch_id(struct diff_options *options, unsigned char *sha1)
 +{
 +      struct diff_queue_struct *q = &diff_queued_diff;
 +      int i;
 +      int result = diff_get_patch_id(options, sha1);
 +
 +      for (i = 0; i < q->nr; i++)
 +              diff_free_filepair(q->queue[i]);
 +
 +      free(q->queue);
 +      q->queue = NULL;
 +      q->nr = q->alloc = 0;
 +
 +      return result;
 +}
 +
  static int is_summary_empty(const struct diff_queue_struct *q)
  {
        int i;