95

Assuming I have a text file

alex bob matrix will be removed git repo 

and I have updated it to be

alex new line here another new line bob matrix git 

Here, I have added lines number (2,3) and updated line number (6)

How can I get these line numbers info using git diff or any other git command?

0

13 Answers 13

100

git diff --stat will show you the output you get when committing stuff which is the one you are referring to I guess.

git diff --stat 

For showing exactly the line numbers that has been changed you can use

git blame -p <file> | grep "Not Committed Yet" 

And the line changed will be the last number before the ending parenthesis in the result. Not a clean solution though :(

Sign up to request clarification or add additional context in comments.

6 Comments

stat only display how many lines are inserted/deleted/updated. But I need to know which line numbers
This seemed to be a harder problem than it should be, but I managed to get it by using git blame and grep. See my updated answer
One should usually call 'git blame -p' if the output is to be processed by other programs such as 'awk' or 'grep'.
git blame won't catch removed lines
Why is this marked as correct when it doesn't do what OP asked for?
|
33

Here's a bash function to calculate the resulting line numbers from a diff:

diff-lines() { local esc=$'\e' local ansi=$'\e\\[[0-9;]*m' local REPLY= local path= local line= # For each line in stdin... while IFS= read -r || [[ -n $REPLY ]]; do # ...match the start of the line, ignoring leading ANSI escapes. if [[ $REPLY =~ ^($ansi)*---\ (a/)?.* ]]; then continue elif [[ $REPLY =~ ^($ansi)*\+\+\+\ (b/)?([^[:blank:]$esc]+).* ]]; then path=${BASH_REMATCH[3]} elif [[ $REPLY =~ ^($ansi)*@@\ -[0-9]+(,[0-9]+)?\ \+([0-9]+)(,[0-9]+)?\ @@.* ]]; then line=${BASH_REMATCH[3]} elif [[ $REPLY =~ ^($ansi)*([\ +-\\]) ]]; then echo "$path:$line:$REPLY" if [[ ${BASH_REMATCH[2]} =~ [\ +] ]]; then ((line++)) fi fi done } 

It can produce output such as:

$ git diff | diff-lines http-fetch.c:1: #include "cache.h" http-fetch.c:2: #include "walker.h" http-fetch.c:3: http-fetch.c:4:-int cmd_http_fetch(int argc, const char **argv, const char *prefix) http-fetch.c:4:+int main(int argc, const char **argv) http-fetch.c:5: { http-fetch.c:6:+ const char *prefix; http-fetch.c:7: struct walker *walker; http-fetch.c:8: int commits_on_stdin = 0; http-fetch.c:9: int commits; http-fetch.c:19: int get_verbosely = 0; http-fetch.c:20: int get_recover = 0; http-fetch.c:21: http-fetch.c:22:+ prefix = setup_git_directory(); http-fetch.c:23:+ http-fetch.c:24: git_config(git_default_config, NULL); http-fetch.c:25: http-fetch.c:26: while (arg < argc && argv[arg][0] == '-') { fetch.h:1: #include "config.h" fetch.h:2: #include "http.h" fetch.h:3: fetch.h:4:-int cmd_http_fetch(int argc, const char **argv, const char *prefix); fetch.h:4:+int main(int argc, const char **argv); fetch.h:5: fetch.h:6: void start_fetch(const char* uri); fetch.h:7:-bool fetch_succeeded(int status_code); fetch.h:7:\ No newline at end of file fetch.h:7:+bool fetch_succeeded(int status_code); 

from a diff like this:

$ git diff diff --git a/builtin-http-fetch.c b/http-fetch.c similarity index 95% rename from builtin-http-fetch.c rename to http-fetch.c index f3e63d7..e8f44ba 100644 --- a/builtin-http-fetch.c +++ b/http-fetch.c @@ -1,8 +1,9 @@ #include "cache.h" #include "walker.h" -int cmd_http_fetch(int argc, const char **argv, const char *prefix) +int main(int argc, const char **argv) { + const char *prefix; struct walker *walker; int commits_on_stdin = 0; int commits; @@ -18,6 +19,8 @@ int cmd_http_fetch(int argc, const char **argv, const char *prefix) int get_verbosely = 0; int get_recover = 0; + prefix = setup_git_directory(); + git_config(git_default_config, NULL); while (arg < argc && argv[arg][0] == '-') { diff --git a/fetch.h b/fetch.h index 5fd3e65..d43e0ca 100644 --- a/fetch.h +++ b/fetch.h @@ -1,7 +1,7 @@ #include "config.h" #include "http.h" -int cmd_http_fetch(int argc, const char **argv, const char *prefix); +int main(int argc, const char **argv); void start_fetch(const char* uri); -bool fetch_succeeded(int status_code); \ No newline at end of file +bool fetch_succeeded(int status_code); 

If you only want to show added/removed/modified lines, and not the surrounding context, you can pass -U0 to git diff:

$ git diff -U0 | diff-lines http-fetch.c:4:-int cmd_http_fetch(int argc, const char **argv, const char *prefix) http-fetch.c:4:+int main(int argc, const char **argv) http-fetch.c:6:+ const char *prefix; http-fetch.c:22:+ prefix = setup_git_directory(); http-fetch.c:23:+ fetch.h:4:-int cmd_http_fetch(int argc, const char **argv, const char *prefix); fetch.h:4:+int main(int argc, const char **argv); fetch.h:7:-bool fetch_succeeded(int status_code); fetch.h:7:\ No newline at end of file fetch.h:7:+bool fetch_succeeded(int status_code); 

It's robust against ANSI color codes, so you can pass --color=always to git diff to get the usual color coding for added/removed lines.

The output can be easily grepped:

$ git diff -U0 | diff-lines | grep 'main' http-fetch.c:4:+int main(int argc, const char **argv) fetch.h:4:+int main(int argc, const char **argv); 

In your case git diff -U0 would give:

$ git diff -U0 | diff-lines test.txt:2:+new line here test.txt:3:+another new line test.txt:6:-will be removed test.txt:6:-git repo test.txt:6:+git 

If you just want the line numbers, change the echo "$path:$line:$REPLY" to just echo "$line" and pipe the output through uniq.

10 Comments

How could I pass-through bash color escape codes? This is great, but the color codes coming from git diff --color do not come through. Or do you think it would be better just to add the color escapes into the return from this function?
I updated the function so the various regexes are robust to ANSI color codes. git diff --color | diff-lines now works as expected :)
This solution works awesome! it should be marked as the answer as it really does what the OP asked. If it worked for you please up vote it so we can make it the popular answer :)
I keep getting this error using zsh: zsh: parse error near `]+m' Any ideas? The error comes from this line: elif [[ $REPLY =~ ^($esc\[[0-9;]+m)*([\ +-]) ]]; then
@HoshSadiq Simply quoting the regular expression seems to have worked.
|
23

I use the --unified=0 option of git diff.

For example, git diff --unified=0 commit1 commit2 outputs the diff:

*enter image description here*

Because of the --unified=0 option, the diff output shows 0 context lines; in other words, it shows exactly the changed lines.

Now, you can identify the lines that start with '@@', and parse it based on the pattern:

@@ -startline1,count1 +startline2,count2 @@

Back to the above example, for the file WildcardBinding.java, start from line 910, 0 lines are deleted. Start from line 911, 4 lines are added.

2 Comments

what if @@ -910,10,+911,15@@ or something, then how do we say exactly how many number of lines being added, deleted or modified
Do you have a good way to output the line numbers in a list like OP asked for?
7

I had this same problem so I wrote a gawk script that changes the output of git diff to prepend the line number for each line. I find it useful sometimes when I need to diff working tree, although it's not limited to that. Maybe it is useful to someone here?

$ git diff HEAD~1 |showlinenum.awk diff --git a/doc.txt b/doc.txt index fae6176..6ca8c26 100644 --- a/doc.txt +++ b/doc.txt @@ -1,3 +1,3 @@ 1: red 2: blue :-green 3:+yellow 

You can download it from here:
https://github.com/jay/showlinenum

2 Comments

Looks very handy. Keep in mind this code has the advantage (or disadvantage) of being GPL licensed.
I wrote git diffn to do this too, and it fully retains terminal colors and shows the line numbers of both the old file on the left and the new file on the right.
4

You can use git diff coupled with shortstat parameter to just show the no of lines changed.

For the no of lines changed (in a file that's already in the repo) since your last commit

git diff HEAD --shortstat 

It'll output something similar to

1 file changed, 4 insertions(+) 

1 Comment

The question asks for the line numbers for each line that has been changed, not a total of how many lines have been changed.
4

Line numbers of all uncommitted lines (added/modified):

git blame <file> | grep -n '^0\{8\} ' | cut -f1 -d: 

Example output:

1 2 8 12 13 14 

1 Comment

what about the contents of the lines that were changed as well?
2

Configure an external diff tool which will show you the line numbers. For example, this is what I have in my git global config:

diff.guitool=kdiff3 difftool.kdiff3.path=c:/Program Files (x86)/KDiff3/kdiff3.exe difftool.kdiff3.cmd="c:/Program Files (x86)/KDiff3/kdiff3.exe" "$LOCAL" "$REMOTE" 

See this answer for more details: https://stackoverflow.com/q/949242/526535

1 Comment

isn't there any other way to get these info without using diff tool. Only using git commands?
2

Here's a bash function I cobbled together:

echo ${f}: for n in $(git --no-pager blame --line-porcelain $1 | awk '/author Not Committed Yet/{if (a && a !~ /author Not Committed Yet/) print a} {a=$0}' | awk '{print $3}') ; do if (( prev_line > -1 )) ; then if (( "$n" > (prev_line + 1) )) ; then if (( (prev_line - range_start) > 1 )) ; then echo -n "$range_start-$prev_line," else echo -n "$range_start,$prev_line," fi range_start=$n fi else range_start=$n fi prev_line=$n done if (( "$range_start" != "$prev_line" )) ; then echo "$range_start-$prev_line" else echo "$range_start" fi 

And it ends up looking like this:

views.py: 403,404,533-538,546-548,550-552,554-559,565-567,580-582 

Comments

1

This is probably a fairly accurate count of changed lines:

git diff --word-diff <commit> |egrep '(?:\[-)|(?:\{\+)' |wc -l 

Also, here is a solution for line numbers in your diff: https://github.com/jay/showlinenum

Comments

1

I was looking for a way to output only the lines changed for each file using git diff. My idea was to feed this output to a linter for type checking. This is what helped me

Comments

0

Not exactly what you were asking for, but git blame TEXTFILE may help.

Comments

0

Here's some Python copypasta to get the line numbers for modified / removed lines, in case you came across this question looking for that.

It should be fairly easy to modify it into something that gets the modified and added line numbers as well.

I've only tested on Windows, but it should be cross platform as well.

import re import subprocess def main(file1: str, file2: str): diff = get_git_diff(file1, file2) print(edited_lines(diff)) def edited_lines(git_diff: str): ans = [] diff_lines = git_diff.split("\n") found_first = False # adjust for added lines adjust = 0 # how many lines since the start count = 0 for line in diff_lines: if found_first: count += 1 if line.startswith('-'): # minus one because count is 1 when we're looking at the start line ans.append(start + count - adjust - 1) continue if line.startswith('+'): adjust += 1 continue # get the start line match = re.fullmatch(r'@@ \-(\d+),\d+ \+\d+,\d+ @@', line) if match: start = int(match.group(1)) count = 0 adjust = 0 found_first = True return ans def get_git_diff(file1: str, file2: str): try: diff_process: subprocess.CompletedProcess = subprocess.run(['git', 'diff', '--no-index', '-u', file1, file2], shell=True, check=True, stdout=subprocess.PIPE) ans = diff_process.stdout # git may exit with 1 even though it worked except subprocess.CalledProcessError as e: if e.stdout and e.stderr is None: ans = e.stdout else: raise # remove carriage at the end of lines from Windows ans = ans.decode() ans.replace('\r', '') return ans if __name__ == "__main__": main("file1.txt", "file2.txt") 

Comments

0

Perhaps this, credit goes to Jakub Bochenski - Git diff with line numbers (Git log with line numbers)

git diff --unified=0 | grep -Po '^\+\+\+ ./\K.*|^@@ -[0-9]+(,[0-9]+)? \+\K[0-9]+(,[0-9]+)?(?= @@)'

2 Comments

grep: invalid option -- P
@TayJen as usually it might depend on a specific grep version as its like with many different cli tools, specific flag might be in that or another version, for me it works.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.