+ $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 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 l $grouplinestart
+ for {set i 0} {$i < $nevents} {set i $j} {
+ 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
+ set bestpn -1
+ if {[llength $active] == $nmerge - 1} {
+ # no diff for one of the parents, i.e. it's identical
+ 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
+ }
+ }
+ } elseif {[llength $active] == $nmerge} {
+ # all parents are different, see if one is very similar
+ set bestsim 30
+ for {set pnum 0} {$pnum < $nmerge} {incr pnum} {
+ set sim [similarity $pnum $l $nlc $f \
+ [lrange $events $i [expr {$j-1}]]]
+ if {$sim > $bestsim} {
+ set bestsim $sim
+ set bestpn $pnum
+ }
+ }
+ if {$bestpn >= 0} {
+ lappend ncol m$bestpn
+ }
+ }
+ set pnum -1
+ foreach p $parents($id) {
+ incr pnum
+ if {![info exists delta($pnum)] || $pnum == $bestpn} 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
+ }
+ }
+ set endl [expr {$l + $nlc}]
+ if {$bestpn >= 0} {
+ # show this pretty much as a normal diff
+ set p [lindex $parents($id) $bestpn]
+ set ol [expr {$l + $diffoffset($p)}]
+ incr diffoffset($p) $delta($bestpn)
+ unset delta($bestpn)
+ for {set k $i} {$k < $j} {incr k} {
+ set e [lindex $events $k]
+ if {[lindex $e 2] != $bestpn} continue
+ set nl [lindex $e 0]
+ set ol [expr {$ol + $nl - $l}]
+ for {} {$l < $nl} {incr l} {
+ $ctext insert end "+$filelines($id,$f,$l)\n" $ncol
+ }
+ set c [lindex $e 3]
+ for {} {$c > 0} {incr c -1} {
+ $ctext insert end "-$filelines($p,$f,$ol)\n" m$bestpn
+ incr ol
+ }
+ set nl [lindex $e 1]
+ for {} {$l < $nl} {incr l} {
+ $ctext insert end "+$filelines($id,$f,$l)\n" mresult
+ }
+ }
+ }
+ for {} {$l < $endl} {incr l} {
+ $ctext insert end "+$filelines($id,$f,$l)\n" $ncol
+ }
+ }
+ while {$l < $grouplineend} {
+ $ctext insert end " $filelines($id,$f,$l)\n"
+ incr l
+ }
+ $ctext conf -state disabled
+}
+
+proc similarity {pnum l nlc f events} {
+ global diffmergeid parents diffoffset filelines
+
+ set id $diffmergeid
+ set p [lindex $parents($id) $pnum]
+ set ol [expr {$l + $diffoffset($p)}]
+ set endl [expr {$l + $nlc}]
+ set same 0
+ set diff 0
+ foreach e $events {
+ if {[lindex $e 2] != $pnum} continue
+ set nl [lindex $e 0]
+ set ol [expr {$ol + $nl - $l}]
+ for {} {$l < $nl} {incr l} {
+ incr same [string length $filelines($id,$f,$l)]
+ incr same
+ }
+ set oc [lindex $e 3]
+ for {} {$oc > 0} {incr oc -1} {
+ incr diff [string length $filelines($p,$f,$ol)]
+ incr diff
+ incr ol
+ }
+ set nl [lindex $e 1]
+ for {} {$l < $nl} {incr l} {
+ incr diff [string length $filelines($id,$f,$l)]
+ incr diff
+ }
+ }
+ for {} {$l < $endl} {incr l} {
+ incr same [string length $filelines($id,$f,$l)]
+ incr same
+ }
+ if {$same == 0} {
+ return 0
+ }
+ return [expr {200 * $same / (2 * $same + $diff)}]
+}
+
+proc startdiff {ids} {
+ global treediffs diffids treepending diffmergeid
+
+ set diffids $ids
+ catch {unset diffmergeid}
+ if {![info exists treediffs($ids)]} {
+ if {![info exists treepending]} {
+ gettreediffs $ids
+ }
+ } else {
+ addtocflist $ids
+ }
+}
+
+proc addtocflist {ids} {
+ global treediffs cflist
+ foreach f $treediffs($ids) {
+ $cflist insert end $f
+ }
+ getblobdiffs $ids
+}
+
+proc gettreediffs {ids} {
+ global treediff parents treepending
+ set treepending $ids
+ set treediff {}
+ set id [lindex $ids 0]
+ set p [lindex $ids 1]
+ if [catch {set gdtf [open "|git-diff-tree -r $p $id" r]}] return
+ fconfigure $gdtf -blocking 0
+ fileevent $gdtf readable [list gettreediffline $gdtf $ids]
+}
+
+proc gettreediffline {gdtf ids} {
+ global treediff treediffs treepending diffids diffmergeid
+
+ set n [gets $gdtf line]
+ if {$n < 0} {
+ if {![eof $gdtf]} return
+ close $gdtf
+ set treediffs($ids) $treediff
+ unset treepending
+ if {$ids != $diffids} {
+ gettreediffs $diffids
+ } else {
+ if {[info exists diffmergeid]} {
+ contmergediff $ids
+ } else {
+ addtocflist $ids
+ }
+ }
+ return
+ }
+ set file [lindex $line 5]
+ lappend treediff $file
+}
+
+proc getblobdiffs {ids} {
+ global diffopts blobdifffd diffids env curdifftag curtagstart
+ global difffilestart nextupdate diffinhdr treediffs
+
+ set id [lindex $ids 0]
+ set p [lindex $ids 1]
+ set env(GIT_DIFF_OPTS) $diffopts
+ set cmd [list | git-diff-tree -r -p -C $p $id]
+ if {[catch {set bdf [open $cmd r]} err]} {
+ puts "error getting diffs: $err"
+ return
+ }
+ set diffinhdr 0
+ fconfigure $bdf -blocking 0
+ set blobdifffd($ids) $bdf
+ set curdifftag Comments
+ set curtagstart 0.0
+ catch {unset difffilestart}
+ fileevent $bdf readable [list getblobdiffline $bdf $diffids]
+ set nextupdate [expr {[clock clicks -milliseconds] + 100}]
+}
+
+proc getblobdiffline {bdf ids} {
+ global diffids blobdifffd ctext curdifftag curtagstart
+ global diffnexthead diffnextnote difffilestart
+ global nextupdate diffinhdr treediffs
+ global gaudydiff
+
+ set n [gets $bdf line]
+ if {$n < 0} {
+ if {[eof $bdf]} {
+ close $bdf
+ if {$ids == $diffids && $bdf == $blobdifffd($ids)} {
+ $ctext tag add $curdifftag $curtagstart end
+ }
+ }
+ return
+ }
+ if {$ids != $diffids || $bdf != $blobdifffd($ids)} {
+ return
+ }
+ $ctext conf -state normal
+ if {[regexp {^diff --git a/(.*) b/(.*)} $line match fname newname]} {
+ # start of a new file
+ $ctext insert end "\n"
+ $ctext tag add $curdifftag $curtagstart end
+ set curtagstart [$ctext index "end - 1c"]
+ set header $newname
+ set here [$ctext index "end - 1c"]
+ set i [lsearch -exact $treediffs($diffids) $fname]
+ if {$i >= 0} {
+ set difffilestart($i) $here
+ incr i
+ $ctext mark set fmark.$i $here
+ $ctext mark gravity fmark.$i left
+ }
+ if {$newname != $fname} {
+ set i [lsearch -exact $treediffs($diffids) $newname]
+ if {$i >= 0} {
+ set difffilestart($i) $here
+ incr i
+ $ctext mark set fmark.$i $here
+ $ctext mark gravity fmark.$i left
+ }
+ }
+ set curdifftag "f:$fname"
+ $ctext tag delete $curdifftag
+ set l [expr {(78 - [string length $header]) / 2}]
+ set pad [string range "----------------------------------------" 1 $l]
+ $ctext insert end "$pad $header $pad\n" filesep
+ set diffinhdr 1
+ } elseif {[regexp {^(---|\+\+\+)} $line]} {
+ set diffinhdr 0
+ } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \
+ $line match f1l f1c f2l f2c rest]} {
+ if {$gaudydiff} {
+ $ctext insert end "\t" hunksep
+ $ctext insert end " $f1l " d0 " $f2l " d1
+ $ctext insert end " $rest \n" hunksep
+ } else {
+ $ctext insert end "$line\n" hunksep
+ }
+ set diffinhdr 0
+ } else {
+ set x [string range $line 0 0]
+ if {$x == "-" || $x == "+"} {
+ set tag [expr {$x == "+"}]
+ if {$gaudydiff} {
+ set line [string range $line 1 end]
+ }
+ $ctext insert end "$line\n" d$tag
+ } elseif {$x == " "} {
+ if {$gaudydiff} {
+ set line [string range $line 1 end]
+ }
+ $ctext insert end "$line\n"
+ } elseif {$diffinhdr || $x == "\\"} {
+ # e.g. "\ No newline at end of file"
+ $ctext insert end "$line\n" filesep
+ } else {
+ # Something else we don't recognize
+ if {$curdifftag != "Comments"} {
+ $ctext insert end "\n"
+ $ctext tag add $curdifftag $curtagstart end
+ set curtagstart [$ctext index "end - 1c"]
+ set curdifftag Comments
+ }
+ $ctext insert end "$line\n" filesep
+ }
+ }
+ $ctext conf -state disabled
+ if {[clock clicks -milliseconds] >= $nextupdate} {
+ incr nextupdate 100
+ fileevent $bdf readable {}
+ update
+ fileevent $bdf readable "getblobdiffline $bdf {$ids}"
+ }
+}
+
+proc nextfile {} {
+ global difffilestart ctext
+ set here [$ctext index @0,0]
+ for {set i 0} {[info exists difffilestart($i)]} {incr i} {
+ if {[$ctext compare $difffilestart($i) > $here]} {
+ if {![info exists pos]
+ || [$ctext compare $difffilestart($i) < $pos]} {
+ set pos $difffilestart($i)
+ }
+ }
+ }
+ if {[info exists pos]} {
+ $ctext yview $pos
+ }
+}
+
+proc listboxsel {} {
+ global ctext cflist currentid
+ if {![info exists currentid]} return
+ set sel [lsort [$cflist curselection]]
+ if {$sel eq {}} return
+ set first [lindex $sel 0]
+ catch {$ctext yview fmark.$first}
+}
+
+proc setcoords {} {
+ global linespc charspc canvx0 canvy0 mainfont
+ global xspc1 xspc2
+
+ set linespc [font metrics $mainfont -linespace]
+ set charspc [font measure $mainfont "m"]
+ set canvy0 [expr 3 + 0.5 * $linespc]
+ set canvx0 [expr 3 + 0.5 * $linespc]
+ set xspc1(0) $linespc
+ set xspc2 $linespc
+}
+
+proc redisplay {} {
+ global stopped redisplaying phase
+ if {$stopped > 1} return
+ if {$phase == "getcommits"} return
+ set redisplaying 1
+ if {$phase == "drawgraph" || $phase == "incrdraw"} {
+ set stopped 1
+ } else {
+ drawgraph
+ }
+}
+
+proc incrfont {inc} {
+ global mainfont namefont textfont ctext canv phase
+ global stopped entries
+ unmarkmatches
+ set mainfont [lreplace $mainfont 1 1 [expr {[lindex $mainfont 1] + $inc}]]
+ set namefont [lreplace $namefont 1 1 [expr {[lindex $namefont 1] + $inc}]]
+ set textfont [lreplace $textfont 1 1 [expr {[lindex $textfont 1] + $inc}]]
+ setcoords
+ $ctext conf -font $textfont
+ $ctext tag conf filesep -font [concat $textfont bold]
+ foreach e $entries {
+ $e conf -font $mainfont
+ }
+ if {$phase == "getcommits"} {
+ $canv itemconf textitems -font $mainfont
+ }
+ redisplay
+}
+
+proc clearsha1 {} {
+ global sha1entry sha1string
+ if {[string length $sha1string] == 40} {
+ $sha1entry delete 0 end
+ }
+}
+
+proc sha1change {n1 n2 op} {
+ global sha1string currentid sha1but
+ if {$sha1string == {}
+ || ([info exists currentid] && $sha1string == $currentid)} {
+ set state disabled
+ } else {
+ set state normal
+ }
+ if {[$sha1but cget -state] == $state} return
+ if {$state == "normal"} {
+ $sha1but conf -state normal -relief raised -text "Goto: "
+ } else {
+ $sha1but conf -state disabled -relief flat -text "SHA1 ID: "
+ }
+}
+
+proc gotocommit {} {
+ global sha1string currentid idline tagids
+ global lineid numcommits
+
+ if {$sha1string == {}
+ || ([info exists currentid] && $sha1string == $currentid)} return
+ if {[info exists tagids($sha1string)]} {
+ set id $tagids($sha1string)
+ } else {
+ set id [string tolower $sha1string]
+ if {[regexp {^[0-9a-f]{4,39}$} $id]} {
+ set matches {}
+ for {set l 0} {$l < $numcommits} {incr l} {
+ if {[string match $id* $lineid($l)]} {
+ lappend matches $lineid($l)
+ }
+ }
+ if {$matches ne {}} {
+ if {[llength $matches] > 1} {
+ error_popup "Short SHA1 id $id is ambiguous"
+ return
+ }
+ set id [lindex $matches 0]
+ }
+ }
+ }
+ if {[info exists idline($id)]} {
+ selectline $idline($id) 1
+ return
+ }
+ if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} {
+ set type "SHA1 id"
+ } else {
+ set type "Tag"
+ }
+ error_popup "$type $sha1string is not known"
+}
+
+proc lineenter {x y id} {
+ global hoverx hovery hoverid hovertimer
+ global commitinfo canv
+
+ if {![info exists commitinfo($id)]} return
+ set hoverx $x
+ set hovery $y
+ set hoverid $id
+ if {[info exists hovertimer]} {
+ after cancel $hovertimer
+ }
+ set hovertimer [after 500 linehover]
+ $canv delete hover
+}
+
+proc linemotion {x y id} {
+ global hoverx hovery hoverid hovertimer
+
+ if {[info exists hoverid] && $id == $hoverid} {
+ set hoverx $x
+ set hovery $y
+ if {[info exists hovertimer]} {
+ after cancel $hovertimer
+ }
+ set hovertimer [after 500 linehover]
+ }
+}
+
+proc lineleave {id} {
+ global hoverid hovertimer canv
+
+ if {[info exists hoverid] && $id == $hoverid} {
+ $canv delete hover
+ if {[info exists hovertimer]} {
+ after cancel $hovertimer
+ unset hovertimer
+ }
+ unset hoverid
+ }
+}
+
+proc linehover {} {
+ global hoverx hovery hoverid hovertimer
+ global canv linespc lthickness
+ global commitinfo mainfont
+
+ set text [lindex $commitinfo($hoverid) 0]
+ set ymax [lindex [$canv cget -scrollregion] 3]
+ if {$ymax == {}} return
+ set yfrac [lindex [$canv yview] 0]
+ set x [expr {$hoverx + 2 * $linespc}]
+ set y [expr {$hovery + $yfrac * $ymax - $linespc / 2}]
+ set x0 [expr {$x - 2 * $lthickness}]
+ set y0 [expr {$y - 2 * $lthickness}]
+ set x1 [expr {$x + [font measure $mainfont $text] + 2 * $lthickness}]
+ set y1 [expr {$y + $linespc + 2 * $lthickness}]
+ set t [$canv create rectangle $x0 $y0 $x1 $y1 \
+ -fill \#ffff80 -outline black -width 1 -tags hover]
+ $canv raise $t
+ set t [$canv create text $x $y -anchor nw -text $text -tags hover]
+ $canv raise $t
+}
+
+proc lineclick {x y id isnew} {
+ global ctext commitinfo children cflist canv
+
+ unmarkmatches
+ unselectline
+ if {$isnew} {
+ addtohistory [list lineclick $x $x $id 0]
+ }
+ $canv delete hover
+ # fill the details pane with info about this line
+ $ctext conf -state normal
+ $ctext delete 0.0 end
+ $ctext tag conf link -foreground blue -underline 1
+ $ctext tag bind link <Enter> { %W configure -cursor hand2 }
+ $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
+ $ctext insert end "Parent:\t"
+ $ctext insert end $id [list link link0]
+ $ctext tag bind link0 <1> [list selbyid $id]
+ set info $commitinfo($id)
+ $ctext insert end "\n\t[lindex $info 0]\n"
+ $ctext insert end "\tAuthor:\t[lindex $info 1]\n"
+ $ctext insert end "\tDate:\t[lindex $info 2]\n"
+ if {[info exists children($id)]} {
+ $ctext insert end "\nChildren:"
+ set i 0
+ foreach child $children($id) {
+ incr i
+ set info $commitinfo($child)
+ $ctext insert end "\n\t"
+ $ctext insert end $child [list link link$i]
+ $ctext tag bind link$i <1> [list selbyid $child]
+ $ctext insert end "\n\t[lindex $info 0]"
+ $ctext insert end "\n\tAuthor:\t[lindex $info 1]"
+ $ctext insert end "\n\tDate:\t[lindex $info 2]\n"
+ }
+ }
+ $ctext conf -state disabled
+
+ $cflist delete 0 end
+}
+
+proc selbyid {id} {
+ global idline
+ if {[info exists idline($id)]} {
+ selectline $idline($id) 1
+ }
+}
+
+proc mstime {} {
+ global startmstime
+ if {![info exists startmstime]} {
+ set startmstime [clock clicks -milliseconds]
+ }
+ return [format "%.3f" [expr {([clock click -milliseconds] - $startmstime) / 1000.0}]]
+}
+
+proc rowmenu {x y id} {
+ global rowctxmenu idline selectedline rowmenuid
+
+ if {![info exists selectedline] || $idline($id) eq $selectedline} {
+ set state disabled
+ } else {
+ set state normal
+ }
+ $rowctxmenu entryconfigure 0 -state $state
+ $rowctxmenu entryconfigure 1 -state $state
+ $rowctxmenu entryconfigure 2 -state $state
+ set rowmenuid $id
+ tk_popup $rowctxmenu $x $y
+}
+
+proc diffvssel {dirn} {
+ global rowmenuid selectedline lineid
+
+ if {![info exists selectedline]} return
+ if {$dirn} {
+ set oldid $lineid($selectedline)
+ set newid $rowmenuid
+ } else {
+ set oldid $rowmenuid
+ set newid $lineid($selectedline)
+ }
+ addtohistory [list doseldiff $oldid $newid]
+ doseldiff $oldid $newid
+}
+
+proc doseldiff {oldid newid} {
+ global ctext cflist
+ global commitinfo
+
+ $ctext conf -state normal
+ $ctext delete 0.0 end
+ $ctext mark set fmark.0 0.0
+ $ctext mark gravity fmark.0 left
+ $cflist delete 0 end
+ $cflist insert end "Top"
+ $ctext insert end "From "
+ $ctext tag conf link -foreground blue -underline 1
+ $ctext tag bind link <Enter> { %W configure -cursor hand2 }
+ $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
+ $ctext tag bind link0 <1> [list selbyid $oldid]
+ $ctext insert end $oldid [list link link0]
+ $ctext insert end "\n "
+ $ctext insert end [lindex $commitinfo($oldid) 0]
+ $ctext insert end "\n\nTo "
+ $ctext tag bind link1 <1> [list selbyid $newid]
+ $ctext insert end $newid [list link link1]
+ $ctext insert end "\n "
+ $ctext insert end [lindex $commitinfo($newid) 0]
+ $ctext insert end "\n"
+ $ctext conf -state disabled
+ $ctext tag delete Comments
+ $ctext tag remove found 1.0 end
+ startdiff [list $newid $oldid]
+}
+
+proc mkpatch {} {
+ global rowmenuid currentid commitinfo patchtop patchnum
+
+ if {![info exists currentid]} return
+ set oldid $currentid
+ set oldhead [lindex $commitinfo($oldid) 0]
+ set newid $rowmenuid
+ set newhead [lindex $commitinfo($newid) 0]
+ set top .patch
+ set patchtop $top
+ catch {destroy $top}
+ toplevel $top
+ label $top.title -text "Generate patch"
+ grid $top.title - -pady 10
+ label $top.from -text "From:"
+ entry $top.fromsha1 -width 40 -relief flat
+ $top.fromsha1 insert 0 $oldid
+ $top.fromsha1 conf -state readonly
+ grid $top.from $top.fromsha1 -sticky w
+ entry $top.fromhead -width 60 -relief flat
+ $top.fromhead insert 0 $oldhead
+ $top.fromhead conf -state readonly
+ grid x $top.fromhead -sticky w
+ label $top.to -text "To:"
+ entry $top.tosha1 -width 40 -relief flat
+ $top.tosha1 insert 0 $newid
+ $top.tosha1 conf -state readonly
+ grid $top.to $top.tosha1 -sticky w
+ entry $top.tohead -width 60 -relief flat
+ $top.tohead insert 0 $newhead
+ $top.tohead conf -state readonly
+ grid x $top.tohead -sticky w
+ button $top.rev -text "Reverse" -command mkpatchrev -padx 5
+ grid $top.rev x -pady 10
+ label $top.flab -text "Output file:"
+ entry $top.fname -width 60
+ $top.fname insert 0 [file normalize "patch$patchnum.patch"]
+ incr patchnum
+ grid $top.flab $top.fname -sticky w
+ frame $top.buts
+ button $top.buts.gen -text "Generate" -command mkpatchgo
+ button $top.buts.can -text "Cancel" -command mkpatchcan
+ grid $top.buts.gen $top.buts.can
+ grid columnconfigure $top.buts 0 -weight 1 -uniform a
+ grid columnconfigure $top.buts 1 -weight 1 -uniform a
+ grid $top.buts - -pady 10 -sticky ew
+ focus $top.fname
+}
+
+proc mkpatchrev {} {
+ global patchtop
+
+ set oldid [$patchtop.fromsha1 get]
+ set oldhead [$patchtop.fromhead get]
+ set newid [$patchtop.tosha1 get]
+ set newhead [$patchtop.tohead get]
+ foreach e [list fromsha1 fromhead tosha1 tohead] \
+ v [list $newid $newhead $oldid $oldhead] {
+ $patchtop.$e conf -state normal
+ $patchtop.$e delete 0 end
+ $patchtop.$e insert 0 $v
+ $patchtop.$e conf -state readonly
+ }
+}
+
+proc mkpatchgo {} {
+ global patchtop
+
+ set oldid [$patchtop.fromsha1 get]
+ set newid [$patchtop.tosha1 get]
+ set fname [$patchtop.fname get]
+ if {[catch {exec git-diff-tree -p $oldid $newid >$fname &} err]} {
+ error_popup "Error creating patch: $err"
+ }
+ catch {destroy $patchtop}
+ unset patchtop
+}
+
+proc mkpatchcan {} {
+ global patchtop
+
+ catch {destroy $patchtop}
+ unset patchtop
+}
+
+proc mktag {} {
+ global rowmenuid mktagtop commitinfo
+
+ set top .maketag
+ set mktagtop $top
+ catch {destroy $top}
+ toplevel $top
+ label $top.title -text "Create tag"
+ grid $top.title - -pady 10
+ label $top.id -text "ID:"
+ entry $top.sha1 -width 40 -relief flat
+ $top.sha1 insert 0 $rowmenuid
+ $top.sha1 conf -state readonly
+ grid $top.id $top.sha1 -sticky w
+ entry $top.head -width 60 -relief flat
+ $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
+ $top.head conf -state readonly
+ grid x $top.head -sticky w
+ label $top.tlab -text "Tag name:"
+ entry $top.tag -width 60
+ grid $top.tlab $top.tag -sticky w
+ frame $top.buts
+ button $top.buts.gen -text "Create" -command mktaggo
+ button $top.buts.can -text "Cancel" -command mktagcan
+ grid $top.buts.gen $top.buts.can
+ grid columnconfigure $top.buts 0 -weight 1 -uniform a
+ grid columnconfigure $top.buts 1 -weight 1 -uniform a
+ grid $top.buts - -pady 10 -sticky ew
+ focus $top.tag
+}
+
+proc domktag {} {
+ global mktagtop env tagids idtags
+ global idpos idline linehtag canv selectedline
+
+ set id [$mktagtop.sha1 get]
+ set tag [$mktagtop.tag get]
+ if {$tag == {}} {
+ error_popup "No tag name specified"
+ return
+ }
+ if {[info exists tagids($tag)]} {
+ error_popup "Tag \"$tag\" already exists"
+ return
+ }
+ if {[catch {
+ set dir [gitdir]
+ set fname [file join $dir "refs/tags" $tag]
+ set f [open $fname w]
+ puts $f $id
+ close $f
+ } err]} {
+ error_popup "Error creating tag: $err"
+ return
+ }
+
+ set tagids($tag) $id
+ lappend idtags($id) $tag
+ $canv delete tag.$id
+ set xt [eval drawtags $id $idpos($id)]
+ $canv coords $linehtag($idline($id)) $xt [lindex $idpos($id) 2]
+ if {[info exists selectedline] && $selectedline == $idline($id)} {
+ selectline $selectedline 0
+ }
+}
+
+proc mktagcan {} {
+ global mktagtop
+
+ catch {destroy $mktagtop}
+ unset mktagtop
+}
+
+proc mktaggo {} {
+ domktag
+ mktagcan
+}
+
+proc writecommit {} {
+ global rowmenuid wrcomtop commitinfo wrcomcmd
+
+ set top .writecommit
+ set wrcomtop $top
+ catch {destroy $top}
+ toplevel $top
+ label $top.title -text "Write commit to file"
+ grid $top.title - -pady 10
+ label $top.id -text "ID:"
+ entry $top.sha1 -width 40 -relief flat
+ $top.sha1 insert 0 $rowmenuid
+ $top.sha1 conf -state readonly
+ grid $top.id $top.sha1 -sticky w
+ entry $top.head -width 60 -relief flat
+ $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
+ $top.head conf -state readonly
+ grid x $top.head -sticky w
+ label $top.clab -text "Command:"
+ entry $top.cmd -width 60 -textvariable wrcomcmd
+ grid $top.clab $top.cmd -sticky w -pady 10
+ label $top.flab -text "Output file:"
+ entry $top.fname -width 60
+ $top.fname insert 0 [file normalize "commit-[string range $rowmenuid 0 6]"]
+ grid $top.flab $top.fname -sticky w
+ frame $top.buts
+ button $top.buts.gen -text "Write" -command wrcomgo
+ button $top.buts.can -text "Cancel" -command wrcomcan
+ grid $top.buts.gen $top.buts.can
+ grid columnconfigure $top.buts 0 -weight 1 -uniform a
+ grid columnconfigure $top.buts 1 -weight 1 -uniform a
+ grid $top.buts - -pady 10 -sticky ew
+ focus $top.fname
+}
+
+proc wrcomgo {} {
+ global wrcomtop