util: fix race in makedirs()
Update makedirs() to ignore EEXIST in case someone else has already created the
directory in question. Previously the ensuredirs() function existed, and was
nearly identical to makedirs() except that it fixed this race. Unfortunately
ensuredirs() was only used in 3 places, and most code uses the racy makedirs()
function. This fixes makedirs() to be non-racy, and replaces calls to
ensuredirs() with makedirs().
In particular, mercurial.scmutil.origpath() used the racy makedirs() code,
which could cause failures during "hg update" as it tried to create backup
directories.
This does slightly change the behavior of call sites using ensuredirs():
previously ensuredirs() would throw EEXIST if the path existed but was a
regular file instead of a directory. It did this by explicitly checking
os.path.isdir() after getting EEXIST. The makedirs() code did not do this and
swallowed all EEXIST errors. I kept the makedirs() behavior, since it seemed
preferable to avoid the extra stat call in the common case where this directory
already exists. If the path does happen to be a file, the caller will almost
certainly fail with an ENOTDIR error shortly afterwards anyway. I checked
the 3 existing call sites of ensuredirs(), and this seems to be the case for
them.
Test if logtoprocess correctly captures command-related log calls.
$ hg init
$ cat > $TESTTMP/foocommand.py << EOF
> from mercurial import cmdutil
> from time import sleep
> cmdtable = {}
> command = cmdutil.command(cmdtable)
> @command('foo', [])
> def foo(ui, repo):
> ui.log('foo', 'a message: %(bar)s\n', bar='spam')
> EOF
$ cat >> $HGRCPATH << EOF
> [extensions]
> logtoprocess=
> foocommand=$TESTTMP/foocommand.py
> [logtoprocess]
> command=echo 'logtoprocess command output:';
> echo "\$EVENT";
> echo "\$MSG1";
> echo "\$MSG2"
> commandfinish=echo 'logtoprocess commandfinish output:';
> echo "\$EVENT";
> echo "\$MSG1";
> echo "\$MSG2";
> echo "\$MSG3"
> foo=echo 'logtoprocess foo output:';
> echo "\$EVENT";
> echo "\$MSG1";
> echo "\$OPT_BAR"
> EOF
Running a command triggers both a ui.log('command') and a
ui.log('commandfinish') call. The foo command also uses ui.log.
Use head to ensure we wait for all lines to be produced, and sort to avoid
ordering issues between the various processes we spawn:
$ hg foo | head -n 17 | sort
0
a message: spam
command
commandfinish
foo
foo
foo
foo
foo exited 0 after * seconds (glob)
logtoprocess command output:
logtoprocess commandfinish output:
logtoprocess foo output:
spam