Push code for transport library
[git/git.git] / transport.c
1 #include "cache.h"
2 #include "transport.h"
3 #include "run-command.h"
4
5 static const struct transport_ops rsync_transport;
6
7 static int curl_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) {
8 const char **argv;
9 int argc;
10 int err;
11
12 argv = xmalloc((refspec_nr + 11) * sizeof(char *));
13 argv[0] = "http-push";
14 argc = 1;
15 if (flags & TRANSPORT_PUSH_ALL)
16 argv[argc++] = "--all";
17 if (flags & TRANSPORT_PUSH_FORCE)
18 argv[argc++] = "--force";
19 argv[argc++] = transport->url;
20 while (refspec_nr--)
21 argv[argc++] = *refspec++;
22 argv[argc] = NULL;
23 err = run_command_v_opt(argv, RUN_GIT_CMD);
24 switch (err) {
25 case -ERR_RUN_COMMAND_FORK:
26 error("unable to fork for %s", argv[0]);
27 case -ERR_RUN_COMMAND_EXEC:
28 error("unable to exec %s", argv[0]);
29 break;
30 case -ERR_RUN_COMMAND_WAITPID:
31 case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
32 case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
33 case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
34 error("%s died with strange error", argv[0]);
35 }
36 return !!err;
37 }
38
39 static const struct transport_ops curl_transport = {
40 /* set_option */ NULL,
41 /* push */ curl_transport_push
42 };
43
44 static const struct transport_ops bundle_transport = {
45 };
46
47 struct git_transport_data {
48 unsigned thin : 1;
49
50 const char *receivepack;
51 };
52
53 static int set_git_option(struct transport *connection,
54 const char *name, const char *value)
55 {
56 struct git_transport_data *data = connection->data;
57 if (!strcmp(name, TRANS_OPT_RECEIVEPACK)) {
58 data->receivepack = value;
59 return 0;
60 } else if (!strcmp(name, TRANS_OPT_THIN)) {
61 data->thin = !!value;
62 return 0;
63 }
64 return 1;
65 }
66
67 static int git_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) {
68 struct git_transport_data *data = transport->data;
69 const char **argv;
70 char *rem;
71 int argc;
72 int err;
73
74 argv = xmalloc((refspec_nr + 11) * sizeof(char *));
75 argv[0] = "send-pack";
76 argc = 1;
77 if (flags & TRANSPORT_PUSH_ALL)
78 argv[argc++] = "--all";
79 if (flags & TRANSPORT_PUSH_FORCE)
80 argv[argc++] = "--force";
81 if (data->receivepack) {
82 char *rp = xmalloc(strlen(data->receivepack) + 16);
83 sprintf(rp, "--receive-pack=%s", data->receivepack);
84 argv[argc++] = rp;
85 }
86 if (data->thin)
87 argv[argc++] = "--thin";
88 rem = xmalloc(strlen(transport->remote->name) + 10);
89 sprintf(rem, "--remote=%s", transport->remote->name);
90 argv[argc++] = rem;
91 argv[argc++] = transport->url;
92 while (refspec_nr--)
93 argv[argc++] = *refspec++;
94 argv[argc] = NULL;
95 err = run_command_v_opt(argv, RUN_GIT_CMD);
96 switch (err) {
97 case -ERR_RUN_COMMAND_FORK:
98 error("unable to fork for %s", argv[0]);
99 case -ERR_RUN_COMMAND_EXEC:
100 error("unable to exec %s", argv[0]);
101 break;
102 case -ERR_RUN_COMMAND_WAITPID:
103 case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
104 case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
105 case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
106 error("%s died with strange error", argv[0]);
107 }
108 return !!err;
109 }
110
111 static const struct transport_ops git_transport = {
112 /* set_option */ set_git_option,
113 /* push */ git_transport_push
114 };
115
116 static int is_local(const char *url)
117 {
118 const char *colon = strchr(url, ':');
119 const char *slash = strchr(url, '/');
120 return !colon || (slash && slash < colon);
121 }
122
123 static int is_file(const char *url)
124 {
125 struct stat buf;
126 if (stat(url, &buf))
127 return 0;
128 return S_ISREG(buf.st_mode);
129 }
130
131 struct transport *transport_get(struct remote *remote, const char *url,
132 int fetch)
133 {
134 struct transport *ret = NULL;
135 if (!prefixcmp(url, "rsync://")) {
136 ret = xmalloc(sizeof(*ret));
137 ret->data = NULL;
138 ret->ops = &rsync_transport;
139 } else if (!prefixcmp(url, "http://") || !prefixcmp(url, "https://") ||
140 !prefixcmp(url, "ftp://")) {
141 ret = xmalloc(sizeof(*ret));
142 ret->ops = &curl_transport;
143 ret->data = NULL;
144 } else if (is_local(url) && is_file(url)) {
145 ret = xmalloc(sizeof(*ret));
146 ret->data = NULL;
147 ret->ops = &bundle_transport;
148 } else {
149 struct git_transport_data *data = xcalloc(1, sizeof(*data));
150 ret = xcalloc(1, sizeof(*ret));
151 ret->data = data;
152 data->thin = 1;
153 data->receivepack = "git-receive-pack";
154 if (remote && remote->receivepack)
155 data->receivepack = remote->receivepack;
156 ret->ops = &git_transport;
157 }
158 if (ret) {
159 ret->remote = remote;
160 ret->url = url;
161 ret->fetch = !!fetch;
162 }
163 return ret;
164 }
165
166 int transport_set_option(struct transport *transport,
167 const char *name, const char *value)
168 {
169 int ret = 1;
170 if (transport->ops->set_option)
171 ret = transport->ops->set_option(transport, name, value);
172 if (ret < 0)
173 fprintf(stderr, "For '%s' option %s cannot be set to '%s'\n",
174 transport->url, name, value);
175 if (ret > 0)
176 fprintf(stderr, "For '%s' option %s is ignored\n",
177 transport->url, name);
178 return ret;
179 }
180
181 int transport_push(struct transport *transport,
182 int refspec_nr, const char **refspec, int flags)
183 {
184 if (!transport->ops->push)
185 return 1;
186 return transport->ops->push(transport, refspec_nr, refspec, flags);
187 }
188
189 int transport_disconnect(struct transport *transport)
190 {
191 int ret = 0;
192 if (transport->ops->disconnect)
193 ret = transport->ops->disconnect(transport);
194 free(transport);
195 return ret;
196 }