GitHub
Nested Repositories
Git
Version Control
Repository Management

How do I create nested repositories in GitHub?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

GitHub does not provide a special repository type called a nested repository. What people usually want is to keep one repository inside another project directory without losing the child project's independent history. In practice, that means choosing between Git submodules, Git subtree, or a simpler monorepo layout.

Decide What Kind of Boundary You Actually Need

Before creating anything, decide whether the child codebase is truly independent. If the inner project has its own releases, its own contributors, or needs to be reused by other repositories, keeping it separate makes sense. If the same team changes both codebases together all the time, a single repository is often the better design.

There are three common models:

  • 'submodule: the parent stores a pointer to a specific commit in another repository'
  • 'subtree: the parent vendors the child repository content into its own history'
  • monorepo: both codebases live in one repository and no nesting exists at the Git level

The wrong long-term solution is usually a plain folder that contains its own .git directory but is not registered as a submodule. That may appear to work locally, but it creates confusing behavior for teammates and CI.

Use a Submodule When the Child Repository Is Truly Independent

Submodules are the most explicit way to nest one repository into another. The parent repository records which exact child commit should be checked out.

bash
1git init app
2cd app
3git commit --allow-empty -m "Initial commit"
4
5git submodule add https://github.com/example/shared-lib.git vendor/shared-lib
6git commit -m "Add shared-lib as a submodule"

This creates a .gitmodules file and stages the submodule pointer in the parent repository. The child code is not copied into the parent history. Instead, the parent tracks one specific revision of the child.

When someone clones the parent repository, they should use:

bash
git clone --recurse-submodules https://github.com/example/app.git

If they already cloned without that flag, they can initialize everything afterward:

bash
git submodule update --init --recursive

Update a Submodule Intentionally

A submodule does not automatically follow the latest branch tip. Updating it is a deliberate change to the parent repository.

bash
1cd vendor/shared-lib
2git checkout main
3git pull
4cd ../..
5
6git add vendor/shared-lib
7git commit -m "Update shared-lib pointer"

This explicit pointer update is a feature, not a bug. It makes builds reproducible because the parent repository always records exactly which child revision it expects.

Use Git Subtree for Easier Cloning

If your team dislikes the extra setup that comes with submodules, git subtree can be easier operationally. With subtree, the child repository content is copied into the parent repository history.

bash
git remote add shared-lib https://github.com/example/shared-lib.git
git subtree add --prefix=vendor/shared-lib shared-lib main --squash

To pull later updates:

bash
git subtree pull --prefix=vendor/shared-lib shared-lib main --squash

The advantage is that a normal clone of the parent repository contains everything. The tradeoff is that the parent now vendors the child code instead of referencing it externally.

Prefer a Monorepo When the Split Is Artificial

Sometimes the desire for nested repositories is really a symptom of over-separating a codebase. If the same team owns both sides, changes must be atomic, and release cadence is shared, then one repository with clearly named folders is simpler.

For example, a monorepo might look like this:

text
1app/
2  services/api
3  packages/ui
4  tools/build

That keeps code organization without introducing submodule workflows or split release management.

Configure GitHub and CI to Match the Model

If you choose submodules, your automation must check them out. In GitHub Actions, enable submodule checkout explicitly.

yaml
1steps:
2  - uses: actions/checkout@v4
3    with:
4      submodules: recursive

Also pay attention to the submodule URL. HTTPS is often easier for CI because it avoids SSH key setup. Whatever workflow you choose, document clone and update steps in the README so new contributors do not see empty child directories and assume something is broken.

Common Pitfalls

The biggest mistake is treating a raw nested .git directory as if GitHub will manage it automatically. It will not. The parent repository may ignore that folder in unexpected ways, and collaborators will have no standard instructions for cloning it.

Another common issue is forgetting that submodules store commit pointers, not branch subscriptions. Pulling new commits in the child repository does nothing for the parent until the parent stages and commits the new pointer.

Teams also overuse submodules when they actually need monorepo-style atomic changes. If every feature touches both repositories together, the nesting boundary is likely working against the team rather than helping it.

Summary

  • GitHub nested repositories are usually implemented with submodules or subtree, not with an unmanaged inner .git directory.
  • Use a submodule when the child project has its own lifecycle and should stay independently versioned.
  • Use subtree when you want a simpler clone experience and are willing to vendor child content.
  • Use a monorepo when both codebases change together and do not need separate release boundaries.
  • Document the chosen workflow so local development and CI behave the same way.

Course illustration
Course illustration

All Rights Reserved.