git is a bag of commits organized into a tree (a tree of Merkle hash chains). Branches and tags are symbolic names for these. Think of it this way and there's no danger.
Indeed, published repos/branches must not be rebased. That's what receive hooks are for: to reject non-fast-forward merges. If you don't understand a word of the previous sentence, don't worry: you're just not ready to run a public repo.
What is dangerous is:
- pruning repositories
- lack of pushable (and pullable) reflogs
Reflogs are what makes it easy to recover if you lose a symbolic name and you're left with a pile of commits you'd not find your way through: the reflog tells you what you did and gives you the commit hashes you need to find your way home.
I often push from detached head state, and I did so today. Here's what I did:
$ git remote add contributor https://github.com/contributor/project
$ git fetch contributor
$ git checkout contributor/their-branch
$ git rebase -i origin/master
<fix up some things>
$ git push origin HEAD:master
$ git checkout master
$ git merge origin/master
Yes, that's advanced usage, and in particular the push command-line can be dangerous, so learn exactly what "git push origin HEAD:master" means before copying that, but compare now to the crutches equivalent of the above: $ git remote add contributor https://github.com/contributor/project
$ git fetch contributor
$ git checkout contributor/their-branch
$ git checkout -b their-branch
$ git rebase -i master
<fix up some things>
$ git push origin master
$ git checkout master
Or, maybe I could have told the contributor to rebase and fix those things. Or maybe I could have merged the contribution and then committed said fixes. But I like clean history -- I've been taught to by the very best.I didn't need a local branch crutch to find my way around because I know the model: a tree of commits.
Understanding the model is the key.
There are other VCSes that also use Merkle hash trees. Internally they have the power that git has. Mercurial's language for expressing refs is great. Fossil has all the power of SQL, and it shows. But they all try oh so hard to hide the model from me. It's not like hiding what a process is from a non-developer user. SourceTree does a great job allowing non-developers to understand their version trees, so I know this is not too hard on non-developers. Yes, non-developers should mostly never rebase, but detached head state is useful for them: because sometimes they need to "see" old versions, so they end up branching, and so they end up merging, and guess what: merging is what's not easy for them.
If you want to help non-developers use a VCS, give them a better UI for dealing with diffs. Don't punish the rest of us by hiding the wrong thing from everyone.