Commit 3aef623b225b81a61fd3786dc79d091f47b68225

Stefan Sperling 2019-10-15T11:38:28

catch and reject integration into the freshly rebased branch

diff --git a/got/got.c b/got/got.c
index c2246a9..5423c33 100644
--- a/got/got.c
+++ b/got/got.c
@@ -775,7 +775,8 @@ check_cancelled(void *arg)
 
 static const struct got_error *
 check_linear_ancestry(struct got_object_id *commit_id,
-    struct got_object_id *base_commit_id, struct got_repository *repo)
+    struct got_object_id *base_commit_id, int allow_forwards_in_time_only,
+    struct got_repository *repo)
 {
 	const struct got_error *err = NULL;
 	struct got_object_id *yca_id;
@@ -806,7 +807,10 @@ check_linear_ancestry(struct got_object_id *commit_id,
 	 * Update forwards in time:  A (base/yca) - B - C - D (commit)
 	 * Update backwards in time: D (base) - C - B - A (commit/yca)
 	 */
-	if (got_object_id_cmp(commit_id, yca_id) != 0 &&
+	if (allow_forwards_in_time_only) {
+	    if (got_object_id_cmp(base_commit_id, yca_id) != 0)
+		return got_error(GOT_ERR_ANCESTRY);
+	} else if (got_object_id_cmp(commit_id, yca_id) != 0 &&
 	    got_object_id_cmp(base_commit_id, yca_id) != 0)
 		return got_error(GOT_ERR_ANCESTRY);
 
@@ -1063,7 +1067,7 @@ cmd_checkout(int argc, char *argv[])
 		if (error)
 			goto done;
 		error = check_linear_ancestry(commit_id,
-		    got_worktree_get_base_commit_id(worktree), repo);
+		    got_worktree_get_base_commit_id(worktree), 0, repo);
 		if (error != NULL) {
 			free(commit_id);
 			goto done;
@@ -1143,7 +1147,7 @@ switch_head_ref(struct got_reference *head_ref,
 	}
 
 	err = check_linear_ancestry(commit_id,
-	    got_worktree_get_base_commit_id(worktree), repo);
+	    got_worktree_get_base_commit_id(worktree), 0, repo);
 	if (err) {
 		if (err->code != GOT_ERR_ANCESTRY)
 			return err;
@@ -1317,7 +1321,8 @@ cmd_update(int argc, char *argv[])
 		error = got_ref_resolve(&head_commit_id, repo, head_ref);
 		if (error)
 			goto done;
-		error = check_linear_ancestry(commit_id, head_commit_id, repo);
+		error = check_linear_ancestry(commit_id, head_commit_id, 0,
+		    repo);
 		free(head_commit_id);
 		if (error != NULL)
 			goto done;
@@ -1329,7 +1334,7 @@ cmd_update(int argc, char *argv[])
 			goto done;
 	} else {
 		error = check_linear_ancestry(commit_id,
-		    got_worktree_get_base_commit_id(worktree), repo);
+		    got_worktree_get_base_commit_id(worktree), 0, repo);
 		if (error != NULL) {
 			if (error->code == GOT_ERR_ANCESTRY)
 				error = got_error(GOT_ERR_BRANCH_MOVED);
@@ -6564,7 +6569,7 @@ cmd_integrate(int argc, char *argv[])
 		goto done;
 	}
 
-	error = check_linear_ancestry(commit_id, base_commit_id, repo);
+	error = check_linear_ancestry(commit_id, base_commit_id, 1, repo);
 	if (error) {
 		if (error->code == GOT_ERR_ANCESTRY)
 			error = got_error(GOT_ERR_REBASE_REQUIRED);
diff --git a/regress/cmdline/integrate.sh b/regress/cmdline/integrate.sh
index 86d2fa9..35a98a4 100644
--- a/regress/cmdline/integrate.sh
+++ b/regress/cmdline/integrate.sh
@@ -291,7 +291,84 @@ function test_integrate_path_prefix {
 	test_done "$testroot" "$ret"
 }
 
+function test_integrate_backwards_in_time {
+	local testroot=`test_init integrate_backwards_in_time`
+
+	(cd $testroot/repo && git checkout -q -b newbranch)
+	echo "modified delta on branch" > $testroot/repo/gamma/delta
+	git_commit $testroot/repo -m "committing to delta on newbranch"
+
+	echo "modified alpha on branch" > $testroot/repo/alpha
+	(cd $testroot/repo && git rm -q beta)
+	echo "new file on branch" > $testroot/repo/epsilon/new
+	(cd $testroot/repo && git add epsilon/new)
+	git_commit $testroot/repo -m "committing more changes on newbranch"
+
+	local orig_commit1=`git_show_parent_commit $testroot/repo`
+	local orig_commit2=`git_show_head $testroot/repo`
+
+	(cd $testroot/repo && git checkout -q master)
+	echo "modified zeta on master" > $testroot/repo/epsilon/zeta
+	git_commit $testroot/repo -m "committing to zeta on master"
+	local master_commit=`git_show_head $testroot/repo`
+
+	got checkout $testroot/repo $testroot/wt > /dev/null
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	(cd $testroot/wt && got rebase newbranch > /dev/null)
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		echo "got rebase failed unexpectedly"
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	(cd $testroot/repo && git checkout -q newbranch)
+	local new_commit1=`git_show_parent_commit $testroot/repo`
+	local new_commit2=`git_show_head $testroot/repo`
+
+	# attempt to integrate master into newbranch (wrong way around)
+	(cd $testroot/wt && got update -b newbranch > /dev/null)
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		echo "got update failed unexpectedly"
+		test_done "$testroot" "$ret"
+	 return 1
+	fi
+
+	(cd $testroot/wt && got integrate master \
+		> $testroot/stdout 2> $testroot/stderr)
+	ret="$?"
+	if [ "$ret" == "0" ]; then
+		echo "got integrate succeeded unexpectedly"
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	echo -n > $testroot/stdout.expected
+	cmp -s $testroot/stdout.expected $testroot/stdout
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/stdout.expected $testroot/stdout
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	echo "got: specified branch must be rebased first" \
+		> $testroot/stderr.expected
+	cmp -s $testroot/stderr.expected $testroot/stderr
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/stderr.expected $testroot/stderr
+	fi
+	test_done "$testroot" "$ret"
+}
 
 run_test test_integrate_basic
 run_test test_integrate_requires_rebase_first
 run_test test_integrate_path_prefix
+run_test test_integrate_backwards_in_time