两个目录中相同文件的Git Diff总是导致;更名为;

两个目录中相同文件的Git Diff总是导致;更名为;,git,version-control,diff,git-branch,Git,Version Control,Diff,Git Branch,git diff--无索引--无前缀--摘要-U4000目录1目录2 这与预期的一样,因为它返回两个目录之间所有文件的差异。添加的文件按预期输出,删除的文件也会产生预期的差异输出 但是,由于diff将文件路径作为文件名的一部分考虑在内,因此两个不同目录中具有相同名称的文件会导致diff输出带有重命名标志,而不是更改 有没有办法告诉git不要考虑diff中的完整文件路径,而只查看文件名,就好像文件来自同一个目录一样 git有没有办法知道不同目录中同一文件的副本是否被重命名?我不知道该怎么做,除非它

git diff--无索引--无前缀--摘要-U4000目录1目录2

这与预期的一样,因为它返回两个目录之间所有文件的差异。添加的文件按预期输出,删除的文件也会产生预期的差异输出

但是,由于diff将文件路径作为文件名的一部分考虑在内,因此两个不同目录中具有相同名称的文件会导致diff输出带有重命名标志,而不是更改

  • 有没有办法告诉git不要考虑diff中的完整文件路径,而只查看文件名,就好像文件来自同一个目录一样

  • git有没有办法知道不同目录中同一文件的副本是否被重命名?我不知道该怎么做,除非它能以某种方式比较文件MD5或其他东西(可能是一个错误的猜测lol)

  • 使用分支而不是目录是否可以轻松解决此问题?如果可以,上面列出的命令的分支版本是什么


  • 这里有许多问题,它们的答案交织在一起。让我们从重命名和复制检测开始,然后转到分支

    重命名检测 但是,由于diff将文件路径作为文件名的一部分考虑在内,因此两个不同目录中具有相同名称的文件会导致diff输出带有重命名标志,而不是更改

    这是不对的。(以下文字旨在说明第1项和第2项。)

    尽管您使用的是
    --no index
    (大概是为了让Git在存储库之外的目录上工作),Git的diff代码在所有情况下的行为都是相同的。为了区分(比较)两个树中的两个文件,Git必须首先确定文件标识。也就是说,有两组文件:位于“左侧”或源目录树(第一个目录名)中的文件,以及位于“右侧”或目标目录树(第二个目录名)中的文件。左侧的某些文件与右侧的某些文件相同。左侧的一些文件是没有相应右侧文件的不同文件,即它们已被删除。最后,右侧的一些文件是新的,即它们已创建

    “相同文件”的文件不必具有相同的路径名。在本例中,这些文件已重命名

    下面是它的详细工作原理。请注意,在使用
    git diff--no index dir1 dir2
    时,“完整路径名”有所修改:“完整路径名”是去掉
    dir1
    dir2
    前缀后剩下的内容

    在比较左侧树和右侧树时,具有相同完整路径名的文件通常会自动视为“同一文件”。我们将所有这些文件放入一个“要更改的文件”队列中,没有一个文件会显示为被重命名。注意这里的“正常”一词,我们稍后会回到这里

    这就剩下了两个文件列表:

    • 存在于左侧但不存在于右侧的路径:源而不存在目标
    • 存在于右侧但不存在于左侧的路径:无源的目标
    天真地说,我们可以简单地声明所有这些源端文件都已删除,所有这些目标文件都已创建。您可以指示
    git diff
    这样做:设置
    --no renames
    标志以禁用重命名检测

    或者,Git可以继续使用更智能的算法:设置
    --find renames
    和/或
    -M
    标志来完成此操作。在Git版本2.9及更高版本中,默认情况下启用重命名检测

    现在,Git如何确定源文件与目标文件具有相同的标识?他们有不同的道路;左侧的
    a/b/c.txt
    对应于哪个右侧文件?它可能是
    d/e/f.bin
    ,或者
    d/e/f.txt
    ,或者
    a/b/rename.txt
    ,等等。实际的算法相对简单,过去没有将最终名称组件生效(我不确定现在是否生效,Git正在不断发展):

    • 如果有内容完全匹配的源文件和目标文件,请将它们配对。因为Git散列内容,所以这种比较非常快。我们可以通过查看左侧的
      a/b/c.txt的散列ID将其与右侧的每个文件进行比较,只需查看它们的所有散列ID即可。因此,我们首先运行所有源文件,找到匹配的目标文件,将新的对放入diff队列,并将它们从两个列表中拉出

    • 对于所有剩余的源文件和目标文件,运行一个高效但不适合于
      git diff
      输出的算法来计算“文件相似性”。源文件至少与某个目标文件类似,会导致配对,并且该文件对将被删除。默认阈值为50%:如果启用了重命名检测而未选择特定阈值,则此时仍在列表中且50%相似的两个文件将配对

    • 将删除或创建所有剩余文件

    现在我们已经找到了所有的配对,
    git diff
    继续区分配对的相同标识文件,并告诉我们删除的文件将被删除,新创建的文件将被创建。如果相同标识文件的两个路径名不同,
    git diff
    表示该文件已重命名

    任意文件配对代码非常昂贵(即使同名配对代码非常便宜),因此Git对进入这些配对源和目标列表的名称数量有限制。该限制是通过git config diff.renameLimit
    配置的。多年来,默认值一直在攀升,现在是数千个文件。您可以将其设置为
    0
    (零),使Git使用自己的内部代码
    ...--o--o--o   <-- branch1
             \
              o--o--o   <-- branch2
    
    $ git diff branch1 branch2
    
    $ git diff 1234567 89abcde
    
    ...--o--o   <-- tip1
             \
              o--o   <-- tip2, tip3