Push code for transport library
[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 }