Only treat issue IDs as hierarchical (parent.child) when the dot appears AFTER the first hyphen. This prevents false positives when the project directory name contains a dot (e.g., "my.project-abc123" was incorrectly being treated as having parent "my"). Fixes GH#508 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
67 lines
2.2 KiB
Go
67 lines
2.2 KiB
Go
package migrations
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"log"
|
|
)
|
|
|
|
// MigrateOrphanDetection detects orphaned child issues and logs them for user action
|
|
// Orphaned children are issues with hierarchical IDs (e.g., "parent.child") where the
|
|
// parent issue no longer exists in the database.
|
|
//
|
|
// This migration does NOT automatically delete or convert orphans - it only logs them
|
|
// so the user can decide whether to:
|
|
// - Delete the orphans if they're no longer needed
|
|
// - Convert them to top-level issues by renaming them
|
|
// - Restore the missing parent issues
|
|
func MigrateOrphanDetection(db *sql.DB) error {
|
|
// Query for orphaned children:
|
|
// - ID contains a dot (potential hierarchical structure)
|
|
// - The part before the first dot must contain a hyphen (GH#508)
|
|
// This ensures the dot is in the hash portion (e.g., "bd-abc.1"), not in the prefix
|
|
// (e.g., "my.project-abc123" where "my.project" is the prefix from directory name)
|
|
// - Parent (part before first dot) doesn't exist in database
|
|
rows, err := db.Query(`
|
|
SELECT id
|
|
FROM issues
|
|
WHERE id LIKE '%.%'
|
|
AND instr(substr(id, 1, instr(id, '.') - 1), '-') > 0
|
|
AND substr(id, 1, instr(id || '.', '.') - 1) NOT IN (SELECT id FROM issues)
|
|
ORDER BY id
|
|
`)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to query for orphaned children: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
var orphans []string
|
|
for rows.Next() {
|
|
var id string
|
|
if err := rows.Scan(&id); err != nil {
|
|
return fmt.Errorf("failed to scan orphan ID: %w", err)
|
|
}
|
|
orphans = append(orphans, id)
|
|
}
|
|
|
|
if err := rows.Err(); err != nil {
|
|
return fmt.Errorf("error iterating orphan results: %w", err)
|
|
}
|
|
|
|
// Log results for user review
|
|
if len(orphans) > 0 {
|
|
log.Printf("⚠️ Orphan Detection: Found %d orphaned child issue(s):", len(orphans))
|
|
for _, id := range orphans {
|
|
log.Printf(" - %s", id)
|
|
}
|
|
log.Println("\nThese issues have hierarchical IDs but their parent issues no longer exist.")
|
|
log.Println("You can:")
|
|
log.Println(" 1. Delete them if no longer needed: bd delete <issue-id>")
|
|
log.Println(" 2. Convert to top-level issues by exporting and reimporting with new IDs")
|
|
log.Println(" 3. Restore the missing parent issues")
|
|
}
|
|
|
|
// Migration is idempotent - always succeeds since it's just detection/logging
|
|
return nil
|
|
}
|