Commit 6aac5afb6d74a016a4066612d99c5a58dea46b40

Vicent Martí 2011-10-09T12:09:57

Merge pull request #444 from carlosmn/fetch-fixes A couple of fetch fixes

diff --git a/src/fetch.c b/src/fetch.c
index 1bb8968..3c3dbcb 100644
--- a/src/fetch.c
+++ b/src/fetch.c
@@ -24,7 +24,7 @@ static int filter_wants(git_remote *remote)
 	git_repository *repo = remote->repo;
 	const git_refspec *spec;
 	int error;
-	unsigned int i;
+	unsigned int i = 0;
 
 	error = git_vector_init(&list, 16, NULL);
 	if (error < GIT_SUCCESS)
@@ -36,13 +36,32 @@ static int filter_wants(git_remote *remote)
 		goto cleanup;
 	}
 
+	/*
+	 * The fetch refspec can be NULL, and what this means is that the
+	 * user didn't specify one. This is fine, as it means that we're
+	 * not interested in any particular branch but just the remote's
+	 * HEAD, which will be stored in FETCH_HEAD after the fetch.
+	 */
 	spec = git_remote_fetchspec(remote);
-	if (spec == NULL) {
-		error = git__throw(GIT_ERROR, "The remote has no fetchspec");
-		goto cleanup;
+
+	/*
+	 * We need to handle HEAD separately, as we always want it, but it
+	 * probably won't matcht he refspec.
+	 */
+	head = refs.heads[0];
+	if (refs.len > 0 && !strcmp(head->name, GIT_HEAD_FILE)) {
+		if (git_odb_exists(repo->db, &head->oid))
+			head->local = 1;
+		else
+			remote->need_pack = 1;
+
+		i = 1;
+		error = git_vector_insert(&list, refs.heads[0]);
+		if (error < GIT_SUCCESS)
+			goto cleanup;
 	}
 
-	for (i = 0; i < refs.len; ++i) {
+	for (; i < refs.len; ++i) {
 		git_remote_head *head = refs.heads[i];
 
 		/* If it doesn't match the refpec, we don't want it */
diff --git a/src/refs.c b/src/refs.c
index 1135de4..fcf771b 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -1713,7 +1713,8 @@ static int normalize_name(char *buffer_out, size_t out_size, const char *name, i
 	/* Object id refname have to contain at least one slash, except
 	 * for HEAD in a detached state or MERGE_HEAD if we're in the
 	 * middle of a merge */
-	if (is_oid_ref && !contains_a_slash && (strcmp(name, GIT_HEAD_FILE) && strcmp(name, GIT_MERGE_HEAD_FILE)))
+	if (is_oid_ref && !contains_a_slash && (strcmp(name, GIT_HEAD_FILE) && strcmp(name, GIT_MERGE_HEAD_FILE)
+	                                        && strcmp(name, GIT_FETCH_HEAD_FILE)))
 		return git__throw(GIT_EINVALIDREFNAME, "Failed to normalize name. Reference name contains no slashes");
 
 	/* A refname can not end with ".lock" */
diff --git a/src/refs.h b/src/refs.h
index 979af2e..c4b0b0e 100644
--- a/src/refs.h
+++ b/src/refs.h
@@ -24,6 +24,7 @@
 #define GIT_PACKEDREFS_HEADER "# pack-refs with: peeled "
 
 #define GIT_HEAD_FILE "HEAD"
+#define GIT_FETCH_HEAD_FILE "FETCH_HEAD"
 #define GIT_MERGE_HEAD_FILE "MERGE_HEAD"
 #define GIT_REFS_HEADS_MASTER_FILE GIT_REFS_HEADS_DIR "master"
 
diff --git a/src/refspec.c b/src/refspec.c
index 9de2730..ed4b5e6 100644
--- a/src/refspec.c
+++ b/src/refspec.c
@@ -42,17 +42,17 @@ int git_refspec_parse(git_refspec *refspec, const char *str)
 
 const char *git_refspec_src(const git_refspec *refspec)
 {
-	return refspec->src;
+	return refspec == NULL ? NULL : refspec->src;
 }
 
 const char *git_refspec_dst(const git_refspec *refspec)
 {
-	return refspec->dst;
+	return refspec == NULL ? NULL : refspec->dst;
 }
 
 int git_refspec_src_match(const git_refspec *refspec, const char *refname)
 {
-	return git__fnmatch(refspec->src, refname, 0);
+	return refspec == NULL ? GIT_ENOMATCH : git__fnmatch(refspec->src, refname, 0);
 }
 
 int git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, const char *name)
diff --git a/src/remote.c b/src/remote.c
index f581b97..a557a49 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -219,7 +219,7 @@ int git_remote_download(char **filename, git_remote *remote)
 int git_remote_update_tips(struct git_remote *remote)
 {
 	int error = GIT_SUCCESS;
-	unsigned int i;
+	unsigned int i = 0;
 	char refname[GIT_PATH_MAX];
 	git_headarray *refs = &remote->refs;
 	git_remote_head *head;
@@ -228,8 +228,21 @@ int git_remote_update_tips(struct git_remote *remote)
 
 	memset(refname, 0x0, sizeof(refname));
 
-	for (i = 0; i < refs->len; ++i) {
+	if (refs->len == 0)
+		return GIT_SUCCESS;
+
+	/* HEAD is only allowed to be the first in the list */
+	head = refs->heads[0];
+	if (!strcmp(head->name, GIT_HEAD_FILE)) {
+		error = git_reference_create_oid(&ref, remote->repo, GIT_FETCH_HEAD_FILE, &head->oid, 1);
+		i = 1;
+		if (error < GIT_SUCCESS)
+			return git__rethrow(error, "Failed to update FETCH_HEAD");
+	}
+
+	for (; i < refs->len; ++i) {
 		head = refs->heads[i];
+
 		error = git_refspec_transform(refname, sizeof(refname), spec, head->name);
 		if (error < GIT_SUCCESS)
 			return error;