Commit a378724ff7c52fed47ca10ec7bb827ad4955ce57

Stefan Sperling 2019-02-10T13:20:43

make 'got update' restore missing files

diff --git a/got/got.1 b/got/got.1
index 914b39f..1ce550d 100644
--- a/got/got.1
+++ b/got/got.1
@@ -105,6 +105,7 @@ Show the status of each affected file, using the following status codes:
 .It D Ta file was deleted
 .It A Ta new file was added
 .It ~ Ta versioned file is obstructed by a non-regular file
+.It ! Ta a missing versioned file was restored
 .El
 .Pp
 If the
diff --git a/got/got.c b/got/got.c
index 755f467..709848c 100644
--- a/got/got.c
+++ b/got/got.c
@@ -436,7 +436,8 @@ update_progress(void *arg, unsigned char status, const char *path)
 	if (status == GOT_STATUS_EXISTS)
 		return;
 
-	*did_something = 1;
+	if (status != GOT_STATUS_MISSING)
+		*did_something = 1;
 	while (path[0] == '/')
 		path++;
 	printf("%c  %s\n", status, path);
diff --git a/lib/worktree.c b/lib/worktree.c
index f8449d3..893ba6c 100644
--- a/lib/worktree.c
+++ b/lib/worktree.c
@@ -697,7 +697,7 @@ done:
 static const struct got_error *
 install_blob(struct got_worktree *worktree, struct got_fileindex *fileindex,
    struct got_fileindex_entry *entry, const char *ondisk_path, const char *path,
-   uint16_t mode, struct got_blob_object *blob,
+   uint16_t mode, struct got_blob_object *blob, int restoring_missing_file,
    struct got_repository *repo, got_worktree_checkout_cb progress_cb,
    void *progress_arg)
 {
@@ -742,8 +742,11 @@ install_blob(struct got_worktree *worktree, struct got_fileindex *fileindex,
 			return got_error_from_errno();
 	}
 
-	(*progress_cb)(progress_arg,
-	    update ? GOT_STATUS_UPDATE : GOT_STATUS_ADD, path);
+	if (restoring_missing_file)
+		(*progress_cb)(progress_arg, GOT_STATUS_MISSING, path);
+	else
+		(*progress_cb)(progress_arg,
+		    update ? GOT_STATUS_UPDATE : GOT_STATUS_ADD, path);
 
 	hdrlen = got_object_blob_get_hdrlen(blob);
 	do {
@@ -826,8 +829,13 @@ get_file_status(unsigned char *status, struct got_fileindex_entry *ie,
 
 	*status = GOT_STATUS_NO_CHANGE;
 
-	if (lstat(abspath, &sb) == -1)
+	if (lstat(abspath, &sb) == -1) {
+		if (errno == ENOENT) {
+			*status = GOT_STATUS_MISSING;
+			return NULL;
+		}
 		return got_error_from_errno();
+	}
 
 	if (!S_ISREG(sb.st_mode)) {
 		*status = GOT_STATUS_OBSTRUCTED;
@@ -903,16 +911,6 @@ update_blob(struct got_worktree *worktree,
 		return got_error_from_errno();
 
 	if (ie) {
-		if (memcmp(ie->commit_sha1, worktree->base_commit_id->sha1,
-		    SHA1_DIGEST_LENGTH) == 0) {
-			(*progress_cb)(progress_arg, GOT_STATUS_EXISTS,
-			    path);
-			goto done;
-		}
-		if (memcmp(ie->blob_sha1,
-		    te->id->sha1, SHA1_DIGEST_LENGTH) == 0)
-			goto done;
-
 		err = get_file_status(&status, ie, ondisk_path, repo);
 		if (err)
 			goto done;
@@ -921,6 +919,19 @@ update_blob(struct got_worktree *worktree,
 			(*progress_cb)(progress_arg, status, path);
 			goto done;
 		}
+
+		if (status == GOT_STATUS_NO_CHANGE) {
+			if (memcmp(ie->commit_sha1,
+			    worktree->base_commit_id->sha1,
+			    SHA1_DIGEST_LENGTH) == 0) {
+				(*progress_cb)(progress_arg, GOT_STATUS_EXISTS,
+				    path);
+				goto done;
+			}
+			if (memcmp(ie->blob_sha1,
+			    te->id->sha1, SHA1_DIGEST_LENGTH) == 0)
+				goto done;
+		}
 	}
 
 	err = got_object_open_as_blob(&blob, repo, te->id, 8192);
@@ -932,7 +943,8 @@ update_blob(struct got_worktree *worktree,
 		    te->mode, blob, repo, progress_cb, progress_arg);
 	else
 		err = install_blob(worktree, fileindex, ie, ondisk_path, path,
-		    te->mode, blob, repo, progress_cb, progress_arg);
+		    te->mode, blob, status == GOT_STATUS_MISSING, repo,
+		    progress_cb, progress_arg);
 
 	got_object_blob_close(blob);
 done:
diff --git a/regress/cmdline/update.sh b/regress/cmdline/update.sh
index e7ae6c1..3267b8e 100755
--- a/regress/cmdline/update.sh
+++ b/regress/cmdline/update.sh
@@ -810,6 +810,42 @@ function test_update_clears_xbit {
 	test_done "$testroot" "$ret"
 }
 
+function test_update_restores_missing_file {
+	local testroot=`test_init update_restores_missing_file`
+
+	got checkout $testroot/repo $testroot/wt > /dev/null
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	rm $testroot/wt/alpha
+
+	echo "!  alpha" > $testroot/stdout.expected
+	echo "Already up-to-date" >> $testroot/stdout.expected
+	(cd $testroot/wt && got update > $testroot/stdout)
+
+	cmp $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 "alpha" > $testroot/content.expected
+
+	cat $testroot/wt/alpha > $testroot/content
+
+	cmp $testroot/content.expected $testroot/content
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/content.expected $testroot/content
+	fi
+	test_done "$testroot" "$ret"
+}
+
 run_test test_update_basic
 run_test test_update_adds_file
 run_test test_update_deletes_file
@@ -826,3 +862,4 @@ run_test test_update_file_in_subsubdir
 run_test test_update_merges_file_edits
 run_test test_update_keeps_xbit
 run_test test_update_clears_xbit
+run_test test_update_restores_missing_file