When I started to develop a huge php project consisting of several composer (= php dependency manager) modules which I wanted to develop along with a core project I spent quite a while researching on how to master that. To hopefully save you some time I would like to share my approach here.
Although the following example focuses on organizing composer specific projects the idea is valid and suitable for all kinds of projects consisting of sub modules which need to be developed in parallel and to be organized in their own git repository.
Just one quick example: A content management system with plugin support. You might want to be able to develop a bunch of standard plugins along with your cms core application so that you can test them instantly before publishing them. You might not want the plugins to be bundled with your cms core application but to have them separated in a dedicated git repository so that everybody can use it independently from the core application.

My requirements

Changes in a module are instantly available to other modules without pushing the according changes of one module and pulling it back to the top level project or any dependent sub module (ensures testability before committing and pushing changes)

A clean and uninterrupted git history for each component as well as the top level project itself

Development of top level project including a set of independent sub modules organized in a single IDE project

Since composer requires each module (the top level project as each nested composer project) to have its own git repository I tried to find a suitable way of nesting git repositories. I don’t want to open the “why you do not want to use git submodules” topic again since there are plenty of resources on the internet explaining the disadvantages very well. However git submodules was not what I wanted to use. I found git subtrees but they were not very popular at that time when the project started so it was hard to find some information on how I could benefit from using them. But I figured out that git subtrees was exactly what I needed.

git subtrees

Split up existing repositories into smaller repositories while keeping the entire and specific history for that tree

Add existing subtrees to an existing project or start developing new components from scratch inside your top level project

The project structure

It is very important to keep the single components separated like you would do when developing the component in its own project scope. That does not mean they cannot use other components but they need to use it in the way your dependency manager defines. In terms of PHP and composer that means:

Keep the components inside their own namespace

Add interdependencies to other components to the according ComponentX/composer.json

Adding/pushing/pulling component sources

I actually doesn’t matter whether you start from scratch, you already have the top level project and only need to add sub modules or whether you already have an existing top level project and some sub modules organized in git repositories. You can apply the described mechanism in any state of your development process.

Starting from sratch (no existing sources)

1) Initialize a top level project repository in your project root directory: git init
2) Develop your core application as well as the single components inside your top level project
3) Push your changes to the core repository and each component into its own component repository:

Tagging components

To be honest tagging is the only thing which is not very handy since you have to split the components first into its own branch, tag it accordingly, push the tags and remove the splitted component branch:

Automating the daily work

In order to automate my daily work I wrote the following little PHP cli scripts which push changes into a specific branch and create a tag if needed. Feel free to use it and let me know if it works for you.

1) Create a component.json containing a component directory mapping
2) Create a push.php (code given below) and save it along with your components.json (i.e. in bin/git/push.php)
3) Call the script from your main repository, i.e.:cd /home/dev/corephp ./bin/git/push.php [branch_name] [tag_name]default of [branch_name] is master; no tag given means do not tag