git如何跟踪文件的更改

ndn*_*kov 5 git internals

在最长的时间里,我认为git提交会保留更改文件的差异,而不是副本.我能找到的任何信息都表明相反.我做了一个小实验:

$ git init
$ subl wtf
Run Code Online (Sandbox Code Playgroud)

在这里,我创建了一个包含99 999行的文件,每行都是foo bar baz #line

$ ls -la
total 1760
drwxrwxr-x 3 __user__ __user__    4096 Aug 13 21:02 .
drwxr-xr-x 3 __user__ __user__    4096 Aug 13 19:57 ..
drwxrwxr-x 7 __user__ __user__    4096 Aug 13 21:02 .git
-rw-rw-rw- 1 __user__ __user__ 1788875 Aug 13 21:02 wtf
$ git add --all
$ git commit -m 'Initial commit'
[master (root-commit) 6ef5084] Initial commit
 1 file changed, 99999 insertions(+)
 create mode 100644 wtf
$ subl wtf
$ git diff
diff --git a/wtf b/wtf
index 7ba3acb..bf7a9ed 100644
--- a/wtf
+++ b/wtf
@@ -14156,7 +14156,7 @@ foo bar baz 14155
 foo bar baz 14156
 foo bar baz 14157
 foo bar baz 14158
-foo bar baz 14159
+foo qux baz 14159
 foo bar baz 14160
 foo bar baz 14161
 foo bar baz 14162
$ git add --all
$ git commit -m 'bar -> qux on #14159'
[master 1b5ab4b] bar -> qux on #14159
 1 file changed, 1 insertion(+), 1 deletion(-)
$ subl wtf
$ git diff
diff --git a/wtf b/wtf
index bf7a9ed..1aeeaa3 100644
--- a/wtf
+++ b/wtf
@@ -14156,7 +14156,7 @@ foo bar baz 14155
 foo bar baz 14156
 foo bar baz 14157
 foo bar baz 14158
-foo qux baz 14159
+xyz abc baz 14159
 foo bar baz 14160
 foo bar baz 14161
 foo bar baz 14162
$ git add --all
$ git commit -m 'foo qux -> xyz abc on #14159'
[master 85ccf97] foo qux -> xyz abc on #14159
 1 file changed, 1 insertion(+), 1 deletion(-)
$ ls -la
total 1760
drwxrwxr-x 3 __user__ __user__    4096 Aug 13 21:02 .
drwxr-xr-x 3 __user__ __user__    4096 Aug 13 19:57 ..
drwxrwxr-x 9 __user__ __user__    4096 Aug 13 21:05 .git
-rw-rw-rw- 1 __user__ __user__ 1788875 Aug 13 21:04 wtf
Run Code Online (Sandbox Code Playgroud)

即使是在有冲突的不同分支上提交也没有改变这种情况.

如果git真的保留了每次提交所有已更改文件的副本,那么为什么使用的空间没有显着变化?

kan*_*kan 7

git有对象数据库.存在一种对象"blob",其由sha1的内容标识.因此,这意味着,如果您在存储库中的任何位置具有相同内容的文件(历史/目录/等的分支/点),它将仅存储在数据库中一次.

数据库中有两个部分,这些objects/??/*文件是单个对象.即如果你有两个版本的大文件只有单行差异 - 它将被存储两次,在两个不同的文件中(使用简单的lzma?压缩).

然后,如果git认为objects目录增长太多,它会运行垃圾收集.这个过程的一个步骤 - 重新包装.它在objects/pack/文件夹中创建大包文件,使用巧妙的增量压缩算法,它不是在特定文件的历史记录上工作,而是在整个对象数据库的范围内工作,所以这意味着即使一些完全不相关的文件看起来相似偶尔,他们可以被打包成彼此的三角洲.

因此,git gc在考虑到历史记录中的最新变化的每个命令之后,可以不同地重新压缩增量.

另外,object packsvs loose objects只是物理存储细节,每天使用git时都是完全透明的.例如log cherry-pick,merge正在执行提交的完整快照.所以,如果你正在做差异,它只是比较两个版本的目录/文件,生成一个补丁/差异.

与其他VCS相比,这种方法非常独特.例如,Mercurial分别为每个文件存储不可变的delta-logs,而Subversion正在为整个存储库存储增量.它会影响系统的工作方式 - 物理存储不会被抽象出来并导致一些重大限制,而git允许非常灵活的工作流程和算法,同时保持存储库的大小非常小

  • @ndn`ls -la`没有显示目录中的所有文件发生了多少.它显示文件系统目录条目列表占用的磁盘空间.4k - 是文件系统块大小.与git完全无关.使用`du -sh .git/objects`计算所有文件大小的总和. (2认同)