esc*_*ten 3 git version-control git-filter-branch
我有一个git repo,其中一些文件的名称只有不同的分支.
作为一个简单的例子,在master中,有一个文件alpha/beta/foo.cpp,在分支中bar,有一个file alpha/beta/Foo.cpp.
问题是,当我尝试切换分支时,git将不允许我这样做.有一个错误,我目前没有方便,但它基本上看起来像
对文件alpha/beta/Foo.cpp的更改将被覆盖 - 中止
即使后续git status显示工作目录是干净的.
由于此repo尚未共享(它实际上是我正在进行迁移的大型Perforce软件仓库的镜像),我认为使用git filter-branch重写历史记录没有任何问题,但是当我这样做时,我做了任何区分大小写的更改迷路了.
我用的时候
git filter-branch -f -d /tmp/tmpfs/filter-it \
--tree-filter path/to/script \
--tag-name-filter cat --prune-empty -- --all
Run Code Online (Sandbox Code Playgroud)
脚本看起来像这样
#!/bin/bash
if [ -e alpha/beta/foo.cpp ] ; then
mv alpha/beta/foo.cpp alpha/beta/Foo.cpp
fi
Run Code Online (Sandbox Code Playgroud)
最终的结果是重写refs(预期),但文件本身实际上并没有像我期望的那样在两个分支上重命名.
有什么建议?
小智 11
以下解决方案是从多个来源修改的:
这是一个过滤器分支调用,它使用索引过滤器来重写提交而不需要工作副本,因此它应该运行得非常快.请注意,作为示例,我将文件重命名alpha/beta/foo.cpp为alpha/beta/Foo.cpp.
与任何可能具有破坏性的Git操作一样,强烈建议您在使用之前制作repo的备份克隆:
git filter-branch --index-filter '
git ls-files --stage | \
sed "s:alpha/beta/foo.cpp:alpha/beta/Foo.cpp:" | \
GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
git update-index --index-info && \
mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"
' HEAD
Run Code Online (Sandbox Code Playgroud)
请注意,这HEAD是可选的,因为它应该是默认值filter-branch.它将重写从根提交到HEAD指向的提交的所有提交.如果你想进一步提高filter-branch的速度,你可以传递一系列的提交而不是HEAD,例如
HEAD~20..HEAD
Run Code Online (Sandbox Code Playgroud)
只重写最后20次提交.范围的开头是独占的,即它不被重写,只有它的子节点,并且结尾HEAD也是可选的,因为它是默认值.
做一些快速的健全性检查以验证过滤器分支是否符合您的预期,这是一个好主意.首先,将当前历史记录与之前的历史进行比较:
git diff --name-status refs/original/refs/heads/master
D foo.cpp
A Foo.cpp
Run Code Online (Sandbox Code Playgroud)
请注意,当先前的历史记录相对于当前历史记录进行比较时,当前历史记录不再具有foo.cpp(已删除),Foo.cpp而是添加到其中.
现在确认foo.cpp包含与以下内容完全相同的内容Foo.cpp:
git diff refs/original/refs/heads/master:foo.cpp Foo.cpp
Run Code Online (Sandbox Code Playgroud)
输出应为空,表示两个版本之间没有差异.
从博客文章" 用Git重命名过去 "中还可以更详细地了解以下细分.我在这里总结一下.该脚本的基本思想是创建一个包含文件新名称foo(即foo成为Foo)的新索引文件,然后用新索引替换旧索引.
首先,git update-index使用以下--stage选项以当前索引文件内容的形式输出,然后输入:
git ls-files --stage
100644 195ff081f7d0d37a60181de790ae1c6b9f177be8 0 alpha/beta/foo.cpp
100644 0504de8997941bf10bcfb5af9a0bf472d6c061d3 0 LICENSE
100644 6293167f0eb7389b2f6f6b73e838d3a547787cbf 0 README.md
...etc...
Run Code Online (Sandbox Code Playgroud)
既然我们要重命名foo.cpp到Foo.cpp,我们用sed一个正则表达式替换字符串foo有Foo:
"s:alpha/beta/foo.cpp:alpha/beta/Foo.cpp:"
Run Code Online (Sandbox Code Playgroud)
在上面的命令中,我使用冒号:来分隔sed命令中的正则表达式,但您也可以使用其他字符作为分隔符,例如管道|.我选择冒号而不是更标准的正斜杠/作为分隔符,这样就不必转义文件路径中使用的正斜杠.
管道后git ls-files --stage通过sed,你应该得到如下:
git ls-files --stage | sed "s:alpha/beta/foo.cpp:alpha/beta/Foo.cpp:"
100644 195ff081f7d0d37a60181de790ae1c6b9f177be8 0 alpha/beta/Foo.cpp
100644 0504de8997941bf10bcfb5af9a0bf472d6c061d3 0 LICENSE
100644 6293167f0eb7389b2f6f6b73e838d3a547787cbf 0 README.md
...etc...
Run Code Online (Sandbox Code Playgroud)
现在git ls-files --stage可以通过管道输入修改后的输出git update-index --index-info来重命名索引中的文件.因为我们想要创建一个全新的索引来替换旧索引,所以在调用git update-index命令之前,需要首先设置索引文件路径的一些环境变量:
GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info
Run Code Online (Sandbox Code Playgroud)
现在我们只用新的索引替换旧索引,这有效地"重命名"了文件:
mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"
Run Code Online (Sandbox Code Playgroud)
当一切都放在一起时,这是整个命令:
git filter-branch --index-filter '
git ls-files --stage | \
sed "s:alpha/beta/foo.cpp:alpha/beta/Foo.cpp:" | \
GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
git update-index --index-info && \
mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"
' HEAD
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1086 次 |
| 最近记录: |