Archive

Posts Tagged ‘git’

SVN to Git

September 10, 2010 1 comment

How to cleanly migrate from svn to git.

Git’s in, SVN’s out

Let’s face it, it’s 2010 and DVCS’s are in style. SVN and CVS are out, git and hg are in. All the cool kids (python & Ruby-istas alike) are jumping ship on SVN and git is the new cool tool.

So how do you jump on the bandwagon and kick your SVN habit to the curb?

How do you migrate from SVN to Git?

If you’re seriously considering a move from Subversion to a DVCS (and git in particular), please choose from the following options:

  1. You’re sick of subversion and have heard of git, but have no clue where to start
  2. You’re sick of SVN, but your team/job/friends are forcing you to stick with SVN
  3. You’re ready to move to git and leave SVN behind, but you need to port legacy code/repositories on your local server
  4. You’re ready to move to git and don’t have a place to host the code, or don’t care if it’s hosted in the cloud
  5. You think git is confusing and wish this article was about mercurial (hg)

If you answered #1 or #4, stop reading now and go over to Github and sign up for a free (or paid) account. Github will guide you every single step of the way, from setting up SSH keys to common git commands and gotchas. Not to mention the interface is very usable and downright helpful in many cases.

If you answered #2 (sick of SVN but stuck with it), you probably want git-svn to help bridge the gap. The ‘net is full of examples, tutorials, and quite frankly I’ve never had the patience to make it work right… but some people swear by it.

My biggest issue with using git-svn is you get some of the benefit of git (local commits, fast log access, etc) but none of the workflow changes that git inspires/prompts. Go read about the Integrator Workflow chapter on Pro Git. It looks like a lot of overhead to begin with, but it saves you SO much time in the long run, it actually improves code quality.

If you answered #5 (you don’t like git) — well, sorry, I haven’t used hg much. Best of luck, let me know how it works for you.

For the rest of you — those who are ready to migrate to git but don’t know how — read on!

First things first: install git.

If you’re on Windows, things have gotten better but are still a little clunky. Check out msysgit and TortoiseGit.

If you’re on Linux, you need to install the “git-core” package — assuming you’re on Debian or Ubuntu, try

$ sudo apt-get install git-core

If you’re unlucky and stuck on RHEL, CentOS, or Fedora — first of all, I’m sorry that you have to deal with yum. Cross your fingers, and try

$ sudo yum install git-core

If the mirrors aren’t down and the planets align correctly, yum will eventually install git.

Export your SVN repository to Git

There are at least two popular ways to import from SVN to git. You can use git-svn (git svn clone -s SVN_URL LOCAL_DIR), but I haven’t had a lot of luck getting this to work right. Last I checked it leaves some annoying metadata and requires some extra hackery.

I personally have found the best success using svn2git. Github has some details on svn importing page — but the main svn2git page spells it out pretty clearly.


$ mkdir my_local_dir && cd my_local_dir
$ svn2git http://my.svn.server/path/to/repo/and/subdir

That’s pretty much it, after svn2git runs you will have all of branches/tags/trunk imported into your local directory as a git repository. You may have to fiddle around with the options, and if you’re behind a proxy (why do corporations think this is useful or somehow more secure?!!! proxies cause SO many issues… sigh) then you may have to fiddle with $http_proxy.

Oh, and before I forget, you’ll probably want to create a “git authors” file — this provides a mapping between subversion-style user IDs (like abaker) and git-style IDs (Adam Baker <abaker@mycorp.com>). This isn’t important for a dry run, but when you do migrate your code in the end, you’ll definitely want this to be working correctly.

Sharing your changes

Last but not least, chances are you’ll want to push all these commits to a bare repository on a central server somewhere. Git doesn’t require you to have a “trunk” or official repository, but most people generally designate one repository as the official / blessed / trunk repo. It just makes logical sense to have one spot where are the good code is, where everything’s clean/tested/reviewed and thereby ‘blessed’.

I’m assuming my_local_dir is on your personal dev box — I would highly recommend following something similar to the Integrator Workflow, in which case you’ll do the following


# initialize the bare repository which will be considered 'pristine' or 'blessed' (aka SVN trunk)
[user@main_server:/path/to/repo] $ git init --bare

# add the bare repository as a 'remote' (something you can push changes to)
[user@dev_box:/path/to/my_local_dir] $ git remote add origin user@main_server:/path/to/repo
[user@dev_box:/path/to/my_local_dir] $ git push --all

At this point, you've imported from your Subversion repository into a local git repo, initialized a 'blessed' (trunk) on your main server (which presumably everyone has SSH access to - or you've set up Gitorious, Github:FI, or gitweb) and you're ready to start coding!

If you've made it this far, you're definitely going to want to read the Pro Git book. Consider it the complete reference on git, and an invaluable asset on your way to becoming a git master.

The short answer is someone new to your team/project/etc will clone the blessed repository one or more times, make some local changes, commit them locally. Later, someone will pull those changes into the main repository when they're stable/reviewed/tested and otherwise considered ready for merging.

Conclusion

Git is a very powerful but often confusing DVCS. If you're new to git, I highly recommend you start with Github or Gitorious. They both provide a nice interface with tips and hints to help you with common workflow tasks, and they provide/require/strongly promote changeset review on every merge. It feels awkward at first, but it changes your life... seriously.

So what're you waiting for? Go 'git' started! (ha ha ha)

atto

Categories: Uncategorized Tags: , , ,

Cloning a clone: Hg vs Git

May 12, 2010 13 comments

I’m in the progress of learning about git and mercurial (Hg), and so you’ll probably see a number of posts from me about this subject.

I know, I know, cue the “git rawks!”, “Hg is da bomb!”, and “they’re both lame, stick with SVN!” debates… anyways, I’m coming at this from a total newbie’s perspective. In the past I’ve written about TortoiseGit and some of the issues I had, so I decided to skip the GUI and go commando command line only.

The first experiment I’ll try where I pit one DVCS against the other is the “cloning a clone” scenario. In the mercurial tutorial, this is described as the “blessed” way of doing things – see the section Making and Reviewing Changes. Whenever you have an experimental change to make, you should clone your local repo (which is itself a clone of someone else’s) and push changes up to your primary cloned repo when they’re stable. Then push them up to the place you cloned them from later. Also, this is described/required by the Integrator workflow in the Pro Git book, specifically Chapter 5.1 – Distributed workflows.

Cloning a Clone: Hg

In the spirit of brevity, I’ll just stick to a terse command line dump. I’m working on Ubuntu 9.10 with Hg 1.3.1, which is installed via

sudo apt-get install hg

First things first… let’s clone someone’s public repo. Heck, how about hgsubversion?

atto@zues:~$ hg clone http://bitbucket.org/durin42/hgsubversion
destination directory: hgsubversion
requesting all changes
adding changesets
adding manifests
adding file changes
added 608 changesets with 1432 changes to 181 files
updating working directory
144 files updated, 0 files merged, 0 files removed, 0 files unresolved
atto@zues:~$

So far, so good. I now have a clone of hgsubversion. Now, let’s clone the clone.

atto@zues:~$ hg clone hgsubversion my-hgsubversion
updating working directory
144 files updated, 0 files merged, 0 files removed, 0 files unresolved
atto@zues:~$

Yawn. This works so flawlessly, it is actually boring me to sleep.

What happens if I now make conflicting changes in each of the repos, then try to mash those conflicts together into an unholy mess?

Edit setup.py line 42 in both repos, commit, etc.

atto@zues:~/hgsubversion$ hg status
M setup.py
atto@zues:~/hgsubversion$ hg commit -m "Conflict in remote"
atto@zues:~/hgsubversion$ cd ../my-hgsubversion
atto@zues:~/my-hgsubversion$ gvim setup.py (make changes)
atto@zues:~/my-hgsubversion$ hg commit -m "Conflict in clone clone"
atto@zues:~/my-hgsubversion$

Ok, it took longer to type this post than it took to make these changes. Boring so far…

At this point, I have two conflicting changes – one in my clone of hgsubversion, and one in my clone-clone of hg-subversion. Now, a sensible thing to do would be to push changes from the clone-clone up to the clone (pretend it’s an integration branch in SVN speak, or a testing playground of sorts).

atto@zues:~/my-hgsubversion$ hg outgoing
comparing with /home/atto/hgsubversion
searching for changes
changeset:   608:5776ac5c7b12
tag:         tip
user:        Foo Bar
date:        Wed May 12 21:25:09 2010 -0700
summary:     Conflict in clone clone

atto@zues:~/my-hgsubversion$ hg push
pushing to /home/atto/hgsubversion
searching for changes
abort: push creates new remote heads!
(did you forget to merge? use push -f to force)
atto@zues:~/my-hgsubversion$ hg incoming
comparing with /home/atto/hgsubversion
searching for changes
changeset:   608:45a0d7d3e796
tag:         tip
user:        Foo Bar
date:        Wed May 12 21:24:40 2010 -0700
summary:     Conflict in remote
atto@zues:~/my-hgsubversion$ hg pull
pulling from /home/atto/hgsubversion
searching for changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files (+1 heads)
(run 'hg heads' to see heads, 'hg merge' to merge)
atto@zues:~/my-hgsubversion$

So, I tried to push up some changes, but doing so would’ve created a two-headed monster, so Hg told me “did you forget to merge? use push -f to force”. That’s actually pretty friendly, maybe I should do a merge.

atto@zues:~/my-hgsubversion$ hg merge
merging setup.py
QFSFileEngine::open: No file name specified
0 files updated, 1 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)
atto@zues:~/my-hgsubversion$ hg stat
M setup.py
atto@zues:~/my-hgsubversion$ hg diff
diff -r 5776ac5c7b12 setup.py
(cut diff)
atto@zues:~/my-hgsubversion$ hg commit -m "Fix merge issues. Bad developer, no pizza"

The odd message about QFSFileEngine::open: was the point in time where Kdiff3 popped up to help me resolve the merge. No surprises here, the merge went off flawlessly as expected, cool.

Now I can push my changes (dumb as they are) up to the original clone:

atto@zues:~/my-hgsubversion$ hg outgoing
comparing with /home/atto/hgsubversion
searching for changes
changeset:   608:5776ac5c7b12
user:        Foo Bar
date:        Wed May 12 21:25:09 2010 -0700
summary:     Conflict in clone clone

changeset:   610:b333c539af3d
tag:         tip
parent:      608:5776ac5c7b12
parent:      609:45a0d7d3e796
user:        Foo Bar
date:        Wed May 12 21:29:59 2010 -0700
summary:     Fix merge issues. Bad developer, no pizza

atto@zues:~/my-hgsubversion$ hg push
pushing to /home/atto/hgsubversion
searching for changes
adding changesets
adding manifests
adding file changes
added 2 changesets with 2 changes to 1 files
atto@zues:~/my-hgsubversion$

Cool. That just… worked. I don’t think I was confused for even a second, things just worked as I would expect them to. I created a conflict I knew Hg couldn’t automatically resolve, it told me what to do, and it took <5 min total to run the experiment.

How about git?

Cloning a Clone: Git

Here I’m installing git-core and git-svn, git-svn isn’t strictly necessary but if I get time I’d like to experiment with pulling (and hopefully pushing) to an SVN repo. If it works, then I can use git all day long and seamlessly push to SVN when my changes are stable.

sudo apt-get install git-core git-svn

Now, let’s clone a public repo so we can experiment and have a little fun.

atto@zues:~$ git clone http://github.com/schacon/hg-git.git
Initialized empty Git repository in /home/atto/hg-git/.git/
got f5493088320f5587bcbcf701bd82ce6625a50e27
walk f5493088320f5587bcbcf701bd82ce6625a50e27
(cut 200+ lines of debug-type info)
walk 01bddec68dab48693af40968567ce4cff535f269
atto@zues:~$

OK, it’s a little verbose – I mean, come on, do I really need to care about each and every changeset and it’s hash? But hey, at least it worked with no issues.

Now let’s clone the clone…

atto@zues:~$ git clone hg-git my-hg-git
Initialized empty Git repository in /home/atto/my-hg-git/.git
atto@zues:~$ 

Fast forward a bit, I’m gonna skip the part where I make conflicting changes and commit them to the two different clones. BTW, don’t forget to do a “git add” before committing, which is annoying but probably a useful feature somehow.

Now, how do we tell what changes are ready to go out without actually pushing them?

There is no “git outgoing”, but after fishing around a bit I discovered that “git status” has some useful information:

atto@zues:~/my-hg-git$ git status
# On branch master
# Your branch is ahead of 'origin/master' by 1 commit.
#
# Untracked files:
#   (use "git add ..." to include in what will be committed)
#
#	setup.py~
nothing added to commit but untracked files present (use "git add" to track)

Hmmm OK so I’ve got one commit that needs pushing, still not sure which one but I’ll leave that be for now.

Of course, doing a “git push” isn’t smart enough to figure out where to push to or what branch to push… so I finally ended up with

atto@zues:~/my-hg-git$ git push origin master
To /home/atto/hg-git
 ! [rejected]        master -> master (non-fast forward)
error: failed to push some refs to '/home/atto/hg-git'
atto@zues:~/my-hg-git$

Well, this error mesage looks annoyingly familiar… is this git’s unique way of telling me that I need to pull and merge first?

atto@zues:~/my-hg-git$ git pull origin master
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 2), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From /home/atto/hg-git
 * branch            master     -> FETCH_HEAD
Auto-merging setup.py
CONFLICT (content): Merge conflict in setup.py
Automatic merge failed; fix conflicts and then commit the result.
atto@zues:~/my-hg-git$ 

Cool, that did something useful-ish. It didn’t pop up kdiff3, or any editor for that matter – but perhaps that’s a matter of configuration? Anyways, off I go to gvim and resolve the conflict by hand.

atto@zues:~/my-hg-git$ git add setup.py
atto@zues:~/my-hg-git$ git commit -m "Fix for conflict in clone clone"
[master fabff96] Fix for conflict in clone clone

With any luck, now I can finally push my two changesets (the conflict and the conflict fix) to origin master:

atto@zues:~/my-hg-git$ git push origin master
Counting objects: 10, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (6/6), done.
Writing objects: 100% (6/6), 730 bytes, done.
Total 6 (delta 4), reused 0 (delta 0)
Unpacking objects: 100% (6/6), done.
warning: updating the current branch
warning: Updating the currently checked out branch may cause confusion,
warning: as the index and work tree do not reflect changes that are in HEAD.
warning: As a result, you may see the changes you just pushed into it
warning: reverted when you run 'git diff' over there, and you may want
warning: to run 'git reset --hard' before starting to work to recover.
warning:
warning: You can set 'receive.denyCurrentBranch' configuration variable to
warning: 'refuse' in the remote repository to forbid pushing into its
warning: current branch.
warning: To allow pushing into the current branch, you can set it to 'ignore';
warning: but this is not recommended unless you arranged to update its work
warning: tree to match what you pushed in some other way.
warning:
warning: To squelch this message, you can set it to 'warn'.
warning:
warning: Note that the default will change in a future version of git
warning: to refuse updating the current branch unless you have the
warning: configuration variable set to either 'ignore' or 'warn'.
To /home/atto/hg-git
   be73d6a..fabff96  master -> master
atto@zues:~/my-hg-git$

Well, that’s downright confusing, but at the end of the barf message, it looks like something happened. Trotting over to the original clone and checking the log, I can see that I now have three new commits – one in the clone, two in the clone-clone.

So… I think it worked.

Just to be sure, I opened setup.py and checked the line in question… and it’s not been updated.

Well, in SVN and Hg you do an “update” command to bring the working copy into latest state… does that work for git?

atto@zues:~/hg-git$ git update
atto@zues:~/hg-git$ git help
(nothing useful)

Argh. I know this one. It’s somewhere in my brain…. processing…. processing… the lights are on but nobody’s home.

Oh yeah, the log above said something about running “git reset –hard”. That sounds a bit scary, but these are dummy test repos so who cares if I blow them away by accident?

atto@zues:~/hg-git$ git reset --hard
HEAD is now at fabff96 Fix for conflict in clone clone

Oooooooh yeah, now that’s what I’m talking about… suddenly, setup.py has the new, updated, correct information.

Why does that take a 2-page error/warning message?

So, at this point, I’ve successfully cloned a clone, and pushed conflicting changes up from the clone-clone to the clone – just the same as in Hg.

Conclusions

It doesn’t take a genius to see two things right off the bat:

  1. git’s error mesages range from confusing to overly verbose to completely useless
  2. git required more time reading help files and guessing which commands to run

Sure, #2 could be explained quite simply by my lack of understanding, my complete idiot self not reading enough documentation and manuals, me being a typical user. And… guess what? That’s correct.

I didn’t read any documents, manuals, tutorials, or howtos. And I shouldn’t have to, not for simple things like conflict resolution and cloning (aka checkouts in SVN speak).

Either way, git’s apparent lack of useful error messages makes it critically apparent that git wasn’t designed for ease of use or to ease the transition from any other SCM. Git was designed by Linus as a change/patch management tool for serious kernel hackers, and even now, years later… it’s just not easy to switch.

And Hg?

Well, there are other issues with Hg (like HTTPS behind a corporate proxy, for example) that are well known and documented. But for now, as I sit here writing this incredibly long and boring post… Hg just works for me.

I didn’t have to read the manual, I just started typing commands. What little bit I remembered about Hg is floating around from reading the Hg book several months ago, and so I’m starting basically at ground level.

I want to like git… I’ve read of good things about git… but the barriers to entry are significantly higher, such that I’m having a hard time learning how to use it. Trying to teach a team of other developers to use git? Wow, I’m not even sure I want to think about that. My head is spinning just imagining how to explain to someone why they have to “git add” before a “git commit”, much less what “origin” and “master” are and why you have to “git reset -hard” whenever you push.

So I guess that’s the bottom line… as a newbie idiot user, git confuses the heck out of me, and Hg just works.

Best,

Atto

Categories: Uncategorized Tags: , , ,

TortoiseGit – round 2, fight!

April 22, 2010 2 comments

For some odd reason, I’ve gotten my head wrapped around TortoiseGit and hence this follow-up post.

Cloning a public repository successfully is neat… but not exactly useful.

So, for this post I’m going to try a couple of tricky things in rapid succession:

  1. creating conflicting changes in a clone that I have to resolve when I push
  2. create two radically different changesets consistent with two sparring developers

And if I get really creative, maybe I’ll try to clone an SVN repository just for the heck of it.

First things first.

Cloning my local repo was trivial, and not worth detailing. I created two conflicting changes, one in the master and one in the cloned repo. Committed in the clone with no issues, then hit the “push” dialog.

I was expecting some kind of error, or perhaps a merge dialog to pop up – but instead I got the following very confusing error message.

git.exe push “origin” master:master

To prevent you from losing history, non-fast-forward updates were rejected
Merge the remote changes before pushing again. See the ‘Note about
fast-forwards’ section of ‘git push –help’ for details.
To C:\Users\foo\Downloads\hg-git
! [rejected] master -> master (non-fast-forward)
error: failed to push some refs to ‘C:\Users\foo\Downloads\hg-git’

As a complete and total git newbie, I found this error message very confusing. What is a fast-forward update? I actually missed the “merge the remote changes” message the first time through. This dialog is not very useful – why not list the log message of the remote changes that need to be pulled? This would be much more user friendly. At the very least, it should pop up with a button to pull & merge.

Hmmm… what to do next.

The dialog says I need to merge, but I seem to remember reading something about pulling before a merge.

How about TortoiseGit->Pull?

The result is shown below in the following image.

Wow, that is not only confusing, but downright useless.

Unless I completely missed something, there’s no easy “edit conflicts” button that pops up the 3-way merge tool. I actually had to search around for a bit to figure it out. You can either right click on a file and then select TortoiseGit->Edit conflicts, or you can use the “Check for modifications” link to show a list of all changed (and conflicting) files.

OK, so I edited the conflicts, then committed them to my local clone. Now what happens if I push?

git.exe push “origin” master:master

remote: error: refusing to update checked out branch: refs/heads/master
remote: error: By default, updating the current branch in a non-bare repository
remote: error: is denied, because it will make the index and work tree inconsistent
remote: error: with what you pushed, and will require ‘git reset –hard’ to match
remote: error: the work tree to HEAD.
remote: error:
remote: error: You can set ‘receive.denyCurrentBranch’ configuration variable to
remote: error: ‘ignore’ or ‘warn’ in the remote repository to allow pushing into
remote: error: its current branch; however, this is not recommended unless you
remote: error: arranged to update its work tree to match what you pushed in some
remote: error: other way.
remote: error:
remote: error: To squelch this message and still keep the default behaviour, set
remote: error: ‘receive.denyCurrentBranch’ configuration variable to ‘refuse’.
To C:\Users\foo\Downloads\hg-git
! [remote rejected] master -> master (branch is currently checked out)
error: failed to push some refs to ‘C:\Users\foo\Downloads\hg-git’

Wow, this is so lame I’m almost ready to give up on git.

At this point, git is zero for two on the user friendliness scale. So far my googling for the many error messages in this git-barf just turn up more of the same – I can’t push from master -> master on a local repository. Which is completely and totally not helpful, and doesn’t make any logical sense.

It’s getting late, so I’ll post back when I git this figured out (ha ha very punny).

Atto

Categories: Uncategorized Tags: , , ,