@@ -1,3 +1,14 @@
+push.autoSetupRemote::
+ If set to "true" assume `--set-upstream` on default push when no
+ upstream tracking exists for the current branch; this option
+ takes effect with push.default options 'simple', 'upstream',
+ and 'current'. It is useful if by default you want new branches
+ to be pushed to the default remote (like the behavior of
+ 'push.default=current') and you also want the upstream tracking
+ to be set. Workflows most likely to benefit from this option are
+ 'simple' central workflows where all branches are expected to
+ have the same name on the remote.
+
push.default::
Defines the action `git push` should take if no refspec is
given (whether from the command-line, config, or elsewhere).
@@ -195,16 +195,32 @@ static const char message_detached_head_die[] =
"\n"
" git push %s HEAD:<name-of-remote-branch>\n");
-static const char *get_upstream_ref(struct branch *branch, const char *remote_name)
+static const char *get_upstream_ref(int flags, struct branch *branch, const char *remote_name)
{
- if (!branch->merge_nr || !branch->merge || !branch->remote_name)
+ if (branch->merge_nr == 0 && (flags & TRANSPORT_PUSH_AUTO_UPSTREAM)) {
+ /* if missing, assume same; set_upstream will be defined later */
+ return branch->refname;
+ }
+
+ if (!branch->merge_nr || !branch->merge || !branch->remote_name) {
+ const char *advice_autosetup_maybe = "";
+ if (!(flags & TRANSPORT_PUSH_AUTO_UPSTREAM)) {
+ advice_autosetup_maybe = _("\n"
+ "To have this happen automatically for "
+ "branches without a tracking\n"
+ "upstream, see 'push.autoSetupRemote' "
+ "in 'git help config'.\n");
+ }
die(_("The current branch %s has no upstream branch.\n"
"To push the current branch and set the remote as upstream, use\n"
"\n"
- " git push --set-upstream %s %s\n"),
+ " git push --set-upstream %s %s\n"
+ "%s"),
branch->name,
remote_name,
- branch->name);
+ branch->name,
+ advice_autosetup_maybe);
+ }
if (branch->merge_nr != 1)
die(_("The current branch %s has multiple upstream branches, "
"refusing to push."), branch->name);
@@ -212,7 +228,7 @@ static const char *get_upstream_ref(struct branch *branch, const char *remote_na
return branch->merge[0]->src;
}
-static void setup_default_push_refspecs(struct remote *remote)
+static void setup_default_push_refspecs(int *flags, struct remote *remote)
{
struct branch *branch;
const char *dst;
@@ -244,7 +260,7 @@ static void setup_default_push_refspecs(struct remote *remote)
case PUSH_DEFAULT_SIMPLE:
if (!same_remote)
break;
- if (strcmp(branch->refname, get_upstream_ref(branch, remote->name)))
+ if (strcmp(branch->refname, get_upstream_ref(*flags, branch, remote->name)))
die_push_simple(branch, remote);
break;
@@ -254,13 +270,21 @@ static void setup_default_push_refspecs(struct remote *remote)
"your current branch '%s', without telling me what to push\n"
"to update which remote branch."),
remote->name, branch->name);
- dst = get_upstream_ref(branch, remote->name);
+ dst = get_upstream_ref(*flags, branch, remote->name);
break;
case PUSH_DEFAULT_CURRENT:
break;
}
+ /*
+ * this is a default push - if auto-upstream is enabled and there is
+ * no upstream defined, then set it (with options 'simple', 'upstream',
+ * and 'current').
+ */
+ if ((*flags & TRANSPORT_PUSH_AUTO_UPSTREAM) && branch->merge_nr == 0)
+ *flags |= TRANSPORT_PUSH_SET_UPSTREAM;
+
refspec_appendf(&rs, "%s:%s", branch->refname, dst);
}
@@ -411,7 +435,7 @@ static int do_push(int flags,
if (remote->push.nr) {
push_refspec = &remote->push;
} else if (!(flags & TRANSPORT_PUSH_MIRROR))
- setup_default_push_refspecs(remote);
+ setup_default_push_refspecs(&flags, remote);
}
errs = 0;
url_nr = push_url_of_remote(remote, &url);
@@ -482,6 +506,10 @@ static int git_push_config(const char *k, const char *v, void *cb)
else
*flags &= ~TRANSPORT_PUSH_FOLLOW_TAGS;
return 0;
+ } else if (!strcmp(k, "push.autosetupremote")) {
+ if (git_config_bool(k, v))
+ *flags |= TRANSPORT_PUSH_AUTO_UPSTREAM;
+ return 0;
} else if (!strcmp(k, "push.gpgsign")) {
const char *value;
if (!git_config_get_value("push.gpgsign", &value)) {
@@ -162,6 +162,20 @@ test_expect_success 'push from/to branch with tracking fails with nothing ' '
test_push_failure nothing
'
+test_expect_success 'push from/to new branch succeeds with upstream if push.autoSetupRemote' '
+ git checkout -b new-branch-a &&
+ test_config push.autoSetupRemote true &&
+ test_config branch.new-branch-a.remote parent1 &&
+ test_push_success upstream new-branch-a
+'
+
+test_expect_success 'push from/to new branch succeeds with simple if push.autoSetupRemote' '
+ git checkout -b new-branch-c &&
+ test_config push.autoSetupRemote true &&
+ test_config branch.new-branch-c.remote parent1 &&
+ test_push_success simple new-branch-c
+'
+
test_expect_success '"matching" fails if none match' '
git init --bare empty &&
test_must_fail git push empty : 2>actual &&
@@ -145,6 +145,7 @@ struct transport {
#define TRANSPORT_PUSH_OPTIONS (1<<14)
#define TRANSPORT_RECURSE_SUBMODULES_ONLY (1<<15)
#define TRANSPORT_PUSH_FORCE_IF_INCLUDES (1<<16)
+#define TRANSPORT_PUSH_AUTO_UPSTREAM (1<<17)
int transport_summary_width(const struct ref *refs);