To be honest, I was surprised too, but the documentation quickly explains what is happening:
If the submodule histories have diverged and are committed to diverging branches in a superproject, [...] Git will not attempt even a trivial merge for you.
I guess the answer to the question "why don't merging strategies work?" is: because there is no merging process between different submodules versions. The approach suggested by the doc is still not that complex if you need to do an actual merge, but it is even "easier" in your case.
Suppose you are on master
and you want to merge my_branch
in it. There are 3 scenarios:
git merge my_branch
This is very well explained by the documentation I already linked, so there is no reason why I should repeat it. I still suggest you to read it because you could end up in an unexpected situation. I am telling you about it at the end of the answer.
git merge -Xours my_branch
I would not know if this is a correct way to solve this, but there is a very simple shortcut to clear any conflict on submodules. Because git does not do any merge operation on submodule versions, it only tells you about the diverging versions. So, if you look at the index, you still find the 3 versions coming from the 3-way merge
(output of git ls-files -s
)
100644 acbc19aafbf0c14e67f9a437d465351a7e96388b 0 .gitmodules
100644 3da4fcc7b3a9bc886b50977dc35e10f48a42416b 0 your_files
160000 e826e1b762a17dbc7225b36db4a9f7f6c08774ad 1 submod
160000 fef2abfb901d20ba1f4d1023ba384bbc6afbc392 2 submod
160000 cd5caa8674fe078e0fb875861bb075ccb60cfee0 3 submod
The first one (e826e1b) is the merge base, but you are interested on the one with index 2. Unfortunately, I think you cannot refer to the submodule revision with the common :2:./submod
while add
ing it to the index because it is not a real path. The easy shortcut I told you about is this one :)
git add submod
And then you can commit
. It automatically keeps the master
submodule version, but it is not the best way of course, since the behaviour could change in some future versions of git. I will show you the other way for the next scenario.
git merge -Xtheirs my_branch
Here things get complicated: we cannot use the above trick (simple add
that for now defaults to the master
submodule revision), because now we need the revision with index 3, which is :3:./submod
that does not work with the add
command.
You can also update the index with the plumbing command update-index
and pass a raw cache-info entry, like:
git update-index --cacheinfo 160000,cd5caa8674fe078e0fb875861bb075ccb60cfee0,submod
mode 160000
is the one used for submodules (more specifically for git links), cd5caa86...
is the object we want to add to the index, and submod
is the path. Here, strangely, putting the submodule name works.
If you need to script this, you cannot obviously put a hard-code object there, but you can retrieve it with
git rev-parse :3:./submod
In the end, this is the command to run to keep theirs
version of submodule:
git update-index --cacheinfo 160000,$(git rev-parse :3:./submod),submod
Instead, to keep ours
version, replace :3:
with :2:
.
Git does not merge submodules, but could reuse a merge
Also this part is very well explained in the reference, but you need to be very careful. In summary, git does not try to merge your submodules, unless:
- the merge can be solved using a fast-forward strategy, and in this case it uses the most recent commit, OR
- the merge for conflicting submodule revisions is already in the submodule repository, so git will just add that merge commit to the index (instead of
ours
or theirs
).
In my humble opinion, git team should add an option to give us more control on how we want to handle these situations. By the way, you can always allow the commit and then --amend
it, or fix it before committing using git merge --no-commit
. In both cases you cannot use the index revisions (like :2:./submod
), but HEAD^1
and HEAD^2
in the first case, and the MERGE_HEAD
in the second case. If this is a very unlikely scenario, just skip it, otherwise I would commit and --amend
if it is acceptable.
Conclusions
I hope there is an easier way to merge branches with different submodule revisions, so do not consider this answer as the one and only possibility. Moreover, after the merge you may need to deinit
and reinitialize (or resync
) your submodule, but as far as this is scriptable, this is not a real issue. If I were you, I would create an alias containing all these operations.