The command (suggested by Chronial)
git rev-list --all | xargs -l -I '{}' sh -c 'if git ls-tree -rt {} > /dev/null 2>&1 ; then true; else git log --oneline -1 {}; git ls-tree -r -t {} | tail -1; fi'
returned the first commit that depended on missing 2e223ce
object - its SHA-2 hash was 499b8fb
. Its parent was all right (I could see its content, check it out, etc.), and I was also able to check out the next commit after the broken one (89b0fc4
).
Now I needed to see what changes happened between these two "good" commits - that was easy: git diff 499b8fb~ 89b0fc4
returned
diff --git a/somefile b/somefile
deleted file mode 100644
index f5d1e1e..0000000
--- a/somefile
+++ /dev/null
@@ -1,79 +0,0 @@
[ contents of the deleted "somefile"... ]
diff --git a/moje/cli b/moje/cli
index 640a825..c0b1a24 160000
--- a/moje/cli
+++ b/moje/cli
@@ -1 +1 @@
-Subproject commit 640a825cd671dfba83601d6271e7e027665eaca8
+Subproject commit c0b1a24aa246289831ec7db3a8596376db1f625a
Now I know that between the parent of the bad commit and the good commit the file somefile
was deleted, and submodule's HEAD changed from 640a825
to c0b1a24
. I went to the submodule repository and asked what commits happened between those two:
git log --oneline 640a825..c0b1a24
which returned
c0b1a24 <commit message>
8be9433 <commit message>
02564e1 <commit message>
Now I knew that four things happened between 499b8fb~
and 89b0fc4
:
somefile
was deleted
/moje/cli
HEAD was changed from 640a825
to 02564e1
/moje/cli
HEAD was changed from 02564e1
to 8be9433
/moje/cli
HEAD was changed from 8be9433
to c0b1a24
I didn't know which part happened in 499b8fb
(the bad commit), and which in 89b0fc4
. But fortunately there are not that many possibilities, so i just tried every one of them. With each combination I made a commit so that Git would calculate appropriate objects and store them in the database. It turned out that when /moje/cli
HEAD was at 8be9433
, git commit
resulted in creating the missing 2e223ce
object - hooray!
Note: if you're having a similar situation and you're poking around to see which commits are good and what Git can tell you about them, remember that being able to checkout
a commit and show
it are two different things. For example, I initially thought that if git show somesha
throws an error it means that somesha
commit is corrupted, and I cannot use it for anything. That turned out to be false: while git show 89b0fc4
returned an error, I was able to git checkout 89b0fc4
and also git diff 499b8fb~ 89b0fc4
worked.
I suppose that's because git show somesha
shows what changes are introduced by somesha
, and for that Git needs to read the content of the previous commit (in this case a corrupted one). Apparently, Git doesn't need to look at the previous commit to check out one.
(I managed to do this thanks to Chronial's answer - kudos to him! I was advised to post this as my own answer.)