Git相关

15 minute read

Git 版本控制 完整系统学习大纲

本文面向测试工程师,按”概念 → 单人使用 → 分支 → 远程协作 → 撤销回退 → 测试实战”的顺序组织,由浅入深。 Git 不只是开发的工具:测试脚本要版本管理、Bug 要定位是哪次提交引入的、回归范围要靠 git diff 确定、CI 流水线靠 Git 事件触发。掌握 Git,是测试工程化的第一步。

一、Git 概述

1.1 什么是 Git?

  • 定义:Git 是一个分布式版本控制系统(DVCS),由 Linus Torvalds 于 2005 年为管理 Linux 内核开发而创建。
  • 作用:记录文件的每一次变更,支持多人协作、版本回退、分支并行开发。
  • 地位:当今事实标准,GitHub / GitLab / Gitee / Bitbucket 都是基于 Git 的平台。

1.2 为什么需要版本控制?

没有版本控制 有 Git
代码_v1.java、代码_v2_final.java、代码_最终版.java 完整历史,随时回退
改坏了没法恢复 一键回退到任意历史版本
两个人改同一个文件互相覆盖 分支并行,合并时冲突可解
不知道谁改的、为什么改 每次提交有作者、时间、说明
无法定位 Bug 是哪次改动引入 git bisect 二分定位

1.3 集中式 vs 分布式

维度 集中式(SVN) 分布式(Git)
版本库位置 只在中央服务器 每个开发者都有完整仓库
断网能否提交 ❌ 不能 ✅ 能(本地提交,联网再推)
速度 慢(每次都要联网) 快(大部分操作在本地)
分支 目录拷贝,重 指针,极轻量
单点故障 服务器挂了就全瘫 任一副本都能恢复

1.4 Git vs SVN 对比

对比项 Git SVN
架构 分布式 集中式
存储方式 快照(每次提交存完整快照) 增量(存差异)
版本号 40 位 SHA-1 哈希 递增数字(r1, r2…)
分支成本 极低(创建分支就是建指针) 高(复制目录)
学习曲线 较陡 平缓
现状 主流,新项目首选 老项目维护

1.5 三个区域与四种状态(核心概念,务必理解)

┌─────────────┐   git add    ┌─────────────┐  git commit  ┌─────────────┐
│  工作区      │ ──────────► │  暂存区      │ ──────────► │  版本库      │
│  Working    │ ◄────────── │  Staging /  │ ◄────────── │  Repository │
│  Directory  │   checkout  │  Index      │    reset    │   (.git)    │
└─────────────┘             └─────────────┘              └─────────────┘
     你编辑文件的地方          临时存放待提交内容            已提交的历史快照

文件的四种状态:

状态 含义 转换命令
Untracked 未跟踪 新文件,Git 还没管 git add → Staged
Modified 已修改 已跟踪文件被改动了 git add → Staged
Staged 已暂存 已加入暂存区,待提交 git commit → Committed
Committed 已提交 已安全存入版本库 —
git status          # 看文件处于什么状态(最常用命令,没有之一)
git status -s       # 简洁模式

状态简写含义:

??  未跟踪
A   新加入暂存区
M   已修改(第一列:暂存区中的改动;第二列:工作区的改动)
D   已删除
R   重命名

1.6 测试工程师为什么必须会 Git?

场景 用途
管理测试脚本 自动化用例、压测脚本纳入版本管理
定位 Bug 引入点 git bisect 二分查找哪次提交引入问题
确定回归范围 git diff 看改了哪些文件/模块
代码评审 参与 Code Review,提前发现缺陷
分支与环境对应 测试环境部署指定分支
CI/CD 提交触发流水线(配合 Jenkins)
缺陷复现 切到出问题的版本复现

二、安装与配置

2.1 安装

# Windows:下载 Git for Windows(自带 Git Bash)
# https://git-scm.com/download/win
# 或用包管理器(PowerShell 管理员权限)
winget install Git.Git

# macOS
brew install git

# Ubuntu/Debian
sudo apt install git

# 验证
git --version

💡 Windows 用户建议用 Git Bash 或 PowerShell(非 cmd),命令提示符习惯与 Linux 一致,教程里的命令可直接用。

2.2 首次配置(必做)

# 配置用户名和邮箱(提交时会记录,务必配置)
git config --global user.name "YourName"
git config --global user.email "your@email.com"

# 查看配置
git config --list
git config user.name

配置三个级别:

级别 参数 作用域 文件位置
系统 --system 所有用户 /etc/gitconfig
全局 --global 当前用户所有仓库 ~/.gitconfig
本地 --local(默认) 仅当前仓库 .git/config

优先级:local > global > system

2.3 常用配置项

# 默认编辑器(改 VSCode)
git config --global core.editor "code --wait"

# 换行符处理(跨平台协作必配!)
# Windows:
git config --global core.autocrlf true      # 检出转 CRLF,提交转 LF
# macOS / Linux:
git config --global core.autocrlf input     # 提交转 LF,检出不变

# 中文文件名不转义显示(默认会显示成 \344\275\240 这种八进制)
git config --global core.quotepath false

# 默认分支名
git config --global init.defaultBranch main

# 开启命令行着色
git config --global color.ui auto

# 记住密码(避免每次推送都输入)
git config --global credential.helper store     # 明文存储
git config --global credential.helper cache --timeout=3600   # 缓存 1 小时

配置别名(强烈建议,能省大量敲字):

git config --global alias.st status
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.cm "commit -m"
git config --global alias.lg "log --oneline --graph --all --decorate"

# 一个好用的日志别名(推荐直接配上)
git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"

# 之后就能用
git st
git lg

2.4 SSH 密钥配置(免密推送)

# 1. 生成密钥(ed25519 算法,比 RSA 更短更安全)
ssh-keygen -t ed25519 -C "your@email.com"
# 一路回车即可(也可设密码)

# 2. 查看公钥并复制
cat ~/.ssh/id_ed25519.pub
# Windows: type C:\Users\你的用户名\.ssh\id_ed25519.pub

# 3. 粘贴到 GitHub / GitLab
#    GitHub: Settings → SSH and GPG keys → New SSH key

# 4. 测试连通
ssh -T git@github.com
# 看到 "Hi xxx! You've successfully authenticated..." 即成功

⚠️ 配置 SSH 后,clone 要用 SSH 地址(git@github.com:user/repo.git)而不是 HTTPS 地址,否则仍会要求输密码。

已有仓库从 HTTPS 切到 SSH:

git remote set-url origin git@github.com:user/repo.git
git remote -v          # 确认

2.5 .gitignore(忽略不需要的文件)

# 常见忽略项
*.log                  # 日志
*.tmp
.DS_Store              # macOS
Thumbs.db              # Windows
node_modules/          # 依赖目录
target/                # Java 构建产物
dist/ build/           # 前端构建产物
__pycache__/           # Python 缓存
*.pyc
.env                   # 环境变量(含密码,绝不能提交!)
*.iml
.idea/ .vscode/        # IDE 配置(一般忽略)
语法规则:
  # 开头        注释
  /           目录
  *           通配多个字符
  ?           通配单个字符
  !           取反(不忽略)
  **          多级目录
  /xxx        只忽略根目录下的 xxx
# 已提交的文件想忽略,需要先从版本库移除
git rm --cached <file>          # 从暂存区删除,保留本地文件
git rm -r --cached .            # 全部移除(然后重新 add)

💡 各语言的 .gitignore 模板可以在 gitignore.io 直接生成。

三、基础操作(单人使用)

3.1 创建仓库

# 方式一:本地新建
mkdir my-project && cd my-project
git init                        # 生成 .git 目录
git init -b main                # 指定初始分支名

# 方式二:克隆远程仓库(更常见)
git clone https://github.com/user/repo.git
git clone https://github.com/user/repo.git mydir      # 指定目录名
git clone -b dev https://github.com/user/repo.git     # 克隆指定分支
git clone --depth 1 https://github.com/user/repo.git  # 浅克隆,只要最后一次提交(大仓库提速)

3.2 添加与提交

git add <file>              # 添加指定文件
git add .                   # 添加当前目录所有变更(最常用)
git add -A                  # 添加所有变更(含删除,等价于 git add --all)
git add -p                  # 交互式分块添加(一次提交拆成多个,很实用)

git commit -m "提交说明"     # 提交
git commit -am "说明"        # add + commit(仅对已跟踪文件有效,新文件不行)
git commit --amend          # 修改最后一次提交(见 6.4)
# 标准提交流程
git status          # 1. 看改了什么
git diff            # 2. 看具体改动内容
git add .           # 3. 加入暂存区
git commit -m "feat: 新增登录接口测试"   # 4. 提交

3.3 查看差异

git diff                    # 工作区 vs 暂存区(未 add 的改动)
git diff --staged           # 暂存区 vs 版本库(已 add 未 commit 的改动)
git diff HEAD               # 工作区 vs 最新版本库
git diff <commit1> <commit2>    # 两次提交之间
git diff main..dev          # 两个分支之间

git diff --stat             # 只看统计(改了几个文件、多少行)
git diff --name-only        # 只看文件名 ★ 确定回归范围常用
git diff <file>             # 只看某文件

💡 测试常用:git diff --name-only main...HEAD 列出本次改动涉及的文件,据此圈定回归测试范围。

3.4 查看历史

git log                     # 完整日志
git log --oneline           # 一行一条(最常用)
git log --oneline -5        # 最近 5 条
git log --graph             # 图形化显示分支
git log --all --decorate --oneline --graph    # 全部分支的图形化日志

git log --author="张三"      # 按作者过滤
git log --since="2026-10-01" --until="2026-10-31"   # 按时间
git log --grep="登录"        # 按提交信息关键字搜索
git log -S "keyword"        # 按代码内容变更搜索(找某行代码是什么时候加的)

git log -- <file>           # 某文件的变更历史
git log -p <file>           # 某文件的变更历史 + 具体 diff
git show <commit>           # 查看某次提交的详细信息
git show <commit>:<file>    # 查看某次提交中该文件的内容

3.5 撤销修改(分场景,务必看清)

场景 命令
撤销工作区未 add 的修改 git restore <file> 或 git checkout -- <file>
撤销已 add 但未 commit git restore --staged <file> 或 git reset HEAD <file>
撤销已 commit(未推送) git reset --soft HEAD~1(保留改动)
撤销已 commit(已推送) git revert <commit>(推荐,不改历史)
# 丢弃工作区某个文件的改动(危险,不可恢复)
git restore <file>

# 把文件从暂存区撤回来(保留工作区改动)
git restore --staged <file>

# 恢复到某个提交的某个文件
git restore --source=<commit> <file>

⚠️ git checkout -- <file> 会直接丢弃工作区改动,无法恢复。执行前先确认 git diff 看过内容。

3.6 删除与重命名

git rm <file>               # 删除文件(同时从版本库移除)
git rm --cached <file>      # 只从版本库移除,保留本地文件(配 .gitignore 用)
git mv old.txt new.txt      # 重命名

# 也可以直接删除文件后 git add -A,Git 会自动识别为删除

3.7 标签 Tag(版本发布用)

git tag                     # 查看所有标签
git tag v1.0.0              # 给当前提交打轻量标签
git tag -a v1.0.0 -m "发布1.0"  # 附注标签(带说明,推荐)
git tag -a v0.9.0 <commit>  # 给历史提交补打标签

git show v1.0.0             # 查看标签详情
git push origin v1.0.0      # 推送单个标签(标签默认不会随 push 推送!)
git push origin --tags      # 推送所有标签
git tag -d v1.0.0           # 删除本地标签
git push origin --delete v1.0.0   # 删除远程标签

💡 测试价值:用 Tag 标记每个提测版本(如 v2.3.0-test1),随时能切回该版本复现问题。

四、分支管理(核心)

4.1 分支的本质

Git 的分支只是一个指向某次提交的可变指针——极其轻量,创建分支瞬间完成。

main:    A ── B ── C
                    │
                    └── dev:  C ── D ── E

HEAD 指向当前所在分支
git branch              # 查看本地分支(* 标记当前分支)
git branch -a           # 查看全部分支(含远程)
git branch -v           # 查看分支最后一次提交

4.2 分支操作

# 创建
git branch dev                  # 创建分支(不切换)
git switch dev                  # 切换分支(Git 2.23+ 推荐)
git checkout dev                # 切换分支(老写法)
git switch -c dev               # 创建并切换 ★ 最常用
git checkout -b dev             # 同上(老写法)

# 从远程分支创建本地分支
git switch -c dev origin/dev
git checkout --track origin/dev

# 删除
git branch -d dev               # 删除已合并的分支(安全)
git branch -D dev               # 强制删除未合并分支

# 重命名
git branch -m old new

4.3 合并分支

# 合并 dev 到当前分支(先切到 main)
git switch main
git merge dev

两种合并方式:

类型 条件 结果
Fast-forward 快进合并 main 没有新提交,dev 只是领先 直接把 main 指针前移,不产生新提交
三方合并 两边都有新提交 产生一个新的”合并提交”
git merge --no-ff dev       # 强制生成合并提交(保留分支历史,推荐用于功能分支)
git merge --ff-only dev     # 只允许快进,否则失败

4.4 冲突解决(重点)

冲突何时发生:两个分支修改了同一个文件的同一部分,Git 无法自动合并。

冲突文件中的标记:

<<<<<<< HEAD
这是当前分支(main)的内容
=======
这是要合并进来的分支(dev)的内容
>>>>>>> dev

解决步骤:

# 1. 合并时提示冲突
git merge dev
# Auto-merging app.py
# CONFLICT (content): Merge conflict in app.py
# Automatic merge failed; fix conflicts and then commit the result.

# 2. 查看冲突文件
git status          # 显示 "both modified"

# 3. 手动编辑文件,删除 <<<<<<< ======= >>>>>>> 标记,
#    保留正确的内容(或合并两边的内容)

# 4. 标记为已解决
git add app.py

# 5. 完成合并提交
git commit          # 会自动带合并说明,直接保存即可

# 6. 放弃合并(搞不定了想重来)
git merge --abort

💡 建议:冲突涉及业务逻辑时,不要自己拍脑袋决定保留哪边,找相关开发一起确认。测试同学最容易在合并测试脚本分支时遇到冲突。

4.5 rebase 变基(merge 的替代方案)

# 把当前分支的提交"接"到目标分支的后面
git switch feature
git rebase main

# 等价于:把 feature 的改动重新在 main 最新提交上放一遍
# 结果:历史变成一条直线,没有合并提交

merge vs rebase:

维度 merge rebase
历史 保留分支结构,有合并提交 变直线,更清爽
提交历史 不改写 改写(产生新的 commit hash)
冲突处理 一次解决 可能需要多次(每个提交一次)
安全性 安全 ⚠️ 不要对已推送的公共分支 rebase
黄金法则:
  ✅ 对自己本地未推送的分支随便 rebase
  ❌ 绝不要对已推送到远程、别人也在用的分支 rebase(会打乱别人的历史)

4.6 cherry-pick(摘樱桃)

只把某一次提交应用到当前分支(不想合并整个分支时用)。

git switch main
git cherry-pick <commit-hash>          # 摘取某次提交
git cherry-pick <commit1> <commit2>    # 多个
git cherry-pick <commit1>^..<commit3>  # 连续区间
git cherry-pick --abort                # 取消

💡 测试场景:修复 Bug 的提交在 dev 分支上,需要单独拎到发布分支 → cherry-pick。

4.7 stash 暂存(临时切换分支用)

git stash                   # 把未提交的改动"藏起来",工作区变干净
git stash push -m "说明"     # 带说明
git stash -u                # 连同未跟踪文件一起暂存

git stash list              # 查看暂存列表
git stash pop               # 恢复最近一次并删除记录 ★
git stash apply             # 恢复但保留记录
git stash apply stash@{1}   # 恢复指定那次
git stash drop              # 删除最近一次暂存
git stash clear             # 清空所有

典型场景:正在改 A 功能,突然要切到 main 看个东西 → git stash → 切走 → 回来 git stash pop。

4.8 分支策略(团队协作规范)

策略 分支构成 适用
Git Flow main / develop / feature / release / hotfix 传统、版本节奏明确的项目
GitHub Flow main + 临时 feature 分支 持续交付,互联网团队常用
Trunk-Based 主干 + 短期分支 高频发布,需强 CI 支撑

Git Flow 典型分支:

main        生产环境代码,只能从 release/hotfix 合并
develop     开发主线
feature/*   功能分支,从 develop 切,完成后合回 develop
release/*   发布分支,从 develop 切,测试在此分支进行
hotfix/*    线上紧急修复,从 main 切,修完合回 main 和 develop

💡 测试要知道:测试通常在 release/* 分支上进行,测完合入 main 发布。所以提 Bug 时要说清楚是哪个分支,否则开发可能找错地方。

五、远程仓库协作

5.1 远程仓库管理

git remote -v               # 查看远程仓库
git remote add origin <url> # 添加远程仓库
git remote set-url origin <new-url>     # 修改地址
git remote remove origin    # 删除
git remote show origin      # 查看远程详情

5.2 拉取与推送

git clone <url>             # 首次:克隆

git fetch                   # 拉取远程更新到本地(**不自动合并**,安全)
git pull                    # = fetch + merge(自动合并)
git pull --rebase           # = fetch + rebase(推荐,历史更清爽)

git push origin main        # 推送到远程 main
git push -u origin main     # 首次推送并设置上游跟踪(-u 只需一次)
git push                    # 之后直接 git push

fetch vs pull(重要区别):

命令 行为 安全性
git fetch 只下载远程更新到本地的 origin/main,不改你的工作区 ✅ 安全,可先 diff 再决定
git pull 下载 + 立即合并到当前分支 ⚠️ 可能产生冲突或意外改动
# 推荐的安全流程
git fetch origin
git diff main origin/main       # 先看看别人改了什么
git merge origin/main           # 确认无误再合并

5.3 跟踪分支与 upstream

git branch -vv                          # 查看本地分支与远程的对应关系
git push -u origin feature              # 首次推送时建立跟踪
git branch --set-upstream-to=origin/main main    # 手动设置

5.4 多人协作标准流程

# 1. 开始工作前,先同步最新代码(重要!)
git switch main
git pull

# 2. 创建自己的分支
git switch -c feature/login

# 3. 开发/编写,阶段性提交
git add .
git commit -m "feat: 登录接口用例"

# 4. 推送到远程
git push -u origin feature/login

# 5. 提 Pull Request / Merge Request,等待评审

# 6. 评审通过后合并到 main,删除自己的分支
git switch main
git pull
git branch -d feature/login

5.5 Fork 工作流(开源项目)

1. 在 GitHub 上 Fork 别人的仓库到自己账号
2. clone 自己账号下的仓库
3. 添加上游仓库,便于同步
   git remote add upstream https://github.com/原作者/repo.git
4. 改动提交到自己仓库,推送
5. 在 GitHub 上发起 Pull Request 给原作者
6. 原作者合并

同步上游更新:
   git fetch upstream
   git merge upstream/main

5.6 PR / MR 评审要点(测试参与)

检查项 说明
改动范围 Files changed 是否超出本次需求范围
逻辑正确性 边界条件、异常处理
是否影响其他模块 公共方法、配置改动
是否有测试覆盖 新增功能是否带用例
命名与规范 变量命名、注释
安全问题 硬编码密码、SQL 拼接

六、撤销与回退(危险操作,重点)

6.1 三个命令的区别(易混淆)

命令 作用对象 是否改历史 适用
git reset 移动分支指针 ✅ 改写历史 本地未推送的提交
git revert 新建一个”反向提交” ❌ 不改历史 已推送的提交(推荐)
git restore / checkout 恢复文件内容 — 撤销文件改动

6.2 reset 三种模式

git reset --soft <commit>     # 只移动 HEAD,暂存区和工作区都不变
git reset --mixed <commit>    # 移动 HEAD + 重置暂存区(默认),工作区不变
git reset --hard <commit>     # 移动 HEAD + 重置暂存区 + 重置工作区 ⚠️
模式 HEAD 暂存区 工作区 后果
--soft 移动 不变 不变 改动还在暂存区,可重新提交
--mixed(默认) 移动 重置 不变 改动回到工作区,未 add
--hard 移动 重置 重置 ⚠️ 改动全部丢失
git reset --hard HEAD~1       # 回退到上一次提交,丢弃最后那次提交的所有改动 ⚠️
git reset --hard HEAD~3       # 回退 3 次提交
git reset --hard <commit>     # 回退到指定提交
git reset --hard origin/main  # 丢弃所有本地改动,与远程完全一致

⚠️ --hard 是 Git 里最危险的操作之一,工作区未提交的改动会永久丢失。执行前务必确认 git status 干净,或先 git stash。

6.3 revert(安全撤销,推荐)

git revert <commit>           # 创建一个新提交,内容与该提交相反
git revert HEAD               # 撤销最后一次提交
git revert --no-edit <commit> # 不打开编辑器,直接提交
git revert <c1>..<c2>         # 撤销一段提交
git revert -m 1 <merge-commit>    # 撤销合并提交(-m 1 表示保留主线)
原来:A ── B ── C ── D
revert C 后:A ── B ── C ── D ── C'(撤销C的改动)

历史保留完整,别人拉代码不会出问题 → 已推送的提交一律用 revert

6.4 修改提交

# 修改最后一次提交的说明
git commit --amend -m "新的提交说明"

# 补充文件到最后一次提交(不产生新提交)
git add forgotten.txt
git commit --amend --no-edit

# 修改作者
git commit --amend --author="Name <email>"

⚠️ --amend 会改写历史,只用于未推送的提交。已推送的提交 amend 后再 push 需要 --force,会影响他人。

6.5 reflog:找回”丢掉”的提交(救命命令)

Git 会记录 HEAD 的每一次移动,即使你 reset –hard 了,提交对象通常还在(直到 GC)。

git reflog                    # 查看 HEAD 的移动历史
git reflog --all              # 全部

# 找到误删的提交 hash 后,恢复
git reset --hard <找到的hash>
# 或
git cherry-pick <hash>
实操:
  1. git reflog  找到 "commit: xxx" 那行的 hash(如 a1b2c3d)
  2. git show a1b2c3d   确认是不是要找的内容
  3. git reset --hard a1b2c3d   恢复

💡 只要提交过(commit 过),基本都救得回来。真正救不回来的是从未 commit 的工作区改动。

6.6 安全准则

  1. 已推送的公共提交 → 用 revert,不用 reset
  2. 不确定时先 git stash 或 git branch backup 建个备份分支
  3. reset --hard 前必看 git status
  4. 强制推送(--force)前,先确认没人基于你的分支工作;团队建议用 --force-with-lease(更安全)
git push --force-with-lease   # 只在远程没有别人新提交时才强制推送(推荐)
git push --force              # 无条件强制推送 ⚠️

七、高级技巧

7.1 bisect:二分查找引入 Bug 的提交(测试利器)

# 场景:某功能现在是坏的,但之前是好的,想知道是哪次提交搞坏的

git bisect start
git bisect bad                  # 标记当前版本是坏的
git bisect good <已知正常的commit或tag>   # 标记一个已知正常的版本

# Git 会自动切换到中间的提交,你测试后标记:
git bisect bad      # 这个版本有问题
git bisect good     # 这个版本正常

# 反复几次后,Git 会告诉你第一个坏提交:
# xxxxxxx is the first bad commit

git bisect reset                # 结束,回到原分支

自动化 bisect(有测试脚本时更高效):

git bisect start HEAD v1.0.0
git bisect run python test_login.py     # 脚本退出码 0 = good,非 0 = bad
git bisect reset

7.2 blame:查看每行代码是谁改的

git blame <file>                # 每行显示作者、时间、提交
git blame -L 10,20 <file>       # 只看 10-20 行
git blame -w <file>             # 忽略空白改动
git blame -C <file>             # 追踪代码移动

7.3 其他实用命令

# 查看某行代码的变更历史
git log -L 10,20:<file>

# 清理未跟踪文件(小心)
git clean -n                    # 先预览会删什么
git clean -fd                   # 删除未跟踪的文件和目录
git clean -fdx                  # 连 .gitignore 里的也删(谨慎)

# 压缩历史(交互式 rebase)
git rebase -i HEAD~3            # 合并/修改最近 3 次提交
# 编辑界面里:
#   pick   保留
#   squash 合并到上一个
#   reword 修改提交信息
#   drop   删除该提交

# 查看某个提交的改动文件
git show --stat <commit>
git show --name-only <commit>

# 归档导出(打包某版本的代码)
git archive --format=zip --output=v1.0.zip v1.0.0

7.4 子模块与工作树(了解)

# submodule:仓库里引用另一个仓库
git submodule add <url> path/
git submodule update --init --recursive

# worktree:同一个仓库同时检出多个分支到不同目录(避免来回切换)
git worktree add ../hotfix-dir hotfix
git worktree list
git worktree remove ../hotfix-dir
# Git LFS:管理大文件(如测试用的大视频、模型文件)
git lfs install
git lfs track "*.mp4"
git add .gitattributes

八、团队规范

8.1 Commit Message 规范(Conventional Commits)

格式:<type>(<scope>): <subject>

type 取值:
  feat     新功能
  fix      修复 bug
  docs     文档
  style    格式(不影响代码逻辑)
  refactor 重构
  perf     性能优化
  test     增加/修改测试 ★
  chore    构建/工具/依赖
  ci       CI 配置

示例:
  feat(login): 新增短信验证码登录
  fix(order): 修复金额为 0 时下单失败
  test(order): 补充订单取消的边界用例
写好提交信息的原则:
  ✅ 一行说清"做了什么"(50 字内)
  ✅ 正文说明"为什么做"(不是怎么做)
  ✅ 关联需求/Bug 单号:fix(order): 修复重复下单 #BUG-1234
  ❌ 避免:"修改"、"更新"、"fix"、"aaa" 这种无信息量的说明

8.2 Git Hooks(自动化钩子)

.git/hooks/ 目录下有各种钩子脚本,在特定时机自动触发。

钩子 触发时机 常见用途
pre-commit 提交前 跑 lint、单元测试 ★
commit-msg 提交信息写入前 校验 commit 格式
pre-push 推送前 跑完整测试
post-merge 合并后 自动安装依赖
# 示例:pre-commit 钩子(.git/hooks/pre-commit,需 chmod +x)
#!/bin/sh
echo "提交前检查:运行单元测试..."
python -m pytest tests/ -q
if [ $? -ne 0 ]; then
    echo "❌ 测试未通过,提交被拒绝"
    exit 1
fi

💡 测试价值:用 pre-commit 强制跑 lint 和冒烟测试,能拦住大量低级错误。推荐用 pre-commit 框架 统一管理(配置在 .pre-commit-config.yaml,可随仓库共享)。

8.3 分支保护(平台侧配置)

GitHub / GitLab 都支持对分支设置保护规则:

  • 禁止直接 push 到 main,必须走 PR
  • PR 必须有 N 个评审通过
  • CI 流水线必须通过才能合并
  • 要求分支是最新的

九、测试视角:Git 在测试工作中的应用(重点)

9.1 测试代码也要版本管理

测试仓库建议结构:
  test-repo/
  ├── api/                  接口自动化用例
  ├── web/                  Web 自动化(Selenium/Playwright)
  ├── app/                  App 自动化
  ├── performance/          压测脚本(JMeter/Locust)
  ├── data/                 测试数据
  ├── config/               环境配置(不含密码!)
  ├── conftest.py           pytest 公共 fixture
  ├── pytest.ini
  ├── requirements.txt
  └── .gitignore

⚠️ 安全红线:测试脚本里绝不能硬编码密码、token、AK/SK。用环境变量或配置中心,并把 .env 加入 .gitignore。

9.2 用 Git 确定回归测试范围(高频实战)

# 场景:开发说"本次改动不大",但你想知道到底改了哪些模块

# 1. 查看本次发布改了哪些文件
git diff --name-only v2.2.0...v2.3.0

# 2. 只看某个模块的改动
git diff --stat v2.2.0...v2.3.0 -- src/order/

# 3. 查看具体业务逻辑改动
git diff v2.2.0...v2.3.0 -- src/order/service.py

# 4. 找出改动涉及的所有提交(便于逐个确认)
git log --oneline v2.2.0...v2.3.0
# analysis_change.py —— 自动分析变更范围,辅助确定回归用例
import subprocess


def changed_files(base: str, target: str = "HEAD") -> list:
    """获取两个版本之间的变更文件"""
    out = subprocess.run(
        ["git", "diff", "--name-only", f"{base}...{target}"],
        capture_output=True, text=True, encoding="utf-8"
    ).stdout
    return [f for f in out.splitlines() if f.strip()]


def classify(files: list) -> dict:
    """按目录归类,映射到测试模块"""
    mapping = {
        "src/order/": "订单模块",
        "src/user/": "用户模块",
        "src/pay/": "支付模块",
        "src/product/": "商品模块",
        "api/": "接口层",
        "web/": "前端页面",
    }
    result = {}
    for f in files:
        hit = "其他"
        for prefix, module in mapping.items():
            if f.startswith(prefix):
                hit = module
                break
        result.setdefault(hit, []).append(f)
    return result


if __name__ == "__main__":
    files = changed_files("v2.2.0", "v2.3.0")
    print(f"共变更 {len(files)} 个文件")
    for module, fs in classify(files).items():
        print(f"\n【{module}】{len(fs)} 个文件")
        for f in fs[:10]:
            print(f"  - {f}")
    # 输出示例:→ 订单模块 12 个文件 → 本轮回归重点:订单

9.3 定位 Bug 引入点(bisect 实战)

# 场景:登录功能昨天还好好的,今天挂了,中间有 30 次提交

git bisect start
git bisect bad HEAD
git bisect good v2.2.0

# 如果有自动化测试脚本,全自动定位:
git bisect run pytest tests/test_login.py::test_login_success

# Git 自动二分,最终输出:
# a1b2c3d4 is the first bad commit
# commit a1b2c3d4
# Author: 张三
# Date: ...
#     refactor(auth): 重构鉴权逻辑

# 拿到这个提交,直接定位到"重构鉴权"引入的问题
git bisect reset

💡 给开发的 Bug 描述:”该问题由提交 a1b2c3d4(refactor(auth): 重构鉴权逻辑)引入,bisect 已确认”——这种 Bug 单质量极高,开发会立刻信服并快速修复。

9.4 复现历史版本的问题

# 切到出问题的版本
git switch -c reproduce v2.1.0      # 从 tag 创建分支
# 部署/运行,复现问题

# 或者只检出某个历史版本的配置文件
git show v2.1.0:config/app.yml > app_v2.1.0.yml

# 复现完记得切回去
git switch main

9.5 分支与测试环境对应

分支 环境 测试内容
feature/* 开发自测环境 开发自测、冒烟
develop 集成测试环境 接口测试、集成测试
release/* 预发布/Staging 系统测试、回归、验收
main 生产 灰度验证、线上监控

💡 提 Bug 时务必注明分支名 + commit hash + 环境,避免”我这里好的”这类扯皮。

9.6 与 CI/CD 集成

典型流水线(配合 Jenkins / GitLab CI / GitHub Actions):

  代码 push / PR
      ↓ 触发
  1. 拉取代码(git clone)
  2. 安装依赖
  3. 静态检查(lint)
  4. 单元测试
  5. 接口自动化(pytest)
  6. 构建打包
  7. 部署到测试环境
  8. UI 自动化 / 冒烟
      ↓
  结果回写到 PR(通过才允许合并)
  • git commit 触发构建 → 见 Jenkins
  • 自动化测试脚本本身也在 Git 仓库中,CI 拉取后执行
  • 测试报告可归档为 CI 产物

9.7 测试团队的 Git 建议

  1. 测试脚本与开发代码同仓库或独立仓库均可,但要保证版本可追溯
  2. 用例分支跟随需求分支,需求合入主干时用例一起合入
  3. 每个提测版本打 Tag,便于回溯
  4. 提交信息规范统一,方便追溯(如 test(order): 补充取消订单用例 #BUG-123)
  5. 敏感信息绝不入库(密码、token、生产数据)
  6. 定期同步主干,避免分支长期偏离导致合并困难

十、图形化工具

工具 平台 特点
VSCode 内置 Git 跨平台 轻量,够用,推荐日常
IntelliJ IDEA / PyCharm 跨平台 Java/Python 开发者首选,冲突解决体验好
SourceTree Win/mac 免费,可视化分支图清晰
GitKraken 跨平台 漂亮,功能强(部分收费)
TortoiseGit Windows 资源管理器右键集成,老牌
GitHub Desktop Win/mac 简单,适合新手

💡 建议:图形化工具用来”看”(分支图、diff 对比),命令行用来”做”(rebase、reset、bisect 等复杂操作)。两者结合效率最高。图形化工具能帮你直观理解分支结构,但复杂操作还是命令行更可控。

十一、常见问题速查

问题 原因 解决
not a git repository 不在 Git 仓库里 cd 到仓库目录或先 git init
提交时提示配用户名邮箱 没配置 git config --global user.name/email
failed to push... rejected 远程有新提交,本地落后 先 git pull(或 git pull --rebase)再 push
push 要输密码 用的 HTTPS 地址 改 SSH 地址或配 credential helper
Permission denied (publickey) SSH 密钥没配好 检查 ~/.ssh/id_ed25519.pub 是否加到平台
中文文件名显示 \344\275\240 默认转义 git config --global core.quotepath false
换行符冲突(^M / 整个文件都被改) CRLF/LF 不一致 配 core.autocrlf
误删分支 — git reflog 找回最后一个提交,再 git branch <name> <commit>
reset --hard 后想恢复 — git reflog 找回(前提是 commit 过)
提交了大文件导致 push 很慢 仓库有二进制大文件 用 git lfs,或 git filter-branch 清理历史(谨慎)
合并冲突不知道怎么选 — 别自己猜,找相关开发确认;或 git merge --abort 重来
工作区乱了想重来 — git stash(有改动要留)或 git reset --hard origin/main(确认无重要改动)

常用命令速查:

# 日常
git status / git add . / git commit -m "xxx" / git push / git pull
git log --oneline / git diff / git diff --name-only

# 分支
git branch -a / git switch -c <name> / git switch <name> / git merge <name>
git branch -d <name> / git cherry-pick <commit>

# 撤销
git restore <file>              # 丢工作区改动
git restore --staged <file>     # 撤出暂存区
git reset --soft HEAD~1         # 撤提交,保留改动
git revert <commit>             # 安全撤销(推荐)
git reflog                      # 找回丢失的提交

# 临时
git stash / git stash pop / git stash list

# 排查
git bisect start/bad/good/reset     # 定位引入 Bug 的提交
git blame <file>                    # 谁改的这行
git log -S "keyword"                # 搜代码变更

十二、学习资源

资源 说明
Pro Git 中文版 最权威的免费教程,强烈推荐
Git 官方文档 命令完整参考
Learn Git Branching 交互式可视化练习,理解分支最佳工具
GitHub Skills GitHub 官方互动课程
gitignore.io 生成 .gitignore 模板
Conventional Commits 提交信息规范
Oh Shit, Git!?! 救急:Git 搞砸了怎么办
Git Flight Rules 常见问题的应对手册