Merge branch 'jk/two-way-merge-corner-case-fix'
authorJunio C Hamano <gitster@pobox.com>
Thu, 5 Dec 2013 20:59:25 +0000 (12:59 -0800)
committerJunio C Hamano <gitster@pobox.com>
Thu, 5 Dec 2013 20:59:25 +0000 (12:59 -0800)
Fix a rather longstanding corner-case bug in twoway "reset to
there" merge, which is most often seen in "git am --abort".

* jk/two-way-merge-corner-case-fix:
t1005: add test for "read-tree --reset -u A B"
t1005: reindent
unpack-trees: fix "read-tree -u --reset A B" with conflicted index

1  2 
unpack-trees.c
diff --combined unpack-trees.c
index 35cb05e92bed9fb273747adc4718458b9f500898,bf978e154c82570f535b3f39b1235baeac781a92..ad3e9a04fe8ccb9286b94480f4484b0569f70a57
@@@ -102,34 -102,21 +102,34 @@@ void setup_unpack_trees_porcelain(struc
                opts->unpack_rejects[i].strdup_strings = 1;
  }
  
 -static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
 -      unsigned int set, unsigned int clear)
 +static void do_add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
 +                       unsigned int set, unsigned int clear)
  {
 -      unsigned int size = ce_size(ce);
 -      struct cache_entry *new = xmalloc(size);
 -
        clear |= CE_HASHED | CE_UNHASHED;
  
        if (set & CE_REMOVE)
                set |= CE_WT_REMOVE;
  
 +      ce->next = NULL;
 +      ce->ce_flags = (ce->ce_flags & ~clear) | set;
 +      add_index_entry(&o->result, ce,
 +                      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 = xmalloc(size);
 +
        memcpy(new, ce, size);
 -      new->next = NULL;
 -      new->ce_flags = (new->ce_flags & ~clear) | set;
 -      add_index_entry(&o->result, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
 +      return new;
 +}
 +
 +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);
  }
  
  /*
@@@ -179,7 -166,7 +179,7 @@@ static void display_error_msgs(struct u
   * Unlink the last component and schedule the leading directories for
   * removal, such that empty directories get removed.
   */
 -static void unlink_entry(struct cache_entry *ce)
 +static void unlink_entry(const struct cache_entry *ce)
  {
        if (!check_leading_path(ce->name, ce_namelen(ce)))
                return;
@@@ -199,7 -186,7 +199,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];
 +                      const struct cache_entry *ce = index->cache[cnt];
                        if (ce->ce_flags & (CE_UPDATE | CE_WT_REMOVE))
                                total++;
                }
        if (o->update)
                git_attr_set_direction(GIT_ATTR_CHECKOUT, &o->result);
        for (i = 0; i < index->cache_nr; i++) {
 -              struct cache_entry *ce = index->cache[i];
 +              const struct cache_entry *ce = index->cache[i];
  
                if (ce->ce_flags & CE_WT_REMOVE) {
                        display_progress(progress, ++cnt);
        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, enum unpack_trees_error_types, struct unpack_trees_options *o);
 +static int verify_uptodate_sparse(const struct cache_entry *ce,
 +                                struct unpack_trees_options *o);
 +static int verify_absent_sparse(const struct cache_entry *ce,
 +                              enum unpack_trees_error_types,
 +                              struct unpack_trees_options *o);
  
  static int apply_sparse_checkout(struct cache_entry *ce, struct unpack_trees_options *o)
  {
        return 0;
  }
  
 -static inline int call_unpack_fn(struct cache_entry **src, struct unpack_trees_options *o)
 +static inline int call_unpack_fn(const struct cache_entry * const *src,
 +                               struct unpack_trees_options *o)
  {
        int ret = o->fn(src, o);
        if (ret > 0)
@@@ -330,7 -313,7 +330,7 @@@ static void mark_all_ce_unused(struct i
                index->cache[i]->ce_flags &= ~(CE_UNPACKED | CE_ADDED | CE_NEW_SKIP_WORKTREE);
  }
  
 -static int locate_in_src_index(struct cache_entry *ce,
 +static int locate_in_src_index(const struct cache_entry *ce,
                               struct unpack_trees_options *o)
  {
        struct index_state *index = o->src_index;
@@@ -376,7 -359,7 +376,7 @@@ static struct cache_entry *next_cache_e
        return NULL;
  }
  
 -static void add_same_unmerged(struct cache_entry *ce,
 +static void add_same_unmerged(const struct cache_entry *ce,
                              struct unpack_trees_options *o)
  {
        struct index_state *index = o->src_index;
  static int unpack_index_entry(struct cache_entry *ce,
                              struct unpack_trees_options *o)
  {
 -      struct cache_entry *src[MAX_UNPACK_TREES + 1] = { NULL, };
 +      const struct cache_entry *src[MAX_UNPACK_TREES + 1] = { NULL, };
        int ret;
  
        src[0] = ce;
@@@ -464,7 -447,7 +464,7 @@@ static int traverse_trees_recursive(in
        newinfo.pathspec = info->pathspec;
        newinfo.name = *p;
        newinfo.pathlen += tree_entry_len(p) + 1;
 -      newinfo.conflicts |= df_conflicts;
 +      newinfo.df_conflicts |= df_conflicts;
  
        for (i = 0; i < n; i++, dirmask >>= 1) {
                const unsigned char *sha1 = NULL;
@@@ -549,8 -532,7 +549,8 @@@ static struct cache_entry *create_ce_en
        struct cache_entry *ce = xcalloc(1, cache_entry_size(len));
  
        ce->ce_mode = create_ce_mode(n->mode);
 -      ce->ce_flags = create_ce_flags(len, stage);
 +      ce->ce_flags = create_ce_flags(stage);
 +      ce->ce_namelen = len;
        hashcpy(ce->sha1, n->sha1);
        make_traverse_path(ce->name, info, n);
  
@@@ -565,12 -547,17 +565,12 @@@ static int unpack_nondirectories(int n
  {
        int i;
        struct unpack_trees_options *o = info->data;
 -      unsigned long conflicts;
 +      unsigned long conflicts = info->df_conflicts | dirmask;
  
        /* Do we have *only* directories? Nothing to do */
        if (mask == dirmask && !src[0])
                return 0;
  
 -      conflicts = info->conflicts;
 -      if (o->merge)
 -              conflicts >>= 1;
 -      conflicts |= dirmask;
 -
        /*
         * Ok, we've filled in up to any potential index entry in src[0],
         * now do the rest.
                src[i + o->merge] = create_ce_entry(info, names + i, stage);
        }
  
 -      if (o->merge)
 -              return call_unpack_fn(src, o);
 +      if (o->merge) {
 +              int rc = call_unpack_fn((const struct cache_entry * const *)src,
 +                                      o);
 +              for (i = 0; i < n; i++) {
 +                      struct cache_entry *ce = src[i + o->merge];
 +                      if (ce != o->df_conflict_entry)
 +                              free(ce);
 +              }
 +              return rc;
 +      }
  
        for (i = 0; i < n; i++)
                if (src[i] && src[i] != o->df_conflict_entry)
 -                      add_entry(o, src[i], 0, 0);
 +                      do_add_entry(o, src[i], 0, 0);
        return 0;
  }
  
@@@ -650,7 -629,7 +650,7 @@@ static int find_cache_pos(struct traver
        int p_len = tree_entry_len(p);
  
        for (pos = o->cache_bottom; pos < index->cache_nr; pos++) {
 -              struct cache_entry *ce = index->cache[pos];
 +              const struct cache_entry *ce = index->cache[pos];
                const char *ce_name, *ce_slash;
                int cmp, ce_len;
  
@@@ -793,7 -772,7 +793,7 @@@ static int unpack_callback(int n, unsig
        if (unpack_nondirectories(n, mask, dirmask, src, names, info) < 0)
                return -1;
  
 -      if (src[0]) {
 +      if (o->merge && src[0]) {
                if (ce_stage(src[0]))
                        mark_ce_used_same_name(src[0], o);
                else
  
        /* Now handle any directories.. */
        if (dirmask) {
 -              unsigned long conflicts = mask & ~dirmask;
 -              if (o->merge) {
 -                      conflicts <<= 1;
 -                      if (src[0])
 -                              conflicts |= 1;
 -              }
 -
                /* special case: "diff-index --cached" looking at a tree */
                if (o->diff_index_cached &&
                    n == 1 && dirmask == 1 && S_ISDIR(names->mode)) {
                        }
                }
  
 -              if (traverse_trees_recursive(n, dirmask, conflicts,
 +              if (traverse_trees_recursive(n, dirmask, mask & ~dirmask,
                                             names, info) < 0)
                        return -1;
                return mask;
@@@ -843,8 -829,7 +843,8 @@@ static int clear_ce_flags_dir(struct ca
  {
        struct cache_entry **cache_end;
        int dtype = DT_DIR;
 -      int ret = excluded_from_list(prefix, prefix_len, basename, &dtype, el);
 +      int ret = is_excluded_from_list(prefix, prefix_len,
 +                                      basename, &dtype, el);
  
        prefix[prefix_len++] = '/';
  
         * with ret (iow, we know in advance the incl/excl
         * decision for the entire directory), clear flag here without
         * calling clear_ce_flags_1(). That function will call
 -       * the expensive excluded_from_list() on every entry.
 +       * the expensive is_excluded_from_list() on every entry.
         */
        return clear_ce_flags_1(cache, cache_end - cache,
                                prefix, prefix_len,
@@@ -946,8 -931,7 +946,8 @@@ static int clear_ce_flags_1(struct cach
  
                /* Non-directory */
                dtype = ce_to_dtype(ce);
 -              ret = excluded_from_list(ce->name, ce_namelen(ce), name, &dtype, el);
 +              ret = is_excluded_from_list(ce->name, ce_namelen(ce),
 +                                          name, &dtype, el);
                if (ret < 0)
                        ret = defval;
                if (ret > 0)
@@@ -1001,9 -985,7 +1001,9 @@@ static void mark_new_skip_worktree(stru
                       select_flag, skip_wt_flag, el);
  }
  
 -static int verify_absent(struct cache_entry *, enum unpack_trees_error_types, struct unpack_trees_options *);
 +static int verify_absent(const struct cache_entry *,
 +                       enum unpack_trees_error_types,
 +                       struct unpack_trees_options *);
  /*
   * N-way merge "len" trees.  Returns 0 on success, -1 on failure to manipulate the
   * resulting index, -2 on failure to reflect the changes to the work tree.
@@@ -1028,7 -1010,7 +1028,7 @@@ int unpack_trees(unsigned len, struct t
        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)
 +              if (add_excludes_from_file_to_list(git_path("info/sparse-checkout"), "", 0, &el, 0) < 0)
                        o->skip_sparse_checkout = 1;
                else
                        o->el = &el;
        o->result.initialized = 1;
        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->merge_size = len;
        mark_all_ce_unused(o->src_index);
  
  
        o->src_index = NULL;
        ret = check_updates(o) ? (-2) : 0;
 -      if (o->dst_index)
 +      if (o->dst_index) {
 +              discard_index(o->dst_index);
                *o->dst_index = o->result;
 +      }
  
  done:
 -      free_excludes(&el);
 +      clear_exclude_list(&el);
        return ret;
  
  return_failed:
  
  /* Here come the merge functions */
  
 -static int reject_merge(struct cache_entry *ce, struct unpack_trees_options *o)
 +static int reject_merge(const struct cache_entry *ce,
 +                      struct unpack_trees_options *o)
  {
        return add_rejected_path(o, ERROR_WOULD_OVERWRITE, ce->name);
  }
  
 -static int same(struct cache_entry *a, struct cache_entry *b)
 +static int same(const struct cache_entry *a, const struct cache_entry *b)
  {
        if (!!a != !!b)
                return 0;
   * When a CE gets turned into an unmerged entry, we
   * want it to be up-to-date
   */
 -static int verify_uptodate_1(struct cache_entry *ce,
 -                                 struct unpack_trees_options *o,
 -                                 enum unpack_trees_error_types error_type)
 +static int verify_uptodate_1(const struct cache_entry *ce,
 +                           struct unpack_trees_options *o,
 +                           enum unpack_trees_error_types error_type)
  {
        struct stat st;
  
                        return 0;
                /*
                 * NEEDSWORK: the current default policy is to allow
 -               * submodule to be out of sync wrt the supermodule
 +               * submodule to be out of sync wrt the superproject
                 * index.  This needs to be tightened later for
                 * submodules that are marked to be automatically
                 * checked out.
                add_rejected_path(o, error_type, ce->name);
  }
  
 -static int verify_uptodate(struct cache_entry *ce,
 +static int verify_uptodate(const struct cache_entry *ce,
                           struct unpack_trees_options *o)
  {
        if (!o->skip_sparse_checkout && (ce->ce_flags & CE_NEW_SKIP_WORKTREE))
        return verify_uptodate_1(ce, o, ERROR_NOT_UPTODATE_FILE);
  }
  
 -static int verify_uptodate_sparse(struct cache_entry *ce,
 +static int verify_uptodate_sparse(const struct cache_entry *ce,
                                  struct unpack_trees_options *o)
  {
        return verify_uptodate_1(ce, o, ERROR_SPARSE_NOT_UPTODATE_FILE);
  }
  
 -static void invalidate_ce_path(struct cache_entry *ce, struct unpack_trees_options *o)
 +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);
   * Currently, git does not checkout subprojects during a superproject
   * checkout, so it is not going to overwrite anything.
   */
 -static int verify_clean_submodule(struct cache_entry *ce,
 -                                    enum unpack_trees_error_types error_type,
 -                                    struct unpack_trees_options *o)
 +static int verify_clean_submodule(const struct cache_entry *ce,
 +                                enum unpack_trees_error_types error_type,
 +                                struct unpack_trees_options *o)
  {
        return 0;
  }
  
 -static int verify_clean_subdirectory(struct cache_entry *ce,
 -                                    enum unpack_trees_error_types error_type,
 -                                    struct unpack_trees_options *o)
 +static int verify_clean_subdirectory(const struct cache_entry *ce,
 +                                   enum unpack_trees_error_types error_type,
 +                                   struct unpack_trees_options *o)
  {
        /*
         * we are about to extract "ce->name"; we would not want to lose
         * First let's make sure we do not have a local modification
         * in that directory.
         */
 -      namelen = strlen(ce->name);
 +      namelen = ce_namelen(ce);
        for (i = locate_in_src_index(ce, o);
             i < o->src_index->cache_nr;
             i++) {
   */
  static int icase_exists(struct unpack_trees_options *o, const char *name, int len, struct stat *st)
  {
 -      struct cache_entry *src;
 +      const struct cache_entry *src;
  
 -      src = index_name_exists(o->src_index, name, len, 1);
 +      src = index_file_exists(o->src_index, name, len, 1);
        return src && !ie_match_stat(o->src_index, src, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);
  }
  
  static int check_ok_to_remove(const char *name, int len, int dtype,
 -                            struct cache_entry *ce, struct stat *st,
 +                            const struct cache_entry *ce, struct stat *st,
                              enum unpack_trees_error_types error_type,
                              struct unpack_trees_options *o)
  {
 -      struct cache_entry *result;
 +      const struct cache_entry *result;
  
        /*
         * It may be that the 'lstat()' succeeded even though
        if (ignore_case && icase_exists(o, name, len, st))
                return 0;
  
 -      if (o->dir && excluded(o->dir, name, &dtype))
 +      if (o->dir &&
 +          is_excluded(o->dir, name, &dtype))
                /*
                 * ce->name is explicitly excluded, so it is Ok to
                 * overwrite it.
         * delete this path, which is in a subdirectory that
         * is being replaced with a blob.
         */
 -      result = index_name_exists(&o->result, name, len, 0);
 +      result = index_file_exists(&o->result, name, len, 0);
        if (result) {
                if (result->ce_flags & CE_REMOVE)
                        return 0;
   * We do not want to remove or overwrite a working tree file that
   * is not tracked, unless it is ignored.
   */
 -static int verify_absent_1(struct cache_entry *ce,
 -                               enum unpack_trees_error_types error_type,
 -                               struct unpack_trees_options *o)
 +static int verify_absent_1(const struct cache_entry *ce,
 +                         enum unpack_trees_error_types error_type,
 +                         struct unpack_trees_options *o)
  {
        int len;
        struct stat st;
        }
  }
  
 -static int verify_absent(struct cache_entry *ce,
 +static int verify_absent(const struct cache_entry *ce,
                         enum unpack_trees_error_types error_type,
                         struct unpack_trees_options *o)
  {
        return verify_absent_1(ce, error_type, o);
  }
  
 -static int verify_absent_sparse(struct cache_entry *ce,
 -                       enum unpack_trees_error_types error_type,
 -                       struct unpack_trees_options *o)
 +static int verify_absent_sparse(const struct cache_entry *ce,
 +                              enum unpack_trees_error_types error_type,
 +                              struct unpack_trees_options *o)
  {
        enum unpack_trees_error_types orphaned_error = error_type;
        if (orphaned_error == ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN)
        return verify_absent_1(ce, orphaned_error, o);
  }
  
 -static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
 -              struct unpack_trees_options *o)
 +static int merged_entry(const struct cache_entry *ce,
 +                      const struct cache_entry *old,
 +                      struct unpack_trees_options *o)
  {
        int update = CE_UPDATE;
 +      struct cache_entry *merge = dup_entry(ce);
  
        if (!old) {
                /*
                update |= CE_ADDED;
                merge->ce_flags |= CE_NEW_SKIP_WORKTREE;
  
 -              if (verify_absent(merge, ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o))
 +              if (verify_absent(merge,
 +                                ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o)) {
 +                      free(merge);
                        return -1;
 +              }
                invalidate_ce_path(merge, o);
        } else if (!(old->ce_flags & CE_CONFLICTED)) {
                /*
                        copy_cache_entry(merge, old);
                        update = 0;
                } else {
 -                      if (verify_uptodate(old, o))
 +                      if (verify_uptodate(old, o)) {
 +                              free(merge);
                                return -1;
 +                      }
                        /* Migrate old flags over */
                        update |= old->ce_flags & (CE_SKIP_WORKTREE | CE_NEW_SKIP_WORKTREE);
                        invalidate_ce_path(old, o);
                invalidate_ce_path(old, o);
        }
  
 -      add_entry(o, merge, update, CE_STAGEMASK);
 +      do_add_entry(o, merge, update, CE_STAGEMASK);
        return 1;
  }
  
 -static int deleted_entry(struct cache_entry *ce, struct cache_entry *old,
 -              struct unpack_trees_options *o)
 +static int deleted_entry(const struct cache_entry *ce,
 +                       const struct cache_entry *old,
 +                       struct unpack_trees_options *o)
  {
        /* Did it exist in the index? */
        if (!old) {
        return 1;
  }
  
 -static int keep_entry(struct cache_entry *ce, struct unpack_trees_options *o)
 +static int keep_entry(const struct cache_entry *ce,
 +                    struct unpack_trees_options *o)
  {
        add_entry(o, ce, 0, 0);
        return 1;
@@@ -1573,12 -1540,11 +1573,12 @@@ static void show_stage_entry(FILE *o
  }
  #endif
  
 -int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o)
 +int threeway_merge(const struct cache_entry * const *stages,
 +                 struct unpack_trees_options *o)
  {
 -      struct cache_entry *index;
 -      struct cache_entry *head;
 -      struct cache_entry *remote = stages[o->head_idx + 1];
 +      const struct cache_entry *index;
 +      const struct cache_entry *head;
 +      const struct cache_entry *remote = stages[o->head_idx + 1];
        int count;
        int head_match = 0;
        int remote_match = 0;
        if (o->aggressive) {
                int head_deleted = !head;
                int remote_deleted = !remote;
 -              struct cache_entry *ce = NULL;
 +              const struct cache_entry *ce = NULL;
  
                if (index)
                        ce = index;
   * "carry forward" rule, please see <Documentation/git-read-tree.txt>.
   *
   */
 -int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o)
 +int twoway_merge(const struct cache_entry * const *src,
 +               struct unpack_trees_options *o)
  {
 -      struct cache_entry *current = src[0];
 -      struct cache_entry *oldtree = src[1];
 -      struct cache_entry *newtree = src[2];
 +      const struct cache_entry *current = src[0];
 +      const struct cache_entry *oldtree = src[1];
 +      const struct cache_entry *newtree = src[2];
  
        if (o->merge_size != 2)
                return error("Cannot do a twoway merge of %d trees",
                newtree = NULL;
  
        if (current) {
-               if ((!oldtree && !newtree) || /* 4 and 5 */
-                   (!oldtree && newtree &&
-                    same(current, newtree)) || /* 6 and 7 */
-                   (oldtree && newtree &&
-                    same(oldtree, newtree)) || /* 14 and 15 */
-                   (oldtree && newtree &&
-                    !same(oldtree, newtree) && /* 18 and 19 */
-                    same(current, newtree))) {
+               if (current->ce_flags & CE_CONFLICTED) {
+                       if (same(oldtree, newtree) || o->reset) {
+                               if (!newtree)
+                                       return deleted_entry(current, current, o);
+                               else
+                                       return merged_entry(newtree, current, o);
+                       }
+                       return o->gently ? -1 : reject_merge(current, o);
+               }
+               else if ((!oldtree && !newtree) || /* 4 and 5 */
+                        (!oldtree && newtree &&
+                         same(current, newtree)) || /* 6 and 7 */
+                        (oldtree && newtree &&
+                         same(oldtree, newtree)) || /* 14 and 15 */
+                        (oldtree && newtree &&
+                         !same(oldtree, newtree) && /* 18 and 19 */
+                         same(current, newtree))) {
                        return keep_entry(current, o);
                }
                else if (oldtree && !newtree && same(current, oldtree)) {
   * Keep the index entries at stage0, collapse stage1 but make sure
   * stage0 does not have anything there.
   */
 -int bind_merge(struct cache_entry **src,
 -              struct unpack_trees_options *o)
 +int bind_merge(const struct cache_entry * const *src,
 +             struct unpack_trees_options *o)
  {
 -      struct cache_entry *old = src[0];
 -      struct cache_entry *a = src[1];
 +      const struct cache_entry *old = src[0];
 +      const struct cache_entry *a = src[1];
  
        if (o->merge_size != 1)
 -              return error("Cannot do a bind merge of %d trees\n",
 +              return error("Cannot do a bind merge of %d trees",
                             o->merge_size);
        if (a && old)
                return o->gently ? -1 :
   * The rule is:
   * - take the stat information from stage0, take the data from stage1
   */
 -int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o)
 +int oneway_merge(const struct cache_entry * const *src,
 +               struct unpack_trees_options *o)
  {
 -      struct cache_entry *old = src[0];
 -      struct cache_entry *a = src[1];
 +      const struct cache_entry *old = src[0];
 +      const struct cache_entry *a = src[1];
  
        if (o->merge_size != 1)
                return error("Cannot do a oneway merge of %d trees",
  
        if (old && same(old, a)) {
                int update = 0;
 -              if (o->reset && !ce_uptodate(old) && !ce_skip_worktree(old)) {
 +              if (o->reset && o->update && !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|CE_MATCH_IGNORE_SKIP_WORKTREE))