]> git.ipfire.org Git - thirdparty/git.git/commitdiff
mv: add check_dir_in_index() and solve general dir check issue
authorShaoxuan Yuan <shaoxuan.yuan02@gmail.com>
Thu, 30 Jun 2022 02:37:37 +0000 (10:37 +0800)
committerJunio C Hamano <gitster@pobox.com>
Fri, 1 Jul 2022 21:50:16 +0000 (14:50 -0700)
Originally, moving a <source> directory which is not on-disk due
to its existence outside of sparse-checkout cone, "giv mv" command
errors out with "bad source".

Add a helper check_dir_in_index() function to see if a directory
name exists in the index. Also add a SKIP_WORKTREE_DIR bit to mark
such directories.

Change the checking logic, so that such <source> directory makes
"giv mv" command warns with "advise_on_updating_sparse_paths()"
instead of "bad source"; also user now can supply a "--sparse" flag so
this operation can be carried out successfully.

Helped-by: Victoria Dye <vdye@github.com>
Helped-by: Derrick Stolee <derrickstolee@github.com>
Signed-off-by: Shaoxuan Yuan <shaoxuan.yuan02@gmail.com>
Acked-by: Derrick Stolee <derrickstolee@github.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin/mv.c
t/t1092-sparse-checkout-compatibility.sh
t/t7002-mv-sparse-checkout.sh

index b805a0d0f6e87c8e5ae5308db9d516909f6e242f..2a38e2af4664dc9e6920862b4c029810ebcb03c8 100644 (file)
@@ -25,6 +25,7 @@ enum update_mode {
        WORKING_DIRECTORY = (1 << 1),
        INDEX = (1 << 2),
        SPARSE = (1 << 3),
+       SKIP_WORKTREE_DIR = (1 << 4),
 };
 
 #define DUP_BASENAME 1
@@ -123,6 +124,36 @@ static int index_range_of_same_dir(const char *src, int length,
        return last - first;
 }
 
+/*
+ * Check if an out-of-cone directory should be in the index. Imagine this case
+ * that all the files under a directory are marked with 'CE_SKIP_WORKTREE' bit
+ * and thus the directory is sparsified.
+ *
+ * Return 0 if such directory exist (i.e. with any of its contained files not
+ * marked with CE_SKIP_WORKTREE, the directory would be present in working tree).
+ * Return 1 otherwise.
+ */
+static int check_dir_in_index(const char *name)
+{
+       const char *with_slash = add_slash(name);
+       int length = strlen(with_slash);
+
+       int pos = cache_name_pos(with_slash, length);
+       const struct cache_entry *ce;
+
+       if (pos < 0) {
+               pos = -pos - 1;
+               if (pos >= the_index.cache_nr)
+                       return 1;
+               ce = active_cache[pos];
+               if (strncmp(with_slash, ce->name, length))
+                       return 1;
+               if (ce_skip_worktree(ce))
+                       return 0;
+       }
+       return 1;
+}
+
 int cmd_mv(int argc, const char **argv, const char *prefix)
 {
        int i, flags, gitmodules_modified = 0;
@@ -184,7 +215,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
        /* Checking */
        for (i = 0; i < argc; i++) {
                const char *src = source[i], *dst = destination[i];
-               int length, src_is_dir;
+               int length;
                const char *bad = NULL;
                int skip_sparse = 0;
 
@@ -198,12 +229,17 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 
                        pos = cache_name_pos(src, length);
                        if (pos < 0) {
+                               const char *src_w_slash = add_slash(src);
+                               if (!path_in_sparse_checkout(src_w_slash, &the_index) &&
+                                   !check_dir_in_index(src)) {
+                                       modes[i] |= SKIP_WORKTREE_DIR;
+                                       goto dir_check;
+                               }
                                /* only error if existence is expected. */
                                if (!(modes[i] & SPARSE))
                                        bad = _("bad source");
                                goto act_on_entry;
                        }
-
                        ce = active_cache[pos];
                        if (!ce_skip_worktree(ce)) {
                                bad = _("bad source");
@@ -230,12 +266,14 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
                        bad = _("can not move directory into itself");
                        goto act_on_entry;
                }
-               if ((src_is_dir = S_ISDIR(st.st_mode))
+               if (S_ISDIR(st.st_mode)
                    && lstat(dst, &st) == 0) {
                        bad = _("cannot move directory over file");
                        goto act_on_entry;
                }
-               if (src_is_dir) {
+
+dir_check:
+               if (S_ISDIR(st.st_mode)) {
                        int j, dst_len, n;
                        int first = cache_name_pos(src, length), last;
 
@@ -369,7 +407,7 @@ remove_entry:
                        printf(_("Renaming %s to %s\n"), src, dst);
                if (show_only)
                        continue;
-               if (!(mode & (INDEX | SPARSE)) &&
+               if (!(mode & (INDEX | SPARSE | SKIP_WORKTREE_DIR)) &&
                    rename(src, dst) < 0) {
                        if (ignore_errors)
                                continue;
@@ -384,7 +422,7 @@ remove_entry:
                                                              1);
                }
 
-               if (mode & (WORKING_DIRECTORY))
+               if (mode & (WORKING_DIRECTORY | SKIP_WORKTREE_DIR))
                        continue;
 
                pos = cache_name_pos(src, strlen(src));
index 5eef799e2552cde2ea90715bd9038b64c5af0d9a..763c6cc684687a3aee6b915dc676f4425065eeeb 100755 (executable)
@@ -1828,7 +1828,7 @@ test_expect_success 'checkout behaves oddly with df-conflict-2' '
        test_cmp full-checkout-err sparse-index-err
 '
 
-test_expect_failure 'mv directory from out-of-cone to in-cone' '
+test_expect_success 'mv directory from out-of-cone to in-cone' '
        init_repos &&
 
        # <source> as a sparse directory (or SKIP_WORKTREE_DIR without enabling
index 6d2fb4f8d2634138bb6091af14c57c94f788f941..71fe29690fd1226082f14df5b8e17cef76659e3f 100755 (executable)
@@ -219,7 +219,7 @@ test_expect_success 'refuse to move file to non-skip-worktree sparse path' '
        test_cmp expect stderr
 '
 
-test_expect_failure 'refuse to move out-of-cone directory without --sparse' '
+test_expect_success 'refuse to move out-of-cone directory without --sparse' '
        test_when_finished "cleanup_sparse_checkout" &&
        setup_sparse_checkout &&
 
@@ -230,7 +230,7 @@ test_expect_failure 'refuse to move out-of-cone directory without --sparse' '
        test_cmp expect stderr
 '
 
-test_expect_failure 'can move out-of-cone directory with --sparse' '
+test_expect_success 'can move out-of-cone directory with --sparse' '
        test_when_finished "cleanup_sparse_checkout" &&
        setup_sparse_checkout &&