* [RFC] branch: delete gone-upstream branches only when proven merged into a named ref
@ 2026-10-17 0:35 DonQuinleone
0 siblings, 0 replies; only message in thread
From: DonQuinleone @ 2026-10-17 0:35 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Harald Nordgren, Phillip Wood
Hi all,
This is my first time posting to the list, so good to meet everyone,
and thanks in advance for bearing with me.
I've just noticed that "git branch --delete-merged" is now available,
and that it never deletes a topic in the workflow that hosting
services (e.g. GitHub) encourage. Before writing a patch, I wanted to
ask how the list might consider closing that gap.
In that workflow, the topic tracks and pushes to a branch of its own
name; when the pull request is merged with a merge commit, the server
deletes that branch. With master as of c51408889:
$ git push -u origin topic
... merged on the server, and origin's topic deleted ...
$ git fetch origin
$ git branch --dry-run --delete-merged 'origin/*'
... spared, as pushing would update the upstream ...
$ git fetch --prune origin
$ git branch -vv
* master 61e9886 [origin/master: behind 2] base
topic 87d0c48 [origin/topic: gone] topic work
$ git branch --dry-run --delete-merged 'origin/*'
... skipped, because the upstream is gone ...
$ git merge-base --is-ancestor topic origin/master && echo merged
merged
So the branch is provably merged, but at no point in this workflow
will --delete-merged remove it. A script reproducing this is at the
bottom.
From reading back through the archives, this is deliberate. When v7
of the series fell back to the remote's default branch for a gone
upstream, Junio wrote [1]:
> If the upstream itself is gone, removing anything based on some other
> criteria cannot by definition a "safety check".
I agree that a vanished upstream proves nothing, so I would like to
propose separating selection from proof:
"Upstream gone" may select a candidate, but it is never proof. The
only proof is that the branch tip is reachable from a ref the user
names explicitly, so nothing is inferred: neither HEAD, nor the
remote's default branch.
I can see two possible shapes.
(A) A modifier on --delete-merged:
$ git branch --delete-merged 'origin/*' --into origin/master
Branches whose upstream matches the pattern but no longer exists would
become candidates, and would be deleted only if their tip is reachable
from the named commit. check_branch_commit() would be handed that
commit in place of HEAD, as suggested on v7 [1]. Branches whose
upstream still exists would be judged exactly as today, and the
worktree, stacked-branch and deleteMerged=false exclusions would still
apply.
(B) Composable filters on -d:
$ git branch --upstream-gone --merged=origin/master # preview
$ git branch -d --upstream-gone --merged=origin/master # delete
--upstream-gone would be useful on its own for listing (today the
usual approach is to grep "git branch -vv" for ": gone]"). But it
turns -d into a second bulk-delete mode beside --delete-merged, and
--merged defaults to HEAD when it comes last, so a forgotten argument
would quietly bring back HEAD as the proxy.
I lean towards (A) because it is small and keeps every existing
protection, but I'd rather settle the shape here than assume anything.
So:
1. Is "selection is not proof" an acceptable basis for touching
gone-upstream branches?
2. (A), (B), or something else? If (A), is there a better name than
--into (--merged-into?), and should it stay limited to branches
whose upstream is gone?
I would leave squashing and rebasing out of scope, in keeping with
--delete-merged staying "one stable and strict" operation [2]. I've
Cc'd Harald, as this relates closely to his in-flight --delete-merged
topics [3][4], and Phillip, who reviewed the series.
Thanks in advance. Look forward to hearing what people think.
~ Don
[1] https://lore.kernel.org/git/xmqq5x4sn393.fsf@gitster.g/
[2] https://lore.kernel.org/git/xmqqy0c21gq1.fsf@gitster.g/
[3] https://lore.kernel.org/git/pull.2428.v2.git.git.1791102886740.gitgitgadget@gmail.com/
[4] https://lore.kernel.org/git/94a6d23f8b96d4a07fb58c71f4a2f7dc4d3055bc.1791711665.git.gitgitgadget@gmail.com/
-- >8 --
#!/bin/sh
set -e
export GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_NOSYSTEM=1
export GIT_AUTHOR_NAME=A GIT_AUTHOR_EMAIL=a@example.com
export GIT_COMMITTER_NAME=C GIT_COMMITTER_EMAIL=c@example.com
cd "$(mktemp -d)"
git init -q --bare -b master origin.git
git clone -q origin.git work 2>/dev/null && cd work
git commit -q --allow-empty -m base && git push -q origin HEAD:master
git switch -q -c topic && git commit -q --allow-empty -m "topic work"
git push -q -u origin topic 2>/dev/null && git switch -q master
git clone -q ../origin.git ../server && (cd ../server &&
git merge -q --no-ff -m "Merge topic" origin/topic &&
git push -q origin master :topic)
echo "after fetch:"; git fetch -q
git branch --dry-run --delete-merged 'origin/*'
echo "after fetch --prune:"; git fetch -q --prune
git branch --dry-run --delete-merged 'origin/*'
git merge-base --is-ancestor topic origin/master && echo merged
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2026-10-17 0:41 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-17 0:35 [RFC] branch: delete gone-upstream branches only when proven merged into a named ref DonQuinleone
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox