"GOT", but the "O" is a cute, smiling pufferfish. Index | Thread | Search

From:
Stefan Sperling <stsp@stsp.name>
Subject:
fix 'got update' with added + obstructed file
To:
gameoftrees@openbsd.org
Date:
Fri, 24 Sep 2021 23:31:14 +0200

Download raw body.

Thread
When 'got update' tries to add a new file to the work tree and this
file is obstructed by a directory on disk, the update fails like this:

  $ got update
  ?  new
  got: new: Is a directory
  $

And the work tree is not updated. The above update with error can be
run over and over and won't proceed until the directory is removed.
The problem here is simply that the update as a whole should succeed,
not stop on one such file even though there might be more files to update.

With the patch below this situation is properly detected as an obstruction
and the update succeeds:

  $ got update
  ~  new
  Updated to refs/heads/master: c1f85b4938dc4c668a88f13df2b98a520fc077cc
  File paths obstructed by a non-regular file: 1
  $

Regardless, the file 'new' will not be added to the work tree until the
directory is moved and 'got update' is run again.

And I am extending a corresponding test to cover this issue.

ok?

diff f365d76274ee1ae2b57225686a0733176fc4bfb2 /home/stsp/src/got
blob - f5bc677438d23ddca778382674edef04e09cb939
file + lib/worktree.c
--- lib/worktree.c
+++ lib/worktree.c
@@ -1942,8 +1942,20 @@ update_blob(struct got_worktree *worktree,
 		if (status == GOT_STATUS_MISSING || status == GOT_STATUS_DELETE)
 			sb.st_mode = got_fileindex_perms_to_st(ie);
 	} else {
-		sb.st_mode = GOT_DEFAULT_FILE_MODE;
-		status = GOT_STATUS_UNVERSIONED;
+		if (stat(ondisk_path, &sb) == -1) {
+			if (errno != ENOENT) {
+				err = got_error_from_errno2("stat",
+				    ondisk_path);
+				goto done;
+			}
+			sb.st_mode = GOT_DEFAULT_FILE_MODE;
+			status = GOT_STATUS_UNVERSIONED;
+		} else {
+			if (S_ISREG(sb.st_mode) || S_ISLNK(sb.st_mode))
+				status = GOT_STATUS_UNVERSIONED;
+			else
+				status = GOT_STATUS_OBSTRUCTED;
+		}
 	}
 
 	if (status == GOT_STATUS_OBSTRUCTED) {
blob - cae602420f097ed69b049e9fffb3fc46510d2de5
file + regress/cmdline/update.sh
--- regress/cmdline/update.sh
+++ regress/cmdline/update.sh
@@ -2588,6 +2588,8 @@ test_update_file_skipped_due_to_obstruction() {
 	blob_id0=`get_blob_id $testroot/repo "" beta`
 
 	echo "changed beta" > $testroot/repo/beta
+	echo "new file" > $testroot/repo/new
+	(cd $testroot/repo && git add new)
 	git_commit $testroot/repo -m "changed beta"
 	local commit_id1=`git_show_head $testroot/repo`
 	blob_id1=`get_blob_id $testroot/repo "" beta`
@@ -2617,14 +2619,22 @@ test_update_file_skipped_due_to_obstruction() {
 
 	rm $testroot/wt/beta
 	mkdir -p $testroot/wt/beta/psi
+	mkdir -p $testroot/wt/new
 
-	# update to the latest commit; this skips beta
+	# update to the latest commit; this skips beta and the new file
 	(cd $testroot/wt && got update > $testroot/stdout)
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		echo "update failed unexpectedly" >&2
+		test_done "$testroot" "1"
+		return 1
+	fi
 
 	echo "~  beta" > $testroot/stdout.expected
+	echo "~  new" >> $testroot/stdout.expected
 	echo "Updated to refs/heads/master: $commit_id1" \
 		>> $testroot/stdout.expected
-	echo "File paths obstructed by a non-regular file: 1" \
+	echo "File paths obstructed by a non-regular file: 2" \
 		>> $testroot/stdout.expected
 	cmp -s $testroot/stdout.expected $testroot/stdout
 	ret="$?"
@@ -2656,8 +2666,11 @@ test_update_file_skipped_due_to_obstruction() {
 	# updating to the latest commit should now update beta
 	(cd $testroot/wt && got update > $testroot/stdout)
 	echo "!  beta" > $testroot/stdout.expected
+	echo "~  new" >> $testroot/stdout.expected
 	echo "Updated to refs/heads/master: $commit_id1" \
 		>> $testroot/stdout.expected
+	echo "File paths obstructed by a non-regular file: 1" \
+		>> $testroot/stdout.expected
 	cmp -s $testroot/stdout.expected $testroot/stdout
 	ret="$?"
 	if [ "$ret" != "0" ]; then