前言
我们不说那些 git push, git merge, git reset
等等,这些基操,这里主要讲一下 git rebase, git cherry-pick, git reflog, git stash
四个可能会用到的操作。
git rebase
两个作用
作用一:合并多次提交记录
一个小功能更改,提交 几十次 ,项目充满了无用的 commit
纪录。
简介
简要概括为:可以对某一段线性提交历史进行编辑、删除、复制、粘贴;因此,合理使用rebase
命令可以使我们的提交历史干净、简洁!
基本用法
这里介绍合并提交:
-
命令
git rebase -i [startpoint] [endpoint]
-i
的意思是--interactive
,即弹出交互式的界面让用户编辑完成合并操作。[startpoint] [endpoint]
则指定了一个编辑区间。(一个前开后闭的区间)
或者
git rebase -i HEAD~3
-
最新的三个
commit
-
然后进入
vi
编辑模式上面的是本次
rebase
操作包含的所有提交,下面注释的是git命令说明。pick:保留该commit(缩写:p)
reword:保留该commit,但我需要修改该commit的注释(缩写:r)
edit:保留该commit, 但我要停下来修改该提交(不仅仅修改注释)(缩写:e)
squash:将该commit和前一个commit合并(缩写:s)
fixup:将该commit和前一个commit合并,但我不要保留该提交的注释信息(缩写:f)
exec:执行shell命令(缩写:x)
drop:我要丢弃该commit(缩写:d)
-
修改2、3的第一个单词为s,输入:
wq
orx
保存退出。 -
有冲突解决冲突。没有则会出现一个
commit message
编辑页面。修改
commit message
,然后 输入:wq
orx
保存退出。 -
查看结果
git log
-
异常退出vi窗口,想回去:
git rebase --edit-todo
-
放弃此次压缩:
git rebase --abort
当然,你可以使用其他命令比如:d(删除),修改这几次的提交。
作用二:分支合并(变基)
从 master
分支切出一个 dev
分支,进行开发,你的同事在master
上完成一次提交,这时两个分支情况:
我们想要同步 master
分支的改动,首先想到了 merge
,还有一个命令就是rebase
,那这两个的区别是什么呢?
merge 和 rebase 的区别
两张图就可以看出:
merge:
- 新增一个新的
merge commit
,然后将两个分支的历史联系在一起。 - 使用
merge
是很好的方式,因为它是一种非破坏性的操作,对现有分支不会以任何方式被更改。
rebase:
-
rebase
会将整个dev
分支移动到master
分支的顶端,从而有效地整合了所有master
分支上的提交。 -
rebase
通过为原始分支中的每个提交创建全新的commits
来重写项目历史记录,特点是仍然会在dev
分支上形成线性提交。
Git rebase
并不会删除老的提交,也就是说你在对某个分支执行了rebase
操作之后,老的提交仍然会存放在.git文件夹的objects目录下。
git rebase 做了什么
-
git
会把dev
分支里面的每个commit
取消掉; -
把上面的操作临时保存成
patch
文件,存在.git/rebase
目录下; -
把
dev
分支更新到最新的master
分支; -
把上面保存的
patch
文件应用到dev
分支上。
基本用法
-
命令:
git rebase master #将master最新的commit作为基
-
解决冲突:
rebase
和merge
的另一个区别是rebase
的冲突是一个一个解决,如果有十个冲突,先解决第一个,然后用命令:# 解决冲突 git add xxx git rebase --continue
继续后才会出现第二个冲突。
-
在任何时候,我们都可以用
--abort
参数来终止rebase
的行动,并且分支会回到rebase
开始前的状态。git rebase —abort
总结
-
融合代码到公共分支的时使用
git merge
,而不用git rebase
-
融合代码到个人分支的时候使用
git rebase
,可以不污染分支的提交记录,形成简洁的线性提交历史记录。
作用三:将某一段commit粘贴到另一个分支上
这个建议使用git cherry-pick
,我也没实践过。
git cherry-pick
作用:挑选指定 commit
来合并。
将某个分支的部分提交(某几个提交)转移到另一个分支,原先分支提交不变。
好处
举个栗子:
在 dev
上改了提交1个更改,然后这个更改也想用到 master
分支上面。但又不需要全部提交。
基本用法
转移一个提交
-
命令:
git cherry-pick <commitHash>
在
master
分支执行,指定dev
分支的一个提交的commitHash
,就可以将这个提交应用到master
分支,产生一个新的提交,两个的哈希值不一样,相当于在两个分支上做一样的修改,然后分别提交。 -
看图秒懂:
在
master
分支上git cherry-pick Y
,然后master
上会有一个Y'
-
参数不一定是哈希值,也可以是分支名:
git cherry-pick dev
表示将
dev
分支的最近一次提交,转移到当前分支。
转移多个提交
-
多个不连续的
git cherry-pick <A> <B>
转移
A
和B
两个提交。 -
多个连续的(不包含
A
)git cherry-pick <A>..<B>
转移从
A
到B
的所有提交。 -
多个连续的(包含
A
)git cherry-pick <A>^..<B>
转移到另一个代码库
cherry-pick
的奥义就是,只要是在一个 .git
仓库管理下的本地代码,任何提交都可以被应用到任何可访问的本地分支,哪怕是跨代码库。
常用配置项
-
n, –no-commit
只更新工作区和暂存区。不产生新的提交
-
-x
在提交信息末尾追加一行(cherry picked from commit…)方便以后查到这个提交是如何产生的。
-
m parent-number, –mainline parent-number
-
如果原始分支是一个合并节点,那么
cherry-pick
默认会失败,因为不知道应该采用哪个分支的代码变动。 -
-m 配置项告诉 git 应该采用哪个分支分变动,parent-number 代表原始提交的父分支编号。
git cherry-pick -m 1 <A>
一般1号父分支是接受变动分支,2号父分支是作为变动来源的分支。
-
git reflog
作用
-
git reflog
可以查看所有分支的 所有操作记录 (包括(包括checkout
和reset
的操作),包括已经被删除的commit
记录。 -
用来恢复本地错误操作。
基本用法
-
git reflog
显示所有操作。回退到某一步:
git reset --hard HEAD@{1}
或者用前面的id -
git reflog 分支名
显示某个分支的引用日志
与 git log 的区别
-
git log
命令可以显示所有提交过的版本信息。git reflog
查看所有分支的 所有操作记录 。 -
git log
不能察看已经删除了的commit
记录。
git stash
作用
保存当前的工作进度至栈中。会分别对暂存区和工作区的状态进行保存(未提交前)
场景:
-
在一个分支开发,有一个紧急的bug,不想提交现在的更改,
stash
存起来,改完bug后又切回该分支,从栈中取出来继续开发。 -
本来应该在
feat
分支上开发,但是却在dev
分支上开发了,可以暂存起来,切到想要开发的分支,然后取出来。
注意
没有在git 版本控制中的文件,是不能被git stash 存起来的
基本用法
-
git stash
保存当前的工作空间。但是提交信息是上次commit
的信息。 -
git stash save "save message"
保存;添加备注。 -
git stash list
显示保存进度的列表。也就意味着,git stash
命令可以多次执行。 -
git stash pop
恢复最新保存的工作进度。-
默认会把工作区和暂存区的改动都恢复到工作区。
-
恢复进度后,会删除栈中的当前进度。
-
-
git stash pop --index
恢复最新的进度到工作区和暂存区。(尝试将原来暂存区的改动还恢复到暂存区)。 -
git stash pop stash@{1}
恢复指定的进度到工作区。 -
git stash show stash@{1}
显示具体某一个做了哪些改动。直接show
显示全部。 -
git stash apply
除了不删除恢复的进度之外,其余和git stash pop
命令一样。 -
git stash drop stash@{1}
删除一个存储的进度。如果不指定id,则默认删除最新的存储进度。 -
git stash clear
删除所有存储的进度。