I always do my work on a branch, switch to master when I want to pull, and rebase my branch on the new master. If I'm working on a feature branch that other developers are also working on I do the same thing with the feature branch; my local work is on a local branch that I rebase onto the tip of the shared feature branch.
When I want to share my commits, I do my rebase first, do a fast-forward merge in the shared branch (with or without a merge commit, depending on whether or not I need to group my commits), and then I push the shared branch. After that my local branch is rooted at the tip of the shared branch, and prior history never gets rebased again.
Merging master into a shared feature branch requires different techniques; we don't use rebase for that. Instead we coordinate: everyone gets their work into the feature branch (or takes responsibility for anything they're not ready to commit yet), and one person takes on merging master into the feature branch, fixing conflicts, and pushing the result back to the shared branch. Everyone else pulls, rebases any local work they still have, and we move on. When it's time to merge the feature branch into master we do the same thing, but no local work is allowed to be held back, and the person doing the merging will also merge the final feature branch back into master.
Luckily I'm in a small company with not too many developers. I can see the coordination required for what I just described getting more and more difficult as the branches get bigger, last longer, and have more people working on them. When you get up to that scale the Linux Kernel approach of breaking the project into largely independent sub-projects with different owners responsible for merging their own parts is probably the best approach. That makes the coordination manageable.
When I want to share my commits, I do my rebase first, do a fast-forward merge in the shared branch (with or without a merge commit, depending on whether or not I need to group my commits), and then I push the shared branch. After that my local branch is rooted at the tip of the shared branch, and prior history never gets rebased again.
Merging master into a shared feature branch requires different techniques; we don't use rebase for that. Instead we coordinate: everyone gets their work into the feature branch (or takes responsibility for anything they're not ready to commit yet), and one person takes on merging master into the feature branch, fixing conflicts, and pushing the result back to the shared branch. Everyone else pulls, rebases any local work they still have, and we move on. When it's time to merge the feature branch into master we do the same thing, but no local work is allowed to be held back, and the person doing the merging will also merge the final feature branch back into master.
Luckily I'm in a small company with not too many developers. I can see the coordination required for what I just described getting more and more difficult as the branches get bigger, last longer, and have more people working on them. When you get up to that scale the Linux Kernel approach of breaking the project into largely independent sub-projects with different owners responsible for merging their own parts is probably the best approach. That makes the coordination manageable.