The one first thing you should do first is to make sure that your component structure (the way you have structured your program into loosely coupled libraries) mirrors your requirements.
Things go a lot easier when you have the common code in one or more separate libraries, and the other code too (but clearly separated from the common code). Then, you can handle your common code like a separate product, and your different "versions" (as you call them) as separate programs, each one using a specific revision of the common library.
Said this, both approaches (separate repositories or one big single repository) might work. If your different "versions" have to be really separate, because they are for different customers with different support agreements, IMHO separate repositories will work better (one for each product and one for you common library), because you can more easily avoid unwanted side effects between the different versions. On the other hand, if the situation is different, having all things in one repository can save you a lot of administrative work, for example, when you often have to move code from the common libary to one of the other components, or vice versa.
For example, in our company, we have one repository for a software product sold to other companies, and one for our in-house software, Obviously, we have very different support needs between those two types of software. When we got to the point where we needed to reuse a library from our product in the in-house code, we wrote some simple scripts to check out a specific revision of the library and copy it to the build environment of the in-house code. We try to avoid too much influence from the in-house software development to the product development, but when there is a requirement from in-house that belongs clearly to the common library, we add it there, test it in combination with our product, and when it works, we update our script to get the new revision.