我受到Nico Riedmann 的 Learn git concepts, not commands 的启发,我用我自己的方式总结了 git。当然,我也通过阅读官方文档来补充它。
从系统结构上理解git,让git更有趣。我最近对 git 上瘾了,以至于我正在创建自己的 git 系统。
我学习git的仓库
现在我正在尝试制作类似 git 的系统并了解,
基于 Git in Rust 的原始版本管理系统正在处理!
什么是 Git?
管理版本和分配工作
使用 Git 意味着
形象理解
开始新的工作
资料库
复制存储库并开始工作
(补充)工作目录
更改和添加文件
适配远程仓库
查看差异
(旁白)一个台阶称为暂存区
概括
分支
创建新分支
在分支机构工作
(旁白)Git-Flow 和 GitHub-Flow
概括
合并
快进
没有快进
处理冲突
删除不需要的分支
(旁白)早午餐是什么?
概括
变基
移动分支
处理变基冲突
使本地存储库保持最新
分支和存储库
查看最新状态
更新到最新状态
处理拉取冲突
(旁白)拉取请求的身份
实用功能
更正提交
删除提交
撤离工作
带来提交
掌握头部
结尾
没有 Git 的源代码管理
远程仓库在哪里
指针
进一步了解Git
参考
什么是 Git?
管理版本和分配工作
Git 是一种称为分布式版本控制系统的源代码管理系统。Git 是一种通过记录和跟踪文件的更改日志(版本)、比较过去和当前文件以及阐明更改
来促进开发工作的工具。 该系统还允许多个开发人员同时编辑文件,因此可以分配工作。
使用 Git 意味着
首先,在您的计算机(以下称为“本地存储库”)上的每个人都可以共享的存储位置(以下称为“远程存储库”)中复制该文件或其他文件,并且然后添加或编辑新代码或文件。
然后,文件将通过将它们从本地存储库注册到远程存储库来更新。
通过图像理解
在与 Git 打交道时,重要的是要遵循“如何工作”从“什么”到“什么”。
如果你只操作命令,你可能不明白发生了什么而使用错误的命令。
(信息)
在操作 Git 时,试着想象操作前后发生了什么。
开始新的工作
资料库
Git 中的存储库是文件的存储,可以是远程的也可以是本地的。
Remote Repository是将源代码放在互联网上的服务器上并可以被所有人共享的存储库。
本地存储库是源代码位于您的计算机上的存储库,只有您可以进行更改。
复制存储库并开始工作
首先,准备好自己的开发环境。
您需要做的只是决定您将在哪个目录中工作。
例如,您的主目录或您通常使用的任何目录都可以。
接下来,从远程存储库复制并引入文件。
这叫做clone。
调用的远程存储库project仅包含first.txt,这是您clone访问远程存储库时的图像。
(信息)
当然,你也可以先创建一个本地仓库,然后再反映远程仓库。
这称为initialize并允许您将已经在处理的目录转换为存储库。
(补充)工作目录
工作目录不是任何特殊目录,而是您始终在计算机上工作的目录。如果您将其视为一个目录,您可以在其中使用 Git 暂存区或本地存储库
连接到 Git 管理的目标目录(在本例中为 ),这样会更容易理解。project
更改和添加文件
对源代码的更改是通过工作目录(暂存区)进行的。
实际上,我们在工作目录中工作。
让我们创建一个名为second.txt.
接下来,将修改后的文件移动到暂存区。
这叫做add。
Git 的一个特性是在更改反映到本地存储库之前有缓冲。
稍后我将更详细地解释为什么存在此缓冲垫。
然后,我们将暂存区中的内容注册到本地存储库。
这叫做commit。
顺便说一下,我们可以在您发表评论时commit。
在这种情况下,我们添加了一个文件,所以写git commit -m 'add second.txt'.
(信息)
提交时,会在存储库中创建一个提交对象。
提交对象的简单解释是具有更新者信息和修改文件的数据。
(所有数据都被保存,不仅仅是差异,而是当时文件的整个状态(快照)。有关Git 对象的更多信息,
请参阅Git 对象。
适配远程仓库
然后,工作完成了!
最后一步是将本地存储库中的更改反映到远程存储库中。
这叫做push。
如果将其视为对远程存储库的提交,可能会更容易理解。
查看差异
同一文件之间的更改称为diff.
我们可以看到文件中的变化点。
我不会详细介绍这些命令,但这里有三个我经常使用的命令。
git diff --stage在您之前查看原始工作目录的更改add。
git diff --stage后查看工作目录的更改add。
git diff <commit> <commit>比较提交。
(旁白)一个台阶称为暂存区
随着开发工作的增长,我们经常在一个工作目录中进行许多更改。
如果您一次将所有更改放在本地存储库中,会发生什么情况?
在这种情况下,在解析提交时,您可能不知道某个功能是在哪里实现的。
在 Git 中,建议为commit每个功能做一个。
这就是为什么有一个暂存区,您可以在其中将commit单元细分为更小的单元。
Git 的概念是只暂存需要的东西,然后继续工作或commit提前促进高效的开发,可以追溯到每个实施的历史。
概括
基本的工作流程是clone一次,一次,一次,add每次commit工作push。
动图
(信息)
clone:从远程存储库复制到您的开发环境(本地存储库和工作目录)。
add:将工作目录中的文件添加到暂存区并准备提交。
commit: 将文件从暂存区注册到本地仓库。这时候,一个commit对象就创建好了。
push: 将更改从本地存储库注册到远程存储库。
分支
我们创建一个branch来更改和添加多个分支中的文件。
保存在main分支中的文件正在使用中。
分开分支的原因是在不影响当前运行的源代码的情况下工作。
创建新分支
让我们创建名为develop!
我们可以用git branch <new branch>or创建一个分支git checkout -b <new branch>。
前者只是创建一个分支,后者创建一个分支并将您移动到该分支。
(分支在存储库中维护。)
生成分支的关键点是从哪个分支派生。
我们可以将源指定为git checkout -b <new branch> <from branch>.
如果我们不这样做,您当前正在处理的分支将成为<from branch>.
(信息)
分支实际上是指向提交的指针(严格来说,是提交对象的散列)。
生成一个新分支意味着新分支指示 from 分支也指向的提交。
在分支机构工作
移动分支称为checking out。
指向您当前正在处理的分支的指针称为HEAD。
因此,从一个main分支移动到另一个develop分支意味着改变HEAD.
现在两个分支都指向名为 的提交Atr3ul。
您只是second.txt通过在main分支中提交来添加,所以您领先于 commit f27baz。
从这里开始,假设您second.txt在develop分支中进行了更改并进行了新的提交。
然后,如图所示,该develop分支创建了一个名为 commitm9sgle并指向该 commit。
当前的 HEAD 位置(工作分支位置),文件已经工作到什么阶段,或者谁在工作的状态被调用status。
(信息)
如果您熟悉面向对象,您可能会理解提交上箭头的原因。
它表示“父”提交和“子”提交之间的关系。
假设是parent←-child,即从父项 (commit) 出生的子项 (commit) 增长了(改变)了多少。
(旁白)Git-Flow 和 GitHub-Flow
分支机构的管理方式因开发团队而异。
另一方面,就像编程命名约定一样,在 Git 中有一个关于如何增长分支的通用模型。
这里有两个简单的。我想知道有这么个东西就够了。
“Git Flow”是一个相当复杂和错综复杂的结构。
我认为这是应该如何使用 Git 的模型。
每个分支的定义。
master: 发布产品的分支。没有在这个分支上工作。
development:分支开发产品。准备发布时,合并到release. 没有在这个分支上工作。
feature:用于添加功能的分支,准备发布时合并到开发中。
hotfix: 对于紧急的发布后工作(关键错误修复等),从 master 分支出来,合并到 master,并合并到 develop。
release: 用于准备产品发布。develop具有要发布的功能和错误修复的分支。
准备好发布时,合并到 master 并合并到 develop。
“GitHub Flow”是 Git Flow 的简化模型。
如您所见,它仅由master和组成feature。
重要的区别是缓冲pull requests(在下面的拉动中解释),它允许分支之间的集成。
概括
基本上,由于 main (master) 上没有工作,我们为每个我们想做的工作单元创建一个分支,并创建一个新的提交。
动图
(信息)
branch:指向提交的新指针
checkout:移动HEAD以更改branch要继续的工作。
合并
合并分支称为merge。
基本上,我们合并到mainordevelop分支。
注意不要搞错哪个分支的主题是合并(吸收)哪个分支。
我们总是会把(HEAD)移动到你派生的分支上,然后从你派生的分支做整合。
我目前在feature分支机构工作并创建了以下third.txt.
第三个.txt
Hello, World! I'm noshishi, from Japan.
I like dancing on house music.
然后我们add完成了commit。
快进
当feature分支指向可以追溯到该分支的提交时develop,该develop分支就处于一个fast-forward状态。
首先,移动到developwith checkout。
在这种情况下,develop分支根本没有进展,所以到merge分支feature只会将提交向前移动。
在这种情况下,分支develop和feature分支共享相同的提交。
没有快进
如果develop分支已经通过提交或合并进行到新的提交怎么办?
这称为no fast-forward情况。
在develop分支中,您已对 进行了更改first.txt并已完成commit。
所以develop分支和feature分支是完全分裂的。
如果您尝试从一个merge分支转到另一个分支,Git 将相互检查您的变更日志。 如果没有冲突的编辑,则立即创建 a。 这称为.featuredevelop
merge commit
automatic merge
处理冲突
在no fast-forward状态上,工作内容的差异。被称为conflict。
在这种情况下,我们必须手动修复conflict内容和commit.
在develop分支中,我们创建了以下third.txt和committed.
第三个.txt
Hello, World! I'm nope, from USA.
I like dancing on house music.
在develop分支机构中,I'm nope, from USA。
在feature分支机构中,I'm noshishi, from Japan。
第一行的内容有冲突。
如果你merge此时做a,conflict就会发生a。
Git 会commit在解析conflict.
(我们工作的分支是developbranch)
如果您third.txt按照说明查看,您将看到以下添加内容
third.txt (冲突后)
<<<<<<<< HEAD
Hello, World! I'm noshishi, from Japan.
=======
Hello, World! I'm nope, from USA.
>>>>>>>> feature
I like dancing on house music.
上面HEAD用 隔开=======,代表develop分支的内容。
下方代表feature分支。
你先考虑采用哪一个,决定采用这次feature分支中所做的修改。
唯一的操作就是third.txt手动编辑(删除不需要的部分)。
third.txt(编辑后)
Hello, World! I'm noshishi, from Japan.
I like dancing on house music.
接下来你要做的是addand commit。
已conflict解决并merge commit创建了一个新的。
初学者最怕的就是冲突,学会了这个,就不怕了。
(信息)
如果你merge解决了conflict,为什么不再merge呢?
当你merge一次,develop分支进入merge状态,如果没有conflicts,新文件自动added和commit。
所以不是特别解决commit后conflict。
这就是为什么它被称为merge commit。
删除不需要的分支
合并的分支基本没什么用,我们就删掉。
如果我们单独留下一个分支,您可以从要删除的分支移动到另一个分支并git branch -d <branch>.
您可能认为该分支上的提交已被删除。
事实上,提交被转移到合并的分支。
您可以使用它git log来查看您在分支上所做的所有提交以及在合并分支上的提交。
(旁白)早午餐是什么
我们说分支是指向提交的指针,但它还保存着另一个重要的数据。
这是在该分支上所做的所有提交。
分支是提交的集合,它有一个指向该集合中最新提交的指针。(严格来说,提交可以追溯到以前的提交。)
下图说明了这一点。
所以我们可以把分支想成像 Git Flow 这样的水平轴上的分支。
顺便说一句,如果你把上面的图画成水平轴上有分支,它看起来像这样。
概括
fast-forward merge
动图
no fast-forward merge
动图
no fast-forward merge with conflict
动图
(信息)
merge:将一个工作分支(如feature)整合(吸收)到一个特定的分支(如main或develop)并创建一个新的提交。
变基
Rebase是通过更改派生分支的提交来合并分支的过程。
它类似于merge,只是您正在处理的分支是目标分支。
假设您正在处理develop和feature分支。
移动分支
您可能会考虑将分支上的当前提交反映develop到feature分支中。
您需要将feature分支从gp55sw提交移动到3x7oit提交。
这可以feature通过执行 a 立即从分支中移动git rebase develop。
这个过程更像是feature从分支上的最新提交重新增长分支,而develop不是做一个merge.
不同之处在于您移动了整个提交并进行了新的提交。
这样做的原因之一是它在任何时候都很fast-forward容易。 另一个原因是提交是对齐的,这样可以很容易地跟踪提交历史,并且文件更新的顺序是一致的。merge
处理变基冲突
当然还有一个conflictin rebase。
您fourth.txt在feature分支中添加,但您没有fourth.txt在develop分支中进行更改。
有conflict。
但是,如果下面的变化相互覆盖,conflict就会发生。
您可以像处理merge.
但是,在检查差异并完成文件编辑后,您应该使用git rebase --continue.
你不必commit,它会自动提交。
(信息)
rebase:将派生分支所在的提交移动到新的提交。
使本地存储库保持最新
在完成一些本地工作后,您可能会遇到远程存储库已被其他开发人员更新的情况。
在这种情况下,您可以使用pull将远程存储库中的信息重新安装回本地存储库。
分支和存储库
分支存储在每个存储库中。
这是完成实际工作的分支。
另一方面,本地存储库具有远程存储库的复制分支。
这称为“远程跟踪分支”。
它是一个名称与远程分支相关联的分支remotes/<remote branch>。
这只是监视远程存储库。
查看最新状态
假设您遇到这样一种情况,develop远程存储库中的分支比远程跟踪分支领先一步。
在远程跟踪分支上反映远程存储库中分支的最新状态称为fetch.
更新到最新状态
如果你想让它反映在你当地的分支机构中,你可以做一个pull.
当你pull,本地远程跟踪分支首先被更新。
然后merge到当地分行。
这一次,有一个提交在该分支之前进行了一个分支,因此您在本地分支中develop创建了一个新提交。mergedevelop
处理拉取冲突
当远程存储库提交与本地存储库提交冲突时,您将面临conflict远程跟踪分支和本地分支之间的冲突pull。
在下面的例子中,remotes/develop和develop分支是冲突的。
由于push是fetch和merge,您可以用与 中相同的方法conflict求解merge。
这次,develop merges remotes/develop,所以工作分支是develop。
打开导致问题的文件夹以及commit修复它的时间。
(旁白)拉取请求的身份
基本上,远程和本地的关系就是从远程仓库pull到本地仓库,从本地仓库push到远程仓库。
但是,GitHub 和其他服务有一种机制,可以在从远程存储库中的分支合并到 main 等分支之前发送请求。
这是因为如果开发人员推送到主分支并更新远程存储库,则没有人可以检查它,并且可能会发生重大故障。
Pull request就是插入一个流程,更高级别的开发人员检查一次代码。
(信息)
pull:fetch+ merge。pull是为了在本地仓库中反映出远程仓库的状态。
实用功能
更正提交
更正以前的commit提交称为. 例如,假设您使用.revert
second.txtm9sgLe
当你revert时,提交被撤销并且second.txt不再在本地存储库中。
的优点revert是可以让你离开commit。这个和后面介绍
的区别。reset
删除提交
撤消当前的最新提交并再次对其进行处理称为reset.
该--soft选项允许您在 之后立即返回舞台add。
该--mixed选项允许您返回到您在工作目录中工作的阶段。
该--hard <commit>选项会删除直到您要返回的提交点之前的所有提交,并移至head指定的提交。
由于reset 完全删除提交,建议您除非有充分的理由,否则不要使用它,尤其是对于 '--hard' 选项。
如果您想取回提交,可以使用git reflog查看已删除的提交。
撤离工作
由于如果有更改文件则无法移动到另一个分支,因此您必须在转到commit或放弃更改之间做出选择。
这是stash派上用场的地方。
您可以临时撤出工作目录或暂存区中的文件。
当你想移动到另一个分支时,stash当你返回时,使用它stash pop来检索撤离的文件并恢复工作。
带来提交
将任何提交带到当前分支以创建提交称为cherry-pick。
这是一个非常好的功能。
例如,当您只想带回以前在feature分支中实现的功能并将它们用于当前develop分支中的工作时,可以使用此方法。
掌握头部
我解释说 HEAD 是指向您当前正在处理的分支的指针。
我还解释了分支是指向提交的指针。
见下图。
HEAD 指向develop分支,develop分支指向提交eaPk76。
因此,在这种情况下, HEAD 指的是 commit eaPk76。
您是否经常看到在命令后使用的 Git 文档或文章HEAD?
例如,git revert HEAD。
这是一个可以实现的命令,因为你可以HEAD用commit替换。
结尾
没有 Git 的源代码管理
Mercurial 与 Git 有着相同的历史。
Mercurial 有一个非常简单的命令行界面 (CLI),牺牲了 Git 的灵活性。
最近,Meta 基于 Mercurial 开源了一个名为 Sapling 的新源代码管理系统。
我想再试一次,写下我的印象。
远程仓库在哪里
托管服务是为远程存储库租用服务器的服务。
典型的例子有 GitHub、Bitbucket 和供私人使用的 Aws Code Commit。
Git 和 Git Hub 是完全不同的。
顺便说一句,如上所述,我们可以使用自己的服务器来进行远程存储。
指针
如果您接触过直接与内存打交道的编程,例如 C 编程语言,您就会知道什么是“指针”。
另一方面,对于初学者来说,这似乎很模糊。
我说过提交对象存储在存储库中。
如果存储库中有很多提交对象,您如何选择您想要的?
我们需要一个标签(地址)来定位特定的提交对象。
“指针”是一种有价值的数据,它向我们指示标签,以便我们不会忘记它。
顺便说一下,标签是通过一个hash function.
如果您好奇,请参阅Git 如何计算文件哈希?.
进一步了解Git
有很多事情我在这篇文章中没有提到。
Git的核心是一个简单的key-value类型的数据存储
作为值的 Git 对象的详细信息
如何与每个对象相关联。
我希望有一天能充分探索这一点。
参考
Git 文档
学习 git 概念,而不是命令