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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
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);
+}