From 546de2f8161c2ce513d5a2b28a788396c7021e29 Mon Sep 17 00:00:00 2001 From: Peter Mertz Date: Thu, 26 Feb 2026 11:10:25 -0500 Subject: [PATCH] Fix diff view empty for sessions created before base_commit tracking Sessions created before migration 004 have base_commit=''. Instead of returning an empty diff, infer the base commit via git merge-base using the source branch (or repo default branch), then backfill the DB so it only needs to be computed once. Co-Authored-By: Claude Opus 4.6 --- internal/api/sessions.go | 45 +++++++++++++++++++++++++++++++++++----- internal/git/diff.go | 10 +++++++++ 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/internal/api/sessions.go b/internal/api/sessions.go index 49568de..3b8722a 100644 --- a/internal/api/sessions.go +++ b/internal/api/sessions.go @@ -212,9 +212,10 @@ func (h *SessionsHandler) HandleDelete(w http.ResponseWriter, r *http.Request) { func (h *SessionsHandler) HandleDiff(w http.ResponseWriter, r *http.Request) { id := r.PathValue("id") - var worktreePath, baseCommit string - err := h.db.QueryRow(`SELECT worktree_path, base_commit FROM sessions WHERE id = ?`, id). - Scan(&worktreePath, &baseCommit) + var worktreePath, baseCommit, sourceBranch string + var repoID int64 + err := h.db.QueryRow(`SELECT worktree_path, base_commit, source_branch, repo_id FROM sessions WHERE id = ?`, id). + Scan(&worktreePath, &baseCommit, &sourceBranch, &repoID) if err == sql.ErrNoRows { WriteError(w, http.StatusNotFound, "session not found") return @@ -223,9 +224,16 @@ func (h *SessionsHandler) HandleDiff(w http.ResponseWriter, r *http.Request) { WriteError(w, http.StatusInternalServerError, err.Error()) return } + + // For sessions created before base_commit was tracked, try to compute it if baseCommit == "" { - WriteJSON(w, http.StatusOK, git.DiffResult{}) - return + baseCommit = inferBaseCommit(h.db, worktreePath, sourceBranch, repoID) + if baseCommit == "" { + WriteJSON(w, http.StatusOK, git.DiffResult{}) + return + } + // Backfill so we don't recompute next time + h.db.Exec(`UPDATE sessions SET base_commit = ? WHERE id = ?`, baseCommit, id) } diff, err := git.Diff(worktreePath, baseCommit) @@ -236,6 +244,33 @@ func (h *SessionsHandler) HandleDiff(w http.ResponseWriter, r *http.Request) { WriteJSON(w, http.StatusOK, diff) } +// inferBaseCommit tries to determine the base commit for a session that +// was created before base_commit tracking was added. It uses git merge-base +// to find where the worktree branch diverged from the source branch. +func inferBaseCommit(db *sql.DB, worktreePath, sourceBranch string, repoID int64) string { + // Try source_branch first, fall back to repo's default branch + ref := sourceBranch + if ref == "" { + var defaultBranch string + db.QueryRow(`SELECT default_branch FROM repositories WHERE id = ?`, repoID).Scan(&defaultBranch) + if defaultBranch != "" { + ref = defaultBranch + } else { + ref = "main" + } + } + + // Try merge-base with origin/ + if commit, err := git.MergeBase(worktreePath, "HEAD", "origin/"+ref); err == nil { + return commit + } + // Try without origin/ prefix + if commit, err := git.MergeBase(worktreePath, "HEAD", ref); err == nil { + return commit + } + return "" +} + // resolveCommand returns the override command string for a CLI type if one // exists in settings, otherwise returns the bare CLI type name. func resolveCommand(db *sql.DB, cliType string) string { diff --git a/internal/git/diff.go b/internal/git/diff.go index 44379ca..6e6673d 100644 --- a/internal/git/diff.go +++ b/internal/git/diff.go @@ -59,6 +59,16 @@ func ResolveCommit(repoOrWorktreePath, ref string) (string, error) { return strings.TrimSpace(string(out)), nil } +// MergeBase finds the best common ancestor between two commits. +func MergeBase(repoOrWorktreePath, ref1, ref2 string) (string, error) { + cmd := exec.Command("git", "-C", repoOrWorktreePath, "merge-base", ref1, ref2) + out, err := cmd.Output() + if err != nil { + return "", fmt.Errorf("git merge-base %s %s: %w", ref1, ref2, err) + } + return strings.TrimSpace(string(out)), nil +} + // Diff computes the diff between a base commit and the current working tree state. func Diff(worktreePath, baseCommit string) (*DiffResult, error) { cmd := exec.Command("git", "-C", worktreePath, "diff", baseCommit)