24

Is it possible in Git to list all the previous versions of a given line in a file by the line number?

The reason why I would find it useful is to be able to easier troubleshoot a problem based on logged stack trace report.

i.e. I have a undefined method exception logged at a line 100 of a given file. The file is included in plenty of commits which caused the given line might have 'traveled' up and down the file even without any changes made to it.

How would I print out the content of line 100 of a given file across last x commits?

mrt
  • 1,669
  • 3
  • 22
  • 32
  • See also [git history of a source line](http://stackoverflow.com/q/10854370) – CharlesB Jul 22 '13 at 12:43
  • @MRT: In my answer, I have described how to look beyond the last change of a line. – David Pärsson Jul 23 '13 at 07:00
  • 2
    I have posted a new [answer](http://stackoverflow.com/a/17803860/946850) to the duplicate question. Will keep the same answer here as a copy just in case this question doesn't get closed. – krlmlr Jul 23 '13 at 07:26

7 Answers7

11

This will call git blame for every meaningful revision to show line $LINE of file $FILE:

git log --format=format:%H $FILE | xargs -L 1 git blame $FILE -L $LINE,$LINE

As usual, the blame shows the revision number in the beginning of each line. You can append

| sort | uniq -c

to get aggregated results, something like a list of commits that changed this line. (Not quite, if code only has been moved around, this might show the same commit ID twice for different contents of the line. For a more detailed analysis you'd have to do a lagged comparison of the git blame results for adjacent commits. Anyone?)

krlmlr
  • 25,056
  • 14
  • 120
  • 217
  • Thanks, it seems like this is doing the job. Just had to use capital `L` in the xargs options: `git log --format=format:%H $FILE | xargs -L1 git blame $FILE -L $LINE,$LINE` – mrt Jul 23 '13 at 08:01
  • @MRT: Right, the `-l` switch to `xargs` is deprecated according to POSIX. Worked on my system, but `-L` is better. – krlmlr Jul 23 '13 at 08:09
  • The `-L` argument to `git blame` may be used to find code rather than line numbers. This can for example be useful for finding a function that has been moved around (but not when it's been renamed). – David Pärsson Jul 23 '13 at 08:37
7

It's not exactly what you're asking for, but with git blame <file> you can see the commit that last modified every line.

The first columns shows the commit ID:

$ git blame my-file.txt
65126918 (David Pärsson 2013-07-22 12:53:02 +0200 1) Heading
c6e6d36d (David Pärsson 2013-07-22 12:53:10 +0200 2) =======
65126918 (David Pärsson 2013-07-22 12:53:02 +0200 3) 
13e293e3 (David Pärsson 2013-07-22 12:49:33 +0200 4) Text on first line
8b3d2e15 (David Pärsson 2013-07-22 12:49:49 +0200 5) Text on second line
13e293e3 (David Pärsson 2013-07-22 12:49:33 +0200 6) Text on third line

You can look beyond the last modification by supplying a revision, e.g.

$ git blame 8b3d2e15 my-file.txt

You can also select specific lines with the -L argument, like this:

$ git blame my-file.txt -L 4,+3
13e293e3 (David Pärsson 2013-07-22 12:49:33 +0200 4) Text on first line
8b3d2e15 (David Pärsson 2013-07-22 12:49:49 +0200 5) Text on second line
13e293e3 (David Pärsson 2013-07-22 12:49:33 +0200 6) Text on third line

More details and clever tricks can be found on the git-blame man page.

David Pärsson
  • 6,038
  • 2
  • 37
  • 52
  • 1
    You can specify the revision as well i.e. it's no only about the *last* commit only that modified every line. – the.malkolm Jul 22 '13 at 12:16
  • 3
    Don't forget the `-L100,+10` option to blame 10 lines from line 100, or `-L/void foo/,+10` to blame 10 lines from where function foo is defined. – sehe Jul 22 '13 at 12:45
3

Check out this short tutorial as well: http://zsoltfabok.com/blog/2012/02/git-blame-line-history/
Basically, it guides you through a combination of git blame and git show to see which commits change a specific line (git blame, as already suggested especially in David Parsson's answer) and how that line change (git show <commit-id>). So, iterating on commits alternating git blame <commit-id> git show <commit-id> will give you the full history of a specific line of a file.
Also, don't forget git blame -M in case you doubt the line has been copied from another file. From https://www.kernel.org/pub/software/scm/git/docs/git-blame.html

-M |num|
Detect moved or copied lines within a file. When a commit moves or copies a block of lines (e.g. the original file has A and then B, and the commit changes it to B and then A), the traditional blame algorithm notices only half of the movement and typically blames the lines that were moved up (i.e. B) to the parent and assigns blame to the lines that were moved down (i.e. A) to the child commit. With this option, both groups of lines are blamed on the parent by running extra passes of inspection.

num is optional but it is the lower bound on the number of alphanumeric characters that git must detect as moving/copying within a file for it to associate those lines with the parent commit. The default value is 20.

ThanksForAllTheFish
  • 7,101
  • 5
  • 35
  • 54
2

git blame <file> -L <line>,<line>

mathieug
  • 901
  • 1
  • 11
  • 24
2

There's actually a fancy GUI shipped with Git which makes it easy to travel back and forth in time to see how a particular line changed. Try

git gui blame <file>

You can click on the revision next to a link to "travel back in time".

Frerich Raabe
  • 90,689
  • 19
  • 115
  • 207
1

I think you're after git blame, it tells you the commit that added each line.

So run git blame the-file-that-has-that-line.txt and go to line 100, it'll tell you what commit added it (and when it was committed, and by whom).

Leigh
  • 12,038
  • 4
  • 28
  • 36
-2

use git blame to achieve

see if it works

NAME
       git-blame - Show what revision and author last modified each line of a file

SYNOPSIS
       git blame [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-e] [-p] [-w] [--incremental] [-L n,m]
                   [-S <revs-file>] [-M] [-C] [-C] [-C] [--since=<date>] [--abbrev=<n>]
                   [<rev> | --contents <file> | --reverse <rev>] [--] <file>

DESCRIPTION
       Annotates each line in the given file with information from the revision which last modified the line. Optionally, start annotating from the given revision.
TheOneTeam
  • 25,806
  • 45
  • 116
  • 158