git-gui / lib / remote_branch_delete.tclon commit transport-helper: mismerge fix (a21455a)
   1# git-gui remote branch deleting support
   2# Copyright (C) 2007 Shawn Pearce
   3
   4class remote_branch_delete {
   5
   6field w
   7field head_m
   8
   9field urltype   {url}
  10field remote    {}
  11field url       {}
  12
  13field checktype  {head}
  14field check_head {}
  15
  16field status    {}
  17field idle_id   {}
  18field full_list {}
  19field head_list {}
  20field active_ls {}
  21field head_cache
  22field full_cache
  23field cached
  24
  25constructor dialog {} {
  26        global all_remotes M1B use_ttk NS
  27
  28        make_dialog 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        ${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
 138method _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
 231method _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
 249method _write_remote     {args} { set urltype remote }
 250method _write_url        {args} { set urltype url    }
 251method _write_check_head {args} { set checktype head }
 252
 253method _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
 279method _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
 291method _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
 325method _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}