push: change simple to accommodate triangular workflows

When remote.pushdefault or branch.<name>.pushremote is set to a
remote that is different from where you usually fetch from (i.e. a
triangular workflow), master@{u} != origin, and push.default is set
to `upstream` or `simple` would fail with this error:

  $ git push
  fatal: You are pushing to remote 'origin', which is not the upstream of
  your current branch 'master', without telling me what to push
  to update which remote branch.

The very name of "upstream" indicates that it is only suitable for
use in central workflows; let us not even attempt to give it a new
meaning in triangular workflows, and error out as before.

However, the `simple` does not have to share this error.  It is
poised to be the default for Git 2.0, and we would like it to do
something sensible in triangular workflows.

Redefine "simple" as "safer upstream" for centralized workflow as
before, but work as "current" for triangular workflow.

We may want to make it "safer current", but that is a separate
issue.

Reported-by: Leandro Lucarella <leandro.lucarella@sociomantic.com>
Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Ramkumar Ramachandra 2013-06-19 16:41:41 +05:30 committed by Junio C Hamano
parent 87a70e4ce8
commit ed2b18292b
2 changed files with 38 additions and 15 deletions

View File

@ -1848,9 +1848,13 @@ push.default::
pushing to the same repository you would normally pull from pushing to the same repository you would normally pull from
(i.e. central workflow). (i.e. central workflow).
* `simple` - like `upstream`, but refuses to push if the upstream * `simple` - in centralized workflow, work like `upstream` with an
branch's name is different from the local one. This is the safest added safety to refuse to push if the upstream branch's name is
option and is well-suited for beginners. different from the local one.
+
When pushing to a remote that is different from the remote you normally
pull from, work as `current`. This is the safest option and is suited
for beginners.
+ +
This mode will become the default in Git 2.0. This mode will become the default in Git 2.0.

View File

@ -120,10 +120,11 @@ static const char message_detached_head_die[] =
"\n" "\n"
" git push %s HEAD:<name-of-remote-branch>\n"); " git push %s HEAD:<name-of-remote-branch>\n");
static void setup_push_upstream(struct remote *remote, int simple) static void setup_push_upstream(struct remote *remote, struct branch *branch,
int triangular)
{ {
struct strbuf refspec = STRBUF_INIT; struct strbuf refspec = STRBUF_INIT;
struct branch *branch = branch_get(NULL);
if (!branch) if (!branch)
die(_(message_detached_head_die), remote->name); die(_(message_detached_head_die), remote->name);
if (!branch->merge_nr || !branch->merge || !branch->remote_name) if (!branch->merge_nr || !branch->merge || !branch->remote_name)
@ -137,18 +138,29 @@ static void setup_push_upstream(struct remote *remote, int simple)
if (branch->merge_nr != 1) if (branch->merge_nr != 1)
die(_("The current branch %s has multiple upstream branches, " die(_("The current branch %s has multiple upstream branches, "
"refusing to push."), branch->name); "refusing to push."), branch->name);
if (strcmp(branch->remote_name, remote->name)) if (triangular)
die(_("You are pushing to remote '%s', which is not the upstream of\n" die(_("You are pushing to remote '%s', which is not the upstream of\n"
"your current branch '%s', without telling me what to push\n" "your current branch '%s', without telling me what to push\n"
"to update which remote branch."), "to update which remote branch."),
remote->name, branch->name); remote->name, branch->name);
if (simple && strcmp(branch->refname, branch->merge[0]->src))
if (push_default == PUSH_DEFAULT_SIMPLE) {
/* Additional safety */
if (strcmp(branch->refname, branch->merge[0]->src))
die_push_simple(branch, remote); die_push_simple(branch, remote);
}
strbuf_addf(&refspec, "%s:%s", branch->name, branch->merge[0]->src); strbuf_addf(&refspec, "%s:%s", branch->name, branch->merge[0]->src);
add_refspec(refspec.buf); add_refspec(refspec.buf);
} }
static void setup_push_current(struct remote *remote, struct branch *branch)
{
if (!branch)
die(_(message_detached_head_die), remote->name);
add_refspec(branch->name);
}
static char warn_unspecified_push_default_msg[] = static char warn_unspecified_push_default_msg[] =
N_("push.default is unset; its implicit value is changing in\n" N_("push.default is unset; its implicit value is changing in\n"
"Git 2.0 from 'matching' to 'simple'. To squelch this message\n" "Git 2.0 from 'matching' to 'simple'. To squelch this message\n"
@ -173,9 +185,16 @@ static void warn_unspecified_push_default_configuration(void)
warning("%s\n", _(warn_unspecified_push_default_msg)); warning("%s\n", _(warn_unspecified_push_default_msg));
} }
static int is_workflow_triangular(struct remote *remote)
{
struct remote *fetch_remote = remote_get(NULL);
return (fetch_remote && fetch_remote != remote);
}
static void setup_default_push_refspecs(struct remote *remote) static void setup_default_push_refspecs(struct remote *remote)
{ {
struct branch *branch; struct branch *branch = branch_get(NULL);
int triangular = is_workflow_triangular(remote);
switch (push_default) { switch (push_default) {
default: default:
@ -188,18 +207,18 @@ static void setup_default_push_refspecs(struct remote *remote)
break; break;
case PUSH_DEFAULT_SIMPLE: case PUSH_DEFAULT_SIMPLE:
setup_push_upstream(remote, 1); if (triangular)
setup_push_current(remote, branch);
else
setup_push_upstream(remote, branch, triangular);
break; break;
case PUSH_DEFAULT_UPSTREAM: case PUSH_DEFAULT_UPSTREAM:
setup_push_upstream(remote, 0); setup_push_upstream(remote, branch, triangular);
break; break;
case PUSH_DEFAULT_CURRENT: case PUSH_DEFAULT_CURRENT:
branch = branch_get(NULL); setup_push_current(remote, branch);
if (!branch)
die(_(message_detached_head_die), remote->name);
add_refspec(branch->name);
break; break;
case PUSH_DEFAULT_NOTHING: case PUSH_DEFAULT_NOTHING: