Sunday, July 22, 2007

git cvs in 5min

Tired of CVS?  Use git instead.
  1. cvs -d cvs-myproj@cvs.foo.com:/cvs co projectwd
  2. mv projectwd myproj && cd myproj
  3. git-cvsimport -v -C ../git-myproj -a
  4. cd ../git-myproj && git status
This will checkout the CVS repo and convert it in git.  You will need cvsps >= 2.1.
For some reason, when I've done this, I ended up with all my files marked as deleted in my working copy...  If you have the same problem, a simple git reset --hard will undo the deletion.

Now do some work, and commit in your git repo.

It is now time to export these commits in the CVS repo.  You can review your commits with git log origin..master (This assumes that you worked in the master branch which is the case by default.  The origin branch was created by cvsimport and should not be modified unless when you actually sync it with the CVS repo).  Now go back to your CVS working copy:
  1. cd ../myproj
  2. cvs up -d
  3. for i in `GIT_DIR=../git-piscine-j01/.git git-cherry origin work | sed -n 's/^+ //p'`; do echo Exporting $i; GIT_DIR=../git-piscine-j01/.git git-cvsexportcommit -c -p -v $i || break; done
  4. cvs diff -u
  5. git-cvsimport -v -C ../git-myproj -a
  6. cd ../git-myproj
  7. git checkout origin
  8. git merge master
  9. git checkout master
First, make sure your CVS working copy is up to date.  Then we ask git-cherry to give us all the commits that are in master and not in origin, and we pass them to git-cvsexportcommit.  If the patch applies cleanly, the commit will be automatically sent to the CVS server.  It happened to me once that git-cvsexportcommit failed to properly add the new directories I commited in git and I had to checkout another fresh CVS working copy and then it worked.  Weird.  Then (step 4) review your CVS working copy to make sure nothing was left behind.  And then you update your git repository so that it takes into account the new commits you just exported in the CVS repo.  Finally, go back to your git repository and enter the origin branch.  Sync it with your master branch (step 8) and finally switch back to the master branch where you can do some more work and repeat the whole process.

At first I did not expect to have to do steps 7 and 8 but since the sha1 sums generated by the cvsimport for the commits exported by cvsexportcommit are different, these are required so that branches stay in sync.  This extra merge step should however not be of a problem since it should be a simple "fast forward" merge.   The only minor inconvenience is that commits made in git, exported to CVS and imported back to git will appear twice with different sha1 IDs.

This should be enough to work with git instead of CVS.

Wednesday, July 18, 2007

Learning git-svn in 5min

You are a SVN user and you don't have time to learn new things, here is a 5min course to get started with Git and git-svn.
  1. Import your SVN repository in Git:
    git svn clone -s https://svn.foo.com/svn/proj
  2. Make your own Git branch:
    git checkout -b work trunk
  3. git add the files you changed.
  4. git commit
  5. Want to sync with the remote master SVN repos?
    git svn dcommit

There you go!  And guess what, svn-wrapper supports Git!

Some more details now:
  1. The various -s argument is simply here to tell git svn that you use the standard SVN-style layout (trunk/branches/tags).
  2. You must not work in a remote branch.  That's why the 2nd step is to setup a local branch where your work will happen. I checked-out trunk but you could also checkout branch "1.0" or whatever.
  3. This is the only notable difference with SVN for basic usage.  With subversion, you svn add your files once and then SVN will track them automatically.  Git does not track files but content.  I know this might sound weird and hard to digest when you start using Git, but that's how Git is and there are many reasons why things are such.  So for now, just don't forget that you must git add the files you want to commit.  Alternatively, you can use git commit -a to commit all the files.  Be warned though that if you do this, git will schedule all the files for the next commit.  It will remove all files that disappeared in the mean time too.
  4. Don't forget that the commit will be done in your local repository only.
  5. git-svn will push each commit you made in the remote SVN repository.  Each commit will be pushed separately with the log message you gave to Git.
  6. Someone committed in the SVN repository?  Fetch the new revisions with git svn fetch

Now you can read the previous post in order to find some useful references that are worth reading.  You will quickly see that the time you invest learning Git will pay off (for those of you who are very concerned with ROI). git-svn is ironically the best SVN client IMO.

Some questions you might ask:

Where does git store its stuff?
In the single .git folder at the root of the working copy.  Everything is there, you don't have .git folders all over the place like .svn folders.

Where are my branches and stuff?
They are in .git/refs/remotes/, you can easily switch between branches with git checkout.

What about my svn:ignore?
You can import them in git with:
(echo; git-svn show-ignore) >> .git/info/exclude

What about my svn:externals?
Sorry, they are not yet supported by git-svn.  But you're not lost!  Create another git repository with the svn:external repository and put that repository where it's meant to be and checkout the revision that was pinned in the SVN.  Look near the end of .git/svn//unhandled.log, you'll see:
rREVISION1
+dir_prop: trunk svn:externals external_name%20-r%20REVISION2%20URL

This tells you that at REVISION1 in the branch , the svn:externals pinned the revision REVISION2 of URL as external_name.
You need to find the sha1 hash of this revision in git, enter the "external" repository and do:
grep -r rREVISION2 .git
.git/logs/refs/remotes/trunk: [...] rREVISION2

There you go, you can simply issue:
git checkout

Still not convinced by Git?
  • Git is way faster than SVN
  • Git is distributed (you can work offline which is a great advantage for laptop users)
  • Git makes branching and tagging extremely cheap and convenient.
  • Most important: Git makes merging a trivial yet powerful operation.  Subversion is flawed in this respect, merging is a pain, you have to manually track the last revision that was merged, you loose the history, etc.  Git does not have all these disadvantages.  Merging is done right: fast, easy, reliable.  As a bonus, you even get less conflicts.
  • Git has tons of sexy features that SVN will probably never have and SVN users can only dream about.  For instance, Git can instantly tell you where does THIS LINE come from, even if this line moved across different files over years.
  • Git is safe and reliable.  We also use versioning systems because we want to keep a safe backup of all the history of a project.  But servers crash, filesystems get corrupted, we all have this kind of problem. Sometimes malicious people try to fiddle with the history on well-known public servers.  Git checks every single thing it controls with sha1 sums, there is no way you can screw things up without noticing.  This reason is actually good enough that everyone actually carrying about their code should switch to Git right now. With a single 40-bytes sha1 hash, you can make sure that, not only a single revision is OK, but that the entire history, all the files and stuff straight from the beginning until this revision are OK.
  • Git makes it a lot easier for everyone to contribute.  No endless delicate political discussions about commit access, people pull changes from each other, usually from the people they trust.  Everyone maintains their own branches and publish only what they want to publish.
  • Git is highly optimized.  The first thing people usually worry about is "OMG, this is gonna take a hell lot of space if I gotta import the entire history on my local hard drive".  First off, you don't have to import everything if you're wrapping SVN repositories with Git (see the links in my previous post).  Second thing, most of my Git repos are actually smaller than the same working copy in SVN (even though the Git one actually has the entire history it its .git!)  I have an example at hand, a 1340 revision SVN working copy freshly checked out.  It's 7.6MB.  The same working copy in Git but with the entire history of the 1340 revisions (for all branches and tags) is only 6.6MB.

Sunday, July 15, 2007

scm_type git = ++svn;

(Hmm, I need to personnalize this "blog" a lil bit but I'm too lazy)

If you really enjoy SVN, you'll love Git.
I won't go into all the details because there are already plenty of pages explaining how to get started and why Git kicks ass, but here are some useful links (in the suggested order of reading):
I also added (beta) Git-support in SVN-wrapper which is very handy in order to automatically generate template ChangeLog entries.

I strongly encourage anyone working with SCMs on a daily basis to give Git a try, it's really worth it.  Especially because Git is very good at wrapping other SCMs (I only wrapped SVN repositories so far, had some troubles with big CVS repositories, most probably because of cvsps).
Anyways, thanks to the KDE people I met at the aKademy (pics) for sorta convincing me to try Git.

PS: QGit is cool.