Skip to content

Commit 2acdd07

Browse files
status: show default branch comparison when tracking non-default branch
When a branch tracks a non-default remote branch (e.g., origin/feature), git status now also displays how the branch compares to the default branch (origin/main or upstream/main). This helps users understand if their branch has drifted from the main development line even when it's in sync with its tracking branch. The comparison is shown as a separate line after the tracking branch status: - "Ahead of 'origin/main' by N commits" when purely ahead - "Behind 'origin/main' by N commits" when purely behind - "Diverged from 'origin/main' by N commits" when diverged The default branch is determined dynamically by checking: 1. refs/remotes/upstream/HEAD (if upstream remote exists) 2. refs/remotes/origin/HEAD (fallback) This works with any default branch name (main, master, develop, etc.) as long as the symbolic ref is configured. The comparison is also shown when the branch is up-to-date with its tracking branch but differs from the default branch. Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
1 parent c4a0c88 commit 2acdd07

File tree

2 files changed

+347
-0
lines changed

2 files changed

+347
-0
lines changed

remote.c

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2237,6 +2237,95 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
22372237
return stat_branch_pair(branch->refname, base, num_ours, num_theirs, abf);
22382238
}
22392239

2240+
static const char *get_default_remote_ref(char **full_ref_out)
2241+
{
2242+
int flag;
2243+
const char *resolved;
2244+
static const char *remotes[] = { "upstream", "origin", NULL };
2245+
int i;
2246+
2247+
for (i = 0; remotes[i]; i++) {
2248+
struct strbuf head_ref = STRBUF_INIT;
2249+
strbuf_addf(&head_ref, "refs/remotes/%s/HEAD", remotes[i]);
2250+
2251+
resolved = refs_resolve_ref_unsafe(
2252+
get_main_ref_store(the_repository),
2253+
head_ref.buf,
2254+
RESOLVE_REF_READING,
2255+
NULL, &flag);
2256+
2257+
strbuf_release(&head_ref);
2258+
2259+
if (resolved && (flag & REF_ISSYMREF)) {
2260+
if (full_ref_out)
2261+
*full_ref_out = xstrdup(resolved);
2262+
return refs_shorten_unambiguous_ref(
2263+
get_main_ref_store(the_repository), resolved, 0);
2264+
}
2265+
}
2266+
2267+
return NULL;
2268+
}
2269+
2270+
static int is_default_remote_branch(const char *name)
2271+
{
2272+
char *default_full = NULL;
2273+
const char *default_short;
2274+
int result = 0;
2275+
2276+
default_short = get_default_remote_ref(&default_full);
2277+
if (!default_short)
2278+
return 0;
2279+
2280+
result = !strcmp(name, default_short);
2281+
2282+
free(default_full);
2283+
return result;
2284+
}
2285+
2286+
static void format_default_branch_comparison(struct strbuf *sb,
2287+
const char *branch_refname,
2288+
enum ahead_behind_flags abf)
2289+
{
2290+
int default_ours = 0, default_theirs = 0;
2291+
char *default_full = NULL;
2292+
const char *default_short;
2293+
2294+
default_short = get_default_remote_ref(&default_full);
2295+
if (!default_short)
2296+
return;
2297+
2298+
if (stat_branch_pair(branch_refname, default_full,
2299+
&default_ours, &default_theirs, abf) <= 0) {
2300+
free(default_full);
2301+
return;
2302+
}
2303+
2304+
strbuf_addstr(sb, "\n");
2305+
2306+
if (default_ours > 0 && default_theirs == 0) {
2307+
strbuf_addf(sb,
2308+
Q_("Ahead of '%s' by %d commit.\n",
2309+
"Ahead of '%s' by %d commits.\n",
2310+
default_ours),
2311+
default_short, default_ours);
2312+
} else if (default_theirs > 0 && default_ours == 0) {
2313+
strbuf_addf(sb,
2314+
Q_("Behind '%s' by %d commit.\n",
2315+
"Behind '%s' by %d commits.\n",
2316+
default_theirs),
2317+
default_short, default_theirs);
2318+
} else if (default_ours > 0 && default_theirs > 0) {
2319+
strbuf_addf(sb,
2320+
Q_("Diverged from '%s' by %d commit.\n",
2321+
"Diverged from '%s' by %d commits.\n",
2322+
default_ours + default_theirs),
2323+
default_short, default_ours + default_theirs);
2324+
}
2325+
2326+
free(default_full);
2327+
}
2328+
22402329
/*
22412330
* Return true when there is anything to report, otherwise false.
22422331
*/
@@ -2248,6 +2337,7 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
22482337
const char *full_base;
22492338
char *base;
22502339
int upstream_is_gone = 0;
2340+
int show_default_branch_comparison;
22512341

22522342
sti = stat_tracking_info(branch, &ours, &theirs, &full_base, 0, abf);
22532343
if (sti < 0) {
@@ -2258,6 +2348,9 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
22582348

22592349
base = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
22602350
full_base, 0);
2351+
2352+
show_default_branch_comparison = !is_default_remote_branch(base);
2353+
22612354
if (upstream_is_gone) {
22622355
strbuf_addf(sb,
22632356
_("Your branch is based on '%s', but the upstream is gone.\n"),
@@ -2269,6 +2362,8 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
22692362
strbuf_addf(sb,
22702363
_("Your branch is up to date with '%s'.\n"),
22712364
base);
2365+
if (show_default_branch_comparison)
2366+
format_default_branch_comparison(sb, branch->refname, abf);
22722367
} else if (abf == AHEAD_BEHIND_QUICK) {
22732368
strbuf_addf(sb,
22742369
_("Your branch and '%s' refer to different commits.\n"),
@@ -2285,6 +2380,8 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
22852380
if (advice_enabled(ADVICE_STATUS_HINTS))
22862381
strbuf_addstr(sb,
22872382
_(" (use \"git push\" to publish your local commits)\n"));
2383+
if (show_default_branch_comparison)
2384+
format_default_branch_comparison(sb, branch->refname, abf);
22882385
} else if (!ours) {
22892386
strbuf_addf(sb,
22902387
Q_("Your branch is behind '%s' by %d commit, "
@@ -2296,6 +2393,8 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
22962393
if (advice_enabled(ADVICE_STATUS_HINTS))
22972394
strbuf_addstr(sb,
22982395
_(" (use \"git pull\" to update your local branch)\n"));
2396+
if (show_default_branch_comparison)
2397+
format_default_branch_comparison(sb, branch->refname, abf);
22992398
} else {
23002399
strbuf_addf(sb,
23012400
Q_("Your branch and '%s' have diverged,\n"
@@ -2310,6 +2409,8 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
23102409
advice_enabled(ADVICE_STATUS_HINTS))
23112410
strbuf_addstr(sb,
23122411
_(" (use \"git pull\" if you want to integrate the remote branch with yours)\n"));
2412+
if (show_default_branch_comparison)
2413+
format_default_branch_comparison(sb, branch->refname, abf);
23132414
}
23142415
free(base);
23152416
return 1;

0 commit comments

Comments
 (0)