I want to see the what changes were made to this file's at line 147. So i enquired the file line by line for commits by:
git blame include/svl/itemset.hxx
Here is the trimmed output of git blame:
4b3a535ae3 include/svl/itemset.hxx (Michael Stahl 2015-04-08 15:02:47 +0200 145) SfxItemPool* GetPool() const { return m_pPool; } 4b3a535ae3 include/svl/itemset.hxx (Michael Stahl 2015-04-08 15:02:47 +0200 146) const sal_uInt16* GetRanges() const { return m_pWhichRanges; } d210c6ccc3 svl/inc/svl/itemset.hxx (Xiaofei Zhang 2010-07-29 10:56:19 +0800 147) void SetRanges( const sal_uInt16 *pRanges ); d210c6ccc3 svl/inc/svl/itemset.hxx (Xiaofei Zhang 2010-07-29 10:56:19 +0800 148) void MergeRange( sal_uInt16 nFrom, sal_uInt16 nTo ); 4b3a535ae3 include/svl/itemset.hxx (Michael Stahl 2015-04-08 15:02:47 +0200 149) const SfxItemSet* GetParent() const { return m_pParent; }
Now i want to to see what changes did that commit with SHA d210c6ccc3
to those lines. Basically i want to see the changes made by this commit to the file. So i did :
`git show d210c6ccc3 include/svl/itemset.hxx`
But this does not seem to give me right output, in fact it outputs nothing. Could anyone please suggest what could i be missing? Or maybe there is some other better way to know what changes were made to a file by a selected commit?
To see the diff for a particular COMMIT hash, where COMMIT is the hash of the commit: git diff COMMIT~ COMMIT will show you the difference between that COMMIT 's ancestor and the COMMIT .
eftshift0's answer is correct (and I've upvoted it). Here's why, though—along with the one other thing that can go wrong here.
In most cases, git show commit -- path
would be correct and would show you:
The patch would be the same as that produced by git diff commit^1 commit -- path
. (Note that the ^1
suffix here is literal text while the commit
part is a hash ID that you replace. The suffix syntax means "find the first parent". You can add this suffix to most commit selectors, although not to selectors that use certain search patterns. See the gitrevisions documentation.)
There are two important exceptions. Only one of these applies here, because git blame
normally does not blame a merge, it tries to trace the source of the change that fed into the merge. Still, I want to mention it, because git show
's behavior on merges is ... interesting. :-)
If you look at a merge commit with git show
, you will see, by default, a combined diff ("all parents" vs the merge commit's content). In this case you may wish to fall back directly on git diff
, so that you can specify the parent you want to compare (^1
, ^2
, and even more if this is an octopus merge). The reason is that combined diffs deliberately omit any file that matches the version in one of the parent commits: this means that whatever is in the repository from that point forward, it came from one of the two (or N if N > 2) "sides" of the merge.
With git blame
, you are looking for "who changed what". The person who did the merge is often not the person who made the change—so you should keep going "behind the merge" to find who really changed it.
The second exception is the one that caused a problem for you here, and it's really more a case of the way git blame
works when files get renamed during development.
When git blame
is analyzing changes to a file, such as include/svl/itemset.hxx
, it steps back one commit at at time, starting from whichever commit you specify. If you don't select your own starting point, it starts from from HEAD
, i.e., the current commit. It then looks at the parent commit(s) (as if via git show
for instance). For instance, if the current commit 922e935c8812
is an ordinary commit and its parent is 22c6554c98e2
, it compares commit 922e935c8812
to 22c6554c98e2
. If 22c6554c98e2
has a file of the same name, that's probably the same file ... but if not, Git tries to figure out which file in 22c6554c98e2
is the same file as include/svl/itemset.hxx
.
In this case, that exact thing happens at commit b9337e22ce1d
. There is a file named include/svl/itemset.hxx
in commit b9337e22ce1d
, but in commit b9337e22ce1d^
or f4e1642a1761
, the file is named svl/inc/svl/itemset.hxx
. Git detects this rename when stepping back from commit b9337e22ce1d
to commit f4e1642a1761
, and git blame
then carries the new name back with it from commit f4e1642a1761
to commit d210c6ccc3
.
When you run git show d210c6ccc3
, however, Git jumps directly to d210c6ccc3
(and its parent 7f0993d43019
). It no longer knows that the file named include/svl/itemset.hxx
in HEAD
is named svl/inc/svl/itemset.hxx
in d210c6ccc3
. So you must discover this, and pass the earlier name to Git.
You might wonder how you can find this. The answer is to use git log --follow
. The --follow
code for git log
is not great,1 but it's the same code that git blame
uses, so it produces the same answers, at least. Here is what I did:
$ git log --oneline --follow --name-status include/svl/itemset.hxx 00aa9f622c29 Revert "used std::map in SfxItemSet" M include/svl/itemset.hxx afaa10da2572 make SfxItemSet with SAL_WARN_UNUSED M include/svl/itemset.hxx [snip] a7724966ab4f Bin comments that claim to say why some header is included M include/svl/itemset.hxx b9337e22ce1d execute move of global headers R100 svl/inc/svl/itemset.hxx include/svl/itemset.hxx
There's a second rename even earlier. Here's another, shorter way to find only the renames:
$ git log --oneline --follow --diff-filter=R --name-status include/svl/itemset.hxx b9337e22ce1d execute move of global headers R100 svl/inc/svl/itemset.hxx include/svl/itemset.hxx e6b4345c7f40 #i103496#: split svtools in two libs, depending on whether the code needs vcl or not R100 svtools/inc/svtools/itemset.hxx svl/inc/svl/itemset.hxx
If d210c6ccc3
came "before" e6b4345c7f40
, you would have to use the even-earlier path name—but commit d210c6ccc3
is a descendant of (comes after) e6b4345c7f40
, not an ancestor.
1When changes occur at merges, this really, in a fundamental sense, requires following both (or all) input commits simultaneously. However, Git is not (currently?) capable of doing this: both git log --follow
and git blame
really only traverse whichever parent the final version of the file "came from". That is, if we look at a typical merge commit M
containing merged file F
, there are two inputs M^1:F
and M^2:F
, and one output, M:F
. If M:F
is the same as M^1:F
, we should ignore M^2:F
entirely: all the contents of M:F
are those from whoever provided M^1:F
. If M:F
is the same as M^2:F
, we should ignore M^1:F
entirely: all the contents of M:F
are those from whoever provided M^2:F
.
Note that this only works for one file at a time, and even then, it only works if the file exactly matches one of the two inputs. Otherwise, we should look at the combined diff to see how the file got modified from both inputs. This is the logic behind both combined diffs and History Simplification. But it's over-simplified in some rare cases, and hence sometimes gets things wrong.
You have to provide the path in that revision:
git show d210c6ccc3 -- svl/inc/svl/itemset.hxx
That should do
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With