git-chopstick-core 0.1.6 → 0.1.8
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 +17 -6
- package/README.md +12 -9
- package/docs/exec-to-git-migration.md +75 -0
- package/docs/skills/release.md +82 -0
- package/package.json +5 -3
- package/src/__tests__/integration.test.ts +156 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.1.8] — 2026-06-13
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- **Test files included in package**: Added `src/__tests__/` to the `files` array in `package.json`. Integration tests ship with the installed package so consumers can run `npm test` to verify the package works for their environment.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## [0.1.7] — 2026-06-13
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **Documentation included in package**: Added `docs/` to the `files` array in `package.json`. The migration guide (`docs/exec-to-git-migration.md`) and release skill (`docs/skills/release.md`) now ship with the installed package.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
3
17
|
## [0.1.6] — 2026-06-13
|
|
4
18
|
|
|
5
19
|
### Fixed
|
|
@@ -115,11 +129,8 @@
|
|
|
115
129
|
|
|
116
130
|
---
|
|
117
131
|
|
|
118
|
-
[0.1.
|
|
119
|
-
[0.1.
|
|
120
|
-
[0.1.2]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.2
|
|
121
|
-
[0.1.1]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.1
|
|
122
|
-
[0.1.0]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.0
|
|
132
|
+
[0.1.8]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.8
|
|
133
|
+
[0.1.7]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.7
|
|
123
134
|
[0.1.6]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.6
|
|
124
135
|
[0.1.5]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.5
|
|
125
136
|
[0.1.4]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.4
|
|
@@ -127,4 +138,4 @@
|
|
|
127
138
|
[0.1.2]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.2
|
|
128
139
|
[0.1.1]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.1
|
|
129
140
|
[0.1.0]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.0
|
|
130
|
-
[Unreleased]: https://github.com/parkiyong/git-chopstick-core/compare/v0.1.
|
|
141
|
+
[Unreleased]: https://github.com/parkiyong/git-chopstick-core/compare/v0.1.8...HEAD
|
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ npm install file:../path/to/git-chopstick-core
|
|
|
26
26
|
```typescript
|
|
27
27
|
import { Repository, getStatus, GitError } from 'git-chopstick-core'
|
|
28
28
|
|
|
29
|
-
const repo = new Repository('/path/to/repo'
|
|
29
|
+
const repo = new Repository('/path/to/repo')
|
|
30
30
|
|
|
31
31
|
try {
|
|
32
32
|
const status = await getStatus(repo)
|
|
@@ -46,7 +46,7 @@ try {
|
|
|
46
46
|
```typescript
|
|
47
47
|
import { Repository, getStatus } from 'git-chopstick-core'
|
|
48
48
|
|
|
49
|
-
const repo = new Repository('/path/to/repo'
|
|
49
|
+
const repo = new Repository('/path/to/repo')
|
|
50
50
|
const status = await getStatus(repo)
|
|
51
51
|
|
|
52
52
|
if (!status) {
|
|
@@ -81,7 +81,7 @@ if (status.rebaseInternalState) {
|
|
|
81
81
|
```typescript
|
|
82
82
|
import { Repository, getStatus, createCommit } from 'git-chopstick-core'
|
|
83
83
|
|
|
84
|
-
const repo = new Repository('/path/to/repo'
|
|
84
|
+
const repo = new Repository('/path/to/repo')
|
|
85
85
|
const status = await getStatus(repo)
|
|
86
86
|
|
|
87
87
|
// Stage and commit all tracked files
|
|
@@ -98,7 +98,7 @@ import {
|
|
|
98
98
|
deleteLocalBranch, renameBranch, getBranches
|
|
99
99
|
} from 'git-chopstick-core'
|
|
100
100
|
|
|
101
|
-
const repo = new Repository('/path/to/repo'
|
|
101
|
+
const repo = new Repository('/path/to/repo')
|
|
102
102
|
|
|
103
103
|
// Create a branch
|
|
104
104
|
await createBranch(repo, 'feature/new-feature', 'main')
|
|
@@ -125,7 +125,7 @@ import {
|
|
|
125
125
|
abortMerge, getMergeBase, isMergeHeadSet
|
|
126
126
|
} from 'git-chopstick-core'
|
|
127
127
|
|
|
128
|
-
const repo = new Repository('/path/to/repo'
|
|
128
|
+
const repo = new Repository('/path/to/repo')
|
|
129
129
|
|
|
130
130
|
const result = await merge(repo, 'feature/new-feature')
|
|
131
131
|
|
|
@@ -149,7 +149,7 @@ import {
|
|
|
149
149
|
Repository, push, pull, getRemotes, getStatus
|
|
150
150
|
} from 'git-chopstick-core'
|
|
151
151
|
|
|
152
|
-
const repo = new Repository('/path/to/repo'
|
|
152
|
+
const repo = new Repository('/path/to/repo')
|
|
153
153
|
const [remote] = await getRemotes(repo)
|
|
154
154
|
const status = await getStatus(repo)
|
|
155
155
|
|
|
@@ -167,7 +167,7 @@ import {
|
|
|
167
167
|
Repository, merge, GitError, GitErrorCodes
|
|
168
168
|
} from 'git-chopstick-core'
|
|
169
169
|
|
|
170
|
-
const repo = new Repository('/path/to/repo'
|
|
170
|
+
const repo = new Repository('/path/to/repo')
|
|
171
171
|
|
|
172
172
|
try {
|
|
173
173
|
await merge(repo, 'other-branch')
|
|
@@ -328,11 +328,14 @@ src/
|
|
|
328
328
|
# Type-check
|
|
329
329
|
npm run typecheck
|
|
330
330
|
|
|
331
|
+
# Run integration tests (uses vitest, creates temp git repos)
|
|
332
|
+
npm test
|
|
333
|
+
|
|
331
334
|
# Build (compile TypeScript → dist/)
|
|
332
335
|
npm run build
|
|
333
336
|
|
|
334
|
-
#
|
|
335
|
-
npm run
|
|
337
|
+
# Package contents dry-run (verify files before publish)
|
|
338
|
+
npm pack --dry-run
|
|
336
339
|
|
|
337
340
|
# Run examples (uses tsx for source-level execution)
|
|
338
341
|
npx tsx examples/get-status.ts /path/to/repo
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Migration Guide: `exec` → `git()`
|
|
2
|
+
|
|
3
|
+
> **Applies to:** Upgrading from `git-chopstick-core@0.1.4` to `0.1.5+`
|
|
4
|
+
|
|
5
|
+
In v0.1.4, `exec()` was briefly exposed as a public function. Starting in **v0.1.5**, `exec()` is no longer part of the public API — `git()` is the single function for running Git commands.
|
|
6
|
+
|
|
7
|
+
## Why?
|
|
8
|
+
|
|
9
|
+
`exec()` and `git()` did the same thing with different semantics:
|
|
10
|
+
|
|
11
|
+
| Aspect | `exec()` | `git()` |
|
|
12
|
+
|--------|----------|---------|
|
|
13
|
+
| `successExitCodes` | ❌ No — raw exit code | ✅ Yes — configurable set |
|
|
14
|
+
| Error parsing | ❌ No | ✅ Yes — 50-code `GitError` enum |
|
|
15
|
+
| Throws on bad exit | ❌ No — returns raw result | ✅ Yes — throws `GitError` |
|
|
16
|
+
| Hook interception | ❌ No | ✅ Yes — `interceptHooks` option |
|
|
17
|
+
| Buffer output | ✅ Yes — `encoding: 'buffer'` | ✅ Yes — `{ encoding: 'buffer' }` |
|
|
18
|
+
|
|
19
|
+
`git()` does everything `exec()` did, plus error handling, typed error codes, and hook support. Maintaining both was confusing.
|
|
20
|
+
|
|
21
|
+
## Before → After
|
|
22
|
+
|
|
23
|
+
### Raw git execution (basic)
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
// Before (v0.1.4)
|
|
27
|
+
import { exec } from 'git-chopstick-core/git'
|
|
28
|
+
const result = await exec(['rev-parse', '--show-toplevel'], '/path/to/repo')
|
|
29
|
+
|
|
30
|
+
// After (v0.1.5+)
|
|
31
|
+
import { git } from 'git-chopstick-core'
|
|
32
|
+
const result = await git(['rev-parse', '--show-toplevel'], '/path/to/repo', 'my-op')
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Buffer output (binary diffs)
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
// Before
|
|
39
|
+
import { exec } from 'git-chopstick-core/git'
|
|
40
|
+
const result = await exec(['diff', '--binary', sha], path, { encoding: 'buffer' })
|
|
41
|
+
|
|
42
|
+
// After
|
|
43
|
+
import { git } from 'git-chopstick-core'
|
|
44
|
+
const result = await git(['diff', '--binary', sha], path, 'my-op', { encoding: 'buffer' })
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Ignoring non-zero exit codes
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
// Before — exec doesn't filter, so you check exitCode manually
|
|
51
|
+
const result = await exec(['symbolic-ref', '--short', 'HEAD'], path)
|
|
52
|
+
const branch = result.exitCode === 0 ? result.stdout.toString().trim() : undefined
|
|
53
|
+
|
|
54
|
+
// After — git() accepts successExitCodes, returns result or throws
|
|
55
|
+
const result = await git(['symbolic-ref', '--short', 'HEAD'], path, 'getBranch', {
|
|
56
|
+
successExitCodes: new Set([0, 128]),
|
|
57
|
+
})
|
|
58
|
+
const branch = result.exitCode === 0 ? result.stdout.trim() : undefined
|
|
59
|
+
|
|
60
|
+
// Or just use the built-in helper:
|
|
61
|
+
import { getCurrentBranch } from 'git-chopstick-core'
|
|
62
|
+
const branch = await getCurrentBranch(path)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## What stayed public?
|
|
66
|
+
|
|
67
|
+
The following from `exec.ts` remain in the public API because they serve different purposes:
|
|
68
|
+
|
|
69
|
+
| Export | Purpose |
|
|
70
|
+
|--------|---------|
|
|
71
|
+
| `spawnGit(args, path, options?)` | Returns a `ChildProcess` for streaming git access — useful for long-running operations where you want direct process control |
|
|
72
|
+
| `parseError(stderr)` | Parse git stderr into a `GitError` enum value for manual error inspection |
|
|
73
|
+
| `parseBadConfigValueErrorInfo(stderr)` | Extract key/value from bad config value errors |
|
|
74
|
+
| `ExecError` (class) | Error thrown when maxBuffer is exceeded |
|
|
75
|
+
| `GitError` (enum) | 50+ typed error codes (available as `GitErrorCodes` from root barrel) |
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: release
|
|
3
|
+
description: Automated release workflow for git-chopstick-core — version bump, changelog, build, validation, and npm publish.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Release: git-chopstick-core
|
|
7
|
+
|
|
8
|
+
> Loaded via `skill("release")`. Run this whenever you need to publish a new release.
|
|
9
|
+
|
|
10
|
+
## Process
|
|
11
|
+
|
|
12
|
+
### 1. Read current state
|
|
13
|
+
|
|
14
|
+
Read `package.json`, `CHANGELOG.md`, and run `git log --oneline -5` to understand what has changed since the last release.
|
|
15
|
+
|
|
16
|
+
### 2. Determine version bump
|
|
17
|
+
|
|
18
|
+
Check what has changed:
|
|
19
|
+
|
|
20
|
+
- **Breaking API changes** (removed exports, changed signatures) → minor bump (0.x.0)
|
|
21
|
+
- **New features** (new functions, new exports) → patch bump (0.0.x)
|
|
22
|
+
- **Bug fixes, docs, internal refactoring** → patch bump (0.0.x)
|
|
23
|
+
|
|
24
|
+
Current version: read from `package.json`. Bump accordingly.
|
|
25
|
+
|
|
26
|
+
### 3. Update `CHANGELOG.md`
|
|
27
|
+
|
|
28
|
+
Insert a new `## [VERSION] — YYYY-MM-DD` section at the top (below the `# Changelog` heading) with:
|
|
29
|
+
|
|
30
|
+
- `### Added` — new features, exports, helpers
|
|
31
|
+
- `### Changed` — behavioral changes, renames, config changes
|
|
32
|
+
- `### Fixed` — bug fixes
|
|
33
|
+
|
|
34
|
+
Then update the link references at the bottom:
|
|
35
|
+
|
|
36
|
+
- Add `[VERSION]: https://github.com/parkiyong/git-chopstick-core/releases/tag/vVERSION`
|
|
37
|
+
- Update `[Unreleased]: .../compare/vVERSION...HEAD`
|
|
38
|
+
|
|
39
|
+
### 4. Bump `package.json`
|
|
40
|
+
|
|
41
|
+
Update `"version"` field.
|
|
42
|
+
|
|
43
|
+
### 5. Build and validate
|
|
44
|
+
|
|
45
|
+
Run all of these (use parallel agents):
|
|
46
|
+
|
|
47
|
+
- `npm run typecheck` — must pass with zero errors
|
|
48
|
+
- `npm run build` — must produce a clean `dist/`
|
|
49
|
+
- `npm test` — all integration tests must pass
|
|
50
|
+
- `npm pack --dry-run` — confirm all of:
|
|
51
|
+
- `dist/` is included (expect ~351 files, ~700kB)
|
|
52
|
+
- `CHANGELOG.md`, `docs/`, and `src/__tests__/integration.test.ts` appear in the file list
|
|
53
|
+
- `package.json`, `README.md`, `LICENSE` are present (npm includes these automatically)
|
|
54
|
+
- No unexpected files are leaking through (check for test artifacts, `.ts` sources outside `dist/`)
|
|
55
|
+
|
|
56
|
+
If any fail, fix before proceeding.
|
|
57
|
+
|
|
58
|
+
### 6. Stage and commit
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
git add -A
|
|
62
|
+
git commit -m "vVERSION: <short summary of changes>"
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 7. Push and publish
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
git push origin main
|
|
69
|
+
npm publish
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 8. Verify
|
|
73
|
+
|
|
74
|
+
- Confirm npm shows the new version: `npm view git-chopstick-core version`
|
|
75
|
+
- Confirm the tag exists on GitHub: `git tag -l`
|
|
76
|
+
- Suggest followup tasks
|
|
77
|
+
|
|
78
|
+
## Notes
|
|
79
|
+
|
|
80
|
+
- `prepublishOnly` runs `npm run typecheck && npm test && npm run build` automatically — integration tests must pass before publish can proceed. This catches API regressions, broken imports, and test failures before they ship.
|
|
81
|
+
You still validate in step 5 beforehand rather than relying solely on `prepublishOnly`.
|
|
82
|
+
- If this is the first release in a session, run `setup-matt-pocock-skills` first to configure issue tracker context.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "git-chopstick-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -32,7 +32,9 @@
|
|
|
32
32
|
},
|
|
33
33
|
"files": [
|
|
34
34
|
"dist",
|
|
35
|
-
"CHANGELOG.md"
|
|
35
|
+
"CHANGELOG.md",
|
|
36
|
+
"docs",
|
|
37
|
+
"src/__tests__"
|
|
36
38
|
],
|
|
37
39
|
"description": "Git backend library extracted from GitHub Desktop",
|
|
38
40
|
"scripts": {
|
|
@@ -40,7 +42,7 @@
|
|
|
40
42
|
"build": "npm run clean && tsc",
|
|
41
43
|
"typecheck": "tsc --noEmit",
|
|
42
44
|
"test": "vitest run",
|
|
43
|
-
"prepublishOnly": "npm run typecheck && npm run build"
|
|
45
|
+
"prepublishOnly": "npm run typecheck && npm test && npm run build"
|
|
44
46
|
},
|
|
45
47
|
"dependencies": {
|
|
46
48
|
"byline": "^5.0.0",
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
|
|
2
|
+
import { mkdtempSync, writeFileSync } from 'fs'
|
|
3
|
+
import { execSync } from 'child_process'
|
|
4
|
+
import { tmpdir } from 'os'
|
|
5
|
+
import { join } from 'path'
|
|
6
|
+
import {
|
|
7
|
+
Repository, getStatus, getCommits, getBranches,
|
|
8
|
+
createCommit, createBranch, deleteLocalBranch, renameBranch,
|
|
9
|
+
getCurrentBranch, getRepositoryType,
|
|
10
|
+
} from '../index.js'
|
|
11
|
+
|
|
12
|
+
let repoPath: string
|
|
13
|
+
let repo: Repository
|
|
14
|
+
|
|
15
|
+
function git(args: string) {
|
|
16
|
+
execSync(`git ${args}`, { cwd: repoPath, stdio: 'pipe' })
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function writeFile(path: string, content: string) {
|
|
20
|
+
writeFileSync(join(repoPath, path), content)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
beforeAll(() => {
|
|
24
|
+
repoPath = mkdtempSync(join(tmpdir(), 'gcctest-'))
|
|
25
|
+
git('init -q')
|
|
26
|
+
git('config user.email test@test.test')
|
|
27
|
+
git('config user.name Test')
|
|
28
|
+
|
|
29
|
+
// Create an initial commit so we have history for detached HEAD tests
|
|
30
|
+
writeFile('README.md', '# test')
|
|
31
|
+
git('add README.md')
|
|
32
|
+
git('commit -m "initial"')
|
|
33
|
+
|
|
34
|
+
repo = new Repository(repoPath)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
describe('getRepositoryType', () => {
|
|
38
|
+
it('returns regular for a valid repo', async () => {
|
|
39
|
+
const type = await getRepositoryType(repoPath)
|
|
40
|
+
expect(type.kind).toBe('regular')
|
|
41
|
+
if (type.kind === 'regular') {
|
|
42
|
+
expect(type.topLevelWorkingDirectory).toBe(repoPath)
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('returns missing for a non-existent path', async () => {
|
|
47
|
+
const type = await getRepositoryType('/nonexistent/path')
|
|
48
|
+
expect(type.kind).toBe('missing')
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
describe('getCurrentBranch', () => {
|
|
53
|
+
it('returns the current branch name', async () => {
|
|
54
|
+
const branch = await getCurrentBranch(repoPath)
|
|
55
|
+
expect(branch).toBeTruthy()
|
|
56
|
+
expect(typeof branch).toBe('string')
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('returns undefined when HEAD is detached', async () => {
|
|
60
|
+
git('checkout --detach')
|
|
61
|
+
const branch = await getCurrentBranch(repoPath)
|
|
62
|
+
expect(branch).toBeUndefined()
|
|
63
|
+
// Reset back to a branch for subsequent tests
|
|
64
|
+
git('checkout -')
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
describe('getStatus', () => {
|
|
69
|
+
it('returns a status with branch info', async () => {
|
|
70
|
+
const status = await getStatus(repo)
|
|
71
|
+
expect(status).toBeTruthy()
|
|
72
|
+
expect(status!.currentBranch).toBeTruthy()
|
|
73
|
+
expect(typeof status!.currentBranch).toBe('string')
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it('detects untracked files', async () => {
|
|
77
|
+
writeFile('untracked.txt', 'hello')
|
|
78
|
+
const status = await getStatus(repo)
|
|
79
|
+
expect(status!.workingDirectory.files.length).toBeGreaterThanOrEqual(1)
|
|
80
|
+
const untracked = status!.workingDirectory.files.find(
|
|
81
|
+
f => f.path === 'untracked.txt'
|
|
82
|
+
)
|
|
83
|
+
expect(untracked).toBeTruthy()
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('returns null for a path that exists but is not a git repo', async () => {
|
|
87
|
+
const nonRepoPath = mkdtempSync(join(tmpdir(), 'gcctest-nonrepo-'))
|
|
88
|
+
const result = await getStatus(new Repository(nonRepoPath))
|
|
89
|
+
expect(result).toBeNull()
|
|
90
|
+
execSync(`rm -rf ${nonRepoPath}`)
|
|
91
|
+
})
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
describe('getCommits', () => {
|
|
95
|
+
it('returns commits in order', async () => {
|
|
96
|
+
// Create another commit so we have history to read
|
|
97
|
+
writeFile('commits-test.txt', 'data')
|
|
98
|
+
git('add commits-test.txt')
|
|
99
|
+
git('commit -m "test commit for getCommits"')
|
|
100
|
+
|
|
101
|
+
const commits = await getCommits(repo, 'HEAD', 10)
|
|
102
|
+
expect(commits.length).toBeGreaterThanOrEqual(1)
|
|
103
|
+
expect(commits[0].summary).toBe('test commit for getCommits')
|
|
104
|
+
expect(commits[0].sha).toBeTruthy()
|
|
105
|
+
expect(commits[0].author).toBeTruthy()
|
|
106
|
+
})
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
describe('Branch operations', () => {
|
|
110
|
+
it('creates, lists, renames, and deletes branches', async () => {
|
|
111
|
+
// Create a branch
|
|
112
|
+
await createBranch(repo, 'test-feature', 'HEAD')
|
|
113
|
+
let branches = await getBranches(repo)
|
|
114
|
+
const names = branches.map(b => b.name)
|
|
115
|
+
expect(names).toContain('test-feature')
|
|
116
|
+
|
|
117
|
+
// Rename the branch
|
|
118
|
+
const featureBranch = branches.find(b => b.name === 'test-feature')!
|
|
119
|
+
await renameBranch(repo, featureBranch, 'test-feature-renamed')
|
|
120
|
+
branches = await getBranches(repo)
|
|
121
|
+
expect(branches.map(b => b.name)).toContain('test-feature-renamed')
|
|
122
|
+
expect(branches.map(b => b.name)).not.toContain('test-feature')
|
|
123
|
+
|
|
124
|
+
// Delete the branch
|
|
125
|
+
await deleteLocalBranch(repo, 'test-feature-renamed')
|
|
126
|
+
branches = await getBranches(repo)
|
|
127
|
+
expect(branches.map(b => b.name)).not.toContain('test-feature-renamed')
|
|
128
|
+
})
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
describe('createCommit', () => {
|
|
132
|
+
it('creates a commit with files', async () => {
|
|
133
|
+
writeFile('commit-test.txt', 'commit data')
|
|
134
|
+
git('add commit-test.txt')
|
|
135
|
+
|
|
136
|
+
const status = await getStatus(repo)
|
|
137
|
+
const files = status!.workingDirectory.files.filter(
|
|
138
|
+
f => f.path === 'commit-test.txt'
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
const sha = await createCommit(repo, 'feat: add commit-test.txt', files)
|
|
142
|
+
expect(sha).toBeTruthy()
|
|
143
|
+
// createCommit returns the abbreviated SHA from git commit output (7+ chars)
|
|
144
|
+
expect(sha.length).toBeGreaterThanOrEqual(7)
|
|
145
|
+
|
|
146
|
+
// Verify the commit exists — the full SHA from getCommits should start
|
|
147
|
+
// with the abbreviated SHA returned by createCommit
|
|
148
|
+
const commits = await getCommits(repo, 'HEAD', 5)
|
|
149
|
+
expect(commits[0].sha.startsWith(sha)).toBe(true)
|
|
150
|
+
expect(commits[0].summary).toBe('feat: add commit-test.txt')
|
|
151
|
+
})
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
afterAll(() => {
|
|
155
|
+
execSync(`rm -rf ${repoPath}`)
|
|
156
|
+
})
|