-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Synthetic merge commits #4
Comments
Another point against the clean merge requirement: just because
could well result in a merge conflict! |
Right now, after going through all this, what I'm thinking going forward is (assuming I don't kill
Even though we lose the guarantee of a clean merge into Because of the loss of the clean merge guarantee, we must also indeed change |
To summarize, the ultimate proposed algorithm is:
This feels like a very clean contract to me, and I think it's "optimal" in the sense that in every case where there's an obvious right answer to a human about how to merge the Main tree of the synthetic merge commit, this algorithm would pick it, and it would also never pick any obviously wrong answers, all while maintaining the guarantee that, outside the subproject, the Main tree of the synthetic commit is always identical to that of some real, non-synthetic Main commit. |
Man, for a day there I almost convinced myself that case 2 was strictly a subset of case 1 (and hence superfluous); it isn't, but in the course of trying to prove it I've figured out exactly why and when it's needed. Math! Proofs! It's been so long! Case 2 is not needed in the example above, which actually also satisfies case 1 because for each of those merge commits, one parent is synthetic and therefore makes no changes to the Main tree (outside the subproject) relative to the merge base [FN1], so normal merge will result in a Main tree identical (outside the subproject) to the non-synthetic parent even if it does change the Main tree. However, case 2 is necessary if the synthetic merge commit
There's a unique merge base between One might hope that's unnecessary because if This example not only illustrates why case 2 is not a subset of case 1, but also illustrates why case 2 is exactly what we want: in the general case, even for octopus merges, if there's a unique merge base Footnote:
|
(Trying to understand it to help you :) But maybe in the end I will just wait for the time, when in test.sh does appear a new test that will simulate this behavior to test if your new strategy and algorithm works... :) ) |
This is the subject of a lengthy multi-part comment in the code, and is still not resolved and needs improvement:
git-subhistory/git-subhistory.sh
Line 186 in 18c3489
Basically the situation is, suppose there are two branches earlier in the history of Main that conflict in files outside Sub. Suppose each branch is split off into subproject commits, and the subproject commits are merged. What happens when we assimilate this merge commit (call it "assimilatee") back into the
HEAD
of Main? Obviously the subproject tree of the new synthetic merge commit will just be the root tree of the assimilatee, but what about the rest of the tree, which conflicts?git merge ASSIMILATE_HEAD
will not change anything outside the subproject. This is nice because it means thatgit-subhistory merge
is almost literally justgit-subhistory assimilate "$@" && git merge ASSIMILATE_HEAD
, isolating the complexity to justgit-subhistory assimilate
. I see no reason for this to be a hard requirement though, we could instead dogit-subhistory assimilate "$@" && git merge ASSIMILATE_HEAD --no-commit && git checkout HEAD <all-files-outside-subproject>
(to enforce thatgit-subhistory merge
doesn't change anything outside the subproject, which in my opinion is a hard requirement) if loosening that contract would somehow improvegit-subhistory assimilate
, like by making it more predictable or usable by a use case other thangit-subhistory merge
.ASSIMILATE_HEAD
andHEAD
, use that merge base's Main tree, since that can't possibly conflict withHEAD
; 3) if all else fails, just useHEAD
's Main tree, sinceHEAD
cannot conflict with itself. Note that paths 2 and 3 may result in a synthetic merge commit whose Main tree is very different from any of its parents, which seems pretty undesirable, ideally synthetic merge commits at least vaguely resemble the commit you'd get if you manually merged the parents.HEAD
, rather thanASSIMILATE_HEAD
; that way, the synthetic merge commit's Main tree would be as close to its parents as it could be while still being based on a real Main merge commit, and also while still doing "exactly the right thing in a common use case".HEAD
's history where these conflicting branches were merged (HEAD
is one commit so this must have happened at some point). Even if that only happens once, though, there's no guarantee that it will prevent a merge conflict betweenASSIMILATE_HEAD
andHEAD
: if the two conflicting branches turn out to be the merge bases, thengit-merge
will recursively merge the multiple merge bases together, until eventually a single fictitious merge base is left, which is used as the base of the 3-way merge; the tree in this fictitious merge base has no relationship to the tree of the actual merge commit, which you can set to whatever you want when you create a merge commit.ASSIMILATE_HEAD
will be a clean merge intoHEAD
outside the subproject, and instead specify that synthetic merge commits always resolve conflicts in the Main tree in favor of the first parent (^1
), or even to specify that synthetic merge commits don't even bother trying to merge the Main tree, outside the subproject they're always identical to the first parent's Main tree. (This might be desirable because it's common for automatic merging to result in a broken project state due to semantic merge conflicts, especially in the case of merge conflicts that are blindly resolved in favor of one branch or other.)The text was updated successfully, but these errors were encountered: