]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Revert "Merge branch 'hn/branch-delete-merged' into next"
authorJunio C Hamano <gitster@pobox.com>
Wed, 29 Jul 2026 15:53:32 +0000 (08:53 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 29 Jul 2026 15:53:32 +0000 (08:53 -0700)
This reverts commit c61a87bdf5fd46f4921228da399264eced14f380, reversing
changes made to 57c0e98bfb6395faec6e8ec9b4b6374364f8898b.

cf. <80bd230e-7b8c-41d3-af1c-fa84b0c7b1c4@gmail.com>

Documentation/config/branch.adoc
Documentation/git-branch.adoc
builtin/branch.c
ref-filter.c
ref-filter.h
t/t3200-branch.sh

index c2acd89c9936cbd8c064f9755ed28f9e8b4da9d3..5a85fde8de9286a4ac2d188a1044dddd2855b928 100644 (file)
@@ -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.
index 51dda151142c66a49c506262372381535b65b92d..c0afddc424d610122f9a468bc1c7eaae6b373fdd 100644 (file)
@@ -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>_.
 
index 985e36d68d96b009eb62352c670ed105158fe147..357209748d4fe4f77230c569b169655a276d77bf 100644 (file)
@@ -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;
 }
index bdf54f6f592499e8056147427bb29ead781f4267..29aca08ce7b3339bca5b407b24fde858a4d6ca62 100644 (file)
@@ -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);
index 9361296e2a74402ba817685129588313fccd7b60..120221b47fa30dc05d5e57b34d0b30242e2ae23b 100644 (file)
@@ -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. */
index 443512677a9a48512b353e8a4e79276279d32e51..b5a56ff25d871cfd0068991ba5073ad9ec8a1e40 100755 (executable)
@@ -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