Braid is a simple tool to help track vendor branches in a Git repository. Braid mirrors selected files or directories from upstream Git repositories into a downstream repository.
This is a port of the Ruby Braid implemented in Go and built with Bazel.
Vendoring allows you take the source code of an external library and ensure it's version controlled along with the main project. This is in contrast to including a reference to a packaged version of an external library that is available in a binary artifact repository such as Maven Central, RubyGems or NPM.
Vendoring is useful when you need to patch or customize the external libraries or the external library is expected to co-evolve with the main project. The developer can make changes to the main project and patch the library in a single commit.
The problem arises when the external library makes changes that you want to integrate into your local vendored version or the developer makes changes to the local version that they want integrated into the external library.
A typical "implementation" of vendoring is to simply download or checkout the
source for the external library, remove the .git or .svn directories and
commit it to the main source tree. However this approach makes it very difficult
to update the library. When you want to update the library do you re-apply your
local changes onto a new copy of the vendored library or do you re-apply the
changes from the external library to local version? Both cases involve manual
generation and application of patch files to source trees.
This is where Braid comes into play. Braid makes it easy to vendor in remote git repositories and use an automated mechanism for updating the external library and generating patches to upgrade the external library.
Braid creates a file .braids.json in the root of your repository that contains
references to external libraries or mirrors. The configuration allows you to control
aspects of the mirroring process such as;
- whether the mirror is locked to a particular version of the external library.
- whether the mirror is tracking a tag or a branch.
- whether the mirror includes the entire external library or just a subdirectory.
For a release build, download the artifact for your platform from the release
page, verify SHA256SUMS, make the binary executable on Unix-like systems, and
place it on PATH.
shasum -a 256 -c SHA256SUMS
chmod +x braid-linux-amd64
sudo install -m 0755 braid-linux-amd64 /usr/local/bin/braidFor local development, run the binary through Bazel:
bazel run //cmd/braid:braid -- versionBraid commands run from the root of a Git working tree. add, update, and
remove write commits and require a clean working tree. status, diff, and
push are the usual commands for deciding whether to update, prepare a patch, or
send local mirror changes upstream.
- Command form
- Quick start
- Adding mirrors
- Checking status and local changes
- Updating mirrors
- Pushing local changes upstream
- Removing mirrors
- Remotes, cache, and paths
- Command reference
Global flags must appear before the command name:
braid [--verbose|-v] [--no-cache | --cache-dir <path>] <command> [options]Use the built-in help for the command list or command-specific syntax:
braid help
braid add help
braid add --helpStart in the root of an existing Git repository with a clean working tree. Add a mirror at the path where you want the upstream content to live:
braid add <upstream-git-url> lib/gritBraid copies the upstream content into lib/grit, records the mirror in
.braids.json, stages both, and creates a Braid: Add mirror ... commit. If you
do not specify --branch, --tag, or --revision, Braid tracks the upstream
repository's default branch.
Later, bring in upstream changes with:
braid update lib/gritIf you changed the vendored code locally and want to send those changes back upstream, inspect or save a patch:
braid diff lib/grit
braid diff lib/grit > grit.patchIf you have push access, commit your downstream changes first, then push the mirror changes back to the tracked branch or to a separate upstream branch:
braid push lib/grit
braid push lib/grit --branch myproject_customizationsAfter upstream accepts your changes, update the mirror again so your downstream repository records the accepted upstream revision:
braid update lib/gritAdd a whole upstream repository:
braid add https://github.com/rails/rails.git vendor/railsAdd only a subdirectory or a single file from upstream:
braid add https://github.com/twbs/bootstrap.git vendor/assets/bootstrap --path dist
braid add <upstream-git-url> licenses/PROJECT-LICENSE.txt --path LICENSE.txtTrack a specific branch or tag:
braid add https://github.com/rails/rails.git vendor/rails --branch 5-0-stable
braid add https://github.com/rails/rails.git vendor/rails-7 --tag v7.0.0Lock a mirror to an explicit revision when you do not want ordinary
braid update runs to move it:
braid add https://github.com/rails/rails.git vendor/rails --revision 5850a65The local_path argument is optional. If you omit it, Braid derives the local
path from the upstream repository name, or from the --path basename when
mirroring a subdirectory or file.
Show every configured mirror, or just one mirror:
braid status
braid status vendor/railsStatus output includes the recorded revision and tracking mode, such as
[BRANCH=main], [TAG=v1.0], or [REVISION LOCKED]. It also reports useful
state markers:
(Remote Modified): the tracked branch or tag points at a different upstream revision.(Locally Modified): the vendored files differ from the recorded upstream revision.(Removed Locally): the configured mirror path is no longer present in the downstream repository.
Show local changes as a Git diff:
braid diff
braid diff vendor/rails
braid diff vendor/rails -- --stat
braid diff vendor/rails -- --cachedArguments after -- are passed to git diff. This is useful for generating
patches, limiting output, or checking staged changes only.
Update one mirror to the newest revision for its tracked branch or tag:
braid update vendor/railsUpdate every branch and tag mirror in .braids.json:
braid updateRevision-locked mirrors are skipped by braid update without a path. Strategy
changes require a local path:
braid update vendor/rails --revision <revision>
braid update vendor/rails --branch main
braid update vendor/rails --tag <tag>If an update conflicts with local mirror changes, Braid leaves conflict markers
in the working tree and writes a prepared .git/MERGE_MSG. Resolve the
conflicts, stage the resolved files and .braids.json, then commit. To abandon
the conflicted update, reset the working tree and index with git reset --hard.
braid push creates an upstream commit from the mirror content recorded in your
downstream HEAD, opens Git's commit editor for the upstream commit message, and
pushes that commit.
For branch mirrors, pushing without --branch targets the tracked branch:
braid push vendor/railsUse --branch to push to a different upstream branch, or when the mirror tracks
a tag or fixed revision:
braid push vendor/rails --branch myproject_customizationsBraid stops without pushing if the upstream branch has moved since the recorded mirror revision. In that case, update the mirror first, resolve any conflicts, and then push.
Remove a mirror from the downstream repository:
braid remove vendor/railsBraid removes the vendored content, updates .braids.json, and creates a
Braid: Remove mirror ... commit.
Braid normally creates Git remotes as needed and removes them when the command
finishes. Use setup when you want Braid-managed remotes to exist in the
repository, for example before inspecting them with Git:
braid setup
braid setup vendor/rails --forceThe local cache is enabled by default. Without overrides, Braid stores it under
the OS user cache directory with a braid child directory. Use
BRAID_LOCAL_CACHE_DIR or --cache-dir to choose a location, and use
BRAID_USE_LOCAL_CACHE=false or --no-cache to disable it.
Mirror paths stored in .braids.json always use / separators. CLI mirror path
arguments may use / or \; Braid normalizes them before config lookup. The
--path option is an upstream Git path and should use Git's / separator. When
adding from a local Windows repository path such as C:\src\upstream.git, Braid
keeps that original upstream value for Git and derives the default mirror path
from the repository basename.
Braid validates configured mirror paths for cross-platform safety. It does not preflight every file inside the selected upstream tree; if an upstream filename cannot be materialized on the current OS, Git reports the checkout failure.
When you vendor only a subdirectory or single file, remember that license files outside the mirrored path are not copied automatically. If the upstream license must travel with the vendored content, add it separately or mirror a path that includes it.
| Command | Purpose |
|---|---|
add |
Add a branch, tag, or revision mirror and create the initial Braid commit. |
status |
Show whether mirrors have remote, local, or removal changes. |
diff |
Show local mirror changes, with Git diff arguments after --. |
update |
Update one mirror, or every branch/tag mirror when no path is given. |
push |
Push committed local mirror changes upstream. |
remove |
Remove mirrored content and config. |
setup |
Add or refresh Braid-managed Git remotes. |
version |
Print the Braid version. |
This repository is Bazel-first. Use Bazel as the source of truth for builds, tests, formatting, vetting, linting, and Go toolchain selection.
bazel test //...
bazel build //cmd/braid:braidFast Go quality checks used by CI run through the Bazel-pinned Go SDK:
bazel run @rules_go//go -- fmt ./...
bazel test //...
bazel run @rules_go//go -- vet ./...
bazel run @rules_go//go -- run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.12.0 runGitHub Actions CI workflow lives in .github/workflows/ci.yml and has two job families:
Go quality and lintruns formatting, tests, vet, and golangci-lint through Bazel. Tests usebazel test //...so unit tests, real-Git tests, and the executable integration target all run as first-class Bazel targets.Integration (<platform>)runs the executable integration target on the non-default native release platforms used for early cross-platform signal.
Each job installs Bazel, then uses rules_go to supply Go. golangci-lint is run
with bazel run @rules_go//go -- run ... so CI still has a single automation
entrypoint: Bazel.
Releases are cut through GitHub Actions:
- Run the
Release Cutworkflow frommainwith a stable version such as0.1.0orv0.1.0. - The workflow validates the exact
maincommit, creates an annotatedvX.Y.Ztag, and dispatches theReleaseworkflow on that tag. - The
Releaseworkflow builds the supported native artifacts, verifies checksums, and creates a draft GitHub release. - Review the draft release assets and generated notes, then publish manually.
The workflow files own the operational details, including runner labels, permissions, version stamping, artifact names, and verification commands.