Git: Equivalent of `--full-history` for `git bisect` and `git blame`
Asked Answered
F

1

8

I've been using Git heavily for about 7 years. A few days ago I found a behavior that surprised me. I found git log, git blame and git bisect to exhibit this weird behavior. A friend let me know about the --full-history flag to git log that solved my problem. I want to know, for my own education, whether there is an equivalent fix for git blame and git bisect.

Feel free to see the problem for yourself with this repo: https://dl.dropboxusercontent.com/u/1927707/problematic_repo.7z

Here is its log:

$ git log  --graph
* commit b7a8d7aa001d06eb7491ab5fb447a8dd3aa421a8
| Author: Ram Rachum <[email protected]>
| Date:   Tue Apr 19 17:45:01 2016 +0300
|
|     adding more to some-file
|
*   commit 0aa833916e908ea93902a6c4c227f9a884a1bcef
|\  Merge: 2413945 3068c7d
| | Author: Ram Rachum <[email protected]>
| | Date:   Tue Apr 19 17:44:31 2016 +0300
| |
| |     Merge branch 'master' into development
| |
| * commit 3068c7d2548f1798b6840f73b13a649937339f28
| | Author: Ram Rachum <[email protected]>
| | Date:   Tue Apr 19 16:02:27 2016 +0300
| |
| |     Adding sugar to coffee
| |
* | commit 24139451ab954b1f0a9ef616775a3dba0ac81669
|/  Author: Ram Rachum <[email protected]>
|   Date:   Tue Apr 19 16:01:28 2016 +0300
|
|       Creating some-file
|
* commit cf02fbbc40104cd02eea4c7c6f134ef1fd7b5661
  Author: Ram Rachum <[email protected]>
  Date:   Tue Apr 19 16:00:47 2016 +0300

      Create coffee

In the very first commit, the file coffee was added. In the commit 3068c7d, I added a line "sugar" to the coffee file. But then I merged this branch into the development branch, and in that merge, a mistake was made and the "sugar" line was removed, leaving coffee empty. Then another commit b7a8d7a, making an unrelated change, was added for good measure.

Now I'm looking at my coffee, and finding there's no sugar in it. I distinctly remember adding sugar to my coffee. I run git log coffee, and get this output:

$ git log coffee
commit cf02fbbc40104cd02eea4c7c6f134ef1fd7b5661
Author: Ram Rachum <[email protected]>
Date:   Tue Apr 19 16:00:47 2016 +0300

    Create coffee

That's it. git log is showing neither my original commit that added the sugar, nor the merge that removed it. Two very relevant commits that are missing.

I was frustrated for about an hour by this problem, because it happened in a huge enterprise repo, where commits are much harder to find manually.

I also tried using git bisect and git blame to pin down the two commits, but both of these tools ignored the two commits. git bisect pointed me to the wrong commit after I finished doing all the git bisect bad and git bisect good actions.

Then, as I said in the beginning, a friend pointed me towards the --full-history flag:

$ git log --full-history --graph coffee                
*   commit 0aa833916e908ea93902a6c4c227f9a884a1bcef    
|\  Merge: cf02fbb 3068c7d                             
| | Author: Ram Rachum <[email protected]>                
| | Date:   Tue Apr 19 17:44:31 2016 +0300             
| |                                                    
| |     Merge branch 'master' into development         
| |                                                    
| * commit 3068c7d2548f1798b6840f73b13a649937339f28    
|/  Author: Ram Rachum <[email protected]>                
|   Date:   Tue Apr 19 16:02:27 2016 +0300             
|                                                      
|       Adding sugar to coffee                         
|                                                      
* commit cf02fbbc40104cd02eea4c7c6f134ef1fd7b5661      
  Author: Ram Rachum <[email protected]>                  
  Date:   Tue Apr 19 16:00:47 2016 +0300               

      Create coffee                                    

This makes me happy because it shows the two relevant commits, the one adding sugar and the merge that removed it. So my problem is solved. But I really wish I could know how to make git bisect and git blame behave as well. Does anyone happen to know?

Furr answered 20/4, 2016 at 9:58 Comment(4)
git blame might well allow --full-history directly here (I would try it on your repo but I cannot unpack a 7zip file). Bisect might refuse to touch merges since it handles them itself. In general you aren't seeing these because git rev-list skips a lot of merges: see the documentation's discussion on TREESAME and merge handling (and how --full-history alters this).Selfmade
@Selfmade I tried it now, blame appears to accept the flag but it looks like it doesn't change anything. Trying both git blame --full-history and git blame --full-history --reverse showed no results. Regarding not being able to open 7z archives, here it is in zip: dl.dropboxusercontent.com/u/1927707/foo.zipFurr
@Selfmade Also tried git blame --reverse HEAD^^^..HEAD --full-history coffee , still getting nothing.Furr
Not really trying to give you a silver bullet here but you might find github.com/dreamyguy/gitlogg useful to parse all your commits to JSON so that you can have a good clinical look at them. You might want to uncomment all the commented-out fields on the gitlogg-parse-json.js file and remove the --no-merges on the gitlogg-generate-log.sh to get all the output available.Varix
S
1

Interesting. Since the line isn't there, git blame starts out quite unhelpful. As the documentation for git blame notes:

The report does not tell you anything about lines which have been deleted or replaced; you need to use a tool such as git diff or the "pickaxe" interface briefly mentioned in the following paragraph.

In this case we might run git log -SSugar to find where it went in:

$ git log --pretty=oneline -SSugar
3068c7d2548f1798b6840f73b13a649937339f28 Adding sugar to coffee

but git blame won't immediately help us find where it went out. (And as you have just discovered, if we want to find that line when mentioning files, we may need --full-history, since adding path(s) to limit the commits git log will consider, also causes history simplification by pruning each commit's tree to contain just the mentioned files and then using that TREESAME code.)

Starting from the known-good rev, we can now try --reverse [edit: I noticed 3068c7d2548f1798b6840f73b13a649937339f28 == master and actually used master here, probably should have used the SHA-1 directly]:

$ git blame --reverse master..HEAD coffee
^3068c7d (Ram Rachum 2016-04-19 16:02:27 +0300 1) Sugar

This seems to imply that 3068c7d is the last rev in which the line exists, so it must be deleted in some or all of the children along this particular path, which is true:

$ git log --oneline --graph --decorate --all
* b7a8d7a (HEAD -> development) adding more to some-file
*   0aa8339 Merge branch 'master' into development
|\  
| * 3068c7d (master) Adding sugar to coffee
* | 2413945 Creating some-file
|/  
* cf02fbb Create coffee

There is only one commit that is a child of 3068c7d here, namely 0aa8339, so:

$ git show -m 0aa8339
commit 0aa833916e908ea93902a6c4c227f9a884a1bcef (from 3068c7d2548f1798b6840f73b1
Merge: 2413945 3068c7d
Author: Ram Rachum <[email protected]>
Date:   Tue Apr 19 17:44:31 2016 +0300

    Merge branch 'master' into development

diff --git a/coffee b/coffee
index 4d0f160..e69de29 100644
--- a/coffee
+++ b/coffee
@@ -1 +0,0 @@
-Sugar
diff --git a/some-file b/some-file
new file mode 100644
index 0000000..e69de29

(we need -m to get git to compare the merge against both parents). And that does find it, in a slightly roundabout way.


(Meanwhile, there seems to be no cure for the bisect issue. Well, other than "avoid evil merges"...)

Selfmade answered 20/4, 2016 at 12:17 Comment(3)
Thanks for your answer! My mistake with --reverse was not using the correct left commit.Furr
Update: I tried using blame now for a similar case, but where the old commit removed the lines that existed before, and the merge mistakenly re-added them. blame failed to show the merge that mistakenly re-added the lines, even when I used --full-history and --full-history --reverse branch..other_branch. Any idea why?Furr
I'm not really sure: I do not use git blame all that much myself. In general, though, whether it's going forward or reverse, it just compares (the lines of a file in) each before/after commit, taken pairwise, to see if a line was changed or removed. The line then gets assigned the ID of the corresponding commit of the pair. This method does not (and cannot) work very well across merges since there are multiple pairings.Selfmade

© 2022 - 2024 — McMap. All rights reserved.