git重命名许多文件和文件夹

Posted

技术标签:

【中文标题】git重命名许多文件和文件夹【英文标题】:git rename many files and folders 【发布时间】:2012-04-16 14:28:10 【问题描述】:

我正在尝试重命名我的应用程序中的许多文件,并且需要能够通过仅替换匹配文本的 git(即 git mv %filenamematch% %replacement%)在应用程序根目录中的所有子目录中进行重命名。不过我不擅长 bash 脚本。

更新:如果也重命名匹配的目录就好了!

【问题讨论】:

你有例子吗? '%filenamematch%' 喜欢什么;它总是在开头或结尾或中间还是什么? %replacement% 是什么样的? 可以是 person.rb 或 lookitisa_person_here.html。在这两种情况下,该人都需要匹配并从该人更改为其他人。它也应该区分大小写 【参考方案1】:

这应该可以解决问题:

for file in $(git ls-files | grep %filenamematch% | sed -e 's/\(%filenamematch%[^/]*\).*/\1/' | uniq); do git mv $file $(echo $file | sed -e 's/%filenamematch%/%replacement%/'); done

要了解它的作用,您需要了解带有“|”的管道并用“$(...)”替换命令。这些强大的 shell 结构允许我们组合几个命令来获得我们需要的结果。请参阅Pipelines 和Command Substitution。

这就是这个单线中发生的事情:

    git ls-files:这会生成 Git 存储库中的文件列表。它类似于您可以从ls 获得的内容,只是它只输出 Git 项目文件。从这个列表开始可以确保你的 .git/ 目录中没有任何东西被触及。

    | grep %filenamematch%:我们从git ls-files 获取列表并通过 grep 将其过滤为仅包含我们正在寻找的单词或模式的文件名。

    | sed -e 's/\(%filenamematch%[^/]*\).*/\1/':我们通过sed(流编辑器)管道这些匹配,执行(-e)sed的s(替代)命令在我们匹配的目录之后删除任何/和后续字符(如果它恰好是一)。

    | uniq: 如果匹配的是一个目录,现在我们已经删除了包含的目录和文件,可能会有很多匹配的行。我们使用 uniq 将它们全部放在一行中。

    for file in ...:shell 的"for" command 将遍历列表中的所有项目(文件名)。每个文件名依次赋值给变量“$file”,然后执行分号(;)后面的命令。

    sed -e 's/%filenamematch%/%replacement%/':我们使用echo 将每个文件名通过 sed 管道传输,再次使用它的替换命令——这次是对文件名执行我们的模式替换。

    git mv:我们使用这个 git 命令将现有文件 ($file) 转换为新文件名(由 sed 更改的文件名)。

更好地理解这一点的一种方法是单独观察这些步骤中的每一个。为此,请在您的 shell 中运行以下命令,并观察输出。所有这些都是非破坏性的,仅生成列表供您观察:

    git ls-files

    git ls-files | grep %filenamematch%

    git ls-files | grep %filenamematch% | sed -e 's/\(%filenamematch%[^/]*\).*/\1/'

    git ls-files | grep %filenamematch% | sed -e 's/\(%filenamematch%[^/]*\).*/\1/' | uniq

    for file in $(git ls-files | grep %filenamematch% | sed -e 's/\(%filenamematch%[^/]*\).*/\1/' | uniq); do echo $file;完成

    for file in $(git ls-files | grep %filenamematch% | sed -e 's/\(%filenamematch%[^/]*\).*/\1/' | uniq); do echo $file | sed -e 's/%filenamematch%/%replacement%/';完成

【讨论】:

当然。这是那些看起来有点令人生畏的 bash 单行代码之一。也许其他人会对使其更简洁或可读性提出建议。我将添加一些解释性说明。 注意:此命令确实有一个缺陷:如果您有任何与您的替换模式匹配的目录,它们将导致错误。换句话说,它将尝试使用git mv path/person/file.rb path/alien/file.rb 进行移动,如果目标目录不存在,这可能会失败。为了解决这个问题,我们可以在我们正在搜索的模式中添加一些逻辑,以确保匹配的字符串后面没有斜杠。 更健壮的模式看起来像这样:for file in $(git ls-files | grep '%filenamematch%[^/]*$); git mv $file $(echo $file | sed -e 's/%filenamematch%/%replacement%[^/]*$/') 但是,我需要测试以确保我正确使用了 grep 和 sed 的正则表达式语法。它们并不总是和我习惯的完全一样。 在这一点上,你可能根本不需要seddo git mv $file $file//old/new。见:mywiki.wooledge.org/BashFAQ/100 我不得不将 git move 语句用do %git mv ....%; done 括起来,以使其按照cyberciti.biz/faq/linux-unix-bash-for-loop-one-line-command 中的描述工作【参考方案2】:

使用命令rename 使用正则表达式重命名文件:

rename 's/old/new/' *

然后通过添加新文件并删除旧文件来向 Git 注册更改:

git add .
git ls-files -z --deleted | xargs -0 git rm

在较新版本的 Git 中,您可以改用 --all 标志:

git add --all .

【讨论】:

这和git mv文件一样吗?历史保留了吗? 是的,是一样的。 git mv 始终与 git rmgit add 相同,Git 不会像其他版本控制系统那样存储额外的元数据。 很好的方法,很容易理解。不过,对我来说,rename old new * 在每个文件名(*)中第一次出现的new 被替换为old TIL --all 标志。我认为这是最清晰的方法。 这正是我想要的。 rename 已经提供了一种干净的方式来重命名多个文件。我一直在寻找类似git rename 而不是git mv 的东西,这通过使用rename 后跟一个简单的git add --all 可以有效地完成相同的工作。在这里轻松找到最佳解决方案。【参考方案3】:

迟到了,但这应该在 BASH 中工作(对于文件和目录,但我会小心目录):

find . -name '*foo*' -exec bash -c 'file=; git mv $file $file/foo/bar' \;

【讨论】:

如果您能在脚本中解释之前和之后的位置会很酷。在*foo*/foo/bar foo=before bar=after 之间是我的假设 是的。之前是“foo”(您之前评论中的人)。之后是“酒吧”。 BASH shell 语法 $variable/pattern-to-match/replace-with 用于生成新名称。 我在 Ubuntu 14.04 上得到 sh: 1: Bad substitution,除非我将 sh 替换为 bash,请参阅 here。 @GoZoner 哦,是$file/foo/bar"shell parameter expansion" technique?【参考方案4】:

git mv 在 shell 循环中?

What's the purpose of git-mv?

(假设您在具有合理外壳的平台上!)

以the answer by @jonathan-camenish 为基础:

# things between backticks are 'subshell' commands.  I like the $() spelling over ``
# git ls-files     -> lists the files tracked by git, one per line
# | grep somestring -> pipes (i.e., "|") that list through a filter
#     '|' connects the output of one command to the input of the next
# leading to:  for file in some_filtered_list
# git mv  f1 f2  ->  renames the file, and informs git of the move.
# here 'f2' is constructed as the result of a subshell command
#     based on the sed command you listed earlier.

for file in `git ls-files | grep filenamematch`; do git mv $file `echo $file | sed -e 's/%filenamematch%/%replacement%/'`; done

这是一个更长的例子(在 bash 或类似的)

mkdir blah; cd blah; 
touch old_f1,f2,f3,f4 same_f1,f2,f3
git init && git add old_* same_* && git commit -m "first commit"
for file in $(git ls-files | grep old); do git mv $file $(echo $file | sed -e 's/old/new/'); done
git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   renamed:    old_f1 -> new_f1
#   renamed:    old_f2 -> new_f2
#   renamed:    old_f3 -> new_f3
#   renamed:    old_f4 -> new_f4
#

另见:Ad Hoc Data Analysis From The Unix Command Line

【讨论】:

基本上,无论您在哪里使用mv,都使用git mv。如果您需要更清楚,请使用一些示例重命名来更新您的问题。 更新了帖子,也许现在更清楚了,有什么想法吗?我不擅长 bash 脚本。 我希望扩展示例有所帮助! 我真的认为这是问题的正确答案。 GIT 能够跟踪文件移动。没有专有的复制脚本。谢谢!【参考方案5】:

这对我的用例很有效:

ls folder*/*.css | while read line; do git mv -- $line $line%.css.scss; done;

解释:

ls folder*/*.css - 使用 ls 获取所有目录的列表,其中包含与 glob 模式匹配的 CSS 文件。 (以folder 开头并包含带有.css 扩展名的文件的目录) while read line - 逐行读取ls 命令行的结果输出 do git mv -- $line $line%.css.css - 在逐行输出($line 变量包含每一行)上执行git mv,同时匹配每个文件名的开头并排除.css 扩展名(使用$line% 并添加新的@987654333 @ 扩展名(-- 用于防止文件名和标志之间的歧义)

下面的代码可用于“试运行”(实际上不会执行git mv):

ls variant*/*.css | while read line; do echo git mv $line to $line%.css.scss; done;

【讨论】:

【参考方案6】:

我自己解决了这个问题 https://github.com/75lb/renamer - 完美运行 :-) 没有明确地执行git mv,但 git 似乎无论如何都能完美地处理结果。

当我按照最佳答案中的所有步骤操作时,我卡在最后一步,当我的控制台响应时

for pipe quote&gt;

如果有人能解释一下,我将不胜感激!

【讨论】:

这看起来像是在提示关闭 )` 字符【参考方案7】:

我通常使用 NetBeans 来做这类事情,因为当有更简单的方法时,我会避免使用命令行。 NetBeans 对 git 有一些支持,您可以通过“收藏夹”选项卡在任意目录/文件上使用它。

【讨论】:

【参考方案8】:

这是我如何将./src 文件夹中的所有.less 文件重命名为.scss

find ./src -type f |grep "\.less$" | while read line; do git mv -- $line $line%.less.scss; done;

【讨论】:

以上是关于git重命名许多文件和文件夹的主要内容,如果未能解决你的问题,请参考以下文章

git重命名文件和文件夹

Git和文件重命名和替换

git:重命名文件并更改文件内容

git diff 重命名/移动和修改的文件,但跳过重命名/移动和相同的文件

如何使用 git 真正显示重命名文件的日志

git gui app 显示检测到的重命名