Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Git: Subtree Merge into a Deeply Nested Subdirectory?

Tags:

git

git-merge

I'm attempting to use git's subtree merge stategy where the subdirectory into which I want to merge is nested fairly deeply - currently four levels deep.

I followed the directions here to add the module repository as a remote, run git read-tree to get the remote code into a subdirectory in my local repo, and commit those changes.

My problem comes when I try to pull and merge changes from the remote into the master branch of my main project. Step 5 at the page above suggests a git pull with the -s subtree switch. This works correctly for me when my subdirectory is one, two or three levels deep, but not four.

Here is the result of the merge into a subdirectory 2 levels deep. You can see that the README file in sites/all/ has been updated correctly. In my remote repo, the README is in the root.

$ git pull -s subtree REMOTE_REPO master
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 2), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From /path/to/my/REMOTE_REPO
 * branch            master     -> FETCH_HEAD
Merge made by subtree.
 sites/all/README |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

Here the subdirectory is 3 levels deep: sites/all/modules/. This works fine, too, pulling the changes and updating the files.

$ git pull -s subtree REMOTE_REPO master
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 2), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From /path/to/my/REMOTE_REPO
 * branch            master     -> FETCH_HEAD
Merge made by subtree.
 sites/all/modules/README |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

But now my code is in a subdirectory 4 levels deep: sites/all/modules/my_module/. Git seems to pull the changes from REMOTE_REPO, but it doesn't update the files, instead telling me that it's already up to date.

$ git pull -s subtree REMOTE_REPO master
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 2), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From /path/to/my/REMOTE_REPO
 * branch            master     -> FETCH_HEAD
Already up-to-date!
Merge made by subtree.

And if I run it again right away, it doesn't pull the changes or update the files.

$ git pull -s subtree REMOTE_REPO master
From /path/to/my/REMOTE_REPO
 * branch            master     -> FETCH_HEAD
Already up-to-date.

Viewing the git log at this point will show me the changes from the remote repo and the merge, but the files in my checkout have not been updated.

Is this a bug, or am I doing something wrong?

Update: Chris Johnsen offered the following option, which throws an error:

$ git pull -X subtree=sites/all/modules/my_module/ REMOTE_REPO master
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 2), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From /Users/jeff/work/checkouts/compass_suite
 * branch            master     -> FETCH_HEAD
fatal: entry  not found in tree 173d4f16af4d2d61ae5c4b3446c392e8b49cc57d
like image 627
Jeff Avatar asked May 05 '11 21:05

Jeff


People also ask

What is subtree merge?

About subtree merges Typically, a subtree merge is used to contain a repository within a repository. The "subrepository" is stored in a folder of the main repository.

How does Git subtree work?

git subtree allows you to nest a repository as a subdirectory inside another. It's one of the various options for managing project dependencies in Git projects. You add a subtree to an existing repository where the subtree is a reference to another repository URL and branch/tag when you wish to utilize it.

How do you push to a subtree?

To push commits back to a subtree, select the (remote) subtree branch in the Branches view and invoke Remote|Subtree|Push. Pushing a subtree involves splitting changes back from main repository to subtree repository (more details can be found below).


2 Answers

Clarifying for future readers, the solution is in the comments of Chris Johnsen's response. If you're seeing the "fatal: entry not found in tree" error, get rid of the trailing slash at the end of your subtree prefix.

For example, if you are trying to pull a GitHub Pages subtree with a command like

git subtree --prefix gh-pages/ pull origin gh-pages

and there are conflicts you will get an error like

 * branch            gh-pages   -> FETCH_HEAD
fatal: entry  not found in tree 6e69aa201cad3e5888f1d12af0d910f8a10a8ec3

Just remove the trailing slash from the gh-pages directory

git subtree --prefix gh-pages pull origin gh-pages

This will work, and will try to merge. The worst scenario you can get is when the auto merge fails and you get an error like

Automatic merge failed; fix conflicts and then commit the result.

but you just have to resolve the conflicts manually and you are done.

like image 148
Jack O'Connor Avatar answered Oct 12 '22 18:10

Jack O'Connor


The subtree merge strategy artificially limits the depth of its search for where the subtree “fits” into the overall tree. Unfortunately this limit is hard coded (see match-trees.c:267).

Luckily, Git 1.7.0 added the subtree=… option to the (default) recursive merge strategy. This option lets you exactly specify the prefix so that Git does not have to guess (as much).

With Git 1.7.0 or later, try this:

git pull -X subtree=sites/all/modules/my_module REMOTE_REPO master
like image 33
Chris Johnsen Avatar answered Oct 12 '22 16:10

Chris Johnsen