Git features the "rebase" command which can be used to change the sequence of check-ins in the repository. Rebase can be used to "clean up" a complex sequence of check-ins to make their intent easier for others to understand. From another point of view, rebase can be used to "rewrite history" - to do what Winston Smith did for a living in Orwell's novel 1984.
This is FUD. Git never erases history; it merely moves mutable refs around. When you rebase a branch called "master", nothing is rewritten: a new immutable commit history is created, and the ref "master" is moved from pointing at the old history to pointing at the new history. The old history still exists in all its glory at "master@{0}". (And when you change that, it goes to master@{1}, and so on.)
If git didn't have the concept of refs, then this would never even worry anyone. Once you have commit ab387df, it's refers to the same sequence of changes for all eternity. What master points to may change out from under you, but the history never goes anywhere.
The only way to "rewrite history" is to delete every copy of the repository ever made, which is exactly how you would rewrite history with Fossil or Subversion or anything.
(And now, if I may, a digression. I've noticed that the "rewriting history" aspect of Git makes for a good personality test. It's strongly polarizing -- the people that think source control is designed to be documentation love it, and the people that think source control is an audit mechanism hate it. Control freaks hate git, and long-haired hippies seem to love it.)
I'm not familiar with other DCVS tools, but I have delved pretty heavily into the history rewriting capabilities of git. Git is just a power tool. It won't ruin your day like rm -rf will, but it will give you enough rope to rappel your way down into ontological horror.
I have been using it to write extended tutorials on my local system. I may find a bug at step 45 that needs to be fixed back at step 2. I am pretty sure that git was never designed for me to do this, but it can be done pretty safely, in a way that I can recover from.
Now, is this method of going back and fixing things in the past a good idea for general software development? Absolutely not! If I wrote a bug in march, it serves the team rather poorly to go back and replace every commit in the master branch with a new one that doesn't have the bug. And that's why I feel it's accurate to call it rewriting - sure, git keeps enough data around for you to recover from any boneheaded edit you may make to a branch, but from the perspective of a teammate you're still going back and modifying history. It's still confusing. You're still creating new commits out of their commits that have the same name, but different contents.
You're not looking at the possibility that someone may do a bunch of
development locally, then use rebase to clean it (the local changes)
up before pushing it out to a central repository (or merging it into
master). If you're really paranoid that someone will use rebase to screw up master, you can use a tool like gitosis or gitolite to manage permissions on a central repository. You can say that no one has permissions to push things to master that are not fast-forwards.
Scenario #1:
I'm working on a feature branch. I have a number of discrete changes
that I'm making. I attempt to keep each of these changes in its own
separate commit. After completing 10 such changes, I realize that I
introduced a bug in change 3. I commit the bugfix with the same
commit message title as change 3, but with a "squash!" prefixed at
the front of it. Now when I'm finished with my changes on the
feature branch I can run "git rebase -i". git-rebase will
automatically position the bugfix to change 3 next to change 3 and
set it up to be squashed into the change 3 commit. Then when I merge
my changes into master there are only commits for changes 1-12,
instead of 50 commits covering all of the minor bugs that I fixed
with my undeployed code.
Scenario #2:
Rewriting commit messages. This is one that I use all of the time at
work. I use a commit message template that has "Reviewed-by: ???" at
the bottom. When I do all of be topic branch development, I don't
get someone to review each individual commit prior to commiting it
(as that would defeat the purpose of a DVCS... might as well go back
to SVN at that point and managing chunks of changes prior to commits
with something like `quilt`). So now all of my commit messages have
"Reviewed-by: ???" on them. Prior to merging the changes back into
master, I can use git-rebase or git-filter-branch to go back and
edit my history to change "Reviewed-by: ???" to "Reviewed-by: Bob"
on my commit messages.
Often when pairing we'd commit with messages like "WIP: 1" "WIP: 2" etc. push pull switch machines (remote pairing) get checkpoints in there. Then before pushing to the main branch we'd roll all that up into one commit with a very helpful message. Boom solves a great workflow problem.
I don't use rebase and I can't say that I like it, but that doesn't make me hate git. I commit all my crappy and stupid intermediate code (for my personal projects).
When you keep the change history clean, it's a lot easier to answer questions like "why did I do that?", and it's a lot easier to explore topic branches and back out bad design ideas.
If the history is messy, removing bad changes is about as difficult as opening up every file and removing the changes you don't want. In other words -- error prone.
Git gives you granular control of every aspect of itself. When you REALLY need it, it works out. Insane history rewrites make the full team have to propperly sync. Rebase is an amazing tool for getting your repo up-to-date and not making someone else when looking through history have to worry about mix-matching commits to see exactly how one branch turned out. Its personal prefernece. In the end of the day, git will not let you cover your tracks.
Anecdote:
I fucked up some commits. I put in wrong conmiter names and so on, omitted some data in some of my commits. With git I could clone my repo, experiment with rewriting commits to fix conmiter names, and then merge it to everything. Yes history is changed, but end of the day this is not about hiding shit. This is about keeping the repo information useful and simple. If you want to blame shit on people, there are external systems which will ensure that people will get blamed for their own shit. VCS is not for that, VCS is to version your code, be able to un-fuck yourself up, and figure out why someone did something and how they did it exactly at some point in time. Nowhere in the is "blame people for mistakes" seen.
I've seen people not want to get blamed or use VCS. They used a word document to version their source. Yes. And I assure you it took people lots of time to realize wtf is going on.
This is FUD. Git never erases history; it merely moves mutable refs around. When you rebase a branch called "master", nothing is rewritten: a new immutable commit history is created, and the ref "master" is moved from pointing at the old history to pointing at the new history. The old history still exists in all its glory at "master@{0}". (And when you change that, it goes to master@{1}, and so on.)
If git didn't have the concept of refs, then this would never even worry anyone. Once you have commit ab387df, it's refers to the same sequence of changes for all eternity. What master points to may change out from under you, but the history never goes anywhere.
The only way to "rewrite history" is to delete every copy of the repository ever made, which is exactly how you would rewrite history with Fossil or Subversion or anything.
(And now, if I may, a digression. I've noticed that the "rewriting history" aspect of Git makes for a good personality test. It's strongly polarizing -- the people that think source control is designed to be documentation love it, and the people that think source control is an audit mechanism hate it. Control freaks hate git, and long-haired hippies seem to love it.)