feat(staging): per-line selection + Cmd+S shortcut#9
Merged
poolcamacho merged 3 commits intomasterfrom Apr 20, 2026
Merged
Conversation
Stage 4 of the interactive-staging plan. Replaces the whole-hunk-only selection model with per-line selection, falling back to whole-hunk behaviour when every modifiable line in a hunk is picked. - AppState.selectedHunks (Set<Int>) becomes selectedLines ([Int: Set<Int>]) keyed by hunk index, valued by line indices within that hunk's lines array. One model covers both hunk-whole and partial selections. - DiffFile.patchText(forLines:) walks each selected hunk and rewrites the body: unselected + lines are dropped, unselected - lines are demoted to context so the line survives the partial patch, and the hunk header's oldCount / newCount are recomputed. oldStart / newStart stay anchored to their original positions. - GitCoordinator.stageSelectedLines / unstageSelectedLines replace the previous hunk-only helpers and pipe the rewritten patch to git apply --cached (with --reverse for unstaging). - DiffView: every + / - DiffLineView gains an optional selection binding and a checkbox. Context rows reserve the slot so columns stay aligned. The @@ header checkbox still toggles the whole hunk by writing all-or-nothing into selectedLines. - HunkStagingToolbar: Select all now targets every modifiable line across all hunks; the counter reports "X of Y lines" and the Stage / Unstage button flips based on the selected file's isStaged.
Adds four shields.io badges next to the existing CI / Platform / Swift / License / Sponsors row so the repo page surfaces engagement signals (stargazers, forks, open issues) and freshness (last commit) at a glance.
Binds Cmd+S to the visible Stage / Unstage selected button in the changes diff toolbar. The shortcut fires whichever action is active given the selected file's staged state. Also promotes interactive staging — line level to Done in the README roadmap, removes it from Next, and trims Cmd+S from the pending keyboard-shortcuts bullet since it ships here.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Line-level interactive staging. The checkbox on each
@@header still stages the whole hunk, but now every individual+/-line has its own checkbox too — pick exactly which additions and deletions go into the index.Cmd+Sstages (or unstages, depending on the view) the current selection without leaving the diff.What changed
Stage 4 — per-line selection (
d2a0576)AppState.selectedHunks: Set<Int>becomesselectedLines: [Int: Set<Int>], keyed by hunk index with values pointing at line indices inside that hunk'slinesarray. A hunk with every modifiable line selected collapses to the same patch the hunk-only path produced.DiffFile.patchText(forLines:)walks each selected hunk and rewrites the body:+lines are dropped — they don't exist in the old side and we don't want them in the new side either.-lines are demoted to contextso the line survives the partial patch.oldCount/newCountare recomputed from the rewritten body.oldStart/newStartstay anchored.rewriteHunk+emitsplit out so cyclomatic complexity stays under the linter limit.GitCoordinator.stageSelectedLines/unstageSelectedLinesreplace the previous hunk-only methods and pipe the rewritten patch togit apply --cached(with--reversewhen unstaging).DiffView: every+/-DiffLineViewgains an optionalisSelectedbinding with a checkbox. Context rows reserve the slot so columns stay aligned. The@@header checkbox still toggles the whole hunk by writing all-or-nothing intoselectedLines.HunkStagingToolbar: Select all now targets every modifiable line across all hunks; the counter reportsX of Y lines.Cmd+S shortcut + docs (
0032212).keyboardShortcut("s", modifiers: .command)on both Stage selected and Unstage selected buttons in the toolbar. The shortcut fires whichever action is active given the selected file's staged state.Engagement badges (
5b5134d)shields.io/github/stars|forks|issues|last-commitbadges alongside the existing CI / Platform / Swift / License / Sponsors row.Not in this PR
git checkout -pequivalent). Destructive; deserves its own review.Cmd+Entercommit and command palette — separate follow-up.Test plan
+line in one hunk →Cmd+S→git diff --cachedshows only that line;git diffstill shows the rest-and+, select only the+→ stage → the-does NOT land in the index (demoted to context) and the file patches cleanly@@header checkbox still selects every modifiable line in that hunk; toolbar Select all selects every line across all hunksCmd+S→ those lines leave the index, rest stays stagedxcodebuildclean,swiftlint --strictclean