使用git filter-branch更改文件名称大小写 [英] Change file name case using git filter-branch

查看:121
本文介绍了使用git filter-branch更改文件名称大小写的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



作为一个简单的例子,在master中,有一个文件 alpha / beta / foo.cpp ,并在分支 bar 中,有一个文件alpha / beta / Foo .cpp



问题是,当我尝试切换分支时,git不会允许我这样做。有一个错误,我目前没有得心应手,但它基本上看起来像是对文件alpha / beta / Foo进行的更改。


更改。即使随后的 git status 显示cpp也会被覆盖 - 放弃

工作目录是干净的。



由于此回购尚未共享(它实际上是我正在迁移的大型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



脚本看起来像这样

 #! / bin / bash 
if [-e alpha / beta / foo.cpp];然后
mv alpha / beta / foo.cpp alpha / beta / Foo.cpp
fi

最终的结果是重写的refs(预期),但是文件本身实际上并没有按照我的预期在两个分支上重新命名。



有什么建议吗? / b>

解决方案

简答



以下解决方案已从多个来源:


  1. filter-branch --index-filter总是与致命:坏源一起失败


  2. 用Git重命名过去



  3. 这是一个过滤分支调用,它使用索引过滤器重写提交而没有工作副本,所以它应该运行 真的很快 。请注意,作为示例,我将文件 alpha / beta / foo.cpp 重命名为 alpha / beta / Foo.cpp $ b

    与任何具有破坏性的Git操作一样,强烈建议您在使用此

    git filter-branch --index-filter'
    git ls -files --stage | \
    seds: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

请注意, HEAD 是可选的,因为它应该是 filter-branch 的默认值。它将重写从根提交到由HEAD指向的提交的所有提交。如果你想增加filter-branch的速度,你可以传递一系列的提交而不是 HEAD ,比如

  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 

输出应该是空的,这意味着这两个版本。

详细解释

以下细目还可从博客文章 用Git重命名过去。我在这里总结。该脚本的基本思想是创建一个新的索引文件,其中包含文件 foo 的新名称(即 foo 变成 Foo ),然后用新的索引替换旧的索引。



第1步:获取索引文件内容



首先,当前索引文件的内容以一种形式输出,然后可以输入 git update-index - 阶段选项:

git ls-files --stage
100644 195ff081f7d0d37a60181de790ae1c6b9f177be8 0 alpha / beta / foo.cpp
100644 0504de8997941bf10bcfb5af9a0bf472d6c061d3 0 LICENSE
100644 6293167f0eb7389b2f6f6b73e838d3a547787cbf 0 README.md
... etc ...



第2步:重命名文件



由于我们要将 foo.cpp 重命名为 Foo.cpp ,我们使用 sed 用正则表达式替换字符串 foo with Foo

 s:alpha / beta / foo .cpp:alpha / beta / Foo.cpp:

在上面的命令中,我使用一个冒号 sed 命令中分隔正则表达式,但也可以使用其他字符作为分隔符,例如管道 | 。我选择了一个冒号而不是更加标准的正斜杠 / 作为分隔符,这样就没有必要转义文件路径中使用的正斜杠。



在管道 git ls-files --stage sed ,你应该得到以下内容:

  git ls-files --stage | SED S:α/β/ Foo.cpp中:α/β/ Foo.cpp中: 
100644 195ff081f7d0d37a60181de790ae1c6b9f177be8 0α/β/ 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 


现在我们只需替换旧索引与新的,有效地重命名文件:

  mv$ GIT_INDEX_FILE.new$ GIT_INDEX_FILE



总结



下面是整个命令,当一切都放在一起:

git filter-branch --index-filter'
git ls-files --stage | \
seds: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



< h2>文档


  1. git filter-branch


  2. git ls-files


  3. git update-index

  4. Git环境变量



I've got a git repo where some files differ in name by case only across branches.

As a simplified example, in master, there's a file alpha/beta/foo.cpp and in branch bar, there's a file alpha/beta/Foo.cpp.

The problem is that when I attempt to switch branches, git won't allow me to do it. There's an error that I don't have handy at the moment, but it basically looks like

changes to file alpha/beta/Foo.cpp would be overwritten -- aborting

even though a subsequent git status shows the working directory is clean.

Since this repo is not yet shared (it's actually a mirror of a large Perforce depot that I'm working on migrating), I see no problem with using git filter-branch to rewrite the history, but when I do so, any case-sensitive changes I make are lost.

When I use

git filter-branch -f -d /tmp/tmpfs/filter-it \
--tree-filter path/to/script \
--tag-name-filter cat --prune-empty -- --all

with the script looking like this

#!/bin/bash
if [ -e alpha/beta/foo.cpp ] ; then
    mv alpha/beta/foo.cpp alpha/beta/Foo.cpp
fi

the end result winds up with rewritten refs (expected) but the files themselves are not actually renamed across both branches as I would expect.

Any suggestions?

解决方案

The Short Answer

The following solution was modified from multiple sources:

  1. filter-branch --index-filter always failing with "fatal: bad source".

  2. Renaming The Past With Git.

Here is a filter-branch invocation that uses an index-filter to rewrite the commits without a working copy, so it should run really fast. Note that, as an example, I'm renaming the file alpha/beta/foo.cpp to alpha/beta/Foo.cpp.

As with any potentially destructive Git operation, it is highly recommended that you make a backup clone of your repo before you use this:

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

Note that HEAD is optional, because it should be the default for filter-branch. It will rewrite all commits from the root commit to the commit pointed to by HEAD. If you want to increase the speed of the filter-branch even more, you can pass a range of commits instead of HEAD, such as

HEAD~20..HEAD

to rewrite just the last 20 commits. The beginning of the range is exclusive, i.e. it's not rewritten, only its children are, and the ending HEAD is again optional, since its a default.

Verification

It's a good idea to do some quick sanity-checks to verify that the filter-branch did what you expected it to do. First, compare the current history with the previous history:

git diff --name-status refs/original/refs/heads/master
D       foo.cpp
A       Foo.cpp

Notice that when the previous history is compared relative to the current one, the current history no longer has foo.cpp (it's deleted), while Foo.cpp was added to it.

Now confirm that foo.cpp contains the exact same content as Foo.cpp:

git diff refs/original/refs/heads/master:foo.cpp Foo.cpp

The output should be empty, meaning that there are no differences between the two versions.

Detailed Explanation

The following breakdown is also available in more detail from the blog post "Renaming The Past With Git". I am summarizing it here. The basic idea of the script is to create a new index file that contains the new name for the file foo (i.e. foo becomes Foo), and then replace the old index with the new one.

Step 1: Get the Index File Contents

First, the current index file contents are output in a form that can then be fed into git update-index, using the --stage option:

git ls-files --stage
100644 195ff081f7d0d37a60181de790ae1c6b9f177be8 0       alpha/beta/foo.cpp
100644 0504de8997941bf10bcfb5af9a0bf472d6c061d3 0       LICENSE
100644 6293167f0eb7389b2f6f6b73e838d3a547787cbf 0       README.md
...etc...

Step 2: Rename the File

Since we want to rename foo.cpp to Foo.cpp, we use sed with a regular expression to replace the string foo with Foo:

"s:alpha/beta/foo.cpp:alpha/beta/Foo.cpp:"

In the above command, I'm using a colon : to delimit the regexes in the sed command, but you can use other characters as delimiters too, such as pipe |. I chose a colon instead of the more standard forward-slash / as a delimeter so that it wasn't necessary to escape the forward-slashes used in the file paths.

After piping git ls-files --stage through sed, you should get the following:

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...

Step 3: Create a New Index with the Renamed File

Now the modified output of git ls-files --stage can be piped into git update-index --index-info to rename the file in the index. Because we want to create an entirely new index to replace the old one, some environment variables for the path to the index file need to be set first, before invoking the git update-index command:

GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info

Step 4: Replace the Old Index

Now we just replace the old index with the new one, which effectively "renames" the file:

mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"

Summary

Here's the whole command again, when everything is put together:

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

Documentation

  1. git filter-branch.

  2. git ls-files.

  3. git update-index.

  4. Git environment variables.

这篇关于使用git filter-branch更改文件名称大小写的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆