On Sat, Apr 22, 2017 at 10:05:02AM +0200, Michael Haggerty wrote: > I find this implementation confusing: > > * `if (iter->worktree_dir_iterator)` sounds like it should mean > that we are iterating over worktree references but it really means > that we are iterating over the common references in a repository > that is a linked worktree. > * `files_reflog_iterator_advance()` is called recursively, but only > for the first worktree reference. > * `iter->worktree_dir_iterator` is moved over to `iter->dir_iterator` > when the common refs are exhausted. > > Do you find it more readable as follows?: It's a bit better, but while we're at it, why not take full advantage of iterator abstraction? This replacement patch (with some unrelated bits removed to reduce distraction) adds a new meta ref-iterator that combine a per-repo and a per-worktree iterators together. The new iterator walks through both sub-iterators and drop the per-worktree results from the per-repo iterator, which will be replaced with results from per-worktree iterator. You probably see where I'm going with this. When the new "linked worktree ref store" comes, it will combine two per-worktree and per-repo ref stores together and this iterator will come handy. At that point, files-backend can go back to being oblivious about $GIT_DIR vs $GIT_COMMON_DIR and files_reflog_iterator_begin() will be reverted back to the version before this patch. diff --git a/refs/files-backend.c b/refs/files-backend.c index 4149943a6e..817b7b5d5e 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -3432,23 +3423,37 @@ static struct ref_iterator_vtable files_reflog_iterator_vtable = { files_reflog_iterator_abort }; -static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store) +static struct ref_iterator *reflog_iterator_begin(struct ref_store *ref_store, + const char *gitdir) { - struct files_ref_store *refs = - files_downcast(ref_store, REF_STORE_READ, - "reflog_iterator_begin"); struct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter)); struct ref_iterator *ref_iterator = &iter->base; struct strbuf sb = STRBUF_INIT; base_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable); - files_reflog_path(refs, &sb, NULL); + strbuf_addf(&sb, "%s/logs", gitdir); iter->dir_iterator = dir_iterator_begin(sb.buf); iter->ref_store = ref_store; strbuf_release(&sb); + return ref_iterator; } +static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store) +{ + struct files_ref_store *refs = + files_downcast(ref_store, REF_STORE_READ, + "reflog_iterator_begin"); + + if (!strcmp(refs->gitdir, refs->gitcommondir)) { + return reflog_iterator_begin(ref_store, refs->gitcommondir); + } else { + return worktree_ref_iterator_begin( + reflog_iterator_begin(ref_store, refs->gitcommondir), + reflog_iterator_begin(ref_store, refs->gitdir)); + } +} + static int ref_update_reject_duplicates(struct string_list *refnames, struct strbuf *err) { diff --git a/refs/iterator.c b/refs/iterator.c index bce1f192f7..93243a00c4 100644 --- a/refs/iterator.c +++ b/refs/iterator.c @@ -40,6 +40,14 @@ void base_ref_iterator_free(struct ref_iterator *iter) free(iter); } +static void ref_iterator_copy_result(struct ref_iterator *dst, + const struct ref_iterator *src) +{ + dst->refname = src->refname; + dst->oid = src->oid; + dst->flags = src->flags; +} + struct empty_ref_iterator { struct ref_iterator base; }; @@ -382,3 +390,100 @@ int do_for_each_ref_iterator(struct ref_iterator *iter, return -1; return retval; } + +struct worktree_ref_iterator { + struct ref_iterator base; + struct ref_iterator *per_repo_iterator; + struct ref_iterator *per_worktree_iterator; +}; + +static int worktree_ref_iterator_advance(struct ref_iterator *ref_iterator) +{ + struct worktree_ref_iterator *iter = + (struct worktree_ref_iterator *)ref_iterator; + int ok; + + while (1) { + struct ref_iterator **subiter; + int normal_only; + + if (iter->per_repo_iterator) { + subiter = &iter->per_repo_iterator; + /* + * If we are in a worktree, then we only + * include "normal" common references: + */ + normal_only = !!iter->per_worktree_iterator; + } else if (iter->per_worktree_iterator) { + subiter = &iter->per_worktree_iterator; + normal_only = 0; + } else { + ok = ITER_DONE; + break; + } + + ok = ref_iterator_advance(*subiter); + if (ok == ITER_ERROR) { + *subiter = NULL; + break; + } else if (ok == ITER_DONE) { + *subiter = NULL; + /* There might still be worktree refs left: */ + continue; + } + + if (normal_only && + ref_type((*subiter)->refname) != REF_TYPE_NORMAL) + continue; + + ref_iterator_copy_result(&iter->base, *subiter); + return ITER_OK; + } + + if (ref_iterator_abort(ref_iterator) == ITER_ERROR) + return ITER_ERROR; + + return ok; +} + +static int worktree_ref_iterator_peel(struct ref_iterator *ref_iterator, + struct object_id *peeled) +{ + die("BUG: ref_iterator_peel() called for reflog_iterator"); +} + +static int worktree_ref_iterator_abort(struct ref_iterator *ref_iterator) +{ + struct worktree_ref_iterator *iter = + (struct worktree_ref_iterator *)ref_iterator; + int ok = ITER_DONE; + + if (iter->per_repo_iterator) + ok = ref_iterator_abort(iter->per_repo_iterator); + + if (iter->per_worktree_iterator) { + int ok2 = ref_iterator_abort(iter->per_worktree_iterator); + if (ok2 == ITER_ERROR) + ok = ok2; + } + + base_ref_iterator_free(ref_iterator); + return ok; +} + +static struct ref_iterator_vtable worktree_ref_iterator_vtable = { + worktree_ref_iterator_advance, + worktree_ref_iterator_peel, + worktree_ref_iterator_abort +}; + +struct ref_iterator *worktree_ref_iterator_begin(struct ref_iterator *per_repo, + struct ref_iterator *per_worktree) +{ + struct worktree_ref_iterator *iter = xcalloc(1, sizeof(*iter)); + + base_ref_iterator_init(&iter->base, &worktree_ref_iterator_vtable); + iter->per_repo_iterator = per_repo; + iter->per_worktree_iterator = per_worktree; + return &iter->base; +} diff --git a/refs/refs-internal.h b/refs/refs-internal.h index 690498698e..dcb1f1d73d 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -387,6 +387,14 @@ struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0, const char *prefix, int trim); +/* + * Wrap per_repo and per_worktree iterators. Traverse per_repo + * iterator, drop per-worktree refs. Then traverse per_worktree + * iterator. + */ +struct ref_iterator *worktree_ref_iterator_begin(struct ref_iterator *per_repo, + struct ref_iterator *per_worktree); + /* Internal implementation of reference iteration: */ /*