Subject: [PATCH v15 0/2] status: show comparison with push remote tracking branch

From: "Harald Nordgren via GitGitGadget" <[email-masked]>
Date: Sun, 04 Jan 2026 11:53:52 +0000

cc: Chris Torek [email-masked] cc: Yee Cheng Chin
[email-masked] cc: "brian m. carlson" [email-masked]
cc: Ben Knoble [email-masked] cc: "Kristoffer Haugsbakk"
[email-masked] cc: Phillip Wood [email-masked]
cc: Nico Williams [email-masked]

Harald Nordgren (2):
  refactor format_branch_comparison in preparation
  status: show comparison with push remote tracking branch

 remote.c                 | 168 ++++++++++++++++++++++++-------
 t/t6040-tracking-info.sh | 210 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 342 insertions(+), 36 deletions(-)

base-commit: 68cb7f9e92a5d8e9824f5b52ac3d0a9d8f653dbe
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2138%2FHaraldNordgren%2Fahead_of_main_status-v15
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2138/HaraldNordgren/ahead_of_main_status-v15
Pull-Request: https://github.com/git/git/pull/2138

Range-diff vs v14:

 1:  a2c160c53e ! 1:  cf4e9779c5 refactor: format_branch_comparison in preparation
     @@ Metadata
      Author: Harald Nordgren <[email-masked]>
      
       ## Commit message ##
     -    refactor: format_branch_comparison in preparation
     +    refactor format_branch_comparison in preparation
      
          Refactor format_branch_comparison function in preparation for showing
          comparison with push remote tracking branch.
     @@ remote.c: int stat_tracking_info(struct branch *branch, int *num_ours, int *num_
      -			 enum ahead_behind_flags abf,
      -			 int show_divergence_advice)
      +static void format_branch_comparison(struct strbuf *sb,
     -+				     int ahead, int behind,
     ++				     int ours, int theirs,
      +				     const char *branch_name,
     -+				     int upstream_is_gone,
      +				     enum ahead_behind_flags abf,
     -+				     int sti)
     ++				     int show_divergence_advice)
       {
      -	int ours, theirs, sti;
      -	const char *full_base;
     @@ remote.c: int stat_tracking_info(struct branch *branch, int *num_ours, int *num_
      -
      -	base = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
      -					    full_base, 0);
     - 	if (upstream_is_gone) {
     - 		strbuf_addf(sb,
     - 			_("Your branch is based on '%s', but the upstream is gone.\n"),
     +-	if (upstream_is_gone) {
     +-		strbuf_addf(sb,
     +-			_("Your branch is based on '%s', but the upstream is gone.\n"),
      -			base);
     -+			branch_name);
     - 		if (advice_enabled(ADVICE_STATUS_HINTS))
     - 			strbuf_addstr(sb,
     - 				_("  (use \"git branch --unset-upstream\" to fixup)\n"));
     - 	} else if (!sti) {
     - 		strbuf_addf(sb,
     - 			_("Your branch is up to date with '%s'.\n"),
     +-		if (advice_enabled(ADVICE_STATUS_HINTS))
     +-			strbuf_addstr(sb,
     +-				_("  (use \"git branch --unset-upstream\" to fixup)\n"));
     +-	} else if (!sti) {
     +-		strbuf_addf(sb,
     +-			_("Your branch is up to date with '%s'.\n"),
      -			base);
     -+			branch_name);
     - 	} else if (abf == AHEAD_BEHIND_QUICK) {
     +-	} else if (abf == AHEAD_BEHIND_QUICK) {
     ++	if (abf == AHEAD_BEHIND_QUICK) {
       		strbuf_addf(sb,
       			    _("Your branch and '%s' refer to different commits.\n"),
      -			    base);
     @@ remote.c: int stat_tracking_info(struct branch *branch, int *num_ours, int *num_
       		if (advice_enabled(ADVICE_STATUS_HINTS))
       			strbuf_addf(sb, _("  (use \"%s\" for details)\n"),
       				    "git status --ahead-behind");
     --	} else if (!theirs) {
     -+	} else if (ahead == 0 && behind == 0) {
     ++	} else if (!ours && !theirs) {
      +		strbuf_addf(sb,
      +			_("Your branch is up to date with '%s'.\n"),
      +			branch_name);
     -+	} else if (ahead > 0 && behind == 0) {
     + 	} else if (!theirs) {
       		strbuf_addf(sb,
       			Q_("Your branch is ahead of '%s' by %d commit.\n",
       			   "Your branch is ahead of '%s' by %d commits.\n",
     --			   ours),
     + 			   ours),
      -			base, ours);
     --		if (advice_enabled(ADVICE_STATUS_HINTS))
     --			strbuf_addstr(sb,
     --				_("  (use \"git push\" to publish your local commits)\n"));
     --	} else if (!ours) {
     -+			   ahead),
     -+			branch_name, ahead);
     -+	} else if (behind > 0 && ahead == 0) {
     - 		strbuf_addf(sb,
     - 			Q_("Your branch is behind '%s' by %d commit, "
     - 			       "and can be fast-forwarded.\n",
     ++			branch_name, ours);
     + 		if (advice_enabled(ADVICE_STATUS_HINTS))
     + 			strbuf_addstr(sb,
     + 				_("  (use \"git push\" to publish your local commits)\n"));
     +@@ remote.c: int format_tracking_info(struct branch *branch, struct strbuf *sb,
       			   "Your branch is behind '%s' by %d commits, "
       			       "and can be fast-forwarded.\n",
     --			   theirs),
     + 			   theirs),
      -			base, theirs);
     --		if (advice_enabled(ADVICE_STATUS_HINTS))
     --			strbuf_addstr(sb,
     --				_("  (use \"git pull\" to update your local branch)\n"));
     --	} else {
     -+			   behind),
     -+			branch_name, behind);
     -+	} else if (ahead > 0 && behind > 0) {
     - 		strbuf_addf(sb,
     - 			Q_("Your branch and '%s' have diverged,\n"
     - 			       "and have %d and %d different commit each, "
     ++			branch_name, theirs);
     + 		if (advice_enabled(ADVICE_STATUS_HINTS))
     + 			strbuf_addstr(sb,
     + 				_("  (use \"git pull\" to update your local branch)\n"));
      @@ remote.c: int format_tracking_info(struct branch *branch, struct strbuf *sb,
     - 			   "Your branch and '%s' have diverged,\n"
       			       "and have %d and %d different commits each, "
       			       "respectively.\n",
     --			   ours + theirs),
     + 			   ours + theirs),
      -			base, ours, theirs);
     --		if (show_divergence_advice &&
     --		    advice_enabled(ADVICE_STATUS_HINTS))
     -+			   ahead + behind),
     -+			branch_name, ahead, behind);
     -+	}
     ++			branch_name, ours, theirs);
     + 		if (show_divergence_advice &&
     + 		    advice_enabled(ADVICE_STATUS_HINTS))
     + 			strbuf_addstr(sb,
     + 				_("  (use \"git pull\" if you want to integrate the remote branch with yours)\n"));
     + 	}
      +}
      +
      +/*
     @@ remote.c: int format_tracking_info(struct branch *branch, struct strbuf *sb,
      +	base = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
      +					    full_base, 0);
      +
     -+	format_branch_comparison(sb, ours, theirs, base, upstream_is_gone, abf, sti);
     -+	if (sti > 0 && abf != AHEAD_BEHIND_QUICK) {
     -+		if (!theirs && advice_enabled(ADVICE_STATUS_HINTS)) {
     -+			strbuf_addstr(sb,
     -+				_("  (use \"git push\" to publish your local commits)\n"));
     -+		} else if (!ours && advice_enabled(ADVICE_STATUS_HINTS)) {
     ++	if (upstream_is_gone) {
     ++		strbuf_addf(sb,
     ++			_("Your branch is based on '%s', but the upstream is gone.\n"),
     ++			base);
     ++		if (advice_enabled(ADVICE_STATUS_HINTS))
      +			strbuf_addstr(sb,
     -+				_("  (use \"git pull\" to update your local branch)\n"));
     -+		} else if (ours && theirs && show_divergence_advice &&
     -+			   advice_enabled(ADVICE_STATUS_HINTS)) {
     - 			strbuf_addstr(sb,
     - 				_("  (use \"git pull\" if you want to integrate the remote branch with yours)\n"));
     -+		}
     - 	}
     ++				_("  (use \"git branch --unset-upstream\" to fixup)\n"));
     ++	} else {
     ++		format_branch_comparison(sb, ours, theirs, base, abf, show_divergence_advice);
     ++	}
      +
       	free(base);
       	return 1;
 2:  b9b2f15498 ! 2:  a435cf4ce4 status: show comparison with push remote tracking branch
     @@ Commit message
          Signed-off-by: Harald Nordgren <[email-masked]>
      
       ## remote.c ##
     +@@
     + 
     + enum map_direction { FROM_SRC, FROM_DST };
     + 
     ++enum branch_type {
     ++	PUSH = 1 << 0,
     ++	PULL = 1 << 1
     ++};
     ++
     + struct counted_string {
     + 	size_t len;
     + 	const char *s;
      @@ remote.c: int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
       	return stat_branch_pair(branch->refname, base, num_ours, num_theirs, abf);
       }
     @@ remote.c: int stat_tracking_info(struct branch *branch, int *num_ours, int *num_
      +}
      +
       static void format_branch_comparison(struct strbuf *sb,
     - 				     int ahead, int behind,
     + 				     int ours, int theirs,
       				     const char *branch_name,
     + 				     enum ahead_behind_flags abf,
     ++				     enum branch_type bt,
     + 				     int show_divergence_advice)
     + {
     + 	if (abf == AHEAD_BEHIND_QUICK) {
     +@@ remote.c: static void format_branch_comparison(struct strbuf *sb,
     + 			   "Your branch is ahead of '%s' by %d commits.\n",
     + 			   ours),
     + 			branch_name, ours);
     +-		if (advice_enabled(ADVICE_STATUS_HINTS))
     ++		if ((bt & PUSH) && advice_enabled(ADVICE_STATUS_HINTS))
     + 			strbuf_addstr(sb,
     + 				_("  (use \"git push\" to publish your local commits)\n"));
     + 	} else if (!ours) {
     +@@ remote.c: static void format_branch_comparison(struct strbuf *sb,
     + 			       "and can be fast-forwarded.\n",
     + 			   theirs),
     + 			branch_name, theirs);
     +-		if (advice_enabled(ADVICE_STATUS_HINTS))
     ++		if ((bt & PULL) && advice_enabled(ADVICE_STATUS_HINTS))
     + 			strbuf_addstr(sb,
     + 				_("  (use \"git pull\" to update your local branch)\n"));
     + 	} else {
     +@@ remote.c: static void format_branch_comparison(struct strbuf *sb,
     + 			       "respectively.\n",
     + 			   ours + theirs),
     + 			branch_name, ours, theirs);
     +-		if (show_divergence_advice &&
     ++		if ((bt & PULL) &&
     ++		    show_divergence_advice &&
     + 		    advice_enabled(ADVICE_STATUS_HINTS))
     + 			strbuf_addstr(sb,
     + 				_("  (use \"git pull\" if you want to integrate the remote branch with yours)\n"));
      @@ remote.c: int format_tracking_info(struct branch *branch, struct strbuf *sb,
       	const char *full_base;
       	char *base;
       	int upstream_is_gone = 0;
     ++	enum branch_type base_bt = PUSH | PULL;
      +	int push_ours, push_theirs, push_sti;
      +	char *full_push = NULL;
      +	char *push = NULL;
     -+	int show_push_comparison = 0;
     ++	enum branch_type push_bt = 0;
       
       	sti = stat_tracking_info(branch, &ours, &theirs, &full_base, 0, abf);
       	if (sti < 0) {
     @@ remote.c: int format_tracking_info(struct branch *branch, struct strbuf *sb,
      +	if (push && strcmp(base, push)) {
      +		push_sti = stat_branch_pair(branch->refname, full_push,
      +					   &push_ours, &push_theirs, abf);
     -+		if (push_sti >= 0)
     -+			show_push_comparison = 1;
     ++		if (push_sti >= 0) {
     ++			base_bt = PULL;
     ++			push_bt = PUSH;
     ++		}
      +	}
      +
     - 	format_branch_comparison(sb, ours, theirs, base, upstream_is_gone, abf, sti);
     - 	if (sti > 0 && abf != AHEAD_BEHIND_QUICK) {
     --		if (!theirs && advice_enabled(ADVICE_STATUS_HINTS)) {
     -+		if (!theirs && !show_push_comparison &&
     -+		    advice_enabled(ADVICE_STATUS_HINTS)) {
     - 			strbuf_addstr(sb,
     - 				_("  (use \"git push\" to publish your local commits)\n"));
     - 		} else if (!ours && advice_enabled(ADVICE_STATUS_HINTS)) {
     + 	if (upstream_is_gone) {
     + 		strbuf_addf(sb,
     + 			_("Your branch is based on '%s', but the upstream is gone.\n"),
      @@ remote.c: int format_tracking_info(struct branch *branch, struct strbuf *sb,
     - 		}
     - 	}
     - 
     -+	if (show_push_comparison) {
     -+		strbuf_addstr(sb, "\n");
     -+		format_branch_comparison(sb, push_ours, push_theirs, push, 0, abf, push_sti);
     -+		if (push_sti > 0 && abf != AHEAD_BEHIND_QUICK) {
     -+			if (!push_theirs && advice_enabled(ADVICE_STATUS_HINTS)) {
     -+				strbuf_addstr(sb,
     -+					_("  (use \"git push\" to publish your local commits)\n"));
     -+			}
     -+		}
     + 			strbuf_addstr(sb,
     + 				_("  (use \"git branch --unset-upstream\" to fixup)\n"));
     + 	} else {
     +-		format_branch_comparison(sb, ours, theirs, base, abf, show_divergence_advice);
     ++		format_branch_comparison(sb, ours, theirs, base, abf, base_bt,
     ++					 show_divergence_advice);
      +	}
      +
     ++	if (push_bt & PUSH) {
     ++		strbuf_addstr(sb, "\n");
     ++		format_branch_comparison(sb, push_ours, push_theirs, push, abf,
     ++					 push_bt, 0);
     + 	}
     + 
       	free(base);
      +	free(full_push);
      +	free(push);

-- 
gitgitgadget