--- layout: post title: Tips for a disciplined git workflow tags: ["git", "philosophy"] --- Basic git usage involves typing a few stock commands to "[sync everyone up](https://xkcd.com/1597/)". Many people who are frustrated with git become so because they never progress beyond this surface-level understanding of how it works. However, mastering git is easily worth your time. How much of your day is spent using git? I would guess that there are many tools in your belt that you use half as often and have spent twice the time studying. [![](https://imgs.xkcd.com/comics/is_it_worth_the_time.png)](https://xkcd.com/1205/) If you'd like to learn more about git, I suggest starting with [Chapter 10][ch-10] of [Pro Git][pro-git] (it's free!), then reading chapters 2, 3, and 7. The rest is optional. In this article, we're going to discuss how you can apply the tools discussed in the book to a disciplined and productive git workflow. [ch-10]: https://git-scm.com/book/en/v2/Git-Internals-Plumbing-and-Porcelain [pro-git]:https://git-scm.com/book/en/v2 ### The basics: Writing good commit messages [![](https://imgs.xkcd.com/comics/git_commit.png)](https://xkcd.com/1296/) You may have heard this speech before, but bear with me. Generally, you should not use `git commit -m "Your message here"`. Start by configuring git to use your favorite editor: `git config --global core.editor vim`, then simply run `git commit` alone. Your editor will open and you can fill in the file with your commit message. The first line should be limited to 50 characters in length, and should complete this sentence: when applied, this commit will... "Fix text rendering in CJK languages". "Add support for protocol v3". "Refactor CRTC handling". Then, add a single empty line, and expand on this in the *extended commit description*, which should be hard-wrapped at 72 columns, and include details like rationale for the change, tradeoffs and limitations of the approach, etc. We use 72 characters because that's [the standard width of an email][rfc], and email is an important tool for git. The 50 character limit is used because the first line becomes the subject line of your email - and lots of text like "`[PATCH linux-usb v2 0/13]`" can get added to beginning. You might find wrapping your lines like this annoying and burdensome - but consider that when working with others, they may not be reading the commit log in the same context as you. I have a vertical monitor that I often read commit logs on, which is not going to cram as much text into one line as your 4K 16:9 display could. [rfc]: https://tools.ietf.org/html/rfc2822#section-2.1.1 ### Each commit should be a self-contained change Every commit should only contain one change - avoid sneaking in little unrelated changes into the same commit[^1]. Additionally, avoid breaking one change into several commits, unless you can refactor the idea into discrete steps - each of which represents a complete change in its own right. If you have several changes in your working tree and only need to commit some of them, try `git add -i` or `git add -p`. Additionally, every commit should compile and run all tests successfully, and should avoid having any known bugs which will be fixed up in a future commit. [^1]: I could stand to take my own advice more often in this respect. If this is true of your repository, then you can check out any commit and expect the code to work correctly. This also becomes useful later, for example when cherry-picking commits into a release branch. Using this approach also allows [git-bisect](https://git-scm.com/docs/git-bisect) to become more useful[^2], because if you can expect the code to compile and complete tests successfully for every commit, you can pass `git-bisect` a script which programmatically tests a tree for the presence of a bug and avoid false positives. These self-contained commits with good commit messages can also make it really easy to prepare release notes with [git-shortlog][shortlog], [like Linus does with Linux releases][linux-announcement]. [^2]: In a nutshell, git bisect is a tool which does a binary search between two commits in your history, checking out the commits in between one at a time to allow you to test for the presence of a bug. In this manner you can narrow down the commit which introduced a problem. [shortlog]: https://git-scm.com/docs/git-shortlog [linux-announcement]: https://lkml.org/lkml/2019/1/6/178 ### Get it right on the first try We now come to one of the most important features of git which distinguishes it from its predecessors: history editing. All version control systems come with a time machine of some sort, but before git they were mostly read-only. However, git's time machine is different: you can change the past. In fact, you're encouraged to! But a word of warning: only change a future which has yet to be merged into a stable public branch. The advice in this article - bug-free, self-contained commits with a good commit message - is hard to get right on the first try. Editing your history, however, is easy and part of an effective git workflow. Familiarize yourself with [git-rebase](https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History) and use it liberally. You can use rebase to reorder, combine, delete, edit, and split commits. One workflow I find myself commonly using is to make some changes to a file, commit a "fixup" commit (`git commit -m fixup`), then use `git rebase -i` to squash it into an earlier commit. ### Other miscellaneous tips - Read the man pages! Pick a random git man page and read it now. Also, if you haven't read the top-level git man page (simply `man git`), do so. - At the bottom of each man page for a high-level git command is usually a list of low-level git commands that the high-level command relies on. If you want to learn more about how a high-level git command works, try reading these man pages, too. - Learn how to specify the commit you want with [rev selection][rev-select] - Branches are useful, but you should learn how to work without them as well to have a nice set of tools in your belt. Use tools like `git pull --rebase`, `git send-email -1 HEAD~2`, and `git push origin HEAD~2:master`. [rev-select]: https://git-scm.com/book/en/v2/Git-Tools-Revision-Selection