| 1 | #!/bin/sh |
| 2 | |
| 3 | usage() { |
| 4 | cat <<\EOF |
| 5 | usage: git jump [--stdout] <mode> [<args>] |
| 6 | or: git jump [--stdout] |
| 7 | |
| 8 | Jump to interesting elements in an editor. |
| 9 | The <mode> parameter is one of the following. |
| 10 | With no <mode> and no <args>, it defaults to "auto". |
| 11 | |
| 12 | diff: elements are diff hunks. Arguments are given to diff. |
| 13 | |
| 14 | merge: elements are merge conflicts. Arguments are given to ls-files -u. |
| 15 | |
| 16 | grep: elements are grep hits. Arguments are given to git grep or, if |
| 17 | configured, to the command in `jump.grepCmd`. |
| 18 | |
| 19 | ws: elements are whitespace errors. Arguments are given to diff --check. |
| 20 | |
| 21 | auto: select one of the other modes based on worktree state; |
| 22 | "merge" if there are unmerged paths, "diff" if there are |
| 23 | unstaged changes, "ws" if there are whitespace errors. |
| 24 | |
| 25 | If the optional argument `--stdout` is given, print the quickfix |
| 26 | lines to standard output instead of feeding it to the editor. |
| 27 | EOF |
| 28 | } |
| 29 | |
| 30 | open_editor() { |
| 31 | editor=`git var GIT_EDITOR` |
| 32 | case "$editor" in |
| 33 | *emacs*) |
| 34 | # Supported editor values are: |
| 35 | # - emacs |
| 36 | # - emacsclient |
| 37 | # - emacsclient -t |
| 38 | # |
| 39 | # Wait for completion of the asynchronously executed process |
| 40 | # to avoid race conditions in case of "emacsclient". |
| 41 | eval "$editor --eval \"(let ((buf (grep \\\"cat \$1\\\"))) (pop-to-buffer buf) (select-frame-set-input-focus (selected-frame)) (while (get-buffer-process buf) (sleep-for 0.1)))\"" |
| 42 | ;; |
| 43 | *) |
| 44 | # assume anything else is vi-compatible |
| 45 | eval "$editor -q \$1" |
| 46 | ;; |
| 47 | esac |
| 48 | } |
| 49 | |
| 50 | mode_diff() { |
| 51 | git diff --no-prefix --relative "$@" | |
| 52 | perl -ne ' |
| 53 | if (m{^\+\+\+ (.*?)\t?$}) { $file = $1 eq "/dev/null" ? undef : $1; next } |
| 54 | defined($file) or next; |
| 55 | if (m/^@@ .*?\+(\d+)/) { $line = $1; next } |
| 56 | defined($line) or next; |
| 57 | if (/^ /) { $line++; next } |
| 58 | if (/^[-+]\s*(.*)/) { |
| 59 | print "$file:$line:1: $1\n"; |
| 60 | $line = undef; |
| 61 | } |
| 62 | ' |
| 63 | } |
| 64 | |
| 65 | mode_merge() { |
| 66 | git ls-files -u "$@" | |
| 67 | perl -pe 's/^.*?\t//' | |
| 68 | sort -u | |
| 69 | while IFS= read fn; do |
| 70 | grep -Hn '^<<<<<<<' "$fn" |
| 71 | done |
| 72 | } |
| 73 | |
| 74 | # Grep -n generates nice quickfix-looking lines by itself, |
| 75 | # but let's clean up extra whitespace, so they look better if the |
| 76 | # editor shows them to us in the status bar. |
| 77 | mode_grep() { |
| 78 | cmd=$(git config jump.grepCmd) |
| 79 | test -n "$cmd" || cmd="git grep -n --column" |
| 80 | $cmd "$@" | |
| 81 | perl -pe ' |
| 82 | s/[ \t]+/ /g; |
| 83 | s/^ *//; |
| 84 | ' |
| 85 | } |
| 86 | |
| 87 | mode_ws() { |
| 88 | git diff --check "$@" |
| 89 | } |
| 90 | |
| 91 | mode_auto() { |
| 92 | if test "$(git rev-parse --is-inside-work-tree 2>/dev/null)" != "true"; then |
| 93 | usage >&2 |
| 94 | exit 1 |
| 95 | fi |
| 96 | if test -n "$(git ls-files -u "$@")"; then |
| 97 | mode_merge "$@" |
| 98 | elif ! git diff --quiet "$@"; then |
| 99 | mode_diff "$@" |
| 100 | else |
| 101 | usage >&2 |
| 102 | exit 1 |
| 103 | fi |
| 104 | } |
| 105 | |
| 106 | use_stdout= |
| 107 | while test $# -gt 0; do |
| 108 | case "$1" in |
| 109 | --stdout) |
| 110 | use_stdout=t |
| 111 | ;; |
| 112 | --*) |
| 113 | usage >&2 |
| 114 | exit 1 |
| 115 | ;; |
| 116 | *) |
| 117 | break |
| 118 | ;; |
| 119 | esac |
| 120 | shift |
| 121 | done |
| 122 | if test $# -lt 1; then |
| 123 | set -- auto |
| 124 | fi |
| 125 | mode=$1; shift |
| 126 | type "mode_$mode" >/dev/null 2>&1 || { usage >&2; exit 1; } |
| 127 | |
| 128 | if test "$use_stdout" = "t"; then |
| 129 | "mode_$mode" "$@" |
| 130 | exit 0 |
| 131 | fi |
| 132 | |
| 133 | trap 'rm -f "$tmp"' 0 1 2 3 15 |
| 134 | tmp=`mktemp -t git-jump.XXXXXX` || exit 1 |
| 135 | "mode_$mode" "$@" >"$tmp" |
| 136 | test -s "$tmp" || exit 0 |
| 137 | open_editor "$tmp" |