From: Phillip Wood <phillip.wood123@gmail.com>
To: Harald Nordgren via GitGitGadget <gitgitgadget@gmail.com>,
git@vger.kernel.org
Cc: Kristoffer Haugsbakk <kristofferhaugsbakk@fastmail.com>,
Johannes Sixt <j6t@kdbg.org>,
Harald Nordgren <haraldnordgren@gmail.com>
Subject: Re: [PATCH v23 5/7] branch: add --delete-merged <branch>
Date: Wed, 29 Jul 2026 16:10:12 +0100 [thread overview]
Message-ID: <1f282ad4-9937-4c95-89d4-70f7a1c883a8@gmail.com> (raw)
In-Reply-To: <5fd74f0050e5af1f2ab03ddae56dc96385e6a237.1784979136.git.gitgitgadget@gmail.com>
Hi Harald
Sorry it has taken so long for we to look at this again - I'm not sure
where last week went.
On 25/07/2026 12:32, Harald Nordgren via GitGitGadget wrote:
> From: Harald Nordgren <haraldnordgren@gmail.com>
>
> git branch (--delete-merged <branch>)... [<pattern>...]
>
> deletes local branches matching the optional patterns when their
> configured upstream matches one of the --delete-merged arguments and
> their tip is reachable from that upstream. The work has already landed
> on the upstream they track, so the local copy is no longer needed.
>
> The option can be repeated to widen the upstream match. Keeping the
> candidate patterns as positional arguments lets users bound the set of
> local branches that may be deleted independently of the upstream
> selection.
>
> A branch is not deleted when:
>
> * it is checked out in any worktree
> * its configured upstream ref no longer exists, since a missing
> upstream is not by itself a sign of integration
> * pushing it by name to the remote configured by
> branch.<name>.remote would update its upstream, as determined by
> mapping the branch ref through that remote's fetch refspec. For
> example, a local "main" that tracks "origin/main" is kept even when
> remote.pushDefault names a fork. Right after a pull it merely looks
> fully merged.
>
> A branch whose work is not yet merged into its upstream is silently
> skipped, so one unmerged topic does not abort the whole sweep.
>
> A branch that a surviving branch depends on through a chain of local
> upstreams is also kept, so no branch is deleted out from under stacked
> work.
Shouldn't this be part of the list above. So we no-longer delete any
branch in a chain of stacked branches when one of them is unmerged?
Previously we only kept the upstream of the unmerged branch and deleted
the rest.
Collect this transitive set without changing the candidate set
> during ref iteration: walk upstream chains from surviving branches,
> visit each branch at most once, and remove the collected bases only
> after the iteration completes. This makes the result independent of
> ref iteration order without repeated full scans.
>
> Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
> ---
> Documentation/git-branch.adoc | 30 +++++
> builtin/branch.c | 158 +++++++++++++++++++++++++-
> t/t3200-branch.sh | 204 ++++++++++++++++++++++++++++++++++
> 3 files changed, 390 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/git-branch.adoc b/Documentation/git-branch.adoc
> index b0d66a6deb..2a96cd7253 100644
> --- a/Documentation/git-branch.adoc
> +++ b/Documentation/git-branch.adoc
> @@ -25,6 +25,7 @@ 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 (--delete-merged <branch>)... [<pattern>...]
>
> DESCRIPTION
> -----------
> @@ -201,6 +202,35 @@ 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, or
> +* 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.
* it is the upstream of an unmerged branch
> +--
> ++
> +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.
> +
> `-v`::
> `-vv`::
> `--verbose`::
> diff --git a/builtin/branch.c b/builtin/branch.c
> index 1ef8362c12..78b694034f 100644
> --- a/builtin/branch.c
> +++ b/builtin/branch.c
> @@ -21,6 +21,7 @@
> #include "branch.h"
> #include "path.h"
> #include "string-list.h"
> +#include "strmap.h"
> #include "column.h"
> #include "utf8.h"
> #include "ref-filter.h"
> @@ -38,6 +39,7 @@ 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>...]"),
I don't quite follow this - why the "()" and doesn't --delete-merged
take a pattern?
> NULL
> };
>
> @@ -699,6 +701,148 @@ static int parse_opt_forked(const struct option *opt, const char *arg, int unset
> 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;
> + }
This looks correct, it is a shame we have to build
"visited_branch_names" but the code is clear.
> + 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);
We remove the protected branches from deleteable - good
> +
> + 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);
This tells us which remote tracking ref corresponds to the branch
> + if (tracking && !strcmp(tracking, upstream))
> + ret = 1;
Here we check that it does not match the upstream branch. That ignores
the push refspect though so does not tell us whether pushing the branch
to the upstream remote would update the upstream branch on that remote.
We need to apply the push refspec to the local branch, apply the fetch
refspec in reverse to the result and then compare that to the upstream
branch.
> +
> + 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 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;
> +
> + 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;
> +
> + strset_add(&deletable_branch_names, branch_name);
> + }
We build the set of branches to delete and then
> + protect_stacked_branch_bases(refs, &deletable_branch_names);
remove the ones that are upstreams of branches that are not in the set
and then
> + strset_for_each_entry(&deletable_branch_names, &iter, entry)
> + strvec_push(&branches_to_delete, entry->key);
build a list of branches to delete - good.
> + 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);
> +
> + strvec_clear(&branches_to_delete);
> + strset_clear(&deletable_branch_names);
> + ref_array_clear(&candidates);
> + ref_filter_clear(&filter);
> + return ret;
> +}
> diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
> index 4ffd224a71..268203089b 100755
> --- a/t/t3200-branch.sh
> +++ b/t/t3200-branch.sh
> @@ -1872,4 +1872,208 @@ test_expect_success '--forked requires a value' '
> 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
> +}
This makes checking the remaining branches in the tests really nice> +
> +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 */* &&
Unless I've missed something main does not have an upstream branch set,
so we'd never expect it to be deleted, even if a push remote was set.
> +
> + 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 &&
In the tests we try to avoid mixing options and positional arguments so
we use either
git checkout --track -b unmerged origin/next
or
git checkout -b unmerged --track origin/next
> + git commit --allow-empty -m "unmerged work" &&
> + git checkout -b tracks-other other/main --track &&
> + sha=$(git rev-parse --short merged) &&
> +
> + 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
As we passed "merged" on the command line "also-merged" was not deleted
- good.
> + 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 &&
As I think I said last time, this is a very round-about way to have the
upstream gone. It would be much simpler just to set the config directly.
> + git checkout -b tracks-other other/main --track &&
> + git checkout checked-out &&
> +
> + git branch --delete-merged origin/* \
> + --delete-merged main &&
These lines look very short why the wrapping?
> +
> + 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 &&
we base our branch on feature which is merged into origin/next
> + git commit --allow-empty -m "topic work" &&
> +
> + 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
and so we keep feature and it's upstream config (because it's upstream
branch still exists) - good.
> + 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 --delete-merged origin/next \
> + --delete-merged lower >actual 2>&1 &&
> + test_must_be_empty actual &&
> +
> + check_branches <<-\EOF &&
> + lower
Why do we keep "lower", rather than clear the upstream config of "mid"?
I'm not completely convinced by the new behavior wrt stacked branches,
but this looks good apart from the branch_pushes_to_upstream() issue.
Thanks
Phillip
> + 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_done
next prev parent reply other threads:[~2026-07-29 15:10 UTC|newest]
Thread overview: 269+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-01 21:35 [PATCH] fetch: add fetch.pruneLocalBranches config Harald Nordgren via GitGitGadget
2026-05-03 22:39 ` Junio C Hamano
2026-05-04 18:28 ` [PATCH] checkout: add --autostash option for branch switching Harald Nordgren
2026-05-10 1:01 ` Junio C Hamano
2026-05-05 7:14 ` [PATCH] fetch: add fetch.pruneLocalBranches config Johannes Sixt
2026-05-04 18:27 ` [PATCH v2 0/6] fetch: add fetch.pruneBranches config Harald Nordgren via GitGitGadget
2026-05-04 18:27 ` [PATCH v2 1/6] branch: add --forked <remote> Harald Nordgren via GitGitGadget
2026-05-04 23:25 ` Kristoffer Haugsbakk
2026-05-04 18:27 ` [PATCH v2 2/6] branch: let delete_branches warn instead of error on bulk refusal Harald Nordgren via GitGitGadget
2026-05-04 18:27 ` [PATCH v2 3/6] branch: add --prune-merged <remote> Harald Nordgren via GitGitGadget
2026-05-04 18:27 ` [PATCH v2 4/6] fetch: add --prune-merged Harald Nordgren via GitGitGadget
2026-05-04 18:27 ` [PATCH v2 5/6] branch: add branch.<name>.pruneMerged opt-out Harald Nordgren via GitGitGadget
2026-05-04 18:27 ` [PATCH v2 6/6] branch: add --all-remotes flag Harald Nordgren via GitGitGadget
2026-05-05 7:22 ` [PATCH v3 0/6] fetch: add fetch.pruneBranches config Harald Nordgren via GitGitGadget
2026-05-05 7:22 ` [PATCH v3 1/6] branch: add --forked <remote> Harald Nordgren via GitGitGadget
2026-05-05 7:22 ` [PATCH v3 2/6] branch: let delete_branches warn instead of error on bulk refusal Harald Nordgren via GitGitGadget
2026-05-05 7:22 ` [PATCH v3 3/6] branch: add --prune-merged <remote> Harald Nordgren via GitGitGadget
2026-05-05 7:22 ` [PATCH v3 4/6] fetch: add --prune-merged Harald Nordgren via GitGitGadget
2026-05-05 7:22 ` [PATCH v3 5/6] branch: add branch.<name>.pruneMerged opt-out Harald Nordgren via GitGitGadget
2026-05-05 7:22 ` [PATCH v3 6/6] branch: add --all-remotes flag Harald Nordgren via GitGitGadget
2026-05-05 19:23 ` [PATCH v4 0/6] fetch: add fetch.pruneBranches config Harald Nordgren via GitGitGadget
2026-05-05 19:23 ` [PATCH v4 1/6] branch: add --forked <remote> Harald Nordgren via GitGitGadget
2026-05-05 19:23 ` [PATCH v4 2/6] branch: let delete_branches warn instead of error on bulk refusal Harald Nordgren via GitGitGadget
2026-05-05 19:23 ` [PATCH v4 3/6] branch: add --prune-merged <remote> Harald Nordgren via GitGitGadget
2026-05-05 19:23 ` [PATCH v4 4/6] fetch: add --prune-merged Harald Nordgren via GitGitGadget
2026-05-05 20:48 ` Johannes Sixt
2026-05-05 22:07 ` [PATCH] fetch: add fetch.pruneLocalBranches config Harald Nordgren
2026-05-11 2:59 ` Junio C Hamano
2026-05-11 6:56 ` Harald Nordgren
2026-05-05 19:23 ` [PATCH v4 5/6] branch: add branch.<name>.pruneMerged opt-out Harald Nordgren via GitGitGadget
2026-05-05 19:23 ` [PATCH v4 6/6] branch: add --all-remotes flag Harald Nordgren via GitGitGadget
2026-05-07 20:14 ` [PATCH v4 0/6] fetch: add fetch.pruneBranches config Harald Nordgren
2026-05-11 6:58 ` [PATCH v5 0/5] branch: prune-merged Harald Nordgren via GitGitGadget
2026-05-11 6:58 ` [PATCH v5 1/5] branch: add --forked <remote> Harald Nordgren via GitGitGadget
2026-05-11 6:58 ` [PATCH v5 2/5] branch: let delete_branches warn instead of error on bulk refusal Harald Nordgren via GitGitGadget
2026-05-11 8:18 ` Junio C Hamano
2026-05-11 8:44 ` [PATCH] fetch: add fetch.pruneLocalBranches config Harald Nordgren
2026-05-11 6:58 ` [PATCH v5 3/5] branch: add --prune-merged <remote> Harald Nordgren via GitGitGadget
2026-05-11 6:58 ` [PATCH v5 4/5] branch: add branch.<name>.pruneMerged opt-out Harald Nordgren via GitGitGadget
2026-05-11 6:58 ` [PATCH v5 5/5] branch: add --all-remotes flag Harald Nordgren via GitGitGadget
2026-05-11 9:44 ` [PATCH v6 0/5] branch: prune-merged Harald Nordgren via GitGitGadget
2026-05-11 9:44 ` [PATCH v6 1/5] branch: add --forked <remote> Harald Nordgren via GitGitGadget
2026-05-11 9:44 ` [PATCH v6 2/5] branch: let delete_branches warn instead of error on bulk refusal Harald Nordgren via GitGitGadget
2026-05-11 9:44 ` [PATCH v6 3/5] branch: add --prune-merged <remote> Harald Nordgren via GitGitGadget
2026-05-11 9:44 ` [PATCH v6 4/5] branch: add branch.<name>.pruneMerged opt-out Harald Nordgren via GitGitGadget
2026-05-11 9:44 ` [PATCH v6 5/5] branch: add --all-remotes flag Harald Nordgren via GitGitGadget
2026-05-11 23:20 ` [PATCH v6 0/5] branch: prune-merged Junio C Hamano
2026-05-12 7:35 ` [PATCH] fetch: add fetch.pruneLocalBranches config Harald Nordgren
2026-05-12 8:23 ` [PATCH v7 0/5] branch: prune-merged Harald Nordgren via GitGitGadget
2026-05-12 8:23 ` [PATCH v7 1/5] branch: add --forked <remote> Harald Nordgren via GitGitGadget
2026-05-12 8:23 ` [PATCH v7 2/5] branch: let delete_branches warn instead of error on bulk refusal Harald Nordgren via GitGitGadget
2026-05-12 8:23 ` [PATCH v7 3/5] branch: add --prune-merged <remote> Harald Nordgren via GitGitGadget
2026-05-12 13:53 ` Junio C Hamano
2026-05-12 17:00 ` [PATCH] fetch: add fetch.pruneLocalBranches config Harald Nordgren
2026-05-12 8:23 ` [PATCH v7 4/5] branch: add branch.<name>.pruneMerged opt-out Harald Nordgren via GitGitGadget
2026-05-12 8:23 ` [PATCH v7 5/5] branch: add --all-remotes flag Harald Nordgren via GitGitGadget
2026-05-12 17:07 ` [PATCH v8 0/5] branch: prune-merged Harald Nordgren via GitGitGadget
2026-05-12 17:07 ` [PATCH v8 1/5] branch: add --forked <remote> Harald Nordgren via GitGitGadget
2026-05-12 17:07 ` [PATCH v8 2/5] branch: let delete_branches warn instead of error on bulk refusal Harald Nordgren via GitGitGadget
2026-05-12 17:07 ` [PATCH v8 3/5] branch: add --prune-merged <remote> Harald Nordgren via GitGitGadget
2026-05-12 17:07 ` [PATCH v8 4/5] branch: add branch.<name>.pruneMerged opt-out Harald Nordgren via GitGitGadget
2026-05-12 17:07 ` [PATCH v8 5/5] branch: add --all-remotes flag Harald Nordgren via GitGitGadget
2026-05-13 13:46 ` [PATCH v8 0/5] branch: prune-merged Junio C Hamano
2026-05-13 18:57 ` [PATCH] fetch: add fetch.pruneLocalBranches config Harald Nordgren
2026-05-13 19:34 ` [PATCH v9 0/5] branch: prune-merged Harald Nordgren via GitGitGadget
2026-05-13 19:34 ` [PATCH v9 1/5] branch: add --forked <remote> Harald Nordgren via GitGitGadget
2026-05-13 19:34 ` [PATCH v9 2/5] branch: let delete_branches warn instead of error on bulk refusal Harald Nordgren via GitGitGadget
2026-05-13 19:34 ` [PATCH v9 3/5] branch: add --prune-merged <remote> Harald Nordgren via GitGitGadget
2026-05-18 15:27 ` Phillip Wood
2026-05-21 9:46 ` Phillip Wood
2026-05-21 19:16 ` Harald Nordgren
2026-05-22 9:47 ` Phillip Wood
2026-05-22 10:51 ` Harald Nordgren
2026-05-21 12:37 ` Harald Nordgren
2026-05-21 13:29 ` Junio C Hamano
2026-05-13 19:34 ` [PATCH v9 4/5] branch: add branch.<name>.pruneMerged opt-out Harald Nordgren via GitGitGadget
2026-05-13 19:34 ` [PATCH v9 5/5] branch: add --all-remotes flag Harald Nordgren via GitGitGadget
2026-05-18 15:27 ` Phillip Wood
2026-05-18 8:14 ` [PATCH v9 0/5] branch: prune-merged Harald Nordgren
2026-05-21 22:40 ` [PATCH v10 0/4] " Harald Nordgren via GitGitGadget
2026-05-21 22:40 ` [PATCH v10 1/4] branch: add --forked <branch> Harald Nordgren via GitGitGadget
2026-05-22 1:52 ` Junio C Hamano
2026-05-22 6:18 ` Johannes Sixt
2026-05-22 6:36 ` Junio C Hamano
2026-05-22 10:49 ` Harald Nordgren
2026-05-22 11:25 ` Johannes Sixt
2026-05-21 22:40 ` [PATCH v10 2/4] branch: add --prune-merged <branch> Harald Nordgren via GitGitGadget
2026-05-22 1:17 ` Junio C Hamano
2026-05-22 2:51 ` Junio C Hamano
2026-05-22 2:53 ` Junio C Hamano
2026-05-22 7:59 ` Harald Nordgren
2026-05-22 11:58 ` Junio C Hamano
2026-05-22 2:52 ` Junio C Hamano
2026-05-21 22:40 ` [PATCH v10 3/4] branch: add branch.<name>.pruneMerged opt-out Harald Nordgren via GitGitGadget
2026-05-21 22:40 ` [PATCH v10 4/4] branch: add --dry-run for --prune-merged Harald Nordgren via GitGitGadget
2026-05-22 11:31 ` [PATCH v11 0/6] branch: prune-merged Harald Nordgren via GitGitGadget
2026-05-22 11:31 ` [PATCH v11 1/6] branch: add --forked <branch> Harald Nordgren via GitGitGadget
2026-05-22 11:31 ` [PATCH v11 2/6] branch: let delete_branches warn instead of error on bulk refusal Harald Nordgren via GitGitGadget
2026-05-22 11:31 ` [PATCH v11 3/6] branch: prepare delete_branches for a bulk caller Harald Nordgren via GitGitGadget
2026-05-22 11:31 ` [PATCH v11 4/6] branch: add --prune-merged <branch> Harald Nordgren via GitGitGadget
2026-05-22 11:31 ` [PATCH v11 5/6] branch: add branch.<name>.pruneMerged opt-out Harald Nordgren via GitGitGadget
2026-05-22 11:31 ` [PATCH v11 6/6] branch: add --dry-run for --prune-merged Harald Nordgren via GitGitGadget
2026-06-02 13:05 ` [PATCH v11 0/6] branch: prune-merged Phillip Wood
2026-06-02 13:41 ` Harald Nordgren
2026-06-03 9:04 ` [PATCH v12 " Harald Nordgren via GitGitGadget
2026-06-03 9:04 ` [PATCH v12 1/6] branch: add --forked filter for --list mode Harald Nordgren via GitGitGadget
2026-06-05 13:48 ` Phillip Wood
2026-06-05 17:50 ` Harald Nordgren
2026-06-03 9:04 ` [PATCH v12 2/6] branch: let delete_branches warn instead of error on bulk refusal Harald Nordgren via GitGitGadget
2026-06-05 13:49 ` Phillip Wood
2026-06-03 9:04 ` [PATCH v12 3/6] branch: prepare delete_branches for a bulk caller Harald Nordgren via GitGitGadget
2026-06-05 13:49 ` Phillip Wood
2026-06-03 9:04 ` [PATCH v12 4/6] branch: add --prune-merged <branch> Harald Nordgren via GitGitGadget
2026-06-05 13:50 ` Phillip Wood
2026-06-05 15:04 ` Phillip Wood
2026-06-03 9:04 ` [PATCH v12 5/6] branch: add branch.<name>.pruneMerged opt-out Harald Nordgren via GitGitGadget
2026-06-03 9:04 ` [PATCH v12 6/6] branch: add --dry-run for --prune-merged Harald Nordgren via GitGitGadget
2026-06-05 18:35 ` [PATCH v13 0/6] branch: prune-merged Harald Nordgren via GitGitGadget
2026-06-05 18:35 ` [PATCH v13 1/6] branch: add --forked filter for --list mode Harald Nordgren via GitGitGadget
2026-06-05 18:35 ` [PATCH v13 2/6] branch: let delete_branches warn instead of error on bulk refusal Harald Nordgren via GitGitGadget
2026-06-08 23:56 ` Junio C Hamano
2026-06-09 7:52 ` Harald Nordgren
2026-06-09 12:38 ` Junio C Hamano
2026-06-09 13:20 ` Harald Nordgren
2026-06-05 18:35 ` [PATCH v13 3/6] branch: prepare delete_branches for a bulk caller Harald Nordgren via GitGitGadget
2026-06-05 18:35 ` [PATCH v13 4/6] branch: add --prune-merged <branch> Harald Nordgren via GitGitGadget
2026-06-05 18:35 ` [PATCH v13 5/6] branch: add branch.<name>.pruneMerged opt-out Harald Nordgren via GitGitGadget
2026-06-05 18:35 ` [PATCH v13 6/6] branch: add --dry-run for --prune-merged Harald Nordgren via GitGitGadget
2026-06-09 10:11 ` [PATCH v14 0/6] branch: prune-merged Harald Nordgren via GitGitGadget
2026-06-09 10:11 ` [PATCH v14 1/6] branch: add --forked filter for --list mode Harald Nordgren via GitGitGadget
2026-06-15 9:46 ` Phillip Wood
2026-06-09 10:11 ` [PATCH v14 2/6] branch: let delete_branches warn instead of error on bulk refusal Harald Nordgren via GitGitGadget
2026-06-09 13:21 ` Phillip Wood
2026-06-09 10:11 ` [PATCH v14 3/6] branch: prepare delete_branches for a bulk caller Harald Nordgren via GitGitGadget
2026-06-15 9:47 ` Phillip Wood
2026-06-09 10:11 ` [PATCH v14 4/6] branch: add --prune-merged <branch> Harald Nordgren via GitGitGadget
2026-06-15 9:46 ` Phillip Wood
2026-06-16 9:59 ` Phillip Wood
2026-06-16 19:15 ` Harald Nordgren
2026-06-18 13:42 ` Phillip Wood
2026-06-18 16:08 ` Junio C Hamano
2026-06-19 13:13 ` Phillip Wood
2026-06-19 15:42 ` Junio C Hamano
2026-06-19 16:01 ` Junio C Hamano
2026-06-20 9:04 ` Harald Nordgren
2026-06-21 18:46 ` Harald Nordgren
2026-06-22 9:09 ` Phillip Wood
2026-06-22 9:28 ` Phillip Wood
2026-06-22 9:37 ` Harald Nordgren
2026-06-22 9:57 ` Phillip Wood
2026-06-22 10:52 ` Harald Nordgren
2026-06-22 9:25 ` Phillip Wood
2026-06-22 9:07 ` Phillip Wood
2026-06-22 12:26 ` Junio C Hamano
2026-06-09 10:11 ` [PATCH v14 5/6] branch: add branch.<name>.pruneMerged opt-out Harald Nordgren via GitGitGadget
2026-06-16 9:57 ` Phillip Wood
2026-06-09 10:11 ` [PATCH v14 6/6] branch: add --dry-run for --prune-merged Harald Nordgren via GitGitGadget
2026-06-16 9:57 ` Phillip Wood
2026-06-16 18:28 ` Harald Nordgren
2026-06-15 16:47 ` [PATCH v15 0/7] branch: delete-merged Harald Nordgren via GitGitGadget
2026-06-15 16:47 ` [PATCH v15 1/7] branch: add --forked filter for --list mode Harald Nordgren via GitGitGadget
2026-06-15 16:47 ` [PATCH v15 2/7] branch: convert delete_branches() to a flags argument Harald Nordgren via GitGitGadget
2026-06-15 16:47 ` [PATCH v15 3/7] branch: let delete_branches skip unmerged branches on bulk refusal Harald Nordgren via GitGitGadget
2026-06-15 16:47 ` [PATCH v15 4/7] branch: prepare delete_branches for a bulk caller Harald Nordgren via GitGitGadget
2026-06-15 16:47 ` [PATCH v15 5/7] branch: add --delete-merged <branch> Harald Nordgren via GitGitGadget
2026-06-15 16:47 ` [PATCH v15 6/7] branch: add branch.<name>.deleteMerged opt-out Harald Nordgren via GitGitGadget
2026-06-15 16:47 ` [PATCH v15 7/7] branch: add --dry-run for --delete-merged Harald Nordgren via GitGitGadget
2026-06-17 10:01 ` [PATCH v15 0/7] branch: delete-merged Phillip Wood
2026-06-17 11:17 ` Harald Nordgren
2026-06-17 14:21 ` Phillip Wood
2026-06-17 19:11 ` Harald Nordgren
2026-06-18 13:48 ` Phillip Wood
2026-06-18 17:53 ` Harald Nordgren
2026-06-18 19:25 ` [PATCH v16 " Harald Nordgren via GitGitGadget
2026-06-18 19:25 ` [PATCH v16 1/7] branch: add --forked filter for --list mode Harald Nordgren via GitGitGadget
2026-06-18 19:25 ` [PATCH v16 2/7] branch: convert delete_branches() to a flags argument Harald Nordgren via GitGitGadget
2026-06-18 19:25 ` [PATCH v16 3/7] branch: let delete_branches skip unmerged branches on bulk refusal Harald Nordgren via GitGitGadget
2026-06-18 19:25 ` [PATCH v16 4/7] branch: prepare delete_branches for a bulk caller Harald Nordgren via GitGitGadget
2026-06-18 19:25 ` [PATCH v16 5/7] branch: add --delete-merged <branch> Harald Nordgren via GitGitGadget
2026-06-18 19:25 ` [PATCH v16 6/7] branch: add branch.<name>.deleteMerged opt-out Harald Nordgren via GitGitGadget
2026-06-18 19:25 ` [PATCH v16 7/7] branch: add --dry-run for --delete-merged Harald Nordgren via GitGitGadget
2026-06-22 7:29 ` [PATCH v17 0/7] branch: delete-merged Harald Nordgren via GitGitGadget
2026-06-22 7:29 ` [PATCH v17 1/7] branch: add --forked filter for --list mode Harald Nordgren via GitGitGadget
2026-06-22 7:29 ` [PATCH v17 2/7] branch: convert delete_branches() to a flags argument Harald Nordgren via GitGitGadget
2026-06-22 7:29 ` [PATCH v17 3/7] branch: let delete_branches skip unmerged branches on bulk refusal Harald Nordgren via GitGitGadget
2026-06-22 7:29 ` [PATCH v17 4/7] branch: prepare delete_branches for a bulk caller Harald Nordgren via GitGitGadget
2026-06-22 7:29 ` [PATCH v17 5/7] branch: add --delete-merged <branch> Harald Nordgren via GitGitGadget
2026-06-22 15:36 ` Phillip Wood
2026-06-22 7:29 ` [PATCH v17 6/7] branch: add branch.<name>.deleteMerged opt-out Harald Nordgren via GitGitGadget
2026-06-22 7:29 ` [PATCH v17 7/7] branch: add --dry-run for --delete-merged Harald Nordgren via GitGitGadget
2026-06-24 21:54 ` [PATCH v18 0/7] branch: delete-merged Harald Nordgren via GitGitGadget
2026-06-24 21:55 ` [PATCH v18 1/7] branch: add --forked filter for --list mode Harald Nordgren via GitGitGadget
2026-07-10 15:18 ` Phillip Wood
2026-07-11 12:30 ` Harald Nordgren
2026-06-24 21:55 ` [PATCH v18 2/7] branch: convert delete_branches() to a flags argument Harald Nordgren via GitGitGadget
2026-07-10 15:18 ` Phillip Wood
2026-07-10 17:25 ` Harald Nordgren
2026-07-13 15:28 ` Phillip Wood
2026-06-24 21:55 ` [PATCH v18 3/7] branch: let delete_branches skip unmerged branches on bulk refusal Harald Nordgren via GitGitGadget
2026-07-10 15:18 ` Phillip Wood
2026-06-24 21:55 ` [PATCH v18 4/7] branch: prepare delete_branches for a bulk caller Harald Nordgren via GitGitGadget
2026-06-24 21:55 ` [PATCH v18 5/7] branch: add --delete-merged <branch> Harald Nordgren via GitGitGadget
2026-07-10 15:24 ` Phillip Wood
2026-07-11 13:08 ` Harald Nordgren
2026-07-11 19:36 ` Harald Nordgren
2026-07-13 15:39 ` Phillip Wood
2026-07-13 16:39 ` Phillip Wood
2026-07-13 18:17 ` Harald Nordgren
2026-07-14 13:01 ` Phillip Wood
2026-07-14 17:00 ` Harald Nordgren
2026-06-24 21:55 ` [PATCH v18 6/7] branch: add branch.<name>.deleteMerged opt-out Harald Nordgren via GitGitGadget
2026-07-13 15:27 ` Phillip Wood
2026-07-13 16:33 ` Harald Nordgren
2026-06-24 21:55 ` [PATCH v18 7/7] branch: add --dry-run for --delete-merged Harald Nordgren via GitGitGadget
2026-07-13 15:27 ` Phillip Wood
2026-07-13 16:23 ` Harald Nordgren
2026-07-10 15:26 ` [PATCH v18 0/7] branch: delete-merged Phillip Wood
2026-07-14 18:24 ` [PATCH v19 " Harald Nordgren via GitGitGadget
2026-07-14 18:24 ` [PATCH v19 1/7] branch: add --forked filter for --list mode Harald Nordgren via GitGitGadget
2026-07-14 18:24 ` [PATCH v19 2/7] branch: convert delete_branches() to a flags argument Harald Nordgren via GitGitGadget
2026-07-14 18:24 ` [PATCH v19 3/7] branch: let delete_branches skip unmerged branches on bulk refusal Harald Nordgren via GitGitGadget
2026-07-14 18:24 ` [PATCH v19 4/7] branch: prepare delete_branches for a bulk caller Harald Nordgren via GitGitGadget
2026-07-14 18:24 ` [PATCH v19 5/7] branch: add --delete-merged <branch> Harald Nordgren via GitGitGadget
2026-07-19 3:02 ` Junio C Hamano
2026-07-19 15:30 ` Harald Nordgren
2026-07-19 19:22 ` Junio C Hamano
2026-07-19 19:42 ` Phillip Wood
2026-07-19 21:42 ` Junio C Hamano
2026-07-14 18:24 ` [PATCH v19 6/7] branch: add branch.<name>.deleteMerged opt-out Harald Nordgren via GitGitGadget
2026-07-14 18:24 ` [PATCH v19 7/7] branch: add --dry-run for --delete-merged Harald Nordgren via GitGitGadget
2026-07-22 7:10 ` [PATCH v20 0/7] branch: delete-merged Harald Nordgren via GitGitGadget
2026-07-22 7:10 ` [PATCH v20 1/7] branch: add --forked filter for --list mode Harald Nordgren via GitGitGadget
2026-07-22 7:10 ` [PATCH v20 2/7] branch: convert delete_branches() to a flags argument Harald Nordgren via GitGitGadget
2026-07-22 7:10 ` [PATCH v20 3/7] branch: let delete_branches skip unmerged branches on bulk refusal Harald Nordgren via GitGitGadget
2026-07-22 7:10 ` [PATCH v20 4/7] branch: prepare delete_branches for a bulk caller Harald Nordgren via GitGitGadget
2026-07-22 7:10 ` [PATCH v20 5/7] branch: add --delete-merged <branch> Harald Nordgren via GitGitGadget
2026-07-22 7:10 ` [PATCH v20 6/7] branch: add branch.<name>.deleteMerged opt-out Harald Nordgren via GitGitGadget
2026-07-22 7:10 ` [PATCH v20 7/7] branch: add --dry-run for --delete-merged Harald Nordgren via GitGitGadget
2026-07-22 13:39 ` [PATCH v20 0/7] branch: delete-merged Phillip Wood
2026-07-22 15:41 ` Harald Nordgren
2026-07-24 10:36 ` [PATCH v21 " Harald Nordgren via GitGitGadget
2026-07-24 10:36 ` [PATCH v21 1/7] branch: add --forked filter for --list mode Harald Nordgren via GitGitGadget
2026-07-24 10:36 ` [PATCH v21 2/7] branch: convert delete_branches() to a flags argument Harald Nordgren via GitGitGadget
2026-07-24 10:36 ` [PATCH v21 3/7] branch: let delete_branches skip unmerged branches on bulk refusal Harald Nordgren via GitGitGadget
2026-07-24 10:36 ` [PATCH v21 4/7] branch: prepare delete_branches for a bulk caller Harald Nordgren via GitGitGadget
2026-07-24 10:36 ` [PATCH v21 5/7] branch: add --delete-merged <branch> Harald Nordgren via GitGitGadget
2026-07-24 10:36 ` [PATCH v21 6/7] branch: add branch.<name>.deleteMerged opt-out Harald Nordgren via GitGitGadget
2026-07-24 10:36 ` [PATCH v21 7/7] branch: add --dry-run for --delete-merged Harald Nordgren via GitGitGadget
2026-07-24 15:43 ` [PATCH v21 0/7] branch: delete-merged Junio C Hamano
2026-07-24 19:29 ` [PATCH v22 " Harald Nordgren via GitGitGadget
2026-07-24 19:29 ` [PATCH v22 1/7] branch: add --forked filter for --list mode Harald Nordgren via GitGitGadget
2026-07-25 4:16 ` Junio C Hamano
2026-07-24 19:29 ` [PATCH v22 2/7] branch: convert delete_branches() to a flags argument Harald Nordgren via GitGitGadget
2026-07-24 19:29 ` [PATCH v22 3/7] branch: let delete_branches skip unmerged branches on bulk refusal Harald Nordgren via GitGitGadget
2026-07-24 19:29 ` [PATCH v22 4/7] branch: prepare delete_branches for a bulk caller Harald Nordgren via GitGitGadget
2026-07-24 19:29 ` [PATCH v22 5/7] branch: add --delete-merged <branch> Harald Nordgren via GitGitGadget
2026-07-24 19:29 ` [PATCH v22 6/7] branch: add branch.<name>.deleteMerged opt-out Harald Nordgren via GitGitGadget
2026-07-24 19:29 ` [PATCH v22 7/7] branch: add --dry-run for --delete-merged Harald Nordgren via GitGitGadget
2026-07-25 11:32 ` [PATCH v23 0/7] branch: delete-merged Harald Nordgren via GitGitGadget
2026-07-25 11:32 ` [PATCH v23 1/7] branch: add --forked filter for --list mode Harald Nordgren via GitGitGadget
2026-07-25 11:32 ` [PATCH v23 2/7] branch: convert delete_branches() to a flags argument Harald Nordgren via GitGitGadget
2026-07-25 11:32 ` [PATCH v23 3/7] branch: let delete_branches skip unmerged branches on bulk refusal Harald Nordgren via GitGitGadget
2026-07-25 11:32 ` [PATCH v23 4/7] branch: prepare delete_branches for a bulk caller Harald Nordgren via GitGitGadget
2026-07-25 11:32 ` [PATCH v23 5/7] branch: add --delete-merged <branch> Harald Nordgren via GitGitGadget
2026-07-29 15:10 ` Phillip Wood [this message]
2026-07-29 23:13 ` Harald Nordgren
2026-07-25 11:32 ` [PATCH v23 6/7] branch: add branch.<name>.deleteMerged opt-out Harald Nordgren via GitGitGadget
2026-07-25 11:32 ` [PATCH v23 7/7] branch: add --dry-run for --delete-merged Harald Nordgren via GitGitGadget
2026-07-25 15:13 ` [PATCH v23 0/7] branch: delete-merged Junio C Hamano
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1f282ad4-9937-4c95-89d4-70f7a1c883a8@gmail.com \
--to=phillip.wood123@gmail.com \
--cc=git@vger.kernel.org \
--cc=gitgitgadget@gmail.com \
--cc=haraldnordgren@gmail.com \
--cc=j6t@kdbg.org \
--cc=kristofferhaugsbakk@fastmail.com \
--cc=phillip.wood@dunelm.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox