Garden User Guide

Introduction

Garden streamlines development workflows that involve a loosely-coupled set of multiple, independent Git trees.

Garden allows you to define dynamic relationships and workflows between these repositories using a YAML configuration file that can be shared and used as a bootstrapping mechanism for getting an audit-able, from-source project cloned, built, installed and running with minimal effort for consumers of a Garden file.

Garden sits above any individual project's build scripts and conventions. Garden is all about making it easy to remix and reuse libraries maintained in separate Git repositories.

Glossary

  • garden.yaml -- also known as a "Garden file", a YAML file named garden.yaml defines trees, groups gardens, variables, commands and environment variables. garden looks for a file named garden.yaml by default, but other filenames can be specified using the garden -c | --config <path> option.

  • trees -- trees represent Git worktrees and repository clones. A tree represents a single repository and its configuration. Trees provide garden variables, commands and environment variables that are defined when commands are run on a tree. Environment variables are defined when a tree is operated on by itself and when combined alongside other trees in a group or garden context.

  • groups -- a named collection of trees. The groups block in the garden file defines groups of trees that are available for use by commands and gardens. Groups are lighter-weight than a garden and group trees together into named collections that can be referenced by gardens and commands. In contrast to gardens, groups do not provide a scope in which variables, commands environment variables can be defined.

  • garden -- a composite of trees and groups for defining project-level variables, commands and environment variables. Gardens help create environments for working on projects composed of multiple Git repositories. The gardens YAML key/value block defines named gardens with a scope in which garden-specific variables, commands and environment variables can be defined.

  • variables A YAML key/value table defines named variables that can be used in YAML strings by using braced shell ${expressions} to provide dynamic values that can be used to provide modularity and configurability to a garden file. Variables defined in a garden file are overridden on the command-line by using the -D / --define key=value option.

  • commands The commands YAML key/value table defines named commands that can be run against trees. The commands block extends garden with with user-defined functionality. The commands block can be defined at global scope, within a tree block, and within a garden block. The scope in which a command is defined limits the scope in which it is visible. This means that a command defined in tree scope will only execute when garden <command> <query> uses a query that ends up including that tree, and will only run when garden visits that specific tree.

  • environment variables Not to be confused with the variables block, the environment block is a YAML key/value table that defines environment variables that will be set when executing commands.

  • tree query -- a shellexpand glob expression that matches against garden names, group names and tree names. Several garden builtin commands take tree queries as arguments as a mechanism for selecting one or more trees to operate on. The default behavior is to match the tree query pattern against gardens, groups and trees, in that order, and return the first matching set of trees. If searching for gardens finds matches then groups and trees are not searched. If searching for groups finds matches then trees are not searched. Prefix the tree query pattern with a percent-sign (%), e.g. %group*, to only query for groups matching the pattern. Prefix the pattern with an at-sign (@), e.g. @tree, to only query for trees. Prefix the pattern with colon (:), e.g. :garden, to only query for gardens. The :garden syntax is not typically used because gardens are already searched first. %group and @tree can be used to disambiguate queries for groups and trees that share the same name as a garden.

  • string expressions String values can use shell ${variables} expressions to interpolate and expand values in the string. ~ is expanded to $HOME. These expressions can be used in most fields where strings are accepted, e.g. when defining variables, commands and environment variables.

  • exec expressions When a string expression starts with $ (dollar-sign then space) then the variable's value is computed by expanding the string's ${garden} variables and then executing the result and capturing stdout. The captured stdout output from the command becomes the variable's value. For example, if a variable called my-pwd is defined using an exec expression such as my-pwd: $ pwd then the ${my_pwd} variable will contain a path. The my_pwd variable can be used to define other variables, environment variables, and commands. For example, a command called example-cmd can be defined using: example-cmd: ls -l ${my-pwd} && echo my_pwd is ${my_pwd},

Installation

There are multiple ways to install garden.

These instructions assume that you have cargo installed for Rust development.

Skip ahead to the Homebrew section if you're on macOS and prefer to install garden using Homebrew.

Skip ahead to the NetBSD section if you're on NetBSD and prefer to install garden using pkgin or the pkgsrc/NetBSD sources.

Prebuilt Binaries

Pre-built Binaries are available for Linux, macOS and Windows.

Nightly Builds for x86_64 Linux are also available from the build:amd64 jobs.

Rust and Cargo

If you already have cargo installed then you can skip this section.

You may be able to install cargo on macOS and Linux using standard package managers, e.g. brew install rust or apt install rust-all.

Other platforms and older distributions can get a Rust development toolchain by going to rustup.rs and following the installation instructions.

Crates.io

This requires at least Rust 1.45 and Cargo to be installed. Once you have installed Rust, type the following in the terminal:

cargo install garden-tools

This will download and compile garden for you. The only thing left to do is to add the Cargo $HOME/.cargo/bin directory to your $PATH.

Latest using Cargo

The version published to crates.io will sometimes be behind the source code repository. If you want to install the latest pre-release version then you can build the Git version of Garden yourself using cargo.

cargo install --git https://gitlab.com/garden-rs/garden.git garden-tools

Install using Homebrew

You can install garden on macOS using Homebrew.

Add the homebrew-garden tap

NOTE: The custom brew tap will not be needed in the future once the Garden repository gets enough stars, forks or watchers to allow garden to be added to the main homebrew-core repository.

You will need to enable the homebrew-garden tap until then.

brew tap garden-rs/garden https://gitlab.com/garden-rs/homebrew-garden

Stable Release

To install the latest stable release from the homebrew-garden tap:

brew install garden

Upgrade garden in the future by using brew upgrade garden.

Development Version

To install the latest development version from Git:

brew install --head garden

NOTE: If you install the latest development version with --head then you will need to use brew upgrade --fetch-HEAD garden to upgrade it.

If you don't specify --fetch-HEAD when upgrading then brew upgrade garden will actually downgrade garden to the latest stable release.

Cleanup

Installing garden with Homebrew may leave behind the Rust development tools.

Use brew remove rust after garden is installed to save on disk space.

Read on for how to build garden from source.

Install on NetBSD

Garden has been packaged for pkgsrc/NetBSD as described in #13.

To install from the official repository, run:

pkgin install garden

If you prefer to build from the pkgsrc sources, run:

cd /usr/pkgsrc/devel/garden
make install

Build and Install from Source for Development

If you would like to develop features and contribute to Garden then you will have to clone the repository on your local machine.

git clone https://gitlab.com/garden-rs/garden.git
cd garden

# Build ./target/debug/garden
cargo build

# Install $HOME/.cargo/bin/garden
cargo install --path .

This will install garden to ~/.cargo/bin/garden by default.

Once you have garden installed then you can use Garden's garden.yaml to run Garden's custom workflow commands.

  • garden test runs the test suite using cargo test.
  • garden check runs checks and lints.
  • garden doc builds the documentation.
  • garden fmt formats the source code.
  • garden install-doc installs the documentation.

See Garden's garden.yaml for more details.

Nix

Nix Flakes

Nix Flakes can be used to build, test and install garden. A flake.nix file is provided in the source tree.

Enabling flakes permanently is recommended by either adding experimental-features = nix-command flakes to your ~/.config/nix/nix.conf or /etc/nix/nix.conf, or by using Home Manager to install flakes.

The following commands are available once installed.

  • nix build builds the garden package.
  • nix shell opens a shell with garden installed.
  • nix run directly run garden.
  • nix develop opens a development shell with garden and cargo installed.
  • nix flake check builds garden and runs the test suite.

Activating Garden's Nix Package

You can open a nix shell with garden installed by issuing the following command from any directory:

nix shell github:garden-rs/garden

Alternatively, you can also run garden directly by issuing nix run as follows:

nix run github:garden-rs/garden -- --help

Inside Garden's git repository, you can inspect its derivation by issuing nix derivation show. The output.path field contains the Nix Store location of the nix package. For example, nix derivation show will contain output that looks like the following:

{
  "/nix/store/n92qrx1j889bl2ippabpghsr4kyqbknh-garden-tools-1.0.0-beta2.drv": {
    # ...
    "outputs": {
      "out": {
        "path": "/nix/store/8i7pgb529lq8id1z4xfmcyh8xsc4w6q0-garden-tools-1.0.0-beta2"
      }
    },
    # ...
  }
}

You can use these details to open a shell with your previously-built garden package.

nix-shell -p /nix/store/8i7pgb529lq8id1z4xfmcyh8xsc4w6q0-garden-tools-1.0.0-beta2

Nix home-manager integration

One may also use home-manager to have a permanent environment with garden available across all shells. This can be done by setting up a derivation in your home-manager configuration.

Example:

File /path/to/home-manager/derivations/garden.nix:

{ lib, fetchFromGitHub, rustPlatform }:

rustPlatform.buildRustPackage rec {
  pname = "garden";
  version = "v1.8.0";

  src = fetchFromGitHub {
    owner = "garden-rs";
    repo = "garden";
    rev = version;
    hash = "sha256-+hcjHuJvqxkp+5FikVb8+mpU6rObC4xkMj/NBZDYTrQ=";
  };
  cargoHash = "sha256-12X4bMDAQTLZfiPSbY9cPXB8cW1L0zYrts5F5CXzV7Y=";
}

Note 1: hashes hash and cargoHash above are for v1.8.0 specifically. One could set them to lib.fakeHash; let the build fail with the error specifying the right hash, paste the correct hash and rebuild.

Note 2: If tests fail due to missing git in your build environment, one can set this up with doCheck = false.

you can activate the derivation above in your home.nix with

home.nix

{ config, pkgs, ... }:
    let garden =  pkgs.callPackage ./derivations/garden.nix {};
in
{
   home.packages = [
       garden
  ]
}

then run home-manager switch to activate the changes.

Windows

Garden is developed on Linux and supported on macOS and BSDs where Rust is available.

Garden is not supported on Windows.

Garden "should" work fine on Windows if you install a shell (e.g. bash.exe or zsh.exe) in $PATH and patch a few details to deal with Windows-isms, but Garden is untested and not supported by the core team on Windows or WSL.

Issues related to Windows will be closed. Pull requests related to these systems are welcome as long as they do not clutter the core or test suite with Windows-isms.

Setup and Usage

Setup

A garden can be created in any directory. Run garden init to create an empty "garden.yaml" file for defining trees, groups and gardens.

# Create an empty "garden.yaml" in the current directory.
garden init

By default, the garden.yaml is configured to operate on trees in the current directory relative to the garden file. The garden command searches the current directory for a "garden.yaml" config file.

If no "garden.yaml" could be found in the current directory then garden will attempt to load a "global" configuration file, typically located in ~/.config/garden/garden.yaml.

The following garden init invocation creates an empty garden.yaml with a root directory of ~/src in the user's home configuration directory. This is typically ~/.config/garden/garden.yaml.

garden init --global --root '~/src'

This allows you to access the config irrespective of the current directory and perform garden operations without needing to cd into ~/src/ to do so.

If multiple configuration files are made available in ~/.config/garden, then using garden -c alt.yaml ... from any directory (without specifying an absolute path) will use the alt.yaml garden file.

Usage

Now that we know how to create a garden.yaml we can start using garden to track our development repositories.

When we have a ~/src/garden.yaml file with Git worktrees alongside it in the ~/src directory then we can start adding those trees to the garden file. Existing trees are added to a garden file using garden plant <tree>.

Once we have a garden file with trees in it then we can use the garden file to recreate our setup using garden grow. The garden grow command brings trees into existence by cloning trees that have been configured in a garden file.

Garden commands are used to operate over sets of trees once a configuration has been defined. See the Garden Commands Documentation for detailed information about the built-in garden commands.

Tree Queries

Garden commands use "Tree Query" arguments that specify which groups, gardens or trees to operate on. Tree Queries are strings that resolve to a set of trees.

Strings with garden, group or tree names, @tree references, %group references, :garden references and wildcards are all Tree Queries.

When a query is specified, garden will use the first matching garden, group or tree, in that order, when determining which trees to operate on. When a garden or group is matched, all of its associated trees are used.

Tree Queries Resolve to Multiple Trees

The "cola" garden in the configuration section contains multiple trees that can be operated on together by specifying "cola" as the tree query.

# Run "git status -s" over each tree in the "cola" garden.
garden exec cola git status -s
# Run the custom "status" command over each tree in the "cola" garden.
garden status cola
# Run the "status" and "build" commands over each tree in the "cola" garden.
garden cmd cola status build

Wildcards

Garden understands shell wildcards in tree queries. Glob wildcards allow commands to operate on multiple sets of gardens, groups or trees.

# Clone trees whose name starts with "git" and run "pwd" in each tree.
garden grow '@git*'
garden exec '@git*' pwd

Paths

Paths can be used as a tree query as long as the specified directory refers to a tree known to Garden. If the path does not correspond to a tree known to garden then no commands will be run.

# Run "build" on the tree in the current directory and pass "--verbose" to the command.
garden build . -- --verbose

The file system has the lowest priority relative to gardens, groups, and trees when expanding a tree query. If an argument is ambiguous and can, for example, refer to both a "group" name and a single tree's path on the file system, garden will interpret the argument as a "group".

Please note that garden will use . (dot, the current directory in file system jargon) as the default tree query for custom commands when no tree query is specified.

# This...
garden build
# is equivalent to specifying "." as the tree query.
garden build .

Resolving Trees, Groups or Gardens Only

If you have groups, gardens, and trees with the same name then you can use the @tree, %group, and :garden prefixes to disambiguate the query.

  • @tree - values prefixed with @ resolve trees only
  • %group - values prefixed with % resolve groups only
  • :garden - values prefixed with : resolve gardens only
garden grow @tree      # grow the tree called "tree"
garden grow %group     # grow the group called "group"
garden grow :garden    # grow the garden called "garden"

When no prefixes are used then the names are resolved in a specific order. Gardens have the highest priority, followed by groups, trees and lastly paths.

If your trees, groups and gardens are named uniquely then you will rarely need to use prefixes in your tree queries.

Commands

Garden includes a set of built-in commands and can be flexibly extended with user-defined commands. User-defined commands are one Garden's most useful features.

Command-Line Conventions

Run garden help to display usage information for garden commands. The usage information is where the command-line options are documented.

garden help
garden help <command>
garden <command> --help

Built-in commands use this basic syntax:

garden [options] <command> [command-options] [command-arguments]*

The following options come before <command> and are common to all commands.

-C | --chdir <directory>

Navigate to the specified directory before searching for configuration. This is modeled after make -C <path> ... or git -C <path> ....

-c | --config <filename>

Specify a garden file to use instead of searching for garden.yaml. The filename can be either the path to a file or the basename of a file in the configuration search path.

-v | --verbose

Enable verbose debugging output.

-D | --define name=value

Override a configured variable by passing a name=value string to the -D | --define option. The variable named name will be updated with the garden expression value. Multiple variables can be set by specifying the flag multiple times.

garden init

garden init [options] [<filename>]

# create a local garden config rooted at the current directory
garden init

# create a global garden config rooted at ~/src
garden init --global --root '~/src'

The init command will create an empty Garden YAML file with the minimal boilerplate to start using garden. If no <filename> is specified, garden.yaml in the current directory will be written.

The Garden file is written to the user's ~/.config/garden/ global configuration directory when --global is specified.

This command is typically run without specifying a filename. garden init typically creates a garden.yaml in the current directory:

current-directory/
└── garden.yaml

If the current directory contains a Git repository then it will be added to the trees block automatically. Use garden init --empty to disable this behavior.

garden plant

garden plant <tree>

Add a pre-existing Git worktree to garden.yaml.

The trees section in the garden.yaml file will be updated with details about the new tree.

garden plant records the Git remotes associated with a repository. It is safe to re-run garden plant in order to add new remotes to an existing configuration.

Repositories created using git worktree are supported by garden plant. Parent trees must be planted first before planting a child tree.

Use the --sort option to sort all of the trees entries after planting.

garden ... [tree-query]

Garden commands accept tree query strings that are used to refer to sets of trees.

Tree queries are glob string patterns that can be used to match the gardens, groups or trees defined in "garden.yaml".

garden grow

garden grow <tree-query>

# Example usage
garden grow cola

If you have a garden.yaml file, either one that you authored yourself or one that was provided to you, then you will need to grow the Git trees into existence.

The grow sub-command updates or creates the trees matched by the <tree-query> and places them into the paths defined by the garden file.

Use the -t | --trees option to specify a glob pattern for filtering trees by name post-query. This lets you filter a tree query so that only a subset of the trees in the query are grown.

It is safe to re-run the grow command and re-grow a tree. Existing trees will have their git configuration updated to match the configured remotes. Missing repositories are created by cloning the configured tree URL.

Branches

The branch: <branch-name> tree variable is used to specify which branch should be cloned and checked out when the tree is grown.

trees:
  example:
    branch: dev
    url: git@example.org:trees/example.git

garden grow example clones the repository using git clone --branch=dev. The branch setting is a tree variable and supports ${variable} expressions.

Shallow Clones

The depth: <integer> tree parameter is used to create shallow clones.

trees:
  example:
    depth: 42
    url: git@example.org:trees/example.git

garden grow example clones the repository using:

git clone --depth=42 --no-single-branch

Even though a shallow clone is created, all of the remote tracking branches (e.g. origin/*) are available because we clone the repository using the --no-single-branch option.

The single-branch: true tree parameter is used to create clones that contain a single branch only. This is useful if you want to limit the on-disk footprint of repositories by only having a single branch available.

This par mater is typically used in conjunction with branch: <branch-name> and depth: 1 to create a 1-commit shallow clone with a single branch.

trees:
  example:
    branch: dev
    depth: 1
    single-branch: true
    url: git@example.org:trees/example.git

Wildcards

Wildcards are supported in the trees queries supported by garden grow. garden grow 'glob*' grows the gardens, groups or trees that start with "glob".

If garden.yaml contains gardens whose name matches the query then the trees associated with each garden are grown.

If no gardens are found then garden will search for "groups" that match the query. If groups are found then the trees within each group will be grown.

If no gardens and no groups are found then will garden search for trees and grow those whose names match the query string.

Worktrees

garden grow can be used to create worktrees that share their .git storage using git worktree.

To create shared storage, define the primary worktree where the .git storage will reside and then define additional trees that reference the worktrees.

trees:
  example/main: git@example.org:trees/example.git

  example/dev:
    worktree: example/main
    branch: dev

  example/v2:
    worktree: example/main
    branch: v2

This example uses example/main tree for the shared storage and two additional worktrees. example/dev uses the dev branch and example/v2 uses the v2 branch.

Upstream Branches

Remote tracking branches can be configured by defining a branches block that maps local branch names (the key) to a remote branch name expression (the value).

trees:
  example/branches:
    branch: local
    branches:
      local: origin/dev
      dev: origin/dev

The above configuration creates local branches called local and dev and checks out the local branch when garden grow example/branches is run.

Git Configuration Values

The garden grow command will apply git configuration values that are specified using the gitconfig key.

trees:
  foo:
    gitconfig:
      # Override the submodule URL for "thirdparty/repo".
      submodule.thirdparty/repo.url: "git@private.example.com:thirdparty/repo.git"

Multi-valued git config --add values are also supported, for example the remote.$name.pushurl value can be set to multiple values.

Use a list as the value for the key and multiple values will be added using git config --add $name $value.

trees:
  foo:
    url: "git@example.org:trees/foo.git"
    gitconfig:
      remote.origin.pushurl:
        # Push to multiple remotes when using "git push origin"
        - "git@jupiter.example.org:trees/foo.git"
        - "git@saturn.example.org:trees/foo.git"

Bare Repositories

To clone bare repositories use bare: true in the tree configuration.

trees:
  example:
    bare: true
    url: url

Bare clones are created by default when the tree path ends in .git. For example, a tree called example.git will be bare: true by default.

trees:
  example.git: git@example.org:trees/example.git

Setting bare: false overrides the name-based detection of bare repositories.

trees:
  example.git:
    bare: false
    url: git@example.org:tree/example.git

garden cmd

garden cmd <tree-query> <command> [<command>]... [-- <arguments>..]

# Example usage
garden cmd cola build test -- V=1

Run one or more user-defined commands over the gardens, groups or trees that match the specified tree query.

The example above runs the build and test commands in all of the trees that are part of the cola garden.

Commands

garden cmd and garden <command> interact with custom commands that are configured in the commands section for templates, trees, gardens, and the top-level scope.

# Example usage

# Run the test" command in the cola and vx trees
garden test git-cola vx

# Use "--" to forward arguments to the custom commands
garden test git-cola -- --verbose

# Print what would get run without running anything
garden test --dry-run -vv git-cola

Custom commands can be defined at either the tree or garden scope. Commands defined at the garden scope extend commands defined on a tree. If both a tree and the garden containing that tree defines a command called test then garden test will first run the tree's test command followed by the garden's test command.

Commands are executed in a shell so that shell expressions can be used in commands. A POSIX-compatible shell must be installed in your $PATH.

Additional command-line <arguments> specified after a double-dash (--) end-of-options marker are forwarded to each command.

"$0" in a custom command points to the current garden executable and can be used to rerun garden from within a garden command.

Additional arguments are available to command strings by using the traditional "$@" shell syntax. When additional arguments are present "$1", "$2", and subsequent variables will be set according to each argument.

The -N | --dry-run option performs a trial run without running any commands. Combine this option with the -vv extra-verbose mode to print the commands that would be run without running them.

# Commands can be defined in multiple ways.
# Strings and lists of strings are both supported via "String to List Promotion".
# The YAML reader accepts multi-line strings using the "|" pipe syntax.

commands:
  one-line: echo hello "$@"
  multi-line: |
    if test "${debian}" = "yes"
    then
        sudo apt install rust-all
    fi
  multiple-commands:
    - echo a $1 ${name}
    - |
        echo b $3
        echo c $4

variables:
  debian: $ type apt-get >/dev/null && echo yes || echo no
  name: value

# Commands can also be defined at tree and garden scope
trees:
  our-tree:
    commands:
      tree-cmd: echo ${TREE_NAME}

gardens:
  all:
    trees: "*"
    commands:
      print-pwd: pwd

Forwarding arguments using the double-dash -- end-of-options marker

Use -- (double-dash) to forward arguments to custom commands. Everything specified after -- on the command-line will be present in the "$@" array.

commands:
  test: cargo test -v "$@"

If the wrapped command also uses -- then you have to specify -- twice in order for the inner command to see a -- marker.

The cargo test command uses -- in order to pass options to the inner test commands. To pass options to the inner test command we'd have to use -- twice.

garden test -- -- --list
# Runs "cargo test -v -- --list". The first "--" is consumed by "garden".

If you always pass arguments to inner commands after their -- marker then you can simplify the command's usage by embedding -- into the command definition.

commands:
  test: cargo test -v -- "$@"

Using -- in the command lets us use a single -- to pass arguments to the innermost tests instead of two.

garden test -- --list
# Runs "cargo test -v -- --list".

Shebang (#!) Commands

Custom command shells can be used on a per-command basis by using a #!<command> shebang line as the start of a multi-line command.

The command specified in the #! shebang line must accept a command string as the next argument to the specified command.

commands:
  python-cmd: |
    #!python3 -c
    import sys
    print(sys.implementation)

Using #!python3 -c as the shebang line, as demonstrated above in the python-cmd command, will result in the remainder of the command getting passed as the next argument.

Concretely, garden will run python3 -c "import sys\nprint(sys.implementation)" when the garden python-cmd command is run.

Parallel Execution

By default, garden runs commands sequentially, one command and tree after another.

garden cmd <tree-query> <command>... and garden <command> [<tree-query>...] are able execute commands in parallel by using the -j# | --jobs # option.

Using the -j# | --jobs=# option makes garden use the specified number of cores when running commands in parallel.

Passing the value zero (-j0 | --jobs 0) to this option tells garden to detect and use all available cores. When -j | --jobs is the last option on the command line you can omit the numeric value and garden will treat it as if 0 was specified and use all cores.

garden test '@*' -j
garden test --jobs=0 '@*'

The commands above run the custom test command over all trees, in parallel, using all cores as detected at runtime.

Similarly, garden cmd '@*' test -j has the same behavior -- all trees are visited and operated on concurrently.

When multiple commands are specified, e.g. garden cmd '@*' a b c -j, then garden will run commands a, b and c in order, one after another, over all trees in parallel.

garden can also be made to visit each tree sequentially while running the a, b and c commands in parallel as each tree is visited.

The -b | --breadth-first option enables this behavior.

garden cmd -b '@*' a b c -j

The command above visits each tree sequentially and executes a, b, and c in parallel on each tree. The next tree is visited as soon as all three a, b, and c commands complete.

Parallel execution does not effect the ordering of pre and post-commands. Pre and post-commands run sequentially, in-order, relative to their associated command irrespective of the parallel execution mode.

Garden Shell

The garden.shell configuration value controls which shell interpreter is used to interpret custom commands.

garden:
  shell: zsh  # zsh is used by default when available. Uses bash, dash or sh otherwise.
  shell-wordsplit: true  # Words are split by default in zsh using "zsh -o shwordsplit"
  shell-errexit: true  # Non-zero exit status stops execution using "zsh -e".

garden.shell defaults to zsh when zsh is installed but can be set to any shell that accepts -e and -c <string> options (for example ksh). If zsh is not installed then bash or dash will be used instead. If neither zsh, bash nor dash is installed then sh will be used.

Each command runs under ["zsh", "-e", "-c", "<command>"] with the resolved environment from the corresponding garden, group, or tree.

Multi-line and multi-statement command strings will stop executing as soon as the first non-zero exit code is encountered due to the use of the -e shell option. Use the -n | --no-errexit option to inhibit the use of the -e "errexit" option.

The --no-errexit option causes commands with multiple statements to run to completion even when a non-zero exit code is encountered. This is akin to a regular shell script.

Configure garden.shell-errexit to false in garden.yaml to opt-out of this behavior. You can also opt-out of the errexit behavior on a per-command basis by using set +e as the first line of a multi-line command.

When zsh is used it is executed with the -o shwordsplit option so that zsh behaves similarly to traditional shells by splitting words in unquoted $variable expressions rather than treating $variable like a single argument.

Configure garden.shell-wordsplit to false to opt-out of this behavior. You can also opt-out of the shwordsplit behavior on a per-command basis by using set +o shwordsplit as the first line of a multi-line command.

Builtin Shells

The following values for garden.shell are understood directly by garden and the following commands are used when garden detects that these shells are configured. Not applicable (N/A) is denoted by -.

garden.shellCommand used for running commandserrexit=falsewordsplit=false
bunbun -e--
bashbash -e -cOmit -e-
dashdash -e -cOmit -e-
kshksh -e -cOmit -e-
nodenode -e--
nodejsnodejs -e--
perlperl -e--
rubyruby -e--
shsh -e -cOmit -e-
zshzsh -o shwordsplit +o nomatch -e -cOmit -eOmit -o shwordsplit

The following shells are not builtin, but they work as expected because they accept -c <string> arguments for running command strings.

garden.shellCommand used for running commands
fishfish -c
python3python3 -c

Custom Command Interpreters

The command used in garden.shell must specify a command that takes a string to execute using the -c <string> option. Some commands take -e <string> instead, namely nodejs, perl and ruby, which are all understood by garden and are expanded into their corresponding command-line.

This allows you to specify python3 as the shell because garden assumes that it can call python3 -c <string> to execute commands.

# This uses "python -c <string>" to run commands.
garden:
  shell: python3
commands:
  hello: print('hello world')
  info: |
    import platform
    print(platform.platform())

# Builtin shorthand shell values are special-cased internally.
# This uses "zsh -o shwordsplit -e -c <string>" to run commands.
garden:
  shell: zsh

# This uses "bash -e -c <string>" to run commands.
garden:
  shell: bash

# Use "#!" shebang lines to use a custom interpreters for a command.
commands:
  info: |
    #!python3 -c
    import platform
    print(platform.platform())

garden.shell also allows you to completely override the command used for interpreting custom commands. When you specify a command line with multiple arguments then garden will no longer internally manage the command-line options passed to the shell command. The options that you specify will be used instead. You must specify -c or similar arguments yourself when garden is configured with a custom command. The garden.shell-wordsplit and garden.shell-errexit options have no effect when using custom shell commands.

# Use "zsh -c" directly without "-o shwordsplit" and "-e".
garden:
  shell: zsh -c

# Additional examples.
garden:
  shell: python3 -s -u -c

Shell Syntax

User-defined Commands and Exec Expressions are evaluated by the shell configured in the garden.shell configuration value.

Garden and Shells share a key piece of common syntax: the interpolated braced ${variable} syntax.

Garden Variables that use the ${variable} syntax are evaluated by garden first before the shell has evaluated them.

This means that the shell syntax supported by Garden's Exec Expressions is a subset of the full syntax because shell-only variables such as ${OSTYPE} cannot be accessed using the braced-variable syntax.

commands:
  example-command: |
    shell_variable=$(date +%s)
    echo hello $OSTYPE $shell_variable

The plain $variable syntax is reserved for use by the shell commands used in user-defined Commands and Exec Expressions.

Environment Variables can be used in shell scriptlets through both the $ENV and ${ENV} braced variable syntax. Garden makes all environment variables available during variable expansion.

The distinction between the ${garden} and $shell syntax is only relevant when using variables defined within shell command, such as $shell_variable above.

If the ${shell_variable} syntax were to be used in the example-command then an empty value would have been used instead of the output of date +%s.

Sometimes it is necessary to actually use the ${...} braced literal syntax in shell commands. The $${...} braced double-dollar syntax can be used to escape a braced value and disable evaluation by garden.

Double-$ can generally be used to escape literal $ values in commands, but escaping is handled automatically for regular $shell variables.

Tree Traversal Order

Garden traverses trees in the same order that appear in the gardens, groups, and trees blocks in your garden.yaml files. You can affect the order that trees and commands are traversed by ordering the entries in the Garden file accordingly.

Depth-first and Breadth-first Tree Traversal

The following two invocations run commands in a different order:

# Depth-first (default)
garden cmd treesitters build test

# Breadth-first
garden cmd --breadth-first treesitters build test

The default traversal order for commands is depth-first. This means that both the build and test commands are run on each tree in the treesitters group before running any commands on the next tree.

The -b | --breadth-first option enables a breadth-first traversal. A breadth-first traversal runs the build command over all of the trees in the treesitters group before the test command is run over all of the trees in the same group.

Custom Commands

garden <command> <query> [<query>]* [-- <arguments>...]

# Example usage
garden status @git-cola .
garden build cola/next

garden <command> is another way to execute a user-defined <command>. This form allows you to specify multiple queries rather than multiple commands.

When no builtin command exists by the specified name then garden will use custom commands defined in a commands block at the corresponding garden or tree scope.

garden <command> [<query>...] is complementary to garden cmd <query> <command>....

garden cmd <query> <command>... runs multiple commands over a single query.

garden <command> [<query>...] runs a single command over multiple queries.

For example, garden build treesitters catsitters will run a user-defined build command over both the treesitters and catsitters groups.

Use the garden -vv extra-verbose option to display the commands being run.

Use the -t | --trees option to specify a glob pattern to filter trees by name post-query. Commands will only be run inside trees whose names match the pattern. This lets you activate a garden and its environment variables while only running a commands against a subset of trees in that garden.

Pre and Post Commands

Commands can specify references to other commands that should be run before and/or after a command.

  • Pre-commands are run before the command.

  • Pre-commands use a < suffix with values that specify the names of other commands to run before the command.

  • Post-commands are run after the command.

  • Post-commands use a > suffix with values that specify the names of other commands to run after the command.

  • Pre-commands and post-commands can only refer to other custom commands.

commands:
  custom-cmd: echo custom-cmd
  custom-cmd<: pre
  custom-cmd>:
    - post1
    - post2
  pre: echo before
  post1: echo after1
  post2: echo after2

Running garden custom-cmd with the above configuration runs the following commands:

# pre
echo before

# custom-cmd
echo custom-cmd

# post1
echo after1

# post2
echo after2

Missing Trees

garden skips missing trees when running custom commands by default.

This means that if a tree has not yet been grown then garden will not visit it when running custom commands. Missing trees are silently ignored and skipped.

Use garden --verbose <command> ... to instruct garden to print information about missing trees when it skips them. Missing trees are annotated with a (skipped) message.

Using mkdir to create the tree's directory is all that's needed to get garden to run commands in those trees.

Alternatively, use the -f | --force option when running custom commands to get garden to run custom commands on missing trees. The --force option only affects the behavior of custom commands when they operate on missing trees.

Since the tree's directory does not exist the current working directory when running commands on missing trees using --force will be different.

Using garden <command> --force <query> to run commands on missing trees will use the ${GARDEN_ROOT} directory (garden.root) instead of the tree's ${TREE_PATH} directory as the current working directory when commands are run.

garden exec

garden exec <tree-query> <command> [<arguments>]*

# example
garden exec cola git status -s
garden exec -j0 cola git status -s

Run commands over the trees, groups or gardens matched by tree query. When the <tree-query> resolves to a garden then the environment is configured for the command using the environment variables and custom commands from both the tree and the garden.

Use the garden -vv extra-verbose option to display the command being run.

Use the -N | --dry-run option to perform a trial run without running any commands.

Use the -t | --trees option to specify a glob pattern to filter trees by name post-query. Commands will only be run inside trees whose names match the pattern. This lets you activate a garden and its environment variables while only running the <command> against a subset of trees in that garden.

Use the -j# | --jobs=# option to enable parallel execution of commands limited to the specified number of cores. Trees will be visited in parallel and the command will be executed concurrently in each tree.

Set the jobs value to zero -j0 | --jobs=0 to use all available cores.

garden eval

garden eval <expression> [<tree>] [<garden>]

# example
garden eval '${GARDEN_ROOT}'
garden eval '${TREE_PATH}' cola

Evaluate a Garden Expression in the specified tree context and output the result to stdout.

Garden Expressions are strings-with-${variables} that get substituted and resolved using garden's variables and environment blocks.

If no tree is given then the variable scope includes the top-level variables block only.

When a tree is given then its variables are considered as well.

When a garden is specified then the garden's variables are also available for evaluation.

garden shell

garden shell <tree-query> [<tree>]

# example
garden shell cola

Launch a shell inside the environment synthesized by the tree query. If <tree> is specified then the current directory will be set to the tree's directory.

If the resolved tree query contains a tree whose name exactly matches the query string then that tree's directory will be used when opening the shell. The optional tree argument is not needed for the case where a garden and tree share a name -- garden will chdir into that same-named tree when creating the shell.

If you would like to customize the command to use for garden shell then you can configure garden.interactive-shell. This value overrides garden.shell and is only used by the garden shell command.

# Launch a fish login shell for "garden shell".
garden:
  interactive-shell: fish

Arbitrary command interpreters can be specified.

# Launch a python interpreter for "garden shell".
garden:
  interactive-shell: python3 -B

garden ls

garden ls [options] [<tree-query>...]

Display configured information about trees and commands. Tree details are not displayed for missing / ungrown trees.

Use the -a | --all option to display details for missing trees.

If no tree-queries are specified then garden ls behaves as if garden ls '@*' were specified, which displays all trees.

Use the -t | --trees option to specify a glob pattern that can be used to filter trees by name post-query. This is useful when you want to list details about a group or garden while only listing details about a subset of the trees.

garden prune

garden prune [options] [<subdirs>...]

Traverse the filesystem and interactively delete any repositories that are not referenced by the garden file.

This command is intended to cleanup a garden-managed directory. Its intended usage is to delete repositories that were created (e.g. via garden grow) and have since been removed from your version-controlled garden configuration.

Warning: garden prune is a dangerous command and must be run with care. garden prune deletes repositories and all of their files (including the .git storage)!

The following options are supported by garden prune.

Enable deletions

--rm

The garden prune command uses a no-op "safe mode" which does not actually delete repositories by default. Deletions must be enabled by specifying the --rm option.

Use the --rm option only after you have verified that garden prune is not going to delete any unexpected repositories that you intended to keep.

Limit concurrency

--jobs <jobs>

The prune process runs in parallel across multiple cores. All cores are used by default. The --jobs option limits the number of cores to the specified number of jobs.

Limit filesystem traversal depth

--min-depth <minimum-depth>
--max-depth <maximum-depth>
--exact-depth <exact-depth>

The cleanup process can be limited to specific traversal depths. The filesystem is traversed with no limits by default.

Specifying a minimum depth will not remove repositories shallower than the specified depth. For example, --min-depth 1 will not remove repositories in the same directory as the garden file.

Specifying a maximum depth will not remove repositories deeper than the specified depth. For example, --max-depth 0 will not remove repositories in subdirectories below the directory containing the garden file.

Enable scripted usage by answering "yes" to all prompts

--no-prompt

The garden prune command interactively prompts before removing each repository. The prompt looks like the following:

# /home/user/src/example
Delete the "example" repository?
WARNING: "all" deletes "example" and ALL subsequent repositories!
Choices: yes, no, all, quit [y,n,all,q]?

Entering y (or yes) at the prompt will delete the repository and all of its files.

Entering n (or no) at the prompt will skip and not remove the repository.

Entering q (or quit) will exit garden prune without deleting the repository.

Entering all will remove the repository and all subsequent repositories. all is equivalent to answering yes to all further prompts.

Entering all is dangerous and proceeds without further prompts. Be careful!

--no-prompt is also equivalent to answering yes to all prompts. --no-prompt is intended for use in scripts where user interaction is not desired. Use with caution!

garden completion

Shell completions for garden can be generated by running the garden completion command.

garden completion uses clap complete to generate its completions.

The --commands options will additionally generate completions for custom commands.

Zsh

Ensure that your ~/.zshrc file has completions enabled and that you have a directory configured in your $fpath for zsh completion scripts. Add the following snippet to your ~/.zshrc to enable completions and configure ~/.config/zsh/completion for providing completion scripts.

fpath=(~/.config/zsh/completion $fpath)
autoload -U +X compinit
compinit -i

These settings make zsh look for a script called _garden in the directory when tab-completing for garden.

Lastly, create the directory and generate the ~/.config/zsh/completion/_garden zsh shell completion script.

mkdir -p ~/.config/zsh/completion
garden completion zsh >~/.config/zsh/completion/_garden

Use garden completion --commands zsh instead of garden completion zsh to include completions for custom commands.

NOTE: You should regenerate the _garden zsh completion script whenever garden is upgraded to ensure that all of the options and commands have up to date completions.

Bash

Add the following snippet to your ~/.bashrc to enable bash tab completions.

if test -n "$PS1" && type garden >/dev/null 2>&1
then
    eval "$(garden completion bash)" 2>/dev/null
fi

Use garden completion --commands bash instead of garden completion bash to include completions for custom commands.

Future shell completion enhancements

Tab completion can only be made to include a static set of user-defined commands. Custom commands cannot be defined dynamically, which means that the same completions will be used irrespective of your current directory.

Improvements to the shell completions can be made once traction has been made on the following upstream issues:

Configuration

Garden is configured through a YAML configuration file, typically called "garden.yaml".

Garden will find garden.yaml in current directory or in specific locations on the filesystem when unspecified. Garden searches for garden.yaml in the following locations. The first one found is used.

# Relative to the current directory
./garden.yaml
./garden/garden.yaml
./etc/garden/garden.yaml

# Relative to $HOME
~/.config/garden/garden.yaml
~/etc/garden/garden.yaml

# Global configuration
/etc/garden/garden.yaml

If garden.yaml is not found in these directories then garden will walk up the file system searching for config files.

You can prevent garden from traversing into directories higher in the file system tree by setting either the GARDEN_CEILING_DIRS or GIT_CEILING_DIRS environment variables. Multiple directories can be specified by using a colon(:)-delimited list of ceiling directories.

garden uses the GIT_CEILING_DIRS environment variable from core Git as a fallback when GARDEN_CEILING_DIRS it is not set.

GARDEN_CEILING_DIRS has higher precedence and overrides values configured in GIT_CEILING_DIRS.

Use garden -c|--config <filename> to specify a garden file and override garden's file discovery.

If a basename is specified, e.g. garden --config custom.yaml, then garden will search these same locations for custom.yaml instead of garden.yaml.

The following example garden.yaml is referred to by the documentation when showing examples.

# Create environments for developing Git Cola against development versions of Git.
#
# The following gardens are defined: "cola", "cola/main" and "cola/next".
#
# The "cola" garden runs git-cola using the "git" command from your $PATH.
# The "cola/main" garden runs git-cola using git.git's "main" branch.
# The "cola/next" garden runs git-cola using git.git's "next" branch.
#
# One-time Setup:
#
# * Clone repositories and create worktrees:
#
#   garden grow cola/main cola/next
#
# * Initialize git-cola's Python virtualenv environment.
#
#   garden setup cola
#
# Development workflows:
#
# * Build the "cola/main" garden using git.git's "main" branch:
#   garden build cola/main
#
# * Build the "cola/next" garden using git.git's "next" branch:
#   garden build cola/next
#
# * Run Git Cola
#   garden run
#
# * Run Git Cola on the git/next and git-cola trees using the cola/next garden environment:
#   garden run --trees git/next cola/next
#   garden run -t git-cola cola/next
#
# * Open a shell for running Git Cola in different enviornments
#   garden shell cola/main
#   garden shell cola/next
#
# * Run just Git Cola's tests in each environment.
#   garden test/cola cola
#   garden test/cola cola/main
#   garden test/cola cola/next
#
# * Run tests for all projects in each environment. Also runs Git's testsuite.
#   garden test cola
#   garden test cola/main
#   garden test cola/next

# The following variables are used in the custom commands below.
variables:
  # A virtualenv is created in the ./dist/git-cola/env3 directory.
  prefix: ${GARDEN_ROOT}/git-cola/env3
  jobs: $ nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 8

commands:
  add-all: git add --update
  diff: GIT_PAGER= git diff --patience --color-words "$@"
  lol: git log --decorate --graph --oneline "$@" && echo
  run: vx ${prefix} git cola "$@"
  status:
    - git status --short
    - git branch

templates:
  bin:
    environment:
      PATH: ${TREE_PATH}/bin
  python:
    environment:
      PYTHONPATH: ${TREE_PATH}
  makefile:
    commands:
      build: make -j ${jobs} prefix="${prefix}" all "$@"
      install: make -j ${jobs} prefix="${prefix}" install "$@"
      test: make -j ${jobs} prefix="${prefix}" test "$@"
      doc: make -j ${jobs} prefix="${prefix}" doc "$@"

trees:
  # git/main is the parent worktree that shares its .git storage with the child
  # git/next worktrees using "git worktree" -- https://git-scm.com/docs/git-worktree
  git/main:
    url: https://github.com/git/git.git
    templates: makefile
    environment:
      PATH: ${TREE_PATH}/bin-wrappers
      # git/next is a "git worktree" for git.git's "next" branch.
  git/next:
    worktree: git/main
    branch: next

  # git-cola's worktree can be reused alongside any of the git/* trees defined above.
  git-cola:
    url: https://gitlab.com/git-cola/git-cola.git
    templates: [bin, python]
    commands:
      setup: garden dev
      test/cola: garden test "$@"
      install: garden -D prefix="${prefix}" install "$@"
      test: garden test "$@"
      doc: garden doc "$@"

  qtpy:
    description: Qt abstraction library used by git-cola
    url: https://github.com/spyder-ide/qtpy.git
    templates: python
    setup: vx ${prefix} pip install pytest-qt

  vx:
    description: Utility for activating Python virtualenvs
    url: https://gitlab.com/davvid/vx.git
    depth: 1  # Create a shallow clone using "git clone --depth=1"
    environment:
      PATH: ${TREE_PATH}
    commands:
      test: make test

  # This tree allows the "run" command to be run from any directory.
  cwd:
    path: ${PWD}

groups:
  cola-repos-grp:
    - git-cola
    - qtpy
    - vx

gardens:
  cola:
    groups: cola-repos-grp
    environment:
      PATH: ${prefix}/bin
  cola/main:
    groups: cola-repos-grp
    trees: git/main
    environment:
      PATH: ${prefix}/bin
  cola/next:
    groups: cola-repos-grp
    trees: git/next
    environment:
      PATH: ${prefix}/bin

Garden Root

The garden root directory is configured in the garden.root field. This directory is the parent directory in which all trees will be cloned.

garden.root defaults to ${GARDEN_CONFIG_DIR} when unspecified. {GARDEN_CONFIG_DIR} is the directory that contains the current garden.yaml .

The default ${GARDEN_CONFIG_DIR} value lets you create relocatable setups with trees that are located relative to the garden.yaml config file.

Slashes in tree paths will create new directories on disk as needed.

# By default, "garden init" generates a "garden.yaml" that uses the
# ${GARDEN_CONFIG_DIR} for its "garden.root" location.
garden:
  root: ${GARDEN_CONFIG_DIR}

Omitting garden.root is equivalent to the configuration above.

To place all trees in a src directory sibling to the garden.yaml file, the following configuration can be used:

garden:
  root: ${GARDEN_CONFIG_DIR}/src

To place all trees in a src directory in your $HOME directory, the following configuration can be used:

garden:
  root: ~/src

Configure garden.root to "" (an empty string) to use a dynamic garden.root that follows your current directory. This lets you use garden --config <path> to create and interact with trees in your current directory instead of a fixed configuration-defined location.

garden:
  root: ""

Garden Shell

The shell used by garden when running Commands is configured by the garden.shell field.

garden:
  shell: zsh

The $PATH environment variable is probed for available shells in the following order when garden.shell is omitted. The first one found is the one that's used.

  • zsh
  • bash
  • dash
  • sh

The following shell interpreters can also be configured in garden.shell for running custom commands.

  • bun
  • fish
  • node
  • perl
  • python3

Any command that can run command strings using -c can be used as a garden.shell interpreter.

Exec expressions are always run using the default /bin/sh system shell irrespective of garden.shell.

Tree Display

Garden will display the tree's current branch when running commands. While this has a marginal performance impact, this feature can be disabled by either passing the garden -D garden.tree-branches=0 option or by configuring the garden.tree-branches option to false in the garden.yaml configuration.

garden:
  tree-branches: false

Includes

Garden files can be split apart into several files for modularity and reuse. You can use the garden.includes list to specify other garden files to include into the current garden file.

garden:
  includes:
    # Includes are relative to the GARDEN_CONFIG_DIR by default.
    - variables.yaml
    # Includes can reference custom and built-in ${variables}.
    - ${include_dir}/commands.yaml

variables:
  include_dir: ${GARDEN_ROOT}

Absolute paths in the garden.includes list are included as-is. Relative paths in the garden.includes list are resolved.

Relative paths are first resolved relative to the file in which they are defined. This allows nested include files to use relative paths for their nested includes.

If an include file is not found relative to the current garden file then a path relative to the root configuration directory will be checked for the existence of the file.

Includes files are treated like "optional" includes -- include files that cannot be found are silently ignored.

Enable the garden -d config ... debug flag to display warnings about missing include files.

The "Last One Wins" Rule

Entities in garden files such as trees, gardens, groups, commands and variables can be sparsely defined across multiple files by using includes.

When the same entry is found in multiple included files then the only last definition will be used. This is referred to as the "Last One Wins" rule.

Entities defined in the root garden.yaml have the highest precedence and override entries provided via garden.includes.

variables, commands, groups and gardens are completely replaced when multiple definitions are found.

trees are sparsely overridden. If an override definition in the top-level garden.yaml replaces just the url field, for example, then all of the commands, variables and environment values from the earlier definition are retained and only the url for the origin remote is replaced.

If a tree needs to completely override a base definition then a tree can use replace: true to indicate that the tree definition is replacement for the earlier tree definition.

# garden.yaml
garden:
  includes: trees.yaml

trees:
  example:
    replace: true
    url: https://custom.example.org/custom/example.git

The garden.yaml above includes trees.yaml. The example tree is originally defined here, but it is completely replaced by the same entry above.

# trees.yaml
trees:
  example: https://example.com/original/tree.git
  commands:
    echo: Hello, ${TREE_NAME}

In the example above, the example tree completely replaces the same tree from the included trees.yaml. None of the commands, variables or other settings from the replaced tree are retained.

Variables

Garden configuration contains a "variables" block that allows defining variables that are can be referenced by other garden values.

variables:
  flavor: debug
  user: $ whoami
  libdir: $ test -e /usr/lib64 && echo lib64 || echo lib
  nproc: $ nproc
  prefix: ~/.local
  py_ver_code: from sys import version_info as v; print("%s.%s" % v[:2])
  py_ver: $ python3 -c '${py_ver_code}'
  py_site: ${libdir}/python${py_ver}/site-packages

Variables definitions can reference environment variables and other garden variables.

Variable references use shell ${variable} syntax.

Values that start with dollar-sign+space ($ ) are called "exec expressions". Exec expressions are run through a shell after evaluation and replaced with the output of the evaluated command.

When resolving values, variables defined in a tree scope override/replace variables defined at the global scope. Variables defined in garden scope override/replace variables defined in a tree scope.

Built-in variables

Garden automatically defines some built-in variables that can be useful when constructing values for variables, commands, and paths.

  • GARDEN_CONFIG_DIR -- Directory containing the garden.yaml file.
  • GARDEN_ROOT -- Root directory for trees.
  • TREE_NAME -- Current tree name.
  • TREE_PATH -- Current tree path.

Variables for Custom Commands

The following built-in variables are provided to make it easier to write custom commands that re-execute garden. These variables allow you to forward the user-specified -vv or --quiet options to child commands.

  • GARDEN_CMD_QUIET -- --quiet when --quiet is specified, empty otherwise.
  • GARDEN_CMD_VERBOSE -- -v, -vv etc. when verbosity is increased, empty otherwise.

Environment Variables

The "environment" block defines variables that are stored in the environment.

Environment variables are resolved in the same order as garden variables: global scope, tree scope, and garden scope. This allows gardens to prepend/append variables after a tree, thus allowing for customization of behavior from the garden scope.

Values in environment blocks prepend to the environment variable by default. The : UNIX path separator is used when prepending and appending values.

trees:
  foo:
    environment:
      PATH: ${TREE_PATH}/bin

The example above prepends the foo/bin directory to the colon (:)-delimited PATH environment variable.

Names with an equals sign (=) suffix are treated as "store" operations and are stored into the environment, fully replacing any pre-existing values.

trees:
  foo:
    environment:
      ${TREE_NAME}_LOCATION=: ${TREE_PATH}

Environment variable entries can use garden ${variable} syntax when defining both their name and values. The example above exports a variable called foo_LOCATION with the location of the tree. If foo_LOCATION is already defined then its value is replaced.

A plus sign (+) suffix in the name append to a variable instead of prepending.

trees:
  foo:
    environment:
      PATH+: ${TREE_PATH}/bin

The example above appends to the PATH environment variable. Note the + suffix after PATH.

OS Environment Variables

OS-level environment variables that are present in garden's runtime environment can be referenced using garden ${variable} expression syntax. Garden variables have higher precedence than environment variables when resolving ${variable} references -- the environment is checked only when no garden variables exist by that name.

Variable Expression Evaluation Order

Garden ${variables} expressions can reference other variables and they can also reference variables from environment entries.

Referencing variables defined in an environment section from a variables entry is generally discouraged for simplicity, but it is supported and valid. It is recommended that a variables entry should only reference other variables and avoid referencing entries from environment blocks.

Following this rule of thumb results in configurations where entries from environment sections depend on entries from variables and not the other way around. This minimizes the chance of introducing cyclical dependencies across variables.

environment entries are only used during variable resolution when the expression evaluation engine is unable to find the variable in any of the higher-precedence scopes.

The precedence order when expanding variables, from strongest to weakest, is as follows:

  • The variables block in a garden's scope.

  • The variables block in a tree's scope.

  • The variables block in the global configuration scope.

  • The environments block in a garden's scope.

  • The environments block in a tree's scope.

  • The environments block in the global configuration scope.

  • OS environment variables.

The first entry found is used when expanding ${variable} expressions.

Gardens, Groups and Trees

Trees are Git repositories with configuration that allows for the specification of arbitrary commands and workflows. Groups are a simple named grouping mechanism.

Gardens aggregate groups and trees. Define a group and reuse the group in different gardens to share tree lists between gardens. Defining gardens and groups make those names available when querying and performing operations over trees.

Gardens can also include environment, gitconfig, and custom group-level commands in addition to the commands provided by each tree.

Trees

The trees block in a garden.yaml Garden file defines the trees that are available for running commands against.

Trees represent Git worktrees and can be aggregated into groups and gardens. The tree block defines properties about a tree such as its Git URL, custom variables, environment variables, Git remotes, Git configuration variables, and custom commands.

trees:
  git-scm:
    description: Fast, scalable, distributed version control
    path: git
    url: git://git.kernel.org/pub/scm/git/git.git
    remotes:
      gitster: https://github.com/gitster/git.git
      gitlab: https://gitlab.com/git-scm/git.git
      github: https://github.com/git/git.git
    commands:
      build: make all -j ${num_procs} "$@"
      test: make test "$@"
    variables:
      num_procs: $ nproc 2>/dev/null || sysctl -n hw.activecpu 2>/dev/null || echo 4
    links:
      - "https://gitgitgadget.github.io/"
      - "https://github.com/gitgitgadget/git/issues"

All fields are more or less optional. The path field defaults to the same name as the tree, so the path: git setting above can be omitted without any differences in behavior.

The path field can be used to name trees independently of directory on disk. The path value defaults to the same name as the tree, for example git-scm would be used as the directory without the path: git setting above.

The path: git setting causes garden grow git-scm to clone into a directory called git instead of git-scm.

Relative paths are assumed to be relative to the ${GARDEN_ROOT}, typically the same directory as the garden.yaml.

If the only field you want to configure is the url field then you can also use a string rather than a dictionary / table to represent a tree by providing just the URL. This configures a tree with a single origin remote pointing to the configured URL.

trees:
  git-scm: git://git.kernel.org/pub/scm/git/git.git

Default Tree

The trees block is optional when a commands block exists.

An implicit default tree called . will be synthesized into existence when the trees block is empty.

The default tree's path is set to the ${GARDEN_CONFIG_DIR}. Omitting the trees block lets you use garden as a simple command runner.

Remotes

The remotes field defines named Git remotes that will be configured when growing the repository.

If you edit your garden.yaml then you can always re-run garden grow to add remotes that were added to garden.yaml.

Likewise, you can always rerun garden plant to record new remotes that you may have added using the git remote add command-line interface.

Default Remote Name

The default origin remote name used by Git can be overridden by setting the default-remote field in the tree's configuration.

trees:
  git:
    url: git://git.kernel.org/pub/scm/git/git.git
    default-remote: kernel.org

This will create a remote called kernel.org instead or origin when growing trees. This feature can also be used when multiple named remotes are configured.

trees:
  git:
    default-remote: kernel.org
    remotes:
      kernel.org: git://git.kernel.org/pub/scm/git/git.git
      gitster: https://github.com/gitster/git.git

The links field allows you to specify a list of related URLs. Links are displayed by garden ls and are clickable when using a capable terminal.

Templates

Templates allow sharing of command, variable, gitconfig, and environment definitions across trees. Adding an entry to the templates configuration block makes a template available when defining trees.

Specify templates: <template-name> to inherit the specified template's settings when defining trees. The templates field also accepts a list of template names.

Trees can also reuse another tree definition by specifying the "extend" keyword with the name of another tree. Only the first remote is used when extending a tree.

templates:
  hello:
    variables:
      message: Hello ${TREE_NAME}.
    commands:
      echo: echo ${message}

trees:
  hello-tree:
    templates: hello
  hello-tree-extended:
    extend: hello-tree
    variables:
      message: The time is now: $(date)

When a tree specifies multiple templates then all of the templates are merged into the tree's definitions. If variables are multiply-defined across multiple templates then the variable's value from the last specified template will be used.

String to List Promotion

Fields that expect Lists can also be specified using a String value. Strings will be promoted to a List containing a single String. This is useful when defining commands and groups.

The commands block defines commands that are specified using Lists of Strings. String to List Promotion makes it easier to define commands by specifying a single String that can either be a simple value or a multi-line YAML String.

The following commands show the various ways that commands can be specified due to the automatic promotion of Strings into Lists.

commands:
  # commands are lists of command strings.
  cmd1:
    - echo cmd1 ${TREE_NAME}
    - pwd

  # Strings are promoted to a list with a single item.
  cmd2: echo cmd2 ${TREE_NAME} && pwd
  # cmd2 is equivalent to...
  cmd2:
    - echo cmd2 ${TREE_NAME} && pwd

  # Multi-line command strings can be written using "|" YAML syntax.
  cmd4: |
    echo ${TREE_NAME}
    pwd

  # cmd4 is promoted into
  cmd4:
    - "echo ${TREE_NAME}\npwd"

Wildcards

The names in garden tree and group lists, and group member names accept glob wildcard patterns.

The "annex" group definition is: annex/*. This matches all trees that start with "annex/". The "git-all" group has two entries -- git* and cola. The first matches all trees that start with "git", and the second one matches "cola" only.

Symlink trees create a symlink on the filesystem during garden init. garden exec, and custom garden cmd commands ignore symlink trees.

trees:
  media:
    path: ~/media
    symlink: /media/${USER}

The "path" entry behaves like the tree "path" entry -- when unspecified it defaults to a path named after the tree relative to the garden root.

Grafts

A more advanced modularity feature allow you to stitch additional garden.yaml files underneath a custom "graft namespace".

The example below demonstrates how to define trees and variables in separate "graft" files and refer to them using a graft:: namespace qualifier.

# Top-level garden.yaml
grafts:
  graft: graft.yaml
  graft-repos:
    config: repos.yaml
    root: repos

trees:
  local-tree:
    url: https://example.com/trees/repo.git
    variables:
      value: "local ${graft::value}"

variables:
  value: "global ${graft::value}"

gardens:
  example:
    trees:
      - local-tree
      - graft::tree
      - graft-repos::example

The graft-repos graft entry demonstrates how to use a custom root directory for the trees provided by the grafted configuration.

The grafts.yaml file provides a tree called tree and a variable called value. We refer to them as graft::tree when specifying trees and ${graft::value} when using variables.

graft.yaml contains the following:

# The grafted "graft.yaml" file.
trees:
  tree: https://example.org/trees/tree.git

variables:
  value: "grafted value"

Running garden eval '${graft::value}' will output grafted value.

Running garden eval '${value}' will output global grafted value, as it evaluates at global scope.

Running garden eval '${value}' local-tree will output local grafted value, as it evaluates from local-tree's scope.

Examples

These examples demonstrate how to use garden using runnable examples.

Photo Restoration

The "Photo Restoration" example sets up the Bringing Old Photos Back to Life software for restoring old photos that suffer from severe degradation using machine-learning techniques.

Git Cola

The "Git Cola" example sets up a development environment for Git Cola, a PyQt-based graphical user interface for Git.

Git Cola's garden.yaml uses garden as a command runner, to keep track of development forks, and to gather associated trees without the overhead of using submodules.

Command Interpreters

The "Command Interpreters" example demonstrates how to use a custom interpreter on a per-command basis.

Python

The "Python" example demonstrates how to use python3 to interpret all custom commands.

Node

The "Node" example demonstrates how to use node to interpret all custom commands.

Photo Restoration

This example demonstrates how to run the Bringing Old Photo Back to Life photo restoration project.

Setup

Run the following commands to clone the repository, download pre-trained deep-learning model data files used by the software and configure a Python virtualenv used to run the tools.

NOTE: this example uses almost 7GB of disk space.

# Create a directory we'll clone and build a few repositories.
mkdir -p photo-restoration && cd photo-restoration

# Download and audit the garden file we're going to run.
wget https://gitlab.com/garden-rs/garden/-/raw/main/doc/src/examples/photo-restoration/garden.yaml
cat garden.yaml

# One-time setup: Clone all of the trees. This will clone an "old-photos" repo.
garden grow old-photos

# One-time setup: Download resources.
garden setup old-photos

Run the Software

Now that everything is setup we can run the tools using the custom run command provided by the garden.yaml file. The run.py script takes several options.

garden run old-photos -- --help

Arguments can be passed directly to run.py by passing additional arguments after the special double-dash -- "end of options" marker.

The example above pases the --help option for demonstration purposes. You will have to specify the --input_folder <folder> and --output_folder <folder> in order to use the photo restoration tool. See the --help output for more details.

garden.yaml

The following is the contents of the garden.yaml file used in this example.

The setup command defines what happens during garden setup old-photos.

The run command defines when happens during garden run old-photos.

Additional command-line arguments specified after the double-dash -- marker are available to commands via conventional $1, $2, $N, ... "$@" shell variables.

trees:
  old-photos:
    description: |
      Restore old photos using machine-learning.
      $ garden grow old-photos
      $ garden setup old-photos
      $ garden run old-photos -- --help
    url: "https://github.com/microsoft/Bringing-Old-Photos-Back-to-Life.git"
    links:
      - "https://colab.research.google.com/drive/1NEm6AsybIiC5TwTU_4DqDkQO0nFRB-uA?usp=sharing&authuser=2#scrollTo=32jCofdSr8AW"
      - "http://raywzy.com/Old_Photo/"
      - "https://news.ycombinator.com/item?id=25148253"
    environment:
      PATH: "${TREE_PATH}/${env3}/bin"
    commands:
      # "setup" uses ">" post-commands to run "setup/virtualenv" and "setup/download-*".
      setup>:
        - setup/virtualenv
        - setup/download-face-landmarks
        - setup/download-face-checkpoints
        - setup/download-global-checkpoints
      setup/virtualenv: |
        test -d ${env3} || (
            python3 -m venv ${env3}
            ${env3}/bin/pip install -r requirements.txt
        )
      setup/download-face-landmarks: |
        cd ./Face_Detection
        test -f shape_predictor_68_face_landmarks.dat || (
            curl -L http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2 \
                -o shape_predictor_68_face_landmarks.dat.bz2
            bzip2 -v -d shape_predictor_68_face_landmarks.dat.bz2
        )
      setup/download-face-checkpoints: |
        cd ./Face_Enhancement
        test -f checkpoints.zip ||
        curl -L https://github.com/microsoft/Bringing-Old-Photos-Back-to-Life/releases/download/v1.0/face_checkpoints.zip \
            -o checkpoints.zip
        test -d checkpoints || unzip checkpoints.zip
      setup/download-global-checkpoints: |
        cd ./Global
        test -f checkpoints.zip ||
        curl -L https://github.com/microsoft/Bringing-Old-Photos-Back-to-Life/releases/download/v1.0/global_checkpoints.zip \
            -o checkpoints.zip
        test -d checkpoints || unzip checkpoints.zip
      run: ${env3}/bin/python3 run.py "$@"

variables:
  env3: $ python3 -c 'import sys; print("env%s%s" % sys.version_info[:2])'

Git Cola Development

This garden file sets up a development garden with the latest version of Git, Git Cola, qtpy and vx.

Run the following commands to see this in action.

# Create a directory we'll clone and build a few repositories.
mkdir -p cola && cd cola

# Download and audit the garden file we're going to run.
wget https://gitlab.com/garden-rs/garden/-/raw/main/doc/src/examples/git-cola/garden.yaml
cat garden.yaml

# One-time setup: Clone all of the repos in the "cola" garden and use a custom
# "garden grow" is a garden built-in command that clones.
garden grow cola

# "garden setup" command defined in "garden.yaml" to initializes the environment.
garden setup cola

# All set! Now we can run Git Cola from the development environment.
garden run

# Daily development workflow: run tests in each repository in-place.
garden test cola

# Commands can be passed to the underlying "run" command to run Git Cola
# against any Git repository.
garden run -- --repo path/to/any/git/tree

# These projects don't need to be "built", so this is technically a no-op.
# A Rust or C++ Rust project could use something like this to run "make"
# in each repository.
garden build cola

The development repositories are now in your current directory and a development virtualenv is present in the ./dist directory.

garden.yaml

The following is the contents of the garden.yaml file used in this example.

# Create environments for developing Git Cola against development versions of Git.
#
# The following gardens are defined: "cola", "cola/main" and "cola/next".
#
# The "cola" garden runs git-cola using the "git" command from your $PATH.
# The "cola/main" garden runs git-cola using git.git's "main" branch.
# The "cola/next" garden runs git-cola using git.git's "next" branch.
#
# One-time Setup:
#
# * Clone repositories and create worktrees:
#
#   garden grow cola/main cola/next
#
# * Initialize git-cola's Python virtualenv environment.
#
#   garden setup cola
#
# Development workflows:
#
# * Build the "cola/main" garden using git.git's "main" branch:
#   garden build cola/main
#
# * Build the "cola/next" garden using git.git's "next" branch:
#   garden build cola/next
#
# * Run Git Cola
#   garden run
#
# * Run Git Cola on the git/next and git-cola trees using the cola/next garden environment:
#   garden run --trees git/next cola/next
#   garden run -t git-cola cola/next
#
# * Open a shell for running Git Cola in different enviornments
#   garden shell cola/main
#   garden shell cola/next
#
# * Run just Git Cola's tests in each environment.
#   garden test/cola cola
#   garden test/cola cola/main
#   garden test/cola cola/next
#
# * Run tests for all projects in each environment. Also runs Git's testsuite.
#   garden test cola
#   garden test cola/main
#   garden test cola/next

# The following variables are used in the custom commands below.
variables:
  # A virtualenv is created in the ./dist/git-cola/env3 directory.
  prefix: ${GARDEN_ROOT}/git-cola/env3
  jobs: $ nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 8

commands:
  add-all: git add --update
  diff: GIT_PAGER= git diff --patience --color-words "$@"
  lol: git log --decorate --graph --oneline "$@" && echo
  run: vx ${prefix} git cola "$@"
  status:
    - git status --short
    - git branch

templates:
  bin:
    environment:
      PATH: ${TREE_PATH}/bin
  python:
    environment:
      PYTHONPATH: ${TREE_PATH}
  makefile:
    commands:
      build: make -j ${jobs} prefix="${prefix}" all "$@"
      install: make -j ${jobs} prefix="${prefix}" install "$@"
      test: make -j ${jobs} prefix="${prefix}" test "$@"
      doc: make -j ${jobs} prefix="${prefix}" doc "$@"

trees:
  # git/main is the parent worktree that shares its .git storage with the child
  # git/next worktrees using "git worktree" -- https://git-scm.com/docs/git-worktree
  git/main:
    url: https://github.com/git/git.git
    templates: makefile
    environment:
      PATH: ${TREE_PATH}/bin-wrappers
      # git/next is a "git worktree" for git.git's "next" branch.
  git/next:
    worktree: git/main
    branch: next

  # git-cola's worktree can be reused alongside any of the git/* trees defined above.
  git-cola:
    url: https://gitlab.com/git-cola/git-cola.git
    templates: [bin, python]
    commands:
      setup: garden dev
      test/cola: garden test "$@"
      install: garden -D prefix="${prefix}" install "$@"
      test: garden test "$@"
      doc: garden doc "$@"

  qtpy:
    description: Qt abstraction library used by git-cola
    url: https://github.com/spyder-ide/qtpy.git
    templates: python
    setup: vx ${prefix} pip install pytest-qt

  vx:
    description: Utility for activating Python virtualenvs
    url: https://gitlab.com/davvid/vx.git
    depth: 1  # Create a shallow clone using "git clone --depth=1"
    environment:
      PATH: ${TREE_PATH}
    commands:
      test: make test

  # This tree allows the "run" command to be run from any directory.
  cwd:
    path: ${PWD}

groups:
  cola-repos-grp:
    - git-cola
    - qtpy
    - vx

gardens:
  cola:
    groups: cola-repos-grp
    environment:
      PATH: ${prefix}/bin
  cola/main:
    groups: cola-repos-grp
    trees: git/main
    environment:
      PATH: ${prefix}/bin
  cola/next:
    groups: cola-repos-grp
    trees: git/next
    environment:
      PATH: ${prefix}/bin

Pre-defined Custom Commands and Ad-Hoc Commands

Included in garden.yaml are a few few helpful commands that give us a quick view of what's going on in each tree:

garden diff cola
garden status cola
garden lol cola

If we want to perform git stuff (like fetch the latest changes), we can always use garden exec to run arbitrary commands:

garden exec cola git fetch --verbose

# When needed, we can hop into a shell with all of the environment variables set
garden shell cola

Self-contained installation demo

The garden run example runs git and git cola in-place in their respective trees. The git-cola project is not installed into the ./dist directory. It contains just the virtualenv created needed to run it.

In order to create a self-contained installation to run the tools independently of their source repositories we have to install them into the ./dist directory.

The following example installs Git and Git Cola into the ./dist directory by running the "make install" targets in each repo:

garden install cola

Now we can test the installed tools directly by adding ./dist/bin to our $PATH, or just invoke the script directly:

./dist/bin/git-cola

Voila, we now have a fully functional development environment with PyQt5, qtpy and Git Cola ready to go for development.

Command Interpreters

This example demonstrates how to use custom interpreters on a per-command basis.

Examples

garden hello
garden info
# Pass additional arguments after the double-dash `--` end-of-options marker.
garden hello -- cat

garden.yaml

# Demo using custom interpreters on a per-command basis.
commands:
  # The "#!" shebang line specifies the interpreter to use.
  hello: |
    #!python3 -c
    import sys
    # dash_c = sys.argv[0]  # sys.argv[0] contains '-c'
    args = sys.argv[1:]  # additional arguments specified after '--'
    if args:
        world = ' '.join(args)
    else:
        world = 'world'
    print(f'hello {world}')
  # The default `garden.shell` is used when no shebang line is present.
  info: echo ${uname}

# NOTE: exec expressions are always run using the system's default #!/bin/sh shell.
variables:
  uname: $ uname -a

Python Commands

This example demonstrates how to configure garden.shell to use python3 when running custom commands.

Examples

garden hello
garden info
# Pass additional arguments after the double-dash `--` end-of-options marker.
garden hello -- cat

garden.yaml

# Demo using "python3" as the interpreter for custom commands.
garden:
  shell: python3

commands:
  hello: |
    import sys
    # dash_c = sys.argv[0]  # sys.argv[0] contains '-c'
    args = sys.argv[1:]  # additional arguments specified after '--'
    if args:
        world = ' '.join(args)
    else:
        world = 'world'
    print(f'hello {world}')

  info: |
    import os
    import sys
    print(f'os.name = {os.name}')
    print(f'sys.platform = {sys.platform}')
    print(r'sys.int_info = {sys.int_info}')
    print('uname = ${uname}')

variables:
  # NOTE: exec expressions are always run using the system's default #!/bin/sh shell.
  uname: $ uname -a

Node.js Commands

This example demonstrates how to configure garden.shell to use node when running custom commands.

Examples

garden hello
garden info
# Pass additional arguments after the double-dash `--` end-of-options marker.
garden hello -- cat

garden.yaml

# Demo using "node" as the interpreter for custom commands.
garden:
  shell: node

commands:
  hello: |
    if (process.argv.length > 1) {
      console.log('hello ' + process.argv.slice(1).join(' '));
    } else {
      console.log('hello world')
    }

  info: |
    console.log(process)
    console.log('${uname}')

# NOTE: exec expressions are always run using the system's default #!/bin/sh shell.
variables:
  uname: $ uname -a

FAQ, Tips and Tricks

Can garden print out each command in a multi-line command as it is run?

While garden -vv can be used to make garden print out the rendered command right before garden runs it, all of the commands in a multi-line command are printed at once before any command is run.

You can enable "echo mode" in the command's shell to make it print out each command as it is run.

Enable echo mode by calling set -x in your command.

commands:
  echo: |
    set -x
    echo hello
    echo world

Calling set -x results in the following output from garden echo:

$ garden --quiet echo
+ echo hello
hello
+ echo world
world

Lines starting with + display the command being used.

The following is printed instead when "echo mode" is not enabled.

$ garden --quiet echo
hello
world

Changelog

v1.9.0

Released 2024-10-11

Features:

  • garden exec can now run commands in parallel using the -j# | --jobs=# option. (#43)

Packaging:

  • Garden's Nix flake was improved and using Garden with Nix home-manager was documented. (#46) (#17)

Development:

  • Internal APIs for running commands were refactored.

  • The yaml-rust2 dependency was upgraded to v0.9.0.

v1.8.0

Released 2024-09-26

Features:

  • garden cmd and custom commands now have a -j# | --jobs=# option for running commands in parallel. Use -j0 | --jobs=0 to use all available cores. (#43) (#45)

  • garden ls now has a --reverse | -r option to display trees in reverse order. (#44)

Development:

  • The which, yansi and strum dependencies were upgraded. yansi was a new major version and required a fair amount of changes. strum involved minor changes. (#42)

v1.7.0

Released 2024-06-29

Features:

  • garden ls now has a --commands | -c option to display just commands. The related --no-commands | -C option is used to omit commands from being displayed. (#39) (#41)

  • garden cmd and garden <custom-command> now support a --dry-run | -N option to perform trial runs without actually running any commands. (#39) (#41)

  • garden exec made -N the short option for its --dry-run option and the original -n short option was made an undocumented alias for compatibility. (#41)

  • The garden eval, garden exec, garden cmd and custom sub-commands now accept the same --define | -D name=value override options as the root garden command.

  • garden grow reports more details about the commands it runs and no longer prints redundant git config commands.

Fixes:

  • garden ls now prints the list of commands in the same order as they appear in garden.yaml. (#39) (#41)

Packaging:

  • The nix flake was updated to re-enable llvm coverage. (#38)

  • nix run can now be used to run garden and nix shell can now be used to open a nix shell with garden installed. (#40)

Development:

  • More structs, functions and methods were made private.

  • Several types were renamed from "HashMap" to "Map".

v1.6.0

Released 2024-06-02

Features:

  • zsh is now invoked using zsh +o nomatch for better portability across shells. This prevents zsh from erroring when wildcard patterns find no matches. Wildcards can be used, for example, to implement a custom clean command that feeds rm -f using wildcard patterns, but these commands would generate errors without disabling nomatch. The zsh nomatch option is a less useful option for non-interactive use so we disable it unconditionally.

  • The --verbose | -v option can now be passed to custom and built-in commands. The verbose option was previously a global option that had to be specified before sub-commands. The following invocations are all equivalent now:

    • garden -vv build
    • garden -v build -v
    • garden build -vv

    (#36)

Packaging:

  • The nix flake was updated to use Fenix for the latest stable rustc 1.78.0. (#37)

Development:

  • An .envrc file was added to enable the nix flake for direnv users. (#37)

v1.5.0

Released 2024-04-14

Features:

  • Running garden init inside a Git repository will now record the current directory as a tree with its path set to ${GARDEN_CONFIG_DIR}. (#34)

  • Custom commands skip missing trees by default. A new -f | --force option can be used to make garden run commands on missing trees. (#33)

  • garden plant now avoids updating the configuration when a tree is re-planted and its configuration contains expressions that evaluate to the same value as currently exist in git. (#31) (#32)

Packaging:

Development:

  • The original github repository under davvid's namespace was transferred to the garden-rs organization on github.

  • The yaml-rust2 dependency was upgraded to 0.8.0 to avoid the encoding crate (RUSTSEC-2021-0153).

v1.4.1

Released 2024-03-22

Features:

  • The empty directory detection in garden grow was improved.

Development:

  • The internal APIs were updated to use AsRef<Path> wherever possible.

v1.4.0

Released 2024-03-21

Features:

  • Custom commands can now specify an interpreter to use on a per-command basis. If a command uses a shebang #! line then the command's text will be passed as the next argument to the specified command. For example, using #!python3 -c as the first line in a custom command will cause python3 -c <command> to be executed.

  • Trees can now use branches defined in separate remotes when configuring the default branch to checkout. garden grow will now fetch the remote associated with the configured branch switching branches in order to make this possible.

  • Trees can now use any upstream branch from any configured remote in the branches section. Previously, branches associated with non-default remotes could not be created unless they were fetched beforehand. garden grow will now fetch the associated remote before creating the local branch.

  • garden grow now detects empty directories (e.g. the directories that are created when using uninitialized Git submodules) and will properly clone into the empty directories instead of treating them like an already-grown tree. (#30)

Development:

  • garden can now be built on Windows. Symlink trees and the XDG base directory support is UNIX-only and disabled on Windows. (#17)

  • yaml-rust2 is now used instead of the yaml-rust-davvid fork that was being maintained by @davvid for use by garden. (#29)

v1.3.0

Released 2024-02-19

Features:

  • garden eval and garden expressions in general will now resolve variables defined within environment blocks. Environment blocks were previously not considered when resolving variables. Environment blocks are now resolved and checked for variables when ${variable} expressions do not find the variable in scopes with higher precedence. The precedence order, from strongest to weakest, is the variables block in a garden's scope, the variables block in a tree's scope, the variables block in global configuration scope, the environments block in a garden's scope, the environments block in a tree's scope, the environments block in global configuration scope and, lastly, OS environment variables. The first entry found is used when expanding variable expressions. (#23)

  • Evaluation cycles (i.e. circular variable dependencies) are now prevented when evaluating garden variables. The evaluation engine will now return empty strings when a variable with a cyclical expression is evaluated. (#24)

  • When zsh is used as the garden.shell, which happens automatically when zsh is installed, garden will now use zsh -o shwordsplit in order to enable word-splitting of $variable expressions by default. This makes zsh behave just like other shells by default, which improves the portability of commands. Configure garden.shell-wordsplit to false or use the garden <cmd> -z | --no-wordsplit option to opt-out of this behavior. (#25)

  • garden.shell can now be configured to use arbitrary commands for executing command strings. Garden uses the configured garden.shell as-is and does not augment its options (e.g. -e or -o shwordsplit) when a custom command is used. Custom commands are identified as commands that expand to 2 or more command-line arguments. Thus, python3 is not considered a custom command and garden will run python3 -c <string> to run commands. On the other hand, specifying ruby -e is considered a custom command because it expands to ["ruby", "-e"] under the hood. If you need to use a custom command that takes no additional command-line arguments then you can use env as an extra argument to have it be considered as a custom shell. For example, env custom-shell will cause garden to run env custom-shell <string>, which is equivalent to custom-shell <string>. Using just custom-shell would have resulted in garden running custom-shell -c <string> instead, which may not be desired. (#26)

  • The garden shell command can now be configured to use an interactive command shell that is distinct from the command specified in the garden.shell configuration by configuring the garden.interactive-shell value. (#26)

  • garden shell can now be run without any arguments. The tree query now defaults to . so that the tree in the current directory is used when nothing is specified. (#26)

  • Custom commands now have access to a ${GARDEN_CMD_VERBOSE} and ${GARDEN_CMD_QUIET} variables which can be used to forward the --verbose and --quiet arguments down into child garden invocations. ${GARDEN_CMD_VERBOSE} uses the short -v flag in the value to support the case where the verbose option is specified multiples times to increase the verbosity level (e.g. -vv). (#27)

v1.2.1

Released 2024-02-05

Development:

  • The yaml-rust-davvid dependency was upgraded to v0.6.0.

  • Documentation and code maintenance.

v1.2.0

Released 2024-01-27

Features:

  • If a garden file has no trees defined at all then an implicit tree called . will now be synthesized into existence. The presence of this implicit tree allows garden files that define just the current directory as a tree to omit the entire trees section altogether. This consequently makes it easier to use garden as a simple command runner because the commands section is the only section required in order to run garden commands.

  • When garden.root is not configured garden will behave as if garden.root is configured to ${GARDEN_CONFIG_DIR}. This allows garden files to omit garden.root from their configuration in typical scenarios.

  • Configuring garden.root to an empty string ("") will behave as if garden.root is configured to the current directory from which garden was run.

  • When a garden.yaml file does not exist in the current directory then garden will walk up the file system searching for garden.yaml or the name specified using garden -c <name>. Garden will behave as if it were launched from the directory containing the garden file when a configuration file is found.

  • The GARDEN_CEILING_DIRS and GIT_CEILING_DIRS environment variables can be used to limit the garden.yaml discovery by preventing garden from traversing into and beyond the specified directories when discovering garden files.

  • garden exec, garden cmd garden grow, garden ls and custom garden commands can now filter the trees they operate over by passing a glob pattern using -t | --trees option. These commands will only operate on the trees whose names match the pattern. This allows you to specify a garden as the tree query and use the full set of environment variables from all trees in the query while executing commands over a subset of the trees in that garden.

  • garden init will now add the current directory to the trees block when the current directory contains a Git repository. Use garden init --empty to disable this behavior.

Development:

v1.1.0

Released 2024-01-15

Features:

  • garden ls now displays information about trees, groups, gardens and commands.

  • garden ls -c (i.e. --no-commands) hides command details from the output.

  • garden plant -s (i.e. --sort) sorts all of the configured trees entries after planting the specified trees.

  • garden exec -n (i.e. --dry-run) performs a trial run without executing any commands.

  • garden.shell-errexit can now be configured to false in garden.yaml to opt-out of using the -e exit-on-error shell option when running custom commands.

  • garden.shell can now be configured to bun, fish, node, perl and python3 in addition to the traditional bash, zsh, dash, ksh and sh shells. This allows you to use these interpreters to run custom commands.

Development:

v1.0.0

Released 2023-12-23

Features:

  • Commands can now specify pre-commands and post-commands that are run before/after the specified command. (#3) (documentation)

  • The default origin remote name can now be configured using tree.<tree>.default-remote. (#16)

  • Commands now display the tree's current branch alongside the tree name. (#18)

  • garden -vv exec and garden -vv shell now display the command being run.

Packaging:

  • garden can now be installed as a nix flake package. A flake.nix file is now provided. (#16)

v0.9.1

Released 2023-11-19

Fixes:

  • garden -D name=value now overrides variables in all scopes. Variables defined in tree scope were not subject to overrides and will now get properly overridden by the --define / -D command-line options.

v0.9.0

Released 2023-11-02

Features:

  • garden grow now sets git config remote.$name.tagopt --no-tags when adding additional remotes. This prevents accidentally fetching tags when interacting with remotes.

v0.8.1

Released 2023-07-18

Fixes:

  • garden grow was fixed to detect existing branches when growing git worktree-created child worktrees.

Development:

  • strum is now used to implement FromStr for enum ColorMode.

  • is-terminal is now used instead of the unmaintained atty crate.

v0.8.0

Released 2023-07-16

Features:

v0.7.0

Released 2023-02-12

Features:

  • Trees, Groups, Gardens and Commands defined in the top-level garden.yaml can now override entries defined via garden.includes. Configuration entities now follow "last one wins" semantics -- if the same entity is defined in multiple includes files then only the final definition will be used. (#14) (#15)

  • Trees now sparsely override existing entries. This behavior allows a tree definition to replace just the url field, or to replace individual tree commands while retaining the rest. Use replace: true in a Tree definition in order to completely replace the existing entry instead of sparsely overriding it.

  • Improved shell completions for garden, garden init and garden plant.

Packaging:

v0.6.0

Released 2023-01-20

Features:

  • Both names and values in gitconfig can now use ${var} expressions. Previously only values were evaluated. Names are evaluated now as well.

Fixes:

v0.5.1

Released 2023-01-15

Fixes:

  • Exec expressions were previously run with the current directory set to the directory from which garden was run. Exec expressions are now run in the tree's current directory.

v0.5.0

Released 2023-01-12

Features:

  • Garden configuration files can now include other configuration files by specifying the additional files to include in the garden.includes field. The includes feature makes it possible to create modular and reusable garden files. The trees, variables, commands, groups and gardens defined in the included files are added to the current configuration. (#7)

  • Garden commands can now use shell variables using the standard (brace-less) shell $variable syntax. The braced ${garden} variable syntax remains reserved for resolving Garden Variables. Double-$ braces (ex: $${...}) can be used to escape a $${variable} from evaluation so that a literal ${variable} value is used in the garden command. (#11) (#12)

  • A new garden completion subcommand was added for providing shell command-line completion using the clap_complete crate. (#9)

  • garden -V | --version was added alongside the clap rewrite for displaying the garden command version.

Development:

  • The Makefile has been replaced by a garden.yaml Garden file. We can now use garden {build, test, check, fmt, clippy, ...} instead of make .... See garden.yaml @ 5ef8d0ab16 for more details. Packagers can use cargo install to install garden and invoke mdbook directly to install the user manual. We also provide garden -D DESTDIR=/tmp/stage -D prefix=/usr/local install-doc if distros want to install the user manual using our recipe. (#8)

  • Garden's command-line parsing has been rewritten to leverage the clap crate and ecosystem.

v0.4.1

Released 2022-12-26

Features:

  • The garden cmd --no-errexit option was extended to work with commands that are configured using a YAML list of strings. Commands that are specified using lists are now indistinguishable from commands specified using multi-line strings.

v0.4.0

Released 2022-12-23

Breaking Changes:

  • garden cmd now runs custom commands using <shell> -e -c '<command>' by default. The use of -e is a change in behavior and causes multi-line / multi-statement commands to halt execution when the first non-zero exit code is encountered. Use set +e at the top of of a multi-line command to opt-out of this behavior on a per-command basis, or specify the -n | --no-errexit option.

  • garden will now fallback to bash (and sh) as the default garden.shell value when zsh (and bash) are not installed. As before, the garden.shell configuration variable can be used to override the default shell.

Features:

  • garden prune was added for removing orphaned Git repositories. (#4)

  • garden cmd can now run commands in breadth-first order when the -b/--breadth-first option is used. Depth-first tree traversal is the default. The garden cmd --breadth-first traversal runs all commands on a tree before continuing on to the next tree. The default garden cmd depth-first traversal runs a command across all trees before continuing on to the next command. (#3)

v0.3.0

Released 2022-08-20

Features:

  • garden plant can now detect git worktree repositories. (#1)

v0.2.0

Released 2022-07-29

Breaking Changes:

  • garden add was renamed to garden plant.

Features:

  • garden grow can now grow trees using "git worktree" (#1).
  • garden grow learned to clone specific branches.
  • garden grow and garden plant can now handle bare repositories.

v0.1.0

Released 2022-06-13

Features:

This is the initial garden release.

  • garden grow grows worktrees.
  • garden init initializes configuration.
  • garden plant (formerly garden add) adds existing trees.
  • garden cmd and garden <custom-command> can run custom commands.
  • Templates, variables, and environment variables are all supported.