Skip to content

Commit

Permalink
Improve support for amending git commits (#67)
Browse files Browse the repository at this point in the history
* Optional improved support for amending git commits

Add opt-in feature to allow the diff and status to be extracted from the
commit message template if the verbose option is used with git commit,
e.g. git commit --verbose or git config --global commit.verbose=true

When amending commits, this allows the author of the commit message to
see all of the changes, in both the status and diff windows, if they
enabled the verbose option for git commit. Without the verbose option,
the behaviour is as before, show only new changes, if there are any.

Enable in your vimrc with:

    let g:committia#git#use_verbose = 1

* Improve regex to find git diff start line

Allow for non-default git config core.commentChar, e.g. ;

Supported comment chars extracted from pattern in gitcommit syntax file,
see https://github.com/tpope/vim-git/blob/master/syntax/gitcommit.vim

* Fix regex to extract status from commit template

* Fix inaccurate status when amending commits

When amending commits without g:committia#git#use_verbose enabled but
with the verbose option provided to git commit, committia falls back
to extracting the diff from the commit message template when the git
diff command shows no changes, but this leads to an inaccurate status
as the diff show existing changes, but the status shows no changes.

* Improve code to extract status from commit template

Search backwards from the scissors line to find the start of the status
in case someone copies the same text somewhere into the commit message.

* Minor changes to code to extract verbose status

- Rename status_end variable to scissors_line, more accurate
- Switch single line if to multi line for readability & consistency

* Improve code to clean comments from message window

Use match() with regex to allow for non-default git `core.commentChar`

* Further improve code to extract status from commit

Avoid moving the cursor by simply searching backwards for pattern match

* Revert "Further improve code to extract status from commit"

Will do this by iterating in reverse from scissors line instead, safer

This reverts commit 6b91ce8.

* Further improve code to extract status from commit

Avoid moving cursor by iterating in reverse from scissors line

This is safer than simply searching backwards as it allows for weirdness
like someone managing to put exactly the start of the status in code
that gets shown in the diff (yeah, I know, unlikely, but you never know)

* Update README, document new use_verbose option

* Safer code to clean comments from message window

Do not assume any lines starting with common comment chars is a comment,
actually get the comment char from the first line and then match that.

Note: I did initially use `s:execute_git('config core.commentChar)`, and
it worked fine, but concluded it was unnecessary, at least for now.

* Respect git comment char in status window

Prefix lines in status with git comment char rather than #

For most people, who accept the default comment char of #, this makes
absolutely no difference. However, if you do set core.commentChar to
something other than the default, this change means that character is
now consistently used in the status window, whether it is populated by
by running `git status` or extracted from the verbose commit message.

Technical note: this assumes that the last line in the message before
the diff is a comment, which it should be unless the --no-status option
is used, in which case it falls back to using '#' rather than execute a
git command. The alternative would be to run `git config core.commentChar`

* Improve regex conditional statements, match case

Replace match() with case sensitive regex comparison operators

Apply same change to all regex conditional statements for consistency
(even ones which currently do not match any alphabetical characters)

Co-authored-by: Linda_pp <rhysd@users.noreply.github.com>

* Fix for regex special chars as comment char

Revert to using stridx() to match comment char, which avoids issues with
regex special chars being used as comment char, e.g. $. The regex match
is no longer needed as we are again matching on a single comment char.

Co-authored-by: Linda_pp <rhysd@users.noreply.github.com>

---------

Co-authored-by: Linda_pp <rhysd@users.noreply.github.com>
  • Loading branch information
mmrwoods and rhysd authored Nov 3, 2024
1 parent 8a9a47d commit a9e604d
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 3 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,12 @@ If committia.vim is in multi-columns mode, specifies the width of the edit windo

Minimum height of a status window.

### `g:committia#git#use_verbose` (default: `0`)

If the value is `1`, extract the diff and status from `COMMIT_EDITMSG` when the
`verbose` option is used with `git commit`, e.g. `git commit --verbose` or `git
config --global commit.verbose=true`.

## Future

- Cooperate with [vim-fugitive](https://github.com/tpope/vim-fugitive).
Expand Down
57 changes: 54 additions & 3 deletions autoload/committia/git.vim
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ let g:committia#git#cmd = get(g:, 'committia#git#cmd', 'git')
let g:committia#git#diff_cmd = get(g:, 'committia#git#diff_cmd', 'diff -u --cached --no-color --no-ext-diff')
let g:committia#git#status_cmd = get(g:, 'committia#git#status_cmd', '-c color.status=false status -b')

" Experimental: extract diff and status from commit message template when
" using git commit --verbose, particularly useful when amending commits
let g:committia#git#use_verbose = get(g:, 'committia#git#use_verbose', 0)

try
silent call vimproc#version()

Expand Down Expand Up @@ -159,6 +163,13 @@ function! s:unset_index_file() abort
endfunction

function! committia#git#diff() abort
if g:committia#git#use_verbose
let line = s:diff_start_line()
if line > 0
return getline(line, '$')
endif
endif

let diff = s:execute_git(g:committia#git#diff_cmd)

if diff !=# ''
Expand All @@ -170,22 +181,62 @@ function! committia#git#diff() abort
return ['']
endif

" Fugly hack to tell committia#git#status() to get the status from the
" commit message template too, otherwise status may not match with diff
" Could be removed if g:committia#git#use_verbose was enabled by default
let s:use_verbose_status = 1

return getline(line, '$')
endfunction

function! s:diff_start_line() abort
let re_start_diff_line = '# -\+ >8 -\+\n\%(#.*\n\)\+diff --git'
let re_start_diff_line = '^[#;@!$%^&|:] -\+ >8 -\+\n\%([#;@!$%^&|:].*\n\)\+diff --git'
return search(re_start_diff_line, 'cenW')
endfunction

function! s:comment_char() abort
let line = s:diff_start_line()
if line == 0
let line = line('$') + 1
endif
if getline(line - 1) =~# '^[#;@!$%^&|:]'
return getline(line - 1)[0]
else
return '#'
endif
endfunction

function! committia#git#status() abort
try
let status = s:execute_git(g:committia#git#status_cmd)
catch /^committia: git: Failed to retrieve git-dir or work-tree/
" Leave status window empty when git-dir or work-tree not found
return ''
endtry
return map(split(status, '\n'), 'substitute(v:val, "^", "# ", "g")')
if g:committia#git#use_verbose || exists('s:use_verbose_status')
if exists('s:use_verbose_status')
unlet s:use_verbose_status
end
let scissors_line = search('^[#;@!$%^&|:] -\+ >8 -\+\n', 'cenW')
if scissors_line > 1
" Localisation hack, find the start of the status in the commit
" message template using the first line of output from `git status`
" Search backwards to avoid match in message, and start search at
" scissors line to avoid potential match in diff, unlikely, but...
let status_start = scissors_line
let re_status_start = '^[#;@!$%^&|:] ' . split(status, '\n')[0]
while status_start > 1
if getline(status_start - 1) =~# re_status_start
break
endif
let status_start -= 1
endwhile
if status_start > 1 && status_start < scissors_line
return getline(status_start, scissors_line-1)
endif
endif
endif
return map(split(status, '\n'), 'substitute(v:val, "^", s:comment_char() . " ", "g")')
endfunction

function! committia#git#end_of_edit_region_line() abort
Expand All @@ -197,7 +248,7 @@ function! committia#git#end_of_edit_region_line() abort
let line = line('$') + 1
endif
while line > 1
if stridx(getline(line - 1), '#') != 0
if stridx(getline(line - 1), s:comment_char()) != 0
break
endif
let line -= 1
Expand Down

0 comments on commit a9e604d

Please sign in to comment.