How can I view prior commits with git blame?
Asked Answered
C

14

500

Is it possible to see who edited a specific line before the commit reported by git blame, like a history of commits for a given line?

For example, I run the following (on the superb uncrustify project):

$ git blame -L10,+1 src/options.cpp
^fe25b6d (Ben Gardner 2009-10-17 13:13:55 -0500 10) #include "prototypes.h"

How can I find out who edited that line before commit fe25b6d? And who edited it before that commit?

Crackleware answered 23/2, 2011 at 22:50 Comment(4)
if the reason you are looking for previous commits are whitespace changes use the -w option. There is also -M for moved/copied codeOutrider
To look for all commits which involve a given word, see my script belowOdaniel
Here is a useful script to add this functionality on github greasyfork.org/en/scripts/…Alaska
Not sure what github looked like when @AaronHoffman posted, but it's mucho easy to blame -- and get blames for previous versions -- in github now.Beavers
O
487
git blame -L 10,+1 fe25b6d^ -- src/options.cpp

You can specify a revision for git blame to look back starting from (instead of the default of HEAD); fe25b6d^ is the parent of fe25b6d.


Edit: New to Git 2.23, we have the --ignore-rev option added to git blame:

git blame --ignore-rev fe25b6d

While this doesn't answer OP's question of giving the stack of commits (you'll use git log for that, as per the other answer), it is a better way of this solution, as you won't potentially misblame the other lines.

Overcheck answered 23/2, 2011 at 22:55 Comment(15)
Can you get a complete history, without having to re-enter the command several times with different hashes?Thee
Superb! I knew it should have been simple. I had tried ^fe25b6d but not fe25b6d^, silly me. I second the question posed by @mrz; this is nice functionality, but a full history with one command would be wonderful.Crackleware
I don't believe Git has a built-in way of getting every blame that touched a line number (which kind of makes sense, since a given line may not have a consistent line number throughout the history of a file due to insertions and deletions of lines).Overcheck
@Amber: Pretty sure you're right that the feature doesn't exist, but it does sort of seem like it could be implemented naively, by simply doing what a human would do: blame it once, grab the reported information, blame that, and so on.Brinkmanship
Sure; you could certainly do it naively with a fairly trivial shell script.Overcheck
git gui makes it quite easy to check the history of a line as the versions are clickable.Mckenna
What is -- doing here? Where can I know more about it in the documentation?Rondon
@Rondon -- is commonly used as a separator in command line arguments - in the case of Git, it's usually used to separate things like commit hashes from a list of filenames.Overcheck
@Mckenna which git GUI are you referring to?Rhodonite
Amber's answer is correct but I found it unclear; The syntax is: git blame {sha1} -- {path/to/file} Note: the -- is used to separate the tree-ish sha1 from the relative file paths. 1 For example: git blame master -- index.php. Full credit to Amber for knowing all the things! :)Tarp
@AndersZommarin please see Thomas's answer for what you're looking forTrestlework
@Overcheck sorry, I need to get this kind information from the github repository. what I meant to ask is that how can we obtain the same information from the github APIBridge
in Emacs it works like a charm - emacs.stackexchange.com/questions/31010/…Animalist
if you just want the previous commit, git blame HEAD^ or the commit before that git blame HEAD^^ etc.Pandemic
adding multiple ignore-rev <commit-id> --ignore-rev <commit-id> allows one to ignore multiple layers of commits even (which can be configured in config file; if those commits always need to be ignored)Coset
B
270

You can use git log -L to view the evolution of a range of lines.

For example :

git log -L 15,23:filename.txt

means "trace the evolution of lines 15 to 23 in the file named filename.txt".

Bumkin answered 4/9, 2015 at 11:12 Comment(5)
This is a solid answer and addresses Anders Zommarin's question above on how to see the changes to specific lines over time.Pyrotechnics
FYI: git log -L <start>,<end>:<file> requires Git 1.8.4+ see: git-scm.com/docs/git-log#git-log--Lltstartgtltendgtltfilegt for syntax optionsThamora
This didn't work for me with a line that had been moved from another file.Outset
I highly recommend adding -p to show all the git diff-like changes as well, like this, for instance, for the whole file: git log -p filename.txt.Chessy
Finally, I found the command I am looking for. Thank you so much! This solved my problem.Inchon
T
42

Amber's answer is correct but I found it unclear; The syntax is:

git blame {commit_id} -- {path/to/file}

Note: the -- is used to separate the tree-ish sha1 from the relative file paths. 1

For example:

git blame master -- index.html

Full credit to Amber for knowing all the things! :)

Tarp answered 19/5, 2014 at 23:31 Comment(2)
I agree with your sentiment. The comment system is however too restricted to present all the information clearly. I have added the content of this answer in a comment; though, I insist on leaving this answer is place for ease of access.Tarp
It should either be a separate post or an edit. I like this it as a separate answer.Mistook
W
35

You might want to check out:

git gui blame <filename>

Gives you a nice graphical display of changes like "git blame" but with clickable links per line, to move into earlier commits. Hover over the links to get a popup with commit details. Not my credits... found it here:

http://zsoltfabok.com/blog/2012/02/git-blame-line-history/

git gui is a graphical Tcl/Tc interface to git. Without any other params it starts a pretty simple but useful graphical app for committing files, hunks or even single lines and other similar commands like amend, revert, push... It's part of the git stock suite. On windows it is included in the installer. On debian - I don't know about other *nix systems - it has to be installed separately:

apt-get install git-gui

From the docs:

https://git-scm.com/docs/git-gui

DESCRIPTION

A Tcl/Tk based graphical user interface to Git. git gui focuses on allowing users to make changes to their repository by making new commits, amending existing ones, creating branches, performing local merges, and fetching/pushing to remote repositories.

Unlike gitk, git gui focuses on commit generation and single file annotation and does not show project history. It does however supply menu actions to start a gitk session from within git gui.

git gui is known to work on all popular UNIX systems, Mac OS X, and Windows (under both Cygwin and MSYS). To the extent possible OS specific user interface guidelines are followed, making git gui a fairly native interface for users.

COMMANDS

blame

Start a blame viewer on the specified file on the given version (or working directory if not specified).

browser

Start a tree browser showing all files in the specified commit. Files selected through the browser are opened in the blame viewer.

citool

Start git gui and arrange to make exactly one commit before exiting and returning to the shell. The interface is limited to only commit actions, slightly reducing the application’s startup time and simplifying the menubar.

version

Display the currently running version of git gui.

Winson answered 20/9, 2016 at 10:59 Comment(4)
It doesn't work for me. I can click the change on the given line, but that just changes the view to be for that commit, and the current line now shows as this: but how do I see the previous version of the line and when it was added?Pentathlon
This is the one use case I know of where git gui is the best solutionFederico
@Pentathlon After clicking on the line so it says this, right-click on that line, and choose "Blame Parent Commit" from the menu that pops up.Fed
git gui blame can be a little confusing at first. The second column commit hash is the default git blame. The first column commit hash is git blame -M. See stackoverflow.com/questions/33899195/…Haslam
J
26

A very unique solution for this problem is using git log, as explained by Andre here:

git log -p -M --follow --stat -- path/to/your/file
Jaques answered 29/5, 2016 at 7:21 Comment(3)
I created an alias to use this: git config --global alias.changes 'log -p -M --follow --stat --' and then I can simply type git changes path/to/your/fileNomen
This is by far the best answer and exactly what I was looking for. Simple and elegant.Sublieutenant
+1 the link to Andre's blog is dead so use the wayback machine link here.Semicentennial
C
19

Building on the previous answer, this bash one-liner should give you what you're looking for. It displays the git blame history for a particular line of a particular file, through the last 5 revisions:

LINE=10 FILE=src/options.cpp REVS=5; for commit in $(git rev-list -n $REVS HEAD $FILE); do git blame -n -L$LINE,+1 $commit -- $FILE; done

In the output of this command, you might see the content of the line change, or the line number displayed might even change, for a particular commit.

This often indicates that the line was added for the first time, after that particular commit. It could also indicate the line was moved from another part of the file.

Cumulation answered 23/2, 2011 at 22:51 Comment(3)
Note that this is the last $REVS revisions in which $FILE changed, rather than the last $REVS revisions in which $LINE changed.Moan
Which answer are you referring to?Mistook
I don't remember anymore. Possibly I could have future-proofed my answer better.Cumulation
F
13

If you are using JetBrains Idea IDE (and derivatives) you can select several lines, right click for the context menu, then Git -> Show history for selection. You will see list of commits which were affecting the selected lines:

enter image description here

Freudberg answered 18/12, 2017 at 7:17 Comment(1)
This worked better than the other answers for me (using IntelliJ). Took a while to load all the revisions but worth the wait.Apostle
A
12

There's also recursive-blame. It can be installed with

npm install -g recursive-blame
Atheroma answered 4/9, 2015 at 10:46 Comment(0)
T
7

As of Git 2.23 you can use git blame --ignore-rev

For the example given in the question this would be:

git blame -L10,+1 src/options.cpp --ignore-rev fe25b6d

(however it's a trick question because fe25b6d is the file's first revision!)

Thirtythree answered 6/9, 2019 at 22:56 Comment(0)
O
1

Build on stangls's answer, I put this script in my PATH (even on Windows) as git-bh:

That allows me to look for all commits where a word was involved:

git bh path/to/myfile myWord

Script:

#!/bin/bash
f=$1
shift
csha=""
{ git log --pretty=format:%H -- "$f"; echo; } | {
  while read hash; do
    res=$(git blame -L"/$1/",+1 $hash -- "$f" 2>/dev/null | sed 's/^/  /')
    sha=${res%% (*}
    if [[ "${res}" != "" && "${csha}" != "${sha}" ]]; then
      echo "--- ${hash}"
      echo "${res}"
      csha="${sha}"
    fi
  done
}
Odaniel answered 11/7, 2014 at 12:52 Comment(0)
C
1

Building on Will Shepard's answer, his output will include duplicate lines for commits where there was no change, so you can filter those as as follows (using this answer)

LINE=1 FILE=a; for commit in $(git rev-list HEAD $FILE); do git blame -n -L$LINE,+1 $commit -- $FILE; done | sed '$!N; /^\(.*\)\n\1$/!P; D'

Note that I removed the REVS argument and this goes back to the root commit. This is due to Max Nanasy's observation above.

Casque answered 20/9, 2015 at 4:8 Comment(0)
B
1

Building on DavidN's answer and I want to follow renamed file:

LINE=8 FILE=Info.plist; for commit in $(git log --format='%h%%' --name-only --follow -- $FILE | xargs echo | perl -pe 's/\%\s/,/g'); do hash=$(echo $commit | cut -f1 -d ','); fileMayRenamed=$(echo $commit | cut -f2 -d ','); git blame -n -L$LINE,+1 $hash -- $fileMayRenamed; done | sed '$!N; /^\(.*\)\n\1$/!P; D'

ref: nicely display file rename history in git log

Bide answered 29/5, 2016 at 21:40 Comment(0)
F
0

I use this little bash script to look at a blame history.

First parameter: file to look at

Subsequent parameters: Passed to git blame

#!/bin/bash
f=$1
shift
{ git log --pretty=format:%H -- "$f"; echo; } | {
  while read hash; do
    echo "--- $hash"
    git blame $@ $hash -- "$f" | sed 's/^/  /'
  done
}

You may supply blame-parameters like -L 70,+10 but it is better to use the regex-search of git blame because line-numbers typically "change" over time.

Fernandefernandel answered 16/2, 2014 at 16:28 Comment(0)
F
0

I wrote ublame python tool that returns a naive history of a file commits that impacted a given search term, you'll find more information on the þroject page.

Freewheel answered 11/11, 2020 at 10:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.