* [PATCH 0/2] branch: -d protects upstream branches
@ 2026-08-25 21:25 Harald Nordgren via GitGitGadget
2026-08-25 21:25 ` [PATCH 1/2] branch: move stacked branch helpers Harald Nordgren via GitGitGadget
2026-08-25 21:25 ` [PATCH 2/2] branch: protect local upstreams from -d Harald Nordgren via GitGitGadget
0 siblings, 2 replies; 9+ messages in thread
From: Harald Nordgren via GitGitGadget @ 2026-08-25 21:25 UTC (permalink / raw)
To: git; +Cc: Harald Nordgren
Protect local branches from git branch -d when a surviving branch depends on
them through a local upstream chain.
Harald Nordgren (2):
branch: move stacked branch helpers
branch: protect local upstreams from -d
Documentation/git-branch.adoc | 4 +-
builtin/branch.c | 142 +++++++++++++++++++++-------------
t/t1507-rev-parse-upstream.sh | 4 +-
t/t3200-branch.sh | 43 ++++++++++
t/t6040-tracking-info.sh | 2 +-
5 files changed, 137 insertions(+), 58 deletions(-)
base-commit: 2c3adbb2c475981e340c79fdc5e7f4f9b5d9054e
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2365%2FHaraldNordgren%2Fbranch-d-protect-stacked-upstreams-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2365/HaraldNordgren/branch-d-protect-stacked-upstreams-v1
Pull-Request: https://github.com/git/git/pull/2365
--
gitgitgadget
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 1/2] branch: move stacked branch helpers
2026-08-25 21:25 [PATCH 0/2] branch: -d protects upstream branches Harald Nordgren via GitGitGadget
@ 2026-08-25 21:25 ` Harald Nordgren via GitGitGadget
2026-08-25 21:25 ` [PATCH 2/2] branch: protect local upstreams from -d Harald Nordgren via GitGitGadget
1 sibling, 0 replies; 9+ messages in thread
From: Harald Nordgren via GitGitGadget @ 2026-08-25 21:25 UTC (permalink / raw)
To: git; +Cc: Harald Nordgren, Harald Nordgren
From: Harald Nordgren <haraldnordgren@gmail.com>
Move the stacked branch helpers earlier so delete_branches() can use
them without a forward declaration.
Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
---
builtin/branch.c | 104 +++++++++++++++++++++++------------------------
1 file changed, 52 insertions(+), 52 deletions(-)
diff --git a/builtin/branch.c b/builtin/branch.c
index a613148fc7..87f0aa4051 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -203,6 +203,58 @@ enum delete_branch_flags {
DELETE_BRANCH_DRY_RUN = (1 << 4),
};
+struct stacked_branch_data {
+ struct strset *deletable_branch_names;
+ struct strset *protected_branch_names;
+};
+
+static int collect_stacked_branch_base(const struct reference *ref,
+ void *cb_data)
+{
+ struct stacked_branch_data *data = cb_data;
+ const char *branch_name;
+ struct branch *branch;
+ const char *upstream_refname;
+ const char *upstream_branch_name;
+
+ if (!skip_prefix(ref->name, "refs/heads/", &branch_name))
+ BUG("expected local branch ref, got '%s'", ref->name);
+ if (strset_contains(data->deletable_branch_names, branch_name))
+ return 0;
+
+ branch = branch_get(branch_name);
+ upstream_refname = branch_get_upstream(branch, NULL);
+ if (!upstream_refname ||
+ !skip_prefix(upstream_refname, "refs/heads/",
+ &upstream_branch_name) ||
+ !strset_contains(data->deletable_branch_names,
+ upstream_branch_name))
+ return 0;
+
+ strset_add(data->protected_branch_names, upstream_branch_name);
+ return 0;
+}
+
+static void protect_stacked_branch_bases(struct ref_store *refs,
+ struct strset *deletable_branch_names,
+ struct strset *protected_branch_names)
+{
+ struct stacked_branch_data data = {
+ .deletable_branch_names = deletable_branch_names,
+ .protected_branch_names = protected_branch_names,
+ };
+ struct refs_for_each_ref_options opts = {
+ .prefix = "refs/heads/",
+ };
+ struct hashmap_iter iter;
+ struct strmap_entry *entry;
+
+ refs_for_each_ref_ext(refs, collect_stacked_branch_base, &data, &opts);
+
+ strset_for_each_entry(protected_branch_names, &iter, entry)
+ strset_remove(deletable_branch_names, entry->key);
+}
+
static int check_branch_commit(const char *branchname, const char *refname,
const struct object_id *oid, struct commit *head_rev,
int kinds, unsigned int flags)
@@ -718,58 +770,6 @@ static int parse_opt_forked(const struct option *opt, const char *arg, int unset
return 0;
}
-struct stacked_branch_data {
- struct strset *deletable_branch_names;
- struct strset *protected_branch_names;
-};
-
-static int collect_stacked_branch_base(const struct reference *ref,
- void *cb_data)
-{
- struct stacked_branch_data *data = cb_data;
- const char *branch_name;
- struct branch *branch;
- const char *upstream_refname;
- const char *upstream_branch_name;
-
- if (!skip_prefix(ref->name, "refs/heads/", &branch_name))
- BUG("expected local branch ref, got '%s'", ref->name);
- if (strset_contains(data->deletable_branch_names, branch_name))
- return 0;
-
- branch = branch_get(branch_name);
- upstream_refname = branch_get_upstream(branch, NULL);
- if (!upstream_refname ||
- !skip_prefix(upstream_refname, "refs/heads/",
- &upstream_branch_name) ||
- !strset_contains(data->deletable_branch_names,
- upstream_branch_name))
- return 0;
-
- strset_add(data->protected_branch_names, upstream_branch_name);
- return 0;
-}
-
-static void protect_stacked_branch_bases(struct ref_store *refs,
- struct strset *deletable_branch_names,
- struct strset *protected_branch_names)
-{
- struct stacked_branch_data data = {
- .deletable_branch_names = deletable_branch_names,
- .protected_branch_names = protected_branch_names,
- };
- struct refs_for_each_ref_options opts = {
- .prefix = "refs/heads/",
- };
- struct hashmap_iter iter;
- struct strmap_entry *entry;
-
- refs_for_each_ref_ext(refs, collect_stacked_branch_base, &data, &opts);
-
- strset_for_each_entry(protected_branch_names, &iter, entry)
- strset_remove(deletable_branch_names, entry->key);
-}
-
static void clear_deleted_upstreams(struct strset *protected_branch_names,
struct strset *deletable_branch_names)
{
--
gitgitgadget
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 2/2] branch: protect local upstreams from -d
2026-08-25 21:25 [PATCH 0/2] branch: -d protects upstream branches Harald Nordgren via GitGitGadget
2026-08-25 21:25 ` [PATCH 1/2] branch: move stacked branch helpers Harald Nordgren via GitGitGadget
@ 2026-08-25 21:25 ` Harald Nordgren via GitGitGadget
2026-08-25 21:59 ` Junio C Hamano
2026-08-27 5:19 ` Elijah Newren
1 sibling, 2 replies; 9+ messages in thread
From: Harald Nordgren via GitGitGadget @ 2026-08-25 21:25 UTC (permalink / raw)
To: git; +Cc: Harald Nordgren, Harald Nordgren
From: Harald Nordgren <haraldnordgren@gmail.com>
A local branch may be fully merged into its own upstream while still
serving as the base of a surviving stacked branch. Deleting it with
"git branch -d" then leaves the surviving branch with a missing
upstream.
Use the existing stacked-branch protection after checking every
requested deletion. This makes multi-branch deletion independent of
argument order: a branch that fails its safety check remains available
to protect its upstream. Allow -D to override the protection, and allow
a complete stack to be deleted together.
Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
---
Documentation/git-branch.adoc | 4 +++-
builtin/branch.c | 38 +++++++++++++++++++++++++++++--
t/t1507-rev-parse-upstream.sh | 4 ++--
t/t3200-branch.sh | 43 +++++++++++++++++++++++++++++++++++
t/t6040-tracking-info.sh | 2 +-
5 files changed, 85 insertions(+), 6 deletions(-)
diff --git a/Documentation/git-branch.adoc b/Documentation/git-branch.adoc
index bfdf459329..5c2a3339b2 100644
--- a/Documentation/git-branch.adoc
+++ b/Documentation/git-branch.adoc
@@ -102,7 +102,9 @@ OPTIONS
`--delete`::
Delete a branch. The branch must be fully merged in its
upstream branch, or in `HEAD` if no upstream was set with
- `--track` or `--set-upstream-to`.
+ `--track` or `--set-upstream-to`, and must not be an upstream,
+ directly or indirectly, of another local branch that will remain
+ after the operation.
`-D`::
Shortcut for `--delete --force`.
diff --git a/builtin/branch.c b/builtin/branch.c
index 87f0aa4051..7f76789027 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -294,12 +294,13 @@ static int delete_branches(int argc, const char **argv, int kinds,
struct object_id oid;
char *name = NULL;
const char *fmt;
- int i;
int ret = 0;
int remote_branch = 0;
struct strbuf bname = STRBUF_INIT;
enum interpret_branch_kind allowed_interpret;
struct string_list refs_to_delete = STRING_LIST_INIT_DUP;
+ struct strset deletable_branch_names = STRSET_INIT;
+ struct strset protected_branch_names = STRSET_INIT;
struct string_list_item *item;
int branch_name_pos;
const char *fmt_remotes = "refs/remotes/%s";
@@ -326,7 +327,7 @@ static int delete_branches(int argc, const char **argv, int kinds,
!(flags & DELETE_BRANCH_NO_HEAD_FALLBACK))
head_rev = lookup_commit_reference(the_repository, &head_oid);
- for (i = 0; i < argc; i++, strbuf_reset(&bname)) {
+ for (int i = 0; i < argc; i++, strbuf_reset(&bname)) {
char *target = NULL;
int ref_flags = 0;
@@ -397,11 +398,42 @@ static int delete_branches(int argc, const char **argv, int kinds,
item->util = xstrdup((ref_flags & REF_ISBROKEN) ? "broken"
: (ref_flags & REF_ISSYMREF) ? target
: repo_find_unique_abbrev(the_repository, &oid, DEFAULT_ABBREV));
+ if (!remote_branch && !(flags & (DELETE_BRANCH_FORCE |
+ DELETE_BRANCH_SKIP_UNMERGED)))
+ strset_add(&deletable_branch_names, bname.buf);
next:
free(target);
}
+ if (!remote_branch &&
+ !(flags & (DELETE_BRANCH_FORCE | DELETE_BRANCH_SKIP_UNMERGED)) &&
+ refs_to_delete.nr) {
+ protect_stacked_branch_bases(get_main_ref_store(the_repository),
+ &deletable_branch_names, &protected_branch_names);
+ for (size_t i = refs_to_delete.nr; i; i--) {
+ const char *branch_name;
+
+ item = &refs_to_delete.items[i - 1];
+ if (!skip_prefix(item->string, "refs/heads/",
+ &branch_name))
+ BUG("expected local branch ref, got '%s'",
+ item->string);
+ if (strset_contains(&deletable_branch_names, branch_name))
+ continue;
+
+ error(_("the branch '%s' is an upstream of another branch"),
+ branch_name);
+ advise_if_enabled(ADVICE_FORCE_DELETE_BRANCH,
+ _("If you are sure you want to delete it, "
+ "run 'git branch -D %s'"),
+ branch_name);
+ ret = 1;
+ unsorted_string_list_delete_item(&refs_to_delete, i - 1,
+ 1);
+ }
+ }
+
if (!(flags & DELETE_BRANCH_DRY_RUN) &&
refs_delete_refs(get_main_ref_store(the_repository), NULL, &refs_to_delete, REF_NO_DEREF))
ret = 1;
@@ -428,6 +460,8 @@ static int delete_branches(int argc, const char **argv, int kinds,
free(describe_ref);
}
string_list_clear(&refs_to_delete, 0);
+ strset_clear(&deletable_branch_names);
+ strset_clear(&protected_branch_names);
free(name);
strbuf_release(&bname);
diff --git a/t/t1507-rev-parse-upstream.sh b/t/t1507-rev-parse-upstream.sh
index cb9ef7e329..04abfb6f94 100755
--- a/t/t1507-rev-parse-upstream.sh
+++ b/t/t1507-rev-parse-upstream.sh
@@ -146,9 +146,9 @@ test_expect_success 'merge my-side@{u} records the correct name' '
)
'
-test_expect_success 'branch -d other@{u}' '
+test_expect_success 'branch -D other@{u}' '
git checkout -t -b other main &&
- git branch -d @{u} &&
+ git branch -D @{u} &&
git for-each-ref refs/heads/main >actual &&
test_must_be_empty actual
'
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index cdb6c6a634..a3d492ffcd 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -2173,6 +2173,49 @@ test_expect_success "branch -d still deletes a deleteMerged=false branch" '
)
'
+test_expect_success 'branch -d keeps the upstream of a surviving branch' '
+ setup_repo_for_delete_merged &&
+ (
+ cd repo &&
+ git branch foundation origin/next --track &&
+ git checkout -b topic foundation --track &&
+ git commit --allow-empty -m "topic work" &&
+ git checkout --detach &&
+
+ test_must_fail git branch -d foundation 2>err &&
+ test_grep "branch .foundation. is an upstream of another branch" err &&
+ test_ref_exists refs/heads/foundation &&
+ test_ref_exists refs/heads/topic &&
+
+ git branch -D foundation &&
+ test_ref_missing refs/heads/foundation &&
+ test_ref_exists refs/heads/topic
+ )
+'
+
+test_expect_success 'branch -d protects a base when another deletion fails' '
+ setup_repo_for_delete_merged &&
+ (
+ cd repo &&
+ git branch foundation origin/next --track &&
+ git checkout -b topic foundation --track &&
+ git commit --allow-empty -m "topic work" &&
+ git checkout --detach &&
+
+ test_must_fail git branch -d foundation topic 2>err &&
+ test_grep "branch .foundation. is an upstream of another branch" err &&
+ test_grep "branch .topic. is not fully merged" err &&
+ test_ref_exists refs/heads/foundation &&
+ test_ref_exists refs/heads/topic &&
+
+ test_must_fail git branch -d topic foundation 2>err &&
+ test_grep "branch .foundation. is an upstream of another branch" err &&
+ test_grep "branch .topic. is not fully merged" err &&
+ test_ref_exists refs/heads/foundation &&
+ test_ref_exists refs/heads/topic
+ )
+'
+
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
diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh
index e95d420972..01145f6681 100755
--- a/t/t6040-tracking-info.sh
+++ b/t/t6040-tracking-info.sh
@@ -34,7 +34,7 @@ test_expect_success setup '
git checkout -b brokenbase origin &&
git checkout -b b5 --track brokenbase &&
advance g &&
- git branch -d brokenbase &&
+ git branch -D brokenbase &&
git checkout -b b6 origin
) &&
git checkout -b follower --track main &&
--
gitgitgadget
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH 2/2] branch: protect local upstreams from -d
2026-08-25 21:25 ` [PATCH 2/2] branch: protect local upstreams from -d Harald Nordgren via GitGitGadget
@ 2026-08-25 21:59 ` Junio C Hamano
2026-08-26 6:56 ` Harald Nordgren
2026-08-27 5:19 ` Elijah Newren
1 sibling, 1 reply; 9+ messages in thread
From: Junio C Hamano @ 2026-08-25 21:59 UTC (permalink / raw)
To: Harald Nordgren via GitGitGadget; +Cc: git, Harald Nordgren
"Harald Nordgren via GitGitGadget" <gitgitgadget@gmail.com> writes:
> From: Harald Nordgren <haraldnordgren@gmail.com>
>
> A local branch may be fully merged into its own upstream while still
> serving as the base of a surviving stacked branch. Deleting it with
> "git branch -d" then leaves the surviving branch with a missing
> upstream.
>
> Use the existing stacked-branch protection after checking every
> requested deletion. This makes multi-branch deletion independent of
> argument order: a branch that fails its safety check remains available
> to protect its upstream. Allow -D to override the protection, and allow
> a complete stack to be deleted together.
It may be a good thing to optionally be able to do this, but
changing the long-established semantics of what the '-d' option
means would lead to serious breakage to the end-user workflows
people depend on, I am afraid, and...
> -test_expect_success 'branch -d other@{u}' '
> +test_expect_success 'branch -D other@{u}' '
> git checkout -t -b other main &&
> - git branch -d @{u} &&
> + git branch -D @{u} &&
> git for-each-ref refs/heads/main >actual &&
> test_must_be_empty actual
> '
... having to adjust the test command sequence like this is a very
clear illustration of why it is not a safe thing to do. Our change
just broke what the user wanted to do, i.e., removing the branch
@{u}, which they have happily been doing with '-d' while guarded by
the original safety feature '-d' already had. Now they have to use
'-D' to remove it unconditionally without safety -- that is not
exactly progress. In addition, depending on the version of Git, our
change makes 'git branch -d' behave differently, making it less
predictable.
And no, a configuration variable to tweak the behaviour of '-d' is
unwelcome here; it would make the behavior of the command and the
option even less predictable.
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 2/2] branch: protect local upstreams from -d
2026-08-25 21:59 ` Junio C Hamano
@ 2026-08-26 6:56 ` Harald Nordgren
2026-08-26 8:46 ` Tuomas Ahola
2026-08-26 14:37 ` Junio C Hamano
0 siblings, 2 replies; 9+ messages in thread
From: Harald Nordgren @ 2026-08-26 6:56 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Harald Nordgren via GitGitGadget, git
Hmm, it makes sense what you are saying, but I could have sworn that
you asked for this (likely some very different version from this) when
I was working on delete-merged, i.e. to extend branch protections to
'-d' as well. Phillip brought it up recently as well. Maybe I
misunderstood.
Harald
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 2/2] branch: protect local upstreams from -d
2026-08-26 6:56 ` Harald Nordgren
@ 2026-08-26 8:46 ` Tuomas Ahola
2026-08-26 9:20 ` Harald Nordgren
2026-08-26 14:37 ` Junio C Hamano
1 sibling, 1 reply; 9+ messages in thread
From: Tuomas Ahola @ 2026-08-26 8:46 UTC (permalink / raw)
To: Harald Nordgren; +Cc: Junio C Hamano, Harald Nordgren via GitGitGadget, git
Harald Nordgren <haraldnordgren@gmail.com> wrote:
> Hmm, it makes sense what you are saying, but I could have sworn that
> you asked for this (likely some very different version from this) when
> I was working on delete-merged, i.e. to extend branch protections to
> '-d' as well. Phillip brought it up recently as well. Maybe I
> misunderstood.
>
>
> Harald
In <xmqq33yimsdp.fsf@gitster.g> Junio wrote:
} [...]
}
} Do we also need the same safety around "git branch -d feature1" by
} the way? The "-d" option with safety checks the same "is feature1
} already merged (to its upstream)?" condition, so it can protect the
} feature2 branch the same way, by saying either "oops, you cannot
} delete feature1 because you still have other branches like feature2
} that depend on it", or "ok, featur2 used to depend on feature1, but
} because we are deleting feature1 based on it being in origin/master,
} we will make feature2 depend on origin/master from now on".
}
Harald, perhaps that's the passage you thought of?
--
Tuomas
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 2/2] branch: protect local upstreams from -d
2026-08-26 8:46 ` Tuomas Ahola
@ 2026-08-26 9:20 ` Harald Nordgren
0 siblings, 0 replies; 9+ messages in thread
From: Harald Nordgren @ 2026-08-26 9:20 UTC (permalink / raw)
To: Tuomas Ahola; +Cc: Junio C Hamano, Harald Nordgren via GitGitGadget, git
> } Do we also need the same safety around "git branch -d feature1" by
> } the way? The "-d" option with safety checks the same "is feature1
> } already merged (to its upstream)?" condition, so it can protect the
> } feature2 branch the same way, by saying either "oops, you cannot
> } delete feature1 because you still have other branches like feature2
> } that depend on it", or "ok, featur2 used to depend on feature1, but
> } because we are deleting feature1 based on it being in origin/master,
> } we will make feature2 depend on origin/master from now on".
> }
>
> Harald, perhaps that's the passage you thought of?
Yes, exactly. Thanks for finding it!
Harald
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 2/2] branch: protect local upstreams from -d
2026-08-26 6:56 ` Harald Nordgren
2026-08-26 8:46 ` Tuomas Ahola
@ 2026-08-26 14:37 ` Junio C Hamano
1 sibling, 0 replies; 9+ messages in thread
From: Junio C Hamano @ 2026-08-26 14:37 UTC (permalink / raw)
To: Harald Nordgren; +Cc: Harald Nordgren via GitGitGadget, git
Harald Nordgren <haraldnordgren@gmail.com> writes:
> Hmm, it makes sense what you are saying, but I could have sworn that
> you asked for this (likely some very different version from this) when
> I was working on delete-merged, i.e. to extend branch protections to
> '-d' as well. Phillip brought it up recently as well. Maybe I
> misunderstood.
It is more likely that I misstated. I do appreciate that we now
have a machinery that allows us to offer an "improved" protection
feature that may be "better" than 'branch -d' to users. It is a
different matter in what shape we offer the feature while balancing
the need to avoid breaking established end-user workflow.
Thanks for working on this topic.
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 2/2] branch: protect local upstreams from -d
2026-08-25 21:25 ` [PATCH 2/2] branch: protect local upstreams from -d Harald Nordgren via GitGitGadget
2026-08-25 21:59 ` Junio C Hamano
@ 2026-08-27 5:19 ` Elijah Newren
1 sibling, 0 replies; 9+ messages in thread
From: Elijah Newren @ 2026-08-27 5:19 UTC (permalink / raw)
To: Harald Nordgren via GitGitGadget; +Cc: git, Harald Nordgren
On Tue, Aug 25, 2026 at 2:30 PM Harald Nordgren via GitGitGadget
<gitgitgadget@gmail.com> wrote:
>
> A local branch may be fully merged into its own upstream while still
> serving as the base of a surviving stacked branch. Deleting it with
> "git branch -d" then leaves the surviving branch with a missing
> upstream.
>
> Use the existing stacked-branch protection after checking every
> requested deletion. This makes multi-branch deletion independent of
> argument order: a branch that fails its safety check remains available
> to protect its upstream.
[...]
> diff --git a/Documentation/git-branch.adoc b/Documentation/git-branch.adoc
> index bfdf459329..5c2a3339b2 100644
> --- a/Documentation/git-branch.adoc
> +++ b/Documentation/git-branch.adoc
> @@ -102,7 +102,9 @@ OPTIONS
> `--delete`::
> Delete a branch. The branch must be fully merged in its
> upstream branch, or in `HEAD` if no upstream was set with
> - `--track` or `--set-upstream-to`.
> + `--track` or `--set-upstream-to`, and must not be an upstream,
> + directly or indirectly, of another local branch that will remain
> + after the operation.
With this patch applied:
$ git init -q repo && cd repo
$ git commit --allow-empty -m base
[master (root-commit) b9a0882] base
$ git branch A
$ git branch B
$ git branch C
$ git branch --set-upstream-to=A B
branch 'B' set up to track 'A'.
$ git branch --set-upstream-to=B C
branch 'C' set up to track 'B'.
$ ~/floss/git-review/bin-wrappers/git branch -d A B
error: the branch 'B' is an upstream of another branch
hint: If you are sure you want to delete it, run 'git branch -D B'
hint: Disable this message with "git config set advice.forceDeleteBranch false"
Deleted branch A (was b9a0882).
So, C had B as an upstream and git did protect B from being deleted.
That matches the claims above.
However, B had A as an upstream and git didn't protect A; it deleted
it. That doesn't match the claims above.
Verifying:
$ git config branch.B.merge
refs/heads/A
$ git rev-parse -q --verify refs/heads/A ; echo $?
1
$ git rev-parse -q --verify refs/heads/B ; echo $?
b9a088270710b2494f5fa0668fc7e81a40aebd35
0
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-08-27 5:20 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-25 21:25 [PATCH 0/2] branch: -d protects upstream branches Harald Nordgren via GitGitGadget
2026-08-25 21:25 ` [PATCH 1/2] branch: move stacked branch helpers Harald Nordgren via GitGitGadget
2026-08-25 21:25 ` [PATCH 2/2] branch: protect local upstreams from -d Harald Nordgren via GitGitGadget
2026-08-25 21:59 ` Junio C Hamano
2026-08-26 6:56 ` Harald Nordgren
2026-08-26 8:46 ` Tuomas Ahola
2026-08-26 9:20 ` Harald Nordgren
2026-08-26 14:37 ` Junio C Hamano
2026-08-27 5:19 ` Elijah Newren
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.