-
Star
(272)
You must be signed in to star a gist -
Fork
(98)
You must be signed in to fork a gist
-
-
Save emiller/6769886 to your computer and use it in GitHub Desktop.
| #!/bin/bash | |
| # | |
| # git-mv-with-history -- move/rename file or folder, with history. | |
| # | |
| # Moving a file in git doesn't track history, so the purpose of this | |
| # utility is best explained from the kernel wiki: | |
| # | |
| # Git has a rename command git mv, but that is just for convenience. | |
| # The effect is indistinguishable from removing the file and adding another | |
| # with different name and the same content. | |
| # | |
| # https://git.wiki.kernel.org/index.php/GitFaq#Why_does_Git_not_.22track.22_renames.3F | |
| # | |
| # While the above sucks, git has the ability to let you rewrite history | |
| # of anything via `filter-branch`. This utility just wraps that functionality, | |
| # but also allows you to easily specify more than one rename/move at a | |
| # time (since the `filter-branch` can be slow on big repos). | |
| # | |
| # Usage: | |
| # | |
| # git-rewrite-history [-d/--dry-run] [-v/--verbose] <srcname>=<destname> <...> <...> | |
| # | |
| # After the repsitory is re-written, eyeball it, commit and push up. | |
| # | |
| # Given this example repository structure: | |
| # | |
| # src/makefile | |
| # src/test.cpp | |
| # src/test.h | |
| # src/help.txt | |
| # README.txt | |
| # | |
| # The command: | |
| # | |
| # git-rewrite-history README.txt=README.md \ <-- rename to markdpown | |
| # src/help.txt=docs/ \ <-- move help.txt into docs | |
| # src/makefile=src/Makefile <-- capitalize makefile | |
| # | |
| # Would restructure and retain history, resulting in the new structure: | |
| # | |
| # docs/help.txt | |
| # src/Makefile | |
| # src/test.cpp | |
| # src/test.h | |
| # README.md | |
| # | |
| # @author emiller | |
| # @date 2013-09-29 | |
| # | |
| function usage() { | |
| echo "usage: `basename $0` [-d/--dry-run] [-v/--verbose] <srcname>=<destname> <...> <...>" | |
| [ -z "$1" ] || echo $1 | |
| exit 1 | |
| } | |
| [ ! -d .git ] && usage "error: must be ran from within the root of the repository" | |
| dryrun=0 | |
| filter="" | |
| verbose="" | |
| repo=$(basename `git rev-parse --show-toplevel`) | |
| while [[ $1 =~ ^\- ]]; do | |
| case $1 in | |
| -d|--dry-run) | |
| dryrun=1 | |
| ;; | |
| -v|--verbose) | |
| verbose="-v" | |
| ;; | |
| *) | |
| usage "invalid argument: $1" | |
| esac | |
| shift | |
| done | |
| for arg in $@; do | |
| val=`echo $arg | grep -q '=' && echo 1 || echo 0` | |
| src=`echo $arg | sed 's/\(.*\)=\(.*\)/\1/'` | |
| dst=`echo $arg | sed 's/\(.*\)=\(.*\)/\2/'` | |
| dir=`echo $dst | grep -q '/$' && echo $dst || dirname $dst` | |
| [ "$val" -ne 1 ] && usage | |
| [ ! -e "$src" ] && usage "error: $src does not exist" | |
| filter="$filter \n\ | |
| if [ -e \"$src\" ]; then \n\ | |
| echo \n\ | |
| if [ ! -e \"$dir\" ]; then \n\ | |
| mkdir -p ${verbose} \"$dir\" && echo \n\ | |
| fi \n\ | |
| mv $verbose \"$src\" \"$dst\" \n\ | |
| fi \n\ | |
| " | |
| done | |
| [ -z "$filter" ] && usage | |
| if [[ $dryrun -eq 1 || ! -z $verbose ]]; then | |
| echo | |
| echo "tree-filter to execute against $repo:" | |
| echo -e "$filter" | |
| fi | |
| [ $dryrun -eq 0 ] && git filter-branch -f --tree-filter "`echo -e $filter`" |
This script knows its job. Thank you!
I have been looking for this kind of script. We have some SQL scripts stored as ".txt" files in many directories. I am new to bash script. Is there any way I can rename all files with '.txt' extension to '.sql' in batch?
Thanks for this script! However, it's worth noting that Git now officially recommends against using git filter-branch due to performance and safety concerns. From the official Git documentation:
WARNING: git filter-branch has a plethora of pitfalls that can produce non-obvious manglings of the intended history rewrite [...] Please use an alternative history filtering tool such as git filter-repo.
I've created a PowerShell prototype that uses git-filter-repo instead. It's still experimental but aims to:
- Work cross-platform (Windows/Linux/macOS)
- Use Git's built-in rename detection
- Preserve file history properly using
git-filter-repo
Would love to get feedback and suggestions for improvement if anyone wants to test it out!
Anything like this with a date of 2013-09-29, I could consider more inspiration, not something one should consider using. Although, you can probably just drop it in chatgpt with a copy of this discussion, and chatgpt will output something ready to review and then debug.
git: 'rewrite-history' is not a git command. See 'git --help'.