Commit Diff


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 <sha1.h>
 #include <zlib.h>
 #include <fnmatch.h>
+#include <libgen.h>
 
 #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