]> git.ipfire.org Git - thirdparty/git.git/blobdiff - unpack-trees.c
Merge branch 'ds/sparse-allow-empty-working-tree'
[thirdparty/git.git] / unpack-trees.c
index f618a644efa0f942619d0ed71c8ff4d68f11a4de..1fe3764f2b217263d00332bcca203f0c9ef6ee5f 100644 (file)
@@ -24,7 +24,7 @@
  * situation better.  See how "git checkout" and "git merge" replaces
  * them using setup_unpack_trees_porcelain(), for example.
  */
-static const char *unpack_plumbing_errors[NB_UNPACK_TREES_ERROR_TYPES] = {
+static const char *unpack_plumbing_errors[NB_UNPACK_TREES_WARNING_TYPES] = {
        /* ERROR_WOULD_OVERWRITE */
        "Entry '%s' would be overwritten by merge. Cannot merge.",
 
@@ -43,17 +43,20 @@ static const char *unpack_plumbing_errors[NB_UNPACK_TREES_ERROR_TYPES] = {
        /* ERROR_BIND_OVERLAP */
        "Entry '%s' overlaps with '%s'.  Cannot bind.",
 
-       /* ERROR_SPARSE_NOT_UPTODATE_FILE */
-       "Entry '%s' not uptodate. Cannot update sparse checkout.",
+       /* ERROR_WOULD_LOSE_SUBMODULE */
+       "Submodule '%s' cannot checkout new HEAD.",
 
-       /* ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN */
-       "Working tree file '%s' would be overwritten by sparse checkout update.",
+       /* NB_UNPACK_TREES_ERROR_TYPES; just a meta value */
+       "",
 
-       /* ERROR_WOULD_LOSE_ORPHANED_REMOVED */
-       "Working tree file '%s' would be removed by sparse checkout update.",
+       /* WARNING_SPARSE_NOT_UPTODATE_FILE */
+       "Path '%s' not uptodate; will not remove from working tree.",
 
-       /* ERROR_WOULD_LOSE_SUBMODULE */
-       "Submodule '%s' cannot checkout new HEAD.",
+       /* WARNING_SPARSE_UNMERGED_FILE */
+       "Path '%s' unmerged; will not remove from working tree.",
+
+       /* WARNING_SPARSE_ORPHANED_NOT_OVERWRITTEN */
+       "Path '%s' already present; will not overwrite with sparse update.",
 };
 
 #define ERRORMSG(o,type) \
@@ -168,15 +171,16 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
         */
        msgs[ERROR_BIND_OVERLAP] = _("Entry '%s' overlaps with '%s'.  Cannot bind.");
 
-       msgs[ERROR_SPARSE_NOT_UPTODATE_FILE] =
-               _("Cannot update sparse checkout: the following entries are not up to date:\n%s");
-       msgs[ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN] =
-               _("The following working tree files would be overwritten by sparse checkout update:\n%s");
-       msgs[ERROR_WOULD_LOSE_ORPHANED_REMOVED] =
-               _("The following working tree files would be removed by sparse checkout update:\n%s");
        msgs[ERROR_WOULD_LOSE_SUBMODULE] =
                _("Cannot update submodule:\n%s");
 
+       msgs[WARNING_SPARSE_NOT_UPTODATE_FILE] =
+               _("The following paths are not up to date and were left despite sparse patterns:\n%s");
+       msgs[WARNING_SPARSE_UNMERGED_FILE] =
+               _("The following paths are unmerged and were left despite sparse patterns:\n%s");
+       msgs[WARNING_SPARSE_ORPHANED_NOT_OVERWRITTEN] =
+               _("The following paths were already present and thus not updated despite sparse patterns:\n%s");
+
        opts->show_all_errors = 1;
        /* rejected paths may not have a static buffer */
        for (i = 0; i < ARRAY_SIZE(opts->unpack_rejects); i++)
@@ -226,7 +230,7 @@ static int add_rejected_path(struct unpack_trees_options *o,
 
        /*
         * Otherwise, insert in a list for future display by
-        * display_error_msgs()
+        * display_(error|warning)_msgs()
         */
        string_list_append(&o->unpack_rejects[e], path);
        return -1;
@@ -237,13 +241,16 @@ static int add_rejected_path(struct unpack_trees_options *o,
  */
 static void display_error_msgs(struct unpack_trees_options *o)
 {
-       int e, i;
-       int something_displayed = 0;
+       int e;
+       unsigned error_displayed = 0;
        for (e = 0; e < NB_UNPACK_TREES_ERROR_TYPES; e++) {
                struct string_list *rejects = &o->unpack_rejects[e];
+
                if (rejects->nr > 0) {
+                       int i;
                        struct strbuf path = STRBUF_INIT;
-                       something_displayed = 1;
+
+                       error_displayed = 1;
                        for (i = 0; i < rejects->nr; i++)
                                strbuf_addf(&path, "\t%s\n", rejects->items[i].string);
                        error(ERRORMSG(o, e), super_prefixed(path.buf));
@@ -251,10 +258,36 @@ static void display_error_msgs(struct unpack_trees_options *o)
                }
                string_list_clear(rejects, 0);
        }
-       if (something_displayed)
+       if (error_displayed)
                fprintf(stderr, _("Aborting\n"));
 }
 
+/*
+ * display all the warning messages stored in a nice way
+ */
+static void display_warning_msgs(struct unpack_trees_options *o)
+{
+       int e;
+       unsigned warning_displayed = 0;
+       for (e = NB_UNPACK_TREES_ERROR_TYPES + 1;
+            e < NB_UNPACK_TREES_WARNING_TYPES; e++) {
+               struct string_list *rejects = &o->unpack_rejects[e];
+
+               if (rejects->nr > 0) {
+                       int i;
+                       struct strbuf path = STRBUF_INIT;
+
+                       warning_displayed = 1;
+                       for (i = 0; i < rejects->nr; i++)
+                               strbuf_addf(&path, "\t%s\n", rejects->items[i].string);
+                       warning(ERRORMSG(o, e), super_prefixed(path.buf));
+                       strbuf_release(&path);
+               }
+               string_list_clear(rejects, 0);
+       }
+       if (warning_displayed)
+               fprintf(stderr, _("After fixing the above paths, you may want to run `git sparse-checkout reapply`.\n"));
+}
 static int check_submodule_move_head(const struct cache_entry *ce,
                                     const char *old_id,
                                     const char *new_id,
@@ -357,12 +390,12 @@ static void report_collided_checkout(struct index_state *index)
        string_list_clear(&list, 0);
 }
 
-static int check_updates(struct unpack_trees_options *o)
+static int check_updates(struct unpack_trees_options *o,
+                        struct index_state *index)
 {
        unsigned cnt = 0;
        int errs = 0;
        struct progress *progress;
-       struct index_state *index = &o->result;
        struct checkout state = CHECKOUT_INIT;
        int i;
 
@@ -423,9 +456,8 @@ static int check_updates(struct unpack_trees_options *o)
                                continue;
                        oid_array_append(&to_fetch, &ce->oid);
                }
-               if (to_fetch.nr)
-                       promisor_remote_get_direct(the_repository,
-                                                  to_fetch.oid, to_fetch.nr);
+               promisor_remote_get_direct(the_repository,
+                                          to_fetch.oid, to_fetch.nr);
                oid_array_clear(&to_fetch);
        }
        for (i = 0; i < index->cache_nr; i++) {
@@ -505,19 +537,39 @@ static int apply_sparse_checkout(struct index_state *istate,
                 * 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))
+               if (!(ce->ce_flags & CE_UPDATE) &&
+                   verify_uptodate_sparse(ce, o)) {
+                       ce->ce_flags &= ~CE_SKIP_WORKTREE;
                        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))
+               if (verify_absent_sparse(ce, WARNING_SPARSE_ORPHANED_NOT_OVERWRITTEN, o))
                        return -1;
                ce->ce_flags |= CE_UPDATE;
        }
        return 0;
 }
 
+static int warn_conflicted_path(struct index_state *istate,
+                               int i,
+                               struct unpack_trees_options *o)
+{
+       char *conflicting_path = istate->cache[i]->name;
+       int count = 0;
+
+       add_rejected_path(o, WARNING_SPARSE_UNMERGED_FILE, conflicting_path);
+
+       /* Find out how many higher stage entries at same path */
+       while (++count < istate->cache_nr &&
+              !strcmp(conflicting_path,
+                      istate->cache[i+count]->name))
+               /* do nothing */;
+       return count;
+}
+
 static inline int call_unpack_fn(const struct cache_entry * const *src,
                                 struct unpack_trees_options *o)
 {
@@ -1494,6 +1546,20 @@ static void mark_new_skip_worktree(struct pattern_list *pl,
        clear_ce_flags(istate, select_flag, skip_wt_flag, pl, show_progress);
 }
 
+static void populate_from_existing_patterns(struct unpack_trees_options *o,
+                                           struct pattern_list *pl)
+{
+       char *sparse = git_pathdup("info/sparse-checkout");
+
+       pl->use_cone_patterns = core_sparse_checkout_cone;
+       if (add_patterns_from_file_to_list(sparse, "", 0, pl, NULL) < 0)
+               o->skip_sparse_checkout = 1;
+       else
+               o->pl = pl;
+       free(sparse);
+}
+
+
 static int verify_absent(const struct cache_entry *,
                         enum unpack_trees_error_types,
                         struct unpack_trees_options *);
@@ -1508,22 +1574,18 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
        int i, ret;
        static struct cache_entry *dfc;
        struct pattern_list pl;
+       int free_pattern_list = 0;
 
        if (len > MAX_UNPACK_TREES)
                die("unpack_trees takes at most %d trees", MAX_UNPACK_TREES);
 
        trace_performance_enter();
-       memset(&pl, 0, sizeof(pl));
        if (!core_apply_sparse_checkout || !o->update)
                o->skip_sparse_checkout = 1;
        if (!o->skip_sparse_checkout && !o->pl) {
-               char *sparse = git_pathdup("info/sparse-checkout");
-               pl.use_cone_patterns = core_sparse_checkout_cone;
-               if (add_patterns_from_file_to_list(sparse, "", 0, &pl, NULL) < 0)
-                       o->skip_sparse_checkout = 1;
-               else
-                       o->pl = &pl;
-               free(sparse);
+               memset(&pl, 0, sizeof(pl));
+               free_pattern_list = 1;
+               populate_from_existing_patterns(o, &pl);
        }
 
        memset(&o->result, 0, sizeof(o->result));
@@ -1615,11 +1677,9 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
        }
 
        if (!o->skip_sparse_checkout) {
-               int empty_worktree = 1;
-
                /*
                 * 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
+                * If they 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->pl, &o->result,
@@ -1639,36 +1699,24 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
                         * correct CE_NEW_SKIP_WORKTREE
                         */
                        if (ce->ce_flags & CE_ADDED &&
-                           verify_absent(ce, ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o)) {
-                               if (!o->show_all_errors)
-                                       goto return_failed;
-                               ret = -1;
-                       }
-
-                       if (apply_sparse_checkout(&o->result, ce, o)) {
-                               if (!o->show_all_errors)
-                                       goto return_failed;
-                               ret = -1;
-                       }
-                       if (!ce_skip_worktree(ce))
-                               empty_worktree = 0;
+                           verify_absent(ce, WARNING_SPARSE_ORPHANED_NOT_OVERWRITTEN, o))
+                               ret = 1;
 
+                       if (apply_sparse_checkout(&o->result, ce, o))
+                               ret = 1;
                }
-               if (ret < 0)
-                       goto return_failed;
-               /*
-                * Sparse checkout is meant to narrow down checkout area
-                * but it does not make sense to narrow down to empty working
-                * tree. This is usually a mistake in sparse checkout rules.
-                * Do not allow users to do that.
-                */
-               if (o->result.cache_nr && empty_worktree) {
-                       ret = unpack_failed(o, "Sparse checkout leaves no entry on working directory");
-                       goto done;
+               if (ret == 1) {
+                       /*
+                        * Inability to sparsify or de-sparsify individual
+                        * paths is not an error, but just a warning.
+                        */
+                       if (o->show_all_errors)
+                               display_warning_msgs(o);
+                       ret = 0;
                }
        }
 
-       ret = check_updates(o) ? (-2) : 0;
+       ret = check_updates(o, &o->result) ? (-2) : 0;
        if (o->dst_index) {
                move_index_extensions(&o->result, o->src_index);
                if (!ret) {
@@ -1691,9 +1739,9 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
        o->src_index = NULL;
 
 done:
-       trace_performance_leave("unpack_trees");
-       if (!o->keep_pattern_list)
+       if (free_pattern_list)
                clear_pattern_list(&pl);
+       trace_performance_leave("unpack_trees");
        return ret;
 
 return_failed:
@@ -1706,6 +1754,74 @@ return_failed:
        goto done;
 }
 
+/*
+ * Update SKIP_WORKTREE bits according to sparsity patterns, and update
+ * working directory to match.
+ *
+ * CE_NEW_SKIP_WORKTREE is used internally.
+ */
+enum update_sparsity_result update_sparsity(struct unpack_trees_options *o)
+{
+       enum update_sparsity_result ret = UPDATE_SPARSITY_SUCCESS;
+       struct pattern_list pl;
+       int i;
+       unsigned old_show_all_errors;
+       int free_pattern_list = 0;
+
+       old_show_all_errors = o->show_all_errors;
+       o->show_all_errors = 1;
+
+       /* Sanity checks */
+       if (!o->update || o->index_only || o->skip_sparse_checkout)
+               BUG("update_sparsity() is for reflecting sparsity patterns in working directory");
+       if (o->src_index != o->dst_index || o->fn)
+               BUG("update_sparsity() called wrong");
+
+       trace_performance_enter();
+
+       /* If we weren't given patterns, use the recorded ones */
+       if (!o->pl) {
+               memset(&pl, 0, sizeof(pl));
+               free_pattern_list = 1;
+               populate_from_existing_patterns(o, &pl);
+               if (o->skip_sparse_checkout)
+                       goto skip_sparse_checkout;
+       }
+
+       /* Set NEW_SKIP_WORKTREE on existing entries. */
+       mark_all_ce_unused(o->src_index);
+       mark_new_skip_worktree(o->pl, o->src_index, 0,
+                              CE_NEW_SKIP_WORKTREE, o->verbose_update);
+
+       /* Then loop over entries and update/remove as needed */
+       ret = UPDATE_SPARSITY_SUCCESS;
+       for (i = 0; i < o->src_index->cache_nr; i++) {
+               struct cache_entry *ce = o->src_index->cache[i];
+
+
+               if (ce_stage(ce)) {
+                       /* -1 because for loop will increment by 1 */
+                       i += warn_conflicted_path(o->src_index, i, o) - 1;
+                       ret = UPDATE_SPARSITY_WARNINGS;
+                       continue;
+               }
+
+               if (apply_sparse_checkout(o->src_index, ce, o))
+                       ret = UPDATE_SPARSITY_WARNINGS;
+       }
+
+skip_sparse_checkout:
+       if (check_updates(o, o->src_index))
+               ret = UPDATE_SPARSITY_WORKTREE_UPDATE_FAILURES;
+
+       display_warning_msgs(o);
+       o->show_all_errors = old_show_all_errors;
+       if (free_pattern_list)
+               clear_pattern_list(&pl);
+       trace_performance_leave("update_sparsity");
+       return ret;
+}
+
 /* Here come the merge functions */
 
 static int reject_merge(const struct cache_entry *ce,
@@ -1790,7 +1906,7 @@ int verify_uptodate(const 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);
+       return verify_uptodate_1(ce, o, WARNING_SPARSE_NOT_UPTODATE_FILE);
 }
 
 /*
@@ -2028,11 +2144,7 @@ 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)
-               orphaned_error = ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN;
-
-       return verify_absent_1(ce, orphaned_error, o);
+       return verify_absent_1(ce, error_type, o);
 }
 
 static int merged_entry(const struct cache_entry *ce,