Commit Diff


commit - 8cf2970b6c6851169342b56a57bfe72560170f1a
commit + bb494413b7b04767e20fc5a04378d517cda008ad
blob - d988c12aff8b0cdf7dd6438196b3b325427b11f6
blob + 266da97fcdf429bd1e4dd0750268ac9b71761352
--- got/got.1
+++ got/got.1
@@ -1750,6 +1750,12 @@ using the following status codes:
 .Pp
 If merge conflicts occur the rebase operation is interrupted and may
 be continued once conflicts have been resolved.
+If any files with destined changes are found to be missing or unversioned,
+or if files could not be deleted due to differences in deleted content,
+the rebase operation will be interrupted to prevent potentially incomplete
+changes from being committed to the repository without user intervention.
+The work tree may be modified as desired and the rebase operation can be
+continued once the changes present in the work tree are considered complete.
 Alternatively, the rebase operation may be aborted which will leave
 .Ar branch
 unmodified and the work tree switched back to its original branch.
blob - 243ec3f29a0ff1dcb5f15112f6890755b0ce698b
blob + 05dbf3f24ea57bb5b550bc434de96340ac61cac5
--- got/got.c
+++ got/got.c
@@ -8808,14 +8808,15 @@ cmd_rebase(int argc, char *argv[])
 	int ch, rebase_in_progress = 0, abort_rebase = 0, continue_rebase = 0;
 	int histedit_in_progress = 0, merge_in_progress = 0;
 	int create_backup = 1, list_backups = 0, delete_backups = 0;
-	unsigned char rebase_status = GOT_STATUS_NO_CHANGE;
 	struct got_object_id_queue commits;
 	struct got_pathlist_head merged_paths;
 	const struct got_object_id_queue *parent_ids;
 	struct got_object_qid *qid, *pid;
+	struct got_update_progress_arg upa;
 
 	STAILQ_INIT(&commits);
 	TAILQ_INIT(&merged_paths);
+	memset(&upa, 0, sizeof(upa));
 
 	while ((ch = getopt(argc, argv, "aclX")) != -1) {
 		switch (ch) {
@@ -8931,7 +8932,6 @@ cmd_rebase(int argc, char *argv[])
 		goto done;
 
 	if (abort_rebase) {
-		struct got_update_progress_arg upa;
 		if (!rebase_in_progress) {
 			error = got_error(GOT_ERR_NOT_REBASING);
 			goto done;
@@ -8943,7 +8943,6 @@ cmd_rebase(int argc, char *argv[])
 			goto done;
 		printf("Switching work tree to %s\n",
 		    got_ref_get_symref_target(new_base_branch));
-		memset(&upa, 0, sizeof(upa));
 		error = got_worktree_rebase_abort(worktree, fileindex, repo,
 		    new_base_branch, update_progress, &upa);
 		if (error)
@@ -9029,8 +9028,6 @@ cmd_rebase(int argc, char *argv[])
 	pid = STAILQ_FIRST(parent_ids);
 	if (pid == NULL) {
 		if (!continue_rebase) {
-			struct got_update_progress_arg upa;
-			memset(&upa, 0, sizeof(upa));
 			error = got_worktree_rebase_abort(worktree, fileindex,
 			    repo, new_base_branch, update_progress, &upa);
 			if (error)
@@ -9080,7 +9077,6 @@ cmd_rebase(int argc, char *argv[])
 
 	pid = NULL;
 	STAILQ_FOREACH(qid, &commits, entry) {
-		struct got_update_progress_arg upa;
 
 		commit_id = qid->id;
 		parent_id = pid ? pid->id : yca_id;
@@ -9094,13 +9090,14 @@ cmd_rebase(int argc, char *argv[])
 			goto done;
 
 		print_merge_progress_stats(&upa);
-		if (upa.conflicts > 0)
-			rebase_status = GOT_STATUS_CONFLICT;
-
-		if (rebase_status == GOT_STATUS_CONFLICT) {
-			error = show_rebase_merge_conflict(qid->id, repo);
-			if (error)
-				goto done;
+		if (upa.conflicts > 0 || upa.missing > 0 ||
+		    upa.not_deleted > 0 || upa.unversioned > 0) {
+			if (upa.conflicts > 0) {
+				error = show_rebase_merge_conflict(qid->id,
+				    repo);
+				if (error)
+					goto done;
+			}
 			got_worktree_rebase_pathlist_free(&merged_paths);
 			break;
 		}
@@ -9112,12 +9109,30 @@ cmd_rebase(int argc, char *argv[])
 			goto done;
 	}
 
-	if (rebase_status == GOT_STATUS_CONFLICT) {
+	if (upa.conflicts > 0 || upa.missing > 0 ||
+	    upa.not_deleted > 0 || upa.unversioned > 0) {
 		error = got_worktree_rebase_postpone(worktree, fileindex);
 		if (error)
 			goto done;
-		error = got_error_msg(GOT_ERR_CONFLICTS,
-		    "conflicts must be resolved before rebasing can continue");
+		if (upa.conflicts > 0 && upa.missing == 0 &&
+		    upa.not_deleted == 0 && upa.unversioned == 0) {
+			error = got_error_msg(GOT_ERR_CONFLICTS,
+			    "conflicts must be resolved before rebasing "
+			    "can continue");
+		} else if (upa.conflicts > 0) {
+			error = got_error_msg(GOT_ERR_CONFLICTS,
+			    "conflicts must be resolved before rebasing "
+			    "can continue; changes destined for some "
+			    "files were not yet merged and should be "
+			    "merged manually if required before the "
+			    "rebase operation is continued");
+		} else {
+			error = got_error_msg(GOT_ERR_CONFLICTS,
+			    "changes destined for some files were not "
+			    "yet merged and should be merged manually "
+			    "if required before the rebase operation "
+			    "is continued");
+		}
 	} else
 		error = rebase_complete(worktree, fileindex, branch,
 		    new_base_branch, tmp_branch, repo, create_backup);
blob - 14c4abae4bf327f96937f9c2ad442aaf638bce8e
blob + 6685635eca33a2a5aab0ef38cd5416ca47853bca
--- regress/cmdline/rebase.sh
+++ regress/cmdline/rebase.sh
@@ -1187,6 +1187,9 @@ test_rebase_delete_missing_file() {
 	(cd $testroot/repo && git checkout -q newbranch)
 	local orig_commit1=`git_show_parent_commit $testroot/repo`
 	local orig_commit2=`git_show_head $testroot/repo`
+
+	local short_orig_commit1=`trim_obj_id 28 $orig_commit1`
+	local short_orig_commit2=`trim_obj_id 28 $orig_commit2`
 
 	(cd $testroot/wt && got update -b master > /dev/null)
 	(cd $testroot/wt && got rm beta d/f/g/new > /dev/null)
@@ -1197,12 +1200,18 @@ test_rebase_delete_missing_file() {
 	local master_commit=`git_show_head $testroot/repo`
 
 	(cd $testroot/wt && got update -b master > /dev/null)
-	(cd $testroot/wt && got rebase newbranch > $testroot/stdout)
+	(cd $testroot/wt && got rebase newbranch > $testroot/stdout \
+		2> $testroot/stderr)
+	ret="$?"
+	if [ "$ret" = "0" ]; then
+		echo "rebase succeeded unexpectedly" >&2
+		test_done "$testroot" "1"
+		return 1
+	fi
 
-	(cd $testroot/repo && git checkout -q newbranch)
-	local new_commit1=`git_show_head $testroot/repo`
+	local new_commit1=$(cd $testroot/wt && got info | \
+		grep '^work tree base commit: ' | cut -d: -f2 | tr -d ' ')
 
-	local short_orig_commit1=`trim_obj_id 28 $orig_commit1`
 	local short_orig_commit2=`trim_obj_id 28 $orig_commit2`
 	local short_new_commit1=`trim_obj_id 28 $new_commit1`
 
@@ -1215,8 +1224,37 @@ test_rebase_delete_missing_file() {
 	echo -n "Files which had incoming changes but could not be found " \
 		>> $testroot/stdout.expected
 	echo "in the work tree: 2" >> $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 -n "got: changes destined for some files were not yet merged " \
+		> $testroot/stderr.expected
+	echo -n "and should be merged manually if required before the " \
+		>> $testroot/stderr.expected
+	echo "rebase operation is continued" >> $testroot/stderr.expected
+	cmp -s $testroot/stderr.expected $testroot/stderr
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/stderr.expected $testroot/stderr
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	# ignore the missing changes and continue
+	(cd $testroot/wt && got rebase -c > $testroot/stdout)
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		echo "rebase failed unexpectedly" >&2
+		test_done "$testroot" "1"
+		return 1
+	fi
 	echo -n "$short_orig_commit2 -> no-op change" \
-		>> $testroot/stdout.expected
+		> $testroot/stdout.expected
 	echo ": removing beta and d/f/g/new on newbranch" \
 		>> $testroot/stdout.expected
 	echo "Switching work tree to refs/heads/newbranch" \
@@ -1257,6 +1295,10 @@ test_rebase_delete_missing_file() {
 		return 1
 	fi
 
+	(cd $testroot/repo && git checkout -q newbranch)
+	local new_commit1=`git_show_head $testroot/repo`
+	local short_new_commit1=`trim_obj_id 28 $new_commit1`
+
 	(cd $testroot/wt && got log -l3 | grep ^commit > $testroot/stdout)
 	echo "commit $new_commit1 (newbranch)" > $testroot/stdout.expected
 	echo "commit $master_commit (master)" >> $testroot/stdout.expected