Skip to content

Instantly share code, notes, and snippets.

@larsxschneider
Last active July 15, 2023 08:54
Show Gist options
  • Save larsxschneider/e431a2dcc325135bcda5cc459e6b6976 to your computer and use it in GitHub Desktop.
Save larsxschneider/e431a2dcc325135bcda5cc459e6b6976 to your computer and use it in GitHub Desktop.
Clone a repository with Git LFS files and leverage a local cache.
#!/usr/bin/env bash
#
# Clone a repository with Git LFS files and leverage a local cache.
#
# Usage:
#
# Clone and checkout any branch:
# $ clone.sh <repo-url> <cache-dir> <working-copy-dir> <branch-name>
#
# Clone and checkout a PR head:
# $ clone.sh <repo-url> <cache-dir> <working-copy-dir> pull/<pr-id>/head
#
# Clone and checkout a PR merge:
# $ clone.sh <repo-url> <cache-dir> <working-copy-dir> pull/<pr-id>/merge
#
GIT_REPO_URL=$1
GIT_OBJECT_CACHE_DIR=$2
WORKING_COPY_DIR=$3
REF=$4
# Abort the script if a single command fails.
set -e
# Check if $DEBUG variable is defined (e.g. with "export DEBUG=1")
# If it is defined, then print all commands executed by this script
# and all internal commands executed by Git.
if [[ -n "$DEBUG" ]]; then
set -x
export GIT_TRACE=1
fi
# Helper function that retries a command if it failed.
# This is useful for Git commands that perform network requests as
# network requests can always fail.
function execute_with_retry {
COMMAND=$1
RETRIES=7 # longest continuous wait should be 64s (2**6)
COUNT=1 # first try after 4s, if needed
RET=1 # make command overwrite this
while [ $COUNT -lt $RETRIES ]; do
set +e
$COMMAND
RET=$?
set -e
if [ $RET -eq 0 ]; then
break
fi
COUNT=$((COUNT+1))
DELAY=$((2**COUNT))
sleep $DELAY
done
if [ $RET -gt 0 ]; then
echo "'$COMMAND' failed with exit code '$RET'"
exit $RET
fi
}
# Create cache if it does not exist yet
if [ -z "$(ls -A "$GIT_OBJECT_CACHE_DIR" 2>/dev/null)" ]; then
printf "\n## Initializing repo cache...\n"
execute_with_retry "git clone --bare $GIT_REPO_URL $GIT_OBJECT_CACHE_DIR"
fi
# Update the cache
printf "\n## Fetching latest changes into repo cache...\n"
pushd "$GIT_OBJECT_CACHE_DIR" >/dev/null
CACHE_URL=$(git remote get-url origin)
if [ $CACHE_URL != $GIT_REPO_URL ]; then
printf "\nERROR: Repo cache ($CACHE_URL) does not match request repo ($GIT_REPO_URL)!\n"
exit 1
fi
execute_with_retry "git fetch origin $REF:$REF"
execute_with_retry "git lfs fetch origin $REF"
popd
# Create a fresh clone and leverage the cache
printf "\n## Setup working copy...\n"
rm -rf "$WORKING_COPY_DIR"
mkdir -p "$WORKING_COPY_DIR"
pushd "$WORKING_COPY_DIR" >/dev/null
git init .
mkdir -p .git/objects/info/
echo "${GIT_OBJECT_CACHE_DIR}/objects" > .git/objects/info/alternates
git remote add origin "$GIT_REPO_URL"
execute_with_retry "git fetch origin $REF:local-ci-branch/$REF"
execute_with_retry "git checkout -f local-ci-branch/$REF"
popd
printf "\n## DONE!\n"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment