Commit e1f4a761506f61f05e74265633aec76cf75cbd50

Carlos Martín Nieto 2011-06-22T14:53:01

Add git_fetch_list_want which creates the "want" list Signed-off-by: Carlos Martín Nieto <carlos@cmartin.tk>

diff --git a/include/git2/net.h b/include/git2/net.h
index 164f0ac..ecf4f6c 100644
--- a/include/git2/net.h
+++ b/include/git2/net.h
@@ -48,10 +48,17 @@ GIT_BEGIN_DECL
 #define GIT_DIR_FETCH 0
 #define GIT_DIR_PUSH 1
 
+enum git_whn {
+	GIT_WHN_NONE,
+	GIT_WHN_HAVE,
+	GIT_WHN_WANT,
+};
+
 /**
  * Remote head description, given out on `ls` calls.
  */
 struct git_remote_head {
+	enum git_whn type;
 	git_oid oid;
 	char *name;
 };
diff --git a/src/fetch.c b/src/fetch.c
new file mode 100644
index 0000000..21f52fc
--- /dev/null
+++ b/src/fetch.c
@@ -0,0 +1,123 @@
+/*
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2,
+ * as published by the Free Software Foundation.
+ *
+ * In addition to the permissions in the GNU General Public License,
+ * the authors give you unlimited permission to link the compiled
+ * version of this file into combinations with other programs,
+ * and to distribute those combinations without any restriction
+ * coming from the use of this file.  (The General Public License
+ * restrictions do apply in other respects; for example, they cover
+ * modification of the file, and distribution when not linked into
+ * a combined executable.)
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "git2/remote.h"
+#include "git2/oid.h"
+#include "git2/refs.h"
+
+#include "common.h"
+#include "transport.h"
+#include "remote.h"
+#include "refspec.h"
+
+/*
+ * Don't forget that this depends on the enum being correctly set
+ */
+static int whn_cmp(const void *a, const void *b)
+{
+	git_remote_head *heada = *(git_remote_head **)(a);
+	git_remote_head *headb = *(git_remote_head **)(b);
+
+	return headb->type - heada->type;
+}
+
+/* FIXME: we assume that the transport has been connected, enforce that somehow */
+int git_fetch_list_want(git_headarray *whn_list, git_repository *repo, git_remote *remote)
+{
+	git_vector list;
+	git_headarray refs, lrefs;
+	git_transport *t = remote->transport;
+	const git_refspec *spec;
+	int error, i;
+
+	error = git_vector_init(&list, 16, whn_cmp);
+	if (error < GIT_SUCCESS)
+		return error;
+
+	error = git_transport_ls(t, &refs);
+	if (error < GIT_SUCCESS) {
+		error = git__rethrow(error, "Failed to list local refs");
+		goto cleanup;
+	}
+
+	spec = git_remote_fetchspec(remote);
+	if (spec == NULL) {
+		error = git__throw(GIT_ERROR, "The remote has to fetchspec");
+		goto cleanup;
+	}
+
+	for (i = 0; i < refs.len; ++i) {
+		char local[1024];
+		git_reference *ref;
+		git_remote_head *head = refs.heads[i];
+
+		/* If it doesn't match the refpec, we don't want it */
+		error = git_refspec_src_match(spec, head->name);
+		if (error == GIT_ENOMATCH)
+			continue;
+		if (error < GIT_SUCCESS) {
+			error = git__rethrow(error, "Error matching remote ref name");
+			goto cleanup;
+		}
+
+		/* If the local ref is the same, we don't want it either */
+		error = git_refspec_transform(local, sizeof(local), spec, head->name);
+		if (error < GIT_SUCCESS) {
+			error = git__rethrow(error, "Error transforming ref name");
+			goto cleanup;
+		}
+
+		error = git_reference_lookup(&ref, repo, local);
+		/* If we don't have it locally, it's new, so we want it */
+		if (error < GIT_SUCCESS && error != GIT_ENOTFOUND) {
+			error = git__rethrow(error, "Error looking up local ref");
+			goto cleanup;
+		}
+
+		if (ref != NULL) {
+			if (!git_oid_cmp(&head->oid, git_reference_oid(ref)))
+				continue;
+		}
+
+		/*
+		 * Now we know we want to have that ref, so add it as a "want"
+		 * to the list.
+		 */
+		head->type = GIT_WHN_WANT;
+		error = git_vector_insert(&list, head);
+		if (error < GIT_SUCCESS)
+			goto cleanup;
+	}
+
+	git_vector_sort(&list);
+	whn_list->len = list.length;
+	whn_list->heads = (git_remote_head **) list.contents;
+
+	return GIT_SUCCESS;
+
+cleanup:
+	git_vector_free(&list);
+	return error;
+}