I work this way and agree about the value of a clean, linear history. It makes working with past versions of your code a breeze. There's one thing the OP doesn't mention that I've found important.
Say you're working on a major design change in a private branch and it has 100 commits. When it's ready to be put on top of master, you'd really like not to squash all 100 commits. Unfortunately, if there are conflicts, then rebasing B1,B2,...,B100 onto master is likely to be much harder than squashing B1,...,B99 into B100 and then rebasing. Why? In the squashed case you only have to deal with conflicts between B100 and master, while in the unsquashed case you have to deal with all the conflicts that ever existed as you progressed from B1 to B100. It's frustrating to find yourself fixing conflicts in code that you know doesn't exist any more. It's also error-prone since it forces you to remember what you were doing at all those steps. In such situations, I give up and squash. That's not great either, since you now have the disadvantages of a single monolithic commit.
The solution is to be diligent about rebasing B onto master as frequently as master changes, so B never has a chance to drift too far afield. This at least gets rid of the worst pain, which is conflicts that compounded unnecessarily. It also keeps you aware of what's happening on master.
Here's a trick for you: make sure you have rerere enabled. Merge the end commit, resolve all the conflicts and commit the merge (or just run rerere to record the conflict resolution). Then abort the merge or reset back to undo it. Now do the rebase, which will re-use the resolutions for any identical conflicts. You still have to deal with conflicts unique to the intermediate state, but in my experience rerere helps a lot.
I tried rerere once and it felt too much like magic to me, i.e. too complicated in a way that I didn't trust. Experience with conflicts has led me to eschew magic merge tools and rely on the simplest strategies: 1. minimize conflicts; 2. bite the bullet and deal with them manually. (Edit: my question about rerere is: how identical is "identical"? How can I be sure that it will redo what I did before in exactly the way I would do it now? Doesn't it have to understand my intent to achieve that?)
The diligent-rebasing-along-the-way workflow I proposed is all about #1. You still have to deal with intermediate conflicts this way too, but at least they're minimized. If something you commit to master conflicts with my B49, I have to fix B1..B49 but at least I can write B50..B100 in a way that takes your work into account.
There is nothing really magic about rerere. During a merge, it records each conflict. When you commit the merge, it records your resolution of the conflict. If that _identical_ conflict occurs again, it re-applies the same resolution. You can choose whether it marks the the file as resolved or not, which allows you to easily review what was done before committing the merge.
Say you're working on a major design change in a private branch and it has 100 commits. When it's ready to be put on top of master, you'd really like not to squash all 100 commits. Unfortunately, if there are conflicts, then rebasing B1,B2,...,B100 onto master is likely to be much harder than squashing B1,...,B99 into B100 and then rebasing. Why? In the squashed case you only have to deal with conflicts between B100 and master, while in the unsquashed case you have to deal with all the conflicts that ever existed as you progressed from B1 to B100. It's frustrating to find yourself fixing conflicts in code that you know doesn't exist any more. It's also error-prone since it forces you to remember what you were doing at all those steps. In such situations, I give up and squash. That's not great either, since you now have the disadvantages of a single monolithic commit.
The solution is to be diligent about rebasing B onto master as frequently as master changes, so B never has a chance to drift too far afield. This at least gets rid of the worst pain, which is conflicts that compounded unnecessarily. It also keeps you aware of what's happening on master.