9

Using git how can I load the list of files in the current commit into Quickfix window?

Code Review didn't pass and I want to just fix them one by one.

7

You could use the following function which has the advantage of not changing the state of your local git repo:

function! CommitQF(...)
    " Get the commit hash if it was specified
    let commit = a:0 == 0 ? '' : a:1

    " Get the result of git show in a list
    let flist = system('git show --name-only ' . commit . ' | tail -n +7')
    let flist = split(flist, '\n')

    " Create the dictionnaries used to populate the quickfix list
    let list = []
    for f in flist
        let dic = {'filename': f, "lnum": 1}
        call add(list, dic)
    endfor

    " Populate the qf list
    call setqflist(list)
endfunction

You can call it without argument or with a commit hash as argument (if no argument is specified, the last commit will be used):

call CommitQF("cc5b6a976fde8c9ee66387c91765feb202ba0124")
call CommitQF()

It will use the git show command with the --name-only argument to get the name of the files in the commit. The tail -n +7 command removes the lines which are not a file name in the output of git show.

Then it will create a list of dictionnaries used to populate the quickfix list (see :h setqflist() for more details about that).

You will need to use :copen to open the quicklist after calling the function (or you can add it to the function)


Edit Another possible approach (and probably a better one) using only built in features would be to use these settings:

set makeprg=git\ show\ --name-only\ $*\ \\\|\ tail\ -n\ +7
set efm+=%f

This way you can use :make (for the current commit) or :make fff93a09 (to specify a commit).

The quickfix window will then contain the list of files and you'll be able to navigate to them.

(Of course you might want to create a mapping of something to switch between your usual makeprg and this one)

You can have a look at these help topics:


Second edit @PeterRincker and @LucHermitte made some really nice suggestions in the comments, many thanks to them.

You could use the following one liner:

command -nargs=? -bar Gshow call setqflist(map(systemlist("git show --pretty='' --name-only <args>"), '{"filename": v:val, "lnum": 1}'))

To create a command :Gshow which takes a commit hash as an optionnal argument and populate the quickfix list.

| improve this answer | |
  • Can also use git show --pretty='' --name-only <revision> to remove the commit message information and only show filenames – Peter Rincker Aug 31 '17 at 17:03
  • I would have used map(copy(flist), '{"filename": v:val, "lnum": 1}') instead of the :for loop -- it's always faster, but also much less annoying to debug. – Luc Hermitte Aug 31 '17 at 17:10
  • With @LucHermitte suggestions this could easily be a one liner: command -nargs=? -bar Gshow call setqflist(map(systemlist("git show --pretty='' --name-only <args>"), '{"filename": v:val, "lnum": 1}')) – Peter Rincker Aug 31 '17 at 20:46
  • Thank you both for your suggestion it is indeed much more efficient than my original answer. – statox Sep 1 '17 at 7:36
  • Is there a plug-in that does that? – firedev Sep 1 '17 at 15:05
5

Here are two related approaches. Both use git-* scripts; if you put them on your PATH, you can invoke them as if they were subcommands of git.

Use the contrib script git-jump

The git-jump script provides a handy way to load diff hunks, grep results, or whitespace errors into an editor that supports quickfix formats.

Since git jump diff forwards its arguments to diff, and since you’re interested in the last commit, you can do

git jump diff @^ @

Use my git-ed script

My git-ed script was written before I knew about git-jump. I used it a lot then (less now). It’s only purpose is to open in an editor the changed files (either currently, in a single commit, or in a range of commits). So simply invoke

git ed @

And use the argument list.

| improve this answer | |
  • Installation note: You can't add /usr/share/doc/git/contrib/git-jump to your path because git doesn't ship git-jump as executable. You'll have to copy git-jump into your own bin directory. – idbrii May 12 at 8:45
  • @idbrii For me the path was /usr/local/share/git-core/contrib/git-jump, which does ship executable (or at least, for me it did). If you're under doc, you may not have the right path? The script in the repo has executable permissions, so unless whatever you used to install git removed them, it should retain them – D. Ben Knoble May 12 at 12:46
3

The only thing that comes to mind is

git reset --soft head~1

That would "uncommit" the files, and then browse them in Preview window using vim-fugitive :Gtatus command. Not exactly Quickfix, but it's readable, stores your position and you can stage/unstage from there.

| improve this answer | |
3

Add this to your .vimrc file and you're all set up. This adds files from git diff to the quick fix list:

let s:git_status_dictionary = {
            \ "A": "Added",
            \ "B": "Broken",
            \ "C": "Copied",
            \ "D": "Deleted",
            \ "M": "Modified",
            \ "R": "Renamed",
            \ "T": "Changed",
            \ "U": "Unmerged",
            \ "X": "Unknown"
            \ }
function! s:get_diff_files()
  let list = map(split(system(
              \ 'git diff --name-status HEAD'), '\n'),
              \ '{"filename":matchstr(v:val, "\\S\\+$"),"text":s:git_status_dictionary[matchstr(v:val, "^\\w")]}'
              \ )
  call setqflist(list)
  copen
endfunction

command! -nargs=0 DiffRev call s:get_diff_files()

Credits: slightly edited

| improve this answer | |
1

Plugin Fugitive v3.2 (Jan 2020)

vim-fugitive has added new commands sine the OP raised this question which can do exactly what the OP has asked.

Now you can call

:Git difftool

or the abbreviated version

:G difft[tab complete]

This loads all changesets into the quickfix list. If files have several changes, they occur several times in the quickfix list. I like this. However, if you prefer to have only each file once, run

 :G difftool --name-only
| improve this answer | |
0

Fastest way to add diff hunks to quickfix

Quickfix is very handy feature. It is even has it's own format definition, but it is not easy expand. I found more easy to reformat diff to format of compilation errors: "$file:$line:$message". Reformatting works as standalone filter function. It combination with vim option -q it very easy to use and resuse, no need to additional installations and configutaions:

Here is my the shortest solution.

diff_to_quickfix()
{
     perl -ne '/^\+\+\+ (.+)/ && { $f="$1"};/@@.*\+(\d+)/ && print "$f:$1:$_\n"'
}

vim -q <(git show -U0 --no-prefix | diff_to_quickfix)


vim -q <(git diff -U0 --no-prefix | diff_to_quickfix)
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.