23

I want to rename a file for all the commits in Git repository. Here's what I have tried:

git filter-branch --index-filter 'git mv -k <old name> <new name>' HEAD

This command went through all the commits in the repository, but it ended up with the message:

WARNING: Ref 'refs/heads/master' is unchanged

which means nothing had been changed. What had been wrong here?

Note that the file which I wanted to rename doesn't exist from the first commit. Therefore if I do not use -k in git mv, I mean if I use:

git filter-branch --index-filter 'git mv <old name> <new name>' HEAD`

Git would error out while trying the first commit saying something like "bad source...".

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
York
  • 2,056
  • 1
  • 15
  • 23
  • you're going to change history with that. – three Feb 25 '12 at 18:20
  • 4
    Yes, that is the point of filter-branch. – Rodney Folz Feb 25 '12 at 18:22
  • 1
    I understand that "filter-branch" is all about rewriting history which is exactly what I wanted. – York Feb 25 '12 at 18:22
  • Can you try with `--tree-filter`? I know it will be slower than index-only, but if it works … – knittl Feb 25 '12 at 18:42
  • Yes, I did try "tree-filter", like this: $ git filter-branch --tree-filter 'mv ` `' HEAD The problem was that the file I wanted to rename does not exist from first commit, therefor git errors out with "mv: cannot stat ``': No such file or director" How can I test whether the file exsit or not before "mv" command? – York Feb 25 '12 at 18:58
  • Duplicate of [filter-branch --index-filter always failing with "fatal: bad source"](http://stackoverflow.com/questions/15028580/filter-branch-index-filter-always-failing-with-fatal-bad-source) –  May 24 '14 at 05:08

3 Answers3

21

I finally solved my original problem by using:

git filter-branch --tree-filter '
if [ -f <old name> ]; then
  mv <old name> <new name>
fi' --force HEAD
York
  • 2,056
  • 1
  • 15
  • 23
  • 5
    For a solution using `--index-filter`, please refer to [this answer](http://stackoverflow.com/a/15029691/572644) – Daniel Hilgarth Feb 22 '13 at 17:26
  • 3
    Note that `git mv` doesn't work with an index-filter, because `git mv` needs a working copy, which you only get with a tree-filter. –  May 24 '14 at 05:07
  • 4
    Replace `if [ -f ]` with `if [ -d ]` to rename a directory instead of a file. – naitsirhc May 27 '15 at 14:02
6

Nowadays it is best to use filter-repo - https://github.com/newren/git-filter-repo :

git filter-repo --path-rename full/path/to/file.old:full/path/to/file.new

See my own blog page: http://ben.goodacre.name/tech/Rename-file-for-all-previous-commits-in-Git

0

For anyone interested in just renaming a file - git rebase -i will work just fine. Include the commit that created the file in the rebase, mark it with edit, and just rename the file. Git will apply the next commits to this file correctly.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mr_and_Mrs_D
  • 32,208
  • 39
  • 178
  • 361
  • 1
    that works, but requires you to manually rename the file for every commit. No big deal if it's one commit, but a filter-branch will cleanup the whole branch in one pass – STW Nov 12 '17 at 15:53
  • 1
    when I tried it @STW I just had to rename the file in the one commit that created it, it really surprised me (in a good way) that git was smart enough to apply modifications in the renamed file afterwards – Mr_and_Mrs_D Nov 13 '17 at 12:51
  • yep, and if you just need to go back a commit or two it's a faster and safer approach. Choose your weapon wisely! And yeah, git is epic – STW Nov 13 '17 at 13:04
  • this creates a new commit from your HEAD commit with the file renamed but if you look in the history you will see that the file still has the old name, i.e. it will not help if you `rebase` onto another branch with the same file – Mihai Soloi Mar 18 '20 at 20:18
  • even when you include the new commit with the renamed file @MihaiSoloi – Mr_and_Mrs_D Mar 19 '20 at 17:46