试图了解git跟踪,分期等

时间:2017-07-29 23:11:03

标签: git github commit definition staging

我已经看到很多线索,但答案往往是不一致和矛盾的,所以如果可能的话,我会在不确定的条件下尽可能简短的解释:

根据我目前的理解,我们有一个工作目录我们正在制作项目的文件,临时区域存储库。暂存区域在技术上也是索引,并使用缓存,只是工作目录中为提交<设置的所有文件中的一个大列表/ strong>,将增量更改复制到存储库。

因此,例如我有一个文件test.txt,我在其中写入“1234”并将其添加到临时区域然后将其提交到repo,因此我假设整个test.txt是保存在某处的repo文件中。然后我编辑文件并将文本更改为“1235”并提交更改。我认为repo现在不会保存另一个“1235”的副本,但只是注意到“第四个字符从'4'变为'5'”等等。

无论如何,在我开始之前,test.txt 未跟踪。然后,当我git add test.txt时,它现在变成跟踪的文件,文件以及索引上的暂存文件

然后,如果我做git commit -m "some message"文件变成......什么?承诺?跟踪但不上演?

然后,如果我再次编辑该文件,它将变为跟踪已修改,但不一定上演,除非我再次添加,等等?

我试图理解什么被定义为何处。到目前为止我的理解是否正确?我在哪里需要更正?

1 个答案:

答案 0 :(得分:1)

在深入研究其余部分之前,跟踪文件的定义很简单:当且仅当文件当前在索引中时才会跟踪文件。

您需要测试的全部内容:&#34;索引中的路径 P ?&#34;如果是,则跟踪 P 。如果没有, P 未被跟踪(因此只是未跟踪,或者可能未跟踪并且也被忽略)。

棘手的部分是弄清楚索引中的内容!

直接问答

  

根据我目前的理解,我们有一个工作目录我们正在制作项目的文件,临时区域存储库。暂存区域在技术上也是索引 ...

是。存储库本身就是一种数据库;在这个数据库中,我们有四种类型的对象。此时最有趣的是提交对象, 1 ,它们充当快照,通过保存 - 作为永久副本,以及一些附加信息 - 索引中的任何内容你运行git commit的时间。第二个最有趣的是 blob 对象。 Blob有多种用途,但主要用于存储文件内容,我们稍后会看到。

至关重要的是,总会有当前提交。您可以通过多种方式命名,但始终有效的方法是单词HEAD。符号@通常也有效(它只能在真正古老版本的Git中失败)。然而,哪个提交是&#34;当前&#34;随着时间的推移而变化。

  

...并使用缓存

缓存只是索引/暂存区的第三个术语。

  

只是工作目录中为提交设置的所有文件的一个大列表,它将增量更改复制到存储库。

这不太对。

index / staging-area / cache中的东西的格式通常不是很有趣(并且记录很少并且也可能会发生变化),但是如前所述,每次提交都充当完整的快照,不是变更集。

  

例如,我有一个文件test.txt,我写了#34; 1234&#34;在它内部并将其添加到临时区域然后将其提交到repo,因此我假设整个test.txt都保存在某个repo文件中。

它是1234\n数据,它被保存在对象中,特别是上面提到的一个blob对象。这并不一定意味着 file 。存储库对象具有内部格式,对其进行相对较少的承诺。如果您想深入研究这些细节,您应该知道它们可能存储松散(每个文件一个存储在单独的文件中),或打包(许多在一个包文件中,使用与索引/暂存区/缓存索引无关的包索引。

承诺,您可以使用git cat-file --batch(使用git cat-file -p生成原始数据并使用有点棘手的方法)将任何ID对象提取回其原始格式。 git cat-file -p <object-id>(这会产生漂亮的印刷版本并且易于使用)。对于标记和提交对象,原始数据通常已经可打印,因此git cat-file -p按原样打印它。树对象是混合的,因此Hello world将已知的二进制内容转换为文本。 Blob对象完全按原样保存。 2

  

然后我编辑文件并将文本更改为&#34; 1235&#34;并承诺改变。我认为回购现在不会保存另一份&#34; 1235&#34;但只是注意到了#34;第四个角色改变了&#39; 4&#39;到&#39; 5&#39;&#34;什么的。

不,这是非常错误的。新提交有一个新的完整的新内容副本。此外,每个blob都是该特定文件内容的特定版本的完整快照,而不管文件的名称如何。 (如果这两个blob都是松散的对象,并且两者都是非常大的文件 - 比如每个4.5 GB的DVD数据,并且不是很可压缩 - 那么你只需要为这两个松散的对象使用9 GB的磁盘空间。请参阅下面的对象压缩和打包部分,了解这是否存在问题。)

如果将相同的内容存储在两个单独的提交中,无论是使用相同的名称还是使用不同的名称,只需存储blob一次。即使您在一次提交中存储两个文件,这也成立。例如,仅包含文本3b18e512dba79e4c8300dd08aeb37f8e728b8dad(和换行符)的任何文件的哈希ID为$ echo hello world | git hash-object -t blob --stdin 3b18e512dba79e4c8300dd08aeb37f8e728b8dad

hello world

如果您制作的六个文件只包含一行tree,则您的$ for i in 1 2 3 4 5 6; do > echo hello world > copy_$i.txt; git add copy_$i.txt > done $ git commit -m 'six is just one' [master (root-commit) 5a66ef1] six is just one 6 files changed, 6 insertions(+) create mode 100644 copy_1.txt create mode 100644 copy_2.txt create mode 100644 copy_3.txt create mode 100644 copy_4.txt create mode 100644 copy_5.txt create mode 100644 copy_6.txt $ git cat-file -p HEAD^{tree} 100644 blob 3b18e512dba79e4c8300dd08aeb37f8e728b8dad copy_1.txt 100644 blob 3b18e512dba79e4c8300dd08aeb37f8e728b8dad copy_2.txt 100644 blob 3b18e512dba79e4c8300dd08aeb37f8e728b8dad copy_3.txt 100644 blob 3b18e512dba79e4c8300dd08aeb37f8e728b8dad copy_4.txt 100644 blob 3b18e512dba79e4c8300dd08aeb37f8e728b8dad copy_5.txt 100644 blob 3b18e512dba79e4c8300dd08aeb37f8e728b8dad copy_6.txt 对象(请参阅脚注1)将有六个与此哈希ID关联的名称:

test.txt

我们有:六个文件,一个对象。

除此之外,让我们说你的$ echo 1234 | git hash-object -t blob --stdin 81c545efebe5f57d4cab2ba9ec294c4b0cadf672 文件有1234加上换行符。它的哈希ID是:

1234\n

Universe中的每个文件 - 更准确地说,将在您的存储库 3 中具有内容test.txt的所有文件的Universe都具有此哈希。它是否被命名为1234\n并不重要。制造它或何时制造它并不重要。如果它存储在本地驱动器或云端中并不重要。 4 重要的是1235\n哈希到上面的数字。

同时,$ echo 1235 | git hash-object -t blob --stdin d729899c33fcf5c75fda5369a64898c85a46bcf7 哈希到不同的数字:

1235\n

所以你的git cat-file -p HEAD内容会进入另一个blob。

如果blob已经存在于数据库中,则不会发生任何有趣的事情:您只需重新使用它即可。如果它不在数据库中,Git会将其添加到数据库中。 blob必须具有唯一的哈希ID,与数据库中的每个其他对象不同。但它总是这样;再看一遍脚注3。

1 为了完整起见,四种对象类型是标记注释标记; 提交; ;和 blob 。提交通常很短:尝试tree查看您当前的提交。请注意,它通过哈希ID引用恰好一个tree。此.gitattributes依次引用您的每个文件,并提供其名称及其Blob哈希ID。如果您有子目录,它们将存储为子树。

2 您可以使用.gitattributes和其他技巧启用特定转换,例如行结束转换。这些转换发生在索引与工作树操作期间;由于技术原因,in-repo表示始终与索引内表示完全匹配(特别是,索引仅存储对象哈希ID,因此对象必须采用&#34; repo格式&#34;此时)。

3 这就是你如何为Git解决问题the breaking of SHA-1。只需找到两个blob-hash相同的不同文件,就不能再将这两个文件存储在同一个存储库中。 Since Git's blob-hash is not quite a straight SHA-1 hash of the two files, the example file that the researchers have supplied is not itself a problem for Git. Some other file-pair would be.

任意两个对象ID随机冲突的可能性(目前,使用SHA-1)1 in 2 160 ,这是非常小的,我们可以忽略它。但是,由于birthday paradox,保持任何Git存储库under a few quadrillion中的对象数量是明智的。如果平均对象只占用一个字节,那么在几千兆字节(即几兆字节)之后就会遇到问题。显然,平均对象大小更大,因此在问题变得可能之前计算出大量的exabytes存储库大小 - 当然,除了Git的工程破解之外。

4 Git并不真正将内容存储在云存储中,因为它&#34;喜欢&#34;本地文件系统,虽然没有理由不能使用支持云的本地文件系统。事实上,Dropbox等尝试做到这一点 - 但他们也坚持使用相同名称的文件来解决在不同计算机上采取的操作,这会严重干扰Git的操作,因为Git需要维护自己的元数据关于其内部文件。如果您使用Git-LFS,它有自己的技巧,可以将大文件卸载到单独的存储区域,使用&#34; clean&#34;和&#34;涂抹&#34;过滤器和一些聪明的git ls-files文件黑客。

了解(并查看)索引

如果你想直接查看索引,Git拥有它所谓的管道命令 -i.e。这是一个不供人类使用的命令 - 执行此操作:--stage。尝试一下 - 特别是使用 HEAD index work-tree --------- --------- --------- README.md README.md README.md test.txt test.txt test.txt 参数 - 但请注意,在一个大型存储库中,它会产生太多的输出。

通常最好间接查看索引。请记住,始终存在当前提交(HEAD或@)。总是有一个你可以看到的当前工作树。假设您有这两个文件:

git diff

您可以运行两个git status,一个用于比较HEAD与索引,另一个用于比较索引与工作树。这就是git status的作用。

如果两个文件的所有三个版本完全相同,并且您运行test.txt,则它根本不会显示任何内容。

如果您更改git status的工作树版本并运行index,它会发现HEAD与索引没有区别,但它发现test.txt与工作树不同。因此它告诉您,您已将非分段更改更改为git status

如果将新的工作树版本复制到索引中并再次运行git status,现在索引和工作树匹配,但第一个diff-HEAD vs index不再匹配。现在,z表示您已经暂存了更改

如果您将文件 HEAD index work-tree --------- --------- --------- README.md README.md README.md test.txt test.txt test.txt z 添加到工作树,则图片如下所示:

git status

现在z表示您有未跟踪的文件,因为git add z不在索引中。使用 HEAD index work-tree --------- --------- --------- README.md README.md README.md test.txt test.txt test.txt z z 将其复制到索引中即可获得此图片:

git status

再次运行git diff,再次执行两次z。现在git status位于索引中(被跟踪)但不在HEAD中,因此它是一个新文件并且已暂存。它在索引和工作树中是相同的,因此git status --short第二次没有提到它。 5

5 我实际上喜欢.git/objects/的输出,它会显示每个文件的两个差异。未跟踪的文件有两个问号,而跟踪的文件在前两列中有一个或两个字母,用于描述HEAD-vs-index和index-vs-work-tree。

对象压缩和打包

  

注意类似于&#34;第四个字符从&#39; 4&#39;到&#39; 5&#39;&#34; ...

Git 做了这样的事情,但是 - 与大多数版本控制系统不同 - 它在 lower 级别执行此类delta compression而不是blob。

使用zlib简单地压缩松散的对象。您可以在1234\n下的哈希ID名称下找到这些对象(前两个字符分开以创建目录,这样目录就不会太大)。打开其中一个文件,读取它并对其进行de-zlib压缩,您将看到松散的对象数据格式。

当有足够的松散物体使它值得时,Git 打包松散的物体。这种打包使用了一种启发式算法(因为完成一项完美的工作需要花费太多的计算量),但它基本上相当于找到了一些类似于嗅觉的对象。建议基于某个其他对象对一个对象进行delta编码将生成一个较小的包文件。

如果对象打包器选择这两个文件,它会注意到一个是1235\n而另一个是1235\n并且它可以表示第二个对象为&#34;替换第四个字符上一个对象&#34;。 1234\n基于--depth没有特别的承诺 - 它可以按其他顺序排列 - 但通常Git 尝试以保持&# 34;最近的&#34; &#34;最少压缩&#34;中的对象关于新历史比旧历史更常被访问的理论形式。

请注意,一个对象可以基于先前的对象,该对象本身基于前一个对象。这称为 delta chain :我们必须将每个对象扩展到 base ,以便依次应用每个delta拿出链中的最后一个对象。 Git的deltifier将限制delta链长度;请参阅调用它的各种事物的{{1}}参数。

(在这种特殊情况下,对象ID远远超过对象的内容,因此在这里尝试制作增量没有任何好处。但是这个原则适用于更大的文件。在任何二进制压缩之前,应始终应用增量压缩:增量压缩依赖于输入文件中较小的Shannon entropy值,而压缩器(如gzip和bzip2)则通过挤压这样的熵来工作。)< / p>

包文件的格式已多次更改。另请参阅Are Git's pack files deltas rather than snapshots?