Rebasing can cause the loss of history and developers should be as careful with it as system admins are with `sudo`. I can't recommend any workflow that includes it without treating is as a terrifying and scary thing. How easy is it to accidentally remove a line during interactive rebase and lose all work associated with it?
This is why my team and I moved to squash merging. Sure it has it's own drawbacks, but they're far less worrisome than rebasing. If you screw up a rebase, the history is re-written or force-pushed by accident. If you screw up a squash merge, you can still check out the intermediate commits if you know the hash.
We won a Ruby award for our work on Git Reflow. There are big improvements coming this week that can make it easy for teams to tweak the workflow to suit any special needs you might have. It works on github and bitbucket and automatically creates pull requests (and makes sure they're reviewed.) Gitlab support coming soon (maybe this month).
I wouldn't recommend it to others as I don't trust them to read the man page and understand what git-rebase does. Those of us who use git-rebase also know how to recover the refs since before they are GCed, though I've never had to do that.
It's dangerous to pronounce certain powerful features of a system as off limits for day to day private use. That's a different pronouncement than a decision not to share "why everyone should use git-rebase often".
An exception can be made for topic branches, especially in a pull-request workflow. These branches could be rebased / amended to update the final result, even after they have been pushed already.
> anything accessible without any authorization.
Even if it does require authorization, it's considered public in regard to this discussion.
Why is this a conversation?
It does not matter if you pushed it to a private repository where some other people still have access to; they might have fetched that history and committed on, which causes unexpected results when you rewrite such a branch.
I tend to use published history / commits for this reason to make it less confusing.
This is not only not easy, it's actually very difficult. If you drop something in an interactive rebase, you can reset your HEAD to the HEAD commit from before your rebase. It's a bit arcane, and has its own dangers, but it's also important to be clear that rebases are not destructive unless a git gc runs between your rebase and realizing you made a mistake. It's also the equivalent fix to checking out intermediate commits for a squash merge, as far as I know.
Don't get me wrong, I don't think rebase should be the first tool you reach for and I don't particularly like “rebase everything to master” workflows. But it's not as dangerous as you're making it sound, IMO.
Until the original branch is deleted and the refs are garbage collected, anyways.
It seems strange to me to advocate for squash merging on a premise of not losing history. A squash merge is a rebase.
> A squash merge is a rebase.
But my point was just that a merge squash is just a specific incantation of the git-rebase tool. And it is one of the most history destroying incantations, rather than the least.
C -> D -> E
A -> B
A -> B -> F
Whereas a rebase + (now fast-forward) merge would be
A -> B -> C' -> D' -> E'
A -> B -> S
(One thing I really love about git is that it's a tool I use extremely often (it's something like, 20% of the commands I run in my terminal), yet I learn something new and useful all the time.)
Also it's entirely possible I'm wrong, I'm a fan of merge bubbles (sometimes rebased for clarity), and avoid large squashes in general, so maybe I just don't understand how people do it. I don't know why you'd bother to keep both a merge and squash commit around, though.
I don't know why you'd want to do it that way either! But I also don't understand why you'd want to "never rebase", so when it comes to git, I assume that someone has reasons for doing all kinds of things I don't understand.
Our workflow is:
- locally, commit to local master or a local branch
- occasionally checkout local master (if necessary) and pull using the 'rebase after fetch' option.
- if we had local master commits, fixup any conflicts in our code
- if we were working on a local branch, checkout that branch and rebase it on the new master, fixing any conflicts that arise
- If our work is complete, possibly do a final rebase to reorder and squash local commits, and fast-forward merge to master if we were on a branch. Finally, push the local master to share our work.
Note that we never rebase anything that's been pushed.
Also, if we're worried that a rebase is potentially complex and error prone, we create a new branch at the existing HEAD so that the old commits don't get lost in the reflog. Once the rebase is done, we can delete that branch so the old commits can be garbage collected.
Getting rid of merge commits is literally the only benefit of your workflow over the standard branching model.
This may sound counter-intuitive, because you're thinking it's the same conflict either way. Most of the time it is; if you and someone else changed the same bit of code, the conflict will be shown to you the same way whether you merge or rebase. Those are easy to fix. What's harder is when someone reorganizes some code without making significant changes to it. In a merge, you'll see changes all over the place, but in a rebase git can usually figure out the new line numbers, and may not indicate a conflict at all. The other area where my team has had difficulty with merges is in Visual Studio sln and csproj files. When you add new projects to a solution or new references to a project, git can present a very confusing diff during a merge conflict. But for rebase only your additions are highlighted, and most of the time you can solve the conflict with "use theirs before mine".
The fear of rebase seems to always come from its ability to "delete your work", but that fear is almost always unfounded and based on a lack of knowledge on git's internal structures. Of course, one of git's biggest and well recognized faults is that its UI makes no attempt to alleviate those fears.
Git rebase is absolutely a vital part of the developer toolkit. If you're not using it, you're missing out on a big timesaver and git feature.
I think you must be talking about rebasing public commits/history. In the article he is specifically talking about private history and has a nice big warning against rebasing public history. Linus has explained the distinction pretty well before: http://firstname.lastname@example.org/... .
Wouldn't your tests catch when lines get elided by manual merge conflict resolution? Have you see mjd's Git Habits, which lays out a foolproof way of rebasing without losing lines?
When I'm dealing with my own work branches -- I absolutely rebase my commits before submitting a pull request to my team. I'll either squash irrelevant commits or I'll reword poorly written git commit messages. I also almost always default to `get pull --rebase` as well, as I hate the noise of merge commits littering up my commit log.
This is what Mercurial Evolve tries to solve. There's nothing wrong with rewriting draft commits. The only potential problem is rewriting public commits. Mercurial uses phases to distinguish drafts from published commits and you may optionally designate certain repositories as non-publishing, so that they can be used for collaboratively editing draft commits.
A similar de facto convention on git is to only rewrite certain branches (e.g. feature branches) but never rewrite others (e.g. master). Commits that are local-only can be rewritten at will. Mercurial just codifies this convention via phases.
We need more articles like this! Thank you for your work!
> …or force-pushed by accident
Well, you could nuke git folder "by accident" as well :) Jokes aside, don't force-push to "other's" branches).
git push --force-with-lease origin/other_branch
if you're going to store commit hashes externally instead of using the reflog then it makes no difference whether you use rebase, merge, or even cherry-pick or reset.
also, sudo itself poses no risk; it's much more important to evaluate what you're running, instead of a blanket restriction on what is just another tool. if I run "cd /; rm -rf *" on my desktop, it doesn't really matter whether I'm running as root or as my user, I'm going to have a bad day. "curl | sh" is equally as dangerous as "curl | sudo sh".
And I also mention in the post that we should always use what's best for our teams, so if the squash merge works best for you, go for it! :)
git diff origin/master branch
This way you can see if the diff looks like what you expect it to be. If it doesn't and you believe you messed something up while rebasing just
git reset --hard origin/branch
and redo the rebase