Git development
 help / color / mirror / Atom feed
From: "Harald Nordgren via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: Kristoffer Haugsbakk <kristofferhaugsbakk@fastmail.com>,
	"D. Ben Knoble" <ben.knoble@gmail.com>,
	Phillip Wood <phillip.wood123@gmail.com>,
	Harald Nordgren <haraldnordgren@gmail.com>,
	Harald Nordgren <haraldnordgren@gmail.com>
Subject: [PATCH v2 2/2] branch: add --no-squashed to skip the squash search
Date: Sun, 11 Oct 2026 09:41:05 +0000	[thread overview]
Message-ID: <ec88bde25273700c70a547593ac8f68046218408.1791711665.git.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.2425.v2.git.git.1791711665.gitgitgadget@gmail.com>

From: Harald Nordgren <haraldnordgren@gmail.com>

Finding branches that were squash or rebase merged makes "git branch
--delete-merged" slower, and in a partial clone it may have to
download blobs. Users who do not need it have no way to skip that
search.

Add --no-squashed, which only deletes branches whose tip is reachable
from their upstream.

Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
---
 Documentation/git-branch.adoc |  8 +++++++-
 builtin/branch.c              | 12 ++++++++++--
 t/t3200-branch.sh             | 27 +++++++++++++++++++++++++++
 3 files changed, 44 insertions(+), 3 deletions(-)

diff --git a/Documentation/git-branch.adoc b/Documentation/git-branch.adoc
index 0427324de1..90960384c4 100644
--- a/Documentation/git-branch.adoc
+++ b/Documentation/git-branch.adoc
@@ -25,7 +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 [--dry-run] (--delete-merged <pattern>)... [<branch-pattern>...]
+git branch [--dry-run] [--no-squashed] (--delete-merged <pattern>)... [<branch-pattern>...]
 
 DESCRIPTION
 -----------
@@ -244,6 +244,12 @@ remove it anyway.
 	sanity-checking a wide pattern like `'origin/*'` before
 	committing to the deletion.
 
+`--no-squashed`::
+	With `--delete-merged`, only delete branches whose tip is
+	reachable from their upstream. This skips the search for
+	branches that were squash or rebase merged, which takes longer
+	and may have to download blobs in a partial clone.
+
 `-v`::
 `-vv`::
 `--verbose`::
diff --git a/builtin/branch.c b/builtin/branch.c
index 513c39763b..96f10a7546 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -209,6 +209,7 @@ enum delete_branch_flags {
 	DELETE_BRANCH_SKIP_UNMERGED = (1 << 2),
 	DELETE_BRANCH_NO_HEAD_FALLBACK = (1 << 3),
 	DELETE_BRANCH_DRY_RUN = (1 << 4),
+	DELETE_BRANCH_NO_SQUASHED = (1 << 5),
 };
 
 static int check_branch_commit(const char *branchname, const char *refname,
@@ -1149,7 +1150,8 @@ static int delete_merged_branches(const struct strvec *upstreams,
 			struct commit *upstream = lookup_commit_reference_by_name(
 				upstream_refname);
 
-			if (!rev || !upstream)
+			if ((flags & DELETE_BRANCH_NO_SQUASHED) ||
+			    !rev || !upstream)
 				continue;
 			ALLOC_GROW(searches, searches_nr + 1, searches_alloc);
 			searches[searches_nr++] = (struct landing_search) {
@@ -1272,6 +1274,7 @@ int cmd_branch(int argc,
 	    unset_upstream = 0, show_current = 0, edit_description = 0;
 	struct strvec delete_merged = STRVEC_INIT;
 	int dry_run = 0;
+	int no_squashed = 0;
 	const char *new_upstream = NULL;
 	int noncreate_actions = 0;
 	/* possible options */
@@ -1330,6 +1333,8 @@ int cmd_branch(int argc,
 			PARSE_OPT_NONEG, parse_opt_strvec),
 		OPT_BOOL(0, "dry-run", &dry_run,
 			N_("with --delete-merged, only print which branches would be deleted")),
+		OPT_BOOL(0, "no-squashed", &no_squashed,
+			N_("with --delete-merged, skip the search for squash merged branches")),
 		OPT__FORCE(&force, N_("force creation, move/rename, deletion"), PARSE_OPT_NOCOMPLETE),
 		OPT_MERGED(&filter, N_("print only branches that are merged")),
 		OPT_NO_MERGED(&filter, N_("print only branches that are not merged")),
@@ -1394,6 +1399,8 @@ int cmd_branch(int argc,
 
 	if (dry_run && !delete_merged.nr)
 		die(_("--dry-run requires --delete-merged"));
+	if (no_squashed && !delete_merged.nr)
+		die(_("--no-squashed requires --delete-merged"));
 
 	if (recurse_submodules_explicit) {
 		if (!submodule_propagate_branches)
@@ -1436,7 +1443,8 @@ int cmd_branch(int argc,
 	} else if (delete_merged.nr) {
 		ret = delete_merged_branches(&delete_merged, argv,
 					     (quiet ? DELETE_BRANCH_QUIET : 0) |
-					     (dry_run ? DELETE_BRANCH_DRY_RUN : 0));
+					     (dry_run ? DELETE_BRANCH_DRY_RUN : 0) |
+					     (no_squashed ? DELETE_BRANCH_NO_SQUASHED : 0));
 		goto out;
 	} else if (show_current) {
 		print_current_branch_name();
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index adf1bc2233..70a38663f1 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -2095,6 +2095,28 @@ test_expect_success '--delete-merged deletes a squash merged branch after upstre
 	)
 '
 
+test_expect_success '--delete-merged --no-squashed deletes only reachable branches' '
+	setup_repo_for_delete_merged &&
+	create_merged_branch merged &&
+	push_topic squash-merged squash-merged-work &&
+	squash_merge_upstream squash-merged &&
+	(
+		cd repo &&
+		git fetch origin &&
+		sha=$(git rev-parse --short merged) &&
+
+		git branch --delete-merged origin/next --no-squashed \
+			>actual 2>&1 &&
+		echo "Deleted branch merged (was $sha)." >expect &&
+		test_cmp expect actual &&
+
+		check_branches <<-\EOF
+		main
+		squash-merged
+		EOF
+	)
+'
+
 test_expect_success '--delete-merged reports a merge plainly next to an unmerged branch' '
 	setup_repo_for_delete_merged &&
 	push_topic a-unmerged unmerged-work &&
@@ -2316,6 +2338,11 @@ test_expect_success "branch -d still deletes a deleteMerged=false branch" '
 	)
 '
 
+test_expect_success '--no-squashed without --delete-merged is rejected' '
+	test_must_fail git -C forked branch --no-squashed 2>err &&
+	test_grep "requires --delete-merged" err
+'
+
 test_expect_success '--dry-run without --delete-merged is rejected' '
 	test_must_fail git -C forked branch --dry-run 2>err &&
 	test_grep "requires --delete-merged" err
-- 
gitgitgadget

      parent reply	other threads:[~2026-10-11  9:41 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-29  7:30 [PATCH] branch: let --delete-merged find squash merged branches Harald Nordgren via GitGitGadget
2026-09-29  7:46 ` Kristoffer Haugsbakk
2026-09-29  7:52   ` Harald Nordgren
2026-09-29  8:08     ` Kristoffer Haugsbakk
2026-09-29 11:18       ` D. Ben Knoble
2026-09-29 13:31         ` Kristoffer Haugsbakk
2026-09-29 11:26 ` D. Ben Knoble
2026-09-29 13:44   ` Harald Nordgren
2026-09-29 13:52   ` Harald Nordgren
2026-09-29 15:28     ` D. Ben Knoble
2026-09-29 18:34       ` Harald Nordgren
2026-10-04  9:54   ` Phillip Wood
2026-10-04 22:28     ` Harald Nordgren
2026-10-08 15:25       ` Phillip Wood
2026-10-08 18:42         ` Harald Nordgren
2026-10-09  8:16           ` Kristoffer Haugsbakk
2026-10-09 11:11             ` Harald Nordgren
2026-10-09 11:18           ` Harald Nordgren
2026-10-09 13:40             ` Phillip Wood
2026-10-09 13:37           ` Phillip Wood
2026-10-09 15:27             ` Harald Nordgren
2026-10-08 17:51     ` D. Ben Knoble
2026-10-09 13:34       ` Phillip Wood
2026-10-11  9:41 ` [PATCH v2 0/2] " Harald Nordgren via GitGitGadget
2026-10-11  9:41   ` [PATCH v2 1/2] " Harald Nordgren via GitGitGadget
2026-10-11  9:41   ` Harald Nordgren via GitGitGadget [this message]

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=ec88bde25273700c70a547593ac8f68046218408.1791711665.git.gitgitgadget@gmail.com \
    --to=gitgitgadget@gmail.com \
    --cc=ben.knoble@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=haraldnordgren@gmail.com \
    --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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox