3

这是我之前提出的另一个问题的后续。

在被编辑之前,最初创建的文件something被重命名为somethingelse,可以在这里观察到:

git mv something somethingelse

然后该文件somethingelse重命名回something第二次 vim 编辑之前:

git mv somethingelse something

基本上在代码的以下部分

# If you add something to the first line, the rename will not be detected by Git
# However, if you instead create 2 newlines and fill line 3 with new code,
# the rename gets detected for whatever reason
printf "\nCOMMAND: vim something\n\n"
vim something

如果此时我添加abc到代码中,我们将得到:

First line of code. abc

我认为这是在第 1 行增加了 4 个字节,这反过来又会是这样的:

On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        new file:   something
        deleted:    somethingelse

然后,如果我们添加一个换行符并在第三行输入 abc (这也应该是 4 个字节,如果有错请纠正我):

First line of code.

abc

突然,Git 会检测到重命名(包含编辑):

On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        renamed:    somethingelse -> something

@torek在这里给出的一个很好的回答/评论在一定程度上解释了这一点,并考虑了git diff重命名检测阈值git status

由于我们在两种情况下都添加了 4 个字节,Git 的行为不应该相同,但是以不同的方式,或者换行符与此有关吗?

4

1 回答 1

6

据我所知,Git 的“相似性指数”计算没有记录在源文件以外的任何地方,从 diffcore-delta.c 开始

要计算两个文件S(源)和D(目标)的相似性指数,Git:

  • 读取两个文件
  • 计算文件S的所有块的哈希表
  • 计算文件D的所有块的第二个哈希表

这两个哈希表中的条目只是该哈希值实例出现的计数(加上,如下所述,块的长度)。

文件块的哈希值通过以下方式计算:

  • 从当前文件偏移量开始(最初为零)
  • 读取 64 个字节或直到'\n'字符,以先到者为准
  • 如果文件声称是文本并且在'\r'之前有'\n',则丢弃'\r'
  • 使用链接文件中显示的算法对生成的最多 64 个字节的字符串进行哈希处理

现在SD都有哈希表,每个可能的哈希h i在S中出现n S次,在D中出现n D(两者都可能为零,尽管代码直接跳过了两个零哈希值)。如果D中出现的次数小于或等于 S 中出现的次数——即 n D n S——则D “从S中复制” n D次。如果D中出现的次数超过了S(包括当S中的数字为零时),则D具有n D - n S出现的散列块的“文字添加”,并且D还复制所有n S原始出现。

每个散列块保留其输入字节数,这些乘以“块”的副本数或添加数以获得复制或添加的字节数。(删除,其中D缺少S中存在的项目,在这里只有间接影响:字节复制和添加计数变小,但 Git 并没有专门计算删除本身。)

计算的这两个值 (src_copiedliteral_added)diffcore_count_changes被移交给函数estimate_similarityindiffcore-rename.c。它完全忽略了literal_added计数(此计数用于决定如何构建包文件增量,但不用于重命名评分)。相反,只有src_copied数字很重要:

score = (int)(src_copied * MAX_SCORE / max_size);

其中max_size是两个输入文件SD中较大的字节大小。

请注意,有一个较早的计算:

max_size = ((src->size > dst->size) ? src->size : dst->size);
base_size = ((src->size < dst->size) ? src->size : dst->size);
delta_size = max_size - base_size;

如果这两个文件的大小改变“太多”:

if (max_size * (MAX_SCORE-minimum_score) < delta_size * MAX_SCORE)
        return 0;

我们甚至从未进入diffcore-delta.c代码对它们进行哈希处理。这里minimum_score-Mor的参数--find-renames,转换为一个缩放的数字。 MAX_SCORE60000.0(type double),所以minimum_score当你使用 default 时,default-M50%是 30000(60000 的一半)。但是,除了 CR-before-LF 吃掉的情况之外,这种特殊的捷径不应该影响更昂贵的相似性计算的结果。

[编辑:现在已过时:] git status始终使用默认值。没有旋钮可以更改阈值(也没有重命名队列中允许的文件数)。如果有代码会去这里,设置rename_score差异选项的字段。 在 Git 版本 2.18.0 之前,无法控制git status. 在 Git 2.18.0 及更高版本中,git status具有--find-renamesgit diff. Git 配置中的status.renames选项启用任何默认检测,如果未设置,则git status服从diff.renames设置;参阅文档文档_git configgit status

于 2017-09-16T22:24:24.633 回答