Linus on git rebase and merge (2009)
mail-archive.com
mail-archive.com
And, in fact, preferably you don't pull my tree at ALL, since nothing
in my tree should be relevant to the development work _you_ do.
Sometimes you have to (in order to solve some particularly nasty
dependency issue), but it should be a very rare and special thing, and
you should think very hard about it.
That might work well for the kind of highly decoupled development he's dealing with, but I'm not sure it'll work for the kind of work I do with my colleagues. The first thing I do every morning is a git pull (often surrounded by git stash [pop] and sometimes with a --rebase appended if I have outgoing commits), because I might need the stuff a colleague worked on the past days, or we might work in the same files and generate lots of conflicts otherwise. Maybe I'm doing it wrong.If you review now Linus comment, it makes sense that pulling should be done on a feature branch: one that has a complete, working solution of something you need.
His suggestion that in order to keep your git history pristine you should just email patches until you can get to a working state seems bizarre. git is a tool that works really well for collaborating on something until it works. In a small project, who gives a crap if you see a commit that fixes the previous commit once in a blue moon. Yes, on a large project like the Linux kernel that might get annoying, but for 99% of people it's irrelevant.
It's not about pushing broken code, it's about having a very complex code base and you just can't foresee how each change will behave before it has been tested by a lot of people on a lot of different machines.
A linux release is a pretty good point in time to get a quite well tested version and therefore it's a good base for your changes and to be able to test how your changes - only your changes - behave.
To me, it doesn't sound meaningfully different from walking over to a coworker and arguing over the code or its results for a while. The "emailing patches" thing seems more like a slow-by-necessity feedback loop due to the fact that Linux contributors are across the world and even IMing or real-time screen-sharing doesn't make sense.
I know that keeping a clean history is very important, but keeping all the patches straight IMHO is even more important and git is exactly the tool for that job. Email is the wrong tool for that job aside from some very occasional "check this out, am I being stupid here?" type things.
Then again, I am not a kernel developer and have no idea what I am talking about. All I can contribute is that on small-ish projects (up to 10 people on a team), using git differently makes everyone's lives easier at the cost of the history looking slightly worse.
* Breakage is localized to small merges. Instead of debugging the breakage of 1 month of work integrated with yours, you debug it a day of work at a time. Localized/differential debugging is much easier.
* Ditto with conflicts. Dealing with daily conflicts is much easier than dealing with the aggregate of a month's conflict. Beyond a certain threshold of difficulty, conflict resolution quality becomes very low. People just give up and start resolving conflicts semi-randomly.
So it's better to base your changes on the commit of a linux release, which has been tested quite a lot, which has some kind of defined state.
But I can also imagine it being useful for everybody to pull the code from the last release and work on it, before a single person pulls the code, merges, and commits it to the central repo.
I have a similar flow, I might maintain multiple feature branches but I will pull and rebase master very regularly to be sure I am working against the most recent code base. I will also rebase my work to squash commits to create a clean history prior to moving work from my feature branches to master. So if someone was to pull in one of my feature branches prior to it being merged or tagged, that tree would very likely change and then we have two versions of my work that would create a merge nightmare when we decided to get everything into master.
Git eventually garbage collects old versions (i.e. not accessible from a branch), but by default only after a month.
Unfortunately... using git reflog to find the old version and undo a command itself has a learning curve. :\
* Heuristic textual diffing is probably inferior to recorded actual diffs from our code editors
* Git merges are monolithic, and hide conflict resolution work within them in a difficult-to-review way. On the other hand, git cherry-picks do not track history properly. Future revision control will probably solve this problem better (e.g: darcs seems like it should work better, except darcs has some other [probably more important] disadvantages).
One workflow I've seen other projects use -- that I've also adopted for my own projects -- is to have a separate (non-private) branch called "pu" (pending updates) or "wip" (work in progress). With warnings in the developer docs that this branch may be rebased at any time.
In short, keep code that you're willing to rebase clearly delineated from the code you're not, tell other people that it may be rebased in the future, and let them decide whether the benefit of pulling the code today outweighs the drawback of potentially being inconvenienced by a rebase tomorrow.
I very often run `git rebase master` in my feature branches to avoid having many conflicts to resolve just before my pull request to master. Once merged in master, initial commits I rebased from master did not seem to have changed. Am I missing something, here ?
I realize it has never been a problem for me because commits replayed when I rebase are always isolated in my feature branch and not pushed anywhere.
I suppose it would be a problem if I rebased from two different branches that were already public.
Also, I often work with "dependent branch". When a change has to be made that does not fit the feature branch "theme", I create a new feature branch from master, make my change, make a PR to master and rebase the dependent branch in the feature branch. It seems it does not cause any history overwrite too.
When rebasing, you're at least changing a parent commit and therefore changing all of your rebased commits, because their SHA1 is also based on their parents.
> I very often run `git rebase master` in my feature branches to avoid having many conflicts to resolve just before my pull request to master. Once merged in master, initial commits I rebased from master did not seem to have changed. Am I missing something, here ?
As long as you don't rebase a feature branch after you have pushed it into a remote repository or merged it into an already published branch, you're fine.
If the master branch went back and changed something before that common point of reference, things get more confusing. If you want to see this yourself, checkout a separate branch, rebase interactively and make a big edit in the past. It's much more painful to add on top of that successfully with the first branch (git will want to do a merge commit) because now the history has diverged between them. (and if you use CI, you might notice that when you rebase a wip topic branch locally, git tells you things like "You are 10 commits behind and 5 commits ahead".
whenever you rebase you create _brand new commits_ based off another part of the project history which bear a strong resemblance to the original commits but are nonetheless different.
adding -i allows you to do further modification of commit contents (reordering, squashing, dropping, etc. but a vanilla rebase is still changing history.
Linus addresses this in his mail early on: "People can (and probably should) rebase their _private_ trees (their own work). That's a _cleanup_. But never other peoples code. That's a "destroy history" "
That hasn't been my experience. As far as I can tell.
Edit: OK, I did a test and I can see that it is changing the sha1 for my commits on my private branch after I rebase from upstream master. So at least the way I'm doing it I'm not changing public comit hashes.
/s
The article explains why it makes sense to rewrite history - it's so as not to push garbage out on the world. One of the big niceties of distributed, disconnected repositories is that you can muck about, try things out, make mistakes, correct them, clean things up, and then push that out to a public repo. The difference with git is that you don't have to make a separate patch, roll things back (possibly restarting with a clean repo from master), then apply the patch and make a "clean" commit - you simply rebase the commits in git.
When I'm maintaining code, I don't care about every little twiddle of bits that happened; I care about discrete, human-level, bug or feature related patches, and sometimes development in progress doesn't match up with that. Being able to rewrite history means being able to have not just maintainable code, but a coherent change history.
And it's perfectly possible to mess up and create ugly histories without rebase; that's exactly one of the prime use cases for rebase - cleaning up ugly histories. You want me to clone a clean repo from master and try applying pieces of my patch from scratch? Screw that noise! Even with the speed of git clone, I'd much rather pick, squash and rewrite commit messages in rebase.