git builtin "push"
[git.git] / builtin-push.c
1 /*
2  * "git push"
3  */
4 #include "cache.h"
5 #include "refs.h"
6 #include "run-command.h"
7 #include "builtin.h"
8
9 #define MAX_URI (16)
10
11 static const char push_usage[] = "git push [--all] [--tags] [--force] <repository> [<refspec>...]";
12
13 static int all = 0, tags = 0, force = 0, thin = 1;
14 static const char *execute = NULL;
15
16 #define BUF_SIZE (2084)
17 static char buffer[BUF_SIZE];
18
19 static const char **refspec = NULL;
20 static int refspec_nr = 0;
21
22 static void add_refspec(const char *ref)
23 {
24         int nr = refspec_nr + 1;
25         refspec = xrealloc(refspec, nr * sizeof(char *));
26         refspec[nr-1] = ref;
27         refspec_nr = nr;
28 }
29
30 static int expand_one_ref(const char *ref, const unsigned char *sha1)
31 {
32         /* Ignore the "refs/" at the beginning of the refname */
33         ref += 5;
34
35         if (strncmp(ref, "tags/", 5))
36                 return 0;
37
38         add_refspec(strdup(ref));
39         return 0;
40 }
41
42 static void expand_refspecs(void)
43 {
44         if (all) {
45                 if (refspec_nr)
46                         die("cannot mix '--all' and a refspec");
47
48                 /*
49                  * No need to expand "--all" - we'll just use
50                  * the "--all" flag to send-pack
51                  */
52                 return;
53         }
54         if (!tags)
55                 return;
56         for_each_ref(expand_one_ref);
57 }
58
59 static void set_refspecs(const char **refs, int nr)
60 {
61         if (nr) {
62                 size_t bytes = nr * sizeof(char *);
63
64                 refspec = xrealloc(refspec, bytes);
65                 memcpy(refspec, refs, bytes);
66                 refspec_nr = nr;
67         }
68         expand_refspecs();
69 }
70
71 static int get_remotes_uri(const char *repo, const char *uri[MAX_URI])
72 {
73         int n = 0;
74         FILE *f = fopen(git_path("remotes/%s", repo), "r");
75
76         if (!f)
77                 return -1;
78         while (fgets(buffer, BUF_SIZE, f)) {
79                 char *s, *p;
80
81                 if (strncmp("URL: ", buffer, 5))
82                         continue;
83                 s = buffer + 5;
84
85                 /* Remove whitespace at the head.. */
86                 while (isspace(*s))
87                         s++;
88                 if (!*s)
89                         continue;
90
91                 /* ..and at the end */
92                 p = s + strlen(s);
93                 while (isspace(p[-1]))
94                         *--p = 0;
95
96                 uri[n++] = strdup(s);
97                 if (n == MAX_URI)
98                         break;
99         }
100         fclose(f);
101         if (!n)
102                 die("remote '%s' has no URL", repo);
103         return n;
104 }
105
106 static int get_branches_uri(const char *repo, const char *uri[MAX_URI])
107 {
108         const char *slash = strchr(repo, '/');
109         int n = slash ? slash - repo : 1000;
110         FILE *f = fopen(git_path("branches/%.*s", n, repo), "r");
111         char *s, *p;
112         int len;
113
114         if (!f)
115                 return 0;
116         s = fgets(buffer, BUF_SIZE, f);
117         fclose(f);
118         if (!s)
119                 return 0;
120         while (isspace(*s))
121                 s++;
122         if (!*s)
123                 return 0;
124         p = s + strlen(s);
125         while (isspace(p[-1]))
126                 *--p = 0;
127         len = p - s;
128         if (slash)
129                 len += strlen(slash);
130         p = xmalloc(len + 1);
131         strcpy(p, s);
132         if (slash)
133                 strcat(p, slash);
134         uri[0] = p;
135         return 1;
136 }
137
138 static int get_uri(const char *repo, const char *uri[MAX_URI])
139 {
140         int n;
141
142         if (*repo != '/') {
143                 n = get_remotes_uri(repo, uri);
144                 if (n > 0)
145                         return n;
146
147                 n = get_branches_uri(repo, uri);
148                 if (n > 0)
149                         return n;
150         }
151
152         uri[0] = repo;
153         return 1;
154 }
155
156 static int do_push(const char *repo)
157 {
158         const char *uri[MAX_URI];
159         int i, n = get_uri(repo, uri);
160         int remote;
161         const char **argv;
162         int argc;
163
164         n = get_uri(repo, uri);
165         if (n <= 0)
166                 die("bad repository '%s'", repo);
167
168         argv = xmalloc((refspec_nr + 10) * sizeof(char *));
169         argv[0] = "dummy-send-pack";
170         argc = 1;
171         if (all)
172                 argv[argc++] = "--all";
173         if (force)
174                 argv[argc++] = "--force";
175         if (execute)
176                 argv[argc++] = execute;
177         if (thin)
178                 argv[argc++] = "--thin";
179         remote = argc;
180         argv[argc++] = "dummy-remote";
181         while (refspec_nr--)
182                 argv[argc++] = *refspec++;
183         argv[argc] = NULL;
184
185         for (i = 0; i < n; i++) {
186                 int error;
187                 const char *dest = uri[i];
188                 const char *sender = "git-send-pack";
189                 if (!strncmp(dest, "http://", 7) ||
190                     !strncmp(dest, "https://", 8))
191                         sender = "git-http-push";
192                 argv[0] = sender;
193                 argv[remote] = dest;
194                 error = run_command_v(argc, argv);
195                 if (!error)
196                         continue;
197                 switch (error) {
198                 case -ERR_RUN_COMMAND_FORK:
199                         die("unable to fork for %s", sender);
200                 case -ERR_RUN_COMMAND_EXEC:
201                         die("unable to exec %s", sender);
202                 case -ERR_RUN_COMMAND_WAITPID:
203                 case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
204                 case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
205                 case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
206                         die("%s died with strange error", sender);
207                 default:
208                         return -error;
209                 }
210         }
211         return 0;
212 }
213
214 int cmd_push(int argc, const char **argv, char **envp)
215 {
216         int i;
217         const char *repo = "origin";    // default repository
218
219         for (i = 1; i < argc; i++) {
220                 const char *arg = argv[i];
221
222                 if (arg[0] != '-') {
223                         repo = arg;
224                         i++;
225                         break;
226                 }
227                 if (!strcmp(arg, "--all")) {
228                         all = 1;
229                         continue;
230                 }
231                 if (!strcmp(arg, "--tags")) {
232                         tags = 1;
233                         continue;
234                 }
235                 if (!strcmp(arg, "--force")) {
236                         force = 1;
237                         continue;
238                 }
239                 if (!strcmp(arg, "--thin")) {
240                         thin = 1;
241                         continue;
242                 }
243                 if (!strcmp(arg, "--no-thin")) {
244                         thin = 0;
245                         continue;
246                 }
247                 if (!strncmp(arg, "--exec=", 7)) {
248                         execute = arg;
249                         continue;
250                 }
251                 usage(push_usage);
252         }
253         set_refspecs(argv + i, argc - i);
254         return do_push(repo);
255 }