Identify the current file / line that corresponds to a position from a previous git commit

Viewed 79

Suppose I have a branch in a git repository. The history of the branch might look something like this:

A <- [...] <- Z

Now let's say I have a position P (e.g. src/index.js:122) that refers to a line from the A commit.

Between commits A and Z, commits might refactor the file referenced by the hunk, modify the line slightly (e.g. adding a new argument to a function signature), or even move the file itself to somewhere else in the repository tree. I would like to determine the position P' that reflects where that line is in commit Z.

Is there a git command that will help me determine this new position?

3 Answers

I would start with git log -S. Give it the line as the argument to -S. If the line hasn’t changed, this should find it.

From the "git log" man page:

   -S<string>
       Look for differences that change the number of occurrences of the
       specified string (i.e. addition/deletion) in a file. Intended for
       the scripter’s use.

       It is useful when you’re looking for an exact block of code (like a
       struct), and want to know the history of that block since it first
       came into being: use the feature iteratively to feed the
       interesting block in the preimage back into -S, and keep going
       until you get the very first version of the block.

       Binary files are searched as well.

The simplest answer I would suggest is :

Inspect the output of

git diff A Z -- src/index.js

You can inspect it by hand, or process the output with a script, to spot how line 122 was shifted between A and Z with this view of the differences.


If the evolution of said line is too intricate, you may want to inspect :

git log A..Z -p -- src/index.js
# or :
git log A..Z --reverse -p -- src/index.js

You will see all the individual evolutions of said file, and, likewise, you can compute how the initial line 122 evolves till Z.

I solved my problem using git blame and taking a reverse approach, as suggested by torek. This works for my use case as I am attempting to show metadata alongside a file in a git repository, so I know the set of lines I'd like to find original positions for. Assuming that A is an ancestor commit of Z, we can do the following:

git blame -fns Z -- api/v3/views.py

This will give commit hashes, original tree paths, and line numbers for each line in the file, e.g.

481daae85d6 api/v3/files/views.py   2   4) from rest_framework.response import Response
^           ^                       ^   ^
$1          $2                      $3  $4

Let's denote a position as <hash>:<path>@<line>. This shows us that Z:api/v3/views.py@4 originally showed up as 481daae85d6:api/v3/files/views.py:2.

Last step: the position P we've identified in A might not be the original commit that included that line, which will lead to false negatives. When importing these meta annotations, we can do another git blame for that specific line in that commit to determine the original commit:

git blame -fns -L <line>,<line> A -- <path>

Then we can store the annotation on the returned commit hash, rather than A.

The open question here is whether this will be enough: code annotations will likely still be relevant even if a line was modified slightly, while this approach will cause these annotations to be "lost".

Related