Git development
 help / color / mirror / Atom feed
From: "Harald Nordgren via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: Harald Nordgren <haraldnordgren@gmail.com>,
	Harald Nordgren <haraldnordgren@gmail.com>
Subject: [PATCH v2 4/6] fetch: add --prune-merged
Date: Mon, 04 May 2026 18:27:28 +0000	[thread overview]
Message-ID: <e979fd238bdffd772783f47f42fdc6d8c2dc0205.1777919250.git.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.2285.v2.git.git.1777919250.gitgitgadget@gmail.com>

From: Harald Nordgren <haraldnordgren@gmail.com>

After a successful fetch from a configured remote, run
'git branch --prune-merged <remote>' to delete local branches
whose push destination ref has just been pruned.

Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
---
 Documentation/fetch-options.adoc |  8 ++++++++
 builtin/fetch.c                  | 20 ++++++++++++++++++++
 t/t5510-fetch.sh                 | 31 +++++++++++++++++++++++++++++++
 3 files changed, 59 insertions(+)

diff --git a/Documentation/fetch-options.adoc b/Documentation/fetch-options.adoc
index 81a9d7f9bb..d863a9184e 100644
--- a/Documentation/fetch-options.adoc
+++ b/Documentation/fetch-options.adoc
@@ -185,6 +185,14 @@ See the PRUNING section below for more details.
 +
 See the PRUNING section below for more details.
 
+`--prune-merged`::
+	After a successful fetch, run `git branch --prune-merged
+	<remote>` for the fetched remote, deleting local branches
+	that fork from this remote and whose tip is reachable from
+	their upstream remote-tracking ref. See linkgit:git-branch[1]
+	for the exact selection rules. The currently checked-out
+	branch is always preserved.
+
 endif::git-pull[]
 
 ifndef::git-pull[]
diff --git a/builtin/fetch.c b/builtin/fetch.c
index a22c319467..5451bf3b5b 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -82,6 +82,8 @@ static int prune = -1; /* unspecified */
 static int prune_tags = -1; /* unspecified */
 #define PRUNE_TAGS_BY_DEFAULT 0 /* do we prune tags by default? */
 
+static int prune_merged;
+
 static int append, dry_run, force, keep, update_head_ok;
 static int write_fetch_head = 1;
 static int verbosity, deepen_relative, set_upstream, refetch;
@@ -2189,6 +2191,8 @@ static void add_options_to_argv(struct strvec *argv,
 		strvec_push(argv, prune ? "--prune" : "--no-prune");
 	if (prune_tags != -1)
 		strvec_push(argv, prune_tags ? "--prune-tags" : "--no-prune-tags");
+	if (prune_merged)
+		strvec_push(argv, "--prune-merged");
 	if (update_head_ok)
 		strvec_push(argv, "--update-head-ok");
 	if (force)
@@ -2382,6 +2386,15 @@ static inline void fetch_one_setup_partial(struct remote *remote,
 	return;
 }
 
+static int prune_merged_for_remote(const struct remote *remote)
+{
+	struct child_process cmd = CHILD_PROCESS_INIT;
+
+	cmd.git_cmd = 1;
+	strvec_pushl(&cmd.args, "branch", "--prune-merged", remote->name, NULL);
+	return run_command(&cmd);
+}
+
 static int fetch_one(struct remote *remote, int argc, const char **argv,
 		     int prune_tags_ok, int use_stdin_refspecs,
 		     const struct fetch_config *config,
@@ -2457,6 +2470,11 @@ static int fetch_one(struct remote *remote, int argc, const char **argv,
 	refspec_clear(&rs);
 	transport_disconnect(gtransport);
 	gtransport = NULL;
+
+	if (!exit_code && prune_merged && remote_via_config &&
+	    prune_merged_for_remote(remote))
+		exit_code = 1;
+
 	return exit_code;
 }
 
@@ -2520,6 +2538,8 @@ int cmd_fetch(int argc,
 			 N_("prune remote-tracking branches no longer on remote")),
 		OPT_BOOL('P', "prune-tags", &prune_tags,
 			 N_("prune local tags no longer on remote and clobber changed tags")),
+		OPT_BOOL(0, "prune-merged", &prune_merged,
+			 N_("after pruning, also delete local branches forked from this remote whose tips are reachable from their upstream")),
 		OPT_CALLBACK_F(0, "recurse-submodules", &recurse_submodules_cli, N_("on-demand"),
 			    N_("control recursive fetching of submodules"),
 			    PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules),
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index 6fe21e2b3a..b94fd2bda0 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -386,6 +386,37 @@ test_expect_success REFFILES 'fetch --prune fails to delete branches' '
 	)
 '
 
+test_expect_success 'fetch --prune-merged: setup' '
+	git init -b main fetch-pm-parent &&
+	test_commit -C fetch-pm-parent base
+'
+
+test_expect_success 'fetch --prune-merged deletes merged local branches' '
+	test_when_finished "rm -rf fetch-pm-clone" &&
+	git -C fetch-pm-parent branch one base &&
+	git clone fetch-pm-parent fetch-pm-clone &&
+	git -C fetch-pm-clone branch one --track origin/one &&
+	git -C fetch-pm-parent branch -D one &&
+
+	git -C fetch-pm-clone fetch --prune --prune-merged origin &&
+
+	test_must_fail git -C fetch-pm-clone rev-parse --verify refs/heads/one
+'
+
+test_expect_success 'fetch --prune-merged skips unmerged local branches' '
+	test_when_finished "rm -rf fetch-pm-unmerged" &&
+	git -C fetch-pm-parent branch two base &&
+	git clone fetch-pm-parent fetch-pm-unmerged &&
+	git -C fetch-pm-unmerged checkout -b two --track origin/two &&
+	test_commit -C fetch-pm-unmerged unpushed &&
+	git -C fetch-pm-unmerged checkout - &&
+	git -C fetch-pm-parent branch -D two &&
+
+	git -C fetch-pm-unmerged fetch --prune --prune-merged origin 2>err &&
+	test_grep "not fully merged" err &&
+	git -C fetch-pm-unmerged rev-parse --verify refs/heads/two
+'
+
 test_expect_success 'fetch --atomic works with a single branch' '
 	test_when_finished "rm -rf atomic" &&
 
-- 
gitgitgadget


  parent reply	other threads:[~2026-05-04 18:27 UTC|newest]

Thread overview: 70+ 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   ` Harald Nordgren via GitGitGadget [this message]
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-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

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=e979fd238bdffd772783f47f42fdc6d8c2dc0205.1777919250.git.gitgitgadget@gmail.com \
    --to=gitgitgadget@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=haraldnordgren@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