Learn Git Branching

141 阅读27分钟

一个很好锻炼Git语法的平台 learngitbranching.js.org/?locale=zh_…

image.png

www.ruanyifeng.com/blog/2015/1…

基础篇

Git Commit

教学

Git 仓库中的提交记录保存的是你的目录下所有文件的快照,就像是把整个目录复制,然后再粘贴一样,但比复制粘贴优雅许多!

Git 希望提交记录尽可能地轻量,因此在你每次进行提交时,它并不会盲目地复制整个目录。条件允许的情况下,它会将当前版本与仓库中的上一个版本进行对比,并把所有的差异打包到一起作为一个提交记录。

Git 还保存了提交的历史记录。这也是为什么大多数提交记录的上面都有父节点的原因 —— 我们会在图示中用箭头来表示这种关系。对于项目组的成员来说,维护提交历史对大家都有好处。

关于提交记录太深入的东西咱们就不再继续探讨了,现在你可以把提交记录看作是项目的快照。提交记录非常轻量,可以快速地在这些提交记录之间切换

演示

使用 git commit 创建一条新的提交记录,把当前节点作为父节点,新生成的子节点放到后面。

练习

创建两个提交

  • 提交两次即可
git commit
git commit

Git Branch

教学

Git 的分支也非常轻量。它们只是简单地指向某个提交纪录 —— 仅此而已。所以许多 Git 爱好者传颂:

早建分支!多用分支!

这是因为即使创建再多的分支也不会造成储存或内存上的开销,并且按逻辑分解工作到不同的分支要比维护那些特别臃肿的分支简单多了。

在将分支和提交记录结合起来后,我们会看到两者如何协作。现在只要记住使用分支其实就相当于在说:“我想基于这个提交以及它所有的父提交进行新的工作。”

演示

  1. 使用 git branch newImage 创建一个新分支

  2. 使用 git commit 创建一条新的提交记录

  • main分支前进了,newImage分支没动,这是因为当前还在main(后面有星号)
  1. 切换到新分支 git checkout <name> ,再进行提交
  • Git 2.23 版本中,引入了一个名为 git switch 的新命令,最终会取代 git checkout,因为 checkout 作为单个命令有点超载(它承载了很多独立的功能)。
  • 创建的同时切换:git checkout -b <your-branch-name>

练习

创建一个名为 bugFix 的新分支,然后切换过去。

git branch bugFix
git checkout bugFix
# git switch bugFix

或:

git chechout -b bugFix

Git Merge

教学

新建一个分支,在其上开发某个新功能,开发完成后再合并回主线。——方法1

在 Git 中合并两个分支时会产生一个特殊的提交记录,它有两个父节点。翻译成自然语言相当于:“我要把这两个父节点本身及它们所有的祖先都包含进来。”

演示

要把 bugFix 合并到 main 里(当前在main分支)

  • git merge bugFixmain 现在指向了一个拥有两个父节点的提交记录
    • main 开始沿着箭头向上看,在到达起点的路上会经过所有的提交记录。这意味着 main 包含了对代码库的所有修改。

再把 main 分支合并到 bugFix

  • git checkout bugFix; git merge main:把 bugFix 移动到 main 所指向的那个提交记录

练习

  • 创建新分支 bugFix

  • git checkout bugFix 命令切换到该分支

  • 提交一次

  • git checkout main 切换回 main

  • 再提交一次

  • git mergebugFix 合并到 main

git branch bugFix
git checkout bugFix
git commit
git checkout main
git commit
git merge bugFix

Git Rebase

教学

新建一个分支,在其上开发某个新功能,开发完成后再合并回主线。——方法2

  • 取出一系列的提交记录,“复制”它们,然后在另外一个地方逐个的放下去。

  • 优势就是可以创造更线性的提交历史,如果只允许使用 Rebase 的话,代码库的提交历史将会变得异常清晰。

演示

把 bugFix 分支里的工作直接移到 main 分支上。移动以后会使得两个分支的功能看起来像是按顺序开发,但实际上它们是并行开发的。

  • git rebase main,bugFix 分支上的工作在 main 的最顶端,同时我们也得到了一个更线性的提交序列。
    • 提交记录 C3 依然存在(树上那个半透明的节点),而 C3' 是我们 Rebase 到 main 分支上的 C3 的副本。
    • 支持两个参数,前一个参数是插入的父节点,后一个参数是最新分支

1-3

  • 再切换到main分支运行git rebase bugFix,此时两个分支处在同一顶点上,不会有分叉记录

练习

  • 新建并切换到 bugFix 分支
  • 提交一次
  • 切换回 main 分支再提交一次
  • 再次切换到 bugFix 分支,rebase 到 main 上
git checkout -b bugFix
git commit
git checkout main
git commit
git checkout bugFix
git rebase main

附录

  • 快捷命令
levels  # 返回选择题目
level <name>  # 跳转到指定题目
objective  # 打开目标描述窗口
show goal  # 打开目标图示窗口
hide goal  # 隐藏目标图示窗口
show solution  # 查看答案
hint  # 进行提示
undo  # 撤销
reset  # 返回初始状态
delay 2000  # 延时2秒
help  # 弹出帮助窗口

高级篇

分离 HEAD

学习在项目的提交树上前后移动的几种方法

教学

HEAD 是一个对当前检出记录的符号引用 —— 也就是指向你正在其基础上进行工作的提交记录。

  • HEAD 总是指向当前分支上最近一次提交记录。大多数修改提交树的 Git 命令都是从改变 HEAD 的指向开始的。

  • HEAD 通常情况下是指向分支名的(如 bugFix)。在你提交时,改变了 bugFix 的状态,这一变化通过 HEAD 变得可见。

演示

  • git checkout C1; git checkout main; git commit; git checkout C2

    • HEAD 指向了 main,随着提交向前移动。
    • 注意这部分代码并不是真的在查看 HEAD 指向。
    • 如果想看 HEAD 指向,可以通过 cat .git/HEAD 查看, 如果 HEAD 指向的是一个引用,还可以用 git symbolic-ref HEAD 查看它的指向。
  • 分离头指针HEAD:让其指向了某个具体的提交记录而不是分支名。

    • 刚开始指向HEAD -> main -> C1
    • git checkout C1
    • 变成了HEAD -> C1
    • 这就是译者注里面说的不是真正的查看HEAD指向

练习

bugFix 分支中分离出 HEAD 并让其指向一个提交记录。
——通过哈希值指定提交记录。每个提交记录的哈希值显示在代表提交记录的圆圈中。(C1C2之类的)

git checkout C4

相对引用1(^)

教学

  • 实际中提交并非C1C2……而是一串哈希值(40位)。只需要提供能够唯一标识提交记录的前几个字符即可
  • 但这样还是很不方便,使用相对引用可以从一个易于记忆的地方(比如 bugFix 分支或 HEAD)开始计算。
    • 使用 ^ 向上移动 1 个提交记录
    • 使用 ~<num> 向上移动多个提交记录,如 ~3

演示

main^main 的父节点,main^^main 的第二个父节点

  • git checkout main^ ——切换到当前main分支提交的父节点
  • 也可以将 HEAD 作为相对引用的参照:git checkout C3; git checkout HEAD^; git checkout HEAD^; git checkout HEAD^——一直使用HEAD向上移动

练习

切换到 bugFix 的父节点。这会进入分离 HEAD 状态。

git checkout bugFix^

相对引用2(~)

教学

该操作符后面可以跟一个数字(可选,不跟数字时与 ^ 相同,向上移动一次),指定向上移动多少次。

演示

后退四步

  • 连续向上后退四个:git checkout HEAD~4
  • 直接使用 -f 选项让分支指向另一个提交
    • git branch -f main HEAD~3 将 main 分支强制指向 HEAD 的第 3 级父提交。

练习

移动 HEADmainbugFix 到目标所示的位置
这一关至少要用到一次直接引用 (即哈希值)

2-1

git checkout HEAD^
git branch -f main C6
git branch -f bugFix bugFix~3

Note: git branch -f main C6 git branch -f bugFix bugFix~3 还用的不熟悉

撤销变更

教学

撤销变更由底层部分(暂存区的独立文件或者片段)和上层部分(变更到底是通过哪种方式被撤销的)组成。我们这个应用主要关注的是后者。

主要有两种方法用来撤销变更 —— 一是 git reset,还有就是 git revert

演示

Git Reset

通过把分支记录回退几个提交记录来实现撤销改动。你可以将这想象成“改写历史”。git reset 向上移动分支,原来指向的提交记录就跟从来没有提交过一样。

  • git reset HEAD~1:把 main 分支移回到 C1;现在本地代码库根本就不知道有 C2 这个提交了。
    • 这种“改写历史”的方法对大家一起使用的远程分支是无效的

Git Revert

撤销更改并分享给别人

  • git revert HEAD:要撤销的提交记录C2后面多了一个新提交C2'
    • 新提交记录 C2' 引入了更改 —— 这些更改刚好是用来撤销 C2 这个提交的。
    • 也就是说 C2' 的状态与 C1 是相同的。

练习

分别撤销 local 分支和 pushed 分支上的最近一次提交。共需要撤销两个提交(每个分支一个)。
记住 pushed 是远程分支,local 是本地分支

git reset HEAD^
git checkout pushed
git revert HEAD

移动提交记录

Git Cherry-pick

教学

  • git cherry-pick <提交号>...:将一些提交复制到当前所在的位置(HEAD)下面

演示

  • git cherry-pick C2 C4:把这两个提交复制到当前HEAD的下面,可以跳记录

练习

将三个分支中的提交记录复制到 main 上

git cherry-pick C3 C4 C7

交互式 rebase

不清楚你想要的提交记录的哈希值时 ——交互式rebase:从一系列的提交记录中找到想要的记录

教学

  • 交互式 rebase 指的是使用带参数 --interactive 的 rebase 命令, 简写为 -i

    • 如果你在命令后增加了这个选项, Git 会打开一个 UI 界面并列出将要被复制到目标分支的备选提交记录,它还会显示每个提交记录的哈希值和提交说明,提交说明有助于你理解这个提交进行了哪些更改。
    • 在实际使用时,所谓的 UI 窗口一般会在文本编辑器 —— 如 Vim —— 中打开一个文件。
  • 当 rebase UI界面打开时, 你能做3件事:

    • 调整提交记录的顺序(通过鼠标拖放来完成)
    • 删除你不想要的提交(通过切换 pick 的状态来完成,关闭就意味着你不想要这个提交记录)
    • 合并提交,它允许你把多个提交记录合并成一个。(本课程不支持)

演示

  • git rebase -i HEAD~4:把当前HEAD指针往上(包括当前)的四个提交提取出来,进行排序、删除等操作,结束后返回想要调整的顺序

练习

做一次交互式的 rebase,整理成目标窗口中的提交顺序。

git rebase -i HEAD~4

  • 打开窗口后拖动成要求状态

杂项

只取一个提交记录

不保留分支中的某些提交记录(日志打印等调试语句)

教学

  • 使用git rebase -igit cherry-pick即可

练习

确保 main 分支能得到 bugFix 分支上的相关提交

方法1:

git checkout main
git cherry-pick C4

4-1

方法2:

git rebase -i HEAD~3

4-2

git checkout main
git rebase bugFix

提交的技巧1

对某个以前的提交记录进行一些小小的调整

教学

一种思路:

  • 先用 git rebase -i 将提交重新排序,然后把我们想要修改的提交记录挪到最前

  • 然后用 git commit --amend 来进行一些小修改

  • 接着再用 git rebase -i 来将他们调回原来的顺序

  • 最后我们把 main 移到修改的最前端(用你自己喜欢的方法)

——对于 ' 的数量上的不同,并不纳入对比范围内

git commit --amend :对上一次提交有修改,可以修改文件也可以修改说明,不产生新的commit

练习

git rebase -i HEAD~2  # 交换caption分支的C2和C3
git commit --amend  # 重新提交C2
git rebase -i HEAD~2  # 交换caption分支的C3和C2

git branch -f main  # 更新main分支到最新,空白默认为当前HEAD
# 或
git checkout main  # 切换到main分支
git rebase caption  # 更新main分支到最新caption

4-3

——本关不允许使用cherry-pick,否则的话直接

git checkout main

# 可以达到main分支要求
git cherry-pick C2 C3

# 达到修改C2提交的要求
git cherry-pick C2
git commit --amend
git cherry-pick C3

提交的技巧2

上一关进行了两次排序,而这有可能造成由 rebase 而导致的冲突

演示

  • cherry-pick 可以将提交树上任何地方的提交记录取过来追加到 HEAD 上(只要不是 HEAD 上游的提交就没问题

练习

通过 --amend 改变提交记录 C2,但你不能用 rebase -i

Git Tags

  • 分支很容易被人为移动,并且当有新的提交时,它也会移动。分支很容易被改变,大部分分支还只是临时的,并且还一直在变。
  • 有没有什么可以永远指向某个提交记录的标识?

教学

  • Git 的 tag 可以(在某种程度上 —— 因为标签可以被删除后重新在另外一个位置创建同名的标签)永久地将某个特定的提交命名为里程碑,然后就可以像分支一样引用了。
  • 它们并不会随着新的提交而移动。你也不能切换到某个标签上面进行修改提交,它就像是提交树上的一个锚点,标识了某个特定的位置。——在tag上进行新git commit --amend会创建新分支。

演示

  • git tag v1 C1:我们将这个标签命名为 v1,并且明确地让它指向提交记录 C1,如果你不指定提交记录,Git 会用 HEAD 所指向的位置。

练习

按照目标建立两个标签,然后切换到 v1 上面,要注意你会进到分离 HEAD 的状态 —— 这是因为不能直接在v1 上面做 commit。

git tag v0 C1
git tag v1 C2
git checkout v1

4-4

Git Describe

用来描述离你最近的锚点(也就是标签)

教学

  • 场景:当你用 git bisect(一个查找产生 Bug 的提交记录的指令)找到某个提交记录时,或者是当你坐在你那刚刚度假回来的同事的电脑前时, 可能会用到这个命令。

  • 语法是:git describe <ref>

    • <ref> 可以是任何能被 Git 识别成提交记录的引用,如果你没有指定的话,Git 会以你目前所检出的位置(HEAD
  • 输出结果为:<tag>_<numCommits>_g<hash>

    • tag 表示的是离 ref 最近的标签, numCommits 是表示这个 reftag 相差有多少个提交记录, hash 表示的是你所给定的 ref 所表示的提交记录哈希值的前几位。
    • ref 提交记录上有某个标签时,则只输出标签名称

演示

  • git tag v2 C3打好标记
  • git describe main输出:v1_2_gC2
  • git describe side输出:v2_1_gC4

4-5

练习

  • 随意指定几个位置来查看即可
  • 都是往上找标签
  • 随意提交就可以过关

4-6

高级操作

多分支 rebase

练习

  • 我们准备了很多分支!咱们把这些分支 rebase 到 main 上吧。
  • 你的领导给你提了点要求 —— 他们希望得到有序的提交历史,也就是我们最终的结果应该是 C6'C7' 上面, C5'C6' 上面,依此类推。

——本关不允许使用cherry-pick

git rebase main bugFix
git rebase bugFix side
git rebase side another
git branch -f main 

5-1

两个父节点

教学

  • 操作符 ^~ 符一样,后面也可以跟一个数字。
  • 作用是指定合并提交记录的某个父提交
    • Git 默认选择合并提交的“第一个”父提交
    • 添加数字可以选择切换到第几个父提交上

演示

5-2

  • 操作符支持链式操作

5-3

练习

在指定的目标位置创建一个新的分支,要求你使用刚刚讲到的相对引用修饰符

git checkout HEAD^^2~
git branch bugWork
git checkout main

# 或者一句解决
git branch bugWork HEAD~^2^

5-4

纠缠不清的分支

练习

  • 现在我们的 main 分支是比 onetwothree 要多几个提交。出于某种原因,我们需要把 main 分支上最近的几次提交做不同的调整后,分别添加到各个的分支上。

  • one 需要重新排序并删除 C5two 仅需要重排排序,而 three 只需要提交一次。

# 强制移动分支指针到指定位置
git branch -f three HEAD~3
git branch -f one HEAD
git branch -f two HEAD
# 拖动修改one分支
git checkout one
git rebase -i HEAD~4  # 修改成指定样子
# 拖动修改two分支
git checkout one
git rebase -i HEAD~4  # 修改成指定样子

5-5

标准答案:使用cherry-pick

git checkout one
git cherry-pick c4 c3 c2
git checkout two
git cherry-pick c5 c4 c3 c2
git branch -f three c2

远程仓库

远程仓库只是你的仓库在另个一台计算机上的拷贝。你可以通过因特网与这台计算机通信 —— 也就是增加或是获取提交记录

  • 远程仓库是一个强大的备份。本地仓库也有恢复文件到指定版本的能力, 但所有的信息都是保存在本地的。有了远程仓库以后,即使丢失了本地所有数据, 你仍可以通过远程仓库拿回你丢失的数据
  • 远程让代码社交化了! 既然你的项目被托管到别的地方了, 你的朋友可以更容易地为你的项目做贡献(或者拉取最新的变更)

Git Clone

演示

  • 创建远程仓库的命令: git clone本地创建一个远程仓库的拷贝

练习

git clone 复制下你的仓库

git clone

远程分支

教学

  • 在我们的本地仓库多了一个名为 o/main 的分支, 这种类型的分支就叫远程分支
  • 远程分支反映了远程仓库(在你上次和它通信时)的状态。这会有助于你理解本地的工作与公共工作的差别 —— 这是你与别人分享工作成果前至关重要的一步
  • 远程分支有一个特别的属性,在你检出时自动进入分离 HEAD 状态。Git 这么做是出于不能直接在这些分支上进行操作的原因, 你必须在别的地方完成你的工作, (更新了远程分支之后)再用远程分享你的工作成果。

命名规范:

  • <remote name>/<branch name>
  • 如果你看到一个名为 o/main 的分支,那么这个分支就叫 main,远程仓库的名称就是 o
  • 使用真正的 Git 时, 你的远程仓库默认为 origin

演示

  • git checkout o/main; git commit
  • Git 变成了分离 HEAD 状态,当添加新的提交时 o/main 也不会更新。这是因为 o/main 只有在远程仓库中相应的分支更新了以后才会更新。

6-1

练习

main 分支上做一次提交;然后检出 o/main,再做一提交。这有助于你理解远程分支的不同,他们的更新只是反映了远程的状态。

git commit
git checkout o/main
git commit

Git Fetch

从远程仓库获取数据

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

演示

  • 运行git fetch,远程最新提交被下载到本地,同时远程分支 o/main 也被更新

    • 从远程仓库下载本地仓库中缺失的提交记录
    • 更新远程分支指针(如 o/main)
  • 远程分支反映了远程仓库在你最后一次与它通信时的状态,git fetch 就是你与远程仓库通信的方式了

  • 不能做的事:不会改变你本地仓库的状态。它不会更新你的 main 分支,也不会修改你磁盘上的文件。

练习

git fetch 下载所有的提交

git fetch

6-2

Git Pull

抓取更新再合并到本地分支

演示

  • git fetch; git merge o/main:先下载远程更新,再合并到当前分支
  • git pull 就是 git fetchgit merge 的缩写

练习

git pull

模拟团队合作

“假装”你的同事、朋友、合作伙伴更新了远程仓库,有可能是某个特定的分支,或是几个提交记录
引入一个自造命令 git fakeTeamwork

演示

  • git fakeTeamwork 假装在远程新建一个提交
  • git fakeTeamwork foo 3 假装在远程foo分支新建三个提交

练习

克隆一个远程仓库(用 git clone),再在刚创建的远程仓库中模拟一些修改,然后在你自己的本地分支上做一些提交,再拉取远程仓库的变更。

git clone
git fakeTeamwork 2
git commit
git pull

Git Push

你的变更上传到指定的远程仓库,并在远程仓库上合并你的新提交记录

教程

  • git push 不带任何参数时的行为与 Git 的一个名为 push.default 的配置有关。它的默认值取决于你正使用的 Git 的版本,但是在教程中我们使用的是 upstream
  • 在你的项目中进行推送之前,最好检查一下这个配置。

演示

  • git pull:把本地提交上传到远程仓库
    • 我们的远程分支 (o/main) 也同样被更新了

练习

向远程仓库分享两个提交记录

git commit
git commit 
git push

偏离的提交历史

push和pull的困难来自于远程库提交历史的偏离

例子

假设你周一克隆了一个仓库,然后开始研发某个新功能。到周五时,你新功能开发测试完毕,可以发布了。但是 —— 天啊!你的同事这周写了一堆代码,还改了许多你的功能中使用的 API,这些变动会导致你新开发的功能变得不可用。但是他们已经将那些提交推送到远程仓库了,因此你的工作就变成了基于项目旧版的代码,与远程仓库最新的代码不匹配了。

  • 此时是不允许push的
  • 它会强制你先合并远程最新的代码,然后才能分享你的工作。

演示

  • git push 失效,因为远程仓库有未拉取到本地的提交
  • 你需要做的就是使你的工作基于最新的远程分支——最直接的方法就是通过 rebase 调整你的工作

方法1:

  • git fetch; git rebase o/main; git pushgit pull --rebase; git push

方法2:

  • git fetch; git merge o/main; git pushgit pull; git push

练习

  • 克隆你的仓库
  • 模拟一次远程提交(fakeTeamwork)
  • 完成一次本地提交
  • rebase 发布你的工作
git clone
git fakeTeamwork
git commit
git pull --rebase
git push

6-3

锁定的main

大型项目中,main(master)分支被锁定保护,需要一些Pull Request流程来合并修改

教程

卡住原因:

  • 远程服务器拒绝直接推送(push)提交到main, 因为策略配置要求 pull requests 来提交更新.

  • 你应该按照流程,新建一个分支, 推送(push)这个分支并申请pull request,但是你忘记并直接提交给了main.现在你卡住并且无法推送你的更新.

解决办法:

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

练习

git branch feature
git reset HEAD^
git checkout feature
git push

6-4

标准答案:

git reset --hard o/master
git checkout -b feature C2
git push origin feature
  • git push 后接的参数,第一个为远程仓库名(默认为origin),第二个为分支名(默认为当前HEAD指向的)

补充 参考
1、git reset --hard
重置stage区和工作目录里的内容,就是你的没有commit的修改会被全部擦掉
2、git reset --soft
reset --soft:用于版本的回退,只进行对commit操作的回退,不影响工作区的文件。
在提交代码的时候,commit之后,然后我又在工作区添加了东西,这时候突然发现,上一次的commit有错误的文件,需要重新修改,但是我添加的东西友不想丢失,而且我想修改上一次的提交,这时候可进行git reset --soft 版本号
3、git reset --mixed
reset 不加参数(mixed):保留工作目录,并清空暂存区

远程仓库高级操作

推送主分支

  • 将特性分支集成到 main
  • 推送并更新远程分支

演示

  • 快速的更新 main 分支并推送到远程:git pull --rebase; git push

练习

  • 这里共有三个特性分支 —— side1 side2side3
  • 我需要将这三分支按顺序推送到远程仓库
  • 因为远程仓库已经被更新过了,所以我们还要把那些工作合并过来
git fetch
git rebase o/main side1
git rebase side1 side2
git rebase side2 side3
git rebase side3 main
git push

7-1

合并远程仓库

教学

rebase的优缺点:

  • 优点:Rebase 使你的提交树变得很干净, 所有的提交都在一条线上
  • 缺点:Rebase 修改了提交树的历史

练习

和上一题一样,不过不能用rebase,要用merge

git fetch
git checkout main
git merge side1
git merge o/main
git merge side2
git merge side3
git push

7-2

标准答案:

git checkout main
git pull origin main
git merge side1
git merge side2
git merge side3
git push

远程追踪

  • pull 操作时, 提交记录会被先下载到 o/main 上,之后再合并到本地的 main 分支。隐含的合并目标由这个关联确定的。
  • push 操作时, 我们把工作从 main 推到远程仓库中的 main 分支(同时会更新远程分支 o/main) 。这个推送的目的地也是由这种关联确定的!

教学

  • 关联关系就是由分支的“remote tracking”属性决定的。main 被设定为跟踪 o/main —— 这意味着为 main 分支指定了推送的目的地以及拉取后合并的目标。
  • 当你克隆仓库的时候, Git 就自动帮你把这个属性设置好了,会看到如下输出:
    • local branch "main" set to track remote branch "o/main"

演示

  • 自定义这个属性:可以让任意分支跟踪 o/main——两种方法
  1. git checkout -b totallyNotMain o/main,新建分支让他追踪远程main分支
    git checkout -b foo o/main; git pull:foo分支和远程main分支一致,而原来的main分支没变

git checkout -b foo o/main; git commit; git push:本地提交的foo分支被push到了远程的main

7-4

    1. git branch -u o/main foo:这样 foo 就会跟踪 o/main 了。如果当前就在 foo 分支上, 还可以省略 foo
  • 实现的操作和上面的一样,只不过需要提前创建foo分支

练习

我们在检出 main 分支的情况下将工作推送到的远程仓库中的 main 分支上

git checkout -b side o/main # git checkout -b side; git branch -u o/main side
git commit
git pull --rebase  # 注意顺序
git push

7-5

Git Push的参数

未指定参数时,通过当前检出分支的属性来确定远程仓库以及要 push 的目的地

教程

  • 指定参数语法为:git push <remote> <place>
  • 如:git push origin main切到本地仓库中的“main”分支,获取所有的提交,再到远程仓库“origin”中找到“main”分支,将远程仓库中没有的提交记录都添加上去,搞定之后告诉我。
  • 因为我们通过指定参数告诉了 Git 所有它需要的信息, 所以它就忽略了我们所检出的分支的属性

演示

  • 使用参数后不用考虑HEAD的指向(即当前检出状态)

7-6

练习

更新远程仓库中的 foomain, 但是 git checkout 被禁用了

git push origin main
git push origin foo

7-7

Git Push的参数2

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

教学

  • 同时为源和目的地指定 <place> 的话,只需要用冒号 : 将二者连起来就可以了:
    git push origin <source>:<destination>

演示

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

7-8

7-9

练习

git push origin foo:main
git push origin main^:foo  # git push origin HEAD^:foo

7-10

Git Fetch的参数

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

教学

git fetch origin foo
  • Git 会到远程仓库的 foo 分支上,然后获取所有本地不存在的提交,放到本地的 o/foo 上。
  • 因为fetch不会更改本地分支,所以本地foo分支没有同步
  • 直接<source>:<destination>的话,确实可以更新本地分支,但不能在当前检出分支上这么做,而且也没有必要,因为pull就是干这个的
  • source 现在指的是远程仓库中的位置,而 destination 才是要放置提交的本地仓库的位置。它与 git push 刚好相反

演示

7-11

一般没人这样做吧喂

  • 如果本地没有bar分支,就会自己新建。和push一样,要是远程没有就新建。

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

练习

抓取目标窗口中指定的提交记录,使用这些魔幻的命令

git fetch origin foo:main
git fetch origin main^:foo
git checkout foo
git merge main

7-12

没有Source的Source*

教学

你可以在 git push 或 git fetch 时不指定任何 source,方法就是仅保留冒号和 destination 部分,source 部分留空。

  • git push origin :side
  • git fetch origin :bugFix

演示

  • 如果 push 空 到远程仓库它会删除远程仓库中的分支!
  • 如果 fetch 空 到本地,会在本地HEAD位置创建一个新分支

练习

删除一个远程的分支, 再用 git fetch 在本地创建一个新分支

git fetch origin :bar  # pull也是可以的
git push origin :foo

7-13

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 唯一关注的是提交最终合并到哪里(也就是为 git fetch 所提供的 destination 参数传递给merge, merge 到我们的检出位置,无论我们当前检出的位置是哪)
  • pull 也可以用 source:destination

7-14

练习

按照目标窗口中的状态进行操作。你需要下载一些提交,然后创建一些新分支,再合并这些分支到其它分支

git pull origin bar:foo
git pull origin main:side

7-15