for_each_reflog(): reimplement using iterators

Allow references with reflogs to be iterated over using a ref_iterator. The latter is implemented as a files_reflog_iterator, which in turn uses dir_iterator to read the "logs" directory. Note that reflog iteration doesn't correctly handle per-worktree reflogs (either before or after this patch). Signed-off-by: Ramsay Jones <ramsay@ramsayjones.plus.com> Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Michael Haggerty committed Jun 18, 2016 at 06:15 UTC 2880d16f09635f9d43247b27fd7e6508b992e599
2 files changed +78 -42
refs/files-backend.c
+71 -42
@@ -2,6 +2,7 @@
2 #include "../refs.h"
3 #include "refs-internal.h"
4 #include "../iterator.h"
5 +#include "../dir-iterator.h"
6 #include "../lockfile.h"
7 #include "../object.h"
8 #include "../dir.h"
@@ -3291,60 +3292,88 @@ int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_dat
3292 strbuf_release(&sb);
3293 return ret;
3294 }
3294 -/*
3295 - * Call fn for each reflog in the namespace indicated by name. name
3296 - * must be empty or end with '/'. Name will be used as a scratch
3297 - * space, but its contents will be restored before return.
3298 - */
3299 -static int do_for_each_reflog(struct strbuf *name, each_ref_fn fn, void *cb_data)
3300 -{
3301 - DIR *d = opendir(git_path("logs/%s", name->buf));
3302 - int retval = 0;
3303 - struct dirent *de;
3304 - int oldlen = name->len;
3295
3306 - if (!d)
3307 - return name->len ? errno : 0;
3296 +struct files_reflog_iterator {
3297 + struct ref_iterator base;
3298
3309 - while ((de = readdir(d)) != NULL) {
3310 - struct stat st;
3299 + struct dir_iterator *dir_iterator;
3300 + struct object_id oid;
3301 +};
3302
3312 - if (de->d_name[0] == '.')
3303 +static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator)
3304 +{
3305 + struct files_reflog_iterator *iter =
3306 + (struct files_reflog_iterator *)ref_iterator;
3307 + struct dir_iterator *diter = iter->dir_iterator;
3308 + int ok;
3309 +
3310 + while ((ok = dir_iterator_advance(diter)) == ITER_OK) {
3311 + int flags;
3312 +
3313 + if (!S_ISREG(diter->st.st_mode))
3314 continue;
3314 - if (ends_with(de->d_name, ".lock"))
3315 + if (diter->basename[0] == '.')
3316 + continue;
3317 + if (ends_with(diter->basename, ".lock"))
3318 continue;
3316 - strbuf_addstr(name, de->d_name);
3317 - if (stat(git_path("logs/%s", name->buf), &st) < 0) {
3318 - ; /* silently ignore */
3319 - } else {
3320 - if (S_ISDIR(st.st_mode)) {
3321 - strbuf_addch(name, '/');
3322 - retval = do_for_each_reflog(name, fn, cb_data);
3323 - } else {
3324 - struct object_id oid;
3319
3326 - if (read_ref_full(name->buf, 0, oid.hash, NULL))
3327 - error("bad ref for %s", name->buf);
3328 - else
3329 - retval = fn(name->buf, &oid, 0, cb_data);
3330 - }
3331 - if (retval)
3332 - break;
3320 + if (read_ref_full(diter->relative_path, 0,
3321 + iter->oid.hash, &flags)) {
3322 + error("bad ref for %s", diter->path.buf);
3323 + continue;
3324 }
3334 - strbuf_setlen(name, oldlen);
3325 +
3326 + iter->base.refname = diter->relative_path;
3327 + iter->base.oid = &iter->oid;
3328 + iter->base.flags = flags;
3329 + return ITER_OK;
3330 }
3336 - closedir(d);
3337 - return retval;
3331 +
3332 + iter->dir_iterator = NULL;
3333 + if (ref_iterator_abort(ref_iterator) == ITER_ERROR)
3334 + ok = ITER_ERROR;
3335 + return ok;
3336 +}
3337 +
3338 +static int files_reflog_iterator_peel(struct ref_iterator *ref_iterator,
3339 + struct object_id *peeled)
3340 +{
3341 + die("BUG: ref_iterator_peel() called for reflog_iterator");
3342 +}
3343 +
3344 +static int files_reflog_iterator_abort(struct ref_iterator *ref_iterator)
3345 +{
3346 + struct files_reflog_iterator *iter =
3347 + (struct files_reflog_iterator *)ref_iterator;
3348 + int ok = ITER_DONE;
3349 +
3350 + if (iter->dir_iterator)
3351 + ok = dir_iterator_abort(iter->dir_iterator);
3352 +
3353 + base_ref_iterator_free(ref_iterator);
3354 + return ok;
3355 +}
3356 +
3357 +static struct ref_iterator_vtable files_reflog_iterator_vtable = {
3358 + files_reflog_iterator_advance,
3359 + files_reflog_iterator_peel,
3360 + files_reflog_iterator_abort
3361 +};
3362 +
3363 +struct ref_iterator *files_reflog_iterator_begin(void)
3364 +{
3365 + struct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter));
3366 + struct ref_iterator *ref_iterator = &iter->base;
3367 +
3368 + base_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable);
3369 + iter->dir_iterator = dir_iterator_begin(git_path("logs"));
3370 + return ref_iterator;
3371 }
3372
3373 int for_each_reflog(each_ref_fn fn, void *cb_data)
3374 {
3342 - int retval;
3343 - struct strbuf name;
3344 - strbuf_init(&name, PATH_MAX);
3345 - retval = do_for_each_reflog(&name, fn, cb_data);
3346 - strbuf_release(&name);
3347 - return retval;
3375 + return do_for_each_ref_iterator(files_reflog_iterator_begin(),
3376 + fn, cb_data);
3377 }
3378
3379 static int ref_update_reject_duplicates(struct string_list *refnames,
refs/refs-internal.h
+7
@@ -404,6 +404,13 @@ struct ref_iterator *files_ref_iterator_begin(const char *submodule,
404 const char *prefix,
405 unsigned int flags);
406
407 +/*
408 + * Iterate over the references in the main ref_store that have a
409 + * reflog. The paths within a directory are iterated over in arbitrary
410 + * order.
411 + */
412 +struct ref_iterator *files_reflog_iterator_begin(void);
413 +
414 /* Internal implementation of reference iteration: */
415
416 /*