DAG Branching & Merge-Back¶
SkillMeat's DAG (Directed Acyclic Graph) branching system lets you create isolated versions of artifacts, experiment freely, and merge changes back cleanly. This guide walks you through the entire workflow — from creating a branch to resolving merge conflicts.
Enterprise Edition. DAG branching is available in SkillMeat Enterprise with the
dvcs_branching_enabledfeature flag. Local edition uses linear version history. See Edition Feature Matrix for more.
Overview: Why Branches?¶
Before DAG branching, artifact versions were linear: v1 → v2 → v3. If you wanted to test a change without affecting the main line, you had to work in a separate collection or project.
With DAG branching, you can:
- Create parallel timelines — Branch from any version and edit independently
- Test safely — Experiment without affecting the default
mainbranch - Merge cleanly — Bring changes back to main with 3-way merge conflict resolution
- Cherry-pick — Apply specific commits from one branch to another
- Track divergence — See which branches differ from main and by how much
Real-world scenario: Your team uses a skill in production (main branch). A developer branches to add a new feature, while another fixes a bug on a separate branch. Both merge back without overwriting each other's changes.
Key Concepts¶
Branches¶
A branch is an independent timeline of versions for an artifact. Each branch has a name (e.g., main, feature/auth-v2, hotfix/validator-bug) and tracks its own history.
Default Main Branch¶
Every artifact starts with a main branch. This is the production baseline — the version most users interact with. You can't delete main, but you can branch from any point in its history.
Parent Branch & Divergence Point¶
When you create a branch, you choose a parent branch and a point in history (commit). The new branch inherits all versions up to that point, then diverges with its own edits.
Example:
The divergence point is v2 (the parent commit). feature/auth includes all history up to v2, then adds its own changes.
3-Way Merge¶
When you merge a branch back to main, SkillMeat performs a 3-way merge using:
- Common ancestor — the divergence point
- Left side — main branch edits since divergence
- Right side — feature branch edits since divergence
If both sides edited different fields, the merge auto-resolves. If both edited the same field, you get a conflict to resolve manually.
Cherry-Pick¶
Cherry-pick lets you copy a single commit (change) from one branch to another without merging the entire branch. Useful for applying a critical bug fix to multiple branches.
Creating a Branch¶
Via Web UI¶
- Open an artifact in the Time Machine DAG view (clock icon → DAG tab)
- Right-click any version node and select Branch from this commit
- Enter a branch name (e.g.,
feature/v2-rewrite) and confirm - The new branch opens in the editor; all edits go to the new branch
Screenshot: The DAG view shows lanes for each branch; click a node to branch from that point.
Via API¶
Endpoint: POST /api/v1/artifacts/{artifact_id}/branches
Response (201 Created):
{
"branch_name": "feature/auth-v2",
"parent_branch": "main",
"created_at": "2026-04-11T14:22:00Z",
"divergence_point": "abc123def456...",
"ahead_by": 0,
"behind_by": 0
}
Viewing the DAG¶
The Time Machine DAG view in the web UI shows your artifact's branching history as a graph:
- Vertical lanes — one lane per branch
- Nodes — individual commits (versions)
- Edges — the flow of history within and between branches
- Branch badges — color-coded labels showing branch name and status
Interpreting the DAG¶
- Nodes on the same lane — history within a single branch
- Branching point — where one lane splits from another
- Merge node — where two branches come back together (if merged)
- Ahead/Behind counts — labels showing divergence from main
Deploy-by-Branch Selector¶
When viewing deployments or artifact details, use the branch dropdown to:
- View a different branch — Switch which branch's content is displayed
- Deploy a branch — Activate a non-main branch for testing or staged rollout
- Filter history — Show version timeline for the selected branch only
Branch badges on artifact cards indicate: - Active branch (highlight color) - Divergence status (icon: ↔ if diverged from main) - Merge status (icon: ↓ if this branch contains unmerged commits)
Merging Branches¶
Open the Merge Dialog¶
- Open the artifact in Time Machine
- Select the branch you want to merge (from the dropdown)
- Click Merge into Main or Merge into [target]
- The BranchMergeDialog opens, showing the 3-way diff
Merge Dialog Walkthrough¶
The dialog displays:
| Section | Content |
|---|---|
| Source | The branch being merged (e.g., feature/auth-v2) |
| Target | Destination branch (default: main) |
| Common ancestor | Divergence commit hash |
| Diff summary | Fields changed in source and target since divergence |
| Merge result preview | What the merged content will look like |
| Conflict markers (if any) | Fields that need manual resolution |
Content-Type-Aware Merge¶
SkillMeat's merge engine uses different strategies based on artifact type:
| Format | Strategy | Example |
|---|---|---|
| YAML | Recursive merge on key-value pairs | Merges metadata.tags without losing either side's additions |
| TOML | Section-aware merge | Combines [dependencies] from both branches |
| Text (plain skill code) | TextMergeDriver — line-based conflict markers | Conflicts marked with <<<, ===, >>> |
| JSON | Object-aware merge | Merges top-level keys preserving both sides |
Clean Merge (No Conflicts)¶
If no conflicts exist:
- Review the merge preview
- Click Confirm Merge
- A new merge commit is created on
main - The branch remains open (not deleted)
Response:
{
"merge_commit": "def789ghi012...",
"merged_at": "2026-04-11T14:25:00Z",
"conflicts_resolved": 0,
"result_version_hash": "def789ghi012..."
}
Resolving Conflicts¶
If the same field was edited in both branches, you see a BranchConflictResolutionCard for each conflict.
Conflict Markers¶
Text-based artifacts show inline markers:
<<<<<<< feature/auth-v2
def validate_token(token: str) -> bool:
"""New token format."""
return len(token) == 64
=======
def validate_token(token: str) -> bool:
"""Legacy token format."""
return len(token) == 32
>>>>>>> main
The section between <<<<<<< and ======= is from the branch being merged (source).
The section between ======= and >>>>>>> is from the target branch.
Manual Resolution¶
For each conflict:
- Edit the code to keep both changes, one side, or a combination
- Delete the
<<<<<<<,=======, and>>>>>>>markers - Click Mark as Resolved
- Repeat for other conflicts
Example resolved:
def validate_token(token: str) -> bool:
"""New token format; supports legacy fallback."""
return len(token) in (32, 64)
What Falls into Conflict vs. Auto-Resolve¶
| Scenario | Outcome |
|---|---|
| Source adds field; target unchanged | Auto-resolve — field added from source |
| Both add the same field with different values | Conflict — manual resolution required |
| Source modifies field; target deletes it | Conflict — keep or remove? |
| Both independently add the same lines | Auto-resolve (usually) — depends on merge driver |
| Source edits lines; target edits different lines | Auto-resolve — no overlap |
Cherry-Picking¶
Cherry-pick lets you apply a single commit from one branch to another without merging the entire branch history.
When to Cherry-Pick¶
- Critical bug fix — Apply a hotfix from
mainto thefeature/v2branch - Selective rollout — Apply just one feature commit to a staging branch
- Avoid full merge — Extract one change from a messy branch
Via Web UI¶
- Open Time Machine DAG view
- Right-click a commit node and select Cherry-pick
- Choose the target branch
- If conflicts occur, resolve them (same as merge conflicts)
- Confirm to create a new commit on the target branch
Via API¶
Endpoint: POST /api/v1/artifacts/{artifact_id}/branches/{branch_name}/cherry-pick
Response (201 Created):
{
"new_commit": "xyz789uvw012...",
"cherry_picked_from": "abc123def456...",
"target_branch": "feature/v2",
"conflicts_resolved": 0
}
Branch-Scoped Version History¶
When viewing an artifact's version timeline, you can filter by branch:
- Open Version History (clock icon → History tab)
- Use the branch dropdown to show only commits from a specific branch
- The timeline shows only that branch's commits in order
API: GET /api/v1/artifacts/{artifact_id}/history?branch=feature/auth-v2
This is useful for reviewing what changed on a feature branch before merging.
Enterprise Considerations¶
Protected Branches¶
Enterprise admins can mark branches as protected, requiring approval before merge:
- Prevent accidental overwrites of production (
main) - Require code review before merge (via RBAC)
- Enforce required status checks (e.g., tests must pass)
RBAC & Branch Permissions¶
If your enterprise has role-based access control (RBAC) enabled:
| Permission | Effect |
|---|---|
artifact:branch:create |
Can create new branches |
artifact:branch:delete |
Can delete branches |
artifact:branch:merge |
Can merge any branch |
require_branch_permission |
Can only merge if you have artifact:branch:merge AND the role-specific branch permission |
If you lack artifact:branch:merge, you'll see a 403 Forbidden error when attempting to merge.
Divergence at Scale¶
Enterprise admins can query divergence across all artifacts and branches:
- Which branches diverged from main? — Use the admin dashboard
- How far behind is feature/v2? — Version history shows commit count (
ahead_by,behind_by) - Force-sync a branch — Admins can reset a branch to match main (destructive; use carefully)
Feature Flag¶
DAG branching is controlled by the dvcs_branching_enabled feature flag in your server configuration.
To enable (for Enterprise):
- Set environment variable:
SKILLMEAT_DVCS_BRANCHING_ENABLED=true - Or update config file:
skillmeat/api/config.py→APISettings.dvcs_branching_enabled - Restart the server
- The Time Machine DAG view, branch selector, and merge UI become available
If disabled: Artifacts use linear version history only (default behavior).
Troubleshooting¶
"Branch not found" Error¶
Cause: Branch was deleted or doesn't exist. Solution: Refresh the DAG view; create the branch again.
"Permission denied on protected branch"¶
Cause: You lack artifact:branch:merge role or branch is protected and requires approval.
Solution: Contact your enterprise admin to grant the role or approve the merge request.
"Unsupported content type for merge"¶
Cause: Artifact type (e.g., binary image, custom schema) doesn't support 3-way merge. Solution: The system falls back to TextMergeDriver (line-based conflict markers). Resolve conflicts manually or re-export and re-import the artifact.
"Merge created too many conflicts"¶
Cause: Branches diverged too far (many overlapping edits). Solution: 1. Start a fresh branch from a more recent common commit 2. Discuss changes with your team before merging 3. Use cherry-pick to apply specific, non-conflicting commits first
Conflict Loop (Conflicts Keep Reappearing)¶
Cause: Two branches keep editing the same line. Each merge leaves markers; re-editing causes new conflicts.
Solution:
1. Fully resolve all conflict markers (ensure no <<<<<<<, =======, >>>>>>> remain)
2. Test the merged artifact before considering it done
3. If conflicts persist after resolution, reset and rebase the branch
Related Resources¶
- ADR-017: DAG Versioning & Branching Architecture — Design rationale and technical details
- API Reference: Branches — Complete endpoint documentation
- Enterprise Governance Guide — Protected branches and RBAC setup
- Time Machine Guide — Full version history and restore workflows