Git 沙盒模拟实战(基础篇)

Git 沙盒模拟实战

分支

现有一个主分支前端

image-20200911190747381

建立分支

# 建立分支
$ git branch bugFix
# 切换到指定分支
$ git checkout bugFix

或者git

# 建立分支,并切换到该分支
$ git checkout -b bugFix

切换到指定分支后,工做区也会更新。好比在 IDEA 中工程文件会切换到该分支的保存的版本数据。shell

image-20200911190925836

合并分支

先要建立分支,切换到该分支,并进行提交ide

$ git checkout -b bugFix

$ git commit -m "c2"

image-20200911191243994

$ git checkout master

$ git commit -m "c3"

image-20200911191533239

# 合并指定分支到当前分支
$ git merge "须要合并的分支"

image-20200911191605939

Rebase

第二种合并分支的方法是 git rebase。Rebase 实际上就是取出一系列的提交记录,“复制”它们,而后在另一个地方逐个的放下去。学习

Rebase 的优点就是能够创造更线性的提交历史,这听上去有些难以理解。若是只容许使用 Rebase 的话,代码库的提交历史将会变得异常清晰。设计

image-20200911193322069

$ git rebase master

image-20200911193111298

其余关于分支命令:调试

# 列出全部本地分支
$ git branch
 
# 列出全部远程分支
$ git branch -r
 
# 列出全部本地分支和远程分支
$ git branch -a

# 新建一个分支,指向指定commit
$ git branch [branch] [commit]

# 选择一个commit,合并进当前分支
$ git cherry-pick [commit]

# 新建一个分支,与指定的远程分支创建追踪关系
$ git branch --track [branch] [remote-branch]

# 创建追踪关系,在现有分支与指定的远程分支之间
$ git branch --set-upstream [branch] [remote-branch]

# 删除分支
$ git branch -d [branch-name]
 
# 删除远程分支
$ git push origin --delete [branch-name]
$ git branch -dr [remote/branch]

分离 HEAD

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

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

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

分离的 HEAD 就是让其指向了某个具体的提交记录而不是分支名。在命令执行以前的状态以下所示:

HEAD -> bugFix-> C4

HEAD 指向 bugFix, bugFix 指向 C4

image-20200911195102361

$ git checkout c4

image-20200911195202561

相对引用

经过指定提交记录哈希值的方式在 Git 中移动不太方便。在实际应用时,并无像上图中这么漂亮的可视化提交树供你参考,因此你就不得不用 git log 来查查看提交记录的哈希值。

例如上面的介绍中的提交记录的哈希值多是 fed2da64c0efc5293610bdd892f82a58e8cbc5d8。舌头都快打结了吧...

比较使人欣慰的是,Git 对哈希的处理很智能。你只须要提供可以惟一标识提交记录的前几个字符便可。所以我能够仅输入fed2 而不是上面的一长串字符。

正如我前面所说,经过哈希值指定提交记录很不方便,因此 Git 引入了相对引用。这个就很厉害了!

使用相对引用的话,你就能够从一个易于记忆的地方(好比 bugFix 分支或 HEAD)开始计算。

相对引用很是给力,这里我介绍两个简单的用法:

  • 使用 ^ 向上移动 1 个提交记录
  • 使用 ~<num> 向上移动多个提交记录,如 ~3

首先看看操做符 (^)。把这个符号加在引用名称的后面,表示让 Git 寻找指定提交记录的父提交。

因此 master^ 至关于“master 的父节点”。

master^^master 的第二个父节点

image-20200911200338921

$ git checkout bugFix^

image-20200911200436148


若是你想在提交树中向上移动不少步的话,敲那么多 ^ 貌似也挺烦人的,Git 固然也考虑到了这一点,因而又引入了操做符 ~

该操做符后面能够跟一个数字(可选,不跟数字时与 ^ 相同,向上移动一次),指定向上移动多少次。我们仍是经过实际操做看一下吧

我使用相对引用最多的就是移动分支。能够直接使用 -f 选项让分支指向另外一个提交。例如:

$ git branch -f master HEAD~3

上面的命令会将 master 分支强制指向 HEAD 的第 3 级父提交。

image-20200911201012151

移动 HEADbugFix 到目标所示的位置。

$ git checkout HEAD~1

image-20200911201156183

$ git branch -f bugFix HEAD~1

image-20200911201338403

撤销变动

在 Git 里撤销变动的方法不少。和提交同样,撤销变动由底层部分(暂存区的独立文件或者片断)和上层部分(变动究竟是经过哪一种方式被撤销的)组成。咱们这个应用主要关注的是后者。

主要有两种方法用来撤销变动 —— 一是 git reset,还有就是 git revert。接下来我们逐个进行讲解。


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

虽然在你的本地分支中使用 git reset 很方便,可是这种“改写历史”的方法对你们一块儿使用的远程分支是无效的哦!

为了撤销更改并分享给别人,咱们须要使用 git revert

分别撤销 local 分支和 pushed 分支上的最近一次提交。共须要撤销两个提交(每一个分支一个)。

记住 pushed 是远程分支,local 是本地分支

image-20200911203311362

$ git reset master

image-20200911203637240

# 先切换到 pushed
$ git checkout pushed
$ git revert c2  # 须要用到提交的哈希码

image-20200911203831371

移动(整理)提交记录

到如今咱们已经学习了 Git 的基础知识 —— 提交、分支以及在提交树上移动。 这些概念涵盖了 Git 90% 的功能,一样也足够知足开发者的平常需求

然而, 剩余的 10% 在处理复杂的工做流时(或者当你陷入困惑时)可能就显得尤其重要了。接下来要讨论的这个话题是“整理提交记录” —— 开发人员有时会说“我想要把这个提交放到这里, 那个提交放到刚才那个提交的后面”, 而接下来就讲的就是它的实现方式,很是清晰、灵活,还很生动。

看起来挺复杂, 实际上是个很简单的概念。

Git Cherry-pick

本小节的第一个命令是 git cherry-pick, 命令形式为:

  • git cherry-pick <提交号>...

若是你想将一些提交复制到当前所在的位置(HEAD)下面的话, Cherry-pick 是最直接的方式了。我我的很是喜欢 cherry-pick,由于它特别简单。

我们仍是经过例子来看一下!

须要简单的将三个分支中的提交记录复制到 master 上就能够了。

image-20200911205410850

$ git cherry-pick C3 C4 C7

image-20200911205504309

交互式 Rebase

当你知道你所须要的提交记录(而且还知道这些提交记录的哈希值)时, 用 cherry-pick 再好不过了 —— 没有比这更简单的方式了。

可是若是你不清楚你想要的提交记录的哈希值呢? 幸亏 Git 帮你想到了这一点, 咱们能够利用交互式的 rebase —— 若是你想从一系列的提交记录中找到想要的记录, 这就是最好的方法了

我们具体来看一下……

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

若是你在命令后增长了这个选项, Git 会打开一个 UI 界面并列出将要被复制到目标分支的备选提交记录,它还会显示每一个提交记录的哈希值和提交说明,提交说明有助于你理解这个提交进行了哪些更改。

当 rebase UI界面打开时, 你能作3件事:

  • 调整提交记录的顺序(经过鼠标拖放来完成)
  • 删除你不想要的提交(经过切换 pick 的状态来完成,关闭就意味着你不想要这个提交记录)
  • 合并提交。 简而言之,它容许你把多个提交记录合并成一个。

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

image-20200911210955287

$ git rebase -i overHere

image-20200911211106726

image-20200911210919344

一些提交技巧

只去一个提交记录

来看一个在开发中常常会遇到的状况:我正在解决某个特别棘手的 Bug,为了便于调试而在代码中添加了一些调试命令并向控制台打印了一些信息。

这些调试和打印语句都在它们各自的提交记录里。最后我终于找到了形成这个 Bug 的根本缘由,解决掉之后以为沾沾自喜!

最后就差把 bugFix 分支里的工做合并回 master 分支了。你能够选择经过 fast-forward 快速合并到 master 分支上,但这样的话 master 分支就会包含我这些调试语句了。你确定不想这样,应该还有更好的方式……


实际咱们只要让 Git 复制解决问题的那一个提交记录就能够了。跟以前咱们在“整理提交记录”中学到的同样,咱们可使用

  • git rebase -i
  • git cherry-pick

来达到目的。

image-20200911212116887

$ git cherry-pick bugFix

image-20200911212058175

提交的技巧 #1

接下来这种状况也是很常见的:你以前在 newImage 分支上进行了一次提交,而后又基于它建立了 caption 分支,而后又提交了一次。

此时你想对的某个之前的提交记录进行一些小小的调整。好比设计师想修改一下 newImage 中图片的分辨率,尽管那个提交记录并非最新的了。


咱们能够经过下面的方法来克服困难:

  • 先用 git rebase -i 将提交从新排序,而后把咱们想要修改的提交记录挪到最前
  • 而后用 commit --amend 来进行一些小修改
  • 接着再用 git rebase -i 来将他们调回原来的顺序
  • 最后咱们把 master 移到修改的最前端(用你本身喜欢的方法),就大功告成啦!

固然完成这个任务的方法不止上面提到的一种(我知道你在看 cherry-pick 啦),以后咱们会多点关注这些技巧啦,但如今暂时只专一上面这种方法。 最后有必要说明一下目标状态中的那几个' —— 咱们把这个提交移动了两次,每移动一次会产生一个 ';而 C2 上多出来的那个是咱们在使用了 amend 参数提交时产生的,因此最终结果就是这样了。

也就是说,我在对比结果的时候只会对比提交树的结构,对于 ' 的数量上的不一样,并不归入对比范围内。只要你的 master 分支结构与目标结构相同,我就算你经过。

image-20200911213636719

$ git rebase -i master
# 使用新的一次提交
$ git commit --amend

$ git rebase -i master

$ git checkout master

$ git merge caption

image-20200911213454999

提交的技巧 #2

正如你在上小节所见到的,咱们可使用 rebase -i 对提交记录进行从新排序。只要把咱们想要的提交记录挪到最前端,咱们就能够很轻松的用 --amend 修改它,而后把它们从新排成咱们想要的顺序。

但这样作就惟一的问题就是要进行两次排序,而这有可能形成由 rebase 而致使的冲突。下面仍是看看 git cherry-pick是怎么作的吧。

要在内心牢记 cherry-pick 能够将提交树上任何地方的提交记录取过来追加到 HEAD 上(只要不是 HEAD 上游的提交就没问题)。

同上一小节同样的问题,咱们是用 git cherry-pick来解决。

image-20200911214431155

$ git checkout master

$ git cherry-pick c2

$ git commit --amend

$ git cherry-pick c3

image-20200911214358004

打标签

相信你已经发现了:分支很容易被人为移动,而且当有新的提交时,它也会移动。分支很容易被改变,大部分分支还只是临时的,而且还一直在变。

你可能会问了:有没有什么能够永远指向某个提交记录的标识呢,好比软件发布新的大版本,或者是修正一些重要的 Bug 或是增长了某些新特性,有没有比分支更好的能够永远指向这些提交的方法呢?

固然有了!Git 的 tag 就是干这个用的啊,它们能够(在某种程度上 —— 由于标签能够被删除后从新在另一个位置建立同名的标签)永久地将某个特定的提交命名为里程碑,而后就能够像分支同样引用了。

更可贵的是,它们并不会随着新的提交而移动。你也不能检出到某个标签上面进行修改提交,它就像是提交树上的一个锚点,标识了某个特定的位置。

image-20200912075930584

$ git tag v1 c2

$ git tag v0 c1

$ git checkout v1

image-20200912075759033

若是你不指定提交记录,Git 会用 HEAD所指向的位置。进行标记 Tag。

$ git tag v2
# 此时 HEAD 指向 c2 就会为 c2增长一个名为 v2 的tag

获取描述

因为标签在代码库中起着“锚点”的做用,Git 还为此专门设计了一个命令用来描述离你最近的锚点(也就是标签),它就是 git describe

Git Describe 能帮你在提交历史中移动了屡次之后找到方向;当你用 git bisect(一个查找产生 Bug 的提交记录的指令)找到某个提交记录时,或者是当你坐在你那刚刚度假回来的同事的电脑前时, 可能会用到这个命令。

git describe 的语法是:

git describe <ref>

<ref> 能够是任何能被 Git 识别成提交记录的引用,若是你没有指定的话,Git 会以你目前所检出的位置(HEAD)。

它输出的结果是这样的:

<tag>_<numCommits>_g<hash>
  • tag 表示的是往上追溯,离 ref 最近的标签,
  • numCommits 是表示这个 reftag 相差有多少个提交记录,
  • hash 表示的是你所给定的 ref 所表示的提交记录哈希值的前几位。

ref 提交记录上有某个标签时,则只输出标签名称

image-20200912084711001

$ git describe side

v1_1_gC4

$ git describe master

v0_2_gC2

高级操做

多分支 rebase

哥们儿,咱们准备了不少分支!我们把这些分支 rebase 到 master 上吧。

可是你的领导给你提了点要求 —— 他们但愿获得有序的提交历史,也就是咱们最终的结果应该是 C6'C7' 上面, C5'C6' 上面,依此类推。

即便你搞砸了也不要紧,用 reset 命令就能够从新开始了。记得看看咱们提供的答案,看你可否使用更少的命令来完成任务!

image-20200912093804536

$ git rebase master bugFix

$ git rebase bugFix side

$ git rebase side another

$ git rebase another master

image-20200912093751409

选择父提交记录

操做符 ^~ 符同样,后面也能够跟一个数字。

可是该操做符后面的数字与 ~ 后面的不一样,并非用来指定向上返回几代,而是指定合并提交记录的某个父提交。还记得前面提到过的一个合并提交有两个父提交吧,因此遇到这样的节点时该选择哪条路径就不是很清晰了。

Git 默认选择合并提交的“第一个”父提交,在操做符 ^ 后跟一个数字能够改变这一默认行为。

使用 ^~ 能够自由地在提交树中移动,很是给力。

更厉害的是,这些操做符还支持链式操做!

image-20200912095014091

$ git branch bugWork master^^2^

image-20200912094954220

纠缠不清的分支

如今咱们的 master 分支是比 onetwothree 要多几个提交。出于某种缘由,咱们须要把 master 分支上最近的几回提交作不一样的调整后,分别添加到各个的分支上。

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

慢慢来,你会找到答案的 —— 记得通关以后用 show solution 看看咱们的答案哦。

image-20200912101754067

$ git checkout one

$ git cherry-pick C4 C3 C2

$ git checkout two

$ git cherry-pick C5 C4 C3 C2

$ git branch -f three C2

image-20200912101724049

相关文章
相关标签/搜索