Merge branch 'ms/refs-list'

The "list" subcommand of "git refs" acts as a front-end for
"git for-each-ref".

* ms/refs-list:
  t: add test for git refs list subcommand
  t6300: refactor tests to be shareable
  builtin/refs: add list subcommand
  builtin/for-each-ref: factor out core logic into a helper
  builtin/for-each-ref: align usage string with the man page
  doc: factor out common option
main
Junio C Hamano 2025-08-22 13:13:20 -07:00
commit c72d5bbf49
11 changed files with 2316 additions and 2244 deletions

View File

@ -0,0 +1,88 @@
<pattern>...::
If one or more patterns are given, only refs are shown that
match against at least one pattern, either using fnmatch(3) or
literally, in the latter case matching completely or from the
beginning up to a slash.

--stdin::
If `--stdin` is supplied, then the list of patterns is read from
standard input instead of from the argument list.

--count=<count>::
By default the command shows all refs that match
`<pattern>`. This option makes it stop after showing
that many refs.

--sort=<key>::
A field name to sort on. Prefix `-` to sort in
descending order of the value. When unspecified,
`refname` is used. You may use the --sort=<key> option
multiple times, in which case the last key becomes the primary
key.

--format=<format>::
A string that interpolates `%(fieldname)` from a ref being shown and
the object it points at. In addition, the string literal `%%`
renders as `%` and `%xx` - where `xx` are hex digits - renders as
the character with hex code `xx`. For example, `%00` interpolates to
`\0` (NUL), `%09` to `\t` (TAB), and `%0a` to `\n` (LF).
+
When unspecified, `<format>` defaults to `%(objectname) SPC %(objecttype)
TAB %(refname)`.

--color[=<when>]::
Respect any colors specified in the `--format` option. The
`<when>` field must be one of `always`, `never`, or `auto` (if
`<when>` is absent, behave as if `always` was given).

--shell::
--perl::
--python::
--tcl::
If given, strings that substitute `%(fieldname)`
placeholders are quoted as string literals suitable for
the specified host language. This is meant to produce
a scriptlet that can directly be `eval`ed.

--points-at=<object>::
Only list refs which points at the given object.

--merged[=<object>]::
Only list refs whose tips are reachable from the
specified commit (HEAD if not specified).

--no-merged[=<object>]::
Only list refs whose tips are not reachable from the
specified commit (HEAD if not specified).

--contains[=<object>]::
Only list refs which contain the specified commit (HEAD if not
specified).

--no-contains[=<object>]::
Only list refs which don't contain the specified commit (HEAD
if not specified).

--ignore-case::
Sorting and filtering refs are case insensitive.

--omit-empty::
Do not print a newline after formatted refs where the format expands
to the empty string.

--exclude=<pattern>::
If one or more patterns are given, only refs which do not match
any excluded pattern(s) are shown. Matching is done using the
same rules as `<pattern>` above.

--include-root-refs::
List root refs (HEAD and pseudorefs) apart from regular refs.

--start-after=<marker>::
Allows paginating the output by skipping references up to and including the
specified marker. When paging, it should be noted that references may be
deleted, modified or added between invocations. Output will only yield those
references which follow the marker lexicographically. Output begins from the
first reference that would come after the marker alphabetically. Cannot be
used with `--sort=<key>` or `--stdin` options, or the _<pattern>_ argument(s)
to limit the refs.

View File

@ -28,94 +28,7 @@ host language allowing their direct evaluation in that language.

OPTIONS
-------
<pattern>...::
If one or more patterns are given, only refs are shown that
match against at least one pattern, either using fnmatch(3) or
literally, in the latter case matching completely or from the
beginning up to a slash.

--stdin::
If `--stdin` is supplied, then the list of patterns is read from
standard input instead of from the argument list.

--count=<count>::
By default the command shows all refs that match
`<pattern>`. This option makes it stop after showing
that many refs.

--sort=<key>::
A field name to sort on. Prefix `-` to sort in
descending order of the value. When unspecified,
`refname` is used. You may use the --sort=<key> option
multiple times, in which case the last key becomes the primary
key.

--format=<format>::
A string that interpolates `%(fieldname)` from a ref being shown and
the object it points at. In addition, the string literal `%%`
renders as `%` and `%xx` - where `xx` are hex digits - renders as
the character with hex code `xx`. For example, `%00` interpolates to
`\0` (NUL), `%09` to `\t` (TAB), and `%0a` to `\n` (LF).
+
When unspecified, `<format>` defaults to `%(objectname) SPC %(objecttype)
TAB %(refname)`.

--color[=<when>]::
Respect any colors specified in the `--format` option. The
`<when>` field must be one of `always`, `never`, or `auto` (if
`<when>` is absent, behave as if `always` was given).

--shell::
--perl::
--python::
--tcl::
If given, strings that substitute `%(fieldname)`
placeholders are quoted as string literals suitable for
the specified host language. This is meant to produce
a scriptlet that can directly be `eval`ed.

--points-at=<object>::
Only list refs which points at the given object.

--merged[=<object>]::
Only list refs whose tips are reachable from the
specified commit (HEAD if not specified).

--no-merged[=<object>]::
Only list refs whose tips are not reachable from the
specified commit (HEAD if not specified).

--contains[=<object>]::
Only list refs which contain the specified commit (HEAD if not
specified).

--no-contains[=<object>]::
Only list refs which don't contain the specified commit (HEAD
if not specified).

--ignore-case::
Sorting and filtering refs are case insensitive.

--omit-empty::
Do not print a newline after formatted refs where the format expands
to the empty string.

--exclude=<pattern>::
If one or more patterns are given, only refs which do not match
any excluded pattern(s) are shown. Matching is done using the
same rules as `<pattern>` above.

--include-root-refs::
List root refs (HEAD and pseudorefs) apart from regular refs.

--start-after=<marker>::
Allows paginating the output by skipping references up to and including the
specified marker. When paging, it should be noted that references may be
deleted, modified or added between invocations. Output will only yield those
references which follow the marker lexicographically. Output begins from the
first reference that would come after the marker alphabetically. Cannot be
used with `--sort=<key>` or `--stdin` options, or the _<pattern>_ argument(s)
to limit the refs.
include::for-each-ref-options.adoc[]

FIELD NAMES
-----------

View File

@ -11,6 +11,13 @@ SYNOPSIS
[synopsis]
git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]
git refs verify [--strict] [--verbose]
git refs list [--count=<count>] [--shell|--perl|--python|--tcl]
[(--sort=<key>)...] [--format=<format>]
[--include-root-refs] [--points-at=<object>]
[--merged[=<object>]] [--no-merged[=<object>]]
[--contains[=<object>]] [--no-contains[=<object>]]
[(--exclude=<pattern>)...] [--start-after=<marker>]
[ --stdin | <pattern>... ]

DESCRIPTION
-----------
@ -26,6 +33,11 @@ migrate::
verify::
Verify reference database consistency.

list::
List references in the repository with support for filtering,
formatting, and sorting. This subcommand is an alias for
linkgit:git-for-each-ref[1] and offers identical functionality.

OPTIONS
-------

@ -57,6 +69,10 @@ The following options are specific to 'git refs verify':
--verbose::
When verifying the reference database consistency, be chatty.

The following options are specific to 'git refs list':

include::for-each-ref-options.adoc[]

KNOWN LIMITATIONS
-----------------


View File

@ -2,6 +2,7 @@
#include "commit.h"
#include "config.h"
#include "environment.h"
#include "for-each-ref.h"
#include "gettext.h"
#include "object.h"
#include "parse-options.h"
@ -9,19 +10,7 @@
#include "strbuf.h"
#include "strvec.h"

static char const * const for_each_ref_usage[] = {
N_("git for-each-ref [<options>] [<pattern>]"),
N_("git for-each-ref [--points-at <object>]"),
N_("git for-each-ref [--merged [<commit>]] [--no-merged [<commit>]]"),
N_("git for-each-ref [--contains [<commit>]] [--no-contains [<commit>]]"),
N_("git for-each-ref [--start-after <marker>]"),
NULL
};

int cmd_for_each_ref(int argc,
const char **argv,
const char *prefix,
struct repository *repo)
int for_each_ref_core(int argc, const char **argv, const char *prefix, struct repository *repo, const char *const *usage)
{
struct ref_sorting *sorting;
struct string_list sorting_options = STRING_LIST_INIT_DUP;
@ -70,17 +59,17 @@ int cmd_for_each_ref(int argc,
/* Set default (refname) sorting */
string_list_append(&sorting_options, "refname");

parse_options(argc, argv, prefix, opts, for_each_ref_usage, 0);
parse_options(argc, argv, prefix, opts, usage, 0);
if (format.array_opts.max_count < 0) {
error("invalid --count argument: `%d'", format.array_opts.max_count);
usage_with_options(for_each_ref_usage, opts);
usage_with_options(usage, opts);
}
if (HAS_MULTI_BITS(format.quote_style)) {
error("more than one quoting style?");
usage_with_options(for_each_ref_usage, opts);
usage_with_options(usage, opts);
}
if (verify_ref_format(&format))
usage_with_options(for_each_ref_usage, opts);
usage_with_options(usage, opts);

if (filter.start_after && sorting_options.nr > 1)
die(_("cannot use --start-after with custom sort options"));
@ -120,3 +109,16 @@ int cmd_for_each_ref(int argc,
strvec_clear(&vec);
return 0;
}

int cmd_for_each_ref(int argc,
const char **argv,
const char *prefix,
struct repository *repo)
{
static char const * const for_each_ref_usage[] = {
N_("git for-each-ref " COMMON_USAGE_FOR_EACH_REF),
NULL
};

return for_each_ref_core(argc, argv, prefix, repo, for_each_ref_usage);
}

View File

@ -6,6 +6,7 @@
#include "refs.h"
#include "strbuf.h"
#include "worktree.h"
#include "for-each-ref.h"

#define REFS_MIGRATE_USAGE \
N_("git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]")
@ -101,6 +102,17 @@ static int cmd_refs_verify(int argc, const char **argv, const char *prefix,
return ret;
}

static int cmd_refs_list(int argc, const char **argv, const char *prefix,
struct repository *repo)
{
static char const * const refs_list_usage[] = {
N_("git refs list " COMMON_USAGE_FOR_EACH_REF),
NULL
};

return for_each_ref_core(argc, argv, prefix, repo, refs_list_usage);
}

int cmd_refs(int argc,
const char **argv,
const char *prefix,
@ -109,12 +121,14 @@ int cmd_refs(int argc,
const char * const refs_usage[] = {
REFS_MIGRATE_USAGE,
REFS_VERIFY_USAGE,
"git refs list " COMMON_USAGE_FOR_EACH_REF,
NULL,
};
parse_opt_subcommand_fn *fn = NULL;
struct option opts[] = {
OPT_SUBCOMMAND("migrate", &fn, cmd_refs_migrate),
OPT_SUBCOMMAND("verify", &fn, cmd_refs_verify),
OPT_SUBCOMMAND("list", &fn, cmd_refs_list),
OPT_END(),
};


26
for-each-ref.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef FOR_EACH_REF_H
#define FOR_EACH_REF_H

struct repository;

/*
* Shared usage string for options common to git-for-each-ref(1)
* and git-refs-list(1). The command-specific part (e.g., "git refs list ")
* must be prepended by the caller.
*/
#define COMMON_USAGE_FOR_EACH_REF \
"[--count=<count>] [--shell|--perl|--python|--tcl]\n" \
" [(--sort=<key>)...] [--format=<format>]\n" \
" [--include-root-refs] [--points-at=<object>]\n" \
" [--merged[=<object>]] [--no-merged[=<object>]]\n" \
" [--contains[=<object>]] [--no-contains[=<object>]]\n" \
" [(--exclude=<pattern>)...] [--start-after=<marker>]\n" \
" [ --stdin | <pattern>... ]"

/*
* The core logic for for-each-ref and its clones.
*/
int for_each_ref_core(int argc, const char **argv, const char *prefix,
struct repository *repo, const char *const *usage);

#endif /* FOR_EACH_REF_H */

2141
t/for-each-ref-tests.sh Normal file

File diff suppressed because it is too large Load Diff

View File

@ -209,6 +209,7 @@ integration_tests = [
't1450-fsck.sh',
't1451-fsck-buffer.sh',
't1460-refs-migrate.sh',
't1461-refs-list.sh',
't1500-rev-parse.sh',
't1501-work-tree.sh',
't1502-rev-parse-parseopt.sh',

View File

@ -17,7 +17,6 @@ fast-export
fast-import
fetch-pack
fmt-merge-msg
for-each-ref
format-patch
fsck-objects
fsmonitor--daemon

8
t/t1461-refs-list.sh Executable file
View File

@ -0,0 +1,8 @@
#!/bin/sh

test_description='git refs list tests'

. ./test-lib.sh

git_for_each_ref='git refs list'
. "$TEST_DIRECTORY"/for-each-ref-tests.sh

File diff suppressed because it is too large Load Diff