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