Commit c932eeeb83c00ba282780d657b539e342d092311

Stefan Sperling 2019-05-22T10:25:50

make 'got update' bump the base commit ID of unchanged files This change makes it actually possible to get around commit-time out-of-dateness by running 'got update'. The test added with this commit shows that our out-of-dateness check is currently too simplistic; an update is required between any two commit operations! It would be better to allow commits to proceed until a situation arises where file content must be merged.

diff --git a/lib/worktree.c b/lib/worktree.c
index 813c260..c0b4a22 100644
--- a/lib/worktree.c
+++ b/lib/worktree.c
@@ -1447,6 +1447,29 @@ done:
 	return err;
 }
 
+struct bump_base_commit_id_arg {
+	struct got_object_id *base_commit_id;
+	const char *path;
+	size_t path_len;
+	const char *entry_name;
+};
+
+/* Bump base commit ID of all files within an updated part of the work tree. */
+static const struct got_error *
+bump_base_commit_id(void *arg, struct got_fileindex_entry *ie)
+{
+	struct bump_base_commit_id_arg *a = arg;
+
+	if (a->entry_name) {
+		if (strcmp(ie->path, a->path) != 0)
+			return NULL;
+	} else if (!got_path_is_child(ie->path, a->path, a->path_len))
+		return NULL;
+
+	memcpy(ie->commit_sha1, a->base_commit_id->sha1, SHA1_DIGEST_LENGTH);
+	return NULL;
+}
+
 const struct got_error *
 got_worktree_checkout_files(struct got_worktree *worktree, const char *path,
     struct got_repository *repo, got_worktree_checkout_cb progress_cb,
@@ -1584,6 +1607,18 @@ got_worktree_checkout_files(struct got_worktree *worktree, const char *path,
 	checkout_err = got_fileindex_diff_tree(fileindex, tree, relpath,
 	    entry_name, repo, &diff_cb, &arg);
 
+	if (checkout_err == NULL) {
+		struct bump_base_commit_id_arg bbc_arg;
+		bbc_arg.base_commit_id = worktree->base_commit_id;
+		bbc_arg.entry_name = entry_name;
+		bbc_arg.path = path;
+		bbc_arg.path_len = strlen(path);
+		err = got_fileindex_for_each_entry_safe(fileindex,
+		    bump_base_commit_id, &bbc_arg);
+		if (err)
+			goto done;
+	}
+
 	/* Try to sync the fileindex back to disk in any case. */
 	err = got_fileindex_write(fileindex, new_index);
 	if (err)
diff --git a/regress/cmdline/update.sh b/regress/cmdline/update.sh
index da70308..13de6cc 100755
--- a/regress/cmdline/update.sh
+++ b/regress/cmdline/update.sh
@@ -1424,6 +1424,72 @@ function test_update_to_commit_on_wrong_branch {
 	test_done "$testroot" "$ret"
 }
 
+function test_update_bumps_base_commit_id {
+	local testroot=`test_init update_to_commit_on_wrong_branch`
+
+	got checkout $testroot/repo $testroot/wt > /dev/null
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	echo "modified alpha" > $testroot/wt/alpha
+	(cd $testroot/wt && got commit -m "changed alpha" > $testroot/stdout)
+
+	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
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	echo "modified beta" > $testroot/wt/beta
+	(cd $testroot/wt && got commit -m "changed beta" > $testroot/stdout \
+		2> $testroot/stderr)
+
+	echo -n "" > $testroot/stdout.expected
+	echo "got: work tree must be updated before these changes can be committed"  > $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
+
+	# XXX At present, got requires users to run 'update' after 'commit'.
+	(cd $testroot/wt && got update > $testroot/stdout)
+
+	echo "Already up-to-date" > $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
+
+	(cd $testroot/wt && got commit -m "changed beta" > $testroot/stdout)
+
+	local head_rev=`git_show_head $testroot/repo`
+	echo "M  beta" > $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
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	test_done "$testroot" "$ret"
+}
+
 run_test test_update_basic
 run_test test_update_adds_file
 run_test test_update_deletes_file
@@ -1452,3 +1518,4 @@ run_test test_update_partial_dir
 run_test test_update_moved_branch_ref
 run_test test_update_to_another_branch
 run_test test_update_to_commit_on_wrong_branch
+run_test test_update_bumps_base_commit_id