git status如何在内部工作?

roo*_*kie 9 git

git对象模型,文件和文件夹通过sha1哈希保存到.git文件夹中的位置.

git如何在内部知道文件是否已被删除,添加或编辑(具体来说,它如何计算您在键入时看到的更改git status)?系统是否完全从sha1中确定此信息?

Cod*_*ard 19

答案有点太长,需要一段时间才能写,所以这里是摘要.

简短的回答是git使用它SHA-1来检查跟踪更改,但文件的名称存储在其他地方.


内容存储在pack (*1-读下面)中,而名称存储在a中idx.
当你运行时git status,git检查是否已经在idx文件(元数据)中有这个路径,并根据结果确定它是否是新文件.

如果它不是新文件,git将SHA-1与跟踪更改进行比较.


为什么我必须使用git mv来移动文件而不是简单的MV?

执行git statusgit搜索工作目录时,要查找idx文件中"已注册"路径与工作目录之间的匹配项.

当您使用mv工作目录移动文件时,没有git存储的"原始"路径,并且由于git无法再找到"已注册"路径,因此该文件被标记为已删除.

但是同时git看到一个新文件,你只是将文件移动到的路径,所以文件将被标记为新文件.

另一方面,在使用时git mv,git更新元数据以指向新名称,内容将被标记为重命名.在这种情况下,git更新idx文件中文件的注册路径.
如果您移动并更新它将被标记为重命名+修改.

在此输入图像描述 在此输入图像描述


如何找出文件的SHA-1是什么?

使用git ls-treeinternal命令查找文件的SHA-1.

在此输入图像描述


注意

当您将内容添加到暂存区域时,Git会开始跟踪内容.
添加文件后,git将以下信息存储在文件中

[blob][1 white space][content length][null][content]

If you have a file with the string `hello` it will look like this:
blob 5\0Hello
Run Code Online (Sandbox Code Playgroud)

现在git计算这个文件的SHA-1(使用sha1sumn)用z-lib压缩它并用这个SHA-1保存文件作为名称``.git/objects'.

当git打包存储库时,它将进入包文件.

如何查看文件的内容?

由于文件是使用z-lib压缩的,因此我们有几个选项来获取内容:

  • 第一个选项是查看存储在文件中的压缩文件
  • 第二是查看解压缩的内容(如上所述)
  • 仅在没有任何git元数据的情况下查看文件的内容.

在此输入图像描述

并且为了表明git实际上使用上面描述的内容,这里的命令与git在后台执行的命令相同,以计算SHA-1

在此输入图像描述


这是提交的说明以及它如何处理文件名.

(http://shafiulazam.com/gitbook/1_the_git_object_model.html) 在此输入图像描述

  • 我同意,它太长了,所以我试图总结它而不涉及太多细节。随意编辑和添加您的内容。适用 (2认同)

tor*_*rek 14

正如Edward Thomson在评论中指出的那样,CodeWizard的答案在一些重要细节中是错误的.

超短版本即git status运行git diff.

事实上,它运行两次,或者更准确地说,它运行两个不同的内部变体git diff:一个HEAD用于与索引/登台区域进行比较,另一个用于比较登台区域和工作树.它运行每个diff以请求搜索重命名,即设置-M标志(见下文).最后,它以您请求的任何格式向您展示这些差异的结果.在任何情况下它都不会显示文件之间的实际变化(因此实际上它--name-status也会运行这些差异).

使用各种差异

您可以手动运行这两个内部差异:一个拼写了前端命令git diff-index --cached,另一个拼写了前端命令git diff-files.这个前端选择是在标题为Raw输出格式的略微奇怪的部分中捕获的(我必须修改这一点以便在StackOverflow上更好地显示):

原始输出格式从git-diff-index,git-diff-tree,git-diff-filesgit diff --raw非常相似.

这些命令都比较了两组东西; 比较的不同之处是:

git-diff-index tree-ish
      比较tree-ish文件系统上的文件和文件.

git-diff-index --cached tree-ish
      比较tree-ish和索引.

git-diff-tree [-r] tree-ish-1 tree-ish-2 [pattern ...]
      比较两个参数命名的树.

git-diff-files [pattern ...]
      比较索引和文件系统上的文件.

(您也可以使用常规调用它们git diff:git diff --cached将current(HEAD)提交与临时区域git diff进行比较,并且没有其他参数将暂存区域与工作树进行比较.)

将树映射回路径

CodeWizard的答案是这个过程的关键.实质上,对象包含路径名组件(例如foobarin foo/bar)和另一个对象ID.如果组件表示目录,则对象ID定位另一个树对象; 如果它表示文件,则对象ID将查找blob对象.在任何一种情况下,ID都是Git的内部名称,这使Git能够在存储库中找到它.

(对于索引/登台区本身而言,情况并非如此,其格式没有很好地记录.它是所有文件的平面列表,具有完整路径名,但也使用名称压缩技术,因此VeryLongDirectory/AnotherLongDirectory/bar后面VeryLongDirectory/AnotherLongDirectory/baz没有VeryLongDirectory/AnotherLongDirectory每次都必须拼出来.)

(树对象还存储Git应该分配给文件的模式,在提取时,除了在树对象中,每个文件模式只是永远100644或者100755;最后的rwx位是基于你的umask设置的,假设类Unix主机,与x被始终清楚,如果所存储的模式100644,否则设置除-AS-清零逐个的umask.)

未分段文件和检测重命名

git如何在内部知道文件是否已被删除,添加或编辑(具体来说,它如何计算您在键入git status时看到的更改)?

工作树中的文件,但既不在HEAD提交中也不在索引/临时区域中的文件是非分段的(这实际上是"未分级"的定义).Git通过查看所有这三个文件来找到这样的文件(并使用索引/临时区域来获取缓存信息以加快进程).所有未停止的文件路径通常都被提供给"忽略"代码,如果它们列在其中.gitignore或任何其他忽略某些路径文件中,则git会关闭它们.

在省去了非分段路径之后,让我们考虑剩余的路径,这些路径(根据定义)出现在至少一个HEAD或索引/暂存区域中.

一般情况下,有更多的标志用于更精细地控制它,虽然git status没有设置它们中的任何一个 - Git首先比较"A"侧(a/foo/bar)中可用的路径名与"B"侧(b/foo/bar)中的路径名.如果相同的路径出现在两个边,很可能是该文件代替简单的修改,和Git与假设开始.如果路径出现在A但不出现在B中,而某些其他路径出现在B中而不出现在A中,则两个路径将配对并赋予重命名检测器(如果已启用).

所有内部差异共享一堆代码,并共享文档.点击以上链接之一并搜索-M--find-renames:

-M[n] --find-renames[=n]

检测重命名.如果指定了n,则它是相似性指数的阈值(即与文件大小相比的添加/删除量).例如,-M90%如果多个90%文件没有更改,Git应该将删除/添加对视为重命名.没有%符号,数字将被读作分数,前面有一个小数点.即,-M5成为0.5,因此也是如此-M50%.同样,-M05也是如此-M5%.要将检测限制为精确重命名,请使用-M100%.默认的相似性指数是50%.

默认情况下,可以通过在配置中进行设置diff.renameLimit来启用重命名检测器0.否则,默认情况下它当前处于禁用状态,但默认情况下将在即将发布的Git版本中启用(我不确定是哪一个).

有关相似性匹配的更多详细信息,请参阅Edward Thomson的回答.

一旦重命名检测器确定某些A到B的更改是重命名,它就会将两个名称从"仅在A中"和"仅在B中"列表中拉出.

添加和删​​除

运行重命名检测器(如果启用)后,任何仅在A侧找到的文件都将被"删除",并且仅在B侧找到的任何文件都将被"添加".因为git status,这结束了整个过程(显示结果除外).对于常规git diff我们通常会在修改或重命名和修改某个文件时继续生成实际的diff输出.

(请注意,所有Git的差异共享所有这些机制,因此如果您打开重命名检测并设置相同的阈值,它们都将找到相同的重命名集.这些也在使用期间使用git merge.)

附注:检测到重命名,不记录

许多其他版本控制系统(Mercurial,ClearCase,Perforce)要求您使用它们注册文件重命名:hg mv依此类推.这是因为它们会在每次提交时记录重命名.执行此操作的系统必然会为每个文件提供某种标识符(这可能是ClearCase中的真实对象ID,或者只是"当前提交中的名称",然后在我们从提交转为提交时根据需要进行管理) .该系统的优点是VCS可以跟踪文件,无论文件如何变化.缺点是您必须记录更改,并且意外删除然后复活的文件可以获得新ID(请参阅ClearCase"evil twins").

每次将一个提交与另一个提交(或对索引的提交,或工作树的索引等)进行比较时,Git只是重新发现重命名.这意味着你不具备使用git mv:您可以git rm --cached将旧路径和git add新的一个,以达到同样的效果.(当然,git mv大多数情况下,您可以随时使用它.但这与版本控制系统的重大区别在于,每次签入或提交时都会记录目录修改:使用这些系统,您必须调用特定于VCS的mv命令,例如hg mvcleartool mv,以通知VCS文件已移动,而不是让VCS稍后解决.)