NEW The Byte 404 HTTP Status Code Lookup Tool is now live! Launch Tool →
ERROR FIXES // GIT & VERSION CONTROL

Git Rebase Conflict Submodules — How to Resolve It Quickly

Published: July 1, 2026 • Written by Alex Rivera • Read Time: 14 min • Word Count: 2,120 words

A stylized Git branch diagram showing a rebase conflict with a red warning badge labeled Submodule Conflict

1. Introduction: The Git Submodule Nightmare

In modern software engineering, modularity is key. We break large codebases into smaller, reusable packages, libraries, or microservices. Git provides a native mechanism to handle this called **Git Submodules**, which allows you to keep a Git repository as a subdirectory of another Git repository.

While submodules are incredibly useful for managing shared dependencies, they are also notorious for causing complex version control issues.

The absolute peak of submodule frustration occurs during a **Git Rebase**. You run `git rebase main` on your feature branch, only to be greeted by a cryptic merge conflict inside a submodule directory. Unlike standard code conflicts where you can open a file, resolve the conflict markers (`<<<<<<<` and `>>>>>>>`), and commit, submodule conflicts present themselves as conflicting **git commit hashes** (gitlinks).

If you don't know exactly how Git tracks submodules, you can easily end up in a loop of broken references, detached HEAD states, or accidentally reverting your team's changes.

In this guide, we will demystify submodule conflicts during a rebase. You will learn why these conflicts occur, how Git tracks submodule states, and a bulletproof, step-by-step workflow to resolve them quickly and safely in 2026.

2. Why Submodule Conflicts Occur During Rebase

To understand why submodule conflicts occur, we must understand how the parent repository (the "superproject") tracks a submodule.

A Git submodule is **not** a copy-paste of files. Instead, the parent repository tracks a submodule using two pieces of information:

  1. The `.gitmodules` file, which maps the local directory path to the remote repository URL.
  2. A special directory entry called a **gitlink** (mode `160000`), which is simply a pointer to a **specific commit hash** in the submodule's repository history.

When you perform a rebase, Git attempts to re-apply your feature branch commits on top of the target branch (e.g., `main`). If someone else has updated the submodule pointer on `main` to point to commit `AAAAAA`, and your feature branch has updated that same submodule pointer to point to commit `BBBBBB`, Git does not know which commit hash should win.

Because Git cannot automatically merge two different commit hashes, it stops the rebase and declares a **submodule merge conflict**.

3. Anatomy of a Submodule Conflict

When a submodule conflict halts your rebase, running `git status` will output something like this:

rebase in progress; onto 1a2b3c4
You are currently rebasing branch 'feature/api-upgrade' on '1a2b3c4'.

Unmerged paths:
  (use "git restore --staged <file>..." to unstage)
  (use "git add <file>..." to mark resolution)

	both modified:   libs/shared-utils

If you attempt to open `libs/shared-utils`, you will find it is a directory, not a file, and contains no conflict markers.

To inspect the conflicting hashes, you must run `git diff`:

$ git diff libs/shared-utils
diff --cc libs/shared-utils
index aaaaaaa,bbbbbbb..0000000
--- a/libs/shared-utils
+++ b/libs/shared-utils
@@@ -1,1 -1,1 +1,1 @@@
- Subproject commit aaaaaaa... (Commit on main)
 -Subproject commit bbbbbbb... (Commit on your feature branch)

This output tells you exactly what is happening: the parent repository is conflicted between pointing to commit `aaaaaaa` (from the base branch) and commit `bbbbbbb` (from your feature branch).

In-Content Image Placement

Step-by-step Git command flowchart for resolving submodule conflicts

4. Step-by-Step Resolution Workflow

When a submodule conflict occurs, follow this precise, 4-step workflow to resolve it quickly and cleanly:

Step 1: Determine the Correct Submodule State

First, decide which commit hash the submodule should point to:

  • Use Theirs (Base Branch): If you want to discard your feature branch's submodule updates and keep the version from `main`.
  • Use Ours (Your Feature Branch): If you want to keep your feature branch's submodule updates.
  • Merge Within Submodule: If you need to merge changes *inside* the submodule itself, create a new commit inside the submodule, and point the parent repository to that new merged commit.

Step 2: Checkout the Target Commit Hash

Use Git's checkout command with the `--theirs` or `--ours` flag to instantly resolve the pointer conflict in the parent repository:

# To keep the version from the base branch (main)
git checkout --theirs path/to/submodule

# To keep the version from your feature branch
git checkout --ours path/to/submodule

Step 3: Update the Submodule Files

After updating the pointer in the parent repository, you must tell Git to sync the actual files inside the submodule directory to match that chosen commit hash:

git submodule update --init --recursive

Step 4: Stage the Resolution and Continue Rebase

Once the submodule pointer and files are synced, stage the resolution and resume your rebase:

git add path/to/submodule
git rebase --continue

5. Practical Real-World Example: Merging Submodule Changes

What if both branches made critical, non-overlapping changes inside the submodule, and you need to keep **both**?

Here is the advanced workflow to perform a merge inside the submodule itself and update the parent repository:

# 1. Navigate into the submodule directory
cd path/to/submodule

# 2. Fetch all remote changes for the submodule
git fetch origin

# 3. Merge the conflicting branches inside the submodule
git merge origin/main

# 4. Resolve any code conflicts inside the submodule files, then commit
git add .
git commit -m "merge: integrate main updates into feature branch"

# 5. Push the merged commit to the submodule's remote repository
git push origin HEAD:feature-branch-name

# 6. Navigate back to the parent repository root
cd ../..

# 7. Stage the new merged submodule pointer in the parent repository
git add path/to/submodule

# 8. Continue the parent repository's rebase
git rebase --continue

6. Best Practices & Prevention

To prevent submodule conflicts from disrupting your team's development velocity, implement these best practices:

  • Always Sync Submodules on Pull: Configure your global Git settings to automatically update submodules whenever you pull changes from the remote:
    git config --global submodule.recurse true
  • Avoid Accidental Submodule Commits: Developers often run `git commit -a` and accidentally commit an outdated submodule pointer because they forgot to run `git submodule update`. Always review your staged changes using `git diff --cached` before committing.
  • Use Submodule Branches: Pin your submodules to specific tracking branches rather than detached commit hashes. This allows Git to handle updates more predictably:
    git submodule add -b main [repository-url] path/to/submodule

7. Conclusion: Master Your Version Control

Git submodules are a double-edged sword. They offer clean dependency boundaries but require a deep understanding of Git's internal tracking mechanisms to manage successfully.

By mastering the `--ours` and `--theirs` checkout workflows, you can resolve submodule conflicts during a rebase in seconds, keeping your repository clean and your development team moving forward without interruption.

To bootstrap a clean, optimized repository setup for your next project, use our interactive .gitignore Generator, or check out our guide on VS Code Profiles Setup to customize your editor for Git workflows.

Alex Rivera

About the Author: Alex Rivera

Founder & Editor-in-Chief, The Byte 404

Alex is a former Senior Systems Architect at Netflix and Stripe with over 15 years of experience building high-throughput distributed APIs. He writes about distributed systems, backend performance, and AI-native engineering workflows.