我正试图在我的应用程序中重命名许多文件,并且需要能够通过git从应用程序根目录中的所有子目录中执行重命名 (即 git mv %filenamematch% %replacement%),仅替换匹配的文本。但我不擅长bash脚本。
更新:如果还可以重命名与之匹配的目录,那就太好了!
我正试图在我的应用程序中重命名许多文件,并且需要能够通过git从应用程序根目录中的所有子目录中执行重命名 (即 git mv %filenamematch% %replacement%),仅替换匹配的文本。但我不擅长bash脚本。
更新:如果还可以重命名与之匹配的目录,那就太好了!
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
git ls-files
: 这将生成Git存储库中的文件列表。 它类似于您可以从ls
获得的内容,但它仅输出Git项目文件。 从此列表开始确保不会触及您的.git /目录中的任何内容。
| grep %filenamematch%
: 我们通过grep将来自git ls-files
的列表进行筛选,以将其缩小为仅包含我们要查找的单词或模式的文件名。
| sed -e 's/\(%filenamematch%[^/]*\).*/\1/'
: 我们将这些匹配项通过sed(流编辑器)执行(-e)sed的s(替换)命令来筛选出匹配目录后的任何/和随后的字符(如果有)。
| uniq
: 在匹配是目录的情况下,现在我们已经削减了包含的目录和文件,可能会有许多匹配行。 我们使用uniq将它们全部变成一行。
for file in ...
: shell的“for”命令将遍历列表中的所有项目(文件名)。 每个文件名依次分配给变量“$file”,然后执行分号(;)后面的命令。
sed -e 's/%filenamematch%/%replacement%/'
: 我们使用echo将每个文件名通过sed进行传递,再次使用其替换命令-这次对文件名执行模式替换。
git mv
: 我们使用此git命令将现有文件($file)移动到新文件名(由sed更改)。
更好地理解这一点的方法是观察每个步骤的独立性。为此,请在您的shell中运行以下命令,并观察输出。所有这些都是非破坏性的,只产生列表供您观察:
1.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
; done
for file in $(git ls-files | grep %filenamematch% | sed -e 's/\(%filenamematch%[^/]*\).*/\1/' | uniq); do echo $file | sed -e 's/%filenamematch%/%replacement%/'
; done
git mv path/person/file.rb path/alien/file.rb
进行移动,如果目标目录不存在,则可能会失败。为了解决这个问题,我们可以在搜索模式中添加一些逻辑,以确保匹配的字符串后面没有斜杠。 - Jonathan Camenischfor file in $(git ls-files | grep '%filenamematch%[^/]*$); git mv $file $(echo $file | sed -e 's/%filenamematch%/%replacement%[^/]*$/')
但是,我需要测试以确保我正确使用了grep和sed的正则表达式语法。它们并不总是完全与我习惯的相同。 - Jonathan Camenischsed
:只需运行 do git mv $file ${file//old/new}
。参见:http://mywiki.wooledge.org/BashFAQ/100 - Gregg Lindrename
和正则表达式重命名文件:rename 's/old/new/' *
然后通过添加新文件和删除旧文件将更改在 Git 中进行注册:
git add .
git ls-files -z --deleted | xargs -0 git rm
在较新的 Git 版本中,您可以使用 --all
标志:
git add --all .
git mv
命令移动一个文件是一样的吗?历史记录会被保留吗? - R.D.git mv
命令始终等同于 git rm
和 git add
命令的组合,Git 不会像其他版本控制系统那样为移动操作存储额外的元数据。 - Flimmrename old new *
是一个更简单的方式,其中每个文件名(*
)中第一次出现的new
被替换为old
。 - Chris K--all
标志。我认为这是最清晰的方法来完成这个任务。 - Kyle Heironimusrename
已经提供了一种干净的方式来重命名多个文件。我正在寻找像 git rename
而不是 git mv
这样的东西,而这实际上通过使用 rename
然后跟着一个简单的 git add --all
来完成相同的工作。这是最好的解决方案。 - Praveen虽然来晚了,但这应该可以在BASH中运行(适用于文件和目录,但对于目录我会小心谨慎):
find . -name '*foo*' -exec bash -c 'file={}; git mv $file ${file/foo/bar}' \;
*foo*
和 /foo/bar
之间,我认为 foo 表示 before,bar 表示 after。 - Jonathan在shell循环中使用git mv
?
(假设你在一个合理的平台上!)
在@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
#
这对我来说很有效:
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
- 在每一行的输出结果上执行git mv
命令($line
变量包含每一行),匹配每个文件名的开头并排除.css
扩展名(使用${line%
),然后添加一个新的.scss
扩展名(使用--
来防止文件名和标志之间的歧义)。下面的代码可以用于“干运行”(不会实际执行git mv
):
ls variant*/*.css | while read line; do echo git mv $line to ${line%.css}.scss; done;
git mv
,但是Git似乎仍然可以完美地处理结果。for pipe quote>
。)
或 ``` 字符。 - Jonathan Camenisch以下是使用PowerShell进行操作的一般方法,使用foreach()
。
foreach($varname in Get-ChildItem …) {…}
来迭代文件,使用变量$varname
。这里我使用Get-ChildItem
,因为这是该命令的官方名称,但您可以使用dir
或ls
或任何您已分配给Get-ChildItem
的自定义别名。[io.path]::ChangeExtension($varname, "new-ext"))
更改扩展名。(感谢https://dev59.com/zGct5IYBdhLWcg3wZcmL#12120352。)$(…)
在新命令中评估子表达式,例如作为git mv
的参数。因此,如果您想使用git将所有*.bat
文件重命名为*.cmd
,则应使用以下命令。我已经很好地格式化了它,但您也可以将其全部输入到一行中。
foreach ($bat in Get-ChildItem *.bat) {
git mv $bat $([io.path]::ChangeExtension($bat, "cmd"))
}
我通常使用NetBeans来做这种类型的工作,因为当有更简单的方法时,我会避免使用命令行。NetBeans对git有一些支持,并且您可以通过“收藏夹”选项卡在任意目录/文件上使用它。
这里是我如何将 ./src
文件夹中的所有 .less
文件重命名为 .scss
find ./src -type f |grep "\.less$" | while read line; do git mv -- $line ${line%.less}.scss; done;