Skip to content

Commit d5f4b82

Browse files
matheustavaresgitster
authored andcommitted
rm: honor sparse checkout patterns
`git add` refrains from adding or updating index entries that are outside the current sparse checkout, but `git rm` doesn't follow the same restriction. This is somewhat counter-intuitive and inconsistent. So make `rm` honor the sparsity rules and advise on how to remove SKIP_WORKTREE entries just like `add` does. Also add some tests for the new behavior. Suggested-by: Elijah Newren <[email protected]> Signed-off-by: Matheus Tavares <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent a20f704 commit d5f4b82

File tree

5 files changed

+108
-19
lines changed

5 files changed

+108
-19
lines changed

Documentation/config/advice.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ advice.*::
120120
Advice shown if a user runs the add command without providing
121121
the pathspec parameter.
122122
updateSparsePath::
123-
Advice shown when linkgit:git-add[1] is asked to update index
124-
entries outside the current sparse checkout.
123+
Advice shown when either linkgit:git-add[1] or linkgit:git-rm[1]
124+
is asked to update index entries outside the current sparse
125+
checkout.
125126
--

Documentation/git-rm.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ branch, and no updates to their contents can be staged in the index,
2323
though that default behavior can be overridden with the `-f` option.
2424
When `--cached` is given, the staged content has to
2525
match either the tip of the branch or the file on disk,
26-
allowing the file to be removed from just the index.
26+
allowing the file to be removed from just the index. When
27+
sparse-checkouts are in use (see linkgit:git-sparse-checkout[1]),
28+
`git rm` will only remove paths within the sparse-checkout patterns.
2729

2830

2931
OPTIONS

builtin/rm.c

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66
#define USE_THE_INDEX_COMPATIBILITY_MACROS
77
#include "builtin.h"
8+
#include "advice.h"
89
#include "config.h"
910
#include "lockfile.h"
1011
#include "dir.h"
@@ -254,7 +255,7 @@ static struct option builtin_rm_options[] = {
254255
int cmd_rm(int argc, const char **argv, const char *prefix)
255256
{
256257
struct lock_file lock_file = LOCK_INIT;
257-
int i;
258+
int i, ret = 0;
258259
struct pathspec pathspec;
259260
char *seen;
260261

@@ -295,6 +296,8 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
295296

296297
for (i = 0; i < active_nr; i++) {
297298
const struct cache_entry *ce = active_cache[i];
299+
if (ce_skip_worktree(ce))
300+
continue;
298301
if (!ce_path_match(&the_index, ce, &pathspec, seen))
299302
continue;
300303
ALLOC_GROW(list.entry, list.nr + 1, list.alloc);
@@ -308,24 +311,34 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
308311
if (pathspec.nr) {
309312
const char *original;
310313
int seen_any = 0;
314+
char *skip_worktree_seen = NULL;
315+
struct string_list only_match_skip_worktree = STRING_LIST_INIT_NODUP;
316+
311317
for (i = 0; i < pathspec.nr; i++) {
312318
original = pathspec.items[i].original;
313-
if (!seen[i]) {
314-
if (!ignore_unmatch) {
315-
die(_("pathspec '%s' did not match any files"),
316-
original);
317-
}
318-
}
319-
else {
319+
if (seen[i])
320320
seen_any = 1;
321-
}
321+
else if (ignore_unmatch)
322+
continue;
323+
else if (matches_skip_worktree(&pathspec, i, &skip_worktree_seen))
324+
string_list_append(&only_match_skip_worktree, original);
325+
else
326+
die(_("pathspec '%s' did not match any files"), original);
327+
322328
if (!recursive && seen[i] == MATCHED_RECURSIVELY)
323329
die(_("not removing '%s' recursively without -r"),
324330
*original ? original : ".");
325331
}
326332

333+
if (only_match_skip_worktree.nr) {
334+
advise_on_updating_sparse_paths(&only_match_skip_worktree);
335+
ret = 1;
336+
}
337+
free(skip_worktree_seen);
338+
string_list_clear(&only_match_skip_worktree, 0);
339+
327340
if (!seen_any)
328-
exit(0);
341+
exit(ret);
329342
}
330343

331344
if (!index_only)
@@ -405,5 +418,5 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
405418
COMMIT_LOCK | SKIP_IF_UNCHANGED))
406419
die(_("Unable to write new index file"));
407420

408-
return 0;
421+
return ret;
409422
}

t/t3602-rm-sparse-checkout.sh

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#!/bin/sh
2+
3+
test_description='git rm in sparse checked out working trees'
4+
5+
. ./test-lib.sh
6+
7+
test_expect_success 'setup' "
8+
mkdir -p sub/dir &&
9+
touch a b c sub/d sub/dir/e &&
10+
git add -A &&
11+
git commit -m files &&
12+
13+
cat >sparse_error_header <<-EOF &&
14+
The following pathspecs didn't match any eligible path, but they do match index
15+
entries outside the current sparse checkout:
16+
EOF
17+
18+
cat >sparse_hint <<-EOF &&
19+
hint: Disable or modify the sparsity rules if you intend to update such entries.
20+
hint: Disable this message with \"git config advice.updateSparsePath false\"
21+
EOF
22+
23+
echo b | cat sparse_error_header - >sparse_entry_b_error &&
24+
cat sparse_entry_b_error sparse_hint >b_error_and_hint
25+
"
26+
27+
for opt in "" -f --dry-run
28+
do
29+
test_expect_success "rm${opt:+ $opt} does not remove sparse entries" '
30+
git sparse-checkout set a &&
31+
test_must_fail git rm $opt b 2>stderr &&
32+
test_cmp b_error_and_hint stderr &&
33+
git ls-files --error-unmatch b
34+
'
35+
done
36+
37+
test_expect_success 'recursive rm does not remove sparse entries' '
38+
git reset --hard &&
39+
git sparse-checkout set sub/dir &&
40+
git rm -r sub &&
41+
git status --porcelain -uno >actual &&
42+
echo "D sub/dir/e" >expected &&
43+
test_cmp expected actual
44+
'
45+
46+
test_expect_success 'rm obeys advice.updateSparsePath' '
47+
git reset --hard &&
48+
git sparse-checkout set a &&
49+
test_must_fail git -c advice.updateSparsePath=false rm b 2>stderr &&
50+
test_cmp sparse_entry_b_error stderr
51+
'
52+
53+
test_expect_success 'do not advice about sparse entries when they do not match the pathspec' '
54+
git reset --hard &&
55+
git sparse-checkout set a &&
56+
test_must_fail git rm nonexistent 2>stderr &&
57+
grep "fatal: pathspec .nonexistent. did not match any files" stderr &&
58+
! grep -F -f sparse_error_header stderr
59+
'
60+
61+
test_expect_success 'do not warn about sparse entries when pathspec matches dense entries' '
62+
git reset --hard &&
63+
git sparse-checkout set a &&
64+
git rm "[ba]" 2>stderr &&
65+
test_must_be_empty stderr &&
66+
git ls-files --error-unmatch b &&
67+
test_must_fail git ls-files --error-unmatch a
68+
'
69+
70+
test_expect_success 'do not warn about sparse entries with --ignore-unmatch' '
71+
git reset --hard &&
72+
git sparse-checkout set a &&
73+
git rm --ignore-unmatch b 2>stderr &&
74+
test_must_be_empty stderr &&
75+
git ls-files --error-unmatch b
76+
'
77+
78+
test_done

t/t7011-skip-worktree-reading.sh

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -132,11 +132,6 @@ test_expect_success 'diff-files does not examine skip-worktree dirty entries' '
132132
test -z "$(git diff-files -- one)"
133133
'
134134

135-
test_expect_success 'git-rm succeeds on skip-worktree absent entries' '
136-
setup_absent &&
137-
git rm 1
138-
'
139-
140135
test_expect_success 'commit on skip-worktree absent entries' '
141136
git reset &&
142137
setup_absent &&

0 commit comments

Comments
 (0)