]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'jc/report-tracking'
authorJunio C Hamano <gitster@pobox.com>
Sun, 13 Jul 2008 22:15:23 +0000 (15:15 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sun, 13 Jul 2008 22:15:23 +0000 (15:15 -0700)
* 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"

builtin-branch.c
builtin-checkout.c
remote.c
remote.h
revision.h
t/t6040-tracking-info.sh [new file with mode: 0755]
wt-status.c

index d279702ba9a90faa020fa2f102d595cec082be21..ff71f3d8a6dd784ae08f89b8a98136e4cd369295 100644 (file)
@@ -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,
index 93ea69bfaa083f49a6e808c7317ea1910b29c847..d6641c2c562c6b35bfed1cd3053945b460472581 100644 (file)
@@ -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)
-               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))
+       if (!format_tracking_info(branch, &sb))
                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)
index ff2c8021678fee5ef12060d35c6eb1960e8792c7..df8bd72ba9b9728aeff0c34208a6492b7d0b692c 100644 (file)
--- a/remote.c
+++ b/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;
+}
index 8eed87ba5ab78eb4635632c21843590467d0d864..091b1d041f8a4d255f59bfc001e098e692dbc15c 100644 (file)
--- a/remote.h
+++ b/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
index 83f364ace240ce22d577f65e7a3f60bf20ccdf2f..f57b6c5d9a832311711e989a63911c886dd1a418 100644 (file)
@@ -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;
diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh
new file mode 100755 (executable)
index 0000000..aac212e
--- /dev/null
@@ -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
index e7d42d0491743d577f84dc262ead721eb82c4785..889e50f89fc24984f700d14f7033600fa9fdf642 100644 (file)
@@ -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) {