14 KiB
GIT-005: Enhanced Push/Pull UI
Overview
Improve the push/pull experience with better visibility, branch management, conflict previews, and dashboard-level sync controls. Make syncing with remotes more intuitive and less error-prone.
Context
The current Version Control panel has push/pull functionality via GitStatusButton, but:
- Only visible when the panel is open
- Branch switching is buried in menus
- No preview of what will be pulled
- Conflict resolution is complex
This task brings sync operations to the forefront and adds safeguards.
Existing Infrastructure
From GitStatusButton.tsx:
// Status kinds: 'default', 'fetch', 'error-fetch', 'pull', 'push', 'push-repository', 'set-authorization'
case 'push': {
label = localCommitCount === 1 ? `Push 1 local commit` : `Push ${localCommitCount} local commits`;
}
case 'pull': {
label = remoteCommitCount === 1 ? `Pull 1 remote commit` : `Pull ${remoteCommitCount} remote commits`;
}
From fetch.context.ts:
localCommitCount // Commits ahead of remote
remoteCommitCount // Commits behind remote
currentBranch // Current branch info
branches // All branches
Requirements
Functional Requirements
-
Dashboard Sync Button
- Visible sync button in project row (from GIT-002)
- One-click fetch & show status
- Quick push/pull from dashboard
-
Branch Selector
- Dropdown showing current branch
- Quick switch between branches
- Create new branch option
- Branch search for projects with many branches
- Remote branch indicators
-
Pull Preview
- Show what commits will be pulled
- List affected files
- Warning for potential conflicts
- "Preview" mode before actual pull
-
Conflict Prevention
- Check for conflicts before pull
- Suggest stashing changes first
- Clear conflict resolution workflow
- "Abort" option during conflicts
-
Push Confirmation
- Show commits being pushed
- Branch protection warning (if pushing to main)
- Force push warning (if needed)
-
Sync Status Header
- Always-visible status in editor header
- Current branch display
- Quick sync actions
- Connection indicator
Non-Functional Requirements
- Sync operations don't block UI
- Progress visible for long operations
- Works offline (queues operations)
- Clear error messages
Technical Approach
1. Sync Status Header Component
// packages/noodl-core-ui/src/components/git/SyncStatusHeader/SyncStatusHeader.tsx
interface SyncStatusHeaderProps {
currentBranch: string;
aheadCount: number;
behindCount: number;
hasUncommitted: boolean;
isOnline: boolean;
lastFetchTime: number;
onPush: () => void;
onPull: () => void;
onFetch: () => void;
onBranchChange: (branch: string) => void;
}
2. Branch Selector Component
// packages/noodl-core-ui/src/components/git/BranchSelector/BranchSelector.tsx
interface BranchSelectorProps {
currentBranch: Branch;
branches: Branch[];
onSelect: (branch: Branch) => void;
onCreate: (name: string) => void;
}
interface Branch {
name: string;
nameWithoutRemote: string;
isLocal: boolean;
isRemote: boolean;
isCurrent: boolean;
lastCommit?: {
sha: string;
message: string;
date: string;
};
}
3. Pull Preview Modal
// packages/noodl-core-ui/src/components/git/PullPreviewModal/PullPreviewModal.tsx
interface PullPreviewModalProps {
commits: Commit[];
affectedFiles: FileChange[];
hasConflicts: boolean;
conflictFiles?: string[];
onPull: () => Promise<void>;
onCancel: () => void;
}
interface Commit {
sha: string;
message: string;
author: string;
date: string;
}
interface FileChange {
path: string;
status: 'added' | 'modified' | 'deleted';
hasConflict: boolean;
}
4. Conflict Resolution Flow
// packages/noodl-editor/src/editor/src/services/ConflictResolutionService.ts
class ConflictResolutionService {
// Check for potential conflicts before pull
async previewConflicts(): Promise<ConflictPreview>;
// Handle stashing
async stashAndPull(): Promise<void>;
// Resolution strategies
async resolveWithOurs(file: string): Promise<void>;
async resolveWithTheirs(file: string): Promise<void>;
async openMergeTool(file: string): Promise<void>;
// Abort
async abortMerge(): Promise<void>;
}
UI Mockups
Sync Status Header (Editor)
┌─────────────────────────────────────────────────────────────────────────────┐
│ [main ▾] ↑3 ↓2 ●5 uncommitted 🟢 Connected [Fetch] [Pull] [Push] │
└─────────────────────────────────────────────────────────────────────────────┘
Branch Selector Dropdown
┌─────────────────────────────────────┐
│ 🔍 Search branches... │
├─────────────────────────────────────┤
│ LOCAL │
│ ✓ main │
│ feature/new-login │
│ bugfix/header-styling │
├─────────────────────────────────────┤
│ REMOTE │
│ origin/develop │
│ origin/release-1.0 │
├─────────────────────────────────────┤
│ + Create new branch... │
└─────────────────────────────────────┘
Pull Preview Modal
┌─────────────────────────────────────────────────────────────────────┐
│ Pull Preview [×] │
├─────────────────────────────────────────────────────────────────────┤
│ Pulling 3 commits from origin/main │
│ │
│ COMMITS │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ a1b2c3d Fix login validation John Doe 2 hours ago │ │
│ │ d4e5f6g Add password reset flow Jane Smith 5 hours ago │ │
│ │ h7i8j9k Update dependencies John Doe 1 day ago │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ FILES CHANGED (12) │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ M components/LoginPage.ndjson │ │
│ │ M components/Header.ndjson │ │
│ │ A components/PasswordReset.ndjson │ │
│ │ D components/OldLogin.ndjson │ │
│ │ ⚠️ M project.json (potential conflict) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ⚠️ You have uncommitted changes. They will be stashed before pull. │
│ │
│ [Cancel] [Pull Now] │
└─────────────────────────────────────────────────────────────────────┘
Conflict Warning
┌─────────────────────────────────────────────────────────────────────┐
│ ⚠️ Potential Conflicts Detected [×] │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ The following files have been modified both locally and remotely: │
│ │
│ • project.json │
│ • components/LoginPage.ndjson │
│ │
│ Noodl will attempt to merge these changes automatically, but you │
│ may need to resolve conflicts manually. │
│ │
│ Recommended: Commit your local changes first for a cleaner merge. │
│ │
│ [Cancel] [Commit First] [Pull Anyway] │
└─────────────────────────────────────────────────────────────────────┘
Files to Create
packages/noodl-core-ui/src/components/git/SyncStatusHeader/SyncStatusHeader.tsxpackages/noodl-core-ui/src/components/git/SyncStatusHeader/SyncStatusHeader.module.scsspackages/noodl-core-ui/src/components/git/BranchSelector/BranchSelector.tsxpackages/noodl-core-ui/src/components/git/BranchSelector/BranchSelector.module.scsspackages/noodl-core-ui/src/components/git/PullPreviewModal/PullPreviewModal.tsxpackages/noodl-core-ui/src/components/git/PushConfirmModal/PushConfirmModal.tsxpackages/noodl-core-ui/src/components/git/ConflictWarningModal/ConflictWarningModal.tsxpackages/noodl-editor/src/editor/src/services/ConflictResolutionService.ts
Files to Modify
-
packages/noodl-editor/src/editor/src/pages/EditorPage/EditorPage.tsx- Add SyncStatusHeader to editor layout
-
packages/noodl-editor/src/editor/src/views/panels/VersionControlPanel/VersionControlPanel.tsx- Integrate new BranchSelector
- Add pull preview before pulling
-
packages/noodl-editor/src/editor/src/views/panels/VersionControlPanel/components/GitStatusButton.tsx- Update to use new pull/push flows
-
packages/noodl-editor/src/editor/src/views/panels/VersionControlPanel/context/fetch.context.ts- Add preview fetch logic
- Add conflict detection
-
packages/noodl-core-ui/src/preview/launcher/Launcher/components/ProjectList/ProjectListRow.tsx- Add quick sync button (if not in GIT-002)
Implementation Steps
Phase 1: Branch Selector
- Create BranchSelector component
- Implement search/filter
- Add create branch flow
- Integrate into Version Control panel
Phase 2: Sync Status Header
- Create SyncStatusHeader component
- Add to editor layout
- Wire up actions
- Add connection indicator
Phase 3: Pull Preview
- Create PullPreviewModal
- Implement commit/file listing
- Add conflict detection
- Wire up pull action
Phase 4: Conflict Handling
- Create ConflictWarningModal
- Create ConflictResolutionService
- Implement stash-before-pull
- Add abort functionality
Phase 5: Push Enhancements
- Create PushConfirmModal
- Add branch protection warning
- Show commit list
- Handle force push
Phase 6: Dashboard Integration
- Add sync button to project rows
- Quick push/pull from dashboard
- Update status after sync
Testing Checklist
- Branch selector shows all branches
- Branch search filters correctly
- Switching branches works
- Creating new branch works
- Sync status header shows correct counts
- Fetch updates status
- Pull preview shows correct commits
- Pull preview shows affected files
- Conflict warning appears when appropriate
- Stash-before-pull works
- Pull completes successfully
- Push confirmation shows commits
- Push completes successfully
- Dashboard sync button works
- Offline state handled gracefully
Dependencies
- GIT-002 (Git Status Dashboard) - for dashboard integration
- GIT-001 (GitHub OAuth) - for authenticated operations
Blocked By
- GIT-002
Blocks
- None
Estimated Effort
- Branch selector: 3-4 hours
- Sync status header: 2-3 hours
- Pull preview: 4-5 hours
- Conflict handling: 4-5 hours
- Push enhancements: 2-3 hours
- Dashboard integration: 2-3 hours
- Total: 17-23 hours
Success Criteria
- Branch switching is easy and visible
- Users can preview what will be pulled
- Conflict potential is detected before pull
- Stashing is automatic when needed
- Push shows what's being pushed
- Quick sync available from dashboard
- Status always visible in editor
Future Enhancements
- Pull request creation
- Branch comparison
- Revert/cherry-pick commits
- Squash commits before push
- Auto-sync on save (optional)
- Branch naming conventions/templates