diff --git a/src/checkout.c b/src/checkout.c index a412c4c4e..bc976b854 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -336,22 +336,22 @@ static int checkout_action_wd_only( error = git_iterator_advance(wditem, workdir); } else { /* untracked or ignored - can't know which until we advance through */ - bool ignored, over = false; - bool removable = wd_item_is_removable(workdir, wd); + bool over = false, removable = wd_item_is_removable(workdir, wd); + git_iterator_status_t untracked_state; /* copy the entry for issuing notification callback later */ git_index_entry saved_wd = *wd; git_buf_sets(&data->tmp, wd->path); saved_wd.path = data->tmp.ptr; - error = git_iterator_advance_over_and_check_ignored( - wditem, &ignored, workdir); + error = git_iterator_advance_over_with_status( + wditem, &untracked_state, workdir); if (error == GIT_ITEROVER) over = true; else if (error < 0) return error; - if (ignored) { + if (untracked_state == GIT_ITERATOR_STATUS_IGNORED) { notify = GIT_CHECKOUT_NOTIFY_IGNORED; remove = ((data->strategy & GIT_CHECKOUT_REMOVE_IGNORED) != 0); } else { diff --git a/src/diff.c b/src/diff.c index f1c1b0543..18cd52c55 100644 --- a/src/diff.c +++ b/src/diff.c @@ -839,7 +839,7 @@ static int handle_unmatched_new_item( DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS)) { git_diff_delta *last; - bool ignored; + git_iterator_status_t untracked_state; /* attempt to insert record for this directory */ if ((error = diff_delta__from_one(diff, delta_type, nitem)) != 0) @@ -851,13 +851,14 @@ static int handle_unmatched_new_item( return git_iterator_advance(&info->nitem, info->new_iter); /* iterate into dir looking for an actual untracked file */ - if ((error = git_iterator_advance_over_and_check_ignored( - &info->nitem, &ignored, info->new_iter)) < 0 && + if ((error = git_iterator_advance_over_with_status( + &info->nitem, &untracked_state, info->new_iter)) < 0 && error != GIT_ITEROVER) return error; - /* it iteration only found ignored items, update the record */ - if (ignored) { + /* if we found nothing or just ignored items, update the record */ + if (untracked_state == GIT_ITERATOR_STATUS_IGNORED || + untracked_state == GIT_ITERATOR_STATUS_EMPTY) { last->status = GIT_DELTA_IGNORED; /* remove the record if we don't want ignored records */ diff --git a/src/iterator.c b/src/iterator.c index 1a24dad10..dfa79977d 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -1529,15 +1529,17 @@ int git_iterator_current_workdir_path(git_buf **path, git_iterator *iter) return 0; } -int git_iterator_advance_over_and_check_ignored( - const git_index_entry **entryptr, bool *ignored, git_iterator *iter) +int git_iterator_advance_over_with_status( + const git_index_entry **entryptr, + git_iterator_status_t *status, + git_iterator *iter) { int error = 0; workdir_iterator *wi = (workdir_iterator *)iter; char *base = NULL; const git_index_entry *entry; - *ignored = false; + *status = GIT_ITERATOR_STATUS_NORMAL; if (iter->type != GIT_ITERATOR_TYPE_WORKDIR) return git_iterator_advance(entryptr, iter); @@ -1548,11 +1550,12 @@ int git_iterator_advance_over_and_check_ignored( if (git_ignore__lookup( &wi->ignores, wi->fi.entry.path, &wi->is_ignored) < 0) wi->is_ignored = true; - *ignored = wi->is_ignored; + if (wi->is_ignored) + *status = GIT_ITERATOR_STATUS_IGNORED; return git_iterator_advance(entryptr, iter); } - *ignored = true; + *status = GIT_ITERATOR_STATUS_EMPTY; base = git__strdup(entry->path); GITERR_CHECK_ALLOC(base); @@ -1577,9 +1580,11 @@ int git_iterator_advance_over_and_check_ignored( /* if we found a non-ignored item, treat parent as untracked */ if (!wi->is_ignored) { - *ignored = false; + *status = GIT_ITERATOR_STATUS_NORMAL; break; } + if (entry && !S_ISDIR(entry->mode)) + *status = GIT_ITERATOR_STATUS_IGNORED; if ((error = git_iterator_advance(&entry, iter)) < 0) break; diff --git a/src/iterator.h b/src/iterator.h index dcedbd530..ba9c1e486 100644 --- a/src/iterator.h +++ b/src/iterator.h @@ -258,12 +258,23 @@ extern int git_iterator_current_workdir_path( /* Return index pointer if index iterator, else NULL */ extern git_index *git_iterator_get_index(git_iterator *iter); -/* Special type of advance that can be called when looking at a tree in - * the working directory that leaves the iterator on the next item after - * the tree, but also scans the tree contents looking for any items that - * are not ignored. +typedef enum { + GIT_ITERATOR_STATUS_NORMAL = 0, + GIT_ITERATOR_STATUS_IGNORED = 1, + GIT_ITERATOR_STATUS_EMPTY = 2 +} git_iterator_status_t; + +/* Advance over a directory and check if it contains no files or just + * ignored files. + * + * In a tree or the index, all directories will contain files, but in the + * working directory it is possible to have an empty directory tree or a + * tree that only contains ignored files. Many Git operations treat these + * cases specially. This advances over a directory (presumably an + * untracked directory) but checks during the scan if there are any files + * and any non-ignored files. */ -extern int git_iterator_advance_over_and_check_ignored( - const git_index_entry **entry, bool *ignored, git_iterator *iter); +extern int git_iterator_advance_over_with_status( + const git_index_entry **entry, git_iterator_status_t *status, git_iterator *iter); #endif