问题描述
我有一个目录中有这样的文件
a.JPG
b.JPG
c .JPG
我想执行类似这样的操作
git mv a.JPG a.jpg
我尝试使用xargs和其他工具,但似乎没有什么工作。
解决方案的核心将是使用一个工具/方法,将自动批量重命名。您可以使用 mv 与 git add 或 git mv 在任一情况下,如果使用不区分大小写的文件系统,则可能需要执行额外的步骤。因此,在我们处理批量重命名之前,讨论如何处理一些情况可能会有帮助。
案例敏感度
某些系统(或系统+文件系统组合,例如Mac OS X上的HFS +文件系统的默认变体)是案例保留,但不区分大小写。在这种系统上,在进行重命名时,您可能需要小心,只涉及更改名称的大小写。通常的解决方法是使用一个临时名称,这个名称的区别不只是case作为两个名称之间的桥梁不同的情况单独(例如 mv foo.JPG tmp& mv tmp foo .jpg
)。
从这里开始,我假设一个不区分大小写的文件系统。
Mac OS X上的 mv 命令可以在一个步骤中处理大小写改变的重命名。如果使用 -i
选项运行,它会给出一个覆盖?提示,如果给定 -n
选项。
git mv
命令有点更偏执的情况。除非给定 -f
/ - force
选项,否则拒绝操作(destination exists错误)。 #这将成功,但如果mv别名使用-n / -i
mv foo,它可能会失败/提示。 JPG foo.jpg
#这将会成功
mv -f bar.JPG bar.jpg
#这将成功,但给出警告
git mv -f quux.JPG quux.jpg
批量重命名选项
Perl rename
所需的操作非常简单,只需要一点shell脚本,但你可以得到Perl 实用程序(乔丹·刘易斯提到的)如果你需要做一些更复杂的事情。您可以尝试,或者如果你觉得使用CPAN,你可以安装,其中包含重命名程序。
>下面使用的 -ef
不是POSIX兼容的。同样,虽然在POSIX中指定 -e
,但它不是纯Bourne兼容的。两者都得到广泛支持。
for f in * .JPG; do
ff =$ {f%.JPG} .jpg
test -e$ f||当不使用nullglob时,可以继续#b $ b test$ f!=$ ff||使用nocaseglob时可以继续#b $ b如果test -e$ ff&&&
! test$ f-ef$ ff;那么#可能在区分大小写的文件系统上
echoskipping< $ f> ;: destination< $ ff>存在且不同于1& 2
continue
fi
#mvwithgit rmandgit add
mv -f$ f$ ff&&
git rm --cached$ f&&&
git add$ ff
done
最后一节> mv , git rm , git add )可以替换为 git mv :
#git mv
pre>
git mv -f$ f$ ff
如果您非常关心重命名在不区分大小写的系统上如何失败,那么您可以使用临时名称:
#temp-basedmvwithgit rmandgit add
t =$ ff.tmp; while test -e$ t; do t =$ t.tmp; done
mv -n$ f$ t&&&
mv -n$ t$ ff&&&
git rm --cached$ f&&&
git add$ ff
或使用 git mv :
#temp- basedgit mv
t =$ ff.tmp; while test -e$ t; do t =$ t.tmp; done
git mv$ f$ t&&&
git mv$ t$ ff
zsh /&#xfeff; zmv
这两个都需要
-f
em> zmv 和 git mv 。& $ 0 $ @'zmv -fp git -o'mv -f''(*)。JPG''$ 1 x.jpg'
现在你已经在Git的索引中对它们进行了重命名和更新,你可以提交它们。
git checkout 之后,仅限重命名
如果您的历史记录还有其他用户,他们可能仍然会有
JPG
文件,当他们最终结帐时)您的提交与jpg
文件。对他们会发生什么?
无论发生什么情况,都不需要重命名为temp,commit,重命名为final,commit。在提交之间移动时, git checkout 不会顺序应用提交。它真的工作通过合并索引和工作树从HEAD到新的提交。这有效地意味着它直接拖动到新的提交,同时拖动HEAD和索引/工作树之间发现的非冲突的更改。
在内部,Git视图重命名为删除和添加。我没有找到任何描述了关于删除和添加顺序的 git checkout 行为的文档,所以我查看了源代码。 git checkout 在任何更新/添加之前处理所有删除(cmd_checkout - > switch_branches - > merge_working_tree( - > reset_tree) - > unpack_trees - > )。
您可以在重命名提交后立即测试此操作:
git checkout HEAD〜注意:分离HEAD
#验证原始名称是否已回到原来
git checkout - #回到您的分支
#验证新名称是否已就位
文件中的 git blame 似乎表示可能的提交:之前删除文件,使得解压缩树更新首先发布在Git 1.5.6-rc0(2008-06-18)。
非常感谢,Linus!
这个行为是为了支持不区分大小写的文件系统I have a directory with files like this
a.JPG b.JPG c.JPG
I would like to perform something like this
git mv a.JPG a.jpg
I tried using xargs and other tools but nothing seems to work.
解决方案The core of the solution will be to use a tool/method that will automate the bulk rename. You can either use mv in combination with git add or just git mv. In either case you may have to take extra steps if you are using a case insensitive filesystem. So before we tackle the bulk renaming, it may be useful to discuss how case is handled a bit.
Case Sensitivity
Some systems (or system+filesystem combinations—like the default variant of the HFS+ filesystem on Mac OS X) are case preserving, but case insensitive. On such systems, you may need to be careful when making renames that involve only changing the case of a name. The usual workaround is to use a temporary name that differs by more than just case as a "bridge" between the two names that differ by case alone (e.g.
mv foo.JPG tmp && mv tmp foo.jpg
).From here on, I will assume a case insensitive filesystem.
The mv command on Mac OS X can handle case-change-only renames in a single step. It will give an "overwrite?" prompt if run with the
-i
option, and it will skip the rename if given the-n
option. It is only succeeding through the "enough rope to hang your self" default operation of many parts of Unix-like systems.The git mv command is a bit more paranoid about the situation. It refuses the operation ("destination exists" error) unless given
-f
/--force
option.# this will succeed, though it may fail/prompt if mv is aliased to use -n/-i mv foo.JPG foo.jpg # this will succeed mv -f bar.JPG bar.jpg # this will succeed but give a warning git mv -f quux.JPG quux.jpg
Bulk Rename Options
Perl rename
The desired operation is simple enough to do with a bit of shell scripting, but you could get the Perl rename utility (the one Jordan Lewis mentions) if you needed to do something that was a lot more complicated. You could try the rename from Debian's perl package, or if you feel up to using CPAN, you could install File::Rename, which includes the rename program.
ksh, bash, zsh, dash
The
-ef
used below is not POSIX compatible. Likewise, while-e
is specified in POSIX, it is not pure-Bourne compatible. Both of them are widely supported though.for f in *.JPG; do ff="${f%.JPG}.jpg" test -e "$f" || continue # possible when not using nullglob test "$f" != "$ff" || continue # possible when using nocaseglob if test -e "$ff" && ! test "$f" -ef "$ff"; then # possible on a case sensitive filesystem echo "skipping <$f>: destination <$ff> exists and is distinct" 1>&2 continue fi # "mv" with "git rm" and "git add" mv -f "$f" "$ff" && git rm --cached "$f" && git add "$ff" done
The last section (mv, git rm, git add) could be replaced with just git mv:
# "git mv" git mv -f "$f" "$ff"
If you are very concerned with how the rename might fail on case insensitive systems, then you could use a temp name:
# temp-based "mv" with "git rm" and "git add" t="$ff.tmp"; while test -e "$t"; do t="$t.tmp"; done mv -n "$f" "$t" && mv -n "$t" "$ff" && git rm --cached "$f" && git add "$ff"
Or with git mv:
# temp-based "git mv" t="$ff.tmp"; while test -e "$t"; do t="$t.tmp"; done git mv "$f" "$t" && git mv "$t" "$ff"
zsh/zmv
This one needs
-f
for both zmv and git mv.zsh -c 'autoload zmv && $0 $@' zmv -fp git -o 'mv -f' '(*).JPG' '$1 x.jpg'
Now that you have them all renamed and updated in Git's index you can commit them.
But will other Git users using case sensitive filesystems be able to check them out?
git checkout After a Case-Only Rename
If there are other users of your history, they will probably still have the
JPG
files and when they eventually checkout (a descendent of) your commit with thejpg
files. What will happen for them?No matter what happens, there is no need for "rename to temp, commit, rename to final, commit". git checkout does not apply commits in sequence when moving between commits. It really works by "merging" the index and working tree from HEAD to the new commit. This effectively means that it "jumps" directly to the new commit while dragging along non-conflicting changes found between HEAD and the index/working-tree.
Internally, Git views renames as a deletion and an addition. I did not find any documentation that described the behavior of git checkout with respect to the order of deletions and additions, so I looked at the source code. git checkout processes all deletions before any updates/additions (cmd_checkout -> switch_branches -> merge_working_tree (-> reset_tree) -> unpack_trees -> check_updates).
You can test this out right after your rename commit:
git checkout HEAD~ # note: detached HEAD # verify that the original names are back in place git checkout - # back to your branch # verify that the new names are in place again
The git blame on the file seemed to indicate a likely commit: Make unpack-tree update removed files before any updated files, which was first released in Git 1.5.6-rc0 (2008-06-18). So, though undocumented(?), this behavior was implemented specifically to support case insensitive filesystems.
Thanks, Linus!
这篇关于如何重命名大量的文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!