abbrev: prepare for new world order
[gitweb.git] / sha1_name.c
index acea241139d29f75bd2494e7bff3c2e6fb3976a2..3b647fd7cf79127cfa6f684836e387568a87c310 100644 (file)
@@ -7,6 +7,7 @@
 #include "refs.h"
 #include "remote.h"
 #include "dir.h"
+#include "sha1-array.h"
 
 static int get_sha1_oneline(const char *, unsigned char *, struct commit_list *);
 
@@ -282,6 +283,36 @@ static int disambiguate_blob_only(const unsigned char *sha1, void *cb_data_unuse
        return kind == OBJ_BLOB;
 }
 
+static disambiguate_hint_fn default_disambiguate_hint;
+
+int set_disambiguate_hint_config(const char *var, const char *value)
+{
+       static const struct {
+               const char *name;
+               disambiguate_hint_fn fn;
+       } hints[] = {
+               { "none", NULL },
+               { "commit", disambiguate_commit_only },
+               { "committish", disambiguate_committish_only },
+               { "tree", disambiguate_tree_only },
+               { "treeish", disambiguate_treeish_only },
+               { "blob", disambiguate_blob_only }
+       };
+       int i;
+
+       if (!value)
+               return config_error_nonbool(var);
+
+       for (i = 0; i < ARRAY_SIZE(hints); i++) {
+               if (!strcasecmp(value, hints[i].name)) {
+                       default_disambiguate_hint = hints[i].fn;
+                       return 0;
+               }
+       }
+
+       return error("unknown hint type for '%s': %s", var, value);
+}
+
 static int init_object_disambiguation(const char *name, int len,
                                      struct disambiguate_state *ds)
 {
@@ -317,6 +348,38 @@ static int init_object_disambiguation(const char *name, int len,
        return 0;
 }
 
+static int show_ambiguous_object(const unsigned char *sha1, void *data)
+{
+       const struct disambiguate_state *ds = data;
+       struct strbuf desc = STRBUF_INIT;
+       int type;
+
+       if (ds->fn && !ds->fn(sha1, ds->cb_data))
+               return 0;
+
+       type = sha1_object_info(sha1, NULL);
+       if (type == OBJ_COMMIT) {
+               struct commit *commit = lookup_commit(sha1);
+               if (commit) {
+                       struct pretty_print_context pp = {0};
+                       pp.date_mode.type = DATE_SHORT;
+                       format_commit_message(commit, " %ad - %s", &desc, &pp);
+               }
+       } else if (type == OBJ_TAG) {
+               struct tag *tag = lookup_tag(sha1);
+               if (!parse_tag(tag) && tag->tag)
+                       strbuf_addf(&desc, " %s", tag->tag);
+       }
+
+       advise("  %s %s%s",
+              find_unique_abbrev(sha1, DEFAULT_ABBREV),
+              typename(type) ? typename(type) : "unknown type",
+              desc.buf);
+
+       strbuf_release(&desc);
+       return 0;
+}
+
 static int get_short_sha1(const char *name, int len, unsigned char *sha1,
                          unsigned flags)
 {
@@ -340,30 +403,56 @@ static int get_short_sha1(const char *name, int len, unsigned char *sha1,
                ds.fn = disambiguate_treeish_only;
        else if (flags & GET_SHA1_BLOB)
                ds.fn = disambiguate_blob_only;
+       else
+               ds.fn = default_disambiguate_hint;
 
        find_short_object_filename(&ds);
        find_short_packed_object(&ds);
        status = finish_object_disambiguation(&ds, sha1);
 
-       if (!quietly && (status == SHORT_NAME_AMBIGUOUS))
-               return error(_("short SHA1 %s is ambiguous"), ds.hex_pfx);
+       if (!quietly && (status == SHORT_NAME_AMBIGUOUS)) {
+               error(_("short SHA1 %s is ambiguous"), ds.hex_pfx);
+
+               /*
+                * We may still have ambiguity if we simply saw a series of
+                * candidates that did not satisfy our hint function. In
+                * that case, we still want to show them, so disable the hint
+                * function entirely.
+                */
+               if (!ds.ambiguous)
+                       ds.fn = NULL;
+
+               advise(_("The candidates are:"));
+               for_each_abbrev(ds.hex_pfx, show_ambiguous_object, &ds);
+       }
+
        return status;
 }
 
+static int collect_ambiguous(const unsigned char *sha1, void *data)
+{
+       sha1_array_append(data, sha1);
+       return 0;
+}
+
 int for_each_abbrev(const char *prefix, each_abbrev_fn fn, void *cb_data)
 {
+       struct sha1_array collect = SHA1_ARRAY_INIT;
        struct disambiguate_state ds;
+       int ret;
 
        if (init_object_disambiguation(prefix, strlen(prefix), &ds) < 0)
                return -1;
 
        ds.always_call_fn = 1;
-       ds.cb_data = cb_data;
-       ds.fn = fn;
-
+       ds.fn = collect_ambiguous;
+       ds.cb_data = &collect;
        find_short_object_filename(&ds);
        find_short_packed_object(&ds);
-       return ds.ambiguous;
+
+       ret = sha1_array_for_each_unique(&collect, fn, cb_data);
+       sha1_array_clear(&collect);
+       return ret;
 }
 
 int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len)