Mercurial

From DreamHost
Jump to: navigation, search
The instructions provided in this article or section are considered advanced.

You are expected to be knowledgeable in the UNIX shell.
Support for these instructions is not available from DreamHost tech support.
Server changes may cause this to break. Be prepared to troubleshoot this yourself if this happens.
We seriously aren't kidding about this.

The instructions provided in this article or section require shell access unless otherwise stated.

You can use the PuTTY client on Windows, or SSH on UNIX and UNIX-like systems such as Linux or Mac OS X.
Your account must be configured for shell access in the Control Panel.
More information may be available on the article's talk page.



Mercurial (official site) is a distributed version control system that is used track revisions of your files. You can find the unofficial manual here: (Distributed Revision Control with Mercurial).

Mercurial, git, and subversion are apparently the most popular version control systems used by DreamHost customers.

This page will document how to install the latest version of Mercurial in your home account and get the webserver up and running. This is a work in progress.

(Optional) Install an Updated Mercurial Into Your Home Directory

Type "hg -v" at a server shell prompt.

The DreamHost people have already installed Mercurial 1.6.x on most (all?) of their servers, so server should reply with something like "Mercurial Distributed SCM" -- so skip to Configuration.

If the server replies with something like "-bash: hg: command not found", follow the following instructions to install the latest version of Mercurial in your home account:

  • Note: This script should work with no problems. If you have any issues, please make a note and we can try and get them fixed. Last updated: March 2014.
#!/bin/bash

# This script should install the latest Mercurial onto a DreamHost
# account.  It will only run if ~/lib/python* does not already exist.
if [[ -d ~/lib/python || -d ~/lib/python2.6 ]]; then
    echo
    echo "Script does not support installing when"
    echo "~/lib/python* already exists.  Exiting."
    exit 1
fi

echo -n "[+] Determining latest mercurial...  "
HG_URL="`curl -s "http://mercurial.selenic.com/sources.js" | \
    grep "source release" | \
    sed "s/.*\\\"\\(http:\\/\\/[^\\\"]\\+\\)\\\".*/\1/g"`"
HG_FILENAME="${HG_URL##*/}"
if [[ -z ${HG_FILENAME} ]]; then
    echo "Failed, please use failsafes in script.  Exiting."
    exit 1
fi

# Use these "failsafes" if the above lines do not work
#LATEST_HG="2.9.1"
#HG_FILENAME="mercurial-${LATEST_HG}.tar.gz"
#HG_URL="http://mercurial.selenic.com/release/${HG_FILENAME}"
HG_BASE=${HG_FILENAME%.tar.gz}
LATEST_HG=${HG_BASE#mercurial-}
echo ${LATEST_HG}

# Prepare your home directory and environment
echo "[+] Preparing home directory... "
export PYTHONPATH=~/lib/python2.6/site-packages
export PATH=~/bin:${PATH}
export HGRCPATH=~/.hgrc
pushd ~ &>/dev/null                    || exit 1
mkdir -p ~/bin                         || exit 1
mkdir -p ~/lib/python                  || exit 1
mkdir -p ~/lib/python2.6/site-packages || exit 1

cat >> ~/.bashrc << _EOF
export PYTHONPATH=~/lib/python
export PATH=~/bin:\${PATH}
export HGRCPATH=~/.hgrc
_EOF


if [[ -f ${HOME}/.bash_profile ]]; then
    PROFILE_FILE="${HOME}/.bash_profile"
elif [[ -f ${HOME}/.bash_login ]]; then
    PROFILE_FILE="${HOME}/.bash_login"
elif [[ -f ${HOME}/.profile ]]; then
    PROFILE_FILE="${HOME}/.profile"
else
    PROFILE_FILE="${HOME}/.bash_profile"
fi
cat >> "${PROFILE_FILE}" << _EOF
export PYTHONPATH=~/lib/python
export PATH=~/bin:\${PATH}
export HGRCPATH=~/.hgrc
_EOF

# Install Python docutils
echo "[+] Installing required Python utils..."
easy_install --prefix="~" docutils &>/dev/null
if [[ $? -ne 0 ]]; then
    echo "Failed, exiting."
    exit 1
fi

# Retrieve and extract the latest mercurial
echo "[+] Downloading mercurial... "
wget -q ${HG_URL}
if [[ ! -f mercurial-${LATEST_HG}.tar.gz ]]; then
    echo "Failed to get mercurial sources, exiting"
    exit 1
fi

# Extract
echo "[+] Extracting  mercurial..."
tar zxf ./mercurial-${LATEST_HG}.tar.gz || exit 1

# Go into extracted mercurial directory
pushd mercurial-${LATEST_HG} &>/dev/null || exit 1

# Build local copy of mercurial
echo "[+] Building    mercurial..."
make local &>/dev/null
if [[ $? -ne 0 ]]; then
    echo "Failed to build, exiting"
    exit 1
fi

# Install local copy of mercurial
echo "[+] Installing  mercurial..."
make install-home &>/dev/null
if [[ $? -ne 0 ]]; then
    echo "Failed to install, exiting"
    exit 1
fi

# Change back into home directory
popd &>/dev/null || exit 1

# Relocate local PYTHONPATH
mv ~/lib/python2.6/site-packages ~/lib/python/ || exit 1
rmdir lib/python2.6 || exit 1
echo "[+] Successfully installed!"
popd &>/dev/null || exit 1
exit 0


Configure Your DreamHost Account to Serve Mercurial Repositories

  • Note: These instructions are based on the instructions listed here ([1])
  • Make sure you change $MY_DOMAIN_DIR below!
#!/bin/bash

# This script should initialize a Mercurial repository in DreamHost account.
HG_DIR=~/hg
PYTHON_LIB=~/lib/python
MY_DOMAIN_DIR=~/somedomain.com

# Ensure existence of readlink
readlink --help 2>/dev/null | grep -q -- "--canonicalize "
if [[ $? -ne 0 ]]; then
    echo
    echo "Script requires \`readlink\` and "
    echo "\"--canonicalize-missing\" support.  Exiting."
    exit 1
fi

# Re-source .bashrc in case the companion script was *just* run
. ~/.bashrc

# Enter home directory
pushd ~ &>/dev/null || exit 1
HGWEB_CGI=`echo mercurial-*/hgweb.cgi`
if [[ -f ${HGWEB_CGI} ]]; then
    HG_BASE=${HGWEB_CGI%/hgweb.cgi}
    LATEST_HG=${HG_BASE#mercurial-}
else
    echo -n "[+] Retrieving latest mercurial...  "
    HG_URL="`curl -s "http://mercurial.selenic.com/sources.js" | \
        grep "source release" | \
        sed "s/.*\\\"\\(http:\\/\\/[^\\\"]\\+\\)\\\".*/\1/g"`"
    HG_FILENAME="${HG_URL##*/}"
    if [[ -z ${HG_FILENAME} ]]; then
        echo "Failed, please use failsafes in script.  Exiting."
        exit 1
    fi

    # Use these "failsafes" if the above lines do not work
    #LATEST_HG="2.9.1"
    #HG_FILENAME="mercurial-${LATEST_HG}.tar.gz"
    #HG_URL="http://mercurial.selenic.com/release/${HG_FILENAME}"
    HG_BASE=${HG_FILENAME%.tar.gz}
    LATEST_HG=${HG_BASE#mercurial-}
    echo ${LATEST_HG}

    # Download
    wget -q ${HG_URL}
    if [[ ! -f mercurial-${LATEST_HG}.tar.gz ]]; then
        echo "Failed to get mercurial sources, exiting"
        exit 1
    fi

    # Extract
    echo "[+] Extracting  mercurial..."
    tar zxf ./mercurial-${LATEST_HG}.tar.gz || exit 1
fi

echo "[+] Preparing local repository..."
HG_DIR="`readlink --canonicalize-missing \"/${HG_DIR}\"`"
mkdir -p "${HG_DIR}/repos" || exit 1

HGWEB_CONF="${HG_DIR}/hgweb.conf"
cat > "${HGWEB_CONF}" << _EOF
[collections]
repos/ = repos/
[web]
#style = gitweb
style = paper
_EOF

cp "${HG_BASE}/hgweb.cgi" "${HG_DIR}/" || exit 1
sed -i "s/^config = .*/config = \\\"${HGWEB_CONF//\//\/}\\\"/" \
    "${HG_DIR}/hgweb.cgi"
NEW_PY_SYS_STR="import sys; sys.path.insert(0, \\\"${PYTHON_LIB//\//\/}\\\")/"
sed -i "s/^#import sys.*/${NEW_PY_SYS_STR}" "${HG_DIR}/hgweb.cgi"

cat > "${HG_DIR}/.htaccess" << _EOF
# Taken from http://www.pmwiki.org/wiki/Cookbook/CleanUrls#samedir
# Used at http://ggap.sf.net/hg/
Options +ExecCGI
RewriteEngine On
#write base depending on where the base url lives
RewriteBase /hg
RewriteRule ^$ hgweb.cgi  [L]
# Send requests for files that exist to those files.
RewriteCond %{REQUEST_FILENAME} !-f
# Send requests for directories that exist to those directories.
RewriteCond %{REQUEST_FILENAME} !-d
# Send requests to hgweb.cgi, appending the rest of url.
RewriteRule (.*) hgweb.cgi/$1  [QSA,L]
_EOF

# Fix up permissions
chmod a+x "${HG_DIR}/hgweb.cgi" || exit 1
chmod go+x "${HG_DIR}"          || exit 1
pushd "${HG_DIR}" &>/dev/null   || exit 1
chmod go+r .htaccess            || exit 1
chmod go+rx hgweb.cgi           || exit 1
chmod go+r hgweb.conf           || exit 1
chmod go+x repos/               || exit 1
popd &>/dev/null                || exit 1

# Create symlink in the web directory
pushd "${MY_DOMAIN_DIR}" &>/dev/null || exit 1
ln -s "${HG_DIR}" ./ &>/dev/null
popd &>/dev/null || exit 1
popd &>/dev/null || exit 1

MY_DOMAIN="${MY_DOMAIN_DIR##*/}"
echo -n "[+] Your mercurial repositories are now hosted at "
echo "http://${MY_DOMAIN}/hg"


Create a New Repository

  • Login to your shell account
  • Create a repository called my_first_repo
hg init ~/hg/repos/my_first_repo
  • Create a hgrc file for the repository
vi ~/hg/repos/my_first_repo/.hg/hgrc
  • ...add the following lines to this file
[web]
contact = <Your Name>
description = My first Mercurial Repository
push_ssl = false
 
[ui]
username = Firstname Lastname <youremailaddress@hostname.com>
  • If you happen to need accents and/or tildes (áéíóú), also add to the [web] part:
charsets = utf-8
  • Test your repository on DreamHost. First, make a file in your repository.
cd ~/hg/repos/my_first_repo
date >> my_first_file
hg add
hg commit -m "Added my_first_file to the repository"

On your home system, try to clone your repository.

hg clone http://<your-domain>/hg/repos/my_first_repo local_repo_copy
cat local_repo_copy/my_first_file
  • By default, no one is allowed to push your repository. Read the next section, and review the Mercurial documentation on publishing repositories.
  • If you want to share the access via SSH and not HTTP, skip the next section, the following one should have all the answers you need. The only thing you could need (if you want of course) is to restrict access by adding the lines to the .htaccess on the beginning of the next section. Everything after that (inside that section) has to do with http authentication.

Securing your Repositories

'This hasn't been updated since a while, so I don't know if it's actually working as of 1.6.3 --Pablo Olmos de Aguilera Corradini 22:55, 30 September 2010 (UTC)

  • Login to your shell account
  • Add this to the .htaccess file for the hg directory
AuthUserFile /home/<username>/hg/hgweb.passwd
AuthGroupFile /dev/null
AuthName "My Mercurial Repository"
AuthType Basic
    <Limit GET POST PUT> # Remove GET to allow anonymous read-only access
        Require valid-user
    </Limit>
  • Create hgweb.passwd
htpasswd -c -m ~/hg/hgweb.passwd <first-user>
htpasswd -m ~/hg/hgweb.passwd <second-user>
...
  • Set up user names in the repo hgrc
vi ~/hg/repos/my_first_repo/.hg/hgrc
  • ...add the following line:
allow_push = <comma-delimited username list>
  • "Trick" Mercurial into asking for authentication:
touch ~/hg/failed_auth.html
  • You can avoid having to type in your user name and password each time by providing it when you clone the repository locally. If you did not clone the repository with a user name and password you can also update the .hg/hgrc of a local repository by updating the [paths] section.
hg clone http://<user>:<password>@<your-domain.com>/hg <localdir>

Sharing access to the repositories through SSH

I installed Mercurial 2.0 from source, and am only sharing the repository with myself. If this situation fits you there's no need to mess around with hg-ssh. You can just use your ssh key and change the remotecmd on client to point to the absolute path of your hg installation. For example, I compiled mercurial-2.0 from source per the earlier instructions and set up my remotecmd on TortoiseHg to be "~/bin/hg". This works for push / pull now. Example command line would be:

hg clone --verbose --debug ssh://<user>@<host>/<path-to-repo-relative-to-home-dir> --remotecmd "~/bin/hg"

I spent a lot of time tracking this issue down, since my non-interactive ssh would not load my .bash_profile or .bashrc and kept referencing the system wide 0.9.1 version of Mercurial. By specifying the absolute path to my Mercurial install I was able to pull and push successfully.

Thanks to Ry4an and mpm1 at #mercurial on irc.freenode.net for pointing me in this direction. --Karlgrz 15:27, 7 November 2011 (PST)

I haven't tested this for 1.6.3, I'll updated as soon as I do it. The file still exists and (I think) it's the same so this should work if you change the directory name. --Pablo Olmos de Aguilera Corradini 23:06, 30 September 2010 (UTC)

There are at least three ways to do this according to the Mercurial Wiki:

  • Mercurial Server (Not the mercurial server, it's a piece of software that let's you configure your account to share the repository access with SSH)
  • hg-ssh
  • Hg-login

In this how-to I'm gonna use hg-ssh. It's the simplest way, but also the less sophisticated. Hg-login should work too, but I don't know why I couldn't make it work.

Be advised that this might be a not secure way and I have no doubt that should have many flaws, anyway I found it the most comfortable way since you don't have to send passwords in clear text through http. It could be a good idea to create a new user to just host the repositories so in the worst of the cases, someone could break in your repos and not your whole user directory.

Ok, let's get started:

  • Login to your shell account
  • Copy the hg-ssh script from the source to your bin directory
cp ~/srcs/mercurial-1.5.4/contrib/hg-ssh ~/bin
  • Get the public ssh key of the people you want to have access to the Shared SSH (you can check the SSH Dreamhost Wiki instructions about Passwordless Login to achieve that
  • Edit authorized_keys file to add hg-ssh so the collaborators can have only access through mercurial and no access to the shell
vim ~/.ssh/authorized_keys
  • Before the previously added public keys add, but on the same line:
#So it will go from something like this:
ssh-rsa [LONG STRING]== Collaborator1
#to: 
command="hg-ssh /home/username/hg/path/to/repo",no-port-forwarding,no-X11-forwarding,no-agent-forwarding ssh-rsa [LONG STRING]== Collaborator1
  • Supposedly it should work with a line break between ssh-rsa and the key itself...:
command="hg-ssh /home/username/hg/path/to/repo",no-port-forwarding,no-X11-forwarding,no-agent-forwarding ssh-rsa
[LONG STRING]== Collaborator1
  • ...but it didn't work for me
  • Now you just have to use the ssh:// protocol. Even if to access via web you don't have to write repos, you have to write it down or the command will fail
hg clone ssh://<dreamhost-user-where-you-installed-mercurial>@domain.com/hg/repos/repo-name

Well, that's it. Remember though that this method doesn't allow advanced permissions like give only read access or just pull, but not push changes into the repository. Probably Hg-login can.

How to Configure a Dedicated Subdomain with Cleaner URLS

This section (not the following) doesn't work in 1.6.3 since those lines doesn't exist in hgweb.cgi --Pablo Olmos de Aguilera Corradini 23:02, 30 September 2010 (UTC)

The #import os line doesn't exists, but you can the needed code after the import sys;... line --Miquelfire 14:14, 1 October 2010 (UTC)

This step can be done after the previously listed steps, and should be done after having verified all other aspects are working

If your domain directory is one you have made explicitly for serving mercurial at the root level (e.g. something like http://hg.domain.com/some_repo), then you can do this by simply moving the .htaccess to your root public web directory (since it will rewrite to /hg/hgwebdir.cgi from root level, which is valid due to the ~/hg symlink):

cd to your domain directory
mv ~/hg/.htaccess ./
  • Now we make some modifications in hgwebdir.cgi:
vi ~/hg/hgwebdir.cgi
  • Modify the following lines to override the script name (here I have given it a blank name so that nothing extra is prepended in links)
>Change this:
 #import os
>to:
 import os
 # Force script name - needs webserver rewrite support
 os.environ["SCRIPT_NAME"] = ""
  • Now you should be able to navigate projects with a format like http://hg.domain.com/some_repo instead of the existing format: http://hg.domain.com/hg/hgwebdir.cgi/some_repo

Delete (only) the hgwebdir.cgi in URL

If you are using a subdomain like http://hg.domain.com/hg/hgweb.cgi/some_repo and you just want to delete the annoying hgweb.cgi from the URL, but for some reason you want to keep the /hg:

  • Don't move the .htaccess from /hg to the root folder
  • Modify the hgweb.config:
vim ~/hg/hgweb.config
  • Add below the [web] section the following:
baseurl = /hg
  • So it will look something like this:
[collections]
repos/ = repos/
[web]
style = gitweb
baseurl = /hg
  • Now you should be able to see the projects in a format like http://hg.domain.com/hg/some_repo