git快捷脚本
zoe.X Lv2

git快捷脚本

本文介绍一套适用于 macOS 和 Windows 的多仓库 Git 快捷管理脚本,支持自动化提交、推送、AI 生成提交信息、批量管理等功能,极大提升日常开发效率。


功能概览

  1. 自动拉取、提交、推送:一键同步所有配置仓库,自动检测更改并推送。
  2. 仅拉取更新:批量拉取所有仓库的远程最新内容。
  3. 仅推送更改:批量推送本地未同步的提交。
  4. 添加临时目录:支持临时添加额外仓库目录进行同步管理。
  5. AI生成提交信息:通过 commit.py 脚本,自动生成符合 Conventional Commits 规范的提交说明。
  6. 重置到远程分支:一键将本地分支重置为远程最新状态(危险操作,慎用)。
  7. 回退上一个提交:快速回退最近一次提交并强制推送。
  8. 查看分支和提交信息:批量查看所有仓库的分支、提交历史及本地/远程差异。
  9. 管理Git用户配置:批量查看和修改全局/仓库级 Git 用户信息。

使用说明

1. 环境准备

  • Python 依赖:需安装 Python 3 和 requests 库。
  • AI提交信息:需注册并在commit.py中配置 OpenAI 兼容 API(如 OpenAI/GPT4/自建服务等),获取 API_KEY 和 BASE_URL。
  • 脚本权限:macOS 下需为 git.sh 脚本赋予可执行权限:
    1
    chmod +x git.sh

2. 关键变量配置

  • REPO_DIRS:在git脚本顶部配置需要批量管理的仓库绝对路径。
  • git脚本:在git脚本中配置commit.py的路径和你能够运行commit.py的Python解释器路径。
    • macOS 示例:/opt/anaconda3/bin/python /Users/zhouzhou/python/test/commit.py
    • Windows 示例:C:\Python39\python.exe C:\path\to\commit.py
  • commit.py 路径:在 git 脚本中指定 commit.py 的绝对路径。
  • python 路径:如有多版本 Python,需指定正确的解释器路径。

3. AI自动生成提交信息

  • 脚本会自动调用 commit.py,根据 git diff 内容生成规范化的中文提交信息。
  • commit.py 支持自定义模型、API 地址、Token 长度等参数,详见脚本内注释。

4. 脚本运行方式

  • macOS:直接运行 git.sh,按菜单提示操作。
  • Windows:推荐使用 git.bat 启动 git.ps1,保证编码和兼容性。

目录

  1. commit.py 说明
  2. macOS 脚本 git.sh 说明
  3. Windows 脚本 git.bat/git.ps1 说明

commit.py 说明

用于自动生成符合 Conventional Commits 规范的提交信息,支持中文输出。需配置 API_KEY、BASE_URL、MODEL_NAME 等参数。

主要功能:

  • 获取 git diff 或 git show HEAD 内容
  • 调用 AI API 生成简洁、规范的提交信息
  • 兼容多平台编码

使用方法:

  1. 配置好 API_KEY、BASE_URL、MODEL_NAME。
  2. git.sh 或 git.ps1 中调用该脚本。
  3. 支持通过标准输入传递 diff 内容。

macOS 脚本 git.sh 说明

主要功能:

  • 支持批量仓库自动拉取、提交、推送
  • 支持 AI 生成提交信息
  • 支持临时目录、分支管理、用户配置等

使用方法:

  1. 编辑 REPO_DIRS,添加需要管理的仓库路径
  2. 赋予执行权限:chmod +x git.sh
  3. 运行脚本:./git.sh
  4. 按菜单选择对应操作

Windows 脚本 git.bat/git.ps1 说明

主要功能:

  • 兼容 Windows 环境的批量仓库管理
  • 支持 PowerShell 菜单交互
  • 推荐用 git.bat 启动 git.ps1,避免编码问题

使用方法:

  1. 编辑 $REPO_DIRS,添加需要管理的仓库路径
  2. 双击 git.bat 或在命令行运行
  3. 按菜单选择对应操作

代码示例

以下为各平台脚本示例,代码块内容请根据电脑环境进行变量或路径修改

commit.py代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# commit.py
import subprocess
import json
import os
import requests # 需要安装: pip install requests
import sys
import io # 用于 TextIOWrapper

# --- 配置 ---
# 你的 OpenAI 兼容 API 的 Base URL
BASE_URL = os.environ.get("OPENAI_API_BASE", "https://yourbaseurl/v1")
# 你的 API Key
API_KEY = os.environ.get("OPENAI_API_KEY", "sk-ssfgyygvfsdfdgfhjhytgrfOIcQ")
# 使用的模型名称
MODEL_NAME = "gpt-4.1-mini" # 或者你部署的模型名
# 生成commit message的最大token数
MAX_TOKENS = 60 # 适当调整,常规提交信息不需要太长
# 控制输出的随机性
TEMPERATURE = 0.3
# --- END 配置 ---

SYSTEM_PROMPT = """你是一个专业的Git提交信息生成器。
请根据提供的 'git diff --staged' 输出或 'git show HEAD' 输出,生成一个简短、清晰且符合 Conventional Commits 规范的提交信息。
规范格式为: <type>(<scope>): <subject>
例如: feat: add new login button
fix(parser): handle malformed input
docs: update README with API instructions

- type: feat, fix, docs, style, refactor, perf, test, chore
- scope: 可选,指明影响范围
- subject: 动词开头,现在时态,简洁描述。

只输出最终的commit信息本身,不要包含任何解释、前缀如 "Commit message:" 或 markdown 代码块标记。
请使用中文回答。
"""

def get_staged_diff():
"""获取 git staged 的 diff 内容"""
try:
result = subprocess.run(
["git", "diff", "--staged", "--patch", "--unified=0"],
capture_output=True,
check=True
)
# Git diff的输出编码可能受多种因素影响,但通常目标是UTF-8
# 我们假设它是UTF-8,如果不是,替换掉有问题的字符
return result.stdout.decode('utf-8', errors='replace').strip()
except subprocess.CalledProcessError as e:
stderr_msg = e.stderr.decode('utf-8', errors='replace').strip() if e.stderr else "N/A"
print(f"获取 git diff 失败: {e}. Stderr: {stderr_msg}", file=sys.stderr)
return None
except FileNotFoundError:
print("错误: git 命令未找到。请确保 git 已安装并在 PATH 中。", file=sys.stderr)
return None

def generate_commit_message(diff_content):
"""调用 AI API 生成 commit message"""
if not diff_content or not diff_content.strip():
# AI 对于空 diff 可能会返回通用消息,或者我们直接定义一个更明确的
return "chore: no meaningful changes detected to generate commit message"

if BASE_URL == "YOUR_OPENAI_COMPATIBLE_BASE_URL/v1": # 示例检查,保留或按需修改
print("错误:请在脚本中或通过环境变量 OPENAI_API_BASE 设置 BASE_URL。", file=sys.stderr)
return None
if "openai.com" in BASE_URL and (not API_KEY or API_KEY == "YOUR_API_KEY"): # 示例检查
print("错误:使用 OpenAI 官方 API 时必须提供 API_KEY。", file=sys.stderr)
return None

headers = {
"Content-Type": "application/json; charset=utf-8",
}
if API_KEY and API_KEY != "YOUR_API_KEY": # 避免使用占位符 API Key
headers["Authorization"] = f"Bearer {API_KEY}"

user_prompt = f"请为以下 git diff/show 内容生成一个 commit message:\n\n```diff\n{diff_content}\n```"

payload = {
"model": MODEL_NAME,
"messages": [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_prompt}
],
"max_tokens": MAX_TOKENS,
"temperature": TEMPERATURE,
"stream": False
}

api_endpoint = f"{BASE_URL.rstrip('/')}/chat/completions"

try:
response = requests.post(api_endpoint, headers=headers, json=payload, timeout=45) # 增加超时
response.raise_for_status()
response_data = response.json()

if "choices" in response_data and len(response_data["choices"]) > 0:
message_content = response_data["choices"][0].get("message", {}).get("content", "")
# 清理AI可能添加的多余字符
message_content = message_content.strip()
# 移除常见的 markdown 代码块标记,即使 prompt 要求不要加
if message_content.startswith("```") and message_content.endswith("```"):
# 移除包裹的```text ```, ```json ```, ``` ```等
message_content = message_content[message_content.find('\n')+1:-3].strip() if '\n' in message_content else message_content[3:-3].strip()

message_content = message_content.removeprefix("`").removesuffix("`")
message_content = message_content.strip('"').strip("'")
return message_content.strip()
else:
error_details = response_data.get("error", {}).get("message", "No error message in response.")
print(f"错误:API 响应中没有找到有效的 'choices'。API 返回错误: {error_details} 完整响应: {response.text}", file=sys.stderr)
return None

except requests.exceptions.Timeout:
print(f"API 请求超时 ({api_endpoint})。", file=sys.stderr)
return None
except requests.exceptions.RequestException as e:
print(f"API 请求失败: {e}", file=sys.stderr)
if hasattr(e, 'response') and e.response is not None:
print(f"API 响应状态码: {e.response.status_code}, 内容: {e.response.text}", file=sys.stderr)
return None
except json.JSONDecodeError:
print(f"错误:解析API响应失败,不是有效的JSON。响应: {response.text}", file=sys.stderr)
return None


if __name__ == "__main__":
# 确保标准输入/输出/错误流使用 UTF-8 编码
# 这对于 macOS 通常是默认的,但显式设置更健壮
sys.stdin = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8', errors='surrogateescape')
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') # 用replace避免输出时出错
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')

# Windows console encoding (如果需要在Windows上运行)
if sys.platform.startswith('win'):
try:
import ctypes
kernel32 = ctypes.windll.kernel32
# 65001 is UTF-8, but might cause issues with some Windows shells / fonts
# kernel32.SetConsoleOutputCP(65001)
# kernel32.SetConsoleCP(65001)
# Fallback to a common codepage if UTF-8 setup is problematic, or rely on Python's default.
# For this script, primarily concerned with macOS, this part is less critical.
except Exception as e:
print(f"无法设置Windows控制台编码: {e}", file=sys.stderr)


diff_content_input = "" # 用于存储从 stdin 或 git diff 获取的原始内容
diff_source_description = "" # 用于日志/错误消息

# 优先从 stdin 读取 diff 内容 (用于 amend_last_commit_msg)
if not sys.stdin.isatty(): # True if input is piped or via here-string
diff_source_description = "标准输入 (stdin)"
# 以二进制方式读取标准输入,然后尝试解码
input_bytes = sys.stdin.buffer.read() # sys.stdin 被重新包装后,buffer 仍然是原始字节流
try:
diff_content_input = input_bytes.decode('utf-8')
except UnicodeDecodeError:
print(f"警告: 来自 {diff_source_description} 的输入不是有效的 UTF-8。尝试 GBK 解码...", file=sys.stderr)
try:
diff_content_input = input_bytes.decode('gbk', errors='replace')
except UnicodeDecodeError: # Should not happen if errors='replace'
print(f"警告: 来自 {diff_source_description} 的输入也不是有效的 GBK。最终使用 UTF-8 并替换无效字符...", file=sys.stderr)
diff_content_input = input_bytes.decode('utf-8', errors='replace')
else: # (用于自动提交和手动推送模式)
diff_source_description = "'git diff --staged'"
diff_content_input = get_staged_diff()

if diff_content_input is None: # get_staged_diff() 失败
# 错误信息已在 get_staged_diff() 内部打印到 stderr
exit(1)

# 处理 diff 内容为空的情况
if not diff_content_input.strip():
if diff_source_description == "'git diff --staged'":
print("没有暂存的更改可以提交。", file=sys.stderr) # 输出到 stderr
# 对于空的 staged diff, 正常退出,不生成提交信息。
# bash 脚本的 `if [ -z "$msg" ]` 会捕获到 stdout 为空,并使用默认提交信息。
exit(0)
else: # 来自 stdin 的内容为空 (例如 amend 一个空提交的 diff)
print(f"从 {diff_source_description} 读取的内容为空或仅含空白字符。将尝试让AI处理此情况。", file=sys.stderr)
# diff_content_input 已经是空字符串或仅含空格,将传递给 generate_commit_message
# generate_commit_message 会返回 "chore: no meaningful changes..."

# 限制diff长度,避免请求体过大或token超限
# Gemini 1.5 Flash 有 1M token 上下文,但其他模型可能更小。
# 30000 字符约 7500 英文 token 或更少中文 token,是一个相对安全的值。
max_diff_length = 800000
if len(diff_content_input) > max_diff_length:
print(f"警告: 来自 {diff_source_description} 的 Diff 内容过长 ({len(diff_content_input)} characters),将截断为 {max_diff_length} characters。", file=sys.stderr)
diff_content_input = diff_content_input[:max_diff_length] + "\n... (diff truncated)"

commit_msg = generate_commit_message(diff_content_input)

if commit_msg:
print(commit_msg, flush=True) # 确保立即输出到 stdout
exit(0)
else:
# 详细错误已在 generate_commit_message 中打印到 stderr
print("最终未能生成 commit message。", file=sys.stderr)
exit(1)

mac下的git脚本:git.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
#!/bin/bash

# 配置需要同步的仓库目录(修改这里)
REPO_DIRS=(
"/Users/zhouzhou/Desktop/edit/documents"
"/Users/zhouzhou/Desktop/edit/uptime"
"/Users/zhouzhou/hexo/keep"
"/Users/zhouzhou/Code"
"/Users/zhouzhou/Desktop/edit/study"
)

# 临时目录数组
TEMP_DIRS=()

# 颜色定义
RED='\033[1;31m'
GREEN='\033[1;32m'
YELLOW='\033[1;33m'
BLUE='\033[1;34m'
NC='\033[0m' # No Color

# 主菜单
show_menu() {
clear
echo -e "${BLUE}Git 仓库同步工具 (macOS 版)${NC}"
echo "========================================"
echo "1) 自动模式 (拉取 + 自动提交推送)"
echo "2) 仅拉取更新"
echo "3) 仅推送更改"
echo "4) 退出"
echo "5) 添加临时目录"
echo "6) 修改上次提交信息(AI 生成)"
echo "7) 重置到远程分支"
echo "8) 回退上一个提交"
echo "9) 查看分支和提交信息"
echo "10) 管理Git用户配置"
echo "========================================"
}

# 修改上次提交信息(AI 生成)
amend_last_commit_msg() {
local ALL_DIRS=("${REPO_DIRS[@]}" "${TEMP_DIRS[@]}")
for dir in "${ALL_DIRS[@]}"; do
echo -e "\n${BLUE}正在处理: ${dir}${NC}"
echo "----------------------------------------"
if [ ! -d "$dir" ]; then
echo -e "${RED}❌ 错误: 目录不存在${NC}"
continue
fi
cd "$dir" || { echo -e "${RED}❌ 错误: 无法进入目录${NC}"; continue; }
# 检查是否有提交
if git rev-parse HEAD >/dev/null 2>&1; then
# 获取原始提交信息
original_msg=$(git log -1 --pretty=%B)
echo -e "${YELLOW}当前提交信息: ${original_msg}${NC}"

# 获取新的提交信息(基于上次提交内容)
commit_content=$(git show HEAD)
msg=$(/opt/anaconda3/bin/python /Users/zhouzhou/python/test/commit.py <<< "$commit_content")
if [ -z "$msg" ]; then
msg="AI修正提交信息 [$(date '+%Y-%m-%d %H:%M:%S')]"
fi
echo -e "${GREEN}新的提交信息: ${msg}${NC}"

# 询问是否要修改提交信息
read -rp "是否修改并推送此仓库的提交信息?(y/n): " confirm
if [[ $confirm =~ ^[Yy]$ ]]; then
git commit --amend -m "$msg"
git push --force-with-lease
echo -e "${GREEN}✅ 已修改并推送提交信息${NC}"
else
echo -e "${YELLOW}⚠️ 已跳过此仓库${NC}"
fi
else
echo -e "${YELLOW}⚠️ 当前仓库没有提交,跳过${NC}"
fi
echo "----------------------------------------"
sleep 1
done
}

# 重置到远程分支函数
reset_to_remote() {
local ALL_DIRS=("${REPO_DIRS[@]}" "${TEMP_DIRS[@]}")
for dir in "${ALL_DIRS[@]}"; do
echo -e "\n${BLUE}正在处理: ${dir}${NC}"
echo "----------------------------------------"
if [ ! -d "$dir" ]; then
echo -e "${RED}❌ 错误: 目录不存在${NC}"
continue
fi
cd "$dir" || { echo -e "${RED}❌ 错误: 无法进入目录${NC}"; continue; }

# 获取当前分支名
current_branch=$(git rev-parse --abbrev-ref HEAD)
echo -e "${YELLOW}当前分支: ${current_branch}${NC}"

read -rp "是否重置本地分支到远程 origin/${current_branch}?这将丢弃所有本地更改!(y/n): " confirm
if [[ $confirm =~ ^[Yy]$ ]]; then
echo -e "${YELLOW}正在重置到 origin/${current_branch}...${NC}"
git fetch
git reset --hard origin/"${current_branch}"
echo -e "${GREEN}✅ 重置成功${NC}"
else
echo -e "${YELLOW}⚠️ 已取消重置操作${NC}"
fi
echo "----------------------------------------"
sleep 1
done
}

# 回退上一个提交函数
revert_last_commit() {
local ALL_DIRS=("${REPO_DIRS[@]}" "${TEMP_DIRS[@]}")
for dir in "${ALL_DIRS[@]}"; do
echo -e "\n${BLUE}正在处理: ${dir}${NC}"
echo "----------------------------------------"
if [ ! -d "$dir" ]; then
echo -e "${RED}❌ 错误: 目录不存在${NC}"
continue
fi
cd "$dir" || { echo -e "${RED}❌ 错误: 无法进入目录${NC}"; continue; }

# 检查是否有提交
if git rev-parse HEAD >/dev/null 2>&1; then
# 获取当前提交信息
current_commit=$(git rev-parse --short HEAD)
previous_commit=$(git rev-parse --short HEAD~1)
current_msg=$(git log -1 --pretty=%B)
echo -e "${YELLOW}当前提交: ${current_commit} - ${current_msg}${NC}"
echo -e "${YELLOW}上一提交: ${previous_commit}${NC}"

read -rp "是否回退到上一个提交? 这将删除最近的提交! (y/n): " confirm
if [[ $confirm =~ ^[Yy]$ ]]; then
echo -e "${YELLOW}正在回退到上一个提交...${NC}"
git reset --hard HEAD~1
git push --force-with-lease
echo -e "${GREEN}✅ 已成功回退并推送${NC}"
else
echo -e "${YELLOW}⚠️ 已取消回退操作${NC}"
fi
else
echo -e "${YELLOW}⚠️ 当前仓库没有提交,跳过${NC}"
fi
echo "----------------------------------------"
sleep 1
done
}

# 处理单个仓库
process_repo() {
local dir="$1"
local mode="$2"

echo -e "\n${BLUE}正在处理: ${dir}${NC}"
echo "----------------------------------------"

# 检查目录是否存在
if [ ! -d "$dir" ]; then
echo -e "${RED}❌ 错误: 目录不存在${NC}"
return 1
fi

cd "$dir" || {
echo -e "${RED}❌ 错误: 无法进入目录${NC}"
return 1
}

case $mode in
1) # 自动模式
echo -e "${YELLOW}🔄 正在同步仓库...${NC}"
git pull
if [ -n "$(git status --porcelain)" ]; then
echo -e "${YELLOW}⚡ 检测到未提交修改${NC}"
git add .
# AI生成commit message
msg=$(/opt/anaconda3/bin/python /Users/zhouzhou/python/test/commit.py)
# 这里是特定的python路径和commit.py脚本路径,请根据实际情况修改
if [ -z "$msg" ]; then
msg="自动提交 [$(date '+%Y-%m-%d %H:%M:%S')]"
fi
git commit -m "$msg"
git push
elif [ -n "$(git cherry -v)" ]; then
echo -e "${YELLOW}⬆️ 推送未同步提交${NC}"
git push
else
echo -e "${GREEN}✅ 仓库已同步${NC}"
fi
;;
2) # 仅拉取
echo -e "${YELLOW}⬇️ 正在拉取更新...${NC}"
git pull
;;
3) # 仅推送
if [ -n "$(git status --porcelain)" ]; then
echo -e "${YELLOW}⚡ 发现未提交修改${NC}"

# 先添加所有更改
git add .

# 使用循环允许重新生成提交信息
while true; do
# 使用AI生成提交信息
echo -e "${YELLOW}正在生成提交信息...${NC}"
msg=$(/opt/anaconda3/bin/python /Users/zhouzhou/python/test/commit.py)

# 检查生成是否成功
if [ -z "$msg" ]; then
echo -e "${RED}⚠️ 生成提交信息失败${NC}"
msg="手动提交 [$(date '+%Y-%m-%d %H:%M:%S')]"
fi

# 显示生成的信息
echo -e "${GREEN}AI生成的提交信息: ${msg}${NC}"

# 询问用户操作
echo "请选择操作:"
echo "1) 使用此提交信息"
echo "2) 重新生成提交信息"
echo "3) 手动输入提交信息"
echo "4) 取消提交"
read -rp "请选择 (1-4) [默认:1]: " commit_choice

# 设置默认值为1
if [ -z "$commit_choice" ]; then
commit_choice="1"
fi

case $commit_choice in
1)
# 使用生成的信息
break
;;
2)
# 重新生成信息
continue
;;
3)
# 手动输入信息
read -rp "请输入提交信息: " msg
break
;;
4)
# 取消提交
echo -e "${YELLOW}⚠️ 已取消提交${NC}"
git reset
return 0
;;
*)
echo -e "${RED}⚠️ 无效选择,请重新输入${NC}"
;;
esac
done

# 提交更改
echo -e "${YELLOW}正在提交...${NC}"
git commit -m "$msg"

# 询问是否推送
read -rp "是否将更改推送到远程?(y/n) [默认:y]: " push_confirm

# 设置默认值为y
if [ -z "$push_confirm" ]; then
push_confirm="y"
fi
if [[ $push_confirm =~ ^[Yy]$ ]]; then
echo -e "${YELLOW}正在推送...${NC}"
git push
echo -e "${GREEN}✅ 已推送更改${NC}"
else
echo -e "${YELLOW}⚠️ 仅提交本地,未推送${NC}"
fi
else
# 检查是否有未推送的提交
if [ -n "$(git cherry -v)" ]; then
echo -e "${YELLOW}⬆️ 发现未推送的提交${NC}"
read -rp "是否推送?(y/n): " push_confirm
if [[ $push_confirm =~ ^[Yy]$ ]]; then
echo -e "${YELLOW}正在推送...${NC}"
git push
echo -e "${GREEN}✅ 已推送更改${NC}"
else
echo -e "${YELLOW}⚠️ 已取消推送${NC}"
fi
else
echo -e "${GREEN}✅ 仓库已是最新状态,无需推送${NC}"
fi
fi
;;
esac

echo "----------------------------------------"
sleep 1
}

# 添加临时目录函数
add_temp_dir() {
read -rp "请输入要添加的临时目录: " new_dir

# 检查目录是否存在
if [ ! -d "$new_dir" ]; then
echo -e "${RED}❌ 错误: 目录不存在${NC}"
return 1
fi

# 检查是否重复添加
for dir in "${REPO_DIRS[@]}"; do
if [ "$dir" = "$new_dir" ]; then
echo -e "${RED}❌ 错误: 目录已存在于永久目录中${NC}"
return 1
fi
done

for dir in "${TEMP_DIRS[@]}"; do
if [ "$dir" = "$new_dir" ]; then
echo -e "${RED}❌ 错误: 目录已存在于临时目录中${NC}"
return 1
fi
done

# 添加到临时目录数组
TEMP_DIRS+=("$new_dir")
echo -e "${GREEN}✅ 已添加临时目录: $new_dir${NC}"
}


# 处理所有仓库
process_all_repos() {
local mode="$1"
local ALL_DIRS=("${REPO_DIRS[@]}" "${TEMP_DIRS[@]}") # 合并永久和临时目录

for repo in "${ALL_DIRS[@]}"; do
process_repo "$repo" "$mode"
done
}

# 查看分支和提交信息函数
show_branches_and_commits() {
local ALL_DIRS=("${REPO_DIRS[@]}" "${TEMP_DIRS[@]}")
for dir in "${ALL_DIRS[@]}"; do
echo -e "\n${BLUE}正在处理: ${dir}${NC}"
echo "----------------------------------------"
if [ ! -d "$dir" ]; then
echo -e "${RED}❌ 错误: 目录不存在${NC}"
continue
fi
cd "$dir" || { echo -e "${RED}❌ 错误: 无法进入目录${NC}"; continue; }

# 检查是否是Git仓库
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
echo -e "${RED}❌ 错误: 不是一个Git仓库${NC}"
continue
fi

# 获取当前分支
current_branch=$(git rev-parse --abbrev-ref HEAD)
echo -e "${GREEN}当前分支: ${current_branch}${NC}"

# 更新远程分支信息
echo -e "${YELLOW}更新远程信息...${NC}"
git fetch --quiet

# 显示本地分支
echo -e "\n${BLUE}本地分支:${NC}"
git branch | sed 's/^/ /'

# 显示远程分支
echo -e "\n${BLUE}远程分支:${NC}"
git branch -r | grep -v "HEAD" | sed 's/^/ /'

# 本地与远程的差异
if [ -n "$(git rev-parse --abbrev-ref --symbolic-full-name @{u} 2>/dev/null)" ]; then
echo -e "\n${BLUE}本地与远程差异:${NC}"
ahead=$(git rev-list @{u}..HEAD --count)
behind=$(git rev-list HEAD..@{u} --count)

if [ "$ahead" -gt 0 ] && [ "$behind" -gt 0 ]; then
echo -e " ${YELLOW}本地领先 ${ahead} 个提交,落后 ${behind} 个提交${NC}"
elif [ "$ahead" -gt 0 ]; then
echo -e " ${GREEN}本地领先 ${ahead} 个提交${NC}"
elif [ "$behind" -gt 0 ]; then
echo -e " ${RED}本地落后 ${behind} 个提交${NC}"
else
echo -e " ${GREEN}本地与远程同步${NC}"
fi
else
echo -e "\n${YELLOW}⚠️ 当前分支没有对应的远程分支${NC}"
fi

# 显示最近的提交
echo -e "\n${BLUE}最近提交:${NC}"
git log -5 --pretty=format:" %h %s [%an, %ar]" --abbrev-commit

echo -e "\n${YELLOW}==========================================${NC}"

# 询问是否查看详细信息
read -rp "查看详细的远程差异?(y/n): " show_detail
if [[ $show_detail =~ ^[Yy]$ ]]; then
if [ -n "$(git rev-parse --abbrev-ref --symbolic-full-name @{u} 2>/dev/null)" ]; then
# 显示具体的差异内容
echo -e "\n${BLUE}与远程的具体差异:${NC}"

# 如果本地领先,显示远程没有的提交
if [ "$ahead" -gt 0 ]; then
echo -e "\n${YELLOW}本地有而远程没有的提交:${NC}"
git log @{u}..HEAD --pretty=format:" %h %s [%an, %ar]" --abbrev-commit
fi

# 如果本地落后,显示本地没有的提交
if [ "$behind" -gt 0 ]; then
echo -e "\n${YELLOW}远程有而本地没有的提交:${NC}"
git log HEAD..@{u} --pretty=format:" %h %s [%an, %ar]" --abbrev-commit
fi
else
echo -e "${YELLOW}⚠️ 无法显示差异,当前分支没有关联的远程分支${NC}"
fi
fi

echo "----------------------------------------"
sleep 1
done
}

# 管理Git用户配置函数
manage_git_user_config() {
local ALL_DIRS=("${REPO_DIRS[@]}" "${TEMP_DIRS[@]}")

while true; do
clear
echo -e "${BLUE}Git 用户配置管理${NC}"
echo "========================================"
echo "1) 查看全局用户配置"
echo "2) 查看所有仓库的用户配置"
echo "3) 修改全局用户配置"
echo "4) 修改指定仓库的用户配置"
echo "5) 返回主菜单"
echo "========================================"

read -rp "请选择操作 (1-5): " config_choice

case $config_choice in
1)
# 查看全局用户配置
echo -e "\n${BLUE}全局Git用户配置:${NC}"
echo "----------------------------------------"
global_name=$(git config --global user.name 2>/dev/null)
global_email=$(git config --global user.email 2>/dev/null)

if [ -n "$global_name" ]; then
echo -e "${GREEN}用户名: $global_name${NC}"
else
echo -e "${YELLOW}用户名: 未设置${NC}"
fi

if [ -n "$global_email" ]; then
echo -e "${GREEN}邮箱: $global_email${NC}"
else
echo -e "${YELLOW}邮箱: 未设置${NC}"
fi
echo "----------------------------------------"
read -rp "按回车键继续..."
;;
2)
# 查看所有仓库的用户配置
echo -e "\n${BLUE}所有仓库的用户配置:${NC}"
for dir in "${ALL_DIRS[@]}"; do
echo -e "\n${BLUE}正在检查: $dir${NC}"
echo "----------------------------------------"
if [ ! -d "$dir" ]; then
echo -e "${RED}❌ 错误: 目录不存在${NC}"
continue
fi

cd "$dir" || { echo -e "${RED}❌ 错误: 无法进入目录${NC}"; continue; }

# 检查是否是Git仓库
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
echo -e "${RED}❌ 错误: 不是一个Git仓库${NC}"
continue
fi

repo_name=$(git config --local user.name 2>/dev/null)
repo_email=$(git config --local user.email 2>/dev/null)

if [ -n "$repo_name" ]; then
echo -e "${GREEN}本地用户名: $repo_name${NC}"
else
echo -e "${YELLOW}本地用户名: 未设置 (使用全局配置)${NC}"
fi

if [ -n "$repo_email" ]; then
echo -e "${GREEN}本地邮箱: $repo_email${NC}"
else
echo -e "${YELLOW}本地邮箱: 未设置 (使用全局配置)${NC}"
fi

# 显示实际生效的配置
effective_name=$(git config user.name 2>/dev/null)
effective_email=$(git config user.email 2>/dev/null)
echo -e "${BLUE}实际生效用户名: $effective_name${NC}"
echo -e "${BLUE}实际生效邮箱: $effective_email${NC}"
echo "----------------------------------------"
done
read -rp "按回车键继续..."
;;
3)
# 修改全局用户配置
echo -e "\n${BLUE}修改全局用户配置:${NC}"
echo "----------------------------------------"

# 显示当前全局配置
current_global_name=$(git config --global user.name 2>/dev/null)
current_global_email=$(git config --global user.email 2>/dev/null)
echo -e "${YELLOW}当前全局用户名: $current_global_name${NC}"
echo -e "${YELLOW}当前全局邮箱: $current_global_email${NC}"

echo ""
read -rp "请输入新的全局用户名 (直接回车保持不变): " new_global_name
read -rp "请输入新的全局邮箱 (直接回车保持不变): " new_global_email

changed=false

if [ -n "$new_global_name" ]; then
if git config --global user.name "$new_global_name"; then
echo -e "${GREEN}✅ 全局用户名已更新为: $new_global_name${NC}"
changed=true
else
echo -e "${RED}❌ 更新全局用户名失败${NC}"
fi
fi

if [ -n "$new_global_email" ]; then
if git config --global user.email "$new_global_email"; then
echo -e "${GREEN}✅ 全局邮箱已更新为: $new_global_email${NC}"
changed=true
else
echo -e "${RED}❌ 更新全局邮箱失败${NC}"
fi
fi

if [ "$changed" = false ]; then
echo -e "${YELLOW}⚠️ 没有进行任何更改${NC}"
fi

echo "----------------------------------------"
read -rp "按回车键继续..."
;;
4)
# 修改指定仓库的用户配置
echo -e "\n${BLUE}选择要修改配置的仓库:${NC}"
echo "----------------------------------------"

# 显示所有可用的仓库
for i in "${!ALL_DIRS[@]}"; do
echo "$((i + 1))) ${ALL_DIRS[i]}"
done
echo "0) 返回上级菜单"

read -rp "请选择仓库编号: " repo_choice

if [ "$repo_choice" = "0" ]; then
continue
fi

# 验证输入并获取选择的仓库
if [[ "$repo_choice" =~ ^[0-9]+$ ]] && [ "$repo_choice" -ge 1 ] && [ "$repo_choice" -le "${#ALL_DIRS[@]}" ]; then
repo_index=$((repo_choice - 1))
selected_repo="${ALL_DIRS[repo_index]}"

if [ ! -d "$selected_repo" ]; then
echo -e "${RED}❌ 错误: 目录不存在${NC}"
read -rp "按回车键继续..."
continue
fi

cd "$selected_repo" || { echo -e "${RED}❌ 错误: 无法进入目录${NC}"; read -rp "按回车键继续..."; continue; }

# 检查是否是Git仓库
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
echo -e "${RED}❌ 错误: 不是一个Git仓库${NC}"
read -rp "按回车键继续..."
continue
fi

echo -e "\n${BLUE}修改仓库配置: $selected_repo${NC}"
echo "----------------------------------------"

# 显示当前仓库配置
current_repo_name=$(git config --local user.name 2>/dev/null)
current_repo_email=$(git config --local user.email 2>/dev/null)
effective_name=$(git config user.name 2>/dev/null)
effective_email=$(git config user.email 2>/dev/null)

echo -e "${YELLOW}当前本地用户名: $current_repo_name${NC}"
echo -e "${YELLOW}当前本地邮箱: $current_repo_email${NC}"
echo -e "${BLUE}实际生效用户名: $effective_name${NC}"
echo -e "${BLUE}实际生效邮箱: $effective_email${NC}"

echo ""
read -rp "请输入新的仓库用户名 (直接回车保持不变): " new_repo_name
read -rp "请输入新的仓库邮箱 (直接回车保持不变): " new_repo_email

changed=false

if [ -n "$new_repo_name" ]; then
if git config --local user.name "$new_repo_name"; then
echo -e "${GREEN}✅ 仓库用户名已更新为: $new_repo_name${NC}"
changed=true
else
echo -e "${RED}❌ 更新仓库用户名失败${NC}"
fi
fi

if [ -n "$new_repo_email" ]; then
if git config --local user.email "$new_repo_email"; then
echo -e "${GREEN}✅ 仓库邮箱已更新为: $new_repo_email${NC}"
changed=true
else
echo -e "${RED}❌ 更新仓库邮箱失败${NC}"
fi
fi

if [ "$changed" = false ]; then
echo -e "${YELLOW}⚠️ 没有进行任何更改${NC}"
fi

else
echo -e "${RED}❌ 无效的仓库编号${NC}"
fi

echo "----------------------------------------"
read -rp "按回车键继续..."
;;
5)
# 返回主菜单
return
;;
*)
echo -e "${RED}⚠️ 无效选项,请重新输入${NC}"
sleep 1
;;
esac
done
}

# 主程序
while true; do
show_menu
read -rp "请输入选项 (1-10): " choice

case $choice in
1|2|3)
process_all_repos "$choice"
read -rp "操作完成,按回车返回主菜单..."
;;
4)
echo -e "${GREEN}👋 再见!${NC}"
exit 0
;;
5)
add_temp_dir
read -rp "按回车返回主菜单..."
;;
6)
amend_last_commit_msg
read -rp "操作完成,按回车返回主菜单..."
;;
7)
reset_to_remote
read -rp "操作完成,按回车返回主菜单..."
;;
8)
revert_last_commit
read -rp "操作完成,按回车返回主菜单..."
;;
9)
show_branches_and_commits
read -rp "操作完成,按回车返回主菜单..."
;;
10)
manage_git_user_config
;;
*)
echo -e "${RED}⚠️ 无效选项,请重新输入${NC}"
sleep 1
;;
esac
done

win下的git脚本:git.bat和git.ps1

  • 说明:初始使用的均是单独的git.bat脚本,后来惨遭windows更新后背刺,导致无法正常运行,改用git.ps1脚本,应为个人习惯使用git.bat作为git.ps1的启动脚本,git.bat脚本内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@echo off
chcp 65001 >nul
title Git仓库同步工具(Windows版)
color 0B

:: 设置窗口大小
mode con cols=100 lines=30

echo 正在启动Git仓库同步工具...
echo.

:: 检查PowerShell是否存在
where powershell >nul 2>nul
if %errorlevel% neq 0 (
echo 错误: 找不到PowerShell,请确保已安装PowerShell。
goto :error
)

:: 使用PowerShell生成临时脚本文件,确保UTF-8编码
echo 正在准备脚本文件...
PowerShell -NoProfile -ExecutionPolicy Bypass -Command "$PSDefaultParameterValues['Out-File:Encoding'] = 'utf8'; Get-Content -Path '%~dp0git.ps1' -Encoding UTF8 -Raw | Out-File -FilePath '%TEMP%\git_temp.ps1' -Force"

:: 执行临时脚本文件
echo 正在加载PowerShell脚本...
echo.
PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& {try { & '%TEMP%\git_temp.ps1' } catch { Write-Host '错误: ' $_.Exception.Message -ForegroundColor Red; Read-Host '按回车键退出' }}"

:: 清理临时文件
del /q "%TEMP%\git_temp.ps1" >nul 2>nul

:: 如果直接双击运行,脚本结束后保持窗口开启
if %errorlevel% neq 0 goto :error
goto :end

:error
echo.
echo 运行过程中发生错误,请检查以上信息。
echo 如需帮助,请确保:
echo 1. PowerShell已正确安装
echo 2. git.ps1文件与git.bat在同一目录
echo 3. 您有足够的权限运行PowerShell脚本
echo 4. 文件编码为UTF-8格式
echo.
pause

:end
if not defined PROMPT pause

git.ps1脚本内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
# Git 仓库同步工具 (Windows 版)

# 配置需要同步的仓库目录(修改这里)
$REPO_DIRS = @(
"C:\Users\user\OneDrive\Code-win"
"C:\Users\user\Desktop\edit"
"C:\Users\user\Desktop\study"
# 添加更多仓库路径
)

# 临时目录数组
$TEMP_DIRS = @()

# 颜色定义
$RED = "Red"
$GREEN = "Green"
$YELLOW = "Yellow"
$BLUE = "Cyan"

# 主菜单
function Show-Menu {
Clear-Host
Write-Host "Git 仓库同步工具 (Windows 版)" -ForegroundColor $BLUE
Write-Host "========================================"
Write-Host "1) 自动模式 (拉取 + 自动提交推送)"
Write-Host "2) 仅拉取更新"
Write-Host "3) 仅推送更改"
Write-Host "4) 退出"
Write-Host "5) 添加临时目录"
Write-Host "6) 修改上次提交信息(AI 生成)"
Write-Host "7) 重置到远程分支"
Write-Host "8) 回退上一个提交"
Write-Host "9) 查看分支和提交信息"
Write-Host "10) 管理Git用户配置"
Write-Host "========================================"
}

# 修改上次提交信息(AI 生成)
function Amend-LastCommitMsg {
$ALL_DIRS = $REPO_DIRS + $TEMP_DIRS
foreach ($dir in $ALL_DIRS) {
Write-Host "`n正在处理: $dir" -ForegroundColor $BLUE
Write-Host "----------------------------------------"
if (-not (Test-Path $dir)) {
Write-Host "❌ 错误: 目录不存在" -ForegroundColor $RED
continue
}
Set-Location $dir
# 检查是否有提交
try {
$null = git rev-parse HEAD 2>$null
# 获取原始提交信息
$original_msg = git log -1 --pretty=%B
Write-Host "当前提交信息: $original_msg" -ForegroundColor $YELLOW

# 获取新的提交信息(基于上次提交内容)
$commit_content = git show HEAD
$msg = $commit_content | ~/.conda/envs/zhouzhou/python.exe "C:\Users\user\commit.py"
if ([string]::IsNullOrWhiteSpace($msg)) {
$msg = "AI修正提交信息 [$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')]"
}
Write-Host "新的提交信息: $msg" -ForegroundColor $GREEN

# 询问是否要修改提交信息
$confirm = Read-Host "是否修改并推送此仓库的提交信息?(y/n)"
if ($confirm -eq "y" -or $confirm -eq "Y") {
git commit --amend -m $msg
git push --force-with-lease
Write-Host "✅ 已修改并推送提交信息" -ForegroundColor $GREEN
} else {
Write-Host "⚠️ 已跳过此仓库" -ForegroundColor $YELLOW
}
} catch {
Write-Host "⚠️ 当前仓库没有提交,跳过" -ForegroundColor $YELLOW
}
Write-Host "----------------------------------------"
Start-Sleep -Seconds 1
}
}

# 重置到远程分支函数
function Reset-ToRemote {
$ALL_DIRS = $REPO_DIRS + $TEMP_DIRS
foreach ($dir in $ALL_DIRS) {
Write-Host "`n正在处理: $dir" -ForegroundColor $BLUE
Write-Host "----------------------------------------"
if (-not (Test-Path $dir)) {
Write-Host "❌ 错误: 目录不存在" -ForegroundColor $RED
continue
}
Set-Location $dir

# 获取当前分支名
$current_branch = git rev-parse --abbrev-ref HEAD
Write-Host "当前分支: $current_branch" -ForegroundColor $YELLOW

$confirm = Read-Host "是否重置本地分支到远程 origin/$current_branch?这将丢弃所有本地更改!(y/n)"
if ($confirm -eq "y" -or $confirm -eq "Y") {
Write-Host "正在重置到 origin/$current_branch..." -ForegroundColor $YELLOW
git fetch
git reset --hard origin/$current_branch
Write-Host "✅ 重置成功" -ForegroundColor $GREEN
} else {
Write-Host "⚠️ 已取消重置操作" -ForegroundColor $YELLOW
}
Write-Host "----------------------------------------"
Start-Sleep -Seconds 1
}
}

# 回退上一个提交函数
function Revert-LastCommit {
$ALL_DIRS = $REPO_DIRS + $TEMP_DIRS
foreach ($dir in $ALL_DIRS) {
Write-Host "`n正在处理: $dir" -ForegroundColor $BLUE
Write-Host "----------------------------------------"
if (-not (Test-Path $dir)) {
Write-Host "❌ 错误: 目录不存在" -ForegroundColor $RED
continue
}
Set-Location $dir

# 检查是否有提交
try {
$null = git rev-parse HEAD 2>$null
# 获取当前提交信息
$current_commit = git rev-parse --short HEAD
$previous_commit = git rev-parse --short HEAD~1
$current_msg = git log -1 --pretty=%B
Write-Host "当前提交: $current_commit - $current_msg" -ForegroundColor $YELLOW
Write-Host "上一提交: $previous_commit" -ForegroundColor $YELLOW

$confirm = Read-Host "是否回退到上一个提交? 这将删除最近的提交! (y/n)"
if ($confirm -eq "y" -or $confirm -eq "Y") {
Write-Host "正在回退到上一个提交..." -ForegroundColor $YELLOW
git reset --hard HEAD~1
git push --force-with-lease
Write-Host "✅ 已成功回退并推送" -ForegroundColor $GREEN
} else {
Write-Host "⚠️ 已取消回退操作" -ForegroundColor $YELLOW
}
} catch {
Write-Host "⚠️ 当前仓库没有提交,跳过" -ForegroundColor $YELLOW
}
Write-Host "----------------------------------------"
Start-Sleep -Seconds 1
}
}

# 处理单个仓库
function Process-Repo {
param (
[string]$dir,
[int]$mode
)

Write-Host "`n正在处理: $dir" -ForegroundColor $BLUE
Write-Host "----------------------------------------"

# 检查目录是否存在
if (-not (Test-Path $dir)) {
Write-Host "❌ 错误: 目录不存在" -ForegroundColor $RED
return
}

Set-Location $dir

switch ($mode) {
1 { # 自动模式
Write-Host "🔄 正在同步仓库..." -ForegroundColor $YELLOW
git pull
$status = git status --porcelain
if ($status) {
Write-Host "⚡ 检测到未提交修改" -ForegroundColor $YELLOW
git add .
# AI生成commit message
$msg = ~/.conda/envs/zhouzhou/python.exe "C:\Users\user\commit.py"
if ([string]::IsNullOrWhiteSpace($msg)) {
$msg = "自动提交 [$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')]"
}
git commit -m "$msg"
git push
} elseif (git cherry -v) {
Write-Host "⬆️ 推送未同步提交" -ForegroundColor $YELLOW
git push
} else {
Write-Host "✅ 仓库已同步" -ForegroundColor $GREEN
}
}
2 { # 仅拉取
Write-Host "⬇️ 正在拉取更新..." -ForegroundColor $YELLOW
git pull
}
3 { # 仅推送
$status = git status --porcelain
if ($status) {
Write-Host "⚡ 发现未提交修改" -ForegroundColor $YELLOW

# 先添加所有更改
git add .

# 使用循环允许重新生成提交信息
while ($true) {
# 使用AI生成提交信息
Write-Host "正在生成提交信息..." -ForegroundColor $YELLOW
$msg = ~/.conda/envs/zhouzhou/python.exe "C:\Users\user\commit.py"
# 这里是特定的python脚本路径,和commit.py的脚本路径
# 检查生成是否成功
if ([string]::IsNullOrWhiteSpace($msg)) {
Write-Host "⚠️ 生成提交信息失败" -ForegroundColor $RED
$msg = "手动提交 [$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')]"
}

# 显示生成的信息
Write-Host "AI生成的提交信息: $msg" -ForegroundColor $GREEN

# 询问用户操作
Write-Host "请选择操作:"
Write-Host "1) 使用此提交信息"
Write-Host "2) 重新生成提交信息"
Write-Host "3) 手动输入提交信息"
Write-Host "4) 取消提交"
$commit_choice = Read-Host "请选择 (1-4) [默认:1]"

# 设置默认值为1
if ([string]::IsNullOrWhiteSpace($commit_choice)) {
$commit_choice = "1"
}

switch ($commit_choice) {
"1" {
# 使用生成的信息
break
}
"2" {
# 重新生成信息
continue
}
"3" {
# 手动输入信息
$msg = Read-Host "请输入提交信息"
break
}
"4" {
# 取消提交
Write-Host "⚠️ 已取消提交" -ForegroundColor $YELLOW
git reset
return
}
default {
Write-Host "⚠️ 无效选择,请重新输入" -ForegroundColor $RED
}
}

# 如果跳出循环,结束while
if ($commit_choice -eq "1" -or $commit_choice -eq "3") {
break
}
}

# 提交更改
Write-Host "正在提交..." -ForegroundColor $YELLOW
# 将提交信息写入临时文件
$tempFile = [System.IO.Path]::GetTempFileName()
$msg | Out-File -Encoding utf8 $tempFile
# 使用临时文件进行提交
git commit -F $tempFile
# 删除临时文件
Remove-Item $tempFile

# 询问是否推送
$push_confirm = Read-Host "是否将更改推送到远程?(y/n) [默认:y]"

# 设置默认值为y
if ([string]::IsNullOrWhiteSpace($push_confirm)) {
$push_confirm = "y"
}

if ($push_confirm -eq "y" -or $push_confirm -eq "Y") {
Write-Host "正在推送..." -ForegroundColor $YELLOW
git push
Write-Host "✅ 已推送更改" -ForegroundColor $GREEN
} else {
Write-Host "⚠️ 仅提交本地,未推送" -ForegroundColor $YELLOW
}
} else {
# 检查是否有未推送的提交
if (git cherry -v) {
Write-Host "⬆️ 发现未推送的提交" -ForegroundColor $YELLOW
$push_confirm = Read-Host "是否推送?(y/n)"
if ($push_confirm -eq "y" -or $push_confirm -eq "Y") {
Write-Host "正在推送..." -ForegroundColor $YELLOW
git push
Write-Host "✅ 已推送更改" -ForegroundColor $GREEN
} else {
Write-Host "⚠️ 已取消推送" -ForegroundColor $YELLOW
}
} else {
Write-Host "✅ 仓库已是最新状态,无需推送" -ForegroundColor $GREEN
}
}
}
}

Write-Host "----------------------------------------"
Start-Sleep -Seconds 1
}

# 添加临时目录函数
function Add-TempDir {
$new_dir = Read-Host "请输入要添加的临时目录 (直接回车返回主菜单)"

# 检查是否为空输入,直接返回
if ([string]::IsNullOrWhiteSpace($new_dir)) {
Write-Host "返回主菜单..." -ForegroundColor $YELLOW
return
}

# 检查目录是否存在
if (-not (Test-Path $new_dir)) {
Write-Host "❌ 错误: 目录不存在" -ForegroundColor $RED
Write-Host "按回车键返回主菜单..." -ForegroundColor $YELLOW
Read-Host
return
}

# 检查是否重复添加
foreach ($dir in $REPO_DIRS) {
if ($dir -eq $new_dir) {
Write-Host "❌ 错误: 目录已存在于永久目录中" -ForegroundColor $RED
Write-Host "按回车键返回主菜单..." -ForegroundColor $YELLOW
Read-Host
return
}
}

foreach ($dir in $TEMP_DIRS) {
if ($dir -eq $new_dir) {
Write-Host "❌ 错误: 目录已存在于临时目录中" -ForegroundColor $RED
Write-Host "按回车键返回主菜单..." -ForegroundColor $YELLOW
Read-Host
return
}
}

# 添加到临时目录数组
$TEMP_DIRS += $new_dir
Write-Host "✅ 已添加临时目录: $new_dir" -ForegroundColor $GREEN
}


# 处理所有仓库
function Process-AllRepos {
param (
[int]$mode
)
$ALL_DIRS = $REPO_DIRS + $TEMP_DIRS # 合并永久和临时目录

foreach ($repo in $ALL_DIRS) {
Process-Repo $repo $mode
}
}

# 查看分支和提交信息函数
function Show-BranchesAndCommits {
$ALL_DIRS = $REPO_DIRS + $TEMP_DIRS
foreach ($dir in $ALL_DIRS) {
Write-Host "`n正在处理: $dir" -ForegroundColor $BLUE
Write-Host "----------------------------------------"
if (-not (Test-Path $dir)) {
Write-Host "❌ 错误: 目录不存在" -ForegroundColor $RED
continue
}
Set-Location $dir

# 检查是否是Git仓库
try {
$null = git rev-parse --is-inside-work-tree 2>$null
} catch {
Write-Host "❌ 错误: 不是一个Git仓库" -ForegroundColor $RED
continue
}

# 获取当前分支
$current_branch = git rev-parse --abbrev-ref HEAD
Write-Host "当前分支: $current_branch" -ForegroundColor $GREEN

# 更新远程分支信息
Write-Host "更新远程信息..." -ForegroundColor $YELLOW
git fetch --quiet

# 显示本地分支
Write-Host "`n本地分支:" -ForegroundColor $BLUE
git branch | ForEach-Object { " $_" }

# 显示远程分支
Write-Host "`n远程分支:" -ForegroundColor $BLUE
git branch -r | Where-Object { $_ -notmatch "HEAD" } | ForEach-Object { " $_" }

# 本地与远程的差异
try {
$upstream = git rev-parse --abbrev-ref --symbolic-full-name "@{u}" 2>$null
if ($upstream) {
Write-Host "`n本地与远程差异:" -ForegroundColor $BLUE
$ahead = git rev-list "@{u}..HEAD" --count
$behind = git rev-list "HEAD..@{u}" --count

if ($ahead -gt 0 -and $behind -gt 0) {
Write-Host " 本地领先 $ahead 个提交,落后 $behind 个提交" -ForegroundColor $YELLOW
} elseif ($ahead -gt 0) {
Write-Host " 本地领先 $ahead 个提交" -ForegroundColor $GREEN
} elseif ($behind -gt 0) {
Write-Host " 本地落后 $behind 个提交" -ForegroundColor $RED
} else {
Write-Host " 本地与远程同步" -ForegroundColor $GREEN
}
} else {
Write-Host "`n⚠️ 当前分支没有对应的远程分支" -ForegroundColor $YELLOW
}
} catch {
Write-Host "`n⚠️ 无法获取分支跟踪信息" -ForegroundColor $YELLOW
}

# 显示最近的提交
Write-Host "`n最近提交:" -ForegroundColor $BLUE
git log -5 --pretty=format:" %h %s [%an, %ar]" --abbrev-commit

Write-Host "`n==========================================" -ForegroundColor $YELLOW

# 询问是否查看详细信息
$show_detail = Read-Host "查看详细的远程差异?(y/n)"
if ($show_detail -eq "y" -or $show_detail -eq "Y") {
try {
$upstream = git rev-parse --abbrev-ref --symbolic-full-name "@{u}" 2>$null
if ($upstream) {
# 显示具体的差异内容
Write-Host "`n与远程的具体差异:" -ForegroundColor $BLUE

# 如果本地领先,显示远程没有的提交
if ($ahead -gt 0) {
Write-Host "`n本地有而远程没有的提交:" -ForegroundColor $YELLOW
git log "@{u}..HEAD" --pretty=format:" %h %s [%an, %ar]" --abbrev-commit
}

# 如果本地落后,显示本地没有的提交
if ($behind -gt 0) {
Write-Host "`n远程有而本地没有的提交:" -ForegroundColor $YELLOW
git log "HEAD..@{u}" --pretty=format:" %h %s [%an, %ar]" --abbrev-commit
}
} else {
Write-Host "⚠️ 无法显示差异,当前分支没有关联的远程分支" -ForegroundColor $YELLOW
}
} catch {
Write-Host "⚠️ 无法获取远程差异信息" -ForegroundColor $YELLOW
}
}

Write-Host "----------------------------------------"
Start-Sleep -Seconds 1
}
}

# 管理Git用户配置函数
function Manage-GitUserConfig {
$ALL_DIRS = $REPO_DIRS + $TEMP_DIRS

while ($true) {
Clear-Host
Write-Host "Git 用户配置管理" -ForegroundColor $BLUE
Write-Host "========================================"
Write-Host "1) 查看全局用户配置"
Write-Host "2) 查看所有仓库的用户配置"
Write-Host "3) 修改全局用户配置"
Write-Host "4) 修改指定仓库的用户配置"
Write-Host "5) 返回主菜单"
Write-Host "========================================"

$config_choice = Read-Host "请选择操作 (1-5)"

switch ($config_choice) {
"1" {
# 查看全局用户配置
Write-Host "`n全局Git用户配置:" -ForegroundColor $BLUE
Write-Host "----------------------------------------"
try {
$global_name = git config --global user.name
$global_email = git config --global user.email

if ($global_name) {
Write-Host "用户名: $global_name" -ForegroundColor $GREEN
} else {
Write-Host "用户名: 未设置" -ForegroundColor $YELLOW
}

if ($global_email) {
Write-Host "邮箱: $global_email" -ForegroundColor $GREEN
} else {
Write-Host "邮箱: 未设置" -ForegroundColor $YELLOW
}
} catch {
Write-Host "❌ 获取全局配置失败" -ForegroundColor $RED
}
Write-Host "----------------------------------------"
Read-Host "按回车键继续..."
}
"2" {
# 查看所有仓库的用户配置
Write-Host "`n所有仓库的用户配置:" -ForegroundColor $BLUE
foreach ($dir in $ALL_DIRS) {
Write-Host "`n正在检查: $dir" -ForegroundColor $BLUE
Write-Host "----------------------------------------"
if (-not (Test-Path $dir)) {
Write-Host "❌ 错误: 目录不存在" -ForegroundColor $RED
continue
}

Set-Location $dir

# 检查是否是Git仓库
try {
$null = git rev-parse --is-inside-work-tree 2>$null
} catch {
Write-Host "❌ 错误: 不是一个Git仓库" -ForegroundColor $RED
continue
}

try {
$repo_name = git config --local user.name
$repo_email = git config --local user.email

if ($repo_name) {
Write-Host "本地用户名: $repo_name" -ForegroundColor $GREEN
} else {
Write-Host "本地用户名: 未设置 (使用全局配置)" -ForegroundColor $YELLOW
}

if ($repo_email) {
Write-Host "本地邮箱: $repo_email" -ForegroundColor $GREEN
} else {
Write-Host "本地邮箱: 未设置 (使用全局配置)" -ForegroundColor $YELLOW
}

# 显示实际生效的配置
$effective_name = git config user.name
$effective_email = git config user.email
Write-Host "实际生效用户名: $effective_name" -ForegroundColor $BLUE
Write-Host "实际生效邮箱: $effective_email" -ForegroundColor $BLUE

} catch {
Write-Host "❌ 获取仓库配置失败" -ForegroundColor $RED
}
Write-Host "----------------------------------------"
}
Read-Host "按回车键继续..."
}
"3" {
# 修改全局用户配置
Write-Host "`n修改全局用户配置:" -ForegroundColor $BLUE
Write-Host "----------------------------------------"

# 显示当前全局配置
try {
$current_global_name = git config --global user.name
$current_global_email = git config --global user.email
Write-Host "当前全局用户名: $current_global_name" -ForegroundColor $YELLOW
Write-Host "当前全局邮箱: $current_global_email" -ForegroundColor $YELLOW
} catch {
Write-Host "⚠️ 无法获取当前全局配置" -ForegroundColor $YELLOW
}

Write-Host ""
$new_global_name = Read-Host "请输入新的全局用户名 (直接回车保持不变)"
$new_global_email = Read-Host "请输入新的全局邮箱 (直接回车保持不变)"

$changed = $false

if (-not [string]::IsNullOrWhiteSpace($new_global_name)) {
try {
git config --global user.name "$new_global_name"
Write-Host "✅ 全局用户名已更新为: $new_global_name" -ForegroundColor $GREEN
$changed = $true
} catch {
Write-Host "❌ 更新全局用户名失败" -ForegroundColor $RED
}
}

if (-not [string]::IsNullOrWhiteSpace($new_global_email)) {
try {
git config --global user.email "$new_global_email"
Write-Host "✅ 全局邮箱已更新为: $new_global_email" -ForegroundColor $GREEN
$changed = $true
} catch {
Write-Host "❌ 更新全局邮箱失败" -ForegroundColor $RED
}
}

if (-not $changed) {
Write-Host "⚠️ 没有进行任何更改" -ForegroundColor $YELLOW
}

Write-Host "----------------------------------------"
Read-Host "按回车键继续..."
}
"4" {
# 修改指定仓库的用户配置
Write-Host "`n选择要修改配置的仓库:" -ForegroundColor $BLUE
Write-Host "----------------------------------------"

# 显示所有可用的仓库
for ($i = 0; $i -lt $ALL_DIRS.Count; $i++) {
Write-Host "$($i + 1)) $($ALL_DIRS[$i])" -ForegroundColor $YELLOW
}
Write-Host "0) 返回上级菜单"

$repo_choice = Read-Host "请选择仓库编号"

if ($repo_choice -eq "0") {
continue
}

try {
$repo_index = [int]$repo_choice - 1
if ($repo_index -lt 0 -or $repo_index -ge $ALL_DIRS.Count) {
Write-Host "❌ 无效的仓库编号" -ForegroundColor $RED
Read-Host "按回车键继续..."
continue
}

$selected_repo = $ALL_DIRS[$repo_index]

if (-not (Test-Path $selected_repo)) {
Write-Host "❌ 错误: 目录不存在" -ForegroundColor $RED
Read-Host "按回车键继续..."
continue
}

Set-Location $selected_repo

# 检查是否是Git仓库
try {
$null = git rev-parse --is-inside-work-tree 2>$null
} catch {
Write-Host "❌ 错误: 不是一个Git仓库" -ForegroundColor $RED
Read-Host "按回车键继续..."
continue
}

Write-Host "`n修改仓库配置: $selected_repo" -ForegroundColor $BLUE
Write-Host "----------------------------------------"

# 显示当前仓库配置
try {
$current_repo_name = git config --local user.name
$current_repo_email = git config --local user.email
$effective_name = git config user.name
$effective_email = git config user.email

Write-Host "当前本地用户名: $current_repo_name" -ForegroundColor $YELLOW
Write-Host "当前本地邮箱: $current_repo_email" -ForegroundColor $YELLOW
Write-Host "实际生效用户名: $effective_name" -ForegroundColor $BLUE
Write-Host "实际生效邮箱: $effective_email" -ForegroundColor $BLUE
} catch {
Write-Host "⚠️ 无法获取当前仓库配置" -ForegroundColor $YELLOW
}

Write-Host ""
$new_repo_name = Read-Host "请输入新的仓库用户名 (直接回车保持不变)"
$new_repo_email = Read-Host "请输入新的仓库邮箱 (直接回车保持不变)"

$changed = $false

if (-not [string]::IsNullOrWhiteSpace($new_repo_name)) {
try {
git config --local user.name "$new_repo_name"
Write-Host "✅ 仓库用户名已更新为: $new_repo_name" -ForegroundColor $GREEN
$changed = $true
} catch {
Write-Host "❌ 更新仓库用户名失败" -ForegroundColor $RED
}
}

if (-not [string]::IsNullOrWhiteSpace($new_repo_email)) {
try {
git config --local user.email "$new_repo_email"
Write-Host "✅ 仓库邮箱已更新为: $new_repo_email" -ForegroundColor $GREEN
$changed = $true
} catch {
Write-Host "❌ 更新仓库邮箱失败" -ForegroundColor $RED
}
}

if (-not $changed) {
Write-Host "⚠️ 没有进行任何更改" -ForegroundColor $YELLOW
}

} catch {
Write-Host "❌ 无效的输入" -ForegroundColor $RED
}

Write-Host "----------------------------------------"
Read-Host "按回车键继续..."
}
"5" {
# 返回主菜单
return
}
default {
Write-Host "⚠️ 无效选项,请重新输入" -ForegroundColor $RED
Start-Sleep -Seconds 1
}
}
}
}

# 主程序
while ($true) {
Show-Menu
$choice = Read-Host "请输入选项 (1-10)"

switch ($choice) {
"1" { Process-AllRepos 1; Read-Host "操作完成,按回车返回主菜单..." }
"2" { Process-AllRepos 2; Read-Host "操作完成,按回车返回主菜单..." }
"3" { Process-AllRepos 3; Read-Host "操作完成,按回车返回主菜单..." }
"4" {
Write-Host "👋 再见!" -ForegroundColor $GREEN
exit
}
"5" {
Add-TempDir
Read-Host "按回车返回主菜单..."
}
"6" { Amend-LastCommitMsg; Read-Host "操作完成,按回车返回主菜单..." }
"7" { Reset-ToRemote; Read-Host "操作完成,按回车返回主菜单..." }
"8" { Revert-LastCommit; Read-Host "操作完成,按回车返回主菜单..." }
"9" { Show-BranchesAndCommits; Read-Host "操作完成,按回车返回主菜单..." }
"10" { Manage-GitUserConfig }
default {
Write-Host "⚠️ 无效选项,请重新输入" -ForegroundColor $RED
Start-Sleep -Seconds 1
}
}
}

 评论
评论插件加载失败
正在加载评论插件
由 Hexo 驱动 & 主题 Keep
访客数 访问量