git-gui / lib / remote.tclon commit Merge branch 'md/url-parse-harden' (f9089e8)
   1# git-gui remote management
   2# Copyright (C) 2006, 2007 Shawn Pearce
   3
   4set some_heads_tracking 0;  # assume not
   5
   6proc is_tracking_branch {name} {
   7        global tracking_branches
   8        foreach spec $tracking_branches {
   9                set t [lindex $spec 0]
  10                if {$t eq $name || [string match $t $name]} {
  11                        return 1
  12                }
  13        }
  14        return 0
  15}
  16
  17proc all_tracking_branches {} {
  18        global tracking_branches
  19
  20        set all [list]
  21        set pat [list]
  22        set cmd [list]
  23
  24        foreach spec $tracking_branches {
  25                set dst [lindex $spec 0]
  26                if {[string range $dst end-1 end] eq {/*}} {
  27                        lappend pat $spec
  28                        lappend cmd [string range $dst 0 end-2]
  29                } else {
  30                        lappend all $spec
  31                }
  32        }
  33
  34        if {$pat ne {}} {
  35                set fd [eval git_read for-each-ref --format=%(refname) $cmd]
  36                while {[gets $fd n] > 0} {
  37                        foreach spec $pat {
  38                                set dst [string range [lindex $spec 0] 0 end-2]
  39                                set len [string length $dst]
  40                                if {[string equal -length $len $dst $n]} {
  41                                        set src [string range [lindex $spec 2] 0 end-2]
  42                                        set spec [list \
  43                                                $n \
  44                                                [lindex $spec 1] \
  45                                                $src[string range $n $len end] \
  46                                                ]
  47                                        lappend all $spec
  48                                }
  49                        }
  50                }
  51                close $fd
  52        }
  53
  54        return [lsort -index 0 -unique $all]
  55}
  56
  57proc load_all_remotes {} {
  58        global repo_config
  59        global all_remotes tracking_branches some_heads_tracking
  60        global remote_url
  61
  62        set some_heads_tracking 0
  63        set all_remotes [list]
  64        set trck [list]
  65
  66        set rh_str refs/heads/
  67        set rh_len [string length $rh_str]
  68        set rm_dir [gitdir remotes]
  69        if {[file isdirectory $rm_dir]} {
  70                set all_remotes [glob \
  71                        -types f \
  72                        -tails \
  73                        -nocomplain \
  74                        -directory $rm_dir *]
  75
  76                foreach name $all_remotes {
  77                        catch {
  78                                set fd [open [file join $rm_dir $name] r]
  79                                while {[gets $fd line] >= 0} {
  80                                        if {[regexp {^URL:[     ]*(.+)$} $line line url]} {
  81                                                set remote_url($name) $url
  82                                                continue
  83                                        }
  84                                        if {![regexp {^Pull:[   ]*([^:]+):(.+)$} \
  85                                                $line line src dst]} continue
  86                                        if {[string index $src 0] eq {+}} {
  87                                                set src [string range $src 1 end]
  88                                        }
  89                                        if {![string equal -length 5 refs/ $src]} {
  90                                                set src $rh_str$src
  91                                        }
  92                                        if {![string equal -length 5 refs/ $dst]} {
  93                                                set dst $rh_str$dst
  94                                        }
  95                                        if {[string equal -length $rh_len $rh_str $dst]} {
  96                                                set some_heads_tracking 1
  97                                        }
  98                                        lappend trck [list $dst $name $src]
  99                                }
 100                                close $fd
 101                        }
 102                }
 103        }
 104
 105        foreach line [array names repo_config remote.*.url] {
 106                if {![regexp ^remote\.(.*)\.url\$ $line line name]} continue
 107                lappend all_remotes $name
 108                set remote_url($name) $repo_config(remote.$name.url)
 109
 110                if {[catch {set fl $repo_config(remote.$name.fetch)}]} {
 111                        set fl {}
 112                }
 113                foreach line $fl {
 114                        if {![regexp {^([^:]+):(.+)$} $line line src dst]} continue
 115                        if {[string index $src 0] eq {+}} {
 116                                set src [string range $src 1 end]
 117                        }
 118                        if {![string equal -length 5 refs/ $src]} {
 119                                set src $rh_str$src
 120                        }
 121                        if {![string equal -length 5 refs/ $dst]} {
 122                                set dst $rh_str$dst
 123                        }
 124                        if {[string equal -length $rh_len $rh_str $dst]} {
 125                                set some_heads_tracking 1
 126                        }
 127                        lappend trck [list $dst $name $src]
 128                }
 129        }
 130
 131        set tracking_branches [lsort -index 0 -unique $trck]
 132        set all_remotes [lsort -unique $all_remotes]
 133}
 134
 135proc add_fetch_entry {r} {
 136        global repo_config
 137        set remote_m .mbar.remote
 138        set fetch_m $remote_m.fetch
 139        set prune_m $remote_m.prune
 140        set remove_m $remote_m.remove
 141        set enable 0
 142        if {![catch {set a $repo_config(remote.$r.url)}]} {
 143                if {![catch {set a $repo_config(remote.$r.fetch)}]} {
 144                        set enable 1
 145                }
 146        } else {
 147                catch {
 148                        set fd [open [gitdir remotes $r] r]
 149                        while {[gets $fd n] >= 0} {
 150                                if {[regexp {^Pull:[ \t]*([^:]+):} $n]} {
 151                                        set enable 1
 152                                        break
 153                                }
 154                        }
 155                        close $fd
 156                }
 157        }
 158
 159        if {$enable} {
 160                make_sure_remote_submenues_exist $remote_m
 161
 162                $fetch_m add command \
 163                        -label $r \
 164                        -command [list fetch_from $r]
 165                $prune_m add command \
 166                        -label $r \
 167                        -command [list prune_from $r]
 168                $remove_m add command \
 169                        -label $r \
 170                        -command [list remove_remote $r]
 171        }
 172}
 173
 174proc add_push_entry {r} {
 175        global repo_config
 176        set remote_m .mbar.remote
 177        set push_m $remote_m.push
 178        set enable 0
 179        if {![catch {set a $repo_config(remote.$r.url)}]} {
 180                if {![catch {set a $repo_config(remote.$r.push)}]} {
 181                        set enable 1
 182                }
 183        } else {
 184                catch {
 185                        set fd [open [gitdir remotes $r] r]
 186                        while {[gets $fd n] >= 0} {
 187                                if {[regexp {^Push:[ \t]*([^:]+):} $n]} {
 188                                        set enable 1
 189                                        break
 190                                }
 191                        }
 192                        close $fd
 193                }
 194        }
 195
 196        if {$enable} {
 197                if {![winfo exists $push_m]} {
 198                        menu $push_m
 199                        $remote_m insert 0 cascade \
 200                                -label [mc "Push to"] \
 201                                -menu $push_m
 202                }
 203
 204                $push_m add command \
 205                        -label $r \
 206                        -command [list push_to $r]
 207        }
 208}
 209
 210proc make_sure_remote_submenues_exist {remote_m} {
 211        set fetch_m $remote_m.fetch
 212        set prune_m $remote_m.prune
 213        set remove_m $remote_m.remove
 214
 215        if {![winfo exists $fetch_m]} {
 216                menu $remove_m
 217                $remote_m insert 0 cascade \
 218                        -label [mc "Remove Remote"] \
 219                        -menu $remove_m
 220
 221                menu $prune_m
 222                $remote_m insert 0 cascade \
 223                        -label [mc "Prune from"] \
 224                        -menu $prune_m
 225
 226                menu $fetch_m
 227                $remote_m insert 0 cascade \
 228                        -label [mc "Fetch from"] \
 229                        -menu $fetch_m
 230        }
 231}
 232
 233proc update_all_remotes_menu_entry {} {
 234        global all_remotes
 235
 236        if {[git-version < 1.6.6]} { return }
 237
 238        set have_remote 0
 239        foreach r $all_remotes {
 240                incr have_remote
 241        }
 242
 243        set remote_m .mbar.remote
 244        set fetch_m $remote_m.fetch
 245        set prune_m $remote_m.prune
 246        if {$have_remote > 1} {
 247                make_sure_remote_submenues_exist $remote_m
 248                if {[$fetch_m type end] eq "command" \
 249                                && [$fetch_m entrycget end -label] ne [mc "All"]} {
 250
 251                        $fetch_m insert end separator
 252                        $fetch_m insert end command \
 253                                -label [mc "All"] \
 254                                -command fetch_from_all
 255
 256                        $prune_m insert end separator
 257                        $prune_m insert end command \
 258                                -label [mc "All"] \
 259                                -command prune_from_all
 260                }
 261        } else {
 262                if {[winfo exists $fetch_m]} {
 263                        if {[$fetch_m type end] eq "command" \
 264                                        && [$fetch_m entrycget end -label] eq [mc "All"]} {
 265
 266                                delete_from_menu $fetch_m end
 267                                delete_from_menu $fetch_m end
 268
 269                                delete_from_menu $prune_m end
 270                                delete_from_menu $prune_m end
 271                        }
 272                }
 273        }
 274}
 275
 276proc populate_remotes_menu {} {
 277        global all_remotes
 278
 279        foreach r $all_remotes {
 280                add_fetch_entry $r
 281                add_push_entry $r
 282        }
 283
 284        update_all_remotes_menu_entry
 285}
 286
 287proc add_single_remote {name location} {
 288        global all_remotes repo_config
 289        lappend all_remotes $name
 290
 291        git remote add $name $location
 292
 293        # XXX: Better re-read the config so that we will never get out
 294        # of sync with git remote implementation?
 295        set repo_config(remote.$name.url) $location
 296        set repo_config(remote.$name.fetch) "+refs/heads/*:refs/remotes/$name/*"
 297
 298        add_fetch_entry $name
 299        add_push_entry $name
 300
 301        update_all_remotes_menu_entry
 302}
 303
 304proc delete_from_menu {menu name} {
 305        if {[winfo exists $menu]} {
 306                $menu delete $name
 307        }
 308}
 309
 310proc remove_remote {name} {
 311        global all_remotes repo_config
 312
 313        git remote rm $name
 314
 315        catch {
 316                # Missing values are ok
 317                unset repo_config(remote.$name.url)
 318                unset repo_config(remote.$name.fetch)
 319                unset repo_config(remote.$name.push)
 320        }
 321
 322        set i [lsearch -exact $all_remotes $name]
 323        set all_remotes [lreplace $all_remotes $i $i]
 324
 325        set remote_m .mbar.remote
 326        delete_from_menu $remote_m.fetch $name
 327        delete_from_menu $remote_m.prune $name
 328        delete_from_menu $remote_m.remove $name
 329        # Not all remotes are in the push menu
 330        catch { delete_from_menu $remote_m.push $name }
 331
 332        update_all_remotes_menu_entry
 333}