Git: Git检出与分支切换详解
检出(Checkout)是Git中多功能的重要命令,可以切换分支、恢复文件、检出特定提交。Git 2.23引入了更清晰的 git switch 和 git restore 命令来分担checkout的职责。
1. checkout命令概述
(1) checkout的多重功能
git checkout 是一个多功能命令:
- 切换分支:切换到不同的分支
- 创建并切换分支:创建新分支并立即切换
- 检出文件:从版本库恢复文件
- 检出提交:切换到特定的提交(分离头指针)
graph TB
A[git checkout] --> B[切换分支]
A --> C[创建分支]
A --> D[恢复文件]
A --> E[检出提交]
style A fill:#fff3cd
style B fill:#d4edda
style C fill:#c3e6cb
style D fill:#cce5ff
style E fill:#f8d7da
(2) 新命令的引入
由于checkout功能过多,Git 2.23引入了两个新命令:
git switch:专门用于切换分支git restore:专门用于恢复文件
(3) 命令对比
| 操作 | checkout | switch/restore |
|---|---|---|
| 切换分支 | git checkout <branch> |
git switch <branch> |
| 创建并切换 | git checkout -b <branch> |
git switch -c <branch> |
| 恢复文件 | git checkout -- <file> |
git restore <file> |
| 恢复暂存 | git checkout HEAD -- <file> |
git restore --staged <file> |
2. 切换分支
(1) 基本切换
▶ 示例:切换到已存在的分支
BASH
# 传统方式
git checkout feature
# 新方式(推荐)
git switch feature
# 输出:
# Switched to branch 'feature'
# 切换到main分支
git switch main
# 输出:
# Switched to branch 'main'
# Your branch is up to date with 'origin/main'.
(2) 切换流程详解
切换分支时,Git会执行以下操作:
sequenceDiagram
participant 用户
participant Git
participant HEAD
participant 工作区
用户->>Git: git switch feature
Git->>Git: 检查未提交修改
Git->>HEAD: 更新HEAD指针
HEAD->>Git: 指向feature分支
Git->>工作区: 更新文件
工作区->>用户: 切换完成
(3) 切换选项
▶ 示例:使用切换选项
BASH
# 切换到上一个分支
git switch -
# 输出:
# Switched to branch 'main'
# 切换并创建分支(如果不存在)
git switch -c new-feature
# 强制切换(丢弃本地修改)
git switch -f feature
# 切换但不改变工作区(只更新HEAD)
git switch --detach feature
3. 创建并切换分支
(1) 创建新分支
▶ 示例:创建并立即切换
BASH
# 传统方式
git checkout -b feature
# 新方式(推荐)
git switch -c feature
# 输出:
# Switched to a new branch 'feature'
# 基于特定提交创建
git switch -c feature a1b2c3d
# 基于远程分支创建
git switch -c feature origin/feature
# 输出:
# Branch 'feature' set up to track remote branch 'feature' from 'origin'.
# Switched to a new branch 'feature'
(2) 创建分支的选项
▶ 示例:创建分支选项
BASH
# 创建分支但不切换
git branch feature
# 创建并切换,设置上游分支
git switch -c feature --track origin/feature
# 基于某个标签创建分支
git switch -c v1.1-branch v1.0.0
# 从另一个分支的某个提交创建
git switch -c feature main~5
4. 检出文件
(1) 从版本库恢复文件
▶ 示例:恢复工作区文件
BASH
# 传统方式
git checkout -- README.md
# 新方式(推荐)
git restore README.md
# 从特定提交恢复
git restore --source=a1b2c3d README.md
# 从HEAD恢复(最新提交)
git restore --source=HEAD README.md
# 恢复多个文件
git restore file1.js file2.js
# 恢复整个目录
git restore src/
(2) 从暂存区恢复
▶ 示例:取消暂存
BASH
# 传统方式
git reset HEAD README.md
# 新方式(推荐)
git restore --staged README.md
# 同时取消暂存并恢复工作区
git restore --staged --worktree README.md
# 或者
git checkout HEAD -- README.md
(3) 恢复删除的文件
▶ 示例:恢复误删文件
BASH
# 误删文件
rm important-file.js
# 从版本库恢复
git restore important-file.js
# 或者
git checkout HEAD -- important-file.js
# 查看被删除的文件
git status
# 输出:
# deleted: important-file.js
# 恢复所有删除的文件
git restore .
5. 检出提交(分离头指针)
(1) 什么是分离头指针
当检出某个提交而不是分支时,HEAD会直接指向该提交,而不是指向分支指针。这种状态称为"分离头指针"(Detached HEAD)。
graph TB
subgraph 正常状态
HEAD1[HEAD] --> main1[main]
main1 --> C1[提交C3]
end
subgraph 分离头指针
HEAD2[HEAD] --> C2[提交a1b2c3d]
main2[main] --> C2
end
style HEAD1 fill:#d4edda
style HEAD2 fill:#f8d7da
(2) 进入分离头指针状态
▶ 示例:检出特定提交
BASH
# 检出特定提交
git checkout a1b2c3d
# 输出:
# Note: switching to 'a1b2c3d'.
#
# You are in 'detached HEAD' state. You can look around, make experimental
# changes and commit them, and you can discard any commits you make in this
# state without impacting any branches by switching back to a branch.
#
# HEAD is now at a1b2c3d feat: 添加登录功能
# 检出标签
git checkout v1.0.0
# 检出远程分支
git checkout origin/feature
(3) 在分离头指针状态工作
▶ 示例:分离头指针下的操作
BASH
# 当前处于分离头指针状态
git status
# 输出:
# HEAD detached at a1b2c3d
# nothing to commit, working tree clean
# 可以查看代码
git log --oneline
# 可以修改文件
echo "test" > test.js
# 可以提交
git add test.js
git commit -m "test commit"
# 输出:
# [detached HEAD d4e5f6g] test commit
# 1 file changed, 1 insertion(+)
# create mode 100644 test.js
# ⚠️ 注意:这个提交不属于任何分支!
(4) 保存分离头指针的修改
▶ 示例:保存修改到新分支
BASH
# 在分离头指针状态创建了提交
# 方式1:创建新分支保存
git switch -c new-branch
# 输出:
# Switched to a new branch 'new-branch'
# 方式2:合并到现有分支
git branch temp-branch
git switch main
git merge temp-branch
git branch -d temp-branch
# 如果忘记保存就切换分支
git switch main
# 可以通过reflog找回
git reflog
# d4e5f6g HEAD@{0}: checkout: moving from a1b2c3d to main
# a1b2c3d HEAD@{1}: commit: test commit
# 创建分支指向该提交
git branch saved-work d4e5f6g
6. 处理切换分支时的冲突
(1) 未提交修改的处理
切换分支时,如果工作区有未提交的修改,可能出现以下情况:
▶ 示例:处理未提交修改
BASH
# 查看当前状态
git status
# 输出:
# Changes not staged for commit:
# modified: README.md
# 情况1:修改和新分支不冲突
git switch feature
# 成功切换,修改被保留
# 情况2:修改和新分支冲突
git switch feature
# 输出错误:
# error: Your local changes to the following files would be overwritten by checkout:
# README.md
# Please commit your changes or stash them before you switch branches.
(2) 解决切换冲突的方法
▶ 示例:解决切换冲突
BASH
# 方法1:提交修改
git add .
git commit -m "WIP: 临时保存"
git switch feature
# 方法2:暂存修改(stash)
git stash
git switch feature
# 完成其他工作后
git switch main
git stash pop
# 方法3:放弃修改
git restore .
git switch feature
# 方法4:强制切换(丢弃修改)
git switch -f feature
# 方法5:携带修改切换(如果可能)
git switch -m feature
# Git会尝试合并修改
7. checkout vs switch vs restore
(1) 命令对比总结
graph TB
A[Git命令选择] --> B{操作类型}
B -->|切换分支| C[git switch]
B -->|恢复文件| D[git restore]
B -->|复杂操作| E[git checkout]
style C fill:#d4edda
style D fill:#c3e6cb
style E fill:#fff3cd
(2) 推荐使用方式
▶ 示例:现代Git命令使用
BASH
# 切换分支 - 使用switch
git switch feature
git switch -c new-feature
# 恢复文件 - 使用restore
git restore README.md
git restore --staged README.md
# 检出特定提交 - 使用switch --detach
git switch --detach a1b2c3d
# checkout保留用于特殊场景
git checkout HEAD~5 -- file.js # 从历史版本恢复特定文件
❓ 常见问题
Q checkout、switch、restore应该用哪个?
A 推荐使用新命令:
git switch 用于切换分支,git restore 用于恢复文件。git checkout 功能过于复杂,容易混淆。但checkout在某些高级场景下仍然有用。Q 什么是分离头指针?有什么风险?
A 分离头指针是指HEAD直接指向某个提交而不是分支。风险是在此状态的提交不属于任何分支,切换分支后可能丢失。建议创建新分支保存工作。
Q 切换分支时提示有未提交修改怎么办?
A 有三种选择:1) 提交修改后再切换;2) 使用
git stash 暂存修改;3) 使用 git restore . 放弃修改。根据修改的重要性选择合适的方式。Q 如何从历史版本恢复某个文件?
A 使用
git restore --source=<commit> <file> 或 git checkout <commit> -- <file> 从指定提交恢复文件。例如:git restore --source=HEAD~5 README.md。Q 如何在分离头指针状态保存工作?
A 使用
git switch -c <new-branch> 创建新分支保存当前工作。如果已经切换走了,可以通过 git reflog 找回提交,然后用 git branch <name> <commit> 创建分支。📖 小节
- checkout是多功能的命令,建议使用switch和restore替代
- 切换分支:
git switch <branch>更新HEAD和工作区 - 创建并切换:
git switch -c <branch>一步完成 - 恢复文件:
git restore <file>从版本库恢复 - 分离头指针:HEAD直接指向提交,需要创建分支保存工作
- 处理未提交修改:提交、暂存或放弃后再切换分支
📝 作业
-
基础题:创建多个分支,使用
git switch在分支间切换,体验切换分支时工作区的变化,理解HEAD指针的作用。 -
进阶题:模拟分离头指针场景:检出某个历史提交,在该状态下创建提交,然后使用
git switch -c创建新分支保存工作。 -
挑战题:模拟实际开发场景:在feature分支开发功能时,突然需要切换到main分支修复紧急Bug,使用stash保存当前工作,修复Bug后再恢复工作继续开发。