Git Field Guide
Git has become an indispensable tool of software development, regardless of which language or framework your team uses it. But in the other hand, it isn’t a tool that you can adopt, learn and be effective from day 1 - it takes time and guidance to master it, both in the command line the integrations and practices most teams have adopted.
I’ve put up together a guide on how I see and use git on my day to day work, that can be used as a starting both for both newcomers who are trying to grok or more experienced professionals who want to make a more effective use of it on they work.
- Git is more of a tool to document and communicate changes rather than storing them. As the number of people involved or time spent in a codebase grows, you will eventually find yourself reading more about what (and why) have change rather than reverting changes.
- Git is not trivial. All its power and usefulness can come with more complexity than your one might expect. Be humble enough to understand that you might not master it as fully as you think, and trust it to be more powerful than you currently expect it to be.
- Failures are usually recoverable. Whenever some git operation does not goes
how you would expect (from commiting to a wrong branch to rebases with absurds conflicts)
do not panic as not all is broken or lost, and commands like
rebasecan be extremely useful to recover and fix whatever might have happened to your git tree. Oh shit, git! is a tiny but useful resource on possible git screw ups and how to fix them.
- On commit messages: care about writing good commit messages and read your git history often. Chris Beams has probably the best post on how to write commit messages and I recommend reading it rather than trying to explain on this post.
- When working with feature branches, Always keep them up to date, with both
git fetchevery now and then, rebasing it early and often with its base branch (the
masterbranch on your remote repository on most cases), and fixing conflicts as soon as they happen. Fixing conflicts on larger rebases will be harder and merges will add noisy commits to your branch’s history.
git checkout feature/add-new-stuff # work work work... # download the refs that are on your remote repository, and rebase your feature # branch on top of whatever's is on your remote's master branch, so it will be # up to date. Hopefully no conflicts will need to be fixed. git fetch origin && \ git rebase origin/master # if you want to make your local `master` branch is up to date as well, do # the following: `git pull` will fetch and rebase the references from your # remote repository, and then rebase the feature branch against the local # master branch instead. git checkout master && \ git pull --rebase && \ git checkout - && \ git rebase master
- Use git commit –fixup
and interactive rebases
to rewrite the history on your feature branches to keep its commits in order and
organized. Intermediate changes are useful when rolling changes and trying fixes, but
once the dust has settled you can rework the history to represent the final changes
you want to introduce.
# So, you added a few files and some commits on your branch git commit -m 'Add README.md' # [master (root-commit) f1a4ab4] Add README.md # 1 file changed, 0 insertions(+), 0 deletions(-) # create mode 100644 README.md $ git commit -m 'Add CONTRIBUTING.md' # [master e8644e4] Add CONTRIBUTING.md # 1 file changed, 0 insertions(+), 0 deletions(-) # create mode 100644 CONTRIBUTING.md # Now you need to update `README.md` and want to "merge" the new commit with # these new changes to the original commit (f1a4ab4) using an interactive rebase git add README.md git commit --fixup f1a4ab4 # [master 13168c2] fixup! Add README.md # 1 file changed, 0 insertions(+), 0 deletions(-) # `--autosquash` will prepare the interactive rebase to squash 13168c2 on f1a4ab4 # for you. git rebase --interactive --autosquash HEAD~3
- Not all branches/Pull Requests need to be squashed into a single commit when doing interactive rebases. Squashing too many commits can make bisecting or navigating through the history harder than you might want to. You can practice this by avoiding shortcuts like the GitHub “squash merge” button and favoring rebases through your editor where you have more control over the operation and review your commits to decide how they should be squashed.
- Configure git to always
rebasebranches when doing
git config --global pull.rebase trueand never use fast forward when doing merges with
git config --global merge.ff false. These changes are vital to maintain sane tree histories when working with remote repositories.
- Configure your identity with
git config --global user.nameand
git config --global user.emailon every workstation you use. This helps to identify who’s the real author of a changes and is important for tools like GitHub to associate a commit with a particular person. I also configure my local repositories with different email given the context - commits to company related work is made using a business email while open source is commited using my personal email - I have an alias
git workthat sets the “work” identity to the current repository while my global config remains personal.
- Set up aliases to abstract common git operations, like:
- Undo the last commit (
git reset --soft HEAD^) and keep it changes to be reviewed or split among different commits.
- Add new changes to the latest commit while maintaining the same message (
git commit --amend --no-edit). Useful when applying small fixes like typos or styleschanges without having to do a complete
rebaseoperation - don’t forget that you will need to push it using
--forcewhen working with remote repositories.
- Run an “automatic” interactive rebase to apply your
--fixupmarked commits without going through your editor to describe what the rebase should do with each commit (
!GIT_SEQUENCE_EDITOR=touch git rebase --interactive --autosquash).
- Undo the last commit (
For more configuration settings and aliases that I use (and don’t use), you can check my gitconfig on GitHub.