Skip to main content

Understanding branching in Infrahub

Infrahub’s branching model allows teams to work on features, fixes, or experiments in isolated environments. Each branch represents a separate line of development, enabling parallel work streams without interference. Unlike traditional version control systems that branch files, Infrahub branches graph data representing your infrastructure, providing flexibility when working with complex, interconnected components.

Why branching matters for infrastructure

Infrastructure changes are risky. Deploying untested configurations to production can cause outages. Making large-scale architecture changes requires coordination across teams. Testing new ideas in production isn’t feasible. Branching solves these challenges by providing isolated workspaces:
  • Risk isolation: Test changes without affecting production systems
  • Parallel workflows: Multiple teams work simultaneously without conflicts
  • Change validation: Review and test before merging to production
  • Experimentation: Try new approaches in a safe environment
  • Atomic changes: Group related modifications into coherent units

Core concepts

Branches as isolated realities

Each branch provides a complete, isolated environment where changes can be made without affecting other branches. When you create a branch, you establish a separate reality where your infrastructure exists in a specific state. Changes you make in that branch are invisible to other branches until explicitly merged. This isolation extends to all aspects of Infrahub:
  • Data changes (devices, interfaces, IP addresses)
  • Schema modifications (adding attributes, changing relationships)
  • Generated artifacts (configuration files, documentation)
  • Validation results (checks, tests, constraints)
Think of branches like parallel universes where your infrastructure can evolve independently. Each branch maintains its own timeline of changes, and you control when those changes merge into the production reality.

Branch hierarchy

Infrahub implements a hierarchical branch model:
  • Branches are created from a parent branch (currently always main)
  • Changes flow back to the parent through controlled merge operations
  • Each branch tracks its creation point (branched_from) for accurate diffing
Currently, Infrahub supports a single level of hierarchy—all branches must be created from and merged back to the default branch. This constraint simplifies conflict resolution and ensures clear change lineage. Future versions may support nested branches (branches from branches).

The default branch

The default branch (typically named main) serves as the authoritative source of truth for your infrastructure. It has unique characteristics that distinguish it from other branches: Integrity guarantees: The default branch enforces comprehensive validation to ensure production data quality:
  • Schema validation: All data must conform to current schema definitions
  • Relationship integrity: All references between objects must be valid
  • Uniqueness constraints: Duplicate values in unique fields are prevented
  • Required fields: All mandatory attributes must have values
These protections ensure production data remains consistent, valid, and ready for deployment to real infrastructure environments. Performance trade-offs: The integrity guarantees come with performance implications:
  • Sequential writes: Certain operations serialize to prevent conflicts
  • Validation overhead: Every change undergoes comprehensive validation
  • Lock contention: Concurrent modifications may experience delays
Feature branches strategically relax some constraints to optimize for development speed. This balanced approach maintains data integrity in production while enabling rapid iteration in development.

Branch isolation modes

When creating a branch, you choose how it interacts with external Git repositories: Full branches (sync_with_git: true): Extend to Git repositories, allowing infrastructure-as-code changes. These branches are synchronized with corresponding Git branches, enabling coordinated changes across both Infrahub data and infrastructure code. Data-only branches (sync_with_git: false): Isolate changes to Infrahub data without affecting Git repositories. These branches are ideal for data updates, corrections, or enhancements that don’t require changes to infrastructure code. This flexibility allows different workflows—from quick data fixes to comprehensive infrastructure changes involving both data and code.

Branch-aware vs. branch-agnostic

Unlike Git where all content is branch-specific, Infrahub introduces a nuanced approach where some schema elements can bypass the branching system and be global to all branches: Branch-aware: Records that are local to branches and follow the standard branching workflow. Changes to branch-aware records in one branch do not affect other branches until explicitly merged. Most user-created infrastructure data falls into this category. Branch-agnostic: Records that exist globally regardless of branch. These typically include system data, global configurations, and reference information that should remain consistent across all branches. Changes to these records are immediately visible in all branches. Branch-local: Records that exist only within a specific branch and are not meant to be merged to other branches. These include temporary data, experimental configurations, or branch-specific metadata that supports the workflow but isn’t part of the final solution. You can define the branch behavior for each node or attribute in the schema:
nodes:
  - name: Device
    namespace: Infra
    branch: aware  # Default - changes are local to branches
    attributes:
      - name: serial_number
        kind: Text
        branch: agnostic  # Serial numbers are global across branches

Architecture and implementation

Copy-on-write semantics

Branches don’t duplicate the entire database. Instead, they maintain:
  • A pointer to their base (branched_from timestamp)
  • Only the delta of changes made within the branch
  • References to unchanged data in the parent branch
This approach means creating a branch has minimal overhead, and storage grows only with actual changes. Creating a new branch is almost instantaneous, as it doesn’t involve copying large amounts of data. When you query data in a branch, Infrahub:
  1. Looks for changes made in the branch after the branched_from timestamp
  2. Falls back to the parent branch for unchanged data
  3. Traverses this relationship recursively up the branch hierarchy
This copy-on-write model is similar to Git’s internal structure but adapted for graph database semantics.

Branch operations

Infrahub provides a comprehensive set of operations to manage the branch lifecycle: Branch creation: Start a new branch from an existing one, typically the main branch. This creates a snapshot of the current state that can be modified independently:
mutation {
  BranchCreate(
    data: {
      name: "feature-new-datacenter"
      description: "Add new datacenter configuration"
      sync_with_git: true
    }
  ) {
    object {
      id
      name
    }
  }
}
Branch merging: Once changes are complete, they can be merged back into the parent branch through a controlled process using Proposed Changes. This ensures that all modifications are validated and conflicts are resolved. The merge process creates a permanent record in the commit history. Branch deletion: After merging, branches can be safely deleted without losing any history. All changes are preserved in the commit history of the default branch:
mutation {
  BranchDelete(data: { name: "feature-new-datacenter" }) {
    ok
  }
}
Branch rebase: Rebasing allows you to incorporate the latest changes from the parent branch into your feature branch. This helps keep your branch up-to-date and minimizes potential merge conflicts.

Merge semantics

Infrahub implements a “latest-value wins” merge strategy. During a merge, only the most recent value for each changed attribute transfers to the destination branch. The complete change history within the branch doesn’t carry over—instead, the final state becomes a single change at the merge point. This approach provides several benefits:
  • Maintains immutability of the main branch by creating a clean, atomic change
  • Simplifies conflict resolution by focusing only on the final state rather than intermediate changes
  • Provides clear merge points in history that are straightforward to trace and understand
  • Reduces storage overhead by not duplicating the entire change history of the branch
This merge strategy aligns with Infrahub’s focus on the current state of infrastructure rather than the historical evolution of individual files.

Rebase operations

Rebasing is a powerful operation that updates a branch with the latest changes from its parent branch:
  1. The branch’s base point (branched_from) updates to the current time
  2. Changes in the branch are analyzed and replayed on top of the new base
  3. Conflicts that arise during this process require manual resolution
  4. The branch history resets, preserving only the final state
Rebasing serves several important purposes:
  • Resolving conflicts before merge: Identify and address conflicts in the development branch before creating a Proposed Change
  • Incorporating upstream changes: Ensure your branch includes the latest updates from the main branch
  • Maintaining a clean merge history: Produce a cleaner, more linear history

Conflict detection and resolution

Conflicts occur when the same data changes in both a feature branch and the main branch. Infrahub’s conflict management system automatically detects several types of conflicts: Attribute conflicts: The same field of an object has been modified in both branches with different values. For example, the hostname of a device is changed to “router-1” in one branch and “router-primary” in another. Relationship conflicts: Conflicting changes to object relationships occur when the connections between objects are modified differently in separate branches. For instance, a device might be assigned to datacenter A in one branch and datacenter B in another. Schema conflicts: Incompatible schema modifications between branches can cause structural conflicts. This might happen when a field is removed in one branch but modified in another. Uniqueness conflicts: Changes that would violate uniqueness constraints upon merge are flagged to prevent data integrity issues. This occurs when both branches create different objects with the same unique identifier. The Proposed Change feature provides comprehensive tools for managing these conflicts with intuitive side-by-side diffs that clearly highlight differences between branches.

Use cases and workflows

Large scale changes

Create a branch for a large feature or refactor, allowing for extensive changes without impacting the production pipeline. This is particularly valuable when:
  • Redesigning network architectures (moving from spine-leaf to fat-tree topology)
  • Migrating to a new vendor (replacing all Cisco devices with Arista)
  • Implementing new standards (transitioning from IPv4 to IPv6)
Example workflow:
  1. Create branch feature-ipv6-migration
  2. Add IPv6 attributes to device and interface schemas
  3. Populate IPv6 addresses across all interfaces
  4. Update routing protocol configurations
  5. Generate updated device configurations in the branch
  6. Test in staging environment
  7. Create Proposed Change to review and merge

Collaborative review

Create a branch for a feature, allowing team members to review and discuss changes before merging:
  1. Junior engineer creates fix-vlan-assignments branch
  2. Makes necessary changes to VLAN configurations
  3. Creates Proposed Change for review
  4. Senior engineer reviews changes, leaves comments
  5. Junior engineer addresses feedback in the branch
  6. Changes are validated by automated checks
  7. Senior engineer approves and merges
This workflow enables quality control and knowledge sharing within the team.

Experimentation

Use branches to test new ideas or approaches without affecting the production environment: A/B testing: Create two branches with different BGP configurations, deploy to test routers, measure performance, and merge the winner. Schema evolution: Test a new schema structure in a branch to ensure it meets requirements before committing to it in production. Vendor evaluation: Model configurations for different vendors in separate branches to compare approaches.

Transaction support

Group related changes into a single branch, ensuring atomic updates and easier rollbacks: Service deployment: When deploying a new service, create a branch that includes:
  • New device records
  • Interface configurations
  • IP address allocations
  • Routing protocol changes
  • Firewall rules
If any part fails validation, the entire branch can be abandoned without affecting production. If everything succeeds, merge atomically.

Best practices

Use descriptive branch names

Name branches in a way that clearly identifies their purpose:
  • feature-network-redesign - Adding new capability
  • fix-datacenter-connectivity - Fixing a problem
  • experiment-bgp-tuning - Testing an idea
  • refactor-location-schema - Improving structure

Keep branches short-lived

Complete work and merge branches promptly to minimize drift from the main branch and reduce conflict potential. Long-lived branches accumulate changes that make merging more complex. Target: Feature branches live days to weeks, not months.

Rebase before creating a Proposed Change

Update your branch with the latest changes from the default branch to identify and resolve conflicts early. This makes the review process smoother and reduces merge complexity.
infrahubctl branch rebase feature-network-redesign

Use data-only branches for quick fixes

When changes don’t require infrastructure code updates, data-only branches provide a streamlined workflow:
  • Fixing incorrect IP addresses
  • Updating device serial numbers
  • Correcting location assignments
  • Adding missing interfaces

Leverage branch permissions

Restrict who can create branches and merge to the default branch to maintain quality control in production environments. Use Infrahub’s permission system to:
  • Allow all engineers to create feature branches
  • Require senior engineers to approve merges to main
  • Restrict schema changes to architecture team

Document branch purpose

Add clear descriptions to branches to help team members understand their purpose and scope:
mutation {
  BranchCreate(
    data: {
      name: "feature-network-redesign"
      description: "Redesign core network to spine-leaf topology. Includes schema changes for new device roles and updated BGP configuration templates. Target completion: Q2 2024."
      sync_with_git: true
    }
  ) {
    object {
      id
    }
  }
}

Branch lifecycle

Understanding the branch lifecycle helps teams manage the flow of changes through Infrahub:
  1. Branch creation: Establishes an isolated workspace at a specific point in time, capturing the state of the parent branch as a starting point
  2. Development: Changes accumulate in the branch without affecting the main branch, allowing for focused work and experimentation
  3. Validation: Proposed Changes run automated checks and tests to verify the integrity and functionality of modifications
  4. Conflict resolution: Any conflicts with the main branch are identified and resolved, ensuring smooth integration
  5. Merge: Approved changes integrate into the main branch through a controlled process that preserves production integrity
  6. Cleanup: The branch can be deleted after successful merge, maintaining a clean workspace
This structured approach ensures that changes are thoroughly validated before reaching production while giving teams the flexibility to work independently.

Design trade-offs

Single-level hierarchy

Infrahub currently supports only one level of branch hierarchy (branches from main). This limitation simplifies conflict resolution and ensures clear change lineage but prevents some workflows:
  • Can’t create a branch from a feature branch
  • Can’t have long-lived development branches with sub-features
  • Can’t isolate experimental work within a feature branch
The single-level constraint is a deliberate design choice that prioritizes simplicity and clarity over flexibility. Future versions may relax this constraint.

Merge vs. rebase philosophy

Infrahub’s “latest-value wins” merge strategy differs from Git’s approach. Git preserves commit history during merge; Infrahub captures only the final state. This trade-off optimizes for:
  • Simpler conflict resolution (only final state matters)
  • Cleaner main branch history (atomic merge commits)
  • Reduced storage overhead (no duplicate history)
The cost is loss of intermediate development history in the main branch. The feature branch retains full history until deletion.

Performance characteristics

Feature branches optimize for development speed by relaxing some validation constraints. The default branch optimizes for data integrity by enforcing comprehensive validation. This creates different performance profiles:
  • Feature branches: Faster writes, some validation deferred
  • Default branch: Slower writes, complete validation
This trade-off is appropriate for infrastructure management where production data quality is paramount.

Build docs developers (and LLMs) love