Date Tags tutorial / git (6 min read)

When I career switched into tech, the two big management shocks were daily standup and git. And I mean that in a good way, a very good way. I love git very much, and am starting to get opinionated about it. It's not just version control! It's history, and narrative, and structure. It's collaboration! It's TEAM. It's a beautiful tree spawning many branches. Or a lot of hair with a lot of braids that need to get re-braided. I like to keep my repos like little bonsais or Princess Leia buns of good practice. git is not to be neglected! And often is!

git commit aaagh xkcd/1296

Tutorial assumptions

This tutorial assumes that you have used git, at least a little bit. That is, you know that it's a version control system, you've made some commits, pushed and pulled, and have an account on GitHub. If you're very new to git, then I highly recommend Udacity's How to Use Git and GitHub.

Some philosophy: Git as history (AKA the power of squash and rebase)

I think the main power of git is that it tells a story of your project. Sometimes I get lazy and just rely on git to, well, not lose all my stuff. And this is, yeah, its big sell. But, if you write good (long!) commit messages and commit at the Right Time, and if you wield the majestic power of git squash and git rebase with aplomb, I think you can create quite a beautiful, readable little story, something to be passed down from collaborator to collaborator. This is useful for welcoming new team members, or for dealing with the passage of time (why did you write it that way, all those months ago?).

Here are some stuff I find helpful in git:

1. Visualize your history

There are several ways to visualize git histories:

  • Tower ($80/license) - A very pretty GUI, integrates nicely with GitHub.
  • gitk - A built-in command-line "graphical repository browser" that ships with git. Uglier GUI than Tower.
  • A bash alias:
alias glg='git log --graph --full-history --all --color --pretty=format:"%x1b[31m%h%x09%x1b[32m%d%x1b[0m%x20%s"'

I don't know who wrote the above. Someone at my old gig? It was passed down to me from the ancients. If this was you, please let me know. I feel super guilty sharing some mystery code like this. Please let me assuage my guilt and just say you wrote this.

Anyway, the point is: find some way to visually navigate through the commit history and the various branches. Git is surprisingly/eerily smart about tracking when things are happening in relation to each other. Seeing all that is powerful and informative. I glg repeatedly throughout the day. I just checked now --

history | grep glg | wc -l
>> 34

-- 34 times! That's 13 times an hour, or once every 4.6 minutes.

Okay, maybe I check it too much.

2. Pick a comfy editor

Git uses your global default $EDITOR as its own. I think that's Vim on OSX? Or is it nano? Anyway, I like Sublime as my lightweight "small jobs" editor. If you want to change your default text editor for git to Sublime (but keep whatever $EDITOR for other stuff), then:

git config --global core.editor "subl -n -w"

You can also do Atom or TextMate, or anything that launches from the command line.

Having a comfy/friendly text editor is important so I can write long, involved commit messages (further reading: How to Write a Git Commit Message).

3. Rewrite history (responsibly): git squash, git rebase

When we're working on our local machines, we can create as many branches as we like, however we like. We can commit whenever and whatever. But this can get messy when we start sharing that history around.

I fucked up Git so bad it turned into Guitar Hero pic.twitter.com/vUKZJAQKWg -- Huenry Hueffman (@HenryHoffman) February 1, 2016

One way to keep things clean is to combine many commits into one big commit (git squash) and to rewrite our git history such that our suggested changes are always on top of the newest version of code (git rebase).

Git squashing and rebasing is useful but potentially destructive. It rewrites git history. I think a good general rule of thumb is: rewrite your own history, not other people's. In other words, right before submitting a pull request, you can combine all your branch commits into one - "This is my final version!" - and you rebase that one commit on top of the latest develop branch such that it looks like you used the freshest codebase to start your branch from.

But you never want to squash or rebase master or develop themselves.

git rebase is distinct from git merge since it doesn't merge in other people's commits (thus sullying your story), but rather just pretends you started work on whatever the latest code is.

The end goal is this: theory "beautifull"

Walkthrough

Let's assume you've been working on a branch for a few days. You have some code you want to PR into blog/master.

  1. Update your local copy of the master branch with any remote commits.
git fetch
git pull
  1. If there are any updates, rebase your work on top of those changes in master.
git checkout [my branch]
git rebase master

This might be a pain point: merge conflicts. Untangle each of them in turn (I like grepping for <<< in the conflict files), git add . them and then git rebase --continue.

Now it should look like this: git history Source: https://www.atlassian.com/blog/sourcetree/interactive-rebase-sourcetree

  1. Squash your commits (which you can count down from HEAD to where your work begins, or use a specific commit's SHA) into one final commit. For example, say you wanted to squash the last five commits:
# Rebase by counting down from the HEAD...
git rebase -i HEAD~5

# ...or by selecting a specific, older commit's SHA
git rebase -i 9b20280

The interactive -i flag will launch git's default editor. There, you select which commits to keep as separate commits (pick, or p) and which to combine (squash, s).

Pro tip: If you forget the -i flag, git will not squash anything, and will just exit out back to your shell.

pick your branch's first commit, and s (squash) all the others.

squash

Next, git will prompt you to adjust the squashed commit message.

commit message

Check your git history, and voila! You should see that all those commits collapsed into one!

4. Keep polishing your git skills

In my day to day git, I've always tried to note down the random stuff I've learned about how to use it. Can I search for a specific piece of code? Even if it's been deleted? Can I search for a deleted file? Stuff like that. The mystical .git file keeps track of so much, and it's great to be able to essentially trawl back through all these different versions of the past.

Here are some useful recent TILs:

  • To search all commit history for when a string changed:
git log -Sstring
  • To compare the differences between specific commits:
git diff COMMIT_SHA^ COMMIT_SHA
  • To remove stuff from the staging area:
git reset

(I know, I know. That one should be basic, but I almost never used it, yolooooo.)