I have used Perforce for a long time, and so my comments may be a little Perforce-centric, but the basic principles apply to any SCM software that has half decent branching.
I'm a very strong believer in using branched development practices. I have a "main" (aka "mainline") that represents the codebase from now to eternity. The aim is that this is, most of the time, stable and, if push came to shove, you could cut a release anytime that would reflect the current functionality of the system. Those pesky sales guys keep asking....
Developments happen in branches that are branched from MAIN (normally - occasionally you may want to branch from an existing dev branch). Integrate from MAIN to your dev branches as often as you can, to stop things diverging too much - or you can simply budget for a bigger integration period later. Only integrate your arse kicking new feature to MAIN when you are sure that it will go out in a forthcoming release.
Finally, you have a RELEASE line, which the option of different branches for different releases. There's some choices depending on the labelling capabilities of your SCM software,and how different major/minor revisions are likely to be. So you may opt, for example, for a release branch for every point release, or only for major rev number. Your mileage may vary.
Generally, branch from MAIN to release as late as possible. Bugfixes and last minute changes can either go straight into RELEASE for later integration to MAIN, or into MAIN for immediate integration back up. There's no hard and fast rule - do what works best. If, however, you have changes that may be submitted to MAIN (e.g. from a dev branch, or "little tweaks" by someone on MAIN), then do the former. It depends on how your team works, what your release cycles are etc.
E.g. I would have something like this:
//MYPROJECT/MAIN/... - the top level folder for a complete build of all the product in main.
//MYPROJECT/DEV/ArseKickingFeature/... - a branch from MAIN where developers work.
//MYPROJECT/RELEASE/1.0/...
//MYPROJECT/RELEASE/2.0/...
A non-trivial project will probably have a number of DEV branches active at once. When a development has been integrated into MAIN so that it is now part of the core project, kill off the old DEV branch as soon as you can. Many engineers will treat a DEV branch as their own personal space, and reuse it for different features over time. Discourage this.
If, after release, you have to fix a bug, then do that in the corresponding release branch. If the bug has been previously fixed in MAIN, then integrate across, unless the code has changed so much in MAIN the fix is different.
What really differentiates the codelines is the policies you use to manage them. For example, what tests get run, who reviews pre/post a change, what action happens if a build breaks. Typically policies - and therefore overhead - are strongest in release branches, and weakest in DEV. There's an article here that goes through some scenarios, and links to other useful things.
Finally, I recommend going with a simple structure to start with, and only introduce extra dev & release ones as needed.
Hope that helps, and is not stating-the-bleedin'-obvious too much.