Merge branch 'db/learn-HEAD'

* db/learn-HEAD:
  Make ls-remote http://... list HEAD, like for git://...
  Make walker.fetch_ref() take a struct ref.
diff --git a/cache.h b/cache.h
index d5d5dad..7fb8f33 100644
--- a/cache.h
+++ b/cache.h
@@ -635,6 +635,7 @@
 	struct ref *next;
 	unsigned char old_sha1[20];
 	unsigned char new_sha1[20];
+	char *symref;
 	unsigned int force:1,
 		merge:1,
 		nonfastforward:1,
diff --git a/http-push.c b/http-push.c
index 5b23038..939a764 100644
--- a/http-push.c
+++ b/http-push.c
@@ -1759,15 +1759,16 @@
 static void one_remote_ref(char *refname)
 {
 	struct ref *ref;
-	unsigned char remote_sha1[20];
 	struct object *obj;
-	int len = strlen(refname) + 1;
 
-	if (http_fetch_ref(remote->url, refname + 5 /* "refs/" */,
-			   remote_sha1) != 0) {
+	ref = alloc_ref(strlen(refname) + 1);
+	strcpy(ref->name, refname);
+
+	if (http_fetch_ref(remote->url, ref) != 0) {
 		fprintf(stderr,
 			"Unable to fetch ref %s from %s\n",
 			refname, remote->url);
+		free(ref);
 		return;
 	}
 
@@ -1775,18 +1776,15 @@
 	 * Fetch a copy of the object if it doesn't exist locally - it
 	 * may be required for updating server info later.
 	 */
-	if (remote->can_update_info_refs && !has_sha1_file(remote_sha1)) {
-		obj = lookup_unknown_object(remote_sha1);
+	if (remote->can_update_info_refs && !has_sha1_file(ref->old_sha1)) {
+		obj = lookup_unknown_object(ref->old_sha1);
 		if (obj) {
 			fprintf(stderr,	"  fetch %s for %s\n",
-				sha1_to_hex(remote_sha1), refname);
+				sha1_to_hex(ref->old_sha1), refname);
 			add_fetch_request(obj);
 		}
 	}
 
-	ref = xcalloc(1, sizeof(*ref) + len);
-	hashcpy(ref->old_sha1, remote_sha1);
-	memcpy(ref->name, refname, len);
 	*remote_tail = ref;
 	remote_tail = &ref->next;
 }
@@ -1891,33 +1889,37 @@
 static void add_remote_info_ref(struct remote_ls_ctx *ls)
 {
 	struct strbuf *buf = (struct strbuf *)ls->userData;
-	unsigned char remote_sha1[20];
 	struct object *o;
 	int len;
 	char *ref_info;
+	struct ref *ref;
 
-	if (http_fetch_ref(remote->url, ls->dentry_name + 5 /* "refs/" */,
-			   remote_sha1) != 0) {
+	ref = alloc_ref(strlen(ls->dentry_name) + 1);
+	strcpy(ref->name, ls->dentry_name);
+
+	if (http_fetch_ref(remote->url, ref) != 0) {
 		fprintf(stderr,
 			"Unable to fetch ref %s from %s\n",
 			ls->dentry_name, remote->url);
 		aborted = 1;
+		free(ref);
 		return;
 	}
 
-	o = parse_object(remote_sha1);
+	o = parse_object(ref->old_sha1);
 	if (!o) {
 		fprintf(stderr,
 			"Unable to parse object %s for remote ref %s\n",
-			sha1_to_hex(remote_sha1), ls->dentry_name);
+			sha1_to_hex(ref->old_sha1), ls->dentry_name);
 		aborted = 1;
+		free(ref);
 		return;
 	}
 
 	len = strlen(ls->dentry_name) + 42;
 	ref_info = xcalloc(len + 1, 1);
 	sprintf(ref_info, "%s	%s\n",
-		sha1_to_hex(remote_sha1), ls->dentry_name);
+		sha1_to_hex(ref->old_sha1), ls->dentry_name);
 	fwrite_buffer(ref_info, 1, len, buf);
 	free(ref_info);
 
@@ -1932,6 +1934,7 @@
 			free(ref_info);
 		}
 	}
+	free(ref);
 }
 
 static void update_remote_info_refs(struct remote_lock *lock)
diff --git a/http-walker.c b/http-walker.c
index 7bda34d..99f397e 100644
--- a/http-walker.c
+++ b/http-walker.c
@@ -888,10 +888,10 @@
 		     data->alt->base);
 }
 
-static int fetch_ref(struct walker *walker, char *ref, unsigned char *sha1)
+static int fetch_ref(struct walker *walker, struct ref *ref)
 {
 	struct walker_data *data = walker->data;
-	return http_fetch_ref(data->alt->base, ref, sha1);
+	return http_fetch_ref(data->alt->base, ref);
 }
 
 static void cleanup(struct walker *walker)
diff --git a/http.c b/http.c
index 256a5f1..acf746a 100644
--- a/http.c
+++ b/http.c
@@ -589,8 +589,9 @@
 			len += 2; /* extra two hex plus replacement % */
 	qref = xmalloc(len);
 	memcpy(qref, base, baselen);
-	memcpy(qref + baselen, "/refs/", 6);
-	for (cp = ref, dp = qref + baselen + 6; (ch = *cp) != 0; cp++) {
+	dp = qref + baselen;
+	*(dp++) = '/';
+	for (cp = ref; (ch = *cp) != 0; cp++) {
 		if (needs_quote(ch)) {
 			*dp++ = '%';
 			*dp++ = hex((ch >> 4) & 0xF);
@@ -604,7 +605,7 @@
 	return qref;
 }
 
-int http_fetch_ref(const char *base, const char *ref, unsigned char *sha1)
+int http_fetch_ref(const char *base, struct ref *ref)
 {
 	char *url;
 	struct strbuf buffer = STRBUF_INIT;
@@ -612,7 +613,7 @@
 	struct slot_results results;
 	int ret;
 
-	url = quote_ref_url(base, ref);
+	url = quote_ref_url(base, ref->name);
 	slot = get_active_slot();
 	slot->results = &results;
 	curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
@@ -624,12 +625,15 @@
 		if (results.curl_result == CURLE_OK) {
 			strbuf_rtrim(&buffer);
 			if (buffer.len == 40)
-				ret = get_sha1_hex(buffer.buf, sha1);
-			else
+				ret = get_sha1_hex(buffer.buf, ref->old_sha1);
+			else if (!prefixcmp(buffer.buf, "ref: ")) {
+				ref->symref = xstrdup(buffer.buf + 5);
+				ret = 0;
+			} else
 				ret = 1;
 		} else {
 			ret = error("Couldn't get %s for %s\n%s",
-				    url, ref, curl_errorstr);
+				    url, ref->name, curl_errorstr);
 		}
 	} else {
 		ret = error("Unable to start request");
diff --git a/http.h b/http.h
index 04169d5..a04fc6a 100644
--- a/http.h
+++ b/http.h
@@ -105,6 +105,6 @@
 
 #define missing_target(a) missing__target((a)->http_code, (a)->curl_result)
 
-extern int http_fetch_ref(const char *base, const char *ref, unsigned char *sha1);
+extern int http_fetch_ref(const char *base, struct ref *ref);
 
 #endif /* HTTP_H */
diff --git a/remote.c b/remote.c
index 870d224..6b480cb 100644
--- a/remote.c
+++ b/remote.c
@@ -711,13 +711,22 @@
 	return ret;
 }
 
+void free_ref(struct ref *ref)
+{
+	if (!ref)
+		return;
+	free(ref->remote_status);
+	free(ref->symref);
+	free(ref);
+}
+
 void free_refs(struct ref *ref)
 {
 	struct ref *next;
 	while (ref) {
 		next = ref->next;
 		free(ref->peer_ref);
-		free(ref);
+		free_ref(ref);
 		ref = next;
 	}
 }
@@ -1177,3 +1186,15 @@
 
 	return 0;
 }
+
+int resolve_remote_symref(struct ref *ref, struct ref *list)
+{
+	if (!ref->symref)
+		return 0;
+	for (; list; list = list->next)
+		if (!strcmp(ref->symref, list->name)) {
+			hashcpy(ref->old_sha1, list->old_sha1);
+			return 0;
+		}
+	return 1;
+}
diff --git a/remote.h b/remote.h
index 6878c52..75d006b 100644
--- a/remote.h
+++ b/remote.h
@@ -63,6 +63,8 @@
  */
 void free_refs(struct ref *ref);
 
+int resolve_remote_symref(struct ref *ref, struct ref *list);
+
 /*
  * Removes and frees any duplicate refs in the map.
  */
diff --git a/transport.c b/transport.c
index 393e0e8..b012a28 100644
--- a/transport.c
+++ b/transport.c
@@ -441,10 +441,14 @@
 	struct ref *ref = NULL;
 	struct ref *last_ref = NULL;
 
+	struct walker *walker;
+
 	if (!transport->data)
 		transport->data = get_http_walker(transport->url,
 						transport->remote);
 
+	walker = transport->data;
+
 	refs_url = xmalloc(strlen(transport->url) + 11);
 	sprintf(refs_url, "%s/info/refs", transport->url);
 
@@ -500,6 +504,16 @@
 
 	strbuf_release(&buffer);
 
+	ref = alloc_ref(strlen("HEAD") + 1);
+	strcpy(ref->name, "HEAD");
+	if (!walker->fetch_ref(walker, ref) &&
+	    !resolve_remote_symref(ref, refs)) {
+		ref->next = refs;
+		refs = ref;
+	} else {
+		free(ref);
+	}
+
 	return refs;
 }
 
diff --git a/walker.c b/walker.c
index c10eca8..fa96a7c 100644
--- a/walker.c
+++ b/walker.c
@@ -190,9 +190,14 @@
 	if (!get_sha1_hex(target, sha1))
 		return 0;
 	if (!check_ref_format(target)) {
-		if (!walker->fetch_ref(walker, target, sha1)) {
+		struct ref *ref = alloc_ref(strlen(target));
+		strcpy(ref->name, target);
+		if (!walker->fetch_ref(walker, ref)) {
+			hashcpy(sha1, ref->old_sha1);
+			free(ref);
 			return 0;
 		}
+		free(ref);
 	}
 	return -1;
 }
diff --git a/walker.h b/walker.h
index e1d40de..8a149e1 100644
--- a/walker.h
+++ b/walker.h
@@ -5,7 +5,7 @@
 
 struct walker {
 	void *data;
-	int (*fetch_ref)(struct walker *, char *ref, unsigned char *sha1);
+	int (*fetch_ref)(struct walker *, struct ref *ref);
 	void (*prefetch)(struct walker *, unsigned char *sha1);
 	int (*fetch)(struct walker *, unsigned char *sha1);
 	void (*cleanup)(struct walker *);