Merge branch 'jx/branch-vv-always-compare-with-upstream'

"git branch -v -v" (and "git status") did not distinguish among a
branch that does not build on any other branch, a branch that is in
sync with the branch it builds on, and a branch that is configured
to build on some other branch that no longer exists.

* jx/branch-vv-always-compare-with-upstream:
  status: always show tracking branch even no change
  branch: report invalid tracking branch as gone
This commit is contained in:
Junio C Hamano 2013-09-20 12:26:57 -07:00
commit 2e6e3e82ee
4 changed files with 175 additions and 48 deletions

View File

@ -423,19 +423,19 @@ static void fill_tracking_info(struct strbuf *stat, const char *branch_name,
char *ref = NULL; char *ref = NULL;
struct branch *branch = branch_get(branch_name); struct branch *branch = branch_get(branch_name);
struct strbuf fancy = STRBUF_INIT; struct strbuf fancy = STRBUF_INIT;
int upstream_is_gone = 0;
if (!stat_tracking_info(branch, &ours, &theirs)) { switch (stat_tracking_info(branch, &ours, &theirs)) {
if (branch && branch->merge && branch->merge[0]->dst && case 0:
show_upstream_ref) { /* no base */
ref = shorten_unambiguous_ref(branch->merge[0]->dst, 0);
if (want_color(branch_use_color))
strbuf_addf(stat, "[%s%s%s] ",
branch_get_color(BRANCH_COLOR_UPSTREAM),
ref, branch_get_color(BRANCH_COLOR_RESET));
else
strbuf_addf(stat, "[%s] ", ref);
}
return; return;
case -1:
/* with "gone" base */
upstream_is_gone = 1;
break;
default:
/* with base */
break;
} }
if (show_upstream_ref) { if (show_upstream_ref) {
@ -448,19 +448,25 @@ static void fill_tracking_info(struct strbuf *stat, const char *branch_name,
strbuf_addstr(&fancy, ref); strbuf_addstr(&fancy, ref);
} }
if (!ours) { if (upstream_is_gone) {
if (ref) if (show_upstream_ref)
strbuf_addf(stat, _("[%s: gone]"), fancy.buf);
} else if (!ours && !theirs) {
if (show_upstream_ref)
strbuf_addf(stat, _("[%s]"), fancy.buf);
} else if (!ours) {
if (show_upstream_ref)
strbuf_addf(stat, _("[%s: behind %d]"), fancy.buf, theirs); strbuf_addf(stat, _("[%s: behind %d]"), fancy.buf, theirs);
else else
strbuf_addf(stat, _("[behind %d]"), theirs); strbuf_addf(stat, _("[behind %d]"), theirs);
} else if (!theirs) { } else if (!theirs) {
if (ref) if (show_upstream_ref)
strbuf_addf(stat, _("[%s: ahead %d]"), fancy.buf, ours); strbuf_addf(stat, _("[%s: ahead %d]"), fancy.buf, ours);
else else
strbuf_addf(stat, _("[ahead %d]"), ours); strbuf_addf(stat, _("[ahead %d]"), ours);
} else { } else {
if (ref) if (show_upstream_ref)
strbuf_addf(stat, _("[%s: ahead %d, behind %d]"), strbuf_addf(stat, _("[%s: ahead %d, behind %d]"),
fancy.buf, ours, theirs); fancy.buf, ours, theirs);
else else

View File

@ -1729,7 +1729,11 @@ int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1)
} }
/* /*
* Return true if there is anything to report, otherwise false. * Compare a branch with its upstream, and save their differences (number
* of commits) in *num_ours and *num_theirs.
*
* Return 0 if branch has no upstream (no base), -1 if upstream is missing
* (with "gone" base), otherwise 1 (with base).
*/ */
int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs) int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs)
{ {
@ -1740,34 +1744,30 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs)
const char *rev_argv[10], *base; const char *rev_argv[10], *base;
int rev_argc; int rev_argc;
/* /* Cannot stat unless we are marked to build on top of somebody else. */
* Nothing to report unless we are marked to build on top of
* somebody else.
*/
if (!branch || if (!branch ||
!branch->merge || !branch->merge[0] || !branch->merge[0]->dst) !branch->merge || !branch->merge[0] || !branch->merge[0]->dst)
return 0; return 0;
/* /* Cannot stat if what we used to build on no longer exists */
* If what we used to build on no longer exists, there is
* nothing to report.
*/
base = branch->merge[0]->dst; base = branch->merge[0]->dst;
if (read_ref(base, sha1)) if (read_ref(base, sha1))
return 0; return -1;
theirs = lookup_commit_reference(sha1); theirs = lookup_commit_reference(sha1);
if (!theirs) if (!theirs)
return 0; return -1;
if (read_ref(branch->refname, sha1)) if (read_ref(branch->refname, sha1))
return 0; return -1;
ours = lookup_commit_reference(sha1); ours = lookup_commit_reference(sha1);
if (!ours) if (!ours)
return 0; return -1;
/* are we the same? */ /* are we the same? */
if (theirs == ours) if (theirs == ours) {
return 0; *num_theirs = *num_ours = 0;
return 1;
}
/* Run "rev-list --left-right ours...theirs" internally... */ /* Run "rev-list --left-right ours...theirs" internally... */
rev_argc = 0; rev_argc = 0;
@ -1809,31 +1809,53 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs)
*/ */
int format_tracking_info(struct branch *branch, struct strbuf *sb) int format_tracking_info(struct branch *branch, struct strbuf *sb)
{ {
int num_ours, num_theirs; int ours, theirs;
const char *base; const char *base;
int upstream_is_gone = 0;
if (!stat_tracking_info(branch, &num_ours, &num_theirs)) switch (stat_tracking_info(branch, &ours, &theirs)) {
case 0:
/* no base */
return 0; return 0;
case -1:
/* with "gone" base */
upstream_is_gone = 1;
break;
default:
/* with base */
break;
}
base = branch->merge[0]->dst; base = branch->merge[0]->dst;
base = shorten_unambiguous_ref(base, 0); base = shorten_unambiguous_ref(base, 0);
if (!num_theirs) { if (upstream_is_gone) {
strbuf_addf(sb,
_("Your branch is based on '%s', but the upstream is gone.\n"),
base);
if (advice_status_hints)
strbuf_addf(sb,
_(" (use \"git branch --unset-upstream\" to fixup)\n"));
} else if (!ours && !theirs) {
strbuf_addf(sb,
_("Your branch is up-to-date with '%s'.\n"),
base);
} else if (!theirs) {
strbuf_addf(sb, strbuf_addf(sb,
Q_("Your branch is ahead of '%s' by %d commit.\n", Q_("Your branch is ahead of '%s' by %d commit.\n",
"Your branch is ahead of '%s' by %d commits.\n", "Your branch is ahead of '%s' by %d commits.\n",
num_ours), ours),
base, num_ours); base, ours);
if (advice_status_hints) if (advice_status_hints)
strbuf_addf(sb, strbuf_addf(sb,
_(" (use \"git push\" to publish your local commits)\n")); _(" (use \"git push\" to publish your local commits)\n"));
} else if (!num_ours) { } else if (!ours) {
strbuf_addf(sb, strbuf_addf(sb,
Q_("Your branch is behind '%s' by %d commit, " Q_("Your branch is behind '%s' by %d commit, "
"and can be fast-forwarded.\n", "and can be fast-forwarded.\n",
"Your branch is behind '%s' by %d commits, " "Your branch is behind '%s' by %d commits, "
"and can be fast-forwarded.\n", "and can be fast-forwarded.\n",
num_theirs), theirs),
base, num_theirs); base, theirs);
if (advice_status_hints) if (advice_status_hints)
strbuf_addf(sb, strbuf_addf(sb,
_(" (use \"git pull\" to update your local branch)\n")); _(" (use \"git pull\" to update your local branch)\n"));
@ -1845,8 +1867,8 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb)
"Your branch and '%s' have diverged,\n" "Your branch and '%s' have diverged,\n"
"and have %d and %d different commits each, " "and have %d and %d different commits each, "
"respectively.\n", "respectively.\n",
num_theirs), theirs),
base, num_ours, num_theirs); base, ours, theirs);
if (advice_status_hints) if (advice_status_hints)
strbuf_addf(sb, strbuf_addf(sb,
_(" (use \"git pull\" to merge the remote branch into yours)\n")); _(" (use \"git pull\" to merge the remote branch into yours)\n"));

View File

@ -28,10 +28,15 @@ test_expect_success setup '
git reset --hard HEAD^ && git reset --hard HEAD^ &&
git checkout -b b4 origin && git checkout -b b4 origin &&
advance e && advance e &&
advance f advance f &&
git checkout -b brokenbase origin &&
git checkout -b b5 --track brokenbase &&
advance g &&
git branch -d brokenbase &&
git checkout -b b6 origin
) && ) &&
git checkout -b follower --track master && git checkout -b follower --track master &&
advance g advance h
' '
script='s/^..\(b.\)[ 0-9a-f]*\[\([^]]*\)\].*/\1 \2/p' script='s/^..\(b.\)[ 0-9a-f]*\[\([^]]*\)\].*/\1 \2/p'
@ -56,6 +61,8 @@ b1 origin/master: ahead 1, behind 1
b2 origin/master: ahead 1, behind 1 b2 origin/master: ahead 1, behind 1
b3 origin/master: behind 1 b3 origin/master: behind 1
b4 origin/master: ahead 2 b4 origin/master: ahead 2
b5 brokenbase: gone
b6 origin/master
EOF EOF
test_expect_success 'branch -vv' ' test_expect_success 'branch -vv' '
@ -67,7 +74,7 @@ test_expect_success 'branch -vv' '
test_i18ncmp expect actual test_i18ncmp expect actual
' '
test_expect_success 'checkout' ' test_expect_success 'checkout (diverged from upstream)' '
( (
cd test && git checkout b1 cd test && git checkout b1
) >actual && ) >actual &&
@ -80,7 +87,22 @@ test_expect_success 'checkout with local tracked branch' '
test_i18ngrep "is ahead of" actual test_i18ngrep "is ahead of" actual
' '
test_expect_success 'status' ' test_expect_success 'checkout (upstream is gone)' '
(
cd test &&
git checkout b5
) >actual &&
test_i18ngrep "is based on .*, but the upstream is gone." actual
'
test_expect_success 'checkout (up-to-date with upstream)' '
(
cd test && git checkout b6
) >actual &&
test_i18ngrep "Your branch is up-to-date with .origin/master" actual
'
test_expect_success 'status (diverged from upstream)' '
( (
cd test && cd test &&
git checkout b1 >/dev/null && git checkout b1 >/dev/null &&
@ -90,6 +112,65 @@ test_expect_success 'status' '
test_i18ngrep "have 1 and 1 different" actual test_i18ngrep "have 1 and 1 different" actual
' '
test_expect_success 'status (upstream is gone)' '
(
cd test &&
git checkout b5 >/dev/null &&
# reports nothing to commit
test_must_fail git commit --dry-run
) >actual &&
test_i18ngrep "is based on .*, but the upstream is gone." actual
'
test_expect_success 'status (up-to-date with upstream)' '
(
cd test &&
git checkout b6 >/dev/null &&
# reports nothing to commit
test_must_fail git commit --dry-run
) >actual &&
test_i18ngrep "Your branch is up-to-date with .origin/master" actual
'
cat >expect <<\EOF
## b1...origin/master [ahead 1, behind 1]
EOF
test_expect_success 'status -s -b (diverged from upstream)' '
(
cd test &&
git checkout b1 >/dev/null &&
git status -s -b | head -1
) >actual &&
test_i18ncmp expect actual
'
cat >expect <<\EOF
## b5...brokenbase [gone]
EOF
test_expect_success 'status -s -b (upstream is gone)' '
(
cd test &&
git checkout b5 >/dev/null &&
git status -s -b | head -1
) >actual &&
test_i18ncmp expect actual
'
cat >expect <<\EOF
## b6...origin/master
EOF
test_expect_success 'status -s -b (up-to-date with upstream)' '
(
cd test &&
git checkout b6 >/dev/null &&
git status -s -b | head -1
) >actual &&
test_i18ncmp expect actual
'
test_expect_success 'fail to track lightweight tags' ' test_expect_success 'fail to track lightweight tags' '
git checkout master && git checkout master &&
git tag light && git tag light &&

View File

@ -1363,6 +1363,7 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)
const char *base; const char *base;
const char *branch_name; const char *branch_name;
int num_ours, num_theirs; int num_ours, num_theirs;
int upstream_is_gone = 0;
color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "## "); color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "## ");
@ -1380,20 +1381,37 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)
branch = branch_get(s->branch + 11); branch = branch_get(s->branch + 11);
if (s->is_initial) if (s->is_initial)
color_fprintf(s->fp, header_color, _("Initial commit on ")); color_fprintf(s->fp, header_color, _("Initial commit on "));
if (!stat_tracking_info(branch, &num_ours, &num_theirs)) {
color_fprintf(s->fp, branch_color_local, "%s", branch_name); color_fprintf(s->fp, branch_color_local, "%s", branch_name);
switch (stat_tracking_info(branch, &num_ours, &num_theirs)) {
case 0:
/* no base */
fputc(s->null_termination ? '\0' : '\n', s->fp); fputc(s->null_termination ? '\0' : '\n', s->fp);
return; return;
case -1:
/* with "gone" base */
upstream_is_gone = 1;
break;
default:
/* with base */
break;
} }
base = branch->merge[0]->dst; base = branch->merge[0]->dst;
base = shorten_unambiguous_ref(base, 0); base = shorten_unambiguous_ref(base, 0);
color_fprintf(s->fp, branch_color_local, "%s", branch_name);
color_fprintf(s->fp, header_color, "..."); color_fprintf(s->fp, header_color, "...");
color_fprintf(s->fp, branch_color_remote, "%s", base); color_fprintf(s->fp, branch_color_remote, "%s", base);
if (!upstream_is_gone && !num_ours && !num_theirs) {
fputc(s->null_termination ? '\0' : '\n', s->fp);
return;
}
color_fprintf(s->fp, header_color, " ["); color_fprintf(s->fp, header_color, " [");
if (!num_ours) { if (upstream_is_gone) {
color_fprintf(s->fp, header_color, _("gone"));
} else if (!num_ours) {
color_fprintf(s->fp, header_color, _("behind ")); color_fprintf(s->fp, header_color, _("behind "));
color_fprintf(s->fp, branch_color_remote, "%d", num_theirs); color_fprintf(s->fp, branch_color_remote, "%d", num_theirs);
} else if (!num_theirs) { } else if (!num_theirs) {