Initial public release of Warp.
Repo-Sync-Origin: warpdotdev/warp-internal@12af1d983b
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
# APP-3918: Git Operations Button in Code Review Header
|
||||
|
||||
## Summary
|
||||
|
||||
Add a context-aware git operations button to the code review header that surfaces the most relevant git action (Commit, Push, or Create PR) based on the current repository state. This builds on the header UI refactor in APP-3632.
|
||||
|
||||
## Problem
|
||||
|
||||
Users viewing diffs in the code review panel must leave the panel to commit, push, or create a PR. There is no inline way to advance changes through the git workflow from the code review context. This creates friction — especially for quick commit-and-push flows — and breaks the user's focus.
|
||||
|
||||
## Goals
|
||||
|
||||
- Surface the next logical git action directly in the code review header.
|
||||
- Reduce the number of steps to go from reviewing diffs to committing / pushing / opening a PR.
|
||||
- Provide a dropdown with related git operations so users aren't limited to only the primary action.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- The actual commit, push, and create-PR dialogs are handled by child branches in the stack (`edward/commit-dialog`, `edward/push-dialog`, `edward/pr-dialog`). This branch only wires the button and dropdown; dialog implementation is out of scope.
|
||||
- Staging individual files or hunks from the button (staging is done elsewhere).
|
||||
|
||||
## Figma
|
||||
|
||||
https://www.figma.com/design/T2CtyXgIdjtrLfC03K1n1H/Code-review-2.0?node-id=6138-21140&m=dev
|
||||
|
||||
## User Experience
|
||||
|
||||
### Button appearance
|
||||
|
||||
The git operations button is a split button rendered in the right section of the inner code review header (to the left of the file-nav and overflow buttons). It consists of:
|
||||
|
||||
1. **Primary action button** — an `ActionButton` with `SecondaryTheme`, `ButtonSize::Small`, an icon, and a label describing the current primary action.
|
||||
2. **Chevron button** — a separate `ActionButton` with a `ChevronDown` icon adjoined to the primary button's right edge, opening a dropdown menu of related actions.
|
||||
|
||||
The two buttons use `AdjoinedSide::Right` (primary) and `AdjoinedSide::Left` (chevron) so they visually merge into one split button.
|
||||
|
||||
### Primary action mode
|
||||
|
||||
The button's label, icon, and click behavior are determined by the current `PrimaryGitActionMode`, which is recomputed whenever diff stats or unpushed-commit state changes:
|
||||
|
||||
1. **Commit** — when there are uncommitted changes (diff stats are non-empty).
|
||||
- Label: "Commit", Icon: `GitCommit`
|
||||
- Click dispatches `OpenCommitDialog`
|
||||
- Chevron visible
|
||||
|
||||
2. **Publish** — when there is no upstream tracking branch but there are local commits.
|
||||
- Label: "Publish", Icon: `UploadCloud`
|
||||
- Click dispatches `PublishBranch` (pushes and sets upstream in one step)
|
||||
- Chevron **hidden**
|
||||
|
||||
3. **Push** — when there are no uncommitted changes, branch has upstream, and there are unpushed commits.
|
||||
- Label: "Push", Icon: `ArrowUp`
|
||||
- Click dispatches `OpenPushDialog`
|
||||
- Chevron visible
|
||||
|
||||
4. **View PR** — when there is nothing to commit or push and a PR exists for the branch.
|
||||
- Label: "PR #N", Icon: `Github`
|
||||
- Click opens the PR URL in the browser
|
||||
- Chevron **hidden**
|
||||
|
||||
5. **Create PR** — when there is nothing to commit or push, branch has upstream, not on main, and no PR exists.
|
||||
- Label: "Create PR", Icon: `Github`
|
||||
- Click dispatches `OpenCreatePrDialog`
|
||||
- Chevron **hidden**
|
||||
|
||||
6. **Disabled Commit** (fallback) — when nothing is actionable (e.g. on main with no changes, or empty unpublished branch).
|
||||
- Label: "Commit", Icon: `GitCommit`, button disabled
|
||||
- Chevron **hidden**, no adjoined side (fully rounded)
|
||||
|
||||
### Dropdown menu
|
||||
|
||||
When the chevron is clicked, a dropdown menu appears anchored to the bottom-right of the button group. Menu items depend on the current mode:
|
||||
|
||||
**Commit mode:**
|
||||
- Commit (icon: GitCommit)
|
||||
- Commit and push (icon: ArrowUp)
|
||||
- Commit and create PR (icon: Github)
|
||||
|
||||
**Push mode:**
|
||||
- Commit (icon: GitCommit) — **disabled** (nothing to commit)
|
||||
- Push (icon: ArrowUp)
|
||||
- Create PR (icon: Github)
|
||||
|
||||
**Create PR / View PR / Publish / Disabled Commit modes:**
|
||||
- Chevron is hidden; dropdown is never shown.
|
||||
|
||||
The dropdown closes when an item is selected or when the user clicks away. The chevron button shows an active/pressed state while the dropdown is open.
|
||||
|
||||
### Overflow menu (three-dot button)
|
||||
|
||||
When the `GitOperationsInCodeReview` flag is enabled and there are no changes, the overflow menu is hidden entirely. All overflow menu items (Discard all, Add diff set as context, Add comment) are gated on having changes.
|
||||
|
||||
### State transitions
|
||||
|
||||
The button updates reactively:
|
||||
- When new git diffs arrive (`update_diff_state`), the mode is recomputed and the button label/icon/handler are updated.
|
||||
- When the diff stats change (`apply_diff_stats`), the mode is recomputed.
|
||||
- Transitions between modes are instant — no animation or intermediate states.
|
||||
|
||||
### Unpushed commits detection
|
||||
|
||||
To support the Push mode on new local branches (no upstream tracking branch), `get_unpushed_commits` falls back to comparing against the detected main branch (`main` or `master`) when `@{u}..HEAD` fails. This ensures the button correctly shows "Push" on a fresh branch with local commits.
|
||||
|
||||
### Feature flag
|
||||
|
||||
All git operations button UI is gated behind `FeatureFlag::GitOperationsInCodeReview`. When the flag is off, the button is not rendered and no new actions are dispatched.
|
||||
|
||||
## Success Criteria
|
||||
|
||||
1. When the code review panel shows uncommitted changes, the header displays a "Commit" button with a chevron dropdown.
|
||||
2. When there are no uncommitted changes but unpushed commits exist (with upstream), the button reads "Push" with a chevron dropdown.
|
||||
3. On a new local branch with no upstream but with local commits, the button shows "Publish".
|
||||
4. When everything is pushed and a PR exists, the button shows "PR #N".
|
||||
5. When everything is pushed, no PR exists, and not on main, the button shows "Create PR".
|
||||
6. On main with no changes, the button shows a disabled "Commit" with no chevron.
|
||||
7. The button transitions between modes automatically as the user commits or pushes (once dialogs are wired by child branches).
|
||||
8. The dropdown displays the correct items for each mode, with "Commit" disabled in Push mode.
|
||||
9. The button does not appear when `FeatureFlag::GitOperationsInCodeReview` is off.
|
||||
10. The overflow menu is hidden when there are no changes.
|
||||
|
||||
## Validation
|
||||
|
||||
- Manual verification: toggle the feature flag and confirm the button appears/disappears.
|
||||
- Modify a file in a repo, open the code review panel, and verify "Commit" is shown.
|
||||
- Commit the change (via terminal) and verify the button switches to "Push".
|
||||
- Push the branch and verify the button switches to "Create PR" with no chevron.
|
||||
- On a fresh local branch with commits, verify "Push" is shown (not "Create PR").
|
||||
- Open the chevron dropdown in each mode and verify correct menu items and disabled states.
|
||||
|
||||
## Open Questions
|
||||
|
||||
- Should compound actions ("Commit and push", "Commit and create PR") show a confirmation or progress indicator? (Deferred to dialog branches.)
|
||||
@@ -0,0 +1,140 @@
|
||||
# APP-3918: Git Operations Button — Tech Spec
|
||||
|
||||
Product spec: `specs/APP-3918/PRODUCT.md`
|
||||
Parent: APP-3632 (header UI refactor)
|
||||
|
||||
## Problem
|
||||
|
||||
The code review header refactor (APP-3632) cleared space for git operation buttons, but left them as a placeholder. This branch adds a context-aware split button that adapts its label, icon, and dropdown to the current repository state (uncommitted changes → commit, unpushed commits → push, otherwise → create PR).
|
||||
|
||||
## Relevant Code
|
||||
|
||||
- `app/src/code_review/code_review_view.rs:280-291` — `PrimaryGitActionMode` enum
|
||||
- `app/src/code_review/code_review_view.rs:6702-6806` — `primary_git_action_mode`, `update_git_operations_ui`, `git_operations_menu_items`
|
||||
- `app/src/code_review/code_review_view.rs:1308-1358` — button/menu construction in `new()`
|
||||
- `app/src/code_review/code_review_header/header_revamp.rs:102-135` — `render_git_operations_button`
|
||||
- `app/src/code_review/diff_state.rs:530-535` — `DiffStateModel::unpushed_commits()`
|
||||
- `app/src/util/git.rs:247-290` — `get_unpushed_commits` with main-branch fallback
|
||||
- `app/src/view_components/action_button.rs:296-321` — `AdjoinedSide`, `set_adjoined_side`, `clear_adjoined_side`
|
||||
|
||||
## Current State
|
||||
|
||||
The header refactor (APP-3632) restructured the code review header into two layers and moved contextual info to the right-panel header. The inner header now has space for action buttons on the right side. `DiffStateModel` already tracks `unpushed_commits` and provides `unpushed_commits()` accessor. Diff stats are available via the loaded state's `to_diff_stats()`.
|
||||
|
||||
However, `get_unpushed_commits` previously returned an empty vec for branches with no upstream tracking, which meant new local branches would incorrectly show "Create PR" instead of "Push".
|
||||
|
||||
## Changes
|
||||
|
||||
### 1. `PrimaryGitActionMode` enum
|
||||
|
||||
New private enum in `code_review_view.rs` with five variants: `Commit`, `Publish`, `Push`, `ViewPr`, `CreatePr`. Computed from:
|
||||
- `LoadedState::to_diff_stats().has_no_changes()` — uncommitted changes check
|
||||
- `DiffStateModel::has_upstream()` — upstream tracking branch check
|
||||
- `DiffStateModel::unpushed_commits().is_empty()` — unpushed commits check
|
||||
- `DiffStateModel::pr_info()` — existing PR check
|
||||
- `DiffStateModel::is_on_main_branch()` — main branch check
|
||||
|
||||
### 2. Split button in `CodeReviewView`
|
||||
|
||||
Four new fields on `CodeReviewView`:
|
||||
- `git_primary_action_button: ViewHandle<ActionButton>` — primary button (`SecondaryTheme`, `ButtonSize::Small`, `AdjoinedSide::Right`)
|
||||
- `git_operations_chevron: ViewHandle<ActionButton>` — chevron button (`AdjoinedSide::Left`)
|
||||
- `git_operations_menu: ViewHandle<Menu<CodeReviewAction>>` — dropdown menu
|
||||
- `git_operations_menu_open: bool` — menu state
|
||||
- `show_git_operations_chevron: bool` — controls chevron visibility (hidden in `CreatePr` mode)
|
||||
|
||||
Created once in `new()`. The menu subscribes to `MenuEvent::ItemSelected` / `MenuEvent::Close` to reset open state and chevron active state.
|
||||
|
||||
Passed to the header via six new fields on `CodeReviewHeaderFields`.
|
||||
|
||||
### 3. New `CodeReviewAction` variants
|
||||
|
||||
- `OpenCommitDialog`, `OpenPushDialog`, `OpenCreatePrDialog` — dispatch to dialogs (currently TODO stubs, wired in child branches)
|
||||
- `OpenGitOperationsMenu` — toggles the dropdown
|
||||
- `CommitAndPush`, `CommitAndCreatePr` — compound actions (stubs for now)
|
||||
- `ViewPr(String)` — opens the PR URL in the browser
|
||||
- `PublishBranch` — pushes and sets upstream (stub for now)
|
||||
|
||||
### 4. Reactive state updates
|
||||
|
||||
`update_git_operations_ui` is called from two sites:
|
||||
- `update_aggregate_stats` (line 2777) — when diff stats change
|
||||
- `invalidate_all` (line 2872) — when a full diff reload completes
|
||||
|
||||
It recomputes `primary_git_action_mode` and updates the button's label, icon, click handler, and adjoined-side. In `CreatePr` mode, the adjoined side is cleared via `clear_adjoined_side` and the chevron is hidden.
|
||||
|
||||
### 5. Header rendering
|
||||
|
||||
`render_git_operations_button` in `header_revamp.rs` builds a `Stack` with:
|
||||
- A `Flex::row` of primary button + optional chevron
|
||||
- A positioned overlay for the dropdown menu (anchored `BottomRight → TopRight`)
|
||||
|
||||
Gated on `CodeReviewHeaderFields::show_git_operations` (which is `FeatureFlag::GitOperationsInCodeReview.is_enabled()`).
|
||||
|
||||
### 6. `get_unpushed_commits` fallback
|
||||
|
||||
When `git log @{u}..HEAD` fails with "no upstream configured" or "unknown revision", now falls back to `detect_main_branch` and runs `git log {main_branch}..HEAD`. This ensures new local branches correctly report their commits as "unpushed". Extracted `parse_commit_log` helper to avoid duplication.
|
||||
|
||||
### 7. `has_upstream` detection
|
||||
|
||||
New `has_upstream: bool` field on `DiffMetadata`, computed during `load_metadata_for_repo` via `git rev-parse --abbrev-ref --symbolic-full-name @{u}`. Exposed as `DiffStateModel::has_upstream()`. Used by `primary_git_action_mode` to distinguish Publish (no upstream) from Push (has upstream) and to gate Create PR (only with upstream).
|
||||
|
||||
### 8. PR info refresh on metadata updates
|
||||
|
||||
Previously `refresh_pr_info` only ran on branch change. Now also runs during throttled metadata refreshes when `pr_info` is `None`, so the button updates to "PR #N" after an external push or PR creation without requiring a branch switch.
|
||||
|
||||
### 9. Overflow menu gated on changes
|
||||
|
||||
When the git operations flag is enabled, `has_header_menu_items` now requires actual changes to be present. The "Add comment" item is also gated on `has_changes`. This hides the three-dot overflow menu entirely when there are no changes.
|
||||
|
||||
### 10. New `UploadCloud` icon
|
||||
|
||||
Added `app/assets/bundled/svg/upload-cloud-01.svg` and registered as `Icon::UploadCloud` for the Publish button.
|
||||
|
||||
### 11. New `GitCommit` icon
|
||||
|
||||
Added `app/assets/bundled/svg/git-commit.svg` (a 24×24 SVG with `fill="#FF0000"` per the existing icon convention) and registered it as `Icon::GitCommit` in `crates/warp_core/src/ui/icons.rs`. This replaces `Icon::GitBranch` for the Commit action — a git-branch icon is semantically incorrect for a commit operation.
|
||||
|
||||
### 8. `ActionButton::clear_adjoined_side`
|
||||
|
||||
New public method to remove the adjoined-side styling at runtime, used when transitioning to `CreatePr` mode (standalone button, no chevron).
|
||||
|
||||
## End-to-End Flow
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[Diff stats change or full reload] --> B[update_git_operations_ui]
|
||||
B --> C{has uncommitted changes?}
|
||||
C -->|yes| D["Commit mode: label=Commit, icon=GitCommit, chevron=visible"]
|
||||
C -->|no| E{has upstream?}
|
||||
E -->|no, has commits| F["Publish mode: label=Publish, icon=UploadCloud, chevron=hidden"]
|
||||
E -->|yes| G{unpushed commits?}
|
||||
G -->|yes| H["Push mode: label=Push, icon=ArrowUp, chevron=visible"]
|
||||
G -->|no| I{PR exists?}
|
||||
I -->|yes| J["ViewPr mode: label=PR #N, icon=Github, chevron=hidden"]
|
||||
I -->|no, not main| K["CreatePr mode: label=Create PR, icon=Github, chevron=hidden"]
|
||||
I -->|no, on main| L["Disabled Commit: label=Commit, disabled, no chevron"]
|
||||
E -->|no, no commits| L
|
||||
```
|
||||
|
||||
## Risks and Mitigations
|
||||
|
||||
- **Stale button state**: If diff stats and unpushed-commit data get out of sync, the button could show the wrong mode. Mitigated by recomputing on every diff reload and stats update.
|
||||
- **Fallback branch detection**: `detect_main_branch` may fail in unusual repo setups (bare repos, orphan branches). Falls back to empty vec (shows "Create PR"), which is a reasonable degraded state.
|
||||
- **Action stubs**: The dialog actions are TODO stubs. If child branches aren't merged alongside this one, clicking the buttons does nothing. Acceptable because the feature is behind a flag that's off by default.
|
||||
|
||||
## Testing and Validation
|
||||
|
||||
- Manual: enable feature flag, modify files, and step through Commit → Push → Create PR states by committing and pushing from the terminal.
|
||||
- Verify dropdown items and disabled states in each mode.
|
||||
- Test on a new local branch with no upstream to confirm "Push" is shown.
|
||||
- Verify the flag-off path renders no git operations button.
|
||||
|
||||
## Follow-ups
|
||||
|
||||
- Wire `OpenCommitDialog`, `OpenPushDialog`, `OpenCreatePrDialog` to actual dialogs (child branches in stack).
|
||||
- Wire `CommitAndPush` and `CommitAndCreatePr` compound actions.
|
||||
- Wire `PublishBranch` to `git push --set-upstream origin <branch>`.
|
||||
- Add integration tests for mode transitions once dialogs are functional.
|
||||
- Consider progress/error UI for push and PR creation flows.
|
||||
- Handle the "no remote at all" edge case (currently treated same as no upstream).
|
||||
Reference in New Issue
Block a user