I doubt you can do much better than your script unless you write a compiled program that does the equivalent.
Git's test, using git rev-parse
, is coded in C and compiled to a simple binary and hence runs very quickly. Mercurial's is coded in Python, so you pay the Python startup overhead. Moreover, any extensions you load in Mercurial require searching extension paths and additional run-time loading, which makes Mercurial more flexible than Git, but slower.
If hg root
were fast enough, you could just run both hg root
and git rev-parse --show-toplevel
(and then maybe resolve symbolic links if desired) and see which one is closer to the current directory (again with symbolic links resolved), if both succeed. Presumably you already tried this and it was not fast enough.
You can resort to your own path-searching, as you did in the sample code. Instead of just searching for .hg
, however, you could search for both .hg
and .git
, and stop when you find either one. There are many prompt libraries for many shells and several of them use this approach.
All methods are a bit error-prone, especially because it is possible to have a Git repository within a Mercurial repository and vice versa (e.g., /path/to/current/working/dir
might be Mercurial at the current
level and Git at the dir
level, or vice versa). Meanwhile, the presence of a .git
directory is not sufficient to guarantee that you are in a valid Git work-tree. For instance, Git believes a directory is a repository only if:
- It can find the Git directory itself (perhaps using
$GIT_DIR
; Mercurial does not seem to have an environment variable override the way Git does; if no $GIT_DIR
, start at the current directory and climb as needed); and
- the found Git directory contains a file named
HEAD
; and
- the found directory contains a
refs
directory; and
$GIT_OBJECT_DIRECTORY
points to a directory, or the found directory contains an objects/
directory.
The climbing-towards-root search normally tests for, and stops at, mount points, so that if $HOME/foo
is a Git repository, but your current working directory is $HOME/foo/bar
and this is a mount point and this is not a Git repository, Git will not detect $HOME/foo
as a repository. A simple in-shell path search would detect $HOME/foo
as a repository.
Mercurial, by default, just looks for the .hg
directory in each parent directory, so you are on much firmer ground here.
Note that you can even have a single directory that has both .hg
and .git
subdirectories, with both being valid repositories (I have done this for experimental purposes; it works, it is just a bit tricky: you will want to ignore the other VCS's VCS-control files). That's probably not something you need to handle, which is good since there's no right obvious right way to handle it. :-)