Skip to content

feat(git-graph): revamp history browsing and rendering - #288

Merged
heath-s merged 3 commits into
mainfrom
fix/git-graph-revamp
Jul 31, 2026
Merged

feat(git-graph): revamp history browsing and rendering#288
heath-s merged 3 commits into
mainfrom
fix/git-graph-revamp

Conversation

@heath-s

@heath-s heath-s commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • rebuild Git Graph as a persistent editor surface with branch/ref filtering, search navigation, paging, configurable columns, and working-tree/commit details
  • extend the SCM bridge with validated graph, repository-state, commit metadata, file-stat, and diff contracts
  • use Stave-authored graph layout, commit parsing, and SVG2 M/L/C edge serialization while preserving smooth lane transitions and layered branch strokes
  • remove the prior Git Graph-specific attribution resource and packaging entry while retaining Stave's own Apache LICENSE and NOTICE

Why

Git Graph needed a denser history-browsing workflow and stronger renderer-to-host contracts. A provenance audit also found that later Git Graph releases use a restrictive license. The graph implementation is now independently authored, including the edge path geometry, and adds no runtime geometry dependency or edge-specific third-party notice requirement.

Verification

  • bun run test:ci — reliability gates, 319 isolated test files, typecheck, web build, and desktop build passed
  • focused Git Graph, packaging, and theme tests — 56 tests passed
  • bun run test:e2e -- tests/e2e/git-graph-entry.e2e.ts — 2 Chromium tests passed
  • git diff --check
  • Lens inspection in light and dark themes across 300+ commits, multi-lane histories, merge curves, and deep scrolling

@upwind-code-us

upwind-code-us Bot commented Jul 31, 2026

Copy link
Copy Markdown

Upwind Upwind IaC Scan - ✅ Proceed with Deployment

0 misconfigurations detected

View full analysis in Upwind Console →

Scan completed in 2s

Scan history (4 scans)
Commit Scanned at New Resolved Net
bd06827 2026-07-31 10:13 UTC 0 0 0
bd06827 2026-07-31 10:12 UTC
1355625 2026-07-31 10:53 UTC
feebdcb < 2026-07-31 11:27 UTC 0 0 0

Last scanned: feebdcb · 2026-07-31 11:27 UTC

@heath-s
heath-s marked this pull request as ready for review July 31, 2026 10:13

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bd0682744a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +437 to +440
setWorkingTreeFiles(
result.items.map((item) => ({
path: item.path,
status: resolveWorkingTreeStatus(item),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve exact paths for working-tree diffs

When a changed path contains spaces or non-ASCII characters, git status --porcelain returns a quoted/escaped display path, and this mapping stores that representation as the filesystem path. Clicking the entry later passes the quoted value to loadWorkingTreeDiff and openFileFromTree, so a file such as file name.txt opens an empty diff or cannot be opened. Parse a NUL-delimited status response into exact paths, including separate old/new paths for renames, before creating these file changes.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in feebdcb.

Root cause was the shared status parser, so the fix landed there rather than in this hook. parseSourceControlStatusLines now parses git status --porcelain=v1 -z (NUL-delimited, paths verbatim) first and falls back to newline output only when needed, un-escaping octal/quoted forms via unquoteGitPath. path is therefore an exact filesystem path, and renames keep a separate oldPath.

Every producer and consumer was aligned: -z added in electron/host-service/scm-runtime.ts and server/dev-server.ts, and renames are recombined into the old -> new pathspec pair through formatSourceControlDiffPath before crossing IPC, so stage/unstage/discard/diff all keep both pathspecs.

Covered by new tests in tests/source-control-status.test.ts (NUL, newline fallback, non-ASCII, trailing space) and tests/source-control-diff.test.ts (rename recombination).

Comment on lines +241 to +246
if (!nextSelection) {
if (currentSelection) {
detailsRequestRef.current += 1;
}
setDetails(null);
setWorkingTreeFiles([]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reload preserved working-tree details

When the working-tree row is selected, hiding and reopening the Git Graph restores that selection from graphCache, but workingTreeFiles is initialized empty and the initial reload only fetches files when the user explicitly selects the row again. The same branch leaves the file list stale after Refresh while preserving the selection, so the details panel can show no files or outdated files despite the refreshed summary. Reload or cache the working-tree file list whenever this selection survives reconciliation.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in feebdcb.

Extracted refreshWorkingTreeFiles and added a pure helper, resolveGitGraphReloadEffects, that decides the post-reconciliation effects: whenever a working-tree selection survives a reload or remount, the file list is refetched instead of staying at its empty initial value. selectWorkingTree reuses the same helper path, so the details panel and the refreshed summary no longer diverge after Refresh or after hiding and reopening the graph.

Covered by new tests in tests/git-graph-selection.test.ts.

? result.availableRefs
: current.availableRefs,
};
cacheCurrent(next, selection);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Cache the live selection after pagination

When a load-more request starts and the user selects a commit before it completes, this callback writes the selection captured when pagination began back into graphCache. The visible selection remains correct until the graph surface unmounts, but reopening it restores the stale cached selection and unexpectedly closes or changes the details panel. Cache selectionRef.current (or otherwise read the live selection) when the page resolves.

AGENTS.md reference: AGENTS.md:L151-L151

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in feebdcb.

The load-more completion callback now caches selectionRef.current instead of the selection value captured when pagination began, so a commit selected mid-request is what gets written into graphCache. Reopening the surface no longer restores a stale selection.

@heath-s
heath-s enabled auto-merge (squash) July 31, 2026 10:59
@heath-s
heath-s disabled auto-merge July 31, 2026 10:59
Address Codex review on #288:

- Parse `git status --porcelain -z` (and unquote newline output) into
  exact filesystem paths with separate rename sources, so entries with
  spaces or non-ASCII names open real diffs instead of empty ones.
- Refetch the working-tree file list whenever that selection survives a
  reload or remount, keeping the details panel in sync after Refresh.
- Cache the live selection (`selectionRef.current`) when a pagination
  page resolves, so reopening the graph no longer restores a stale one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@heath-s
heath-s enabled auto-merge (squash) July 31, 2026 11:30
@heath-s
heath-s merged commit 4ae1617 into main Jul 31, 2026
4 checks passed
@heath-s
heath-s deleted the fix/git-graph-revamp branch July 31, 2026 13:45
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.

2 participants