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