Merge branch 'maint-1.6.0' into maint
[gitweb.git] / builtin-describe.c
index 7542b5705c4e0f3d214d3f3ff7367ee03bf82b9f..71be2a9364748668996696f6c74057dba43315b5 100644 (file)
@@ -5,12 +5,14 @@
 #include "builtin.h"
 #include "exec_cmd.h"
 #include "parse-options.h"
+#include "diff.h"
 
 #define SEEN           (1u<<0)
 #define MAX_TAGS       (FLAG_BITS - 1)
 
 static const char * const describe_usage[] = {
        "git describe [options] <committish>*",
+       "git describe [options] --dirty",
        NULL
 };
 
@@ -23,6 +25,13 @@ static int max_candidates = 10;
 static int found_names;
 static const char *pattern;
 static int always;
+static const char *dirty;
+
+/* diff-index command arguments to check if working tree is dirty. */
+static const char *diff_index_args[] = {
+       "diff-index", "--quiet", "HEAD", "--", NULL
+};
+
 
 struct commit_name {
        struct tag *tag;
@@ -96,8 +105,6 @@ static int get_name(const char *path, const unsigned char *sha1, int flag, void
        if (!all) {
                if (!prio)
                        return 0;
-               if (!tags && prio < 2)
-                       return 0;
        }
        add_to_known_names(all ? path + 5 : path + 10, commit, prio, sha1);
        return 0;
@@ -180,11 +187,11 @@ static void describe(const char *arg, int last_one)
        unsigned char sha1[20];
        struct commit *cmit, *gave_up_on = NULL;
        struct commit_list *list;
-       static int initialized = 0;
        struct commit_name *n;
        struct possible_tag all_matches[MAX_TAGS];
        unsigned int match_cnt = 0, annotated_cnt = 0, cur_match;
        unsigned long seen_commits = 0;
+       unsigned int unannotated_cnt = 0;
 
        if (get_sha1(arg, sha1))
                die("Not a valid object name %s", arg);
@@ -192,22 +199,16 @@ static void describe(const char *arg, int last_one)
        if (!cmit)
                die("%s is not a valid '%s' object", arg, commit_type);
 
-       if (!initialized) {
-               initialized = 1;
-               for_each_ref(get_name, NULL);
-       }
-
-       if (!found_names && !always)
-               die("cannot describe '%s'", sha1_to_hex(sha1));
-
        n = cmit->util;
-       if (n) {
+       if (n && (tags || all || n->prio == 2)) {
                /*
                 * Exact match to an existing ref.
                 */
                display_name(n);
                if (longformat)
                        show_suffix(0, n->tag ? n->tag->tagged->sha1 : sha1);
+               if (dirty)
+                       printf("%s", dirty);
                printf("\n");
                return;
        }
@@ -226,7 +227,9 @@ static void describe(const char *arg, int last_one)
                seen_commits++;
                n = c->util;
                if (n) {
-                       if (match_cnt < max_candidates) {
+                       if (!tags && !all && n->prio < 2) {
+                               unannotated_cnt++;
+                       } else if (match_cnt < max_candidates) {
                                struct possible_tag *t = &all_matches[match_cnt++];
                                t->name = n;
                                t->depth = seen_commits - 1;
@@ -265,10 +268,20 @@ static void describe(const char *arg, int last_one)
        if (!match_cnt) {
                const unsigned char *sha1 = cmit->object.sha1;
                if (always) {
-                       printf("%s\n", find_unique_abbrev(sha1, abbrev));
+                       printf("%s", find_unique_abbrev(sha1, abbrev));
+                       if (dirty)
+                               printf("%s", dirty);
+                       printf("\n");
                        return;
                }
-               die("cannot describe '%s'", sha1_to_hex(sha1));
+               if (unannotated_cnt)
+                       die("No annotated tags can describe '%s'.\n"
+                           "However, there were unannotated tags: try --tags.",
+                           sha1_to_hex(sha1));
+               else
+                       die("No tags can describe '%s'.\n"
+                           "Try --always, or create some tags.",
+                           sha1_to_hex(sha1));
        }
 
        qsort(all_matches, match_cnt, sizeof(all_matches[0]), compare_pt);
@@ -300,6 +313,8 @@ static void describe(const char *arg, int last_one)
        display_name(all_matches[0].name);
        if (abbrev)
                show_suffix(all_matches[0].depth, cmit->object.sha1);
+       if (dirty)
+               printf("%s", dirty);
        printf("\n");
 
        if (!last_one)
@@ -324,6 +339,9 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
                           "only consider tags matching <pattern>"),
                OPT_BOOLEAN(0, "always",     &always,
                           "show abbreviated commit object as fallback"),
+               {OPTION_STRING, 0, "dirty",  &dirty, "mark",
+                          "append <mark> on dirty working tree (default: \"-dirty\")",
+                PARSE_OPT_OPTARG, NULL, (intptr_t) "-dirty"},
                OPT_END(),
        };
 
@@ -359,8 +377,16 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
                return cmd_name_rev(i + argc, args, prefix);
        }
 
+       for_each_ref(get_name, NULL);
+       if (!found_names && !always)
+               die("No names found, cannot describe anything.");
+
        if (argc == 0) {
+               if (dirty && !cmd_diff_index(ARRAY_SIZE(diff_index_args) - 1, diff_index_args, prefix))
+                       dirty = NULL;
                describe("HEAD", 1);
+       } else if (dirty) {
+               die("--dirty is incompatible with committishes");
        } else {
                while (argc-- > 0) {
                        describe(*argv++, argc == 0);