I don't know if it is the best way to do it... but I'll explain how we do it. One or more developers work on a given branch and commit their code as often as they can in order to avoid wasting time on merge that would not have happened otherwise. Only when the code is ready is it committed into the head. Now that's for the commits and branch/head thing.
As for the code review, we use Sonar as our continuous integration tool (and Maven/Jenkins for interacting with Sonar) for providing us with fresh test results, code coverage, and automatic code review every morning (builds are done nightly) so that we developers can spend a maximum of one hour every morning to fix their problems/code smells. Each developer takes responsibility (being proud too!) for the feature he is writing. Now, that's automatic code review, which is great to find potential technical/architectural problems, but what is more important is to test if these new implemented features are doing what the the business wants them to do, properly.
And for that, there are two things: integration tests and peer code review. Integration tests help in being reasonable confident that the new code does not break the existing code. As for the peer code review, we are doing it on friday afternoons, which is a bit more relaxed time to do that :-)
Each developer is assigned to a branch he does not work on, takes some time to read the requirements of the new feature first, and then checks what has been done. His most important job is to make sure that the new code works as expected given the requirements, does not break our own "rules" (use this object for that, and not that one), is easy to read, and that it allows for easy extension.
So we have two code reviews, one automatic and one "human" and we try to avoid committing unreviewed code into the HEAD branch. Now... It does happen sometimes for various reasons, we're far from perfect, but we try to keep a fair balance between quality and cost (time!)
@pjz provides a good answer too, and he mentions code review tools. I'never used any, so I can't say anything about that... although I have been tempted in the past to work with Crucible since we're already using JIRA.