#!/bin/bash
#emacs: -*- mode: shell-script; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil -*-
#ex: set sts=4 ts=4 sw=4 et:
#
# git-bzr
#
# Bidirectional operation with Bazaar repositories. Add remote branches, pull
# from them and push to them using this script.

gitbzr_version='1.2'

function gitbzr_help_header() {
    echo >&2 "git-bzr - Bidirectional operation between Bazaar and git"
}

function gitbzr_git_directory() {
    gitdir=$(git rev-parse --git-dir)
    [ -e "$gitdir" ] || exit 1
    if [ $(uname -s) != "Darwin" ]; then
        gitdir=$(readlink -f $gitdir)
    fi

    echo $gitdir
}

function gitbzr_help_all() {
    gitbzr_help_header
    echo >&2 "Usage: git bzr <command> [arguments]"
    echo >&2
    echo >&2 "Commands:"
    echo >&2
    echo >&2 "  add                         Add a bzr branch as a remote"
    echo >&2 "  fetch                       Fetch from bzr into a named branch"
    echo >&2 "  push                        Push to the tracked bzr branch"
    echo >&2 "  show                        Print location of a tracked bzr branch"
    echo >&2 "  help                        Display help for a specific command"
    echo >&2
    echo >&2 "Arguments are detailed in each command's help message. For more"
    echo >&2 "information, use 'git bzr help <command>'"
    echo >&2
}

function gitbzr_print_version() {
cat << EOT
git-bzr (bash version) $gitbzr_version
Copyright (C) of respective authors (see AUTHORS).
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

EOT
}

function perror() {
    echo "git-bzr: $@" >&2
}

function gitbzr_add_help() {
    gitbzr_help_header
    echo >&2 "Add a remote bzr branch to your git repository"
    echo >&2
    echo >&2 "Usage: git-bzr add <remote> /path/to/bzr/branch"
    echo >&2
    exit 1
}

function gitbzr_add() {
    if [ $# -lt 2 ] ; then
        gitbzr_add_help
    fi
    name="$1"
    location="$2"
    if [[ -z "$@" ]] ; then
        gitbzr_add_help
    fi
    if [ -n "$(git remote show | grep -q \"$name\")" ] ; then
        perror "There is already a remote with that name"
        exit 1
    fi
    if [ -n "$(git config git-bzr.${name}.url)" ] ; then
        perror "There is already a bazaar branch with that name"
        exit 1
    fi
    if ! bzr info -q ${location} > /dev/null 2>&1 ; then
        perror "Remote is not a bazaar repository"
        exit 1
    fi

    git config "git-bzr.${name}.location" "$location"
    echo -e "Bazaar branch $name added.\nYou can fetch it with 'git bzr fetch $name'."
}

function get_location() {
    if [ $# -lt 1 ] ; then
        echo "At least the name of the remote is needed for get_location"
        exit 1
    fi
    remote=$1
    shift
    args=$@
    l="$(git config git-bzr.${remote}.location)"
    if [ -z "$l" ] ; then
        perror "Cannot find bazaar remote with name '${remote}'."
        exit 1
    fi
    return="$l"
}

function gitbzr_show_help() {
    gitbzr_help_header
    echo >&2 "Print location of a tracked bzr branch"
    echo >&2
    echo >&2 "Usage: git bzr show <remote>"
    echo >&2
    exit 1
}

function gitbzr_show() {
    if [ $# -lt 1 ] ; then
        gitbzr_show_help
    fi
    remote=$1
    get_location "$remote"
    echo $return
}

function gitbzr_fetch_help() {
    gitbzr_help_header
    echo >&2 "Fetch from bzr into a named branch. The branch must have been"
    echo >&2 "added with 'git bzr add' first."
    echo >&2
    echo >&2 "Usage: git bzr fetch <remote>"
    echo >&2
    exit 1
}

function gitbzr_fetch() {
    if [ $# -lt 1 ] ; then
        gitbzr_fetch_help
    fi
    gitdir=$(gitbzr_git_directory)
    remote=$1
    shift
    args=$@
    if [ -z "${remote}${args}" ] ; then
        gitbzr_fetch_help
    fi
    get_location "$remote"
    location=$return

    git_map="$gitdir/bzr-git/${remote}-git-map"
    bzr_map="$gitdir/bzr-git/${remote}-bzr-map"

    if [ ! -f "$git_map" -a ! -f "$bzr_map" ] ; then
        echo "There doesn't seem to be an existing refmap. "
        echo "Doing an initial import"
        mkdir -p "$(dirname $git_map)"
        bzr fast-export --export-marks=${bzr_map} \
            --git-branch=${remote} ${location} \
            | git fast-import --export-marks=${git_map}
    elif [ -f "$git_map" -a -f "$bzr_map" ] ; then
        echo "Updating remote ${remote}"
        old_rev="$(git rev-parse ${remote})"
        bzr fast-export --import-marks=${bzr_map} \
            --export-marks=${bzr_map} --git-branch=${remote} ${location} \
            | git fast-import --quiet --export-marks=${git_map} \
            --import-marks=${git_map}
        new_rev="$(git rev-parse ${remote})"
        echo "Changes since last update:"
        git shortlog ${old_rev}..${new_rev}
    else
        perror "One of the mapfiles is missing! Something went wrong!"
        exit 1
    fi
}

function gitbzr_push_help() {
    gitbzr_help_header
    echo >&2 "Push to a tracked bzr branch"
    echo >&2
    echo >&2 "Usage: git bzr push <remote>"
    echo >&2
    exit 1
}

function gitbzr_push() {
    if [ $# -lt 1 ] ; then
        gitbzr_push_help
    fi
    gitdir=$(gitbzr_git_directory)
    remote=$1
    shift
    args=$@

    if [ -z "${remote}${args}" ] ; then
        gitbzr_push_help
    fi
    get_location "$remote"
    location=$return

    if [ -n "$(git rev-list --left-right HEAD...$remote | sed -n '/^>/ p')" ] ; then
        perror "HEAD is not a strict child of {remote}, cannot push. Merge first"
        exit
    fi

    if [ -z "$(git rev-list --left-right HEAD...$remote | sed -n '/^</ p')" ] ; then
        perror "Nothing to push. Commit something first"
        exit
    fi

    git_map="$gitdir/bzr-git/${remote}-git-map"
    bzr_map="$gitdir/bzr-git/${remote}-bzr-map"

    if [ ! -f "$git_map" -o ! -f  "$bzr_map" ] ; then
        perror "We do not have refmapping yet. Then how can I push?"
        exit
    fi

    git fast-export --import-marks=$git_map --export-marks=$git_map HEAD \
        | (cd $location \
        && bzr fast-import --import-marks=$bzr_map --export-marks=$bzr_map -)
}

function gitbzr_help() {
    cmd=$1

    case $cmd in
      add )
        gitbzr_add_help
        ;;
      fetch )
        gitbzr_fetch_help
        ;;
      push )
        gitbzr_push_help
        ;;
      show )
        gitbzr_show_help
        ;;
      * )
        gitbzr_help_all
        exit 1
        ;;
    esac
}

function gitbzr_run() {
    if [ $# -lt 2 ] ; then
        if [ "x$1" = "x--version" ]; then
            gitbzr_print_version
            exit 0
        elif [ "x$1" = "x--help" ]; then
            gitbzr_help_all 2>&1
            exit 0
        elif [ "x$1" = "x--manpage" ]; then
            help2man -N $0
            exit 0
        fi

        gitbzr_help $1
    fi

    cmd=$1
    shift
    args="$@"

    if [ "x$cmd" != "xhelp" ]; then
        git rev-parse 1>/dev/null 2>&1
        if [ $? -ne 0 ] ; then
            perror "Must be inside a git repository to work"
            exit 1
        fi
        up=$(git rev-parse --show-cdup)
        if [ "x$up" == "x" ] ; then
            up="."
        fi
        cd $up
    fi

    case $cmd in
      help )
        gitbzr_help $1
        ;;
      add )
        gitbzr_add $args
        ;;
      fetch )
        gitbzr_fetch $args
        ;;
      push )
        gitbzr_push $args
        ;;
      show )
        gitbzr_show $args
        ;;
      * )
        gitbzr_help
        ;;
    esac
}

return=""

gitbzr_run $@
