From: Junio C Hamano <hidden> Date: 2016-06-15 23:00:58
Marc Branchaud [off-list ref] writes:
(Apologies for not CCing all the folks who've participated in the "Pull is
Evil" thread -- I couldn't find a good branch of that thread for this message.)
OK, so maybe "git pull" is just Mostly Evil. People seem to have found many
different ways to make it work for them.
But in reality "git pull" has become a chimera that confuses a large number
of new users, and that experienced users either avoid entirely or customize
to give them a convenient shorthand for working in their particular
environment. As a tool for new git users, it just doesn't seem to be
achieving its goals.
I think the git project as a whole would benefit if it started to treat "git
pull" as an advanced command, in the sense that it needs to be configured by
an experienced user in order to make it correctly follow a project's
workflow. Once it's configured properly, "git pull" is a powerful tool that
gives users an easy way to do complex things. In that sense, it may be
appropriate for a project to tailor "git pull" as it likes, then teach its
own users to use the command.
However, when it comes to teaching people how to use git qua git, "git pull"
should be the last thing they learn about, because it's only after you
understand various basic git concepts that you can configure "git pull" to do
the right thing.
To that end, I suggest that pull's default behaviour should be to do
*nothing*. It should just print out a message to the effect that it hasn't
been configured, and that the user should run "git help pull" for guidance.
It'll take quite a bit of time, but I think that if we change our attitude
towards "git pull" and take this unconfigured-by-default approach, then in a
few years the entire git ecosystem will be in a better place.
Your earlier long-hand, together with the two examples that pulls
into the same "maint" branch Brian gave us, may give us a better
starting points to think about a saner way.
To me, the problem sounds like:
Tutorials of Git often says "use 'git pull' to catch up your
branch with your upstream work and then 'git push' back" (and
worse yet, 'git push' that does not fast-forward suggests doing
so), but 'git pull' that creates a merge in a wrong direction is
not the right thing for many people.
And proposed solutions range from "let's write 'pull' off as a
failed experiment" to "let's forbid any merge made by use of 'pull'
by default, because it is likely that merge may be in reverse".
Let's look at Brian's examples, which may point at a good direction.
When he becomes in charge of producing a new 'maint' (in his
original, he says 'maintenance-branch'), he first does this:
$ git checkout maint
$ git pull --ff-only [ origin maint ]
He may have a stale 'maint' branch for a variety of reasons. He may
have been the pumpking in the past, worked on his local 'maint' to
advance its tip with merges in the right direction and pushed the
result out to the central repository when he was done, and kept that
then-current 'maint' in his repository without removing when he
passed the pumpkin to somebody else. As you said in the thread,
this could have been done on a detached head, but keeping the local
branch around is more convenient (you may want to do a disconnected
development and having a reference point is handy). Or he may be
the long-term pumpking for 'maint' branch, but is working on a
machine different from the one he updated the shared 'maint' the
last time.
In either case, what is most important for this 'pull' is that he is
catching up with today's central repository, without losing any old
work that he forgot to push out when he was playing the pumpking the
last time (hence --ff-only to cause it to fail if that is the case)
in this local repository.
Then he integrates a topic by another and push the result with:
$ git pull [--no-ff] developer-remote topic-branch
$ git push [ origin maint ]
For this 'pull', he knows that this may not fast-forward (the
$DAYJOB convention to use a real merge even when the merge
fast-forwards is optional).
Even with the proposed "pull.mode" or "branch.maint.pullmode", these
two 'pull' cannot be given a convenient default. The best we can do
with the approach is to set pull.mode to ff-only for safety to protect
his first 'pull' from the origin/maint, and have him remember to
override it from the command line with "--merge --no-ff" [*1*].
If we step back a bit, because we are forcing him to differentiate
these two pulls in his mental model anyway, perhaps it may help
people (both new and old) if we had a new command to make the
distinction stand out more. What if the command sequence were like
this instead?
$ git checkout maint
$ git update [ origin maint ]
$ git pull [--no-ff] developer-remote topic-branch
$ git push [ origin maint ]
where the new command 'update' enforces the '--ff-only' update. And
then we would stop telling "'git pull' first" when a push does not
fast-forward.
Stepping back even further, and thinking what is different between
these two pulls, we notice that the first one is pulling from the
place we push back to. Perhaps a way to solve this issue, without
having to introduce a new 'git update' and updating the tutorials,
may be disallow fetch+merge by default only when pulling from the
place the result is going to be pushed back to? That is one case in
which it is very clear that we are making a merge in the wrong
direction. When you are pulling from developer-remote that is not
where you are going to push back, is there a reason to forbid a
non-ff pull from creating a merge?
Also I think what you said in a separate subthread merits more
thought:
What's more, it seems to me that the only real advantage "git
pull" provides here is a less typing compared to the non-pull
equivalent:
git fetch main-repo
git checkout main-repo/maintenance-branch
git fetch developer-remote
git merge --no-ff developer-remote/topic-branch
git push main-repo HEAD
I suggest that this approach is superior for new users (despite
the increased risk of finger cramps), because if main-repo's
maintenance-branch is updated in the interim and the push fails,
the user can use the exact same commands to resolve the
situation.
I very much like the "you can easily tell the new person to redo the
whole thing when the last push does not fast-forwareed" aspect of
this approach. Maybe a good alternative may be to encapsulate this
sequence to a handy "git update" (which is *NOT* the one I suggested
off-the-cuff in the above to replace the first 'git pull' in Brian's
example---it is more for the second one) wrapper and promote the use
of that in our tutorials. Perhaps:
$ git checkout maint
$ git update developer-remote topic-branch
$ git push [ origin maint ]
that tells us to fast-forward update the current branch (maint) from
its upstream (origin), fetch the developer's work and merge it.
When the last 'push' does not fast-forward, that has to be because
somebody else pushed (because we already made it up-to-date), so the
second time around, "git update" that notices that it cannot
fast-forward can offer to recreate the merge (or any further work
done since the local 'maint' diverged from the origin/maint), before
redoing the "git pull developer-remote topic-branch" phase.
A hypothetical transcript might go like this.
$ git checkout maint
$ git update developer-remote topic-branch
... does a rough equivalent of
$ git pull --ff-only [ origin maint ]
$ git pull [--no-ff] developer-remote topic-branch
$ git push [ origin maint ]
error: the push does not fast-forward. "git update" before
error: attempting to push again.
$ git update
... internally does:
$ git pull --ff-only [ origin maint ]
... which fails due to --ff-only
info: You have some local work made on an old version of
info: origin/maint. Let's rebuild it on top of the latest.
... does a rough equivalent of
$ git rebase --preserve-merges origin/maint
$ git push [ origin maint ]
... this time it succeeds.
Note that this would also support, without any change, those who
build their own changes directly on top of their 'master' and push
the result back to the shared 'master'.
And to guard new people who type 'pull' when they meant 'update',
we can notice if the pull is coming from the same place origin/maint
we will push back to.
Hmm?
[Footnote]
*1* I do not think it is *wrong* to say "we won't differentiate
these two modes; if somebody pulls into the same branch this
way, he is an integrator and should know better than newbies who
gets harmed by a merge in the wrong direction" and stop our
effort at this point. I would say that is perfectly a valid
position to take, as long as it is clearly documented: in order
to help majority of new people, experienced ones are asked to do
X and Y that they did not have to.
From: Felipe Contreras <hidden> Date: 2016-06-15 23:00:58
Junio C Hamano wrote:
If we step back a bit, because we are forcing him to differentiate
these two pulls in his mental model anyway, perhaps it may help
people (both new and old) if we had a new command to make the
distinction stand out more. What if the command sequence were like
this instead?
$ git checkout maint
$ git update [ origin maint ]
$ git pull [--no-ff] developer-remote topic-branch
$ git push [ origin maint ]
where the new command 'update' enforces the '--ff-only' update. And
then we would stop telling "'git pull' first" when a push does not
fast-forward.
In addition to barf when it's not a fast-forward, such command can
switch the parents, so it appears 'maint' was merged to 'origin/maint'.
Many people have complained about this order.
Stepping back even further, and thinking what is different between
these two pulls, we notice that the first one is pulling from the
place we push back to. Perhaps a way to solve this issue, without
having to introduce a new 'git update' and updating the tutorials,
may be disallow fetch+merge by default only when pulling from the
place the result is going to be pushed back to?
Which is basically essentially the same as not specifying anything, or
rather, running `git pull` without arguments.
--
Felipe Contreras
From: Jeff King <hidden> Date: 2016-06-15 23:00:59
On Fri, May 02, 2014 at 02:11:05PM -0500, Felipe Contreras wrote:
Junio C Hamano wrote:
quoted
If we step back a bit, because we are forcing him to differentiate
these two pulls in his mental model anyway, perhaps it may help
people (both new and old) if we had a new command to make the
distinction stand out more. What if the command sequence were like
this instead?
$ git checkout maint
$ git update [ origin maint ]
$ git pull [--no-ff] developer-remote topic-branch
$ git push [ origin maint ]
where the new command 'update' enforces the '--ff-only' update. And
then we would stop telling "'git pull' first" when a push does not
fast-forward.
In addition to barf when it's not a fast-forward, such command can
switch the parents, so it appears 'maint' was merged to 'origin/maint'.
Many people have complained about this order.
I realize this has veered off into talking about an "update" command,
and not necessarily "pull", but since there a lot of proposals floating
around, I wanted to make one point: if we are going to do such a switch,
let's please make it something the user explicitly turns on.
One common workflow for GitHub users is to back-merge master into a
topic, because they want the final "integrated" version on the topic
branch. That lets it get review, run tests, and even get test-deployed
from there before merging to master (and then when it does merge to
master, we know the result will be a trivial merge). This workflow
helps spread out the load (there is no central "integration" person or
script, and the merge itself becomes a possible part of the review/test
cycle). Some projects will do this by rebasing the topic, but that has
its own complications (like making collaboration harder because the
commits are being frequently rewritten).
Such users are going to run "git pull origin master" or just "git pull"
to get that merge. A switch to disallowing non-ff is going to disrupt
that workflow. I think we can live with that, as they should be able to
stop and say "no, my workflow wants these merges", set a config
variable, and be done.
But I think that is the same moment they should probably be deciding on
whether their workflow wants "regular" or "reverse" merges. And I do not
think the decision between the two has an obvious split over which is
better. So it makes sense to me to take the opportunity when the user is
thinking about their workflow to have them specify one or the other.
-Peff
From: Felipe Contreras <hidden> Date: 2016-06-15 23:00:59
Jeff King wrote:
On Fri, May 02, 2014 at 02:11:05PM -0500, Felipe Contreras wrote:
quoted
Junio C Hamano wrote:
quoted
If we step back a bit, because we are forcing him to differentiate
these two pulls in his mental model anyway, perhaps it may help
people (both new and old) if we had a new command to make the
distinction stand out more. What if the command sequence were like
this instead?
$ git checkout maint
$ git update [ origin maint ]
$ git pull [--no-ff] developer-remote topic-branch
$ git push [ origin maint ]
where the new command 'update' enforces the '--ff-only' update. And
then we would stop telling "'git pull' first" when a push does not
fast-forward.
In addition to barf when it's not a fast-forward, such command can
switch the parents, so it appears 'maint' was merged to 'origin/maint'.
Many people have complained about this order.
I realize this has veered off into talking about an "update" command,
and not necessarily "pull", but since there a lot of proposals floating
around, I wanted to make one point: if we are going to do such a switch,
let's please make it something the user explicitly turns on.
This is sensible, but with warning "X will be the default in the
future", just like we did with push.default = simple.
One common workflow for GitHub users is to back-merge master into a
topic, because they want the final "integrated" version on the topic
branch. That lets it get review, run tests, and even get test-deployed
from there before merging to master (and then when it does merge to
master, we know the result will be a trivial merge). This workflow
helps spread out the load (there is no central "integration" person or
script, and the merge itself becomes a possible part of the review/test
cycle). Some projects will do this by rebasing the topic, but that has
its own complications (like making collaboration harder because the
commits are being frequently rewritten).
They can do:
% git pull origin master
That shouldn't revese the bases.
Such users are going to run "git pull origin master" or just "git pull"
to get that merge.
I'd say the vast majority of users running "git pull" want the parents
reversed, the minority that doesn't can switch to "git pull origin
master" (or add a configuration).
A switch to disallowing non-ff is going to disrupt
that workflow.
Only if the refuse to do "git pull origin master".
But I think that is the same moment they should probably be deciding on
whether their workflow wants "regular" or "reverse" merges. And I do not
think the decision between the two has an obvious split over which is
better.
Because there hasn't been enough discussion on this topic. I'm fairly
certain there will be consensus once concrete proposals are properly
discussed.
Most likely the consensus and the proposals will be ignored and nothing
will change as usual, but that's a different thing.
--
Felipe Contreras
From: Jeff King <hidden> Date: 2016-06-15 23:00:59
On Fri, May 02, 2014 at 04:55:01PM -0500, Felipe Contreras wrote:
They can do:
% git pull origin master
That shouldn't revese the bases.
Then they have to remember to do that every time, no? That seems a
little error-prone versus setting a config option.
quoted
Such users are going to run "git pull origin master" or just "git pull"
to get that merge.
I'd say the vast majority of users running "git pull" want the parents
reversed, the minority that doesn't can switch to "git pull origin
master" (or add a configuration).
I'm not sure I agree, but I don't think either of us has actual data.
Most likely the consensus and the proposals will be ignored and nothing
will change as usual, but that's a different thing.
Is it truly necessary to make sniping comments like this at the end of
each email? It _is_ being discussed right now, and these comments do
nothing except irritate your readers. Please stop.
-Peff
From: Felipe Contreras <hidden> Date: 2016-06-15 23:00:59
Jeff King wrote:
On Fri, May 02, 2014 at 04:55:01PM -0500, Felipe Contreras wrote:
quoted
They can do:
% git pull origin master
That shouldn't revese the bases.
Then they have to remember to do that every time, no? That seems a
little error-prone versus setting a config option.
Yes. However, since not many people do this, and they don't do it that
often that's not a big deal.
It's much more important to fix the issue the vast majority of users
face constantly.
quoted
quoted
Such users are going to run "git pull origin master" or just "git pull"
to get that merge.
I'd say the vast majority of users running "git pull" want the parents
reversed, the minority that doesn't can switch to "git pull origin
master" (or add a configuration).
I'm not sure I agree, but I don't think either of us has actual data.
Do you want me to go dig in the mailing list and point you to the
endless discussions?
I assure you, if this is not changed, we will have this discussion
again.
quoted
Most likely the consensus and the proposals will be ignored and nothing
will change as usual, but that's a different thing.
Is it truly necessary to make sniping comments like this at the end of
each email? It _is_ being discussed right now, and these comments do
nothing except irritate your readers. Please stop.
And it has been discussed before. If history is any indication, it will
be discussed again.
--
Felipe Contreras
From: David Kastrup <hidden> Date: 2016-06-15 23:00:59
Jeff King [off-list ref] writes:
On Fri, May 02, 2014 at 02:11:05PM -0500, Felipe Contreras wrote:
quoted
Junio C Hamano wrote:
quoted
If we step back a bit, because we are forcing him to differentiate
these two pulls in his mental model anyway, perhaps it may help
people (both new and old) if we had a new command to make the
distinction stand out more. What if the command sequence were like
this instead?
$ git checkout maint
$ git update [ origin maint ]
$ git pull [--no-ff] developer-remote topic-branch
$ git push [ origin maint ]
where the new command 'update' enforces the '--ff-only' update. And
then we would stop telling "'git pull' first" when a push does not
fast-forward.
In addition to barf when it's not a fast-forward, such command can
switch the parents, so it appears 'maint' was merged to 'origin/maint'.
Many people have complained about this order.
I realize this has veered off into talking about an "update" command,
and not necessarily "pull", but since there a lot of proposals floating
around, I wanted to make one point: if we are going to do such a switch,
let's please make it something the user explicitly turns on.
A safety catch defaulting to a factory position of "off" is not going to
stop inexperienced people from shooting themselves in the foot.
--
David Kastrup
From: Richard Hansen <hidden> Date: 2016-06-15 23:00:59
On 2014-05-02 14:13, Junio C Hamano wrote:
Stepping back even further, and thinking what is different between
these two pulls, we notice that the first one is pulling from the
place we push back to.
I think the fundamental difference is in the relationship between the
local and the remote branch (which branch derives from the other).
The relationship between the branches determines what the user wants
from 'git pull'.
In my experience 'git pull' is mostly (only?) used for the following
three tasks:
1. update a local branch to incorporate the latest upstream changes
In this case, the local branch (master) is a derivative of the
upstream branch (origin/master). The user wants all of the
commits in the remote branch to be in the local branch. And the
user would like the local changes, if any, to descend from the tip
of the remote branch.
For this case, 'git pull --ff-only' followed by 'git rebase -p'
works well, as does 'git pull --rebase=preserve' if the user is
comfortable rebasing without reviewing the incoming commits first.
A plain 'git pull' or 'git pull --ff' is suboptimal due to the
awkward backwards-parents merge commit.
2. update a published feature branch with the latest changes from its
parent branch
In this case, the local branch (foo) is a derivative of the
upstream branch (origin/foo) which is itself a derivative of
another branch (origin/master). All commits in origin/master
should be in origin/foo, and ideally all commits unique to
origin/foo would descend from the tip of origin/master.
The relationship between origin/foo and origin/master is similar
to the relationship between master and origin/master in case #1
above, but rebase is frowned upon because the feature branch has
been shared with other developers (and the shared repository might
reject non-ff updates).
This case is sort-of like case #1 above (updating) and sort-of
like case #3 below (integrating).
For this case, after the local branch foo is updated (case #1
above), 'git pull --ff origin master' or 'git fetch --all && git
merge --ff origin/master' work well to update origin/foo.
3. integrate a more-or-less complete feature/fix back into the line
of development it forked off of
In this case the local branch is a primary line of development and
the remote branch contains the derivative work. Think Linus
pulling in contributions. Different situations will call for
different ways to handle this case, but most will probably want
some or all of:
* rebase the remote commits onto local HEAD
* merge into local HEAD so that the first parent (if a real merge
and not a ff) is the previous version of the main line of
development and the second parent is the derivative work
* merge --no-ff so that:
- the merge can serve as a cover letter (who reviewed it,
which bug reports were fixed, where the changes came from,
etc.)
- the commits that compose the new topic are grouped together
- the first-parent path represents a series of completed tasks
(I prefer to do all three, although I may skip the rebase if the
commits came from another public repository so as to not annoy
users of that downstream repository.)
For this case, 'git pull --no-ff' is better than 'git pull --ff'
(for the reasons listed above), but perhaps something more
elaborate would be ideal (e.g., rebase there onto here, then merge
--no-ff).
These three usage patterns are at odds; it's hard to change the
default behavior of 'git pull' to favor one usage case without harming
another. Perhaps this is why there's so much disagreement about what
'git pull' should do.
I see a few ways to improve the situation:
1. Add one or two new commands to split up how the three cases are
handled. For example:
* Add a new 'git update' command that is friendly for case
#1. Update tutorials to recommend 'git update' instead of
'git pull'. It would behave like 'git pull --ff-only' by
default.
It could behave like 'git pull --rebase[=preserve]' instead,
but this has a few downsides:
- It doesn't give the user an opportunity to review the
incoming commits before rebasing (e.g., to see what sort of
conflicts to expect).
- It subjects new users to that scary rebase thing before
they are prepared to handle it.
- The branch to be updated must be checked out. If 'git
update' used --ff-only, then 'git update --all' could
fast-forward all local branches to their configured
upstreams when possible. (How cool would that be?)
* Leave 'git pull' and 'git pull $remote [$refspec]' alone --
the current defaults are acceptable (though maybe not ideal)
for cases #2 and #3.
Another example:
* Add a new 'git integrate' command to handle case #3. Ideally
it would be configurable enough to work with various
workflows. It would behave like 'git pull --no-ff' by
default.
* Change plain 'git pull' to assume case #1 and default to
--ff-only. It could default to --rebase[=preserve] instead,
but that has the same downsides as those listed for 'git
update' above.
* Have 'git pull $remote [$refspec]' also default to merge
--ff-only. It could assume case #2 and default to --ff, but
that would cause 'git pull' to have different behaviors
depending on how it is invoked. That might be too confusing
to users. If 'git pull origin master' errors out due to
non-ff, it's easy enough for users to manually run 'git merge
origin/master'. Alternatively users could use 'git integrate
origin master', so long as it does not rebase by default.
Thus, I don't think that defaulting to --ff-only (when the
remote is specified) would be a huge loss.
2. Teach 'git pull' to have different defaults depending on how it
is invoked:
* If plain 'git pull', assume case #1 above and default to merge
--ff-only.
* If 'git pull $configured_remote_name [$refspec]', assume case
#2 and default to merge --ff.
* If 'git pull $url [$refspec]', assume case #3 and default to
merge --no-ff.
I'm not a fan of this approach -- it seems like it would be a
huge source of confusion for users.
3. Add some branch metadata to automatically figure out branch
relationships, then adjust the default behavior of 'git pull'
according to that metadata. This seems like a complicated and
disruptive change, but it could have other benefits.
Of these options, I prefer adding a new 'git integrate' command and
changing 'git pull' (and 'git pull $remote [$refspec]') to default to
--ff-only.
-Richard
From: Felipe Contreras <hidden> Date: 2016-06-15 23:00:59
Richard Hansen wrote:
I think the fundamental difference is in the relationship between the
local and the remote branch (which branch derives from the other).
The relationship between the branches determines what the user wants
from 'git pull'.
In my experience 'git pull' is mostly (only?) used for the following
three tasks:
I agree.
1. update a local branch to incorporate the latest upstream changes
In this case, the local branch (master) is a derivative of the
upstream branch (origin/master). The user wants all of the
commits in the remote branch to be in the local branch. And the
user would like the local changes, if any, to descend from the tip
of the remote branch.
My current propsal of making `git pull` by default do --ff-only would
solve this. In addition I think by default 'master' should be merged to
'origin/master', if say --merge is given.
For this case, 'git pull --ff-only' followed by 'git rebase -p'
works well, as does 'git pull --rebase=preserve' if the user is
comfortable rebasing without reviewing the incoming commits first.
I suppose you mean a `git rebase -p` if the `git pull --ff-only` failed.
This might be OK on most projects, but not all.
What happens after a `git pull --ff-only` fails should be totally
up to the user.
2. update a published feature branch with the latest changes from its
parent branch
In this case, the local branch (foo) is a derivative of the
upstream branch (origin/foo) which is itself a derivative of
another branch (origin/master). All commits in origin/master
should be in origin/foo, and ideally all commits unique to
origin/foo would descend from the tip of origin/master.
I don't understand why are you tainting the example with 'origin/foo',
'foo' and 'origin/master' are enough for this example. In fact, the
mention of 'origin/master' made it wrong: after the pull not all the
commits of origin/master would be in origin/foo, you need a push for
that. We have enough in our plate to taint this with yet another branch
and push.
For this case `git pull origin master` already work correctly for most
projects. We probably shouldn't change that.
3. integrate a more-or-less complete feature/fix back into the line
of development it forked off of
In this case the local branch is a primary line of development and
the remote branch contains the derivative work. Think Linus
pulling in contributions. Different situations will call for
different ways to handle this case, but most will probably want
some or all of:
* rebase the remote commits onto local HEAD
No. Most people will merge the remote branch as it is. There's no reason
to rebase, specially if you are creating a merge commit.
* merge into local HEAD so that the first parent (if a real merge
and not a ff) is the previous version of the main line of
development and the second parent is the derivative work
* merge --no-ff so that:
- the merge can serve as a cover letter (who reviewed it,
which bug reports were fixed, where the changes came from,
etc.)
- the commits that compose the new topic are grouped together
- the first-parent path represents a series of completed tasks
It is very rare that an integrator is even able to do a fast-forward
merge anyway. So being explicit about --no-ff might better, but it would
hardly make a difference. Either way, a good integrator would configure
pull.ff = false.
I'd say `git pull origin master` already works fine for this case.
--
Felipe Contreras
From: John Szakmeister <hidden> Date: 2016-06-15 23:00:59
On Fri, May 2, 2014 at 2:13 PM, Junio C Hamano [off-list ref] wrote:
[snip]
Your earlier long-hand, together with the two examples that pulls
into the same "maint" branch Brian gave us, may give us a better
starting points to think about a saner way.
To me, the problem sounds like:
Tutorials of Git often says "use 'git pull' to catch up your
branch with your upstream work and then 'git push' back" (and
worse yet, 'git push' that does not fast-forward suggests doing
so), but 'git pull' that creates a merge in a wrong direction is
not the right thing for many people.
Yes, that's a good portion of the problem.
And proposed solutions range from "let's write 'pull' off as a
failed experiment" to "let's forbid any merge made by use of 'pull'
by default, because it is likely that merge may be in reverse".
FWIW, at my company, we took another approach. We introduced a `git
ffwd` command that fetches from all remotes, and fast-forwards all
your local branches that are tracking a remote, and everyone on the
team uses it all the time. It should be said this team also likes to
use Git bare-metal, because they like knowing how things work
out-of-the-box. But they all use the command because it's so
convenient.
I had started making a C version a while back, but never completed it.
I could take a stab at doing so again, if there's interest.
-John
From: Richard Hansen <hidden> Date: 2016-06-15 23:01:00
On 2014-05-03 05:26, Felipe Contreras wrote:
Richard Hansen wrote:
quoted
I think the fundamental difference is in the relationship between the
local and the remote branch (which branch derives from the other).
The relationship between the branches determines what the user wants
from 'git pull'.
In my experience 'git pull' is mostly (only?) used for the following
three tasks:
I agree.
quoted
1. update a local branch to incorporate the latest upstream changes
In this case, the local branch (master) is a derivative of the
upstream branch (origin/master). The user wants all of the
commits in the remote branch to be in the local branch. And the
user would like the local changes, if any, to descend from the tip
of the remote branch.
My current propsal of making `git pull` by default do --ff-only would
solve this.
It would go a long way toward improving the situation, yes.
In addition I think by default 'master' should be merged to
'origin/master', if say --merge is given.
This would break cases #2 and #3. (With cases #2 and #3 you want the
fetched branch to be the second parent, not the first.)
Or are you proposing that pull --merge should reverse the parents if and
only if the remote ref is @{u}?
quoted
For this case, 'git pull --ff-only' followed by 'git rebase -p'
works well, as does 'git pull --rebase=preserve' if the user is
comfortable rebasing without reviewing the incoming commits first.
I suppose you mean a `git rebase -p` if the `git pull --ff-only` failed.
Yes.
This might be OK on most projects, but not all.
The rebase only affects the local repository (the commits haven't been
pushed yet or else they'd be in @{u} already), so I'd say it's more of
an individual developer decision than a project decision.
In my opinion rebase would be the best option here, but if the project
is OK with developers pushing merge or merge-there commits and the
developer isn't yet comfortable with rebasing, then merge is also an
acceptable option.
What happens after a `git pull --ff-only` fails should be totally
up to the user.
I tend to agree, mostly because I want users to have an opportunity to
review incoming commits before action is taken. Also, though rebasing
would yield the nicest history, some users aren't yet comfortable with
rebase. If a project is OK with silly little merge commits from users
that aren't comfortable with rebase, then I don't want to force everyone
to rebase by default.
As an added bonus: Defaulting to --ff-only makes it possible for 'git
pull --all' to fast-forward every local branch to their configured
upstream, not just the currently checked-out branch. I think this would
be a huge usability win.
quoted
2. update a published feature branch with the latest changes from its
parent branch
In this case, the local branch (foo) is a derivative of the
upstream branch (origin/foo) which is itself a derivative of
another branch (origin/master). All commits in origin/master
should be in origin/foo, and ideally all commits unique to
origin/foo would descend from the tip of origin/master.
I don't understand why are you tainting the example with 'origin/foo',
Originally I didn't have this case in my list, but I added it after
thinking about Peff's comment:
On 2014-05-02 17:48, Jeff King wrote:
> One common workflow for GitHub users is to back-merge master into a
> topic, because they want the final "integrated" version on the topic
> branch.
This almost but doesn't quite fit neatly into the other two cases. It's
not case #1 because the shared nature of origin/foo means that rebasing
origin/foo onto origin/master is usually bad instead of usually good.
It's not case #3 because rebasing origin/master commits onto origin/foo
(assuming that the user would usually want to rebase the topic branch
when integrating) would definitely be bad.
'foo' and 'origin/master' are enough for this example. In fact, the
mention of 'origin/master' made it wrong: after the pull not all the
commits of origin/master would be in origin/foo, you need a push for
that.
The push of foo to origin/foo was meant to be implied.
We have enough in our plate to taint this with yet another branch
and push.
For this case `git pull origin master` already work correctly for most
projects.
Yes, it does.
We probably shouldn't change that.
If we change 'git pull' to default to --ff-only but let 'git pull
$remote [$refspec]' continue to default to --ff then we have two
different behaviors depending on how 'git pull' is invoked. I'm worried
that this would trip up users. I'm not convinced that having two
different behaviors would be bad, but I'm not convinced that it would be
good either.
quoted
3. integrate a more-or-less complete feature/fix back into the line
of development it forked off of
In this case the local branch is a primary line of development and
the remote branch contains the derivative work. Think Linus
pulling in contributions. Different situations will call for
different ways to handle this case, but most will probably want
some or all of:
* rebase the remote commits onto local HEAD
No. Most people will merge the remote branch as it is. There's no reason
to rebase, specially if you are creating a merge commit.
I disagree. I prefer to rebase a topic branch before merging (no-ff) to
the main line of development for a couple of reasons:
* It makes commits easier to review. For example, assume the
following commit history:
* merge topic-foo
|\
| * merge master into topic-foo
|/|
* | tweak the behavior of Thing
| |
| * refactor Thing
| |
| * wrap long lines; no behavior changes
|/
* blah
|
...
In this case, the impact the "refactor Thing" and "wrap long lines"
commits have on master can't be fully understood without also
examining the presumed merge conflict resolution in the "merge
master into topic-foo" commit. Merge commits are very hard to
review, even if (especially if?) there are no conflicts.
Developers can diff 'merge topic-foo' to its first parent, but then
they'll see lots of noise caused by the "wrap long lines" commit.
If the integrator rebases first, then the history looks like this:
* merge topic-foo
|\
| * refactor Thing
| |
| * wrap long lines; no behavior changes
|/
* tweak the behavior of Thing
|
* blah
|
...
Now the merge conflict resolution is integrated into the "refactor
Thing" and "wrap long lines" commits, making them easier to review.
* Rebasing makes the commit history pretty and easier to understand.
Instead of this:
* merge feature.xyz
|\
| * xyz part 3/3
| |
| * merge master into feature.xyz
|/|
* | merge feature.foo
|\ \
| | * xyz part 2/3
| * | foo part 2/2
| * | foo part 1/2
| | * xyz part 1/3
|/ /
| /
|/
* merge feature.bar
|\
you get this:
* merge feature.xyz
|\
| * xyz part 3/3
| * xyz part 2/3
| * xyz part 1/3
|/
* merge feature.foo
|\
| * foo part 2/2
| * foo part 1/2
|/
* merge feature.bar
|\
When there are regularly dozens of active branches at a time, this
improved readability can be quite valuable.
quoted
* merge into local HEAD so that the first parent (if a real merge
and not a ff) is the previous version of the main line of
development and the second parent is the derivative work
* merge --no-ff so that:
- the merge can serve as a cover letter (who reviewed it,
which bug reports were fixed, where the changes came from,
etc.)
- the commits that compose the new topic are grouped together
- the first-parent path represents a series of completed tasks
It is very rare that an integrator is even able to do a fast-forward
merge anyway.
It depends on the level of project activity. A project as active as the
Linux kernel or Git will almost never have fast-forwards. But
occasional contributions by random users to a small, simple project will
likely be fast-forwards.
So being explicit about --no-ff might better, but it would
hardly make a difference. Either way, a good integrator would configure
pull.ff = false.
Configuring pull.ff = false is OK if the integrator only integrates and
only uses one machine. But if the integrator also wants to develop in
the same repository, or if the integrator uses multiple machines to do
the integration work (e.g., office desktop and laptop), then setting
pull.ff may be less convenient, not more.
Maybe it's OK to require integrators to get in the habit of typing 'git
pull --no-ff'. Presumably integrators are experienced Git users, so
they can create their own 'git integrate' alias if they don't want to
have to remember to type '--no-ff' all the time.
I'd say `git pull origin master` already works fine for this case.
It does, but again preserving the current behavior would cause the
behavior of 'git pull origin master' to be inconsistent with the
proposed ff-only default for a plain 'git pull'.
-Richard
From: Felipe Contreras <hidden> Date: 2016-06-15 23:01:00
Richard Hansen wrote:
On 2014-05-03 05:26, Felipe Contreras wrote:
quoted
Richard Hansen wrote:
quoted
I think the fundamental difference is in the relationship between the
local and the remote branch (which branch derives from the other).
The relationship between the branches determines what the user wants
from 'git pull'.
In my experience 'git pull' is mostly (only?) used for the following
three tasks:
I agree.
quoted
1. update a local branch to incorporate the latest upstream changes
In this case, the local branch (master) is a derivative of the
upstream branch (origin/master). The user wants all of the
commits in the remote branch to be in the local branch. And the
user would like the local changes, if any, to descend from the tip
of the remote branch.
My current propsal of making `git pull` by default do --ff-only would
solve this.
It would go a long way toward improving the situation, yes.
quoted
In addition I think by default 'master' should be merged to
'origin/master', if say --merge is given.
This would break cases #2 and #3. (With cases #2 and #3 you want the
fetched branch to be the second parent, not the first.)
Or are you proposing that pull --merge should reverse the parents if and
only if the remote ref is @{u}?
Only if no remote or branch are specified `git pull --merge`.
quoted
quoted
For this case, 'git pull --ff-only' followed by 'git rebase -p'
works well, as does 'git pull --rebase=preserve' if the user is
comfortable rebasing without reviewing the incoming commits first.
I suppose you mean a `git rebase -p` if the `git pull --ff-only` failed.
Yes.
quoted
This might be OK on most projects, but not all.
The rebase only affects the local repository (the commits haven't been
pushed yet or else they'd be in @{u} already), so I'd say it's more of
an individual developer decision than a project decision.
In my opinion rebase would be the best option here, but if the project
is OK with developers pushing merge or merge-there commits and the
developer isn't yet comfortable with rebasing, then merge is also an
acceptable option.
Precisely for that reason.
quoted
quoted
2. update a published feature branch with the latest changes from its
parent branch
quoted
We probably shouldn't change that.
If we change 'git pull' to default to --ff-only but let 'git pull
$remote [$refspec]' continue to default to --ff then we have two
different behaviors depending on how 'git pull' is invoked. I'm worried
that this would trip up users. I'm not convinced that having two
different behaviors would be bad, but I'm not convinced that it would be
good either.
It is the only solution that has been proposed.
Moreover, while it's a bit worrisome, it wouldn't create any actual
problems. Since `git pull $what` remains the same, there's no problems
there. The only change would be on `git pull`.
Since most users are not going to do `git pull $what` therefore it would
only be a small subset of users that would notice the discrepancy
between running with $what, or not. And the only discrepancy they could
notice is that when they run `git pull $what` they expect it to be
--ff-only, or when the run `git pull` they don't. Only the former could
be an issue, but even then, it's highly unlikely that `git pull $what`
would ever be a fast-forward.
So althought conceptually it doesn't look clean, in reality there
wouldn't be any problems.
quoted
quoted
3. integrate a more-or-less complete feature/fix back into the line
of development it forked off of
In this case the local branch is a primary line of development and
the remote branch contains the derivative work. Think Linus
pulling in contributions. Different situations will call for
different ways to handle this case, but most will probably want
some or all of:
* rebase the remote commits onto local HEAD
No. Most people will merge the remote branch as it is. There's no reason
to rebase, specially if you are creating a merge commit.
I disagree. I prefer to rebase a topic branch before merging (no-ff) to
the main line of development for a couple of reasons:
Well that is *your* preference. Most people would prefer to preserve the
history.
* It makes commits easier to review.
The review in the vast majority of cases happens *before* the
integration.
And the problem comes when the integrator makes a mistake, which they
inevitable do (we all do), then there's no history about how the
conflict was resolved, and what whas the original patch.
That's why most people don't do this.
* Rebasing makes the commit history pretty and easier to understand.
It is more important to be able to track integration errors than to have
a pretty history. That is for most people.
I like to have a pretty history for my own local branches, but once
something gets integrated it's important to see who did exactly what
(the integrator did the merge).
quoted
It is very rare that an integrator is even able to do a fast-forward
merge anyway.
It depends on the level of project activity. A project as active as the
Linux kernel or Git will almost never have fast-forwards. But
occasional contributions by random users to a small, simple project will
likely be fast-forwards.
And small simple projects don't care about such issues.
quoted
So being explicit about --no-ff might better, but it would
hardly make a difference. Either way, a good integrator would configure
pull.ff = false.
Configuring pull.ff = false is OK if the integrator only integrates and
only uses one machine. But if the integrator also wants to develop in
the same repository, or if the integrator uses multiple machines to do
the integration work (e.g., office desktop and laptop), then setting
pull.ff may be less convenient, not more.
Any good integrator would find solutions for those problems easily.
Either way I don't see any proposed solutions.
quoted
I'd say `git pull origin master` already works fine for this case.
It does, but again preserving the current behavior would cause the
behavior of 'git pull origin master' to be inconsistent with the
proposed ff-only default for a plain 'git pull'.
Yes, it doesn't look clean. But I don't see any proposed alternatives.
--
Felipe Contreras
From: Richard Hansen <hidden> Date: 2016-06-15 23:01:00
On 2014-05-03 23:08, Felipe Contreras wrote:
Richard Hansen wrote:
quoted
Or are you proposing that pull --merge should reverse the parents if and
only if the remote ref is @{u}?
Only if no remote or branch are specified `git pull --merge`.
OK. Let me summarize to make sure I understand your full proposal:
1. if plain 'git pull', default to --ff-only
2. if 'git pull --merge', default to --ff. If the local branch can't
be fast-forwarded to the upstream branch, then create a merge
commit where the local branch is the *second* parent, not the first
3. if 'git pull $remote [$refspec]', default to --merge --ff. If the
local branch can't be fast-forwarded to the remote branch, then
create a merge commit where the remote branch is the second parent
(the current behavior)
Is that accurate?
quoted
If we change 'git pull' to default to --ff-only but let 'git pull
$remote [$refspec]' continue to default to --ff then we have two
different behaviors depending on how 'git pull' is invoked. I'm worried
that this would trip up users. I'm not convinced that having two
different behaviors would be bad, but I'm not convinced that it would be
good either.
It is the only solution that has been proposed.
It's not the only proposal -- I proposed a few alternatives in my
earlier email (though not in the form of code), and others have too. In
particular:
* create a new 'git integrate' command/alias that behaves like 'git
pull --no-ff'
* change 'git pull' and 'git pull $remote [$refspec]' to do --ff-only
by default
Another option that I just thought of: Instead of your proposed
pull.mode and branch.<name>.pullmode, add the following two sets of configs:
* pull.updateMode, branch.<name>.pullUpdateMode:
The default mode to use when running 'git pull' without naming a
remote repository or when the named remote branch is @{u}. Valid
options: ff-only (default), merge-ff, merge-ff-there, merge-no-ff,
merge-no-ff-there, rebase, rebase-here, rebase-here-then-merge-no-ff
* pull.integrateMode, branch.<name>.pullIntegrateMode:
The default mode to use when running 'git pull $remote [$refspec]'
when '$remote [$refspec]' is not @{u}. Valid options are the same
as those for pull.updateMode. Default is merge-ff.
This gives the default split behavior as you propose, but the user can
reconfigure to suit personal preference (and we can easily change the
default for one or the other if there's too much outcry).
Moreover, while it's a bit worrisome, it wouldn't create any actual
problems. Since `git pull $what` remains the same, there's no problems
there. The only change would be on `git pull`.
Since most users are not going to do `git pull $what` therefore it would
only be a small subset of users that would notice the discrepancy
between running with $what, or not. And the only discrepancy they could
notice is that when they run `git pull $what` they expect it to be
--ff-only, or when the run `git pull` they don't. Only the former could
be an issue, but even then, it's highly unlikely that `git pull $what`
would ever be a fast-forward.
So althought conceptually it doesn't look clean, in reality there
wouldn't be any problems.
Yes, it might not be a problem, but I'm still nervous. I'd need more
input (e.g., user survey, broad mailing list consensus, long beta test
period, decree by a benevolent dictator) before I'd be comfortable with it.
quoted
quoted
quoted
3. integrate a more-or-less complete feature/fix back into the line
of development it forked off of
In this case the local branch is a primary line of development and
the remote branch contains the derivative work. Think Linus
pulling in contributions. Different situations will call for
different ways to handle this case, but most will probably want
some or all of:
* rebase the remote commits onto local HEAD
No. Most people will merge the remote branch as it is. There's no reason
to rebase, specially if you are creating a merge commit.
I disagree. I prefer to rebase a topic branch before merging (no-ff) to
the main line of development for a couple of reasons:
Well that is *your* preference. Most people would prefer to preserve the
history.
Probably. My point is that the behavior should be configurable, and I'd
like that particular behavior to be one of the options (but not the
default -- that wouldn't be appropriate).
quoted
* It makes commits easier to review.
The review in the vast majority of cases happens *before* the
integration.
True, although even when review happens before integration there is
value in making code archeology easier.
And the problem comes when the integrator makes a mistake, which they
inevitable do (we all do), then there's no history about how the
conflict was resolved, and what whas the original patch.
Good point, although if I was the integrator and there was a
particularly hairy conflict I'd still rebase but ask the original
contributor to review the results before merging (or ask the contributor
to rebase).
-Richard
From: Felipe Contreras <hidden> Date: 2016-06-15 23:01:00
Richard Hansen wrote:
On 2014-05-03 23:08, Felipe Contreras wrote:
quoted
Richard Hansen wrote:
quoted
Or are you proposing that pull --merge should reverse the parents if and
only if the remote ref is @{u}?
Only if no remote or branch are specified `git pull --merge`.
OK. Let me summarize to make sure I understand your full proposal:
1. if plain 'git pull', default to --ff-only
2. if 'git pull --merge', default to --ff. If the local branch can't
be fast-forwarded to the upstream branch, then create a merge
commit where the local branch is the *second* parent, not the first
3. if 'git pull $remote [$refspec]', default to --merge --ff. If the
local branch can't be fast-forwarded to the remote branch, then
create a merge commit where the remote branch is the second parent
(the current behavior)
Is that accurate?
Yes, that is accurate. Note that 3. is the current behavior.
quoted
quoted
If we change 'git pull' to default to --ff-only but let 'git pull
$remote [$refspec]' continue to default to --ff then we have two
different behaviors depending on how 'git pull' is invoked. I'm worried
that this would trip up users. I'm not convinced that having two
different behaviors would be bad, but I'm not convinced that it would be
good either.
It is the only solution that has been proposed.
It's not the only proposal -- I proposed a few alternatives in my
earlier email (though not in the form of code), and others have too. In
particular:
* create a new 'git integrate' command/alias that behaves like 'git
pull --no-ff'
Yeah but that's for a different issue altogheter. I doesn't solve the
problems in 1. nor 2. nor 3.
* change 'git pull' and 'git pull $remote [$refspec]' to do --ff-only
by default
Another option that I just thought of: Instead of your proposed
pull.mode and branch.<name>.pullmode, add the following two sets of configs:
* pull.updateMode, branch.<name>.pullUpdateMode:
The default mode to use when running 'git pull' without naming a
remote repository or when the named remote branch is @{u}. Valid
options: ff-only (default), merge-ff, merge-ff-there, merge-no-ff,
merge-no-ff-there, rebase, rebase-here, rebase-here-then-merge-no-ff
Those are way too many options to be able to sensibly explain them.
* pull.integrateMode, branch.<name>.pullIntegrateMode:
The default mode to use when running 'git pull $remote [$refspec]'
when '$remote [$refspec]' is not @{u}. Valid options are the same
as those for pull.updateMode. Default is merge-ff.
This gives the default split behavior as you propose, but the user can
reconfigure to suit personal preference (and we can easily change the
default for one or the other if there's too much outcry).
If we reduce the number of options to begin with (more can be added
later), then it might make sense to have these two options.
However, that doesn't change the proposal you described above (1. 2.
3.).
quoted
Moreover, while it's a bit worrisome, it wouldn't create any actual
problems. Since `git pull $what` remains the same, there's no problems
there. The only change would be on `git pull`.
Since most users are not going to do `git pull $what` therefore it would
only be a small subset of users that would notice the discrepancy
between running with $what, or not. And the only discrepancy they could
notice is that when they run `git pull $what` they expect it to be
--ff-only, or when the run `git pull` they don't. Only the former could
be an issue, but even then, it's highly unlikely that `git pull $what`
would ever be a fast-forward.
So althought conceptually it doesn't look clean, in reality there
wouldn't be any problems.
Yes, it might not be a problem, but I'm still nervous. I'd need more
input (e.g., user survey, broad mailing list consensus, long beta test
period, decree by a benevolent dictator) before I'd be comfortable with it.
The user surveys are not happening any more. The results were ignored by
the developers anyway.
Mailing list consensus might be possible, but that wouldn't tell us
much.
There's something we can do, and let me clarify my proposal. What you
described above is what I think should happen eventually, however, we
can start by doing something like what my patch series is doing; issue a
warning that the merge is not fast-forward and things might change in
the future.
If people find this behavior confusing they will complain in the mailing
list. Although I suspect it would be for other reasons, not the 'git
pull'/'git pull $there' division. Either way we would see in the
discussion.
quoted
quoted
quoted
quoted
3. integrate a more-or-less complete feature/fix back into the line
of development it forked off of
In this case the local branch is a primary line of development and
the remote branch contains the derivative work. Think Linus
pulling in contributions. Different situations will call for
different ways to handle this case, but most will probably want
some or all of:
* rebase the remote commits onto local HEAD
No. Most people will merge the remote branch as it is. There's no reason
to rebase, specially if you are creating a merge commit.
I disagree. I prefer to rebase a topic branch before merging (no-ff) to
the main line of development for a couple of reasons:
Well that is *your* preference. Most people would prefer to preserve the
history.
Probably. My point is that the behavior should be configurable, and I'd
like that particular behavior to be one of the options (but not the
default -- that wouldn't be appropriate).
All right. But I'm a bit overwhelmed by all the things to keep in mind.
Does your proposed IntegradeMode/UpdateMode deal with this?
I will try to gather a bunch of discussions and create a new thread to
summrize what is probably the best, and Intage/Update mode is as far as
I'm willing to go into considering options.
quoted
quoted
* It makes commits easier to review.
The review in the vast majority of cases happens *before* the
integration.
True, although even when review happens before integration there is
value in making code archeology easier.
I think I explained below why "code archeology" is better served by
preserving the history.
quoted
And the problem comes when the integrator makes a mistake, which they
inevitable do (we all do), then there's no history about how the
conflict was resolved, and what whas the original patch.
Good point, although if I was the integrator and there was a
particularly hairy conflict I'd still rebase but ask the original
contributor to review the results before merging (or ask the contributor
to rebase).
Sure, asking the contributor to rebase is best. However, sending the
rebase results is not that useful; the contributor would like to see
what actually changed so an interdiff might be more than enough. But
then that's basically the same as reviewing the merge commit.
Anyway, I'll try to grab what I can from previous discussions (mainly
about switching the merge parents) and create a new thread with a
summary.
Cheers.
--
Felipe Contreras
From: Richard Hansen <hidden> Date: 2016-06-15 23:01:00
On 2014-05-04 06:17, Felipe Contreras wrote:
Richard Hansen wrote:
quoted
On 2014-05-03 23:08, Felipe Contreras wrote:
quoted
It is the only solution that has been proposed.
It's not the only proposal -- I proposed a few alternatives in my
earlier email (though not in the form of code), and others have too. In
particular:
* create a new 'git integrate' command/alias that behaves like 'git
pull --no-ff'
Yeah but that's for a different issue altogheter. I doesn't solve the
problems in 1. nor 2. nor 3.
'git integrate' would handle usage cases #2 (update a published branch
to its "parent" branch) and #3 (integrate a completed task into the main
line of development), making it feasible to change 'git pull' and 'git
pull $remote [$refspec]' to default to --ff-only to handle usage case #1
(update local branch to @{u}).
quoted
* change 'git pull' and 'git pull $remote [$refspec]' to do --ff-only
by default
Another option that I just thought of: Instead of your proposed
pull.mode and branch.<name>.pullmode, add the following two sets of configs:
* pull.updateMode, branch.<name>.pullUpdateMode:
The default mode to use when running 'git pull' without naming a
remote repository or when the named remote branch is @{u}. Valid
options: ff-only (default), merge-ff, merge-ff-there, merge-no-ff,
merge-no-ff-there, rebase, rebase-here, rebase-here-then-merge-no-ff
Those are way too many options to be able to sensibly explain them.
Certainly this is too many options for a first patch series, but I don't
think they're unexplainable. (I listed a bunch of options because I was
trying to envision where this might take us in the long run.)
For the first patch series, I'd expect: merge (which uses the merge.ff
option to determine whether to ff, ff-only, or no-ff), rebase, and ff-only.
Later ff-only would be made the default.
Later some or all of the other options would be added depending on user
interest.
quoted
* pull.integrateMode, branch.<name>.pullIntegrateMode:
The default mode to use when running 'git pull $remote [$refspec]'
when '$remote [$refspec]' is not @{u}. Valid options are the same
as those for pull.updateMode. Default is merge-ff.
This gives the default split behavior as you propose, but the user can
reconfigure to suit personal preference (and we can easily change the
default for one or the other if there's too much outcry).
If we reduce the number of options to begin with (more can be added
later),
yup
then it might make sense to have these two options.
However, that doesn't change the proposal you described above (1. 2.
3.).
Not sure what you mean. I oulined three usage cases:
#1 update local branch to @{u}
#2 update a published branch to its "parent" branch
#3 integrate a completed task into the main line of development
Having these two sets of options (updateMode and integrateMode) would
make it possible to configure plain 'git pull' to handle usage case #1
and 'git pull $remote [$refspec]' to handle usage cases #2 and #3.
Or the user could configure 'git pull' and 'git pull $remote [$refspec]'
to behave the same, in case they find the different behaviors to be too
confusing.
There's something we can do, and let me clarify my proposal. What you
described above is what I think should happen eventually, however, we
can start by doing something like what my patch series is doing; issue a
warning that the merge is not fast-forward and things might change in
the future.
OK, let me rephrase to make sure I understand:
1. leave the default behavior as-is for now (merge with local
branch the first parent)
2. add --merge argument
3. add ff-only setting
4. plan to eventually change the plain 'git pull' default to ff-only,
but don't change the default yet
5. add a warning if the plain 'git pull' is a non-ff
6. wait and see how users react. If they're OK with it, switch the
default of the plain 'git pull' to ff-only.
Is that accurate? If so, sounds OK to me.
If people find this behavior confusing they will complain in the mailing
list.
true
Although I suspect it would be for other reasons, not the 'git
pull'/'git pull $there' division.
probably
Either way we would see in the discussion.
sounds good to me
quoted
quoted
quoted
quoted
quoted
3. integrate a more-or-less complete feature/fix back into the line
of development it forked off of
In this case the local branch is a primary line of development and
the remote branch contains the derivative work. Think Linus
pulling in contributions. Different situations will call for
different ways to handle this case, but most will probably want
some or all of:
* rebase the remote commits onto local HEAD
No. Most people will merge the remote branch as it is. There's no reason
to rebase, specially if you are creating a merge commit.
I disagree. I prefer to rebase a topic branch before merging (no-ff) to
the main line of development for a couple of reasons:
Well that is *your* preference. Most people would prefer to preserve the
history.
Probably. My point is that the behavior should be configurable, and I'd
like that particular behavior to be one of the options (but not the
default -- that wouldn't be appropriate).
All right. But I'm a bit overwhelmed by all the things to keep in mind.
Sure, this would be an option to add later.
Does your proposed IntegradeMode/UpdateMode deal with this?
mode = rebase-here-then-merge-no-ff would do what I described
Anyway, I'll try to grab what I can from previous discussions (mainly
about switching the merge parents) and create a new thread with a
summary.
From: Felipe Contreras <hidden> Date: 2016-06-15 23:01:00
Richard Hansen wrote:
On 2014-05-04 06:17, Felipe Contreras wrote:
quoted
Richard Hansen wrote:
quoted
On 2014-05-03 23:08, Felipe Contreras wrote:
quoted
It is the only solution that has been proposed.
It's not the only proposal -- I proposed a few alternatives in my
earlier email (though not in the form of code), and others have too. In
particular:
* create a new 'git integrate' command/alias that behaves like 'git
pull --no-ff'
Yeah but that's for a different issue altogheter. I doesn't solve the
problems in 1. nor 2. nor 3.
'git integrate' would handle usage cases #2 (update a published branch
to its "parent" branch) and #3 (integrate a completed task into the main
line of development),
But these cases are completely different. One should reverse the
parents, the other one not.
I feel if a new command is to be added, it should be the one that is
introducing the brand new behavior: switching the parents. So it would
be appropriate for 1. and 2.
quoted
quoted
* change 'git pull' and 'git pull $remote [$refspec]' to do --ff-only
by default
Another option that I just thought of: Instead of your proposed
pull.mode and branch.<name>.pullmode, add the following two sets of configs:
* pull.updateMode, branch.<name>.pullUpdateMode:
The default mode to use when running 'git pull' without naming a
remote repository or when the named remote branch is @{u}. Valid
options: ff-only (default), merge-ff, merge-ff-there, merge-no-ff,
merge-no-ff-there, rebase, rebase-here, rebase-here-then-merge-no-ff
Those are way too many options to be able to sensibly explain them.
Certainly this is too many options for a first patch series, but I don't
think they're unexplainable. (I listed a bunch of options because I was
trying to envision where this might take us in the long run.)
Actually I think they are too many for any point in time.
Maybe pull.updateArgs would make more sense.
For the first patch series, I'd expect: merge (which uses the merge.ff
option to determine whether to ff, ff-only, or no-ff), rebase, and ff-only.
Seems sensible.
quoted
then it might make sense to have these two options.
However, that doesn't change the proposal you described above (1. 2.
3.).
Not sure what you mean. I oulined three usage cases:
#1 update local branch to @{u}
#2 update a published branch to its "parent" branch
#3 integrate a completed task into the main line of development
Having these two sets of options (updateMode and integrateMode) would
make it possible to configure plain 'git pull' to handle usage case #1
and 'git pull $remote [$refspec]' to handle usage cases #2 and #3.
Not if by default they are already handled.
quoted
There's something we can do, and let me clarify my proposal. What you
described above is what I think should happen eventually, however, we
can start by doing something like what my patch series is doing; issue a
warning that the merge is not fast-forward and things might change in
the future.
OK, let me rephrase to make sure I understand:
1. leave the default behavior as-is for now (merge with local
branch the first parent)
2. add --merge argument
3. add ff-only setting
4. plan to eventually change the plain 'git pull' default to ff-only,
but don't change the default yet
5. add a warning if the plain 'git pull' is a non-ff
6. wait and see how users react. If they're OK with it, switch the
default of the plain 'git pull' to ff-only.
Is that accurate? If so, sounds OK to me.
That is what my patch series is doing already, basically.
The new warning I'm proposing would be for the split behavior of 'git
merge' and 'git merge $there'. Which is what is worrysome.
mode = rebase-here-then-merge-no-ff would do what I described
I think that mode is way too specific to be useful for most people.
--
Felipe Contreras
From: Richard Hansen <hidden> Date: 2016-06-15 23:01:00
On 2014-05-04 17:13, Felipe Contreras wrote:
Richard Hansen wrote:
quoted
On 2014-05-04 06:17, Felipe Contreras wrote:
quoted
Richard Hansen wrote:
quoted
On 2014-05-03 23:08, Felipe Contreras wrote:
quoted
It is the only solution that has been proposed.
It's not the only proposal -- I proposed a few alternatives in my
earlier email (though not in the form of code), and others have too. In
particular:
* create a new 'git integrate' command/alias that behaves like 'git
pull --no-ff'
Yeah but that's for a different issue altogheter. I doesn't solve the
problems in 1. nor 2. nor 3.
'git integrate' would handle usage cases #2 (update a published branch
to its "parent" branch) and #3 (integrate a completed task into the main
line of development),
But these cases are completely different. One should reverse the
parents, the other one not.
No -- for both #2 and #3 I want the remote branch to be merged into the
local branch.
In the example I gave for use case #2, foo is a local branch with
origin/foo as the configured upstream and origin/foo was forked off of
origin/master. Someone pushed new stuff to origin/master, and the user
wants the new stuff to also be in origin/foo. So the user does this:
git checkout foo
git pull --ff-only # this is use case #1
git pull origin master # this is use case #2
git push
The merge commit created by 'git pull origin master' should have
origin/master as the second parent, not the first.
-Richard
From: Felipe Contreras <hidden> Date: 2016-06-15 23:01:00
Richard Hansen wrote:
On 2014-05-04 17:13, Felipe Contreras wrote:
quoted
Richard Hansen wrote:
quoted
On 2014-05-04 06:17, Felipe Contreras wrote:
quoted
Richard Hansen wrote:
quoted
On 2014-05-03 23:08, Felipe Contreras wrote:
quoted
It is the only solution that has been proposed.
It's not the only proposal -- I proposed a few alternatives in my
earlier email (though not in the form of code), and others have too. In
particular:
* create a new 'git integrate' command/alias that behaves like 'git
pull --no-ff'
Yeah but that's for a different issue altogheter. I doesn't solve the
problems in 1. nor 2. nor 3.
'git integrate' would handle usage cases #2 (update a published branch
to its "parent" branch) and #3 (integrate a completed task into the main
line of development),
But these cases are completely different. One should reverse the
parents, the other one not.
No -- for both #2 and #3 I want the remote branch to be merged into the
local branch.
I didn't mean #2 and #3, I meant (#1) vs. (#2, #3).
--
Felipe Contreras
From: Richard Hansen <hidden> Date: 2016-06-15 23:01:01
On 2014-05-03 06:00, John Szakmeister wrote:
FWIW, at my company, we took another approach. We introduced a `git
ffwd` command that fetches from all remotes, and fast-forwards all
your local branches that are tracking a remote, and everyone on the
team uses it all the time. It should be said this team also likes to
use Git bare-metal, because they like knowing how things work
out-of-the-box. But they all use the command because it's so
convenient.
I also wrote a script to fast-forward all local branches to their
configured upstream refs. I finally got around to uploading it
somewhere public:
https://github.com/richardhansen/git-update-branch
I use it in my 'git up' alias:
git config --global alias.up \
'!git remote update -p; git update-branch -a'
If there's interest I can tweak the style to conform to
Documentation/CodingGuidelines and stick it in contrib/ or something.
-Richard
From: Felipe Contreras <hidden> Date: 2016-06-15 23:01:01
Richard Hansen wrote:
On 2014-05-03 06:00, John Szakmeister wrote:
quoted
FWIW, at my company, we took another approach. We introduced a `git
ffwd` command that fetches from all remotes, and fast-forwards all
your local branches that are tracking a remote, and everyone on the
team uses it all the time. It should be said this team also likes to
use Git bare-metal, because they like knowing how things work
out-of-the-box. But they all use the command because it's so
convenient.
I also wrote a script to fast-forward all local branches to their
configured upstream refs. I finally got around to uploading it
somewhere public:
https://github.com/richardhansen/git-update-branch
I use it in my 'git up' alias:
git config --global alias.up \
'!git remote update -p; git update-branch -a'
If there's interest I can tweak the style to conform to
Documentation/CodingGuidelines and stick it in contrib/ or something.
I think this would fit perfectly in the proposed `git update` command as
an option: `git update --all`.
--
Felipe Contreras
From: Max Kirillov <hidden> Date: 2016-06-15 23:01:03
Hi.
I might be late to this discussion, but here either
something I don't understand or something is missed.
On Sat, May 03, 2014 at 03:56:51AM -0400, Richard Hansen wrote:
In my experience 'git pull' is mostly (only?) used for the following
three tasks:
1. update a local branch to incorporate the latest upstream changes
In this case, the local branch (master) is a
derivative of the upstream branch (origin/master).
The user wants all of the commits in the remote branch
to be in the local branch. And the user would like
the local changes, if any, to descend from the tip of
the remote branch.
For this case, 'git pull --ff-only' followed by 'git
rebase -p' works well, as does 'git pull
--rebase=preserve' if the user is comfortable rebasing
without reviewing the incoming commits first. A plain
'git pull' or 'git pull --ff' is suboptimal due to the
awkward backwards-parents merge commit.
This is actually not a finally defined use case. What kind
of "local changes" user can have ahead of the remote? As
far I understand, there are 3 cases:
1a. Changes that are going to be merged back to the master,
but not yet ready to be there.
This is essentially the same as case 2, but it does not name
the development branch explicitely. Switching parents for
this case is not desirable.
1b. Some truly local changes which never goes anywhere.
For this case the parent order does not matter.
1c. The local changes prepared for integration, but instead
of filing a pull request of otherwise publishing the
branch for integrator, the leaf developer does the
integrator's job and merges it back to master and then
publishing the master.
As far as I understand, this is the only case when somebody
would want the parents to be switched. And this does not
seem to be a good practice, because it's prone to push races
and requires letting everyone to push to master. So maybe
git should not encourage people to do so.
And the name "update", proposed here, does not seem to be
correct. Because what happens is not updating, but merging
feature to master and closing it.
2. update a published feature branch with the latest
changes from its parent branch
3. integrate a more-or-less complete feature/fix back
into the line of development it forked off of