#!/bin/sh

NWD=contrib/workdir/git-new-workdir
MASTER=master

section () {
	printf "\033]0;%s %s\007" "$branch" "$*"

	printf "\n\n\n"
	printf "############ %s %s\n" "$branch" "$*"
	printf "\n\n\n"
}

inst_prefix=$(
	IFS=:
	for p in $PATH
	do
		probed=${p%/git-active/bin}
		if test "$probed" != "$p"
		then
			echo "$probed"
			exit
		fi
	done
	echo $HOME
)

force= with_dash= test_long= M= install= doc= notest= bootstrap= branches= jobs=
scratch= noprove= memtrash=--memtrash with_cocci= with_leaks= with_sha256= san= 
clean= with_meson= breaking= test_vars=

while	case "$1" in
	--pedantic | --locale=* | --loose) M="$M $1" ;;
	--force) force=$1 ;;
	--dash) with_dash=y ;;
	--clean) clean=y ;;
	--cocci) with_cocci=y ;;
	--no-cocci) with_cocci= ;;
	--long) test_long=--long ;;
	--noinstall) install=noinstall ;;
	--nodoc) doc=no ;;
	--asciidoc) doc=asciidoc ;;
	--asciidoctor) doc=ascidoctor ;;
	--notest) notest=y ;;
	--nomemtrash) memtrash= ;;
	--memtrash) memtrash=--memtrash ;;
	--test=*) test="$1" ;;
	--scratch) scratch=y ;;
	--breaking) breaking=breaking ;;
	--no-breaking) breaking= ;;
	--bootstrap) bootstrap=y ;;
	--base=*) BUILDBASE=${1#*=} ;;
	--branches=*) branches=${1#*=} ;;
	--noprove) noprove=$1 ;;
	--san) san=t ;;
	--no-san) san= ;;
	--leaks) with_leaks=t ;;
	--no-leaks) with_leaks= ;;
	--sha256) with_sha256=t ;;
	--no-sha256) with_sha256= ;;
	--meson) with_meson=t ;;
	--test-vars) test_vars=test_vars ;;
	-j*) jobs=$1 ;;
	--) shift; break ;;
	-*) echo >&2 "Unknown option: $1"; exit 1 ;;
	*) break ;;
	esac
do
	shift
done

if test -n "$doc"
then
	: ;# happy with whatever specified
elif sh -c 'asciidoc --version >/dev/null 2>&1'
then
	doc=asciidoc
elif sh -c 'asciidoctor --version >/dev/null 2>&1'
then
	doc=asciidoctor
else
	doc=no
fi

if test "$doc" = asciidoctor
then
	USE_ASCIIDOCTOR=YesPlease
else
	USE_ASCIIDOCTOR=
fi

GIT_TEST_CHAIN_LINT=1
export GIT_TEST_CHAIN_LINT

GIT_PROVE_OPTS="${GIT_PROVE_OPTS:+$GIT_PROVE_OPTS }--state=slow,save"
export GIT_PROVE_OPTS

TEST_CONTRIB_TOO=YesPlease
export TEST_CONTRIB_TOO

test -f /bin/dash || with_dash=
if test -z "$BUILDBASE"
then
	if test -d "$inst_prefix/buildfarm"
	then
		BUILDBASE="$inst_prefix/buildfarm"
	elif test -d "../buildfarm"
	then
		BUILDBASE=../buildfarm
	else
		echo >&2 "Buildbase unknown"
		exit 1
	fi
fi
test -n "$branches" || branches="next $MASTER maint jch seen"
test -n "$jobs" || jobs=-j32

find_installed () {
	branch=$1
	test -f "$inst_prefix/git-$branch/bin/git" &&
	installed=$($inst_prefix/git-$branch/bin/git version) &&
	if version=$(expr "$installed" : '.*\.g\([0-9a-f]*\)$')
	then
		:
	elif version=v$(expr "$installed" : \
		'git version \(.*\)\.rc[0-9]*$')
	then
		version="$version"-$(expr "$installed" : \
			'git version .*\.\(rc[0-9]*\)$')
	else
		version=v$(expr "$installed" : 'git version \(.*\)')
	fi &&
	git rev-parse --verify "$version^0" 2>/dev/null
}

installed_source_trees=" "
for branch in $branches
do
	if	v=$(find_installed $branch) &&
		test -n "$v" &&
		v=$(git rev-parse --verify "$v^{tree}" 2>/dev/null)
	then
		installed_source_trees="$installed_source_trees$v "
	fi
done

for branch in $branches
do
	echo "** $branch **"
	revision=$(git show-ref -s --verify "refs/heads/$branch") || {
		echo "** No $branch"
		continue
	}

	if test ! -d "$BUILDBASE/$branch"
	then
		if test -z "$bootstrap"
		then
			echo "** No $BUILDBASE/$branch"
			continue
		fi
		"$NWD" . "$BUILDBASE/$branch" $branch &&
		ln -s "$(pwd)/Meta" "$BUILDBASE/$branch/Meta" || {
			echo "** Failed to bootstrap $BUILDBASE/$branch"
			continue
		}
	fi

	version=$(find_installed $branch)
	if	test "z$version" = "z$revision"
	then
		echo "* up-to-date version is already installed from $branch"
		test -n "$force" || continue
	fi

	private=$(git rev-parse -q --verify private-$branch 2>/dev/null)
	case $? in 0|1) ;; *) exit $? ;; esac

	vtree=$(git rev-parse --verify "$version^{tree}")
	rtree=$(git rev-parse --verify "$revision^{tree}")
	(
		skip_test=$notest
		case "$doc" in no) skip_doc=1 ;; *) skip_doc= ;; esac
		case "$force" in
		?*)
			;;
		'')
			for xtree in $installed_source_trees $vtree
			do
				if test "z$xtree" = "z$rtree" ||
					git diff --quiet "$xtree" "$rtree" -- . \
					':!GIT-VERSION-GEN' \
					':!RelNotes' \
					':!Documentation/'
				then
					skip_test=1
					break
				fi
			done

			dvtree=$(git rev-parse --verify "$version:Documentation/")
			drtree=$(git rev-parse --verify "$revision:Documentation/")
			if test "z$dvtree" = "z$drtree"
			then
				skip_doc=1
			fi
			;;
		esac

		case "$skip_test" in
		?*)	dotest= ;;
		'')	dotest=test ;;
		esac

		cd "$BUILDBASE/$branch"
		git reset --hard  &&

		case "$scratch$clean" in
		'')
			;;
		*y*)
			saveMeta=$(readlink Meta)
			Meta/Make distclean
			git clean -f -x -d
			ln -s "$saveMeta" Meta
			;;
		esac &&

		case "$(git symbolic-ref HEAD)" in
		"refs/heads/$branch")
			: ;;
		*)
			git checkout "$branch" &&
			git reset --hard || exit
		esac &&

		case "$clean" in
		y)	exit 0 ;;
		esac &&

		case "$private" in
		'')
			;;
		?*)
			git merge --squash --no-commit "$private" || {
				echo >&2 "** Cannot apply private edition changes"
				git reset --hard
			}
			;;
		esac &&

		save=$(git rev-parse HEAD) &&

		if test -n "$with_meson" && test -f "meson.build"
		then
			section meson
			rm -fr "../.$branch.boson" &&
			meson setup "../.$branch.boson" &&
			(
				cd "../.$branch.boson" &&
				meson compile &&
				meson test
			) || exit $?
		fi &&

		# cocci
		if test -n "$with_cocci"
		then
			section coccicheck
			Meta/Make $M $jobs -- coccicheck
		fi &&

		# sparse
		section sparse
		Meta/Make $M $jobs -- NO_REGEX=NoThanks \
			SPARSE_FLAGS=-Wsparse-error sparse &&
		rm -f compat/regex/regex.o &&

		# hdr
		section hdr-check
		Meta/Make $M $jobs -- hdr-check &&

		# test-lint
		section test-lint
		Meta/Make -- -C t test-lint &&

		did_test=
		if test -n "$san"
		then
			section SANITIZE=address,undefined
			did_test=did_test
			SANITIZE=address,undefined Meta/Make $M $jobs $T test &&
			rm -f compat/mmap.o &&
			Meta/Make >/dev/null distclean
		fi &&
		if test -n "$with_leaks"
		then
			section leaks
			did_test=did_test
			SANITIZE=leak \
			GIT_TEST_PASSING_SANITIZE_LEAK=true \
			Meta/Make $jobs $T CC=clang test &&
			Meta/Make >/dev/null distclean
		fi &&
		if test -n "$with_sha256"
		then
			section sha256
			did_test=did_test
			GIT_TEST_DEFAULT_HASH=sha256 Meta/Make $jobs $T test
		fi &&
		if test -n "$test_vars"
		then
			section "test vars"
			did_test=did_test
			(
				export OPENSSL_SHA1_UNSAFE=YesPlease
				export GIT_TEST_SPLIT_INDEX=yes
				export GIT_TEST_FULL_IN_PACK_ARRAY=true
				export GIT_TEST_OE_SIZE=10
				export GIT_TEST_OE_DELTA_SIZE=5
				export GIT_TEST_COMMIT_GRAPH=1
				export GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=1
				export GIT_TEST_MULTI_PACK_INDEX=1
				export GIT_TEST_MULTI_PACK_INDEX_WRITE_INCREMENTAL=1
				export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
				export GIT_TEST_NO_WRITE_REV_INDEX=1
				export GIT_TEST_CHECKOUT_WORKERS=2
				export GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=1
				Meta/Make >/dev/null distclean &&
				Meta/Make $jobs $T test &&
				Meta/Make >/dev/null distclean
			)
		fi &&
		if test -n "$breaking"
		then
			section breaking
			did_test=did_test
			Meta/Make >/dev/null distclean &&
			Meta/Make $jobs WITH_BREAKING_CHANGES=YesPlease $T test &&
			Meta/Make >/dev/null distclean
		fi &&

		if test "$did_test$dotest" = test
		then
			section test
			Meta/Make $M $noprove ${test+"$test"} $jobs $test_long \
				  $memtrash \
			    -- ${with_dash:+SHELL_PATH=/bin/dash} "$@" $T $dotest
		fi &&

		# docs
		{
			test -n "$skip_doc" ||
			if test "$save" = "$(git rev-parse HEAD)"
			then
				section docs
				Meta/Make $M $jobs -- check-docs &&
				Meta/Make $M $jobs -- $USE_ASCIIDOCTOR doc &&
				Meta/Make $M -- install-man install-html
			else
				echo >&2 "Head moved--not installing docs"
			fi
		} &&

		{
			test z$install = znoinstall ||
			if test "$save" = "$(git rev-parse HEAD)"
			then
				section install
				Meta/Make >/dev/null distclean &&
				Meta/Make $jobs $M \
				-- ${with_dash:+SHELL_PATH=/bin/dash} "$@" install
			else
				echo >&2 "Head moved--not installing"
			fi
		} || exit $?

		git reset --hard
	) </dev/null || exit $?

	installed_source_trees="$installed_source_trees$rtree "
done
