I use mercurial subrepos. I think the official advice of “have a thin super project with all real code in subrepos” (as opposed to eg a project repo with code and a subrepo in it for some library) is quite key.
Subrepos are checked out automatically.
Pushing the super project ensures sub projects it references are pushed first.
It will prevent you from committing the super project state when a subrepo has uncommitted changes (and tell you which subrepo)