Add conflict marker detection to bd import/validate
- bd import now detects git conflict markers and shows resolution steps - bd validate --checks=conflicts improved with clearer instructions - Updated AGENTS.md with better conflict resolution docs - Closes bd-7e7ddffa epic (repair tools complete) Amp-Thread-ID: https://ampcode.com/threads/T-4a11a65b-56c2-4d92-8f68-66f0a56ab5e3 Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
28
AGENTS.md
28
AGENTS.md
@@ -602,17 +602,27 @@ The daemon maintains its own view of the current working directory and git state
|
|||||||
**With hash-based IDs (v0.20.1+), ID collisions are eliminated!** Different issues get different hash IDs, so most git merges succeed cleanly.
|
**With hash-based IDs (v0.20.1+), ID collisions are eliminated!** Different issues get different hash IDs, so most git merges succeed cleanly.
|
||||||
|
|
||||||
**When git merge conflicts occur:**
|
**When git merge conflicts occur:**
|
||||||
Git conflicts in `.beads/beads.jsonl` happen when the same issue is modified on both branches (different timestamps/fields). This is a **same-issue update conflict**, not an ID collision.
|
Git conflicts in `.beads/beads.jsonl` happen when the same issue is modified on both branches (different timestamps/fields). This is a **same-issue update conflict**, not an ID collision. Conflicts are rare in practice since hash IDs prevent structural collisions.
|
||||||
|
|
||||||
**Resolution:**
|
**Automatic detection:**
|
||||||
|
bd automatically detects conflict markers (`<<<<<<<`, `=======`, `>>>>>>>`) and shows clear resolution steps:
|
||||||
|
- `bd import` rejects files with conflict markers and shows resolution commands
|
||||||
|
- `bd validate --checks=conflicts` scans for conflicts in JSONL
|
||||||
|
|
||||||
|
**Resolution workflow:**
|
||||||
```bash
|
```bash
|
||||||
# After git merge creates conflict
|
# After git merge creates conflict in .beads/beads.jsonl
|
||||||
git checkout --theirs .beads/beads.jsonl # Accept remote version
|
|
||||||
# OR
|
|
||||||
git checkout --ours .beads/beads.jsonl # Keep local version
|
|
||||||
# OR manually resolve in editor
|
|
||||||
|
|
||||||
# Import the resolved JSONL
|
# Option 1: Accept their version (remote)
|
||||||
|
git checkout --theirs .beads/beads.jsonl
|
||||||
|
bd import -i .beads/beads.jsonl
|
||||||
|
|
||||||
|
# Option 2: Keep our version (local)
|
||||||
|
git checkout --ours .beads/beads.jsonl
|
||||||
|
bd import -i .beads/beads.jsonl
|
||||||
|
|
||||||
|
# Option 3: Manual resolution in editor
|
||||||
|
# Edit .beads/beads.jsonl to remove conflict markers
|
||||||
bd import -i .beads/beads.jsonl
|
bd import -i .beads/beads.jsonl
|
||||||
|
|
||||||
# Commit the merge
|
# Commit the merge
|
||||||
@@ -620,7 +630,7 @@ git add .beads/beads.jsonl
|
|||||||
git commit
|
git commit
|
||||||
```
|
```
|
||||||
|
|
||||||
**bd automatically handles updates** - same ID with different content is a normal update operation. No special flags needed.
|
**Note:** `bd import` automatically handles updates - same ID with different content is a normal update operation. No special flags needed. If you accidentally modified the same issue in both branches, just pick whichever version is more complete.
|
||||||
|
|
||||||
### Advanced: Intelligent Merge Tools
|
### Advanced: Intelligent Merge Tools
|
||||||
|
|
||||||
|
|||||||
@@ -59,24 +59,34 @@ Behavior:
|
|||||||
lineNum := 0
|
lineNum := 0
|
||||||
|
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
lineNum++
|
lineNum++
|
||||||
line := scanner.Text()
|
line := scanner.Text()
|
||||||
|
|
||||||
// Skip empty lines
|
// Skip empty lines
|
||||||
if line == "" {
|
if line == "" {
|
||||||
continue
|
continue
|
||||||
}
|
|
||||||
|
|
||||||
// Parse JSON
|
|
||||||
var issue types.Issue
|
|
||||||
if err := json.Unmarshal([]byte(line), &issue); err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "Error parsing line %d: %v\n", lineNum, err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
allIssues = append(allIssues, &issue)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Detect git conflict markers
|
||||||
|
if strings.Contains(line, "<<<<<<<") || strings.Contains(line, "=======") || strings.Contains(line, ">>>>>>>") {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error: Git conflict markers detected in JSONL file (line %d)\n\n", lineNum)
|
||||||
|
fmt.Fprintf(os.Stderr, "To resolve:\n")
|
||||||
|
fmt.Fprintf(os.Stderr, " git checkout --ours .beads/issues.jsonl && bd import -i .beads/issues.jsonl\n")
|
||||||
|
fmt.Fprintf(os.Stderr, " git checkout --theirs .beads/issues.jsonl && bd import -i .beads/issues.jsonl\n\n")
|
||||||
|
fmt.Fprintf(os.Stderr, "For advanced field-level merging, see: https://github.com/neongreen/mono/tree/main/beads-merge\n")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse JSON
|
||||||
|
var issue types.Issue
|
||||||
|
if err := json.Unmarshal([]byte(line), &issue); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error parsing line %d: %v\n", lineNum, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
allIssues = append(allIssues, &issue)
|
||||||
|
}
|
||||||
|
|
||||||
if err := scanner.Err(); err != nil {
|
if err := scanner.Err(); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Error reading input: %v\n", err)
|
fmt.Fprintf(os.Stderr, "Error reading input: %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|||||||
@@ -400,17 +400,21 @@ func validateGitConflicts(_ context.Context, fix bool) checkResult {
|
|||||||
if len(conflictLines) > 0 {
|
if len(conflictLines) > 0 {
|
||||||
result.issueCount = 1 // One conflict situation
|
result.issueCount = 1 // One conflict situation
|
||||||
result.suggestions = append(result.suggestions,
|
result.suggestions = append(result.suggestions,
|
||||||
fmt.Sprintf("Resolve git conflict in %s (markers at lines: %v)", jsonlPath, conflictLines))
|
fmt.Sprintf("Git conflict markers found in %s at lines: %v", jsonlPath, conflictLines))
|
||||||
if !fix {
|
result.suggestions = append(result.suggestions,
|
||||||
result.suggestions = append(result.suggestions,
|
"To resolve, choose one version:")
|
||||||
fmt.Sprintf("Then run 'bd import -i %s' to reload issues", jsonlPath))
|
result.suggestions = append(result.suggestions,
|
||||||
}
|
" git checkout --ours .beads/issues.jsonl && bd import -i .beads/issues.jsonl")
|
||||||
|
result.suggestions = append(result.suggestions,
|
||||||
|
" git checkout --theirs .beads/issues.jsonl && bd import -i .beads/issues.jsonl")
|
||||||
|
result.suggestions = append(result.suggestions,
|
||||||
|
"For advanced field-level merging: https://github.com/neongreen/mono/tree/main/beads-merge")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Can't auto-fix git conflicts
|
// Can't auto-fix git conflicts
|
||||||
if fix && result.issueCount > 0 {
|
if fix && result.issueCount > 0 {
|
||||||
result.suggestions = append(result.suggestions,
|
result.suggestions = append(result.suggestions,
|
||||||
"Git conflicts cannot be auto-fixed. Resolve manually in your editor or run 'bd export' to regenerate JSONL")
|
"Note: Git conflicts cannot be auto-fixed with --fix-all")
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|||||||
Reference in New Issue
Block a user