Skip to content

Instantly share code, notes, and snippets.

@eonist
Created February 22, 2026 19:57
Show Gist options
  • Select an option

  • Save eonist/ef6d0f2903e5cdbfe4fa4d9a063c088f to your computer and use it in GitHub Desktop.

Select an option

Save eonist/ef6d0f2903e5cdbfe4fa4d9a063c088f to your computer and use it in GitHub Desktop.
rebase-algo.md

Here's a comprehensive implementation plan for AI Rebase, drawing from issue #177 , the existing split architecture in issue #196 , and the codebase structure .

High-Level Architecture

The AI Rebase is the inverse of the split feature — instead of one commit → many, it's many commits → fewer logical ones. You can mirror the existing split pipeline but reverse the data flow. The key files to model after are omni_commit.rs on the Rust side and the commit view components in src/views/commit/ .

Phase 1: Data Collection (Rust Backend)

Add a new Tauri command in src-tauri/src/ (e.g., omni_rebase.rs) that:

  1. Collects commit metadata for the selected range — for each commit, gather: SHA, message, author, timestamp, and the list of files changed with their diffs
  2. Builds per-commit dossiers — a stripped-down summary per commit, similar to what you described in (https://github.com/edgeleap/omni-app/issues/196#issuecomment-3796993207): file paths touched, diff stats (additions/deletions), and a truncated diff snippet
  3. Deduplicates file changes — track how many times each file appears across the selected commits (this feeds the "file change reduction" metric)
// src-tauri/src/omni_rebase.rs (new file)
#[tauri::command]
pub async fn ai_rebase_analyze(
    repo_path: String,
    commit_shas: Vec<String>,  // selected commits
) -> Result<RebaseAnalysis, String> {
    // 1. git log --format for each SHA → collect metadata
    // 2. git diff SHA~1..SHA for each → build dossiers
    // 3. Return RebaseAnalysis { dossiers, file_touch_counts }
}

#[tauri::command]
pub async fn ai_rebase_execute(
    repo_path: String,
    rebase_plan: RebasePlan,  // the AI-decided groupings
) -> Result<Vec<NewCommit>, String> {
    // interactive rebase execution via git
}

Register these in main.rs alongside the existing commit commands .

Phase 2: Recursive Grouping Algorithm

This is the core AI logic. Following the recursive approach you described in (https://github.com/edgeleap/omni-app/issues/196#issuecomment-3796993207) , build this as a TypeScript service (since AI calls likely go through the frontend):

// src/services/rebaseGrouper.ts (new file)

interface CommitDossier {
  sha: string;
  message: string;
  files: string[];        // paths touched
  diffStats: { additions: number; deletions: number };
  diffSnippet: string;    // truncated for token budget
}

interface RebaseGroup {
  commits: CommitDossier[];
  proposedMessage: string;
  category: string;       // e.g., "auth", "database", "UI"
}

// Step 1: Heuristic pre-grouping (fast, no AI needed)
function preGroupByFileOverlap(dossiers: CommitDossier[]): CommitDossier[][] {
  // Group commits that touch the same files together
  // Uses set intersection: if commits A and B share >50% files, group them
  // This is the "recursive" part — split into buckets, then sub-bucket
}

// Step 2: AI refinement (send pre-groups to LLM)
async function aiRefineGroups(
  preGroups: CommitDossier[][],
  userPrompt?: string     // #196 mentions user wishes
): Promise<RebaseGroup[]> {
  // Send each pre-group to LLM with prompt:
  // "Given these commits, should they stay as one group or be split further?"
  // Works on AFM because each call is small (one group at a time)
}

// Step 3: AI message generation (reuse commit enricher)
async function enrichGroupMessages(
  groups: RebaseGroup[]
): Promise<RebaseGroup[]> {
  // Reuse existing commit enricher algorithm
  // Carry over relevant original descriptions
}

The recursive design means it scales from 10 to 1000 commits — more commits just means more iterations, not bigger payloads per AI call . @dernDren161's existing DiffSense algorithm can be reused for the diff analysis step within each dossier .

Phase 3: Git Execution Layer

The actual rebase needs to happen in Rust via git2 or shell commands:

// Inside omni_rebase.rs
pub fn execute_rebase_plan(
    repo_path: &str, 
    groups: Vec<RebaseGroup>
) -> Result<(), String> {
    // Option A: git rebase -i with auto-generated todo list
    //   - Mark commits as "squash" or "fixup" per group
    //   - Set the new commit message per group
    
    // Option B: Soft reset + selective re-commit (simpler)
    //   1. git branch backup-<timestamp>  (safety)
    //   2. git reset --soft <base_commit>
    //   3. For each group, stage only the group's files and commit
    //   4. This naturally deduplicates file touches
}

Option B (soft reset + re-commit) is probably simpler and more reliable than generating an interactive rebase script, especially since you're rewriting history entirely anyway .

Phase 4: UI Integration

Build on the existing commit view components :

Component Location Purpose
RebaseToggle Extend BottomBar.tsx Toggle to switch commit list into "select for rebase" mode
RebaseSelectionList New component in src/views/commit/components/ Checkboxes on each commit row
RebasePreview New component Shows proposed groupings before executing
RebaseContextMenu Extend existing context menu Right-click → "Auto Rebase" option

The state lives in a new store:

// src/stores/commit/rebaseStore.ts (new file)
interface RebaseStore {
  isRebaseMode: boolean;
  selectedCommitShas: string[];
  proposedGroups: RebaseGroup[] | null;
  isAnalyzing: boolean;
  isExecuting: boolean;
  
  // Actions
  toggleRebaseMode: () => void;
  toggleCommitSelection: (sha: string) => void;
  analyzeSelected: () => Promise<void>;  // triggers Phase 2
  executeRebase: () => Promise<void>;    // triggers Phase 3
  reset: () => void;
}

Phase 5: The Full Pipeline

User selects commits in UI
         |
         v
[Rust] ai_rebase_analyze() → commit dossiers
         |
         v
[TS] preGroupByFileOverlap() → heuristic clusters
         |
         v
[TS] aiRefineGroups() → LLM validates/adjusts groups
         |
         v
[TS] enrichGroupMessages() → clean commit messages
         |
         v
Preview shown to user (editable)
         |
         v
[Rust] ai_rebase_execute() → git operations
         |
         v
Clean commit history

Key Design Decisions

  • Safety first: Always create a backup branch before rebase. Show it in the UI so users can undo .
  • AFM-compatible: Keep per-AI-call token budgets small by using the recursive approach — group-by-group, not all commits at once. This is the key insight from (https://github.com/edgeleap/omni-app/issues/196#issuecomment-3796993207) .
  • Reuse the commit enricher you already have for description generation — no need to build new prompt engineering for that .
  • The heuristic pre-grouping step (file overlap detection) does the heavy lifting for free. AI only needs to validate and refine, which makes it fast even on AFM .
  • User prompt support from (https://github.com/edgeleap/omni-app/issues/196#issuecomment-3797070923) — the existing PromptOverlay.tsx component can be reused to let users guide the grouping ("keep .md files separate", etc.) .

Suggested File Additions

src-tauri/src/omni_rebase.rs          # Rust: git operations + dossier builder
src/services/rebaseGrouper.ts         # TS: recursive grouping algorithm
src/stores/commit/rebaseStore.ts      # TS: UI state management
src/views/commit/components/RebasePreview.tsx  # Preview grouped commits
src/views/commit/hooks/useRebase.ts   # Hook wiring store → Tauri commands

The biggest win is that your split infra (omni_commit.rs, the DiffSense algorithm, the PromptOverlay, the GenerateButton) gives you ~60% of what you need already — you're essentially running the same pipeline in reverse .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment