Files
bDS/IMPLEMENT_GIT.md

344 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# IMPLEMENT_GIT
## Goal
Implement a VS Code-like Git sidebar workflow for the current project in bDS with:
- Left sidebar rail sync icon (bottom section, directly above Settings)
- Sidebar split into upper **Open Changes** and lower **Version History**
- File click to open diff view against repository in editor tabs with transient/persistent behavior
- `Initialize Git` action when no repo exists
- Periodic status polling and remote fetch polling
- Fetch / Pull / Push actions in repo header
- Commit message input + `Commit` button (add-all + commit)
This plan is scoped to the existing Electron architecture:
- Main process business logic in `src/main/engine`
- IPC handlers in `src/main/ipc`
- Typed bridge in `src/main/preload.ts` and `src/main/shared/electronApi`
- Renderer state/UI in `src/renderer/store` and `src/renderer/components`
---
## External Requirements
## 1) Runtime Dependencies
- **Primary library**: `simple-git`
- **System requirement**: `git` CLI installed and available in PATH
- **Optional later**: fallback to bundled Git binary for users without system Git
## 2) OS / Packaging
- Support macOS, Linux, Windows path handling and quoting.
- If bundling Git later, include signing/notarization and license notices in release pipeline.
## 3) Performance Requirements
- Must handle large change sets without list jump/reflow issues.
- Polling and fetch operations must be background/non-blocking and cancellable.
## 4) Security Requirements
- Run all Git commands in main process only.
- Validate project root path before command execution.
- Never allow arbitrary command injection via renderer inputs.
---
## UX and Behavioral Requirements (from spec)
## Sidebar Rail Button
- Add a new Git sync icon button in the left sidebar rail bottom section, directly above the Settings icon.
- Clicking icon opens/closes Git sidebar view similarly to existing views.
## Sidebar Layout (Git view)
- Upper half: **Open Changes** list.
- Lower half: **Version History** list.
- Repo actions in header: **Fetch**, **Pull**, **Push** icons.
- Open Changes area includes:
- Commit message input field
- Commit button (does add-all + commit)
## No Repository State
- If current project is not a git repo:
- show `Initialize Git` button.
- action runs `git init`.
- if git executable not found, show explicit install guidance message.
## Diff Behavior
- Diff views open in tabs in the editor area.
- Single-click on a changed file opens a diff in a transient tab (reused for subsequent single-clicks), matching post transient tab behavior.
- Double-click on a changed file opens a dedicated non-transient diff tab that remains open until explicitly closed by the user.
- Dedicated diff tabs stay open even when other files are clicked in the sidebar.
- Open diff tabs are persisted in app tab state and restored on next app start, same persistence model as post tabs.
- Clicking a changed file opens diff view of working tree vs repository (HEAD/index depending file state).
- Committing changes automatically closes all open diff tabs because the compared diff baseline no longer applies.
## Polling Behavior
- Poll git status regularly (VS Code-like freshness).
- Refresh should be incremental (preserve list item identity/order strategy where possible).
- Preserve scroll position for large lists; avoid jump-to-top.
## Remote Awareness
- If remote exists, perform regular `git fetch` polling.
- Show upstream relationship in version history section:
- current local HEAD
- upstream branch tip
- ahead/behind indicators
---
## Proposed Architecture
## Main Process (new engine)
Create `src/main/engine/GitEngine.ts` with focused methods:
- `checkAvailability(): Promise<{ gitFound: boolean; version?: string }>`
- `getRepoState(projectPath): Promise<RepoState>`
- `initializeRepo(projectPath): Promise<Result>`
- `getStatus(projectPath): Promise<GitStatusDto>`
- `getDiff(projectPath, filePath): Promise<GitDiffDto>`
- `getHistory(projectPath, limit, cursor?): Promise<HistoryDto>`
- `getRemoteState(projectPath): Promise<RemoteStateDto>`
- `fetch(projectPath): Promise<Result>`
- `pull(projectPath): Promise<Result>`
- `push(projectPath): Promise<Result>`
- `commitAll(projectPath, message): Promise<Result>`
Implementation notes:
- Use `simple-git` instance rooted at active project path.
- Distinguish error classes:
- git missing
- not a repo
- auth/network/merge conflict
- detached HEAD / no upstream
- Normalize file paths to repo-relative format for renderer stability.
## IPC Layer
Add handlers in `src/main/ipc/handlers.ts` and type contracts in shared API:
- `git:checkAvailability`
- `git:getRepoState`
- `git:init`
- `git:status`
- `git:diff`
- `git:history`
- `git:remoteState`
- `git:fetch`
- `git:pull`
- `git:push`
- `git:commitAll`
Expose via `src/main/preload.ts`:
- `window.electronAPI.git.*` methods.
## Renderer State
Extend `src/renderer/store/appStore.ts` with Git slice:
- `activeView` union includes `'git'`
- `git: {`
- `availability`
- `repoState`
- `status` (files + counts)
- `history`
- `remoteState` (branch, upstream, ahead, behind, lastFetchAt)
- `selectedDiffFile`
- `commitMessage`
- `loading/action flags`
- `error`
- `}`
Store actions:
- `setGitStatus`, `mergeGitStatusIncremental`, `setGitHistory`, `setGitRemoteState`
- `setSelectedDiffFile`, `setCommitMessage`
- `setGitPollingState`
Tab behavior extensions:
- Extend `TabType` with `'git-diff'`.
- Use existing transient tab mechanics for single-click diff open.
- Add/ensure explicit pinning path for double-click diff tabs (`isTransient: false`).
- Include diff tabs in persisted tab state (`getTabState` / `restoreTabState`) so they reopen after restart.
- On successful commit action, remove all open `'git-diff'` tabs and clear selected diff state.
## Renderer Components
- Update `src/renderer/components/ActivityBar/ActivityBar.tsx` to place Git icon in the bottom rail group above Settings and wire view toggle.
- Add Git section to `src/renderer/components/Sidebar/Sidebar.tsx` render switch.
- Add dedicated presentational components:
- `src/renderer/components/GitSidebar/GitSidebar.tsx`
- `src/renderer/components/GitSidebar/OpenChangesList.tsx`
- `src/renderer/components/GitSidebar/VersionHistoryList.tsx`
- `src/renderer/components/GitSidebar/RepoActions.tsx`
- Add diff tab rendering in editor area:
- new tab type `'git-diff'`
- diff viewer component `GitDiffView`.
- single-click handler opens/reuses transient diff tab.
- double-click handler opens persistent diff tab.
---
## Polling and Update Strategy
## Status Polling (fast)
- Interval: ~2s when Git view visible, ~510s when hidden.
- Trigger immediate refresh after commit/fetch/pull/push/init.
- Use in-flight guard to avoid concurrent status calls.
## Remote Polling (slower)
- Run only when remote exists and git is available.
- Interval: ~3060s with backoff on errors.
- Use `git fetch --prune` equivalent through `simple-git`.
## Scroll/Render Stability
- Keep stable `key` = repo-relative file path.
- Preserve existing array reference for unchanged items when merging updates.
- Update only changed/added/removed entries in store (incremental diff merge).
- Use virtualization (`react-window`) if list grows beyond threshold (e.g., >300 entries).
- Preserve scrollTop by storing/restoring container position if full list replacement is unavoidable.
## Repositioning Rules
- Do not auto-sort on every tick if sort key not changed.
- Insert new items predictably (status-group + path order) to minimize movement.
- Never auto-scroll on status update.
---
## History Model (lower half)
Each history item should include:
- `commitHash`
- `author`
- `date`
- `subject`
- `isHead`
- `isRemoteHead` (where applicable)
- `refs` (branch/tag labels)
Remote awareness section:
- Show `localBranch -> upstreamBranch`
- Show `ahead N / behind M`
- Show last fetch timestamp and fetch errors (if any)
---
## Error Handling UX
## Git Missing
- Detect once on startup/opening Git view and before actions.
- Display clear CTA text: install Git and restart app.
## Not a Repo
- Show empty state with `Initialize Git` button.
- After successful init, auto-refresh status/history.
## Action Failures
- Show concise toast + inline error in Git panel section.
- Keep previous state rendered (no hard reset).
## Auth/Conflict Cases
- For pull/push conflicts/auth failures, show actionable message; do not hide current status/history.
---
## Test-First Delivery Plan (TDD)
Follow strict red-green-refactor per project rules.
## Phase 1: Contracts and engine scaffolding
1. Add failing tests for `GitEngine` availability/repo detection/status parsing.
2. Implement minimal engine methods.
3. Add IPC contract tests for new `git:*` handlers.
## Phase 2: No-repo + init workflow
1. Add renderer tests for no-repo state and `Initialize Git` button.
2. Implement init action and git-missing messaging.
3. Validate with integration-style IPC mock tests.
## Phase 3: Open changes + diff
1. Add tests for open changes list rendering and file selection.
2. Add tests for opening `git-diff` tab and loading diff.
3. Add tests for single-click transient tab reuse and double-click persistent tab behavior.
4. Implement diff component and tab behavior.
## Phase 3b: Diff tab persistence and commit cleanup
1. Add tests to verify `git-diff` tabs are persisted/restored via tab state.
2. Add tests to verify successful commit closes all open `git-diff` tabs.
3. Implement store and commit-flow wiring for cleanup behavior.
## Phase 4: Commit + repo actions
1. Add tests for commit message entry and commit action (`addAll + commit`).
2. Add tests for fetch/pull/push button wiring and disabled/loading states.
3. Implement action handlers with refresh chaining.
## Phase 5: Polling and stability
1. Add tests for polling intervals and in-flight guards (fake timers).
2. Add tests for incremental list updates preserving scroll/identity.
3. Implement merge strategy + optional virtualization threshold.
## Phase 6: Remote tracking in history
1. Add tests for ahead/behind and upstream marker rendering.
2. Implement periodic fetch + remote state projection.
3. Validate remote indicators against mocked git responses.
## Phase 7: Hardening
1. Add tests for error surfaces (git missing, auth fail, merge conflict).
2. Verify all tests pass.
3. Run full build and fix regressions.
---
## Suggested File-Level Work Breakdown
Main process:
- `src/main/engine/GitEngine.ts` (new)
- `src/main/engine/index.ts` (export)
- `src/main/ipc/handlers.ts` (new handlers)
- `src/main/shared/electronApi.ts` (API types)
- `src/main/preload.ts` (bridge methods)
Renderer:
- `src/renderer/store/appStore.ts` (Git state/actions)
- `src/renderer/components/ActivityBar/ActivityBar.tsx` (Git icon entry in bottom rail, above Settings)
- `src/renderer/components/Sidebar/Sidebar.tsx` (Git view integration)
- `src/renderer/components/GitSidebar/*` (new)
- `src/renderer/components/Editor/*` or new `GitDiffView` component
Tests:
- `tests/engine/GitEngine.test.ts` (new)
- `tests/ipc/handlers.test.ts` (extend)
- `tests/renderer/components/GitSidebar.test.tsx` (new)
- `tests/renderer/store/appStore.git.test.ts` (new)
---
## Milestones and Acceptance Criteria
## Milestone A: Basic Git UX
- Git sync icon appears in left sidebar rail bottom section above Settings.
- Git sidebar opens with no-repo empty state.
- `Initialize Git` works and transitions to repo state.
## Milestone B: Changes + Diff
- Open Changes list renders tracked/untracked/modified/deleted files.
- Single-click opens/reuses transient diff tab and renders correct patch.
- Double-click opens persistent diff tab that remains until user closes it.
- Diff tabs persist across app restarts.
## Milestone C: Commit and Repo Actions
- Commit message + Commit button performs add-all + commit.
- Successful commit closes all open diff tabs automatically.
- Fetch/Pull/Push actions execute with visible status feedback.
## Milestone D: Polling + Remote
- Status polling updates changes without scroll jump.
- Remote fetch polling updates ahead/behind and remote markers.
- History clearly shows local/remote relation.
## Milestone E: Quality Gate
- All tests pass.
- Full build passes.
- No console spam, no renderer freeze on large change sets.
---
## Implementation Order Recommendation
1. Contracts + GitEngine + IPC
2. No-repo/init UX
3. Open changes list + diff viewer
4. Commit + fetch/pull/push actions
5. Polling + incremental list merge + scroll stability
6. Remote-aware history refinement and hardening
This order minimizes risk and delivers user-visible value early while preserving room for performance optimization in later iterations.