return NULL;
}
+static int ambiguous_path(const char *path, int len)
+{
+ int slash = 1;
+ int cnt;
+
+ for (cnt = 0; cnt < len; cnt++) {
+ switch (*path++) {
+ case '\0':
+ break;
+ case '/':
+ if (slash)
+ break;
+ slash = 1;
+ continue;
+ case '.':
+ continue;
+ default:
+ slash = 0;
+ continue;
+ }
+ return slash;
+ }
+ return slash;
+}
+
static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
{
static const char *prefix[] = {
NULL
};
const char **p;
+ int found = 0;
if (len == 40 && !get_sha1_hex(str, sha1))
return 0;
+ /* Accept only unambiguous ref paths. */
+ if (ambiguous_path(str, len))
+ return -1;
+
for (p = prefix; *p; p++) {
char *pathname = git_path("%s/%.*s", *p, len, str);
- if (!read_ref(pathname, sha1))
- return 0;
- }
- return -1;
+ if (!read_ref(pathname, sha1)) {
+ /* Must be unique; i.e. when heads/foo and
+ * tags/foo are both present, reject "foo".
+ */
+ if (1 < found++)
+ return -1;
+ }
+
+ /* We want to allow .git/description file and
+ * "description" branch to exist at the same time.
+ * "git-rev-parse description" should silently skip
+ * .git/description file as a candidate for
+ * get_sha1(). However, having garbage file anywhere
+ * under refs/ is not OK, and we would not have caught
+ * ambiguous heads and tags with the above test.
+ */
+ else if (**p && !access(pathname, F_OK)) {
+ /* Garbage exists under .git/refs */
+ return error("garbage ref found '%s'", pathname);
+ }
+ }
+ switch (found) {
+ case 0:
+ return -1;
+ case 1:
+ return 0;
+ default:
+ return error("ambiguous refname '%.*s'", len, str);
+ }
}
static int get_sha1_1(const char *name, int len, unsigned char *sha1);
if (!o)
return -1;
if (!type_string) {
- o = deref_tag(o);
+ o = deref_tag(o, name, sp - name - 2);
+ if (!o || (!o->parsed && !parse_object(o->sha1)))
+ return -1;
memcpy(sha1, o->sha1, 20);
}
else {
*/
while (1) {
- if (!o)
+ if (!o || (!o->parsed && !parse_object(o->sha1)))
return -1;
if (o->type == type_string) {
memcpy(sha1, o->sha1, 20);