Skip to content

Commit bdecd9d

Browse files
moygitster
authored andcommitted
More permissive "git-rm --cached" behavior without -f.
In the previous behavior, "git-rm --cached" (without -f) had the same restriction as "git-rm". This forced the user to use the -f flag in situations which weren't actually dangerous, like: $ git add foo # oops, I didn't want this $ git rm --cached foo # back to initial situation Previously, the index had to match the file *and* the HEAD. With --cached, the index must now match the file *or* the HEAD. The behavior without --cached is unchanged, but provides better error messages. Signed-off-by: Matthieu Moy <Matthieu.Moy@imag.fr> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 1701872 commit bdecd9d

File tree

3 files changed

+62
-7
lines changed

3 files changed

+62
-7
lines changed

Documentation/git-rm.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ DESCRIPTION
1414
Remove files from the working tree and from the index. The
1515
files have to be identical to the tip of the branch, and no
1616
updates to its contents must have been placed in the staging
17-
area (aka index).
17+
area (aka index). When --cached is given, the staged content has to
18+
match either the tip of the branch *or* the file on disk.
1819

1920

2021
OPTIONS

builtin-rm.c

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ static int remove_file(const char *name)
4646
return ret;
4747
}
4848

49-
static int check_local_mod(unsigned char *head)
49+
static int check_local_mod(unsigned char *head, int index_only)
5050
{
5151
/* items in list are already sorted in the cache order,
5252
* so we could do this a lot more efficiently by using
@@ -65,6 +65,8 @@ static int check_local_mod(unsigned char *head)
6565
const char *name = list.name[i];
6666
unsigned char sha1[20];
6767
unsigned mode;
68+
int local_changes = 0;
69+
int staged_changes = 0;
6870

6971
pos = cache_name_pos(name, strlen(name));
7072
if (pos < 0)
@@ -87,14 +89,32 @@ static int check_local_mod(unsigned char *head)
8789
continue;
8890
}
8991
if (ce_match_stat(ce, &st, 0))
90-
errs = error("'%s' has local modifications "
91-
"(hint: try -f)", ce->name);
92+
local_changes = 1;
9293
if (no_head
9394
|| get_tree_entry(head, name, sha1, &mode)
9495
|| ce->ce_mode != create_ce_mode(mode)
9596
|| hashcmp(ce->sha1, sha1))
96-
errs = error("'%s' has changes staged in the index "
97-
"(hint: try -f)", name);
97+
staged_changes = 1;
98+
99+
if (local_changes && staged_changes)
100+
errs = error("'%s' has staged content different "
101+
"from both the file and the HEAD\n"
102+
"(use -f to force removal)", name);
103+
else if (!index_only) {
104+
/* It's not dangerous to git-rm --cached a
105+
* file if the index matches the file or the
106+
* HEAD, since it means the deleted content is
107+
* still available somewhere.
108+
*/
109+
if (staged_changes)
110+
errs = error("'%s' has changes staged in the index\n"
111+
"(use --cached to keep the file, "
112+
"or -f to force removal)", name);
113+
if (local_changes)
114+
errs = error("'%s' has local modifications\n"
115+
"(use --cached to keep the file, "
116+
"or -f to force removal)", name);
117+
}
98118
}
99119
return errs;
100120
}
@@ -192,7 +212,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
192212
unsigned char sha1[20];
193213
if (get_sha1("HEAD", sha1))
194214
hashclr(sha1);
195-
if (check_local_mod(sha1))
215+
if (check_local_mod(sha1, index_only))
196216
exit(1);
197217
}
198218

t/t3600-rm.sh

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,40 @@ test_expect_success \
4545
'Test that git rm foo succeeds' \
4646
'git rm --cached foo'
4747

48+
test_expect_success \
49+
'Test that git rm --cached foo succeeds if the index matches the file' \
50+
'echo content > foo
51+
git add foo
52+
git rm --cached foo'
53+
54+
test_expect_success \
55+
'Test that git rm --cached foo succeeds if the index matches the file' \
56+
'echo content > foo
57+
git add foo
58+
git commit -m foo
59+
echo "other content" > foo
60+
git rm --cached foo'
61+
62+
test_expect_failure \
63+
'Test that git rm --cached foo fails if the index matches neither the file nor HEAD' \
64+
'echo content > foo
65+
git add foo
66+
git commit -m foo
67+
echo "other content" > foo
68+
git add foo
69+
echo "yet another content" > foo
70+
git rm --cached foo'
71+
72+
test_expect_success \
73+
'Test that git rm --cached -f foo works in case where --cached only did not' \
74+
'echo content > foo
75+
git add foo
76+
git commit -m foo
77+
echo "other content" > foo
78+
git add foo
79+
echo "yet another content" > foo
80+
git rm --cached -f foo'
81+
4882
test_expect_success \
4983
'Post-check that foo exists but is not in index after git rm foo' \
5084
'[ -f foo ] && ! git ls-files --error-unmatch foo'

0 commit comments

Comments
 (0)