Revert "Merge branch 'hn/branch-delete-merged' into next"
This reverts commit c61a87bdf5fd46f4921228da399264eced14f380, reversing changes made to 57c0e98bfb6395faec6e8ec9b4b6374364f8898b. cf. <80bd230e-7b8c-41d3-af1c-fa84b0c7b1c4@gmail.com>
Junio C Hamano committed
Jul 29, 2026 at 08:53 UTC
36b42465bff74d8651ed552422e082bc6b048cc6
6 files changed
+31
-771
Documentation/config/branch.adoc
-7
index c2acd89c99..5a85fde8de 100644
--- a/Documentation/config/branch.adoc
+++ b/Documentation/config/branch.adoc
@@ -103,10 +103,3 @@ for details).
`git branch --edit-description`. Branch description is
automatically added to the `format-patch` cover letter or
`request-pull` summary.
-
-`branch.<name>.deleteMerged`::
- If set to `false`, branch _<name>_ is exempt from
- `git branch --delete-merged`. Useful for a topic branch you
- intend to develop further after an initial round has been
- merged upstream. Defaults to true. Explicit deletion via
- `git branch -d` is unaffected.
Documentation/git-branch.adoc
+1
-46
index 51dda15114..c0afddc424 100644
--- a/Documentation/git-branch.adoc
+++ b/Documentation/git-branch.adoc
@@ -13,7 +13,6 @@ git branch [--color[=<when>] | --no-color] [--show-current]
[--column[=<options>] | --no-column] [--sort=<key>]
[--merged [<commit>]] [--no-merged [<commit>]]
[--contains [<commit>]] [--no-contains [<commit>]]
- [(--forked <branch>)...]
[--points-at <object>] [--format=<format>]
[(-r|--remotes) | (-a|--all)]
[--list] [<pattern>...]
@@ -25,7 +24,6 @@ git branch (-m|-M) [<old-branch>] <new-branch>
git branch (-c|-C) [<old-branch>] <new-branch>
git branch (-d|-D) [-r] <branch-name>...
git branch --edit-description [<branch-name>]
-git branch [--dry-run] (--delete-merged <branch>)... [<pattern>...]
DESCRIPTION
-----------
@@ -53,8 +51,7 @@ merged into the named commit (i.e. the branches whose tip commits are
reachable from the named commit) will be listed. With `--no-merged` only
branches not merged into the named commit will be listed. If the _<commit>_
argument is missing it defaults to `HEAD` (i.e. the tip of the current
-branch). With `--forked`, only branches whose configured upstream matches
-the given branch or pattern will be listed.
+branch).
The command's second form creates a new branch head named _<branch-name>_
which points to the current `HEAD`, or _<start-point>_ if given. As a
@@ -202,42 +199,6 @@ This option is only applicable in non-verbose mode.
Print the name of the current branch. In detached `HEAD` state,
nothing is printed.
-`--delete-merged <branch>`::
- Delete local branches whose configured upstream matches
- _<branch>_, but only when their tip is reachable from that
- upstream. In other words, the work on the branch has already
- landed on the upstream it tracks, so the local copy is no longer
- needed. The option can be repeated to widen the upstream match.
- Optional _<pattern>_ arguments limit which local branches are
- considered, e.g. `git branch --delete-merged 'origin/*'
- 'topic-*'`.
-+
-A branch is not deleted when:
-+
---
-* its configured upstream ref no longer exists,
-* it is checked out in any worktree,
-* pushing it by name to the remote configured by
- `branch.<name>.remote` would update its upstream, so it cannot be
- distinguished from a branch that just looks "fully merged" right
- after a pull, or
-* `branch.<name>.deleteMerged` is set to `false`.
---
-+
-A branch whose work has not yet been merged into its upstream is
-silently skipped. Delete it with `git branch -D` if you want to
-remove it anyway.
-+
-A branch that a surviving branch depends on through a chain of local
-upstreams is kept, so a branch is never deleted out from under stacked
-work.
-
-`--dry-run`::
- With `--delete-merged`, print which branches would be
- deleted and exit without touching any ref. Useful for
- sanity-checking a wide pattern like `'origin/*'` before
- committing to the deletion.
-
`-v`::
`-vv`::
`--verbose`::
@@ -350,14 +311,6 @@ superproject's "origin/main", but tracks the submodule's "origin/main".
Only list branches whose tips are not reachable from
_<commit>_ (`HEAD` if not specified). Implies `--list`.
-`--forked <branch>`::
- Only list branches whose configured upstream matches
- _<branch>_. The argument can be a ref (e.g. `origin/main`,
- `master`), a remote name like `origin` for the branch its
- `origin/HEAD` points at, or a shell-style glob (e.g.
- `'origin/*'`). The option can be repeated to widen the
- filter. Implies `--list`.
-
`--points-at <object>`::
Only list branches of _<object>_.
builtin/branch.c
+30
-245
index 985e36d68d..357209748d 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -21,7 +21,6 @@
#include "branch.h"
#include "path.h"
#include "string-list.h"
-#include "strmap.h"
#include "column.h"
#include "utf8.h"
#include "ref-filter.h"
@@ -31,7 +30,7 @@
#include "commit-reach.h"
static const char * const builtin_branch_usage[] = {
- N_("git branch [<options>] [-r | -a] [--merged] [--no-merged] [(--forked <branch>)...]"),
+ N_("git branch [<options>] [-r | -a] [--merged] [--no-merged]"),
N_("git branch [<options>] [-f] [--recurse-submodules] <branch-name> [<start-point>]"),
N_("git branch [<options>] [-l] [<pattern>...]"),
N_("git branch [<options>] [-r] (-d | -D) <branch-name>..."),
@@ -39,7 +38,6 @@ static const char * const builtin_branch_usage[] = {
N_("git branch [<options>] (-c | -C) [<old-branch>] <new-branch>"),
N_("git branch [<options>] [-r | -a] [--points-at]"),
N_("git branch [<options>] [-r | -a] [--format]"),
- N_("git branch [<options>] (--delete-merged <branch>)... [<pattern>...]"),
NULL
};
@@ -170,13 +168,10 @@ static int branch_merged(int kind, const char *name,
* upstream, if any, otherwise with HEAD", we should just
* return the result of the repo_in_merge_bases() above without
* any of the following code, but during the transition period,
- * a gentle reminder is in order. Callers that opt out of the
- * HEAD fallback by passing head_rev=NULL are not interested in
- * the reminder either: they have already established that the
- * branch has an upstream, so HEAD is irrelevant to the decision.
+ * a gentle reminder is in order.
*/
- if (head_rev && head_rev != reference_rev) {
- int expect = repo_in_merge_bases(the_repository, rev, head_rev);
+ if (head_rev != reference_rev) {
+ int expect = head_rev ? repo_in_merge_bases(the_repository, rev, head_rev) : 0;
if (expect < 0)
exit(128);
if (expect == merged)
@@ -194,32 +189,20 @@ static int branch_merged(int kind, const char *name,
return merged;
}
-enum delete_branch_flags {
- DELETE_BRANCH_FORCE = (1 << 0),
- DELETE_BRANCH_QUIET = (1 << 1),
- DELETE_BRANCH_SKIP_UNMERGED = (1 << 2),
- DELETE_BRANCH_NO_HEAD_FALLBACK = (1 << 3),
- DELETE_BRANCH_DRY_RUN = (1 << 4),
-};
-
static int check_branch_commit(const char *branchname, const char *refname,
const struct object_id *oid, struct commit *head_rev,
- int kinds, unsigned int flags)
+ int kinds, int force)
{
struct commit *rev = lookup_commit_reference(the_repository, oid);
- if (!(flags & DELETE_BRANCH_FORCE) && !rev) {
+ if (!force && !rev) {
error(_("couldn't look up commit object for '%s'"), refname);
return -1;
}
- if (!(flags & DELETE_BRANCH_FORCE) &&
- !branch_merged(kinds, branchname, rev, head_rev)) {
- if (!(flags & DELETE_BRANCH_SKIP_UNMERGED)) {
- error(_("the branch '%s' is not fully merged"),
- branchname);
- advise_if_enabled(ADVICE_FORCE_DELETE_BRANCH,
- _("If you are sure you want to delete it, "
- "run 'git branch -D %s'"), branchname);
- }
+ if (!force && !branch_merged(kinds, branchname, rev, head_rev)) {
+ error(_("the branch '%s' is not fully merged"), branchname);
+ advise_if_enabled(ADVICE_FORCE_DELETE_BRANCH,
+ _("If you are sure you want to delete it, "
+ "run 'git branch -D %s'"), branchname);
return -1;
}
return 0;
@@ -234,8 +217,8 @@ static void delete_branch_config(const char *branchname)
strbuf_release(&buf);
}
-static int delete_branches(int argc, const char **argv, int kinds,
- unsigned int flags)
+static int delete_branches(int argc, const char **argv, int force, int kinds,
+ int quiet)
{
struct commit *head_rev = NULL;
struct object_id oid;
@@ -258,7 +241,7 @@ static int delete_branches(int argc, const char **argv, int kinds,
remote_branch = 1;
allowed_interpret = INTERPRET_BRANCH_REMOTE;
- flags |= DELETE_BRANCH_FORCE;
+ force = 1;
break;
case FILTER_REFS_BRANCHES:
fmt = "refs/heads/%s";
@@ -269,13 +252,12 @@ static int delete_branches(int argc, const char **argv, int kinds,
}
branch_name_pos = strcspn(fmt, "%");
- if (!(flags & DELETE_BRANCH_FORCE) &&
- !(flags & DELETE_BRANCH_NO_HEAD_FALLBACK))
+ if (!force)
head_rev = lookup_commit_reference(the_repository, &head_oid);
for (i = 0; i < argc; i++, strbuf_reset(&bname)) {
char *target = NULL;
- int ref_flags = 0;
+ int flags = 0;
copy_branchname(the_repository, &bname,
argv[i], allowed_interpret);
@@ -305,7 +287,7 @@ static int delete_branches(int argc, const char **argv, int kinds,
RESOLVE_REF_READING
| RESOLVE_REF_NO_RECURSE
| RESOLVE_REF_ALLOW_BAD_NAME,
- &oid, &ref_flags);
+ &oid, &flags);
if (!target) {
if (remote_branch) {
error(_("remote-tracking branch '%s' not found"), bname.buf);
@@ -317,7 +299,7 @@ static int delete_branches(int argc, const char **argv, int kinds,
| RESOLVE_REF_NO_RECURSE
| RESOLVE_REF_ALLOW_BAD_NAME,
&oid,
- &ref_flags);
+ &flags);
FREE_AND_NULL(virtual_name);
if (virtual_target)
@@ -332,39 +314,31 @@ static int delete_branches(int argc, const char **argv, int kinds,
continue;
}
- if (!(ref_flags & (REF_ISSYMREF|REF_ISBROKEN)) &&
+ if (!(flags & (REF_ISSYMREF|REF_ISBROKEN)) &&
check_branch_commit(bname.buf, name, &oid, head_rev, kinds,
- flags)) {
- if (!(flags & DELETE_BRANCH_SKIP_UNMERGED))
- ret = 1;
+ force)) {
+ ret = 1;
goto next;
}
item = string_list_append(&refs_to_delete, name);
- item->util = xstrdup((ref_flags & REF_ISBROKEN) ? "broken"
- : (ref_flags & REF_ISSYMREF) ? target
+ item->util = xstrdup((flags & REF_ISBROKEN) ? "broken"
+ : (flags & REF_ISSYMREF) ? target
: repo_find_unique_abbrev(the_repository, &oid, DEFAULT_ABBREV));
next:
free(target);
}
- if (!(flags & DELETE_BRANCH_DRY_RUN) &&
- refs_delete_refs(get_main_ref_store(the_repository), NULL, &refs_to_delete, REF_NO_DEREF))
+ if (refs_delete_refs(get_main_ref_store(the_repository), NULL, &refs_to_delete, REF_NO_DEREF))
ret = 1;
for_each_string_list_item(item, &refs_to_delete) {
char *describe_ref = item->util;
char *name = item->string;
- if (flags & DELETE_BRANCH_DRY_RUN) {
- if (!(flags & DELETE_BRANCH_QUIET))
- printf(remote_branch
- ? _("Would delete remote-tracking branch %s (was %s).\n")
- : _("Would delete branch %s (was %s).\n"),
- name + branch_name_pos, describe_ref);
- } else if (!refs_ref_exists(get_main_ref_store(the_repository), name)) {
+ if (!refs_ref_exists(get_main_ref_store(the_repository), name)) {
char *refname = name + branch_name_pos;
- if (!(flags & DELETE_BRANCH_QUIET))
+ if (!quiet)
printf(remote_branch
? _("Deleted remote-tracking branch %s (was %s).\n")
: _("Deleted branch %s (was %s).\n"),
@@ -707,172 +681,6 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
free_worktrees(worktrees);
}
-static int parse_opt_forked(const struct option *opt, const char *arg, int unset)
-{
- struct ref_filter *filter = opt->value;
-
- BUG_ON_OPT_NEG(unset);
- if (ref_filter_forked_add(filter, arg) < 0)
- die(_("'%s' is not a valid branch or pattern"), arg);
- return 0;
-}
-
-struct stacked_branch_data {
- struct strset *deletable_branch_names;
- struct strset *protected_branch_names;
- struct strset *visited_branch_names;
-};
-
-static int collect_stacked_branch_bases(const struct reference *ref,
- void *cb_data)
-{
- struct stacked_branch_data *data = cb_data;
- const char *branch_name;
-
- if (!skip_prefix(ref->name, "refs/heads/", &branch_name))
- BUG("expected local branch ref, got '%s'", ref->name);
- if (strset_contains(data->deletable_branch_names, branch_name))
- return 0;
-
- while (strset_add(data->visited_branch_names, branch_name)) {
- struct branch *branch = branch_get(branch_name);
- const char *upstream_refname = branch_get_upstream(branch, NULL);
- const char *upstream_branch_name;
-
- if (!upstream_refname ||
- !skip_prefix(upstream_refname, "refs/heads/",
- &upstream_branch_name) ||
- !strset_contains(data->deletable_branch_names,
- upstream_branch_name))
- break;
-
- strset_add(data->protected_branch_names, upstream_branch_name);
- branch_name = upstream_branch_name;
- }
-
- return 0;
-}
-
-static void protect_stacked_branch_bases(struct ref_store *refs,
- struct strset *deletable_branch_names)
-{
- struct strset protected_branch_names = STRSET_INIT;
- struct strset visited_branch_names = STRSET_INIT;
- struct stacked_branch_data data = {
- .deletable_branch_names = deletable_branch_names,
- .protected_branch_names = &protected_branch_names,
- .visited_branch_names = &visited_branch_names,
- };
- struct refs_for_each_ref_options opts = {
- .prefix = "refs/heads/",
- };
- struct hashmap_iter iter;
- struct strmap_entry *entry;
-
- refs_for_each_ref_ext(refs, collect_stacked_branch_bases, &data, &opts);
-
- strset_for_each_entry(&protected_branch_names, &iter, entry)
- strset_remove(deletable_branch_names, entry->key);
-
- strset_clear(&visited_branch_names);
- strset_clear(&protected_branch_names);
-}
-
-static int branch_pushes_to_upstream(struct branch *branch,
- const char *upstream)
-{
- struct remote *remote = remote_get(remote_for_branch(branch, NULL));
- char *tracking = NULL;
- int ret = 0;
-
- if (remote)
- tracking = apply_refspecs(&remote->fetch, branch->refname);
- if (tracking && !strcmp(tracking, upstream))
- ret = 1;
-
- free(tracking);
- return ret;
-}
-
-static int delete_merged_branches(const struct strvec *upstreams,
- const char **argv, unsigned int flags)
-{
- struct ref_store *refs = get_main_ref_store(the_repository);
- struct ref_filter filter = REF_FILTER_INIT;
- struct ref_array candidates = { 0 };
- struct strset deletable_branch_names = STRSET_INIT;
- struct strvec branches_to_delete = STRVEC_INIT;
- struct strbuf key = STRBUF_INIT;
- struct hashmap_iter iter;
- struct strmap_entry *entry;
- int ret = 0;
-
- for (size_t i = 0; i < upstreams->nr; i++)
- if (ref_filter_forked_add(&filter, upstreams->v[i]) < 0)
- die(_("'%s' is not a valid branch or pattern"),
- upstreams->v[i]);
-
- filter.kind = FILTER_REFS_BRANCHES;
- filter.name_patterns = argv;
- filter_refs(&candidates, &filter, filter.kind);
-
- for (int i = 0; i < candidates.nr; i++) {
- const char *branch_refname = candidates.items[i]->refname;
- const char *branch_name;
- struct branch *branch;
- const char *upstream_refname;
- int opt_out;
-
- if (!skip_prefix(branch_refname, "refs/heads/", &branch_name))
- BUG("filter returned non-branch ref '%s'", branch_refname);
- if (branch_checked_out(branch_refname))
- continue;
-
- branch = branch_get(branch_name);
- upstream_refname = branch_get_upstream(branch, NULL);
- if (!upstream_refname || !refs_ref_exists(refs, upstream_refname))
- continue;
- if (branch_pushes_to_upstream(branch, upstream_refname))
- continue;
- if (check_branch_commit(branch_name, branch_name,
- &candidates.items[i]->objectname, NULL,
- FILTER_REFS_BRANCHES, DELETE_BRANCH_SKIP_UNMERGED))
- continue;
-
- strbuf_reset(&key);
- strbuf_addf(&key, "branch.%s.deletemerged", branch_name);
- if (!repo_config_get_bool(the_repository, key.buf, &opt_out) &&
- !opt_out) {
- if (!(flags & DELETE_BRANCH_QUIET))
- fprintf(stderr,
- _("Skipping '%s' (branch.%s.deleteMerged is false)\n"),
- branch_name, branch_name);
- continue;
- }
-
- strset_add(&deletable_branch_names, branch_name);
- }
-
- protect_stacked_branch_bases(refs, &deletable_branch_names);
-
- strset_for_each_entry(&deletable_branch_names, &iter, entry)
- strvec_push(&branches_to_delete, entry->key);
-
- if (branches_to_delete.nr)
- ret = delete_branches(branches_to_delete.nr, branches_to_delete.v,
- FILTER_REFS_BRANCHES,
- DELETE_BRANCH_SKIP_UNMERGED |
- DELETE_BRANCH_NO_HEAD_FALLBACK |
- flags);
-
- strbuf_release(&key);
- strvec_clear(&branches_to_delete);
- strset_clear(&deletable_branch_names);
- ref_array_clear(&candidates);
- ref_filter_clear(&filter);
- return ret;
-}
-
static GIT_PATH_FUNC(edit_description, "EDIT_DESCRIPTION")
static int edit_branch_description(const char *branch_name)
@@ -937,8 +745,6 @@ int cmd_branch(int argc,
/* possible actions */
int delete = 0, rename = 0, copy = 0, list = 0,
unset_upstream = 0, show_current = 0, edit_description = 0;
- struct strvec delete_merged = STRVEC_INIT;
- int dry_run = 0;
const char *new_upstream = NULL;
int noncreate_actions = 0;
/* possible options */
@@ -992,17 +798,9 @@ int cmd_branch(int argc,
OPT_BOOL(0, "create-reflog", &reflog, N_("create the branch's reflog")),
OPT_BOOL(0, "edit-description", &edit_description,
N_("edit the description for the branch")),
- OPT_CALLBACK_F(0, "delete-merged", &delete_merged, N_("branch"),
- N_("delete merged branches whose upstream matches <branch> (repeatable)"),
- PARSE_OPT_NONEG, parse_opt_strvec),
- OPT_BOOL(0, "dry-run", &dry_run,
- N_("with --delete-merged, only print which branches would be deleted")),
OPT__FORCE(&force, N_("force creation, move/rename, deletion"), PARSE_OPT_NOCOMPLETE),
OPT_MERGED(&filter, N_("print only branches that are merged")),
OPT_NO_MERGED(&filter, N_("print only branches that are not merged")),
- OPT_CALLBACK_F(0, "forked", &filter, N_("branch"),
- N_("print only branches whose upstream matches <branch> (repeatable)"),
- PARSE_OPT_NONEG, parse_opt_forked),
OPT_COLUMN(0, "column", &colopts, N_("list branches in columns")),
OPT_REF_SORT(&sorting_options),
OPT_CALLBACK(0, "points-at", &filter.points_at, N_("object"),
@@ -1044,24 +842,19 @@ int cmd_branch(int argc,
0);
if (!delete && !rename && !copy && !edit_description && !new_upstream &&
- !show_current && !unset_upstream && !delete_merged.nr &&
- argc == 0)
+ !show_current && !unset_upstream && argc == 0)
list = 1;
if (filter.with_commit || filter.no_commit ||
- filter.reachable_from || filter.unreachable_from ||
- filter.points_at.nr || filter.forked.nr)
+ filter.reachable_from || filter.unreachable_from || filter.points_at.nr)
list = 1;
noncreate_actions = !!delete + !!rename + !!copy + !!new_upstream +
!!show_current + !!list + !!edit_description +
- !!unset_upstream + !!delete_merged.nr;
+ !!unset_upstream;
if (noncreate_actions > 1)
usage_with_options(builtin_branch_usage, options);
- if (dry_run && !delete_merged.nr)
- die(_("--dry-run requires --delete-merged"));
-
if (recurse_submodules_explicit) {
if (!submodule_propagate_branches)
die(_("branch with --recurse-submodules can only be used if submodule.propagateBranches is enabled"));
@@ -1096,14 +889,7 @@ int cmd_branch(int argc,
if (delete) {
if (!argc)
die(_("branch name required"));
- ret = delete_branches(argc, argv, filter.kind,
- (delete > 1 ? DELETE_BRANCH_FORCE : 0) |
- (quiet ? DELETE_BRANCH_QUIET : 0));
- goto out;
- } else if (delete_merged.nr) {
- ret = delete_merged_branches(&delete_merged, argv,
- (quiet ? DELETE_BRANCH_QUIET : 0) |
- (dry_run ? DELETE_BRANCH_DRY_RUN : 0));
+ ret = delete_branches(argc, argv, delete > 1, filter.kind, quiet);
goto out;
} else if (show_current) {
print_current_branch_name();
@@ -1276,7 +1062,6 @@ int cmd_branch(int argc,
ret = 0;
out:
- strvec_clear(&delete_merged);
string_list_clear(&sorting_options, 0);
return ret;
}
ref-filter.c
-70
index bdf54f6f59..29aca08ce7 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -2744,72 +2744,6 @@ static int filter_exclude_match(struct ref_filter *filter, const char *refname)
return match_pattern(filter->exclude.v, refname, filter->ignore_case);
}
-static const char *short_upstream_name(const char *full_ref)
-{
- const char *short_name = full_ref;
-
- if (!skip_prefix(short_name, "refs/heads/", &short_name))
- skip_prefix(short_name, "refs/remotes/", &short_name);
- return short_name;
-}
-
-/*
- * Match the configured upstream of a branch against the registered
- * --forked patterns. Exact patterns are compared against the full
- * upstream refname so they are unambiguous; glob patterns are matched
- * against the abbreviated upstream so that a glob such as origin/...
- * works as typed.
- */
-static int filter_forked_match(struct ref_filter *filter, const char *refname)
-{
- const char *short_name;
- struct branch *branch;
- const char *upstream;
-
- if (!skip_prefix(refname, "refs/heads/", &short_name))
- return 0;
- branch = branch_get(short_name);
- if (!branch)
- return 0;
- upstream = branch_get_upstream(branch, NULL);
- if (!upstream)
- return 0;
-
- for (size_t i = 0; i < filter->forked.nr; i++) {
- const char *pattern = filter->forked.v[i];
- if (has_glob_specials(pattern)) {
- if (!wildmatch(pattern, short_upstream_name(upstream),
- WM_PATHNAME))
- return 1;
- } else if (!strcmp(pattern, upstream)) {
- return 1;
- }
- }
- return 0;
-}
-
-int ref_filter_forked_add(struct ref_filter *filter, const char *arg)
-{
- struct object_id oid;
- char *full_ref = NULL;
-
- if (has_glob_specials(arg)) {
- strvec_push(&filter->forked, arg);
- return 0;
- }
-
- if (repo_dwim_ref(the_repository, arg, strlen(arg), &oid,
- &full_ref, 0) == 1 &&
- (starts_with(full_ref, "refs/heads/") ||
- starts_with(full_ref, "refs/remotes/"))) {
- strvec_push(&filter->forked, full_ref);
- free(full_ref);
- return 0;
- }
- free(full_ref);
- return -1;
-}
-
/*
* We need to seek to the reference right after a given marker but excluding any
* matching references. So we seek to the lexicographically next reference.
@@ -3045,9 +2979,6 @@ static struct ref_array_item *apply_ref_filter(const struct reference *ref,
if (filter->points_at.nr && !match_points_at(&filter->points_at, ref->oid, ref->name))
return NULL;
- if (filter->forked.nr && !filter_forked_match(filter, ref->name))
- return NULL;
-
/*
* A merge filter is applied on refs pointing to commits. Hence
* obtain the commit using the 'oid' available and discard all
@@ -3833,7 +3764,6 @@ void ref_filter_init(struct ref_filter *filter)
void ref_filter_clear(struct ref_filter *filter)
{
strvec_clear(&filter->exclude);
- strvec_clear(&filter->forked);
oid_array_clear(&filter->points_at);
commit_list_free(filter->with_commit);
commit_list_free(filter->no_commit);
ref-filter.h
-10
index 9361296e2a..120221b47f 100644
--- a/ref-filter.h
+++ b/ref-filter.h
@@ -67,7 +67,6 @@ struct ref_filter {
const char **name_patterns;
const char *start_after;
struct strvec exclude;
- struct strvec forked;
struct oid_array points_at;
struct commit_list *with_commit;
struct commit_list *no_commit;
@@ -111,7 +110,6 @@ struct ref_format {
#define REF_FILTER_INIT { \
.points_at = OID_ARRAY_INIT, \
.exclude = STRVEC_INIT, \
- .forked = STRVEC_INIT, \
}
#define REF_FORMAT_INIT { \
.use_color = GIT_COLOR_UNKNOWN, \
@@ -174,14 +172,6 @@ void ref_sorting_release(struct ref_sorting *);
struct ref_sorting *ref_sorting_options(struct string_list *);
/* Function to parse --merged and --no-merged options */
int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset);
-/*
- * Register a --forked <branch> pattern on the filter. The argument is
- * either a ref, which is resolved to its full refname, or a shell-style
- * glob. Branches are kept only when their configured upstream matches
- * one of the registered patterns. Returns -1 if the argument is not a
- * valid ref or pattern.
- */
-int ref_filter_forked_add(struct ref_filter *filter, const char *arg);
/* Get the current HEAD's description */
char *get_head_description(void);
/* Set up translated strings in the output. */
t/t3200-branch.sh
-393
index 443512677a..b5a56ff25d 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -1757,397 +1757,4 @@ test_expect_success 'errors if given a bad branch name' '
test_cmp expect actual
'
-test_expect_success '--forked: setup' '
- test_create_repo forked-upstream &&
- (
- cd forked-upstream &&
- test_commit base &&
- git branch one base &&
- git branch two base
- ) &&
-
- test_create_repo forked-other &&
- (
- cd forked-other &&
- test_commit other-base &&
- git branch foreign other-base
- ) &&
-
- git clone forked-upstream forked &&
- (
- cd forked &&
- git remote add -f other ../forked-other &&
- git branch local-base &&
- git branch --track local-one origin/one &&
- git branch --track local-two origin/two &&
- git branch --track local-foreign other/foreign &&
- git branch --track local-onbase local-base &&
-
- git checkout local-one &&
- test_commit --no-tag local-one-work local-one.t &&
- git checkout local-foreign &&
- test_commit --no-tag local-foreign-work local-foreign.t
- )
-'
-
-test_expect_success '--forked <upstream-tracking-branch> filters by upstream' '
- git -C forked branch --forked origin/one --format="%(refname:short)" >actual &&
- echo local-one >expect &&
- test_cmp expect actual
-'
-
-test_expect_success '--forked <glob> filters by wildmatch' '
- git -C forked branch --forked "origin/*" --format="%(refname:short)" >actual &&
- cat >expect <<-\EOF &&
- local-one
- local-two
- main
- EOF
- test_cmp expect actual
-'
-
-test_expect_success '--forked <local-branch> matches branches with local upstream' '
- git -C forked branch --forked local-base --format="%(refname:short)" >actual &&
- echo local-onbase >expect &&
- test_cmp expect actual
-'
-
-test_expect_success '--forked can be repeated to widen the filter' '
- git -C forked branch --forked origin/one --forked other/foreign --format="%(refname:short)" >actual &&
- cat >expect <<-\EOF &&
- local-foreign
- local-one
- EOF
- test_cmp expect actual
-'
-
-test_expect_success '--forked combines literal and glob arguments' '
- git -C forked branch --forked local-base --forked "other/*" --format="%(refname:short)" >actual &&
- cat >expect <<-\EOF &&
- local-foreign
- local-onbase
- EOF
- test_cmp expect actual
-'
-
-test_expect_success '--forked "*/*" covers every remote-tracking upstream' '
- git -C forked branch --forked "*/*" --format="%(refname:short)" >actual &&
- cat >expect <<-\EOF &&
- local-foreign
- local-one
- local-two
- main
- EOF
- test_cmp expect actual
-'
-
-test_expect_success '--forked composes with --no-merged' '
- git -C forked branch --forked "origin/*" --no-merged origin/one \
- --format="%(refname:short)" >actual &&
- echo local-one >expect &&
- test_cmp expect actual
-'
-
-test_expect_success '--forked <remote> uses the branch <remote>/HEAD points at' '
- git -C forked branch --forked origin --format="%(refname:short)" >actual &&
- echo main >expect &&
- test_cmp expect actual
-'
-
-test_expect_success '--forked narrows a <pattern> argument' '
- git -C forked branch --forked "origin/*" "local-*" \
- --format="%(refname:short)" >actual &&
- cat >expect <<-\EOF &&
- local-one
- local-two
- EOF
- test_cmp expect actual
-'
-
-test_expect_success '--forked rejects unknown branch/pattern' '
- test_must_fail git -C forked branch --forked nope 2>err &&
- test_grep "not a valid branch or pattern" err
-'
-
-test_expect_success '--forked requires a value' '
- test_must_fail git -C forked branch --forked 2>err &&
- test_grep "requires a value" err
-'
-
-test_expect_success '--delete-merged: setup' '
- git init -b main upstream &&
- (
- cd upstream &&
- test_commit base &&
- git checkout -b next &&
- test_commit next-work &&
- git checkout main
- ) &&
- git init -b main other &&
- test_commit -C other other-base &&
- git init -b main fork
-'
-
-setup_repo_for_delete_merged () {
- rm -rf repo &&
- git clone upstream repo &&
- (
- cd repo &&
- git remote add fork ../fork &&
- git remote add other ../other &&
- git config push.default current &&
- git fetch other
- )
-}
-
-create_merged_branch () {
- (
- cd repo &&
- git checkout -b "$1" origin/next --track &&
- git commit --allow-empty -m "$1 work" &&
- git push origin "$1:next"
- )
-}
-
-check_branches () {
- git for-each-ref --format="%(refname:short)" refs/heads/ >actual &&
- cat >expect &&
- test_cmp expect actual
-}
-
-test_expect_success '--delete-merged keeps cloned main without a default push remote' '
- setup_repo_for_delete_merged &&
- (
- cd repo &&
- git checkout --detach &&
-
- git branch --delete-merged */* &&
-
- check_branches <<-\EOF
- main
- EOF
- )
-'
-
-test_expect_success '--delete-merged deletes only selected merged branches' '
- setup_repo_for_delete_merged &&
- create_merged_branch also-merged &&
- create_merged_branch merged &&
- (
- cd repo &&
- git checkout -b unmerged origin/next --track &&
- git commit --allow-empty -m "unmerged work" &&
- git checkout -b tracks-other other/main --track &&
- sha=$(git rev-parse --short merged) &&
-
- git branch --dry-run --delete-merged origin/next merged >actual 2>&1 &&
- echo "Would delete branch merged (was $sha)." >expect &&
- test_cmp expect actual &&
- git rev-parse --verify refs/heads/merged &&
-
- check_branches <<-\EOF &&
- also-merged
- main
- merged
- tracks-other
- unmerged
- EOF
-
- git branch --delete-merged origin/next merged >actual 2>&1 &&
- echo "Deleted branch merged (was $sha)." >expect &&
- test_cmp expect actual &&
-
- check_branches <<-\EOF
- also-merged
- main
- tracks-other
- unmerged
- EOF
- )
-'
-
-test_expect_success '--delete-merged keeps main despite a different default push remote' '
- setup_repo_for_delete_merged &&
- create_merged_branch on-next &&
- create_merged_branch checked-out &&
- create_merged_branch upstream-gone &&
- (
- cd repo &&
- git config remote.pushDefault fork &&
- git checkout -b local-to-delete main --track &&
- git update-ref refs/remotes/origin/topic refs/remotes/origin/next &&
- git branch --set-upstream-to=origin/topic upstream-gone &&
- git update-ref -d refs/remotes/origin/topic &&
- git checkout -b tracks-other other/main --track &&
- git checkout checked-out &&
-
- git branch --delete-merged origin/* \
- --delete-merged main &&
-
- check_branches <<-\EOF
- checked-out
- main
- tracks-other
- upstream-gone
- EOF
- )
-'
-
-test_expect_success '--delete-merged keeps the upstream of a surviving branch' '
- setup_repo_for_delete_merged &&
- create_merged_branch feature &&
- (
- cd repo &&
- git checkout -b topic feature --track &&
- git commit --allow-empty -m "topic work" &&
-
- git branch --dry-run --delete-merged origin/next >out &&
- test_grep ! "feature" out &&
-
- git branch --delete-merged origin/next 2>err &&
- test_must_be_empty err &&
-
- check_branches <<-\EOF &&
- feature
- main
- topic
- EOF
-
- git config --local --get-regexp "branch\\.(feature|topic)\\.(merge|remote)" >actual &&
- cat >expect <<-\EOF &&
- branch.feature.remote origin
- branch.feature.merge refs/heads/next
- branch.topic.remote .
- branch.topic.merge refs/heads/feature
- EOF
- test_cmp expect actual
- )
-'
-
-test_expect_success '--delete-merged keeps the upstream chain of a surviving branch' '
- setup_repo_for_delete_merged &&
- (
- cd repo &&
- git config remote.pushDefault fork &&
- git branch lower origin/next --track &&
- git branch mid lower --track &&
- git checkout -b tip mid --track &&
- git commit --allow-empty -m "tip work" &&
-
- git branch --dry-run --delete-merged origin/next \
- --delete-merged lower >actual 2>&1 &&
- test_must_be_empty actual &&
-
- git config --local --get-regexp "branch\\.(lower|mid|tip)\\.(merge|remote)" >actual &&
- cat >expect <<-\EOF &&
- branch.lower.remote origin
- branch.lower.merge refs/heads/next
- branch.mid.remote .
- branch.mid.merge refs/heads/lower
- branch.tip.remote .
- branch.tip.merge refs/heads/mid
- EOF
- test_cmp expect actual &&
-
- git branch --delete-merged origin/next \
- --delete-merged lower >actual 2>&1 &&
- test_must_be_empty actual &&
-
- check_branches <<-\EOF &&
- lower
- main
- mid
- tip
- EOF
-
- git config --local --get-regexp "branch\\.(lower|mid|tip)\\.(merge|remote)" >actual &&
- cat >expect <<-\EOF &&
- branch.lower.remote origin
- branch.lower.merge refs/heads/next
- branch.mid.remote .
- branch.mid.merge refs/heads/lower
- branch.tip.remote .
- branch.tip.merge refs/heads/mid
- EOF
- test_cmp expect actual
- )
-'
-
-test_expect_success '--delete-merged result is independent of stacked branch names' '
- setup_repo_for_delete_merged &&
- (
- cd repo &&
- git branch c-lower origin/next --track &&
- git branch b-mid c-lower --track &&
- git checkout -b a-tip b-mid --track &&
- git commit --allow-empty -m "tip work" &&
-
- git branch --delete-merged origin/next \
- --delete-merged "c-*" &&
-
- check_branches <<-\EOF &&
- a-tip
- b-mid
- c-lower
- main
- EOF
-
- git branch --delete-merged origin/next \
- --delete-merged "c-*" >actual 2>&1 &&
- test_must_be_empty actual &&
-
- check_branches <<-\EOF
- a-tip
- b-mid
- c-lower
- main
- EOF
- )
-'
-
-test_expect_success '--delete-merged requires a value' '
- test_must_fail git -C forked branch --delete-merged 2>err &&
- test_grep "requires a value" err
-'
-
-test_expect_success '--delete-merged honours branch.<name>.deleteMerged=false' '
- setup_repo_for_delete_merged &&
- create_merged_branch deleted &&
- create_merged_branch kept &&
- (
- cd repo &&
- git config branch.kept.deleteMerged false &&
- git checkout --detach &&
-
- git branch --delete-merged origin/next 2>err &&
-
- test_grep "Skipping .kept." err &&
- check_branches <<-\EOF
- kept
- main
- EOF
- )
-'
-
-test_expect_success "branch -d still deletes a deleteMerged=false branch" '
- setup_repo_for_delete_merged &&
- create_merged_branch kept &&
- (
- cd repo &&
- git config branch.kept.deleteMerged false &&
- git checkout --detach &&
-
- git branch -d kept &&
-
- check_branches <<-\EOF
- main
- EOF
- )
-'
-
-test_expect_success '--dry-run without --delete-merged is rejected' '
- test_must_fail git -C forked branch --dry-run 2>err &&
- test_grep "requires --delete-merged" err
-'
-
test_done