Strong Opinion on git Workflow
Strong Opinion
Whether working in trunk-based style or feature-branch style, the main (or trunk, also known as master) branch, along with any release branches if doing that thing, SHOULD NOT have merge-commits.
Reasons
I do not like to see merge commits in the main/trunk branch. I prefer a straight, unblemished history line.
* d589966 -(17 hours ago) (HEAD->main, origin/main,origin/HEAD)
* 5f3c420 -(17 hours ago) Merge branch ‘main’
|\
| * 4650c10 -(17 hours ago)
| * ae51497 -(17 hours ago)
| * b45fa11 -(18 hours ago)
* | d532336 -(17 hours ago)
|/
* 3d7bb50 -(18 hours ago)
* 6851949 -(18 hours ago)
* 28a191b -(19 hours ago)
* 01f6be2 -(19 hours ago)
Merge commits make reviewing the history more difficult for humans and source control tools to understand, explore, and resolve issues. Other than the visual part, they introduce complexities to reverting any issues that come up which cross the merge-commit line.
gitconfig
I have these settings in my global .gitconfig
to fail any merges that are not fast-forward. This forces me to resolve those with a proper rebase before merging into main.
[merge]
ff = only
[pull]
ff = only
Workflow
The workflow I use to resolve such problems is:
-
Create a branch at the current state
git branch wip
-
Reset the
main
branch upstream head either by resetting it then pulling or fetching then resetting it. -
Change to the
wip
branchgit checkout wip
-
Rebase
wip
onto mainget rebase main
(resolve any merge conflicts the usual way) -
Change to the main branch
git checkout main
-
Merge the
wip
branch into main (since it will fast forward now)git merge wip
-
Delete the
wip
branchgit branch -d wip
scripts.sh
As with anything that I do more than a couple of times, I have automated this workflow. The scripts below are in the set that I source into my current terminal. When I git pull
and it fails because it cannot fast forward (see the .gitconfig
settings above), I can just repo_wip_rebase
.
The script stops at the rebase step if there are merge conflicts to resolve. I use the normal git mergetool
and get rebase --continue
to handle those and then repo_wip_merge
to finish up.
Note that when there are no merge conflicts, repo_wip_rebase
does all the work.
function repo_wip_merge()
{
git checkout main || return $?
git merge –ff-only wip || return $?
git branch -d wip || return $?
}
function repo_wip_rebase()
{
git branch wip || return $?
git reset –hard origin/main || return $?
git checkout wip || return $?
git rebase main || return $?
# if there are merge conflicts, it stops here. Resolve them and then do this separately
repo_wip_merge
}
It is possible to do this as get aliases instead so that the commands might be: git wip_rebase
.
I tend not to create git aliases for two reasons. The most important is to keep custom stuff clearly custom. I do not want to get in the habit of using git xx
short cuts and erode my memory of the raw git commands. Having scripts with the prefix repo_
makes it clear to my brain that this is a custom thing. The second reason is that having these in a script separate from my home folder that is under source control is easier for me to manage.
Coda
I know there are other options for both of these. This is what works for me.
I hold no strong opinions on how you should automate your work. I only hold that you should automate as much as you can in whatever way works for you.