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