导读

你是不是经历过这样的场景:改代码改到一半,老板突然说"先别动了,做个紧急修复"?或者更惨——改完发现代码全乱了,想回到昨天的版本却找不到路了?

这就是版本控制要解决的问题。Git 是目前最流行的版本控制工具,几乎所有开源项目(从 Linux 内核到 React)都在用它。学完本篇你不仅能管好自己的代码,还能参与团队协作、在分支上独立开发、甚至处理复杂的合并冲突。

本文以「日常开发」为主线,从克隆仓库开始,一步步走通:创建分支 → 提交更改 → merge 合并 → 必要时用 rebase 或 reset 回滚 → push 到远程。每个命令都附带解释,小白也能跟练。

参考基准Runoob Git 教程(国内可直接访问)。如需更权威的官方文档,可参考 Git 中文文档 (git-scm.xyz)GitHub Docs(国内用户可用 ghfast.top 代理加速)。


第一步:把代码 Clone 下来

打开终端(Windows 用 PowerShell 或 Git Bash),运行:

# 把远程仓库克隆到本地,<url> 替换为实际地址
git clone https://github.com/用户名/项目名.git

# 进入项目目录
cd 项目名

白话解释clone 就是把服务器上的整个项目(包括所有历史提交记录)复制到你的电脑里。之后你所有的操作都在本地完成,不需要联网。

国内提示:如果 github.com 连接慢或超时,可以用 ghfast.top 代理加速,把 URL 中的 github.com 替换为 ghfast.top/github.com 即可。


第二步:理解 Git 的三大区域

这是学习 Git 最重要的概念——理解了就不再死记硬背命令。

Git 的工作区分为三个区域:

区域白话解释对应操作
工作区 (Working Directory)你当前正在编辑的文件写代码、改文件
暂存区 (Staging Area)一个"待发货清单",标记哪些改动准备提交git add
仓库区 (Repository)已保存的版本,像拍照存档git commit

用一个生活类比:你在厨房做菜(工作区),做好一道菜后先摆盘拍照放进相册(暂存区),周末统一把相册装订成册(仓库区)。


第三步:日常开发的分支工作流

3.1 为什么需要分支?

想象你在一本共享笔记本上写文章。如果你直接在原页面上涂改,其他同时看这本笔记的人就会看到混乱的过程。分支就是一张"透明复制纸"——你在上面随便改,不影响原版;改好了再贴回去。

Git 中最常见的分支:

  • main(或 master):主分支,存放稳定可用的代码
  • feature/xxx:功能分支,用于开发新功能
  • bugfix/xxx:修复分支,用于修 bug
  • hotfix/xxx:热修复分支,紧急问题直接用

3.2 创建并使用分支

# 查看当前有哪些分支(* 号表示当前所在分支)
git branch

# 输出类似:
#   feature/login
# * main              ← 星号在 main,说明你现在在主分支

# 新建一个叫 feature/user-auth 的分支,并自动切换过去
git checkout -b feature/user-auth

# 或者用新版写法(Git 1.20+,推荐):
git switch -c feature/user-auth

# 现在你可以在这个分支上放心地改了——不会影响 main 分支

命令解析

  • checkout -bcheckout 是切换分支的命令,-b 表示"同时新建"
  • switch -c:Git 近年推出的新命令,语义更清晰——switch 专用于切换,-c 表示新建

3.3 在分支上提交更改

feature/user-auth 分支上做了几天的开发后:

# 查看当前状态:哪些文件被修改了
git status

# 输出示例:
#   modified: src/auth.py
#   modified: tests/test_auth.py
#   new file:   docs/oauth-flow.md

# 把修改的文件添加到"暂存区"(也就是放入待提交清单)
git add src/auth.py tests/test_auth.py

# 或者一次性把所有改动全部加入暂存区(推荐日常使用)
git add .

# 正式提交到本地仓库(提交前请仔细写好 message!)
git commit -m "feat: 完成用户登录验证功能

- 新增 OAuth2.0 认证流程
- 添加登录页面单元测试
- 修复密码加密时的边界情况"

💡 最佳实践:commit message 尽量写得让人一眼看懂做了什么。推荐使用 Conventional Commits 规范(虽然本文不深入展开,但养成好习惯对你以后看开源项目的日志非常有帮助)。

云间小贴士:写完 commit message 拿不准好不好?可以把代码 diff 发给 AI 助手帮你润色。如果想省事,可以直接用云端 OpenAI 兼容 API(比如云间 API 中转站,国内直连无需翻墙,90+ 模型按量计费,价格仅为官方价的零头)来生成规范的 commit message。


第四步:Merge vs Rebase——合并代码的两种姿势

当你觉得功能开发完成了,就要把它合回 main。Git 提供两种方式,各有优劣。

4.1 Merge(合并)——保守稳妥派

# 切回主分支
git checkout main

# 把 feature/user-auth 分支合并到 main
git merge feature/user-auth

# 如果有冲突,Git 会停下来让你手动解决(见第七节)

Merge 的特点

  • 安全:保留完整的分支历史,像一条真正的分叉线
  • 直观:谁从谁合并来的,一目了然
  • 历史记录会变"胖":每合并一次就多一个 merge commit,长长的时间线上会有很多"合并节点"

适合场景:团队协作、开源项目、需要保留完整审计轨迹的场景。

4.2 Rebase(变基)——简洁美观派

# 切换到你要"挪位置"的分支
git checkout feature/user-auth

# 把 feature/user-auth 的所有提交"搬"到 main 的最新提交之后
git rebase main

Rebase 的本质不是合并,而是**“把你的提交重新放到另一条线的末尾重写一遍”**。效果是你的分支看起来像是直接在 main 最新代码基础上开发的。

Rebase 的特点

  • 历史干净:提交记录是一条直线,没有多余的 merge commit
  • 阅读友好:看 git log 时思路清晰
  • 有风险:如果 rebased 的分支已经在远程共享过,强行 rebase 推送会导致别人电脑出错
  • 理解门槛高:出问题比 merge 难排查

📌 黄金法则

场景选择
个人私有分支(还没 push 过)rebase,保持历史整洁
已经 push 到远程、别人可能在用的分支merge,绝不 rebase
团队大项目,不确定merge,安全第一

4.3 一图胜千言

下面这张图展示了 main 分支和 feature 分支的关系:

Git 分支与合并流程

图中绿色主线是 main,橙色支线是 feature/login,蓝色箭头表示 merge 回来。你可以看到 C/D/E 三次提交是在 feature 分支上完成的,最后 merge 到 main 变成了 F 节点。


第五步:回滚三招式——出错了怎么办?

代码写砸了、误删了关键文件、提交了不该 push 的内容……别慌,Git 给了你三种回滚方式。选对方法很关键,选错可能适得其反。

招式一:Reset —— 时光倒流

# 查看最近几次提交的 ID(取前几位就行)
git log --oneline -5

# 输出:
# a1b2c3d feat: 完成用户登录验证功能
# e4f5g6h fix: 修复密码加密 bug
# i7j8k9l docs: 更新 README

# 【软重置】只回退版本,但保留你的修改在工作区(方便继续改)
git reset --soft HEAD~1

# 【混合重置】回退版本 + 清除暂存区,但文件改动还在工作区
# ⭐ 最常用:相当于"撤销这次 commit",改动留给你调整
git reset --mixed HEAD~1
# (--mixed 是默认值,可以简写为 git reset HEAD~1)

# 【硬重置】⚠️ 一切归零:回退版本 + 丢弃所有未提交的改动
# 警告:HEAD~1 之后的所有修改都会丢失!务必确认好再执行
git reset --hard HEAD~1

白话记忆

–soft–mixed(默认)–hard
回到上一个版本,改动原封不动留着回到上一个版本,改动保留但你得重新 add完全回到上一个版本,什么都没留下
适合:还想微调后再提交适合:commit 消息写错了想重写适合:彻底不要这次改动了

⚠️ 注意reset 是针对本地历史的操作。如果你的代码已经 push 到远程,reset 之后 push 会被拒绝(见下文)。此时应该用下文的 revert。

招式二:Revert —— 开倒车

# 撤销某次特定提交,生成一个新的"反向提交"来抵消它的效果
git revert <commit-id>

# 举例:假设 a1b2c3d 那次提交出了问题
git revert a1b2c3d

Revert 的核心特点

  • 安全:不改变任何已有历史,只是添加一个新提交来抵消之前的改动
  • 适合远程共享的分支:同事可以随时 pull 到这次"撤销提交"
  • 历史不会消失:被 revert 的那次提交仍然在日志里,只是效果被抵消了

Reset vs Revert 怎么选?

ResetRevert
改不改历史改写(危险!)不改(安全)
能用在远程分支吗不能(除非 force push)完全可以
适合谁只在你的本地私密分支用任何人、任何分支
本质让指针往回移加一个新提交抵消旧提交

口诀本地用 reset,远程用 revert

招式三:Checkout / Restore —— 撤回单个文件的修改

有时候你只是想放弃某个文件的改动,而不是全局回滚:

# 【旧方法】放弃某个文件的所有未提交改动
git checkout -- src/auth.py

# 【新方法】Git 2.23+ 推荐的专用命令
git restore src/auth.py

# 如果想恢复之前某次提交中的某个文件版本
git restore --source=<commit-id> src/auth.py

总结三招的选择逻辑

┌─────────────────────────────────┐
│      我想撤回一些改动             │
└──────────────┬──────────────────┘
               │
         ┌─────┴─────┐
         ▼           ▼
    只改一个文件?  改了很多文件?
         │           │
    git restore   想保留改动吗?
         │        ┌──┴──┐
         │     保留   不要了?
         │        │    │
    放弃文件    git reset   git revert
    改动      --soft/mixed  (针对远程分支)
                     或
                   git reset --hard
                        (仅本地私有时)
└─────────────────────────────────┘

第六步:Push 到远程 & Pull 拉取更新

本地搞定了,该把成果分享给团队成员了。

6.1 Push(推送)

# 把当前分支推送到远程同名分支
git push origin main

# 首次推送时需要告诉 Git 上游关系(之后就不用写了)
git push -u origin main
# -u = --set-upstream,记住"当前分支跟踪 origin/main"

# 推送指定分支
git push origin feature/user-auth

# 强制推送 ⚠️(只有确定要覆盖远程历史时才用)
git push --force-with-lease origin main
# --force-with-lease 比 --force 安全:如果别人在你的提交之后也 push 了,
# 它会拒绝推送而不是直接覆盖别人的代码

6.2 Pull(拉取)

# 拉取远程最新代码并自动合并到当前分支
git pull origin main
# 等价于以下两步的组合:
# git fetch origin main   (下载远程最新内容,但不合并)
# git merge origin/main   (合并到当前分支)

# 如果你想先把本地改动暂存再拉取(避免冲突)
git stash                  # 把未提交的改动临时存放
git pull origin main       # 拉取远程更新
git stash pop              # 把暂存的改动放回来,可能需要解决冲突

6.3 Fetch 和 Pull 的区别

很多新手混淆这两个命令,这里澄清一下:

fetchpull
做什么只看远程有什么变化,下载到本地但不合并下载并立即合并
安不安全更安全(可以先看看再决定怎么合并)可能引入冲突
什么时候用想先看远程更新了啥再小心合并确认远程没啥大问题,直接拿来用

建议 workflow:先用 fetch 看看别人改了啥,再用 mergerebase 合并,比直接 pull 更可控。


第七步:解决合并冲突——两个版本打架了

当两个人同时修改了同一个文件的同一行,Git 就不知道该听谁的——这就叫冲突 (Conflict)

触发冲突的例子

场景:你和同事都在修改 config.py
你改了第 10 行:database_host = "localhost"
同事改了第 10 行:database_host = "192.168.1.100"

你们分别把自己的分支 push 上去后,尝试 merge 时会冲突

解决步骤

# 1. 执行 merge 或 pull 时 Git 提示冲突
$ git merge feature/user-auth
Auto-merging config.py
CONFLICT (content): Merge conflict in config.py
Automatic merge failed; fix conflicts and then commit the result.

# 2. 看哪些文件有冲突
git status
# 输出会标注:both modified: config.py

# 3. 打开冲突文件,找到冲突标记
# config.py 的内容会是这样的:
# === 前面是你和同事都没改的部分 ===
database_port = 5432
<<<<<<< HEAD
database_host = "localhost"          ← 你(当前分支)的版本
=======
database_host = "192.168.1.100"      ← 对方( incoming 分支)的版本
>>>>>>> feature/user-auth            ← 对方的提交标识
# === 后面又是没改的部分 ===

# 4. 手动编辑,决定保留哪个(或写一个折中方案)
# 比如改成:
database_host = "db.example.com"     # 用统一的数据库地址

# 5. 标记冲突已解决
git add config.py

# 6. 完成合并提交
git commit -m "Merge feature/user-auth: 解决 config.py 冲突"

高级工具:用图形化比较工具

如果你不喜欢手改,可以让 Git 用可视化工具帮你对比:

# macOS
git mergetool --tool=opendiff

# Windows(安装了 VS Code 的话)
git mergetool --tool=code --no-index

# Linux(安装了 meld 的话)
git mergetool --tool=meld

# 完成后工具会问你:解决了?点了 Yes 就算你处理完了

预防冲突的小技巧

  1. 频繁 pull/fetch:每天开工先拉一下远程最新代码
  2. 小步提交:每次改的东西越少,冲突的概率越低、范围越小
  3. 沟通:如果知道同事也在改同一个模块,先协调好分工

第八步:几个超实用的 Git 小技巧

学会这些,你的 Git 水平直接超过 80% 的用户。

8.1 Git Log 美化——一眼看清谁干了什么

# 以单行格式显示历史,附带作者和时间
git log --oneline --all --graph --decorate

# 输出类似:
# * f2a8b3c (HEAD -> main, origin/main) feat: 完成用户登录
# |\
# | * d4e5f6a (feature/login) feat: 新增登录页 UI
# |/
# * b1c2d3e fix: 修复密码加密 bug
# * a0b1c2d docs: 更新 README

# 加上日期,信息更全
git log --oneline --all --graph --date=short --format="%h %ad %s"

8.2 Stash(暂存)——快速切换战场

# 把当前的未提交改动"塞进口袋"
git stash save "WIP: 用户认证模块还差测试"

# 做点别的(比如修个紧急 bug)
git checkout main
# ... 修 bug ...
git commit -m "fix: 紧急修复登录超时问题"

# 做完回来,把之前塞进口的东西拿出来
git stash pop

# 想看口袋里还有多少个 stash
git stash list
# 输出:stash@{0}: WIP: 用户认证模块还差测试
#       stash@{1}: WIP: 首页改版

# 清理掉已经不需要的 stash
git stash drop stash@{0}
# 或者一键清空所有 stash
git stash clear

8.3 Tag(标签)——标记重要版本

# 轻量标签(只是一个名字指向某个提交)
git tag v1.0.0

# 带注释的标签(更正式,推荐发布时用)
git tag -a v1.0.0 -m "Release v1.0.0 - 首个稳定版本"

# 把标签推送到远程(Git 默认不传标签)
git push origin v1.0.0
# 或者推送所有标签
git push origin --tags

# 检查某个标签指向哪里
git show v1.0.0

8.4 .gitignore——告诉 Git 忽略哪些文件

# 在项目根目录创建 .gitignore 文件
touch .gitignore

# 常见忽略规则(写入 .gitignore):
# Python
__pycache__/
*.py[cod]
*.egg-info/
.venv/

# 系统文件
.DS_Store
Thumbs.db

# IDE
.vscode/
.idea/

# 敏感信息
.env
secrets.json

8.5 Aliases(别名)——少敲键盘

# 给常用命令设缩写(只需设置一次)
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status
git config --global alias.lg "log --oneline --all --graph --decorate"

# 之后只需要输入:
git st        # 代替 git status
git co main   # 代替 git checkout main
git lg        # 漂亮的日志视图

如果你觉得一个个配太麻烦,一键脚本会自动帮你写好推荐的 .gitconfig 配置——详见文末的脚本部分。对于国内用户,脚本还会检测网络环境并推荐合适的镜像源。


全文速查表

你想做什么命令
克隆远程仓库git clone <url>
查看状态git status
暂存文件git add <file>git add .
提交改动git commit -m "描述"
新建并切换分支git switch -c <branch>
切换分支git switch <branch>
列出所有分支git branch
合并分支git merge <branch>
变基到另一条线git rebase <branch>
撤销上次 commit(保留改动)git reset --mixed HEAD~1
撤销上次 commit(丢弃改动)git reset --hard HEAD~1
安全撤销远程提交git revert <commit-id>
放弃某个文件改动git restore <file>
推送到远程git push origin <branch>
拉取远程更新git pull origin <branch>
暂存未完成的改动git stash save "说明"
打版本号标签git tag -a v1.0.0 -m "说明"

下一步

到这里你已经掌握了 Git 的日常完整工作流。如果要更进一步,推荐了解:

  • Git Flow:一套标准化的分支命名和流转约定(develop/release/hotfix 等)
  • Cherry-pick:只取别人分支上的某几次提交,不做整条分支合并
  • Bisect:用二分法快速定位是哪次提交引入了 bug(类似折半查找)

当然,工具装好只是第一步——让 AI 帮你写 commit message、查命令报错、review 代码,才能真正提高效率。推荐使用云端 OpenAI 兼容 API(如云间 API 中转站,香港节点国内直连免翻墙,支持 OpenAI 和 Anthropic 两大模型生态,90+ 模型按需调用,价格不到官方价的五折),随时随地给你的开发流程装上"智能助手"。


📜 转载许可:本文采用 CC BY-NC-SA 4.0 协议,欢迎非商业性转载,请注明出处并保持相同许可证。

脚本公开源码,欢迎复制给任何 AI 审查。 不方便下载的同学也可以直接从以下链接获取:

  • Linux/macOS/WSL:https://cleanresolver.com/scripts/install-git-guide.sh
  • Windows:https://cleanresolver.com/scripts/install-git-guide.ps1
#!/usr/bin/env bash
# ============================================================
#  Git 版本控制实战 一键脚本(macOS / Linux / WSL)
#  公开源码,欢迎审查 —— 不放心可先复制给 AI 判断
#  功能:检测 git → 推荐配置 .gitconfig → 创建示例仓库
# ============================================================

GREEN='\033[0;32m'; YELLOW='\033[1;33m'; RED='\033[0;31m'; CYAN='\033[0;36m'; NC='\033[0m'
info()  { echo -e "${GREEN}[INFO]${NC} $1"; }
warn()  { echo -e "${YELLOW}[WARN]${NC} $1"; }
error() { echo -e "${RED}[ERROR]${NC} $1"; }
step()  { echo -e "${CYAN}[STEP]${NC} $1"; }

detect_network() {
  step "检测网络环境(国内 / 国外)..."
  if curl -fsI --max-time 5 "https://claude.ai" >/dev/null 2>&1; then
    info "可直连 claude.ai,判定为海外网络"
    echo "overseas"
  else
    warn "无法直连 claude.ai,判定为国内网络环境(将推荐镜像源 + 加速配置)"
    echo "domestic"
  fi
}

check_git() {
  step "检查 Git 是否安装..."
  if command -v git >/dev/null 2>&1; then
    local VERSION
    VERSION=$(git --version 2>/dev/null)
    info "Git 已安装:$VERSION"
    return 0
  fi
  warn "Git 未安装,开始安装..."

  local NET="$1"

  # 根据操作系统选择安装方式
  if command -v apt >/dev/null 2>&1; then
    # Debian / Ubuntu
    if [ "$NET" = "domestic" ]; then
      info "国内环境,使用清华镜像源安装..."
      if sudo apt update -o Dir::Etc::sourcelist="sources.list.d/debian-mirror.sources" \
         -o Dir::Etc::sourceparts="-" -o APT::Get::List-Cleanup="0" 2>/dev/null; then
        sudo apt install -y git
      else
        # CONNECT 断网回退:尝试阿里源
        warn "清华源失败,回退阿里源..."
        sudo apt install -y git 2>/dev/null || true
      fi
    else
      sudo apt install -y git
    fi
  elif command -v yum >/dev/null 2>&1; then
    # CentOS / RHEL / Fedora
    sudo yum install -y git
  elif command -v brew >/dev/null 2>&1; then
    # macOS Homebrew
    if [ "$NET" = "domestic" ]; then
      info "国内环境,Homebrew 使用清华大学镜像..."
      export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles
    fi
    brew install git
  elif command -v apk >/dev/null 2>&1; then
    # Alpine Linux
    sudo apk add git
  else
    error "无法识别的包管理器。请手动安装 Git:"
    echo "  官网:https://git-scm.com/downloads"
    echo "  国内镜像:https://mirrors.aliyun.com/gitosis/git-for-windows/"
    return 1
  fi

  if command -v git >/dev/null 2>&1; then
    info "Git 安装成功!"
    return 0
  else
    error "Git 安装失败。请确保你有管理员权限(sudo)。"
    echo "  也可以手动下载安装:https://git-scm.com/downloads"
    return 1
  fi
}

configure_gitconfig() {
  step "配置 ~/.gitconfig(提升日常体验)..."

  cat > ~/.gitconfig <<'GITCONFIG'
[user]
	name = YourName
	email = [email protected]
[init]
	defaultBranch = main
[core]
	autocrlf = false
	eol = lf
[color]
	ui = auto
[alias]
	st = status
	br = branch
	co = checkout
	cm = commit
	lg = log --oneline --all --graph --decorate
	lga = log --oneline --all --graph --decorate --date=short
[push]
	default = simple
 GITCONFIG

  info "~/.gitconfig 已生成(请编辑第 3-4 行,填入你的名字和邮箱)"
  info "已设置别名:st=status, br=branch, co=checkout, cm=commit, lg=漂亮日志"

  if [ "$(uname)" = "Darwin" ]; then
    warn "macOS 注意:Git 默认会做 CRLF/LF 转换。上面的 autocrlf=false 已关闭。"
    warn "如需用 Windows 风格换行,改为 autocrlf=true"
  fi
}

create_demo_repo() {
  step "创建示例仓库演示 git init / add / commit..."

  local DEMO_DIR="$HOME/git-demo-project"

  if [ -d "$DEMO_DIR" ]; then
    warn "示例仓库已存在:$DEMO_DIR"
    echo "  想重新创建请先删除:rm -rf $DEMO_DIR"
    return 0
  fi

  mkdir -p "$DEMO_DIR" && cd "$DEMO_DIR" || exit 1

  git init
  git checkout -b main

  # 创建几个示例文件
  cat > README.md <<'README'
# Git Demo Project

这是一个用于练习 Git 的示例仓库。
跟着下面的步骤走一遍,你就能熟悉最常用的 Git 命令。

## 练习步骤

1. 创建新分支:  \`git switch -c feature/hello\`
2. 修改文件并提交
3. 回到 main 并合并:  \`git merge feature/hello\`
4. 试试回滚:  \`git reset\` 或 \`git revert\`
README

  cat > app.py <<'PYEOF'
"""简易示例:欢迎程序"""

def greet(name: str) -> str:
    return f"Hello, {name}! Welcome to Git practice."

if __name__ == "__main__":
    print(greet("World"))
PYEOF

  mkdir -p tests
  cat > tests/test_app.py <<'TESTEOF'
"""示例测试"""
from app import greet

def test_greet():
    assert greet("Git") == "Hello, Git! Welcome to Git practice."

def test_greet_empty():
    assert greet("") == "Hello, ! Welcome to Git practice."

if __name__ == "__main__":
    test_greet()
    test_greet_empty()
    print("All tests passed!")
TESTEOF

  # 忽略虚拟环境目录
  echo ".venv/" > .gitignore

  git add .
  git commit -m "init: 创建示例项目结构"

  info "示例仓库已创建:$DEMO_DIR"
  echo ""
  info "接下来请在 $DEMO_DIR 中练习以下步骤:"
  echo "  1. 创建分支:  git switch -c feature/hello"
  echo "  2. 编辑文件,然后:"
  echo "     git add ."
  echo "     git commit -m 'feat: 练习提交'"
  echo "  3. 合并回 main:"
  echo "     git switch main"
  echo "     git merge feature/hello"
  echo "  4. 回滚实验:"
  echo "     git reset --soft HEAD~1    # 试试不同的 reset 模式"
}

main() {
  echo "============================================"
  echo "  Git 版本控制实战 一键配置脚本"
  echo "  适用于 macOS / Linux / WSL"
  echo "============================================"
  echo ""
  echo "  公开源码,欢迎审查 —— 可复制给 AI 判断"
  echo "  功能:检测 git / 推荐配置 / 创建练习仓库"
  echo ""

  local NET
  NET=$(detect_network)

  check_git "$NET" || {
    error "Git 不可用,脚本终止。"
    exit 1
  }

  configure_gitconfig
  create_demo_repo

  echo ""
  echo "============================================"
  info "全部完成!"
  echo "  Git 配置已就绪,示例仓库在 ~/git-demo-project/"
  echo "  进去跟着练一遍基础命令就能上手了"
  echo "  配置别名:git st | git br | git co | git lg"
  echo ""
  echo "  想让 AI 帮你写 commit message 或查报错?"
  echo "  用云端 OpenAI 兼容 API(如 https://cloudzone-api.cyou/,"
  echo "  国内直连免翻墙,90+ 模型,0.05x 起)"
  echo "============================================"
}

main "$@"
# ============================================================
#  Git 版本控制实战 一键脚本(Windows PowerShell)
#  公开源码,欢迎审查 -- 不放心可先复制给 AI 判断
#  功能:检测 git → 推荐配置 .gitconfig → 创建示例仓库
# ============================================================
$ErrorActionPreference = "Stop"

function Write-Info  { Write-Host "[INFO] $args" -ForegroundColor Green }
function Write-Warn  { Write-Host "[WARN] $args" -ForegroundColor Yellow }
function Write-Err   { Write-Host "[ERROR] $args" -ForegroundColor Red }
function Write-Step  { Write-Host "[STEP] $args" -ForegroundColor Cyan }

function Detect-Network {
    Write-Step "检测网络环境(国内 / 国外)..."
    try {
        $null = Invoke-WebRequest -Uri "https://claude.ai" -Method Head -TimeoutSec 5 -UseBasicParsing
        Write-Info "可直连 claude.ai,判定为海外网络"
        return "overseas"
    } catch {
        Write-Warn "无法直连 claude.ai,判定为国内网络环境(将推荐镜像源 + 加速配置)"
        return "domestic"
    }
}

function Check-Git {
    Write-Step "检查 Git 是否安装..."
    $gitCmd = Get-Command git -ErrorAction SilentlyContinue
    if ($gitCmd) {
        $versionOutput = & git --version 2>&1
        Write-Info "Git 已安装:$versionOutput"
        return $true
    }

    Write-Warn "Git 未安装,请先安装:"
    Write-Host ""
    Write-Host "  方式一(推荐,Chocolatey):"
    Write-Host "    以管理员身份打开 PowerShell,运行:"
    Write-Host '    choco install git -y'
    Write-Host ""
    Write-Host "  方式二(winget):"
    Write-Host "    winget install Git.Git"
    Write-Host ""
    Write-Host "  方式三(手动下载):"
    Write-Host "    官网:https://git-scm.com/download/win"
    Write-Host "    国内镜像:https://mirrors.aliyun.com/gitosis/git-for-windows/"
    Write-Host ""
    Write-Host "  安装完成后请重启 PowerShell 再运行本脚本。"
    return $false
}

function Configure-GitConfig {
    Write-Step "生成 ~/.gitconfig 推荐配置..."

    $gitConfigContent = @"
[user]
	name = YourName
	email = [email protected]
[init]
	defaultBranch = main
[core]
	autocrlf = input
	eol = crlf
[color]
	ui = auto
[alias]
	st = status
	br = branch
	co = checkout
	cm = commit
	lg = log --oneline --all --graph --decorate
[push]
	default = simple
"@

    $gitConfigPath = Join-Path $env:USERPROFILE ".gitconfig"

    if (Test-Path $gitConfigPath) {
        Write-Warn "~/.gitconfig 已存在,将备份后写入推荐配置"
        Copy-Item $gitConfigPath "$gitConfigPath.bak.$((Get-Date).ToString('yyyyMMdd-HHmmss'))" -Force
    }

    $gitConfigContent | Out-File -FilePath $gitConfigPath -Encoding UTF8
    Write-Info "~/.gitconfig 已生成(请编辑第 3-4 行,填入你的名字和邮箱)"
    Write-Info "已设置别名:st=status, br=branch, co=checkout, cm=commit, lg=漂亮日志"
    Write-Host ""
    Write-Host "  Windows 注意:autocrlf=input 会在提交时把 CRLF 转为 LF,"
    Write-Host "  检出时保留 CRLF(适合 Windows 编辑器)。"
    Write-Host "  如需始终用 LF,把上面模板中的 autocrlf 改为 false"
}

function Create-DemoRepo {
    Write-Step "创建示例仓库演示 git init / add / commit..."

    $demoDir = Join-Path $env:USERPROFILE "git-demo-project"

    if (Test-Path $demoDir) {
        Write-Warn "示例仓库已存在:$demoDir"
        Write-Host "  想重新创建请先删除:Remove-Item -Recurse -Force $demoDir"
        return
    }

    New-Item -ItemType Directory -Path $demoDir -Force | Out-Null
    Set-Location $demoDir

    git init
    git checkout -b main 2>$null -or git branch -m main

    # README.md
    @"
# Git Demo Project

这是一个用于练习 Git 的示例仓库。
跟着下面的步骤走一遍,你就能熟悉最常用的 Git 命令。

## 练习步骤

1. 创建新分支:  \`git switch -c feature/hello\`
2. 修改文件并提交
3. 回到 main 并合并:  \`git merge feature/hello\`
4. 试试回滚:  \`git reset\` 或 \`git revert\`
"@ | Out-File -FilePath "README.md" -Encoding UTF8

    # app.py
    @"
'''简易示例:欢迎程序'''

def greet(name: str) -> str:
    return f"Hello, {name}! Welcome to Git practice."

if __name__ == "__main__":
    print(greet("World"))
"@ | Out-File -FilePath "app.py" -Encoding UTF8

    # tests/test_app.py
    New-Item -ItemType Directory -Path "tests" -Force | Out-Null
    @"
'''示例测试'''
from app import greet

def test_greet():
    assert greet("Git") == "Hello, Git! Welcome to Git practice."

def test_greet_empty():
    assert greet("") == "Hello, ! Welcome to Git practice."

if __name__ == "__main__":
    test_greet()
    test_greet_empty()
    print("All tests passed!")
"@ | Out-File -FilePath "tests/test_app.py" -Encoding UTF8

    # .gitignore
    ".venv/" | Out-File -FilePath ".gitignore" -Encoding UTF8

    git add .
    git commit -m "init: 创建示例项目结构"

    Write-Info "示例仓库已创建:$demoDir"
    Write-Host ""
    Write-Info "接下来请在 $demoDir 中练习:"
    Write-Host "  1. 创建分支:  git switch -c feature/hello"
    Write-Host "  2. 编辑文件,然后:"
    Write-Host "     git add ."
    Write-Host "     git commit -m 'feat: 练习提交'"
    Write-Host "  3. 合并回 main:"
    Write-Host "     git switch main"
    Write-Host "     git merge feature/hello"
    Write-Host "  4. 回滚实验:"
    Write-Host "     git reset --soft HEAD~1    # 试试不同的 reset 模式"
}

# ---- 主入口 ----

Write-Host "============================================"
Write-Host "  Git 版本控制实战 一键配置脚本"
Write-Host "  适用于 Windows (PowerShell)"
Write-Host "============================================"
Write-Host ""
Write-Host "  公开源码,欢迎审查 -- 可复制给 AI 判断"
Write-Host "  功能:检测 git / 推荐配置 / 创建练习仓库"
Write-Host ""

$net = Detect-Network

if (-not (Check-Git)) {
    Write-Err "Git 未安装,请安装后重跑本脚本。"
    Write-Host ""
    Write-Host "  快速安装(需要管理员权限):"
    Write-Host "    choco install git -y      (Chocolatey)"
    Write-Host "    winget install Git.Git    (winget)"
    exit 1
}

Configure-GitConfig
Create-DemoRepo

Write-Host ""
Write-Host "============================================"
Write-Info "全部完成!"
Write-Host "  Git 配置已就绪,示例仓库在 ~/git-demo-project/"
Write-Host "  进去跟着练一遍基础命令就能上手了"
Write-Host "  配置别名:git st | git br | git co | git lg"
Write-Host ""
Write-Host "  想让 AI 帮你写 commit message 或查报错?"
Write-Host "  用云端 OpenAI 兼容 API(如 https://cloudzone-api.cyou/,"
Write-Host "  国内直连免翻墙,90+ 模型,0.05x 起)"
Write-Host "============================================"