commit 512f0d0efcba9d2e0925fd76670087a383c0bf6f from: Stefan Sperling date: Wed Jan 02 23:57:49 2019 UTC make 'got update' remove files which were deleted commit - 3b4d3732e625911f517e47976088f6a23d4935b8 commit + 512f0d0efcba9d2e0925fd76670087a383c0bf6f blob - 1424a427f623a992ad42ee5b418df6c7f7b3c687 blob + 689be6253bdaeef913a9fe3b3ec17ee50d342005 --- include/got_worktree.h +++ include/got_worktree.h @@ -20,6 +20,7 @@ struct got_worktree; #define GOT_STATUS_ADD 'A' #define GOT_STATUS_EXISTS 'E' #define GOT_STATUS_UPDATE 'U' +#define GOT_STATUS_DELETE 'D' /* * Attempt to initialize a new work tree on disk. blob - f49097e88b26a862b3ef59f17f2fc1be309b880d blob + dbe625f9575b42c5baeca2ec33f1d9a00b3d6b52 --- lib/fileindex.c +++ lib/fileindex.c @@ -100,6 +100,14 @@ got_fileindex_entry_add(struct got_fileindex *fileinde return NULL; } +void +got_fileindex_entry_remove(struct got_fileindex *fileindex, + struct got_fileindex_entry *entry) +{ + TAILQ_REMOVE(&fileindex->entries, entry, entry); + fileindex->nentries--; +} + struct got_fileindex_entry * got_fileindex_entry_get(struct got_fileindex *fileindex, const char *path) { @@ -112,6 +120,23 @@ got_fileindex_entry_get(struct got_fileindex *fileinde return NULL; } +const struct got_error * +got_fileindex_for_each_entry(struct got_fileindex *fileindex, + const struct got_error *(cb)(void *, struct got_fileindex_entry *), + void *cb_arg) +{ + const struct got_error *err = NULL; + struct got_fileindex_entry *entry; + + TAILQ_FOREACH(entry, &fileindex->entries, entry) { + err = cb(cb_arg, entry); + if (err) + break; + } + + return err; +} + struct got_fileindex * got_fileindex_alloc(void) { blob - 78770a187a8aeab06554502e2672852c6e8e833b blob + f93174f7e634e06da5a522e80f1baba31dcca4e0 --- lib/got_lib_fileindex.h +++ lib/got_lib_fileindex.h @@ -95,6 +95,10 @@ void got_fileindex_free(struct got_fileindex *); const struct got_error *got_fileindex_write(struct got_fileindex *, FILE *); const struct got_error *got_fileindex_entry_add(struct got_fileindex *, struct got_fileindex_entry *); +void got_fileindex_entry_remove(struct got_fileindex *, + struct got_fileindex_entry *); struct got_fileindex_entry *got_fileindex_entry_get(struct got_fileindex *, const char *); const struct got_error *got_fileindex_read(struct got_fileindex *, FILE *); +const struct got_error *got_fileindex_for_each_entry(struct got_fileindex *, + const struct got_error *(cb)(void *, struct got_fileindex_entry *), void *); blob - 94e0615878acd046d8424d079eb4ad72548eee1d blob + 79520bd02c0689c8b16ebcc2c3a0193916f8bd27 --- lib/worktree.c +++ lib/worktree.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "got_error.h" #include "got_repository.h" @@ -738,6 +739,105 @@ done: if (obj) got_object_close(obj); free(path); + return err; +} + +struct collect_missing_entry_args { + struct got_fileindex *fileindex; + const struct got_tree_entries *entries; + struct got_fileindex missing_entries; + const char *path_prefix; +}; + +static const struct got_error * +collect_missing_file(void *args, struct got_fileindex_entry *entry) +{ + struct collect_missing_entry_args *a = args; + char *name; + struct got_tree_entry *te; + int found = 0; + + if (a->path_prefix[0] == '\0' && strchr(entry->path, '/') != NULL) + return NULL; + if (a->path_prefix[0] != '\0' && + strncmp(a->path_prefix, entry->path, strlen(a->path_prefix)) != 0) + return NULL; + + name = basename(entry->path); + if (name == NULL) + return got_error_from_errno(); + + SIMPLEQ_FOREACH(te, &a->entries->head, entry) { + if (strcmp(te->name, name) == 0) { + found = 1; + break; + } + } + + if (found) + return NULL; + + got_fileindex_entry_remove(a->fileindex, entry); + return got_fileindex_entry_add(&a->missing_entries, entry); +} + +/* Remove files which exist in the file index but not in the tree. */ +static const struct got_error * +remove_missing_files(struct got_worktree *worktree, const char *path, + struct got_fileindex *fileindex, const struct got_tree_entries *entries, + got_worktree_checkout_cb progress_cb, void *progress_arg, + got_worktree_cancel_cb cancel_cb, void *cancel_arg) +{ + const struct got_error *err = NULL; + struct collect_missing_entry_args a; + struct got_fileindex_entry *entry, *tmp; + + a.fileindex = fileindex; + a.entries = entries; + a.missing_entries.nentries = 0; + a.path_prefix = path; + while (a.path_prefix[0] == '/') + a.path_prefix++; + TAILQ_INIT(&a.missing_entries.entries); + err = got_fileindex_for_each_entry(fileindex, collect_missing_file, &a); + if (err) + return err; + + TAILQ_FOREACH_SAFE(entry, &a.missing_entries.entries, entry, tmp) { + char *ondisk_path = NULL; + + if (cancel_cb) { + err = (*cancel_cb)(cancel_arg); + if (err) + break; + } + + (*progress_cb)(progress_arg, GOT_STATUS_DELETE, entry->path); + + if (asprintf(&ondisk_path, "%s/%s", worktree->root_path, + entry->path) == -1) { + err = got_error_from_errno(); + break; + } + + if (unlink(ondisk_path) == -1) + err = got_error_from_errno(); + free(ondisk_path); + if (err) + break; + + TAILQ_REMOVE(&a.missing_entries.entries, entry, entry); + got_fileindex_entry_free(entry); + } + + if (err) { + while (!TAILQ_EMPTY(&a.missing_entries.entries)) { + entry = TAILQ_FIRST(&a.missing_entries.entries); + TAILQ_REMOVE(&a.missing_entries.entries, entry, entry); + got_fileindex_entry_free(entry); + } + } + return err; } @@ -763,14 +863,22 @@ tree_checkout(struct got_worktree *worktree, if (cancel_cb) { err = (*cancel_cb)(cancel_arg); if (err) - break; + return err; } err = tree_checkout_entry(worktree, fileindex, te, path, repo, progress_cb, progress_arg, cancel_cb, cancel_arg); if (err) - break; + return err; } + len = strlen(worktree->path_prefix); + if (strncmp(worktree->path_prefix, path, len) == 0) { + err = remove_missing_files(worktree, path, fileindex, entries, + progress_cb, progress_arg, cancel_cb, cancel_arg); + if (err) + return err; + } + return err; } blob - 00ace25b2ca2a630ed219a9d70ac227dd984d3ce blob + a8a3ea579510a1fb52d1335755b07681391a398d --- regress/cmdline/common.sh +++ regress/cmdline/common.sh @@ -26,6 +26,13 @@ function git_commit (cd $repo && git commit -q -a "$@") } +function git_rm +{ + local repo="$1" + shift + (cd $repo && git rm -q "$@") +} + function git_show_head { local repo="$1" blob - f5525ccdcb63c9d66621fca292548f7f2a0c72c1 blob + 644f3de5670a7abe8e3549b13c5675ce0a506b2d --- regress/cmdline/update.sh +++ regress/cmdline/update.sh @@ -98,5 +98,50 @@ function test_update_adds_file { test_done "$testroot" "$?" } +function test_update_deletes_file { + local testroot=`test_init update_deletes_file` + + got checkout $testroot/repo $testroot/wt > /dev/null + if [ "$?" != "0" ]; then + test_done "$testroot" "$?" + return 1 + fi + + (cd $testroot/repo && git_rm $testroot/repo beta) + git_commit $testroot/repo -m "deleting a file" + + echo "D beta" > $testroot/stdout.expected + echo -n "Updated to commit " >> $testroot/stdout.expected + git_show_head $testroot/repo >> $testroot/stdout.expected + echo >> $testroot/stdout.expected + + (cd $testroot/wt && got update > $testroot/stdout) + + cmp $testroot/stdout.expected $testroot/stdout + if [ "$?" != "0" ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$?" + return 1 + fi + + if [ -e $testroot/wt/beta ]; then + echo "removed file beta still exists on disk" >&2 + return 1 + fi + + echo "alpha" >> $testroot/content.expected + echo "zeta" >> $testroot/content.expected + echo "delta" >> $testroot/content.expected + cat $testroot/wt/alpha $testroot/wt/epsilon/zeta \ + $testroot/wt/gamma/delta > $testroot/content + + cmp $testroot/content.expected $testroot/content + if [ "$?" != "0" ]; then + diff -u $testroot/content.expected $testroot/content + fi + test_done "$testroot" "$?" +} + run_test test_update_basic run_test test_update_adds_file +run_test test_update_deletes_file