Skip to content

Commit

Permalink
More permissive "git-rm --cached" behavior without -f.
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Matthieu Moy authored and Junio C Hamano committed Jul 14, 2007
1 parent 1701872 commit bdecd9d
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 7 deletions.
3 changes: 2 additions & 1 deletion Documentation/git-rm.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ DESCRIPTION
Remove files from the working tree and from the index. The
files have to be identical to the tip of the branch, and no
updates to its contents must have been placed in the staging
area (aka index).
area (aka index). When --cached is given, the staged content has to
match either the tip of the branch *or* the file on disk.


OPTIONS
Expand Down
32 changes: 26 additions & 6 deletions builtin-rm.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ static int remove_file(const char *name)
return ret;
}

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

pos = cache_name_pos(name, strlen(name));
if (pos < 0)
Expand All @@ -87,14 +89,32 @@ static int check_local_mod(unsigned char *head)
continue;
}
if (ce_match_stat(ce, &st, 0))
errs = error("'%s' has local modifications "
"(hint: try -f)", ce->name);
local_changes = 1;
if (no_head
|| get_tree_entry(head, name, sha1, &mode)
|| ce->ce_mode != create_ce_mode(mode)
|| hashcmp(ce->sha1, sha1))
errs = error("'%s' has changes staged in the index "
"(hint: try -f)", name);
staged_changes = 1;

if (local_changes && staged_changes)
errs = error("'%s' has staged content different "
"from both the file and the HEAD\n"
"(use -f to force removal)", name);
else if (!index_only) {
/* It's not dangerous to git-rm --cached a
* file if the index matches the file or the
* HEAD, since it means the deleted content is
* still available somewhere.
*/
if (staged_changes)
errs = error("'%s' has changes staged in the index\n"
"(use --cached to keep the file, "
"or -f to force removal)", name);
if (local_changes)
errs = error("'%s' has local modifications\n"
"(use --cached to keep the file, "
"or -f to force removal)", name);
}
}
return errs;
}
Expand Down Expand Up @@ -192,7 +212,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
unsigned char sha1[20];
if (get_sha1("HEAD", sha1))
hashclr(sha1);
if (check_local_mod(sha1))
if (check_local_mod(sha1, index_only))
exit(1);
}

Expand Down
34 changes: 34 additions & 0 deletions t/t3600-rm.sh
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,40 @@ test_expect_success \
'Test that git rm foo succeeds' \
'git rm --cached foo'

test_expect_success \
'Test that git rm --cached foo succeeds if the index matches the file' \
'echo content > foo
git add foo
git rm --cached foo'

test_expect_success \
'Test that git rm --cached foo succeeds if the index matches the file' \
'echo content > foo
git add foo
git commit -m foo
echo "other content" > foo
git rm --cached foo'

test_expect_failure \
'Test that git rm --cached foo fails if the index matches neither the file nor HEAD' \
'echo content > foo
git add foo
git commit -m foo
echo "other content" > foo
git add foo
echo "yet another content" > foo
git rm --cached foo'

test_expect_success \
'Test that git rm --cached -f foo works in case where --cached only did not' \
'echo content > foo
git add foo
git commit -m foo
echo "other content" > foo
git add foo
echo "yet another content" > foo
git rm --cached -f foo'

test_expect_success \
'Post-check that foo exists but is not in index after git rm foo' \
'[ -f foo ] && ! git ls-files --error-unmatch foo'
Expand Down

0 comments on commit bdecd9d

Please sign in to comment.