Subject: [PATCH v4 3/4] Use repo.settings.statusGoalBranch config for status comparison

From: "Harald Nordgren via GitGitGadget" <[email-masked]>
Date: Wed, 24 Dec 2025 10:19:46 +0000

From: Harald Nordgren <[email-masked]>

Replace hardcoded upstream/origin preference with configurable
repo.settings.statusGoalBranch setting. When unset, skip the
default branch comparison entirely.

Signed-off-by: Harald Nordgren <[email-masked]>
---
 remote.c                 | 48 ++++++++++++++++-----------
 t/t6040-tracking-info.sh | 71 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 100 insertions(+), 19 deletions(-)

diff --git a/remote.c b/remote.c
index f3831ef3be..edb6d374e7 100644
--- a/remote.c
+++ b/remote.c
@@ -2239,32 +2239,42 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
 
 static char *get_default_remote_ref(char **full_ref_out)
 {
-	int flag;
+	const char *config_value;
 	const char *resolved;
-	static const char *remotes[] = { "upstream", "origin", NULL };
-	int i;
+	int flag;
+	struct strbuf ref_buf = STRBUF_INIT;
+	char *slash_pos;
+	char *ret = NULL;
 
-	for (i = 0; remotes[i]; i++) {
-		struct strbuf head_ref = STRBUF_INIT;
-		strbuf_addf(&head_ref, "refs/remotes/%s/HEAD", remotes[i]);
+	if (repo_config_get_value(the_repository, "repo.settings.statusGoalBranch", &config_value))
+		return NULL;
 
-		resolved = refs_resolve_ref_unsafe(
-			get_main_ref_store(the_repository),
-			head_ref.buf,
-			RESOLVE_REF_READING,
-			NULL, &flag);
+	if (!config_value || !*config_value)
+		return NULL;
 
-		strbuf_release(&head_ref);
+	slash_pos = strchr(config_value, '/');
+	if (!slash_pos || slash_pos == config_value || !slash_pos[1])
+		return NULL;
 
-		if (resolved && (flag & REF_ISSYMREF)) {
-			if (full_ref_out)
-				*full_ref_out = xstrdup(resolved);
-			return refs_shorten_unambiguous_ref(
-				get_main_ref_store(the_repository), resolved, 0);
-		}
+	strbuf_addf(&ref_buf, "refs/remotes/%.*s/%s",
+		    (int)(slash_pos - config_value), config_value,
+		    slash_pos + 1);
+
+	resolved = refs_resolve_ref_unsafe(
+		get_main_ref_store(the_repository),
+		ref_buf.buf,
+		RESOLVE_REF_READING,
+		NULL, &flag);
+
+	if (resolved) {
+		if (full_ref_out)
+			*full_ref_out = xstrdup(resolved);
+		ret = refs_shorten_unambiguous_ref(
+			get_main_ref_store(the_repository), resolved, 0);
 	}
 
-	return NULL;
+	strbuf_release(&ref_buf);
+	return ret;
 }
 
 static void format_default_branch_comparison(struct strbuf *sb,
diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh
index e2bd48f858..00dadc03e7 100755
--- a/t/t6040-tracking-info.sh
+++ b/t/t6040-tracking-info.sh
@@ -309,6 +309,7 @@ test_expect_success 'status shows ahead of both tracked branch and origin/main'
 	(
 		cd test &&
 		git checkout work >/dev/null &&
+		git config repo.settings.statusGoalBranch origin/main &&
 		git status --long -b | head -5
 	) >actual &&
 	cat >expect <<-\EOF &&
@@ -325,6 +326,7 @@ test_expect_success 'checkout shows ahead of both tracked branch and origin/main
 	(
 		cd test &&
 		git checkout main >/dev/null &&
+		git config repo.settings.statusGoalBranch origin/main &&
 		git checkout work 2>&1 | grep -E "(Switched|Your branch|Ahead of)" | head -3
 	) >actual &&
 	cat >expect <<-\EOF &&
@@ -364,6 +366,7 @@ test_expect_success 'status shows ahead of tracked and diverged from origin/main
 	(
 		cd test &&
 		git checkout work2 >/dev/null &&
+		git config repo.settings.statusGoalBranch origin/main &&
 		git status --long -b | head -5
 	) >actual &&
 	cat >expect <<-\EOF &&
@@ -393,6 +396,7 @@ test_expect_success 'status shows diverged from tracked and behind origin/main'
 	(
 		cd test &&
 		git checkout work2b >/dev/null &&
+		git config repo.settings.statusGoalBranch origin/main &&
 		git status --long -b | head -6
 	) >actual &&
 	cat >expect <<-\EOF &&
@@ -425,6 +429,7 @@ test_expect_success 'status shows behind tracked and ahead of origin/main' '
 	(
 		cd test &&
 		git checkout work3 >/dev/null &&
+		git config repo.settings.statusGoalBranch origin/main &&
 		git status --long -b | head -5
 	) >actual &&
 	cat >expect <<-\EOF &&
@@ -450,6 +455,7 @@ test_expect_success 'status prefers upstream remote over origin for comparison'
 	(
 		cd test &&
 		git checkout work >/dev/null &&
+		git config repo.settings.statusGoalBranch upstream/main &&
 		git status --long -b | head -5
 	) >actual &&
 	cat >expect <<-\EOF &&
@@ -477,6 +483,7 @@ test_expect_success 'status shows up to date with tracked but diverged from defa
 	(
 		cd test &&
 		git checkout synced_feature >/dev/null &&
+		git config repo.settings.statusGoalBranch upstream/main &&
 		git status --long -b | head -4
 	) >actual &&
 	cat >expect <<-\EOF &&
@@ -504,6 +511,7 @@ test_expect_success 'status shows up to date with tracked but diverged from orig
 	(
 		cd test &&
 		git checkout synced_feature2 >/dev/null &&
+		git config repo.settings.statusGoalBranch origin/main &&
 		git status --long -b | head -4
 	) >actual &&
 	cat >expect <<-\EOF &&
@@ -527,6 +535,7 @@ test_expect_success 'status shows up to date with tracked but shows default bran
 	(
 		cd test &&
 		git checkout synced_feature3 >/dev/null &&
+		git config repo.settings.statusGoalBranch origin/main &&
 		git status --long -b | head -4
 	) >actual &&
 	cat >expect <<-\EOF &&
@@ -538,4 +547,66 @@ EOF
 	test_cmp expect actual
 '
 
+test_expect_success 'status with repo.settings.statusGoalBranch unset shows no default comparison' '
+	(
+		cd test &&
+		git checkout synced_feature3 >/dev/null &&
+		git config --unset repo.settings.statusGoalBranch 2>/dev/null || true &&
+		git status --long -b | head -3
+	) >actual &&
+	cat >expect <<-\EOF &&
+On branch synced_feature3
+Your branch is up to date with '\''origin/feature'\''.
+
+EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'status with repo.settings.statusGoalBranch set uses configured branch' '
+	(
+		cd test &&
+		git checkout synced_feature3 >/dev/null &&
+		git config repo.settings.statusGoalBranch origin/main &&
+		git status --long -b | head -4
+	) >actual &&
+	cat >expect <<-\EOF &&
+On branch synced_feature3
+Your branch is up to date with '\''origin/feature'\''.
+
+Diverged from '\''origin/main'\'' by 5 commits.
+EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'status with repo.settings.statusGoalBranch set to different remote/branch' '
+	(
+		cd test &&
+		git checkout work >/dev/null &&
+		git config repo.settings.statusGoalBranch origin/feature &&
+		git status --long -b | head -4
+	) >actual &&
+	cat >expect <<-\EOF &&
+On branch work
+Your branch is ahead of '\''origin/feature'\'' by 2 commits.
+  (use "git push" to publish your local commits)
+
+EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'status with repo.settings.statusGoalBranch set to non-existent branch' '
+	(
+		cd test &&
+		git checkout synced_feature3 >/dev/null &&
+		git config repo.settings.statusGoalBranch origin/nonexistent &&
+		git status --long -b | head -3
+	) >actual &&
+	cat >expect <<-\EOF &&
+On branch synced_feature3
+Your branch is up to date with '\''origin/feature'\''.
+
+EOF
+	test_cmp expect actual
+'
+
 test_done
-- 
gitgitgadget