I think the issue here is a mismatch between the design of Git and the problem you are looking to solve.

Git is good for keeping track of Trees. Dependency relationships between projects can (and likely do) form a Graph. A Tree is a Graph but a Graph is not necessarily a Tree. Since your problem is how to effectively represent a Graph, a Tree is not the best tool for the job.

Here's an approach that might work:

A git project has a .gitmodules directory where it records "hints" stating which projects a commit may depend on, where they can be found, and what path inside the project they are expected to be inserted at. ( http://osdir.com/ml/git/2009-04/msg00746.html )

You could add a script which reads this information from a set of projects, maps the hints found in each project's .gitmodules file to the locations on the filesystem where those projects have actually been placed, and then adds symbolic links from the paths where git expects to check out submodules to the actual filesystem locations of the respective projects.

This approach uses symbolic links to break out of the Tree mold and build a Graph. If we record the links directly in git repos, we'd have relative paths specific to our local setup recorded in the individual projects, and the projects wouldn't be 'fully independent' like you wanted. Hence, the script to dynamically build the symlinks.

I'm thinking this approach might interfere with git in undesirable ways, since we've taken paths where it expects to find one thing, and put something else there instead. Maybe we could .gitignore the symlink paths. But now we're writing those paths down twice and violating DRY. At this point we've also gotten pretty far away from pretending to use submodules. We could record the dependencies elsewhere in each project, and leave the .gitmodules file for the things git expects. So we'll make up our own file, say, .dependencies, and each project can state its dependencies there. Our script will look there and then go and build its symlinks.

Hmm, I think I may have just described an ad-hoc package management system, with its own lightweight package format :)

megamic's suggestion seems like a good use of git submodules to me. We're only dealing with keeping track of a Set here rather than a Graph, and a Set fits easily into a Tree. A Tree one level deep is essentially a parent node and a Set of child nodes.

As you pointed out, that does not completely solve the problem stated in your question. We can break out two distinct types of "this works with that" information we're likely interested in: 1. A statement from a version of a project (presumably by the project's author) saying "I require version X of project Y" 2. A statement used by your own build setup saying "I've successfully tested our whole system using this set of project versions"

megamic's answer solved (2) but for (1) we still want projects to tell us what their dependencies are. Then we can use the info from (1) to compute those version sets which we'll end up recording as (2). This is a complex enough problem to warrant its own tool, which brings us back to package management systems :)

As far as I know, most of the good package management tools are made for users of a specific language or operating system. See Bundler for 'gem' packages in the ruby world and apt for '.deb' packages in the Debian world.

If anyone knows of a good language-neutral, OS-neutral solution to this that is well-suited to 'polyglot' ( http://blog.heroku.com/archives/2011/8/3/polyglot_platform/ ) programming projects, I would be very interested! I should post that as a question.