Merge branch 'jc/blame'
[git] / git-gui / lib / remote_branch_delete.tcl
1 # git-gui remote branch deleting support
2 # Copyright (C) 2007 Shawn Pearce
3
4 class remote_branch_delete {
5
6 field w
7 field head_m
8
9 field urltype   {url}
10 field remote    {}
11 field url       {}
12
13 field checktype  {head}
14 field check_head {}
15
16 field status    {}
17 field idle_id   {}
18 field full_list {}
19 field head_list {}
20 field active_ls {}
21 field head_cache
22 field full_cache
23 field cached
24
25 constructor dialog {} {
26         global all_remotes M1B
27
28         make_toplevel top w
29         wm title $top [append "[appname] ([reponame]): " [mc "Delete Branch Remotely"]]
30         if {$top ne {.}} {
31                 wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
32         }
33
34         label $w.header -text [mc "Delete Branch Remotely"] -font font_uibold
35         pack $w.header -side top -fill x
36
37         frame $w.buttons
38         button $w.buttons.delete -text [mc Delete] \
39                 -default active \
40                 -command [cb _delete]
41         pack $w.buttons.delete -side right
42         button $w.buttons.cancel -text [mc "Cancel"] \
43                 -command [list destroy $w]
44         pack $w.buttons.cancel -side right -padx 5
45         pack $w.buttons -side bottom -fill x -pady 10 -padx 10
46
47         labelframe $w.dest -text [mc "From Repository"]
48         if {$all_remotes ne {}} {
49                 radiobutton $w.dest.remote_r \
50                         -text [mc "Remote:"] \
51                         -value remote \
52                         -variable @urltype
53                 eval tk_optionMenu $w.dest.remote_m @remote $all_remotes
54                 grid $w.dest.remote_r $w.dest.remote_m -sticky w
55                 if {[lsearch -sorted -exact $all_remotes origin] != -1} {
56                         set remote origin
57                 } else {
58                         set remote [lindex $all_remotes 0]
59                 }
60                 set urltype remote
61                 trace add variable @remote write [cb _write_remote]
62         } else {
63                 set urltype url
64         }
65         radiobutton $w.dest.url_r \
66                 -text [mc "Arbitrary Location:"] \
67                 -value url \
68                 -variable @urltype
69         entry $w.dest.url_t \
70                 -borderwidth 1 \
71                 -relief sunken \
72                 -width 50 \
73                 -textvariable @url \
74                 -validate key \
75                 -validatecommand {
76                         if {%d == 1 && [regexp {\s} %S]} {return 0}
77                         return 1
78                 }
79         trace add variable @url write [cb _write_url]
80         grid $w.dest.url_r $w.dest.url_t -sticky we -padx {0 5}
81         grid columnconfigure $w.dest 1 -weight 1
82         pack $w.dest -anchor nw -fill x -pady 5 -padx 5
83
84         labelframe $w.heads -text [mc "Branches"]
85         listbox $w.heads.l \
86                 -height 10 \
87                 -width 70 \
88                 -listvariable @head_list \
89                 -selectmode extended \
90                 -yscrollcommand [list $w.heads.sby set]
91         scrollbar $w.heads.sby -command [list $w.heads.l yview]
92
93         frame $w.heads.footer
94         label $w.heads.footer.status \
95                 -textvariable @status \
96                 -anchor w \
97                 -justify left
98         button $w.heads.footer.rescan \
99                 -text [mc "Rescan"] \
100                 -command [cb _rescan]
101         pack $w.heads.footer.status -side left -fill x
102         pack $w.heads.footer.rescan -side right
103
104         pack $w.heads.footer -side bottom -fill x
105         pack $w.heads.sby -side right -fill y
106         pack $w.heads.l -side left -fill both -expand 1
107         pack $w.heads -fill both -expand 1 -pady 5 -padx 5
108
109         labelframe $w.validate -text [mc "Delete Only If"]
110         radiobutton $w.validate.head_r \
111                 -text [mc "Merged Into:"] \
112                 -value head \
113                 -variable @checktype
114         set head_m [tk_optionMenu $w.validate.head_m @check_head {}]
115         trace add variable @head_list write [cb _write_head_list]
116         trace add variable @check_head write [cb _write_check_head]
117         grid $w.validate.head_r $w.validate.head_m -sticky w
118         radiobutton $w.validate.always_r \
119                 -text [mc "Always (Do not perform merge checks)"] \
120                 -value always \
121                 -variable @checktype
122         grid $w.validate.always_r -columnspan 2 -sticky w
123         grid columnconfigure $w.validate 1 -weight 1
124         pack $w.validate -anchor nw -fill x -pady 5 -padx 5
125
126         trace add variable @urltype write [cb _write_urltype]
127         _rescan $this
128
129         bind $w <Key-F5>     [cb _rescan]
130         bind $w <$M1B-Key-r> [cb _rescan]
131         bind $w <$M1B-Key-R> [cb _rescan]
132         bind $w <Key-Return> [cb _delete]
133         bind $w <Key-Escape> [list destroy $w]
134         return $w
135 }
136
137 method _delete {} {
138         switch $urltype {
139         remote {set uri $remote}
140         url    {set uri $url}
141         }
142
143         set cache $urltype:$uri
144         set crev {}
145         if {$checktype eq {head}} {
146                 if {$check_head eq {}} {
147                         tk_messageBox \
148                                 -icon error \
149                                 -type ok \
150                                 -title [wm title $w] \
151                                 -parent $w \
152                                 -message [mc "A branch is required for 'Merged Into'."]
153                         return
154                 }
155                 set crev $full_cache("$cache\nrefs/heads/$check_head")
156         }
157
158         set not_merged [list]
159         set need_fetch 0
160         set have_selection 0
161         set push_cmd [list git push]
162         lappend push_cmd -v
163         lappend push_cmd $uri
164
165         foreach i [$w.heads.l curselection] {
166                 set ref [lindex $full_list $i]
167                 if {$crev ne {}} {
168                         set obj $full_cache("$cache\n$ref")
169                         if {[catch {set m [git merge-base $obj $crev]}]} {
170                                 set need_fetch 1
171                                 set m {}
172                         }
173                         if {$obj ne $m} {
174                                 lappend not_merged [lindex $head_list $i]
175                                 continue
176                         }
177                 }
178
179                 lappend push_cmd :$ref
180                 set have_selection 1
181         }
182
183         if {$not_merged ne {}} {
184                 set msg [mc "The following branches are not completely merged into %s:
185
186  - %s" $check_head [join $not_merged "\n - "]]
187
188                 if {$need_fetch} {
189                         append msg "\n\n" [mc "One or more of the merge tests failed because you have not fetched the necessary commits.  Try fetching from %s first." $uri]
190                 }
191
192                 tk_messageBox \
193                         -icon info \
194                         -type ok \
195                         -title [wm title $w] \
196                         -parent $w \
197                         -message $msg
198                 if {!$have_selection} return
199         }
200
201         if {!$have_selection} {
202                 tk_messageBox \
203                         -icon error \
204                         -type ok \
205                         -title [wm title $w] \
206                         -parent $w \
207                         -message [mc "Please select one or more branches to delete."]
208                 return
209         }
210
211         if {[tk_messageBox \
212                 -icon warning \
213                 -type yesno \
214                 -title [wm title $w] \
215                 -parent $w \
216                 -message [mc "Recovering deleted branches is difficult.
217
218 Delete the selected branches?"]] ne yes} {
219                 return
220         }
221
222         destroy $w
223
224         set cons [console::new \
225                 "push $uri" \
226                 [mc "Deleting branches from %s" $uri]]
227         console::exec $cons $push_cmd
228 }
229
230 method _rescan {{force 1}} {
231         switch $urltype {
232         remote {set uri $remote}
233         url    {set uri $url}
234         }
235
236         if {$force} {
237                 unset -nocomplain cached($urltype:$uri)
238         }
239
240         if {$idle_id ne {}} {
241                 after cancel $idle_id
242                 set idle_id {}
243         }
244
245         _load $this $urltype:$uri $uri
246 }
247
248 method _write_remote     {args} { set urltype remote }
249 method _write_url        {args} { set urltype url    }
250 method _write_check_head {args} { set checktype head }
251
252 method _write_head_list {args} {
253         $head_m delete 0 end
254         foreach abr $head_list {
255                 $head_m insert end radiobutton \
256                         -label $abr \
257                         -value $abr \
258                         -variable @check_head
259         }
260         if {[lsearch -exact -sorted $head_list $check_head] < 0} {
261                 set check_head {}
262         }
263 }
264
265 method _write_urltype {args} {
266         if {$urltype eq {url}} {
267                 if {$idle_id ne {}} {
268                         after cancel $idle_id
269                 }
270                 _load $this none: {}
271                 set idle_id [after 1000 [cb _rescan 0]]
272         } else {
273                 _rescan $this 0
274         }
275 }
276
277 method _load {cache uri} {
278         if {$active_ls ne {}} {
279                 catch {close $active_ls}
280         }
281
282         if {$uri eq {}} {
283                 $w.heads.l conf -state disabled
284                 set head_list [list]
285                 set full_list [list]
286                 set status [mc "No repository selected."]
287                 return
288         }
289
290         if {[catch {set x $cached($cache)}]} {
291                 set status [mc "Scanning %s..." $uri]
292                 $w.heads.l conf -state disabled
293                 set head_list [list]
294                 set full_list [list]
295                 set head_cache($cache) [list]
296                 set full_cache($cache) [list]
297                 set active_ls [git_read ls-remote $uri]
298                 fconfigure $active_ls \
299                         -blocking 0 \
300                         -translation lf \
301                         -encoding utf-8
302                 fileevent $active_ls readable [cb _read $cache $active_ls]
303         } else {
304                 set status {}
305                 set full_list $full_cache($cache)
306                 set head_list $head_cache($cache)
307                 $w.heads.l conf -state normal
308         }
309 }
310
311 method _read {cache fd} {
312         if {$fd ne $active_ls} {
313                 catch {close $fd}
314                 return
315         }
316
317         while {[gets $fd line] >= 0} {
318                 if {[string match {*^{}} $line]} continue
319                 if {[regexp {^([0-9a-f]{40})    (.*)$} $line _junk obj ref]} {
320                         if {[regsub ^refs/heads/ $ref {} abr]} {
321                                 lappend head_list $abr
322                                 lappend head_cache($cache) $abr
323                                 lappend full_list $ref
324                                 lappend full_cache($cache) $ref
325                                 set full_cache("$cache\n$ref") $obj
326                         }
327                 }
328         }
329
330         if {[eof $fd]} {
331                 if {[catch {close $fd} err]} {
332                         set status $err
333                         set head_list [list]
334                         set full_list [list]
335                 } else {
336                         set status {}
337                         set cached($cache) 1
338                         $w.heads.l conf -state normal
339                 }
340         }
341 } ifdeleted {
342         catch {close $fd}
343 }
344
345 }