Skip to content

Commit 3f1adf9

Browse files
committed
sparse-checkout: match some 'clean' behavior
The 'git sparse-checkout clean' subcommand is somewhat similar to 'git clean' in that it will delete files that should not be in the worktree. The big difference is that it focuses on the directories that should not be in the worktree due to cone-mode sparse-checkout. It also does not discriminate in the kinds of files and focuses on deleting entire directories. However, there are some restrictions that would be good to bring over from 'git clean', specifically how it refuses to do anything without the '-f'/'--force' or '-n'/'--dry-run' arguments. The 'clean.requireForce' config can be set to 'false' to imply '--force'. Add this behavior to avoid accidental deletion of files that cannot be recovered from Git. Signed-off-by: Derrick Stolee <[email protected]>
1 parent ab99f92 commit 3f1adf9

File tree

3 files changed

+52
-6
lines changed

3 files changed

+52
-6
lines changed

Documentation/git-sparse-checkout.adoc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,15 @@ all sparsity paths.
119119
This command can be used to be sure the sparse index works
120120
efficiently, though it does not require enabling the sparse index
121121
feature via the `index.sparse=true` configuration.
122+
+
123+
To prevent accidental deletion of worktree files, the `clean` subcommand
124+
will not delete any files without the `-f` or `--force` option, unless
125+
the `clean.requireForce` config option is set to `false`.
126+
+
127+
The `--dry-run` option will list the directories that would be removed
128+
without deleting them. Running in this mode can be helpful to predict the
129+
behavior of the clean comand or to determine which kinds of files are left
130+
in the sparse directories.
122131

123132
'disable'::
124133
Disable the `core.sparseCheckout` config setting, and restore the

builtin/sparse-checkout.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -931,6 +931,7 @@ static char const * const builtin_sparse_checkout_clean_usage[] = {
931931
};
932932

933933
static const char *msg_remove = N_("Removing %s\n");
934+
static const char *msg_would_remove = N_("Would remove %s\n");
934935

935936
static int sparse_checkout_clean(int argc, const char **argv,
936937
const char *prefix,
@@ -939,8 +940,12 @@ static int sparse_checkout_clean(int argc, const char **argv,
939940
struct strbuf full_path = STRBUF_INIT;
940941
const char *msg = msg_remove;
941942
size_t worktree_len;
943+
int force = 0, dry_run = 0;
944+
int require_force = 1;
942945

943946
struct option builtin_sparse_checkout_clean_options[] = {
947+
OPT__DRY_RUN(&dry_run, N_("dry run")),
948+
OPT__FORCE(&force, N_("force"), PARSE_OPT_NOCOMPLETE),
944949
OPT_END(),
945950
};
946951

@@ -954,6 +959,13 @@ static int sparse_checkout_clean(int argc, const char **argv,
954959
builtin_sparse_checkout_clean_options,
955960
builtin_sparse_checkout_clean_usage, 0);
956961

962+
repo_config_get_bool(repo, "clean.requireforce", &require_force);
963+
if (require_force && !force && !dry_run)
964+
die(_("clean.requireForce is true and -f not given: refusing to clean"));
965+
966+
if (dry_run)
967+
msg = msg_would_remove;
968+
957969
if (repo_read_index(repo) < 0)
958970
die(_("failed to read index"));
959971

@@ -977,7 +989,8 @@ static int sparse_checkout_clean(int argc, const char **argv,
977989

978990
printf(msg, ce->name);
979991

980-
if (remove_dir_recursively(&full_path, 0))
992+
if (dry_run <= 0 &&
993+
remove_dir_recursively(&full_path, 0))
981994
warning_errno(_("failed to remove '%s'"), ce->name);
982995
}
983996

t/t1091-sparse-checkout-builtin.sh

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,12 +1056,25 @@ test_expect_success 'clean' '
10561056
touch repo/deep/deeper2/file &&
10571057
touch repo/folder1/file &&
10581058
1059+
test_must_fail git -C repo sparse-checkout clean 2>err &&
1060+
grep "refusing to clean" err &&
1061+
1062+
cat >expect <<-\EOF &&
1063+
Would remove deep/deeper2/
1064+
Would remove folder1/
1065+
EOF
1066+
1067+
git -C repo sparse-checkout clean --dry-run >out &&
1068+
test_cmp expect out &&
1069+
test_path_exists repo/deep/deeper2 &&
1070+
test_path_exists repo/folder1 &&
1071+
10591072
cat >expect <<-\EOF &&
10601073
Removing deep/deeper2/
10611074
Removing folder1/
10621075
EOF
10631076
1064-
git -C repo sparse-checkout clean >out &&
1077+
git -C repo sparse-checkout clean -f >out &&
10651078
test_cmp expect out &&
10661079
10671080
test_path_is_missing repo/deep/deeper2 &&
@@ -1077,16 +1090,27 @@ test_expect_success 'clean with staged sparse change' '
10771090
10781091
git -C repo add --sparse folder1/file &&
10791092
1093+
cat >expect <<-\EOF &&
1094+
Would remove deep/deeper2/
1095+
EOF
1096+
1097+
git -C repo sparse-checkout clean --dry-run >out &&
1098+
test_cmp expect out &&
1099+
test_path_exists repo/deep/deeper2 &&
1100+
test_path_exists repo/folder1 &&
1101+
test_path_exists repo/folder2 &&
1102+
10801103
# deletes deep/deeper2/ but leaves folder1/ and folder2/
10811104
cat >expect <<-\EOF &&
10821105
Removing deep/deeper2/
10831106
EOF
10841107
1085-
git -C repo sparse-checkout clean >out &&
1108+
git -C repo sparse-checkout clean -f >out &&
10861109
test_cmp expect out &&
10871110
10881111
test_path_is_missing repo/deep/deeper2 &&
1089-
test_path_exists repo/folder1
1112+
test_path_exists repo/folder1 &&
1113+
test_path_exists repo/folder2
10901114
'
10911115

10921116
test_expect_success 'clean with merge conflict status' '
@@ -1103,7 +1127,7 @@ test_expect_success 'clean with merge conflict status' '
11031127
11041128
git -C clean-merge sparse-checkout set deep/deeper1 &&
11051129
1106-
test_must_fail git -C clean-merge sparse-checkout clean 2>err &&
1130+
test_must_fail git -C clean-merge sparse-checkout clean -f 2>err &&
11071131
grep "failed to convert index to a sparse index" err &&
11081132
11091133
mkdir -p clean-merge/folder1/ &&
@@ -1116,7 +1140,7 @@ test_expect_success 'clean with merge conflict status' '
11161140
Removing folder2/
11171141
EOF
11181142
1119-
git -C clean-merge sparse-checkout clean >out &&
1143+
git -C clean-merge sparse-checkout clean -f >out &&
11201144
test_cmp expect out
11211145
'
11221146

0 commit comments

Comments
 (0)