tags:

views:

315

answers:

3

I'm writing code to programmatically run git commands and learning git at the same time. Am I mis-reading the man pages or is what I want to do not doable?

The following will tell me how MYFILE changed between the two commits:

git diff COMMIT1..COMMIT2 -- MYFILE

Good.

But, let's say I just want to ask how COMMITX changed the file, without specifying the prior commit. In my imagination the syntax would be something like this:

git diff COMMITX -- MYFILE

or this:

git diff COMMITX^..COMMITX -- MYFILE

But the above commands don't work (for me).

The following works in the sense that it gives me the unified diff showing how that COMMITX changed MYFILE, but it also includes other stuff I have to strip out - like author, date, the checkin msg. Stripping out the extra stuff is easy, but it feels like it's something I shouldn't have to be doing. Does the command exist? Am I misunderstanding something simple?

git show COMMITX -- MYFILE

EDIT1: I'm showing here the actual output from my "git bash" window. I changed the "show" to "diff", and got no output.

$ git show 789e9 -- dir1/file3.txt
commit 789e948bce733dab9605bf8eb51584e3b9a2eba3
Author: corey 
Date:   Sun Oct 11 21:54:14 2009 -0500

    my msg

diff --git a/dir1/file3.txt b/dir1/file3.txt
index a351259..cf2bd35 100644
--- a/dir1/file3.txt
+++ b/dir1/file3.txt
@@ -4,5 +4,7 @@ c
 ddd
 e
 f
+a new line
+another new line
 g
 h

Administrator@BIOSTAR /c/temp/mygit (master)
$ git diff 789e9 -- dir1/file3.txt

Administrator@BIOSTAR /c/temp/mygit (master)
+3  A: 

git diff COMMIT^..COMMIT file or git diff COMMIT^..COMMIT -- file both work perfectly well for me with git 1.6.3.3. Update courtesy of Jakub Narębski: you can also write git diff COMMIT^! -- file.

$ git log --oneline b8ad655^!
b8ad655 Bring in the "SimpleMenu" loader plugin


$ git diff b8ad655^! lib/WWW/MenuGrinder/Plugin/SimpleLoader.pm
diff --git a/WWW-MenuGrinder/lib/WWW/MenuGrinder/Plugin/SimpleLoader.pm b/WWW-MenuGrinder/lib/WWW/MenuGrinder/Plugin/SimpleLoader.pm                                                                                                                        
new file mode 100644                                                                                                          
index 0000000..14f6cd8                                                                                                        
--- /dev/null                                                                                                                 
+++ b/WWW-MenuGrinder/lib/WWW/MenuGrinder/Plugin/SimpleLoader.pm
[...]
hobbs
git diff shows differences from the current working tree to the commit, not the changes from the commit to its parent
bdonlan
You're right, my mistake. Corrected above.
hobbs
It would be really cute if there was an extension to allow `git diff ^..COMMIT` as an analog to `svn diff -c rev` though :)
hobbs
Although my question is not an EXACT dupe of other stackoverflow questions, it is similar. I'm not fair to expect git to be predictable based on svn analogies, but it would be nice if the behavior of one git commands were a bit more predictable based on behavior of other git commands.
Corey Trager
@bdonlan: that's merely the default mode of operation. Have a look at the top section of the man page. It can show differences between two arbitrary trees (e.g. commits), a tree and the working tree, a tree and the index, or the index and the working tree.
Jefromi
@hobbs: There is, and it's called `git diff COMMIT^!` (see git-rev-parse manpage, section "Specyfying ranges").
Jakub Narębski
+3  A: 

Try:

git show --pretty=format: <commitid> -- <file>

Here's how it looks:

diff --git a/config.y b/config.y
index 7750514..f051b99 100644
--- a/config.y
+++ b/config.y
@@ -454,8 +454,8 @@ definegame : TYPE_DEFINE_GAME '{'
        }
        game_definitions '}'
        {
-           num_games = ncnf;
            ncnf++;
+           num_games = ncnf;
        }
        ;

There's still a single blank line at the start (not shown here due to markdown limitations), but patch will happily ignore that (indeed, it will ignore the header stuff in default git show output too). You can also pipe through tail -n +2 to drop said line.

--pretty=oneline is also useful:

3ed347de4c6e0e3230f651f82aabf964c6d16100 Fix a bug where more than one defined game didn't show up or showed gibberish
diff --git a/config.y b/config.y
index 7750514..f051b99 100644
--- a/config.y
+++ b/config.y
@@ -454,8 +454,8 @@ definegame : TYPE_DEFINE_GAME '{'
        }
        game_definitions '}'
        {
-           num_games = ncnf;
            ncnf++;
+           num_games = ncnf;
        }
        ;

That said, if you're formatting a patch for submission somewhere, don't strip off that stuff. In fact, use git format-patch and revel in it. Third-party patching tools will happily ignore that extra metadata, and for projects using git, git apply will use your provided commit message and author line, making it easy for them to apply it.

bdonlan
Seems like it's working. I took your answer, went back to the "git show" man page, and in my newly enlightened state tried to see if I could "see" the answer your answer in the page knowing exactly what I was looking for. Still tricky-> The "format:" with no format specification at all. For extra credit, what is the difference between the "author" and the "committer"?
Corey Trager
Found an explanation of author/committer difference here: http://book.git-scm.com/1_the_git_object_model.html
Corey Trager
This is really quite a lot of overkill, unless you're really trying to create a patch for submission, which is unlikely if you're only diffing a single file. `git-diff` is designed to do quite a lot of things, including this task.
Jefromi
+1  A: 

You don't have to use a workaround like git-show - you are just slightly off on your syntax. git-diff shows the difference between two named commits. The .., on the other hand, means "the range of commits between...". The correct syntax is:

git diff COMMITX^ COMMIT -- MYFILE

That said, it does actually work for me with the ... (I tested git diff master^..master -- git-add--interactive.perl in git.git) It probably hasn't always been this way, but it has worked at least since git moved to index version 2 (between v1.5.1 and v1.5.2) - it'd be a pain for me to test back before this.

Jefromi
Actually "git diff A..B" works as "git diff A B" for quite some time, mainly so one can cut'n'paste revision [range] to dif, for example from git-fetch output.
Jakub Narębski
The "git diff" documentation, http://www.kernel.org/pub/software/scm/git/docs/git-diff.html ,says "COMMIT..COMMIT" is "synonymous to the previous form" "COMMIT COMMIT". Neither work for me when I write "COMMIT^". Both work the same when I actually specify the prior commit directly. I'm using msysgit on windows, git version 1.6.4.
Corey Trager
Interesting - I wonder if it's a msysgit bug!
Jefromi
Looking back at all your test cases, it looks like `diff` is working fine, but maybe the revision parsing isn't - does COMMIT^ work in other contexts?
Jefromi
@Corey Trager: what **shell** do you use with Git? It might be that your shell does something strange with `^` and/or `!` characters... did you try e.g. `"COMMIT^"` or `'COMMIT^'` (use double or single quotes)?
Jakub Narębski