@@ -542,10 +542,23 @@ proc apply_hunk {x y} {
542
542
}
543
543
}
544
544
545
- proc apply_line {x y} {
545
+ proc apply_range_or_line {x y} {
546
546
global current_diff_path current_diff_header current_diff_side
547
547
global ui_diff ui_index file_states
548
548
549
+ set selected [$ui_diff tag nextrange sel 0.0]
550
+
551
+ if {$selected == {}} {
552
+ set first [$ui_diff index " @$x ,$y " ]
553
+ set last $first
554
+ } else {
555
+ set first [lindex $selected 0]
556
+ set last [lindex $selected 1]
557
+ }
558
+
559
+ set first_l [$ui_diff index " $first linestart" ]
560
+ set last_l [$ui_diff index " $last lineend" ]
561
+
549
562
if {$current_diff_path eq {} || $current_diff_header eq {}} return
550
563
if {![lock_index apply_hunk]} return
551
564
@@ -568,120 +581,147 @@ proc apply_line {x y} {
568
581
}
569
582
}
570
583
571
- set the_l [ $ui_diff index @ $x , $y ]
584
+ set wholepatch {}
572
585
573
- # operate only on change lines
574
- set c1 [$ui_diff get " $the_l linestart" ]
575
- if {$c1 ne {+} && $c1 ne {-}} {
576
- unlock_index
577
- return
578
- }
579
- set sign $c1
580
-
581
- set i_l [$ui_diff search -backwards -regexp ^@@ $the_l 0.0]
582
- if {$i_l eq {}} {
583
- unlock_index
584
- return
585
- }
586
- # $i_l is now at the beginning of a line
586
+ while {$first_l < $last_l } {
587
+ set i_l [$ui_diff search -backwards -regexp ^@@ $first_l 0.0]
588
+ if {$i_l eq {}} {
589
+ # If there's not a @@ above, then the selected range
590
+ # must have come before the first_l @@
591
+ set i_l [$ui_diff search -regexp ^@@ $first_l $last_l ]
592
+ }
593
+ if {$i_l eq {}} {
594
+ unlock_index
595
+ return
596
+ }
597
+ # $i_l is now at the beginning of a line
587
598
588
- # pick start line number from hunk header
589
- set hh [$ui_diff get $i_l " $i_l + 1 lines" ]
590
- set hh [lindex [split $hh ,] 0]
591
- set hln [lindex [split $hh -] 1]
599
+ # pick start line number from hunk header
600
+ set hh [$ui_diff get $i_l " $i_l + 1 lines" ]
601
+ set hh [lindex [split $hh ,] 0]
602
+ set hln [lindex [split $hh -] 1]
592
603
593
- # There is a special situation to take care of. Consider this hunk:
594
- #
595
- # @@ -10,4 +10,4 @@
596
- # context before
597
- # -old 1
598
- # -old 2
599
- # +new 1
600
- # +new 2
601
- # context after
602
- #
603
- # We used to keep the context lines in the order they appear in the
604
- # hunk. But then it is not possible to correctly stage only
605
- # "-old 1" and "+new 1" - it would result in this staged text:
606
- #
607
- # context before
608
- # old 2
609
- # new 1
610
- # context after
611
- #
612
- # (By symmetry it is not possible to *un*stage "old 2" and "new 2".)
613
- #
614
- # We resolve the problem by introducing an asymmetry, namely, when
615
- # a "+" line is *staged*, it is moved in front of the context lines
616
- # that are generated from the "-" lines that are immediately before
617
- # the "+" block. That is, we construct this patch:
618
- #
619
- # @@ -10,4 +10,5 @@
620
- # context before
621
- # +new 1
622
- # old 1
623
- # old 2
624
- # context after
625
- #
626
- # But we do *not* treat "-" lines that are *un*staged in a special
627
- # way.
628
- #
629
- # With this asymmetry it is possible to stage the change
630
- # "old 1" -> "new 1" directly, and to stage the change
631
- # "old 2" -> "new 2" by first staging the entire hunk and
632
- # then unstaging the change "old 1" -> "new 1".
633
-
634
- # This is non-empty if and only if we are _staging_ changes;
635
- # then it accumulates the consecutive "-" lines (after converting
636
- # them to context lines) in order to be moved after the "+" change
637
- # line.
638
- set pre_context {}
639
-
640
- set n 0
641
- set i_l [$ui_diff index " $i_l + 1 lines" ]
642
- set patch {}
643
- while {[$ui_diff compare $i_l < " end - 1 chars" ] &&
644
- [$ui_diff get $i_l " $i_l + 2 chars" ] ne {@@}} {
645
- set next_l [$ui_diff index " $i_l + 1 lines" ]
646
- set c1 [$ui_diff get $i_l ]
647
- if {[$ui_diff compare $i_l <= $the_l ] &&
648
- [$ui_diff compare $the_l < $next_l ]} {
649
- # the line to stage/unstage
650
- set ln [$ui_diff get $i_l $next_l ]
651
- if {$c1 eq {-}} {
652
- set n [expr $n +1]
604
+ # There is a special situation to take care of. Consider this
605
+ # hunk:
606
+ #
607
+ # @@ -10,4 +10,4 @@
608
+ # context before
609
+ # -old 1
610
+ # -old 2
611
+ # +new 1
612
+ # +new 2
613
+ # context after
614
+ #
615
+ # We used to keep the context lines in the order they appear in
616
+ # the hunk. But then it is not possible to correctly stage only
617
+ # "-old 1" and "+new 1" - it would result in this staged text:
618
+ #
619
+ # context before
620
+ # old 2
621
+ # new 1
622
+ # context after
623
+ #
624
+ # (By symmetry it is not possible to *un*stage "old 2" and "new
625
+ # 2".)
626
+ #
627
+ # We resolve the problem by introducing an asymmetry, namely,
628
+ # when a "+" line is *staged*, it is moved in front of the
629
+ # context lines that are generated from the "-" lines that are
630
+ # immediately before the "+" block. That is, we construct this
631
+ # patch:
632
+ #
633
+ # @@ -10,4 +10,5 @@
634
+ # context before
635
+ # +new 1
636
+ # old 1
637
+ # old 2
638
+ # context after
639
+ #
640
+ # But we do *not* treat "-" lines that are *un*staged in a
641
+ # special way.
642
+ #
643
+ # With this asymmetry it is possible to stage the change "old
644
+ # 1" -> "new 1" directly, and to stage the change "old 2" ->
645
+ # "new 2" by first staging the entire hunk and then unstaging
646
+ # the change "old 1" -> "new 1".
647
+ #
648
+ # Applying multiple lines adds complexity to the special
649
+ # situation. The pre_context must be moved after the entire
650
+ # first block of consecutive staged "+" lines, so that
651
+ # staging both additions gives the following patch:
652
+ #
653
+ # @@ -10,4 +10,6 @@
654
+ # context before
655
+ # +new 1
656
+ # +new 2
657
+ # old 1
658
+ # old 2
659
+ # context after
660
+
661
+ # This is non-empty if and only if we are _staging_ changes;
662
+ # then it accumulates the consecutive "-" lines (after
663
+ # converting them to context lines) in order to be moved after
664
+ # "+" change lines.
665
+ set pre_context {}
666
+
667
+ set n 0
668
+ set m 0
669
+ set i_l [$ui_diff index " $i_l + 1 lines" ]
670
+ set patch {}
671
+ while {[$ui_diff compare $i_l < " end - 1 chars" ] &&
672
+ [$ui_diff get $i_l " $i_l + 2 chars" ] ne {@@}} {
673
+ set next_l [$ui_diff index " $i_l + 1 lines" ]
674
+ set c1 [$ui_diff get $i_l ]
675
+ if {[$ui_diff compare $first_l <= $i_l ] &&
676
+ [$ui_diff compare $i_l < $last_l ] &&
677
+ ($c1 eq {-} || $c1 eq {+})} {
678
+ # a line to stage/unstage
679
+ set ln [$ui_diff get $i_l $next_l ]
680
+ if {$c1 eq {-}} {
681
+ set n [expr $n +1]
682
+ set patch " $patch$pre_context$ln "
683
+ set pre_context {}
684
+ } else {
685
+ set m [expr $m +1]
686
+ set patch " $patch$ln "
687
+ }
688
+ } elseif {$c1 ne {-} && $c1 ne {+}} {
689
+ # context line
690
+ set ln [$ui_diff get $i_l $next_l ]
653
691
set patch " $patch$pre_context$ln "
692
+ set n [expr $n +1]
693
+ set m [expr $m +1]
694
+ set pre_context {}
695
+ } elseif {$c1 eq $to_context } {
696
+ # turn change line into context line
697
+ set ln [$ui_diff get " $i_l + 1 chars" $next_l ]
698
+ if {$c1 eq {-}} {
699
+ set pre_context " $pre_context $ln "
700
+ } else {
701
+ set patch " $patch $ln "
702
+ }
703
+ set n [expr $n +1]
704
+ set m [expr $m +1]
654
705
} else {
655
- set patch " $patch$ln$pre_context "
656
- }
657
- set pre_context {}
658
- } elseif {$c1 ne {-} && $c1 ne {+}} {
659
- # context line
660
- set ln [$ui_diff get $i_l $next_l ]
661
- set patch " $patch$pre_context$ln "
662
- set n [expr $n +1]
663
- set pre_context {}
664
- } elseif {$c1 eq $to_context } {
665
- # turn change line into context line
666
- set ln [$ui_diff get " $i_l + 1 chars" $next_l ]
667
- if {$c1 eq {-}} {
668
- set pre_context " $pre_context $ln "
669
- } else {
670
- set patch " $patch $ln "
706
+ # a change in the opposite direction of
707
+ # to_context which is outside the range of
708
+ # lines to apply.
709
+ set patch " $patch$pre_context "
710
+ set pre_context {}
671
711
}
672
- set n [ expr $n +1]
712
+ set i_l $next_l
673
713
}
674
- set i_l $next_l
714
+ set patch " $patch$pre_context "
715
+ set wholepatch " $wholepatch @@ -$hln ,$n +$hln ,$m @@\n $patch "
716
+ set first_l [$ui_diff index " $next_l + 1 lines" ]
675
717
}
676
- set patch " $patch$pre_context "
677
- set patch " @@ -$hln ,$n +$hln ,[ eval expr $n $sign 1] @@\n $patch "
678
718
679
719
if {[catch {
680
720
set enc [get_path_encoding $current_diff_path ]
681
721
set p [eval git_write $apply_cmd ]
682
722
fconfigure $p -translation binary -encoding $enc
683
723
puts -nonewline $p $current_diff_header
684
- puts -nonewline $p $patch
724
+ puts -nonewline $p $wholepatch
685
725
close $p } err]} {
686
726
error_popup [append $failed_msg " \n\n $err " ]
687
727
}
0 commit comments