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 的正则表达式语法。它们并不总是和我习惯的完全一样。
在这一点上,你可能根本不需要sed
:do 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 rm
和 git 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>
如果有人能解释一下,我将不胜感激!
【讨论】:
这看起来像是在提示关闭)
或 `
字符【参考方案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重命名许多文件和文件夹的主要内容,如果未能解决你的问题,请参考以下文章