Commit 1dd5492090bfc3d9e553b4306a3cca1f03adaa71

Stefan Sperling 2019-05-11T18:50:57

open the file index just once when adding multiple files

diff --git a/got/got.c b/got/got.c
index 73e05dd..d99ceb0 100644
--- a/got/got.c
+++ b/got/got.c
@@ -35,12 +35,12 @@
 #include "got_object.h"
 #include "got_reference.h"
 #include "got_repository.h"
+#include "got_path.h"
 #include "got_worktree.h"
 #include "got_diff.h"
 #include "got_commit_graph.h"
 #include "got_blame.h"
 #include "got_privsep.h"
-#include "got_path.h"
 
 #ifndef nitems
 #define nitems(_a)	(sizeof((_a)) / sizeof((_a)[0]))
@@ -1896,9 +1896,13 @@ cmd_add(int argc, char *argv[])
 	const struct got_error *error = NULL;
 	struct got_repository *repo = NULL;
 	struct got_worktree *worktree = NULL;
-	char *cwd = NULL, *path = NULL, *relpath = NULL;
+	char *cwd = NULL;
+	struct got_pathlist_head paths;
+	struct got_pathlist_entry *pe;
 	int ch, x;
 
+	TAILQ_INIT(&paths);
+
 	while ((ch = getopt(argc, argv, "")) != -1) {
 		switch (ch) {
 		default:
@@ -1915,11 +1919,12 @@ cmd_add(int argc, char *argv[])
 
 	/* make sure each file exists before doing anything halfway */
 	for (x = 0; x < argc; x++) {
-		path = realpath(argv[x], NULL);
+		char *path = realpath(argv[x], NULL);
 		if (path == NULL) {
 			error = got_error_prefix_errno2("realpath", argv[x]);
 			goto done;
 		}
+		free(path);
 	}
 
 	cwd = getcwd(NULL, 0);
@@ -1942,30 +1947,29 @@ cmd_add(int argc, char *argv[])
 		goto done;
 
 	for (x = 0; x < argc; x++) {
-		path = realpath(argv[x], NULL);
+		char *path = realpath(argv[x], NULL);
 		if (path == NULL) {
 			error = got_error_prefix_errno2("realpath", argv[x]);
 			goto done;
 		}
 
 		got_path_strip_trailing_slashes(path);
-
-		error = got_worktree_schedule_add(worktree, path, print_status,
-		    NULL, repo);
-		if (errno == EEXIST) {
-			error = NULL;
-			continue;
-		}
-		else if (error)
+		error = got_pathlist_insert(&pe, &paths, path, NULL);
+		if (error) {
+			free(path);
 			goto done;
+		}
 	}
+	error = got_worktree_schedule_add(worktree, &paths, print_status,
+	    NULL, repo);
 done:
 	if (repo)
 		got_repo_close(repo);
 	if (worktree)
 		got_worktree_close(worktree);
-	free(path);
-	free(relpath);
+	TAILQ_FOREACH(pe, &paths, entry)
+		free((char *)pe->path);
+	got_pathlist_free(&paths);
 	free(cwd);
 	return error;
 }
diff --git a/include/got_worktree.h b/include/got_worktree.h
index 0261d10..3c54627 100644
--- a/include/got_worktree.h
+++ b/include/got_worktree.h
@@ -138,9 +138,10 @@ const struct got_error *got_worktree_status(struct got_worktree *,
 const struct got_error *got_worktree_resolve_path(char **,
     struct got_worktree *, const char *);
 
-/* Schedule a file at an on-disk path for addition in the next commit. */
+/* Schedule files at on-disk paths for addition in the next commit. */
 const struct got_error *got_worktree_schedule_add(struct got_worktree *,
-    const char *, got_worktree_status_cb, void *, struct got_repository *);
+    struct got_pathlist_head *, got_worktree_status_cb, void *,
+    struct got_repository *);
 
 /*
  * Remove a file from disk and schedule it to be deleted in the next commit.
diff --git a/lib/repository.c b/lib/repository.c
index 51cd9ab..bb14921 100644
--- a/lib/repository.c
+++ b/lib/repository.c
@@ -38,9 +38,9 @@
 #include "got_error.h"
 #include "got_reference.h"
 #include "got_repository.h"
+#include "got_path.h"
 #include "got_worktree.h"
 #include "got_object.h"
-#include "got_path.h"
 
 #include "got_lib_delta.h"
 #include "got_lib_inflate.h"
diff --git a/lib/worktree.c b/lib/worktree.c
index 3ce4537..da771a2 100644
--- a/lib/worktree.c
+++ b/lib/worktree.c
@@ -38,9 +38,9 @@
 #include "got_repository.h"
 #include "got_reference.h"
 #include "got_object.h"
+#include "got_path.h"
 #include "got_worktree.h"
 #include "got_opentemp.h"
-#include "got_path.h"
 
 #include "got_lib_worktree.h"
 #include "got_lib_sha1.h"
@@ -1818,24 +1818,20 @@ done:
 
 const struct got_error *
 got_worktree_schedule_add(struct got_worktree *worktree,
-    const char *ondisk_path, got_worktree_status_cb status_cb, void *status_arg,
+    struct got_pathlist_head *ondisk_paths,
+    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 = NULL, *new_fileindex_path = NULL;
+	char *fileindex_path = NULL, *new_fileindex_path = NULL;
 	FILE *index = NULL, *new_index = NULL;
 	const struct got_error *err = NULL, *unlockerr = NULL;
-	int ie_added = 0;
+	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) {
@@ -1860,19 +1856,36 @@ got_worktree_schedule_add(struct got_worktree *worktree,
 	if (err)
 		goto done;
 
-	if (got_fileindex_entry_get(fileindex, relpath) != NULL) {
-		err = got_error_set_errno(EEXIST, relpath);
-		goto done;
-	}
+	TAILQ_FOREACH(pe, ondisk_paths, entry) {
+		struct got_fileindex_entry *ie = NULL;
+		char *relpath;
 
-	err = got_fileindex_entry_alloc(&ie, ondisk_path, relpath, NULL, NULL);
-	if (err)
-		goto done;
+		err = got_path_skip_common_ancestor(&relpath,
+		    got_worktree_get_root_path(worktree), pe->path);
+		if (err)
+			goto done;
 
-	err = got_fileindex_entry_add(fileindex, ie);
-	if (err)
-		goto done;
-	ie_added = 1; /* now owned by fileindex; don't free separately */
+		/* Re-adding an existing entry is a no-op. */
+		if (got_fileindex_entry_get(fileindex, relpath) != NULL)
+			continue;
+
+		err = got_fileindex_entry_alloc(&ie, pe->path, relpath,
+		    NULL, NULL);
+		free(relpath);
+		if (err)
+			goto done;
+
+		err = got_fileindex_entry_add(fileindex, ie);
+		if (err) {
+			got_fileindex_entry_free(ie);
+			goto done;
+		}
+
+		err = report_file_status(ie, pe->path, status_cb, status_arg,
+		    repo);
+		if (err)
+			goto done;
+	}
 
 	err = got_opentemp_named(&new_fileindex_path, &new_index,
 	    fileindex_path);
@@ -1892,7 +1905,6 @@ got_worktree_schedule_add(struct got_worktree *worktree,
 	free(new_fileindex_path);
 	new_fileindex_path = NULL;
 
-	err = report_file_status(ie, ondisk_path, status_cb, status_arg, repo);
 done:
 	if (index) {
 		if (fclose(index) != 0 && err == NULL)
@@ -1904,14 +1916,11 @@ done:
 			    new_fileindex_path);
 		free(new_fileindex_path);
 	}
-	if (ie && !ie_added)
-		got_fileindex_entry_free(ie);
 	if (fileindex)
 		got_fileindex_free(fileindex);
 	unlockerr = lock_worktree(worktree, LOCK_SH);
 	if (unlockerr && err == NULL)
 		err = unlockerr;
-	free(relpath);
 	return err;
 }
 
diff --git a/regress/worktree/worktree_test.c b/regress/worktree/worktree_test.c
index 54bb129..df3b5be 100644
--- a/regress/worktree/worktree_test.c
+++ b/regress/worktree/worktree_test.c
@@ -34,10 +34,10 @@
 #include "got_object.h"
 #include "got_reference.h"
 #include "got_repository.h"
+#include "got_path.h"
 #include "got_worktree.h"
 #include "got_opentemp.h"
 #include "got_privsep.h"
-#include "got_path.h"
 
 #include "got_lib_worktree.h"
 
diff --git a/tog/tog.c b/tog/tog.c
index 3f82dba..c3da2f3 100644
--- a/tog/tog.c
+++ b/tog/tog.c
@@ -47,6 +47,7 @@
 #include "got_utf8.h"
 #include "got_blame.h"
 #include "got_privsep.h"
+#include "got_path.h"
 #include "got_worktree.h"
 
 #ifndef MIN