2022:
This should no longer be an issue with Git 2.35 (Q1 2022)
No more "fatal: Unable to read current working directory: No such file or directory
"
2019: original answer:
This is followed in this June 2018 thread, where it is reported as a "git rm bug
"
TLDR; it is not a bug.
No Git command should behave in such a way as to leave the tree in a state when moving from commit X
to Y
that you wouldn't get the same Y
if you re-cloned.
On to the thread:
OVERVIEW
"git rm
" will remove more files than specified. This is either a bug or undocumented behavior (not in the man pages).
SETUP
In a git repository, create an empty directory OR a chain of empty directories
$ mkdir -p path/to/some/
Create a file in the deepest directory and add it to tracking
$ touch path/to/some/file
$ git add path/to/some/file
$ git commit -m 'add path/to/some/file'
THE BUG
Run 'git rm
' on the tracked file.
EXPECTED BEHAVIOR
$ git rm path/to/some/file
rm 'path/to/some/file'
$ ls path
to/
$ ls path/to
some/
Note that path/
, path/to/
, and path/to/some/
still exist.
ACTUAL BEHAVIOR
$ git rm path/to/some/file
rm 'path/to/some/file'
$ ls path
ls: cannot access 'path': No such file or directory
The entire chain of empty directories is removed, despite the fact the git outputs only "rm 'path/to/some/file'
".
This ONLY occurs when all the directories in the chain are empty after the tracked file has been removed.
This behavior is NOT documented in the man pages.
I propose that 'rmdir' statements are added to 'git rm' output, or that the man pages be updated to reflect this behavior.
The general principle is:
Git cannot track empty directories.
As that was the only content in that whole hierarchy, the entire hierarchy had to be deleted.
It looks like this behavior has been in place for many years, since d9b814cc97 ("Add builtin "git rm" command", 2006-05-19, Git v1.4.0-rc1).
Interestingly, Linus noted in the commit message that the removal of leading directories was different than when git-rm
was a shell script.
And he wondered if it might be worth having an option to control
that behavior.
I imagine that most users either want the current behavior
or they rarely run across this and are surprised, given how
long git rm
has worked this way.
It's also consistent with other parts of Git that remove files. E.g.,
"git checkout
" to a state that does not have the file will remove the
leading directories (if they're empty, of course).
More generally:
I am going to be contrarian and obstinate and suggest that the current behaviour is fine, since there is no compelling rationale for any other behaviour.
Invariably, every defense for hanging on to empty directories boils
down to, "I might do something in the future that expects those directories to exist."
Well, if that's the case, then create them when you need them -- nothing you do should ever simply assume the existence of essential directories.
In addition, by "untracking" those directories, you're suggesting
that Git quietly do what should normally be done by "git rm --cached
".
If I want that behaviour, I would prefer to have to type it myself.
For example, to illustrate why not deleting empty folder would be problematic:
Others have said why, but here's an edge case you probably haven't
thought of:
(
rm -rf /tmp/repo &&
git init /tmp/repo &&
cd /tmp/repo &&
mkdir -p foo/bar/baz &&
git status
)
If you just have empty directories "git status
" will report nothing,
although "git clean -dxfn
" will show what would be cleaned up.
So if this worked as you're suggesting then someone could git rm
some
file, then everything would report that they're on commit XYZ
, but if they re-cloned at that commit they'd get a tree that would look different.
No Git command should behave in such a way as to leave the tree in a
state when moving from commit X->Y
that you wouldn't get the same Y
if
you re-cloned.
Note: the official git rm
test (git/git/t/t3600-rm.sh
) of the Git repo itself is quite clear as to what it expects:
test_expect_success 'rm removes subdirectories recursively' '
mkdir -p dir/subdir/subsubdir &&
echo content >dir/subdir/subsubdir/file &&
git add dir/subdir/subsubdir/file &&
git rm -f dir/subdir/subsubdir/file &&
! test -d dir