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);
+}