Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fastest way to check if the current directory is under mercurial?

Tags:

zsh

mercurial

I'm trying to speed up my zsh theme prompt and hg checks are a bit slower than git's. That result in a slower prompt to show when using the hg plugin.

For git we use git rev-parse --is-inside-work-tree which is the standard and pretty fast.

For hg I've tried to find a similar command but nothing is as fast:

  • hg --cwd $PWD root
  • hg summary
  • hg root
  • hg -q stat

All these commands work but are not as efficient as git rev-parse --is-inside-work-tree.

Also tried this little script which is the best one currently. Anything I can do to speed this script even more?

is_dir_hg() {
  local root="$(pwd -P)"
  while [[ $root && ! -d $root/.hg ]]
  do
    root="${root%/*}"
  done

  echo "$root"
}

Note that the solution must detect hg directory even when we're not in the root directory.

like image 949
dguay Avatar asked Jul 01 '17 19:07

dguay


2 Answers

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. :-)

like image 100
torek Avatar answered Nov 08 '22 00:11

torek


I’d use hg id, which sets the return code usefully as well. At least it’s fast to type :-)

like image 34
peak Avatar answered Nov 08 '22 00:11

peak