I love git
and I use it all the time. For my personal projects, my dotfiles, my documents and even for my home directory. But for my work, I need to use mercurial
, as well as git
(mercurial for mozilla-central
mono-repo and git for other repositories hosted separately in GitHub mostly.) I like mercurial too. But there’s a problem, whenever I switch between those version control systems, I always forget which one I’m using right now. For example, when I switch from git repo to mercurial repo, use git
command instead of hg
. Then I change my command back and run the correct command after getting an error that says this is not a git repository… That was driving me crazy for a while, then I came up with a bash function that will help me not to re-write the whole command and will act like the version control of that repository.1 It’s not perfect and doesn’t solve everything. But it solves most of my issues, so I wanted to write it in case someone else needs it.
By the way, for the background, I’m using g
alias for git
command, and h
alias for hg
command.
Proxy function that acts like it’s git or hg depending on your repository
Let’s see the whole function first:
f() {
if [ -d “.git” ]; then
git “$@“
elif [ -d “.hg” ]; then
hg “$@“
else
if git rev-parse —git-dir > /dev/null 2>&1; then
git “$@“
elif hg root > /dev/null 2>&1; then
hg “$@“
else
echo “Not a git or hg repository!”
fi
fi
}
alias g=“f”
alias h=“f”
As I told you before, I’m using g
and h
, that’s why I aliased this f
function to them at last. So, now, whenever you type a git or h command, that function will take that command as a proxy and then dispatch it to the corresponding version control system. I was using g
and h
commands before this function and that’s why I wanted to keep them and aliased to this. But if you want to, you can remove the aliases and just use f
function itself(or you can rename and use it at your own convenience).
Now if you run g status
in a hg repository. That command will automatically convert it to hg status
and you won’t get any error that says “not a git repository bla bla…”.
Now let’s go through the function and see what’s going on there.
if [ -d “.git” ]; then
git “$@“
elif [ -d “.hg” ]; then
hg “$@“
This is the first part of our function. That will look at your current directory and see if there is .git
or .hg
directories inside. If there is one of them, that means we found our version control system since these are the directories that contain all the information about the repository. But that part will only work if you are in the root directory of your repository. But you may be a few level inside of that root directory. So if we fail to find the .git
or .hg
, we go inside of the else branch:
if git rev-parse —git-dir > /dev/null 2>&1; then
git “$@“
elif hg root > /dev/null 2>&1; then
hg “$@“
else
echo “Not a git or hg repository!”
fi
In the first if condition, we have git rev-parse --git-dir > /dev/null 2>&1
, that command goes through from your current directory to your root directory and tries to find a .git
directory. If it finds one, returns it, if it doesn’t find any, it says “not in a git repository” and fails with an error return code”. Since we have > /dev/null 2>&1
at the end, we won’t see any output during execution, it will pass or fail gracefully. So with that command we will be able to see if we are inside a git repository.
elif
branch works very similar also, it tries to find root hg directory of the current directory. Returns that dir if it finds it, fails if it doesn’t find it.
So that way we manage to find the current repository of that directory. You may be wondering why we have the outer if, since we can also use the inner if as a more general solution and get rid of the outer one. These commands are pretty fast, but checking a directory and seeing if that’s present is much more faster. So I wanted to keep this outer if as fast track, since we mostly run our git/hg commands in the root directory.
But the commands are not the same!
There is another thing. Let’s say that you wrote g show
in hg repository. That will automatically make it a hg show
. But the problem is, there is no show
command in mercurial :(. So hg
will try to run the closest command to that and that is showconfig
. So it will print all the config, but we didn’t want that. We wanted to see the latest revision. At this point, aliases come to the rescue! You can create a show
alias in mercurial to act like hg log -pr
if you want. The simplest alias would be like this in your .hgrc
file:
[alias]
show = log -pr
That’s the simplest solution, But it will fail if we enter h show
because we didn’t put any revision number after that. We can make it a little bit more sophisticated like this:
[alias]
show = !sh -c 'REV=$1; hg log -pr ${REV:-tip};' -
In this alias, we are running a shell command directly with !sh -c
command. Then, we are doing argument defaulting in ${REV:-tip}
. If nothing is passed to the command, it will print hg log -pr tip
, if a revision number is passed it will print hg log -pr <revision>
.
So yeah, I hope you got the idea. My example was about using the git command in hg. But the opposite is also possible with adding an alias to your .gitconfig
file. That way you can create all the commands in git or hg that you use frequently so you won’t have to rewrite everything. Of course, sometimes it’s not possible to create an alias for that specific command you use. But hey, I didn’t say that my solution solves everything :). But it does solve majority of my problems, I hope it does yours as well!
- You may ask about using git-cinnabar for mercurial repositories. I know that git-cinnabar project is also pretty solid and has a great maintainer, but I personally don’t like using third-party tools to interact with my repositories. [return]