#!/bin/sh

test_description='test aborting in-progress merges

Set up repo with conflicting and non-conflicting branches:

There are three files foo/bar/baz, and the following graph illustrates the
content of these files in each commit:

# foo/bar/baz --- foo/bar/bazz     <-- main
#             \
#              --- foo/barf/bazf   <-- conflict_branch
#               \
#                --- foo/bart/baz  <-- clean_branch

Next, test git merge --abort with the following variables:
- before/after successful merge (should fail when not in merge context)
- with/without conflicts
- clean/dirty index before merge
- clean/dirty worktree before merge
- dirty index before merge matches contents on remote branch
- changed/unchanged worktree after merge
- changed/unchanged index after merge
'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME

. ./test-lib.sh

test_expect_success 'setup' '
	# Create the above repo
	echo foo > foo &&
	echo bar > bar &&
	echo baz > baz &&
	git add foo bar baz &&
	git commit -m initial &&
	echo bazz > baz &&
	git commit -a -m "second" &&
	git checkout -b conflict_branch HEAD^ &&
	echo barf > bar &&
	echo bazf > baz &&
	git commit -a -m "conflict" &&
	git checkout -b clean_branch HEAD^ &&
	echo bart > bar &&
	git commit -a -m "clean" &&
	git checkout main
'

pre_merge_head="$(git rev-parse HEAD)"

test_expect_success 'fails without MERGE_HEAD (unstarted merge)' '
	test_must_fail git merge --abort 2>output &&
	test_grep MERGE_HEAD output
'

test_expect_success 'fails without MERGE_HEAD (unstarted merge): .git/MERGE_HEAD sanity' '
	test_path_is_missing .git/MERGE_HEAD &&
	test "$pre_merge_head" = "$(git rev-parse HEAD)"
'

test_expect_success 'fails without MERGE_HEAD (completed merge)' '
	git merge clean_branch &&
	test_path_is_missing .git/MERGE_HEAD &&
	# Merge successfully completed
	post_merge_head="$(git rev-parse HEAD)" &&
	test_must_fail git merge --abort 2>output &&
	test_grep MERGE_HEAD output
'

test_expect_success 'fails without MERGE_HEAD (completed merge): .git/MERGE_HEAD sanity' '
	test_path_is_missing .git/MERGE_HEAD &&
	test "$post_merge_head" = "$(git rev-parse HEAD)"
'

test_expect_success 'Forget previous merge' '
	git reset --hard "$pre_merge_head"
'

test_expect_success 'Abort after --no-commit' '
	# Redo merge, but stop before creating merge commit
	git merge --no-commit clean_branch &&
	test_path_is_file .git/MERGE_HEAD &&
	# Abort non-conflicting merge
	git merge --abort &&
	test_path_is_missing .git/MERGE_HEAD &&
	test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
	test -z "$(git diff)" &&
	test -z "$(git diff --staged)"
'

test_expect_success 'Abort after conflicts' '
	# Create conflicting merge
	test_must_fail git merge conflict_branch &&
	test_path_is_file .git/MERGE_HEAD &&
	# Abort conflicting merge
	git merge --abort &&
	test_path_is_missing .git/MERGE_HEAD &&
	test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
	test -z "$(git diff)" &&
	test -z "$(git diff --staged)"
'

test_expect_success 'Clean merge with dirty index fails' '
	echo xyzzy >> foo &&
	git add foo &&
	git diff --staged > expect &&
	test_must_fail git merge clean_branch &&
	test_path_is_missing .git/MERGE_HEAD &&
	test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
	test -z "$(git diff)" &&
	git diff --staged > actual &&
	test_cmp expect actual
'

test_expect_success 'Conflicting merge with dirty index fails' '
	test_must_fail git merge conflict_branch &&
	test_path_is_missing .git/MERGE_HEAD &&
	test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
	test -z "$(git diff)" &&
	git diff --staged > actual &&
	test_cmp expect actual
'

test_expect_success 'Reset index (but preserve worktree changes)' '
	git reset "$pre_merge_head" &&
	git diff > actual &&
	test_cmp expect actual
'

test_expect_success 'Abort clean merge with non-conflicting dirty worktree' '
	git merge --no-commit clean_branch &&
	test_path_is_file .git/MERGE_HEAD &&
	# Abort merge
	git merge --abort &&
	test_path_is_missing .git/MERGE_HEAD &&
	test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
	test -z "$(git diff --staged)" &&
	git diff > actual &&
	test_cmp expect actual
'

test_expect_success 'Abort conflicting merge with non-conflicting dirty worktree' '
	test_must_fail git merge conflict_branch &&
	test_path_is_file .git/MERGE_HEAD &&
	# Abort merge
	git merge --abort &&
	test_path_is_missing .git/MERGE_HEAD &&
	test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
	test -z "$(git diff --staged)" &&
	git diff > actual &&
	test_cmp expect actual
'

test_expect_success 'Reset worktree changes' '
	git reset --hard "$pre_merge_head"
'

test_expect_success 'Fail clean merge with conflicting dirty worktree' '
	echo xyzzy >> bar &&
	git diff > expect &&
	test_must_fail git merge --no-commit clean_branch &&
	test_path_is_missing .git/MERGE_HEAD &&
	test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
	test -z "$(git diff --staged)" &&
	git diff > actual &&
	test_cmp expect actual
'

test_expect_success 'Fail conflicting merge with conflicting dirty worktree' '
	test_must_fail git merge conflict_branch &&
	test_path_is_missing .git/MERGE_HEAD &&
	test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
	test -z "$(git diff --staged)" &&
	git diff > actual &&
	test_cmp expect actual
'

test_expect_success 'Reset worktree changes' '
	git reset --hard "$pre_merge_head"
'

test_expect_success 'Fail clean merge with matching dirty worktree' '
	echo bart > bar &&
	git diff > expect &&
	test_must_fail git merge --no-commit clean_branch &&
	test_path_is_missing .git/MERGE_HEAD &&
	test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
	test -z "$(git diff --staged)" &&
	git diff > actual &&
	test_cmp expect actual
'

test_expect_success 'Fail conflicting merge with matching dirty worktree' '
	echo barf > bar &&
	git diff > expect &&
	test_must_fail git merge conflict_branch &&
	test_path_is_missing .git/MERGE_HEAD &&
	test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
	test -z "$(git diff --staged)" &&
	git diff > actual &&
	test_cmp expect actual
'

test_done
