Skip to content

Git Expressions

Match units and stacks based on changes between Git references. This is useful for targeting infrastructure that has been modified, added, or removed between commits, branches, or tags.

Git-based expressions are written between [ and ] characters, and use the ... operator to indicate the range of changes to compare.

Terminal window
# Compare between two references
terragrunt find --filter '[main...HEAD]'
# Shorthand: compare reference to HEAD
terragrunt find --filter '[main]'
# Compare between specific commits
terragrunt find --filter '[abc123...def456]'
# Compare between tags
terragrunt find --filter '[v1.0.0...v2.0.0]'
# Compare using relative references
terragrunt find --filter '[HEAD~1...HEAD]'
# Compare between branches
terragrunt find --filter '[feature-branch...main]'
  • Directory.
    • Directorymodified-unit <— Matched by [main…HEAD] (terragrunt.hcl was modified)
      • terragrunt.hcl (modified)
    • Directorynew-unit <— Matched by [main…HEAD] (terragrunt.hcl was added)
      • terragrunt.hcl (added)
    • Directoryremoved-unit <— Matched by [main…HEAD] (terragrunt.hcl was removed)
      • (directory removed)
    • Directoryunchanged-unit
      • terragrunt.hcl (unchanged)

When evaluating a Git-based filter, Terragrunt will first generate a worktree for every reference that needs to be evaluated, and assess the diffs between Git references.

e.g. For a filter like [main...HEAD], Terragrunt will generate a worktree for main and one for HEAD in temporary directories, and use git diff to assess the diffs between the two references.

Then, for any unit that is discovered within those worktrees, Terragrunt will enqueue that unit for a run in the run queue in the worktree where it was discovered.

In the example above, the modified-unit will be discovered in a “to” temporary directory (e.g. /tmp/.../terragrunt-worktree-HEAD.../modified-unit), whereas the removed-unit would be discovered in the “from” temporary directory (e.g. /tmp/.../terragrunt-worktree-main.../removed-unit).

This is important to recognize, as it’s how destroys will be possible despite the fact that the unit is no longer present in the current working directory. As a consequence, however, you may find that paths don’t behave how you expect, as you will be performing runs in the temporary directories created for the relevant worktrees.

When using Git-based expressions and the run command, you are required to use one of the plan or apply commands, and not the -destroy flag.

This is because whether or not a unit will be destroyed is determined by logic relevant to inspecting changes in Git.

When units are added or modified between two Git references, they will be planned or applied. When the units are removed between two Git references, they will be planned for destruction (with plan -destroy) or destroyed (with apply -destroy).

In the scenario above, running the following:

Terminal window
terragrunt run --filter '[main...HEAD]' plan

Will result in the following:

  • modified-unit will be planned (tofu plan)
  • new-unit will be planned (tofu plan)
  • removed-unit will be planned for destruction (tofu plan -destroy)
  • unchanged-unit will be ignored

For the common use case of comparing the default branch (typically main) with HEAD, you can use the --filter-affected flag as a convenient shorthand:

Terminal window
# Find components affected by changes between main and HEAD
terragrunt find --filter-affected
# Equivalent to:
terragrunt find --filter '[main...HEAD]'

The --filter-affected flag automatically detects your repository’s default branch and compares it with HEAD.