Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

git rename many files and folders

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!

like image 655
Jonathan Avatar asked Apr 02 '12 22:04

Jonathan


3 Answers

This should do the trick:

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

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:

  1. git ls-files: This produces a list of files in the Git repository. It's similar to what you could get from ls, except it only outputs Git project files. Starting from this list ensures that nothing in your .git/ directory gets touched.

  2. | grep %filenamematch%: We take the list from git 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.

  3. | 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).

  4. | 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.

  5. 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 (;).

  6. 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.

  7. 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:

  1. git ls-files

  2. git ls-files | grep %filenamematch%

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

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

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

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

like image 89
Jonathan Camenisch Avatar answered Nov 09 '22 21:11

Jonathan Camenisch


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 .
like image 38
Flimm Avatar answered Nov 09 '22 21:11

Flimm


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}' \;
like image 16
GoZoner Avatar answered Nov 09 '22 22:11

GoZoner