mirror of
https://github.com/git/git.git
synced 2026-01-11 13:23:12 +09:00
Merge branch 'hn/status-compare-with-push' into seen
"git status" learned to show comparison between the current branch and its push destination as well as its upstream, when the two are different (i.e., triangular workflow). Comments? * hn/status-compare-with-push: status: show comparison with push remote tracking branch refactor format_branch_comparison in preparation
This commit is contained in:
commit
f00c1e029e
171
remote.c
171
remote.c
@ -29,6 +29,11 @@
|
||||
|
||||
enum map_direction { FROM_SRC, FROM_DST };
|
||||
|
||||
enum branch_mode_flags {
|
||||
BRANCH_MODE_PULL = (1 << 0),
|
||||
BRANCH_MODE_PUSH = (1 << 1),
|
||||
};
|
||||
|
||||
struct counted_string {
|
||||
size_t len;
|
||||
const char *s;
|
||||
@ -2230,52 +2235,85 @@ 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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true when there is anything to report, otherwise false.
|
||||
*/
|
||||
int format_tracking_info(struct branch *branch, struct strbuf *sb,
|
||||
enum ahead_behind_flags abf,
|
||||
int show_divergence_advice)
|
||||
static char *get_remote_push_branch(struct branch *branch, char **full_ref_out)
|
||||
{
|
||||
int ours, theirs, sti;
|
||||
const char *full_base;
|
||||
char *base;
|
||||
int upstream_is_gone = 0;
|
||||
struct remote *remote;
|
||||
const char *push_remote;
|
||||
char *push_dst = NULL;
|
||||
char *tracking_ref;
|
||||
const char *resolved;
|
||||
char *ret;
|
||||
|
||||
sti = stat_tracking_info(branch, &ours, &theirs, &full_base, 0, abf);
|
||||
if (sti < 0) {
|
||||
if (!full_base)
|
||||
return 0;
|
||||
upstream_is_gone = 1;
|
||||
if (!branch)
|
||||
return NULL;
|
||||
|
||||
push_remote = pushremote_for_branch(branch, NULL);
|
||||
if (!push_remote)
|
||||
return NULL;
|
||||
|
||||
remote = remotes_remote_get(the_repository, push_remote);
|
||||
if (!remote)
|
||||
return NULL;
|
||||
|
||||
push_dst = remote_ref_for_branch(branch, 1);
|
||||
if (!push_dst) {
|
||||
if (remote->push.nr)
|
||||
return NULL;
|
||||
push_dst = xstrdup(branch->refname);
|
||||
}
|
||||
|
||||
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"),
|
||||
base);
|
||||
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);
|
||||
} else if (abf == AHEAD_BEHIND_QUICK) {
|
||||
tracking_ref = (char *)tracking_for_push_dest(remote, push_dst, NULL);
|
||||
free(push_dst);
|
||||
|
||||
if (!tracking_ref)
|
||||
return NULL;
|
||||
|
||||
resolved = refs_resolve_ref_unsafe(
|
||||
get_main_ref_store(the_repository),
|
||||
tracking_ref,
|
||||
RESOLVE_REF_READING,
|
||||
NULL, NULL);
|
||||
|
||||
if (!resolved) {
|
||||
free(tracking_ref);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (full_ref_out)
|
||||
*full_ref_out = xstrdup(resolved);
|
||||
|
||||
ret = refs_shorten_unambiguous_ref(
|
||||
get_main_ref_store(the_repository), resolved, 0);
|
||||
free(tracking_ref);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void format_branch_comparison(struct strbuf *sb,
|
||||
int ours, int theirs,
|
||||
const char *branch_name,
|
||||
enum ahead_behind_flags abf,
|
||||
enum branch_mode_flags advice_flags,
|
||||
int show_divergence_advice)
|
||||
{
|
||||
if (abf == AHEAD_BEHIND_QUICK) {
|
||||
strbuf_addf(sb,
|
||||
_("Your branch and '%s' refer to different commits.\n"),
|
||||
base);
|
||||
branch_name);
|
||||
if (advice_enabled(ADVICE_STATUS_HINTS))
|
||||
strbuf_addf(sb, _(" (use \"%s\" for details)\n"),
|
||||
"git status --ahead-behind");
|
||||
} else if (!ours && !theirs) {
|
||||
strbuf_addf(sb,
|
||||
_("Your branch is up to date with '%s'.\n"),
|
||||
branch_name);
|
||||
} 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),
|
||||
base, ours);
|
||||
if (advice_enabled(ADVICE_STATUS_HINTS))
|
||||
branch_name, ours);
|
||||
if ((advice_flags & BRANCH_MODE_PUSH) &&
|
||||
advice_enabled(ADVICE_STATUS_HINTS))
|
||||
strbuf_addstr(sb,
|
||||
_(" (use \"git push\" to publish your local commits)\n"));
|
||||
} else if (!ours) {
|
||||
@ -2285,8 +2323,9 @@ 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),
|
||||
base, theirs);
|
||||
if (advice_enabled(ADVICE_STATUS_HINTS))
|
||||
branch_name, theirs);
|
||||
if ((advice_flags & BRANCH_MODE_PULL) &&
|
||||
advice_enabled(ADVICE_STATUS_HINTS))
|
||||
strbuf_addstr(sb,
|
||||
_(" (use \"git pull\" to update your local branch)\n"));
|
||||
} else {
|
||||
@ -2298,13 +2337,73 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
|
||||
"and have %d and %d different commits each, "
|
||||
"respectively.\n",
|
||||
ours + theirs),
|
||||
base, ours, theirs);
|
||||
if (show_divergence_advice &&
|
||||
branch_name, ours, theirs);
|
||||
if ((advice_flags & BRANCH_MODE_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"));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true when there is anything to report, otherwise false.
|
||||
*/
|
||||
int format_tracking_info(struct branch *branch, struct strbuf *sb,
|
||||
enum ahead_behind_flags abf,
|
||||
int show_divergence_advice)
|
||||
{
|
||||
int ours, theirs, sti;
|
||||
const char *full_base;
|
||||
char *base;
|
||||
int upstream_is_gone = 0;
|
||||
enum branch_mode_flags base_branch_modes = BRANCH_MODE_PULL | BRANCH_MODE_PUSH;
|
||||
int push_ours, push_theirs, push_sti;
|
||||
char *full_push = NULL;
|
||||
char *push = NULL;
|
||||
enum branch_mode_flags push_branch_modes = 0;
|
||||
|
||||
sti = stat_tracking_info(branch, &ours, &theirs, &full_base, 0, abf);
|
||||
if (sti < 0) {
|
||||
if (!full_base)
|
||||
return 0;
|
||||
upstream_is_gone = 1;
|
||||
}
|
||||
|
||||
base = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
|
||||
full_base, 0);
|
||||
|
||||
push = get_remote_push_branch(branch, &full_push);
|
||||
if (push && strcmp(base, push)) {
|
||||
push_sti = stat_branch_pair(branch->refname, full_push,
|
||||
&push_ours, &push_theirs, abf);
|
||||
if (push_sti >= 0) {
|
||||
base_branch_modes = BRANCH_MODE_PULL;
|
||||
push_branch_modes = BRANCH_MODE_PUSH;
|
||||
}
|
||||
}
|
||||
|
||||
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 branch --unset-upstream\" to fixup)\n"));
|
||||
} else {
|
||||
format_branch_comparison(sb, ours, theirs, base, abf,
|
||||
base_branch_modes, show_divergence_advice);
|
||||
}
|
||||
|
||||
if (push_branch_modes & BRANCH_MODE_PUSH) {
|
||||
strbuf_addstr(sb, "\n");
|
||||
format_branch_comparison(sb, push_ours, push_theirs, push, abf,
|
||||
push_branch_modes, 0);
|
||||
}
|
||||
|
||||
free(base);
|
||||
free(full_push);
|
||||
free(push);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@ -292,4 +292,214 @@ test_expect_success '--set-upstream-to @{-1}' '
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'status tracking origin/main shows only main' '
|
||||
(
|
||||
cd test &&
|
||||
git checkout b4 &&
|
||||
git status >../actual
|
||||
) &&
|
||||
cat >expect <<-EOF &&
|
||||
On branch b4
|
||||
Your branch is ahead of ${SQ}origin/main${SQ} by 2 commits.
|
||||
(use "git push" to publish your local commits)
|
||||
|
||||
nothing to commit, working tree clean
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'status shows ahead of both origin/main and feature branch' '
|
||||
(
|
||||
cd test &&
|
||||
git checkout -b feature2 origin/main &&
|
||||
git push origin HEAD &&
|
||||
advance work &&
|
||||
git status >../actual
|
||||
) &&
|
||||
cat >expect <<-EOF &&
|
||||
On branch feature2
|
||||
Your branch is ahead of ${SQ}origin/main${SQ} by 1 commit.
|
||||
|
||||
Your branch is ahead of ${SQ}origin/feature2${SQ} by 1 commit.
|
||||
(use "git push" to publish your local commits)
|
||||
|
||||
nothing to commit, working tree clean
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'checkout shows ahead of both origin/main and feature branch' '
|
||||
(
|
||||
cd test &&
|
||||
git checkout feature2 >../actual
|
||||
) &&
|
||||
cat >expect <<-EOF &&
|
||||
Your branch is ahead of ${SQ}origin/main${SQ} by 1 commit.
|
||||
|
||||
Your branch is ahead of ${SQ}origin/feature2${SQ} by 1 commit.
|
||||
(use "git push" to publish your local commits)
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'setup for ahead of tracked but diverged from main' '
|
||||
(
|
||||
cd test &&
|
||||
git checkout -b feature4 origin/main &&
|
||||
advance work1 &&
|
||||
git checkout origin/main &&
|
||||
advance work2 &&
|
||||
git push origin HEAD:main &&
|
||||
git checkout feature4 &&
|
||||
advance work3
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'status shows diverged from origin/main and ahead of feature branch' '
|
||||
(
|
||||
cd test &&
|
||||
git checkout feature4 &&
|
||||
git branch --set-upstream-to origin/main &&
|
||||
git push origin HEAD &&
|
||||
advance work &&
|
||||
git status >../actual
|
||||
) &&
|
||||
cat >expect <<-EOF &&
|
||||
On branch feature4
|
||||
Your branch and ${SQ}origin/main${SQ} 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 ${SQ}origin/feature4${SQ} by 1 commit.
|
||||
(use "git push" to publish your local commits)
|
||||
|
||||
nothing to commit, working tree clean
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'setup upstream remote' '
|
||||
(
|
||||
cd test &&
|
||||
git remote add upstream ../. &&
|
||||
git fetch upstream &&
|
||||
git config remote.pushDefault origin
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'status with upstream remote and push.default set to origin' '
|
||||
(
|
||||
cd test &&
|
||||
git checkout -b feature5 upstream/main &&
|
||||
git push origin &&
|
||||
advance work &&
|
||||
git status >../actual
|
||||
) &&
|
||||
cat >expect <<-EOF &&
|
||||
On branch feature5
|
||||
Your branch is ahead of ${SQ}upstream/main${SQ} by 1 commit.
|
||||
|
||||
Your branch is ahead of ${SQ}origin/feature5${SQ} by 1 commit.
|
||||
(use "git push" to publish your local commits)
|
||||
|
||||
nothing to commit, working tree clean
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'status with upstream remote and push.default set to origin and diverged' '
|
||||
(
|
||||
cd test &&
|
||||
git checkout -b feature6 upstream/main &&
|
||||
advance work &&
|
||||
git push origin &&
|
||||
git reset --hard upstream/main &&
|
||||
advance work &&
|
||||
git status >../actual
|
||||
) &&
|
||||
cat >expect <<-EOF &&
|
||||
On branch feature6
|
||||
Your branch is ahead of ${SQ}upstream/main${SQ} by 1 commit.
|
||||
|
||||
Your branch and ${SQ}origin/feature6${SQ} have diverged,
|
||||
and have 1 and 1 different commits each, respectively.
|
||||
|
||||
nothing to commit, working tree clean
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'status with upstream remote and push branch up to date' '
|
||||
(
|
||||
cd test &&
|
||||
git checkout -b feature7 upstream/main &&
|
||||
git push origin &&
|
||||
git status >../actual
|
||||
) &&
|
||||
cat >expect <<-EOF &&
|
||||
On branch feature7
|
||||
Your branch is up to date with ${SQ}upstream/main${SQ}.
|
||||
|
||||
Your branch is up to date with ${SQ}origin/feature7${SQ}.
|
||||
|
||||
nothing to commit, working tree clean
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'checkout shows push branch up to date' '
|
||||
(
|
||||
cd test &&
|
||||
git checkout feature7 >../actual
|
||||
) &&
|
||||
cat >expect <<-EOF &&
|
||||
Your branch is up to date with ${SQ}upstream/main${SQ}.
|
||||
|
||||
Your branch is up to date with ${SQ}origin/feature7${SQ}.
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'status shows remapped push refspec' '
|
||||
(
|
||||
cd test &&
|
||||
git checkout -b feature8 origin/main &&
|
||||
git config remote.origin.push refs/heads/feature8:refs/heads/remapped &&
|
||||
git push &&
|
||||
advance work &&
|
||||
git status >../actual
|
||||
) &&
|
||||
cat >expect <<-EOF &&
|
||||
On branch feature8
|
||||
Your branch is ahead of ${SQ}origin/main${SQ} by 1 commit.
|
||||
|
||||
Your branch is ahead of ${SQ}origin/remapped${SQ} by 1 commit.
|
||||
(use "git push" to publish your local commits)
|
||||
|
||||
nothing to commit, working tree clean
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'status shows remapped push refspec with upstream remote' '
|
||||
(
|
||||
cd test &&
|
||||
git checkout -b feature9 upstream/main &&
|
||||
git config remote.origin.push refs/heads/feature9:refs/heads/remapped &&
|
||||
git push origin &&
|
||||
advance work &&
|
||||
git status >../actual
|
||||
) &&
|
||||
cat >expect <<-EOF &&
|
||||
On branch feature9
|
||||
Your branch is ahead of ${SQ}upstream/main${SQ} by 1 commit.
|
||||
|
||||
Your branch is ahead of ${SQ}origin/remapped${SQ} by 1 commit.
|
||||
(use "git push" to publish your local commits)
|
||||
|
||||
nothing to commit, working tree clean
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user