From: Harald Nordgren via GitGitGadget Date: Fri, 30 Oct 2026 10:52:58 GMT Subject: [PATCH v5 0/4] branch: let --delete-merged find squash merged branches Message-ID: In-Reply-To: Branches merged on GitHub with "Squash and merge" or "Rebase and merge" are never deleted by git branch --delete-merged, because their tips are not reachable from the upstream. This treats such a branch as merged when some upstream commit contains all of its changes, and names that commit in the output: Deleted branch topic (was 1a2b3c4, landed as 9f8e7d6). Changes in v5: * git branch -h lists --delete-integrated in the usage lines * Giving both --delete-merged and --delete-integrated dies with a short "cannot be used together" message instead of the full usage text Changes in v4: * --delete-merged works as it does on master again: it only deletes branches whose tip is reachable from their upstream * New --delete-integrated also deletes branches that were rebase or squash merged, including those squashed after upstream changed the same files. It replaces --squashed * New prep commit lets delete_branches() take a map of where each branch landed, so "landed as" is printed there * Added Junio's test that --delete-merged keeps a branch when an unrelated upstream commit adds an identical file * --dry-run works with either option, and the two options can't be combined Changes in v3: * Split into multiple commits, one for rebase merges and one for squash merges. * By default a branch counts as merged when an upstream commit has its exact version of every file it changed. This never reads file contents, so it stays cheap in a partial clone. It finds rebase merges, and squash merges where upstream left those files alone. * Replace --no-squashed with an opt-in --squashed, which also finds branches merged after upstream changed the same files by merging those files. * Add a test for a rebase merge, and check that a squash onto changed files is kept without --squashed and deleted with it. Changes in v2: * Diff each upstream commit once and share the result between all branches, faster than v1. * Fix a branch merged with a merge commit being reported as "landed as" the merge when another branch was searched first, and add a test for it. Show the topic commit itself in "landed as" for work that arrived through a merge. * Use git merge --squash as the example in the commit message and mention GitHub's squash and rebase buttons. * Add --no-squashed to skip the squash search. Harald Nordgren (4): branch: move the deleteMerged opt-out check into a helper branch: let delete_branches() name where a branch landed branch: add --delete-integrated to delete rebase and squash merged branches branch: let --delete-integrated find branches merged onto changed files Documentation/config/branch.adoc | 8 +- Documentation/git-branch.adoc | 22 +- builtin/branch.c | 360 +++++++++++++++++++++++++++++-- t/t3200-branch.sh | 218 +++++++++++++++++++ 4 files changed, 578 insertions(+), 30 deletions(-) base-commit: 8d1b215c01512b9658d4525c54ff9b9245f9a7e8 Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2425%2FHaraldNordgren%2Fbranch-delete-squashed-v5 Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2425/HaraldNordgren/branch-delete-squashed-v5 Pull-Request: https://github.com/git/git/pull/2425 Range-diff vs v4: 1: 1d01085b75 = 1: 090aec5725 branch: move the deleteMerged opt-out check into a helper 2: 592562d7e7 = 2: 24180bf4a2 branch: let delete_branches() name where a branch landed 3: deb1e649cf ! 3: bf30f57ceb branch: add --delete-integrated to delete rebase and squash merged branches @@ builtin/branch.c static const char * const builtin_branch_usage[] = { N_("git branch [] [-r | -a] [--merged] [--no-merged] [(--forked )...]"), +@@ builtin/branch.c: static const char * const builtin_branch_usage[] = { + N_("git branch [] [-r | -a] [--format]"), + N_("git branch [] (--delete-merged )... " + "[...]"), ++ N_("git branch [] (--delete-integrated )... " ++ "[...]"), + NULL + }; + @@ builtin/branch.c: enum delete_branch_flags { DELETE_BRANCH_SKIP_UNMERGED = (1 << 2), DELETE_BRANCH_NO_HEAD_FALLBACK = (1 << 3), @@ builtin/branch.c: int cmd_branch(int argc, if (filter.with_commit || filter.no_commit || @@ builtin/branch.c: int cmd_branch(int argc, + filter.points_at.nr || filter.forked.nr) + list = 1; ++ die_for_incompatible_opt2(!!delete_merged.nr, "--delete-merged", ++ !!delete_integrated.nr, "--delete-integrated"); ++ noncreate_actions = !!delete + !!rename + !!copy + !!new_upstream + !!show_current + !!list + !!edit_description + - !!unset_upstream + !!delete_merged.nr; @@ t/t3200-branch.sh: test_expect_success '--dry-run without --delete-merged is rej +test_expect_success '--delete-integrated cannot be combined with --delete-merged' ' + test_must_fail git -C forked branch --delete-merged origin/next \ -+ --delete-integrated origin/next ++ --delete-integrated origin/next 2>err && ++ test_grep "cannot be used together" err +' + test_done 4: 19a9630c13 = 4: 9443055242 branch: let --delete-integrated find branches merged onto changed files -- gitgitgadget