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>
This commit is contained in:
Michael Haggerty 2016-06-18 06:15:19 +02:00 committed by Junio C Hamano
parent 0fe5043dad
commit 2880d16f09
2 changed files with 78 additions and 42 deletions

View file

@ -2,6 +2,7 @@
#include "../refs.h"
#include "refs-internal.h"
#include "../iterator.h"
#include "../dir-iterator.h"
#include "../lockfile.h"
#include "../object.h"
#include "../dir.h"
@ -3291,60 +3292,88 @@ int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_dat
strbuf_release(&sb);
return ret;
}
/*
* Call fn for each reflog in the namespace indicated by name. name
* must be empty or end with '/'. Name will be used as a scratch
* space, but its contents will be restored before return.
*/
static int do_for_each_reflog(struct strbuf *name, each_ref_fn fn, void *cb_data)
struct files_reflog_iterator {
struct ref_iterator base;
struct dir_iterator *dir_iterator;
struct object_id oid;
};
static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator)
{
DIR *d = opendir(git_path("logs/%s", name->buf));
int retval = 0;
struct dirent *de;
int oldlen = name->len;
struct files_reflog_iterator *iter =
(struct files_reflog_iterator *)ref_iterator;
struct dir_iterator *diter = iter->dir_iterator;
int ok;
if (!d)
return name->len ? errno : 0;
while ((ok = dir_iterator_advance(diter)) == ITER_OK) {
int flags;
while ((de = readdir(d)) != NULL) {
struct stat st;
if (de->d_name[0] == '.')
if (!S_ISREG(diter->st.st_mode))
continue;
if (ends_with(de->d_name, ".lock"))
if (diter->basename[0] == '.')
continue;
if (ends_with(diter->basename, ".lock"))
continue;
strbuf_addstr(name, de->d_name);
if (stat(git_path("logs/%s", name->buf), &st) < 0) {
; /* silently ignore */
} else {
if (S_ISDIR(st.st_mode)) {
strbuf_addch(name, '/');
retval = do_for_each_reflog(name, fn, cb_data);
} else {
struct object_id oid;
if (read_ref_full(name->buf, 0, oid.hash, NULL))
error("bad ref for %s", name->buf);
else
retval = fn(name->buf, &oid, 0, cb_data);
}
if (retval)
break;
if (read_ref_full(diter->relative_path, 0,
iter->oid.hash, &flags)) {
error("bad ref for %s", diter->path.buf);
continue;
}
strbuf_setlen(name, oldlen);
iter->base.refname = diter->relative_path;
iter->base.oid = &iter->oid;
iter->base.flags = flags;
return ITER_OK;
}
closedir(d);
return retval;
iter->dir_iterator = NULL;
if (ref_iterator_abort(ref_iterator) == ITER_ERROR)
ok = ITER_ERROR;
return ok;
}
static int files_reflog_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
die("BUG: ref_iterator_peel() called for reflog_iterator");
}
static int files_reflog_iterator_abort(struct ref_iterator *ref_iterator)
{
struct files_reflog_iterator *iter =
(struct files_reflog_iterator *)ref_iterator;
int ok = ITER_DONE;
if (iter->dir_iterator)
ok = dir_iterator_abort(iter->dir_iterator);
base_ref_iterator_free(ref_iterator);
return ok;
}
static struct ref_iterator_vtable files_reflog_iterator_vtable = {
files_reflog_iterator_advance,
files_reflog_iterator_peel,
files_reflog_iterator_abort
};
struct ref_iterator *files_reflog_iterator_begin(void)
{
struct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter));
struct ref_iterator *ref_iterator = &iter->base;
base_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable);
iter->dir_iterator = dir_iterator_begin(git_path("logs"));
return ref_iterator;
}
int for_each_reflog(each_ref_fn fn, void *cb_data)
{
int retval;
struct strbuf name;
strbuf_init(&name, PATH_MAX);
retval = do_for_each_reflog(&name, fn, cb_data);
strbuf_release(&name);
return retval;
return do_for_each_ref_iterator(files_reflog_iterator_begin(),
fn, cb_data);
}
static int ref_update_reject_duplicates(struct string_list *refnames,

View file

@ -404,6 +404,13 @@ struct ref_iterator *files_ref_iterator_begin(const char *submodule,
const char *prefix,
unsigned int flags);
/*
* Iterate over the references in the main ref_store that have a
* reflog. The paths within a directory are iterated over in arbitrary
* order.
*/
struct ref_iterator *files_reflog_iterator_begin(void);
/* Internal implementation of reference iteration: */
/*