@alecsibilia/luca 14.0.0-alpha.2 → 14.0.0-alpha.21
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/CHANGELOG.md +76 -0
- package/README.md +7 -2
- package/board/client/board-look.ts +31 -1
- package/board/client/board-panel-parts.tsx +150 -0
- package/board/client/board-panel-stages.ts +99 -0
- package/board/client/board-panel-styles.ts +223 -0
- package/board/client/board-panel.tsx +77 -1275
- package/board/client/engine-life.ts +48 -0
- package/board/client/final-review-stack.tsx +144 -0
- package/board/client/heartbeat.tsx +61 -0
- package/board/client/helper-lines.tsx +69 -0
- package/board/client/live-feed.tsx +86 -0
- package/board/client/live-line.ts +73 -0
- package/board/client/model-line.tsx +178 -0
- package/board/client/needs-you-section.tsx +133 -0
- package/board/client/one-line.tsx +193 -0
- package/board/client/repo-wait-banner.tsx +69 -0
- package/board/client/run-footer.tsx +68 -0
- package/board/client/run-header.tsx +189 -0
- package/board/client/run-picker.tsx +98 -0
- package/board/client/status-bar.tsx +131 -0
- package/board/client/step-dots.tsx +127 -0
- package/board/client/stuck-actions.tsx +269 -0
- package/board/client/ticket-card-view.tsx +238 -0
- package/board/client/ticket-detail.tsx +87 -0
- package/board/client/ticket-stack.tsx +119 -0
- package/board/client/ticket-wait.ts +188 -0
- package/board/client/timeline-rows.tsx +41 -14
- package/board/client/usage-line.tsx +51 -0
- package/board/client/wait-line.tsx +39 -0
- package/board/index.client.tsx +1 -1
- package/board/index.server.ts +7 -1
- package/board/package.json +1 -1
- package/board/server/board-panel-state-helpers.ts +67 -0
- package/board/server/board-server-check.ts +294 -0
- package/board/server/board-server-engine.ts +129 -0
- package/board/server/board-server-events.ts +134 -0
- package/board/server/board-server-read.ts +190 -0
- package/board/server/board-server-resume.ts +164 -0
- package/board/server/board-server-start.ts +174 -0
- package/board/server/board-server-store.ts +281 -0
- package/board/server/board-server-types.ts +114 -0
- package/board/server/board-server-util.ts +4 -0
- package/board/server/board-server.ts +52 -943
- package/board/server/board-vocabulary-engine.ts +144 -0
- package/board/server/board-vocabulary-review.ts +115 -0
- package/board/server/board-vocabulary-run.ts +145 -0
- package/board/server/board-vocabulary-shapes.ts +153 -0
- package/board/server/board-vocabulary.ts +20 -375
- package/board/server/current-step-panel-helpers.ts +108 -0
- package/board/server/describe-record.ts +403 -0
- package/board/server/engine-launch.ts +20 -3
- package/board/server/engine-restart-helpers.ts +105 -0
- package/board/server/engine-watch.ts +45 -5
- package/board/server/journal-rebuild-helpers.ts +169 -0
- package/board/server/live-file.ts +130 -0
- package/board/server/luca-version.ts +1 -1
- package/board/server/make-rows.ts +14 -629
- package/board/server/record-texts.ts +350 -0
- package/board/server/reduce-board-agent-start.ts +224 -0
- package/board/server/reduce-board-agents.ts +160 -0
- package/board/server/reduce-board-counters.ts +138 -0
- package/board/server/reduce-board-create.ts +148 -0
- package/board/server/reduce-board-final-review.ts +174 -0
- package/board/server/reduce-board-needs-you.ts +283 -0
- package/board/server/reduce-board-reasons.ts +59 -0
- package/board/server/reduce-board-run.ts +255 -0
- package/board/server/reduce-board-settle.ts +88 -0
- package/board/server/reduce-board-steps.ts +226 -0
- package/board/server/reduce-board-text.ts +139 -0
- package/board/server/reduce-board-ticket-checks.ts +168 -0
- package/board/server/reduce-board-ticket-join.ts +144 -0
- package/board/server/reduce-board-ticket-start.ts +132 -0
- package/board/server/reduce-board-update.ts +117 -0
- package/board/server/reduce-board-usage.ts +247 -0
- package/board/server/reduce-board.ts +100 -1917
- package/board/server/reply-post.ts +407 -0
- package/board/server/run-registry.ts +8 -4
- package/board/server/ticket-stages-panel-helpers.ts +296 -0
- package/board/shared/board-rows.ts +9 -0
- package/board/shared/board-rpc.ts +42 -0
- package/board/shared/board-state-counts.ts +77 -0
- package/board/shared/board-state-live.ts +99 -0
- package/board/shared/board-state-review.ts +59 -0
- package/board/shared/board-state-run.ts +236 -0
- package/board/shared/board-state-ticket.ts +257 -0
- package/board/shared/board-state-usage.ts +107 -0
- package/board/shared/board-state.ts +97 -567
- package/board/shared/reply-actions.ts +207 -0
- package/engine/agents/agent-launcher.ts +27 -4
- package/engine/agents/claude-launcher-helpers.ts +233 -0
- package/engine/agents/claude-launcher.ts +28 -669
- package/engine/agents/claude-messages.ts +212 -0
- package/engine/agents/claude-options.ts +43 -8
- package/engine/agents/common-instructions.ts +138 -0
- package/engine/agents/input-channel.ts +36 -0
- package/engine/agents/launch-paths.ts +29 -0
- package/engine/agents/launch-session.ts +227 -0
- package/engine/agents/learner-instructions.ts +31 -0
- package/engine/agents/open-session.ts +210 -0
- package/engine/agents/prompt-sections.ts +95 -0
- package/engine/agents/rejoin-prompts.ts +135 -0
- package/engine/agents/reviewer-instructions.ts +99 -0
- package/engine/agents/role-instructions.ts +31 -244
- package/engine/agents/role-prompts.ts +39 -109
- package/engine/agents/role-results.ts +106 -7
- package/engine/agents/scripted-launcher.ts +21 -6
- package/engine/agents/session-pool.ts +124 -0
- package/engine/agents/turn-progress.ts +283 -0
- package/engine/agents/writer-instructions.ts +151 -0
- package/engine/cli/computer-adapters-real.ts +15 -3
- package/engine/cli/computer-adapters.ts +2 -0
- package/engine/cli/computer-checks.ts +87 -4
- package/engine/cli/config-reload-text.ts +39 -0
- package/engine/cli/decision-model-setup.ts +70 -0
- package/engine/cli/doctor-command.ts +1 -0
- package/engine/cli/doctor.ts +21 -3
- package/engine/cli/going-runs.ts +192 -36
- package/engine/cli/init-command.ts +1 -0
- package/engine/cli/init.ts +23 -1
- package/engine/cli/live-runs.ts +73 -0
- package/engine/cli/luca-run.ts +42 -7
- package/engine/cli/luca-skills.ts +183 -0
- package/engine/cli/luca.ts +8 -1
- package/engine/cli/models-command.ts +119 -0
- package/engine/cli/read-run.ts +81 -0
- package/engine/cli/run-args.ts +33 -6
- package/engine/cli/run-demo.ts +143 -0
- package/engine/cli/run-end.ts +184 -0
- package/engine/cli/run-modes-helpers.ts +81 -0
- package/engine/cli/run-modes.ts +62 -439
- package/engine/cli/setup.ts +5 -48
- package/engine/cli/unfinished-runs.ts +122 -0
- package/engine/cli/upgrade-command.ts +212 -30
- package/engine/cli/upgrade-finish.ts +149 -0
- package/engine/cli/upgrade.ts +112 -71
- package/engine/config/config-fields.ts +127 -0
- package/engine/config/config-reload.ts +250 -0
- package/engine/config/engine-config-schema.ts +235 -0
- package/engine/config/engine-config.ts +72 -142
- package/engine/config/test-commands.ts +102 -0
- package/engine/core/agent-guards-helpers.ts +84 -0
- package/engine/core/agent-turn.ts +395 -0
- package/engine/core/already-done.ts +44 -1
- package/engine/core/build-action.ts +214 -0
- package/engine/core/build-context.ts +135 -0
- package/engine/core/build-launch.ts +259 -0
- package/engine/core/commit-in.ts +282 -0
- package/engine/core/decide-build-early.ts +108 -0
- package/engine/core/decide-build-ending.ts +145 -0
- package/engine/core/decide-build-helpers.ts +87 -0
- package/engine/core/decide-build-tickets.ts +207 -0
- package/engine/core/decide-build-view.ts +103 -0
- package/engine/core/decide-build.ts +101 -1368
- package/engine/core/decide-final-fix.ts +203 -0
- package/engine/core/decide-final-review.ts +55 -310
- package/engine/core/decide-red-green.ts +398 -0
- package/engine/core/decide-rejoin.ts +259 -0
- package/engine/core/decide-repo-wait.ts +129 -0
- package/engine/core/decide-run-budget.ts +86 -12
- package/engine/core/decide-stuck-helpers.ts +127 -0
- package/engine/core/decide-stuck.ts +60 -8
- package/engine/core/decide-ticket-review.ts +95 -0
- package/engine/core/decide-ticket.ts +267 -0
- package/engine/core/decide-waits.ts +47 -0
- package/engine/core/decide.ts +74 -23
- package/engine/core/engine-chores.ts +163 -0
- package/engine/core/environment-rerun.ts +83 -0
- package/engine/core/evidence-text.ts +84 -0
- package/engine/core/execute-action.ts +303 -0
- package/engine/core/execute-already-done-check.ts +133 -0
- package/engine/core/execute-build.ts +68 -944
- package/engine/core/execute-changeset.ts +5 -7
- package/engine/core/execute-final-review.ts +125 -13
- package/engine/core/execute-gates.ts +182 -0
- package/engine/core/execute-intake.ts +130 -0
- package/engine/core/execute-join.ts +276 -0
- package/engine/core/execute-memory.ts +12 -0
- package/engine/core/execute-pull-request.ts +84 -0
- package/engine/core/execute-red-check.ts +219 -0
- package/engine/core/execute-repo-wait.ts +129 -0
- package/engine/core/execute-stuck.ts +25 -10
- package/engine/core/execute-waits.ts +158 -0
- package/engine/core/execute-with-jev.ts +118 -0
- package/engine/core/execute.ts +124 -831
- package/engine/core/final-failure-cap.ts +38 -0
- package/engine/core/final-review-action.ts +125 -0
- package/engine/core/final-review-text.ts +63 -17
- package/engine/core/final-stuck-text.ts +86 -0
- package/engine/core/fix-loop-text.ts +49 -2
- package/engine/core/left-out.ts +120 -0
- package/engine/core/live-file.ts +372 -0
- package/engine/core/loop-caps.ts +25 -2
- package/engine/core/not-in-run.ts +7 -3
- package/engine/core/pull-request-text.ts +255 -6
- package/engine/core/repo-wait-text.ts +81 -0
- package/engine/core/review-text.ts +55 -1
- package/engine/core/run-crash-recovery-helpers.ts +16 -0
- package/engine/core/run-session-close-helpers.ts +96 -0
- package/engine/core/run-start.ts +148 -0
- package/engine/core/run-stuck-text.ts +86 -0
- package/engine/core/schedule-rules.ts +193 -0
- package/engine/core/stuck-detail-text.ts +89 -0
- package/engine/core/stuck-format.ts +22 -0
- package/engine/core/stuck-text.ts +29 -332
- package/engine/core/ticket-stuck-text.ts +192 -0
- package/engine/core/worktree-changes.ts +106 -0
- package/engine/gates/already-done-check.ts +120 -0
- package/engine/gates/feature-map.ts +96 -0
- package/engine/gates/gate-runner.ts +108 -108
- package/engine/gates/gate-schemas.ts +61 -2
- package/engine/gates/leftover-scan.ts +154 -31
- package/engine/gates/new-tests.ts +45 -0
- package/engine/gates/own-files-check.ts +98 -0
- package/engine/gates/prepare-check.ts +60 -0
- package/engine/gates/prepare-slots.ts +99 -0
- package/engine/gates/red-check.ts +184 -16
- package/engine/gates/shell-check.ts +64 -0
- package/engine/gates/slot-waits.ts +55 -0
- package/engine/gates/slow-test-runner.ts +59 -0
- package/engine/gates/test-checks.ts +161 -0
- package/engine/gates/test-runner.ts +164 -5
- package/engine/gates/test-slots.ts +48 -0
- package/engine/git/git-adapter.ts +55 -396
- package/engine/git/git-commits.ts +132 -0
- package/engine/git/git-join.ts +139 -0
- package/engine/git/git-read.ts +123 -0
- package/engine/git/git-run.ts +32 -0
- package/engine/git/git-worktrees.ts +143 -0
- package/engine/git/outside-links.ts +55 -0
- package/engine/guards/after-turn-check.ts +31 -5
- package/engine/guards/guard-hook.ts +60 -12
- package/engine/guards/guard-roles.ts +68 -0
- package/engine/guards/helper-rules.ts +92 -0
- package/engine/guards/permission-rules.ts +161 -0
- package/engine/guards/role-rules-helpers.ts +48 -0
- package/engine/guards/role-rules.ts +34 -606
- package/engine/guards/shell-rules.ts +344 -0
- package/engine/guards/worktree-git.ts +208 -0
- package/engine/guards/worktree-snapshot.ts +412 -0
- package/engine/guards/worktree-state.ts +28 -825
- package/engine/guards/worktree-undo.ts +260 -0
- package/engine/guards/write-rules.ts +149 -0
- package/engine/index.ts +30 -9
- package/engine/intake/criteria-checks.ts +114 -0
- package/engine/intake/intake-checks.ts +164 -30
- package/engine/intake/intake-schemas.ts +37 -2
- package/engine/jev/clef-client.ts +259 -0
- package/engine/jev/decision-model-credentials.ts +212 -0
- package/engine/jev/jev-jobs.ts +144 -60
- package/engine/jev/jev-schemas.ts +86 -5
- package/engine/jev/jev-shadow-helpers.ts +218 -0
- package/engine/jev/jev-shadow.ts +109 -64
- package/engine/jev/model-pick.ts +275 -0
- package/engine/journal/agent-entries.ts +167 -0
- package/engine/journal/agent-message-entries.ts +72 -0
- package/engine/journal/agent-turn-replay.ts +203 -0
- package/engine/journal/already-done-entries.ts +55 -0
- package/engine/journal/commit-entries.ts +56 -0
- package/engine/journal/cross-ticket-replay.ts +222 -0
- package/engine/journal/decision-model-entries.ts +86 -0
- package/engine/journal/entry-fields.ts +10 -0
- package/engine/journal/final-review-entries.ts +128 -0
- package/engine/journal/final-review-replay.ts +324 -0
- package/engine/journal/final-review-round.ts +320 -0
- package/engine/journal/final-review-state.ts +180 -0
- package/engine/journal/gate-entries.ts +81 -0
- package/engine/journal/intake-entries.ts +87 -0
- package/engine/journal/join-entries.ts +126 -0
- package/engine/journal/journal-entry-schema.ts +218 -0
- package/engine/journal/journal-kinds.ts +97 -0
- package/engine/journal/journal-record-schema.ts +218 -0
- package/engine/journal/journal-record.ts +43 -1400
- package/engine/journal/journal.ts +6 -1
- package/engine/journal/limit-entries.ts +90 -0
- package/engine/journal/memory-entries.ts +124 -0
- package/engine/journal/memory-replay.ts +157 -0
- package/engine/journal/plan-replay.ts +139 -0
- package/engine/journal/pull-request-entries.ts +41 -0
- package/engine/journal/replay.ts +157 -2144
- package/engine/journal/reply-replay.ts +144 -0
- package/engine/journal/repo-wait-entries.ts +53 -0
- package/engine/journal/run-entries.ts +172 -0
- package/engine/journal/run-state.ts +264 -0
- package/engine/journal/shared-failure-replay.ts +133 -0
- package/engine/journal/stuck-entries.ts +193 -0
- package/engine/journal/ticket-progress.ts +392 -0
- package/engine/journal/ticket-replay.ts +411 -0
- package/engine/journal/ticket-result-replay.ts +364 -0
- package/engine/journal/ticket-retry-replay.ts +142 -0
- package/engine/journal/worktree-entries.ts +122 -0
- package/engine/models/model-score.ts +512 -0
- package/engine/shell/run-command.ts +187 -18
- package/engine/testing/agent-turn-fixtures.ts +287 -0
- package/engine/testing/already-done-fixtures.ts +97 -0
- package/engine/testing/build-fixtures.ts +68 -973
- package/engine/testing/checkouts-fixtures.ts +48 -0
- package/engine/testing/final-review-fixtures.ts +62 -1
- package/engine/testing/final-review-steps.ts +143 -0
- package/engine/testing/limit-fixtures.ts +85 -0
- package/engine/testing/live-file-fixtures.ts +57 -0
- package/engine/testing/practice-repo.ts +10 -4
- package/engine/testing/practice-run.ts +1 -1
- package/engine/testing/reply-fixtures.ts +88 -0
- package/engine/testing/run-budget-fixtures.ts +165 -0
- package/engine/testing/run-fixtures.ts +219 -0
- package/engine/testing/sdk-play.ts +177 -0
- package/engine/testing/setup-repo.ts +194 -0
- package/engine/testing/step-record-fixtures.ts +55 -0
- package/engine/testing/ticket-step-fixtures.ts +272 -0
- package/engine/testing/wait-scenarios.ts +186 -0
- package/engine/tracker/gh-retry.ts +217 -0
- package/engine/tracker/github-tracker.ts +310 -90
- package/engine/tracker/in-memory-tracker.ts +8 -4
- package/engine/tracker/tracker.ts +131 -6
- package/package.json +2 -1
- package/skills/luca-retro/SKILL.md +74 -0
- package/skills/luca-retro/scripts/retro-summary.ts +1356 -0
- package/skills/luca-unstick/SKILL.md +153 -0
- package/skills/luca-unstick/scripts/stuck-summary.ts +1063 -0
- package/engine/jev/jev-client.ts +0 -173
|
@@ -1,12 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
import sortBy from 'lodash/sortBy'
|
|
7
|
-
import uniq from 'lodash/uniq'
|
|
8
|
-
|
|
9
|
-
import { runCommand } from '../shell/run-command'
|
|
1
|
+
import { commitOps } from './git-commits'
|
|
2
|
+
import { joinOps } from './git-join'
|
|
3
|
+
import { readOps } from './git-read'
|
|
4
|
+
import { createWorktreeOps } from './git-worktrees'
|
|
10
5
|
|
|
11
6
|
/** One changed path in a worktree, from `git status`. */
|
|
12
7
|
export type FileChange = {
|
|
@@ -17,6 +12,9 @@ export type FileChange = {
|
|
|
17
12
|
/** A commit the engine made. */
|
|
18
13
|
export type EngineCommit = { sha: string; files: string[] }
|
|
19
14
|
|
|
15
|
+
/** Where a commit is, next to a base commit (see `commitOnBase`). */
|
|
16
|
+
export type CommitPlace = 'on_base' | 'not_on_base' | 'unknown'
|
|
17
|
+
|
|
20
18
|
/**
|
|
21
19
|
* Every git side effect of a run. Only the engine calls these; agents never
|
|
22
20
|
* commit, branch, or push.
|
|
@@ -59,6 +57,12 @@ export type GitAdapter = {
|
|
|
59
57
|
* included.
|
|
60
58
|
*/
|
|
61
59
|
changedSince: (args: { cwd: string; from: string }) => Promise<string[]>
|
|
60
|
+
/** The text of `path` at commit `sha`, or `null` if it wasn't there (#581). */
|
|
61
|
+
fileAt: (args: {
|
|
62
|
+
cwd: string
|
|
63
|
+
sha: string
|
|
64
|
+
path: string
|
|
65
|
+
}) => Promise<string | null>
|
|
62
66
|
/** Paths that differ between two commits. */
|
|
63
67
|
filesBetween: (args: {
|
|
64
68
|
cwd: string
|
|
@@ -87,6 +91,15 @@ export type GitAdapter = {
|
|
|
87
91
|
/** Untracked paths to keep out of the commit. Left out, none. */
|
|
88
92
|
leave_out?: string[]
|
|
89
93
|
}) => Promise<EngineCommit>
|
|
94
|
+
/**
|
|
95
|
+
* Commits only `paths` (new or changed), skipping hooks. Every other
|
|
96
|
+
* change at `cwd`, staged or not, stays out of the commit, as it was.
|
|
97
|
+
*/
|
|
98
|
+
commitPaths: (args: {
|
|
99
|
+
cwd: string
|
|
100
|
+
message: string
|
|
101
|
+
paths: string[]
|
|
102
|
+
}) => Promise<EngineCommit>
|
|
90
103
|
/**
|
|
91
104
|
* Throws away every uncommitted change at `cwd`, new untracked files
|
|
92
105
|
* included (ignored files such as `node_modules` stay), and returns the
|
|
@@ -111,22 +124,33 @@ export type GitAdapter = {
|
|
|
111
124
|
}) => Promise<string[]>
|
|
112
125
|
/**
|
|
113
126
|
* Replays commits onto the branch checked out at `cwd`, in order. A clash
|
|
114
|
-
* is undone and returned as an error.
|
|
127
|
+
* is undone and returned as an error. `lines_changed` is the lines the
|
|
128
|
+
* replay added plus removed, binary files counting 0.
|
|
115
129
|
*/
|
|
116
130
|
replay: (args: {
|
|
117
131
|
cwd: string
|
|
118
132
|
commits: string[]
|
|
119
|
-
}) => Promise<
|
|
133
|
+
}) => Promise<
|
|
134
|
+
| { ok: true; shas: string[]; lines_changed: number }
|
|
135
|
+
| { ok: false; error: string }
|
|
136
|
+
>
|
|
120
137
|
/**
|
|
121
138
|
* Undoes a replay on the branch checked out at `cwd`: resets it hard to
|
|
122
|
-
* the commit before `first_sha`.
|
|
139
|
+
* the commit before `first_sha`. It never rewinds over other work
|
|
140
|
+
* (#519), so it refuses, with the branch left as it is, when
|
|
141
|
+
* `first_sha` is no commit (or has no parent), is not on the branch, or
|
|
142
|
+
* when any commit from `first_sha` up to the branch's tip is not in
|
|
143
|
+
* `only`. Safe to run twice: with the branch already at the commit
|
|
144
|
+
* before `first_sha`, it undoes nothing.
|
|
123
145
|
*
|
|
124
|
-
* @returns The commits it undid, oldest first.
|
|
146
|
+
* @returns The commits it undid, oldest first, or why it refused.
|
|
125
147
|
*/
|
|
126
148
|
undoReplay: (args: {
|
|
127
149
|
cwd: string
|
|
128
150
|
first_sha: string
|
|
129
|
-
|
|
151
|
+
/** The commits it may undo: any other on top of `first_sha` refuses. */
|
|
152
|
+
only: string[]
|
|
153
|
+
}) => Promise<{ ok: true; undone: string[] } | { ok: false; error: string }>
|
|
130
154
|
/**
|
|
131
155
|
* Moves a worktree's change onto another commit: resets the worktree at
|
|
132
156
|
* `cwd` hard to `onto`, then applies the whole diff from `from` to `to`
|
|
@@ -150,6 +174,16 @@ export type GitAdapter = {
|
|
|
150
174
|
push: (args: { cwd: string; branch: string }) => Promise<void>
|
|
151
175
|
/** The commit checked out at `cwd`. */
|
|
152
176
|
head: (args: { cwd: string }) => Promise<string>
|
|
177
|
+
/**
|
|
178
|
+
* Where commit `sha` is: `on_base` if it is `base` or in its history,
|
|
179
|
+
* `not_on_base` if the repo has it but `base` doesn't, `unknown` if the
|
|
180
|
+
* repo has no such commit (or `sha` is no commit id).
|
|
181
|
+
*/
|
|
182
|
+
commitOnBase: (args: {
|
|
183
|
+
cwd: string
|
|
184
|
+
sha: string
|
|
185
|
+
base: string
|
|
186
|
+
}) => Promise<CommitPlace>
|
|
153
187
|
/**
|
|
154
188
|
* The commit checked out at `cwd` with its whole message (trimmed) and
|
|
155
189
|
* its files, such as to adopt a commit a crash left out of the journal.
|
|
@@ -159,84 +193,6 @@ export type GitAdapter = {
|
|
|
159
193
|
}) => Promise<EngineCommit & { message: string }>
|
|
160
194
|
}
|
|
161
195
|
|
|
162
|
-
const gitRun = ({ cwd, args }: { cwd: string; args: string[] }) =>
|
|
163
|
-
runCommand({ cmd: ['git', ...args], cwd, timeout_ms: 120_000 })
|
|
164
|
-
|
|
165
|
-
const gitOk = async ({
|
|
166
|
-
cwd,
|
|
167
|
-
args,
|
|
168
|
-
}: {
|
|
169
|
-
cwd: string
|
|
170
|
-
args: string[]
|
|
171
|
-
}): Promise<string> => {
|
|
172
|
-
const result = await gitRun({ cwd, args })
|
|
173
|
-
if (result.exit_code !== 0) {
|
|
174
|
-
throw new Error(
|
|
175
|
-
`git ${args.join(' ')} failed in ${cwd}:\n${result.stderr || result.stdout}`
|
|
176
|
-
)
|
|
177
|
-
}
|
|
178
|
-
return result.stdout
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const lines = (text: string): string[] =>
|
|
182
|
-
text.split('\n').filter((line) => line.trim() !== '')
|
|
183
|
-
|
|
184
|
-
/** Parses `git status --porcelain=v1 -z --untracked-files=all`. */
|
|
185
|
-
const parseStatus = ({ text }: { text: string }): FileChange[] => {
|
|
186
|
-
const parts = text.split('\0').filter((part) => part.length > 0)
|
|
187
|
-
const changes: FileChange[] = []
|
|
188
|
-
for (let index = 0; index < parts.length; index += 1) {
|
|
189
|
-
const part = parts[index] ?? ''
|
|
190
|
-
const [x = ' ', y = ' '] = part
|
|
191
|
-
const path = part.slice(3)
|
|
192
|
-
if (x === 'R' || x === 'C') index += 1 // the next part is the old path
|
|
193
|
-
if (x === 'D' || y === 'D') changes.push({ path, change: 'deleted' })
|
|
194
|
-
else if (x === '?' || x === 'A' || x === 'R' || x === 'C')
|
|
195
|
-
changes.push({ path, change: 'added' })
|
|
196
|
-
else changes.push({ path, change: 'modified' })
|
|
197
|
-
}
|
|
198
|
-
return sortBy(changes, 'path')
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Stages every change at `cwd` (`git add -A`) but the paths in `leave_out`,
|
|
203
|
-
* which git never looks at: each is an exact, literal pathspec to exclude,
|
|
204
|
-
* passed through a file so any number of them fit.
|
|
205
|
-
*/
|
|
206
|
-
const stageAll = async ({
|
|
207
|
-
cwd,
|
|
208
|
-
leave_out,
|
|
209
|
-
}: {
|
|
210
|
-
cwd: string
|
|
211
|
-
leave_out: string[]
|
|
212
|
-
}): Promise<void> => {
|
|
213
|
-
if (leave_out.length === 0) {
|
|
214
|
-
await gitOk({ cwd, args: ['add', '-A'] })
|
|
215
|
-
return
|
|
216
|
-
}
|
|
217
|
-
const folder = await mkdtemp(join(tmpdir(), 'luca-stage-'))
|
|
218
|
-
try {
|
|
219
|
-
const pathspecs = join(folder, 'pathspecs')
|
|
220
|
-
await Bun.write(
|
|
221
|
-
pathspecs,
|
|
222
|
-
['.', ...leave_out.map((path) => `:(exclude,literal)${path}`)]
|
|
223
|
-
.map((spec) => `${spec}\0`)
|
|
224
|
-
.join('')
|
|
225
|
-
)
|
|
226
|
-
await gitOk({
|
|
227
|
-
cwd,
|
|
228
|
-
args: [
|
|
229
|
-
'add',
|
|
230
|
-
'-A',
|
|
231
|
-
`--pathspec-from-file=${pathspecs}`,
|
|
232
|
-
'--pathspec-file-nul',
|
|
233
|
-
],
|
|
234
|
-
})
|
|
235
|
-
} finally {
|
|
236
|
-
await rm(folder, { recursive: true, force: true })
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
196
|
/**
|
|
241
197
|
* Runs async work one piece at a time, in call order, so git never races
|
|
242
198
|
* itself on the repo's lock files when tickets build at the same time.
|
|
@@ -263,311 +219,11 @@ export const createGitAdapter = ({
|
|
|
263
219
|
}: {
|
|
264
220
|
repo_root: string
|
|
265
221
|
}): GitAdapter => {
|
|
266
|
-
const head = async ({ cwd }: { cwd: string }) =>
|
|
267
|
-
(await gitOk({ cwd, args: ['rev-parse', 'HEAD'] })).trim()
|
|
268
|
-
|
|
269
|
-
/** The branch git has checked out in a worktree at `path`, if any. */
|
|
270
|
-
const worktreeBranchAt = async ({
|
|
271
|
-
path,
|
|
272
|
-
}: {
|
|
273
|
-
path: string
|
|
274
|
-
}): Promise<{ branch: string | null } | null> => {
|
|
275
|
-
if (!existsSync(path)) return null
|
|
276
|
-
const wanted = realpathSync(path)
|
|
277
|
-
const listed = await gitOk({
|
|
278
|
-
cwd: repo_root,
|
|
279
|
-
args: ['worktree', 'list', '--porcelain'],
|
|
280
|
-
})
|
|
281
|
-
for (const block of listed.split('\n\n')) {
|
|
282
|
-
const fields = lines(block)
|
|
283
|
-
const at = fields
|
|
284
|
-
.find((line) => line.startsWith('worktree '))
|
|
285
|
-
?.slice('worktree '.length)
|
|
286
|
-
if (at === undefined || !existsSync(at)) continue
|
|
287
|
-
if (realpathSync(at) !== wanted) continue
|
|
288
|
-
const ref = fields
|
|
289
|
-
.find((line) => line.startsWith('branch '))
|
|
290
|
-
?.slice('branch '.length)
|
|
291
|
-
return { branch: ref?.replace(/^refs\/heads\//, '') ?? null }
|
|
292
|
-
}
|
|
293
|
-
return null
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
const branchExists = async ({ branch }: { branch: string }) =>
|
|
297
|
-
(
|
|
298
|
-
await gitRun({
|
|
299
|
-
cwd: repo_root,
|
|
300
|
-
args: [
|
|
301
|
-
'rev-parse',
|
|
302
|
-
'--verify',
|
|
303
|
-
'--quiet',
|
|
304
|
-
`refs/heads/${branch}`,
|
|
305
|
-
],
|
|
306
|
-
})
|
|
307
|
-
).exit_code === 0
|
|
308
|
-
|
|
309
|
-
const addWorktree = async ({
|
|
310
|
-
branch,
|
|
311
|
-
from,
|
|
312
|
-
path,
|
|
313
|
-
}: {
|
|
314
|
-
branch: string
|
|
315
|
-
from: string
|
|
316
|
-
path: string
|
|
317
|
-
}) => {
|
|
318
|
-
const known = await worktreeBranchAt({ path })
|
|
319
|
-
if (known !== null && known.branch !== branch) {
|
|
320
|
-
throw new Error(
|
|
321
|
-
`${path} is already a worktree on ${known.branch ?? 'no branch'}, not ${branch}.`
|
|
322
|
-
)
|
|
323
|
-
}
|
|
324
|
-
if (known !== null) {
|
|
325
|
-
// Made before a crash: nothing has worked in it since, so finish
|
|
326
|
-
// a checkout the crash may have cut off, and adopt it.
|
|
327
|
-
const reset = await gitRun({
|
|
328
|
-
cwd: path,
|
|
329
|
-
args: ['reset', '--quiet', '--hard', 'HEAD'],
|
|
330
|
-
})
|
|
331
|
-
if (reset.exit_code === 0) {
|
|
332
|
-
return { base_sha: await head({ cwd: path }) }
|
|
333
|
-
}
|
|
334
|
-
await gitRun({
|
|
335
|
-
cwd: repo_root,
|
|
336
|
-
args: ['worktree', 'remove', '--force', '--force', path],
|
|
337
|
-
})
|
|
338
|
-
}
|
|
339
|
-
// A half-made folder git doesn't know (or no longer does) goes.
|
|
340
|
-
await rm(path, { recursive: true, force: true })
|
|
341
|
-
await gitOk({ cwd: repo_root, args: ['worktree', 'prune'] })
|
|
342
|
-
await gitOk({
|
|
343
|
-
cwd: repo_root,
|
|
344
|
-
args: (await branchExists({ branch }))
|
|
345
|
-
? ['worktree', 'add', '--quiet', path, branch]
|
|
346
|
-
: [
|
|
347
|
-
'worktree',
|
|
348
|
-
'add',
|
|
349
|
-
'--quiet',
|
|
350
|
-
'--no-track',
|
|
351
|
-
'-b',
|
|
352
|
-
branch,
|
|
353
|
-
path,
|
|
354
|
-
from,
|
|
355
|
-
],
|
|
356
|
-
})
|
|
357
|
-
return { base_sha: await head({ cwd: path }) }
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
const filesOf = async ({ cwd, sha }: { cwd: string; sha: string }) =>
|
|
361
|
-
lines(
|
|
362
|
-
await gitOk({
|
|
363
|
-
cwd,
|
|
364
|
-
args: ['show', '--name-only', '--format=', sha],
|
|
365
|
-
})
|
|
366
|
-
).toSorted()
|
|
367
|
-
|
|
368
222
|
const raw: GitAdapter = {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
'fetch',
|
|
374
|
-
'--quiet',
|
|
375
|
-
'origin',
|
|
376
|
-
`+refs/heads/${base_branch}:refs/remotes/origin/${base_branch}`,
|
|
377
|
-
],
|
|
378
|
-
})
|
|
379
|
-
return fetched.exit_code === 0
|
|
380
|
-
? { ok: true }
|
|
381
|
-
: {
|
|
382
|
-
ok: false,
|
|
383
|
-
error: (fetched.stderr || fetched.stdout).trim(),
|
|
384
|
-
}
|
|
385
|
-
},
|
|
386
|
-
createRunBranch: ({ branch, base_branch, path }) =>
|
|
387
|
-
addWorktree({ branch, from: `origin/${base_branch}`, path }),
|
|
388
|
-
createWorktree: addWorktree,
|
|
389
|
-
changes: async ({ cwd }) =>
|
|
390
|
-
parseStatus({
|
|
391
|
-
text: await gitOk({
|
|
392
|
-
cwd,
|
|
393
|
-
args: [
|
|
394
|
-
'status',
|
|
395
|
-
'--porcelain=v1',
|
|
396
|
-
'-z',
|
|
397
|
-
'--untracked-files=all',
|
|
398
|
-
],
|
|
399
|
-
}),
|
|
400
|
-
}),
|
|
401
|
-
changedSince: async ({ cwd, from }) => {
|
|
402
|
-
const tracked = lines(
|
|
403
|
-
await gitOk({ cwd, args: ['diff', '--name-only', from] })
|
|
404
|
-
)
|
|
405
|
-
const untracked = lines(
|
|
406
|
-
await gitOk({
|
|
407
|
-
cwd,
|
|
408
|
-
args: ['ls-files', '--others', '--exclude-standard'],
|
|
409
|
-
})
|
|
410
|
-
)
|
|
411
|
-
return uniq([...tracked, ...untracked]).toSorted()
|
|
412
|
-
},
|
|
413
|
-
filesBetween: async ({ cwd, from, to }) =>
|
|
414
|
-
lines(
|
|
415
|
-
await gitOk({ cwd, args: ['diff', '--name-only', from, to] })
|
|
416
|
-
).toSorted(),
|
|
417
|
-
untracked: async ({ cwd }) =>
|
|
418
|
-
(
|
|
419
|
-
await gitOk({
|
|
420
|
-
cwd,
|
|
421
|
-
args: ['ls-files', '--others', '--exclude-standard', '-z'],
|
|
422
|
-
})
|
|
423
|
-
)
|
|
424
|
-
.split('\0')
|
|
425
|
-
.filter((path) => path !== ''),
|
|
426
|
-
listFiles: async ({ cwd }) =>
|
|
427
|
-
lines(
|
|
428
|
-
await gitOk({
|
|
429
|
-
cwd,
|
|
430
|
-
args: [
|
|
431
|
-
'ls-files',
|
|
432
|
-
'--cached',
|
|
433
|
-
'--others',
|
|
434
|
-
'--exclude-standard',
|
|
435
|
-
],
|
|
436
|
-
})
|
|
437
|
-
),
|
|
438
|
-
filesMentioning: async ({ cwd, text }) => {
|
|
439
|
-
// git grep exits 1 when nothing matches; that is an empty answer.
|
|
440
|
-
const result = await gitRun({
|
|
441
|
-
cwd,
|
|
442
|
-
args: ['grep', '-l', '-F', '-e', text, '--', '.'],
|
|
443
|
-
})
|
|
444
|
-
return result.exit_code === 0 ? lines(result.stdout) : []
|
|
445
|
-
},
|
|
446
|
-
commitAll: async ({ cwd, message, leave_out }) => {
|
|
447
|
-
await stageAll({ cwd, leave_out: leave_out ?? [] })
|
|
448
|
-
await gitOk({
|
|
449
|
-
cwd,
|
|
450
|
-
args: ['commit', '--quiet', '--no-verify', '-m', message],
|
|
451
|
-
})
|
|
452
|
-
const sha = await head({ cwd })
|
|
453
|
-
return { sha, files: await filesOf({ cwd, sha }) }
|
|
454
|
-
},
|
|
455
|
-
discardChanges: async ({ cwd }) => {
|
|
456
|
-
await gitOk({ cwd, args: ['reset', '--quiet', '--hard', 'HEAD'] })
|
|
457
|
-
await gitOk({ cwd, args: ['clean', '--quiet', '-f', '-d'] })
|
|
458
|
-
return { sha: await head({ cwd }) }
|
|
459
|
-
},
|
|
460
|
-
resetWorktree: async ({ cwd, to }) => {
|
|
461
|
-
// A cherry-pick a crash cut off leaves its sequence behind.
|
|
462
|
-
await gitRun({ cwd, args: ['cherry-pick', '--quit'] })
|
|
463
|
-
await gitOk({ cwd, args: ['reset', '--quiet', '--hard', to] })
|
|
464
|
-
await gitOk({ cwd, args: ['clean', '--quiet', '-f', '-d'] })
|
|
465
|
-
return { sha: await head({ cwd }) }
|
|
466
|
-
},
|
|
467
|
-
commitsBetween: async ({ cwd, from, to }) =>
|
|
468
|
-
lines(
|
|
469
|
-
await gitOk({
|
|
470
|
-
cwd,
|
|
471
|
-
args: ['rev-list', '--reverse', `${from}..${to}`],
|
|
472
|
-
})
|
|
473
|
-
),
|
|
474
|
-
replay: async ({ cwd, commits }) => {
|
|
475
|
-
const before = await head({ cwd })
|
|
476
|
-
const picked = await gitRun({
|
|
477
|
-
cwd,
|
|
478
|
-
args: ['cherry-pick', ...commits],
|
|
479
|
-
})
|
|
480
|
-
if (picked.exit_code !== 0) {
|
|
481
|
-
await gitRun({ cwd, args: ['cherry-pick', '--abort'] })
|
|
482
|
-
return { ok: false, error: picked.stderr || picked.stdout }
|
|
483
|
-
}
|
|
484
|
-
return {
|
|
485
|
-
ok: true,
|
|
486
|
-
shas: lines(
|
|
487
|
-
await gitOk({
|
|
488
|
-
cwd,
|
|
489
|
-
args: ['rev-list', '--reverse', `${before}..HEAD`],
|
|
490
|
-
})
|
|
491
|
-
),
|
|
492
|
-
}
|
|
493
|
-
},
|
|
494
|
-
undoReplay: async ({ cwd, first_sha }) => {
|
|
495
|
-
const before = `${first_sha}^`
|
|
496
|
-
const undone = lines(
|
|
497
|
-
await gitOk({
|
|
498
|
-
cwd,
|
|
499
|
-
args: ['rev-list', '--reverse', `${before}..HEAD`],
|
|
500
|
-
})
|
|
501
|
-
)
|
|
502
|
-
await gitOk({ cwd, args: ['reset', '--quiet', '--hard', before] })
|
|
503
|
-
return { undone }
|
|
504
|
-
},
|
|
505
|
-
rebaseWorktree: async ({ cwd, from, to, onto }) => {
|
|
506
|
-
const folder = await mkdtemp(join(tmpdir(), 'luca-rebase-'))
|
|
507
|
-
try {
|
|
508
|
-
const patch = join(folder, 'change.patch')
|
|
509
|
-
await gitOk({
|
|
510
|
-
cwd,
|
|
511
|
-
args: ['diff', '--binary', `--output=${patch}`, from, to],
|
|
512
|
-
})
|
|
513
|
-
await gitOk({ cwd, args: ['reset', '--quiet', '--hard', onto] })
|
|
514
|
-
if ((await Bun.file(patch).size) === 0) return { conflicts: [] }
|
|
515
|
-
const applied = await gitRun({
|
|
516
|
-
cwd,
|
|
517
|
-
args: ['apply', '--3way', patch],
|
|
518
|
-
})
|
|
519
|
-
const conflicts = lines(
|
|
520
|
-
await gitOk({
|
|
521
|
-
cwd,
|
|
522
|
-
args: ['diff', '--name-only', '--diff-filter=U'],
|
|
523
|
-
})
|
|
524
|
-
).toSorted()
|
|
525
|
-
if (applied.exit_code !== 0 && conflicts.length === 0) {
|
|
526
|
-
throw new Error(
|
|
527
|
-
`git apply --3way failed in ${cwd}:\n${applied.stderr || applied.stdout}`
|
|
528
|
-
)
|
|
529
|
-
}
|
|
530
|
-
// Everything becomes plain uncommitted changes, markers kept.
|
|
531
|
-
await gitOk({ cwd, args: ['reset', '--quiet'] })
|
|
532
|
-
return { conflicts }
|
|
533
|
-
} finally {
|
|
534
|
-
await rm(folder, { recursive: true, force: true })
|
|
535
|
-
}
|
|
536
|
-
},
|
|
537
|
-
removeWorktree: async ({ path }) => {
|
|
538
|
-
if (existsSync(path)) {
|
|
539
|
-
// A folder that is no worktree (or not any more) is fine.
|
|
540
|
-
await gitRun({
|
|
541
|
-
cwd: repo_root,
|
|
542
|
-
args: ['worktree', 'remove', '--force', path],
|
|
543
|
-
})
|
|
544
|
-
}
|
|
545
|
-
await gitOk({ cwd: repo_root, args: ['worktree', 'prune'] })
|
|
546
|
-
},
|
|
547
|
-
push: async ({ cwd, branch }) => {
|
|
548
|
-
await gitOk({
|
|
549
|
-
cwd,
|
|
550
|
-
args: [
|
|
551
|
-
'push',
|
|
552
|
-
'--quiet',
|
|
553
|
-
'origin',
|
|
554
|
-
`${branch}:refs/heads/${branch}`,
|
|
555
|
-
],
|
|
556
|
-
})
|
|
557
|
-
},
|
|
558
|
-
head,
|
|
559
|
-
lastCommit: async ({ cwd }) => {
|
|
560
|
-
const sha = await head({ cwd })
|
|
561
|
-
const message = await gitOk({
|
|
562
|
-
cwd,
|
|
563
|
-
args: ['log', '-1', '--format=%B', sha],
|
|
564
|
-
})
|
|
565
|
-
return {
|
|
566
|
-
sha,
|
|
567
|
-
message: message.trim(),
|
|
568
|
-
files: await filesOf({ cwd, sha }),
|
|
569
|
-
}
|
|
570
|
-
},
|
|
223
|
+
...createWorktreeOps({ repo_root }),
|
|
224
|
+
...readOps,
|
|
225
|
+
...commitOps,
|
|
226
|
+
...joinOps,
|
|
571
227
|
}
|
|
572
228
|
|
|
573
229
|
const inTurn = createQueue()
|
|
@@ -581,11 +237,13 @@ export const createGitAdapter = ({
|
|
|
581
237
|
createWorktree: serial(raw.createWorktree),
|
|
582
238
|
changes: serial(raw.changes),
|
|
583
239
|
changedSince: serial(raw.changedSince),
|
|
240
|
+
fileAt: serial(raw.fileAt),
|
|
584
241
|
filesBetween: serial(raw.filesBetween),
|
|
585
242
|
untracked: serial(raw.untracked),
|
|
586
243
|
listFiles: serial(raw.listFiles),
|
|
587
244
|
filesMentioning: serial(raw.filesMentioning),
|
|
588
245
|
commitAll: serial(raw.commitAll),
|
|
246
|
+
commitPaths: serial(raw.commitPaths),
|
|
589
247
|
discardChanges: serial(raw.discardChanges),
|
|
590
248
|
resetWorktree: serial(raw.resetWorktree),
|
|
591
249
|
commitsBetween: serial(raw.commitsBetween),
|
|
@@ -595,6 +253,7 @@ export const createGitAdapter = ({
|
|
|
595
253
|
removeWorktree: serial(raw.removeWorktree),
|
|
596
254
|
push: serial(raw.push),
|
|
597
255
|
head: serial(raw.head),
|
|
256
|
+
commitOnBase: serial(raw.commitOnBase),
|
|
598
257
|
lastCommit: serial(raw.lastCommit),
|
|
599
258
|
}
|
|
600
259
|
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The git adapter's commits and clean-ups: stage and commit all or some
|
|
3
|
+
* paths, read the last commit, throw away or reset a worktree's changes, and
|
|
4
|
+
* push. Every call skips hooks: the engine ran the gates.
|
|
5
|
+
*/
|
|
6
|
+
import { mkdtemp, rm } from 'node:fs/promises'
|
|
7
|
+
import { tmpdir } from 'node:os'
|
|
8
|
+
import { join } from 'node:path'
|
|
9
|
+
|
|
10
|
+
import type { GitAdapter } from './git-adapter'
|
|
11
|
+
import { gitOk, gitRun, head, lines } from './git-run'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Stages every change at `cwd` (`git add -A`) but the paths in `leave_out`,
|
|
15
|
+
* which git never looks at: each is an exact, literal pathspec to exclude,
|
|
16
|
+
* passed through a file so any number of them fit.
|
|
17
|
+
*/
|
|
18
|
+
export const stageAll = async ({
|
|
19
|
+
cwd,
|
|
20
|
+
leave_out,
|
|
21
|
+
}: {
|
|
22
|
+
cwd: string
|
|
23
|
+
leave_out: string[]
|
|
24
|
+
}): Promise<void> => {
|
|
25
|
+
if (leave_out.length === 0) {
|
|
26
|
+
await gitOk({ cwd, args: ['add', '-A'] })
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
const folder = await mkdtemp(join(tmpdir(), 'luca-stage-'))
|
|
30
|
+
try {
|
|
31
|
+
const pathspecs = join(folder, 'pathspecs')
|
|
32
|
+
await Bun.write(
|
|
33
|
+
pathspecs,
|
|
34
|
+
['.', ...leave_out.map((path) => `:(exclude,literal)${path}`)]
|
|
35
|
+
.map((spec) => `${spec}\0`)
|
|
36
|
+
.join('')
|
|
37
|
+
)
|
|
38
|
+
await gitOk({
|
|
39
|
+
cwd,
|
|
40
|
+
args: [
|
|
41
|
+
'add',
|
|
42
|
+
'-A',
|
|
43
|
+
`--pathspec-from-file=${pathspecs}`,
|
|
44
|
+
'--pathspec-file-nul',
|
|
45
|
+
],
|
|
46
|
+
})
|
|
47
|
+
} finally {
|
|
48
|
+
await rm(folder, { recursive: true, force: true })
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const filesOf = async ({ cwd, sha }: { cwd: string; sha: string }) =>
|
|
53
|
+
lines(
|
|
54
|
+
await gitOk({
|
|
55
|
+
cwd,
|
|
56
|
+
args: ['show', '--name-only', '--format=', sha],
|
|
57
|
+
})
|
|
58
|
+
).toSorted()
|
|
59
|
+
|
|
60
|
+
export const commitOps: Pick<
|
|
61
|
+
GitAdapter,
|
|
62
|
+
| 'commitAll'
|
|
63
|
+
| 'commitPaths'
|
|
64
|
+
| 'discardChanges'
|
|
65
|
+
| 'resetWorktree'
|
|
66
|
+
| 'push'
|
|
67
|
+
| 'lastCommit'
|
|
68
|
+
> = {
|
|
69
|
+
commitAll: async ({ cwd, message, leave_out }) => {
|
|
70
|
+
await stageAll({ cwd, leave_out: leave_out ?? [] })
|
|
71
|
+
await gitOk({
|
|
72
|
+
cwd,
|
|
73
|
+
args: ['commit', '--quiet', '--no-verify', '-m', message],
|
|
74
|
+
})
|
|
75
|
+
const sha = await head({ cwd })
|
|
76
|
+
return { sha, files: await filesOf({ cwd, sha }) }
|
|
77
|
+
},
|
|
78
|
+
commitPaths: async ({ cwd, message, paths }) => {
|
|
79
|
+
const pathspecs = paths.map((path) => `:(literal)${path}`)
|
|
80
|
+
await gitOk({ cwd, args: ['add', '--', ...pathspecs] })
|
|
81
|
+
// With paths, `git commit` commits just them (`--only`).
|
|
82
|
+
await gitOk({
|
|
83
|
+
cwd,
|
|
84
|
+
args: [
|
|
85
|
+
'commit',
|
|
86
|
+
'--quiet',
|
|
87
|
+
'--no-verify',
|
|
88
|
+
'-m',
|
|
89
|
+
message,
|
|
90
|
+
'--',
|
|
91
|
+
...pathspecs,
|
|
92
|
+
],
|
|
93
|
+
})
|
|
94
|
+
const sha = await head({ cwd })
|
|
95
|
+
return { sha, files: await filesOf({ cwd, sha }) }
|
|
96
|
+
},
|
|
97
|
+
discardChanges: async ({ cwd }) => {
|
|
98
|
+
await gitOk({ cwd, args: ['reset', '--quiet', '--hard', 'HEAD'] })
|
|
99
|
+
await gitOk({ cwd, args: ['clean', '--quiet', '-f', '-d'] })
|
|
100
|
+
return { sha: await head({ cwd }) }
|
|
101
|
+
},
|
|
102
|
+
resetWorktree: async ({ cwd, to }) => {
|
|
103
|
+
// A cherry-pick a crash cut off leaves its sequence behind.
|
|
104
|
+
await gitRun({ cwd, args: ['cherry-pick', '--quit'] })
|
|
105
|
+
await gitOk({ cwd, args: ['reset', '--quiet', '--hard', to] })
|
|
106
|
+
await gitOk({ cwd, args: ['clean', '--quiet', '-f', '-d'] })
|
|
107
|
+
return { sha: await head({ cwd }) }
|
|
108
|
+
},
|
|
109
|
+
push: async ({ cwd, branch }) => {
|
|
110
|
+
await gitOk({
|
|
111
|
+
cwd,
|
|
112
|
+
args: [
|
|
113
|
+
'push',
|
|
114
|
+
'--quiet',
|
|
115
|
+
'origin',
|
|
116
|
+
`${branch}:refs/heads/${branch}`,
|
|
117
|
+
],
|
|
118
|
+
})
|
|
119
|
+
},
|
|
120
|
+
lastCommit: async ({ cwd }) => {
|
|
121
|
+
const sha = await head({ cwd })
|
|
122
|
+
const message = await gitOk({
|
|
123
|
+
cwd,
|
|
124
|
+
args: ['log', '-1', '--format=%B', sha],
|
|
125
|
+
})
|
|
126
|
+
return {
|
|
127
|
+
sha,
|
|
128
|
+
message: message.trim(),
|
|
129
|
+
files: await filesOf({ cwd, sha }),
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
}
|