proc updatecommits {} {
global viewdata curview phase displayorder
- global children commitrow selectedline thickerline
+ global children commitrow selectedline thickerline showneartags
if {$phase ne {}} {
stop_rev_list
catch {unset viewdata($n)}
readrefs
changedrefs
- regetallcommits
+ if {$showneartags} {
+ getallcommits
+ }
showview $n
}
lappend idotherrefs($id) $name
}
}
- close $refd
+ catch {close $refd}
set mainhead {}
set mainheadid {}
catch {
global textfont mainfont uifont tabstop
global findtype findtypemenu findloc findstring fstring geometry
global entries sha1entry sha1string sha1but
+ global diffcontextstring diffcontext
global maincursor textcursor curtextcursor
global rowctxmenu fakerowmenu mergemax wrapcomment
global highlight_files gdttype
menu .bar.file
.bar.file add command -label "Update" -command updatecommits
.bar.file add command -label "Reread references" -command rereadrefs
+ .bar.file add command -label "List references" -command showrefs
.bar.file add command -label "Quit" -command doquit
.bar.file configure -font $uifont
menu .bar.edit
-command changediffdisp -variable diffelide -value {0 1}
radiobutton .bleft.mid.new -text "New version" \
-command changediffdisp -variable diffelide -value {1 0}
+ label .bleft.mid.labeldiffcontext -text " Lines of context: " \
+ -font $uifont
pack .bleft.mid.diff .bleft.mid.old .bleft.mid.new -side left
+ spinbox .bleft.mid.diffcontext -width 5 -font $textfont \
+ -from 1 -increment 1 -to 10000000 \
+ -validate all -validatecommand "diffcontextvalidate %P" \
+ -textvariable diffcontextstring
+ .bleft.mid.diffcontext set $diffcontext
+ trace add variable diffcontextstring write diffcontextchange
+ lappend entries .bleft.mid.diffcontext
+ pack .bleft.mid.labeldiffcontext .bleft.mid.diffcontext -side left
set ctext .bleft.ctext
text $ctext -background $bgcolor -foreground $fgcolor \
-tabs "[expr {$tabstop * $charspc}]" \
pack .ctop -fill both -expand 1
bindall <1> {selcanvline %W %x %y}
#bindall <B1-Motion> {selcanvline %W %x %y}
- bindall <ButtonRelease-4> "allcanvs yview scroll -5 units"
- bindall <ButtonRelease-5> "allcanvs yview scroll 5 units"
+ if {[tk windowingsystem] == "win32"} {
+ bind . <MouseWheel> { windows_mousewheel_redirector %W %X %Y %D }
+ bind $ctext <MouseWheel> { windows_mousewheel_redirector %W %X %Y %D ; break }
+ } else {
+ bindall <ButtonRelease-4> "allcanvs yview scroll -5 units"
+ bindall <ButtonRelease-5> "allcanvs yview scroll 5 units"
+ }
bindall <2> "canvscan mark %W %x %y"
bindall <B2-Motion> "canvscan dragto %W %x %y"
bindkey <Home> selfirstline
bind $cflist <1> {sel_flist %W %x %y; break}
bind $cflist <B1-Motion> {sel_flist %W %x %y; break}
bind $cflist <ButtonRelease-1> {treeclick %W %x %y}
+ bind $cflist <Button-3> {pop_flist_menu %W %X %Y %x %y}
set maincursor [. cget -cursor]
set textcursor [$ctext cget -cursor]
-command cobranch
$headctxmenu add command -label "Remove this branch" \
-command rmbranch
+
+ global flist_menu
+ set flist_menu .flistctxmenu
+ menu $flist_menu -tearoff 0
+ $flist_menu add command -label "Highlight this too" \
+ -command {flist_hl 0}
+ $flist_menu add command -label "Highlight this only" \
+ -command {flist_hl 1}
+}
+
+# Windows sends all mouse wheel events to the current focused window, not
+# the one where the mouse hovers, so bind those events here and redirect
+# to the correct window
+proc windows_mousewheel_redirector {W X Y D} {
+ global canv canv2 canv3
+ set w [winfo containing -displayof $W $X $Y]
+ if {$w ne ""} {
+ set u [expr {$D < 0 ? 5 : -5}]
+ if {$w == $canv || $w == $canv2 || $w == $canv3} {
+ allcanvs yview scroll $u units
+ } else {
+ catch {
+ $w yview scroll $u units
+ }
+ }
+ }
}
# mouse-2 makes all windows scan vertically, but only the one
# set the focus back to the toplevel for any click outside
# the entry widgets
proc click {w} {
- global entries
- foreach e $entries {
+ global ctext entries
+ foreach e [concat $entries $ctext] {
if {$w == $e} return
}
focus .
global stuffsaved findmergefiles maxgraphpct
global maxwidth showneartags showlocalchanges
global viewname viewfiles viewargs viewperm nextviewnum
- global cmitmode wrapcomment
- global colors bgcolor fgcolor diffcolors selectbgcolor
+ global cmitmode wrapcomment datetimeformat
+ global colors bgcolor fgcolor diffcolors diffcontext selectbgcolor
if {$stuffsaved} return
if {![winfo viewable .]} return
puts $f [list set wrapcomment $wrapcomment]
puts $f [list set showneartags $showneartags]
puts $f [list set showlocalchanges $showlocalchanges]
+ puts $f [list set datetimeformat $datetimeformat]
puts $f [list set bgcolor $bgcolor]
puts $f [list set fgcolor $fgcolor]
puts $f [list set colors $colors]
puts $f [list set diffcolors $diffcolors]
+ puts $f [list set diffcontext $diffcontext]
puts $f [list set selectbgcolor $selectbgcolor]
puts $f "set geometry(main) [wm geometry .]"
0x00, 0x00};
}
+image create bitmap reficon-T -background black -foreground yellow -data {
+ #define tagicon_width 13
+ #define tagicon_height 9
+ static unsigned char tagicon_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0xf0, 0x07, 0xf8, 0x07,
+ 0xfc, 0x07, 0xf8, 0x07, 0xf0, 0x07, 0x00, 0x00, 0x00, 0x00};
+} -maskdata {
+ #define tagicon-mask_width 13
+ #define tagicon-mask_height 9
+ static unsigned char tagicon-mask_bits[] = {
+ 0x00, 0x00, 0xf0, 0x0f, 0xf8, 0x0f, 0xfc, 0x0f,
+ 0xfe, 0x0f, 0xfc, 0x0f, 0xf8, 0x0f, 0xf0, 0x0f, 0x00, 0x00};
+}
+set rectdata {
+ #define headicon_width 13
+ #define headicon_height 9
+ static unsigned char headicon_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0xf8, 0x07,
+ 0xf8, 0x07, 0xf8, 0x07, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00};
+}
+set rectmask {
+ #define headicon-mask_width 13
+ #define headicon-mask_height 9
+ static unsigned char headicon-mask_bits[] = {
+ 0x00, 0x00, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f,
+ 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0x00, 0x00};
+}
+image create bitmap reficon-H -background black -foreground green \
+ -data $rectdata -maskdata $rectmask
+image create bitmap reficon-o -background black -foreground "#ddddff" \
+ -data $rectdata -maskdata $rectmask
+
proc init_flist {first} {
global cflist cflist_top selectedline difffilestart
}
}
+proc pop_flist_menu {w X Y x y} {
+ global ctext cflist cmitmode flist_menu flist_menu_file
+ global treediffs diffids
+
+ set l [lindex [split [$w index "@$x,$y"] "."] 0]
+ if {$l <= 1} return
+ if {$cmitmode eq "tree"} {
+ set e [linetoelt $l]
+ if {[string index $e end] eq "/"} return
+ } else {
+ set e [lindex $treediffs($diffids) [expr {$l-2}]]
+ }
+ set flist_menu_file $e
+ tk_popup $flist_menu $X $Y
+}
+
+proc flist_hl {only} {
+ global flist_menu_file highlight_files
+
+ set x [shellquote $flist_menu_file]
+ if {$only || $highlight_files eq {}} {
+ set highlight_files $x
+ } else {
+ append highlight_files " " $x
+ }
+}
+
# Functions for adding and removing shell-type quoting
proc shellquote {str} {
} elseif {$numcommits == 0} {
show_status "No commits selected"
}
+ run refill_reflist
}
# Stuff relating to the highlighting facility
proc showstuff {canshow last} {
global numcommits commitrow pending_select selectedline curview
global lookingforhead mainheadid displayorder selectfirst
- global lastscrollset
+ global lastscrollset commitinterest
if {$numcommits == 0} {
global phase
set phase "incrdraw"
allcanvs delete all
}
+ for {set l $numcommits} {$l < $canshow} {incr l} {
+ set id [lindex $displayorder $l]
+ if {[info exists commitinterest($id)]} {
+ foreach script $commitinterest($id) {
+ eval [string map [list "%I" $id] $script]
+ }
+ unset commitinterest($id)
+ }
+ }
set r0 $numcommits
set prev $numcommits
set numcommits $canshow
set offs [lindex $rowoffsets $row]
while {$row < $endrow} {
set id [lindex $displayorder $row]
- set oldolds {}
- set newolds {}
+ set nev [expr {[llength $idlist] - $maxwidth + 1}]
foreach p [lindex $parentlist $row] {
- if {![info exists idinlist($p)]} {
- lappend newolds $p
- } elseif {!$idinlist($p)} {
- lappend oldolds $p
+ if {![info exists idinlist($p)] || !$idinlist($p)} {
+ incr nev
}
}
- set nev [expr {[llength $idlist] + [llength $newolds]
- + [llength $oldolds] - $maxwidth + 1}]
if {$nev > 0} {
if {!$last &&
$row + $uparrowlen + $mingaplen >= $commitidx($curview)} break
if {[incr nev -1] <= 0} break
continue
}
- set rowchk($id) [expr {$row + $r}]
+ set rowchk($i) [expr {$row + $r}]
}
}
lset rowidlist $row $idlist
lset rowoffsets $row $offs
}
+ set oldolds {}
+ set newolds {}
+ foreach p [lindex $parentlist $row] {
+ if {![info exists idinlist($p)]} {
+ lappend newolds $p
+ } elseif {!$idinlist($p)} {
+ lappend oldolds $p
+ }
+ set idinlist($p) 1
+ }
set col [lsearch -exact $idlist $id]
if {$col < 0} {
set col [llength $idlist]
lset offs $col {}
}
foreach i $newolds {
- set idinlist($i) 1
set idrowranges($i) $id
}
incr col $l
foreach oid $oldolds {
- set idinlist($oid) 1
set idlist [linsert $idlist $col $oid]
set offs [linsert $offs $col $o]
makeuparrow $oid $col $row $o
set col [expr {[llength $idlist] - 1}]
set id [lindex $idlist $col]
addextraid $id $row
- unset idinlist($id)
+ catch {unset idinlist($id)}
lappend idrowranges($id) $id
lappend rowrangelist $idrowranges($id)
unset idrowranges($id)
$canv delete hover
normalline
cancel_next_highlight
+ unsel_reflist
if {$l < 0 || $l >= $numcommits} return
set y [expr {$canvy0 + $l * $linespc}]
set ymax [lindex [$canv cget -scrollregion] 3]
proc selnextline {dir} {
global selectedline
+ focus .
if {![info exists selectedline]} return
set l [expr {$selectedline + $dir}]
unmarkmatches
proc goback {} {
global history historyindex
+ focus .
if {$historyindex > 1} {
incr historyindex -1
proc goforw {} {
global history historyindex
+ focus .
if {$historyindex < [llength $history]} {
set cmd [lindex $history $historyindex]
return 0
}
+# empty string or positive integer
+proc diffcontextvalidate {v} {
+ return [regexp {^(|[1-9][0-9]*)$} $v]
+}
+
+proc diffcontextchange {n1 n2 op} {
+ global diffcontextstring diffcontext
+
+ if {[string is integer -strict $diffcontextstring]} {
+ if {$diffcontextstring > 0} {
+ set diffcontext $diffcontextstring
+ reselectline
+ }
+ }
+}
+
proc getblobdiffs {ids} {
global diffopts blobdifffd diffids env
global diffinhdr treediffs
+ global diffcontext
set env(GIT_DIFF_OPTS) $diffopts
- if {[catch {set bdf [open [diffcmd $ids {-p -C --no-commit-id}] r]} err]} {
+ if {[catch {set bdf [open [diffcmd $ids "-p -C --no-commit-id -U$diffcontext"] r]} err]} {
puts "error getting diffs: $err"
return
}
# the middle char will be a space, and the two bits either
# side will be a/name and b/name, or "a/name" and "b/name".
# If the name has changed we'll get "rename from" and
- # "rename to" lines following this, and we'll use them
- # to get the filenames.
+ # "rename to" or "copy from" and "copy to" lines following this,
+ # and we'll use them to get the filenames.
# This complexity is necessary because spaces in the filename(s)
# don't get escaped.
set l [string length $line]
set diffinhdr 0
} elseif {$diffinhdr} {
- if {![string compare -length 12 "rename from " $line]} {
- set fname [string range $line 12 end]
+ if {![string compare -length 12 "rename from " $line] ||
+ ![string compare -length 10 "copy from " $line]} {
+ set fname [string range $line [expr 6 + [string first " from " $line] ] end]
if {[string index $fname 0] eq "\""} {
set fname [lindex $fname 0]
}
if {$i >= 0} {
setinlist difffilestart $i $curdiffstart
}
- } elseif {![string compare -length 10 $line "rename to "]} {
- set fname [string range $line 10 end]
+ } elseif {![string compare -length 10 $line "rename to "] ||
+ ![string compare -length 8 $line "copy to "]} {
+ set fname [string range $line [expr 4 + [string first " to " $line] ] end]
if {[string index $fname 0] eq "\""} {
set fname [lindex $fname 0]
}
}
proc incrfont {inc} {
- global mainfont textfont ctext canv phase cflist
+ global mainfont textfont ctext canv phase cflist showrefstop
global charspc tabstop
global stopped entries
unmarkmatches
if {$phase eq "getcommits"} {
$canv itemconf textitems -font $mainfont
}
+ if {[info exists showrefstop] && [winfo exists $showrefstop]} {
+ $showrefstop.list conf -font $mainfont
+ }
redisplay
}
lappend idtags($id) $tag
redrawtags $id
addedtag $id
+ dispneartags 0
+ run refill_reflist
}
proc redrawtags {id} {
notbusy newbranch
redrawtags $id
dispneartags 0
+ run refill_reflist
}
}
proc rmbranch {} {
global headmenuid headmenuhead mainhead
- global headids idheads
+ global idheads
set head $headmenuhead
set id $headmenuid
return
}
set dheads [descheads $id]
- if {$dheads eq $headids($head)} {
+ if {[llength $dheads] == 1 && $idheads($dheads) eq $head} {
# the stuff on this branch isn't on any other branch
if {![confirm_popup "The commits on branch $head aren't on any other\
branch.\nReally delete branch $head?"]} return
redrawtags $id
notbusy rmbranch
dispneartags 0
+ run refill_reflist
+}
+
+# Display a list of tags and heads
+proc showrefs {} {
+ global showrefstop bgcolor fgcolor selectbgcolor mainfont
+ global bglist fglist uifont reflistfilter reflist maincursor
+
+ set top .showrefs
+ set showrefstop $top
+ if {[winfo exists $top]} {
+ raise $top
+ refill_reflist
+ return
+ }
+ toplevel $top
+ wm title $top "Tags and heads: [file tail [pwd]]"
+ text $top.list -background $bgcolor -foreground $fgcolor \
+ -selectbackground $selectbgcolor -font $mainfont \
+ -xscrollcommand "$top.xsb set" -yscrollcommand "$top.ysb set" \
+ -width 30 -height 20 -cursor $maincursor \
+ -spacing1 1 -spacing3 1 -state disabled
+ $top.list tag configure highlight -background $selectbgcolor
+ lappend bglist $top.list
+ lappend fglist $top.list
+ scrollbar $top.ysb -command "$top.list yview" -orient vertical
+ scrollbar $top.xsb -command "$top.list xview" -orient horizontal
+ grid $top.list $top.ysb -sticky nsew
+ grid $top.xsb x -sticky ew
+ frame $top.f
+ label $top.f.l -text "Filter: " -font $uifont
+ entry $top.f.e -width 20 -textvariable reflistfilter -font $uifont
+ set reflistfilter "*"
+ trace add variable reflistfilter write reflistfilter_change
+ pack $top.f.e -side right -fill x -expand 1
+ pack $top.f.l -side left
+ grid $top.f - -sticky ew -pady 2
+ button $top.close -command [list destroy $top] -text "Close" \
+ -font $uifont
+ grid $top.close -
+ grid columnconfigure $top 0 -weight 1
+ grid rowconfigure $top 0 -weight 1
+ bind $top.list <1> {break}
+ bind $top.list <B1-Motion> {break}
+ bind $top.list <ButtonRelease-1> {sel_reflist %W %x %y; break}
+ set reflist {}
+ refill_reflist
+}
+
+proc sel_reflist {w x y} {
+ global showrefstop reflist headids tagids otherrefids
+
+ if {![winfo exists $showrefstop]} return
+ set l [lindex [split [$w index "@$x,$y"] "."] 0]
+ set ref [lindex $reflist [expr {$l-1}]]
+ set n [lindex $ref 0]
+ switch -- [lindex $ref 1] {
+ "H" {selbyid $headids($n)}
+ "T" {selbyid $tagids($n)}
+ "o" {selbyid $otherrefids($n)}
+ }
+ $showrefstop.list tag add highlight $l.0 "$l.0 lineend"
+}
+
+proc unsel_reflist {} {
+ global showrefstop
+
+ if {![info exists showrefstop] || ![winfo exists $showrefstop]} return
+ $showrefstop.list tag remove highlight 0.0 end
+}
+
+proc reflistfilter_change {n1 n2 op} {
+ global reflistfilter
+
+ after cancel refill_reflist
+ after 200 refill_reflist
+}
+
+proc refill_reflist {} {
+ global reflist reflistfilter showrefstop headids tagids otherrefids
+ global commitrow curview commitinterest
+
+ if {![info exists showrefstop] || ![winfo exists $showrefstop]} return
+ set refs {}
+ foreach n [array names headids] {
+ if {[string match $reflistfilter $n]} {
+ if {[info exists commitrow($curview,$headids($n))]} {
+ lappend refs [list $n H]
+ } else {
+ set commitinterest($headids($n)) {run refill_reflist}
+ }
+ }
+ }
+ foreach n [array names tagids] {
+ if {[string match $reflistfilter $n]} {
+ if {[info exists commitrow($curview,$tagids($n))]} {
+ lappend refs [list $n T]
+ } else {
+ set commitinterest($tagids($n)) {run refill_reflist}
+ }
+ }
+ }
+ foreach n [array names otherrefids] {
+ if {[string match $reflistfilter $n]} {
+ if {[info exists commitrow($curview,$otherrefids($n))]} {
+ lappend refs [list $n o]
+ } else {
+ set commitinterest($otherrefids($n)) {run refill_reflist}
+ }
+ }
+ }
+ set refs [lsort -index 0 $refs]
+ if {$refs eq $reflist} return
+
+ # Update the contents of $showrefstop.list according to the
+ # differences between $reflist (old) and $refs (new)
+ $showrefstop.list conf -state normal
+ $showrefstop.list insert end "\n"
+ set i 0
+ set j 0
+ while {$i < [llength $reflist] || $j < [llength $refs]} {
+ if {$i < [llength $reflist]} {
+ if {$j < [llength $refs]} {
+ set cmp [string compare [lindex $reflist $i 0] \
+ [lindex $refs $j 0]]
+ if {$cmp == 0} {
+ set cmp [string compare [lindex $reflist $i 1] \
+ [lindex $refs $j 1]]
+ }
+ } else {
+ set cmp -1
+ }
+ } else {
+ set cmp 1
+ }
+ switch -- $cmp {
+ -1 {
+ $showrefstop.list delete "[expr {$j+1}].0" "[expr {$j+2}].0"
+ incr i
+ }
+ 0 {
+ incr i
+ incr j
+ }
+ 1 {
+ set l [expr {$j + 1}]
+ $showrefstop.list image create $l.0 -align baseline \
+ -image reficon-[lindex $refs $j 1] -padx 2
+ $showrefstop.list insert $l.1 "[lindex $refs $j 0]\n"
+ incr j
+ }
+ }
+ }
+ set reflist $refs
+ # delete last newline
+ $showrefstop.list delete end-2c end-1c
+ $showrefstop.list conf -state disabled
}
# Stuff for finding nearby tags
proc getallcommits {} {
global allcommits allids nbmp nextarc seeds
- set allids {}
- set nbmp 0
- set nextarc 0
- set allcommits 0
- set seeds {}
- regetallcommits
-}
-
-# Called when the graph might have changed
-proc regetallcommits {} {
- global allcommits seeds
+ if {![info exists allcommits]} {
+ set allids {}
+ set nbmp 0
+ set nextarc 0
+ set allcommits 0
+ set seeds {}
+ }
set cmd [concat | git rev-list --all --parents]
foreach id $seeds {
proc addnewchild {id p} {
global allids allparents allchildren idtags nextarc nbmp
global arcnos arcids arctags arcout arcend arcstart archeads growing
- global seeds
+ global seeds allcommits
+ if {![info exists allcommits]} return
lappend allids $id
set allparents($id) [list $p]
set allchildren($id) {}
redrawtags $id
}
}
+ run refill_reflist
}
proc listrefs {id} {
}
proc formatdate {d} {
+ global datetimeformat
if {$d ne {}} {
- set d [clock format $d -format "%Y-%m-%d %H:%M:%S"]
+ set d [clock format $d -format $datetimeformat]
}
return $d
}
set maxrefs 20
set maxlinelen 200
set showlocalchanges 1
+set datetimeformat "%Y-%m-%d %H:%M:%S"
set colors {green red blue magenta darkgrey brown orange}
set bgcolor white
set fgcolor black
set diffcolors {red "#00a000" blue}
+set diffcontext 3
set selectbgcolor gray85
catch {source ~/.gitk}
font create optionfont -family sans-serif -size -12
# check that we can find a .git directory somewhere...
-set gitdir [gitdir]
+if {[catch {set gitdir [gitdir]}]} {
+ show_error {} . "Cannot find a git repository here."
+ exit 1
+}
if {![file isdirectory $gitdir]} {
show_error {} . "Cannot find the git directory \"$gitdir\"."
exit 1