天天看点

Git学习-图文并茂还有游戏玩!

目录

主要

基础篇

Git Commit

Git Branch

Git Merge

Git Rebase

在提交树上移动HEAD

高级篇

分离HEAD

相对引用

撤销变更

移动提交记录

Git Cherry-pick

交互式 rebase

杂项

只取一个提交记录

Git Tag

Git Describe

远程

Push & Pull —— Git 远程仓库!

Git clone

Git Fetch

Git Pull

Git Push

偏离的提交历史

锁定的Master

IDEA中使用Git

参考

学习网址:https://learngitbranching.js.org/?locale=zh_CN

闯关的形式,很棒。

循序渐进地介绍 Git 主要命令。

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

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

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

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

Git学习-图文并茂还有游戏玩!

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

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

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

Git学习-图文并茂还有游戏玩!

太好了! 我们已经知道如何提交以及如何使用分支了。接下来咱们看看如何将两个分支合并到一起。就是说我们新建一个分支,在其上开发某个新功能,开发完成后再合并回主线。

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

Git学习-图文并茂还有游戏玩!
Git学习-图文并茂还有游戏玩!

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

Rebase 的优势就是可以创造更线性的提交历史,这听上去有些难以理解。如果只允许使用 Rebase 的话,代码库的提交历史将会变得异常清晰。

Git学习-图文并茂还有游戏玩!

在接触 Git 更高级功能之前,我们有必要先学习在你项目的提交树上前后移动的几种方法。

一旦熟悉了如何在 Git 提交树上移动,你驾驭其它命令的能力也将水涨船高!

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

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

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

要开始介绍 Git 的超棒特性了,快来吧!

git ckeckout c4

通过指定提交记录哈希值的方式在 Git 中移动不太方便。在实际应用时,并没有像本程序中这么漂亮的可视化提交树供你参考,所以你就不得不用 <code>git log</code> 来查查看提交记录的哈希值。

并且哈希值在真实的 Git 世界中也会更长(译者注:基于 SHA-1,共 40 位)。例如前一关的介绍中的提交记录的哈希值可能是 <code>fed2da64c0efc5293610bdd892f82a58e8cbc5d8</code>。舌头都快打结了吧...

比较令人欣慰的是,Git 对哈希的处理很智能。你只需要提供能够唯一标识提交记录的前几个字符即可。因此我可以仅输入<code>fed2</code> 而不是上面的一长串字符。

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

使用 <code>^</code> 向上移动 1 个提交记录

使用 <code>~</code> 向上移动多个提交记录,如 <code>~3</code>

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

主要有两种方法用来撤销变更 —— 一是 <code>git reset</code>,还有就是 <code>git revert</code>。接下来咱们逐个进行讲解。

Git Reset

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

Git学习-图文并茂还有游戏玩!

Git Revert

虽然在你的本地分支中使用 <code>git reset</code> 很方便,但是这种“改写历史”的方法对大家一起使用的远程分支是无效的哦!

Git学习-图文并茂还有游戏玩!

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

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

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

本系列的第一个命令是 <code>git cherry-pick</code>, 命令形式为:

<code>git cherry-pick &lt;提交号&gt;...</code>

如果你想将一些提交复制到当前所在的位置(<code>HEAD</code>)下面的话, Cherry-pick 是最直接的方式了。我个人非常喜欢 <code>cherry-pick</code>,因为它特别简单。

Git学习-图文并茂还有游戏玩!

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

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

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

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

在实际使用时,所谓的 UI 窗口一般会在文本编辑器 —— 如 Vim —— 中打开一个文件。

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

调整提交记录的顺序(通过鼠标拖放来完成)

删除你不想要的提交(通过切换 <code>pick</code> 的状态来完成,关闭就意味着你不想要这个提交记录)

合并提交。 简而言之,它允许你把多个提交记录合并成一个。

Git 技术、技巧与贴士大集合。

本地栈式提交

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

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

最后就差把 <code>bugFix</code> 分支里的工作合并回 <code>main</code> 分支了。你可以选择通过 fast-forward 快速合并到 <code>main</code> 分支上,但这样的话 <code>main</code> 分支就会包含我这些调试语句了。你肯定不想这样,应该还有更好的方式……

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

<code>git rebase -i</code>

<code>git cherry-pick</code>

来达到目的。

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

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

当然有了!Git 的 tag 就是干这个用的啊,它们可以(在某种程度上 —— 因为标签可以被删除后重新在另外一个位置创建同名的标签)永久地将某个特定的提交命名为里程碑,然后就可以像分支一样引用了。

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

Git学习-图文并茂还有游戏玩!

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

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

是时候分享你的代码了,让编码变得社交化吧

远程仓库

远程仓库并不复杂, 在如今的云计算盛行的世界很容易把远程仓库想象成一个富有魔力的东西, 但实际上它们只是你的仓库在另个一台计算机上的拷贝。你可以通过因特网与这台计算机通信 —— 也就是增加或是获取提交记录。

话虽如此, 远程仓库却有一系列强大的特性

首先也是最重要的的点, 远程仓库是一个强大的备份。本地仓库也有恢复文件到指定版本的能力, 但所有的信息都是保存在本地的。有了远程仓库以后,即使丢失了本地所有数据, 你仍可以通过远程仓库拿回你丢失的数据。

还有就是, 远程让代码社交化了! 既然你的项目被托管到别的地方了, 你的朋友可以更容易地为你的项目做贡献(或者拉取最新的变更)

现在用网站来对远程仓库进行可视化操作变得越发流行了(像 GitHub 或 Phabricator),但远程仓库永远是这些工具的顶梁柱, 因此理解其概念非常的重要!

直到现在, 教程都聚焦于本地仓库的操作(branch、merge、rebase 等等)。但我们现在需要学习远程仓库的操作 —— 我们需要一个配置这种环境的命令, 它就是 <code>git clone</code>。

从技术上来讲,<code>git clone</code> 命令在真实的环境下的作用是在本地创建一个远程仓库的拷贝(比如从 github.com)。

Git学习-图文并茂还有游戏玩!

远程分支

既然你已经看过 <code>git clone</code> 命令了,咱们深入地看一下发生了什么。

你可能注意到的第一个事就是在我们的本地仓库多了一个名为 <code>o/main</code> 的分支, 这种类型的分支就叫远程分支。由于远程分支的特性导致其拥有一些特殊属性。

远程分支反映了远程仓库(在你上次和它通信时)的状态。这会有助于你理解本地的工作与公共工作的差别 —— 这是你与别人分享工作成果前至关重要的一步。

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

为什么有 <code>o/</code>?

你可能想问这些远程分支的前面的 <code>o/</code> 是什么意思呢?好吧, 远程分支有一个命名规范 —— 它们的格式是:

<code>/</code>

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

大多数的开发人员会将它们主要的远程仓库命名为 <code>origin</code>,并不是 <code>o</code>。这是因为当你用 <code>git clone</code> 某个仓库时,Git 已经帮你把远程仓库的名称设置为 <code>origin</code> 了

不过 <code>origin</code> 对于我们的 UI 来说太长了,因此不得不使用简写 <code>o</code> 😃 但是要记住, 当你使用真正的 Git 时, 你的远程仓库默认为 <code>origin</code>!

Git 远程仓库相当的操作实际可以归纳为两点:向

远程仓库传输数据

从远程仓库获取数据。

既然我们能与远程仓库同步,那么就可以分享任何能被 Git 管理的更新(因此可以分享代码、文件、想法、情书等等)。

Git学习-图文并茂还有游戏玩!

git fetch 做了些什么?

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

从远程仓库下载本地仓库中缺失的提交记录

更新远程分支指针(如 <code>o/main</code>)

<code>git fetch</code> 实际上将本地仓库中的远程分支更新成了远程仓库相应分支最新的状态。

远程分支反映了远程仓库在你最后一次与它通信时的状态,<code>git fetch</code> 就是你与远程仓库通信的方式了!希望我说的够明白了,你已经了解 <code>git fetch</code> 与远程分支之间的关系了吧。

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

git fetch 不会做的事?

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

理解这一点很重要,因为许多开发人员误以为执行了 <code>git fetch</code> 以后,他们本地仓库就与远程仓库同步了。它可能已经将进行这一操作所需的所有数据都下载了下来,但是并没有修改你本地的文件。

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

既然我们已经知道了如何用 <code>git fetch</code> 获取远程的数据, 现在我们学习如何将这些变化更新到我们的工作当中。

其实有很多方法的 —— 当远程分支中有新的提交时,你可以像合并本地分支那样来合并远程分支。也就是说就是你可以执行以下命令:

<code>git cherry-pick o/main</code>

<code>git rebase o/main</code>

<code>git merge o/main</code>

等等

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

<code>git pull</code> 就是 git fetch 和 git merge 的缩写!

OK,我们已经学过了如何从远程仓库获取更新并合并到本地的分支当中。这非常棒……但是我如何与大家分享我的成果呢?

嗯,上传自己分享内容与下载他人的分享刚好相反,那与 <code>git pull</code> 相反的命令是什么呢?<code>git push</code>!

<code>git push</code> 负责将你的变更上传到指定的远程仓库,并在远程仓库上合并你的新提交记录。一旦 <code>git push</code> 完成, 你的朋友们就可以从这个远程仓库下载你分享的成果了!

你可以将 <code>git push</code> 想象成发布你成果的命令。它有许多应用技巧,稍后我们会了解到,但是咱们还是先从基础的开始吧……

*注意 —— <code>git push</code> 不带任何参数时的行为与 Git 的一个名为 <code>push.default</code> 的配置有关。它的默认值取决于你正使用的 Git 的版本,但是在教程中我们使用的是 <code>upstream</code>。 这没什么太大的影响,但是在你的项目中进行推送之前,最好检查一下这个配置。*

现在我们已经知道了如何从其它地方 <code>pull</code> 提交记录,以及如何 <code>push</code> 我们自己的变更。看起来似乎没什么难度,但是为何还会让人们如此困惑呢?

困难来自于远程库提交历史的偏离。

例子

Git学习-图文并茂还有游戏玩!
Git学习-图文并茂还有游戏玩!

很好!但是要敲那么多命令,有没有更简单一点的?

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

其实,你直接 pull push也是一样的,…………

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

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

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

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

IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMine 中的 Git

JetBrains IDEs(比如 IntelliJ IDEA,PyCharm,WebStorm,PhpStorm,RubyMine,以及其他)自带 Git 集成插件。插件在 IDE 中提供了一个专门的页面,可以使用 Git 和 GitHub 的 Pull Request。

该集成插件依赖于 Git 的命令行客户端,所以需要先安装一个 Git 客户端。官方文档请访问: https://www.jetbrains.com/help/idea/using-git-integration.html 。

学习网址:https://learngitbranching.js.org/,闯关的形式,很棒。

关卡答案:https://blog.csdn.net/qq_34519487/article/details/107882290

Git-中文文档:https://git-scm.com/book/zh/v2