How I know it from @torek's thoroughgoing answers (for example, here), when Git does a merge between two branches (commits) and some conflict gets in its way there are three non-empty "slots" in the index: 1st stores the common (base) versions of each file, 2nd does local (ours) versions and 3rd - the remote (theirs) versions.
It is quite simple to envisage because all these versions is actually stored as blob objects in the objects directory. So Git can extract needed versions to its index from these blob objects.
However, what if criss-cross merge occurs? In this case we have multiple merge bases and Git merges them all into one commit considering the new one to be a single base (see this).
I found Git does not create any blob object for this new commit. Does it mean the base versions exist nowhere but in the working memory (the 1st slot of the index for Git)? And so, if my computer shuts down and I have an unresolved conflict, would the generated base commit be lost?
Updated per comments (don't have time right now to research this, so I'm assuming what ET notes is accurate...)
First a quick aside that this is all premised on the recursive merge strategy, and on the rather specific circumstance that it will try to compute a merge base from multiple candidates.
With that, it's easiest to see what's happening if we contrive to have all these merges affecting a single file. "Easiest" is a relative term, but here we go...
O -- A - M2 <--(master)
\ X
---B - M1 <--(dev)
In O
we have a file named foo
. A
and B
each change this file in a way that does not conflict. (That it not conflict may not be strictly necessary - apparently even a conflicted result can serve as the computed merge base in the recursive strategy - but it makes it easier to see what's going on if you want to reproduce these steps and look at the repo.)
M1
is an "evil merge" of A
and B
; although A
and B
would merge cleanly, an additional change to foo
is made when creating M1
.
Likewise, M2
is an "evil merge" of A
and B
; the added change to foo
in M2
conflicts with the added change to foo
in M1
.
Now if we try to merge dev
with master
then foo
is in conflict; and sure enough
git cat-file -p :1:foo
will show us a file that we never stored before - the result of a clean merge between the versions of foo
in A
and B
.
So now to your question:
I found Git does not create any blob object for this new commit
That statement is a little confusing; commits are not represented by BLOB
objects. It's true that there is no COMMIT
object representing the calculated merge base.
But there is a new BLOB
for :1:foo
- just as there would be for any index entry[1]. There's also (in my test) a TREE
object to contain the BLOB
. In other words, the entire content of the calculated merge base is, in fact, stored in .git/objects
.
Does it mean the base versions exist nowhere but in the working memory (the 1st slot of the index for Git)?
The base versions exist in the 1st slot of the index, but that is most definitely not "working memory". There generally isn't even a git process running during the time you're resolving conflicts - so no process whose working memory could be used to contain it.
As noted above, the index is made up of BLOB
objects on disk.
And so, if my computer shuts down and I have an unresolved conflict, would the generated base commit be lost?
No.
But would it matter if it were? If somehow the automatically generated merge base were lost and you had to start over, git would just automatically generate it again.
[1] - Ok... sigh... not quite "any" index entry. There are "content-less" index entries for when you declare the intent to add a file. But that's not really related to all this and it just confuses matters. Only mentioning it to get ahead of the pedants and trolls.