Commit 233884131d123432d2e1ad7236ad3c6ef7b2b518

nulltoken 2012-10-04T15:13:43

stash: add git_stash_foreach()

diff --git a/include/git2/stash.h b/include/git2/stash.h
index cadc656..6ebf89e 100644
--- a/include/git2/stash.h
+++ b/include/git2/stash.h
@@ -62,6 +62,46 @@ GIT_EXTERN(int) git_stash_save(
 	const char *message,
 	uint32_t flags);
 
+/**
+ * When iterating over all the stashed states, callback that will be
+ * issued per entry.
+ *
+ * @param index The position within the stash list. 0 points to the
+ * most recent stashed state.
+ *
+ * @param message The stash message.
+ *
+ * @param stash_oid The commit oid of the stashed state.
+ *
+ * @param payload Extra parameter to callback function.
+ *
+ * @return 0 on success, GIT_EUSER on non-zero callback, or error code
+ */
+typedef int (*stash_cb)(
+	size_t index,
+	const char* message,
+	const git_oid *stash_oid,
+	void *payload);
+
+/**
+ * Loop over all the stashed states and issue a callback for each one.
+ *
+ * If the callback returns a non-zero value, this will stop looping.
+ *
+ * @param repo Repository where to find the stash.
+ *
+ * @param callabck Callback to invoke per found stashed state. The most recent
+ * stash state will be enumerated first.
+ *
+ * @param payload Extra parameter to callback function.
+ *
+ * @return 0 on success, GIT_EUSER on non-zero callback, or error code
+ */
+GIT_EXTERN(int) git_stash_foreach(
+	git_repository *repo,
+	stash_cb callback,
+	void *payload);
+
 /** @} */
 GIT_END_DECL
 #endif
diff --git a/src/stash.c b/src/stash.c
index e63ee99..ed0b20f 100644
--- a/src/stash.c
+++ b/src/stash.c
@@ -575,3 +575,45 @@ cleanup:
 	git_index_free(index);
 	return error;
 }
+
+int git_stash_foreach(
+	git_repository *repo,
+	stash_cb callback,
+	void *payload)
+{
+	git_reference *stash;
+	git_reflog *reflog = NULL;
+	int error;
+	size_t i, max;
+	const git_reflog_entry *entry;
+
+	error = git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE);
+	if (error == GIT_ENOTFOUND)
+		return 0;
+
+	if (error < 0)
+		goto cleanup;
+
+	if ((error = git_reflog_read(&reflog, stash)) < 0)
+		goto cleanup;
+
+	max = git_reflog_entrycount(reflog);
+	for (i = 0; i < max; i++) {
+		entry = git_reflog_entry_byindex(reflog, max - i - 1);
+		
+		if (callback(i,
+			git_reflog_entry_msg(entry),
+			git_reflog_entry_oidnew(entry),
+			payload)) {
+				error = GIT_EUSER;
+				goto cleanup;
+		}
+	}
+
+	error = 0;
+
+cleanup:
+	git_reference_free(stash);
+	git_reflog_free(reflog);
+	return error;
+}
diff --git a/tests-clar/stash/foreach.c b/tests-clar/stash/foreach.c
new file mode 100644
index 0000000..8086507
--- /dev/null
+++ b/tests-clar/stash/foreach.c
@@ -0,0 +1,119 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+#include "stash_helpers.h"
+
+struct callback_data
+{
+	char **oids;
+	int invokes;
+};
+
+static git_repository *repo;
+static git_signature *signature;
+static git_oid stash_tip_oid;
+struct callback_data data;
+
+#define REPO_NAME "stash"
+
+void test_stash_foreach__initialize(void)
+{
+	cl_git_pass(git_signature_new(
+		&signature,
+		"nulltoken",
+		"emeric.fermas@gmail.com",
+		1323847743, 60)); /* Wed Dec 14 08:29:03 2011 +0100 */
+
+	memset(&data, 0, sizeof(struct callback_data));
+}
+
+void test_stash_foreach__cleanup(void)
+{
+	git_signature_free(signature);
+	git_repository_free(repo);
+	cl_git_pass(git_futils_rmdir_r(REPO_NAME, NULL, GIT_DIRREMOVAL_FILES_AND_DIRS));
+}
+
+static int callback_cb(
+		size_t index,
+		const char* message,
+		const git_oid *stash_oid,
+		void *payload)
+{
+	int i = 0;
+	bool found = false;
+	struct callback_data *data = (struct callback_data *)payload;
+
+	cl_assert_equal_i(0, git_oid_streq(stash_oid, data->oids[data->invokes++]));
+	
+	return 0;
+}
+
+void test_stash_foreach__enumerating_a_empty_repository_doesnt_fail(void)
+{
+	char *oids[] = { NULL };
+
+	data.oids = oids;
+
+	cl_git_pass(git_repository_init(&repo, REPO_NAME, 0));
+
+	cl_git_pass(git_stash_foreach(repo, callback_cb, &data));
+
+	cl_assert_equal_i(0, data.invokes);
+}
+
+void test_stash_foreach__can_enumerate_a_repository(void)
+{
+	char *oids_default[] = {
+		"1d91c842a7cdfc25872b3a763e5c31add8816c25", NULL };
+
+	char *oids_untracked[] = {
+		"7f89a8b15c878809c5c54d1ff8f8c9674154017b",
+		"1d91c842a7cdfc25872b3a763e5c31add8816c25", NULL };
+
+	char *oids_ignored[] = {
+		"c95599a8fef20a7e57582c6727b1a0d02e0a5828",
+		"7f89a8b15c878809c5c54d1ff8f8c9674154017b",
+		"1d91c842a7cdfc25872b3a763e5c31add8816c25", NULL };
+
+	cl_git_pass(git_repository_init(&repo, REPO_NAME, 0));
+
+	setup_stash(repo, signature);
+
+	cl_git_pass(git_stash_save(
+		&stash_tip_oid,
+		repo,
+		signature,
+		NULL,
+		GIT_STASH_DEFAULT));
+
+	data.oids = oids_default;
+
+	cl_git_pass(git_stash_foreach(repo, callback_cb, &data));
+	cl_assert_equal_i(1, data.invokes);
+
+	data.oids = oids_untracked;
+	data.invokes = 0;
+
+	cl_git_pass(git_stash_save(
+		&stash_tip_oid,
+		repo,
+		signature,
+		NULL,
+		GIT_STASH_INCLUDE_UNTRACKED));
+
+	cl_git_pass(git_stash_foreach(repo, callback_cb, &data));
+	cl_assert_equal_i(2, data.invokes);
+
+	data.oids = oids_ignored;
+	data.invokes = 0;
+
+	cl_git_pass(git_stash_save(
+		&stash_tip_oid,
+		repo,
+		signature,
+		NULL,
+		GIT_STASH_INCLUDE_IGNORED));
+
+	cl_git_pass(git_stash_foreach(repo, callback_cb, &data));
+	cl_assert_equal_i(3, data.invokes);
+}