CodeProject
I recently started migrating my code repositories from Subversion (svn) to Git. This is an account of my setting up Git and migrating one of my svn repositories over to a Git repository.
How this began
I don’t really know what exactly made me switch to git, but I was regularly encountering problems using TortoiseSVN and AnkhSVN, where on good days I was forced to delete my working copy and work with a new checkout. On bad days, I was forced to delete my repository and create a new one from a working copy cleansed of all .svn stains.
One of the appeal factors was the facility provided by git to push changes from a local repository up to a remote repository. I have toyed with many ideas for achieving this, first with VSS, and later with svn, but all of them were, in my naiveté, risky and complicated. With git, this is painless and routine task. Please note that this is not the same as committing changes from a local working copy to a remote repository. I have full source control locally, with commit histories and branching and merging, in what is essentially the same repository as the remote one, just more downstream.
I’m sure getting to know the architecture of svn, and learning svn best practices, and cutting out the often shaky GUI tools, and going straight to the metal on the svn command line interface, would have helped me solve many of my problems, but the thing is, when svn suddenly throws a hissy fit a few hours away from a demo, there isn’t time for a second high school education.
I began this project like I begin most projects, with a Google search (for git windows) followed by a vetting process that selects a few promising tabs in the browser out of the millions opened off the search results. The first post I found with enough information to actually get started was Kyle Cordes’s Getting Started with Git and GitHub on Windows. At the time I only had a vague idea of what GitHub was, but I quickly found out that, as Kyle explains, “GitHub offers a useful set of online features to supplement what Git has built in and available locally“ Upon further investigation, Wikipedia told me that, “GitHub is a web-based hosting service for projects that use the Git revision control system.” I was now thoroughly confused as to whether to spell git with an upper or lower case gee, but I knew I didn’t want GitHub, as I already pay unfuddle.com for subversion hosting and project management tools, and I knew they also provide git hosting. I decided I would follow Kyle’s tidy looking instructions, but skip the GitHub bits.
My main objective with this post is to describe my experiences in moving to git, as well as to use that experience to try and provide some guidance to others considering making the change. This is in no way a comprehensive guide to installing or using git, and for that kind of information, I pray you consult the resources I refer to in my text.
Getting my hands dirty.
Guidance.
Although Kyle Cordes gets first credit for getting me up and running, my first planned migration to git took second place to urgent work on a project I was busy with, and staying with a mostly stable and known source control setup. I eventually ended up removing git in preparation for a more serious re-install and adoption. No turning back. I started at base Google again, but this time I found some more two more great resources for setting up git on Windows:
-
This is an excellent screenshot based walk-through, from setting up git and creating a repository, making several local commits, creating and later merging a branch, and pushing to a remote repository. I was very pleasantly surprised, as normally a first glance that shows me more screenshots than prose immediately instils a bad impression. I think I’ve seen too many copy and paste articles and bad PowerPoint presentations based only on screenshots. It covers most common tasks using the very competent GUI tools installed with git.
-
A more involved and lengthy work than Illustrated Guide, this series of three articles by Jason Meridth also uses lots of screenshots, but includes more in-depth explanations and concentrates more on using the bash shell installed with msysgit. Readers wanting to really get to know git should work through all three of these articles, while interested in just getting up and running on git for basic tasks need only work through Illustrated Guide.
Installing git.
Although it is possible to install proper git, or the mainstream Linux based git package, on Windows under Cygwin, for most purposes the Windows targeted package, msysgit, is more than adequate, and its installation is much simpler.
Download the installer from it’s Google Code home and run it. I went with the Git-1.6.5.1-review package, and have had no problems so far, and I mean absolute zero, even during installation! However, I am a very ‘light’ single developer shop, sticking mainly to ten to twenty local commits a day, and maybe two or three remote pushes.
The installer prompts for a series of very usual installation options, and you may stick with defaults or choose to customize, but, I find the Windows Explorer integration options, on the “Select Additional Tasks” page of the wizard (the 5th page, including the start page), ‘Add Git “Bash Here”‘, and ‘Add “Git GUI Here”‘, to be very important. I use these two context menu items all the time.
The options after that, for “Adjusting your PATH environment” are also important, and I recommend sticking to the conservative, “Use Gut Bash only”. This doesn’t mean no GUI, just that you won’t be mixing and matching the Gut Bash shell with the Windows command line.
Who goes there?
At this point git is already ready to use, as you don’t have to create a central repository anywhere, as git creates a separate repository for each project or solution, but for communicating with a git hosting provider, we need an SSH public and private key pair. This ensures no other copy of you can maliciously push code to a remote repository on your behalf. Of cause if you already using such a key pair, you can skip the key generation step.
Talking of intruders or imposters, if you’ve never used bash or a Linux shell before, things may now look a little strange and out of place, but except for some really esoteric things, you get used to Git Bash quite quickly. Plus it looks nicer than the default Windows command window. The Illustrated Guide talks about downloading and using puttygen.exe for this step, but that guide seemed a little dated in this area, so I ignored it in favour of the Git Bash approach described in Git For Windows Developers. Right clicking on any folder in Windows Explorer brings up the context menu, and a click on the new “Git Bash Here” command opens up the bash shell. The folder you do this on is unimportant; in this case, we only do this to get the context menu. Git For Windows Developers describes two extra steps here that I missed following Cordes’s instructions, but somehow I found out how to perform them later, and while they are not required now, I recommend getting everything done now.
Configure the global default user name and email, which I’m guessing will be yours, as I did it with mine and it still works. In the bright and colourful Git Bash window, enter the following two commands. I don’t know if it’s apparent that I’ve highlighted the second hyphen just before the “config” on each command, but when I copied and pasted from the Git For Windows Developers into notepad (I couldn’t figure out how to paste into Git Bash), the two hyphens were merged into one, and only typing one in the commands caused me some confusion, anger, and frustration.
git config – -global user.emailmy@email.com
git config – -global user.name “User Name”
Then proceed to generate yourself an SSH key pair (if you don’t already have one). Please observe case-sensitivity and this time, be careful to only use single hyphens. I hear you crying, “WTF?” Bash uses single hyphens for single character command line switches, but double hyphens for the full versions of the switches. This command only has single character switches. Welcome to the Linux world young Padawan.
ssh-keygen –C “my@email.com” –t rsa
The command prompts for a location to store the key pair, and I just took the default here, which makes sense, because only SSH uses this, and the default stores in in your .ssh user folder. It then prompts you for a pass-phrase, which if you enter and confirm one, will be required anytime your key is accessed. Only enter one if you are really concerned about security and work on a shared machine or something. Otherwise every time you push to a remote server, you are prompted for the passphrase, and it is no trivial matter to automate this. This throws a spanner in any plans you may have to try scheduled, silent pushes through scripting etc. Ask me, it’s not worth it unless you really need it.
Meridith adds a side note:
Back up these keys to a secure location. If they accidentally get corrupted, deleted, or changed you will be able to restore them at any time. I type “secure” because you need to make sure no one gets a hold of your private key. If either of those situations occur you can just recreate the keys, but every server that already has your public key will need the new public key (kind of a headache and time consuming). I’m speaking from experience on this.
Getting into git.
My first task was to migrate a solution currently controlled using svn over to git. First task was to make sure all changes were committed to my svn repository hosted by unfuddle, and then get a clean working copy, sans the nauseatingly recurring .svn folders. The ‘Export’ command of svn does this nicely, and I did it nicely using TortoiseSVN:
I exported to the root of C: merely as a temporary measure, because as soon as the code was pushed up to my new git repository on unfuddle, I could move my svn working copy, and pull down a new git working copy from unfuddle, completing
and testing the SCC round-trip. Creating a new git repository and getting my code into it was too easy, and here is a warning. Git for Windows Developers has the best guidance on this under the section, Putting My Code Under Source Control With Git, in Part 1. I’m getting a little ahead of myself here, but, it is essential that you do not add files to the new repository before creating your .gitIgnore file. This file tells git which other files to ignore, i.e. not consider part of the repository. You should never include .gitIgnore in .gitIgnore, because then changes to .gitIgnore will not be detected or committed. .gitIgnore should always be treated as the part of the repository that it indeed is.
I think it’s always better to start out learning the command line ways, and then get lazy:
Note that I’m opening Git Bash in the location I exported to, not my usual working copy location.
The entire, marathon process of creating a repository distils down to the following tasks:
Create the repository using the git init command.
Create the .gitIgnore file to list files to be excluded from the repository.
I use touch because Notepad wont let you save a file starting with a dot, so touch creates it, and then I can open it with notepad and populate it with entries such as these:
Cottages/bin
Cottages/obj
Rhino/bin
Rhino/obj
_ReSharper.Rhino
/Rhino.5.resharper.user
/Rhino.suo
These means: ignore entire folders like Cottages/bin and Cottages/obj, as these are always rebuilt; ignore files like /Rhino.suo and Rhino.5.resharper.user, as these store only user specific settings.
Stage and commit all files in the solution.
Staging means getting a bunch of files ready for a commit, but can be done incrementally, as you review files etc. and is done via the
add command. Here we add all files (excluding those listed in .gitIgnore). Only staged files are committed. Git requires that every commit have a commit message, provided here with the -m switch on the commit command. Commits act on all staged files.
<!--StyleSheet Link--><!-- body{ font-family: Georgia, "Times New Roman", Times, serif; font-size: small; } .consolasCommmand { font-family: Consolas; font-size: medium; } -->When created my first git repository, I didn’t include a .gitIgnore file, and I went ahead and staged and committed all files. I also proceeded to make code changes and subsequent, additional commits. I found out quite quickly that removing unwanted files from a repository with history was no trivial matter, and it was easier to delete the repository and start again.
It took all of about three minutes to do that, excluding redoing my code changes, because if you look at any subfolder of your repository, you don’t see anything. There is only one .git file at the root of your repository, and removing that file removes everything to do with git, except .gitIgnore.
<!--StyleSheet Link--><!-- body{ font-family: Georgia, "Times New Roman", Times, serif; font-size: small; } .consolasCommmand { font-family: Consolas; font-size: medium; } -->
Getting out of the house.
My next big step was to set up a git repository on unfuddle, which I called RhinoNew, because I already have an svn repository hosted there called Rhino, and I really didn’t want to delete that just yet. They have excellent guidance for this (and, of course, support if you need it). To use my new remote git repository, I needed to authenticate myself to unfuddle, and to do this I needed to add the public key I created earlier, when first setting up git, to my personal account settings on unfuddle. I then needed to add a remote repository ‘reference’ to my local repository. Once again all commands are executed in a Git Bash shell opened on my new local repository:
<!--StyleSheet Link--><!-- body{ font-family: Georgia, "Times New Roman", Times, serif; font-size: small; } .consolasCommmand { font-family: Consolas; font-size: medium; } -->Unfuddle have cool help text that automatically adapts to the repository you navigated to help from. See how the uri etc. are all correct in their example!
<!--StyleSheet Link--><!-- body{ font-family: Georgia, "Times New Roman", Times, serif; font-size: small; } .consolasCommmand { font-family: Consolas; font-size: medium; } -->It was after all this, at the bottom of the unfuddle help page that I discovered that there is an easier way: Git can actually clone an svn repository over to a new git repository! I will investigate this procedure in my next post, coming soon.
I am only a single user, with clients that may need a copy of my repository, so I haven’t gone as far in to git as pulling down other people’s changes from the remote repository or anything yet. All I need is a change history and an off-site repository that I can clone again onto my dev machine should disaster strike. My last step was to check if my code had survived the full round-trip, from me to remote, and back to me.
I moved my original svn repository somewhere out of the way, and then cloned the remote repository using my unfuddle url. Cloning creates a directory and repository automatically, so no need for mkdir and git init commands.
<!--StyleSheet Link--><!-- body{ font-family: Georgia, "Times New Roman", Times, serif; font-size: small; } .consolasCommmand { font-family: Consolas; font-size: medium; } -->Notice that in pushing to and cloning from unfuddle, I was nagged for the passphrase to access my SSH key-pair. This is the issue that is difficult to automate, that I mentioned earlier.