Git: Git检出与分支切换详解

检出(Checkout)是Git中多功能的重要命令,可以切换分支、恢复文件、检出特定提交。Git 2.23引入了更清晰的 git switchgit restore 命令来分担checkout的职责。

1. checkout命令概述

(1) checkout的多重功能

git checkout 是一个多功能命令:

100%
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引入了两个新命令:

(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会执行以下操作:

100%
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)。

100%
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) 命令对比总结

100%
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> 创建分支。

📖 小节


📝 作业

  1. 基础题:创建多个分支,使用 git switch 在分支间切换,体验切换分支时工作区的变化,理解HEAD指针的作用。

  2. 进阶题:模拟分离头指针场景:检出某个历史提交,在该状态下创建提交,然后使用 git switch -c 创建新分支保存工作。

  3. 挑战题:模拟实际开发场景:在feature分支开发功能时,突然需要切换到main分支修复紧急Bug,使用stash保存当前工作,修复Bug后再恢复工作继续开发。

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏