8c0424b521
One of the features I miss most from Magit/Fugitive/Tig is to apply/revert/stage/unstage individual hunks or even exactly the selected line(s). This provides a much more convenient way of splitting changes than "git add/restore -p". Implement a "patch" command that applies the selected lines within a diff by piping them to the "patch" program. It can also feed other programs like "git apply" (see the next commit). Original discussion: https://discuss.kakoune.com/t/atomic-commits-in-kakoune/1446 Interestingly, :patch is defined outside the "patch" module. This is to make it readily available for interactive use. Putting it into the module does not save any work. I tentatively added a patch module anyway so we can explicitly declare this dependency.. although there is the argument that this is not really needed?
94 lines
2.2 KiB
Perl
Executable File
94 lines
2.2 KiB
Perl
Executable File
#!/usr/bin/env perl
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
my $min_line = $ARGV[0];
|
|
shift @ARGV;
|
|
my $max_line = $ARGV[0];
|
|
shift @ARGV;
|
|
|
|
my $patch_cmd;
|
|
if (defined $ARGV[0] and $ARGV[0] =~ m{^[^-]}) {
|
|
$patch_cmd = "@ARGV";
|
|
} else {
|
|
$patch_cmd = "patch @ARGV";
|
|
}
|
|
my $reverse = grep /^(--reverse|-R)$/, @ARGV;
|
|
|
|
my $lineno = 0;
|
|
my $original = "";
|
|
my $wheat = "";
|
|
my $chaff = "";
|
|
my $state = undef;
|
|
my $hunk_wheat = undef;
|
|
my $hunk_chaff = undef;
|
|
my $hunk_header = undef;
|
|
|
|
sub compute_hunk_header {
|
|
my $original_header = shift;
|
|
my $hunk = shift;
|
|
my $old_lines = 0;
|
|
my $new_lines = 0;
|
|
for (split /\n/, $hunk) {
|
|
$old_lines++ if m{^[ -]};
|
|
$new_lines++ if m{^[ +]};
|
|
}
|
|
my $updated_header = $original_header =~ s/^@@ -(\d+),\d+\s+\+(\d+),\d+ @@(.*)/@@ -$1,$old_lines +$2,$new_lines @\@$3/mr;
|
|
return $updated_header;
|
|
}
|
|
|
|
sub finish_hunk {
|
|
return unless defined $hunk_header;
|
|
if ($hunk_wheat =~ m{^[-+]}m) {
|
|
$wheat .= (compute_hunk_header $hunk_header, $hunk_wheat). $hunk_wheat;
|
|
}
|
|
$chaff .= (compute_hunk_header $hunk_header, $hunk_chaff) . $hunk_chaff;
|
|
$hunk_header = undef;
|
|
}
|
|
|
|
while (<STDIN>) {
|
|
++$lineno;
|
|
$original .= $_;
|
|
if (m{^diff}) {
|
|
finish_hunk();
|
|
$state = "diff header";
|
|
}
|
|
if (m{^@@}) {
|
|
finish_hunk();
|
|
$state = "diff hunk";
|
|
$hunk_header = $_;
|
|
$hunk_wheat = "";
|
|
$hunk_chaff = "";
|
|
next;
|
|
}
|
|
if ($state eq "diff header") {
|
|
$wheat .= $_;
|
|
$chaff .= $_;
|
|
next;
|
|
}
|
|
my $include = m{^ } || ($lineno >= $min_line && $lineno <= $max_line);
|
|
if ($include) {
|
|
$hunk_wheat .= $_;
|
|
$hunk_chaff .= $_ if m{^ };
|
|
if ($reverse ? m{^[-]} : m{^\+}) {
|
|
$hunk_chaff .= " " . substr $_, 1;
|
|
}
|
|
} else {
|
|
if ($reverse ? m{^\+} : m{^-}) {
|
|
$hunk_wheat .= " " . substr $_, 1;
|
|
}
|
|
$hunk_chaff .= $_;
|
|
}
|
|
}
|
|
finish_hunk();
|
|
|
|
open PATCH_COMMAND, "|-", "$patch_cmd 1>&2" or die "patch-range.pl: error running '$patch_cmd': $!";
|
|
print PATCH_COMMAND $wheat;
|
|
if (not close PATCH_COMMAND) {
|
|
print $original;
|
|
print STDERR "patch-range.pl: error running:\n" . "\$ $patch_cmd << EOF\n$wheat" . "EOF\n";
|
|
exit 1;
|
|
}
|
|
print $chaff;
|