2

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.

Dan Moulding
  • 211,373
  • 23
  • 97
  • 98
  • what version git? I can't repeat that behavior. Instead, git add -p after git mv responds: no changes – William Pursell Dec 07 '09 at 18:38
  • @William: Using `git mv` is not equivalent, because it moves and adds the file at the same time. I am moving the file using just plain `mv`, and then using `git add -p` to add it. – Dan Moulding Dec 07 '09 at 18:46
  • @Dan, why? `git mv` is the 'proper' way to change a file's name. To delete a file you use `git rm` and to move one you use `git mv`, don't just move the file yourself and expect git to read your thoughts :) – rfunduk Dec 07 '09 at 18:52
  • @thenduks: I don't believe that's true. `git mv`, if I'm not mistaken, was added to appease the crowds that were clamoring for such a command. It doesn't do anything that simply moving the file and then adding it would do. git, in fact, *does* read your mind (well, actually it examines the differences and identifies the rename on its own). Git doesn't need, or **want**, you to tell it about such things. – Dan Moulding Dec 07 '09 at 18:57
  • Read your example many times, and each time I looked at 'mv foo bar' I read 'git mv foo bar'. Odd. Anyway, as a workaround, if you invoke 'git add dev/null' after you run add -p you get the behavior that you want. – William Pursell Dec 07 '09 at 19:54

1 Answers1

2

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.

Community
  • 1
  • 1
jamessan
  • 41,569
  • 8
  • 85
  • 85
  • 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 Dec 07 '09 at 19:10
  • 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 Dec 07 '09 at 19:13
  • `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 Dec 07 '09 at 19:16
  • 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 Dec 07 '09 at 19:24
  • 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 Dec 07 '09 at 19:29
  • 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 Dec 07 '09 at 20:05
  • 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 Dec 07 '09 at 20:09
  • 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 Dec 07 '09 at 20:26
  • 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 Dec 07 '09 at 20:53
  • So, this looks like a potential bug in `git apply`. – jamessan Dec 07 '09 at 20:57
  • Looks like it may be fixed by http://git.kernel.org/?p=git/git.git;a=commitdiff;h=ec7fc0b1a46c5a352532ea3f29c5663752fd8ac6 – jamessan Dec 07 '09 at 21:00
  • 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 Dec 07 '09 at 21:08
  • 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 Dec 08 '09 at 16:15