Random learnings and other thoughts from an unashamed geek

My Favourite Git Commands I: Patch

| Comments

This is part 1 in my Favourite Git Commands series.

Last year I wrote a post about how I intended to switch over to and master the tools I’d always meant to but somehow never got around to. Among these was git.

I’ve now been using git exclusively for the last year (even when interfacing with SVN projects I do so using the awesome git-svn bridge) and I love it. It took me a while to form a decent workflow and break the old SVN habits, so over the next few days I thought I’d share with you some of my favourite git commands.

I’m going to assume that you’ve used git at least once or twice, and know what I mean by repo (where the git history is stored, i.e. the .git folder), HEAD (the top commit in the current branch), staging area (where the files for the next commit are stored, also known as the index or cache), and the working copy (the files that you’re modifying).

The --patch option

My friend Jasper helped a lot when I first started getting into git, and noted one of his favourite commands was git add -i - an interactive way of adding files to your staging area before commits. I used this a lot, but found that mostly I was using the patch function, and going through all the menus was unnecessarily slowing me down. Enter:

git add --patch

Or git add -p if you prefer. This command jumps straight to patch mode on all your tracked files, asking you for each hunk whether you want to add them to the staging area or not. I generally answer with one of the following, though there are other options that can be useful:

  • y - yes, add this hunk
  • n - no, skip over this hunk
  • s - split this hunk into smaller hunks
  • e - edit this hunk, then add it (I normally use this to remove debugging lines)
  • q - I’m all done adding stuff, go away.

This ensures that I can keep my debugging and other temporary mods out of git, only adding the files I want to appear in git history. But sometimes I accidentally add a line I didn’t mean to, so then I use:

git reset --patch

Or git reset -p if you prefer. This command is basically the opposite of git add --patch - it enables you to move specific hunks back out of staging and into your working copy. It accepts the same options as its sibling above.

Tip: removing one line from the middle of a hunk

You can use the edit command to open the hunk in your text editor, then change all the + at the beginning of lines for a space except on the lines you want to remove. Remember this is removing a hunk, so a + actually means “remove this line from the staging area”. (The spaces are needed for context.)

When I have the staging area how I want it, I commit it (see below). But then I’m left with all the lines I didn’t stage. Some of these I want to keep whilst I work on the next commit, others I’m done with (mostly console.log/NSLog statements, if I’m honest). So I want to selectively delete modified lines from my working copy without having to find them in my text editor. Enter:

git checkout --patch

Yes, or git checkout -p. This command is more dangerous as it throws away the work you select, with no history of it (since it was never committed). You select the hunks you want to throw away using the same options as its siblings above. You can also use it for surgery-like removal of lines at any time using the e option and the same tip as git reset --patch but be very careful!

git [add/etc] --patch [filenames]

All the above commands accept file or directory names as an argument, so you can just patch add/reset/checkout a small selection of files without worrying about modifications to other unrelated files. This is particularly powerful when paired with zsh’s globbing.