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