Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are there any git merge-strategies for ignoring submodule updates on a commit or branch merge into target branch?

My usecase is: I have a two branches of a repo containing submodules. I want to set up auto-integrate of these two branches. But whenever there are submodule updates on both branches, my auto integrate script fails with conflicts on submodules. Is there way to ask git to ignore the changes in submodule pointers during the merge? Or provide something like merge=ours for the given submodules?

I've read this here.

I tried the merge strategies in the above page for submodule folder like this (but it only works for files)

submodule-name merge=ours
like image 668
Aila Avatar asked Jun 23 '15 05:06

Aila


People also ask

What are some of the merge strategies in git?

The most commonly used strategies are Fast Forward Merge and Recursive Merge. In this most commonly used merge strategy, history is just one straight line. When you create a branch, make some commits in that branch, the time you're ready to merge, there is no new merge on the master.

Which strategy is used by git for merging two branches?

Some strategies can also take their own options, which can be passed by giving -X<option> arguments to git merge and/or git pull . This is the default merge strategy when pulling or merging one branch. This strategy can only resolve two heads using a 3-way merge algorithm.


1 Answers

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 adding 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.

like image 67
Marco Luzzara Avatar answered Oct 24 '22 03:10

Marco Luzzara