Merge branch 'master' into js/diff-ni
authorJunio C Hamano <junkio@cox.net>
Mon, 26 Feb 2007 09:20:42 +0000 (01:20 -0800)
committerJunio C Hamano <junkio@cox.net>
Mon, 26 Feb 2007 09:20:42 +0000 (01:20 -0800)
* master: (201 commits)
Documentation: link in 1.5.0.2 material to the top documentation page.
Documentation: document remote.<name>.tagopt
GIT 1.5.0.2
git-remote: support remotes with a dot in the name
Documentation: describe "-f/-t/-m" options to "git-remote add"
diff --cc: fix display of symlink conflicts during a merge.
merge-recursive: fix longstanding bug in merging symlinks
merge-index: fix longstanding bug in merging symlinks
diff --cached: give more sensible error message when HEAD is yet to be created.
Update tests to use test-chmtime
Add test-chmtime: a utility to change mtime on files
Add Release Notes to prepare for 1.5.0.2
Allow arbitrary number of arguments to git-pack-objects
rerere: do not deal with symlinks.
rerere: do not skip two conflicted paths next to each other.
Don't modify CREDITS-FILE if it hasn't changed.
diff-patch: Avoid emitting double-slashes in textual patch.
Reword git-am 3-way fallback failure message.
Limit filename for format-patch
core.legacyheaders: Use the description used in RelNotes-1.5.0
...

1  2 
builtin-diff.c
diff-lib.c
diff.c
git-commit.sh
git.c
wt-status.c
diff --combined builtin-diff.c
index 45faa2067a7f15286216c0323de0bf0ed5ef0f4c,67f49329bf6bad1a5bf2592b1792b65421278708..28b660a780f9f2ae48acf5e8d3dc7c7aee178de2
@@@ -25,6 -25,40 +25,6 @@@ struct blobinfo 
  static const char builtin_diff_usage[] =
  "git-diff <options> <rev>{0,2} -- <path>*";
  
 -static int builtin_diff_files(struct rev_info *revs,
 -                            int argc, const char **argv)
 -{
 -      int silent = 0;
 -      while (1 < argc) {
 -              const char *arg = argv[1];
 -              if (!strcmp(arg, "--base"))
 -                      revs->max_count = 1;
 -              else if (!strcmp(arg, "--ours"))
 -                      revs->max_count = 2;
 -              else if (!strcmp(arg, "--theirs"))
 -                      revs->max_count = 3;
 -              else if (!strcmp(arg, "-q"))
 -                      silent = 1;
 -              else
 -                      usage(builtin_diff_usage);
 -              argv++; argc--;
 -      }
 -      /*
 -       * Make sure there are NO revision (i.e. pending object) parameter,
 -       * specified rev.max_count is reasonable (0 <= n <= 3), and
 -       * there is no other revision filtering parameter.
 -       */
 -      if (revs->pending.nr ||
 -          revs->min_age != -1 ||
 -          revs->max_age != -1 ||
 -          3 < revs->max_count)
 -              usage(builtin_diff_usage);
 -      if (revs->max_count < 0 &&
 -          (revs->diffopt.output_format & DIFF_FORMAT_PATCH))
 -              revs->combine_merges = revs->dense_combined_merges = 1;
 -      return run_diff_files(revs, silent);
 -}
 -
  static void stuff_change(struct diff_options *opt,
                         unsigned old_mode, unsigned new_mode,
                         const unsigned char *old_sha1,
@@@ -117,10 -151,6 +117,10 @@@ static int builtin_diff_index(struct re
            revs->max_count != -1 || revs->min_age != -1 ||
            revs->max_age != -1)
                usage(builtin_diff_usage);
 +      if (read_cache() < 0) {
 +              perror("read_cache");
 +              return -1;
 +      }
        return run_diff_index(revs, cached);
  }
  
@@@ -162,7 -192,8 +162,8 @@@ static int builtin_diff_combined(struc
        parent = xmalloc(ents * sizeof(*parent));
        /* Again, the revs are all reverse */
        for (i = 0; i < ents; i++)
-               hashcpy((unsigned char*)parent + i, ent[ents - 1 - i].item->sha1);
+               hashcpy((unsigned char *)(parent + i),
+                       ent[ents - 1 - i].item->sha1);
        diff_tree_combined(parent[0], parent + 1, ents - 1,
                           revs->dense_combined_merges, revs);
        return 0;
@@@ -188,7 -219,6 +189,7 @@@ int cmd_diff(int argc, const char **arg
        int ents = 0, blobs = 0, paths = 0;
        const char *path = NULL;
        struct blobinfo blob[2];
 +      int nongit = 0;
  
        /*
         * We could get N tree-ish in the rev.pending_objects list.
         * Other cases are errors.
         */
  
 +      prefix = setup_git_directory_gently(&nongit);
        git_config(git_diff_ui_config);
        init_revisions(&rev, prefix);
  
                                break;
                        else if (!strcmp(arg, "--cached")) {
                                add_head(&rev);
+                               if (!rev.pending.nr)
+                                       die("No HEAD commit to compare with (yet)");
                                break;
                        }
                }
        if (!ents) {
                switch (blobs) {
                case 0:
 -                      return builtin_diff_files(&rev, argc, argv);
 +                      return run_diff_files_cmd(&rev, argc, argv);
                        break;
                case 1:
                        if (paths != 1)
diff --combined diff-lib.c
index 17b9a56fa2d01433430f8245218b98bce0545858,60c0fa6488eda7ae382500b70041aac4202a59c0..ae8364b42a0c67e2461fc20e731417b3203c472f
  #include "diffcore.h"
  #include "revision.h"
  #include "cache-tree.h"
 +#include "path-list.h"
  
  /*
   * diff-files
   */
  
 +static int read_directory(const char *path, struct path_list *list)
 +{
 +      DIR *dir;
 +      struct dirent *e;
 +
 +      if (!(dir = opendir(path)))
 +              return error("Could not open directory %s", path);
 +
 +      while ((e = readdir(dir)))
 +              if (strcmp(".", e->d_name) && strcmp("..", e->d_name))
 +                      path_list_insert(xstrdup(e->d_name), list);
 +
 +      closedir(dir);
 +      return 0;
 +}
 +
 +static int queue_diff(struct diff_options *o,
 +              const char *name1, const char *name2)
 +{
 +      struct stat st;
 +      int mode1 = 0, mode2 = 0;
 +
 +      if (name1) {
 +              if (stat(name1, &st))
 +                      return error("Could not access '%s'", name1);
 +              mode1 = st.st_mode;
 +      }
 +      if (name2) {
 +              if (stat(name2, &st))
 +                      return error("Could not access '%s'", name2);
 +              mode2 = st.st_mode;
 +      }
 +
 +      if (mode1 && mode2 && S_ISDIR(mode1) != S_ISDIR(mode2))
 +              return error("file/directory conflict: %s, %s", name1, name2);
 +
 +      if (S_ISDIR(mode1) || S_ISDIR(mode2)) {
 +              char buffer1[PATH_MAX], buffer2[PATH_MAX];
 +              struct path_list p1 = {NULL, 0, 0, 1}, p2 = {NULL, 0, 0, 1};
 +              int len1 = 0, len2 = 0, i1, i2, ret = 0;
 +
 +              if (name1 && read_directory(name1, &p1))
 +                      return -1;
 +              if (name2 && read_directory(name2, &p2)) {
 +                      path_list_clear(&p1, 0);
 +                      return -1;
 +              }
 +
 +              if (name1) {
 +                      len1 = strlen(name1);
 +                      if (len1 > 0 && name1[len1 - 1] == '/')
 +                              len1--;
 +                      memcpy(buffer1, name1, len1);
 +                      buffer1[len1++] = '/';
 +              }
 +
 +              if (name2) {
 +                      len2 = strlen(name2);
 +                      if (len2 > 0 && name2[len2 - 1] == '/')
 +                              len2--;
 +                      memcpy(buffer2, name2, len2);
 +                      buffer2[len2++] = '/';
 +              }
 +
 +              for (i1 = i2 = 0; !ret && (i1 < p1.nr || i2 < p2.nr); ) {
 +                      const char *n1, *n2;
 +                      int comp;
 +
 +                      if (i1 == p1.nr)
 +                              comp = 1;
 +                      else if (i2 == p2.nr)
 +                              comp = -1;
 +                      else
 +                              comp = strcmp(p1.items[i1].path,
 +                                      p2.items[i2].path);
 +
 +                      if (comp > 0)
 +                              n1 = NULL;
 +                      else {
 +                              n1 = buffer1;
 +                              strncpy(buffer1 + len1, p1.items[i1++].path,
 +                                              PATH_MAX - len1);
 +                      }
 +
 +                      if (comp < 0)
 +                              n2 = NULL;
 +                      else {
 +                              n2 = buffer2;
 +                              strncpy(buffer2 + len2, p2.items[i2++].path,
 +                                              PATH_MAX - len2);
 +                      }
 +
 +                      ret = queue_diff(o, n1, n2);
 +              }
 +              path_list_clear(&p1, 0);
 +              path_list_clear(&p2, 0);
 +
 +              return ret;
 +      } else {
 +              struct diff_filespec *d1, *d2;
 +
 +              if (o->reverse_diff) {
 +                      unsigned tmp;
 +                      const char *tmp_c;
 +                      tmp = mode1; mode1 = mode2; mode2 = tmp;
 +                      tmp_c = name1; name1 = name2; name2 = tmp_c;
 +              }
 +
 +              if (!name1)
 +                      name1 = "/dev/null";
 +              if (!name2)
 +                      name2 = "/dev/null";
 +              d1 = alloc_filespec(name1);
 +              d2 = alloc_filespec(name2);
 +              fill_filespec(d1, null_sha1, mode1);
 +              fill_filespec(d2, null_sha1, mode2);
 +
 +              diff_queue(&diff_queued_diff, d1, d2);
 +              return 0;
 +      }
 +}
 +
 +static int is_in_index(const char *path)
 +{
 +      int len = strlen(path);
 +      int pos = cache_name_pos(path, len);
 +      char c;
 +
 +      if (pos < 0)
 +              return 0;
 +      if (strncmp(active_cache[pos]->name, path, len))
 +              return 0;
 +      c = active_cache[pos]->name[len];
 +      return c == '\0' || c == '/';
 +}
 +
 +static int handle_diff_files_args(struct rev_info *revs,
 +              int argc, const char **argv, int *silent)
 +{
 +      *silent = 0;
 +
 +      /* revs->max_count == -2 means --no-index */
 +      while (1 < argc && argv[1][0] == '-') {
 +              if (!strcmp(argv[1], "--base"))
 +                      revs->max_count = 1;
 +              else if (!strcmp(argv[1], "--ours"))
 +                      revs->max_count = 2;
 +              else if (!strcmp(argv[1], "--theirs"))
 +                      revs->max_count = 3;
 +              else if (!strcmp(argv[1], "-n") ||
 +                              !strcmp(argv[1], "--no-index"))
 +                      revs->max_count = -2;
 +              else if (!strcmp(argv[1], "-q"))
 +                      *silent = 1;
 +              else
 +                      return error("invalid option: %s", argv[1]);
 +              argv++; argc--;
 +      }
 +
 +      if (revs->max_count == -1 && revs->diffopt.nr_paths == 2) {
 +              /*
 +               * If two files are specified, and at least one is untracked,
 +               * default to no-index.
 +               */
 +              read_cache();
 +              if (!is_in_index(revs->diffopt.paths[0]) ||
 +                                      !is_in_index(revs->diffopt.paths[1]))
 +                      revs->max_count = -2;
 +      }
 +
 +      /*
 +       * Make sure there are NO revision (i.e. pending object) parameter,
 +       * rev.max_count is reasonable (0 <= n <= 3),
 +       * there is no other revision filtering parameters.
 +       */
 +      if (revs->pending.nr || revs->max_count > 3 ||
 +          revs->min_age != -1 || revs->max_age != -1)
 +              return error("no revision allowed with diff-files");
 +
 +      if (revs->max_count == -1 &&
 +          (revs->diffopt.output_format & DIFF_FORMAT_PATCH))
 +              revs->combine_merges = revs->dense_combined_merges = 1;
 +
 +      return 0;
 +}
 +
 +int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv)
 +{
 +      int silent_on_removed;
 +
 +      if (handle_diff_files_args(revs, argc, argv, &silent_on_removed))
 +              return -1;
 +
 +      if (revs->max_count == -2) {
 +              if (revs->diffopt.nr_paths != 2)
 +                      return error("need two files/directories with --no-index");
 +              queue_diff(&revs->diffopt, revs->diffopt.paths[0],
 +                              revs->diffopt.paths[1]);
 +              diffcore_std(&revs->diffopt);
 +              diff_flush(&revs->diffopt);
 +              return 0;
 +      }
 +
 +      if (read_cache() < 0) {
 +              perror("read_cache");
 +              return -1;
 +      }
 +      return run_diff_files(revs, silent_on_removed);
 +}
 +
  int run_diff_files(struct rev_info *revs, int silent_on_removed)
  {
        int entries, i;
  
        if (diff_unmerged_stage < 0)
                diff_unmerged_stage = 2;
 -      entries = read_cache();
 -      if (entries < 0) {
 -              perror("read_cache");
 -              return -1;
 -      }
 +      entries = active_nr;
        for (i = 0; i < entries; i++) {
                struct stat st;
                unsigned int oldmode, newmode;
  
                        path_len = ce_namelen(ce);
  
-                       dpath = xmalloc (combine_diff_path_size (5, path_len));
+                       dpath = xmalloc(combine_diff_path_size(5, path_len));
                        dpath->path = (char *) &(dpath->parent[5]);
  
                        dpath->next = NULL;
                        dpath->len = path_len;
                        memcpy(dpath->path, ce->name, path_len);
                        dpath->path[path_len] = '\0';
-                       dpath->mode = 0;
                        hashclr(dpath->sha1);
                        memset(&(dpath->parent[0]), 0,
-                                       sizeof(struct combine_diff_parent)*5);
+                              sizeof(struct combine_diff_parent)*5);
+                       if (lstat(ce->name, &st) < 0) {
+                               if (errno != ENOENT && errno != ENOTDIR) {
+                                       perror(ce->name);
+                                       continue;
+                               }
+                               if (silent_on_removed)
+                                       continue;
+                       }
+                       else
+                               dpath->mode = canon_mode(st.st_mode);
  
                        while (i < entries) {
                                struct cache_entry *nce = active_cache[i];
@@@ -559,6 -362,10 +569,6 @@@ int run_diff_index(struct rev_info *rev
        if (!revs->ignore_merges)
                match_missing = 1;
  
 -      if (read_cache() < 0) {
 -              perror("read_cache");
 -              return -1;
 -      }
        mark_merge_entries();
  
        ent = revs->pending.objects[0].item;
diff --combined diff.c
index 701880abadd4bd93b492d2cd73e164d8a95c20ac,d1eae7214d863d5a87735059d509afcdf60507d7..6bd456ed42e76a41c62908e5bb0d7df2b329b032
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -77,7 -77,7 +77,7 @@@ int git_diff_ui_config(const char *var
                        diff_detect_rename_default = DIFF_DETECT_RENAME;
                return 0;
        }
-       if (!strncmp(var, "diff.color.", 11) || !strncmp(var, "color.diff.", 11)) {
+       if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) {
                int slot = parse_diff_color_slot(var, 11);
                color_parse(value, var, diff_colors[slot]);
                return 0;
@@@ -184,31 -184,43 +184,43 @@@ static void print_line_count(int count
        }
  }
  
- static void copy_file(int prefix, const char *data, int size)
+ static void copy_file(int prefix, const char *data, int size,
+               const char *set, const char *reset)
  {
        int ch, nl_just_seen = 1;
        while (0 < size--) {
                ch = *data++;
-               if (nl_just_seen)
+               if (nl_just_seen) {
+                       fputs(set, stdout);
                        putchar(prefix);
-               putchar(ch);
-               if (ch == '\n')
+               }
+               if (ch == '\n') {
                        nl_just_seen = 1;
-               else
+                       fputs(reset, stdout);
+               } else
                        nl_just_seen = 0;
+               putchar(ch);
        }
        if (!nl_just_seen)
-               printf("\n\\ No newline at end of file\n");
+               printf("%s\n\\ No newline at end of file\n", reset);
  }
  
  static void emit_rewrite_diff(const char *name_a,
                              const char *name_b,
                              struct diff_filespec *one,
-                             struct diff_filespec *two)
+                             struct diff_filespec *two,
+                             int color_diff)
  {
        int lc_a, lc_b;
        const char *name_a_tab, *name_b_tab;
+       const char *metainfo = diff_get_color(color_diff, DIFF_METAINFO);
+       const char *fraginfo = diff_get_color(color_diff, DIFF_FRAGINFO);
+       const char *old = diff_get_color(color_diff, DIFF_FILE_OLD);
+       const char *new = diff_get_color(color_diff, DIFF_FILE_NEW);
+       const char *reset = diff_get_color(color_diff, DIFF_RESET);
+       name_a += (*name_a == '/');
+       name_b += (*name_b == '/');
        name_a_tab = strchr(name_a, ' ') ? "\t" : "";
        name_b_tab = strchr(name_b, ' ') ? "\t" : "";
  
        diff_populate_filespec(two, 0);
        lc_a = count_lines(one->data, one->size);
        lc_b = count_lines(two->data, two->size);
-       printf("--- a/%s%s\n+++ b/%s%s\n@@ -",
-              name_a, name_a_tab,
-              name_b, name_b_tab);
+       printf("%s--- a/%s%s%s\n%s+++ b/%s%s%s\n%s@@ -",
+              metainfo, name_a, name_a_tab, reset,
+              metainfo, name_b, name_b_tab, reset, fraginfo);
        print_line_count(lc_a);
        printf(" +");
        print_line_count(lc_b);
-       printf(" @@\n");
+       printf(" @@%s\n", reset);
        if (lc_a)
-               copy_file('-', one->data, one->size);
+               copy_file('-', one->data, one->size, old, reset);
        if (lc_b)
-               copy_file('+', two->data, two->size);
+               copy_file('+', two->data, two->size, new, reset);
  }
  
  static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
@@@ -405,22 -417,16 +417,16 @@@ static void emit_line(const char *set, 
        puts(reset);
  }
  
- static void emit_add_line(const char *reset, struct emit_callback *ecbdata, const char *line, int len)
+ static void emit_line_with_ws(int nparents,
+               const char *set, const char *reset, const char *ws,
+               const char *line, int len)
  {
-       int col0 = ecbdata->nparents;
+       int col0 = nparents;
        int last_tab_in_indent = -1;
        int last_space_in_indent = -1;
        int i;
        int tail = len;
        int need_highlight_leading_space = 0;
-       const char *ws = diff_get_color(ecbdata->color_diff, DIFF_WHITESPACE);
-       const char *set = diff_get_color(ecbdata->color_diff, DIFF_FILE_NEW);
-       if (!*ws) {
-               emit_line(set, reset, line, len);
-               return;
-       }
        /* The line is a newly added line.  Does it have funny leading
         * whitespaces?  In indent, SP should never precede a TAB.
         */
                emit_line(set, reset, line + i, len - i);
  }
  
+ static void emit_add_line(const char *reset, struct emit_callback *ecbdata, const char *line, int len)
+ {
+       const char *ws = diff_get_color(ecbdata->color_diff, DIFF_WHITESPACE);
+       const char *set = diff_get_color(ecbdata->color_diff, DIFF_FILE_NEW);
+       if (!*ws)
+               emit_line(set, reset, line, len);
+       else
+               emit_line_with_ws(ecbdata->nparents, set, reset, ws,
+                               line, len);
+ }
  static void fn_out_consume(void *priv, char *line, unsigned long len)
  {
        int i;
@@@ -884,30 -902,44 +902,44 @@@ static void show_numstat(struct diffsta
  struct checkdiff_t {
        struct xdiff_emit_state xm;
        const char *filename;
-       int lineno;
+       int lineno, color_diff;
  };
  
  static void checkdiff_consume(void *priv, char *line, unsigned long len)
  {
        struct checkdiff_t *data = priv;
+       const char *ws = diff_get_color(data->color_diff, DIFF_WHITESPACE);
+       const char *reset = diff_get_color(data->color_diff, DIFF_RESET);
+       const char *set = diff_get_color(data->color_diff, DIFF_FILE_NEW);
  
        if (line[0] == '+') {
-               int i, spaces = 0;
+               int i, spaces = 0, space_before_tab = 0, white_space_at_end = 0;
  
                /* check space before tab */
                for (i = 1; i < len && (line[i] == ' ' || line[i] == '\t'); i++)
                        if (line[i] == ' ')
                                spaces++;
                if (line[i - 1] == '\t' && spaces)
-                       printf("%s:%d: space before tab:%.*s\n",
-                               data->filename, data->lineno, (int)len, line);
+                       space_before_tab = 1;
  
                /* check white space at line end */
                if (line[len - 1] == '\n')
                        len--;
                if (isspace(line[len - 1]))
-                       printf("%s:%d: white space at end: %.*s\n",
-                               data->filename, data->lineno, (int)len, line);
+                       white_space_at_end = 1;
+               if (space_before_tab || white_space_at_end) {
+                       printf("%s:%d: %s", data->filename, data->lineno, ws);
+                       if (space_before_tab) {
+                               printf("space before tab");
+                               if (white_space_at_end)
+                                       putchar(',');
+                       }
+                       if (white_space_at_end)
+                               printf("white space at end");
+                       printf(":%s ", reset);
+                       emit_line_with_ws(1, set, reset, ws, line, len);
+               }
  
                data->lineno++;
        } else if (line[0] == ' ')
@@@ -1034,8 -1066,8 +1066,8 @@@ static void builtin_diff(const char *na
        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);
+       a_one = quote_two("a/", name_a + (*name_a == '/'));
+       b_two = quote_two("b/", name_b + (*name_b == '/'));
        lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null";
        lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null";
        printf("%sdiff --git %s %s%s\n", set, a_one, b_two, reset);
                if ((one->mode ^ two->mode) & S_IFMT)
                        goto free_ab_and_return;
                if (complete_rewrite) {
-                       emit_rewrite_diff(name_a, name_b, one, two);
+                       emit_rewrite_diff(name_a, name_b, one, two,
+                                       o->color_diff);
                        goto free_ab_and_return;
                }
        }
                xecfg.flags = XDL_EMIT_FUNCNAMES;
                if (!diffopts)
                        ;
-               else if (!strncmp(diffopts, "--unified=", 10))
+               else if (!prefixcmp(diffopts, "--unified="))
                        xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10);
-               else if (!strncmp(diffopts, "-u", 2))
+               else if (!prefixcmp(diffopts, "-u"))
                        xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10);
                ecb.outf = xdiff_outf;
                ecb.priv = &ecbdata;
@@@ -1165,7 -1198,7 +1198,7 @@@ static void builtin_diffstat(const cha
  
  static void builtin_checkdiff(const char *name_a, const char *name_b,
                             struct diff_filespec *one,
-                            struct diff_filespec *two)
+                            struct diff_filespec *two, struct diff_options *o)
  {
        mmfile_t mf1, mf2;
        struct checkdiff_t data;
        data.xm.consume = checkdiff_consume;
        data.filename = name_b ? name_b : name_a;
        data.lineno = 0;
+       data.color_diff = o->color_diff;
  
        if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
                die("unable to read files to diff");
@@@ -1346,6 -1380,9 +1380,9 @@@ int diff_populate_filespec(struct diff_
            reuse_worktree_file(s->path, s->sha1, 0)) {
                struct stat st;
                int fd;
+               char *buf;
+               unsigned long size;
                if (lstat(s->path, &st) < 0) {
                        if (errno == ENOENT) {
                        err_empty:
                s->data = xmmap(NULL, s->size, PROT_READ, MAP_PRIVATE, fd, 0);
                close(fd);
                s->should_munmap = 1;
-               /* FIXME! CRLF -> LF conversion goes here, based on "s->path" */
+               /*
+                * Convert from working tree format to canonical git format
+                */
+               buf = s->data;
+               size = s->size;
+               if (convert_to_git(s->path, &buf, &size)) {
+                       munmap(s->data, s->size);
+                       s->should_munmap = 0;
+                       s->data = buf;
+                       s->size = size;
+                       s->should_free = 1;
+               }
        }
        else {
                char type[20];
@@@ -1787,7 -1836,7 +1836,7 @@@ static void run_checkdiff(struct diff_f
        diff_fill_sha1_info(p->one);
        diff_fill_sha1_info(p->two);
  
-       builtin_checkdiff(name, other, p->one, p->two);
+       builtin_checkdiff(name, other, p->one, p->two, o);
  }
  
  void diff_setup(struct diff_options *options)
@@@ -1936,7 -1985,7 +1985,7 @@@ int diff_opt_parse(struct diff_options 
        else if (!strcmp(arg, "--shortstat")) {
                options->output_format |= DIFF_FORMAT_SHORTSTAT;
        }
-       else if (!strncmp(arg, "--stat", 6)) {
+       else if (!prefixcmp(arg, "--stat")) {
                char *end;
                int width = options->stat_width;
                int name_width = options->stat_name_width;
  
                switch (*arg) {
                case '-':
-                       if (!strncmp(arg, "-width=", 7))
+                       if (!prefixcmp(arg, "-width="))
                                width = strtoul(arg + 7, &end, 10);
-                       else if (!strncmp(arg, "-name-width=", 12))
+                       else if (!prefixcmp(arg, "-name-width="))
                                name_width = strtoul(arg + 12, &end, 10);
                        break;
                case '=':
        }
        else if (!strcmp(arg, "-z"))
                options->line_termination = 0;
-       else if (!strncmp(arg, "-l", 2))
+       else if (!prefixcmp(arg, "-l"))
                options->rename_limit = strtoul(arg+2, NULL, 10);
        else if (!strcmp(arg, "--full-index"))
                options->full_index = 1;
                options->output_format |= DIFF_FORMAT_NAME_STATUS;
        else if (!strcmp(arg, "-R"))
                options->reverse_diff = 1;
-       else if (!strncmp(arg, "-S", 2))
+       else if (!prefixcmp(arg, "-S"))
                options->pickaxe = arg + 2;
        else if (!strcmp(arg, "-s")) {
                options->output_format |= DIFF_FORMAT_NO_OUTPUT;
        }
-       else if (!strncmp(arg, "-O", 2))
+       else if (!prefixcmp(arg, "-O"))
                options->orderfile = arg + 2;
-       else if (!strncmp(arg, "--diff-filter=", 14))
+       else if (!prefixcmp(arg, "--diff-filter="))
                options->filter = arg + 14;
        else if (!strcmp(arg, "--pickaxe-all"))
                options->pickaxe_opts = DIFF_PICKAXE_ALL;
        else if (!strcmp(arg, "--pickaxe-regex"))
                options->pickaxe_opts = DIFF_PICKAXE_REGEX;
-       else if (!strncmp(arg, "-B", 2)) {
+       else if (!prefixcmp(arg, "-B")) {
                if ((options->break_opt =
                     diff_scoreopt_parse(arg)) == -1)
                        return -1;
        }
-       else if (!strncmp(arg, "-M", 2)) {
+       else if (!prefixcmp(arg, "-M")) {
                if ((options->rename_score =
                     diff_scoreopt_parse(arg)) == -1)
                        return -1;
                options->detect_rename = DIFF_DETECT_RENAME;
        }
-       else if (!strncmp(arg, "-C", 2)) {
+       else if (!prefixcmp(arg, "-C")) {
                if ((options->rename_score =
                     diff_scoreopt_parse(arg)) == -1)
                        return -1;
                options->find_copies_harder = 1;
        else if (!strcmp(arg, "--abbrev"))
                options->abbrev = DEFAULT_ABBREV;
-       else if (!strncmp(arg, "--abbrev=", 9)) {
+       else if (!prefixcmp(arg, "--abbrev=")) {
                options->abbrev = strtoul(arg + 9, NULL, 10);
                if (options->abbrev < MINIMUM_ABBREV)
                        options->abbrev = MINIMUM_ABBREV;
@@@ -2406,8 -2455,7 +2455,8 @@@ static void diff_resolve_rename_copy(vo
                                p->status = DIFF_STATUS_RENAMED;
                }
                else if (hashcmp(p->one->sha1, p->two->sha1) ||
 -                       p->one->mode != p->two->mode)
 +                       p->one->mode != p->two->mode ||
 +                       is_null_sha1(p->one->sha1))
                        p->status = DIFF_STATUS_MODIFIED;
                else {
                        /* This is a "no-change" entry and should not
@@@ -2533,7 -2581,7 +2582,7 @@@ static void patch_id_consume(void *priv
        int new_len;
  
        /* Ignore line numbers when computing the SHA1 of the patch */
-       if (!strncmp(line, "@@ -", 4))
+       if (!prefixcmp(line, "@@ -"))
                return;
  
        new_len = remove_space(line, len);
diff --combined git-commit.sh
index cfa15110f6d7b11c7f778a3ef7ebbc034ce4d877,476f4f18dbb76041fbbfe6243785ad4997a7e7c7..be3677c2049dca4deb35403804d5ea2d72254e7d
@@@ -13,10 -13,10 +13,10 @@@ git-rev-parse --verify HEAD >/dev/null 
  case "$0" in
  *status)
        status_only=t
 -      unmerged_ok_if_status=--unmerged ;;
 +      ;;
  *commit)
        status_only=
 -      unmerged_ok_if_status= ;;
 +      ;;
  esac
  
  refuse_partial () {
@@@ -318,6 -318,10 +318,10 @@@ esa
  
  case "$all,$also" in
  t,)
+       if test ! -f "$THIS_INDEX"
+       then
+               die 'nothing to commit (use "git add file1 file2" to include for commit)'
+       fi
        save_index &&
        (
                cd_to_toplevel &&
@@@ -389,17 -393,16 +393,17 @@@ els
        USE_INDEX="$THIS_INDEX"
  fi
  
 -GIT_INDEX_FILE="$USE_INDEX" \
 -      git-update-index -q $unmerged_ok_if_status --refresh || exit
 -
 -################################################################
 -# If the request is status, just show it and exit.
 -
 -case "$0" in
 -*status)
 +case "$status_only" in
 +t)
 +      # This will silently fail in a read-only repository, which is
 +      # what we want.
 +      GIT_INDEX_FILE="$USE_INDEX" git-update-index -q --unmerged --refresh
        run_status
        exit $?
 +      ;;
 +'')
 +      GIT_INDEX_FILE="$USE_INDEX" git-update-index -q --refresh || exit
 +      ;;
  esac
  
  ################################################################
diff --combined git.c
index 79fc73ca46b298533e2ebf8b7314ab33cf3b3cbf,83f3d90ee35418976baea4f032c13aed0ec9e664..fa3fd11386eac14d0aed918e88d5dc449ef1b37f
--- 1/git.c
--- 2/git.c
+++ b/git.c
@@@ -48,7 -48,7 +48,7 @@@ static int handle_options(const char**
                /*
                 * Check remaining flags.
                 */
-               if (!strncmp(cmd, "--exec-path", 11)) {
+               if (!prefixcmp(cmd, "--exec-path")) {
                        cmd += 11;
                        if (*cmd == '=')
                                git_set_exec_path(cmd + 1);
@@@ -66,7 -66,7 +66,7 @@@
                        setenv(GIT_DIR_ENVIRONMENT, (*argv)[1], 1);
                        (*argv)++;
                        (*argc)--;
-               } else if (!strncmp(cmd, "--git-dir=", 10)) {
+               } else if (!prefixcmp(cmd, "--git-dir=")) {
                        setenv(GIT_DIR_ENVIRONMENT, cmd + 10, 1);
                } else if (!strcmp(cmd, "--bare")) {
                        static char git_dir[PATH_MAX+1];
@@@ -88,7 -88,7 +88,7 @@@ static char *alias_string
  
  static int git_alias_config(const char *var, const char *value)
  {
-       if (!strncmp(var, "alias.", 6) && !strcmp(var + 6, alias_command)) {
+       if (!prefixcmp(var, "alias.") && !strcmp(var + 6, alias_command)) {
                alias_string = xstrdup(value);
        }
        return 0;
@@@ -237,8 -237,8 +237,8 @@@ static void handle_internal_command(in
                { "config", cmd_config },
                { "count-objects", cmd_count_objects, RUN_SETUP },
                { "describe", cmd_describe, RUN_SETUP },
 -              { "diff", cmd_diff, RUN_SETUP | USE_PAGER },
 -              { "diff-files", cmd_diff_files, RUN_SETUP },
 +              { "diff", cmd_diff, USE_PAGER },
 +              { "diff-files", cmd_diff_files },
                { "diff-index", cmd_diff_index, RUN_SETUP },
                { "diff-tree", cmd_diff_tree, RUN_SETUP },
                { "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP },
                { "fsck", cmd_fsck, RUN_SETUP },
                { "fsck-objects", cmd_fsck, RUN_SETUP },
                { "get-tar-commit-id", cmd_get_tar_commit_id },
-               { "grep", cmd_grep, RUN_SETUP },
+               { "grep", cmd_grep, RUN_SETUP | USE_PAGER },
                { "help", cmd_help },
                { "init", cmd_init_db },
                { "init-db", cmd_init_db },
@@@ -348,7 -348,7 +348,7 @@@ int main(int argc, const char **argv, c
         * So we just directly call the internal command handler, and
         * die if that one cannot handle it.
         */
-       if (!strncmp(cmd, "git-", 4)) {
+       if (!prefixcmp(cmd, "git-")) {
                cmd += 4;
                argv[0] = cmd;
                handle_internal_command(argc, argv, envp);
        argc--;
        handle_options(&argv, &argc);
        if (argc > 0) {
-               if (!strncmp(argv[0], "--", 2))
+               if (!prefixcmp(argv[0], "--"))
                        argv[0] += 2;
        } else {
                /* Default command: "help" */
diff --combined wt-status.c
index e3465111530b1fb1ce7645cb810c00c2339f01a4,035e546ed77248720813e7c42e0e2b25f5e760a3..a25632bc87867748016e32a4ba4652918c8705a3
@@@ -191,18 -191,12 +191,18 @@@ static void wt_status_print_changed_cb(
                wt_status_print_trailer();
  }
  
 +static void wt_read_cache(struct wt_status *s)
 +{
 +      discard_cache();
 +      read_cache();
 +}
 +
  void wt_status_print_initial(struct wt_status *s)
  {
        int i;
        char buf[PATH_MAX];
  
 -      read_cache();
 +      wt_read_cache(s);
        if (active_nr) {
                s->commitable = 1;
                wt_status_print_cached_header(NULL);
@@@ -226,7 -220,6 +226,7 @@@ static void wt_status_print_updated(str
        rev.diffopt.format_callback = wt_status_print_updated_cb;
        rev.diffopt.format_callback_data = s;
        rev.diffopt.detect_rename = 1;
 +      wt_read_cache(s);
        run_diff_index(&rev, 1);
  }
  
@@@ -238,7 -231,6 +238,7 @@@ static void wt_status_print_changed(str
        rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
        rev.diffopt.format_callback = wt_status_print_changed_cb;
        rev.diffopt.format_callback_data = s;
 +      wt_read_cache(s);
        run_diff_files(&rev, 0);
  }
  
@@@ -295,7 -287,6 +295,7 @@@ static void wt_status_print_verbose(str
        setup_revisions(0, NULL, &rev, s->reference);
        rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
        rev.diffopt.detect_rename = 1;
 +      wt_read_cache(s);
        run_diff_index(&rev, 1);
  }
  
@@@ -307,7 -298,7 +307,7 @@@ void wt_status_print(struct wt_status *
        if (s->branch) {
                const char *on_what = "On branch ";
                const char *branch_name = s->branch;
-               if (!strncmp(branch_name, "refs/heads/", 11))
+               if (!prefixcmp(branch_name, "refs/heads/"))
                        branch_name += 11;
                else if (!strcmp(branch_name, "HEAD")) {
                        branch_name = "";
        }
        else {
                wt_status_print_updated(s);
 -              discard_cache();
        }
  
        wt_status_print_changed(s);
@@@ -352,7 -344,7 +352,7 @@@ int git_status_config(const char *k, co
                wt_status_use_color = git_config_colorbool(k, v);
                return 0;
        }
-       if (!strncmp(k, "status.color.", 13) || !strncmp(k, "color.status.", 13)) {
+       if (!prefixcmp(k, "status.color.") || !prefixcmp(k, "color.status.")) {
                int slot = parse_status_slot(k, 13);
                color_parse(v, k, wt_status_colors[slot]);
        }