feat(storage): add VersionedStorage interface with history/diff/branch operations

Extends Storage interface with Dolt-specific version control capabilities:

- New VersionedStorage interface in storage/versioned.go with:
  - History queries: History(), AsOf(), Diff()
  - Branch operations: Branch(), Merge(), CurrentBranch(), ListBranches()
  - Commit operations: Commit(), GetCurrentCommit()
  - Conflict resolution: GetConflicts(), ResolveConflicts()
  - Helper types: HistoryEntry, DiffEntry, Conflict

- DoltStore implements VersionedStorage interface

- New CLI commands:
  - bd history <id> - Show issue version history
  - bd diff <from> <to> - Show changes between commits/branches
  - bd branch [name] - List or create branches
  - bd vc merge <branch> - Merge branch to current
  - bd vc commit -m <msg> - Create a commit
  - bd vc status - Show current branch/commit

- Added --as-of flag to bd show for time-travel queries

- IsVersioned() helper for graceful SQLite backend detection

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
quartz
2026-01-17 01:54:55 -08:00
committed by gastown/crew/dennis
parent a7cd9136d8
commit 94581ab233
11 changed files with 1031 additions and 10 deletions

View File

@@ -25,8 +25,15 @@ var showCmd = &cobra.Command{
shortMode, _ := cmd.Flags().GetBool("short")
showRefs, _ := cmd.Flags().GetBool("refs")
showChildren, _ := cmd.Flags().GetBool("children")
asOfRef, _ := cmd.Flags().GetString("as-of")
ctx := rootCtx
// Handle --as-of flag: show issue at a specific point in history
if asOfRef != "" {
showIssueAsOf(ctx, args, asOfRef, shortMode)
return
}
// Check database freshness before reading
// Skip check when using daemon (daemon auto-imports on staleness)
if daemonClient == nil {
@@ -1039,11 +1046,62 @@ func containsStr(slice []string, val string) bool {
return false
}
// showIssueAsOf displays issues as they existed at a specific commit or branch ref.
// This requires a versioned storage backend (e.g., Dolt).
func showIssueAsOf(ctx context.Context, args []string, ref string, shortMode bool) {
// Check if storage supports versioning
vs, ok := storage.AsVersioned(store)
if !ok {
FatalErrorRespectJSON("--as-of requires Dolt backend (current backend does not support versioning)")
}
var allIssues []*types.Issue
for idx, id := range args {
issue, err := vs.AsOf(ctx, id, ref)
if err != nil {
fmt.Fprintf(os.Stderr, "Error fetching %s as of %s: %v\n", id, ref, err)
continue
}
if issue == nil {
fmt.Fprintf(os.Stderr, "Issue %s did not exist at %s\n", id, ref)
continue
}
if shortMode {
fmt.Println(formatShortIssue(issue))
continue
}
if jsonOutput {
allIssues = append(allIssues, issue)
continue
}
if idx > 0 {
fmt.Println("\n" + ui.RenderMuted(strings.Repeat("-", 60)))
}
// Display header with ref indicator
fmt.Printf("\n%s (as of %s)\n", formatIssueHeader(issue), ui.RenderMuted(ref))
fmt.Println(formatIssueMetadata(issue))
if issue.Description != "" {
fmt.Printf("\n%s\n%s\n", ui.RenderBold("DESCRIPTION"), ui.RenderMarkdown(issue.Description))
}
fmt.Println()
}
if jsonOutput && len(allIssues) > 0 {
outputJSON(allIssues)
}
}
func init() {
showCmd.Flags().Bool("thread", false, "Show full conversation thread (for messages)")
showCmd.Flags().Bool("short", false, "Show compact one-line output per issue")
showCmd.Flags().Bool("refs", false, "Show issues that reference this issue (reverse lookup)")
showCmd.Flags().Bool("children", false, "Show only the children of this issue")
showCmd.Flags().String("as-of", "", "Show issue as it existed at a specific commit hash or branch (requires Dolt)")
showCmd.ValidArgsFunction = issueIDCompletion
rootCmd.AddCommand(showCmd)
}