Skip to content

Commit e636799

Browse files
committed
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"
2 parents fc062aa + 926ab84 commit e636799

File tree

7 files changed

+244
-90
lines changed

7 files changed

+244
-90
lines changed

builtin-branch.c

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,21 @@ static int ref_cmp(const void *r1, const void *r2)
282282
return strcmp(c1->name, c2->name);
283283
}
284284

285+
static void fill_tracking_info(char *stat, const char *branch_name)
286+
{
287+
int ours, theirs;
288+
struct branch *branch = branch_get(branch_name);
289+
290+
if (!stat_tracking_info(branch, &ours, &theirs) || (!ours && !theirs))
291+
return;
292+
if (!ours)
293+
sprintf(stat, "[behind %d] ", theirs);
294+
else if (!theirs)
295+
sprintf(stat, "[ahead %d] ", ours);
296+
else
297+
sprintf(stat, "[ahead %d, behind %d] ", ours, theirs);
298+
}
299+
285300
static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
286301
int abbrev, int current)
287302
{
@@ -310,19 +325,26 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
310325
if (verbose) {
311326
struct strbuf subject;
312327
const char *sub = " **** invalid ref ****";
328+
char stat[128];
313329

314330
strbuf_init(&subject, 0);
331+
stat[0] = '\0';
315332

316333
commit = lookup_commit(item->sha1);
317334
if (commit && !parse_commit(commit)) {
318335
pretty_print_commit(CMIT_FMT_ONELINE, commit,
319336
&subject, 0, NULL, NULL, 0, 0);
320337
sub = subject.buf;
321338
}
322-
printf("%c %s%-*s%s %s %s\n", c, branch_get_color(color),
339+
340+
if (item->kind == REF_LOCAL_BRANCH)
341+
fill_tracking_info(stat, item->name);
342+
343+
printf("%c %s%-*s%s %s %s%s\n", c, branch_get_color(color),
323344
maxwidth, item->name,
324345
branch_get_color(COLOR_BRANCH_RESET),
325-
find_unique_abbrev(item->sha1, abbrev), sub);
346+
find_unique_abbrev(item->sha1, abbrev),
347+
stat, sub);
326348
strbuf_release(&subject);
327349
} else {
328350
printf("%c %s%s%s\n", c, branch_get_color(color), item->name,

builtin-checkout.c

Lines changed: 6 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -305,97 +305,15 @@ static int merge_working_tree(struct checkout_opts *opts,
305305
return 0;
306306
}
307307

308-
static void report_tracking(struct branch_info *new, struct checkout_opts *opts)
308+
static void report_tracking(struct branch_info *new)
309309
{
310-
/*
311-
* We have switched to a new branch; is it building on
312-
* top of another branch, and if so does that other branch
313-
* have changes we do not have yet?
314-
*/
315-
char *base;
316-
unsigned char sha1[20];
317-
struct commit *ours, *theirs;
318-
char symmetric[84];
319-
struct rev_info revs;
320-
const char *rev_argv[10];
321-
int rev_argc;
322-
int num_ours, num_theirs;
323-
const char *remote_msg;
310+
struct strbuf sb = STRBUF_INIT;
324311
struct branch *branch = branch_get(new->name);
325312

326-
/*
327-
* Nothing to report unless we are marked to build on top of
328-
* somebody else.
329-
*/
330-
if (!branch || !branch->merge || !branch->merge[0] || !branch->merge[0]->dst)
331-
return;
332-
333-
/*
334-
* If what we used to build on no longer exists, there is
335-
* nothing to report.
336-
*/
337-
base = branch->merge[0]->dst;
338-
if (!resolve_ref(base, sha1, 1, NULL))
313+
if (!format_tracking_info(branch, &sb))
339314
return;
340-
341-
theirs = lookup_commit(sha1);
342-
ours = new->commit;
343-
if (!hashcmp(sha1, ours->object.sha1))
344-
return; /* we are the same */
345-
346-
/* Run "rev-list --left-right ours...theirs" internally... */
347-
rev_argc = 0;
348-
rev_argv[rev_argc++] = NULL;
349-
rev_argv[rev_argc++] = "--left-right";
350-
rev_argv[rev_argc++] = symmetric;
351-
rev_argv[rev_argc++] = "--";
352-
rev_argv[rev_argc] = NULL;
353-
354-
strcpy(symmetric, sha1_to_hex(ours->object.sha1));
355-
strcpy(symmetric + 40, "...");
356-
strcpy(symmetric + 43, sha1_to_hex(theirs->object.sha1));
357-
358-
init_revisions(&revs, NULL);
359-
setup_revisions(rev_argc, rev_argv, &revs, NULL);
360-
prepare_revision_walk(&revs);
361-
362-
/* ... and count the commits on each side. */
363-
num_ours = 0;
364-
num_theirs = 0;
365-
while (1) {
366-
struct commit *c = get_revision(&revs);
367-
if (!c)
368-
break;
369-
if (c->object.flags & SYMMETRIC_LEFT)
370-
num_ours++;
371-
else
372-
num_theirs++;
373-
}
374-
375-
if (!prefixcmp(base, "refs/remotes/")) {
376-
remote_msg = " remote";
377-
base += strlen("refs/remotes/");
378-
} else {
379-
remote_msg = "";
380-
}
381-
382-
if (!num_theirs)
383-
printf("Your branch is ahead of the tracked%s branch '%s' "
384-
"by %d commit%s.\n",
385-
remote_msg, base,
386-
num_ours, (num_ours == 1) ? "" : "s");
387-
else if (!num_ours)
388-
printf("Your branch is behind the tracked%s branch '%s' "
389-
"by %d commit%s,\n"
390-
"and can be fast-forwarded.\n",
391-
remote_msg, base,
392-
num_theirs, (num_theirs == 1) ? "" : "s");
393-
else
394-
printf("Your branch and the tracked%s branch '%s' "
395-
"have diverged,\nand respectively "
396-
"have %d and %d different commit(s) each.\n",
397-
remote_msg, base,
398-
num_ours, num_theirs);
315+
fputs(sb.buf, stdout);
316+
strbuf_release(&sb);
399317
}
400318

401319
static void update_refs_for_switch(struct checkout_opts *opts,
@@ -441,7 +359,7 @@ static void update_refs_for_switch(struct checkout_opts *opts,
441359
remove_branch_state();
442360
strbuf_release(&msg);
443361
if (!opts->quiet && (new->path || !strcmp(new->name, "HEAD")))
444-
report_tracking(new, opts);
362+
report_tracking(new);
445363
}
446364

447365
static int switch_branches(struct checkout_opts *opts, struct branch_info *new)

remote.c

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
#include "cache.h"
22
#include "remote.h"
33
#include "refs.h"
4+
#include "commit.h"
5+
#include "diff.h"
6+
#include "revision.h"
47

58
static struct refspec s_tag_refspec = {
69
0,
@@ -1222,3 +1225,117 @@ int resolve_remote_symref(struct ref *ref, struct ref *list)
12221225
}
12231226
return 1;
12241227
}
1228+
1229+
/*
1230+
* Return true if there is anything to report, otherwise false.
1231+
*/
1232+
int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs)
1233+
{
1234+
unsigned char sha1[20];
1235+
struct commit *ours, *theirs;
1236+
char symmetric[84];
1237+
struct rev_info revs;
1238+
const char *rev_argv[10], *base;
1239+
int rev_argc;
1240+
1241+
/*
1242+
* Nothing to report unless we are marked to build on top of
1243+
* somebody else.
1244+
*/
1245+
if (!branch ||
1246+
!branch->merge || !branch->merge[0] || !branch->merge[0]->dst)
1247+
return 0;
1248+
1249+
/*
1250+
* If what we used to build on no longer exists, there is
1251+
* nothing to report.
1252+
*/
1253+
base = branch->merge[0]->dst;
1254+
if (!resolve_ref(base, sha1, 1, NULL))
1255+
return 0;
1256+
theirs = lookup_commit(sha1);
1257+
if (!theirs)
1258+
return 0;
1259+
1260+
if (!resolve_ref(branch->refname, sha1, 1, NULL))
1261+
return 0;
1262+
ours = lookup_commit(sha1);
1263+
if (!ours)
1264+
return 0;
1265+
1266+
/* are we the same? */
1267+
if (theirs == ours)
1268+
return 0;
1269+
1270+
/* Run "rev-list --left-right ours...theirs" internally... */
1271+
rev_argc = 0;
1272+
rev_argv[rev_argc++] = NULL;
1273+
rev_argv[rev_argc++] = "--left-right";
1274+
rev_argv[rev_argc++] = symmetric;
1275+
rev_argv[rev_argc++] = "--";
1276+
rev_argv[rev_argc] = NULL;
1277+
1278+
strcpy(symmetric, sha1_to_hex(ours->object.sha1));
1279+
strcpy(symmetric + 40, "...");
1280+
strcpy(symmetric + 43, sha1_to_hex(theirs->object.sha1));
1281+
1282+
init_revisions(&revs, NULL);
1283+
setup_revisions(rev_argc, rev_argv, &revs, NULL);
1284+
prepare_revision_walk(&revs);
1285+
1286+
/* ... and count the commits on each side. */
1287+
*num_ours = 0;
1288+
*num_theirs = 0;
1289+
while (1) {
1290+
struct commit *c = get_revision(&revs);
1291+
if (!c)
1292+
break;
1293+
if (c->object.flags & SYMMETRIC_LEFT)
1294+
(*num_ours)++;
1295+
else
1296+
(*num_theirs)++;
1297+
}
1298+
1299+
/* clear object flags smudged by the above traversal */
1300+
clear_commit_marks(ours, ALL_REV_FLAGS);
1301+
clear_commit_marks(theirs, ALL_REV_FLAGS);
1302+
return 1;
1303+
}
1304+
1305+
/*
1306+
* Return true when there is anything to report, otherwise false.
1307+
*/
1308+
int format_tracking_info(struct branch *branch, struct strbuf *sb)
1309+
{
1310+
int num_ours, num_theirs;
1311+
const char *base, *remote_msg;
1312+
1313+
if (!stat_tracking_info(branch, &num_ours, &num_theirs))
1314+
return 0;
1315+
1316+
base = branch->merge[0]->dst;
1317+
if (!prefixcmp(base, "refs/remotes/")) {
1318+
remote_msg = " remote";
1319+
base += strlen("refs/remotes/");
1320+
} else {
1321+
remote_msg = "";
1322+
}
1323+
if (!num_theirs)
1324+
strbuf_addf(sb, "Your branch is ahead of the tracked%s branch '%s' "
1325+
"by %d commit%s.\n",
1326+
remote_msg, base,
1327+
num_ours, (num_ours == 1) ? "" : "s");
1328+
else if (!num_ours)
1329+
strbuf_addf(sb, "Your branch is behind the tracked%s branch '%s' "
1330+
"by %d commit%s,\n"
1331+
"and can be fast-forwarded.\n",
1332+
remote_msg, base,
1333+
num_theirs, (num_theirs == 1) ? "" : "s");
1334+
else
1335+
strbuf_addf(sb, "Your branch and the tracked%s branch '%s' "
1336+
"have diverged,\nand respectively "
1337+
"have %d and %d different commit(s) each.\n",
1338+
remote_msg, base,
1339+
num_ours, num_theirs);
1340+
return 1;
1341+
}

remote.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,4 +129,8 @@ enum match_refs_flags {
129129
MATCH_REFS_MIRROR = (1 << 1),
130130
};
131131

132+
/* Reporting of tracking info */
133+
int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs);
134+
int format_tracking_info(struct branch *branch, struct strbuf *sb);
135+
132136
#endif

revision.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#define ADDED (1u<<7) /* Parents already parsed and added? */
1212
#define SYMMETRIC_LEFT (1u<<8)
1313
#define TOPOSORT (1u<<9) /* In the active toposort list.. */
14+
#define ALL_REV_FLAGS ((1u<<10)-1)
1415

1516
struct rev_info;
1617
struct log_info;

t/t6040-tracking-info.sh

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#!/bin/sh
2+
3+
test_description='remote tracking stats'
4+
5+
. ./test-lib.sh
6+
7+
advance () {
8+
echo "$1" >"$1" &&
9+
git add "$1" &&
10+
test_tick &&
11+
git commit -m "$1"
12+
}
13+
14+
test_expect_success setup '
15+
for i in a b c;
16+
do
17+
advance $i || break
18+
done &&
19+
git clone . test &&
20+
(
21+
cd test &&
22+
git checkout -b b1 origin &&
23+
git reset --hard HEAD^ &&
24+
advance d &&
25+
git checkout -b b2 origin &&
26+
git reset --hard b1 &&
27+
git checkout -b b3 origin &&
28+
git reset --hard HEAD^ &&
29+
git checkout -b b4 origin &&
30+
advance e &&
31+
advance f
32+
)
33+
'
34+
35+
script='s/^..\(b.\)[ 0-9a-f]*\[\([^]]*\)\].*/\1 \2/p'
36+
cat >expect <<\EOF
37+
b1 ahead 1, behind 1
38+
b2 ahead 1, behind 1
39+
b3 behind 1
40+
b4 ahead 2
41+
EOF
42+
43+
test_expect_success 'branch -v' '
44+
(
45+
cd test &&
46+
git branch -v
47+
) |
48+
sed -n -e "$script" >actual &&
49+
test_cmp expect actual
50+
'
51+
52+
test_expect_success 'checkout' '
53+
(
54+
cd test && git checkout b1
55+
) >actual &&
56+
grep -e "have 1 and 1 different" actual
57+
'
58+
59+
test_expect_success 'status' '
60+
(
61+
cd test &&
62+
git checkout b1 >/dev/null &&
63+
# reports nothing to commit
64+
test_must_fail git status
65+
) >actual &&
66+
grep -e "have 1 and 1 different" actual
67+
'
68+
69+
70+
test_done

0 commit comments

Comments
 (0)