<div dir="ltr"><div style>[I don't do development on Mercury, so take this advice with a grain of salt.]</div><div style><br></div><div style>Is this advice just for people who are doing local commits to master and then pushing them, or also for people working with branches? I can see the appeal of trying to rebase remote changes onto a few short local commits, and keeping the history clean. But this will end up pretty chaotic for anyone who is either a) working on a feature branch long-term, or b) has made quite a lot of local commits to master without pushing them for awhile.</div>

<div style><br></div><div style>My main concern is that you end up with this Subversion-like state of having local changes, doing a pull, getting a conflict, and then having to fix your local changes to match the master -- not just the current revision, but all historical revisions going back to the branch point as well. One of the key advantages of distributed revision control is that you don't have to do that -- you can let the branch history accurately reflect the changes you actually made, instead of changing history to show what changes you <i>would</i> have made, had you made them against the current HEAD.</div>

<div style><br></div><div style>In certain cases, this can result in main-line commits being broken, so the project history is not stable. To take an example, let's say the master has the following commits on the server (in reverse chronological order):</div>

<div><div style>C</div><div style>B</div><div style>A</div><div style><br></div><div style>Then I start working locally, and, I do two commits called C1 and C2. My local history looks like this:</div><div style>C2<br></div>

<div style>C1</div><div style>C</div><div style>B</div><div style>A</div><div style><br></div><div style>Now in the mean time, let's say that the other developers continue working on master. By the time I am ready to submit C2, master looks like this:</div>

<div style>E<br></div><div style>D</div><div style>C</div><div style>B</div><div style>A</div><div style><br></div><div style>Now D actually changed an API that I am relying on in my local changes, so when I pull from the server, I get a semantic conflict -- git won't detect it as a merge conflict, but my code will no longer compile until I change my branch to call the new introduced in D.</div>

<div style><br></div><div style>Say that I use git pull. My branch ends up like this (with git log --graph):</div><div style><br></div><div style>* F</div><div style>|\</div><div style>| \</div><div style>|  * C2<br></div>

<div style>|  |</div><div style>|  * C1</div><div style>|  |</div><div style>*  | E</div><div style>|  |</div><div style>*  | D</div><div style>|  /<br></div><div style>|/</div><div style>* C</div><div style>|</div><div style>

* B</div><div style>|</div><div style>* A</div><div style><br></div><div style>I think this is what you meant by "complicating the history". Well, I don't find this complicated -- I find it <i>truthful</i>. C1 was not developed before or after D, it was developed simultaneously to D on a different branch.</div>

<div style><br></div><div style>Now I have a compiler error which I need to resolve. I'll commit that as G (or potentially as an --amend to F itself, to avoid having a broken history in my branch). Then I'll git push the result up to the server. Now the server's history will include this little detour, but at least the <i>main line</i> of development (which you can see with git log --first-parent) will just be F, E, D, C, B, A -- all of which are good builds. In fact, there are no broken builds here.</div>

</div><div style><br></div><div style>Now let's go back to the same scenario, but this time use git pull --rebase. My local history ends up like this:</div><div style><br></div><div style>C2-rebased</div><div style>C1-rebased</div>

<div style>E</div><div style>D</div><div style>C</div><div style>B</div><div style>A</div><div style><br></div><div style>It's all flattened into a single line of history, which is a lie. It lies about the fact that C1-rebased and C2-rebased were developed strictly after D and E. Now I'm going to fix the compile error which I may commit as F or possibly as an --amend to C2. But note that C1-rebased also does not compile, because it was written before D, but has been re-based so that it is after D. In fact, the entire history of my local commits will be potentially broken by the rebase.</div>

<div style><br></div><div style>A good developer will carefully go back and check that each commit compiles and passes the tests before pushing. But that is a waste of time. And certainly someone will not be bothered to do this. So when I do a git push, while the most recent commit will still compile, I will be pushing changes into the history that are broken. They won't even show up as a branch in the git logs. They are main-line commits, because they were rebased into the main line of development.</div>

<div style><br></div><div style>The other problem, of course, is that you have an <i>actual</i> merge conflict, because both changes are making changes to the same text. With the rebase approach, you don't just resolve them at the merge point, but you have to resolve the conflicts for each of your local commits. Not to mention that if the point of revision control is to reflect the history of development, we are failing at it, because we've forever lost (git reflog aside) the original commits C1 and C2, in favour of C1-rebased and C2-rebased, which were never authored, compiled, tested or even seen by a human being.</div>

<div style><br></div><div style>It seems to me like it's better to have "complicated", but accurate, history, than it is to have history that is a) a lie, and b) potentially untested and broken.</div><div style>

<br></div><div style>Certainly if you were actually developing a feature branch over some time, you would not want to rebase the changes from master into the branch. So I assume this advice is only for people making local commits to master with the intention of immediately pushing them up.</div>

<div style><br></div><div style>(Note: I am not saying that you should never rebase. The advice that Paul gave is sound and useful when used sparingly and appropriately. I just don't think that blindly rebasing as a solution to forked development is a good idea.)</div>

<div style><br></div></div><div class="gmail_extra"><br><br><div class="gmail_quote">On Fri, Jan 11, 2013 at 2:33 PM, Peter Wang <span dir="ltr"><<a href="mailto:novalazy@gmail.com" target="_blank">novalazy@gmail.com</a>></span> wrote:<br>

<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Hi,<br>
<br>
If you try to push some changes and git tells you that you're not up to<br>
date, please use "git pull --rebase" (you can make it the default).<br>
By default, "git pull" will merge, complicating the history (see gitk)<br>
and making it harder to work with.<br>
<span class="HOEnZb"><font color="#888888"><br>
Peter<br>
</font></span><br>
PS. git log -p --simplify-merges seems to DWIM<br>
</blockquote></div><br></div>