Merge pull request #4206 from libgit2/cmn/transport-get-proxy

transport: provide a getter for the proxy options
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0ae75e1..bcf8160 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,10 @@
 
 ### API additions
 
+* You can now set the default share mode on Windows for opening files using
+  `GIT_OPT_SET_WINDOWS_SHAREMODE` option with `git_libgit2_opts()`.
+  You can query the current share mode with `GIT_OPT_GET_WINDOWS_SHAREMODE`.
+
 ### API removals
 
 ### Breaking API changes
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d5bf21f..ca52db7 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -238,6 +238,7 @@
 
 IF (WIN32 AND WINHTTP)
 	ADD_DEFINITIONS(-DGIT_WINHTTP)
+	ADD_DEFINITIONS(-DGIT_HTTPS)
 
 	# Since MinGW does not come with headers or an import library for winhttp,
 	# we have to include a private header and generate our own import library
@@ -546,11 +547,13 @@
 
 IF (SECURITY_FOUND)
   ADD_DEFINITIONS(-DGIT_SECURE_TRANSPORT)
+  ADD_DEFINITIONS(-DGIT_HTTPS)
   INCLUDE_DIRECTORIES(${SECURITY_INCLUDE_DIR})
 ENDIF ()
 
 IF (OPENSSL_FOUND)
   ADD_DEFINITIONS(-DGIT_OPENSSL)
+  ADD_DEFINITIONS(-DGIT_HTTPS)
   INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR})
   SET(SSL_LIBRARIES ${OPENSSL_LIBRARIES})
 ENDIF()
diff --git a/include/git2/common.h b/include/git2/common.h
index c909f86..6d20920 100644
--- a/include/git2/common.h
+++ b/include/git2/common.h
@@ -180,6 +180,8 @@
 	GIT_OPT_GET_USER_AGENT,
 	GIT_OPT_ENABLE_OFS_DELTA,
 	GIT_OPT_ENABLE_SYNCHRONOUS_OBJECT_CREATION,
+	GIT_OPT_GET_WINDOWS_SHAREMODE,
+	GIT_OPT_SET_WINDOWS_SHAREMODE,
 } git_libgit2_opt_t;
 
 /**
@@ -284,6 +286,17 @@
  *		> - `user_agent` is the value that will be delivered as the
  *		>   User-Agent header on HTTP requests.
  *
+ *	* opts(GIT_OPT_SET_WINDOWS_SHAREMODE, unsigned long value)
+ *
+ *		> Set the share mode used when opening files on Windows.
+ *		> For more information, see the documentation for CreateFile.
+ *		> The default is: FILE_SHARE_READ | FILE_SHARE_WRITE.  This is
+ *		> ignored and unused on non-Windows platforms.
+ *
+ *	* opts(GIT_OPT_GET_WINDOWS_SHAREMODE, unsigned long *value)
+ *
+ *		> Get the share mode used when opening files on Windows.
+ *
  *	* opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, int enabled)
  *
  *		> Enable strict input validation when creating new objects
diff --git a/include/git2/errors.h b/include/git2/errors.h
index 3b746b7..71bff0f 100644
--- a/include/git2/errors.h
+++ b/include/git2/errors.h
@@ -53,6 +53,7 @@
 
 	GIT_PASSTHROUGH     = -30,	/**< Internal only */
 	GIT_ITEROVER        = -31,	/**< Signals end of iteration with iterator */
+	GIT_RETRY           = -32,	/**< Internal only */
 } git_error_code;
 
 /**
diff --git a/include/git2/global.h b/include/git2/global.h
index ce5bdf4..2a87e10 100644
--- a/include/git2/global.h
+++ b/include/git2/global.h
@@ -14,7 +14,7 @@
 /**
  * Init the global state
  *
- * This function must the called before any other libgit2 function in
+ * This function must be called before any other libgit2 function in
  * order to set up global state and threading.
  *
  * This function may be called multiple times - it will return the number
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/posix.h b/src/posix.h
index bd5a98e..d26371b 100644
--- a/src/posix.h
+++ b/src/posix.h
@@ -24,6 +24,10 @@
 #define _S_IFLNK S_IFLNK
 #endif
 
+#ifndef S_IWUSR
+#define S_IWUSR 00200
+#endif
+
 #ifndef S_IXUSR
 #define S_IXUSR 00100
 #endif
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/settings.c b/src/settings.c
index 24e549e..42f247a 100644
--- a/src/settings.c
+++ b/src/settings.c
@@ -31,7 +31,7 @@
 #ifdef GIT_THREADS
 		| GIT_FEATURE_THREADS
 #endif
-#if defined(GIT_OPENSSL) || defined(GIT_WINHTTP) || defined(GIT_SECURE_TRANSPORT)
+#ifdef GIT_HTTPS
 		| GIT_FEATURE_HTTPS
 #endif
 #if defined(GIT_SSH)
@@ -231,6 +231,18 @@
 		git_object__synchronous_writing = (va_arg(ap, int) != 0);
 		break;
 
+	case GIT_OPT_GET_WINDOWS_SHAREMODE:
+#ifdef GIT_WIN32
+		*(va_arg(ap, unsigned long *)) = git_win32__createfile_sharemode;
+#endif
+		break;
+
+	case GIT_OPT_SET_WINDOWS_SHAREMODE:
+#ifdef GIT_WIN32
+		git_win32__createfile_sharemode = va_arg(ap, unsigned long);
+#endif
+		break;
+
 	default:
 		giterr_set(GITERR_INVALID, "invalid option key");
 		error = -1;
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/src/win32/posix.h b/src/win32/posix.h
index 73705fb..64769ec 100644
--- a/src/win32/posix.h
+++ b/src/win32/posix.h
@@ -14,6 +14,9 @@
 #include "utf-conv.h"
 #include "dir.h"
 
+extern unsigned long git_win32__createfile_sharemode;
+extern int git_win32__retries;
+
 typedef SOCKET GIT_SOCKET;
 
 #define p_lseek(f,n,w) _lseeki64(f, n, w)
diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c
index 5172627..e4fe414 100644
--- a/src/win32/posix_w32.c
+++ b/src/win32/posix_w32.c
@@ -26,15 +26,6 @@
 #define IO_REPARSE_TAG_SYMLINK (0xA000000CL)
 #endif
 
-/* Options which we always provide to _wopen.
- *
- * _O_BINARY - Raw access; no translation of CR or LF characters
- * _O_NOINHERIT - Do not mark the created handle as inheritable by child processes.
- *    The Windows default is 'not inheritable', but the CRT's default (following
- *    POSIX convention) is 'inheritable'. We have no desire for our handles to be
- *    inheritable on Windows, so specify the flag to get default behavior back. */
-#define STANDARD_OPEN_FLAGS (_O_BINARY | _O_NOINHERIT)
-
 /* Allowable mode bits on Win32.  Using mode bits that are not supported on
  * Win32 (eg S_IRWXU) is generally ignored, but Wine warns loudly about it
  * so we simply remove them.
@@ -44,6 +35,164 @@
 /* GetFinalPathNameByHandleW signature */
 typedef DWORD(WINAPI *PFGetFinalPathNameByHandleW)(HANDLE, LPWSTR, DWORD, DWORD);
 
+unsigned long git_win32__createfile_sharemode =
+ FILE_SHARE_READ | FILE_SHARE_WRITE;
+int git_win32__retries = 10;
+
+GIT_INLINE(void) set_errno(void)
+{
+	switch (GetLastError()) {
+	case ERROR_FILE_NOT_FOUND:
+	case ERROR_PATH_NOT_FOUND:
+	case ERROR_INVALID_DRIVE:
+	case ERROR_NO_MORE_FILES:
+	case ERROR_BAD_NETPATH:
+	case ERROR_BAD_NET_NAME:
+	case ERROR_BAD_PATHNAME:
+	case ERROR_FILENAME_EXCED_RANGE:
+		errno = ENOENT;
+		break;
+	case ERROR_BAD_ENVIRONMENT:
+		errno = E2BIG;
+		break;
+	case ERROR_BAD_FORMAT:
+	case ERROR_INVALID_STARTING_CODESEG:
+	case ERROR_INVALID_STACKSEG:
+	case ERROR_INVALID_MODULETYPE:
+	case ERROR_INVALID_EXE_SIGNATURE:
+	case ERROR_EXE_MARKED_INVALID:
+	case ERROR_BAD_EXE_FORMAT:
+	case ERROR_ITERATED_DATA_EXCEEDS_64k:
+	case ERROR_INVALID_MINALLOCSIZE:
+	case ERROR_DYNLINK_FROM_INVALID_RING:
+	case ERROR_IOPL_NOT_ENABLED:
+	case ERROR_INVALID_SEGDPL:
+	case ERROR_AUTODATASEG_EXCEEDS_64k:
+	case ERROR_RING2SEG_MUST_BE_MOVABLE:
+	case ERROR_RELOC_CHAIN_XEEDS_SEGLIM:
+	case ERROR_INFLOOP_IN_RELOC_CHAIN:
+		errno = ENOEXEC;
+		break;
+	case ERROR_INVALID_HANDLE:
+	case ERROR_INVALID_TARGET_HANDLE:
+	case ERROR_DIRECT_ACCESS_HANDLE:
+		errno = EBADF;
+		break;
+	case ERROR_WAIT_NO_CHILDREN:
+	case ERROR_CHILD_NOT_COMPLETE:
+		errno = ECHILD;
+		break;
+	case ERROR_NO_PROC_SLOTS:
+	case ERROR_MAX_THRDS_REACHED:
+	case ERROR_NESTING_NOT_ALLOWED:
+		errno = EAGAIN;
+		break;
+	case ERROR_ARENA_TRASHED:
+	case ERROR_NOT_ENOUGH_MEMORY:
+	case ERROR_INVALID_BLOCK:
+	case ERROR_NOT_ENOUGH_QUOTA:
+		errno = ENOMEM;
+		break;
+	case ERROR_ACCESS_DENIED:
+	case ERROR_CURRENT_DIRECTORY:
+	case ERROR_WRITE_PROTECT:
+	case ERROR_BAD_UNIT:
+	case ERROR_NOT_READY:
+	case ERROR_BAD_COMMAND:
+	case ERROR_CRC:
+	case ERROR_BAD_LENGTH:
+	case ERROR_SEEK:
+	case ERROR_NOT_DOS_DISK:
+	case ERROR_SECTOR_NOT_FOUND:
+	case ERROR_OUT_OF_PAPER:
+	case ERROR_WRITE_FAULT:
+	case ERROR_READ_FAULT:
+	case ERROR_GEN_FAILURE:
+	case ERROR_SHARING_VIOLATION:
+	case ERROR_LOCK_VIOLATION:
+	case ERROR_WRONG_DISK:
+	case ERROR_SHARING_BUFFER_EXCEEDED:
+	case ERROR_NETWORK_ACCESS_DENIED:
+	case ERROR_CANNOT_MAKE:
+	case ERROR_FAIL_I24:
+	case ERROR_DRIVE_LOCKED:
+	case ERROR_SEEK_ON_DEVICE:
+	case ERROR_NOT_LOCKED:
+	case ERROR_LOCK_FAILED:
+		errno = EACCES;
+		break;
+	case ERROR_FILE_EXISTS:
+	case ERROR_ALREADY_EXISTS:
+		errno = EEXIST;
+		break;
+	case ERROR_NOT_SAME_DEVICE:
+		errno = EXDEV;
+		break;
+	case ERROR_INVALID_FUNCTION:
+	case ERROR_INVALID_ACCESS:
+	case ERROR_INVALID_DATA:
+	case ERROR_INVALID_PARAMETER:
+	case ERROR_NEGATIVE_SEEK:
+		errno = EINVAL;
+		break;
+	case ERROR_TOO_MANY_OPEN_FILES:
+		errno = EMFILE;
+		break;
+	case ERROR_DISK_FULL:
+		errno = ENOSPC;
+		break;
+	case ERROR_BROKEN_PIPE:
+		errno = EPIPE;
+		break;
+	case ERROR_DIR_NOT_EMPTY:
+		errno = ENOTEMPTY;
+		break;
+	default:
+		errno = EINVAL;
+	}
+}
+
+GIT_INLINE(bool) last_error_retryable(void)
+{
+	int os_error = GetLastError();
+
+	return (os_error == ERROR_SHARING_VIOLATION ||
+		os_error == ERROR_ACCESS_DENIED);
+}
+
+#define do_with_retries(fn, remediation) \
+	do {                                                             \
+		int __tries, __ret;                                          \
+		for (__tries = 0; __tries < git_win32__retries; __tries++) { \
+			if (__tries && (__ret = (remediation)) != 0)             \
+				return __ret;                                        \
+			if ((__ret = (fn)) != GIT_RETRY)                         \
+				return __ret;                                        \
+			Sleep(5);                                                \
+		}                                                            \
+		return -1;                                                   \
+	} while (0)                                                      \
+
+static int ensure_writable(wchar_t *path)
+{
+	DWORD attrs;
+
+	if ((attrs = GetFileAttributesW(path)) == INVALID_FILE_ATTRIBUTES)
+		goto on_error;
+
+	if ((attrs & FILE_ATTRIBUTE_READONLY) == 0)
+		return 0;
+
+	if (!SetFileAttributesW(path, (attrs & ~FILE_ATTRIBUTE_READONLY)))
+		goto on_error;
+
+	return 0;
+
+on_error:
+	set_errno();
+	return -1;
+}
+
 /**
  * Truncate or extend file.
  *
@@ -89,24 +238,26 @@
 	return -1;
 }
 
+GIT_INLINE(int) unlink_once(const wchar_t *path)
+{
+	if (DeleteFileW(path))
+		return 0;
+
+	if (last_error_retryable())
+		return GIT_RETRY;
+
+	set_errno();
+	return -1;
+}
+
 int p_unlink(const char *path)
 {
-	git_win32_path buf;
-	int error;
+	git_win32_path wpath;
 
-	if (git_win32_path_from_utf8(buf, path) < 0)
+	if (git_win32_path_from_utf8(wpath, path) < 0)
 		return -1;
 
-	error = _wunlink(buf);
-
-	/* If the file could not be deleted because it was
-	 * read-only, clear the bit and try again */
-	if (error == -1 && errno == EACCES) {
-		_wchmod(buf, 0666);
-		error = _wunlink(buf);
-	}
-
-	return error;
+	do_with_retries(unlink_once(wpath), ensure_writable(wpath));
 }
 
 int p_fsync(int fd)
@@ -212,44 +363,6 @@
 	return do_lstat(filename, buf, true);
 }
 
-int p_utimes(const char *filename, const struct p_timeval times[2])
-{
-	int fd, error;
-
-	if ((fd = p_open(filename, O_RDWR)) < 0)
-		return fd;
-
-	error = p_futimes(fd, times);
-
-	close(fd);
-	return error;
-}
-
-int p_futimes(int fd, const struct p_timeval times[2])
-{
-	HANDLE handle;
-	FILETIME atime = {0}, mtime = {0};
-
-	if (times == NULL) {
-		SYSTEMTIME st;
-
-		GetSystemTime(&st);
-		SystemTimeToFileTime(&st, &atime);
-		SystemTimeToFileTime(&st, &mtime);
-	} else {
-		git_win32__timeval_to_filetime(&atime, times[0]);
-		git_win32__timeval_to_filetime(&mtime, times[1]);
-	}
-
-	if ((handle = (HANDLE)_get_osfhandle(fd)) == INVALID_HANDLE_VALUE)
-		return -1;
-
-	if (SetFileTime(handle, NULL, &atime, &mtime) == 0)
-		return -1;
-
-	return 0;
-}
-
 int p_readlink(const char *path, char *buf, size_t bufsiz)
 {
 	git_win32_path path_w, target_w;
@@ -282,12 +395,91 @@
 	return git_futils_fake_symlink(old, new);
 }
 
+struct open_opts {
+	DWORD access;
+	DWORD sharing;
+	SECURITY_ATTRIBUTES security;
+	DWORD creation_disposition;
+	DWORD attributes;
+	int osf_flags;
+};
+
+GIT_INLINE(void) open_opts_from_posix(struct open_opts *opts, int flags, mode_t mode)
+{
+	memset(opts, 0, sizeof(struct open_opts));
+
+	switch (flags & (O_WRONLY | O_RDWR)) {
+	case O_WRONLY:
+		opts->access = GENERIC_WRITE;
+		break;
+	case O_RDWR:
+		opts->access = GENERIC_READ | GENERIC_WRITE;
+		break;
+	default:
+		opts->access = GENERIC_READ;
+		break;
+	}
+
+	opts->sharing = (DWORD)git_win32__createfile_sharemode;
+
+	switch (flags & (O_CREAT | O_TRUNC | O_EXCL)) {
+	case O_CREAT | O_EXCL:
+	case O_CREAT | O_TRUNC | O_EXCL:
+		opts->creation_disposition = CREATE_NEW;
+		break;
+	case O_CREAT | O_TRUNC:
+		opts->creation_disposition = CREATE_ALWAYS;
+		break;
+	case O_TRUNC:
+		opts->creation_disposition = TRUNCATE_EXISTING;
+		break;
+	case O_CREAT:
+		opts->creation_disposition = OPEN_ALWAYS;
+		break;
+	default:
+		opts->creation_disposition = OPEN_EXISTING;
+		break;
+	}
+
+	opts->attributes = ((flags & O_CREAT) && !(mode & S_IWRITE)) ?
+		FILE_ATTRIBUTE_READONLY : FILE_ATTRIBUTE_NORMAL;
+	opts->osf_flags = flags & (O_RDONLY | O_APPEND);
+
+	opts->security.nLength = sizeof(SECURITY_ATTRIBUTES);
+	opts->security.lpSecurityDescriptor = NULL;
+	opts->security.bInheritHandle = 0;
+}
+
+GIT_INLINE(int) open_once(
+	const wchar_t *path,
+	struct open_opts *opts)
+{
+	int fd;
+
+	HANDLE handle = CreateFileW(path, opts->access, opts->sharing,
+		&opts->security, opts->creation_disposition, opts->attributes, 0);
+
+	if (handle == INVALID_HANDLE_VALUE) {
+		if (last_error_retryable())
+			return GIT_RETRY;
+
+		set_errno();
+		return -1;
+	}
+
+	if ((fd = _open_osfhandle((intptr_t)handle, opts->osf_flags)) < 0)
+		CloseHandle(handle);
+
+	return fd;
+}
+
 int p_open(const char *path, int flags, ...)
 {
-	git_win32_path buf;
+	git_win32_path wpath;
 	mode_t mode = 0;
+	struct open_opts opts = {0};
 
-	if (git_win32_path_from_utf8(buf, path) < 0)
+	if (git_win32_path_from_utf8(wpath, path) < 0)
 		return -1;
 
 	if (flags & O_CREAT) {
@@ -298,19 +490,83 @@
 		va_end(arg_list);
 	}
 
-	return _wopen(buf, flags | STANDARD_OPEN_FLAGS, mode & WIN32_MODE_MASK);
+	open_opts_from_posix(&opts, flags, mode);
+
+	do_with_retries(
+		open_once(wpath, &opts),
+		0);
 }
 
 int p_creat(const char *path, mode_t mode)
 {
-	git_win32_path buf;
+	return p_open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);
+}
 
-	if (git_win32_path_from_utf8(buf, path) < 0)
+int p_utimes(const char *path, const struct p_timeval times[2])
+{
+	git_win32_path wpath;
+	int fd, error;
+	DWORD attrs_orig, attrs_new = 0;
+	struct open_opts opts = { 0 };
+
+	if (git_win32_path_from_utf8(wpath, path) < 0)
 		return -1;
 
-	return _wopen(buf,
-		_O_WRONLY | _O_CREAT | _O_TRUNC | STANDARD_OPEN_FLAGS,
-		mode & WIN32_MODE_MASK);
+	attrs_orig = GetFileAttributesW(wpath);
+
+	if (attrs_orig & FILE_ATTRIBUTE_READONLY) {
+		attrs_new = attrs_orig & ~FILE_ATTRIBUTE_READONLY;
+
+		if (!SetFileAttributesW(wpath, attrs_new)) {
+			giterr_set(GITERR_OS, "failed to set attributes");
+			return -1;
+		}
+	}
+
+	open_opts_from_posix(&opts, O_RDWR, 0);
+
+	if ((fd = open_once(wpath, &opts)) < 0) {
+		error = -1;
+		goto done;
+	}
+
+	error = p_futimes(fd, times);
+	close(fd);
+
+done:
+	if (attrs_orig != attrs_new) {
+		DWORD os_error = GetLastError();
+		SetFileAttributesW(wpath, attrs_orig);
+		SetLastError(os_error);
+	}
+
+	return error;
+}
+
+int p_futimes(int fd, const struct p_timeval times[2])
+{
+	HANDLE handle;
+	FILETIME atime = { 0 }, mtime = { 0 };
+
+	if (times == NULL) {
+		SYSTEMTIME st;
+
+		GetSystemTime(&st);
+		SystemTimeToFileTime(&st, &atime);
+		SystemTimeToFileTime(&st, &mtime);
+	}
+	else {
+		git_win32__timeval_to_filetime(&atime, times[0]);
+		git_win32__timeval_to_filetime(&mtime, times[1]);
+	}
+
+	if ((handle = (HANDLE)_get_osfhandle(fd)) == INVALID_HANDLE_VALUE)
+		return -1;
+
+	if (SetFileTime(handle, NULL, &atime, &mtime) == 0)
+		return -1;
+
+	return 0;
 }
 
 int p_getcwd(char *buffer_out, size_t size)
@@ -583,62 +839,27 @@
 	return _waccess(buf, mode & WIN32_MODE_MASK);
 }
 
-static int ensure_writable(wchar_t *fpath)
+GIT_INLINE(int) rename_once(const wchar_t *from, const wchar_t *to)
 {
-	DWORD attrs;
-
-	attrs = GetFileAttributesW(fpath);
-	if (attrs == INVALID_FILE_ATTRIBUTES) {
-		if (GetLastError() == ERROR_FILE_NOT_FOUND)
-			return 0;
-
-		giterr_set(GITERR_OS, "failed to get attributes");
-		return -1;
-	}
-
-	if (!(attrs & FILE_ATTRIBUTE_READONLY))
+	if (MoveFileExW(from, to, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED))
 		return 0;
 
-	attrs &= ~FILE_ATTRIBUTE_READONLY;
-	if (!SetFileAttributesW(fpath, attrs)) {
-		giterr_set(GITERR_OS, "failed to set attributes");
-		return -1;
-	}
+	if (last_error_retryable())
+		return GIT_RETRY;
 
-	return 0;
+	set_errno();
+	return -1;
 }
 
 int p_rename(const char *from, const char *to)
 {
-	git_win32_path wfrom;
-	git_win32_path wto;
-	int rename_tries;
-	int rename_succeeded;
-	int error;
+	git_win32_path wfrom, wto;
 
 	if (git_win32_path_from_utf8(wfrom, from) < 0 ||
 		git_win32_path_from_utf8(wto, to) < 0)
 		return -1;
 
-	/* wait up to 50ms if file is locked by another thread or process */
-	rename_tries = 0;
-	rename_succeeded = 0;
-	while (rename_tries < 10) {
-		if (ensure_writable(wto) == 0 &&
-		    MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) != 0) {
-			rename_succeeded = 1;
-			break;
-		}
-		
-		error = GetLastError();
-		if (error == ERROR_SHARING_VIOLATION || error == ERROR_ACCESS_DENIED) {
-			Sleep(5);
-			rename_tries++;
-		} else
-			break;
-	}
-	
-	return rename_succeeded ? 0 : -1;
+	do_with_retries(rename_once(wfrom, wto), ensure_writable(wto));
 }
 
 int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags)
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/core/features.c b/tests/core/features.c
index cf5e190..7b28cc0 100644
--- a/tests/core/features.c
+++ b/tests/core/features.c
@@ -17,7 +17,9 @@
 	cl_assert((caps & GIT_FEATURE_THREADS) == 0);
 #endif
 
+#ifdef GIT_HTTPS
 	cl_assert((caps & GIT_FEATURE_HTTPS) != 0);
+#endif
 
 #if defined(GIT_SSH)
 	cl_assert((caps & GIT_FEATURE_SSH) != 0);
diff --git a/tests/odb/freshen.c b/tests/odb/freshen.c
index f41e436..9d3cf51 100644
--- a/tests/odb/freshen.c
+++ b/tests/odb/freshen.c
@@ -55,6 +55,31 @@
 	cl_assert(before.st_mtime < after.st_mtime);
 }
 
+#define UNIQUE_STR     "doesnt exist in the odb yet\n"
+#define UNIQUE_BLOB_ID "78a87d0b8878c5953b9a63015ff4e22a3d898826"
+#define UNIQUE_BLOB_FN "78/a87d0b8878c5953b9a63015ff4e22a3d898826"
+
+void test_odb_freshen__readonly_object(void)
+{
+	git_oid expected_id, id;
+	struct stat before, after;
+
+	cl_git_pass(git_oid_fromstr(&expected_id, UNIQUE_BLOB_ID));
+
+	cl_git_pass(git_blob_create_frombuffer(&id, repo, UNIQUE_STR, CONST_STRLEN(UNIQUE_STR)));
+	cl_assert_equal_oid(&expected_id, &id);
+
+	set_time_wayback(&before, UNIQUE_BLOB_FN);
+	cl_assert((before.st_mode & S_IWUSR) == 0);
+
+	cl_git_pass(git_blob_create_frombuffer(&id, repo, UNIQUE_STR, CONST_STRLEN(UNIQUE_STR)));
+	cl_assert_equal_oid(&expected_id, &id);
+	cl_must_pass(p_lstat("testrepo.git/objects/" UNIQUE_BLOB_FN, &after));
+
+	cl_assert(before.st_atime < after.st_atime);
+	cl_assert(before.st_mtime < after.st_mtime);
+}
+
 #define LOOSE_TREE_ID "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162"
 #define LOOSE_TREE_FN "94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162"
 
diff --git a/tests/online/badssl.c b/tests/online/badssl.c
index aa4c24d..6524fcd 100644
--- a/tests/online/badssl.c
+++ b/tests/online/badssl.c
@@ -4,7 +4,7 @@
 
 static git_repository *g_repo;
 
-#if defined(GIT_OPENSSL) || defined(GIT_WINHTTP) || defined(GIT_SECURE_TRANSPORT)
+#ifdef GIT_HTTPS
 static bool g_has_ssl = true;
 #else
 static bool g_has_ssl = false;
diff --git a/tests/threads/diff.c b/tests/threads/diff.c
index c328114..2560402 100644
--- a/tests/threads/diff.c
+++ b/tests/threads/diff.c
@@ -19,12 +19,27 @@
 static git_tree *_a, *_b;
 static git_atomic _counts[4];
 static int _check_counts;
+#ifdef GIT_WIN32
+static int _retries;
+#endif
 
 #define THREADS 20
 
+void test_threads_diff__initialize(void)
+{
+#ifdef GIT_WIN32
+	_retries = git_win32__retries;
+	git_win32__retries = 1;
+#endif
+}
+
 void test_threads_diff__cleanup(void)
 {
 	cl_git_sandbox_cleanup();
+
+#ifdef GIT_WIN32
+	git_win32__retries = _retries;
+#endif
 }
 
 static void setup_trees(void)
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);
+}