介绍
git rebase最主要的功能:通过移动某些commit,将当前分支的起始节点重置为目标分支,并且最终
实验
准备2个分支
首先,初始化一个仓库:
mkdir git-rebase
cd git-rebase
git init
# 添加一个空文件
touch a.txt
git add a.txt
# 初始提交
git commit -m "init"
# 创建feat分支
git checkout -b feat
我们将会在这个仓库上建立两个分支:master和feat;仓库中我们建立一个a.txt文件用于测试。
master分支将会对a.txt文件进行两次修改,feat分支也会对a.txt文件进行两次修改。
最终我们会在feat分支上rebase master,看看会有什么结果。
master分支修改a.txt:
git checkout master
echo -ne 'first line by master\nsecond line by master\n' > a.txt
git add a.txt
git commit -m "master add first line and second line"
echo -ne 'FIRST line by master\nsecond LINE by master\nTHIRD LINE by master\n' > a.txt
git add a.txt
git commit -m "master add thrid line, and change letter in first line and second line"
git log:
feat分支修改a.txt:
git checkout feat
echo -ne 'first line by feat\nsecond line by feat\nthird line by feat\n' > a.txt
git add a.txt
git commit -m "feat add first,second and third line"
echo -ne 'first line BY feat\nsecond line BY feat\nthird line BY feat\n' > a.txt
git add a.txt
git commit -m "feat change change letter in first,second and thrid line"
git log:
进行rebase
git checkout feat
git rebase master
我们必须清楚rebase的过程,即先计算两个分支的最近起点,然后将差异的分支依次移动到目标分支上。在这个移动的过程中,目标分支是作为基准的,所以用"us",“ours”, “current"指代;差异分支是作为引入分支,所以用"theirs”,"incoming"指代。
feat的第1个commit
在这里,最近的共同提交是init。因此,差异分支是feat的两次提交,基准则是master的最后一次提交。
因为这两次提交都修改了a.txt文件,所以产生了冲突。我们合并冲突,分别保留部分:
feat的第2个commit
继续合并:
git add a.txt
git rebase --continue
继续合并,feat的第二次提交与刚才的commit也产生了冲突,编辑文件内容,提交后使用git log
查看:
结论
rebase的操作就是将当前分支的差异移动到目标分支上。其结果就是差异分支全都基于目标分支。
在rebase过程中skip掉的那些commit不会影响最终形成的结果。因为,假设中间所有的commit都skip了,最后一个commit结果仍然是以最后一个差异commit来合入基准分支的,因此不会导致结果丢失。
rebase结束后,除了skip掉的commit,所有的commit都会有形成新的提交记录。
–skip
skip,即跳过当前分支,最终rebase的历史记录中不会体现这次提交。
当所有的commit都被skip掉时,最终就是与目标分支相同,等价于:git reset --hard target
rebase v.s. cherry-pick
当除了最后一个commit被合入,其他commit都被skip掉时,其操作等价于cherry-pick。
squash
可以使用git rebase -i COMMIT
来“交互式”的选择需要squash的项:
前面的行列出的是所有的commit,pick表示保留,squash表示将会与其他提交被合并。
git rebase --edit-todo
可以继续编辑这个文件。
使用git merge --squash
可以在merge时将提交合并成一个。