From: "Harald Nordgren via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: Kristoffer Haugsbakk <kristofferhaugsbakk@fastmail.com>,
Johannes Sixt <j6t@kdbg.org>,
Phillip Wood <phillip.wood123@gmail.com>,
Harald Nordgren <haraldnordgren@gmail.com>
Subject: [PATCH v20 0/7] branch: delete-merged
Date: Wed, 22 Jul 2026 07:10:31 +0000 [thread overview]
Message-ID: <pull.2285.v20.git.git.1784704238.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.2285.v19.git.git.1784053493.gitgitgadget@gmail.com>
Delete branches that have already been merged on upstream.
Changes in v20:
* Protect branches transitively required by a surviving local upstream
stack. Traverse upstream chains once and defer delete-set mutation until
traversal completes.
* Make stacked-branch handling independent of ref iteration order and
update the documentation accordingly.
* Clarify variable names with regards to branch names (short) to reduce
confusion.
Changes in v19:
* Fix bug where dry-run would still remove config, added test coverage.
* Redesigned --delete-merged as a repeatable upstream selector with
optional positional patterns limiting deletion scope.
* Protect same-name upstream branches independently of push-default
configuration.
* Simplified flags handling where local caching became complicated when
mutating values.
* Clarified assertions in tests.
Changes in v18:
* Instead of keeping the whole chain of upstream branches, keep only the
ones an unmerged branch still needs. When a kept (merged) branch in turn
tracks a branch that is being deleted, clear its now-stale upstream
config.
* Rework spare_stacked_bases() to record the kept bases and, in a second
pass, clear the upstream of any whose own base is going away. Build the
to-delete list with strset_for_each_entry() instead of re-walking the
candidate array.
Changes in v17:
* Keep a merged branch when another surviving branch still tracks it as its
upstream, so --delete-merged no longer deletes a branch out from under
one stacked on top of it.
* Move the --dry-run and branch.<name>.deleteMerged opt-out fully into
their own commits.
Changes in v16:
* Convert delete_merged_branches() to take an unsigned int flags argument
instead of separate quiet/dry_run booleans, matching delete_branches()
* Reuse the strbuf across the skip-config loop (strbuf_reset per iteration,
single strbuf_release after) instead of allocating and freeing it each
time
* Rewrite the --delete-merged tests as integration tests: branches that
land commits upstream, with deletion and the checked-out, upstream-gone,
and push-equals-upstream safety cases exercised together in one run and
output asserted via test_cmp
* Collapse the many per-aspect test repos into a single reused repo set up
by a setup_repo_for_delete_merged helper, and rename helpers off the old
pm_/prune naming
* Nest single-repo setup sequences in ( cd ... ) subshells instead of
prefixing every command with -C
Changes in v15:
* Renamed --prune-merged to --delete-merged throughout. Not necessarily
final, but something to advance the discussion.
* --delete-merged now silently skips not-yet-merged branches instead of
warning.
* Initialized the delete_branches() flag locals where declared. Only force
stays deferred.
* delete_branches()/check_branch_commit() doc and code cleanups: redundant
branch NULL checks dropped, ref_array candidates = { 0 }, a BUG() for the
unreachable non-branch ref, and reworked --delete-merged doc wording.
* Broadened the --forked tests (local commits for realism, remote add -f,
--forked coverage), renamed the misleading trunk fixture, and replaced
the misnamed detached branch with git checkout --detach.
Changes in v14:
* Fixed a git branch -d -r regression (broke t5404/t5505/t5514): the
remotes path set a local force but not the DELETE_BRANCH_FORCE bit that
check_branch_commit() reads, so it wrongly ran the merge check.
* Made flags the single source of truth in delete_branches() so the bit and
the derived locals can't disagree.
* Works locally, but GitHub CI has problems that are there for other
branches too, hopefully not related
(https://github.com/git/git/pull/2285).
Changes in v13:
* Reworked --forked into a real ref-filter applied in apply_ref_filter()
instead of a post-pass, so non-matching branches are never allocated.
* Match exact --forked patterns on full refnames (only globs use the
abbreviated upstream), and dropped the old helper machinery, forward
declaration, and string_list in favor of a strvec.
* Replaced the boolean parameters of
delete_branches()/check_branch_commit() with a single unsigned int flags.
* --prune-merged now collects candidates via filter_refs() rather than its
own branch walk.
* --prune-merged now takes its patterns as positional arguments (e.g. git
branch --prune-merged origin/main 'feature*') instead of repeating the
option.
Changes in v12:
* Reworked --forked from a standalone action into a --list-mode filter.
* Switched --forked and --prune-merged to repeatable OPT_STRING_LIST
options.
* Dropped the bare-remote-name resolution for --forked, the argument is now
a ref or a glob.
Changes in v11:
* The flags now take a branch, not a remote. --forked and --prune-merged
accept a literal upstream short name like origin/main or a wildmatch
pattern like origin/. The old --all-remotes flag is gone, since origin/
covers that case.
* The prune guard now compares @{push} against @{upstream}. A branch is
spared when these are equal. That is the trunk like case, such as local
main tracking and pushing to origin/main, where "fully merged to
upstream" cannot be told apart from "just pulled". Only branches that
push somewhere other than their upstream, typically fork based topics,
are candidates. The earlier /HEAD by name guard that the reviewer
rejected is gone.
* New --dry-run for --prune-merged.
Changes in v10:
* --forked / --prune-merged now take a branch glob instead of a remote name
— origin, origin/*, origin/release-- all work. This replaces the
remote-only form and subsumes the old --all-remotes flag, which has been
dropped.
* New --dry-run for --prune-merged.
Changes in v9:
* --force no longer has special meaning with --prune-merged; reachability
is always enforced. Use git branch -D to delete an unmerged branch.
Matches how git branch's other read/safe actions treat --force.
* Synopsis drops [-f]; "not fully merged" hint points at git branch -D.
* Dropped the --prune-merged --force tests.
Changes in v8:
* Delete only when the branch's work is actually reachable from its
upstream
* Skip branches whose upstream is gone (even with --force)
* Simplified the internal safety flag to live in one place
Changes in v7:
* --prune-merged now checks if a branch is merged into its own upstream
first. If the upstream is gone, it checks against the remote's default
branch instead. If neither exists, the branch is refused (use --force to
delete anyway).
Changes in v6:
* --prune-merged now measures merged-ness against the remote's default
branch instead of the candidate's upstream — so the decision no longer
depends on which branch happens to be checked out locally.
* delete_branches() / check_branch_commit() gained a per-candidate override
that lets a caller substitute a different "what counts as merged"
reference (or skip the check). branch -d callers pass NULL and keep their
existing semantics.
* prune_merged_branches() resolves each candidate's push-remote HEAD and
threads it through, so --prune-merged --all-remotes measures each
candidate against its own remote rather than a single global reference.
Changes in v5:
* Drop commit 'fetch: add --prune-merged'
Changes in v4:
* Resolve each remote's HEAD and collect the targets into a
protected_default_refs set in collect_forked_set.
* In prune_merged_branches, skip a candidate when its upstream is a
protected default ref and the local branch name matches the default
branch's leaf name (so a local main tracking origin/main is spared, but a
renamed trunk tracking origin/main is not).
* Also skip when the candidate's push ref points at a protected default
ref, so a topic branch configured to push to origin/main is never pruned.
* Tests: spare the local default branch; only protect by matching leaf name
(not by upstream alone); spare a branch whose push ref is the remote
default.
Changes in v3:
* s/remote-tracking refs/remote-tracking branches/g
Changes in v2:
* The whole feature moved out of git fetch and into git branch. git fetch
--prune-merged now just calls git branch --prune-merged after fetching.
* The fetch.pruneLocalBranches and remote..pruneLocalBranches config
options are gone, replaced by per-branch opt-out via branch..pruneMerged.
* New git branch --forked lists local branches whose upstream lives on the
given remote (read-only building block).
* New git branch --prune-merged deletes those branches, but only if their
tip is reachable from the upstream tracking ref; --force skips that
safety check.
* New git branch --all-remotes lets --forked/--prune-merged operate across
every configured remote at once.
* The currently checked-out branch in any worktree is always preserved.
* branch..pruneMerged=false lets you exempt a branch (e.g. a long-running
topic branch) even with --force; doesn't affect explicit git branch -d.
* delete_branches() got a warn_only mode so bulk deletion prints a one-line
warning per skipped branch instead of the noisy four-line hint that git
branch -d shows.
* New section in git-branch docs; git-fetch docs trimmed to just mention
--prune-merged.
* New tests in t3200-branch.sh for the new branch flags; t5510-fetch.sh
shrunk since most logic moved.
Harald Nordgren (7):
branch: add --forked filter for --list mode
branch: convert delete_branches() to a flags argument
branch: let delete_branches skip unmerged branches on bulk refusal
branch: prepare delete_branches for a bulk caller
branch: add --delete-merged <branch>
branch: add branch.<name>.deleteMerged opt-out
branch: add --dry-run for --delete-merged
Documentation/config/branch.adoc | 7 +
Documentation/git-branch.adoc | 49 +++-
builtin/branch.c | 276 +++++++++++++++++++---
ref-filter.c | 70 ++++++
ref-filter.h | 10 +
t/t3200-branch.sh | 393 +++++++++++++++++++++++++++++++
6 files changed, 774 insertions(+), 31 deletions(-)
base-commit: 5d2e7709234afea1b6ddb25cd4f60d3d5fb3c200
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2285%2FHaraldNordgren%2Ffetch-prune-local-branches-v20
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2285/HaraldNordgren/fetch-prune-local-branches-v20
Pull-Request: https://github.com/git/git/pull/2285
Range-diff vs v19:
1: 562648132d = 1: 51c1c9d075 branch: add --forked filter for --list mode
2: c7ebd9344c = 2: 711574b2e5 branch: convert delete_branches() to a flags argument
3: 0c4f3358e3 = 3: 47c5975dc7 branch: let delete_branches skip unmerged branches on bulk refusal
4: 64a202526a = 4: 46268acec5 branch: prepare delete_branches for a bulk caller
5: a6caa5b397 ! 5: ef9f57e735 branch: add --delete-merged <branch>
@@ Commit message
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 another, surviving branch tracks as its upstream is
- also kept, so a branch is never deleted out from under one stacked
- on top of it. Such a kept branch is itself merged, so when its own
- upstream is being deleted, clear its now-stale upstream config.
+ 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. 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: This option is only applicable in non-verbose mod
+silently skipped. Delete it with `git branch -D` if you want to
+remove it anyway.
++
-+A branch that another, surviving branch tracks as its upstream is
-+kept, so a branch is never deleted out from under one stacked on top
-+of it. If that kept branch in turn tracks a branch that is being
-+deleted, its now-stale upstream configuration is cleared.
++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`::
@@ builtin/branch.c: static int parse_opt_forked(const struct option *opt, const ch
return 0;
}
-+struct spare_data {
-+ struct strset *deletable;
-+ struct strset *spared;
++struct stacked_branch_data {
++ struct strset *deletable_branch_names;
++ struct strset *protected_branch_names;
++ struct strset *visited_branch_names;
+};
+
-+/*
-+ * A surviving branch stacked on a deletion candidate would lose its
-+ * upstream, so drop that candidate from the delete set and remember it
-+ * in "spared" so its own upstream can be tidied up afterwards.
-+ */
-+static int spare_stacked_base(const struct reference *ref, void *cb_data)
++static int collect_stacked_branch_bases(const struct reference *ref,
++ void *cb_data)
+{
-+ struct spare_data *data = cb_data;
-+ struct branch *branch;
-+ const char *upstream, *up_short;
++ struct stacked_branch_data *data = cb_data;
++ const char *branch_name;
+
-+ if (strset_contains(data->deletable, ref->name))
-+ return 0;
-+ branch = branch_get(ref->name);
-+ upstream = branch_get_upstream(branch, NULL);
-+ if (!upstream || !skip_prefix(upstream, "refs/heads/", &up_short) ||
-+ !strset_contains(data->deletable, up_short))
++ 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;
+
-+ strset_remove(data->deletable, up_short);
-+ strset_add(data->spared, up_short);
++ 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;
+}
+
-+/*
-+ * Keep any branch that a surviving branch tracks as its upstream, so we
-+ * never delete a branch out from under one stacked on top of it. Such a
-+ * base is itself merged, so when its own upstream is also going away
-+ * (no surviving branch tracks it), clear the base's now-stale upstream.
-+ */
-+static void spare_stacked_bases(struct ref_store *refs, struct strset *deletable)
++static void protect_stacked_branch_bases(struct ref_store *refs,
++ struct strset *deletable_branch_names)
+{
-+ struct strset spared = STRSET_INIT;
-+ struct spare_data data = { .deletable = deletable, .spared = &spared };
-+ struct strbuf key = STRBUF_INIT;
++ 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_branch_ref(refs, spare_stacked_base, &data);
-+
-+ strset_for_each_entry(&spared, &iter, entry) {
-+ struct branch *branch = branch_get(entry->key);
-+ const char *upstream = branch_get_upstream(branch, NULL);
-+ const char *up_short;
-+
-+ if (!upstream || !skip_prefix(upstream, "refs/heads/", &up_short) ||
-+ !strset_contains(deletable, up_short))
-+ continue;
++ refs_for_each_ref_ext(refs, collect_stacked_branch_bases, &data, &opts);
+
-+ strbuf_reset(&key);
-+ strbuf_addf(&key, "branch.%s.merge", branch->name);
-+ repo_config_set_gently(the_repository, key.buf, NULL);
-+ strbuf_reset(&key);
-+ strbuf_addf(&key, "branch.%s.remote", branch->name);
-+ repo_config_set_gently(the_repository, key.buf, NULL);
-+ }
++ strset_for_each_entry(&protected_branch_names, &iter, entry)
++ strset_remove(deletable_branch_names, entry->key);
+
-+ strbuf_release(&key);
-+ strset_clear(&spared);
++ strset_clear(&visited_branch_names);
++ strset_clear(&protected_branch_names);
+}
+
+static int branch_pushes_to_upstream(struct branch *branch,
@@ builtin/branch.c: static int parse_opt_forked(const struct option *opt, const ch
+ 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 = STRSET_INIT;
-+ struct strvec to_delete = STRVEC_INIT;
++ struct strset deletable_branch_names = STRSET_INIT;
++ struct strvec branches_to_delete = STRVEC_INIT;
+ struct hashmap_iter iter;
+ struct strmap_entry *entry;
+ size_t i;
@@ builtin/branch.c: static int parse_opt_forked(const struct option *opt, const ch
+ filter_refs(&candidates, &filter, filter.kind);
+
+ for (i = 0; i < (size_t)candidates.nr; i++) {
-+ const char *full_name = candidates.items[i]->refname;
-+ const char *short_name;
++ const char *branch_refname = candidates.items[i]->refname;
++ const char *branch_name;
+ struct branch *branch;
-+ const char *upstream;
++ const char *upstream_refname;
+
-+ if (!skip_prefix(full_name, "refs/heads/", &short_name))
-+ BUG("filter returned non-branch ref '%s'", full_name);
-+ if (branch_checked_out(full_name))
++ 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(short_name);
-+ upstream = branch_get_upstream(branch, NULL);
-+ if (!upstream || !refs_ref_exists(refs, upstream))
++ 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))
++ if (branch_pushes_to_upstream(branch, upstream_refname))
+ continue;
-+ if (check_branch_commit(short_name, short_name,
++ if (check_branch_commit(branch_name, branch_name,
+ &candidates.items[i]->objectname, NULL,
+ FILTER_REFS_BRANCHES, DELETE_BRANCH_SKIP_UNMERGED))
+ continue;
+
-+ strset_add(&deletable, short_name);
++ strset_add(&deletable_branch_names, branch_name);
+ }
+
-+ spare_stacked_bases(refs, &deletable);
++ protect_stacked_branch_bases(refs, &deletable_branch_names);
+
-+ strset_for_each_entry(&deletable, &iter, entry)
-+ strvec_push(&to_delete, entry->key);
++ strset_for_each_entry(&deletable_branch_names, &iter, entry)
++ strvec_push(&branches_to_delete, entry->key);
+
-+ if (to_delete.nr)
-+ ret = delete_branches(to_delete.nr, to_delete.v,
++ 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(&to_delete);
-+ strset_clear(&deletable);
++ strvec_clear(&branches_to_delete);
++ strset_clear(&deletable_branch_names);
+ ref_array_clear(&candidates);
+ ref_filter_clear(&filter);
+ return ret;
@@ t/t3200-branch.sh: test_expect_success '--forked requires a value' '
+ )
+'
+
-+test_expect_success '--delete-merged clears the deleted upstream of a spared branch' '
++test_expect_success '--delete-merged keeps the upstream chain of a surviving branch' '
+ setup_repo_for_delete_merged &&
+ (
+ cd repo &&
@@ t/t3200-branch.sh: test_expect_success '--forked requires a value' '
+ git commit --allow-empty -m "tip work" &&
+
+ git branch --delete-merged origin/next \
-+ --delete-merged lower &&
++ --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\\.(mid|tip)\\.(merge|remote)" >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
@@ t/t3200-branch.sh: test_expect_success '--forked requires a value' '
+ )
+'
+
++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
6: 734d27c908 ! 6: fa70108611 branch: add branch.<name>.deleteMerged opt-out
@@ Documentation/git-branch.adoc: A branch is not deleted when:
## builtin/branch.c ##
@@ builtin/branch.c: static int delete_merged_branches(const struct strvec *upstreams,
struct ref_array candidates = { 0 };
- struct strset deletable = STRSET_INIT;
- struct strvec to_delete = STRVEC_INIT;
+ 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;
size_t i;
@@ builtin/branch.c: static int delete_merged_branches(const struct strvec *upstreams,
- const char *short_name;
+ const char *branch_name;
struct branch *branch;
- const char *upstream;
+ const char *upstream_refname;
+ int opt_out;
- if (!skip_prefix(full_name, "refs/heads/", &short_name))
- BUG("filter returned non-branch ref '%s'", full_name);
+ if (!skip_prefix(branch_refname, "refs/heads/", &branch_name))
+ BUG("filter returned non-branch ref '%s'", branch_refname);
@@ builtin/branch.c: static int delete_merged_branches(const struct strvec *upstreams,
FILTER_REFS_BRANCHES, DELETE_BRANCH_SKIP_UNMERGED))
continue;
+ strbuf_reset(&key);
-+ strbuf_addf(&key, "branch.%s.deletemerged", short_name);
++ 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"),
-+ short_name, short_name);
++ branch_name, branch_name);
+ continue;
+ }
+
- strset_add(&deletable, short_name);
+ strset_add(&deletable_branch_names, branch_name);
}
@@ builtin/branch.c: static int delete_merged_branches(const struct strvec *upstreams,
@@ builtin/branch.c: static int delete_merged_branches(const struct strvec *upstrea
flags);
+ strbuf_release(&key);
- strvec_clear(&to_delete);
- strset_clear(&deletable);
+ strvec_clear(&branches_to_delete);
+ strset_clear(&deletable_branch_names);
ref_array_clear(&candidates);
## t/t3200-branch.sh ##
7: 7aa9d5db14 ! 7: 13bac431a3 branch: add --dry-run for --delete-merged
@@ Documentation/git-branch.adoc: git branch (-m|-M) [<old-branch>] <new-branch>
DESCRIPTION
-----------
-@@ Documentation/git-branch.adoc: kept, so a branch is never deleted out from under one stacked on top
- of it. If that kept branch in turn tracks a branch that is being
- deleted, its now-stale upstream configuration is cleared.
+@@ Documentation/git-branch.adoc: 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
@@ builtin/branch.c: static int delete_branches(int argc, const char **argv, int ki
char *refname = name + branch_name_pos;
if (!(flags & DELETE_BRANCH_QUIET))
printf(remote_branch
-@@ builtin/branch.c: static int spare_stacked_base(const struct reference *ref, void *cb_data)
- * base is itself merged, so when its own upstream is also going away
- * (no surviving branch tracks it), clear the base's now-stale upstream.
- */
--static void spare_stacked_bases(struct ref_store *refs, struct strset *deletable)
-+static void spare_stacked_bases(struct ref_store *refs, struct strset *deletable,
-+ unsigned int flags)
- {
- struct strset spared = STRSET_INIT;
- struct spare_data data = { .deletable = deletable, .spared = &spared };
-@@ builtin/branch.c: static void spare_stacked_bases(struct ref_store *refs, struct strset *deletable
-
- refs_for_each_branch_ref(refs, spare_stacked_base, &data);
-
-- strset_for_each_entry(&spared, &iter, entry) {
-- struct branch *branch = branch_get(entry->key);
-- const char *upstream = branch_get_upstream(branch, NULL);
-- const char *up_short;
-+ if (!(flags & DELETE_BRANCH_DRY_RUN)) {
-+ strset_for_each_entry(&spared, &iter, entry) {
-+ struct branch *branch = branch_get(entry->key);
-+ const char *upstream = branch_get_upstream(branch, NULL);
-+ const char *up_short;
-
-- if (!upstream || !skip_prefix(upstream, "refs/heads/", &up_short) ||
-- !strset_contains(deletable, up_short))
-- continue;
-+ if (!upstream || !skip_prefix(upstream, "refs/heads/", &up_short) ||
-+ !strset_contains(deletable, up_short))
-+ continue;
-
-- strbuf_reset(&key);
-- strbuf_addf(&key, "branch.%s.merge", branch->name);
-- repo_config_set_gently(the_repository, key.buf, NULL);
-- strbuf_reset(&key);
-- strbuf_addf(&key, "branch.%s.remote", branch->name);
-- repo_config_set_gently(the_repository, key.buf, NULL);
-+ strbuf_reset(&key);
-+ strbuf_addf(&key, "branch.%s.merge", branch->name);
-+ repo_config_set_gently(the_repository, key.buf, NULL);
-+ strbuf_reset(&key);
-+ strbuf_addf(&key, "branch.%s.remote", branch->name);
-+ repo_config_set_gently(the_repository, key.buf, NULL);
-+ }
- }
-
- strbuf_release(&key);
-@@ builtin/branch.c: static int delete_merged_branches(const struct strvec *upstreams,
- strset_add(&deletable, short_name);
- }
-
-- spare_stacked_bases(refs, &deletable);
-+ spare_stacked_bases(refs, &deletable, flags);
-
- strset_for_each_entry(&deletable, &iter, entry)
- strvec_push(&to_delete, entry->key);
@@ builtin/branch.c: int cmd_branch(int argc,
int delete = 0, rename = 0, copy = 0, list = 0,
unset_upstream = 0, show_current = 0, edit_description = 0;
@@ t/t3200-branch.sh: test_expect_success '--delete-merged keeps the upstream of a
check_branches <<-\EOF &&
feature
main
-@@ t/t3200-branch.sh: test_expect_success '--delete-merged clears the deleted upstream of a spared bra
+@@ t/t3200-branch.sh: test_expect_success '--delete-merged keeps the upstream chain of a surviving bra
git checkout -b tip mid --track &&
git commit --allow-empty -m "tip work" &&
+ git branch --dry-run --delete-merged origin/next \
-+ --delete-merged lower &&
++ --delete-merged lower >actual 2>&1 &&
++ test_must_be_empty actual &&
+
-+ git config --local --get-regexp "branch\\.(mid|tip)\\.(merge|remote)" >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 .
@@ t/t3200-branch.sh: test_expect_success '--delete-merged clears the deleted upstr
+ test_cmp expect actual &&
+
git branch --delete-merged origin/next \
- --delete-merged lower &&
-
+ --delete-merged lower >actual 2>&1 &&
+ test_must_be_empty actual &&
@@ t/t3200-branch.sh: test_expect_success "branch -d still deletes a deleteMerged=false branch" '
)
'
--
gitgitgadget
next prev parent reply other threads:[~2026-07-22 7:10 UTC|newest]
Thread overview: 239+ 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 ` Harald Nordgren via GitGitGadget [this message]
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
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=pull.2285.v20.git.git.1784704238.gitgitgadget@gmail.com \
--to=gitgitgadget@gmail.com \
--cc=git@vger.kernel.org \
--cc=haraldnordgren@gmail.com \
--cc=j6t@kdbg.org \
--cc=kristofferhaugsbakk@fastmail.com \
--cc=phillip.wood123@gmail.com \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.