Skip to content

Instantly share code, notes, and snippets.

@esmooov
Created May 31, 2012 15:54
Show Gist options
  • Star 16 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save esmooov/2844353 to your computer and use it in GitHub Desktop.
Save esmooov/2844353 to your computer and use it in GitHub Desktop.
The Secret Passions of Git Checkout

The Secret Passions of Git Checkout


The Hand of God

Master Hand

Git checkout can do almost anything ... or, at least, many things. It can switch branches. It can mix and match branches. It can resolve merge conflicts. It can give you a scratchpad to test things. It can even be used to interactively patch files. It's so powerful because it's so abstract. But much like numinous mystics, abstraction makes it confusing.

Basically git checkout does two things:

  • Git Check It Out, Man (Changes your point of view, phenomenological): If you don't give checkout a file and just a branch/commit, it will move the HEAD to that branch or commit. If you just pass it a branch/commit it will "move" you to that branch or commit. (More on this later).

  • Git Check This Book Out, Return this Copy (Changes your files, ontological) If you do pass it a file and a branch/commit it will immediately, and without mercy, set the index and working version of the file to the state it is in at that branch/commit. It basically plucks out a different version of the file and pastes it over whatever you have.

Rather than just go through everything it can do in boring order, each section will be a "I want to do X. Here's how to do it with checkout." I'll save the boring, obvious stuff for last.

I wanna take an old commit and experiment

No one writes code that's perfect. Pretty regularly I chase rabbits down holes that end in me "implementing this one paper is saw somewhere." Generally, that means it's time to cut bait. But what if your wild goose chase has put you ten commits out from sane code. Do you just reset back ten commits and hope your new direction works? Do you start a new branch? What's an intrepid programmer to do!?

Git checkout <commit sha>

Oh that seems reasonable. And the you realize that I have beheaded you. You are in detached HEAD state. That state that looks like you made an error. That state that you've found yourself in before, causing you to start frantically rattling off checkouts and resets.

Don't despair, it's a good thing.

See, generally your HEAD is the tip of your current branch, you most recent commit. But what happens if you git checkout that old commit? You are no longer at the tip of a branch. You are floating over an old piece of the branch and looking at it. To keep the tree metaphor going, you are checking out a knot on the bark. To wit, you are in detached head.

You can continue to edit, stage and commit. However, you are still not on a branch. You have created a phantom limb off a mid-point of the branch you were on. So, say you checked out three commits ago (git checkout HEAD~3)* Now you are in detached HEAD, floating over a piece of the branch you had been on. You are no longer on that branch You work away. Now what?

If you like your work: git checkout -b new_branch. Your series of commits in detached HEAD will become a branch off the commit you checked out. It's as if you went back in time and branched. You can keep editing on that branch without losing the changes you made chasing your rabbit. You probably wrote some good code too. You now have both. But how can you transfer files and commits from the old branch to the new ... (See next section 'I wanna take this file from that branch and put it into the one I'm on')

(SUPER USER POWER TIP #1: You can accomplish the above without detached heads! git checkout -b new_branch <commit sha> will do the exact same thing. You will move back to an old commit and cut a new branch from it. However, you lose the ability to, say, not create a new branch from the detached changes and just tag them ... or something.)

Or, you can simply git tag coolio -m "It worked!", checkout the branch you were on before you went detached and then git merge coolio. Detached states get thrown away unless they turned into branches or are tagged. By tagging them you can merge in some good changes without making a new branch just for the purpose. I've found this is a nice, no mess way to try experiments with older version of files without messing up history or creating a forest of scrap branches.

(SUPER USER POWER TIP #2: "This detachment is awesome" you say, doing your best Truffaut. Can I detach a different branch, to experiment on? Yes and no. See, if you try to checkout a branch you will do just that. Switch your branch. However, if you git checkout foo@{0} you are really saying 'Checkout the last, the 0th commit on this branch'. You will put in detached head mode and can experiment without repercussion just as before. No more having to create a branch you will just throw away after a few commits. No more messing up a good branch and having to search back in the log for the ambiguous message you left.)

I wanna take this file from that branch (or commit) and put it into the one I'm on

Maybe you really nailed some css changes on that branch but everything else was garbage. You are on a much better branch now. Tests are passing. But that diamond css in the code rough is languishing. You need him back.

git checkout <branch> <file>

It's that simple. It's also that destructive. As, Al Shaw once wrote 'checkout has a “destructive” side as well, which instantly changes content in your current working tree.'

Warning: You will immediately and irrevocably lose any changes you have made to that file. With great power comes great responsibility.

You can also do this with commits. git checkout <commit> <file>

I want to move some parts of some files from one branch (or commit) to another.

So you have your master branch and you have your develop branch. Suddenly, you realize that the function you fixed on develop, really should be fixed on master. Do you copy the lines from one to another. Ew. We have git. One does not simply ... copy.

git checkout develop -p

The -p flag throws you into interactive patch mode. There are tons of options. Really you only care about y (Yes apply this hunk), n (Do not apply this hunk), s (split this hunk into smaller hunks and go wild). What is a hunk? How does one apply them?

A hunk

An applied hunk

A hunk is just a small group of changes in a file. "Applying" simply means that it will stage those changes for commit over whatever you currently have in your file.

(SUPER USER POWER TIP #3: If you git checkout . -p you can selectively throw away uncommitted changes. For all those times when you really mess up just part of a file [or vim does ...]. Remember, the s command in interactive -p mode will split the hunk into smaller pieces.)

I want to fix a merge conflict by keeping either my version of the file or the remote version.

Usually merge conflicts can be solved by the following question: Is your version right or this their version right? Many times, you just fixed the same thing and either is fine. Or you just messed something up and the their version is right. Etc etc.

Well, git checkout is here to save the day.

When you get into a failed merge state, the index/stage splits into three. The first index is the abyss. The second index is your version of things. The third is the remote version of things. From here, you can either git checkout file --ours or git checkout file --theirs. This will accept either your file or their file into the stage, overriding the merge conflict in that file. git commit and that pesky merge conflict is dead.

I just want to switch branches

Yeah, git checkout does this too. But everyone already knew that.

git checkout <branch>

However, git is very cagey about what is does when you checkout a branch while you have changes in your working directory. According to the documentation, checkout "Updates files in the working tree to match the version in the index or the specified tree." But this isn't really true. If you have changes in your index or working tree and checkout a branch, you will keep those changes. In fact, git will show you a little [M] file when you check the branch out. This is why you can make and stage changes, realize you should be on another branch, check it out before committing, and be good as gold.

However, git checkout branch -f will ruthlessly destroy you changes and set your working tree to the state of the branch you checked out.

Cold.

Cold

* If you forget what tildes mean see my other piece on git.

@tiffehr
Copy link

tiffehr commented Jun 2, 2012

Lovely deployment of D'.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment