git clone
克隆远程仓库到本地
git clone 发生了什么
- 你可能注意到的第一个事就是在我们的本地仓库多了一个名为
o/main
的分支, 这种类型的分支就叫远程分支。由于远程分支的特性导致其拥有一些特殊属性。 - 远程分支反映了远程仓库(在你上次和它通信时)的状态。这会有助于你理解本地的工作与公共工作的差别 —— 这是你与别人分享工作成果前至关重要的一步.
- 远程分支有一个特别的属性,在你检出时自动进入分离 HEAD 状态。Git 这么做是出于不能直接在这些分支上进行操作的原因, 你必须在别的地方完成你的工作, (更新了远程分支之后)再用远程分享你的工作成果。
为什么有 o/
?
你可能想问这些远程分支的前面的 o/
是什么意思呢?好吧, 远程分支有一个命名规范 —— 它们的格式是:
<remote name>/<branch name>
因此,如果你看到一个名为 o/main
的分支,那么这个分支就叫 main
,远程仓库的名称就是 o
。
大多数的开发人员会将它们主要的远程仓库命名为 origin
,并不是 o
。这是因为当你用 git clone
某个仓库时,Git 已经帮你把远程仓库的名称设置为 origin
了
不过 origin
对于我们的 UI 来说太长了,因此不得不使用简写 o
:) 但是要记住, 当你使用真正的 Git 时, 你的远程仓库默认为 origin
!
git fetch
Git 远程仓库相当的操作实际可以归纳为两点:向远程仓库传输数据以及从远程仓库获取数据。既然我们能与远程仓库同步,那么就可以分享任何能被 Git 管理的更新
git fetch表示从远程仓库获取数据
看下面的例子
C2
,C3
被下载到了本地仓库,同时远程分支 o/main
也被更新,反映到了这一变化
git fetch 做了些什么
git fetch
完成了仅有的但是很重要的两步:
- 从远程仓库下载本地仓库中缺失的提交记录
- 更新远程分支指针(如
o/main
)
git fetch
实际上将本地仓库中的远程分支更新成了远程仓库相应分支最新的状态。
git fetch 不会做的事
git fetch
并不会改变你本地仓库的状态。它不会更新你的 main
分支,也不会修改你磁盘上的文件。
理解这一点很重要,因为许多开发人员误以为执行了 git fetch
以后,他们本地仓库就与远程仓库同步了。它可能已经将进行这一操作所需的所有数据都下载了下来,但是并没有修改你本地的文件
当远程分支中有新的提交时,你可以像合并本地分支那样来合并远程分支。也就是说就是你可以执行以下命令:
git cherry-pick o/main
git rebase o/main
git merge o/main
等等.....
git pull
由于先抓取更新再合并到本地分支这个流程很常用,因此 Git 提供了一个专门的命令---git pull来完成这两个操作
- git pull 等于 git fecth; git merge ,
- git pull --rebase 等于 git fecth ; git rebase
git push
负责将你的变更上传到指定的远程仓库,并在远程仓库上合并你的新提交记录。一旦 git push
完成, 你的朋友们就可以从这个远程仓库下载你分享的成果了!
你可以将 git push
想象成发布你成果的命令。它有许多应用技巧
偏离的工作
假设你周一克隆了一个仓库,然后开始研发某个新功能。到周五时,你新功能开发测试完毕,可以发布了。但是 —— 天啊!你的同事这周写了一堆代码,还改了许多你的功能中使用的 API,这些变动会导致你新开发的功能变得不可用。但是他们已经将那些提交推送到远程仓库了,因此你的工作就变成了基于项目旧版的代码,与远程仓库最新的代码不匹配了。
这种情况下, git push
就不知道该如何操作了。如果你执行 git push
,Git 应该让远程仓库回到星期一那天的状态吗?还是直接在新代码的基础上添加你的代码,亦或由于你的提交已经过时而直接忽略你的提交?
因为这情况(历史偏离)有许多的不确定性,Git 是不会允许你 push
变更的。实际上它会强制你先合并远程最新的代码,然后才能分享你的工作
可以使用git pull --rebase得到远程最新的代码,再用git push 提交你的本地代码
远程服务器拒绝!(Remote Rejected)
如果你是在一个大的合作团队中工作, 很可能是master被锁定了, 需要一些Pull Request流程来合并修改。如果你直接提交(commit)到本地master, 然后试图推送(push)修改, 你将会收到这样类似的信息:
! [远程服务器拒绝] main -> main (TF402455: 不允许推送(push)这个分支; 你必须使用pull request来更新这个分支.)
为什么会被拒绝?
远程服务器拒绝直接推送(push)提交到master, 因为策略配置要求 pull requests 来提交更新.
你应该按照流程,新建一个分支, 推送(push)这个分支并申请pull request,但是你忘记并直接提交给了master.现在你卡住并且无法推送你的更新.
- git pull --rebase
远程跟踪分支
main
和 o/main
的关联关系就是由分支的“remote tracking”属性决定的。main
被设定为跟踪 o/main
—— 这意味着为 main
分支指定了推送的目的地以及拉取后合并的目标。
你可能想知道 main
分支上这个属性是怎么被设定的,你并没有用任何命令指定过这个属性呀!好吧, 当你克隆仓库的时候, Git 就自动帮你把这个属性设置好了。
当你克隆时, Git 会为远程仓库中的每个分支在本地仓库中创建一个远程分支(比如 o/main
)。然后再创建一个跟踪远程仓库中活动分支的本地分支,默认情况下这个本地分支会被命名为 main
。
克隆完成后,你会得到一个本地分支(如果没有这个本地分支的话,你的目录就是“空白”的),但是可以查看远程仓库中所有的分支。
我能自己指定remote tracking这个属性吗?
当然可以啦!你可以让任意分支跟踪 o/main
, 然后该分支会像 main
分支一样得到隐含的 push 目的地以及 merge 的目标。 这意味着你可以在分支 totallyNotMain
上执行 git push
,将工作推送到远程仓库的 main
分支上。
有两种方法设置这个属性,第一种就是通过远程分支检出一个新的分支,执行:
git checkout -b foo o/main
就可以创建一个名为 foo 的分支,它跟踪远程分支 o/main
。
但是git pull 之后,本地main 并未被更新
git push 同样适用
- git checkout foo o/main
- git commit
- git push
另一种设置远程追踪分支的方法就是使用:git branch -u
命令
git branch -u o/main foo 还可以省略 foo
git branch -u o/main
Git Push 的参数
- git push origin main
把这个命令翻译过来就是:
切到本地仓库中的“main”分支,获取所有的提交,再到远程仓库“origin”中找到“main”分支,将远程仓库中没有的提交记录都添加上去,搞定之后告诉我。
如果想把本地的 foo
分支推送到远程仓库中的 bar
分支。该怎么做呢?
只需要用冒号 :
将二者连起来就可以了:
git push origin <source>:<destination>
git push origin foo^ :main :表示提交foo的上一个记录到远程的main分支上
- git push origin main : newBranch :表示提交所有main所有记录到远程的newBranch分支上(如果远程没有newBranch这个分支,Git 会在远程仓库中根据你提供的名称帮你创建这个分支)
Git fetch 的参数
git fetch
的参数和 git push
极其相似。他们的概念是相同的,只是方向相反,因为git fetch 是下载
如果你觉得直接更新本地分支很爽,那你就用冒号分隔,不过,你不能在当前检出的分支上干这个事,但是其它分支是可以的。
- git fetch origin foo:Git 会到远程仓库的
foo
分支上,然后获取所有本地不存在的提交,放到本地的o/foo
上 - git fetch origin foo~1:bar:表示下载远程的foo的上一个记录到本地的bar分支
如果执行命令前目标分支不存在会怎样呢 ,跟 git push 一样,Git 会在 fetch 前自己创建立本地分支
如果 git fetch
没有参数,它会下载所有的提交记录到各个本地的远程分支……
古怪的 <source>
Git 有两种关于 <source>
的用法比较诡异
git push如果<source>为空,会删除远程的分支
git push origin :foo 删除远程的foo分支
但是fetch<source>为空,会在本地创建一个新的分支
git fetch origin :foo 在本地创建foo分支
Git pull 参数
以下命令在 Git 中是等效的:
git pull origin foo
相当于:
git fetch origin foo; git merge o/foo
还有...
git pull origin bar~1:bugFix
相当于:
git fetch origin bar~1:bugFix; git merge bugFix
git pull 唯一关注的是提交最终合并到哪里
git checkout bar
git pull origin main:通过指定 main
我们更新了 o/main
。然后将 o/main
merge 到我们的检出的bar位置
那git pull origin <source>:<destination>是什么意思呢?
git pull origin main:foo: 它先在本地创建了一个叫 foo
的分支,从远程仓库中的 main 分支中下载提交记录,并合并到 foo
,然后再 merge 到我们的当前检出的分支 bar
上