cg-Xlib | 2 - cg-diff | 166 ++++++++++++++++++++++++++++++--------------------------------- 2 files changed, 79 insertions(+), 89 deletions(-) diff --git a/cg-Xlib b/cg-Xlib --- a/cg-Xlib +++ b/cg-Xlib @@ -24,7 +24,7 @@ usage() { pager () { local line # Invoke pager only if there's any actual output - if read -r line; then + if IFS='' read -r line; then ( echo "$line"; cat; ) | LESS="R$LESS" ${PAGER:-less} $PAGER_FLAGS fi } diff --git a/cg-diff b/cg-diff --- a/cg-diff +++ b/cg-diff @@ -13,6 +13,9 @@ # -c:: # Colorize the diff output # +# -d, --diffstat:: +# Show `diffstat' output before diff. +# # -p:: # Instead of one ID denotes a parent commit to the specified ID # (which must not be a tree, obviously). @@ -26,7 +29,10 @@ # # -m:: # Base the diff at the merge base of the -r arguments (defaulting -# to master and origin). +# to HEAD and origin). +# +# -R:: +# Output diff in reverse. # # ENVIRONMENT VARIABLES # --------------------- @@ -37,132 +43,116 @@ # Flags to pass to the pager. By default `R` is added to the `LESS` # environment variable to allow displaying of colorized output. -USAGE="cg-diff [-c] [-m] [-p] [-r FROM_ID[:TO_ID]] [FILE]..." +USAGE="cg-diff [-c] [-d] [-m] [-p] [-R] [-r FROM_ID[:TO_ID]] [FILE]..." . ${COGITO_LIB}cg-Xlib -id1=" " -id2=" " -parent= -opt_color= -mergebase= +unset id1 id2 parent opt_color mergebase diffprog sedprog diffstat difftmp +dtargs=() # TODO: Make cg-log use this too. setup_colors() { - local C="diffhdr=1;36:diffhdradd=1;32:diffadd=32:diffhdrmod=1;35:diffmod=35:diffhdrrem=1;31:diffrem=31:diffhunk=36:diffctx=34:diffcctx=33:default=0" + local C="diffhdr=1;36:diffhdradd=1;32:diffadd=32:diffhdrmod=1;35" + C="$C:diffmod=35:diffhdrrem=1;31:diffrem=31:diffhunk=36:diffctx=34" + C="$C:files=34:default=0" [ -n "$COGITO_COLORS" ] && C="$C:$COGITO_COLORS" C=${C//=/=\'$'\e'[} C=col${C//:/m\'; col}m\' - #coldefault=$(tput op) eval $C + + color_rules=" +s,^+++.*,$coldiffhdradd&$coldefault, +s,^new file mode.*,$coldiffhdradd&$coldefault, +s,^---.*,$coldiffhdrrem&$coldefault, +s,^deleted file mode.*,$coldiffhdrrem&$coldefault, +s,^[+].*,$coldiffadd&$coldefault, +s,^[-].*,$coldiffrem&$coldefault, +s,^\\(@@.*@@\\)\\(.*\\),$coldiffhunk\\1$coldiffctx\\2$coldefault, +s,^\\(diff\\) .*,$coldiffhdr&$coldefault," +} + +show_diffstat() { + [ -s "$difftmp" ] || return + git-apply --stat "$difftmp" + echo + cat "$difftmp" } while optparse; do if optparse -c; then opt_color=1 - setup_colors elif optparse -p; then + [ "$mergebase" ] && optconflict parent=1 elif optparse -r=; then - if echo "$OPTARG" | grep -q ':'; then - id2=$(echo "$OPTARG" | cut -d : -f 2) - [ "$id2" ] || log_end="HEAD" - id1=$(echo "$OPTARG" | cut -d : -f 1) - elif [ "$id1" = " " ]; then - id1="$OPTARG" + if [ ! "${id1+set}" ]; then + id1=$OPTARG + if [[ "$id1" == *:* ]]; then + id2=${id1#*:} + id1=${id1%:*} + fi else - id2="$OPTARG" + [ "${id2+set}" ] && die "too many revisions" + id2=$OPTARG fi elif optparse -m; then + [ "$parent" ] && optconflict mergebase=1 + elif optparse -d || optparse --diffstat; then + diffstat=1 + elif optparse -R; then + dtargs[${#dtargs[@]}]="-R" else optfail fi done -colorize() { - if [ "$opt_color" ]; then - gawk ' - { if (/^(Index:|diff --git) /) - print "'$coldiffhdr'" $0 "'$coldefault'" - else if (/^======*$/) - print "'$coldiffhdr'" $0 "'$coldefault'" - else if (/^\+\+\+/) - print "'$coldiffhdradd'" $0 "'$coldefault'" - else if (/^\*\*\*/) - print "'$coldiffhdrmod'" $0 "'$coldefault'" - else if (/^---/) - print "'$coldiffhdrrem'" $0 "'$coldefault'" - else if (/^(\+|new( file)? mode )/) - print "'$coldiffadd'" $0 "'$coldefault'" - else if (/^(-|(deleted file|old) mode )/) - print "'$coldiffrem'" $0 "'$coldefault'" - else if (/^!/) - print "'$coldiffmod'" $0 "'$coldefault'" - else if (/^@@ \-[0-9]+(,[0-9]+)? \+[0-9]+(,[0-9]+)? @@/) - print gensub(/^(@@[^@]*@@)([ \t]*)(.*)/, - "'$coldiffhunk'" "\\1" "'$coldefault'" \ - "\\2" \ - "'$coldiffctx'" "\\3" "'$coldefault'", "") - else if (/^\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/) - print "'$coldiffcctx'" $0 "'$coldefault'" - else - print - }' - else - cat - fi -} - if [ "$parent" ]; then + [ "${id2+set}" ] && die "too many revisions" id2="$id1" - id="$id2"; [ "$id" = " " ] && id="" - id1=$(parent-id "$id" | head -n 1) || exit 1 + id1=$(parent-id "$id2" | head -n 1) || exit 1 +elif [ "$mergebase" ]; then + id1="$(commit-id "${id1:-HEAD}")" || exit 1 + id2="$(commit-id "${id2:-origin}")" || exit 1 + id1="$(git-merge-base "$id1" "$id2")" || exit 1 +else + id1=$(tree-id "$id1") || exit 1 fi -if [ "$mergebase" ]; then - [ "$id1" != " " ] || id1="master" - [ "$id2" != " " ] || id2="origin" - id1=$(git-merge-base $(commit-id "$id1") $(commit-id "$id2")) -fi - - -filter=$(mktemp -t gitdiff.XXXXXX) -for file in "${ARGS[@]}"; do - echo "$file" >>$filter -done - -if [ "$id2" = " " ]; then - if [ "$id1" != " " ]; then - tree=$(tree-id "$id1") || exit 1 - else - tree=$(tree-id) || exit 1 - fi +diffprog=git-diff-tree +if [ ! "${id2+set}" ]; then # Ensure to only diff modified files git-update-cache --refresh >/dev/null - - # FIXME: Update ret based on what did we match. And take "$@" - # to account after all. - ret= - cat $filter | xargs git-diff-cache -r -p $tree | colorize | pager - - rm $filter - - [ "$ret" ] && die "no files matched" - exit $ret + diffprog=git-diff-cache +else + id2=$(tree-id "$id2") || exit 1 fi - -id1=$(tree-id "$id1") || exit 1 -id2=$(tree-id "$id2") || exit 1 - [ "$id1" = "$id2" ] && die "trying to diff $id1 against itself" +diffopts=(-r -p "${dtargs[@]}" $id1 $id2 "${ARGS[@]}") -cat $filter | xargs git-diff-tree -r -p $id1 $id2 | colorize | pager +if [ "$diffstat" ]; then + difftmp=$(mktemp -t cgdiff.XXXXXX) || exit 1 + trap "rm '$difftmp'" SIGTERM EXIT + $diffprog "${diffopts[@]}" > $difftmp -rm $filter -exit 0 + diffprog=show_diffstat + diffopts= +fi + +if [ "$opt_color" ]; then + setup_colors + sedprog="$color_rules" + + [ "$diffstat" ] && sedprog="$sedprog +s,^\\( [^ ].*\\)\\( | *[0-9][0-9]* \\),$colfiles\\1$coldefault\\2," + + $diffprog "${diffopts[@]}" | sed -e "$sedprog" | pager + exit $PIPESTATUS +else + $diffprog "${diffopts[@]}" | pager +fi