Data Processing Pipelines
Status Processing
Copy
+===============================================================================+
| |
| STATUS PROCESSING PIPELINE |
| |
+===============================================================================+
| |
| INPUT: path (repository path) |
| |
| Step 1: Repository::open(path) |
| | |
| v |
| +-------------------------------------------------------------------------+ |
| | Step 2: Configure StatusOptions | |
| +-------------------------------------------------------------------------+ |
| | opts.include_untracked(true) | |
| | opts.recurse_untracked_dirs(true) | |
| | opts.include_ignored(false) | |
| | opts.exclude_submodules(true) | |
| +-------------------------------------------------------------------------+ |
| | |
| v |
| Step 3: repo.statuses(Some(&mut opts)) |
| | |
| v |
| +-------------------------------------------------------------------------+ |
| | Step 4: Iterate and classify each entry | |
| +-------------------------------------------------------------------------+ |
| | for entry in statuses.iter() { | |
| | let status = entry.status(); | |
| | | |
| | is_staged | is_unstaged | is_untracked | |
| | status.is_* | status.is_* | status.is_wt_new() | |
| | | | | | |
| | v v v | |
| | staged.push unstaged.push untracked.push | |
| | } | |
| +-------------------------------------------------------------------------+ |
| | |
| v |
| +-------------------------------------------------------------------------+ |
| | Step 5: Get branch info + ahead/behind counts | |
| +-------------------------------------------------------------------------+ |
| | branch = repo.head().shorthand() | |
| | (ahead, behind) = get_ahead_behind(&repo) | |
| +-------------------------------------------------------------------------+ |
| | |
| v |
| OUTPUT: GitStatus { |
| branch, is_head_detached, ahead, behind, |
| staged, unstaged, untracked |
| } |
| |
+===============================================================================+
Diff Parsing
The diff parser uses VTE callbacks to classify each line:Copy
+===============================================================================+
| |
| DIFF PARSING PIPELINE |
| |
+===============================================================================+
| |
| INPUT: path, file_path, staged (bool) |
| |
| +-------------------------------------------------------------------------+ |
| | Step 1: Setup diff options with pathspec | |
| +-------------------------------------------------------------------------+ |
| | let mut diff_opts = DiffOptions::new(); | |
| | diff_opts.pathspec(file_path); | |
| +-------------------------------------------------------------------------+ |
| | |
| v |
| +-------------------------------------------------------------------------+ |
| | Step 2: Get HEAD tree for comparison base | |
| +-------------------------------------------------------------------------+ |
| | let head = repo.head()?; | |
| | let head_tree = head.peel_to_tree()?; | |
| +-------------------------------------------------------------------------+ |
| | |
| v |
| +-------------------------------------------------------------------------+ |
| | Step 3: Choose diff method based on staged flag | |
| +-------------------------------------------------------------------------+ |
| | | |
| | if staged { } else { | |
| | diff_tree_to_index diff_tree_to_workdir_with_index | |
| | } } | |
| | | | | |
| | v v | |
| | Compare HEAD to index Compare HEAD to working dir | |
| +-------------------------------------------------------------------------+ |
| | |
| v |
| +-------------------------------------------------------------------------+ |
| | Step 4: Parse diff using print callback | |
| +-------------------------------------------------------------------------+ |
| | diff.print(Patch, |delta, hunk, line| { | |
| | match line.origin() { | |
| | +------------------------------------------------+ | |
| | | Origin | Line Type | Action | | |
| | |--------|------------|--------------------------| | |
| | | 'F' | "header" | File header | | |
| | | 'H' | "header" | Hunk header | | |
| | | '@' | "hunk" | Hunk marker | | |
| | | '+' | "addition" | Count additions | | |
| | | '-' | "deletion" | Count deletions | | |
| | | ' ' | "context" | Context line | | |
| | +------------------------------------------------+ | |
| | } | |
| | }); | |
| +-------------------------------------------------------------------------+ |
| | |
| v |
| OUTPUT: ParsedDiff { |
| path: file_path, |
| lines: Vec<ParsedDiffLine>, |
| additions: count, |
| deletions: count |
| } |
| |
+===============================================================================+
Commit Graph Generation
Copy
+===============================================================================+
| |
| COMMIT GRAPH PIPELINE |
| |
+===============================================================================+
| |
| INPUT: path, limit (max commits to process) |
| |
| +-------------------------------------------------------------------------+ |
| | Step 1: Collect branch tips | |
| +-------------------------------------------------------------------------+ |
| | for branch in repo.branches()? { | |
| | branch_tips.insert(tip_oid, branch_name); | |
| | } | |
| +-------------------------------------------------------------------------+ |
| | |
| v |
| +-------------------------------------------------------------------------+ |
| | Step 2: Walk commits from HEAD | |
| +-------------------------------------------------------------------------+ |
| | let mut revwalk = repo.revwalk()?; | |
| | revwalk.push_head()?; | |
| | revwalk.set_sorting(Topological | Time); | |
| +-------------------------------------------------------------------------+ |
| | |
| v |
| +-------------------------------------------------------------------------+ |
| | Step 3: Assign columns to branches | |
| +-------------------------------------------------------------------------+ |
| | | |
| | Column Assignment Algorithm: | |
| | +-------------------------------------------------------------------+ | |
| | | Column 0 | Column 1 | Column 2 | Column N | | |
| | |-------------|-------------|-------------|-------------------------| | |
| | | main | feature-a | feature-b | ... | | |
| | | branch | | | | | |
| | +-------------------------------------------------------------------+ | |
| | | |
| | - Main branch gets column 0 | |
| | - Feature branches get 1, 2, ... | |
| | - Reuse columns when branches merge | |
| +-------------------------------------------------------------------------+ |
| | |
| v |
| +-------------------------------------------------------------------------+ |
| | Step 4: Build graph connections | |
| +-------------------------------------------------------------------------+ |
| | for commit in commits { | |
| | Condition | Connection Type | |
| | parents.len() > 1 | "merge_left/right" | |
| | column changes | "branch_left/right" | |
| | otherwise | "straight" | |
| | } | |
| +-------------------------------------------------------------------------+ |
| | |
| v |
| OUTPUT: CommitGraph { |
| nodes: Vec<CommitGraphNode>, |
| branches: Vec<BranchInfo>, |
| total_commits, |
| max_columns |
| } |
| |
+===============================================================================+
Error Handling
All errors are converted to String for IPC compatibility:Copy
// Pattern 1: Direct conversion
let repo = Repository::open(path).map_err(|e| e.to_string())?;
// Pattern 2: With custom context
let output = Command::new("git")
.args(&["..."])
.output()
.map_err(|e| format!("Failed to run git: {}", e))?;
// Pattern 3: Check status and return stderr
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
return Err(stderr.to_string());
}
Graceful Degradation
The backend returns partial data on non-critical errors:Copy
+===============================================================================+
| |
| GRACEFUL DEGRADATION STRATEGY |
| |
+===============================================================================+
| |
| Operation |
| | |
| +-------+-------+ |
| | | |
| v v |
| Success Failure |
| | | |
| v v |
| Use value Try fallback 1 |
| | |
| +-------+-------+ |
| | | |
| v v |
| Success Failure |
| | | |
| v v |
| Use value Try fallback 2 |
| | |
| +-------+-------+ |
| | | |
| v v |
| Success Failure |
| | | |
| v v |
| Use value Use default |
| |
| Result: Always return SOMETHING rather than fail completely |
| |
+===============================================================================+
Data Transformation Chain
Copy
+===============================================================================+
| |
| DATA TRANSFORMATION CHAIN |
| |
+===============================================================================+
| |
| RAW GIT DATA RUST PROCESSING FRONTEND-READY |
| ============ =============== ============== |
| |
| +--------------------+ +--------------------+ +------------------+ |
| | commit.author() | ---> | GitCommitInfo. | ---> | "John Doe" | |
| | .name() | | author | | | |
| +--------------------+ +--------------------+ +------------------+ |
| |
| +--------------------+ +--------------------+ +------------------+ |
| | commit.time() | ---> | format_relative_ | ---> | "5m ago" | |
| | .seconds() | | time() | | | |
| +--------------------+ +--------------------+ +------------------+ |
| |
| +--------------------+ +--------------------+ +------------------+ |
| | diff.print() | ---> | ParsedDiffLine | ---> | Ready for CSS | |
| | (raw callback) | | with line_type | | class binding | |
| +--------------------+ +--------------------+ +------------------+ |
| |
| +--------------------+ +--------------------+ +------------------+ |
| | repo.branches() | ---> | group_branches_ | ---> | "Today", | |
| | (iterator) | | by_time() | | "Yesterday", ... | |
| +--------------------+ +--------------------+ +------------------+ |
| |
+===============================================================================+
Optimization Strategies
Batch API for IPC Reduction
Single call replaces 5 separate roundtrips:Copy
BEFORE: 5 Separate IPC Calls
- git_status
- git_branches
- git_log
- worktree_list
- stash_list
Total: 5 roundtrips, 5 repository opens
AFTER: Single Batch Call
- git_full_refresh
Total: 1 roundtrip, 1 repository open
PERFORMANCE GAIN: ~5x reduction in IPC overhead
Parsed Diff Optimization
Copy
BEFORE: Raw Diff + JS Parsing
Backend sends raw diff string
Frontend splits by lines, detects types, parses headers
Cost: ~10ms per 1000-line diff
AFTER: Parsed Diff
Backend sends ParsedDiff with line_type classification
Frontend iterates array, applies CSS classes
Cost: <1ms per 1000-line diff
PERFORMANCE GAIN: 5-10x improvement on large diffs
Time Formatting in Rust
Relative time formatting moved from JavaScript to Rust:| diff (seconds) | Output |
|---|---|
| < 60 | ”just now” |
| < 3600 | ”m ago” |
| < 86400 | ”h ago” |
| < 604800 | ”d ago” |
| < 2592000 | ”w ago” |
| >= 2592000 | ”YYYY-MM-DD” format |
THE CENTER
These implementation patterns optimize for speed, structure, and completeness. Human should not wait to see AI work. Data must be ready for direct rendering. All relevant context must be included.