Git 与代码协作
原始素材:
2-开发环境与工具/2.03-git-version-control.md
这篇解决什么问题
Git 是现代软件开发最重要的基础工具之一。它解决三类问题:
- 版本管理:知道每次改了什么,并能回到历史版本。
- 并行开发:不同功能可以在不同分支上推进。
- 团队协作:多人通过远程仓库共享、审查和合并代码。
这篇文档不要求死背命令,而是先建立几个核心模型:工作区、暂存区、仓库、分支、远程仓库和冲突处理。
Git 和 GitHub 的区别
| 对比 | Git | GitHub |
|---|---|---|
| 是什么 | 版本控制工具 | Git 仓库托管平台 |
| 运行在哪里 | 本地电脑 | 云端网站 |
| 是否必须联网 | 不需要 | 需要 |
| 作用 | 记录和管理代码历史 | 共享代码、PR、Issue、Code Review |
| 类比 | 本地日记本 | 云端协作空间 |
Git 是工具,GitHub 是服务。类似的平台还有 GitLab、Gitee、Bitbucket。
Git 解决的问题
没有 Git 时,版本管理常常变成这样:
项目_v1.zip
项目_v2_修改版.zip
项目_v3_最终版.zip
项目_v3_最终版_真的最终版.zip
Git 的做法是把每次「有意义的改动」保存成一次提交。提交之间形成历史链路,你可以查看、比较、回退和协作。
三个区域:工作区、暂存区、仓库
Git 最核心的模型是三个区域。
| 区域 | 英文 | 说明 |
|---|---|---|
| 工作区 | Working Directory | 你正在编辑的普通文件夹 |
| 暂存区 | Staging Area / Index | 准备进入下一次提交的改动 |
| 仓库 | Repository | .git 中保存的永久历史 |
日常提交流程:
修改文件
-> git add
-> 暂存区
-> git commit
-> 仓库历史
为什么要有 add 再 commit 两步?
因为你经常不想把所有改动都放进同一次提交。例如:
login.js:登录功能,想提交。style.css:登录页样式,想提交。debug.log:临时日志,不想提交。experiment.js:实验代码,还没完成,不想提交。
有暂存区后,你可以只提交真正属于同一件事的改动:
git add login.js style.css
git commit -m "feat: 完成登录页面"
初始化和基础配置
在当前目录初始化 Git 仓库:
git init
第一次使用 Git 时配置身份:
git config --global user.name "你的名字"
git config --global user.email "你的邮箱"
查看配置:
git config --list
日常三步工作流
1. 查看状态
git status
它会告诉你:
- 当前在哪个分支。
- 哪些文件修改了但未暂存。
- 哪些文件已暂存,等待提交。
- 是否有未跟踪文件。
2. 添加到暂存区
git add login.js
git add login.js style.css
git add .
git add . 很方便,但也容易把临时文件加进去。更稳的习惯是先 git status,再精确添加。
3. 提交
git commit -m "feat: 添加用户登录功能"
提交信息要写给未来的自己和队友看,不要只写 update、fix、改了点东西。
Commit Message 建议
常见前缀:
| 前缀 | 含义 |
|---|---|
feat: |
新功能 |
fix: |
修复 Bug |
docs: |
文档 |
style: |
格式调整,不影响功能 |
refactor: |
重构 |
test: |
测试 |
chore: |
工具、构建、依赖 |
示例:
git commit -m "feat: 添加用户登录功能"
git commit -m "fix: 修复 iOS Safari 首页白屏"
git commit -m "docs: 更新部署说明"
git commit -m "refactor: 拆分 UserService"
查看历史和差异
git log
git log --oneline
git diff
git diff --staged
常用场景:
| 命令 | 用途 |
|---|---|
git log --oneline |
简洁查看提交历史 |
git diff |
查看工作区未暂存改动 |
git diff --staged |
查看暂存区即将提交的改动 |
git show <commit> |
查看某次提交详情 |
分支:平行开发线
分支可以理解为一条独立开发线。主分支保持稳定,新功能在功能分支上开发,完成后再合并。
创建并切换分支:
git checkout -b feature-login
查看分支:
git branch
切换分支:
git checkout main
git checkout feature-login
合并分支:
git checkout main
git merge feature-login
删除已合并分支:
git branch -d feature-login
什么时候该开分支?
| 场景 | 建议 | 理由 |
|---|---|---|
| 开发新功能 | 开分支 | 完成前不影响主线 |
| 修复线上 Bug | 开 hotfix/xxx |
可快速合并上线 |
| 多人并行开发 | 各自分支 | 减少互相干扰 |
| 改一个错别字 | 可直接改 | 风险低 |
常见分支命名:
| 分支 | 用途 |
|---|---|
main / master |
稳定主线 |
develop / dev |
日常集成 |
feature/user-login |
功能开发 |
fix/login-error |
普通修复 |
hotfix/payment-timeout |
紧急修复 |
远程仓库
远程仓库是团队共享的云端仓库。常用别名是 origin。
关联远程仓库:
git remote add origin git@github.com:user/repo.git
git remote -v
第一次推送:
git push -u origin main
之后推送:
git push
拉取远程更新:
git pull
克隆已有项目:
git clone git@github.com:team/project.git
方向记忆:
git push 本地 -> 远程
git pull 远程 -> 本地
git clone 远程 -> 本地完整复制
git pull 实际上约等于:
git fetch + git merge
更谨慎的团队会先 git fetch 看看远程变化,再决定怎么合并。
Pull Request / Merge Request
团队协作通常不会直接把代码推到 main,而是:
创建功能分支
-> 提交代码
-> push 到远程
-> 创建 PR / MR
-> Code Review
-> CI 通过
-> 合并到主分支
PR 的价值:
- 让队友审查代码。
- 触发自动测试和构建。
- 留下讨论和决策记录。
- 避免直接污染主分支。
冲突处理
冲突通常发生在多人修改同一文件同一位置时。
冲突标记长这样:
function login() {
const url = '/api/login';
<<<<<<< HEAD
const timeout = 3000; // 当前分支
=======
const timeout = 5000; // 合并进来的分支
>>>>>>> feature/update-timeout
return fetch(url, { timeout });
}
含义:
| 标记 | 含义 |
|---|---|
<<<<<<< HEAD 到 ======= |
当前分支内容 |
======= 到 >>>>>>> xxx |
合并进来的内容 |
解决流程:
- 打开冲突文件。
- 判断保留哪一段,或手动合并两边逻辑。
- 删除
<<<<<<<、=======、>>>>>>>标记。 - 运行测试或至少确认代码能编译。
git add标记已解决。git commit完成合并。
git add login.js
git commit
减少冲突的习惯:
- 每天开始工作前先拉最新代码。
- 小步提交,不要攒一周再提交。
- 功能分支尽量短生命周期。
- 改公共文件前先和队友沟通。
- PR 尽量小,便于 review。
stash:临时保存现场
当你改到一半,突然要切分支修 Bug,可以用 stash 暂存未提交改动:
git stash
git checkout hotfix/payment
恢复:
git stash pop
查看 stash:
git stash list
stash 适合临时切换任务,不适合长期保存重要工作。重要改动还是应该提交到分支。
reset、revert 和 checkout 的区别
这些命令容易危险,使用前要确认目标。
| 命令 | 作用 | 风险 |
|---|---|---|
git reset |
移动当前分支指针,可改写本地历史 | 对已共享提交要谨慎 |
git revert |
新增一个反向提交,撤销某次改动 | 安全,适合公共分支 |
git checkout <file> |
恢复文件到某个版本 | 会丢弃工作区改动 |
撤销最近一次提交但保留改动:
git reset HEAD~1
撤销已推送到远程的提交,优先用:
git revert <commit>
团队项目标准流程
加入一个团队项目时,可以按这个流程:
# 1. 克隆项目
git clone git@github.com:team/project.git
cd project
# 2. 同步主分支
git checkout main
git pull origin main
# 3. 创建功能分支
git checkout -b feature/user-profile
# 4. 开发后提交
git add src/UserProfile.vue
git commit -m "feat: 添加用户资料页"
# 5. 推送功能分支
git push origin feature/user-profile
# 6. 到 GitHub / GitLab 创建 PR / MR
# 7. 根据 review 修改,继续 commit + push
# 8. 合并后回到 main 并同步
git checkout main
git pull origin main
git branch -d feature/user-profile
.gitignore
不应该提交的文件包括:
node_modules/.env、.env.local*.log.DS_Storedist/、build/- IDE 临时文件
- 密钥、证书、数据库备份
示例:
node_modules/
.env
.env.local
dist/
build/
.DS_Store
Thumbs.db
*.log
如果某个文件已经被 Git 跟踪,后来再加入 .gitignore 不会自动停止跟踪。需要:
git rm --cached <file>
常用命令速查
| 命令 | 用途 |
|---|---|
git status |
查看工作区和暂存区状态 |
git add <file> |
添加文件到暂存区 |
git add . |
添加当前目录所有改动 |
git commit -m "..." |
提交暂存区内容 |
git log --oneline |
查看简洁历史 |
git diff |
查看未暂存差异 |
git diff --staged |
查看已暂存差异 |
git branch |
查看分支 |
git checkout -b <branch> |
创建并切换分支 |
git checkout <branch> |
切换分支 |
git merge <branch> |
合并分支 |
git push |
推送到远程 |
git pull |
拉取并合并远程更新 |
git clone <url> |
克隆远程仓库 |
git remote -v |
查看远程仓库 |
git stash |
临时保存未提交改动 |
git stash pop |
恢复最近一次 stash |
git branch -d <branch> |
删除已合并分支 |
名词速查表
| 名词 | 英文 | 解释 |
|---|---|---|
| 仓库 | Repository | 保存项目历史的数据库,位于 .git。 |
| 提交 | Commit | 一次完整版本记录。 |
| 分支 | Branch | 独立开发线。 |
| 合并 | Merge | 把一个分支改动整合到另一个分支。 |
| 冲突 | Conflict | 同一位置被不同分支修改,需人工决定。 |
| 暂存 | Stage / Index | 准备进入下一次提交的改动集合。 |
| 远程 | Remote | 云端仓库副本。 |
| 克隆 | Clone | 把远程仓库复制到本地。 |
| 推送 | Push | 上传本地提交到远程。 |
| 拉取 | Pull | 下载并合并远程更新。 |
| HEAD | HEAD | 当前所在提交或分支指针。 |
| origin | origin | 默认远程仓库别名。 |
| stash | Stash | 临时保存未提交改动。 |
| PR / MR | Pull Request / Merge Request | 请求合并分支并进行代码审查。 |
小结
- Git 是版本控制工具,GitHub 是托管平台。
- 工作区、暂存区、仓库是 Git 的核心模型。
add和commit分开,是为了让提交更精确。- 分支让功能开发和主线稳定互不干扰。
- 远程仓库让团队通过 push、pull、PR 协作。
- 冲突不可怕,关键是理解冲突标记并手动选择正确结果。
.gitignore用来避免把依赖、密钥、构建产物和临时文件提交进仓库。