Perforce branches the right way. In CVS and many other tools the branch is part of the file. The file src/configuration.cpp may have thirteen branches, one for each release and one for main development, and they'll have version numbers like configuration.cpp#220.127.116.11.5.
By contrast, in perforce the branches are (simplifying a little) directories. At Oryx we have one directory/branch called src/rel/0.90, one called src/rel/0.91, one called src/main, etc. When src/rel/0.90 was branched from src/main, perforce learned that the file src/rel/0.90/core/configuration.cpp was branched from src/main/core/configuration.cpp.
IMHO, that's a much better and more natural way to represent branches than what CVS does.
Perforce notifies me before a colleague and I collide. If I'm about to edit configuration.cpp and someone else is also doing that, perforce will tell me that in advance. CVS says nothing.
Both systems let me edit the file. The difference is that CVS expects me and my colleagues to communicate infallibly, while Perforce gives me a warning when I need one.
Perforce deals well with conflicts. When there's a conflict, for example because both I and a colleague have added a function to the end of configuration.cpp, perforce doesn't require me to fix that at once.
With CVS, as soon as I type
cvs update I have to deal with all the conflicts. With perforce,
p4 sync updates all the files I'm not working on and tells me
hey Arnt, before you submit you have to deal with this conflict. Later I can type
p4 resolve -am to have perforce try its automatic resolve, or
p4 resolve to do it myself.
p4 sync doesn't interrupt my train of thoughts. I can finish what I'm doing, then deal with the conflicts.
Perforce remembers the branch relationships as they evolve. Once a fix from src/main/core/configuration.cpp has been integrated onto src/rel/0.90/core/configuration.cpp, perforce remembers that. It's really easy to answer questions like
what has been done on the main branch and not yet integrated into 0.90? And if I don't want a particular change to be integrated into 0.90, perforce remembers that too.
Perforce has atomic changes. Either a submit completes or it doesn't, and when it finishes it has a convenient identifier. It's easier to say
uh, I fixed that bug in change 5122 than to talk about revisions configuration.cpp#18.104.22.168.5 and configuration.h#22.214.171.124.5.
Perforce is fast. That really makes a difference.
p4 sync can take a second where
cvs update takes a minute. Because it's so fast, perforce doesn't get in the way, so it gets used it all the time.
Perforce has excellent support. It's not free. (Well, for open-source projects it is, but anyway.) But it's good. For something as valuable as the source code depot, I enjoy the feeling that support is capable, friendly and fast.
Last but not least, Perforce Software, Inc. provides a guarantee. In the cupboard to my left, there's a contract in which Perforce Software, Inc. promises that the software will behave as the manual says. I like that a great deal better than the usual disclaimer.
Update: FAQs soon appeared.
What don't I like about perforce? A couple of things. If a file is renamed or moved on one branch, integrating changes between that file and its relatives on other branches is too much bother. Not as bad as CVS, but definitely not as good as I'd expect from perforce. Second, perforce has no IPv6 support. That may not matter to you, but I use IPv6.
What about Subversion? How does that compare? Subversion seems to be (in my limited understanding) about the same as CVS wrt. most of the points above, better than CVS in a few respects, and worse in one (speed when used across a WAN). I'm not tempted to migrate.