mirror of
https://github.com/git/git.git
synced 2024-11-27 12:03:55 +08:00
Merge branch 'jc/report-tracking'
* jc/report-tracking: branch -r -v: do not spit out garbage stat_tracking_info(): clear object flags used during counting git-branch -v: show the remote tracking statistics git-status: show the remote tracking statistics Refactor "tracking statistics" code used by "git checkout"
This commit is contained in:
commit
e636799b4d
@ -282,6 +282,21 @@ static int ref_cmp(const void *r1, const void *r2)
|
||||
return strcmp(c1->name, c2->name);
|
||||
}
|
||||
|
||||
static void fill_tracking_info(char *stat, const char *branch_name)
|
||||
{
|
||||
int ours, theirs;
|
||||
struct branch *branch = branch_get(branch_name);
|
||||
|
||||
if (!stat_tracking_info(branch, &ours, &theirs) || (!ours && !theirs))
|
||||
return;
|
||||
if (!ours)
|
||||
sprintf(stat, "[behind %d] ", theirs);
|
||||
else if (!theirs)
|
||||
sprintf(stat, "[ahead %d] ", ours);
|
||||
else
|
||||
sprintf(stat, "[ahead %d, behind %d] ", ours, theirs);
|
||||
}
|
||||
|
||||
static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
|
||||
int abbrev, int current)
|
||||
{
|
||||
@ -310,8 +325,10 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
|
||||
if (verbose) {
|
||||
struct strbuf subject;
|
||||
const char *sub = " **** invalid ref ****";
|
||||
char stat[128];
|
||||
|
||||
strbuf_init(&subject, 0);
|
||||
stat[0] = '\0';
|
||||
|
||||
commit = lookup_commit(item->sha1);
|
||||
if (commit && !parse_commit(commit)) {
|
||||
@ -319,10 +336,15 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
|
||||
&subject, 0, NULL, NULL, 0, 0);
|
||||
sub = subject.buf;
|
||||
}
|
||||
printf("%c %s%-*s%s %s %s\n", c, branch_get_color(color),
|
||||
|
||||
if (item->kind == REF_LOCAL_BRANCH)
|
||||
fill_tracking_info(stat, item->name);
|
||||
|
||||
printf("%c %s%-*s%s %s %s%s\n", c, branch_get_color(color),
|
||||
maxwidth, item->name,
|
||||
branch_get_color(COLOR_BRANCH_RESET),
|
||||
find_unique_abbrev(item->sha1, abbrev), sub);
|
||||
find_unique_abbrev(item->sha1, abbrev),
|
||||
stat, sub);
|
||||
strbuf_release(&subject);
|
||||
} else {
|
||||
printf("%c %s%s%s\n", c, branch_get_color(color), item->name,
|
||||
|
@ -305,97 +305,15 @@ static int merge_working_tree(struct checkout_opts *opts,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void report_tracking(struct branch_info *new, struct checkout_opts *opts)
|
||||
static void report_tracking(struct branch_info *new)
|
||||
{
|
||||
/*
|
||||
* We have switched to a new branch; is it building on
|
||||
* top of another branch, and if so does that other branch
|
||||
* have changes we do not have yet?
|
||||
*/
|
||||
char *base;
|
||||
unsigned char sha1[20];
|
||||
struct commit *ours, *theirs;
|
||||
char symmetric[84];
|
||||
struct rev_info revs;
|
||||
const char *rev_argv[10];
|
||||
int rev_argc;
|
||||
int num_ours, num_theirs;
|
||||
const char *remote_msg;
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
struct branch *branch = branch_get(new->name);
|
||||
|
||||
/*
|
||||
* Nothing to report unless we are marked to build on top of
|
||||
* somebody else.
|
||||
*/
|
||||
if (!branch || !branch->merge || !branch->merge[0] || !branch->merge[0]->dst)
|
||||
if (!format_tracking_info(branch, &sb))
|
||||
return;
|
||||
|
||||
/*
|
||||
* If what we used to build on no longer exists, there is
|
||||
* nothing to report.
|
||||
*/
|
||||
base = branch->merge[0]->dst;
|
||||
if (!resolve_ref(base, sha1, 1, NULL))
|
||||
return;
|
||||
|
||||
theirs = lookup_commit(sha1);
|
||||
ours = new->commit;
|
||||
if (!hashcmp(sha1, ours->object.sha1))
|
||||
return; /* we are the same */
|
||||
|
||||
/* Run "rev-list --left-right ours...theirs" internally... */
|
||||
rev_argc = 0;
|
||||
rev_argv[rev_argc++] = NULL;
|
||||
rev_argv[rev_argc++] = "--left-right";
|
||||
rev_argv[rev_argc++] = symmetric;
|
||||
rev_argv[rev_argc++] = "--";
|
||||
rev_argv[rev_argc] = NULL;
|
||||
|
||||
strcpy(symmetric, sha1_to_hex(ours->object.sha1));
|
||||
strcpy(symmetric + 40, "...");
|
||||
strcpy(symmetric + 43, sha1_to_hex(theirs->object.sha1));
|
||||
|
||||
init_revisions(&revs, NULL);
|
||||
setup_revisions(rev_argc, rev_argv, &revs, NULL);
|
||||
prepare_revision_walk(&revs);
|
||||
|
||||
/* ... and count the commits on each side. */
|
||||
num_ours = 0;
|
||||
num_theirs = 0;
|
||||
while (1) {
|
||||
struct commit *c = get_revision(&revs);
|
||||
if (!c)
|
||||
break;
|
||||
if (c->object.flags & SYMMETRIC_LEFT)
|
||||
num_ours++;
|
||||
else
|
||||
num_theirs++;
|
||||
}
|
||||
|
||||
if (!prefixcmp(base, "refs/remotes/")) {
|
||||
remote_msg = " remote";
|
||||
base += strlen("refs/remotes/");
|
||||
} else {
|
||||
remote_msg = "";
|
||||
}
|
||||
|
||||
if (!num_theirs)
|
||||
printf("Your branch is ahead of the tracked%s branch '%s' "
|
||||
"by %d commit%s.\n",
|
||||
remote_msg, base,
|
||||
num_ours, (num_ours == 1) ? "" : "s");
|
||||
else if (!num_ours)
|
||||
printf("Your branch is behind the tracked%s branch '%s' "
|
||||
"by %d commit%s,\n"
|
||||
"and can be fast-forwarded.\n",
|
||||
remote_msg, base,
|
||||
num_theirs, (num_theirs == 1) ? "" : "s");
|
||||
else
|
||||
printf("Your branch and the tracked%s branch '%s' "
|
||||
"have diverged,\nand respectively "
|
||||
"have %d and %d different commit(s) each.\n",
|
||||
remote_msg, base,
|
||||
num_ours, num_theirs);
|
||||
fputs(sb.buf, stdout);
|
||||
strbuf_release(&sb);
|
||||
}
|
||||
|
||||
static void update_refs_for_switch(struct checkout_opts *opts,
|
||||
@ -441,7 +359,7 @@ static void update_refs_for_switch(struct checkout_opts *opts,
|
||||
remove_branch_state();
|
||||
strbuf_release(&msg);
|
||||
if (!opts->quiet && (new->path || !strcmp(new->name, "HEAD")))
|
||||
report_tracking(new, opts);
|
||||
report_tracking(new);
|
||||
}
|
||||
|
||||
static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
|
||||
|
117
remote.c
117
remote.c
@ -1,6 +1,9 @@
|
||||
#include "cache.h"
|
||||
#include "remote.h"
|
||||
#include "refs.h"
|
||||
#include "commit.h"
|
||||
#include "diff.h"
|
||||
#include "revision.h"
|
||||
|
||||
static struct refspec s_tag_refspec = {
|
||||
0,
|
||||
@ -1222,3 +1225,117 @@ int resolve_remote_symref(struct ref *ref, struct ref *list)
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true if there is anything to report, otherwise false.
|
||||
*/
|
||||
int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs)
|
||||
{
|
||||
unsigned char sha1[20];
|
||||
struct commit *ours, *theirs;
|
||||
char symmetric[84];
|
||||
struct rev_info revs;
|
||||
const char *rev_argv[10], *base;
|
||||
int rev_argc;
|
||||
|
||||
/*
|
||||
* Nothing to report unless we are marked to build on top of
|
||||
* somebody else.
|
||||
*/
|
||||
if (!branch ||
|
||||
!branch->merge || !branch->merge[0] || !branch->merge[0]->dst)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If what we used to build on no longer exists, there is
|
||||
* nothing to report.
|
||||
*/
|
||||
base = branch->merge[0]->dst;
|
||||
if (!resolve_ref(base, sha1, 1, NULL))
|
||||
return 0;
|
||||
theirs = lookup_commit(sha1);
|
||||
if (!theirs)
|
||||
return 0;
|
||||
|
||||
if (!resolve_ref(branch->refname, sha1, 1, NULL))
|
||||
return 0;
|
||||
ours = lookup_commit(sha1);
|
||||
if (!ours)
|
||||
return 0;
|
||||
|
||||
/* are we the same? */
|
||||
if (theirs == ours)
|
||||
return 0;
|
||||
|
||||
/* Run "rev-list --left-right ours...theirs" internally... */
|
||||
rev_argc = 0;
|
||||
rev_argv[rev_argc++] = NULL;
|
||||
rev_argv[rev_argc++] = "--left-right";
|
||||
rev_argv[rev_argc++] = symmetric;
|
||||
rev_argv[rev_argc++] = "--";
|
||||
rev_argv[rev_argc] = NULL;
|
||||
|
||||
strcpy(symmetric, sha1_to_hex(ours->object.sha1));
|
||||
strcpy(symmetric + 40, "...");
|
||||
strcpy(symmetric + 43, sha1_to_hex(theirs->object.sha1));
|
||||
|
||||
init_revisions(&revs, NULL);
|
||||
setup_revisions(rev_argc, rev_argv, &revs, NULL);
|
||||
prepare_revision_walk(&revs);
|
||||
|
||||
/* ... and count the commits on each side. */
|
||||
*num_ours = 0;
|
||||
*num_theirs = 0;
|
||||
while (1) {
|
||||
struct commit *c = get_revision(&revs);
|
||||
if (!c)
|
||||
break;
|
||||
if (c->object.flags & SYMMETRIC_LEFT)
|
||||
(*num_ours)++;
|
||||
else
|
||||
(*num_theirs)++;
|
||||
}
|
||||
|
||||
/* clear object flags smudged by the above traversal */
|
||||
clear_commit_marks(ours, ALL_REV_FLAGS);
|
||||
clear_commit_marks(theirs, ALL_REV_FLAGS);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true when there is anything to report, otherwise false.
|
||||
*/
|
||||
int format_tracking_info(struct branch *branch, struct strbuf *sb)
|
||||
{
|
||||
int num_ours, num_theirs;
|
||||
const char *base, *remote_msg;
|
||||
|
||||
if (!stat_tracking_info(branch, &num_ours, &num_theirs))
|
||||
return 0;
|
||||
|
||||
base = branch->merge[0]->dst;
|
||||
if (!prefixcmp(base, "refs/remotes/")) {
|
||||
remote_msg = " remote";
|
||||
base += strlen("refs/remotes/");
|
||||
} else {
|
||||
remote_msg = "";
|
||||
}
|
||||
if (!num_theirs)
|
||||
strbuf_addf(sb, "Your branch is ahead of the tracked%s branch '%s' "
|
||||
"by %d commit%s.\n",
|
||||
remote_msg, base,
|
||||
num_ours, (num_ours == 1) ? "" : "s");
|
||||
else if (!num_ours)
|
||||
strbuf_addf(sb, "Your branch is behind the tracked%s branch '%s' "
|
||||
"by %d commit%s,\n"
|
||||
"and can be fast-forwarded.\n",
|
||||
remote_msg, base,
|
||||
num_theirs, (num_theirs == 1) ? "" : "s");
|
||||
else
|
||||
strbuf_addf(sb, "Your branch and the tracked%s branch '%s' "
|
||||
"have diverged,\nand respectively "
|
||||
"have %d and %d different commit(s) each.\n",
|
||||
remote_msg, base,
|
||||
num_ours, num_theirs);
|
||||
return 1;
|
||||
}
|
||||
|
4
remote.h
4
remote.h
@ -129,4 +129,8 @@ enum match_refs_flags {
|
||||
MATCH_REFS_MIRROR = (1 << 1),
|
||||
};
|
||||
|
||||
/* Reporting of tracking info */
|
||||
int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs);
|
||||
int format_tracking_info(struct branch *branch, struct strbuf *sb);
|
||||
|
||||
#endif
|
||||
|
@ -11,6 +11,7 @@
|
||||
#define ADDED (1u<<7) /* Parents already parsed and added? */
|
||||
#define SYMMETRIC_LEFT (1u<<8)
|
||||
#define TOPOSORT (1u<<9) /* In the active toposort list.. */
|
||||
#define ALL_REV_FLAGS ((1u<<10)-1)
|
||||
|
||||
struct rev_info;
|
||||
struct log_info;
|
||||
|
70
t/t6040-tracking-info.sh
Executable file
70
t/t6040-tracking-info.sh
Executable file
@ -0,0 +1,70 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='remote tracking stats'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
advance () {
|
||||
echo "$1" >"$1" &&
|
||||
git add "$1" &&
|
||||
test_tick &&
|
||||
git commit -m "$1"
|
||||
}
|
||||
|
||||
test_expect_success setup '
|
||||
for i in a b c;
|
||||
do
|
||||
advance $i || break
|
||||
done &&
|
||||
git clone . test &&
|
||||
(
|
||||
cd test &&
|
||||
git checkout -b b1 origin &&
|
||||
git reset --hard HEAD^ &&
|
||||
advance d &&
|
||||
git checkout -b b2 origin &&
|
||||
git reset --hard b1 &&
|
||||
git checkout -b b3 origin &&
|
||||
git reset --hard HEAD^ &&
|
||||
git checkout -b b4 origin &&
|
||||
advance e &&
|
||||
advance f
|
||||
)
|
||||
'
|
||||
|
||||
script='s/^..\(b.\)[ 0-9a-f]*\[\([^]]*\)\].*/\1 \2/p'
|
||||
cat >expect <<\EOF
|
||||
b1 ahead 1, behind 1
|
||||
b2 ahead 1, behind 1
|
||||
b3 behind 1
|
||||
b4 ahead 2
|
||||
EOF
|
||||
|
||||
test_expect_success 'branch -v' '
|
||||
(
|
||||
cd test &&
|
||||
git branch -v
|
||||
) |
|
||||
sed -n -e "$script" >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'checkout' '
|
||||
(
|
||||
cd test && git checkout b1
|
||||
) >actual &&
|
||||
grep -e "have 1 and 1 different" actual
|
||||
'
|
||||
|
||||
test_expect_success 'status' '
|
||||
(
|
||||
cd test &&
|
||||
git checkout b1 >/dev/null &&
|
||||
# reports nothing to commit
|
||||
test_must_fail git status
|
||||
) >actual &&
|
||||
grep -e "have 1 and 1 different" actual
|
||||
'
|
||||
|
||||
|
||||
test_done
|
22
wt-status.c
22
wt-status.c
@ -9,6 +9,7 @@
|
||||
#include "diffcore.h"
|
||||
#include "quote.h"
|
||||
#include "run-command.h"
|
||||
#include "remote.h"
|
||||
|
||||
int wt_status_relative_paths = 1;
|
||||
int wt_status_use_color = -1;
|
||||
@ -315,6 +316,25 @@ static void wt_status_print_verbose(struct wt_status *s)
|
||||
run_diff_index(&rev, 1);
|
||||
}
|
||||
|
||||
static void wt_status_print_tracking(struct wt_status *s)
|
||||
{
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
const char *cp, *ep;
|
||||
struct branch *branch;
|
||||
|
||||
assert(s->branch && !s->is_initial);
|
||||
if (prefixcmp(s->branch, "refs/heads/"))
|
||||
return;
|
||||
branch = branch_get(s->branch + 11);
|
||||
if (!format_tracking_info(branch, &sb))
|
||||
return;
|
||||
|
||||
for (cp = sb.buf; (ep = strchr(cp, '\n')) != NULL; cp = ep + 1)
|
||||
color_fprintf_ln(s->fp, color(WT_STATUS_HEADER),
|
||||
"# %.*s", (int)(ep - cp), cp);
|
||||
color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
|
||||
}
|
||||
|
||||
void wt_status_print(struct wt_status *s)
|
||||
{
|
||||
unsigned char sha1[20];
|
||||
@ -333,6 +353,8 @@ void wt_status_print(struct wt_status *s)
|
||||
}
|
||||
color_fprintf(s->fp, color(WT_STATUS_HEADER), "# ");
|
||||
color_fprintf_ln(s->fp, branch_color, "%s%s", on_what, branch_name);
|
||||
if (!s->is_initial)
|
||||
wt_status_print_tracking(s);
|
||||
}
|
||||
|
||||
if (s->is_initial) {
|
||||
|
Loading…
Reference in New Issue
Block a user