In the following articles, I am going to discuss several topics around git repo, workflow and finally the GitOps.
Linear git history is a type of git history where each commit is a direct descendant of the previous commit. This creates a linear chain of commits.
The benefit of having a linear git history
Linear git history has several benefits, including:
- It is easier to understand and follow than a non-linear history.
- With linear history, each commit has a clear purpose, making them easier to review.
- Linear history makes it more simple to use
git bisect
to track a bug. - It is also easier to cherry pick a linear history for release.
GitOps suggests a trunk-based development model. With linear git history, the trunk can be made clear, easy to navigate and manipulate, hence the life of GitOps will be made easier.
How to get a linear git history
Often people complain that linear git history is hard to achieve, saying only experienced developers can pilot a project with linear history. However, a linear history can be reached by following the following breakdown practices and they are easy to comprehend.
I will explain the practices within the pull-request workflow. They can be extended to other change-review-accept workflows.
One commit per pull-request
A good start towards having a linear history is to follow the one-commit-per-pull-request practice. This also implies one pull-request for one thing: one single pull-request only solves one issue, or implements one feature or accomplishes one task.
To make this execute smoothly, these are additional suggestions:
- The commit in pull-requests shall pass all CI tests, sometimes this also is mentioned as “never break master”
- Write meaningful commit message, especially include the issue(feature, task) id keep track between pull-requests and issues
- Break large issues(feature requests, tasks) into sub issues to avoid submitting huge commits.
As an individual developer, there are generally two ways to achieve this one-commit-per-pull-request.
- The first way is to develop and commit as usually in the local branch on developers’ desktop, but squash them into one commit before creating a pull-request. For convenience, this workflow will be called
branch-then-squash
. The work flow looks like this:
git checkout -b feature-1-dev main
#do edit
git commit
# do more edit
git commit
# ...5
# create a ready for pull-request branch with one squashed commits
git checkout -b feature-1-pr feature-1-dev
git reset --soft $(git merge-base main $(git branch --show-current))
git commit # Input meaningful commit message here for review
git push your-remote-repo feature-1-pr
# create a pull-request on your-remote-repo from branch feature-1-pr
If the PR needs additional work to do, the work can be repeated with a slight change. The whole workflow will look like this:
- Another way is instead of using
git commit
each time a developer saves changes, usegit commit --amend
so that there is always only one commit in the local develop branch. This workflow looks a little simpler:
The main drawback of the second workflow is that the local edit history is lost.
It is recommended to have an automated check on receiving a pull-request about this one-commit-per-pull-request and giving warnings or rejects for those pull-requests having more commits. GitHub users can use an action to check the number of commits in a pull-request is recommended. For example, this OneCommit action can be used. Otherwise, you can develop a CI script using your CI tools to check whether a pull-request has only one commit.
Having one-commit-per-pull-request can also make the next step easier.
Do rebase merge when the pull-request gets accepted
A linear git history should be free of merge commits. That says the only way of accepting a pull-request is to do “rebase merge”. Rebase merge means each time when a pull-request is accepted, the changes in that pull-request need to be rebased against the current head of the main branch.
Conflicts can arise. To solve the conflicts, the following changed develop flows apply:
- if you use
branch-then-squash
workflow:
- if you use commit — amend workflow:
If you are using GitHub to host your repository, you can set your repository to only allow rebase-merge under “Settings -> Pull Requests”.
To reduce the cases of conflicts, good communication between developers is necessary. For example, reviewing code synchronously could reduce the lifespan of a pending PR then reduce the possibility of conflicts.
Summary and what’s next
To sum up, having a git linear history has many benefits. It is worth adjusting the development workflow to have a linear git history.
This doc discusses deeply the trunk-based development, including a lot of cooperation pitfalls and suggestions, nice to read.
And in the next post, I will discuss more about linear git history.