#! /bin/sh -e

url=$1
rev=$2
expHash=$3

hashType=$NIX_HASH_ALGO
if test -z "$hashType"; then
    hashType=sha256
fi

if test -z "$url"; then
    echo "syntax: nix-prefetch-git URL [REVISION [EXPECTED-HASH]]" >&2
    exit 1
fi

# If the hash was given, a file with that hash may already be in the
# store.
if test -n "$expHash"; then
    finalPath=$(nix-store --print-fixed-path --recursive "$hashType" "$expHash" git-export)
    if ! nix-store --check-validity "$finalPath" 2> /dev/null; then
        finalPath=
    fi
    hash=$expHash
fi

init_remote(){
    local url=$1;
    git init;
    git remote add origin $url;
}

# Return the reference of an hash if it exists on the remote repository.
ref_from_hash(){
    local hash=$1;
    git ls-remote origin | sed -n "\,$hash\t, { s,\(.*\)\t\(.*\),\2,; p; q}"
}

# Return the hash of a reference if it exists on the remote repository.
hash_from_ref(){
    local ref=$1
    git ls-remote origin | sed -n "\,\t$ref, { s,\(.*\)\t\(.*\),\1,; p; q}"
}

# Fetch everything and checkout the right sha1
checkout_hash(){
    local hash="$1";
    local ref="$2";

    if test -z "$hash"; then
        hash=$(hash_from_ref $ref);
    fi;

    git fetch origin || return 1
    git checkout $hash || return 1
}

# Fetch only a branch/tag and checkout it.
checkout_ref(){
    local hash="$1";
    local ref="$2";

    if test -n "$NIX_PREFETCH_GIT_DEEP_CLONE"; then
	# The caller explicitly asked for a deep clone.  Deep clones
	# allow "git describe" and similar tools to work.  See
	# http://thread.gmane.org/gmane.linux.distributions.nixos/3569
	# for a discussion.
	return 1
    fi

    if test -z "$ref"; then
        ref=$(ref_from_hash $hash);
    fi;

    if test -n "$ref"; then
        # --depth option is ignored on http repository.
        git fetch --depth 1 origin +"$ref" || return 1
        git checkout FETCH_HEAD || return 1
    else
        return 1;
    fi;
}

# Update submodules
init_submodules(){
    # Add urls into .git/config file
    git submodule init

    # list submodule directories and their hashes
    git submodule status |
    while read l; do
        # checkout each submodule
        local hash=$(echo $l | sed 's,^-\([0-9a-f]*\) \(.*\)$,\1,');
        local dir=$(echo $l | sed 's,^-\([0-9a-f]*\) \(.*\)$,\2,');
        local url=$(sed -n "\,$dir, { :loop; n; s,^.*url = ,,; T loop; p; q }" .git/config);

        clone "$dir" "$url" "$hash" "";
    done;
}

clone(){
    local top=$(pwd)
    local dir="$1"
    local url="$2"
    local hash="$3"
    local ref="$4"

    cd $dir;

    # Initialize the repository.
    init_remote "$url";

    # Download data from the repository.
    checkout_ref "$hash" "$ref" ||
    checkout_hash "$hash" "$ref" || (
        echo 1>&2 "Unable to checkout $hash$ref from $url.";
        exit 1;
    )

    # Checkout linked sources.
    init_submodules;

    cd $top;
}

# If we don't know the hash or a path with that hash doesn't exist,
# download the file and add it to the store.
if test -z "$finalPath"; then

    tmpPath=/tmp/git-checkout-tmp-$$
    tmpFile=$tmpPath/git-export
    mkdir $tmpPath $tmpFile

    trap "rm -rf $tmpPath" EXIT

    # Perform the checkout.
    case "$rev" in
        HEAD|refs/*)
            clone "$tmpFile" "$url" "" "$rev" 1>&2;;
        [0-9a-f]*)
            if test -z "$(echo $rev | tr -d 0123456789abcdef)"; then
                clone "$tmpFile" "$url" "$rev" "" 1>&2;
            else
                echo 1>&2 "Bad commit hash or bad reference.";
                exit 1;
            fi;;
        "")
            clone "$tmpFile" "$url" "" "HEAD" 1>&2;;
    esac

    # Allow doing additional processing before .git removal
    eval "$NIX_PREFETCH_GIT_CHECKOUT_HOOK"
    if test "$NIX_PREFETCH_GIT_LEAVE_DOT_GIT" != 1
    then
	echo "removing \`.git'..." >&2
	rm -rf $tmpFile/.git
    fi

    # Compute the hash.
    hash=$(nix-hash --type $hashType $hashFormat $tmpFile)
    if ! test -n "$QUIET"; then echo "hash is $hash" >&2; fi

    # Add the downloaded file to the Nix store.
    finalPath=$(nix-store --add-fixed --recursive "$hashType" $tmpFile)

    if test -n "$expHash" -a "$expHash" != "$hash"; then
        echo "hash mismatch for URL \`$url'"
        exit 1
    fi
fi

if ! test -n "$QUIET"; then echo "path is $finalPath" >&2; fi

echo $hash

if test -n "$PRINT_PATH"; then
    echo $finalPath
fi
