DIY monorepo flow with git subtree
Experimenting with getting a good monorepo setup going. The goal is to have something that’s easy to work with (ie it should feel like I’m only working in one repo), but to have “subrepos” inside it that correspond to different git repos. Basically what git submodules does, but without all the headache.
Some of the tools I’ve looked at in the past for this type of thing are git-subrepo and josh, but they all ended up being quite cumbersome.
This time I’m just using the built-in git subtree
command directly, we’ll see if that’s works any better.
How to get it setup:
First create an umbrella repo that’s going to hold all the other repos.
Then, add the other repos as remotes. In this case I’m adding a remote called API
(I’ve just been naming them the same as their respective repos):
git remote add API $(gh-remote)
And then turn a branch from the remote repo into a subtree locally. In this case, this will add a subfolder to my umbrella repo called API
, which pulls from the API
remote we added above, and grabs the dev
branch of that remote.
git subtree add --prefix=API API dev
In other words the prefix (API
) controls what local folder it gets written to, the second API
is the name of the remote you added earlier, and the final dev
is the branch in the remote repository that changes will be pulled in from.
Once you have all that done you can just work in your umbrella repo as normal, and commit stuff inside the ./API
folder as needed.
How to push your changes back up to one of the subrepos
If you want to push the changes you’ve made in this clone’s ./API
folder back up to the API
you first have to split your subdirectory out to it’s own branch. If you check out the new API
branch, it’ll contain only the contents of the ./API
folder.
git subtree split --prefix=API -b API
I named the remotes and the branches the same thing, so the above is a little confusing, but it can be written more generically as:
git subtree split --prefix=<subdirectory-name> -b <branch-name>
Once you have the commits split out, you can push them up to the remote repo like so:
git push API API:dev
Or in confusion reduction notation:
git push <remote-name> <local branch name>:<remote branch name>
In other words, you’re pushing your local branch <local branch name>
to a branch called <remote branch name>
on a remote called <remote name>
.
How to pull changes back down from the upstream repo
This is a little trickier because you need to pull it down from the remote to the branch you checked out. So step 1 is to check out your local split branch that you made with subtree split
earlier (git checkout <branch-name>
should do it)
Then once you have it checked out, just do a normal pull from the remote you specified
git pull <remote-name> <remote-branch-name>
This should leave you with the latest changes in your current branch (API
). To merge them back into your monorepo structure, switch back to the original branch:
git checkout master
Now you’ll have a ./API
folder, but it won’t yet contain the changes you pulled in from the remote. To get those you have to use git subtree merge
.
git subtree merge --prefix=API/ API
This will add a merge commit to your branch that brings in all the changes from the API
remote’s dev
branch, and you’ll be able to see them in you ./API
folder.