As we all know, git can be a bit fickle from time to time. My scenario was the following:
My repository (let’s call it “extension”) is a fork of another repository that I’m adding functionality to (let’s call this one “framework”). When I’m finished developing my extension, I’ll merge it back into framework, so I want to keep up with the changes that are being made to framework while I’m working on extension. This is generally a good idea if you want to avoid hour-long merge conflict hassle when you’re finally ready for merging (smaller increments - faster resolution). So to keep the extension repo up to date with the framework repo, I imported framework as a subtree into extension, in it’s own branch. These are the commands you need for that:
git remote add <framework repo> <url to other repo> git fetch <framework repo> git checkout -b <framework branch> <framework repo>/master git pull
Now I have a branch that’s set up to follow the master branch of framework. Since I don’t want to put the contents of framework in a subfolder in extension like you see in many subtree tutorials (because extension is basically framework + extra content), I’m not doing the read-tree command, but this doesn’t change the workflow. Once someone adds a commit to framework, you can pull it into your branch with a normal git pull. Now you want to add whatever has happened in framework to your master branch of extension:
git checkout <framework branch> git pull # newest changes from framework git checkout master git merge -Xsubtree=<framework branch> --squash --no-commit <framework branch>
The --squashoption keeps the extension repo clean by not showing all of framework’s commits in extension’s log. This makes sense because you want to keep track of your own changes and not of whatever changed in framework. The --no-commit option makes the merge stop before committing, so after your merge you have to git commit once.
This procedure can go very well or... not so well. Especially if it’s been a while since you updated extension to framework’s changes, there can be a lot of merge conflicts resulting from things like deleted empty lines etc. In this case you probably don’t want to go through every file that has been modified since your last update and resolve the conflicts manually. What you most likely want is to tell git to use the new file versions from framework and ignore whatever is still in your master branch. You would achieve that like this:
git merge -X theirs -Xsubtree=<framework branch> --squash --no-commit <framework branch>
BUT THIS IS A BAD IDEA. This will override all changes you made to files that have also been changed in framework. So no, really bad idea. You can do this, however, if you’re absolutely sure that you haven’t modified any of the files that framework modified, but I’d advise you to take a look at the diff before you do that. It’s easy to forget that you added a minor detail to a class you didn’t use otherwise. What you can do instead is the following:
>git merge -Xsubtree=<framework branch> --squash --no-commit <framework branch> # some/a lot of conflicts grep -lr '<<<<<<<' .
The last command shows you a list of all the files that have merge conflicts. Now you go through them one by one. If it is a file you modified, open a text editor and resolve the conflict manually. If you haven’t touched the file (again, check the diff to be sure), execute the following command for this file:
git checkout --theirs <path/to/file>
The --theirs option will simply replace the version of the file in extension with that in framework. In other words, this is a manual, file by file command for the version shown above.
After you’ve finished going through your list of files, git add them all (or maybe add them after resolving the conflict to be sure not to forget one). Now commit your changes:
git commit -m “Merged with current version of <framework>”
Phew. This is the time to check if your code still compiles or if you somehow missed a conflict and need to resolve it. If that’s the case, do that for all conflicts and then
git add <all files you changed> git commit --amend
to pretend this never happened and you did all this already during your merge.
Chances are, you have a branch your developing a new feature on, let’s call it “featurebranch”. You want to get this branch up to date with your master branch so, again, merging it later will not be such a pain in the ass. What you want to do now is rebase featurebranch onto master. This will make it seem as if you only branched off master after you updated it to framework’s changes.
git checkout featurebranch git rebase master
Again, you might get some merge conflicts that you can resolve the same way you did before. But be careful: the --theirs option will now mean whatever you did on featurebranch, so to accept changes from master you’ll want to exchange it with --ours.
If you did all this you’re ready to go! You updated both your master branch and your feature branch to the changes made in framework and now have whatever bugfixes or new features were added to it. If you do this often enough, you should have no trouble merging your extension back into framework once you are done.











