Raw
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"