一、Push & Pull
1.1 Git fetch
注:
o/master
即origin/master
Git 远程仓库相当的操作实际可以归纳为两点:向远程仓库传输数据以及从远程仓库获取数据。既然我们能与远程仓库同步,那么就可以分享任何能被 Git 管理的更新(因此可以分享代码、文件、想法、情书等等)。
本节课我们将学习如何从远程仓库获取数据 —— 命令如其名,它就是 git fetch
。
在解释 git fetch
前,我们先看看实例。这里我们有一个远程仓库, 它有两个我们本地仓库中没有的提交。
使用git fetch
后, C2
,C3
被下载到了本地仓库,同时远程分支 o/master
也被更新,反映到了这一变化:
git fetch
完成了仅有的但是很重要的两步:
- 从远程仓库下载本地仓库中缺失的提交记录
- 更新远程分支指针(如
oirgin/master
)
git fetch
并不会改变你本地仓库的状态。它不会更新你的master
分支,也不会修改你磁盘上的文件。
理解这一点很重要,因为许多开发人员误以为执行了 git fetch
以后,他们本地仓库就与远程仓库同步了。它可能已经将进行这一操作所需的所有数据都下载了下来,但是并没有修改你本地的文件。我们在后面的课程中将会讲解能完成该操作的命令 😀
1.2 Git pull
既然我们已经知道了如何用 git fetch
获取远程的数据, 现在我们学习如何将这些变化更新到我们的工作当中。
其实有很多方法的 —— 当远程分支中有新的提交时,你可以像合并本地分支那样来合并远程分支。也就是说就是你可以执行以下命令:
git cherry-pick origin/master
git rebase origin/master
git merge origin/master
- 等等
实际上,由于先抓取更新再合并到本地分支这个流程很常用,因此 Git 提供了一个专门的命令来完成这两个操作。它就是 git pull
。
git pull
===git fetch
+git merge
1.3 Git push
git push
不带任何参数时的行为与 Git 的一个名为 push.default
的配置有关。它的默认值取决于你正使用的 Git 的版本,但是在教程中我们使用的是 upstream
。 这没什么太大的影响,但是在你的项目中进行推送之前,最好检查一下这个配置。
让我们来模拟一下,下图左边是本地分支,右边是远程分支
使用git push
后:
1.4 常见的Git工作流一
现在我们已经知道了如何从其它地方 pull
提交记录,以及如何 push
我们自己的变更。看起来似乎没什么难度,但是为何还会让人们如此困惑呢?困难来自于远程库提交历史的偏离。在讨论这个问题的细节前,我们先来看一个例子……
假设你周一克隆了一个仓库,然后开始研发某个新功能。到周五时,你新功能开发测试完毕,可以发布了。但是 —— 天啊!你的同事这周写了一堆代码,还改了许多你的功能中使用的 API,这些变动会导致你新开发的功能变得不可用。但是他们已经将那些提交推送到远程仓库了,因此你的工作就变成了基于项目旧版的代码,与远程仓库最新的代码不匹配了。
这种情况下, git push
就不知道该如何操作了。如果你执行 git push
,Git 应该让远程仓库回到星期一那天的状态吗?还是直接在新代码的基础上添加你的代码,异或由于你的提交已经过时而直接忽略你的提交?
因为这情况(历史偏离)有许多的不确定性,Git 是不会允许你 push
变更的。实际上它会强制你先合并远程最新的代码,然后才能分享你的工作。
下图所示不可使用
git push
那该如何解决这个问题呢?很简单,你需要做的就是使你的工作基于最新的远程分支。有许多方法做到这一点呢,不过最直接的方法就是通过 rebase
调整你的工作。咱们继续,看看怎么 rebase
!
git fetch
git rebase origin/master
// 上面两步等同于
git pull --rebase origin/master
git push
复制代码
得到的结果如下:
当然,除了rebase
,你当然还可以使用merge
,同样以上面的例子为例:
git fetch
git merge origin/master
// 上面两步等同于
git pull origin/master
git push
复制代码
得到的结果如下:
1.5 rebase 还是 merge?
在开发社区里,有许多关于 merge 与 rebase 的讨论。以下是关于 rebase 的优缺点:
-
优点: Rebase 使你的提交树变得很干净, 所有的提交都在一条线上
-
缺点: Rebase 修改了提交树的历史
比如, 提交 C1 可以被 rebase 到 C3 之后。这看起来 C1 中的工作是在 C3 之后进行的,但实际上是在 C3 之前。
一些开发人员喜欢保留提交历史,因此更偏爱 merge。而其他人可能更喜欢干净的提交树,于是偏爱 rebase。仁者见仁,智者见智。 😀
1.6 常见的Git工作流二
下面是一个更加复杂的例子,如何将图一转化为图二?
解决步骤如下:
git checkout master
git pull --rebase
git checkout side1
git rebase master
git checkout side2
git rebase side1
git checkout side3
git rebase side2
git checkout master
git rebase side3
git push
复制代码
看起来步骤有点多,不要慌,其实我们有更简单的写法:
git fetch
git rebase origin/master side1
git rebase side1 side2
git rebase side2 side3
git rebase side3 master
git push
复制代码
当然,我们也可以使用merge
,不过最终得到的提交时间线是完全不同的喔:
git checkout master
git pull
git merge side1
git merge side2
git merge side3
git push
复制代码