This command can be used to be sure the sparse index works efficiently,
though it does not require enabling the sparse index feature via the
`index.sparse=true` configuration.
++
+To prevent accidental deletion of worktree files, the `clean` subcommand
+will not delete any files without the `-f` or `--force` option, unless
+the `clean.requireForce` config option is set to `false`.
++
+The `--dry-run` option will list the directories that would be removed
+without deleting them. Running in this mode can be helpful to predict the
+behavior of the clean comand or to determine which kinds of files are left
+in the sparse directories.
'disable'::
Disable the `core.sparseCheckout` config setting, and restore the
};
static const char *msg_remove = N_("Removing %s\n");
+static const char *msg_would_remove = N_("Would remove %s\n");
static int sparse_checkout_clean(int argc, const char **argv,
const char *prefix,
struct strbuf full_path = STRBUF_INIT;
const char *msg = msg_remove;
size_t worktree_len;
+ int force = 0, dry_run = 0;
+ int require_force = 1;
struct option builtin_sparse_checkout_clean_options[] = {
+ OPT__DRY_RUN(&dry_run, N_("dry run")),
+ OPT__FORCE(&force, N_("force"), PARSE_OPT_NOCOMPLETE),
OPT_END(),
};
builtin_sparse_checkout_clean_options,
builtin_sparse_checkout_clean_usage, 0);
+ repo_config_get_bool(repo, "clean.requireforce", &require_force);
+ if (require_force && !force && !dry_run)
+ die(_("for safety, refusing to clean without one of --force or --dry-run"));
+
+ if (dry_run)
+ msg = msg_would_remove;
+
if (repo_read_index(repo) < 0)
die(_("failed to read index"));
printf(msg, ce->name);
- if (remove_dir_recursively(&full_path, 0))
+ if (dry_run <= 0 &&
+ remove_dir_recursively(&full_path, 0))
warning_errno(_("failed to remove '%s'"), ce->name);
}
touch repo/deep/deeper2/file &&
touch repo/folder1/file &&
+ test_must_fail git -C repo sparse-checkout clean 2>err &&
+ grep "refusing to clean" err &&
+
+ git -C repo config clean.requireForce true &&
+ test_must_fail git -C repo sparse-checkout clean 2>err &&
+ grep "refusing to clean" err &&
+
+ cat >expect <<-\EOF &&
+ Would remove deep/deeper2/
+ Would remove folder1/
+ EOF
+
+ git -C repo sparse-checkout clean --dry-run >out &&
+ test_cmp expect out &&
+ test_path_exists repo/deep/deeper2 &&
+ test_path_exists repo/folder1 &&
+
cat >expect <<-\EOF &&
Removing deep/deeper2/
Removing folder1/
EOF
- git -C repo sparse-checkout clean >out &&
+ git -C repo sparse-checkout clean -f >out &&
test_cmp expect out &&
test_path_is_missing repo/deep/deeper2 &&
git -C repo sparse-checkout set --cone deep/deeper1 &&
mkdir repo/folder2 &&
+ # The previous test case checked the -f option, so
+ # test the config option in this one.
+ git -C repo config clean.requireForce false &&
+
# create an untracked file and a modified file
touch repo/folder2/file &&
echo dirty >repo/folder2/a &&
test_must_be_empty out
'
+test_expect_success 'clean with merge conflict status' '
+ git clone repo clean-merge &&
+
+ echo dirty >clean-merge/deep/deeper2/a &&
+ touch clean-merge/folder2/extra &&
+
+ cat >input <<-EOF &&
+ 0 $ZERO_OID folder1/a
+ 100644 $(git -C clean-merge rev-parse HEAD:folder1/a) 1 folder1/a
+ EOF
+ git -C clean-merge update-index --index-info <input &&
+
+ git -C clean-merge sparse-checkout set deep/deeper1 &&
+
+ test_must_fail git -C clean-merge sparse-checkout clean -f 2>err &&
+ grep "failed to convert index to a sparse index" err &&
+
+ mkdir -p clean-merge/folder1/ &&
+ echo merged >clean-merge/folder1/a &&
+ git -C clean-merge add --sparse folder1/a &&
+
+ # deletes folder2/ but leaves staged change in folder1
+ # and dirty change in deep/deeper2/
+ cat >expect <<-\EOF &&
+ Removing folder2/
+ EOF
+
+ git -C clean-merge sparse-checkout clean -f >out &&
+ test_cmp expect out
+'
+
test_done