commit - 3b4d3732e625911f517e47976088f6a23d4935b8
commit + 512f0d0efcba9d2e0925fd76670087a383c0bf6f
blob - 1424a427f623a992ad42ee5b418df6c7f7b3c687
blob + 689be6253bdaeef913a9fe3b3ec17ee50d342005
--- include/got_worktree.h
+++ include/got_worktree.h
#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
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)
{
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
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
#include <sha1.h>
#include <zlib.h>
#include <fnmatch.h>
+#include <libgen.h>
#include "got_error.h"
#include "got_repository.h"
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;
}
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
(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
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