Dr Nic

Using Git within a project (forking around)

Death Star - as seen on TV

In Star Wars, when Grand Moff Tarkin orders the destruction of the planet Alderaan, you must question his motivations. Historically, I think too much emphasis has been placed on “he’s an Evil Warlord doing the bidding of Darth Vader and the Emperor”, and “the Rebel scum must be tortured and punished.” But I’m not so sure this is the whole picture.

As developers, we know how exciting it is for a project/website to “go live”. You’ll get some praise from users, some bugs reports, but mostly, you will get the thrill of having finished something and seen it in action.

I’ve got to believe that when Tarkin gave the orders to “open up the taps on this baby,” there was a very different emotion at work amongst all his outwards “Give in to the Empire, Princess Leia” soapboxing. After flying around the emptiness of space in his brand new Death Star, never having actually used its main weapon, he just wanted to see if the thing actually worked.

If George Lucas hadn’t cut away from the cockpit of the Death Star to show us the planet Alderaan being obliterated, I guarantee you that Tarkin was waving his arms in the air, yelling “Oh that’s what I’m talkin’ ’bout,” with his lackies were all high-fiving each other in the background.

This brings me to Git.

Since moving the development of the Rails TextMate bundle to a git repository, several people have forked the repository.

When I pulled in the first “patch” from one of the forked repositories, I finally felt I’d “opened up the taps” on git. It was a sweet feeling.

Whilst Git was created by Linus for managing the Linux project, I get the feeling that Git’s raison d’être was to manage TextMate bundles. Weird since Linus probably doesn’t use TextMate.

A TextMate bundle is a combination of common/default snippets and personal snippets. Those personal snippets may even be useful for other people. Other people way want them. They may even join the set of default snippets.

Git’s system of forked repositories makes all this a reality. In a different, subversion-tainted reality, this scenario would mean that your local subversion checkout of the bundle becomes a mess of modified files and patch requests. With Git, you can stop caring about the central SCM. You make your own local changes, you push them to your own remote fork of the repository, and if they get absorbed into the common/default/central bundle so be it.

As I’m to understand it, this workflow is to Git as destroying planets is to the Death Star. Both Git and the Death Star are technologically impressive and to associate with them is to be seen as “cool”, but they can both do so much more.

Forking and Cloning

I’m not sure if I’m using official vocabulary, but I think of “forking” and “cloning” as different and I’ll use these terms until I’m educated otherwise.

Cloning is to make a copy of a remote repository into your local development environment. Your work machine. With the exception of IM, bittorrent and your shared iTunes library, that FTP server you don’t know is running, etc, no one’s got access to this machine or your clone.

You can make changes to your clone. For the TM bundles you could create a new snippet etc. You can then add and commit those changes into your local Git clone. They are committed now like a normal subversion repository commit. Except only you have them.

You could share the changes with a patch. You create the patch, then email it or upload it to wherever patches are uploaded to for your project.

Alternatively, you can push your changes to remote repositories (see “fork” below). If you have commit access to the original repository you cloned, you’d probably push your commits to this repository. If you don’t have commit rights, then you can fork the repository and push your commits there. “Freeeeeedooooom!!!” cried William Wallace whilst simultaneously having his Scottish testicles emasculated.

Forking is to clone an entire remote repository into another remote repository. Now you have two repositories based around the same code base. At that one point in time, they have the same set of files and the same history of changes.

As suggested above, you might do this because you don’t have commit rights to another fork, or because you are literally doing just as the name suggests: forking the project and going your own way. Either a moderate or radically different direction.

This is exactly what is wonderful about Git with TextMate bundles – there is no “one true path”.

If you wanted to fork the Rails TM bundle that I’ve hosted on github into another repository hosted on github then you click the “fork” button. [Email me for for the secret "I want to help make TM Rails Bundle awesome" github password if you need it to create an account]

drnic's ruby-on-rails-tmbundle at two_point_ooh 2014 GitHub

Alternately you could fork and host your repository on any public git server. Dean Strelau forked it onto Gitorious. You could also fork it onto gitorious.org, you’ll just need to give it a different name; e.g. prefix/suffix your name.

“In the end, there can be only one”

No. No one needs to have their head cut off, Highlander-style, with Git. I mean, unlike Subversion, Git doesn’t force any organisational behaviour. All commits don’t have to make their way back into one “root” repository.

But, once you have committed changes to your fork you might like to let other forkers know about them; especially if there is one repository that is commonly known as the “root” repository. For the TM Rails bundle it would be my repository since I’ll then aggregate changes and push them down to the official Subversion repository and from there they get pushed out with future TextMate releases.

For the Rubinius project, you’d probably want all your commits investigated and pulled into Evan Pheonix’s personal git clone. He’s not good looking enough to just be a poster boy so I assume he knows how to build and distribute the project for download.

I’m not sure what the organisation hierarchy of evil that is being built around the Rubinius project, but you can probably post a notification of your commits and your repository URL to the Rubinius lighthouse tracker. Don’t have a fork? Just submit patch files. Already have commit rights to Evan’s repository? Push directly to it.

Three different ways to contribute. Which one to use? The organisation of the project decides. Rubinius has a how to contribute page.

For the Rails TextMate bundle project, the preferred way to contribute is either patch files from your local clone, or to fork the project and let me know of your fork URL. Discussion of the project happens on the Google Group.

So, once your project has multiple forks floating around – how do you as a project maintainer pull in the commits contributed by others?

How to pull friends and influence people

Let’s continue with the example of Dean’s fork of the TM bundle. After he forked it, he used the previous article’s instructions to clone his own fork, rather than my repository. To you, once you’ve forked a repository, then your repository is your git “origin” and the original repository you forked just becomes a fork to pull from when you want to synchronise your repositories. Make sense? Thought so.

Put another way, if you don’t fork my TextMate repository rather you just clone it and modify it, then your “origin” will be my repository. You’ll then contribute by emailing patch files.

Or you can fork my repository and then you clone this new repository locally. This new repository is your “origin”. You contribute by committing changes locally and pushing them to your origin (your fork). I then investigate them (see below), and merge them into my repository if I feel like it (gifts help too).

So, Dean’s got a fork. He’s cloned it locally, made changes, committed them locally and pushed them out to his fork. I know I’ve said the three times, but it confused the crap out of me so I think its worth repeating… a lot.

Now how do I pull in Dean’s commits into my local clone, investigate them, and then merge them into my repository? I’m glad you asked.

From within my local clone:

git checkout two_point_ooh
git remote add strelau git://gitorious.org/ruby-on-rails-tmbundle/mainline.git
git checkout -b strelau/two_point_ooh
git pull strelau two_point_ooh

The first line changes my local clone into the “two_point_ooh” branch. Dean has told me that he has made his commits into his own two_point_ooh branch. So, my own two_point_ooh branch is the closest so I’ll start here. Its probably irrelevant which branch you start with. Perhaps.

The second line stores the information about Dean’s fork into my .git/config file. I’ve given the fork a label “strelau”, to differentiate it from my own remote repository “origin”.

The third line creates a new branch of my local repository. This branch will not be pushed up into my remote repository later on, rather its just for my own personal collection. The name of this branch is arbitrary. I could call it “ginger_beer_cordial”, but “strelau/two_point_ooh” is more meaningful. “strelau” to remind me which fork I should pull from, and “two_point_ooh” to remind me which branch of “strelau” to pull from.

Note, you are best to run this “git checkout -b name” operation from within another branch that most closely matches the contents you are about to pull in. That’s why I did the first line – to change into my own two_point_ooh branch first.

The fourth line pulls down any changes from Dean’s two_point_ooh branch into the current branch – strelau/two_point_ooh. That is, you now have a copy of it locally.

UPDATE: I really think this needs a good diagram here to explain the existance of two remote repositories, and within the local clone, the existance of two branches: two_point_ooh, and strelau/two_point_ooh. Plus there’ll be other branches in the local clone: master, origin/master (my remote version), origin/two_point_ooh (my remote version that I’ll push too when I’ve merged in Dean’s changes).

I can now investigate his commits. gitk is a fun tool to see the various branches and differences. It also shows the unique SHA1 codes for each commit, if you want to cherry pick changes rather than merge all of Dean’s changes into your own code.

Later, when Dean has more commits to offer the world, I’ll just run:

git checkout strelau/two_point_ooh
git pull strelau two_point_ooh

See how the name of “Dean’s branch” helps remember what the “git pull” command is? TIP: Give meaningful name to remote branches.

What now?

Once you’ve pulled down someone else’s remote branch (from their fork) it is just another local branch. You can explore it in gitk and see how it compares to other branches, for example.

gitk: Ruby on Rails.tmbundle

But what you’ll ultimately want to do is merge some/all of Dean’s commits into your branch, commit them, and push them out to your remote repository. If you have the “central” repository then this is akin to “accepting a patch”.

This process is now basic Git. Like driving the Death Star. Boring, and described elsewhere. Go back to your own branch, merge changes over, and push out to remote repo.

And now, Dean’s changes are in my remote repo:

Commit History for drnic's ruby-on-rails-tmbundle 2014 GitHub

And my gitk now show’s I’m sweet:

gitk: Ruby on Rails.tmbundle

But what about Dean?

That’s like caring about the people on Alderaan…

What I mean to say is… Dean can now pull in my remote “two_point_ooh” branch into his local “two_point_ooh” branch. He may or may not like what I did to it.

I guess Dean and the people of Alderaan can blog about that one…

Get ready for the TextMate “Trundle to Rails 2.0 Bundle”

logo_bundle.png

Have you noticed lately that the Rails TextMate git pull origin two_point_oohbundle (also installed with e – the Text Editor for Windows) isn’t quite as helpful as it once was – guiding and amplifying your rails app development.

Your resourceful/restful rails apps are now filled with named routes, but none of the helpers (link_to, redirect_to, etc) use them. The default view template was still rhtml for a long time (latest svn trunk fixes this to be html.erb, though Marc Jeanson is having a crack at supporting both through a per-project config file).

Or perhaps you were hoping for some snippets for new features of Rails 2.0: assert_difference (asd), assert_select (ass), respond_to (rst).

Or just maybe, you’ve been updating your local bundle to Rails 2.0 already? Please say yes, please say yes. If you are saying “yes”, read at the bottom for how/where to send me the patches. If you are saying “no”, read at the bottom for how/where to start creating patches and sending them to me.

Firstly, DHH didn’t write the Rails bundle

I didn’t know this – I just assumed the guy who created Rails and helped promote TextMate also created the Ruby on Rails TextMate bundle. Instead if was WWF’s “The Rock” himself Duane Johnson (figurines sold separately, and may look different to the real Duane Johnson who writes Rails code instead of prancing around in his underpants with other adult males in confined spaces).

The TextMate SVN logs say that it the bundle was first committed in Jan 2005. Its now Jan 2008. That’s three years of maintaining a suite of code that thousands of people take for granted every day writing Rails code. What a champion.

Like the Australian Cricket team’s wicketkeeper, Adam Gilchrist, he’s also retiring at the top of his game.

New Maintainer… me

Instead of maintaining my own projects, I’ve decided to maintain this one. I love the TextMate bundles. Before I had a Mac, I ported them across to RadRails and many RadRailers entered a golden era of snippet happiness.

My bundle-fu may not be the equivalent of Bruce Lee, but I’m keen to see my favourite Rails tool live on strong.

Bleeding edge bundle

Whilst the TextMate SVN administrators process the maintainer change request (Allan is on holidays somewhere and he took the house keys with him), you might think all is lost!

Never.

The straight-off-the-cow bleeding edge Rails bundle can be accessed via Git. Specifically the branch two_point_ooh.

So, let’s all do away with our pre-existing Rails bundle and live on the edge.

cd ~/"Library/Application Support/TextMate/Bundles/"
mv "Ruby on Rails.tmbundle" "RoR.tmbundle.orig"
git clone git://github.com/drnic/ruby-on-rails-tmbundle.git "Ruby on Rails.tmbundle"
cd "Ruby on Rails.tmbundle"
git checkout --track -b two_point_ooh origin/two_point_ooh
git pull

Now, back in TextMate, go to menu “Bundles” > “Bundle Editor” > “Reload Bundles”.

You, my friend, are sitting pretty.

Upgrading to when newer blood is on the bleeding edge

Coming back for more?

cd ~/"Library/Application Support/TextMate/Bundles/Ruby on Rails.tmbundle"
git checkout two_point_ooh  # optional; only if you changed to another branch etc
git pull

Now, back in TextMate, go to menu “Bundles” > “Bundle Editor” > “Reload Bundles”.

Bathe yourself in glory. Splash it all over yourself.

Submitting patches

If you go into TextMate, launch the Bundle Editor (Cmd+Alt+Ctrl+B) and change something in the Ruby on Rails bundle, then files will added/changed within your local git clone.

What I mean is, you can very simply submit patches now. [When I say "simply", I mean, "I think this is the best approach but I could be completely wrong here..."] Git is so cool that I don’t know what its doing most of the time; but let’s hang in there as a team.

cd ~/"Library/Application Support/TextMate/Bundles/Ruby on Rails.tmbundle"
git add .   # to add any new files/snippets/commands you created; TextMate uses one file per snippet
git commit -a -m "Useful message about this patch goes here"
git diff -p HEAD^ HEAD

[Theoretically] this creates a patch based on your most recent commit. So you can easily create patches for changes even if your repository and the remote/central/bleeding edge repository look different.

If your patch was created over several commits, then replace HEAD^ with HEAD~4, if there were 4 commits.

I think. God I could be wrong about this though.

Just try your best and if it works, let me know and I’ll remove all this “I don’t know what I’m talking about” text and future readers will think I know everything. Web 2.0 rocks.

Send patches to…

Me drnicwilliams@gmail.com [update] or via the new Google Group.

Update: Note there is a new Google Group (rails-textmate) created in Feb 2009 due to Spam and loss of admin control on original group.

Want to fork the git repo? Use a free account at Gitorious or GitHub.

Google Groups
Ruby on Rails: TextMate
Visit this group

Trundle to Rails 2.0 Bundle

Arguably, we just keep patching the bundle forever and bleeding edges will continue to live happily within the blood and muck of said existence.

But, lots of other people want nicely zipped up bundles. No doubt “e – the Text Editor” will want a “released” version so they can clean it of all the ugly things they can’t yet replicate like Macros. And eventually I’ll push it all down to the SVN repo and it will be released with future TextMate upgrades.

So, there will be a “Rails 2.0 compatible” grand opening one day soon. Probably before 14th of March 2008, when I’m giving a “What’s new in Rails 2.0?” talk at QCON London. Showing off the new bundle will be sweet.

That gives us 6 wks to refactor this bundle to make it a shining example of all that is wonderful in Rails 2.0. Every patch helps.

[Please consider Rails 1.2.6 and before developers when conceiving of patches, and please consult your doctor if pain persists.]

Going offline without your favourite Subversion repository?

UPDATE: useful gitify command below.

All my client projects are hosted on Subversion repos. All my OSS projects are hosted on Rubyforge Subversion repos.

And tomorrow I head off for the 2nd “no internet” RailsCamp megafest, being held outside of Melbourne. From what I heard about the 1st one, it involved a lot of Guitar Hero II and beer. This weekend I’m promised that someone is bringing a Wii – dear God I do love the tennis game.

Like MacDonalds burgers and Veganism, if there are two things that don’t go together its Subversion and “No Internet”.

Fortunately, for all RailsCampers, there is a solution to your fears that you won’t be able to hack on your favourite project, and will be forced into playing Guitar Hero II/III and/or the Wii for the whole weekend.

Git.

Specifically, the git-svn command.

More specifically, follow the following steps:

  1. git-svn clone <your svn url> projectname.git
  2. Go camping for the weekend.
  3. Commit changes with git commit -a -m "hahaha I'm camping and working - doh!"
  4. Come home from camping.
  5. git-svn rebase to re-import any SVN changes that might have been committed whilst you were away. If there are conflicts, just follow the instructions.
  6. git-svn dcommit and your svn repository will be updated with all your fancy changes; no one will ever know you used Git.

Installing Git

I remember having problems getting git-svn to run because the default path to the perl cmd is crap, and you need to specify PERL_PATH='/usr/bin/env perl', I think, before installation.

For MacPorts etc, its sudo port install git-core. I think the reason its not just called ‘git’ is to remind you that you know nothing about git. Which is fair. You don’t if you don’t know how to install it, I guess.

From source, get the tarball from the home page.

For Windows, use cygwin.

Gitify command

Want a utility cmd to create a Git repo in the parent folder of a Subversion project? That is, go to your Rails app that is checked out from a Subversion repo, and run gitify, and you will have a foldername.git folder in the parent folder.

Stick this code into ~/bin/gitify and add ~/bin to your path: