Bare Git repositories
We started a new project at Resolver today -- I'm pretty excited about it, and will be blogging about it soon. However, in the meantime, here's something that's half a note-to-self and half something to help people googling for help with Git problems.
We've previously been using Subversion as our main source code control system, but for more recent projects we've moved to Mercurial. When we started the new one today, we decided to try out Git for a change; I use GitHub for my personal stuff, but hadn't used it for anything involving multiple developers -- and various people had been telling us that it wasn't subject to some of the problems we'd had with Mercurial.
So we created a new Git repo on a shared directory, by creating a directory and
then running git init in it. We then cloned it into a working directory on my
machine, and started work. After a while, we had our first checkin ready, so we a
dded the files, committed them, and then decided to push to the central repo to
make sure everything worked OK. We got this error message:
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'.
It took us a while to work out precisely what this meant, because we'd never heard of "bare" repositories before. It turns out that there are two kinds of repository in Git: bare and non-bare. A non-bare repository is the same as the ones we were used to in Mercurial; it has a bunch of working files, and a directory containing the version control information. A bare repository, by contrast, just contains the version control information -- no working files.
Now, you can (in theory) push and pull between repositories regardless of whether they are bare or not. But if you were to push to a non-bare repository, it would cause problems. Part of the SCC data that Git keeps is an index, which basically tells it what the head of the current branch looks like. Now, if you push to a non-bare repository, Git will look at the working files, compare them to the index, and see that they differ -- so it will think that the working files have changed! For example, if your push added a new file, it would notice that the working directory didn't include that file, and would conclude that it had been deleted. There's a step-by-step example here.
You can see how that could be confusing. So bare repositories exist as a way of having central repositories that a number of people can push to. If you want to transfer changes from a non-bare repository to another, the correct way is to pull from the destination rather than push from the target -- which makes some kind of sense when you think about it. In general, any repository that someone is working on is not something that should be receiving changes without their approval... on the other hand, we've not encountered problems with pushing to regular repositories with Mercurial.
Anyway, this was our first checkin, so we had no history to lose, we fixed the
problem by creating a new central repository using git --bare init in a new
directory on the shared drive, cloning it to a new working repo, copying our files
over from the old working repo to the new one, committing, and pushing back to
the bare repository. It worked just fine. If we'd done multiple checkins before
we tried our first push, we could have saved things by hand-editing the central
repository; it had no working files (because we'd only just created it) so we
could have moved the contents of the .git directory up to the repository's root,
and deleted .git -- this would have "bared" it so that we could have pushed
from our working repo. That would have been a bit scary, though.
An odd crontab problem
This took a little while to work out, so it's worth sharing here just in case anyone else has the same problems and is googling for solutions. We had a problem on one of our web servers at Resolver which manifested itself in some (but not all) cron jobs being run twice, which was causing all kinds of problems. Here's how we tracked it down and solved it.
Fix for pygame/PyOpenGL/NeHe tutorial windows not disappearing when run from IDLE
It's a long weekend here in the UK and I thought I'd spend some time working through Paul Furber's Python translations of the well-known NeHe OpenGL tutorial, which use the pygame and PyOpenGL libraries. (This is all with Python 2.6 on Windows XP.)
I noticed that when I ran the sample files from IDLE, the windows did not close -- it didn't matter whether I used the close box or hit escape; the program would seem to exit, but IDLE was in a messy state, and the OpenGL window would sit there not repainting.
Googling didn't turn up anything that sounded relevant, but this archived mailing list message
mentioned a pygame.quit() function that sounded relevant. I tried putting this
at the end of each of the samples, and it seems to fix the problem.
Workaround for Vista stupidity
When I run certain command-line tools from a command prompt in Windows Vista, it displays the results in a separate window. This separate window disappears when the tool exits. This is the most mind-bogglingly stupid behaviour I have encountered so far in an operating system famed for its mind-boggling stupidity. However, there is a workaround -- you need to start a shell as the Administrator user (not just as an Administrator).
Here's some more detail:
- If you are not the Administrator user (even if you are a member of the Administrators group) then when you run a command-line tool that requires admin privileges, you need to click on one of Vista's never-ending stream of "this program wants access to your computer" messages. This is pretty sensible, annoying though it can be.
- It then opens a new command-line window in which you can interact with the program. I can see no value in this whatsoever; the window is not highlighted in any way, so it's not to make it clear to you that this is a potentially dangerous program. Potential reasons welcome in the comments.
- The real stupidity, however, shows itself when the program exits. Because then
the window closes -- taking with it all of the information the tool displayed
for you.
foo /helpsuddenly becomes totally useless. Error messages? Forget about them. - And to make it worse, if you try to redirect the standard output or error of the program to a file or to more or anything else, you get nothing -- it still goes into the disappearing window.
After spending quite literally hours trying to debug a problem with the Python
easy_install script, which was quite sensibly logging the details of the problem
into a window that Vista promptly closed, I discovered a workaround:
H:\>runas /user:Administrator cmd
Enter the password for Administrator:
Attempting to start cmd as user "DRX\Administrator" ...
H:\>
And up comes a new command prompt. Anything you run in there will put the standard output and error into the command line it was started from, just as any sane user would have expected in the first place.
MSBuild WTF: 'The error was:'
Here's a fun one for anyone who uses msbuild (at least, v2.0.50727). Create a project file like this:
<Project DefaultTargets="Foo" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Foo">
<Exec Command = "echo Hello!" />
</Target>
</Project>
From a command prompt, run the project; you will get a the effect you would expect.
Now replace the word "Hello" with "The error was: something random". Run it again.
C:\\Dev\\Resolver>msbuild foo.proj
Microsoft (R) Build Engine Version 2.0.50727.42
[Microsoft .NET Framework, Version 2.0.50727.42]
Copyright (C) Microsoft Corporation 2005. All rights reserved.
Build started 15/11/2006 17:50:22.
__________________________________________________
Project "C:\\Dev\\Resolver\\foo.proj" (default targets):
Target Foo:
echo The error was: something random
EXEC : The error was: something random
C:\\Dev\\Resolver\\foo.proj(3,9): error MSB3073: The command "echo The error was: something random" exited with code -1.
Done building target "Foo" in project "foo.proj" -- FAILED.
Done building project "foo.proj" -- FAILED.
Build FAILED.
EXEC : The error was:
C:\\Dev\\Resolver\\foo.proj(3,9): error MSB3073: The command "echo The error was: something random" exited with code -1.
0 Warning(s)
2 Error(s)
Time Elapsed 00:00:00.09
C:\\Dev\\Resolver>
Fuzzyman and I bumped into this one at work today; our continuous integration server, which watches our Subversion repository and checks out, builds, and tests any code changes it sees, had reported a failure despite the fact that none of the tests had failed. It turned out that one test was quite innocently printing out the text "The error was: " followed by some logging information; it wasn't an error at all. As far as I can tell, the statement that the echo command exited with code -1 is absolute nonsense.
This behaviour is not documented anywhere that we were able to find; I can only assume it was added for some specific purpose in the context of Visual Studio...