Re: [PATCH] builtin-reset.c: Extend hard reset error message when using paths.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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 additional

You 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
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]