findchris
findchris

Reputation: 1854

Git: Show all of the various changes to a single line in a specified file over the entire git history

I've looked around, and am not sure if this is possible, but here goes:

I have a (javascript) file (say /lib/client.js) in which I have a unique identifier assigned to a variable, like so: var identifier = "SOME_IDENTIFIER";

You can think of the identifier like a version number: Periodically, we'll change this variable to a new identifier.

What I'd like to do is find all of the unique identifiers we've ever used. How can I do this with git?

I imagine there might be a way to search through the git history, and print the line matching "var identifier =". I could de-dupe this list manually.

Anyway, I'd appreciate any insight here. Thanks.

Upvotes: 163

Views: 68104

Answers (6)

starfry
starfry

Reputation: 9943

I wanted to list all the pet names that the Linux kernel has been given. This is set in the Makefile in the kernel Git repo (although this isn't the full history). I didn't want to rely on it being a specific line number and I wanted the line's actual content rather than the commit refs, and I wanted to remove duplicates without affecting the order. I came up with this:

git log --format=format:%H Makefile | xargs -I{} git show {}:Makefile | awk -F '=' '/^NAME = /{print $NF}' | awk '!x[$0]++'

It first uses git log to get the commits affecting that file then passes the file content, obtained with git show, at that ref through awk which extracts the line of interest and a 2nd awk de-duplicates the list.

Just in case it's useful to anyone.

Upvotes: 0

Andrew
Andrew

Reputation: 3442

If you adapt @rob's answer just a bit, git log will basically do this for you, if all you need is a visual comparison:

git log -U0 -S "var identifier =" path/to/file

-U0 means output in patch mode (-p), and show zero lines of context around the patch.

You can even do this across branches:

git log -U0 -S "var identifier =" branchname1 branchname2 -- path/to/file

There may be a way to suppress the diff header, but I don't know of one.

Upvotes: 10

Alexander Bird
Alexander Bird

Reputation: 40639

Since Git 1.8.4, there is a more direct way to answer your question.

Assuming that line 110 is the line saying var identifier = "SOME_IDENTIFIER";, then do this:

git log -L110,110:/lib/client.js

This will return every commit which touched that line of code.

See git-log's documentation for the -L command line parameter.

Upvotes: 175

slumos
slumos

Reputation: 61

In magit, you can do this with

l, =L

It will then ask you for file and start,end lines.

Upvotes: 5

rob
rob

Reputation: 2123

See the man page for git-log and gitdiffcore. I believe this command would do it, but it might not be quite right:

git log -G "var identifier =" file.js

EDIT: Here's a rough start for a bash script to show the actual lines. This might be more what you're looking for.

for c in $(git log -G "something" --format=%H -- file.js); do
    git --no-pager grep -e "something" $c -- file.js
done

It uses git log -G to find the interesting commits, using --format=%H to produce a list of commit hashes. It then iterates over each interesting commit, asking git grep to show the lines from that commit and file that contain the regex, prefaced with the commit hash.


EDIT: Changed to use -G instead of -S as suggested in comments.

Upvotes: 62

Ethan Brown
Ethan Brown

Reputation: 27282

You can also do this with gitk:

gitk file.js

In the "commit" drop down, choose "adding/removing string:" and in the text box next to it, enter "var identifier =", and any commits that add or remove lines that contain that string will be highlighted.

Upvotes: 14

Related Questions