Commit Diff


commit - 606719cd7bdc625a582f93ddbc966675568f0026
commit + 1fa4907213af37d50029ee33b989867f41f0f124
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 - 5aa3eb413b775cdcac39caf15b875eb40f76ad63
blob + cc893a7ecded361e36d9c9311949b5349d944a41
--- 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 - 334330319a6a65e06ab84e1264750ba5c01ecb33
blob + 8ca3370f2ac372e3923bb4199f6774b6159e3ec5
--- 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