1
0
Fork 0
mirror of https://github.com/git/git.git synced 2024-05-05 18:46:10 +02:00

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 <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Patrick Steinhardt 2024-02-21 13:37:47 +01:00 committed by Junio C Hamano
parent 59c50a96c5
commit d699d15c32
3 changed files with 145 additions and 0 deletions

View File

@ -10,6 +10,7 @@ SYNOPSIS
--------
[verse]
'git reflog' [show] [<log-options>] [<ref>]
'git reflog list'
'git reflog expire' [--expire=<time>] [--expire-unreachable=<time>]
[--rewrite] [--updateref] [--stale-fix]
[--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
--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
than `expire` time, or entries older than `expire-unreachable` time
and not reachable from the current tip, are removed from the reflog.

View File

@ -7,11 +7,15 @@
#include "wildmatch.h"
#include "worktree.h"
#include "reflog.h"
#include "refs.h"
#include "parse-options.h"
#define BUILTIN_REFLOG_SHOW_USAGE \
N_("git reflog [show] [<log-options>] [<ref>]")
#define BUILTIN_REFLOG_LIST_USAGE \
N_("git reflog list")
#define BUILTIN_REFLOG_EXPIRE_USAGE \
N_("git reflog expire [--expire=<time>] [--expire-unreachable=<time>]\n" \
" [--rewrite] [--updateref] [--stale-fix]\n" \
@ -29,6 +33,11 @@ static const char *const reflog_show_usage[] = {
NULL,
};
static const char *const reflog_list_usage[] = {
BUILTIN_REFLOG_LIST_USAGE,
NULL,
};
static const char *const reflog_expire_usage[] = {
BUILTIN_REFLOG_EXPIRE_USAGE,
NULL
@ -46,6 +55,7 @@ static const char *const reflog_exists_usage[] = {
static const char *const reflog_usage[] = {
BUILTIN_REFLOG_SHOW_USAGE,
BUILTIN_REFLOG_LIST_USAGE,
BUILTIN_REFLOG_EXPIRE_USAGE,
BUILTIN_REFLOG_DELETE_USAGE,
BUILTIN_REFLOG_EXISTS_USAGE,
@ -238,6 +248,29 @@ static int cmd_reflog_show(int argc, const char **argv, const char *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)
{
struct cmd_reflog_expire_cb cmd = { 0 };
@ -417,6 +450,7 @@ int cmd_reflog(int argc, const char **argv, const char *prefix)
parse_opt_subcommand_fn *fn = NULL;
struct option options[] = {
OPT_SUBCOMMAND("show", &fn, cmd_reflog_show),
OPT_SUBCOMMAND("list", &fn, cmd_reflog_list),
OPT_SUBCOMMAND("expire", &fn, cmd_reflog_expire),
OPT_SUBCOMMAND("delete", &fn, cmd_reflog_delete),
OPT_SUBCOMMAND("exists", &fn, cmd_reflog_exists),

View File

@ -436,4 +436,112 @@ test_expect_success 'empty reflog' '
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