I can search a string in changes with git log -S "text to find" --stat
. How can I do a similar search, except look for the string in only added or removed lines (not both)?
There is a --diff-filter
option but looks like it filters the whole file rather than individual lines.
UPDATE
I've crafted a bash script based on @LeGEC's answer. It's pretty overengineered and rather inefficient (especially on cygwin) but it works well.
Save the following script as a file named git-xlog
if you like Git to pick it up as an xlog
sub-command. Don't forget to set the executable bit on linux.
Example:
git xlog --diff-type=added --log-opt='--author=someone -S "sometext"' --diff-opt='--stat'
#!/usr/bin/env bash
app_name="$(basename "${BASH_SOURCE[0]}")"
USAGE=$(cat << EOF
Usage:
$app_name
Search history for string only in added or removed lines.
Options:
-t, --diff-type=<A|R|Added|Removed>
Whether to search in added or removed lines.
-l, --log-opt=<LOG_OPTIONS>
Options to pass on to git log commands.
-d, --diff-opt=<DIFF_OPTIONS>
Options to pass on to git diff commands.
--help
Show this help information and exit.
EOF
)
getopt -q -T
if [[ $? -ne 4 ]]; then
echo "$app_name: This script requires an enhanced getopt version." >&2
exit 1
fi
opts=$(getopt -o 't:l:d:' --long 'help,diff-type:,log-opt:,diff-opt:' -n "$app_name" -- "$@") || exit $?
eval "opts=($opts)"
for ((i=0; i < ${#opts[@]}; i++)); do
opt="${opts[$i]}"
case "$opt" in
--help)
echo "$USAGE"
exit 0
;;
-t|--diff-type)
diff_type="${opts[++i]}"
;;
-l|--log-opt)
log_opt="${opts[++i]}"
;;
-d|--diff-opt)
diff_opt="${opts[++i]}"
;;
--)
break
;;
*)
echo 'Internal error!' >&2
exit 1
;;
esac
done
if [[ ! -v diff_type ]]; then
echo "$app_name: Missing '--diff-type' argument." >&2
exit 1
elif [[ -z $diff_type ]]; then
echo "$app_name: Missing value for argument '--diff-type'." >&2
exit 1
elif [[ ${diff_type@U} == 'A' || ${diff_type@U} == "ADDED" ]]; then
diff_mark='>'
elif [[ ${diff_type@U} == 'R' || ${diff_type@U} == "REMOVED" ]]; then
diff_mark='<'
else
echo "$app_name: Unrecognized diff-type: '$diff_type'. Accepted values: 'A', 'Added', 'R', 'Removed'." >&2
exit 1
fi
if [[ ! -v log_opt ]]; then
echo "$app_name: Missing '--log-opt' argument." >&2
exit 1
elif [[ -z $log_opt ]]; then
echo "$app_name: Missing value for argument '--log-opt'." >&2
exit 1
fi
opts=$(eval getopt -q -o 'S:G:' -n "$app_name" -- "$log_opt")
eval "opts=($opts)"
for ((i=0; i < ${#opts[@]}; i++)); do
opt="${opts[$i]}"
case "$opt" in
-S)
search_text="${opts[++i]}"
;;
-G)
search_regex="${opts[++i]}"
;;
--)
break
;;
*)
echo 'Internal error!' >&2
exit 1
;;
esac
done
if [[ ! -v search_text && ! -v search_regex ]]; then
echo "$app_name: No '-S' or '-G' found in log options." >&2
exit 1
elif [[ -z $search_text && -z $search_regex ]]; then
echo "$app_name: Missing value for argument '-S' or '-G' in log options." >&2
exit 1
fi
trap interrupt INT
interrupt () {
echo "$app_name: Interrupted." >&2
exit 1
}
if [[ -t 1 ]]; then
git_color="--color"
less_color="-r"
fi
if [[ -n $search_regex ]]; then
grep_opt='-E "$search_regex"'
elif [[ -n $search_text ]]; then
grep_opt='"$search_text"'
fi
set -e
eval git log "$log_opt" --no-patch --format=%H |
while read -r sha; do
eval git diff -U0 "$sha~" "$sha" --output-indicator-new='\>' --output-indicator-old='\<' |
grep -E "^$diff_mark" |
eval grep -q "$grep_opt" &&
eval git --no-pager log "$git_color" --no-walk "$sha" "$log_opt" "$diff_opt"
done | less -deFX $less_color
There is no built-in way to do that.
The closest thing I think of would be to first run git log -S "text" ...
to identify commits, then scan the diffs:
# get a list of commit hashes from 'git log':
git log --format=%H -S "text" |
# for each commit: do some processing on the diff:
while read sha; do
git diff -U0 -S "text" "$sha"~ "$sha" |
# process ... count number of additions/deletions, and print the
# information you want
done
note that:
git diff
also accepts -S "text"
: this allows to at least narrow the diff to the files involved in contributing to -S-U0
will remove all context lines, if you are interested in counting only the added/removed counts, this may help reduce the number of lines to scan