ctapobep / blog

My personal blog on IT topics in the form of GitHub issues.
6 stars 0 forks source link

Fully detached. Git workflow without local branches #10

Open ctapobep opened 2 years ago

ctapobep commented 2 years ago

Prerequisites: This article is for people proficient in Git. For those who know what Git Objects, Detached Mode, Symbolic References, Remote-Tracking Branches are.

Local branches are mostly useless

One of Gits pitching points is that you can commit locally and when needed - push to a remote. For younger generation of software developers this may be the only logical way of working, but those who used Subversion remember that svn commit would both commit and push. We didn't have an option to create local commits.

While there are certainly use cases for working with local commits and branches, most of time we don't really want that. There are no benefits of keeping the changes locally due to possibility of disk crashes, OS-bugs, our own mistakes. Due to this paranoia I push to the remote quite often - e.g. every 30-60 minutes. Then, when the work is ready I squash all the temporary commits and push to master. This is certainly not an uncommon workflow, Git even has a special support for it (--fixup and --autosquash).

And in this workflow since I constantly push to the remote I started to wonder - why would you ever want to have local branches? After all, this just adds extra steps (to create and then to delete branches) with no benefits - since all of my changes are always in the remote, I can easy checkout remote-tracking branches instead.

The workflow

So I'd like to describe this branchless approach in more details, here is how it works:

# checkout the latest changes
git fetch
git checkout origin/master

# create many small commits and push all the time
git commit -am 'wip'
git push origin HEAD:stas # my personal branch for the work in progress
...
git commit -am 'wip'
git push origin HEAD:stas

# Now let's squash
git reset --soft origin/master
git commit -m 'nice commit message'

# and push to master
git push origin HEAD:master

I don't actually type so much, instead I created aliases:

fetch
oma # git checkout origin/master

# Next one is equivalent to: git commit -m 'wip' && git push -f origin HEAD:stas
# `stas` is my personal branch 
commit && stas 
...
commit && stas
soft origin/master
git commit -am 'nice message'

Notice the use of personal branches - without them you can't create aliases. Sometimes I work on 2 things at a time, so I actually have 2 personal branches. And 2 aliases respectively: stas, stas1. Each team member can have their own set of personal branches. BTW, it often makes it simpler for code reviews because you don't have to remember the task number of feature name your colleague is working on, compare these:

# branch with a task name is handy when you work on many things at a time:
git checkout origin/1343-super-awesome-feature

# branch with personal name is handy when your/your colleagues work on 1-2 things at a time:
co origin/mike

Squashing without rebase

And since there are many people who squash using git rebase -i (which I think isn't the right choice), let me pay more attention to it. Remember that you can create whatever commits (including merge commits) you want, then soft reset HEAD to some older commit and the diff between current HEAD and that commit - all those files will be uncommitted. So it usually looks like this:

# commit my changes on and on
commit && stas
...
# merge latest updates with my commits
fetch
git merge origin/master

# now squash is super simple
soft origin/master
git commit -m 'Nice message'

Summary

While Git is purposely a distributed VCS, most of the time we don't need this feature. We mostly work with a single Git Server through which the whole team is collaborating. Creating local branches in such workflow doesn't make much sense, most of the time working in Detached Mode appears to be more effective.