commit 17ed46186c2a79ae984817ddcdab9f803f23636a from: Stefan Sperling date: Sun Jun 02 19:24:17 2019 UTC allow removing multiple paths at once for 'got rm' commit - 92228c38cdd5d16ccd0505c2580c3a6dc7e3c10b commit + 17ed46186c2a79ae984817ddcdab9f803f23636a blob - 8f54d73de965624d4580dfe1b34497c3bbc299ee blob + aca7b79e0e5080b9d461a192939085492d710c60 --- TODO +++ TODO @@ -1,7 +1,6 @@ lib: - handle checkout of trees which contain submodules by identifying and ignoring such tree entries; requires a .ini config parser (from isakmpd?) -- allow removing multiple paths at once for 'got rm' - allow adding directory paths with 'got add' - recursive addition: got add -R - recursive removal: got rm -R blob - 524d8eee79bef0ea730e3b19bbcda19891667168 blob + 4790d3daa456337e94fc78e750586101b308afa2 --- got/got.1 +++ got/got.1 @@ -310,8 +310,8 @@ Delete the reference with the specified name from the .It Cm add Ar file-path ... Schedule unversioned files in a work tree for addition to the repository in the next commit. -.It Cm rm Ar file-path -Remove a versioned file from a work tree and schedule it for deletion +.It Cm rm Ar file-path ... +Remove versioned files from a work tree and schedule them for deletion from the repository in the next commit. .Pp The options for @@ -319,7 +319,7 @@ The options for are as follows: .Bl -tag -width Ds .It Fl f -Perform the operation even if the file contains uncommitted modifications. +Perform the operation even if a file contains uncommitted modifications. .El .It Cm revert Ar file-path Revert any uncommited changes in the file at the specified path. blob - fd6712a8a82ee0d50d4a511136d1704b36783aa5 blob + 5b82d9b91538794dff2694713b051107efd74e9e --- got/got.c +++ got/got.c @@ -2215,9 +2215,13 @@ cmd_rm(int argc, char *argv[]) const struct got_error *error = NULL; struct got_worktree *worktree = NULL; struct got_repository *repo = NULL; - char *cwd = NULL, *path = NULL; - int ch, delete_local_mods = 0; + char *cwd = NULL; + struct got_pathlist_head paths; + struct got_pathlist_entry *pe; + int ch, i, delete_local_mods = 0; + TAILQ_INIT(&paths); + while ((ch = getopt(argc, argv, "f")) != -1) { switch (ch) { case 'f': @@ -2232,15 +2236,18 @@ cmd_rm(int argc, char *argv[]) argc -= optind; argv += optind; - if (argc != 1) + if (argc < 1) usage_rm(); - path = realpath(argv[0], NULL); - if (path == NULL) { - error = got_error_from_errno2("realpath", argv[0]); - goto done; + /* make sure each file exists before doing anything halfway */ + for (i = 0; i < argc; i++) { + char *path = realpath(argv[i], NULL); + if (path == NULL) { + error = got_error_from_errno2("realpath", argv[i]); + goto done; + } + free(path); } - got_path_strip_trailing_slashes(path); cwd = getcwd(NULL, 0); if (cwd == NULL) { @@ -2260,8 +2267,22 @@ cmd_rm(int argc, char *argv[]) if (error) goto done; - error = got_worktree_schedule_delete(worktree, path, delete_local_mods, - print_status, NULL, repo); + for (i = 0; i < argc; i++) { + char *path = realpath(argv[i], NULL); + if (path == NULL) { + error = got_error_from_errno2("realpath", argv[i]); + goto done; + } + + got_path_strip_trailing_slashes(path); + error = got_pathlist_insert(&pe, &paths, path, NULL); + if (error) { + free(path); + goto done; + } + } + error = got_worktree_schedule_delete(worktree, &paths, + delete_local_mods, print_status, NULL, repo); if (error) goto done; done: @@ -2269,7 +2290,9 @@ done: got_repo_close(repo); if (worktree) got_worktree_close(worktree); - free(path); + TAILQ_FOREACH(pe, &paths, entry) + free((char *)pe->path); + got_pathlist_free(&paths); free(cwd); return error; } blob - 5dbd06969f53eeda039f83786a72a2a24d10e5bf blob + 91899731f8655f5772579fe3e40e0e2716cb82e3 --- include/got_worktree.h +++ include/got_worktree.h @@ -160,13 +160,14 @@ const struct got_error *got_worktree_schedule_add(stru struct got_repository *); /* - * Remove a file from disk and schedule it to be deleted in the next commit. + * Remove files from disk and schedule them to be deleted in the next commit. * Don't allow deleting files with uncommitted modifications, unless the * parameter 'delete_local_mods' is set. */ const struct got_error * -got_worktree_schedule_delete(struct got_worktree *, const char *, int, - got_worktree_status_cb, void *, struct got_repository *); +got_worktree_schedule_delete(struct got_worktree *, + struct got_pathlist_head *, int, got_worktree_status_cb, void *, + struct got_repository *); /* * Revert a file at the specified path such that it matches its blob - ca87d48087173582492bf1fbb809c2e55b333e27 blob + 39f7d97dd22baa55d4d1314bf1fb2f3a1875a135 --- lib/worktree.c +++ lib/worktree.c @@ -2262,29 +2262,57 @@ done: err = unlockerr; return err; } + +static const struct got_error * +schedule_for_deletion(const char *ondisk_path, struct got_fileindex *fileindex, + const char *relpath, int delete_local_mods, + got_worktree_status_cb status_cb, void *status_arg, + struct got_repository *repo) +{ + const struct got_error *err = NULL; + struct got_fileindex_entry *ie = NULL; + unsigned char status; + struct stat sb; + + ie = got_fileindex_entry_get(fileindex, relpath); + if (ie == NULL) + return got_error(GOT_ERR_BAD_PATH); + err = get_file_status(&status, &sb, ie, ondisk_path, repo); + if (err) + return err; + + if (status != GOT_STATUS_NO_CHANGE) { + if (status == GOT_STATUS_DELETE) + return got_error_set_errno(ENOENT, ondisk_path); + if (status != GOT_STATUS_MODIFY) + return got_error(GOT_ERR_FILE_STATUS); + if (!delete_local_mods) + return got_error(GOT_ERR_FILE_MODIFIED); + } + + if (unlink(ondisk_path) != 0) + return got_error_from_errno2("unlink", ondisk_path); + + got_fileindex_entry_mark_deleted_from_disk(ie); + return report_file_status(ie, ondisk_path, status_cb, status_arg, repo); +} + const struct got_error * got_worktree_schedule_delete(struct got_worktree *worktree, - const char *ondisk_path, int delete_local_mods, + struct got_pathlist_head *ondisk_paths, int delete_local_mods, got_worktree_status_cb status_cb, void *status_arg, struct got_repository *repo) { struct got_fileindex *fileindex = NULL; - struct got_fileindex_entry *ie = NULL; - char *relpath, *fileindex_path ; + char *fileindex_path = NULL; FILE *index = NULL; const struct got_error *err = NULL, *unlockerr = NULL; - unsigned char status; - struct stat sb; + struct got_pathlist_entry *pe; err = lock_worktree(worktree, LOCK_EX); if (err) return err; - - err = got_path_skip_common_ancestor(&relpath, - got_worktree_get_root_path(worktree), ondisk_path); - if (err) - goto done; fileindex = got_fileindex_alloc(); if (fileindex == NULL) { @@ -2309,45 +2337,23 @@ got_worktree_schedule_delete(struct got_worktree *work if (err) goto done; - ie = got_fileindex_entry_get(fileindex, relpath); - if (ie == NULL) { - err = got_error(GOT_ERR_BAD_PATH); - goto done; - } - - err = get_file_status(&status, &sb, ie, ondisk_path, repo); - if (err) - goto done; - - if (status != GOT_STATUS_NO_CHANGE) { - if (status == GOT_STATUS_DELETE) { - err = got_error_set_errno(ENOENT, ondisk_path); - goto done; - } - if (status != GOT_STATUS_MODIFY) { - err = got_error(GOT_ERR_FILE_STATUS); + TAILQ_FOREACH(pe, ondisk_paths, entry) { + char *relpath; + err = got_path_skip_common_ancestor(&relpath, + got_worktree_get_root_path(worktree), pe->path); + if (err) goto done; - } - if (!delete_local_mods) { - err = got_error(GOT_ERR_FILE_MODIFIED); + err = schedule_for_deletion(pe->path, fileindex, relpath, + delete_local_mods, status_cb, status_arg, repo); + free(relpath); + if (err) goto done; - } - } - - if (unlink(ondisk_path) != 0) { - err = got_error_from_errno2("unlink", ondisk_path); - goto done; } - got_fileindex_entry_mark_deleted_from_disk(ie); - err = sync_fileindex(fileindex, fileindex_path); if (err) goto done; - - err = report_file_status(ie, ondisk_path, status_cb, status_arg, repo); done: - free(relpath); if (index) { if (fclose(index) != 0 && err == NULL) err = got_error_from_errno("fclose"); blob - bb717cac07e1b3f367f8a6d63f607e748587f356 blob + 34059af1fb4817e58dffc51d50920a50a7eb7522 --- regress/cmdline/rm.sh +++ regress/cmdline/rm.sh @@ -26,22 +26,37 @@ function test_rm_basic { return 1 fi - echo 'D beta' > $testroot/stdout.expected - (cd $testroot/wt && got rm beta > $testroot/stdout) + echo 'D alpha' > $testroot/stdout.expected + echo 'D beta' >> $testroot/stdout.expected + (cd $testroot/wt && got rm alpha beta > $testroot/stdout) 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 - if [ -e $testroot/wt/beta ]; then - echo "removed file beta still exists on disk" >&2 - test_done "$testroot" "1" + (cd $testroot/wt && got status > $testroot/stdout) + + 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" + for f in alpha beta; do + if [ -e $testroot/wt/$f ]; then + echo "removed file $f still exists on disk" >&2 + test_done "$testroot" "1" + return 1 + fi + done + + test_done "$testroot" "0" } function test_rm_with_local_mods {