1# git-gui remote management
2# Copyright (C) 2006, 2007 Shawn Pearce
34
set some_heads_tracking 0; # assume not
56
proc is_tracking_branch {name} {
7global tracking_branches
8foreach spec $tracking_branches {
9set t [lindex $spec 0]
10if {$t eq $name || [string match $t $name]} {
11return 1
12}
13}
14return 0
15}
1617
proc all_tracking_branches {} {
18global tracking_branches
1920
set all [list]
21set pat [list]
22set cmd [list]
2324
foreach spec $tracking_branches {
25set dst [lindex $spec 0]
26if {[string range $dst end-1 end] eq {/*}} {
27lappend pat $spec
28lappend cmd [string range $dst 0 end-2]
29} else {
30lappend all $spec
31}
32}
3334
if {$pat ne {}} {
35set fd [eval git_read for-each-ref --format=%(refname) $cmd]
36while {[gets $fd n] > 0} {
37foreach spec $pat {
38set dst [string range [lindex $spec 0] 0 end-2]
39set len [string length $dst]
40if {[string equal -length $len $dst $n]} {
41set src [string range [lindex $spec 2] 0 end-2]
42set spec [list \
43$n \
44[lindex $spec 1] \
45$src[string range $n $len end] \
46]
47lappend all $spec
48}
49}
50}
51close $fd
52}
5354
return [lsort -index 0 -unique $all]
55}
5657
proc load_all_remotes {} {
58global repo_config
59global all_remotes tracking_branches some_heads_tracking
60global remote_url
6162
set some_heads_tracking 0
63set all_remotes [list]
64set trck [list]
6566
set rh_str refs/heads/
67set rh_len [string length $rh_str]
68set rm_dir [gitdir remotes]
69if {[file isdirectory $rm_dir]} {
70set all_remotes [glob \
71-types f \
72-tails \
73-nocomplain \
74-directory $rm_dir *]
7576
foreach name $all_remotes {
77catch {
78set fd [open [file join $rm_dir $name] r]
79while {[gets $fd line] >= 0} {
80if {[regexp {^URL:[ ]*(.+)$} $line line url]} {
81set remote_url($name) $url
82continue
83}
84if {![regexp {^Pull:[ ]*([^:]+):(.+)$} \
85$line line src dst]} continue
86if {[string index $src 0] eq {+}} {
87set src [string range $src 1 end]
88}
89if {![string equal -length 5 refs/ $src]} {
90set src $rh_str$src
91}
92if {![string equal -length 5 refs/ $dst]} {
93set dst $rh_str$dst
94}
95if {[string equal -length $rh_len $rh_str $dst]} {
96set some_heads_tracking 1
97}
98lappend trck [list $dst $name $src]
99}
100close $fd
101}
102}
103}
104105
foreach line [array names repo_config remote.*.url] {
106if {![regexp ^remote\.(.*)\.url\$ $line line name]} continue
107lappend all_remotes $name
108set remote_url($name) $repo_config(remote.$name.url)
109110
if {[catch {set fl $repo_config(remote.$name.fetch)}]} {
111set fl {}
112}
113foreach line $fl {
114if {![regexp {^([^:]+):(.+)$} $line line src dst]} continue
115if {[string index $src 0] eq {+}} {
116set src [string range $src 1 end]
117}
118if {![string equal -length 5 refs/ $src]} {
119set src $rh_str$src
120}
121if {![string equal -length 5 refs/ $dst]} {
122set dst $rh_str$dst
123}
124if {[string equal -length $rh_len $rh_str $dst]} {
125set some_heads_tracking 1
126}
127lappend trck [list $dst $name $src]
128}
129}
130131
set tracking_branches [lsort -index 0 -unique $trck]
132set all_remotes [lsort -unique $all_remotes]
133}
134135
proc add_fetch_entry {r} {
136global repo_config
137set remote_m .mbar.remote
138set fetch_m $remote_m.fetch
139set prune_m $remote_m.prune
140set remove_m $remote_m.remove
141set enable 0
142if {![catch {set a $repo_config(remote.$r.url)}]} {
143if {![catch {set a $repo_config(remote.$r.fetch)}]} {
144set enable 1
145}
146} else {
147catch {
148set fd [open [gitdir remotes $r] r]
149while {[gets $fd n] >= 0} {
150if {[regexp {^Pull:[ \t]*([^:]+):} $n]} {
151set enable 1
152break
153}
154}
155close $fd
156}
157}
158159
if {$enable} {
160if {![winfo exists $fetch_m]} {
161menu $remove_m
162$remote_m insert 0 cascade \
163-label [mc "Remove Remote"] \
164-menu $remove_m
165166
menu $prune_m
167$remote_m insert 0 cascade \
168-label [mc "Prune from"] \
169-menu $prune_m
170171
menu $fetch_m
172$remote_m insert 0 cascade \
173-label [mc "Fetch from"] \
174-menu $fetch_m
175}
176177
$fetch_m add command \
178-label $r \
179-command [list fetch_from $r]
180$prune_m add command \
181-label $r \
182-command [list prune_from $r]
183$remove_m add command \
184-label $r \
185-command [list remove_remote $r]
186}
187}
188189
proc add_push_entry {r} {
190global repo_config
191set remote_m .mbar.remote
192set push_m $remote_m.push
193set enable 0
194if {![catch {set a $repo_config(remote.$r.url)}]} {
195if {![catch {set a $repo_config(remote.$r.push)}]} {
196set enable 1
197}
198} else {
199catch {
200set fd [open [gitdir remotes $r] r]
201while {[gets $fd n] >= 0} {
202if {[regexp {^Push:[ \t]*([^:]+):} $n]} {
203set enable 1
204break
205}
206}
207close $fd
208}
209}
210211
if {$enable} {
212if {![winfo exists $push_m]} {
213menu $push_m
214$remote_m insert 0 cascade \
215-label [mc "Push to"] \
216-menu $push_m
217}
218219
$push_m add command \
220-label $r \
221-command [list push_to $r]
222}
223}
224225
proc populate_remotes_menu {} {
226global all_remotes
227228
foreach r $all_remotes {
229add_fetch_entry $r
230add_push_entry $r
231}
232}
233234
proc add_single_remote {name location} {
235global all_remotes repo_config
236lappend all_remotes $name
237238
git remote add $name $location
239240
# XXX: Better re-read the config so that we will never get out
241# of sync with git remote implementation?
242set repo_config(remote.$name.url) $location
243set repo_config(remote.$name.fetch) "+refs/heads/*:refs/remotes/$name/*"
244245
add_fetch_entry $name
246add_push_entry $name
247}
248249
proc delete_from_menu {menu name} {
250if {[winfo exists $menu]} {
251$menu delete $name
252}
253}
254255
proc remove_remote {name} {
256global all_remotes repo_config
257258
git remote rm $name
259260
catch {
261# Missing values are ok
262unset repo_config(remote.$name.url)
263unset repo_config(remote.$name.fetch)
264unset repo_config(remote.$name.push)
265}
266267
set i [lsearch -exact $all_remotes $name]
268set all_remotes [lreplace $all_remotes $i $i]
269270
set remote_m .mbar.remote
271delete_from_menu $remote_m.fetch $name
272delete_from_menu $remote_m.prune $name
273delete_from_menu $remote_m.remove $name
274# Not all remotes are in the push menu
275catch { delete_from_menu $remote_m.push $name }
276}