Code

Merge branch 'cc/reset-more'
authorJunio C Hamano <gitster@pobox.com>
Wed, 13 Jan 2010 19:58:56 +0000 (11:58 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 13 Jan 2010 19:58:56 +0000 (11:58 -0800)
* cc/reset-more:
  t7111: check that reset options work as described in the tables
  Documentation: reset: add some missing tables
  Fix bit assignment for CE_CONFLICTED
  "reset --merge": fix unmerged case
  reset: use "unpack_trees()" directly instead of "git read-tree"
  reset: add a few tests for "git reset --merge"
  Documentation: reset: add some tables to describe the different options
  reset: improve mixed reset error message when in a bare repo

1  2 
Documentation/git-reset.txt
builtin-reset.c
cache.h
read-cache.c
unpack-trees.c

index 9df6de2e7dcdfa8f6629e13d5f5768787fccff66,c13718357448cbf5c7d9c8d806d9ec9ab44d7a74..c7aa444317855b5b1c00c15284a16fdd4c4cb3ad
@@@ -62,12 -62,100 +62,101 @@@ This means that `git reset -p` is the o
  linkgit:git-add[1]).
  
  -q::
 +--quiet::
        Be quiet, only report errors.
  
  <commit>::
        Commit to make the current HEAD. If not given defaults to HEAD.
  
+ DISCUSSION
+ ----------
+ The tables below show what happens when running:
+ ----------
+ git reset --option target
+ ----------
+ to reset the HEAD to another commit (`target`) with the different
+ reset options depending on the state of the files.
+ In these tables, A, B, C and D are some different states of a
+ file. For example, the first line of the first table means that if a
+ file is in state A in the working tree, in state B in the index, in
+ state C in HEAD and in state D in the target, then "git reset --soft
+ target" will put the file in state A in the working tree, in state B
+ in the index and in state D in HEAD.
+       working index HEAD target         working index HEAD
+       ----------------------------------------------------
+        A       B     C    D     --soft   A       B     D
+                               --mixed  A       D     D
+                               --hard   D       D     D
+                               --merge (disallowed)
+       working index HEAD target         working index HEAD
+       ----------------------------------------------------
+        A       B     C    C     --soft   A       B     C
+                               --mixed  A       C     C
+                               --hard   C       C     C
+                               --merge (disallowed)
+       working index HEAD target         working index HEAD
+       ----------------------------------------------------
+        B       B     C    D     --soft   B       B     D
+                               --mixed  B       D     D
+                               --hard   D       D     D
+                               --merge  D       D     D
+       working index HEAD target         working index HEAD
+       ----------------------------------------------------
+        B       B     C    C     --soft   B       B     C
+                               --mixed  B       C     C
+                               --hard   C       C     C
+                               --merge  C       C     C
+       working index HEAD target         working index HEAD
+       ----------------------------------------------------
+        B       C     C    D     --soft   B       C     D
+                               --mixed  B       D     D
+                               --hard   D       D     D
+                               --merge (disallowed)
+       working index HEAD target         working index HEAD
+       ----------------------------------------------------
+        B       C     C    C     --soft   B       C     C
+                               --mixed  B       C     C
+                               --hard   C       C     C
+                               --merge  B       C     C
+ "reset --merge" is meant to be used when resetting out of a conflicted
+ merge. Any mergy operation guarantees that the work tree file that is
+ involved in the merge does not have local change wrt the index before
+ it starts, and that it writes the result out to the work tree. So if
+ we see some difference between the index and the target and also
+ between the index and the work tree, then it means that we are not
+ resetting out from a state that a mergy operation left after failing
+ with a conflict. That is why we disallow --merge option in this case.
+ The following tables show what happens when there are unmerged
+ entries:
+       working index HEAD target         working index HEAD
+       ----------------------------------------------------
+        X       U     A    B     --soft  (disallowed)
+                               --mixed  X       B     B
+                               --hard   B       B     B
+                               --merge  B       B     B
+       working index HEAD target         working index HEAD
+       ----------------------------------------------------
+        X       U     A    A     --soft  (disallowed)
+                               --mixed  X       A     A
+                               --hard   A       A     A
+                               --merge  A       A     A
+ X means any state and U means an unmerged index.
  Examples
  --------
  
diff --combined builtin-reset.c
index 5b647422d6d23c31ce568ea9ba3bf69eb39cec44,2c880a7e7acf05f3d1ed162ffc503ca2adbd5298..0f5022eed24f980f6fedee49f8602fefa6fe85e4
@@@ -18,6 -18,8 +18,8 @@@
  #include "tree.h"
  #include "branch.h"
  #include "parse-options.h"
+ #include "unpack-trees.h"
+ #include "cache-tree.h"
  
  static const char * const git_reset_usage[] = {
        "git reset [--mixed | --soft | --hard | --merge] [-q] [<commit>]",
@@@ -54,27 -56,44 +56,44 @@@ static inline int is_merge(void
  
  static int reset_index_file(const unsigned char *sha1, int reset_type, int quiet)
  {
-       int i = 0;
-       const char *args[6];
+       int nr = 1;
+       int newfd;
+       struct tree_desc desc[2];
+       struct unpack_trees_options opts;
+       struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
  
-       args[i++] = "read-tree";
+       memset(&opts, 0, sizeof(opts));
+       opts.head_idx = 1;
+       opts.src_index = &the_index;
+       opts.dst_index = &the_index;
+       opts.fn = oneway_merge;
+       opts.merge = 1;
        if (!quiet)
-               args[i++] = "-v";
+               opts.verbose_update = 1;
        switch (reset_type) {
        case MERGE:
-               args[i++] = "-u";
-               args[i++] = "-m";
+               opts.update = 1;
                break;
        case HARD:
-               args[i++] = "-u";
+               opts.update = 1;
                /* fallthrough */
        default:
-               args[i++] = "--reset";
+               opts.reset = 1;
        }
-       args[i++] = sha1_to_hex(sha1);
-       args[i] = NULL;
  
-       return run_command_v_opt(args, RUN_GIT_CMD);
+       newfd = hold_locked_index(lock, 1);
+       read_cache_unmerged();
+       if (!fill_tree_descriptor(desc + nr - 1, sha1))
+               return error("Failed to find tree of %s.", sha1_to_hex(sha1));
+       if (unpack_trees(nr, desc, &opts))
+               return -1;
+       if (write_cache(newfd, active_cache, active_nr) ||
+           commit_locked_index(lock))
+               return error("Could not write new index file.");
+       return 0;
  }
  
  static void print_new_head_line(struct commit *commit)
@@@ -202,7 -221,6 +221,7 @@@ int cmd_reset(int argc, const char **ar
        struct commit *commit;
        char *reflog_action, msg[1024];
        const struct option options[] = {
 +              OPT__QUIET(&quiet),
                OPT_SET_INT(0, "mixed", &reset_type,
                                                "reset HEAD and index", MIXED),
                OPT_SET_INT(0, "soft", &reset_type, "reset only HEAD", SOFT),
                                "reset HEAD, index and working tree", HARD),
                OPT_SET_INT(0, "merge", &reset_type,
                                "reset HEAD, index and working tree", MERGE),
 -              OPT_BOOLEAN('q', NULL, &quiet,
 -                              "disable showing new HEAD in hard reset and progress message"),
                OPT_BOOLEAN('p', "patch", &patch_mode, "select hunks interactively"),
                OPT_END()
        };
        if (reset_type == NONE)
                reset_type = MIXED; /* by default */
  
 -      if ((reset_type == HARD || reset_type == MERGE)
 -          && !is_inside_work_tree())
 -              die("%s reset requires a work tree",
 -                  reset_type_names[reset_type]);
 +      if (reset_type == HARD || reset_type == MERGE)
 +              setup_work_tree();
  
+       if (reset_type == MIXED && is_bare_repository())
+               die("%s reset is not allowed in a bare repository",
+                   reset_type_names[reset_type]);
        /* Soft reset does not touch the index file nor the working tree
         * at all, but requires them in a good order.  Other resets reset
         * the index file to the tree object we are switching to. */
diff --combined cache.h
index 46606477b626c9bdb6a60f9c0cf826c8fc435e00,7759c2cf94e41c4b85e178b77411293fe2a6d3e1..3e52c4efb1fba0577b18529afb8fcf75f51ddc2b
+++ b/cache.h
@@@ -177,19 -177,16 +177,20 @@@ struct cache_entry 
  
  #define CE_HASHED    (0x100000)
  #define CE_UNHASHED  (0x200000)
+ #define CE_CONFLICTED (0x800000)
  
 +/* Only remove in work directory, not index */
 +#define CE_WT_REMOVE (0x400000)
 +
  /*
   * Extended on-disk flags
   */
  #define CE_INTENT_TO_ADD 0x20000000
 +#define CE_SKIP_WORKTREE 0x40000000
  /* CE_EXTENDED2 is for future extension */
  #define CE_EXTENDED2 0x80000000
  
 -#define CE_EXTENDED_FLAGS (CE_INTENT_TO_ADD)
 +#define CE_EXTENDED_FLAGS (CE_INTENT_TO_ADD | CE_SKIP_WORKTREE)
  
  /*
   * Safeguard to avoid saving wrong flags:
@@@ -238,7 -235,6 +239,7 @@@ static inline size_t ce_namelen(const s
                            ondisk_cache_entry_size(ce_namelen(ce)))
  #define ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT)
  #define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)
 +#define ce_skip_worktree(ce) ((ce)->ce_flags & CE_SKIP_WORKTREE)
  #define ce_mark_uptodate(ce) ((ce)->ce_flags |= CE_UPTODATE)
  
  #define ce_permissions(mode) (((mode) & 0100) ? 0755 : 0644)
@@@ -469,9 -465,7 +470,9 @@@ extern int index_name_is_other(const st
  /* 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
 +#define CE_MATCH_RACY_IS_DIRTY                02
 +/* do stat comparison even if CE_SKIP_WORKTREE is true */
 +#define CE_MATCH_IGNORE_SKIP_WORKTREE 04
  extern int ie_match_stat(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
  extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
  
@@@ -536,7 -530,6 +537,7 @@@ extern int auto_crlf
  extern int read_replace_refs;
  extern int fsync_object_files;
  extern int core_preload_index;
 +extern int core_apply_sparse_checkout;
  
  enum safe_crlf {
        SAFE_CRLF_FALSE = 0,
@@@ -710,11 -703,7 +711,11 @@@ static inline unsigned int hexval(unsig
  #define DEFAULT_ABBREV 7
  
  extern int get_sha1(const char *str, unsigned char *sha1);
 -extern int get_sha1_with_mode(const char *str, unsigned char *sha1, unsigned *mode);
 +extern int get_sha1_with_mode_1(const char *str, unsigned char *sha1, unsigned *mode, int gently, const char *prefix);
 +static inline int get_sha1_with_mode(const char *str, unsigned char *sha1, unsigned *mode)
 +{
 +      return get_sha1_with_mode_1(str, sha1, mode, 1, NULL);
 +}
  extern int get_sha1_hex(const char *hex, unsigned char *sha1);
  extern char *sha1_to_hex(const unsigned char *sha1);  /* static buffer result! */
  extern int read_ref(const char *filename, unsigned char *sha1);
diff --combined read-cache.c
index a0adb272fdbbb73765f54bb65be0b0453450bf0d,16c4548a74561fdb8b744167b24af580234317e8..d214abab163e30c0328eac0bd9a65749c271d173
@@@ -259,17 -259,12 +259,17 @@@ int ie_match_stat(const struct index_st
  {
        unsigned int changed;
        int ignore_valid = options & CE_MATCH_IGNORE_VALID;
 +      int ignore_skip_worktree = options & CE_MATCH_IGNORE_SKIP_WORKTREE;
        int assume_racy_is_modified = options & CE_MATCH_RACY_IS_DIRTY;
  
        /*
         * If it's marked as always valid in the index, it's
         * valid whatever the checked-out copy says.
 +       *
 +       * skip-worktree has the same effect with higher precedence
         */
 +      if (!ignore_skip_worktree && ce_skip_worktree(ce))
 +              return 0;
        if (!ignore_valid && (ce->ce_flags & CE_VALID))
                return 0;
  
@@@ -569,7 -564,7 +569,7 @@@ int add_to_index(struct index_state *is
        int size, namelen, was_same;
        mode_t st_mode = st->st_mode;
        struct cache_entry *ce, *alias;
 -      unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_RACY_IS_DIRTY;
 +      unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE|CE_MATCH_RACY_IS_DIRTY;
        int verbose = flags & (ADD_CACHE_VERBOSE | ADD_CACHE_PRETEND);
        int pretend = flags & ADD_CACHE_PRETEND;
        int intent_only = flags & ADD_CACHE_INTENT;
@@@ -1005,20 -1000,14 +1005,20 @@@ static struct cache_entry *refresh_cach
        struct cache_entry *updated;
        int changed, size;
        int ignore_valid = options & CE_MATCH_IGNORE_VALID;
 +      int ignore_skip_worktree = options & CE_MATCH_IGNORE_SKIP_WORKTREE;
  
        if (ce_uptodate(ce))
                return ce;
  
        /*
 -       * CE_VALID means the user promised us that the change to
 -       * the work tree does not matter and told us not to worry.
 +       * CE_VALID or CE_SKIP_WORKTREE means the user promised us
 +       * that the change to the work tree does not matter and told
 +       * us not to worry.
         */
 +      if (!ignore_skip_worktree && ce_skip_worktree(ce)) {
 +              ce_mark_uptodate(ce);
 +              return ce;
 +      }
        if (!ignore_valid && (ce->ce_flags & CE_VALID)) {
                ce_mark_uptodate(ce);
                return ce;
@@@ -1333,7 -1322,7 +1333,7 @@@ int read_index_from(struct index_state 
                 * extension name (4-byte) and section length
                 * in 4-byte network byte order.
                 */
 -              unsigned long extsize;
 +              uint32_t extsize;
                memcpy(&extsize, (char *)mmap + src_offset + 4, 4);
                extsize = ntohl(extsize);
                if (read_index_extension(istate,
@@@ -1617,9 -1606,8 +1617,8 @@@ int read_index_unmerged(struct index_st
                len = strlen(ce->name);
                size = cache_entry_size(len);
                new_ce = xcalloc(1, size);
-               hashcpy(new_ce->sha1, ce->sha1);
                memcpy(new_ce->name, ce->name, len);
-               new_ce->ce_flags = create_ce_flags(len, 0);
+               new_ce->ce_flags = create_ce_flags(len, 0) | CE_CONFLICTED;
                new_ce->ce_mode = ce->ce_mode;
                if (add_index_entry(istate, new_ce, 0))
                        return error("%s: cannot drop to stage #0",
diff --combined unpack-trees.c
index 7570475b453bbaceab74fa04826ff0d23f9e6e8f,3df0de60054a2cd82b1284f5c223ff8c44a0768b..acdd3117370e596716a0bdb751d6255690e6c700
@@@ -32,12 -32,6 +32,12 @@@ static struct unpack_trees_error_msgs u
  
        /* bind_overlap */
        "Entry '%s' overlaps with '%s'.  Cannot bind.",
 +
 +      /* sparse_not_uptodate_file */
 +      "Entry '%s' not uptodate. Cannot update sparse checkout.",
 +
 +      /* would_lose_orphaned */
 +      "Working tree file '%s' would be %s by sparse checkout update.",
  };
  
  #define ERRORMSG(o,fld) \
@@@ -84,7 -78,7 +84,7 @@@ static int check_updates(struct unpack_
        if (o->update && o->verbose_update) {
                for (total = cnt = 0; cnt < index->cache_nr; cnt++) {
                        struct cache_entry *ce = index->cache[cnt];
 -                      if (ce->ce_flags & (CE_UPDATE | CE_REMOVE))
 +                      if (ce->ce_flags & (CE_UPDATE | CE_REMOVE | CE_WT_REMOVE))
                                total++;
                }
  
        for (i = 0; i < index->cache_nr; i++) {
                struct cache_entry *ce = index->cache[i];
  
 +              if (ce->ce_flags & CE_WT_REMOVE) {
 +                      display_progress(progress, ++cnt);
 +                      if (o->update)
 +                              unlink_entry(ce);
 +                      continue;
 +              }
 +
                if (ce->ce_flags & CE_REMOVE) {
                        display_progress(progress, ++cnt);
                        if (o->update)
        return errs != 0;
  }
  
 +static int verify_uptodate_sparse(struct cache_entry *ce, struct unpack_trees_options *o);
 +static int verify_absent_sparse(struct cache_entry *ce, const char *action, struct unpack_trees_options *o);
 +
 +static int will_have_skip_worktree(const struct cache_entry *ce, struct unpack_trees_options *o)
 +{
 +      const char *basename;
 +
 +      if (ce_stage(ce))
 +              return 0;
 +
 +      basename = strrchr(ce->name, '/');
 +      basename = basename ? basename+1 : ce->name;
 +      return excluded_from_list(ce->name, ce_namelen(ce), basename, NULL, o->el) <= 0;
 +}
 +
 +static int apply_sparse_checkout(struct cache_entry *ce, struct unpack_trees_options *o)
 +{
 +      int was_skip_worktree = ce_skip_worktree(ce);
 +
 +      if (will_have_skip_worktree(ce, o))
 +              ce->ce_flags |= CE_SKIP_WORKTREE;
 +      else
 +              ce->ce_flags &= ~CE_SKIP_WORKTREE;
 +
 +      /*
 +       * We only care about files getting into the checkout area
 +       * If merge strategies want to remove some, go ahead, this
 +       * flag will be removed eventually in unpack_trees() if it's
 +       * outside checkout area.
 +       */
 +      if (ce->ce_flags & CE_REMOVE)
 +              return 0;
 +
 +      if (!was_skip_worktree && ce_skip_worktree(ce)) {
 +              /*
 +               * If CE_UPDATE is set, verify_uptodate() must be called already
 +               * also stat info may have lost after merged_entry() so calling
 +               * verify_uptodate() again may fail
 +               */
 +              if (!(ce->ce_flags & CE_UPDATE) && verify_uptodate_sparse(ce, o))
 +                      return -1;
 +              ce->ce_flags |= CE_WT_REMOVE;
 +      }
 +      if (was_skip_worktree && !ce_skip_worktree(ce)) {
 +              if (verify_absent_sparse(ce, "overwritten", o))
 +                      return -1;
 +              ce->ce_flags |= CE_UPDATE;
 +      }
 +      return 0;
 +}
 +
  static inline int call_unpack_fn(struct cache_entry **src, struct unpack_trees_options *o)
  {
        int ret = o->fn(src, o);
@@@ -433,9 -369,8 +433,9 @@@ static int unpack_callback(int n, unsig
   */
  int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o)
  {
 -      int ret;
 +      int i, ret;
        static struct cache_entry *dfc;
 +      struct exclude_list el;
  
        if (len > MAX_UNPACK_TREES)
                die("unpack_trees takes at most %d trees", MAX_UNPACK_TREES);
        state.quiet = 1;
        state.refresh_cache = 1;
  
 +      memset(&el, 0, sizeof(el));
 +      if (!core_apply_sparse_checkout || !o->update)
 +              o->skip_sparse_checkout = 1;
 +      if (!o->skip_sparse_checkout) {
 +              if (add_excludes_from_file_to_list(git_path("info/sparse-checkout"), "", 0, NULL, &el, 0) < 0)
 +                      o->skip_sparse_checkout = 1;
 +              else
 +                      o->el = &el;
 +      }
 +
        memset(&o->result, 0, sizeof(o->result));
        o->result.initialized = 1;
        if (o->src_index) {
                info.fn = unpack_callback;
                info.data = o;
  
 -              if (traverse_trees(len, t, &info) < 0)
 -                      return unpack_failed(o, NULL);
 +              if (traverse_trees(len, t, &info) < 0) {
 +                      ret = unpack_failed(o, NULL);
 +                      goto done;
 +              }
        }
  
        /* Any left-over entries in the index? */
        if (o->merge) {
                while (o->pos < o->src_index->cache_nr) {
                        struct cache_entry *ce = o->src_index->cache[o->pos];
 -                      if (unpack_index_entry(ce, o) < 0)
 -                              return unpack_failed(o, NULL);
 +                      if (unpack_index_entry(ce, o) < 0) {
 +                              ret = unpack_failed(o, NULL);
 +                              goto done;
 +                      }
                }
        }
  
 -      if (o->trivial_merges_only && o->nontrivial_merge)
 -              return unpack_failed(o, "Merge requires file-level merging");
 +      if (o->trivial_merges_only && o->nontrivial_merge) {
 +              ret = unpack_failed(o, "Merge requires file-level merging");
 +              goto done;
 +      }
 +
 +      if (!o->skip_sparse_checkout) {
 +              int empty_worktree = 1;
 +              for (i = 0;i < o->result.cache_nr;i++) {
 +                      struct cache_entry *ce = o->result.cache[i];
 +
 +                      if (apply_sparse_checkout(ce, o)) {
 +                              ret = -1;
 +                              goto done;
 +                      }
 +                      /*
 +                       * Merge strategies may set CE_UPDATE|CE_REMOVE outside checkout
 +                       * area as a result of ce_skip_worktree() shortcuts in
 +                       * verify_absent() and verify_uptodate(). Clear them.
 +                       */
 +                      if (ce_skip_worktree(ce))
 +                              ce->ce_flags &= ~(CE_UPDATE | CE_REMOVE);
 +                      else
 +                              empty_worktree = 0;
 +
 +              }
 +              if (o->result.cache_nr && empty_worktree) {
 +                      ret = unpack_failed(o, "Sparse checkout leaves no entry on working directory");
 +                      goto done;
 +              }
 +      }
  
        o->src_index = NULL;
        ret = check_updates(o) ? (-2) : 0;
        if (o->dst_index)
                *o->dst_index = o->result;
 +
 +done:
 +      for (i = 0;i < el.nr;i++)
 +              free(el.excludes[i]);
 +      if (el.excludes)
 +              free(el.excludes);
 +
        return ret;
  }
  
@@@ -550,6 -436,8 +550,8 @@@ static int same(struct cache_entry *a, 
                return 0;
        if (!a && !b)
                return 1;
+       if ((a->ce_flags | b->ce_flags) & CE_CONFLICTED)
+               return 0;
        return a->ce_mode == b->ce_mode &&
               !hashcmp(a->sha1, b->sha1);
  }
   * When a CE gets turned into an unmerged entry, we
   * want it to be up-to-date
   */
 -static int verify_uptodate(struct cache_entry *ce,
 -              struct unpack_trees_options *o)
 +static int verify_uptodate_1(struct cache_entry *ce,
 +                                 struct unpack_trees_options *o,
 +                                 const char *error_msg)
  {
        struct stat st;
  
 -      if (o->index_only || o->reset || ce_uptodate(ce))
 +      if (o->index_only || (!ce_skip_worktree(ce) && (o->reset || ce_uptodate(ce))))
                return 0;
  
        if (!lstat(ce->name, &st)) {
 -              unsigned changed = ie_match_stat(o->src_index, ce, &st, CE_MATCH_IGNORE_VALID);
 +              unsigned changed = ie_match_stat(o->src_index, ce, &st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);
                if (!changed)
                        return 0;
                /*
        if (errno == ENOENT)
                return 0;
        return o->gently ? -1 :
 -              error(ERRORMSG(o, not_uptodate_file), ce->name);
 +              error(error_msg, ce->name);
 +}
 +
 +static int verify_uptodate(struct cache_entry *ce,
 +                         struct unpack_trees_options *o)
 +{
 +      if (!o->skip_sparse_checkout && will_have_skip_worktree(ce, o))
 +              return 0;
 +      return verify_uptodate_1(ce, o, ERRORMSG(o, not_uptodate_file));
 +}
 +
 +static int verify_uptodate_sparse(struct cache_entry *ce,
 +                                struct unpack_trees_options *o)
 +{
 +      return verify_uptodate_1(ce, o, ERRORMSG(o, sparse_not_uptodate_file));
  }
  
  static void invalidate_ce_path(struct cache_entry *ce, struct unpack_trees_options *o)
@@@ -701,16 -574,15 +703,16 @@@ static int icase_exists(struct unpack_t
        struct cache_entry *src;
  
        src = index_name_exists(o->src_index, dst->name, ce_namelen(dst), 1);
 -      return src && !ie_match_stat(o->src_index, src, st, CE_MATCH_IGNORE_VALID);
 +      return src && !ie_match_stat(o->src_index, src, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);
  }
  
  /*
   * We do not want to remove or overwrite a working tree file that
   * is not tracked, unless it is ignored.
   */
 -static int verify_absent(struct cache_entry *ce, const char *action,
 -                       struct unpack_trees_options *o)
 +static int verify_absent_1(struct cache_entry *ce, const char *action,
 +                               struct unpack_trees_options *o,
 +                               const char *error_msg)
  {
        struct stat st;
  
        }
        return 0;
  }
 +static int verify_absent(struct cache_entry *ce, const char *action,
 +                       struct unpack_trees_options *o)
 +{
 +      if (!o->skip_sparse_checkout && will_have_skip_worktree(ce, o))
 +              return 0;
 +      return verify_absent_1(ce, action, o, ERRORMSG(o, would_lose_untracked));
 +}
 +
 +static int verify_absent_sparse(struct cache_entry *ce, const char *action,
 +                       struct unpack_trees_options *o)
 +{
 +      return verify_absent_1(ce, action, o, ERRORMSG(o, would_lose_orphaned));
 +}
  
  static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
                struct unpack_trees_options *o)
  {
        int update = CE_UPDATE;
  
-       if (old) {
+       if (!old) {
+               if (verify_absent(merge, "overwritten", o))
+                       return -1;
+               invalidate_ce_path(merge, o);
+       } else if (!(old->ce_flags & CE_CONFLICTED)) {
                /*
                 * See if we can re-use the old CE directly?
                 * That way we get the uptodate stat info.
                } else {
                        if (verify_uptodate(old, o))
                                return -1;
 +                      if (ce_skip_worktree(old))
 +                              update |= CE_SKIP_WORKTREE;
                        invalidate_ce_path(old, o);
                }
-       }
-       else {
-               if (verify_absent(merge, "overwritten", o))
-                       return -1;
-               invalidate_ce_path(merge, o);
+       } else {
+               /*
+                * Previously unmerged entry left as an existence
+                * marker by read_index_unmerged();
+                */
+               invalidate_ce_path(old, o);
        }
  
        add_entry(o, merge, update, CE_STAGEMASK);
@@@ -847,7 -709,7 +854,7 @@@ static int deleted_entry(struct cache_e
                        return -1;
                return 0;
        }
-       if (verify_uptodate(old, o))
+       if (!(old->ce_flags & CE_CONFLICTED) && verify_uptodate(old, o))
                return -1;
        add_entry(o, ce, CE_REMOVE, 0);
        invalidate_ce_path(ce, o);
@@@ -1149,10 -1011,10 +1156,10 @@@ int oneway_merge(struct cache_entry **s
  
        if (old && same(old, a)) {
                int update = 0;
 -              if (o->reset && !ce_uptodate(old)) {
 +              if (o->reset && !ce_uptodate(old) && !ce_skip_worktree(old)) {
                        struct stat st;
                        if (lstat(old->name, &st) ||
 -                          ie_match_stat(o->src_index, old, &st, CE_MATCH_IGNORE_VALID))
 +                          ie_match_stat(o->src_index, old, &st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE))
                                update |= CE_UPDATE;
                }
                add_entry(o, old, update, 0);