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