81

Consider the following situation:

  • You have a clone of a git repository
  • You have some local commits (commits that have not yet been pushed anywhere)
  • The remote repository has new commits that you have not yet reconciled

So something like this:

Unmerged commits

If you execute git pull with the default settings, you'll get something like this:

merge commit

This is because git performed a merge.

There's an alternative, though. You can tell pull to do a rebase instead:

git pull --rebase

and you'll get this:

rebased

In my opinion, the rebased version has numerous advantages that mostly center around keeping both your code and the history clean, so I'm a little struck by the fact that git does the merge by default. Yes, the hashes of your local commits will get changed, but this seems like a small price to pay for the simpler history you get in return.

By no means am I suggesting that this is somehow a bad or a wrong default, though. I am just having trouble thinking of reasons why the merge might be preferred for the default. Do we have any insight into why it was chosen? Are there benefits that make it more suitable as a default?

The primary motivation for this question is that my company is trying to establish some baseline standards (hopefully, more like guidelines) for how we organize and manage our repositories to make it easier for developers to approach a repository they haven't worked with before. I am interested in making a case that we should usually rebase in this type of situation (and probably for recommending developers set their global config to rebase by default), but if I were opposed to that, I would certainly be asking why rebase isn't the default if it's so great. So I'm wondering if there is something I'm missing.

It has been suggested that this question is a duplicate of Why do so many websites prefer “git rebase” over “git merge”?; however, that question is somewhat the reverse of this one. It discusses the merits of rebase over merge, while this question asks about the benefits of merge over rebase. The answers there reflect this, focusing on problems with merge and benefits of rebase.

8
  • 6
    Possible duplicate of Why do so many websites prefer "git rebase" over "git merge"?
    – gbjbaanb
    Jan 11, 2016 at 8:44
  • The other problem is that if you accidentally commit to master, then do a pull which merged, the two masters can be out of sync even though the merge looks normal. This is why my pull alias includes --ff-only.
    – Ixrec
    Jan 11, 2016 at 10:14
  • 1
    @Ewan SourceTree shouldn't change its graph view of this. It accurately represents the graph.
    – jpmc26
    Jan 11, 2016 at 19:56
  • 4
    For dupe voters, please note that the content in the answer I accepted is most definitely not present in the question you're claiming is a duplicate.
    – jpmc26
    Jan 12, 2016 at 3:28
  • 1
    The answers touched on this but I'll reiterate that changing hashes in Git is not "a small price". Git revolves around each commit's hash (and other object hashes) and any change to them (especially if it were default behaviour) goes against some the fundamental ideas Git is built upon. Yes, there are scenarios where we mutating hashes is acceptable, but as this as an out-of-the-box default would be madness.
    – Whymarrh
    Jan 17, 2016 at 22:30

5 Answers 5

67

It is hard to know for sure why merge is the default without hearing from the person who made that decision.

Here is a theory...

Git cannot presume it is ok to --rebase every pull. Listen to how that sounds. "Rebase every pull." just sounds wrong if you use pull requests or similar. Would you rebase on a pull request?

In a team that is not just using Git for centralized source control...

  • You may pull from upstream and from downstream. Some people do a lot of pulling from downstream, from contributors, etc.

  • You may work on features in close collaboration with other developers, pulling from them or from a shared topic branch and still occasionally updated from upstream. If you always rebase then you end up changing shared history, not to mention fun conflict cycles.

Git was designed for a large highly distributed team where everyone does not pull and push to a single central repo. So the default makes sense.

  • Developers who do not know when it's ok to rebase will merge by default.
  • Developers can rebase when they want to.
  • Commiters who do a lot of pulls and have a lot of pull get the default that suits them best.

For evidence of intent, here's a link to a well known email from Linus Torvalds with his views on when they should not rebase. Dri-devel git pull email

If you follow the whole thread you can see that one developer is pulling from another developer and Linus is pulling from both of them. He makes his opinion pretty clear. Since he probably decided Git's defaults, this might explain why.

A lot of people now use Git in a centralized way, where everyone in a small team pulls only from an upstream central repo and pushes to that same remote. This scenario avoids some of the situations where a rebase is not good, but usually does not eliminate them.

Suggestion: Don't make a policy of changing the default. Any time you put Git together with a big group of developers some of the developers won't understand Git all that deeply (myself included). They will go to Google, SO, get cookbook advice and then wonder why some things don't work, e.g. why does git checkout --ours <path> get the wrong version of the file? You can always revise your local environment, make aliases, etc. to suit your taste.

5
  • 5
    +1 for not changing the default - rather than rolling your own git workflow it's probably better to grab one that exists (e.g. any of the ones documented by Atlassian atlassian.com/git/tutorials/comparing-workflows/… )
    – Rob Church
    Jan 11, 2016 at 13:17
  • 2
    Thanks for the answer. The info about pulling from downstream was what I was missing. Also, interestingly enough, the link you gave encourages exactly what I would like to achieve: "People can (and probably should) rebase their private trees (their own work)."
    – jpmc26
    Jan 12, 2016 at 3:36
  • 2
    @jpmc Great. Indeed. Rebasing private trees can keep a lot of distracting crud out of the shared history. I do it. It's the clarity on when not to do it that's important.
    – joshp
    Jan 12, 2016 at 4:06
  • "some of the developers won't understand Git all that deeply". Rebase is NOT a super fancy ("deep", etc) feature of Git. Do you really consider this thing that hard to understand? I think that this should be a trigger to call dev incompetent. Aug 26, 2018 at 8:36
  • There is a caveat to all arguments you have listed, git sometimes when there are tons of merges is losing commits. If you have a large de-centrilzed team working on one branch history becomes pretty useless for developers and sometimes even git does not know what is where. Happened to me 3 times in my career of about 12 years on git. Apr 22, 2021 at 12:42
17

If you read the git manpage for rebase, it says:

Rebasing (or any other form of rewriting) a branch that others have based work on is a bad idea: anyone downstream of it is forced to manually fix their history. This section explains how to do the fix from the downstream’s point of view. The real fix, however, would be to avoid rebasing the upstream in the first place.

I think that says it well enough as a reason not to use rebase at all, let alone automatically do it for every pull. Some people consider rebase to be harmful. Perhaps it should never have been put into git at all, as all it appears to do is prettify the history, something that shouldn't be necessary in any SCM whose single essential job is to preserve history.

When you say 'keeping ... the history clean', think you're wrong. It may look nicer, but for a tool that is designed to keep history of revisions, it is much cleaner to keep every commit so you can see what happened. Sanitising the history afterwards is like polishing the patina away, making a rich antique look like a shiny repro :-)

10
  • 3
    @Ewan IMHO "doesn't work but looks pretty" is something that should be solely reserved for the fashion industry not IT. IMHO git should remove it as it obviously encourages too many people to think style is more important than substance.
    – gbjbaanb
    Jan 11, 2016 at 16:03
  • 19
    I would dispute your suggestion that keeping the history clean is useless. It's not. A repository history is intended for human consumption, and as such, there's a lot of value in making it readable. In my particular use case, we're even intending that non-developer Project Managers should be able to read the history. A clean history can only help with that, and it can help a new developer who has never seen the code base and has to dive in and fix a bug understand what happened in a particular area of code.
    – jpmc26
    Jan 12, 2016 at 4:42
  • 5
    Code exists first and foremost to be executed by a machine (possibly after being compiled). Applying you philosophy above, we would conclude that readability of the code doesn't matter because no amount of readability will solve the difficulty of understanding code. I never suggested that anything should be squashed. I am merely saying that a complex graph that diverges into a lot of crossing paths is difficult to follow, so it's worth investing a little into keeping your history fairly clean. Note also that a linear history is more conducive to leveraging git tools like blame and bisect.
    – jpmc26
    Jan 23, 2016 at 21:12
  • 10
    The docs don't say "do not rebase." It says, "don't rebase changes that are upstream of other people." There is a world of difference between those two. Linus himself advises some rebasing for organizing history, as can be seen in the link in the accepted answer. And how would any tool know which commits are irrelevant (especially if tools have difficulty analyzing a non-linear history!), and how would you know which commit you want to diff with (especially without being able to dig through the history!)? You are taking a word of caution about a specific situation to a dogmatic extreme.
    – jpmc26
    Jan 24, 2016 at 17:20
  • 5
    You definitely do not want "keep history of revisions" if that includes every commit a developer has ever made. Following that logic we should also record the original version of code every time the backspace key has been hit. Every commit should be a minimal and complete change that still keeps the whole project in stable state. If your original commit is later found to be buggy, it's much better to rebase/edit/amend the commit than to create another. However, see also mail-archive.com/dri-devel@lists.sourceforge.net/msg39091.html Jul 7, 2016 at 9:16
12

You're correct, assuming you have only one local/changed repository. However, consider there being a second local PC, for example.

Once your local/modified copy is pushed somewhere else rebasing will screw up those copies. Of course, you could force pushing, but that quickly becomes complicated. What happens if one or more of these have another completely new commit?

As you can see, it's very situational, but the base strategy seems (to me) a lot more practical in non-special/collaboration cases.


A second difference: The merge strategy will keep a clear and time consistent structure. After rebases it's very likely that older commits might follow newer changes, making the whole history and its flow harder to understand.

1
  • 2
    "After rebases it's very likely that older commits might follow newer changes" -- that happens with merges too, though, which is why you (might) need the pretty graph to make sense of it. The time at which a commit was made might be long before the merge that brought it into this branch. Jan 12, 2016 at 15:33
8

The big reason is probably that the default behaviour should "just work" in public repos. Rebasing history that other people have already merged is going to cause them trouble. I know you're talking about your private repo, but generally speaking git doesn't know or care what's private or public, so the chosen default is going to be the default for both.

I use git pull --rebase quite a lot in my private repo, but even there it has a potential disadvantage, which is that the history of my HEAD no longer reflects the tree as I actually worked on it.

So for a big example, suppose that I always run tests and ensure they pass before doing a commit. This has less relevance after I do a git pull --rebase, because it is no longer true that the tree at each of my commits has passed the tests. As long as the changes don't interfere in any way, and the code I pull has been tested, then presumably it would pass the tests, but we don't know because I never tried it. If continuous integration is an important part of your workflow, then any kind of rebase in a repo that's being CIed is troubling.

I don't really mind this, but it does bother some people: they'd prefer that their history in git reflects the code they actually worked on (or perhaps by the time they push it, a simplified version of the truth after some use of "fixup").

I don't know whether this issue in particular is the reason that Linus chose to merge rather than rebasing by default. There could be other disadvantages I haven't encountered. But since he's not shy of expressing his opinion in code, I'm pretty sure it comes down to what he thinks is a suitable workflow for people who don't want to think about it too much (and especially those working in a public rather than a private repo). Avoiding parallel lines in the pretty graph, in favour of a clean straight line that doesn't represent the parallel development as it happened, is probably not his top priority even though it is yours :-)

3
  • "Rebasing history that other people have already merged" read the original question again.
    – Spongman
    Nov 13, 2021 at 3:21
  • 1
    @Spongman No, that was a fair point in light of the fact that "generally speaking git doesn't know or care what's private or public, so the chosen default is going to be the default for both." Maybe in retrospect, it might have been more useful if git had resisted the temptation to guess in the face of ambiguity, as in force the user to choose explicitly instead; nowadays, I typically configure my local repositories to do just that (git config --global merge.ff only).
    – jpmc26
    Jan 7, 2022 at 9:25
  • the original question specifically states: "commits that have not yet been pushed anywhere" so, "Rebasing history that other people have already merged" is irrelevant.
    – Spongman
    Jan 11, 2022 at 18:52
0

It seems that the primary reason is that git-pull(1) originally only did merges. The rebase option came later.[1]

Here’s the commit message of what seems to be the initial commit for the original git-pull script:[3]

Add the simple scripts I used to do a merge with content conflicts.

They sure as hell aren't perfect, but they allow you to do:

    ./git-pull-script {other-git-directory}

to do the initial merge, and if that had content clashes, you do

    merge-cache ./git-merge-one-file-script -a

which tries to auto-merge. When/if the auto-merge fails, it will leave the last file in your working directory, and you can edit it and then when you're happy you can do "update-cache filename" on it. Re-do the merge-cache thing until there are no files left to be merged, and now you can write the tree and commit:

    write-tree
    commit-tree .... -p $(cat .git/HEAD) -p $(cat .git/MERGE_HEAD)

and you're done.

Here’s the stat for that commit:

 git-merge-one-file-script | 35 +++++++++++++++++++++++++++++++++++
 git-prune-script          |  2 ++
 git-pull-script           | 46 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 83 insertions(+)

No rebase or something that looks like it.

The --squash option was added in 2006.[4] The commit says nothing about something that might remind one of “rebase”, i.e. replaying commits; instead, the apparent workaround before --squash was to do git --no-commit and then fiddle around until you are able to do a regular (single parent) commit.

And then in 2007:[5]

Teach 'git pull' about --rebase

When calling 'git pull' with the '--rebase' option, it performs a fetch + rebase instead of a fetch + merge.

This behavior is more desirable than fetch + pull when a topic branch is ready to be submitted and needs to be update.

fetch + rebase might also be considered a better workflow with shared repositories in any case, or for contributors to a centrally managed repository, such as WINE's.

As a convenience, you can set the default behavior for a branch by defining the config variable branch..rebase, which is interpreted as a bool. This setting can be overridden on the command line by --rebase and --no-rebase.

Signed-off-by: Johannes Schindelin johannes.schindelin@gmx.de Signed-off-by: Junio C Hamano gitster@pobox.com

Notes

  1. Most commits listed here were found using git lp --follow -- contrib/examples/git-pull.sh. git-pull(1) today is a “C builtin”, but it was originally a shell script.[2]
  2. 1e1ea69fa4 (pull: implement skeletal builtin pull, 2015-06-14)
  3. Maybe 839a7a06f3 (Add the simple scripts I used to do a merge with content conflicts., 2005-04-18)
  4. 7d0c68871a (git-merge --squash, 2006-06-23)
  5. cd67e4d46b (Teach 'git pull' about --rebase, 2007-11-28)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.