@nitra/cursor 3.4.0 → 3.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/package.json +1 -1
- package/scripts/lib/worktree.mjs +27 -1
- package/scripts/worktree-cli.mjs +18 -4
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
package/scripts/lib/worktree.mjs
CHANGED
|
@@ -24,13 +24,39 @@ export function sanitizeBranch(branch) {
|
|
|
24
24
|
if (typeof branch !== 'string' || branch.trim() === '') {
|
|
25
25
|
throw new Error('worktree: імʼя гілки обовʼязкове')
|
|
26
26
|
}
|
|
27
|
-
const sanitized = branch
|
|
27
|
+
const sanitized = branch
|
|
28
|
+
.trim()
|
|
29
|
+
.replace(UNSAFE_PATH_CHARS_RE, '-')
|
|
30
|
+
.replace(/^-+|-+$/gu, '')
|
|
28
31
|
if (sanitized === '') {
|
|
29
32
|
throw new Error(`worktree: імʼя гілки "${branch}" не містить допустимих символів`)
|
|
30
33
|
}
|
|
31
34
|
return sanitized
|
|
32
35
|
}
|
|
33
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Перша вільна назва гілки за конвенцією `base`, `base2`, `base3`, … —
|
|
39
|
+
* суфікс просто число без розділювача (як `main-fix` → `main-fix2`).
|
|
40
|
+
* Дає змогу `worktree add` спершу перевірити зайнятість і обрати назву,
|
|
41
|
+
* що спрацює, замість падіння на `fatal: a branch named '…' already exists`.
|
|
42
|
+
* @param {string} branch бажане імʼя гілки
|
|
43
|
+
* @param {(candidate: string) => boolean} isTaken чи зайнята назва (гілка/worktree вже існують)
|
|
44
|
+
* @param {number} [limit] стеля кількості спроб (захист від нескінченного циклу)
|
|
45
|
+
* @returns {string} перша вільна назва (= `branch`, якщо вона вільна)
|
|
46
|
+
*/
|
|
47
|
+
export function firstFreeBranch(branch, isTaken, limit = 1000) {
|
|
48
|
+
if (typeof branch !== 'string' || branch.trim() === '') {
|
|
49
|
+
throw new Error('worktree: імʼя гілки обовʼязкове')
|
|
50
|
+
}
|
|
51
|
+
const base = branch.trim()
|
|
52
|
+
if (!isTaken(base)) return base
|
|
53
|
+
for (let n = 2; n <= limit; n++) {
|
|
54
|
+
const candidate = `${base}${n}`
|
|
55
|
+
if (!isTaken(candidate)) return candidate
|
|
56
|
+
}
|
|
57
|
+
throw new Error(`worktree: не знайдено вільної назви для "${base}" за ${limit} спроб`)
|
|
58
|
+
}
|
|
59
|
+
|
|
34
60
|
/**
|
|
35
61
|
* Детерміновані шляхи checkout і файла-опису для гілки.
|
|
36
62
|
* @param {string} repoRoot абсолютний корінь репозиторію
|
package/scripts/worktree-cli.mjs
CHANGED
|
@@ -16,7 +16,7 @@ import { join } from 'node:path'
|
|
|
16
16
|
import { cwd as processCwd } from 'node:process'
|
|
17
17
|
|
|
18
18
|
import { cleanupFlowSiblings } from './dispatcher/lib/state-store.mjs'
|
|
19
|
-
import { buildDescription, findOrphanDescFiles, worktreePaths } from './lib/worktree.mjs'
|
|
19
|
+
import { buildDescription, findOrphanDescFiles, firstFreeBranch, worktreePaths } from './lib/worktree.mjs'
|
|
20
20
|
|
|
21
21
|
const USAGE = [
|
|
22
22
|
'Usage:',
|
|
@@ -89,20 +89,34 @@ function cmdAdd(rest, ctx) {
|
|
|
89
89
|
ctx.logError('worktree add: опис обовʼязковий — `worktree add <branch> "<опис>"`')
|
|
90
90
|
return 1
|
|
91
91
|
}
|
|
92
|
+
// Зайнята, якщо вже є git-гілка з такою назвою або checkout-каталог `.worktrees/<sanit>`.
|
|
93
|
+
const isTaken = name => {
|
|
94
|
+
if (git(['show-ref', '--verify', '--quiet', `refs/heads/${name}`], ctx.cwd).status === 0) return true
|
|
95
|
+
try {
|
|
96
|
+
return existsSync(worktreePaths(ctx.cwd, name).checkout)
|
|
97
|
+
} catch {
|
|
98
|
+
return false // невалідна для шляху назва — впаде нижче на worktreePaths(chosen) з людинозрозумілим текстом
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
let chosen
|
|
92
102
|
let paths
|
|
93
103
|
try {
|
|
94
|
-
|
|
104
|
+
chosen = firstFreeBranch(branch, isTaken)
|
|
105
|
+
paths = worktreePaths(ctx.cwd, chosen)
|
|
95
106
|
} catch (error) {
|
|
96
107
|
ctx.logError(error.message)
|
|
97
108
|
return 1
|
|
98
109
|
}
|
|
99
|
-
|
|
110
|
+
if (chosen !== branch) {
|
|
111
|
+
ctx.log(`ℹ️ гілка/worktree "${branch}" уже існує — обрано вільну назву "${chosen}"`)
|
|
112
|
+
}
|
|
113
|
+
const added = git(['worktree', 'add', paths.checkout, '-b', chosen], ctx.cwd)
|
|
100
114
|
if (added.status !== 0) {
|
|
101
115
|
ctx.logError(`worktree add не вдався: ${added.stderr.trim()}`)
|
|
102
116
|
return 1
|
|
103
117
|
}
|
|
104
118
|
const baseCommit = git(['rev-parse', '--short', 'HEAD'], ctx.cwd).stdout.trim()
|
|
105
|
-
const md = buildDescription({ branch, task, baseCommit, date: today(ctx.now) })
|
|
119
|
+
const md = buildDescription({ branch: chosen, task, baseCommit, date: today(ctx.now) })
|
|
106
120
|
writeFileSync(paths.descFile, md, 'utf8')
|
|
107
121
|
ctx.log(`✅ worktree: ${paths.checkout}`)
|
|
108
122
|
ctx.log(` опис: ${paths.descFile}`)
|