just-git 1.0.2 → 1.1.0

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/README.md CHANGED
@@ -46,7 +46,8 @@ await bash.exec("git log --oneline");
46
46
  | `credentials` | `(url) => HttpAuth \| null` callback for Smart HTTP transport auth. |
47
47
  | `disabled` | `GitCommandName[]` of subcommands to block (e.g. `["push", "rebase"]`). |
48
48
  | `network` | `{ allowed?: string[], fetch?: FetchFunction }` to restrict HTTP access and/or provide a custom `fetch`. `allowed` accepts hostnames (`"github.com"`) or URL prefixes (`"https://github.com/myorg/"`). Set to `false` to block all network access. |
49
- | `resolveRemote` | `(url) => GitContext \| null` callback for cross-VFS remote resolution. See [Multi-agent collaboration](#multi-agent-collaboration). |
49
+ | `hooks` | `GitHooks` config object with named callback properties. See [Hooks](#hooks). |
50
+ | `resolveRemote` | `(url) => GitRepo \| null` callback for cross-VFS remote resolution. See [Multi-agent collaboration](#multi-agent-collaboration). |
50
51
 
51
52
  ```ts
52
53
  const git = createGit({
@@ -57,80 +58,73 @@ const git = createGit({
57
58
  });
58
59
  ```
59
60
 
60
- ## Middleware
61
+ ## Hooks
62
+
63
+ Hooks fire at specific points inside command execution. Specified as a `GitHooks` config object at construction time. All hook event payloads include `repo: GitRepo`, providing access to the [repo module helpers](src/repo/) inside hooks.
61
64
 
62
- Middleware wraps every `git <subcommand>` invocation. Each middleware receives a `CommandEvent` and a `next()` function. Call `next()` to proceed, or return an `ExecResult` to short-circuit. Middlewares compose in registration order (first registered = outermost). `git.use()` returns an unsubscribe function.
65
+ Pre-hooks can reject the operation by returning `{ reject: true, message? }`. Post-hooks are observational return value is ignored.
63
66
 
64
67
  ```ts
65
- // Audit log record every command the agent runs
66
- git.use(async (event, next) => {
67
- const result = await next();
68
- auditLog.push({ command: `git ${event.command}`, exitCode: result.exitCode });
69
- return result;
70
- });
68
+ import { createGit, type GitHooks } from "just-git";
69
+ import { getChangedFiles } from "just-git/repo";
71
70
 
72
- // Gate pushes on human approval
73
- git.use(async (event, next) => {
74
- if (event.command === "push" && !(await getHumanApproval(event.rawArgs))) {
75
- return { stdout: "", stderr: "Push blocked — awaiting approval.\n", exitCode: 1 };
76
- }
77
- return next();
78
- });
71
+ const git = createGit({
72
+ hooks: {
73
+ // Block secrets from being committed
74
+ preCommit: ({ index }) => {
75
+ const forbidden = index.entries.filter((e) => /\.(env|pem|key)$/.test(e.path));
76
+ if (forbidden.length) {
77
+ return { reject: true, message: `Blocked: ${forbidden.map((e) => e.path).join(", ")}` };
78
+ }
79
+ },
79
80
 
80
- // Block commits that add large files (uses event.fs to read the worktree)
81
- git.use(async (event, next) => {
82
- if (event.command === "add") {
83
- for (const path of event.rawArgs.filter((a) => !a.startsWith("-"))) {
84
- const resolved = path.startsWith("/") ? path : `${event.cwd}/${path}`;
85
- const stat = await event.fs.stat(resolved).catch(() => null);
86
- if (stat && stat.size > 5_000_000) {
87
- return { stdout: "", stderr: `Blocked: ${path} exceeds 5 MB\n`, exitCode: 1 };
81
+ // Enforce conventional commit messages
82
+ commitMsg: (event) => {
83
+ if (!/^(feat|fix|docs|refactor|test|chore)(\(.+\))?:/.test(event.message)) {
84
+ return { reject: true, message: "Commit message must follow conventional commits format" };
88
85
  }
89
- }
90
- }
91
- return next();
86
+ },
87
+
88
+ // Feed agent activity to your UI — with changed file list
89
+ postCommit: async ({ repo, hash, branch, parents }) => {
90
+ const files = await getChangedFiles(repo, parents[0] ?? null, hash);
91
+ onAgentCommit({ hash, branch, changedFiles: files });
92
+ },
93
+
94
+ // Audit log — record every command
95
+ afterCommand: ({ command, args, result }) => {
96
+ auditLog.push({ command: `git ${command}`, exitCode: result.exitCode });
97
+ },
98
+
99
+ // Gate pushes on human approval
100
+ beforeCommand: async ({ command }) => {
101
+ if (command === "push" && !(await getHumanApproval())) {
102
+ return { reject: true, message: "Push blocked — awaiting approval." };
103
+ }
104
+ },
105
+ },
92
106
  });
93
107
  ```
94
108
 
95
- ## Hooks
96
-
97
- Hooks fire at specific points inside command execution (after middleware, inside operation logic). Register with `git.on(event, handler)`, which returns an unsubscribe function.
98
-
99
- Pre-hooks can abort the operation by returning `{ abort: true, message? }`. Post-hooks are observational — return value is ignored.
109
+ Use `composeGitHooks()` to combine multiple hook sets:
100
110
 
101
111
  ```ts
102
- // Block secrets from being committed
103
- git.on("pre-commit", (event) => {
104
- const forbidden = event.index.entries.filter((e) => /\.(env|pem|key)$/.test(e.path));
105
- if (forbidden.length) {
106
- return { abort: true, message: `Blocked: ${forbidden.map((e) => e.path).join(", ")}` };
107
- }
108
- });
109
-
110
- // Enforce conventional commit messages
111
- git.on("commit-msg", (event) => {
112
- if (!/^(feat|fix|docs|refactor|test|chore)(\(.+\))?:/.test(event.message)) {
113
- return { abort: true, message: "Commit message must follow conventional commits format" };
114
- }
115
- });
112
+ import { createGit, composeGitHooks } from "just-git";
116
113
 
117
- // Feed agent activity to your UI or orchestration layer
118
- git.on("post-commit", (event) => {
119
- onAgentCommit({ hash: event.hash, branch: event.branch, message: event.message });
114
+ const git = createGit({
115
+ hooks: composeGitHooks(auditHooks, policyHooks, loggingHooks),
120
116
  });
121
117
  ```
122
118
 
123
- Available pre-hooks: `pre-commit`, `commit-msg`, `merge-msg`, `pre-merge-commit`, `pre-checkout`, `pre-push`, `pre-fetch`, `pre-clone`, `pre-pull`, `pre-rebase`, `pre-reset`, `pre-clean`, `pre-rm`, `pre-cherry-pick`, `pre-revert`, `pre-stash`. Available post-hooks: `post-commit`, `post-merge`, `post-checkout`, `post-push`, `post-fetch`, `post-clone`, `post-pull`, `post-reset`, `post-clean`, `post-rm`, `post-cherry-pick`, `post-revert`, `post-stash`. Low-level events: `ref:update`, `ref:delete`, `object:write`.
124
-
125
- See [HOOKS.md](HOOKS.md) for full payload types and the `CommandEvent` shape.
119
+ Available pre-hooks: `preCommit`, `commitMsg`, `mergeMsg`, `preMergeCommit`, `preCheckout`, `prePush`, `preFetch`, `preClone`, `prePull`, `preRebase`, `preReset`, `preClean`, `preRm`, `preCherryPick`, `preRevert`, `preStash`. Available post-hooks: `postCommit`, `postMerge`, `postCheckout`, `postPush`, `postFetch`, `postClone`, `postPull`, `postReset`, `postClean`, `postRm`, `postCherryPick`, `postRevert`, `postStash`. Low-level events: `onRefUpdate`, `onRefDelete`, `onObjectWrite`. Command-level: `beforeCommand`, `afterCommand`.
126
120
 
127
121
  ## Multi-agent collaboration
128
122
 
129
- Multiple agents can work on clones of the same repository in the same process, each with full VFS isolation. The `resolveRemote` option maps remote URLs to `GitContext` instances on other virtual filesystems, so clone/fetch/push/pull cross VFS boundaries without any network or shared filesystem.
123
+ Multiple agents can work on clones of the same repository in the same process, each with full VFS isolation. The `resolveRemote` option maps remote URLs to `GitRepo` instances (any object/ref store — VFS-backed, SQLite, etc.), so clone/fetch/push/pull cross VFS boundaries without any network or shared filesystem.
130
124
 
131
125
  ```ts
132
126
  import { Bash, InMemoryFs } from "just-bash";
133
- import { createGit, findGitDir } from "just-git";
127
+ import { createGit, findRepo } from "just-git";
134
128
 
135
129
  // Origin repo on its own filesystem
136
130
  const originFs = new InMemoryFs();
@@ -145,17 +139,13 @@ await setupBash.exec("git init");
145
139
  await setupBash.exec("echo 'hello' > README.md");
146
140
  await setupBash.exec("git add . && git commit -m 'initial'");
147
141
 
148
- const originCtx = await findGitDir(originFs, "/repo");
149
- const resolve = (url: string) => (url === "/origin" ? originCtx : null);
150
-
151
- // Each agent gets its own filesystem + resolveRemote pointing to origin
152
142
  const alice = new Bash({
153
143
  fs: new InMemoryFs(),
154
144
  cwd: "/repo",
155
145
  customCommands: [
156
146
  createGit({
157
147
  identity: { name: "Alice", email: "alice@example.com", locked: true },
158
- resolveRemote: resolve,
148
+ resolveRemote: () => findRepo(originFs, "/repo"),
159
149
  }),
160
150
  ],
161
151
  });
@@ -166,7 +156,7 @@ const bob = new Bash({
166
156
  customCommands: [
167
157
  createGit({
168
158
  identity: { name: "Bob", email: "bob@example.com", locked: true },
169
- resolveRemote: resolve,
159
+ resolveRemote: () => findRepo(originFs, "/repo"),
170
160
  }),
171
161
  ],
172
162
  });
@@ -0,0 +1,480 @@
1
+ interface FileStat {
2
+ isFile: boolean;
3
+ isDirectory: boolean;
4
+ isSymbolicLink: boolean;
5
+ mode: number;
6
+ size: number;
7
+ mtime: Date;
8
+ }
9
+ interface FileSystem {
10
+ readFile(path: string): Promise<string>;
11
+ readFileBuffer(path: string): Promise<Uint8Array>;
12
+ writeFile(path: string, content: string | Uint8Array): Promise<void>;
13
+ exists(path: string): Promise<boolean>;
14
+ stat(path: string): Promise<FileStat>;
15
+ mkdir(path: string, options?: {
16
+ recursive?: boolean;
17
+ }): Promise<void>;
18
+ readdir(path: string): Promise<string[]>;
19
+ rm(path: string, options?: {
20
+ recursive?: boolean;
21
+ force?: boolean;
22
+ }): Promise<void>;
23
+ /** Stat without following symlinks. Falls back to stat() semantics when not implemented. */
24
+ lstat?(path: string): Promise<FileStat>;
25
+ /** Read the target of a symbolic link. */
26
+ readlink?(path: string): Promise<string>;
27
+ /** Create a symbolic link pointing to target at the given path. */
28
+ symlink?(target: string, path: string): Promise<void>;
29
+ }
30
+
31
+ /** 40-character lowercase hex SHA-1 hash. */
32
+ type ObjectId = string;
33
+ /** The four Git object types. */
34
+ type ObjectType = "blob" | "tree" | "commit" | "tag";
35
+ /** An object as stored in .git/objects — type + raw content bytes. */
36
+ interface RawObject {
37
+ type: ObjectType;
38
+ content: Uint8Array;
39
+ }
40
+ /** Author or committer identity with timestamp. */
41
+ interface Identity {
42
+ name: string;
43
+ email: string;
44
+ /** Unix epoch seconds. */
45
+ timestamp: number;
46
+ /** Timezone offset string, e.g. "+0000", "-0500". */
47
+ timezone: string;
48
+ }
49
+ interface Commit {
50
+ type: "commit";
51
+ tree: ObjectId;
52
+ parents: ObjectId[];
53
+ author: Identity;
54
+ committer: Identity;
55
+ message: string;
56
+ }
57
+ interface SymbolicRef {
58
+ type: "symbolic";
59
+ /** The ref path this points to, e.g. "refs/heads/main". */
60
+ target: string;
61
+ }
62
+ interface DirectRef {
63
+ type: "direct";
64
+ hash: ObjectId;
65
+ }
66
+ type Ref = SymbolicRef | DirectRef;
67
+ /** Stat-like metadata stored per index entry. */
68
+ interface IndexStat {
69
+ ctimeSeconds: number;
70
+ ctimeNanoseconds: number;
71
+ mtimeSeconds: number;
72
+ mtimeNanoseconds: number;
73
+ dev: number;
74
+ ino: number;
75
+ uid: number;
76
+ gid: number;
77
+ size: number;
78
+ }
79
+ interface IndexEntry {
80
+ /** File path relative to the work tree root. */
81
+ path: string;
82
+ /** File mode as a numeric value (e.g. 0o100644). */
83
+ mode: number;
84
+ /** SHA-1 of the blob content. */
85
+ hash: ObjectId;
86
+ /** Merge stage: 0 = normal, 1 = base, 2 = ours, 3 = theirs. */
87
+ stage: number;
88
+ stat: IndexStat;
89
+ }
90
+ interface Index {
91
+ version: number;
92
+ entries: IndexEntry[];
93
+ }
94
+ interface RefEntry {
95
+ name: string;
96
+ hash: ObjectId;
97
+ }
98
+ /**
99
+ * Abstract ref storage backend.
100
+ * Implementations handle reading, writing, deleting, and listing git refs.
101
+ * The default filesystem-backed implementation is `FileSystemRefStore`.
102
+ */
103
+ interface RefStore {
104
+ /** Read a single ref without following symbolic refs. */
105
+ readRef(name: string): Promise<Ref | null>;
106
+ /** Write a ref (direct or symbolic). */
107
+ writeRef(name: string, ref: Ref): Promise<void>;
108
+ /** Delete a ref from storage. */
109
+ deleteRef(name: string): Promise<void>;
110
+ /** List all refs under a prefix, returning resolved hashes. */
111
+ listRefs(prefix?: string): Promise<RefEntry[]>;
112
+ /**
113
+ * Atomically update a ref only if its current resolved hash matches
114
+ * `expectedOldHash`. Returns true on success, false if the ref has
115
+ * been modified concurrently.
116
+ *
117
+ * - `expectedOldHash === null` — create-only: fails if the ref exists.
118
+ * - `expectedOldHash === "<hash>"` — fails if current hash !== expected.
119
+ * - `newRef === null` — conditional delete.
120
+ * - `newRef === Ref` — conditional create/update.
121
+ */
122
+ compareAndSwapRef(name: string, expectedOldHash: string | null, newRef: Ref | null): Promise<boolean>;
123
+ }
124
+ /**
125
+ * Abstract object storage backend.
126
+ * Implementations handle reading, writing, and querying git objects.
127
+ * The default filesystem-backed implementation is `PackedObjectStore`.
128
+ */
129
+ interface ObjectStore {
130
+ read(hash: ObjectId): Promise<RawObject>;
131
+ write(type: ObjectType, content: Uint8Array): Promise<ObjectId>;
132
+ exists(hash: ObjectId): Promise<boolean>;
133
+ ingestPack(packData: Uint8Array): Promise<number>;
134
+ /** Return all object hashes matching a hex prefix (for short hash resolution). */
135
+ findByPrefix(prefix: string): Promise<ObjectId[]>;
136
+ /**
137
+ * Signal that pack files on disk have changed externally (e.g. after
138
+ * repack or gc). Implementations should discard cached pack state
139
+ * and re-scan on the next read.
140
+ */
141
+ invalidatePacks?(): void;
142
+ }
143
+ /**
144
+ * Minimal repository handle: object store + ref store + hooks.
145
+ * Sufficient for all pure object/ref operations (read, write, walk,
146
+ * diff trees, merge-base, blame, etc.) without filesystem access.
147
+ *
148
+ * Used directly by the server module and accepted by ~35 lib functions.
149
+ */
150
+ interface GitRepo {
151
+ objectStore: ObjectStore;
152
+ refStore: RefStore;
153
+ /** Hook callbacks for operation hooks and low-level events. */
154
+ hooks?: GitHooks;
155
+ }
156
+ /**
157
+ * Resolves a remote URL to a GitRepo, enabling cross-VFS transport.
158
+ * Called before local filesystem lookup for non-HTTP URLs.
159
+ * Return null to fall back to local filesystem resolution.
160
+ */
161
+ type RemoteResolver = (url: string) => GitRepo | null | Promise<GitRepo | null>;
162
+ /**
163
+ * Full repository context including filesystem access.
164
+ * Extends `GitRepo` with the filesystem handle, resolved paths,
165
+ * and operator-level extensions (credentials, identity, network).
166
+ *
167
+ * Threaded through command handlers and lib functions that need
168
+ * worktree/index/config/reflog access.
169
+ */
170
+ interface GitContext extends GitRepo {
171
+ fs: FileSystem;
172
+ /** Absolute path to the .git directory. */
173
+ gitDir: string;
174
+ /** Absolute path to the working tree root, or null for bare repos. */
175
+ workTree: string | null;
176
+ /** Operator-provided credential resolver (bypasses env vars). */
177
+ credentialProvider?: CredentialProvider;
178
+ /** Operator-provided identity override for author/committer. */
179
+ identityOverride?: IdentityOverride;
180
+ /** Custom fetch function for HTTP transport. Falls back to globalThis.fetch. */
181
+ fetchFn?: FetchFunction;
182
+ /** Network access policy. `false` blocks all HTTP access. */
183
+ networkPolicy?: NetworkPolicy | false;
184
+ /** Resolves remote URLs to GitRepos on potentially different VFS instances. */
185
+ resolveRemote?: RemoteResolver;
186
+ }
187
+ type DiffStatus = "added" | "deleted" | "modified";
188
+ interface TreeDiffEntry {
189
+ path: string;
190
+ status: DiffStatus;
191
+ /** Hash in tree A (undefined if added). */
192
+ oldHash?: ObjectId;
193
+ /** Hash in tree B (undefined if deleted). */
194
+ newHash?: ObjectId;
195
+ oldMode?: string;
196
+ newMode?: string;
197
+ }
198
+
199
+ type HttpAuth = {
200
+ type: "basic";
201
+ username: string;
202
+ password: string;
203
+ } | {
204
+ type: "bearer";
205
+ token: string;
206
+ };
207
+
208
+ interface ExecResult {
209
+ stdout: string;
210
+ stderr: string;
211
+ exitCode: number;
212
+ }
213
+
214
+ type CredentialProvider = (url: string) => HttpAuth | null | Promise<HttpAuth | null>;
215
+ interface IdentityOverride {
216
+ name: string;
217
+ email: string;
218
+ locked?: boolean;
219
+ }
220
+ type FetchFunction = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
221
+ interface NetworkPolicy {
222
+ /**
223
+ * Allowed URL patterns. Can be:
224
+ * - A hostname: "github.com" (matches any URL whose host equals this)
225
+ * - A URL prefix: "https://github.com/myorg/" (matches URLs starting with this)
226
+ */
227
+ allowed?: string[];
228
+ /** Custom fetch function for HTTP transport. Falls back to globalThis.fetch. */
229
+ fetch?: FetchFunction;
230
+ }
231
+ interface Rejection {
232
+ reject: true;
233
+ message?: string;
234
+ }
235
+ declare function isRejection(value: unknown): value is Rejection;
236
+ interface PreCommitEvent {
237
+ readonly repo: GitRepo;
238
+ readonly index: Index;
239
+ readonly treeHash: ObjectId;
240
+ }
241
+ interface CommitMsgEvent {
242
+ readonly repo: GitRepo;
243
+ message: string;
244
+ }
245
+ interface MergeMsgEvent {
246
+ readonly repo: GitRepo;
247
+ message: string;
248
+ readonly treeHash: ObjectId;
249
+ readonly headHash: ObjectId;
250
+ readonly theirsHash: ObjectId;
251
+ }
252
+ interface PostCommitEvent {
253
+ readonly repo: GitRepo;
254
+ readonly hash: ObjectId;
255
+ readonly message: string;
256
+ readonly branch: string | null;
257
+ readonly parents: readonly ObjectId[];
258
+ readonly author: Identity;
259
+ }
260
+ interface PreMergeCommitEvent {
261
+ readonly repo: GitRepo;
262
+ readonly mergeMessage: string;
263
+ readonly treeHash: ObjectId;
264
+ readonly headHash: ObjectId;
265
+ readonly theirsHash: ObjectId;
266
+ }
267
+ interface PostMergeEvent {
268
+ readonly repo: GitRepo;
269
+ readonly headHash: ObjectId;
270
+ readonly theirsHash: ObjectId;
271
+ readonly strategy: "fast-forward" | "three-way";
272
+ readonly commitHash: ObjectId | null;
273
+ }
274
+ interface PostCheckoutEvent {
275
+ readonly repo: GitRepo;
276
+ readonly prevHead: ObjectId | null;
277
+ readonly newHead: ObjectId;
278
+ readonly isBranchCheckout: boolean;
279
+ }
280
+ interface PrePushEvent {
281
+ readonly repo: GitRepo;
282
+ readonly remote: string;
283
+ readonly url: string;
284
+ readonly refs: ReadonlyArray<{
285
+ srcRef: string | null;
286
+ srcHash: ObjectId | null;
287
+ dstRef: string;
288
+ dstHash: ObjectId | null;
289
+ force: boolean;
290
+ delete: boolean;
291
+ }>;
292
+ }
293
+ type PostPushEvent = PrePushEvent;
294
+ interface PreRebaseEvent {
295
+ readonly repo: GitRepo;
296
+ readonly upstream: string;
297
+ readonly branch: string | null;
298
+ }
299
+ interface PreCheckoutEvent {
300
+ readonly repo: GitRepo;
301
+ readonly target: string;
302
+ readonly mode: "switch" | "detach" | "create-branch" | "paths";
303
+ }
304
+ interface PreFetchEvent {
305
+ readonly repo: GitRepo;
306
+ readonly remote: string;
307
+ readonly url: string;
308
+ readonly refspecs: readonly string[];
309
+ readonly prune: boolean;
310
+ readonly tags: boolean;
311
+ }
312
+ interface PostFetchEvent {
313
+ readonly repo: GitRepo;
314
+ readonly remote: string;
315
+ readonly url: string;
316
+ readonly refsUpdated: number;
317
+ }
318
+ interface PreCloneEvent {
319
+ readonly repo?: GitRepo;
320
+ readonly repository: string;
321
+ readonly targetPath: string;
322
+ readonly bare: boolean;
323
+ readonly branch: string | null;
324
+ }
325
+ interface PostCloneEvent {
326
+ readonly repo: GitRepo;
327
+ readonly repository: string;
328
+ readonly targetPath: string;
329
+ readonly bare: boolean;
330
+ readonly branch: string | null;
331
+ }
332
+ interface PrePullEvent {
333
+ readonly repo: GitRepo;
334
+ readonly remote: string;
335
+ readonly branch: string | null;
336
+ }
337
+ interface PostPullEvent {
338
+ readonly repo: GitRepo;
339
+ readonly remote: string;
340
+ readonly branch: string | null;
341
+ readonly strategy: "up-to-date" | "fast-forward" | "three-way";
342
+ readonly commitHash: ObjectId | null;
343
+ }
344
+ interface PreResetEvent {
345
+ readonly repo: GitRepo;
346
+ readonly mode: "soft" | "mixed" | "hard" | "paths";
347
+ readonly target: string | null;
348
+ }
349
+ interface PostResetEvent {
350
+ readonly repo: GitRepo;
351
+ readonly mode: "soft" | "mixed" | "hard" | "paths";
352
+ readonly targetHash: ObjectId | null;
353
+ }
354
+ interface PreCleanEvent {
355
+ readonly repo: GitRepo;
356
+ readonly dryRun: boolean;
357
+ readonly force: boolean;
358
+ readonly removeDirs: boolean;
359
+ readonly removeIgnored: boolean;
360
+ readonly onlyIgnored: boolean;
361
+ }
362
+ interface PostCleanEvent {
363
+ readonly repo: GitRepo;
364
+ readonly removed: readonly string[];
365
+ readonly dryRun: boolean;
366
+ }
367
+ interface PreRmEvent {
368
+ readonly repo: GitRepo;
369
+ readonly paths: readonly string[];
370
+ readonly cached: boolean;
371
+ readonly recursive: boolean;
372
+ readonly force: boolean;
373
+ }
374
+ interface PostRmEvent {
375
+ readonly repo: GitRepo;
376
+ readonly removedPaths: readonly string[];
377
+ readonly cached: boolean;
378
+ }
379
+ interface PreCherryPickEvent {
380
+ readonly repo: GitRepo;
381
+ readonly mode: "pick" | "continue" | "abort";
382
+ readonly commit: string | null;
383
+ }
384
+ interface PostCherryPickEvent {
385
+ readonly repo: GitRepo;
386
+ readonly mode: "pick" | "continue" | "abort";
387
+ readonly commitHash: ObjectId | null;
388
+ readonly hadConflicts: boolean;
389
+ }
390
+ interface PreRevertEvent {
391
+ readonly repo: GitRepo;
392
+ readonly mode: "revert" | "continue" | "abort";
393
+ readonly commit: string | null;
394
+ }
395
+ interface PostRevertEvent {
396
+ readonly repo: GitRepo;
397
+ readonly mode: "revert" | "continue" | "abort";
398
+ readonly commitHash: ObjectId | null;
399
+ readonly hadConflicts: boolean;
400
+ }
401
+ interface PreStashEvent {
402
+ readonly repo: GitRepo;
403
+ readonly action: "push" | "pop" | "apply" | "list" | "drop" | "show" | "clear";
404
+ readonly ref: string | null;
405
+ }
406
+ interface PostStashEvent {
407
+ readonly repo: GitRepo;
408
+ readonly action: "push" | "pop" | "apply" | "list" | "drop" | "show" | "clear";
409
+ readonly ok: boolean;
410
+ }
411
+ interface RefUpdateEvent {
412
+ readonly repo: GitRepo;
413
+ readonly ref: string;
414
+ readonly oldHash: ObjectId | null;
415
+ readonly newHash: ObjectId;
416
+ }
417
+ interface RefDeleteEvent {
418
+ readonly repo: GitRepo;
419
+ readonly ref: string;
420
+ readonly oldHash: ObjectId | null;
421
+ }
422
+ interface ObjectWriteEvent {
423
+ readonly repo: GitRepo;
424
+ readonly type: ObjectType;
425
+ readonly hash: ObjectId;
426
+ }
427
+
428
+ interface BeforeCommandEvent {
429
+ readonly command: string;
430
+ readonly args: string[];
431
+ readonly fs: FileSystem;
432
+ readonly cwd: string;
433
+ readonly env: Map<string, string>;
434
+ }
435
+ interface AfterCommandEvent {
436
+ readonly command: string;
437
+ readonly args: string[];
438
+ readonly result: ExecResult;
439
+ }
440
+ type PreHookReturn = void | Rejection | Promise<void | Rejection>;
441
+ type PostHookReturn = void | Promise<void>;
442
+ interface GitHooks {
443
+ preCommit?: (event: PreCommitEvent) => PreHookReturn;
444
+ commitMsg?: (event: CommitMsgEvent) => PreHookReturn;
445
+ mergeMsg?: (event: MergeMsgEvent) => PreHookReturn;
446
+ preMergeCommit?: (event: PreMergeCommitEvent) => PreHookReturn;
447
+ preCheckout?: (event: PreCheckoutEvent) => PreHookReturn;
448
+ prePush?: (event: PrePushEvent) => PreHookReturn;
449
+ preFetch?: (event: PreFetchEvent) => PreHookReturn;
450
+ preClone?: (event: PreCloneEvent) => PreHookReturn;
451
+ prePull?: (event: PrePullEvent) => PreHookReturn;
452
+ preRebase?: (event: PreRebaseEvent) => PreHookReturn;
453
+ preReset?: (event: PreResetEvent) => PreHookReturn;
454
+ preClean?: (event: PreCleanEvent) => PreHookReturn;
455
+ preRm?: (event: PreRmEvent) => PreHookReturn;
456
+ preCherryPick?: (event: PreCherryPickEvent) => PreHookReturn;
457
+ preRevert?: (event: PreRevertEvent) => PreHookReturn;
458
+ preStash?: (event: PreStashEvent) => PreHookReturn;
459
+ postCommit?: (event: PostCommitEvent) => PostHookReturn;
460
+ postMerge?: (event: PostMergeEvent) => PostHookReturn;
461
+ postCheckout?: (event: PostCheckoutEvent) => PostHookReturn;
462
+ postPush?: (event: PostPushEvent) => PostHookReturn;
463
+ postFetch?: (event: PostFetchEvent) => PostHookReturn;
464
+ postClone?: (event: PostCloneEvent) => PostHookReturn;
465
+ postPull?: (event: PostPullEvent) => PostHookReturn;
466
+ postReset?: (event: PostResetEvent) => PostHookReturn;
467
+ postClean?: (event: PostCleanEvent) => PostHookReturn;
468
+ postRm?: (event: PostRmEvent) => PostHookReturn;
469
+ postCherryPick?: (event: PostCherryPickEvent) => PostHookReturn;
470
+ postRevert?: (event: PostRevertEvent) => PostHookReturn;
471
+ postStash?: (event: PostStashEvent) => PostHookReturn;
472
+ onRefUpdate?: (event: RefUpdateEvent) => void;
473
+ onRefDelete?: (event: RefDeleteEvent) => void;
474
+ onObjectWrite?: (event: ObjectWriteEvent) => void;
475
+ beforeCommand?: (event: BeforeCommandEvent) => PreHookReturn;
476
+ afterCommand?: (event: AfterCommandEvent) => PostHookReturn;
477
+ }
478
+ declare function composeGitHooks(...hookSets: (GitHooks | undefined)[]): GitHooks;
479
+
480
+ export { type Commit as $, type AfterCommandEvent as A, type BeforeCommandEvent as B, type CredentialProvider as C, type PrePullEvent as D, type ExecResult as E, type FileSystem as F, type GitHooks as G, type HttpAuth as H, type IdentityOverride as I, type PrePushEvent as J, type PreRebaseEvent as K, type PreResetEvent as L, type MergeMsgEvent as M, type NetworkPolicy as N, type ObjectStore as O, type PostCheckoutEvent as P, type PreRevertEvent as Q, type RemoteResolver as R, type PreRmEvent as S, type PreStashEvent as T, type RefDeleteEvent as U, type RefEntry as V, type RefUpdateEvent as W, type Rejection as X, composeGitHooks as Y, isRejection as Z, type ObjectId as _, type RefStore as a, type Identity as a0, type TreeDiffEntry as a1, type ObjectType as a2, type RawObject as a3, type Ref as a4, type FetchFunction as b, type GitContext as c, type CommitMsgEvent as d, type FileStat as e, type GitRepo as f, type ObjectWriteEvent as g, type PostCherryPickEvent as h, type PostCleanEvent as i, type PostCloneEvent as j, type PostCommitEvent as k, type PostFetchEvent as l, type PostMergeEvent as m, type PostPullEvent as n, type PostPushEvent as o, type PostResetEvent as p, type PostRevertEvent as q, type PostRmEvent as r, type PostStashEvent as s, type PreCheckoutEvent as t, type PreCherryPickEvent as u, type PreCleanEvent as v, type PreCloneEvent as w, type PreCommitEvent as x, type PreFetchEvent as y, type PreMergeCommitEvent as z };