十五. 版本控制基础(Git)
版本控制可以说是程序员必须掌握的必备技能。从小的方面看,它能让你记录代码的每一次变更,即便不小心删除了文件或改乱了逻辑,也能轻松恢复到之前的正确状态;从大的方面看,它能让团队成员在同一个项目上并行开发,彼此互不干扰。Git 是当下最主流的分布式版本控制系统,没有之一。本章节将从零开始,覆盖日常开发中 90% 的使用场景,并提供大量可以直接上手的实战案例,帮助你快速掌握 Git 核心用法。

15.1 为什么需要版本控制?
先说说最直接的几个好处。首先是代码备份与恢复:文件误删,或者修改了一堆代码后发现逻辑完全错误,版本控制能让你像时空穿越一样,快速回到之前的正确状态。其次是团队协作开发:多人同时修改同一个项目,Git 会自动尝试合并大家的改动,如果实在合并不了,也会明确提示冲突位置,避免出现“谁覆盖了谁的代码”这类混乱情况。再就是分支管理:你可以在不影响主代码的前提下,单独开辟一个分支来开发新功能或修复漏洞,等开发测试完毕再合并回去,整个过程对主分支零影响。最后是审计追踪:每次提交都会记录是谁、在什么时间、改了哪些内容,并且可以附带提交说明,出了问题追溯起来非常方便。
15.2 Git 安装与初始配置
15.2.1 安装 Git
不同操作系统的安装方式略有不同。Windows 用户可以直接从 git-scm.com 下载安装程序,一路默认选项安装即可,装完后就能使用 Git Bash 终端。macOS 用户推荐使用 Homebrew:brew install git,或者直接下载官方安装包。Linux(以 Ubuntu/Debian 为例)使用 sudo apt install git 即可。安装完成后,打开终端或 Git Bash,输入 git --version,如果看到类似 git version 2.40.0 的输出,就说明安装成功了。
15.2.2 初次配置
安装完成后,必须配置用户名和邮箱,因为每次提交都会记录这些信息。
# 设置用户名和邮箱
git config --global user.name "你的名字"
git config --global user.email "your_email@example.com"
# 设置默认分支名称为 main(Git 2.28 版本之后支持此选项)
git config --global init.defaultBranch main
# 可选:设置别名,提高日常操作效率
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 --list
15.3 Git 核心概念:工作区、暂存区、仓库
理解 Git,首先要搞清楚三个核心区域的关系。工作区就是你电脑上看到的项目目录,所有文件的变化 Git 都能检测到。暂存区是一个临时存放区,你通过 git add 把工作区的修改添加到暂存区,相当于告诉 Git“这些改动我准备提交了”。仓库就是项目根目录下的 .git 文件夹,它存储了所有的历史版本和元数据,通过 git commit 将暂存区的内容永久保存到仓库。简单流程就是:
工作区 → git add → 暂存区 → git commit → 仓库
15.4 基本操作:从初始化到提交
15.4.1 创建仓库
有两种常见方式。一种是在现有项目目录中初始化:cd my-project 然后 git init。另一种是从远程仓库克隆:git clone https://github.com/用户名/仓库名.git。
15.4.2 查看状态
git status 是非常常用的命令,它会告诉你当前工作区和暂存区的状态。红色文件表示已修改但未暂存,绿色文件表示已暂存。
15.4.3 添加与提交
# 添加单个文件
git add README.md
# 添加所有修改(包括新文件和删除)
git add .
# 添加所有 .py 文件
git add *.py
# 提交(必须写有意义的消息)
git commit -m "添加了登录功能"
# 跳过暂存区直接提交(只对已跟踪的文件有效)
git commit -a -m "直接提交所有修改"
这里有个好习惯:提交消息使用现在时、简洁清晰。比如“修复登录按钮点击无响应”,而不是“fix bug”。
15.4.4 查看提交历史
git log # 完整历史
git log --oneline # 简洁一行显示
git log --graph --oneline # 图形化显示分支
git log -p # 显示每次提交的差异
git log --author="张三" # 过滤作者
15.5 撤销与回退(安全第一)
操作失误了怎么办?别慌,Git 提供了多种撤销方式,但需要根据你的操作阶段选择正确的方法。
15.5.1 撤销工作区的修改(未 add)
# 丢弃某个文件的修改,恢复到最近一次 commit 或 add 的状态
git restore README.md
# 丢弃所有工作区修改(危险!会丢失所有未暂存的改动)
git restore .
15.5.2 撤销暂存区的修改(已 add 但未 commit)
# 将文件从暂存区移出,保留工作区修改
git restore --staged README.md
# 等价于旧命令
git reset HEAD README.md
15.5.3 修正最后一次提交
# 如果提交后发现漏了文件或写错消息
git add 遗漏的文件
git commit --amend -m "新的提交消息"
# 不修改消息,只添加文件
git commit --amend --no-edit
15.5.4 回退到历史版本
# 先用 git log --oneline 查看提交哈希
git log --oneline
# 回退到上一个版本(HEAD^ 表示上一个,HEAD~3 表示上三个)
git reset --hard HEAD^ # 彻底删除之后的修改(危险!丢失未提交的改动)
git reset --soft HEAD^ # 回退提交但保留工作区和暂存区修改
git reset --mixed HEAD^ # 默认方式,保留工作区但清空暂存区
# 回退到指定哈希
git reset --hard a1b2c3d
# 如果你已经推送到远程,不要用 reset,而要用 revert(生成反向提交来撤销)
git revert HEAD
15.6 分支管理(并行开发的利器)
分支是 Git 最强大的功能之一,它让并行开发成为可能。
15.6.1 创建与切换分支
# 列出本地分支,当前分支前有 * 号
git branch
# 创建新分支
git branch feature-login
# 切换到 feature-login 分支
git checkout feature-login
# 或使用新命令(推荐)
git switch feature-login
# 创建并切换一步完成
git checkout -b feature-login
git switch -c feature-login
15.6.2 合并分支
# 首先切换到目标分支(如 main)
git switch main
# 将 feature-login 分支合并进来
git merge feature-login
# 合并后可以删除特性分支
git branch -d feature-login
15.6.3 合并冲突与解决
当两个分支修改了同一文件的同一区域时,Git 无法自动合并,就会产生冲突。举个典型的例子:main 分支的 hello.py 第 5 行是 print("Hello"),而 feature 分支改成了 print("Hi")。合并时 Git 会提示冲突。
git merge feature
# 输出:
# Auto-merging hello.py
# CONFLICT (content): Merge conflict in hello.py
打开冲突文件,你会看到类似这样的内容:
<<<<<<< HEAD
print("Hello")
=======
print("Hi")
>>>>>>> feature
解决步骤如下:
- 手动编辑文件,删除
<<<<<<<、=======、>>>>>>>这些标记,保留你想要的内容(比如print("Hello World"))。 - 保存文件。
- 告诉 Git 冲突已解决:
git add hello.py,然后git commit -m "解决合并冲突"。
当然,最好的策略是避免冲突。方法也很简单:频繁拉取远程更新、保持分支短小、尽量让团队成员修改不同的文件。
15.6.4 分支管理最佳实践(Git Flow 简化版)
- main 分支:稳定生产版本,只接受合并,不直接提交。
- develop 分支:日常开发主分支(个人项目可以省略)。
- feature/* 分支:每个新功能一个分支,开发完成后合并回 develop。
- hotfix/* 分支:紧急修复线上 Bug,直接从 main 创建。
15.7 远程仓库(GitHub / GitLab)
本地操作熟悉之后,接下来就是与远程仓库配合使用。
15.7.1 添加远程仓库
# 在 GitHub 上新建一个空仓库(不要勾选 Initialize with README)
# 本地已有仓库,关联远程
git remote add origin https://github.com/你的用户名/仓库名.git
# 查看远程仓库
git remote -v
15.7.2 推送与拉取
# 第一次推送,设置上游分支
git push -u origin main
# 后续推送
git push
# 拉取远程更新(下载并合并)
git pull
# 仅下载远程更新(不合并)
git fetch
git diff origin/main # 查看本地与远程差异
git merge origin/main # 手动合并
15.7.3 克隆远程仓库
git clone https://github.com/用户名/仓库名.git
cd 仓库名
15.7.4 处理推送冲突
当你 git push 被拒绝时,说明远程有新的提交你没有拉取。标准流程是:
git pull --rebase # 拉取远程提交,并将你的本地提交重新应用到最新版本上
# 如果有冲突,解决后 git add . 然后 git rebase --continue
git push
15.8 储藏(Stash):临时保存未提交的改动
有时候你正在修改代码,突然需要切换到其他分支(比如紧急修复 Bug),但当前改动尚未完成,又不想提交一个半成品。这时候就可以用 stash。
# 储藏所有未提交的修改(包括暂存区)
git stash sa ve "正在进行登录功能开发"
# 查看储藏列表
git stash list
# 恢复最近一次储藏,并删除储藏记录
git stash pop
# 恢复但不删除
git stash apply
# 删除指定储藏
git stash drop stash@{0}
15.9 标签(Tag):标记重要版本
标签常用于标记版本发布点,比如 v1.0、v2.0 等。
# 创建轻量标签
git tag v1.0
# 创建附注标签(推荐,包含作者、日期、信息)
git tag -a v1.0 -m "发布第一个正式版本"
# 推送标签到远程
git push origin v1.0
# 推送所有标签
git push --tags
# 查看标签
git tag
# 基于标签创建分支修复旧版本
git checkout -b fix-old-bug v1.0
15.10 .gitignore 文件详解
在项目根目录创建 .gitignore 文件,可以指定哪些文件或目录不被 Git 追踪。
# 编译产物
*.pyc
__pycache__/
# 虚拟环境
venv/
env/
.venv
# IDE 配置
.vscode/
.idea/
# 系统文件
.DS_Store
Thumbs.db
# 日志和数据库
*.log
*.sqlite3
# 敏感配置
.env
secrets.yaml
# 自己生成的文件
output/
temp/
需要特别注意:.gitignore 只能忽略未追踪的文件。如果某个文件已经被 Git 追踪过,你需要先将其从追踪中移除:
git rm --cached config.py # 停止追踪但保留文件
git commit -m "停止追踪 config.py"
