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