##// END OF EJS Templates
hgk: display committer name when set by hg-git
Andrew Shadura -
r24604:ce8dd4fd default
parent child Browse files
Show More
@@ -1,4152 +1,4155
1 #!/usr/bin/env wish
1 #!/usr/bin/env wish
2
2
3 # Copyright (C) 2005 Paul Mackerras. All rights reserved.
3 # Copyright (C) 2005 Paul Mackerras. All rights reserved.
4 # This program is free software; it may be used, copied, modified
4 # This program is free software; it may be used, copied, modified
5 # and distributed under the terms of the GNU General Public Licence,
5 # and distributed under the terms of the GNU General Public Licence,
6 # either version 2, or (at your option) any later version.
6 # either version 2, or (at your option) any later version.
7 #
7 #
8 # See hgk.py for extension usage and configuration.
8 # See hgk.py for extension usage and configuration.
9
9
10
10
11 # Modified version of Tip 171:
11 # Modified version of Tip 171:
12 # http://www.tcl.tk/cgi-bin/tct/tip/171.html
12 # http://www.tcl.tk/cgi-bin/tct/tip/171.html
13 #
13 #
14 # The in_mousewheel global was added to fix strange reentrancy issues.
14 # The in_mousewheel global was added to fix strange reentrancy issues.
15 # The whole snipped is activated only under windows, mouse wheel
15 # The whole snipped is activated only under windows, mouse wheel
16 # bindings working already under MacOSX and Linux.
16 # bindings working already under MacOSX and Linux.
17
17
18 if {[catch {package require Ttk}]} {
18 if {[catch {package require Ttk}]} {
19 # use a shim
19 # use a shim
20 namespace eval ttk {
20 namespace eval ttk {
21 proc style args {}
21 proc style args {}
22
22
23 proc entry args {
23 proc entry args {
24 eval [linsert $args 0 ::entry] -relief flat
24 eval [linsert $args 0 ::entry] -relief flat
25 }
25 }
26 }
26 }
27
27
28 interp alias {} ttk::button {} button
28 interp alias {} ttk::button {} button
29 interp alias {} ttk::frame {} frame
29 interp alias {} ttk::frame {} frame
30 interp alias {} ttk::label {} label
30 interp alias {} ttk::label {} label
31 interp alias {} ttk::scrollbar {} scrollbar
31 interp alias {} ttk::scrollbar {} scrollbar
32 interp alias {} ttk::optionMenu {} tk_optionMenu
32 interp alias {} ttk::optionMenu {} tk_optionMenu
33
33
34 proc updatepalette {} {}
34 proc updatepalette {} {}
35 } else {
35 } else {
36 proc ::ttk::optionMenu {w varName firstValue args} {
36 proc ::ttk::optionMenu {w varName firstValue args} {
37 upvar #0 $varName var
37 upvar #0 $varName var
38
38
39 if {![info exists var]} {
39 if {![info exists var]} {
40 set var $firstValue
40 set var $firstValue
41 }
41 }
42 ttk::menubutton $w -textvariable $varName -menu $w.menu \
42 ttk::menubutton $w -textvariable $varName -menu $w.menu \
43 -direction flush
43 -direction flush
44 menu $w.menu -tearoff 0
44 menu $w.menu -tearoff 0
45 $w.menu add radiobutton -label $firstValue -variable $varName
45 $w.menu add radiobutton -label $firstValue -variable $varName
46 foreach i $args {
46 foreach i $args {
47 $w.menu add radiobutton -label $i -variable $varName
47 $w.menu add radiobutton -label $i -variable $varName
48 }
48 }
49 return $w.menu
49 return $w.menu
50 }
50 }
51 proc updatepalette {} {
51 proc updatepalette {} {
52 catch {
52 catch {
53 tk_setPalette background [ttk::style lookup client -background]
53 tk_setPalette background [ttk::style lookup client -background]
54 }
54 }
55 }
55 }
56 }
56 }
57
57
58 if {[tk windowingsystem] eq "win32"} {
58 if {[tk windowingsystem] eq "win32"} {
59
59
60 ttk::style theme use xpnative
60 ttk::style theme use xpnative
61
61
62 set mw_classes [list Text Listbox Table TreeCtrl]
62 set mw_classes [list Text Listbox Table TreeCtrl]
63 foreach class $mw_classes { bind $class <MouseWheel> {} }
63 foreach class $mw_classes { bind $class <MouseWheel> {} }
64
64
65 set in_mousewheel 0
65 set in_mousewheel 0
66
66
67 proc ::tk::MouseWheel {wFired X Y D {shifted 0}} {
67 proc ::tk::MouseWheel {wFired X Y D {shifted 0}} {
68 global in_mousewheel
68 global in_mousewheel
69 if { $in_mousewheel != 0 } { return }
69 if { $in_mousewheel != 0 } { return }
70 # Set event to check based on call
70 # Set event to check based on call
71 set evt "<[expr {$shifted?{Shift-}:{}}]MouseWheel>"
71 set evt "<[expr {$shifted?{Shift-}:{}}]MouseWheel>"
72 # do not double-fire in case the class already has a binding
72 # do not double-fire in case the class already has a binding
73 if {[bind [winfo class $wFired] $evt] ne ""} { return }
73 if {[bind [winfo class $wFired] $evt] ne ""} { return }
74 # obtain the window the mouse is over
74 # obtain the window the mouse is over
75 set w [winfo containing $X $Y]
75 set w [winfo containing $X $Y]
76 # if we are outside the app, try and scroll the focus widget
76 # if we are outside the app, try and scroll the focus widget
77 if {![winfo exists $w]} { catch {set w [focus]} }
77 if {![winfo exists $w]} { catch {set w [focus]} }
78 if {[winfo exists $w]} {
78 if {[winfo exists $w]} {
79
79
80 if {[bind $w $evt] ne ""} {
80 if {[bind $w $evt] ne ""} {
81 # Awkward ... this widget has a MouseWheel binding, but to
81 # Awkward ... this widget has a MouseWheel binding, but to
82 # trigger successfully in it, we must give it focus.
82 # trigger successfully in it, we must give it focus.
83 catch {focus} old
83 catch {focus} old
84 if {$w ne $old} { focus $w }
84 if {$w ne $old} { focus $w }
85 set in_mousewheel 1
85 set in_mousewheel 1
86 event generate $w $evt -rootx $X -rooty $Y -delta $D
86 event generate $w $evt -rootx $X -rooty $Y -delta $D
87 set in_mousewheel 0
87 set in_mousewheel 0
88 if {$w ne $old} { focus $old }
88 if {$w ne $old} { focus $old }
89 return
89 return
90 }
90 }
91
91
92 # aqua and x11/win32 have different delta handling
92 # aqua and x11/win32 have different delta handling
93 if {[tk windowingsystem] ne "aqua"} {
93 if {[tk windowingsystem] ne "aqua"} {
94 set delta [expr {- ($D / 30)}]
94 set delta [expr {- ($D / 30)}]
95 } else {
95 } else {
96 set delta [expr {- ($D)}]
96 set delta [expr {- ($D)}]
97 }
97 }
98 # scrollbars have different call conventions
98 # scrollbars have different call conventions
99 if {[string match "*Scrollbar" [winfo class $w]]} {
99 if {[string match "*Scrollbar" [winfo class $w]]} {
100 catch {tk::ScrollByUnits $w \
100 catch {tk::ScrollByUnits $w \
101 [string index [$w cget -orient] 0] $delta}
101 [string index [$w cget -orient] 0] $delta}
102 } else {
102 } else {
103 set cmd [list $w [expr {$shifted ? "xview" : "yview"}] \
103 set cmd [list $w [expr {$shifted ? "xview" : "yview"}] \
104 scroll $delta units]
104 scroll $delta units]
105 # Walking up to find the proper widget (handles cases like
105 # Walking up to find the proper widget (handles cases like
106 # embedded widgets in a canvas)
106 # embedded widgets in a canvas)
107 while {[catch $cmd] && [winfo toplevel $w] ne $w} {
107 while {[catch $cmd] && [winfo toplevel $w] ne $w} {
108 set w [winfo parent $w]
108 set w [winfo parent $w]
109 }
109 }
110 }
110 }
111 }
111 }
112 }
112 }
113
113
114 bind all <MouseWheel> [list ::tk::MouseWheel %W %X %Y %D 0]
114 bind all <MouseWheel> [list ::tk::MouseWheel %W %X %Y %D 0]
115
115
116 # end of win32 section
116 # end of win32 section
117 } else {
117 } else {
118
118
119 if {[catch {
119 if {[catch {
120 set theme [ttk::style theme use]
120 set theme [ttk::style theme use]
121 }]} {
121 }]} {
122 set theme $::ttk::currentTheme
122 set theme $::ttk::currentTheme
123 }
123 }
124 if {$theme eq "default"} {
124 if {$theme eq "default"} {
125 ttk::style theme use clam
125 ttk::style theme use clam
126 }
126 }
127
127
128 }
128 }
129
129
130 updatepalette
130 updatepalette
131
131
132 # Unify right mouse button handling.
132 # Unify right mouse button handling.
133 # See "mouse buttons on macintosh" thread on comp.lang.tcl
133 # See "mouse buttons on macintosh" thread on comp.lang.tcl
134 if {[tk windowingsystem] eq "aqua"} {
134 if {[tk windowingsystem] eq "aqua"} {
135 event add <<B3>> <Control-ButtonPress-1>
135 event add <<B3>> <Control-ButtonPress-1>
136 event add <<B3>> <Button-2>
136 event add <<B3>> <Button-2>
137 } else {
137 } else {
138 event add <<B3>> <Button-3>
138 event add <<B3>> <Button-3>
139 }
139 }
140
140
141 proc gitdir {} {
141 proc gitdir {} {
142 global env
142 global env
143 if {[info exists env(GIT_DIR)]} {
143 if {[info exists env(GIT_DIR)]} {
144 return $env(GIT_DIR)
144 return $env(GIT_DIR)
145 } else {
145 } else {
146 return ".hg"
146 return ".hg"
147 }
147 }
148 }
148 }
149
149
150 proc popupify {w} {
150 proc popupify {w} {
151 wm resizable $w 0 0
151 wm resizable $w 0 0
152 wm withdraw $w
152 wm withdraw $w
153 update
153 update
154 set x [expr {([winfo screenwidth .]-[winfo reqwidth $w])/2}]
154 set x [expr {([winfo screenwidth .]-[winfo reqwidth $w])/2}]
155 set y [expr {([winfo screenheight .]-[winfo reqheight $w])/2}]
155 set y [expr {([winfo screenheight .]-[winfo reqheight $w])/2}]
156 wm geometry $w +$x+$y
156 wm geometry $w +$x+$y
157 wm transient $w .
157 wm transient $w .
158 wm deiconify $w
158 wm deiconify $w
159 wm resizable $w 1 1
159 wm resizable $w 1 1
160 }
160 }
161
161
162 proc getcommits {rargs} {
162 proc getcommits {rargs} {
163 global commits commfd phase canv mainfont env
163 global commits commfd phase canv mainfont env
164 global startmsecs nextupdate ncmupdate
164 global startmsecs nextupdate ncmupdate
165 global ctext maincursor textcursor leftover
165 global ctext maincursor textcursor leftover
166
166
167 # check that we can find a .git directory somewhere...
167 # check that we can find a .git directory somewhere...
168 set gitdir [gitdir]
168 set gitdir [gitdir]
169 if {![file isdirectory $gitdir]} {
169 if {![file isdirectory $gitdir]} {
170 error_popup "Cannot find the git directory \"$gitdir\"."
170 error_popup "Cannot find the git directory \"$gitdir\"."
171 exit 1
171 exit 1
172 }
172 }
173 set commits {}
173 set commits {}
174 set phase getcommits
174 set phase getcommits
175 set startmsecs [clock clicks -milliseconds]
175 set startmsecs [clock clicks -milliseconds]
176 set nextupdate [expr $startmsecs + 100]
176 set nextupdate [expr $startmsecs + 100]
177 set ncmupdate 1
177 set ncmupdate 1
178 set limit 0
178 set limit 0
179 set revargs {}
179 set revargs {}
180 set showhidden no
180 set showhidden no
181 for {set i 0} {$i < [llength $rargs]} {incr i} {
181 for {set i 0} {$i < [llength $rargs]} {incr i} {
182 set opt [lindex $rargs $i]
182 set opt [lindex $rargs $i]
183 switch -- $opt --limit {
183 switch -- $opt --limit {
184 incr i
184 incr i
185 set limit [lindex $rargs $i]
185 set limit [lindex $rargs $i]
186 } --hidden {
186 } --hidden {
187 set showhidden yes
187 set showhidden yes
188 } default {
188 } default {
189 lappend revargs $opt
189 lappend revargs $opt
190 }
190 }
191 }
191 }
192 if [catch {
192 if [catch {
193 set parse_args [concat tip $revargs]
193 set parse_args [concat tip $revargs]
194 set parse_temp [eval exec {$env(HG)} --config ui.report_untrusted=false log --template '{node}\n' $parse_args]
194 set parse_temp [eval exec {$env(HG)} --config ui.report_untrusted=false log --template '{node}\n' $parse_args]
195 regsub -all "\r\n" $parse_temp "\n" parse_temp
195 regsub -all "\r\n" $parse_temp "\n" parse_temp
196 set parsed_args [split $parse_temp "\n"]
196 set parsed_args [split $parse_temp "\n"]
197 } err] {
197 } err] {
198 # if git-rev-parse failed for some reason...
198 # if git-rev-parse failed for some reason...
199 if {$rargs == {}} {
199 if {$rargs == {}} {
200 set revargs HEAD
200 set revargs HEAD
201 }
201 }
202 set parsed_args $revargs
202 set parsed_args $revargs
203 }
203 }
204 if {$limit > 0} {
204 if {$limit > 0} {
205 set parsed_args [concat -n $limit $parsed_args]
205 set parsed_args [concat -n $limit $parsed_args]
206 }
206 }
207 if {$showhidden} {
207 if {$showhidden} {
208 append parsed_args --hidden
208 append parsed_args --hidden
209 }
209 }
210 if [catch {
210 if [catch {
211 set commfd [open "|{$env(HG)} --config ui.report_untrusted=false debug-rev-list --header --topo-order --parents $parsed_args" r]
211 set commfd [open "|{$env(HG)} --config ui.report_untrusted=false debug-rev-list --header --topo-order --parents $parsed_args" r]
212 } err] {
212 } err] {
213 puts stderr "Error executing hg debug-rev-list: $err"
213 puts stderr "Error executing hg debug-rev-list: $err"
214 exit 1
214 exit 1
215 }
215 }
216 set leftover {}
216 set leftover {}
217 fconfigure $commfd -blocking 0 -translation lf -eofchar {}
217 fconfigure $commfd -blocking 0 -translation lf -eofchar {}
218 fileevent $commfd readable [list getcommitlines $commfd]
218 fileevent $commfd readable [list getcommitlines $commfd]
219 $canv delete all
219 $canv delete all
220 $canv create text 3 3 -anchor nw -text "Reading commits..." \
220 $canv create text 3 3 -anchor nw -text "Reading commits..." \
221 -font $mainfont -tags textitems
221 -font $mainfont -tags textitems
222 . config -cursor watch
222 . config -cursor watch
223 settextcursor watch
223 settextcursor watch
224 }
224 }
225
225
226 proc getcommitlines {commfd} {
226 proc getcommitlines {commfd} {
227 global commits parents cdate children
227 global commits parents cdate children
228 global commitlisted phase commitinfo nextupdate
228 global commitlisted phase commitinfo nextupdate
229 global stopped redisplaying leftover
229 global stopped redisplaying leftover
230
230
231 set stuff [read $commfd]
231 set stuff [read $commfd]
232 if {$stuff == {}} {
232 if {$stuff == {}} {
233 if {![eof $commfd]} return
233 if {![eof $commfd]} return
234 # set it blocking so we wait for the process to terminate
234 # set it blocking so we wait for the process to terminate
235 fconfigure $commfd -blocking 1
235 fconfigure $commfd -blocking 1
236 if {![catch {close $commfd} err]} {
236 if {![catch {close $commfd} err]} {
237 after idle finishcommits
237 after idle finishcommits
238 return
238 return
239 }
239 }
240 if {[string range $err 0 4] == "usage"} {
240 if {[string range $err 0 4] == "usage"} {
241 set err \
241 set err \
242 {Gitk: error reading commits: bad arguments to git-rev-list.
242 {Gitk: error reading commits: bad arguments to git-rev-list.
243 (Note: arguments to gitk are passed to git-rev-list
243 (Note: arguments to gitk are passed to git-rev-list
244 to allow selection of commits to be displayed.)}
244 to allow selection of commits to be displayed.)}
245 } else {
245 } else {
246 set err "Error reading commits: $err"
246 set err "Error reading commits: $err"
247 }
247 }
248 error_popup $err
248 error_popup $err
249 exit 1
249 exit 1
250 }
250 }
251 set start 0
251 set start 0
252 while 1 {
252 while 1 {
253 set i [string first "\0" $stuff $start]
253 set i [string first "\0" $stuff $start]
254 if {$i < 0} {
254 if {$i < 0} {
255 append leftover [string range $stuff $start end]
255 append leftover [string range $stuff $start end]
256 return
256 return
257 }
257 }
258 set cmit [string range $stuff $start [expr {$i - 1}]]
258 set cmit [string range $stuff $start [expr {$i - 1}]]
259 if {$start == 0} {
259 if {$start == 0} {
260 set cmit "$leftover$cmit"
260 set cmit "$leftover$cmit"
261 set leftover {}
261 set leftover {}
262 }
262 }
263 set start [expr {$i + 1}]
263 set start [expr {$i + 1}]
264 regsub -all "\r\n" $cmit "\n" cmit
264 regsub -all "\r\n" $cmit "\n" cmit
265 set j [string first "\n" $cmit]
265 set j [string first "\n" $cmit]
266 set ok 0
266 set ok 0
267 if {$j >= 0} {
267 if {$j >= 0} {
268 set ids [string range $cmit 0 [expr {$j - 1}]]
268 set ids [string range $cmit 0 [expr {$j - 1}]]
269 set ok 1
269 set ok 1
270 foreach id $ids {
270 foreach id $ids {
271 if {![regexp {^[0-9a-f]{12}$} $id]} {
271 if {![regexp {^[0-9a-f]{12}$} $id]} {
272 set ok 0
272 set ok 0
273 break
273 break
274 }
274 }
275 }
275 }
276 }
276 }
277 if {!$ok} {
277 if {!$ok} {
278 set shortcmit $cmit
278 set shortcmit $cmit
279 if {[string length $shortcmit] > 80} {
279 if {[string length $shortcmit] > 80} {
280 set shortcmit "[string range $shortcmit 0 80]..."
280 set shortcmit "[string range $shortcmit 0 80]..."
281 }
281 }
282 error_popup "Can't parse hg debug-rev-list output: {$shortcmit}"
282 error_popup "Can't parse hg debug-rev-list output: {$shortcmit}"
283 exit 1
283 exit 1
284 }
284 }
285 set id [lindex $ids 0]
285 set id [lindex $ids 0]
286 set olds [lrange $ids 1 end]
286 set olds [lrange $ids 1 end]
287 set cmit [string range $cmit [expr {$j + 1}] end]
287 set cmit [string range $cmit [expr {$j + 1}] end]
288 lappend commits $id
288 lappend commits $id
289 set commitlisted($id) 1
289 set commitlisted($id) 1
290 parsecommit $id $cmit 1 [lrange $ids 1 end]
290 parsecommit $id $cmit 1 [lrange $ids 1 end]
291 drawcommit $id
291 drawcommit $id
292 if {[clock clicks -milliseconds] >= $nextupdate} {
292 if {[clock clicks -milliseconds] >= $nextupdate} {
293 doupdate 1
293 doupdate 1
294 }
294 }
295 while {$redisplaying} {
295 while {$redisplaying} {
296 set redisplaying 0
296 set redisplaying 0
297 if {$stopped == 1} {
297 if {$stopped == 1} {
298 set stopped 0
298 set stopped 0
299 set phase "getcommits"
299 set phase "getcommits"
300 foreach id $commits {
300 foreach id $commits {
301 drawcommit $id
301 drawcommit $id
302 if {$stopped} break
302 if {$stopped} break
303 if {[clock clicks -milliseconds] >= $nextupdate} {
303 if {[clock clicks -milliseconds] >= $nextupdate} {
304 doupdate 1
304 doupdate 1
305 }
305 }
306 }
306 }
307 }
307 }
308 }
308 }
309 }
309 }
310 }
310 }
311
311
312 proc doupdate {reading} {
312 proc doupdate {reading} {
313 global commfd nextupdate numcommits ncmupdate
313 global commfd nextupdate numcommits ncmupdate
314
314
315 if {$reading} {
315 if {$reading} {
316 fileevent $commfd readable {}
316 fileevent $commfd readable {}
317 }
317 }
318 update
318 update
319 set nextupdate [expr {[clock clicks -milliseconds] + 100}]
319 set nextupdate [expr {[clock clicks -milliseconds] + 100}]
320 if {$numcommits < 100} {
320 if {$numcommits < 100} {
321 set ncmupdate [expr {$numcommits + 1}]
321 set ncmupdate [expr {$numcommits + 1}]
322 } elseif {$numcommits < 10000} {
322 } elseif {$numcommits < 10000} {
323 set ncmupdate [expr {$numcommits + 10}]
323 set ncmupdate [expr {$numcommits + 10}]
324 } else {
324 } else {
325 set ncmupdate [expr {$numcommits + 100}]
325 set ncmupdate [expr {$numcommits + 100}]
326 }
326 }
327 if {$reading} {
327 if {$reading} {
328 fileevent $commfd readable [list getcommitlines $commfd]
328 fileevent $commfd readable [list getcommitlines $commfd]
329 }
329 }
330 }
330 }
331
331
332 proc readcommit {id} {
332 proc readcommit {id} {
333 global env
333 global env
334 if [catch {set contents [exec $env(HG) --config ui.report_untrusted=false debug-cat-file commit $id]}] return
334 if [catch {set contents [exec $env(HG) --config ui.report_untrusted=false debug-cat-file commit $id]}] return
335 parsecommit $id $contents 0 {}
335 parsecommit $id $contents 0 {}
336 }
336 }
337
337
338 proc parsecommit {id contents listed olds} {
338 proc parsecommit {id contents listed olds} {
339 global commitinfo children nchildren parents nparents cdate ncleft
339 global commitinfo children nchildren parents nparents cdate ncleft
340 global firstparents obsolete
340 global firstparents obsolete
341
341
342 set inhdr 1
342 set inhdr 1
343 set comment {}
343 set comment {}
344 set headline {}
344 set headline {}
345 set auname {}
345 set auname {}
346 set audate {}
346 set audate {}
347 set comname {}
347 set comname {}
348 set comdate {}
348 set comdate {}
349 set rev {}
349 set rev {}
350 set branch {}
350 set branch {}
351 set bookmark {}
351 set bookmark {}
352 if {![info exists nchildren($id)]} {
352 if {![info exists nchildren($id)]} {
353 set children($id) {}
353 set children($id) {}
354 set nchildren($id) 0
354 set nchildren($id) 0
355 set ncleft($id) 0
355 set ncleft($id) 0
356 }
356 }
357 set parents($id) $olds
357 set parents($id) $olds
358 set nparents($id) [llength $olds]
358 set nparents($id) [llength $olds]
359 foreach p $olds {
359 foreach p $olds {
360 if {![info exists nchildren($p)]} {
360 if {![info exists nchildren($p)]} {
361 set children($p) [list $id]
361 set children($p) [list $id]
362 set nchildren($p) 1
362 set nchildren($p) 1
363 set ncleft($p) 1
363 set ncleft($p) 1
364 } elseif {[lsearch -exact $children($p) $id] < 0} {
364 } elseif {[lsearch -exact $children($p) $id] < 0} {
365 lappend children($p) $id
365 lappend children($p) $id
366 incr nchildren($p)
366 incr nchildren($p)
367 incr ncleft($p)
367 incr ncleft($p)
368 }
368 }
369 }
369 }
370 regsub -all "\r\n" $contents "\n" contents
370 regsub -all "\r\n" $contents "\n" contents
371 foreach line [split $contents "\n"] {
371 foreach line [split $contents "\n"] {
372 if {$inhdr} {
372 if {$inhdr} {
373 set line [split $line]
373 set line [split $line]
374 if {$line == {}} {
374 if {$line == {}} {
375 set inhdr 0
375 set inhdr 0
376 } else {
376 } else {
377 set tag [lindex $line 0]
377 set tag [lindex $line 0]
378 switch -- $tag "author" {
378 switch -- $tag "author" {
379 set x [expr {[llength $line] - 2}]
379 set x [expr {[llength $line] - 2}]
380 set audate [lindex $line $x]
380 set audate [lindex $line $x]
381 set auname [join [lrange $line 1 [expr {$x - 1}]]]
381 set auname [join [lrange $line 1 [expr {$x - 1}]]]
382 } "committer" {
382 } "committer" {
383 set x [expr {[llength $line] - 2}]
383 set x [expr {[llength $line] - 2}]
384 set comdate [lindex $line $x]
384 set comdate [lindex $line $x]
385 set comname [join [lrange $line 1 [expr {$x - 1}]]]
385 set comname [join [lrange $line 1 [expr {$x - 1}]]]
386 } "revision" {
386 } "revision" {
387 set rev [lindex $line 1]
387 set rev [lindex $line 1]
388 } "branch" {
388 } "branch" {
389 set branch [join [lrange $line 1 end]]
389 set branch [join [lrange $line 1 end]]
390 } "bookmark" {
390 } "bookmark" {
391 set bookmark [join [lrange $line 1 end]]
391 set bookmark [join [lrange $line 1 end]]
392 } "obsolete" {
392 } "obsolete" {
393 set obsolete($id) ""
393 set obsolete($id) ""
394 } "phase" {
394 } "phase" {
395 set phase [lindex $line 1 end]
395 set phase [lindex $line 1 end]
396 }
396 }
397 }
397 }
398 } else {
398 } else {
399 if {$comment == {}} {
399 if {$comment == {}} {
400 set headline [string trim $line]
400 set headline [string trim $line]
401 } else {
401 } else {
402 append comment "\n"
402 append comment "\n"
403 }
403 }
404 if {!$listed} {
404 if {!$listed} {
405 # git-rev-list indents the comment by 4 spaces;
405 # git-rev-list indents the comment by 4 spaces;
406 # if we got this via git-cat-file, add the indentation
406 # if we got this via git-cat-file, add the indentation
407 append comment " "
407 append comment " "
408 }
408 }
409 append comment $line
409 append comment $line
410 }
410 }
411 }
411 }
412 if {$audate != {}} {
412 if {$audate != {}} {
413 set audate [clock format $audate]
413 set audate [clock format $audate]
414 }
414 }
415 if {$comdate != {}} {
415 if {$comdate != {}} {
416 set cdate($id) $comdate
416 set cdate($id) $comdate
417 set comdate [clock format $comdate]
417 set comdate [clock format $comdate]
418 }
418 }
419 set commitinfo($id) [list $headline $auname $audate \
419 set commitinfo($id) [list $headline $auname $audate \
420 $comname $comdate $comment $rev $branch $bookmark $phase]
420 $comname $comdate $comment $rev $branch $bookmark $phase]
421
421
422 if {[info exists firstparents]} {
422 if {[info exists firstparents]} {
423 set i [lsearch $firstparents $id]
423 set i [lsearch $firstparents $id]
424 if {$i != -1} {
424 if {$i != -1} {
425 # remove the parent from firstparents, possible building
425 # remove the parent from firstparents, possible building
426 # an empty list
426 # an empty list
427 set firstparents [concat \
427 set firstparents [concat \
428 [lrange $firstparents 0 [expr $i - 1]] \
428 [lrange $firstparents 0 [expr $i - 1]] \
429 [lrange $firstparents [expr $i + 1] end]]
429 [lrange $firstparents [expr $i + 1] end]]
430 if {$firstparents eq {}} {
430 if {$firstparents eq {}} {
431 # we have found all parents of the first changeset
431 # we have found all parents of the first changeset
432 # which means that we can safely select the first line
432 # which means that we can safely select the first line
433 after idle {
433 after idle {
434 selectline 0 0
434 selectline 0 0
435 }
435 }
436 }
436 }
437 }
437 }
438 } else {
438 } else {
439 # this is the first changeset, save the parents
439 # this is the first changeset, save the parents
440 set firstparents $olds
440 set firstparents $olds
441 if {$firstparents eq {}} {
441 if {$firstparents eq {}} {
442 # a repository with a single changeset
442 # a repository with a single changeset
443 after idle {
443 after idle {
444 selectline 0 0
444 selectline 0 0
445 }
445 }
446 }
446 }
447 }
447 }
448 }
448 }
449
449
450 proc readrefs {} {
450 proc readrefs {} {
451 global bookmarkcurrent bookmarkids tagids idtags idbookmarks headids idheads tagcontents env curid
451 global bookmarkcurrent bookmarkids tagids idtags idbookmarks headids idheads tagcontents env curid
452
452
453 set status [catch {exec $env(HG) --config ui.report_untrusted=false id} curid]
453 set status [catch {exec $env(HG) --config ui.report_untrusted=false id} curid]
454 if { $status != 0 } {
454 if { $status != 0 } {
455 puts $::errorInfo
455 puts $::errorInfo
456 if { ![string equal $::errorCode NONE] } {
456 if { ![string equal $::errorCode NONE] } {
457 exit 2
457 exit 2
458 }
458 }
459 }
459 }
460 regexp -- {[[:xdigit:]]+} $curid curid
460 regexp -- {[[:xdigit:]]+} $curid curid
461
461
462 set status [catch {exec $env(HG) --config ui.report_untrusted=false tags} tags]
462 set status [catch {exec $env(HG) --config ui.report_untrusted=false tags} tags]
463 if { $status != 0 } {
463 if { $status != 0 } {
464 puts $::errorInfo
464 puts $::errorInfo
465 if { ![string equal $::errorCode NONE] } {
465 if { ![string equal $::errorCode NONE] } {
466 exit 2
466 exit 2
467 }
467 }
468 }
468 }
469
469
470 foreach {- tag rev id} [regexp -inline -all -line {^(.+\S)\s+(\d+):(\S+)} $tags] {
470 foreach {- tag rev id} [regexp -inline -all -line {^(.+\S)\s+(\d+):(\S+)} $tags] {
471 # we use foreach as Tcl8.4 doesn't support lassign
471 # we use foreach as Tcl8.4 doesn't support lassign
472 lappend tagids($tag) $id
472 lappend tagids($tag) $id
473 lappend idtags($id) $tag
473 lappend idtags($id) $tag
474 }
474 }
475
475
476 set status [catch {exec $env(HG) --config ui.report_untrusted=false heads} heads]
476 set status [catch {exec $env(HG) --config ui.report_untrusted=false heads} heads]
477 if { $status != 0 } {
477 if { $status != 0 } {
478 puts $::errorInfo
478 puts $::errorInfo
479 if { ![string equal $::errorCode NONE] } {
479 if { ![string equal $::errorCode NONE] } {
480 exit 2
480 exit 2
481 }
481 }
482 }
482 }
483
483
484 set lines [split $heads \r\n]
484 set lines [split $heads \r\n]
485 foreach f $lines {
485 foreach f $lines {
486 set match ""
486 set match ""
487 regexp {changeset:\s+(\S+):(\S+)$} $f match id sha
487 regexp {changeset:\s+(\S+):(\S+)$} $f match id sha
488 if {$match != ""} {
488 if {$match != ""} {
489 lappend idheads($sha) $id
489 lappend idheads($sha) $id
490 }
490 }
491 }
491 }
492
492
493 set status [catch {exec $env(HG) --config ui.report_untrusted=false bookmarks} bookmarks]
493 set status [catch {exec $env(HG) --config ui.report_untrusted=false bookmarks} bookmarks]
494 if { $status != 0 } {
494 if { $status != 0 } {
495 puts $::errorInfo
495 puts $::errorInfo
496 if { ![string equal $::errorCode NONE] } {
496 if { ![string equal $::errorCode NONE] } {
497 exit 2
497 exit 2
498 }
498 }
499 }
499 }
500 set lines [split $bookmarks "\n"]
500 set lines [split $bookmarks "\n"]
501 set bookmarkcurrent 0
501 set bookmarkcurrent 0
502 foreach f $lines {
502 foreach f $lines {
503 regexp {(\S+)$} $f full
503 regexp {(\S+)$} $f full
504 regsub {\s+(\S+)$} $f "" direct
504 regsub {\s+(\S+)$} $f "" direct
505 set sha [split $full ':']
505 set sha [split $full ':']
506 set bookmark [lindex $sha 1]
506 set bookmark [lindex $sha 1]
507 set current [string first " * " $direct)]
507 set current [string first " * " $direct)]
508 regsub {^\s(\*|\s)\s} $direct "" direct
508 regsub {^\s(\*|\s)\s} $direct "" direct
509 lappend bookmarkids($direct) $bookmark
509 lappend bookmarkids($direct) $bookmark
510 lappend idbookmarks($bookmark) $direct
510 lappend idbookmarks($bookmark) $direct
511 if {$current >= 0} {
511 if {$current >= 0} {
512 set bookmarkcurrent $direct
512 set bookmarkcurrent $direct
513 }
513 }
514 }
514 }
515 }
515 }
516
516
517 proc readotherrefs {base dname excl} {
517 proc readotherrefs {base dname excl} {
518 global otherrefids idotherrefs
518 global otherrefids idotherrefs
519
519
520 set git [gitdir]
520 set git [gitdir]
521 set files [glob -nocomplain -types f [file join $git $base *]]
521 set files [glob -nocomplain -types f [file join $git $base *]]
522 foreach f $files {
522 foreach f $files {
523 catch {
523 catch {
524 set fd [open $f r]
524 set fd [open $f r]
525 set line [read $fd 40]
525 set line [read $fd 40]
526 if {[regexp {^[0-9a-f]{12}} $line id]} {
526 if {[regexp {^[0-9a-f]{12}} $line id]} {
527 set name "$dname[file tail $f]"
527 set name "$dname[file tail $f]"
528 set otherrefids($name) $id
528 set otherrefids($name) $id
529 lappend idotherrefs($id) $name
529 lappend idotherrefs($id) $name
530 }
530 }
531 close $fd
531 close $fd
532 }
532 }
533 }
533 }
534 set dirs [glob -nocomplain -types d [file join $git $base *]]
534 set dirs [glob -nocomplain -types d [file join $git $base *]]
535 foreach d $dirs {
535 foreach d $dirs {
536 set dir [file tail $d]
536 set dir [file tail $d]
537 if {[lsearch -exact $excl $dir] >= 0} continue
537 if {[lsearch -exact $excl $dir] >= 0} continue
538 readotherrefs [file join $base $dir] "$dname$dir/" {}
538 readotherrefs [file join $base $dir] "$dname$dir/" {}
539 }
539 }
540 }
540 }
541
541
542 proc allcansmousewheel {delta} {
542 proc allcansmousewheel {delta} {
543 set delta [expr -5*(int($delta)/abs($delta))]
543 set delta [expr -5*(int($delta)/abs($delta))]
544 allcanvs yview scroll $delta units
544 allcanvs yview scroll $delta units
545 }
545 }
546
546
547 proc error_popup msg {
547 proc error_popup msg {
548 set w .error
548 set w .error
549 toplevel $w
549 toplevel $w
550 wm transient $w .
550 wm transient $w .
551 message $w.m -text $msg -justify center -aspect 400
551 message $w.m -text $msg -justify center -aspect 400
552 pack $w.m -side top -fill x -padx 20 -pady 20
552 pack $w.m -side top -fill x -padx 20 -pady 20
553 ttk::button $w.ok -text OK -command "destroy $w"
553 ttk::button $w.ok -text OK -command "destroy $w"
554 pack $w.ok -side bottom -fill x
554 pack $w.ok -side bottom -fill x
555 bind $w <Visibility> "grab $w; focus $w"
555 bind $w <Visibility> "grab $w; focus $w"
556 popupify $w
556 popupify $w
557 tkwait window $w
557 tkwait window $w
558 }
558 }
559
559
560 proc makewindow {} {
560 proc makewindow {} {
561 global canv canv2 canv3 linespc charspc ctext cflist textfont
561 global canv canv2 canv3 linespc charspc ctext cflist textfont
562 global findtype findtypemenu findloc findstring fstring geometry
562 global findtype findtypemenu findloc findstring fstring geometry
563 global entries sha1entry sha1string sha1but
563 global entries sha1entry sha1string sha1but
564 global maincursor textcursor curtextcursor
564 global maincursor textcursor curtextcursor
565 global rowctxmenu gaudydiff mergemax
565 global rowctxmenu gaudydiff mergemax
566 global hgvdiff bgcolor fgcolor diffremcolor diffaddcolor diffmerge1color
566 global hgvdiff bgcolor fgcolor diffremcolor diffaddcolor diffmerge1color
567 global diffmerge2color hunksepcolor
567 global diffmerge2color hunksepcolor
568 global posx posy
568 global posx posy
569
569
570 if {[info exists posx]} {
570 if {[info exists posx]} {
571 wm geometry . +$posx+$posy
571 wm geometry . +$posx+$posy
572 }
572 }
573
573
574 menu .bar
574 menu .bar
575 .bar add cascade -label "File" -menu .bar.file
575 .bar add cascade -label "File" -menu .bar.file
576 menu .bar.file
576 menu .bar.file
577 .bar.file add command -label "Reread references" -command rereadrefs
577 .bar.file add command -label "Reread references" -command rereadrefs
578 .bar.file add command -label "Quit" -command doquit
578 .bar.file add command -label "Quit" -command doquit
579 menu .bar.help
579 menu .bar.help
580 .bar add cascade -label "Help" -menu .bar.help
580 .bar add cascade -label "Help" -menu .bar.help
581 .bar.help add command -label "About hgk" -command about
581 .bar.help add command -label "About hgk" -command about
582 . configure -menu .bar
582 . configure -menu .bar
583
583
584 if {![info exists geometry(canv1)]} {
584 if {![info exists geometry(canv1)]} {
585 set geometry(canv1) [expr 45 * $charspc]
585 set geometry(canv1) [expr 45 * $charspc]
586 set geometry(canv2) [expr 30 * $charspc]
586 set geometry(canv2) [expr 30 * $charspc]
587 set geometry(canv3) [expr 15 * $charspc]
587 set geometry(canv3) [expr 15 * $charspc]
588 set geometry(canvh) [expr 25 * $linespc + 4]
588 set geometry(canvh) [expr 25 * $linespc + 4]
589 set geometry(ctextw) 80
589 set geometry(ctextw) 80
590 set geometry(ctexth) 30
590 set geometry(ctexth) 30
591 set geometry(cflistw) 30
591 set geometry(cflistw) 30
592 }
592 }
593 panedwindow .ctop -orient vertical
593 panedwindow .ctop -orient vertical
594 if {[info exists geometry(width)]} {
594 if {[info exists geometry(width)]} {
595 .ctop conf -width $geometry(width) -height $geometry(height)
595 .ctop conf -width $geometry(width) -height $geometry(height)
596 set texth [expr {$geometry(height) - $geometry(canvh) - 56}]
596 set texth [expr {$geometry(height) - $geometry(canvh) - 56}]
597 set geometry(ctexth) [expr {($texth - 8) /
597 set geometry(ctexth) [expr {($texth - 8) /
598 [font metrics $textfont -linespace]}]
598 [font metrics $textfont -linespace]}]
599 }
599 }
600 ttk::frame .ctop.top
600 ttk::frame .ctop.top
601 ttk::frame .ctop.top.bar
601 ttk::frame .ctop.top.bar
602 pack .ctop.top.bar -side bottom -fill x
602 pack .ctop.top.bar -side bottom -fill x
603 set cscroll .ctop.top.csb
603 set cscroll .ctop.top.csb
604 ttk::scrollbar $cscroll -command {allcanvs yview}
604 ttk::scrollbar $cscroll -command {allcanvs yview}
605 pack $cscroll -side right -fill y
605 pack $cscroll -side right -fill y
606 panedwindow .ctop.top.clist -orient horizontal -sashpad 0 -handlesize 4
606 panedwindow .ctop.top.clist -orient horizontal -sashpad 0 -handlesize 4
607 pack .ctop.top.clist -side top -fill both -expand 1
607 pack .ctop.top.clist -side top -fill both -expand 1
608 .ctop add .ctop.top
608 .ctop add .ctop.top
609 set canv .ctop.top.clist.canv
609 set canv .ctop.top.clist.canv
610 canvas $canv -height $geometry(canvh) -width $geometry(canv1) \
610 canvas $canv -height $geometry(canvh) -width $geometry(canv1) \
611 -bg $bgcolor -bd 0 \
611 -bg $bgcolor -bd 0 \
612 -yscrollincr $linespc -yscrollcommand "$cscroll set" -selectbackground "#c0c0c0"
612 -yscrollincr $linespc -yscrollcommand "$cscroll set" -selectbackground "#c0c0c0"
613 .ctop.top.clist add $canv
613 .ctop.top.clist add $canv
614 set canv2 .ctop.top.clist.canv2
614 set canv2 .ctop.top.clist.canv2
615 canvas $canv2 -height $geometry(canvh) -width $geometry(canv2) \
615 canvas $canv2 -height $geometry(canvh) -width $geometry(canv2) \
616 -bg $bgcolor -bd 0 -yscrollincr $linespc -selectbackground "#c0c0c0"
616 -bg $bgcolor -bd 0 -yscrollincr $linespc -selectbackground "#c0c0c0"
617 .ctop.top.clist add $canv2
617 .ctop.top.clist add $canv2
618 set canv3 .ctop.top.clist.canv3
618 set canv3 .ctop.top.clist.canv3
619 canvas $canv3 -height $geometry(canvh) -width $geometry(canv3) \
619 canvas $canv3 -height $geometry(canvh) -width $geometry(canv3) \
620 -bg $bgcolor -bd 0 -yscrollincr $linespc -selectbackground "#c0c0c0"
620 -bg $bgcolor -bd 0 -yscrollincr $linespc -selectbackground "#c0c0c0"
621 .ctop.top.clist add $canv3
621 .ctop.top.clist add $canv3
622 bind .ctop.top.clist <Configure> {resizeclistpanes %W %w}
622 bind .ctop.top.clist <Configure> {resizeclistpanes %W %w}
623
623
624 set sha1entry .ctop.top.bar.sha1
624 set sha1entry .ctop.top.bar.sha1
625 set entries $sha1entry
625 set entries $sha1entry
626 set sha1but .ctop.top.bar.sha1label
626 set sha1but .ctop.top.bar.sha1label
627 button $sha1but -text "SHA1 ID: " -state disabled -relief flat \
627 button $sha1but -text "SHA1 ID: " -state disabled -relief flat \
628 -command gotocommit -width 8
628 -command gotocommit -width 8
629 $sha1but conf -disabledforeground [$sha1but cget -foreground]
629 $sha1but conf -disabledforeground [$sha1but cget -foreground]
630 pack .ctop.top.bar.sha1label -side left
630 pack .ctop.top.bar.sha1label -side left
631 ttk::entry $sha1entry -width 40 -font $textfont -textvariable sha1string
631 ttk::entry $sha1entry -width 40 -font $textfont -textvariable sha1string
632 trace add variable sha1string write sha1change
632 trace add variable sha1string write sha1change
633 pack $sha1entry -side left -pady 2
633 pack $sha1entry -side left -pady 2
634
634
635 image create bitmap bm-left -data {
635 image create bitmap bm-left -data {
636 #define left_width 16
636 #define left_width 16
637 #define left_height 16
637 #define left_height 16
638 static unsigned char left_bits[] = {
638 static unsigned char left_bits[] = {
639 0x00, 0x00, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00,
639 0x00, 0x00, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00,
640 0x0e, 0x00, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x0e, 0x00, 0x1c, 0x00,
640 0x0e, 0x00, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x0e, 0x00, 0x1c, 0x00,
641 0x38, 0x00, 0x70, 0x00, 0xe0, 0x00, 0xc0, 0x01};
641 0x38, 0x00, 0x70, 0x00, 0xe0, 0x00, 0xc0, 0x01};
642 }
642 }
643 image create bitmap bm-right -data {
643 image create bitmap bm-right -data {
644 #define right_width 16
644 #define right_width 16
645 #define right_height 16
645 #define right_height 16
646 static unsigned char right_bits[] = {
646 static unsigned char right_bits[] = {
647 0x00, 0x00, 0xc0, 0x01, 0x80, 0x03, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x1c,
647 0x00, 0x00, 0xc0, 0x01, 0x80, 0x03, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x1c,
648 0x00, 0x38, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x00, 0x38, 0x00, 0x1c,
648 0x00, 0x38, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x00, 0x38, 0x00, 0x1c,
649 0x00, 0x0e, 0x00, 0x07, 0x80, 0x03, 0xc0, 0x01};
649 0x00, 0x0e, 0x00, 0x07, 0x80, 0x03, 0xc0, 0x01};
650 }
650 }
651 ttk::button .ctop.top.bar.leftbut -image bm-left -command goback \
651 ttk::button .ctop.top.bar.leftbut -image bm-left -command goback \
652 -state disabled -width 26
652 -state disabled -width 26
653 pack .ctop.top.bar.leftbut -side left -fill y
653 pack .ctop.top.bar.leftbut -side left -fill y
654 ttk::button .ctop.top.bar.rightbut -image bm-right -command goforw \
654 ttk::button .ctop.top.bar.rightbut -image bm-right -command goforw \
655 -state disabled -width 26
655 -state disabled -width 26
656 pack .ctop.top.bar.rightbut -side left -fill y
656 pack .ctop.top.bar.rightbut -side left -fill y
657
657
658 ttk::button .ctop.top.bar.findbut -text "Find" -command dofind
658 ttk::button .ctop.top.bar.findbut -text "Find" -command dofind
659 pack .ctop.top.bar.findbut -side left
659 pack .ctop.top.bar.findbut -side left
660 set findstring {}
660 set findstring {}
661 set fstring .ctop.top.bar.findstring
661 set fstring .ctop.top.bar.findstring
662 lappend entries $fstring
662 lappend entries $fstring
663 ttk::entry $fstring -width 30 -font $textfont -textvariable findstring
663 ttk::entry $fstring -width 30 -font $textfont -textvariable findstring
664 pack $fstring -side left -expand 1 -fill x
664 pack $fstring -side left -expand 1 -fill x
665 set findtype Exact
665 set findtype Exact
666 set findtypemenu [ttk::optionMenu .ctop.top.bar.findtype \
666 set findtypemenu [ttk::optionMenu .ctop.top.bar.findtype \
667 findtype Exact IgnCase Regexp]
667 findtype Exact IgnCase Regexp]
668 set findloc "All fields"
668 set findloc "All fields"
669 ttk::optionMenu .ctop.top.bar.findloc findloc "All fields" Headline \
669 ttk::optionMenu .ctop.top.bar.findloc findloc "All fields" Headline \
670 Comments Author Files Pickaxe
670 Comments Author Files Pickaxe
671 pack .ctop.top.bar.findloc -side right
671 pack .ctop.top.bar.findloc -side right
672 pack .ctop.top.bar.findtype -side right
672 pack .ctop.top.bar.findtype -side right
673 # for making sure type==Exact whenever loc==Pickaxe
673 # for making sure type==Exact whenever loc==Pickaxe
674 trace add variable findloc write findlocchange
674 trace add variable findloc write findlocchange
675
675
676 panedwindow .ctop.cdet -orient horizontal
676 panedwindow .ctop.cdet -orient horizontal
677 .ctop add .ctop.cdet
677 .ctop add .ctop.cdet
678 ttk::frame .ctop.cdet.left
678 ttk::frame .ctop.cdet.left
679 set ctext .ctop.cdet.left.ctext
679 set ctext .ctop.cdet.left.ctext
680 text $ctext -fg $fgcolor -bg $bgcolor -state disabled -font $textfont \
680 text $ctext -fg $fgcolor -bg $bgcolor -state disabled -font $textfont \
681 -width $geometry(ctextw) -height $geometry(ctexth) \
681 -width $geometry(ctextw) -height $geometry(ctexth) \
682 -yscrollcommand ".ctop.cdet.left.sb set" \
682 -yscrollcommand ".ctop.cdet.left.sb set" \
683 -xscrollcommand ".ctop.cdet.left.hb set" -wrap none
683 -xscrollcommand ".ctop.cdet.left.hb set" -wrap none
684 ttk::scrollbar .ctop.cdet.left.sb -command "$ctext yview"
684 ttk::scrollbar .ctop.cdet.left.sb -command "$ctext yview"
685 ttk::scrollbar .ctop.cdet.left.hb -orient horizontal -command "$ctext xview"
685 ttk::scrollbar .ctop.cdet.left.hb -orient horizontal -command "$ctext xview"
686 pack .ctop.cdet.left.sb -side right -fill y
686 pack .ctop.cdet.left.sb -side right -fill y
687 pack .ctop.cdet.left.hb -side bottom -fill x
687 pack .ctop.cdet.left.hb -side bottom -fill x
688 pack $ctext -side left -fill both -expand 1
688 pack $ctext -side left -fill both -expand 1
689 .ctop.cdet add .ctop.cdet.left
689 .ctop.cdet add .ctop.cdet.left
690
690
691 $ctext tag conf filesep -font [concat $textfont bold] -back "#aaaaaa"
691 $ctext tag conf filesep -font [concat $textfont bold] -back "#aaaaaa"
692 if {$gaudydiff} {
692 if {$gaudydiff} {
693 $ctext tag conf hunksep -back blue -fore white
693 $ctext tag conf hunksep -back blue -fore white
694 $ctext tag conf d0 -back "#ff8080"
694 $ctext tag conf d0 -back "#ff8080"
695 $ctext tag conf d1 -back green
695 $ctext tag conf d1 -back green
696 } else {
696 } else {
697 $ctext tag conf hunksep -fore $hunksepcolor
697 $ctext tag conf hunksep -fore $hunksepcolor
698 $ctext tag conf d0 -fore $diffremcolor
698 $ctext tag conf d0 -fore $diffremcolor
699 $ctext tag conf d1 -fore $diffaddcolor
699 $ctext tag conf d1 -fore $diffaddcolor
700
700
701 # The mX colours seem to be used in merge changesets, where m0
701 # The mX colours seem to be used in merge changesets, where m0
702 # is first parent, m1 is second parent and so on. Git can have
702 # is first parent, m1 is second parent and so on. Git can have
703 # several parents, Hg cannot, so I think the m2..mmax would be
703 # several parents, Hg cannot, so I think the m2..mmax would be
704 # unused.
704 # unused.
705 $ctext tag conf m0 -fore $diffmerge1color
705 $ctext tag conf m0 -fore $diffmerge1color
706 $ctext tag conf m1 -fore $diffmerge2color
706 $ctext tag conf m1 -fore $diffmerge2color
707 $ctext tag conf m2 -fore green
707 $ctext tag conf m2 -fore green
708 $ctext tag conf m3 -fore purple
708 $ctext tag conf m3 -fore purple
709 $ctext tag conf m4 -fore brown
709 $ctext tag conf m4 -fore brown
710 $ctext tag conf mmax -fore darkgrey
710 $ctext tag conf mmax -fore darkgrey
711 set mergemax 5
711 set mergemax 5
712 $ctext tag conf mresult -font [concat $textfont bold]
712 $ctext tag conf mresult -font [concat $textfont bold]
713 $ctext tag conf msep -font [concat $textfont bold]
713 $ctext tag conf msep -font [concat $textfont bold]
714 $ctext tag conf found -back yellow
714 $ctext tag conf found -back yellow
715 }
715 }
716
716
717 ttk::frame .ctop.cdet.right
717 ttk::frame .ctop.cdet.right
718 set cflist .ctop.cdet.right.cfiles
718 set cflist .ctop.cdet.right.cfiles
719 listbox $cflist -fg $fgcolor -bg $bgcolor \
719 listbox $cflist -fg $fgcolor -bg $bgcolor \
720 -selectmode extended -width $geometry(cflistw) \
720 -selectmode extended -width $geometry(cflistw) \
721 -yscrollcommand ".ctop.cdet.right.sb set"
721 -yscrollcommand ".ctop.cdet.right.sb set"
722 ttk::scrollbar .ctop.cdet.right.sb -command "$cflist yview"
722 ttk::scrollbar .ctop.cdet.right.sb -command "$cflist yview"
723 pack .ctop.cdet.right.sb -side right -fill y
723 pack .ctop.cdet.right.sb -side right -fill y
724 pack $cflist -side left -fill both -expand 1
724 pack $cflist -side left -fill both -expand 1
725 .ctop.cdet add .ctop.cdet.right
725 .ctop.cdet add .ctop.cdet.right
726 bind .ctop.cdet <Configure> {resizecdetpanes %W %w}
726 bind .ctop.cdet <Configure> {resizecdetpanes %W %w}
727
727
728 pack .ctop -side top -fill both -expand 1
728 pack .ctop -side top -fill both -expand 1
729
729
730 bindall <1> {selcanvline %W %x %y}
730 bindall <1> {selcanvline %W %x %y}
731 #bindall <B1-Motion> {selcanvline %W %x %y}
731 #bindall <B1-Motion> {selcanvline %W %x %y}
732 bindall <MouseWheel> "allcansmousewheel %D"
732 bindall <MouseWheel> "allcansmousewheel %D"
733 bindall <ButtonRelease-4> "allcanvs yview scroll -5 units"
733 bindall <ButtonRelease-4> "allcanvs yview scroll -5 units"
734 bindall <ButtonRelease-5> "allcanvs yview scroll 5 units"
734 bindall <ButtonRelease-5> "allcanvs yview scroll 5 units"
735 bindall <2> "allcanvs scan mark 0 %y"
735 bindall <2> "allcanvs scan mark 0 %y"
736 bindall <B2-Motion> "allcanvs scan dragto 0 %y"
736 bindall <B2-Motion> "allcanvs scan dragto 0 %y"
737 bind . <Key-Up> "selnextline -1"
737 bind . <Key-Up> "selnextline -1"
738 bind . <Key-Down> "selnextline 1"
738 bind . <Key-Down> "selnextline 1"
739 bind . <Key-Prior> "allcanvs yview scroll -1 pages"
739 bind . <Key-Prior> "allcanvs yview scroll -1 pages"
740 bind . <Key-Next> "allcanvs yview scroll 1 pages"
740 bind . <Key-Next> "allcanvs yview scroll 1 pages"
741 bindkey <Key-Delete> "$ctext yview scroll -1 pages"
741 bindkey <Key-Delete> "$ctext yview scroll -1 pages"
742 bindkey <Key-BackSpace> "$ctext yview scroll -1 pages"
742 bindkey <Key-BackSpace> "$ctext yview scroll -1 pages"
743 bindkey <Key-space> "$ctext yview scroll 1 pages"
743 bindkey <Key-space> "$ctext yview scroll 1 pages"
744 bindkey p "selnextline -1"
744 bindkey p "selnextline -1"
745 bindkey n "selnextline 1"
745 bindkey n "selnextline 1"
746 bindkey b "$ctext yview scroll -1 pages"
746 bindkey b "$ctext yview scroll -1 pages"
747 bindkey d "$ctext yview scroll 18 units"
747 bindkey d "$ctext yview scroll 18 units"
748 bindkey u "$ctext yview scroll -18 units"
748 bindkey u "$ctext yview scroll -18 units"
749 bindkey / {findnext 1}
749 bindkey / {findnext 1}
750 bindkey <Key-Return> {findnext 0}
750 bindkey <Key-Return> {findnext 0}
751 bindkey ? findprev
751 bindkey ? findprev
752 bindkey f nextfile
752 bindkey f nextfile
753 bind . <Control-q> doquit
753 bind . <Control-q> doquit
754 bind . <Control-w> doquit
754 bind . <Control-w> doquit
755 bind . <Control-f> dofind
755 bind . <Control-f> dofind
756 bind . <Control-g> {findnext 0}
756 bind . <Control-g> {findnext 0}
757 bind . <Control-r> findprev
757 bind . <Control-r> findprev
758 bind . <Control-equal> {incrfont 1}
758 bind . <Control-equal> {incrfont 1}
759 bind . <Control-KP_Add> {incrfont 1}
759 bind . <Control-KP_Add> {incrfont 1}
760 bind . <Control-minus> {incrfont -1}
760 bind . <Control-minus> {incrfont -1}
761 bind . <Control-KP_Subtract> {incrfont -1}
761 bind . <Control-KP_Subtract> {incrfont -1}
762 bind $cflist <<ListboxSelect>> listboxsel
762 bind $cflist <<ListboxSelect>> listboxsel
763 bind . <Destroy> {savestuff %W}
763 bind . <Destroy> {savestuff %W}
764 bind . <Button-1> "click %W"
764 bind . <Button-1> "click %W"
765 bind $fstring <Key-Return> dofind
765 bind $fstring <Key-Return> dofind
766 bind $sha1entry <Key-Return> gotocommit
766 bind $sha1entry <Key-Return> gotocommit
767 bind $sha1entry <<PasteSelection>> clearsha1
767 bind $sha1entry <<PasteSelection>> clearsha1
768
768
769 set maincursor [. cget -cursor]
769 set maincursor [. cget -cursor]
770 set textcursor [$ctext cget -cursor]
770 set textcursor [$ctext cget -cursor]
771 set curtextcursor $textcursor
771 set curtextcursor $textcursor
772
772
773 set rowctxmenu .rowctxmenu
773 set rowctxmenu .rowctxmenu
774 menu $rowctxmenu -tearoff 0
774 menu $rowctxmenu -tearoff 0
775 $rowctxmenu add command -label "Diff this -> selected" \
775 $rowctxmenu add command -label "Diff this -> selected" \
776 -command {diffvssel 0}
776 -command {diffvssel 0}
777 $rowctxmenu add command -label "Diff selected -> this" \
777 $rowctxmenu add command -label "Diff selected -> this" \
778 -command {diffvssel 1}
778 -command {diffvssel 1}
779 $rowctxmenu add command -label "Make patch" -command mkpatch
779 $rowctxmenu add command -label "Make patch" -command mkpatch
780 $rowctxmenu add command -label "Create tag" -command mktag
780 $rowctxmenu add command -label "Create tag" -command mktag
781 $rowctxmenu add command -label "Write commit to file" -command writecommit
781 $rowctxmenu add command -label "Write commit to file" -command writecommit
782 if { $hgvdiff ne "" } {
782 if { $hgvdiff ne "" } {
783 $rowctxmenu add command -label "Visual diff with parent" \
783 $rowctxmenu add command -label "Visual diff with parent" \
784 -command {vdiff 1}
784 -command {vdiff 1}
785 $rowctxmenu add command -label "Visual diff with selected" \
785 $rowctxmenu add command -label "Visual diff with selected" \
786 -command {vdiff 0}
786 -command {vdiff 0}
787 }
787 }
788 }
788 }
789
789
790 # when we make a key binding for the toplevel, make sure
790 # when we make a key binding for the toplevel, make sure
791 # it doesn't get triggered when that key is pressed in the
791 # it doesn't get triggered when that key is pressed in the
792 # find string entry widget.
792 # find string entry widget.
793 proc bindkey {ev script} {
793 proc bindkey {ev script} {
794 global entries
794 global entries
795 bind . $ev $script
795 bind . $ev $script
796 set escript [bind Entry $ev]
796 set escript [bind Entry $ev]
797 if {$escript == {}} {
797 if {$escript == {}} {
798 set escript [bind Entry <Key>]
798 set escript [bind Entry <Key>]
799 }
799 }
800 foreach e $entries {
800 foreach e $entries {
801 bind $e $ev "$escript; break"
801 bind $e $ev "$escript; break"
802 }
802 }
803 }
803 }
804
804
805 # set the focus back to the toplevel for any click outside
805 # set the focus back to the toplevel for any click outside
806 # the entry widgets
806 # the entry widgets
807 proc click {w} {
807 proc click {w} {
808 global ctext entries
808 global ctext entries
809 foreach e [concat $entries $ctext] {
809 foreach e [concat $entries $ctext] {
810 if {$w == $e} return
810 if {$w == $e} return
811 }
811 }
812 focus .
812 focus .
813 }
813 }
814
814
815 proc savestuff {w} {
815 proc savestuff {w} {
816 global canv canv2 canv3 ctext cflist mainfont textfont
816 global canv canv2 canv3 ctext cflist mainfont textfont
817 global stuffsaved findmergefiles gaudydiff maxgraphpct
817 global stuffsaved findmergefiles gaudydiff maxgraphpct
818 global maxwidth authorcolors curidfont bgcolor fgcolor
818 global maxwidth authorcolors curidfont bgcolor fgcolor
819 global diffremcolor diffaddcolor hunksepcolor
819 global diffremcolor diffaddcolor hunksepcolor
820 global diffmerge1color diffmerge2color
820 global diffmerge1color diffmerge2color
821
821
822 if {$stuffsaved} return
822 if {$stuffsaved} return
823 if {![winfo viewable .]} return
823 if {![winfo viewable .]} return
824 catch {
824 catch {
825 set f [open "~/.hgk-new" w]
825 set f [open "~/.hgk-new" w]
826 puts $f [list set mainfont $mainfont]
826 puts $f [list set mainfont $mainfont]
827 puts $f [list set curidfont $curidfont]
827 puts $f [list set curidfont $curidfont]
828 puts $f [list set textfont $textfont]
828 puts $f [list set textfont $textfont]
829 puts $f [list set findmergefiles $findmergefiles]
829 puts $f [list set findmergefiles $findmergefiles]
830 puts $f [list set gaudydiff $gaudydiff]
830 puts $f [list set gaudydiff $gaudydiff]
831 puts $f [list set maxgraphpct $maxgraphpct]
831 puts $f [list set maxgraphpct $maxgraphpct]
832 puts $f [list set maxwidth $maxwidth]
832 puts $f [list set maxwidth $maxwidth]
833 puts $f "set geometry(width) [winfo width .ctop]"
833 puts $f "set geometry(width) [winfo width .ctop]"
834 puts $f "set geometry(height) [winfo height .ctop]"
834 puts $f "set geometry(height) [winfo height .ctop]"
835 puts $f "set geometry(canv1) [expr [winfo width $canv]-2]"
835 puts $f "set geometry(canv1) [expr [winfo width $canv]-2]"
836 puts $f "set geometry(canv2) [expr [winfo width $canv2]-2]"
836 puts $f "set geometry(canv2) [expr [winfo width $canv2]-2]"
837 puts $f "set geometry(canv3) [expr [winfo width $canv3]-2]"
837 puts $f "set geometry(canv3) [expr [winfo width $canv3]-2]"
838 puts $f "set geometry(canvh) [expr [winfo height $canv]-2]"
838 puts $f "set geometry(canvh) [expr [winfo height $canv]-2]"
839 set wid [expr {([winfo width $ctext] - 8) \
839 set wid [expr {([winfo width $ctext] - 8) \
840 / [font measure $textfont "0"]}]
840 / [font measure $textfont "0"]}]
841 puts $f "set geometry(ctextw) $wid"
841 puts $f "set geometry(ctextw) $wid"
842 set wid [expr {([winfo width $cflist] - 11) \
842 set wid [expr {([winfo width $cflist] - 11) \
843 / [font measure [$cflist cget -font] "0"]}]
843 / [font measure [$cflist cget -font] "0"]}]
844 puts $f "set geometry(cflistw) $wid"
844 puts $f "set geometry(cflistw) $wid"
845 puts $f "#"
845 puts $f "#"
846 puts $f "# main window position:"
846 puts $f "# main window position:"
847 puts $f "set posx [winfo x .]"
847 puts $f "set posx [winfo x .]"
848 puts $f "set posy [winfo y .]"
848 puts $f "set posy [winfo y .]"
849 puts $f "#"
849 puts $f "#"
850 puts $f "# authorcolors format:"
850 puts $f "# authorcolors format:"
851 puts $f "#"
851 puts $f "#"
852 puts $f "# zero or more sublists of"
852 puts $f "# zero or more sublists of"
853 puts $f "#"
853 puts $f "#"
854 puts $f "# { regex color }"
854 puts $f "# { regex color }"
855 puts $f "#"
855 puts $f "#"
856 puts $f "# followed by a list of colors"
856 puts $f "# followed by a list of colors"
857 puts $f "#"
857 puts $f "#"
858 puts $f "# If the commit author matches a regex in a sublist,"
858 puts $f "# If the commit author matches a regex in a sublist,"
859 puts $f "# the commit will be colored by that color"
859 puts $f "# the commit will be colored by that color"
860 puts $f "# otherwise the next unused entry from the list of colors"
860 puts $f "# otherwise the next unused entry from the list of colors"
861 puts $f "# will be assigned to this commit and also all other commits"
861 puts $f "# will be assigned to this commit and also all other commits"
862 puts $f "# of the same author. When the list of colors is exhausted,"
862 puts $f "# of the same author. When the list of colors is exhausted,"
863 puts $f "# the last entry will be reused."
863 puts $f "# the last entry will be reused."
864 puts $f "#"
864 puts $f "#"
865 puts $f "set authorcolors {$authorcolors}"
865 puts $f "set authorcolors {$authorcolors}"
866 puts $f "#"
866 puts $f "#"
867 puts $f "# The background color in the text windows"
867 puts $f "# The background color in the text windows"
868 puts $f "set bgcolor $bgcolor"
868 puts $f "set bgcolor $bgcolor"
869 puts $f "#"
869 puts $f "#"
870 puts $f "# The text color used in the diff and file list view"
870 puts $f "# The text color used in the diff and file list view"
871 puts $f "set fgcolor $fgcolor"
871 puts $f "set fgcolor $fgcolor"
872 puts $f "#"
872 puts $f "#"
873 puts $f "# Color to display + lines in diffs"
873 puts $f "# Color to display + lines in diffs"
874 puts $f "set diffaddcolor $diffaddcolor"
874 puts $f "set diffaddcolor $diffaddcolor"
875 puts $f "#"
875 puts $f "#"
876 puts $f "# Color to display - lines in diffs"
876 puts $f "# Color to display - lines in diffs"
877 puts $f "set diffremcolor $diffremcolor"
877 puts $f "set diffremcolor $diffremcolor"
878 puts $f "#"
878 puts $f "#"
879 puts $f "# Merge diffs: Color to signal lines from first parent"
879 puts $f "# Merge diffs: Color to signal lines from first parent"
880 puts $f "set diffmerge1color $diffmerge1color"
880 puts $f "set diffmerge1color $diffmerge1color"
881 puts $f "#"
881 puts $f "#"
882 puts $f "# Merge diffs: Color to signal lines from second parent"
882 puts $f "# Merge diffs: Color to signal lines from second parent"
883 puts $f "set diffmerge2color $diffmerge2color"
883 puts $f "set diffmerge2color $diffmerge2color"
884 puts $f "#"
884 puts $f "#"
885 puts $f "# Hunkseparator (@@ -lineno,lines +lineno,lines @@) color"
885 puts $f "# Hunkseparator (@@ -lineno,lines +lineno,lines @@) color"
886 puts $f "set hunksepcolor $hunksepcolor"
886 puts $f "set hunksepcolor $hunksepcolor"
887 close $f
887 close $f
888 file rename -force "~/.hgk-new" "~/.hgk"
888 file rename -force "~/.hgk-new" "~/.hgk"
889 }
889 }
890 set stuffsaved 1
890 set stuffsaved 1
891 }
891 }
892
892
893 proc resizeclistpanes {win w} {
893 proc resizeclistpanes {win w} {
894 global oldwidth
894 global oldwidth
895 if [info exists oldwidth($win)] {
895 if [info exists oldwidth($win)] {
896 set s0 [$win sash coord 0]
896 set s0 [$win sash coord 0]
897 set s1 [$win sash coord 1]
897 set s1 [$win sash coord 1]
898 if {$w < 60} {
898 if {$w < 60} {
899 set sash0 [expr {int($w/2 - 2)}]
899 set sash0 [expr {int($w/2 - 2)}]
900 set sash1 [expr {int($w*5/6 - 2)}]
900 set sash1 [expr {int($w*5/6 - 2)}]
901 } else {
901 } else {
902 set factor [expr {1.0 * $w / $oldwidth($win)}]
902 set factor [expr {1.0 * $w / $oldwidth($win)}]
903 set sash0 [expr {int($factor * [lindex $s0 0])}]
903 set sash0 [expr {int($factor * [lindex $s0 0])}]
904 set sash1 [expr {int($factor * [lindex $s1 0])}]
904 set sash1 [expr {int($factor * [lindex $s1 0])}]
905 if {$sash0 < 30} {
905 if {$sash0 < 30} {
906 set sash0 30
906 set sash0 30
907 }
907 }
908 if {$sash1 < $sash0 + 20} {
908 if {$sash1 < $sash0 + 20} {
909 set sash1 [expr $sash0 + 20]
909 set sash1 [expr $sash0 + 20]
910 }
910 }
911 if {$sash1 > $w - 10} {
911 if {$sash1 > $w - 10} {
912 set sash1 [expr $w - 10]
912 set sash1 [expr $w - 10]
913 if {$sash0 > $sash1 - 20} {
913 if {$sash0 > $sash1 - 20} {
914 set sash0 [expr $sash1 - 20]
914 set sash0 [expr $sash1 - 20]
915 }
915 }
916 }
916 }
917 }
917 }
918 $win sash place 0 $sash0 [lindex $s0 1]
918 $win sash place 0 $sash0 [lindex $s0 1]
919 $win sash place 1 $sash1 [lindex $s1 1]
919 $win sash place 1 $sash1 [lindex $s1 1]
920 }
920 }
921 set oldwidth($win) $w
921 set oldwidth($win) $w
922 }
922 }
923
923
924 proc resizecdetpanes {win w} {
924 proc resizecdetpanes {win w} {
925 global oldwidth
925 global oldwidth
926 if [info exists oldwidth($win)] {
926 if [info exists oldwidth($win)] {
927 set s0 [$win sash coord 0]
927 set s0 [$win sash coord 0]
928 if {$w < 60} {
928 if {$w < 60} {
929 set sash0 [expr {int($w*3/4 - 2)}]
929 set sash0 [expr {int($w*3/4 - 2)}]
930 } else {
930 } else {
931 set factor [expr {1.0 * $w / $oldwidth($win)}]
931 set factor [expr {1.0 * $w / $oldwidth($win)}]
932 set sash0 [expr {int($factor * [lindex $s0 0])}]
932 set sash0 [expr {int($factor * [lindex $s0 0])}]
933 if {$sash0 < 45} {
933 if {$sash0 < 45} {
934 set sash0 45
934 set sash0 45
935 }
935 }
936 if {$sash0 > $w - 15} {
936 if {$sash0 > $w - 15} {
937 set sash0 [expr $w - 15]
937 set sash0 [expr $w - 15]
938 }
938 }
939 }
939 }
940 $win sash place 0 $sash0 [lindex $s0 1]
940 $win sash place 0 $sash0 [lindex $s0 1]
941 }
941 }
942 set oldwidth($win) $w
942 set oldwidth($win) $w
943 }
943 }
944
944
945 proc allcanvs args {
945 proc allcanvs args {
946 global canv canv2 canv3
946 global canv canv2 canv3
947 eval $canv $args
947 eval $canv $args
948 eval $canv2 $args
948 eval $canv2 $args
949 eval $canv3 $args
949 eval $canv3 $args
950 }
950 }
951
951
952 proc bindall {event action} {
952 proc bindall {event action} {
953 global canv canv2 canv3
953 global canv canv2 canv3
954 bind $canv $event $action
954 bind $canv $event $action
955 bind $canv2 $event $action
955 bind $canv2 $event $action
956 bind $canv3 $event $action
956 bind $canv3 $event $action
957 }
957 }
958
958
959 proc about {} {
959 proc about {} {
960 set w .about
960 set w .about
961 if {[winfo exists $w]} {
961 if {[winfo exists $w]} {
962 raise $w
962 raise $w
963 return
963 return
964 }
964 }
965 toplevel $w
965 toplevel $w
966 wm title $w "About hgk"
966 wm title $w "About hgk"
967 message $w.m -text {
967 message $w.m -text {
968 Hgk version 1.2
968 Hgk version 1.2
969
969
970 Copyright οΏ½ 2005 Paul Mackerras
970 Copyright οΏ½ 2005 Paul Mackerras
971
971
972 Use and redistribute under the terms of the GNU General Public License} \
972 Use and redistribute under the terms of the GNU General Public License} \
973 -justify center -aspect 400
973 -justify center -aspect 400
974 pack $w.m -side top -fill x -padx 20 -pady 20
974 pack $w.m -side top -fill x -padx 20 -pady 20
975 ttk::button $w.ok -text Close -command "destroy $w"
975 ttk::button $w.ok -text Close -command "destroy $w"
976 pack $w.ok -side bottom
976 pack $w.ok -side bottom
977 popupify $w
977 popupify $w
978 }
978 }
979
979
980 set aunextcolor 0
980 set aunextcolor 0
981 proc assignauthorcolor {name} {
981 proc assignauthorcolor {name} {
982 global authorcolors aucolormap aunextcolor
982 global authorcolors aucolormap aunextcolor
983 if [info exists aucolormap($name)] return
983 if [info exists aucolormap($name)] return
984
984
985 set randomcolors {black}
985 set randomcolors {black}
986 for {set i 0} {$i < [llength $authorcolors]} {incr i} {
986 for {set i 0} {$i < [llength $authorcolors]} {incr i} {
987 set col [lindex $authorcolors $i]
987 set col [lindex $authorcolors $i]
988 if {[llength $col] > 1} {
988 if {[llength $col] > 1} {
989 set re [lindex $col 0]
989 set re [lindex $col 0]
990 set c [lindex $col 1]
990 set c [lindex $col 1]
991 if {[regexp -- $re $name]} {
991 if {[regexp -- $re $name]} {
992 set aucolormap($name) $c
992 set aucolormap($name) $c
993 return
993 return
994 }
994 }
995 } else {
995 } else {
996 set randomcolors [lrange $authorcolors $i end]
996 set randomcolors [lrange $authorcolors $i end]
997 break
997 break
998 }
998 }
999 }
999 }
1000
1000
1001 set ncolors [llength $randomcolors]
1001 set ncolors [llength $randomcolors]
1002 set c [lindex $randomcolors $aunextcolor]
1002 set c [lindex $randomcolors $aunextcolor]
1003 if {[incr aunextcolor] >= $ncolors} {
1003 if {[incr aunextcolor] >= $ncolors} {
1004 incr aunextcolor -1
1004 incr aunextcolor -1
1005 }
1005 }
1006 set aucolormap($name) $c
1006 set aucolormap($name) $c
1007 }
1007 }
1008
1008
1009 proc assigncolor {id} {
1009 proc assigncolor {id} {
1010 global commitinfo colormap commcolors colors nextcolor
1010 global commitinfo colormap commcolors colors nextcolor
1011 global parents nparents children nchildren
1011 global parents nparents children nchildren
1012 global cornercrossings crossings
1012 global cornercrossings crossings
1013
1013
1014 if [info exists colormap($id)] return
1014 if [info exists colormap($id)] return
1015 set ncolors [llength $colors]
1015 set ncolors [llength $colors]
1016 if {$nparents($id) <= 1 && $nchildren($id) == 1} {
1016 if {$nparents($id) <= 1 && $nchildren($id) == 1} {
1017 set child [lindex $children($id) 0]
1017 set child [lindex $children($id) 0]
1018 if {[info exists colormap($child)]
1018 if {[info exists colormap($child)]
1019 && $nparents($child) == 1} {
1019 && $nparents($child) == 1} {
1020 set colormap($id) $colormap($child)
1020 set colormap($id) $colormap($child)
1021 return
1021 return
1022 }
1022 }
1023 }
1023 }
1024 set badcolors {}
1024 set badcolors {}
1025 if {[info exists cornercrossings($id)]} {
1025 if {[info exists cornercrossings($id)]} {
1026 foreach x $cornercrossings($id) {
1026 foreach x $cornercrossings($id) {
1027 if {[info exists colormap($x)]
1027 if {[info exists colormap($x)]
1028 && [lsearch -exact $badcolors $colormap($x)] < 0} {
1028 && [lsearch -exact $badcolors $colormap($x)] < 0} {
1029 lappend badcolors $colormap($x)
1029 lappend badcolors $colormap($x)
1030 }
1030 }
1031 }
1031 }
1032 if {[llength $badcolors] >= $ncolors} {
1032 if {[llength $badcolors] >= $ncolors} {
1033 set badcolors {}
1033 set badcolors {}
1034 }
1034 }
1035 }
1035 }
1036 set origbad $badcolors
1036 set origbad $badcolors
1037 if {[llength $badcolors] < $ncolors - 1} {
1037 if {[llength $badcolors] < $ncolors - 1} {
1038 if {[info exists crossings($id)]} {
1038 if {[info exists crossings($id)]} {
1039 foreach x $crossings($id) {
1039 foreach x $crossings($id) {
1040 if {[info exists colormap($x)]
1040 if {[info exists colormap($x)]
1041 && [lsearch -exact $badcolors $colormap($x)] < 0} {
1041 && [lsearch -exact $badcolors $colormap($x)] < 0} {
1042 lappend badcolors $colormap($x)
1042 lappend badcolors $colormap($x)
1043 }
1043 }
1044 }
1044 }
1045 if {[llength $badcolors] >= $ncolors} {
1045 if {[llength $badcolors] >= $ncolors} {
1046 set badcolors $origbad
1046 set badcolors $origbad
1047 }
1047 }
1048 }
1048 }
1049 set origbad $badcolors
1049 set origbad $badcolors
1050 }
1050 }
1051 if {[llength $badcolors] < $ncolors - 1} {
1051 if {[llength $badcolors] < $ncolors - 1} {
1052 foreach child $children($id) {
1052 foreach child $children($id) {
1053 if {[info exists colormap($child)]
1053 if {[info exists colormap($child)]
1054 && [lsearch -exact $badcolors $colormap($child)] < 0} {
1054 && [lsearch -exact $badcolors $colormap($child)] < 0} {
1055 lappend badcolors $colormap($child)
1055 lappend badcolors $colormap($child)
1056 }
1056 }
1057 if {[info exists parents($child)]} {
1057 if {[info exists parents($child)]} {
1058 foreach p $parents($child) {
1058 foreach p $parents($child) {
1059 if {[info exists colormap($p)]
1059 if {[info exists colormap($p)]
1060 && [lsearch -exact $badcolors $colormap($p)] < 0} {
1060 && [lsearch -exact $badcolors $colormap($p)] < 0} {
1061 lappend badcolors $colormap($p)
1061 lappend badcolors $colormap($p)
1062 }
1062 }
1063 }
1063 }
1064 }
1064 }
1065 }
1065 }
1066 if {[llength $badcolors] >= $ncolors} {
1066 if {[llength $badcolors] >= $ncolors} {
1067 set badcolors $origbad
1067 set badcolors $origbad
1068 }
1068 }
1069 }
1069 }
1070 for {set i 0} {$i <= $ncolors} {incr i} {
1070 for {set i 0} {$i <= $ncolors} {incr i} {
1071 set c [lindex $colors $nextcolor]
1071 set c [lindex $colors $nextcolor]
1072 if {[incr nextcolor] >= $ncolors} {
1072 if {[incr nextcolor] >= $ncolors} {
1073 set nextcolor 0
1073 set nextcolor 0
1074 }
1074 }
1075 if {[lsearch -exact $badcolors $c]} break
1075 if {[lsearch -exact $badcolors $c]} break
1076 }
1076 }
1077 set colormap($id) $c
1077 set colormap($id) $c
1078 }
1078 }
1079
1079
1080 proc initgraph {} {
1080 proc initgraph {} {
1081 global canvy canvy0 lineno numcommits nextcolor linespc
1081 global canvy canvy0 lineno numcommits nextcolor linespc
1082 global mainline mainlinearrow sidelines
1082 global mainline mainlinearrow sidelines
1083 global nchildren ncleft
1083 global nchildren ncleft
1084 global displist nhyperspace
1084 global displist nhyperspace
1085
1085
1086 allcanvs delete all
1086 allcanvs delete all
1087 set nextcolor 0
1087 set nextcolor 0
1088 set canvy $canvy0
1088 set canvy $canvy0
1089 set lineno -1
1089 set lineno -1
1090 set numcommits 0
1090 set numcommits 0
1091 catch {unset mainline}
1091 catch {unset mainline}
1092 catch {unset mainlinearrow}
1092 catch {unset mainlinearrow}
1093 catch {unset sidelines}
1093 catch {unset sidelines}
1094 foreach id [array names nchildren] {
1094 foreach id [array names nchildren] {
1095 set ncleft($id) $nchildren($id)
1095 set ncleft($id) $nchildren($id)
1096 }
1096 }
1097 set displist {}
1097 set displist {}
1098 set nhyperspace 0
1098 set nhyperspace 0
1099 }
1099 }
1100
1100
1101 proc bindline {t id} {
1101 proc bindline {t id} {
1102 global canv
1102 global canv
1103
1103
1104 $canv bind $t <Enter> "lineenter %x %y $id"
1104 $canv bind $t <Enter> "lineenter %x %y $id"
1105 $canv bind $t <Motion> "linemotion %x %y $id"
1105 $canv bind $t <Motion> "linemotion %x %y $id"
1106 $canv bind $t <Leave> "lineleave $id"
1106 $canv bind $t <Leave> "lineleave $id"
1107 $canv bind $t <Button-1> "lineclick %x %y $id 1"
1107 $canv bind $t <Button-1> "lineclick %x %y $id 1"
1108 }
1108 }
1109
1109
1110 proc drawlines {id xtra} {
1110 proc drawlines {id xtra} {
1111 global mainline mainlinearrow sidelines lthickness colormap canv
1111 global mainline mainlinearrow sidelines lthickness colormap canv
1112
1112
1113 $canv delete lines.$id
1113 $canv delete lines.$id
1114 if {[info exists mainline($id)]} {
1114 if {[info exists mainline($id)]} {
1115 set t [$canv create line $mainline($id) \
1115 set t [$canv create line $mainline($id) \
1116 -width [expr {($xtra + 1) * $lthickness}] \
1116 -width [expr {($xtra + 1) * $lthickness}] \
1117 -fill $colormap($id) -tags lines.$id \
1117 -fill $colormap($id) -tags lines.$id \
1118 -arrow $mainlinearrow($id)]
1118 -arrow $mainlinearrow($id)]
1119 $canv lower $t
1119 $canv lower $t
1120 bindline $t $id
1120 bindline $t $id
1121 }
1121 }
1122 if {[info exists sidelines($id)]} {
1122 if {[info exists sidelines($id)]} {
1123 foreach ls $sidelines($id) {
1123 foreach ls $sidelines($id) {
1124 set coords [lindex $ls 0]
1124 set coords [lindex $ls 0]
1125 set thick [lindex $ls 1]
1125 set thick [lindex $ls 1]
1126 set arrow [lindex $ls 2]
1126 set arrow [lindex $ls 2]
1127 set t [$canv create line $coords -fill $colormap($id) \
1127 set t [$canv create line $coords -fill $colormap($id) \
1128 -width [expr {($thick + $xtra) * $lthickness}] \
1128 -width [expr {($thick + $xtra) * $lthickness}] \
1129 -arrow $arrow -tags lines.$id]
1129 -arrow $arrow -tags lines.$id]
1130 $canv lower $t
1130 $canv lower $t
1131 bindline $t $id
1131 bindline $t $id
1132 }
1132 }
1133 }
1133 }
1134 }
1134 }
1135
1135
1136 # level here is an index in displist
1136 # level here is an index in displist
1137 proc drawcommitline {level} {
1137 proc drawcommitline {level} {
1138 global parents children nparents displist
1138 global parents children nparents displist
1139 global canv canv2 canv3 mainfont namefont canvy linespc
1139 global canv canv2 canv3 mainfont namefont canvy linespc
1140 global lineid linehtag linentag linedtag commitinfo
1140 global lineid linehtag linentag linedtag commitinfo
1141 global colormap numcommits currentparents dupparents
1141 global colormap numcommits currentparents dupparents
1142 global idtags idline idheads idotherrefs idbookmarks
1142 global idtags idline idheads idotherrefs idbookmarks
1143 global lineno lthickness mainline mainlinearrow sidelines
1143 global lineno lthickness mainline mainlinearrow sidelines
1144 global commitlisted rowtextx idpos lastuse displist
1144 global commitlisted rowtextx idpos lastuse displist
1145 global oldnlines olddlevel olddisplist
1145 global oldnlines olddlevel olddisplist
1146 global aucolormap curid curidfont obsolete
1146 global aucolormap curid curidfont obsolete
1147
1147
1148 incr numcommits
1148 incr numcommits
1149 incr lineno
1149 incr lineno
1150 set id [lindex $displist $level]
1150 set id [lindex $displist $level]
1151 set lastuse($id) $lineno
1151 set lastuse($id) $lineno
1152 set lineid($lineno) $id
1152 set lineid($lineno) $id
1153 set idline($id) $lineno
1153 set idline($id) $lineno
1154 set shape oval
1154 set shape oval
1155 set outline #000080
1155 set outline #000080
1156 set ofill [expr {[info exists commitlisted($id)]? "#7f7fff": "white"}]
1156 set ofill [expr {[info exists commitlisted($id)]? "#7f7fff": "white"}]
1157 if {![info exists commitinfo($id)]} {
1157 if {![info exists commitinfo($id)]} {
1158 readcommit $id
1158 readcommit $id
1159 if {![info exists commitinfo($id)]} {
1159 if {![info exists commitinfo($id)]} {
1160 set commitinfo($id) {"No commit information available"}
1160 set commitinfo($id) {"No commit information available"}
1161 set nparents($id) 0
1161 set nparents($id) 0
1162 }
1162 }
1163 } else {
1163 } else {
1164 switch [lindex $commitinfo($id) 9] secret {
1164 switch [lindex $commitinfo($id) 9] secret {
1165 set shape rect
1165 set shape rect
1166 } public {
1166 } public {
1167 set outline black
1167 set outline black
1168 set ofill blue
1168 set ofill blue
1169 }
1169 }
1170 }
1170 }
1171 if {[info exists obsolete($id)]} {
1171 if {[info exists obsolete($id)]} {
1172 set outline darkgrey
1172 set outline darkgrey
1173 set ofill lightgrey
1173 set ofill lightgrey
1174 }
1174 }
1175 assigncolor $id
1175 assigncolor $id
1176 set currentparents {}
1176 set currentparents {}
1177 set dupparents {}
1177 set dupparents {}
1178 if {[info exists commitlisted($id)] && [info exists parents($id)]} {
1178 if {[info exists commitlisted($id)] && [info exists parents($id)]} {
1179 foreach p $parents($id) {
1179 foreach p $parents($id) {
1180 if {[lsearch -exact $currentparents $p] < 0} {
1180 if {[lsearch -exact $currentparents $p] < 0} {
1181 lappend currentparents $p
1181 lappend currentparents $p
1182 } else {
1182 } else {
1183 # remember that this parent was listed twice
1183 # remember that this parent was listed twice
1184 lappend dupparents $p
1184 lappend dupparents $p
1185 }
1185 }
1186 }
1186 }
1187 }
1187 }
1188 set x [xcoord $level $level $lineno]
1188 set x [xcoord $level $level $lineno]
1189 set y1 $canvy
1189 set y1 $canvy
1190 set canvy [expr $canvy + $linespc]
1190 set canvy [expr $canvy + $linespc]
1191 allcanvs conf -scrollregion \
1191 allcanvs conf -scrollregion \
1192 [list 0 0 0 [expr $y1 + 0.5 * $linespc + 2]]
1192 [list 0 0 0 [expr $y1 + 0.5 * $linespc + 2]]
1193 if {[info exists mainline($id)]} {
1193 if {[info exists mainline($id)]} {
1194 lappend mainline($id) $x $y1
1194 lappend mainline($id) $x $y1
1195 if {$mainlinearrow($id) ne "none"} {
1195 if {$mainlinearrow($id) ne "none"} {
1196 set mainline($id) [trimdiagstart $mainline($id)]
1196 set mainline($id) [trimdiagstart $mainline($id)]
1197 }
1197 }
1198 }
1198 }
1199 drawlines $id 0
1199 drawlines $id 0
1200 set orad [expr {$linespc / 3}]
1200 set orad [expr {$linespc / 3}]
1201 set t [$canv create $shape [expr $x - $orad] [expr $y1 - $orad] \
1201 set t [$canv create $shape [expr $x - $orad] [expr $y1 - $orad] \
1202 [expr $x + $orad - 1] [expr $y1 + $orad - 1] \
1202 [expr $x + $orad - 1] [expr $y1 + $orad - 1] \
1203 -fill $ofill -outline $outline -width 1]
1203 -fill $ofill -outline $outline -width 1]
1204 $canv raise $t
1204 $canv raise $t
1205 $canv bind $t <1> {selcanvline {} %x %y}
1205 $canv bind $t <1> {selcanvline {} %x %y}
1206 set xt [xcoord [llength $displist] $level $lineno]
1206 set xt [xcoord [llength $displist] $level $lineno]
1207 if {[llength $currentparents] > 2} {
1207 if {[llength $currentparents] > 2} {
1208 set xt [expr {$xt + ([llength $currentparents] - 2) * $linespc}]
1208 set xt [expr {$xt + ([llength $currentparents] - 2) * $linespc}]
1209 }
1209 }
1210 set rowtextx($lineno) $xt
1210 set rowtextx($lineno) $xt
1211 set idpos($id) [list $x $xt $y1]
1211 set idpos($id) [list $x $xt $y1]
1212 if {[info exists idtags($id)] || [info exists idheads($id)]
1212 if {[info exists idtags($id)] || [info exists idheads($id)]
1213 || [info exists idotherrefs($id)] || [info exists idbookmarks($id)]} {
1213 || [info exists idotherrefs($id)] || [info exists idbookmarks($id)]} {
1214 set xt [drawtags $id $x $xt $y1]
1214 set xt [drawtags $id $x $xt $y1]
1215 }
1215 }
1216 set headline [lindex $commitinfo($id) 0]
1216 set headline [lindex $commitinfo($id) 0]
1217 set name [lindex $commitinfo($id) 1]
1217 set name [lindex $commitinfo($id) 1]
1218 assignauthorcolor $name
1218 assignauthorcolor $name
1219 set fg $aucolormap($name)
1219 set fg $aucolormap($name)
1220 if {$id == $curid} {
1220 if {$id == $curid} {
1221 set fn $curidfont
1221 set fn $curidfont
1222 } else {
1222 } else {
1223 set fn $mainfont
1223 set fn $mainfont
1224 }
1224 }
1225
1225
1226 set date [lindex $commitinfo($id) 2]
1226 set date [lindex $commitinfo($id) 2]
1227 set linehtag($lineno) [$canv create text $xt $y1 -anchor w \
1227 set linehtag($lineno) [$canv create text $xt $y1 -anchor w \
1228 -text $headline -font $fn \
1228 -text $headline -font $fn \
1229 -fill $fg]
1229 -fill $fg]
1230 $canv bind $linehtag($lineno) <<B3>> "rowmenu %X %Y $id"
1230 $canv bind $linehtag($lineno) <<B3>> "rowmenu %X %Y $id"
1231 set linentag($lineno) [$canv2 create text 3 $y1 -anchor w \
1231 set linentag($lineno) [$canv2 create text 3 $y1 -anchor w \
1232 -text $name -font $namefont \
1232 -text $name -font $namefont \
1233 -fill $fg]
1233 -fill $fg]
1234 set linedtag($lineno) [$canv3 create text 3 $y1 -anchor w \
1234 set linedtag($lineno) [$canv3 create text 3 $y1 -anchor w \
1235 -text $date -font $mainfont \
1235 -text $date -font $mainfont \
1236 -fill $fg]
1236 -fill $fg]
1237
1237
1238 set olddlevel $level
1238 set olddlevel $level
1239 set olddisplist $displist
1239 set olddisplist $displist
1240 set oldnlines [llength $displist]
1240 set oldnlines [llength $displist]
1241 }
1241 }
1242
1242
1243 proc drawtags {id x xt y1} {
1243 proc drawtags {id x xt y1} {
1244 global bookmarkcurrent idtags idbookmarks idheads idotherrefs commitinfo
1244 global bookmarkcurrent idtags idbookmarks idheads idotherrefs commitinfo
1245 global linespc lthickness
1245 global linespc lthickness
1246 global canv mainfont idline rowtextx
1246 global canv mainfont idline rowtextx
1247
1247
1248 set marks {}
1248 set marks {}
1249 set nbookmarks 0
1249 set nbookmarks 0
1250 set ntags 0
1250 set ntags 0
1251 set nheads 0
1251 set nheads 0
1252 if {[info exists idtags($id)]} {
1252 if {[info exists idtags($id)]} {
1253 set marks $idtags($id)
1253 set marks $idtags($id)
1254 set ntags [llength $marks]
1254 set ntags [llength $marks]
1255 }
1255 }
1256 if {[info exists idbookmarks($id)]} {
1256 if {[info exists idbookmarks($id)]} {
1257 set marks [concat $marks $idbookmarks($id)]
1257 set marks [concat $marks $idbookmarks($id)]
1258 set nbookmarks [llength $idbookmarks($id)]
1258 set nbookmarks [llength $idbookmarks($id)]
1259 }
1259 }
1260 if {[info exists idheads($id)]} {
1260 if {[info exists idheads($id)]} {
1261 set headmark [lindex $commitinfo($id) 7]
1261 set headmark [lindex $commitinfo($id) 7]
1262 if {$headmark ne "default"} {
1262 if {$headmark ne "default"} {
1263 lappend marks $headmark
1263 lappend marks $headmark
1264 set nheads 1
1264 set nheads 1
1265 }
1265 }
1266 }
1266 }
1267 if {$marks eq {}} {
1267 if {$marks eq {}} {
1268 return $xt
1268 return $xt
1269 }
1269 }
1270
1270
1271 set delta [expr {int(0.5 * ($linespc - $lthickness))}]
1271 set delta [expr {int(0.5 * ($linespc - $lthickness))}]
1272 set yt [expr $y1 - 0.5 * $linespc]
1272 set yt [expr $y1 - 0.5 * $linespc]
1273 set yb [expr $yt + $linespc - 1]
1273 set yb [expr $yt + $linespc - 1]
1274 set xvals {}
1274 set xvals {}
1275 set wvals {}
1275 set wvals {}
1276 foreach tag $marks {
1276 foreach tag $marks {
1277 set wid [font measure $mainfont $tag]
1277 set wid [font measure $mainfont $tag]
1278 lappend xvals $xt
1278 lappend xvals $xt
1279 lappend wvals $wid
1279 lappend wvals $wid
1280 set xt [expr {$xt + $delta + $wid + $lthickness + $linespc}]
1280 set xt [expr {$xt + $delta + $wid + $lthickness + $linespc}]
1281 }
1281 }
1282 set t [$canv create line $x $y1 [lindex $xvals end] $y1 \
1282 set t [$canv create line $x $y1 [lindex $xvals end] $y1 \
1283 -width $lthickness -fill black -tags tag.$id]
1283 -width $lthickness -fill black -tags tag.$id]
1284 $canv lower $t
1284 $canv lower $t
1285 foreach tag $marks x $xvals wid $wvals {
1285 foreach tag $marks x $xvals wid $wvals {
1286 set xl [expr $x + $delta]
1286 set xl [expr $x + $delta]
1287 set xr [expr $x + $delta + $wid + $lthickness]
1287 set xr [expr $x + $delta + $wid + $lthickness]
1288 if {[incr ntags -1] >= 0} {
1288 if {[incr ntags -1] >= 0} {
1289 # draw a tag
1289 # draw a tag
1290 set t [$canv create polygon $x [expr $yt + $delta] $xl $yt \
1290 set t [$canv create polygon $x [expr $yt + $delta] $xl $yt \
1291 $xr $yt $xr $yb $xl $yb $x [expr $yb - $delta] \
1291 $xr $yt $xr $yb $xl $yb $x [expr $yb - $delta] \
1292 -width 1 -outline black -fill yellow -tags tag.$id]
1292 -width 1 -outline black -fill yellow -tags tag.$id]
1293 $canv bind $t <1> [list showtag $tag 1]
1293 $canv bind $t <1> [list showtag $tag 1]
1294 set rowtextx($idline($id)) [expr {$xr + $linespc}]
1294 set rowtextx($idline($id)) [expr {$xr + $linespc}]
1295 } elseif {[incr nbookmarks -1] >= 0} {
1295 } elseif {[incr nbookmarks -1] >= 0} {
1296 # draw a tag
1296 # draw a tag
1297 set col "#7f7f7f"
1297 set col "#7f7f7f"
1298 if {[string compare $bookmarkcurrent $tag] == 0} {
1298 if {[string compare $bookmarkcurrent $tag] == 0} {
1299 set col "#bebebe"
1299 set col "#bebebe"
1300 }
1300 }
1301 set xl [expr $xl - $delta/2]
1301 set xl [expr $xl - $delta/2]
1302 $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
1302 $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
1303 -width 1 -outline black -fill $col -tags tag.$id
1303 -width 1 -outline black -fill $col -tags tag.$id
1304 } else {
1304 } else {
1305 # draw a head or other ref
1305 # draw a head or other ref
1306 if {[incr nheads -1] >= 0} {
1306 if {[incr nheads -1] >= 0} {
1307 set col "#00ff00"
1307 set col "#00ff00"
1308 } else {
1308 } else {
1309 set col "#ddddff"
1309 set col "#ddddff"
1310 }
1310 }
1311 set xl [expr $xl - $delta/2]
1311 set xl [expr $xl - $delta/2]
1312 $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
1312 $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
1313 -width 1 -outline black -fill $col -tags tag.$id
1313 -width 1 -outline black -fill $col -tags tag.$id
1314 }
1314 }
1315 set t [$canv create text $xl $y1 -anchor w -text $tag \
1315 set t [$canv create text $xl $y1 -anchor w -text $tag \
1316 -font $mainfont -tags tag.$id]
1316 -font $mainfont -tags tag.$id]
1317 if {$ntags >= 0} {
1317 if {$ntags >= 0} {
1318 $canv bind $t <1> [list showtag $tag 1]
1318 $canv bind $t <1> [list showtag $tag 1]
1319 }
1319 }
1320 }
1320 }
1321 return $xt
1321 return $xt
1322 }
1322 }
1323
1323
1324 proc notecrossings {id lo hi corner} {
1324 proc notecrossings {id lo hi corner} {
1325 global olddisplist crossings cornercrossings
1325 global olddisplist crossings cornercrossings
1326
1326
1327 for {set i $lo} {[incr i] < $hi} {} {
1327 for {set i $lo} {[incr i] < $hi} {} {
1328 set p [lindex $olddisplist $i]
1328 set p [lindex $olddisplist $i]
1329 if {$p == {}} continue
1329 if {$p == {}} continue
1330 if {$i == $corner} {
1330 if {$i == $corner} {
1331 if {![info exists cornercrossings($id)]
1331 if {![info exists cornercrossings($id)]
1332 || [lsearch -exact $cornercrossings($id) $p] < 0} {
1332 || [lsearch -exact $cornercrossings($id) $p] < 0} {
1333 lappend cornercrossings($id) $p
1333 lappend cornercrossings($id) $p
1334 }
1334 }
1335 if {![info exists cornercrossings($p)]
1335 if {![info exists cornercrossings($p)]
1336 || [lsearch -exact $cornercrossings($p) $id] < 0} {
1336 || [lsearch -exact $cornercrossings($p) $id] < 0} {
1337 lappend cornercrossings($p) $id
1337 lappend cornercrossings($p) $id
1338 }
1338 }
1339 } else {
1339 } else {
1340 if {![info exists crossings($id)]
1340 if {![info exists crossings($id)]
1341 || [lsearch -exact $crossings($id) $p] < 0} {
1341 || [lsearch -exact $crossings($id) $p] < 0} {
1342 lappend crossings($id) $p
1342 lappend crossings($id) $p
1343 }
1343 }
1344 if {![info exists crossings($p)]
1344 if {![info exists crossings($p)]
1345 || [lsearch -exact $crossings($p) $id] < 0} {
1345 || [lsearch -exact $crossings($p) $id] < 0} {
1346 lappend crossings($p) $id
1346 lappend crossings($p) $id
1347 }
1347 }
1348 }
1348 }
1349 }
1349 }
1350 }
1350 }
1351
1351
1352 proc xcoord {i level ln} {
1352 proc xcoord {i level ln} {
1353 global canvx0 xspc1 xspc2
1353 global canvx0 xspc1 xspc2
1354
1354
1355 set x [expr {$canvx0 + $i * $xspc1($ln)}]
1355 set x [expr {$canvx0 + $i * $xspc1($ln)}]
1356 if {$i > 0 && $i == $level} {
1356 if {$i > 0 && $i == $level} {
1357 set x [expr {$x + 0.5 * ($xspc2 - $xspc1($ln))}]
1357 set x [expr {$x + 0.5 * ($xspc2 - $xspc1($ln))}]
1358 } elseif {$i > $level} {
1358 } elseif {$i > $level} {
1359 set x [expr {$x + $xspc2 - $xspc1($ln)}]
1359 set x [expr {$x + $xspc2 - $xspc1($ln)}]
1360 }
1360 }
1361 return $x
1361 return $x
1362 }
1362 }
1363
1363
1364 # it seems Tk can't draw arrows on the end of diagonal line segments...
1364 # it seems Tk can't draw arrows on the end of diagonal line segments...
1365 proc trimdiagend {line} {
1365 proc trimdiagend {line} {
1366 while {[llength $line] > 4} {
1366 while {[llength $line] > 4} {
1367 set x1 [lindex $line end-3]
1367 set x1 [lindex $line end-3]
1368 set y1 [lindex $line end-2]
1368 set y1 [lindex $line end-2]
1369 set x2 [lindex $line end-1]
1369 set x2 [lindex $line end-1]
1370 set y2 [lindex $line end]
1370 set y2 [lindex $line end]
1371 if {($x1 == $x2) != ($y1 == $y2)} break
1371 if {($x1 == $x2) != ($y1 == $y2)} break
1372 set line [lreplace $line end-1 end]
1372 set line [lreplace $line end-1 end]
1373 }
1373 }
1374 return $line
1374 return $line
1375 }
1375 }
1376
1376
1377 proc trimdiagstart {line} {
1377 proc trimdiagstart {line} {
1378 while {[llength $line] > 4} {
1378 while {[llength $line] > 4} {
1379 set x1 [lindex $line 0]
1379 set x1 [lindex $line 0]
1380 set y1 [lindex $line 1]
1380 set y1 [lindex $line 1]
1381 set x2 [lindex $line 2]
1381 set x2 [lindex $line 2]
1382 set y2 [lindex $line 3]
1382 set y2 [lindex $line 3]
1383 if {($x1 == $x2) != ($y1 == $y2)} break
1383 if {($x1 == $x2) != ($y1 == $y2)} break
1384 set line [lreplace $line 0 1]
1384 set line [lreplace $line 0 1]
1385 }
1385 }
1386 return $line
1386 return $line
1387 }
1387 }
1388
1388
1389 proc drawslants {id needonscreen nohs} {
1389 proc drawslants {id needonscreen nohs} {
1390 global canv mainline mainlinearrow sidelines
1390 global canv mainline mainlinearrow sidelines
1391 global canvx0 canvy xspc1 xspc2 lthickness
1391 global canvx0 canvy xspc1 xspc2 lthickness
1392 global currentparents dupparents
1392 global currentparents dupparents
1393 global lthickness linespc canvy colormap lineno geometry
1393 global lthickness linespc canvy colormap lineno geometry
1394 global maxgraphpct maxwidth
1394 global maxgraphpct maxwidth
1395 global displist onscreen lastuse
1395 global displist onscreen lastuse
1396 global parents commitlisted
1396 global parents commitlisted
1397 global oldnlines olddlevel olddisplist
1397 global oldnlines olddlevel olddisplist
1398 global nhyperspace numcommits nnewparents
1398 global nhyperspace numcommits nnewparents
1399
1399
1400 if {$lineno < 0} {
1400 if {$lineno < 0} {
1401 lappend displist $id
1401 lappend displist $id
1402 set onscreen($id) 1
1402 set onscreen($id) 1
1403 return 0
1403 return 0
1404 }
1404 }
1405
1405
1406 set y1 [expr {$canvy - $linespc}]
1406 set y1 [expr {$canvy - $linespc}]
1407 set y2 $canvy
1407 set y2 $canvy
1408
1408
1409 # work out what we need to get back on screen
1409 # work out what we need to get back on screen
1410 set reins {}
1410 set reins {}
1411 if {$onscreen($id) < 0} {
1411 if {$onscreen($id) < 0} {
1412 # next to do isn't displayed, better get it on screen...
1412 # next to do isn't displayed, better get it on screen...
1413 lappend reins [list $id 0]
1413 lappend reins [list $id 0]
1414 }
1414 }
1415 # make sure all the previous commits's parents are on the screen
1415 # make sure all the previous commits's parents are on the screen
1416 foreach p $currentparents {
1416 foreach p $currentparents {
1417 if {$onscreen($p) < 0} {
1417 if {$onscreen($p) < 0} {
1418 lappend reins [list $p 0]
1418 lappend reins [list $p 0]
1419 }
1419 }
1420 }
1420 }
1421 # bring back anything requested by caller
1421 # bring back anything requested by caller
1422 if {$needonscreen ne {}} {
1422 if {$needonscreen ne {}} {
1423 lappend reins $needonscreen
1423 lappend reins $needonscreen
1424 }
1424 }
1425
1425
1426 # try the shortcut
1426 # try the shortcut
1427 if {$currentparents == $id && $onscreen($id) == 0 && $reins eq {}} {
1427 if {$currentparents == $id && $onscreen($id) == 0 && $reins eq {}} {
1428 set dlevel $olddlevel
1428 set dlevel $olddlevel
1429 set x [xcoord $dlevel $dlevel $lineno]
1429 set x [xcoord $dlevel $dlevel $lineno]
1430 set mainline($id) [list $x $y1]
1430 set mainline($id) [list $x $y1]
1431 set mainlinearrow($id) none
1431 set mainlinearrow($id) none
1432 set lastuse($id) $lineno
1432 set lastuse($id) $lineno
1433 set displist [lreplace $displist $dlevel $dlevel $id]
1433 set displist [lreplace $displist $dlevel $dlevel $id]
1434 set onscreen($id) 1
1434 set onscreen($id) 1
1435 set xspc1([expr {$lineno + 1}]) $xspc1($lineno)
1435 set xspc1([expr {$lineno + 1}]) $xspc1($lineno)
1436 return $dlevel
1436 return $dlevel
1437 }
1437 }
1438
1438
1439 # update displist
1439 # update displist
1440 set displist [lreplace $displist $olddlevel $olddlevel]
1440 set displist [lreplace $displist $olddlevel $olddlevel]
1441 set j $olddlevel
1441 set j $olddlevel
1442 foreach p $currentparents {
1442 foreach p $currentparents {
1443 set lastuse($p) $lineno
1443 set lastuse($p) $lineno
1444 if {$onscreen($p) == 0} {
1444 if {$onscreen($p) == 0} {
1445 set displist [linsert $displist $j $p]
1445 set displist [linsert $displist $j $p]
1446 set onscreen($p) 1
1446 set onscreen($p) 1
1447 incr j
1447 incr j
1448 }
1448 }
1449 }
1449 }
1450 if {$onscreen($id) == 0} {
1450 if {$onscreen($id) == 0} {
1451 lappend displist $id
1451 lappend displist $id
1452 set onscreen($id) 1
1452 set onscreen($id) 1
1453 }
1453 }
1454
1454
1455 # remove the null entry if present
1455 # remove the null entry if present
1456 set nullentry [lsearch -exact $displist {}]
1456 set nullentry [lsearch -exact $displist {}]
1457 if {$nullentry >= 0} {
1457 if {$nullentry >= 0} {
1458 set displist [lreplace $displist $nullentry $nullentry]
1458 set displist [lreplace $displist $nullentry $nullentry]
1459 }
1459 }
1460
1460
1461 # bring back the ones we need now (if we did it earlier
1461 # bring back the ones we need now (if we did it earlier
1462 # it would change displist and invalidate olddlevel)
1462 # it would change displist and invalidate olddlevel)
1463 foreach pi $reins {
1463 foreach pi $reins {
1464 # test again in case of duplicates in reins
1464 # test again in case of duplicates in reins
1465 set p [lindex $pi 0]
1465 set p [lindex $pi 0]
1466 if {$onscreen($p) < 0} {
1466 if {$onscreen($p) < 0} {
1467 set onscreen($p) 1
1467 set onscreen($p) 1
1468 set lastuse($p) $lineno
1468 set lastuse($p) $lineno
1469 set displist [linsert $displist [lindex $pi 1] $p]
1469 set displist [linsert $displist [lindex $pi 1] $p]
1470 incr nhyperspace -1
1470 incr nhyperspace -1
1471 }
1471 }
1472 }
1472 }
1473
1473
1474 set lastuse($id) $lineno
1474 set lastuse($id) $lineno
1475
1475
1476 # see if we need to make any lines jump off into hyperspace
1476 # see if we need to make any lines jump off into hyperspace
1477 set displ [llength $displist]
1477 set displ [llength $displist]
1478 if {$displ > $maxwidth} {
1478 if {$displ > $maxwidth} {
1479 set ages {}
1479 set ages {}
1480 foreach x $displist {
1480 foreach x $displist {
1481 lappend ages [list $lastuse($x) $x]
1481 lappend ages [list $lastuse($x) $x]
1482 }
1482 }
1483 set ages [lsort -integer -index 0 $ages]
1483 set ages [lsort -integer -index 0 $ages]
1484 set k 0
1484 set k 0
1485 while {$displ > $maxwidth} {
1485 while {$displ > $maxwidth} {
1486 set use [lindex $ages $k 0]
1486 set use [lindex $ages $k 0]
1487 set victim [lindex $ages $k 1]
1487 set victim [lindex $ages $k 1]
1488 if {$use >= $lineno - 5} break
1488 if {$use >= $lineno - 5} break
1489 incr k
1489 incr k
1490 if {[lsearch -exact $nohs $victim] >= 0} continue
1490 if {[lsearch -exact $nohs $victim] >= 0} continue
1491 set i [lsearch -exact $displist $victim]
1491 set i [lsearch -exact $displist $victim]
1492 set displist [lreplace $displist $i $i]
1492 set displist [lreplace $displist $i $i]
1493 set onscreen($victim) -1
1493 set onscreen($victim) -1
1494 incr nhyperspace
1494 incr nhyperspace
1495 incr displ -1
1495 incr displ -1
1496 if {$i < $nullentry} {
1496 if {$i < $nullentry} {
1497 incr nullentry -1
1497 incr nullentry -1
1498 }
1498 }
1499 set x [lindex $mainline($victim) end-1]
1499 set x [lindex $mainline($victim) end-1]
1500 lappend mainline($victim) $x $y1
1500 lappend mainline($victim) $x $y1
1501 set line [trimdiagend $mainline($victim)]
1501 set line [trimdiagend $mainline($victim)]
1502 set arrow "last"
1502 set arrow "last"
1503 if {$mainlinearrow($victim) ne "none"} {
1503 if {$mainlinearrow($victim) ne "none"} {
1504 set line [trimdiagstart $line]
1504 set line [trimdiagstart $line]
1505 set arrow "both"
1505 set arrow "both"
1506 }
1506 }
1507 lappend sidelines($victim) [list $line 1 $arrow]
1507 lappend sidelines($victim) [list $line 1 $arrow]
1508 unset mainline($victim)
1508 unset mainline($victim)
1509 }
1509 }
1510 }
1510 }
1511
1511
1512 set dlevel [lsearch -exact $displist $id]
1512 set dlevel [lsearch -exact $displist $id]
1513
1513
1514 # If we are reducing, put in a null entry
1514 # If we are reducing, put in a null entry
1515 if {$displ < $oldnlines} {
1515 if {$displ < $oldnlines} {
1516 # does the next line look like a merge?
1516 # does the next line look like a merge?
1517 # i.e. does it have > 1 new parent?
1517 # i.e. does it have > 1 new parent?
1518 if {$nnewparents($id) > 1} {
1518 if {$nnewparents($id) > 1} {
1519 set i [expr {$dlevel + 1}]
1519 set i [expr {$dlevel + 1}]
1520 } elseif {$nnewparents([lindex $olddisplist $olddlevel]) == 0} {
1520 } elseif {$nnewparents([lindex $olddisplist $olddlevel]) == 0} {
1521 set i $olddlevel
1521 set i $olddlevel
1522 if {$nullentry >= 0 && $nullentry < $i} {
1522 if {$nullentry >= 0 && $nullentry < $i} {
1523 incr i -1
1523 incr i -1
1524 }
1524 }
1525 } elseif {$nullentry >= 0} {
1525 } elseif {$nullentry >= 0} {
1526 set i $nullentry
1526 set i $nullentry
1527 while {$i < $displ
1527 while {$i < $displ
1528 && [lindex $olddisplist $i] == [lindex $displist $i]} {
1528 && [lindex $olddisplist $i] == [lindex $displist $i]} {
1529 incr i
1529 incr i
1530 }
1530 }
1531 } else {
1531 } else {
1532 set i $olddlevel
1532 set i $olddlevel
1533 if {$dlevel >= $i} {
1533 if {$dlevel >= $i} {
1534 incr i
1534 incr i
1535 }
1535 }
1536 }
1536 }
1537 if {$i < $displ} {
1537 if {$i < $displ} {
1538 set displist [linsert $displist $i {}]
1538 set displist [linsert $displist $i {}]
1539 incr displ
1539 incr displ
1540 if {$dlevel >= $i} {
1540 if {$dlevel >= $i} {
1541 incr dlevel
1541 incr dlevel
1542 }
1542 }
1543 }
1543 }
1544 }
1544 }
1545
1545
1546 # decide on the line spacing for the next line
1546 # decide on the line spacing for the next line
1547 set lj [expr {$lineno + 1}]
1547 set lj [expr {$lineno + 1}]
1548 set maxw [expr {$maxgraphpct * $geometry(canv1) / 100}]
1548 set maxw [expr {$maxgraphpct * $geometry(canv1) / 100}]
1549 if {$displ <= 1 || $canvx0 + $displ * $xspc2 <= $maxw} {
1549 if {$displ <= 1 || $canvx0 + $displ * $xspc2 <= $maxw} {
1550 set xspc1($lj) $xspc2
1550 set xspc1($lj) $xspc2
1551 } else {
1551 } else {
1552 set xspc1($lj) [expr {($maxw - $canvx0 - $xspc2) / ($displ - 1)}]
1552 set xspc1($lj) [expr {($maxw - $canvx0 - $xspc2) / ($displ - 1)}]
1553 if {$xspc1($lj) < $lthickness} {
1553 if {$xspc1($lj) < $lthickness} {
1554 set xspc1($lj) $lthickness
1554 set xspc1($lj) $lthickness
1555 }
1555 }
1556 }
1556 }
1557
1557
1558 foreach idi $reins {
1558 foreach idi $reins {
1559 set id [lindex $idi 0]
1559 set id [lindex $idi 0]
1560 set j [lsearch -exact $displist $id]
1560 set j [lsearch -exact $displist $id]
1561 set xj [xcoord $j $dlevel $lj]
1561 set xj [xcoord $j $dlevel $lj]
1562 set mainline($id) [list $xj $y2]
1562 set mainline($id) [list $xj $y2]
1563 set mainlinearrow($id) first
1563 set mainlinearrow($id) first
1564 }
1564 }
1565
1565
1566 set i -1
1566 set i -1
1567 foreach id $olddisplist {
1567 foreach id $olddisplist {
1568 incr i
1568 incr i
1569 if {$id == {}} continue
1569 if {$id == {}} continue
1570 if {$onscreen($id) <= 0} continue
1570 if {$onscreen($id) <= 0} continue
1571 set xi [xcoord $i $olddlevel $lineno]
1571 set xi [xcoord $i $olddlevel $lineno]
1572 if {$i == $olddlevel} {
1572 if {$i == $olddlevel} {
1573 foreach p $currentparents {
1573 foreach p $currentparents {
1574 set j [lsearch -exact $displist $p]
1574 set j [lsearch -exact $displist $p]
1575 set coords [list $xi $y1]
1575 set coords [list $xi $y1]
1576 set xj [xcoord $j $dlevel $lj]
1576 set xj [xcoord $j $dlevel $lj]
1577 if {$xj < $xi - $linespc} {
1577 if {$xj < $xi - $linespc} {
1578 lappend coords [expr {$xj + $linespc}] $y1
1578 lappend coords [expr {$xj + $linespc}] $y1
1579 notecrossings $p $j $i [expr {$j + 1}]
1579 notecrossings $p $j $i [expr {$j + 1}]
1580 } elseif {$xj > $xi + $linespc} {
1580 } elseif {$xj > $xi + $linespc} {
1581 lappend coords [expr {$xj - $linespc}] $y1
1581 lappend coords [expr {$xj - $linespc}] $y1
1582 notecrossings $p $i $j [expr {$j - 1}]
1582 notecrossings $p $i $j [expr {$j - 1}]
1583 }
1583 }
1584 if {[lsearch -exact $dupparents $p] >= 0} {
1584 if {[lsearch -exact $dupparents $p] >= 0} {
1585 # draw a double-width line to indicate the doubled parent
1585 # draw a double-width line to indicate the doubled parent
1586 lappend coords $xj $y2
1586 lappend coords $xj $y2
1587 lappend sidelines($p) [list $coords 2 none]
1587 lappend sidelines($p) [list $coords 2 none]
1588 if {![info exists mainline($p)]} {
1588 if {![info exists mainline($p)]} {
1589 set mainline($p) [list $xj $y2]
1589 set mainline($p) [list $xj $y2]
1590 set mainlinearrow($p) none
1590 set mainlinearrow($p) none
1591 }
1591 }
1592 } else {
1592 } else {
1593 # normal case, no parent duplicated
1593 # normal case, no parent duplicated
1594 set yb $y2
1594 set yb $y2
1595 set dx [expr {abs($xi - $xj)}]
1595 set dx [expr {abs($xi - $xj)}]
1596 if {0 && $dx < $linespc} {
1596 if {0 && $dx < $linespc} {
1597 set yb [expr {$y1 + $dx}]
1597 set yb [expr {$y1 + $dx}]
1598 }
1598 }
1599 if {![info exists mainline($p)]} {
1599 if {![info exists mainline($p)]} {
1600 if {$xi != $xj} {
1600 if {$xi != $xj} {
1601 lappend coords $xj $yb
1601 lappend coords $xj $yb
1602 }
1602 }
1603 set mainline($p) $coords
1603 set mainline($p) $coords
1604 set mainlinearrow($p) none
1604 set mainlinearrow($p) none
1605 } else {
1605 } else {
1606 lappend coords $xj $yb
1606 lappend coords $xj $yb
1607 if {$yb < $y2} {
1607 if {$yb < $y2} {
1608 lappend coords $xj $y2
1608 lappend coords $xj $y2
1609 }
1609 }
1610 lappend sidelines($p) [list $coords 1 none]
1610 lappend sidelines($p) [list $coords 1 none]
1611 }
1611 }
1612 }
1612 }
1613 }
1613 }
1614 } else {
1614 } else {
1615 set j $i
1615 set j $i
1616 if {[lindex $displist $i] != $id} {
1616 if {[lindex $displist $i] != $id} {
1617 set j [lsearch -exact $displist $id]
1617 set j [lsearch -exact $displist $id]
1618 }
1618 }
1619 if {$j != $i || $xspc1($lineno) != $xspc1($lj)
1619 if {$j != $i || $xspc1($lineno) != $xspc1($lj)
1620 || ($olddlevel < $i && $i < $dlevel)
1620 || ($olddlevel < $i && $i < $dlevel)
1621 || ($dlevel < $i && $i < $olddlevel)} {
1621 || ($dlevel < $i && $i < $olddlevel)} {
1622 set xj [xcoord $j $dlevel $lj]
1622 set xj [xcoord $j $dlevel $lj]
1623 lappend mainline($id) $xi $y1 $xj $y2
1623 lappend mainline($id) $xi $y1 $xj $y2
1624 }
1624 }
1625 }
1625 }
1626 }
1626 }
1627 return $dlevel
1627 return $dlevel
1628 }
1628 }
1629
1629
1630 # search for x in a list of lists
1630 # search for x in a list of lists
1631 proc llsearch {llist x} {
1631 proc llsearch {llist x} {
1632 set i 0
1632 set i 0
1633 foreach l $llist {
1633 foreach l $llist {
1634 if {$l == $x || [lsearch -exact $l $x] >= 0} {
1634 if {$l == $x || [lsearch -exact $l $x] >= 0} {
1635 return $i
1635 return $i
1636 }
1636 }
1637 incr i
1637 incr i
1638 }
1638 }
1639 return -1
1639 return -1
1640 }
1640 }
1641
1641
1642 proc drawmore {reading} {
1642 proc drawmore {reading} {
1643 global displayorder numcommits ncmupdate nextupdate
1643 global displayorder numcommits ncmupdate nextupdate
1644 global stopped nhyperspace parents commitlisted
1644 global stopped nhyperspace parents commitlisted
1645 global maxwidth onscreen displist currentparents olddlevel
1645 global maxwidth onscreen displist currentparents olddlevel
1646
1646
1647 set n [llength $displayorder]
1647 set n [llength $displayorder]
1648 while {$numcommits < $n} {
1648 while {$numcommits < $n} {
1649 set id [lindex $displayorder $numcommits]
1649 set id [lindex $displayorder $numcommits]
1650 set ctxend [expr {$numcommits + 10}]
1650 set ctxend [expr {$numcommits + 10}]
1651 if {!$reading && $ctxend > $n} {
1651 if {!$reading && $ctxend > $n} {
1652 set ctxend $n
1652 set ctxend $n
1653 }
1653 }
1654 set dlist {}
1654 set dlist {}
1655 if {$numcommits > 0} {
1655 if {$numcommits > 0} {
1656 set dlist [lreplace $displist $olddlevel $olddlevel]
1656 set dlist [lreplace $displist $olddlevel $olddlevel]
1657 set i $olddlevel
1657 set i $olddlevel
1658 foreach p $currentparents {
1658 foreach p $currentparents {
1659 if {$onscreen($p) == 0} {
1659 if {$onscreen($p) == 0} {
1660 set dlist [linsert $dlist $i $p]
1660 set dlist [linsert $dlist $i $p]
1661 incr i
1661 incr i
1662 }
1662 }
1663 }
1663 }
1664 }
1664 }
1665 set nohs {}
1665 set nohs {}
1666 set reins {}
1666 set reins {}
1667 set isfat [expr {[llength $dlist] > $maxwidth}]
1667 set isfat [expr {[llength $dlist] > $maxwidth}]
1668 if {$nhyperspace > 0 || $isfat} {
1668 if {$nhyperspace > 0 || $isfat} {
1669 if {$ctxend > $n} break
1669 if {$ctxend > $n} break
1670 # work out what to bring back and
1670 # work out what to bring back and
1671 # what we want to don't want to send into hyperspace
1671 # what we want to don't want to send into hyperspace
1672 set room 1
1672 set room 1
1673 for {set k $numcommits} {$k < $ctxend} {incr k} {
1673 for {set k $numcommits} {$k < $ctxend} {incr k} {
1674 set x [lindex $displayorder $k]
1674 set x [lindex $displayorder $k]
1675 set i [llsearch $dlist $x]
1675 set i [llsearch $dlist $x]
1676 if {$i < 0} {
1676 if {$i < 0} {
1677 set i [llength $dlist]
1677 set i [llength $dlist]
1678 lappend dlist $x
1678 lappend dlist $x
1679 }
1679 }
1680 if {[lsearch -exact $nohs $x] < 0} {
1680 if {[lsearch -exact $nohs $x] < 0} {
1681 lappend nohs $x
1681 lappend nohs $x
1682 }
1682 }
1683 if {$reins eq {} && $onscreen($x) < 0 && $room} {
1683 if {$reins eq {} && $onscreen($x) < 0 && $room} {
1684 set reins [list $x $i]
1684 set reins [list $x $i]
1685 }
1685 }
1686 set newp {}
1686 set newp {}
1687 if {[info exists commitlisted($x)]} {
1687 if {[info exists commitlisted($x)]} {
1688 set right 0
1688 set right 0
1689 foreach p $parents($x) {
1689 foreach p $parents($x) {
1690 if {[llsearch $dlist $p] < 0} {
1690 if {[llsearch $dlist $p] < 0} {
1691 lappend newp $p
1691 lappend newp $p
1692 if {[lsearch -exact $nohs $p] < 0} {
1692 if {[lsearch -exact $nohs $p] < 0} {
1693 lappend nohs $p
1693 lappend nohs $p
1694 }
1694 }
1695 if {$reins eq {} && $onscreen($p) < 0 && $room} {
1695 if {$reins eq {} && $onscreen($p) < 0 && $room} {
1696 set reins [list $p [expr {$i + $right}]]
1696 set reins [list $p [expr {$i + $right}]]
1697 }
1697 }
1698 }
1698 }
1699 set right 1
1699 set right 1
1700 }
1700 }
1701 }
1701 }
1702 set l [lindex $dlist $i]
1702 set l [lindex $dlist $i]
1703 if {[llength $l] == 1} {
1703 if {[llength $l] == 1} {
1704 set l $newp
1704 set l $newp
1705 } else {
1705 } else {
1706 set j [lsearch -exact $l $x]
1706 set j [lsearch -exact $l $x]
1707 set l [concat [lreplace $l $j $j] $newp]
1707 set l [concat [lreplace $l $j $j] $newp]
1708 }
1708 }
1709 set dlist [lreplace $dlist $i $i $l]
1709 set dlist [lreplace $dlist $i $i $l]
1710 if {$room && $isfat && [llength $newp] <= 1} {
1710 if {$room && $isfat && [llength $newp] <= 1} {
1711 set room 0
1711 set room 0
1712 }
1712 }
1713 }
1713 }
1714 }
1714 }
1715
1715
1716 set dlevel [drawslants $id $reins $nohs]
1716 set dlevel [drawslants $id $reins $nohs]
1717 drawcommitline $dlevel
1717 drawcommitline $dlevel
1718 if {[clock clicks -milliseconds] >= $nextupdate
1718 if {[clock clicks -milliseconds] >= $nextupdate
1719 && $numcommits >= $ncmupdate} {
1719 && $numcommits >= $ncmupdate} {
1720 doupdate $reading
1720 doupdate $reading
1721 if {$stopped} break
1721 if {$stopped} break
1722 }
1722 }
1723 }
1723 }
1724 }
1724 }
1725
1725
1726 # level here is an index in todo
1726 # level here is an index in todo
1727 proc updatetodo {level noshortcut} {
1727 proc updatetodo {level noshortcut} {
1728 global ncleft todo nnewparents
1728 global ncleft todo nnewparents
1729 global commitlisted parents onscreen
1729 global commitlisted parents onscreen
1730
1730
1731 set id [lindex $todo $level]
1731 set id [lindex $todo $level]
1732 set olds {}
1732 set olds {}
1733 if {[info exists commitlisted($id)]} {
1733 if {[info exists commitlisted($id)]} {
1734 foreach p $parents($id) {
1734 foreach p $parents($id) {
1735 if {[lsearch -exact $olds $p] < 0} {
1735 if {[lsearch -exact $olds $p] < 0} {
1736 lappend olds $p
1736 lappend olds $p
1737 }
1737 }
1738 }
1738 }
1739 }
1739 }
1740 if {!$noshortcut && [llength $olds] == 1} {
1740 if {!$noshortcut && [llength $olds] == 1} {
1741 set p [lindex $olds 0]
1741 set p [lindex $olds 0]
1742 if {$ncleft($p) == 1 && [lsearch -exact $todo $p] < 0} {
1742 if {$ncleft($p) == 1 && [lsearch -exact $todo $p] < 0} {
1743 set ncleft($p) 0
1743 set ncleft($p) 0
1744 set todo [lreplace $todo $level $level $p]
1744 set todo [lreplace $todo $level $level $p]
1745 set onscreen($p) 0
1745 set onscreen($p) 0
1746 set nnewparents($id) 1
1746 set nnewparents($id) 1
1747 return 0
1747 return 0
1748 }
1748 }
1749 }
1749 }
1750
1750
1751 set todo [lreplace $todo $level $level]
1751 set todo [lreplace $todo $level $level]
1752 set i $level
1752 set i $level
1753 set n 0
1753 set n 0
1754 foreach p $olds {
1754 foreach p $olds {
1755 incr ncleft($p) -1
1755 incr ncleft($p) -1
1756 set k [lsearch -exact $todo $p]
1756 set k [lsearch -exact $todo $p]
1757 if {$k < 0} {
1757 if {$k < 0} {
1758 set todo [linsert $todo $i $p]
1758 set todo [linsert $todo $i $p]
1759 set onscreen($p) 0
1759 set onscreen($p) 0
1760 incr i
1760 incr i
1761 incr n
1761 incr n
1762 }
1762 }
1763 }
1763 }
1764 set nnewparents($id) $n
1764 set nnewparents($id) $n
1765
1765
1766 return 1
1766 return 1
1767 }
1767 }
1768
1768
1769 proc decidenext {{noread 0}} {
1769 proc decidenext {{noread 0}} {
1770 global ncleft todo
1770 global ncleft todo
1771 global datemode cdate
1771 global datemode cdate
1772 global commitinfo
1772 global commitinfo
1773
1773
1774 # choose which one to do next time around
1774 # choose which one to do next time around
1775 set todol [llength $todo]
1775 set todol [llength $todo]
1776 set level -1
1776 set level -1
1777 set latest {}
1777 set latest {}
1778 for {set k $todol} {[incr k -1] >= 0} {} {
1778 for {set k $todol} {[incr k -1] >= 0} {} {
1779 set p [lindex $todo $k]
1779 set p [lindex $todo $k]
1780 if {$ncleft($p) == 0} {
1780 if {$ncleft($p) == 0} {
1781 if {$datemode} {
1781 if {$datemode} {
1782 if {![info exists commitinfo($p)]} {
1782 if {![info exists commitinfo($p)]} {
1783 if {$noread} {
1783 if {$noread} {
1784 return {}
1784 return {}
1785 }
1785 }
1786 readcommit $p
1786 readcommit $p
1787 }
1787 }
1788 if {$latest == {} || $cdate($p) > $latest} {
1788 if {$latest == {} || $cdate($p) > $latest} {
1789 set level $k
1789 set level $k
1790 set latest $cdate($p)
1790 set latest $cdate($p)
1791 }
1791 }
1792 } else {
1792 } else {
1793 set level $k
1793 set level $k
1794 break
1794 break
1795 }
1795 }
1796 }
1796 }
1797 }
1797 }
1798 if {$level < 0} {
1798 if {$level < 0} {
1799 if {$todo != {}} {
1799 if {$todo != {}} {
1800 puts "ERROR: none of the pending commits can be done yet:"
1800 puts "ERROR: none of the pending commits can be done yet:"
1801 foreach p $todo {
1801 foreach p $todo {
1802 puts " $p ($ncleft($p))"
1802 puts " $p ($ncleft($p))"
1803 }
1803 }
1804 }
1804 }
1805 return -1
1805 return -1
1806 }
1806 }
1807
1807
1808 return $level
1808 return $level
1809 }
1809 }
1810
1810
1811 proc drawcommit {id} {
1811 proc drawcommit {id} {
1812 global phase todo nchildren datemode nextupdate
1812 global phase todo nchildren datemode nextupdate
1813 global numcommits ncmupdate displayorder todo onscreen
1813 global numcommits ncmupdate displayorder todo onscreen
1814
1814
1815 if {$phase != "incrdraw"} {
1815 if {$phase != "incrdraw"} {
1816 set phase incrdraw
1816 set phase incrdraw
1817 set displayorder {}
1817 set displayorder {}
1818 set todo {}
1818 set todo {}
1819 initgraph
1819 initgraph
1820 }
1820 }
1821 if {$nchildren($id) == 0} {
1821 if {$nchildren($id) == 0} {
1822 lappend todo $id
1822 lappend todo $id
1823 set onscreen($id) 0
1823 set onscreen($id) 0
1824 }
1824 }
1825 set level [decidenext 1]
1825 set level [decidenext 1]
1826 if {$level == {} || $id != [lindex $todo $level]} {
1826 if {$level == {} || $id != [lindex $todo $level]} {
1827 return
1827 return
1828 }
1828 }
1829 while 1 {
1829 while 1 {
1830 lappend displayorder [lindex $todo $level]
1830 lappend displayorder [lindex $todo $level]
1831 if {[updatetodo $level $datemode]} {
1831 if {[updatetodo $level $datemode]} {
1832 set level [decidenext 1]
1832 set level [decidenext 1]
1833 if {$level == {}} break
1833 if {$level == {}} break
1834 }
1834 }
1835 set id [lindex $todo $level]
1835 set id [lindex $todo $level]
1836 if {![info exists commitlisted($id)]} {
1836 if {![info exists commitlisted($id)]} {
1837 break
1837 break
1838 }
1838 }
1839 }
1839 }
1840 drawmore 1
1840 drawmore 1
1841 }
1841 }
1842
1842
1843 proc finishcommits {} {
1843 proc finishcommits {} {
1844 global phase
1844 global phase
1845 global canv mainfont ctext maincursor textcursor
1845 global canv mainfont ctext maincursor textcursor
1846
1846
1847 if {$phase != "incrdraw"} {
1847 if {$phase != "incrdraw"} {
1848 $canv delete all
1848 $canv delete all
1849 $canv create text 3 3 -anchor nw -text "No commits selected" \
1849 $canv create text 3 3 -anchor nw -text "No commits selected" \
1850 -font $mainfont -tags textitems
1850 -font $mainfont -tags textitems
1851 set phase {}
1851 set phase {}
1852 } else {
1852 } else {
1853 drawrest
1853 drawrest
1854 }
1854 }
1855 . config -cursor $maincursor
1855 . config -cursor $maincursor
1856 settextcursor $textcursor
1856 settextcursor $textcursor
1857 }
1857 }
1858
1858
1859 # Don't change the text pane cursor if it is currently the hand cursor,
1859 # Don't change the text pane cursor if it is currently the hand cursor,
1860 # showing that we are over a sha1 ID link.
1860 # showing that we are over a sha1 ID link.
1861 proc settextcursor {c} {
1861 proc settextcursor {c} {
1862 global ctext curtextcursor
1862 global ctext curtextcursor
1863
1863
1864 if {[$ctext cget -cursor] == $curtextcursor} {
1864 if {[$ctext cget -cursor] == $curtextcursor} {
1865 $ctext config -cursor $c
1865 $ctext config -cursor $c
1866 }
1866 }
1867 set curtextcursor $c
1867 set curtextcursor $c
1868 }
1868 }
1869
1869
1870 proc drawgraph {} {
1870 proc drawgraph {} {
1871 global nextupdate startmsecs ncmupdate
1871 global nextupdate startmsecs ncmupdate
1872 global displayorder onscreen
1872 global displayorder onscreen
1873
1873
1874 if {$displayorder == {}} return
1874 if {$displayorder == {}} return
1875 set startmsecs [clock clicks -milliseconds]
1875 set startmsecs [clock clicks -milliseconds]
1876 set nextupdate [expr $startmsecs + 100]
1876 set nextupdate [expr $startmsecs + 100]
1877 set ncmupdate 1
1877 set ncmupdate 1
1878 initgraph
1878 initgraph
1879 foreach id $displayorder {
1879 foreach id $displayorder {
1880 set onscreen($id) 0
1880 set onscreen($id) 0
1881 }
1881 }
1882 drawmore 0
1882 drawmore 0
1883 }
1883 }
1884
1884
1885 proc drawrest {} {
1885 proc drawrest {} {
1886 global phase stopped redisplaying selectedline
1886 global phase stopped redisplaying selectedline
1887 global datemode todo displayorder
1887 global datemode todo displayorder
1888 global numcommits ncmupdate
1888 global numcommits ncmupdate
1889 global nextupdate startmsecs
1889 global nextupdate startmsecs
1890
1890
1891 set level [decidenext]
1891 set level [decidenext]
1892 if {$level >= 0} {
1892 if {$level >= 0} {
1893 set phase drawgraph
1893 set phase drawgraph
1894 while 1 {
1894 while 1 {
1895 lappend displayorder [lindex $todo $level]
1895 lappend displayorder [lindex $todo $level]
1896 set hard [updatetodo $level $datemode]
1896 set hard [updatetodo $level $datemode]
1897 if {$hard} {
1897 if {$hard} {
1898 set level [decidenext]
1898 set level [decidenext]
1899 if {$level < 0} break
1899 if {$level < 0} break
1900 }
1900 }
1901 }
1901 }
1902 drawmore 0
1902 drawmore 0
1903 }
1903 }
1904 set phase {}
1904 set phase {}
1905 set drawmsecs [expr [clock clicks -milliseconds] - $startmsecs]
1905 set drawmsecs [expr [clock clicks -milliseconds] - $startmsecs]
1906 #puts "overall $drawmsecs ms for $numcommits commits"
1906 #puts "overall $drawmsecs ms for $numcommits commits"
1907 if {$redisplaying} {
1907 if {$redisplaying} {
1908 if {$stopped == 0 && [info exists selectedline]} {
1908 if {$stopped == 0 && [info exists selectedline]} {
1909 selectline $selectedline 0
1909 selectline $selectedline 0
1910 }
1910 }
1911 if {$stopped == 1} {
1911 if {$stopped == 1} {
1912 set stopped 0
1912 set stopped 0
1913 after idle drawgraph
1913 after idle drawgraph
1914 } else {
1914 } else {
1915 set redisplaying 0
1915 set redisplaying 0
1916 }
1916 }
1917 }
1917 }
1918 }
1918 }
1919
1919
1920 proc findmatches {f} {
1920 proc findmatches {f} {
1921 global findtype foundstring foundstrlen
1921 global findtype foundstring foundstrlen
1922 if {$findtype == "Regexp"} {
1922 if {$findtype == "Regexp"} {
1923 set matches [regexp -indices -all -inline $foundstring $f]
1923 set matches [regexp -indices -all -inline $foundstring $f]
1924 } else {
1924 } else {
1925 if {$findtype == "IgnCase"} {
1925 if {$findtype == "IgnCase"} {
1926 set str [string tolower $f]
1926 set str [string tolower $f]
1927 } else {
1927 } else {
1928 set str $f
1928 set str $f
1929 }
1929 }
1930 set matches {}
1930 set matches {}
1931 set i 0
1931 set i 0
1932 while {[set j [string first $foundstring $str $i]] >= 0} {
1932 while {[set j [string first $foundstring $str $i]] >= 0} {
1933 lappend matches [list $j [expr $j+$foundstrlen-1]]
1933 lappend matches [list $j [expr $j+$foundstrlen-1]]
1934 set i [expr $j + $foundstrlen]
1934 set i [expr $j + $foundstrlen]
1935 }
1935 }
1936 }
1936 }
1937 return $matches
1937 return $matches
1938 }
1938 }
1939
1939
1940 proc dofind {} {
1940 proc dofind {} {
1941 global findtype findloc findstring markedmatches commitinfo
1941 global findtype findloc findstring markedmatches commitinfo
1942 global numcommits lineid linehtag linentag linedtag
1942 global numcommits lineid linehtag linentag linedtag
1943 global mainfont namefont canv canv2 canv3 selectedline
1943 global mainfont namefont canv canv2 canv3 selectedline
1944 global matchinglines foundstring foundstrlen
1944 global matchinglines foundstring foundstrlen
1945
1945
1946 stopfindproc
1946 stopfindproc
1947 unmarkmatches
1947 unmarkmatches
1948 focus .
1948 focus .
1949 set matchinglines {}
1949 set matchinglines {}
1950 if {$findloc == "Pickaxe"} {
1950 if {$findloc == "Pickaxe"} {
1951 findpatches
1951 findpatches
1952 return
1952 return
1953 }
1953 }
1954 if {$findtype == "IgnCase"} {
1954 if {$findtype == "IgnCase"} {
1955 set foundstring [string tolower $findstring]
1955 set foundstring [string tolower $findstring]
1956 } else {
1956 } else {
1957 set foundstring $findstring
1957 set foundstring $findstring
1958 }
1958 }
1959 set foundstrlen [string length $findstring]
1959 set foundstrlen [string length $findstring]
1960 if {$foundstrlen == 0} return
1960 if {$foundstrlen == 0} return
1961 if {$findloc == "Files"} {
1961 if {$findloc == "Files"} {
1962 findfiles
1962 findfiles
1963 return
1963 return
1964 }
1964 }
1965 if {![info exists selectedline]} {
1965 if {![info exists selectedline]} {
1966 set oldsel -1
1966 set oldsel -1
1967 } else {
1967 } else {
1968 set oldsel $selectedline
1968 set oldsel $selectedline
1969 }
1969 }
1970 set didsel 0
1970 set didsel 0
1971 set fldtypes {Headline Author Date CDate Comment}
1971 set fldtypes {Headline Author Date CDate Comment}
1972 for {set l 0} {$l < $numcommits} {incr l} {
1972 for {set l 0} {$l < $numcommits} {incr l} {
1973 set id $lineid($l)
1973 set id $lineid($l)
1974 set info $commitinfo($id)
1974 set info $commitinfo($id)
1975 set doesmatch 0
1975 set doesmatch 0
1976 foreach f $info ty $fldtypes {
1976 foreach f $info ty $fldtypes {
1977 if {$findloc != "All fields" && $findloc != $ty} {
1977 if {$findloc != "All fields" && $findloc != $ty} {
1978 continue
1978 continue
1979 }
1979 }
1980 set matches [findmatches $f]
1980 set matches [findmatches $f]
1981 if {$matches == {}} continue
1981 if {$matches == {}} continue
1982 set doesmatch 1
1982 set doesmatch 1
1983 if {$ty == "Headline"} {
1983 if {$ty == "Headline"} {
1984 markmatches $canv $l $f $linehtag($l) $matches $mainfont
1984 markmatches $canv $l $f $linehtag($l) $matches $mainfont
1985 } elseif {$ty == "Author"} {
1985 } elseif {$ty == "Author"} {
1986 markmatches $canv2 $l $f $linentag($l) $matches $namefont
1986 markmatches $canv2 $l $f $linentag($l) $matches $namefont
1987 } elseif {$ty == "Date"} {
1987 } elseif {$ty == "Date"} {
1988 markmatches $canv3 $l $f $linedtag($l) $matches $mainfont
1988 markmatches $canv3 $l $f $linedtag($l) $matches $mainfont
1989 }
1989 }
1990 }
1990 }
1991 if {$doesmatch} {
1991 if {$doesmatch} {
1992 lappend matchinglines $l
1992 lappend matchinglines $l
1993 if {!$didsel && $l > $oldsel} {
1993 if {!$didsel && $l > $oldsel} {
1994 findselectline $l
1994 findselectline $l
1995 set didsel 1
1995 set didsel 1
1996 }
1996 }
1997 }
1997 }
1998 }
1998 }
1999 if {$matchinglines == {}} {
1999 if {$matchinglines == {}} {
2000 bell
2000 bell
2001 } elseif {!$didsel} {
2001 } elseif {!$didsel} {
2002 findselectline [lindex $matchinglines 0]
2002 findselectline [lindex $matchinglines 0]
2003 }
2003 }
2004 }
2004 }
2005
2005
2006 proc findselectline {l} {
2006 proc findselectline {l} {
2007 global findloc commentend ctext
2007 global findloc commentend ctext
2008 selectline $l 1
2008 selectline $l 1
2009 if {$findloc == "All fields" || $findloc == "Comments"} {
2009 if {$findloc == "All fields" || $findloc == "Comments"} {
2010 # highlight the matches in the comments
2010 # highlight the matches in the comments
2011 set f [$ctext get 1.0 $commentend]
2011 set f [$ctext get 1.0 $commentend]
2012 set matches [findmatches $f]
2012 set matches [findmatches $f]
2013 foreach match $matches {
2013 foreach match $matches {
2014 set start [lindex $match 0]
2014 set start [lindex $match 0]
2015 set end [expr [lindex $match 1] + 1]
2015 set end [expr [lindex $match 1] + 1]
2016 $ctext tag add found "1.0 + $start c" "1.0 + $end c"
2016 $ctext tag add found "1.0 + $start c" "1.0 + $end c"
2017 }
2017 }
2018 }
2018 }
2019 }
2019 }
2020
2020
2021 proc findnext {restart} {
2021 proc findnext {restart} {
2022 global matchinglines selectedline
2022 global matchinglines selectedline
2023 if {![info exists matchinglines]} {
2023 if {![info exists matchinglines]} {
2024 if {$restart} {
2024 if {$restart} {
2025 dofind
2025 dofind
2026 }
2026 }
2027 return
2027 return
2028 }
2028 }
2029 if {![info exists selectedline]} return
2029 if {![info exists selectedline]} return
2030 foreach l $matchinglines {
2030 foreach l $matchinglines {
2031 if {$l > $selectedline} {
2031 if {$l > $selectedline} {
2032 findselectline $l
2032 findselectline $l
2033 return
2033 return
2034 }
2034 }
2035 }
2035 }
2036 bell
2036 bell
2037 }
2037 }
2038
2038
2039 proc findprev {} {
2039 proc findprev {} {
2040 global matchinglines selectedline
2040 global matchinglines selectedline
2041 if {![info exists matchinglines]} {
2041 if {![info exists matchinglines]} {
2042 dofind
2042 dofind
2043 return
2043 return
2044 }
2044 }
2045 if {![info exists selectedline]} return
2045 if {![info exists selectedline]} return
2046 set prev {}
2046 set prev {}
2047 foreach l $matchinglines {
2047 foreach l $matchinglines {
2048 if {$l >= $selectedline} break
2048 if {$l >= $selectedline} break
2049 set prev $l
2049 set prev $l
2050 }
2050 }
2051 if {$prev != {}} {
2051 if {$prev != {}} {
2052 findselectline $prev
2052 findselectline $prev
2053 } else {
2053 } else {
2054 bell
2054 bell
2055 }
2055 }
2056 }
2056 }
2057
2057
2058 proc findlocchange {name ix op} {
2058 proc findlocchange {name ix op} {
2059 global findloc findtype findtypemenu
2059 global findloc findtype findtypemenu
2060 if {$findloc == "Pickaxe"} {
2060 if {$findloc == "Pickaxe"} {
2061 set findtype Exact
2061 set findtype Exact
2062 set state disabled
2062 set state disabled
2063 } else {
2063 } else {
2064 set state normal
2064 set state normal
2065 }
2065 }
2066 $findtypemenu entryconf 1 -state $state
2066 $findtypemenu entryconf 1 -state $state
2067 $findtypemenu entryconf 2 -state $state
2067 $findtypemenu entryconf 2 -state $state
2068 }
2068 }
2069
2069
2070 proc stopfindproc {{done 0}} {
2070 proc stopfindproc {{done 0}} {
2071 global findprocpid findprocfile findids
2071 global findprocpid findprocfile findids
2072 global ctext findoldcursor phase maincursor textcursor
2072 global ctext findoldcursor phase maincursor textcursor
2073 global findinprogress
2073 global findinprogress
2074
2074
2075 catch {unset findids}
2075 catch {unset findids}
2076 if {[info exists findprocpid]} {
2076 if {[info exists findprocpid]} {
2077 if {!$done} {
2077 if {!$done} {
2078 catch {exec kill $findprocpid}
2078 catch {exec kill $findprocpid}
2079 }
2079 }
2080 catch {close $findprocfile}
2080 catch {close $findprocfile}
2081 unset findprocpid
2081 unset findprocpid
2082 }
2082 }
2083 if {[info exists findinprogress]} {
2083 if {[info exists findinprogress]} {
2084 unset findinprogress
2084 unset findinprogress
2085 if {$phase != "incrdraw"} {
2085 if {$phase != "incrdraw"} {
2086 . config -cursor $maincursor
2086 . config -cursor $maincursor
2087 settextcursor $textcursor
2087 settextcursor $textcursor
2088 }
2088 }
2089 }
2089 }
2090 }
2090 }
2091
2091
2092 proc findpatches {} {
2092 proc findpatches {} {
2093 global findstring selectedline numcommits
2093 global findstring selectedline numcommits
2094 global findprocpid findprocfile
2094 global findprocpid findprocfile
2095 global finddidsel ctext lineid findinprogress
2095 global finddidsel ctext lineid findinprogress
2096 global findinsertpos
2096 global findinsertpos
2097 global env
2097 global env
2098
2098
2099 if {$numcommits == 0} return
2099 if {$numcommits == 0} return
2100
2100
2101 # make a list of all the ids to search, starting at the one
2101 # make a list of all the ids to search, starting at the one
2102 # after the selected line (if any)
2102 # after the selected line (if any)
2103 if {[info exists selectedline]} {
2103 if {[info exists selectedline]} {
2104 set l $selectedline
2104 set l $selectedline
2105 } else {
2105 } else {
2106 set l -1
2106 set l -1
2107 }
2107 }
2108 set inputids {}
2108 set inputids {}
2109 for {set i 0} {$i < $numcommits} {incr i} {
2109 for {set i 0} {$i < $numcommits} {incr i} {
2110 if {[incr l] >= $numcommits} {
2110 if {[incr l] >= $numcommits} {
2111 set l 0
2111 set l 0
2112 }
2112 }
2113 append inputids $lineid($l) "\n"
2113 append inputids $lineid($l) "\n"
2114 }
2114 }
2115
2115
2116 if {[catch {
2116 if {[catch {
2117 set f [open [list | $env(HG) --config ui.report_untrusted=false debug-diff-tree --stdin -s -r -S$findstring << $inputids] r]
2117 set f [open [list | $env(HG) --config ui.report_untrusted=false debug-diff-tree --stdin -s -r -S$findstring << $inputids] r]
2118 } err]} {
2118 } err]} {
2119 error_popup "Error starting search process: $err"
2119 error_popup "Error starting search process: $err"
2120 return
2120 return
2121 }
2121 }
2122
2122
2123 set findinsertpos end
2123 set findinsertpos end
2124 set findprocfile $f
2124 set findprocfile $f
2125 set findprocpid [pid $f]
2125 set findprocpid [pid $f]
2126 fconfigure $f -blocking 0
2126 fconfigure $f -blocking 0
2127 fileevent $f readable readfindproc
2127 fileevent $f readable readfindproc
2128 set finddidsel 0
2128 set finddidsel 0
2129 . config -cursor watch
2129 . config -cursor watch
2130 settextcursor watch
2130 settextcursor watch
2131 set findinprogress 1
2131 set findinprogress 1
2132 }
2132 }
2133
2133
2134 proc readfindproc {} {
2134 proc readfindproc {} {
2135 global findprocfile finddidsel
2135 global findprocfile finddidsel
2136 global idline matchinglines findinsertpos
2136 global idline matchinglines findinsertpos
2137
2137
2138 set n [gets $findprocfile line]
2138 set n [gets $findprocfile line]
2139 if {$n < 0} {
2139 if {$n < 0} {
2140 if {[eof $findprocfile]} {
2140 if {[eof $findprocfile]} {
2141 stopfindproc 1
2141 stopfindproc 1
2142 if {!$finddidsel} {
2142 if {!$finddidsel} {
2143 bell
2143 bell
2144 }
2144 }
2145 }
2145 }
2146 return
2146 return
2147 }
2147 }
2148 if {![regexp {^[0-9a-f]{12}} $line id]} {
2148 if {![regexp {^[0-9a-f]{12}} $line id]} {
2149 error_popup "Can't parse git-diff-tree output: $line"
2149 error_popup "Can't parse git-diff-tree output: $line"
2150 stopfindproc
2150 stopfindproc
2151 return
2151 return
2152 }
2152 }
2153 if {![info exists idline($id)]} {
2153 if {![info exists idline($id)]} {
2154 puts stderr "spurious id: $id"
2154 puts stderr "spurious id: $id"
2155 return
2155 return
2156 }
2156 }
2157 set l $idline($id)
2157 set l $idline($id)
2158 insertmatch $l $id
2158 insertmatch $l $id
2159 }
2159 }
2160
2160
2161 proc insertmatch {l id} {
2161 proc insertmatch {l id} {
2162 global matchinglines findinsertpos finddidsel
2162 global matchinglines findinsertpos finddidsel
2163
2163
2164 if {$findinsertpos == "end"} {
2164 if {$findinsertpos == "end"} {
2165 if {$matchinglines != {} && $l < [lindex $matchinglines 0]} {
2165 if {$matchinglines != {} && $l < [lindex $matchinglines 0]} {
2166 set matchinglines [linsert $matchinglines 0 $l]
2166 set matchinglines [linsert $matchinglines 0 $l]
2167 set findinsertpos 1
2167 set findinsertpos 1
2168 } else {
2168 } else {
2169 lappend matchinglines $l
2169 lappend matchinglines $l
2170 }
2170 }
2171 } else {
2171 } else {
2172 set matchinglines [linsert $matchinglines $findinsertpos $l]
2172 set matchinglines [linsert $matchinglines $findinsertpos $l]
2173 incr findinsertpos
2173 incr findinsertpos
2174 }
2174 }
2175 markheadline $l $id
2175 markheadline $l $id
2176 if {!$finddidsel} {
2176 if {!$finddidsel} {
2177 findselectline $l
2177 findselectline $l
2178 set finddidsel 1
2178 set finddidsel 1
2179 }
2179 }
2180 }
2180 }
2181
2181
2182 proc findfiles {} {
2182 proc findfiles {} {
2183 global selectedline numcommits lineid ctext
2183 global selectedline numcommits lineid ctext
2184 global ffileline finddidsel parents nparents
2184 global ffileline finddidsel parents nparents
2185 global findinprogress findstartline findinsertpos
2185 global findinprogress findstartline findinsertpos
2186 global treediffs fdiffids fdiffsneeded fdiffpos
2186 global treediffs fdiffids fdiffsneeded fdiffpos
2187 global findmergefiles
2187 global findmergefiles
2188 global env
2188 global env
2189
2189
2190 if {$numcommits == 0} return
2190 if {$numcommits == 0} return
2191
2191
2192 if {[info exists selectedline]} {
2192 if {[info exists selectedline]} {
2193 set l [expr {$selectedline + 1}]
2193 set l [expr {$selectedline + 1}]
2194 } else {
2194 } else {
2195 set l 0
2195 set l 0
2196 }
2196 }
2197 set ffileline $l
2197 set ffileline $l
2198 set findstartline $l
2198 set findstartline $l
2199 set diffsneeded {}
2199 set diffsneeded {}
2200 set fdiffsneeded {}
2200 set fdiffsneeded {}
2201 while 1 {
2201 while 1 {
2202 set id $lineid($l)
2202 set id $lineid($l)
2203 if {$findmergefiles || $nparents($id) == 1} {
2203 if {$findmergefiles || $nparents($id) == 1} {
2204 foreach p $parents($id) {
2204 foreach p $parents($id) {
2205 if {![info exists treediffs([list $id $p])]} {
2205 if {![info exists treediffs([list $id $p])]} {
2206 append diffsneeded "$id $p\n"
2206 append diffsneeded "$id $p\n"
2207 lappend fdiffsneeded [list $id $p]
2207 lappend fdiffsneeded [list $id $p]
2208 }
2208 }
2209 }
2209 }
2210 }
2210 }
2211 if {[incr l] >= $numcommits} {
2211 if {[incr l] >= $numcommits} {
2212 set l 0
2212 set l 0
2213 }
2213 }
2214 if {$l == $findstartline} break
2214 if {$l == $findstartline} break
2215 }
2215 }
2216
2216
2217 # start off a git-diff-tree process if needed
2217 # start off a git-diff-tree process if needed
2218 if {$diffsneeded ne {}} {
2218 if {$diffsneeded ne {}} {
2219 if {[catch {
2219 if {[catch {
2220 set df [open [list | $env(HG) --config ui.report_untrusted=false debug-diff-tree -r --stdin << $diffsneeded] r]
2220 set df [open [list | $env(HG) --config ui.report_untrusted=false debug-diff-tree -r --stdin << $diffsneeded] r]
2221 } err ]} {
2221 } err ]} {
2222 error_popup "Error starting search process: $err"
2222 error_popup "Error starting search process: $err"
2223 return
2223 return
2224 }
2224 }
2225 catch {unset fdiffids}
2225 catch {unset fdiffids}
2226 set fdiffpos 0
2226 set fdiffpos 0
2227 fconfigure $df -blocking 0
2227 fconfigure $df -blocking 0
2228 fileevent $df readable [list readfilediffs $df]
2228 fileevent $df readable [list readfilediffs $df]
2229 }
2229 }
2230
2230
2231 set finddidsel 0
2231 set finddidsel 0
2232 set findinsertpos end
2232 set findinsertpos end
2233 set id $lineid($l)
2233 set id $lineid($l)
2234 set p [lindex $parents($id) 0]
2234 set p [lindex $parents($id) 0]
2235 . config -cursor watch
2235 . config -cursor watch
2236 settextcursor watch
2236 settextcursor watch
2237 set findinprogress 1
2237 set findinprogress 1
2238 findcont [list $id $p]
2238 findcont [list $id $p]
2239 update
2239 update
2240 }
2240 }
2241
2241
2242 proc readfilediffs {df} {
2242 proc readfilediffs {df} {
2243 global findids fdiffids fdiffs
2243 global findids fdiffids fdiffs
2244
2244
2245 set n [gets $df line]
2245 set n [gets $df line]
2246 if {$n < 0} {
2246 if {$n < 0} {
2247 if {[eof $df]} {
2247 if {[eof $df]} {
2248 donefilediff
2248 donefilediff
2249 if {[catch {close $df} err]} {
2249 if {[catch {close $df} err]} {
2250 stopfindproc
2250 stopfindproc
2251 bell
2251 bell
2252 error_popup "Error in hg debug-diff-tree: $err"
2252 error_popup "Error in hg debug-diff-tree: $err"
2253 } elseif {[info exists findids]} {
2253 } elseif {[info exists findids]} {
2254 set ids $findids
2254 set ids $findids
2255 stopfindproc
2255 stopfindproc
2256 bell
2256 bell
2257 error_popup "Couldn't find diffs for {$ids}"
2257 error_popup "Couldn't find diffs for {$ids}"
2258 }
2258 }
2259 }
2259 }
2260 return
2260 return
2261 }
2261 }
2262 if {[regexp {^([0-9a-f]{12}) \(from ([0-9a-f]{12})\)} $line match id p]} {
2262 if {[regexp {^([0-9a-f]{12}) \(from ([0-9a-f]{12})\)} $line match id p]} {
2263 # start of a new string of diffs
2263 # start of a new string of diffs
2264 donefilediff
2264 donefilediff
2265 set fdiffids [list $id $p]
2265 set fdiffids [list $id $p]
2266 set fdiffs {}
2266 set fdiffs {}
2267 } elseif {[string match ":*" $line]} {
2267 } elseif {[string match ":*" $line]} {
2268 lappend fdiffs [lindex $line 5]
2268 lappend fdiffs [lindex $line 5]
2269 }
2269 }
2270 }
2270 }
2271
2271
2272 proc donefilediff {} {
2272 proc donefilediff {} {
2273 global fdiffids fdiffs treediffs findids
2273 global fdiffids fdiffs treediffs findids
2274 global fdiffsneeded fdiffpos
2274 global fdiffsneeded fdiffpos
2275
2275
2276 if {[info exists fdiffids]} {
2276 if {[info exists fdiffids]} {
2277 while {[lindex $fdiffsneeded $fdiffpos] ne $fdiffids
2277 while {[lindex $fdiffsneeded $fdiffpos] ne $fdiffids
2278 && $fdiffpos < [llength $fdiffsneeded]} {
2278 && $fdiffpos < [llength $fdiffsneeded]} {
2279 # git-diff-tree doesn't output anything for a commit
2279 # git-diff-tree doesn't output anything for a commit
2280 # which doesn't change anything
2280 # which doesn't change anything
2281 set nullids [lindex $fdiffsneeded $fdiffpos]
2281 set nullids [lindex $fdiffsneeded $fdiffpos]
2282 set treediffs($nullids) {}
2282 set treediffs($nullids) {}
2283 if {[info exists findids] && $nullids eq $findids} {
2283 if {[info exists findids] && $nullids eq $findids} {
2284 unset findids
2284 unset findids
2285 findcont $nullids
2285 findcont $nullids
2286 }
2286 }
2287 incr fdiffpos
2287 incr fdiffpos
2288 }
2288 }
2289 incr fdiffpos
2289 incr fdiffpos
2290
2290
2291 if {![info exists treediffs($fdiffids)]} {
2291 if {![info exists treediffs($fdiffids)]} {
2292 set treediffs($fdiffids) $fdiffs
2292 set treediffs($fdiffids) $fdiffs
2293 }
2293 }
2294 if {[info exists findids] && $fdiffids eq $findids} {
2294 if {[info exists findids] && $fdiffids eq $findids} {
2295 unset findids
2295 unset findids
2296 findcont $fdiffids
2296 findcont $fdiffids
2297 }
2297 }
2298 }
2298 }
2299 }
2299 }
2300
2300
2301 proc findcont {ids} {
2301 proc findcont {ids} {
2302 global findids treediffs parents nparents
2302 global findids treediffs parents nparents
2303 global ffileline findstartline finddidsel
2303 global ffileline findstartline finddidsel
2304 global lineid numcommits matchinglines findinprogress
2304 global lineid numcommits matchinglines findinprogress
2305 global findmergefiles
2305 global findmergefiles
2306
2306
2307 set id [lindex $ids 0]
2307 set id [lindex $ids 0]
2308 set p [lindex $ids 1]
2308 set p [lindex $ids 1]
2309 set pi [lsearch -exact $parents($id) $p]
2309 set pi [lsearch -exact $parents($id) $p]
2310 set l $ffileline
2310 set l $ffileline
2311 while 1 {
2311 while 1 {
2312 if {$findmergefiles || $nparents($id) == 1} {
2312 if {$findmergefiles || $nparents($id) == 1} {
2313 if {![info exists treediffs($ids)]} {
2313 if {![info exists treediffs($ids)]} {
2314 set findids $ids
2314 set findids $ids
2315 set ffileline $l
2315 set ffileline $l
2316 return
2316 return
2317 }
2317 }
2318 set doesmatch 0
2318 set doesmatch 0
2319 foreach f $treediffs($ids) {
2319 foreach f $treediffs($ids) {
2320 set x [findmatches $f]
2320 set x [findmatches $f]
2321 if {$x != {}} {
2321 if {$x != {}} {
2322 set doesmatch 1
2322 set doesmatch 1
2323 break
2323 break
2324 }
2324 }
2325 }
2325 }
2326 if {$doesmatch} {
2326 if {$doesmatch} {
2327 insertmatch $l $id
2327 insertmatch $l $id
2328 set pi $nparents($id)
2328 set pi $nparents($id)
2329 }
2329 }
2330 } else {
2330 } else {
2331 set pi $nparents($id)
2331 set pi $nparents($id)
2332 }
2332 }
2333 if {[incr pi] >= $nparents($id)} {
2333 if {[incr pi] >= $nparents($id)} {
2334 set pi 0
2334 set pi 0
2335 if {[incr l] >= $numcommits} {
2335 if {[incr l] >= $numcommits} {
2336 set l 0
2336 set l 0
2337 }
2337 }
2338 if {$l == $findstartline} break
2338 if {$l == $findstartline} break
2339 set id $lineid($l)
2339 set id $lineid($l)
2340 }
2340 }
2341 set p [lindex $parents($id) $pi]
2341 set p [lindex $parents($id) $pi]
2342 set ids [list $id $p]
2342 set ids [list $id $p]
2343 }
2343 }
2344 stopfindproc
2344 stopfindproc
2345 if {!$finddidsel} {
2345 if {!$finddidsel} {
2346 bell
2346 bell
2347 }
2347 }
2348 }
2348 }
2349
2349
2350 # mark a commit as matching by putting a yellow background
2350 # mark a commit as matching by putting a yellow background
2351 # behind the headline
2351 # behind the headline
2352 proc markheadline {l id} {
2352 proc markheadline {l id} {
2353 global canv mainfont linehtag commitinfo
2353 global canv mainfont linehtag commitinfo
2354
2354
2355 set bbox [$canv bbox $linehtag($l)]
2355 set bbox [$canv bbox $linehtag($l)]
2356 set t [$canv create rect $bbox -outline {} -tags matches -fill yellow]
2356 set t [$canv create rect $bbox -outline {} -tags matches -fill yellow]
2357 $canv lower $t
2357 $canv lower $t
2358 }
2358 }
2359
2359
2360 # mark the bits of a headline, author or date that match a find string
2360 # mark the bits of a headline, author or date that match a find string
2361 proc markmatches {canv l str tag matches font} {
2361 proc markmatches {canv l str tag matches font} {
2362 set bbox [$canv bbox $tag]
2362 set bbox [$canv bbox $tag]
2363 set x0 [lindex $bbox 0]
2363 set x0 [lindex $bbox 0]
2364 set y0 [lindex $bbox 1]
2364 set y0 [lindex $bbox 1]
2365 set y1 [lindex $bbox 3]
2365 set y1 [lindex $bbox 3]
2366 foreach match $matches {
2366 foreach match $matches {
2367 set start [lindex $match 0]
2367 set start [lindex $match 0]
2368 set end [lindex $match 1]
2368 set end [lindex $match 1]
2369 if {$start > $end} continue
2369 if {$start > $end} continue
2370 set xoff [font measure $font [string range $str 0 [expr $start-1]]]
2370 set xoff [font measure $font [string range $str 0 [expr $start-1]]]
2371 set xlen [font measure $font [string range $str 0 [expr $end]]]
2371 set xlen [font measure $font [string range $str 0 [expr $end]]]
2372 set t [$canv create rect [expr $x0+$xoff] $y0 [expr $x0+$xlen+2] $y1 \
2372 set t [$canv create rect [expr $x0+$xoff] $y0 [expr $x0+$xlen+2] $y1 \
2373 -outline {} -tags matches -fill yellow]
2373 -outline {} -tags matches -fill yellow]
2374 $canv lower $t
2374 $canv lower $t
2375 }
2375 }
2376 }
2376 }
2377
2377
2378 proc unmarkmatches {} {
2378 proc unmarkmatches {} {
2379 global matchinglines findids
2379 global matchinglines findids
2380 allcanvs delete matches
2380 allcanvs delete matches
2381 catch {unset matchinglines}
2381 catch {unset matchinglines}
2382 catch {unset findids}
2382 catch {unset findids}
2383 }
2383 }
2384
2384
2385 proc selcanvline {w x y} {
2385 proc selcanvline {w x y} {
2386 global canv canvy0 ctext linespc
2386 global canv canvy0 ctext linespc
2387 global lineid linehtag linentag linedtag rowtextx
2387 global lineid linehtag linentag linedtag rowtextx
2388 set ymax [lindex [$canv cget -scrollregion] 3]
2388 set ymax [lindex [$canv cget -scrollregion] 3]
2389 if {$ymax == {}} return
2389 if {$ymax == {}} return
2390 set yfrac [lindex [$canv yview] 0]
2390 set yfrac [lindex [$canv yview] 0]
2391 set y [expr {$y + $yfrac * $ymax}]
2391 set y [expr {$y + $yfrac * $ymax}]
2392 set l [expr {int(($y - $canvy0) / $linespc + 0.5)}]
2392 set l [expr {int(($y - $canvy0) / $linespc + 0.5)}]
2393 if {$l < 0} {
2393 if {$l < 0} {
2394 set l 0
2394 set l 0
2395 }
2395 }
2396 if {$w eq $canv} {
2396 if {$w eq $canv} {
2397 if {![info exists rowtextx($l)] || $x < $rowtextx($l)} return
2397 if {![info exists rowtextx($l)] || $x < $rowtextx($l)} return
2398 }
2398 }
2399 unmarkmatches
2399 unmarkmatches
2400 selectline $l 1
2400 selectline $l 1
2401 }
2401 }
2402
2402
2403 proc commit_descriptor {p} {
2403 proc commit_descriptor {p} {
2404 global commitinfo
2404 global commitinfo
2405 set l "..."
2405 set l "..."
2406 if {[info exists commitinfo($p)]} {
2406 if {[info exists commitinfo($p)]} {
2407 set l [lindex $commitinfo($p) 0]
2407 set l [lindex $commitinfo($p) 0]
2408 set r [lindex $commitinfo($p) 6]
2408 set r [lindex $commitinfo($p) 6]
2409 }
2409 }
2410 return "$r:$p ($l)"
2410 return "$r:$p ($l)"
2411 }
2411 }
2412
2412
2413 # append some text to the ctext widget, and make any SHA1 ID
2413 # append some text to the ctext widget, and make any SHA1 ID
2414 # that we know about be a clickable link.
2414 # that we know about be a clickable link.
2415 proc appendwithlinks {text} {
2415 proc appendwithlinks {text} {
2416 global ctext idline linknum
2416 global ctext idline linknum
2417
2417
2418 set start [$ctext index "end - 1c"]
2418 set start [$ctext index "end - 1c"]
2419 $ctext insert end $text
2419 $ctext insert end $text
2420 $ctext insert end "\n"
2420 $ctext insert end "\n"
2421 set links [regexp -indices -all -inline {[0-9a-f]{12}} $text]
2421 set links [regexp -indices -all -inline {[0-9a-f]{12}} $text]
2422 foreach l $links {
2422 foreach l $links {
2423 set s [lindex $l 0]
2423 set s [lindex $l 0]
2424 set e [lindex $l 1]
2424 set e [lindex $l 1]
2425 set linkid [string range $text $s $e]
2425 set linkid [string range $text $s $e]
2426 if {![info exists idline($linkid)]} continue
2426 if {![info exists idline($linkid)]} continue
2427 incr e
2427 incr e
2428 $ctext tag add link "$start + $s c" "$start + $e c"
2428 $ctext tag add link "$start + $s c" "$start + $e c"
2429 $ctext tag add link$linknum "$start + $s c" "$start + $e c"
2429 $ctext tag add link$linknum "$start + $s c" "$start + $e c"
2430 $ctext tag bind link$linknum <1> [list selectline $idline($linkid) 1]
2430 $ctext tag bind link$linknum <1> [list selectline $idline($linkid) 1]
2431 incr linknum
2431 incr linknum
2432 }
2432 }
2433 $ctext tag conf link -foreground blue -underline 1
2433 $ctext tag conf link -foreground blue -underline 1
2434 $ctext tag bind link <Enter> { %W configure -cursor hand2 }
2434 $ctext tag bind link <Enter> { %W configure -cursor hand2 }
2435 $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
2435 $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
2436 }
2436 }
2437
2437
2438 proc selectline {l isnew} {
2438 proc selectline {l isnew} {
2439 global canv canv2 canv3 ctext commitinfo selectedline
2439 global canv canv2 canv3 ctext commitinfo selectedline
2440 global lineid linehtag linentag linedtag
2440 global lineid linehtag linentag linedtag
2441 global canvy0 linespc parents nparents children
2441 global canvy0 linespc parents nparents children
2442 global cflist currentid sha1entry
2442 global cflist currentid sha1entry
2443 global commentend idtags idbookmarks idline linknum
2443 global commentend idtags idbookmarks idline linknum
2444
2444
2445 $canv delete hover
2445 $canv delete hover
2446 normalline
2446 normalline
2447 if {![info exists lineid($l)] || ![info exists linehtag($l)]} return
2447 if {![info exists lineid($l)] || ![info exists linehtag($l)]} return
2448 $canv delete secsel
2448 $canv delete secsel
2449 set t [eval $canv create rect [$canv bbox $linehtag($l)] -outline {{}} \
2449 set t [eval $canv create rect [$canv bbox $linehtag($l)] -outline {{}} \
2450 -tags secsel -fill [$canv cget -selectbackground]]
2450 -tags secsel -fill [$canv cget -selectbackground]]
2451 $canv lower $t
2451 $canv lower $t
2452 $canv2 delete secsel
2452 $canv2 delete secsel
2453 set t [eval $canv2 create rect [$canv2 bbox $linentag($l)] -outline {{}} \
2453 set t [eval $canv2 create rect [$canv2 bbox $linentag($l)] -outline {{}} \
2454 -tags secsel -fill [$canv2 cget -selectbackground]]
2454 -tags secsel -fill [$canv2 cget -selectbackground]]
2455 $canv2 lower $t
2455 $canv2 lower $t
2456 $canv3 delete secsel
2456 $canv3 delete secsel
2457 set t [eval $canv3 create rect [$canv3 bbox $linedtag($l)] -outline {{}} \
2457 set t [eval $canv3 create rect [$canv3 bbox $linedtag($l)] -outline {{}} \
2458 -tags secsel -fill [$canv3 cget -selectbackground]]
2458 -tags secsel -fill [$canv3 cget -selectbackground]]
2459 $canv3 lower $t
2459 $canv3 lower $t
2460 set y [expr {$canvy0 + $l * $linespc}]
2460 set y [expr {$canvy0 + $l * $linespc}]
2461 set ymax [lindex [$canv cget -scrollregion] 3]
2461 set ymax [lindex [$canv cget -scrollregion] 3]
2462 set ytop [expr {$y - $linespc - 1}]
2462 set ytop [expr {$y - $linespc - 1}]
2463 set ybot [expr {$y + $linespc + 1}]
2463 set ybot [expr {$y + $linespc + 1}]
2464 set wnow [$canv yview]
2464 set wnow [$canv yview]
2465 set wtop [expr [lindex $wnow 0] * $ymax]
2465 set wtop [expr [lindex $wnow 0] * $ymax]
2466 set wbot [expr [lindex $wnow 1] * $ymax]
2466 set wbot [expr [lindex $wnow 1] * $ymax]
2467 set wh [expr {$wbot - $wtop}]
2467 set wh [expr {$wbot - $wtop}]
2468 set newtop $wtop
2468 set newtop $wtop
2469 if {$ytop < $wtop} {
2469 if {$ytop < $wtop} {
2470 if {$ybot < $wtop} {
2470 if {$ybot < $wtop} {
2471 set newtop [expr {$y - $wh / 2.0}]
2471 set newtop [expr {$y - $wh / 2.0}]
2472 } else {
2472 } else {
2473 set newtop $ytop
2473 set newtop $ytop
2474 if {$newtop > $wtop - $linespc} {
2474 if {$newtop > $wtop - $linespc} {
2475 set newtop [expr {$wtop - $linespc}]
2475 set newtop [expr {$wtop - $linespc}]
2476 }
2476 }
2477 }
2477 }
2478 } elseif {$ybot > $wbot} {
2478 } elseif {$ybot > $wbot} {
2479 if {$ytop > $wbot} {
2479 if {$ytop > $wbot} {
2480 set newtop [expr {$y - $wh / 2.0}]
2480 set newtop [expr {$y - $wh / 2.0}]
2481 } else {
2481 } else {
2482 set newtop [expr {$ybot - $wh}]
2482 set newtop [expr {$ybot - $wh}]
2483 if {$newtop < $wtop + $linespc} {
2483 if {$newtop < $wtop + $linespc} {
2484 set newtop [expr {$wtop + $linespc}]
2484 set newtop [expr {$wtop + $linespc}]
2485 }
2485 }
2486 }
2486 }
2487 }
2487 }
2488 if {$newtop != $wtop} {
2488 if {$newtop != $wtop} {
2489 if {$newtop < 0} {
2489 if {$newtop < 0} {
2490 set newtop 0
2490 set newtop 0
2491 }
2491 }
2492 allcanvs yview moveto [expr $newtop * 1.0 / $ymax]
2492 allcanvs yview moveto [expr $newtop * 1.0 / $ymax]
2493 }
2493 }
2494
2494
2495 if {$isnew} {
2495 if {$isnew} {
2496 addtohistory [list selectline $l 0]
2496 addtohistory [list selectline $l 0]
2497 }
2497 }
2498
2498
2499 set selectedline $l
2499 set selectedline $l
2500
2500
2501 set id $lineid($l)
2501 set id $lineid($l)
2502 set currentid $id
2502 set currentid $id
2503 $sha1entry delete 0 end
2503 $sha1entry delete 0 end
2504 $sha1entry insert 0 $id
2504 $sha1entry insert 0 $id
2505 $sha1entry selection range 0 end
2505 $sha1entry selection range 0 end
2506
2506
2507 $ctext conf -state normal
2507 $ctext conf -state normal
2508 $ctext delete 0.0 end
2508 $ctext delete 0.0 end
2509 set linknum 0
2509 set linknum 0
2510 $ctext mark set fmark.0 0.0
2510 $ctext mark set fmark.0 0.0
2511 $ctext mark gravity fmark.0 left
2511 $ctext mark gravity fmark.0 left
2512 set info $commitinfo($id)
2512 set info $commitinfo($id)
2513 $ctext insert end "Changeset: [lindex $info 6]\n"
2513 $ctext insert end "Changeset: [lindex $info 6]\n"
2514 if {[llength [lindex $info 7]] > 0} {
2514 if {[llength [lindex $info 7]] > 0} {
2515 $ctext insert end "Branch: [lindex $info 7]\n"
2515 $ctext insert end "Branch: [lindex $info 7]\n"
2516 }
2516 }
2517 $ctext insert end "User: [lindex $info 1]\n"
2517 $ctext insert end "User: [lindex $info 1]\n"
2518 $ctext insert end "Date: [lindex $info 2]\n"
2518 $ctext insert end "Date: [lindex $info 2]\n"
2519 if {[lindex $info 3] ne ""} {
2520 $ctext insert end "Committer: [lindex $info 3]\n"
2521 }
2519 if {[info exists idbookmarks($id)]} {
2522 if {[info exists idbookmarks($id)]} {
2520 $ctext insert end "Bookmarks:"
2523 $ctext insert end "Bookmarks:"
2521 foreach bookmark $idbookmarks($id) {
2524 foreach bookmark $idbookmarks($id) {
2522 $ctext insert end " $bookmark"
2525 $ctext insert end " $bookmark"
2523 }
2526 }
2524 $ctext insert end "\n"
2527 $ctext insert end "\n"
2525 }
2528 }
2526
2529
2527 if {[info exists idtags($id)]} {
2530 if {[info exists idtags($id)]} {
2528 $ctext insert end "Tags:"
2531 $ctext insert end "Tags:"
2529 foreach tag $idtags($id) {
2532 foreach tag $idtags($id) {
2530 $ctext insert end " $tag"
2533 $ctext insert end " $tag"
2531 }
2534 }
2532 $ctext insert end "\n"
2535 $ctext insert end "\n"
2533 }
2536 }
2534
2537
2535 set comment {}
2538 set comment {}
2536 if {[info exists parents($id)]} {
2539 if {[info exists parents($id)]} {
2537 foreach p $parents($id) {
2540 foreach p $parents($id) {
2538 append comment "Parent: [commit_descriptor $p]\n"
2541 append comment "Parent: [commit_descriptor $p]\n"
2539 }
2542 }
2540 }
2543 }
2541 if {[info exists children($id)]} {
2544 if {[info exists children($id)]} {
2542 foreach c $children($id) {
2545 foreach c $children($id) {
2543 append comment "Child: [commit_descriptor $c]\n"
2546 append comment "Child: [commit_descriptor $c]\n"
2544 }
2547 }
2545 }
2548 }
2546
2549
2547 if {[lindex $info 9] eq "secret"} {
2550 if {[lindex $info 9] eq "secret"} {
2548 # for now, display phase for secret changesets only
2551 # for now, display phase for secret changesets only
2549 append comment "Phase: [lindex $info 9]\n"
2552 append comment "Phase: [lindex $info 9]\n"
2550 }
2553 }
2551
2554
2552 append comment "\n"
2555 append comment "\n"
2553 append comment [lindex $info 5]
2556 append comment [lindex $info 5]
2554
2557
2555 # make anything that looks like a SHA1 ID be a clickable link
2558 # make anything that looks like a SHA1 ID be a clickable link
2556 appendwithlinks $comment
2559 appendwithlinks $comment
2557
2560
2558 $ctext tag delete Comments
2561 $ctext tag delete Comments
2559 $ctext tag remove found 1.0 end
2562 $ctext tag remove found 1.0 end
2560 $ctext conf -state disabled
2563 $ctext conf -state disabled
2561 set commentend [$ctext index "end - 1c"]
2564 set commentend [$ctext index "end - 1c"]
2562
2565
2563 $cflist delete 0 end
2566 $cflist delete 0 end
2564 $cflist insert end "Comments"
2567 $cflist insert end "Comments"
2565 if {$nparents($id) <= 1} {
2568 if {$nparents($id) <= 1} {
2566 set parent "null"
2569 set parent "null"
2567 if {$nparents($id) == 1} {
2570 if {$nparents($id) == 1} {
2568 set parent $parents($id)
2571 set parent $parents($id)
2569 }
2572 }
2570 startdiff [concat $id $parent]
2573 startdiff [concat $id $parent]
2571 } elseif {$nparents($id) > 1} {
2574 } elseif {$nparents($id) > 1} {
2572 mergediff $id
2575 mergediff $id
2573 }
2576 }
2574 }
2577 }
2575
2578
2576 proc selnextline {dir} {
2579 proc selnextline {dir} {
2577 global selectedline
2580 global selectedline
2578 focus .
2581 focus .
2579 if {![info exists selectedline]} return
2582 if {![info exists selectedline]} return
2580 set l [expr $selectedline + $dir]
2583 set l [expr $selectedline + $dir]
2581 unmarkmatches
2584 unmarkmatches
2582 selectline $l 1
2585 selectline $l 1
2583 }
2586 }
2584
2587
2585 proc unselectline {} {
2588 proc unselectline {} {
2586 global selectedline
2589 global selectedline
2587
2590
2588 catch {unset selectedline}
2591 catch {unset selectedline}
2589 allcanvs delete secsel
2592 allcanvs delete secsel
2590 }
2593 }
2591
2594
2592 proc addtohistory {cmd} {
2595 proc addtohistory {cmd} {
2593 global history historyindex
2596 global history historyindex
2594
2597
2595 if {$historyindex > 0
2598 if {$historyindex > 0
2596 && [lindex $history [expr {$historyindex - 1}]] == $cmd} {
2599 && [lindex $history [expr {$historyindex - 1}]] == $cmd} {
2597 return
2600 return
2598 }
2601 }
2599
2602
2600 if {$historyindex < [llength $history]} {
2603 if {$historyindex < [llength $history]} {
2601 set history [lreplace $history $historyindex end $cmd]
2604 set history [lreplace $history $historyindex end $cmd]
2602 } else {
2605 } else {
2603 lappend history $cmd
2606 lappend history $cmd
2604 }
2607 }
2605 incr historyindex
2608 incr historyindex
2606 if {$historyindex > 1} {
2609 if {$historyindex > 1} {
2607 .ctop.top.bar.leftbut conf -state normal
2610 .ctop.top.bar.leftbut conf -state normal
2608 } else {
2611 } else {
2609 .ctop.top.bar.leftbut conf -state disabled
2612 .ctop.top.bar.leftbut conf -state disabled
2610 }
2613 }
2611 .ctop.top.bar.rightbut conf -state disabled
2614 .ctop.top.bar.rightbut conf -state disabled
2612 }
2615 }
2613
2616
2614 proc goback {} {
2617 proc goback {} {
2615 global history historyindex
2618 global history historyindex
2616 focus .
2619 focus .
2617
2620
2618 if {$historyindex > 1} {
2621 if {$historyindex > 1} {
2619 incr historyindex -1
2622 incr historyindex -1
2620 set cmd [lindex $history [expr {$historyindex - 1}]]
2623 set cmd [lindex $history [expr {$historyindex - 1}]]
2621 eval $cmd
2624 eval $cmd
2622 .ctop.top.bar.rightbut conf -state normal
2625 .ctop.top.bar.rightbut conf -state normal
2623 }
2626 }
2624 if {$historyindex <= 1} {
2627 if {$historyindex <= 1} {
2625 .ctop.top.bar.leftbut conf -state disabled
2628 .ctop.top.bar.leftbut conf -state disabled
2626 }
2629 }
2627 }
2630 }
2628
2631
2629 proc goforw {} {
2632 proc goforw {} {
2630 global history historyindex
2633 global history historyindex
2631 focus .
2634 focus .
2632
2635
2633 if {$historyindex < [llength $history]} {
2636 if {$historyindex < [llength $history]} {
2634 set cmd [lindex $history $historyindex]
2637 set cmd [lindex $history $historyindex]
2635 incr historyindex
2638 incr historyindex
2636 eval $cmd
2639 eval $cmd
2637 .ctop.top.bar.leftbut conf -state normal
2640 .ctop.top.bar.leftbut conf -state normal
2638 }
2641 }
2639 if {$historyindex >= [llength $history]} {
2642 if {$historyindex >= [llength $history]} {
2640 .ctop.top.bar.rightbut conf -state disabled
2643 .ctop.top.bar.rightbut conf -state disabled
2641 }
2644 }
2642 }
2645 }
2643
2646
2644 proc mergediff {id} {
2647 proc mergediff {id} {
2645 global parents diffmergeid diffmergegca mergefilelist diffpindex
2648 global parents diffmergeid diffmergegca mergefilelist diffpindex
2646
2649
2647 set diffmergeid $id
2650 set diffmergeid $id
2648 set diffpindex -1
2651 set diffpindex -1
2649 set diffmergegca [findgca $parents($id)]
2652 set diffmergegca [findgca $parents($id)]
2650 if {[info exists mergefilelist($id)]} {
2653 if {[info exists mergefilelist($id)]} {
2651 if {$mergefilelist($id) ne {}} {
2654 if {$mergefilelist($id) ne {}} {
2652 showmergediff
2655 showmergediff
2653 }
2656 }
2654 } else {
2657 } else {
2655 contmergediff {}
2658 contmergediff {}
2656 }
2659 }
2657 }
2660 }
2658
2661
2659 proc findgca {ids} {
2662 proc findgca {ids} {
2660 global env
2663 global env
2661 set gca {}
2664 set gca {}
2662 foreach id $ids {
2665 foreach id $ids {
2663 if {$gca eq {}} {
2666 if {$gca eq {}} {
2664 set gca $id
2667 set gca $id
2665 } else {
2668 } else {
2666 if {[catch {
2669 if {[catch {
2667 set gca [exec $env(HG) --config ui.report_untrusted=false debug-merge-base $gca $id]
2670 set gca [exec $env(HG) --config ui.report_untrusted=false debug-merge-base $gca $id]
2668 } err]} {
2671 } err]} {
2669 return {}
2672 return {}
2670 }
2673 }
2671 }
2674 }
2672 }
2675 }
2673 return $gca
2676 return $gca
2674 }
2677 }
2675
2678
2676 proc contmergediff {ids} {
2679 proc contmergediff {ids} {
2677 global diffmergeid diffpindex parents nparents diffmergegca
2680 global diffmergeid diffpindex parents nparents diffmergegca
2678 global treediffs mergefilelist diffids treepending
2681 global treediffs mergefilelist diffids treepending
2679
2682
2680 # diff the child against each of the parents, and diff
2683 # diff the child against each of the parents, and diff
2681 # each of the parents against the GCA.
2684 # each of the parents against the GCA.
2682 while 1 {
2685 while 1 {
2683 if {[lindex $ids 0] == $diffmergeid && $diffmergegca ne {}} {
2686 if {[lindex $ids 0] == $diffmergeid && $diffmergegca ne {}} {
2684 set ids [list [lindex $ids 1] $diffmergegca]
2687 set ids [list [lindex $ids 1] $diffmergegca]
2685 } else {
2688 } else {
2686 if {[incr diffpindex] >= $nparents($diffmergeid)} break
2689 if {[incr diffpindex] >= $nparents($diffmergeid)} break
2687 set p [lindex $parents($diffmergeid) $diffpindex]
2690 set p [lindex $parents($diffmergeid) $diffpindex]
2688 set ids [list $diffmergeid $p]
2691 set ids [list $diffmergeid $p]
2689 }
2692 }
2690 if {![info exists treediffs($ids)]} {
2693 if {![info exists treediffs($ids)]} {
2691 set diffids $ids
2694 set diffids $ids
2692 if {![info exists treepending]} {
2695 if {![info exists treepending]} {
2693 gettreediffs $ids
2696 gettreediffs $ids
2694 }
2697 }
2695 return
2698 return
2696 }
2699 }
2697 }
2700 }
2698
2701
2699 # If a file in some parent is different from the child and also
2702 # If a file in some parent is different from the child and also
2700 # different from the GCA, then it's interesting.
2703 # different from the GCA, then it's interesting.
2701 # If we don't have a GCA, then a file is interesting if it is
2704 # If we don't have a GCA, then a file is interesting if it is
2702 # different from the child in all the parents.
2705 # different from the child in all the parents.
2703 if {$diffmergegca ne {}} {
2706 if {$diffmergegca ne {}} {
2704 set files {}
2707 set files {}
2705 foreach p $parents($diffmergeid) {
2708 foreach p $parents($diffmergeid) {
2706 set gcadiffs $treediffs([list $p $diffmergegca])
2709 set gcadiffs $treediffs([list $p $diffmergegca])
2707 foreach f $treediffs([list $diffmergeid $p]) {
2710 foreach f $treediffs([list $diffmergeid $p]) {
2708 if {[lsearch -exact $files $f] < 0
2711 if {[lsearch -exact $files $f] < 0
2709 && [lsearch -exact $gcadiffs $f] >= 0} {
2712 && [lsearch -exact $gcadiffs $f] >= 0} {
2710 lappend files $f
2713 lappend files $f
2711 }
2714 }
2712 }
2715 }
2713 }
2716 }
2714 set files [lsort $files]
2717 set files [lsort $files]
2715 } else {
2718 } else {
2716 set p [lindex $parents($diffmergeid) 0]
2719 set p [lindex $parents($diffmergeid) 0]
2717 set files $treediffs([list $diffmergeid $p])
2720 set files $treediffs([list $diffmergeid $p])
2718 for {set i 1} {$i < $nparents($diffmergeid) && $files ne {}} {incr i} {
2721 for {set i 1} {$i < $nparents($diffmergeid) && $files ne {}} {incr i} {
2719 set p [lindex $parents($diffmergeid) $i]
2722 set p [lindex $parents($diffmergeid) $i]
2720 set df $treediffs([list $diffmergeid $p])
2723 set df $treediffs([list $diffmergeid $p])
2721 set nf {}
2724 set nf {}
2722 foreach f $files {
2725 foreach f $files {
2723 if {[lsearch -exact $df $f] >= 0} {
2726 if {[lsearch -exact $df $f] >= 0} {
2724 lappend nf $f
2727 lappend nf $f
2725 }
2728 }
2726 }
2729 }
2727 set files $nf
2730 set files $nf
2728 }
2731 }
2729 }
2732 }
2730
2733
2731 set mergefilelist($diffmergeid) $files
2734 set mergefilelist($diffmergeid) $files
2732 if {$files ne {}} {
2735 if {$files ne {}} {
2733 showmergediff
2736 showmergediff
2734 }
2737 }
2735 }
2738 }
2736
2739
2737 proc showmergediff {} {
2740 proc showmergediff {} {
2738 global cflist diffmergeid mergefilelist parents
2741 global cflist diffmergeid mergefilelist parents
2739 global diffopts diffinhunk currentfile currenthunk filelines
2742 global diffopts diffinhunk currentfile currenthunk filelines
2740 global diffblocked groupfilelast mergefds groupfilenum grouphunks
2743 global diffblocked groupfilelast mergefds groupfilenum grouphunks
2741 global env
2744 global env
2742
2745
2743 set files $mergefilelist($diffmergeid)
2746 set files $mergefilelist($diffmergeid)
2744 foreach f $files {
2747 foreach f $files {
2745 $cflist insert end $f
2748 $cflist insert end $f
2746 }
2749 }
2747 set env(GIT_DIFF_OPTS) $diffopts
2750 set env(GIT_DIFF_OPTS) $diffopts
2748 set flist {}
2751 set flist {}
2749 catch {unset currentfile}
2752 catch {unset currentfile}
2750 catch {unset currenthunk}
2753 catch {unset currenthunk}
2751 catch {unset filelines}
2754 catch {unset filelines}
2752 catch {unset groupfilenum}
2755 catch {unset groupfilenum}
2753 catch {unset grouphunks}
2756 catch {unset grouphunks}
2754 set groupfilelast -1
2757 set groupfilelast -1
2755 foreach p $parents($diffmergeid) {
2758 foreach p $parents($diffmergeid) {
2756 set cmd [list | $env(HG) --config ui.report_untrusted=false debug-diff-tree -p $p $diffmergeid]
2759 set cmd [list | $env(HG) --config ui.report_untrusted=false debug-diff-tree -p $p $diffmergeid]
2757 set cmd [concat $cmd $mergefilelist($diffmergeid)]
2760 set cmd [concat $cmd $mergefilelist($diffmergeid)]
2758 if {[catch {set f [open $cmd r]} err]} {
2761 if {[catch {set f [open $cmd r]} err]} {
2759 error_popup "Error getting diffs: $err"
2762 error_popup "Error getting diffs: $err"
2760 foreach f $flist {
2763 foreach f $flist {
2761 catch {close $f}
2764 catch {close $f}
2762 }
2765 }
2763 return
2766 return
2764 }
2767 }
2765 lappend flist $f
2768 lappend flist $f
2766 set ids [list $diffmergeid $p]
2769 set ids [list $diffmergeid $p]
2767 set mergefds($ids) $f
2770 set mergefds($ids) $f
2768 set diffinhunk($ids) 0
2771 set diffinhunk($ids) 0
2769 set diffblocked($ids) 0
2772 set diffblocked($ids) 0
2770 fconfigure $f -blocking 0
2773 fconfigure $f -blocking 0
2771 fileevent $f readable [list getmergediffline $f $ids $diffmergeid]
2774 fileevent $f readable [list getmergediffline $f $ids $diffmergeid]
2772 }
2775 }
2773 }
2776 }
2774
2777
2775 proc getmergediffline {f ids id} {
2778 proc getmergediffline {f ids id} {
2776 global diffmergeid diffinhunk diffoldlines diffnewlines
2779 global diffmergeid diffinhunk diffoldlines diffnewlines
2777 global currentfile currenthunk
2780 global currentfile currenthunk
2778 global diffoldstart diffnewstart diffoldlno diffnewlno
2781 global diffoldstart diffnewstart diffoldlno diffnewlno
2779 global diffblocked mergefilelist
2782 global diffblocked mergefilelist
2780 global noldlines nnewlines difflcounts filelines
2783 global noldlines nnewlines difflcounts filelines
2781
2784
2782 set n [gets $f line]
2785 set n [gets $f line]
2783 if {$n < 0} {
2786 if {$n < 0} {
2784 if {![eof $f]} return
2787 if {![eof $f]} return
2785 }
2788 }
2786
2789
2787 if {!([info exists diffmergeid] && $diffmergeid == $id)} {
2790 if {!([info exists diffmergeid] && $diffmergeid == $id)} {
2788 if {$n < 0} {
2791 if {$n < 0} {
2789 close $f
2792 close $f
2790 }
2793 }
2791 return
2794 return
2792 }
2795 }
2793
2796
2794 if {$diffinhunk($ids) != 0} {
2797 if {$diffinhunk($ids) != 0} {
2795 set fi $currentfile($ids)
2798 set fi $currentfile($ids)
2796 if {$n > 0 && [regexp {^[-+ \\]} $line match]} {
2799 if {$n > 0 && [regexp {^[-+ \\]} $line match]} {
2797 # continuing an existing hunk
2800 # continuing an existing hunk
2798 set line [string range $line 1 end]
2801 set line [string range $line 1 end]
2799 set p [lindex $ids 1]
2802 set p [lindex $ids 1]
2800 if {$match eq "-" || $match eq " "} {
2803 if {$match eq "-" || $match eq " "} {
2801 set filelines($p,$fi,$diffoldlno($ids)) $line
2804 set filelines($p,$fi,$diffoldlno($ids)) $line
2802 incr diffoldlno($ids)
2805 incr diffoldlno($ids)
2803 }
2806 }
2804 if {$match eq "+" || $match eq " "} {
2807 if {$match eq "+" || $match eq " "} {
2805 set filelines($id,$fi,$diffnewlno($ids)) $line
2808 set filelines($id,$fi,$diffnewlno($ids)) $line
2806 incr diffnewlno($ids)
2809 incr diffnewlno($ids)
2807 }
2810 }
2808 if {$match eq " "} {
2811 if {$match eq " "} {
2809 if {$diffinhunk($ids) == 2} {
2812 if {$diffinhunk($ids) == 2} {
2810 lappend difflcounts($ids) \
2813 lappend difflcounts($ids) \
2811 [list $noldlines($ids) $nnewlines($ids)]
2814 [list $noldlines($ids) $nnewlines($ids)]
2812 set noldlines($ids) 0
2815 set noldlines($ids) 0
2813 set diffinhunk($ids) 1
2816 set diffinhunk($ids) 1
2814 }
2817 }
2815 incr noldlines($ids)
2818 incr noldlines($ids)
2816 } elseif {$match eq "-" || $match eq "+"} {
2819 } elseif {$match eq "-" || $match eq "+"} {
2817 if {$diffinhunk($ids) == 1} {
2820 if {$diffinhunk($ids) == 1} {
2818 lappend difflcounts($ids) [list $noldlines($ids)]
2821 lappend difflcounts($ids) [list $noldlines($ids)]
2819 set noldlines($ids) 0
2822 set noldlines($ids) 0
2820 set nnewlines($ids) 0
2823 set nnewlines($ids) 0
2821 set diffinhunk($ids) 2
2824 set diffinhunk($ids) 2
2822 }
2825 }
2823 if {$match eq "-"} {
2826 if {$match eq "-"} {
2824 incr noldlines($ids)
2827 incr noldlines($ids)
2825 } else {
2828 } else {
2826 incr nnewlines($ids)
2829 incr nnewlines($ids)
2827 }
2830 }
2828 }
2831 }
2829 # and if it's \ No newline at end of line, then what?
2832 # and if it's \ No newline at end of line, then what?
2830 return
2833 return
2831 }
2834 }
2832 # end of a hunk
2835 # end of a hunk
2833 if {$diffinhunk($ids) == 1 && $noldlines($ids) != 0} {
2836 if {$diffinhunk($ids) == 1 && $noldlines($ids) != 0} {
2834 lappend difflcounts($ids) [list $noldlines($ids)]
2837 lappend difflcounts($ids) [list $noldlines($ids)]
2835 } elseif {$diffinhunk($ids) == 2
2838 } elseif {$diffinhunk($ids) == 2
2836 && ($noldlines($ids) != 0 || $nnewlines($ids) != 0)} {
2839 && ($noldlines($ids) != 0 || $nnewlines($ids) != 0)} {
2837 lappend difflcounts($ids) [list $noldlines($ids) $nnewlines($ids)]
2840 lappend difflcounts($ids) [list $noldlines($ids) $nnewlines($ids)]
2838 }
2841 }
2839 set currenthunk($ids) [list $currentfile($ids) \
2842 set currenthunk($ids) [list $currentfile($ids) \
2840 $diffoldstart($ids) $diffnewstart($ids) \
2843 $diffoldstart($ids) $diffnewstart($ids) \
2841 $diffoldlno($ids) $diffnewlno($ids) \
2844 $diffoldlno($ids) $diffnewlno($ids) \
2842 $difflcounts($ids)]
2845 $difflcounts($ids)]
2843 set diffinhunk($ids) 0
2846 set diffinhunk($ids) 0
2844 # -1 = need to block, 0 = unblocked, 1 = is blocked
2847 # -1 = need to block, 0 = unblocked, 1 = is blocked
2845 set diffblocked($ids) -1
2848 set diffblocked($ids) -1
2846 processhunks
2849 processhunks
2847 if {$diffblocked($ids) == -1} {
2850 if {$diffblocked($ids) == -1} {
2848 fileevent $f readable {}
2851 fileevent $f readable {}
2849 set diffblocked($ids) 1
2852 set diffblocked($ids) 1
2850 }
2853 }
2851 }
2854 }
2852
2855
2853 if {$n < 0} {
2856 if {$n < 0} {
2854 # eof
2857 # eof
2855 if {!$diffblocked($ids)} {
2858 if {!$diffblocked($ids)} {
2856 close $f
2859 close $f
2857 set currentfile($ids) [llength $mergefilelist($diffmergeid)]
2860 set currentfile($ids) [llength $mergefilelist($diffmergeid)]
2858 set currenthunk($ids) [list $currentfile($ids) 0 0 0 0 {}]
2861 set currenthunk($ids) [list $currentfile($ids) 0 0 0 0 {}]
2859 processhunks
2862 processhunks
2860 }
2863 }
2861 } elseif {[regexp {^diff --git a/(.*) b/} $line match fname]} {
2864 } elseif {[regexp {^diff --git a/(.*) b/} $line match fname]} {
2862 # start of a new file
2865 # start of a new file
2863 set currentfile($ids) \
2866 set currentfile($ids) \
2864 [lsearch -exact $mergefilelist($diffmergeid) $fname]
2867 [lsearch -exact $mergefilelist($diffmergeid) $fname]
2865 } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \
2868 } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \
2866 $line match f1l f1c f2l f2c rest]} {
2869 $line match f1l f1c f2l f2c rest]} {
2867 if {[info exists currentfile($ids)] && $currentfile($ids) >= 0} {
2870 if {[info exists currentfile($ids)] && $currentfile($ids) >= 0} {
2868 # start of a new hunk
2871 # start of a new hunk
2869 if {$f1l == 0 && $f1c == 0} {
2872 if {$f1l == 0 && $f1c == 0} {
2870 set f1l 1
2873 set f1l 1
2871 }
2874 }
2872 if {$f2l == 0 && $f2c == 0} {
2875 if {$f2l == 0 && $f2c == 0} {
2873 set f2l 1
2876 set f2l 1
2874 }
2877 }
2875 set diffinhunk($ids) 1
2878 set diffinhunk($ids) 1
2876 set diffoldstart($ids) $f1l
2879 set diffoldstart($ids) $f1l
2877 set diffnewstart($ids) $f2l
2880 set diffnewstart($ids) $f2l
2878 set diffoldlno($ids) $f1l
2881 set diffoldlno($ids) $f1l
2879 set diffnewlno($ids) $f2l
2882 set diffnewlno($ids) $f2l
2880 set difflcounts($ids) {}
2883 set difflcounts($ids) {}
2881 set noldlines($ids) 0
2884 set noldlines($ids) 0
2882 set nnewlines($ids) 0
2885 set nnewlines($ids) 0
2883 }
2886 }
2884 }
2887 }
2885 }
2888 }
2886
2889
2887 proc processhunks {} {
2890 proc processhunks {} {
2888 global diffmergeid parents nparents currenthunk
2891 global diffmergeid parents nparents currenthunk
2889 global mergefilelist diffblocked mergefds
2892 global mergefilelist diffblocked mergefds
2890 global grouphunks grouplinestart grouplineend groupfilenum
2893 global grouphunks grouplinestart grouplineend groupfilenum
2891
2894
2892 set nfiles [llength $mergefilelist($diffmergeid)]
2895 set nfiles [llength $mergefilelist($diffmergeid)]
2893 while 1 {
2896 while 1 {
2894 set fi $nfiles
2897 set fi $nfiles
2895 set lno 0
2898 set lno 0
2896 # look for the earliest hunk
2899 # look for the earliest hunk
2897 foreach p $parents($diffmergeid) {
2900 foreach p $parents($diffmergeid) {
2898 set ids [list $diffmergeid $p]
2901 set ids [list $diffmergeid $p]
2899 if {![info exists currenthunk($ids)]} return
2902 if {![info exists currenthunk($ids)]} return
2900 set i [lindex $currenthunk($ids) 0]
2903 set i [lindex $currenthunk($ids) 0]
2901 set l [lindex $currenthunk($ids) 2]
2904 set l [lindex $currenthunk($ids) 2]
2902 if {$i < $fi || ($i == $fi && $l < $lno)} {
2905 if {$i < $fi || ($i == $fi && $l < $lno)} {
2903 set fi $i
2906 set fi $i
2904 set lno $l
2907 set lno $l
2905 set pi $p
2908 set pi $p
2906 }
2909 }
2907 }
2910 }
2908
2911
2909 if {$fi < $nfiles} {
2912 if {$fi < $nfiles} {
2910 set ids [list $diffmergeid $pi]
2913 set ids [list $diffmergeid $pi]
2911 set hunk $currenthunk($ids)
2914 set hunk $currenthunk($ids)
2912 unset currenthunk($ids)
2915 unset currenthunk($ids)
2913 if {$diffblocked($ids) > 0} {
2916 if {$diffblocked($ids) > 0} {
2914 fileevent $mergefds($ids) readable \
2917 fileevent $mergefds($ids) readable \
2915 [list getmergediffline $mergefds($ids) $ids $diffmergeid]
2918 [list getmergediffline $mergefds($ids) $ids $diffmergeid]
2916 }
2919 }
2917 set diffblocked($ids) 0
2920 set diffblocked($ids) 0
2918
2921
2919 if {[info exists groupfilenum] && $groupfilenum == $fi
2922 if {[info exists groupfilenum] && $groupfilenum == $fi
2920 && $lno <= $grouplineend} {
2923 && $lno <= $grouplineend} {
2921 # add this hunk to the pending group
2924 # add this hunk to the pending group
2922 lappend grouphunks($pi) $hunk
2925 lappend grouphunks($pi) $hunk
2923 set endln [lindex $hunk 4]
2926 set endln [lindex $hunk 4]
2924 if {$endln > $grouplineend} {
2927 if {$endln > $grouplineend} {
2925 set grouplineend $endln
2928 set grouplineend $endln
2926 }
2929 }
2927 continue
2930 continue
2928 }
2931 }
2929 }
2932 }
2930
2933
2931 # succeeding stuff doesn't belong in this group, so
2934 # succeeding stuff doesn't belong in this group, so
2932 # process the group now
2935 # process the group now
2933 if {[info exists groupfilenum]} {
2936 if {[info exists groupfilenum]} {
2934 processgroup
2937 processgroup
2935 unset groupfilenum
2938 unset groupfilenum
2936 unset grouphunks
2939 unset grouphunks
2937 }
2940 }
2938
2941
2939 if {$fi >= $nfiles} break
2942 if {$fi >= $nfiles} break
2940
2943
2941 # start a new group
2944 # start a new group
2942 set groupfilenum $fi
2945 set groupfilenum $fi
2943 set grouphunks($pi) [list $hunk]
2946 set grouphunks($pi) [list $hunk]
2944 set grouplinestart $lno
2947 set grouplinestart $lno
2945 set grouplineend [lindex $hunk 4]
2948 set grouplineend [lindex $hunk 4]
2946 }
2949 }
2947 }
2950 }
2948
2951
2949 proc processgroup {} {
2952 proc processgroup {} {
2950 global groupfilelast groupfilenum difffilestart
2953 global groupfilelast groupfilenum difffilestart
2951 global mergefilelist diffmergeid ctext filelines
2954 global mergefilelist diffmergeid ctext filelines
2952 global parents diffmergeid diffoffset
2955 global parents diffmergeid diffoffset
2953 global grouphunks grouplinestart grouplineend nparents
2956 global grouphunks grouplinestart grouplineend nparents
2954 global mergemax
2957 global mergemax
2955
2958
2956 $ctext conf -state normal
2959 $ctext conf -state normal
2957 set id $diffmergeid
2960 set id $diffmergeid
2958 set f $groupfilenum
2961 set f $groupfilenum
2959 if {$groupfilelast != $f} {
2962 if {$groupfilelast != $f} {
2960 $ctext insert end "\n"
2963 $ctext insert end "\n"
2961 set here [$ctext index "end - 1c"]
2964 set here [$ctext index "end - 1c"]
2962 set difffilestart($f) $here
2965 set difffilestart($f) $here
2963 set mark fmark.[expr {$f + 1}]
2966 set mark fmark.[expr {$f + 1}]
2964 $ctext mark set $mark $here
2967 $ctext mark set $mark $here
2965 $ctext mark gravity $mark left
2968 $ctext mark gravity $mark left
2966 set header [lindex $mergefilelist($id) $f]
2969 set header [lindex $mergefilelist($id) $f]
2967 set l [expr {(78 - [string length $header]) / 2}]
2970 set l [expr {(78 - [string length $header]) / 2}]
2968 set pad [string range "----------------------------------------" 1 $l]
2971 set pad [string range "----------------------------------------" 1 $l]
2969 $ctext insert end "$pad $header $pad\n" filesep
2972 $ctext insert end "$pad $header $pad\n" filesep
2970 set groupfilelast $f
2973 set groupfilelast $f
2971 foreach p $parents($id) {
2974 foreach p $parents($id) {
2972 set diffoffset($p) 0
2975 set diffoffset($p) 0
2973 }
2976 }
2974 }
2977 }
2975
2978
2976 $ctext insert end "@@" msep
2979 $ctext insert end "@@" msep
2977 set nlines [expr {$grouplineend - $grouplinestart}]
2980 set nlines [expr {$grouplineend - $grouplinestart}]
2978 set events {}
2981 set events {}
2979 set pnum 0
2982 set pnum 0
2980 foreach p $parents($id) {
2983 foreach p $parents($id) {
2981 set startline [expr {$grouplinestart + $diffoffset($p)}]
2984 set startline [expr {$grouplinestart + $diffoffset($p)}]
2982 set ol $startline
2985 set ol $startline
2983 set nl $grouplinestart
2986 set nl $grouplinestart
2984 if {[info exists grouphunks($p)]} {
2987 if {[info exists grouphunks($p)]} {
2985 foreach h $grouphunks($p) {
2988 foreach h $grouphunks($p) {
2986 set l [lindex $h 2]
2989 set l [lindex $h 2]
2987 if {$nl < $l} {
2990 if {$nl < $l} {
2988 for {} {$nl < $l} {incr nl} {
2991 for {} {$nl < $l} {incr nl} {
2989 set filelines($p,$f,$ol) $filelines($id,$f,$nl)
2992 set filelines($p,$f,$ol) $filelines($id,$f,$nl)
2990 incr ol
2993 incr ol
2991 }
2994 }
2992 }
2995 }
2993 foreach chunk [lindex $h 5] {
2996 foreach chunk [lindex $h 5] {
2994 if {[llength $chunk] == 2} {
2997 if {[llength $chunk] == 2} {
2995 set olc [lindex $chunk 0]
2998 set olc [lindex $chunk 0]
2996 set nlc [lindex $chunk 1]
2999 set nlc [lindex $chunk 1]
2997 set nnl [expr {$nl + $nlc}]
3000 set nnl [expr {$nl + $nlc}]
2998 lappend events [list $nl $nnl $pnum $olc $nlc]
3001 lappend events [list $nl $nnl $pnum $olc $nlc]
2999 incr ol $olc
3002 incr ol $olc
3000 set nl $nnl
3003 set nl $nnl
3001 } else {
3004 } else {
3002 incr ol [lindex $chunk 0]
3005 incr ol [lindex $chunk 0]
3003 incr nl [lindex $chunk 0]
3006 incr nl [lindex $chunk 0]
3004 }
3007 }
3005 }
3008 }
3006 }
3009 }
3007 }
3010 }
3008 if {$nl < $grouplineend} {
3011 if {$nl < $grouplineend} {
3009 for {} {$nl < $grouplineend} {incr nl} {
3012 for {} {$nl < $grouplineend} {incr nl} {
3010 set filelines($p,$f,$ol) $filelines($id,$f,$nl)
3013 set filelines($p,$f,$ol) $filelines($id,$f,$nl)
3011 incr ol
3014 incr ol
3012 }
3015 }
3013 }
3016 }
3014 set nlines [expr {$ol - $startline}]
3017 set nlines [expr {$ol - $startline}]
3015 $ctext insert end " -$startline,$nlines" msep
3018 $ctext insert end " -$startline,$nlines" msep
3016 incr pnum
3019 incr pnum
3017 }
3020 }
3018
3021
3019 set nlines [expr {$grouplineend - $grouplinestart}]
3022 set nlines [expr {$grouplineend - $grouplinestart}]
3020 $ctext insert end " +$grouplinestart,$nlines @@\n" msep
3023 $ctext insert end " +$grouplinestart,$nlines @@\n" msep
3021
3024
3022 set events [lsort -integer -index 0 $events]
3025 set events [lsort -integer -index 0 $events]
3023 set nevents [llength $events]
3026 set nevents [llength $events]
3024 set nmerge $nparents($diffmergeid)
3027 set nmerge $nparents($diffmergeid)
3025 set l $grouplinestart
3028 set l $grouplinestart
3026 for {set i 0} {$i < $nevents} {set i $j} {
3029 for {set i 0} {$i < $nevents} {set i $j} {
3027 set nl [lindex $events $i 0]
3030 set nl [lindex $events $i 0]
3028 while {$l < $nl} {
3031 while {$l < $nl} {
3029 $ctext insert end " $filelines($id,$f,$l)\n"
3032 $ctext insert end " $filelines($id,$f,$l)\n"
3030 incr l
3033 incr l
3031 }
3034 }
3032 set e [lindex $events $i]
3035 set e [lindex $events $i]
3033 set enl [lindex $e 1]
3036 set enl [lindex $e 1]
3034 set j $i
3037 set j $i
3035 set active {}
3038 set active {}
3036 while 1 {
3039 while 1 {
3037 set pnum [lindex $e 2]
3040 set pnum [lindex $e 2]
3038 set olc [lindex $e 3]
3041 set olc [lindex $e 3]
3039 set nlc [lindex $e 4]
3042 set nlc [lindex $e 4]
3040 if {![info exists delta($pnum)]} {
3043 if {![info exists delta($pnum)]} {
3041 set delta($pnum) [expr {$olc - $nlc}]
3044 set delta($pnum) [expr {$olc - $nlc}]
3042 lappend active $pnum
3045 lappend active $pnum
3043 } else {
3046 } else {
3044 incr delta($pnum) [expr {$olc - $nlc}]
3047 incr delta($pnum) [expr {$olc - $nlc}]
3045 }
3048 }
3046 if {[incr j] >= $nevents} break
3049 if {[incr j] >= $nevents} break
3047 set e [lindex $events $j]
3050 set e [lindex $events $j]
3048 if {[lindex $e 0] >= $enl} break
3051 if {[lindex $e 0] >= $enl} break
3049 if {[lindex $e 1] > $enl} {
3052 if {[lindex $e 1] > $enl} {
3050 set enl [lindex $e 1]
3053 set enl [lindex $e 1]
3051 }
3054 }
3052 }
3055 }
3053 set nlc [expr {$enl - $l}]
3056 set nlc [expr {$enl - $l}]
3054 set ncol mresult
3057 set ncol mresult
3055 set bestpn -1
3058 set bestpn -1
3056 if {[llength $active] == $nmerge - 1} {
3059 if {[llength $active] == $nmerge - 1} {
3057 # no diff for one of the parents, i.e. it's identical
3060 # no diff for one of the parents, i.e. it's identical
3058 for {set pnum 0} {$pnum < $nmerge} {incr pnum} {
3061 for {set pnum 0} {$pnum < $nmerge} {incr pnum} {
3059 if {![info exists delta($pnum)]} {
3062 if {![info exists delta($pnum)]} {
3060 if {$pnum < $mergemax} {
3063 if {$pnum < $mergemax} {
3061 lappend ncol m$pnum
3064 lappend ncol m$pnum
3062 } else {
3065 } else {
3063 lappend ncol mmax
3066 lappend ncol mmax
3064 }
3067 }
3065 break
3068 break
3066 }
3069 }
3067 }
3070 }
3068 } elseif {[llength $active] == $nmerge} {
3071 } elseif {[llength $active] == $nmerge} {
3069 # all parents are different, see if one is very similar
3072 # all parents are different, see if one is very similar
3070 set bestsim 30
3073 set bestsim 30
3071 for {set pnum 0} {$pnum < $nmerge} {incr pnum} {
3074 for {set pnum 0} {$pnum < $nmerge} {incr pnum} {
3072 set sim [similarity $pnum $l $nlc $f \
3075 set sim [similarity $pnum $l $nlc $f \
3073 [lrange $events $i [expr {$j-1}]]]
3076 [lrange $events $i [expr {$j-1}]]]
3074 if {$sim > $bestsim} {
3077 if {$sim > $bestsim} {
3075 set bestsim $sim
3078 set bestsim $sim
3076 set bestpn $pnum
3079 set bestpn $pnum
3077 }
3080 }
3078 }
3081 }
3079 if {$bestpn >= 0} {
3082 if {$bestpn >= 0} {
3080 lappend ncol m$bestpn
3083 lappend ncol m$bestpn
3081 }
3084 }
3082 }
3085 }
3083 set pnum -1
3086 set pnum -1
3084 foreach p $parents($id) {
3087 foreach p $parents($id) {
3085 incr pnum
3088 incr pnum
3086 if {![info exists delta($pnum)] || $pnum == $bestpn} continue
3089 if {![info exists delta($pnum)] || $pnum == $bestpn} continue
3087 set olc [expr {$nlc + $delta($pnum)}]
3090 set olc [expr {$nlc + $delta($pnum)}]
3088 set ol [expr {$l + $diffoffset($p)}]
3091 set ol [expr {$l + $diffoffset($p)}]
3089 incr diffoffset($p) $delta($pnum)
3092 incr diffoffset($p) $delta($pnum)
3090 unset delta($pnum)
3093 unset delta($pnum)
3091 for {} {$olc > 0} {incr olc -1} {
3094 for {} {$olc > 0} {incr olc -1} {
3092 $ctext insert end "-$filelines($p,$f,$ol)\n" m$pnum
3095 $ctext insert end "-$filelines($p,$f,$ol)\n" m$pnum
3093 incr ol
3096 incr ol
3094 }
3097 }
3095 }
3098 }
3096 set endl [expr {$l + $nlc}]
3099 set endl [expr {$l + $nlc}]
3097 if {$bestpn >= 0} {
3100 if {$bestpn >= 0} {
3098 # show this pretty much as a normal diff
3101 # show this pretty much as a normal diff
3099 set p [lindex $parents($id) $bestpn]
3102 set p [lindex $parents($id) $bestpn]
3100 set ol [expr {$l + $diffoffset($p)}]
3103 set ol [expr {$l + $diffoffset($p)}]
3101 incr diffoffset($p) $delta($bestpn)
3104 incr diffoffset($p) $delta($bestpn)
3102 unset delta($bestpn)
3105 unset delta($bestpn)
3103 for {set k $i} {$k < $j} {incr k} {
3106 for {set k $i} {$k < $j} {incr k} {
3104 set e [lindex $events $k]
3107 set e [lindex $events $k]
3105 if {[lindex $e 2] != $bestpn} continue
3108 if {[lindex $e 2] != $bestpn} continue
3106 set nl [lindex $e 0]
3109 set nl [lindex $e 0]
3107 set ol [expr {$ol + $nl - $l}]
3110 set ol [expr {$ol + $nl - $l}]
3108 for {} {$l < $nl} {incr l} {
3111 for {} {$l < $nl} {incr l} {
3109 $ctext insert end "+$filelines($id,$f,$l)\n" $ncol
3112 $ctext insert end "+$filelines($id,$f,$l)\n" $ncol
3110 }
3113 }
3111 set c [lindex $e 3]
3114 set c [lindex $e 3]
3112 for {} {$c > 0} {incr c -1} {
3115 for {} {$c > 0} {incr c -1} {
3113 $ctext insert end "-$filelines($p,$f,$ol)\n" m$bestpn
3116 $ctext insert end "-$filelines($p,$f,$ol)\n" m$bestpn
3114 incr ol
3117 incr ol
3115 }
3118 }
3116 set nl [lindex $e 1]
3119 set nl [lindex $e 1]
3117 for {} {$l < $nl} {incr l} {
3120 for {} {$l < $nl} {incr l} {
3118 $ctext insert end "+$filelines($id,$f,$l)\n" mresult
3121 $ctext insert end "+$filelines($id,$f,$l)\n" mresult
3119 }
3122 }
3120 }
3123 }
3121 }
3124 }
3122 for {} {$l < $endl} {incr l} {
3125 for {} {$l < $endl} {incr l} {
3123 $ctext insert end "+$filelines($id,$f,$l)\n" $ncol
3126 $ctext insert end "+$filelines($id,$f,$l)\n" $ncol
3124 }
3127 }
3125 }
3128 }
3126 while {$l < $grouplineend} {
3129 while {$l < $grouplineend} {
3127 $ctext insert end " $filelines($id,$f,$l)\n"
3130 $ctext insert end " $filelines($id,$f,$l)\n"
3128 incr l
3131 incr l
3129 }
3132 }
3130 $ctext conf -state disabled
3133 $ctext conf -state disabled
3131 }
3134 }
3132
3135
3133 proc similarity {pnum l nlc f events} {
3136 proc similarity {pnum l nlc f events} {
3134 global diffmergeid parents diffoffset filelines
3137 global diffmergeid parents diffoffset filelines
3135
3138
3136 set id $diffmergeid
3139 set id $diffmergeid
3137 set p [lindex $parents($id) $pnum]
3140 set p [lindex $parents($id) $pnum]
3138 set ol [expr {$l + $diffoffset($p)}]
3141 set ol [expr {$l + $diffoffset($p)}]
3139 set endl [expr {$l + $nlc}]
3142 set endl [expr {$l + $nlc}]
3140 set same 0
3143 set same 0
3141 set diff 0
3144 set diff 0
3142 foreach e $events {
3145 foreach e $events {
3143 if {[lindex $e 2] != $pnum} continue
3146 if {[lindex $e 2] != $pnum} continue
3144 set nl [lindex $e 0]
3147 set nl [lindex $e 0]
3145 set ol [expr {$ol + $nl - $l}]
3148 set ol [expr {$ol + $nl - $l}]
3146 for {} {$l < $nl} {incr l} {
3149 for {} {$l < $nl} {incr l} {
3147 incr same [string length $filelines($id,$f,$l)]
3150 incr same [string length $filelines($id,$f,$l)]
3148 incr same
3151 incr same
3149 }
3152 }
3150 set oc [lindex $e 3]
3153 set oc [lindex $e 3]
3151 for {} {$oc > 0} {incr oc -1} {
3154 for {} {$oc > 0} {incr oc -1} {
3152 incr diff [string length $filelines($p,$f,$ol)]
3155 incr diff [string length $filelines($p,$f,$ol)]
3153 incr diff
3156 incr diff
3154 incr ol
3157 incr ol
3155 }
3158 }
3156 set nl [lindex $e 1]
3159 set nl [lindex $e 1]
3157 for {} {$l < $nl} {incr l} {
3160 for {} {$l < $nl} {incr l} {
3158 incr diff [string length $filelines($id,$f,$l)]
3161 incr diff [string length $filelines($id,$f,$l)]
3159 incr diff
3162 incr diff
3160 }
3163 }
3161 }
3164 }
3162 for {} {$l < $endl} {incr l} {
3165 for {} {$l < $endl} {incr l} {
3163 incr same [string length $filelines($id,$f,$l)]
3166 incr same [string length $filelines($id,$f,$l)]
3164 incr same
3167 incr same
3165 }
3168 }
3166 if {$same == 0} {
3169 if {$same == 0} {
3167 return 0
3170 return 0
3168 }
3171 }
3169 return [expr {200 * $same / (2 * $same + $diff)}]
3172 return [expr {200 * $same / (2 * $same + $diff)}]
3170 }
3173 }
3171
3174
3172 proc startdiff {ids} {
3175 proc startdiff {ids} {
3173 global treediffs diffids treepending diffmergeid
3176 global treediffs diffids treepending diffmergeid
3174
3177
3175 set diffids $ids
3178 set diffids $ids
3176 catch {unset diffmergeid}
3179 catch {unset diffmergeid}
3177 if {![info exists treediffs($ids)]} {
3180 if {![info exists treediffs($ids)]} {
3178 if {![info exists treepending]} {
3181 if {![info exists treepending]} {
3179 gettreediffs $ids
3182 gettreediffs $ids
3180 }
3183 }
3181 } else {
3184 } else {
3182 addtocflist $ids
3185 addtocflist $ids
3183 }
3186 }
3184 }
3187 }
3185
3188
3186 proc addtocflist {ids} {
3189 proc addtocflist {ids} {
3187 global treediffs cflist
3190 global treediffs cflist
3188 foreach f $treediffs($ids) {
3191 foreach f $treediffs($ids) {
3189 $cflist insert end $f
3192 $cflist insert end $f
3190 }
3193 }
3191 getblobdiffs $ids
3194 getblobdiffs $ids
3192 }
3195 }
3193
3196
3194 proc gettreediffs {ids} {
3197 proc gettreediffs {ids} {
3195 global treediff parents treepending env
3198 global treediff parents treepending env
3196 set treepending $ids
3199 set treepending $ids
3197 set treediff {}
3200 set treediff {}
3198 set id [lindex $ids 0]
3201 set id [lindex $ids 0]
3199 set p [lindex $ids 1]
3202 set p [lindex $ids 1]
3200 if [catch {set gdtf [open "|{$env(HG)} --config ui.report_untrusted=false debug-diff-tree -r $p $id" r]}] return
3203 if [catch {set gdtf [open "|{$env(HG)} --config ui.report_untrusted=false debug-diff-tree -r $p $id" r]}] return
3201 fconfigure $gdtf -blocking 0
3204 fconfigure $gdtf -blocking 0
3202 fileevent $gdtf readable [list gettreediffline $gdtf $ids]
3205 fileevent $gdtf readable [list gettreediffline $gdtf $ids]
3203 }
3206 }
3204
3207
3205 proc gettreediffline {gdtf ids} {
3208 proc gettreediffline {gdtf ids} {
3206 global treediff treediffs treepending diffids diffmergeid
3209 global treediff treediffs treepending diffids diffmergeid
3207
3210
3208 set n [gets $gdtf line]
3211 set n [gets $gdtf line]
3209 if {$n < 0} {
3212 if {$n < 0} {
3210 if {![eof $gdtf]} return
3213 if {![eof $gdtf]} return
3211 close $gdtf
3214 close $gdtf
3212 set treediffs($ids) $treediff
3215 set treediffs($ids) $treediff
3213 unset treepending
3216 unset treepending
3214 if {$ids != $diffids} {
3217 if {$ids != $diffids} {
3215 gettreediffs $diffids
3218 gettreediffs $diffids
3216 } else {
3219 } else {
3217 if {[info exists diffmergeid]} {
3220 if {[info exists diffmergeid]} {
3218 contmergediff $ids
3221 contmergediff $ids
3219 } else {
3222 } else {
3220 addtocflist $ids
3223 addtocflist $ids
3221 }
3224 }
3222 }
3225 }
3223 return
3226 return
3224 }
3227 }
3225 set tab1 [expr [string first "\t" $line] + 1]
3228 set tab1 [expr [string first "\t" $line] + 1]
3226 set tab2 [expr [string first "\t" $line $tab1] - 1]
3229 set tab2 [expr [string first "\t" $line $tab1] - 1]
3227 set file [string range $line $tab1 $tab2]
3230 set file [string range $line $tab1 $tab2]
3228 lappend treediff $file
3231 lappend treediff $file
3229 }
3232 }
3230
3233
3231 proc getblobdiffs {ids} {
3234 proc getblobdiffs {ids} {
3232 global diffopts blobdifffd diffids env curdifftag curtagstart
3235 global diffopts blobdifffd diffids env curdifftag curtagstart
3233 global difffilestart nextupdate diffinhdr treediffs
3236 global difffilestart nextupdate diffinhdr treediffs
3234
3237
3235 set id [lindex $ids 0]
3238 set id [lindex $ids 0]
3236 set p [lindex $ids 1]
3239 set p [lindex $ids 1]
3237 set env(GIT_DIFF_OPTS) $diffopts
3240 set env(GIT_DIFF_OPTS) $diffopts
3238 set cmd [list | $env(HG) --config ui.report_untrusted=false debug-diff-tree -r -p -C $p $id]
3241 set cmd [list | $env(HG) --config ui.report_untrusted=false debug-diff-tree -r -p -C $p $id]
3239 if {[catch {set bdf [open $cmd r]} err]} {
3242 if {[catch {set bdf [open $cmd r]} err]} {
3240 puts "error getting diffs: $err"
3243 puts "error getting diffs: $err"
3241 return
3244 return
3242 }
3245 }
3243 set diffinhdr 0
3246 set diffinhdr 0
3244 fconfigure $bdf -blocking 0
3247 fconfigure $bdf -blocking 0
3245 set blobdifffd($ids) $bdf
3248 set blobdifffd($ids) $bdf
3246 set curdifftag Comments
3249 set curdifftag Comments
3247 set curtagstart 0.0
3250 set curtagstart 0.0
3248 catch {unset difffilestart}
3251 catch {unset difffilestart}
3249 fileevent $bdf readable [list getblobdiffline $bdf $diffids]
3252 fileevent $bdf readable [list getblobdiffline $bdf $diffids]
3250 set nextupdate [expr {[clock clicks -milliseconds] + 100}]
3253 set nextupdate [expr {[clock clicks -milliseconds] + 100}]
3251 }
3254 }
3252
3255
3253 proc getblobdiffline {bdf ids} {
3256 proc getblobdiffline {bdf ids} {
3254 global diffids blobdifffd ctext curdifftag curtagstart
3257 global diffids blobdifffd ctext curdifftag curtagstart
3255 global diffnexthead diffnextnote difffilestart
3258 global diffnexthead diffnextnote difffilestart
3256 global nextupdate diffinhdr treediffs
3259 global nextupdate diffinhdr treediffs
3257 global gaudydiff
3260 global gaudydiff
3258
3261
3259 set n [gets $bdf line]
3262 set n [gets $bdf line]
3260 if {$n < 0} {
3263 if {$n < 0} {
3261 if {[eof $bdf]} {
3264 if {[eof $bdf]} {
3262 close $bdf
3265 close $bdf
3263 if {$ids == $diffids && $bdf == $blobdifffd($ids)} {
3266 if {$ids == $diffids && $bdf == $blobdifffd($ids)} {
3264 $ctext tag add $curdifftag $curtagstart end
3267 $ctext tag add $curdifftag $curtagstart end
3265 }
3268 }
3266 }
3269 }
3267 return
3270 return
3268 }
3271 }
3269 if {$ids != $diffids || $bdf != $blobdifffd($ids)} {
3272 if {$ids != $diffids || $bdf != $blobdifffd($ids)} {
3270 return
3273 return
3271 }
3274 }
3272 regsub -all "\r" $line "" line
3275 regsub -all "\r" $line "" line
3273 $ctext conf -state normal
3276 $ctext conf -state normal
3274 if {[regexp {^diff --git a/(.*) b/(.*)} $line match fname newname]} {
3277 if {[regexp {^diff --git a/(.*) b/(.*)} $line match fname newname]} {
3275 # start of a new file
3278 # start of a new file
3276 $ctext insert end "\n"
3279 $ctext insert end "\n"
3277 $ctext tag add $curdifftag $curtagstart end
3280 $ctext tag add $curdifftag $curtagstart end
3278 set curtagstart [$ctext index "end - 1c"]
3281 set curtagstart [$ctext index "end - 1c"]
3279 set header $newname
3282 set header $newname
3280 set here [$ctext index "end - 1c"]
3283 set here [$ctext index "end - 1c"]
3281 set i [lsearch -exact $treediffs($diffids) $fname]
3284 set i [lsearch -exact $treediffs($diffids) $fname]
3282 if {$i >= 0} {
3285 if {$i >= 0} {
3283 set difffilestart($i) $here
3286 set difffilestart($i) $here
3284 incr i
3287 incr i
3285 $ctext mark set fmark.$i $here
3288 $ctext mark set fmark.$i $here
3286 $ctext mark gravity fmark.$i left
3289 $ctext mark gravity fmark.$i left
3287 }
3290 }
3288 if {$newname != $fname} {
3291 if {$newname != $fname} {
3289 set i [lsearch -exact $treediffs($diffids) $newname]
3292 set i [lsearch -exact $treediffs($diffids) $newname]
3290 if {$i >= 0} {
3293 if {$i >= 0} {
3291 set difffilestart($i) $here
3294 set difffilestart($i) $here
3292 incr i
3295 incr i
3293 $ctext mark set fmark.$i $here
3296 $ctext mark set fmark.$i $here
3294 $ctext mark gravity fmark.$i left
3297 $ctext mark gravity fmark.$i left
3295 }
3298 }
3296 }
3299 }
3297 set curdifftag "f:$fname"
3300 set curdifftag "f:$fname"
3298 $ctext tag delete $curdifftag
3301 $ctext tag delete $curdifftag
3299 set l [expr {(78 - [string length $header]) / 2}]
3302 set l [expr {(78 - [string length $header]) / 2}]
3300 set pad [string range "----------------------------------------" 1 $l]
3303 set pad [string range "----------------------------------------" 1 $l]
3301 $ctext insert end "$pad $header $pad\n" filesep
3304 $ctext insert end "$pad $header $pad\n" filesep
3302 set diffinhdr 1
3305 set diffinhdr 1
3303 } elseif {[regexp {^(---|\+\+\+) } $line] && $diffinhdr} {
3306 } elseif {[regexp {^(---|\+\+\+) } $line] && $diffinhdr} {
3304 set diffinhdr 1
3307 set diffinhdr 1
3305 } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \
3308 } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \
3306 $line match f1l f1c f2l f2c rest]} {
3309 $line match f1l f1c f2l f2c rest]} {
3307 if {$gaudydiff} {
3310 if {$gaudydiff} {
3308 $ctext insert end "\t" hunksep
3311 $ctext insert end "\t" hunksep
3309 $ctext insert end " $f1l " d0 " $f2l " d1
3312 $ctext insert end " $f1l " d0 " $f2l " d1
3310 $ctext insert end " $rest \n" hunksep
3313 $ctext insert end " $rest \n" hunksep
3311 } else {
3314 } else {
3312 $ctext insert end "$line\n" hunksep
3315 $ctext insert end "$line\n" hunksep
3313 }
3316 }
3314 set diffinhdr 0
3317 set diffinhdr 0
3315 } else {
3318 } else {
3316 set x [string range $line 0 0]
3319 set x [string range $line 0 0]
3317 if {$x == "-" || $x == "+"} {
3320 if {$x == "-" || $x == "+"} {
3318 set tag [expr {$x == "+"}]
3321 set tag [expr {$x == "+"}]
3319 if {$gaudydiff} {
3322 if {$gaudydiff} {
3320 set line [string range $line 1 end]
3323 set line [string range $line 1 end]
3321 }
3324 }
3322 $ctext insert end "$line\n" d$tag
3325 $ctext insert end "$line\n" d$tag
3323 } elseif {$x == " "} {
3326 } elseif {$x == " "} {
3324 if {$gaudydiff} {
3327 if {$gaudydiff} {
3325 set line [string range $line 1 end]
3328 set line [string range $line 1 end]
3326 }
3329 }
3327 $ctext insert end "$line\n"
3330 $ctext insert end "$line\n"
3328 } elseif {$diffinhdr || $x == "\\"} {
3331 } elseif {$diffinhdr || $x == "\\"} {
3329 # e.g. "\ No newline at end of file"
3332 # e.g. "\ No newline at end of file"
3330 $ctext insert end "$line\n" filesep
3333 $ctext insert end "$line\n" filesep
3331 } elseif {$line != ""} {
3334 } elseif {$line != ""} {
3332 # Something else we don't recognize
3335 # Something else we don't recognize
3333 if {$curdifftag != "Comments"} {
3336 if {$curdifftag != "Comments"} {
3334 $ctext insert end "\n"
3337 $ctext insert end "\n"
3335 $ctext tag add $curdifftag $curtagstart end
3338 $ctext tag add $curdifftag $curtagstart end
3336 set curtagstart [$ctext index "end - 1c"]
3339 set curtagstart [$ctext index "end - 1c"]
3337 set curdifftag Comments
3340 set curdifftag Comments
3338 }
3341 }
3339 $ctext insert end "$line\n" filesep
3342 $ctext insert end "$line\n" filesep
3340 }
3343 }
3341 }
3344 }
3342 $ctext conf -state disabled
3345 $ctext conf -state disabled
3343 if {[clock clicks -milliseconds] >= $nextupdate} {
3346 if {[clock clicks -milliseconds] >= $nextupdate} {
3344 incr nextupdate 100
3347 incr nextupdate 100
3345 fileevent $bdf readable {}
3348 fileevent $bdf readable {}
3346 update
3349 update
3347 fileevent $bdf readable "getblobdiffline $bdf {$ids}"
3350 fileevent $bdf readable "getblobdiffline $bdf {$ids}"
3348 }
3351 }
3349 }
3352 }
3350
3353
3351 proc nextfile {} {
3354 proc nextfile {} {
3352 global difffilestart ctext
3355 global difffilestart ctext
3353 set here [$ctext index @0,0]
3356 set here [$ctext index @0,0]
3354 for {set i 0} {[info exists difffilestart($i)]} {incr i} {
3357 for {set i 0} {[info exists difffilestart($i)]} {incr i} {
3355 if {[$ctext compare $difffilestart($i) > $here]} {
3358 if {[$ctext compare $difffilestart($i) > $here]} {
3356 if {![info exists pos]
3359 if {![info exists pos]
3357 || [$ctext compare $difffilestart($i) < $pos]} {
3360 || [$ctext compare $difffilestart($i) < $pos]} {
3358 set pos $difffilestart($i)
3361 set pos $difffilestart($i)
3359 }
3362 }
3360 }
3363 }
3361 }
3364 }
3362 if {[info exists pos]} {
3365 if {[info exists pos]} {
3363 $ctext yview $pos
3366 $ctext yview $pos
3364 }
3367 }
3365 }
3368 }
3366
3369
3367 proc listboxsel {} {
3370 proc listboxsel {} {
3368 global ctext cflist currentid
3371 global ctext cflist currentid
3369 if {![info exists currentid]} return
3372 if {![info exists currentid]} return
3370 set sel [lsort [$cflist curselection]]
3373 set sel [lsort [$cflist curselection]]
3371 if {$sel eq {}} return
3374 if {$sel eq {}} return
3372 set first [lindex $sel 0]
3375 set first [lindex $sel 0]
3373 catch {$ctext yview fmark.$first}
3376 catch {$ctext yview fmark.$first}
3374 }
3377 }
3375
3378
3376 proc setcoords {} {
3379 proc setcoords {} {
3377 global linespc charspc canvx0 canvy0 mainfont
3380 global linespc charspc canvx0 canvy0 mainfont
3378 global xspc1 xspc2 lthickness
3381 global xspc1 xspc2 lthickness
3379
3382
3380 set linespc [font metrics $mainfont -linespace]
3383 set linespc [font metrics $mainfont -linespace]
3381 set charspc [font measure $mainfont "m"]
3384 set charspc [font measure $mainfont "m"]
3382 set canvy0 [expr 3 + 0.5 * $linespc]
3385 set canvy0 [expr 3 + 0.5 * $linespc]
3383 set canvx0 [expr 3 + 0.5 * $linespc]
3386 set canvx0 [expr 3 + 0.5 * $linespc]
3384 set lthickness [expr {int($linespc / 9) + 1}]
3387 set lthickness [expr {int($linespc / 9) + 1}]
3385 set xspc1(0) $linespc
3388 set xspc1(0) $linespc
3386 set xspc2 $linespc
3389 set xspc2 $linespc
3387 }
3390 }
3388
3391
3389 proc redisplay {} {
3392 proc redisplay {} {
3390 global stopped redisplaying phase
3393 global stopped redisplaying phase
3391 if {$stopped > 1} return
3394 if {$stopped > 1} return
3392 if {$phase == "getcommits"} return
3395 if {$phase == "getcommits"} return
3393 set redisplaying 1
3396 set redisplaying 1
3394 if {$phase == "drawgraph" || $phase == "incrdraw"} {
3397 if {$phase == "drawgraph" || $phase == "incrdraw"} {
3395 set stopped 1
3398 set stopped 1
3396 } else {
3399 } else {
3397 drawgraph
3400 drawgraph
3398 }
3401 }
3399 }
3402 }
3400
3403
3401 proc incrfont {inc} {
3404 proc incrfont {inc} {
3402 global mainfont namefont textfont ctext canv phase
3405 global mainfont namefont textfont ctext canv phase
3403 global stopped entries curidfont
3406 global stopped entries curidfont
3404 unmarkmatches
3407 unmarkmatches
3405 set mainfont [lreplace $mainfont 1 1 [expr {[lindex $mainfont 1] + $inc}]]
3408 set mainfont [lreplace $mainfont 1 1 [expr {[lindex $mainfont 1] + $inc}]]
3406 set curidfont [lreplace $curidfont 1 1 [expr {[lindex $curidfont 1] + $inc}]]
3409 set curidfont [lreplace $curidfont 1 1 [expr {[lindex $curidfont 1] + $inc}]]
3407 set namefont [lreplace $namefont 1 1 [expr {[lindex $namefont 1] + $inc}]]
3410 set namefont [lreplace $namefont 1 1 [expr {[lindex $namefont 1] + $inc}]]
3408 set textfont [lreplace $textfont 1 1 [expr {[lindex $textfont 1] + $inc}]]
3411 set textfont [lreplace $textfont 1 1 [expr {[lindex $textfont 1] + $inc}]]
3409 setcoords
3412 setcoords
3410 $ctext conf -font $textfont
3413 $ctext conf -font $textfont
3411 $ctext tag conf filesep -font [concat $textfont bold]
3414 $ctext tag conf filesep -font [concat $textfont bold]
3412 foreach e $entries {
3415 foreach e $entries {
3413 $e conf -font $mainfont
3416 $e conf -font $mainfont
3414 }
3417 }
3415 if {$phase == "getcommits"} {
3418 if {$phase == "getcommits"} {
3416 $canv itemconf textitems -font $mainfont
3419 $canv itemconf textitems -font $mainfont
3417 }
3420 }
3418 redisplay
3421 redisplay
3419 }
3422 }
3420
3423
3421 proc clearsha1 {} {
3424 proc clearsha1 {} {
3422 global sha1entry sha1string
3425 global sha1entry sha1string
3423 if {[string length $sha1string] == 40} {
3426 if {[string length $sha1string] == 40} {
3424 $sha1entry delete 0 end
3427 $sha1entry delete 0 end
3425 }
3428 }
3426 }
3429 }
3427
3430
3428 proc sha1change {n1 n2 op} {
3431 proc sha1change {n1 n2 op} {
3429 global sha1string currentid sha1but
3432 global sha1string currentid sha1but
3430 if {$sha1string == {}
3433 if {$sha1string == {}
3431 || ([info exists currentid] && $sha1string == $currentid)} {
3434 || ([info exists currentid] && $sha1string == $currentid)} {
3432 set state disabled
3435 set state disabled
3433 } else {
3436 } else {
3434 set state normal
3437 set state normal
3435 }
3438 }
3436 if {[$sha1but cget -state] == $state} return
3439 if {[$sha1but cget -state] == $state} return
3437 if {$state == "normal"} {
3440 if {$state == "normal"} {
3438 $sha1but conf -state normal -relief raised -text "Goto: "
3441 $sha1but conf -state normal -relief raised -text "Goto: "
3439 } else {
3442 } else {
3440 $sha1but conf -state disabled -relief flat -text "SHA1 ID: "
3443 $sha1but conf -state disabled -relief flat -text "SHA1 ID: "
3441 }
3444 }
3442 }
3445 }
3443
3446
3444 proc gotocommit {} {
3447 proc gotocommit {} {
3445 global sha1string currentid idline tagids
3448 global sha1string currentid idline tagids
3446 global lineid numcommits
3449 global lineid numcommits
3447
3450
3448 if {$sha1string == {}
3451 if {$sha1string == {}
3449 || ([info exists currentid] && $sha1string == $currentid)} return
3452 || ([info exists currentid] && $sha1string == $currentid)} return
3450 if {[info exists tagids($sha1string)]} {
3453 if {[info exists tagids($sha1string)]} {
3451 set id $tagids($sha1string)
3454 set id $tagids($sha1string)
3452 } else {
3455 } else {
3453 set id [string tolower $sha1string]
3456 set id [string tolower $sha1string]
3454 if {[regexp {^[0-9a-f]{4,39}$} $id]} {
3457 if {[regexp {^[0-9a-f]{4,39}$} $id]} {
3455 set matches {}
3458 set matches {}
3456 for {set l 0} {$l < $numcommits} {incr l} {
3459 for {set l 0} {$l < $numcommits} {incr l} {
3457 if {[string match $id* $lineid($l)]} {
3460 if {[string match $id* $lineid($l)]} {
3458 lappend matches $lineid($l)
3461 lappend matches $lineid($l)
3459 }
3462 }
3460 }
3463 }
3461 if {$matches ne {}} {
3464 if {$matches ne {}} {
3462 if {[llength $matches] > 1} {
3465 if {[llength $matches] > 1} {
3463 error_popup "Short SHA1 id $id is ambiguous"
3466 error_popup "Short SHA1 id $id is ambiguous"
3464 return
3467 return
3465 }
3468 }
3466 set id [lindex $matches 0]
3469 set id [lindex $matches 0]
3467 }
3470 }
3468 }
3471 }
3469 }
3472 }
3470 if {[info exists idline($id)]} {
3473 if {[info exists idline($id)]} {
3471 selectline $idline($id) 1
3474 selectline $idline($id) 1
3472 return
3475 return
3473 }
3476 }
3474 if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} {
3477 if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} {
3475 set type "SHA1 id"
3478 set type "SHA1 id"
3476 } else {
3479 } else {
3477 set type "Tag"
3480 set type "Tag"
3478 }
3481 }
3479 error_popup "$type $sha1string is not known"
3482 error_popup "$type $sha1string is not known"
3480 }
3483 }
3481
3484
3482 proc lineenter {x y id} {
3485 proc lineenter {x y id} {
3483 global hoverx hovery hoverid hovertimer
3486 global hoverx hovery hoverid hovertimer
3484 global commitinfo canv
3487 global commitinfo canv
3485
3488
3486 if {![info exists commitinfo($id)]} return
3489 if {![info exists commitinfo($id)]} return
3487 set hoverx $x
3490 set hoverx $x
3488 set hovery $y
3491 set hovery $y
3489 set hoverid $id
3492 set hoverid $id
3490 if {[info exists hovertimer]} {
3493 if {[info exists hovertimer]} {
3491 after cancel $hovertimer
3494 after cancel $hovertimer
3492 }
3495 }
3493 set hovertimer [after 500 linehover]
3496 set hovertimer [after 500 linehover]
3494 $canv delete hover
3497 $canv delete hover
3495 }
3498 }
3496
3499
3497 proc linemotion {x y id} {
3500 proc linemotion {x y id} {
3498 global hoverx hovery hoverid hovertimer
3501 global hoverx hovery hoverid hovertimer
3499
3502
3500 if {[info exists hoverid] && $id == $hoverid} {
3503 if {[info exists hoverid] && $id == $hoverid} {
3501 set hoverx $x
3504 set hoverx $x
3502 set hovery $y
3505 set hovery $y
3503 if {[info exists hovertimer]} {
3506 if {[info exists hovertimer]} {
3504 after cancel $hovertimer
3507 after cancel $hovertimer
3505 }
3508 }
3506 set hovertimer [after 500 linehover]
3509 set hovertimer [after 500 linehover]
3507 }
3510 }
3508 }
3511 }
3509
3512
3510 proc lineleave {id} {
3513 proc lineleave {id} {
3511 global hoverid hovertimer canv
3514 global hoverid hovertimer canv
3512
3515
3513 if {[info exists hoverid] && $id == $hoverid} {
3516 if {[info exists hoverid] && $id == $hoverid} {
3514 $canv delete hover
3517 $canv delete hover
3515 if {[info exists hovertimer]} {
3518 if {[info exists hovertimer]} {
3516 after cancel $hovertimer
3519 after cancel $hovertimer
3517 unset hovertimer
3520 unset hovertimer
3518 }
3521 }
3519 unset hoverid
3522 unset hoverid
3520 }
3523 }
3521 }
3524 }
3522
3525
3523 proc linehover {} {
3526 proc linehover {} {
3524 global hoverx hovery hoverid hovertimer
3527 global hoverx hovery hoverid hovertimer
3525 global canv linespc lthickness
3528 global canv linespc lthickness
3526 global commitinfo mainfont
3529 global commitinfo mainfont
3527
3530
3528 set text [lindex $commitinfo($hoverid) 0]
3531 set text [lindex $commitinfo($hoverid) 0]
3529 set ymax [lindex [$canv cget -scrollregion] 3]
3532 set ymax [lindex [$canv cget -scrollregion] 3]
3530 if {$ymax == {}} return
3533 if {$ymax == {}} return
3531 set yfrac [lindex [$canv yview] 0]
3534 set yfrac [lindex [$canv yview] 0]
3532 set x [expr {$hoverx + 2 * $linespc}]
3535 set x [expr {$hoverx + 2 * $linespc}]
3533 set y [expr {$hovery + $yfrac * $ymax - $linespc / 2}]
3536 set y [expr {$hovery + $yfrac * $ymax - $linespc / 2}]
3534 set x0 [expr {$x - 2 * $lthickness}]
3537 set x0 [expr {$x - 2 * $lthickness}]
3535 set y0 [expr {$y - 2 * $lthickness}]
3538 set y0 [expr {$y - 2 * $lthickness}]
3536 set x1 [expr {$x + [font measure $mainfont $text] + 2 * $lthickness}]
3539 set x1 [expr {$x + [font measure $mainfont $text] + 2 * $lthickness}]
3537 set y1 [expr {$y + $linespc + 2 * $lthickness}]
3540 set y1 [expr {$y + $linespc + 2 * $lthickness}]
3538 set t [$canv create rectangle $x0 $y0 $x1 $y1 \
3541 set t [$canv create rectangle $x0 $y0 $x1 $y1 \
3539 -fill \#ffff80 -outline black -width 1 -tags hover]
3542 -fill \#ffff80 -outline black -width 1 -tags hover]
3540 $canv raise $t
3543 $canv raise $t
3541 set t [$canv create text $x $y -anchor nw -text $text -tags hover]
3544 set t [$canv create text $x $y -anchor nw -text $text -tags hover]
3542 $canv raise $t
3545 $canv raise $t
3543 }
3546 }
3544
3547
3545 proc clickisonarrow {id y} {
3548 proc clickisonarrow {id y} {
3546 global mainline mainlinearrow sidelines lthickness
3549 global mainline mainlinearrow sidelines lthickness
3547
3550
3548 set thresh [expr {2 * $lthickness + 6}]
3551 set thresh [expr {2 * $lthickness + 6}]
3549 if {[info exists mainline($id)]} {
3552 if {[info exists mainline($id)]} {
3550 if {$mainlinearrow($id) ne "none"} {
3553 if {$mainlinearrow($id) ne "none"} {
3551 if {abs([lindex $mainline($id) 1] - $y) < $thresh} {
3554 if {abs([lindex $mainline($id) 1] - $y) < $thresh} {
3552 return "up"
3555 return "up"
3553 }
3556 }
3554 }
3557 }
3555 }
3558 }
3556 if {[info exists sidelines($id)]} {
3559 if {[info exists sidelines($id)]} {
3557 foreach ls $sidelines($id) {
3560 foreach ls $sidelines($id) {
3558 set coords [lindex $ls 0]
3561 set coords [lindex $ls 0]
3559 set arrow [lindex $ls 2]
3562 set arrow [lindex $ls 2]
3560 if {$arrow eq "first" || $arrow eq "both"} {
3563 if {$arrow eq "first" || $arrow eq "both"} {
3561 if {abs([lindex $coords 1] - $y) < $thresh} {
3564 if {abs([lindex $coords 1] - $y) < $thresh} {
3562 return "up"
3565 return "up"
3563 }
3566 }
3564 }
3567 }
3565 if {$arrow eq "last" || $arrow eq "both"} {
3568 if {$arrow eq "last" || $arrow eq "both"} {
3566 if {abs([lindex $coords end] - $y) < $thresh} {
3569 if {abs([lindex $coords end] - $y) < $thresh} {
3567 return "down"
3570 return "down"
3568 }
3571 }
3569 }
3572 }
3570 }
3573 }
3571 }
3574 }
3572 return {}
3575 return {}
3573 }
3576 }
3574
3577
3575 proc arrowjump {id dirn y} {
3578 proc arrowjump {id dirn y} {
3576 global mainline sidelines canv
3579 global mainline sidelines canv
3577
3580
3578 set yt {}
3581 set yt {}
3579 if {$dirn eq "down"} {
3582 if {$dirn eq "down"} {
3580 if {[info exists mainline($id)]} {
3583 if {[info exists mainline($id)]} {
3581 set y1 [lindex $mainline($id) 1]
3584 set y1 [lindex $mainline($id) 1]
3582 if {$y1 > $y} {
3585 if {$y1 > $y} {
3583 set yt $y1
3586 set yt $y1
3584 }
3587 }
3585 }
3588 }
3586 if {[info exists sidelines($id)]} {
3589 if {[info exists sidelines($id)]} {
3587 foreach ls $sidelines($id) {
3590 foreach ls $sidelines($id) {
3588 set y1 [lindex $ls 0 1]
3591 set y1 [lindex $ls 0 1]
3589 if {$y1 > $y && ($yt eq {} || $y1 < $yt)} {
3592 if {$y1 > $y && ($yt eq {} || $y1 < $yt)} {
3590 set yt $y1
3593 set yt $y1
3591 }
3594 }
3592 }
3595 }
3593 }
3596 }
3594 } else {
3597 } else {
3595 if {[info exists sidelines($id)]} {
3598 if {[info exists sidelines($id)]} {
3596 foreach ls $sidelines($id) {
3599 foreach ls $sidelines($id) {
3597 set y1 [lindex $ls 0 end]
3600 set y1 [lindex $ls 0 end]
3598 if {$y1 < $y && ($yt eq {} || $y1 > $yt)} {
3601 if {$y1 < $y && ($yt eq {} || $y1 > $yt)} {
3599 set yt $y1
3602 set yt $y1
3600 }
3603 }
3601 }
3604 }
3602 }
3605 }
3603 }
3606 }
3604 if {$yt eq {}} return
3607 if {$yt eq {}} return
3605 set ymax [lindex [$canv cget -scrollregion] 3]
3608 set ymax [lindex [$canv cget -scrollregion] 3]
3606 if {$ymax eq {} || $ymax <= 0} return
3609 if {$ymax eq {} || $ymax <= 0} return
3607 set view [$canv yview]
3610 set view [$canv yview]
3608 set yspan [expr {[lindex $view 1] - [lindex $view 0]}]
3611 set yspan [expr {[lindex $view 1] - [lindex $view 0]}]
3609 set yfrac [expr {$yt / $ymax - $yspan / 2}]
3612 set yfrac [expr {$yt / $ymax - $yspan / 2}]
3610 if {$yfrac < 0} {
3613 if {$yfrac < 0} {
3611 set yfrac 0
3614 set yfrac 0
3612 }
3615 }
3613 $canv yview moveto $yfrac
3616 $canv yview moveto $yfrac
3614 }
3617 }
3615
3618
3616 proc lineclick {x y id isnew} {
3619 proc lineclick {x y id isnew} {
3617 global ctext commitinfo children cflist canv thickerline
3620 global ctext commitinfo children cflist canv thickerline
3618
3621
3619 unmarkmatches
3622 unmarkmatches
3620 unselectline
3623 unselectline
3621 normalline
3624 normalline
3622 $canv delete hover
3625 $canv delete hover
3623 # draw this line thicker than normal
3626 # draw this line thicker than normal
3624 drawlines $id 1
3627 drawlines $id 1
3625 set thickerline $id
3628 set thickerline $id
3626 if {$isnew} {
3629 if {$isnew} {
3627 set ymax [lindex [$canv cget -scrollregion] 3]
3630 set ymax [lindex [$canv cget -scrollregion] 3]
3628 if {$ymax eq {}} return
3631 if {$ymax eq {}} return
3629 set yfrac [lindex [$canv yview] 0]
3632 set yfrac [lindex [$canv yview] 0]
3630 set y [expr {$y + $yfrac * $ymax}]
3633 set y [expr {$y + $yfrac * $ymax}]
3631 }
3634 }
3632 set dirn [clickisonarrow $id $y]
3635 set dirn [clickisonarrow $id $y]
3633 if {$dirn ne {}} {
3636 if {$dirn ne {}} {
3634 arrowjump $id $dirn $y
3637 arrowjump $id $dirn $y
3635 return
3638 return
3636 }
3639 }
3637
3640
3638 if {$isnew} {
3641 if {$isnew} {
3639 addtohistory [list lineclick $x $y $id 0]
3642 addtohistory [list lineclick $x $y $id 0]
3640 }
3643 }
3641 # fill the details pane with info about this line
3644 # fill the details pane with info about this line
3642 $ctext conf -state normal
3645 $ctext conf -state normal
3643 $ctext delete 0.0 end
3646 $ctext delete 0.0 end
3644 $ctext tag conf link -foreground blue -underline 1
3647 $ctext tag conf link -foreground blue -underline 1
3645 $ctext tag bind link <Enter> { %W configure -cursor hand2 }
3648 $ctext tag bind link <Enter> { %W configure -cursor hand2 }
3646 $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
3649 $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
3647 $ctext insert end "Parent:\t"
3650 $ctext insert end "Parent:\t"
3648 $ctext insert end $id [list link link0]
3651 $ctext insert end $id [list link link0]
3649 $ctext tag bind link0 <1> [list selbyid $id]
3652 $ctext tag bind link0 <1> [list selbyid $id]
3650 set info $commitinfo($id)
3653 set info $commitinfo($id)
3651 $ctext insert end "\n\t[lindex $info 0]\n"
3654 $ctext insert end "\n\t[lindex $info 0]\n"
3652 $ctext insert end "\tUser:\t[lindex $info 1]\n"
3655 $ctext insert end "\tUser:\t[lindex $info 1]\n"
3653 $ctext insert end "\tDate:\t[lindex $info 2]\n"
3656 $ctext insert end "\tDate:\t[lindex $info 2]\n"
3654 if {[info exists children($id)]} {
3657 if {[info exists children($id)]} {
3655 $ctext insert end "\nChildren:"
3658 $ctext insert end "\nChildren:"
3656 set i 0
3659 set i 0
3657 foreach child $children($id) {
3660 foreach child $children($id) {
3658 incr i
3661 incr i
3659 set info $commitinfo($child)
3662 set info $commitinfo($child)
3660 $ctext insert end "\n\t"
3663 $ctext insert end "\n\t"
3661 $ctext insert end $child [list link link$i]
3664 $ctext insert end $child [list link link$i]
3662 $ctext tag bind link$i <1> [list selbyid $child]
3665 $ctext tag bind link$i <1> [list selbyid $child]
3663 $ctext insert end "\n\t[lindex $info 0]"
3666 $ctext insert end "\n\t[lindex $info 0]"
3664 $ctext insert end "\n\tUser:\t[lindex $info 1]"
3667 $ctext insert end "\n\tUser:\t[lindex $info 1]"
3665 $ctext insert end "\n\tDate:\t[lindex $info 2]\n"
3668 $ctext insert end "\n\tDate:\t[lindex $info 2]\n"
3666 }
3669 }
3667 }
3670 }
3668 $ctext conf -state disabled
3671 $ctext conf -state disabled
3669
3672
3670 $cflist delete 0 end
3673 $cflist delete 0 end
3671 }
3674 }
3672
3675
3673 proc normalline {} {
3676 proc normalline {} {
3674 global thickerline
3677 global thickerline
3675 if {[info exists thickerline]} {
3678 if {[info exists thickerline]} {
3676 drawlines $thickerline 0
3679 drawlines $thickerline 0
3677 unset thickerline
3680 unset thickerline
3678 }
3681 }
3679 }
3682 }
3680
3683
3681 proc selbyid {id} {
3684 proc selbyid {id} {
3682 global idline
3685 global idline
3683 if {[info exists idline($id)]} {
3686 if {[info exists idline($id)]} {
3684 selectline $idline($id) 1
3687 selectline $idline($id) 1
3685 }
3688 }
3686 }
3689 }
3687
3690
3688 proc mstime {} {
3691 proc mstime {} {
3689 global startmstime
3692 global startmstime
3690 if {![info exists startmstime]} {
3693 if {![info exists startmstime]} {
3691 set startmstime [clock clicks -milliseconds]
3694 set startmstime [clock clicks -milliseconds]
3692 }
3695 }
3693 return [format "%.3f" [expr {([clock click -milliseconds] - $startmstime) / 1000.0}]]
3696 return [format "%.3f" [expr {([clock click -milliseconds] - $startmstime) / 1000.0}]]
3694 }
3697 }
3695
3698
3696 proc rowmenu {x y id} {
3699 proc rowmenu {x y id} {
3697 global rowctxmenu idline selectedline rowmenuid hgvdiff
3700 global rowctxmenu idline selectedline rowmenuid hgvdiff
3698
3701
3699 if {![info exists selectedline] || $idline($id) eq $selectedline} {
3702 if {![info exists selectedline] || $idline($id) eq $selectedline} {
3700 set state disabled
3703 set state disabled
3701 } else {
3704 } else {
3702 set state normal
3705 set state normal
3703 }
3706 }
3704 $rowctxmenu entryconfigure 0 -state $state
3707 $rowctxmenu entryconfigure 0 -state $state
3705 $rowctxmenu entryconfigure 1 -state $state
3708 $rowctxmenu entryconfigure 1 -state $state
3706 $rowctxmenu entryconfigure 2 -state $state
3709 $rowctxmenu entryconfigure 2 -state $state
3707 if { $hgvdiff ne "" } {
3710 if { $hgvdiff ne "" } {
3708 $rowctxmenu entryconfigure 6 -state $state
3711 $rowctxmenu entryconfigure 6 -state $state
3709 }
3712 }
3710 set rowmenuid $id
3713 set rowmenuid $id
3711 tk_popup $rowctxmenu $x $y
3714 tk_popup $rowctxmenu $x $y
3712 }
3715 }
3713
3716
3714 proc diffvssel {dirn} {
3717 proc diffvssel {dirn} {
3715 global rowmenuid selectedline lineid
3718 global rowmenuid selectedline lineid
3716
3719
3717 if {![info exists selectedline]} return
3720 if {![info exists selectedline]} return
3718 if {$dirn} {
3721 if {$dirn} {
3719 set oldid $lineid($selectedline)
3722 set oldid $lineid($selectedline)
3720 set newid $rowmenuid
3723 set newid $rowmenuid
3721 } else {
3724 } else {
3722 set oldid $rowmenuid
3725 set oldid $rowmenuid
3723 set newid $lineid($selectedline)
3726 set newid $lineid($selectedline)
3724 }
3727 }
3725 addtohistory [list doseldiff $oldid $newid]
3728 addtohistory [list doseldiff $oldid $newid]
3726 doseldiff $oldid $newid
3729 doseldiff $oldid $newid
3727 }
3730 }
3728
3731
3729 proc doseldiff {oldid newid} {
3732 proc doseldiff {oldid newid} {
3730 global ctext cflist
3733 global ctext cflist
3731 global commitinfo
3734 global commitinfo
3732
3735
3733 $ctext conf -state normal
3736 $ctext conf -state normal
3734 $ctext delete 0.0 end
3737 $ctext delete 0.0 end
3735 $ctext mark set fmark.0 0.0
3738 $ctext mark set fmark.0 0.0
3736 $ctext mark gravity fmark.0 left
3739 $ctext mark gravity fmark.0 left
3737 $cflist delete 0 end
3740 $cflist delete 0 end
3738 $cflist insert end "Top"
3741 $cflist insert end "Top"
3739 $ctext insert end "From "
3742 $ctext insert end "From "
3740 $ctext tag conf link -foreground blue -underline 1
3743 $ctext tag conf link -foreground blue -underline 1
3741 $ctext tag bind link <Enter> { %W configure -cursor hand2 }
3744 $ctext tag bind link <Enter> { %W configure -cursor hand2 }
3742 $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
3745 $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
3743 $ctext tag bind link0 <1> [list selbyid $oldid]
3746 $ctext tag bind link0 <1> [list selbyid $oldid]
3744 $ctext insert end $oldid [list link link0]
3747 $ctext insert end $oldid [list link link0]
3745 $ctext insert end "\n "
3748 $ctext insert end "\n "
3746 $ctext insert end [lindex $commitinfo($oldid) 0]
3749 $ctext insert end [lindex $commitinfo($oldid) 0]
3747 $ctext insert end "\n\nTo "
3750 $ctext insert end "\n\nTo "
3748 $ctext tag bind link1 <1> [list selbyid $newid]
3751 $ctext tag bind link1 <1> [list selbyid $newid]
3749 $ctext insert end $newid [list link link1]
3752 $ctext insert end $newid [list link link1]
3750 $ctext insert end "\n "
3753 $ctext insert end "\n "
3751 $ctext insert end [lindex $commitinfo($newid) 0]
3754 $ctext insert end [lindex $commitinfo($newid) 0]
3752 $ctext insert end "\n"
3755 $ctext insert end "\n"
3753 $ctext conf -state disabled
3756 $ctext conf -state disabled
3754 $ctext tag delete Comments
3757 $ctext tag delete Comments
3755 $ctext tag remove found 1.0 end
3758 $ctext tag remove found 1.0 end
3756 startdiff [list $newid $oldid]
3759 startdiff [list $newid $oldid]
3757 }
3760 }
3758
3761
3759 proc mkpatch {} {
3762 proc mkpatch {} {
3760 global rowmenuid currentid commitinfo patchtop patchnum
3763 global rowmenuid currentid commitinfo patchtop patchnum
3761
3764
3762 if {![info exists currentid]} return
3765 if {![info exists currentid]} return
3763 set oldid $currentid
3766 set oldid $currentid
3764 set oldhead [lindex $commitinfo($oldid) 0]
3767 set oldhead [lindex $commitinfo($oldid) 0]
3765 set newid $rowmenuid
3768 set newid $rowmenuid
3766 set newhead [lindex $commitinfo($newid) 0]
3769 set newhead [lindex $commitinfo($newid) 0]
3767 set top .patch
3770 set top .patch
3768 set patchtop $top
3771 set patchtop $top
3769 catch {destroy $top}
3772 catch {destroy $top}
3770 toplevel $top
3773 toplevel $top
3771 ttk::label $top.from -text "From:"
3774 ttk::label $top.from -text "From:"
3772 ttk::entry $top.fromsha1 -width 40
3775 ttk::entry $top.fromsha1 -width 40
3773 $top.fromsha1 insert 0 $oldid
3776 $top.fromsha1 insert 0 $oldid
3774 $top.fromsha1 conf -state readonly
3777 $top.fromsha1 conf -state readonly
3775 grid $top.from $top.fromsha1 -sticky w -pady {10 0}
3778 grid $top.from $top.fromsha1 -sticky w -pady {10 0}
3776 ttk::entry $top.fromhead -width 60
3779 ttk::entry $top.fromhead -width 60
3777 $top.fromhead insert 0 $oldhead
3780 $top.fromhead insert 0 $oldhead
3778 $top.fromhead conf -state readonly
3781 $top.fromhead conf -state readonly
3779 grid x $top.fromhead -sticky w
3782 grid x $top.fromhead -sticky w
3780 ttk::label $top.to -text "To:"
3783 ttk::label $top.to -text "To:"
3781 ttk::entry $top.tosha1 -width 40
3784 ttk::entry $top.tosha1 -width 40
3782 $top.tosha1 insert 0 $newid
3785 $top.tosha1 insert 0 $newid
3783 $top.tosha1 conf -state readonly
3786 $top.tosha1 conf -state readonly
3784 grid $top.to $top.tosha1 -sticky w
3787 grid $top.to $top.tosha1 -sticky w
3785 ttk::entry $top.tohead -width 60
3788 ttk::entry $top.tohead -width 60
3786 $top.tohead insert 0 $newhead
3789 $top.tohead insert 0 $newhead
3787 $top.tohead conf -state readonly
3790 $top.tohead conf -state readonly
3788 grid x $top.tohead -sticky w
3791 grid x $top.tohead -sticky w
3789 ttk::button $top.rev -text "Reverse" -command mkpatchrev
3792 ttk::button $top.rev -text "Reverse" -command mkpatchrev
3790 grid $top.rev x -pady 10
3793 grid $top.rev x -pady 10
3791 ttk::label $top.flab -text "Output file:"
3794 ttk::label $top.flab -text "Output file:"
3792 ttk::entry $top.fname -width 60
3795 ttk::entry $top.fname -width 60
3793 $top.fname insert 0 [file normalize "patch$patchnum.patch"]
3796 $top.fname insert 0 [file normalize "patch$patchnum.patch"]
3794 incr patchnum
3797 incr patchnum
3795 grid $top.flab $top.fname -sticky w
3798 grid $top.flab $top.fname -sticky w
3796 ttk::frame $top.buts
3799 ttk::frame $top.buts
3797 ttk::button $top.buts.gen -text "Generate" -command mkpatchgo
3800 ttk::button $top.buts.gen -text "Generate" -command mkpatchgo
3798 ttk::button $top.buts.can -text "Cancel" -command mkpatchcan
3801 ttk::button $top.buts.can -text "Cancel" -command mkpatchcan
3799 grid $top.buts.gen $top.buts.can
3802 grid $top.buts.gen $top.buts.can
3800 grid columnconfigure $top.buts 0 -weight 1 -uniform a
3803 grid columnconfigure $top.buts 0 -weight 1 -uniform a
3801 grid columnconfigure $top.buts 1 -weight 1 -uniform a
3804 grid columnconfigure $top.buts 1 -weight 1 -uniform a
3802 grid $top.buts - -pady 10 -sticky ew
3805 grid $top.buts - -pady 10 -sticky ew
3803 focus $top.fname
3806 focus $top.fname
3804 popupify $top
3807 popupify $top
3805 wm title $top "Generate a patch"
3808 wm title $top "Generate a patch"
3806 }
3809 }
3807
3810
3808 proc mkpatchrev {} {
3811 proc mkpatchrev {} {
3809 global patchtop
3812 global patchtop
3810
3813
3811 set oldid [$patchtop.fromsha1 get]
3814 set oldid [$patchtop.fromsha1 get]
3812 set oldhead [$patchtop.fromhead get]
3815 set oldhead [$patchtop.fromhead get]
3813 set newid [$patchtop.tosha1 get]
3816 set newid [$patchtop.tosha1 get]
3814 set newhead [$patchtop.tohead get]
3817 set newhead [$patchtop.tohead get]
3815 foreach e [list fromsha1 fromhead tosha1 tohead] \
3818 foreach e [list fromsha1 fromhead tosha1 tohead] \
3816 v [list $newid $newhead $oldid $oldhead] {
3819 v [list $newid $newhead $oldid $oldhead] {
3817 $patchtop.$e conf -state normal
3820 $patchtop.$e conf -state normal
3818 $patchtop.$e delete 0 end
3821 $patchtop.$e delete 0 end
3819 $patchtop.$e insert 0 $v
3822 $patchtop.$e insert 0 $v
3820 $patchtop.$e conf -state readonly
3823 $patchtop.$e conf -state readonly
3821 }
3824 }
3822 }
3825 }
3823
3826
3824 proc mkpatchgo {} {
3827 proc mkpatchgo {} {
3825 global patchtop env
3828 global patchtop env
3826
3829
3827 set oldid [$patchtop.fromsha1 get]
3830 set oldid [$patchtop.fromsha1 get]
3828 set newid [$patchtop.tosha1 get]
3831 set newid [$patchtop.tosha1 get]
3829 set fname [$patchtop.fname get]
3832 set fname [$patchtop.fname get]
3830 if {[catch {exec $env(HG) --config ui.report_untrusted=false debug-diff-tree -p $oldid $newid >$fname &} err]} {
3833 if {[catch {exec $env(HG) --config ui.report_untrusted=false debug-diff-tree -p $oldid $newid >$fname &} err]} {
3831 error_popup "Error creating patch: $err"
3834 error_popup "Error creating patch: $err"
3832 }
3835 }
3833 catch {destroy $patchtop}
3836 catch {destroy $patchtop}
3834 unset patchtop
3837 unset patchtop
3835 }
3838 }
3836
3839
3837 proc mkpatchcan {} {
3840 proc mkpatchcan {} {
3838 global patchtop
3841 global patchtop
3839
3842
3840 catch {destroy $patchtop}
3843 catch {destroy $patchtop}
3841 unset patchtop
3844 unset patchtop
3842 }
3845 }
3843
3846
3844 proc mktag {} {
3847 proc mktag {} {
3845 global rowmenuid mktagtop commitinfo
3848 global rowmenuid mktagtop commitinfo
3846
3849
3847 set top .maketag
3850 set top .maketag
3848 set mktagtop $top
3851 set mktagtop $top
3849 catch {destroy $top}
3852 catch {destroy $top}
3850 toplevel $top
3853 toplevel $top
3851 ttk::label $top.id -text "ID:"
3854 ttk::label $top.id -text "ID:"
3852 ttk::entry $top.sha1 -width 40
3855 ttk::entry $top.sha1 -width 40
3853 $top.sha1 insert 0 $rowmenuid
3856 $top.sha1 insert 0 $rowmenuid
3854 $top.sha1 conf -state readonly
3857 $top.sha1 conf -state readonly
3855 grid $top.id $top.sha1 -sticky w -pady {10 0}
3858 grid $top.id $top.sha1 -sticky w -pady {10 0}
3856 ttk::entry $top.head -width 60
3859 ttk::entry $top.head -width 60
3857 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
3860 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
3858 $top.head conf -state readonly
3861 $top.head conf -state readonly
3859 grid x $top.head -sticky w
3862 grid x $top.head -sticky w
3860 ttk::label $top.tlab -text "Tag name:"
3863 ttk::label $top.tlab -text "Tag name:"
3861 ttk::entry $top.tag -width 60
3864 ttk::entry $top.tag -width 60
3862 grid $top.tlab $top.tag -sticky w
3865 grid $top.tlab $top.tag -sticky w
3863 ttk::frame $top.buts
3866 ttk::frame $top.buts
3864 ttk::button $top.buts.gen -text "Create" -command mktaggo
3867 ttk::button $top.buts.gen -text "Create" -command mktaggo
3865 ttk::button $top.buts.can -text "Cancel" -command mktagcan
3868 ttk::button $top.buts.can -text "Cancel" -command mktagcan
3866 grid $top.buts.gen $top.buts.can
3869 grid $top.buts.gen $top.buts.can
3867 grid columnconfigure $top.buts 0 -weight 1 -uniform a
3870 grid columnconfigure $top.buts 0 -weight 1 -uniform a
3868 grid columnconfigure $top.buts 1 -weight 1 -uniform a
3871 grid columnconfigure $top.buts 1 -weight 1 -uniform a
3869 grid $top.buts - -pady 10 -sticky ew
3872 grid $top.buts - -pady 10 -sticky ew
3870 focus $top.tag
3873 focus $top.tag
3871 popupify $top
3874 popupify $top
3872 wm title $top "Create a tag"
3875 wm title $top "Create a tag"
3873 }
3876 }
3874
3877
3875 proc domktag {} {
3878 proc domktag {} {
3876 global mktagtop env tagids idtags
3879 global mktagtop env tagids idtags
3877
3880
3878 set id [$mktagtop.sha1 get]
3881 set id [$mktagtop.sha1 get]
3879 set tag [$mktagtop.tag get]
3882 set tag [$mktagtop.tag get]
3880 if {$tag == {}} {
3883 if {$tag == {}} {
3881 error_popup "No tag name specified"
3884 error_popup "No tag name specified"
3882 return
3885 return
3883 }
3886 }
3884 if {[info exists tagids($tag)]} {
3887 if {[info exists tagids($tag)]} {
3885 error_popup "Tag \"$tag\" already exists"
3888 error_popup "Tag \"$tag\" already exists"
3886 return
3889 return
3887 }
3890 }
3888 if {[catch {
3891 if {[catch {
3889 set out [exec $env(HG) --config ui.report_untrusted=false tag -r $id $tag]
3892 set out [exec $env(HG) --config ui.report_untrusted=false tag -r $id $tag]
3890 } err]} {
3893 } err]} {
3891 error_popup "Error creating tag: $err"
3894 error_popup "Error creating tag: $err"
3892 return
3895 return
3893 }
3896 }
3894
3897
3895 set tagids($tag) $id
3898 set tagids($tag) $id
3896 lappend idtags($id) $tag
3899 lappend idtags($id) $tag
3897 redrawtags $id
3900 redrawtags $id
3898 }
3901 }
3899
3902
3900 proc redrawtags {id} {
3903 proc redrawtags {id} {
3901 global canv linehtag idline idpos selectedline
3904 global canv linehtag idline idpos selectedline
3902
3905
3903 if {![info exists idline($id)]} return
3906 if {![info exists idline($id)]} return
3904 $canv delete tag.$id
3907 $canv delete tag.$id
3905 set xt [eval drawtags $id $idpos($id)]
3908 set xt [eval drawtags $id $idpos($id)]
3906 $canv coords $linehtag($idline($id)) $xt [lindex $idpos($id) 2]
3909 $canv coords $linehtag($idline($id)) $xt [lindex $idpos($id) 2]
3907 if {[info exists selectedline] && $selectedline == $idline($id)} {
3910 if {[info exists selectedline] && $selectedline == $idline($id)} {
3908 selectline $selectedline 0
3911 selectline $selectedline 0
3909 }
3912 }
3910 }
3913 }
3911
3914
3912 proc mktagcan {} {
3915 proc mktagcan {} {
3913 global mktagtop
3916 global mktagtop
3914
3917
3915 catch {destroy $mktagtop}
3918 catch {destroy $mktagtop}
3916 unset mktagtop
3919 unset mktagtop
3917 }
3920 }
3918
3921
3919 proc mktaggo {} {
3922 proc mktaggo {} {
3920 domktag
3923 domktag
3921 mktagcan
3924 mktagcan
3922 }
3925 }
3923
3926
3924 proc writecommit {} {
3927 proc writecommit {} {
3925 global rowmenuid wrcomtop commitinfo
3928 global rowmenuid wrcomtop commitinfo
3926
3929
3927 set top .writecommit
3930 set top .writecommit
3928 set wrcomtop $top
3931 set wrcomtop $top
3929 catch {destroy $top}
3932 catch {destroy $top}
3930 toplevel $top
3933 toplevel $top
3931 ttk::label $top.id -text "ID:"
3934 ttk::label $top.id -text "ID:"
3932 ttk::entry $top.sha1 -width 40
3935 ttk::entry $top.sha1 -width 40
3933 $top.sha1 insert 0 $rowmenuid
3936 $top.sha1 insert 0 $rowmenuid
3934 $top.sha1 conf -state readonly
3937 $top.sha1 conf -state readonly
3935 grid $top.id $top.sha1 -sticky w -pady {10 0}
3938 grid $top.id $top.sha1 -sticky w -pady {10 0}
3936 ttk::entry $top.head -width 60
3939 ttk::entry $top.head -width 60
3937 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
3940 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
3938 $top.head conf -state readonly
3941 $top.head conf -state readonly
3939 grid x $top.head -sticky w
3942 grid x $top.head -sticky w
3940 ttk::label $top.flab -text "Output file:"
3943 ttk::label $top.flab -text "Output file:"
3941 ttk::entry $top.fname -width 60
3944 ttk::entry $top.fname -width 60
3942 $top.fname insert 0 [file normalize "commit-[string range $rowmenuid 0 6].diff"]
3945 $top.fname insert 0 [file normalize "commit-[string range $rowmenuid 0 6].diff"]
3943 grid $top.flab $top.fname -sticky w
3946 grid $top.flab $top.fname -sticky w
3944 ttk::frame $top.buts
3947 ttk::frame $top.buts
3945 ttk::button $top.buts.gen -text "Write" -command wrcomgo
3948 ttk::button $top.buts.gen -text "Write" -command wrcomgo
3946 ttk::button $top.buts.can -text "Cancel" -command wrcomcan
3949 ttk::button $top.buts.can -text "Cancel" -command wrcomcan
3947 grid $top.buts.gen $top.buts.can
3950 grid $top.buts.gen $top.buts.can
3948 grid columnconfigure $top.buts 0 -weight 1 -uniform a
3951 grid columnconfigure $top.buts 0 -weight 1 -uniform a
3949 grid columnconfigure $top.buts 1 -weight 1 -uniform a
3952 grid columnconfigure $top.buts 1 -weight 1 -uniform a
3950 grid $top.buts - -pady 10 -sticky ew
3953 grid $top.buts - -pady 10 -sticky ew
3951 focus $top.fname
3954 focus $top.fname
3952 popupify $top
3955 popupify $top
3953 wm title $top "Write commit to a file"
3956 wm title $top "Write commit to a file"
3954 }
3957 }
3955
3958
3956 proc wrcomgo {} {
3959 proc wrcomgo {} {
3957 global wrcomtop
3960 global wrcomtop
3958
3961
3959 set id [$wrcomtop.sha1 get]
3962 set id [$wrcomtop.sha1 get]
3960 set fname [$wrcomtop.fname get]
3963 set fname [$wrcomtop.fname get]
3961 if {[catch {exec $::env(HG) --config ui.report_untrusted=false export --git -o [string map {% %%} $fname] $id} err]} {
3964 if {[catch {exec $::env(HG) --config ui.report_untrusted=false export --git -o [string map {% %%} $fname] $id} err]} {
3962 error_popup "Error writing commit: $err"
3965 error_popup "Error writing commit: $err"
3963 }
3966 }
3964 catch {destroy $wrcomtop}
3967 catch {destroy $wrcomtop}
3965 unset wrcomtop
3968 unset wrcomtop
3966 }
3969 }
3967
3970
3968 proc wrcomcan {} {
3971 proc wrcomcan {} {
3969 global wrcomtop
3972 global wrcomtop
3970
3973
3971 catch {destroy $wrcomtop}
3974 catch {destroy $wrcomtop}
3972 unset wrcomtop
3975 unset wrcomtop
3973 }
3976 }
3974
3977
3975 proc listrefs {id} {
3978 proc listrefs {id} {
3976 global idtags idheads idotherrefs idbookmarks
3979 global idtags idheads idotherrefs idbookmarks
3977
3980
3978 set w {}
3981 set w {}
3979 if {[info exists idbookmarks($id)]} {
3982 if {[info exists idbookmarks($id)]} {
3980 set w $idbookmarks($id)
3983 set w $idbookmarks($id)
3981 }
3984 }
3982 set x {}
3985 set x {}
3983 if {[info exists idtags($id)]} {
3986 if {[info exists idtags($id)]} {
3984 set x $idtags($id)
3987 set x $idtags($id)
3985 }
3988 }
3986 set y {}
3989 set y {}
3987 if {[info exists idheads($id)]} {
3990 if {[info exists idheads($id)]} {
3988 set y $idheads($id)
3991 set y $idheads($id)
3989 }
3992 }
3990 set z {}
3993 set z {}
3991 if {[info exists idotherrefs($id)]} {
3994 if {[info exists idotherrefs($id)]} {
3992 set z $idotherrefs($id)
3995 set z $idotherrefs($id)
3993 }
3996 }
3994 return [list $w $x $y $z]
3997 return [list $w $x $y $z]
3995 }
3998 }
3996
3999
3997 proc rereadrefs {} {
4000 proc rereadrefs {} {
3998 global idbookmarks idtags idheads idotherrefs
4001 global idbookmarks idtags idheads idotherrefs
3999 global bookmarkids tagids headids otherrefids
4002 global bookmarkids tagids headids otherrefids
4000
4003
4001 set refids [concat [array names idtags] \
4004 set refids [concat [array names idtags] \
4002 [array names idheads] [array names idotherrefs] \
4005 [array names idheads] [array names idotherrefs] \
4003 [array names idbookmarks]]
4006 [array names idbookmarks]]
4004 foreach id $refids {
4007 foreach id $refids {
4005 if {![info exists ref($id)]} {
4008 if {![info exists ref($id)]} {
4006 set ref($id) [listrefs $id]
4009 set ref($id) [listrefs $id]
4007 }
4010 }
4008 }
4011 }
4009 foreach v {tagids idtags headids idheads otherrefids idotherrefs \
4012 foreach v {tagids idtags headids idheads otherrefids idotherrefs \
4010 bookmarkids idbookmarks} {
4013 bookmarkids idbookmarks} {
4011 catch {unset $v}
4014 catch {unset $v}
4012 }
4015 }
4013 readrefs
4016 readrefs
4014 set refids [lsort -unique [concat $refids [array names idtags] \
4017 set refids [lsort -unique [concat $refids [array names idtags] \
4015 [array names idheads] [array names idotherrefs] \
4018 [array names idheads] [array names idotherrefs] \
4016 [array names idbookmarks]]]
4019 [array names idbookmarks]]]
4017 foreach id $refids {
4020 foreach id $refids {
4018 set v [listrefs $id]
4021 set v [listrefs $id]
4019 if {![info exists ref($id)] || $ref($id) != $v} {
4022 if {![info exists ref($id)] || $ref($id) != $v} {
4020 redrawtags $id
4023 redrawtags $id
4021 }
4024 }
4022 }
4025 }
4023 }
4026 }
4024
4027
4025 proc vdiff {withparent} {
4028 proc vdiff {withparent} {
4026 global env rowmenuid selectedline lineid hgvdiff
4029 global env rowmenuid selectedline lineid hgvdiff
4027
4030
4028 if {![info exists rowmenuid]} return
4031 if {![info exists rowmenuid]} return
4029 set curid $rowmenuid
4032 set curid $rowmenuid
4030
4033
4031 if {$withparent} {
4034 if {$withparent} {
4032 set parents [exec $env(HG) --config ui.report_untrusted=false parents --rev $curid --template "{node}\n"]
4035 set parents [exec $env(HG) --config ui.report_untrusted=false parents --rev $curid --template "{node}\n"]
4033 set firstparent [lindex [split $parents "\n"] 0]
4036 set firstparent [lindex [split $parents "\n"] 0]
4034 set otherid $firstparent
4037 set otherid $firstparent
4035 } else {
4038 } else {
4036 if {![info exists selectedline]} return
4039 if {![info exists selectedline]} return
4037 set otherid $lineid($selectedline)
4040 set otherid $lineid($selectedline)
4038 }
4041 }
4039 set range "$otherid:$curid"
4042 set range "$otherid:$curid"
4040 if {[catch {exec $env(HG) --config ui.report_untrusted=false $hgvdiff -r $range} err]} {
4043 if {[catch {exec $env(HG) --config ui.report_untrusted=false $hgvdiff -r $range} err]} {
4041 # Ignore errors, this is just visualization
4044 # Ignore errors, this is just visualization
4042 }
4045 }
4043 }
4046 }
4044
4047
4045 proc showtag {tag isnew} {
4048 proc showtag {tag isnew} {
4046 global ctext cflist tagcontents tagids linknum
4049 global ctext cflist tagcontents tagids linknum
4047
4050
4048 if {$isnew} {
4051 if {$isnew} {
4049 addtohistory [list showtag $tag 0]
4052 addtohistory [list showtag $tag 0]
4050 }
4053 }
4051 $ctext conf -state normal
4054 $ctext conf -state normal
4052 $ctext delete 0.0 end
4055 $ctext delete 0.0 end
4053 set linknum 0
4056 set linknum 0
4054 if {[info exists tagcontents($tag)]} {
4057 if {[info exists tagcontents($tag)]} {
4055 set text $tagcontents($tag)
4058 set text $tagcontents($tag)
4056 } else {
4059 } else {
4057 set text "Tag: $tag\nId: $tagids($tag)"
4060 set text "Tag: $tag\nId: $tagids($tag)"
4058 }
4061 }
4059 appendwithlinks $text
4062 appendwithlinks $text
4060 $ctext conf -state disabled
4063 $ctext conf -state disabled
4061 $cflist delete 0 end
4064 $cflist delete 0 end
4062 }
4065 }
4063
4066
4064 proc doquit {} {
4067 proc doquit {} {
4065 global stopped
4068 global stopped
4066 set stopped 100
4069 set stopped 100
4067 destroy .
4070 destroy .
4068 }
4071 }
4069
4072
4070 proc getconfig {} {
4073 proc getconfig {} {
4071 global env
4074 global env
4072 set config {}
4075 set config {}
4073
4076
4074 set lines [exec $env(HG) debugconfig]
4077 set lines [exec $env(HG) debugconfig]
4075 foreach line [split $lines \n] {
4078 foreach line [split $lines \n] {
4076 set line [string trimright $line \r]
4079 set line [string trimright $line \r]
4077 if {[string match hgk.* $line]} {
4080 if {[string match hgk.* $line]} {
4078 regexp {(.*)=(.*)} $line - k v
4081 regexp {(.*)=(.*)} $line - k v
4079 lappend config $k $v
4082 lappend config $k $v
4080 }
4083 }
4081 }
4084 }
4082 return $config
4085 return $config
4083 }
4086 }
4084
4087
4085 # defaults...
4088 # defaults...
4086 set datemode 0
4089 set datemode 0
4087 set boldnames 0
4090 set boldnames 0
4088 set diffopts "-U 5 -p"
4091 set diffopts "-U 5 -p"
4089
4092
4090 set mainfont {Helvetica 9}
4093 set mainfont {Helvetica 9}
4091 set curidfont {}
4094 set curidfont {}
4092 set textfont {Courier 9}
4095 set textfont {Courier 9}
4093 set findmergefiles 0
4096 set findmergefiles 0
4094 set gaudydiff 0
4097 set gaudydiff 0
4095 set maxgraphpct 50
4098 set maxgraphpct 50
4096 set maxwidth 16
4099 set maxwidth 16
4097
4100
4098 set colors {green red blue magenta darkgrey brown orange}
4101 set colors {green red blue magenta darkgrey brown orange}
4099 set authorcolors {
4102 set authorcolors {
4100 black blue deeppink mediumorchid blue burlywood4 goldenrod slateblue red2 navy dimgrey
4103 black blue deeppink mediumorchid blue burlywood4 goldenrod slateblue red2 navy dimgrey
4101 }
4104 }
4102 set bgcolor white
4105 set bgcolor white
4103
4106
4104 # This color should probably be some system color (provided by tk),
4107 # This color should probably be some system color (provided by tk),
4105 # but as the bgcolor has always been set to white, I choose to ignore
4108 # but as the bgcolor has always been set to white, I choose to ignore
4106 set fgcolor black
4109 set fgcolor black
4107 set diffaddcolor "#00a000"
4110 set diffaddcolor "#00a000"
4108 set diffremcolor red
4111 set diffremcolor red
4109 set diffmerge1color red
4112 set diffmerge1color red
4110 set diffmerge2color blue
4113 set diffmerge2color blue
4111 set hunksepcolor blue
4114 set hunksepcolor blue
4112
4115
4113 catch {source ~/.hgk}
4116 catch {source ~/.hgk}
4114
4117
4115 if {$curidfont == ""} { # initialize late based on current mainfont
4118 if {$curidfont == ""} { # initialize late based on current mainfont
4116 set curidfont "$mainfont bold italic underline"
4119 set curidfont "$mainfont bold italic underline"
4117 }
4120 }
4118
4121
4119 set namefont $mainfont
4122 set namefont $mainfont
4120 if {$boldnames} {
4123 if {$boldnames} {
4121 lappend namefont bold
4124 lappend namefont bold
4122 }
4125 }
4123
4126
4124 set revtreeargs {}
4127 set revtreeargs {}
4125 foreach arg $argv {
4128 foreach arg $argv {
4126 switch -regexp -- $arg {
4129 switch -regexp -- $arg {
4127 "^$" { }
4130 "^$" { }
4128 "^-b" { set boldnames 1 }
4131 "^-b" { set boldnames 1 }
4129 "^-d" { set datemode 1 }
4132 "^-d" { set datemode 1 }
4130 default {
4133 default {
4131 lappend revtreeargs $arg
4134 lappend revtreeargs $arg
4132 }
4135 }
4133 }
4136 }
4134 }
4137 }
4135
4138
4136 set history {}
4139 set history {}
4137 set historyindex 0
4140 set historyindex 0
4138
4141
4139 set stopped 0
4142 set stopped 0
4140 set redisplaying 0
4143 set redisplaying 0
4141 set stuffsaved 0
4144 set stuffsaved 0
4142 set patchnum 0
4145 set patchnum 0
4143
4146
4144 set config(hgk.vdiff) ""
4147 set config(hgk.vdiff) ""
4145 array set config [getconfig]
4148 array set config [getconfig]
4146 set hgvdiff $config(hgk.vdiff)
4149 set hgvdiff $config(hgk.vdiff)
4147 setcoords
4150 setcoords
4148 makewindow
4151 makewindow
4149 readrefs
4152 readrefs
4150 set hgroot [exec $env(HG) root]
4153 set hgroot [exec $env(HG) root]
4151 wm title . "hgk $hgroot"
4154 wm title . "hgk $hgroot"
4152 getcommits $revtreeargs
4155 getcommits $revtreeargs
@@ -1,335 +1,331
1 # Minimal support for git commands on an hg repository
1 # Minimal support for git commands on an hg repository
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 '''browse the repository in a graphical way
8 '''browse the repository in a graphical way
9
9
10 The hgk extension allows browsing the history of a repository in a
10 The hgk extension allows browsing the history of a repository in a
11 graphical way. It requires Tcl/Tk version 8.4 or later. (Tcl/Tk is not
11 graphical way. It requires Tcl/Tk version 8.4 or later. (Tcl/Tk is not
12 distributed with Mercurial.)
12 distributed with Mercurial.)
13
13
14 hgk consists of two parts: a Tcl script that does the displaying and
14 hgk consists of two parts: a Tcl script that does the displaying and
15 querying of information, and an extension to Mercurial named hgk.py,
15 querying of information, and an extension to Mercurial named hgk.py,
16 which provides hooks for hgk to get information. hgk can be found in
16 which provides hooks for hgk to get information. hgk can be found in
17 the contrib directory, and the extension is shipped in the hgext
17 the contrib directory, and the extension is shipped in the hgext
18 repository, and needs to be enabled.
18 repository, and needs to be enabled.
19
19
20 The :hg:`view` command will launch the hgk Tcl script. For this command
20 The :hg:`view` command will launch the hgk Tcl script. For this command
21 to work, hgk must be in your search path. Alternately, you can specify
21 to work, hgk must be in your search path. Alternately, you can specify
22 the path to hgk in your configuration file::
22 the path to hgk in your configuration file::
23
23
24 [hgk]
24 [hgk]
25 path=/location/of/hgk
25 path=/location/of/hgk
26
26
27 hgk can make use of the extdiff extension to visualize revisions.
27 hgk can make use of the extdiff extension to visualize revisions.
28 Assuming you had already configured extdiff vdiff command, just add::
28 Assuming you had already configured extdiff vdiff command, just add::
29
29
30 [hgk]
30 [hgk]
31 vdiff=vdiff
31 vdiff=vdiff
32
32
33 Revisions context menu will now display additional entries to fire
33 Revisions context menu will now display additional entries to fire
34 vdiff on hovered and selected revisions.
34 vdiff on hovered and selected revisions.
35 '''
35 '''
36
36
37 import os
37 import os
38 from mercurial import cmdutil, commands, patch, scmutil, obsolete
38 from mercurial import cmdutil, commands, patch, scmutil, obsolete
39 from mercurial.node import nullid, nullrev, short
39 from mercurial.node import nullid, nullrev, short
40 from mercurial.i18n import _
40 from mercurial.i18n import _
41
41
42 cmdtable = {}
42 cmdtable = {}
43 command = cmdutil.command(cmdtable)
43 command = cmdutil.command(cmdtable)
44 testedwith = 'internal'
44 testedwith = 'internal'
45
45
46 @command('debug-diff-tree',
46 @command('debug-diff-tree',
47 [('p', 'patch', None, _('generate patch')),
47 [('p', 'patch', None, _('generate patch')),
48 ('r', 'recursive', None, _('recursive')),
48 ('r', 'recursive', None, _('recursive')),
49 ('P', 'pretty', None, _('pretty')),
49 ('P', 'pretty', None, _('pretty')),
50 ('s', 'stdin', None, _('stdin')),
50 ('s', 'stdin', None, _('stdin')),
51 ('C', 'copy', None, _('detect copies')),
51 ('C', 'copy', None, _('detect copies')),
52 ('S', 'search', "", _('search'))],
52 ('S', 'search', "", _('search'))],
53 ('[OPTION]... NODE1 NODE2 [FILE]...'),
53 ('[OPTION]... NODE1 NODE2 [FILE]...'),
54 inferrepo=True)
54 inferrepo=True)
55 def difftree(ui, repo, node1=None, node2=None, *files, **opts):
55 def difftree(ui, repo, node1=None, node2=None, *files, **opts):
56 """diff trees from two commits"""
56 """diff trees from two commits"""
57 def __difftree(repo, node1, node2, files=[]):
57 def __difftree(repo, node1, node2, files=[]):
58 assert node2 is not None
58 assert node2 is not None
59 mmap = repo[node1].manifest()
59 mmap = repo[node1].manifest()
60 mmap2 = repo[node2].manifest()
60 mmap2 = repo[node2].manifest()
61 m = scmutil.match(repo[node1], files)
61 m = scmutil.match(repo[node1], files)
62 modified, added, removed = repo.status(node1, node2, m)[:3]
62 modified, added, removed = repo.status(node1, node2, m)[:3]
63 empty = short(nullid)
63 empty = short(nullid)
64
64
65 for f in modified:
65 for f in modified:
66 # TODO get file permissions
66 # TODO get file permissions
67 ui.write(":100664 100664 %s %s M\t%s\t%s\n" %
67 ui.write(":100664 100664 %s %s M\t%s\t%s\n" %
68 (short(mmap[f]), short(mmap2[f]), f, f))
68 (short(mmap[f]), short(mmap2[f]), f, f))
69 for f in added:
69 for f in added:
70 ui.write(":000000 100664 %s %s N\t%s\t%s\n" %
70 ui.write(":000000 100664 %s %s N\t%s\t%s\n" %
71 (empty, short(mmap2[f]), f, f))
71 (empty, short(mmap2[f]), f, f))
72 for f in removed:
72 for f in removed:
73 ui.write(":100664 000000 %s %s D\t%s\t%s\n" %
73 ui.write(":100664 000000 %s %s D\t%s\t%s\n" %
74 (short(mmap[f]), empty, f, f))
74 (short(mmap[f]), empty, f, f))
75 ##
75 ##
76
76
77 while True:
77 while True:
78 if opts['stdin']:
78 if opts['stdin']:
79 try:
79 try:
80 line = raw_input().split(' ')
80 line = raw_input().split(' ')
81 node1 = line[0]
81 node1 = line[0]
82 if len(line) > 1:
82 if len(line) > 1:
83 node2 = line[1]
83 node2 = line[1]
84 else:
84 else:
85 node2 = None
85 node2 = None
86 except EOFError:
86 except EOFError:
87 break
87 break
88 node1 = repo.lookup(node1)
88 node1 = repo.lookup(node1)
89 if node2:
89 if node2:
90 node2 = repo.lookup(node2)
90 node2 = repo.lookup(node2)
91 else:
91 else:
92 node2 = node1
92 node2 = node1
93 node1 = repo.changelog.parents(node1)[0]
93 node1 = repo.changelog.parents(node1)[0]
94 if opts['patch']:
94 if opts['patch']:
95 if opts['pretty']:
95 if opts['pretty']:
96 catcommit(ui, repo, node2, "")
96 catcommit(ui, repo, node2, "")
97 m = scmutil.match(repo[node1], files)
97 m = scmutil.match(repo[node1], files)
98 diffopts = patch.difffeatureopts(ui)
98 diffopts = patch.difffeatureopts(ui)
99 diffopts.git = True
99 diffopts.git = True
100 chunks = patch.diff(repo, node1, node2, match=m,
100 chunks = patch.diff(repo, node1, node2, match=m,
101 opts=diffopts)
101 opts=diffopts)
102 for chunk in chunks:
102 for chunk in chunks:
103 ui.write(chunk)
103 ui.write(chunk)
104 else:
104 else:
105 __difftree(repo, node1, node2, files=files)
105 __difftree(repo, node1, node2, files=files)
106 if not opts['stdin']:
106 if not opts['stdin']:
107 break
107 break
108
108
109 def catcommit(ui, repo, n, prefix, ctx=None):
109 def catcommit(ui, repo, n, prefix, ctx=None):
110 nlprefix = '\n' + prefix
110 nlprefix = '\n' + prefix
111 if ctx is None:
111 if ctx is None:
112 ctx = repo[n]
112 ctx = repo[n]
113 # use ctx.node() instead ??
113 # use ctx.node() instead ??
114 ui.write(("tree %s\n" % short(ctx.changeset()[0])))
114 ui.write(("tree %s\n" % short(ctx.changeset()[0])))
115 for p in ctx.parents():
115 for p in ctx.parents():
116 ui.write(("parent %s\n" % p))
116 ui.write(("parent %s\n" % p))
117
117
118 date = ctx.date()
118 date = ctx.date()
119 description = ctx.description().replace("\0", "")
119 description = ctx.description().replace("\0", "")
120 lines = description.splitlines()
120 ui.write(("author %s %s %s\n" % (ctx.user(), int(date[0]), date[1])))
121 if lines and lines[-1].startswith('committer:'):
122 committer = lines[-1].split(': ')[1].rstrip()
123 else:
124 committer = ""
125
121
126 ui.write(("author %s %s %s\n" % (ctx.user(), int(date[0]), date[1])))
122 if 'committer' in ctx.extra():
127 if committer != '':
123 ui.write(("committer %s\n" % ctx.extra()['committer']))
128 ui.write(("committer %s %s %s\n" % (committer, int(date[0]), date[1])))
124
129 ui.write(("revision %d\n" % ctx.rev()))
125 ui.write(("revision %d\n" % ctx.rev()))
130 ui.write(("branch %s\n" % ctx.branch()))
126 ui.write(("branch %s\n" % ctx.branch()))
131 if obsolete.isenabled(repo, obsolete.createmarkersopt):
127 if obsolete.isenabled(repo, obsolete.createmarkersopt):
132 if ctx.obsolete():
128 if ctx.obsolete():
133 ui.write(("obsolete\n"))
129 ui.write(("obsolete\n"))
134 ui.write(("phase %s\n\n" % ctx.phasestr()))
130 ui.write(("phase %s\n\n" % ctx.phasestr()))
135
131
136 if prefix != "":
132 if prefix != "":
137 ui.write("%s%s\n" % (prefix,
133 ui.write("%s%s\n" % (prefix,
138 description.replace('\n', nlprefix).strip()))
134 description.replace('\n', nlprefix).strip()))
139 else:
135 else:
140 ui.write(description + "\n")
136 ui.write(description + "\n")
141 if prefix:
137 if prefix:
142 ui.write('\0')
138 ui.write('\0')
143
139
144 @command('debug-merge-base', [], _('REV REV'))
140 @command('debug-merge-base', [], _('REV REV'))
145 def base(ui, repo, node1, node2):
141 def base(ui, repo, node1, node2):
146 """output common ancestor information"""
142 """output common ancestor information"""
147 node1 = repo.lookup(node1)
143 node1 = repo.lookup(node1)
148 node2 = repo.lookup(node2)
144 node2 = repo.lookup(node2)
149 n = repo.changelog.ancestor(node1, node2)
145 n = repo.changelog.ancestor(node1, node2)
150 ui.write(short(n) + "\n")
146 ui.write(short(n) + "\n")
151
147
152 @command('debug-cat-file',
148 @command('debug-cat-file',
153 [('s', 'stdin', None, _('stdin'))],
149 [('s', 'stdin', None, _('stdin'))],
154 _('[OPTION]... TYPE FILE'),
150 _('[OPTION]... TYPE FILE'),
155 inferrepo=True)
151 inferrepo=True)
156 def catfile(ui, repo, type=None, r=None, **opts):
152 def catfile(ui, repo, type=None, r=None, **opts):
157 """cat a specific revision"""
153 """cat a specific revision"""
158 # in stdin mode, every line except the commit is prefixed with two
154 # in stdin mode, every line except the commit is prefixed with two
159 # spaces. This way the our caller can find the commit without magic
155 # spaces. This way the our caller can find the commit without magic
160 # strings
156 # strings
161 #
157 #
162 prefix = ""
158 prefix = ""
163 if opts['stdin']:
159 if opts['stdin']:
164 try:
160 try:
165 (type, r) = raw_input().split(' ')
161 (type, r) = raw_input().split(' ')
166 prefix = " "
162 prefix = " "
167 except EOFError:
163 except EOFError:
168 return
164 return
169
165
170 else:
166 else:
171 if not type or not r:
167 if not type or not r:
172 ui.warn(_("cat-file: type or revision not supplied\n"))
168 ui.warn(_("cat-file: type or revision not supplied\n"))
173 commands.help_(ui, 'cat-file')
169 commands.help_(ui, 'cat-file')
174
170
175 while r:
171 while r:
176 if type != "commit":
172 if type != "commit":
177 ui.warn(_("aborting hg cat-file only understands commits\n"))
173 ui.warn(_("aborting hg cat-file only understands commits\n"))
178 return 1
174 return 1
179 n = repo.lookup(r)
175 n = repo.lookup(r)
180 catcommit(ui, repo, n, prefix)
176 catcommit(ui, repo, n, prefix)
181 if opts['stdin']:
177 if opts['stdin']:
182 try:
178 try:
183 (type, r) = raw_input().split(' ')
179 (type, r) = raw_input().split(' ')
184 except EOFError:
180 except EOFError:
185 break
181 break
186 else:
182 else:
187 break
183 break
188
184
189 # git rev-tree is a confusing thing. You can supply a number of
185 # git rev-tree is a confusing thing. You can supply a number of
190 # commit sha1s on the command line, and it walks the commit history
186 # commit sha1s on the command line, and it walks the commit history
191 # telling you which commits are reachable from the supplied ones via
187 # telling you which commits are reachable from the supplied ones via
192 # a bitmask based on arg position.
188 # a bitmask based on arg position.
193 # you can specify a commit to stop at by starting the sha1 with ^
189 # you can specify a commit to stop at by starting the sha1 with ^
194 def revtree(ui, args, repo, full="tree", maxnr=0, parents=False):
190 def revtree(ui, args, repo, full="tree", maxnr=0, parents=False):
195 def chlogwalk():
191 def chlogwalk():
196 count = len(repo)
192 count = len(repo)
197 i = count
193 i = count
198 l = [0] * 100
194 l = [0] * 100
199 chunk = 100
195 chunk = 100
200 while True:
196 while True:
201 if chunk > i:
197 if chunk > i:
202 chunk = i
198 chunk = i
203 i = 0
199 i = 0
204 else:
200 else:
205 i -= chunk
201 i -= chunk
206
202
207 for x in xrange(chunk):
203 for x in xrange(chunk):
208 if i + x >= count:
204 if i + x >= count:
209 l[chunk - x:] = [0] * (chunk - x)
205 l[chunk - x:] = [0] * (chunk - x)
210 break
206 break
211 if full is not None:
207 if full is not None:
212 if (i + x) in repo:
208 if (i + x) in repo:
213 l[x] = repo[i + x]
209 l[x] = repo[i + x]
214 l[x].changeset() # force reading
210 l[x].changeset() # force reading
215 else:
211 else:
216 if (i + x) in repo:
212 if (i + x) in repo:
217 l[x] = 1
213 l[x] = 1
218 for x in xrange(chunk - 1, -1, -1):
214 for x in xrange(chunk - 1, -1, -1):
219 if l[x] != 0:
215 if l[x] != 0:
220 yield (i + x, full is not None and l[x] or None)
216 yield (i + x, full is not None and l[x] or None)
221 if i == 0:
217 if i == 0:
222 break
218 break
223
219
224 # calculate and return the reachability bitmask for sha
220 # calculate and return the reachability bitmask for sha
225 def is_reachable(ar, reachable, sha):
221 def is_reachable(ar, reachable, sha):
226 if len(ar) == 0:
222 if len(ar) == 0:
227 return 1
223 return 1
228 mask = 0
224 mask = 0
229 for i in xrange(len(ar)):
225 for i in xrange(len(ar)):
230 if sha in reachable[i]:
226 if sha in reachable[i]:
231 mask |= 1 << i
227 mask |= 1 << i
232
228
233 return mask
229 return mask
234
230
235 reachable = []
231 reachable = []
236 stop_sha1 = []
232 stop_sha1 = []
237 want_sha1 = []
233 want_sha1 = []
238 count = 0
234 count = 0
239
235
240 # figure out which commits they are asking for and which ones they
236 # figure out which commits they are asking for and which ones they
241 # want us to stop on
237 # want us to stop on
242 for i, arg in enumerate(args):
238 for i, arg in enumerate(args):
243 if arg.startswith('^'):
239 if arg.startswith('^'):
244 s = repo.lookup(arg[1:])
240 s = repo.lookup(arg[1:])
245 stop_sha1.append(s)
241 stop_sha1.append(s)
246 want_sha1.append(s)
242 want_sha1.append(s)
247 elif arg != 'HEAD':
243 elif arg != 'HEAD':
248 want_sha1.append(repo.lookup(arg))
244 want_sha1.append(repo.lookup(arg))
249
245
250 # calculate the graph for the supplied commits
246 # calculate the graph for the supplied commits
251 for i, n in enumerate(want_sha1):
247 for i, n in enumerate(want_sha1):
252 reachable.append(set())
248 reachable.append(set())
253 visit = [n]
249 visit = [n]
254 reachable[i].add(n)
250 reachable[i].add(n)
255 while visit:
251 while visit:
256 n = visit.pop(0)
252 n = visit.pop(0)
257 if n in stop_sha1:
253 if n in stop_sha1:
258 continue
254 continue
259 for p in repo.changelog.parents(n):
255 for p in repo.changelog.parents(n):
260 if p not in reachable[i]:
256 if p not in reachable[i]:
261 reachable[i].add(p)
257 reachable[i].add(p)
262 visit.append(p)
258 visit.append(p)
263 if p in stop_sha1:
259 if p in stop_sha1:
264 continue
260 continue
265
261
266 # walk the repository looking for commits that are in our
262 # walk the repository looking for commits that are in our
267 # reachability graph
263 # reachability graph
268 for i, ctx in chlogwalk():
264 for i, ctx in chlogwalk():
269 if i not in repo:
265 if i not in repo:
270 continue
266 continue
271 n = repo.changelog.node(i)
267 n = repo.changelog.node(i)
272 mask = is_reachable(want_sha1, reachable, n)
268 mask = is_reachable(want_sha1, reachable, n)
273 if mask:
269 if mask:
274 parentstr = ""
270 parentstr = ""
275 if parents:
271 if parents:
276 pp = repo.changelog.parents(n)
272 pp = repo.changelog.parents(n)
277 if pp[0] != nullid:
273 if pp[0] != nullid:
278 parentstr += " " + short(pp[0])
274 parentstr += " " + short(pp[0])
279 if pp[1] != nullid:
275 if pp[1] != nullid:
280 parentstr += " " + short(pp[1])
276 parentstr += " " + short(pp[1])
281 if not full:
277 if not full:
282 ui.write("%s%s\n" % (short(n), parentstr))
278 ui.write("%s%s\n" % (short(n), parentstr))
283 elif full == "commit":
279 elif full == "commit":
284 ui.write("%s%s\n" % (short(n), parentstr))
280 ui.write("%s%s\n" % (short(n), parentstr))
285 catcommit(ui, repo, n, ' ', ctx)
281 catcommit(ui, repo, n, ' ', ctx)
286 else:
282 else:
287 (p1, p2) = repo.changelog.parents(n)
283 (p1, p2) = repo.changelog.parents(n)
288 (h, h1, h2) = map(short, (n, p1, p2))
284 (h, h1, h2) = map(short, (n, p1, p2))
289 (i1, i2) = map(repo.changelog.rev, (p1, p2))
285 (i1, i2) = map(repo.changelog.rev, (p1, p2))
290
286
291 date = ctx.date()[0]
287 date = ctx.date()[0]
292 ui.write("%s %s:%s" % (date, h, mask))
288 ui.write("%s %s:%s" % (date, h, mask))
293 mask = is_reachable(want_sha1, reachable, p1)
289 mask = is_reachable(want_sha1, reachable, p1)
294 if i1 != nullrev and mask > 0:
290 if i1 != nullrev and mask > 0:
295 ui.write("%s:%s " % (h1, mask)),
291 ui.write("%s:%s " % (h1, mask)),
296 mask = is_reachable(want_sha1, reachable, p2)
292 mask = is_reachable(want_sha1, reachable, p2)
297 if i2 != nullrev and mask > 0:
293 if i2 != nullrev and mask > 0:
298 ui.write("%s:%s " % (h2, mask))
294 ui.write("%s:%s " % (h2, mask))
299 ui.write("\n")
295 ui.write("\n")
300 if maxnr and count >= maxnr:
296 if maxnr and count >= maxnr:
301 break
297 break
302 count += 1
298 count += 1
303
299
304 # git rev-list tries to order things by date, and has the ability to stop
300 # git rev-list tries to order things by date, and has the ability to stop
305 # at a given commit without walking the whole repo. TODO add the stop
301 # at a given commit without walking the whole repo. TODO add the stop
306 # parameter
302 # parameter
307 @command('debug-rev-list',
303 @command('debug-rev-list',
308 [('H', 'header', None, _('header')),
304 [('H', 'header', None, _('header')),
309 ('t', 'topo-order', None, _('topo-order')),
305 ('t', 'topo-order', None, _('topo-order')),
310 ('p', 'parents', None, _('parents')),
306 ('p', 'parents', None, _('parents')),
311 ('n', 'max-count', 0, _('max-count'))],
307 ('n', 'max-count', 0, _('max-count'))],
312 ('[OPTION]... REV...'))
308 ('[OPTION]... REV...'))
313 def revlist(ui, repo, *revs, **opts):
309 def revlist(ui, repo, *revs, **opts):
314 """print revisions"""
310 """print revisions"""
315 if opts['header']:
311 if opts['header']:
316 full = "commit"
312 full = "commit"
317 else:
313 else:
318 full = None
314 full = None
319 copy = [x for x in revs]
315 copy = [x for x in revs]
320 revtree(ui, copy, repo, full, opts['max_count'], opts['parents'])
316 revtree(ui, copy, repo, full, opts['max_count'], opts['parents'])
321
317
322 @command('view',
318 @command('view',
323 [('l', 'limit', '',
319 [('l', 'limit', '',
324 _('limit number of changes displayed'), _('NUM'))],
320 _('limit number of changes displayed'), _('NUM'))],
325 _('[-l LIMIT] [REVRANGE]'))
321 _('[-l LIMIT] [REVRANGE]'))
326 def view(ui, repo, *etc, **opts):
322 def view(ui, repo, *etc, **opts):
327 "start interactive history viewer"
323 "start interactive history viewer"
328 os.chdir(repo.root)
324 os.chdir(repo.root)
329 optstr = ' '.join(['--%s %s' % (k, v) for k, v in opts.iteritems() if v])
325 optstr = ' '.join(['--%s %s' % (k, v) for k, v in opts.iteritems() if v])
330 if repo.filtername is None:
326 if repo.filtername is None:
331 optstr += '--hidden'
327 optstr += '--hidden'
332
328
333 cmd = ui.config("hgk", "path", "hgk") + " %s %s" % (optstr, " ".join(etc))
329 cmd = ui.config("hgk", "path", "hgk") + " %s %s" % (optstr, " ".join(etc))
334 ui.debug("running %s\n" % cmd)
330 ui.debug("running %s\n" % cmd)
335 ui.system(cmd)
331 ui.system(cmd)
General Comments 0
You need to be logged in to leave comments. Login now