Git. Hopping Between Branches Like Tree Frogs
![](https://habrastorage.org/getpro/habr/upload_files/eec/f1f/65c/eecf1f65cb785acee4d87b645f7cf12e.jpg)
There are many articles on this topic, but apparently not enough: from time to time I hear from colleagues (for the last 10 years, in 4 different companies):
“I can't look at the screen with the code, I have a different branch now.”
“I don't want to switch branches, I'll have to run code generation, my build files will reset, and then I'll have to rebuild it all again!”
“Pull a branch to view PR? It's inconvenient, you have to “stash” the changes, switch the branch.”
“And I “cloned” 3 copies of the project, `git clone` to the rescue!”
I haven't heard any of the above, but added them for dramatic effect. If you recognize yourself in any of the phrases, I suggest you read the article, maybe you'll find something useful.
Why “tree frogs”? Just a coincidence of the word “Tree” in “Tree frogs” and git worktree
which will be discussed.
And lastly, if you already know about git worktree
I suggest you skip straight to the section “My use case for git worktree”.
About problems and “wrong” decisions
Everything was already said in the introduction to the article, but for the sake of formality I will write it out again.
Problems:
Losing current code changes when changing branches;
Loss of temporary code generation/compilation files when changing branches.
Solutions:
Temporary commit and branch switch. Solves issue 1;
git stash
and branch change. Solves problem 1, but you can get lost in the stashes if you don't give them names;git clone
project to another folder. Solves 1 and 2;git wortree
project to another folder. Solves 1 and 2.
Solution 3 introduces new problems. You will have to duplicate .git files in each project clone – keep each project up to date, call git fetch
for everyone, etc.
Solution 4 is the one I use, and this article is essentially about that.
More about “wrong” decisions
Both solutions (1 and 2 from the section above) involve changing branches. This can be git checkout
or git switch
— I came across a comment that talked about the difference in more detail, I don’t want to duplicate it.
I call the solutions “wrong”, meaning that there is a better solution. Better in that it allows you not to lose code generation files, caches and other optimizations of the project build systems. I put the word “wrong” in quotes, because not everything is always clear, and sometimes `git stash` is the best solution, more on that below.
Solution with temporary commit
It's convenient to make a commit rather than stash
so as not to accidentally lose changes. I have had cases when instead of git stash pop
called git stash drop.
It's not fatal, but it's a bit of a hassle reflog
have to.
First, commit all changes
> git add -A && git commit -m 'tmp commit'
Then we switch to another branch with git checkout <branch name>
. When we return to the original branch, we might be interested in what changes were in 'tmp commit'.
> git diff $(git rev-parse HEAD)^!
The command above is the same as manually getting the commit hash from git log
and then view its contents with the command
> git diff 460802e4e3b4e070b5fc831c582a2d23aa4dbce5^!
Next we can do “uncommit” with the command git reset HEAD~1 --soft
or add changes to an existing commit by changing its name:
> git add -A
> git commit -m 'Fix all the release bugs, but introduce more' --ammend
Solution with `git stash`
Everything is the same, only instead of a commit we use stash, which is specially designed for storage temporary changes.
Example of using stash without name with removal from stack:
> git stash
# переключается на другую ветку
> git checkout some-branch
# делаем что-то, что нужно в some-branch, и возвращаемся обратно
> git checkout -
# возвращаем то, что у нас было
> git stash pop
Example using name in stash, searching by name, using without removing from stack (may be needed to avoid confusion when using stash often):
# создаем стеш с именем
> git stash push -m "trying to make something work"
# переключается на другую ветку
> git switch some-branch
# делаем что-то, что нужно в some-branch, и возвращаемся обратно
> git switch -
# смоотрим стек стешей, копируем нужный
> git stash list
# смотрим его содержание, чтобы убедиться, что этот тот самый
> git show stash@{0}
# примеряем stash, не удаляя его из стека
> git stash apply
When are “wrong” decisions right?
If you work alone, spend most of your time in one branch, make your own release branches, and rarely switch between them, then both solutions are great.
If the project does not have code generation (or in a particular case it is not needed) or a clean build of the project takes a few seconds, both solutions are also good.
Sometimes it's convenient to push temporary commits to make sure you don't lose anything, or to allow yourself or someone else to continue working from another machine.
If code generation takes several minutes, and you need to actively make commits to different branches, which require a clean build each time, then it is convenient to use git worktree
.
About git worktree
The team is simple, I heard about it from colleagues, I considered it example in the docsas a developer quickly switches without changing anything in his branch, fixes the problem, deletes the copy and returns to the original folder with the project – and since then I have been using it every day (a little differently, but more on that later).
— What does the team do?
— Creates a copy of the project. The copy looks at the specified branch.
– How git worktree
different from calling git clone
with a different folder name?
—git worktree
allows you to centrally manage your repository. In simple terms: just call git fetch
in any folder so that updates are visible in all.
Example of use:
# переходим в папку с проектом
> cd ~/project
# создаем 2 копии для двух релизных веток
> git worktree add ../release1 release-branch1
> git worktree add ../release2 release-branch2
# проверяем, что все создалось
> git worktree list
~/project d5e92f1 [master]
~/release1 9d77097 [release-branch1]
~/release2 8b2f312 [release-branch2]
Now the folders will contain copies of the project with the corresponding branch.
Ok, what about
git clone --reference <project path> --dissociate
?In short: with
git clone --reference
It's easier to shoot yourself in the foot, because the main project doesn't know that some clone is changing its files. An article has been written about other problems: git clone –reference Considered Harmful.
My use case for git worktree
I already wrote about the problems above, so I’ll hide them.
Problems on the current project that I solve with `git worktree`
On a project, you often have to work with three branches, each of which requires code generation. If you change release-branch1 to release-branch2 or master, you need to run a clean build, which will break with some probability, and you will need to manually delete build folders or edit something else. If it doesn't break, you still have to wait about 5 minutes.
In addition to release branches, there are branches where I work on tasks that can be “split” into other branches that are incompatible in code generation files. If you don’t run code generation, IntellijIDEA will highlight some of the project files in red. Sometimes it’s okay, and the tests will still pass on CI, but sometimes you need to run it and debug.
Sometimes you want to look at the PR branch locally and even run it, because it is more efficient and reliable (IMHO). Again, I don't want to waste time on temporary commits And loss of code generation files.
I always keep several project folders according to release branches:
master
(the main branch, new releases are branched off from it);release3
(new release branch – next release);release2
(previous release branch – release in progress);release1
(the oldest one, I delete it afterрелиз2
“will be released”);master
copy (the master always has the most PRs, so it’s convenient to have a clone).
In the current process that the team follows, any PR can be in one of the above branches, so when I need to look at something locally or do a PR myself, I open the project in the folder with the corresponding branch and start working from there. This allows me not to waste time on code generation.
Example
I do my task in master, I have a project open in the master folder. They ask me to quickly make a hotfix in release2. I open the project in the release2 folder and create a branch there: git checkout -b hotfix release2
. You can immediately run the project, bypassing the clean build. No need to fuss, hiding your current changes stash.
In cases where I need to jump between two branches that belong to the same release, I can temporarily create another git-worktree:
> git worktree add -b release1-2 ../release1-2 release-branch1
# сделать и запушить нужный мне фикс, а когда буду уверен, что папка больше не нужна,
# удалить папку, чтобы не копить мусор
> git wortree remove ../release1-2
Or make a regular one git stash
and switch right away. I prefer the latter when the action is one-time, and git worktree
— when it is clear that the branch will be used several times, for example, when releasing a hotfix. But I repeat, the main thing is not to waste time on code generation and other problems that arise when changing distant branches.
Problems using git worktree
In general, there are no problems.
![](https://habrastorage.org/getpro/habr/upload_files/f72/af9/937/f72af9937e273575e2df130f9f4c4fad.png)
But there may be some minor inconveniences:
Extra disk space.
You can't checkout the same branch in two places.
worktree
.You can't delete a branch if any of the
worktree
. Git will tell you about it, and here you just need to delete thiswortree
(git worktree remove <path>
).There may be problems in the functionality of unfinished tools. For example, I once refused the neovim-plugin neogit, because there were bugs in
worktree
(Github Issues: 1, 2).
If you know of other problems, write in the comments or in a private message, I will supplement the article.
Finally
Try turning it on git wortree
into your workflow. You might save a lot of time and nerves, especially if the project involves working with many branches, and a clean build takes a lot of time.