#!/bin/sh -efu

. gb-sh-functions

enable -f /usr/lib/bash/lockf lockf
# Obtain an exclusive lock on the $GB_GEARS_DIR.
builtin lockf -v "$GB_GEARS_DIR"

updated_dirs=
update_git_timestamp()
{
	local s
	s="$(git for-each-ref "--format=%(tagger)" --sort=-taggerdate --count=1 refs/tags |
		sed -n 's/.* \([1-9][0-9]\{9,\}\) [+-][0-9]\+$/\1/p')"
	[ -n "$s" ] || return 0
	touch --date="1970-01-01 $s seconds UTC" "$GIT_DIR"
	updated_dirs="$updated_dirs
${GIT_DIR%/*}"
}

git_fetch()
{
	git fetch -q "$@"
	rm -f "$GIT_DIR/FETCH_HEAD"
}

git_update()
{
	local tag_name tag_id old_id branch_id
	tag_name="$1"
	tag_id="$2"
	old_id="$3"

	branch_id="$tag_id"
	if [ -s "gears/$i/branch_id" ]; then
		branch_id="$(cat "gears/$i/branch_id")"
		git_fetch "gears/$i/git" master
	else
		git_fetch "gears/$i/git" tag "$tag_name"
	fi

	if [ -n "$old_id" ]; then
		check_inheritance "$branch_id" "$old_id" ||
			girar_rename_repo_branch
	fi

	git branch -f "$GB_REPO_NAME" "$branch_id"
}

check_inheritance()
{
	local new_id="$1"; shift
	local old_id="$1"; shift

	[ -z "$(git log -n1 --pretty=format:1 ^"$new_id" "$old_id")" ]
}

task_id=$(cat task/id)
girar_rename_repo_branch()
{
	local n
	n="old/$GB_REPO_NAME-task$task_id"
	git branch -m "$GB_REPO_NAME" "$n"
	stamp_echo >&2 "saved $GIT_DIR branch \`$GB_REPO_NAME' as \`$n'"
	git symbolic-ref HEAD refs/heads/sisyphus
}

girar_commit_git()
{
	local tag_name tag_id old_id
	tag_name="$(cat "gears/$i/tag_name")"
	tag_id="$(cat "gears/$i/tag_id")"
	old_id="$(git_get_branch_id "$GB_REPO_NAME" 2>/dev/null ||:)"
	if [ -n "$old_id" ]; then
		# update gear
		git_update "$tag_name" "$tag_id" "$old_id"
		stamp_echo >&2 "updated $GIT_DIR branch \`$GB_REPO_NAME'"
	else
		# first time push
		mkdir -p -- "$GIT_DIR"
		git init -q --template=/var/empty
		git_update "$tag_name" "$tag_id" "$old_id"
		git symbolic-ref HEAD refs/heads/sisyphus
		touch -- "$GIT_DIR/git-daemon-export-ok"
		stamp_echo >&2 "created $GIT_DIR branch \`$GB_REPO_NAME'"
	fi
	git repack -q -a -d ||:
	update_git_timestamp
	# Save space by replacing local objects store with symlink to global store.
	if mv "gears/$i/git/objects"{,.orig}; then
		if ln -s "$GIT_DIR/objects" "gears/$i/git/"; then
			rm -rf "gears/$i/git/objects.orig"
		else
			mv "gears/$i/git/objects"{.orig,}
		fi
	fi
}

girar_commit_srpm()
{
	local srpm tmp_repo="$tmpdir/repo"
	srpm="$PWD/gears/$i/$(cat "gears/$i/srpm")"
	rm -rf -- "$tmp_repo"
	if [ -n "$(git_get_branch_id "$GB_REPO_NAME" 2>/dev/null)" ]; then
		# update gear
		export -n GIT_DIR
		git clone -q -s --bare --template=/var/empty "$GIT_DIR" "$tmp_repo/.git"
		cd "$tmp_repo"
		git config core.bare false
		gear-srpmimport -q --import-only --branch="$GB_REPO_NAME" -- "$srpm" ||:
		cd - >/dev/null

		export GIT_DIR
		git_fetch -u "$tmp_repo/.git" "refs/heads/$GB_REPO_NAME:refs/heads/$GB_REPO_NAME"
		stamp_echo >&2 "updated $GIT_DIR branch \`$GB_REPO_NAME'"
	else
		# first time push
		export -n GIT_DIR
		mkdir -p -- "$tmp_repo"
		cd "$tmp_repo"
		git init -q --template=/var/empty
		gear-srpmimport -q --import-only --branch="$GB_REPO_NAME" -- "$srpm" ||:
		cd - >/dev/null

		export GIT_DIR
		mkdir -p -- "$GIT_DIR"
		git init -q --template=/var/empty
		git_fetch -u "$tmp_repo/.git" "refs/heads/$GB_REPO_NAME:refs/heads/$GB_REPO_NAME"
		git symbolic-ref HEAD refs/heads/sisyphus
		touch -- "$GIT_DIR/git-daemon-export-ok"
		stamp_echo >&2 "created $GIT_DIR branch \`$GB_REPO_NAME'"
	fi
	rm -rf -- "$tmp_repo"
	git repack -q -a -d ||:
	update_git_timestamp
}

girar_obsolete()
{
	local dir="$1"; shift
	local N="$1"; shift
	local GIT_DIR
	set_GIT_DIR "$dir" "$N"
	[ -d "$GIT_DIR" ] || return 0
	local id
	if [ -n "$(git_get_branch_id "$GB_REPO_NAME" 2>/dev/null)" ]; then
		girar_rename_repo_branch
		update_git_timestamp
		stamp_echo >&2 "removed $GIT_DIR branch \`$GB_REPO_NAME'"
	fi
}

girar_commit_copy()
{
	local copy_repo text
	copy_repo="$(cat "gears/$i/copy_repo")"
	if [ -n "$(git_get_branch_id "$copy_repo" 2>/dev/null)" ]; then
		[ -n "$(git_get_branch_id "$GB_REPO_NAME" 2>/dev/null)" ] &&
			text=updated || text=created
		git branch -f "$GB_REPO_NAME" "refs/heads/$copy_repo"
		stamp_echo >&2 "$text $GIT_DIR branch \`$GB_REPO_NAME'"
		update_git_timestamp
	else
		if [ -n "$(git_get_branch_id "$GB_REPO_NAME" 2>/dev/null)" ]; then
			girar_obsolete "$GB_GEARS_DIR" "$N"
		fi
	fi
}

girar_commit()
{
	local N="$1" i="$2"; shift 2
	local GIT_DIR

	if [ -s "gears/$i/dir" ]; then
		# build from gear
		if [ -f "task/pocket" ]; then
			pocket=$(cat "task/pocket")
			set_GIT_DIR "$GB_POCKETS_DIR/$pocket/files/gears" "$N"
		else
			set_GIT_DIR "$GB_GEARS_DIR" "$N"
		fi
		girar_commit_git
	elif [ -s "gears/$i/srpm" ]; then
		# build from srpm
		set_GIT_DIR "$GB_SRPMS_DIR" "$N"
		girar_commit_srpm
		girar_obsolete "$GB_GEARS_DIR" "$N"
	elif [ -s "gears/$i/copy_repo" ]; then
		# copy from another branch
		set_GIT_DIR "$GB_GEARS_DIR" "$N"
		girar_commit_copy
	else
		fail "#$i: source not found"
	fi
}

. gb-sh-tmpdir

while read -r N EVR F P I; do
	girar_commit "$N" "$I"
done <plan/add-src

join -v2 plan/add-src plan/rm-src > "$tmpdir"/rm

while read -r N EVR F P I; do
	girar_obsolete "$GB_GEARS_DIR" "$N"
done <"$tmpdir"/rm

gb-y-make-git-html-index $(printf '%s\n' "$updated_dirs" |sort -u)

stamp_echo >&2 'gears update OK'
