Merge pull request #4216 from pks-t/pks/debian-test-failures
Debian HTTPS feature test failure
diff --git a/include/git2/remote.h b/include/git2/remote.h
index 244794e..e9e4e5b 100644
--- a/include/git2/remote.h
+++ b/include/git2/remote.h
@@ -715,8 +715,8 @@
* Peform all the steps from a push.
*
* @param remote the remote to push to
- * @param refspecs the refspecs to use for pushing. If none are
- * passed, the configured refspecs will be used
+ * @param refspecs the refspecs to use for pushing. If NULL or an empty
+ * array, the configured refspecs will be used
* @param opts options to use for this push
*/
GIT_EXTERN(int) git_remote_push(git_remote *remote,
diff --git a/src/branch.c b/src/branch.c
index 7d5e9cb..7a83b83 100644
--- a/src/branch.c
+++ b/src/branch.c
@@ -127,61 +127,28 @@
repository, branch_name, commit->commit, commit->description, force);
}
-int git_branch_is_checked_out(
- const git_reference *branch)
+static int branch_equals(git_repository *repo, const char *path, void *payload)
{
- git_buf path = GIT_BUF_INIT, buf = GIT_BUF_INIT;
- git_strarray worktrees;
- git_reference *ref = NULL;
- git_repository *repo;
- const char *worktree;
- int found = false;
- size_t i;
+ git_reference *branch = (git_reference *) payload;
+ git_reference *head;
+ int equal;
- assert(branch && git_reference_is_branch(branch));
+ if (git_reference__read_head(&head, repo, path) < 0 ||
+ git_reference_type(head) != GIT_REF_SYMBOLIC)
+ return 0;
- repo = git_reference_owner(branch);
-
- if (git_worktree_list(&worktrees, repo) < 0)
- return -1;
-
- for (i = 0; i < worktrees.count; i++) {
- worktree = worktrees.strings[i];
-
- if (git_repository_head_for_worktree(&ref, repo, worktree) < 0)
- continue;
-
- if (git__strcmp(ref->name, branch->name) == 0) {
- found = true;
- git_reference_free(ref);
- break;
- }
-
- git_reference_free(ref);
- }
- git_strarray_free(&worktrees);
-
- if (found)
- return found;
-
- /* Check HEAD of parent */
- if (git_buf_joinpath(&path, repo->commondir, GIT_HEAD_FILE) < 0)
- goto out;
- if (git_futils_readbuffer(&buf, path.ptr) < 0)
- goto out;
- if (git__prefixcmp(buf.ptr, "ref: ") == 0)
- git_buf_consume(&buf, buf.ptr + strlen("ref: "));
- git_buf_rtrim(&buf);
-
- found = git__strcmp(buf.ptr, branch->name) == 0;
-
-out:
- git_buf_free(&buf);
- git_buf_free(&path);
-
- return found;
+ equal = !git__strcmp(head->target.symbolic, branch->name);
+ git_reference_free(head);
+ return equal;
}
+int git_branch_is_checked_out(const git_reference *branch)
+{
+ assert(branch && git_reference_is_branch(branch));
+
+ return git_repository_foreach_head(git_reference_owner(branch),
+ branch_equals, (void *) branch) == 1;
+}
int git_branch_delete(git_reference *branch)
{
diff --git a/src/config.c b/src/config.c
index 0d73ad2..cbcea2e 100644
--- a/src/config.c
+++ b/src/config.c
@@ -576,22 +576,50 @@
* Setters
**************/
-static int config_error_nofiles(const char *name)
+typedef enum {
+ BACKEND_USE_SET,
+ BACKEND_USE_DELETE
+} backend_use;
+
+static const char *uses[] = {
+ "set",
+ "delete"
+};
+
+static int get_backend_for_use(git_config_backend **out,
+ git_config *cfg, const char *name, backend_use use)
{
+ size_t i;
+ file_internal *f;
+
+ *out = NULL;
+
+ if (git_vector_length(&cfg->files) == 0) {
+ giterr_set(GITERR_CONFIG,
+ "cannot %s value for '%s' when no config files exist",
+ uses[use], name);
+ return GIT_ENOTFOUND;
+ }
+
+ git_vector_foreach(&cfg->files, i, f) {
+ if (!f->file->readonly) {
+ *out = f->file;
+ return 0;
+ }
+ }
+
giterr_set(GITERR_CONFIG,
- "cannot set value for '%s' when no config files exist", name);
+ "cannot %s value for '%s' when all config files are readonly",
+ uses[use], name);
return GIT_ENOTFOUND;
}
int git_config_delete_entry(git_config *cfg, const char *name)
{
git_config_backend *file;
- file_internal *internal;
- internal = git_vector_get(&cfg->files, 0);
- if (!internal || !internal->file)
- return config_error_nofiles(name);
- file = internal->file;
+ if (get_backend_for_use(&file, cfg, name, BACKEND_USE_DELETE) < 0)
+ return GIT_ENOTFOUND;
return file->del(file, name);
}
@@ -617,17 +645,14 @@
{
int error;
git_config_backend *file;
- file_internal *internal;
if (!value) {
giterr_set(GITERR_CONFIG, "the value to set cannot be NULL");
return -1;
}
- internal = git_vector_get(&cfg->files, 0);
- if (!internal || !internal->file)
- return config_error_nofiles(name);
- file = internal->file;
+ if (get_backend_for_use(&file, cfg, name, BACKEND_USE_SET) < 0)
+ return GIT_ENOTFOUND;
error = file->set(file, name, value);
@@ -1032,12 +1057,9 @@
int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value)
{
git_config_backend *file;
- file_internal *internal;
- internal = git_vector_get(&cfg->files, 0);
- if (!internal || !internal->file)
- return config_error_nofiles(name);
- file = internal->file;
+ if (get_backend_for_use(&file, cfg, name, BACKEND_USE_DELETE) < 0)
+ return GIT_ENOTFOUND;
return file->set_multivar(file, name, regexp, value);
}
@@ -1045,12 +1067,9 @@
int git_config_delete_multivar(git_config *cfg, const char *name, const char *regexp)
{
git_config_backend *file;
- file_internal *internal;
- internal = git_vector_get(&cfg->files, 0);
- if (!internal || !internal->file)
- return config_error_nofiles(name);
- file = internal->file;
+ if (get_backend_for_use(&file, cfg, name, BACKEND_USE_DELETE) < 0)
+ return GIT_ENOTFOUND;
return file->del_multivar(file, name, regexp);
}
diff --git a/src/config_file.h b/src/config_file.h
index 1c52892..654e6ca 100644
--- a/src/config_file.h
+++ b/src/config_file.h
@@ -7,6 +7,7 @@
#ifndef INCLUDE_config_file_h__
#define INCLUDE_config_file_h__
+#include "git2/sys/config.h"
#include "git2/config.h"
GIT_INLINE(int) git_config_file_open(git_config_backend *cfg, unsigned int level)
diff --git a/src/diff_parse.c b/src/diff_parse.c
index 24a8a4a..5e3a7a1 100644
--- a/src/diff_parse.c
+++ b/src/diff_parse.c
@@ -45,7 +45,7 @@
diff->base.free_fn = diff_parsed_free;
if (git_diff_init_options(&diff->base.opts, GIT_DIFF_OPTIONS_VERSION) < 0) {
- git__free(&diff);
+ git__free(diff);
return NULL;
}
diff --git a/src/refs.c b/src/refs.c
index 0837dc4..31410b7 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -249,6 +249,40 @@
return 0;
}
+int git_reference__read_head(
+ git_reference **out,
+ git_repository *repo,
+ const char *path)
+{
+ git_buf reference = GIT_BUF_INIT;
+ char *name = NULL;
+ int error;
+
+ if ((error = git_futils_readbuffer(&reference, path)) < 0)
+ goto out;
+ git_buf_rtrim(&reference);
+
+ if (git__strncmp(reference.ptr, GIT_SYMREF, strlen(GIT_SYMREF)) == 0) {
+ git_buf_consume(&reference, reference.ptr + strlen(GIT_SYMREF));
+
+ name = git_path_basename(path);
+
+ if ((*out = git_reference__alloc_symbolic(name, reference.ptr)) == NULL) {
+ error = -1;
+ goto out;
+ }
+ } else {
+ if ((error = git_reference_lookup(out, repo, reference.ptr)) < 0)
+ goto out;
+ }
+
+out:
+ free(name);
+ git_buf_clear(&reference);
+
+ return error;
+}
+
int git_reference_dwim(git_reference **out, git_repository *repo, const char *refname)
{
int error = 0, i;
@@ -580,19 +614,52 @@
out, ref->db->repo, ref->name, target, 1, ref->target.symbolic, log_message);
}
+typedef struct {
+ const char *old_name;
+ git_refname_t new_name;
+} rename_cb_data;
+
+static int update_wt_heads(git_repository *repo, const char *path, void *payload)
+{
+ rename_cb_data *data = (rename_cb_data *) payload;
+ git_reference *head;
+ char *gitdir = NULL;
+ int error = 0;
+
+ if (git_reference__read_head(&head, repo, path) < 0 ||
+ git_reference_type(head) != GIT_REF_SYMBOLIC ||
+ git__strcmp(head->target.symbolic, data->old_name) != 0 ||
+ (gitdir = git_path_dirname(path)) == NULL)
+ goto out;
+
+ /* Update HEAD it was pointing to the reference being renamed */
+ if ((error = git_repository_create_head(gitdir, data->new_name)) < 0) {
+ giterr_set(GITERR_REFERENCE, "failed to update HEAD after renaming reference");
+ goto out;
+ }
+
+out:
+ git_reference_free(head);
+ git__free(gitdir);
+
+ return error;
+}
+
static int reference__rename(git_reference **out, git_reference *ref, const char *new_name, int force,
const git_signature *signature, const char *message)
{
+ git_repository *repo;
git_refname_t normalized;
bool should_head_be_updated = false;
int error = 0;
assert(ref && new_name && signature);
- if ((error = reference_normalize_for_repo(
- normalized, git_reference_owner(ref), new_name, true)) < 0)
- return error;
+ repo = git_reference_owner(ref);
+ if ((error = reference_normalize_for_repo(
+ normalized, repo, new_name, true)) < 0)
+ return error;
/* Check if we have to update HEAD. */
if ((error = git_branch_is_head(ref)) < 0)
@@ -603,14 +670,18 @@
if ((error = git_refdb_rename(out, ref->db, ref->name, normalized, force, signature, message)) < 0)
return error;
- /* Update HEAD it was pointing to the reference being renamed */
- if (should_head_be_updated &&
- (error = git_repository_set_head(ref->db->repo, normalized)) < 0) {
- giterr_set(GITERR_REFERENCE, "failed to update HEAD after renaming reference");
- return error;
+ /* Update HEAD if it was pointing to the reference being renamed */
+ if (should_head_be_updated) {
+ error = git_repository_set_head(ref->db->repo, normalized);
+ } else {
+ rename_cb_data payload;
+ payload.old_name = ref->name;
+ memcpy(&payload.new_name, &normalized, sizeof(normalized));
+
+ error = git_repository_foreach_head(repo, update_wt_heads, &payload);
}
- return 0;
+ return error;
}
diff --git a/src/refs.h b/src/refs.h
index 80e655a..0c90db3 100644
--- a/src/refs.h
+++ b/src/refs.h
@@ -107,6 +107,20 @@
const char *name,
int max_deref);
+/**
+ * Read reference from a file.
+ *
+ * This function will read in the file at `path`. If it is a
+ * symref, it will return a new unresolved symbolic reference
+ * with the given name pointing to the reference pointed to by
+ * the file. If it is not a symbolic reference, it will return
+ * the resolved reference.
+ */
+int git_reference__read_head(
+ git_reference **out,
+ git_repository *repo,
+ const char *path);
+
int git_reference__log_signature(git_signature **out, git_repository *repo);
/** Update a reference after a commit. */
diff --git a/src/remote.c b/src/remote.c
index d3132f7..d090fda 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -260,7 +260,6 @@
if (error)
git_remote_free(remote);
- git_config_free(config);
git_buf_free(&canonical_url);
git_buf_free(&var);
return error;
@@ -2412,7 +2411,7 @@
proxy = &opts->proxy_opts;
}
- assert(remote && refspecs);
+ assert(remote);
if ((error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs, proxy, custom_headers)) < 0)
return error;
diff --git a/src/repository.c b/src/repository.c
index 425ef79..707b7b7 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -2063,47 +2063,27 @@
return exists;
}
-static int read_worktree_head(git_buf *out, git_repository *repo, const char *name)
+static int get_worktree_file_path(git_buf *out, git_repository *repo, const char *worktree, const char *file)
{
- git_buf path = GIT_BUF_INIT;
- int err;
-
- assert(out && repo && name);
-
git_buf_clear(out);
-
- if ((err = git_buf_printf(&path, "%s/worktrees/%s/HEAD", repo->commondir, name)) < 0)
- goto out;
- if (!git_path_exists(path.ptr))
- {
- err = -1;
- goto out;
- }
-
- if ((err = git_futils_readbuffer(out, path.ptr)) < 0)
- goto out;
- git_buf_rtrim(out);
-
-out:
- git_buf_free(&path);
-
- return err;
+ return git_buf_printf(out, "%s/worktrees/%s/%s", repo->commondir, worktree, file);
}
int git_repository_head_detached_for_worktree(git_repository *repo, const char *name)
{
- git_buf buf = GIT_BUF_INIT;
- int ret;
+ git_reference *ref = NULL;
+ int error;
assert(repo && name);
- if (read_worktree_head(&buf, repo, name) < 0)
- return -1;
+ if ((error = git_repository_head_for_worktree(&ref, repo, name)) < 0)
+ goto out;
- ret = git__strncmp(buf.ptr, GIT_SYMREF, strlen(GIT_SYMREF)) != 0;
- git_buf_free(&buf);
+ error = (git_reference_type(ref) != GIT_REF_SYMBOLIC);
+out:
+ git_reference_free(ref);
- return ret;
+ return error;
}
int git_repository_head(git_reference **head_out, git_repository *repo)
@@ -2127,44 +2107,66 @@
int git_repository_head_for_worktree(git_reference **out, git_repository *repo, const char *name)
{
- git_buf buf = GIT_BUF_INIT;
- git_reference *head;
- int err;
+ git_buf path = GIT_BUF_INIT;
+ git_reference *head = NULL;
+ int error;
assert(out && repo && name);
*out = NULL;
- if (git_repository_head_detached_for_worktree(repo, name))
- return -1;
- if ((err = read_worktree_head(&buf, repo, name)) < 0)
+ if ((error = get_worktree_file_path(&path, repo, name, GIT_HEAD_FILE)) < 0 ||
+ (error = git_reference__read_head(&head, repo, path.ptr)) < 0)
goto out;
- /* We can only resolve symbolic references */
- if (git__strncmp(buf.ptr, GIT_SYMREF, strlen(GIT_SYMREF)))
- {
- err = -1;
- goto out;
- }
- git_buf_consume(&buf, buf.ptr + strlen(GIT_SYMREF));
+ if (git_reference_type(head) != GIT_REF_OID) {
+ git_reference *resolved;
- if ((err = git_reference_lookup(&head, repo, buf.ptr)) < 0)
- goto out;
- if (git_reference_type(head) == GIT_REF_OID)
- {
- *out = head;
- err = 0;
- goto out;
+ error = git_reference_lookup_resolved(&resolved, repo, git_reference_symbolic_target(head), -1);
+ git_reference_free(head);
+ head = resolved;
}
- err = git_reference_lookup_resolved(
- out, repo, git_reference_symbolic_target(head), -1);
- git_reference_free(head);
+ *out = head;
out:
- git_buf_free(&buf);
+ if (error)
+ git_reference_free(head);
+ git_buf_clear(&path);
- return err;
+ return error;
+}
+
+int git_repository_foreach_head(git_repository *repo, git_repository_foreach_head_cb cb, void *payload)
+{
+ git_strarray worktrees = GIT_VECTOR_INIT;
+ git_buf path = GIT_BUF_INIT;
+ int error;
+ size_t i;
+
+ /* Execute callback for HEAD of commondir */
+ if ((error = git_buf_joinpath(&path, repo->commondir, GIT_HEAD_FILE)) < 0 ||
+ (error = cb(repo, path.ptr, payload) != 0))
+ goto out;
+
+ if ((error = git_worktree_list(&worktrees, repo)) < 0) {
+ error = 0;
+ goto out;
+ }
+
+ /* Execute callback for all worktree HEADs */
+ for (i = 0; i < worktrees.count; i++) {
+ if (get_worktree_file_path(&path, repo, worktrees.strings[i], GIT_HEAD_FILE) < 0)
+ continue;
+
+ if ((error = cb(repo, path.ptr, payload)) != 0)
+ goto out;
+ }
+
+out:
+ git_buf_free(&path);
+ git_strarray_free(&worktrees);
+ return error;
}
int git_repository_head_unborn(git_repository *repo)
@@ -2562,6 +2564,8 @@
if (ref && current->type == GIT_REF_SYMBOLIC && git__strcmp(current->target.symbolic, ref->name) &&
git_reference_is_branch(ref) && git_branch_is_checked_out(ref)) {
+ giterr_set(GITERR_REPOSITORY, "cannot set HEAD to reference '%s' as it is the current HEAD "
+ "of a linked repository.", git_reference_name(ref));
error = -1;
goto cleanup;
}
diff --git a/src/repository.h b/src/repository.h
index 33adfa6..f922d00 100644
--- a/src/repository.h
+++ b/src/repository.h
@@ -160,6 +160,26 @@
int git_repository_create_head(const char *git_dir, const char *ref_name);
/*
+ * Called for each HEAD.
+ *
+ * Can return either 0, causing the iteration over HEADs to
+ * continue, or a non-0 value causing the iteration to abort. The
+ * return value is passed back to the caller of
+ * `git_repository_foreach_head`
+ */
+typedef int (*git_repository_foreach_head_cb)(git_repository *repo, const char *path, void *payload);
+
+/*
+ * Iterate over repository and all worktree HEADs.
+ *
+ * This function will be called for the repository HEAD and for
+ * all HEADS of linked worktrees. For each HEAD, the callback is
+ * executed with the given payload. The return value equals the
+ * return value of the last executed callback function.
+ */
+int git_repository_foreach_head(git_repository *repo, git_repository_foreach_head_cb cb, void *payload);
+
+/*
* Weak pointers to repository internals.
*
* The returned pointers do not need to be freed. Do not keep
diff --git a/src/socket_stream.c b/src/socket_stream.c
index fca4117..c0a1684 100644
--- a/src/socket_stream.c
+++ b/src/socket_stream.c
@@ -106,10 +106,8 @@
for (p = info; p != NULL; p = p->ai_next) {
s = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
- if (s == INVALID_SOCKET) {
- net_set_error("error creating socket");
- break;
- }
+ if (s == INVALID_SOCKET)
+ continue;
if (connect(s, p->ai_addr, (socklen_t)p->ai_addrlen) == 0)
break;
diff --git a/tests/config/readonly.c b/tests/config/readonly.c
new file mode 100644
index 0000000..f45abdd
--- /dev/null
+++ b/tests/config/readonly.c
@@ -0,0 +1,64 @@
+#include "clar_libgit2.h"
+#include "config_file.h"
+#include "config.h"
+
+static git_config *cfg;
+
+void test_config_readonly__initialize(void)
+{
+ cl_git_pass(git_config_new(&cfg));
+}
+
+void test_config_readonly__cleanup(void)
+{
+ git_config_free(cfg);
+ cfg = NULL;
+}
+
+void test_config_readonly__writing_to_readonly_fails(void)
+{
+ git_config_backend *backend;
+
+ cl_git_pass(git_config_file__ondisk(&backend, "global"));
+ backend->readonly = 1;
+ cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, 0));
+
+ cl_git_fail_with(GIT_ENOTFOUND, git_config_set_string(cfg, "foo.bar", "baz"));
+ cl_assert(!git_path_exists("global"));
+}
+
+void test_config_readonly__writing_to_cfg_with_rw_precedence_succeeds(void)
+{
+ git_config_backend *backend;
+
+ cl_git_pass(git_config_file__ondisk(&backend, "global"));
+ backend->readonly = 1;
+ cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, 0));
+
+ cl_git_pass(git_config_file__ondisk(&backend, "local"));
+ cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_LOCAL, 0));
+
+ cl_git_pass(git_config_set_string(cfg, "foo.bar", "baz"));
+
+ cl_assert(git_path_exists("local"));
+ cl_assert(!git_path_exists("global"));
+ cl_git_pass(p_unlink("local"));
+}
+
+void test_config_readonly__writing_to_cfg_with_ro_precedence_succeeds(void)
+{
+ git_config_backend *backend;
+
+ cl_git_pass(git_config_file__ondisk(&backend, "local"));
+ backend->readonly = 1;
+ cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_LOCAL, 0));
+
+ cl_git_pass(git_config_file__ondisk(&backend, "global"));
+ cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, 0));
+
+ cl_git_pass(git_config_set_string(cfg, "foo.bar", "baz"));
+
+ cl_assert(!git_path_exists("local"));
+ cl_assert(git_path_exists("global"));
+ cl_git_pass(p_unlink("global"));
+}
diff --git a/tests/worktree/refs.c b/tests/worktree/refs.c
index b9a0560..95b173e 100644
--- a/tests/worktree/refs.c
+++ b/tests/worktree/refs.c
@@ -107,28 +107,42 @@
void test_worktree_refs__delete_fails_for_checked_out_branch(void)
{
- git_reference *branch;
+ git_reference *branch;
- cl_git_pass(git_branch_lookup(&branch, fixture.repo,
- "testrepo-worktree", GIT_BRANCH_LOCAL));
- cl_git_fail(git_branch_delete(branch));
+ cl_git_pass(git_branch_lookup(&branch, fixture.repo,
+ "testrepo-worktree", GIT_BRANCH_LOCAL));
+ cl_git_fail(git_branch_delete(branch));
- git_reference_free(branch);
+ git_reference_free(branch);
}
void test_worktree_refs__delete_succeeds_after_pruning_worktree(void)
{
- git_reference *branch;
- git_worktree *worktree;
+ git_reference *branch;
+ git_worktree *worktree;
- cl_git_pass(git_worktree_lookup(&worktree, fixture.repo, fixture.worktreename));
- cl_git_pass(git_worktree_prune(worktree, GIT_WORKTREE_PRUNE_VALID));
- git_worktree_free(worktree);
+ cl_git_pass(git_worktree_lookup(&worktree, fixture.repo, fixture.worktreename));
+ cl_git_pass(git_worktree_prune(worktree, GIT_WORKTREE_PRUNE_VALID));
+ git_worktree_free(worktree);
- cl_git_pass(git_branch_lookup(&branch, fixture.repo,
- "testrepo-worktree", GIT_BRANCH_LOCAL));
- cl_git_pass(git_branch_delete(branch));
- git_reference_free(branch);
+ cl_git_pass(git_branch_lookup(&branch, fixture.repo,
+ "testrepo-worktree", GIT_BRANCH_LOCAL));
+ cl_git_pass(git_branch_delete(branch));
+ git_reference_free(branch);
+}
+
+void test_worktree_refs__renaming_reference_updates_worktree_heads(void)
+{
+ git_reference *head, *branch, *renamed;
+
+ cl_git_pass(git_branch_lookup(&branch, fixture.repo,
+ "testrepo-worktree", GIT_BRANCH_LOCAL));
+ cl_git_pass(git_reference_rename(&renamed, branch, "refs/heads/renamed", 0, NULL));
+ cl_git_pass(git_repository_head(&head, fixture.worktree));
+
+ git_reference_free(head);
+ git_reference_free(branch);
+ git_reference_free(renamed);
}
void test_worktree_refs__creating_refs_uses_commondir(void)
diff --git a/tests/worktree/worktree.c b/tests/worktree/worktree.c
index 6e90e6a..73991bf 100644
--- a/tests/worktree/worktree.c
+++ b/tests/worktree/worktree.c
@@ -486,3 +486,46 @@
git_worktree_free(wt);
}
+
+static int read_head_ref(git_repository *repo, const char *path, void *payload)
+{
+ git_vector *refs = (git_vector *) payload;
+ git_reference *head;
+
+ GIT_UNUSED(repo);
+
+ cl_git_pass(git_reference__read_head(&head, repo, path));
+
+ git_vector_insert(refs, head);
+
+ return 0;
+}
+
+void test_worktree_worktree__foreach_head_gives_same_results_in_wt_and_repo(void)
+{
+ git_vector repo_refs = GIT_VECTOR_INIT, worktree_refs = GIT_VECTOR_INIT;
+ git_reference *heads[2];
+ size_t i;
+
+ cl_git_pass(git_reference_lookup(&heads[0], fixture.repo, GIT_HEAD_FILE));
+ cl_git_pass(git_reference_lookup(&heads[1], fixture.worktree, GIT_HEAD_FILE));
+
+ cl_git_pass(git_repository_foreach_head(fixture.repo, read_head_ref, &repo_refs));
+ cl_git_pass(git_repository_foreach_head(fixture.worktree, read_head_ref, &worktree_refs));
+
+ cl_assert_equal_i(repo_refs.length, ARRAY_SIZE(heads));
+ cl_assert_equal_i(worktree_refs.length, ARRAY_SIZE(heads));
+
+ for (i = 0; i < ARRAY_SIZE(heads); i++) {
+ cl_assert_equal_s(heads[i]->name, ((git_reference *) repo_refs.contents[i])->name);
+ cl_assert_equal_s(heads[i]->name, ((git_reference *) repo_refs.contents[i])->name);
+ cl_assert_equal_s(heads[i]->name, ((git_reference *) worktree_refs.contents[i])->name);
+
+ git_reference_free(heads[i]);
+ git_reference_free(repo_refs.contents[i]);
+ git_reference_free(worktree_refs.contents[i]);
+ }
+
+ git_vector_free(&repo_refs);
+ git_vector_free(&worktree_refs);
+}