Skip to content

Instantly share code, notes, and snippets.

@mdawaffe
Last active October 4, 2023 08:38
Show Gist options
  • Star 19 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save mdawaffe/529e6b3ee820e777c2cfd2f8255d187f to your computer and use it in GitHub Desktop.
Save mdawaffe/529e6b3ee820e777c2cfd2f8255d187f to your computer and use it in GitHub Desktop.
Get line numbers of changed lines - git, diff - Full of bashisms
#!/bin/bash
# Call like you would `diff`
# `./diff-changed-lines.sh old new`
# Outputs the lines numbers of the new file
# that are not present in the old file.
# That is, outputs line numbers for new lines and changed lines
# and does not output line numbers deleted or unchanged lines.
args=(
# Don't output info for old (deleted) or unchanged (context) lines
--old-group-format="" --unchanged-group-format=""
# For new and changed lines, output one LINE_RANGE per line
--new-group-format="%dF-%dL%c'\012'"
--changed-group-format="%dF-%dL%c'\012'"
)
diff "${args[@]}" "$@" | while IFS=- read -r LINE END; do
for (( ; LINE <= END; LINE++ )); do
echo "$LINE"
done
done
#!/bin/bash
# Call like you would for `git diff`
# `./git-diff-changed-lines`
# `./git-diff-changed-lines master...HEAD`
# `./git-diff-changed-lines branch1 branch2`
# etc.
# Outputs the lines numbers of the new file
# that are not present in the old file.
# That is, outputs line numbers for new lines and changed lines
# and does not output line numbers deleted or unchanged lines.
# FORMAT: One file per line
# FILE:LINE_NUMBERS
# Where LINE_NUMBERS is a comma separated list of line numbers
# https://stackoverflow.com/a/246128
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
SOURCE="$(readlink "$SOURCE")"
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
git difftool --no-prompt --extcmd "$DIR/git-difftool-changed-lines.sh" "$@"
#!/bin/bash
# Useful for `git difftool`
# `git difftool --extcmd=/path/to/git-difftool-changed-lines.sh`
# Outputs the lines numbers of the new file
# that are not present in the old file.
# That is, outputs line numbers for new lines and changed lines
# and does not output line numbers deleted or unchanged lines.
# FORMAT: One file per line
# FILE:LINE_NUMBERS
# Where LINE_NUMBERS is a comma separated list of line numbers
args=(
# Don't output info for old (deleted) or unchanged (context) lines
--old-group-format="" --unchanged-group-format=""
# For new and changed lines, output one LINE_RANGE per line
--new-group-format="%dF-%dL%c'\012'"
--changed-group-format="%dF-%dL%c'\012'"
)
# `git difftool` calls this command as `git-difftool.sh "$LOCAL" "$REMOTE"
# and adds BASE to the environment.
# See https://git-scm.com/docs/git-difftool#Documentation/git-difftool.txt--xltcommandgt
echo -n "$BASE:"
diff "${args[@]}" "$1" "$2" | while IFS=- read -r LINE END; do
echo -n "$(( LINE++ ))"
for (( ; LINE <= END; LINE++ )); do
echo -n ",$LINE"
done
echo
done
@louwers
Copy link

louwers commented Mar 17, 2023

macOS users: install GNU diff or else it won't work.

@johnnymo87
Copy link

I have a use-case where on CI I cross this output with the output from a linter, and fail the CI build if there are any lines that match. Given the output of my linter (rubocop), I need these bash scripts to output a filename with each line number on a separate line. To accomplish this, I made this change:

diff --git c/scripts/git-difftool-changed-lines.sh i/scripts/git-difftool-changed-lines.sh
index 174035ee..5e1f5210 100644
--- c/scripts/git-difftool-changed-lines.sh
+++ i/scripts/git-difftool-changed-lines.sh
@@ -27,12 +27,9 @@ args=(
 # and adds BASE to the environment.
 # See https://git-scm.com/docs/git-difftool#Documentation/git-difftool.txt--xltcommandgt
 
-echo -n "$BASE:"
-
 diff "${args[@]}" "$1" "$2" | while IFS=- read -r LINE END; do
-  echo -n "$(( LINE++ ))"
   for (( ; LINE  <= END; LINE++ )); do
-    echo -n ",$LINE"
+    echo "$BASE:$LINE"
   done
   echo
 done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment