使用git筛选器分支更改文件名大小写
我有一个git repo,其中一些文件的名称仅在不同的分支之间存在大小写差异 作为一个简化的示例,在master中有一个文件使用git筛选器分支更改文件名大小写,git,version-control,git-filter-branch,Git,Version Control,Git Filter Branch,我有一个git repo,其中一些文件的名称仅在不同的分支之间存在大小写差异 作为一个简化的示例,在master中有一个文件alpha/beta/foo.cpp,在branchbar中有一个文件alpha/beta/foo.cpp 问题是,当我尝试切换分支时,git不允许我这样做。有一个错误,我现在手头没有,但它基本上看起来像 对文件alpha/beta/Foo.cpp的更改将被覆盖--正在中止 即使后续的git状态显示工作目录是干净的 由于这个repo尚未共享(它实际上是我正在进行迁移的大型p
alpha/beta/foo.cpp
,在branchbar
中有一个文件alpha/beta/foo.cpp
问题是,当我尝试切换分支时,git不允许我这样做。有一个错误,我现在手头没有,但它基本上看起来像
对文件alpha/beta/Foo.cpp的更改将被覆盖--正在中止
即使后续的git状态显示工作目录是干净的
由于这个repo尚未共享(它实际上是我正在进行迁移的大型performe仓库的镜像),我认为使用git filter branch
重写历史记录没有问题,但是当我这样做时,我所做的任何区分大小写的更改都会丢失
当我使用
git filter-branch -f -d /tmp/tmpfs/filter-it \
--tree-filter path/to/script \
--tag-name-filter cat --prune-empty -- --all
脚本是这样的
#!/bin/bash
if [ -e alpha/beta/foo.cpp ] ; then
mv alpha/beta/foo.cpp alpha/beta/Foo.cpp
fi
最终结果是重写了引用(预期),但文件本身并没有像我预期的那样跨两个分支重命名
有什么建议吗?简短的回答
从多个来源修改了以下解决方案:
下面是一个筛选器分支调用,它使用索引筛选器重写提交,而不使用工作副本,因此它应该运行得非常快。注意,作为一个例子,我将文件alpha/beta/foo.cpp
重命名为alpha/beta/foo.cpp
与任何具有潜在破坏性的Git操作一样,强烈建议您在使用此操作之前对回购进行备份克隆:
git过滤器分支--索引过滤器'
git ls文件——阶段|\
sed“s:alpha/beta/foo.cpp:alpha/beta/foo.cpp:”\
GIT_INDEX_FILE=$GIT_INDEX_FILE.new\
git更新索引--索引信息&&\
mv“$GIT_INDEX_FILE.new”“$GIT_INDEX_FILE”
”“头
请注意,头
是可选的,因为它应该是过滤器分支
的默认值。它将重写所有从根提交到头指向的提交的提交。如果您想进一步提高筛选器分支的速度,可以传递一系列提交,而不是头
,例如
HEAD~20..HEAD
只重写最后20次提交。范围的开头是排他性的,即它不被重写,只有它的子项是排他性的,而结尾的HEAD
也是可选的,因为它是默认值
验证
最好做一些快速的健全性检查,以验证过滤器分支是否完成了您期望它完成的任务。首先,将当前历史与以前的历史进行比较:
git diff --name-status refs/original/refs/heads/master
D foo.cpp
A Foo.cpp
请注意,当将上一个历史记录与当前历史记录进行比较时,当前历史记录不再具有foo.cpp
(已删除),而foo.cpp
已添加到它
现在确认foo.cpp
包含与foo.cpp
完全相同的内容:
git diff refs/original/refs/heads/master:foo.cpp Foo.cpp
输出应该为空,这意味着两个版本之间没有差异
详细说明
下面的细目也可以在博客文章“”中找到。我在这里总结一下。脚本的基本思想是创建一个新的索引文件,其中包含文件foo
的新名称(即foo
变为foo
),然后用新索引替换旧索引
步骤1:获取索引文件内容
首先,当前索引文件内容以某种形式输出,然后使用--stage
选项将其输入到git update index
:
git ls文件——stage
100644 195ff081f7d0d37a60181de790ae1c6b9f177be8 0 alpha/beta/foo.cpp
100644 0504de8997941bf10bcfb5af9a0bf472d6c061d3 0许可证
100644 6293167f0eb7389b2f6f6b73e838d3a547787cbf 0 README.md
等
步骤2:重命名文件
由于我们希望将foo.cpp
重命名为foo.cpp
,因此我们使用带有正则表达式的sed
将字符串foo
替换为foo
:
"s:alpha/beta/foo.cpp:alpha/beta/Foo.cpp:"
在上面的命令中,我使用冒号:
来分隔sed
命令中的正则表达式,但是您也可以使用其他字符作为分隔符,例如管道
。我选择了冒号而不是更标准的正斜杠/
作为delimeter,这样就不必转义文件路径中使用的正斜杠
在管道化git ls文件--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...
步骤3:使用重命名的文件创建新索引
现在,git ls files--stage
的修改输出可以通过管道输送到git update index--index info
以重命名索引中的文件。因为我们希望创建一个全新的索引来替换旧索引,所以在调用git update index
命令之前,需要首先设置索引文件路径的一些环境变量:
GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info
步骤4:替换旧索引
现在,我们只需将旧索引替换为新索引,这将有效地“重命名”文件:
mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"
总结
下面是整个命令,当所有内容都放在一起时:
git过滤器分支--索引过滤器'
git ls文件——阶段|\
sed“s:alpha/beta/foo.cpp:alpha/beta/foo.cpp:”\
GIT_INDEX_FILE=$GIT_INDEX_FILE.new\
git更新索引--索引信息&&\
mv“$GIT_INDEX_FILE.new”“$GIT_INDEX_FILE”
”“头
文档
My.profile别名基于@cupcake的答案,修复了如何扩展变量的问题
用法示例:
mvidx src/myfile.cs src/myfolder/myfile.cs origin/develop..feature/myfeature
~/.profilebash配置文件
alias mvidx=rewriteIndexToMoveFile
red="\e[0;31m"
green="\e[0;32m"
rewriteIndexToMoveFile() {
if [ $# -ne 3 ] ; then
echo -e "Rewrite index to move a file in a range of commits."
echo -e "Args: <from file path> <to file path> <range of commits>"
echo -e "${green}Examples:"
echo -e "mvidx src/myproject/myfile.cs src/myproject/subfolder/myfile.cs origin/develop..feature/myfeature"
return
fi
fromFilePath=$1
toFilePath=$2
revisionRange=$3
echo -e "Renaming ${red}$fromFilePath${nc} to ${red}$toFilePath${nc}."
git filter-branch --index-filter 'git ls-files -s \
| sed "s|\t'"$fromFilePath"'|\t'"$toFilePath"'|" \
| GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info \
&& mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' \
$revisionRange
}
alias mvidx=rewriteIndexToMoveFile
红色=“\e[0;31m”
绿色=“\e[0;32m”
重写IndexToMoveFile(){
如果[$#-ne 3];则
echo-e“重写索引以将文件移动到