7

Consider following usecase:

I'm working on my local branch while refactoring has been done on the main branch. Now some files (classes) have been renamed and/or moved to new locations. When merging i get lots and lots of import-errors due to missing classes.

--A--B--C--D--E-- (master)
   \           \
    F--G--H--I--J  (topic)

In A there are the old names wich i used in F--G--H--I.

In B--C--D--E the files are refactored resulting in new file names in E. This includes chained renames like

   B: path/to/File.java              
-> C: path/to/BetterName.java
-> D: better/package/BetterName.java 
-> E: final/package/FinalName.java

Now merging results in J with many (compilation) errors due to the missing refactorings on my branch. (Because i still refer to path.to.File instead of final.package.FinalName

In order to fix the broken build, i need to know the new names for the old classes.

Is there a git command to get all the renames that have been applied to a particular file?

helt
  • 4,925
  • 3
  • 35
  • 54
  • does your script work? if yes I don't get your question – CharlesB Apr 17 '12 at 16:01
  • Yes it works but i was wondering if there is a simpler solution than the script above. As i have to do it for every file. – helt Apr 18 '12 at 16:16
  • You need to tell `git log` to find renames with the `-M` option. Then you want to only report renames and that's done with the `--diff-filter` option. I've added more detail to my answer. – Adam Dymitruk Jun 26 '12 at 19:24
  • I think it is crucial to note, that a particular file has been renamed multiple times in different commits – helt Jun 27 '12 at 09:43

2 Answers2

1

Git should find the renames of the files. However, if the file has had 50% or more lines change in that file, it will no longer be considered a rename. You can change this threshold if you wish.

Here is the command to do it:

git log -M --diff-filter=R --stat

It will only show you the renames. If you want to change the threshold to something else other than the default 50%, you can just add the number to the M option:

git log -M90 --diff-filter=R --stat

will only consider a file as renamed if the contents have changed by no more than 10%

UPDATE:

To skip all the intermediate steps, use diff instead. Git will follow the renames for you:

git diff -M --diff-filter=R --name-status ..master | cut -f2-

when you are on your topic branch.

You can then pipe this to make a bunch of sed instructions to adjust your references.

Adam Dymitruk
  • 124,556
  • 26
  • 146
  • 141
  • your command is quite useful but i still have to scrape it to achieve my goal – helt Jun 27 '12 at 09:58
  • what do you want? You can format the output to your liking. You should be able to pipe it to a few more commands if you need. You shouldn't need to walk your history. – Adam Dymitruk Jun 27 '12 at 17:47
  • 1
    `git log -M --diff-filter=R --name-status --reverse` provides a list of all renames in the needed order (oldest first). But i have the old name ONLY. The problem is not to find the direct rename of the old name but find all successive renames afterwards, too. Hopefully its somewhat comprehensible now. – helt Jun 28 '12 at 10:15
  • Then let git roll up all the renames with a diff. Updated my answer. – Adam Dymitruk Jun 29 '12 at 00:51
  • 1
    `git log --diff-filter=RD --summary --reverse` works best for me – Ilya Serbis Aug 09 '20 at 18:03
-1

I couldn't find a satisfying answer on the web, so i've wrote a (awkward) script to go through the logs one by one (renaming by renaming)

Using the script with above example provides the follwing output:

$ git history path/to/File.java

final/package/FinalName.java

I added the script to my ~.bashrc and added a the option alias.history=!bash -ic 'git_file_history "$@"' - to my global git config

#! /bin/sh
### Takes a filename as parameter ($1)
### Echoes all filenames related to $1
function git_file_history() {
        loc_var=$1;
        old=""
        while [ "$loc_var" != "$old"  ] && [ "$loc_var" != "" ]
        do
                old=$loc_var;
                #echo $loc_var;
                hashes=$(git log --format="%P %H" -1 --follow --  $loc_var);
                status=$(git diff --name-status --find-renames $hashes | grep $loc_var);
                awks=`echo $status | awk '{print $3}'`
                loc_var=$awks;
        done 
        echo "$loc_var";
}
helt
  • 4,925
  • 3
  • 35
  • 54