Sync with 2.4.9
authorJunio C Hamano <gitster@pobox.com>
Fri, 4 Sep 2015 17:43:23 +0000 (10:43 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 4 Sep 2015 17:43:23 +0000 (10:43 -0700)
1  2 
Documentation/git.txt
builtin/show-branch.c
notes.c
sha1_file.c
unpack-trees.c
diff --combined Documentation/git.txt
index 5a78d77533f18c96a0fbee7ac97ba8478eb169ca,97d9fb41b2b41555732cc349da043f7ce3a2f24a..3b5573d1919b7c6bd5edffa05adb4900a870e00e
@@@ -43,15 -43,10 +43,16 @@@ unreleased) version of Git, that is ava
  branch of the `git.git` repository.
  Documentation for older releases are available here:
  
- * link:v2.4.8/git.html[documentation for release 2.4.8]
 +* link:v2.5.1/git.html[documentation for release 2.5.1]
 +
 +* release notes for
 +  link:RelNotes/2.5.1.txt[2.5.1],
 +  link:RelNotes/2.5.0.txt[2.5].
 +
+ * link:v2.4.9/git.html[documentation for release 2.4.9]
  
  * release notes for
+   link:RelNotes/2.4.9.txt[2.4.9],
    link:RelNotes/2.4.8.txt[2.4.8],
    link:RelNotes/2.4.7.txt[2.4.7],
    link:RelNotes/2.4.6.txt[2.4.6],
    link:RelNotes/2.4.1.txt[2.4.1],
    link:RelNotes/2.4.0.txt[2.4].
  
- * link:v2.3.8/git.html[documentation for release 2.3.8]
+ * link:v2.3.9/git.html[documentation for release 2.3.9]
  
  * release notes for
+   link:RelNotes/2.3.9.txt[2.3.9],
    link:RelNotes/2.3.8.txt[2.3.8],
    link:RelNotes/2.3.7.txt[2.3.7],
    link:RelNotes/2.3.6.txt[2.3.6],
    link:RelNotes/2.3.1.txt[2.3.1],
    link:RelNotes/2.3.0.txt[2.3].
  
- * link:v2.2.2/git.html[documentation for release 2.2.2]
+ * link:v2.2.3/git.html[documentation for release 2.2.3]
  
  * release notes for
+   link:RelNotes/2.2.3.txt[2.2.3],
    link:RelNotes/2.2.2.txt[2.2.2],
    link:RelNotes/2.2.1.txt[2.2.1],
    link:RelNotes/2.2.0.txt[2.2].
@@@ -782,7 -779,7 +785,7 @@@ The Git Repositor
  ~~~~~~~~~~~~~~~~~~
  These environment variables apply to 'all' core Git commands. Nb: it
  is worth noting that they may be used/overridden by SCMS sitting above
 -Git so take care if using Cogito etc.
 +Git so take care if using a foreign front-end.
  
  'GIT_INDEX_FILE'::
        This environment allows the specification of an alternate
        an explicit repository directory set via 'GIT_DIR' or on the
        command line.
  
 +'GIT_COMMON_DIR'::
 +      If this variable is set to a path, non-worktree files that are
 +      normally in $GIT_DIR will be taken from this path
 +      instead. Worktree-specific files such as HEAD or index are
 +      taken from $GIT_DIR. See linkgit:gitrepository-layout[5] and
 +      linkgit:git-worktree[1] for
 +      details. This variable has lower precedence than other path
 +      variables such as GIT_INDEX_FILE, GIT_OBJECT_DIRECTORY...
 +
  Git Commits
  ~~~~~~~~~~~
  'GIT_AUTHOR_NAME'::
diff --combined builtin/show-branch.c
index 323f85746358676cccecd646e6cb820e723031cf,9b0aba2accee0b1984aba859b22b423f0bab5dec..344ae4ce2a8d95f0aca6f93ef2d714db059e6431
@@@ -369,10 -369,10 +369,10 @@@ static void sort_ref_range(int bottom, 
              compare_ref_name);
  }
  
 -static int append_ref(const char *refname, const unsigned char *sha1,
 +static int append_ref(const char *refname, const struct object_id *oid,
                      int allow_dups)
  {
 -      struct commit *commit = lookup_commit_reference_gently(sha1, 1);
 +      struct commit *commit = lookup_commit_reference_gently(oid->hash, 1);
        int i;
  
        if (!commit)
        return 0;
  }
  
 -static int append_head_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 +static int append_head_ref(const char *refname, const struct object_id *oid,
 +                         int flag, void *cb_data)
  {
 -      unsigned char tmp[20];
 +      struct object_id tmp;
        int ofs = 11;
        if (!starts_with(refname, "refs/heads/"))
                return 0;
        /* If both heads/foo and tags/foo exists, get_sha1 would
         * get confused.
         */
 -      if (get_sha1(refname + ofs, tmp) || hashcmp(tmp, sha1))
 +      if (get_sha1(refname + ofs, tmp.hash) || oidcmp(&tmp, oid))
                ofs = 5;
 -      return append_ref(refname + ofs, sha1, 0);
 +      return append_ref(refname + ofs, oid, 0);
  }
  
 -static int append_remote_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 +static int append_remote_ref(const char *refname, const struct object_id *oid,
 +                           int flag, void *cb_data)
  {
 -      unsigned char tmp[20];
 +      struct object_id tmp;
        int ofs = 13;
        if (!starts_with(refname, "refs/remotes/"))
                return 0;
        /* If both heads/foo and tags/foo exists, get_sha1 would
         * get confused.
         */
 -      if (get_sha1(refname + ofs, tmp) || hashcmp(tmp, sha1))
 +      if (get_sha1(refname + ofs, tmp.hash) || oidcmp(&tmp, oid))
                ofs = 5;
 -      return append_ref(refname + ofs, sha1, 0);
 +      return append_ref(refname + ofs, oid, 0);
  }
  
 -static int append_tag_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 +static int append_tag_ref(const char *refname, const struct object_id *oid,
 +                        int flag, void *cb_data)
  {
        if (!starts_with(refname, "refs/tags/"))
                return 0;
 -      return append_ref(refname + 5, sha1, 0);
 +      return append_ref(refname + 5, oid, 0);
  }
  
  static const char *match_ref_pattern = NULL;
@@@ -443,8 -440,7 +443,8 @@@ static int count_slash(const char *s
        return cnt;
  }
  
 -static int append_matching_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 +static int append_matching_ref(const char *refname, const struct object_id *oid,
 +                             int flag, void *cb_data)
  {
        /* we want to allow pattern hold/<asterisk> to show all
         * branches under refs/heads/hold/, and v0.99.9? to show
        if (wildmatch(match_ref_pattern, tail, 0, NULL))
                return 0;
        if (starts_with(refname, "refs/heads/"))
 -              return append_head_ref(refname, sha1, flag, cb_data);
 +              return append_head_ref(refname, oid, flag, cb_data);
        if (starts_with(refname, "refs/tags/"))
 -              return append_tag_ref(refname, sha1, flag, cb_data);
 -      return append_ref(refname, sha1, 0);
 +              return append_tag_ref(refname, oid, flag, cb_data);
 +      return append_ref(refname, oid, 0);
  }
  
  static void snarf_refs(int head, int remotes)
  {
        if (head) {
                int orig_cnt = ref_name_cnt;
 +
                for_each_ref(append_head_ref, NULL);
                sort_ref_range(orig_cnt, ref_name_cnt);
        }
        if (remotes) {
                int orig_cnt = ref_name_cnt;
 +
                for_each_ref(append_remote_ref, NULL);
                sort_ref_range(orig_cnt, ref_name_cnt);
        }
@@@ -536,15 -530,14 +536,15 @@@ static int show_independent(struct comm
  
  static void append_one_rev(const char *av)
  {
 -      unsigned char revkey[20];
 -      if (!get_sha1(av, revkey)) {
 -              append_ref(av, revkey, 0);
 +      struct object_id revkey;
 +      if (!get_sha1(av, revkey.hash)) {
 +              append_ref(av, &revkey, 0);
                return;
        }
        if (strchr(av, '*') || strchr(av, '?') || strchr(av, '[')) {
                /* glob style match */
                int saved_matches = ref_name_cnt;
 +
                match_ref_pattern = av;
                match_ref_slash = count_slash(av);
                for_each_ref(append_matching_ref, NULL);
@@@ -643,7 -636,7 +643,7 @@@ int cmd_show_branch(int ac, const char 
        char head[128];
        const char *head_p;
        int head_len;
 -      unsigned char head_sha1[20];
 +      struct object_id head_oid;
        int merge_base = 0;
        int independent = 0;
        int no_name = 0;
        }
  
        /* If nothing is specified, show all branches by default */
 -      if (ac + all_heads + all_remotes == 0)
 +      if (ac <= topics && all_heads + all_remotes == 0)
                all_heads = 1;
  
        if (reflog) {
 -              unsigned char sha1[20];
 +              struct object_id oid;
-               char nth_desc[256];
                char *ref;
                int base = 0;
                unsigned int flags = 0;
  
                        fake_av[0] = resolve_refdup("HEAD",
                                                    RESOLVE_REF_READING,
 -                                                  sha1, NULL);
 +                                                  oid.hash, NULL);
                        fake_av[1] = NULL;
                        av = fake_av;
                        ac = 1;
                if (MAX_REVS < reflog)
                        die("Only %d entries can be shown at one time.",
                            MAX_REVS);
 -              if (!dwim_ref(*av, strlen(*av), sha1, &ref))
 +              if (!dwim_ref(*av, strlen(*av), oid.hash, &ref))
                        die("No such ref %s", *av);
  
                /* Has the base been specified? */
                                /* Ah, that is a date spec... */
                                unsigned long at;
                                at = approxidate(reflog_base);
 -                              read_ref_at(ref, flags, at, -1, sha1, NULL,
 +                              read_ref_at(ref, flags, at, -1, oid.hash, NULL,
                                            NULL, NULL, &base);
                        }
                }
  
                for (i = 0; i < reflog; i++) {
                        char *logmsg;
+                       char *nth_desc;
                        const char *msg;
                        unsigned long timestamp;
                        int tz;
  
 -                      if (read_ref_at(ref, flags, 0, base+i, sha1, &logmsg,
 +                      if (read_ref_at(ref, flags, 0, base+i, oid.hash, &logmsg,
                                        &timestamp, &tz, NULL)) {
                                reflog = i;
                                break;
                                                show_date(timestamp, tz, 1),
                                                msg);
                        free(logmsg);
-                       sprintf(nth_desc, "%s@{%d}", *av, base+i);
+                       nth_desc = xstrfmt("%s@{%d}", *av, base+i);
 -                      append_ref(nth_desc, sha1, 1);
 +                      append_ref(nth_desc, &oid, 1);
+                       free(nth_desc);
                }
                free(ref);
        }
 -      else if (all_heads + all_remotes)
 -              snarf_refs(all_heads, all_remotes);
        else {
                while (0 < ac) {
                        append_one_rev(*av);
                        ac--; av++;
                }
 +              if (all_heads + all_remotes)
 +                      snarf_refs(all_heads, all_remotes);
        }
  
        head_p = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
 -                                  head_sha1, NULL);
 +                                  head_oid.hash, NULL);
        if (head_p) {
                head_len = strlen(head_p);
                memcpy(head, head_p, head_len + 1);
                        if (rev_is_head(head,
                                        head_len,
                                        ref_name[i],
 -                                      head_sha1, NULL))
 +                                      head_oid.hash, NULL))
                                has_head++;
                }
                if (!has_head) {
        }
  
        for (num_rev = 0; ref_name[num_rev]; num_rev++) {
 -              unsigned char revkey[20];
 +              struct object_id revkey;
                unsigned int flag = 1u << (num_rev + REV_SHIFT);
  
                if (MAX_REVS <= num_rev)
                        die("cannot handle more than %d revs.", MAX_REVS);
 -              if (get_sha1(ref_name[num_rev], revkey))
 +              if (get_sha1(ref_name[num_rev], revkey.hash))
                        die("'%s' is not a valid ref.", ref_name[num_rev]);
 -              commit = lookup_commit_reference(revkey);
 +              commit = lookup_commit_reference(revkey.hash);
                if (!commit)
                        die("cannot find commit %s (%s)",
 -                          ref_name[num_rev], revkey);
 +                          ref_name[num_rev], oid_to_hex(&revkey));
                parse_commit(commit);
                mark_seen(commit, &seen);
  
                        int is_head = rev_is_head(head,
                                                  head_len,
                                                  ref_name[i],
 -                                                head_sha1,
 +                                                head_oid.hash,
                                                  rev[i]->object.sha1);
                        if (extra < 0)
                                printf("%c [%s] ",
diff --combined notes.c
index df08209dee498d624817f071d7e9ac5194d2334f,d46520054123bd52d10496bb09b67725ea0614d5..eacd2a61daf9b4133e81a936340abc63c1078a48
+++ b/notes.c
@@@ -362,13 -362,14 +362,14 @@@ static int non_note_cmp(const struct no
        return strcmp(a->path, b->path);
  }
  
- static void add_non_note(struct notes_tree *t, const char *path,
+ /* note: takes ownership of path string */
+ static void add_non_note(struct notes_tree *t, char *path,
                unsigned int mode, const unsigned char *sha1)
  {
        struct non_note *p = t->prev_non_note, *n;
        n = (struct non_note *) xmalloc(sizeof(struct non_note));
        n->next = NULL;
-       n->path = xstrdup(path);
+       n->path = path;
        n->mode = mode;
        hashcpy(n->sha1, sha1);
        t->prev_non_note = n;
@@@ -482,17 -483,17 +483,17 @@@ handle_non_note
                 * component.
                 */
                {
-                       char non_note_path[PATH_MAX];
-                       char *p = non_note_path;
+                       struct strbuf non_note_path = STRBUF_INIT;
                        const char *q = sha1_to_hex(subtree->key_sha1);
                        int i;
                        for (i = 0; i < prefix_len; i++) {
-                               *p++ = *q++;
-                               *p++ = *q++;
-                               *p++ = '/';
+                               strbuf_addch(&non_note_path, *q++);
+                               strbuf_addch(&non_note_path, *q++);
+                               strbuf_addch(&non_note_path, '/');
                        }
-                       strcpy(p, entry.path);
-                       add_non_note(t, non_note_path, entry.mode, entry.sha1);
+                       strbuf_addstr(&non_note_path, entry.path);
+                       add_non_note(t, strbuf_detach(&non_note_path, NULL),
+                                    entry.mode, entry.sha1);
                }
        }
        free(buf);
@@@ -918,7 -919,7 +919,7 @@@ out
        return ret;
  }
  
 -static int string_list_add_one_ref(const char *refname, const unsigned char *sha1,
 +static int string_list_add_one_ref(const char *refname, const struct object_id *oid,
                                   int flag, void *cb)
  {
        struct string_list *refs = cb;
diff --combined sha1_file.c
index f2e993e0b8b998c91b945771a87a099d882448f5,99155c0d6b77aa2a772330d51fa604b1c4a9b0cf..17262e18269e0fa97236fc6a39e9731a2752736a
@@@ -377,15 -377,12 +377,12 @@@ void read_info_alternates(const char * 
        char *map;
        size_t mapsz;
        struct stat st;
-       const char alt_file_name[] = "info/alternates";
-       /* Given that relative_base is no longer than PATH_MAX,
-          ensure that "path" has enough space to append "/", the
-          file name, "info/alternates", and a trailing NUL.  */
-       char path[PATH_MAX + 1 + sizeof alt_file_name];
+       char *path;
        int fd;
  
-       sprintf(path, "%s/%s", relative_base, alt_file_name);
+       path = xstrfmt("%s/info/alternates", relative_base);
        fd = git_open_noatime(path);
+       free(path);
        if (fd < 0)
                return;
        if (fstat(fd, &st) || (st.st_size == 0)) {
@@@ -405,7 -402,7 +402,7 @@@ void add_to_alternates_file(const char 
  {
        struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
        int fd = hold_lock_file_for_append(lock, git_path("objects/info/alternates"), LOCK_DIE_ON_ERROR);
 -      char *alt = mkpath("%s\n", reference);
 +      const char *alt = mkpath("%s\n", reference);
        write_or_die(fd, alt, strlen(alt));
        if (commit_lock_file(lock))
                die("could not close alternates file");
@@@ -1461,10 -1458,7 +1458,10 @@@ int git_open_noatime(const char *name
        static int sha1_file_open_flag = O_NOATIME;
  
        for (;;) {
 -              int fd = open(name, O_RDONLY | sha1_file_open_flag);
 +              int fd;
 +
 +              errno = 0;
 +              fd = open(name, O_RDONLY | sha1_file_open_flag);
                if (fd >= 0)
                        return fd;
  
@@@ -1582,40 -1576,6 +1579,40 @@@ int unpack_sha1_header(git_zstream *str
        return git_inflate(stream, 0);
  }
  
 +static int unpack_sha1_header_to_strbuf(git_zstream *stream, unsigned char *map,
 +                                      unsigned long mapsize, void *buffer,
 +                                      unsigned long bufsiz, struct strbuf *header)
 +{
 +      int status;
 +
 +      status = unpack_sha1_header(stream, map, mapsize, buffer, bufsiz);
 +
 +      /*
 +       * Check if entire header is unpacked in the first iteration.
 +       */
 +      if (memchr(buffer, '\0', stream->next_out - (unsigned char *)buffer))
 +              return 0;
 +
 +      /*
 +       * buffer[0..bufsiz] was not large enough.  Copy the partial
 +       * result out to header, and then append the result of further
 +       * reading the stream.
 +       */
 +      strbuf_add(header, buffer, stream->next_out - (unsigned char *)buffer);
 +      stream->next_out = buffer;
 +      stream->avail_out = bufsiz;
 +
 +      do {
 +              status = git_inflate(stream, 0);
 +              strbuf_add(header, buffer, stream->next_out - (unsigned char *)buffer);
 +              if (memchr(buffer, '\0', stream->next_out - (unsigned char *)buffer))
 +                      return 0;
 +              stream->next_out = buffer;
 +              stream->avail_out = bufsiz;
 +      } while (status != Z_STREAM_END);
 +      return -1;
 +}
 +
  static void *unpack_sha1_rest(git_zstream *stream, void *buffer, unsigned long size, const unsigned char *sha1)
  {
        int bytes = strlen(buffer) + 1;
   * too permissive for what we want to check. So do an anal
   * object header parse by hand.
   */
 -int parse_sha1_header(const char *hdr, unsigned long *sizep)
 +static int parse_sha1_header_extended(const char *hdr, struct object_info *oi,
 +                             unsigned int flags)
  {
 -      char type[10];
 -      int i;
 +      const char *type_buf = hdr;
        unsigned long size;
 +      int type, type_len = 0;
  
        /*
 -       * The type can be at most ten bytes (including the
 -       * terminating '\0' that we add), and is followed by
 +       * The type can be of any size but is followed by
         * a space.
         */
 -      i = 0;
        for (;;) {
                char c = *hdr++;
                if (c == ' ')
                        break;
 -              type[i++] = c;
 -              if (i >= sizeof(type))
 -                      return -1;
 +              type_len++;
        }
 -      type[i] = 0;
 +
 +      type = type_from_string_gently(type_buf, type_len, 1);
 +      if (oi->typename)
 +              strbuf_add(oi->typename, type_buf, type_len);
 +      /*
 +       * Set type to 0 if its an unknown object and
 +       * we're obtaining the type using '--allow-unkown-type'
 +       * option.
 +       */
 +      if ((flags & LOOKUP_UNKNOWN_OBJECT) && (type < 0))
 +              type = 0;
 +      else if (type < 0)
 +              die("invalid object type");
 +      if (oi->typep)
 +              *oi->typep = type;
  
        /*
         * The length must follow immediately, and be in canonical
                        size = size * 10 + c;
                }
        }
 -      *sizep = size;
 +
 +      if (oi->sizep)
 +              *oi->sizep = size;
  
        /*
         * The length must be followed by a zero byte
         */
 -      return *hdr ? -1 : type_from_string(type);
 +      return *hdr ? -1 : type;
 +}
 +
 +int parse_sha1_header(const char *hdr, unsigned long *sizep)
 +{
 +      struct object_info oi;
 +
 +      oi.sizep = sizep;
 +      oi.typename = NULL;
 +      oi.typep = NULL;
 +      return parse_sha1_header_extended(hdr, &oi, LOOKUP_REPLACE_OBJECT);
  }
  
  static void *unpack_sha1_file(void *map, unsigned long mapsize, enum object_type *type, unsigned long *size, const unsigned char *sha1)
@@@ -2597,15 -2534,13 +2594,15 @@@ struct packed_git *find_sha1_pack(cons
  }
  
  static int sha1_loose_object_info(const unsigned char *sha1,
 -                                struct object_info *oi)
 +                                struct object_info *oi,
 +                                int flags)
  {
 -      int status;
 -      unsigned long mapsize, size;
 +      int status = 0;
 +      unsigned long mapsize;
        void *map;
        git_zstream stream;
        char hdr[32];
 +      struct strbuf hdrbuf = STRBUF_INIT;
  
        if (oi->delta_base_sha1)
                hashclr(oi->delta_base_sha1);
         * return value implicitly indicates whether the
         * object even exists.
         */
 -      if (!oi->typep && !oi->sizep) {
 +      if (!oi->typep && !oi->typename && !oi->sizep) {
                struct stat st;
                if (stat_sha1_file(sha1, &st) < 0)
                        return -1;
                return -1;
        if (oi->disk_sizep)
                *oi->disk_sizep = mapsize;
 -      if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0)
 +      if ((flags & LOOKUP_UNKNOWN_OBJECT)) {
 +              if (unpack_sha1_header_to_strbuf(&stream, map, mapsize, hdr, sizeof(hdr), &hdrbuf) < 0)
 +                      status = error("unable to unpack %s header with --allow-unknown-type",
 +                                     sha1_to_hex(sha1));
 +      } else if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0)
                status = error("unable to unpack %s header",
                               sha1_to_hex(sha1));
 -      else if ((status = parse_sha1_header(hdr, &size)) < 0)
 +      if (status < 0)
 +              ; /* Do nothing */
 +      else if (hdrbuf.len) {
 +              if ((status = parse_sha1_header_extended(hdrbuf.buf, oi, flags)) < 0)
 +                      status = error("unable to parse %s header with --allow-unknown-type",
 +                                     sha1_to_hex(sha1));
 +      } else if ((status = parse_sha1_header_extended(hdr, oi, flags)) < 0)
                status = error("unable to parse %s header", sha1_to_hex(sha1));
 -      else if (oi->sizep)
 -              *oi->sizep = size;
        git_inflate_end(&stream);
        munmap(map, mapsize);
 -      if (oi->typep)
 +      if (status && oi->typep)
                *oi->typep = status;
 +      strbuf_release(&hdrbuf);
        return 0;
  }
  
@@@ -2660,7 -2586,6 +2657,7 @@@ int sha1_object_info_extended(const uns
        struct cached_object *co;
        struct pack_entry e;
        int rtype;
 +      enum object_type real_type;
        const unsigned char *real = lookup_replace_object_extended(sha1, flags);
  
        co = find_cached_object(real);
                        *(oi->disk_sizep) = 0;
                if (oi->delta_base_sha1)
                        hashclr(oi->delta_base_sha1);
 +              if (oi->typename)
 +                      strbuf_addstr(oi->typename, typename(co->type));
                oi->whence = OI_CACHED;
                return 0;
        }
  
        if (!find_pack_entry(real, &e)) {
                /* Most likely it's a loose object. */
 -              if (!sha1_loose_object_info(real, oi)) {
 +              if (!sha1_loose_object_info(real, oi, flags)) {
                        oi->whence = OI_LOOSE;
                        return 0;
                }
                        return -1;
        }
  
 +      /*
 +       * packed_object_info() does not follow the delta chain to
 +       * find out the real type, unless it is given oi->typep.
 +       */
 +      if (oi->typename && !oi->typep)
 +              oi->typep = &real_type;
 +
        rtype = packed_object_info(e.p, e.offset, oi);
        if (rtype < 0) {
                mark_bad_packed_object(e.p, real);
 +              if (oi->typep == &real_type)
 +                      oi->typep = NULL;
                return sha1_object_info_extended(real, oi, 0);
        } else if (in_delta_base_cache(e.p, e.offset)) {
                oi->whence = OI_DBCACHED;
                oi->u.packed.is_delta = (rtype == OBJ_REF_DELTA ||
                                         rtype == OBJ_OFS_DELTA);
        }
 +      if (oi->typename)
 +              strbuf_addstr(oi->typename, typename(*oi->typep));
 +      if (oi->typep == &real_type)
 +              oi->typep = NULL;
  
        return 0;
  }
@@@ -3586,19 -3496,14 +3583,19 @@@ int for_each_packed_object(each_packed_
  {
        struct packed_git *p;
        int r = 0;
 +      int pack_errors = 0;
  
        prepare_packed_git();
        for (p = packed_git; p; p = p->next) {
                if ((flags & FOR_EACH_OBJECT_LOCAL_ONLY) && !p->pack_local)
                        continue;
 +              if (open_pack_index(p)) {
 +                      pack_errors = 1;
 +                      continue;
 +              }
                r = for_each_object_in_pack(p, cb, data);
                if (r)
                        break;
        }
 -      return r;
 +      return r ? r : pack_errors;
  }
diff --combined unpack-trees.c
index d6cf84904f189a284b4bd1d72c09ee975207700e,17a5eeb6cd0e211c8c5215ff1ebec912739f5d21..6e9f75549dde32781d6a2b032ab9edb1b7a5e079
@@@ -9,7 -9,6 +9,7 @@@
  #include "refs.h"
  #include "attr.h"
  #include "split-index.h"
 +#include "dir.h"
  
  /*
   * Error messages expected by scripts out of plumbing commands such as
@@@ -224,9 -223,6 +224,9 @@@ static int check_updates(struct unpack_
                struct cache_entry *ce = index->cache[i];
  
                if (ce->ce_flags & CE_UPDATE) {
 +                      if (ce->ce_flags & CE_WT_REMOVE)
 +                              die("BUG: both update and delete flags are set on %s",
 +                                  ce->name);
                        display_progress(progress, ++cnt);
                        ce->ce_flags &= ~CE_UPDATE;
                        if (o->update && !o->dry_run) {
@@@ -296,7 -292,6 +296,7 @@@ static int apply_sparse_checkout(struc
                if (!(ce->ce_flags & CE_UPDATE) && verify_uptodate_sparse(ce, o))
                        return -1;
                ce->ce_flags |= CE_WT_REMOVE;
 +              ce->ce_flags &= ~CE_UPDATE;
        }
        if (was_skip_worktree && !ce_skip_worktree(ce)) {
                if (verify_absent_sparse(ce, ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o))
@@@ -1264,10 -1259,8 +1264,10 @@@ static int verify_uptodate_sparse(cons
  static void invalidate_ce_path(const struct cache_entry *ce,
                               struct unpack_trees_options *o)
  {
 -      if (ce)
 -              cache_tree_invalidate_path(o->src_index, ce->name);
 +      if (!ce)
 +              return;
 +      cache_tree_invalidate_path(o->src_index, ce->name);
 +      untracked_cache_invalidate_path(o->src_index, ce->name);
  }
  
  /*
@@@ -1441,15 -1434,18 +1441,18 @@@ static int verify_absent_1(const struc
        if (!len)
                return 0;
        else if (len > 0) {
-               char path[PATH_MAX + 1];
-               memcpy(path, ce->name, len);
-               path[len] = 0;
+               char *path;
+               int ret;
+               path = xmemdupz(ce->name, len);
                if (lstat(path, &st))
-                       return error("cannot stat '%s': %s", path,
+                       ret = error("cannot stat '%s': %s", path,
                                        strerror(errno));
-               return check_ok_to_remove(path, len, DT_UNKNOWN, NULL, &st,
-                               error_type, o);
+               else
+                       ret = check_ok_to_remove(path, len, DT_UNKNOWN, NULL,
+                                                &st, error_type, o);
+               free(path);
+               return ret;
        } else if (lstat(ce->name, &st)) {
                if (errno != ENOENT)
                        return error("cannot stat '%s': %s", ce->name,