giant robots smashing into other giant robots

Written by thoughtbot

gabebw

Clone Me Maybe

Let’s say you’re on the Github page for rails/rails. Copying the URL from the  top of the page, pasting it into the terminal takes too dang long! Instead, put this in your ~/.gitconfig:

# ~/.gitconfig
[url "git://github.com/"]
  # Read-only
  insteadOf = gh:

In the terminal:

$ git clone gh:rails/rails

Read/write github repos

What if I want to clone something to which I have push (write) access, like thoughtbot/high_voltage? Here you go:

# ~/.gitconfig
[url "git@github.com:"]
  # With write access
  insteadOf = wgh:
$ git clone wgh:thoughtbot/high_voltage

Easily add a Heroku remote

Now then, what if you need to add a Heroku remote for the sushi app? You could go to Heroku, log in, click “My Apps”, click “Sushi”, and find the remote URL. Or, you can do this:

# ~/.gitconfig
[url "git@heroku.com:"]
  insteadOf = heroku:
$ git remote add heroku:sushi.git

Your turn

Before I learned this tip, I missed it real bad. For more, check out my gitconfig. What are your little Git tips?

gabebw

git checkout and see

If you use git, you’re running git checkout $BRANCH all the time. Everyone likes a fast checkout at the grocery store. How about making your git checkouts fast, too? Since I do it so often, I want it to go as fast as possible, and I bet you do too.

Are you … hot blooded?!

git checkout - (that’s git checkout followed by a dash) is the bee’s knees. It switches back to the branch you were on previously. If you run it again, you’re toggled back to the first branch. A useful comparison is cd -, which as you may know takes you back to your most recent directory. Here’s an example:

$ git branch
  alternate
* master
$ git checkout alternate
$ git branch
* alternate
  master
$ git checkout -
# Now we're back to master! 
$ git branch
  alternate
* master 

What are your favorite git tips and tricks?

dancroak

Trail Map

> How do I learn Ruby on Rails? Vim? Test-Driven Development?

Someone asks us these questions weekly. We think we finally have good answers.

Extracting answers from apprentice.io

apprentice.io is a program designed around 1-to-1 mentor-to-apprentice relationships with a heavy emphasis on pair programming.

However, each apprentice additionally has extra time each week to study topics of their choice. They set goals with their mentors and are held accountable to reaching them by publicizing the goals in an internal wiki.

Example goals include:

  • Read chapters 14 and 17 of The RSpec Book.
  • Review and merge a pull request on an open source project.
  • Write blog post about anonymizing data.

One curriculum does not fit all

We’ve been calling each apprentice’s wiki page their “trail map”.

To us, the “trail map” metaphor relates to hikers, bikers, and skiiers:

  • start in different places
  • want to go to different places
  • often change direction mid-journey

Likewise, apprentices (and anyone learning a topic):

  • have different past experiences
  • have different learning styles
  • change their goals mid-process

Trail map

Announcement!

With 12 apprentices in the apprentice.io program, we’ve noticed common patterns in each apprentice’s trail map.

So, we’ve consolidated trails into a default trail map and we’re pleased to now announce its release under a Creative Commons Atribution license.

You’re free to use the trail map however you’d like, even commercial training.

Initial trails

The trails exist as a single git repository on Github named Trail Map:

We hope learners everywhere will fork these trails for their own learning purposes and submit improvements via pull requests.

Each trail has three sections:

  • Critical learning
  • Validation
  • Ongoing reference

Critical learning

This section lists things like books or blog posts to read, screencasts to watch, code to read or write, and koans or tutorials to complete.

In each topic, we aren’t aiming for greatest depth, but rather the most efficient way for the learner to become productive.

For example: we suggest chapters, rather than entire books, to read.

Validation

This section lists simple tasks the learner should be able to perform during routine development. We’ve never liked quizzes or certifications, but some hueristic is useful for assessment. We think self-assessment is a simple, fast, and low-stress approach.

For example: we say you know everyday git when you can (among other things), “stage a file, create a commit, and push to a remote branch.”

Ongoing reference

This section lists things like man pages and API documentation which we’ll always reference regardless of experience. Many things are not worth memorizing.

For example, we suggest that a developer refers to man git-rebase during a project.

What’s next

This is a work in progress. We plan to add and edit trails as new resources are released or people tell us better ways they’re learning a topic.

We’d love to get your feedback in the form of Github Issues.

Written by .

dancroak

Remote Branch

Does your pair programmer maintain order, keeping the mean streets (ie. remote branches) clean? Let’s ride along with a remote branch cop to see how they work.

Remote Branch poster

fetch

Once inside the squad car, the cop runs git fetch origin:

remote: Counting objects: 45, done.
remote: Compressing objects: 100% (27/27), done.
remote: Total 28 (delta 23), reused 0 (delta 0)
Unpacking objects: 100% (28/28), done.
   0417137..5eb7f00  2933-upgrade-factory-girl -> origin/2933-upgrade-factory-girl
 * [new branch]      3159-fix-mobile-web -> origin/3159-fix-mobile-web

The manual tells us what happened:

> The above command copies all branches from the remote refs/heads/ namespace and stores them to the local refs/remotes/origin/ namespace.

This is useful for seeing what’s new in the remote named origin.

rebase

If we’re currently working in a local feature branch, we might look for something like this in the output of git fetch origin:

84d3635..19780cc master -> origin/master

If a line like that is in the output, there’s something new in origin/master that we might want to investigate and git rebase origin/master into our feature branch.

tracking

However, the output in this case tells us that two remote branches have changed:

0417137..5eb7f00  2933-upgrade-factory-girl -> origin/2933-upgrade-factory-girl
* [new branch]    3159-fix-mobile-web -> origin/3159-fix-mobile-web

The 2933-upgrade-factory-girl branch is one we had tracked and was since updated. The 3159-mobile-web-fixes branch is a new branch.

git branch -r

We view our existing known remote branches with git branch -r:

origin/2933-upgrade-factory-girl
origin/3114-add-validations-for-subscription-token
origin/3159-fix-mobile-web
origin/HEAD -> origin/master
origin/master
origin/production
origin/staging

The git branch -r command works without an internet connection. It has all the data stored in git objects from the earlier git fetch origin.

remote tracking branches

We can see those objects with ls .git/refs/remotes/origin:

2933-upgrade-factory-girl
3114-add-validations-for-subscription-token
3159-fix-mobile-web
HEAD
master
production
staging

These are the “remote branches”, or more formally named “remote-tracking branches” in the git man pages, but who has time for that mouthful when serving and protecting the general populace?

The more formal name is clearer because the remote branches are not the actual remote’s branches. They are like pointers or bookmarks to origin’s branches.

naming convention

The naming convention of ticket-number-verb-noun is nice for quickly finding things and tab completing. For example: git branch -r | ack 2933 outputs:

origin/2933-upgrade-factory-girl

For more on ack, see betterthangrep.com.

When a teammate asks us to review their code on “ticket 2933”, we could:

git fetch
git diff origin/master..`git branch -r | ack 2933`

However, that’s only the diff without syntax highlighting. Github is better for this.

We can do better by using the number to check out a new tracking branch:

git checkout `git branch -r | ack 2933 | sed 's/.*\///'`
Switched to branch '2933-upgrade-factory-girl'
Your branch is behind 'origin/2933-upgrade-factory-girl' by 1 commit, and can be fast-forwarded

We’re too busy fighting crime to type that again. So, we abstract it to a script:

#!/bin/sh

code-review()
{
  branch=`git branch -r | ack $1 | sed 's/.*\///'`
  git checkout $branch
}

Now, we can code-review 2933 or even code-review 3159, which creates a new branch and immediately tracks origin/3159-fix-mobile-web.

local machine code reviews

Github pull requests for feature branch code reviews have benefits:

  • People can review code at the same time (parallel processing).
  • People can comment at the line level (good for discussion).

However, code reviews on our local machines have benefits, too:

  • Run tests after checkout so they run while you review (parallel processing).
  • Read surrounding context of the diff in your editor (faster understanding).
  • Make small changes yourself, without relaying to the original author things like “formatting”, “spelling”, “parentheses” in comments (faster fixes).
  • Use the app, perhaps in a web browser. (smoke test it works as intended).

squash

While helping our teammates is part of the job, we have our own beat (our own feature branches) to finish and send for a code review.

We’ve been working on a feature that adds the Chosen plugin to an admin screen, which required about an hour of overriding styles with !important.

The code now works, passes tests, and looks great in a browser. We’re almost ready for a code review.

We’ve been making many small commits in our feature branch as we wrote the feature but to be nice guys toward the reviewer, we squash the commits down to one good commit message:

git checkout 4000-use-chosen-in-admin-section
git rebase -i origin/master

The -i flag is an interactive rebase, and opens ourEDITOR with:

pick 28e91bb [#4000] Add Chosen as Javascript asset
pick 2043850 [#4000] Link to Chosen stylesheet in admin layout
pick fd38b60 [#4000] Add Chosen to select field
pick 34c2e5c [#4000] Fix Chosen formatting

# Rebase 19780cc..34c2e5c onto 19780cc
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.

The comment tells us what to do but we’re no rookies: been doing this for years!

Fingers like lightning, we squash it down:

pick 28e91bb [#4000] Add Chosen as Javascript asset
squash 2043850 [#4000] Link to Chosen stylesheet in admin layout
squash fd38b60 [#4000] Add Chosen to select field

Since we didn’t actually change anything, we don’t feel the need to run the tests again and just force push to origin:

git push -f

We ask a teammate for a code review. They oblige.

A few minutes later, they surprise us with a question:

> Did you forget to commit some files?

Confused, we check the commit.

To our horror, we realize we accidentally dd‘ed in vim the time-consuming commit that we don’t think we can easily re-write.

To make matters even worse, with the interactive rebase, we re-wrote the git history! When we force pushed, we overwrote the clone on origin!

Our families start screaming: “We’re burning alive! I can’t feel my legs!” Here comes the meatwagon: “Whee-ooh! Whee-ooh!” The medic gets out and says Oh. My. God. New guy’s in the corner puking his guts out: “Bleah! Bleah!”

reflog

Just then, your grizzled old partner calmly types git reflog into the shell:

df83012 HEAD@{0}: checkout: moving from 4000-use-chosen-in-admin-section to df83012
3bc02a4 HEAD@{1}: commit (amend): [#4000] Use Chosen in admin section
9dd0667 HEAD@{2}: rebase -i (squash): [#4000] Add Chosen to select field
9dd0667 HEAD@{3}: rebase -i (squash): [#4000] Link to Chosen stylesheet in admin layout
7d14c9d HEAD@{4}: rebase -i (pick): [#4000] Add Chosen as Javascript asset
7a0ed21 HEAD@{5}: rebase -i (squash): updating HEAD
19780cc HEAD@{6}: checkout: moving from 2933-upgrade-factory-girl to 19780cc47583778009f7a2c4b52dbb50bda1d78f
b6fd856 HEAD@{7}: commit: [#4000] Fix Chosen formatting

He copies the SHA1 hash and types git merge b6fd856, then git log:

9eaa992 [2 seconds ago] Merge commit 'b6fd856' into 4000-use-chosen-in-admin section

Whoa. That was close.

We make a mental note to run tests after future rebases and before force pushes, for reflog is an option of last resort, like a union lawyer.

prune

Before we end our shift, we git remote prune origin:

- [deleted] 3159-fix-mobile-web

The manual explains:

> Deletes all stale remote-tracking branches under origin. These stale branches have already been removed from the remote repository referenced by origin, but are still locally available in remotes/origin.

So, it looks like 3159 was merged in or deleted on origin by another teammate.

Works for us. Let’s go for a taste.

Fake movie poster by Kyle Fiedler. For more posters, see Team Rotations.

Written by .

dancroak

Global .gitignore

Set a .gitignore file to apply across all projects on your local machine with:

git config --global core.excludesfile ~/.gitignore

The only ignored pattern I have right now is:

*.swp

That ignores temporary files created by vim.

I used to ignore those files in each project I worked on but then I recognized my presumptuousness: not every teammate on every project is also using vim.

For them, that line is unnecessary.

We might say, “Who cares? It’s only one line.” but I appreciate it when my teammates are similarly disciplined so I ought to apply The Golden Rule.

> “Programming at its best is an act of empathy.” - Kent Beck

What else do you have in your global ~/.gitignore?

Written by .