Commit Diff

Commit:
7f4e5320512ed5a2b28d740e0dc25cde52ba1876
From:
Stefan Sperling <stsp@stsp.name>
Date:
Mon May 29 14:50:36 2023 UTC
Message:
only delete empty directories appearing in arguments to 'got rm' Make 'got rm' keep empty directories which are not explicitly listed for deletion. Deleting such directories is problematic in several use cases. Avoids deleting the current working directory when the user runs "got rm *" (pointed out by Mikhail), and avoids deletion of an empty directory "foo/" after 'got rm foo/a foo/b' (pointed out by op@). ok jamsek, op

commit - 6c685612338950f89dc47cd0ef36bcd65fe6404f
commit + 7f4e5320512ed5a2b28d740e0dc25cde52ba1876
blob - 622c0ce1ca669ed14ca0d26259483e9beea95863
blob + 9d45bcc89fba03349491b41c260b4fc0c9606531
--- lib/worktree.c
+++ lib/worktree.c
@@ -4312,6 +4312,8 @@ struct schedule_deletion_args {
 	int delete_local_mods;
 	int keep_on_disk;
 	int ignore_missing_paths;
+	const char *status_path;
+	size_t status_path_len;
 	const char *status_codes;
 };
 
@@ -4410,11 +4412,17 @@ schedule_for_deletion(void *arg, unsigned char status,
 		root_len = strlen(a->worktree->root_path);
 		do {
 			char *parent;
+
 			err = got_path_dirname(&parent, ondisk_path);
 			if (err)
 				goto done;
 			free(ondisk_path);
 			ondisk_path = parent;
+			if (got_path_cmp(ondisk_path, a->status_path,
+			    strlen(ondisk_path), a->status_path_len) != 0 &&
+			    !got_path_is_child(ondisk_path, a->status_path,
+			    a->status_path_len))
+				break;
 			if (rmdir(ondisk_path) == -1) {
 				if (errno != ENOTEMPTY)
 					err = got_error_from_errno2("rmdir",
@@ -4468,8 +4476,19 @@ got_worktree_schedule_delete(struct got_worktree *work
 	sda.status_codes = status_codes;
 
 	TAILQ_FOREACH(pe, paths, entry) {
+		char *ondisk_status_path;
+
+		if (asprintf(&ondisk_status_path, "%s%s%s",
+		    got_worktree_get_root_path(worktree),
+		    pe->path[0] == '\0' ? "" : "/", pe->path) == -1) {
+			err = got_error_from_errno("asprintf");
+			goto done;
+		}
+		sda.status_path = ondisk_status_path;
+		sda.status_path_len = strlen(ondisk_status_path);
 		err = worktree_status(worktree, pe->path, fileindex, repo,
 			schedule_for_deletion, &sda, NULL, NULL, 1, 1);
+		free(ondisk_status_path);
 		if (err)
 			break;
 	}
@@ -9838,7 +9857,9 @@ got_worktree_patch_schedule_rm(const char *path, struc
     struct got_worktree *worktree, struct got_fileindex *fileindex,
     got_worktree_delete_cb progress_cb, void *progress_arg)
 {
+	const struct got_error *err;
 	struct schedule_deletion_args sda;
+	char *ondisk_status_path;
 
 	memset(&sda, 0, sizeof(sda));
 	sda.worktree = worktree;
@@ -9850,9 +9871,16 @@ got_worktree_patch_schedule_rm(const char *path, struc
 	sda.keep_on_disk = 0;
 	sda.ignore_missing_paths = 0;
 	sda.status_codes = NULL;
-
-	return worktree_status(worktree, path, fileindex, repo,
+	if (asprintf(&ondisk_status_path, "%s/%s",
+	    got_worktree_get_root_path(worktree), path) == -1)
+		return got_error_from_errno("asprintf");
+	sda.status_path = ondisk_status_path;
+	sda.status_path_len = strlen(ondisk_status_path);
+
+	err = worktree_status(worktree, path, fileindex, repo,
 	    schedule_for_deletion, &sda, NULL, NULL, 1, 1);
+	free(ondisk_status_path);
+	return err;
 }
 
 const struct got_error *