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