Linear git history, Part I

Jun Sheng
5 min readAug 2, 2023

--

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:

image
  • Another way is instead of using git commit each time a developer saves changes, use git commit --amend so that there is always only one commit in the local develop branch. This workflow looks a little simpler:
image

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.

image

Conflicts can arise. To solve the conflicts, the following changed develop flows apply:

  • if you use branch-then-squash workflow:
image
  • if you use commit — amend workflow:
image

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.

--

--

Jun Sheng
Jun Sheng

Written by Jun Sheng

Jun Sheng is a Solutions Architect at Google in Kubernetes. Currently his main focus is in the GitOps Area.

Responses (1)