# 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 use_ttk NS
make_dialog top w
wm title $top [mc "%s (%s): Delete Branch Remotely" [appname] [reponame]]
if {$top ne {.}} {
wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
}
${NS}::label $w.header -text [mc "Delete Branch Remotely"] \
-font font_uibold -anchor center
pack $w.header -side top -fill x
${NS}::frame $w.buttons
${NS}::button $w.buttons.delete -text [mc Delete] \
-default active \
-command [cb _delete]
pack $w.buttons.delete -side right
${NS}::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
${NS}::labelframe $w.dest -text [mc "From Repository"]
if {$all_remotes ne {}} {
${NS}::radiobutton $w.dest.remote_r \
-text [mc "Remote:"] \
-value remote \
-variable @urltype
if {$use_ttk} {
ttk::combobox $w.dest.remote_m -textvariable @remote \
-values $all_remotes -state readonly
} else {
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
}
${NS}::radiobutton $w.dest.url_r \
-text [mc "Arbitrary Location:"] \
-value url \
-variable @urltype
${NS}::entry $w.dest.url_t \
-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
${NS}::labelframe $w.heads -text [mc "Branches"]
slistbox $w.heads.l \
-height 10 \
-width 70 \
-listvariable @head_list \
-selectmode extended
${NS}::frame $w.heads.footer
${NS}::label $w.heads.footer.status \
-textvariable @status \
-anchor w \
-justify left
${NS}::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.l -side left -fill both -expand 1
pack $w.heads -fill both -expand 1 -pady 5 -padx 5
${NS}::labelframe $w.validate -text [mc "Delete Only If"]
${NS}::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
${NS}::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 <Key-F5> [cb _rescan]
bind $w <$M1B-Key-r> [cb _rescan]
bind $w <$M1B-Key-R> [cb _rescan]
bind $w <Key-Return> [cb _delete]
bind $w <Key-Escape> [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 {$checktype ne {head}} {
if {[tk_messageBox \
-icon warning \
-type yesno \
-title [wm title $w] \
-parent $w \
-message [mc "Recovering deleted branches is difficult.\n\nDelete 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} {
global current_branch _last_merged_branch
$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} {
if {[lsearch -exact -sorted $head_list $current_branch] < 0} {
set check_head {}
} else {
set check_head $current_branch
}
}
set lmb [lsearch -exact -sorted $head_list $_last_merged_branch]
if {$lmb >= 0} {
$w.heads.l conf -state normal
$w.heads.l select set $lmb
$w.heads.l yview $lmb
$w.heads.l conf -state disabled
}
}
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}
}
}