Hi, I put off reviewing this patch, thinking that it would appear in a re-roll, then never came back to it. :-( On 04/23/2017 06:44 AM, Duy Nguyen wrote: > 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. Yes, that's even better. > 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; > } Makes sense. > +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)); > + } > +} > + Yes. > 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: */ > > /* > > I think `worktree_ref_iterator_begin()` could be implemented with a lot less code via a `merge_ref_iterator`. You would need to define a `ref_iterator_select_fn`, which could look like something like this: enum iterator_selection worktree_iterator_select_fn( struct ref_iterator *iter0, struct ref_iterator *iter1, void *cb_data) { if (iter0) { if (ref_type(iter0->refname) != REF_TYPE_NORMAL) return ITER_SKIP_0; return ITER_SELECT_0; } else if (iter1) { return ITER_SELECT_1; } else { return ITER_SELECT_DONE; } } Then `worktree_ref_iterator_begin()` is simply struct ref_iterator *worktree_ref_iterator_begin(struct ref_iterator *per_repo, struct ref_iterator *per_worktree) { return merge_ref_iterator_begin(per_repo, per_worktree, worktree_iterator_select_fn, NULL); } For references (as opposed to reflogs) you would want to interleave the outputs from the two input iterators in the correct order. See `overlay_iterator_select()` for how that can be done. Michael