Atomic commits in kakoune?

When working with git, I always try to keep my commits as atomic as possible. This means I rely heavily on tig to make stage changes in hunks/lines instead of staging entire files at a time. Is there a way to achieve this in Kakoune? I feel like it would be really intuitive when paired with Kakounes selection model. Thanks.

1 Like

I use git add -p for this but I agree its quite sub-optimal, I’d be keen to see some experiment in a more advanced git integration plugin.

I would also like to be able to easily stage hunks/selections in Kakoune, since this is one of the few reasons I still use Vim with vim-fugitive’s :Gvdiff. Not sure if it would be easy using a unified diff view like :git show-diff, or if a side-by-side diff view is required.

Yeah git add -p is great, I wish I could use it within kakoune. If I’m exiting back to the command line I’ll just fire up tig for the added ergonomics.

Not sure if it solves your problem, but this plugin might be useful https://github.com/chambln/kakoune-kit

These commands can be used in a diff buffer, as created by :git diff, :git diff --cached or :git show and friends.

They will stage/unstage the entire hunk around the cursor.

Staging selected lines can be added as well. Maybe the commands should check if all selected lines end in \n. If yes, use the selected lines, otherwise the entire hunk.

I can put these in a plugin, but I’m not yet sure what the scope of this plugin should be.

define-command git-hunk-stage %{
	git-hunk-apply-impl "--cached"
} -docstring "Stage hunk at cursor"
define-command git-hunk-unstage %{
	git-hunk-apply-impl "--cached --reverse"
} -docstring "Unstage hunk at cursor"
define-command git-hunk-apply %{
	git-hunk-apply-impl ""
} -docstring "Apply hunk at cursor"
define-command git-hunk-revert %{
	git-hunk-apply-impl "--reverse"
} -docstring "Revert hunk at cursor"

define-command -hidden git-hunk-apply-impl -params 1 %{ evaluate-commands -draft -save-regs ah| %{
	set-register a %arg{1}
	# Save the diff header to register h.
	execute-keys -draft '<space><a-/>^diff.*?\n(?=@@)<ret><a-x>"hy'
	# Select the current hunk.
	execute-keys ?^@@|^diff|^$<ret>K<a-x><semicolon><a-?>^@@<ret><a-x>
	set-register | %{
		( printf %s "$kak_reg_h"; cat ) |
		git apply $kak_reg_a --whitespace=nowarn -
	}
	execute-keys |<ret>
}}
1 Like

@krobelus How does this work?

It pipes a single hunk to git apply. The effective command will look like this:

printf %s 'diff --git a/README.asciidoc b/README.asciidoc
index whatever..whatever 100644
--- a/README.asciidoc
+++ b/README.asciidoc
@@ -25,3 +25,3 @@ git clone https://github.com/mawww/kakoune.git
 cd kakoune/src
-make
+make debug=yes
 ./kak
' | git apply

The first “execute-keys” selects the diff header (git apply needs this for the filename).
The second “execute-keys” selects the diff hunk around the cursor. (Each @@ starts a new diff hunk.)
The value of the special register | will be used as shell command if no other command is given (like in “execute-keys |”). The same technique is used in Kakoune’s rc/tools/format.kak. Since we have selected the current hunk, it will be piped to “cat”. This always deletes the applied hunk, so the pipe is not really necessary now, but it may be when staging individual lines.

Thank you for the explanation. I suppose it could go into the main kakoune repo since it is quite small and plays along with the git commands that are already there.