diff --git a/src/iterator.c b/src/iterator.c index 8511d53eb..805ff643e 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -80,7 +80,7 @@ static int expand_tree_if_needed(git_iterator_tree *ti) te = git_tree_entry_byindex(tree, tree_idx); if (!entry_is_tree(te)) - return GIT_SUCCESS; + break; error = git_tree_lookup(&subtree, ti->repo, &te->oid); if (error != GIT_SUCCESS) @@ -98,13 +98,18 @@ static int expand_tree_if_needed(git_iterator_tree *ti) return GIT_SUCCESS; } -static int git_iterator__tree_advance(git_iterator *self) +static int git_iterator__tree_advance( + git_iterator *self, const git_index_entry **entry) { + int error = GIT_SUCCESS; git_iterator_tree *ti = (git_iterator_tree *)self; git_tree *tree = git_vector_last(&ti->tree_stack); unsigned int tree_idx = PTR_AS_IDX(git_vector_last(&ti->idx_stack)); const git_tree_entry *te = git_tree_entry_byindex(tree, tree_idx); + if (entry != NULL) + *entry = NULL; + if (te == NULL) return GIT_SUCCESS; @@ -133,9 +138,12 @@ static int git_iterator__tree_advance(git_iterator *self) } if (te && entry_is_tree(te)) - return expand_tree_if_needed(ti); + error = expand_tree_if_needed(ti); - return GIT_SUCCESS; + if (error == GIT_SUCCESS && entry != NULL) + error = git_iterator__tree_current(self, entry); + + return error; } static void git_iterator__tree_free(git_iterator *self) @@ -204,11 +212,14 @@ static int git_iterator__index_at_end(git_iterator *self) return (ii->current >= git_index_entrycount(ii->index)); } -static int git_iterator__index_advance(git_iterator *self) +static int git_iterator__index_advance( + git_iterator *self, const git_index_entry **entry) { git_iterator_index *ii = (git_iterator_index *)self; if (ii->current < git_index_entrycount(ii->index)) ii->current++; + if (entry) + *entry = git_index_get(ii->index, ii->current); return GIT_SUCCESS; } @@ -315,13 +326,18 @@ static int git_iterator__workdir_at_end(git_iterator *self) return (wi->entry.path == NULL); } -static int git_iterator__workdir_advance(git_iterator *self) +static int git_iterator__workdir_advance( + git_iterator *self, const git_index_entry **entry) { + int error; git_iterator_workdir *wi = (git_iterator_workdir *)self; git_vector *dir; unsigned int pos; const char *next; + if (entry) + *entry = NULL; + if (wi->entry.path == NULL) return GIT_SUCCESS; @@ -348,26 +364,32 @@ static int git_iterator__workdir_advance(git_iterator *self) git_ignore__pop_dir(&wi->ignores); } - return load_workdir_entry(wi); + error = load_workdir_entry(wi); + + if (error == GIT_SUCCESS && entry) + return git_iterator__workdir_current(self, entry); + + return error; } -int git_iterator_advance_into_ignored_directory(git_iterator *iter) +int git_iterator_advance_into_directory( + git_iterator *iter, const git_index_entry **entry) { git_iterator_workdir *wi = (git_iterator_workdir *)iter; if (iter->type != GIT_ITERATOR_WORKDIR) - return GIT_SUCCESS; + return git_iterator_current(iter, entry); /* Loop because the first entry in the ignored directory could itself be * an ignored directory, but we want to descend to find an actual entry. */ - while (wi->entry.path && wi->is_ignored && S_ISDIR(wi->entry.mode)) { - int error = push_directory(wi); - if (error != GIT_SUCCESS) - return error; + if (wi->entry.path && S_ISDIR(wi->entry.mode)) { + if (push_directory(wi) < GIT_SUCCESS) + /* If error loading or if empty, skip the directory. */ + return git_iterator__workdir_advance((git_iterator *)wi, entry); } - return GIT_SUCCESS; + return git_iterator__workdir_current(iter, entry); } static void git_iterator__workdir_free(git_iterator *self) @@ -404,7 +426,7 @@ static int load_workdir_entry(git_iterator_workdir *wi) wi->entry.path = relpath; if (strcmp(relpath, DOT_GIT) == 0) - return git_iterator__workdir_advance((git_iterator *)wi); + return git_iterator__workdir_advance((git_iterator *)wi, NULL); /* if there is an error processing the entry, treat as ignored */ wi->is_ignored = 1; @@ -433,19 +455,12 @@ static int load_workdir_entry(git_iterator_workdir *wi) if (git_path_contains(&wi->path, DOT_GIT) == GIT_SUCCESS) { /* create submodule entry */ wi->entry.mode = S_IFGITLINK; - } else if (wi->is_ignored) { - /* create path entry (which is otherwise impossible), but is - * needed in case descent into ignore dir is required. - */ + } else { + /* create directory entry that can be advanced into as needed */ size_t pathlen = strlen(wi->entry.path); wi->entry.path[pathlen] = '/'; wi->entry.path[pathlen + 1] = '\0'; wi->entry.mode = S_IFDIR; - } else if ((error = push_directory(wi)) < GIT_SUCCESS) { - /* if there is an error loading the directory or if empty - * then skip over the directory completely. - */ - return git_iterator__workdir_advance((git_iterator *)wi); } } diff --git a/src/iterator.h b/src/iterator.h index 4aa1df52d..ac30b4ded 100644 --- a/src/iterator.h +++ b/src/iterator.h @@ -22,7 +22,7 @@ struct git_iterator { git_iterator_type_t type; int (*current)(git_iterator *, const git_index_entry **); int (*at_end)(git_iterator *); - int (*advance)(git_iterator *); + int (*advance)(git_iterator *, const git_index_entry **); void (*free)(git_iterator *); }; @@ -54,9 +54,10 @@ GIT_INLINE(int) git_iterator_at_end(git_iterator *iter) return iter->at_end(iter); } -GIT_INLINE(int) git_iterator_advance(git_iterator *iter) +GIT_INLINE(int) git_iterator_advance( + git_iterator *iter, const git_index_entry **entry) { - return iter->advance(iter); + return iter->advance(iter, entry); } GIT_INLINE(void) git_iterator_free(git_iterator *iter) @@ -76,19 +77,23 @@ extern int git_iterator_current_tree_entry( extern int git_iterator_current_is_ignored(git_iterator *iter); /** - * Iterate into an ignored workdir directory. + * Iterate into a workdir directory. * - * When a workdir iterator encounters a directory that is ignored, it will - * just return a current entry for the directory with is_ignored returning - * true. If you are iterating over the index or a tree in parallel and a - * file in the ignored directory has been added to the index/tree already, - * then it may be necessary to iterate into the directory even though it is - * ignored. Call this function to do that. + * Workdir iterators do not automatically descend into directories (so that + * when comparing two iterator entries you can detect a newly created + * directory in the workdir). As a result, you may get S_ISDIR items from + * a workdir iterator. If you wish to iterate over the contents of the + * directories you encounter, then call this function when you encounter + * a directory. * - * Note that if the tracked file in the ignored directory has been deleted, - * this may end up acting like a full "advance" call and advance past the - * directory completely. You must handle that case. + * If there are no files in the directory, this will end up acting like a + * regular advance and will skip past the directory, so you should be + * prepared for that case. + * + * On non-workdir iterators or if not pointing at a directory, this is a + * no-op and will not advance the iterator. */ -extern int git_iterator_advance_into_ignored_directory(git_iterator *iter); +extern int git_iterator_advance_into_directory( + git_iterator *iter, const git_index_entry **entry); #endif diff --git a/tests-clar/diff/iterator.c b/tests-clar/diff/iterator.c index e13e3e2bb..46f8f59fb 100644 --- a/tests-clar/diff/iterator.c +++ b/tests-clar/diff/iterator.c @@ -73,8 +73,7 @@ static void tree_iterator_test( count++; - cl_git_pass(git_iterator_advance(i)); - cl_git_pass(git_iterator_current(i, &entry)); + cl_git_pass(git_iterator_advance(i, &entry)); } git_iterator_free(i); @@ -201,8 +200,7 @@ static void index_iterator_test( } count++; - cl_git_pass(git_iterator_advance(i)); - cl_git_pass(git_iterator_current(i, &entry)); + cl_git_pass(git_iterator_advance(i, &entry)); } git_iterator_free(i); @@ -314,6 +312,11 @@ static void workdir_iterator_test( while (entry != NULL) { int ignored = git_iterator_current_is_ignored(i); + if (!ignored && S_ISDIR(entry->mode)) { + cl_git_pass(git_iterator_advance_into_directory(i, &entry)); + continue; + } + if (expected_names != NULL) cl_assert_strequal(expected_names[count_all], entry->path); @@ -324,8 +327,7 @@ static void workdir_iterator_test( count++; count_all++; - cl_git_pass(git_iterator_advance(i)); - cl_git_pass(git_iterator_current(i, &entry)); + cl_git_pass(git_iterator_advance(i, &entry)); } git_iterator_free(i);