Skip to content

Instantly share code, notes, and snippets.

@xkr47
Last active June 16, 2023 07:11
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xkr47/f766f4082112c086af63ef8d378c4304 to your computer and use it in GitHub Desktop.
Save xkr47/f766f4082112c086af63ef8d378c4304 to your computer and use it in GitHub Desktop.
Script for moving/renaming files/directories arbitrarily in whole git history of single branch, using perl expressions

Install into PATH e.g. $HOME/bin/ as "git-filter-mv" and chmod a+rx git-filter-mv

It works with any special characters in filenames like tabs and linefeeds. It works with empty commits.

Based on example from git-filter-branch manpage.

If you want to print debug messages, use STDERR e.g. print STDERR "File: $_\n";

Examples:

git filter-mv 's!^!subdir/!'

➜ moves all files to a subdirectory "subdir/" in all commits of the current branch

git filter-mv 's!^source/!src/!'

➜ moves all files in "source/" to "src/" in all commits of the current branch

git filter-mv 's!^foo/bar.txt!foo/barbar.txt!'

➜ renames foo/bar.txt to foo/barbar.txt directly below the repository root in all commits of the current branch

git filter-mv 's!(^|/)bar.txt!$1barbar.txt!'

➜ renames bar.txt to barbar.txt in any directory in all commits of the current branch

#!/bin/bash
set -eo pipefail
if [ $# != 1 ]; then
echo "usage: git ${0#*git-} <perl>"
echo "Filter branch by transforming all paths with given perl expression"
echo "Example: git ${0#git-} 's!^!subdir/!' # moves all files to a subdirectory"
exit 1
fi
perl="$1"
shift
if ! git ls-files -z | perl -e 'use strict; use warnings; $/="\0"; $_=<>; '"$perl;" > /dev/null ; then
echo 'ERROR: Given perl script broken, please correct above errors and try again'
exit 1
fi
script='
use strict;
use warnings;
sub ren {
$_ = $_[0];
'"$perl"';
$_
}
$/="\0";
while(<>) {
s/\t(.*)$/"\t".ren($1)/e;
print;
}
'
shellquote () {
perl -e '
use strict;
use warnings;
print "'\''",$ARGV[0] =~ s/'\''/'\''\\'\'''\''/gr,"'\''";
' "$1"
}
git filter-branch --index-filter \
'if git ls-files | grep -q . ; then
git ls-files --stage -z |
perl -e '"$(shellquote "$script")"' | tee /tmp/lada |
GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index -z --index-info &&
touch "$GIT_INDEX_FILE.new" &&
mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"
fi' \
HEAD
@arvindsree
Copy link

Works like a charm. This was the only script that finally worked for me. I would also add --tag-name-filter cat to migrate tags and --prune-empty to the git filter-branch command

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment