Skip to content

Commit d699d15

Browse files
pks-tgitster
authored andcommitted
builtin/reflog: introduce subcommand to list reflogs
While the git-reflog(1) command has subcommands to show reflog entries or check for reflog existence, it does not have any subcommands that would allow the user to enumerate all existing reflogs. This makes it quite hard to discover which reflogs a repository has. While this can be worked around with the "files" backend by enumerating files in the ".git/logs" directory, users of the "reftable" backend don't enjoy such a luxury. Introduce a new subcommand `git reflog list` that lists all reflogs the repository knows of to fill this gap. Signed-off-by: Patrick Steinhardt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 59c50a9 commit d699d15

File tree

3 files changed

+145
-0
lines changed

3 files changed

+145
-0
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/reflog.c

Lines changed: 34 additions & 0 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,
@@ -238,6 +248,29 @@ static int cmd_reflog_show(int argc, const char **argv, const char *prefix)
238248
return cmd_log_reflog(argc, argv, prefix);
239249
}
240250

251+
static int show_reflog(const char *refname, void *cb_data UNUSED)
252+
{
253+
printf("%s\n", refname);
254+
return 0;
255+
}
256+
257+
static int cmd_reflog_list(int argc, const char **argv, const char *prefix)
258+
{
259+
struct option options[] = {
260+
OPT_END()
261+
};
262+
struct ref_store *ref_store;
263+
264+
argc = parse_options(argc, argv, prefix, options, reflog_list_usage, 0);
265+
if (argc)
266+
return error(_("%s does not accept arguments: '%s'"),
267+
"list", argv[0]);
268+
269+
ref_store = get_main_ref_store(the_repository);
270+
271+
return refs_for_each_reflog(ref_store, show_reflog, NULL);
272+
}
273+
241274
static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
242275
{
243276
struct cmd_reflog_expire_cb cmd = { 0 };
@@ -417,6 +450,7 @@ int cmd_reflog(int argc, const char **argv, const char *prefix)
417450
parse_opt_subcommand_fn *fn = NULL;
418451
struct option options[] = {
419452
OPT_SUBCOMMAND("show", &fn, cmd_reflog_show),
453+
OPT_SUBCOMMAND("list", &fn, cmd_reflog_list),
420454
OPT_SUBCOMMAND("expire", &fn, cmd_reflog_expire),
421455
OPT_SUBCOMMAND("delete", &fn, cmd_reflog_delete),
422456
OPT_SUBCOMMAND("exists", &fn, cmd_reflog_exists),

t/t1410-reflog.sh

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,4 +436,112 @@ test_expect_success 'empty reflog' '
436436
test_must_be_empty err
437437
'
438438

439+
test_expect_success 'list reflogs' '
440+
test_when_finished "rm -rf repo" &&
441+
git init repo &&
442+
(
443+
cd repo &&
444+
git reflog list >actual &&
445+
test_must_be_empty actual &&
446+
447+
test_commit A &&
448+
cat >expect <<-EOF &&
449+
HEAD
450+
refs/heads/main
451+
EOF
452+
git reflog list >actual &&
453+
test_cmp expect actual &&
454+
455+
git branch b &&
456+
cat >expect <<-EOF &&
457+
HEAD
458+
refs/heads/b
459+
refs/heads/main
460+
EOF
461+
git reflog list >actual &&
462+
test_cmp expect actual
463+
)
464+
'
465+
466+
test_expect_success 'list reflogs with worktree' '
467+
test_when_finished "rm -rf repo" &&
468+
git init repo &&
469+
(
470+
cd repo &&
471+
472+
test_commit A &&
473+
git worktree add wt &&
474+
git -c core.logAllRefUpdates=always \
475+
update-ref refs/worktree/main HEAD &&
476+
git -c core.logAllRefUpdates=always \
477+
update-ref refs/worktree/per-worktree HEAD &&
478+
git -c core.logAllRefUpdates=always -C wt \
479+
update-ref refs/worktree/per-worktree HEAD &&
480+
git -c core.logAllRefUpdates=always -C wt \
481+
update-ref refs/worktree/worktree HEAD &&
482+
483+
cat >expect <<-EOF &&
484+
HEAD
485+
refs/heads/main
486+
refs/heads/wt
487+
refs/worktree/main
488+
refs/worktree/per-worktree
489+
EOF
490+
git reflog list >actual &&
491+
test_cmp expect actual &&
492+
493+
cat >expect <<-EOF &&
494+
HEAD
495+
refs/heads/main
496+
refs/heads/wt
497+
refs/worktree/per-worktree
498+
refs/worktree/worktree
499+
EOF
500+
git -C wt reflog list >actual &&
501+
test_cmp expect actual
502+
)
503+
'
504+
505+
test_expect_success 'reflog list returns error with additional args' '
506+
cat >expect <<-EOF &&
507+
error: list does not accept arguments: ${SQ}bogus${SQ}
508+
EOF
509+
test_must_fail git reflog list bogus 2>err &&
510+
test_cmp expect err
511+
'
512+
513+
test_expect_success 'reflog for symref with unborn target can be listed' '
514+
test_when_finished "rm -rf repo" &&
515+
git init repo &&
516+
(
517+
cd repo &&
518+
test_commit A &&
519+
git symbolic-ref HEAD refs/heads/unborn &&
520+
cat >expect <<-EOF &&
521+
HEAD
522+
refs/heads/main
523+
EOF
524+
git reflog list >actual &&
525+
test_cmp expect actual
526+
)
527+
'
528+
529+
test_expect_success 'reflog with invalid object ID can be listed' '
530+
test_when_finished "rm -rf repo" &&
531+
git init repo &&
532+
(
533+
cd repo &&
534+
test_commit A &&
535+
test-tool ref-store main update-ref msg refs/heads/missing \
536+
$(test_oid deadbeef) "$ZERO_OID" REF_SKIP_OID_VERIFICATION &&
537+
cat >expect <<-EOF &&
538+
HEAD
539+
refs/heads/main
540+
refs/heads/missing
541+
EOF
542+
git reflog list >actual &&
543+
test_cmp expect actual
544+
)
545+
'
546+
439547
test_done

0 commit comments

Comments
 (0)