Commit 7e000ab2ece977a028e3f1327de37342bef9f687

Russell Belfer 2012-05-08T15:03:59

Add support for diffing index with no HEAD When a repo is first created, there is no HEAD yet and attempting to diff files in the index was showing nothing because a tree iterator could not be constructed. This adds an "empty" iterator and falls back on that when the head cannot be looked up.

diff --git a/src/diff.c b/src/diff.c
index 524cc9f..fed22f4 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -631,7 +631,7 @@ int git_diff_index_to_tree(
 {
 	git_iterator *a = NULL, *b = NULL;
 
-	assert(repo && old_tree && diff);
+	assert(repo && diff);
 
 	if (git_iterator_for_tree(repo, old_tree, &a) < 0 ||
 		git_iterator_for_index(repo, &b) < 0)
diff --git a/src/iterator.c b/src/iterator.c
index 3a3be17..646990d 100644
--- a/src/iterator.c
+++ b/src/iterator.c
@@ -11,6 +11,48 @@
 #include "buffer.h"
 #include "git2/submodule.h"
 
+static int empty_iterator__no_item(
+	git_iterator *iter, const git_index_entry **entry)
+{
+	GIT_UNUSED(iter);
+	*entry = NULL;
+	return 0;
+}
+
+static int empty_iterator__at_end(git_iterator *iter)
+{
+	GIT_UNUSED(iter);
+	return 1;
+}
+
+static int empty_iterator__noop(git_iterator *iter)
+{
+	GIT_UNUSED(iter);
+	return 0;
+}
+
+static void empty_iterator__free(git_iterator *iter)
+{
+	GIT_UNUSED(iter);
+}
+
+int git_iterator_for_nothing(git_iterator **iter)
+{
+	git_iterator *i = git__calloc(1, sizeof(git_iterator));
+	GITERR_CHECK_ALLOC(i);
+
+	i->type    = GIT_ITERATOR_EMPTY;
+	i->current = empty_iterator__no_item;
+	i->at_end  = empty_iterator__at_end;
+	i->advance = empty_iterator__no_item;
+	i->reset   = empty_iterator__noop;
+	i->free    = empty_iterator__free;
+
+	*iter = i;
+
+	return 0;
+}
+
 typedef struct tree_iterator_frame tree_iterator_frame;
 struct tree_iterator_frame {
 	tree_iterator_frame *next;
@@ -155,7 +197,12 @@ int git_iterator_for_tree(
 	git_repository *repo, git_tree *tree, git_iterator **iter)
 {
 	int error;
-	tree_iterator *ti = git__calloc(1, sizeof(tree_iterator));
+	tree_iterator *ti;
+
+	if (tree == NULL)
+		return git_iterator_for_nothing(iter);
+
+	ti = git__calloc(1, sizeof(tree_iterator));
 	GITERR_CHECK_ALLOC(ti);
 
 	ti->base.type    = GIT_ITERATOR_TREE;
diff --git a/src/iterator.h b/src/iterator.h
index aa78c9f..12eb96b 100644
--- a/src/iterator.h
+++ b/src/iterator.h
@@ -13,6 +13,7 @@
 typedef struct git_iterator git_iterator;
 
 typedef enum {
+	GIT_ITERATOR_EMPTY = 0,
 	GIT_ITERATOR_TREE = 1,
 	GIT_ITERATOR_INDEX = 2,
 	GIT_ITERATOR_WORKDIR = 3
@@ -27,6 +28,8 @@ struct git_iterator {
 	void (*free)(git_iterator *);
 };
 
+int git_iterator_for_nothing(git_iterator **iter);
+
 int git_iterator_for_tree(
 	git_repository *repo, git_tree *tree, git_iterator **iter);
 
diff --git a/src/status.c b/src/status.c
index 546dc86..d07b0c4 100644
--- a/src/status.c
+++ b/src/status.c
@@ -101,7 +101,7 @@ int git_status_foreach_ext(
 		diffopt.flags = diffopt.flags | GIT_DIFF_RECURSE_UNTRACKED_DIRS;
 	/* TODO: support EXCLUDE_SUBMODULES flag */
 
-	if (show != GIT_STATUS_SHOW_WORKDIR_ONLY && head != NULL &&
+	if (show != GIT_STATUS_SHOW_WORKDIR_ONLY &&
 		(err = git_diff_index_to_tree(repo, &diffopt, head, &idx2head)) < 0)
 		goto cleanup;
 
diff --git a/tests-clar/status/worktree.c b/tests-clar/status/worktree.c
index c4c935c..4ac556a 100644
--- a/tests-clar/status/worktree.c
+++ b/tests-clar/status/worktree.c
@@ -401,3 +401,48 @@ void test_status_worktree__cannot_retrieve_the_status_of_a_bare_repository(void)
 
 	git_repository_free(repo);
 }
+
+typedef struct {
+	int count;
+	unsigned int status;
+} status_entry_single;
+
+static int
+cb_status__single(const char *p, unsigned int s, void *payload)
+{
+	status_entry_single *data = (status_entry_single *)payload;
+
+	GIT_UNUSED(p);
+
+	data->count++;
+	data->status = s;
+
+	return 0;
+}
+
+void test_status_worktree__first_commit_in_progress(void)
+{
+	git_repository *repo;
+	git_index *index;
+	status_entry_single result;
+
+	cl_git_pass(git_repository_init(&repo, "getting_started", 0));
+	cl_git_mkfile("getting_started/testfile.txt", "content\n");
+
+	memset(&result, 0, sizeof(result));
+	cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
+	cl_assert(result.count == 1);
+	cl_assert(result.status == GIT_STATUS_WT_NEW);
+
+	cl_git_pass(git_repository_index(&index, repo));
+	cl_git_pass(git_index_add(index, "testfile.txt", 0));
+	cl_git_pass(git_index_write(index));
+
+	memset(&result, 0, sizeof(result));
+	cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
+	cl_assert(result.count == 1);
+	cl_assert(result.status == GIT_STATUS_INDEX_NEW);
+
+	git_index_free(index);
+	git_repository_free(repo);
+}