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 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. 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.

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.3.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.3.0
File Actions
git_orchard-1.3.0-py3-none-win_arm64.whl Python 3 none Windows ARM64 Details for git_orchard-1.3.0-py3-none-win_arm64.whl
git_orchard-1.3.0-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details for git_orchard-1.3.0-py3-none-win_amd64.whl
git_orchard-1.3.0-py3-none-manylinux_2_17_x86_64.whl Python 3 none Linux glibc 2.17+ x86-64 Details for git_orchard-1.3.0-py3-none-manylinux_2_17_x86_64.whl
git_orchard-1.3.0-py3-none-manylinux_2_17_aarch64.whl Python 3 none Linux glibc 2.17+ ARM64 Details for git_orchard-1.3.0-py3-none-manylinux_2_17_aarch64.whl
git_orchard-1.3.0-py3-none-macosx_11_0_x86_64.whl Python 3 none macOS 11.0+ x86-64 Details for git_orchard-1.3.0-py3-none-macosx_11_0_x86_64.whl
git_orchard-1.3.0-py3-none-macosx_11_0_arm64.whl Python 3 none macOS 11.0+ ARM64 Details for git_orchard-1.3.0-py3-none-macosx_11_0_arm64.whl
git_orchard-1.3.0-py3-none-any.whl Python 3 none any Details for git_orchard-1.3.0-py3-none-any.whl

Total release size: 29.8 MB

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

Download URL git_orchard-1.3.0-py3-none-win_arm64.whl
Size 4.0 MB
Tags Python 3 Windows ARM64
SHA-256 checksum
How to use checksums
9713e629322586f9eb3c7da592a08871ce6eba7ff2300f4241c37893611d2836
BLAKE2b-256 checksum
How to use checksums
bb7f91b615c347263c4fd2a63837d7945f9f44ca6fa6260e21cadb7c6b76d4d8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","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.3.0-py3-none-win_amd64.whl

Download URL git_orchard-1.3.0-py3-none-win_amd64.whl
Size 4.5 MB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
34f56a18a0c1af7761a1eabda51bf994224dfb829e639cdd81128817ea587cbf
BLAKE2b-256 checksum
How to use checksums
96a5070c18342a00066afd860d9d08d87e78373505eff01f9f1782ed66fb0351
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","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.3.0-py3-none-manylinux_2_17_x86_64.whl

Download URL git_orchard-1.3.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
734409bf04614981c9d5edbb391c14f7a9b049b619391255770b7944d8c5865a
BLAKE2b-256 checksum
How to use checksums
349488a6894931a969eda76dff40a70ff721392bd2e9f7195909735a31ab76d8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","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.3.0-py3-none-manylinux_2_17_aarch64.whl

Download URL git_orchard-1.3.0-py3-none-manylinux_2_17_aarch64.whl
Size 3.9 MB
Tags Linux glibc 2.17+ ARM64 Python 3
SHA-256 checksum
How to use checksums
403d40eabd8cb071e296746751a946dd00b37fae6f21e8466c44d4bbfa452cb6
BLAKE2b-256 checksum
How to use checksums
21762a32ea28227d97e51d11fff8f3ecdbcefb383edeb515358bd25f21280c58
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","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.3.0-py3-none-macosx_11_0_x86_64.whl

Download URL git_orchard-1.3.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
3512d356893d84d6f13aa22c07334f0a15fcce5737f4f89a32be4967334b6a26
BLAKE2b-256 checksum
How to use checksums
48d4ac26970058a974875e7b880c83af1c9840195931a434832e0b35fad92834
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","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.3.0-py3-none-macosx_11_0_arm64.whl

Download URL git_orchard-1.3.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
6d24552502d96ea7c431c5e28199ec0f0ecc2ce99ba97ad4fef0043b243aa809
BLAKE2b-256 checksum
How to use checksums
c3bc6c3bce88cbb4962e59eb4f08b70ed6c6fe5e7e9523be946d81ae8106ec4f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","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.3.0-py3-none-any.whl

Download URL git_orchard-1.3.0-py3-none-any.whl
Size 4.4 MB
Tags Python 3
SHA-256 checksum
How to use checksums
98a11e158cf39f7dca48f8c00e1a50b1118831ca113aafc591ec14f987e8c44c
BLAKE2b-256 checksum
How to use checksums
0ac3d1ddcbd0d06e801a982223f139ba8e829328a4d5a06b1d1daf143d619764
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","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.3.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