Commit Diff


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