From: "Harald Nordgren via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: Harald Nordgren <haraldnordgren@gmail.com>,
Harald Nordgren <haraldnordgren@gmail.com>
Subject: [PATCH 2/6] status: say when the push branch was rebased cleanly
Date: Tue, 06 Oct 2026 07:08:20 +0000 [thread overview]
Message-ID: <dc0efefa0ef02a04f211cf7e24a8b6855f36e12f.1791270504.git.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.2437.git.git.1791270504.gitgitgadget@gmail.com>
From: Harald Nordgren <haraldnordgren@gmail.com>
After rebasing pushed work onto a newer upstream without changing it,
"git status" reports the commits outside the upstream on both sides,
as if the branch and its push branch held different work.
When those commits carry the same changes on both sides, say that the
branch was rebased cleanly:
Your branch and 'origin/topic' have diverged,
and have 51 and 1 different commits each (rebased cleanly on 'upstream/main').
Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
---
remote.c | 49 ++++++++++++++++++++++++++++++++--------
t/t6040-tracking-info.sh | 30 ++++++++++++++++++++++++
2 files changed, 69 insertions(+), 10 deletions(-)
diff --git a/remote.c b/remote.c
index cbe79275f7..89d142cc7e 100644
--- a/remote.c
+++ b/remote.c
@@ -2248,7 +2248,7 @@ int resolve_remote_symref(struct ref *ref, struct ref *list)
static int stat_branch_pair(const char *branch_name, const char *base,
const char *exclude,
- int *num_ours, int *num_theirs,
+ int *num_ours, int *num_theirs, int *num_same,
enum ahead_behind_flags abf)
{
struct object_id oid;
@@ -2278,6 +2278,8 @@ static int stat_branch_pair(const char *branch_name, const char *base,
}
*num_theirs = *num_ours = 0;
+ if (num_same)
+ *num_same = 0;
/* are we the same? */
if (theirs == ours)
@@ -2290,6 +2292,8 @@ static int stat_branch_pair(const char *branch_name, const char *base,
/* Run "rev-list --left-right ours...theirs" internally... */
strvec_push(&argv, ""); /* ignored */
strvec_push(&argv, "--left-right");
+ if (num_same)
+ strvec_push(&argv, "--cherry-mark");
strvec_pushf(&argv, "%s...%s",
oid_to_hex(&ours->object.oid),
oid_to_hex(&theirs->object.oid));
@@ -2311,6 +2315,8 @@ static int stat_branch_pair(const char *branch_name, const char *base,
(*num_ours)++;
else
(*num_theirs)++;
+ if (num_same && (c->object.flags & PATCHSAME))
+ (*num_same)++;
}
/* clear object flags smudged by the above traversal */
@@ -2358,22 +2364,31 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
return -1;
return stat_branch_pair(branch->refname, base, NULL,
- num_ours, num_theirs, abf);
+ num_ours, num_theirs, NULL, abf);
}
/*
* Count the commits that differ between branch_name and base but are not
* in upstream. Return false when they cannot be counted or upstream
- * accounts for none of the ours and theirs commits.
+ * accounts for none of the ours and theirs commits. Otherwise set
+ * *same_changes when the remaining commits carry the same changes on
+ * both sides.
*/
static bool stat_outside_upstream(const char *branch_name, const char *base,
const char *upstream, int ours, int theirs,
- int *ours_unmerged, int *theirs_unmerged)
+ int *ours_unmerged, int *theirs_unmerged,
+ bool *same_changes)
{
+ int same;
+
if (stat_branch_pair(branch_name, base, upstream, ours_unmerged,
- theirs_unmerged, AHEAD_BEHIND_FULL) < 0)
+ theirs_unmerged, &same, AHEAD_BEHIND_FULL) < 0)
+ return false;
+ if (*ours_unmerged == ours && *theirs_unmerged == theirs)
return false;
- return *ours_unmerged != ours || *theirs_unmerged != theirs;
+ *same_changes = *ours_unmerged &&
+ same == *ours_unmerged + *theirs_unmerged;
+ return true;
}
static char *resolve_compare_branch(struct branch *branch, const char *name)
@@ -2407,6 +2422,7 @@ static void format_branch_comparison(struct strbuf *sb,
const char *push_branch_name,
const char *upstream_name,
int ours_unmerged, int theirs_unmerged,
+ bool same_changes,
enum ahead_behind_flags abf,
unsigned flags)
{
@@ -2452,7 +2468,17 @@ static void format_branch_comparison(struct strbuf *sb,
_(" (use \"git pull\" to update your local branch)\n"));
}
} else {
- if (upstream_name)
+ if (same_changes)
+ strbuf_addf(sb,
+ Q_("Your branch and '%s' have diverged,\n"
+ "and have %d and %d different commit each "
+ "(rebased cleanly on '%s').\n",
+ "Your branch and '%s' have diverged,\n"
+ "and have %d and %d different commits each "
+ "(rebased cleanly on '%s').\n",
+ ours + theirs),
+ branch_name, ours, theirs, upstream_name);
+ else if (upstream_name)
strbuf_addf(sb,
Q_("Your branch and '%s' have diverged,\n"
"and have %d and %d different commit each "
@@ -2519,6 +2545,7 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
char *upstream_name = NULL;
int ours, theirs, cmp;
int ours_unmerged = 0, theirs_unmerged = 0;
+ bool same_changes = false;
int is_upstream, is_push;
unsigned flags = 0;
const char *push_remote_name = NULL;
@@ -2544,12 +2571,13 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
is_push = 1;
cmp = stat_branch_pair(branch->refname, full_ref, NULL,
- &ours, &theirs, abf);
+ &ours, &theirs, NULL, abf);
if (cmp > 0 && ours && theirs && upstream_ref && !is_upstream &&
stat_outside_upstream(branch->refname, full_ref,
upstream_ref, ours, theirs,
- &ours_unmerged, &theirs_unmerged))
+ &ours_unmerged, &theirs_unmerged,
+ &same_changes))
upstream_name = refs_shorten_unambiguous_ref(
get_main_ref_store(the_repository),
upstream_ref, 0);
@@ -2596,7 +2624,8 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
format_branch_comparison(sb, !cmp, ours, theirs, short_ref,
push_remote_name, push_branch_name,
upstream_name, ours_unmerged,
- theirs_unmerged, abf, flags);
+ theirs_unmerged, same_changes,
+ abf, flags);
reported = 1;
free(full_ref);
diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh
index f8df16a5de..2ebc3573da 100755
--- a/t/t6040-tracking-info.sh
+++ b/t/t6040-tracking-info.sh
@@ -778,4 +778,34 @@ test_expect_success 'status.compareBranches counts push divergence outside upstr
test_cmp expect actual
'
+test_expect_success 'status.compareBranches after a clean rebase of the push branch' '
+ test_config -C test push.default current &&
+ test_config -C test status.compareBranches "@{upstream} @{push}" &&
+ (
+ cd test &&
+ git checkout -b feature19 origin/main &&
+ advance work19 &&
+ git push
+ ) &&
+ git checkout main &&
+ advance main19a &&
+ advance main19b &&
+ git checkout - &&
+ (
+ cd test &&
+ git pull --rebase &&
+ git status >../actual
+ ) &&
+ cat >expect <<-EOF &&
+ On branch feature19
+ Your branch is ahead of ${SQ}origin/main${SQ} by 1 commit.
+
+ Your branch and ${SQ}origin/feature19${SQ} have diverged,
+ and have 3 and 1 different commits each (rebased cleanly on ${SQ}origin/main${SQ}).
+
+ nothing to commit, working tree clean
+ EOF
+ test_cmp expect actual
+'
+
test_done
--
gitgitgadget
next prev parent reply other threads:[~2026-10-06 7:08 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-06 7:08 [PATCH 0/6] status, push: handle a push branch rebased onto a newer upstream Harald Nordgren via GitGitGadget
2026-10-06 7:08 ` [PATCH 1/6] status: count push divergence outside the upstream Harald Nordgren via GitGitGadget
2026-10-06 7:08 ` Harald Nordgren via GitGitGadget [this message]
2026-10-06 7:08 ` [PATCH 3/6] status: suggest a force push after a clean rebase Harald Nordgren via GitGitGadget
2026-10-06 7:08 ` [PATCH 4/6] push: name the branch to pull from when it is not the upstream Harald Nordgren via GitGitGadget
2026-10-06 7:08 ` [PATCH 5/6] push: offer a force push after rewriting pushed commits Harald Nordgren via GitGitGadget
2026-10-06 7:08 ` [PATCH 6/6] push: suggest a force push after a clean rebase Harald Nordgren via GitGitGadget
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=dc0efefa0ef02a04f211cf7e24a8b6855f36e12f.1791270504.git.gitgitgadget@gmail.com \
--to=gitgitgadget@gmail.com \
--cc=git@vger.kernel.org \
--cc=haraldnordgren@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox