From: Junio C Hamano <hidden> Date: 2016-06-15 22:56:45
Jeff King [off-list ref] writes:
quoted
With the branch.$name.remote, the user tells us "When I am on this
branch, I want to talk to this remote". When you did
git push -- master next ;# case #4
on branch maint, branch.maint.remote should not come into play.
I understand that's your position, but I don't understand _why_.
If branch.$name.remote is "when I am on this branch, I want to talk to
this remote", that rule is not be impacted by the presence of refspecs
at all.
So running the above while on 'maint' will send master and next to
the remote your "git push" would send to when run without any
refspecs?
That is internally consistent and understandable, and I have no
objection to it. Certainly much better than basing the decision on
branch.{master,next}.remote as I thought you were suggesting to do.
From: Jeff King <hidden> Date: 2016-06-15 22:56:45
On Wed, Apr 10, 2013 at 11:54:34AM -0700, Junio C Hamano wrote:
quoted
If branch.$name.remote is "when I am on this branch, I want to talk to
this remote", that rule is not be impacted by the presence of refspecs
at all.
So running the above while on 'maint' will send master and next to
the remote your "git push" would send to when run without any
refspecs?
Exactly. The remote selection is orthogonal to the refspecs provided,
and only cares about which branch you are on.
Which is still kind of weird, because why should the branch you are on
affect the default push location? But that is how default "matching" has
always behaved, and we would remain consistent with that.
That is internally consistent and understandable, and I have no
objection to it. Certainly much better than basing the decision on
branch.{master,next}.remote as I thought you were suggesting to do.
No, I am not suggesting that. I can see how such a command might be
useful (i.e. "push master to where it goes, next to where it goes",
where "goes" is defined by the upstream config). But that is not
remotely close to how "git push" works now, and would be inconsistent
with the other modes (e.g., matching, explicit refspecs, pushing
non-branches, etc).
-Peff
On Wed, Apr 10, 2013 at 11:54:34AM -0700, Junio C Hamano wrote:
quoted
quoted
If branch.$name.remote is "when I am on this branch, I want to talk to
this remote", that rule is not be impacted by the presence of refspecs
at all.
So running the above while on 'maint' will send master and next to
the remote your "git push" would send to when run without any
refspecs?
Exactly. The remote selection is orthogonal to the refspecs provided,
and only cares about which branch you are on.
Which is still kind of weird, because why should the branch you are on
affect the default push location? But that is how default "matching" has
always behaved, and we would remain consistent with that.
git push -- master next; pushes to my current branch's
branch.<name>.pushremote? Isn't that a disaster?
quoted
That is internally consistent and understandable, and I have no
objection to it. Certainly much better than basing the decision on
branch.{master,next}.remote as I thought you were suggesting to do.
No, I am not suggesting that. I can see how such a command might be
useful (i.e. "push master to where it goes, next to where it goes",
where "goes" is defined by the upstream config). But that is not
remotely close to how "git push" works now, and would be inconsistent
with the other modes (e.g., matching, explicit refspecs, pushing
non-branches, etc).
Otherwise, I think we're consistent. git push master; pushes the
refspec master (with no explicit :<dst> counterpart) to the "default
place to push to" (either depending on which branch I am, or global).
I think Junio was mixing up refspecs with refs (branches, and hence
branch configuration) earlier. git push origin; pushes to "default
refspecs" on the remote origin. By extension, git push; should push
"default respecs" to the "default place to push to". The "default
refspecs" in this context is determined by push.default, which is the
problem.
Otherwise, I think we're consistent. git push master; pushes the
refspec master (with no explicit :<dst> counterpart) to the "default
place to push to" (either depending on which branch I am, or global).
I think Junio was mixing up refspecs with refs (branches, and hence
branch configuration) earlier. git push origin; pushes to "default
refspecs" on the remote origin. By extension, git push; should push
"default respecs" to the "default place to push to". The "default
refspecs" in this context is determined by push.default, which is the
problem.
Major thinko here. The problem is git push master; choosing the
"default place to push to" depending on what branch I'm in. A plain
git push; is just fine.
From: Jeff King <hidden> Date: 2016-06-15 22:56:46
On Thu, Apr 11, 2013 at 01:01:33AM +0530, Ramkumar Ramachandra wrote:
Jeff King wrote:
quoted
On Wed, Apr 10, 2013 at 11:54:34AM -0700, Junio C Hamano wrote:
quoted
quoted
If branch.$name.remote is "when I am on this branch, I want to talk to
this remote", that rule is not be impacted by the presence of refspecs
at all.
So running the above while on 'maint' will send master and next to
the remote your "git push" would send to when run without any
refspecs?
Exactly. The remote selection is orthogonal to the refspecs provided,
and only cares about which branch you are on.
Which is still kind of weird, because why should the branch you are on
affect the default push location? But that is how default "matching" has
always behaved, and we would remain consistent with that.
git push -- master next; pushes to my current branch's
branch.<name>.pushremote? Isn't that a disaster?
Maybe. But no more so than the current:
git push
which may also push master and next to the same remote. As I said in an
earlier message, I would be OK with allowing both or neither, but
allowing one but not the other is even more confusing.
If we changed push.default=matching to ignore branch.*.remote, then that
would be consistent, and would probably be safer over all. It is a
regression, but I doubt that anybody was using branch.*.remote for this;
it really only makes sense with the "upstream" mode.
-Peff
git push -- master next; pushes to my current branch's
branch.<name>.pushremote? Isn't that a disaster?
Actually, branch.<name>.pushremote already breaks the current design
in a way, as Junio pointed out in a different email: a push.default
set to anything except "current" is already nonsensical. Why should
"matching" branches be pushed to the remote that my current branch
specifies? That might well have their own branch.<name>.pushremote
configured, which should be respected.
We should fix this now. I think the fault lies in the rather old
design of push.default. Do you have any suggestions as what would
make sense here? Ultimately, I think a git push; needs to pick
remotes for each refspec separately. The orthogonal design is
definitely not right in my opinion.
As the author of branch.<name>.pushremote, I apologize for not having
caught this earlier. I've been using push.default = current for a
long time, and don't often think about the other settings.
From: Jonathan Nieder <hidden> Date: 2016-06-15 22:56:46
Ramkumar Ramachandra wrote:
Ramkumar Ramachandra wrote:
quoted
git push -- master next; pushes to my current branch's
branch.<name>.pushremote? Isn't that a disaster?
Actually, branch.<name>.pushremote already breaks the current design
in a way
I don't see a big problem here, actually. What's so wrong with
branch.<name>.remote affecting what "git push" does? If
branch.crazy-feature.remote is my-personal-remote and I run
git push
and "[push] default = upstream", then it is obvious what the user
wanted to happen. But what about when "[push] default = matching"?
Which of the following behaviors is correct?
a) Error: you didn't tell me which remote to push to.
b) Just behave like "git push my-personal-remote :".
c) Ignore which branch is the current branch and behave like
"git push origin :".
How about when "[push] default = current"?
Except that people might have scripts or habits tied to the current
behavior, any of (a), (b), and (c) sounds fine to me. (b) is the
obvious choice for historical reasons.
Now if I rely on the proposed DWIM and run
git push master
then the corresponding choices are:
a) Error: you didn't tell me which remote to push to.
b) Just behave like "git push my-personal-remote master".
c) Behave like "git push origin master".
(b) is not a good choice there, but (a) and (c) look equally fine.
From: Jeff King <hidden> Date: 2016-06-15 22:56:46
On Thu, Apr 11, 2013 at 01:23:57AM +0530, Ramkumar Ramachandra wrote:
Ramkumar Ramachandra wrote:
quoted
git push -- master next; pushes to my current branch's
branch.<name>.pushremote? Isn't that a disaster?
Actually, branch.<name>.pushremote already breaks the current design
in a way, as Junio pointed out in a different email: a push.default
set to anything except "current" is already nonsensical. Why should
"matching" branches be pushed to the remote that my current branch
specifies? That might well have their own branch.<name>.pushremote
configured, which should be respected.
I'm not sure that it should be respected. "master" is short for
"refs/heads/master:refs/heads/master", and does not mean "push master to
where I have it configured to go" at all. That may be what the user
means, but changing how "git push" works is going to create
inconsistency with other cases.
We should fix this now. I think the fault lies in the rather old
design of push.default. Do you have any suggestions as what would
make sense here? Ultimately, I think a git push; needs to pick
remotes for each refspec separately. The orthogonal design is
definitely not right in my opinion.
Right, the example above might include multiple remotes if pushremote is
respected. Or it might not come up with an answer at all for a tag.
If you do:
git push -- v1.2.3 master
where does v1.2.3 go? To remote.pushdefault? That seems simple and
consistent, as there is no ref-specific pushremote defined. But I'd
guess that the user probably _wanted_ it to go to
branch.master.pushremote.
As the author of branch.<name>.pushremote, I apologize for not having
caught this earlier. I've been using push.default = current for a
long time, and don't often think about the other settings.
I don't think pushremote introduced the problem. It is much older than
that, and dates back to respecting branch.*.remote at all for pushes,
even though push.default=matching (and before we had push.default, it
was always matching) does not have anything to do with the current
branch.
-Peff
Maybe. But no more so than the current:
git push
which may also push master and next to the same remote.
I would argue that this was not really a problem in practice, until I
introduced branch.<name>.pushremote.
Let us imagine that I was working on artagnon/git.git (remote: ram), a
fork of git/git.git (remote: origin) earlier. My fork contains the
link and implicit-push branches in addition to the master, next and pu
branches, which are present on both. When I push from my
implicit-push branch with push.default = matching, I'm updating all
the matching refs on the remote ram (since branch.implicit-push.remote
is set to ram), which is fine. Now, I git push while on branch
master. My push is simply rejected, as I don't have write access to
the remote origin.
This is designed exactly for the read-only upstream, read-write fork
scenario. If I had write access to upstream (where we're essentially
regression to a centralized model), we'd have some major confusion.
As I said in an
earlier message, I would be OK with allowing both or neither, but
allowing one but not the other is even more confusing.
What is the point of allowing something internally consistent, but
nonsensical? You should complain.
From: Jeff King <hidden> Date: 2016-06-15 22:56:46
On Wed, Apr 10, 2013 at 01:05:12PM -0700, Jonathan Nieder wrote:
I don't see a big problem here, actually. What's so wrong with
branch.<name>.remote affecting what "git push" does? If
branch.crazy-feature.remote is my-personal-remote and I run
git push
and "[push] default = upstream", then it is obvious what the user
wanted to happen. But what about when "[push] default = matching"?
Which of the following behaviors is correct?
a) Error: you didn't tell me which remote to push to.
b) Just behave like "git push my-personal-remote :".
c) Ignore which branch is the current branch and behave like
"git push origin :".
How about when "[push] default = current"?
Except that people might have scripts or habits tied to the current
behavior, any of (a), (b), and (c) sounds fine to me. (b) is the
obvious choice for historical reasons.
I think (b) could be quite surprising to a user. I suspect it hasn't
come up because people just don't work with a lot of different remotes
in practice.
Now if I rely on the proposed DWIM and run
git push master
then the corresponding choices are:
a) Error: you didn't tell me which remote to push to.
b) Just behave like "git push my-personal-remote master".
c) Behave like "git push origin master".
(b) is not a good choice there, but (a) and (c) look equally fine.
My complaint with anything but (b) is that you can't use a relatively
simple rule ("if you do not specify a remote, we fallback to defaults,
in this order"). Now the rule is different depending on what is in the
refspecs. If I say "git push HEAD", where should it go? Does it respect
branch.*.remote or not?
-Peff
On Thu, Apr 11, 2013 at 01:23:57AM +0530, Ramkumar Ramachandra wrote:
quoted
Ramkumar Ramachandra wrote:
quoted
git push -- master next; pushes to my current branch's
branch.<name>.pushremote? Isn't that a disaster?
Actually, branch.<name>.pushremote already breaks the current design
in a way, as Junio pointed out in a different email: a push.default
set to anything except "current" is already nonsensical. Why should
"matching" branches be pushed to the remote that my current branch
specifies? That might well have their own branch.<name>.pushremote
configured, which should be respected.
I'm not sure that it should be respected. "master" is short for
"refs/heads/master:refs/heads/master", and does not mean "push master to
where I have it configured to go" at all. That may be what the user
means, but changing how "git push" works is going to create
inconsistency with other cases.
Yes, I know "master" refers to the refspec in the above, not the ref
(ie. branch). Hence branch configuration should have nothing to do
with this. That's just the way things currently are: doesn't mean
that it's perfect; and I'm just throwing ideas around.
quoted
We should fix this now. I think the fault lies in the rather old
design of push.default. Do you have any suggestions as what would
make sense here? Ultimately, I think a git push; needs to pick
remotes for each refspec separately. The orthogonal design is
definitely not right in my opinion.
Right, the example above might include multiple remotes if pushremote is
respected. Or it might not come up with an answer at all for a tag.
If you do:
git push -- v1.2.3 master
where does v1.2.3 go? To remote.pushdefault? That seems simple and
consistent, as there is no ref-specific pushremote defined.
remote.pushdefault indeed.
But I'd
guess that the user probably _wanted_ it to go to
branch.master.pushremote.
Huh, why? Simply because he specified master alongside it? How can
we infer what you said in a consistent system?
From: Jeff King <hidden> Date: 2016-06-15 22:56:46
On Thu, Apr 11, 2013 at 01:35:34AM +0530, Ramkumar Ramachandra wrote:
Jeff King wrote:
quoted
Maybe. But no more so than the current:
git push
which may also push master and next to the same remote.
I would argue that this was not really a problem in practice, until I
introduced branch.<name>.pushremote.
Let us imagine that I was working on artagnon/git.git (remote: ram), a
fork of git/git.git (remote: origin) earlier. My fork contains the
link and implicit-push branches in addition to the master, next and pu
branches, which are present on both. When I push from my
implicit-push branch with push.default = matching, I'm updating all
the matching refs on the remote ram (since branch.implicit-push.remote
is set to ram), which is fine. Now, I git push while on branch
master. My push is simply rejected, as I don't have write access to
the remote origin.
This is designed exactly for the read-only upstream, read-write fork
scenario. If I had write access to upstream (where we're essentially
regression to a centralized model), we'd have some major confusion.
I don't see how pushremote changes that. It was already a problem with
branch.*.remote, no?
I have a similar remote setup in my git.git repository. But all of my
branch.*.remote variables point to origin, because my branches are based
off of Junio's master. A matching push goes to the wrong place (and I
have screwed it up many times; it is nice that I do not have write
access to Junio's repository). The is broken without having pushremote
at all (and the proper fix is your remote.pushdefault).
quoted
As I said in an
earlier message, I would be OK with allowing both or neither, but
allowing one but not the other is even more confusing.
What is the point of allowing something internally consistent, but
nonsensical? You should complain.
If I were designing it today, I definitely think complaining is the
right thing to do. My only hesitation is the backwards compatibility.
If we are not going to break the existing behavior, I think it can be
argued that consistency and simplicity of the rules is important, so the
user can predict what will happen. But the more we discuss, the more I
think we should simply change the current behavior (to stop respecting
branch.* config with "matching"), which just seems wrong to me. Then we
can be simple and consistent, and do what the user probably intended.
-Peff
From: Jeff King <hidden> Date: 2016-06-15 22:56:46
On Thu, Apr 11, 2013 at 01:49:54AM +0530, Ramkumar Ramachandra wrote:
quoted
Right, the example above might include multiple remotes if pushremote is
respected. Or it might not come up with an answer at all for a tag.
If you do:
git push -- v1.2.3 master
where does v1.2.3 go? To remote.pushdefault? That seems simple and
consistent, as there is no ref-specific pushremote defined.
remote.pushdefault indeed.
quoted
But I'd
guess that the user probably _wanted_ it to go to
branch.master.pushremote.
Huh, why? Simply because he specified master alongside it? How can
we infer what you said in a consistent system?
That's kind of my point. Why would they put two refs together in a
single push command? Did they mean "I am pushing up master, and since I
just tagged it, send the tag along, too"? Or did they really mean to
push them to two different places? If so, why not just run two separate
push commands?
I am not saying git should guess that the user wanted the tag to along
with master. I am saying that the set of rules to come to that
conclusion is going to be too baroque for the user to understand, and
too often wrong in other cases, and that we should not go there.
-Peff
On Thu, Apr 11, 2013 at 01:35:34AM +0530, Ramkumar Ramachandra wrote:
quoted
Jeff King wrote:
quoted
Maybe. But no more so than the current:
git push
which may also push master and next to the same remote.
I would argue that this was not really a problem in practice, until I
introduced branch.<name>.pushremote.
Let us imagine that I was working on artagnon/git.git (remote: ram), a
fork of git/git.git (remote: origin) earlier. My fork contains the
link and implicit-push branches in addition to the master, next and pu
branches, which are present on both. When I push from my
implicit-push branch with push.default = matching, I'm updating all
the matching refs on the remote ram (since branch.implicit-push.remote
is set to ram), which is fine. Now, I git push while on branch
master. My push is simply rejected, as I don't have write access to
the remote origin.
This is designed exactly for the read-only upstream, read-write fork
scenario. If I had write access to upstream (where we're essentially
regression to a centralized model), we'd have some major confusion.
I don't see how pushremote changes that. It was already a problem with
branch.*.remote, no?
Technically, it changes nothing. pushremote is only an enabler for
more complex scenarios where git push; breaking user expectations is
magnified.
According to me, what branch.<name>.pushremote suddenly starts
supporting (apart from the use I intended for it) is each branch
having different read/ write access. So, we're back to git.git where
Junio has graciously given me write support to pu, but not next or
master. So I set up branch.master.pushremote and
branch.next.pushremote to ram and run git push; from pu. Disaster:
the pu ref went through fine, but master and next failed to get pushed
despite me specifying a proper pushremote for them.
I have a similar remote setup in my git.git repository. But all of my
branch.*.remote variables point to origin, because my branches are based
off of Junio's master. A matching push goes to the wrong place (and I
have screwed it up many times; it is nice that I do not have write
access to Junio's repository). The is broken without having pushremote
at all (and the proper fix is your remote.pushdefault).
Yeah, I can't believe I lived without remote.pushdefault for this long.
If we are not going to break the existing behavior, I think it can be
argued that consistency and simplicity of the rules is important, so the
user can predict what will happen. But the more we discuss, the more I
think we should simply change the current behavior (to stop respecting
branch.* config with "matching"), which just seems wrong to me. Then we
can be simple and consistent, and do what the user probably intended.
So there are some push.default options that respect branch.* config
(ie. "current"), and others that don't (ie. "matching"). I would
argue that push.default is badly designed to begin with, so the
solution makes sense to me even if the patch is a bit of hack; we
never guaranteed that the various push.default options respect the
same configuration variables.
On Thu, Apr 11, 2013 at 01:49:54AM +0530, Ramkumar Ramachandra wrote:
quoted
Huh, why? Simply because he specified master alongside it? How can
we infer what you said in a consistent system?
That's kind of my point. Why would they put two refs together in a
single push command? Did they mean "I am pushing up master, and since I
just tagged it, send the tag along, too"? Or did they really mean to
push them to two different places? If so, why not just run two separate
push commands?
I disagree. The protocol was built ground up to support updating
multiple refs in the same git push. Running N separate push commands
is _not_ the same thing at all; it running N times as slowly aside.
Pushing multiple refs is a valid and cogent usecase (while multiple
remotes is not). git is a distributed system: I make lots of changes
to various branches, tags and decide to push only when I'm taking a
break for lunch: at this point, I want to update all my refs on the
remote. In other words, I batch up ref updates because git is _meant_
to do that: creating/ modifying/ moving/ deleting refs is super-fast
(and happens all the time), while pushing is a slow and dangerous
(because gc runs) operation.
If we are not going to break the existing behavior, I think it can be
argued that consistency and simplicity of the rules is important, so the
user can predict what will happen. But the more we discuss, the more I
think we should simply change the current behavior (to stop respecting
branch.* config with "matching"), which just seems wrong to me. Then we
can be simple and consistent, and do what the user probably intended.
So there are some push.default options that respect branch.* config
(ie. "current"), and others that don't (ie. "matching"). I would
argue that push.default is badly designed to begin with, so the
solution makes sense to me even if the patch is a bit of hack; we
never guaranteed that the various push.default options respect the
same configuration variables.
If we're going to break "matching" anyway, let's break it fully. I
propose that we make it respect each individual branch's
branch.<name>.pushremote/ branch.<name>.remote and push the branch to
that remote. That'll let us design a git push -- master
implicit-push; that actually makes sense.
From: Jeff King <hidden> Date: 2016-06-15 22:56:46
On Thu, Apr 11, 2013 at 02:25:59AM +0530, Ramkumar Ramachandra wrote:
Jeff King wrote:
quoted
On Thu, Apr 11, 2013 at 01:49:54AM +0530, Ramkumar Ramachandra wrote:
quoted
Huh, why? Simply because he specified master alongside it? How can
we infer what you said in a consistent system?
That's kind of my point. Why would they put two refs together in a
single push command? Did they mean "I am pushing up master, and since I
just tagged it, send the tag along, too"? Or did they really mean to
push them to two different places? If so, why not just run two separate
push commands?
I disagree. The protocol was built ground up to support updating
multiple refs in the same git push. Running N separate push commands
is _not_ the same thing at all; it running N times as slowly aside.
But I think all of this discussion just reinforces my point. We do not
have to agree on what the user intended. But the fact that we do not
agree means that out of a sample size of 2 users, we have 2 different
things the user expects to happen. If we choose a behavior and say "this
makes sense", then the other half of the users are going to be confused
or annoyed.
-Peff
But I think all of this discussion just reinforces my point. We do not
have to agree on what the user intended. But the fact that we do not
agree means that out of a sample size of 2 users, we have 2 different
things the user expects to happen. If we choose a behavior and say "this
makes sense", then the other half of the users are going to be confused
or annoyed.
Yes, disagreement is healthy. My point is that we should have "sane"
defaults, and fine-grained configurability so that uses who disagree
can maintain their own configs. In this case, respecting a
branch.*.remote for each branch is more fine-grained, while not doing
so is coarse and makes me unhappy. Then again, we don't have to go
overboard and design another ten configuration variables, but we can
atleast improve on what we already have without breaking consistency
(but we have to minimally break backward compatibility).
From: Jonathan Nieder <hidden> Date: 2016-06-15 22:56:46
Ramkumar Ramachandra wrote:
My point is that we should have "sane"
defaults, and fine-grained configurability so that uses who disagree
can maintain their own configs.
I don't agree with this principle. I like a tool that behaves sanely
with little work and that is flexible enough to do hard things when
that's needed. Neither of those attributes implies configurability,
except in those unfortunate cases where "behaving sanely with little
work on the user's part" has to involve a different behavior from
person to person.
When people disagree about sane defaults, that's a sign that we didn't
understand the problem well. Often more thinking can lead to a
simpler answer.
From: Jonathan Nieder <hidden> Date: 2016-06-15 22:56:46
Jeff King wrote:
On Wed, Apr 10, 2013 at 01:05:12PM -0700, Jonathan Nieder wrote:
quoted
git push
and "[push] default = upstream", then it is obvious what the user
wanted to happen. But what about when "[push] default = matching"?
Which of the following behaviors is correct?
a) Error: you didn't tell me which remote to push to.
b) Just behave like "git push my-personal-remote :".
c) Ignore which branch is the current branch and behave like
"git push origin :".
How about when "[push] default = current"?
Except that people might have scripts or habits tied to the current
behavior, any of (a), (b), and (c) sounds fine to me. (b) is the
obvious choice for historical reasons.
I think (b) could be quite surprising to a user. I suspect it hasn't
come up because people just don't work with a lot of different remotes
in practice.
Yeah, I think you're right.
I'll try writing a series to switch to (c) for [push] default = matching
and (a) for default = simple (and one of the two for default = current.
Not sure which yet).
Thanks,
Jonathan
When people disagree about sane defaults, that's a sign that we didn't
understand the problem well. Often more thinking can lead to a
simpler answer.
Okay, let's see if we can all agree.
In a different email, you wrote:
git push master
...
a) Error: you didn't tell me which remote to push to.
b) Just behave like "git push my-personal-remote master".
c) Behave like "git push origin master".
Here, I'd argue for (d): push to branch.master.pushremote/
branch.master.remote/ remote.pushdefault/ origin. If others agree on
this, we can break "matching" appropriately, like I proposed earlier,
to make everything consistent once again.
From: Jonathan Nieder <hidden> Date: 2016-06-15 22:56:46
Ramkumar Ramachandra wrote:
Jonathan Nieder wrote:
quoted
git push master
...
a) Error: you didn't tell me which remote to push to.
b) Just behave like "git push my-personal-remote master".
c) Behave like "git push origin master".
Here, I'd argue for (d): push to branch.master.pushremote/
branch.master.remote/ remote.pushdefault/ origin.
My first hunch is not to like this, since it means
git push -- master next
might push to two different remotes and because it's not obvious
to me when it would be useful.
So I am still leaning toward the behavior Jeff suggested. That said,
we're probably at the point where enough scenarios have been described
for someone to write a patch with a clear explanation about how their
proposed behavior takes care of them all.
Thanks,
Jonathan
If we're going to break "matching" anyway, let's break it fully.
Wait, let's not break anything. Instead, let us invent a new
push.default that does this ref-to-remote matching, and make git push
-- master push-implicit; consistent with that. Then "matching" can be
deprecated as usual.
My first hunch is not to like this, since it means
git push -- master next
might push to two different remotes and because it's not obvious
to me when it would be useful.
Yes, it will push to two different remotes. And why is it not useful?
If we also had something corresponding to branch.<name>.merge for
push*, I would argue that this and branch.<name>.pushremote together
define how a branch should be pushed, independent of everything else.
Like I said earlier, I don't think of a git repository as a whole, but
rather a collection of upstream and forked branches. Every branch is
always fetched from its upstream (might or might not be the same as
fork) using branch.<name>.remote, and pushed to its fork using
branch.<name>.pushremote. A git push; can use the context of the
current branch to select branches (along with their configurations) to
push, nothing more. It should not apply the configuration of the
current branch to the other branches that it's pushing; that's just
wrong.
* Can we write this branch.<name>.pushmap, effectively overriding
branch.<name>.merge when in push.default = upstream and simple?
From: Jeff King <hidden> Date: 2016-06-15 22:56:46
On Thu, Apr 11, 2013 at 03:12:20AM +0530, Ramkumar Ramachandra wrote:
Jonathan Nieder wrote:
quoted
My first hunch is not to like this, since it means
git push -- master next
might push to two different remotes and because it's not obvious
to me when it would be useful.
Yes, it will push to two different remotes. And why is it not useful?
It's not that it's not potentially useful. It's that it may be
surprising and annoying to users who did not want that.
-Peff
It's not that it's not potentially useful. It's that it may be
surprising and annoying to users who did not want that.
... but this is a new syntax, and doesn't break any existing
expectations. Why are you imagining what users will expect with a git
push -- master next; that hasn't been invented yet? From "matching"?
But we've made it very clear that it's going to change soon. Users
can still use the git push origin master next; form as usual. In my
opinion, this new syntax is incredibly useful if users set pushremote
properly.
From: Jeff King <hidden> Date: 2016-06-15 22:56:46
On Thu, Apr 11, 2013 at 03:36:10AM +0530, Ramkumar Ramachandra wrote:
Jeff King wrote:
quoted
It's not that it's not potentially useful. It's that it may be
surprising and annoying to users who did not want that.
... but this is a new syntax, and doesn't break any existing
expectations. Why are you imagining what users will expect with a git
push -- master next; that hasn't been invented yet?
Didn't I already say that I expected a different behavior than you are
proposing from "git push -- v1.2.3 master"?
Yes, it is possible to lay out all of the rules in the manpage so that
the user can predict what will happen. But users do not always know or
remember all of those rules, especially if it "just works" most of the
time (e.g., they usually push two branches, but this time push a tag).
If a command is easy to screw up, people will screw it up, and get
surprised and annoyed when it happens. We can say "well, you should have
read the manpage", but it is much nicer if we can come up with a command
that is harder to screw up.
-Peff
From: Jeff King <hidden> Date: 2016-06-15 22:56:46
On Thu, Apr 11, 2013 at 03:41:33AM +0530, Ramkumar Ramachandra wrote:
Jeff King wrote:
quoted
It's not that it's not potentially useful. It's that it may be
surprising and annoying to users who did not want that.
Besides, I'm not able to imagine one scenario where this is the wrong
or annoying thing to do. Can you provide an example?
To flesh out my earlier example:
$ git clone https://github.com/upstream/project.git
$ cd project
$ hack hack hack; commit commit commit
$ git tag -m 'something of note' my-tag
$ git remote add me https://github.com/me/project.git
$ git config branch.master.remote me
$ git tag -m 'something of note'
$ git push master my-tag
My intent there is publish both master and mytag, but my-tag goes to
origin. It's obvious if you think carefully about (and know) the rules,
and it's user error. But what fault do we take for designing a feature
that causes confusion?
Maybe I am the only one who might make that mistake, and it is a
non-issue. But I would be much happier if git said "hey, are you sure
you wanted to push to two different remotes?". At least by default.
-Peff
To flesh out my earlier example:
$ git clone https://github.com/upstream/project.git
$ cd project
$ hack hack hack; commit commit commit
$ git tag -m 'something of note' my-tag
$ git remote add me https://github.com/me/project.git
$ git config branch.master.remote me
$ git tag -m 'something of note'
$ git push master my-tag
My intent there is publish both master and mytag, but my-tag goes to
origin. It's obvious if you think carefully about (and know) the rules,
and it's user error. But what fault do we take for designing a feature
that causes confusion?
Good example. Sorry, I misunderstood your one-liner. I agree that
this is confusing. Tags are a bit of an outlier, and we have to think
of some way to behave sensibly with them. I'll let you know if I
think of something in the next few hours.
$ git clone https://github.com/upstream/project.git
$ cd project
$ hack hack hack; commit commit commit
$ git tag -m 'something of note' my-tag
$ git remote add me https://github.com/me/project.git
$ git config branch.master.remote me
$ git tag -m 'something of note'
$ git push master my-tag
Tags have nothing to do with branches, and it is illogical to respect
branch.* when pushing a tag. You've illustrated a common case when
the user creates a tag on a specific branch and immediately pushes it.
I would argue that optimizing our tools for this specific usecase
breaks the general case. What if I create a branch on master, and
decide to push the tag after checking out implicit-push and doing some
work on it? Does that not break user expectations?
In the "I push to the same place I pull from" (aka. single-remote)
case, there are never any problems and even "matching" works fine.
However, in the triangular workflow (aka. multiple-remote) case, you
must give git enough information about your workflow for it to DTRT.
I will argue that, in the above example, you have not configured git
for a multiple-remote case, and that you cannot expect it to DTRT
since you have supplied insufficient information. As to how to
configure git for the general multiple-remote case:
Let us imagine that origin points to git/git.git (upstream), ram
points to artagnon/git.git and peff points to peff/git.git. I fork
off from upstream and have various local branches that only have
corresponding refs in ram (say implicit-push). Then, you fork off
from me, and have various local branches that only have corresponding
refs in peff (say implicit-push-next). I have peff as a configured
remote, because I routinely review the changes you make to my fork.
In this case, I must have:
- push.default set to anything but matching, because matching makes no
sense in the multiple-remote scenario*.
- remote.default set to origin, because this is where I get new code
from for all branches.
- remote.pushdefault set to ram, because this is where I publish all
my refs to (whether branches or tags). I might have local branches
that I will never publish, but that's a separate issue. The point is
that all my tags will always be published here.
- branch.implicit-push.remote set to ram, because this is the correct
upstream for the implicit-push branch.
- branch.implicit-push-next.remote set to peff, because this is the
correct upstream for the implicit-push-next branch.
- branch.implicit-push-next.pushremote set to null**, because I will
never want to push this branch.
(Note that branch.implicit-push.pushremote is unnecessary because
remote.pushdefault takes care of that)
With these settings, git push will always DTRT when you specify
nothing, just a remote, or just a refspec. Ofcourse, you can specify
both and be explicit (which is what we do now). Does this make sense?
Should we document this in gitworkflows.txt so that users know what
is expected of them when they move from a single-remote setup to a
multi-remote setup?
* I will definitely push for the deprecation of push.default=matching,
but I doubt we need to invent a new push.default. current makes a lot
of sense to me personally.
** Yet to be invented.
- branch.implicit-push-next.pushremote set to null**, because I will
never want to push this branch.
Currently, I have a hacky workaround: I set
branch.implicit-push-next.pushremote to a remote that I don't have
write access to (ie. origin), effectively failing all my attempts to
push this branch.
From: Junio C Hamano <hidden> Date: 2016-06-15 22:56:48
Jeff King [off-list ref] writes:
Which is still kind of weird, because why should the branch you are on
affect the default push location? But that is how default "matching" has
always behaved, and we would remain consistent with that.
I agree that what makes us behave "kind of weird" is that the
current branch is used to look up branch.$name.{remote,pushremote}
when pushing. I do not think "matching" [*1*] has anything to do
with it.
The per-branch configuration, branch.$name.{remote,pushremote}, says
"this branch interacts with a remote that is different from what I
normally interact with".
It is excusable for branch.$name.remote to take the current branch
into account, when it is used to govern the fetch-integrate side
(i.e. not used as a fall-back for branch.$name.pushremote). In
order to affect that configured local branch, e.g. "git pull" to
merge other's work, you need to have that named branch checked out
in your working tree. Triggering the effect of the configuration
based on which branch is checked out makes more sense because of
that reason when you are fetching.
It does not make much sense to use the current branch as the key to
look it up when you are pushing things out. If anything, what is
being pushed out should be what determines where it goes.
But that is a realization that comes after you think the issue long
and hard enough. To a casual end user, I think it is an equally or
even more natural expectation a "git push" would pick the destination
based on what branch you are currently on, as that is what happens
when he runs the command without any argument.
[Footnote]
*1* The "matching" semantics is to support the workflow for people
who batch things up. You perfect _all_ your branches that matter to
the public, and push all of them in one go. If you do not finish a
work on one branch and push out when other branches are not yet
ready, you do not want your push to be limited to the current
branch. And you do not have to "configure" what branches should be
visible to the public. Instead, you have _your_ remote remember it
for you: what are already there are the ones that are updated.
The "current", "upstream", etc. are to support folks who want to
push work done on a single branch out as soon as it is done, even
though the other branches are in no shape to be pushed out.