Merge branch 'ps/reflog-list' into HEAD
"git reflog" learned a "list" subcommand that enumerates known reflogs. * ps/reflog-list: builtin/reflog: introduce subcommand to list reflogs refs: stop resolving ref corresponding to reflogs refs: drop unused params from the reflog iterator callback refs: always treat iterators as ordered refs/files: sort merged worktree and common reflogs refs/files: sort reflogs returned by the reflog iterator dir-iterator: support iteration in sorted order dir-iterator: pass name to `prepare_next_entry_data()` directlymaint
commit
510a27e9e4
|
@ -10,6 +10,7 @@ SYNOPSIS
|
||||||
--------
|
--------
|
||||||
[verse]
|
[verse]
|
||||||
'git reflog' [show] [<log-options>] [<ref>]
|
'git reflog' [show] [<log-options>] [<ref>]
|
||||||
|
'git reflog list'
|
||||||
'git reflog expire' [--expire=<time>] [--expire-unreachable=<time>]
|
'git reflog expire' [--expire=<time>] [--expire-unreachable=<time>]
|
||||||
[--rewrite] [--updateref] [--stale-fix]
|
[--rewrite] [--updateref] [--stale-fix]
|
||||||
[--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]
|
[--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]
|
||||||
|
@ -39,6 +40,8 @@ actions, and in addition the `HEAD` reflog records branch switching.
|
||||||
`git reflog show` is an alias for `git log -g --abbrev-commit
|
`git reflog show` is an alias for `git log -g --abbrev-commit
|
||||||
--pretty=oneline`; see linkgit:git-log[1] for more information.
|
--pretty=oneline`; see linkgit:git-log[1] for more information.
|
||||||
|
|
||||||
|
The "list" subcommand lists all refs which have a corresponding reflog.
|
||||||
|
|
||||||
The "expire" subcommand prunes older reflog entries. Entries older
|
The "expire" subcommand prunes older reflog entries. Entries older
|
||||||
than `expire` time, or entries older than `expire-unreachable` time
|
than `expire` time, or entries older than `expire-unreachable` time
|
||||||
and not reachable from the current tip, are removed from the reflog.
|
and not reachable from the current tip, are removed from the reflog.
|
||||||
|
|
|
@ -509,9 +509,7 @@ static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int fsck_handle_reflog(const char *logname,
|
static int fsck_handle_reflog(const char *logname, void *cb_data)
|
||||||
const struct object_id *oid UNUSED,
|
|
||||||
int flag UNUSED, void *cb_data)
|
|
||||||
{
|
{
|
||||||
struct strbuf refname = STRBUF_INIT;
|
struct strbuf refname = STRBUF_INIT;
|
||||||
|
|
||||||
|
|
|
@ -7,11 +7,15 @@
|
||||||
#include "wildmatch.h"
|
#include "wildmatch.h"
|
||||||
#include "worktree.h"
|
#include "worktree.h"
|
||||||
#include "reflog.h"
|
#include "reflog.h"
|
||||||
|
#include "refs.h"
|
||||||
#include "parse-options.h"
|
#include "parse-options.h"
|
||||||
|
|
||||||
#define BUILTIN_REFLOG_SHOW_USAGE \
|
#define BUILTIN_REFLOG_SHOW_USAGE \
|
||||||
N_("git reflog [show] [<log-options>] [<ref>]")
|
N_("git reflog [show] [<log-options>] [<ref>]")
|
||||||
|
|
||||||
|
#define BUILTIN_REFLOG_LIST_USAGE \
|
||||||
|
N_("git reflog list")
|
||||||
|
|
||||||
#define BUILTIN_REFLOG_EXPIRE_USAGE \
|
#define BUILTIN_REFLOG_EXPIRE_USAGE \
|
||||||
N_("git reflog expire [--expire=<time>] [--expire-unreachable=<time>]\n" \
|
N_("git reflog expire [--expire=<time>] [--expire-unreachable=<time>]\n" \
|
||||||
" [--rewrite] [--updateref] [--stale-fix]\n" \
|
" [--rewrite] [--updateref] [--stale-fix]\n" \
|
||||||
|
@ -29,6 +33,11 @@ static const char *const reflog_show_usage[] = {
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const char *const reflog_list_usage[] = {
|
||||||
|
BUILTIN_REFLOG_LIST_USAGE,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
static const char *const reflog_expire_usage[] = {
|
static const char *const reflog_expire_usage[] = {
|
||||||
BUILTIN_REFLOG_EXPIRE_USAGE,
|
BUILTIN_REFLOG_EXPIRE_USAGE,
|
||||||
NULL
|
NULL
|
||||||
|
@ -46,6 +55,7 @@ static const char *const reflog_exists_usage[] = {
|
||||||
|
|
||||||
static const char *const reflog_usage[] = {
|
static const char *const reflog_usage[] = {
|
||||||
BUILTIN_REFLOG_SHOW_USAGE,
|
BUILTIN_REFLOG_SHOW_USAGE,
|
||||||
|
BUILTIN_REFLOG_LIST_USAGE,
|
||||||
BUILTIN_REFLOG_EXPIRE_USAGE,
|
BUILTIN_REFLOG_EXPIRE_USAGE,
|
||||||
BUILTIN_REFLOG_DELETE_USAGE,
|
BUILTIN_REFLOG_DELETE_USAGE,
|
||||||
BUILTIN_REFLOG_EXISTS_USAGE,
|
BUILTIN_REFLOG_EXISTS_USAGE,
|
||||||
|
@ -60,8 +70,7 @@ struct worktree_reflogs {
|
||||||
struct string_list reflogs;
|
struct string_list reflogs;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int collect_reflog(const char *ref, const struct object_id *oid UNUSED,
|
static int collect_reflog(const char *ref, void *cb_data)
|
||||||
int flags UNUSED, void *cb_data)
|
|
||||||
{
|
{
|
||||||
struct worktree_reflogs *cb = cb_data;
|
struct worktree_reflogs *cb = cb_data;
|
||||||
struct worktree *worktree = cb->worktree;
|
struct worktree *worktree = cb->worktree;
|
||||||
|
@ -238,6 +247,29 @@ static int cmd_reflog_show(int argc, const char **argv, const char *prefix)
|
||||||
return cmd_log_reflog(argc, argv, prefix);
|
return cmd_log_reflog(argc, argv, prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int show_reflog(const char *refname, void *cb_data UNUSED)
|
||||||
|
{
|
||||||
|
printf("%s\n", refname);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_reflog_list(int argc, const char **argv, const char *prefix)
|
||||||
|
{
|
||||||
|
struct option options[] = {
|
||||||
|
OPT_END()
|
||||||
|
};
|
||||||
|
struct ref_store *ref_store;
|
||||||
|
|
||||||
|
argc = parse_options(argc, argv, prefix, options, reflog_list_usage, 0);
|
||||||
|
if (argc)
|
||||||
|
return error(_("%s does not accept arguments: '%s'"),
|
||||||
|
"list", argv[0]);
|
||||||
|
|
||||||
|
ref_store = get_main_ref_store(the_repository);
|
||||||
|
|
||||||
|
return refs_for_each_reflog(ref_store, show_reflog, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
|
static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
|
||||||
{
|
{
|
||||||
struct cmd_reflog_expire_cb cmd = { 0 };
|
struct cmd_reflog_expire_cb cmd = { 0 };
|
||||||
|
@ -417,6 +449,7 @@ int cmd_reflog(int argc, const char **argv, const char *prefix)
|
||||||
parse_opt_subcommand_fn *fn = NULL;
|
parse_opt_subcommand_fn *fn = NULL;
|
||||||
struct option options[] = {
|
struct option options[] = {
|
||||||
OPT_SUBCOMMAND("show", &fn, cmd_reflog_show),
|
OPT_SUBCOMMAND("show", &fn, cmd_reflog_show),
|
||||||
|
OPT_SUBCOMMAND("list", &fn, cmd_reflog_list),
|
||||||
OPT_SUBCOMMAND("expire", &fn, cmd_reflog_expire),
|
OPT_SUBCOMMAND("expire", &fn, cmd_reflog_expire),
|
||||||
OPT_SUBCOMMAND("delete", &fn, cmd_reflog_delete),
|
OPT_SUBCOMMAND("delete", &fn, cmd_reflog_delete),
|
||||||
OPT_SUBCOMMAND("exists", &fn, cmd_reflog_exists),
|
OPT_SUBCOMMAND("exists", &fn, cmd_reflog_exists),
|
||||||
|
|
|
@ -2,10 +2,19 @@
|
||||||
#include "dir.h"
|
#include "dir.h"
|
||||||
#include "iterator.h"
|
#include "iterator.h"
|
||||||
#include "dir-iterator.h"
|
#include "dir-iterator.h"
|
||||||
|
#include "string-list.h"
|
||||||
|
|
||||||
struct dir_iterator_level {
|
struct dir_iterator_level {
|
||||||
DIR *dir;
|
DIR *dir;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The directory entries of the current level. This list will only be
|
||||||
|
* populated when the iterator is ordered. In that case, `dir` will be
|
||||||
|
* set to `NULL`.
|
||||||
|
*/
|
||||||
|
struct string_list entries;
|
||||||
|
size_t entries_idx;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The length of the directory part of path at this level
|
* The length of the directory part of path at this level
|
||||||
* (including a trailing '/'):
|
* (including a trailing '/'):
|
||||||
|
@ -43,6 +52,31 @@ struct dir_iterator_int {
|
||||||
unsigned int flags;
|
unsigned int flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int next_directory_entry(DIR *dir, const char *path,
|
||||||
|
struct dirent **out)
|
||||||
|
{
|
||||||
|
struct dirent *de;
|
||||||
|
|
||||||
|
repeat:
|
||||||
|
errno = 0;
|
||||||
|
de = readdir(dir);
|
||||||
|
if (!de) {
|
||||||
|
if (errno) {
|
||||||
|
warning_errno("error reading directory '%s'",
|
||||||
|
path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_dot_or_dotdot(de->d_name))
|
||||||
|
goto repeat;
|
||||||
|
|
||||||
|
*out = de;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Push a level in the iter stack and initialize it with information from
|
* Push a level in the iter stack and initialize it with information from
|
||||||
* the directory pointed by iter->base->path. It is assumed that this
|
* the directory pointed by iter->base->path. It is assumed that this
|
||||||
|
@ -72,6 +106,35 @@ static int push_level(struct dir_iterator_int *iter)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string_list_init_dup(&level->entries);
|
||||||
|
level->entries_idx = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When the iterator is sorted we read and sort all directory entries
|
||||||
|
* directly.
|
||||||
|
*/
|
||||||
|
if (iter->flags & DIR_ITERATOR_SORTED) {
|
||||||
|
struct dirent *de;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
int ret = next_directory_entry(level->dir, iter->base.path.buf, &de);
|
||||||
|
if (ret < 0) {
|
||||||
|
if (errno != ENOENT &&
|
||||||
|
iter->flags & DIR_ITERATOR_PEDANTIC)
|
||||||
|
return -1;
|
||||||
|
continue;
|
||||||
|
} else if (ret > 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
string_list_append(&level->entries, de->d_name);
|
||||||
|
}
|
||||||
|
string_list_sort(&level->entries);
|
||||||
|
|
||||||
|
closedir(level->dir);
|
||||||
|
level->dir = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,21 +151,22 @@ static int pop_level(struct dir_iterator_int *iter)
|
||||||
warning_errno("error closing directory '%s'",
|
warning_errno("error closing directory '%s'",
|
||||||
iter->base.path.buf);
|
iter->base.path.buf);
|
||||||
level->dir = NULL;
|
level->dir = NULL;
|
||||||
|
string_list_clear(&level->entries, 0);
|
||||||
|
|
||||||
return --iter->levels_nr;
|
return --iter->levels_nr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Populate iter->base with the necessary information on the next iteration
|
* Populate iter->base with the necessary information on the next iteration
|
||||||
* entry, represented by the given dirent de. Return 0 on success and -1
|
* entry, represented by the given name. Return 0 on success and -1
|
||||||
* otherwise, setting errno accordingly.
|
* otherwise, setting errno accordingly.
|
||||||
*/
|
*/
|
||||||
static int prepare_next_entry_data(struct dir_iterator_int *iter,
|
static int prepare_next_entry_data(struct dir_iterator_int *iter,
|
||||||
struct dirent *de)
|
const char *name)
|
||||||
{
|
{
|
||||||
int err, saved_errno;
|
int err, saved_errno;
|
||||||
|
|
||||||
strbuf_addstr(&iter->base.path, de->d_name);
|
strbuf_addstr(&iter->base.path, name);
|
||||||
/*
|
/*
|
||||||
* We have to reset these because the path strbuf might have
|
* We have to reset these because the path strbuf might have
|
||||||
* been realloc()ed at the previous strbuf_addstr().
|
* been realloc()ed at the previous strbuf_addstr().
|
||||||
|
@ -139,27 +203,34 @@ int dir_iterator_advance(struct dir_iterator *dir_iterator)
|
||||||
struct dirent *de;
|
struct dirent *de;
|
||||||
struct dir_iterator_level *level =
|
struct dir_iterator_level *level =
|
||||||
&iter->levels[iter->levels_nr - 1];
|
&iter->levels[iter->levels_nr - 1];
|
||||||
|
const char *name;
|
||||||
|
|
||||||
strbuf_setlen(&iter->base.path, level->prefix_len);
|
strbuf_setlen(&iter->base.path, level->prefix_len);
|
||||||
errno = 0;
|
|
||||||
de = readdir(level->dir);
|
|
||||||
|
|
||||||
if (!de) {
|
if (level->dir) {
|
||||||
if (errno) {
|
int ret = next_directory_entry(level->dir, iter->base.path.buf, &de);
|
||||||
warning_errno("error reading directory '%s'",
|
if (ret < 0) {
|
||||||
iter->base.path.buf);
|
|
||||||
if (iter->flags & DIR_ITERATOR_PEDANTIC)
|
if (iter->flags & DIR_ITERATOR_PEDANTIC)
|
||||||
goto error_out;
|
goto error_out;
|
||||||
} else if (pop_level(iter) == 0) {
|
continue;
|
||||||
|
} else if (ret > 0) {
|
||||||
|
if (pop_level(iter) == 0)
|
||||||
return dir_iterator_abort(dir_iterator);
|
return dir_iterator_abort(dir_iterator);
|
||||||
}
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_dot_or_dotdot(de->d_name))
|
name = de->d_name;
|
||||||
|
} else {
|
||||||
|
if (level->entries_idx >= level->entries.nr) {
|
||||||
|
if (pop_level(iter) == 0)
|
||||||
|
return dir_iterator_abort(dir_iterator);
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (prepare_next_entry_data(iter, de)) {
|
name = level->entries.items[level->entries_idx++].string;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prepare_next_entry_data(iter, name)) {
|
||||||
if (errno != ENOENT && iter->flags & DIR_ITERATOR_PEDANTIC)
|
if (errno != ENOENT && iter->flags & DIR_ITERATOR_PEDANTIC)
|
||||||
goto error_out;
|
goto error_out;
|
||||||
continue;
|
continue;
|
||||||
|
@ -188,6 +259,8 @@ int dir_iterator_abort(struct dir_iterator *dir_iterator)
|
||||||
warning_errno("error closing directory '%s'",
|
warning_errno("error closing directory '%s'",
|
||||||
iter->base.path.buf);
|
iter->base.path.buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string_list_clear(&level->entries, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(iter->levels);
|
free(iter->levels);
|
||||||
|
|
|
@ -54,8 +54,11 @@
|
||||||
* and ITER_ERROR is returned immediately. In both cases, a meaningful
|
* and ITER_ERROR is returned immediately. In both cases, a meaningful
|
||||||
* warning is emitted. Note: ENOENT errors are always ignored so that
|
* warning is emitted. Note: ENOENT errors are always ignored so that
|
||||||
* the API users may remove files during iteration.
|
* the API users may remove files during iteration.
|
||||||
|
*
|
||||||
|
* - DIR_ITERATOR_SORTED: sort directory entries alphabetically.
|
||||||
*/
|
*/
|
||||||
#define DIR_ITERATOR_PEDANTIC (1 << 0)
|
#define DIR_ITERATOR_PEDANTIC (1 << 0)
|
||||||
|
#define DIR_ITERATOR_SORTED (1 << 1)
|
||||||
|
|
||||||
struct dir_iterator {
|
struct dir_iterator {
|
||||||
/* The current path: */
|
/* The current path: */
|
||||||
|
|
27
refs.c
27
refs.c
|
@ -1594,10 +1594,6 @@ struct ref_iterator *refs_ref_iterator_begin(
|
||||||
if (trim)
|
if (trim)
|
||||||
iter = prefix_ref_iterator_begin(iter, "", trim);
|
iter = prefix_ref_iterator_begin(iter, "", trim);
|
||||||
|
|
||||||
/* Sanity check for subclasses: */
|
|
||||||
if (!iter->ordered)
|
|
||||||
BUG("reference iterator is not ordered");
|
|
||||||
|
|
||||||
return iter;
|
return iter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2516,18 +2512,33 @@ cleanup:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data)
|
struct do_for_each_reflog_help {
|
||||||
|
each_reflog_fn *fn;
|
||||||
|
void *cb_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int do_for_each_reflog_helper(struct repository *r UNUSED,
|
||||||
|
const char *refname,
|
||||||
|
const struct object_id *oid UNUSED,
|
||||||
|
int flags,
|
||||||
|
void *cb_data)
|
||||||
|
{
|
||||||
|
struct do_for_each_reflog_help *hp = cb_data;
|
||||||
|
return hp->fn(refname, hp->cb_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
int refs_for_each_reflog(struct ref_store *refs, each_reflog_fn fn, void *cb_data)
|
||||||
{
|
{
|
||||||
struct ref_iterator *iter;
|
struct ref_iterator *iter;
|
||||||
struct do_for_each_ref_help hp = { fn, cb_data };
|
struct do_for_each_reflog_help hp = { fn, cb_data };
|
||||||
|
|
||||||
iter = refs->be->reflog_iterator_begin(refs);
|
iter = refs->be->reflog_iterator_begin(refs);
|
||||||
|
|
||||||
return do_for_each_repo_ref_iterator(the_repository, iter,
|
return do_for_each_repo_ref_iterator(the_repository, iter,
|
||||||
do_for_each_ref_helper, &hp);
|
do_for_each_reflog_helper, &hp);
|
||||||
}
|
}
|
||||||
|
|
||||||
int for_each_reflog(each_ref_fn fn, void *cb_data)
|
int for_each_reflog(each_reflog_fn fn, void *cb_data)
|
||||||
{
|
{
|
||||||
return refs_for_each_reflog(get_main_ref_store(the_repository), fn, cb_data);
|
return refs_for_each_reflog(get_main_ref_store(the_repository), fn, cb_data);
|
||||||
}
|
}
|
||||||
|
|
11
refs.h
11
refs.h
|
@ -534,12 +534,19 @@ int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_dat
|
||||||
/* youngest entry first */
|
/* youngest entry first */
|
||||||
int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void *cb_data);
|
int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void *cb_data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The signature for the callback function for the {refs_,}for_each_reflog()
|
||||||
|
* functions below. The memory pointed to by the refname argument is only
|
||||||
|
* guaranteed to be valid for the duration of a single callback invocation.
|
||||||
|
*/
|
||||||
|
typedef int each_reflog_fn(const char *refname, void *cb_data);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Calls the specified function for each reflog file until it returns nonzero,
|
* Calls the specified function for each reflog file until it returns nonzero,
|
||||||
* and returns the value. Reflog file order is unspecified.
|
* and returns the value. Reflog file order is unspecified.
|
||||||
*/
|
*/
|
||||||
int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data);
|
int refs_for_each_reflog(struct ref_store *refs, each_reflog_fn fn, void *cb_data);
|
||||||
int for_each_reflog(each_ref_fn fn, void *cb_data);
|
int for_each_reflog(each_reflog_fn fn, void *cb_data);
|
||||||
|
|
||||||
#define REFNAME_ALLOW_ONELEVEL 1
|
#define REFNAME_ALLOW_ONELEVEL 1
|
||||||
#define REFNAME_REFSPEC_PATTERN 2
|
#define REFNAME_REFSPEC_PATTERN 2
|
||||||
|
|
|
@ -181,7 +181,6 @@ static int debug_ref_iterator_advance(struct ref_iterator *ref_iterator)
|
||||||
trace_printf_key(&trace_refs, "iterator_advance: %s (0)\n",
|
trace_printf_key(&trace_refs, "iterator_advance: %s (0)\n",
|
||||||
diter->iter->refname);
|
diter->iter->refname);
|
||||||
|
|
||||||
diter->base.ordered = diter->iter->ordered;
|
|
||||||
diter->base.refname = diter->iter->refname;
|
diter->base.refname = diter->iter->refname;
|
||||||
diter->base.oid = diter->iter->oid;
|
diter->base.oid = diter->iter->oid;
|
||||||
diter->base.flags = diter->iter->flags;
|
diter->base.flags = diter->iter->flags;
|
||||||
|
@ -222,7 +221,7 @@ debug_ref_iterator_begin(struct ref_store *ref_store, const char *prefix,
|
||||||
drefs->refs->be->iterator_begin(drefs->refs, prefix,
|
drefs->refs->be->iterator_begin(drefs->refs, prefix,
|
||||||
exclude_patterns, flags);
|
exclude_patterns, flags);
|
||||||
struct debug_ref_iterator *diter = xcalloc(1, sizeof(*diter));
|
struct debug_ref_iterator *diter = xcalloc(1, sizeof(*diter));
|
||||||
base_ref_iterator_init(&diter->base, &debug_ref_iterator_vtable, 1);
|
base_ref_iterator_init(&diter->base, &debug_ref_iterator_vtable);
|
||||||
diter->iter = res;
|
diter->iter = res;
|
||||||
trace_printf_key(&trace_refs, "ref_iterator_begin: \"%s\" (0x%x)\n",
|
trace_printf_key(&trace_refs, "ref_iterator_begin: \"%s\" (0x%x)\n",
|
||||||
prefix, flags);
|
prefix, flags);
|
||||||
|
|
|
@ -879,8 +879,7 @@ static struct ref_iterator *files_ref_iterator_begin(
|
||||||
|
|
||||||
CALLOC_ARRAY(iter, 1);
|
CALLOC_ARRAY(iter, 1);
|
||||||
ref_iterator = &iter->base;
|
ref_iterator = &iter->base;
|
||||||
base_ref_iterator_init(ref_iterator, &files_ref_iterator_vtable,
|
base_ref_iterator_init(ref_iterator, &files_ref_iterator_vtable);
|
||||||
overlay_iter->ordered);
|
|
||||||
iter->iter0 = overlay_iter;
|
iter->iter0 = overlay_iter;
|
||||||
iter->repo = ref_store->repo;
|
iter->repo = ref_store->repo;
|
||||||
iter->flags = flags;
|
iter->flags = flags;
|
||||||
|
@ -2116,10 +2115,8 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store,
|
||||||
|
|
||||||
struct files_reflog_iterator {
|
struct files_reflog_iterator {
|
||||||
struct ref_iterator base;
|
struct ref_iterator base;
|
||||||
|
|
||||||
struct ref_store *ref_store;
|
struct ref_store *ref_store;
|
||||||
struct dir_iterator *dir_iterator;
|
struct dir_iterator *dir_iterator;
|
||||||
struct object_id oid;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator)
|
static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator)
|
||||||
|
@ -2130,25 +2127,13 @@ static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator)
|
||||||
int ok;
|
int ok;
|
||||||
|
|
||||||
while ((ok = dir_iterator_advance(diter)) == ITER_OK) {
|
while ((ok = dir_iterator_advance(diter)) == ITER_OK) {
|
||||||
int flags;
|
|
||||||
|
|
||||||
if (!S_ISREG(diter->st.st_mode))
|
if (!S_ISREG(diter->st.st_mode))
|
||||||
continue;
|
continue;
|
||||||
if (diter->basename[0] == '.')
|
if (check_refname_format(diter->basename,
|
||||||
|
REFNAME_ALLOW_ONELEVEL))
|
||||||
continue;
|
continue;
|
||||||
if (ends_with(diter->basename, ".lock"))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!refs_resolve_ref_unsafe(iter->ref_store,
|
|
||||||
diter->relative_path, 0,
|
|
||||||
&iter->oid, &flags)) {
|
|
||||||
error("bad ref for %s", diter->path.buf);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
iter->base.refname = diter->relative_path;
|
iter->base.refname = diter->relative_path;
|
||||||
iter->base.oid = &iter->oid;
|
|
||||||
iter->base.flags = flags;
|
|
||||||
return ITER_OK;
|
return ITER_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2193,7 +2178,7 @@ static struct ref_iterator *reflog_iterator_begin(struct ref_store *ref_store,
|
||||||
|
|
||||||
strbuf_addf(&sb, "%s/logs", gitdir);
|
strbuf_addf(&sb, "%s/logs", gitdir);
|
||||||
|
|
||||||
diter = dir_iterator_begin(sb.buf, 0);
|
diter = dir_iterator_begin(sb.buf, DIR_ITERATOR_SORTED);
|
||||||
if (!diter) {
|
if (!diter) {
|
||||||
strbuf_release(&sb);
|
strbuf_release(&sb);
|
||||||
return empty_ref_iterator_begin();
|
return empty_ref_iterator_begin();
|
||||||
|
@ -2202,7 +2187,7 @@ static struct ref_iterator *reflog_iterator_begin(struct ref_store *ref_store,
|
||||||
CALLOC_ARRAY(iter, 1);
|
CALLOC_ARRAY(iter, 1);
|
||||||
ref_iterator = &iter->base;
|
ref_iterator = &iter->base;
|
||||||
|
|
||||||
base_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable, 0);
|
base_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable);
|
||||||
iter->dir_iterator = diter;
|
iter->dir_iterator = diter;
|
||||||
iter->ref_store = ref_store;
|
iter->ref_store = ref_store;
|
||||||
strbuf_release(&sb);
|
strbuf_release(&sb);
|
||||||
|
@ -2210,32 +2195,6 @@ static struct ref_iterator *reflog_iterator_begin(struct ref_store *ref_store,
|
||||||
return ref_iterator;
|
return ref_iterator;
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum iterator_selection reflog_iterator_select(
|
|
||||||
struct ref_iterator *iter_worktree,
|
|
||||||
struct ref_iterator *iter_common,
|
|
||||||
void *cb_data UNUSED)
|
|
||||||
{
|
|
||||||
if (iter_worktree) {
|
|
||||||
/*
|
|
||||||
* We're a bit loose here. We probably should ignore
|
|
||||||
* common refs if they are accidentally added as
|
|
||||||
* per-worktree refs.
|
|
||||||
*/
|
|
||||||
return ITER_SELECT_0;
|
|
||||||
} else if (iter_common) {
|
|
||||||
if (parse_worktree_ref(iter_common->refname, NULL, NULL,
|
|
||||||
NULL) == REF_WORKTREE_SHARED)
|
|
||||||
return ITER_SELECT_1;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The main ref store may contain main worktree's
|
|
||||||
* per-worktree refs, which should be ignored
|
|
||||||
*/
|
|
||||||
return ITER_SKIP_1;
|
|
||||||
} else
|
|
||||||
return ITER_DONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store)
|
static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store)
|
||||||
{
|
{
|
||||||
struct files_ref_store *refs =
|
struct files_ref_store *refs =
|
||||||
|
@ -2246,9 +2205,9 @@ static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_st
|
||||||
return reflog_iterator_begin(ref_store, refs->gitcommondir);
|
return reflog_iterator_begin(ref_store, refs->gitcommondir);
|
||||||
} else {
|
} else {
|
||||||
return merge_ref_iterator_begin(
|
return merge_ref_iterator_begin(
|
||||||
0, reflog_iterator_begin(ref_store, refs->base.gitdir),
|
reflog_iterator_begin(ref_store, refs->base.gitdir),
|
||||||
reflog_iterator_begin(ref_store, refs->gitcommondir),
|
reflog_iterator_begin(ref_store, refs->gitcommondir),
|
||||||
reflog_iterator_select, refs);
|
ref_iterator_select, refs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,11 +25,9 @@ int ref_iterator_abort(struct ref_iterator *ref_iterator)
|
||||||
}
|
}
|
||||||
|
|
||||||
void base_ref_iterator_init(struct ref_iterator *iter,
|
void base_ref_iterator_init(struct ref_iterator *iter,
|
||||||
struct ref_iterator_vtable *vtable,
|
struct ref_iterator_vtable *vtable)
|
||||||
int ordered)
|
|
||||||
{
|
{
|
||||||
iter->vtable = vtable;
|
iter->vtable = vtable;
|
||||||
iter->ordered = !!ordered;
|
|
||||||
iter->refname = NULL;
|
iter->refname = NULL;
|
||||||
iter->oid = NULL;
|
iter->oid = NULL;
|
||||||
iter->flags = 0;
|
iter->flags = 0;
|
||||||
|
@ -74,7 +72,7 @@ struct ref_iterator *empty_ref_iterator_begin(void)
|
||||||
struct empty_ref_iterator *iter = xcalloc(1, sizeof(*iter));
|
struct empty_ref_iterator *iter = xcalloc(1, sizeof(*iter));
|
||||||
struct ref_iterator *ref_iterator = &iter->base;
|
struct ref_iterator *ref_iterator = &iter->base;
|
||||||
|
|
||||||
base_ref_iterator_init(ref_iterator, &empty_ref_iterator_vtable, 1);
|
base_ref_iterator_init(ref_iterator, &empty_ref_iterator_vtable);
|
||||||
return ref_iterator;
|
return ref_iterator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,6 +96,49 @@ struct merge_ref_iterator {
|
||||||
struct ref_iterator **current;
|
struct ref_iterator **current;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum iterator_selection ref_iterator_select(struct ref_iterator *iter_worktree,
|
||||||
|
struct ref_iterator *iter_common,
|
||||||
|
void *cb_data UNUSED)
|
||||||
|
{
|
||||||
|
if (iter_worktree && !iter_common) {
|
||||||
|
/*
|
||||||
|
* Return the worktree ref if there are no more common refs.
|
||||||
|
*/
|
||||||
|
return ITER_SELECT_0;
|
||||||
|
} else if (iter_common) {
|
||||||
|
/*
|
||||||
|
* In case we have pending worktree and common refs we need to
|
||||||
|
* yield them based on their lexicographical order. Worktree
|
||||||
|
* refs that have the same name as common refs shadow the
|
||||||
|
* latter.
|
||||||
|
*/
|
||||||
|
if (iter_worktree) {
|
||||||
|
int cmp = strcmp(iter_worktree->refname,
|
||||||
|
iter_common->refname);
|
||||||
|
if (cmp < 0)
|
||||||
|
return ITER_SELECT_0;
|
||||||
|
else if (!cmp)
|
||||||
|
return ITER_SELECT_0_SKIP_1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We now know that the lexicographically-next ref is a common
|
||||||
|
* ref. When the common ref is a shared one we return it.
|
||||||
|
*/
|
||||||
|
if (parse_worktree_ref(iter_common->refname, NULL, NULL,
|
||||||
|
NULL) == REF_WORKTREE_SHARED)
|
||||||
|
return ITER_SELECT_1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Otherwise, if the common ref is a per-worktree ref we skip
|
||||||
|
* it because it would belong to the main worktree, not ours.
|
||||||
|
*/
|
||||||
|
return ITER_SKIP_1;
|
||||||
|
} else {
|
||||||
|
return ITER_DONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int merge_ref_iterator_advance(struct ref_iterator *ref_iterator)
|
static int merge_ref_iterator_advance(struct ref_iterator *ref_iterator)
|
||||||
{
|
{
|
||||||
struct merge_ref_iterator *iter =
|
struct merge_ref_iterator *iter =
|
||||||
|
@ -207,7 +248,6 @@ static struct ref_iterator_vtable merge_ref_iterator_vtable = {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ref_iterator *merge_ref_iterator_begin(
|
struct ref_iterator *merge_ref_iterator_begin(
|
||||||
int ordered,
|
|
||||||
struct ref_iterator *iter0, struct ref_iterator *iter1,
|
struct ref_iterator *iter0, struct ref_iterator *iter1,
|
||||||
ref_iterator_select_fn *select, void *cb_data)
|
ref_iterator_select_fn *select, void *cb_data)
|
||||||
{
|
{
|
||||||
|
@ -222,7 +262,7 @@ struct ref_iterator *merge_ref_iterator_begin(
|
||||||
* references through only if they exist in both iterators.
|
* references through only if they exist in both iterators.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
base_ref_iterator_init(ref_iterator, &merge_ref_iterator_vtable, ordered);
|
base_ref_iterator_init(ref_iterator, &merge_ref_iterator_vtable);
|
||||||
iter->iter0 = iter0;
|
iter->iter0 = iter0;
|
||||||
iter->iter1 = iter1;
|
iter->iter1 = iter1;
|
||||||
iter->select = select;
|
iter->select = select;
|
||||||
|
@ -271,12 +311,9 @@ struct ref_iterator *overlay_ref_iterator_begin(
|
||||||
} else if (is_empty_ref_iterator(back)) {
|
} else if (is_empty_ref_iterator(back)) {
|
||||||
ref_iterator_abort(back);
|
ref_iterator_abort(back);
|
||||||
return front;
|
return front;
|
||||||
} else if (!front->ordered || !back->ordered) {
|
|
||||||
BUG("overlay_ref_iterator requires ordered inputs");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return merge_ref_iterator_begin(1, front, back,
|
return merge_ref_iterator_begin(front, back, overlay_iterator_select, NULL);
|
||||||
overlay_iterator_select, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct prefix_ref_iterator {
|
struct prefix_ref_iterator {
|
||||||
|
@ -315,16 +352,12 @@ static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator)
|
||||||
|
|
||||||
if (cmp > 0) {
|
if (cmp > 0) {
|
||||||
/*
|
/*
|
||||||
* If the source iterator is ordered, then we
|
* As the source iterator is ordered, we
|
||||||
* can stop the iteration as soon as we see a
|
* can stop the iteration as soon as we see a
|
||||||
* refname that comes after the prefix:
|
* refname that comes after the prefix:
|
||||||
*/
|
*/
|
||||||
if (iter->iter0->ordered) {
|
|
||||||
ok = ref_iterator_abort(iter->iter0);
|
ok = ref_iterator_abort(iter->iter0);
|
||||||
break;
|
break;
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iter->trim) {
|
if (iter->trim) {
|
||||||
|
@ -396,7 +429,7 @@ struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
|
||||||
CALLOC_ARRAY(iter, 1);
|
CALLOC_ARRAY(iter, 1);
|
||||||
ref_iterator = &iter->base;
|
ref_iterator = &iter->base;
|
||||||
|
|
||||||
base_ref_iterator_init(ref_iterator, &prefix_ref_iterator_vtable, iter0->ordered);
|
base_ref_iterator_init(ref_iterator, &prefix_ref_iterator_vtable);
|
||||||
|
|
||||||
iter->iter0 = iter0;
|
iter->iter0 = iter0;
|
||||||
iter->prefix = xstrdup(prefix);
|
iter->prefix = xstrdup(prefix);
|
||||||
|
|
|
@ -1111,7 +1111,7 @@ static struct ref_iterator *packed_ref_iterator_begin(
|
||||||
|
|
||||||
CALLOC_ARRAY(iter, 1);
|
CALLOC_ARRAY(iter, 1);
|
||||||
ref_iterator = &iter->base;
|
ref_iterator = &iter->base;
|
||||||
base_ref_iterator_init(ref_iterator, &packed_ref_iterator_vtable, 1);
|
base_ref_iterator_init(ref_iterator, &packed_ref_iterator_vtable);
|
||||||
|
|
||||||
if (exclude_patterns)
|
if (exclude_patterns)
|
||||||
populate_excluded_jump_list(iter, snapshot, exclude_patterns);
|
populate_excluded_jump_list(iter, snapshot, exclude_patterns);
|
||||||
|
|
|
@ -486,7 +486,7 @@ struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache,
|
||||||
|
|
||||||
CALLOC_ARRAY(iter, 1);
|
CALLOC_ARRAY(iter, 1);
|
||||||
ref_iterator = &iter->base;
|
ref_iterator = &iter->base;
|
||||||
base_ref_iterator_init(ref_iterator, &cache_ref_iterator_vtable, 1);
|
base_ref_iterator_init(ref_iterator, &cache_ref_iterator_vtable);
|
||||||
ALLOC_GROW(iter->levels, 10, iter->levels_alloc);
|
ALLOC_GROW(iter->levels, 10, iter->levels_alloc);
|
||||||
|
|
||||||
iter->levels_nr = 1;
|
iter->levels_nr = 1;
|
||||||
|
|
|
@ -312,13 +312,6 @@ enum do_for_each_ref_flags {
|
||||||
*/
|
*/
|
||||||
struct ref_iterator {
|
struct ref_iterator {
|
||||||
struct ref_iterator_vtable *vtable;
|
struct ref_iterator_vtable *vtable;
|
||||||
|
|
||||||
/*
|
|
||||||
* Does this `ref_iterator` iterate over references in order
|
|
||||||
* by refname?
|
|
||||||
*/
|
|
||||||
unsigned int ordered : 1;
|
|
||||||
|
|
||||||
const char *refname;
|
const char *refname;
|
||||||
const struct object_id *oid;
|
const struct object_id *oid;
|
||||||
unsigned int flags;
|
unsigned int flags;
|
||||||
|
@ -386,15 +379,22 @@ typedef enum iterator_selection ref_iterator_select_fn(
|
||||||
struct ref_iterator *iter0, struct ref_iterator *iter1,
|
struct ref_iterator *iter0, struct ref_iterator *iter1,
|
||||||
void *cb_data);
|
void *cb_data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* An implementation of ref_iterator_select_fn that merges worktree and common
|
||||||
|
* refs. Per-worktree refs from the common iterator are ignored, worktree refs
|
||||||
|
* override common refs. Refs are selected lexicographically.
|
||||||
|
*/
|
||||||
|
enum iterator_selection ref_iterator_select(struct ref_iterator *iter_worktree,
|
||||||
|
struct ref_iterator *iter_common,
|
||||||
|
void *cb_data);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Iterate over the entries from iter0 and iter1, with the values
|
* Iterate over the entries from iter0 and iter1, with the values
|
||||||
* interleaved as directed by the select function. The iterator takes
|
* interleaved as directed by the select function. The iterator takes
|
||||||
* ownership of iter0 and iter1 and frees them when the iteration is
|
* ownership of iter0 and iter1 and frees them when the iteration is
|
||||||
* over. A derived class should set `ordered` to 1 or 0 based on
|
* over.
|
||||||
* whether it generates its output in order by reference name.
|
|
||||||
*/
|
*/
|
||||||
struct ref_iterator *merge_ref_iterator_begin(
|
struct ref_iterator *merge_ref_iterator_begin(
|
||||||
int ordered,
|
|
||||||
struct ref_iterator *iter0, struct ref_iterator *iter1,
|
struct ref_iterator *iter0, struct ref_iterator *iter1,
|
||||||
ref_iterator_select_fn *select, void *cb_data);
|
ref_iterator_select_fn *select, void *cb_data);
|
||||||
|
|
||||||
|
@ -423,8 +423,6 @@ struct ref_iterator *overlay_ref_iterator_begin(
|
||||||
* As an convenience to callers, if prefix is the empty string and
|
* As an convenience to callers, if prefix is the empty string and
|
||||||
* trim is zero, this function returns iter0 directly, without
|
* trim is zero, this function returns iter0 directly, without
|
||||||
* wrapping it.
|
* wrapping it.
|
||||||
*
|
|
||||||
* The resulting ref_iterator is ordered if iter0 is.
|
|
||||||
*/
|
*/
|
||||||
struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
|
struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
|
||||||
const char *prefix,
|
const char *prefix,
|
||||||
|
@ -435,14 +433,11 @@ struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
|
||||||
/*
|
/*
|
||||||
* Base class constructor for ref_iterators. Initialize the
|
* Base class constructor for ref_iterators. Initialize the
|
||||||
* ref_iterator part of iter, setting its vtable pointer as specified.
|
* ref_iterator part of iter, setting its vtable pointer as specified.
|
||||||
* `ordered` should be set to 1 if the iterator will iterate over
|
|
||||||
* references in order by refname; otherwise it should be set to 0.
|
|
||||||
* This is meant to be called only by the initializers of derived
|
* This is meant to be called only by the initializers of derived
|
||||||
* classes.
|
* classes.
|
||||||
*/
|
*/
|
||||||
void base_ref_iterator_init(struct ref_iterator *iter,
|
void base_ref_iterator_init(struct ref_iterator *iter,
|
||||||
struct ref_iterator_vtable *vtable,
|
struct ref_iterator_vtable *vtable);
|
||||||
int ordered);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Base class destructor for ref_iterators. Destroy the ref_iterator
|
* Base class destructor for ref_iterators. Destroy the ref_iterator
|
||||||
|
|
|
@ -479,7 +479,7 @@ static struct reftable_ref_iterator *ref_iterator_for_stack(struct reftable_ref_
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
iter = xcalloc(1, sizeof(*iter));
|
iter = xcalloc(1, sizeof(*iter));
|
||||||
base_ref_iterator_init(&iter->base, &reftable_ref_iterator_vtable, 1);
|
base_ref_iterator_init(&iter->base, &reftable_ref_iterator_vtable);
|
||||||
iter->prefix = prefix;
|
iter->prefix = prefix;
|
||||||
iter->base.oid = &iter->oid;
|
iter->base.oid = &iter->oid;
|
||||||
iter->flags = flags;
|
iter->flags = flags;
|
||||||
|
@ -504,49 +504,6 @@ done:
|
||||||
return iter;
|
return iter;
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum iterator_selection iterator_select(struct ref_iterator *iter_worktree,
|
|
||||||
struct ref_iterator *iter_common,
|
|
||||||
void *cb_data UNUSED)
|
|
||||||
{
|
|
||||||
if (iter_worktree && !iter_common) {
|
|
||||||
/*
|
|
||||||
* Return the worktree ref if there are no more common refs.
|
|
||||||
*/
|
|
||||||
return ITER_SELECT_0;
|
|
||||||
} else if (iter_common) {
|
|
||||||
/*
|
|
||||||
* In case we have pending worktree and common refs we need to
|
|
||||||
* yield them based on their lexicographical order. Worktree
|
|
||||||
* refs that have the same name as common refs shadow the
|
|
||||||
* latter.
|
|
||||||
*/
|
|
||||||
if (iter_worktree) {
|
|
||||||
int cmp = strcmp(iter_worktree->refname,
|
|
||||||
iter_common->refname);
|
|
||||||
if (cmp < 0)
|
|
||||||
return ITER_SELECT_0;
|
|
||||||
else if (!cmp)
|
|
||||||
return ITER_SELECT_0_SKIP_1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We now know that the lexicographically-next ref is a common
|
|
||||||
* ref. When the common ref is a shared one we return it.
|
|
||||||
*/
|
|
||||||
if (parse_worktree_ref(iter_common->refname, NULL, NULL,
|
|
||||||
NULL) == REF_WORKTREE_SHARED)
|
|
||||||
return ITER_SELECT_1;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Otherwise, if the common ref is a per-worktree ref we skip
|
|
||||||
* it because it would belong to the main worktree, not ours.
|
|
||||||
*/
|
|
||||||
return ITER_SKIP_1;
|
|
||||||
} else {
|
|
||||||
return ITER_DONE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct ref_iterator *reftable_be_iterator_begin(struct ref_store *ref_store,
|
static struct ref_iterator *reftable_be_iterator_begin(struct ref_store *ref_store,
|
||||||
const char *prefix,
|
const char *prefix,
|
||||||
const char **exclude_patterns,
|
const char **exclude_patterns,
|
||||||
|
@ -575,8 +532,8 @@ static struct ref_iterator *reftable_be_iterator_begin(struct ref_store *ref_sto
|
||||||
* single iterator.
|
* single iterator.
|
||||||
*/
|
*/
|
||||||
worktree_iter = ref_iterator_for_stack(refs, refs->worktree_stack, prefix, flags);
|
worktree_iter = ref_iterator_for_stack(refs, refs->worktree_stack, prefix, flags);
|
||||||
return merge_ref_iterator_begin(1, &worktree_iter->base, &main_iter->base,
|
return merge_ref_iterator_begin(&worktree_iter->base, &main_iter->base,
|
||||||
iterator_select, NULL);
|
ref_iterator_select, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int reftable_be_read_raw_ref(struct ref_store *ref_store,
|
static int reftable_be_read_raw_ref(struct ref_store *ref_store,
|
||||||
|
@ -1637,7 +1594,6 @@ struct reftable_reflog_iterator {
|
||||||
struct reftable_ref_store *refs;
|
struct reftable_ref_store *refs;
|
||||||
struct reftable_iterator iter;
|
struct reftable_iterator iter;
|
||||||
struct reftable_log_record log;
|
struct reftable_log_record log;
|
||||||
struct object_id oid;
|
|
||||||
char *last_name;
|
char *last_name;
|
||||||
int err;
|
int err;
|
||||||
};
|
};
|
||||||
|
@ -1648,8 +1604,6 @@ static int reftable_reflog_iterator_advance(struct ref_iterator *ref_iterator)
|
||||||
(struct reftable_reflog_iterator *)ref_iterator;
|
(struct reftable_reflog_iterator *)ref_iterator;
|
||||||
|
|
||||||
while (!iter->err) {
|
while (!iter->err) {
|
||||||
int flags;
|
|
||||||
|
|
||||||
iter->err = reftable_iterator_next_log(&iter->iter, &iter->log);
|
iter->err = reftable_iterator_next_log(&iter->iter, &iter->log);
|
||||||
if (iter->err)
|
if (iter->err)
|
||||||
break;
|
break;
|
||||||
|
@ -1662,17 +1616,13 @@ static int reftable_reflog_iterator_advance(struct ref_iterator *ref_iterator)
|
||||||
if (iter->last_name && !strcmp(iter->log.refname, iter->last_name))
|
if (iter->last_name && !strcmp(iter->log.refname, iter->last_name))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!refs_resolve_ref_unsafe(&iter->refs->base, iter->log.refname,
|
if (check_refname_format(iter->log.refname,
|
||||||
0, &iter->oid, &flags)) {
|
REFNAME_ALLOW_ONELEVEL))
|
||||||
error(_("bad ref for %s"), iter->log.refname);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
free(iter->last_name);
|
free(iter->last_name);
|
||||||
iter->last_name = xstrdup(iter->log.refname);
|
iter->last_name = xstrdup(iter->log.refname);
|
||||||
iter->base.refname = iter->log.refname;
|
iter->base.refname = iter->log.refname;
|
||||||
iter->base.oid = &iter->oid;
|
|
||||||
iter->base.flags = flags;
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1723,9 +1673,8 @@ static struct reftable_reflog_iterator *reflog_iterator_for_stack(struct reftabl
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
iter = xcalloc(1, sizeof(*iter));
|
iter = xcalloc(1, sizeof(*iter));
|
||||||
base_ref_iterator_init(&iter->base, &reftable_reflog_iterator_vtable, 1);
|
base_ref_iterator_init(&iter->base, &reftable_reflog_iterator_vtable);
|
||||||
iter->refs = refs;
|
iter->refs = refs;
|
||||||
iter->base.oid = &iter->oid;
|
|
||||||
|
|
||||||
ret = refs->err;
|
ret = refs->err;
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -1758,8 +1707,8 @@ static struct ref_iterator *reftable_be_reflog_iterator_begin(struct ref_store *
|
||||||
|
|
||||||
worktree_iter = reflog_iterator_for_stack(refs, refs->worktree_stack);
|
worktree_iter = reflog_iterator_for_stack(refs, refs->worktree_stack);
|
||||||
|
|
||||||
return merge_ref_iterator_begin(1, &worktree_iter->base, &main_iter->base,
|
return merge_ref_iterator_begin(&worktree_iter->base, &main_iter->base,
|
||||||
iterator_select, NULL);
|
ref_iterator_select, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int yield_log_record(struct reftable_log_record *log,
|
static int yield_log_record(struct reftable_log_record *log,
|
||||||
|
|
|
@ -1686,9 +1686,7 @@ static int handle_one_reflog_ent(struct object_id *ooid, struct object_id *noid,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int handle_one_reflog(const char *refname_in_wt,
|
static int handle_one_reflog(const char *refname_in_wt, void *cb_data)
|
||||||
const struct object_id *oid UNUSED,
|
|
||||||
int flag UNUSED, void *cb_data)
|
|
||||||
{
|
{
|
||||||
struct all_refs_cb *cb = cb_data;
|
struct all_refs_cb *cb = cb_data;
|
||||||
struct strbuf refname = STRBUF_INIT;
|
struct strbuf refname = STRBUF_INIT;
|
||||||
|
|
|
@ -221,13 +221,19 @@ static int cmd_verify_ref(struct ref_store *refs, const char **argv)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int each_reflog(const char *refname, void *cb_data UNUSED)
|
||||||
|
{
|
||||||
|
printf("%s\n", refname);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int cmd_for_each_reflog(struct ref_store *refs,
|
static int cmd_for_each_reflog(struct ref_store *refs,
|
||||||
const char **argv UNUSED)
|
const char **argv UNUSED)
|
||||||
{
|
{
|
||||||
return refs_for_each_reflog(refs, each_ref, NULL);
|
return refs_for_each_reflog(refs, each_reflog, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int each_reflog(struct object_id *old_oid, struct object_id *new_oid,
|
static int each_reflog_ent(struct object_id *old_oid, struct object_id *new_oid,
|
||||||
const char *committer, timestamp_t timestamp,
|
const char *committer, timestamp_t timestamp,
|
||||||
int tz, const char *msg, void *cb_data UNUSED)
|
int tz, const char *msg, void *cb_data UNUSED)
|
||||||
{
|
{
|
||||||
|
@ -241,14 +247,14 @@ static int cmd_for_each_reflog_ent(struct ref_store *refs, const char **argv)
|
||||||
{
|
{
|
||||||
const char *refname = notnull(*argv++, "refname");
|
const char *refname = notnull(*argv++, "refname");
|
||||||
|
|
||||||
return refs_for_each_reflog_ent(refs, refname, each_reflog, refs);
|
return refs_for_each_reflog_ent(refs, refname, each_reflog_ent, refs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cmd_for_each_reflog_ent_reverse(struct ref_store *refs, const char **argv)
|
static int cmd_for_each_reflog_ent_reverse(struct ref_store *refs, const char **argv)
|
||||||
{
|
{
|
||||||
const char *refname = notnull(*argv++, "refname");
|
const char *refname = notnull(*argv++, "refname");
|
||||||
|
|
||||||
return refs_for_each_reflog_ent_reverse(refs, refname, each_reflog, refs);
|
return refs_for_each_reflog_ent_reverse(refs, refname, each_reflog_ent, refs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cmd_reflog_exists(struct ref_store *refs, const char **argv)
|
static int cmd_reflog_exists(struct ref_store *refs, const char **argv)
|
||||||
|
|
|
@ -287,23 +287,23 @@ test_expect_success 'for_each_reflog()' '
|
||||||
mkdir -p .git/worktrees/wt/logs/refs/bisect &&
|
mkdir -p .git/worktrees/wt/logs/refs/bisect &&
|
||||||
echo $ZERO_OID > .git/worktrees/wt/logs/refs/bisect/wt-random &&
|
echo $ZERO_OID > .git/worktrees/wt/logs/refs/bisect/wt-random &&
|
||||||
|
|
||||||
$RWT for-each-reflog | cut -d" " -f 2- | sort >actual &&
|
$RWT for-each-reflog >actual &&
|
||||||
cat >expected <<-\EOF &&
|
cat >expected <<-\EOF &&
|
||||||
HEAD 0x1
|
HEAD
|
||||||
PSEUDO-WT 0x0
|
PSEUDO-WT
|
||||||
refs/bisect/wt-random 0x0
|
refs/bisect/wt-random
|
||||||
refs/heads/main 0x0
|
refs/heads/main
|
||||||
refs/heads/wt-main 0x0
|
refs/heads/wt-main
|
||||||
EOF
|
EOF
|
||||||
test_cmp expected actual &&
|
test_cmp expected actual &&
|
||||||
|
|
||||||
$RMAIN for-each-reflog | cut -d" " -f 2- | sort >actual &&
|
$RMAIN for-each-reflog >actual &&
|
||||||
cat >expected <<-\EOF &&
|
cat >expected <<-\EOF &&
|
||||||
HEAD 0x1
|
HEAD
|
||||||
PSEUDO-MAIN 0x0
|
PSEUDO-MAIN
|
||||||
refs/bisect/random 0x0
|
refs/bisect/random
|
||||||
refs/heads/main 0x0
|
refs/heads/main
|
||||||
refs/heads/wt-main 0x0
|
refs/heads/wt-main
|
||||||
EOF
|
EOF
|
||||||
test_cmp expected actual
|
test_cmp expected actual
|
||||||
'
|
'
|
||||||
|
|
|
@ -68,11 +68,11 @@ test_expect_success 'verify_ref(new-main)' '
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'for_each_reflog()' '
|
test_expect_success 'for_each_reflog()' '
|
||||||
$RUN for-each-reflog | sort -k2 | cut -d" " -f 2- >actual &&
|
$RUN for-each-reflog >actual &&
|
||||||
cat >expected <<-\EOF &&
|
cat >expected <<-\EOF &&
|
||||||
HEAD 0x1
|
HEAD
|
||||||
refs/heads/main 0x0
|
refs/heads/main
|
||||||
refs/heads/new-main 0x0
|
refs/heads/new-main
|
||||||
EOF
|
EOF
|
||||||
test_cmp expected actual
|
test_cmp expected actual
|
||||||
'
|
'
|
||||||
|
|
|
@ -63,11 +63,11 @@ test_expect_success 'verify_ref(new-main)' '
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'for_each_reflog()' '
|
test_expect_success 'for_each_reflog()' '
|
||||||
$RUN for-each-reflog | sort | cut -d" " -f 2- >actual &&
|
$RUN for-each-reflog >actual &&
|
||||||
cat >expected <<-\EOF &&
|
cat >expected <<-\EOF &&
|
||||||
HEAD 0x1
|
HEAD
|
||||||
refs/heads/main 0x0
|
refs/heads/main
|
||||||
refs/heads/new-main 0x0
|
refs/heads/new-main
|
||||||
EOF
|
EOF
|
||||||
test_cmp expected actual
|
test_cmp expected actual
|
||||||
'
|
'
|
||||||
|
|
|
@ -436,4 +436,112 @@ test_expect_success 'empty reflog' '
|
||||||
test_must_be_empty err
|
test_must_be_empty err
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'list reflogs' '
|
||||||
|
test_when_finished "rm -rf repo" &&
|
||||||
|
git init repo &&
|
||||||
|
(
|
||||||
|
cd repo &&
|
||||||
|
git reflog list >actual &&
|
||||||
|
test_must_be_empty actual &&
|
||||||
|
|
||||||
|
test_commit A &&
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
HEAD
|
||||||
|
refs/heads/main
|
||||||
|
EOF
|
||||||
|
git reflog list >actual &&
|
||||||
|
test_cmp expect actual &&
|
||||||
|
|
||||||
|
git branch b &&
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
HEAD
|
||||||
|
refs/heads/b
|
||||||
|
refs/heads/main
|
||||||
|
EOF
|
||||||
|
git reflog list >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'list reflogs with worktree' '
|
||||||
|
test_when_finished "rm -rf repo" &&
|
||||||
|
git init repo &&
|
||||||
|
(
|
||||||
|
cd repo &&
|
||||||
|
|
||||||
|
test_commit A &&
|
||||||
|
git worktree add wt &&
|
||||||
|
git -c core.logAllRefUpdates=always \
|
||||||
|
update-ref refs/worktree/main HEAD &&
|
||||||
|
git -c core.logAllRefUpdates=always \
|
||||||
|
update-ref refs/worktree/per-worktree HEAD &&
|
||||||
|
git -c core.logAllRefUpdates=always -C wt \
|
||||||
|
update-ref refs/worktree/per-worktree HEAD &&
|
||||||
|
git -c core.logAllRefUpdates=always -C wt \
|
||||||
|
update-ref refs/worktree/worktree HEAD &&
|
||||||
|
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
HEAD
|
||||||
|
refs/heads/main
|
||||||
|
refs/heads/wt
|
||||||
|
refs/worktree/main
|
||||||
|
refs/worktree/per-worktree
|
||||||
|
EOF
|
||||||
|
git reflog list >actual &&
|
||||||
|
test_cmp expect actual &&
|
||||||
|
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
HEAD
|
||||||
|
refs/heads/main
|
||||||
|
refs/heads/wt
|
||||||
|
refs/worktree/per-worktree
|
||||||
|
refs/worktree/worktree
|
||||||
|
EOF
|
||||||
|
git -C wt reflog list >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'reflog list returns error with additional args' '
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
error: list does not accept arguments: ${SQ}bogus${SQ}
|
||||||
|
EOF
|
||||||
|
test_must_fail git reflog list bogus 2>err &&
|
||||||
|
test_cmp expect err
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'reflog for symref with unborn target can be listed' '
|
||||||
|
test_when_finished "rm -rf repo" &&
|
||||||
|
git init repo &&
|
||||||
|
(
|
||||||
|
cd repo &&
|
||||||
|
test_commit A &&
|
||||||
|
git symbolic-ref HEAD refs/heads/unborn &&
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
HEAD
|
||||||
|
refs/heads/main
|
||||||
|
EOF
|
||||||
|
git reflog list >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'reflog with invalid object ID can be listed' '
|
||||||
|
test_when_finished "rm -rf repo" &&
|
||||||
|
git init repo &&
|
||||||
|
(
|
||||||
|
cd repo &&
|
||||||
|
test_commit A &&
|
||||||
|
test-tool ref-store main update-ref msg refs/heads/missing \
|
||||||
|
$(test_oid deadbeef) "$ZERO_OID" REF_SKIP_OID_VERIFICATION &&
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
HEAD
|
||||||
|
refs/heads/main
|
||||||
|
refs/heads/missing
|
||||||
|
EOF
|
||||||
|
git reflog list >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
|
Loading…
Reference in New Issue