Merge branch 'jn/unpack-lstat-failure-report'
authorJunio C Hamano <gitster@pobox.com>
Thu, 10 Feb 2011 00:41:16 +0000 (16:41 -0800)
committerJunio C Hamano <gitster@pobox.com>
Thu, 10 Feb 2011 00:41:16 +0000 (16:41 -0800)
* jn/unpack-lstat-failure-report:
unpack-trees: handle lstat failure for existing file
unpack-trees: handle lstat failure for existing directory

1  2 
unpack-trees.c
diff --combined unpack-trees.c
index 1ca41b1a6986559a7c5ddc3983d751d75a0d23ff,24227fc6c697f6b249149e404e21df71ac7fbc06..bf204b921040f411a22d0f17592e940517672c66
@@@ -231,11 -231,20 +231,11 @@@ static int check_updates(struct unpack_
  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 will_have_skip_worktree(const struct cache_entry *ce, struct unpack_trees_options *o)
 -{
 -      const char *basename;
 -
 -      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 (!ce_stage(ce) && will_have_skip_worktree(ce, o))
 +      if (ce->ce_flags & CE_NEW_SKIP_WORKTREE)
                ce->ce_flags |= CE_SKIP_WORKTREE;
        else
                ce->ce_flags &= ~CE_SKIP_WORKTREE;
@@@ -310,7 -319,7 +310,7 @@@ static void mark_all_ce_unused(struct i
  {
        int i;
        for (i = 0; i < index->cache_nr; i++)
 -              index->cache[i]->ce_flags &= ~CE_UNPACKED;
 +              index->cache[i]->ce_flags &= ~(CE_UNPACKED | CE_ADDED | CE_NEW_SKIP_WORKTREE);
  }
  
  static int locate_in_src_index(struct cache_entry *ce,
@@@ -811,177 -820,9 +811,177 @@@ static int unpack_callback(int n, unsig
        return mask;
  }
  
 +/* Whole directory matching */
 +static int clear_ce_flags_dir(struct cache_entry **cache, int nr,
 +                            char *prefix, int prefix_len,
 +                            char *basename,
 +                            int select_mask, int clear_mask,
 +                            struct exclude_list *el)
 +{
 +      struct cache_entry **cache_end = cache + nr;
 +      int dtype = DT_DIR;
 +      int ret = excluded_from_list(prefix, prefix_len, basename, &dtype, el);
 +
 +      prefix[prefix_len++] = '/';
 +
 +      /* included, no clearing for any entries under this directory */
 +      if (!ret) {
 +              for (; cache != cache_end; cache++) {
 +                      struct cache_entry *ce = *cache;
 +                      if (strncmp(ce->name, prefix, prefix_len))
 +                              break;
 +              }
 +              return nr - (cache_end - cache);
 +      }
 +
 +      /* excluded, clear all selected entries under this directory. */
 +      if (ret == 1) {
 +              for (; cache != cache_end; cache++) {
 +                      struct cache_entry *ce = *cache;
 +                      if (select_mask && !(ce->ce_flags & select_mask))
 +                              continue;
 +                      if (strncmp(ce->name, prefix, prefix_len))
 +                              break;
 +                      ce->ce_flags &= ~clear_mask;
 +              }
 +              return nr - (cache_end - cache);
 +      }
 +
 +      return 0;
 +}
 +
 +/*
 + * Traverse the index, find every entry that matches according to
 + * o->el. Do "ce_flags &= ~clear_mask" on those entries. Return the
 + * number of traversed entries.
 + *
 + * If select_mask is non-zero, only entries whose ce_flags has on of
 + * those bits enabled are traversed.
 + *
 + * cache      : pointer to an index entry
 + * prefix_len : an offset to its path
 + *
 + * The current path ("prefix") including the trailing '/' is
 + *   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,
 +                          char *prefix, int prefix_len,
 +                          int select_mask, int clear_mask,
 +                          struct exclude_list *el)
 +{
 +      struct cache_entry **cache_end = cache + nr;
 +
 +      /*
 +       * Process all entries that have the given prefix and meet
 +       * select_mask condition
 +       */
 +      while(cache != cache_end) {
 +              struct cache_entry *ce = *cache;
 +              const char *name, *slash;
 +              int len, dtype;
 +
 +              if (select_mask && !(ce->ce_flags & select_mask)) {
 +                      cache++;
 +                      continue;
 +              }
 +
 +              if (prefix_len && strncmp(ce->name, prefix, prefix_len))
 +                      break;
 +
 +              name = ce->name + prefix_len;
 +              slash = strchr(name, '/');
 +
 +              /* If it's a directory, try whole directory match first */
 +              if (slash) {
 +                      int processed;
 +
 +                      len = slash - name;
 +                      memcpy(prefix + prefix_len, name, len);
 +
 +                      /*
 +                       * terminate the string (no trailing slash),
 +                       * clear_c_f_dir needs it
 +                       */
 +                      prefix[prefix_len + len] = '\0';
 +                      processed = clear_ce_flags_dir(cache, cache_end - cache,
 +                                                     prefix, prefix_len + len,
 +                                                     prefix + prefix_len,
 +                                                     select_mask, clear_mask,
 +                                                     el);
 +
 +                      /* clear_c_f_dir eats a whole dir already? */
 +                      if (processed) {
 +                              cache += processed;
 +                              continue;
 +                      }
 +
 +                      prefix[prefix_len + len++] = '/';
 +                      cache += clear_ce_flags_1(cache, cache_end - cache,
 +                                                prefix, prefix_len + len,
 +                                                select_mask, clear_mask, el);
 +                      continue;
 +              }
 +
 +              /* Non-directory */
 +              dtype = ce_to_dtype(ce);
 +              if (excluded_from_list(ce->name, ce_namelen(ce), name, &dtype, el) > 0)
 +                      ce->ce_flags &= ~clear_mask;
 +              cache++;
 +      }
 +      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)
 +{
 +      char prefix[PATH_MAX];
 +      return clear_ce_flags_1(cache, nr,
 +                              prefix, 0,
 +                              select_mask, clear_mask,
 +                              el);
 +}
 +
 +/*
 + * 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,
 +                                 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];
 +
 +              if (select_flag && !(ce->ce_flags & select_flag))
 +                      continue;
 +
 +              if (!ce_stage(ce))
 +                      ce->ce_flags |= skip_wt_flag;
 +              else
 +                      ce->ce_flags &= ~skip_wt_flag;
 +      }
 +
 +      /*
 +       * 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);
 +}
 +
 +static int verify_absent(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.
 + *
 + * CE_ADDED, CE_UNPACKED and CE_NEW_SKIP_WORKTREE are used internally
   */
  int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o)
  {
        o->merge_size = len;
        mark_all_ce_unused(o->src_index);
  
 +      /*
 +       * Sparse checkout loop #1: set NEW_SKIP_WORKTREE on existing entries
 +       */
 +      if (!o->skip_sparse_checkout)
 +              mark_new_skip_worktree(o->el, o->src_index, 0, CE_NEW_SKIP_WORKTREE);
 +
        if (!dfc)
                dfc = xcalloc(1, cache_entry_size(0));
        o->df_conflict_entry = dfc;
  
        if (!o->skip_sparse_checkout) {
                int empty_worktree = 1;
 -              for (i = 0;i < o->result.cache_nr;i++) {
 +
 +              /*
 +               * Sparse checkout loop #2: set NEW_SKIP_WORKTREE on entries not in loop #1
 +               * If the will have NEW_SKIP_WORKTREE, also set CE_SKIP_WORKTREE
 +               * so apply_sparse_checkout() won't attempt to remove it from worktree
 +               */
 +              mark_new_skip_worktree(o->el, &o->result, CE_ADDED, CE_SKIP_WORKTREE | CE_NEW_SKIP_WORKTREE);
 +
 +              for (i = 0; i < o->result.cache_nr; i++) {
                        struct cache_entry *ce = o->result.cache[i];
  
 +                      /*
 +                       * Entries marked with CE_ADDED in merged_entry() do not have
 +                       * verify_absent() check (the check is effectively disabled
 +                       * because CE_NEW_SKIP_WORKTREE is set unconditionally).
 +                       *
 +                       * Do the real check now because we have had
 +                       * correct CE_NEW_SKIP_WORKTREE
 +                       */
 +                      if (ce->ce_flags & CE_ADDED &&
 +                          verify_absent(ce, ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o))
 +                                      return -1;
 +
                        if (apply_sparse_checkout(ce, o)) {
                                ret = -1;
                                goto done;
                *o->dst_index = o->result;
  
  done:
 -      for (i = 0;i < el.nr;i++)
 -              free(el.excludes[i]);
 -      if (el.excludes)
 -              free(el.excludes);
 -
 +      free_excludes(&el);
        return ret;
  
  return_failed:
@@@ -1184,7 -1003,7 +1184,7 @@@ static int verify_uptodate_1(struct cac
  static int verify_uptodate(struct cache_entry *ce,
                           struct unpack_trees_options *o)
  {
 -      if (!o->skip_sparse_checkout && will_have_skip_worktree(ce, o))
 +      if (!o->skip_sparse_checkout && (ce->ce_flags & CE_NEW_SKIP_WORKTREE))
                return 0;
        return verify_uptodate_1(ce, o, ERROR_NOT_UPTODATE_FILE);
  }
@@@ -1374,23 -1193,29 +1374,29 @@@ static int verify_absent_1(struct cache
                char path[PATH_MAX + 1];
                memcpy(path, ce->name, len);
                path[len] = 0;
-               lstat(path, &st);
+               if (lstat(path, &st))
+                       return error("cannot stat '%s': %s", path,
+                                       strerror(errno));
  
                return check_ok_to_remove(path, len, DT_UNKNOWN, NULL, &st,
                                error_type, o);
-       } else if (!lstat(ce->name, &st))
+       } else if (lstat(ce->name, &st)) {
+               if (errno != ENOENT)
+                       return error("cannot stat '%s': %s", ce->name,
+                                    strerror(errno));
+               return 0;
+       } else {
                return check_ok_to_remove(ce->name, ce_namelen(ce),
-                               ce_to_dtype(ce), ce, &st,
-                               error_type, o);
-       return 0;
+                                         ce_to_dtype(ce), ce, &st,
+                                         error_type, o);
+       }
  }
  
  static int verify_absent(struct cache_entry *ce,
                         enum unpack_trees_error_types error_type,
                         struct unpack_trees_options *o)
  {
 -      if (!o->skip_sparse_checkout && will_have_skip_worktree(ce, o))
 +      if (!o->skip_sparse_checkout && (ce->ce_flags & CE_NEW_SKIP_WORKTREE))
                return 0;
        return verify_absent_1(ce, error_type, o);
  }
@@@ -1412,23 -1237,10 +1418,23 @@@ static int merged_entry(struct cache_en
        int update = CE_UPDATE;
  
        if (!old) {
 +              /*
 +               * New index entries. In sparse checkout, the following
 +               * verify_absent() will be delayed until after
 +               * traverse_trees() finishes in unpack_trees(), then:
 +               *
 +               *  - CE_NEW_SKIP_WORKTREE will be computed correctly
 +               *  - verify_absent() be called again, this time with
 +               *    correct CE_NEW_SKIP_WORKTREE
 +               *
 +               * verify_absent() call here does nothing in sparse
 +               * checkout (i.e. o->skip_sparse_checkout == 0)
 +               */
 +              update |= CE_ADDED;
 +              merge->ce_flags |= CE_NEW_SKIP_WORKTREE;
 +
                if (verify_absent(merge, ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o))
                        return -1;
 -              if (!o->skip_sparse_checkout && will_have_skip_worktree(merge, o))
 -                      update |= CE_SKIP_WORKTREE;
                invalidate_ce_path(merge, o);
        } else if (!(old->ce_flags & CE_CONFLICTED)) {
                /*
                } else {
                        if (verify_uptodate(old, o))
                                return -1;
 -                      if (ce_skip_worktree(old))
 -                              update |= CE_SKIP_WORKTREE;
 +                      /* Migrate old flags over */
 +                      update |= old->ce_flags & (CE_SKIP_WORKTREE | CE_NEW_SKIP_WORKTREE);
                        invalidate_ce_path(old, o);
                }
        } else {