4

I have a git repo with a second repo as a submodule. Both repos have corresponding branches for various features which usually correspond pretty directly, e.g. if the parent repo is on branch A, the submodule will also be on branch A.

In both the parent and the submodule repos, as time goes on more branches are added, and sometimes I merge older branches into later ones, e.g. merge branch A into branch B periodically in both repos, while branch B is also getting new commits which are not in A.

The problem comes when I merge branch A into B for the parent repo after the A submodule was updated. I want to ignore those updates (i.e. I always want to discard submodule updates during merges, otherwise the parent branch B might end up with submodule branch A which is wrong).

I found some instructions for how to always use "ours" when merging files, but it seems ineffective for submodules. Here's how to reproduce the problem with a submodule:

# first command will update your ~/.gitconfig, you may want to undo it later
git config --global merge.ours.driver true

mkdir -p tmp/sub
cd tmp
git init
git commit --allow-empty -m "chore: Initial commit"
echo 'sub merge=ours' >> .gitattributes
git add .gitattributes
git commit -m 'chore: Preserve sub during merges'

mkdir sub
pushd sub
git init
git commit --allow-empty -m "sub: init"
popd
git submodule add ./sub
git commit -m 'add submodule'

git checkout -b demo-prod
pushd sub
echo prod > readme
git add readme
git commit -m 'add prod readme'
popd
git commit sub -m 'sub: add prod readme'

git checkout -
git submodule update
pushd sub
echo master > log
git add log
git commit -m 'add master log'
popd
git commit sub -m 'sub: add master log'

git checkout demo-prod
git merge -

The expected result is an automatic merge, but the actual result is a merge conflict because "sub" has new commits in both branches of the parent repo.

How can I achieve easy merging in such a situation?

John Zwinck
  • 239,568
  • 38
  • 324
  • 436

2 Answers2

0

Merging with submodules is tested in t7405-submodule-merge.sh, and it has always included a merge -s ours which should do what you want.
(test added in commit f37ae35, Apr. 2009, Git v1.6.2.4)

An actual merge for submodule was discussed in Nov. 2017, for this patch, in commit 6c8647d, Jan. 2018, v2.17.0-rc0, but you should not need it here.

Using the merge strategy "-s ours" can be too much (in that it ignores all commits from the other branch), but the merge recursive strategy option could help: git merge -X ours.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • 1
    `merge -s ours` avoids the conflict but as you said it is too much as it doesn't actually merge any other changes. I only want "ours" for submodules. I tried `git merge -X ours` as you suggested but that seemed to have no effect (i.e. it generates the same conflict as regular merge). I'm not sure what you were expecting to happen with `-X ours` exactly, but it seems like it doesn't change anything. – John Zwinck Sep 22 '19 at 00:44
  • Any ideas how to get `-X ours` to work for submodules? Surprisingly, this only applies to files, not submodules... I would even be fine with `-s theirs`, but this doesn't exist, only `-s ours`, and that is not what I want. And I can't just `reset` to the other branch, because I lack force-push permissions to fiddle around with the history. – SvenS Mar 25 '21 at 15:25
  • @SvenS Not sure. I suspect that kind of merge would need to be applied inside the submodule itself. – VonC Mar 25 '21 at 15:44
  • Thanks for taking the time to answer back ;) Not sure how I'd go about that, maybe set the submodule pointer to the one in "theirs" and committing this before doing the merge so that there won't be any conflict. Just need to find out the hash to set to, and that isn't a general solution. In the meantime I have a workaround: switch to master, merge with my branch using `-X ours`, then switch back to my branch and make a fast-forward merge to master. Afterwards reset the master to its previous HEAD. Replicates the `-X theirs` behavior pretty well, though maybe the merge message is wrong... – SvenS Mar 26 '21 at 07:36
  • 1
    @SvenS That workaround might be scripted to set the proper merge message at the end. I will monitor your bounty (https://stackoverflow.com/q/30994172/6309) to see if anyone comes up with an actual solution. – VonC Mar 26 '21 at 07:59
  • oh yeah, I just ran out of space ;) `git merge` takes a -m parameter for this, so the message can be overridden with no problem. – SvenS Mar 26 '21 at 12:02
0

The problem comes when I merge branch A into B for the parent repo after the A submodule was updated. I want to ignore [any branch-A submodule] updates (i.e. I always want to discard submodule updates during merges, otherwise the parent branch B might end up with submodule branch A which is wrong).

When you merge, if a path was changed in the other branch and not touched at all in the local branch Git sees no chance of conflict so it "trivially" takes the other-branch version. This is so far under the radar Git doesn't even see it as an automerge, it runs no merge driver at all for it, so far as I can tell there's nothing to shut off, you can't stop it, …

… which might seem like a flat "can't be done", but this is Git. Anything Git does automatically you can easily fix up afterwards if you like. Either run the merge --no-commit and git checkout @ path/to/submodule before committing, or if you forget do git checkout @~ path/to/submodule and git commit --amend. Lots of custom workflows can be automated through config settings or hooks, but I think this one is oddball enough you're going to have to script it if you want automation.

jthill
  • 55,082
  • 5
  • 77
  • 137
  • I'm not worried about the "no conflict" case because there are always conflicts. This happens because the submodule of B is updated in a way that it is never updated in A, so whatever updates occur in A will always conflict with the existing ones in B. Git really does detect conflicts every time, I just need to know how to tell it what to do when it detects them. – John Zwinck Sep 22 '19 at 00:11
  • Well, the same thing, then, just `git checkout @ path/to/submodule` before committing, the effect of the conflict is practically the same as the `--no-commit`. – jthill Sep 22 '19 at 00:23