summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/pages/changelog.asciidoc4
-rwxr-xr-xrc/filetype/diff-parse.pl8
-rw-r--r--rc/tools/git.kak125
-rwxr-xr-xrc/tools/patch-range.pl20
-rw-r--r--src/main.cc1
5 files changed, 140 insertions, 18 deletions
diff --git a/doc/pages/changelog.asciidoc b/doc/pages/changelog.asciidoc
index 3a6804e5..f443f207 100644
--- a/doc/pages/changelog.asciidoc
+++ b/doc/pages/changelog.asciidoc
@@ -5,8 +5,10 @@ released versions.
== Development version
-* Expose env vars that are mentionned in the arguments passed to shell expansions
+* Expose env vars that are mentioned in the arguments passed to shell expansions
* Support for colored double underlines
+* `git apply` can now operate on selected changes in the current buffer's
+ file (useful for quick (un)staging and reverting)
== Kakoune 2024.05.18
diff --git a/rc/filetype/diff-parse.pl b/rc/filetype/diff-parse.pl
index 7453b2b3..5da4e349 100755
--- a/rc/filetype/diff-parse.pl
+++ b/rc/filetype/diff-parse.pl
@@ -45,13 +45,15 @@ our $version = "+";
eval $begin if defined $begin;
-$in_file = "$directory/$in_file" if defined $in_file;
+$in_file = "$directory/$in_file" if defined $in_file && $in_file ne "";
# Outputs
our $diff_line = 0;
our $commit;
our $file;
our $file_line;
+our $other_file;
+our $other_file_line;
our $diff_line_text;
my $other_version;
@@ -63,8 +65,6 @@ if ($version eq "+") {
my $is_recursive_diff = 0;
my $state = "header";
my $fallback_file;
-my $other_file;
-my $other_file_line;
sub strip {
my $is_recursive_diff = shift;
@@ -127,7 +127,7 @@ while (<STDIN>) {
$other_file_line++ if defined $other_file_line;
}
}
- if (defined $in_file and defined $file and $file eq $in_file) {
+ if (defined $in_file and defined $file and ($in_file eq "" or $file eq $in_file)) {
if (defined $in_file_line and defined $file_line and $file_line >= $in_file_line) {
last;
}
diff --git a/rc/tools/git.kak b/rc/tools/git.kak
index 0d837c8d..b98974d1 100644
--- a/rc/tools/git.kak
+++ b/rc/tools/git.kak
@@ -86,7 +86,8 @@ define-command -params 1.. \
All the optional arguments are forwarded to the git utility
Available commands:
add
- apply - alias for "patch git apply"
+ apply - run "patch git apply [<arguments>]"; if buffile is
+ tracked, use the changes to selected lines instead
blame - toggle blame annotations
blame-jump - show the commit that added the line at cursor
checkout
@@ -192,13 +193,15 @@ define-command -params 1.. \
diff_buffer_against_rev() {
rev=$1 # empty means index
shift
- buffile_relative=${kak_buffile#"$PWD/"}
+ buffile_relative=${kak_buffile#"$(git rev-parse --show-toplevel)/"}
echo >${kak_command_fifo} "evaluate-commands -save-regs | %{
set-register | %{ cat >${kak_response_fifo} }
execute-keys -client ${kak_client} -draft %{%<a-|><ret>}
}"
- git show "$rev:./${buffile_relative}" |
- git diff --no-index - ${kak_response_fifo} "$@"
+ git show "$rev:${buffile_relative}" |
+ diff - ${kak_response_fifo} "$@" |
+ sed -e "1c--- a/$buffile_relative" \
+ -e "2c+++ b/$buffile_relative"
}
blame_toggle() {
@@ -435,7 +438,7 @@ define-command -params 1.. \
}
}
}
- print "set-option buffer git_diff_flags $flags"
+ print "set-option buffer git_diff_flags $flags\n"
' )
}
@@ -728,12 +731,118 @@ define-command -params 1.. \
')"
}
- case "$1" in
- apply)
- shift
+ apply_selections() {
+ if [ -z "$(cd_bufdir >/dev/null 2>&1; git ls-files -- ":(literal)${kak_buffile}")" ]; then {
enquoted="$(printf '"%s" ' "$@")"
echo "require-module patch"
echo "patch git apply $enquoted"
+ return
+ } fi
+ base_rev=HEAD
+ index_only=false
+ index=false
+ reverse=false
+ for arg; do
+ case "$arg" in
+ (--cached) index_only=true ; base_rev= ;;
+ (--index) index=true ;;
+ (--reverse|-R) reverse=true ;;
+ esac
+ done
+ if ! $reverse && ! $index_only; then
+ echo "fail %{git apply on buffer contents doesn't make sense without --reverse or --cached}"
+ exit
+ fi
+ cd_bufdir
+ num_inserted=0
+ num_deleted=0
+ for selection_desc in $kak_selections_desc; do {
+ IFS=' .,' read anchor_line _ cursor_line _ <<-EOF
+ $selection_desc
+ EOF
+ if [ $anchor_line -lt $cursor_line ]; then
+ min_line=$anchor_line
+ max_line=$cursor_line
+ else
+ min_line=$cursor_line
+ max_line=$anchor_line
+ fi
+ intended_diff='diff_buffer_against_rev "$base_rev" -u'
+ if $index; then {
+ git update-index --refresh "${kak_buffile}" >/dev/null
+ intended_diff='git diff --no-ext-diff HEAD -- ":(literal)${kak_buffile}"'
+ } elif $index_only && $reverse; then {
+ diff=$(eval "$intended_diff")
+ if [ -n "$diff" ]; then {
+ # Convert from buffile lines to index lines.
+ for line in min_line max_line; do {
+ if ! index_line_or_error_message=$(
+ eval file_line=\$$line
+ printf %s "$diff" |
+ perl "${kak_runtime}/rc/filetype/diff-parse.pl" \
+ BEGIN '
+ $in_file = ""; # no need to check filename, there is only one
+ $in_file_line = '"$file_line"';
+ ' END '
+ $other_file_line++ if $diff_line_text =~ m{^\+};
+ $other_file_line += $in_file_line - $file_line;
+ print "$other_file_line\n";
+ '
+ ); then
+ echo fail "git apply: $index_line_or_error_message"
+ exit
+ fi
+ eval $line=$index_line_or_error_message
+ } done
+ } fi
+ intended_diff='git diff --no-ext-diff --cached -- ":(literal)${kak_buffile}"'
+ } fi
+ diff=$(eval "$intended_diff" |
+ perl "${kak_runtime}"/rc/tools/patch-range.pl -line-numbers-from-new-file \
+ $min_line $max_line sh -c cat -- "$@" # forward any --reverse arg
+ printf .) # avoid stripping newline
+ diff=${diff%.}
+ if ! printf %s "$diff" | git apply "$@"; then
+ printf >&2 "git apply: error running:\n\$ git apply %s << EOF\n" "$*"
+ printf >&2 %s "$diff"
+ printf >&2 'EOF\n'
+ echo "fail 'git apply: failed to apply selections, see *debug* buffer'"
+ exit
+ fi
+ count() {
+ printf %s "$diff" | awk '
+ BEGIN { n = 0 }
+ /^@@/,/^$/ { if ($0 ~ /^'"$1"'/) { n++ } }
+ END { print n }'
+ }
+ num_inserted=$(( $num_inserted + $(count +) ))
+ num_deleted=$(( $num_deleted + $(count -) ))
+ } done
+ if ! $index_only && ! $kak_modified; then
+ echo edit!
+ echo git update-diff
+ else
+ update_diff
+ fi
+ msg=
+ case $index_only,$reverse,$index in
+ (true,false,*) msg=Staged ;;
+ (true,true,*) msg=Unstaged ;;
+ (false,true,false) msg=Reverted ;;
+ (false,true,true) msg='Unstaged and reverted' ;;
+ esac
+ case $num_inserted,$num_deleted in
+ (*,0) msg="$msg $num_inserted inserted line(s)";;
+ (0,*) msg="$msg $num_deleted deleted line(s)";;
+ (*,*) msg="$msg $num_inserted inserted and $num_deleted deleted lines";;
+ esac
+ echo "echo -markup '{Information}{\\}$msg'"
+ }
+
+ case "$1" in
+ apply)
+ shift
+ apply_selections "$@"
;;
show|show-branch|log|diff|status)
show_git_cmd_output "$@"
diff --git a/rc/tools/patch-range.pl b/rc/tools/patch-range.pl
index 2d54ab26..01611abe 100755
--- a/rc/tools/patch-range.pl
+++ b/rc/tools/patch-range.pl
@@ -9,6 +9,12 @@ if ($ARGV[0] eq "-print-remaining-diff") {
shift @ARGV;
}
+my $line_number_kind = "diff";
+if ($ARGV[0] eq "-line-numbers-from-new-file") {
+ $line_number_kind = "new-file";
+ shift @ARGV;
+}
+
my $min_line = $ARGV[0];
shift @ARGV;
my $max_line = $ARGV[0];
@@ -22,7 +28,7 @@ if (defined $ARGV[0] and $ARGV[0] =~ m{^[^-]}) {
}
my $reverse = grep /^(--reverse|-R)$/, @ARGV;
-my $lineno = 0;
+my $lineno = $line_number_kind eq "diff" ? 0 : undef;
my $original = "";
my $diff_header = "";
my $wheat = "";
@@ -63,9 +69,9 @@ sub finish_hunk {
}
while (<STDIN>) {
- ++$lineno;
+ ++$lineno if $line_number_kind eq "diff";
$original .= $_;
- if (m{^diff}) {
+ if (m{^diff} || (not defined $state and m{^---})) {
finish_hunk();
$state = "diff header";
$diff_header = "";
@@ -74,7 +80,8 @@ while (<STDIN>) {
$signature .= $_ if $print_remaining;
next;
}
- if (m{^@@ -\d+(?:,(\d)+)? \+\d+(?:,\d+)? @@}) {
+ if (m{^@@ -\d+(?:,(\d)+)? \+(\d+)(?:,\d+)? @@}) {
+ $lineno = $2 - 1 if $line_number_kind eq "new-file";
$hunk_remaining_lines = $1 or 1;
finish_hunk();
$state = "diff hunk";
@@ -96,8 +103,11 @@ while (<STDIN>) {
$signature .= $_ if $print_remaining;
next;
}
+ ++$lineno if $line_number_kind eq "new-file" && m{^[ +]};
--$hunk_remaining_lines if m{^[ -]};
- my $include = m{^ } || ($lineno >= $min_line && $lineno <= $max_line);
+ my $include = m{^ } ||
+ ($lineno >= $min_line && $lineno <= $max_line) ||
+ ($line_number_kind eq "new-file" && m{^-} && $lineno == $min_line - 1);
if ($include) {
$hunk_wheat .= $_;
if ($print_remaining) {
diff --git a/src/main.cc b/src/main.cc
index 09c9b718..5eb30bc6 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -48,6 +48,7 @@ struct {
0,
"» kak_* appearing in shell arguments will be added to the environment\n"
"» {+U}double underline{} support\n"
+ "» {+u}git apply{} can stage/revert selected changes to current buffer\n"
}, {
20240518,
"» Fix tests failing on some platforms\n"