Skip to content

Commit History

Merge commits

In the context of Intentional Commits, a merge commit has the semantics of ending a line of development by merging the branches back together.

Often developers are not even aware that they are creating a commit. There are certain cases when merging in Git where a merge commit is created but the developer's mental model is more on the level of "merging the code" rather than "merging the commits". Many developers never or rarely see the commit graph and their resulting commits are mostly incidental rather than intentional.

An extreme case of that is "merging back and forth", for example when a mistake was made while resolving merge conflicts so one tries again on top of the existing merge commit. Essentially, trying to get the codebase back in shape one completely messes up the history with commits that are meaningless or even harmful.

Unintentional merge commits can be avoided in some situations using rebase, if doing so results in the intended commits. In the context of Intentional Commits, a merge commit has the semantics of ending a line of development by merging the branches back together, so merging downstream (mainline to feature branch) usually has no meaning.

Intentional Commits does not forbid merging downstream, but it encourages only doing so if the resulting merge commit is intentional, not a byproduct of "updating the project".

Squashing

Squashing can be used to remove commits that have been created accidentally. Commits like "Fix typo in previous commit" can be simply squashed into that previous commit. The result should always be the intended commit history.

Automatic squashing of whole feature branches as a merge strategy for pull request is not an intentional action. Intentional Commits therefore rejects squashing as a "process", but encourages intentional squashing on a per-commit basis (e.g., using interactive rebase in Git). Squashing all commits of a branch can still be used on a case-by-case basis as long as the resulting commit communicates the intent, i.e., if it is the commit that we would have created in retrospect if we had had all the information at the time.

Rebase

Intentional Commits encourages frequently rebasing the local branch onto the upstream branch (usually feature branch onto remote main or local trunk onto remote trunk, or something similar), but it doesn't prescribe it.

Note that when talking about rebase, people often assume a certain context and thus can mean different things. Some examples of what they might imply, depending on their context and background:

  1. rebasing a local feature/topic branch onto the target branch (e.g., main)
  2. rebasing the local trunk (main branch) onto the respective remote branch
  3. rebasing a branch that was accidentally branched off from the wrong base branch (based on the wrong commit, so it gets rebased onto the intended base)
  4. rebasing a public feature branch in a closed source code base where the team has the agreement to not base feature branches onto each other's feature branches
  5. rebasing a published branch in an open source project
  6. rebasing as a merge strategy

When someone talks about "never rebasing" or "always rebasing", make sure you understand the context that they may be implying.