set parsed_args $rargs
}
if [catch {
- set commfd [open "|git-rev-list --header --merge-order $parsed_args" r]
+ set commfd [open "|git-rev-list --header --topo-order $parsed_args" r]
} err] {
puts stderr "Error executing git-rev-list: $err"
exit 1
set stuff [read $commfd]
if {$stuff == {}} {
if {![eof $commfd]} return
- # this works around what is apparently a bug in Tcl...
+ # set it blocking so we wait for the process to terminate
fconfigure $commfd -blocking 1
if {![catch {close $commfd} err]} {
after idle finishcommits
global findtype findtypemenu findloc findstring fstring geometry
global entries sha1entry sha1string sha1but
global maincursor textcursor
- global rowctxmenu
+ global rowctxmenu gaudydiff
menu .bar
.bar add cascade -label "File" -menu .bar.file
pack $ctext -side left -fill both -expand 1
.ctop.cdet add .ctop.cdet.left
- $ctext tag conf filesep -font [concat $textfont bold]
- $ctext tag conf hunksep -back blue -fore white
- $ctext tag conf d0 -back "#ff8080"
- $ctext tag conf d1 -back green
- $ctext tag conf found -back yellow
+ $ctext tag conf filesep -font [concat $textfont bold] -back "#aaaaaa"
+ if {$gaudydiff} {
+ $ctext tag conf hunksep -back blue -fore white
+ $ctext tag conf d0 -back "#ff8080"
+ $ctext tag conf d1 -back green
+ } else {
+ $ctext tag conf hunksep -fore blue
+ $ctext tag conf d0 -fore red
+ $ctext tag conf d1 -fore "#00a000"
+ $ctext tag conf found -back yellow
+ }
frame .ctop.cdet.right
set cflist .ctop.cdet.right.cfiles
if {![winfo viewable .]} return
catch {
set f [open "~/.gitk-new" w]
- puts $f "set mainfont {$mainfont}"
- puts $f "set textfont {$textfont}"
+ puts $f [list set mainfont $mainfont]
+ puts $f [list set textfont $textfont]
+ puts $f [list set findmergefiles $findmergefiles]
+ puts $f [list set gaudydiff $gaudydiff]
puts $f "set geometry(width) [winfo width .ctop]"
puts $f "set geometry(height) [winfo height .ctop]"
puts $f "set geometry(canv1) [expr [winfo width $canv]-2]"
global findstring selectedline numcommits
global findprocpid findprocfile
global finddidsel ctext lineid findinprogress
+ global findinsertpos
if {$numcommits == 0} return
return
}
+ set findinsertpos end
set findprocfile $f
set findprocpid [pid $f]
fconfigure $f -blocking 0
proc readfindproc {} {
global findprocfile finddidsel
- global idline matchinglines
+ global idline matchinglines findinsertpos
set n [gets $findprocfile line]
if {$n < 0} {
return
}
set l $idline($id)
- lappend matchinglines $l
+ insertmatch $l $id
+}
+
+proc insertmatch {l id} {
+ global matchinglines findinsertpos finddidsel
+
+ if {$findinsertpos == "end"} {
+ if {$matchinglines != {} && $l < [lindex $matchinglines 0]} {
+ set matchinglines [linsert $matchinglines 0 $l]
+ set findinsertpos 1
+ } else {
+ lappend matchinglines $l
+ }
+ } else {
+ set matchinglines [linsert $matchinglines $findinsertpos $l]
+ incr findinsertpos
+ }
+ markheadline $l $id
if {!$finddidsel} {
findselectline $l
set finddidsel 1
}
proc findfiles {} {
- global selectedline numcommits lineid
- global ffileline finddidsel parents findstartline
- global findinprogress ctext
+ global selectedline numcommits lineid ctext
+ global ffileline finddidsel parents nparents
+ global findinprogress findstartline findinsertpos
+ global treediffs fdiffids fdiffsneeded fdiffpos
+ global findmergefiles
if {$numcommits == 0} return
set l 0
}
set ffileline $l
- set finddidsel 0
set findstartline $l
+ set diffsneeded {}
+ set fdiffsneeded {}
+ while 1 {
+ set id $lineid($l)
+ if {$findmergefiles || $nparents($id) == 1} {
+ foreach p $parents($id) {
+ if {![info exists treediffs([list $id $p])]} {
+ append diffsneeded "$id $p\n"
+ lappend fdiffsneeded [list $id $p]
+ }
+ }
+ }
+ if {[incr l] >= $numcommits} {
+ set l 0
+ }
+ if {$l == $findstartline} break
+ }
+
+ # start off a git-diff-tree process if needed
+ if {$diffsneeded ne {}} {
+ if {[catch {
+ set df [open [list | git-diff-tree -r --stdin << $diffsneeded] r]
+ } err ]} {
+ error_popup "Error starting search process: $err"
+ return
+ }
+ catch {unset fdiffids}
+ set fdiffpos 0
+ fconfigure $df -blocking 0
+ fileevent $df readable [list readfilediffs $df]
+ }
+
+ set finddidsel 0
+ set findinsertpos end
set id $lineid($l)
set p [lindex $parents($id) 0]
. config -cursor watch
$ctext config -cursor watch
set findinprogress 1
- update
findcont [list $id $p]
+ update
+}
+
+proc readfilediffs {df} {
+ global findids fdiffids fdiffs
+
+ set n [gets $df line]
+ if {$n < 0} {
+ if {[eof $df]} {
+ donefilediff
+ if {[catch {close $df} err]} {
+ stopfindproc
+ bell
+ error_popup "Error in git-diff-tree: $err"
+ } elseif {[info exists findids]} {
+ set ids $findids
+ stopfindproc
+ bell
+ error_popup "Couldn't find diffs for {$ids}"
+ }
+ }
+ return
+ }
+ if {[regexp {^([0-9a-f]{40}) \(from ([0-9a-f]{40})\)} $line match id p]} {
+ # start of a new string of diffs
+ donefilediff
+ set fdiffids [list $id $p]
+ set fdiffs {}
+ } elseif {[string match ":*" $line]} {
+ lappend fdiffs [lindex $line 5]
+ }
+}
+
+proc donefilediff {} {
+ global fdiffids fdiffs treediffs findids
+ global fdiffsneeded fdiffpos
+
+ if {[info exists fdiffids]} {
+ while {[lindex $fdiffsneeded $fdiffpos] ne $fdiffids
+ && $fdiffpos < [llength $fdiffsneeded]} {
+ # git-diff-tree doesn't output anything for a commit
+ # which doesn't change anything
+ set nullids [lindex $fdiffsneeded $fdiffpos]
+ set treediffs($nullids) {}
+ if {[info exists findids] && $nullids eq $findids} {
+ unset findids
+ findcont $nullids
+ }
+ incr fdiffpos
+ }
+ incr fdiffpos
+
+ if {![info exists treediffs($fdiffids)]} {
+ set treediffs($fdiffids) $fdiffs
+ }
+ if {[info exists findids] && $fdiffids eq $findids} {
+ unset findids
+ findcont $fdiffids
+ }
+ }
}
proc findcont {ids} {
if {![info exists treediffs($ids)]} {
set findids $ids
set ffileline $l
- if {![info exists treepending]} {
- gettreediffs $ids
- }
return
}
set doesmatch 0
}
}
if {$doesmatch} {
- lappend matchinglines $l
- markheadline $l $id
- if {!$finddidsel} {
- findselectline $l
- set finddidsel 1
- }
+ insertmatch $l $id
set pi $nparents($id)
}
} else {
global canv canv2 canv3 ctext commitinfo selectedline
global lineid linehtag linentag linedtag
global canvy0 linespc parents nparents
- global cflist currentid sha1entry diffids
- global commentend seenfile idtags
+ global cflist currentid sha1entry
+ global commentend idtags
$canv delete hover
if {![info exists lineid($l)] || ![info exists linehtag($l)]} return
$canv delete secsel
set id $lineid($l)
set currentid $id
- set diffids [concat $id $parents($id)]
$sha1entry delete 0 end
$sha1entry insert 0 $id
$sha1entry selection from 0
$cflist delete 0 end
$cflist insert end "Comments"
- if {$nparents($id) == 1} {
- startdiff
- }
- catch {unset seenfile}
+ startdiff $id $parents($id)
}
-proc startdiff {} {
+proc startdiff {id vs} {
+ global diffpending diffpindex
+ global diffindex difffilestart
+ global curdifftag curtagstart
+
+ set diffpending $vs
+ set diffpindex 0
+ set diffindex 0
+ catch {unset difffilestart}
+ set curdifftag Comments
+ set curtagstart 0.0
+ contdiff [list $id [lindex $vs 0]]
+}
+
+proc contdiff {ids} {
global treediffs diffids treepending
- if {![info exists treediffs($diffids)]} {
+ set diffids $ids
+ if {![info exists treediffs($ids)]} {
if {![info exists treepending]} {
- gettreediffs $diffids
+ gettreediffs $ids
}
} else {
- addtocflist $diffids
+ addtocflist $ids
}
}
}
proc addtocflist {ids} {
- global treediffs cflist
+ global treediffs cflist diffpindex
+
+ set colors {black blue green red cyan magenta}
+ set color [lindex $colors [expr {$diffpindex % [llength $colors]}]]
foreach f $treediffs($ids) {
$cflist insert end $f
+ $cflist itemconf end -foreground $color
}
getblobdiffs $ids
}
}
proc gettreediffline {gdtf ids} {
- global treediffs treepending diffids findids
+ global treediffs treepending diffids
set n [gets $gdtf line]
if {$n < 0} {
if {![eof $gdtf]} return
addtocflist $ids
}
}
- if {[info exists findids]} {
- if {$ids != $findids} {
- if {![info exists treepending]} {
- gettreediffs $findids
- }
- } else {
- findcont $ids
- }
- }
return
}
set file [lindex $line 5]
}
proc getblobdiffs {ids} {
- global diffopts blobdifffd env curdifftag curtagstart
- global diffindex difffilestart nextupdate
+ global diffopts blobdifffd diffids env
+ global nextupdate diffinhdr
set id [lindex $ids 0]
set p [lindex $ids 1]
puts "error getting diffs: $err"
return
}
+ set diffinhdr 0
fconfigure $bdf -blocking 0
set blobdifffd($ids) $bdf
- set curdifftag Comments
- set curtagstart 0.0
- set diffindex 0
- catch {unset difffilestart}
- fileevent $bdf readable "getblobdiffline $bdf {$ids}"
+ fileevent $bdf readable [list getblobdiffline $bdf $ids]
set nextupdate [expr {[clock clicks -milliseconds] + 100}]
}
proc getblobdiffline {bdf ids} {
- global diffids blobdifffd ctext curdifftag curtagstart seenfile
+ global diffids blobdifffd ctext curdifftag curtagstart
global diffnexthead diffnextnote diffindex difffilestart
- global nextupdate
+ global nextupdate diffpending diffpindex diffinhdr
+ global gaudydiff
set n [gets $bdf line]
if {$n < 0} {
if {[eof $bdf]} {
close $bdf
- if {[info exists diffids] && $ids == $diffids
- && $bdf == $blobdifffd($ids)} {
+ if {$ids == $diffids && $bdf == $blobdifffd($ids)} {
$ctext tag add $curdifftag $curtagstart end
- set seenfile($curdifftag) 1
- unset diffids
+ if {[incr diffpindex] < [llength $diffpending]} {
+ set id [lindex $ids 0]
+ set p [lindex $diffpending $diffpindex]
+ contdiff [list $id $p]
+ }
}
}
return
}
- if {![info exists diffids] || $ids != $diffids
- || $bdf != $blobdifffd($ids)} {
+ if {$ids != $diffids || $bdf != $blobdifffd($ids)} {
return
}
$ctext conf -state normal
- if {[regexp {^---[ \t]+([^/])*/(.*)} $line match s1 fname]} {
+ if {[regexp {^diff --git a/(.*) b/} $line match fname]} {
# start of a new file
$ctext insert end "\n"
$ctext tag add $curdifftag $curtagstart end
- set seenfile($curdifftag) 1
set curtagstart [$ctext index "end - 1c"]
set header $fname
- if {[info exists diffnexthead]} {
- set fname $diffnexthead
- set header "$diffnexthead ($diffnextnote)"
- unset diffnexthead
- }
set here [$ctext index "end - 1c"]
set difffilestart($diffindex) $here
incr diffindex
set l [expr {(78 - [string length $header]) / 2}]
set pad [string range "----------------------------------------" 1 $l]
$ctext insert end "$pad $header $pad\n" filesep
- } elseif {[string range $line 0 2] == "+++"} {
- # no need to do anything with this
- } elseif {[regexp {^Created: (.*) \((mode: *[0-7]*)\)} $line match fn m]} {
- set diffnexthead $fn
- set diffnextnote "created, mode $m"
- } elseif {[string range $line 0 8] == "Deleted: "} {
- set diffnexthead [string range $line 9 end]
- set diffnextnote "deleted"
- } elseif {[regexp {^diff --git a/(.*) b/} $line match fn]} {
- # save the filename in case the next thing is "new file mode ..."
- set diffnexthead $fn
- set diffnextnote "modified"
- } elseif {[regexp {^new file mode ([0-7]+)} $line match m]} {
- set diffnextnote "new file, mode $m"
- } elseif {[string range $line 0 11] == "deleted file"} {
- set diffnextnote "deleted"
+ 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]} {
- $ctext insert end "\t" hunksep
- $ctext insert end " $f1l " d0 " $f2l " d1
- $ctext insert end " $rest \n" hunksep
+ 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 == "+"}]
- set line [string range $line 1 end]
+ if {$gaudydiff} {
+ set line [string range $line 1 end]
+ }
$ctext insert end "$line\n" d$tag
} elseif {$x == " "} {
- set line [string range $line 1 end]
+ if {$gaudydiff} {
+ set line [string range $line 1 end]
+ }
$ctext insert end "$line\n"
- } elseif {$x == "\\"} {
+ } elseif {$diffinhdr || $x == "\\"} {
# e.g. "\ No newline at end of file"
$ctext insert end "$line\n" filesep
} else {
if {$curdifftag != "Comments"} {
$ctext insert end "\n"
$ctext tag add $curdifftag $curtagstart end
- set seenfile($curdifftag) 1
set curtagstart [$ctext index "end - 1c"]
set curdifftag Comments
}
}
proc listboxsel {} {
- global ctext cflist currentid treediffs seenfile
+ global ctext cflist currentid treediffs
if {![info exists currentid]} return
set sel [lsort [$cflist curselection]]
if {$sel eq {}} return
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)
return
}
- if {[regexp {^[0-9a-fA-F]{40}$} $sha1string]} {
+ if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} {
set type "SHA1 id"
} else {
set type "Tag"
proc diffvssel {dirn} {
global rowmenuid selectedline lineid
global ctext cflist
- global diffids commitinfo
+ global commitinfo
if {![info exists selectedline]} return
if {$dirn} {
$ctext conf -state disabled
$ctext tag delete Comments
$ctext tag remove found 1.0 end
- set diffids [list $newid $oldid]
- startdiff
+ startdiff [list $newid $oldid]
}
proc mkpatch {} {
set mainfont {Helvetica 9}
set textfont {Courier 9}
set findmergefiles 0
+set gaudydiff 0
set colors {green red blue magenta darkgrey brown orange}