1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/dist/tools/git/git-cache

292 lines
7.3 KiB
Bash
Executable File

#!/usr/bin/env bash
set -e
[ "$GIT_CACHE_VERBOSE" != "1" ] && Q=-q
git_cache() {
git -C "${GIT_CACHE_DIR}" $*
}
git_cache_initialized() {
local _git_dir="$(git_cache rev-parse --git-dir 2>/dev/null)"
test "$_git_dir" = "." -o "$_git_dir" = ".git"
}
init() {
git_cache_initialized || {
mkdir -p "${GIT_CACHE_DIR}"
git_cache init --bare
git_cache config core.compression 1
}
}
startswith() {
case "$1" in
"${2}"*)
return 0
;;
*)
return 1
esac
}
add() {
local repo="$1"
_locked "$GIT_CACHE_DIR/$name.addlock" _add "$repo"
}
_add() {
local repo="$1"
local name="$(_remote_name $repo)"
if ! is_cached "$repo"; then
git_cache remote add "$name" "$repo"
git_cache config --add remote.${name}.fetch "+refs/tags/*:refs/tags/${name}/*"
else
echo "git-cache: $url already in cache"
fi
}
if [ "$(uname)" = Darwin ]; then
_locked() {
local lockfile="$1"
shift
while ! shlock -p $$ -f $lockfile; do
sleep 0.2
done
$*
rm $lockfile
}
else
_locked() {
local lockfile="$1"
shift
(
flock -w 600 9 || exit 1
$*
) 9>"$lockfile"
}
fi
update() {
local REMOTE=${1}
if [ -n "$REMOTE" ]; then
local REMOTES=$(_remote_name $REMOTE)
else
local REMOTES="$(git_cache remote show)"
fi
for remote in $REMOTES; do
echo "git-cache: updating remote $remote"
_locked "$GIT_CACHE_DIR/$remote.lock" git_cache --namespace $remote fetch $Q -n $remote
done
}
is_cached() {
local url="$1"
local REMOTES="$(git_cache remote show)"
for remote in $REMOTES; do
[ "$(git_cache ls-remote --get-url $remote)" = "$url" ] && return 0
done
return 1
}
list() {
local REMOTES="$(git_cache remote show)"
for remote in $REMOTES; do
echo "$(git_cache ls-remote --get-url $remote)"
done
}
drop() {
local REMOTE=${1}
[ -z "$REMOTE" ] && {
echo "usage: git cache drop <url>"
exit 1
}
local REMOTES="$(git_cache remote show)"
for remote in $REMOTES; do
[ "$(git_cache ls-remote --get-url $remote)" = "$REMOTE" ] && {
git_cache remote remove $remote
break
}
done
}
_check_commit() {
git_cache cat-file -e ${1}^{commit} 2>/dev/null
}
_remote_name() {
echo "$*" | git hash-object --stdin
}
_tag_to_sha1() {
local out="$(git_cache log -n 1 --pretty=oneline $1 -- 2>/dev/null || true)"
[ -n "$out" ] && echo $out | cut -f 1 -d" "
}
_check_tag_or_commit() {
local SHA1=$1
local REMOTE_NAME=$2
if _check_commit $SHA1 ; then
local tag=commit$SHA1-$$
git_cache tag $tag $SHA1 2> /dev/null || true # ignore possibly already existing tag
echo "$tag"
elif _tag_to_sha1 ${REMOTE_NAME}/$SHA1 > /dev/null; then
echo "${REMOTE_NAME}/$SHA1"
fi
}
clone() {
local REMOTE="${1}"
local SHA1="${2}"
local REMOTE_NAME="$(_remote_name $REMOTE)"
local TARGET_PATH="${3}"
[ -z "$TARGET_PATH" ] && TARGET_PATH="$(basename $REMOTE)"
# make sure git won't ask for credentials
export GIT_TERMINAL_PROMPT=0
if git_cache_initialized; then
if ! is_cached "$REMOTE"; then
echo "git cache: auto-adding $REMOTE"
add "$REMOTE"
fi
local pull=0
if startswith "$SHA1" "pull/"; then
pull=1
fi
if [ $pull -eq 0 ]; then
local tag="$(_check_tag_or_commit $SHA1 $REMOTE_NAME)"
if [ -z "$tag" ]; then
# commit / tag not in cache, try updating repo
update "$REMOTE"
tag="$(_check_tag_or_commit $SHA1 $REMOTE_NAME)"
fi
else
local tag="remotes/$REMOTE_NAME/master"
if [ -z "$(_tag_to_sha1 $tag)" ]; then
update "$REMOTE"
fi
tag="$(_check_tag_or_commit $(_tag_to_sha1 $tag) $REMOTE_NAME)"
if [ -z "$tag" ]; then
echo "git-cache: cannot checkout master branch of $REMOTE"
false
fi
fi
if [ -n "$tag" ]; then
echo "git-cache: cloning from cache. tag=$tag"
git -c advice.detachedHead=false clone $Q --reference "${GIT_CACHE_DIR}" --shared "${GIT_CACHE_DIR}" "${TARGET_PATH}" --branch $tag
# rename tags from <remote-hash>/* to *
git -C "${TARGET_PATH}" fetch $Q origin "refs/tags/${REMOTE_NAME}/*:refs/tags/*"
# remove all commit* and <remote-hash>/* tags
git -C "${TARGET_PATH}" tag -l \
| grep -P '(^[a-f0-9]{40}/|^commit[a-f0-9]{40}(-\d+)?$)' \
| xargs git -C "${TARGET_PATH}" tag -d > /dev/null
# cleanup possibly created helper tag
case $tag in
commit*)
git_cache tag -d $tag 2>&1 > /dev/null || true
;;
esac
if [ $pull -eq 1 ]; then
git -C "${TARGET_PATH}" fetch $Q $REMOTE $SHA1:$SHA1
git -C "${TARGET_PATH}" checkout $Q $SHA1
fi
else
echo "git-cache: trying checkout from source"
git clone $Q --reference "${GIT_CACHE_DIR}" --shared "${REMOTE}" "${TARGET_PATH}"
git -c advice.detachedHead=false -C "${TARGET_PATH}" checkout $Q $SHA1
fi
else
git clone "${REMOTE}" "${TARGET_PATH}"
git -c advice.detachedHead=false -C "${TARGET_PATH}" checkout $SHA1
fi
}
cleanup() {
git_cache tag -l \
| grep -P '(^commit[a-f0-9]{40}(-\d+)?$)' \
| xargs git -C "${GIT_CACHE_DIR}" tag -d > /dev/null
}
usage() {
echo "git cache uses a bare git repository containing all objects from multiple"
echo "upstream git repositories."
echo ""
echo "usage:"
echo ""
echo " git cache init initialize git cache"
echo " git cache add <url> add repository <url>"
echo " git cache list list cached repositories"
echo " git cache drop <url> drop repo from cache"
echo " git cache update [<url>] fetch repo <url> (or all)"
echo " git cache clone <url> <SHA1> clone repository <url> from cache"
echo " git cache show-path print's the path that can be used as "
echo " '--reference' parameter"
echo " git cache cleanup cleanup dangling temporary tags"
echo " (appear if git-cache gets inter-"
echo " rupted, but are harmless)"
echo ""
echo "To retrieve objects from cache (will use remote repository if needed):"
echo ' git clone --reference $(git cache show-path) <repo>'
}
[ $# -eq 0 ] && {
usage
exit 1
}
ACTION=$1
shift 1
GIT_CACHE_DIR=${GIT_CACHE_DIR:-${HOME}/.gitcache}
case $ACTION in
init)
init $*
;;
add)
add $*
;;
update)
update $*
;;
list)
list $*
;;
drop)
drop $*
;;
show-path)
echo ${GIT_CACHE_DIR}
;;
clone)
clone $*
;;
cleanup)
cleanup
;;
*)
usage
;;
esac