@zjex/git-workflow 0.0.1 → 0.0.2
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 +220 -4
- package/dist/index.js +68 -35
- package/package.json +3 -1
- package/scripts/README.md +57 -0
- package/scripts/release.sh +363 -0
- package/src/commands/branch.ts +123 -28
- package/src/commands/commit.ts +263 -0
- package/src/commands/help.ts +26 -17
- package/src/commands/init.ts +40 -0
- package/src/commands/tag.ts +66 -7
- package/src/config.ts +20 -0
- package/src/index.ts +140 -3
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
set -e
|
|
4
|
+
|
|
5
|
+
# 颜色定义
|
|
6
|
+
RED='\033[0;31m'
|
|
7
|
+
GREEN='\033[0;32m'
|
|
8
|
+
YELLOW='\033[1;33m'
|
|
9
|
+
BLUE='\033[0;34m'
|
|
10
|
+
CYAN='\033[0;36m'
|
|
11
|
+
NC='\033[0m' # No Color
|
|
12
|
+
|
|
13
|
+
# 打印带颜色的消息
|
|
14
|
+
print_info() {
|
|
15
|
+
echo -e "${BLUE}ℹ ${1}${NC}"
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
print_success() {
|
|
19
|
+
echo -e "${GREEN}✔ ${1}${NC}"
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
print_error() {
|
|
23
|
+
echo -e "${RED}✖ ${1}${NC}"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
print_warning() {
|
|
27
|
+
echo -e "${YELLOW}⚠ ${1}${NC}"
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
print_step() {
|
|
31
|
+
echo -e "${CYAN}▶ ${1}${NC}"
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
# 错误处理
|
|
35
|
+
trap 'handle_error $? $LINENO' ERR
|
|
36
|
+
|
|
37
|
+
handle_error() {
|
|
38
|
+
print_error "发布失败 (退出码: $1, 行号: $2)"
|
|
39
|
+
|
|
40
|
+
if [[ -n "$NEW_VERSION" ]]; then
|
|
41
|
+
print_warning "正在回滚更改..."
|
|
42
|
+
|
|
43
|
+
# 回滚 package.json
|
|
44
|
+
if [[ -f "package.json.backup" ]]; then
|
|
45
|
+
mv package.json.backup package.json
|
|
46
|
+
print_info "已恢复 package.json"
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
# 删除本地 tag
|
|
50
|
+
if git tag -l "v${NEW_VERSION}" | grep -q "v${NEW_VERSION}"; then
|
|
51
|
+
git tag -d "v${NEW_VERSION}" 2>/dev/null || true
|
|
52
|
+
print_info "已删除本地 tag"
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
# 回滚 commit
|
|
56
|
+
if git log -1 --pretty=%B | grep -q "chore(release): v${NEW_VERSION}"; then
|
|
57
|
+
git reset --hard HEAD~1 2>/dev/null || true
|
|
58
|
+
print_info "已回滚 commit"
|
|
59
|
+
fi
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
exit 1
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
# 检查命令是否存在
|
|
66
|
+
check_command() {
|
|
67
|
+
if ! command -v "$1" &> /dev/null; then
|
|
68
|
+
print_error "未找到命令: $1"
|
|
69
|
+
exit 1
|
|
70
|
+
fi
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# 检查必要的命令
|
|
74
|
+
check_command git
|
|
75
|
+
check_command node
|
|
76
|
+
check_command npm
|
|
77
|
+
|
|
78
|
+
# Dry-run 模式
|
|
79
|
+
DRY_RUN=false
|
|
80
|
+
if [[ "$1" == "--dry-run" ]]; then
|
|
81
|
+
DRY_RUN=true
|
|
82
|
+
print_warning "Dry-run 模式:仅预览,不会实际执行"
|
|
83
|
+
echo ""
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
# 检查是否在 git 仓库中
|
|
87
|
+
if ! git rev-parse --git-dir > /dev/null 2>&1; then
|
|
88
|
+
print_error "当前目录不是 git 仓库"
|
|
89
|
+
exit 1
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
# 检查是否有未提交的更改
|
|
93
|
+
if [[ -n $(git status --porcelain) ]]; then
|
|
94
|
+
print_error "有未提交的更改,请先提交或暂存"
|
|
95
|
+
git status --short
|
|
96
|
+
exit 1
|
|
97
|
+
fi
|
|
98
|
+
|
|
99
|
+
# 检查当前分支
|
|
100
|
+
CURRENT_BRANCH=$(git branch --show-current)
|
|
101
|
+
if [[ "$CURRENT_BRANCH" != "main" && "$CURRENT_BRANCH" != "master" ]]; then
|
|
102
|
+
print_warning "当前分支是 ${CURRENT_BRANCH},建议在 main/master 分支发布"
|
|
103
|
+
read -p "是否继续? (y/N) " -n 1 -r
|
|
104
|
+
echo
|
|
105
|
+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
106
|
+
print_info "已取消"
|
|
107
|
+
exit 0
|
|
108
|
+
fi
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
# 检查 npm 登录状态
|
|
112
|
+
print_step "检查 npm 登录状态..."
|
|
113
|
+
if ! npm whoami &> /dev/null; then
|
|
114
|
+
print_error "未登录 npm,请先执行: npm login"
|
|
115
|
+
exit 1
|
|
116
|
+
fi
|
|
117
|
+
NPM_USER=$(npm whoami)
|
|
118
|
+
print_success "已登录 npm (用户: ${NPM_USER})"
|
|
119
|
+
|
|
120
|
+
# 拉取最新代码
|
|
121
|
+
print_step "拉取最新代码..."
|
|
122
|
+
if [[ "$DRY_RUN" == false ]]; then
|
|
123
|
+
git pull origin "$CURRENT_BRANCH"
|
|
124
|
+
fi
|
|
125
|
+
print_success "代码已更新"
|
|
126
|
+
|
|
127
|
+
# 获取当前版本
|
|
128
|
+
CURRENT_VERSION=$(node -p "require('./package.json').version")
|
|
129
|
+
print_info "当前版本: ${CURRENT_VERSION}"
|
|
130
|
+
|
|
131
|
+
# 检查远程是否已存在该版本的 tag
|
|
132
|
+
check_tag_exists() {
|
|
133
|
+
local tag="v$1"
|
|
134
|
+
if git ls-remote --tags origin | grep -q "refs/tags/${tag}$"; then
|
|
135
|
+
return 0
|
|
136
|
+
else
|
|
137
|
+
return 1
|
|
138
|
+
fi
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
# 计算下一个版本号
|
|
142
|
+
calculate_next_version() {
|
|
143
|
+
local current=$1
|
|
144
|
+
local type=$2
|
|
145
|
+
|
|
146
|
+
IFS='.' read -r major minor patch <<< "$current"
|
|
147
|
+
|
|
148
|
+
case $type in
|
|
149
|
+
patch)
|
|
150
|
+
echo "${major}.${minor}.$((patch + 1))"
|
|
151
|
+
;;
|
|
152
|
+
minor)
|
|
153
|
+
echo "${major}.$((minor + 1)).0"
|
|
154
|
+
;;
|
|
155
|
+
major)
|
|
156
|
+
echo "$((major + 1)).0.0"
|
|
157
|
+
;;
|
|
158
|
+
esac
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
# 验证版本号格式
|
|
162
|
+
validate_version() {
|
|
163
|
+
if [[ ! $1 =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$ ]]; then
|
|
164
|
+
return 1
|
|
165
|
+
fi
|
|
166
|
+
return 0
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
# 选择版本类型
|
|
170
|
+
echo ""
|
|
171
|
+
print_step "选择新版本号"
|
|
172
|
+
echo ""
|
|
173
|
+
|
|
174
|
+
PATCH_VERSION=$(calculate_next_version "$CURRENT_VERSION" "patch")
|
|
175
|
+
MINOR_VERSION=$(calculate_next_version "$CURRENT_VERSION" "minor")
|
|
176
|
+
MAJOR_VERSION=$(calculate_next_version "$CURRENT_VERSION" "major")
|
|
177
|
+
|
|
178
|
+
echo -e " ${GREEN}1)${NC} patch ${CYAN}${CURRENT_VERSION}${NC} → ${GREEN}${PATCH_VERSION}${NC} (bug 修复)"
|
|
179
|
+
echo -e " ${GREEN}2)${NC} minor ${CYAN}${CURRENT_VERSION}${NC} → ${GREEN}${MINOR_VERSION}${NC} (新功能)"
|
|
180
|
+
echo -e " ${GREEN}3)${NC} major ${CYAN}${CURRENT_VERSION}${NC} → ${GREEN}${MAJOR_VERSION}${NC} (破坏性更新)"
|
|
181
|
+
echo -e " ${GREEN}4)${NC} custom (自定义版本号)"
|
|
182
|
+
echo -e " ${RED}5)${NC} cancel (取消发布)"
|
|
183
|
+
echo ""
|
|
184
|
+
|
|
185
|
+
while true; do
|
|
186
|
+
read -p "请选择 (1-5): " -n 1 -r VERSION_TYPE
|
|
187
|
+
echo ""
|
|
188
|
+
|
|
189
|
+
if [[ "$VERSION_TYPE" =~ ^[1-5]$ ]]; then
|
|
190
|
+
break
|
|
191
|
+
else
|
|
192
|
+
print_error "无效的选择,请输入 1-5"
|
|
193
|
+
fi
|
|
194
|
+
done
|
|
195
|
+
|
|
196
|
+
# 备份 package.json
|
|
197
|
+
cp package.json package.json.backup
|
|
198
|
+
|
|
199
|
+
case $VERSION_TYPE in
|
|
200
|
+
1)
|
|
201
|
+
NEW_VERSION=$PATCH_VERSION
|
|
202
|
+
;;
|
|
203
|
+
2)
|
|
204
|
+
NEW_VERSION=$MINOR_VERSION
|
|
205
|
+
;;
|
|
206
|
+
3)
|
|
207
|
+
NEW_VERSION=$MAJOR_VERSION
|
|
208
|
+
;;
|
|
209
|
+
4)
|
|
210
|
+
while true; do
|
|
211
|
+
read -p "请输入版本号 (如 1.0.0 或 1.0.0-beta.1): " CUSTOM_VERSION
|
|
212
|
+
|
|
213
|
+
if [[ -z "$CUSTOM_VERSION" ]]; then
|
|
214
|
+
print_error "版本号不能为空"
|
|
215
|
+
continue
|
|
216
|
+
fi
|
|
217
|
+
|
|
218
|
+
if ! validate_version "$CUSTOM_VERSION"; then
|
|
219
|
+
print_error "版本号格式无效,请使用语义化版本格式 (如 1.0.0 或 1.0.0-beta.1)"
|
|
220
|
+
continue
|
|
221
|
+
fi
|
|
222
|
+
|
|
223
|
+
NEW_VERSION=$CUSTOM_VERSION
|
|
224
|
+
break
|
|
225
|
+
done
|
|
226
|
+
;;
|
|
227
|
+
5)
|
|
228
|
+
rm package.json.backup
|
|
229
|
+
print_info "已取消发布"
|
|
230
|
+
exit 0
|
|
231
|
+
;;
|
|
232
|
+
esac
|
|
233
|
+
|
|
234
|
+
# 更新 package.json 中的版本号
|
|
235
|
+
if [[ "$DRY_RUN" == false ]]; then
|
|
236
|
+
npm version "$NEW_VERSION" --no-git-tag-version > /dev/null 2>&1 || {
|
|
237
|
+
print_error "更新版本号失败"
|
|
238
|
+
mv package.json.backup package.json
|
|
239
|
+
exit 1
|
|
240
|
+
}
|
|
241
|
+
fi
|
|
242
|
+
|
|
243
|
+
print_success "版本号已更新: ${CURRENT_VERSION} → ${NEW_VERSION}"
|
|
244
|
+
|
|
245
|
+
# 检查版本号是否已存在
|
|
246
|
+
if check_tag_exists "$NEW_VERSION"; then
|
|
247
|
+
print_error "版本 v${NEW_VERSION} 已存在于远程仓库"
|
|
248
|
+
mv package.json.backup package.json
|
|
249
|
+
exit 1
|
|
250
|
+
fi
|
|
251
|
+
|
|
252
|
+
# 运行测试(如果有)
|
|
253
|
+
if grep -q '"test"' package.json; then
|
|
254
|
+
print_step "运行测试..."
|
|
255
|
+
if [[ "$DRY_RUN" == false ]]; then
|
|
256
|
+
npm test || {
|
|
257
|
+
print_error "测试失败"
|
|
258
|
+
mv package.json.backup package.json
|
|
259
|
+
exit 1
|
|
260
|
+
}
|
|
261
|
+
fi
|
|
262
|
+
print_success "测试通过"
|
|
263
|
+
fi
|
|
264
|
+
|
|
265
|
+
# 构建项目
|
|
266
|
+
print_step "构建项目..."
|
|
267
|
+
if [[ "$DRY_RUN" == false ]]; then
|
|
268
|
+
npm run build
|
|
269
|
+
fi
|
|
270
|
+
print_success "构建完成"
|
|
271
|
+
|
|
272
|
+
# 检查构建产物
|
|
273
|
+
if [[ "$DRY_RUN" == false ]]; then
|
|
274
|
+
if [[ ! -f "dist/index.js" ]]; then
|
|
275
|
+
print_error "构建产物不存在: dist/index.js"
|
|
276
|
+
mv package.json.backup package.json
|
|
277
|
+
exit 1
|
|
278
|
+
fi
|
|
279
|
+
print_success "构建产物验证通过"
|
|
280
|
+
fi
|
|
281
|
+
|
|
282
|
+
# 生成 changelog
|
|
283
|
+
print_step "生成 CHANGELOG..."
|
|
284
|
+
if [[ "$DRY_RUN" == false ]]; then
|
|
285
|
+
npm run changelog
|
|
286
|
+
fi
|
|
287
|
+
print_success "CHANGELOG 已更新"
|
|
288
|
+
|
|
289
|
+
# 预览 changelog
|
|
290
|
+
if [[ "$DRY_RUN" == false ]]; then
|
|
291
|
+
echo ""
|
|
292
|
+
print_info "最新的 CHANGELOG 内容:"
|
|
293
|
+
echo "----------------------------------------"
|
|
294
|
+
head -n 30 CHANGELOG.md
|
|
295
|
+
echo "----------------------------------------"
|
|
296
|
+
echo ""
|
|
297
|
+
read -p "是否继续发布? (y/N) " -n 1 -r
|
|
298
|
+
echo
|
|
299
|
+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
300
|
+
print_info "已取消"
|
|
301
|
+
mv package.json.backup package.json
|
|
302
|
+
exit 0
|
|
303
|
+
fi
|
|
304
|
+
fi
|
|
305
|
+
|
|
306
|
+
# 删除备份
|
|
307
|
+
rm package.json.backup
|
|
308
|
+
|
|
309
|
+
if [[ "$DRY_RUN" == true ]]; then
|
|
310
|
+
echo ""
|
|
311
|
+
print_success "Dry-run 完成!以下是将要执行的操作:"
|
|
312
|
+
echo ""
|
|
313
|
+
echo " 1. 提交更改: package.json, package-lock.json, CHANGELOG.md"
|
|
314
|
+
echo " 2. Commit 信息: 🔖 chore(release): v${NEW_VERSION}"
|
|
315
|
+
echo " 3. 创建 tag: v${NEW_VERSION}"
|
|
316
|
+
echo " 4. 推送到 GitHub: ${CURRENT_BRANCH} + v${NEW_VERSION}"
|
|
317
|
+
echo " 5. 发布到 npm: @zjex/git-workflow@${NEW_VERSION}"
|
|
318
|
+
echo ""
|
|
319
|
+
print_info "执行 'npm run release' 进行实际发布"
|
|
320
|
+
exit 0
|
|
321
|
+
fi
|
|
322
|
+
|
|
323
|
+
# 最终确认
|
|
324
|
+
echo ""
|
|
325
|
+
print_warning "即将发布版本 v${NEW_VERSION} 到 npm 和 GitHub"
|
|
326
|
+
read -p "确认发布? (y/N) " -n 1 -r
|
|
327
|
+
echo
|
|
328
|
+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
329
|
+
print_info "已取消"
|
|
330
|
+
exit 0
|
|
331
|
+
fi
|
|
332
|
+
|
|
333
|
+
# 提交更改
|
|
334
|
+
print_step "提交更改..."
|
|
335
|
+
git add package.json package-lock.json CHANGELOG.md
|
|
336
|
+
git commit -m "🔖 chore(release): v${NEW_VERSION}"
|
|
337
|
+
print_success "更改已提交"
|
|
338
|
+
|
|
339
|
+
# 创建 tag
|
|
340
|
+
print_step "创建 tag: v${NEW_VERSION}..."
|
|
341
|
+
git tag -a "v${NEW_VERSION}" -m "Release v${NEW_VERSION}"
|
|
342
|
+
print_success "Tag 已创建"
|
|
343
|
+
|
|
344
|
+
# 推送到 GitHub
|
|
345
|
+
print_step "推送到 GitHub..."
|
|
346
|
+
git push origin "$CURRENT_BRANCH"
|
|
347
|
+
git push origin "v${NEW_VERSION}"
|
|
348
|
+
print_success "已推送到 GitHub"
|
|
349
|
+
|
|
350
|
+
# 发布到 npm
|
|
351
|
+
print_step "发布到 npm..."
|
|
352
|
+
npm publish
|
|
353
|
+
print_success "已发布到 npm"
|
|
354
|
+
|
|
355
|
+
echo ""
|
|
356
|
+
print_success "🎉 发布成功!"
|
|
357
|
+
echo ""
|
|
358
|
+
echo "版本: v${NEW_VERSION}"
|
|
359
|
+
echo "GitHub: https://github.com/iamzjt-front-end/git-workflow/releases/tag/v${NEW_VERSION}"
|
|
360
|
+
echo "npm: https://www.npmjs.com/package/@zjex/git-workflow/v/${NEW_VERSION}"
|
|
361
|
+
echo ""
|
|
362
|
+
print_info "提示: 可以在 GitHub 上创建 Release 并添加发布说明"
|
|
363
|
+
echo " https://github.com/iamzjt-front-end/git-workflow/releases/new?tag=v${NEW_VERSION}"
|
package/src/commands/branch.ts
CHANGED
|
@@ -49,6 +49,38 @@ export async function createBranch(
|
|
|
49
49
|
): Promise<void> {
|
|
50
50
|
const config = getConfig();
|
|
51
51
|
|
|
52
|
+
// 检查是否有未提交的更改
|
|
53
|
+
const hasChanges = execOutput("git status --porcelain");
|
|
54
|
+
if (hasChanges) {
|
|
55
|
+
console.log(colors.yellow("检测到未提交的更改:"));
|
|
56
|
+
console.log(colors.dim(hasChanges));
|
|
57
|
+
divider();
|
|
58
|
+
|
|
59
|
+
const shouldStash = await select({
|
|
60
|
+
message: "是否暂存 (stash) 这些更改后继续?",
|
|
61
|
+
choices: [
|
|
62
|
+
{ name: "是", value: true },
|
|
63
|
+
{ name: "否,取消操作", value: false },
|
|
64
|
+
],
|
|
65
|
+
theme,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
if (!shouldStash) {
|
|
69
|
+
console.log(colors.yellow("已取消"));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const stashSpinner = ora("正在暂存更改...").start();
|
|
74
|
+
try {
|
|
75
|
+
exec('git stash push -m "auto stash before branch switch"', true);
|
|
76
|
+
stashSpinner.succeed("更改已暂存,切换分支后可用 gw s 恢复");
|
|
77
|
+
} catch {
|
|
78
|
+
stashSpinner.fail("暂存失败");
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
divider();
|
|
82
|
+
}
|
|
83
|
+
|
|
52
84
|
const branchName = await getBranchName(type);
|
|
53
85
|
if (!branchName) return;
|
|
54
86
|
|
|
@@ -118,15 +150,32 @@ export async function deleteBranch(branchArg?: string): Promise<void> {
|
|
|
118
150
|
|
|
119
151
|
let branch = branchArg;
|
|
120
152
|
|
|
153
|
+
// 如果传入的是 origin/xxx 格式,提取分支名
|
|
154
|
+
if (branch?.startsWith("origin/")) {
|
|
155
|
+
branch = branch.replace("origin/", "");
|
|
156
|
+
}
|
|
157
|
+
|
|
121
158
|
if (!branch) {
|
|
122
|
-
|
|
159
|
+
// 获取本地分支
|
|
160
|
+
const localBranches = execOutput(
|
|
123
161
|
"git for-each-ref --sort=-committerdate refs/heads/ --format='%(refname:short)'"
|
|
124
162
|
)
|
|
125
163
|
.split("\n")
|
|
126
164
|
.filter((b) => b && b !== currentBranch);
|
|
127
165
|
|
|
128
|
-
|
|
129
|
-
|
|
166
|
+
// 获取远程分支(排除 HEAD 和已有本地分支的)
|
|
167
|
+
const remoteBranches = execOutput(
|
|
168
|
+
"git for-each-ref --sort=-committerdate refs/remotes/origin/ --format='%(refname:short)'"
|
|
169
|
+
)
|
|
170
|
+
.split("\n")
|
|
171
|
+
.map((b) => b.replace("origin/", ""))
|
|
172
|
+
.filter(
|
|
173
|
+
(b) =>
|
|
174
|
+
b && b !== "HEAD" && b !== currentBranch && !localBranches.includes(b)
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
if (localBranches.length === 0 && remoteBranches.length === 0) {
|
|
178
|
+
console.log(colors.yellow("没有可删除的分支"));
|
|
130
179
|
return;
|
|
131
180
|
}
|
|
132
181
|
|
|
@@ -135,13 +184,25 @@ export async function deleteBranch(branchArg?: string): Promise<void> {
|
|
|
135
184
|
value: string;
|
|
136
185
|
}
|
|
137
186
|
|
|
138
|
-
const choices: BranchChoice[] =
|
|
139
|
-
|
|
140
|
-
|
|
187
|
+
const choices: BranchChoice[] = [];
|
|
188
|
+
|
|
189
|
+
// 本地分支
|
|
190
|
+
localBranches.forEach((b) => {
|
|
191
|
+
const hasRemote = execOutput(`git branch -r --list "origin/${b}"`);
|
|
192
|
+
choices.push({
|
|
141
193
|
name: hasRemote ? `${b} (本地+远程)` : `${b} (仅本地)`,
|
|
142
194
|
value: b,
|
|
143
|
-
};
|
|
195
|
+
});
|
|
144
196
|
});
|
|
197
|
+
|
|
198
|
+
// 仅远程分支
|
|
199
|
+
remoteBranches.forEach((b) => {
|
|
200
|
+
choices.push({
|
|
201
|
+
name: `${b} (仅远程)`,
|
|
202
|
+
value: `__remote__${b}`,
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
145
206
|
choices.push({ name: "取消", value: "__cancel__" });
|
|
146
207
|
|
|
147
208
|
branch = await select({
|
|
@@ -154,6 +215,36 @@ export async function deleteBranch(branchArg?: string): Promise<void> {
|
|
|
154
215
|
console.log(colors.yellow("已取消"));
|
|
155
216
|
return;
|
|
156
217
|
}
|
|
218
|
+
|
|
219
|
+
// 处理仅远程分支的情况
|
|
220
|
+
if (branch.startsWith("__remote__")) {
|
|
221
|
+
const remoteBranch = branch.replace("__remote__", "");
|
|
222
|
+
|
|
223
|
+
const confirm = await select({
|
|
224
|
+
message: `确认删除远程分支 origin/${remoteBranch}?`,
|
|
225
|
+
choices: [
|
|
226
|
+
{ name: "是", value: true },
|
|
227
|
+
{ name: "否", value: false },
|
|
228
|
+
],
|
|
229
|
+
theme,
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
if (!confirm) {
|
|
233
|
+
console.log(colors.yellow("已取消"));
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const spinner = ora(`正在删除远程分支: origin/${remoteBranch}`).start();
|
|
238
|
+
try {
|
|
239
|
+
execSync(`git push origin --delete "${remoteBranch}"`, {
|
|
240
|
+
stdio: "pipe",
|
|
241
|
+
});
|
|
242
|
+
spinner.succeed(`远程分支已删除: origin/${remoteBranch}`);
|
|
243
|
+
} catch {
|
|
244
|
+
spinner.fail("远程分支删除失败");
|
|
245
|
+
}
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
157
248
|
}
|
|
158
249
|
|
|
159
250
|
if (branch === currentBranch) {
|
|
@@ -162,7 +253,7 @@ export async function deleteBranch(branchArg?: string): Promise<void> {
|
|
|
162
253
|
}
|
|
163
254
|
|
|
164
255
|
const localExists = execOutput(`git branch --list "${branch}"`);
|
|
165
|
-
const hasRemote = execOutput(`git branch -r
|
|
256
|
+
const hasRemote = execOutput(`git branch -r --list "origin/${branch}"`);
|
|
166
257
|
|
|
167
258
|
if (!localExists) {
|
|
168
259
|
if (hasRemote) {
|
|
@@ -170,7 +261,7 @@ export async function deleteBranch(branchArg?: string): Promise<void> {
|
|
|
170
261
|
colors.yellow(`本地分支不存在,但远程分支存在: origin/${branch}`)
|
|
171
262
|
);
|
|
172
263
|
const deleteRemote = await select({
|
|
173
|
-
message:
|
|
264
|
+
message: `确认删除远程分支 origin/${branch}?`,
|
|
174
265
|
choices: [
|
|
175
266
|
{ name: "是", value: true },
|
|
176
267
|
{ name: "否", value: false },
|
|
@@ -193,6 +284,23 @@ export async function deleteBranch(branchArg?: string): Promise<void> {
|
|
|
193
284
|
return;
|
|
194
285
|
}
|
|
195
286
|
|
|
287
|
+
// 删除本地分支前确认
|
|
288
|
+
const confirmDelete = await select({
|
|
289
|
+
message: `确认删除分支 ${branch}?${
|
|
290
|
+
hasRemote ? " (本地+远程)" : " (仅本地)"
|
|
291
|
+
}`,
|
|
292
|
+
choices: [
|
|
293
|
+
{ name: "是", value: true },
|
|
294
|
+
{ name: "否", value: false },
|
|
295
|
+
],
|
|
296
|
+
theme,
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
if (!confirmDelete) {
|
|
300
|
+
console.log(colors.yellow("已取消"));
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
|
|
196
304
|
const localSpinner = ora(`正在删除本地分支: ${branch}`).start();
|
|
197
305
|
try {
|
|
198
306
|
execSync(`git branch -D "${branch}"`, { stdio: "pipe" });
|
|
@@ -203,25 +311,12 @@ export async function deleteBranch(branchArg?: string): Promise<void> {
|
|
|
203
311
|
}
|
|
204
312
|
|
|
205
313
|
if (hasRemote) {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
{ name: "否", value: false },
|
|
213
|
-
],
|
|
214
|
-
theme,
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
if (deleteRemote) {
|
|
218
|
-
const remoteSpinner = ora(`正在删除远程分支: origin/${branch}`).start();
|
|
219
|
-
try {
|
|
220
|
-
execSync(`git push origin --delete "${branch}"`, { stdio: "pipe" });
|
|
221
|
-
remoteSpinner.succeed(`远程分支已删除: origin/${branch}`);
|
|
222
|
-
} catch {
|
|
223
|
-
remoteSpinner.fail("远程分支删除失败");
|
|
224
|
-
}
|
|
314
|
+
const remoteSpinner = ora(`正在删除远程分支: origin/${branch}`).start();
|
|
315
|
+
try {
|
|
316
|
+
execSync(`git push origin --delete "${branch}"`, { stdio: "pipe" });
|
|
317
|
+
remoteSpinner.succeed(`远程分支已删除: origin/${branch}`);
|
|
318
|
+
} catch {
|
|
319
|
+
remoteSpinner.fail("远程分支删除失败");
|
|
225
320
|
}
|
|
226
321
|
}
|
|
227
322
|
}
|