Commit shows up in history, changes are not in the file and don't show in individual file commit history
I just had a really weird problem with git. I pushed a commit yesterday and after that a few people did pushes. Now I can see my commit in history on github, but the changes I made are not in the HEAD revision of the file and if I look at the history of the individual file, my commit doesn’t show up. My team mates tell me that all they did were regular pull and push. There were no conflicts with the file I changed. If someone used force push, I would expect my commit to completely disappear. If someone screwed up a merge conflict resoltuion, I would expect merge commit to undo my changes and be reflected in the commit history for the file. But somehow neither is the case. To simplify:
- I change file.txt and make commit ABCD
- I push ABCD up to github
- Other people push other commits, not affecting file.txt
- If I look at repository commits on github, I see my commit and I can pull up the change diff
- If I look at the latest revision of file.txt on github my changes are not there
- If I look at the commit history for file.txt ABCD doesn’t show up, last commit on that file is the commit before ABCD.
How does something like this happen?
Edit: per discussion in the comments, here is a piece of my commit graph
* | 8c1d372 ... * | 98dbad7 Merge branch 'master' |\ \ | * | 8d64a09 ... | * | 12beb68 ... | * | 1c94b6d Merge branch 'master' | |\ \ | | * | 80b6285 ... | | * | 9781fad ... | | * | f12fc90 ... | | * | 61411fa ... | | * | 291333b ... | * | | aeee93f ... | |/ / | * | bcfbf65 Merge branch 'master'
My commit that disappeared is 9781fad. If I checkout 8c1d372 my changes disappear. Yet no commits after mine actually affect the file I changed.
2 Solutions collect form web for “Commit shows up in history, changes are not in the file and don't show in individual file commit history”
Actual answer (rather than super-long comment).
Here’s the graph bits again, plus my own annotations:
* | 8c1d372 ... <-- lacks desired changes * | 98dbad7 Merge branch 'master' <-- ? (interesting #1) |\ \ | * | 8d64a09 ... | * | 12beb68 ... | * | 1c94b6d Merge branch 'master' <-- ? (interesting #2) | |\ \ | | * | 80b6285 ... <-- probably also has desired changes | | * | 9781fad ... <-- is commit that makes desired changes | | * | f12fc90 ... | | * | 61411fa ... | | * | 291333b ... | * | | aeee93f ... | |/ / | * | bcfbf65 Merge branch 'master'
Since no one explicitly reverted your changes, and they’re to a file no one else should have been editing, it’s almost certain that whoever lost your changes, did so by doing a bad merge.
There are two merges that are interesting, which I have marked. #1, commit
98dbad7, has two parents: one we cannot see (off the bottom of the graph) and one we can,
git log from this point will show both histories.
git show 98dbad7:path/to/file will show the version of the file attached to the merge commit (i.e., whatever whoever did the merge said was the correct version of that file). If that’s the wrong version—if that file lacks the desired changes—then either the merge itself went bad, or had bad inputs. Presumably the parent-that’s-off-the-bottom is not supposed to have the change, so at this point we should follow the other parent, i.e.,
git show 8d64a09:path/to/file to see if that version has, or is missing, your changes. If your changes are in there then it is the
98dbad7 commit that dropped them. If your changes are not in there, we should press on.
For completeness we can look at
12beb68, but it’s probably not the culprit. It’s an ordinary commit with one parent,
1c94b6d is our second interesting merge. As with any merge, it has two (or more, but usually two) parents. This time we can see both, and they are commits
80b6285 probably has your changes, since it’s an ordinary commit whose parent,
9781fad, is the commit with your changes. And commit
aeee93f won’t have your changes since it’s from the side of the merge that, well, doesn’t have your changes. 🙂 You can
git show 80b6285:path/to/file just to check that whoever made that commit didn’t secretly (as opposed to overtly) revert them, and then
git show 1c94b6d:path/to/file to observe whether it’s this interesting-merge-#2 that dropped your changes.
Chances are very high that one of those merges dropped your changes. Why is still an interesting question, but has to be answered by the person who made that merge. They did something—perhaps a checkout, perhaps a flag given to the merge—that dropped your changes during the merge. Find out which merge dropped the changes, and talk to whoever did it to find out what they did that dropped them.
Getting the changes back
It’s really easy to bring your changes back in: just use
git cherry-pick, which runs a diff for you, then applies that diff to the current commit. So you’d get back onto
master and simply
git cherry-pick 9781fad. That will diff
9781fad‘s parent, which is
9781fad—a diff showing your changes—and apply them to the current commit and make a new commit, re-using the original message. (If your commit affects multiple files and should only bring back the changes to one file, it requires a bit more work, but assuming it’s the one file, this is the easy fix. You will still need to make sure no one undoes it in a future merge, though.)
This answer consists of instructions on how to find the answer.
Use a commit graph viewer (or just
git log --graph, often best invoked as
git log --graph --oneline --decorate --all) to see where your particular commit is in terms of the Git DAG. If the graph is nice and linear it will be easy to follow. If it’s full of branch-and-merge operations, it will be trickier, and it’s likely that someone did a bad merge that introduced the problem. If it is full of unmerged branches, or even just has one unmerged branch, perhaps that is the problem.
--all will show you all labels, including all branch names, and using
--decorate will attach the labels to the commits to which they point. The
--oneline option just compresses the log output to one line per commit, fitting a lot more into a text window.)
git show to view any ordinary (non-merge) commit: this shows you the commit metadata (author, time, log message) and runs
git diff to compare the tree for that commit against the tree for that commit’s parent. For instance,
git show ABCD should show your change, since the tree you committed had that change to that file, as compared to the tree you started with before you made commit
Assuming your commit
ABCD sits where you would like it to within the graph, look at subsequent commits (with
git show again)—subsequent commits are those closer to
HEAD—to see who undid your change. Then you can try to figure out how they did that, perhaps by talking with them.
If a subsequent commit is a merge—a commit with two or more parents—
git show won’t normally show you much, and you need to either use
git show -m, or run
git diff manually, to see two comparisons: from first-parent to that commit, and from second-parent to that commit. The
-m flag tells
git show to do the two separate diffs for you. (You can supply
-m for non-merge commits; it just does nothing for those. It’s not the default because normally merges show a lot of redundant changes.)
git log -- path doesn’t really “show file history” because Git does not have per-file history. Instead, this
git log starts by finding all commits reachable by walking the commit graph back starting from
HEAD, but then throws out commits that do not seem to affect the file in question. It’s not clear why you are not seeing your
ABCD commit here, but the graph version of the log, and
git show output, should make it clearer.)
Per comments below: Given the description of
git log output when checking out the commits labeled
5 (when checking out
5 we see 5, 4, 3, 2, and 1, but when checking out
3 we see only 3 and 2), at least one of
5 must be a merge commit.
Assuming 4 is the (single) merge commit, we can draw this particular graph fragment as:
... <- 1 <------- 4 <- 5 <-- master / ... <- 2 <- 3 <--
Here the branch (
master) points to commit
5, which is an ordinary non-merge commit, while commit
5 points to commit
4 is the parent of
4 is a merge commit, merging the contents of commits
3; it has two parents,
1 (in some order). Commit
3 is a non-merge with
2 as its parent. We know almost nothing about commits
2: I have drawn them as if they have different parents, but perhaps they have the same parent.
It’s possible that commit
5 is the merge, or that
5 are both merges; all we can say for sure, based on
1 showing up when doing
git log from
5, but not when doing
git log from
3, is that
1 is not in the history for
Note that history is found by following the arrows in the direction they point, never going against the arrows. If we start from
4 is the merge, it has two outbound arrows, so we follow both histories to get the history of
git log --graph draws the history vertically (and does not put in arrow-heads), it shows this same information, which you cannot see without
--graph, but which is vitally important for future operations such as merges that depend on the graph-so-far.
Note that if the above graph fragment is accurate (I’m still guessing since you have not posted
git log --graph --oneline --decorate --all output snippets), then we can almost certainly say what happened to your changes in commit
1: whoever made the merge in
4 told Git to wipe out your changes, taking whatever was in commit