feat: surface branch git state in timeline with actionable rows#695
feat: surface branch git state in timeline with actionable rows#695
Conversation
Add git state detection and display as timeline rows, showing the relationship between local branches, their remote tracking branches, and base branches. Each state row includes contextual actions: - Dirty worktree: commit or stash changes - Diverged from origin: rebase onto origin and force-push - Behind/ahead of origin: push or pull - Base branch updates: rebase onto latest base - Merge conflicts: show conflicted files Backend adds a git state module that computes branch state by comparing local, remote, and base branch refs. Frontend renders state as styled timeline rows with icons, badges, and action buttons including progress states for async operations. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
- Combine two separate git fetch calls into a single invocation with both refspecs to halve network round-trips. Falls back to base-only fetch when the branch refspec is missing on the remote. - Extract duplicated is_conflicted_status into a shared status_parse module to avoid drift between state.rs and worktree.rs. - Add comment clarifying the rename/copy arrow format in revert_paths is display-only and never passed back to git commands. - Fix confirmForcePush to keep the dialog open when another operation is in progress, so the user understands why the action was blocked. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Use std::thread::scope to run independent git commands concurrently in two phases: - Phase 1: Fetch runs in parallel with local-only commands (rev-parse HEAD, branch --show-current, status --porcelain) since those read local state unaffected by fetch. - Phase 2: After fetch completes, upstream and base state computations run in parallel since both need up-to-date remote refs but are independent of each other. This reduces wall-clock time by overlapping network I/O (fetch) with local git queries, and overlapping the two post-fetch ref comparisons. The run_git closure gains a Sync bound to allow shared access across threads. All existing callers already satisfy this since their closures only capture shared references and spawn processes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Replace up to 10 individual ws_exec round-trips with at most 2 batched shell scripts when computing branch git state for remote (Blox) projects: - Script 1 (fetch + local state): performs git fetch, rev-parse HEAD, branch --show-current, and status --porcelain in a single sh -c call. Handles missing-ref fallback internally. Replaces 4-5 round-trips. - Script 2 (post-fetch ref state): computes upstream SHA, left-right counts, merge-base, and base branch state in a single sh -c call. Replaces up to 6 round-trips. When the fetch TTL cache is fresh, only a lightweight local-state script + Script 2 runs, giving 2 round-trips total (down from 6-7). The local code path (compute_local_branch_git_state) is unchanged since process spawn overhead is negligible locally. Adds run_workspace_shell helper to branches.rs for executing sh -c scripts via ws_exec, and a resolve_repo_path helper to timeline.rs for resolving workspace repo paths used as script arguments. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Replace the instant-clearing pushingOrigin boolean and the commandPipelinePending flag with session ID tracking that keeps progress states active for the actual duration of the push operation. Push and force push buttons now show "Pushing…" while the session runs, and clicking them opens the session dialog so users can see pipeline steps and output. The force push button switches from danger-red to resume-btn style during progress. The rebase button is disabled with "Force push in progress" while a force push runs. Session IDs are cleared in the session-status-changed listener when the push session completes, errors, or is cancelled, ensuring the progress state matches the real operation lifecycle. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Replace hardcoded /tmp/_staged_fetch_err and /tmp/_staged_fetch_err2 paths with mktemp-generated unique temp files. The previous approach would clobber error output when two branches triggered a batched fetch concurrently on the same Blox workspace. The script now creates a unique temp file at startup and registers a trap to clean it up on exit, eliminating the race condition. Also resolve clippy warnings: - bind_instead_of_map: use map + unwrap_or instead of and_then + Some - needless_lifetimes: remove explicit lifetime in prs.rs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 09024acd65
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| let rebase_ref = rebase_ref_for_target(&branch, target.as_deref()); | ||
| let steps = build_commit_pipeline_steps(&kind, &rebase_ref); | ||
| let pipeline = PipelineExecution::from_steps(&steps).with_kind(kind); |
There was a problem hiding this comment.
Preserve origin-targeted queued rebases
When a diverged branch requests rebase_branch(..., target: "origin") while another session is running/queued, this stores the origin-targeted steps only inside the queued session's pipeline. The queued-session runner later ignores those stored steps and rebuilds the pipeline from base_branch_name(&ctx.branch) in start_queued_commit_pipeline_for_branch, so the queued action silently becomes a rebase onto the base branch instead of origin/{branch} once it starts. Persist the target/kind data used for rebuilding, or reuse the stored queued pipeline steps, so queued diverged-row rebases run the same command the user requested.
Useful? React with 👍 / 👎.
Summary
BranchGitStatecomputation that determines the relationship between local branch, remote tracking branch, and base branch (ahead/behind/diverged/up-to-date)Test plan
state_tests.rs)🤖 Generated with Claude Code