Git: Git获取与安全更新详解
获取(Fetch)是从远程仓库下载更新但不自动合并的操作。相比pull,fetch更安全可控,可以先查看更新内容再决定是否合并。理解fetch的使用对团队协作非常重要。
1. 获取基础概念
(1) 什么是获取
获取是从远程仓库下载更新的操作,但不会修改当前分支:
- 下载提交:获取远程的新提交
- 更新远程分支引用:更新origin/main等引用
- 不修改当前分支:工作区保持不变
- 安全可控:可以先审查再合并
graph TB
A[远程仓库<br/>Remote] -->|git fetch| B[远程分支副本<br/>origin/main]
B --> C{查看更新}
C -->|满意| D[git merge]
C -->|不满意| E[保持原状]
D --> F[当前分支<br/>main]
style A fill:#d4edda
style B fill:#fff3cd
style F fill:#c3e6cb
(2) fetch vs pull
| 特性 | fetch | pull |
|---|---|---|
| 下载更新 | ✅ | ✅ |
| 自动合并 | ❌ | ✅ |
| 修改当前分支 | ❌ | ✅ |
| 安全性 | 高 | 中 |
| 可控性 | 高 | 低 |
(3) 获取的作用
- 安全查看更新:下载但不立即合并
- 审查代码:查看他人的修改
- 选择性合并:决定是否和何时合并
- 更新远程引用:同步远程分支信息
2. 基本获取操作
(1) 获取所有更新
▶ 示例:获取更新
BASH
# 获取所有远程仓库的更新
git fetch
# 输出:
# remote: Enumerating objects: 8, done.
# remote: Counting objects: 100% (8/8), done.
# remote: Compressing objects: 100% (5/5), done.
# remote: Total 5 (delta 3), reused 0 (delta 0), pack-reused 0
# Unpacking objects: 100% (5/5), 1.23 KiB | 1.23 MiB/s, done.
# From https://github.com/user/repo
# a1b2c3d..d4e5f6g main -> origin/main
# * [new branch] feature -> origin/feature
# * [new tag] v1.0.0 -> v1.0.0
(2) 获取特定远程
▶ 示例:获取特定远程
BASH
# 获取origin的更新
git fetch origin
# 获取upstream的更新
git fetch upstream
# 获取所有远程仓库
git fetch --all
# 输出:
# Fetching origin
# Fetching upstream
# Fetching backup
(3) 获取特定分支
▶ 示例:获取特定分支
BASH
# 获取特定分支
git fetch origin main
# 获取多个分支
git fetch origin main develop
# 获取远程分支到本地不同名称
git fetch origin main:local-main
# 获取并创建本地分支
git fetch origin feature:feature
3. 查看获取的更新
(1) 查看远程分支
▶ 示例:查看远程分支
BASH
# 获取更新
git fetch origin
# 查看远程分支
git branch -r
# 输出:
# origin/HEAD -> origin/main
# origin/main
# origin/develop
# origin/feature
# 查看所有分支
git branch -a
# 输出:
# * main
# develop
# remotes/origin/HEAD -> origin/main
# remotes/origin/main
# remotes/origin/develop
# remotes/origin/feature
(2) 查看远程更新内容
▶ 示例:查看更新详情
BASH
# 查看远程分支的最新提交
git log origin/main
# 输出:
# commit d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2
# Author: 张三 <zhangsan@example.com>
# Date: Mon Jan 1 10:00:00 2026 +0800
#
# feat: 添加新功能
# 查看远程的新提交(本地没有的)
git log main..origin/main
# 输出:
# d4e5f6g (origin/main) feat: 添加新功能
# h7i8j9k fix: 修复Bug
# 查看本地有但远程没有的提交
git log origin/main..main
# 查看双向差异
git log main...origin/main
(3) 比较本地和远程
▶ 示例:比较差异
BASH
# 比较本地和远程的差异
git diff main origin/main
# 查看差异统计
git diff --stat main origin/main
# 输出:
# src/auth.js | 10 ++++++++++
# src/user.js | 5 ++---
# 2 files changed, 12 insertions(+), 3 deletions(-)
# 查看修改的文件列表
git diff --name-only main origin/main
# 输出:
# src/auth.js
# src/user.js
# 查看详细的文件状态
git diff --name-status main origin/main
# 输出:
# M src/auth.js
# M src/user.js
4. 合并获取的更新
(1) 合并远程更新
▶ 示例:合并更新
BASH
# 获取更新
git fetch origin
# 查看更新
git log main..origin/main
# 合并远程更新
git merge origin/main
# 输出:
# Updating a1b2c3d..d4e5f6g
# Fast-forward
# src/auth.js | 10 ++++++++++
# 1 file changed, 10 insertions(+)
(2) 使用rebase合并
▶ 示例:rebase更新
BASH
# 获取更新
git fetch origin
# 使用rebase合并
git rebase origin/main
# 输出:
# First, rewinding head to replay your work on top of it...
# Fast-forwarded main to d4e5f6g.
# 如果有冲突
# CONFLICT (content): Merge conflict in file.js
# 解决冲突后
git add file.js
git rebase --continue
(3) 选择性合并
▶ 示例:cherry-pick特定提交
BASH
# 获取更新
git fetch origin
# 查看远程的新提交
git log main..origin/main --oneline
# 输出:
# d4e5f6g feat: 添加功能C
# h7i8j9k fix: 修复Bug
# k9l0m1n feat: 添加功能B
# 只合并某个提交
git cherry-pick h7i8j9k
# 或者合并多个提交
git cherry-pick k9l0m1n d4e5f6g
5. 获取标签
(1) 获取远程标签
▶ 示例:获取标签
BASH
# 获取更新(包括标签)
git fetch origin
# 输出:
# * [new tag] v1.0.0 -> v1.0.0
# * [new tag] v1.1.0 -> v1.1.0
# 查看所有标签
git tag
# 输出:
# v1.0.0
# v1.1.0
# 查看标签详情
git show v1.0.0
(2) 获取特定标签
▶ 示例:获取特定标签
BASH
# 获取特定标签
git fetch origin refs/tags/v1.0.0:refs/tags/v1.0.0
# 获取所有标签
git fetch --tags
# 删除本地标签(远程已删除)
git fetch --prune-tags
# 或者
git fetch -p --prune-tags
6. 获取选项详解
(1) 常用获取选项
| 选项 | 说明 | 使用场景 |
|---|---|---|
--all |
获取所有远程 | 更新所有远程 |
-p |
清理过期引用 | 远程分支已删除 |
--tags |
获取所有标签 | 同步标签 |
--prune-tags |
清理过期标签 | 标签已删除 |
--dry-run |
模拟获取 | 预览获取内容 |
--verbose |
详细输出 | 调试获取 |
▶ 示例:使用获取选项
BASH
# 清理过期的远程分支引用
git fetch -p
# 输出:
# From https://github.com/user/repo
# x [deleted] (none) -> origin/old-feature
# 清理过期的标签
git fetch --prune-tags
# 模拟获取
git fetch --dry-run
# 详细输出
git fetch --verbose
# 获取深度(浅克隆)
git fetch --depth=1
# 获取单分支
git fetch --single-branch
(2) 清理过期引用
▶ 示例:清理远程引用
BASH
# 查看需要清理的分支
git remote prune origin --dry-run
# 输出:
# * origin/deleted-branch would be pruned
# 执行清理
git remote prune origin
# 或者使用fetch -p
git fetch -p
# 输出:
# From https://github.com/user/repo
# x [deleted] (none) -> origin/deleted-branch
7. fetch工作流
(1) 安全更新工作流
sequenceDiagram
participant 开发者
participant 本地仓库
participant 远程分支
participant 远程仓库
开发者->>远程仓库: git fetch origin
远程仓库->>远程分支: 更新origin/main
开发者->>远程分支: git log main..origin/main
开发者->>远程分支: git diff main origin/main
开发者->>本地仓库: git merge origin/main
(2) 实际应用场景
▶ 示例:fetch工作流
BASH
# 场景1:开始工作前检查更新
git fetch origin
git log main..origin/main --oneline
# 输出:
# d4e5f6g feat: 新功能
# h7i8j9k fix: Bug修复
# 查看具体修改
git diff main origin/main
# 决定是否合并
git merge origin/main
# 场景2:有未提交的工作,先获取再决定
git fetch origin
git status
# 输出:
# On branch main
# Your branch is behind 'origin/main' by 2 commits.
# 查看更新内容
git log HEAD..origin/main
# 暂存当前工作
git stash
# 合并更新
git merge origin/main
# 恢复工作
git stash pop
# 场景3:Fork工作流
git fetch upstream
git log main..upstream/main
git merge upstream/main
git push origin main
(3) 定期同步脚本
▶ 示例:自动同步脚本
BASH
#!/bin/bash
# sync.sh - 定期同步远程更新
# 获取所有远程更新
git fetch --all
# 检查每个分支的状态
for branch in $(git branch --format='%(refname:short)'); do
upstream=$(git rev-parse --abbrev-ref $branch@{upstream} 2>/dev/null)
if [ -n "$upstream" ]; then
behind=$(git rev-list --count $branch..$upstream 2>/dev/null)
ahead=$(git rev-list --count $upstream..$branch 2>/dev/null)
if [ "$behind" -gt 0 ] || [ "$ahead" -gt 0 ]; then
echo "$branch: behind $behind, ahead $ahead"
fi
fi
done
# 询问是否同步
read -p "Sync main branch? (y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
git checkout main
git merge origin/main
fi
❓ 常见问题
Q fetch和pull应该用哪个?
A 推荐使用fetch。fetch更安全,可以先查看更新内容再决定是否合并。pull会自动合并,可能产生意外的冲突。养成先fetch再merge的习惯更好。
Q fetch会修改本地代码吗?
A 不会。fetch只下载更新到远程分支引用(如origin/main),不会修改当前分支和工作区。需要手动merge或rebase才会修改本地代码。
Q 如何查看fetch下载了什么?
A 使用
git log HEAD..origin/main 查看远程的新提交,使用 git diff HEAD origin/main 查看差异,使用 git diff --stat HEAD origin/main 查看统计。Q fetch后如何合并?
A 使用
git merge origin/main 合并远程更新,或使用 git rebase origin/main 进行rebase合并。推荐使用rebase保持线性历史。Q 如何清理已删除的远程分支引用?
A 使用
git fetch -p 或 git remote prune origin 清理本地过期的远程分支引用。这不会影响远程仓库,只是清理本地的引用。📖 小节
- 获取是从远程下载更新但不自动合并的操作,更安全可控
- 基本获取:
git fetch下载所有更新,git fetch origin获取特定远程 - 查看更新:使用log和diff查看远程的新提交和差异
- 合并更新:手动merge或rebase,或cherry-pick特定提交
- 获取标签:
--tags获取所有标签,--prune-tags清理过期标签 - fetch工作流:先fetch查看,再决定是否合并,更安全
📝 作业
-
基础题:在远程仓库创建新提交,本地使用fetch获取更新,查看远程的新提交和差异,然后手动合并。
-
进阶题:模拟Fork工作流:添加origin和upstream两个远程,从upstream fetch更新,查看差异,选择性合并到本地,然后推送到origin。
-
挑战题:编写一个脚本,定期fetch所有远程仓库,检查每个分支与远程的差异,生成同步报告,提示哪些分支需要同步。