remote: qualify "git pull" advice for non-upstream compareBranches
Enable ENABLE_ADVICE_PULL for push-branch comparisons too, not just the upstream entry, so the "use git pull" hint prints when the local branch is behind its push branch. Spell out "git pull <remote> <branch>" so running the suggested command actually pulls the ref the user was told about; plain "git pull" would fetch the upstream instead. Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Harald Nordgren committed
May 21, 2026 at 14:06 UTC
4b0a8b2506494323ae5c209ee7df6c08fec3831d
2 files changed
+140
-8
remote.c
+40
-8
@@ -2267,6 +2267,8 @@ static void format_branch_comparison(struct strbuf *sb,
2267
bool up_to_date,
2268
int ours, int theirs,
2269
const char *branch_name,
2270
+ const char *push_remote_name,
2271
+ const char *push_branch_name,
2272
enum ahead_behind_flags abf,
2273
unsigned flags)
2274
{
@@ -2302,9 +2304,15 @@ static void format_branch_comparison(struct strbuf *sb,
2304
"and can be fast-forwarded.\n",
2305
theirs),
2306
branch_name, theirs);
2305
- if (use_pull_advice && advice_enabled(ADVICE_STATUS_HINTS))
2306
- strbuf_addstr(sb,
2307
- _(" (use \"git pull\" to update your local branch)\n"));
2307
+ if (use_pull_advice && advice_enabled(ADVICE_STATUS_HINTS)) {
2308
+ if (push_remote_name && push_branch_name)
2309
+ strbuf_addf(sb,
2310
+ _(" (use \"git pull %s %s\" to update your local branch)\n"),
2311
+ push_remote_name, push_branch_name);
2312
+ else
2313
+ strbuf_addstr(sb,
2314
+ _(" (use \"git pull\" to update your local branch)\n"));
2315
+ }
2316
} else {
2317
strbuf_addf(sb,
2318
Q_("Your branch and '%s' have diverged,\n"
@@ -2315,9 +2323,15 @@ static void format_branch_comparison(struct strbuf *sb,
2323
"respectively.\n",
2324
ours + theirs),
2325
branch_name, ours, theirs);
2318
- if (use_divergence_advice && advice_enabled(ADVICE_STATUS_HINTS))
2319
- strbuf_addstr(sb,
2320
- _(" (use \"git pull\" if you want to integrate the remote branch with yours)\n"));
2326
+ if (use_divergence_advice && advice_enabled(ADVICE_STATUS_HINTS)) {
2327
+ if (push_remote_name && push_branch_name)
2328
+ strbuf_addf(sb,
2329
+ _(" (use \"git pull %s %s\" if you want to integrate the remote branch with yours)\n"),
2330
+ push_remote_name, push_branch_name);
2331
+ else
2332
+ strbuf_addstr(sb,
2333
+ _(" (use \"git pull\" if you want to integrate the remote branch with yours)\n"));
2334
+ }
2335
}
2336
}
2337
@@ -2355,6 +2369,8 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
2369
int ours, theirs, cmp;
2370
int is_upstream, is_push;
2371
unsigned flags = 0;
2372
+ const char *push_remote_name = NULL;
2373
+ const char *push_branch_name = NULL;
2374
2375
full_ref = resolve_compare_branch(branch,
2376
branches.items[i].string);
@@ -2398,11 +2414,27 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
2414
2415
if (is_upstream)
2416
flags |= ENABLE_ADVICE_PULL;
2401
- if (is_push)
2402
- flags |= ENABLE_ADVICE_PUSH;
2417
if (show_divergence_advice && is_upstream)
2418
flags |= ENABLE_ADVICE_DIVERGENCE;
2419
+ if (is_push) {
2420
+ flags |= ENABLE_ADVICE_PUSH;
2421
+ if (!upstream_ref || strcmp(upstream_ref, full_ref)) {
2422
+ push_remote_name = pushremote_for_branch(branch, NULL);
2423
+ if (push_remote_name &&
2424
+ skip_prefix(full_ref, "refs/remotes/", &push_branch_name) &&
2425
+ skip_prefix(push_branch_name, push_remote_name, &push_branch_name) &&
2426
+ *push_branch_name == '/') {
2427
+ push_branch_name++;
2428
+ flags |= ENABLE_ADVICE_PULL;
2429
+ } else {
2430
+ push_remote_name = NULL;
2431
+ }
2432
+ } else {
2433
+ flags |= ENABLE_ADVICE_PULL;
2434
+ }
2435
+ }
2436
format_branch_comparison(sb, !cmp, ours, theirs, short_ref,
2437
+ push_remote_name, push_branch_name,
2438
abf, flags);
2439
reported = 1;
2440
t/t6040-tracking-info.sh
+100
@@ -646,4 +646,104 @@ test_expect_success 'status.compareBranches with remapped push and upstream remo
646
test_cmp expect actual
647
'
648
649
+test_expect_success 'status.compareBranches behind both upstream and push' '
650
+ test_config -C test push.default current &&
651
+ test_config -C test remote.pushDefault origin &&
652
+ test_config -C test status.compareBranches "@{upstream} @{push}" &&
653
+ git -C test checkout -b feature13 upstream/main &&
654
+ (cd test && advance work13) &&
655
+ git -C test push origin &&
656
+ git -C test branch --set-upstream-to upstream/ahead &&
657
+ git -C test reset --hard HEAD^ &&
658
+ git -C test status >actual &&
659
+ cat >expect <<-EOF &&
660
+ On branch feature13
661
+ Your branch is behind ${SQ}upstream/ahead${SQ} by 1 commit, and can be fast-forwarded.
662
+ (use "git pull" to update your local branch)
663
+
664
+ Your branch is behind ${SQ}origin/feature13${SQ} by 1 commit, and can be fast-forwarded.
665
+ (use "git pull origin feature13" to update your local branch)
666
+
667
+ nothing to commit, working tree clean
668
+ EOF
669
+ test_cmp expect actual
670
+'
671
+
672
+test_expect_success 'status.compareBranches with remapped push and behind push branch' '
673
+ test_config -C test remote.pushDefault origin &&
674
+ test_config -C test remote.origin.push refs/heads/feature14:refs/heads/remapped14 &&
675
+ test_config -C test status.compareBranches "@{push}" &&
676
+ git -C test checkout -b feature14 upstream/main &&
677
+ (cd test && advance work14) &&
678
+ git -C test push &&
679
+ git -C test reset --hard HEAD^ &&
680
+ git -C test status >actual &&
681
+ cat >expect <<-EOF &&
682
+ On branch feature14
683
+ Your branch is behind ${SQ}origin/remapped14${SQ} by 1 commit, and can be fast-forwarded.
684
+ (use "git pull origin remapped14" to update your local branch)
685
+
686
+ nothing to commit, working tree clean
687
+ EOF
688
+ test_cmp expect actual
689
+'
690
+
691
+test_expect_success 'status.compareBranches with behind push branch and no upstream' '
692
+ test_config -C test push.default current &&
693
+ test_config -C test remote.pushDefault origin &&
694
+ test_config -C test status.compareBranches "@{push}" &&
695
+ git -C test checkout --no-track -b feature15 upstream/main &&
696
+ (cd test && advance work15) &&
697
+ git -C test push origin &&
698
+ git -C test reset --hard HEAD^ &&
699
+ git -C test status >actual &&
700
+ cat >expect <<-EOF &&
701
+ On branch feature15
702
+ Your branch is behind ${SQ}origin/feature15${SQ} by 1 commit, and can be fast-forwarded.
703
+ (use "git pull origin feature15" to update your local branch)
704
+
705
+ nothing to commit, working tree clean
706
+ EOF
707
+ test_cmp expect actual
708
+'
709
+
710
+test_expect_success 'status.compareBranches behind upstream-equals-push suggests plain pull' '
711
+ test_config -C test status.compareBranches "@{upstream} @{push}" &&
712
+ git -C test checkout -b feature16 origin/main &&
713
+ (cd test && advance work16) &&
714
+ git -C test push origin HEAD:main &&
715
+ git -C test reset --hard HEAD^ &&
716
+ git -C test status >actual &&
717
+ cat >expect <<-EOF &&
718
+ On branch feature16
719
+ Your branch is behind ${SQ}origin/main${SQ} by 1 commit, and can be fast-forwarded.
720
+ (use "git pull" to update your local branch)
721
+
722
+ nothing to commit, working tree clean
723
+ EOF
724
+ test_cmp expect actual
725
+'
726
+
727
+test_expect_success 'status.compareBranches suppresses advice when push tracking ref is unconventional' '
728
+ test_config -C test push.default current &&
729
+ test_config -C test remote.imported.url ../. &&
730
+ test_config -C test remote.imported.fetch "+refs/heads/*:refs/imported/imported/*" &&
731
+ test_config -C test branch.feature17.pushRemote imported &&
732
+ test_config -C test status.compareBranches "@{push}" &&
733
+ git -C test fetch imported &&
734
+ git -C test checkout --no-track -b feature17 refs/imported/imported/main &&
735
+ (cd test && advance work17) &&
736
+ git -C test push imported HEAD:feature17 &&
737
+ git -C test fetch imported &&
738
+ git -C test reset --hard HEAD^ &&
739
+ git -C test status >actual &&
740
+ cat >expect <<-EOF &&
741
+ On branch feature17
742
+ Your branch is behind ${SQ}imported/imported/feature17${SQ} by 1 commit, and can be fast-forwarded.
743
+
744
+ nothing to commit, working tree clean
745
+ EOF
746
+ test_cmp expect actual
747
+'
748
+
749
test_done