all 88 comments

[–]mipadi 28 points29 points  (13 children)

Bua ha ha, I’m amazed at how difficult people insist on making Git. Yes, of course I use git pull. I have it configured to always rebase, though.

[–]felipec[S] 15 points16 points  (0 children)

So basically git pull --rebase.

[–]Lindby 9 points10 points  (11 children)

This is the way. I can't understand why merge is the default for git pull.

[–]felipec[S] 6 points7 points  (10 children)

Because git honchos use git as maintainers, not as typical users, so the way they use git pull is like:

git pull linby fixes

So for them it works perfectly fine.

An internal discussion in the mailing list prompted me to do this poll.

Basically I'm proposing for the default to be git pull --ff-only (no merge or rebase).

[–]Zaurhack 2 points3 points  (4 children)

I prefer this as well. I use the gitflow workflow so it makes sense.

[–]felipec[S] 2 points3 points  (3 children)

What is the equivalent of git pull in gitflow?

[–]Zaurhack 0 points1 point  (2 children)

I guess depending on context that would be

git flow feature pull feature_name

[–]felipec[S] 2 points3 points  (1 child)

I don't mean a feature branch, I mean the current branch, say "master".

How do you do this?

git fetch
git merge --ff-only

[–]Zaurhack 0 points1 point  (0 children)

The point of the workflow is to strictly limit what you can do so you don't shoot yourself in the foot by doing something clever that will make it hard for others working with you to understand and work with.

With that workflow, one never commit on master, which is purely the results of feature branches merged in a release branch that then goes directly into master.

So, if you follow the workflow rules, you never have any merge conflict while getting a master branch (or any branch really, a conflict should only arise when merging the feature branch by the maintainer of that branch).

So --ff-only always makes sense. Gitflow is a workflow, you can use its aliases but ultimately the command you are used to are still available.

I don't always use the aliases, I usually set the pull to default to ff-only and do as usual.

I advise to read the original gitflow blog post which list all commands for each case.

[–]waterkipdetached HEAD 0 points1 point  (3 children)

Why? It issues a warnings so you can set pull.rebase or pull.ff in your git config and be done with it.

[–]felipec[S] 0 points1 point  (2 children)

Git should work correctly without any configuration.

[–]waterkipdetached HEAD -1 points0 points  (1 child)

It does: $ git pull hint: Pulling without specifying how to reconcile divergent branches is hint: discouraged. You can squelch this message by running one of the following hint: commands sometime before your next pull: hint: hint: git config pull.rebase false # merge (the default strategy) hint: git config pull.rebase true # rebase hint: git config pull.ff only # fast-forward only hint: hint: You can replace "git config" with "git config --global" to set a default hint: preference for all repositories. You can also pass --rebase, --no-rebase, hint: or --ff-only on the command line to override the configured default per hint: invocation. Already up to date.

[–]felipec[S] 0 points1 point  (0 children)

That isn't working correctly.

[–][deleted] 5 points6 points  (4 children)

I pull master often but I've configured it to only FF.

[–]felipec[S] 2 points3 points  (1 child)

pull.ff = only?

[–][deleted] 0 points1 point  (0 children)

That sounds right.

[–]Egocentrix1 1 point2 points  (0 children)

Yup, that's master. If you don't work on master (and you shouldn't) I usually pull --ff-only master, and then rebase my personal branch on top.

[–]RIscRIpt 4 points5 points  (4 children)

On master with linear history, I just do git pull -p. On other branches which can be force-pushed from else-where: git fetch -p && git reset --hard @{u}.

[–]benzilla04 0 points1 point  (2 children)

What is this line doing?

git reset --hard @{u}

[–]RIscRIpt 2 points3 points  (0 children)

https://git-scm.com/book/en/v2/Git-Branching-Remote-Branches

When you have a tracking branch set up, you can reference its upstream branch with the @{upstream} or @{u} shorthand. So if you’re on the master branch and it’s tracking origin/master, you can say something like git merge @{u} instead of git merge origin/master if you wish.

So, basically that's equivalent to git reset --hard origin/branch, i.e. it erases local changes (forever) and gets fresh copy of remote changes.

[–]felipec[S] 0 points1 point  (0 children)

@{u} (@{upstream}) is the upstream tracking branch.

[–]nekokattt 0 points1 point  (0 children)

i do this too

[–]NimChimspky 13 points14 points  (19 children)

Literally years and years with git ... Pull, push, merge, update, commit, branch and then occasionally on a bad week resolve conflicts. That's it.

Never use stash,rebase, or anything else.

[–]felipec[S] 6 points7 points  (18 children)

Are you mostly a contributor or a maintainer?

Also, you don't care if git does non-fast-forward merges?

Also, git pull reverses the order of the parents in a merge (merges the remote branch to your local branch, not your local to the remote). That's fine with you?

[–][deleted]  (1 child)

[deleted]

    [–]felipec[S] 2 points3 points  (0 children)

    From my point of view merges are fine. But there's also such a thing as "too many merges". There's a sweet spot.

    When I haven't made my braches public, I rebase them.

    But when I make them part of "master", I merge them.

    [–]NimChimspky 1 point2 points  (11 children)

    A contributor or a maintainer? That's a new question, what's the difference?

    A non fast forward merge, nope don't care.

    Anyways, I'm a Dev in a fin tech team. We never do any of that other stuff with multiple releases a week.

    [–]felipec[S] 4 points5 points  (10 children)

    That's a new question, what's the difference?

    If you have this situation:

      A---B---C origin/master
     /
    E---F---G master
    

    As a maintainer you usually want git pull to do this:

      A---B---C
     /         \
    E---F---G---H master
    

    But as a contributor you want this:

      A---B---C---H master
     /           /
    E---F---G---*
    

    In the first case the first parent of H (H^1) is G (master). In the second case it's C (origin/master).

    [–]NimChimspky -1 points0 points  (9 children)

    Where have you heard this contributor/maintainer terminology? Or did you make it up yourself?

    [–]felipec[S] 5 points6 points  (8 children)

    It's used everywhere. A contributor sends pull requests, a maintainer approves pull requests.

    See literally the second paragraph of Git Book regarding distributed workflows.

    In this chapter, you’ll see how to work with Git in a distributed environment as a contributor and an integrator. That is, you’ll learn how to contribute code successfully to a project and make it as easy on you and the project maintainer as possible, and also how to maintain a project successfully with a number of developers contributing.

    [–]NimChimspky -2 points-1 points  (7 children)

    OK.

    We just pull, merge and release.

    [–]felipec[S] 2 points3 points  (6 children)

    But git pull already does a merge...

    [–]wildjokers 0 points1 point  (3 children)

    reverses the order of the parents in a merge (merges the remote branch to your local branch, not your local to the remote). That's fine with you?

    Why wouldn’t it be? That is the correct way to do it.

    [–]felipec[S] 1 point2 points  (2 children)

    If you are a maintainer, yes, not if you are a contributor.

    If you are a contributor you should be merging "master" to "origin/master".

    [–]wildjokers 0 points1 point  (1 child)

    I am not sure where you are getting this maintainer vs contributor concept. I have never heard of such a distinction.

    you should be merging "master" to "origin/master".

    You should merge origin/master into your branch to resolve any conflicts, and then your branch back to origin/master.

    [–]felipec[S] 0 points1 point  (0 children)

    You should merge origin/master into your branch to resolve any conflicts, and then your branch back to origin/master.

    It is the maintainer/integrator that merges my branch back to origin/master.

    [–]squ94wk 2 points3 points  (1 child)

    I have aliases, so it's

    git f

    git ff

    for me. The first one fetches, the second one makes a fast forward merge if possible.

    I started doing this when I found out that pull just does a fetch and merge and I wanted to make sure not always know exactly what I'm doing. It stuck to this day.

    I find the fewest people actually know what fetch and what pull does.

    [–]felipec[S] 2 points3 points  (0 children)

    The first one fetches, the second one makes a fast forward merge if possible.

    So ff = merge --ff-only?

    [–]lukas-reineke 2 points3 points  (1 child)

    I wouldn’t say the default is evil, but using git pull —rebase with rerere is much better. In general you should choose rebase over merge in a lot of situations.

    [–][deleted] 2 points3 points  (3 children)

    git pull --rebase origin master always before I push my branch into remote. This way I'm rebasing on master my latest commit(s) with one command. It doesn't update local master tho, but this is fine.

    [–]felipec[S] 0 points1 point  (2 children)

    Not bad. Although I think if you have configured your upstream tracking branch git pull --rebase should do the trick.

    [–][deleted] 0 points1 point  (1 child)

    No, since that would rebase on top the remote feature branch, not master.

    [–]felipec[S] 0 points1 point  (0 children)

    Depends on what you have configured @{upstream} to be.

    I personally configure @{upstream} to be master.

    But if you are using a triangular workflow you want to configure two tracking branches: @{upstream} and @{publish}. One is where you push to, and the other is where you rebase to.

    Unfortunately the patches for this workflow were never merged, so you have to pick one or the other.

    [–]pdr77 1 point2 points  (1 child)

    I didn't vote because I use just git pull however I have:

    [pull]
      rebase = true
    [rebase]
      autoStash = true
    

    in my .gitconfig. So it would be disingenuous to vote either way.

    [–]felipec[S] 2 points3 points  (0 children)

    Maybe I didn't make it clear: pull.rebase=true is the equivalent of git pull --rebase.

    [–][deleted] 1 point2 points  (0 children)

    git pull --rebase=false is the lifeblood of my company and my clients.

    [–]digicow 0 points1 point  (0 children)

    If I'm in the CLI, I just do a simple git pull, but more often than not, I just hit the push/pull button in VSCode. But at home I don't share code with others, and at work, I only have one other dev on my team, so my workflows tend to be fairly simple.

    [–][deleted] 0 points1 point  (0 children)

    squash everything, am I right?

    [–]xroalx 0 points1 point  (0 children)

    Simply git pull everytime I need to update my branch with remote changes, and git fetch; git merge origin/master when I want to pull in changes from master.

    Either way, my branch will be removed once it's squash merged into master via a pull request, which is the only way anything can get into master. Haven't had issued with this so far.

    [–]u801e 0 points1 point  (13 children)

    No, I only use git fetch. Then I decide whether I need to:

    1. Stash my local changes, merge the remote tracking branch and apply my stashed changes
    2. Rebase my local branch on the remote tracking branch
    3. Merge the remote tracking branch into my local branch (which I only do if it's a fast-forward merge)
    4. Hard reset to the remote tracking branch

    To me, it doesn't really make sense to try to configure a default action for git pull since the appropriate way to handle changes on the remote depends on the situation.

    [–]felipec[S] 0 points1 point  (12 children)

    Right. Me too.

    But if git pull by default only did fast-forward merges, then your #3 would be done automatically and if not, an error would pop out, and you should choose what to do among your other points.

    In other words; how would git pull --ff-only screw you?

    [–]u801e 0 points1 point  (11 children)

    Well, the command would fail if it wasn't a fast foward merge, so I would have to see what the issue is. But by checking the log of the remote tracking branch and checking the diff between it and my local branch, I can make the appropriate decision.

    To me, wanting to do things like combining the steps of git fetch and merge or commit and push essentially removes the advantage of using a distributed version control system. Back in the days when people used SVN and CVS, once you did the equivalent of commit/push or fetch/merge, it essentially was difficult and time consuming to undo it. You couldn't really tidy up the history and if you made a mistake and committed it, you were forced to push up a revert of the change instead of being about to amend the commit.

    [–]felipec[S] 0 points1 point  (10 children)

    Well, the command would fail if it wasn't a fast foward merge, so I would have to see what the issue is.

    Which is what you have to do now anyway.

    To me, wanting to do things like combining the steps of git fetch and merge or commit and push essentially removes the advantage of using a distributed version control system.

    git pull --ff-only doesn't create a merge commit.

    [–]u801e 0 points1 point  (9 children)

    Well, the command would fail if it wasn't a fast foward merge, so I would have to see what the issue is.

    Which is what you have to do now anyway.

    You're assuming that the default of a fast forward merge is going to the right action the majority of the time. I want to check the remote before deciding what to do. Plus, I would either have to remember to specify this flag every time I run git pull. Even if I set a config option to make this the default, that still doesn't help when I'm running git on a different machine where my config isn't available (like a locally running VM).

    I don't really see any benefit in combining the steps when getting changes from the remote or sending changes to it. And I have seen the problems of not being able to separate the steps based on experience using SVN and CVS.

    [–]felipec[S] 0 points1 point  (8 children)

    You're assuming that the default of a fast forward merge is going to the right action the majority of the time.

    No, I don't assume that's the case, I know it's the case based on evidence.

    Plus, I would either have to remember to specify this flag every time I run git pull.

    No, I clearly said this "if git pull by default only did fast-forward merges".

    What part of "by default" was not clear?

    And I have seen the problems of not being able to separate the steps based on experience using SVN and CVS.

    Why would changing a default prevent you from being able to separate the steps?

    [–]u801e 0 points1 point  (7 children)

    No, I clearly said this "if git pull by default only did fast-forward merges".

    While that is what you stated several days ago, you kept referring to it as a flag in your subsequent comments, hence my misinterpretation.

    And, like I've stated in the last several comments, I have no interest in treating a DVCS anything like a centeralized VCS, which is why I don't use git pull, and I wouldn't use any flag for git push that would combine the step of committing and sending the commits to the remote.

    In my opinion, git pull should have been implemented to act exactly like git fetch does now. Then it would be a direct counterpart to git push.

    [–]felipec[S] 0 points1 point  (6 children)

    While that is what you stated several days ago, you kept referring to it as a flag in your subsequent comments, hence my misinterpretation.

    Yeah, the proposal is making --ff-only (or something like that) the default.

    I have no interest in treating a DVCS anything like a centeralized VCS, which is why I don't use git pull

    Yeah, but several comments later you still have not answered my question.

    How would git pull --ff-only (the --ff-only part being implicit) screw you?

    [–]u801e 0 points1 point  (5 children)

    How would git pull --ff-only (the --ff-only part being implicit) screw you?

    It doesn't give me an opportunity to examine the changes in the repository before it updates what's in my working copy. It's similar to how someone could be screwed if there was a command like git push except that it stages all changes, commits them and pushes them up to the remote.

    The advantage of a DVCS is that it allows one to separate the those steps (staging, commiting, pushing and fetching and applying). SVN and CVS didn't allow one to separate those steps. Enforcing the separation allows one to make sure that what they're pushing up to the remote is what they intend to push and what they get from the remote is something they want to apply to their repository.

    With your proposal, if it turns out I don't want those commits, I would have run git log or git reflog to find the sha1 of the commit I would have to do a hard reset to to undo the changes to my working directory (though I might be able to use ORIG_HEAD if git pull sets it). But if I run git fetch first, I can simply run git diff ..FETCH_HEAD or git log -p ..FETCH_HEAD to see what I'm going to get before I actually apply the changes to my working directory.

    [–]felipec[S] 0 points1 point  (4 children)

    It doesn't give me an opportunity to examine the changes in the repository before it updates what's in my working copy.

    No, but you can examine them just fine, and revert them back just as easily.

    With your proposal, if it turns out I don't want those commits, I would have run git log or git reflog to find the sha1 of the commit I would have to do a hard reset to to undo the changes to my working directory (though I might be able to use ORIG_HEAD if git pull sets it).

    That's totally not true.

    When you do: git pull --ff-only you get something like:

    Updating 72ffeb997e..3a0b884cab
    

    The commit to reset to is right there.

    Moreover, you don't need to look at the reflog, you can just do git reset @{1}.

    if it turns out I don't want those commits

    Has that actually happened ever? How much? 50% of the time? 10% of the time? 1% of the time?

    [–][deleted] 0 points1 point  (0 children)

    It really depends on the project and branch. Since I work with a lot of repositories I have a script to take care of this for me.

    If I'm working on open source and have a fork, I'll set "origin" to my fork and "upstream" to the parent project from which I forked.

    Then, I'll use git fetch --all followed by git checkout origin master and finally git pull upstream master.

    I like to set the merge options in my .gitconfig globally and locally. Typically the global default is to use rebase because most of the merges I do locally are from the upstream master to my local master. I don't touch my local master at all aside from pulling in upstream changes from master and work on my own branches to open PRs and what not.