Git diff to show only lines that have been modified
GitDiffGit Problem Overview
When I do a git diff, it shows lines that have been added:
+ this line is added
lines that have been removed:
- this line is removed
but it also shows many lines which are not modified:
this line is not modified
this line is also not modified
This results in the actual git diff looking something like this:
+ this line is added
this line is not modified
- this line is removed
this line is not modified
Can I ask git to show only lines that have been modified and ignore all other code which has not been modified? I have written a method which will remove all the lines which don't have a "+" or "-" sign in front of them, but I am sure there must be a simpler way to do this.
In my git diff, I am only interested in seeing the lines that have been modified.
Git Solutions
Solution 1 - Git
What you want is a diff with 0 lines of context. You can generate this with:
git diff --unified=0
or
git diff -U0
You can also set this as a config option for that repository:
git config diff.context 0
To have it set globally, for any repository:
git config --global diff.context 0
Solution 2 - Git
Another hack (on un*x) to show just the lines beginning with +
and -
:
git diff -U0 | grep '^[+-]' | grep -Ev '^(--- a/|\+\+\+ b/)'
The code above does the following:
git diff -U0
: choose 0 context lines- The first grep only includes all lines starting with
+
or-
- The second grep excludes lines starting with
--- a/
or+++ b/
Color
To show colored diff, try the following:
git diff -U0 --color | grep '^\e\[[^m]*m[-+]' | grep -Ev '(--- a/|\+\+\+ b/)'
- The expression,
^\e\[[^m]*m[-+]
, looks for start of line (^
), then the escape characer (\e
) followed by[
which together start the escape sequence, then any character that is not an "m" (numbers, semicolons, or nothing), followed by an "m" which ends the escape sequence. - Note that all of the following are valid escape sequences:
\e[0m
(reset),\e[m
(also reset),\e[1m
(bold on),\e[31m
(red),\e[32m
(green),\e[9;31m
(strike out + red),\e[31;9m
(red + strike out),\e[1;4;9;31m
(bold + underline + strike out + red). The default git colors use red and green, but they can be reconfigured. --color
is the same as--color=always
.- The restriction on
--- a/
or+++ b/
to appear at the start of the line has been removed to accommodate for the escape sequences and this could lead to an edge case.
Additional Notes:
- The above solution needs to be modified if you use additional git diff options such as
-R
,--src-prefix
,--dst-prefix
,--no-prefix
, etc. - The two greps can be combined into a single
grep -E -v '^(\+\+\+ b/|--- a/|@@ |diff --git|index )'
, but I find the double grep version easier to understand.
Solution 3 - Git
I think for simple cases the regex can be much shorter and easier to remember, with the caveat that this won't work if you have line changes where the line itself starts with +
or -
$ git diff | grep '^[+|-][^+|-]'
The regex says the line should start with +
or -
, and the immediately following character should be neither of those. I got the same results whether I escaped the +
or not here, btw...
Example:
$ cat testfile
A
B
C
D
E
F
G
Say I change C
to X
, E
to Y
, and G
to Z
.
$ git diff | grep '^[+|-][^+|-]'
-C
+X
-E
+Y
-G
+Z
Like I said above, though, this is just for most cases. If you pipe that output to a file dout
, then try the same regex, it won't work.
$ git diff dout | grep '^[+|-][^+|-]'
$
Anyways, hope that helps in your case
Solution 4 - Git
Following up on Chris' latest comment, the main problem with the post-processing is that you want to keep lines starting with -|+
but you also want to filter out those that start with ---|+++
. If you are storing patch files in your repo (I do, in [Pydoop][1]), on the other hand, you want to keep lines that start with --|++
, so the regexp becomes a bit involved:
git diff | grep -P '^\+(?:(?!\+\+))|^-(?:(?!--))'
The regexp uses a negative lookahead: see Peter Boughton's answer to [this question][2] for a detailed explanation.
If you do this often, you might want to set up a git alias for it:
git config --global alias.diffonly '!git diff | grep -P "^\+(?:(?!\+\+))|^-(?:(?!--))"'
[1]: https://github.com/crs4/pydoop "Pydoop git repository" [2]: https://stackoverflow.com/questions/977251/regular-expressions-and-negating-a-whole-character-group
Solution 5 - Git
This answer will retain the original red/green colors for readability. I provided a few variations in syntax:
git diff --color | grep --color=never $'^\e\[3[12]m'
git diff --color | grep --color=never $'^\033\[3[12]m'
git diff --color | grep --color=never -P '^\e\[3[12]m'
git diff --color | grep --color=never -P '^\033\[3[12]m'
Explanation:
- The
git diff --color
is needed to prevent git from disabling the color when it is piping. - The
grep --color=never
is to prevent grep removing the original color and highlighting the matched string. - We are matching for lines that start with red (
\e[31m
) or green (\e[32m
) escape codes. - The
$'...'
(ANSI-C quoting syntax) or-P
(perl syntax) is to letgrep
to interpret\e
or\033
as anESC
character.
Solution 6 - Git
> Can I ask git to show only lines that have been modified and ignore all other code which has not been modified?
And from the OP's follow-up comment under the main answer:
> Thank you for quick reply. This solves half of my problem but I am still getting some lines like @@ -1 +1 @@
in my diff and top of my git diff have diff --git a/db/xxxxxxx b/db/xxxx index xxxxx..aaaaaaa bbbbbbbb
. -r3b00t
To solve both of those requests above, here is the 1-line solution using the git-diffc.sh
awk
-language-based wrapper I wrote around git diff
:
git diffc
Done!
Here are the features of git diffc
.
All these features, when taken together, solve the shortcomings of every other answer here:
- It handles color AND no-color output. That's what this regular expression does:
^(\033\[(([0-9]{1,2};?){1,10})m)?
- It handles ALL COLORS and ALL TEXT FORMATTING OPTIONS, including bold, italics, strikethrough, etc, which you can set in your
git config
settings. That's why the regex above has;?
and{1,10}
in it: if it detects the start of a color or text formatting code, it will match up to 10 sequences of these combined ANSI codes. - It does NOT also include lines which begin with
@@
and the worddiff
, as the accepted answer does. If you DO want those lines (which quite frankly, I think are useful :) ), do this instead:
orgit diff --unified=0
git diff -U0
- It shows the output in the same exact way as
git diff
would: in theless
pager with optional color output (-R
), and only if the text is > 1 page (-F
), and while retaining the current page of text on the screen when youq
uit (-X
).
It also has the benefit of being powerful and easily configurable since it uses the awk programming language.
Sample output of git diff 8d4d4fd3b60f200cbbb87f2b352fb097792180b2~2..8d4d4fd3b60f200cbbb87f2b352fb097792180b2~3
:
> diff --git a/useful_scripts/rg_replace.sh b/useful_scripts/rg_replace.sh
> index 74bc5bb..0add69d 100755
> --- a/useful_scripts/rg_replace.sh
> +++ b/useful_scripts/rg_replace.sh
> @@ -2,12 +2,11 @@
>
> # This file is part of eRCaGuy_dotfiles: https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles
>
> -# STATUS: functional and ready-to-use
> -
> +# WORK IN PROGRESS! <===========
> # This is a simple wrapper around RipGrep (rg
) to allow in-place find-and-replace, since the
> # rg --replace
option replaces only the stdout, NOT the contents of the file.
> # man rg
under the --replace
section states: "Neither this flag nor any other ripgrep
> -# flag will modify your files." This wrapper overcomes that limitation.
> +# flag will modify your files."
>
> # INSTALLATION INSTRUCTIONS:
> # 1. Install RipGrep: https://github.com/BurntSushi/ripgrep#installation
versus the sample output of git diffc 8d4d4fd3b60f200cbbb87f2b352fb097792180b2~2..8d4d4fd3b60f200cbbb87f2b352fb097792180b2~3
. Notice that only -
and +
lines are shown, whereas surrounding context lines are gone, and all other lines such as diff
, index
, ---
, +++
, and @@
are gone too!:
> -# STATUS: functional and ready-to-use > - > +# WORK IN PROGRESS! <=========== > -# flag will modify your files." This wrapper overcomes that limitation. > +# flag will modify your files."
git diffc
stands for "git diff changes", meaning: show just the changed lines of code, nothing else. I wrote it. It is not part of regular git.
It supports ALL options and parameters supported by git diff
, since it's just a light-weight wrapper around git diff
.
Download it here: git-diffc.sh. It is part of my eRCaGuy_dotfiles repo.
To install it:
git clone https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles.git
cd eRCaGuy_dotfiles/useful_scripts
mkdir -p ~/bin
ln -si "${PWD}/git-diffc.sh" ~/bin/git-diffc
Now manually log out and log back in now if this is the first time you've ever created or used the ~/bin
dir, in order to cause Ubuntu's default ~/.profile
file to add ~/bin
to your PATH
variable. If logging out and back in doesn't work, add the following few lines of code to your ~/.profile
file and then log out of Ubuntu and log back in again:
# set PATH so it includes user's private bin if it exists
if [ -d "$HOME/bin" ] ; then
PATH="$HOME/bin:$PATH"
fi
That's it!
Usage: same as git diff
. Ex:
git diffc
git diffc -h
git diffc commit1 commit2
git diffc --no-color
# etc.
Additional installation notes:
See also the installation section of my other answer about git diffn
, which I also wrote, here. Except, everywhere you see git-diffn
in those instructions, use git-diffc
instead. That includes inside the wget
command too. Downloading and installing git diffc
is easy: it's just a few commands.
awk
to show just the +
and -
lines, accounting for any color or text formatting git diff
may be outputting:
How to use This code below is what makes up the git diffc
wrapper.
Not a single one of the other answers here (including my other answer) will do exactly what you want 100% correctly. This answer, however, will. Here is a 1-liner you can copy and paste into your terminal. I've just made it multiple lines for readability--you can copy-paste it the same either way so I might as well make it readable! It relies on the awk
programming language:
git diff --color=always "$@" | awk '
# 1. Match and then skip "--- a/" and "+++ b/" lines
/^(\033\[(([0-9]{1,2};?){1,10})m)?(--- a\/|\+\+\+ b\/)/ {
next
}
# 2. Now print the remaining "+" and "-" lines ONLY! Note: doing step 1 above first was required or
# else those lines would have been matched by this matcher below too since they also begin with
# the "+" and "-" symbols.
/^(\033\[(([0-9]{1,2};?){1,10})m)?[-+]/ {
print $0
}
' | less -RFX
If you are interested in learning awk, here are some resources:
gawk
(GNUawk
) manual: https://www.gnu.org/software/gawk/manual/html_node/index.html#SEC_Contents- Study
git diffn
and the comments therein: https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles/blob/master/useful_scripts/git-diffn.sh - If you want
git diffn
too, which isgit diff
with line numbers, see here: https://stackoverflow.com/questions/24455377/git-diff-with-line-numbers-git-log-with-line-numbers/61997003#61997003 - Some awk "hello world" and syntax test examples: https://github.com/ElectricRCAircraftGuy/eRCaGuy_hello_world/tree/master/awk
As a bonus, I also wrapped up the above to be used as git diffc
, which means "git diff to show ONLY 'c'hanges". Usage is identical to git diff
; just use git diffc
instead! It supports ALL options. Color is ON by default. To turn it off, simply use git diffc --no-color
or git diffc --color=never
. See man git diff
for details.
Since I just finished git diffn
(a tool to show git diff
with line 'n'umbers) last night, writing git diffc
was trivial. I figured I better do it now while the knowledge is fresh in my head.
Solution 7 - Git
Here's another, simpler way to find only lines which have been modified, and hence begin with a single +
or -
, while retaining color output:
git diff -U0 --color=always HEAD~ | grep --color=never -E $'^\e\[(32m\+|31m-)'
- The
-U0
says to include 0 lines of context around the changed lines--ie: include just the changed lines themselves. Seeman git diff
. - The
-E
for grep allows it to work with extended regular expressions - The
$''
syntax apparently allows ANSI quoting, which properly interprets the ESC (escape, or 0x1b) character properly. See here. - And here's the regex description from https://www.regex101.com:
- Basically,
^
matches the beginning of the line,\e
matches the Escape char, which is the start of a color code in the terminal,\[
matches the next char in the color code, which is[
, and then the(this|that)
syntax matches "this" or "that", where "this" is32m+
, which is a green + line, and31m-
is a red - line. - Colors are like this:
\e[32m
is green and\e[31m
is red. +
shows lines marked bygit diff
as added, of course, and-
shows lines marked bygit diff
as deleted.- Note that
--color=never
is required in the 2ndgrep
expression in order to prevent it from highlighting its matches, which would otherwise screw up the color codes coming in fromgit diff
to the left. - The
+
has to be escaped too as\+
because otherwise the+
is a special regular expression (regex) character which specifies one or more occurrences of the preceding element. See here: https://en.wikipedia.org/wiki/Regular_expression#Basic_concepts.
References:
- https://git-scm.com/docs/git-diff#_combined_diff_format
- Answer by @user650654: https://stackoverflow.com/questions/18810623/git-diff-to-show-only-lines-that-have-been-modified/26622262#26622262
- Answer by @wisbucky: https://stackoverflow.com/questions/18810623/git-diff-to-show-only-lines-that-have-been-modified/57579094#57579094
Related:
- [my own answer] https://stackoverflow.com/questions/24455377/git-diff-with-line-numbers-git-log-with-line-numbers/61997003#61997003
- [someone else's answer] https://stackoverflow.com/questions/24455377/git-diff-with-line-numbers-git-log-with-line-numbers/33249416#33249416
- https://stackoverflow.com/questions/61932427/git-diff-with-line-numbers-and-proper-code-alignment-indentation
git-filechange-search.sh
- a script which allows you to search a file for a variable or function name and figure out which commits contain changes with that variable or function name. Ex. usage:./git-filechange-search.sh path/to/my/file.cpp variable_name
will find all commits with changes to file.cpp that containvariable_name
in them. This is useful to see where and when certain features were changed. It's as though it were a search that could observe sections of a file displayed viagit blame
over time.