Commit 599f2849bad898c5dfd212e0629f79783f75b700

Clemens Buchacher 2011-12-26T18:37:31

add git_index_read_tree

diff --git a/include/git2/index.h b/include/git2/index.h
index 5e9c34d..627d6c4 100644
--- a/include/git2/index.h
+++ b/include/git2/index.h
@@ -301,6 +301,17 @@ GIT_EXTERN(const git_index_entry_unmerged *) git_index_get_unmerged_byindex(git_
  */
 GIT_EXTERN(int) git_index_entry_stage(const git_index_entry *entry);
 
+/**
+ * Read a tree into the index file
+ *
+ * The current index contents will be replaced by the specified tree.
+ *
+ * @param index an existing index object
+ * @param tree tree to read
+ * @return GIT_SUCCESS or an error code
+ */
+GIT_EXTERN(int) git_index_read_tree(git_index *index, git_tree *tree);
+
 /** @} */
 GIT_END_DECL
 #endif
diff --git a/src/index.c b/src/index.c
index 43e8efa..9f9a08f 100644
--- a/src/index.c
+++ b/src/index.c
@@ -10,6 +10,7 @@
 #include "common.h"
 #include "repository.h"
 #include "index.h"
+#include "tree.h"
 #include "tree-cache.h"
 #include "hash.h"
 #include "git2/odb.h"
@@ -936,3 +937,44 @@ int git_index_entry_stage(const git_index_entry *entry)
 {
 	return (entry->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT;
 }
+
+static int read_tree_cb(const char *root, git_tree_entry *tentry, void *data)
+{
+	int ret = GIT_SUCCESS;
+	git_index *index = data;
+	git_index_entry *entry = NULL;
+	git_buf path = GIT_BUF_INIT;
+
+	if (entry_is_tree(tentry))
+		goto exit;
+
+	ret = git_buf_joinpath(&path, root, tentry->filename);
+	if (ret < GIT_SUCCESS)
+		goto exit;
+
+	entry = git__calloc(1, sizeof(git_index_entry));
+	if (!entry) {
+		ret = GIT_ENOMEM;
+		goto exit;
+	}
+
+	entry->mode = tentry->attr;
+	entry->oid = tentry->oid;
+	entry->path = git_buf_detach(&path);
+
+	ret = index_insert(index, entry, 0);
+
+exit:
+	git_buf_free(&path);
+
+	if (ret < GIT_SUCCESS)
+		index_entry_free(entry);
+	return ret;
+}
+
+int git_index_read_tree(git_index *index, git_tree *tree)
+{
+	git_index_clear(index);
+
+	return git_tree_walk(tree, read_tree_cb, GIT_TREEWALK_POST, index);
+}
diff --git a/tests-clay/index/read_tree.c b/tests-clay/index/read_tree.c
new file mode 100644
index 0000000..d884c8d
--- /dev/null
+++ b/tests-clay/index/read_tree.c
@@ -0,0 +1,46 @@
+#include "clay_libgit2.h"
+#include "testlib.h"
+#include "posix.h"
+
+/* Test that reading and writing a tree is a no-op */
+void test_index_read_tree__read_write_involution(void)
+{
+	git_repository *repo;
+	git_index *index;
+	git_oid tree_oid;
+	git_tree *tree;
+	git_oid expected;
+
+	p_mkdir("read_tree", 0700);
+
+	cl_git_pass(git_repository_init(&repo, "./read_tree", 0));
+	cl_git_pass(git_repository_index(&index, repo));
+
+	cl_assert(git_index_entrycount(index) == 0);
+
+	p_mkdir("./read_tree/abc", 0700);
+
+	/* Sort order: '-' < '/' < '_' */
+	file_create("./read_tree/abc-d", NULL);
+	file_create("./read_tree/abc/d", NULL);
+	file_create("./read_tree/abc_d", NULL);
+
+	cl_git_pass(git_index_add(index, "abc-d", 0));
+	cl_git_pass(git_index_add(index, "abc_d", 0));
+	cl_git_pass(git_index_add(index, "abc/d", 0));
+
+	/* write-tree */
+	cl_git_pass(git_tree_create_fromindex(&expected, index));
+
+	/* read-tree */
+	git_tree_lookup(&tree, repo, &expected);
+	cl_git_pass(git_index_read_tree(index, tree));
+
+	cl_git_pass(git_tree_create_fromindex(&tree_oid, index));
+	cl_assert(git_oid_cmp(&expected, &tree_oid) == 0);
+
+	git_index_free(index);
+	git_repository_free(repo);
+
+	cl_fixture_cleanup("read_tree");
+}
diff --git a/tests-clay/index/rename.c b/tests-clay/index/rename.c
index f811254..c949fa7 100644
--- a/tests-clay/index/rename.c
+++ b/tests-clay/index/rename.c
@@ -1,16 +1,7 @@
 #include "clay_libgit2.h"
+#include "testlib.h"
 #include "posix.h"
 
-static void file_create(const char *filename, const char *content)
-{
-	int fd;
-
-	fd = p_creat(filename, 0666);
-	cl_assert(fd != 0);
-	cl_git_pass(p_write(fd, content, strlen(content)));
-	cl_git_pass(p_close(fd));
-}
-
 void test_index_rename__single_file(void)
 {
 	git_repository *repo;
diff --git a/tests-clay/testlib.c b/tests-clay/testlib.c
new file mode 100644
index 0000000..d45fc2c
--- /dev/null
+++ b/tests-clay/testlib.c
@@ -0,0 +1,20 @@
+#include "clay.h"
+#include "testlib.h"
+#include "posix.h"
+
+void file_create(const char *filename, const char *content)
+{
+	int fd;
+
+	fd = p_creat(filename, 0666);
+	cl_assert(fd != 0);
+
+	if (content) {
+		cl_must_pass(p_write(fd, content, strlen(content)));
+	} else {
+		cl_must_pass(p_write(fd, filename, strlen(filename)));
+		cl_must_pass(p_write(fd, "\n", 1));
+	}
+
+	cl_must_pass(p_close(fd));
+}
diff --git a/tests-clay/testlib.h b/tests-clay/testlib.h
new file mode 100644
index 0000000..2e8867c
--- /dev/null
+++ b/tests-clay/testlib.h
@@ -0,0 +1,6 @@
+#ifndef INCLUDE_testlib_h__
+#define INCLUDE_testlib_h__
+
+void file_create(const char *filename, const char *content);
+
+#endif