Merge branch 'nd/clone-case-smashing-warning'
authorJunio C Hamano <gitster@pobox.com>
Mon, 17 Sep 2018 20:53:47 +0000 (13:53 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 17 Sep 2018 20:53:47 +0000 (13:53 -0700)
Running "git clone" against a project that contain two files with
pathnames that differ only in cases on a case insensitive
filesystem would result in one of the files lost because the
underlying filesystem is incapable of holding both at the same
time. An attempt is made to detect such a case and warn.

* nd/clone-case-smashing-warning:
clone: report duplicate entries on case-insensitive filesystems

1  2 
builtin/clone.c
cache.h
entry.c
t/t5601-clone.sh
unpack-trees.c
unpack-trees.h
diff --combined builtin/clone.c
index fd2c3ef090146058651af5e411dd304153ee114d,0702b0e9d025bc9f353b6b7673a106be1f604124..15b142d64640e29c10e62d565ac21adbaaeebca4
@@@ -696,8 -696,7 +696,8 @@@ static void update_head(const struct re
                        install_branch_config(0, head, option_origin, our->name);
                }
        } else if (our) {
 -              struct commit *c = lookup_commit_reference(&our->old_oid);
 +              struct commit *c = lookup_commit_reference(the_repository,
 +                                                         &our->old_oid);
                /* --branch specifies a non-branch (i.e. tags), detach HEAD */
                update_ref(msg, "HEAD", &c->object.oid, NULL, REF_NO_DEREF,
                           UPDATE_REFS_DIE_ON_ERR);
@@@ -748,6 -747,7 +748,7 @@@ static int checkout(int submodule_progr
        memset(&opts, 0, sizeof opts);
        opts.update = 1;
        opts.merge = 1;
+       opts.clone = 1;
        opts.fn = oneway_merge;
        opts.verbose_update = (option_verbosity >= 0);
        opts.src_index = &the_index;
@@@ -897,8 -897,7 +898,8 @@@ int cmd_clone(int argc, const char **ar
        int err = 0, complete_refs_before_fetch = 1;
        int submodule_progress;
  
 -      struct refspec_item refspec;
 +      struct refspec rs = REFSPEC_INIT_FETCH;
 +      struct argv_array ref_prefixes = ARGV_ARRAY_INIT;
  
        fetch_if_missing = 0;
  
        if (option_required_reference.nr || option_optional_reference.nr)
                setup_reference();
  
 -      refspec_item_init_or_die(&refspec, value.buf, REFSPEC_FETCH);
 +      refspec_append(&rs, value.buf);
  
        strbuf_reset(&value);
  
        if (transport->smart_options && !deepen && !filter_options.choice)
                transport->smart_options->check_self_contained_and_connected = 1;
  
 -      refs = transport_get_remote_refs(transport, NULL);
 +
 +      argv_array_push(&ref_prefixes, "HEAD");
 +      refspec_ref_prefixes(&rs, &ref_prefixes);
 +      if (option_branch)
 +              expand_ref_prefix(&ref_prefixes, option_branch);
 +      if (!option_no_tags)
 +              argv_array_push(&ref_prefixes, "refs/tags/");
 +
 +      refs = transport_get_remote_refs(transport, &ref_prefixes);
  
        if (refs) {
 -              mapped_refs = wanted_peer_refs(refs, &refspec);
 +              mapped_refs = wanted_peer_refs(refs, &rs.items[0]);
                /*
                 * transport_get_remote_refs() may return refs with null sha-1
                 * in mapped_refs (see struct transport->get_refs_list
                        }
  
                if (!is_local && !complete_refs_before_fetch)
 -                      transport_fetch_refs(transport, mapped_refs, NULL);
 +                      transport_fetch_refs(transport, mapped_refs);
  
                remote_head = find_ref_by_name(refs, "HEAD");
                remote_head_points_at =
        if (is_local)
                clone_local(path, git_dir);
        else if (refs && complete_refs_before_fetch)
 -              transport_fetch_refs(transport, mapped_refs, NULL);
 +              transport_fetch_refs(transport, mapped_refs);
  
        update_remote_refs(refs, mapped_refs, remote_head_points_at,
                           branch_top.buf, reflog_msg.buf, transport,
        strbuf_release(&value);
        junk_mode = JUNK_LEAVE_ALL;
  
 -      refspec_item_clear(&refspec);
 +      refspec_clear(&rs);
 +      argv_array_clear(&ref_prefixes);
        return err;
  }
diff --combined cache.h
index 4d014541ab7bc7692919c871a5306543bbf361c5,6d6138f4f1cb8ef4e2e9cb3e22502f9f8c01c940..b7166e45ed8c20c627dc741e4b66fa4201c8ec53
+++ b/cache.h
@@@ -15,7 -15,6 +15,7 @@@
  #include "path.h"
  #include "sha1-array.h"
  #include "repository.h"
 +#include "mem-pool.h"
  
  #include <zlib.h>
  typedef struct git_zstream {
@@@ -157,7 -156,6 +157,7 @@@ struct cache_entry 
        struct stat_data ce_stat_data;
        unsigned int ce_mode;
        unsigned int ce_flags;
 +      unsigned int mem_pool_allocated;
        unsigned int ce_namelen;
        unsigned int index;     /* for link extension */
        struct object_id oid;
  /* Forward structure decls */
  struct pathspec;
  struct child_process;
 +struct tree;
  
  /*
   * Copy the sha1 and stat state of a cache entry from one to
@@@ -230,7 -227,6 +230,7 @@@ static inline void copy_cache_entry(str
                                    const struct cache_entry *src)
  {
        unsigned int state = dst->ce_flags & CE_HASHED;
 +      int mem_pool_allocated = dst->mem_pool_allocated;
  
        /* Don't copy hash chain and name */
        memcpy(&dst->ce_stat_data, &src->ce_stat_data,
  
        /* Restore the hash state */
        dst->ce_flags = (dst->ce_flags & ~CE_HASHED) | state;
 +
 +      /* Restore the mem_pool_allocated flag */
 +      dst->mem_pool_allocated = mem_pool_allocated;
  }
  
  static inline unsigned create_ce_flags(unsigned stage)
@@@ -335,7 -328,6 +335,7 @@@ struct index_state 
        struct untracked_cache *untracked;
        uint64_t fsmonitor_last_update;
        struct ewah_bitmap *fsmonitor_dirty;
 +      struct mem_pool *ce_mem_pool;
  };
  
  extern struct index_state the_index;
@@@ -347,60 -339,6 +347,60 @@@ extern void remove_name_hash(struct ind
  extern void free_name_hash(struct index_state *istate);
  
  
 +/* Cache entry creation and cleanup */
 +
 +/*
 + * Create cache_entry intended for use in the specified index. Caller
 + * is responsible for discarding the cache_entry with
 + * `discard_cache_entry`.
 + */
 +struct cache_entry *make_cache_entry(struct index_state *istate,
 +                                   unsigned int mode,
 +                                   const struct object_id *oid,
 +                                   const char *path,
 +                                   int stage,
 +                                   unsigned int refresh_options);
 +
 +struct cache_entry *make_empty_cache_entry(struct index_state *istate,
 +                                         size_t name_len);
 +
 +/*
 + * Create a cache_entry that is not intended to be added to an index.
 + * Caller is responsible for discarding the cache_entry
 + * with `discard_cache_entry`.
 + */
 +struct cache_entry *make_transient_cache_entry(unsigned int mode,
 +                                             const struct object_id *oid,
 +                                             const char *path,
 +                                             int stage);
 +
 +struct cache_entry *make_empty_transient_cache_entry(size_t name_len);
 +
 +/*
 + * Discard cache entry.
 + */
 +void discard_cache_entry(struct cache_entry *ce);
 +
 +/*
 + * Check configuration if we should perform extra validation on cache
 + * entries.
 + */
 +int should_validate_cache_entries(void);
 +
 +/*
 + * Duplicate a cache_entry. Allocate memory for the new entry from a
 + * memory_pool. Takes into account cache_entry fields that are meant
 + * for managing the underlying memory allocation of the cache_entry.
 + */
 +struct cache_entry *dup_cache_entry(const struct cache_entry *ce, struct index_state *istate);
 +
 +/*
 + * Validate the cache entries in the index.  This is an internal
 + * consistency check that the cache_entry structs are allocated from
 + * the expected memory pool.
 + */
 +void validate_cache_entries(const struct index_state *istate);
 +
  #ifndef NO_THE_INDEX_COMPATIBILITY_MACROS
  #define active_cache (the_index.cache)
  #define active_nr (the_index.cache_nr)
@@@ -697,15 -635,12 +697,15 @@@ extern void move_index_extensions(struc
  extern int unmerged_index(const struct index_state *);
  
  /**
 - * Returns 1 if the index differs from HEAD, 0 otherwise. When on an unborn
 - * branch, returns 1 if there are entries in the index, 0 otherwise. If an
 - * strbuf is provided, the space-separated list of files that differ will be
 - * appended to it.
 + * Returns 1 if istate differs from tree, 0 otherwise.  If tree is NULL,
 + * compares istate to HEAD.  If tree is NULL and on an unborn branch,
 + * returns 1 if there are entries in istate, 0 otherwise.  If an strbuf is
 + * provided, the space-separated list of files that differ will be appended
 + * to it.
   */
 -extern int index_has_changes(struct strbuf *sb);
 +extern int index_has_changes(const struct index_state *istate,
 +                           struct tree *tree,
 +                           struct strbuf *sb);
  
  extern int verify_path(const char *path, unsigned mode);
  extern int strcmp_offset(const char *s1, const char *s2, size_t *first_change);
@@@ -763,6 -698,7 +763,6 @@@ extern int remove_file_from_index(struc
  extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
  extern int add_file_to_index(struct index_state *, const char *path, int flags);
  
 -extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, unsigned int refresh_options);
  extern int chmod_index_entry(struct index_state *, struct cache_entry *ce, char flip);
  extern int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
  extern void set_object_name_for_intent_to_add_entry(struct cache_entry *ce);
@@@ -815,7 -751,7 +815,7 @@@ extern void fill_stat_cache_info(struc
  #define REFRESH_IGNORE_SUBMODULES     0x0010  /* ignore submodules */
  #define REFRESH_IN_PORCELAIN  0x0020  /* user friendly output, not "needs update" */
  extern int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
 -extern struct cache_entry *refresh_cache_entry(struct cache_entry *, unsigned int);
 +extern struct cache_entry *refresh_cache_entry(struct index_state *, struct cache_entry *, unsigned int);
  
  /*
   * Opportunistically update the index but do not complain if we can't.
@@@ -868,13 -804,16 +868,13 @@@ void reset_shared_repository(void)
   * Do replace refs need to be checked this run?  This variable is
   * initialized to true unless --no-replace-object is used or
   * $GIT_NO_REPLACE_OBJECTS is set, but is set to false by some
 - * commands that do not want replace references to be active.  As an
 - * optimization it is also set to false if replace references have
 - * been sought but there were none.
 + * commands that do not want replace references to be active.
   */
 -extern int check_replace_refs;
 +extern int read_replace_refs;
  extern char *git_replace_ref_base;
  
  extern int fsync_object_files;
  extern int core_preload_index;
 -extern int core_commit_graph;
  extern int core_apply_sparse_checkout;
  extern int precomposed_unicode;
  extern int protect_hfs;
@@@ -917,6 -856,15 +917,6 @@@ enum log_refs_config 
  };
  extern enum log_refs_config log_all_ref_updates;
  
 -enum branch_track {
 -      BRANCH_TRACK_UNSPECIFIED = -1,
 -      BRANCH_TRACK_NEVER = 0,
 -      BRANCH_TRACK_REMOTE,
 -      BRANCH_TRACK_ALWAYS,
 -      BRANCH_TRACK_EXPLICIT,
 -      BRANCH_TRACK_OVERRIDE
 -};
 -
  enum rebase_setup_type {
        AUTOREBASE_NEVER = 0,
        AUTOREBASE_LOCAL,
@@@ -933,6 -881,7 +933,6 @@@ enum push_default_type 
        PUSH_DEFAULT_UNSPECIFIED
  };
  
 -extern enum branch_track git_branch_track;
  extern enum rebase_setup_type autorebase;
  extern enum push_default_type push_default;
  
@@@ -1023,17 -972,7 +1023,17 @@@ extern const struct object_id null_oid
  
  static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)
  {
 -      return memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
 +      /*
 +       * This is a temporary optimization hack. By asserting the size here,
 +       * we let the compiler know that it's always going to be 20, which lets
 +       * it turn this fixed-size memcmp into a few inline instructions.
 +       *
 +       * This will need to be extended or ripped out when we learn about
 +       * hashes of different sizes.
 +       */
 +      if (the_hash_algo->rawsz != 20)
 +              BUG("hash size not yet supported by hashcmp");
 +      return memcmp(sha1, sha2, the_hash_algo->rawsz);
  }
  
  static inline int oidcmp(const struct object_id *oid1, const struct object_id *oid2)
@@@ -1053,7 -992,7 +1053,7 @@@ static inline int is_null_oid(const str
  
  static inline void hashcpy(unsigned char *sha_dst, const unsigned char *sha_src)
  {
 -      memcpy(sha_dst, sha_src, GIT_SHA1_RAWSZ);
 +      memcpy(sha_dst, sha_src, the_hash_algo->rawsz);
  }
  
  static inline void oidcpy(struct object_id *dst, const struct object_id *src)
@@@ -1070,7 -1009,7 +1070,7 @@@ static inline struct object_id *oiddup(
  
  static inline void hashclr(unsigned char *hash)
  {
 -      memset(hash, 0, GIT_SHA1_RAWSZ);
 +      memset(hash, 0, the_hash_algo->rawsz);
  }
  
  static inline void oidclr(struct object_id *oid)
@@@ -1423,20 -1362,18 +1423,20 @@@ extern void *read_object_with_reference
  extern struct object *peel_to_type(const char *name, int namelen,
                                   struct object *o, enum object_type);
  
 +enum date_mode_type {
 +      DATE_NORMAL = 0,
 +      DATE_RELATIVE,
 +      DATE_SHORT,
 +      DATE_ISO8601,
 +      DATE_ISO8601_STRICT,
 +      DATE_RFC2822,
 +      DATE_STRFTIME,
 +      DATE_RAW,
 +      DATE_UNIX
 +};
 +
  struct date_mode {
 -      enum date_mode_type {
 -              DATE_NORMAL = 0,
 -              DATE_RELATIVE,
 -              DATE_SHORT,
 -              DATE_ISO8601,
 -              DATE_ISO8601_STRICT,
 -              DATE_RFC2822,
 -              DATE_STRFTIME,
 -              DATE_RAW,
 -              DATE_UNIX
 -      } type;
 +      enum date_mode_type type;
        const char *strftime_fmt;
        int local;
  };
@@@ -1518,6 -1455,7 +1518,7 @@@ struct checkout 
        unsigned force:1,
                 quiet:1,
                 not_new:1,
+                clone:1,
                 refresh_cache:1;
  };
  #define CHECKOUT_INIT { NULL, "" }
@@@ -1575,6 -1513,62 +1576,6 @@@ extern int odb_mkstemp(struct strbuf *t
   */
  extern int odb_pack_keep(const char *name);
  
 -/*
 - * Iterate over the files in the loose-object parts of the object
 - * directory "path", triggering the following callbacks:
 - *
 - *  - loose_object is called for each loose object we find.
 - *
 - *  - loose_cruft is called for any files that do not appear to be
 - *    loose objects. Note that we only look in the loose object
 - *    directories "objects/[0-9a-f]{2}/", so we will not report
 - *    "objects/foobar" as cruft.
 - *
 - *  - loose_subdir is called for each top-level hashed subdirectory
 - *    of the object directory (e.g., "$OBJDIR/f0"). It is called
 - *    after the objects in the directory are processed.
 - *
 - * Any callback that is NULL will be ignored. Callbacks returning non-zero
 - * will end the iteration.
 - *
 - * In the "buf" variant, "path" is a strbuf which will also be used as a
 - * scratch buffer, but restored to its original contents before
 - * the function returns.
 - */
 -typedef int each_loose_object_fn(const struct object_id *oid,
 -                               const char *path,
 -                               void *data);
 -typedef int each_loose_cruft_fn(const char *basename,
 -                              const char *path,
 -                              void *data);
 -typedef int each_loose_subdir_fn(unsigned int nr,
 -                               const char *path,
 -                               void *data);
 -int for_each_file_in_obj_subdir(unsigned int subdir_nr,
 -                              struct strbuf *path,
 -                              each_loose_object_fn obj_cb,
 -                              each_loose_cruft_fn cruft_cb,
 -                              each_loose_subdir_fn subdir_cb,
 -                              void *data);
 -int for_each_loose_file_in_objdir(const char *path,
 -                                each_loose_object_fn obj_cb,
 -                                each_loose_cruft_fn cruft_cb,
 -                                each_loose_subdir_fn subdir_cb,
 -                                void *data);
 -int for_each_loose_file_in_objdir_buf(struct strbuf *path,
 -                                    each_loose_object_fn obj_cb,
 -                                    each_loose_cruft_fn cruft_cb,
 -                                    each_loose_subdir_fn subdir_cb,
 -                                    void *data);
 -
 -/*
 - * Iterate over loose objects in both the local
 - * repository and any alternates repositories (unless the
 - * LOCAL_ONLY flag is set).
 - */
 -#define FOR_EACH_OBJECT_LOCAL_ONLY 0x1
 -extern int for_each_loose_object(each_loose_object_fn, void *, unsigned flags);
 -
  /*
   * Set this to 0 to prevent sha1_object_info_extended() from fetching missing
   * blobs. This has a difference only if extensions.partialClone is set.
diff --combined entry.c
index 2a2ab6c839490aba1b9d2723b8e05837855f5b39,8766e2725564b6fc12c28f57e641a507b43735b9..5d136c5d55e0811b70fdb10f59ea506ceb1b273b
+++ b/entry.c
@@@ -266,7 -266,7 +266,7 @@@ static int write_entry(struct cache_ent
        const struct submodule *sub;
  
        if (ce_mode_s_ifmt == S_IFREG) {
 -              struct stream_filter *filter = get_stream_filter(ce->name,
 +              struct stream_filter *filter = get_stream_filter(state->istate, ce->name,
                                                                 &ce->oid);
                if (filter &&
                    !streaming_write_entry(ce, path, filter,
                 * Convert from git internal format to working tree format
                 */
                if (dco && dco->state != CE_NO_DELAY) {
 -                      ret = async_convert_to_working_tree(ce->name, new_blob,
 +                      ret = async_convert_to_working_tree(state->istate, ce->name, new_blob,
                                                            size, &buf, dco);
                        if (ret && string_list_has_string(&dco->paths, ce->name)) {
                                free(new_blob);
                                goto delayed;
                        }
                } else
 -                      ret = convert_to_working_tree(ce->name, new_blob, size, &buf);
 +                      ret = convert_to_working_tree(state->istate, ce->name, new_blob, size, &buf);
  
                if (ret) {
                        free(new_blob);
@@@ -399,6 -399,34 +399,34 @@@ static int check_path(const char *path
        return lstat(path, st);
  }
  
+ static void mark_colliding_entries(const struct checkout *state,
+                                  struct cache_entry *ce, struct stat *st)
+ {
+       int i, trust_ino = check_stat;
+ #if defined(GIT_WINDOWS_NATIVE)
+       trust_ino = 0;
+ #endif
+       ce->ce_flags |= CE_MATCHED;
+       for (i = 0; i < state->istate->cache_nr; i++) {
+               struct cache_entry *dup = state->istate->cache[i];
+               if (dup == ce)
+                       break;
+               if (dup->ce_flags & (CE_MATCHED | CE_VALID | CE_SKIP_WORKTREE))
+                       continue;
+               if ((trust_ino && dup->ce_stat_data.sd_ino == st->st_ino) ||
+                   (!trust_ino && !fspathcmp(ce->name, dup->name))) {
+                       dup->ce_flags |= CE_MATCHED;
+                       break;
+               }
+       }
+ }
  /*
   * Write the contents from ce out to the working tree.
   *
@@@ -422,8 -450,7 +450,8 @@@ int checkout_entry(struct cache_entry *
  
        if (!check_path(path.buf, path.len, &st, state->base_dir_len)) {
                const struct submodule *sub;
 -              unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);
 +              unsigned changed = ie_match_stat(state->istate, ce, &st,
 +                                               CE_MATCH_IGNORE_VALID | CE_MATCH_IGNORE_SKIP_WORKTREE);
                /*
                 * Needs to be checked before !changed returns early,
                 * as the possibly empty directory was not changed
                        return -1;
                }
  
+               if (state->clone)
+                       mark_colliding_entries(state, ce, &st);
                /*
                 * We unlink the old file, to get the new one with the
                 * right permissions (including umask, which is nasty
diff --combined t/t5601-clone.sh
index ddaa96ac4f44a4e4799aa509b3bd69bc28628d60,f2eb73bc74ae70e3e3515c7346f4f39659c86928..f1a49e94f5fe56a0c175b0e5354ce1d5300cb382
@@@ -618,16 -618,22 +618,22 @@@ hex2oct () 
  test_expect_success 'clone on case-insensitive fs' '
        git init icasefs &&
        (
 -              cd icasefs
 +              cd icasefs &&
                o=$(git hash-object -w --stdin </dev/null | hex2oct) &&
                t=$(printf "100644 X\0${o}100644 x\0${o}" |
                        git hash-object -w -t tree --stdin) &&
                c=$(git commit-tree -m bogus $t) &&
                git update-ref refs/heads/bogus $c &&
-               git clone -b bogus . bogus
+               git clone -b bogus . bogus 2>warning
        )
  '
  
+ test_expect_success !MINGW,!CYGWIN,CASE_INSENSITIVE_FS 'colliding file detection' '
+       grep X icasefs/warning &&
+       grep x icasefs/warning &&
+       test_i18ngrep "the following paths have collided" icasefs/warning
+ '
  partial_clone () {
               SERVER="$1" &&
               URL="$2" &&
diff --combined unpack-trees.c
index f25089b878a8b0842a9d6407cb6b1821867a737c,213da8bbb4f90c5e1cbf572bd8640c6093d22e28..cfa88bb6ec8238a6a6a41d32d646610fd2699048
@@@ -204,11 -204,20 +204,11 @@@ static int do_add_entry(struct unpack_t
                               ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
  }
  
 -static struct cache_entry *dup_entry(const struct cache_entry *ce)
 -{
 -      unsigned int size = ce_size(ce);
 -      struct cache_entry *new_entry = xmalloc(size);
 -
 -      memcpy(new_entry, ce, size);
 -      return new_entry;
 -}
 -
  static void add_entry(struct unpack_trees_options *o,
                      const struct cache_entry *ce,
                      unsigned int set, unsigned int clear)
  {
 -      do_add_entry(o, dup_entry(ce), set, clear);
 +      do_add_entry(o, dup_cache_entry(ce, &o->result), set, clear);
  }
  
  /*
@@@ -336,6 -345,46 +336,46 @@@ static struct progress *get_progress(st
        return start_delayed_progress(_("Checking out files"), total);
  }
  
+ static void setup_collided_checkout_detection(struct checkout *state,
+                                             struct index_state *index)
+ {
+       int i;
+       state->clone = 1;
+       for (i = 0; i < index->cache_nr; i++)
+               index->cache[i]->ce_flags &= ~CE_MATCHED;
+ }
+ static void report_collided_checkout(struct index_state *index)
+ {
+       struct string_list list = STRING_LIST_INIT_NODUP;
+       int i;
+       for (i = 0; i < index->cache_nr; i++) {
+               struct cache_entry *ce = index->cache[i];
+               if (!(ce->ce_flags & CE_MATCHED))
+                       continue;
+               string_list_append(&list, ce->name);
+               ce->ce_flags &= ~CE_MATCHED;
+       }
+       list.cmp = fspathcmp;
+       string_list_sort(&list);
+       if (list.nr) {
+               warning(_("the following paths have collided (e.g. case-sensitive paths\n"
+                         "on a case-insensitive filesystem) and only one from the same\n"
+                         "colliding group is in the working tree:\n"));
+               for (i = 0; i < list.nr; i++)
+                       fprintf(stderr, "  '%s'\n", list.items[i].string);
+       }
+       string_list_clear(&list, 0);
+ }
  static int check_updates(struct unpack_trees_options *o)
  {
        unsigned cnt = 0;
        state.refresh_cache = 1;
        state.istate = index;
  
+       if (o->clone)
+               setup_collided_checkout_detection(&state, index);
        progress = get_progress(o);
  
        if (o->update)
 -              git_attr_set_direction(GIT_ATTR_CHECKOUT, index);
 +              git_attr_set_direction(GIT_ATTR_CHECKOUT);
  
        if (should_update_submodules() && o->update && !o->dry_run)
                load_gitmodules_file(index, NULL);
        stop_progress(&progress);
        errs |= finish_delayed_checkout(&state);
        if (o->update)
 -              git_attr_set_direction(GIT_ATTR_CHECKIN, NULL);
 +              git_attr_set_direction(GIT_ATTR_CHECKIN);
+       if (o->clone)
+               report_collided_checkout(index);
        return errs != 0;
  }
  
@@@ -789,17 -845,10 +836,17 @@@ static int ce_in_traverse_path(const st
        return (info->pathlen < ce_namelen(ce));
  }
  
 -static struct cache_entry *create_ce_entry(const struct traverse_info *info, const struct name_entry *n, int stage)
 +static struct cache_entry *create_ce_entry(const struct traverse_info *info,
 +      const struct name_entry *n,
 +      int stage,
 +      struct index_state *istate,
 +      int is_transient)
  {
        int len = traverse_path_len(info, n);
 -      struct cache_entry *ce = xcalloc(1, cache_entry_size(len));
 +      struct cache_entry *ce =
 +              is_transient ?
 +              make_empty_transient_cache_entry(len) :
 +              make_empty_cache_entry(istate, len);
  
        ce->ce_mode = create_ce_mode(n->mode);
        ce->ce_flags = create_ce_flags(stage);
@@@ -845,15 -894,7 +892,15 @@@ static int unpack_nondirectories(int n
                        stage = 3;
                else
                        stage = 2;
 -              src[i + o->merge] = create_ce_entry(info, names + i, stage);
 +
 +              /*
 +               * If the merge bit is set, then the cache entries are
 +               * discarded in the following block.  In this case,
 +               * construct "transient" cache_entries, as they are
 +               * not stored in the index.  otherwise construct the
 +               * cache entry from the index aware logic.
 +               */
 +              src[i + o->merge] = create_ce_entry(info, names + i, stage, &o->result, o->merge);
        }
  
        if (o->merge) {
                for (i = 0; i < n; i++) {
                        struct cache_entry *ce = src[i + o->merge];
                        if (ce != o->df_conflict_entry)
 -                              free(ce);
 +                              discard_cache_entry(ce);
                }
                return rc;
        }
@@@ -1092,15 -1133,13 +1139,15 @@@ static int unpack_callback(int n, unsig
        return mask;
  }
  
 -static int clear_ce_flags_1(struct cache_entry **cache, int nr,
 +static int clear_ce_flags_1(struct index_state *istate,
 +                          struct cache_entry **cache, int nr,
                            struct strbuf *prefix,
                            int select_mask, int clear_mask,
                            struct exclude_list *el, int defval);
  
  /* Whole directory matching */
 -static int clear_ce_flags_dir(struct cache_entry **cache, int nr,
 +static int clear_ce_flags_dir(struct index_state *istate,
 +                            struct cache_entry **cache, int nr,
                              struct strbuf *prefix,
                              char *basename,
                              int select_mask, int clear_mask,
        struct cache_entry **cache_end;
        int dtype = DT_DIR;
        int ret = is_excluded_from_list(prefix->buf, prefix->len,
 -                                      basename, &dtype, el, &the_index);
 +                                      basename, &dtype, el, istate);
        int rc;
  
        strbuf_addch(prefix, '/');
         * calling clear_ce_flags_1(). That function will call
         * the expensive is_excluded_from_list() on every entry.
         */
 -      rc = clear_ce_flags_1(cache, cache_end - cache,
 +      rc = clear_ce_flags_1(istate, cache, cache_end - cache,
                              prefix,
                              select_mask, clear_mask,
                              el, ret);
   *   cache[0]->name[0..(prefix_len-1)]
   * Top level path has prefix_len zero.
   */
 -static int clear_ce_flags_1(struct cache_entry **cache, int nr,
 +static int clear_ce_flags_1(struct index_state *istate,
 +                          struct cache_entry **cache, int nr,
                            struct strbuf *prefix,
                            int select_mask, int clear_mask,
                            struct exclude_list *el, int defval)
                        len = slash - name;
                        strbuf_add(prefix, name, len);
  
 -                      processed = clear_ce_flags_dir(cache, cache_end - cache,
 +                      processed = clear_ce_flags_dir(istate, cache, cache_end - cache,
                                                       prefix,
                                                       prefix->buf + prefix->len - len,
                                                       select_mask, clear_mask,
                        }
  
                        strbuf_addch(prefix, '/');
 -                      cache += clear_ce_flags_1(cache, cache_end - cache,
 +                      cache += clear_ce_flags_1(istate, cache, cache_end - cache,
                                                  prefix,
                                                  select_mask, clear_mask, el, defval);
                        strbuf_setlen(prefix, prefix->len - len - 1);
                /* Non-directory */
                dtype = ce_to_dtype(ce);
                ret = is_excluded_from_list(ce->name, ce_namelen(ce),
 -                                          name, &dtype, el, &the_index);
 +                                          name, &dtype, el, istate);
                if (ret < 0)
                        ret = defval;
                if (ret > 0)
        return nr - (cache_end - cache);
  }
  
 -static int clear_ce_flags(struct cache_entry **cache, int nr,
 -                          int select_mask, int clear_mask,
 -                          struct exclude_list *el)
 +static int clear_ce_flags(struct index_state *istate,
 +                        int select_mask, int clear_mask,
 +                        struct exclude_list *el)
  {
        static struct strbuf prefix = STRBUF_INIT;
  
        strbuf_reset(&prefix);
  
 -      return clear_ce_flags_1(cache, nr,
 +      return clear_ce_flags_1(istate,
 +                              istate->cache,
 +                              istate->cache_nr,
                                &prefix,
                                select_mask, clear_mask,
                                el, 0);
   * Set/Clear CE_NEW_SKIP_WORKTREE according to $GIT_DIR/info/sparse-checkout
   */
  static void mark_new_skip_worktree(struct exclude_list *el,
 -                                 struct index_state *the_index,
 +                                 struct index_state *istate,
                                   int select_flag, int skip_wt_flag)
  {
        int i;
         * 1. Pretend the narrowest worktree: only unmerged entries
         * are checked out
         */
 -      for (i = 0; i < the_index->cache_nr; i++) {
 -              struct cache_entry *ce = the_index->cache[i];
 +      for (i = 0; i < istate->cache_nr; i++) {
 +              struct cache_entry *ce = istate->cache[i];
  
                if (select_flag && !(ce->ce_flags & select_flag))
                        continue;
         * 2. Widen worktree according to sparse-checkout file.
         * Matched entries will have skip_wt_flag cleared (i.e. "in")
         */
 -      clear_ce_flags(the_index->cache, the_index->cache_nr,
 -                     select_flag, skip_wt_flag, el);
 +      clear_ce_flags(istate, select_flag, skip_wt_flag, el);
  }
  
  static int verify_absent(const struct cache_entry *,
@@@ -1556,17 -1593,6 +1603,17 @@@ static int verify_uptodate_sparse(cons
        return verify_uptodate_1(ce, o, ERROR_SPARSE_NOT_UPTODATE_FILE);
  }
  
 +/*
 + * TODO: We should actually invalidate o->result, not src_index [1].
 + * But since cache tree and untracked cache both are not copied to
 + * o->result until unpacking is complete, we invalidate them on
 + * src_index instead with the assumption that they will be copied to
 + * dst_index at the end.
 + *
 + * [1] src_index->cache_tree is also used in unpack_callback() so if
 + * we invalidate o->result, we need to update it to use
 + * o->result.cache_tree as well.
 + */
  static void invalidate_ce_path(const struct cache_entry *ce,
                               struct unpack_trees_options *o)
  {
@@@ -1658,7 -1684,7 +1705,7 @@@ static int verify_clean_subdirectory(co
        memset(&d, 0, sizeof(d));
        if (o->dir)
                d.exclude_per_dir = o->dir->exclude_per_dir;
 -      i = read_directory(&d, &the_index, pathbuf, namelen+1, NULL);
 +      i = read_directory(&d, o->src_index, pathbuf, namelen+1, NULL);
        if (i)
                return o->gently ? -1 :
                        add_rejected_path(o, ERROR_NOT_UPTODATE_DIR, ce->name);
@@@ -1700,7 -1726,7 +1747,7 @@@ static int check_ok_to_remove(const cha
                return 0;
  
        if (o->dir &&
 -          is_excluded(o->dir, &the_index, name, &dtype))
 +          is_excluded(o->dir, o->src_index, name, &dtype))
                /*
                 * ce->name is explicitly excluded, so it is Ok to
                 * overwrite it.
@@@ -1809,7 -1835,7 +1856,7 @@@ static int merged_entry(const struct ca
                        struct unpack_trees_options *o)
  {
        int update = CE_UPDATE;
 -      struct cache_entry *merge = dup_entry(ce);
 +      struct cache_entry *merge = dup_cache_entry(ce, &o->result);
  
        if (!old) {
                /*
  
                if (verify_absent(merge,
                                  ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o)) {
 -                      free(merge);
 +                      discard_cache_entry(merge);
                        return -1;
                }
                invalidate_ce_path(merge, o);
                        update = 0;
                } else {
                        if (verify_uptodate(old, o)) {
 -                              free(merge);
 +                              discard_cache_entry(merge);
                                return -1;
                        }
                        /* Migrate old flags over */
diff --combined unpack-trees.h
index 847f217dbaecea678f48d5086e7099c3c24a53c4,d940f1c5c287a7232e18628c6a7e80e74c73b544..0135080a7b4e91713b66f68669e57a2f5e4edbdd
@@@ -1,14 -1,11 +1,14 @@@
  #ifndef UNPACK_TREES_H
  #define UNPACK_TREES_H
  
 -#include "tree-walk.h"
 +#include "cache.h"
  #include "argv-array.h"
 +#include "string-list.h"
 +#include "tree-walk.h"
  
  #define MAX_UNPACK_TREES 8
  
 +struct cache_entry;
  struct unpack_trees_options;
  struct exclude_list;
  
@@@ -45,6 -42,7 +45,7 @@@ struct unpack_trees_options 
        unsigned int reset,
                     merge,
                     update,
+                    clone,
                     index_only,
                     nontrivial_merge,
                     trivial_merges_only,
@@@ -85,8 -83,8 +86,8 @@@
        struct exclude_list *el; /* for internal use */
  };
  
 -extern int unpack_trees(unsigned n, struct tree_desc *t,
 -              struct unpack_trees_options *options);
 +int unpack_trees(unsigned n, struct tree_desc *t,
 +               struct unpack_trees_options *options);
  
  int verify_uptodate(const struct cache_entry *ce,
                    struct unpack_trees_options *o);