跳转至

Git 分布式版本控制工作原理

目的

理解 Git 的分布式版本控制工作原理,掌握如何在本地和远程分支之间协作和合并。

内容

1. Git 的分布式特性

Git 是一个分布式版本控制系统(DVCS),与集中式系统(如 SVN)不同:

  • 每个开发者的电脑都有完整的仓库副本(包括所有历史记录和分支)
  • 这就像整个团队每个人都拥有完整的项目时光机,而不仅是服务器上有
  • 优势:
    ✅ 离线工作:没网络也能提交代码、查看历史
    ✅ 安全冗余:即使服务器损坏,任何本地仓库都可恢复项目
    ✅ 高效协作:本地操作更快,网络交互只在同步时需要

🌟 新手理解提示: 想象成:每个开发者都有完整的项目备份,而不是只能连接中央服务器查看

2. 分支管理与协作

在 Git 中,分支允许开发者并行工作。分支就像代码的平行宇宙,让多人同时进行不同任务:

分支类型 创建场景 生命周期 合并目标
main/master 项目初始分支 永久存在 -
feature 新增登录功能 功能完成后删除 develop/main
hotfix 紧急修复线上支付漏洞 修复后立即删除 main + develop
develop 集成多个功能的测试分支 长期存在 main

💡 新手提示:

  1. 使用 git branch 查看所有分支
  2. 使用 git switch -c 新分支名 创建并切换分支
  3. 分支名只是约定,实际可用任意名称
  4. 旧教程里的 git checkout -b 是 git switch -c 的旧写法;恢复文件请用 git restore,不要再用 git checkout -- <文件>

3. 合并远程分支与本地分支

何时需要合并?

  • 当多人修改了同一文件
  • 当你拉取代码时其他人已推送新版本
  • 当你完成功能需要合并到主分支

两种合并方式

  • Git Merge:通过 git merge 命令将不同分支的修改合并。当两个分支都已经各自有新提交(即已经分叉)时,会生成一个新的合并提交,保留两个分支的历史记录;如果当前分支是被合入分支的祖先,默认会执行快进(fast-forward),只把当前分支指针移到被合入分支的最新提交,不会产生合并提交。例如下面的 main 若没有独立的新提交,就会快进到 feature-branch;想在这种情况下留下合并提交可以用 git merge --no-ff feature-branch。反过来,如果被合入分支已是当前分支的祖先,Git 会提示 Already up to date.,无需合并。合并时,如果两个分支在同一部分文件有不同修改,Git 会提示冲突,需要手动解决。
git switch main
git pull origin main
git merge feature-branch
  • Git Rebase:通过 git rebase 命令把当前分支上的提交“重放”到目标分支的最新提交之上。与 merge 不同,rebase 不会产生合并提交,历史记录看起来更为线性,但每条被重放的提交都会生成新的哈希。下面这条命令的含义是:取出 feature-branch 上“main 之后”的提交,逐个重放到 main 的最新提交之上;不是把 main 的修改搬进 feature-branch。因此只有自己使用的分支才适合 rebase。
git switch feature-branch
git rebase main

4. 处理分支冲突

在合并分支时,可能会发生冲突,尤其是在多个开发者同时修改相同文件的情况下。Git 无法自动合并这些冲突,因此需要开发者手动干预。冲突的解决通常包括:

  • 查看冲突文件,找到冲突标记的位置。
  • 使用 git add 命令将解决后的文件标记为已解决。
  • 使用 git commit 提交合并结果。 冲突的示例:
# 发生冲突后,编辑冲突文件
git add conflicted-file
git commit -m "Resolved merge conflict"

⚠️ 注意:冲突时不要慌,这只是正常的协作信号!

5. 推送与拉取操作

推送(Push):将本地仓库的更改提交到远程仓库。推送操作要求本地分支与远程分支保持同步,且在推送之前需要先拉取(git pull)远程仓库的更新,以避免冲突。

# 将本地的 feature-branch 推送到远程 origin 仓库
git push origin feature-branch

# 首次推送需设置上游跟踪分支(新手常见问题)
git push -u origin feature-branch

拉取(Pull):将远程仓库的更改拉取到本地仓库。拉取时 Git 会自动进行合并,或者如果使用 git pull --rebase,则会使用 rebase 方式进行合并。

# 基本拉取(相当于 git fetch + git merge)
git pull origin main

# 使用 rebase 方式拉取(保持历史线性)
git pull --rebase origin main

💡 提示:推送前先拉取可减少冲突!

6. Git 工作流建议

标准功能开发流程

# 1. 从主分支开始
git switch main

# 2. 获取最新代码
git pull origin main

# 3. 创建功能分支
git switch -c feature-login

# ...开发完成后...

# 4. 提交到本地仓库
git add .
git commit -m "添加登录功能"

# 5. 同步主分支最新状态
git switch main
git pull origin main

# 6. 合并更新到功能分支
git switch feature-login
git merge main

# 7. 推送到远程
git push origin feature-login

# 8. 在代码平台创建 Pull Request (PR)

常见新手问题

Q1: 如何知道当前在哪个分支?

git status  # 第一行显示 On branch...
git branch  # 带*号的是当前分支

Q2: 推送时提示冲突怎么办?

# 1. 先拉取最新代码
git pull origin 分支名

# 2. 解决冲突(见第4节)

# 3. 重新推送
git push origin 分支名

Q3: rebase 和 merge 有什么区别?

操作方式 历史记录 复杂度 适用场景
merge 保留分支合并痕迹 简单安全 公共分支合并
rebase 变成直线历史 需要理解原理 整理本地提交历史

Q4: 如何撤销错误的提交?

# 撤销最近一次提交(保留修改)
git reset --soft HEAD~1

# 完全丢弃最近提交(危险,慎用!)
git reset --hard HEAD~1

已经推送的提交不要用 reset 改写

git reset 会移动分支指针并改写历史。如果这些提交已经推送到共享分支,你的改写会和别人的历史分叉,逼他们做一次痛苦的清理;随后如果再想同步,就只能强制推送。

这种情况应改用 git revert 生成一个反向提交:它保留原有历史,用新提交抵消错误改动,别人正常 git pull 即可跟上。

git switch main
git pull --ff-only
git revert SHA          # 生成一条“撤销该提交”的新提交
git push origin main

注意 git revert 需要 SHA 对应的提交在本地历史中,且一次只能撤销一条;撤销合并提交时要额外指定 -m <父提交编号>。仅在“提交还没推送、或分支只有自己使用”时,才可以用 reset 改写历史,并用 git push --force-with-lease 更新。

Q5: 如何找回误删的分支?

# 1. 查看最近操作记录
git reflog

# 2. 找到删除前的commit ID
# 3. 重建分支
git branch 分支名 commit-id

总结

掌握 Git 分布式协作需要理解三个核心:

  1. 仓库结构:工作区 → 暂存区 → 本地仓库 → 远程仓库
  2. 分支本质:轻量级可移动的提交指针
  3. 协作流程:拉取 → 开发 → 合并 → 推送

➡️ 新手练习建议:

  1. 在安全环境(个人项目)练习合并冲突
  2. 使用 git status 随时查看状态
  3. 小步提交(每次提交只做一个小修改)

📚 推荐练习

在 Learn Git Branching 进行交互式练习