Monorepo with Git - Centralize without compromise
Historical Background
- Google was one of the pioneers (early 2000s) to adopt this model due to the considerable size of its code base.
- The widespread adoption of Git (2005) as a distributed version control system played a significant role in the popularity of Monorepos.
- Facebook’s transition to a Monorepo structure in 2010 drew the attention of other organizations to explore this approach. Since then, tools such as Bazel (created by Google) and Buck (created by Facebook) have emerged to specifically address the challenges posed by Monorepos. In this article, we will focus on the direct approach, using only Git to merge multiple repositories into a single Monorepo while preserving the commits history of each.
Practical Example
In this example, we will use 2 repositories:
- An empty repository that will serve as our Monorepo under the name ‘monorepo’, initialized with the command
git init- A repo1 (you can clone it using the following link => https://github.com/meriouliabdallah/monorepo-guide-repo1) with a Node.js project, initialized with the command:
node init -yand a file app.js with the following content: which displays a message with the current date and time in the console. The goal is to merge repo1 into the Monorepo while preserving the commit history.
1. Clone the Repository
To begin, create a folder (in our case it will be called monorepo-guide-repo1) in which we will have the empty Monorepo and the cloned repo1 with the following command:
git clone https://github.com/meriouliabdallah/monorepo-guide-repo1You should have the following structure:
2. Add a Reference
It is necessary to add repo1 as a reference from the Monorepo (you should navigate to the folder) with the following command:
git remote add repo1 ../monorepo-guide-repo1which adds a reference named repo1 from a local repository. It is possible to add a reference from a remote repository by specifying the URL after the name.
3. Fetch the Content of repo1
To retrieve the content of repo1 without merging its changes with the current branch, use the following command:
git fetch4. Create a Branch
Before merging, it is advisable to create a branch which in our case will be named feat/repo1 with the following command:
git checkout -b feat/repo1This practice is recommended for several reasons:
- Isolation of changes
- Possibility of review and testing before accepting the pull request.
- Facilitated collaboration.
- Easy rollback.
- Clear history.
5. Merge the Content
To merge the content of repo1 into our Monorepo, we will use the command:
git merge <nom du remote/nom de la branche> --allow-unrelated-historiesThe —allow-unrelated-histories flag allows merging two branches with different development histories. You will end up with the following content:
Note: If there are conflicts, you should resolve them before being able to merge.
6. Move the Content while Preserving History
To avoid having files from multiple repositories in the root of the Monorepo, it is advisable to create a folder for each repo. In our case, we will do this with the following command:
mkdir repo1Then move the files to this folder while preserving history, we will use the following command:
git mv README.md app.js package,json repo17. Commit the Changes
We will use the tool Goji (developed internally by Omnivya) which allows committing beautiful commits quickly while following best practices. To launch it after installation, it’s very simple:
goji -ithen
gojiand you will have the following interface:
specify that it is a feature, add the scope and the commit message
8. Push the Changes
Once finished, push the changes with the command:
git pushSince the branch is not present in the Monorepo, you will be asked to use the following command:
git push --set-upstream repo1 feat/repo1A pull request (PR) will be created and after review and acceptance, the content will be merged while preserving the history of each repository.
Conclusion
Through our practical example, we have explored a direct approach using only Git to merge multiple repositories into a single Monorepo while preserving the commit history of each.