Merge branch 'as/sparse-checkout-removal' into maint
authorJunio C Hamano <gitster@pobox.com>
Wed, 19 Aug 2015 21:41:27 +0000 (14:41 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 19 Aug 2015 21:41:27 +0000 (14:41 -0700)
"sparse checkout" misbehaved for a path that is excluded from the
checkout when switching between branches that differ at the path.

* as/sparse-checkout-removal:
unpack-trees: don't update files with CE_WT_REMOVE set

1  2 
unpack-trees.c
diff --combined unpack-trees.c
index 2927660d929eee776d43a87851a928df12b17716,f177c0e4f7a7f6c9b2b76decd67c22cb5204ac03..d6cf84904f189a284b4bd1d72c09ee975207700e
@@@ -8,8 -8,6 +8,8 @@@
  #include "progress.h"
  #include "refs.h"
  #include "attr.h"
 +#include "split-index.h"
 +#include "dir.h"
  
  /*
   * Error messages expected by scripts out of plumbing commands such as
@@@ -58,15 -56,17 +58,15 @@@ void setup_unpack_trees_porcelain(struc
        int i;
        const char **msgs = opts->msgs;
        const char *msg;
 -      char *tmp;
        const char *cmd2 = strcmp(cmd, "checkout") ? cmd : "switch branches";
 +
        if (advice_commit_before_merge)
                msg = "Your local changes to the following files would be overwritten by %s:\n%%s"
                        "Please, commit your changes or stash them before you can %s.";
        else
                msg = "Your local changes to the following files would be overwritten by %s:\n%%s";
 -      tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen(cmd2) - 2);
 -      sprintf(tmp, msg, cmd, cmd2);
 -      msgs[ERROR_WOULD_OVERWRITE] = tmp;
 -      msgs[ERROR_NOT_UPTODATE_FILE] = tmp;
 +      msgs[ERROR_WOULD_OVERWRITE] = msgs[ERROR_NOT_UPTODATE_FILE] =
 +              xstrfmt(msg, cmd, cmd2);
  
        msgs[ERROR_NOT_UPTODATE_DIR] =
                "Updating the following directories would lose untracked files in it:\n%s";
                        "Please move or remove them before you can %s.";
        else
                msg = "The following untracked working tree files would be %s by %s:\n%%s";
 -      tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen("removed") + strlen(cmd2) - 4);
 -      sprintf(tmp, msg, "removed", cmd, cmd2);
 -      msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] = tmp;
 -      tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen("overwritten") + strlen(cmd2) - 4);
 -      sprintf(tmp, msg, "overwritten", cmd, cmd2);
 -      msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] = tmp;
 +
 +      msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] = xstrfmt(msg, "removed", cmd, cmd2);
 +      msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] = xstrfmt(msg, "overwritten", cmd, cmd2);
  
        /*
         * Special case: ERROR_BIND_OVERLAP refers to a pair of paths, we
@@@ -224,6 -227,9 +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) {
@@@ -243,9 -249,7 +246,9 @@@ static int verify_absent_sparse(const s
                                enum unpack_trees_error_types,
                                struct unpack_trees_options *o);
  
 -static int apply_sparse_checkout(struct cache_entry *ce, struct unpack_trees_options *o)
 +static int apply_sparse_checkout(struct index_state *istate,
 +                               struct cache_entry *ce,
 +                               struct unpack_trees_options *o)
  {
        int was_skip_worktree = ce_skip_worktree(ce);
  
                ce->ce_flags |= CE_SKIP_WORKTREE;
        else
                ce->ce_flags &= ~CE_SKIP_WORKTREE;
 +      if (was_skip_worktree != ce_skip_worktree(ce)) {
 +              ce->ce_flags |= CE_UPDATE_IN_BASE;
 +              istate->cache_changed |= CE_ENTRY_CHANGED;
 +      }
  
        /*
         * if (!was_skip_worktree && !ce_skip_worktree()) {
                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))
@@@ -627,6 -628,17 +631,6 @@@ static int unpack_failed(struct unpack_
        return -1;
  }
  
 -/* NEEDSWORK: give this a better name and share with tree-walk.c */
 -static int name_compare(const char *a, int a_len,
 -                      const char *b, int b_len)
 -{
 -      int len = (a_len < b_len) ? a_len : b_len;
 -      int cmp = memcmp(a, b, len);
 -      if (cmp)
 -              return cmp;
 -      return (a_len - b_len);
 -}
 -
  /*
   * The tree traversal is looking at name p.  If we have a matching entry,
   * return it.  If name p is a directory in the index, do not return
@@@ -1019,7 -1031,6 +1023,7 @@@ int unpack_trees(unsigned len, struct t
        state.force = 1;
        state.quiet = 1;
        state.refresh_cache = 1;
 +      state.istate = &o->result;
  
        memset(&el, 0, sizeof(el));
        if (!core_apply_sparse_checkout || !o->update)
        o->result.timestamp.sec = o->src_index->timestamp.sec;
        o->result.timestamp.nsec = o->src_index->timestamp.nsec;
        o->result.version = o->src_index->version;
 +      o->result.split_index = o->src_index->split_index;
 +      if (o->result.split_index)
 +              o->result.split_index->refcount++;
 +      hashcpy(o->result.sha1, o->src_index->sha1);
        o->merge_size = len;
        mark_all_ce_unused(o->src_index);
  
                                ret = -1;
                        }
  
 -                      if (apply_sparse_checkout(ce, o)) {
 +                      if (apply_sparse_checkout(&o->result, ce, o)) {
                                if (!o->show_all_errors)
                                        goto return_failed;
                                ret = -1;
        if (o->dst_index) {
                discard_index(o->dst_index);
                *o->dst_index = o->result;
 +      } else {
 +              discard_index(&o->result);
        }
  
  done:
@@@ -1181,8 -1186,7 +1185,8 @@@ return_failed
  static int reject_merge(const struct cache_entry *ce,
                        struct unpack_trees_options *o)
  {
 -      return add_rejected_path(o, ERROR_WOULD_OVERWRITE, ce->name);
 +      return o->gently ? -1 :
 +              add_rejected_path(o, ERROR_WOULD_OVERWRITE, ce->name);
  }
  
  static int same(const struct cache_entry *a, const struct cache_entry *b)
@@@ -1260,10 -1264,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->cache_tree, ce->name);
 +      if (!ce)
 +              return;
 +      cache_tree_invalidate_path(o->src_index, ce->name);
 +      untracked_cache_invalidate_path(o->src_index, ce->name);
  }
  
  /*
@@@ -1639,7 -1641,7 +1643,7 @@@ int threeway_merge(const struct cache_e
        /* #14, #14ALT, #2ALT */
        if (remote && !df_conflict_head && head_match && !remote_match) {
                if (index && !same(index, remote) && !same(index, head))
 -                      return o->gently ? -1 : reject_merge(index, o);
 +                      return reject_merge(index, o);
                return merged_entry(remote, index, o);
        }
        /*
         * make sure that it matches head.
         */
        if (index && !same(index, head))
 -              return o->gently ? -1 : reject_merge(index, o);
 +              return reject_merge(index, o);
  
        if (head) {
                /* #5ALT, #15 */
@@@ -1776,8 -1778,9 +1780,8 @@@ int twoway_merge(const struct cache_ent
                                else
                                        return merged_entry(newtree, current, o);
                        }
 -                      return o->gently ? -1 : reject_merge(current, o);
 -              }
 -              else if ((!oldtree && !newtree) || /* 4 and 5 */
 +                      return reject_merge(current, o);
 +              } else if ((!oldtree && !newtree) || /* 4 and 5 */
                         (!oldtree && newtree &&
                          same(current, newtree)) || /* 6 and 7 */
                         (oldtree && newtree &&
                          !same(oldtree, newtree) && /* 18 and 19 */
                          same(current, newtree))) {
                        return keep_entry(current, o);
 -              }
 -              else if (oldtree && !newtree && same(current, oldtree)) {
 +              } else if (oldtree && !newtree && same(current, oldtree)) {
                        /* 10 or 11 */
                        return deleted_entry(oldtree, current, o);
 -              }
 -              else if (oldtree && newtree &&
 +              } else if (oldtree && newtree &&
                         same(current, oldtree) && !same(current, newtree)) {
                        /* 20 or 21 */
                        return merged_entry(newtree, current, o);
 -              }
 -              else {
 -                      /* all other failures */
 -                      if (oldtree)
 -                              return o->gently ? -1 : reject_merge(oldtree, o);
 -                      if (current)
 -                              return o->gently ? -1 : reject_merge(current, o);
 -                      if (newtree)
 -                              return o->gently ? -1 : reject_merge(newtree, o);
 -                      return -1;
 -              }
 +              } else
 +                      return reject_merge(current, o);
        }
        else if (newtree) {
                if (oldtree && !o->initial_checkout) {