Skip to content

Instantly share code, notes, and snippets.

@romainl
Last active Jul 17, 2022
Embed
What would you like to do?
git-jump hack that can be used FROM Vim
#!/bin/sh
usage() {
cat <<\EOF
usage: git jump <mode> [<args>]
Jump to interesting elements in an editor.
The <mode> parameter is one of:
diff: elements are diff hunks. Arguments are given to diff.
merge: elements are merge conflicts. Arguments are ignored.
grep: elements are grep hits. Arguments are given to git grep or, if
configured, to the command in `jump.grepCmd`.
ws: elements are whitespace errors. Arguments are given to diff --check.
EOF
}
open_editor() {
if test -t 1; then
editor=`git var GIT_EDITOR`
eval "$editor -q \$1"
else
eval "cat \$1"
fi
}
mode_diff() {
git diff --no-prefix --relative "$@" |
perl -ne '
if (m{^\+\+\+ (.*)}) { $file = $1; next }
defined($file) or next;
if (m/^@@ .*?\+(\d+)/) { $line = $1; next }
defined($line) or next;
if (/^ /) { $line++; next }
if (/^[-+]\s*(.*)/) {
print "$file:$line: $1\n";
$line = undef;
}
'
}
mode_merge() {
git ls-files -u |
perl -pe 's/^.*?\t//' |
sort -u |
while IFS= read fn; do
grep -Hn '^<<<<<<<' "$fn"
done
}
# Grep -n generates nice quickfix-looking lines by itself,
# but let's clean up extra whitespace, so they look better if the
# editor shows them to us in the status bar.
mode_grep() {
cmd=$(git config jump.grepCmd)
test -n "$cmd" || cmd="git grep -n --column"
$cmd "$@" |
perl -pe '
s/[ \t]+/ /g;
s/^ *//;
'
}
mode_ws() {
git diff --check "$@"
}
if test $# -lt 1; then
usage >&2
exit 1
fi
mode=$1; shift
trap 'rm -f "$tmp"' 0 1 2 3 15
tmp=`mktemp -t git-jump.XXXXXX` || exit 1
type "mode_$mode" >/dev/null 2>&1 || { usage >&2; exit 1; }
"mode_$mode" "$@" >"$tmp"
test -s "$tmp" || exit 0
open_editor "$tmp"

Introduction

git-jump is an amazing little script that can be used to start Vim with the quickfix list populated with interesting things:

  1. The beginning of any diff hunks.
  2. The beginning of any merge conflict markers.
  3. Any grep matches, including the column of the first match on a line.
  4. Any whitespace errors detected by git diff --check.

Example usage:

$ git jump diff
$ git jump grep GetUser

But the original script is written from a shell perspective so it always opens Vim and thus can't be used to populate the quickfix list from within Vim. Bummer…

This hack makes it possible to get two behaviors out of git-jump for the price of one.

  • When executed in an interactive context, it starts Vim with the list:

    $ git jump diff
    
  • when executed in a non-interactive context, it outputs the list:

    $ git jump grep foo | grep -v bar
    

Integration

Having the same feature inside and outside of Vim is quite handy but:

:cexpr system('git jump diff')

is a lot to type! Let's turn that into a proper command:

:command! -bar -nargs=* Jump cexpr system('git jump ' . expand(<q-args>))

that we can use easily:

:Jump diff
:Jump merge
:Jump grep foo
@george-b
Copy link

george-b commented May 8, 2020

Rather than clearing GIT_EDITOR when invoking from Vim you could test if stdout is to a terminal or not, e.g.

open_editor() {
	if test -t 1; then
		editor=`git var GIT_EDITOR`
		eval "$editor -q \$1"
	else
		cat $1
	fi
}

This allows for git jump to be used transparently in and outside of Vim.

@romainl
Copy link
Author

romainl commented May 9, 2020

@george-b

This is beautiful, thank you.

@saravanak
Copy link

saravanak commented Jun 4, 2020

This is cool 👍 Learnt a lot from this.

@Gee19
Copy link

Gee19 commented Nov 26, 2021

This is really awesome, I can't seem to get it to work outside of vim though.

When running interactively it seems to load the quickfix items incorrectly (missing filenames) and pressing Enter on an item gives the error E92 Buffer 2 not found:

https://i.imgur.com/KbLJCr3.png

git jump grep vnoremap | cat
vimrc:387:3:" vnoremap <silent> y y`]
vimrc:854:23: autocmd FileType qf vnoremap <buffer> d :'<,'>Reject<CR>
vimrc:856:23: autocmd FileType qf vnoremap <buffer> gk :'<,'>Keep<CR>
vimrc:863:1:vnoremap <leader>/ "ay:call TrimEscapeRegA()<CR>:Grep <C-r>a<CR>

Have you guys had any success?

@romainl
Copy link
Author

romainl commented Nov 26, 2021

@Gee19 I use that script relatively often and it is working perfectly for me.

The first screenshot is the outcome of $ cd ~/.vim && git jump grep 5 (followed by an automatic :cwindow) on my machine:

Capture d’écran 2021-11-26 à 22 32 05

The second screenshot is the outcome of :Jump grep 5 in Vim (again followed by an automatic :cwindow):

Capture d’écran 2021-11-26 à 22 35 58

This is my :Jump command:

command! -bar -nargs=* Jump cexpr system('git jump ' . expand(<q-args>))

@Gee19
Copy link

Gee19 commented Nov 26, 2021

Appreciate the response @romainl, I ended up narrowing it down to an offending plugin (dhruvasagar/vim-prosession#84).

@Gee19
Copy link

Gee19 commented Nov 29, 2021

@romainl Passing on some comments from the author of git-jump if you are interested:

If somebody was inclined to send a patch to the list that made
it work in that mode, I'd be happy to review it. From skimming what was
in that gist, I'd suggest:

  • rather than swapping the editor for "cat", the non-editor mode can
    just write straight to stdout, and avoid even the tempfile

  • it probably should be a command-line option, which gives people an
    escape hatch if they are doing something clever with stdout. I.e.,
    something like --no-editor, --editor, or --editor=auto (where the
    latter checks isatty(1), and could probably become the default).

@george-b
Copy link

george-b commented Nov 29, 2021

Sorry to spam Romain's notifications but I did try this some time ago [1], it was not a pleasant experience.

[1] https://marc.info/?l=git&m=158905177104766&w=2

@romainl
Copy link
Author

romainl commented Nov 29, 2021

@Gee19 @george-b I actually planned to submit a patch (first patch to Git, you can imagine the excitation ;-)) but well… I couldn't even get past the email setup step so I simply gave up. Frankly, the contrib scripts are not even bundled with the Git package I use, so the benefit is not worth the effort for me and I'm happy with the script as it is.

If any of you want to take it further I would gladly support your effort. My initial implementation has been replaced by @george-b's anyway so I can't even claim paternity anymore. Hell, I don't even remember how or when I got the idea of that change in the first place.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment