Semi-linear git history
Tags: git
Published: 2021-11-24 08:21:02

[Tons of *e-ink*](https://www.reddit.com/r/programming/comments/od5mge/things_i_wish_git_had_commit_groups/) has [been spilled all](https://www.bitsnbites.eu/git-history-work-log-vs-recipe/) over [the internet](https://www.bitsnbites.eu/a-tidy-linear-git-history/) about the *shape* of git history.

Personally, I want to be able to view the project history from a higher level **but** I also want to view the implementation details of a feature.

Typically viewing the higher level means that I want to see the history at the feature or bug-fix level. Additionally, I want to see when each feature made it to the main branch. I can view the history at this level by following only the first-parent commits of the log e.g. using the `--first-parent` flag of the `git log` command.

```bash
git log --oneline --graph --first-parent
* 663653a (HEAD -> main, origin/main, origin/HEAD) Feature 3
* 6803a96 Feature 2
* bdcdfa5 Feature 1
* 2b6737e Initial commit
```

If you are using the [Git Graph vscode extension](https://marketplace.visualstudio.com/items?itemName=mhutchie.git-graph) you could activate this view by checking the `Only follow the first parent of commits` option:

![First-parent commits only in git graph vscode extension settings](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/84orc042tfihjyv0aj2f.png)

The resulting view is the following:

![Following first-parent commits only](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a7n6himbqc2kza7rf1cv.png)

But, if I want to see the implementation details of a feature I can omit the `--first-parent` flag

```bash
git log --oneline --graph
* 663653a (HEAD -> main, origin/main, origin/HEAD) Feature 3
*   6803a96 Feature 2
|\
| * 1189f63 Call hw 5
| * 1f33ff3 Call hw 4
|/
*   bdcdfa5 Feature 1
|\
| * 7f48316 Call 2 more times
| * 9674fd6 Add console application
|/
* 2b6737e Initial commit
```

The corresponding Git Graph view is:

![All commits](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fbv9brcc05htz4yxaflh.png)

If the tool that you are using does not support semi-linear merges you could do it manually. Please keep in mind that there is no way to guarantee that the merge will produce a semi-linear result: someone else could have pushed changes to the main branch! Use the `-no-ff` flag to force the creation of a merge commit [in all cases, even when the merge could instead be resolved as a fast-forward](https://git-scm.com/docs/git-merge).

My current git workflow is the following:

```bash
# pull the latest changes of the main branch
git pull
# create a feature/topic branch before starting new work
git switch -c feature-branch
# make some changes and commit often in the topic branch
git add .
git commit -m "Add a comment about this commit"
# periodically rebase your work onto main branch
git fetch origin
git rebase origin/main
# make more changes
git add .
git commit -m "Add a comment about the new changes"
# switch to main branch and merge the topic branch using the `--no-ff` flag
# don't forget to set the message of the merge commit using the `-m` flag
git switch main
git pull
git merge --no-ff -m "Add feature 5" feature-branch
```

## See also

- [Semi-linear-merge discussion](https://stackoverflow.com/questions/59714347/semi-linear-merge) in Stack Overflow (see several answers).
- [Semi-linear merge](https://devblogs.microsoft.com/devops/pull-requests-with-rebase/#semi-linear-merge) in Azure devops
- [Rebase, merge](https://confluence.atlassian.com/bitbucketserver/pull-request-merge-strategies-844499235.html) in BitBucket
- [Semi-linear merge](https://docs.gitlab.com/ee/user/project/merge_requests/reviews/#semi-linear-history-merge-requests) in BitBucket
- I'm not sure if [GitHub supports semi-linear](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/about-merge-methods-on-github) merges

Happy coding \m/