Git Best Practices

Git is a very powerful version control system that supports many different workflows. But used incorrectly, Git will readily tangle up your code in a web of merge conflicts. Follow these guidelines to keep your Git workflow predictable and efficient.

Commit

Commit small changes, often.

The more time that passes between commits, the greater the likelihood that your changes will trigger merge conflicts with other people's work. Also, commiting often gives you plenty of recent rollback positions for when you make a mistake.

Work in small, testable and deployable increments. Write tests and code simultaneously.

Never use add -A or commit -a. Do not assume that all of your local changes are shippable. Instead use git add -p or a Git GUI like SourceTree or SmartGit. Review your local changes and stage and commit only what you need.

Keep you commit messages terse and concise. Most GUIs will truncate them anyway.

Push your commits to the central repository when you have made sufficient progress to justify peer review. But don't wait too long. Large check-ins are less likely than small ones to get reviewed properly.

Pull

Rather than integrating features into the masters, better to do the integration in reverse: first pull the latest commits from master into the feature branch, and sort out any merge conflicts in the feature branch before finally merging everything into master. This workflow helps to keep the master branches pristine.

To update a feature branch with the latest from master, use pull --rebase. Rebasing rewrites the history of the local working branch. It replays your recent local commits on top of the changes that you pull in. This preserves the order of the changes, keeping your work at the tip of the feature branch. Also, pulling without rebasing adds merge commits, which in this context don't serve any useful purpose in the source code's history.

Always rebase from master immediately before pushing your local commits to a shared central repository. The effect of rebasing is that the push will produce a fast-forward and a nice clean history in the shared repository. The project maintainers will be able to review your work and merge it into the project mainline faster.

By default, pulls are not rebased, but you can make rebasing the default behaviour by modifying Git's global configuration on your local machine:

git config --global pull.rebase true

Merge

Merge feature branches into master regularly. Integrate new code as soon as it is stable. If necessary, use toggle switches to deactivate imcomplete features.

The sooner that code changes are merged into master, the sooner other developers can rebase from the mainline, updating their working copies and building their work on top. Frequent merges into the mainline, followed by rebased pulls back to the distributed forks and clones, mitigates integration hell when multiple feature branches are integrated into master at once.

When a feature branch is merged into master, the merge adds all of the commits in the feature branch to the master branch. If there are conflicts between the two branches, these will be reconciled with a final merge commit. But when there is no divergent work to merge, Git simply "fast forwards" to the most recent commit from the two branches and does not record an explicit merge commit. But merge commits are useful in this context because they record when specific features were integrated into the project mainline. To force Git to record an explicit merge commit every time, use the "no fast forward" (--no-ff) flag with the merge command. This can be made the default behaviour by running the following command against the central repository:

git config --global merge.ff false