Commit d1f6d47b18456c7e66b2076c063fa2a3d0fa5d40

Stefan Sperling 2019-02-04T11:17:36

add support for diffing fileindex against an on-disk directory tree

diff --git a/lib/fileindex.c b/lib/fileindex.c
index 752d228..d25609a 100644
--- a/lib/fileindex.c
+++ b/lib/fileindex.c
@@ -18,6 +18,7 @@
 #include <sys/tree.h>
 #include <sys/stat.h>
 
+#include <dirent.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -690,4 +691,97 @@ got_fileindex_diff_tree(struct got_fileindex *fileindex,
 	return diff_fileindex_tree(fileindex, &min, tree, "", repo, cb, cb_arg);
 }
 
+static const struct got_error *
+diff_fileindex_dir(struct got_fileindex *, struct got_fileindex_entry **, DIR *,
+    const char *, struct got_repository *, struct got_fileindex_diff_dir_cb *,
+    void *);
+
+static const struct got_error *
+walk_dir(struct dirent **next, struct got_fileindex *fileindex,
+    struct got_fileindex_entry **ie, struct dirent *de, const char *path,
+    DIR *dir, struct got_repository *repo,
+    struct got_fileindex_diff_dir_cb *cb, void *cb_arg)
+{
+	const struct got_error *err = NULL;
+
+	if (de->d_type == DT_DIR) {
+		char *subpath;
+		DIR *subdir;
+
+		if (asprintf(&subpath, "%s%s%s", path,
+		    path[0] == '\0' ? "" : "/", de->d_name) == -1)
+			return got_error_from_errno();
+
+		subdir = opendir(subpath);
+		if (subdir == NULL) {
+			free(subpath);
+			return got_error_from_errno();
+		}
+
+		err = diff_fileindex_dir(fileindex, ie, subdir, subpath, repo,
+		    cb, cb_arg);
+		free(subpath);
+		closedir(subdir);
+		if (err)
+			return err;
+	}
+
+	*next = readdir(dir);
+	return NULL;
+}
+
+static const struct got_error *
+diff_fileindex_dir(struct got_fileindex *fileindex,
+    struct got_fileindex_entry **ie, DIR *dir, const char *path,
+    struct got_repository *repo, struct got_fileindex_diff_dir_cb *cb,
+    void *cb_arg)
+{
+	const struct got_error *err = NULL;
+	struct dirent *de = NULL;
+	size_t path_len = strlen(path);
+	struct got_fileindex_entry *next;
+
+	de = readdir(dir);
+	do {
+		if (de && *ie) {
+			int cmp = cmp_entries((*ie)->path, path, path_len,
+			    de->d_name);
+			if (cmp == 0) {
+				err = cb->diff_old_new(cb_arg, *ie, de, path);
+				if (err)
+					break;
+				*ie = walk_fileindex(fileindex, *ie);
+				err = walk_dir(&de, fileindex, ie, de, path,
+				    dir, repo, cb, cb_arg);
+			} else if (cmp < 0 ) {
+				next = walk_fileindex(fileindex, *ie);
+				err = cb->diff_old(cb_arg, *ie, path);
+				if (err)
+					break;
+				*ie = next;
+			} else {
+				err = cb->diff_new(cb_arg, de, path);
+				if (err)
+					break;
+				err = walk_dir(&de, fileindex, ie, de, path,
+				    dir, repo, cb, cb_arg);
+			}
+			if (err)
+				break;
+		}
+	} while ((*ie && got_path_is_child((*ie)->path, path, path_len)) || de);
+
+	return err;
+}
+
+const struct got_error *
+got_fileindex_diff_dir(struct got_fileindex *fileindex, DIR *dir,
+    struct got_repository *repo, struct got_fileindex_diff_dir_cb *cb,
+    void *cb_arg)
+{
+	struct got_fileindex_entry *min;
+	min = RB_MIN(got_fileindex_tree, &fileindex->entries);
+	return diff_fileindex_dir(fileindex, &min, dir, "", repo, cb, cb_arg);
+}
+
 RB_GENERATE(got_fileindex_tree, got_fileindex_entry, entry, got_fileindex_cmp);
diff --git a/lib/got_lib_fileindex.h b/lib/got_lib_fileindex.h
index 0336dd7..12d5ee2 100644
--- a/lib/got_lib_fileindex.h
+++ b/lib/got_lib_fileindex.h
@@ -125,7 +125,20 @@ struct got_fileindex_diff_tree_cb {
 	got_fileindex_diff_tree_old_cb diff_old;
 	got_fileindex_diff_tree_new_cb diff_new;
 };
-
 const struct got_error *got_fileindex_diff_tree(struct got_fileindex *,
     struct got_tree_object *, struct got_repository *,
     struct got_fileindex_diff_tree_cb *, void *);
+
+typedef const struct got_error *(*got_fileindex_diff_dir_old_new_cb)(void *,
+    struct got_fileindex_entry *, struct dirent *, const char *);
+typedef const struct got_error *(*got_fileindex_diff_dir_old_cb)(void *,
+    struct got_fileindex_entry *, const char *);
+typedef const struct got_error *(*got_fileindex_diff_dir_new_cb)(void *,
+    struct dirent *, const char *);
+struct got_fileindex_diff_dir_cb {
+	got_fileindex_diff_dir_old_new_cb diff_old_new;
+	got_fileindex_diff_dir_old_cb diff_old;
+	got_fileindex_diff_dir_new_cb diff_new;
+};
+const struct got_error *got_fileindex_diff_dir(struct got_fileindex *, DIR *,
+    struct got_repository *, struct got_fileindex_diff_dir_cb *, void *);
diff --git a/lib/worktree.c b/lib/worktree.c
index 62fe586..1d33ed0 100644
--- a/lib/worktree.c
+++ b/lib/worktree.c
@@ -19,6 +19,7 @@
 #include <sys/queue.h>
 #include <sys/tree.h>
 
+#include <dirent.h>
 #include <stddef.h>
 #include <string.h>
 #include <stdio.h>