Skip to main content

Vendor third-party Git repositories into your own, with path filtering and local patches.

Project description

git-third-party

git-third-party

Vendor third-party git content into your repo as ordinary files — no submodules.

Three surfaces, one Go core: a git-third-party CLI, Python bindings (import git_third_party), and Node bindings (import { add, list, ... } from 'git-third-party'). All three speak the same third-party.toml / third-party.lock config and share the same cgo bridge for in-process calls.

third-party.toml records each entry's source URL, the ref it tracks, and any filters applied. third-party.lock pins the resolved commits. git-third-party update re-fetches upstream and stages the changes.

Why

Submodules need a second clone, complicate CI, and hide third-party code from grep, build, and IDE tooling. Subtree merges lose provenance. git-third-party keeps content in-tree and visible to every tool, with enough metadata to update in one command.

Requirements

  • git
  • Go 1.21+ (build only)

Install

CLI

Prebuilt binaries cover linux-x64, linux-arm64, darwin-x64, darwin-arm64, and win32-x64. Pick whichever package manager you already have:

# PyPI (wheel ships the binary as a pip script):
uv tool install git-third-party
pip install --user git-third-party

# npm (main package shells out to the platform-specific binary):
npm install -g git-third-party
pnpm add -g git-third-party

Build from source:

go build -o ~/.local/bin/git-third-party .

The binary's git- prefix lets you invoke it as either git-third-party or git third-party.

Python bindings

uv tool install git-third-party    # or pip install
from git_third_party import init, add, list_, version

print(version())
init()
add(dir="vendor/foo", url="https://github.com/x/y", follow="main")
for e in list_():
    print(e.dir, e.commit)

Mutating calls take dry_run= and commit_msg=. All calls take repo_path= (default .). Errors map to GitThirdPartyError and its subclasses (ConfigError, NetworkError, ConflictError, CheckDirtyError).

Node bindings

npm install git-third-party     # or pnpm/yarn add
import { init, add, list, version } from "git-third-party";

console.log(version());
init();
add({ dir: "vendor/foo", url: "https://github.com/x/y", follow: "main" });
for (const e of list()) {
  console.log(e.dir, e.commit);
}

ECMAScript Modules (ESM) only, ships TypeScript types, requires Node ≥ 18. Same options shape as the Python API; same error hierarchy. The bridge serializes calls process-wide — for parallel work, use worker_threads.

Quick start

Vendor a directory tracking a remote branch:

git-third-party add third_party/zlib https://github.com/madler/zlib --follow master
git commit -m "Vendor zlib"

Pull updates:

git-third-party update                    # all entries
git-third-party update third_party/zlib   # one entry
git-third-party status                    # dry-run
git commit -m "Update vendored zlib"

List, rename, remove:

git-third-party list
git-third-party rename third_party/zlib vendor/zlib
git-third-party remove third_party/zlib

Tracking a ref

Each entry tracks exactly one of:

  • --follow <branch> — track the branch tip, re-resolving on every update. Default when neither flag is set (resolved from the remote's HEAD).
  • --pin <tag-or-sha> — pin to a tag (resolved once, then cached) or a commit SHA (used as-is). 40-hex-char strings count as SHAs.

Switch with git-third-party set <dir> --pin v1.3.1 (or --follow master).

Filtering content

Vendor part of an upstream repo:

git-third-party add vendor/foo https://example.com/foo.git \
    --subdir src \
    --include '*.c' --include '*.h' \
    --exclude 'tests/'
  • --subdir — start from a subdirectory of the upstream repo.
  • --include — keep only matching paths (repeatable; default keeps everything).
  • --exclude — drop matching paths (repeatable; always wins over --include).

Patterns follow gitignore rules with documented deviations; see git-third-party add --help for the spec. Upstream submodules inline recursively unless excluded.

Configuration files

Two files live at the repo root, both committed:

  • third-party.toml — hand-editable intent. Each [[third_party]] table is one vendored directory. Each add/set/rename/remove rewrites the file, dropping any user comments.
  • third-party.lock — generated. Records the resolved commit and any saved tree-patch per entry. Sorted by dir for stable diffs. Do not edit by hand.

Example third-party.toml:

# git-third-party — vendored content config.

[[third_party]]
dir = "third_party/zlib"
url = "https://github.com/madler/zlib"
follow = "master"

[[third_party]]
dir = "vendor/foo"
url = "https://example.com/foo.git"
pin = "v1.3.1"
subdir = "src"
include = ["*.c", "*.h"]
exclude = ["tests/"]

Corresponding third-party.lock:

# git-third-party lockfile — generated; do not edit by hand.
version = 1

[[third_party]]
dir = "third_party/zlib"
commit = "abc123..."

[[third_party]]
dir = "vendor/foo"
commit = "def456..."

Commands

Command Aliases Purpose
init Create an empty third-party.toml (most users skip this — add creates it).
add DIR URL Register a new entry and download it.
set DIR … edit Change URL, ref, or filters for an existing entry.
unset DIR FIELD… Clear subdir, include, or exclude.
update [DIR] up Re-fetch and stage updates.
status [DIR] st update --dry-run.
list [DIR] ls Show entries and their tracking mode.
info DIR show Print full details for one entry.
rename DIR NEW_DIR mv Move a vendored directory and update the config.
remove DIR rm Unregister a directory and git rm -r its content.
patch save DIR (experimental) Record local edits as a tree-patch.
patch diff DIR (experimental) Show the saved tree-patch via git diff.
completion SHELL Print a bash/zsh/fish/powershell completion script.

Common flags

  • -v, -vv, -q — debug, trace, or warn-only logging.
  • --log-level=trace|debug|info|warn|error — explicit level.
  • --log-format=text|json — switch the stderr handler to JSON.
  • --color=auto|always|never — honors NO_COLOR.
  • --dry-run — plan without staging.
  • -f, --allow-dir-exists — let add/rename write into a non-empty target.
  • --profile <path> — write a CPU profile.
  • --json — emit a structured entryResult (or array) on stdout instead of human text. Schema lives in output.go.
  • --commit MSG — run git commit -m MSG after the command stages changes.
  • --check (on update/status) — exit non-zero if any entry would change. Useful in CI and pre-commit hooks.

Exit codes

Code Meaning
0 success
1 generic failure
2 configuration invalid (TOML parse, validation, lockfile schema mismatch)
3 network, fetch, or ref-resolution failure
4 unresolvable merge conflict during update
5 --check detected a pending change

Settings

Settings resolve through five layers, each overriding the previous:

  1. Built-in defaults.
  2. Per-user: git config --global third-party.<key>.
  3. Per-repo: a [settings] table in third-party.toml.
  4. Environment variables.
  5. CLI flags.

Environment variables:

  • GIT_THIRD_PARTY_LOG_LEVEL (trace/debug/info/warn/error) — same as --log-level.
  • GIT_THIRD_PARTY_LOG_FORMAT (text/json) — same as --log-format.
  • GIT_THIRD_PARTY_COLOR (auto/always/never) — same as --color.
  • GIT_THIRD_PARTY_EXPERIMENTAL — comma-separated feature names; same as --experimental.
  • NO_COLOR — standard cross-tool convention; disables ANSI color even when --color=auto.

Experimental commands (currently the patch subtree) need explicit opt-in: --experimental=patch (-Z patch), experimental = ["patch"] under [settings], or git config --global third-party.experimental patch.

Shell completions

# bash
source <(git-third-party completion bash)

# zsh
git-third-party completion zsh > "${fpath[1]}/_git-third-party"

# fish
git-third-party completion fish > ~/.config/fish/completions/git-third-party.fish

Editing vendored content (experimental)

Enable with --experimental=patch (or set the equivalent in [settings] or git-config — see Settings):

  • git-third-party --experimental=patch patch save <dir> — record local modifications as a tree-level patch in third-party.lock. The patch reapplies on every update via a 3-way merge.
  • git-third-party --experimental=patch patch diff <dir> — show the saved patch.

If update produces conflicts, git-third-party stores the patch with a -conflicts suffix; resolve with git add followed by another patch save. Review the resulting commits carefully — the feature is experimental for a reason.

License

MIT — see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

git_third_party-1.0.0-py3-none-win_amd64.whl (2.8 MB view details)

Uploaded Python 3Windows x86-64

git_third_party-1.0.0-py3-none-macosx_11_0_arm64.whl (2.6 MB view details)

Uploaded Python 3macOS 11.0+ ARM64

File details

Details for the file git_third_party-1.0.0-py3-none-win_amd64.whl.

File metadata

File hashes

Hashes for git_third_party-1.0.0-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 823d89b47bf470005aac491b6e4c1e1bb9d10e7f3675e95de210f5b19d96dd90
MD5 1b26e9a27646d22d0786fa187bd55626
BLAKE2b-256 556432a17c20da2fce5836d69c85301c74c472b0a2cb0d05df83969023648f79

See more details on using hashes here.

Provenance

The following attestation bundles were made for git_third_party-1.0.0-py3-none-win_amd64.whl:

Publisher: release.yml on khwstolle/git-third-party

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file git_third_party-1.0.0-py3-none-manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for git_third_party-1.0.0-py3-none-manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 9f12f997e885b65445a71858a61c93e1bbb305e537f5e6d3c4f76f24d219a204
MD5 0d863d818a3baad365f63a8f5ad9ff7c
BLAKE2b-256 637a8e52c5669bd3771c2a49f11710f6174b196f6c71b252b8c48ffa28ceb958

See more details on using hashes here.

Provenance

The following attestation bundles were made for git_third_party-1.0.0-py3-none-manylinux2014_x86_64.whl:

Publisher: release.yml on khwstolle/git-third-party

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file git_third_party-1.0.0-py3-none-manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for git_third_party-1.0.0-py3-none-manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 60f89b27fa61496a7ed4b324ddb9cdb935fd3772ad99203c9b025cb70cc02a38
MD5 a3ad611ecf4cbd446fb427c057f5d827
BLAKE2b-256 50f0c5621e066314b595fa06356f804d0f1b36a528051afc15729d16443d7a5f

See more details on using hashes here.

Provenance

The following attestation bundles were made for git_third_party-1.0.0-py3-none-manylinux2014_aarch64.whl:

Publisher: release.yml on khwstolle/git-third-party

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file git_third_party-1.0.0-py3-none-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for git_third_party-1.0.0-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 6ddd782157d61045a2bad53ae1059ba3166d8d1182e9daa02f27ba39f447542d
MD5 fb4247cbe6212dae74b803fb575d1165
BLAKE2b-256 d30d7caa1803c02a7426374c4b3d519061bfd1e144b6cc26ca0bd54691c0f127

See more details on using hashes here.

Provenance

The following attestation bundles were made for git_third_party-1.0.0-py3-none-macosx_11_0_arm64.whl:

Publisher: release.yml on khwstolle/git-third-party

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page