Code

Display the diffs for a merge in a unified fashion.
authorPaul Mackerras <paulus@dorrigo.(none)>
Thu, 28 Jul 2005 03:15:47 +0000 (22:15 -0500)
committerPaul Mackerras <paulus@samba.org>
Thu, 28 Jul 2005 03:15:47 +0000 (22:15 -0500)
Stuff that ended up in the result is shown in bold with a "+" at the
beginning of the line; stuff that didn't is in the normal font with
a "-" at the beginning of the line.  The color shows which parent
the stuff was in; red for the first parent, blue for the second, then
green, purple, brown, and the rest are grey.  If the result is different
from all of the parents it is shown in black (and bold).

gitk

diff --git a/gitk b/gitk
index 112c9c0b60f9157a4556959396be4541a3e14963..61af6390d339b58dc2e56ce7f1577be7ede243ff 100755 (executable)
--- a/gitk
+++ b/gitk
@@ -273,7 +273,7 @@ proc makewindow {} {
     global findtype findtypemenu findloc findstring fstring geometry
     global entries sha1entry sha1string sha1but
     global maincursor textcursor
-    global rowctxmenu gaudydiff
+    global rowctxmenu gaudydiff mergemax
 
     menu .bar
     .bar add cascade -label "File" -menu .bar.file
@@ -373,6 +373,15 @@ proc makewindow {} {
        $ctext tag conf hunksep -fore blue
        $ctext tag conf d0 -fore red
        $ctext tag conf d1 -fore "#00a000"
+       $ctext tag conf m0 -fore red
+       $ctext tag conf m1 -fore blue
+       $ctext tag conf m2 -fore green
+       $ctext tag conf m3 -fore purple
+       $ctext tag conf m4 -fore brown
+       $ctext tag conf mmax -fore darkgrey
+       set mergemax 5
+       $ctext tag conf mresult -font [concat $textfont bold]
+       $ctext tag conf msep -font [concat $textfont bold]
        $ctext tag conf found -back yellow
     }
 
@@ -1752,7 +1761,9 @@ proc contmergediff {ids} {
        }
        if {![info exists treediffs($ids)]} {
            set diffids $ids
-           gettreediffs $ids
+           if {![info exists treepending]} {
+               gettreediffs $ids
+           }
            return
        }
     }
@@ -1790,16 +1801,364 @@ proc contmergediff {ids} {
     }
 
     set mergefilelist($diffmergeid) $files
-    showmergediff
+    if {$files ne {}} {
+       showmergediff
+    }
 }
 
 proc showmergediff {} {
-    global cflist diffmergeid mergefilelist
+    global cflist diffmergeid mergefilelist parents
+    global diffopts diffinhunk currentfile diffblocked
+    global groupfilelast mergefds
 
     set files $mergefilelist($diffmergeid)
     foreach f $files {
        $cflist insert end $f
     }
+    set env(GIT_DIFF_OPTS) $diffopts
+    set flist {}
+    catch {unset currentfile}
+    catch {unset currenthunk}
+    catch {unset filelines}
+    set groupfilelast -1
+    foreach p $parents($diffmergeid) {
+       set cmd [list | git-diff-tree -p $p $diffmergeid]
+       set cmd [concat $cmd $mergefilelist($diffmergeid)]
+       if {[catch {set f [open $cmd r]} err]} {
+           error_popup "Error getting diffs: $err"
+           foreach f $flist {
+               catch {close $f}
+           }
+           return
+       }
+       lappend flist $f
+       set ids [list $diffmergeid $p]
+       set mergefds($ids) $f
+       set diffinhunk($ids) 0
+       set diffblocked($ids) 0
+       fconfigure $f -blocking 0
+       fileevent $f readable [list getmergediffline $f $ids $diffmergeid]
+    }
+}
+
+proc getmergediffline {f ids id} {
+    global diffmergeid diffinhunk diffoldlines diffnewlines
+    global currentfile currenthunk
+    global diffoldstart diffnewstart diffoldlno diffnewlno
+    global diffblocked mergefilelist
+    global noldlines nnewlines difflcounts filelines
+
+    set n [gets $f line]
+    if {$n < 0} {
+       if {![eof $f]} return
+    }
+
+    if {!([info exists diffmergeid] && $diffmergeid == $id)} {
+       if {$n < 0} {
+           close $f
+       }
+       return
+    }
+
+    if {$diffinhunk($ids) != 0} {
+       set fi $currentfile($ids)
+       if {$n > 0 && [regexp {^[-+ \\]} $line match]} {
+           # continuing an existing hunk
+           set line [string range $line 1 end]
+           set p [lindex $ids 1]
+           if {$match eq "-" || $match eq " "} {
+               set filelines($p,$fi,$diffoldlno($ids)) $line
+               incr diffoldlno($ids)
+           }
+           if {$match eq "+" || $match eq " "} {
+               set filelines($id,$fi,$diffnewlno($ids)) $line
+               incr diffnewlno($ids)
+           }
+           if {$match eq " "} {
+               if {$diffinhunk($ids) == 2} {
+                   lappend difflcounts($ids) \
+                       [list $noldlines($ids) $nnewlines($ids)]
+                   set noldlines($ids) 0
+                   set diffinhunk($ids) 1
+               }
+               incr noldlines($ids)
+           } elseif {$match eq "-" || $match eq "+"} {
+               if {$diffinhunk($ids) == 1} {
+                   lappend difflcounts($ids) [list $noldlines($ids)]
+                   set noldlines($ids) 0
+                   set nnewlines($ids) 0
+                   set diffinhunk($ids) 2
+               }
+               if {$match eq "-"} {
+                   incr noldlines($ids)
+               } else {
+                   incr nnewlines($ids)
+               }
+           }
+           # and if it's \ No newline at end of line, then what?
+           return
+       }
+       # end of a hunk
+       if {$diffinhunk($ids) == 1 && $noldlines($ids) != 0} {
+           lappend difflcounts($ids) [list $noldlines($ids)]
+       } elseif {$diffinhunk($ids) == 2
+                 && ($noldlines($ids) != 0 || $nnewlines($ids) != 0)} {
+           lappend difflcounts($ids) [list $noldlines($ids) $nnewlines($ids)]
+       }
+       set currenthunk($ids) [list $currentfile($ids) \
+                                  $diffoldstart($ids) $diffnewstart($ids) \
+                                  $diffoldlno($ids) $diffnewlno($ids) \
+                                  $difflcounts($ids)]
+       set diffinhunk($ids) 0
+       # -1 = need to block, 0 = unblocked, 1 = is blocked
+       set diffblocked($ids) -1
+       processhunks
+       if {$diffblocked($ids) == -1} {
+           fileevent $f readable {}
+           set diffblocked($ids) 1
+       }
+    }
+
+    if {$n < 0} {
+       # eof
+       if {!$diffblocked($ids)} {
+           close $f
+           set currentfile($ids) [llength $mergefilelist($diffmergeid)]
+           set currenthunk($ids) [list $currentfile($ids) 0 0 0 0 {}]
+           processhunks
+       }
+    } elseif {[regexp {^diff --git a/(.*) b/} $line match fname]} {
+       # start of a new file
+       set currentfile($ids) \
+           [lsearch -exact $mergefilelist($diffmergeid) $fname]
+    } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \
+                  $line match f1l f1c f2l f2c rest]} {
+       if {[info exists currentfile($ids)] && $currentfile($ids) >= 0} {
+           # start of a new hunk
+           if {$f1l == 0 && $f1c == 0} {
+               set f1l 1
+           }
+           if {$f2l == 0 && $f2c == 0} {
+               set f2l 1
+           }
+           set diffinhunk($ids) 1
+           set diffoldstart($ids) $f1l
+           set diffnewstart($ids) $f2l
+           set diffoldlno($ids) $f1l
+           set diffnewlno($ids) $f2l
+           set difflcounts($ids) {}
+           set noldlines($ids) 0
+           set nnewlines($ids) 0
+       }
+    }
+}
+
+proc processhunks {} {
+    global diffmergeid parents nparents currenthunk
+    global mergefilelist diffblocked mergefds
+    global grouphunks grouplinestart grouplineend groupfilenum
+
+    set nfiles [llength $mergefilelist($diffmergeid)]
+    while 1 {
+       set fi $nfiles
+       set lno 0
+       # look for the earliest hunk
+       foreach p $parents($diffmergeid) {
+           set ids [list $diffmergeid $p]
+           if {![info exists currenthunk($ids)]} return
+           set i [lindex $currenthunk($ids) 0]
+           set l [lindex $currenthunk($ids) 2]
+           if {$i < $fi || ($i == $fi && $l < $lno)} {
+               set fi $i
+               set lno $l
+               set pi $p
+           }
+       }
+
+       if {$fi < $nfiles} {
+           set ids [list $diffmergeid $pi]
+           set hunk $currenthunk($ids)
+           unset currenthunk($ids)
+           if {$diffblocked($ids) > 0} {
+               fileevent $mergefds($ids) readable \
+                   [list getmergediffline $mergefds($ids) $ids $diffmergeid]
+           }
+           set diffblocked($ids) 0
+
+           if {[info exists groupfilenum] && $groupfilenum == $fi
+               && $lno <= $grouplineend} {
+               # add this hunk to the pending group
+               lappend grouphunks($pi) $hunk
+               set endln [lindex $hunk 4]
+               if {$endln > $grouplineend} {
+                   set grouplineend $endln
+               }
+               continue
+           }
+       }
+
+       # succeeding stuff doesn't belong in this group, so
+       # process the group now
+       if {[info exists groupfilenum]} {
+           processgroup
+           unset groupfilenum
+           unset grouphunks
+       }
+
+       if {$fi >= $nfiles} break
+
+       # start a new group
+       set groupfilenum $fi
+       set grouphunks($pi) [list $hunk]
+       set grouplinestart $lno
+       set grouplineend [lindex $hunk 4]
+    }
+}
+
+proc processgroup {} {
+    global groupfilelast groupfilenum difffilestart
+    global mergefilelist diffmergeid ctext filelines
+    global parents diffmergeid diffoffset
+    global grouphunks grouplinestart grouplineend nparents
+    global mergemax
+
+    $ctext conf -state normal
+    set id $diffmergeid
+    set f $groupfilenum
+    if {$groupfilelast != $f} {
+       $ctext insert end "\n"
+       set here [$ctext index "end - 1c"]
+       set difffilestart($f) $here
+       set mark fmark.[expr {$f + 1}]
+       $ctext mark set $mark $here
+       $ctext mark gravity $mark left
+       set header [lindex $mergefilelist($id) $f]
+       set l [expr {(78 - [string length $header]) / 2}]
+       set pad [string range "----------------------------------------" 1 $l]
+       $ctext insert end "$pad $header $pad\n" filesep
+       set groupfilelast $f
+       foreach p $parents($id) {
+           set diffoffset($p) 0
+       }
+    }
+
+    $ctext insert end "@@" msep
+    set nlines [expr {$grouplineend - $grouplinestart}]
+    set events {}
+    set pnum 0
+    foreach p $parents($id) {
+       set startline [expr {$grouplinestart + $diffoffset($p)}]
+       set offset($p) $diffoffset($p)
+       set ol $startline
+       set nl $grouplinestart
+       if {[info exists grouphunks($p)]} {
+           foreach h $grouphunks($p) {
+               set l [lindex $h 2]
+               if {$nl < $l} {
+                   for {} {$nl < $l} {incr nl} {
+                       set filelines($p,$f,$ol) $filelines($id,$f,$nl)
+                       incr ol
+                   }
+               }
+               foreach chunk [lindex $h 5] {
+                   if {[llength $chunk] == 2} {
+                       set olc [lindex $chunk 0]
+                       set nlc [lindex $chunk 1]
+                       set nnl [expr {$nl + $nlc}]
+                       lappend events [list $nl $nnl $pnum $olc $nlc]
+                       incr ol $olc
+                       set nl $nnl
+                   } else {
+                       incr ol [lindex $chunk 0]
+                       incr nl [lindex $chunk 0]
+                   }
+               }
+           }
+       }
+       if {$nl < $grouplineend} {
+           for {} {$nl < $grouplineend} {incr nl} {
+               set filelines($p,$f,$ol) $filelines($id,$f,$nl)
+               incr ol
+           }
+       }
+       set nlines [expr {$ol - $startline}]
+       $ctext insert end " -$startline,$nlines" msep
+       incr pnum
+    }
+
+    set nlines [expr {$grouplineend - $grouplinestart}]
+    $ctext insert end " +$grouplinestart,$nlines @@\n" msep
+
+    set events [lsort -integer -index 0 $events]
+    set nevents [llength $events]
+    set nmerge $nparents($diffmergeid)
+    set i 0
+    set l $grouplinestart
+    while {$i < $nevents} {
+       set nl [lindex $events $i 0]
+       while {$l < $nl} {
+           $ctext insert end " $filelines($id,$f,$l)\n"
+           incr l
+       }
+       set e [lindex $events $i]
+       set enl [lindex $e 1]
+       set j $i
+       set active {}
+       while 1 {
+           set pnum [lindex $e 2]
+           set olc [lindex $e 3]
+           set nlc [lindex $e 4]
+           if {![info exists delta($pnum)]} {
+               set delta($pnum) [expr {$olc - $nlc}]
+               lappend active $pnum
+           } else {
+               incr delta($pnum) [expr {$olc - $nlc}]
+           }
+           if {[incr j] >= $nevents} break
+           set e [lindex $events $j]
+           if {[lindex $e 0] >= $enl} break
+           if {[lindex $e 1] > $enl} {
+               set enl [lindex $e 1]
+           }
+       }
+       set nlc [expr {$enl - $l}]
+       set ncol mresult
+       if {[llength $active] == $nmerge - 1} {
+           for {set pnum 0} {$pnum < $nmerge} {incr pnum} {
+               if {![info exists delta($pnum)]} {
+                   if {$pnum < $mergemax} {
+                       lappend ncol m$pnum
+                   } else {
+                       lappend ncol mmax
+                   }
+                   break
+               }
+           }
+       }
+       set pnum -1
+       foreach p $parents($id) {
+           incr pnum
+           if {![info exists delta($pnum)]} continue
+           set olc [expr {$nlc + $delta($pnum)}]
+           set ol [expr {$l + $diffoffset($p)}]
+           incr diffoffset($p) $delta($pnum)
+           unset delta($pnum)
+           for {} {$olc > 0} {incr olc -1} {
+               $ctext insert end "-$filelines($p,$f,$ol)\n" m$pnum
+               incr ol
+           }
+       }
+       for {} {$nlc > 0} {incr nlc -1} {
+           $ctext insert end "+$filelines($id,$f,$l)\n" $ncol
+           incr l
+       }
+       set i $j
+    }
+    while {$l < $grouplineend} {
+       $ctext insert end " $filelines($id,$f,$l)\n"
+       incr l
+    }
+    $ctext conf -state disabled
 }
 
 proc startdiff {ids} {