[PATCH 0/6] push.default in the triangular world

DORMANTno replies

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

[PATCH 0/6] push.default in the triangular world

From: Ramkumar Ramachandra <hidden>
Date: 2016-06-15 22:57:49

[2/6] documents existing push.default modes properly, but doesn't
touch `simple`.  It incorporates feedback from Junio, Philip Oakley,
Matthieu Moy.

[3/6] gives `simple` an exciting new meaning.  I think it's an
absolutely fabulous default!  It's aimed at triangular people who
occassionally need to do central stuff.

[6/6] adds some new tests to illustrate how all these push.default
modes work in central and triangular workflows.

Thanks.

Ramkumar Ramachandra (6):
  t/t5528-push-default: remove redundant test_config lines
  config doc: rewrite push.default section
  push: change `simple` to accommodate triangular workflows
  push: remove dead code in setup_push_upstream()
  t/t5528-push-default: generalize test_push_*
  t/t5528-push-default: test pushdefault workflows

 Documentation/config.txt | 62 ++++++++++++++++++++++++++----------------------
 builtin/push.c           | 27 +++++++++++++++++----
 t/t5528-push-default.sh  | 46 +++++++++++++++++++++++++++++++----
 3 files changed, 97 insertions(+), 38 deletions(-)

-- 
1.8.3.1.454.g30263f3.dirty

[PATCH 1/6] t/t5528-push-default: remove redundant test_config lines

From: Ramkumar Ramachandra <hidden>
Date: 2016-06-15 22:57:49

The line

  test_config push.default upstream

appears unnecessarily in two tests, as the final test_push_failure sets
push.default before pushing anyway.

Signed-off-by: Ramkumar Ramachandra <redacted>
---
 t/t5528-push-default.sh | 2 --
 1 file changed, 2 deletions(-)
diff --git a/t/t5528-push-default.sh b/t/t5528-push-default.sh
index 4736da8..69ce6bf 100755
--- a/t/t5528-push-default.sh
+++ b/t/t5528-push-default.sh
@@ -48,7 +48,6 @@ test_expect_success '"upstream" pushes to configured upstream' '
 test_expect_success '"upstream" does not push on unconfigured remote' '
 	git checkout master &&
 	test_unconfig branch.master.remote &&
-	test_config push.default upstream &&
 	test_commit three &&
 	test_push_failure upstream
 '
@@ -57,7 +56,6 @@ test_expect_success '"upstream" does not push on unconfigured branch' '
 	git checkout master &&
 	test_config branch.master.remote parent1 &&
 	test_unconfig branch.master.merge &&
-	test_config push.default upstream
 	test_commit four &&
 	test_push_failure upstream
 '
-- 
1.8.3.1.454.g30263f3.dirty

[PATCH 2/6] config doc: rewrite push.default section

From: Ramkumar Ramachandra <hidden>
Date: 2016-06-15 22:57:49

4d3592 (Merge branch 'rr/triangle', 2013-04-07) introduced support for
triangular workflows in Git, but the push.default values still assume
central workflows.  Rewrite the descriptions of `nothing`, `current`,
`upstream` and `matching` for greater clarity, and explicitly explaining
how they behave in triangular workflows.  Leave `simple` as it is for
the moment, as we plan to change its meaning to accommodate triangular
workflows in a later patch.

Signed-off-by: Ramkumar Ramachandra <redacted>
---
 Documentation/config.txt | 56 ++++++++++++++++++++++++++----------------------
 1 file changed, 30 insertions(+), 26 deletions(-)
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 7fd4035..9f04f74 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -1826,39 +1826,43 @@ pull.twohead::
 	The default merge strategy to use when pulling a single branch.
 
 push.default::
-	Defines the action `git push` should take if no refspec is given
-	on the command line, no refspec is configured in the remote, and
-	no refspec is implied by any of the options given on the command
-	line. Possible values are:
+	Defines the action `git push` should take if no refspec is
+	explicitly given.  Different values are well-suited for
+	specific workflows; for instance, in a purely central workflow
+	(i.e. the fetch source is equal to the push destination),
+	`upstream` is probably what you want.  Possible values are:
 +
 --
-* `nothing` - do not push anything.
-* `matching` - push all branches having the same name in both ends.
-  This is for those who prepare all the branches into a publishable
-  shape and then push them out with a single command.  It is not
-  appropriate for pushing into a repository shared by multiple users,
-  since locally stalled branches will attempt a non-fast forward push
-  if other users updated the branch.
-  +
-  This is currently the default, but Git 2.0 will change the default
-  to `simple`.
-* `upstream` - push the current branch to its upstream branch
-  (`tracking` is a deprecated synonym for this).
-  With this, `git push` will update the same remote ref as the one which
-  is merged by `git pull`, making `push` and `pull` symmetrical.
-  See "branch.<name>.merge" for how to configure the upstream branch.
+* `nothing` - do not push anything (error out) unless a refspec is
+  explicitly given.  Very safe, but not very convenient.
+
+* `current` - push the current branch to update a branch with the same
+  name on the receiving end.  Works in both central and non-central
+  workflows.  Equivalent to pushing the refspec "$branch" ($branch is
+  the name of the current branch).
+
+* `upstream` - push the current branch to a branch with the name
+  branch.$branch.merge on the receiving end, and error out if the push
+  destination is not the same as branch.$branch.remote.  The name
+  "upstream" refers to "@{u[pstream]}" in linkgit:gitrevisions[7],
+  which makes sense only if both branch.$branch.remote and
+  branch.$branch.merge are set.  It makes sure that a `push` is
+  symmetrical to `pull` in central workflows, and cannot be used in
+  non-central workflows.
+
 * `simple` - like `upstream`, but refuses to push if the upstream
   branch's name is different from the local one. This is the safest
   option and is well-suited for beginners. It will become the default
   in Git 2.0.
-* `current` - push the current branch to a branch of the same name.
+
+* `matching` - push all branches having the same name on both ends
+  (essentially ignoring all newly created local branches).
+  Well-suited for those who want to batch-update a specific set of
+  branches they consistently work on.  Use with caution, especially
+  when pushing with '--force'.  Equivalent to pushing the refspec ":".
+  This is currently the default, but Git 2.0 will change the default
+  to `simple`.
 --
-+
-The `simple`, `current` and `upstream` modes are for those who want to
-push out a single branch after finishing work, even when the other
-branches are not yet ready to be pushed out. If you are working with
-other people to push into the same shared repository, you would want
-to use one of these.
 
 rebase.stat::
 	Whether to show a diffstat of what changed upstream since the last
-- 
1.8.3.1.454.g30263f3.dirty

[PATCH 3/6] push: change `simple` to accommodate triangular workflows

From: Ramkumar Ramachandra <hidden>
Date: 2016-06-15 22:57:49

When remote.pushdefault or branch.<name>.pushremote is set (a triangular
workflow feature), master@{u} != origin, and push.default is set to
`upstream` or `simple`:

  $ 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.

Unfortunately, in the case of `upstream`, the very name 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 usual.  However, the `simple` does not have this problem: it is
poised to be the default for Git 2.0, and we would definitely like it to
do something sensible in triangular workflows.

Decouple `simple` from `upstream` completely, and change it to mean
`current` with a safety feature: a `push` and `pull` should not be
asymmetrical in the special case of central workflows.

Reported-by: Leandro Lucarella <redacted>
Signed-off-by: Ramkumar Ramachandra <redacted>
---
 Documentation/config.txt | 10 ++++++----
 builtin/push.c           | 21 ++++++++++++++++++++-
 t/t5528-push-default.sh  |  2 +-
 3 files changed, 27 insertions(+), 6 deletions(-)
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 9f04f74..81628e8 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -1850,10 +1850,12 @@ push.default::
   symmetrical to `pull` in central workflows, and cannot be used in
   non-central workflows.
 
-* `simple` - like `upstream`, but refuses to push if the upstream
-  branch's name is different from the local one. This is the safest
-  option and is well-suited for beginners. It will become the default
-  in Git 2.0.
+* `simple` - a safer version of `current`; push the current branch to
+  update a branch with the same name on the receiving end, with a
+  safety feature: in central workflows, error out if
+  branch.$branch.merge is set and not equal to $branch, to make sure
+  that a `push` and `push` are never asymmetrical.  It will become the
+  default in Git 2.0.
 
 * `matching` - push all branches having the same name on both ends
   (essentially ignoring all newly created local branches).
diff --git a/builtin/push.c b/builtin/push.c
index 2d84d10..d8d27d9 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -120,6 +120,25 @@ static const char message_detached_head_die[] =
 	   "\n"
 	   "    git push %s HEAD:<name-of-remote-branch>\n");
 
+static void setup_push_simple(struct remote *remote)
+{
+	struct branch *branch = branch_get(NULL);
+	if (!branch)
+		die(_(message_detached_head_die), remote->name);
+	if (!branch->merge_nr || !branch->merge || !branch->remote_name)
+		/* No upstream configured */
+		goto end;
+	if (branch->merge_nr != 1)
+		die(_("The current branch %s has multiple upstream branches, "
+		    "refusing to push."), branch->name);
+	if (!strcmp(branch->remote_name, remote->name) &&
+		strcmp(branch->refname, branch->merge[0]->src))
+		/* Central workflow safety feature */
+		die_push_simple(branch, remote);
+end:
+	add_refspec(branch->name);
+}
+
 static void setup_push_upstream(struct remote *remote, int simple)
 {
 	struct strbuf refspec = STRBUF_INIT;
@@ -188,7 +207,7 @@ static void setup_default_push_refspecs(struct remote *remote)
 		break;
 
 	case PUSH_DEFAULT_SIMPLE:
-		setup_push_upstream(remote, 1);
+		setup_push_simple(remote);
 		break;
 
 	case PUSH_DEFAULT_UPSTREAM:
diff --git a/t/t5528-push-default.sh b/t/t5528-push-default.sh
index 69ce6bf..e54dd02 100755
--- a/t/t5528-push-default.sh
+++ b/t/t5528-push-default.sh
@@ -85,7 +85,7 @@ test_expect_success 'push from/to new branch with current creates remote branch'
 test_expect_success 'push to existing branch, with no upstream configured' '
 	test_config branch.master.remote repo1 &&
 	git checkout master &&
-	test_push_failure simple &&
+	test_push_success simple master &&
 	test_push_failure upstream
 '
 
-- 
1.8.3.1.454.g30263f3.dirty

[PATCH 4/6] push: remove dead code in setup_push_upstream()

From: Ramkumar Ramachandra <hidden>
Date: 2016-06-15 22:57:49

Now that simple has been decoupled from upstream in setup_push_simple(),
remove the dead code in setup_push_upstream().

Signed-off-by: Ramkumar Ramachandra <redacted>
---
 builtin/push.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/builtin/push.c b/builtin/push.c
index d8d27d9..783bacf 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -139,7 +139,7 @@ end:
 	add_refspec(branch->name);
 }
 
-static void setup_push_upstream(struct remote *remote, int simple)
+static void setup_push_upstream(struct remote *remote)
 {
 	struct strbuf refspec = STRBUF_INIT;
 	struct branch *branch = branch_get(NULL);
@@ -161,8 +161,6 @@ static void setup_push_upstream(struct remote *remote, int simple)
 		      "your current branch '%s', without telling me what to push\n"
 		      "to update which remote branch."),
 		    remote->name, branch->name);
-	if (simple && strcmp(branch->refname, branch->merge[0]->src))
-		die_push_simple(branch, remote);
 
 	strbuf_addf(&refspec, "%s:%s", branch->name, branch->merge[0]->src);
 	add_refspec(refspec.buf);
@@ -211,7 +209,7 @@ static void setup_default_push_refspecs(struct remote *remote)
 		break;
 
 	case PUSH_DEFAULT_UPSTREAM:
-		setup_push_upstream(remote, 0);
+		setup_push_upstream(remote);
 		break;
 
 	case PUSH_DEFAULT_CURRENT:
-- 
1.8.3.1.454.g30263f3.dirty

[PATCH 5/6] t/t5528-push-default: generalize test_push_*

From: Ramkumar Ramachandra <hidden>
Date: 2016-06-15 22:57:49

The setup creates two bare repositories: repo1 and repo2, but
test_push_commit() hard-codes checking in repo1 for the actual output.
Generalize it and its caller, test_push_success(), to optionally accept
a third argument to specify the name of the repository to check for
actual output.  We will use this in the next patch.

Signed-off-by: Ramkumar Ramachandra <redacted>
---
 t/t5528-push-default.sh | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/t/t5528-push-default.sh b/t/t5528-push-default.sh
index e54dd02..b599186 100755
--- a/t/t5528-push-default.sh
+++ b/t/t5528-push-default.sh
@@ -15,17 +15,19 @@ test_expect_success 'setup bare remotes' '
 
 # $1 = local revision
 # $2 = remote revision (tested to be equal to the local one)
+# $3 = [optional] repo to check for actual output (repo1 by default)
 check_pushed_commit () {
 	git log -1 --format='%h %s' "$1" >expect &&
-	git --git-dir=repo1 log -1 --format='%h %s' "$2" >actual &&
+	git --git-dir="${3:-repo1}" log -1 --format='%h %s' "$2" >actual &&
 	test_cmp expect actual
 }
 
 # $1 = push.default value
 # $2 = expected target branch for the push
+# $3 = [optional] repo to check for actual output (repo1 by default)
 test_push_success () {
 	git -c push.default="$1" push &&
-	check_pushed_commit HEAD "$2"
+	check_pushed_commit HEAD "$2" "$3"
 }
 
 # $1 = push.default value
-- 
1.8.3.1.454.g30263f3.dirty

[PATCH 6/6] t/t5528-push-default: test pushdefault workflows

From: Ramkumar Ramachandra <hidden>
Date: 2016-06-15 22:57:49

Introduce test_pushdefault_workflows(), and test that all push.default
modes work with central and triangular workflows as expected.

Signed-off-by: Ramkumar Ramachandra <redacted>
---
 t/t5528-push-default.sh | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)
diff --git a/t/t5528-push-default.sh b/t/t5528-push-default.sh
index b599186..eabc09d 100755
--- a/t/t5528-push-default.sh
+++ b/t/t5528-push-default.sh
@@ -39,6 +39,26 @@ test_push_failure () {
 	test_cmp expect actual
 }
 
+# $1 = success or failure
+# $2 = push.default value
+# $3 = branch to check for actual output (master or foo)
+# $4 = [optional] switch to triangular workflow
+test_pushdefault_workflow () {
+	workflow=central
+	pushdefault=parent1
+	if test -n "${4-}"; then
+		workflow=triangular
+		pushdefault=parent2
+	fi
+test_expect_success "push.default = $2 $1 in $workflow workflows" "
+	test_config branch.master.remote parent1 &&
+	test_config branch.master.merge refs/heads/foo &&
+	test_config remote.pushdefault $pushdefault &&
+	test_commit commit-for-$2${4+-triangular} &&
+	test_push_$1 $2 $3 ${4+repo2}
+"
+}
+
 test_expect_success '"upstream" pushes to configured upstream' '
 	git checkout master &&
 	test_config branch.master.remote parent1 &&
@@ -115,4 +135,20 @@ test_expect_success 'push to existing branch, upstream configured with different
 	test_cmp expect-other-name actual-other-name
 '
 
+## test_pushdefault_workflow() arguments:
+# $1 = success or failure
+# $2 = push.default value
+# $3 = branch to check for actual output (master or foo)
+# $4 = [optional] switch to triangular workflow
+
+test_pushdefault_workflow success current master  # breaks push/pull symmetry
+test_pushdefault_workflow success upstream foo    # preserves push/pull symmetry
+test_pushdefault_workflow failure simple master   # errors out on asymmetry
+test_pushdefault_workflow success matching master # always works
+
+test_pushdefault_workflow success current master triangular  # always works
+test_pushdefault_workflow failure upstream foo triangular    # always errors out
+test_pushdefault_workflow success simple master triangular   # works like current
+test_pushdefault_workflow success matching master triangular # always works
+
 test_done
-- 
1.8.3.1.454.g30263f3.dirty
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help