@@ -767,6 +767,96 @@ sub split_hunk {
767767 return @split ;
768768}
769769
770+ sub find_last_o_ctx {
771+ my ($it ) = @_ ;
772+ my $text = $it -> {TEXT };
773+ my ($o_ofs , $o_cnt ) = parse_hunk_header($text -> [0]);
774+ my $i = @{$text };
775+ my $last_o_ctx = $o_ofs + $o_cnt ;
776+ while (0 < --$i ) {
777+ my $line = $text -> [$i ];
778+ if ($line =~ / ^ / ) {
779+ $last_o_ctx --;
780+ next ;
781+ }
782+ last ;
783+ }
784+ return $last_o_ctx ;
785+ }
786+
787+ sub merge_hunk {
788+ my ($prev , $this ) = @_ ;
789+ my ($o0_ofs , $o0_cnt , $n0_ofs , $n0_cnt ) =
790+ parse_hunk_header($prev -> {TEXT }[0]);
791+ my ($o1_ofs , $o1_cnt , $n1_ofs , $n1_cnt ) =
792+ parse_hunk_header($this -> {TEXT }[0]);
793+
794+ my (@line , $i , $ofs , $o_cnt , $n_cnt );
795+ $ofs = $o0_ofs ;
796+ $o_cnt = $n_cnt = 0;
797+ for ($i = 1; $i < @{$prev -> {TEXT }}; $i ++) {
798+ my $line = $prev -> {TEXT }[$i ];
799+ if ($line =~ / ^\+ / ) {
800+ $n_cnt ++;
801+ push @line , $line ;
802+ next ;
803+ }
804+
805+ last if ($o1_ofs <= $ofs );
806+
807+ $o_cnt ++;
808+ $ofs ++;
809+ if ($line =~ / ^ / ) {
810+ $n_cnt ++;
811+ }
812+ push @line , $line ;
813+ }
814+
815+ for ($i = 1; $i < @{$this -> {TEXT }}; $i ++) {
816+ my $line = $this -> {TEXT }[$i ];
817+ if ($line =~ / ^\+ / ) {
818+ $n_cnt ++;
819+ push @line , $line ;
820+ next ;
821+ }
822+ $ofs ++;
823+ $o_cnt ++;
824+ if ($line =~ / ^ / ) {
825+ $n_cnt ++;
826+ }
827+ push @line , $line ;
828+ }
829+ my $head = (" @@ -$o0_ofs " .
830+ (($o_cnt != 1) ? " ,$o_cnt " : ' ' ) .
831+ " +$n0_ofs " .
832+ (($n_cnt != 1) ? " ,$n_cnt " : ' ' ) .
833+ " @@\n " );
834+ @{$prev -> {TEXT }} = ($head , @line );
835+ }
836+
837+ sub coalesce_overlapping_hunks {
838+ my (@in ) = @_ ;
839+ my @out = ();
840+
841+ my ($last_o_ctx , $last_was_dirty );
842+
843+ for (grep { $_ -> {USE } } @in ) {
844+ my $text = $_ -> {TEXT };
845+ my ($o_ofs ) = parse_hunk_header($text -> [0]);
846+ if (defined $last_o_ctx &&
847+ $o_ofs <= $last_o_ctx &&
848+ !$_ -> {DIRTY } &&
849+ !$last_was_dirty ) {
850+ merge_hunk($out [-1], $_ );
851+ }
852+ else {
853+ push @out , $_ ;
854+ }
855+ $last_o_ctx = find_last_o_ctx($out [-1]);
856+ $last_was_dirty = $_ -> {DIRTY };
857+ }
858+ return @out ;
859+ }
770860
771861sub color_diff {
772862 return map {
@@ -878,7 +968,8 @@ sub edit_hunk_loop {
878968 my $newhunk = {
879969 TEXT => $text ,
880970 TYPE => $hunk -> [$ix ]-> {TYPE },
881- USE => 1
971+ USE => 1,
972+ DIRTY => 1,
882973 };
883974 if (diff_applies($head ,
884975 @{$hunk }[0..$ix -1],
@@ -1210,6 +1301,8 @@ sub patch_update_file {
12101301 }
12111302 }
12121303
1304+ @hunk = coalesce_overlapping_hunks(@hunk );
1305+
12131306 my $n_lofs = 0;
12141307 my @result = ();
12151308 for (@hunk ) {
@@ -1224,6 +1317,7 @@ sub patch_update_file {
12241317 open $fh , ' | git apply --cached --recount' ;
12251318 for (@{$head -> {TEXT }}, @result ) {
12261319 print $fh $_ ;
1320+ print STDERR $_ ;
12271321 }
12281322 if (!close $fh ) {
12291323 for (@{$head -> {TEXT }}, @result ) {
0 commit comments