问题
I am trying to rename many files in my application and need to be able to do a rename in all subdirectories from the app root through git (i.e. git mv %filenamematch% %replacement%) that only replaces the matching text. I'm no good with bash scripting though.
update: would be good it if also renamed directories that match as well!
回答1:
This should do the trick:
for file in $(git ls-files | grep %filenamematch% | sed -e 's/\(%filenamematch%[^/]*\).*/\1/' | uniq); git mv $file $(echo $file | sed -e 's/%filenamematch%/%replacement%/')
To follow what this is doing, you'll need to understand piping with "|" and command substitution with "$(...)". These powerful shell constructs allow us to combine several commands to get the result we need. See Pipelines and Command Substitution.
Here's what's going on in this one-liner:
git ls-files
: This produces a list of files in the Git repository. It's similar to what you could get fromls
, except it only outputs Git project files. Starting from this list ensures that nothing in your .git/ directory gets touched.| grep %filenamematch%
: We take the list fromgit ls-files
and pipe it through grep to filter it down to only the file names containing the word or pattern we're looking for.| sed -e 's/\(%filenamematch%[^/]*\).*/\1/'
: We pipe these matches through sed (the stream editor), executing (-e) sed's s (substitute) command to chop off any / and subsequent characters after our matching directory (if it happens to be one).| uniq
: In cases where the match is a directory, now that we've chopped off contained directories and files, there could be many matching lines. We use uniq to make them all into one line.for file in ...
: The shell's "for" command will iterate through all the items (file names) in the list. Each filename in turn, it assigns to the variable "$file" and then executes the command after the semicolon (;).sed -e 's/%filenamematch%/%replacement%/'
: We use echo to pipe each filename through sed, using it's substitute command again--this time to perform our pattern replacement on the filename.git mv
: We use this git command to mv the existing file ($file) to the new filename (the one altered by sed).
One way to understand this better would be to observe each of these steps in isolation. To do that, run the commands below in your shell, and observe the output. All of these are non-destructive, only producing lists for your observation:
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); echo $file
for file in $(git ls-files | grep %filenamematch% | sed -e 's/\(%filenamematch%[^/]*\).*/\1/' | uniq); echo $file | sed -e 's/%filenamematch%/%replacement%/'
回答2:
Late to the party but, this should work in BASH (for files and directories, but I'd be careful regarding directories):
find . -name '*foo*' -exec bash -c 'file={}; git mv $file ${file/foo/bar}' \;
回答3:
Rename the files with a regular expression using the command rename
:
rename 's/old/new/' *
Then register the changes with Git by adding the new files and deleting the old ones:
git add .
git ls-files -z --deleted | xargs -0 git rm
In newer versions of Git you can use the --all
flag instead:
git add --all .
回答4:
git mv
inside a shell loop?
What's the purpose of git-mv?
(Assuming you are on a platform with a reasonable shell!)
Building on 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
Here is a longer example (in bash or similar)
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
#
see also: Ad Hoc Data Analysis From The Unix Command Line
回答5:
This worked well for my use case:
ls folder*/*.css | while read line; do git mv -- $line ${line%.css}.scss; done;
Explanation:
ls folder*/*.css
- Usesls
to get a list of all directories with CSS files that match the glob pattern. (Directories starting withfolder
and containing files with.css
extensions)while read line
- Reading in the resulting output of thels
command line-by-linedo git mv -- $line ${line%.css}.css
- Executegit mv
on the line-by-line output ($line
variable contains each line) while matching the beginning of each filename and excluding the.css
extension (with${line%
and adding a new.scss
extension (--
is used to prevent ambiguity between filenames and flags)
Code below can be used for a "dry run" (won't actually execute git mv
):
ls variant*/*.css | while read line; do echo git mv $line to ${line%.css}.scss; done;
回答6:
I solved this for myself by using
https://github.com/75lb/renamer - worked perfectly :-)
Doesn't explicitly do a git mv
but git seemed to deal with the results perfectly anyway.
When I followed all the steps in the top answer I got stuck at the final step, when my console responded with
for pipe quote>
If anyone can explain this I'd appreciate it!
回答7:
I am usually using NetBeans to do this type of stuff because I avoid the command line when there is a easier way. NetBeans has some support for git, and you can use it on arbitrary directory/file via the "Favorites" tab.
来源:https://stackoverflow.com/questions/9984722/git-rename-many-files-and-folders