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