18
$diff_use_color ? (
19
$repo->get_color('color.diff.frag', 'cyan'),
20
) : ();
21
+my ($diff_plain_color) =
22
+ $diff_use_color ? (
23
+ $repo->get_color('color.diff.plain', ''),
24
+ ) : ();
25
+my ($diff_old_color) =
26
+ $diff_use_color ? (
27
+ $repo->get_color('color.diff.old', 'red'),
28
+ ) : ();
29
+my ($diff_new_color) =
30
+ $diff_use_color ? (
31
+ $repo->get_color('color.diff.new', 'green'),
32
+ ) : ();
33
34
my $normal_color = $repo->get_color("", "reset");
35
695
}
696
697
698
+sub color_diff {
699
+ return map {
700
+ colored((/^@/ ? $fraginfo_color :
701
+ /^\+/ ? $diff_new_color :
702
+ /^-/ ? $diff_old_color :
703
+ $diff_plain_color),
704
+ $_);
705
+ } @_;
706
+}
707
+
708
+sub edit_hunk_manually {
709
+ my ($oldtext) = @_;
710
+
711
+ my $hunkfile = $repo->repo_path . "/addp-hunk-edit.diff";
712
+ my $fh;
713
+ open $fh, '>', $hunkfile
714
+ or die "failed to open hunk edit file for writing: " . $!;
715
+ print $fh "# Manual hunk edit mode -- see bottom for a quick guide\n";
716
+ print $fh @$oldtext;
717
+ print $fh <<EOF;
718
+# ---
719
+# To remove '-' lines, make them ' ' lines (context).
720
+# To remove '+' lines, delete them.
721
+# Lines starting with # will be removed.
722
+#
723
+# If the patch applies cleanly, the edited hunk will immediately be
724
+# marked for staging. If it does not apply cleanly, you will be given
725
+# an opportunity to edit again. If all lines of the hunk are removed,
726
+# then the edit is aborted and the hunk is left unchanged.
727
+EOF
728
+ close $fh;
729
+
730
+ my $editor = $ENV{GIT_EDITOR} || $repo->config("core.editor")
731
+ || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
732
+ system('sh', '-c', $editor.' "$@"', $editor, $hunkfile);
733
+
734
+ open $fh, '<', $hunkfile
735
+ or die "failed to open hunk edit file for reading: " . $!;
736
+ my @newtext = grep { !/^#/ } <$fh>;
737
+ close $fh;
738
+ unlink $hunkfile;
739
+
740
+ # Abort if nothing remains
741
+ if (!grep { /\S/ } @newtext) {
742
+ return undef;
743
+ }
744
+
745
+ # Reinsert the first hunk header if the user accidentally deleted it
746
+ if ($newtext[0] !~ /^@/) {
747
+ unshift @newtext, $oldtext->[0];
748
+ }
749
+ return \@newtext;
750
+}
751
+
752
+sub diff_applies {
753
+ my $fh;
754
+ open $fh, '| git apply --recount --cached --check';
755
+ for my $h (@_) {
756
+ print $fh @{$h->{TEXT}};
757
+ }
758
+ return close $fh;
759
+}
760
+
761
+sub prompt_yesno {
762
+ my ($prompt) = @_;
763
+ while (1) {
764
+ print colored $prompt_color, $prompt;
765
+ my $line = <STDIN>;
766
+ return 0 if $line =~ /^n/i;
767
+ return 1 if $line =~ /^y/i;
768
+ }
769
+}
770
+
771
+sub edit_hunk_loop {
772
+ my ($head, $hunk, $ix) = @_;
773
+ my $text = $hunk->[$ix]->{TEXT};
774
+
775
+ while (1) {
776
+ $text = edit_hunk_manually($text);
777
+ if (!defined $text) {
778
+ return undef;
779
+ }
780
+ my $newhunk = { TEXT => $text, USE => 1 };
781
+ if (diff_applies($head,
782
+ @{$hunk}[0..$ix-1],
783
+ $newhunk,
784
+ @{$hunk}[$ix+1..$#{$hunk}])) {
785
+ $newhunk->{DISPLAY} = [color_diff(@{$text})];
786
+ return $newhunk;
787
+ }
788
+ else {
789
+ prompt_yesno(
790
+ 'Your edited hunk does not apply. Edit again '
791
+ . '(saying "no" discards!) [y/n]? '
792
+ ) or return undef;
793
+ }
794
+ }
795
+}
796
+
797
sub help_patch_cmd {
798
print colored $help_color, <<\EOF ;
799
y - stage this hunk
805
k - leave this hunk undecided, see previous undecided hunk
806
K - leave this hunk undecided, see previous hunk
807
s - split the current hunk into smaller hunks
808
+e - manually edit the current hunk
809
? - print help
810
EOF
811
}
910
if (hunk_splittable($hunk[$ix]{TEXT})) {
911
$other .= '/s';
912
}
913
+ $other .= '/e';
914
for (@{$hunk[$ix]{DISPLAY}}) {
915
print;
916
}
975
$num = scalar @hunk;
976
next;
977
}
978
+ elsif ($line =~ /^e/) {
979
+ my $newhunk = edit_hunk_loop($head, \@hunk, $ix);
980
+ if (defined $newhunk) {
981
+ splice @hunk, $ix, 1, $newhunk;
982
+ }
983
+ }
984
else {
985
help_patch_cmd($other);
986
next;