Skip to main content

git-orchard

pre-commit status Deploy Status Go Reference Arch User Repsoitory PyPI Go Report Card

A command-line utility for managing git-subtrees.

Install

AUR:

git-orchard is available from the Arch User Repository.

yay -S git-orchard

pip:

git-orchard is available as a pypi package.

pip install git-orchard

go:

go install github.com/jmelahman/git-orchard@latest

Usage

Subtrees are listed in a committed manifest at the repository root, .gitsubtrees or .config/git-orchard/subtrees (but not both), in the same syntax as .gitmodules:

[subtree "tools/foo"]
	remote = git@github.com:owner/foo.git
	branch = master

The same keys in git's own configuration (e.g. .git/config) override it for one clone. Pulls and adds are squashed unless the manifest sets orchard.squash = false.

git orchard init                        # list the subtrees already in git history
git orchard add git@github.com:owner/foo.git tools/foo
git orchard add git@github.com:owner/foo.git # prefix defaults to the repo name, foo
git orchard status                      # commits ahead/behind each upstream
git orchard pull [prefix...]            # merge upstream changes
git orchard push [prefix...]            # publish, fast-forward only
git orchard push --changed-since REV    # only subtrees changed since REV
git orchard push --tag tools/foo/v1.2.3 # publish as v1.2.3 upstream
git orchard release tools/foo          # tag the next version, e.g. tools/foo/v1.2.4, and push it to origin
git orchard release tools/foo v2.0.0   # or a version of your choosing
git orchard sync [prefix...]            # copy shared files into subtrees
git orchard mv tools/foo foo            # move a subtree and commit it, with anything staged
git orchard detach [worktree]           # turn a linked worktree into a standalone clone
git orchard ci [prefix...]              # GitHub Actions status of the monorepo and each subtree

Without a version, release picks one after the latest release, much as tag does: the patch version incremented (--minor and --major increment those instead), or a pre-release's stable release; --suffix rc picks the next release candidate, e.g. v1.2.4-rc, then v1.2.4-rc.1. Releases are the <prefix>/v* tags in the monorepo and on its remote, and the v* tags upstream, so releases from before the subtree count; --dry-run prints the pick. release requires the upstream branch to contain the release already, so the upstream tag lands on its history; --upstream pushes the branch and tag there directly instead of leaving it to the action. push, pull and release take --no-verify to skip git hooks. Pushes upstream run the monorepo's pre-push hook on what they change there, at monorepo paths, rather than on the split's commits, so a tool like pre-commit checks only those files instead of the whole repository; a tag the upstream branch already contains changes nothing. push --force overwrites upstream branches and tags, leased on their value when the push starts so a concurrent update still fails it; the action never forces. release --force moves an existing tag, unless the upstream already published it at another commit: the Go module proxy and release artifacts won't follow a moved release.

push splits each subtree out of the monorepo with git subtree split, which is deterministic, so the same history always gives the same commits and every push is a fast-forward. An upstream with commits the monorepo doesn't have rejects the push until they're pulled in.

mv moves a subtree's directory and manifest entry and commits the move, with the same git-subtree-dir and git-subtree-split trailers a squash has, so splits of the new prefix carry on from the upstream's history instead of starting over. Anything already staged goes into the same commit, so stage updates to whatever names the old prefix (a go.work, CI paths) first. Clones fetch the recorded split from the upstream, so mv refuses a subtree with unpushed commits; push them first.

detach gives a worktree made with git worktree add its own .git directory, with the repository's refs, config, hooks and excludes, and the worktree's HEAD and index, so uncommitted changes carry over; objects are hardlinked where possible, as for a local clone. It then drops the worktree from the original repository, and refuses one that is locked, has submodules, or is mid-merge, rebase, cherry-pick, revert or bisect.

Besides gh, for ci and release --watch, git-orchard only runs git, so credentials, SSH config and url.<base>.insteadOf rewrites apply as usual.

CI

ci shows the latest run of each workflow on each subtree's upstream branch, and, unless prefixes are given, on the monorepo's own default branch as the prefix .. Deleted and disabled workflows, and pull requests' runs, are left out. It asks the GitHub CLI, gh, so that must be signed in.

git orchard ci                          # every run, with the failed ones in red
git orchard ci --failed                 # only the failed runs
git orchard ci --watch                  # refresh every minute until interrupted
git orchard ci --json                   # for scripts
git orchard ci --failed --logs | claude -p "triage these CI failures"
git orchard ci --prune-stale            # delete stale workflows' failed runs, after asking

--logs adds each failed run's logs as Markdown, cut down to the step that failed in each failed job (at most --lines of its last lines, 150 by default), without the setup and verbose logging GitHub's log viewer collapses. A failed run of a workflow that won't run again is stale: it shows dimmed, --failed hides it, and it doesn't count as a failure. So far that's Dependabot Updates in a repository with no .github/dependabot.yml and security updates off, as in a subtree whose dependencies the monorepo updates. --prune-stale deletes every failed run of such workflows, not just the latest, since the one before would take its place; it lists them and asks first, unless --yes. gh must be able to read the repository's security updates setting, which takes admin access; a repository it can't tell about is taken to be live. ci exits 1 if any run failed, even if it couldn't check some repository; 2 if it couldn't check some repository and nothing failed; and 130 if interrupted. So a script can ask only when something failed:

logs=$(git orchard ci --failed --logs); [ $? = 1 ] && claude -p "triage these CI failures" <<<"$logs"

Shared files

Subtrees that are published on their own each need their own copy of config like .pre-commit-config.yaml or .github/dependabot.yml. git orchard sync keeps those copies in step with one source. A subtree lists the profiles it shares, and each profile is a directory under orchard.sharedDir (.config/git-orchard/shared by default):

[subtree "tools/foo"]
	remote = git@github.com:owner/foo.git
	shared = base
	shared = go
.config/git-orchard/shared/
  base/.github/dependabot.yml   # → tools/foo/.github/dependabot.yml
  base/.pre-commit-config.yaml
  go/.pre-commit-config.yaml

A file in a profile lands at the same path in the subtree, replacing it whole. When the subtree's file marks a block for the profile, only the lines between the markers are replaced, and the rest of the file stays the subtree's own:

repos:
  # BEGIN orchard:base
  # END orchard:base
  # BEGIN orchard:go
  # END orchard:go
  - repo: local # not shared
    hooks: [...]

Markers work in any comment syntax, since git-orchard only looks for BEGIN orchard:<profile> and END orchard:<profile> in the line. Two profiles can share a file only through blocks.

sync exits 1 when it changes a file, like a formatter, and --check prints the differences without writing them. To run it on every commit, add the hook to the monorepo's root pre-commit config (not to the subtrees', since their mirrors have no manifest):

repos:
  - repo: https://github.com/jmelahman/git-orchard
    rev: v1.2.3
    hooks:
      - id: git-orchard-sync

GitHub Action

This repository is also an action that mirrors a monorepo's subtrees on every push: changed subtrees are pushed to their upstreams, and a <prefix>/<name> tag is published to that prefix's upstream as <name>.

on:
  push:
    branches: [master]
    tags: ["**/v*"] # `*` doesn't match `/`

jobs:
  mirror:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: 0 # splits need the full history
          persist-credentials: false
      - uses: jmelahman/git-orchard@v1
        with:
          app-client-id: ${{ vars.ORCHARD_APP_CLIENT_ID }}
          app-private-key: ${{ secrets.ORCHARD_APP_PRIVATE_KEY }}

The action pushes as a GitHub App, which git orchard github-app creates:

git orchard github-app  # add --org ORG for an organization's repositories

It opens a browser to create a private App under your account from a manifest (contents and workflows write, no webhook), then to install it: pick the upstream repositories there. With the GitHub CLI installed, it stores the App's client ID and private key on the monorepo (origin, or --repo) as the ORCHARD_APP_CLIENT_ID variable and ORCHARD_APP_PRIVATE_KEY secret; otherwise it writes the key to a file and prints the gh commands to store it. The App and its key are yours; git-orchard runs no service.

The action mints a short-lived token from the App's installation for owner (default: the monorepo's owner). Alternatively, pass token, e.g. a fine-grained token with "Contents" and "Workflows" read and write on the upstreams; GitHub refuses pushes that change .github/workflows without the latter. Either way, GitHub remotes in the manifest are rewritten to use the token. changed-since defaults to the start of the pushed range; set it empty to push every subtree. The action builds git-orchard from its own source, so the CLI is always the version the action is pinned to.

Metadata

Release files for git-orchard 1.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for git-orchard 1.4.0
File Actions
git_orchard-1.4.0-py3-none-win_arm64.whl Python 3 none Windows ARM64 Details for git_orchard-1.4.0-py3-none-win_arm64.whl
git_orchard-1.4.0-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details for git_orchard-1.4.0-py3-none-win_amd64.whl
git_orchard-1.4.0-py3-none-manylinux_2_17_x86_64.whl Python 3 none Linux glibc 2.17+ x86-64 Details for git_orchard-1.4.0-py3-none-manylinux_2_17_x86_64.whl
git_orchard-1.4.0-py3-none-manylinux_2_17_aarch64.whl Python 3 none Linux glibc 2.17+ ARM64 Details for git_orchard-1.4.0-py3-none-manylinux_2_17_aarch64.whl
git_orchard-1.4.0-py3-none-macosx_11_0_x86_64.whl Python 3 none macOS 11.0+ x86-64 Details for git_orchard-1.4.0-py3-none-macosx_11_0_x86_64.whl
git_orchard-1.4.0-py3-none-macosx_11_0_arm64.whl Python 3 none macOS 11.0+ ARM64 Details for git_orchard-1.4.0-py3-none-macosx_11_0_arm64.whl
git_orchard-1.4.0-py3-none-any.whl Python 3 none any Details for git_orchard-1.4.0-py3-none-any.whl

Total release size: 30.1 MB

Release files / git_orchard-1.4.0-py3-none-win_arm64.whl

Download URL git_orchard-1.4.0-py3-none-win_arm64.whl
Size 4.1 MB
Tags Python 3 Windows ARM64
SHA-256 checksum
How to use checksums
d61299963ecaebd97cb9e8e72af380dfd147c53c68c198b1d7d632ab989cc95e
BLAKE2b-256 checksum
How to use checksums
7e4c0827f578dc069f8ac34486e22d990162e5baa062324e25ed6f5506bd0186
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / git_orchard-1.4.0-py3-none-win_amd64.whl

Download URL git_orchard-1.4.0-py3-none-win_amd64.whl
Size 4.6 MB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
911739557327584b6a050d552c8bf58ee9bf294000649619848bcf3775cece2e
BLAKE2b-256 checksum
How to use checksums
d1d6c6b0c070aeae88847eafd8db115ac0919384fbc9bc8b4e905ecfb0a432cb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / git_orchard-1.4.0-py3-none-manylinux_2_17_x86_64.whl

Download URL git_orchard-1.4.0-py3-none-manylinux_2_17_x86_64.whl
Size 4.4 MB
Tags Linux glibc 2.17+ x86-64 Python 3
SHA-256 checksum
How to use checksums
ac5d62b79c173ea75d7db71b818a4cafbfe3f80ab48df229f6f4ca5bd553bb7f
BLAKE2b-256 checksum
How to use checksums
590f024e1768a4923b75a0c8b700675e510dcc647f62700bd48bb4581ece675a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / git_orchard-1.4.0-py3-none-manylinux_2_17_aarch64.whl

Download URL git_orchard-1.4.0-py3-none-manylinux_2_17_aarch64.whl
Size 4.0 MB
Tags Linux glibc 2.17+ ARM64 Python 3
SHA-256 checksum
How to use checksums
c8b30afc60ca2aab94b1a63516f4fe2663d1e42adb57d36e295ddff8c9d6165c
BLAKE2b-256 checksum
How to use checksums
70ed036d71663724c1d4656d15e83eaa3764224c986bfec507111f29167462a2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / git_orchard-1.4.0-py3-none-macosx_11_0_x86_64.whl

Download URL git_orchard-1.4.0-py3-none-macosx_11_0_x86_64.whl
Size 4.5 MB
Tags Python 3 macOS 11.0+ x86-64
SHA-256 checksum
How to use checksums
5d2adb1e0ce5d52f96f413955a0e6d570c04d1d543a621e0c03bc31dae0d1831
BLAKE2b-256 checksum
How to use checksums
1f7e2423f713ea130ac57af2192db6fea547c96522239907c379639e0fd3e472
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / git_orchard-1.4.0-py3-none-macosx_11_0_arm64.whl

Download URL git_orchard-1.4.0-py3-none-macosx_11_0_arm64.whl
Size 4.1 MB
Tags Python 3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
c36b7aabcb3008f63a1a23f84b889f742bdfcedb72479339acd16d94d7c2adb7
BLAKE2b-256 checksum
How to use checksums
05c8c7fd4c1c266d0b893cf52767e0429d645c1a5b7a0f1cdd8207927e0a5ad1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / git_orchard-1.4.0-py3-none-any.whl

Download URL git_orchard-1.4.0-py3-none-any.whl
Size 4.4 MB
Tags Python 3
SHA-256 checksum
How to use checksums
e76fb1b409536ee586eb8c1a7a37a4da3190d6f1d116192faff4e1f60325c78d
BLAKE2b-256 checksum
How to use checksums
ab405693c32b4ec3978b45123d9f942c62e6bdb7f4d2dc4c238ae1f8b60d5b2a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for git-orchard 1.4.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page