@leroysheep / CrazyWebTemps / commits / 8ac53c6995

branch: add branch.<name>.deleteMerged opt-out

Setting branch.<name>.deleteMerged=false exempts that branch from "git branch --delete-merged", which is useful for a topic you want to keep developing after an early round of it has been merged upstream. Unless --quiet is given, each skip is reported so the user knows why their topic was kept. Explicit deletion with "git branch -d" still uses the normal merge check and ignores this setting. Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Harald Nordgren committed Jul 30, 2026 at 13:58 UTC 8ac53c69950fd4d0e57c1e86d022a4668966b18d
4 files changed +59 -1
Documentation/config/branch.adoc
+7
index a4db9fa5c8..d8483acb4f 100644 --- a/Documentation/config/branch.adoc +++ b/Documentation/config/branch.adoc @@ -102,3 +102,10 @@ for details). `git branch --edit-description`. Branch description is automatically added to the `format-patch` cover letter or `request-pull` summary. + +`branch.<name>.deleteMerged`:: + If set to `false`, branch _<name>_ is exempt from + `git branch --delete-merged`. Useful for a topic branch you + intend to develop further after an initial round has been + merged upstream. Defaults to true. Explicit deletion via + `git branch -d` is unaffected.
Documentation/git-branch.adoc
+2 -1
index 4766178204..cfaac4b90f 100644 --- a/Documentation/git-branch.adoc +++ b/Documentation/git-branch.adoc @@ -224,7 +224,8 @@ A branch is not deleted when: distinguished from a branch that just looks fully merged right after a pull; this is determined by the remote's configured push and fetch refspecs, -* it is the local upstream of a branch that is not being deleted. +* it is the local upstream of a branch that is not being deleted, or +* `branch.<name>.deleteMerged` is set to `false`. -- + When such a local upstream branch has its own upstream deleted by the
builtin/branch.c
+14
index f1a73bcea1..2d0c4f51ea 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -818,6 +818,7 @@ static int delete_merged_branches(const struct strvec *upstreams, struct strset deletable_branch_names = STRSET_INIT; struct strset protected_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; @@ -836,6 +837,7 @@ static int delete_merged_branches(const struct strvec *upstreams, 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); @@ -853,6 +855,17 @@ 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", 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); } @@ -873,6 +886,7 @@ static int delete_merged_branches(const struct strvec *upstreams, clear_deleted_upstreams(&protected_branch_names, &deletable_branch_names); + strbuf_release(&key); strvec_clear(&branches_to_delete); strset_clear(&protected_branch_names); strset_clear(&deletable_branch_names);
t/t3200-branch.sh
+36
index 322c5a20cc..50fd1d348c 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -2084,4 +2084,40 @@ 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_done