Commit d3e7c587d4fa8caef61be4b2bbc9c388b95e7bb9

Stefan Sperling 2019-08-03T17:26:53

handle double-staging

diff --git a/include/got_error.h b/include/got_error.h
index 8450cbb..462aba4 100644
--- a/include/got_error.h
+++ b/include/got_error.h
@@ -115,6 +115,7 @@
 #define GOT_ERR_NO_MERGED_PATHS 99
 #define GOT_ERR_COMMIT_BRANCH	100
 #define GOT_ERR_FILE_STAGED	101
+#define GOT_ERR_STAGE_NO_CHANGE	102
 
 static const struct got_error {
 	int code;
@@ -232,6 +233,7 @@ static const struct got_error {
 	{ GOT_ERR_COMMIT_BRANCH, "will not commit to a branch outside the "
 	    "\"refs/heads/\" reference namespace" },
 	{ GOT_ERR_FILE_STAGED, "file is staged" },
+	{ GOT_ERR_STAGE_NO_CHANGE, "no changes to stage" },
 };
 
 /*
diff --git a/lib/worktree.c b/lib/worktree.c
index 08c378a..67da6f9 100644
--- a/lib/worktree.c
+++ b/lib/worktree.c
@@ -4924,20 +4924,19 @@ stage_path(const char *relpath, const char *ondisk_path,
 {
 	const struct got_error *err = NULL;
 	struct got_fileindex_entry *ie;
-	unsigned char status;
+	unsigned char status, staged_status;
 	struct stat sb;
 	struct got_object_id blob_id, *staged_blob_id = NULL;
 	uint32_t stage;
 
 	ie = got_fileindex_entry_get(fileindex, relpath, strlen(relpath));
-	if (ie == NULL) {
-		err = got_error_path(relpath, GOT_ERR_FILE_STATUS);
-		goto done;
-	}
+	if (ie == NULL)
+		return got_error_path(relpath, GOT_ERR_FILE_STATUS);
 
 	err = get_file_status(&status, &sb, ie, ondisk_path, repo);
 	if (err)
-		goto done;
+		return err;
+	staged_status = get_staged_status(ie);
 
 	switch (status) {
 	case GOT_STATUS_ADD:
@@ -4949,7 +4948,7 @@ stage_path(const char *relpath, const char *ondisk_path,
 		memcpy(&blob_id.sha1, ie->blob_sha1, SHA1_DIGEST_LENGTH);
 		memcpy(ie->staged_blob_sha1, staged_blob_id->sha1,
 		    SHA1_DIGEST_LENGTH);
-		if (status == GOT_STATUS_ADD)
+		if (status == GOT_STATUS_ADD || staged_status == GOT_STATUS_ADD)
 			stage = GOT_FILEIDX_STAGE_ADD;
 		else
 			stage = GOT_FILEIDX_STAGE_MODIFY;
@@ -4959,11 +4958,16 @@ stage_path(const char *relpath, const char *ondisk_path,
 		    staged_blob_id, NULL);
 		break;
 	case GOT_STATUS_DELETE:
+		if (staged_status == GOT_STATUS_DELETE)
+			break;
 		stage = GOT_FILEIDX_STAGE_DELETE;
 		got_fileindex_entry_stage_set(ie, stage);
 		err = (*status_cb)(status_arg, GOT_STATUS_NO_CHANGE,
 		    get_staged_status(ie), relpath, NULL, NULL, NULL);
 		break;
+	case GOT_STATUS_NO_CHANGE:
+		err = got_error_path(relpath, GOT_ERR_STAGE_NO_CHANGE);
+		break;
 	default:
 		err = got_error_path(relpath, GOT_ERR_FILE_STATUS);
 		break;
diff --git a/regress/cmdline/stage.sh b/regress/cmdline/stage.sh
index 8cd9094..9eee795 100755
--- a/regress/cmdline/stage.sh
+++ b/regress/cmdline/stage.sh
@@ -44,6 +44,83 @@ function test_stage_basic {
 	test_done "$testroot" "$ret"
 }
 
+function test_double_stage {
+	local testroot=`test_init double_stage`
+
+	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 "got: alpha: no changes to stage" > $testroot/stderr.expected
+	(cd $testroot/wt && got stage alpha 2> $testroot/stderr)
+	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
+
+	(cd $testroot/wt && got stage beta > $testroot/stdout)
+	if [ "$ret" != "0" ]; then
+		echo "got stage command failed unexpectedly" >&2
+		test_done "$testroot" "1"
+		return 1
+	fi
+	echo -n > $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 "got: foo: no changes to stage" > $testroot/stderr.expected
+	(cd $testroot/wt && got stage foo 2> $testroot/stderr)
+	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
+
+	echo "modified file again" > $testroot/wt/alpha
+	echo "modified new file" > $testroot/wt/foo
+
+	echo ' M alpha' > $testroot/stdout.expected
+	echo ' A foo' >> $testroot/stdout.expected
+	(cd $testroot/wt && got stage alpha beta foo > $testroot/stdout)
+	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 ' M alpha' > $testroot/stdout.expected
+	echo ' D beta' >> $testroot/stdout.expected
+	echo ' A foo' >> $testroot/stdout.expected
+
+	(cd $testroot/wt && got status > $testroot/stdout)
+	cmp -s $testroot/stdout.expected $testroot/stdout
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/stdout.expected $testroot/stdout
+	fi
+	test_done "$testroot" "$ret"
+}
+
 function test_stage_status {
 	local testroot=`test_init stage_status`
 
@@ -440,6 +517,7 @@ function test_stage_revert {
 }
 
 run_test test_stage_basic
+run_test test_double_stage
 run_test test_stage_status
 run_test test_stage_add_already_staged_file
 run_test test_stage_rm_already_staged_file