opencode-git-plugin 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,23 +1,24 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "opencode-git-plugin",
4
- "version": "0.2.0",
4
+ "version": "0.2.1",
5
5
  "description": "OpenCode v2 TUI sidebar plugin listing git working-copy changes with diff preview",
6
6
  "type": "module",
7
7
  "exports": {
8
- "./tui": "./tui.ts"
8
+ "./tui": "./dist/tui.js"
9
9
  },
10
10
  "files": [
11
- "tui.ts",
12
- "src",
11
+ "dist",
13
12
  "docs",
14
13
  "README.md",
15
14
  "LICENSE"
16
15
  ],
17
16
  "scripts": {
17
+ "build": "bun run scripts/build.ts",
18
18
  "typecheck": "tsc --noEmit",
19
19
  "test": "bun test",
20
20
  "check": "bun run typecheck && bun test",
21
+ "prepack": "bun run build",
21
22
  "prepublishOnly": "bun run check"
22
23
  },
23
24
  "dependencies": {
@@ -1,16 +0,0 @@
1
- export const DEFAULT_INTERVAL_MS = 4000
2
- export const MIN_INTERVAL_MS = 1000
3
- export const MAX_DIFF_LINES = 4000
4
- export const GIT_TIMEOUT_MS = 120_000
5
- export const GIT_POLL_MS = 50
6
-
7
- export const DIFF_STORE_KEY = "git-panel.diff-view"
8
- export const DIFF_PANEL_NAME = "git-panel.diff"
9
- export const REFRESH_STORE_KEY = "git-panel.refresh"
10
- export const SETTINGS_STORE_KEY = "git-panel.settings"
11
- export const STAGED_STORE_KEY = "git-panel.staged"
12
-
13
- export const LIST_NAV_MODE = "git-panel-list"
14
-
15
- export const FILE_ROW_BADGE_WIDTH = 3
16
- export const EVENT_REFRESH_DEBOUNCE_MS = 2000
@@ -1,2 +0,0 @@
1
- export * from "./constants"
2
- export * from "./options"
@@ -1,8 +0,0 @@
1
- import type { Context } from "@opencode/plugin/tui/context"
2
- import { DEFAULT_INTERVAL_MS, MIN_INTERVAL_MS } from "./constants"
3
-
4
- export function intervalMs(ctx: Context): number {
5
- const raw = ctx.options.intervalMs
6
- if (typeof raw !== "number" || !Number.isFinite(raw)) return DEFAULT_INTERVAL_MS
7
- return Math.max(MIN_INTERVAL_MS, Math.floor(raw))
8
- }
package/src/diff/body.tsx DELETED
@@ -1,90 +0,0 @@
1
- /** @jsxImportSource @opentui/solid */
2
- import { For, Show } from "solid-js"
3
- import type { generateSyntax } from "@opencode/theme/tui"
4
- import { diffLinePrefix, diffLineStyle, syntaxFiletype } from "../ui"
5
- import type { DiffRow, DiffViewState, ScrollHandle, Theme } from "../types"
6
-
7
- function DiffLineRow(props: { theme: Theme; row: DiffRow; index: number }) {
8
- const theme = props.theme
9
- const row = props.row
10
- const style = diffLineStyle(theme, row.kind)
11
- const isCurrent = row.state === "current"
12
- const gutter = row.line !== undefined ? String(row.line).padStart(4, " ") : " "
13
- const fg = isCurrent
14
- ? theme.diff.background.context
15
- : row.state === "hit"
16
- ? theme.text.feedback.warning.base
17
- : style.fg
18
- const bg = isCurrent ? theme.text.feedback.warning.base : style.bg
19
- return (
20
- <box flexDirection="row" width="100%" id={`git-panel-diff-line-${props.index}`}>
21
- <text fg={isCurrent ? theme.text.feedback.warning.base : theme.text.muted} width={5}>
22
- {isCurrent ? `❯${gutter.slice(1)}` : `${gutter} `}
23
- </text>
24
- <text fg={fg} bg={bg} wrapMode="word" flexGrow={1}>
25
- {diffLinePrefix(row.kind)}
26
- {row.text}
27
- </text>
28
- </box>
29
- )
30
- }
31
-
32
- export function DiffBody(props: {
33
- theme: Theme
34
- state: DiffViewState
35
- rows: DiffRow[]
36
- focused: boolean
37
- syntaxActive: boolean
38
- syntaxStyle: ReturnType<typeof generateSyntax> | undefined
39
- onScrollRef: (el: ScrollHandle) => void
40
- }) {
41
- const theme = props.theme
42
- return (
43
- <>
44
- <Show when={props.state.loading}>
45
- <text fg={theme.text.muted}>Loading diff…</text>
46
- </Show>
47
- <Show when={!props.state.loading && props.state.message}>
48
- <text fg={theme.text.muted}>{props.state.message}</text>
49
- </Show>
50
- <Show when={!props.state.loading && !props.state.message}>
51
- <scrollbox
52
- width="100%"
53
- flexGrow={1}
54
- flexShrink={1}
55
- minHeight={0}
56
- scrollY
57
- scrollX={false}
58
- focused={props.focused}
59
- ref={(el) => props.onScrollRef(el)}
60
- contentOptions={{ flexDirection: "column", gap: 0 }}
61
- wrapperOptions={{ paddingRight: 1 }}
62
- >
63
- <Show when={props.syntaxActive}>
64
- <diff
65
- diff={props.state.patch}
66
- view="unified"
67
- filetype={syntaxFiletype(props.state.file)}
68
- syntaxStyle={props.syntaxStyle}
69
- showLineNumbers
70
- wrapMode="word"
71
- fg={theme.text.base}
72
- addedBg={theme.diff.background.added}
73
- removedBg={theme.diff.background.removed}
74
- contextBg={theme.diff.background.context}
75
- addedSignColor={theme.diff.text.added}
76
- removedSignColor={theme.diff.text.removed}
77
- lineNumberFg={theme.text.muted}
78
- />
79
- </Show>
80
- <Show when={!props.syntaxActive}>
81
- <For each={props.rows}>{(row, i) => <DiffLineRow theme={theme} row={row} index={i()} />}</For>
82
- </Show>
83
- <Show when={props.state.truncated}>
84
- <text fg={theme.text.muted}>… truncated</text>
85
- </Show>
86
- </scrollbox>
87
- </Show>
88
- </>
89
- )
90
- }
@@ -1,77 +0,0 @@
1
- /** @jsxImportSource @opentui/solid */
2
- import { Show } from "solid-js"
3
- import { displayWidth, maxTextWidth, truncatePath } from "../ui"
4
- import type { Theme } from "../types"
5
-
6
- export function DiffHeader(props: {
7
- theme: Theme
8
- width: number
9
- file: string
10
- additions: number
11
- deletions: number
12
- query: string
13
- matchCursor: number
14
- matchCount: number
15
- syntax: boolean
16
- onSearch: () => void
17
- onToggleSyntax: () => void
18
- onClose: () => void
19
- }) {
20
- const theme = props.theme
21
- return (
22
- <box flexDirection="column" gap={0} flexShrink={0}>
23
- <box flexDirection="row" width={Math.max(1, props.width)} flexShrink={0}>
24
- <text fg={theme.text.base} wrapMode="none" flexShrink={0}>
25
- <b>
26
- {truncatePath(
27
- props.file,
28
- maxTextWidth(
29
- props.width,
30
- displayWidth(`+${props.additions}`) + 1 + displayWidth(`-${props.deletions}`) + 1,
31
- ),
32
- )}
33
- </b>
34
- </text>
35
- <box flexGrow={1} />
36
- <box flexDirection="row" gap={1} flexShrink={0}>
37
- <text fg={theme.diff.text.added} wrapMode="none" flexShrink={0}>
38
- +{props.additions}
39
- </text>
40
- <text fg={theme.diff.text.removed} wrapMode="none" flexShrink={0}>
41
- -{props.deletions}
42
- </text>
43
- </box>
44
- </box>
45
- <Show when={props.query}>
46
- <text fg={theme.text.feedback.warning.base} wrapMode="none">
47
- {props.matchCount > 0 ? `${props.matchCursor + 1}/${props.matchCount}` : "0/0"} · {props.query}
48
- </text>
49
- </Show>
50
- <box height={1} flexShrink={0} />
51
- <box flexDirection="row" flexWrap="wrap" width={Math.max(1, props.width)} gap={1} flexShrink={0}>
52
- <text fg={theme.text.muted} wrapMode="none" flexShrink={0} onMouseUp={() => props.onSearch()}>
53
- [? : Search]
54
- </text>
55
- <Show when={props.query}>
56
- <text fg={theme.text.muted} wrapMode="none" flexShrink={0}>
57
- [↑↓ : Jump]
58
- </text>
59
- </Show>
60
- <text
61
- fg={props.syntax ? theme.text.feedback.warning.base : theme.text.muted}
62
- wrapMode="none"
63
- flexShrink={0}
64
- onMouseUp={() => props.onToggleSyntax()}
65
- >
66
- {props.syntax ? "[syntax:on]" : "[syntax:off]"}
67
- </text>
68
- <text fg={theme.text.base} wrapMode="none" flexShrink={0} onMouseUp={() => props.onClose()}>
69
- [ Close · ESC/⌫ ]
70
- </text>
71
- </box>
72
- <text fg={theme.text.muted} wrapMode="none">
73
- {"─".repeat(Math.max(1, props.width))}
74
- </text>
75
- </box>
76
- )
77
- }
package/src/diff/index.ts DELETED
@@ -1,2 +0,0 @@
1
- export { createDiffOpener } from "./open-diff"
2
- export { DiffPanel } from "./panel"
@@ -1,113 +0,0 @@
1
- import type { Context, PanelInput } from "@opencode/plugin/tui/context"
2
- import { DIFF_PANEL_NAME } from "../config"
3
-
4
- export function registerDiffKeymap(
5
- ctx: Context,
6
- deps: {
7
- input: () => PanelInput
8
- searching: () => boolean
9
- hasMatches: () => boolean
10
- syntaxActive: () => boolean
11
- search(): void
12
- stepMatch(dir: 1 | -1): void
13
- clearSearch(): void
14
- close(): void
15
- toggleSyntax(): void
16
- scrollBy(delta: number): void
17
- },
18
- ): void {
19
- ctx.keymap.layer(() => {
20
- const searching = deps.searching()
21
- const hasMatches = deps.hasMatches()
22
- const inSyntax = deps.syntaxActive()
23
- return {
24
- mode: "global",
25
- priority: 20,
26
- enabled: () => deps.input().name === DIFF_PANEL_NAME,
27
- bindings: [
28
- "git-panel.diff-search",
29
- "git-panel.diff-next-match",
30
- "git-panel.diff-prev-match",
31
- "git-panel.diff-clear-search",
32
- "git-panel.close-diff",
33
- "git-panel.close-diff-backspace",
34
- "git-panel.toggle-syntax",
35
- "git-panel.syntax-down",
36
- "git-panel.syntax-up",
37
- ],
38
- commands: [
39
- {
40
- id: "git-panel.diff-search",
41
- title: "Git Panel: Search in Diff",
42
- group: "Git Panel",
43
- bind: "?",
44
- run: () => deps.search(),
45
- },
46
- {
47
- id: "git-panel.diff-next-match",
48
- title: "Git Panel: Next Diff Match",
49
- group: "Git Panel",
50
- bind: "down",
51
- enabled: searching && hasMatches,
52
- run: () => deps.stepMatch(1),
53
- },
54
- {
55
- id: "git-panel.diff-prev-match",
56
- title: "Git Panel: Previous Diff Match",
57
- group: "Git Panel",
58
- bind: "up",
59
- enabled: searching && hasMatches,
60
- run: () => deps.stepMatch(-1),
61
- },
62
- {
63
- id: "git-panel.diff-clear-search",
64
- title: "Git Panel: Clear Diff Search",
65
- group: "Git Panel",
66
- bind: "escape",
67
- enabled: searching,
68
- run: () => deps.clearSearch(),
69
- },
70
- {
71
- id: "git-panel.close-diff",
72
- title: "Git Panel: Close Diff",
73
- group: "Git Panel",
74
- bind: "escape",
75
- enabled: !searching,
76
- run: () => deps.close(),
77
- },
78
- {
79
- id: "git-panel.close-diff-backspace",
80
- title: "Git Panel: Close Diff (Backspace)",
81
- group: "Git Panel",
82
- bind: "backspace",
83
- enabled: !searching,
84
- run: () => deps.close(),
85
- },
86
- {
87
- id: "git-panel.toggle-syntax",
88
- title: "Git Panel: Toggle Diff Syntax Highlighting",
89
- group: "Git Panel",
90
- bind: "s",
91
- palette: true,
92
- run: () => deps.toggleSyntax(),
93
- },
94
- {
95
- id: "git-panel.syntax-down",
96
- title: "Git Panel: Scroll Syntax Diff Down",
97
- group: "Git Panel",
98
- bind: "j",
99
- enabled: inSyntax,
100
- run: () => deps.scrollBy(3),
101
- },
102
- {
103
- id: "git-panel.syntax-up",
104
- title: "Git Panel: Scroll Syntax Diff Up",
105
- group: "Git Panel",
106
- bind: "k",
107
- enabled: inSyntax,
108
- run: () => deps.scrollBy(-3),
109
- },
110
- ],
111
- }
112
- })
113
- }
@@ -1,57 +0,0 @@
1
- import type { Context } from "@opencode/plugin/tui/context"
2
- import { DIFF_PANEL_NAME, MAX_DIFF_LINES } from "../config"
3
- import { parseDiff } from "../git"
4
- import { useDiffStore } from "../state"
5
- import { friendlyError } from "../ui"
6
- import type { ChangeFile } from "../types"
7
-
8
- export function createDiffOpener(ctx: Context, location: () => { directory: string }) {
9
- const [diffView, setDiffState] = useDiffStore(ctx)
10
- let activeRequestId = 0
11
-
12
- return async (f: ChangeFile) => {
13
- const requestId = ++activeRequestId
14
- const isStale = () => requestId !== activeRequestId || diffView.file !== f.file || !diffView.panelOpen
15
-
16
- setDiffState((draft) => {
17
- draft.file = f.file
18
- draft.additions = f.additions
19
- draft.deletions = f.deletions
20
- draft.lines = []
21
- draft.patch = ""
22
- draft.message = ""
23
- draft.truncated = false
24
- draft.loading = true
25
- draft.query = ""
26
- draft.matchCursor = 0
27
- draft.panelOpen = true
28
- })
29
- ctx.ui.panel.open(DIFF_PANEL_NAME, { presentation: "panel" })
30
- try {
31
- const res = await ctx.client.vcs.diff({ location: location(), mode: "working", context: 3 })
32
- if (isStale()) return
33
- const entry = res.data.find((d: { file: string }) => d.file === f.file)
34
- const parsed = entry ? parseDiff(entry.patch) : []
35
- setDiffState((draft) => {
36
- draft.loading = false
37
- draft.patch = entry?.patch ?? ""
38
- if (!entry) {
39
- draft.message = "No diff available"
40
- } else if (parsed.length === 0) {
41
- draft.message = "No visible changes (empty or binary file)"
42
- } else if (parsed.length > MAX_DIFF_LINES) {
43
- draft.lines = parsed.slice(0, MAX_DIFF_LINES)
44
- draft.truncated = true
45
- } else {
46
- draft.lines = parsed
47
- }
48
- })
49
- } catch (e) {
50
- if (isStale()) return
51
- setDiffState((draft) => {
52
- draft.loading = false
53
- draft.message = friendlyError(e)
54
- })
55
- }
56
- }
57
- }
@@ -1,156 +0,0 @@
1
- /** @jsxImportSource @opentui/solid */
2
- import { createEffect, createMemo, Show } from "solid-js"
3
- import { generateSyntax } from "@opencode/theme/tui"
4
- import type { Context, PanelInput } from "@opencode/plugin/tui/context"
5
- import { DIFF_PANEL_NAME, SETTINGS_STORE_KEY } from "../config"
6
- import { notifyDiffClosed, useDiffStore } from "../state"
7
- import { friendlyError } from "../ui"
8
- import type { ScrollHandle } from "../types"
9
- import { DiffBody } from "./body"
10
- import { DiffHeader } from "./header"
11
- import { registerDiffKeymap } from "./keymap"
12
- import { createDiffSearch } from "./search"
13
-
14
- export function DiffPanel(props: { ctx: Context; input: PanelInput }) {
15
- const ctx = props.ctx
16
- const input = () => props.input
17
- const theme = ctx.theme
18
- const [diffState, setDiffState] = useDiffStore(ctx)
19
- const [settings, setSettings] = ctx.storage.store(SETTINGS_STORE_KEY, {
20
- initial: { syntax: false },
21
- })
22
- const toggleSyntax = () => {
23
- void setSettings((draft) => {
24
- draft.syntax = !draft.syntax
25
- }).catch(() => {})
26
- }
27
- const isSyntaxViewActive = () =>
28
- settings.syntax && !diffState.truncated && diffState.query === "" && diffState.patch !== ""
29
- const syntaxHighlightStyle = createMemo(() => (settings.syntax ? generateSyntax(theme) : undefined))
30
- const search = createDiffSearch(diffState)
31
-
32
- createEffect(() => {
33
- if (input().name !== DIFF_PANEL_NAME && diffState.panelOpen) {
34
- setDiffState((draft) => {
35
- draft.panelOpen = false
36
- })
37
- }
38
- })
39
-
40
- createEffect(() => {
41
- if (input().name === DIFF_PANEL_NAME && !input().focused) {
42
- try {
43
- input().focus()
44
- } catch {
45
- }
46
- }
47
- })
48
-
49
- let scrollEl: ScrollHandle | undefined
50
-
51
- const safeScrollTo = (y: number) => {
52
- const el = scrollEl
53
- if (!el) return
54
- try {
55
- el.scrollTo({ x: 0, y: Math.max(0, y) })
56
- } catch {
57
- }
58
- }
59
-
60
- createEffect(() => {
61
- const line = search.currentMatchIndex()
62
- if (line < 0 || input().name !== DIFF_PANEL_NAME || !scrollEl) return
63
- const target = Math.max(0, line - 10)
64
- setTimeout(() => safeScrollTo(target), 50)
65
- })
66
-
67
- const scrollSyntaxBy = (delta: number) => {
68
- if (!scrollEl) return
69
- safeScrollTo(scrollEl.scrollTop + delta)
70
- }
71
-
72
- const stepSearchMatch = (dir: 1 | -1) => {
73
- const m = search.matchIndices()
74
- if (m.length === 0) return
75
- setDiffState((draft) => {
76
- draft.matchCursor = (draft.matchCursor + dir + m.length) % m.length
77
- })
78
- }
79
-
80
- const clearSearch = () => {
81
- setDiffState((draft) => {
82
- draft.query = ""
83
- draft.matchCursor = 0
84
- })
85
- }
86
-
87
- const closeDiff = () => {
88
- setDiffState((draft) => {
89
- draft.panelOpen = false
90
- })
91
- ctx.ui.panel.close()
92
- notifyDiffClosed()
93
- }
94
-
95
- const promptSearchQuery = async () => {
96
- try {
97
- const q = await ctx.ui.dialog.prompt({
98
- title: "Search in diff",
99
- placeholder: "Type to highlight matches",
100
- value: diffState.query,
101
- })
102
- if (q === undefined) return
103
- setDiffState((draft) => {
104
- draft.query = q
105
- draft.matchCursor = 0
106
- })
107
- } catch (e) {
108
- ctx.ui.toast.show({ message: `Search failed: ${friendlyError(e)}`, variant: "error" })
109
- }
110
- }
111
-
112
- registerDiffKeymap(ctx, {
113
- input,
114
- searching: () => diffState.query.length > 0,
115
- hasMatches: () => search.matchIndices().length > 0,
116
- syntaxActive: isSyntaxViewActive,
117
- search: () => void promptSearchQuery(),
118
- stepMatch: stepSearchMatch,
119
- clearSearch,
120
- close: closeDiff,
121
- toggleSyntax,
122
- scrollBy: scrollSyntaxBy,
123
- })
124
-
125
- return (
126
- <Show when={input().name === DIFF_PANEL_NAME}>
127
- <box flexDirection="column" gap={1} width="100%" height="100%">
128
- <DiffHeader
129
- theme={theme}
130
- width={input().width}
131
- file={diffState.file}
132
- additions={diffState.additions}
133
- deletions={diffState.deletions}
134
- query={diffState.query}
135
- matchCursor={diffState.matchCursor}
136
- matchCount={search.matchIndices().length}
137
- syntax={settings.syntax}
138
- onSearch={() => void promptSearchQuery()}
139
- onToggleSyntax={toggleSyntax}
140
- onClose={closeDiff}
141
- />
142
- <DiffBody
143
- theme={theme}
144
- state={diffState}
145
- rows={search.visibleRows()}
146
- focused={input().focused}
147
- syntaxActive={isSyntaxViewActive()}
148
- syntaxStyle={syntaxHighlightStyle()}
149
- onScrollRef={(el) => {
150
- scrollEl = el
151
- }}
152
- />
153
- </box>
154
- </Show>
155
- )
156
- }
@@ -1,38 +0,0 @@
1
- import { createMemo } from "solid-js"
2
- import type { DiffRow, DiffViewState } from "../types"
3
-
4
- export function createDiffSearch(state: DiffViewState) {
5
- const matchIndices = createMemo(() => {
6
- const q = state.query.toLowerCase()
7
- if (!q) return []
8
- const out: number[] = []
9
- state.lines.forEach((line, i) => {
10
- if (line.text.toLowerCase().includes(q)) out.push(i)
11
- })
12
- return out
13
- })
14
-
15
- const currentMatchIndex = createMemo(() => {
16
- const m = matchIndices()
17
- if (m.length === 0) return -1
18
- return m[((state.matchCursor % m.length) + m.length) % m.length]
19
- })
20
-
21
- let previousRows: DiffRow[] = []
22
- const visibleRows = createMemo(() => {
23
- const q = state.query.toLowerCase()
24
- const cur = currentMatchIndex()
25
- const prev = previousRows
26
- const next = state.lines.map((line, idx) => {
27
- const hit = q !== "" && line.text.toLowerCase().includes(q)
28
- const rowState = (idx === cur ? "current" : hit ? "hit" : "plain") as DiffRow["state"]
29
- const p = prev[idx]
30
- if (p && p.kind === line.kind && p.text === line.text && p.line === line.line && p.state === rowState) return p
31
- return { kind: line.kind, text: line.text, line: line.line, state: rowState }
32
- })
33
- previousRows = next
34
- return next
35
- })
36
-
37
- return { matchIndices, currentMatchIndex, visibleRows }
38
- }
@@ -1,35 +0,0 @@
1
- import type { DiffLine } from "../types"
2
-
3
- const DIFF_PREAMBLE_RE = /^(diff --git |index |new file mode|deleted file mode|similarity index|rename from|rename to|old mode|new mode)/
4
- const HUNK_HEADER_RE = /^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@ ?(.*)$/
5
-
6
- export function formatHunkHeader(raw: string): string {
7
- const m = raw.match(HUNK_HEADER_RE)
8
- if (!m) return raw
9
- const [, newStart, trailingContext] = m
10
- return trailingContext ? `⋯ ${trailingContext}` : `⋯ line ${newStart}`
11
- }
12
-
13
- export function parseDiff(patch: string): DiffLine[] {
14
- const out: DiffLine[] = []
15
- let newLine = 0
16
- for (const line of patch.split("\n")) {
17
- if (line === "") continue
18
- if (line.startsWith("@@")) {
19
- const m = line.match(HUNK_HEADER_RE)
20
- if (m) newLine = parseInt(m[1], 10)
21
- out.push({ kind: "hunk", text: formatHunkHeader(line) })
22
- } else if (line.startsWith("+++") || line.startsWith("---")) {
23
- } else if (DIFF_PREAMBLE_RE.test(line)) {
24
- } else if (line.startsWith("Binary files")) {
25
- out.push({ kind: "meta", text: line })
26
- } else if (line.startsWith("+")) {
27
- out.push({ kind: "add", text: line.slice(1), line: newLine++ })
28
- } else if (line.startsWith("-")) {
29
- out.push({ kind: "del", text: line.slice(1) })
30
- } else {
31
- out.push({ kind: "context", text: line.startsWith(" ") ? line.slice(1) : line, line: newLine++ })
32
- }
33
- }
34
- return out
35
- }
package/src/git/index.ts DELETED
@@ -1,3 +0,0 @@
1
- export * from "./diff-parse"
2
- export * from "./porcelain"
3
- export * from "./runner"
@@ -1,10 +0,0 @@
1
- export function parseStagedPorcelain(porcelain: string): Set<string> {
2
- const staged = new Set<string>()
3
- for (const record of porcelain.split("\0")) {
4
- if (record.length < 4) continue
5
- const indexStatus = record[0]
6
- if (indexStatus === " " || indexStatus === "?") continue
7
- staged.add(record.slice(3))
8
- }
9
- return staged
10
- }