We use branches and tags for release-specific work followed by the actual release, respectively:
o---o-----o---o---o--- ... master
\ / /
\ / /
o-------o--- ... 1.6 branch
Every developer makes a mental decision about whether the work they're about to commit is applicable just to master or if it's also relevant to the branch. You can see that changes that are made to the branch are merged back on master, but some changes on master will never go on the branch (that is, those not intended for the 1.6 release, in this example).
When we're ready to release, we tag it and then merge back one last time, and we name the tag with the same name as the branch, but with an extra identifier about what particular version it is, e.g. "1.6-release" or "1.6-beta" or "1.6-rc2", et cetera.
... ------o---o---o--o---o--- ... master
/ /
/ /
... ---o------(*)--- ... 1.6 branch
1.6-release