Skip to content

Commit 9080d2b

Browse files
HaraldNordgrengitster
authored andcommitted
status: show comparison with push remote tracking branch
"git status" on a branch that follows a remote branch compares commits on the current branch and the remote-tracking branch it builds upon, to show "ahead", "behind", or "diverged" status. When working on a feature branch that tracks a remote feature branch, but you also want to track progress relative to the push destination tracking branch (which may differ from the upstream branch), git status now shows an additional comparison. When the upstream tracking branch differs from the push destination tracking branch, git status shows both the comparison with the upstream tracking branch (as before) and an additional comparison with the push destination tracking branch. The push branch comparison appears on a separate line after the upstream branch status, using the same format. Example output when tracking origin/main but push destination is origin/feature: On branch feature Your branch and 'origin/main' have diverged, and have 3 and 1 different commits each, respectively. (use "git pull" if you want to integrate the remote branch with yours) Your branch is ahead of 'origin/feature' by 1 commit. (use "git push" to publish your local commits) The comparison is only shown when the push destination tracking branch differs from the upstream tracking branch, even if they are on the same remote. Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent f3ddacc commit 9080d2b

2 files changed

Lines changed: 358 additions & 6 deletions

File tree

remote.c

Lines changed: 96 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@
2929

3030
enum map_direction { FROM_SRC, FROM_DST };
3131

32+
enum {
33+
BRANCH_MODE_PULL = (1 << 0),
34+
BRANCH_MODE_PUSH = (1 << 1),
35+
};
36+
3237
struct counted_string {
3338
size_t len;
3439
const char *s;
@@ -2237,13 +2242,75 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
22372242
return stat_branch_pair(branch->refname, base, num_ours, num_theirs, abf);
22382243
}
22392244

2245+
static char *get_remote_push_branch(struct branch *branch, char **full_ref_out)
2246+
{
2247+
struct remote *remote;
2248+
const char *push_remote;
2249+
char *push_dst = NULL;
2250+
char *tracking_ref;
2251+
const char *resolved;
2252+
char *ret;
2253+
2254+
if (!branch)
2255+
return NULL;
2256+
2257+
push_remote = pushremote_for_branch(branch, NULL);
2258+
if (!push_remote)
2259+
return NULL;
2260+
2261+
remote = remotes_remote_get(the_repository, push_remote);
2262+
if (!remote)
2263+
return NULL;
2264+
2265+
push_dst = remote_ref_for_branch(branch, 1);
2266+
if (!push_dst) {
2267+
if (remote->push.nr)
2268+
return NULL;
2269+
push_dst = xstrdup(branch->refname);
2270+
}
2271+
2272+
tracking_ref = (char *)tracking_for_push_dest(remote, push_dst, NULL);
2273+
free(push_dst);
2274+
2275+
if (!tracking_ref)
2276+
return NULL;
2277+
2278+
resolved = refs_resolve_ref_unsafe(
2279+
get_main_ref_store(the_repository),
2280+
tracking_ref,
2281+
RESOLVE_REF_READING,
2282+
NULL, NULL);
2283+
2284+
if (!resolved) {
2285+
free(tracking_ref);
2286+
return NULL;
2287+
}
2288+
2289+
if (full_ref_out)
2290+
*full_ref_out = xstrdup(resolved);
2291+
2292+
ret = refs_shorten_unambiguous_ref(
2293+
get_main_ref_store(the_repository), resolved, 0);
2294+
free(tracking_ref);
2295+
return ret;
2296+
}
2297+
22402298
static void format_branch_comparison(struct strbuf *sb,
22412299
bool up_to_date,
22422300
int ours, int theirs,
22432301
const char *branch_name,
22442302
enum ahead_behind_flags abf,
2303+
unsigned flags,
22452304
bool show_divergence_advice)
22462305
{
2306+
bool want_push_advice = (flags & BRANCH_MODE_PUSH) &&
2307+
advice_enabled(ADVICE_STATUS_HINTS);
2308+
bool want_pull_advice = (flags & BRANCH_MODE_PULL) &&
2309+
advice_enabled(ADVICE_STATUS_HINTS);
2310+
bool want_divergence_advice = (flags & BRANCH_MODE_PULL) &&
2311+
show_divergence_advice &&
2312+
advice_enabled(ADVICE_STATUS_HINTS);
2313+
22472314
if (up_to_date) {
22482315
strbuf_addf(sb,
22492316
_("Your branch is up to date with '%s'.\n"),
@@ -2252,7 +2319,7 @@ static void format_branch_comparison(struct strbuf *sb,
22522319
strbuf_addf(sb,
22532320
_("Your branch and '%s' refer to different commits.\n"),
22542321
branch_name);
2255-
if (advice_enabled(ADVICE_STATUS_HINTS))
2322+
if (want_push_advice)
22562323
strbuf_addf(sb, _(" (use \"%s\" for details)\n"),
22572324
"git status --ahead-behind");
22582325
} else if (!theirs) {
@@ -2261,7 +2328,7 @@ static void format_branch_comparison(struct strbuf *sb,
22612328
"Your branch is ahead of '%s' by %d commits.\n",
22622329
ours),
22632330
branch_name, ours);
2264-
if (advice_enabled(ADVICE_STATUS_HINTS))
2331+
if (want_push_advice)
22652332
strbuf_addstr(sb,
22662333
_(" (use \"git push\" to publish your local commits)\n"));
22672334
} else if (!ours) {
@@ -2272,7 +2339,7 @@ static void format_branch_comparison(struct strbuf *sb,
22722339
"and can be fast-forwarded.\n",
22732340
theirs),
22742341
branch_name, theirs);
2275-
if (advice_enabled(ADVICE_STATUS_HINTS))
2342+
if (want_pull_advice)
22762343
strbuf_addstr(sb,
22772344
_(" (use \"git pull\" to update your local branch)\n"));
22782345
} else {
@@ -2285,8 +2352,7 @@ static void format_branch_comparison(struct strbuf *sb,
22852352
"respectively.\n",
22862353
ours + theirs),
22872354
branch_name, ours, theirs);
2288-
if (show_divergence_advice &&
2289-
advice_enabled(ADVICE_STATUS_HINTS))
2355+
if (want_divergence_advice)
22902356
strbuf_addstr(sb,
22912357
_(" (use \"git pull\" if you want to integrate the remote branch with yours)\n"));
22922358
}
@@ -2303,6 +2369,11 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
23032369
const char *full_base;
23042370
char *base;
23052371
int upstream_is_gone = 0;
2372+
unsigned base_branch_modes = BRANCH_MODE_PULL | BRANCH_MODE_PUSH;
2373+
int push_ours, push_theirs, push_cmp_fetch;
2374+
char *full_push = NULL;
2375+
char *push = NULL;
2376+
unsigned push_branch_modes = 0;
23062377

23072378
cmp_fetch = stat_tracking_info(branch, &ours, &theirs, &full_base, 0, abf);
23082379
if (cmp_fetch < 0) {
@@ -2314,6 +2385,16 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
23142385
base = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
23152386
full_base, 0);
23162387

2388+
push = get_remote_push_branch(branch, &full_push);
2389+
if (push && strcmp(base, push)) {
2390+
push_cmp_fetch = stat_branch_pair(branch->refname, full_push,
2391+
&push_ours, &push_theirs, abf);
2392+
if (push_cmp_fetch >= 0) {
2393+
base_branch_modes = BRANCH_MODE_PULL;
2394+
push_branch_modes = BRANCH_MODE_PUSH;
2395+
}
2396+
}
2397+
23172398
if (upstream_is_gone) {
23182399
strbuf_addf(sb,
23192400
_("Your branch is based on '%s', but the upstream is gone.\n"),
@@ -2322,10 +2403,19 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
23222403
strbuf_addstr(sb,
23232404
_(" (use \"git branch --unset-upstream\" to fixup)\n"));
23242405
} else {
2325-
format_branch_comparison(sb, !cmp_fetch, ours, theirs, base, abf, show_divergence_advice);
2406+
format_branch_comparison(sb, !cmp_fetch, ours, theirs, base, abf,
2407+
base_branch_modes, show_divergence_advice);
2408+
}
2409+
2410+
if (push_branch_modes & BRANCH_MODE_PUSH) {
2411+
strbuf_addstr(sb, "\n");
2412+
format_branch_comparison(sb, !push_cmp_fetch, push_ours, push_theirs, push, abf,
2413+
push_branch_modes, show_divergence_advice);
23262414
}
23272415

23282416
free(base);
2417+
free(full_push);
2418+
free(push);
23292419
return 1;
23302420
}
23312421

0 commit comments

Comments
 (0)