{"thread":{"id":"883","subject":"[PATCH 6/6] Make cg-log use optparse, and add features","startedAt":"2005-06-09T11:29:14Z","lastAt":"2005-06-12T07:29:27Z","messageCount":4,"participants":["Dan Holmsand","Petr Baudis"],"isPatch":true,"patchVersion":1,"patchTotal":6},"messages":[{"id":"4782","messageId":"42A8280A.3070607@gmail.com","threadId":"883","inReplyTo":null,"subject":"[PATCH 6/6] Make cg-log use optparse, and add features","fromName":"Dan Holmsand","fromEmail":"holmsand@gmail.com","sentAt":"2005-06-09T11:29:14Z","receivedAt":"2005-06-09T11:29:14Z","isPatch":true,"sender":{"key":"holmsand@gmail.com","avatar":"https://gravatar.com/avatar/5c722084bafd85e754a02efad01fe69107eb6f393253c49232c5c9f7faa974df?d=mp&s=160"},"body":"This is more or less a rewrite of cg-log, that adds a bunch\nof new features and gives a substantial speedup.\n\ncg-log now lets git-rev-list and git-diff-tree do as much\nof the heavy lifting as possible. When possible, cg-log\npretty much reduces to a sed script, that does some fixup\nand adds colorization. Otherwise, cg-log uses\n--pretty=[raw|medium|short] output, that's augmented as\nnecessary.\n\nNew features:\n\n  - New, human friendlier output format. cg-log now uses\n    the same format as git-rev-list --pretty. It also\n    doesn't show tree, committer and parents by default.\n    These are all shown with \"-v or --verbose\", however.\n\n  - Slightly more readable summary format. sha1s are hidden\n    unless -v is given. Date format is condenced to HH:MM\n    for the last day, and Month Day otherwise. (+year is\n    shown for really old stuff).\n\n    summary format can also be combined with other options,\n    like \"-f\" and \"-d\" (diffstat).\n\n  - \"-u\" (or --search=) now searches the entire commit\n    message. This is more flexible, as you can still do e.g.\n    \"cg-log -u 'author Petr Baudis'\". Search is now also\n    a lot faster, done with a single grep invocation.\n\n  - Output can be limited by date or count.\n\n  - New format for \"-f\". This is pretty much the same\n    output as cg-status gives, and allows for the\n    rename/copy detection things to work. New and\n    deleted files are optionally color coded.\n\n    The old format is still available as -F.\n\n  - Merge commits are hidden by default when displaying\n    differences between commits, or when displaying disjoint\n    sets of commits. \"-a\" makes them show again. So\n    \"cg-log -caF\" gives you pretty much the same output\n    as the old \"cg-log -c -f\" would.\n\n  - Shows diffs between commits with the \"-p\" options.\n    The diffs use the same colorization rules as cg-diff,\n    and can be skipped through with \"n\" in less if \"-c\"\n    is given.\n\n  - \"--stdin\" reads sha1s from stdin instead of from\n    git-rev-list, for some extra flexibility.\n\nNew features in common with cg-diff:\n\n  - diffstat support (or rather git-apply --stat). The\n    \"-d\" option outputs (optionally colorized) diff stats\n    before the diff.\n\n  - support for more git-diff-[tree/cache] options:\n    -B, -R, -M, -C are now all passed on.\n\n  - The COGITO_AUTO_COLOR environment variable makes output\n    automatically colorized, if set and if we're on a color\n    capable terminal.\n\nWe also use the new optparse function from cg-Xlib, to allow\nfor e.g. \"cg-log -sfa\" and stuff.\n\nReuse colorization logic from cg-Xlib. All the colors are\nadded in a single sed invocation.\n\nAnd use LESS to make \"less\" search for chunks and diff --git\nmarkers. This allows you to \"n\" your way through a series\nof diffs, and gives a nice visual separation of patches.\n\nSigned-off-by: Dan Holmsand <holmsand@gmail.com>\n---\n\n\n cg-log |  545 +++++++++++++++++++++++++++++++++++++++-------------------------\n 1 files changed, 331 insertions(+), 214 deletions(-)\n\ndiff --git a/cg-log b/cg-log\n--- a/cg-log\n+++ b/cg-log\n@@ -3,6 +3,7 @@\n # Make a log of changes in a GIT branch.\n # Copyright (c) Petr Baudis, 2005.\n # Copyright (c) David Woodhouse, 2005.\n+# Copyright (c) Dan Holmsand, 2005.\n #\n # Display log information for files or a range of commits. The output\n # will automatically be displayed in a pager unless it is piped to\n@@ -13,7 +14,7 @@\n # Arguments not interpreted as options will be interpreted as filenames;\n # cg-log then displays only changes in those files.\n #\n-# -c::\n+# -c, --color::\n #\tColorize to the output. The used colors are listed below together\n #\twith information about which log output (summary, full or both)\n #\tthey apply to:\n@@ -26,8 +27,15 @@\n #\t\t- `date`:\t'green'\t\t(summary)\n #\t\t- `trim_mark`:\t'magenta'\t(summary)\n #\n+# -d, --diffstat::\n+#\tShow `diffstat' for every commit.\n+#\n # -f::\n-#\tList affected files. (No effect when passed along `-s`.)\n+#\tShow list of files modified in each commit.\n+#\n+# -F::\n+#\tShow ChangeLog-style list of modified files, instead of the\n+#\tdefault listing.\n #\n # -r FROM_ID[:TO_ID]::\n #\tLimit the log information to a set of revisions using either\n@@ -37,29 +45,61 @@\n #\tto the initial commit is shown. If no revisions is specified,\n #\tthe log information starting from 'HEAD' will be shown.\n #\n-# -m::\n-#\tEnd the log listing at the merge base of the -r arguments\n-#\t(defaulting to master and origin).\n-#\n-# -s::\n+# -s, --summary::\n #\tShow a one line summary for each log entry. The summary contains\n #\tinformation about the commit date, the author, the first line\n #\tof the commit log and the commit ID. Long author names and commit\n #\tIDs are trimmed and marked with an ending tilde (~).\n #\n-# -uUSERNAME::\n-#\tList only commits where author or committer contains 'USERNAME'.\n-#\tThe search for 'USERNAME' is case-insensitive.\n+# -p, --patches::\n+#\tShow diffs for files modified by each commit.\n+#\n+# -v, --verbose::\n+#\tShow committer, tree and parents for each commit.\n+#\n+# -a, --all::\n+#\tShow merge commits as well.\n+#\n+# -m::\n+#\tEnd the log listing at the merge base of the -r arguments\n+#\t(defaulting to master and origin).\n+#\n+# -u, --search=TEXT::\n+#\tList only commits where author, committer or commit message\n+#\tcontains 'TEXT'. The search for 'TEXT' is case-insensitive.\n+#\n+# -S, --match=TEXT::\n+#\tLook for commits that introduces TEXT in a file.\n+#\n+# -M::\n+#\tDetect renames.\n+#\n+# -C::\n+#\tDetect copies (as well as renames).\n+#\n+# -B::\n+#\tDetect rewrites.\n+#\n+# -y, --max-age=DATE::\n+#\tLimit output to commits younger than DATE.\n+#\n+# -o, --min-age=DATE::\n+#\tLimit output to commits older than DATE.\n+#\n+# --max-count=N::\n+#\tShow at most N commits.\n+#\n+# --stdin::\n+#\tShow commits for sha1s read from stdin.\n #\n # ENVIRONMENT VARIABLES\n # ---------------------\n # PAGER::\n #\tThe pager to display log information in, defaults to `less`.\n #\n-# PAGER_FLAGS::\n-#\tFlags to pass to the pager. By default `R` and `S` is added to the\n-#\t`LESS` environment variable to allow displaying of colorized output\n-#\tand to avoid long lines from wrapping when using `-s`.\n+# COGITO_AUTO_COLOR::\n+#\tIf set, colorized output is used automatically on color-capable\n+#\tterminals.\n #\n # EXAMPLE USAGE\n # -------------\n@@ -75,228 +115,305 @@ USAGE=\"cg-log [-c] [-f] [-m] [-s] [-uUSE\n # at least somewhere it does. Bash is broken.\n trap exit SIGPIPE\n \n-[ \"$COLUMNS\" ] || COLUMNS=\"$(tput cols)\"\n+# speed bash up on utf-8 locales\n+LANG=C\n \n-colheader=\n-colauthor=\n-colcommitter=\n-colfiles=\n-colsignoff=\n-colcommit=\n-coldate=\n-coltrim=\n-coldefault=\n-\n-list_files=\n-log_start=\n-log_end=\n-summary=\n-user=\n-mergebase=\n-files=()\n-\n-while [ \"$1\" ]; do\n-\tcase \"$1\" in\n-\t-c)\n-\t\t# See terminfo(5), \"Color Handling\"\n-\t\tcolheader=\"$(tput setaf 2)\"    # Green\n-\t\tcolauthor=\"$(tput setaf 6)\"    # Cyan\n-\t\tcolcommitter=\"$(tput setaf 5)\" # Magenta\n-\t\tcolfiles=\"$(tput setaf 4)\"     # Blue\n-\t\tcolsignoff=\"$(tput setaf 3)\"   # Yellow\n-\n-\t\tcolcommit=\"$(tput setaf 4)\"\n-\t\tcoldate=\"$(tput setaf 2)\"\n-\t\tcoltrim=\"$(tput setaf 5)\"\n-\n-\t\tcoldefault=\"$(tput op)\"        # Restore default\n-\t\t;;\n-\t-f)\n-\t\tlist_files=1\n-\t\t;;\n-\t-u*)\n-\t\tuser=\"${1#-u}\"\n-\t\t;;\n-\t-r)\n-\t\tshift\n-\t\tif echo \"$1\" | grep -q ':'; then\n-\t\t\tlog_end=$(echo \"$1\" | cut -d : -f 2)\n-\t\t\t[ \"$log_end\" ] || log_end=\"HEAD\"\n-\t\t\tlog_start=$(echo \"$1\" | cut -d : -f 1)\n-\t\telif [ -z \"$log_start\" ]; then\n-\t\t\tlog_start=\"$1\"\n-\t\telse\n-\t\t\tlog_end=\"$1\"\n-\t\tfi\n-\t\t;;\n-\t-m)\n-\t\tmergebase=1\n-\t\t;;\n-\t-s)\n-\t\tsummary=1\n-\t\t;;\n-\t*)\n-\t\tfiles=(\"$@\")\n-\t\tbreak\n-\t\t;;\n-\tesac\n-\tshift\n-done\n+unset id1 id2 user verbose filelist patches sedprog incoming pretty maxn\n+unset diffstat stdin opt_color renames dtmode quiet summary longsummary files\n+unset needparse\n+filemode=-s\n+dtargs=() rlargs=()\n+\n+revlist() {\n+\tif [ \"$stdin\" ]; then\n+\t\t# read sha1s from stdin. remove leading stuff from\n+\t\t# git-rev-tree output.\n+\t\tsed 's/^[0-9]* \\([a-f0-9]\\{40\\}\\)/\\1/'\n+\telif [ \"$user\" ]; then\n+\t\tgit-rev-list --header \"${rlargs[@]}\" |\n+\t\tLANG=C grep -iz \"$user\" | tr '\\n\\0' '\\t\\n' | cut -f1\n+\telse\n+\t\tgit-rev-list \"${rlargs[@]}\" \"$@\"\n+\tfi \n+}\n \n-list_commit_files()\n-{\n-\ttree1=\"$1\"\n-\ttree2=\"$2\"\n-\tline=\n-\tsep=\"    * $colfiles\"\n-\t# List all files for for the initial commit\n-\tif [ -z $tree2 ]; then\n-\t\tlist_cmd=\"git-ls-tree $tree1\"\n+dolog() {\n+\tif [ \"$dtmode\" = diff-tree ]; then\n+\t\trevlist | \n+\t\tgit-diff-tree --stdin -v -r $filemode \"${dtargs[@]}\" \\\n+\t\t\t-- \"${ARGS[@]}\"\n \telse\n-\t\tlist_cmd=\"git-diff-tree -r $tree1 $tree2\"\n+\t\trevlist --pretty${pretty+=}$pretty\n \tfi\n+}\n+\n+showstat() {\n+\tgit-diff-tree -r -p \"${dtargs[@]}\" $1 $2 -- \"${ARGS[@]}\" | \n+\t\tgit-apply --stat 2>/dev/null\n \techo\n-\t$list_cmd | cut -f 2- | while read file; do\n-\t\techo -n \"$sep\"\n-\t\tsep=\", \"\n-\t\tline=\"$line$sep$file\"\n-\t\tif [ ${#line} -le 74 ]; then\n-\t\t\techo -n \"$file\"\n+}\n+\n+showfiles() {\n+\tlocal p=6 sep=\"    * $colfiles\" IFS=$'\\n' file end=\":$coldefault\"\n+\t[ \"$summary\" ] && { end=\"$coldefault\"$'\\n'; }\n+\tfor i in $(git-diff-tree -m -r $1 $2); do\n+\t\t[[ \"$i\" == :* ]] || continue\n+\t\ti=${i##*$'\\t'}\n+\t\tif (( (p += 2 + ${#i}) < 75 )); then\n+\t\t\techo -n \"$sep$i\"\n \t\telse\n-\t\t\tline=\"      $file\"\n-\t\t\techo \"$coldefault\"\n-\t\t\techo -n \"      $colfiles$file\"\n+\t\t\t(( p = 6 + ${#i} ))\n+\t\t\techo \",$coldefault\"\n+\t\t\techo -n \"      $colfiles$i\"\n \t\tfi\n+\t\tsep=\", \"\n \tdone\n-\techo \"$coldefault:\"\n+\techo \"$end\"\n }\n \n-process_commit_line()\n-{\n-\tif [ \"$key\" = \"%\" ] || [ \"$key\" = \"%$colsignoff\" ]; then\n-\t\t# The fast common case\n-\t\t[ \"$summary\" ] || [ \"$skip_commit\" ] || echo \"    $rest\"\n-\t\treturn\n+toepoch() {\n+\texpr \"$1\" : \"[0-9]*$\" >/dev/null && echo \"$1\" ||\n+\tdate -ud \"$1\" +%s || die \"invalid date $1\"\n+}\n+\n+showsummary() {\n+\tlocal commit=$1 author=$2 date=$3 text=$4 da\n+\tauthor=${author#Author: }\n+\tauthor=${author% <*}\n+\t[ ${#author} -gt 14 ] && author=${author:0:13}$coltrim~\n+\tif [ -z \"$longsummary\" ]; then\n+\t\tcommit=\n+\telif [ \"${COLUMNS:-0}\" -le 90 ]; then\n+\t\tcommit=\"$colcommit${commit:0:12}$coltrim~ \"\n+\telse\n+\t\tcommit=\"$colcommit$commit \"\n \tfi\n-\tcase \"$key\" in\n-\t\"commit\")\n-\t\t[ \"$summary\" ] || [ \"$skip_commit\" ] || { [ \"$commit\" ] && echo; }\n-\t\tcommit=\"$rest\"\n-\t\tparents=()\n-\t\tskip_commit=\n-\t\t;;\n-\t\"tree\")\n-\t\ttree=\"$rest\"\n-\t\t;;\n-\t\"parent\")\n-\t\tparents[${#parents[@]}]=\"$rest\"\n-\t\t;;\n-\t\"committer\")\n-\t\tcommitter=\"$rest\"\n-\t\t;;\n-\t\"author\")\n-\t\tauthor=\"$rest\"\n-\t\t;;\n-\t\"\")\n-\t\tif [ ! \"$commit\" ]; then\n-\t\t\t# Next commit is coming\n-\t\t\t[ \"$summary\" ] || echo\n-\t\t\treturn\n-\t\tfi\n+\t# find suitable short date format, assumes date in \"std linus format\"\n+\tda=(${date#Date:})\n+\tlocal dyear=${da[4]}\n+\tif [ \"$year\" -ne \"$dyear\" ]; then\n+\t\t# year month day\n+\t\tdate=\"${da[1]} ${da[2]} $dyear\"\n+\telif [ \"$month\" != \"${da[1]}\" -o \"$day\" -ne \"${da[2]}\" ]; then\n+\t\t# month day\n+\t\tdate=\"${da[1]} ${da[2]}\"\n+\telse\n+\t\t# time\n+\t\tdate=${da[3]%:*}\n+\tfi\n+\tline=${line#    }\n+\tprintf \"%s$colauthor%-14s $coldate%-6s $coldefault%s\\n\" \\\n+\t\t\"$commit\" \"$author\" \"$date\" \"$line\"\n+}\n \n-\t\tif [ \"$user\" ]; then\n-\t\t\tif ! echo -e \"author $author\\ncommitter $committer\" \\\n-\t\t\t     | grep -qi \"$user\"; then\n-\t\t\t\tskip_commit=1\n-\t\t\t\treturn\n-\t\t\tfi\n-\t\tfi\n-\t\tif [ \"$files\" ]; then\n-\t\t\tparent=\"${parents[0]}\"\n-\t\t\tdiff_ops=\n-\t\t\t[ \"$parent\" ] || diff_ops=--root\n-\t\t\tif ! [ \"$(git-diff-tree -r $diff_ops $commit $parent \"${files[@]}\")\" ]; then\n-\t\t\t\tskip_commit=1\n-\t\t\t\treturn\n-\t\t\tfi\n-\t\tfi\n-\t\tif [ \"$summary\" ]; then\n-\t\t\t# Print summary\n-\t\t\tcommit=\"${commit%:*}\"\n-\t\t\tauthor=\"${author% <*}\"\n-\t\t\tdate=(${committer#*> })\n-\t\t\tdate=\"$(showdate $date '+%F %H:%M')\"\n-\t\t\tread title\n-\t\t\tif [ \"${#author}\" -gt 15 ]; then\n-\t\t\t\tauthor=\"${author:0:14}$coltrim~\"\n-\t\t\tfi\n-\t\t\tif [ \"${COLUMNS:-0}\" -le 90 ]; then\n-\t\t\t\tcommit=\"${commit:0:12}$coltrim~\"\n+while optparse; do\n+\tif optparse -c || optparse --color; then\n+\t\topt_color=1\n+\telif optparse -f; then\n+\t\t[ \"$patches\" ] && optconflict\n+\t\tfiles=1\n+\t\tfilemode=\n+\telif optparse -F; then\n+\t\tfilelist=1\n+\t\tdtmode=diff-tree\n+\telif optparse -p || optparse --patches; then\n+\t\t[ \"$files\" ] && optconflict\n+\t\tpatches=1\n+\t\tfilemode=-p\n+\telif optparse -d || optparse --diffstat; then\n+\t\tdiffstat=1\n+\t\tdtmode=diff-tree\n+\telif optparse -u= || optparse --search= 2; then\n+\t\tuser=$OPTARG\n+\t\tdtmode=diff-tree\n+\telif optparse -q || optparse --short; then\n+\t\t[ \"$verbose$summary\" ] && optconflict\n+\t\tquiet=1\n+\telif optparse -v || optparse --verbose; then\n+\t\t[ \"$quiet\" ] && optconflict\n+\t\tverbose=1\n+\telif optparse -s || optparse --summary 2; then\n+\t\t[ \"$quiet\" ] && optconflict\n+\t\tsummary=1\n+\telif optparse --stdin 2; then\n+\t\tstdin=1\n+\t\tdtmode=diff-tree\n+\telif optparse -y= || optparse --max-age= 5; then\n+\t\trlargs[${#rlargs[@]}]=--max-age=$(toepoch \"$OPTARG\") || exit 1\n+\telif optparse -o= || optparse --min-age= 2; then\n+\t\trlargs[${#rlargs[@]}]=--min-age=$(toepoch \"$OPTARG\") || exit 1\n+\telif optparse --max-count= 5; then\n+\t\tmaxn=$OPTARG\n+\telif optparse -a || optparse --all; then\n+\t\tdtargs[${#dtargs[@]}]=\"-m\"\n+\t\tdtargs[${#dtargs[@]}]=\"--root\"\n+\telif optparse -m || optparse --mergebase 2; then\n+\t\tincoming=1\n+\telif optparse -S= || optparse --match= 3; then\n+\t\tdtargs[${#dtargs[@]}]=\"-S$OPTARG\"\n+\telif optparse -M; then\n+\t\t[ \"$renames\" ] && optconflict\n+\t\t[ \"$patches\" ] || filemode=\n+\t\tdtargs[${#dtargs[@]}]=\"-M\"\n+\t\trenames=1\n+\telif optparse -C; then\n+\t\t[ \"$renames\" ] && optconflict\n+\t\t[ \"$patches\" ] || filemode=\n+\t\tdtargs[${#dtargs[@]}]=\"-C\"\n+\t\trenames=1\n+\telif optparse -B; then\n+\t\t[ \"$patches\" ] || filemode=\n+\t\tdtargs[${#dtargs[@]}]=\"-B\"\n+\telif optparse -r= || optparse --revision=; then\n+\t\tif [ -z \"${id1+set}\" ]; then\n+\t\t\tid1=$OPTARG\n+\t\t\tif [[ \"$id1\" == *:* ]]; then\n+\t\t\t\tid2=${id1#*:}\n+\t\t\t\tid1=${id1%:*}\n \t\t\tfi\n-\n-\t\t\tprintf \"$colcommit%s $colauthor%-15s $coldate%s $coldefault%s\\n\" \\\n-\t\t\t\t\"${commit%:*}\" \"$author\" \"$date\" \"${title:2}\"\n-\t\t\tcommit=\n-\t\t\treturn\n+\t\telse\n+\t\t\t[ -z \"${id2+set}\" ] || die \"too many revisions\"\n+\t\t\tid2=$OPTARG\n \t\tfi\n+\telse\n+\t\toptfail\n+\tfi\n+done\n \n-\t\techo ${colheader}commit ${commit%:*} $coldefault\n-\t\techo ${colheader}tree $tree $coldefault\n+[ -n \"$COGITO_AUTO_COLOR\" -a -t 1 ] && [ \"$(tput setaf 1 2>/dev/null)\" ] && \n+\topt_color=1\n \n-\t\tfor parent in \"${parents[@]}\"; do\n-\t\t\techo ${colheader}parent $parent $coldefault\n-\t\tdone\n+LESS=\"-S $LESS\"\n+[ \"$COLUMNS\" ] || COLUMNS=\"$(tput cols)\"\n \n-\t\tdate=(${author#*> })\n-\t\tpdate=\"$(showdate $date)\"\n-\t\t[ \"$pdate\" ] && author=\"${author%> *}> $pdate\"\n-\t\techo ${colauthor}author $author $coldefault\n-\n-\t\tdate=(${committer#*> })\n-\t\tpdate=\"$(showdate $date)\"\n-\t\t[ \"$pdate\" ] && committer=\"${committer%> *}> $pdate\"\n-\t\techo ${colcommitter}committer $committer $coldefault\n+if [ \"$incoming\" ]; then\n+\tid1=\"$(commit-id \"${id1:-origin}\")\" || exit 1\n+\tid2=\"$(commit-id \"${id2:-HEAD}\")\" || exit 1\n+\tid2=\"$(git-merge-base \"$id1\" \"$id2\")\" || exit 1\n+fi\n \n-\t\tif [ -n \"$list_files\" ]; then\n-\t\t\tlist_commit_files \"$tree\" \"${parents[0]}\"\n-\t\tfi\n-\t\techo\n-\t\tcommit=\n-\t\t;;\n-\tesac\n-}\n+id1=$(commit-id \"$id1\") || exit 1\n+[ -z \"${id2+set}\" ] || id2=$(commit-id \"$id2\") || exit 1\n+rlargs=( \"${rlargs[@]}\" $id1 ${id2+^}$id2 )\n \n-print_commit_log()\n-{\n-\tcommit=\n-\tauthor=\n-\tcommitter=\n-\ttree=\n-\n-\tsed -e '\n-\t\ts/^    \\(.*\\)/% \\1/\n-\t\t/^% *[Ss]igned-[Oo]ff-[Bb]y:.*/ s/^% \\(.*\\)/% '$colsignoff'\\1'$coldefault'/\n-\t\t/^% *[Aa]cked-[Bb]y:.*/ s/^% \\(.*\\)/% '$colsignoff'\\1'$coldefault'/\n-\t' | while read key rest; do\n-\t\ttrap exit SIGPIPE\n-\t\tprocess_commit_line\n-\tdone\n-}\n+if [ -n \"${dtargs[*]}${ARGS[*]}\" -o \"$filemode\" != -s -o \"$dtmode\" ]; then\n+\tdtmode=diff-tree\n+else\n+\tdtmode=commit\n+\t[ \"$verbose\" -a -z \"$summary\" ] && pretty=raw\n+\t[ \"$quiet\" ] && { pretty=short; quiet=; } \n+fi\n \n-if [ \"$mergebase\" ]; then\n-\t[ \"$log_start\" ] || log_start=\"master\"\n-\t[ \"$log_end\" ] || log_end=\"origin\"\n-\tlog_start=$(git-merge-base $(commit-id \"$log_start\") $(commit-id \"$log_end\"))\n+if [ \"$summary\" ]; then\n+\tyear=$(date +%Y)\n+\tmonth=$(LANG=C date +%b)\n+\tday=$(date +%d)\n+\t[ \"$verbose\" ] && { longsummary=1; verbose=; }\n fi\n \n-id1=\"$(commit-id \"$log_start\")\" || exit 1\n-if [ \"$log_end\" ]; then\n-\tid2=\"$(commit-id \"$log_end\")\" || exit 1\n-\trevls=\"git-rev-list --pretty=raw $id2 ^$id1\"\n+if [ \"$filelist$diffstat$summary\" -o \"$pretty\" = raw -o $dtmode = diff-tree ]\n+then\n+\tneedparse=1\n else\n-\trevls=\"git-rev-list --pretty=raw $id1\"\n+\t[ \"$maxn\" ] && rlargs=(--max-count=\"$maxn\" \"${rlargs[@]}\")\n+fi\n+\n+if [ \"$opt_color\" ]; then\n+\tsetup_colors\n+\n+\tif [ ! \"$summary\" ]; then\n+\t\tLESS=$'+/\\013^Commit:.[a-f0-9]*|^diff.--git..*$'\" $LESS\"\n+\t\tsedprog=\"\n+s,^\\\\(Commit: [^ ]*\\\\)\\\\(.*\\\\),$colheader\\\\1$coldefault\\\\2,\n+s,^Author:.*,$colauthor&$coldefault,\n+s,^Date:.*,$colauthor&$coldefault,\n+s,^Tree:.*,$colheader&$coldefault,\n+s,^Parent:.*,$colheader&$coldefault,\n+s,^Committer:.*,$colcommitter&$coldefault,\n+s,^Commitdate:.*,$colcommitter&$coldefault,\n+s,^    Signed-[Oo]ff[- ][Bb]y:.*,$colsignoff&$coldefault,\n+s,^    Acked[- ][Bb]y:.*,$colsignoff&$coldefault,\"\n+\tfi\n+\n+\t[ \"$patches\" ] && sedprog=\"$sedprog;$color_rules\"\n+\n+\t[ \"$diffstat\" ] && sedprog=\"$sedprog\n+s,^\\\\( [^ ].*\\\\)\\\\( |  *[0-9][0-9]* \\\\),$colfiles\\\\1$coldefault\\\\2,\"\n+fi\n+\n+if [ $dtmode = diff-tree -a -z \"$filemode\" ]; then\n+\tfilediffstart=$'s,^:[0-9]* [^ ]* [^ ]* [^ ]* \\('\n+\tfilediffend=$'[^\\t]*\\)\\t\\(.*\\)'\n+\tsedprog=\"$sedprog\n+${filediffstart}[NCR]$filediffend,$coldiffadd\\\\1 \\\\2$coldefault,\n+${filediffstart}D$filediffend,$coldiffrem\\\\1 \\\\2$coldefault,\n+${filediffstart}.$filediffend,$colfiles\\\\1 \\\\2$coldefault,\"\n fi\n \n-# LESS=\"S\" will prevent less to wrap too long titles to multiple lines;\n-# you can scroll horizontally.\n-$revls | print_commit_log | LESS=\"S$LESS\" pager\n+if [ \"$needparse\" ]; then\n+\t# Slow version, parsing output from diff-tree or rev-list\n+\tunset commit showds from lineno line\n+\tn=0\n+\tdolog | ( trap exit SIGPIPE\n+\twhile [ \"$line\" ] || IFS='' read -r line; do \n+\tcase $line in \n+\t\t$dtmode\\ *)\n+\t\t[ \"$maxn\" ] && (( ++n > maxn )) && exit\n+\t\t[ \"$showds\" ] && showstat $commit $from\n+\t\tline=${line#$dtmode } f=\"%s\\n\" showds=$diffstat lineno=0\n+\t\tcommit=${line% (*} from=${line#*(from }; from=${from%)}\n+\t\t[ \"$summary\" ] && f= || echo \"Commit: $line\" ;;\n+\n+\t\tAuthor:*) printf \"$f\" \"$line\"; author=$line ;;\n+\n+\t\tDate:*) printf \"$f\" \"$line\"; date=$line ;;\n+\n+\t\tauthor\\ *)\n+\t\tline=${line#author }; author=${line% [0-9]*}; date=${line##*> }\n+\t\tif [ \"$f\" ]; then\n+\t\t\tprintf \"$f\" \"Author: $author\"\n+\t\t\t[ \"$quiet\" ] || printf \"$f\" \"Date:   $(showdate $date)\" \n+\t\telif [ \"$summary\" ]; then\n+\t\t\tdate=$(showdate $date)\n+\t\tfi ;;\n+\n+\t\tcommitter\\ *)\n+\t\tif [ \"$verbose\" ]; then\n+\t\t\tline=${line#committer }\n+\t\t\techo \"Committer:  ${line% [0-9]*}\"\n+\t\t\techo \"Commitdate: $(showdate ${line##*> })\"\n+\t\tfi ;;\n+\n+\t\ttree\\ *) [ \"$verbose\" ] && echo \"Tree: ${line#tree }\" ;;\n+\n+\t\tparent\\ *) [ \"$verbose\" ] && echo \"Parent: ${line#parent }\" ;;\n+\n+\t\t\\ \\ \\ \\ * | '')\n+\t\tif (( ++lineno == 2 )); then\n+\t\t\t[ \"$summary\" ] && \n+\t\t\tshowsummary $commit \"$author\" \"$date\" \"$line\"\n+\t\t\t[ \"$filelist\" ] && showfiles $commit $from\n+\t\t\tprintf \"$f\" \"$line\"\n+\t\t\t[ \"$quiet\" ] && { f=; echo; }\n+\t\telse\n+\t\t\tprintf \"$f\" \"$line\"\n+\t\tfi ;;\n+\n+\t\t*)\n+\t\t[ \"$showds\" ] && { showstat $commit $from; showds=; }\n+\t\tprintf \"%s\\n\" \"$line\"\n+\t\twhile IFS='' read -r line; do\n+\t\t\tcase $line in $dtmode\\ *) break ;; esac\n+\t\t\tprintf \"%s\\n\" \"$line\"\n+\t\tdone\n+\t\tcontinue ;;\n+\tesac\n+\tline=\n+\tdone\n+\t[ \"$showds\" ] && showstat $commit $from\n+\t) | LANG=C sed -e \"$sedprog\" | pager\n+else\n+\t# Fast version, using sed only.\n+\tsedprog=\"s,^$dtmode ,Commit: ,;$sedprog\"\n+\tdolog | LANG=C sed -e \"$sedprog\" | pager\n+fi \n"},{"id":"4838","messageId":"20050611000255.GI22111@pasky.ji.cz","threadId":"883","inReplyTo":"42A8280A.3070607@gmail.com","subject":"Re: [PATCH 5/6] Make cg-diff use optparse, and add features","fromName":"Petr Baudis","fromEmail":"pasky@ucw.cz","sentAt":"2005-06-11T00:02:55Z","receivedAt":"2005-06-11T00:02:55Z","isPatch":true,"sender":{"key":"pasky@ucw.cz","avatar":"https://avatars.githubusercontent.com/u/18439?v=4"},"body":"Dear diary, on Thu, Jun 09, 2005 at 01:24:07PM CEST, I got a letter\nwhere Dan Holmsand <holmsand@gmail.com> told me that...\n> This adds some new features to cg-diff:\n\nDear diary, on Thu, Jun 09, 2005 at 01:29:14PM CEST, I got a letter\nwhere Dan Holmsand <holmsand@gmail.com> told me that...\n> This is more or less a rewrite of cg-log, that adds a bunch\n> of new features and gives a substantial speedup.\n\nI'm sorry, but those two patches are still way too big and therefore\nbasically unreviewable. Could you please split them further to a\nper-feature patches?\n\nA good place to start would be changing the [PATCH 2/6] color refactor\nto actually _replace_ the color stuff of cg-log and cg-diff with the new\ncommon cg-Xlib code - but please don't change the default colors in that\npatch yet (that is because you shouldn't assume in your earlier patches\nthat later patches will be applied, or applied in the form you send\nthem; I don't know about the less search thing yet, since I didn't test\nit, since I don't have a focused patch for it).\n\nAlso, if the big changes won't make your further job significantly\neasier, it's probably good idea to first do the smaller changes and then\nthe big ones - I'm more likely to change something, well, big, in the\nbig patches. ;-)\n\nThanks,\n\n-- \n\t\t\t\tPetr \"Pasky\" Baudis\nStuff: http://pasky.or.cz/\n<Espy> be careful, some twit might quote you out of context..\n"},{"id":"4873","messageId":"42AAF54C.3010908@gmail.com","threadId":"883","inReplyTo":"20050611000255.GI22111@pasky.ji.cz","subject":"[PATCH] Rewrite cg-diff colorization, add diffstat and reverse","fromName":"Dan Holmsand","fromEmail":"holmsand@gmail.com","sentAt":"2005-06-11T14:29:32Z","receivedAt":"2005-06-11T14:29:32Z","isPatch":true,"sender":{"key":"holmsand@gmail.com","avatar":"https://gravatar.com/avatar/5c722084bafd85e754a02efad01fe69107eb6f393253c49232c5c9f7faa974df?d=mp&s=160"},"body":"Petr Baudis wrote:\n> Dear diary, on Thu, Jun 09, 2005 at 01:24:07PM CEST, I got a letter\n> where Dan Holmsand <holmsand@gmail.com> told me that...\n> \n> I'm sorry, but those two patches are still way too big and therefore\n> basically unreviewable. Could you please split them further to a\n> per-feature patches?\n> \n> A good place to start would be changing the [PATCH 2/6] color refactor\n> to actually _replace_ the color stuff of cg-log and cg-diff with the new\n> common cg-Xlib code - but please don't change the default colors in that\n> patch yet (that is because you shouldn't assume in your earlier patches\n> that later patches will be applied, or applied in the form you send\n> them; I don't know about the less search thing yet, since I didn't test\n> it, since I don't have a focused patch for it).,\n\nOk, here's another one to start with.\n\ncg-diff only, this time around. It's much simpler, since cg-diff got a \nlot more similar to your stuff due to your excellent optparse-ification.\n\nIf this is still too big, I'm not really sure how to go about this.\n\nAnd I'm gonna get into even more trouble with cg-log. My code is \nessentially a rewrite, in order to reduce the incredible amount of \nforking going on in the current code.\n\nBut let's take it one step at a time...\n\n/dan\n\n---\n\n[PATCH] Rewrite cg-diff colorization, add diffstat and reverse\n\nThis collects all of the colorization logic into the setup_colors\nmethod, in preparation for codesharing with cg-log. In the process, we \ngo from awk to sed.\n\nThis also cleans up option handling, in order to provide better error \nchecking and error messages. And skip the rather unncessary xargs usage.\n\ndiffstat and \"reverse diff\" output options are added. diffstat output \nrequires a small fix of \"pager\" in cg-Xlib, in order not to lose leading \nwhitespace when color isn't used.\n\nSigned-off-by: Dan Holmsand <holmsand@gmail.com>\n\n---\n\n\n cg-Xlib |    2 -\n cg-diff |  166 ++++++++++++++++++++++++++++++---------------------------------\n 2 files changed, 79 insertions(+), 89 deletions(-)\n\ndiff --git a/cg-Xlib b/cg-Xlib\n--- a/cg-Xlib\n+++ b/cg-Xlib\n@@ -24,7 +24,7 @@ usage() {\n pager () {\n \tlocal line\n \t# Invoke pager only if there's any actual output\n-\tif read -r line; then\n+\tif IFS='' read -r line; then\n \t\t( echo \"$line\"; cat; ) | LESS=\"R$LESS\" ${PAGER:-less} $PAGER_FLAGS\n \tfi\n }\ndiff --git a/cg-diff b/cg-diff\n--- a/cg-diff\n+++ b/cg-diff\n@@ -13,6 +13,9 @@\n # -c::\n #\tColorize the diff output\n #\n+# -d, --diffstat::\n+#\tShow `diffstat' output before diff.\n+#\n # -p::\n #\tInstead of one ID denotes a parent commit to the specified ID\n #\t(which must not be a tree, obviously).\n@@ -26,7 +29,10 @@\n #\n # -m::\n #\tBase the diff at the merge base of the -r arguments (defaulting\n-#\tto master and origin).\n+#\tto HEAD and origin).\n+#\n+# -R::\n+#\tOutput diff in reverse.\n #\n # ENVIRONMENT VARIABLES\n # ---------------------\n@@ -37,132 +43,116 @@\n #\tFlags to pass to the pager. By default `R` is added to the `LESS`\n #\tenvironment variable to allow displaying of colorized output.\n \n-USAGE=\"cg-diff [-c] [-m] [-p] [-r FROM_ID[:TO_ID]] [FILE]...\"\n+USAGE=\"cg-diff [-c] [-d] [-m] [-p] [-R] [-r FROM_ID[:TO_ID]] [FILE]...\"\n \n . ${COGITO_LIB}cg-Xlib\n \n \n-id1=\" \"\n-id2=\" \"\n-parent=\n-opt_color=\n-mergebase=\n+unset id1 id2 parent opt_color mergebase diffprog sedprog diffstat difftmp\n+dtargs=()\n \n # TODO: Make cg-log use this too.\n setup_colors()\n {\n-\tlocal 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\"\n+\tlocal C=\"diffhdr=1;36:diffhdradd=1;32:diffadd=32:diffhdrmod=1;35\"\n+\tC=\"$C:diffmod=35:diffhdrrem=1;31:diffrem=31:diffhunk=36:diffctx=34\"\n+\tC=\"$C:files=34:default=0\"\n \t[ -n \"$COGITO_COLORS\" ] && C=\"$C:$COGITO_COLORS\"\n \n \tC=${C//=/=\\'$'\\e'[}\n \tC=col${C//:/m\\'; col}m\\'\n-\t#coldefault=$(tput op)\n \teval $C\n+\n+\tcolor_rules=\"\n+s,^+++.*,$coldiffhdradd&$coldefault,\n+s,^new file mode.*,$coldiffhdradd&$coldefault,\n+s,^---.*,$coldiffhdrrem&$coldefault,\n+s,^deleted file mode.*,$coldiffhdrrem&$coldefault,\n+s,^[+].*,$coldiffadd&$coldefault,\n+s,^[-].*,$coldiffrem&$coldefault,\n+s,^\\\\(@@.*@@\\\\)\\\\(.*\\\\),$coldiffhunk\\\\1$coldiffctx\\\\2$coldefault,\n+s,^\\\\(diff\\\\) .*,$coldiffhdr&$coldefault,\"\n+}\n+\n+show_diffstat() {\n+\t[ -s \"$difftmp\" ] || return\n+\tgit-apply --stat \"$difftmp\"\n+\techo\n+\tcat \"$difftmp\"\n }\n \n while optparse; do\n \tif optparse -c; then\n \t\topt_color=1\n-\t\tsetup_colors\n \telif optparse -p; then\n+\t\t[ \"$mergebase\" ] && optconflict\n \t\tparent=1\n \telif optparse -r=; then\n-\t\tif echo \"$OPTARG\" | grep -q ':'; then\n-\t\t\tid2=$(echo \"$OPTARG\" | cut -d : -f 2)\n-\t\t\t[ \"$id2\" ] || log_end=\"HEAD\"\n-\t\t\tid1=$(echo \"$OPTARG\" | cut -d : -f 1)\n-\t\telif [ \"$id1\" = \" \" ]; then\n-\t\t\tid1=\"$OPTARG\"\n+\t\tif [ ! \"${id1+set}\" ]; then\n+\t\t\tid1=$OPTARG\n+\t\t\tif [[ \"$id1\" == *:* ]]; then\n+\t\t\t\tid2=${id1#*:}\n+\t\t\t\tid1=${id1%:*}\n+\t\t\tfi\n \t\telse\n-\t\t\tid2=\"$OPTARG\"\n+\t\t\t[ \"${id2+set}\" ] && die \"too many revisions\"\n+\t\t\tid2=$OPTARG\n \t\tfi\n \telif optparse -m; then\n+\t\t[ \"$parent\" ] && optconflict\n \t\tmergebase=1\n+\telif optparse -d || optparse --diffstat; then\n+\t\tdiffstat=1\n+\telif optparse -R; then\n+\t\tdtargs[${#dtargs[@]}]=\"-R\"\n \telse\n \t\toptfail\n \tfi\n done\n \n-colorize() {\n-\tif [ \"$opt_color\" ]; then\n-\t\tgawk '\n-\t\t{ if (/^(Index:|diff --git) /)\n-\t\t    print \"'$coldiffhdr'\" $0 \"'$coldefault'\"\n-\t\t  else if (/^======*$/)\n-\t\t    print \"'$coldiffhdr'\" $0 \"'$coldefault'\"\n-\t\t  else if (/^\\+\\+\\+/)\n-\t\t    print \"'$coldiffhdradd'\" $0 \"'$coldefault'\"\n-\t\t  else if (/^\\*\\*\\*/)\n-\t\t    print \"'$coldiffhdrmod'\" $0 \"'$coldefault'\"\n-\t\t  else if (/^---/)\n-\t\t    print \"'$coldiffhdrrem'\" $0 \"'$coldefault'\"\n-\t\t  else if (/^(\\+|new( file)? mode )/)\n-\t\t    print \"'$coldiffadd'\" $0 \"'$coldefault'\"\n-\t\t  else if (/^(-|(deleted file|old) mode )/)\n-\t\t    print \"'$coldiffrem'\" $0 \"'$coldefault'\"\n-\t\t  else if (/^!/)\n-\t\t    print \"'$coldiffmod'\" $0 \"'$coldefault'\"\n-\t\t  else if (/^@@ \\-[0-9]+(,[0-9]+)? \\+[0-9]+(,[0-9]+)? @@/)\n-\t\t    print gensub(/^(@@[^@]*@@)([ \\t]*)(.*)/,\n-\t\t         \"'$coldiffhunk'\" \"\\\\1\" \"'$coldefault'\" \\\n-\t\t\t \"\\\\2\" \\\n-\t\t\t \"'$coldiffctx'\" \"\\\\3\" \"'$coldefault'\", \"\")\n-\t\t  else if (/^\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*/)\n-\t\t    print \"'$coldiffcctx'\" $0 \"'$coldefault'\"\n-\t\t  else\n-\t\t    print\n-\t\t}'\n-\telse\n-\t\tcat\n-\tfi\n-}\n-\n if [ \"$parent\" ]; then\n+\t[ \"${id2+set}\" ] && die \"too many revisions\"\n \tid2=\"$id1\"\n-\tid=\"$id2\"; [ \"$id\" = \" \" ] && id=\"\"\n-\tid1=$(parent-id \"$id\" | head -n 1) || exit 1\n+\tid1=$(parent-id \"$id2\" | head -n 1) || exit 1\n+elif [ \"$mergebase\" ]; then\n+\tid1=\"$(commit-id \"${id1:-HEAD}\")\" || exit 1\n+\tid2=\"$(commit-id \"${id2:-origin}\")\" || exit 1\n+\tid1=\"$(git-merge-base \"$id1\" \"$id2\")\" || exit 1\n+else\n+\tid1=$(tree-id \"$id1\") || exit 1\n fi\n \n-if [ \"$mergebase\" ]; then\n-\t[ \"$id1\" != \" \" ] || id1=\"master\"\n-\t[ \"$id2\" != \" \" ] || id2=\"origin\"\n-\tid1=$(git-merge-base $(commit-id \"$id1\") $(commit-id \"$id2\"))\n-fi\n-\n-\n-filter=$(mktemp -t gitdiff.XXXXXX)\n-for file in \"${ARGS[@]}\"; do\n-\techo \"$file\" >>$filter\n-done\n-\n-if [ \"$id2\" = \" \" ]; then\n-\tif [ \"$id1\" != \" \" ]; then\n-\t\ttree=$(tree-id \"$id1\") || exit 1\n-\telse\n-\t\ttree=$(tree-id) || exit 1\n-\tfi\n+diffprog=git-diff-tree\n \n+if [ ! \"${id2+set}\" ]; then\n \t# Ensure to only diff modified files\n \tgit-update-cache --refresh >/dev/null\n-\n-\t# FIXME: Update ret based on what did we match. And take \"$@\"\n-\t# to account after all.\n-\tret=\n-\tcat $filter | xargs git-diff-cache -r -p $tree | colorize | pager\n-\n-\trm $filter\n-\n-\t[ \"$ret\" ] && die \"no files matched\"\n-\texit $ret\n+\tdiffprog=git-diff-cache\n+else\n+\tid2=$(tree-id \"$id2\") || exit 1\n fi\n \n-\n-id1=$(tree-id \"$id1\") || exit 1\n-id2=$(tree-id \"$id2\") || exit 1\n-\n [ \"$id1\" = \"$id2\" ] && die \"trying to diff $id1 against itself\"\n+diffopts=(-r -p \"${dtargs[@]}\" $id1 $id2 \"${ARGS[@]}\")\n \n-cat $filter | xargs git-diff-tree -r -p $id1 $id2 | colorize | pager\n+if [ \"$diffstat\" ]; then\n+\tdifftmp=$(mktemp -t cgdiff.XXXXXX) || exit 1\n+\ttrap \"rm '$difftmp'\" SIGTERM EXIT\n+\t$diffprog \"${diffopts[@]}\" > $difftmp\n \n-rm $filter\n-exit 0\n+\tdiffprog=show_diffstat\n+\tdiffopts=\n+fi\n+\n+if [ \"$opt_color\" ]; then\n+\tsetup_colors\n+\tsedprog=\"$color_rules\"\n+\n+\t[ \"$diffstat\" ] && sedprog=\"$sedprog\n+s,^\\\\( [^ ].*\\\\)\\\\( |  *[0-9][0-9]* \\\\),$colfiles\\\\1$coldefault\\\\2,\"\n+\n+\t$diffprog \"${diffopts[@]}\" | sed -e \"$sedprog\" | pager\n+\texit $PIPESTATUS\n+else\n+\t$diffprog \"${diffopts[@]}\" | pager\n+fi\n"},{"id":"4889","messageId":"20050612072927.GA6620@pasky.ji.cz","threadId":"883","inReplyTo":"42AAF54C.3010908@gmail.com","subject":"Re: [PATCH] Rewrite cg-diff colorization, add diffstat and reverse","fromName":"Petr Baudis","fromEmail":"pasky@ucw.cz","sentAt":"2005-06-12T07:29:27Z","receivedAt":"2005-06-12T07:29:27Z","isPatch":true,"sender":{"key":"pasky@ucw.cz","avatar":"https://avatars.githubusercontent.com/u/18439?v=4"},"body":"Dear diary, on Sat, Jun 11, 2005 at 04:29:32PM CEST, I got a letter\nwhere Dan Holmsand <holmsand@gmail.com> told me that...\n> Petr Baudis wrote:\n> >Dear diary, on Thu, Jun 09, 2005 at 01:24:07PM CEST, I got a letter\n> >where Dan Holmsand <holmsand@gmail.com> told me that...\n> >\n> >I'm sorry, but those two patches are still way too big and therefore\n> >basically unreviewable. Could you please split them further to a\n> >per-feature patches?\n> >\n> >A good place to start would be changing the [PATCH 2/6] color refactor\n> >to actually _replace_ the color stuff of cg-log and cg-diff with the new\n> >common cg-Xlib code - but please don't change the default colors in that\n> >patch yet (that is because you shouldn't assume in your earlier patches\n> >that later patches will be applied, or applied in the form you send\n> >them; I don't know about the less search thing yet, since I didn't test\n> >it, since I don't have a focused patch for it).,\n> \n> Ok, here's another one to start with.\n> \n> cg-diff only, this time around. It's much simpler, since cg-diff got a \n> lot more similar to your stuff due to your excellent optparse-ification.\n> \n> If this is still too big, I'm not really sure how to go about this.\n\nWell, would it be a problem to first do the color handling separately,\nas I suggested? Also, you bundle in a bunch of non-vital cleanups -\nnothing wrong about them but they are really cluttering the patch up and\nyou could do them separately.\n\n-- \n\t\t\t\tPetr \"Pasky\" Baudis\nStuff: http://pasky.or.cz/\n<Espy> be careful, some twit might quote you out of context..\n"}]}