Skip to content

Instantly share code, notes, and snippets.

@hlayan
Forked from luismts/GitCommitBestPractices.md
Created November 4, 2022 08:48
Show Gist options
  • Save hlayan/15e5563cb94ce4cab03d12b40e01a69e to your computer and use it in GitHub Desktop.
Save hlayan/15e5563cb94ce4cab03d12b40e01a69e to your computer and use it in GitHub Desktop.
Git Tips and Git Commit Best Practices

Git Commit Best Practices

Basic Rules

Commit Related Changes

A commit should be a wrapper for related changes. For example, fixing two different bugs should produce two separate commits. Small commits make it easier for other developers to understand the changes and roll them back if something went wrong. With tools like the staging area and the ability to stage only parts of a file, Git makes it easy to create very granular commits.

Commit Often

Committing often keeps your commits small and, again, helps you commit only related changes. Moreover, it allows you to share your code more frequently with others. That way it‘s easier for everyone to integrate changes regularly and avoid having merge conflicts. Having large commits and sharing them infrequently, in contrast, makes it hard to solve conflicts.

Don't Commit Half-Done Work

You should only commit code when a logical component is completed. Split a feature‘s implementation into logical chunks that can be completed quickly so that you can commit often. If you‘re tempted to commit just because you need a clean working copy (to check out a branch, pull in changes, etc.) consider using Git‘s «Stash» feature instead.

Test Your Code Before You Commit

Resist the temptation to commit something that you «think» is completed. Test it thoroughly to make sure it really is completed and has no side effects (as far as one can tell). While committing half-baked things in your local repository only requires you to forgive yourself, having your code tested is even more important when it comes to pushing/sharing your code with others.

Write Good Commit Messages

Begin your message with a short summary of your changes (up to 50 characters as a guideline). Separate it from the following body by including a blank line. The body of your message should provide detailed answers to the following questions: – What was the motivation for the change? – How does it differ from the previous implementation? Use the imperative, present tense («change», not «changed» or «changes») to be consistent with generated messages from commands like git merge. Having your files backed up on a remote server is a nice side effect of having a version control system. But you should not use your VCS like it was a backup system. When doing version control, you should pay attention to committing semantically (see «related changes») - you shouldn‘t just cram in files.

Use Branches

Branching is one of Git‘s most powerful features - and this is not by accident: quick and easy branching was a central requirement from day one. Branches are the perfect tool to help you avoid mixing up different lines of development. You should use branches extensively in your development workflows: for new features, bug fixes, ideas...

Agree on A Workflow

Git lets you pick from a lot of different workflows: long-running branches, topic branches, merge or rebase, git-flow... Which one you choose depends on a couple of factors: your project, your overall development and deployment workflows and (maybe most importantly) on your and your teammates‘ personal preferences. However you choose to work, just make sure to agree on a common workflow that everyone follows.

The following document is based on experience doing code development, bug troubleshooting and code review across a number of projects using GIT, including libvirt, QEMU and OpenStack Nova. Examination of other open source projects such as the Kernel, CoreUtils, GNULIB and more suggested they all follow a fairly common practice. It is motivated by a desire to improve the quality of the Nova GIT history. Quality is a hard term to define in computing; one man's "Thing of Beauty" is another man's "Evil Hack". We can, however, come up with some general guidelines for what to do, or conversely what not to do, when publishing GIT commits for merge with a project, in this case, OpenStack.

This topic can be split into two areas of concern

  • The structured set/split of the code changes
  • The information provided in the commit message

Formatting Rules

  • Capitalized, short (50 chars or less) summary

  • More detailed explanatory text, if necessary. Wrap it to about 72 characters. In some contexts, the first line is treated as the subject of an email 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); tools like rebase can get confused if you run the two together.

  • Always leave the second line blank.

  • Write your commit message in the imperative: "Fix bug" and not "Fixed bug" or "Fixes bug." This convention matches up with commit messages generated by commands like git merge and git revert.

  • 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
    • Use a hanging indent

Examples of good practice

Example 1 (no description, only summary)

  commit 3114a97ba188895daff4a3d337b2c73855d4632d
  Author: [removed]
  Date:   Mon Jun 11 17:16:10 2012 +0100

    Update default policies for KVM guest PIT & RTC timers

Example 2 (description as bullet points)

  commit ae878fc8b9761d099a4145617e4a48cbeb390623
  Author: [removed]
  Date:   Fri Jun 1 01:44:02 2012 +0000

    Refactor libvirt create calls

     - Minimize duplicated code for create

     - Make wait_for_destroy happen on shutdown instead of undefine

     - Allow for destruction of an instance while leaving the domain

Example 3 (description as plain text)

  commit 31336b35b4604f70150d0073d77dbf63b9bf7598
  Author: [removed]
  Date:   Wed Jun 6 22:45:25 2012 -0400

    Add CPU arch filter scheduler support

    In a mixed environment of running different CPU architectures,
    one would not want to run an ARM instance on a X86_64 host and
    vice versa.

    This scheduler filter option will prevent instances running
    on a host that it is not intended for.

    The libvirt driver queries the guest capabilities of the
    host and stores the guest arches in the permitted_instances_types
    list in the cpu_info dict of the host.

    The Xen equivalent will be done later in another commit.

    The arch filter will compare the instance arch against
    the permitted_instances_types of a host
    and filter out invalid hosts.

    Also adds ARM as a valid arch to the filter.

    The ArchFilter is not turned on by default.

Git Tips

Properly Configure your ~/.gitconfig

  • Properly configure your user information GitHub tracks your changes by using the information provided by your ~/.gitconfig. If you work on more than one machine and your ~/.gitconfig is not properly configured, you will probably end up with duplicated commits and disorganized history. Here are the lines you will have to modify according to your GitHub credentials.
[user]
	name = Guilherme M. Trein
	email = [email protected]
  • Properly configure your difftool and mergetool. The difftool and mergetool are the software Git will execute during diff or conflict resolution operations respectively.
[difftool "opendiff"]
	cmd = /usr/bin/opendiff \"$LOCAL\" \"$REMOTE\" -merge \"$MERGED\" | cat
[diff]
	tool = opendiff
[merge]
	tool = opendiff
  • Create aliases for the most common used commands. You will noticed that you will end up issuing the same Git command several times a day. Creating aliases to the most common used commands can save precious minutes every day.
[alias]
	st = status
	ci = commit
	br = branch
	co = checkout
	ds = diff --staged
	changes = log -n1 -p --format=fuller
	amend = commit --amend -C HEAD
	undo = clean -f -d
	undoci = reset HEAD~1
	unstage = reset HEAD --
	lg = log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
	ls = log --pretty=format:\"%C(yellow)%h %C(blue)%ad%C(red)%d %C(reset)%s%C(green) [%cn]\" --decorate --date=short
	lg-full = log --name-status --pretty=fuller

Cheat Sheet

Create

Operation Command
Clone an existing repository $ git clone ssh://[email protected]/repo.git
Create a new local repository $ git init

Local Changes

Operation Command
Changed files in your working directory $ git status
Changes to tracked files $ git diff
Add all current changes to the next commit $ git add .
Add some changes in to the next commit $ git add -p <file>
Commit all local changes in tracked files $ git commit -a
Commit previously staged changes $ git commit
Change the last commit (Don‘t amend published commits!) $ git commit --amend

Commit History

Operation Command
Show all commits, starting with newest $ git log
Show changes over time for a specific file $ git log -p <file>
Who changed what and when in $ git blame <file>

Branches and Tags

Operation Command
List all existing branches $ git branch
Switch HEAD branch $ git checkout <branch>
Create a new branch based on your current HEAD $ git branch <new-branch>
Create a new tracking branch based on a remote branch $ git checkout --track <remote/branch>
Delete a local branch $ git branch -d <branch>
Mark the current commit with a tag $ git tag <tag-name>

Update and Publish

Operation Command
List all currently configured remotes $ git remote -v
Show information about a remote $ git remote show <remote>
Add new remote repository, named $ git remote add <remote> <url>
Download all changes from , but don‘t integrate into HEAD $ git fetch <remote>
Download changes and directly merge/ integrate into HEAD $ git pull <remote> <branch>
Publish local changes on a remote $ git push <remote> <branch>
Delete a branch on the remote $ git branch -dr <remote/branch>
Publish your tags $ git push --tags

Merge and Rebase

Operation Command
Merge into your current HEAD $ git merge <branch>
Rebase your current HEAD onto (Don‘t rebase published commits!) $ git rebase <branch>
Abort a rebase $ git rebase --abort
Continue a rebase after resolving conflicts $ git rebase --continue
Use your configured merge tool to solve conflicts $ git mergetool
Use your editor to manually solve con- flicts and (after resolving) mark file as resolved $ git add <resolved-file> $ git rm <resolved-file>

Undo

Operation Command
Discard all local changes in your working directory $ git reset --hard HEAD
Discard local changes in a specific file $ git checkout HEAD <file>
Revert a commit (by producing a new commit with contrary changes) $ git revert <commit>
Reset your HEAD pointer to a previous commit and discard all changes since then $ git reset --hard <commit>
Reset your HEAD pointer to a previous commit and preserve all changes as unstaged changes $ git reset <commit>
Reset your HEAD pointer to a previous commit and preserve uncommitted local changes $ git reset --keep <commit>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment