commit f0b75401029052c2613a1e5dc3882976b2ab4da9 from: Stefan Sperling date: Sat Aug 03 20:55:36 2019 UTC fix out-of-date check regression; only commit staged files after 'got stage' commit - a76c42e670c040cbc63fae5cedba54de3540ce3c commit + f0b75401029052c2613a1e5dc3882976b2ab4da9 blob - 0a2e9fc90236fca9a7c4e1c9efbd07616aa5e5c6 blob + cbf9d0673835a5bbdf1e42566551338b16037fde --- include/got_error.h +++ include/got_error.h @@ -118,6 +118,7 @@ #define GOT_ERR_STAGE_NO_CHANGE 102 #define GOT_ERR_STAGE_CONFLICT 103 #define GOT_ERR_STAGE_OUT_OF_DATE 104 +#define GOT_ERR_FILE_NOT_STAGED 105 static const struct got_error { int code; @@ -239,6 +240,7 @@ static const struct got_error { { GOT_ERR_STAGE_CONFLICT, "cannot stage file in conflicted status" }, { GOT_ERR_STAGE_OUT_OF_DATE, "work tree must be updated before " "changes can be staged" }, + { GOT_ERR_FILE_NOT_STAGED, "file is not staged" }, }; /* blob - f7db624a62986cd2410180c91f9ad5c7bccb6962 blob + c45a95888b5015de44ca323f6dd4759e19eb36f4 --- lib/got_lib_worktree.h +++ lib/got_lib_worktree.h @@ -40,8 +40,10 @@ struct got_commitable { char *in_repo_path; char *ondisk_path; unsigned char status; + unsigned char staged_status; struct got_object_id *blob_id; struct got_object_id *base_blob_id; + struct got_object_id *staged_blob_id; struct got_object_id *base_commit_id; mode_t mode; int flags; blob - 0115f0622a0a1725349d74ab33e8f9c95ba22184 blob + 0f55d723f8ac61a963a08b5f6e01cce9a7b96232 --- lib/worktree.c +++ lib/worktree.c @@ -2843,6 +2843,7 @@ free_commitable(struct got_commitable *ct) free(ct->ondisk_path); free(ct->blob_id); free(ct->base_blob_id); + free(ct->staged_blob_id); free(ct->base_commit_id); free(ct); } @@ -2925,6 +2926,15 @@ collect_commitables(void *arg, unsigned char status, } ct->base_commit_id = got_object_id_dup(commit_id); if (ct->base_commit_id == NULL) { + err = got_error_from_errno("got_object_id_dup"); + goto done; + } + } + ct->staged_status = staged_status; + if (ct->staged_status == GOT_STATUS_ADD || + ct->staged_status == GOT_STATUS_MODIFY) { + ct->staged_blob_id = got_object_id_dup(staged_blob_id); + if (ct->staged_blob_id == NULL) { err = got_error_from_errno("got_object_id_dup"); goto done; } @@ -3412,20 +3422,6 @@ check_out_of_date(const char *in_repo_path, unsigned c done: free(id); return err; -} - -static const struct got_error * -check_ct_out_of_date(struct got_commitable *ct, struct got_repository *repo, - struct got_object_id *head_commit_id) -{ - const char *ct_path = ct->in_repo_path; - - while (ct_path[0] == '/') - ct_path++; - - return check_out_of_date(ct_path, ct->status, ct->base_commit_id, - ct->base_commit_id, head_commit_id, repo, - GOT_ERR_COMMIT_OUT_OF_DATE); } const struct got_error * @@ -3582,6 +3578,19 @@ check_path_is_commitable(const char *path, return NULL; } +static const struct got_error * +check_staged_file(void *arg, struct got_fileindex_entry *ie) +{ + int *have_staged_files = arg; + + if (got_fileindex_entry_stage_get(ie) != GOT_FILEIDX_STAGE_NONE) { + *have_staged_files = 1; + return got_error(GOT_ERR_CANCELLED); + } + + return NULL; +} + const struct got_error * got_worktree_commit(struct got_object_id **new_commit_id, struct got_worktree *worktree, struct got_pathlist_head *paths, @@ -3598,6 +3607,7 @@ got_worktree_commit(struct got_object_id **new_commit_ struct got_pathlist_entry *pe; struct got_reference *head_ref = NULL; struct got_object_id *head_commit_id = NULL; + int have_staged_files = 0; *new_commit_id = NULL; @@ -3639,12 +3649,29 @@ got_worktree_commit(struct got_object_id **new_commit_ if (err) goto done; } + + err = got_fileindex_for_each_entry_safe(fileindex, check_staged_file, + &have_staged_files); + if (err && err->code != GOT_ERR_CANCELLED) + goto done; TAILQ_FOREACH(pe, &commitable_paths, entry) { struct got_commitable *ct = pe->data; - err = check_ct_out_of_date(ct, repo, head_commit_id); + const char *ct_path = ct->in_repo_path; + + while (ct_path[0] == '/') + ct_path++; + err = check_out_of_date(ct_path, ct->status, + ct->base_blob_id, ct->base_commit_id, head_commit_id, + repo, GOT_ERR_COMMIT_OUT_OF_DATE); if (err) + goto done; + if (have_staged_files && + ct->staged_status == GOT_STATUS_NO_CHANGE) { + err = got_error_path(ct_path, GOT_ERR_FILE_NOT_STAGED); goto done; + } + } err = commit_worktree(new_commit_id, &commitable_paths, blob - 55bad574818603f55ad63de1e59ea2d528baa75d blob + 385f2ae28ee8d1ea15b50cecf144752e04df7a6a --- regress/cmdline/commit.sh +++ regress/cmdline/commit.sh @@ -137,6 +137,7 @@ function test_commit_single_file { function test_commit_out_of_date { local testroot=`test_init commit_out_of_date` + local first_commit=`git_show_head $testroot/repo` got checkout $testroot/repo $testroot/wt > /dev/null ret="$?" @@ -153,7 +154,6 @@ function test_commit_out_of_date { (cd $testroot/wt && got commit -m 'test commit_out_of_date' \ > $testroot/stdout 2> $testroot/stderr) - local head_rev=`git_show_head $testroot/repo` echo -n > $testroot/stdout.expected echo "got: work tree must be updated before these" \ "changes can be committed" > $testroot/stderr.expected @@ -170,7 +170,32 @@ function test_commit_out_of_date { ret="$?" if [ "$ret" != "0" ]; then diff -u $testroot/stderr.expected $testroot/stderr + test_done "$testroot" "$ret" + return 1 fi + + echo "alpha" > $testroot/repo/alpha + git_commit $testroot/repo -m "reset alpha contents" + (cd $testroot/wt && got update -c $first_commit > /dev/null) + + echo "modified alpha" > $testroot/wt/alpha + + (cd $testroot/wt && got commit -m 'changed alpha ' > $testroot/stdout) + ret="$?" + if [ "$ret" != "0" ]; then + echo "commit failed unexpectedly" >&2 + test_done "$testroot" "1" + return 1 + fi + + local head_rev=`git_show_head $testroot/repo` + echo "M alpha" > $testroot/stdout.expected + echo "Created commit $head_rev" >> $testroot/stdout.expected + cmp -s $testroot/stdout.expected $testroot/stdout + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stdout.expected $testroot/stdout + fi test_done "$testroot" "$ret" } blob - 4d057d721979e93aa1b31d4d280ee4f9deadbab0 blob + 61e10ca408100bac2afcaedb9e1365b607e0a48b --- regress/cmdline/stage.sh +++ regress/cmdline/stage.sh @@ -894,7 +894,51 @@ function test_stage_update { fi test_done "$testroot" "$ret" } + +function test_stage_commit_non_staged { + local testroot=`test_init stage_commit_non_staged` + + got checkout $testroot/repo $testroot/wt > /dev/null + ret="$?" + if [ "$ret" != "0" ]; then + test_done "$testroot" "$ret" + return 1 + fi + + echo "modified file" > $testroot/wt/alpha + (cd $testroot/wt && got rm beta > /dev/null) + echo "new file" > $testroot/wt/foo + (cd $testroot/wt && got add foo > /dev/null) + (cd $testroot/wt && got stage alpha beta foo > /dev/null) + echo "modified file" > $testroot/wt/gamma/delta + (cd $testroot/wt && got commit -m "change delta" gamma/delta \ + > $testroot/stdout 2> $testroot/stderr) + ret="$?" + if [ "$ret" == "0" ]; then + echo "got commit command succeeded unexpectedly" >&2 + test_done "$testroot" "1" + return 1 + fi + + echo -n > $testroot/stdout.expected + echo "got: gamma/delta: file is not staged" > $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 + cmp -s $testroot/stdout.expected $testroot/stdout + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stdout.expected $testroot/stdout + fi + test_done "$testroot" "$ret" +} + run_test test_stage_basic run_test test_stage_conflict run_test test_stage_out_of_date @@ -907,3 +951,4 @@ run_test test_stage_diff run_test test_stage_histedit run_test test_stage_rebase run_test test_stage_update +run_test test_stage_commit_non_staged