【问题标题】:git filter-branch and gitattributesgit 过滤器分支和 gitattributes
【发布时间】:2016-11-29 19:48:50
【问题描述】:

我想使用 git filter-branch 将企业 git 存储库历史中的 Windows/混合行尾转换为 Unix 行尾。由于存储库包含一些特定于域的二进制文件类型,.gitattributes 文件非常详细,所以我宁愿使用 git 自己的机制进行 EOL 转换,而不是像 here 这样的 dos2unix 脚本。

我设法使用我在https://github.com/cnaj/demo-crlf-rewrite/tree/so-question 中描述的过程进行转换,即执行添加.gitattributes 然后执行git reset树过滤器。如果没有重置,历史记录将保持不变。但是,显然在过滤操作期间索引仍处于 HEAD 修订版,因此仅识别相对于 HEAD 更改的文件,并且仅根据 .gitattributes 转换那些文件(因此演示中的“NUKE”提交)。

我的问题:

  1. 此程序是否可以安全使用,还是只是无证(或被误解)行为的意外结果?
  2. tree-filter 操作期间 git 的索引是多少?可以在 tree-filter 中使用改变索引的命令吗(我没有找到关于这个主题的明确文档)?
  3. 额外问题:究竟是什么导致 git 在此设置中获取行尾转换? (我很难理解发生了什么......)

【问题讨论】:

    标签: git newline gitattributes


    【解决方案1】:

    这个:

    但是,显然在过滤操作期间索引仍处于 HEAD 修订版,因此仅识别相对于 HEAD 更改的文件...

    是完全错误的,但不幸的是事实很复杂。

    让我们从git filter-branch 通过复制每个过滤的提交(和注释标记,如果使用--tag-name-filter)来工作的基本事实开始。如果生成的副本与原始提交逐位相同,则副本最终具有相同的哈希 ID,因此 原始的,在非常真实的意义上。否则,它是一个新的提交,并且是原始提交的替换。

    Git 的索引扮演着几个不同的角色,虽然主要的角色是“构建下一个提交”。因此,索引实际上会随着git filter-branch 迭代每个原始提交以复制它而发生变化。不过……

    即做一个tree-filter,添加.gitattributes,然后执行git reset。

    ...这里有问题。如果您查看the filter-branch code,您将看到它在运行树过滤器结束时运行git update-index。它不运行git add,因此它取决于早期git diff-indexgit ls-files 命令的输出。所有这些都在覆盖正常工作树的临时目录中完成。

    在这里,我们遇到了我自己的个人 Git-fu 的边缘 :-) 因为git add 等调用属性代码(在松鼠路径中,通过函数unpack_trees,直接从builtin/reset.c 调用,并间接地从diff_cache 调用,从run_diff_indexindex_differs_from 调用,以尊重工作树中的.gitattributes 文件。但是,git update-index 似乎没有(这是基于您自己的观察,即您的属性没有得到应用)。

    幸运的是,有一个可能的解决方法。如果您自己明确地git add .gitattributes(而不是,或者可能在必要时之前运行git reset 或另一个git checkout-index),那么应该将新文件放入索引中。然后,如果git update-index 使用索引版本而不是临时树文件(根据来源似乎很可能),它将使用您打算使用的那个。

    最后:

    究竟是什么导致 git 在此设置中获取行尾转换?

    这没有很好的记录,而且这里的代码路径甚至更复杂。

    基本上,所有过滤器,包括文本/CRLF 转换,都应用于两个点:将文件从索引“移出”到工作树时,或将文件从工作树“移入”到索引时.

    ,然而,索引包含一组 stat 数据和标志,因此 Git 甚至只会查看(更不用说复制输入或输出)看起来可能不同的文件在树中比在索引版本中。

    索引中有一个单独的标志来标记文件“脏”。当过滤器在检出期间修改文件时,会设置此标志。由于 initial 结帐时没有现有的.gitattributes,因此此处不会设置脏标志。 (但是这个标志的存在使得任何 确实 被过滤的文件,处理起来要慢得多。因此,使用大量属性会使 Git 变慢,因为它会破坏巧妙的索引缓存。)

    【讨论】:

    • 我花了一些时间来处理您的评论 :-) 感谢您将我指向 git 源!事实上git update-index 确实调用了属性转换,这发生在sha1_file.c:index_path()。从工作树中读取属性文件。然而,git-filter-branch.sh 中的diff-index 不会接受任何更改,直到git reset 有效地弄脏所有文件的索引,此时update-index 进行转换。对我来说看起来很安全,但我明天会在大存储库上试用它......
    【解决方案2】:

    我收到torek's answer后的亲身经历:

    此程序使用安全吗,还是只是无证(或被误解)行为的意外结果?

    在区分大小写的 POSIX 兼容文件系统上使用此过程应该是安全的。但是,它可以变得更简单(参见https://github.com/cnaj/demo-crlf-rewrite)。

    tree-filter 操作期间 git 的索引是什么?可以在 tree-filter 中使用改变索引的命令吗(我没有找到关于这个主题的明确文档)?

    是的。在每一步中,tree-filter 都会将索引设置为当前修订的内容,并将其检出到临时目录中。过滤器运行后,索引和工作副本之间的差异被提升到索引中,然后成为新的修订版本。

    额外问题:究竟是什么导致 git 在此设置中获取行尾转换? (我很难理解发生了什么......)

    虽然 filter-branch 将索引设置为每个处理的版本,但 HEAD 保持在启动 filter-branch 的原始版本。这样,git reset HEAD 命令具有从索引中删除每个文件的效果。之后,所有文件都被视为已更改,并相应更新索引,并在此过程中触发 EOL 转换。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-11-17
      • 2016-12-08
      • 2020-01-14
      • 2014-05-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-18
      相关资源
      最近更新 更多