Skip to main content
Infrahub provides deep integration with Git repositories, allowing you to store schemas, transformations, checks, and generators in version-controlled repositories. Changes to Git repositories automatically sync with Infrahub and trigger associated workflows.

Repository Types

Infrahub supports two types of Git repositories:

Standard Repository (Read-Write)

Full bidirectional integration with branch synchronization:
  • Automatically syncs branches from Git to Infrahub
  • Creates new Infrahub branches when Git branches are detected
  • Supports merge operations from Infrahub to Git
  • Imports objects (schemas, generators, transformations) on sync
  • Validates branch structure before sync
Internal Statuses:
  • active: Repository is fully operational and syncing
  • staging: Repository is being prepared for activation

Read-Only Repository

One-way sync from a specific Git ref:
  • Tracks a single ref (branch/tag/commit)
  • Pulls updates from the remote ref
  • Cannot push changes back to Git
  • Useful for consuming external schemas or libraries

Adding a Repository

Using the UI

  1. Navigate to Resources > Repositories
  2. Click Add Repository
  3. Configure:
    • Name: Unique identifier
    • Location: Git URL (https:// or ssh://)
    • Default Branch: Main branch name
    • Credential: Optional authentication

Using the SDK

from infrahub_sdk import InfrahubClient

client = InfrahubClient()

repo = await client.create(
    kind="CoreRepository",
    data={
        "name": "network-automation",
        "location": "https://github.com/org/network-automation.git",
        "default_branch": "main",
        "commit": "",
    }
)
await repo.save()

Repository Synchronization

Automatic Sync

Repositories sync automatically every minute via the git_repositories_sync workflow:
# backend/infrahub/git/tasks.py:195
@flow(name="git_repositories_sync", flow_run_name="Sync Git Repositories")
async def sync_remote_repositories() -> None:
    # Fetches from remote origins
    # Compares local and remote branches
    # Imports objects from new/updated branches
    ...

Manual Sync

Trigger sync via GraphQL mutation:
mutation SyncRepository {
  CoreRepositorySync(data: { id: "<repository-id>" }) {
    ok
  }
}

Sync Workflow

  1. Fetch: Pull latest changes from remote
  2. Compare: Identify new and updated branches
  3. Validate: Check branch structure matches patterns
  4. Create Branches: Create Infrahub branches for new Git branches
  5. Import Objects: Load schemas, generators, checks from files
  6. Update Commit: Store latest commit SHA in database

Branch Mapping

Infrahub maps Git branches to Infrahub branches:
  • Git branch main → Infrahub branch main
  • Git branch feature/new-schema → Infrahub branch feature/new-schema
  • Branches are created automatically on first sync
  • Worktrees manage per-branch checkouts

Repository Operations

Creating Branches

When you create a branch in Infrahub, it’s created in all active repositories:
# backend/infrahub/git/tasks.py:137
@flow(name="git-repositories-create-branch")
async def create_branch(branch: str, branch_id: str) -> None:
    repositories: list[CoreRepository] = await client.filters(kind=CoreRepository)
    for repository in repositories:
        await git_branch_create(
            repository_name=repository.name.value,
            repository_id=repository.id,
            branch=branch,
            branch_id=branch_id
        )

Merging Branches

Merge operations sync to Git repositories:
# backend/infrahub/git/repository.py:161
async def merge(self, source_branch: str, dest_branch: str, push_remote: bool = True) -> bool:
    """Merge the source branch into the destination branch."""
    repo = self.get_git_repo_worktree(identifier=dest_branch)
    commit = self.get_commit_value(branch_name=source_branch, remote=False)
    
    # Merge with optional explicit merge commit
    if config.SETTINGS.git.use_explicit_merge_commit:
        repo.git.merge(commit, "--no-ff", m="Merged by Infrahub")
    else:
        repo.git.merge(commit)
    
    # Push to remote if configured
    if self.has_origin and push_remote:
        await self.push(branch_name=dest_branch)

File Import

Repositories can contain:

Schemas

  • .schema/ directory
  • YAML files defining node types
  • Auto-loaded on sync

Generators

  • Python files with generator classes
  • Registered via CoreGenerator objects
  • Executed to create nodes

Transformations

  • Jinja2 templates or Python transforms
  • Used by artifacts and webhooks
  • Referenced by CoreTransformJinja2 or CoreTransformPython

Checks

  • Python classes for validation
  • Executed during proposed change validation
  • Referenced by CoreCheckDefinition

Import Process

# backend/infrahub/git/integrator.py
async def import_objects_from_files(
    self,
    infrahub_branch_name: str,
    commit: str | None = None,
) -> None:
    # Loads schemas from .schema/ directory
    # Processes Python modules for generators/checks
    # Imports data files (YAML/JSON)
    ...

Commit Tracking

Each repository tracks the commit SHA per branch:
  • Stored in the commit attribute
  • Updated on successful sync
  • Used to detect changes between syncs
  • Enables diff operations between commits

Worktrees

Infrahub uses Git worktrees for branch isolation:
# backend/infrahub/git/worktree.py
class Worktree:
    """Manages a Git worktree for a specific branch or commit."""
    
    def create(self, identifier: str, branch_name: str | None = None) -> None:
        # Creates isolated checkout in .worktree/{identifier}/
        ...
Benefits:
  • Multiple branches checked out simultaneously
  • Isolated environments for transforms/checks
  • No branch switching overhead

Repository Models

GitRepositoryAdd

# backend/infrahub/git/models.py:50
class GitRepositoryAdd(BaseModel):
    location: str  # External URL
    repository_id: str
    repository_name: str
    created_by: str | None
    default_branch_name: str | None
    infrahub_branch_name: str  # Infrahub branch to sync to
    infrahub_branch_id: str
    internal_status: str  # active, staging

GitRepositoryMerge

# backend/infrahub/git/models.py:88
class GitRepositoryMerge(BaseModel):
    repository_id: str
    repository_name: str
    source_branch: str
    destination_branch: str
    destination_branch_id: str
    default_branch: str | None

Authentication

Repositories support multiple authentication methods:

SSH Keys

  • Configure SSH credential in Infrahub
  • Link to repository via credential relationship
  • Private key stored securely

HTTPS with Tokens

  • Personal access tokens
  • Stored in credential objects
  • Injected into Git URLs

Credential Objects

Create via SDK:
credential = await client.create(
    kind="CorePasswordCredential",
    data={
        "name": "GitHub Token",
        "username": "git",
        "password": "ghp_xxxxxxxxxxxx"
    }
)
await credential.save()

# Link to repository
repo.credential = credential
await repo.save()

Conflict Detection

Infrahub checks for merge conflicts during proposed change validation:
# backend/infrahub/git/tasks.py:844
@flow(name="git-repository-check-merge-conflict")
async def run_check_merge_conflicts(model: CheckRepositoryMergeConflicts) -> ValidatorConclusion:
    repo = await get_initialized_repo(...)
    async with lock.registry.get(name=model.repository_name, namespace="repository"):
        conflicts = await repo.get_conflicts(
            source_branch=model.source_branch,
            dest_branch=model.target_branch
        )
    
    if conflicts:
        # Create failure checks for each conflict
        return ValidatorConclusion.FAILURE
    return ValidatorConclusion.SUCCESS

Best Practices

Repository Structure

network-automation/
├── .schema/
│   └── network.yml
├── generators/
│   └── interface_generator.py
├── checks/
│   └── vlan_validation.py
├── transforms/
│   ├── templates/
│   │   └── device_config.j2
│   └── python/
│       └── custom_transform.py
└── data/
    └── initial_devices.yml

Performance

  • Use read-only repositories for reference data
  • Limit branch creation to necessary branches
  • Keep repositories focused (don’t store large binaries)
  • Use .gitignore to exclude generated files

Security

  • Use SSH keys for private repositories
  • Rotate credentials regularly
  • Audit repository access logs
  • Validate schemas before import

Troubleshooting

Repository Not Syncing

  1. Check repository operational status: operational_status
  2. Verify credential is valid
  3. Review sync logs in workflow runs
  4. Ensure repository is reachable from Infrahub server

Import Failures

  1. Validate schema files locally first
  2. Check Python syntax in generator/check files
  3. Review error messages in import logs
  4. Verify file paths are correct

Merge Conflicts

  1. Use conflict detection check
  2. Resolve conflicts in Git
  3. Re-sync repository
  4. Retry proposed change merge

Build docs developers (and LLMs) love