Commit b31667fb695dab0510cc5fc259e0569ff2a2ef41

Ben Straub 2012-07-27T20:29:06

Checkout: add head- and ref-centric checkouts. Renamed git_checkout_index to what it really was, and removed duplicate code from clone.c. Added git_checkout_ref, which updates HEAD and hands off to git_checkout_head. Added tests for the options the caller can pass to git_checkout_*.

diff --git a/include/git2/checkout.h b/include/git2/checkout.h
index 7a32cff..78367c2 100644
--- a/include/git2/checkout.h
+++ b/include/git2/checkout.h
@@ -35,26 +35,31 @@ typedef struct git_checkout_opts {
 } git_checkout_opts;
 
 /**
- * Updates files in the working tree to match the index.
+ * Updates files in the working tree to match the commit pointed to by HEAD.
  *
  * @param repo repository to check out (must be non-bare)
  * @param opts specifies checkout options (may be NULL)
+ * @param stats structure through which progress information is reported
  * @return 0 on success, GIT_ERROR otherwise (use git_error_last for information about the error)
  */
-GIT_EXTERN(int) git_checkout_index(git_repository *repo,
-											  git_checkout_opts *opts,
-											  git_indexer_stats *stats);
+GIT_EXTERN(int) git_checkout_head(git_repository *repo,
+											 git_checkout_opts *opts,
+											 git_indexer_stats *stats);
+
+
 
 /**
- * Updates files in the working tree to match the commit pointed to by HEAD.
+ * Updates files in the working tree to match a commit pointed to by a ref.
  *
- * @param repo repository to check out (must be non-bare)
+ * @param ref reference to follow to a commit
  * @param opts specifies checkout options (may be NULL)
+ * @param stats structure through which progress information is reported
  * @return 0 on success, GIT_ERROR otherwise (use git_error_last for information about the error)
  */
-GIT_EXTERN(int) git_checkout_head(git_repository *repo,
-											 git_checkout_opts *opts,
-											 git_indexer_stats *stats);
+GIT_EXTERN(int) git_checkout_reference(git_reference *ref,
+													git_checkout_opts *opts,
+													git_indexer_stats *stats);
+
 
 /** @} */
 GIT_END_DECL
diff --git a/src/checkout.c b/src/checkout.c
index 24d2149..81389a7 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -145,7 +145,7 @@ static int checkout_walker(const char *path, const git_tree_entry *entry, void *
 }
 
 
-int git_checkout_index(git_repository *repo, git_checkout_opts *opts, git_indexer_stats *stats)
+int git_checkout_head(git_repository *repo, git_checkout_opts *opts, git_indexer_stats *stats)
 {
 	int retcode = GIT_ERROR;
 	git_indexer_stats dummy_stats;
@@ -188,12 +188,14 @@ int git_checkout_index(git_repository *repo, git_checkout_opts *opts, git_indexe
 	payload.repo = repo;
 	if (git_repository_odb(&payload.odb, repo) < 0) return GIT_ERROR;
 
-	/* TODO: stats->total is never calculated. */
-
 	if (!git_repository_head_tree(&tree, repo)) {
-		/* Checkout the files */
-		if (!git_tree_walk(tree, checkout_walker, GIT_TREEWALK_POST, &payload)) {
-			retcode = 0;
+		git_index *idx;
+		if (!(retcode = git_repository_index(&idx, repo))) {
+			/* TODO: Make git_index_read_tree fill in stats->total */
+			if (!(retcode = git_index_read_tree(idx, tree))) {
+				retcode = git_tree_walk(tree, checkout_walker, GIT_TREEWALK_POST, &payload);
+			}
+			git_index_free(idx);
 		}
 		git_tree_free(tree);
 	}
@@ -203,11 +205,25 @@ int git_checkout_index(git_repository *repo, git_checkout_opts *opts, git_indexe
 }
 
 
-int git_checkout_head(git_repository *repo, git_checkout_opts *opts, git_indexer_stats *stats)
+int git_checkout_reference(git_reference *ref,
+									git_checkout_opts *opts,
+									git_indexer_stats *stats)
 {
-	/* TODO: read HEAD into index */
+	git_repository *repo= git_reference_owner(ref);
+	git_reference *head = NULL;
+	int retcode = GIT_ERROR;
 
-	return git_checkout_index(repo, opts, stats);
+	if ((retcode = git_reference_lookup(&head, repo, GIT_HEAD_FILE)) < 0)
+		return retcode;
+
+	if ((retcode = git_reference_set_target(head, git_reference_name(ref))) < 0)
+		goto gcr_cleanup;
+
+	retcode = git_checkout_head(git_reference_owner(ref), opts, stats);
+
+gcr_cleanup:
+	git_reference_free(head);
+	return retcode;
 }
 
 
diff --git a/src/clone.c b/src/clone.c
index 7ae32a0..9b7ab89 100644
--- a/src/clone.c
+++ b/src/clone.c
@@ -96,25 +96,8 @@ static int update_head_to_new_branch(git_repository *repo, const git_oid *target
 		git_reference *head;
 		if (!git_reference_lookup(&head, repo, GIT_HEAD_FILE)) {
 			git_buf targetbuf = GIT_BUF_INIT;
-			if (!git_buf_printf(&targetbuf, "refs/heads/%s", name) && /* TODO: "refs/heads" constant? */
-				 !git_reference_set_target(head, git_buf_cstr(&targetbuf))) {
-				/* Read the tree into the index */
-				git_commit *commit;
-				if (!git_commit_lookup(&commit, repo, target)) {
-					git_tree *tree;
-					if (!git_commit_tree(&tree, commit)) {
-						git_index *index;
-						if (!git_repository_index(&index, repo)) {
-							if (!git_index_read_tree(index, tree)) {
-								git_index_write(index);
-								retcode = 0;
-							}
-							git_index_free(index);
-						}
-						git_tree_free(tree);
-					}
-					git_commit_free(commit);
-				}
+			if (!git_buf_printf(&targetbuf, "refs/heads/%s", name)) {
+				retcode = git_reference_set_target(head, git_buf_cstr(&targetbuf));
 			}
 			git_buf_free(&targetbuf);
 			git_reference_free(head);
diff --git a/tests-clar/checkout/checkout.c b/tests-clar/checkout/checkout.c
index 53d95c4..856aca3 100644
--- a/tests-clar/checkout/checkout.c
+++ b/tests-clar/checkout/checkout.c
@@ -38,12 +38,12 @@ void test_checkout_checkout__bare(void)
 {
 	cl_git_sandbox_cleanup();
 	g_repo = cl_git_sandbox_init("testrepo.git");
-	cl_git_fail(git_checkout_index(g_repo, NULL, NULL));
+	cl_git_fail(git_checkout_head(g_repo, NULL, NULL));
 }
 
 void test_checkout_checkout__default(void)
 {
-	cl_git_pass(git_checkout_index(g_repo, NULL, NULL));
+	cl_git_pass(git_checkout_head(g_repo, NULL, NULL));
 	test_file_contents("./testrepo/README", "hey there\n");
 	test_file_contents("./testrepo/branch_file.txt", "hi\nbye!\n");
 	test_file_contents("./testrepo/new.txt", "my new file\n");
@@ -57,7 +57,7 @@ void test_checkout_checkout__crlf(void)
 		"README text eol=cr\n"
 		"new.txt text eol=lf\n";
 	cl_git_mkfile("./testrepo/.gitattributes", attributes);
-	cl_git_pass(git_checkout_index(g_repo, NULL, NULL));
+	cl_git_pass(git_checkout_head(g_repo, NULL, NULL));
 	/* test_file_contents("./testrepo/README", "hey there\n"); */
 	/* test_file_contents("./testrepo/new.txt", "my new file\n"); */
 	/* test_file_contents("./testrepo/branch_file.txt", "hi\r\nbye!\r\n"); */
@@ -80,7 +80,7 @@ void test_checkout_checkout__symlinks(void)
 {
 	/* First try with symlinks forced on */
 	enable_symlinks(true);
-	cl_git_pass(git_checkout_index(g_repo, NULL, NULL));
+	cl_git_pass(git_checkout_head(g_repo, NULL, NULL));
 
 #ifdef GIT_WIN32
 	test_file_contents("./testrepo/link_to_new.txt", "new.txt");
@@ -101,7 +101,67 @@ void test_checkout_checkout__symlinks(void)
 	cl_git_sandbox_cleanup();
 	g_repo = cl_git_sandbox_init("testrepo");
 	enable_symlinks(false);
-	cl_git_pass(git_checkout_index(g_repo, NULL, NULL));
+	cl_git_pass(git_checkout_head(g_repo, NULL, NULL));
 
 	test_file_contents("./testrepo/link_to_new.txt", "new.txt");
 }
+
+void test_checkout_checkout__existing_file_options(void)
+{
+	git_checkout_opts opts = {0};
+	cl_git_mkfile("./testrepo/new.txt", "This isn't what's stored!");
+	opts.existing_file_action = GIT_CHECKOUT_SKIP_EXISTING;
+	cl_git_pass(git_checkout_head(g_repo, &opts, NULL));
+	test_file_contents("./testrepo/new.txt", "This isn't what's stored!");
+	opts.existing_file_action = GIT_CHECKOUT_OVERWRITE_EXISTING;
+	cl_git_pass(git_checkout_head(g_repo, &opts, NULL));
+	test_file_contents("./testrepo/new.txt", "my new file\n");
+}
+
+void test_checkout_checkout__disable_filters(void)
+{
+	git_checkout_opts opts = {0};
+	cl_git_mkfile("./testrepo/.gitattributes", "*.txt text eol=crlf\n");
+	/* TODO cl_git_pass(git_checkout_head(g_repo, &opts, NULL));*/
+	/* TODO test_file_contents("./testrepo/new.txt", "my new file\r\n");*/
+	opts.disable_filters = true;
+	cl_git_pass(git_checkout_head(g_repo, &opts, NULL));
+	test_file_contents("./testrepo/new.txt", "my new file\n");
+}
+
+void test_checkout_checkout__dir_modes(void)
+{
+#ifndef GIT_WIN32
+	git_checkout_opts opts = {0};
+	struct stat st;
+	git_reference *ref;
+
+	cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/heads/dir"));
+
+	opts.dir_mode = 0600;
+	cl_git_pass(git_checkout_reference(ref, &opts, NULL));
+	cl_git_pass(p_stat("./testrepo/a", &st));
+	cl_assert_equal_i(st.st_mode & 0777, 0600);
+#endif
+}
+
+void test_checkout_checkout__file_modes(void)
+{
+	git_checkout_opts opts = {0};
+	struct stat st;
+
+	opts.file_mode = 0700;
+	cl_git_pass(git_checkout_head(g_repo, &opts, NULL));
+	cl_git_pass(p_stat("./testrepo/new.txt", &st));
+	cl_assert_equal_i(st.st_mode & 0777, 0700);
+}
+
+void test_checkout_checkout__open_flags(void)
+{
+	git_checkout_opts opts = {0};
+
+	cl_git_mkfile("./testrepo/new.txt", "hi\n");
+	opts.file_open_flags = O_CREAT | O_RDWR | O_APPEND;
+	cl_git_pass(git_checkout_head(g_repo, &opts, NULL));
+	test_file_contents("./testrepo/new.txt", "hi\nmy new file\n");
+}
diff --git a/tests-clar/resources/testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925 b/tests-clar/resources/testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925
new file mode 100644
index 0000000..d37b93e
Binary files /dev/null and b/tests-clar/resources/testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925 differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dcc b/tests-clar/resources/testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dcc
new file mode 100644
index 0000000..b669961
Binary files /dev/null and b/tests-clar/resources/testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dcc differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735 b/tests-clar/resources/testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735
new file mode 100644
index 0000000..9ff5eb2
Binary files /dev/null and b/tests-clar/resources/testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735 differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9 b/tests-clar/resources/testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9
new file mode 100644
index 0000000..7620c51
Binary files /dev/null and b/tests-clar/resources/testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9 differ
diff --git a/tests-clar/resources/testrepo/.gitted/refs/heads/dir b/tests-clar/resources/testrepo/.gitted/refs/heads/dir
new file mode 100644
index 0000000..e140e85
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/refs/heads/dir
@@ -0,0 +1 @@
+cf80f8de9f1185bf3a05f993f6121880dd0cfbc9