用了以后, 树可以非常清晰, 某种程度上便于追踪, 但是 push --force 就多多了, 不用呢, 合并没有远程仓库被修改的麻烦, 可是追踪又不清晰...
push --force
怎样取舍? 团队里一般怎么取舍?
git rebase是对commit history的改写。当你要改写的commit history还没有被提交到远程repo的时候,也就是说,还没有与他人共享之前,commit history是你私人所有的,那么想怎么改写都可以。
git rebase
而一旦被提交到远程后,这时如果再改写history,那么势必和他人的history长的就不一样了。git push的时候,git会比较commit history,如果不一致,commit动作会被拒绝,唯一的办法就是带上-f参数,强制要求commit,这时git会以committer的history覆写远程repo,从而完成代码的提交。虽然代码提交上去了,但是这样可能会造成别人工作成果的丢失,所以使用-f参数要慎重。
git push
-f
楼主遇到的问题,就是改写了公有的commit history造成的。要解决这个问题,就要从提交流程上做规范。
举个正确流程的栗子:
假设楼主的team中有两个developer:tom和jerry,他们共同使用一个远程repo,并各自clone到自己的机器上,为了简化描述,这里假设只有一个branch:master。
master
这时tom机器的repo有两个branchmaster, origin/master, origin/master 而jerry的机器上也是有两个branchmaster, origin/master, origin/master
origin/master
均如下图所示
tom和jerry分别各自开发自己的新feature,不断有新的commit提交到他们各自私有的commit history中,所以他们的master指针不断的向前推移,分别指向不同的commit。而又由于他们都没有git fetch和git push,所以他们的origin/master都维持不变。
git fetch
jerry的repo如下
tom的repo如下,注意T1和上图的J1,分别是两个不同的commit
T1
J1
这时Tom首先把他的commit提交的远程repo中,那么他本机origin/master指针则会前进,和origin/master指针则会前进,和master指针保持一致,如下
远程repo如下
现在jerry也想把他的commit提交到远程repo上去,运行git push,毫无意外的失败了,所以他git fetch了一下,把远程repo,也就是之前tom提交的T1给拉到了他本机repo中,如下
commit history出现了分叉,要想把tom之前提交的内容包含到自己的工作中来,有一个方法就是git merge,它会自动生成一个commit,既包含tom的提交,也包含jerry的提交,这样就把两个分叉的commit重新又合并在一起。但是这个自动生成的commit会有两个parent,review代码的时候必须要比较两次,很不方便。
git merge
jerry为了保证commit history的线性,决定采用另外一种方法,就是git rebase。jerry的提交J1这时还没有被提交到远程repo上去,也就是他完全私有的一个commit,所以使用git rebase改写J1的history完全没有问题,改写之后,如下
注意J1被改写到T1后面了,变成了J1`
J1`
git push后,本机repo
而远程repo
异常的轻松,一条直线,没有-f
所以,在不用-f的前提下,想维持树的整洁,方法就是:在git push之前,先git fetch,再git rebase的前提下,想维持树的整洁,方法就是:在
git fetch origin master git rebase origin/master git push
除非只有自己一个人用,不然用 push --force 的都该去死。
本地分支和远程分支的绑定(tracking),加上 rebase 策略:
[branch "master"] remote = origin merge = refs/heads/master rebase = true
这样一来,更新代码(pull)的时候就会自动应用 rebase 而不是产生 merge commit,除非有其他情况产生,比如三方合并造成了冲突需要人共去干预。大部分时候还是很聪明的,只要团队里的习惯都良好,那么可以保持一个非常干净漂亮的树形。
pull
其实想让树形结构漂亮些清晰些是有很多办法的,但是首先要取决于团队用的是什么样的 Git Model,对症下药即可。在这里就无法一言以蔽之了。
另外,楼上说得对,慎用 push -f!
push -f
这应该是一个git workflow的问题,我们团队也一直在使用rebase保证commit信息的整洁,但不会用到push -f这样的操作。
关于git workflow就是一个见仁见智的问题了,下面几篇文章你可以看下,再找出一套适合自己团队的就可以了,不过最重要的是要保证团队的每个人都熟悉git,防止犯愚蠢的错误。
如果使用github来团队合作的话,用好pull request,它可以解决push -f这种愚蠢问题!
每个人提交前,都应该把自己的修改rebase到服务器的最新代码之上,遵守这个规则就不会有任何问题。如果你需要force push,说明你做反了,把服务器代码rebase到你本地分支之上才会需要force push,这是错误的用法。
建议参考 Pro Git 中关于 rebase 的章节 http://git-scm.com/book/zh/Git-%E5%88%86%E6%94%AF-%E5%88%86%E6%94%AF%E7%9A%84%E8%A1%8D%E5%90%88
衍合的风险 呃,奇妙的衍合也并非完美无缺,要用它得遵守一条准则: 一旦分支中的提交对象发布到公共仓库,就千万不要对该分支进行衍合操作。 如果你遵循这条金科玉律,就不会出差错。否则,人民群众会仇恨你,你的朋友和家人也会嘲笑你,唾弃你。
衍合的风险 呃,奇妙的衍合也并非完美无缺,要用它得遵守一条准则:
一旦分支中的提交对象发布到公共仓库,就千万不要对该分支进行衍合操作。
如果你遵循这条金科玉律,就不会出差错。否则,人民群众会仇恨你,你的朋友和家人也会嘲笑你,唾弃你。
就我而言。如果 rebase 完后,需要使用 push -f 的话,一定代表该 rebase 操作是不合适。除非你是有意在修改提交历史。
不需要push -f啊,如果分支落后就用pull --rebase
楼上的答案都很正,个人认为,除非某个分支只有你自己搞,你怎么rebase都是没有问题的,但是如果你在master或者develop这种分支上来rebase,估计团队里每个人都想拍死你,尤其是对git不熟悉的队友,手足无措是非常正常的表现。
rebase之后 push -f的情况只有一个,就是题主像我一样有强迫症,害怕电脑宕机、系统崩溃这种蛋疼的事情(悲惨的血泪史),完成一个特性commit之后迅速push到远程只属于你的分支上,每天为了拿到develop的新特性,才在自己的分支上rebase develop,重复执行push的操作,这个个人认为是没有问题的,毕竟你只影响了你自己(而你知道这是对的)。
个人认为, 当你在某个分支进行团队合作的时候, 常用rebase真的是不合理。而且容易出问题。慎用 push --force
git rebase 一般自己一个人开发时使用,用来保持提交记录的整洁。一旦上传到github后,不应该使用git rebase,不然被骂死。
git rebase
是对commit history的改写。当你要改写的commit history还没有被提交到远程repo的时候,也就是说,还没有与他人共享之前,commit history是你私人所有的,那么想怎么改写都可以。而一旦被提交到远程后,这时如果再改写history,那么势必和他人的history长的就不一样了。
git push
的时候,git会比较commit history,如果不一致,commit动作会被拒绝,唯一的办法就是带上-f
参数,强制要求commit,这时git会以committer的history覆写远程repo,从而完成代码的提交。虽然代码提交上去了,但是这样可能会造成别人工作成果的丢失,所以使用-f
参数要慎重。楼主遇到的问题,就是改写了公有的commit history造成的。要解决这个问题,就要从提交流程上做规范。
举个正确流程的栗子:
假设楼主的team中有两个developer:tom和jerry,他们共同使用一个远程repo,并各自clone到自己的机器上,为了简化描述,这里假设只有一个branch:
master
。这时tom机器的repo有两个branch
master
,origin/master
,origin/master
而jerry的机器上也是有两个branch
master
,origin/master
,origin/master
均如下图所示
tom和jerry分别各自开发自己的新feature,不断有新的commit提交到他们各自私有的commit history中,所以他们的master指针不断的向前推移,分别指向不同的commit。而又由于他们都没有
git fetch
和git push
,所以他们的origin/master
都维持不变。jerry的repo如下
tom的repo如下,注意
T1
和上图的J1
,分别是两个不同的commit这时Tom首先把他的commit提交的远程repo中,那么他本机
origin/master
指针则会前进,和origin/master
指针则会前进,和master
指针保持一致,如下远程repo如下
现在jerry也想把他的commit提交到远程repo上去,运行
git push
,毫无意外的失败了,所以他git fetch
了一下,把远程repo,也就是之前tom提交的T1
给拉到了他本机repo中,如下commit history出现了分叉,要想把tom之前提交的内容包含到自己的工作中来,有一个方法就是
git merge
,它会自动生成一个commit,既包含tom的提交,也包含jerry的提交,这样就把两个分叉的commit重新又合并在一起。但是这个自动生成的commit会有两个parent,review代码的时候必须要比较两次,很不方便。jerry为了保证commit history的线性,决定采用另外一种方法,就是
git rebase
。jerry的提交J1
这时还没有被提交到远程repo上去,也就是他完全私有的一个commit,所以使用git rebase
改写J1
的history完全没有问题,改写之后,如下注意
J1
被改写到T1
后面了,变成了J1`
git push
后,本机repo而远程repo
异常的轻松,一条直线,没有
-f
所以,在不用
之前,先-f
的前提下,想维持树的整洁,方法就是:在git push
之前,先git fetch
,再git rebase
的前提下,想维持树的整洁,方法就是:在git fetch
,再git rebase
。除非只有自己一个人用,不然用
push --force
的都该去死。本地分支和远程分支的绑定(tracking),加上 rebase 策略:
这样一来,更新代码(
pull
)的时候就会自动应用 rebase 而不是产生 merge commit,除非有其他情况产生,比如三方合并造成了冲突需要人共去干预。大部分时候还是很聪明的,只要团队里的习惯都良好,那么可以保持一个非常干净漂亮的树形。其实想让树形结构漂亮些清晰些是有很多办法的,但是首先要取决于团队用的是什么样的 Git Model,对症下药即可。在这里就无法一言以蔽之了。
另外,楼上说得对,慎用
push -f
!这应该是一个git workflow的问题,我们团队也一直在使用rebase保证commit信息的整洁,但不会用到
push -f
这样的操作。关于git workflow就是一个见仁见智的问题了,下面几篇文章你可以看下,再找出一套适合自己团队的就可以了,不过最重要的是要保证团队的每个人都熟悉git,防止犯愚蠢的错误。
如果使用github来团队合作的话,用好pull request,它可以解决
push -f
这种愚蠢问题!每个人提交前,都应该把自己的修改rebase到服务器的最新代码之上,遵守这个规则就不会有任何问题。如果你需要force push,说明你做反了,把服务器代码rebase到你本地分支之上才会需要force push,这是错误的用法。
建议参考 Pro Git 中关于 rebase 的章节 http://git-scm.com/book/zh/Git-%E5%88%86%E6%94%AF-%E5%88%86%E6%94%AF%E7%9A%84%E8%A1%8D%E5%90%88
就我而言。如果 rebase 完后,需要使用
push -f
的话,一定代表该 rebase 操作是不合适。除非你是有意在修改提交历史。不需要push -f啊,如果分支落后就用pull --rebase
楼上的答案都很正,个人认为,除非某个分支只有你自己搞,你怎么rebase都是没有问题的,但是如果你在master或者develop这种分支上来rebase,估计团队里每个人都想拍死你,尤其是对git不熟悉的队友,手足无措是非常正常的表现。
rebase之后 push -f的情况只有一个,就是题主像我一样有强迫症,害怕电脑宕机、系统崩溃这种蛋疼的事情(悲惨的血泪史),完成一个特性commit之后迅速push到远程只属于你的分支上,每天为了拿到develop的新特性,才在自己的分支上rebase develop,重复执行push的操作,这个个人认为是没有问题的,毕竟你只影响了你自己(而你知道这是对的)。
个人认为, 当你在某个分支进行团队合作的时候, 常用rebase真的是不合理。而且容易出问题。慎用 push --force
git rebase 一般自己一个人开发时使用,用来保持提交记录的整洁。一旦上传到github后,不应该使用git rebase,不然被骂死。