Move remote parsing into a library file out of builtin-push.
[git/git.git] / remote.c
CommitLineData
5751f490
DB
1#include "cache.h"
2#include "remote.h"
3#include "refs.h"
4
5static struct remote **remotes;
6static int allocated_remotes;
7
8#define BUF_SIZE (2048)
9static char buffer[BUF_SIZE];
10
11static void add_push_refspec(struct remote *remote, const char *ref)
12{
13 int nr = remote->push_refspec_nr + 1;
14 remote->push_refspec =
15 xrealloc(remote->push_refspec, nr * sizeof(char *));
16 remote->push_refspec[nr-1] = ref;
17 remote->push_refspec_nr = nr;
18}
19
20static void add_uri(struct remote *remote, const char *uri)
21{
22 int nr = remote->uri_nr + 1;
23 remote->uri =
24 xrealloc(remote->uri, nr * sizeof(char *));
25 remote->uri[nr-1] = uri;
26 remote->uri_nr = nr;
27}
28
29static struct remote *make_remote(const char *name, int len)
30{
31 int i, empty = -1;
32
33 for (i = 0; i < allocated_remotes; i++) {
34 if (!remotes[i]) {
35 if (empty < 0)
36 empty = i;
37 } else {
38 if (len ? (!strncmp(name, remotes[i]->name, len) &&
39 !remotes[i]->name[len]) :
40 !strcmp(name, remotes[i]->name))
41 return remotes[i];
42 }
43 }
44
45 if (empty < 0) {
46 empty = allocated_remotes;
47 allocated_remotes += allocated_remotes ? allocated_remotes : 1;
48 remotes = xrealloc(remotes,
49 sizeof(*remotes) * allocated_remotes);
50 memset(remotes + empty, 0,
51 (allocated_remotes - empty) * sizeof(*remotes));
52 }
53 remotes[empty] = xcalloc(1, sizeof(struct remote));
54 if (len)
55 remotes[empty]->name = xstrndup(name, len);
56 else
57 remotes[empty]->name = xstrdup(name);
58 return remotes[empty];
59}
60
61static void read_remotes_file(struct remote *remote)
62{
63 FILE *f = fopen(git_path("remotes/%s", remote->name), "r");
64
65 if (!f)
66 return;
67 while (fgets(buffer, BUF_SIZE, f)) {
68 int value_list;
69 char *s, *p;
70
71 if (!prefixcmp(buffer, "URL:")) {
72 value_list = 0;
73 s = buffer + 4;
74 } else if (!prefixcmp(buffer, "Push:")) {
75 value_list = 1;
76 s = buffer + 5;
77 } else
78 continue;
79
80 while (isspace(*s))
81 s++;
82 if (!*s)
83 continue;
84
85 p = s + strlen(s);
86 while (isspace(p[-1]))
87 *--p = 0;
88
89 switch (value_list) {
90 case 0:
91 add_uri(remote, xstrdup(s));
92 break;
93 case 1:
94 add_push_refspec(remote, xstrdup(s));
95 break;
96 }
97 }
98 fclose(f);
99}
100
101static void read_branches_file(struct remote *remote)
102{
103 const char *slash = strchr(remote->name, '/');
104 int n = slash ? slash - remote->name : 1000;
105 FILE *f = fopen(git_path("branches/%.*s", n, remote->name), "r");
106 char *s, *p;
107 int len;
108
109 if (!f)
110 return;
111 s = fgets(buffer, BUF_SIZE, f);
112 fclose(f);
113 if (!s)
114 return;
115 while (isspace(*s))
116 s++;
117 if (!*s)
118 return;
119 p = s + strlen(s);
120 while (isspace(p[-1]))
121 *--p = 0;
122 len = p - s;
123 if (slash)
124 len += strlen(slash);
125 p = xmalloc(len + 1);
126 strcpy(p, s);
127 if (slash)
128 strcat(p, slash);
129 add_uri(remote, p);
130}
131
132static char *default_remote_name = NULL;
133static const char *current_branch = NULL;
134static int current_branch_len = 0;
135
136static int handle_config(const char *key, const char *value)
137{
138 const char *name;
139 const char *subkey;
140 struct remote *remote;
141 if (!prefixcmp(key, "branch.") && current_branch &&
142 !strncmp(key + 7, current_branch, current_branch_len) &&
143 !strcmp(key + 7 + current_branch_len, ".remote")) {
144 free(default_remote_name);
145 default_remote_name = xstrdup(value);
146 }
147 if (prefixcmp(key, "remote."))
148 return 0;
149 name = key + 7;
150 subkey = strrchr(name, '.');
151 if (!subkey)
152 return error("Config with no key for remote %s", name);
153 if (*subkey == '/') {
154 warning("Config remote shorthand cannot begin with '/': %s", name);
155 return 0;
156 }
157 remote = make_remote(name, subkey - name);
158 if (!value) {
159 /* if we ever have a boolean variable, e.g. "remote.*.disabled"
160 * [remote "frotz"]
161 * disabled
162 * is a valid way to set it to true; we get NULL in value so
163 * we need to handle it here.
164 *
165 * if (!strcmp(subkey, ".disabled")) {
166 * val = git_config_bool(key, value);
167 * return 0;
168 * } else
169 *
170 */
171 return 0; /* ignore unknown booleans */
172 }
173 if (!strcmp(subkey, ".url")) {
174 add_uri(remote, xstrdup(value));
175 } else if (!strcmp(subkey, ".push")) {
176 add_push_refspec(remote, xstrdup(value));
177 } else if (!strcmp(subkey, ".receivepack")) {
178 if (!remote->receivepack)
179 remote->receivepack = xstrdup(value);
180 else
181 error("more than one receivepack given, using the first");
182 }
183 return 0;
184}
185
186static void read_config(void)
187{
188 unsigned char sha1[20];
189 const char *head_ref;
190 int flag;
191 if (default_remote_name) // did this already
192 return;
193 default_remote_name = xstrdup("origin");
194 current_branch = NULL;
195 head_ref = resolve_ref("HEAD", sha1, 0, &flag);
196 if (head_ref && (flag & REF_ISSYMREF) &&
197 !prefixcmp(head_ref, "refs/heads/")) {
198 current_branch = head_ref + strlen("refs/heads/");
199 current_branch_len = strlen(current_branch);
200 }
201 git_config(handle_config);
202}
203
204struct remote *remote_get(const char *name)
205{
206 struct remote *ret;
207
208 read_config();
209 if (!name)
210 name = default_remote_name;
211 ret = make_remote(name, 0);
212 if (name[0] != '/') {
213 if (!ret->uri)
214 read_remotes_file(ret);
215 if (!ret->uri)
216 read_branches_file(ret);
217 }
218 if (!ret->uri)
219 add_uri(ret, name);
220 if (!ret->uri)
221 return NULL;
222 return ret;
223}