Commit Diff


commit - 2822a3526b8c61302ec86e0871e724c07a4b078d
commit + 3aef623b225b81a61fd3786dc79d091f47b68225
blob - c2246a9a4a1eb2d49378c3096a50bcbf472739e2
blob + 5423c33764d81bfc3fd2c71dc7f9ca32f22bad4a
--- got/got.c
+++ 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);
blob - 86d2fa9ed1614f01bad8d546d2b87b57266c3147
blob + 35a98a41a609ebbd3f4945600522a843e17529f2
--- regress/cmdline/integrate.sh
+++ regress/cmdline/integrate.sh
@@ -290,8 +290,85 @@ function test_integrate_path_prefix {
 	fi
 	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