Commit 2fcb4f2801ef9a1e74b4281a78deae5e25b0ad43

Patrick Steinhardt 2020-06-17T14:09:04

repository: introduce new function to iterate over all worktrees Given a Git repository, it's non-trivial to iterate over all worktrees that are associated with it, including the "main" repository. This commit adds a new internal function `git_repository_foreach_worktree` that does this for us.

diff --git a/src/repository.c b/src/repository.c
index 5e818fb..6c73703 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -2254,6 +2254,51 @@ out:
 	return error;
 }
 
+int git_repository_foreach_worktree(git_repository *repo,
+				    git_repository_foreach_worktree_cb cb,
+				    void *payload)
+{
+	git_strarray worktrees = {0};
+	git_repository *worktree_repo = NULL;
+	git_worktree *worktree = NULL;
+	int error;
+	size_t i;
+
+	if ((error = git_repository_open(&worktree_repo, repo->commondir)) < 0 ||
+	    (error = cb(worktree_repo, payload) != 0))
+		goto out;
+
+	git_repository_free(worktree_repo);
+	worktree_repo = NULL;
+
+	if ((error = git_worktree_list(&worktrees, repo)) < 0)
+		goto out;
+
+	for (i = 0; i < worktrees.count; i++) {
+		git_repository_free(worktree_repo);
+		worktree_repo = NULL;
+		git_worktree_free(worktree);
+		worktree = NULL;
+
+		if ((error = git_worktree_lookup(&worktree, repo, worktrees.strings[i]) < 0) ||
+		    (error = git_repository_open_from_worktree(&worktree_repo, worktree)) < 0) {
+			if (error != GIT_ENOTFOUND)
+				goto out;
+			error = 0;
+			continue;
+		}
+
+		if ((error = cb(worktree_repo, payload)) != 0)
+			goto out;
+	}
+
+out:
+	git_strarray_dispose(&worktrees);
+	git_repository_free(worktree_repo);
+	git_worktree_free(worktree);
+	return error;
+}
+
 int git_repository_foreach_head(git_repository *repo,
 				git_repository_foreach_head_cb cb,
 				int flags, void *payload)
diff --git a/src/repository.h b/src/repository.h
index bafdb58..a823bdc 100644
--- a/src/repository.h
+++ b/src/repository.h
@@ -166,6 +166,12 @@ GIT_INLINE(git_attr_cache *) git_repository_attr_cache(git_repository *repo)
 int git_repository_head_tree(git_tree **tree, git_repository *repo);
 int git_repository_create_head(const char *git_dir, const char *ref_name);
 
+typedef int (*git_repository_foreach_worktree_cb)(git_repository *, void *);
+
+int git_repository_foreach_worktree(git_repository *repo,
+				    git_repository_foreach_worktree_cb cb,
+				    void *payload);
+
 /*
  * Called for each HEAD.
  *
diff --git a/tests/worktree/worktree.c b/tests/worktree/worktree.c
index 716d0aa..a08c305 100644
--- a/tests/worktree/worktree.c
+++ b/tests/worktree/worktree.c
@@ -623,3 +623,33 @@ void test_worktree_worktree__foreach_head_gives_same_results_in_wt_and_repo(void
 	git_vector_free(&repo_refs);
 	git_vector_free(&worktree_refs);
 }
+
+static int foreach_worktree_cb(git_repository *worktree, void *payload)
+{
+	int *counter = (int *)payload;
+
+	switch (*counter) {
+	case 0:
+		cl_assert_equal_s(git_repository_path(fixture.repo),
+				  git_repository_path(worktree));
+		cl_assert(!git_repository_is_worktree(worktree));
+		break;
+	case 1:
+		cl_assert_equal_s(git_repository_path(fixture.worktree),
+				  git_repository_path(worktree));
+		cl_assert(git_repository_is_worktree(worktree));
+		break;
+	default:
+		cl_fail("more worktrees found than expected");
+	}
+
+	(*counter)++;
+
+	return 0;
+}
+
+void test_worktree_worktree__foreach_worktree_lists_all_worktrees(void)
+{
+	int counter = 0;
+	cl_git_pass(git_repository_foreach_worktree(fixture.repo, foreach_worktree_cb, &counter));
+}