持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情
1. 场景再现
某个版本开发过程中,由于代码最新提交存在一定问题,需要对历史提交版本进行修改并发布上线。
为了方便,就选择使用 IDEA 中版本控制的 git checkout [commitId] 命令切换到指定版本。
切换是成功的,并且在当前版本运行打包都没有问题,但是,当对代码进行了修改并进行提交时,就会发现,无法提交!显示 Detached HEAD!
2. Detached HEAD 含义
Git 中正常的流程应该是 HEAD 指向某个分支,分支又有对应的 commitId,这样 HEAD 可以在不同的分支之间进行切换,且每个分支的版本提交也不断进行迭代。
Detached Head,指 HEAD 处于游离状态的,代表 Git 中 HEAD 指针指向了某一个具体的 commitId,而不是指向具体分支。
3. Detached HEAD 的产生和解决方法
3.1 切换到远程分支
使用 git clone 拉取远程项目到本地后,默认拉取远程的 master 并在本地创建同名分支与远程关联;
如果仓库中存在多个分支,使用 git checkout [remoteBranchName] 切换到指定远程分支时,本地并不存在对应的分支,且此时 git 不会自动创建同名分支;
最终可以切换成功,但是会导致出现 Detached HEAD 状态,即此时 HEAD 直接指向了远程分支的最新 commitId。
此种情况下,本地修改的代码无法进行提交,并且在切换分支后 git 会删除修改记录。
解决方法
如果希望切换到指定的远程分支后本地可以正常使用,需要使用命令参数确保 git 切换分支时本地存在关联分支;
用 git checkout -b [remoteBranchName] 命令来进行分支切换,该命在切换分支时会检查如果本地没有同名分支,则 git 会在本地创建同名分支并进行关联。
3.2 切换到历史版本
在当前分支中,可以使用 git checkout [commitId] 来切换到历史版本并查看历史修改内容;
如果针对切换后代码仅是查看,则没有任何问题;但是当需要在此基础上修改并提交代码时,则会出现大问题!
在使用 git checkout [commitId] 或 git checkout [tagXXX] 命令切换到历史版本后,此时的 HEAD 指向了对应的 commitId,而并没有产生新的分支,因此会成 Detached HEAD 状态!
此时在 git 控制台中使用 git status 查看状态显示:
$ git status
HEAD detached at 232sged
- HEAD 游离在 232sged 的提交版本处
还可以使用 git branch 查看当前分支信息显示:
$ git branch
* (HEAD detached at 232sged)
V20220527
master
- HEAD 停留在了一个临时分支之上,即游离状态
解决方法
对于该问题,可以通过创建新分支的方式来解决,保证 HEAD 指向具体的分支,具体流程为:
- 针对指定 commitId 创建一个新分支,
git branch [branch-name] commitId - 在新分支中进行修改并提交 commit
- 切换到目标分支并将新分支合并到目标分支
- 将合并后的 commit 记录提交到远程分支
- 最后删除临时分支
4. 总结
对于 Git 使用过程中出现的 Detached HEAD 问题,通常是因为操作导致 HEAD 没有指向具体的分支,而是指向了远程分支的 commitId 或本地历史版本 commitId,最终导致在此情况下发生的修改不能正确提交。
可以通过在本地创建分支的方法,将修改设置到具体分支上,并将分支合并到最终需要的分支上,以此委婉的完成修改。
另外,如果确定最新提交记录不再需要,可以使用 git reset 命令回滚到指定版本来进行修改,而不是使用 git checkout [commitId]。