Files
airship/packages/git/package.json
T
Nayan 8e0c91c5dc fix(git): carry every failure's reason, and stop undo deleting files
packages/git funnelled every invocation through `catch { return null }`, so a
missing commit identity, a stale index.lock, a pathspec that matched nothing
and a git that was not installed all reached the user as "commit failed" — and
a git that could not run at all was reported as "not a git repository", which
is what a Windows contributor hit after installing Git for Git Bash only.

That null had a second meaning. `fileAtHead` returned it both for "this file
was not in HEAD" and for "we could not ask", so with git unavailable every
edited file was recorded `isNew` and undo deletes what an edit created: on the
codex and opencode paths, Revert deleted pre-existing tracked source files.
`HeadRead` splits the two, `FileDiff.noBaseline` carries the difference, and
`restoreFiles` checks it before `isNew` and refuses.

One `run()` now keeps exit code, stderr and spawn errno, and `failureText`
turns them into the one line a toast has room for. `stdout` is half its input,
not a fallback: `git commit` with nothing staged exits 1 and says so on stdout.

Also here, each its own small correctness fix:

- `cat-file --filters` instead of `show`, so `before` is the working-tree form.
  The stored blob is LF whatever the tree is, and undo was writing that back
  over CRLF files, rewriting every line ending in them.
- `:(literal)` pathspecs. `git add -- 'a[1].ts'` also stages an unrelated
  `a1.ts`, so one edited file could sweep untouched work into the auto-commit.
- A binary blob is `unavailable` rather than decoded through U+FFFD and then
  written back corrupted.
- `restoreFiles` cannot throw. `undo` is called straight out of the WebSocket
  message handler, so an EACCES there took the whole daemon down.
- `push` and `gh pr create` get a timeout, a closed stdin and
  `GIT_TERMINAL_PROMPT=0`. Git Credential Manager opens a GUI behind the
  browser on Windows, and the awaiting handler never replied.
- `LC_ALL=C`, so classifying "absent from HEAD" does not depend on git's
  messages being untranslated.
- One 64MB buffer ceiling for every call, not just the blob read.

`gitStatus()` is the answer that can be shown to someone: not installed, not a
work tree, bare, no commits yet, no identity. `ghStatus` gains the same
precision and a cwd, since `gh auth status` resolves its host from the remote.

The package had no tests at all, which is how the data-loss bug shipped. It has
34 now, including the reported scenario driven with PATH scrubbed of git.
2026-08-16 13:05:37 +05:30

29 lines
683 B
JSON

{
"name": "@airship/git",
"version": "0.0.0",
"private": true,
"type": "module",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"scripts": {
"build": "tsup src/index.ts --format esm --dts --sourcemap --clean",
"clean": "node ../../scripts/clean.mjs dist .turbo",
"dev": "tsup src/index.ts --format esm --dts --sourcemap --watch",
"test": "vitest run",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@airship/protocol": "workspace:*"
},
"devDependencies": {
"@types/node": "^26.2.0",
"vitest": "^4.1.10"
}
}