Git push deletes your files on the server?

I've been scratching my head again (feels quite good actually). This time about setting up Git the SCM. I've been lured into trying out Git hoping that it'll help me not only "Branch often", but even better "Merge often". I wanted to have a CVS/SVN-like set-up with a centralish server repository, but with the freedom of Gitting around locally without worrying about breaking the server. It seems to be very cool when it comes to doing a lot of branching and merging, even stashing your temporary changes (very handy if you suddenly have to do some real work). But another cool thing is that the Git repository is very isolated in the sense that it contains all the changes. But it still isn't isolated in the sense that you can copy it (simply copy the files if you want) but even better you can clone it locally or over the net (preferably SSH).
Having experienced some major problems with merging in both CVS and Subversion (SVN) in the past I'm hoping that I can do better with Git.
Let me also explain why I wanted the centralish server á la CVS/SVN; First of all there's the back-up issues. If I have one "official" location for the code I can easily back it up (in my case mirrored disks). The second is that I'm using Capistrano to deploy my code onwards to the Interwebs. This means that Capistrano needs one specific place from where to get the code. Third is obviously the hope that one day there will be more than myself working on this code and a central location for the "official product" is a Good Thing.
I've followed a few different ways of converting your SVN repository to Git, and this one kind of describes it best: Cleanly Migrate Your Subversion Repository To a GIT Repository. I've also watched the Git Peepcode and Scott Chacon's Git With Rails Tutorial, not to mention the SVN to Git Crash Course.
There's however one thing that isn't really clearly spelled out anywhere. It's mentioned here and there, but it's not really totally in your face. I finally found a good explanation, back at the source, the PeepCode mailing lists. The "problem" that I'm talking about is happening because of Git's incredible flexibility. Because every Git-enabled piece of code is a repository on it's own you can use it as a source, and you can stick your changes into it. In Geoffrey's Peepcode he's simply cloning a repository onto the server and uses that as the "remote server". This works well until you go in there with your curious nose to check for changes (as I did). The issue with happens when you "git push" you local changes to the "server" (server used loosely). The command "git push" does not like it when you push to a branch that's checked out (because "git push" only updates the remote refs, not the file system per se). Therefore if you're using the method mentioned in the Peepcode you should consider your files on the filesystem (except the .git/ directory) as obsolete and out of sync. The alternative is to use the --bare option when you "git clone" your Git-enabled-source. The --bare option is basically just the .git/ directory without the "files" we normally work with. This way the files can't get out of sync, and if you , on the server, want to check what the files look like you can simply do a "git clone" somewhere locally to get a "working copy".
Read up on what Geoffrey has to say about it in the One doubt about Git and pushing to the origin-thread in the PeepCode mailing list.
Summary; There's two ways to have your "server"; just a normal Git repository, or one cloned with the help of the --bare switch. The plain vanilla one will have a set of files lying around that'll be obsolete the moment someone does a "git push". The bare nekkid one won't have the files there, so you can't go in there and sneak in a change or just look at the status.
Hope this saves someone the confusion.


Jakub Narebski said...

Actually there are three possible ways to solve "git push 'deleting' files" problem: 1.) make published/public repository you push to bare repository, without working area. 2.) push to remote branches (refs/mothership/* for example), and update branches using pull on server, 3.) use hook to automatically update working area; of course you shouldn't work on server then (see also continuous integration hook in contrib).

J said...

Jakub, you're absolutely right, of course. This Git "feature" is thanks to Git being so incredibly versatile. I opted for option 1) that you describe. Options 2) and 3) are good "work arounds". Feel free to post code-snippets and/or links that performs 2), 3), and I'll include them in the original post (I'm too tired to go looking for them as my problem is solved, and it's 07:22 in the morning :P).
I'm happy with 1) as it prevents me from going into the server and screwing things up. I just wish I would have known about this "quirk" sooner.
Thanks very much, Jakub! Much appreciated! :)