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