npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

github-update-submodule

v1.2.2

Published

Recursively pull all Git submodules to their latest remote commit and push updated refs up every parent repo — so GitHub always points to the latest.

Downloads

537

Readme

github-update-submodule

Recursively pull all Git submodules to their latest remote commit and push the updated refs up every parent repo — so GitHub always points to the latest commit in every submodule, no matter how deeply nested.


The Problem

Git submodules store a commit pointer (a hash) in the parent repo. When a submodule gets new commits, the parent's pointer goes stale — GitHub still shows the old commit until someone manually updates and pushes it. With deeply nested submodules this becomes a nightmare to manage by hand.

GitHub (parent repo)  ──pins──▶  old commit  ❌
Your local submodule             latest commit ✅

The Solution

One command from any repo with submodules:

github-update-submodule

Everything is handled automatically — pull, commit, push — all the way down the tree and back up again.


Installation

npm install -g github-update-submodule

Usage

# Run from your root repo — pulls + commits + pushes everything
github-update-submodule

# Preview what would change without touching anything
github-update-submodule --dry-run

# Confirm each repo before pushing
github-update-submodule --interactive

# Fetch all submodules at the same time (much faster on large trees)
github-update-submodule --parallel

# Skip specific submodules
github-update-submodule --ignore frontend --ignore legacy-lib

# Local update only, no push
github-update-submodule --no-push

Options

| Flag | Description | |---|---| | --no-push | Pull locally only, skip commit and push | | --interactive | Show a diff and ask yes/no before pushing each repo | | --ignore <name> | Skip a submodule by name. Repeatable: --ignore a --ignore b | | --parallel | Fetch all submodules concurrently (huge speedup on large trees) | | --dry-run | Preview all changes — nothing is modified | | --message <m> | Custom commit message (default: chore: update submodule refs) | | --branch <b> | Default branch if not declared in .gitmodules (default: main) | | --depth <n> | Limit recursion depth | | --verbose | Show full git output for every operation | | --no-color | Disable colored output | | --no-progress | Disable the progress bar | | --make-config | Generate a submodule.config.json in the current repo with all defaults, then exit |


Config File

Run --make-config once inside your repo to generate a pre-filled submodule.config.json with all available keys and their defaults:

github-update-submodule --make-config

This creates submodule.config.json in the current directory and prints a description of every key. Edit the values to set your preferred defaults — CLI flags always override the config file.

Example generated file:

{
  "defaultBranch": "main",
  "parallel": false,
  "ignore": [],
  "commitMessage": "chore: update submodule refs",
  "interactive": false,
  "verbose": false,
  "color": true,
  "progress": true
}

All config keys match the CLI flag names (camelCase, without --):

| Key | Type | Default | |---|---|---| | push | boolean | true | | interactive | boolean | false | | ignore | string or string[] | [] | | parallel | boolean | false | | commitMessage | string | "chore: update submodule refs" | | defaultBranch | string | "main" | | maxDepth | number | unlimited | | verbose | boolean | false | | color | boolean | true | | progress | boolean | true |


How It Works

Phase 1 — Pull

For each submodule (recursively, depth-first):

  1. Initialises any submodule that hasn't been cloned yet
  2. Fetches from origin (in parallel if --parallel is set)
  3. Resolves the correct branch: .gitmodules declaration → remote HEAD → --branch flag
  4. Runs git checkout -B <branch> origin/<branch> to hard-move to the remote tip
  5. Stages the updated pointer in the parent with git add <path>
  6. Prints a clickable GitHub compare URL for every submodule that changed:
    ⎘ https://github.com/org/repo/compare/abc12345...def67890
  7. Recurses into the submodule's own submodules

Phase 2 — Commit & Push

Walks the tree innermost → outermost:

  1. For each repo with staged changes, optionally shows a --interactive diff prompt
  2. Commits with the configured message
  3. Pushes to origin/<branch>
  4. Moves up to the parent and repeats

The innermost-first order guarantees that by the time GitHub receives a pointer update from a parent, the commit it points to already exists on the remote.


Progress Bar

In sequential mode (default) a live progress bar tracks the fetch phase:

[████████████░░░░░░░░░░░░░░░░]  43% (6/13)  frontend

In --parallel mode the bar advances as each concurrent fetch completes.


Example Output

╔══════════════════════════════════════════╗
║   github-update-submodule  v2.0.0        ║
╚══════════════════════════════════════════╝

› Repository     : /projects/my-app
› Default branch : main
› Push mode      : ON
› Interactive    : OFF
› Parallel fetch : ON
› Ignoring       : legacy-lib

Phase 1 — Pull all submodules to latest remote commit

Parallel fetching 12 submodules…
[████████████████████████████]  100% (12/12)

▸ QuantumDocsSyncer  (docs/QuantumDocsSyncer)
  › Branch: main
  ✔ Updated  d11a9fce → 4a82bc91
  ⎘ https://github.com/org/QuantumDocsSyncer/compare/d11a9fce...4a82bc91
  ▸ frontend  (frontend)
    › Branch: main
    ✔ Updated  fe03e5be → 9c14d7aa
    ⎘ https://github.com/org/frontend/compare/fe03e5be...9c14d7aa
  ▸ backend  (backend)
    › Branch: main
    ✔ Already up to date (b6732bc5)
⊘ legacy-lib  (ignored)

Phase 2 — Commit & push updated refs (innermost → root)

› QuantumDocsSyncer — committing on main…
↑ QuantumDocsSyncer → origin/main
› my-app — committing on main…
↑ my-app → origin/main

─────────────────────────────────────────
Summary
  ✔ Updated    : 2
  · Up to date : 1
  ↑ Committed  : 2
  ↑ Pushed     : 2
  ⊘ Ignored    : 1
  ⚠ Skipped    : 0
  ✘ Failed     : 0
    Total      : 4  (8.31s)

Interactive Mode

With --interactive, the tool pauses before pushing each parent repo and shows a staged diff summary:

 docs/QuantumDocsSyncer | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

  Push 'my-app' → origin/main? [y/N]

Type y to push or anything else to skip that repo.


Requirements

  • Node.js >= 14
  • Git installed and in your PATH
  • Remote authentication set up (SSH keys or credential manager) so pushes don't require a password prompt

License

MIT