git
github forking
2011-09-09 16:25:23 -0700 git techI don't think GitHub's default workflow for forking a repo is ideal for my needs. Most of the time when I fork a repo, my aim is to fix something or add a small new feature which I will eventually submit upstream as a pull request. While I am hacking away or living with my modifications for a while before I submit them, I want to continue to be able to easily pull in upstream changes as well. So whenever I fork a repo, I want the following:
- The
master
branch will track the pristine upstream master. Whenever I have no changes of my own pending, I can just switch my work tree to usemaster
to keep up with the latest changes with a simplegit pull
. - A volatile
hack
branch where I work on my changes. This is volatile since I'll regularly be rebasing onto the head ofmaster
. - A volatile
pullme
branch where I will put changes that are ready for a pull request submission. I may want to continue to hack away inhack
while I'm waiting for the resolution of my pull request, so this is analogous to a release branch. But this is volatile since, for the sake of simplicity, I'll be resetting it whenever I'm ready to submit a new pull request.
(By volatile, I mean that I can feel free to rewrite the history on these branches without repercussions.)
So here's how I go about forking a GitHub repo:
git clone <upstream_repo> # e.g. http://github.com/mxcl/homebrew.git
cd <upstream_repo_name> # e.g. homebrew
git remote add fork <forked_repo> # e.g. git@github.com:SethMilliken/homebrew.git
git config branch.hack.remote fork
git config branch.hack.merge refs/heads/hack
git branch --no-track hack origin/master
git push fork hack:refs/heads/hack
git checkout hack
At this point here's how things look branches-wise:
$ git branch -r
* hack
master
remotes/fork/hack
remotes/fork/master
remotes/origin/HEAD -> origin/master
remotes/origin/master
remotes/fork/master
is what the GitHub website uses for it's default branch. I like to clean this up to remove any chance of ambiguity about the name master
. To me, master
always means the authoritative branch from the canonical repository, never a branch in my fork. Here is how I tidy this up:
- Click the
Admin
button on the forked repo's GitHub page. - Change the
Default Branch
to "hack". - Delete the forked repo's
master
withgit push fork :master
.
If there were a way to name the default branch when you first fork, or to subsequently rename it via a commandline git command, that would simplify things a bit.
To rebase my changes onto incoming upstream, which I do periodically as I'm hacking, and again immediately before submitting a pull request:
git pull --rebase origin master
When I'm finally ready to submit a pull request, here is what I do:
git branch -f pullme hack
git push fork pullme:refs/heads/pullme
This essentially replaces the pullme
branch with the current state of the hack
branch.
And after my pull request is accepted, I switch back to master
.
git checkout master
hg stash
2011-02-28 21:34:32 -0800 git hg techI have become accustomed in Git to using git stash
to temporarily set aside in-progress changes in my working directory before pulling from upstream. Whenever I use Mercurial now, I find myself wanting analogous functionality. The Googles don't seem to turn up any decent techniques for this, so I have repeatedly reinvented a series incantations to abuse Mercurial's mq
extension to provide it. Yet I always manage to forget whatever trick I used by the time I want to do it again. I finally decided to fix it for good, giving myself hg stash
and hg pop
, allowing a workflow in a dirty working copy that goes something like:
hg stash
hg fetch
hg pop
Here are the relevant .hgrc
lines:
[alias]
stash = !hg qinit &>2 /dev/null; hg qqueue --create stash-temp && hg qnew stash && hg qfinish tip && hg strip tip && hg qqueue patches && hg qqueue --purge stash-temp
pop = !hg -R .hg/strip-backup/`ls -rt .hg/strip-backup/ | tail -1` diff -r tip | patch -p 1 -R
Breaking it down:
# hg stash
hg qinit &>2 /dev/null # initialize mqueue in this directory
# ignore error output since this has likely already been done
# (this could be potentially be problematic if there is some other problem)
hg qqueue --create stash-temp # create a new patch queue so we don't mess with any existing ones
hg qnew stash # create the 'stash' patch with the outstanding modifications
hg qfinish tip # apply the patch
hg strip tip # now strip it (for the side-effect of the backup bundle)
hg qqueue patches # switch back to original patch queue
hg qqueue --purge stash-temp # delete the temporary patch queue
# hg pop
hg -R .hg/strip-backup/`ls -rt .hg/strip-backup/ | tail -1` # use the strip bundle as the repository
# finding it with some shell tricks
# (likely a better, more portable way to do this)
diff -r tip # output it as a diff
patch -p 1 -R # apply that output as a patch
# and reverse patch since we spun things around
# with the bundle
Your working copy's .hg/strip-backup/
directory could start to fill up if you use this a lot, so you may want to clean it out occasionally. I decided to not have hg pop
automatically cleanup the bundle just in case something goes wrong.
And why not just hg diff > stash.diff && hg revert --all
to stash and patch -p 1 < stash.diff
to pop? That doesn't really handle binaries, and somehow it feels a little too dangerous to me. But, yeah, for the majority of cases that would probably be sufficient. So I'll leave that here, too:
[alias]
stash = !hg diff > stash.diff && hg revert --all
pop = !patch -p 1