Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Documentation/git-rev-parse.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,10 @@ If a `pattern` is given, only refs matching the given shell glob are
shown. If the pattern does not contain a globbing character (`?`,
`*`, or `[`), it is turned into a prefix match by appending `/*`.

--toplevel-branches::
Show every ref directly under `refs/heads/` (that is, a branch
whose short name does not contain a `/`).

--glob=<pattern>::
Show all refs matching the shell glob pattern `pattern`. If
the pattern does not start with `refs/`, this is automatically
Expand Down
6 changes: 6 additions & 0 deletions Documentation/rev-list-options.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,12 @@ endif::git-log[]
branches to ones matching given shell glob. If _<pattern>_ lacks '?',
'{asterisk}', or '[', '/{asterisk}' at the end is implied.

`--toplevel-branches`::
Pretend as if every ref directly under `refs/heads/` is listed on
the command line as _<commit>_. This is a subset of --branches
which excludes branches in deeper hierarchies, i.e. excluding
branches whose short name contains a `/`.

`--tags[=<pattern>]`::
Pretend as if all the refs in `refs/tags` are listed
on the command line as _<commit>_. If _<pattern>_ is given, limit
Expand Down
2 changes: 1 addition & 1 deletion builtin/receive-pack.c
Original file line number Diff line number Diff line change
Expand Up @@ -2058,7 +2058,7 @@ static void execute_commands(struct command *commands,
opt.err_fd = err_fd;
opt.progress = err_fd && !quiet;
opt.env = tmp_objdir_env(tmp_objdir);
opt.exclude_hidden_refs_section = "receive";
opt.use_toplevel_branches_for_reachability = 1;

if (check_connected(iterate_receive_command_list, &data, &opt))
set_connectivity_errors(commands, si);
Expand Down
10 changes: 10 additions & 0 deletions builtin/rev-parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ static int is_rev_argument(const char *arg)
"--dense",
"--branches=",
"--branches",
"--toplevel-branches",
"--header",
"--ignore-missing",
"--max-age=",
Expand Down Expand Up @@ -960,6 +961,15 @@ int cmd_rev_parse(int argc,
free(term_bad);
continue;
}
if (!strcmp(arg, "--toplevel-branches")) {
if (ref_excludes.hidden_refs_configured)
return error(_("options '%s' and '%s' cannot be used together"),
"--exclude-hidden", "--toplevel-branches");
refs_for_each_toplevel_branch_ref(get_main_ref_store(the_repository),
show_reference, NULL);
clear_ref_exclusions(&ref_excludes);
continue;
}
if (opt_with_value(arg, "--branches", &arg)) {
if (ref_excludes.hidden_refs_configured)
return error(_("options '%s' and '%s' cannot be used together"),
Expand Down
16 changes: 12 additions & 4 deletions connected.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "sigchain.h"
#include "connected.h"
#include "transport.h"
#include "object-name.h"
#include "packfile.h"
#include "promisor-remote.h"

Expand Down Expand Up @@ -93,10 +94,17 @@ int check_connected(oid_iterate_fn fn, void *cb_data,
strvec_push(&rev_list.args, "--exclude-promisor-objects");
if (!opt->is_deepening_fetch) {
strvec_push(&rev_list.args, "--not");
if (opt->exclude_hidden_refs_section)
strvec_pushf(&rev_list.args, "--exclude-hidden=%s",
opt->exclude_hidden_refs_section);
strvec_push(&rev_list.args, "--all");
if (opt->use_toplevel_branches_for_reachability) {
struct object_id head_oid;
strvec_push(&rev_list.args, "--toplevel-branches");
if (!repo_get_oid(the_repository, "HEAD", &head_oid))
strvec_push(&rev_list.args, "HEAD");
} else {
if (opt->exclude_hidden_refs_section)
strvec_pushf(&rev_list.args, "--exclude-hidden=%s",
opt->exclude_hidden_refs_section);
strvec_push(&rev_list.args, "--all");
}
}
strvec_push(&rev_list.args, "--quiet");
strvec_push(&rev_list.args, "--alternate-refs");
Expand Down
12 changes: 11 additions & 1 deletion connected.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,19 @@ struct check_connected_options {
/*
* If not NULL, use `--exclude-hidden=$section` to exclude all refs
* hidden via the `$section.hideRefs` config from the set of
* already-reachable refs.
* already-reachable refs; irrelevant if
* use_toplevel_branches_for_reachability is set.
*/
const char *exclude_hidden_refs_section;

/*
* If set, use only toplevel branches (and HEAD) for the
* reachability check. This avoids the linear-in-refcount
* enumeration of every visible ref in repositories with many
* branches/tags, at the cost of walking a little further into
* already-reachable history.
*/
unsigned use_toplevel_branches_for_reachability : 1;
};

#define CHECK_CONNECTED_INIT { 0 }
Expand Down
35 changes: 35 additions & 0 deletions refs.c
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,41 @@ int refs_for_each_branch_ref(struct ref_store *refs, refs_for_each_cb cb, void *
return refs_for_each_ref_ext(refs, cb, cb_data, &opts);
}

struct toplevel_branch_filter {
refs_for_each_cb *fn;
void *cb_data;
};

static int filter_toplevel_branch(const struct reference *ref, void *data)
{
struct toplevel_branch_filter *filter = data;

/*
* ref->name has had the "refs/heads/" prefix trimmed, so a
* top-level branch like refs/heads/main appears as "main",
* while a sub-directory branch like refs/heads/dscho/wip
* appears as "dscho/wip".
*/
if (strchr(ref->name, '/'))
return 0;
return filter->fn(ref, filter->cb_data);
}

int refs_for_each_toplevel_branch_ref(struct ref_store *refs,
refs_for_each_cb cb, void *cb_data)
{
struct toplevel_branch_filter filter = {
.fn = cb,
.cb_data = cb_data,
};
struct refs_for_each_ref_options opts = {
.prefix = "refs/heads/",
.trim_prefix = strlen("refs/heads/"),
};
return refs_for_each_ref_ext(refs, filter_toplevel_branch, &filter,
&opts);
}

int refs_for_each_remote_ref(struct ref_store *refs, refs_for_each_cb cb, void *cb_data)
{
struct refs_for_each_ref_options opts = {
Expand Down
2 changes: 2 additions & 0 deletions refs.h
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,8 @@ int refs_for_each_tag_ref(struct ref_store *refs,
refs_for_each_cb fn, void *cb_data);
int refs_for_each_branch_ref(struct ref_store *refs,
refs_for_each_cb fn, void *cb_data);
int refs_for_each_toplevel_branch_ref(struct ref_store *refs,
refs_for_each_cb fn, void *cb_data);
int refs_for_each_remote_ref(struct ref_store *refs,
refs_for_each_cb fn, void *cb_data);
int refs_for_each_replace_ref(struct ref_store *refs,
Expand Down
7 changes: 7 additions & 0 deletions revision.c
Original file line number Diff line number Diff line change
Expand Up @@ -2328,6 +2328,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg

/* pseudo revision arguments */
if (!strcmp(arg, "--all") || !strcmp(arg, "--branches") ||
!strcmp(arg, "--toplevel-branches") ||
!strcmp(arg, "--tags") || !strcmp(arg, "--remotes") ||
!strcmp(arg, "--reflog") || !strcmp(arg, "--not") ||
!strcmp(arg, "--no-walk") || !strcmp(arg, "--do-walk") ||
Expand Down Expand Up @@ -2807,6 +2808,12 @@ static int handle_revision_pseudo_opt(struct rev_info *revs,
"--exclude-hidden", "--branches");
handle_refs(refs, revs, *flags, refs_for_each_branch_ref);
clear_ref_exclusions(&revs->ref_excludes);
} else if (!strcmp(arg, "--toplevel-branches")) {
if (revs->ref_excludes.hidden_refs_configured)
return error(_("options '%s' and '%s' cannot be used together"),
"--exclude-hidden", "--toplevel-branches");
handle_refs(refs, revs, *flags, refs_for_each_toplevel_branch_ref);
clear_ref_exclusions(&revs->ref_excludes);
} else if (!strcmp(arg, "--bisect")) {
read_bisect_terms(&term_bad, &term_good);
handle_refs(refs, revs, *flags, for_each_bad_bisect_ref);
Expand Down
26 changes: 26 additions & 0 deletions t/t6018-rev-list-glob.sh
Original file line number Diff line number Diff line change
Expand Up @@ -423,4 +423,30 @@ test_expect_failure 'shortlog --glob is not confused by option-like argument' '

'

test_expect_success 'rev-parse --toplevel-branches matches only top-level branches' '

git rev-parse main someref subspace-x | sort >expect &&
git rev-parse --toplevel-branches | sort >actual &&
test_cmp expect actual

'

test_expect_success 'rev-list --toplevel-branches matches only top-level branches' '

git rev-list --no-walk main someref subspace-x | sort >expect &&
git rev-list --no-walk --toplevel-branches | sort >actual &&
test_cmp expect actual

'

test_expect_success 'rev-list --not --toplevel-branches excludes top-level reachable' '

# "main" itself is a top-level branch, so excluding all
# top-level branches from the walk starting at main leaves
# nothing to enumerate.
git rev-list main --not --toplevel-branches >actual &&
test_must_be_empty actual

'

test_done
Loading