From: Elia Pinto <gitter.spiros@gmail.com>
To: git@vger.kernel.org
Cc: "Junio C Hamano" <gitster@pobox.com>,
"Harald Nordgren" <haraldnordgren@gmail.com>,
"Ævar Arnfjörð Bjarmason" <avarab@gmail.com>,
"Elia Pinto" <gitter.spiros@gmail.com>
Subject: [PATCH] status: reword message for a missing upstream branch
Date: Mon, 19 Oct 2026 19:57:22 +0200 [thread overview]
Message-ID: <20261019175722.5223-1-gitter.spiros@gmail.com> (raw)
Clone an empty repository, make a commit and run "git status":
$ git init --bare empty.git
$ git clone empty.git work && cd work
$ git commit --allow-empty -m initial
$ git status
On branch master
Your branch is based on 'origin/master', but the upstream is gone.
(use "git branch --unset-upstream" to fixup)
The upstream is not gone, it was never there: the remote is empty,
so origin/master has not been created yet. The advice is also not
what the user needs; "git push" creates origin/master and the
message goes away.
The message was added in f2e087395b (branch: report invalid tracking
branch as gone, 2013-08-26), assuming that a configured upstream that
cannot be resolved must have been deleted. But "git clone" of an empty
repository configures the upstream anyway, and Git has no way to tell
"deleted" from "not created yet".
Since we cannot tell the two cases apart, change the message for both,
including the "deleted" case that has been reported as "gone" since
2013, so that it is correct either way:
Your branch is set up to track 'origin/master', which does not exist.
(use "git push" to create it)
(use "git branch --unset-upstream" to stop tracking it)
Only suggest "git push" when it would actually create the upstream,
that is when the push destination of the branch (what "@{push}"
resolves to) is the upstream itself. This is not the case, for
example, with push.default set to "nothing", or when pushing to a
different remote (branch.<name>.pushRemote or remote.pushDefault).
It is also not the case with push.default set to "matching": it only
pushes branches that already exist on the remote, so it would not
create the missing upstream. In all these cases only suggest
"git branch --unset-upstream".
"git checkout" prints the same message, so it changes too. The
"[gone]" shown by "git status -sb", "git branch -vv" and
"%(upstream:track)" is not changed, as scripts may depend on it.
Signed-off-by: Elia Pinto <gitter.spiros@gmail.com>
---
This is an RFC mainly about the wording. The case of a freshly cloned
empty repository has been confusing users for a while, e.g.
https://stackoverflow.com/questions/24870145
I could not find a previous discussion of this case on the list.
Opinions on whether "status:" or "remote:" is the better area prefix
are also welcome, as "git checkout" shows the same message.
This is a resubmission of this patch, which I haven't seen in
'seen' or 'next' https://lore.kernel.org/git/20261009134004.188952-1-gitter.spiros@gmail.com/
Thanks
remote.c | 34 +++++++++--
t/t6040-tracking-info.sh | 120 ++++++++++++++++++++++++++++++++++++++-
2 files changed, 146 insertions(+), 8 deletions(-)
diff --git a/remote.c b/remote.c
index 71170f36a9..4ed4d1ee2f 100644
--- a/remote.c
+++ b/remote.c
@@ -2442,6 +2442,31 @@ static void format_branch_comparison(struct strbuf *sb,
}
}
+/*
+ * Would a plain "git push" create the remote-tracking ref "upstream"?
+ * Not with "matching", which only updates branches that already exist
+ * on the remote.
+ */
+static bool push_creates(const char *push_ref, const char *upstream)
+{
+ return push_ref && !strcmp(push_ref, upstream) &&
+ repo_config_values(the_repository)->push_default !=
+ PUSH_DEFAULT_MATCHING;
+}
+
+static void format_missing_upstream(struct strbuf *sb, const char *name,
+ bool suggest_push)
+{
+ strbuf_addf(sb, _("Your branch is set up to track '%s', "
+ "which does not exist.\n"), name);
+ if (!advice_enabled(ADVICE_STATUS_HINTS))
+ return;
+ if (suggest_push)
+ strbuf_addstr(sb, _(" (use \"git push\" to create it)\n"));
+ strbuf_addstr(sb, _(" (use \"git branch --unset-upstream\" "
+ "to stop tracking it)\n"));
+}
+
/*
* Return true when there is anything to report, otherwise false.
*/
@@ -2503,12 +2528,9 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
if (cmp < 0) {
if (is_upstream) {
- strbuf_addf(sb,
- _("Your branch is based on '%s', but the upstream is gone.\n"),
- short_ref);
- if (advice_enabled(ADVICE_STATUS_HINTS))
- strbuf_addstr(sb,
- _(" (use \"git branch --unset-upstream\" to fixup)\n"));
+ format_missing_upstream(sb, short_ref,
+ push_creates(push_ref,
+ full_ref));
reported = 1;
}
free(full_ref);
diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh
index e95d420972..39cb755ccc 100755
--- a/t/t6040-tracking-info.sh
+++ b/t/t6040-tracking-info.sh
@@ -96,7 +96,7 @@ test_expect_success 'checkout (upstream is gone)' '
cd test &&
git checkout b5
) >actual &&
- test_grep "is based on .*, but the upstream is gone." actual
+ test_grep "set up to track .*, which does not exist." actual
'
test_expect_success 'checkout (up-to-date with upstream)' '
@@ -123,7 +123,123 @@ test_expect_success 'status (upstream is gone)' '
# reports nothing to commit
test_must_fail git commit --dry-run
) >actual &&
- test_grep "is based on .*, but the upstream is gone." actual
+ test_grep "set up to track .*, which does not exist." actual
+'
+
+test_expect_success 'setup clone of an empty repository' '
+ git init --bare empty.git &&
+ git clone empty.git empty-clone &&
+ git -C empty-clone commit --allow-empty -m initial
+'
+
+test_expect_success 'status (upstream not yet pushed after empty clone)' '
+ cat >expect <<-EOF &&
+ On branch main
+ Your branch is set up to track ${SQ}origin/main${SQ}, which does not exist.
+ (use "git push" to create it)
+ (use "git branch --unset-upstream" to stop tracking it)
+
+ nothing to commit, working tree clean
+ EOF
+ git -C empty-clone status >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'checkout (upstream not yet pushed after empty clone)' '
+ test_when_finished "git -C empty-clone branch -D tmp" &&
+ cat >expect <<-EOF &&
+ Your branch is set up to track ${SQ}origin/main${SQ}, which does not exist.
+ (use "git push" to create it)
+ (use "git branch --unset-upstream" to stop tracking it)
+ EOF
+ git -C empty-clone checkout -b tmp &&
+ git -C empty-clone checkout main >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'status -s -b (upstream not yet pushed after empty clone)' '
+ echo "## main...origin/main [gone]" >expect &&
+ git -C empty-clone status -s -b >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'setup expected output without "git push" advice' '
+ cat >expect-no-push <<-EOF
+ On branch main
+ Your branch is set up to track ${SQ}origin/main${SQ}, which does not exist.
+ (use "git branch --unset-upstream" to stop tracking it)
+
+ nothing to commit, working tree clean
+ EOF
+'
+
+test_expect_success 'status (missing upstream, push.default=nothing)' '
+ git -C empty-clone -c push.default=nothing status >actual &&
+ test_cmp expect-no-push actual
+'
+
+# "matching" only updates branches that already exist on the remote
+test_expect_success 'status (missing upstream, push.default=matching)' '
+ git -C empty-clone -c push.default=matching status >actual &&
+ test_cmp expect-no-push actual
+'
+
+test_expect_success 'status (missing upstream, pushRemote is another remote)' '
+ git init --bare fork.git &&
+ git -C empty-clone remote add fork ../fork.git &&
+ git -C empty-clone -c branch.main.pushRemote=fork status >actual &&
+ test_cmp expect-no-push actual
+'
+
+test_expect_success 'status (missing upstream, advice.statusHints=false)' '
+ cat >expect <<-EOF &&
+ On branch main
+ Your branch is set up to track ${SQ}origin/main${SQ}, which does not exist.
+
+ nothing to commit, working tree clean
+ EOF
+ git -C empty-clone -c advice.statusHints=false status >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'setup upstream deleted on the remote and pruned' '
+ git init --bare pruned.git &&
+ git clone pruned.git pruned-clone &&
+ (
+ cd pruned-clone &&
+ git commit --allow-empty -m initial &&
+ git push origin main &&
+ git checkout -b topic &&
+ git push -u origin topic
+ ) &&
+ git -C pruned.git branch -D topic &&
+ git -C pruned-clone fetch --prune
+'
+
+test_expect_success 'status (upstream deleted on the remote and pruned)' '
+ cat >expect <<-EOF &&
+ On branch topic
+ Your branch is set up to track ${SQ}origin/topic${SQ}, which does not exist.
+ (use "git push" to create it)
+ (use "git branch --unset-upstream" to stop tracking it)
+
+ nothing to commit, working tree clean
+ EOF
+ git -C pruned-clone status >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'short formats still show "gone" for a deleted upstream' '
+ echo "## topic...origin/topic [gone]" >expect &&
+ git -C pruned-clone status -s -b >actual &&
+ test_cmp expect actual &&
+ git -C pruned-clone branch -vv >actual &&
+ test_grep "^\* topic .* \[origin/topic: gone\] initial$" actual &&
+ echo "topic [gone]" >expect &&
+ git -C pruned-clone for-each-ref \
+ --format="%(refname:short) %(upstream:track)" \
+ refs/heads/topic >actual &&
+ test_cmp expect actual
'
test_expect_success 'status (up-to-date with upstream)' '
--
2.53.0
next reply other threads:[~2026-10-19 17:57 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-19 17:57 Elia Pinto [this message]
2026-10-19 18:04 ` [PATCH] status: reword message for a missing upstream branch Harald Nordgren
2026-10-19 21:42 ` Junio C Hamano
2026-10-20 7:29 ` Elia Pinto
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=20261019175722.5223-1-gitter.spiros@gmail.com \
--to=gitter.spiros@gmail.com \
--cc=avarab@gmail.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--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