Moving From Subversion to GIT

Last weekend was a PFZ meeting. And for this day a few interesting workshops were planned. I mainly was looking forward to the GIT (Git for Subversion Users) talk by Stefan Koopmanschap. And the Dependency Injection presentation by Berry Langerak I didn’t make it too the last one. But learned a lot while doing the GIT workshop with Stefan.

For the last few months i have wanted to take a look at git. But it just never really took the time to sit down and do it. So this opportunity was there at the right time. Although my laptop died halfway through the workshop. I still was able to make it through the whole session together with Joost van Veen. Thanks man. We had a great time. And it was eye opening. Stefan gave a good talk. Helped out where possible. And besides a small Github glitch. It was a successful day.

So successful that i decided to make the switch from Subversion to GIT. Below are some of the steps i took.

The main reasons for me to make the switch

  • Distributed structure make it easy to work from any place. connected or not.

  • Stashing

  • Speed and cleaner folder / file structure

  • Real tags

That’s all for now. but this list will grow. I am sure!

Setting up a GIT server

I decided to go for my own GIT server instead of Github. That’s just the way i like it. Full control.

$ aptitude install git-core

Now all we have to do is create a user for git operations. And set the correct permissions.

$ adduser git $ mkdir /home/git/.ssh $ chown -R git:git /home/git/.ssh $ chmod 700 /home/git

If we want to create a new remote repository. We log in on the git server through SSH and setup a new repository like this.

$ ssh git@git.domain.tld $ mkdir repository.git $ cd repository.git $ git –bare init

That’s all. we now have all we need to connect to git remotely and do some work. But now first we need to import the old Subversion repository. I have found two solid methods for getting the job done.

Import a SVN project into GIT with git-svn

First of install git-svn

$ sudo aptitude install git-svn

Before we start the importing. It’s good practice to create an author mapping file. So the SVN commiter names match the GIT ones. This is done by creating a simple text file like so.

$ touch authors.txt $ echo “m0s = Thijs Lensselink email@lenss.nl” > authors.txt

Importing the existing tags and branches is done by adding the –stdlayout switch

$ git svn clone –stdlayout -A authors.txt svn+ssh://user@svnhost/repository repository.git

The problem with this method is that tags and branches are not created properly. I decided to go for the second method down here.

Import a SVN project into GIT with svn2git

For this to work we first need ruby and some gems packages.

$ apt-get install git-svn ruby rubygems $ gem install svn2git –source http://gemcutter.org

Create the local repository and import the files from SVN

$ mkdir repository.git $ cd repository.git $ /var/lib/gems/1.8/bin/svn2git svn+ssh://user@svnhost/repository –authors ../authors.txt

Show the current selected branch

$ git branch * master

Show all branches. Notice the trunk branch

$ git branch -a * master trunk

List all tags

$ git tag -l v1.0 v2.0

That seemed to work pretty good for almost all projects. Some projects didn’t have the main trunk in SVN. So to import these just add the –rootistrunk switch.

And while this info is still available in my brain at the moment. I will write down some of the workshop material for future reference.

Setup local Development environment

Create directory somewhere that will serve as the local repository. And run init to create the basic git project structure.

$ mkdir [project name] $ cd [project name] $ git init

Now we have a fully functional git repository. We can do anything with (add, commit, tag, branch, push, etc.) The first things to do when starting out is setup the user name and email address of the developer using this repository. This is good when looking though the GIT project logs later on. Also handy to set the core editor to the most convenient editor available.

$ git config –global user.name “Thijs Lensselink” $ git config –global user.email “email@lenss.nl” $ git config –global core.editor vi

The repository exists out of a origin and a master branch at this moment. The origin refers to the current repository location. And the master refers to the current repository state. Like HEAD in CVS and SVN. Let’s add a file for testing.

$ touch index.php $ git add index.php (or add *) $ git commit -m “Commit something”

Working on a local repository is nice. but we want to save it on a server of course. For this we need to set a repository location. We can do this in two ways. We can overwrite the origin or add a new location. Let’s try the first one.

$ git remote add origin git@git.domain.tld:repository.git

Setting up the remote git repository and push the changes

All changes to this repository are made locally so far. Let’s try and push them to the git server. The only problem here is. I haven’t found a way to create repositories remotely. So we have to log in to the server here and create the repository.

$ ssh git@git.domain.tld $ mkdir repository.git $ cd repository.git $ git –bare init $ exit

Back on the client side we can now do a push an see what happens.

$ git push origin master Counting objects: 3, done. Writing objects: 100% (3/3), 232 bytes, done. Total 3 (delta 0), reused 0 (delta 0) To git@192.168.178.24:foo.git * [new branch] master -> master

if you get this error:

error: src refspec master does not match any.

This means there is no master branch yet. This happens when the repository was created with the –bare switch. You have to commit a change first before you can push anything to the remote repository server.

That’s all. We now have a working GIT server and local repository. All that is left is to do some serious development work. On the other hand, before i forget. i will use this space to rehash Stefan’s workshop for future reference.

Tagging

Let start by looking at the way git creates tags. First of there are two types of tags lightweight and annotated. The lightweight version is nothing more then a pointer to a specific commit. It’s like a branch that doesn’t change. Annotated tags are a bit different then their lightweight version. And for sure different from the tags in Subversion. A tags in git contains the following data

  • checksum

  • tagger name

  • email + date

  • tag message

  • GPG signed

Creating an annotated tag for version 1 looks something like this

$ git tag -a v1.0 -m “Creating a tag”

Dispaly a certain tag

$ git show v1.0

I haven’t had time to look into working with signing of tags. So maybe i will add an entry about that later on.

Branching

A repository can have multiple branches. And switching between branches is a since in git. Branching is much like it is in Subversion.

Create a new branch from master called foo

$ git branch foo master

Display all branches available. The current branch is highlighted by the * sign

$ git branch * master foo

Changing branches in git is a piece of cake.

** $** git checkout foo Switched to branch ‘foo’

There is not much more to branching.

diff, merge and delete branches

When adding a new feature to an existing code base. It’s good practice to create a branch before you start editing. And after everything is done and the change is stable. The branch can be merged back into the master.

First let’s check if there is a difference between the newly created branch and the master.

$ git diff foo master diff –git a/touch.foo b/touch.foo deleted file mode 100644 index 257cc56..0000000 — a/touch.foo +++ /dev/null @@ -1 +0,0 @@ -foo

Some changes have been found so let’s merge them in the master branch

$ git checkout master $ git merge foo master (or git pull foo master) Already up-to-date with 12d2f9cebaf71a580b021a8eeddc38f267b69e53 Trying simple merge with 4d3dff7e0d2a26302a5884e763fd7a48f5ab4437 Merge made by octopus. touch.foo | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) create mode 100644 touch.foo

with git pull it is possible to import branches from other repositories

When done with the changes. And everything is pushed to the remote git server, we can remove the ‘foo’ branch

$ git branch -d foo Deleted branch foo (was d9a8c76).

That’s all for branching right now.

Stashing

One of my personal favorite the stash command. Imagine working on a project. And somebody asks you to make a small change. In subversion i needed to do a new checkout and work on that. This can be quite slow and cumbersome. In git this is solved elegantly by using the stash command.

$ git stash Saved working directory and index state “WIP on master: 648f347… foo” (To restore them type “git stash apply”) HEAD is now at 648f347 foo

This saves a copy of the working state and let’s you do changes on a fresh base. After your done get back to your work by doing

$ git stash pop (or apply)

With pop you push the stash back to the master. With apply the changes will first be applied to the master before restoring the work state.

Working with other repositories

Clone a repository from a remote GIT server

$ git clone git@git.domain.tld:repository.git ./project2

Clone a local GIT repository

$ git clone ./project ./project2

Now it could be possible a friend or a remote community member without write access made a few changes to his / her remote repository. And wants them on the global remote GIT server. This can be done by pulling the code.

First add the new repository location

$ git remote add friend git://githubaddress

And then fetch the remote changes

$ git fetch friend

This only fetches the changes. but does not apply them to the master. Apply them

$ git pull friend master

updating remote repository

$ git push friend master

That’s all i can remember right now.

comments powered by Disqus