Say you’re working along on a branch (let’s say master), and suddenly you realize that this task is harder than you thought. You wish you had started a branch for this feature, back before you began it. Now you need to work on a bug instead, so you’ve got to put these changes away — if only you had branched!
This is extremely easy to fix. First, I’ll tell you what to type. Then, I’ll tell you how it works, so that you can adjust the solutions to your own needs.
What to Type:
1) Commit your changes. It’s ok if they’re not finished; you’ll come back to these later.
2) git branch feature_branch_name
3) git reset –hard origin/master
Bam, your changes are saved off on feature_branch_name, your current working directory matches what was in the origin repo last time you fetched (or pulled) from it, and you are ready to start work on the bug.
1) After your changes are committed, you’re still on the master branch. git status looks like:
# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
nothing to commit (working directory clean)
and in gitx (if you’re on a Mac, please download gitx):
Notice that your master branch is ahead of origin/master.
2) git branch does exactly one thing: it creates a new label. It goes right where you are, on your most recent commit.
This operation does not check out the new branch; master is still your current branch. Git status has not changed, and gitx has that new label on it:
3) Now the real magic. “git reset –hard origin/master” does three things. Primarily, it moves the “master” label (your current branch) to wherever “origin/master” is. That’s what reset does: it moves labels around.
The output is:
HEAD is now at 0562dac Some commit that's already been pushed to origin
This tells you the second thing it did: it moved the HEAD label, which marks where you’re currently working. You’re now working on master, which is now before you did your feature-related changes.
Git status looks the same. Gitx says:
That new_feature branch is sticking out up there, saving your work. But you’re back at the “master” label. This is the third thing that your git reset command did, thanks to “–hard”: it replaced everything in your working directory with what the repository looks like in origin’s master branch.
“–hard” tells git to rejigger all your files.
Why is “–hard” not dangerous? well, it can be. But we’re safe because
1) we committed changes
2) we gave that commit a label.
Git will always and forever remember the exact state of your code at any commit that has a label, or is in the history of any commit with a label. If a commit is reachable in the history from any reference (branch, HEAD, tag), then it is safe.
The critical point to this operation — the reason this is really easy for me now, but was super-hard before I understood the commit graph — is that we don’t have to move any code. We don’t have to move any commits to a branch: instead we move the labels around. Branches are simply pointers to a commit. We can move them around all day. In this example, we added a new pointer for the branch, moved the “master” pointer back to where it was before our changes, and replaced our working directory files with the older stuff.
This contrasts sharply with the overhead of branching in Subversion. Yay Git!