Git: Git拉取与远程更新详解
拉取(Pull)是从远程仓库获取更新并合并到本地分支的操作。通过拉取,可以同步团队成员的修改,保持本地代码与远程一致。理解pull与fetch的区别对团队协作至关重要。
1. 拉取基础概念
(1) 什么是拉取
拉取是两个操作的组合:
- fetch:从远程仓库下载更新
- merge:将远程分支合并到当前分支
graph TB
A[远程仓库<br/>Remote] -->|git fetch| B[远程分支副本<br/>origin/main]
B -->|git merge| C[当前分支<br/>main]
A -.->|git pull = fetch + merge| C
style A fill:#d4edda
style B fill:#fff3cd
style C fill:#c3e6cb
(2) pull vs fetch
| 操作 | 说明 | 影响 |
|---|---|---|
| pull | fetch + merge | 修改当前分支 |
| fetch | 只下载更新 | 不修改当前分支 |
(3) 拉取的作用
- 同步代码:获取团队成员的最新修改
- 更新分支:保持本地分支与远程同步
- 获取标签:下载远程的新标签
- 查看更新:了解远程的变化
2. 基本拉取操作
(1) 拉取当前分支
▶ 示例:拉取更新
BASH
# 拉取当前分支的更新
git pull
# 输出:
# remote: Enumerating objects: 5, done.
# remote: Counting objects: 100% (5/5), done.
# remote: Compressing objects: 100% (3/3), done.
# remote: Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
# Unpacking objects: 100% (3/3), 285 bytes | 285.00 KiB/s, done.
# From https://github.com/user/repo
# a1b2c3d..d4e5f6g main -> origin/main
# Updating a1b2c3d..d4e5f6g
# Fast-forward
# file.js | 1 +
# 1 file changed, 1 insertion(+)
(2) 拉取指定分支
▶ 示例:拉取指定分支
BASH
# 拉取指定远程分支
git pull origin main
# 拉取远程分支到本地不同名称的分支
git pull origin remote-branch:local-branch
# 拉取所有远程分支的更新
git pull --all
# 拉取并查看详细信息
git pull --verbose
(3) 拉取前的检查
▶ 示例:拉取前检查
BASH
# 查看本地和远程的差异
git fetch
git diff main origin/main
# 查看远程的新提交
git log main..origin/main
# 输出:
# d4e5f6g (origin/main) feat: 添加新功能
# h7i8j9k fix: 修复Bug
# 查看将要拉取的文件
git diff --name-only main origin/main
# 输出:
# src/auth.js
# src/user.js
# 确认后再拉取
git pull
3. 拉取策略
(1) 使用merge拉取(默认)
▶ 示例:merge拉取
BASH
# 默认使用merge拉取
git pull
# 等价于
git fetch
git merge origin/main
# 拉取后会产生合并提交(如果有分叉)
git log --oneline --graph
# 输出:
# * a1b2c3d Merge branch 'main' of https://github.com/user/repo
# |\
# | * d4e5f6g (origin/main) Remote commit
# * | h7i8j9k Local commit
# |/
# * k9l0m1n Base commit
(2) 使用rebase拉取
▶ 示例:rebase拉取
BASH
# 使用rebase拉取
git pull --rebase
# 输出:
# remote: Enumerating objects: 5, done.
# From https://github.com/user/repo
# a1b2c3d..d4e5f6g main -> origin/main
# First, rewinding head to replay your work on top of it...
# Fast-forwarded main to d4e5f6g.
# 查看历史(线性,无合并提交)
git log --oneline --graph
# 输出:
# * h7i8j9k Local commit
# * d4e5f6g (origin/main) Remote commit
# * k9l0m1n Base commit
(3) merge vs rebase拉取
graph TB
subgraph merge拉取
A1[本地提交] --> M1[合并提交]
B1[远程提交] --> M1
end
subgraph rebase拉取
A2[远程提交] --> B2[本地提交<br/>rebase后]
end
style M1 fill:#fff3cd
style B2 fill:#d4edda
merge拉取:
- 保留完整历史
- 产生合并提交
- 历史图有分叉
rebase拉取:
- 保持线性历史
- 无合并提交
- 历史图更清晰
4. 拉取冲突处理
(1) 拉取时产生冲突
▶ 示例:拉取冲突
BASH
# 本地和远程修改了同一文件
git pull
# 输出:
# remote: Enumerating objects: 5, done.
# From https://github.com/user/repo
# a1b2c3d..d4e5f6g main -> origin/main
# Auto-merging file.js
# CONFLICT (content): Merge conflict in file.js
# Automatic merge failed; fix conflicts and then commit the result.
(2) 解决拉取冲突
▶ 示例:解决冲突
BASH
# 查看冲突状态
git status
# 输出:
# You have unmerged paths.
# (fix conflicts and run "git commit")
#
# Unmerged paths:
# (use "git add <file>..." to mark resolution)
# both modified: file.js
# 查看冲突内容
cat file.js
# 输出:
# <<<<<<< HEAD
# 本地修改内容
# =======
# 远程修改内容
# >>>>>>> d4e5f6g
# 编辑文件解决冲突
# 保留需要的内容
# 标记冲突已解决
git add file.js
# 完成合并
git commit
# 推送到远程
git push
(3) 取消拉取
▶ 示例:取消拉取
BASH
# 拉取产生冲突,想取消
git merge --abort
# 或者
git reset --merge
# 对于rebase拉取
git rebase --abort
5. 拉取选项详解
(1) 常用拉取选项
| 选项 | 说明 | 使用场景 |
|---|---|---|
--rebase |
使用rebase拉取 | 保持线性历史 |
--no-rebase |
使用merge拉取 | 保留完整历史 |
--ff-only |
只允许快进合并 | 确保无分叉 |
--force |
强制拉取 | 覆盖本地修改 |
--all |
拉取所有远程 | 更新所有远程 |
--dry-run |
模拟拉取 | 预览拉取内容 |
▶ 示例:使用拉取选项
BASH
# 只允许快进合并
git pull --ff-only
# 输出(如果不能快进):
# fatal: Not possible to fast-forward, aborting.
# 模拟拉取
git pull --dry-run
# 拉取所有远程仓库
git pull --all
# 设置默认拉取方式
git config pull.rebase true # 默认使用rebase
git config pull.rebase false # 默认使用merge
git config pull.ff only # 只允许快进
(2) 拉取特定远程
▶ 示例:拉取特定远程
BASH
# 拉取origin
git pull origin
# 拉取upstream
git pull upstream main
# 拉取特定分支
git pull origin feature
# 拉取并设置上游
git pull --set-upstream origin feature
6. pull vs fetch详解
(1) fetch的使用
▶ 示例:使用fetch
BASH
# 获取远程更新(不合并)
git fetch origin
# 输出:
# remote: Enumerating objects: 5, done.
# From https://github.com/user/repo
# a1b2c3d..d4e5f6g main -> origin/main
# * [new branch] feature -> origin/feature
# 查看远程分支
git branch -r
# 输出:
# origin/main
# origin/feature
# 查看远程更新
git log origin/main
# 比较差异
git diff main origin/main
# 手动合并
git merge origin/main
# 或者rebase
git rebase origin/main
(2) 何时使用fetch
graph TB
A[需要查看远程更新] --> B[使用fetch]
B --> C[查看差异]
C --> D{是否合并?}
D -->|是| E[手动merge/rebase]
D -->|否| F[保持当前状态]
style B fill:#d4edda
style E fill:#c3e6cb
使用fetch的场景:
- 想查看远程更新但不立即合并
- 需要审查远程修改
- 想选择性地合并更新
- 有未提交的工作,不想产生冲突
使用pull的场景:
- 确定要同步远程更新
- 本地工作区干净
- 快速同步最新代码
(3) fetch + merge vs pull
▶ 示例:对比两种方式
BASH
# 方式1:直接pull
git pull origin main
# 方式2:fetch + merge(更安全)
git fetch origin
git diff main origin/main # 查看差异
git merge origin/main # 确认后合并
# 方式3:fetch + rebase
git fetch origin
git diff main origin/main
git rebase origin/main
7. 拉取最佳实践
(1) 日常拉取工作流
▶ 示例:推荐工作流
BASH
# 1. 开始工作前拉取最新代码
git checkout main
git pull --rebase origin main
# 2. 创建功能分支
git checkout -b feature/new-feature
# 3. 开发并提交
git add .
git commit -m "feat: 添加新功能"
# 4. 推送前再次拉取更新
git fetch origin
git rebase origin/main
# 5. 推送
git push origin feature/new-feature
(2) 处理拉取冲突的最佳实践
▶ 示例:冲突处理流程
BASH
# 拉取产生冲突
git pull
# 1. 查看冲突文件
git status
# 2. 使用工具解决冲突
git mergetool
# 3. 或者手动编辑文件
# 4. 测试代码
npm test
# 5. 标记冲突已解决
git add .
# 6. 完成合并
git commit
# 7. 推送
git push
(3) 保持分支同步
▶ 示例:定期同步
BASH
# 定期同步远程更新
git fetch origin
# 查看所有分支的状态
git branch -vv
# 输出:
# * main a1b2c3d [origin/main: behind 3] feat: 添加功能
# develop d4e5f6g [origin/develop: ahead 2] Fix: 修复Bug
# feature h7i8j9k [origin/feature] WIP: 新功能
# 同步落后的分支
git checkout main
git pull
# 或者同步所有分支
git pull --all
❓ 常见问题
Q pull和fetch有什么区别?
A
git pull = git fetch + git merge,会下载更新并合并到当前分支。git fetch 只下载更新,不修改当前分支,更安全。Q 什么时候使用--rebase拉取?
A 当希望保持提交历史线性,避免产生合并提交时使用。rebase拉取会将本地提交放在远程提交之上,历史更清晰。
Q 拉取时产生冲突怎么办?
A 编辑冲突文件解决冲突,使用
git add 标记冲突已解决,然后 git commit 完成合并。如果不想解决冲突,可以使用 git merge --abort 取消拉取。Q 如何查看远程有哪些新提交?
A 先
git fetch 下载更新,然后使用 git log HEAD..origin/main 查看远程的新提交,或使用 git diff HEAD origin/main 查看差异。Q 拉取失败提示"unrelated histories"怎么办?
A 这表示本地和远程没有共同历史(如本地和远程分别初始化)。使用
git pull --allow-unrelated-histories 允许合并不相关的历史。📖 小节
- 拉取是从远程获取更新并合并到本地的操作(fetch + merge)
- 基本拉取:
git pull拉取当前分支的更新 - 拉取策略:merge拉取保留完整历史,rebase拉取保持线性历史
- 拉取冲突:编辑文件解决冲突,标记已解决后提交
- pull vs fetch:fetch只下载不合并,更安全可控
- 拉取最佳实践:工作前拉取、推送前拉取、定期同步
📝 作业
-
基础题:在本地仓库创建提交并推送到远程,然后在另一位置克隆仓库,修改并推送,回到原仓库拉取更新,体验完整的拉取流程。
-
进阶题:对比merge拉取和rebase拉取:在本地和远程分别创建提交,使用两种方式拉取,观察历史图的差异。
-
挑战题:模拟拉取冲突场景:在本地和远程修改同一文件的同一位置,拉取产生冲突,手动解决冲突并完成合并。