你把太多的魔法归咎于树枝。-)
git的工作方式非常简单。一
分支名称
只是一个git commit hash id的名称。
git pull
即使存在,但我们很快就会看到它是什么,以及如何使用它。)
关于提交、哈希ID、分支名称和提交链
让我们稍微讨论一下这些提交哈希ID。hash id是一个由字母和数字组成的难看的大字符串,例如
0d0ac3826a3bbb9247e39e12623bbcfdd722f24c
. 这唯一地标识了一些git对象——通常是commit,当我们处理分支名称时,它总是,肯定是commit。每个提交都记录其
起源
,或前置任务提交。这允许git将提交的内容串成一个向后看的链。
这意味着我们可以
画
这些承诺链。如果让一个大写字母代表一个丑陋的hash id,我们会得到如下结果:
... <-F <-G <-H <--master
这个
名称
master
保存提交的实际哈希ID
H
. 让吉特
找到
G
在存储库内浮动的提交中。从
h
Git可以得到哈希ID
G
,这就是
H
的父母。所以现在Git可以找到
G
。使用
G
,Git可以找到
F
,等等,向后,向下。这里的箭头可以读为
指向:
主人
指向
h
,
H
指向
G
等等。
每次提交的内容完全、完全、100%冻结/只读。任何承诺都不能改变。所以我们不需要画内部箭头。但是,分支名称
做
改变。git添加
新的
致力于
主人
是写出一个commit对象,存储
H
新对象中的哈希ID,以及新的提交快照和任何其他
元数据
喜欢你的名字,电子邮件地址和日志信息。这将产生一个新的散列,我们将调用它
I
而不是试图猜测:
...--F--G--H--I
现在git只需要编写
我
在名字里
主人
,这样
主人
现在指向
我
:
...--F--G--H--I <-- master
如果你有多个分支,或者你有多个
远程跟踪名称
喜欢
origin/master
和
origin/BranchA
,我们只需要把它们都画出来:
...--F--G--H <-- master, origin/master
\
I--J <-- origin/BranchA
(稍后我们将详细讨论远程跟踪名称。它们有点像树枝的名字,但有点扭曲。)
当您创建一个新的分支名称时,git所要做的就是使新名称指向一些现有的提交。例如,让我们创建自己的
BranchA
现在,使用
git checkout BranchA
:
一
...--F--G--H <-- master, origin/master
\
I--J <-- BranchA, origin/BranchA
现在让我们创建
testBranch
同时,还指出
J
:
...--F--G--H <-- master, origin/master
\
I--J <-- testBranch, BranchA, origin/BranchA
如果您现在创建一个新的提交,您的git需要知道
要更新哪个分支名称
. 所以你的Git有个特别的名字,
HEAD
,所有大写字母都是这样写的。
二
Git将此名称附加到您的一个分支名称:
...--F--G--H <-- master, origin/master
\
I--J <-- testBranch (HEAD), BranchA, origin/BranchA
也就是说
测试分支
是
现在的
分支,因此是
git将更新的名称
当你奔跑
git commit
做出新的承诺。其中之一
git checkout
做的就是管理这个头部附件。
一
自从你
没有
一
布兰查
,你可能会想:
我怎样检查它?
事实上,你
应该
想一想:这是个很好的问题。答案是你的git会
创造
你自己
布兰查
从远程跟踪名称
。所以你必须
git checkout -b testBranch
但不是
git checkout -b BranchA
:
-b
旗帜说
创造
,如果没有它,git将只在名称不存在时创建
和
有一个远程跟踪名称
做
存在看起来是对的。不止这些,这是个好的开始。
二
由于怪癖,通常可以使用小写字母。
head
在windows和macos上,但在类unix系统(如linux)上没有。最好避免这种习惯,因为它在linux上不起作用:如果您不喜欢键入
头
在所有大写字母中,使用
@
,这是魔法名称的同义词。
远程跟踪名称,或者,当有人在其他Git存储库中进行提交时会发生什么情况?
关于这些分支名称的问题是
具体到
你的
Git存储库
。你的
主人
是
你的
主人
。你的
布兰查
是你的
布兰查
你的
测试分支
也是你的。他们不会改变除非
你
改变它们。
事实上,甚至你的远程跟踪名称
原点/主
和
产地/布兰卡
也是你的,但让它们成为远程跟踪名的原因是你的git会
自动
更改它们,以记住您的git在其他git中看到的内容,只要您的git调用它们的git并询问它们
他们的
分支名称。也就是说,您的git有其他git存储库的url,列在
遥远的
名称
origin
:
起源
是一个很长的短名称,可能很难键入url。你可以运行:
git fetch origin
你的git将调用他们的git,在下面列出的url
起源
,并询问他们的git
他们的
树枝。他们会说:
哦,当然,给你:我的
主人
是<hash1>和我的
布兰查
是<哈希2>。
(要看这个,跑
git ls-remote origin
,就像
git fetch origin
不过,在获得远程名称和散列的列表后,它只是将它们打印出来。)
有了这个列表,你的git会继续向他们的git索要任何
新的
提交
他们
你没有。所以如果他们更新了
他们的
布兰查
,你得到他们的新承诺。然后,不管发生了什么,您的git现在设置所有
远程跟踪名称
一开始
origin/
。也就是说,假设他们有两个新的承诺。您自己的存储库现在如下所示:
...--F--G--H <-- master, origin/master
\
I--J <-- testBranch (HEAD), BranchA
\
K--L <-- origin/BranchA
你自己
布兰查
和
测试分支
没有移动
。这些是
你的
树枝,所以它们只有在
你
移动它们。你的
原点/主
因为他们的
主人
没有动,但是你的
产地/布兰卡
有
感动,记住新的承诺
L
你刚从他们那里得到的,因为
他们的
布兰查
确实移动了,现在指向同一个提交
L
.
(请记住,我们的大写字母代表实际的大而丑陋的唯一散列ID。如果他们做了新的提交,而您又做了新的提交,git保证
他们的
新的哈希ID与
每一个
你做的新提交哈希!您可以看到,使用一个活动的存储库,单个大写字母会跑得太快,而且很难使其成为唯一的。但它们更容易绘制,也更容易让我们讨论提交,所以我在这里使用它们。)
让你的分支名称移动
现在他们已经更新了
他们的
布兰查
,你可能想拥有自己的
布兰查
也可以移动。这就是事情开始变得复杂的原因,但是让我们来看一个简单的方法。
我们从跑步开始
Git签出Brancha
再一次。这将附加
头
到
布兰查
,以便使用
当前分支
正在使用
布兰查
。那我们就用
git merge
,在这种情况下,实际上不会进行任何合并!
git checkout BranchA
git merge origin/BranchA
之前
合并分支
,我们的存储库中有:
...--F--G--H <-- master, origin/master
\
I--J <-- testBranch, BranchA (HEAD)
\
K--L <-- origin/BranchA
这个
合并分支
看
产地/布兰卡
发现它指向
L
. 看看我们现在的分行
头
附加到“并发现它指向
J
。它意识到,从
L
工作向后,它可以直接
J
. 这意味着分支名称
布兰查
可以是“向前滑动”,就像是逆着内部的方向,向后指向的箭头。Git将此操作称为
快进
。在
合并分支
,更像是
GIT校验
移动当前分支名称。也就是说,承诺
L
成为
当前提交
,但它是通过
移动名称
布兰查
. 结果是:
...--F--G--H <-- master, origin/master
\
I--J <-- testBranch
\
K--L <-- BranchA (HEAD), origin/BranchA
你现在有承诺了
L
作为你当前的承诺
L
正在填充
指数
以及
工作树
. 是时候谈谈这两个了。
索引和工作树
我们已经提到,提交中存储的文件完全、完全、100%冻结/只读。它们以一种特殊的、压缩的、仅限git的格式存储。这使git可以节省大量空间,并重用未更改的文件:如果新提交的文件与前一次提交的文件基本相同,则无需保存所有文件。旧的提交的副本被冻结,因此新的提交可以
分享
他们。(这个过程的细节在这里并不重要,但是git使用hash id,git调用的是
BLB对象
,以实现此技巧。)
这对于Git来说是很好的,但是我们不能使用冻结的压缩Git文件来做任何事情。
其他的
。所以Git必须解冻和解压缩被冻结的文件,变成它们的日常形式,这样我们和我们计算机上的其他程序就可以了。
使用
他们。
解冻后的文件进入
工作树
,这就是所谓的,因为那是我们研究它们的地方。在这里,我们可以做任何我们想用我们的文件。因此,对于每个文件,当前提交中都有一个冻结的副本,而工作树中有一个解冻的副本。(其他提交中也可能有冻结副本,但
现在的
提交是最有趣的,因为我们可以并且经常将它与工作树中的一个进行比较。
这个
指数
,也被称为
分级区
或者有时
隐藏物
,是Git特有的。其他版本控制系统也有冻结的提交和解冻的工作树,但也没有
有
一个索引,或者完全隐藏任何索引,这样你就不需要知道它了。另一方面,git会时不时地用索引打你的脸。你
必须
知道它,即使你不把它用于花哨的把戏。
从本质上说,索引保存的是每个文件的副本。也就是说,每个文件
当前提交
是
也
在索引中。索引副本是特殊的git-only格式。不过,与冻结提交副本不同的是,如果你愿意的话,这个副本只是半冻结的。你可以
代替
任何时候都可以用一个新的,不同的,合法的,半冷冻的拷贝。就是这样
git add
does:it git确认文件的工作树副本,将其压缩为git-only格式并替换以前的索引副本。(如果新的匹配
任何
旧版本,在任何冻结的git提交中,它最终都会重新使用旧版本:节省空间!否则它就是一个新的git副本。)
制作一个
新的
在git中,commit只需要快速冻结这些索引副本。他们已经准备好了,这也是为什么
GIT提交
比其他版本控制系统快得多。但这也意味着指数可以被描述为
下一个任务是什么
。git从索引而不是从工作树构建新的提交。
你需要工作树来处理你的文件。git需要并使用索引来进行新的提交。索引和工作树副本可以不同,它是
你的
工作到
Git添加
在提交之前,工作树将复制以用更新的索引副本覆盖索引副本。
更新您的
测试分支
现在,让我们看看如何更新
测试分支
. 记住,我们跑了
git fetch
更新我们所有的
origin/*
名字,然后
Git签出Brancha
和
git merge origin/BranchA
更新
布兰查
,所以我们现在有了这个:
…--f--g--h<--master,源/主
\
i--j<--testbranch公司
\
K--L<--Brancha(头),产地/Brancha
我们现在需要
git checkout testBranch
附
头
对它。然后我们可以跑
git merge BranchA
或
Git合并源/分支
:
git checkout testBranch
git merge <anything that identifies commit L>
这里的想法是让git看看commit
L
。然后merge命令将查看是否可以执行它所执行的快速转发操作
布兰查
。答案是肯定的:从提交
J
直接承诺
L
。因此,在默认情况下,git会这样做,您将得到:
...--F--G--H <-- master, origin/master
\
I--J
\
K--L <-- testBranch, BranchA, origin/BranchA
请注意,我们可以做到这一点,即使我们
永远不要创造我们自己的
布兰查
,因为不是
Git合并Brancha
我们可以跑
Git合并源/分支
。也就是说,如果我们有:
...--F--G--H <-- master, origin/master
\
I--J <-- testBranch (HEAD)
\
K--L <-- origin/BranchA
并运行
Git合并源/分支
,git将执行与使用名称的版本完全相同的快速转发。
布兰查
指向提交
L
.
这里重要的不是分支名称,而是提交。
好吧,我们自己的分支名称,比如
测试分支
,重要的是,我们需要让他们指出他们应该去的地方;但是
其他
名称远程跟踪名称我们只使用它们
查找提交
. 他们只是
可读性更强
而我们的git会
自动更新
吉特取出
.
因此,假设我们从未创造
布兰查
首先。假设我们做了:
$ git clone <url>
$ cd <repository>
$ git checkout -b testBranch origin/BranchA
... wait until colleague updates origin/BranchA ...
$ git fetch # defaults to using origin
$ git merge origin/BranchA
那我们就完蛋了,不用再摆弄了
我们的
布兰查
我们从未创造过。
如果你自己作出承诺的话,我就不提发生了什么。在这种情况下,可以得到一个真正的合并
合并分支
会看到的
不
可能只是快进,并将运行合并过程,然后进行类型为的提交
合并提交
. 相反,让我们来解决这个难题的最后一点,
GIT拉力
.
关于
GIT拉力
(不要用它!)
我的建议
GIT拉力
作为一个初学者,你应该努力学习
避免
它。但是,其他人和文档会告诉您使用它,所以您至少应该知道它的作用。所有这些
GIT拉力
是和做就是跑
二
git命令。这是为了方便。问题是,有时它很方便,有时它很明显
不
-方便。在我看来,最好先学会使用两个底层git命令。
第一个git命令
GIT拉力
跑步就是
吉特取出
. 我们已经看到了:它会调用其他git,从中获取
它的
分支名称(和标记名称)和散列ID,并将您需要的任何提交带入您的存储库,以便您的Git可以更新所有
远程跟踪名称
. 然后就完成了:索引和工作树没有发生任何事情。跑起来很安全
吉特取出
随时
,因为它只是添加新的提交和更新远程跟踪名称。
这个
第二
命令
GIT拉力
跑步是麻烦的来源。你可以选择
哪一个
它运行的第二个命令。通常,那是
合并分支
,这就是我们上面看到的。但你可以让它运行
git rebase
,我们在这里没有讨论过。
不管是哪种情况,
GIT拉力
将一些额外的参数传递给
合并分支
或
GIT重碱
命令。这些额外的争论造成了一些不便,因为它们
不同的
从你可能想要使用的参数。尤其是,如果您运行:
git pull origin master
这具有运行的效果:
git fetch origin master
git merge -m "merge branch 'master' of $url" origin/master
注意,最后一个argumentgit中的斜杠将合并现在由
原点/主
. 这个
-m
(消息)包含从
起源
,加上名字
主人
,而不是名字
原点/主
但是
影响
对于合并,快进或实合并与合并更新的远程跟踪名称相同,
原点/主
.
三
如果你使用单独的
吉特取出
和
合并分支
命令,它们更有意义。当你使用
GIT拉力
,列出的分支名称(如果列出一个分支名称)是
在另一个Git上
,而不是中的远程跟踪名称
你的
Git。
同样的道理即使你有
GIT拉力
运行
GIT重碱
为你。在生命的最后一刻
不
-方便,决定是使用merge还是rebase是您有时应该做的决定。
之后
运行
吉特取出
. 也就是说,你应该看看
什么
吉特取出
取
,以决定要运行的第二个命令。但是如果你用
GIT拉力
,你必须做出这个决定
之前
你跑
吉特取出
所以你
不能
看。
一旦你使用了git一段时间,并且对两者都非常熟悉
合并分支
和
GIT重碱
,
然后
你可以开始使用
GIT拉力
安全地。(但我基本上还是没有。)
三
还有一个问题,Git的老版本:在Git版本1.8.4之前,
GIT拉力
没有
更新远程跟踪名称。现代的git消除了这种奇怪的怪癖,但是有些系统仍然使用非常旧的git版本,所以了解它很重要。