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 use_ttk NS
37 if {![info exists
::all_remotes]} {
44 ${NS
}::labelframe $w -text $title
48 bind $w <Destroy
> [cb _delete
%W
]
51 ${NS
}::radiobutton $w.detachedhead_r
\
52 -text [mc
"This Detached Checkout"] \
55 if {!$use_ttk} {$w.detachedhead_r configure
-anchor w
}
56 grid $w.detachedhead_r
-sticky we
-padx {0 5} -columnspan 2
59 ${NS
}::radiobutton $w.expr_r
\
60 -text [mc
"Revision Expression:"] \
63 ${NS
}::entry $w.expr_t
\
65 -textvariable @c_expr
\
67 -validatecommand [cb _validate
%d
%S
]
68 grid $w.expr_r
$w.expr_t
-sticky we
-padx {0 5}
71 ${NS
}::radiobutton $w.types.head_r
\
72 -text [mc
"Local Branch"] \
75 pack $w.types.head_r
-side left
76 ${NS
}::radiobutton $w.types.trck_r
\
77 -text [mc
"Tracking Branch"] \
80 pack $w.types.trck_r
-side left
81 ${NS
}::radiobutton $w.types.tag_r
\
85 pack $w.types.tag_r
-side left
86 set w_filter
$w.types.filter
87 ${NS
}::entry $w_filter \
89 -textvariable @filter
\
91 -validatecommand [cb _filter
%P
]
92 pack $w_filter -side right
93 pack [${NS
}::label $w.types.filter_icon
\
94 -image ::choose_rev::img_find \
96 grid $w.types
-sticky we
-padx {0 5} -columnspan 2
99 ttk
::frame $w.
list -style SListbox.TFrame
-padding 2
109 -exportselection false
\
110 -xscrollcommand [cb _sb_set
$w.
list.sbx h
] \
111 -yscrollcommand [cb _sb_set
$w.
list.sby v
]
113 $w_list configure
-relief flat
-highlightthickness 0 -borderwidth 0
115 pack $w_list -fill both
-expand 1
116 grid $w.
list -sticky nswe
-padx {20 5} -columnspan 2
117 bind $w_list <Any-Motion
> [cb _show_tooltip
@%x
,%y
]
118 bind $w_list <Any-Enter
> [cb _hide_tooltip
]
119 bind $w_list <Any-Leave
> [cb _hide_tooltip
]
120 bind $w_list <Destroy
> [cb _hide_tooltip
]
122 grid columnconfigure
$w 1 -weight 1
124 grid rowconfigure
$w 3 -weight 1
126 grid rowconfigure
$w 2 -weight 1
129 trace add
variable @revtype write
[cb _select
]
130 bind $w_filter <Key-Return
> [list focus $w_list]\;break
131 bind $w_filter <Key-Down
> [list focus $w_list]
134 append fmt
{ %(refname
)}
136 append fmt
{ %(objecttype
)}
137 append fmt
{ %(objectname
)}
138 append fmt
{ [concat %(taggername
) %(authorname
)]}
139 append fmt
{ [reformat_date
[concat %(taggerdate
) %(authordate
)]]}
140 append fmt
{ %(subject
)}
142 append fmt
{ %(*objecttype
)}
143 append fmt
{ %(*objectname
)}
144 append fmt
{ %(*authorname
)}
145 append fmt
{ [reformat_date
%(*authordate
)]}
146 append fmt
{ %(*subject
)}
149 set fr_fd
[git_read for-each-ref
\
157 fconfigure $fr_fd -translation lf
-encoding utf-8
158 while {[gets $fr_fd line
] > 0} {
159 set line
[eval $line]
160 if {[lindex $line 1 0] eq
{tag
}} {
161 if {[lindex $line 2 0] eq
{commit
}} {
162 set sha1
[lindex $line 2 1]
166 } elseif
{[lindex $line 1 0] eq
{commit
}} {
167 set sha1
[lindex $line 1 1]
171 set refn
[lindex $line 0]
172 set tip_data
($refn) [lrange $line 1 end
]
173 lappend cmt_refn
($sha1) $refn
174 lappend all_refn
$refn
178 if {$unmerged_only} {
179 set fr_fd
[git_read rev-list
--all ^
$::HEAD]
180 while {[gets $fr_fd sha1
] > 0} {
181 if {[catch {set rlst
$cmt_refn($sha1)}]} continue
188 foreach refn
$all_refn {
194 foreach name
[load_all_heads
] {
195 set refn refs
/heads
/$name
196 if {[info exists inc
($refn)]} {
197 lappend spec_head
[list $name $refn]
202 foreach spec
[all_tracking_branches
] {
203 set refn
[lindex $spec 0]
204 if {[info exists inc
($refn)]} {
205 regsub ^refs
/(heads|remotes
)/ $refn {} name
206 lappend spec_trck
[concat $name $spec]
211 foreach name
[load_all_tags
] {
212 set refn refs
/tags
/$name
213 if {[info exists inc
($refn)]} {
214 lappend spec_tag
[list $name $refn]
218 if {$is_detached} { set revtype HEAD
219 } elseif
{[llength $spec_head] > 0} { set revtype head
220 } elseif
{[llength $spec_trck] > 0} { set revtype trck
221 } elseif
{[llength $spec_tag ] > 0} { set revtype tag
222 } else { set revtype
expr
225 if {$revtype eq
{head
} && $current_branch ne
{}} {
227 foreach spec
$spec_head {
228 if {[lindex $spec 0] eq
$current_branch} {
229 $w_list selection clear
0 end
230 $w_list selection set $i
242 if {![winfo exists
$w.none_r
]} {
243 ${NS
}::radiobutton $w.none_r
\
246 if {!$use_ttk} {$w.none_r configure
-anchor w
}
247 grid $w.none_r
-sticky we
-padx {0 5} -columnspan 2
249 $w.none_r configure
-text $text
257 set i
[$w_list curselection
]
259 return [lindex $cur_specs $i 0]
266 expr { return $c_expr }
268 default { error "unknown type of revision" }
272 method pick_tracking_branch
{} {
276 method focus_filter
{} {
277 if {[$w_filter cget
-state] eq
{normal
}} {
282 method bind_listbox
{event script
} {
283 bind $w_list $event $script
286 method get_local_branch
{} {
287 if {$revtype eq
{head
}} {
294 method get_tracking_branch
{} {
295 set i
[$w_list curselection
]
296 if {$i eq
{} ||
$revtype ne
{trck
}} {
299 return [lrange [lindex $cur_specs $i] 1 end
]
302 method get_commit
{} {
307 return [git rev-parse
--verify "$e^0"]
310 method commit_or_die
{} {
311 if {[catch {set new
[get_commit
$this]} err
]} {
313 # Cleanup the not-so-friendly error from rev-parse.
315 regsub {^fatal
:\s
*} $err {} err
316 if {$err eq
{Needed a single revision
}} {
320 set top
[winfo toplevel $w]
321 set msg
[strcat
[mc
"Invalid revision: %s" [get
$this]] "\n\n$err"]
325 -title [wm title
$top] \
338 set i
[$w_list curselection
]
340 return [lindex $cur_specs $i 1]
342 error [mc
"No revision selected."]
350 error [mc
"Revision expression is empty."]
355 default { error "unknown type of revision" }
359 method _validate
{d S
} {
361 if {[regexp {\s
} $S]} {
364 if {[string length
$S] > 0} {
372 if {[regexp {\s
} $P]} {
379 method _select
{args
} {
380 _rebuild
$this $filter
384 method _rebuild
{pat
} {
387 head
{ set new
$spec_head }
388 trck
{ set new
$spec_trck }
389 tag
{ set new
$spec_tag }
398 if {[$w_list cget
-state] eq
{disabled
}} {
399 $w_list configure
-state normal
408 set txt
[lindex $spec 0]
409 if {$pat eq
{} ||
[string match
$pat $txt]} {
410 lappend cur_specs
$spec
411 $w_list insert end
$txt
414 if {$cur_specs ne
{}} {
415 $w_list selection clear
0 end
416 $w_list selection set 0
419 if {[$w_filter cget
-state] ne
$ste} {
420 $w_list configure
-state $ste
421 $w_filter configure
-state $ste
425 method _delete
{current
} {
426 if {$current eq
$w} {
431 method _sb_set
{sb orient first last
} {
433 set old_focus
[focus -lastfor $w]
435 if {$first == 0 && $last == 1} {
436 if {[winfo exists
$sb]} {
438 if {$old_focus ne
{}} {
446 if {![winfo exists
$sb]} {
447 if {$orient eq
{h
}} {
448 ${NS
}::scrollbar $sb -orient h
-command [list $w_list xview
]
449 pack $sb -fill x
-side bottom
-before $w_list
451 ${NS
}::scrollbar $sb -orient v
-command [list $w_list yview
]
452 pack $sb -fill y
-side right
-before $w_list
454 if {$old_focus ne
{}} {
460 catch {$sb set $first $last}
463 method _show_tooltip
{pos
} {
464 if {$tooltip_wm ne
{}} {
466 } elseif
{$tooltip_timer eq
{}} {
467 set tooltip_timer
[after 1000 [cb _open_tooltip
]]
471 method _open_tooltip
{} {
475 set pos_x
[winfo pointerx
$w_list]
476 set pos_y
[winfo pointery
$w_list]
477 if {[winfo containing
$pos_x $pos_y] ne
$w_list} {
482 set pos
@[join [list \
483 [expr {$pos_x - [winfo rootx
$w_list]}] \
484 [expr {$pos_y - [winfo rooty
$w_list]}]] ,]
485 set lno
[$w_list index
$pos]
491 set spec
[lindex $cur_specs $lno]
492 set refn
[lindex $spec 1]
498 if {$tooltip_wm eq
{}} {
499 set tooltip_wm
[toplevel $w_list.tooltip
-borderwidth 1]
500 catch {wm attributes
$tooltip_wm -type tooltip
}
501 wm overrideredirect
$tooltip_wm 1
502 wm transient
$tooltip_wm [winfo toplevel $w_list]
503 set tooltip_t
$tooltip_wm.
label
506 -highlightthickness 0 \
510 -background lightyellow
\
512 $tooltip_t tag conf section_header
-font font_uibold
513 bind $tooltip_wm <Escape
> [cb _hide_tooltip
]
516 $tooltip_t conf
-state normal
517 $tooltip_t delete
0.0 end
520 set data
$tip_data($refn)
521 if {[lindex $data 0 0] eq
{tag
}} {
522 set tag
[lindex $data 0]
523 if {[lindex $data 1 0] eq
{commit
}} {
524 set cmit
[lindex $data 1]
528 } elseif
{[lindex $data 0 0] eq
{commit
}} {
530 set cmit
[lindex $data 0]
533 $tooltip_t insert end
[lindex $spec 0]
534 set last
[_reflog_last
$this [lindex $spec 1]]
536 $tooltip_t insert end
"\n"
537 $tooltip_t insert end
[mc
"Updated"]
538 $tooltip_t insert end
" $last"
540 $tooltip_t insert end
"\n"
543 $tooltip_t insert end
"\n"
544 $tooltip_t insert end
[mc
"Tag"] section_header
545 $tooltip_t insert end
" [lindex $tag 1]\n"
546 $tooltip_t insert end
[lindex $tag 2]
547 $tooltip_t insert end
" ([lindex $tag 3])\n"
548 $tooltip_t insert end
[lindex $tag 4]
549 $tooltip_t insert end
"\n"
553 $tooltip_t insert end
"\n"
554 $tooltip_t insert end
[mc
"Commit@@noun"] section_header
555 $tooltip_t insert end
" [lindex $cmit 1]\n"
556 $tooltip_t insert end
[lindex $cmit 2]
557 $tooltip_t insert end
" ([lindex $cmit 3])\n"
558 $tooltip_t insert end
[lindex $cmit 4]
561 if {[llength $spec] > 2} {
562 $tooltip_t insert end
"\n"
563 $tooltip_t insert end
[mc
"Remote"] section_header
564 $tooltip_t insert end
" [lindex $spec 2]\n"
565 $tooltip_t insert end
[mc
"URL"]
566 $tooltip_t insert end
" $remote_url([lindex $spec 2])\n"
567 $tooltip_t insert end
[mc
"Branch"]
568 $tooltip_t insert end
" [lindex $spec 3]"
571 $tooltip_t conf
-state disabled
572 _position_tooltip
$this
575 method _reflog_last
{name
} {
576 if {[info exists reflog_last
($name)]} {
577 return reflog_last
($name)
581 if {[catch {set last
[file mtime
[gitdir
$name]]}]
582 && ![catch {set g
[open [gitdir logs
$name] r
]}]} {
583 fconfigure $g -translation binary
584 while {[gets $g line
] >= 0} {
585 if {[regexp {> ([1-9][0-9]*) } $line line when
]} {
593 set last
[format_date
$last]
595 set reflog_last
($name) $last
599 method _position_tooltip
{} {
600 set max_h
[lindex [split [$tooltip_t index end
] .
] 0]
602 for {set i
1} {$i <= $max_h} {incr i
} {
603 set c
[lindex [split [$tooltip_t index
"$i.0 lineend"] .
] 1]
604 if {$c > $max_w} {set max_w
$c}
606 $tooltip_t conf
-width $max_w -height $max_h
608 set req_w
[winfo reqwidth
$tooltip_t]
609 set req_h
[winfo reqheight
$tooltip_t]
610 set pos_x
[expr {[winfo pointerx .
] + 5}]
611 set pos_y
[expr {[winfo pointery .
] + 10}]
613 set g
"${req_w}x${req_h}"
614 if {[tk windowingsystem
] eq
"win32" ||
$pos_x >= 0} {append g
+}
616 if {[tk windowingsystem
] eq
"win32" ||
$pos_y >= 0} {append g
+}
619 wm geometry
$tooltip_wm $g
623 method _hide_tooltip
{} {
624 if {$tooltip_wm ne
{}} {
628 if {$tooltip_timer ne
{}} {
629 after cancel
$tooltip_timer