Commit 5a62d659bcd3dd529fde9ab7eae2e216cba055cf

Edward Thomson 2013-01-03T12:44:09

MERGE_HEAD contents iterator

diff --git a/include/git2/errors.h b/include/git2/errors.h
index 8ca9009..4eb9e94 100644
--- a/include/git2/errors.h
+++ b/include/git2/errors.h
@@ -64,6 +64,7 @@ typedef enum {
 	GITERR_STASH,
 	GITERR_CHECKOUT,
 	GITERR_FETCHHEAD,
+	GITERR_MERGE,
 } git_error_t;
 
 /**
diff --git a/include/git2/repository.h b/include/git2/repository.h
index 02e6891..1371d54 100644
--- a/include/git2/repository.h
+++ b/include/git2/repository.h
@@ -517,6 +517,22 @@ GIT_EXTERN(int) git_repository_fetchhead_foreach(git_repository *repo,
 	git_repository_fetchhead_foreach_cb callback,
 	void *payload);
 
+typedef int (*git_repository_mergehead_foreach_cb)(const git_oid *oid,
+	void *payload);
+
+/**
+ * If a merge is in progress, call callback 'cb' for each commit ID in the
+ * MERGE_HEAD file.
+ *
+ * @param repo A repository object
+ * @param callback Callback function
+ * @param apyload Pointer to callback data (optional)
+ * @return 0 on success, GIT_ENOTFOUND, GIT_EUSER or error
+ */
+GIT_EXTERN(int) git_repository_mergehead_foreach(git_repository *repo,
+	git_repository_mergehead_foreach_cb callback,
+	void *payload);
+
 /**
  * Calculate hash of file using repository filtering rules.
  *
diff --git a/src/merge.c b/src/merge.c
index dfdadca..f52c112 100644
--- a/src/merge.c
+++ b/src/merge.c
@@ -241,3 +241,56 @@ int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_l
 	*out = result;
 	return 0;
 }
+
+int git_repository_mergehead_foreach(git_repository *repo,
+	git_repository_mergehead_foreach_cb cb,
+	void *payload)
+{
+	git_buf merge_head_path = GIT_BUF_INIT, merge_head_file = GIT_BUF_INIT;
+	char *buffer, *line;
+	size_t line_num = 1;
+	git_oid oid;
+	int error = 0;
+
+	assert(repo && cb);
+
+	if ((error = git_buf_joinpath(&merge_head_path, repo->path_repository,
+		GIT_MERGE_HEAD_FILE)) < 0)
+		return error;
+
+	if ((error = git_futils_readbuffer(&merge_head_file,
+		git_buf_cstr(&merge_head_path))) < 0)
+		goto cleanup;
+
+	buffer = merge_head_file.ptr;
+
+	while ((line = git__strsep(&buffer, "\n")) != NULL) {
+		if (strlen(line) != GIT_OID_HEXSZ) {
+			giterr_set(GITERR_INVALID, "Unable to parse OID - invalid length");
+			error = -1;
+			goto cleanup;
+		}
+
+		if ((error = git_oid_fromstr(&oid, line)) < 0)
+			goto cleanup;
+
+		if (cb(&oid, payload) < 0) {
+			error = GIT_EUSER;
+			goto cleanup;
+		}
+
+		++line_num;
+	}
+
+	if (*buffer) {
+		giterr_set(GITERR_MERGE, "No EOL at line %d", line_num);
+		error = -1;
+		goto cleanup;
+	}
+
+cleanup:
+	git_buf_free(&merge_head_path);
+	git_buf_free(&merge_head_file);
+
+	return error;
+}
diff --git a/src/merge.h b/src/merge.h
index af24de4..03b41e3 100644
--- a/src/merge.h
+++ b/src/merge.h
@@ -10,6 +10,7 @@
 #include "git2/types.h"
 #include "git2/merge.h"
 #include "commit_list.h"
+#include "vector.h"
 
 #define GIT_MERGE_MSG_FILE		"MERGE_MSG"
 #define GIT_MERGE_MODE_FILE		"MERGE_MODE"
diff --git a/tests-clar/merge/setup.c b/tests-clar/merge/setup.c
new file mode 100644
index 0000000..d88b2d9
--- /dev/null
+++ b/tests-clar/merge/setup.c
@@ -0,0 +1,159 @@
+#include "clar_libgit2.h"
+#include "git2/repository.h"
+#include "git2/merge.h"
+#include "merge.h"
+#include "refs.h"
+#include "fileops.h"
+
+static git_repository *repo;
+static git_index *repo_index;
+
+#define TEST_REPO_PATH "testrepo"
+#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
+
+#define ORIG_HEAD                   "bd593285fc7fe4ca18ccdbabf027f5d689101452"
+
+#define THEIRS_SIMPLE_BRANCH        "branch"
+#define THEIRS_SIMPLE_OID           "7cb63eed597130ba4abb87b3e544b85021905520"
+
+#define OCTO1_BRANCH                "octo1"
+#define OCTO1_OID                   "16f825815cfd20a07a75c71554e82d8eede0b061"
+
+#define OCTO2_BRANCH                "octo2"
+#define OCTO2_OID                   "158dc7bedb202f5b26502bf3574faa7f4238d56c"
+
+#define OCTO3_BRANCH                "octo3"
+#define OCTO3_OID                   "50ce7d7d01217679e26c55939eef119e0c93e272"
+
+#define OCTO4_BRANCH                "octo4"
+#define OCTO4_OID                   "54269b3f6ec3d7d4ede24dd350dd5d605495c3ae"
+
+#define OCTO5_BRANCH                "octo5"
+#define OCTO5_OID                   "e4f618a2c3ed0669308735727df5ebf2447f022f"
+
+// Fixture setup and teardown
+void test_merge_setup__initialize(void)
+{
+	repo = cl_git_sandbox_init(TEST_REPO_PATH);
+    git_repository_index(&repo_index, repo);
+}
+
+void test_merge_setup__cleanup(void)
+{
+    git_index_free(repo_index);
+	cl_git_sandbox_cleanup();
+}
+
+static bool test_file_contents(const char *filename, const char *expected)
+{
+    git_buf file_path_buf = GIT_BUF_INIT, file_buf = GIT_BUF_INIT;
+    bool equals;
+
+    git_buf_printf(&file_path_buf, "%s/%s", git_repository_path(repo), filename);
+
+    cl_git_pass(git_futils_readbuffer(&file_buf, file_path_buf.ptr));
+    equals = (strcmp(file_buf.ptr, expected) == 0);
+
+    git_buf_free(&file_path_buf);
+    git_buf_free(&file_buf);
+
+    return equals;
+}
+
+static void write_file_contents(const char *filename, const char *output)
+{
+	git_buf file_path_buf = GIT_BUF_INIT;
+
+    git_buf_printf(&file_path_buf, "%s/%s", git_repository_path(repo), filename);
+	cl_git_rewritefile(file_path_buf.ptr, output);
+
+	git_buf_free(&file_path_buf);
+}
+
+struct merge_head_cb_data {
+	const char **oid_str;
+	unsigned int len;
+
+	unsigned int i;
+};
+
+int merge_head_foreach_cb(git_oid *oid, void *payload)
+{
+	git_oid expected_oid;
+
+	struct merge_head_cb_data *cb_data = payload;
+
+	git_oid_fromstr(&expected_oid, cb_data->oid_str[cb_data->i]);
+
+	cl_assert(git_oid_cmp(&expected_oid, oid) == 0);
+
+	cb_data->i++;
+
+	return 0;
+}
+
+void test_merge_setup__head_notfound(void)
+{
+	int error;
+
+	cl_git_fail((error = git_repository_mergehead_foreach(repo,
+		merge_head_foreach_cb, NULL)));
+	cl_assert(error == GIT_ENOTFOUND);
+}
+
+void test_merge_setup__head_invalid_oid(void)
+{
+	int error;
+
+	write_file_contents(GIT_MERGE_HEAD_FILE, "invalid-oid\n");
+
+	cl_git_fail((error = git_repository_mergehead_foreach(repo,
+		merge_head_foreach_cb, NULL)));
+	cl_assert(error == -1);
+}
+
+void test_merge_setup__head_foreach_nonewline(void)
+{
+	int error;
+
+	write_file_contents(GIT_MERGE_HEAD_FILE, THEIRS_SIMPLE_OID);
+
+	cl_git_fail((error = git_repository_mergehead_foreach(repo,
+		merge_head_foreach_cb, NULL)));
+	cl_assert(error == -1);
+}
+
+void test_merge_setup__head_foreach_one(void)
+{
+	const char *expected = THEIRS_SIMPLE_OID;
+
+	struct merge_head_cb_data cb_data = { &expected, 1 };
+
+	write_file_contents(GIT_MERGE_HEAD_FILE, THEIRS_SIMPLE_OID "\n");
+
+	cl_git_pass(git_repository_mergehead_foreach(repo,
+		merge_head_foreach_cb, &cb_data));
+
+	cl_assert(cb_data.i == cb_data.len);
+}
+
+void test_merge_setup__head_foreach_octopus(void)
+{
+	const char *expected[] = { THEIRS_SIMPLE_OID,
+		OCTO1_OID, OCTO2_OID, OCTO3_OID, OCTO4_OID, OCTO5_OID };
+
+	struct merge_head_cb_data cb_data = { expected, 6 };
+
+	write_file_contents(GIT_MERGE_HEAD_FILE,
+		THEIRS_SIMPLE_OID "\n"
+		OCTO1_OID "\n"
+		OCTO2_OID "\n"
+		OCTO3_OID "\n"
+		OCTO4_OID "\n"
+		OCTO5_OID "\n");
+
+	cl_git_pass(git_repository_mergehead_foreach(repo,
+		merge_head_foreach_cb, &cb_data));
+
+	cl_assert(cb_data.i == cb_data.len);
+}