Hacker News new | past | comments | ask | show | jobs | submit login

This workflow is scary. A rebase should not be part of any everyday workflow and must be reserved _only_ for exceptional situations.

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).


But some of us use git-rebase every day perfectly fine and never lose information. Yes, you must be careful. No, you should never use it on public commits.

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".

If you are planning on doing some tricky rebase with possible problems you can just create a branch before you rebase on the old head. delete the branch when you know the rebase is successful. Then you don't have to deal with the reflog at all.

And we use git-rebase commonly on public commits without concern. Everyone uses git differently.

I think this depends on your definition of public? For me public means "in stable branches". I sure hope you aren't rebasing master

That's a weird definition of 'public'; for most folks, I imagine 'public' to mean anything accessible without any authorization.

When people talk about public commits it means commits other people have access too, and more specific, were able to commit on top. Authorization has nothing to do with it. In practice it means anything you have pushed.

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.

"commits other people have access to"

That's authorization.

You said:

> anything accessible without any authorization.

Even if it does require authorization, it's considered public in regard to this discussion.

It's not public if the public can't read it (requires authorization).

Why is this a conversation?

Because you've applied a different definition of public that doesn't fit the context. Public in the context in which it was used in this thread regarding git commits simply means commits you have pushed to a repo that another developer can access even if authorization is required to access it.

That's not what public means, though, in any context.

What we are talking about is whether or not you should rewrite public history. The technical reason is based on the fact if others have access to this history or not.

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.

Yeah maybe public isn't a good term for it, I usually use the term stable. So any pushed commits are indeed public, but they are only stable when they reach a stable branch. This means that if I am working on a branch I may remove, add, amend, reorder commits even after I've pushed them. If you constrain rebasing only to unpushed(not public) commits then you instead end up discouraging pushing code to the remote.

> How easy is it to accidentally remove a line during interactive rebase and lose all work associated with it?

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.

correction: unless you wait 30 or 90 days (depending on what you do in the interim) and then run other git commands which trigger automatic gc (which typically only happens with large repositories) or manual gc (in which case you're just asking for problems)

> 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

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.
Well, it's not a rebase in the literal sense: the base commit isn't changing. It _is_ modifying history, though.

It probably is in the same sense that 'git rebase -i' often puts the new branch on a new base even if that wasn't your explicit intention. Usually merge squashes are reparented to the current head of the target branch.

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.

Ah, sorry, I'm probably mis-understanding a "squash merge". I thought you meant you have a graph like this:

    C -> D -> E
  A -> B
then you "squash merge"

   /       \
  A -> B -> F 

where S is C + D + E.

Whereas a rebase + (now fast-forward) merge would be

  A -> B -> C' -> D' -> E'
and, if squashed during rebase -i or by some other means

  A -> B -> S
It seems you're saying a "squash merge" is the last one, I thought it was the second one.

That’s right. `git merge --squash` doesn’t even make a commit, it just updates the working tree and index to look like the post-merge contents — it’s then up to you to actually commit. So yeah, you don’t even end up with a merge commit.

Ah ha! TIL. Thank you!

(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.)

I'm sure someone out there does it like that (the joy and pain of git being there's a million ways of doing things), but the name I think is derived from the idea that you're replacing a merge commit with a squash of the disjoint parent, rather than a merge of a squash.

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.

No, I think you're right; take a look at the graphs on https://github.com/blog/2141-squash-your-commits for example.

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.

Rebase is absolutely a part of my everyday workflow, and the rest of my team's workflow as well. (Yesterday I had to show our CTO how to use it, because the rest of the team was getting annoyed by his merge commits.)

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.

Merge commits on pull are an annoying misfeature of the default workflow, but I thought you could fast-forward on pull to eliminate them?

> annoyed by his merge commits

Getting rid of merge commits is literally the only benefit of your workflow over the standard branching model.

Not true. We've also found that rebasing our own work onto the latest master makes it a lot easier to deal with conflicts. When you do a merge, the conflicts you see are a mixture of your own code and someone else's code, and it can be hard to tell which is correct because you're not familiar with the other person's changes. But when you rebases, all of the changes are code that you wrote, so it's easier to figure out how to fix the conflict.

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".

At least locally within your repo, it's actually very hard to lose work completely with git, via any combination of resets or rebases. The reflog (git reflog) stores all the movements of HEAD such that you can recover from almost any mistake as long as you realize you made it.

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.

> A rebase should not be part of any everyday workflow and must be reserved _only_ for exceptional situations

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://www.mail-archive.com/dri-devel@lists.sourceforge.net/... .

This is FUD. Many people use git-rebase flawlessly, without any of the issues you've encountered.

Wouldn't your tests catch when lines get elided by manual merge conflict resolution? Have you see mjd's Git Habits[0], which lays out a foolproof way of rebasing without losing lines?

[0] http://blog.plover.com/prog/git-habits.html

I completely disagree with this. You can easily undo a rebase locally if you mess up by resetting to hashes you pull out of the reflog.

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 workflow is scary. A rebase should not be part of any everyday workflow and must be reserved _only_ for exceptional situations.

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.

Typically, I see folks create a working branch, make incremental commits, such as a commit per day so as not to lose work, then rebase them all into a single commit before pushing back to origin. We haven't had any problems with this approach so far. YMMV

This is misleading advice, principally because “squash merging” is the very definition of losing history — it literally discards the commit history of your branch, which will then be lost to GC.

My apologies if this comes across as overly harsh! This article was really quite well written, and it's only after having been burned through rebases that I've gotten so headstrong against it.

We need more articles like this! Thank you for your work!

What is the harm in rewriting history in your private "topic branch"? This is not uncommon for the use case the author was describing. It certainly creates a cleaner logical history.

> If you screw up a rebase, the history is re-written…


> …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).

If you really feel like you do have to force-push to someone else's branch, please use

    git push --force-with-lease origin/other_branch
so you at least won't trash any commits your collaborator pushed after you last fetched their branch. git will see that your origin/other_branch doesn't match origin's other_branch, and will bail out so you can pull the changes and decide how to incorporate them.

> If you screw up a squash merge, you can still check out the intermediate commits if you know the hash.

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".

The rebase would ideally be the last thing you do on your branch, and very carefully.

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! :)

I'm so happy this uses squash merges, using rebase for this purpose is exceptionally overkill when you just want a clean commit history (due to the complexity of using the rebase tool), if you're writing a feature that involves a large amount of changes throughout the codebase it can be a handy tool to break up your work but for the general case of smaller PR's a squash merge is the way to go.

I don't share this concern, I've been rebasing frequently(several times a day) for years now and never caused any unrecoverable problems. I would recommend that the last thing you do before pushing a branch you've rebased is

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

we've adopted it daily to solve the issue with bad merge history. Seems that git will do a merge on the server and is confused on who did what. Care to elaborate on how you solved this problem w/out rebase?

Why squash merge instead of simple merge?

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact