Re: [PATCH] builtin-reset.c: Extend hard reset error message when using paths.
From: Jeff King <hidden>
Date: 2016-06-15 22:46:36
Subsystem:
the rest · Maintainer:
Linus Torvalds
Possibly related (same subject, not in this thread)
- 2016-06-15 · [PATCH] builtin-reset.c: Extend hard reset error message when using paths. · Tim Retout <hidden>
On Mon, Apr 13, 2009 at 12:51:21PM -0700, Junio C Hamano wrote:
That is one possibility. Another is:
git reset --hard mester
(and you have ./mester file in the work tree) and in that case the user
definitely didn't want to do any checkout.
I wonder if you can tell these cases apart, and also if this (not just
telling these apart, but what your patch adds) is worth additionalYou could always guess based on the existing refs: ---
diff --git a/builtin-reset.c b/builtin-reset.c
index c0cb915..803957d 100644
--- a/builtin-reset.c
+++ b/builtin-reset.c@@ -243,8 +243,11 @@ int cmd_reset(int argc, const char **argv, const char *prefix) } } - if (get_sha1(rev, sha1)) - die("Failed to resolve '%s' as a valid ref.", rev); + if (get_sha1(rev, sha1)) { + error("Failed to resolve '%s' as a valid ref.", rev); + probable_refs_print(rev); + return 1; + } commit = lookup_commit_reference(sha1); if (!commit)
@@ -257,6 +260,11 @@ int cmd_reset(int argc, const char **argv, const char *prefix) if (i < argc) { if (reset_type == MIXED) warning("--mixed option is deprecated with paths."); + else if (reset_type == HARD) { + error("Cannot do hard reset with paths."); + probable_refs_print(argv[i]); + return 1; + } else if (reset_type != NONE) die("Cannot do %s reset with paths.", reset_type_names[reset_type]);
diff --git a/refs.c b/refs.c
index 82afb87..bad7e2f 100644
--- a/refs.c
+++ b/refs.c@@ -3,6 +3,8 @@ #include "object.h" #include "tag.h" #include "dir.h" +#include "levenshtein.h" +#include "string-list.h" /* ISSYMREF=01 and ISPACKED=02 are public interfaces */ #define REF_KNOWS_PEELED 04
@@ -750,9 +752,8 @@ int check_ref_format(const char *ref) } } -const char *prettify_ref(const struct ref *ref) +static const char *prettify_refname(const char *name) { - const char *name = ref->name; return name + ( !prefixcmp(name, "refs/heads/") ? 11 : !prefixcmp(name, "refs/tags/") ? 10 :
@@ -760,6 +761,11 @@ const char *prettify_ref(const struct ref *ref) 0); } +const char *prettify_ref(const struct ref *ref) +{ + return prettify_refname(ref->name); +} + const char *ref_rev_parse_rules[] = { "%.*s", "refs/%.*s",
@@ -1756,3 +1762,74 @@ char *shorten_unambiguous_ref(const char *ref) free(short_name); return xstrdup(ref); } + +struct probable_refs_data { + const char *s; + struct string_list *out; +}; + +static void probable_refs_append(struct probable_refs_data *d, const char *s) +{ + struct string_list_item *i = string_list_append(s, d->out); + i->util = (void *)levenshtein(d->s, s, 0, 2, 1, 4); +} + +static int probable_refs_cb(const char *refname, const unsigned char *sha1, + int flag, void *data) +{ + const char *p = prettify_refname(refname); + probable_refs_append(data, refname); + if (p != refname) + probable_refs_append(data, p); + return 1; +} + +static int util_compare(const void *va, const void *vb) +{ + const struct string_list_item *a = va, *b = vb; + return a->util - b->util; +} + +void probable_refs(const char *s, struct string_list *out) +{ + struct probable_refs_data d; + int i, j; + + memset(out, 0, sizeof(*out)); + out->strdup_strings = 1; + d.s = s; + d.out = out; + for_each_ref(probable_refs_cb, &d); + + if (!out->nr) + return; + + qsort(out->items, out->nr, sizeof(*out->items), util_compare); + /* Arbitrary cutoff at distance '5'. The first is the best match, but + * there may be others which are equally good */ + for (i = 0; i < out->nr; i++) + if ((int)out->items[i].util > 5 || + out->items[i].util > out->items[0].util) + break; + /* truncate the list to i items */ + for (j = i; j < out->nr; j++) + free(out->items[j].string); + out->nr = i; +} + +void probable_refs_print(const char *name) +{ + struct string_list s; + int i; + + probable_refs(name, &s); + if (!s.nr) + return; + + fprintf(stderr, "\nDid you mean %s?\n", + s.nr < 2 ? "this" : "one of these"); + for (i = 0; i < s.nr; i++) + fprintf(stderr, "\t%s\n", s.items[i].string); + + string_list_clear(&s, 0); +}
diff --git a/refs.h b/refs.h
index 50abbbb..586bfab 100644
--- a/refs.h
+++ b/refs.h@@ -95,4 +95,6 @@ int update_ref(const char *action, const char *refname, const unsigned char *sha1, const unsigned char *oldval, int flags, enum action_on_err onerr); +void probable_refs_print(const char *name); + #endif /* REFS_H */
And yes, I am joking. -Peff