# git-gui remote branch deleting support # Copyright (C) 2007 Shawn Pearce class remote_branch_delete { field w field head_m field urltype {url} field remote {} field url {} field checktype {head} field check_head {} field status {} field idle_id {} field full_list {} field head_list {} field active_ls {} field head_cache field full_cache field cached constructor dialog {} { global all_remotes M1B make_toplevel top w wm title $top [append "[appname] ([reponame]): " [mc "Delete Remote Branch"]] if {$top ne {.}} { wm geometry $top "+[winfo rootx .]+[winfo rooty .]" } label $w.header -text [mc "Delete Remote Branch"] -font font_uibold pack $w.header -side top -fill x frame $w.buttons button $w.buttons.delete -text [mc Delete] \ -default active \ -command [cb _delete] pack $w.buttons.delete -side right button $w.buttons.cancel -text [mc "Cancel"] \ -command [list destroy $w] pack $w.buttons.cancel -side right -padx 5 pack $w.buttons -side bottom -fill x -pady 10 -padx 10 labelframe $w.dest -text [mc "From Repository"] if {$all_remotes ne {}} { radiobutton $w.dest.remote_r \ -text [mc "Remote:"] \ -value remote \ -variable @urltype eval tk_optionMenu $w.dest.remote_m @remote $all_remotes grid $w.dest.remote_r $w.dest.remote_m -sticky w if {[lsearch -sorted -exact $all_remotes origin] != -1} { set remote origin } else { set remote [lindex $all_remotes 0] } set urltype remote trace add variable @remote write [cb _write_remote] } else { set urltype url } radiobutton $w.dest.url_r \ -text [mc "Arbitrary URL:"] \ -value url \ -variable @urltype entry $w.dest.url_t \ -borderwidth 1 \ -relief sunken \ -width 50 \ -textvariable @url \ -validate key \ -validatecommand { if {%d == 1 && [regexp {\s} %S]} {return 0} return 1 } trace add variable @url write [cb _write_url] grid $w.dest.url_r $w.dest.url_t -sticky we -padx {0 5} grid columnconfigure $w.dest 1 -weight 1 pack $w.dest -anchor nw -fill x -pady 5 -padx 5 labelframe $w.heads -text [mc "Branches"] listbox $w.heads.l \ -height 10 \ -width 70 \ -listvariable @head_list \ -selectmode extended \ -yscrollcommand [list $w.heads.sby set] scrollbar $w.heads.sby -command [list $w.heads.l yview] frame $w.heads.footer label $w.heads.footer.status \ -textvariable @status \ -anchor w \ -justify left button $w.heads.footer.rescan \ -text [mc "Rescan"] \ -command [cb _rescan] pack $w.heads.footer.status -side left -fill x pack $w.heads.footer.rescan -side right pack $w.heads.footer -side bottom -fill x pack $w.heads.sby -side right -fill y pack $w.heads.l -side left -fill both -expand 1 pack $w.heads -fill both -expand 1 -pady 5 -padx 5 labelframe $w.validate -text [mc "Delete Only If"] radiobutton $w.validate.head_r \ -text [mc "Merged Into:"] \ -value head \ -variable @checktype set head_m [tk_optionMenu $w.validate.head_m @check_head {}] trace add variable @head_list write [cb _write_head_list] trace add variable @check_head write [cb _write_check_head] grid $w.validate.head_r $w.validate.head_m -sticky w radiobutton $w.validate.always_r \ -text [mc "Always (Do not perform merge checks)"] \ -value always \ -variable @checktype grid $w.validate.always_r -columnspan 2 -sticky w grid columnconfigure $w.validate 1 -weight 1 pack $w.validate -anchor nw -fill x -pady 5 -padx 5 trace add variable @urltype write [cb _write_urltype] _rescan $this bind $w [cb _rescan] bind $w <$M1B-Key-r> [cb _rescan] bind $w <$M1B-Key-R> [cb _rescan] bind $w [cb _delete] bind $w [list destroy $w] return $w } method _delete {} { switch $urltype { remote {set uri $remote} url {set uri $url} } set cache $urltype:$uri set crev {} if {$checktype eq {head}} { if {$check_head eq {}} { tk_messageBox \ -icon error \ -type ok \ -title [wm title $w] \ -parent $w \ -message [mc "A branch is required for 'Merged Into'."] return } set crev $full_cache("$cache\nrefs/heads/$check_head") } set not_merged [list] set need_fetch 0 set have_selection 0 set push_cmd [list git push] lappend push_cmd -v lappend push_cmd $uri foreach i [$w.heads.l curselection] { set ref [lindex $full_list $i] if {$crev ne {}} { set obj $full_cache("$cache\n$ref") if {[catch {set m [git merge-base $obj $crev]}]} { set need_fetch 1 set m {} } if {$obj ne $m} { lappend not_merged [lindex $head_list $i] continue } } lappend push_cmd :$ref set have_selection 1 } if {$not_merged ne {}} { set msg [mc "The following branches are not completely merged into %s: - %s" $check_head [join $not_merged "\n - "]] if {$need_fetch} { 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] } tk_messageBox \ -icon info \ -type ok \ -title [wm title $w] \ -parent $w \ -message $msg if {!$have_selection} return } if {!$have_selection} { tk_messageBox \ -icon error \ -type ok \ -title [wm title $w] \ -parent $w \ -message [mc "Please select one or more branches to delete."] return } if {[tk_messageBox \ -icon warning \ -type yesno \ -title [wm title $w] \ -parent $w \ -message [mc "Recovering deleted branches is difficult. Delete the selected branches?"]] ne yes} { return } destroy $w set cons [console::new \ "push $uri" \ [mc "Deleting branches from %s" $uri]] console::exec $cons $push_cmd } method _rescan {{force 1}} { switch $urltype { remote {set uri $remote} url {set uri $url} } if {$force} { unset -nocomplain cached($urltype:$uri) } if {$idle_id ne {}} { after cancel $idle_id set idle_id {} } _load $this $urltype:$uri $uri } method _write_remote {args} { set urltype remote } method _write_url {args} { set urltype url } method _write_check_head {args} { set checktype head } method _write_head_list {args} { $head_m delete 0 end foreach abr $head_list { $head_m insert end radiobutton \ -label $abr \ -value $abr \ -variable @check_head } if {[lsearch -exact -sorted $head_list $check_head] < 0} { set check_head {} } } method _write_urltype {args} { if {$urltype eq {url}} { if {$idle_id ne {}} { after cancel $idle_id } _load $this none: {} set idle_id [after 1000 [cb _rescan 0]] } else { _rescan $this 0 } } method _load {cache uri} { if {$active_ls ne {}} { catch {close $active_ls} } if {$uri eq {}} { $w.heads.l conf -state disabled set head_list [list] set full_list [list] set status [mc "No repository selected."] return } if {[catch {set x $cached($cache)}]} { set status [mc "Scanning %s..." $uri] $w.heads.l conf -state disabled set head_list [list] set full_list [list] set head_cache($cache) [list] set full_cache($cache) [list] set active_ls [git_read ls-remote $uri] fconfigure $active_ls \ -blocking 0 \ -translation lf \ -encoding utf-8 fileevent $active_ls readable [cb _read $cache $active_ls] } else { set status {} set full_list $full_cache($cache) set head_list $head_cache($cache) $w.heads.l conf -state normal } } method _read {cache fd} { if {$fd ne $active_ls} { catch {close $fd} return } while {[gets $fd line] >= 0} { if {[string match {*^{}} $line]} continue if {[regexp {^([0-9a-f]{40}) (.*)$} $line _junk obj ref]} { if {[regsub ^refs/heads/ $ref {} abr]} { lappend head_list $abr lappend head_cache($cache) $abr lappend full_list $ref lappend full_cache($cache) $ref set full_cache("$cache\n$ref") $obj } } } if {[eof $fd]} { if {[catch {close $fd} err]} { set status $err set head_list [list] set full_list [list] } else { set status {} set cached($cache) 1 $w.heads.l conf -state normal } } } ifdeleted { catch {close $fd} } }