But the snapshot-model also doesn't really make a lot of sense for merges. It's a snapshot of what then? A merge of all parent trees? What's a merge of two files then? Defining this merge-operation on trees is at least as mentally taxing as the alternative.
Accumulating all the diffs from two (or more) ends (until they are common again) is at least as useful.
For me, a merge commit in git is just a snapshot like any other except that its metadata contains links to more than one parent.
The parent child relationship acts as nothing more than remark that the child was derived from both parents in some way.
Of course, commonly the child is derived by finding the most recent common parent, using heuristics to guess file identities after any renaming and then performing a 3-way line-based diff between what it thinks are corresponding files.
But actually git doesn't really care - it's just another snapshot you've created and added to the DAG.
I haven't found it helpful to think of what's going on in git in terms of an "accumulated file diffs" abstraction because git has no notion of file identity (across commits).
> But the snapshot-model also doesn't really make a lot of sense for merges. It's a snapshot of what then?
It's a snapshot of the final result.
That's the beauty of the "commit as snapshot" model: each commit always contains the final result of the commit. It doesn't matter if the commit is a normal commit with a single parent, a merge commit with multiple parents, or even an initial commit with zero parents. It doesn't matter if the parent commits are unavailable (shallow repositories). It doesn't matter if the parent commits have been changed (grafts).
Accumulating all the diffs from two (or more) ends (until they are common again) is at least as useful.