| 1 | # git-gui remote management |
| 2 | # Copyright (C) 2006, 2007 Shawn Pearce |
| 3 | |
| 4 | set some_heads_tracking 0; # assume not |
| 5 | |
| 6 | proc 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 | |
| 17 | proc 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 [git_read [concat 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 | |
| 57 | proc 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 [safe_open_file [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 | |
| 135 | proc 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 [safe_open_file [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 | |
| 174 | proc 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 [safe_open_file [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 | |
| 210 | proc 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 | |
| 233 | proc update_all_remotes_menu_entry {} { |
| 234 | global all_remotes |
| 235 | |
| 236 | set have_remote 0 |
| 237 | foreach r $all_remotes { |
| 238 | incr have_remote |
| 239 | } |
| 240 | |
| 241 | set remote_m .mbar.remote |
| 242 | set fetch_m $remote_m.fetch |
| 243 | set prune_m $remote_m.prune |
| 244 | if {$have_remote > 1} { |
| 245 | make_sure_remote_submenues_exist $remote_m |
| 246 | if {[$fetch_m type end] eq "command" \ |
| 247 | && [$fetch_m entrycget end -label] ne [mc "All"]} { |
| 248 | |
| 249 | $fetch_m insert end separator |
| 250 | $fetch_m insert end command \ |
| 251 | -label [mc "All"] \ |
| 252 | -command fetch_from_all |
| 253 | |
| 254 | $prune_m insert end separator |
| 255 | $prune_m insert end command \ |
| 256 | -label [mc "All"] \ |
| 257 | -command prune_from_all |
| 258 | } |
| 259 | } else { |
| 260 | if {[winfo exists $fetch_m]} { |
| 261 | if {[$fetch_m type end] eq "command" \ |
| 262 | && [$fetch_m entrycget end -label] eq [mc "All"]} { |
| 263 | |
| 264 | delete_from_menu $fetch_m end |
| 265 | delete_from_menu $fetch_m end |
| 266 | |
| 267 | delete_from_menu $prune_m end |
| 268 | delete_from_menu $prune_m end |
| 269 | } |
| 270 | } |
| 271 | } |
| 272 | } |
| 273 | |
| 274 | proc populate_remotes_menu {} { |
| 275 | global all_remotes |
| 276 | |
| 277 | foreach r $all_remotes { |
| 278 | add_fetch_entry $r |
| 279 | add_push_entry $r |
| 280 | } |
| 281 | |
| 282 | update_all_remotes_menu_entry |
| 283 | } |
| 284 | |
| 285 | proc add_single_remote {name location} { |
| 286 | global all_remotes repo_config |
| 287 | lappend all_remotes $name |
| 288 | |
| 289 | git remote add $name $location |
| 290 | |
| 291 | # XXX: Better re-read the config so that we will never get out |
| 292 | # of sync with git remote implementation? |
| 293 | set repo_config(remote.$name.url) $location |
| 294 | set repo_config(remote.$name.fetch) "+refs/heads/*:refs/remotes/$name/*" |
| 295 | |
| 296 | add_fetch_entry $name |
| 297 | add_push_entry $name |
| 298 | |
| 299 | update_all_remotes_menu_entry |
| 300 | } |
| 301 | |
| 302 | proc delete_from_menu {menu name} { |
| 303 | if {[winfo exists $menu]} { |
| 304 | $menu delete $name |
| 305 | } |
| 306 | } |
| 307 | |
| 308 | proc remove_remote {name} { |
| 309 | global all_remotes repo_config |
| 310 | |
| 311 | git remote rm $name |
| 312 | |
| 313 | catch { |
| 314 | # Missing values are ok |
| 315 | unset repo_config(remote.$name.url) |
| 316 | unset repo_config(remote.$name.fetch) |
| 317 | unset repo_config(remote.$name.push) |
| 318 | } |
| 319 | |
| 320 | set i [lsearch -exact $all_remotes $name] |
| 321 | set all_remotes [lreplace $all_remotes $i $i] |
| 322 | |
| 323 | set remote_m .mbar.remote |
| 324 | delete_from_menu $remote_m.fetch $name |
| 325 | delete_from_menu $remote_m.prune $name |
| 326 | delete_from_menu $remote_m.remove $name |
| 327 | # Not all remotes are in the push menu |
| 328 | catch { delete_from_menu $remote_m.push $name } |
| 329 | |
| 330 | update_all_remotes_menu_entry |
| 331 | } |