#!/bin/sh

set -e

get_gitdir() {
    if [ -n "${GIT_DIR}" ]; then
	echo "error: environment variable GIT_DIR must not be set"
	exit 1
    fi
    GITDIR="$(git rev-parse --git-dir)"
}

git_current_branch() {
    local ref
    ref=$(git symbolic-ref -q HEAD) && echo "${ref#refs/heads/}"
}

check_hg_fast_export() {
    # Search for hg-fast-export $PATH, use if available, if not fall back
    # to looking around for it in sibling directory of bin directory of
    # the current exeuctable, possibly tracing back along symbolic link.
    if which hg-fast-export >/dev/null 2>&1; then
	HG_FAST_EXPORT=hg-fast-export
    else
	    echo "error: hg-fast-export executable not found"
	    echo 'install hg-fast-export executable in directory on $PATH.'
	    exit 1
    fi
}

git_hg_clone() {
    check_hg_fast_export

    if [ "$1" = "--force" ]; then
        FORCE="--force"
        shift
    fi
    HG_REMOTE=$1
    
    if [ $# -lt 2 ]; then
        CHECKOUT=$(basename "${1%#*}")
    else
        CHECKOUT=$2
    fi
    if [ -e "$CHECKOUT" ] && [ -n "$(ls -A "$CHECKOUT")" ]; then
	echo "error: $CHECKOUT exists"
	exit 1
    fi
    git init "$CHECKOUT"
    (
	cd "$CHECKOUT"
	get_gitdir
	hg clone -U "$HG_REMOTE" "${GITDIR}/hgcheckout"
	git init --bare "${GITDIR}/hgremote"
	(
	    cd "${GITDIR}/hgremote"
	    $HG_FAST_EXPORT -r ../hgcheckout --quiet $FORCE
	)
	git remote add hg "${GITDIR}/hgremote"
	git fetch hg
        if git rev-parse --verify -q remotes/hg/master > /dev/null; then
	    local branch="master"
        else
            local branch=$(cd "${GITDIR}/hgcheckout/" && hg tip | grep branch | awk '{print $2}')
        fi
        git config hg.tracking.master "$branch"
        git pull hg "$branch"
    )
}

git_hg_fetch() {
    check_hg_fast_export
    if [ "$1" = "--force" ]; then
        FORCE="--force"
        shift
    fi
    get_gitdir
    hg -R "${GITDIR}/hgcheckout" pull
    (
	cd "${GITDIR}/hgremote"
	"$HG_FAST_EXPORT" -r ../hgcheckout $FORCE
    )
    git fetch hg
}

git_hg_pull() {
    while [ $# -gt 0 ]; do
        case "$1" in
        --rebase)
            REBASE="--rebase"
            ;;
        --force)
            FORCE="--force"
            ;;
        esac
	shift
    done

    git_hg_fetch $FORCE

    local current_branch remote_branch

    if ! current_branch=$(git_current_branch); then
	    echo "ERROR: You are not currently on a branch."
	    exit 1
    fi

    if [ "$current_branch" = "master" ]; then
        remote_branch=$(git config hg.tracking.master || true)

        if [ -z "$remote_branch" ]; then
            if git rev-parse --verify -q remotes/hg/master > /dev/null; then
                remote_branch="master"
                git config hg.tracking.master master
            else
                echo "ERROR: Cannot determine remote branch. There is no remote branch called master, and hg.tracking.master not set. Merge the desired branch manually."
                exit 1
            fi
        fi
    else
        remote_branch=$(git config "hg.tracking.$current_branch" || true)
        if [ -z "$remote_branch" ]; then
             echo "ERROR: Cannot determine the remote branch to pull from. Run git merge manually against the desired remote branch."
             echo "Alternatively, set hg.tracking.$current_branch to the name of the branch in hg the current branch should track"
             exit 1
        fi
    fi

    if [ "$REBASE" = "--rebase" ]; then
        git rebase "hg/$remote_branch"
    else
        git merge "hg/$remote_branch"
    fi
}

git_hg_checkout() {
    if [ "$1" = "--force" ]; then
        FORCE="--force"
        shift
    fi
    git_hg_fetch $FORCE
    git checkout "hg/$1" -b "$1"
}

git_hg_push() {
    HG_REPO=$1
    get_gitdir
    hg --config extensions.convert= convert . "${GITDIR}/hgcheckout"
    hg -R "${GITDIR}/hgcheckout" push "$HG_REPO"
}

usage() {
    echo "To clone a mercurial repo run:"
    echo "  clone <path/to/mercurial/repo> [local_checkout_path]"
    echo ""
    echo " if that fails (due to unnamed heads) try:"
    echo "  git-hg clone --force <path/to/mercurial/repo> [local_checkout_path]"
    echo ""
    echo "To work with a cloned mercurial repo use: "
    echo "  fetch [ --force ]                   fetch latest branches from mercurial"
    echo "  pull [ --force ] [ --rebase ]       fetch and merge (or rebase) into the"
    echo "                                      current branch"
    echo "  push [destination]                  push latest changes to mercurial"
    echo "  checkout [ --force ] branch_name    checkout a mercurial branch"
}

FORCE=
REBASE=
CMD=$1
shift
case "$CMD" in
    clone|fetch|pull|checkout|push)
	git_hg_$CMD "$@"
	;;
    *)
	usage
	exit 1
	;;
esac
