#! /bin/sh # # usage: fetchgit git_rev git_url out_link # # Clone the specified Git repository and make it available as out_link. # set -euf git_rev=$1 git_url=$2 out_link=$3 # Put all bases in the same place as out_link. # Notice how out_link must not clash with cache_dir and work_dir. cache_base=$(dirname "$out_link") work_base=$(dirname "$out_link") # cache_dir points to a (maybe non-existent) directory, where a shared cache of # the repository should be maintained. The shared cache is used to create # multiple working trees of the repository. cache_dir=$cache_base/$(echo "$git_url" | urlencode) # work_dir points to a (maybe non-existent) directory, where a specific # revision of the repository is checked out. work_dir=$work_base/$(echo "$git_rev" | urlencode) cache_git() { git --git-dir="$cache_dir" "$@" } work_git() { git -C "$work_dir" "$@" } is_up_to_date() { test -d "$cache_dir" && test -d "$work_dir" && test "$(cache_git rev-parse "$git_rev")" = "$git_rev" && test "$(work_git rev-parse HEAD)" = "$git_rev" } # Notice how the remote name "origin" has been chosen arbitrarily. if ! is_up_to_date; then if ! test -d "$cache_dir"; then mkdir -p "$cache_dir" cache_git init --bare fi if ! cache_git_url=$(cache_git config remote.origin.url); then cache_git remote add origin "$git_url" elif test "$cache_git_url" != "$git_url"; then cache_git remote set-url origin "$git_url" fi cache_git fetch origin if ! test -d "$work_dir"; then git clone -n --shared "$cache_dir" "$work_dir" fi commit_name=$(cache_git rev-parse "$git_rev") work_git checkout "$commit_name" -- "$(readlink -f "$work_dir")" work_git checkout -q "$commit_name" work_git submodule init work_git submodule update fi work_git clean -dxf # Relative links are nicer, and actually we know that work_dir and out_link are # the same. But, for robustness, check anyway.. :) if test "$(dirname "$work_dir")" = "$(dirname "$out_link")"; then ln -snf "$(basename "$work_dir")" "$out_link" else ln -snf "$work_dir" "$out_link" fi