Merge branch 'sb/describe-blob'
[gitweb.git] / builtin / describe.c
index e14e162ef6ef829072f01f89ab2c797256a6861d..3b0b204b1e495626f3490d62f281291ee039ba42 100644 (file)
@@ -3,6 +3,7 @@
 #include "lockfile.h"
 #include "commit.h"
 #include "tag.h"
+#include "blob.h"
 #include "refs.h"
 #include "builtin.h"
 #include "exec_cmd.h"
@@ -12,6 +13,8 @@
 #include "hashmap.h"
 #include "argv-array.h"
 #include "run-command.h"
+#include "revision.h"
+#include "list-objects.h"
 
 #define MAX_TAGS       (FLAG_BITS - 1)
 
@@ -256,7 +259,7 @@ static unsigned long finish_depth_computation(
        return seen_commits;
 }
 
-static void display_name(struct commit_name *n)
+static void append_name(struct commit_name *n, struct strbuf *dst)
 {
        if (n->prio == 2 && !n->tag) {
                n->tag = lookup_tag(&n->oid);
@@ -272,19 +275,18 @@ static void display_name(struct commit_name *n)
        }
 
        if (n->tag)
-               printf("%s", n->tag->tag);
+               strbuf_addstr(dst, n->tag->tag);
        else
-               printf("%s", n->path);
+               strbuf_addstr(dst, n->path);
 }
 
-static void show_suffix(int depth, const struct object_id *oid)
+static void append_suffix(int depth, const struct object_id *oid, struct strbuf *dst)
 {
-       printf("-%d-g%s", depth, find_unique_abbrev(oid->hash, abbrev));
+       strbuf_addf(dst, "-%d-g%s", depth, find_unique_abbrev(oid->hash, abbrev));
 }
 
-static void describe(const char *arg, int last_one)
+static void describe_commit(struct object_id *oid, struct strbuf *dst)
 {
-       struct object_id oid;
        struct commit *cmit, *gave_up_on = NULL;
        struct commit_list *list;
        struct commit_name *n;
@@ -293,30 +295,25 @@ static void describe(const char *arg, int last_one)
        unsigned long seen_commits = 0;
        unsigned int unannotated_cnt = 0;
 
-       if (get_oid(arg, &oid))
-               die(_("Not a valid object name %s"), arg);
-       cmit = lookup_commit_reference(&oid);
-       if (!cmit)
-               die(_("%s is not a valid '%s' object"), arg, commit_type);
+       cmit = lookup_commit_reference(oid);
 
        n = find_commit_name(&cmit->object.oid);
        if (n && (tags || all || n->prio == 2)) {
                /*
                 * Exact match to an existing ref.
                 */
-               display_name(n);
+               append_name(n, dst);
                if (longformat)
-                       show_suffix(0, n->tag ? &n->tag->tagged->oid : &oid);
+                       append_suffix(0, n->tag ? &n->tag->tagged->oid : oid, dst);
                if (suffix)
-                       printf("%s", suffix);
-               printf("\n");
+                       strbuf_addstr(dst, suffix);
                return;
        }
 
        if (!max_candidates)
                die(_("no tag exactly matches '%s'"), oid_to_hex(&cmit->object.oid));
        if (debug)
-               fprintf(stderr, _("searching to describe %s\n"), arg);
+               fprintf(stderr, _("No exact match on refs or tags, searching to describe\n"));
 
        if (!have_util) {
                struct hashmap_iter iter;
@@ -381,22 +378,21 @@ static void describe(const char *arg, int last_one)
        }
 
        if (!match_cnt) {
-               struct object_id *oid = &cmit->object.oid;
+               struct object_id *cmit_oid = &cmit->object.oid;
                if (always) {
-                       printf("%s", find_unique_abbrev(oid->hash, abbrev));
+                       strbuf_addstr(dst, find_unique_abbrev(cmit_oid->hash, abbrev));
                        if (suffix)
-                               printf("%s", suffix);
-                       printf("\n");
+                               strbuf_addstr(dst, suffix);
                        return;
                }
                if (unannotated_cnt)
                        die(_("No annotated tags can describe '%s'.\n"
                            "However, there were unannotated tags: try --tags."),
-                           oid_to_hex(oid));
+                           oid_to_hex(cmit_oid));
                else
                        die(_("No tags can describe '%s'.\n"
                            "Try --always, or create some tags."),
-                           oid_to_hex(oid));
+                           oid_to_hex(cmit_oid));
        }
 
        QSORT(all_matches, match_cnt, compare_pt);
@@ -434,15 +430,86 @@ static void describe(const char *arg, int last_one)
                }
        }
 
-       display_name(all_matches[0].name);
+       append_name(all_matches[0].name, dst);
        if (abbrev)
-               show_suffix(all_matches[0].depth, &cmit->object.oid);
+               append_suffix(all_matches[0].depth, &cmit->object.oid, dst);
        if (suffix)
-               printf("%s", suffix);
-       printf("\n");
+               strbuf_addstr(dst, suffix);
+}
+
+struct process_commit_data {
+       struct object_id current_commit;
+       struct object_id looking_for;
+       struct strbuf *dst;
+       struct rev_info *revs;
+};
+
+static void process_commit(struct commit *commit, void *data)
+{
+       struct process_commit_data *pcd = data;
+       pcd->current_commit = commit->object.oid;
+}
+
+static void process_object(struct object *obj, const char *path, void *data)
+{
+       struct process_commit_data *pcd = data;
+
+       if (!oidcmp(&pcd->looking_for, &obj->oid) && !pcd->dst->len) {
+               reset_revision_walk();
+               describe_commit(&pcd->current_commit, pcd->dst);
+               strbuf_addf(pcd->dst, ":%s", path);
+               free_commit_list(pcd->revs->commits);
+               pcd->revs->commits = NULL;
+       }
+}
+
+static void describe_blob(struct object_id oid, struct strbuf *dst)
+{
+       struct rev_info revs;
+       struct argv_array args = ARGV_ARRAY_INIT;
+       struct process_commit_data pcd = { null_oid, oid, dst, &revs};
+
+       argv_array_pushl(&args, "internal: The first arg is not parsed",
+               "--objects", "--in-commit-order", "--reverse", "HEAD",
+               NULL);
+
+       init_revisions(&revs, NULL);
+       if (setup_revisions(args.argc, args.argv, &revs, NULL) > 1)
+               BUG("setup_revisions could not handle all args?");
+
+       if (prepare_revision_walk(&revs))
+               die("revision walk setup failed");
+
+       traverse_commit_list(&revs, process_commit, process_object, &pcd);
+       reset_revision_walk();
+}
+
+static void describe(const char *arg, int last_one)
+{
+       struct object_id oid;
+       struct commit *cmit;
+       struct strbuf sb = STRBUF_INIT;
+
+       if (debug)
+               fprintf(stderr, _("describe %s\n"), arg);
+
+       if (get_oid(arg, &oid))
+               die(_("Not a valid object name %s"), arg);
+       cmit = lookup_commit_reference_gently(&oid, 1);
+
+       if (cmit)
+               describe_commit(&oid, &sb);
+       else if (lookup_blob(&oid))
+               describe_blob(oid, &sb);
+       else
+               die(_("%s is neither a commit nor blob"), arg);
+
+       puts(sb.buf);
 
        if (!last_one)
                clear_commit_marks(cmit, -1);
+
+       strbuf_release(&sb);
 }
 
 int cmd_describe(int argc, const char **argv, const char *prefix)