Git Commit Messages
Git commit messages can help you understand why something happened months or years ago for your collaborators or yourselves. It affects a project’s maintainability, and hence plays a critical role in a project’s long-term success.
This section we summarize some common techniques to write a healthy commit message. Of course there are variations on the styles and conventions adopted by different projects/orgs, you should pick one and stick to it as much as possible to reduce chaos and inconsistency.
A commit message is nothing but some plain texts explaining the changes you made. We can describe a git commit msg convention from several perspectives:
- Style: e.g. capitalization, punctuation
- Content: info that the commit msg body should or should not contain
- Metadata: e.g. reference PR numbers, issue tracking IDs
Fortunately, well-established conventions exist and you don’t need to re-invent the wheel.
Several rules of a great git commit msg 1
- Separate subject from body with a blank line
- Limit the subject line to 50 characters
- Capitalize the subject line
- Do not end the subject line with a period
- Use the imperative mood in the subject line
- Wrap the body at 72 characters
- Use the body to explain what and why vs. how
E.g.
Summarize changes in around 50 characters or less
More detailed explanatory text, if necessary. Wrap it to about 72
characters or so. In some contexts, the first line is treated as the
subject of the commit and the rest of the text as the body. The
blank line separating the summary from the body is critical (unless
you omit the body entirely); various tools like `log`, `shortlog`
and `rebase` can get confused if you run the two together.
Explain the problem that this commit is solving. Focus on why you
are making this change as opposed to how (the code explains that).
Are there side effects or other unintuitive consequences of this
change? Here's the place to explain them.
Further paragraphs come after blank lines.
- Bullet points are okay, too
- Typically a hyphen or asterisk is used for the bullet, preceded
by a single space, with blank lines in between, but conventions
vary here
If you use an issue tracker, put references to them at the bottom,
like this:
Resolves: #123
See also: #456, #789
Notes
-
Not every commit msg needs both a subject and a body and sometimes a single line is fine for simple changes
E.g.
git commit -m "Fix typo in introduction to user guide"
-
Commit msgs with a subject and a body are not easy to write with the
-m
option. You should configure an editor for use with git. You can also define a commit msg template in the git config like below to remind you of the proper format and style when creating a commit msg:E.g. Consider a template file at
~/.gitmessage.txt
that looks like thisSubject line (try to keep under 50 characters) Multi-line description of commit, feel free to be detailed. [Ticket: X]
To tell Git to use it as the default message that appears in your editor when you run git commit, set the commit.template configuration value:
$ git config --global commit.template ~/.gitmessage.txt $ git commit
-
The blank line b/w the subject and body is essential and there’re a number of contexts in git like
git log --oneline
orgit shortlog
that the distinction b/w subject and body matter -
The 50 characters limit for the subject line forces the author to think about the most concise way to express his ideas. It also ensures that they are readable from the GitHub WebUI
-
Your subject line should always be able to complete the following sentence:
If applied, this commit will your subject line here
-
When you write the git msg body if needed, be mindful about its right margin and wrap text at 72 characters. Consider to set up a good text editor that automatically wrap texts for you
If you are using VSCode, the below settings can be a starting point:
-
Set VSCode as your default editor for git
# '--wait': Wait for the files to be closed before returning. $ git config --global core.editor "code --wait"
-
In your VSCode user
settings.json
, add the following wrap rule:"[git-commit]": { "editor.rulers": [ 72 ], "editor.wordWrapColumn": 72, "editor.wordWrap": "wordWrapColumn", "editor.wrappingIndent": "same" }
More customizations can be found here.
-
Git Commands 2
-
Commit changes
git commit -m 'Simple msg goes here'
git commit --amend
3- Sometimes we commit too early and possibly forget to add some files, or you mess up your commit msg format, you want to undo that commit, make additional changes you forgot, stage and commit again. Now this time use the
--amend
option! - This will take you staging area and uses it for the commit. The same commit msg will pop up and you can update this msg and it will overwrites your previous commit
- You end up with a single commit, the one amended will replace the results of the first
- Sometimes we commit too early and possibly forget to add some files, or you mess up your commit msg format, you want to undo that commit, make additional changes you forgot, stage and commit again. Now this time use the
-
Create a new branch
git branch bugFix
(Note that this does not set the HEAD at the new branch)git checkout -b bugFix
-
Combine changes
-
merge
-
Create a special commit that has two unique parents:
-
git merge bugFix
-
If you want to keep the
bugFix
branch up to date and continue work on it, we can merge themaster
branch to thebugFix
branch. SincebugFix
was an ancestor ofmaster
after the above merge, it simply move thebugFix
to the same commit asmaster
:git checkout bugFix; git merge master
-
-
rebase
- Explanations:
- Take a set of commits, copy them and paste them down somewhere else. It can be used to make a nice linear sequence of commits and the commit history will be much cleaner.
- Take all the changes that were committed on one branch and reply them on a different branch.
git rebase target_branch_to_reply_changes_to
- E.g. If you are on branch
bugFix
and want to move work frombugFix
directly onto themaster
. That way the commit history will looks like these two features were developed sequentially, when in reality they were developed in parallel. git checkout bugFix; git rebase master
- Now you can go back to the
master
and do a fast-forward merge to keep themaster
up to date:git checkout master; git merge/rebase bugFix
- Now you can go back to the
- E.g. If you are on branch
- Explanations:
-
-
-
Moving around commits
HEAD
: the symbolic name for the currently checked out commit. It always points to the most recent commit which is reflected in the working tree.- Detaching
HEAD
: attachHEAD
to a commit instead of a branch, e.g. when you checkout a particular commit rather than a branch. Typically you find the commit by usinggit log
to find the right hash.- Relative Refs
- Start from somewhere memorable e.g. branch
bugFix
or theHEAD
, and move from there- moving upwards, 1 commit at a time:
^
(Caret operator)- e.g.
- detach head to its parent:
git checkout HEAD^1
, or - checkout to the
master
’s grandparent:git checkout master^^
- detach head to its parent:
- e.g.
- moving upwards for <num> commits:
~<num>
(Tilde operator)- e.g.
- detach head and move upward 4 commits:
git checkout HEAD~4
- detach head and move upward 4 commits:
- e.g.
- moving upwards, 1 commit at a time:
- Branch forcing:
- Reassign a branch to a particular commit
- E.g. move the
master
branch to 3 parents behind HEAD:git branch -f master HEAD~3
- Start from somewhere memorable e.g. branch
- Relative Refs
-
Reversing changes
git reset
- Rewriting history: revert changes by moving a branch reference backwards in time to an older commit
- Warning: This cmd is great for local branches on your own machine, not good for remote branches that others are using!
- E.g. move the
master
branch to the parent of HEAD:git reset HEAD^
, or you can achieve this viabranch forcing
:git branch -f master HEAD^
- Modes
--soft
- does not touch the index file or the working tree at all but resets the head to the given <commit>
--mixed
- default mode, resets the index file but not the working tree, i.e. preserve changes but not marked for commit
--hard
- resets the index and working tree and any changes to tracked files are discarded
--merge
--keep
git revert
- Revert changes and share those reversed changes with others by creating a new commit, whose effect is to revert a particular commit
-
Move works around
- i.e. “Move this commit here, that commit there”
git cherry-pick <commit1> <commit2> <...>
- A way of saying that you want to copy the provided commits below your current location i.e. below the
HEAD
- Surely you may be able to achieve the same goal by using
git rebase
, butcherry-pick
is kinda of magic and easier to achieve this goal…
- A way of saying that you want to copy the provided commits below your current location i.e. below the
- Interactive rebasing
git rebase -i target_branch_to_replay_changes_to
- Cover the situation where you don’t know what commits you want: review a series of commits you’re about to rebase
- Git will open up a UI to show you which commits are about to be copied below the target of the rebase (i.e.
target_branch_to_replay_changes_to
).- You can reorder commits by changing their order in the UI
- You can choose to completely omit some commits
- And many more…