Last active
December 28, 2025 18:46
-
-
Save Konfekt/429eb0f4d9a17e31235f49c4716ffddb to your computer and use it in GitHub Desktop.
A command using FZF that takes a range to list only those files altered in last <range> commits
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| " A command using FZF to list all files in a Git repo; | |
| " takes a range 0, N or N,M to restrict to files that have been, respectively, | |
| " altered, altered in last N commits, altered in-between last N and M commits. | |
| " From https://github.com/junegunn/fzf.vim/blob/879db51d0965515cdaef9b7f6bdeb91c65d2829e/autoload/fzf/vim.vim#L742 | |
| function! s:get_git_root(dir) | |
| if empty(a:dir) && exists('*FugitiveWorkTree') | |
| return FugitiveWorkTree() | |
| endif | |
| let dir = len(a:dir) ? a:dir : substitute(split(expand('%:p:h'), '[/\\]\.git\([/\\]\|$\)')[0], '^fugitive://', '', '') | |
| let root = systemlist('git -C ' . shellescape(dir) . ' rev-parse --show-toplevel')[0] | |
| return v:shell_error ? '' : (len(a:dir) ? fnamemodify(a:dir, ':p') : root) | |
| endfunction | |
| " Returns the path to bash executable, or empty string if not found. | |
| function! s:bash() abort | |
| if has('win32') | |
| " On Windows, prefer Git Bash or WSL bash. | |
| let git_bash = $ProgramFiles . '\Git\bin\bash.exe' | |
| if filereadable(git_bash) | return git_bash | endif | |
| if executable('bash') | return 'bash' | endif | |
| return '' | |
| else | |
| " On Unix, just use 'bash' if available. | |
| return executable('bash') ? 'bash' : '' | |
| endif | |
| endfunction | |
| " Escapes a path for safe usage in bash -c "...". | |
| function! s:bash_quote(str) abort | |
| return "'" . substitute(a:str, "'", "'\"'\"'", 'g') . "'" | |
| endfunction | |
| function! s:escape_for_bash(path) abort | |
| if has('win32') | |
| let p = a:path | |
| let p = substitute(p, '^\(\a\):\\', '/mnt/\L\1/', '') | |
| let p = substitute(p, '\\', '/', 'g') | |
| return s:bash_quote(p) | |
| endif | |
| return s:bash_quote(a:path) | |
| endfunction | |
| function! s:GitFiles(bang, range, count, args) abort range | |
| let root = s:get_git_root('') | |
| " No explicit range means original behavior. | |
| if a:range == 0 | |
| if empty(root) | |
| return fzf#run(fzf#wrap('FZF')) | |
| endif | |
| return fzf#vim#gitfiles(a:args, fzf#vim#with_preview({}), a:bang) | |
| endif | |
| " Any explicit range requires a git worktree. | |
| if empty(root) | |
| echom getcwd() . ' is not in a git repository' | |
| return 0 | |
| endif | |
| " Single-item range :0GitFiles means :GFiles? behavior. | |
| if a:range == 1 && a:count == 0 | |
| return fzf#vim#gitfiles('?', fzf#vim#with_preview({ 'placeholder': '' }), a:bang) | |
| endif | |
| let n = a:firstline | |
| let m = a:lastline | |
| if n < 1 || m < 1 | |
| echom 'GitFiles range values must be >= 1' | |
| return 0 | |
| endif | |
| let filter = has('win32') ? 'findstr /v "^$"' : 'sed -e /^$/d' | |
| " Compute revision endpoints. | |
| if a:range == 1 | |
| let rev_from = 'HEAD~' . n | |
| let rev_to = 'HEAD' | |
| let source = 'git log HEAD --max-count=' . n . ' --diff-filter=MA --name-only --pretty=format: | ' . filter | |
| else | |
| " Ex ranges generally enforce n <= m, but keep swapping for robustness. | |
| if n > m | |
| let [n, m] = [m, n] | |
| endif | |
| let rev_from = (m == 0 ? 'HEAD' : 'HEAD~' . m) | |
| let rev_to = (n == 0 ? 'HEAD' : 'HEAD~' . n) | |
| let source = 'git log ' . rev_from . '..' . rev_to . ' --diff-filter=MA --name-only --pretty=format: | ' . filter | |
| endif | |
| let prompt = 'GitFiles(' . rev_from . '..' . rev_to . ')> ' | |
| let opts = ['--scheme', 'path', '--prompt', prompt] | |
| " Preview uses bash for consistent quoting and optional delta rendering. | |
| if executable('git') && executable(s:bash()) | |
| let bar = has('win32') ? '^|' : '|' | |
| let diff_prefix = 'git -C ' . s:escape_for_bash(root) . ' ' | |
| if executable('delta') | |
| let diff = | |
| \ diff_prefix | |
| \ . 'diff --color=always ' . rev_from . '..' . rev_to . ' -- {-1} ' | |
| \ . bar . ' delta --width $FZF_PREVIEW_COLUMNS --file-style=omit ' | |
| \ . bar . ' sed 1d' | |
| else | |
| let diff = | |
| \ diff_prefix | |
| \ . 'diff --color=always ' . rev_from . '..' . rev_to . ' -- {-1} ' | |
| \ . bar . ' sed 1,4d' | |
| endif | |
| let preview_cmd = s:bash() . ' -c "' . diff . '"' | |
| call extend(opts, ['--preview', preview_cmd, '--preview-window', 'right,50%,<70(up,40%)']) | |
| endif | |
| return fzf#run(fzf#wrap('gitfiles', { | |
| \ 'source': source, | |
| \ 'dir': root, | |
| \ 'options': opts, | |
| \ })) | |
| endfunction | |
| command! -bang -range=0 -nargs=? GitFiles <line1>,<line2>call s:GitFiles(<bang>0, <range>, <count>, <q-args>) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment