#!/bin/bash
# Copyright, the authors of the Linux man-pages project
# SPDX-License-Identifier: GPL-3.0-or-later

set -Eeufo pipefail;
shopt -s lastpipe;

err()
{
	>&2 printf '%s\n' "$(basename "$0"): error: $*";
	exit 1;
}

git rev-parse --git-dir | read -r git_dir;
git rev-parse --git-path bisect-rebase | read -r dir;

while test $# -ge 1; do
	case "$1" in
	--abort)
		test -d "$dir/" || err 'no bisect-rebase in progress';
		set +e;
		git rebase --abort >/dev/null 2>/dev/null;
		git bisect reset >/dev/null 2>/dev/null;
		xargs git checkout <"$dir/head-name";
		xargs git reset --hard <"$dir/orig-head";
		rm -rf "$dir";
		exit 0;
		;;
	--continue)
		test -d "$dir/" || err 'no bisect-rebase in progress';
		if test -d "$git_dir/rebase-merge"; then
			err 'You must finish the current rebase operation.';
		fi;
		exec git-bisect-rebase--continue;
		;;
	--quit)
		test -d "$dir/" || err 'no bisect-rebase in progress';
		if test -d "$git_dir/rebase-merge"; then
			err 'You must finish the current rebase operation.';
		fi;
		rm -rf "$dir";
		exit 0;
		;;
	*)
		break;
		;;
	esac;
done;

mkdir "$dir";
trap 'rm -rf "$dir";' EXIT;

# shellcheck disable=SC2312  # False positive: <https://github.com/koalaman/shellcheck/issues/3554>
cat <<-'__EOF__' |
	git bisect-rebase [<option> ...] [<upstream> [<branch>]]

	Rebase <branch> on top of the first conflicting commit from <upstream>.
	--
	 Mode options
	abort!		Abort the bisect-rebase operation and reset HEAD to the original <branch>.
	continue!	Restart the bisect-rebase process after having resolved a particular rebase.
	quit!		Abort the bisect-rebase operation but HEAD is not reset.

	 git-bisect(1) options
	first-parent!		See git-bisect(1).

	 git-rebase(1) options
	r,rebase-merges?	See git-rebase(1).
	apply!*			^
	empty!*=		^
	keep-empty*		^
	reapply-cherry-picks*	^
	allow-empty-message*	^
	m,merge!*		^
	s,strategy!*=		^
	X,strategy-option!*=	^
	rerere-autoupdate*	^
	S,gpg-sign*?		^
	q,quiet!*		^
	v,verbose!*		^
	stat!*			^
	n,no-stat!*		^
	verify*			^
	C!*=			^
	no-ff!*			^
	f,force-rebase!*	^
	ignore-whitespace*	^
	update-refs*		^
	x,exec=cmd		See git-rebase(1).

	 Other options
	pre-exec!=cmd	Run <cmd> before every git-rebase(1) operation.
	post-exec!=cmd	Run <cmd> after every git-rebase(1) operation.
__EOF__
eval "$(git rev-parse --parseopt --stuck-long --keep-dashdash -- "$@" || echo exit $?)";

touch "$dir/git-bisect-options";
touch "$dir/git-rebase-options";
while test $# -ge 1; do
	case "$1" in
	--abort | --continue | --quit)
		err "$1 is incompatible with other arguments.";
		;;
	--)
		shift;
		break;
		;;
	--pre-exec=*)
		printf '%s\n' "$1" | sed 's/--pre-exec=//' >"$dir/pre-exec";
		printf '%s\n' "$1" | wc -l | xargs test 1 -eq \
		|| err '--pre-exec: commands cannot contain newlines.';
		;;
	--post-exec=*)
		printf '%s\n' "$1" | sed 's/--post-exec=//' >"$dir/post-exec";
		printf '%s\n' "$1" | wc -l | xargs test 1 -eq \
		|| err '--post-exec: commands cannot contain newlines.';
		;;
	--first-parent)
		printf '%s\n' "$1" >>"$dir/git-bisect-options";
		;;
	-*)
		printf '%s\n' "$1" >>"$dir/git-rebase-options";
		;;
	*)
		exit 1;  # Unreachable
		;;
	esac;
	shift;
done;

git rev-parse --default '@{u}' --verify "${@:1:1}" >"$dir/onto";
test $# -eq 0 || shift;

if test $# -eq 0; then
	git rev-parse --verify --abbrev-ref HEAD;
	git rev-parse --verify HEAD;
else
	git rev-parse --verify --symbolic "$1";
	git rev-parse --verify "$1";
fi \
| sed '/^HEAD$/d' \
| sed '1!d' \
>"$dir/head-name";
test $# -eq 0 || shift;

test $# -eq 0 || err 'Too many arguments.';

xargs git rev-parse --verify <"$dir/head-name" >"$dir/orig-head";
exec git-bisect-rebase--continue;
