How to: Git Subtrees
Introduction
At RiceApps, we often run into scenarios where breaking up code into sub-repositories will allow us to manage our code better. Our most pressing challenge is helping students learn web development. When there are errors within multiple parts of the system, they are unable to learn from that as clearly as they would from modularized code. Several students also express an interest in only learning parts of web development, such as middleware and frontend. In such cases, delineating what separates the two becomes critical. Moreover, this is becoming especially important for us as we transition to building apps that serve thousands of users and get tens of thousands of queries.
Git Subtrees are a wonderful way of breaking down your large, complex projects into modular chunks of code that can be used by team members individually. It also allows us to look at our code more modularly.
There are a number of ways to accomplish this goal. Most prominently, Git supports SVN style submodules as well as the cleaner subtree method to achieve this goal. This tutorial is about Git subtrees specifically. We hope that this will inspire others to use it in a similar fashion as well.
Use case
Our use case for the purpose of this article was for RiceDiscuss, which is a Reddit+Hackernews style webapp for the Rice community. We broke it down into an Apollo GraphQL backend and a React frontend.
If you’ve used these technologies before, you would understand how the backend and frontend in such a system can be very clearly differentiated until the need for deployment arises. Similarly, we wanted to be able to provide a solid foundation for continuous deployment as well as allow students to experiment independently. For this purpose, we realized that subtrees would be a perfect use case.
Without further ado, here’s how to use subtrees.
Assumptions
- You have the repositories that you want to set up as subtrees set up already. If not, make sure to set them up. You can also use subtrees to divide your existing complex project into subrepos.
- You have not set up the parent repository.
- You mostly understand the basics of Git.
Steps
- Make a monorepo on Github (if you haven’t) and clone it to your desktop.
2. Get the origin url of the repositories you want to make subtrees as within the larger monorepo.
In my case, the origin were
RiceDiscuss-backend: https://github.com/rice-apps/RiceDiscuss-backend.git; rice-discuss-frontend : https://github.com/rice-apps/rice-discuss-frontend.git
3. Type git remote add -f my-subtree origin-url and hit enter.
For this case-
git remote add -f frontend https://github.com/rice-apps/rice-discuss-frontend.git
git remote add -f backend https://github.com/rice-apps/RiceDiscuss-backend.git .
Adding remotes in the parent directory makes the rest of the process easier.
4. Now, if you have created a new monorepo, you should commit a file so that the remote master branch can be tracked. Or, you might want to create another branch depending on your use case.
5. Now lets add the subtrees! Type git subtree add — prefix dir remote branch.
dir refers to what you want the subtree to be called within the monorepo, and remote refers to what you made your remote as. branch is the branch of the sub-repo you want to be tracked.
I wanted the parent master branch to be tracked to the master branch for each subtree. For me:
If you do an ls, you should see two folders (I can see two folders frontend and backend).
You’re done! Now if someone commits to the subrepo, you can simply use:
git subtree pull — prefix=path remote branch
to get their changes into the parent repository. It will ask you to merge since it is effectively a merge.
For example, I’d made some changes to the frontend version of the code. To get those changes and any other changes made to the backend repo, I did this:
On the other hand, if you’d like to push changes from the parent repo to the subrepo, you can simply use:
git subtree push — prefix=path remote branch
Again, you will have to merge changes in the subrepo and specifying the — prefix= is necessary.
path here refers to the folder that represents the subtree. In my case, it was frontend and backend.
branch refers to the branch you want to pull changes from. You could specify another branch and merge changes accordingly.
In depth resources: https://git-scm.com/book/en/v2/Git-Tools-Submodules
Happy Hacking!