Example git forkflow: branch hopping
I wanted to show my mercurian friends around git, and what better way to do that than by real examples.
This morning I wanted to persist the changes I've been making to pelican-mockingbird theme for use in my own blog. Increased verbosity for illustration purposes.
$ git status
# Not currently on any branch.
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: static/css/pygments.css
# modified: static/css/screen.css
# modified: templates/article_stub.html
# modified: templates/base.html
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# templates/disqus.html
no changes added to commit (use "git add" and/or "git commit -a")
You may notice I'm "not on any branch" (also called detached head state). This is because I am working inside a submodule of pelican-themes
and the submodule is pinned to a particular commit.
First, I fork pelican-mockingbird
on Github and add my new forked repo as a remote.
$ git remote add mine git@github.com:unthingable/pelican-mockingbird.git
Since I'm not on a branch I need to get on one. I want it to be the master
from my new fork.
First, fetch the state of the new origin:
$ git fetch mine
From github.com:unthingable/pelican-mockingbird
* [new branch] master -> mine/master
It found one remote branch master
and fetched it under a local alias mine/master
. Now I attempt to checkout the remote branch. This is not the branch I'll be working in, but it is a quick way to bring the working dir into the most current state.
$ git checkout mine/master
error: Your local changes to the following files would be overwritten by checkout:
static/css/pygments.css
static/css/screen.css
templates/article_stub.html
templates/base.html
Please, commit your changes or stash them before you can switch branches.
Aborting
OK, apparently I was missing some recent commits and now the incoming changes are conflict with my local changes. Before I risk losing my work, it is time for salvation:
$ git stash
Saved working directory and index state WIP on (no branch): 2f4c1b6 update screenshot
HEAD is now at 2f4c1b6 update screenshot
Now I can get current:
$ git checkout mine/master
Previous HEAD position was 2f4c1b6... update screenshot
HEAD is now at fa46a80... Merge pull request #5 from kevmo314/master
So far so good. Let's bring my changes back in:
$ git stash pop
Auto-merging templates/base.html
Auto-merging templates/article_stub.html
CONFLICT (content): Merge conflict in templates/article_stub.html
Auto-merging static/css/screen.css
Auto-merging static/css/pygments.css
CONFLICT (content): Merge conflict in static/css/pygments.css
Even though I popped the stash, the stashed changes is still there. At this point (or any point in the future) I can decide to scrap what I'm doing, go back to the original commit I started working from (2f4c1b6
) and reapply my stashed changes conflict-free. This would fully restore the state I had in the beginning of the post.
I decide to keep the new changes and fix the conflicts. Now my changes are in a happy state and I am ready to make a new commit atop fa46a80
(the last commit on remote master
).
$ git status
# Not currently on any branch.
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: static/css/screen.css
# modified: templates/base.html
#
# Unmerged paths:
# (use "git reset HEAD <file>..." to unstage)
# (use "git add/rm <file>..." as appropriate to mark resolution)
#
# both modified: static/css/pygments.css
# both modified: templates/article_stub.html
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# templates/disqus.html
Let's tell git my conflicts are resolved, and also add the new template:
$ git add templates/ static/
(channeling Gallagher) Can it be that easy? Yes, it's that easy!
$ git status
# Not currently on any branch.
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: static/css/pygments.css
# modified: static/css/screen.css
# modified: templates/article_stub.html
# modified: templates/base.html
# new file: templates/disqus.html
Now we're ready to commit and push. But wait, we're still not on any branch. Let's take our current state and make a new branch out of it. I don't want to touch my master
just yet, just in case I bork something — after all, I have been doing mercurial for a few months now...
$ git branch
* (no branch)
master
$ git checkout -b new-master
M static/css/pygments.css
M static/css/screen.css
M templates/article_stub.html
M templates/base.html
A templates/disqus.html
Switched to a new branch 'new-master'
$ git commit -am 'fix.'
[new-master e542d31] fix.
5 files changed, 125 insertions(+), 82 deletions(-)
rewrite static/css/pygments.css (98%)
create mode 100644 templates/disqus.html
$ git log
commit e542d311cf1a6ed318c1807a118a7f4a5ce93f80
Author: unthingable <me@example.com>
Date: Tue Nov 5 10:08:43 2013 -0800
fix.
commit fa46a8024ceb821a8717ed36f7b9d2a6a06d2060
Merge: decd2d4 7ea3637
Author: william light <wrl@illest.net>
Date: Sun Oct 13 06:02:45 2013 -0700
Merge pull request #5 from kevmo314/master
use more standardized strftime formats
Looking good! Now I want to take new-master
and push to my github master
without renaming. Can it be that easy?
$ git push mine new-master master
Counting objects: 18, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (9/9), done.
Writing objects: 100% (10/10), 2.28 KiB, done.
Total 10 (delta 5), reused 0 (delta 0)
To git@github.com:unthingable/pelican-mockingbird.git
* [new branch] new-master -> new-master
Oops, it's been a while and I forgot the proper syntax. Now there is a branch called new-master
on github. Let's fix and clean up:
$ git push mine new-master:master
Total 0 (delta 0), reused 0 (delta 0)
To git@github.com:unthingable/pelican-mockingbird.git
fa46a80..e542d31 new-master -> master
$ git push mine :new-master
To git@github.com:unthingable/pelican-mockingbird.git
- [deleted] new-master
All done. I still have a local master
that I don't need anymore, so one more thing to tidy up:
$ git branch -av
master fa46a80 Merge pull request #5 from kevmo314/master
* new-master 51e3737 readme
remotes/mine/master 51e3737 readme
$ git branch -d master
Deleted branch master (was fa46a80).
$ git branch --move master
$ git branch -v
* master 51e3737 readme