wip
diff --git a/include/git2/push.h b/include/git2/push.h
index ecabff3..77e4d06 100644
--- a/include/git2/push.h
+++ b/include/git2/push.h
@@ -34,6 +34,19 @@
 	 * to create. The default value is 1.
 	 */
 	unsigned int pb_parallelism;
+	/**
+	 * The refspecs to use for this push. Leave empty to use the
+	 * base refspecs.
+	 */
+	git_strarray refspecs;
+	/**
+	 * Identity to use to update the reflog.
+	 */
+	git_signature *signature;
+	/**
+	 * The message to insert into the reflogs.
+	 */
+	char *reflog_message;
 } git_push_options;
 
 #define GIT_PUSH_OPTIONS_VERSION 1
diff --git a/include/git2/remote.h b/include/git2/remote.h
index 2bfc35f..abb8927 100644
--- a/include/git2/remote.h
+++ b/include/git2/remote.h
@@ -98,17 +98,6 @@
 GIT_EXTERN(int) git_remote_lookup(git_remote **out, git_repository *repo, const char *name);
 
 /**
- * Save a remote to its repository's configuration
- *
- * One can't save a in-memory remote. Doing so will
- * result in a GIT_EINVALIDSPEC being returned.
- *
- * @param remote the remote to save to config
- * @return 0, GIT_EINVALIDSPEC or an error code
- */
-GIT_EXTERN(int) git_remote_save(const git_remote *remote);
-
-/**
  * Create a copy of an existing remote.  All internal strings are also
  * duplicated. Callbacks are not duplicated.
  *
@@ -317,7 +306,7 @@
  * download. Use NULL or an empty array to use the base refspecs
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_remote_download(git_remote *remote, const git_strarray *refspecs);
+GIT_EXTERN(int) git_remote_download(git_remote *remote, const git_remote_callbacks *callbacks, const git_strarray *refspecs);
 
 /**
  * Create a packfile and send it to the server
@@ -330,7 +319,7 @@
  * upload. Use NULL or an empty array to use the base refspecs
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_remote_upload(git_remote *remote, const git_strarray *refspecs, const git_push_options *opts);
+GIT_EXTERN(int) git_remote_upload(git_remote *remote, const git_remote_callbacks *callbacks, const git_push_options *opts);
 
 /**
  * Check whether the remote is connected
@@ -396,24 +385,77 @@
 GIT_EXTERN(int) git_remote_prune(git_remote *remote);
 
 /**
+ * Automatic tag following option
+ *
+ * Lets us select the --tags option to use.
+ */
+typedef enum {
+	GIT_REMOTE_DOWNLOAD_TAGS_AUTO = 0,
+	GIT_REMOTE_DOWNLOAD_TAGS_NONE = 1,
+	GIT_REMOTE_DOWNLOAD_TAGS_ALL = 2
+} git_remote_autotag_option_t;
+
+typedef struct {
+	unsigned int version;
+	/**
+	 * Whether to update FETCH_HEAD setting.  By default,
+	 * FETCH_HEAD will be updated on every fetch.  Set to 0 to
+	 * disable.
+	 */
+	unsigned int update_fetchhead;
+	/**
+	 * Set to true to prune stale references after fetching
+	 */
+	unsigned int prune;
+	/**
+	 * The tag auto-follow setting
+	 */
+	git_remote_autotag_option_t autotag;
+	/**
+	 * The refspecs to use for this fetch. Leave empty to use the
+	 * base refspecs.
+	 */
+	git_strarray refspecs;
+	/**
+	 * Identity to use to update the reflog.
+	 */
+	const git_signature *signature;
+	/**
+	 * The message to insert into the reflogs. If NULL, the default is "fetch".
+	 */
+	const char *reflog_message;
+} git_fetch_options;
+
+#define GIT_FETCH_OPTIONS_VERSION 1
+#define GIT_FETCH_OPTIONS_INIT { GIT_FETCH_OPTIONS_VERSION, 1 }
+
+/**
+ * Initializes a `git_fetch_options` with default values. Equivalent to
+ * creating an instance with GIT_FETCH_OPTIONS_INIT.
+ *
+ * @param opts the `git_fetch_options` instance to initialize.
+ * @param version the version of the struct; you should pass
+ *        `GIT_FETCH_OPTIONS_VERSION` here.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_fetch_init_options(
+	git_fetch_options *opts,
+	unsigned int version);
+
+/**
  * Download new data and update tips
  *
  * Convenience function to connect to a remote, download the data,
  * disconnect and update the remote-tracking branches.
  *
  * @param remote the remote to fetch from
- * @param refspecs the refspecs to use for this fetch. Pass NULL or an
- *                 empty array to use the base refspecs.
- * @param signature The identity to use when updating reflogs
- * @param reflog_message The message to insert into the reflogs. If NULL, the
- *								 default is "fetch"
+ * @param opts configuration to use for this fetch
  * @return 0 or an error code
  */
 GIT_EXTERN(int) git_remote_fetch(
-		git_remote *remote,
-		const git_strarray *refspecs,
-		const git_signature *signature,
-		const char *reflog_message);
+	git_remote *remote,
+	const git_remote_callbacks *callbacks,
+	const git_fetch_options *opts);
 
 /**
  * Perform a push
@@ -421,16 +463,12 @@
  * 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 opts the options
- * @param signature signature to use for the reflog of updated references
- * @param reflog_message message to use for the reflog of upated references
+ * @param opts the options to use for this push
  */
-GIT_EXTERN(int) git_remote_push(git_remote *remote,
-				const git_strarray *refspecs,
-				const git_push_options *opts,
-				const git_signature *signature, const char *reflog_message);
+GIT_EXTERN(int) git_remote_push(
+	git_remote *remote,
+	const git_remote_callbacks *callbacks,
+	const git_push_options *opts);
 
 /**
  * Get a list of the configured remotes for a repo
@@ -549,45 +587,11 @@
 	unsigned int version);
 
 /**
- * Set the callbacks for a remote
- *
- * Note that the remote keeps its own copy of the data and you need to
- * call this function again if you want to change the callbacks.
- *
- * @param remote the remote to configure
- * @param callbacks a pointer to the user's callback settings
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_remote_set_callbacks(git_remote *remote, const git_remote_callbacks *callbacks);
-
-/**
- * Retrieve the current callback structure
- *
- * This provides read access to the callbacks structure as the remote
- * sees it.
- *
- * @param remote the remote to query
- * @return a pointer to the callbacks structure
- */
-GIT_EXTERN(const git_remote_callbacks *) git_remote_get_callbacks(git_remote *remote);
-
-/**
  * Get the statistics structure that is filled in by the fetch operation.
  */
 GIT_EXTERN(const git_transfer_progress *) git_remote_stats(git_remote *remote);
 
 /**
- * Automatic tag following option
- *
- * Lets us select the --tags option to use.
- */
-typedef enum {
-	GIT_REMOTE_DOWNLOAD_TAGS_AUTO = 0,
-	GIT_REMOTE_DOWNLOAD_TAGS_NONE = 1,
-	GIT_REMOTE_DOWNLOAD_TAGS_ALL = 2
-} git_remote_autotag_option_t;
-
-/**
  * Retrieve the tag auto-follow setting
  *
  * @param remote the remote to query
@@ -596,16 +600,6 @@
 GIT_EXTERN(git_remote_autotag_option_t) git_remote_autotag(const git_remote *remote);
 
 /**
- * Set the tag auto-follow setting
- *
- * @param remote the remote to configure
- * @param value a GIT_REMOTE_DOWNLOAD_TAGS value
- */
-GIT_EXTERN(void) git_remote_set_autotag(
-	git_remote *remote,
-	git_remote_autotag_option_t value);
-
-/**
  * Retrieve the ref-prune setting
  *
  * @param remote the remote to query
@@ -624,7 +618,7 @@
  *
  * No loaded instances of a the remote with the old name will change
  * their name or their list of refspecs.
- *
+ *a
  * @param problems non-default refspecs cannot be renamed and will be
  * stored here for further processing by the caller. Always free this
  * strarray on successful return.
@@ -646,16 +640,7 @@
  * @return the update FETCH_HEAD setting
  */
 GIT_EXTERN(int) git_remote_update_fetchhead(git_remote *remote);
-
-/**
- * Sets the update FETCH_HEAD setting.  By default, FETCH_HEAD will be
- * updated on every fetch.  Set to 0 to disable.
- *
- * @param remote the remote to configure
- * @param value 0 to disable updating FETCH_HEAD
- */
-GIT_EXTERN(void) git_remote_set_update_fetchhead(git_remote *remote, int value);
-
+					
 /**
  * Ensure the remote name is well-formed.
  *
diff --git a/src/clone.c b/src/clone.c
index f18f076..651e9e1 100644
--- a/src/clone.c
+++ b/src/clone.c
@@ -250,7 +250,7 @@
 	if ((error = git_remote_create(out, repo, name, url)) < 0)
 		return error;
 
-	return git_remote_set_callbacks(*out, callbacks);
+	return 0;
 }
 
 /*
@@ -335,6 +335,7 @@
 	int error;
 	git_buf reflog_message = GIT_BUF_INIT;
 	git_remote *remote;
+	git_fetch_options fetch_options = GIT_FETCH_OPTIONS_INIT;
 	const git_remote_callbacks *callbacks;
 
 	assert(repo && _remote);
@@ -347,18 +348,15 @@
 	if ((error = git_remote_dup(&remote, _remote)) < 0)
 		return error;
 
-	callbacks = git_remote_get_callbacks(_remote);
-	if (!giterr__check_version(callbacks, 1, "git_remote_callbacks") &&
-	    (error = git_remote_set_callbacks(remote, callbacks)) < 0)
-		goto cleanup;
-
 	if ((error = git_remote_add_fetch(remote, "refs/tags/*:refs/tags/*")) < 0)
 		goto cleanup;
 
-	git_remote_set_update_fetchhead(remote, 0);
 	git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote));
 
-	if ((error = git_remote_fetch(remote, NULL, signature, git_buf_cstr(&reflog_message))) != 0)
+	fetch_options.signature = signature;
+	fetch_options.reflog_message = git_buf_cstr(&reflog_message);
+	fetch_options.update_fetchhead = 0;
+	if ((error = git_remote_fetch(remote, &_remote->callbacks, &fetch_options)) != 0)
 		goto cleanup;
 
 	error = checkout_branch(repo, remote, co_opts, branch, signature, git_buf_cstr(&reflog_message));
@@ -512,6 +510,7 @@
 {
 	int error, flags;
 	git_repository *src;
+	git_fetch_options fetch_options = GIT_FETCH_OPTIONS_INIT;
 	git_buf src_odb = GIT_BUF_INIT, dst_odb = GIT_BUF_INIT, src_path = GIT_BUF_INIT;
 	git_buf reflog_message = GIT_BUF_INIT;
 
@@ -553,7 +552,9 @@
 
 	git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote));
 
-	if ((error = git_remote_fetch(remote, NULL, signature, git_buf_cstr(&reflog_message))) != 0)
+	fetch_options.signature = signature;
+	fetch_options.reflog_message = git_buf_cstr(&reflog_message);
+	if ((error = git_remote_fetch(remote, NULL, &fetch_options)) != 0)
 		goto cleanup;
 
 	error = checkout_branch(repo, remote, co_opts, branch, signature, git_buf_cstr(&reflog_message));
diff --git a/src/remote.c b/src/remote.c
index 5ba7735..d815d91 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -29,7 +29,7 @@
 
 	spec = git__calloc(1, sizeof(git_refspec));
 	GITERR_CHECK_ALLOC(spec);
-
+	
 	if (git_refspec__parse(spec, string, is_fetch) < 0) {
 		git__free(spec);
 		return -1;
@@ -139,8 +139,8 @@
 static int create_internal(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch)
 {
 	git_remote *remote;
-	git_config *config = NULL;
-	git_buf canonical_url = GIT_BUF_INIT, fetchbuf = GIT_BUF_INIT;
+	git_config *config = NULL, *cfg = NULL;
+	git_buf canonical_url = GIT_BUF_INIT, fetchbuf = GIT_BUF_INIT, buf = GIT_BUF_INIT;
 	int error = -1;
 
 	/* name is optional */
@@ -163,6 +163,22 @@
 		GITERR_CHECK_ALLOC(remote->name);
 	}
 
+	if ((error = git_repository_config__weakptr(&cfg, remote->repo)) < 0)
+		goto on_error;
+
+	if ((error = git_buf_printf(&buf, "remote.%s.url", name)) < 0)
+		goto on_error;
+
+	if ((error = git_config_set_string(cfg, git_buf_cstr(&buf), url)) < 0)
+		goto on_error;
+
+	git_buf_clear(&buf);
+	if ((error = git_buf_printf(&buf, "remote.%s.fetch", name)) < 0)
+		goto on_error;
+
+	if ((error = git_config_set_string(cfg, git_buf_cstr(&buf), fetch)) < 0)
+		goto on_error;
+
 	if (fetch != NULL) {
 		if (add_refspec(remote, fetch, true) < 0)
 			goto on_error;
@@ -190,8 +206,10 @@
 		git_remote_free(remote);
 
 	git_config_free(config);
+	git_config_free(cfg);
 	git_buf_free(&fetchbuf);
 	git_buf_free(&canonical_url);
+	git_buf_free(&buf);
 	return error;
 }
 
@@ -246,9 +264,6 @@
 	if (create_internal(&remote, repo, name, url, fetch) < 0)
 		goto on_error;
 
-	if (git_remote_save(remote) < 0)
-		goto on_error;
-
 	*out = remote;
 
 	return 0;
@@ -543,86 +558,6 @@
 	return error;
 }
 
-int git_remote_save(const git_remote *remote)
-{
-	int error;
-	git_config *cfg;
-	const char *tagopt = NULL;
-	git_buf buf = GIT_BUF_INIT;
-	const git_config_entry *existing;
-
-	assert(remote);
-
-	if (!remote->name) {
-		giterr_set(GITERR_INVALID, "Can't save an anonymous remote.");
-		return GIT_EINVALIDSPEC;
-	}
-
-	if ((error = ensure_remote_name_is_valid(remote->name)) < 0)
-		return error;
-
-	if ((error = git_repository_config__weakptr(&cfg, remote->repo)) < 0)
-		return error;
-
-	if ((error = git_buf_printf(&buf, "remote.%s.url", remote->name)) < 0)
-		return error;
-
-	/* after this point, buffer is allocated so end with cleanup */
-
-	if ((error = git_config_set_string(
-			cfg, git_buf_cstr(&buf), remote->url)) < 0)
-		goto cleanup;
-
-	git_buf_clear(&buf);
-	if ((error = git_buf_printf(&buf, "remote.%s.pushurl", remote->name)) < 0)
-		goto cleanup;
-
-	if ((error = git_config__update_entry(
-			cfg, git_buf_cstr(&buf), remote->pushurl, true, false)) < 0)
-		goto cleanup;
-
-	if ((error = update_config_refspec(remote, cfg, GIT_DIRECTION_FETCH)) < 0)
-		goto cleanup;
-
-	if ((error = update_config_refspec(remote, cfg, GIT_DIRECTION_PUSH)) < 0)
-		goto cleanup;
-
-	/*
-	 * What action to take depends on the old and new values. This
-	 * is describes by the table below. tagopt means whether the
-	 * is already a value set in the config
-	 *
-	 *            AUTO     ALL or NONE
-	 *         +-----------------------+
-	 *  tagopt | remove  |     set     |
-	 *         +---------+-------------|
-	 * !tagopt | nothing |     set     |
-	 *         +---------+-------------+
-	 */
-
-	git_buf_clear(&buf);
-	if ((error = git_buf_printf(&buf, "remote.%s.tagopt", remote->name)) < 0)
-		goto cleanup;
-
-	if ((error = git_config__lookup_entry(
-			&existing, cfg, git_buf_cstr(&buf), false)) < 0)
-		goto cleanup;
-
-	if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL)
-		tagopt = "--tags";
-	else if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_NONE)
-		tagopt = "--no-tags";
-	else if (existing != NULL)
-		tagopt = NULL;
-
-	error = git_config__update_entry(
-		cfg, git_buf_cstr(&buf), tagopt, true, false);
-
-cleanup:
-	git_buf_free(&buf);
-	return error;
-}
-
 const char *git_remote_name(const git_remote *remote)
 {
 	assert(remote);
@@ -641,38 +576,12 @@
 	return remote->url;
 }
 
-int git_remote_set_url(git_remote *remote, const char* url)
-{
-	assert(remote);
-	assert(url);
-
-	git__free(remote->url);
-	remote->url = git__strdup(url);
-	GITERR_CHECK_ALLOC(remote->url);
-
-	return 0;
-}
-
 const char *git_remote_pushurl(const git_remote *remote)
 {
 	assert(remote);
 	return remote->pushurl;
 }
 
-int git_remote_set_pushurl(git_remote *remote, const char* url)
-{
-	assert(remote);
-
-	git__free(remote->pushurl);
-	if (url) {
-		remote->pushurl = git__strdup(url);
-		GITERR_CHECK_ALLOC(remote->pushurl);
-	} else {
-		remote->pushurl = NULL;
-	}
-	return 0;
-}
-
 const char* git_remote__urlfordirection(git_remote *remote, int direction)
 {
 	assert(remote);
@@ -864,7 +773,7 @@
 	return 0;
 }
 
-int git_remote_download(git_remote *remote, const git_strarray *refspecs)
+int git_remote_download(git_remote *remote, const git_remote_callbacks *callbacks, const git_strarray *refspecs)
 {
 	int error = -1;
 	size_t i;
@@ -924,9 +833,8 @@
 
 int git_remote_fetch(
 		git_remote *remote,
-		const git_strarray *refspecs,
-		const git_signature *signature,
-		const char *reflog_message)
+		const git_remote_callbacks *callbacks,
+		const git_fetch_options *opts)
 {
 	int error;
 	git_buf reflog_msg_buf = GIT_BUF_INIT;
@@ -935,7 +843,7 @@
 	if ((error = git_remote_connect(remote, GIT_DIRECTION_FETCH)) != 0)
 		return error;
 
-	error = git_remote_download(remote, refspecs);
+	error = git_remote_download(remote, callbacks, &opts->refspecs);
 
 	/* We don't need to be connected anymore */
 	git_remote_disconnect(remote);
@@ -945,15 +853,15 @@
 		return error;
 
 	/* Default reflog message */
-	if (reflog_message)
-		git_buf_sets(&reflog_msg_buf, reflog_message);
+	if (opts->reflog_message)
+		git_buf_sets(&reflog_msg_buf, opts->reflog_message);
 	else {
 		git_buf_printf(&reflog_msg_buf, "fetch %s",
 				remote->name ? remote->name : remote->url);
 	}
 
 	/* Create "remote/foo" branches for all remote branches */
-	error = git_remote_update_tips(remote, signature, git_buf_cstr(&reflog_msg_buf));
+	error = git_remote_update_tips(remote, opts->signature, git_buf_cstr(&reflog_msg_buf));
 	git_buf_free(&reflog_msg_buf);
 	if (error < 0)
 		return error;
@@ -1619,13 +1527,6 @@
 	return 0;
 }
 
-const git_remote_callbacks *git_remote_get_callbacks(git_remote *remote)
-{
-	assert(remote);
-
-	return &remote->callbacks;
-}
-
 int git_remote_set_transport(
 	git_remote *remote,
 	git_transport_cb transport_cb,
@@ -1654,11 +1555,6 @@
 	return remote->download_tags;
 }
 
-void git_remote_set_autotag(git_remote *remote, git_remote_autotag_option_t value)
-{
-	remote->download_tags = value;
-}
-
 int git_remote_prune_refs(const git_remote *remote)
 {
 	return remote->prune_refs;
@@ -2316,12 +2212,13 @@
 	return git_buf_puts(out, guess->name);
 }
 
-int git_remote_upload(git_remote *remote, const git_strarray *refspecs, const git_push_options *opts)
+int git_remote_upload(git_remote *remote, const git_remote_callbacks *callbacks, const git_push_options *opts)
 {
 	size_t i;
 	int error;
 	git_push *push;
 	git_refspec *spec;
+	const git_strarray *refspecs = &opts->refspecs;
 	git_remote_callbacks *cbs;
 
 	assert(remote);
@@ -2374,20 +2271,19 @@
 	return error;
 }
 
-int git_remote_push(git_remote *remote, const git_strarray *refspecs, const git_push_options *opts,
-		    const git_signature *signature, const char *reflog_message)
+int git_remote_push(git_remote *remote, const git_remote_callbacks *callbacks, const git_push_options *opts)
 {
 	int error;
 
-	assert(remote && refspecs);
+	assert(remote);
 
 	if ((error = git_remote_connect(remote, GIT_DIRECTION_PUSH)) < 0)
 		return error;
 
-	if ((error = git_remote_upload(remote, refspecs, opts)) < 0)
+	if ((error = git_remote_upload(remote, callbacks, opts)) < 0)
 		return error;
 
-	error = git_remote_update_tips(remote, signature, reflog_message);
+	error = git_remote_update_tips(remote, opts->signature, opts->reflog_message);
 
 	git_remote_disconnect(remote);
 	return error;