Howto: fork a Subversion project with Git
This is a short excerpt on how you can use Git to fork a Subversion project.
I assume that:
- you want to maintain your own fork of a project, and intend to follow its evolutions
- you have no commit-access to the upstream Subversion server, or you do not intend to push you changes back.
We will take XStream as a running example.
Import the Subversion project into Git
git svn clone --stdlayout --prefix=svn/ http://svn.codehaus.org/xstream/ xstream-fork
This creates a complete clone of the XStream Subversion repository. Tags and branches will be imported as well (--stdlayout).
Git has local and remote branches. The local ones are those you make modifications too. They generally merge from remote branches, which you can think of as read-only copies of upstream branches.
Git-SVN turns Subversion branches as Git remote branches. The --prefix=svn/ makes it easier to differentiate them from regular Git branches.
What you get
You can check what was imported:
git branch -a * master remotes/svn/pull-parser-merge remotes/svn/tags/XSTREAM_0_2 remotes/svn/tags/XSTREAM_0_3 remotes/svn/tags/XSTREAM_0_4 remotes/svn/tags/XSTREAM_0_5 remotes/svn/tags/XSTREAM_0_6 remotes/svn/tags/XSTREAM_0_6_RC1 remotes/svn/tags/XSTREAM_1_0_1 remotes/svn/tags/XSTREAM_1_0_2 remotes/svn/tags/XSTREAM_1_0_RC1 remotes/svn/tags/XSTREAM_1_1 remotes/svn/tags/XSTREAM_1_1_1 remotes/svn/tags/XSTREAM_1_1_2 remotes/svn/tags/XSTREAM_1_1_3 remotes/svn/tags/XSTREAM_1_2 remotes/svn/tags/XSTREAM_1_2_1 remotes/svn/tags/XSTREAM_1_2_2 remotes/svn/tags/XSTREAM_1_3 remotes/svn/tags/XSTREAM_1_3_1 remotes/svn/trunk remotes/svn/xmlencoder-spike
By default, your master branch tracks svn/trunk. You can easily make a local branch based on something else, such as the xmlencoder-spike branch:
git checkout -b xmlencoder-spike xmlencoder-spike
Another thing is that tags in Subversion are just folders, so Git-SVN imported them as branches. You may transform those into real Git tags:
git tag -m 'Tag for v0.2' -a v0.2 svn/tags/XSTREAM_0_2
Work!
The best practice in Git is to work with feature branches, so each time you need to work on something, you should really spin a branch!
Choose which local branch to fork, such as:
git checkout -b my-cool-feature master
Make your commits along the way, and when your work is ready to be merged:
git checkout master git merge my-cool-feature # Optional, if you don't need the feature branch anynore git branch -d my-cool-feature
For quick one-offs, you may not need feature branches though.
Synchronize!
To update all Git-SVN remote branches:
git svn fetch
To merge onto local branches that track Git-SVN remote branches:
git svn fetch git checkout master git merge svn/trunk git checkout 3.1 git merge svn/branch-3.1
You may also directly fetch and apply the changes if your branch was created from a Git-SVN branch:
git svn rebase
Make a real Git clone repository available
You can't push Subversion remote branches to another repository, so if you need to make your Git fork public somewhere (such as on GitHub), you will need to:
- create tags as mentioned above
- create a local branch for each Subversion branch that you want to republish such as stable branches
- push those tags and local branches to your public repository.
Bring an iPhone 3G back to life from iOS 4
So you've updated your iPhone 3G to iOS 4 and cannot use it anymore due to frequent freezes, seconds-long delays to get taps feedback, and the general sensation that "this is really sluggish!"?
Apple does not provide an official way to downgrade to a previous version of iOS. A real shame as iOS 4 is not usable at all on a 3G while 3.1.3 just worked like a charm.
The solution to downgrade is rather easy though. I won't make this a real tutorial given that there are already lots of them on the Internet, but here is what you have to do.
- Grab a 3.1.3 firmware. I recommend looking around on this page.
- Grab RecBoot, a utility that puts you in and out of the damn DFU/recovery mode.
- Put your iPhone in DFU mode. Opt to restore, but alt+click the button in iTunes instead of just clicking it. Pick the 3.1.3 firmware that you just downloaded.
- When the phone reboots you will get an error message. Nevermind, and use RecBoot to get out of the DFU mode.
- Enjoy the activation process, and preferably, do start with a fresh setup rather than an old backup.
- If you failed during this process then don't blame or sue me. Blame your brain and fingers.
In my case I recovered a fully functional iPhone 3G. I won't upgrade it again to iOS 4 unless Apple makes some serious performance improvements. I honestly think that they made a big big mistake here, making their happy customers angry ones.
Sending an email on iPhone OS
I have been coding on the iPhone OS recently (for a research prototype). The application is basically capturing some data from the GPS and the accelerometers.
The data is stored in a sqlite database that is accessed through Core Data. From time to time, the user can send the data by email. Doing that is extremely easy with the iPhone OS.
I first added a method for creating a CSV representation of the data:
- (NSMutableString*) csvData { NSError *error = nil; if (![[self fetchedResultsController] performFetch:&error]) { NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } NSMutableString* str = [[NSMutableString alloc] initWithString:@"timestamp,altitude,latitude,longitude,xAcceleration,yAcceleration,zAcceleration\n"]; for (NSManagedObject* entry in self.fetchedResultsController.fetchedObjects) { [str appendFormat:@"%@,%@,%@,%@,%@,%@,%@\n", [entry valueForKey:@"timestamp"], [entry valueForKey:@"altitude"], [entry valueForKey:@"latitude"], [entry valueForKey:@"longitude"], [entry valueForKey:@"xAcceleration"], [entry valueForKey:@"yAcceleration"], [entry valueForKey:@"zAcceleration"]]; } [str autorelease]; return str; }
The data is represented as NSMutableString, from which can later extract a NSData representation from. The actual code is just a walk over the data rows that Core Data manages.
The controller method that sends the email is very simple as it uses the ready-to-go MFMailComposeViewController controller.
- (void)sendMail { if (! [MFMailComposeViewController canSendMail]) { UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Unsupported operation" message:@"This device is not configured to send emails." delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil]; [alertView show]; [alertView release]; return; } MFMailComposeViewController* mailController = [[MFMailComposeViewController alloc] init]; [mailController setSubject:@"Entropia Data"]; [mailController setMessageBody:@"This data was captured by Entropia.app" isHTML:NO]; [mailController addAttachmentData:[[self csvData] dataUsingEncoding:NSUTF8StringEncoding] mimeType:@"application/csv" fileName:@"data.csv"]; mailController.mailComposeDelegate = self; [self.navigationController presentModalViewController:mailController animated:YES]; }
The mail composition controller gets some pre-defined data: the subject, a message body and a first attachment which is the CSV data we computed earlier.
When the user either taps for sending the email or canceling it, the mail controller delegate is called. It can perform some checks and cleanups to see if the mail was sent or not, but at the very least you must discard the mail controller and release it. Otherwise, your application will stay blocked on that screen!
- (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error { [self.navigationController dismissModalViewControllerAnimated:YES]; if (result == MFMailComposeResultFailed) { UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:[error localizedDescription] message:[error localizedFailureReason] delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil]; [alertView show]; [alertView release]; } [controller release]; }
As you can see, the iPhone OS provides a very sound and easy framework
On Git-SVN interoperability
This is a short entry to warn you on the Git / Subversion interoperability which I have advocated for quite some time now.
git-svn is a powerful Subversion client. Using it is a very good idea as long as:
- you keep linear history on your branches thanks to rebasing (if you mess up with real merges you can still recover using squashed commits), and
- you do not try to collaborate through Git with other folks too much (i.e., they need to fork your very own repository, and keep history linear as well).
There are some drawbacks when you collaborate with others through Git. An obvious one is that commits back to Subversion rewrite your Git history, which is never good for people who forked your branches.
However, the worst issue is that cloning a Subversion repository in Git yields different hashes over different runs... which means that although the trees and files in several svn-based clones of one repository are the same, they have different hashes, which means repositories with logically independent histories.
This does not mean that Git is wrong, or that the Git / SVN interoperability is flawed, but you must be aware of those issues.
PS: if you have an idea of why cloning Subversion repositories gives different hashes then I look forward to reading your comment!

