commit 1fa4907213af37d50029ee33b989867f41f0f124 from: Stefan Sperling date: Tue Sep 28 21:30:53 2021 UTC interrupt 'got rebase' upon missing/unversioned/not-deleted files 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