Skip to main content
Infrahub provides Git-like branching for infrastructure data, allowing you to make changes in isolation before merging them to the main branch.

Understanding Branches

Branches in Infrahub work similarly to Git:
  • Main branch: The default, production branch
  • Feature branches: Isolated workspaces for changes
  • Merging: Combine branch changes back to main
All objects and changes are branch-aware. You can query and modify data on specific branches.

Creating Branches

Create a New Branch

from infrahub_sdk import InfrahubClient

client = InfrahubClient()

# Create a feature branch
branch = await client.branch.create(branch_name="feature-update")

print(f"Created branch: {branch.name}")
print(f"Branch ID: {branch.id}")
print(f"Created at: {branch.created_at}")

Create Branch with Description

branch = await client.branch.create(
    branch_name="feature-new-datacenter",
    description="Add new datacenter infrastructure",
    sync_with_git=True
)

print(f"Branch: {branch.name}")
print(f"Description: {branch.description}")

Create from Specific Base

# Create branch from another branch
branch = await client.branch.create(
    branch_name="feature-sub-branch",
    branch_from="feature-parent-branch"
)

Working on Branches

Create Objects on a Branch

# Create a branch
branch = await client.branch.create(branch_name="add-devices")

# Create objects on that branch
device = await client.create(
    kind="InfraDevice",
    name="router-new",
    serial_number="SN123456",
    branch=branch.name  # Specify the branch
)
await device.save()

print(f"Created device on branch: {branch.name}")

Query Objects on a Branch

# Query on a specific branch
devices_on_branch = await client.all(
    kind="InfraDevice",
    branch="feature-update"
)

print(f"Devices on feature-update: {len(devices_on_branch)}")

Update Objects on a Branch

# Create branch
branch = await client.branch.create(branch_name="update-config")

# Get object on that branch
device = await client.get(
    kind="InfraDevice",
    id="device-id",
    branch=branch.name
)

# Update on the branch
device.name.value = "router-updated"
await device.save()

print(f"Updated device on branch: {branch.name}")

Delete Objects on a Branch

branch = await client.branch.create(branch_name="cleanup")

device = await client.get(
    kind="InfraDevice",
    id="device-id",
    branch=branch.name
)

await device.delete()
print(f"Deleted device on branch: {branch.name}")

Querying Branches

List All Branches

branches = await client.branch.all()

print(f"Total branches: {len(branches)}")
for branch in branches:
    print(f"  - {branch.name}")

Get Branch Details

branch = await client.branch.get(branch_name="feature-update")

print(f"Name: {branch.name}")
print(f"Description: {branch.description}")
print(f"Created at: {branch.created_at}")
print(f"Is default: {branch.is_default}")

Check Branch Status

branch = await client.branch.get(branch_name="feature-update")

if branch.has_schema_changes:
    print("Branch has schema changes")

if branch.is_isolated:
    print("Branch is isolated")

Comparing Branches

Compare Object Across Branches

# Get object on main branch
main_device = await client.get(
    kind="InfraDevice",
    id="device-id",
    branch="main"
)

# Get same object on feature branch
feature_device = await client.get(
    kind="InfraDevice",
    id="device-id",
    branch="feature-update"
)

# Compare values
if main_device.name.value != feature_device.name.value:
    print(f"Name changed:")
    print(f"  Main: {main_device.name.value}")
    print(f"  Feature: {feature_device.name.value}")

Get Branch Diff

# Get differences between branches
diff = await client.branch.diff(
    branch_name="feature-update",
    target_branch="main"
)

print(f"Added: {len(diff.added)}")
print(f"Modified: {len(diff.modified)}")
print(f"Deleted: {len(diff.deleted)}")

Count Changes on Branch

branch = await client.branch.get(branch_name="feature-update")

# Get all objects on branch
branch_devices = await client.all(
    kind="InfraDevice",
    branch=branch.name
)

# Get objects on main
main_devices = await client.all(
    kind="InfraDevice",
    branch="main"
)

print(f"Main: {len(main_devices)} devices")
print(f"Branch: {len(branch_devices)} devices")
print(f"Difference: {len(branch_devices) - len(main_devices)}")

Branch Workflows

Feature Development Workflow

import asyncio
from infrahub_sdk import InfrahubClient

async def feature_workflow():
    client = InfrahubClient()
    
    # 1. Create feature branch
    branch = await client.branch.create(
        branch_name="feature-new-location",
        description="Add new datacenter location"
    )
    print(f"Created branch: {branch.name}")
    
    # 2. Make changes on the branch
    location = await client.create(
        kind="InfraLocation",
        name="Datacenter-NYC",
        address="New York",
        branch=branch.name
    )
    await location.save()
    print(f"Created location on branch")
    
    # 3. Create related objects
    for i in range(3):
        device = await client.create(
            kind="InfraDevice",
            name=f"nyc-router-{i:02d}",
            serial_number=f"SN{i:06d}",
            location=location,
            branch=branch.name
        )
        await device.save()
    
    print(f"Created 3 devices on branch")
    
    # 4. Verify changes
    branch_devices = await client.all(
        kind="InfraDevice",
        branch=branch.name
    )
    print(f"Total devices on branch: {len(branch_devices)}")
    
    # 5. Ready to merge (in UI or via API)
    print(f"Branch {branch.name} ready for review")

if __name__ == "__main__":
    asyncio.run(feature_workflow())

Testing Changes Workflow

async def test_changes_workflow():
    client = InfrahubClient()
    
    # Create test branch
    test_branch = await client.branch.create(
        branch_name="test-configuration-change"
    )
    
    # Make changes
    devices = await client.all(
        kind="InfraDevice",
        branch=test_branch.name
    )
    
    for device in devices:
        device_full = await client.get(
            kind="InfraDevice",
            id=device.id,
            branch=test_branch.name
        )
        device_full.is_managed.value = True
        await device_full.save()
    
    print(f"Updated {len(devices)} devices on test branch")
    
    # Run tests/validations
    validation_passed = True  # Your validation logic
    
    if validation_passed:
        print("Tests passed - ready to merge")
    else:
        print("Tests failed - delete branch")
        await client.branch.delete(branch_name=test_branch.name)

Bulk Update Workflow

async def bulk_update_workflow():
    client = InfrahubClient()
    
    # Create update branch
    branch = await client.branch.create(
        branch_name="bulk-update-locations"
    )
    
    # Get all devices on branch
    devices = await client.all(
        kind="InfraDevice",
        branch=branch.name
    )
    
    # Get new location
    new_location = await client.get(
        kind="InfraLocation",
        id="new-location-id",
        branch=branch.name
    )
    
    # Update all devices
    for device in devices:
        device_full = await client.get(
            kind="InfraDevice",
            id=device.id,
            branch=branch.name
        )
        device_full.location = new_location
        await device_full.save()
    
    print(f"Updated {len(devices)} devices on branch {branch.name}")

Branch Management

Delete a Branch

# Delete a branch
await client.branch.delete(branch_name="feature-old")

print("Branch deleted")

Rebase a Branch

# Rebase branch onto main
await client.branch.rebase(
    branch_name="feature-update",
    target_branch="main"
)

print("Branch rebased")

Validate Branch

# Check if branch can be merged
validation = await client.branch.validate(
    branch_name="feature-update"
)

if validation.is_valid:
    print("Branch is valid and can be merged")
else:
    print(f"Validation errors: {validation.errors}")

Schema Changes on Branches

Load Schema on Branch

branch = await client.branch.create(
    branch_name="schema-update"
)

# Load new schema on branch
schema = {
    "version": "1.0",
    "nodes": [
        {
            "name": "NewDevice",
            "namespace": "Infra",
            "attributes": [
                {"name": "name", "kind": "Text"},
                {"name": "model", "kind": "Text"}
            ]
        }
    ]
}

response = await client.schema.load(
    schemas=[schema],
    branch=branch.name,
    wait_until_converged=True
)

if response.schema_updated:
    print("Schema updated on branch")

Query Schema on Branch

# Get schema on specific branch
device_schema = await client.schema.get(
    kind="InfraDevice",
    branch="feature-schema-update"
)

print(f"Schema on branch: {device_schema.name}")
print(f"Attributes: {[attr.name for attr in device_schema.attributes]}")

Advanced Branch Patterns

Multi-Branch Coordination

async def multi_branch_workflow():
    client = InfrahubClient()
    
    # Create multiple feature branches
    branches = []
    for i in range(3):
        branch = await client.branch.create(
            branch_name=f"feature-team-{i+1}"
        )
        branches.append(branch)
    
    # Each team works on their branch
    for idx, branch in enumerate(branches):
        device = await client.create(
            kind="InfraDevice",
            name=f"team-{idx+1}-device",
            serial_number=f"SN{idx:06d}",
            branch=branch.name
        )
        await device.save()
        print(f"Team {idx+1} created device on {branch.name}")
    
    # Merge branches sequentially
    for branch in branches:
        print(f"Ready to merge: {branch.name}")
        # Merge would happen via UI or API

Branch-Based Testing

async def branch_testing():
    client = InfrahubClient()
    
    # Create test branch
    test_branch = await client.branch.create(
        branch_name="test-deployment"
    )
    
    try:
        # Make risky changes
        devices = await client.all(
            kind="InfraDevice",
            branch=test_branch.name
        )
        
        for device in devices:
            device_full = await client.get(
                kind="InfraDevice",
                id=device.id,
                branch=test_branch.name
            )
            device_full.firmware_version.value = "2.0.0"
            await device_full.save()
        
        # Run validations
        all_valid = True  # Your validation logic
        
        if all_valid:
            print("Tests passed - merge branch")
            # Merge logic
        else:
            raise Exception("Validation failed")
    
    except Exception as e:
        print(f"Test failed: {e}")
        # Delete test branch
        await client.branch.delete(branch_name=test_branch.name)
        print("Test branch deleted")

Branch Isolation Testing

async def test_isolation():
    client = InfrahubClient()
    
    # Create isolated branch
    branch = await client.branch.create(
        branch_name="isolated-test"
    )
    
    # Create object on branch
    device = await client.create(
        kind="InfraDevice",
        name="test-device",
        serial_number="SN000000",
        branch=branch.name
    )
    await device.save()
    
    # Verify it exists on branch
    branch_devices = await client.all(
        kind="InfraDevice",
        branch=branch.name
    )
    print(f"Devices on branch: {len(branch_devices)}")
    
    # Verify it doesn't exist on main
    main_devices = await client.all(
        kind="InfraDevice",
        branch="main"
    )
    print(f"Devices on main: {len(main_devices)}")
    
    # Cleanup
    await client.branch.delete(branch_name=branch.name)

Best Practices

Name branches to clearly indicate their purpose:
  • feature-add-datacenter-nyc
  • fix-device-configuration
  • update-schema-v2
Merge or delete branches frequently to avoid drift from main.
Use branches as safe environments for testing changes before merging to production.
Delete branches after merging to keep your branch list manageable.

Next Steps

Batch Operations

Perform efficient bulk operations

Error Handling

Handle branch-related errors

Async Operations

Use async patterns with branches

Pagination

Handle large datasets on branches

Build docs developers (and LLMs) love