问题
遇到什么问题?
之前日常操作都是用git pull来获取远程代码并且自动合并,如果和远程代码有分叉的话,就会出现一些奇怪的提交,如下面的提交记录:
geesun@geesun-OptiPlex-3010:~/test/src/local$ git log
commit 0c2df7467d30c46ee4884eb170cf4a521918e7d3
Merge: 2ce37bb 236e1d3
Author: Geesun Xu <geesun@gmail.com>
Date: Thu Sep 24 14:19:49 2015 +0800
Merge branch 'master' of /home/geesun/test/remote
很多情况下,我们并不希望有这样的自动合并产生,因为他产生了一个自动提交,会让版本变得交叉,不清晰。
问题如何产生?
Step 1:本地代码从远程服务器的同步完成后又提交了B,这是本地代码提交记录:
geesun@geesun-OptiPlex-3010:~/test/src/local$ git log
commit 2ce37bbfd25d5e0265cda4949b16f0110a4f45e7
Author: Geesun Xu <geesun@gmail.com>
Date: Thu Sep 24 14:18:40 2015 +0800
Check in by B
commit 9d34ed98d4478fa52424498940c962f9b0d921e2
Author: Geesun Xu <geesun@gmail.com>
Date: Thu Sep 24 14:17:02 2015 +0800
base
Step 2: 远程服务后来又被另外一个人提交了A,远程服务器的提交记录如下:
geesun@geesun-OptiPlex-3010:~/test/src/remote$ git log
commit 236e1d369761e180e4928a717c180d5cb776ba02
Author: Geesun Xu <geesun@gmail.com>
Date: Thu Sep 24 14:18:19 2015 +0800
Check in by A
commit 9d34ed98d4478fa52424498940c962f9b0d921e2
Author: Geesun Xu <geesun@gmail.com>
Date: Thu Sep 24 14:17:02 2015 +0800
base
Step 3:这个时候我要在本地push代码到远程服务器,会被reject,必须先同步才能push,就用git pull同步,操作结果如下:
geesun@geesun-OptiPlex-3010:~/test/src/local$ git pull
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
来自 /home/geesun/test/remote
9d34ed9..236e1d3 master -> origin/master
Merge made by the 'recursive' strategy.
local1 | 1 +
1 file changed, 1 insertion(+)
create mode 100644 local1
geesun@geesun-OptiPlex-3010:~/test/src/local$ git log
commit 0c2df7467d30c46ee4884eb170cf4a521918e7d3
Merge: 2ce37bb 236e1d3
Author: Geesun Xu <geesun@gmail.com>
Date: Thu Sep 24 14:19:49 2015 +0800
Merge branch 'master' of /home/geesun/test/remote
commit 2ce37bbfd25d5e0265cda4949b16f0110a4f45e7
Author: Geesun Xu <geesun@gmail.com>
Date: Thu Sep 24 14:18:40 2015 +0800
Check in by B
commit 236e1d369761e180e4928a717c180d5cb776ba02
Author: Geesun Xu <geesun@gmail.com>
Date: Thu Sep 24 14:18:19 2015 +0800
Check in by A
commit 9d34ed98d4478fa52424498940c962f9b0d921e2
Author: Geesun Xu <geesun@gmail.com>
Date: Thu Sep 24 14:17:02 2015 +0800
base
这个时候别人在从远程服务器同步代码之后,都会看到开始那条本不该存在的提交记录.
如何解决这个问题
在本地代码推送到远程服务器之前,对本地代码进行rebase即可,操作记录:
geesun@geesun-OptiPlex-3010:~/test/src/local$ git pull -r
首先,重置头指针以便在上面重放您的工作...
正应用:check in by B
geesun@geesun-OptiPlex-3010:~/test/src/local$ git log
commit 2ce37bbfd25d5e0265cda4949b16f0110a4f45e7
Author: Geesun Xu <geesun@gmail.com>
Date: Thu Sep 24 14:18:40 2015 +0800
check in by B
commit 236e1d369761e180e4928a717c180d5cb776ba02
Author: Geesun Xu <geesun@gmail.com>
Date: Thu Sep 24 14:18:19 2015 +0800
Check in by A
commit 9d34ed98d4478fa52424498940c962f9b0d921e2
Author: Geesun Xu <geesun@gmail.com>
Date: Thu Sep 24 14:17:02 2015 +0800
base check in
什么是Fast-Forward
要理解rebase,就先要搞清楚Fast-Forward的概念. 如果本地代码在跟远程同步之后,本地没有新的提交,而远程代码有新的提交,这就是说本地代码和远程的代码没有出现分叉,只不过是本地代码指向比较旧的版本而已.这种情况我们就称为本地代码可以做Fast-Forward.
如下:
BASE--->A <--- [Local master]
\
\
B <--- [Remote master]
操作记录:
远端:
geesun@geesun-OptiPlex-3010:~/test/src/remote$ git log
commit 257d73fdbbfbaaaac18a398e46bf296cc01436ca
Author: Geesun Xu <geesun@gmail.com>
Date: Thu Sep 24 16:00:40 2015 +0800
check in by B
commit 236e1d369761e180e4928a717c180d5cb776ba02
Author: Geesun Xu <geesun@gmail.com>
Date: Thu Sep 24 14:18:19 2015 +0800
check in by A
commit 9d34ed98d4478fa52424498940c962f9b0d921e2
Author: Geesun Xu <geesun@gmail.com>
Date: Thu Sep 24 14:17:02 2015 +0800
base
本地:
geesun@geesun-OptiPlex-3010:~/test/src/local$ git log
commit 236e1d369761e180e4928a717c180d5cb776ba02
Author: Geesun Xu <geesun@gmail.com>
Date: Thu Sep 24 14:18:19 2015 +0800
check in by A
commit 9d34ed98d4478fa52424498940c962f9b0d921e2
Author: Geesun Xu <geesun@gmail.com>
Date: Thu Sep 24 14:17:02 2015 +0800
base
geesun@geesun-OptiPlex-3010:~/test/src/local$ git pull
更新 236e1d3..257d73f
Fast-forward
local2 | 1 +
1 file changed, 1 insertion(+)
create mode 100644 local2
geesun@geesun-OptiPlex-3010:~/test/src/local$ git log
commit 257d73fdbbfbaaaac18a398e46bf296cc01436ca
Author: Geesun Xu <geesun@gmail.com>
Date: Thu Sep 24 16:00:40 2015 +0800
check in by B
commit 236e1d369761e180e4928a717c180d5cb776ba02
Author: Geesun Xu <geesun@gmail.com>
Date: Thu Sep 24 14:18:19 2015 +0800
check in by A
commit 9d34ed98d4478fa52424498940c962f9b0d921e2
Author: Geesun Xu <geesun@gmail.com>
Date: Thu Sep 24 14:17:02 2015 +0800
base
这里的git pull相当于 git fetch和git merge origin/master的组合.
因为这种情况是可以做fast-forward,所以git merge就相当于只是做了fast-forward而已.
开始问题描述的就是一种不可以做fast-forward的情况,如果再用git merge则就会产生一个自动提交,如开始问题描述的一样.
有些时候我们希望能fast-forward就fast-forward,否则我们用后面会讲的rebase命令合并。可以这样:
或者
git fetch
git merge --ff-only origin/master
如果不能fast-forward, merge 操作会终止。
什么是Rebase
Rebase就是同步本地和远程的分支,使它们的base一样,然再应用本地没有推送的提交.
git rebase是时做了如下三件事情:
- 把本地未推送到远端的所有提交,放到暂存区。
- 然后把本地的指针指向远端的最新提交,也就使它和远端的提交一样.
- 然后将暂存区的提交挨个挨个提交到本分支. 如果出现冲突,会需要手工merge.
远端提交记录:
geesun@geesun-OptiPlex-3010:~/test/src/remote$ git log
commit 0ab394dc2ac5a4d186eb9be86da4c72987d3023a
Author: Geesun Xu <geesun@gmail.com>
Date: Thu Sep 24 16:37:37 2015 +0800
Check in by A
commit 88e8afd5519fe8eaf7b9e3bb04635567d83eeeee
Author: Geesun Xu <geesun@gmail.com>
Date: Thu Sep 24 16:36:30 2015 +0800
base
本地提交记录:
geesun@geesun-OptiPlex-3010:~/test/src/local$ git log
commit df94a3c6cf05c63a7b9db6fd753ef70b1d9a40df
Author: Geesun Xu <geesun@gmail.com>
Date: Thu Sep 24 16:45:21 2015 +0800
Check in by B
commit 88e8afd5519fe8eaf7b9e3bb04635567d83eeeee
Author: Geesun Xu <geesun@gmail.com>
Date: Thu Sep 24 16:36:30 2015 +0800
base
对本地进行rebase:
geesun@geesun-OptiPlex-3010:~/test/src/local$ git pull -r
首先,重置头指针以便在上面重放您的工作...
正应用:Check in by B
geesun@geesun-OptiPlex-3010:~/test/src/local$ git log
commit 59e14ba0bb2e55401ca0b2d8c309f520843e8144
Author: Geesun Xu <geesun@gmail.com>
Date: Thu Sep 24 16:45:21 2015 +0800
Check in by B
commit 0ab394dc2ac5a4d186eb9be86da4c72987d3023a
Author: Geesun Xu <geesun@gmail.com>
Date: Thu Sep 24 16:37:37 2015 +0800
Check in by A
commit 88e8afd5519fe8eaf7b9e3bb04635567d83eeeee
Author: Geesun Xu <geesun@gmail.com>
Date: Thu Sep 24 16:36:30 2015 +0800
base
Rebase后的结果也许是我们希望的结果,而不是开始问题描述的那样.
这里的git pull -r 相当于 git fetch和git rebase的组合.
如果rebase出现冲突,会需要手工merge
手工merge操作日志:
geesun@geesun-OptiPlex-3010:~/test/src/local$ git log
commit e6bb6173b626f8ef599b86516a0e35e4cbb7bc5a
Author: Geesun Xu <geesun@gmail.com>
Date: Thu Sep 24 16:51:02 2015 +0800
Check in by B
commit 88e8afd5519fe8eaf7b9e3bb04635567d83eeeee
Author: Geesun Xu <geesun@gmail.com>
Date: Thu Sep 24 16:36:30 2015 +0800
base
geesun@geesun-OptiPlex-3010:~/test/src/local$ git rebase
首先,重置头指针以便在上面重放您的工作...
正应用:Check in by B
使用索引来重建一个(三路合并的)基础目录树...
转而在基础版本上打补丁及进行三路合并...
自动合并 test.txt
冲突(添加/添加):合并冲突于 test.txt
无法合并变更。
补丁失败于 0001 Check in by B
失败的补丁文件副本位于:
/home/geesun/test/src/local/.git/rebase-apply/patch
当您解决了此问题后,执行 "git rebase --continue"。
如果您想跳过此补丁,则执行 "git rebase --skip"。
要恢复原分支并停止变基,执行 "git rebase --abort"。
geesun@geesun-OptiPlex-3010:~/test/src/local$ vi test.txt
geesun@geesun-OptiPlex-3010:~/test/src/local$ git add test.txt
geesun@geesun-OptiPlex-3010:~/test/src/local$ git rebase --continue
正应用:Check in by B
geesun@geesun-OptiPlex-3010:~/test/src/local$ git log
commit 0a68c730ee96d4dd37d1dd97f52a5fcb4be383e4
Author: Geesun Xu <geesun@gmail.com>
Date: Thu Sep 24 16:51:02 2015 +0800
Check in by B
commit 0ab394dc2ac5a4d186eb9be86da4c72987d3023a
Author: Geesun Xu <geesun@gmail.com>
Date: Thu Sep 24 16:37:37 2015 +0800
Check in by A
commit 88e8afd5519fe8eaf7b9e3bb04635567d83eeeee
Author: Geesun Xu <geesun@gmail.com>
Date: Thu Sep 24 16:36:30 2015 +0800
base
不同分支之间也可以做rebase,应该不太常用,就不做例子了.
总结
在本地很远端不能做fast-forword的情况下,是采用git merge 还是采用git rebase要看项目的要求. 如果想保持完整的提交记录,可以考虑用git merge. 要想保持清晰的提交记录,采用git rebase会比较好.
git pull默认的行为是merge,如果要更改这种默认行为为rebase,可以用
git config --global branch.autosetuprebase always
comments