Merge branch 'jc/maint-add-sync-stat'
authorJunio C Hamano <gitster@pobox.com>
Wed, 14 Nov 2007 22:15:40 +0000 (14:15 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 14 Nov 2007 22:15:40 +0000 (14:15 -0800)
* jc/maint-add-sync-stat:
  t2200: test more cases of "add -u"
  git-add: make the entry stat-clean after re-adding the same contents
  ce_match_stat, run_diff_files: use symbolic constants for readability

Conflicts:

builtin-add.c

1  2 
builtin-add.c
builtin-apply.c
cache.h
entry.c
read-cache.c
unpack-trees.c

diff --combined builtin-add.c
  #include "diffcore.h"
  #include "commit.h"
  #include "revision.h"
 +#include "run-command.h"
 +#include "parse-options.h"
  
 -static const char builtin_add_usage[] =
 -"git-add [-n] [-v] [-f] [--interactive | -i] [-u] [--refresh] [--] <filepattern>...";
 +static const char * const builtin_add_usage[] = {
 +      "git-add [options] [--] <filepattern>...",
 +      NULL
 +};
  
  static int take_worktree_changes;
  static const char *excludes_file;
@@@ -76,8 -72,12 +76,8 @@@ static void fill_directory(struct dir_s
        baselen = common_prefix(pathspec);
        path = ".";
        base = "";
 -      if (baselen) {
 -              char *common = xmalloc(baselen + 1);
 -              memcpy(common, *pathspec, baselen);
 -              common[baselen] = 0;
 -              path = base = common;
 -      }
 +      if (baselen)
 +              path = base = xmemdupz(*pathspec, baselen);
  
        /* Read the directory and prune it */
        read_directory(dir, path, base, baselen, pathspec);
@@@ -104,6 -104,7 +104,6 @@@ static void update_callback(struct diff
                        break;
                case DIFF_STATUS_DELETED:
                        remove_file_from_cache(path);
 -                      cache_tree_invalidate_path(active_cache_tree, path);
                        if (verbose)
                                printf("remove '%s'\n", path);
                        break;
        }
  }
  
 -static void update(int verbose, const char *prefix, const char **files)
 +void add_files_to_cache(int verbose, const char *prefix, const char **files)
  {
        struct rev_info rev;
        init_revisions(&rev, prefix);
        rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
        rev.diffopt.format_callback = update_callback;
        rev.diffopt.format_callback_data = &verbose;
-       run_diff_files(&rev, 0);
 -      if (read_cache() < 0)
 -              die("index file corrupt");
+       run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
  }
  
  static void refresh(int verbose, const char **pathspec)
@@@ -153,63 -156,80 +153,63 @@@ static int git_add_config(const char *v
        return git_default_config(var, value);
  }
  
 +int interactive_add(void)
 +{
 +      const char *argv[2] = { "add--interactive", NULL };
 +
 +      return run_command_v_opt(argv, RUN_GIT_CMD);
 +}
 +
  static struct lock_file lock_file;
  
  static const char ignore_error[] =
  "The following paths are ignored by one of your .gitignore files:\n";
  
 +static int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0;
 +static int add_interactive = 0;
 +
 +static struct option builtin_add_options[] = {
 +      OPT__DRY_RUN(&show_only),
 +      OPT__VERBOSE(&verbose),
 +      OPT_GROUP(""),
 +      OPT_BOOLEAN('i', "interactive", &add_interactive, "interactive picking"),
 +      OPT_BOOLEAN('f', NULL, &ignored_too, "allow adding otherwise ignored files"),
 +      OPT_BOOLEAN('u', NULL, &take_worktree_changes, "update tracked files"),
 +      OPT_BOOLEAN( 0 , "refresh", &refresh_only, "don't add, only refresh the index"),
 +      OPT_END(),
 +};
 +
  int cmd_add(int argc, const char **argv, const char *prefix)
  {
 -      int i, newfd;
 -      int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0;
 +      int i, newfd, orig_argc = argc;
        const char **pathspec;
        struct dir_struct dir;
 -      int add_interactive = 0;
  
 -      for (i = 1; i < argc; i++) {
 -              if (!strcmp("--interactive", argv[i]) ||
 -                  !strcmp("-i", argv[i]))
 -                      add_interactive++;
 -      }
 +      argc = parse_options(argc, argv, builtin_add_options,
 +                        builtin_add_usage, 0);
        if (add_interactive) {
 -              const char *args[] = { "add--interactive", NULL };
 -
 -              if (add_interactive != 1 || argc != 2)
 +              if (add_interactive != 1 || orig_argc != 2)
                        die("add --interactive does not take any parameters");
 -              execv_git_cmd(args);
 -              exit(1);
 +              exit(interactive_add());
        }
  
        git_config(git_add_config);
  
        newfd = hold_locked_index(&lock_file, 1);
  
 -      for (i = 1; i < argc; i++) {
 -              const char *arg = argv[i];
 -
 -              if (arg[0] != '-')
 -                      break;
 -              if (!strcmp(arg, "--")) {
 -                      i++;
 -                      break;
 -              }
 -              if (!strcmp(arg, "-n")) {
 -                      show_only = 1;
 -                      continue;
 -              }
 -              if (!strcmp(arg, "-f")) {
 -                      ignored_too = 1;
 -                      continue;
 -              }
 -              if (!strcmp(arg, "-v")) {
 -                      verbose = 1;
 -                      continue;
 -              }
 -              if (!strcmp(arg, "-u")) {
 -                      take_worktree_changes = 1;
 -                      continue;
 -              }
 -              if (!strcmp(arg, "--refresh")) {
 -                      refresh_only = 1;
 -                      continue;
 -              }
 -              usage(builtin_add_usage);
 -      }
 -
        if (take_worktree_changes) {
 -              update(verbose, prefix, argv + i);
 +              if (read_cache() < 0)
 +                      die("index file corrupt");
 +              add_files_to_cache(verbose, prefix, argv);
                goto finish;
        }
  
 -      if (argc <= i) {
 +      if (argc == 0) {
                fprintf(stderr, "Nothing specified, nothing added.\n");
                fprintf(stderr, "Maybe you wanted to say 'git add .'?\n");
                return 0;
        }
 -      pathspec = get_pathspec(prefix, argv + i);
 +      pathspec = get_pathspec(prefix, argv);
  
        if (refresh_only) {
                refresh(verbose, pathspec);
diff --combined builtin-apply.c
@@@ -152,7 -152,7 +152,7 @@@ struct patch 
        unsigned int is_rename:1;
        struct fragment *fragments;
        char *result;
 -      unsigned long resultsize;
 +      size_t resultsize;
        char old_sha1_prefix[41];
        char new_sha1_prefix[41];
        struct patch *next;
@@@ -163,14 -163,15 +163,14 @@@ static void say_patch_name(FILE *output
        fputs(pre, output);
        if (patch->old_name && patch->new_name &&
            strcmp(patch->old_name, patch->new_name)) {
 -              write_name_quoted(NULL, 0, patch->old_name, 1, output);
 +              quote_c_style(patch->old_name, NULL, output, 0);
                fputs(" => ", output);
 -              write_name_quoted(NULL, 0, patch->new_name, 1, output);
 -      }
 -      else {
 +              quote_c_style(patch->new_name, NULL, output, 0);
 +      } else {
                const char *n = patch->new_name;
                if (!n)
                        n = patch->old_name;
 -              write_name_quoted(NULL, 0, n, 1, output);
 +              quote_c_style(n, NULL, output, 0);
        }
        fputs(post, output);
  }
  #define CHUNKSIZE (8192)
  #define SLOP (16)
  
 -static void *read_patch_file(int fd, unsigned long *sizep)
 +static void read_patch_file(struct strbuf *sb, int fd)
  {
 -      unsigned long size = 0, alloc = CHUNKSIZE;
 -      void *buffer = xmalloc(alloc);
 -
 -      for (;;) {
 -              ssize_t nr = alloc - size;
 -              if (nr < 1024) {
 -                      alloc += CHUNKSIZE;
 -                      buffer = xrealloc(buffer, alloc);
 -                      nr = alloc - size;
 -              }
 -              nr = xread(fd, (char *) buffer + size, nr);
 -              if (!nr)
 -                      break;
 -              if (nr < 0)
 -                      die("git-apply: read returned %s", strerror(errno));
 -              size += nr;
 -      }
 -      *sizep = size;
 +      if (strbuf_read(sb, fd, 0) < 0)
 +              die("git-apply: read returned %s", strerror(errno));
  
        /*
         * Make sure that we have some slop in the buffer
         * so that we can do speculative "memcmp" etc, and
         * see to it that it is NUL-filled.
         */
 -      if (alloc < size + SLOP)
 -              buffer = xrealloc(buffer, size + SLOP);
 -      memset((char *) buffer + size, 0, SLOP);
 -      return buffer;
 +      strbuf_grow(sb, SLOP);
 +      memset(sb->buf + sb->len, 0, SLOP);
  }
  
  static unsigned long linelen(const char *buffer, unsigned long size)
@@@ -225,33 -244,35 +225,33 @@@ static char *find_name(const char *line
  {
        int len;
        const char *start = line;
 -      char *name;
  
        if (*line == '"') {
 +              struct strbuf name;
 +
                /* Proposed "new-style" GNU patch/diff format; see
                 * http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2
                 */
 -              name = unquote_c_style(line, NULL);
 -              if (name) {
 -                      char *cp = name;
 -                      while (p_value) {
 +              strbuf_init(&name, 0);
 +              if (!unquote_c_style(&name, line, NULL)) {
 +                      char *cp;
 +
 +                      for (cp = name.buf; p_value; p_value--) {
                                cp = strchr(cp, '/');
                                if (!cp)
                                        break;
                                cp++;
 -                              p_value--;
                        }
                        if (cp) {
                                /* name can later be freed, so we need
                                 * to memmove, not just return cp
                                 */
 -                              memmove(name, cp, strlen(cp) + 1);
 +                              strbuf_remove(&name, 0, cp - name.buf);
                                free(def);
 -                              return name;
 -                      }
 -                      else {
 -                              free(name);
 -                              name = NULL;
 +                              return strbuf_detach(&name, NULL);
                        }
                }
 +              strbuf_release(&name);
        }
  
        for (;;) {
                int deflen = strlen(def);
                if (deflen < len && !strncmp(start, def, deflen))
                        return def;
 +              free(def);
        }
  
 -      name = xmalloc(len + 1);
 -      memcpy(name, start, len);
 -      name[len] = 0;
 -      free(def);
 -      return name;
 +      return xmemdupz(start, len);
  }
  
  static int count_slashes(const char *cp)
@@@ -559,30 -583,29 +559,30 @@@ static const char *stop_at_slash(const 
   */
  static char *git_header_name(char *line, int llen)
  {
 -      int len;
        const char *name;
        const char *second = NULL;
 +      size_t len;
  
        line += strlen("diff --git ");
        llen -= strlen("diff --git ");
  
        if (*line == '"') {
                const char *cp;
 -              char *first = unquote_c_style(line, &second);
 -              if (!first)
 -                      return NULL;
 +              struct strbuf first;
 +              struct strbuf sp;
 +
 +              strbuf_init(&first, 0);
 +              strbuf_init(&sp, 0);
 +
 +              if (unquote_c_style(&first, line, &second))
 +                      goto free_and_fail1;
  
                /* advance to the first slash */
 -              cp = stop_at_slash(first, strlen(first));
 -              if (!cp || cp == first) {
 -                      /* we do not accept absolute paths */
 -              free_first_and_fail:
 -                      free(first);
 -                      return NULL;
 -              }
 -              len = strlen(cp+1);
 -              memmove(first, cp+1, len+1); /* including NUL */
 +              cp = stop_at_slash(first.buf, first.len);
 +              /* we do not accept absolute paths */
 +              if (!cp || cp == first.buf)
 +                      goto free_and_fail1;
 +              strbuf_remove(&first, 0, cp + 1 - first.buf);
  
                /* second points at one past closing dq of name.
                 * find the second name.
                        second++;
  
                if (line + llen <= second)
 -                      goto free_first_and_fail;
 +                      goto free_and_fail1;
                if (*second == '"') {
 -                      char *sp = unquote_c_style(second, NULL);
 -                      if (!sp)
 -                              goto free_first_and_fail;
 -                      cp = stop_at_slash(sp, strlen(sp));
 -                      if (!cp || cp == sp) {
 -                      free_both_and_fail:
 -                              free(sp);
 -                              goto free_first_and_fail;
 -                      }
 +                      if (unquote_c_style(&sp, second, NULL))
 +                              goto free_and_fail1;
 +                      cp = stop_at_slash(sp.buf, sp.len);
 +                      if (!cp || cp == sp.buf)
 +                              goto free_and_fail1;
                        /* They must match, otherwise ignore */
 -                      if (strcmp(cp+1, first))
 -                              goto free_both_and_fail;
 -                      free(sp);
 -                      return first;
 +                      if (strcmp(cp + 1, first.buf))
 +                              goto free_and_fail1;
 +                      strbuf_release(&sp);
 +                      return strbuf_detach(&first, NULL);
                }
  
                /* unquoted second */
                cp = stop_at_slash(second, line + llen - second);
                if (!cp || cp == second)
 -                      goto free_first_and_fail;
 +                      goto free_and_fail1;
                cp++;
 -              if (line + llen - cp != len + 1 ||
 -                  memcmp(first, cp, len))
 -                      goto free_first_and_fail;
 -              return first;
 +              if (line + llen - cp != first.len + 1 ||
 +                  memcmp(first.buf, cp, first.len))
 +                      goto free_and_fail1;
 +              return strbuf_detach(&first, NULL);
 +
 +      free_and_fail1:
 +              strbuf_release(&first);
 +              strbuf_release(&sp);
 +              return NULL;
        }
  
        /* unquoted first name */
        name = stop_at_slash(line, llen);
        if (!name || name == line)
                return NULL;
 -
        name++;
  
        /* since the first name is unquoted, a dq if exists must be
         */
        for (second = name; second < line + llen; second++) {
                if (*second == '"') {
 -                      const char *cp = second;
 +                      struct strbuf sp;
                        const char *np;
 -                      char *sp = unquote_c_style(second, NULL);
 -
 -                      if (!sp)
 -                              return NULL;
 -                      np = stop_at_slash(sp, strlen(sp));
 -                      if (!np || np == sp) {
 -                      free_second_and_fail:
 -                              free(sp);
 -                              return NULL;
 -                      }
 +
 +                      strbuf_init(&sp, 0);
 +                      if (unquote_c_style(&sp, second, NULL))
 +                              goto free_and_fail2;
 +
 +                      np = stop_at_slash(sp.buf, sp.len);
 +                      if (!np || np == sp.buf)
 +                              goto free_and_fail2;
                        np++;
 -                      len = strlen(np);
 -                      if (len < cp - name &&
 +
 +                      len = sp.buf + sp.len - np;
 +                      if (len < second - name &&
                            !strncmp(np, name, len) &&
                            isspace(name[len])) {
                                /* Good */
 -                              memmove(sp, np, len + 1);
 -                              return sp;
 +                              strbuf_remove(&sp, 0, np - sp.buf);
 +                              return strbuf_detach(&sp, NULL);
                        }
 -                      goto free_second_and_fail;
 +
 +              free_and_fail2:
 +                      strbuf_release(&sp);
 +                      return NULL;
                }
        }
  
                                        break;
                        }
                        if (second[len] == '\n' && !memcmp(name, second, len)) {
 -                              char *ret = xmalloc(len + 1);
 -                              memcpy(ret, name, len);
 -                              ret[len] = 0;
 -                              return ret;
 +                              return xmemdupz(name, len);
                        }
                }
        }
@@@ -1373,66 -1397,96 +1373,66 @@@ static const char minuses[]= "---------
  
  static void show_stats(struct patch *patch)
  {
 -      const char *prefix = "";
 -      char *name = patch->new_name;
 -      char *qname = NULL;
 -      int len, max, add, del, total;
 -
 -      if (!name)
 -              name = patch->old_name;
 +      struct strbuf qname;
 +      char *cp = patch->new_name ? patch->new_name : patch->old_name;
 +      int max, add, del;
  
 -      if (0 < (len = quote_c_style(name, NULL, NULL, 0))) {
 -              qname = xmalloc(len + 1);
 -              quote_c_style(name, qname, NULL, 0);
 -              name = qname;
 -      }
 +      strbuf_init(&qname, 0);
 +      quote_c_style(cp, &qname, NULL, 0);
  
        /*
         * "scale" the filename
         */
 -      len = strlen(name);
        max = max_len;
        if (max > 50)
                max = 50;
 -      if (len > max) {
 -              char *slash;
 -              prefix = "...";
 -              max -= 3;
 -              name += len - max;
 -              slash = strchr(name, '/');
 -              if (slash)
 -                      name = slash;
 +
 +      if (qname.len > max) {
 +              cp = strchr(qname.buf + qname.len + 3 - max, '/');
 +              if (!cp)
 +                      cp = qname.buf + qname.len + 3 - max;
 +              strbuf_splice(&qname, 0, cp - qname.buf, "...", 3);
 +      }
 +
 +      if (patch->is_binary) {
 +              printf(" %-*s |  Bin\n", max, qname.buf);
 +              strbuf_release(&qname);
 +              return;
        }
 -      len = max;
 +
 +      printf(" %-*s |", max, qname.buf);
 +      strbuf_release(&qname);
  
        /*
         * scale the add/delete
         */
 -      max = max_change;
 -      if (max + len > 70)
 -              max = 70 - len;
 -
 +      max = max + max_change > 70 ? 70 - max : max_change;
        add = patch->lines_added;
        del = patch->lines_deleted;
 -      total = add + del;
  
        if (max_change > 0) {
 -              total = (total * max + max_change / 2) / max_change;
 +              int total = ((add + del) * max + max_change / 2) / max_change;
                add = (add * max + max_change / 2) / max_change;
                del = total - add;
        }
 -      if (patch->is_binary)
 -              printf(" %s%-*s |  Bin\n", prefix, len, name);
 -      else
 -              printf(" %s%-*s |%5d %.*s%.*s\n", prefix,
 -                     len, name, patch->lines_added + patch->lines_deleted,
 -                     add, pluses, del, minuses);
 -      free(qname);
 +      printf("%5d %.*s%.*s\n", patch->lines_added + patch->lines_deleted,
 +              add, pluses, del, minuses);
  }
  
 -static int read_old_data(struct stat *st, const char *path, char **buf_p, unsigned long *alloc_p, unsigned long *size_p)
 +static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)
  {
 -      int fd;
 -      unsigned long got;
 -      unsigned long nsize;
 -      char *nbuf;
 -      unsigned long size = *size_p;
 -      char *buf = *buf_p;
 -
        switch (st->st_mode & S_IFMT) {
        case S_IFLNK:
 -              return readlink(path, buf, size) != size;
 +              strbuf_grow(buf, st->st_size);
 +              if (readlink(path, buf->buf, st->st_size) != st->st_size)
 +                      return -1;
 +              strbuf_setlen(buf, st->st_size);
 +              return 0;
        case S_IFREG:
 -              fd = open(path, O_RDONLY);
 -              if (fd < 0)
 -                      return error("unable to open %s", path);
 -              got = 0;
 -              for (;;) {
 -                      ssize_t ret = xread(fd, buf + got, size - got);
 -                      if (ret <= 0)
 -                              break;
 -                      got += ret;
 -              }
 -              close(fd);
 -              nsize = got;
 -              nbuf = convert_to_git(path, buf, &nsize);
 -              if (nbuf) {
 -                      free(buf);
 -                      *buf_p = nbuf;
 -                      *alloc_p = nsize;
 -                      *size_p = nsize;
 -              }
 -              return got != size;
 +              if (strbuf_read_file(buf, path, st->st_size) != st->st_size)
 +                      return error("unable to open or read %s", path);
 +              convert_to_git(path, buf->buf, buf->len, buf);
 +              return 0;
        default:
                return -1;
        }
@@@ -1537,6 -1591,12 +1537,6 @@@ static void remove_last_line(const cha
        *rsize = offset + 1;
  }
  
 -struct buffer_desc {
 -      char *buffer;
 -      unsigned long size;
 -      unsigned long alloc;
 -};
 -
  static int apply_line(char *output, const char *patch, int plen)
  {
        /* plen is number of bytes to be copied from patch,
        return output + plen - buf;
  }
  
 -static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, int inaccurate_eof)
 +static int apply_one_fragment(struct strbuf *buf, struct fragment *frag, int inaccurate_eof)
  {
        int match_beginning, match_end;
 -      char *buf = desc->buffer;
        const char *patch = frag->patch;
        int offset, size = frag->size;
        char *old = xmalloc(size);
        lines = 0;
        pos = frag->newpos;
        for (;;) {
 -              offset = find_offset(buf, desc->size,
 +              offset = find_offset(buf->buf, buf->len,
                                     oldlines, oldsize, pos, &lines);
 -              if (match_end && offset + oldsize != desc->size)
 +              if (match_end && offset + oldsize != buf->len)
                        offset = -1;
                if (match_beginning && offset)
                        offset = -1;
                if (offset >= 0) {
 -                      int diff;
 -                      unsigned long size, alloc;
 -
                        if (new_whitespace == strip_whitespace &&
 -                          (desc->size - oldsize - offset == 0)) /* end of file? */
 +                          (buf->len - oldsize - offset == 0)) /* end of file? */
                                newsize -= new_blank_lines_at_end;
  
 -                      diff = newsize - oldsize;
 -                      size = desc->size + diff;
 -                      alloc = desc->alloc;
 -
                        /* Warn if it was necessary to reduce the number
                         * of context lines.
                         */
                                        " to apply fragment at %d\n",
                                        leading, trailing, pos + lines);
  
 -                      if (size > alloc) {
 -                              alloc = size + 8192;
 -                              desc->alloc = alloc;
 -                              buf = xrealloc(buf, alloc);
 -                              desc->buffer = buf;
 -                      }
 -                      desc->size = size;
 -                      memmove(buf + offset + newsize,
 -                              buf + offset + oldsize,
 -                              size - offset - newsize);
 -                      memcpy(buf + offset, newlines, newsize);
 +                      strbuf_splice(buf, offset, oldsize, newlines, newsize);
                        offset = 0;
 -
                        break;
                }
  
        return offset;
  }
  
 -static int apply_binary_fragment(struct buffer_desc *desc, struct patch *patch)
 +static int apply_binary_fragment(struct strbuf *buf, struct patch *patch)
  {
 -      unsigned long dst_size;
        struct fragment *fragment = patch->fragments;
 -      void *data;
 -      void *result;
 +      unsigned long len;
 +      void *dst;
  
        /* Binary patch is irreversible without the optional second hunk */
        if (apply_in_reverse) {
                                     ? patch->new_name : patch->old_name);
                fragment = fragment->next;
        }
 -      data = (void*) fragment->patch;
        switch (fragment->binary_patch_method) {
        case BINARY_DELTA_DEFLATED:
 -              result = patch_delta(desc->buffer, desc->size,
 -                                   data,
 -                                   fragment->size,
 -                                   &dst_size);
 -              free(desc->buffer);
 -              desc->buffer = result;
 -              break;
 +              dst = patch_delta(buf->buf, buf->len, fragment->patch,
 +                                fragment->size, &len);
 +              if (!dst)
 +                      return -1;
 +              /* XXX patch_delta NUL-terminates */
 +              strbuf_attach(buf, dst, len, len + 1);
 +              return 0;
        case BINARY_LITERAL_DEFLATED:
 -              free(desc->buffer);
 -              desc->buffer = data;
 -              dst_size = fragment->size;
 -              break;
 +              strbuf_reset(buf);
 +              strbuf_add(buf, fragment->patch, fragment->size);
 +              return 0;
        }
 -      if (!desc->buffer)
 -              return -1;
 -      desc->size = desc->alloc = dst_size;
 -      return 0;
 +      return -1;
  }
  
 -static int apply_binary(struct buffer_desc *desc, struct patch *patch)
 +static int apply_binary(struct strbuf *buf, struct patch *patch)
  {
        const char *name = patch->old_name ? patch->old_name : patch->new_name;
        unsigned char sha1[20];
                /* See if the old one matches what the patch
                 * applies to.
                 */
 -              hash_sha1_file(desc->buffer, desc->size, blob_type, sha1);
 +              hash_sha1_file(buf->buf, buf->len, blob_type, sha1);
                if (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix))
                        return error("the patch applies to '%s' (%s), "
                                     "which does not match the "
        }
        else {
                /* Otherwise, the old one must be empty. */
 -              if (desc->size)
 +              if (buf->len)
                        return error("the patch applies to an empty "
                                     "'%s' but it is not empty", name);
        }
  
        get_sha1_hex(patch->new_sha1_prefix, sha1);
        if (is_null_sha1(sha1)) {
 -              free(desc->buffer);
 -              desc->alloc = desc->size = 0;
 -              desc->buffer = NULL;
 +              strbuf_release(buf);
                return 0; /* deletion patch */
        }
  
                /* We already have the postimage */
                enum object_type type;
                unsigned long size;
 +              char *result;
  
 -              free(desc->buffer);
 -              desc->buffer = read_sha1_file(sha1, &type, &size);
 -              if (!desc->buffer)
 +              result = read_sha1_file(sha1, &type, &size);
 +              if (!result)
                        return error("the necessary postimage %s for "
                                     "'%s' cannot be read",
                                     patch->new_sha1_prefix, name);
 -              desc->alloc = desc->size = size;
 -      }
 -      else {
 -              /* We have verified desc matches the preimage;
 +              /* XXX read_sha1_file NUL-terminates */
 +              strbuf_attach(buf, result, size, size + 1);
 +      else {
 +              /* We have verified buf matches the preimage;
                 * apply the patch data to it, which is stored
                 * in the patch->fragments->{patch,size}.
                 */
 -              if (apply_binary_fragment(desc, patch))
 +              if (apply_binary_fragment(buf, patch))
                        return error("binary patch does not apply to '%s'",
                                     name);
  
                /* verify that the result matches */
 -              hash_sha1_file(desc->buffer, desc->size, blob_type, sha1);
 +              hash_sha1_file(buf->buf, buf->len, blob_type, sha1);
                if (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix))
 -                      return error("binary patch to '%s' creates incorrect result (expecting %s, got %s)", name, patch->new_sha1_prefix, sha1_to_hex(sha1));
 +                      return error("binary patch to '%s' creates incorrect result (expecting %s, got %s)",
 +                              name, patch->new_sha1_prefix, sha1_to_hex(sha1));
        }
  
        return 0;
  }
  
 -static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
 +static int apply_fragments(struct strbuf *buf, struct patch *patch)
  {
        struct fragment *frag = patch->fragments;
        const char *name = patch->old_name ? patch->old_name : patch->new_name;
  
        if (patch->is_binary)
 -              return apply_binary(desc, patch);
 +              return apply_binary(buf, patch);
  
        while (frag) {
 -              if (apply_one_fragment(desc, frag, patch->inaccurate_eof)) {
 +              if (apply_one_fragment(buf, frag, patch->inaccurate_eof)) {
                        error("patch failed: %s:%ld", name, frag->oldpos);
                        if (!apply_with_reject)
                                return -1;
        return 0;
  }
  
 -static int read_file_or_gitlink(struct cache_entry *ce, char **buf_p,
 -                              unsigned long *size_p)
 +static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf)
  {
        if (!ce)
                return 0;
  
        if (S_ISGITLINK(ntohl(ce->ce_mode))) {
 -              *buf_p = xmalloc(100);
 -              *size_p = snprintf(*buf_p, 100,
 -                      "Subproject commit %s\n", sha1_to_hex(ce->sha1));
 +              strbuf_grow(buf, 100);
 +              strbuf_addf(buf, "Subproject commit %s\n", sha1_to_hex(ce->sha1));
        } else {
                enum object_type type;
 -              *buf_p = read_sha1_file(ce->sha1, &type, size_p);
 -              if (!*buf_p)
 +              unsigned long sz;
 +              char *result;
 +
 +              result = read_sha1_file(ce->sha1, &type, &sz);
 +              if (!result)
                        return -1;
 +              /* XXX read_sha1_file NUL-terminates */
 +              strbuf_attach(buf, result, sz, sz + 1);
        }
        return 0;
  }
  
  static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)
  {
 -      char *buf;
 -      unsigned long size, alloc;
 -      struct buffer_desc desc;
 +      struct strbuf buf;
  
 -      size = 0;
 -      alloc = 0;
 -      buf = NULL;
 +      strbuf_init(&buf, 0);
        if (cached) {
 -              if (read_file_or_gitlink(ce, &buf, &size))
 +              if (read_file_or_gitlink(ce, &buf))
                        return error("read of %s failed", patch->old_name);
 -              alloc = size;
        } else if (patch->old_name) {
                if (S_ISGITLINK(patch->old_mode)) {
 -                      if (ce)
 -                              read_file_or_gitlink(ce, &buf, &size);
 -                      else {
 +                      if (ce) {
 +                              read_file_or_gitlink(ce, &buf);
 +                      else {
                                /*
                                 * There is no way to apply subproject
                                 * patch without looking at the index.
                                 */
                                patch->fragments = NULL;
 -                              size = 0;
                        }
 -              }
 -              else {
 -                      size = xsize_t(st->st_size);
 -                      alloc = size + 8192;
 -                      buf = xmalloc(alloc);
 -                      if (read_old_data(st, patch->old_name,
 -                                        &buf, &alloc, &size))
 -                              return error("read of %s failed",
 -                                           patch->old_name);
 +              } else {
 +                      if (read_old_data(st, patch->old_name, &buf))
 +                              return error("read of %s failed", patch->old_name);
                }
        }
  
 -      desc.size = size;
 -      desc.alloc = alloc;
 -      desc.buffer = buf;
 -
 -      if (apply_fragments(&desc, patch) < 0)
 +      if (apply_fragments(&buf, patch) < 0)
                return -1; /* note with --reject this succeeds. */
 -
 -      /* NUL terminate the result */
 -      if (desc.alloc <= desc.size)
 -              desc.buffer = xrealloc(desc.buffer, desc.size + 1);
 -      desc.buffer[desc.size] = 0;
 -
 -      patch->result = desc.buffer;
 -      patch->resultsize = desc.size;
 +      patch->result = strbuf_detach(&buf, &patch->resultsize);
  
        if (0 < patch->is_delete && patch->resultsize)
                return error("removal patch leaves file contents");
@@@ -1993,7 -2099,7 +1993,7 @@@ static int verify_index_match(struct ca
                        return -1;
                return 0;
        }
-       return ce_match_stat(ce, st, 1);
+       return ce_match_stat(ce, st, CE_MATCH_IGNORE_VALID);
  }
  
  static int check_patch(struct patch *patch, struct patch *prev_patch)
@@@ -2209,8 -2315,13 +2209,8 @@@ static void numstat_patch_list(struct p
                if (patch->is_binary)
                        printf("-\t-\t");
                else
 -                      printf("%d\t%d\t",
 -                             patch->lines_added, patch->lines_deleted);
 -              if (line_termination && quote_c_style(name, NULL, NULL, 0))
 -                      quote_c_style(name, NULL, stdout, 0);
 -              else
 -                      fputs(name, stdout);
 -              putchar(line_termination);
 +                      printf("%d\t%d\t", patch->lines_added, patch->lines_deleted);
 +              write_name_quoted(name, stdout, line_termination);
        }
  }
  
@@@ -2319,6 -2430,7 +2319,6 @@@ static void remove_file(struct patch *p
        if (update_index) {
                if (remove_file_from_cache(patch->old_name) < 0)
                        die("unable to remove %s from index", patch->old_name);
 -              cache_tree_invalidate_path(active_cache_tree, patch->old_name);
        }
        if (!cached) {
                if (S_ISGITLINK(patch->old_mode)) {
@@@ -2375,7 -2487,7 +2375,7 @@@ static void add_index_file(const char *
  static int try_create_file(const char *path, unsigned int mode, const char *buf, unsigned long size)
  {
        int fd;
 -      char *nbuf;
 +      struct strbuf nbuf;
  
        if (S_ISGITLINK(mode)) {
                struct stat st;
        if (fd < 0)
                return -1;
  
 -      nbuf = convert_to_working_tree(path, buf, &size);
 -      if (nbuf)
 -              buf = nbuf;
 -
 -      while (size) {
 -              int written = xwrite(fd, buf, size);
 -              if (written < 0)
 -                      die("writing file %s: %s", path, strerror(errno));
 -              if (!written)
 -                      die("out of space writing file %s", path);
 -              buf += written;
 -              size -= written;
 +      strbuf_init(&nbuf, 0);
 +      if (convert_to_working_tree(path, buf, size, &nbuf)) {
 +              size = nbuf.len;
 +              buf  = nbuf.buf;
        }
 +      write_or_die(fd, buf, size);
 +      strbuf_release(&nbuf);
 +
        if (close(fd) < 0)
                die("closing file %s: %s", path, strerror(errno));
 -      if (nbuf)
 -              free(nbuf);
        return 0;
  }
  
@@@ -2466,6 -2585,7 +2466,6 @@@ static void create_file(struct patch *p
                mode = S_IFREG | 0644;
        create_one_file(path, mode, buf, size);
        add_index_file(path, mode, buf, size);
 -      cache_tree_invalidate_path(active_cache_tree, path);
  }
  
  /* phase zero is to remove, phase one is to create */
@@@ -2636,22 -2756,22 +2636,22 @@@ static void prefix_patches(struct patc
  
  static int apply_patch(int fd, const char *filename, int inaccurate_eof)
  {
 -      unsigned long offset, size;
 -      char *buffer = read_patch_file(fd, &size);
 +      size_t offset;
 +      struct strbuf buf;
        struct patch *list = NULL, **listp = &list;
        int skipped_patch = 0;
  
 +      strbuf_init(&buf, 0);
        patch_input_file = filename;
 -      if (!buffer)
 -              return -1;
 +      read_patch_file(&buf, fd);
        offset = 0;
 -      while (size > 0) {
 +      while (offset < buf.len) {
                struct patch *patch;
                int nr;
  
                patch = xcalloc(1, sizeof(*patch));
                patch->inaccurate_eof = inaccurate_eof;
 -              nr = parse_chunk(buffer + offset, size, patch);
 +              nr = parse_chunk(buf.buf + offset, buf.len - offset, patch);
                if (nr < 0)
                        break;
                if (apply_in_reverse)
                        skipped_patch++;
                }
                offset += nr;
 -              size -= nr;
        }
  
        if (whitespace_error && (new_whitespace == error_on_whitespace))
        if (summary)
                summary_patch_list(list);
  
 -      free(buffer);
 +      strbuf_release(&buf);
        return 0;
  }
  
diff --combined cache.h
+++ b/cache.h
@@@ -2,12 -2,11 +2,12 @@@
  #define CACHE_H
  
  #include "git-compat-util.h"
 +#include "strbuf.h"
  
  #include SHA1_HEADER
  #include <zlib.h>
  
 -#if ZLIB_VERNUM < 0x1200
 +#if defined(NO_DEFLATE_BOUND) || ZLIB_VERNUM < 0x1200
  #define deflateBound(c,s)  ((s) + (((s) + 7) >> 3) + (((s) + 63) >> 6) + 11)
  #endif
  
@@@ -175,8 -174,8 +175,8 @@@ extern struct index_state the_index
  #define remove_file_from_cache(path) remove_file_from_index(&the_index, (path))
  #define add_file_to_cache(path, verbose) add_file_to_index(&the_index, (path), (verbose))
  #define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL)
- #define ce_match_stat(ce, st, really) ie_match_stat(&the_index, (ce), (st), (really))
- #define ce_modified(ce, st, really) ie_modified(&the_index, (ce), (st), (really))
+ #define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))
+ #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
  #endif
  
  enum object_type {
@@@ -222,7 -221,6 +222,7 @@@ extern const char *get_git_work_tree(vo
  #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
  
  extern const char **get_pathspec(const char *prefix, const char **pathspec);
 +extern void setup_work_tree(void);
  extern const char *setup_git_directory_gently(int *);
  extern const char *setup_git_directory(void);
  extern const char *prefix_path(const char *prefix, int len, const char *path);
@@@ -268,10 -266,17 +268,16 @@@ extern int remove_file_from_index(struc
  extern int add_file_to_index(struct index_state *, const char *path, int verbose);
  extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh);
  extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);
- extern int ie_match_stat(struct index_state *, struct cache_entry *, struct stat *, int);
- extern int ie_modified(struct index_state *, struct cache_entry *, struct stat *, int);
+ /* do stat comparison even if CE_VALID is true */
+ #define CE_MATCH_IGNORE_VALID         01
+ /* do not check the contents but report dirty on racily-clean entries */
+ #define CE_MATCH_RACY_IS_DIRTY        02
+ extern int ie_match_stat(struct index_state *, struct cache_entry *, struct stat *, unsigned int);
+ extern int ie_modified(struct index_state *, struct cache_entry *, struct stat *, unsigned int);
  extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
  extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path);
 -extern int read_fd(int fd, char **return_buf, unsigned long *return_size);
  extern int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object);
  extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
  extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
@@@ -433,7 -438,6 +439,7 @@@ const char *show_date(unsigned long tim
  int parse_date(const char *date, char *buf, int bufsize);
  void datestamp(char *buf, int bufsize);
  unsigned long approxidate(const char *);
 +enum date_mode parse_date_format(const char *format);
  
  extern const char *git_author_info(int);
  extern const char *git_committer_info(int);
@@@ -494,7 -498,6 +500,7 @@@ struct ref 
        unsigned char old_sha1[20];
        unsigned char new_sha1[20];
        unsigned char force;
 +      unsigned char merge;
        struct ref *peer_ref; /* when renaming */
        char name[FLEX_ARRAY]; /* more */
  };
  #define REF_TAGS      (1u << 2)
  
  #define CONNECT_VERBOSE       (1u << 0)
 -extern pid_t git_connect(int fd[2], char *url, const char *prog, int flags);
 -extern int finish_connect(pid_t pid);
 +extern struct child_process *git_connect(int fd[2], char *url, const char *prog, int flags);
 +extern int finish_connect(struct child_process *conn);
  extern int path_match(const char *path, int nr, char **match);
  extern int get_ack(int fd, unsigned char *result_sha1);
  extern struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match, unsigned int flags);
@@@ -533,7 -536,6 +539,7 @@@ extern void *unpack_entry(struct packed
  extern unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
  extern unsigned long get_size_from_delta(struct packed_git *, struct pack_window **, off_t);
  extern const char *packed_object_info_detail(struct packed_git *, off_t, unsigned long *, unsigned long *, unsigned int *, unsigned char *);
 +extern int matches_pack_name(struct packed_git *p, const char *name);
  
  /* Dumb servers support */
  extern int update_server_info(int);
@@@ -589,13 -591,15 +595,13 @@@ extern void *alloc_object_node(void)
  extern void alloc_report(void);
  
  /* trace.c */
 -extern int nfasprintf(char **str, const char *fmt, ...);
 -extern int nfvasprintf(char **str, const char *fmt, va_list va);
  extern void trace_printf(const char *format, ...);
  extern void trace_argv_printf(const char **argv, int count, const char *format, ...);
  
  /* convert.c */
 -extern char *convert_to_git(const char *path, const char *src, unsigned long *sizep);
 -extern char *convert_to_working_tree(const char *path, const char *src, unsigned long *sizep);
 -extern void *convert_sha1_file(const char *path, const unsigned char *sha1, unsigned int mode, enum object_type *type, unsigned long *size);
 +/* returns 1 if *dst was used */
 +extern int convert_to_git(const char *path, const char *src, size_t len, struct strbuf *dst);
 +extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst);
  
  /* diff.c */
  extern int diff_auto_refresh_index;
diff --combined entry.c
+++ b/entry.c
@@@ -104,8 -104,7 +104,8 @@@ static int write_entry(struct cache_ent
        long wrote;
  
        switch (ntohl(ce->ce_mode) & S_IFMT) {
 -              char *buf, *new;
 +              char *new;
 +              struct strbuf buf;
                unsigned long size;
  
        case S_IFREG:
                /*
                 * Convert from git internal format to working tree format
                 */
 -              buf = convert_to_working_tree(ce->name, new, &size);
 -              if (buf) {
 +              strbuf_init(&buf, 0);
 +              if (convert_to_working_tree(ce->name, new, size, &buf)) {
 +                      size_t newsize = 0;
                        free(new);
 -                      new = buf;
 +                      new = strbuf_detach(&buf, &newsize);
 +                      size = newsize;
                }
  
                if (to_tempfile) {
@@@ -203,7 -200,7 +203,7 @@@ int checkout_entry(struct cache_entry *
        strcpy(path + len, ce->name);
  
        if (!lstat(path, &st)) {
-               unsigned changed = ce_match_stat(ce, &st, 1);
+               unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID);
                if (!changed)
                        return 0;
                if (!state->force) {
diff --combined read-cache.c
@@@ -194,11 -194,12 +194,12 @@@ static int ce_match_stat_basic(struct c
  }
  
  int ie_match_stat(struct index_state *istate,
-                 struct cache_entry *ce, struct stat *st, int options)
+                 struct cache_entry *ce, struct stat *st,
+                 unsigned int options)
  {
        unsigned int changed;
-       int ignore_valid = options & 01;
-       int assume_racy_is_modified = options & 02;
+       int ignore_valid = options & CE_MATCH_IGNORE_VALID;
+       int assume_racy_is_modified = options & CE_MATCH_RACY_IS_DIRTY;
  
        /*
         * If it's marked as always valid in the index, it's
  }
  
  int ie_modified(struct index_state *istate,
-               struct cache_entry *ce, struct stat *st, int really)
+               struct cache_entry *ce, struct stat *st, unsigned int options)
  {
        int changed, changed_fs;
-       changed = ie_match_stat(istate, ce, st, really);
+       changed = ie_match_stat(istate, ce, st, options);
        if (!changed)
                return 0;
        /*
@@@ -348,7 -350,6 +350,7 @@@ int remove_file_from_index(struct index
        int pos = index_name_pos(istate, path, strlen(path));
        if (pos < 0)
                pos = -pos-1;
 +      cache_tree_invalidate_path(istate->cache_tree, path);
        while (pos < istate->cache_nr && !strcmp(istate->cache[pos]->name, path))
                remove_index_entry_at(istate, pos);
        return 0;
@@@ -387,6 -388,7 +389,7 @@@ int add_file_to_index(struct index_stat
        int size, namelen, pos;
        struct stat st;
        struct cache_entry *ce;
+       unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_RACY_IS_DIRTY;
  
        if (lstat(path, &st))
                die("%s: unable to stat (%s)", path, strerror(errno));
        pos = index_name_pos(istate, ce->name, namelen);
        if (0 <= pos &&
            !ce_stage(istate->cache[pos]) &&
-           !ie_modified(istate, istate->cache[pos], &st, 1)) {
+           !ie_match_stat(istate, istate->cache[pos], &st, ce_option)) {
                /* Nothing changed, really */
                free(ce);
                return 0;
                die("unable to add %s to index",path);
        if (verbose)
                printf("add '%s'\n", path);
 -      cache_tree_invalidate_path(istate->cache_tree, path);
        return 0;
  }
  
@@@ -700,7 -703,6 +703,7 @@@ static int add_index_entry_with_check(s
        int ok_to_replace = option & ADD_CACHE_OK_TO_REPLACE;
        int skip_df_check = option & ADD_CACHE_SKIP_DFCHECK;
  
 +      cache_tree_invalidate_path(istate->cache_tree, ce->name);
        pos = index_name_pos(istate, ce->name, ntohs(ce->ce_flags));
  
        /* existing match? Just replace it. */
@@@ -783,11 -785,13 +786,13 @@@ int add_index_entry(struct index_state 
   * to link up the stat cache details with the proper files.
   */
  static struct cache_entry *refresh_cache_ent(struct index_state *istate,
-                                            struct cache_entry *ce, int really, int *err)
+                                            struct cache_entry *ce,
+                                            unsigned int options, int *err)
  {
        struct stat st;
        struct cache_entry *updated;
        int changed, size;
+       int ignore_valid = options & CE_MATCH_IGNORE_VALID;
  
        if (lstat(ce->name, &st) < 0) {
                if (err)
                return NULL;
        }
  
-       changed = ie_match_stat(istate, ce, &st, really);
+       changed = ie_match_stat(istate, ce, &st, options);
        if (!changed) {
-               if (really && assume_unchanged &&
+               /*
+                * The path is unchanged.  If we were told to ignore
+                * valid bit, then we did the actual stat check and
+                * found that the entry is unmodified.  If the entry
+                * is not marked VALID, this is the place to mark it
+                * valid again, under "assume unchanged" mode.
+                */
+               if (ignore_valid && assume_unchanged &&
                    !(ce->ce_flags & htons(CE_VALID)))
                        ; /* mark this one VALID again */
                else
                        return ce;
        }
  
-       if (ie_modified(istate, ce, &st, really)) {
+       if (ie_modified(istate, ce, &st, options)) {
                if (err)
                        *err = EINVAL;
                return NULL;
        memcpy(updated, ce, size);
        fill_stat_cache_info(updated, &st);
  
-       /* In this case, if really is not set, we should leave
-        * CE_VALID bit alone.  Otherwise, paths marked with
-        * --no-assume-unchanged (i.e. things to be edited) will
-        * reacquire CE_VALID bit automatically, which is not
-        * really what we want.
+       /*
+        * If ignore_valid is not set, we should leave CE_VALID bit
+        * alone.  Otherwise, paths marked with --no-assume-unchanged
+        * (i.e. things to be edited) will reacquire CE_VALID bit
+        * automatically, which is not really what we want.
         */
-       if (!really && assume_unchanged && !(ce->ce_flags & htons(CE_VALID)))
+       if (!ignore_valid && assume_unchanged &&
+           !(ce->ce_flags & htons(CE_VALID)))
                updated->ce_flags &= ~htons(CE_VALID);
  
        return updated;
@@@ -835,6 -847,7 +848,7 @@@ int refresh_index(struct index_state *i
        int allow_unmerged = (flags & REFRESH_UNMERGED) != 0;
        int quiet = (flags & REFRESH_QUIET) != 0;
        int not_new = (flags & REFRESH_IGNORE_MISSING) != 0;
+       unsigned int options = really ? CE_MATCH_IGNORE_VALID : 0;
  
        for (i = 0; i < istate->cache_nr; i++) {
                struct cache_entry *ce, *new;
                if (pathspec && !match_pathspec(pathspec, ce->name, strlen(ce->name), 0, seen))
                        continue;
  
-               new = refresh_cache_ent(istate, ce, really, &cache_errno);
+               new = refresh_cache_ent(istate, ce, options, &cache_errno);
                if (new == ce)
                        continue;
                if (!new) {
@@@ -1138,7 -1151,7 +1152,7 @@@ int write_index(struct index_state *ist
  {
        SHA_CTX c;
        struct cache_header hdr;
 -      int i, removed;
 +      int i, err, removed;
        struct cache_entry **cache = istate->cache;
        int entries = istate->cache_nr;
  
  
        /* Write extension data here */
        if (istate->cache_tree) {
 -              unsigned long sz;
 -              void *data = cache_tree_write(istate->cache_tree, &sz);
 -              if (data &&
 -                  !write_index_ext_header(&c, newfd, CACHE_EXT_TREE, sz) &&
 -                  !ce_write(&c, newfd, data, sz))
 -                      free(data);
 -              else {
 -                      free(data);
 +              struct strbuf sb;
 +
 +              strbuf_init(&sb, 0);
 +              cache_tree_write(&sb, istate->cache_tree);
 +              err = write_index_ext_header(&c, newfd, CACHE_EXT_TREE, sb.len) < 0
 +                      || ce_write(&c, newfd, sb.buf, sb.len) < 0;
 +              strbuf_release(&sb);
 +              if (err)
                        return -1;
 -              }
        }
        return ce_flush(&c, newfd);
  }
diff --combined unpack-trees.c
@@@ -297,7 -297,7 +297,7 @@@ static void check_updates(struct cache_
  {
        unsigned short mask = htons(CE_UPDATE);
        unsigned cnt = 0, total = 0;
 -      struct progress progress;
 +      struct progress *progress = NULL;
        char last_symlink[PATH_MAX];
  
        if (o->update && o->verbose_update) {
                                total++;
                }
  
 -              start_progress_delay(&progress, "Checking %u files out...",
 -                                   "", total, 50, 2);
 +              progress = start_progress_delay("Checking out files",
 +                                              total, 50, 2);
                cnt = 0;
        }
  
        while (nr--) {
                struct cache_entry *ce = *src++;
  
 -              if (total)
 -                      if (!ce->ce_mode || ce->ce_flags & mask)
 -                              display_progress(&progress, ++cnt);
 +              if (!ce->ce_mode || ce->ce_flags & mask)
 +                      display_progress(progress, ++cnt);
                if (!ce->ce_mode) {
                        if (o->update)
                                unlink_entry(ce->name, last_symlink);
                        }
                }
        }
 -      if (total)
 -              stop_progress(&progress);;
 +      stop_progress(&progress);
  }
  
  int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o)
@@@ -404,7 -406,7 +404,7 @@@ static void verify_uptodate(struct cach
                return;
  
        if (!lstat(ce->name, &st)) {
-               unsigned changed = ce_match_stat(ce, &st, 1);
+               unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID);
                if (!changed)
                        return;
                /*
@@@ -925,7 -927,7 +925,7 @@@ int oneway_merge(struct cache_entry **s
                if (o->reset) {
                        struct stat st;
                        if (lstat(old->name, &st) ||
-                           ce_match_stat(old, &st, 1))
+                           ce_match_stat(old, &st, CE_MATCH_IGNORE_VALID))
                                old->ce_flags |= htons(CE_UPDATE);
                }
                return keep_entry(old, o);