Skip to content

import

Export existing repository settings as YAML. Useful for bootstrapping gh-infra configuration from an existing repository.

Terminal window
gh infra import <owner/repo> [owner/repo ...]
ArgumentExampleBehavior
<owner/repo>gh infra import babarot/my-projectImport that repository
Multiple reposgh infra import babarot/my-project babarot/my-cliImport each repository

When using --into, arguments are optional. If omitted, all repositories defined in the manifests are targeted.

FlagDescription
--into <path>Pull GitHub state into existing local manifests (directory or file path)
Terminal window
# Import and save to a file
gh infra import babarot/my-project > repos/my-project.yaml
# Import multiple repositories
gh infra import babarot/my-project babarot/my-cli
# Import and review
gh infra import babarot/my-project
# Pull GitHub state into existing manifests (specific repo)
gh infra import babarot/my-project --into=repos/my-project.yaml
# Pull GitHub state for all repos defined in manifests
gh infra import --into=repos/

The output is a complete Repository YAML manifest reflecting the current state of the repository on GitHub.

--into: Pull GitHub State into Local Manifests

Section titled “--into: Pull GitHub State into Local Manifests”

With --into, import works in the reverse direction of plan/apply: it fetches the current GitHub state and updates your existing local YAML manifests to match.

Terminal window
# Import specific repos into manifests
gh infra import <owner/repo> [owner/repo ...] --into=<path>
# Import all repos defined in manifests
gh infra import --into=<path>

The path can be a single YAML file or a directory containing manifests.

When no owner/repo arguments are given, all repositories defined in the manifests (from Repository, RepositorySet, and FileSet resources) are targeted automatically.

  1. Parse local manifests at the given path
  2. Match targets to resources in the manifests — either from arguments or all repos in manifests when no arguments are given
  3. Fetch the current state from GitHub (in parallel)
  4. Diff local vs GitHub, field by field
  5. Display the plan (repo setting changes + file changes with diff stats)
  6. Confirm with interactive diff viewer (for file changes) or simple prompt (repo-only changes)
  7. Write approved changes to local files
Terminal window
# Pull GitHub state for all repos defined in manifests
gh infra import --into=repos/
# Pull GitHub state into a specific manifest file
gh infra import babarot/my-project --into=repos/my-project.yaml
# Pull from a directory of manifests
gh infra import babarot/my-project --into=repos/
# Import multiple repositories at once
gh infra import babarot/my-project babarot/my-cli --into=repos/

After the plan is displayed, the confirmation prompt offers three options:

> Apply import changes? (y)es / (n)o / (d)iff

Press d to open a full-screen diff viewer for file-level changes:

KeyAction
/ or j/kSelect file
TabCycle write / patch / skip for the selected file
d/uScroll diff pane
q/EscReturn to confirmation

Repository setting changes (description, visibility, features, etc.) are shown in the terminal plan output, not in the diff viewer.

The import diff viewer does not just let you approve or reject a file change. For each file, you can choose how the change should be written back:

  • write
    • update the file’s normal write-back target
    • inline entries update the manifest content: | block
    • source-backed entries update the local source file
  • patch
    • store the drift as a manifest patch under patches:
  • skip
    • do not apply that file change in this import run

The default action depends on the file shape:

File shapeDefaultAllowed
Inline contentwritewrite, skip
Local source (single-use)writewrite, patch, skip
Local source shared by multiple repospatchwrite, patch, skip
Existing patches: entrypatchwrite, patch, skip
Simple <% .Repo.* %> substitutionswrite/patch/skip based on file shapeshown in viewer
Files whose remote content cannot be safely written back to the templateskipped in plannot shown in viewer
github:// sourceskipped in plannot shown in viewer

This is especially useful for shared source files:

  • the safe default is patch, so one repo’s drift does not immediately rewrite the shared source
  • but if you intentionally want to update the shared source/template itself, switch that entry to write

There are two kinds of skip behavior:

  • hard skip: the file cannot be written back safely, so it is shown only in the plan with a skip reason
  • default skip: the file is skipped by default, but you can press Tab in the diff viewer to switch to write or patch

For example, files with reconcile: create_only default to skip, while files whose remote content cannot be safely written back to the template are hard-skipped.

These two skip modes mean very different things:

  • default skip
    • the file is importable
    • gh-infra chooses skip as the safest default for this run
    • you can still press Tab and switch to write or patch
  • hard skip
    • gh-infra could not produce a safe local write-back result
    • the file is shown in the terminal plan only
    • it does not appear in the diff viewer and cannot be toggled

In practice:

  • reconcile: create_only files are usually default skip
  • github:// sources are always hard skip
  • template-backed files may be either:
    • importable, if gh-infra can preserve placeholders and safely apply only the literal drift
    • hard-skipped, if the remote file can no longer be mapped back to the original template without risk

For files containing gh-infra template placeholders such as <% .Repo.Name %> or <% .Repo.FullName %>, import does not compare the raw template source directly against the GitHub file.

Instead, gh-infra:

  1. renders the local template for the target repository
  2. compares that rendered content with the GitHub file
  3. tries to reconstruct updated template source while preserving the original placeholders

This means simple cases can be imported safely. For example:

  • module github.com/<% .Repo.FullName %> can stay templated while nearby literal lines such as go 1.26.1 are updated
  • /<% .Repo.Name %> can still be preserved if only the surrounding punctuation changes in a safe way

However, gh-infra hard-skips a template-backed file when the remote file no longer has a structure that can be safely mapped back to the original template. Typical examples are:

  • the remote file removed or rewrote the placeholder-backed lines entirely
  • the remote file changed placeholder-derived content in a way that is ambiguous to reverse
  • the template uses unsupported control flow or complex template syntax

So the rule of thumb is:

  • if placeholders are still recognizable and only literal drift changed, import can usually proceed
  • if the remote file effectively stopped looking like the original template, gh-infra hard-skips it

For implementation details and exact safety rules, see Import Into Manifests.

ResourceBehavior
Repository settingsField-by-field comparison and YAML patch
RepositorySet entriesMinimal override reconstruction (preserves defaults/override separation)
Files with local source (source: ./path)Local file overwritten with GitHub content
Files with inline content (content: |)YAML content block updated in-place
Files with reconcile: create_onlyImported (updates the local master template for future repos)
SourceReason
Files whose remote content cannot be safely written back to the templateThe remote content cannot be mapped back to the original template source without risking an incorrect rewrite
Files from GitHub source (source: github://...)No local file to write back to
SecretsGitHub API does not return secret values; local values are preserved

Skipped files are shown in the plan output with a warning icon and the skip reason displayed dimmed.

When a source file is shared across multiple repositories in a FileSet, the default import action is patch, not write.

This means:

  • by default, drift from one repository is captured in manifest patches
  • the shared source file is left unchanged
  • other repositories are not affected immediately

If you intentionally want to make the shared source authoritative and propagate that change, switch the entry to write in the diff viewer.