This happened because someone—presumably not you—deleted the code.
Remember that merge does not mean make these the same. You tell Git:
git checkout somebranch
git merge anotherbranch
That doesn't mean make a new commit in somebranch
that exactly matches the last commit in anotherbranch
. Instead, this tells Git:
Find some commit at or before the tip of somebranch
, that's also at or before the tip of anotherbranch
. This commit that comes before either tip has to be on both branches, and should be the best common ancestor. Git calls this commit the merge base.
Now that we know that whoever made somebranch
—perhaps us—started with commit B (for base), and whoever made anotherbranch
(maybe this was us instead) also started with commit B, compare commit B to the tip of somebranch
to see what we/they changed. Then, compare B to the tip of anotherbranch
to see what they/we changed.
The merge now combines these two sets of changes. For the merge to have decided that file somedir/somepackage.java
should be deleted, the change from B to one tip must have been: do nothing at all. The change from B to the other tip must have been: delete file somedir/somepackage.java
. The combination of these two changes is obvious: delete the file!
Hence, the correct merge result—the combination of the change made by person S on somebranch
who said delete the file, and the non-change by person A on anotherbranch
who didn't say anything at all—is to delete the file, and Git did that. (Or, if I have the two branches swapped, it doesn't matter: that's still the automatically-right result. It's right for Git even if, by your smarter-than-Git human evaluation, it's wrong.)
The file is still complete, whole and intact, in one of the two commits just before the merge, and also in the merge-base commit. (It's obviously non-existent in the other.) Just extract it from any commit that has it. Those commits still exist, and still hold all their files in their committed state, forever: that's why you have a version control system, so as to hold everything everyone ever did.
Getting a file back
To get the file with path P from commit H (where H
is some commit hash such as a123456
), run:
git checkout H -- P
e.g., git checkout a123456 -- somedir/somepackage.java
. This will extract the copy that's in the commit, placing that copy into your index (ready for your next commit) and your work-tree (ready for you to view it).