git 过滤器分支和 git 属性
git filter-branch and gitattributes
我想使用 git filter-branch
将企业 git 存储库历史中的 Windows/mixed 行结尾转换为 Unix 行结尾。由于存储库包含一些特定于域的二进制文件类型,.gitattributes
文件非常详细,因此我宁愿使用 git 自己的 EOL 转换机制,而不是 dos2unix
脚本,如 here.
我设法使用我在 https://github.com/cnaj/demo-crlf-rewrite/tree/so-question 中描述的过程进行转换,即做一个 tree-filter 添加 .gitattributes
和然后执行 git reset
。如果没有重置,历史将保持不变。但是,显然索引在过滤操作期间仍处于 HEAD 修订版,因此仅识别相对于 HEAD 更改的文件,并且仅根据 .gitattributes
转换这些文件(因此 "NUKE"在演示中提交)。
我的问题:
- 此程序使用安全吗,还是只是未记录(或误解)行为的意外结果?
- 在树过滤器操作期间git的索引是什么?可以在 tree-filter 中使用更改索引的命令(我没有找到关于此主题的明确文档)吗?
- 额外问题:究竟是什么导致 git 在此设置中选择行结束转换? (我很难理解发生了什么......)
这个:
However, apparently the index is still at the HEAD revision during the filter operations, so only files that are changed with respect to HEAD are recognized ...
大错特错,可惜真相很复杂。
让我们从基本事实开始,即 git filter-branch
通过 复制 每个过滤的提交(和注释标签,如果使用 --tag-name-filter
)。如果生成的副本与原始提交逐位相同,则副本最终具有相同的哈希 ID,因此 是 原始的,在非常真实的意义上。否则它是一个新的提交并且是原始提交的替换。
Git 的索引扮演着几个不同的角色,尽管主要角色是 "to build the next commit"。因此,索引确实发生了变化,因为 git filter-branch
遍历每个原始提交以复制它。然而...
namely to do a tree-filter that adds the .gitattributes
and then performs a git reset.
...这里有问题。如果您查看 the filter-branch code,您会看到它 运行s git update-index
在 运行 树过滤器的末尾。它不会 运行 git add
,因此它取决于早期 git diff-index
和 git ls-files
命令的输出。所有这些都在覆盖正常工作树的临时目录中完成。
这里我们运行进入我个人的边缘Git-fu :-)因为git add
之类调用属性代码(在松鼠路径中,通过函数 unpack_trees
,直接从 builtin/reset.c
调用,从 diff_cache
间接调用,从 run_diff_index
或 index_differs_from
调用(对于其他各种调用)以尊重.gitattributes
工作树中的文件。然而,git update-index
似乎没有(这是基于您自己的观察,您的属性未被应用)。
幸运的是,有一个可能的解决方法。如果您明确地 git add .gitattributes
自己(而不是 运行ning git reset
或另一个 git checkout-index
,如果有必要,则应该将新文件放入索引中。然后,如果 git update-index
使用索引版本而不是临时树文件(根据来源似乎很可能),它将使用您打算使用的版本。
最后一个:
What exactly causes git to pick up the line ending conversions in this setting?
这没有很好的记录,这里的代码路径甚至更复杂。
基本上,所有过滤器,包括文本/CRLF 转换,都应用于两点:将文件 "out" 从索引移动到工作树时,或者将文件 "in" 从索引的工作树。
索引 也 ,但是,包含一组 stat
数据和标志,因此 Git 甚至只会查看(更不用说复制in or out) 树中的文件可能与索引版本中的文件不同。
索引中有一个单独的标志来标记文件"dirty"。当过滤器在结帐期间修改文件时,会设置此标志。由于 initial 结帐中不存在 .gitattributes
,因此不会在此处设置脏标志。 (但是这个标志的存在使得任何 做 的文件被过滤,使用起来要慢得多。因此使用很多属性会使 Git 慢得多,通过击败聪明的索引缓存。)
根据我收到后的经验:
Is this procedure safe to use, or is it just an accidental outcome of undocumented (or misunderstood) behavior?
在区分大小写的 POSIX 兼容文件系统上使用此过程应该是安全的。但是,它可以变得更简单(参见 https://github.com/cnaj/demo-crlf-rewrite)。
What is git's index during the tree-filter operation? Can commands that alter the index be used in tree-filter (I didn't find explicit documentation on this topic)?
是的。在每一步中,tree-filter 都会将索引设置为当前修订的内容并将其检出到一个临时目录中。过滤器为运行后,索引与工作副本的差异被提升到索引中,然后成为新的修订。
Bonus question: What exactly causes git to pick up the line ending conversions in this setting? (I'm having a hard time understanding what's going on...)
虽然 filter-branch 为每个处理的修订设置索引,HEAD 停留在 filter-branch 所在的原始修订开始了。这样,git reset HEAD
命令具有从索引中删除每个文件的效果。之后所有文件都被视为已更改并相应更新索引,在此过程中触发EOL转换。
我想使用 git filter-branch
将企业 git 存储库历史中的 Windows/mixed 行结尾转换为 Unix 行结尾。由于存储库包含一些特定于域的二进制文件类型,.gitattributes
文件非常详细,因此我宁愿使用 git 自己的 EOL 转换机制,而不是 dos2unix
脚本,如 here.
我设法使用我在 https://github.com/cnaj/demo-crlf-rewrite/tree/so-question 中描述的过程进行转换,即做一个 tree-filter 添加 .gitattributes
和然后执行 git reset
。如果没有重置,历史将保持不变。但是,显然索引在过滤操作期间仍处于 HEAD 修订版,因此仅识别相对于 HEAD 更改的文件,并且仅根据 .gitattributes
转换这些文件(因此 "NUKE"在演示中提交)。
我的问题:
- 此程序使用安全吗,还是只是未记录(或误解)行为的意外结果?
- 在树过滤器操作期间git的索引是什么?可以在 tree-filter 中使用更改索引的命令(我没有找到关于此主题的明确文档)吗?
- 额外问题:究竟是什么导致 git 在此设置中选择行结束转换? (我很难理解发生了什么......)
这个:
However, apparently the index is still at the HEAD revision during the filter operations, so only files that are changed with respect to HEAD are recognized ...
大错特错,可惜真相很复杂。
让我们从基本事实开始,即 git filter-branch
通过 复制 每个过滤的提交(和注释标签,如果使用 --tag-name-filter
)。如果生成的副本与原始提交逐位相同,则副本最终具有相同的哈希 ID,因此 是 原始的,在非常真实的意义上。否则它是一个新的提交并且是原始提交的替换。
Git 的索引扮演着几个不同的角色,尽管主要角色是 "to build the next commit"。因此,索引确实发生了变化,因为 git filter-branch
遍历每个原始提交以复制它。然而...
namely to do a tree-filter that adds the
.gitattributes
and then performs a git reset.
...这里有问题。如果您查看 the filter-branch code,您会看到它 运行s git update-index
在 运行 树过滤器的末尾。它不会 运行 git add
,因此它取决于早期 git diff-index
和 git ls-files
命令的输出。所有这些都在覆盖正常工作树的临时目录中完成。
这里我们运行进入我个人的边缘Git-fu :-)因为git add
之类调用属性代码(在松鼠路径中,通过函数 unpack_trees
,直接从 builtin/reset.c
调用,从 diff_cache
间接调用,从 run_diff_index
或 index_differs_from
调用(对于其他各种调用)以尊重.gitattributes
工作树中的文件。然而,git update-index
似乎没有(这是基于您自己的观察,您的属性未被应用)。
幸运的是,有一个可能的解决方法。如果您明确地 git add .gitattributes
自己(而不是 运行ning git reset
或另一个 git checkout-index
,如果有必要,则应该将新文件放入索引中。然后,如果 git update-index
使用索引版本而不是临时树文件(根据来源似乎很可能),它将使用您打算使用的版本。
最后一个:
What exactly causes git to pick up the line ending conversions in this setting?
这没有很好的记录,这里的代码路径甚至更复杂。
基本上,所有过滤器,包括文本/CRLF 转换,都应用于两点:将文件 "out" 从索引移动到工作树时,或者将文件 "in" 从索引的工作树。
索引 也 ,但是,包含一组 stat
数据和标志,因此 Git 甚至只会查看(更不用说复制in or out) 树中的文件可能与索引版本中的文件不同。
索引中有一个单独的标志来标记文件"dirty"。当过滤器在结帐期间修改文件时,会设置此标志。由于 initial 结帐中不存在 .gitattributes
,因此不会在此处设置脏标志。 (但是这个标志的存在使得任何 做 的文件被过滤,使用起来要慢得多。因此使用很多属性会使 Git 慢得多,通过击败聪明的索引缓存。)
根据我收到后的经验
Is this procedure safe to use, or is it just an accidental outcome of undocumented (or misunderstood) behavior?
在区分大小写的 POSIX 兼容文件系统上使用此过程应该是安全的。但是,它可以变得更简单(参见 https://github.com/cnaj/demo-crlf-rewrite)。
What is git's index during the tree-filter operation? Can commands that alter the index be used in tree-filter (I didn't find explicit documentation on this topic)?
是的。在每一步中,tree-filter 都会将索引设置为当前修订的内容并将其检出到一个临时目录中。过滤器为运行后,索引与工作副本的差异被提升到索引中,然后成为新的修订。
Bonus question: What exactly causes git to pick up the line ending conversions in this setting? (I'm having a hard time understanding what's going on...)
虽然 filter-branch 为每个处理的修订设置索引,HEAD 停留在 filter-branch 所在的原始修订开始了。这样,git reset HEAD
命令具有从索引中删除每个文件的效果。之后所有文件都被视为已更改并相应更新索引,在此过程中触发EOL转换。