@lakakala/kgit 0.2.1 → 0.3.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 +90 -40
- package/dist/commands/edit.d.ts +3 -0
- package/dist/commands/edit.js +53 -0
- package/dist/config.d.ts +3 -0
- package/dist/config.js +1 -0
- package/dist/index.js +8 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,96 +1,104 @@
|
|
|
1
1
|
# kgit
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
使用 [git worktree](https://git-scm.com/docs/git-worktree) 管理多仓库工作区的 CLI 工具。
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## 安装
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
npm install -g @lakakala/kgit
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## 配置
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
在 `~/.config/kgit/config.json` 中创建配置文件:
|
|
14
14
|
|
|
15
15
|
```json
|
|
16
16
|
{
|
|
17
17
|
"workspace": "$HOME/projects",
|
|
18
|
+
"ide": "trae",
|
|
18
19
|
"projects": [
|
|
19
20
|
{
|
|
20
21
|
"name": "form",
|
|
21
|
-
"path": "$HOME/repos/
|
|
22
|
+
"path": "$HOME/repos/ens_bpm_form"
|
|
22
23
|
},
|
|
23
24
|
{
|
|
24
25
|
"name": "auth",
|
|
25
|
-
"path": "$HOME/repos/
|
|
26
|
+
"path": "$HOME/repos/ens_bpm_auth"
|
|
26
27
|
}
|
|
27
28
|
]
|
|
28
29
|
}
|
|
29
30
|
```
|
|
30
31
|
|
|
31
|
-
|
|
|
32
|
-
|
|
33
|
-
| `workspace` |
|
|
34
|
-
| `
|
|
35
|
-
| `projects[].
|
|
32
|
+
| 字段 | 说明 |
|
|
33
|
+
|------|------|
|
|
34
|
+
| `workspace` | 工作区根目录,所有工程都创建在此目录下 |
|
|
35
|
+
| `ide` | 默认编辑器(可选),支持 `code`、`trae`、`cursor`、`nvim`、`vim` 等 |
|
|
36
|
+
| `projects[].name` | 项目别名,在 `-p` 参数中使用 |
|
|
37
|
+
| `projects[].path` | 项目 git 仓库的绝对路径 |
|
|
36
38
|
|
|
37
|
-
|
|
39
|
+
`workspace` 和 `path` 中均支持环境变量(`$HOME`、`$VAR`、`${VAR}`)。
|
|
38
40
|
|
|
39
|
-
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 命令
|
|
40
44
|
|
|
41
45
|
### `kgit new`
|
|
42
46
|
|
|
43
|
-
|
|
47
|
+
创建新工程,为每个项目建立 git worktree。
|
|
44
48
|
|
|
45
49
|
```bash
|
|
46
|
-
kgit new
|
|
50
|
+
kgit new <工程名称> [-b <分支名>] -p <项目[:基准分支]> [-p ...]
|
|
47
51
|
```
|
|
48
52
|
|
|
49
|
-
|
|
50
|
-
`<base>` is the base branch used when creating a new branch (default: `master`).
|
|
53
|
+
**选项**
|
|
51
54
|
|
|
52
|
-
|
|
55
|
+
| 选项 | 说明 |
|
|
56
|
+
|------|------|
|
|
57
|
+
| `-b <branch>` | 新分支名,默认使用工程名称 |
|
|
58
|
+
| `-p <project[:base]>` | 添加项目,可选指定基准分支(默认 `master`),可重复使用 |
|
|
53
59
|
|
|
54
|
-
|
|
55
|
-
2. Branch exists **on remote** → create a local tracking branch
|
|
56
|
-
3. Branch does not exist → create a new branch from `<base>`
|
|
60
|
+
**分支解析逻辑**(按优先级):
|
|
57
61
|
|
|
58
|
-
|
|
62
|
+
1. 本地已存在该分支 → 直接使用
|
|
63
|
+
2. 远程存在该分支 → 创建本地追踪分支
|
|
64
|
+
3. 分支不存在 → 基于 `<基准分支>` 新建
|
|
65
|
+
|
|
66
|
+
**示例**
|
|
59
67
|
|
|
60
68
|
```bash
|
|
61
|
-
#
|
|
69
|
+
# 基于 master 创建工程 feat-login,新分支名为 feat-login
|
|
62
70
|
kgit new feat-login -p form -p auth
|
|
63
71
|
|
|
64
|
-
#
|
|
72
|
+
# 指定基准分支
|
|
65
73
|
kgit new feat-login -p form:develop -p auth:develop
|
|
66
74
|
|
|
67
|
-
#
|
|
75
|
+
# 使用 -b 指定新分支名(可复用已有本地/远程分支)
|
|
68
76
|
kgit new feat-login -b my-feature -p form:develop
|
|
69
77
|
|
|
70
|
-
#
|
|
78
|
+
# 多项目使用不同基准分支
|
|
71
79
|
kgit new feat-login -p form:master -p auth:develop
|
|
72
80
|
```
|
|
73
81
|
|
|
74
|
-
|
|
82
|
+
**目录结构**
|
|
75
83
|
|
|
76
84
|
```
|
|
77
85
|
$workspace/
|
|
78
86
|
└── feat-login/
|
|
79
|
-
├── form/ ← worktree
|
|
80
|
-
└── auth/ ← worktree
|
|
87
|
+
├── form/ ← worktree,分支 feat-login
|
|
88
|
+
└── auth/ ← worktree,分支 feat-login
|
|
81
89
|
```
|
|
82
90
|
|
|
83
91
|
---
|
|
84
92
|
|
|
85
93
|
### `kgit append`
|
|
86
94
|
|
|
87
|
-
|
|
95
|
+
向已有工程追加项目,选项与 `new` 相同。
|
|
88
96
|
|
|
89
97
|
```bash
|
|
90
|
-
kgit append
|
|
98
|
+
kgit append <工程名称> [-b <分支名>] -p <项目[:基准分支]> [-p ...]
|
|
91
99
|
```
|
|
92
100
|
|
|
93
|
-
|
|
101
|
+
**示例**
|
|
94
102
|
|
|
95
103
|
```bash
|
|
96
104
|
kgit append feat-login -p gateway:master
|
|
@@ -100,34 +108,76 @@ kgit append feat-login -p gateway:master
|
|
|
100
108
|
|
|
101
109
|
### `kgit remove`
|
|
102
110
|
|
|
103
|
-
|
|
111
|
+
删除工程或工程中的指定项目。
|
|
104
112
|
|
|
105
113
|
```bash
|
|
106
|
-
kgit remove
|
|
107
|
-
kgit remove
|
|
114
|
+
kgit remove <工程名称> # 删除整个工程
|
|
115
|
+
kgit remove <工程名称> -p <项目> [-p ...] # 删除指定项目
|
|
108
116
|
```
|
|
109
117
|
|
|
110
|
-
|
|
118
|
+
删除前会检测分支是否已推送到远程,若存在未同步的提交则需二次确认。
|
|
119
|
+
|
|
120
|
+
**示例**
|
|
111
121
|
|
|
112
122
|
```bash
|
|
113
|
-
kgit remove feat-login #
|
|
114
|
-
kgit remove feat-login -p form #
|
|
123
|
+
kgit remove feat-login # 删除所有 worktree 及工程目录
|
|
124
|
+
kgit remove feat-login -p form # 仅删除 form 的 worktree
|
|
115
125
|
```
|
|
116
126
|
|
|
117
127
|
---
|
|
118
128
|
|
|
119
129
|
### `kgit list`
|
|
120
130
|
|
|
121
|
-
|
|
131
|
+
列出所有已创建的工程及其项目和当前分支。
|
|
122
132
|
|
|
123
133
|
```bash
|
|
124
134
|
kgit list
|
|
125
135
|
```
|
|
126
136
|
|
|
127
|
-
|
|
137
|
+
**输出示例**
|
|
128
138
|
|
|
129
139
|
```
|
|
130
140
|
feat-login
|
|
131
141
|
└─ form (feat-login)
|
|
132
142
|
└─ auth (feat-login)
|
|
133
143
|
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
### `kgit edit`
|
|
148
|
+
|
|
149
|
+
用指定编辑器打开项目目录。
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
kgit edit <工程名称> <项目名称> [--ide <编辑器>]
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**选项**
|
|
156
|
+
|
|
157
|
+
| 选项 | 说明 |
|
|
158
|
+
|------|------|
|
|
159
|
+
| `--ide <name>` | 指定编辑器,未传则依次读取 config.json 中的 `ide` 字段,再自动检测 |
|
|
160
|
+
|
|
161
|
+
**编辑器优先级**:`--ide` 参数 → config.json `ide` 字段 → 自动检测(`code → trae → cursor → nvim → vim`)
|
|
162
|
+
|
|
163
|
+
| 类型 | 支持的编辑器 |
|
|
164
|
+
|------|------------|
|
|
165
|
+
| GUI(后台启动) | `code`、`trae`、`cursor`、`idea`、`webstorm` |
|
|
166
|
+
| 终端(前台运行) | `nvim`、`vim`、`vi`、`nano` |
|
|
167
|
+
|
|
168
|
+
**示例**
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
kgit edit feat-login form
|
|
172
|
+
kgit edit feat-login form --ide nvim
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
### `kgit version`
|
|
178
|
+
|
|
179
|
+
打印当前版本号。
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
kgit version
|
|
183
|
+
```
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { execa } from 'execa';
|
|
4
|
+
import { loadConfig } from '../config.js';
|
|
5
|
+
const IDE_COMMANDS = {
|
|
6
|
+
code: 'code',
|
|
7
|
+
trae: 'trae',
|
|
8
|
+
cursor: 'cursor',
|
|
9
|
+
nvim: 'nvim',
|
|
10
|
+
vim: 'vim',
|
|
11
|
+
vi: 'vi',
|
|
12
|
+
nano: 'nano',
|
|
13
|
+
idea: 'idea',
|
|
14
|
+
webstorm: 'webstorm',
|
|
15
|
+
};
|
|
16
|
+
const TERMINAL_IDES = new Set(['nvim', 'vim', 'vi', 'nano']);
|
|
17
|
+
async function detectDefaultIde() {
|
|
18
|
+
for (const cmd of ['code', 'trae', 'cursor', 'nvim', 'vim']) {
|
|
19
|
+
try {
|
|
20
|
+
await execa('which', [cmd], { stdio: 'pipe' });
|
|
21
|
+
return cmd;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// not found, try next
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
export async function editCommand(workspaceName, projectName, options) {
|
|
30
|
+
const config = loadConfig();
|
|
31
|
+
const worktreePath = path.join(config.workspace, workspaceName, projectName);
|
|
32
|
+
if (!fs.existsSync(worktreePath)) {
|
|
33
|
+
throw new Error(`Project path does not exist: ${worktreePath}`);
|
|
34
|
+
}
|
|
35
|
+
let ideName = options.ide?.toLowerCase()
|
|
36
|
+
?? config.ide?.toLowerCase();
|
|
37
|
+
if (!ideName) {
|
|
38
|
+
const detected = await detectDefaultIde();
|
|
39
|
+
if (!detected) {
|
|
40
|
+
throw new Error('No IDE detected. Please specify one with --ide or set "ide" in config.');
|
|
41
|
+
}
|
|
42
|
+
ideName = detected;
|
|
43
|
+
console.log(`Using detected IDE: ${ideName}`);
|
|
44
|
+
}
|
|
45
|
+
const command = IDE_COMMANDS[ideName] ?? ideName;
|
|
46
|
+
console.log(`Opening ${worktreePath} with ${command}...`);
|
|
47
|
+
if (TERMINAL_IDES.has(ideName)) {
|
|
48
|
+
await execa(command, [worktreePath], { stdio: 'inherit' });
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
execa(command, [worktreePath], { detached: true, stdio: 'ignore' }).unref();
|
|
52
|
+
}
|
|
53
|
+
}
|
package/dist/config.d.ts
CHANGED
|
@@ -21,18 +21,21 @@ declare const ConfigSchema: z.ZodObject<{
|
|
|
21
21
|
name: string;
|
|
22
22
|
path: string;
|
|
23
23
|
}>, "many">;
|
|
24
|
+
ide: z.ZodOptional<z.ZodString>;
|
|
24
25
|
}, "strip", z.ZodTypeAny, {
|
|
25
26
|
workspace: string;
|
|
26
27
|
projects: {
|
|
27
28
|
name: string;
|
|
28
29
|
path: string;
|
|
29
30
|
}[];
|
|
31
|
+
ide?: string | undefined;
|
|
30
32
|
}, {
|
|
31
33
|
workspace: string;
|
|
32
34
|
projects: {
|
|
33
35
|
name: string;
|
|
34
36
|
path: string;
|
|
35
37
|
}[];
|
|
38
|
+
ide?: string | undefined;
|
|
36
39
|
}>;
|
|
37
40
|
export type Project = z.infer<typeof ProjectSchema>;
|
|
38
41
|
export type Config = z.infer<typeof ConfigSchema>;
|
package/dist/config.js
CHANGED
|
@@ -10,6 +10,7 @@ const ProjectSchema = z.object({
|
|
|
10
10
|
const ConfigSchema = z.object({
|
|
11
11
|
workspace: z.string(),
|
|
12
12
|
projects: z.array(ProjectSchema),
|
|
13
|
+
ide: z.string().optional(),
|
|
13
14
|
});
|
|
14
15
|
function expandEnvVars(value) {
|
|
15
16
|
return value.replace(/\$([A-Z_][A-Z0-9_]*)/g, (_, name) => {
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { newCommand } from './commands/new.js';
|
|
|
5
5
|
import { appendCommand } from './commands/append.js';
|
|
6
6
|
import { removeCommand } from './commands/remove.js';
|
|
7
7
|
import { listCommand } from './commands/list.js';
|
|
8
|
+
import { editCommand } from './commands/edit.js';
|
|
8
9
|
const require = createRequire(import.meta.url);
|
|
9
10
|
const { version } = require('../package.json');
|
|
10
11
|
function collectProject(value, previous) {
|
|
@@ -55,6 +56,13 @@ program
|
|
|
55
56
|
.action(async () => {
|
|
56
57
|
await listCommand();
|
|
57
58
|
});
|
|
59
|
+
program
|
|
60
|
+
.command('edit <workspace> <project>')
|
|
61
|
+
.description('Open a project worktree in an IDE or editor')
|
|
62
|
+
.option('--ide <name>', 'Editor to use: code, trae, cursor, nvim, vim, etc.')
|
|
63
|
+
.action(async (workspace, project, options) => {
|
|
64
|
+
await editCommand(workspace, project, options);
|
|
65
|
+
});
|
|
58
66
|
program
|
|
59
67
|
.command('version')
|
|
60
68
|
.description('Print the current version')
|