1 # git-gui revision chooser
2 # Copyright (C) 2006, 2007 Shawn Pearce
6 image create
photo ::choose_rev::img_find -data {R0lGODlhEAAQAIYAAPwCBCQmJDw
+PBQSFAQCBMza3NTm5MTW1HyChOT29Ozq7MTq7Kze5Kzm7Oz6
/NTy9Iza5GzGzKzS1Nzy9Nz29Kzq9HTGzHTK1Lza3AwKDLzu9JTi7HTW5GTCzITO1Mzq7Hza5FTK1ESyvHzKzKzW3DQyNDyqtDw6PIzW5HzGzAT
+/Dw
+RKyurNTOzMTGxMS
+tJSGdATCxHRydLSqpLymnLSijBweHERCRNze3Pz69PTy9Oze1OTSxOTGrMSqlLy
+vPTu5OzSvMymjNTGvNS
+tMy2pMyunMSefAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAe4gACCAAECA4OIiAIEBQYHBAKJgwIICQoLDA0IkZIECQ4PCxARCwSSAxITFA8VEBYXGBmJAQYLGhUbHB0eH7KIGRIMEBAgISIjJKaIJQQLFxERIialkieUGigpKRoIBCqJKyyLBwvJAioEyoICLS4v6QQwMQQyLuqLli8zNDU2BCf1lN3AkUPHDh49fAQAAEnGD1MCCALZEaSHkIUMBQS8wWMIkSJGhBzBmFEGgRsBUqpMiSgdAD
+BAAAh
/mhDcmVhdGVkIGJ5IEJNUFRvR0lGIFBybyB2ZXJzaW9uIDIuNQ0KqSBEZXZlbENvciAxOTk3LDE5OTguIEFsbCByaWdodHMgcmVzZXJ2ZWQuDQpodHRwOi8vd3d3LmRldmVsY29yLmNvbQA7
}
8 field w
; # our megawidget path
9 field w_list
; # list of currently filtered specs
10 field w_filter
; # filter entry for $w_list
12 field c_expr
{}; # current revision expression
13 field filter
; # current filter string
14 field revtype head
; # type of revision chosen
15 field cur_specs
[list]; # list of specs for $revtype
16 field spec_head
; # list of all head specs
17 field spec_trck
; # list of all tracking branch specs
18 field spec_tag
; # list of all tag specs
19 field tip_data
; # array of tip commit info by refname
20 field log_last
; # array of reflog date by refname
22 field tooltip_wm
{} ; # Current tooltip toplevel, if open
23 field tooltip_t
{} ; # Text widget in $tooltip_wm
24 field tooltip_timer
{} ; # Current timer event for our tooltip
26 proc new
{path
{title
{}}} {
27 return [_new
$path 0 $title]
30 proc new_unmerged
{path
{title
{}}} {
31 return [_new
$path 1 $title]
34 constructor _new
{path unmerged_only title
} {
35 global current_branch is_detached
37 if {![info exists
::all_remotes]} {
44 labelframe $w -text $title
48 bind $w <Destroy
> [cb _delete
%W
]
51 radiobutton $w.detachedhead_r
\
53 -text {This Detached Checkout
} \
56 grid $w.detachedhead_r
-sticky we
-padx {0 5} -columnspan 2
59 radiobutton $w.expr_r
\
60 -text {Revision Expression
:} \
67 -textvariable @c_expr
\
69 -validatecommand [cb _validate
%d
%S
]
70 grid $w.expr_r
$w.expr_t
-sticky we
-padx {0 5}
73 radiobutton $w.types.head_r
\
74 -text {Local Branch
} \
77 pack $w.types.head_r
-side left
78 radiobutton $w.types.trck_r
\
79 -text {Tracking Branch
} \
82 pack $w.types.trck_r
-side left
83 radiobutton $w.types.tag_r
\
87 pack $w.types.tag_r
-side left
88 set w_filter
$w.types.filter
93 -textvariable @filter
\
95 -validatecommand [cb _filter
%P
]
96 pack $w_filter -side right
97 pack [label $w.types.filter_icon
\
98 -image ::choose_rev::img_find \
100 grid $w.types
-sticky we
-padx {0 5} -columnspan 2
109 -exportselection false
\
110 -xscrollcommand [cb _sb_set
$w.
list.sbx h
] \
111 -yscrollcommand [cb _sb_set
$w.
list.sby v
]
112 pack $w_list -fill both
-expand 1
113 grid $w.
list -sticky nswe
-padx {20 5} -columnspan 2
114 bind $w_list <Any-Motion
> [cb _show_tooltip
@%x
,%y
]
115 bind $w_list <Any-Enter
> [cb _hide_tooltip
]
116 bind $w_list <Any-Leave
> [cb _hide_tooltip
]
117 bind $w_list <Destroy
> [cb _hide_tooltip
]
119 grid columnconfigure
$w 1 -weight 1
121 grid rowconfigure
$w 3 -weight 1
123 grid rowconfigure
$w 2 -weight 1
126 trace add
variable @revtype write
[cb _select
]
127 bind $w_filter <Key-Return
> [list focus $w_list]\;break
128 bind $w_filter <Key-Down
> [list focus $w_list]
131 append fmt
{ %(refname
)}
133 append fmt
{ %(objecttype
)}
134 append fmt
{ %(objectname
)}
135 append fmt
{ [concat %(taggername
) %(authorname
)]}
136 append fmt
{ [concat %(taggerdate
) %(authordate
)]}
137 append fmt
{ %(subject
)}
139 append fmt
{ %(*objecttype
)}
140 append fmt
{ %(*objectname
)}
141 append fmt
{ %(*authorname
)}
142 append fmt
{ %(*authordate
)}
143 append fmt
{ %(*subject
)}
146 set fr_fd
[git_read for-each-ref
\
154 fconfigure $fr_fd -translation lf
-encoding utf-8
155 while {[gets $fr_fd line
] > 0} {
156 set line
[eval $line]
157 if {[lindex $line 1 0] eq
{tag
}} {
158 if {[lindex $line 2 0] eq
{commit
}} {
159 set sha1
[lindex $line 2 1]
163 } elseif
{[lindex $line 1 0] eq
{commit
}} {
164 set sha1
[lindex $line 1 1]
168 set refn
[lindex $line 0]
169 set tip_data
($refn) [lrange $line 1 end
]
170 lappend cmt_refn
($sha1) $refn
171 lappend all_refn
$refn
175 if {$unmerged_only} {
176 set fr_fd
[git_read rev-list
--all ^
$::HEAD]
177 while {[gets $fr_fd sha1
] > 0} {
178 if {[catch {set rlst
$cmt_refn($sha1)}]} continue
185 foreach refn
$all_refn {
191 foreach name
[load_all_heads
] {
192 set refn refs
/heads
/$name
193 if {[info exists inc
($refn)]} {
194 lappend spec_head
[list $name $refn]
199 foreach spec
[all_tracking_branches
] {
200 set refn
[lindex $spec 0]
201 if {[info exists inc
($refn)]} {
202 regsub ^refs
/(heads|remotes
)/ $refn {} name
203 lappend spec_trck
[concat $name $spec]
208 foreach name
[load_all_tags
] {
209 set refn refs
/tags
/$name
210 if {[info exists inc
($refn)]} {
211 lappend spec_tag
[list $name $refn]
215 if {$is_detached} { set revtype HEAD
216 } elseif
{[llength $spec_head] > 0} { set revtype head
217 } elseif
{[llength $spec_trck] > 0} { set revtype trck
218 } elseif
{[llength $spec_tag ] > 0} { set revtype tag
219 } else { set revtype
expr
222 if {$revtype eq
{head
} && $current_branch ne
{}} {
224 foreach spec
$spec_head {
225 if {[lindex $spec 0] eq
$current_branch} {
226 $w_list selection clear
0 end
227 $w_list selection set $i
238 if {![winfo exists
$w.none_r
]} {
239 radiobutton $w.none_r
\
243 grid $w.none_r
-sticky we
-padx {0 5} -columnspan 2
245 $w.none_r configure
-text $text
253 set i
[$w_list curselection
]
255 return [lindex $cur_specs $i 0]
262 expr { return $c_expr }
264 default { error "unknown type of revision" }
268 method pick_tracking_branch
{} {
272 method focus_filter
{} {
273 if {[$w_filter cget
-state] eq
{normal
}} {
278 method bind_listbox
{event script
} {
279 bind $w_list $event $script
282 method get_local_branch
{} {
283 if {$revtype eq
{head
}} {
290 method get_tracking_branch
{} {
291 set i
[$w_list curselection
]
292 if {$i eq
{} ||
$revtype ne
{trck
}} {
295 return [lrange [lindex $cur_specs $i] 1 end
]
298 method get_commit
{} {
303 return [git rev-parse
--verify "$e^0"]
306 method commit_or_die
{} {
307 if {[catch {set new
[get_commit
$this]} err
]} {
309 # Cleanup the not-so-friendly error from rev-parse.
311 regsub {^fatal
:\s
*} $err {} err
312 if {$err eq
{Needed a single revision
}} {
316 set top
[winfo toplevel $w]
317 set msg
"Invalid revision: [get $this]\n\n$err"
321 -title [wm title
$top] \
334 set i
[$w_list curselection
]
336 return [lindex $cur_specs $i 1]
338 error "No revision selected."
346 error "Revision expression is empty."
351 default { error "unknown type of revision" }
355 method _validate
{d S
} {
357 if {[regexp {\s
} $S]} {
360 if {[string length
$S] > 0} {
368 if {[regexp {\s
} $P]} {
375 method _select
{args
} {
376 _rebuild
$this $filter
380 method _rebuild
{pat
} {
383 head
{ set new
$spec_head }
384 trck
{ set new
$spec_trck }
385 tag
{ set new
$spec_tag }
394 if {[$w_list cget
-state] eq
{disabled
}} {
395 $w_list configure
-state normal
404 set txt
[lindex $spec 0]
405 if {$pat eq
{} ||
[string match
$pat $txt]} {
406 lappend cur_specs
$spec
407 $w_list insert end
$txt
410 if {$cur_specs ne
{}} {
411 $w_list selection clear
0 end
412 $w_list selection set 0
415 if {[$w_filter cget
-state] ne
$ste} {
416 $w_list configure
-state $ste
417 $w_filter configure
-state $ste
421 method _delete
{current
} {
422 if {$current eq
$w} {
427 method _sb_set
{sb orient first last
} {
428 set old_focus
[focus -lastfor $w]
430 if {$first == 0 && $last == 1} {
431 if {[winfo exists
$sb]} {
433 if {$old_focus ne
{}} {
441 if {![winfo exists
$sb]} {
442 if {$orient eq
{h
}} {
443 scrollbar $sb -orient h
-command [list $w_list xview
]
444 pack $sb -fill x
-side bottom
-before $w_list
446 scrollbar $sb -orient v
-command [list $w_list yview
]
447 pack $sb -fill y
-side right
-before $w_list
449 if {$old_focus ne
{}} {
457 method _show_tooltip
{pos
} {
458 if {$tooltip_wm ne
{}} {
460 } elseif
{$tooltip_timer eq
{}} {
461 set tooltip_timer
[after 1000 [cb _open_tooltip
]]
465 method _open_tooltip
{} {
469 set pos_x
[winfo pointerx
$w_list]
470 set pos_y
[winfo pointery
$w_list]
471 if {[winfo containing
$pos_x $pos_y] ne
$w_list} {
476 set pos
@[join [list \
477 [expr {$pos_x - [winfo rootx
$w_list]}] \
478 [expr {$pos_y - [winfo rooty
$w_list]}]] ,]
479 set lno
[$w_list index
$pos]
485 set spec
[lindex $cur_specs $lno]
486 set refn
[lindex $spec 1]
492 if {$tooltip_wm eq
{}} {
493 set tooltip_wm
[toplevel $w_list.tooltip
-borderwidth 1]
494 wm overrideredirect
$tooltip_wm 1
495 wm transient
$tooltip_wm [winfo toplevel $w_list]
496 set tooltip_t
$tooltip_wm.
label
499 -highlightthickness 0 \
503 -background lightyellow
\
505 $tooltip_t tag conf section_header
-font font_uibold
506 bind $tooltip_wm <Escape
> [cb _hide_tooltip
]
509 $tooltip_t conf
-state normal
510 $tooltip_t delete
0.0 end
513 set data
$tip_data($refn)
514 if {[lindex $data 0 0] eq
{tag
}} {
515 set tag
[lindex $data 0]
516 if {[lindex $data 1 0] eq
{commit
}} {
517 set cmit
[lindex $data 1]
521 } elseif
{[lindex $data 0 0] eq
{commit
}} {
523 set cmit
[lindex $data 0]
526 $tooltip_t insert end
[lindex $spec 0]
527 set last
[_reflog_last
$this [lindex $spec 1]]
529 $tooltip_t insert end
"\n"
530 $tooltip_t insert end
"updated"
531 $tooltip_t insert end
" $last"
533 $tooltip_t insert end
"\n"
536 $tooltip_t insert end
"\n"
537 $tooltip_t insert end
"tag" section_header
538 $tooltip_t insert end
" [lindex $tag 1]\n"
539 $tooltip_t insert end
[lindex $tag 2]
540 $tooltip_t insert end
" ([lindex $tag 3])\n"
541 $tooltip_t insert end
[lindex $tag 4]
542 $tooltip_t insert end
"\n"
546 $tooltip_t insert end
"\n"
547 $tooltip_t insert end
"commit" section_header
548 $tooltip_t insert end
" [lindex $cmit 1]\n"
549 $tooltip_t insert end
[lindex $cmit 2]
550 $tooltip_t insert end
" ([lindex $cmit 3])\n"
551 $tooltip_t insert end
[lindex $cmit 4]
554 if {[llength $spec] > 2} {
555 $tooltip_t insert end
"\n"
556 $tooltip_t insert end
"remote" section_header
557 $tooltip_t insert end
" [lindex $spec 2]\n"
558 $tooltip_t insert end
"url"
559 $tooltip_t insert end
" $remote_url([lindex $spec 2])\n"
560 $tooltip_t insert end
"branch"
561 $tooltip_t insert end
" [lindex $spec 3]"
564 $tooltip_t conf
-state disabled
565 _position_tooltip
$this
568 method _reflog_last
{name
} {
569 if {[info exists reflog_last
($name)]} {
570 return reflog_last
($name)
574 if {[catch {set last
[file mtime
[gitdir
$name]]}]
575 && ![catch {set g
[open [gitdir logs
$name] r
]}]} {
576 fconfigure $g -translation binary
577 while {[gets $g line
] >= 0} {
578 if {[regexp {> ([1-9][0-9]*) } $line line when
]} {
586 set last
[clock format $last -format {%a
%b
%e
%H
:%M
:%S
%Y
}]
588 set reflog_last
($name) $last
592 method _position_tooltip
{} {
593 set max_h
[lindex [split [$tooltip_t index end
] .
] 0]
595 for {set i
1} {$i <= $max_h} {incr i
} {
596 set c
[lindex [split [$tooltip_t index
"$i.0 lineend"] .
] 1]
597 if {$c > $max_w} {set max_w
$c}
599 $tooltip_t conf
-width $max_w -height $max_h
601 set req_w
[winfo reqwidth
$tooltip_t]
602 set req_h
[winfo reqheight
$tooltip_t]
603 set pos_x
[expr {[winfo pointerx .
] + 5}]
604 set pos_y
[expr {[winfo pointery .
] + 10}]
606 set g
"${req_w}x${req_h}"
607 if {$pos_x >= 0} {append g
+}
609 if {$pos_y >= 0} {append g
+}
612 wm geometry
$tooltip_wm $g
616 method _hide_tooltip
{} {
617 if {$tooltip_wm ne
{}} {
621 if {$tooltip_timer ne
{}} {
622 after cancel
$tooltip_timer