Skip to content

Git log dashboard#55

Open
jaromil wants to merge 9 commits intomainfrom
git-log-dashboard
Open

Git log dashboard#55
jaromil wants to merge 9 commits intomainfrom
git-log-dashboard

Conversation

@jaromil
Copy link
Member

@jaromil jaromil commented Feb 19, 2026

panel in dashboard showing current git log

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a “Git log” panel to the dashboard by introducing a backend /api/git/log endpoint and wiring a new frontend store + UI rendering path (replacing the previous “API metrics” dashboard column).

Changes:

  • Backend: add internal/gitlog (git command adapter + parser) and expose it via GET /api/git/log.
  • Frontend: add fetchGitLog, dashboard store plumbing, conventional-commit parsing, and a new Git log UI section in Dashboard.svelte.
  • Docs/plans: document the new endpoint and record implementation notes.

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
internal/gitlog/parser_test.go Adds unit tests for parsing git log output (binary/truncation/empty).
internal/gitlog/parser.go Implements parsing of git log --numstat output into structured commits/files/stats.
internal/gitlog/gitlog.go Adds git command reader, options normalization, error classification, and shared DTOs.
internal/api/routes.go Registers /api/git/log route and wires GitCmdReader into the REST handler.
internal/api/rest_types.go Adds REST-layer response DTOs for the git log payload and adds GitLogReader to RestHandler.
internal/api/rest_git.go Implements GET /api/git/log handler, query param validation, timeout handling, and DTO conversion.
internal/api/rest_git_test.go Adds API handler tests for payload, validation, and error mapping.
frontend/src/lib/apiClient.js Adds fetchGitLog() plus normalization helpers for commits/files.
frontend/src/lib/dashboardStore.js Replaces metrics-summary loading with git-log loading + auto-refresh + event-triggered refresh.
frontend/src/lib/conventionalCommit.js Adds conventional-commit subject parser and type→badge-class mapping.
frontend/src/views/Dashboard.svelte Replaces “API metrics” section with “Git log” section and styles + conventional badges.
frontend/tests/apiClient.test.js Adds a test for fetchGitLog() normalization and request URL.
frontend/tests/dashboardStore.test.js Updates dashboard store tests to use git log loading and auto-refresh instead of metrics.
frontend/tests/dashboard.test.js Adds UI test coverage for rendering git log entries in the dashboard view.
frontend/tests/conventionalCommit.test.js Adds unit tests for conventional-commit parsing helper.
docs/reference/http-api.md Documents /api/git/log endpoint behavior and example response.
AGENTS.md Adds implementation notes for the git log dashboard feature.
.gestalt/plans/git-log-dashboard.org Adds/updates the implementation plan for the feature.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 499 to 502
<ul>
{#each gitLog.commits as commit}
<li class="gitlog-entry">
<details>
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The git log commits list is rendered with an unkeyed {#each}. Because this list is refreshed periodically, Svelte may reuse DOM nodes by index, which can cause expanded <details> state and rendered content to jump to the wrong commit after updates. Key the loop by a stable identifier (e.g., commit SHA).

Copilot uses AI. Check for mistakes.
}
file, added, deleted, binary, err := parseNumstatLine(line)
if err != nil {
continue
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ParseLogOutput silently skips numstat lines when parseNumstatLine returns an error. That can produce incorrect files_changed/line totals without signaling upstream, making debugging hard if git output format changes or contains unexpected paths. Consider returning an error (or at least collecting and returning a parse warning/error) rather than continuing silently.

Suggested change
continue
return nil, fmt.Errorf("failed to parse numstat line %q: %w", line, err)

Copilot uses AI. Check for mistakes.

workDir, err := os.Getwd()
if err != nil {
return &apiError{Status: http.StatusInternalServerError, Message: "failed to resolve working directory"}
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On os.Getwd() failure this endpoint returns a 500. handleStatus falls back to "unknown" workdir instead of failing the request, so this introduces inconsistent behavior and can make /api/git/log unexpectedly brittle in constrained environments. Consider aligning behavior (e.g., return 503 "git log unavailable" or a 200 empty payload) and optionally log a warning via h.Logger.

Suggested change
return &apiError{Status: http.StatusInternalServerError, Message: "failed to resolve working directory"}
workDir = "unknown"

Copilot uses AI. Check for mistakes.
t.Fatalf("unexpected rename-ish path: %#v", second.Files)
}
}

Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parser tests cover HasBinary and truncation, but they don’t verify the per-file representation of binary numstat entries (i.e., Binary=true with Added/Deleted omitted) when files are not truncated. Adding a test where maxFilesPerCommit is large enough to include the binary file would lock in the intended JSON shape/semantics.

Suggested change
func TestParseLogOutputParsesBinaryFileDetails(t *testing.T) {
raw := "" +
"1111111111111111111111111111111111111111\x002026-02-18T00:00:00Z\x00feat(ui): add dashboard\n" +
"10\t2\tfrontend/src/views/Dashboard.svelte\n" +
"-\t-\tassets/logo.png\n" +
"\n" +
"2222222222222222222222222222222222222222\x002026-02-17T15:00:00Z\x00chore: rename file\n" +
"5\t1\tinternal/api/{old.go => new.go}\n"
commits, err := ParseLogOutput(raw, 10)
if err != nil {
t.Fatalf("parse output: %v", err)
}
if len(commits) != 2 {
t.Fatalf("expected 2 commits, got %d", len(commits))
}
first := commits[0]
if first.ShortSHA != "111111111111" {
t.Fatalf("unexpected short sha: %q", first.ShortSHA)
}
if first.Stats.FilesChanged != 2 {
t.Fatalf("expected files changed 2, got %d", first.Stats.FilesChanged)
}
if !first.Stats.HasBinary {
t.Fatalf("expected binary flag on stats")
}
if first.FilesTruncated {
t.Fatalf("did not expect files to be truncated")
}
if len(first.Files) != 2 {
t.Fatalf("expected 2 files in payload, got %d", len(first.Files))
}
textFile := first.Files[0]
if textFile.Path != "frontend/src/views/Dashboard.svelte" {
t.Fatalf("unexpected first file path: %q", textFile.Path)
}
if textFile.Added == nil || *textFile.Added != 10 {
t.Fatalf("expected added=10 for first file")
}
if textFile.Deleted == nil || *textFile.Deleted != 2 {
t.Fatalf("expected deleted=2 for first file")
}
binaryFile := first.Files[1]
if binaryFile.Path != "assets/logo.png" {
t.Fatalf("unexpected binary file path: %q", binaryFile.Path)
}
if !binaryFile.Binary {
t.Fatalf("expected Binary=true for binary file")
}
if binaryFile.Added != nil || binaryFile.Deleted != nil {
t.Fatalf("expected Added/Deleted to be omitted for binary file, got added=%v deleted=%v", binaryFile.Added, binaryFile.Deleted)
}
}

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments