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