Long ago, I learned that dependencies should be stored with the project code. Then, when you return to the old version of the code, it is much easier to restore the environment.
My project has several dependencies. Most of the dependencies live in the git repositories. The project itself also lives in the gita.
One of the libraries we use is frequently updated. We are sitting on the developer version, and often we ourselves commit to it the code that is required by our project. That is, it is required to promptly skip our edits through the main repository of this library — you don’t want to create and maintain your fork for a number of reasons.
')
Previously, I simply copied the dependencies to the project folder, and added to each file a VERSION.TXT with its version. But, if you need to work with the current version of third-party code, it is inconvenient. Yes, and copy files by hand when there is a git is somehow stupid. I want to find a more modern solution.
The most widely advertised and fashionable feature for working with third-party repositories is
git submodules (“submodules”). Naturally, first of all I began to look at her.
Before that, I had already tried working with sub-modules in home projects. While one person uses the repository carefully, there are no special problems. In any case, it is much more convenient than copying the code by hand.
However, as soon as I tried to transfer my experience to a more serious workflow, it turned out that not everything is so simple.
This is what we encountered:
- Each person working with the main repository must have access to the repository from which the submodule is taken.
- In general, it is not possible to get a complete working copy with a single command. Now after git checkout you need to do git submodule update --init.
- The same applies to some other teams of the gita. For example, git archive ignores submodules - it is no longer possible to pack an entire project into an archive with a single command.
- From the top-level project, no changes are seen within the submodules and vice versa. To find out the full status of a working copy of a project, it is necessary to request it for each sub-module and for the parent project separately. Without submodules, it suffices to say git status anywhere within the working copy.
- After replacing the subdirectory root directory with something else (for example, another submodule), you need to manually delete the old version in all working copies.
- The git submodule command does not understand the standard options --git-dir and --work-tree. It can only be run from the root of a working copy. This makes automation difficult.
In general, quite unpleasant. Each of the problems can somehow be circumvented, but there are too many problems and new ones constantly appear.
From submodules had to be abandoned.
But every cloud has a silver lining. Knowledgeable people
suggested that in gita for a long time (even before 1.5.2) there exists an alternative solution -
subtree merge strategy (“subtrees”).
The idea is to take the commit history from the external project and redirect it to the internal subdirectory. It uses the standard guitar mechanism for working with external branches.
Example from the documentation: add the code from the master repository branch Bproject (lies in / path / to / B) to our project in the dir-B / subdirectory.
$ git remote add -f Bproject /path/to/B
$ git merge -s ours --no-commit Bproject/master
$ git read-tree --prefix=dir-B/ -u Bproject/master
$ git commit -m "Merge B project as our subdirectory"
You need to pay attention to the -f switch of git remote add. He tells the gita to immediately fetch this remote.
Further, the changes in Bproject are tightened with the git pull command with explicit indication of the desired branch and merge strategy:
$ git pull -s subtree Bproject master
If the corresponding remote-in subtree is not added to the working copy, the history of the main repository does not show the name of the branch from which the changes are pulled.
The problem is purely cosmetic, and does not affect the work. It is treated by adding this remote to the working copy:
$ git remote add -f Bproject /path/to/B
In the future, if new branches appear, you can pull up the changes in the remote:
$ git fetch Bproject
There are a couple of drawbacks:
- As in the case of the usual merging of branches, in the commit logs, the history of the subtree is mixed with the history of the main project.
- Submitting changes to a subtree design is much more complicated than with submodules. But it's easy to get around by making changes to a separate clone of this project.
None of these shortcomings is critical to our workflow.
Working with subtrees is much more convenient than with submodules. It does not need to re-educate users, it is easier to automate. Subtrees easier to maintain. Recommend.
By the way, on Gitkhab there is a project aimed at developing work with subtrees:
git-subtree .
Additional reading:
- Pro Git: Submodules
- Pro Git: Subtree Merging