Skip to content

Commit 510a27e

Browse files
committed
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()` directly
2 parents 221c3da + d699d15 commit 510a27e

20 files changed

+380
-205
lines changed

Documentation/git-reflog.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ SYNOPSIS
1010
--------
1111
[verse]
1212
'git reflog' [show] [<log-options>] [<ref>]
13+
'git reflog list'
1314
'git reflog expire' [--expire=<time>] [--expire-unreachable=<time>]
1415
[--rewrite] [--updateref] [--stale-fix]
1516
[--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]
@@ -39,6 +40,8 @@ actions, and in addition the `HEAD` reflog records branch switching.
3940
`git reflog show` is an alias for `git log -g --abbrev-commit
4041
--pretty=oneline`; see linkgit:git-log[1] for more information.
4142

43+
The "list" subcommand lists all refs which have a corresponding reflog.
44+
4245
The "expire" subcommand prunes older reflog entries. Entries older
4346
than `expire` time, or entries older than `expire-unreachable` time
4447
and not reachable from the current tip, are removed from the reflog.

builtin/fsck.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -509,9 +509,7 @@ static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid
509509
return 0;
510510
}
511511

512-
static int fsck_handle_reflog(const char *logname,
513-
const struct object_id *oid UNUSED,
514-
int flag UNUSED, void *cb_data)
512+
static int fsck_handle_reflog(const char *logname, void *cb_data)
515513
{
516514
struct strbuf refname = STRBUF_INIT;
517515

builtin/reflog.c

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,15 @@
77
#include "wildmatch.h"
88
#include "worktree.h"
99
#include "reflog.h"
10+
#include "refs.h"
1011
#include "parse-options.h"
1112

1213
#define BUILTIN_REFLOG_SHOW_USAGE \
1314
N_("git reflog [show] [<log-options>] [<ref>]")
1415

16+
#define BUILTIN_REFLOG_LIST_USAGE \
17+
N_("git reflog list")
18+
1519
#define BUILTIN_REFLOG_EXPIRE_USAGE \
1620
N_("git reflog expire [--expire=<time>] [--expire-unreachable=<time>]\n" \
1721
" [--rewrite] [--updateref] [--stale-fix]\n" \
@@ -29,6 +33,11 @@ static const char *const reflog_show_usage[] = {
2933
NULL,
3034
};
3135

36+
static const char *const reflog_list_usage[] = {
37+
BUILTIN_REFLOG_LIST_USAGE,
38+
NULL,
39+
};
40+
3241
static const char *const reflog_expire_usage[] = {
3342
BUILTIN_REFLOG_EXPIRE_USAGE,
3443
NULL
@@ -46,6 +55,7 @@ static const char *const reflog_exists_usage[] = {
4655

4756
static const char *const reflog_usage[] = {
4857
BUILTIN_REFLOG_SHOW_USAGE,
58+
BUILTIN_REFLOG_LIST_USAGE,
4959
BUILTIN_REFLOG_EXPIRE_USAGE,
5060
BUILTIN_REFLOG_DELETE_USAGE,
5161
BUILTIN_REFLOG_EXISTS_USAGE,
@@ -60,8 +70,7 @@ struct worktree_reflogs {
6070
struct string_list reflogs;
6171
};
6272

63-
static int collect_reflog(const char *ref, const struct object_id *oid UNUSED,
64-
int flags UNUSED, void *cb_data)
73+
static int collect_reflog(const char *ref, void *cb_data)
6574
{
6675
struct worktree_reflogs *cb = cb_data;
6776
struct worktree *worktree = cb->worktree;
@@ -238,6 +247,29 @@ static int cmd_reflog_show(int argc, const char **argv, const char *prefix)
238247
return cmd_log_reflog(argc, argv, prefix);
239248
}
240249

250+
static int show_reflog(const char *refname, void *cb_data UNUSED)
251+
{
252+
printf("%s\n", refname);
253+
return 0;
254+
}
255+
256+
static int cmd_reflog_list(int argc, const char **argv, const char *prefix)
257+
{
258+
struct option options[] = {
259+
OPT_END()
260+
};
261+
struct ref_store *ref_store;
262+
263+
argc = parse_options(argc, argv, prefix, options, reflog_list_usage, 0);
264+
if (argc)
265+
return error(_("%s does not accept arguments: '%s'"),
266+
"list", argv[0]);
267+
268+
ref_store = get_main_ref_store(the_repository);
269+
270+
return refs_for_each_reflog(ref_store, show_reflog, NULL);
271+
}
272+
241273
static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
242274
{
243275
struct cmd_reflog_expire_cb cmd = { 0 };
@@ -417,6 +449,7 @@ int cmd_reflog(int argc, const char **argv, const char *prefix)
417449
parse_opt_subcommand_fn *fn = NULL;
418450
struct option options[] = {
419451
OPT_SUBCOMMAND("show", &fn, cmd_reflog_show),
452+
OPT_SUBCOMMAND("list", &fn, cmd_reflog_list),
420453
OPT_SUBCOMMAND("expire", &fn, cmd_reflog_expire),
421454
OPT_SUBCOMMAND("delete", &fn, cmd_reflog_delete),
422455
OPT_SUBCOMMAND("exists", &fn, cmd_reflog_exists),

dir-iterator.c

Lines changed: 89 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,19 @@
22
#include "dir.h"
33
#include "iterator.h"
44
#include "dir-iterator.h"
5+
#include "string-list.h"
56

67
struct dir_iterator_level {
78
DIR *dir;
89

10+
/*
11+
* The directory entries of the current level. This list will only be
12+
* populated when the iterator is ordered. In that case, `dir` will be
13+
* set to `NULL`.
14+
*/
15+
struct string_list entries;
16+
size_t entries_idx;
17+
918
/*
1019
* The length of the directory part of path at this level
1120
* (including a trailing '/'):
@@ -43,6 +52,31 @@ struct dir_iterator_int {
4352
unsigned int flags;
4453
};
4554

55+
static int next_directory_entry(DIR *dir, const char *path,
56+
struct dirent **out)
57+
{
58+
struct dirent *de;
59+
60+
repeat:
61+
errno = 0;
62+
de = readdir(dir);
63+
if (!de) {
64+
if (errno) {
65+
warning_errno("error reading directory '%s'",
66+
path);
67+
return -1;
68+
}
69+
70+
return 1;
71+
}
72+
73+
if (is_dot_or_dotdot(de->d_name))
74+
goto repeat;
75+
76+
*out = de;
77+
return 0;
78+
}
79+
4680
/*
4781
* Push a level in the iter stack and initialize it with information from
4882
* 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)
72106
return -1;
73107
}
74108

109+
string_list_init_dup(&level->entries);
110+
level->entries_idx = 0;
111+
112+
/*
113+
* When the iterator is sorted we read and sort all directory entries
114+
* directly.
115+
*/
116+
if (iter->flags & DIR_ITERATOR_SORTED) {
117+
struct dirent *de;
118+
119+
while (1) {
120+
int ret = next_directory_entry(level->dir, iter->base.path.buf, &de);
121+
if (ret < 0) {
122+
if (errno != ENOENT &&
123+
iter->flags & DIR_ITERATOR_PEDANTIC)
124+
return -1;
125+
continue;
126+
} else if (ret > 0) {
127+
break;
128+
}
129+
130+
string_list_append(&level->entries, de->d_name);
131+
}
132+
string_list_sort(&level->entries);
133+
134+
closedir(level->dir);
135+
level->dir = NULL;
136+
}
137+
75138
return 0;
76139
}
77140

@@ -88,21 +151,22 @@ static int pop_level(struct dir_iterator_int *iter)
88151
warning_errno("error closing directory '%s'",
89152
iter->base.path.buf);
90153
level->dir = NULL;
154+
string_list_clear(&level->entries, 0);
91155

92156
return --iter->levels_nr;
93157
}
94158

95159
/*
96160
* Populate iter->base with the necessary information on the next iteration
97-
* entry, represented by the given dirent de. Return 0 on success and -1
161+
* entry, represented by the given name. Return 0 on success and -1
98162
* otherwise, setting errno accordingly.
99163
*/
100164
static int prepare_next_entry_data(struct dir_iterator_int *iter,
101-
struct dirent *de)
165+
const char *name)
102166
{
103167
int err, saved_errno;
104168

105-
strbuf_addstr(&iter->base.path, de->d_name);
169+
strbuf_addstr(&iter->base.path, name);
106170
/*
107171
* We have to reset these because the path strbuf might have
108172
* been realloc()ed at the previous strbuf_addstr().
@@ -139,27 +203,34 @@ int dir_iterator_advance(struct dir_iterator *dir_iterator)
139203
struct dirent *de;
140204
struct dir_iterator_level *level =
141205
&iter->levels[iter->levels_nr - 1];
206+
const char *name;
142207

143208
strbuf_setlen(&iter->base.path, level->prefix_len);
144-
errno = 0;
145-
de = readdir(level->dir);
146209

147-
if (!de) {
148-
if (errno) {
149-
warning_errno("error reading directory '%s'",
150-
iter->base.path.buf);
210+
if (level->dir) {
211+
int ret = next_directory_entry(level->dir, iter->base.path.buf, &de);
212+
if (ret < 0) {
151213
if (iter->flags & DIR_ITERATOR_PEDANTIC)
152214
goto error_out;
153-
} else if (pop_level(iter) == 0) {
154-
return dir_iterator_abort(dir_iterator);
215+
continue;
216+
} else if (ret > 0) {
217+
if (pop_level(iter) == 0)
218+
return dir_iterator_abort(dir_iterator);
219+
continue;
155220
}
156-
continue;
157-
}
158221

159-
if (is_dot_or_dotdot(de->d_name))
160-
continue;
222+
name = de->d_name;
223+
} else {
224+
if (level->entries_idx >= level->entries.nr) {
225+
if (pop_level(iter) == 0)
226+
return dir_iterator_abort(dir_iterator);
227+
continue;
228+
}
161229

162-
if (prepare_next_entry_data(iter, de)) {
230+
name = level->entries.items[level->entries_idx++].string;
231+
}
232+
233+
if (prepare_next_entry_data(iter, name)) {
163234
if (errno != ENOENT && iter->flags & DIR_ITERATOR_PEDANTIC)
164235
goto error_out;
165236
continue;
@@ -188,6 +259,8 @@ int dir_iterator_abort(struct dir_iterator *dir_iterator)
188259
warning_errno("error closing directory '%s'",
189260
iter->base.path.buf);
190261
}
262+
263+
string_list_clear(&level->entries, 0);
191264
}
192265

193266
free(iter->levels);

dir-iterator.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,11 @@
5454
* and ITER_ERROR is returned immediately. In both cases, a meaningful
5555
* warning is emitted. Note: ENOENT errors are always ignored so that
5656
* the API users may remove files during iteration.
57+
*
58+
* - DIR_ITERATOR_SORTED: sort directory entries alphabetically.
5759
*/
5860
#define DIR_ITERATOR_PEDANTIC (1 << 0)
61+
#define DIR_ITERATOR_SORTED (1 << 1)
5962

6063
struct dir_iterator {
6164
/* The current path: */

refs.c

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1594,10 +1594,6 @@ struct ref_iterator *refs_ref_iterator_begin(
15941594
if (trim)
15951595
iter = prefix_ref_iterator_begin(iter, "", trim);
15961596

1597-
/* Sanity check for subclasses: */
1598-
if (!iter->ordered)
1599-
BUG("reference iterator is not ordered");
1600-
16011597
return iter;
16021598
}
16031599

@@ -2516,18 +2512,33 @@ int refs_verify_refname_available(struct ref_store *refs,
25162512
return ret;
25172513
}
25182514

2519-
int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data)
2515+
struct do_for_each_reflog_help {
2516+
each_reflog_fn *fn;
2517+
void *cb_data;
2518+
};
2519+
2520+
static int do_for_each_reflog_helper(struct repository *r UNUSED,
2521+
const char *refname,
2522+
const struct object_id *oid UNUSED,
2523+
int flags,
2524+
void *cb_data)
2525+
{
2526+
struct do_for_each_reflog_help *hp = cb_data;
2527+
return hp->fn(refname, hp->cb_data);
2528+
}
2529+
2530+
int refs_for_each_reflog(struct ref_store *refs, each_reflog_fn fn, void *cb_data)
25202531
{
25212532
struct ref_iterator *iter;
2522-
struct do_for_each_ref_help hp = { fn, cb_data };
2533+
struct do_for_each_reflog_help hp = { fn, cb_data };
25232534

25242535
iter = refs->be->reflog_iterator_begin(refs);
25252536

25262537
return do_for_each_repo_ref_iterator(the_repository, iter,
2527-
do_for_each_ref_helper, &hp);
2538+
do_for_each_reflog_helper, &hp);
25282539
}
25292540

2530-
int for_each_reflog(each_ref_fn fn, void *cb_data)
2541+
int for_each_reflog(each_reflog_fn fn, void *cb_data)
25312542
{
25322543
return refs_for_each_reflog(get_main_ref_store(the_repository), fn, cb_data);
25332544
}

refs.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -534,12 +534,19 @@ int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_dat
534534
/* youngest entry first */
535535
int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void *cb_data);
536536

537+
/*
538+
* The signature for the callback function for the {refs_,}for_each_reflog()
539+
* functions below. The memory pointed to by the refname argument is only
540+
* guaranteed to be valid for the duration of a single callback invocation.
541+
*/
542+
typedef int each_reflog_fn(const char *refname, void *cb_data);
543+
537544
/*
538545
* Calls the specified function for each reflog file until it returns nonzero,
539546
* and returns the value. Reflog file order is unspecified.
540547
*/
541-
int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data);
542-
int for_each_reflog(each_ref_fn fn, void *cb_data);
548+
int refs_for_each_reflog(struct ref_store *refs, each_reflog_fn fn, void *cb_data);
549+
int for_each_reflog(each_reflog_fn fn, void *cb_data);
543550

544551
#define REFNAME_ALLOW_ONELEVEL 1
545552
#define REFNAME_REFSPEC_PATTERN 2

refs/debug.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,6 @@ static int debug_ref_iterator_advance(struct ref_iterator *ref_iterator)
181181
trace_printf_key(&trace_refs, "iterator_advance: %s (0)\n",
182182
diter->iter->refname);
183183

184-
diter->base.ordered = diter->iter->ordered;
185184
diter->base.refname = diter->iter->refname;
186185
diter->base.oid = diter->iter->oid;
187186
diter->base.flags = diter->iter->flags;
@@ -222,7 +221,7 @@ debug_ref_iterator_begin(struct ref_store *ref_store, const char *prefix,
222221
drefs->refs->be->iterator_begin(drefs->refs, prefix,
223222
exclude_patterns, flags);
224223
struct debug_ref_iterator *diter = xcalloc(1, sizeof(*diter));
225-
base_ref_iterator_init(&diter->base, &debug_ref_iterator_vtable, 1);
224+
base_ref_iterator_init(&diter->base, &debug_ref_iterator_vtable);
226225
diter->iter = res;
227226
trace_printf_key(&trace_refs, "ref_iterator_begin: \"%s\" (0x%x)\n",
228227
prefix, flags);

0 commit comments

Comments
 (0)