[PATCH 0/6] Tracking setup improvements: per-remote config, autosetuppush

DORMANTno replies

7 messages, 1 author, 2016-06-15 · open the first message on its own page

[PATCH 0/6] Tracking setup improvements: per-remote config, autosetuppush

From: Paolo Bonzini <hidden>
Date: 2016-06-15 22:47:03

This patch series adds two features that are a step toward providing
consistent per-remote configuration of push behavior in addition
to merge (tracking) behavior.

In particular, the two features are:

1) per-remote configuration of automatic tracking setup.  This leads
to some nice refactoring of the code handling autosetup, where the
combination of autosetupmerge and autosetuprebase is consolidated in
a struct.

2) The ability to automatically setup push refspecs corresponding
to local branches tracking a remote.  This is false by default, since
we also have push.default---and actually, a generalized default of
true is probably not a good idea, unlike a per-remote setting.  In
the future, 'git remote add' might set the per-remote autosetuppush
to true if it gets some appropriate command-line argument.


Patch 1 is a somewhat tangential DWIM that I've meant to send for
a while.  It conflicts with this patch series, so I've included it.

Patches 2 and 3 include the refactorings in preparation for the new
remote.*.autosetup configuration, which is then implemented by
patch 4.

Patch 5 extracts some code to a function that will become more
complicated in patch 6.  Patch 6 implements autosetuppush by
automatically creating and deleting push refspecs upon branch
creation and deletion.

 config: allow false and true values for branch.autosetuprebase
 branch: install_branch_config and struct tracking refactoring
 introduce a struct tracking_config
 remote: add per-remote autosetupmerge and autosetuprebase configuration
 move deletion of merge configuration to branch.c
 branch, checkout: introduce autosetuppush

 Documentation/config.txt |   36 +++++++++++++++++-
 branch.c                 |   92 +++++++++++++++++++++++++++++++++++++--------
 branch.h                 |    5 ++-
 builtin-branch.c         |   10 +----
 builtin-checkout.c       |    2 +-
 builtin-clone.c          |    4 +-
 cache.h                  |   11 ++++-
 config.c                 |   48 +++++++++++++++++-------
 environment.c            |    7 ++-
 remote.c                 |    3 +
 remote.h                 |    1 +
 t/t3200-branch.sh        |   92 ++++++++++++++++++++++++++++++++++++++++++++-
 12 files changed, 259 insertions(+), 52 deletions(-)

[PATCH 1/6] config: allow false and true values for branch.autosetuprebase

From: Paolo Bonzini <hidden>
Date: 2016-06-15 22:47:03

Boolean values can be clearly DWIM'ed from false to never and
from true to always.  This is nicer to the user because autosetupmerge
allows boolean values.

Signed-off-by: Paolo Bonzini <redacted>
---
 Documentation/config.txt |    4 ++--
 config.c                 |   12 ++++++------
 t/t3200-branch.sh        |   15 ++++++++++++---
 3 files changed, 20 insertions(+), 11 deletions(-)
diff --git a/Documentation/config.txt b/Documentation/config.txt
index cb6832b..1e37e33 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -479,12 +479,12 @@ branch.autosetuprebase::
 	When a new branch is created with 'git-branch' or 'git-checkout'
 	that tracks another branch, this variable tells git to set
 	up pull to rebase instead of merge (see "branch.<name>.rebase").
-	When `never`, rebase is never automatically set to true.
+	When `never` or `false`, rebase is never automatically set to true.
 	When `local`, rebase is set to true for tracked branches of
 	other local branches.
 	When `remote`, rebase is set to true for tracked branches of
 	remote branches.
-	When `always`, rebase will be set to true for all tracking
+	When `always` or `true`, rebase will be set to true for all tracking
 	branches.
 	See "branch.autosetupmerge" for details on how to set up a
 	branch to track another branch.
diff --git a/config.c b/config.c
index 1682273..b47a40e 100644
--- a/config.c
+++ b/config.c
@@ -557,17 +557,17 @@ static int git_default_branch_config(const char *var, const char *value)
 	}
 	if (!strcmp(var, "branch.autosetuprebase")) {
 		if (!value)
-			return config_error_nonbool(var);
-		else if (!strcmp(value, "never"))
-			autorebase = AUTOREBASE_NEVER;
+			value = "always";
+		autorebase = AUTOREBASE_NEVER;
+		if (!strcmp(value, "never"))
+			;
 		else if (!strcmp(value, "local"))
 			autorebase = AUTOREBASE_LOCAL;
 		else if (!strcmp(value, "remote"))
 			autorebase = AUTOREBASE_REMOTE;
-		else if (!strcmp(value, "always"))
+		else if (!strcmp(value, "always") ||
+			 git_config_bool (var, value))
 			autorebase = AUTOREBASE_ALWAYS;
-		else
-			return error("Malformed value for %s", var);
 		return 0;
 	}
 
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index d59a9b4..b14a3a9 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -461,11 +461,20 @@ test_expect_success 'detect misconfigured autosetuprebase (bad value)' '
 	test_must_fail git branch
 '
 
-test_expect_success 'detect misconfigured autosetuprebase (no value)' '
+test_expect_success 'boolean value (no value) for autosetuprebase' '
 	git config --unset branch.autosetuprebase &&
 	echo "[branch] autosetuprebase" >> .git/config &&
-	test_must_fail git branch &&
-	git config --unset branch.autosetuprebase
+	git config remote.local.url . &&
+	git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+	(git show-ref -q refs/remotes/local/master || git fetch local) &&
+	git branch --track myr21 local/master &&
+	git branch --track myr22 myr21 &&
+	test "$(git config branch.myr21.remote)" = local &&
+	test "$(git config branch.myr21.merge)" = refs/heads/master &&
+	test "z$(git config branch.myr21.rebase)" = ztrue &&
+	test "$(git config branch.myr22.remote)" = . &&
+	test "$(git config branch.myr22.merge)" = refs/heads/myr21 &&
+	test "z$(git config branch.myr22.rebase)" = ztrue
 '
 
 test_done
-- 
1.6.2.5

[PATCH 2/6] branch: install_branch_config and struct tracking refactoring

From: Paolo Bonzini <hidden>
Date: 2016-06-15 22:47:03

This patch combines two small refactoring.  The first is to store
a struct remote into struct tracking.  The second is to pass a
struct remote instead of just the name to install_branch_config.

Together, these will allow to easily implement the new configuration
keys remote.*.autosetupmerge and remote.*.autosetuprebase.

Signed-off-by: Paolo Bonzini <redacted>
---
 branch.c        |   34 ++++++++++++++++++----------------
 branch.h        |    3 ++-
 builtin-clone.c |    4 ++--
 3 files changed, 22 insertions(+), 19 deletions(-)
diff --git a/branch.c b/branch.c
index 05ef3f5..d17c5ec 100644
--- a/branch.c
+++ b/branch.c
@@ -7,7 +7,7 @@
 struct tracking {
 	struct refspec spec;
 	char *src;
-	const char *remote;
+	struct remote *remote;
 	int matches;
 };
 
@@ -18,7 +18,7 @@ static int find_tracked_branch(struct remote *remote, void *priv)
 	if (!remote_find_tracking(remote, &tracking->spec)) {
 		if (++tracking->matches == 1) {
 			tracking->src = tracking->spec.src;
-			tracking->remote = remote->name;
+			tracking->remote = remote;
 		} else {
 			free(tracking->spec.src);
 			if (tracking->src) {
@@ -32,7 +32,7 @@ static int find_tracked_branch(struct remote *remote, void *priv)
 	return 0;
 }
 
-static int should_setup_rebase(const char *origin)
+static int should_setup_rebase(struct remote *origin)
 {
 	switch (autorebase) {
 	case AUTOREBASE_NEVER:
@@ -47,17 +47,18 @@ static int should_setup_rebase(const char *origin)
 	return 0;
 }
 
-void install_branch_config(int flag, const char *local, const char *origin, const char *remote)
+void install_branch_config(int flag, const char *local, struct remote *remote,
+			   const char *merge)
 {
 	struct strbuf key = STRBUF_INIT;
-	int rebasing = should_setup_rebase(origin);
+	int rebasing = should_setup_rebase(remote);
 
 	strbuf_addf(&key, "branch.%s.remote", local);
-	git_config_set(key.buf, origin ? origin : ".");
+	git_config_set(key.buf, remote ? remote->name : ".");
 
 	strbuf_reset(&key);
 	strbuf_addf(&key, "branch.%s.merge", local);
-	git_config_set(key.buf, remote);
+	git_config_set(key.buf, merge);
 
 	if (rebasing) {
 		strbuf_reset(&key);
@@ -68,16 +69,15 @@ void install_branch_config(int flag, const char *local, const char *origin, cons
 	if (flag & BRANCH_CONFIG_VERBOSE) {
 		strbuf_reset(&key);
 
-		strbuf_addstr(&key, origin ? "remote" : "local");
+		strbuf_addstr(&key, remote ? "remote" : "local");
 
 		/* Are we tracking a proper "branch"? */
-		if (!prefixcmp(remote, "refs/heads/")) {
-			strbuf_addf(&key, " branch %s", remote + 11);
-			if (origin)
-				strbuf_addf(&key, " from %s", origin);
-		}
+		if (!prefixcmp(merge, "refs/heads/"))
+			strbuf_addf(&key, " branch %s", merge + 11);
 		else
-			strbuf_addf(&key, " ref %s", remote);
+			strbuf_addf(&key, " ref %s", merge);
+		if (remote)
+			strbuf_addf(&key, " from %s", remote->name);
 		printf("Branch %s set up to track %s%s.\n",
 		       local, key.buf,
 		       rebasing ? " by rebasing" : "");
@@ -117,9 +117,11 @@ static int setup_tracking(const char *new_ref, const char *orig_ref,
 		return error("Not tracking: ambiguous information for ref %s",
 				orig_ref);
 
-	install_branch_config(BRANCH_CONFIG_VERBOSE, new_ref, tracking.remote,
-			      tracking.src ? tracking.src : orig_ref);
+	if (!tracking.src)
+		tracking.src = xstrdup (orig_ref);
 
+	install_branch_config(BRANCH_CONFIG_VERBOSE, new_ref, tracking.remote,
+			      tracking.src);
 	free(tracking.src);
 	return 0;
 }
diff --git a/branch.h b/branch.h
index eed817a..f7c02c9 100644
--- a/branch.h
+++ b/branch.h
@@ -26,6 +26,7 @@ void remove_branch_state(void);
  * taken from origin "origin".
  */
 #define BRANCH_CONFIG_VERBOSE 01
-extern void install_branch_config(int flag, const char *local, const char *origin, const char *remote);
+struct remote;
+extern void install_branch_config(int flag, const char *local, struct remote *remote, const char *merge);
 
 #endif
diff --git a/builtin-clone.c b/builtin-clone.c
index 32dea74..552ddf6 100644
--- a/builtin-clone.c
+++ b/builtin-clone.c
@@ -527,7 +527,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 		remote_head = NULL;
 		option_no_checkout = 1;
 		if (!option_bare)
-			install_branch_config(0, "master", option_origin,
+			install_branch_config(0, "master", remote_get(option_origin),
 					      "refs/heads/master");
 	}
 
@@ -557,7 +557,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 				      head_points_at->peer_ref->name,
 				      reflog_msg.buf);
 
-			install_branch_config(0, head, option_origin,
+			install_branch_config(0, head, remote_get(option_origin),
 					      head_points_at->name);
 		}
 	} else if (remote_head) {
-- 
1.6.2.5

[PATCH 3/6] introduce a struct tracking_config

From: Paolo Bonzini <hidden>
Date: 2016-06-15 22:47:03

This struct groups together the settings for autosetupmerge and
autosetuprebase.

This refactoring will make it easier to add per-remote tracking
configuration, as well as making the addition of autosetuppush
more tidy.

Signed-off-by: Paolo Bonzini <redacted>
---
 branch.c           |    2 +-
 builtin-branch.c   |    2 +-
 builtin-checkout.c |    2 +-
 cache.h            |    9 +++++++--
 config.c           |   34 +++++++++++++++++++++++++---------
 environment.c      |    6 ++++--
 6 files changed, 39 insertions(+), 16 deletions(-)
diff --git a/branch.c b/branch.c
index d17c5ec..be683d9 100644
--- a/branch.c
+++ b/branch.c
@@ -34,7 +34,7 @@ static int find_tracked_branch(struct remote *remote, void *priv)
 
 static int should_setup_rebase(struct remote *origin)
 {
-	switch (autorebase) {
+	switch (git_branch_track.rebase) {
 	case AUTOREBASE_NEVER:
 		return 0;
 	case AUTOREBASE_LOCAL:
diff --git a/builtin-branch.c b/builtin-branch.c
index 5687d60..fdd6c05 100644
--- a/builtin-branch.c
+++ b/builtin-branch.c
@@ -595,7 +595,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 	if (branch_use_color == -1)
 		branch_use_color = git_use_color_default;
 
-	track = git_branch_track;
+	track = git_branch_track.merge;
 
 	head = resolve_ref("HEAD", head_sha1, 0, NULL);
 	if (!head)
diff --git a/builtin-checkout.c b/builtin-checkout.c
index 8a9a474..446cac7 100644
--- a/builtin-checkout.c
+++ b/builtin-checkout.c
@@ -624,7 +624,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	}
 
 	if (opts.track == BRANCH_TRACK_UNSPECIFIED)
-		opts.track = git_branch_track;
+		opts.track = git_branch_track.merge;
 	if (conflict_style) {
 		opts.merge = 1; /* implied */
 		git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
diff --git a/cache.h b/cache.h
index f1e5ede..2db4c3b 100644
--- a/cache.h
+++ b/cache.h
@@ -550,8 +550,12 @@ enum push_default_type {
 	PUSH_DEFAULT_CURRENT,
 };
 
-extern enum branch_track git_branch_track;
-extern enum rebase_setup_type autorebase;
+struct tracking_config {
+	enum branch_track merge;
+	enum rebase_setup_type rebase;
+};
+
+extern struct tracking_config git_branch_track;
 extern enum push_default_type push_default;
 
 enum object_creation_mode {
@@ -632,6 +636,7 @@ enum sharedrepo {
 	PERM_EVERYBODY      = 0664,
 };
 int git_config_perm(const char *var, const char *value);
+int git_config_tracking(const char *var, const char *value, struct tracking_config *cfg);
 int set_shared_perm(const char *path, int mode);
 #define adjust_shared_perm(path) set_shared_perm((path), 0)
 int safe_create_leading_directories(char *path);
diff --git a/config.c b/config.c
index b47a40e..aa695d4 100644
--- a/config.c
+++ b/config.c
@@ -545,29 +545,30 @@ static int git_default_i18n_config(const char *var, const char *value)
 	return 0;
 }
 
-static int git_default_branch_config(const char *var, const char *value)
+int git_tracking_config(const char *var, const char *value, struct tracking_config *cfg)
 {
-	if (!strcmp(var, "branch.autosetupmerge")) {
+	var = strrchr (var, '.');
+	if (!strcmp(var, ".autosetupmerge")) {
 		if (value && !strcasecmp(value, "always")) {
-			git_branch_track = BRANCH_TRACK_ALWAYS;
+			cfg->merge = BRANCH_TRACK_ALWAYS;
 			return 0;
 		}
-		git_branch_track = git_config_bool(var, value);
+		cfg->merge = git_config_bool(var, value);
 		return 0;
 	}
-	if (!strcmp(var, "branch.autosetuprebase")) {
+	if (!strcmp(var, ".autosetuprebase")) {
 		if (!value)
 			value = "always";
-		autorebase = AUTOREBASE_NEVER;
+		cfg->rebase = AUTOREBASE_NEVER;
 		if (!strcmp(value, "never"))
 			;
 		else if (!strcmp(value, "local"))
-			autorebase = AUTOREBASE_LOCAL;
+			cfg->rebase = AUTOREBASE_LOCAL;
 		else if (!strcmp(value, "remote"))
-			autorebase = AUTOREBASE_REMOTE;
+			cfg->rebase = AUTOREBASE_REMOTE;
 		else if (!strcmp(value, "always") ||
 			 git_config_bool (var, value))
-			autorebase = AUTOREBASE_ALWAYS;
+			cfg->rebase = AUTOREBASE_ALWAYS;
 		return 0;
 	}
 
@@ -575,6 +576,21 @@ static int git_default_branch_config(const char *var, const char *value)
 	return 0;
 }
 
+static int git_default_branch_config(const char *var, const char *value)
+{
+	int result;
+
+	if (!prefixcmp(var, "branch.")
+	    && !strchr (var + 7, '.')) {
+		result = git_tracking_config (var, value, &git_branch_track);
+		if (result)
+			return result;
+	}
+
+	/* Add other config variables here and to Documentation/config.txt. */
+	return 0;
+}
+
 static int git_default_push_config(const char *var, const char *value)
 {
 	if (!strcmp(var, "push.default")) {
diff --git a/environment.c b/environment.c
index 801a005..049d269 100644
--- a/environment.c
+++ b/environment.c
@@ -40,8 +40,10 @@ const char *excludes_file;
 int auto_crlf = 0;	/* 1: both ways, -1: only when adding git objects */
 enum safe_crlf safe_crlf = SAFE_CRLF_WARN;
 unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
-enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
-enum rebase_setup_type autorebase = AUTOREBASE_NEVER;
+struct tracking_config git_branch_track = {
+	BRANCH_TRACK_REMOTE,
+	AUTOREBASE_NEVER
+};
 enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;
 #ifndef OBJECT_CREATION_MODE
 #define OBJECT_CREATION_MODE OBJECT_CREATION_USES_HARDLINKS
-- 
1.6.2.5

[PATCH 4/6] remote: add per-remote autosetupmerge and autosetuprebase configuration

From: Paolo Bonzini <hidden>
Date: 2016-06-15 22:47:03

This is self-explanatory.  I chose to retain the full range of
possibilities for autosetuprebase, even though in practice only
never/false and always/true will make sense (local will be a
synonym of false, remote will be a synonym of true).

Signed-off-by: Paolo Bonzini <redacted>
---
 Documentation/config.txt |   13 +++++++++++++
 branch.c                 |    9 ++++++++-
 builtin-branch.c         |    4 +---
 cache.h                  |    1 +
 remote.c                 |    3 +++
 remote.h                 |    1 +
 t/t3200-branch.sh        |   41 +++++++++++++++++++++++++++++++++++++++++
 7 files changed, 68 insertions(+), 4 deletions(-)
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 1e37e33..524a222 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -1363,6 +1363,19 @@ remote.<name>.tagopt::
 	Setting this value to \--no-tags disables automatic tag following when
 	fetching from remote <name>
 
+remote.<name>.autosetupmerge::
+	Tells 'git-branch' and 'git-checkout' to setup new branches
+	so that linkgit:git-pull[1] will appropriately merge from the
+	starting point branch.  If present, this overrides the global
+	option branch.autosetupmerge, and can have the same settings.
+
+remote.<name>.autosetuprebase::
+	When a new branch is created with 'git-branch' or 'git-checkout'
+	that tracks another branch, this variable tells git to set
+	up pull to rebase instead of merge (see "branch.<name>.rebase").
+	If present, this overrides the global option
+	branch.autosetuprebase, and can have the same settings.
+
 remotes.<group>::
 	The list of remotes which are fetched by "git remote update
 	<group>".  See linkgit:git-remote[1].
diff --git a/branch.c b/branch.c
index be683d9..e427721 100644
--- a/branch.c
+++ b/branch.c
@@ -34,7 +34,7 @@ static int find_tracked_branch(struct remote *remote, void *priv)
 
 static int should_setup_rebase(struct remote *origin)
 {
-	switch (git_branch_track.rebase) {
+	switch (origin ? origin->track.rebase : git_branch_track.rebase) {
 	case AUTOREBASE_NEVER:
 		return 0;
 	case AUTOREBASE_LOCAL:
@@ -104,6 +104,13 @@ static int setup_tracking(const char *new_ref, const char *orig_ref,
 	if (for_each_remote(find_tracked_branch, &tracking))
 		return 1;
 
+	if (track == BRANCH_TRACK_UNSPECIFIED) {
+		track = (tracking.remote
+			 ? tracking.remote->track.merge : git_branch_track.merge);
+		if (!track)
+			return 0;
+	}
+
 	if (!tracking.matches)
 		switch (track) {
 		case BRANCH_TRACK_ALWAYS:
diff --git a/builtin-branch.c b/builtin-branch.c
index fdd6c05..f045e16 100644
--- a/builtin-branch.c
+++ b/builtin-branch.c
@@ -540,7 +540,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 	int delete = 0, rename = 0, force_create = 0;
 	int verbose = 0, abbrev = DEFAULT_ABBREV, detached = 0;
 	int reflog = 0;
-	enum branch_track track;
+	enum branch_track track = BRANCH_TRACK_UNSPECIFIED;
 	int kinds = REF_LOCAL_BRANCH;
 	struct commit_list *with_commit = NULL;
 
@@ -595,8 +595,6 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 	if (branch_use_color == -1)
 		branch_use_color = git_use_color_default;
 
-	track = git_branch_track.merge;
-
 	head = resolve_ref("HEAD", head_sha1, 0, NULL);
 	if (!head)
 		die("Failed to resolve HEAD as a valid ref.");
diff --git a/cache.h b/cache.h
index 2db4c3b..703dc45 100644
--- a/cache.h
+++ b/cache.h
@@ -881,6 +881,7 @@ extern const char *packed_object_info_detail(struct packed_git *, off_t, unsigne
 extern int update_server_info(int);
 
 typedef int (*config_fn_t)(const char *, const char *, void *);
+extern int git_tracking_config(const char *, const char *, struct tracking_config *);
 extern int git_default_config(const char *, const char *, void *);
 extern int git_config_from_file(config_fn_t fn, const char *, void *);
 extern int git_config(config_fn_t fn, void *);
diff --git a/remote.c b/remote.c
index c3ada2d..5e21da3 100644
--- a/remote.c
+++ b/remote.c
@@ -131,6 +131,7 @@ static struct remote *make_remote(const char *name, int len)
 		ret->name = xstrndup(name, len);
 	else
 		ret->name = xstrdup(name);
+	ret->track = git_branch_track;
 	return ret;
 }
 
@@ -375,6 +376,8 @@ static int handle_config(const char *key, const char *value, void *cb)
 		return 0;
 	remote = make_remote(name, subkey - name);
 	remote->origin = REMOTE_CONFIG;
+	if (git_tracking_config(key, value, &remote->track))
+		return -1;
 	if (!strcmp(subkey, ".mirror"))
 		remote->mirror = git_config_bool(key, value);
 	else if (!strcmp(subkey, ".skipdefaultupdate"))
diff --git a/remote.h b/remote.h
index 5db8420..86b18dc 100644
--- a/remote.h
+++ b/remote.h
@@ -38,6 +38,7 @@ struct remote {
 	int fetch_tags;
 	int skip_default_update;
 	int mirror;
+	struct tracking_config track;
 
 	const char *receivepack;
 	const char *uploadpack;
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index b14a3a9..186ba56 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -476,5 +476,46 @@ test_expect_success 'boolean value (no value) for autosetuprebase' '
 	test "$(git config branch.myr22.merge)" = refs/heads/myr21 &&
 	test "z$(git config branch.myr22.rebase)" = ztrue
 '
+test_expect_success 'tracking remote with branch.autosetupmerge = false' '
+	git config remote.local.autosetupmerge true &&
+	git config branch.autosetupmerge false &&
+	git config remote.local.url . &&
+	git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+	(git show-ref -q refs/remotes/local/master || git fetch local) &&
+	git branch myr23 local/master &&
+	test "$(git config branch.myr23.remote)" = local &&
+	test "$(git config branch.myr23.merge)" = refs/heads/master
+'
+
+test_expect_success 'non-tracking remote with branch.autosetupmerge = always' '
+	git config remote.local.autosetupmerge false &&
+	git config branch.autosetupmerge always &&
+	git branch myr24 local/master &&
+	test z"$(git config branch.myr24.remote)" = z &&
+	test z"$(git config branch.myr24.merge)" = z
+'
 
+test_expect_success 'tracking branch overriding remote configuration' '
+	git config remote.local.autosetupmerge false &&
+	git branch --track myr25 local/master &&
+	test "$(git config branch.myr25.remote)" = local &&
+	test "$(git config branch.myr25.merge)" = refs/heads/master
+'
+
+test_expect_success 'non-tracking branch overriding remote configuration' '
+	git config remote.local.autosetupmerge true &&
+	git branch --no-track myr26 local/master &&
+	test z"$(git config branch.myr26.remote)" = z &&
+	test z"$(git config branch.myr26.merge)" = z
+'
+
+test_expect_success '--no-track and multiple matches ' '
+	git config branch.autosetupmerge true &&
+	git config remote.local2.url . &&
+	git config remote.local2.fetch refs/heads/*:refs/remotes/local/* &&
+	git fetch local2 &&
+	git branch --no-track myr96 local/master
+	test z"$(git config branch.myr96.remote)" = z &&
+	test z"$(git config branch.myr96.merge)" = z
+'
 test_done
-- 
1.6.2.5

[PATCH 5/6] move deletion of merge configuration to branch.c

From: Paolo Bonzini <hidden>
Date: 2016-06-15 22:47:03

In the next step, deletion of merge configuration will become more
complex, so we move it into a separate function.  Since the
configuration is installed with a function in branch.c, I'm placing
deletion there as well.

At the same time, the interface is changed to get the full ref name
including refs/heads/.  This makes no big difference for the caller.

Signed-off-by: Paolo Bonzini <redacted>
---
 branch.c         |   11 +++++++++++
 branch.h         |    2 ++
 builtin-branch.c |    6 +-----
 3 files changed, 14 insertions(+), 5 deletions(-)
diff --git a/branch.c b/branch.c
index e427721..8d3e8d8 100644
--- a/branch.c
+++ b/branch.c
@@ -85,6 +85,17 @@ void install_branch_config(int flag, const char *local, struct remote *remote,
 	strbuf_release(&key);
 }
 
+void delete_branch_config (const char *name)
+{
+	struct strbuf buf = STRBUF_INIT;
+	if (prefixcmp(name, "refs/heads/"))
+		return;
+	strbuf_addf(&buf, "branch.%s", name + 11);
+	if (git_config_rename_section(buf.buf, NULL) < 0)
+		warning("Update of config-file failed");
+	strbuf_release(&buf);
+}
+
 /*
  * This is called when new_ref is branched off of orig_ref, and tries
  * to infer the settings for branch.<new_ref>.{remote,merge} from the
diff --git a/branch.h b/branch.h
index f7c02c9..efa4506 100644
--- a/branch.h
+++ b/branch.h
@@ -21,6 +21,8 @@ void create_branch(const char *head, const char *name, const char *start_name,
  */
 void remove_branch_state(void);
 
+void delete_branch_config(const char *name);
+
 /*
  * Configure local branch "local" to merge remote branch "remote"
  * taken from origin "origin".
diff --git a/builtin-branch.c b/builtin-branch.c
index f045e16..4469ea9 100644
--- a/builtin-branch.c
+++ b/builtin-branch.c
@@ -166,14 +166,10 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
 			      bname.buf);
 			ret = 1;
 		} else {
-			struct strbuf buf = STRBUF_INIT;
 			printf("Deleted %sbranch %s (was %s).\n", remote,
 			       bname.buf,
 			       find_unique_abbrev(sha1, DEFAULT_ABBREV));
-			strbuf_addf(&buf, "branch.%s", bname.buf);
-			if (git_config_rename_section(buf.buf, NULL) < 0)
-				warning("Update of config-file failed");
-			strbuf_release(&buf);
+			delete_branch_config (name);
 		}
 	}
 
-- 
1.6.2.5

[PATCH 6/6] branch, checkout: introduce autosetuppush

From: Paolo Bonzini <hidden>
Date: 2016-06-15 22:47:03

The autosetuppush configuration arranges so that whenever
a merge configuration is setup, a push refspec is added for
the remote.

This is a step towards providing the functionality currently
enabled by push.default=tracking on a per-remote basis, and
without the restriction of pushing a single branch at a time.

Signed-off-by: Paolo Bonzini <redacted>
---
 Documentation/config.txt |   19 +++++++++++++++++++
 branch.c                 |   38 ++++++++++++++++++++++++++++++++++++++
 cache.h                  |    1 +
 config.c                 |    4 ++++
 environment.c            |    3 ++-
 t/t3200-branch.sh        |   36 ++++++++++++++++++++++++++++++++++++
 6 files changed, 100 insertions(+), 1 deletions(-)
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 524a222..4c27e9d 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -475,6 +475,15 @@ branch.autosetupmerge::
 	done when the starting point is either a local branch or remote
 	branch. This option defaults to true.
 
+branch.autosetuppush::
+	Tells 'git-branch' and 'git-checkout' to setup new branches
+	so that linkgit:git-push[1] will push into the starting point
+	branch.  Note that this option applies only to branches created
+	from a remote branch, and only if git is setting up merging
+	from the remote branch (via any one of branch.autosetupmerge,
+	remote.<name>.autosetupmerge, or `--track`).  This option defaults
+	to false.
+
 branch.autosetuprebase::
 	When a new branch is created with 'git-branch' or 'git-checkout'
 	that tracks another branch, this variable tells git to set
@@ -1369,6 +1378,16 @@ remote.<name>.autosetupmerge::
 	starting point branch.  If present, this overrides the global
 	option branch.autosetupmerge, and can have the same settings.
 
+remote.<name>.autosetuppush::
+	Tells 'git-branch' and 'git-checkout' to setup new branches
+	so that linkgit:git-push[1] will push into the starting point
+	branch.  If present, this overrides the global option
+	branch.autosetuppush, and can have the same settings.
+	Note that this option applies only if git is setting
+	up merging from the remote branch (via any one of
+	branch.autosetupmerge, remote.<name>.autosetupmerge, or
+	`--track`).
+
 remote.<name>.autosetuprebase::
 	When a new branch is created with 'git-branch' or 'git-checkout'
 	that tracks another branch, this variable tells git to set
diff --git a/branch.c b/branch.c
index 8d3e8d8..dfde568 100644
--- a/branch.c
+++ b/branch.c
@@ -51,6 +51,7 @@ void install_branch_config(int flag, const char *local, struct remote *remote,
 			   const char *merge)
 {
 	struct strbuf key = STRBUF_INIT;
+	struct strbuf value = STRBUF_INIT;
 	int rebasing = should_setup_rebase(remote);
 
 	strbuf_addf(&key, "branch.%s.remote", local);
@@ -60,6 +61,13 @@ void install_branch_config(int flag, const char *local, struct remote *remote,
 	strbuf_addf(&key, "branch.%s.merge", local);
 	git_config_set(key.buf, merge);
 
+	if (remote && remote->track.push) {
+		strbuf_reset(&key);
+		strbuf_addf(&key, "remote.%s.push", remote->name);
+		strbuf_addf(&value, "refs/heads/%s:%s", local, merge);
+		git_config_set_multivar(key.buf, value.buf, "^$", 0);
+	}
+
 	if (rebasing) {
 		strbuf_reset(&key);
 		strbuf_addf(&key, "branch.%s.rebase", local);
@@ -83,16 +91,46 @@ void install_branch_config(int flag, const char *local, struct remote *remote,
 		       rebasing ? " by rebasing" : "");
 	}
 	strbuf_release(&key);
+	strbuf_release(&value);
+}
+
+static void strbuf_addstr_escape_re (struct strbuf *buf, const char *add)
+{
+	const char *p = add;
+	while ((add = strpbrk(add, ".*?+^$(){}[]")) != NULL) {
+		strbuf_add(buf, p, add - p);
+		strbuf_addf(buf, "\\%c", *add++);
+		p = add;
+	}
+	strbuf_addstr(buf, p);
 }
 
 void delete_branch_config (const char *name)
 {
 	struct strbuf buf = STRBUF_INIT;
+	struct strbuf push_re = STRBUF_INIT;
+	struct branch *branch;
+
 	if (prefixcmp(name, "refs/heads/"))
 		return;
+
+	/* git config --unset-all remote.foo.push ^\+?refs/heads/bar:  */
+	branch = branch_get(name + 11);
+	strbuf_addf(&buf, "remote.%s.push", branch->remote_name);
+	strbuf_addstr(&push_re, "^\\+?");
+	strbuf_addstr_escape_re(&push_re, name);
+	strbuf_addch(&push_re, ':');
+	if (git_config_set_multivar(buf.buf, NULL, push_re.buf, 1) < 0) {
+		warning("Update of config-file failed");
+		goto fail;
+	}
+	strbuf_reset(&buf);
 	strbuf_addf(&buf, "branch.%s", name + 11);
 	if (git_config_rename_section(buf.buf, NULL) < 0)
 		warning("Update of config-file failed");
+
+fail:
+	strbuf_release(&push_re);
 	strbuf_release(&buf);
 }
 
diff --git a/cache.h b/cache.h
index 703dc45..2d9a864 100644
--- a/cache.h
+++ b/cache.h
@@ -553,6 +553,7 @@ enum push_default_type {
 struct tracking_config {
 	enum branch_track merge;
 	enum rebase_setup_type rebase;
+	int push;
 };
 
 extern struct tracking_config git_branch_track;
diff --git a/config.c b/config.c
index aa695d4..04380bb 100644
--- a/config.c
+++ b/config.c
@@ -556,6 +556,10 @@ int git_tracking_config(const char *var, const char *value, struct tracking_conf
 		cfg->merge = git_config_bool(var, value);
 		return 0;
 	}
+	if (!strcmp(var, ".autosetuppush")) {
+		cfg->push = git_config_bool(var, value);
+		return 0;
+	}
 	if (!strcmp(var, ".autosetuprebase")) {
 		if (!value)
 			value = "always";
diff --git a/environment.c b/environment.c
index 049d269..2b66ac6 100644
--- a/environment.c
+++ b/environment.c
@@ -42,7 +42,8 @@ enum safe_crlf safe_crlf = SAFE_CRLF_WARN;
 unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
 struct tracking_config git_branch_track = {
 	BRANCH_TRACK_REMOTE,
-	AUTOREBASE_NEVER
+	AUTOREBASE_NEVER,
+	0
 };
 enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;
 #ifndef OBJECT_CREATION_MODE
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index 186ba56..5e23cf5 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -233,11 +233,47 @@ test_expect_success 'avoid ambiguous track' '
 	git branch all1 master &&
 	test -z "$(git config branch.all1.merge)"
 '
+test_expect_success 'test push setup' \
+    'git config branch.autosetupmerge true &&
+     git config branch.autosetuppush true &&
+     git config remote.local.url . &&
+     git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+     (git show-ref -q refs/remotes/local/master || git fetch local) &&
+     git branch my11 local/master &&
+     test $(git config branch.my11.remote) = local &&
+     test $(git config branch.my11.merge) = refs/heads/master
+     test $(git config remote.local.push) = refs/heads/my11:refs/heads/master
+'
+
+test_expect_success 'test multiple push setups' \
+    'git config remote.local.url . &&
+     git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+     (git show-ref -q refs/remotes/local/master || git fetch local) &&
+     git branch my12 local/master &&
+     test $(git config branch.my12.remote) = local &&
+     test $(git config branch.my12.merge) = refs/heads/master &&
+     (if git config remote.local.push; then exit 1; else test $? = 2; fi)
+'
+
+test_expect_success 'test push setup cleanup' \
+    'git config branch.autosetupmerge true &&
+     git config branch.autosetuppush true &&
+     git config remote.local.url . &&
+     git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+     (git show-ref -q refs/remotes/local/master || git fetch local) &&
+     (git branch my11 local/master || :) &&
+     (git branch my12 local/master || :) &&
+     git branch -d my11 &&
+     test $(git config remote.local.push) = refs/heads/my12:refs/heads/master &&
+     git branch -d my12 &&
+     test z$(git config remote.local.push) = z
+'
 
 test_expect_success 'autosetuprebase local on a tracked local branch' '
 	git config remote.local.url . &&
 	git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
 	git config branch.autosetuprebase local &&
+	git config branch.autosetuppush false &&
 	(git show-ref -q refs/remotes/local/o || git fetch local) &&
 	git branch mybase &&
 	git branch --track myr1 mybase &&
-- 
1.6.2.5
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help