Merge branch 'pc/uninteresting-submodule-disappear-upon-switch-branches'
authorJunio C Hamano <gitster@pobox.com>
Tue, 19 Jan 2010 02:12:57 +0000 (18:12 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 19 Jan 2010 02:12:57 +0000 (18:12 -0800)
* pc/uninteresting-submodule-disappear-upon-switch-branches:
  Remove empty directories when checking out a commit with fewer submodules

1  2 
unpack-trees.c

diff --combined unpack-trees.c
@@@ -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) \
@@@ -67,8 -61,16 +67,16 @@@ static void unlink_entry(struct cache_e
  {
        if (has_symlink_or_noent_leading_path(ce->name, ce_namelen(ce)))
                return;
-       if (unlink_or_warn(ce->name))
-               return;
+       if (S_ISGITLINK(ce->ce_mode)) {
+               if (rmdir(ce->name)) {
+                       warning("unable to rmdir %s: %s",
+                               ce->name, strerror(errno));
+                       return;
+               }
+       }
+       else
+               if (unlink_or_warn(ce->name))
+                       return;
        schedule_dir_for_removal(ce->name, ce_namelen(ce));
  }
  
@@@ -84,7 -86,7 +92,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 -377,8 +441,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,8 -444,6 +558,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)
@@@ -703,16 -580,15 +711,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);
@@@ -854,7 -710,7 +862,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);
@@@ -1156,10 -1012,10 +1164,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);