Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

git-subtree pull merge conflict

Tags:

So I used git-subtree to have various branches of repoB in sub-directories of repoA, like so

git clone repoA cd repoA // some commits to repoA here git subtree add --prefix=src/dirA repoB branchA 

I did a few commits in repoA using

git subtree push --prefix=src/dirA repoB branchA 

Some time later, I committed something to repoB/branchA from another repoC, where branchA was added using git-subtree, too.

Now, I try

git subtree pull --prefix=src/dirA repoB branchA 

However, I'm getting a merge conflict for no apparent reason. The changes are simple and don't conflict at all -- as confirmed by patch.

I'm unsure how to fix this error. I already found two four other threads that deal with the same/similar issue:

  1. git-subtree pull complications
  2. git subtree pull -P whatever <repo> <ref> always merge conflict
  3. git-subtree conflict when pulling from central repo
  4. Git Subtree Merging reports conflict when merging a simple upstream change (this one is about subtree merge strategy though, see below)

I'm not sure this is related to different SHA-1s as I did not rebase my commits nor did I edit them; links 1 thru 3.

My problem is more like link 4, where git magically fails to do a simple merge. However, link 3 talks about the subtree merge strategy, not git-subtree in particular, so I'm not sure if this is applicable at all in my situation.

Situation looks to be the same though:

<<<<<<< HEAD ======= // changes from commit I try to pull from repoB/branchA >>>>>>> {commit SHA-1 from commit I try to pull from repoB/branchA} 

So I noticed that BASE is blatantly wrong in a three-way merge window (kdiff3). However, if that's the case, why doesn't git try to apply all earlier commits since base? Issuing

git log --oneline 

after the failed merge but before merging/trying to merge shows no duplicated commits prior to the offending commit. The version of the file that's shown as BASE is the file as it was when I first issued

git subtree add --prefix=src/dirA repoB branchA 

inside repoA.

So what's going on? It seems to be related that git subtree cannot find my commits for some reason, yet it does not try to apply the commit from BASE to HEAD~1, but only the commit I'm actually missing, HEAD.

How can I fix this error without screwing up either repository's history? Why can't git pull this simple commit and instead thinks it's a merge conflict?

Any insight will be much appreciated.

like image 666
FRob Avatar asked Aug 13 '14 19:08

FRob


People also ask

How do you pull a subtree?

Adding a subtreeSpecify the prefix local directory into which you want to pull the subtree. Specify the remote repository URL [of the subtree being pulled in] Specify the remote branch [of the subtree being pulled in] Specify you want to squash all the remote repository's [the subtree's] logs.

What is git subtree merge?

About subtree merges We will: Make an empty repository called test that represents our project. Merge another repository into it as a subtree called Spoon-Knife . The test project will use that subproject as if it were part of the same repository. Fetch updates from Spoon-Knife into our test project.

What is the subtree merge strategy?

This is where the subtree merge strategy comes in. It allows you to merge another repository (let's call it “Project B”) into your own, but into its own subdirectory. It takes a bit of effort to set everything up the first time, but after that a pull from the other repository is all that is needed to update.


1 Answers

Ok, so I figured this out. It was a two-pronged problem. First of all, my tree actually looked like this:

Status Quo

I had a commit in my tree that touched src/dirA, but was not yet pushed when repoB/branchA had already moved on.

I discovered that git subtree pull would not find the right base, because it is looking for a common ancestor, hence it used the version when I had last merged the trees, i.e. when I called git subtree add initially.

Now, to resolve the common ancestor problem, one has to git subtree split --rejoin, which performs a perfunctory merge, so git finds the right base again, i.e. after the commits pushed from repoA to repoB/branchA.

However, as you can see in my case, a git subtree split --rejoin followed by a git subtree pull does not solve my problems:

Broken History after git subtree pull.

Due to the fact that git subtree split creates a synthetic history of all commit that touch src/dirA irrespective of the fact whether or not they were pushed, the SHA-1 sums diverge. I split the synthetic history into its own branch split for demonstration purposes.

git subtree pull will of course succeed after git subtree split --rejoin. However, the next git subtree push will fail, because the histories of repoB and the synthetic tree are completely different after that.

Therefore, I had to go back before the offending non-pushed commit and pull the changes into my branch from there. This is complicated by the fact that git subtree split --rejoin is still necessary, because git subtree pull via git merge can still not figure out the correct base on its own.

Final solution to my problem

So the way I currently resolved my issue was by checking out the commit directly before the offending non-pushed src/dirA commit. Then I did a git subtree split --rejoin followed by a git subtree pull. This of course adds two merges into my main tree, which I could not figure out how to squash into one merge, and from what I read in source code, there doesn't seem to be an easy solution to that problem.

After the successful git subtree pull, I rebased the remaining commits from my master branch onto master_fix. Now the SHA-1 sums match throughout the shared history of repoA/master_fix and repoB/branchA.

This of course has the usual drawbacks of a rebase: if somebody else was working on master, their history will be ruined by git branch -m master_fix master.

like image 179
FRob Avatar answered Oct 17 '22 08:10

FRob