Add collision resolution with automatic ID remapping
Implements --resolve-collisions flag for import command to safely handle ID collisions during branch merges. When enabled, colliding issues are remapped to new IDs and all text references and dependencies are automatically updated. Also adds comprehensive tests, branch-merge example, and documentation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
176
examples/branch-merge/README.md
Normal file
176
examples/branch-merge/README.md
Normal file
@@ -0,0 +1,176 @@
|
||||
# Branch Merge Workflow with Collision Resolution
|
||||
|
||||
This example demonstrates how to handle ID collisions when merging branches that have diverged and created issues with the same IDs.
|
||||
|
||||
## The Problem
|
||||
|
||||
When two branches work independently and both create issues, they'll often generate overlapping IDs:
|
||||
|
||||
```
|
||||
main: bd-1, bd-2, bd-3, bd-4, bd-5
|
||||
feature: bd-1, bd-2, bd-3, bd-6, bd-7 (diverged from main earlier)
|
||||
```
|
||||
|
||||
When you try to merge `feature` into `main`, you'll have ID collisions for bd-1 through bd-3 (if the content differs).
|
||||
|
||||
## The Solution
|
||||
|
||||
bd provides automatic collision resolution that:
|
||||
1. Detects collisions (same ID, different content)
|
||||
2. Renumbers the incoming colliding issues
|
||||
3. Updates ALL text references and dependencies automatically
|
||||
|
||||
## Demo Workflow
|
||||
|
||||
### 1. Setup - Two Diverged Branches
|
||||
|
||||
```bash
|
||||
# Start on main branch
|
||||
git checkout main
|
||||
bd create "Feature A" -t feature -p 1
|
||||
bd create "Bug fix B" -t bug -p 0
|
||||
bd create "Task C" -t task -p 2
|
||||
bd export -o .beads/issues.jsonl
|
||||
git add .beads/issues.jsonl
|
||||
git commit -m "Add main branch issues"
|
||||
|
||||
# Create feature branch from an earlier commit
|
||||
git checkout -b feature-branch HEAD~5
|
||||
|
||||
# On feature branch, create overlapping issues
|
||||
bd create "Different feature A" -t feature -p 2
|
||||
bd create "Different bug B" -t bug -p 1
|
||||
bd create "Feature D" -t feature -p 1
|
||||
bd export -o .beads/issues.jsonl
|
||||
git add .beads/issues.jsonl
|
||||
git commit -m "Add feature branch issues"
|
||||
```
|
||||
|
||||
At this point:
|
||||
- `main` has: bd-1 (Feature A), bd-2 (Bug fix B), bd-3 (Task C)
|
||||
- `feature-branch` has: bd-1 (Different feature A), bd-2 (Different bug B), bd-3 (Feature D)
|
||||
|
||||
The bd-1 and bd-2 on each branch have different content = collisions!
|
||||
|
||||
### 2. Merge and Detect Collisions
|
||||
|
||||
```bash
|
||||
# Merge feature branch into main
|
||||
git checkout main
|
||||
git merge feature-branch
|
||||
|
||||
# Git will show merge conflict in .beads/issues.jsonl
|
||||
# Manually resolve the conflict by keeping both versions
|
||||
# (or use a merge tool)
|
||||
|
||||
# After resolving the git conflict, check for ID collisions
|
||||
bd import -i .beads/issues.jsonl --dry-run
|
||||
```
|
||||
|
||||
Output shows:
|
||||
```
|
||||
=== Collision Detection Report ===
|
||||
Exact matches (idempotent): 0
|
||||
New issues: 1
|
||||
COLLISIONS DETECTED: 2
|
||||
|
||||
Colliding issues:
|
||||
bd-1: Different feature A
|
||||
Conflicting fields: [title, priority]
|
||||
bd-2: Different bug B
|
||||
Conflicting fields: [title, priority]
|
||||
```
|
||||
|
||||
### 3. Resolve Collisions Automatically
|
||||
|
||||
```bash
|
||||
# Let bd resolve the collisions
|
||||
bd import -i .beads/issues.jsonl --resolve-collisions
|
||||
```
|
||||
|
||||
Output shows:
|
||||
```
|
||||
Resolving collisions...
|
||||
|
||||
=== Remapping Report ===
|
||||
Issues remapped: 2
|
||||
|
||||
Remappings (sorted by reference count):
|
||||
bd-1 → bd-4 (refs: 0)
|
||||
bd-2 → bd-5 (refs: 0)
|
||||
|
||||
All text and dependency references have been updated.
|
||||
|
||||
Import complete: 2 created, 0 updated, 1 dependencies added, 2 issues remapped
|
||||
```
|
||||
|
||||
Result:
|
||||
- `main` keeps: bd-1 (Feature A), bd-2 (Bug fix B), bd-3 (Task C)
|
||||
- `feature-branch` issues become: bd-4 (Different feature A), bd-5 (Different bug B), bd-3 (Feature D)
|
||||
|
||||
### 4. Export and Commit
|
||||
|
||||
```bash
|
||||
# Export the resolved state back to JSONL
|
||||
bd export -o .beads/issues.jsonl
|
||||
|
||||
# Commit the merge
|
||||
git add .beads/issues.jsonl
|
||||
git commit -m "Merge feature-branch with collision resolution"
|
||||
```
|
||||
|
||||
## Advanced: Cross-References
|
||||
|
||||
If your issues reference each other in text or dependencies, bd updates those automatically:
|
||||
|
||||
```bash
|
||||
# On feature branch, create issues with references
|
||||
bd create "Feature X" -d "Implements the core logic" -t feature -p 1
|
||||
# Assume this becomes bd-10
|
||||
|
||||
bd create "Test for X" -d "Tests bd-10 functionality" -t task -p 2
|
||||
# This references bd-10 in the description
|
||||
|
||||
bd dep add bd-11 bd-10 --type blocks
|
||||
# Dependencies are created
|
||||
|
||||
# After merge with collision resolution
|
||||
bd import -i .beads/issues.jsonl --resolve-collisions
|
||||
|
||||
# If bd-10 collided and was remapped to bd-15:
|
||||
# - bd-11's description becomes: "Tests bd-15 functionality"
|
||||
# - Dependency becomes: bd-11 → bd-15
|
||||
```
|
||||
|
||||
## When to Use This
|
||||
|
||||
1. **Feature branches** - Long-lived branches that create issues independently
|
||||
2. **Parallel development** - Multiple developers working on separate branches
|
||||
3. **Stale branches** - Old branches that need to be merged but have ID conflicts
|
||||
4. **Distributed teams** - Teams that work offline and sync via git
|
||||
|
||||
## Safety Notes
|
||||
|
||||
- `--resolve-collisions` preserves your existing database (current branch's issues never change IDs)
|
||||
- Only the incoming colliding issues get new IDs
|
||||
- Use `--dry-run` first to preview what will happen
|
||||
- All text references use word-boundary matching (bd-10 won't match bd-100)
|
||||
- The collision resolution is deterministic (same input = same output)
|
||||
|
||||
## Alternative: Manual Resolution
|
||||
|
||||
If you prefer manual control:
|
||||
|
||||
1. Don't use `--resolve-collisions`
|
||||
2. Manually edit the JSONL file before import
|
||||
3. Rename colliding IDs to unique values
|
||||
4. Manually update any cross-references
|
||||
5. Import normally
|
||||
|
||||
This gives you complete control but is more error-prone and time-consuming.
|
||||
|
||||
## See Also
|
||||
|
||||
- [Git Hooks Example](../git-hooks/) - Automate export/import with git hooks
|
||||
- [README.md](../../README.md) - Full collision resolution documentation
|
||||
- [TEXT_FORMATS.md](../../TEXT_FORMATS.md) - JSONL merge strategies
|
||||
145
examples/branch-merge/demo.sh
Executable file
145
examples/branch-merge/demo.sh
Executable file
@@ -0,0 +1,145 @@
|
||||
#!/bin/bash
|
||||
# Demo script for branch merge collision resolution workflow
|
||||
# This script simulates a branch merge with ID collisions
|
||||
|
||||
set -e # Exit on error
|
||||
|
||||
echo "=== Branch Merge Collision Resolution Demo ==="
|
||||
echo ""
|
||||
|
||||
# Check if bd is available
|
||||
if ! command -v bd &> /dev/null; then
|
||||
echo "Error: bd command not found. Please install bd first."
|
||||
echo "Run: go install github.com/steveyegge/beads/cmd/bd@latest"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create a temporary directory for the demo
|
||||
DEMO_DIR=$(mktemp -d -t bd-merge-demo-XXXXXX)
|
||||
echo "Demo directory: $DEMO_DIR"
|
||||
cd "$DEMO_DIR"
|
||||
|
||||
# Initialize git repo
|
||||
echo ""
|
||||
echo "Step 1: Initialize git repo and bd database"
|
||||
git init
|
||||
git config user.name "Demo User"
|
||||
git config user.email "demo@example.com"
|
||||
bd init --prefix demo
|
||||
|
||||
# Create initial commit
|
||||
echo "Initial project" > README.txt
|
||||
git add README.txt .beads/
|
||||
git commit -m "Initial commit"
|
||||
|
||||
# Create issues on main branch
|
||||
echo ""
|
||||
echo "Step 2: Create issues on main branch"
|
||||
bd create "Implement login" -d "User authentication system" -t feature -p 1 --json
|
||||
bd create "Fix memory leak" -d "Memory leak in parser" -t bug -p 0 --json
|
||||
bd create "Update docs" -d "Document new API" -t task -p 2 --json
|
||||
|
||||
echo ""
|
||||
echo "Main branch issues:"
|
||||
bd list
|
||||
|
||||
# Export and commit
|
||||
bd export -o .beads/issues.jsonl
|
||||
git add .beads/issues.jsonl
|
||||
git commit -m "Add main branch issues (bd-1, bd-2, bd-3)"
|
||||
|
||||
# Create feature branch from earlier point
|
||||
echo ""
|
||||
echo "Step 3: Create feature branch"
|
||||
git checkout -b feature-branch HEAD~1
|
||||
|
||||
# Reimport to get clean state
|
||||
bd import -i .beads/issues.jsonl
|
||||
|
||||
# Create overlapping issues on feature branch
|
||||
echo ""
|
||||
echo "Step 4: Create different issues with same IDs on feature branch"
|
||||
bd create "Add dashboard" -d "Admin dashboard feature" -t feature -p 2 --json
|
||||
bd create "Improve performance" -d "Optimize queries" -t task -p 1 --json
|
||||
bd create "Add metrics" -d "Monitoring and metrics" -t feature -p 1 --json
|
||||
|
||||
echo ""
|
||||
echo "Feature branch issues:"
|
||||
bd list
|
||||
|
||||
# Export and commit
|
||||
bd export -o .beads/issues.jsonl
|
||||
git add .beads/issues.jsonl
|
||||
git commit -m "Add feature branch issues (bd-1, bd-2, bd-3)"
|
||||
|
||||
# Merge back to main
|
||||
echo ""
|
||||
echo "Step 5: Merge feature branch into main"
|
||||
git checkout main
|
||||
|
||||
# Attempt merge (will conflict)
|
||||
if git merge feature-branch --no-edit; then
|
||||
echo "Merge succeeded without conflicts"
|
||||
else
|
||||
echo "Merge conflict detected - resolving..."
|
||||
# Keep both versions by accepting both sides
|
||||
# In a real scenario, you'd resolve this more carefully
|
||||
git checkout --ours .beads/issues.jsonl
|
||||
git checkout --theirs .beads/issues.jsonl --patch || true
|
||||
# For demo purposes, accept theirs
|
||||
git checkout --theirs .beads/issues.jsonl
|
||||
git add .beads/issues.jsonl
|
||||
git commit -m "Merge feature-branch"
|
||||
fi
|
||||
|
||||
# Detect collisions
|
||||
echo ""
|
||||
echo "Step 6: Detect ID collisions"
|
||||
echo "Running: bd import -i .beads/issues.jsonl --dry-run"
|
||||
echo ""
|
||||
|
||||
if bd import -i .beads/issues.jsonl --dry-run; then
|
||||
echo "No collisions detected!"
|
||||
else
|
||||
echo ""
|
||||
echo "Collisions detected (expected)!"
|
||||
fi
|
||||
|
||||
# Resolve collisions
|
||||
echo ""
|
||||
echo "Step 7: Resolve collisions automatically"
|
||||
echo "Running: bd import -i .beads/issues.jsonl --resolve-collisions"
|
||||
echo ""
|
||||
|
||||
bd import -i .beads/issues.jsonl --resolve-collisions
|
||||
|
||||
# Show final state
|
||||
echo ""
|
||||
echo "Step 8: Final issue list after resolution"
|
||||
bd list
|
||||
|
||||
# Show remapping details
|
||||
echo ""
|
||||
echo "Step 9: Show how dependencies and references are maintained"
|
||||
echo "All text references like 'see bd-1' and dependencies were automatically updated!"
|
||||
|
||||
# Export final state
|
||||
bd export -o .beads/issues.jsonl
|
||||
git add .beads/issues.jsonl
|
||||
git commit -m "Resolve collisions and finalize merge"
|
||||
|
||||
echo ""
|
||||
echo "=== Demo Complete ==="
|
||||
echo ""
|
||||
echo "Summary:"
|
||||
echo "- Created issues on main branch (bd-1, bd-2, bd-3)"
|
||||
echo "- Created different issues on feature branch (also bd-1, bd-2, bd-3)"
|
||||
echo "- Merged branches with Git"
|
||||
echo "- Detected collisions with --dry-run"
|
||||
echo "- Resolved collisions with --resolve-collisions"
|
||||
echo "- Feature branch issues were renumbered to avoid conflicts"
|
||||
echo ""
|
||||
echo "Demo directory: $DEMO_DIR"
|
||||
echo "You can explore the git history: cd $DEMO_DIR && git log --oneline"
|
||||
echo ""
|
||||
echo "To clean up: rm -rf $DEMO_DIR"
|
||||
Reference in New Issue
Block a user