分离头指针
当前分支图
切换到之前的某一次提交
执行命令
$ git checkout de11fa87ea
提示,当前位于“分离头指针”状态
分支图
注意
- 在分离头指针状态下没有绑定分支
- 产生的commit不会被保存
- 分支切换后在分支图中看不到该commit
- 一段时间后会被git自动清除
使用场景
- 尝试性做一些变更,可以随时丢弃
此处演示分离头指针丢失commit的情况和补救措施
丢失commit
使用IDE修改文件并提交,忽略所有警告,修改后分支图如下
此时接到其他需求,需要切换分支进行紧急修复
执行命令
$ git checkout master
分支切换成功,并弹出提示和告警
Warning: you are leaving 1 commit behind, not connected to
any of your branches:199ac20 游离状态修改文件
If you want to keep it by creating a new branch, this may be a good time
to do so with:git branch
199ac20 Switched to branch 'master'
Your branch is up to date with 'origin/master'.
此时查看分支图,刚才的commit已经不可见
补救
此时发现刚才的commit十分重要,可根据git的提示进行补救
执行命令
$ git branch hot-fix 199ac20
再次查看分支图,可见commit已经恢复
本地分支操作
以下操作仅适用于本地分支,无远程分支协同工作的情况
修改当前commit的message——amend
当前分支图
执行命令
$ git commit --amend
自动弹出编辑器
修改后保存并关闭编辑器即可,输出如下
再次查看分支图
修改前面某次commit的message——rebase
现计划修改如下message
使用IDE拷贝其父提交的SHA值
执行命令,-i
表示交互式执行
$ git rebase -i 199ac203c90f881024c6870d56517df9e2080841
自动弹出编辑器
同时包含提示操作
Rebase 199ac20..bfe2b7f onto 199ac20 (3 commands)
Commands:
p, pick= use commit
r, reword= use commit, but edit the commit message
e, edit= use commit, but stop for amending
s, squash= use commit, but meld into previous commit
f, fixup= like "squash", but discard this commit's log message
x, exec= run command (the rest of the line) using shell
b, break = stop here (continue rebase later with 'git rebase --continue')
d, drop= remove commit
l, labelThese lines can be re-ordered; they are executed from top to bottom.
If you remove a line here THAT COMMIT WILL BE LOST.
However, if you remove everything, the rebase will be aborted.
Note that empty commits are commented out
由此可见“变基”支持很多操作,此处需要使用的是reword
,修改目标提交的命令为r
保存并关闭编辑器,会自动弹出新编辑器界面
修改后保存并关闭即可,输出如下
查看分支图
整理连续多个commit——rebase
现计划合并3个commit
使用IDE拷贝其父SHA值
执行命令
$ git rebase -i 199ac203c90f881024c6870d56517df9e2080841
此处使用squash
,官方介绍如下
s, squash
= use commit, but meld into previous commit
修改内容如下,说明将中间两个合并进第一个commit中
保存后关闭,会弹出新的编辑器界面,提示可以输出合并commit的message
编写message
保存后关闭,提示修改成功
查看分支图
分支合并策略
演示不同分支合并策略效果,当前的分支图如下
现在将hot-fix合并进master
GitLab默认合并策略——merge commit
gitlab默认合并请求使用的策略
由于没有冲突,可以线上直接合并
其对应执行的命令如下
Step 1. Fetch and check out the branch for this merge request
$ git fetch origin $ git checkout -b hot-fix origin/hot-fix
Step 2. Review the changes locally
Step 3. Merge the branch and fix any conflicts that come up
$ git fetch origin $ git checkout origin/master $ git merge --no-ff hot-fix
Step 4. Push the result of the merge to GitLab
$ git push origin master
合并完成
查看分支图,故当前分支会影响特性分支并产生新的提交
GitLab合并提交策略
提交合并请求的时候勾选策略Squash commits when merge request is accepted
其对应执行的命令如下(与默认策略一致)
Step 1. Fetch and check out the branch for this merge request
$ git fetch origin $ git checkout -b hot-fix origin/hot-fix
Step 2. Review the changes locally
Step 3. Merge the branch and fix any conflicts that come up
$ git fetch origin $ git checkout origin/master $ git merge --no-ff hot-fix
Step 4. Push the result of the merge to GitLab
$ git push origin master
合并成功
查看分支图,可见其影响特性分支,会产生新的提交,同时覆盖特性分支最后一次提交
分支合并策略——squash merge
GitHub支持squash merge
合并策略,此处通过命令行执行测试
$ git merge --squash origin/hot-fix
$ git commit -m "squash commit"
查看分支图,特性分支不变,目标分支合并提交后产生一条提交,效果与GitLab合并提交类似,但命令使用方式不同
注意:squash merge
会变更提交者作者信息,这是一个很大的问题,后期问题追溯不好处理
变基合并策略——rebase merge
该策略也是GitHub支持的合并策略,可能会产生较多冲突
选择合并策略,最后一种即变基合并策略
合并成功
查看分支图,其产生的三次提交即变基合并的结果,不会影响特性分支,也不会变更提交人,类似cherry pick
分支回滚策略
回滚策略分为两种,一种是合并后回滚,一种是普通提交回滚,不同回滚策略操作方法不同
普通提交回滚
当前分支图,计划回滚最新提交
直接在IDE中执行revert
操作即可
回滚会产生新的提交,回滚结束
合并后回滚
此时分支图,使用merge commit
方式进行过一次合并,此时不想要合并的结果,想恢复到未合并状态
错误操作——直接执行revert
此时IDE已经不支持通过revert进行回滚合并操作,通过命令行强行执行
查找SHA值
回滚merge
需要加上-m
参数git就知道到底要revert哪一个merge
$ git revert -n 25d7b405a4d50fe1b36019d90276973a8ed9160d -m 1
回滚后做一次commit即可,使用默认的message后如下
验证回滚结果,使用两个提交进行差异比对
无差异,回滚看似成功了
此时在原分支做了修复后再进行合并
报无法合并,因为分支已经进行过合并
正确操作——reset
使用reset操作到上一次提交
使用--hard
参数,丢弃后续的commit
此时继续进行操作,并支持重新合并
禁止类操作
说明:禁止类说明是对开发人员禁止,而该命令在必要时候依然需要由配置管理员使用
push -f
官方使用说明
-f, --force force updates
原因:
- 默认情况下远端分支若能顺利合并(fast forward),则会顺利合并
- 若远端分支产生冲突,则会停止push,同时提示错误,要pull到本地处理冲突后重新push
- 加
-f
之后,本地分支会强行推送到远端,若本地分支落后于远端分支也不会弹出提示,由此导致远程commit丢失
适用场景:
- 远程分支已经混乱,本地分支符合需要,需要强制整理远程分支
公共分支禁止rebase
此处对场景进行模拟,正常情况下,多位开发者对公共分支common-branch
进行修改,分支图如下
另一开发者在本地修改后执行变基操作,此处以修改历史提交message为例
变基
录入变基信息
变基成功
此时查看本地分支图,可见产生了新的分支路径,同时远程message并没有变化,故rebase对存在远程分支的情况无效
变基后推送到远端分支,提示需要合并,选择“合并”
合并后分支图如下
此时其他开发者修改文件后并fetch
远程分支查看变更情况如下
此时无法对远程分支进行push
操作
处理方法
pull远程分支在本地合并后重新推送,此时在本地再产生一次合并
此时可以成功推送
由此一来一回操作导致大量不必要的合并操作,并可能导致大量冲突,并且没有解决问题
警告类操作
reset --hard
官方对reset
操作用法说明
usage: git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [
]
or: git reset [-q] [] [--] ...
or: EXPERIMENTAL: git reset [-q] [--stdin [-z]] []
or: git reset --patch [] [--] [ ...] -q, --quiet be quiet, only report errors --mixed reset HEAD and index --soft reset only HEAD --hard reset HEAD, index and working tree --merge reset HEAD, index and working tree --keep reset HEAD but keep local changes --recurse-submodules[=
] control recursive updating of submodules -p, --patch select hunks interactively -N, --intent-to-add record only the fact that removed paths will be added later -z EXPERIMENTAL: paths are separated with NUL character --stdin EXPERIMENTAL: read paths from
原因:
- 在分支上执行该操作会丢失目标commit以后的所有commit
适用场景
- 用于分支回滚且不打算保留其后的所有变更