背景
git checkout 这个命令承载了太多的功能,以至于在很长一段时间,我都会时不时疑惑,“咦,git checkout怎么还有这个作用?”。感觉还是没有理解到本质,只是停留在粗浅的表面。为了减轻记忆负担,本文就来梳理一下git checkout的核心作用。
相关概念
下面将介绍三个相关概念:提交哈希(Commit Hash)、分支名(Branch Name)、 HEAD
为了形象理解,如果我们把 Git 仓库当作一本代码历史书,那么:
提交哈希:每一页的唯一编号(如 SHA-1: 1a2b3c)。它是Git 为每个提交生成的唯一标识符(40 位 16 进制字符串,如 1a2b3c…),永久标记历史中的某个提交节点,不可变。
分支名:贴在某一页的书签(如 master、feature),书签可移动。它是指向某个提交的可变指针,一般用在不同方向开发(如 master 主分支、feature 功能分支)。
HEAD:当前阅读的页码(永远指向你正在查看的那一页)。它是指向当前分支或提交的特殊指针。HEAD有两种状态:
分支模式(默认):HEAD → 分支名 → 提交哈希。例如:HEAD → master → 1a2b3c
分离 HEAD 模式:HEAD 直接指向某个提交哈希(不通过分支)。例如:HEAD → 1a2b3c(此时 master 仍指向其他提交)。
git checkout两大作用
分支切换
git checkout <branch>
例如:git checkout master 切换到 master 分支。
此时会更新工作区、暂存区以及 HEAD 指针,使其指向目标分支的最新提交,从而呈现该分支的文件状态。例如,从 master 分支切换到 develop 分支,工作区文件会变为 develop 分支的状态。
文件重置,恢复对象是工作区文件
git checkout <提交哈希/标签/相对引用/HEAD> -- <文件路径>
例如:git checkout HEAD -- a.txt 重置 a.txt 到 HEAD 状态。
此时会更新工作区文件、HEAD 指针。HEAD指针只要不是指向当前分支的最新提交,就会出现分离HEAD的情况。代码版本管理依托分支进行,在分离HEAD状态下创建的提交可能难以通过常规方式访问,有可能会在后续的 Git 垃圾回收操作中被删除。所以,通常建议在分离HEAD状态下提交后,及时创建一个新分支并切换到该分支,将新提交整合到正常的分支体系中。这时,可以使用如下命令,创建并切换到新分支:
git checkout -b new-feature
# 等价于 git branch new-feature + git checkout new-feature
文件重置模式的两处缺省
– <文件路径> 省略
当你执行 git checkout HEAD 时,Git 会默认应用到当前目录(等价于 .),因此效果与 git checkout HEAD . 一致,所以省略文件路径,相当于作用当前目录所有文件。
<提交哈希/分支名/标签/相对引用> 省略
git checkout --<文件路径> 从暂存区获取指定文件覆盖工作区中的该文件。它主要用于撤销对工作区文件的修改,并使其回到添加到暂存区时的状态,不会影响到暂存区与版本库之间的关系
对比<提交哈希/分支名/标签/相对引用> 没省略的情况
git checkout HEAD – <文件路径> 或 git checkout commit – <文件路径>
从版本库(仓库区)中最新提交(HEAD 指向的提交)或者指定提交获取指定文件,然后覆盖工作区中的该文件。它会使工作区中的文件回到最近一次提交时的状态。