远程仓库操作

git clone

远程仓库:

1607623827355-e6542f72-35d4-4563-ab42-5810d4e01f3c.png1607623872874-e56c181e-3663-49b1-9766-51b5e0ab4e30.png

git clone

远程使用虚线表示

本地仓库多了一个名为 o/master 的分支, 这种类型的分支就叫远程分支。远程分支反映了远程仓库(在你上次和它通信时)的状态。

远程分支有一个特别的属性,在你检出时自动进入分离 HEAD 状态。Git 这么做是出于不能直接在这些分支上进行操作的原因, 你必须在别的地方完成你的工作, (更新了远程分支之后)再用远程分享你的工作成果。

为什么有 o/?

远程分支有一个命名规范 —— 它们的格式是:

<remote name>/<branch name>

因此,如果你看到一个名为 o/master 的分支,那么这个分支就叫 master,远程仓库的名称就是 o。

大多数的开发人员会将它们主要的远程仓库命名为 origin,并不是 o。这是因为当你用 git clone 某个仓库时,Git 已经帮你把远程仓库的名称设置为 origin 了不过 origin 对于我们的 UI 来说太长了,因此不得不使用简写 o 😃 但是要记住, 当你使用真正的 Git 时, 你的远程仓库默认为 origin!

Git 变成了分离 HEAD 状态,当添加新的提交时 o/master 也不会更新。这是因为 o/master 只有在远程仓库中相应的分支更新了以后才会更新。
1607624089312-4332d997-ddd8-4661-a117-64469a7a9f82.png

git checkout o/master
git commit 

git fetch

当我们从远程仓库获取数据时, 远程分支也会更新以反映最新的远程仓库。

1607624469876-18730c11-af93-4d5f-add5-c8abda706a75.png1607624487480-c179e1f7-e3ce-444f-9861-948545bd1be1.png

解释 git fetch 前,我们先看看实例。这里我们有一个远程仓库, 它有两个我们本地仓库中没有的提交。

C2,C3 被下载到了本地仓库,同时远程分支 o/master 也被更新,反映到了这一变化

git fetch 完成了仅有的但是很重要的两步:

  • 从远程仓库下载本地仓库中缺失的提交记录
  • 更新远程分支指针(如 o/master)

git fetch 通常通过互联网(使用 http:// 或 git:// 协议) 与远程仓库通信。

git fetch 并不会改变你本地仓库的状态。它不会更新你的 master 分支,也不会修改你磁盘上的文件。

所以, 你可以将 git fetch 的理解为单纯的下载操作。

git fetch 参数

git fetch 的参数和 git push 极其相似。他们的概念是相同的,只是方向相反罢了(因为现在你是下载,而非上传)

git fetch origin foo

1607628977586-2339c8d2-d9fb-4c62-862e-5b873d14d1a2.png1607628999882-957d6150-81c6-42b6-8fe9-265f48f0a814.png

我们只下载了远程仓库中 foo 分支中的最新提交记录,并更新了 o/foo

可能在 foo 分支上的工作还未完成,你也不想弄乱它。

source 现在指的是远程仓库中的位置,而 才是要放置提交的本地仓库的位置。它与 git push 刚好相反,这是可以讲的通的,因为我们在往相反的方向传送数据。

1607629198166-3339687b-55e4-4876-8049-a3a3e0350148.png1607629244402-de05c157-618c-413d-bf0b-00492b71d22d.png

git fetch foo~1:bar

Git 将 foo~1 解析成一个 origin 仓库的位置,然后将那些提交记录下载到了本地的 bar 分支(一个本地分支)上。注意由于我们指定了目标分支,foo 和 o/foo 都没有被更新。

如果执行命令前目标分支不存在会怎样呢?我们看一下上个对话框中没有 bar 分支的情况。跟 git push 一样,Git 会在 fetch 前自己创建立本地分支, 就像是 Git 在 push 时,如果远程仓库中不存在目标分支,会自己在建立一样。

1607629291649-f586aaa3-1c45-4ba1-9187-4d11a9e577d9.png1607629338511-5155c919-5758-4f5e-bec6-f6f943163880.png

如果 git fetch 没有参数,它会下载所有的提交记录到各个远程分支……

1607629372915-f2745322-da09-43ad-96b3-0339c0172e94.png1607629404651-9ee02d07-d84b-4ee2-b962-3312d50cc3d1.png

1607629471990-04bdb523-4f31-4591-8b53-83dab576683c.png1607629481834-fe068840-5d43-46ca-859c-54216d6a94a3.png

git fetch origin master~1:foo
git fetch origin foo:master
git checkout foo
git merge master

古怪的

Git 有两种关于 的用法是比较诡异的,即你可以在 git push 或 git fetch 时不指定任何 source,方法就是仅保留冒号和 destination 部分,source 部分留空。

git push origin :side

git fetch origin :bugFix

通过给 push 传空值 source,成功删除了远程仓库中的 foo 分支

1607630401507-72661321-77dc-44ac-8c55-09763f113761.png1607630414065-01791a0c-f190-4561-bca1-8c85afb2c821.png

git push origin :foo

如果 fetch 空 到本地,会在本地创建一个新分支。

1607630490021-82d9cdcb-91da-456c-9b54-9091c2d4e8f0.png1607630501274-38e394ef-f4b5-4242-8de2-7e98af6df97c.png

git fetch origin :bar

git pull

当远程分支中有新的提交时,你可以像合并本地分支那样来合并远程分支。

git cherry-pick o/master

git rebase o/master

git merge o/master

实际上,由于先抓取更新再合并到本地分支这个流程很常用,因此 Git 提供了一个专门的命令来完成这两个操作。它就是我们要讲的 git pull。

1607624735207-266e46a1-9e5b-47ef-846d-594fc99b0eec.png1607624787353-afc421d0-1ada-47b3-8f20-34bcfaa4f600.png

先来看看 fetch、merge 依次执行的效果

我们用 fetch 下载了 C3, 然后通过 git merge o/master 合并了这一提交记录。现在我们的 master 分支包含了远程仓库中的更新(在本例中远程仓库名为 origin)

等价于git pull

git pull参数

git pull 到头来就是 fetch 后跟 merge 的缩写。你可以理解为用同样的参数执行 git fetch,然后再 merge 你所抓取到的提交记录。

以下命令在 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 实际上就是 fetch + merge 的缩写, git pull 唯一关注的是提交最终合并到哪里(也就是为 git fetch 所提供的 destination 参数)

git merge origin master

1607629807359-12008da0-e1e9-4f63-9f7a-cf319d20458b.png1607629835535-b8eafe10-4bda-47b8-b8a3-9af3ed4e9191.png

通过指定 master 我们更新了 o/master。然后将 o/master merge 到我们的检出位置,无论我们当前检出的位置是哪。

案例二:merge到本地不存在的分支

1607629905306-70f2fc6d-53c9-4234-948c-6f4fd605f066.png1607629921017-04037e17-2532-4bfc-8339-5d4daaa796cc.png

git pull origin master:foo

它先在本地创建了一个叫 foo 的分支,从远程仓库中的 master 分支中下载提交记录,并合并到 foo,然后再 merge 到我们的当前检出的分支 bar 上。

案例三:

1607630009733-d09ed12f-2ffe-428a-884b-88440804045b.png1607630024830-15becc3b-e4e2-4ec6-897d-f170f0101efd.png

git pull origin bar:foo
git pull origin master:side

git push

git push 不带任何参数时的行为与 Git 的一个名为 push.default 的配置有关。它的默认值取决于你正使用的 Git 的版本,

1607625172660-2b1bdf2a-8239-4413-b61d-451b44748f64.png1607625184052-80220ced-efd1-4092-8d3d-38f572ae03bc.png

git push

git push <remote> <place>

Git 是通过当前检出分支的属性来确定远程仓库以及要 push 的目的地的。这是未指定参数时的行为,我们可以为 push 指定参数,语法是:

git push <remote> <place>

git push origin master

1607628065312-ee18181e-2c4d-4ff4-b516-e5022a5a765c.png1607628081329-413951d1-bd29-4834-88f2-9624a8c3f525.png

git checkout c0
git push origin master

分离头指针的时候直接push不会有任何的提交

如果不指定master:

git checkout c0
git push 

命令失败了(正如你看到的,什么也没有发生)! 因为我们所检出的 HEAD 没有跟踪任何分支。

1626309768177-92c0a93e-3289-41a5-baf5-c18e39496f16.png1626309761241-daf33a39-cb6f-4efe-bcde-0a0a2f14a95e.png  1607628256101-c025a4e5-66f0-4701-9b82-ffaa450c6c26.png

git push origin master
git push origin foo

git push origin <source>:<destination>

当为 git push 指定 place 参数为 master 时,我们同时指定了提交记录的来源和去向。

你可能想问 —— 如果来源和去向分支的名称不同呢?比如你想把本地的 foo 分支推送到远程仓库中的 bar 分支。

要同时为源和目的地指定 的话,只需要用冒号 : 将二者连起来就可以了:

这个参数实际的值是个 refspec,“refspec” 是一个自造的词,意思是 Git 能识别的位置(比如分支 foo 或者 HEAD~1)1607628437377-c9994d70-4566-4b45-a62a-1a18ccfd19a5.png1607628484700-4214c20c-f29f-4d37-b2e0-5efa306f4bfc.png

source 可以是任何 Git 能识别的位置

git push origin foo^:master

但是它确实是可以运行的 —— Git 将 foo^ 解析为一个位置,上传所有未被包含到远程仓库里 master 分支中的提交记录。

如果你要推送到的目的分支不存在会怎么样呢?没问题!Git 会在远程仓库中根据你提供的名称帮你创建这个分支!

1607628580422-c54e9939-f3c9-4150-a8f9-3503a57951e8.png1607628596698-aee99506-13fa-41d9-82f5-f50a24c4a21f.png

git push origin master:newBranch

1607628665400-b73d8cde-1a20-467a-afca-a99b194d5157.png1607628676023-41c92299-7af4-4c76-9ae2-b61878eda3fe.png

git push origin foo:master
git push origin master^:foo

历史偏离

1607625350497-0c5aee87-4cbf-4baa-bc5e-399e026da2d3.png

因为这情况(历史偏离)有许多的不确定性,Git 是不会允许你 push 变更的。

你需要做的就是使你的工作基于最新的远程分支。

有许多方法做到这一点呢,不过最直接的方法就是通过 rebase 调整你的工作

我们用 git fetch 更新了本地仓库中的远程分支,然后用 rebase 将我们的工作移动到最新的提交记录下,最后再用 git push 推送到远程仓库。

git fetch
git rebase o/master
git push

变基的时候是需要解决冲突的,

1607625504595-a3c59b66-bf3c-4d0b-8788-477e6989c78b.png

尽管 git merge 不会移动你的工作(它会创建新的合并提交),但是它会告诉 Git 你已经合并了远程仓库的所有变更。这是因为远程分支现在是你本地分支的祖先,也就是说你的提交已经包含了远程分支的所有变化。

git fetch
git merge o/master
git push

1607625715933-9c858ebc-d92f-421d-986f-3a014352af7f.png

当然 —— 前面已经介绍过 git pull 就是 fetch 和 merge 的简写,类似的 git pull --rebase 就是 fetch 和 rebase 的简写!

git pull
git pull --rebase
git push

git fetch
git merge
git push

git fetch
git rebase
git push

由 fetch、rebase/merge 和 push 组成的工作流很普遍。

远程服务器拒绝!(Remote Rejected)

如果你是在一个大的合作团队中工作, 很可能是master被锁定了, 需要一些Pull Request流程来合并修改。如果你直接提交(commit)到本地master, 然后试图推送(push)修改, 你将会收到这样类似的信息:

! [远程服务器拒绝] master -> master (TF402455: 不允许推送(push)这个分支; 你必须使用pull request来更新这个分支.)

远程服务器拒绝直接推送(push)提交到master, 因为策略配置要求 pull requests 来提交更新.你应该按照流程,新建一个分支, 推送(push)这个分支并申请pull request,但是你忘记并直接提交给了master.现在你卡住并且无法推送你的更新.

新建一个分支feature, 推送到远程服务器. 然后reset你的master分支和远程服务器保持一致, 否则下次你pull并且他人的提交和你冲突的时候就会有问题.

1607626096939-1ca2ca7f-eb93-447b-98c6-805ee313da04.png1607626104810-67341f90-6056-424f-97eb-eca36deb1b3d.png

git reset --hard o/master
git checkout -b feature c2
git push origin feature

合并特性分支

这里共有三个特性分支 —— side1 side2 和 side3

我需要将这三分支按顺序推送到远程仓库

因为远程仓库已经被更新过了,所以我们还要把那些工作合并过来

1607626456781-e710ebb4-8d87-41b4-8217-7753dd6ef5b1.png1607626468621-abfb7e62-5eff-4d01-940f-e33f19001b15.png

git fetch
git rebase c8 side1
git rebase side1 side2
git rebase side2 side3
git rebase side3 master 
git push

为什么不用 merge 呢?

优点:Rebase 使你的提交树变得很干净, 所有的提交都在一条线上

缺点:Rebase 修改了提交树的历史

1607626786014-7c360f19-19a4-42d1-ac34-8532ce5a4fc3.png

git checkout master
git pull
git merge side1
git merge side2
git merge side3
git push

1607626997758-7b02ec10-dcf3-4094-88cc-c9e7e134a748.png

远程跟踪分支

master 和 o/master 的关联关系就是由分支的“remote tracking”属性决定的。master 被设定为跟踪 o/master,当你克隆仓库的时候, Git 就自动帮你把这个属性设置好了。当你克隆时, Git 会为远程仓库中的每个分支在本地仓库中创建一个远程分支(比如 o/master)。然后再创建一个跟踪远程仓库中活动分支的本地分支,默认情况下这个本地分支会被命名为 master。

可以让任意分支跟踪 o/master, 然后该分支会像 master 分支一样得到隐含的 push 目的地以及 merge 的目标。 这意味着你可以在分支 totallyNotMaster 上执行 git push,将工作推送到远程仓库的 master 分支上。

通过远程分支checkout一个新分支

有两种方法设置这个属性,第一种就是通过远程分支检出一个新的分支,执行:

**git checkout -b totallyNotMaster o/master**

就可以创建一个名为 totallyNotMaster 的分支,它跟踪远程分支 o/master。

1607627292951-8629fc84-35b0-4077-a67f-63d47ffff070.png1607627351412-d5824d97-5c8b-4cef-95eb-0bf859f9d574.png

我们检出一个名叫 foo 的新分支,让其跟踪远程仓库中的 master

git checkout -f foo o/master
git pull

我们使用了隐含的目标 o/master 来更新 foo 分支。需要注意的是 master 并未被更新!

1607627394380-749920ca-d3ae-4252-979f-b9701ff2b4a9.png1607627426473-11a11af3-a5c5-4ff9-86f8-0be520a2fe4e.png

git checkout -b foo o/master
git push

我们将一个并不叫 master 的分支上的工作推送到了远程仓库中的 master 分支上

设置远程追踪分支

另一种设置远程追踪分支的方法就是使用:git branch -u 命令,执行:

git branch -u o/master foo

这样 foo 就会跟踪 o/master 了。如果当前就在 foo 分支上, 还可以省略 foo:

git branch -u o/master

1607627607870-76115ef4-7bf7-4757-a5e9-52079c5bdd6c.png1607627629468-11d066e3-507f-49f1-8dc3-d0689b76dd56.png

git branch -u o/master foo
git commit
git pull --rebase
git push

posted on 2025-10-14 22:34  chuchengzhi  阅读(5)  评论(0)    收藏  举报

导航

杭州技术博主,专注分享云计算领域实战经验、技术教程与行业洞察, 打造聚焦云计算技术的垂直博客,助力开发者快速掌握云服务核心能力。

褚成志 云计算 技术博客