Commits in a branch or a PR is said to be well-organized if they have the following qualities:
Each commit contains a single logical change, and this change must stand on its own. i.e. each commit has a single responsibility, and that responsibility must be fully carried out.
For example, if the commit message says Move delete() from Person class to Address class
, the commit cannot contain the addition of delete()
to Address
class only; it should also contain the deletion of delete()
from the Person
class for it to be a complete implementation what is stated in the commit message.
Each commit has a well-written commit message i.e., it follows these guidelines.
Commits are ordered in a bottom-up fashion, each commit building on top of the previous one towards the end goal of the PR.
Rationale: Reviewers should be able to review one commit at a time.
Ideally, a commit does not modify more than 100 lines of code.
Rationale: Bigger commits are harder to review.
"Ask a programmer to review 10 lines of code, he'll find 10 issues. Ask him to do 500 lines and he'll say it looks good." --[source]
Commits containing mechanical changes (e.g. automated refactorings, cut-paste type code movements, file renames, etc.),
Every commit pass CI. when you merge a series of commits (without squashing), every commit in your push (not just the last commit) should pass CI.
Rationale: Build-breaking commits in the version history hinder the ability to use git bisect
for locating bugs.
Here is an example PR of commits that are organized as described above.
Refactor commits before pushing. It is unlikely that you can produce a series of commits that meet all the above criteria in the first try. In such cases, refactor commits until they meet the criteria. This S/O post describes how to refactor commits (even easier to do with visual tools such as SourceTree -- see this video).
When merging branch, the aim is to keep the version history neat so that it is easy to do things such as the following:
git bisect
.Use a merge commit if the commits are well-organized, and the branch tackles only one task. In this case the commit message of the merge commit should explain the full task.
Use a rebase-merge if the commits are well-organized and each commit is an independent task (as opposed to steps or a bigger tasks).
In other cases, consider reorganizing/splitting the branch to match one of the above.