views:

191

answers:

1

I get an unexpected appearance of "dev/null" in my git status output after interactively adding a patch for a file that was renamed. I'm wondering if this is expected and there is some good reason for this behavior, or if this could be a bug.

Below is a simple illustration of how to reproduce this. In my real-world scenario, it's a bit more complicated and there's a good reason why I'm using git add -p, but I was able to boil it down to this minimal example:

$ git init test
Initialized empty Git repository in /local_disk/tmp/test/.git/
$ cd test
$ echo "foo" > foo
$ git add foo
$ git commit -m 'Add foo'
[master (root-commit) 3643b5d] Add foo
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 foo
$ mv foo bar
$ git add -p
diff --git a/foo b/foo
index 257cc56..0000000
--- a/foo
+++ /dev/null
@@ -1 +0,0 @@
-foo
Stage this hunk [y,n,q,a,d,/,e,?]? y

$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD ..." to unstage)
#
#       new file:   dev/null
#       deleted:    foo
#
# Changed but not updated:
#   (use "git add/rm ..." to update what will be committed)
#   (use "git checkout -- ..." to discard changes in working directory)
#
#       deleted:    dev/null
#
# Untracked files:
#   (use "git add ..." to include in what will be committed)
#
#       bar

What is with the "new file: dev/null" and "deleted file: dev/null"? I would expect this to result in exactly the same thing as if I had done:

$ mv foo bar
$ git rm foo
$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD ..." to unstage)
#
#       deleted:    foo
#
# Untracked files:
#   (use "git add ..." to include in what will be committed)
#
#       bar

I am using Git version 1.6.5.5, and have also reproduced it in 1.6.5.4. I was unable to reproduce it in my Cygwin environment which has Git at version 1.6.1.2.

+1  A: 

As thenduks mentions, you shouldn't be trying to git add the file removal. git add the new file, git rm the old file (or git mv old new to take the simple approach). On the other hand, git should either complain about what you're doing or not get confused and try to add a non-existent dev/null file.

Update
git add -p is indeed a valid method to stage file removals, but it looks like a bug was introduced to git apply when fixing a different git apply bug.

Update
I can reproduce it on Linux with 1.6.1.2, so it may be that the cygwin git is what differs from the normal behavior. In that case, the previously mentioned bug-fix may not have introduced this behavior and the working git add -p may be specific to cygwin's git. I've been trying to bisect to find where git add -p started failing.

Update
Turns out it was rather a bug in the interactive aspect of git add which Jeff King has proposed patches for.

jamessan
Like I said in my question, this is a simplified "boiled-down" example. In my real-world scenario, `git add -p` was used because it's a bit more complicated (some changes I want to have staged, and some I don't). Git normally allows "removing" content by using `git add -p`. `git rm` is not *required* to do this, it's just a convenient way of telling git that you'd like to "add the removal" of a (no longer existing) file to the index. If it were incorrect to do this, then `git add -p` **wouldn't offer** to stage patches that remove content.
Dan Moulding
Put another way, let's say I have 100 files that I've removed, but I only want to commit the removal of 50 of them. `git add -p` would be a very convenient way to quickly say 'y'/'n' and go through the list of files to choose which removals should be staged for the commit.
Dan Moulding
`git rm` is needed to stage file removal. You can verify that by trying to do `git add deletedFile`. Nothing happens. This is why I say that `git add -p` should complain or not do anything when you use it to stage a file removal. `git mv` is indeed a convenience command, but `git rm` is not.
jamessan
The reason you can't `git add foo` if foo has been deleted is because foo is no longer a file in the filesystem, so Git has no way of knowing what you are trying to do (i.e. did you mistype the filename?). However, there are other ways of letting Git know that you'd like to stage (**add** to the index) the removal of foo. `git rm` is one way, `git add -A` is another, `git add -i` is yet another. In fact, this is *exactly* what *all* the variants of the `git add` command do. I'm sure if you look at `git add` and `git rm` you'll probably find they use the same plumbing underneath the porcelain.
Dan Moulding
Except that when you do `git add unknownfile` you get an error about the unknown pathspec. That isn't the case when there's a known file that has been removed from the working copy.
jamessan
And that may be a bug :) Whatever. I still maintain that `git add` may be used for file removals, otherwise `git add -A` wouldn't be doing what it's supposed to do.
Dan Moulding
I'm not disagreeing with the use of `git add -A` or `git add -u`, but those are quite different than `git add`, `git add -i`, or `git add -p`. The command to use to say "stage the removal of this file" is `git rm`. `git add -u` is "stage any change to files that are known", of which "stage the removal of this file" is a subset.
jamessan
Isn't `git add -u` basically the same as `git add -p` and answering 'y' to all the questions? Anyway, I see your point of view. You believe that `git add` may only *sometimes* be used to stage removals (depending on the options you give it). You could very well be right, but that seems fishy. I'll just add that in version 1.6.1.2 `git add -p` correctly stages the removal (as I expect it to) without the "dev/null" weirdness. (I tried with that version on another machine after I asked this question).
Dan Moulding
No, they're quite different. `git add -u` just looks at all the modified files in the working directory and stages those files to the index. `git add -p` parses the output of `git diff-files` (a few times with varying invocations) and then `git apply`s the resulting diffs that are approved.
jamessan
So, this looks like a potential bug in `git apply`.
jamessan
Looks like it may be fixed by http://git.kernel.org/?p=git/git.git;a=commitdiff;h=ec7fc0b1a46c5a352532ea3f29c5663752fd8ac6
jamessan
Well, sure I understand that `git add -u` and `git add -p` with "yes to all" have different work to do, so they must be implemented differently. But the end result of both should always be the same (i.e. all changes to files Git knows about should be added to the index). Again, if it's incorrect to stage a file removal using `git add -p`, then Git shouldn't ask if you want to stage such a change during the interactive add. But it does. So that's either a) a bug, or b) intended behavior and staging removals that way is allowed.
Dan Moulding
Cool. Thanks for the updates. And thanks for taking the time to hunt this down. I didn't find anything in the git mailing list and was tempted to file a bug report with them, but didn't want to get yelled at if this "dev/null" thing was *supposed* to happen, for some reason that my puny brain couldn't understand ;)
Dan Moulding