Linus Torvalds | 755225d | 2006-04-29 21:22:49 -0700 | [diff] [blame] | 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"); |
Junio C Hamano | 7aaf83d | 2006-04-30 16:03:27 -0700 | [diff] [blame] | 75 | int has_explicit_refspec = refspec_nr; |
Linus Torvalds | 755225d | 2006-04-29 21:22:49 -0700 | [diff] [blame] | 76 | |
| 77 | if (!f) |
| 78 | return -1; |
| 79 | while (fgets(buffer, BUF_SIZE, f)) { |
Johannes Schindelin | 5c477b9 | 2006-04-30 14:05:55 +0200 | [diff] [blame] | 80 | int is_refspec; |
Linus Torvalds | 755225d | 2006-04-29 21:22:49 -0700 | [diff] [blame] | 81 | char *s, *p; |
| 82 | |
Johannes Schindelin | 5c477b9 | 2006-04-30 14:05:55 +0200 | [diff] [blame] | 83 | if (!strncmp("URL: ", buffer, 5)) { |
| 84 | is_refspec = 0; |
| 85 | s = buffer + 5; |
| 86 | } else if (!strncmp("Push: ", buffer, 6)) { |
| 87 | is_refspec = 1; |
| 88 | s = buffer + 6; |
| 89 | } else |
Linus Torvalds | 755225d | 2006-04-29 21:22:49 -0700 | [diff] [blame] | 90 | continue; |
Linus Torvalds | 755225d | 2006-04-29 21:22:49 -0700 | [diff] [blame] | 91 | |
| 92 | /* Remove whitespace at the head.. */ |
| 93 | while (isspace(*s)) |
| 94 | s++; |
| 95 | if (!*s) |
| 96 | continue; |
| 97 | |
| 98 | /* ..and at the end */ |
| 99 | p = s + strlen(s); |
| 100 | while (isspace(p[-1])) |
| 101 | *--p = 0; |
| 102 | |
Junio C Hamano | 7aaf83d | 2006-04-30 16:03:27 -0700 | [diff] [blame] | 103 | if (!is_refspec) { |
| 104 | if (n < MAX_URI) |
| 105 | uri[n++] = strdup(s); |
| 106 | else |
| 107 | error("more than %d URL's specified, ignoreing the rest", MAX_URI); |
| 108 | } |
| 109 | else if (is_refspec && !has_explicit_refspec) |
| 110 | add_refspec(strdup(s)); |
Linus Torvalds | 755225d | 2006-04-29 21:22:49 -0700 | [diff] [blame] | 111 | } |
| 112 | fclose(f); |
| 113 | if (!n) |
| 114 | die("remote '%s' has no URL", repo); |
| 115 | return n; |
| 116 | } |
| 117 | |
Johannes Schindelin | 97d4df0 | 2006-04-30 15:23:48 +0200 | [diff] [blame^] | 118 | static const char **config_uri; |
| 119 | static const char *config_repo; |
| 120 | static int config_repo_len; |
| 121 | static int config_current_uri; |
| 122 | static int config_get_refspecs; |
| 123 | |
| 124 | static int get_remote_config(const char* key, const char* value) |
| 125 | { |
| 126 | if (!strncmp(key, "remote.", 7) && |
| 127 | !strncmp(key + 7, config_repo, config_repo_len)) { |
| 128 | if (!strcmp(key + 7 + config_repo_len, ".url")) { |
| 129 | if (config_current_uri < MAX_URI) |
| 130 | config_uri[config_current_uri++] = strdup(value); |
| 131 | else |
| 132 | error("more than %d URL's specified, ignoring the rest", MAX_URI); |
| 133 | } |
| 134 | else if (config_get_refspecs && |
| 135 | !strcmp(key + 7 + config_repo_len, ".push")) |
| 136 | add_refspec(strdup(value)); |
| 137 | } |
| 138 | return 0; |
| 139 | } |
| 140 | |
| 141 | static int get_config_remotes_uri(const char *repo, const char *uri[MAX_URI]) |
| 142 | { |
| 143 | config_repo_len = strlen(repo); |
| 144 | config_repo = repo; |
| 145 | config_current_uri = 0; |
| 146 | config_uri = uri; |
| 147 | config_get_refspecs = !refspec_nr; |
| 148 | |
| 149 | git_config(get_remote_config); |
| 150 | return config_current_uri; |
| 151 | } |
| 152 | |
Linus Torvalds | 755225d | 2006-04-29 21:22:49 -0700 | [diff] [blame] | 153 | static int get_branches_uri(const char *repo, const char *uri[MAX_URI]) |
| 154 | { |
| 155 | const char *slash = strchr(repo, '/'); |
| 156 | int n = slash ? slash - repo : 1000; |
| 157 | FILE *f = fopen(git_path("branches/%.*s", n, repo), "r"); |
| 158 | char *s, *p; |
| 159 | int len; |
| 160 | |
| 161 | if (!f) |
| 162 | return 0; |
| 163 | s = fgets(buffer, BUF_SIZE, f); |
| 164 | fclose(f); |
| 165 | if (!s) |
| 166 | return 0; |
| 167 | while (isspace(*s)) |
| 168 | s++; |
| 169 | if (!*s) |
| 170 | return 0; |
| 171 | p = s + strlen(s); |
| 172 | while (isspace(p[-1])) |
| 173 | *--p = 0; |
| 174 | len = p - s; |
| 175 | if (slash) |
| 176 | len += strlen(slash); |
| 177 | p = xmalloc(len + 1); |
| 178 | strcpy(p, s); |
| 179 | if (slash) |
| 180 | strcat(p, slash); |
| 181 | uri[0] = p; |
| 182 | return 1; |
| 183 | } |
| 184 | |
Junio C Hamano | 7aaf83d | 2006-04-30 16:03:27 -0700 | [diff] [blame] | 185 | /* |
| 186 | * Read remotes and branches file, fill the push target URI |
| 187 | * list. If there is no command line refspecs, read Push: lines |
| 188 | * to set up the *refspec list as well. |
| 189 | * return the number of push target URIs |
| 190 | */ |
| 191 | static int read_config(const char *repo, const char *uri[MAX_URI]) |
Linus Torvalds | 755225d | 2006-04-29 21:22:49 -0700 | [diff] [blame] | 192 | { |
| 193 | int n; |
| 194 | |
| 195 | if (*repo != '/') { |
| 196 | n = get_remotes_uri(repo, uri); |
| 197 | if (n > 0) |
| 198 | return n; |
| 199 | |
Johannes Schindelin | 97d4df0 | 2006-04-30 15:23:48 +0200 | [diff] [blame^] | 200 | n = get_config_remotes_uri(repo, uri); |
| 201 | if (n > 0) |
| 202 | return n; |
| 203 | |
Linus Torvalds | 755225d | 2006-04-29 21:22:49 -0700 | [diff] [blame] | 204 | n = get_branches_uri(repo, uri); |
| 205 | if (n > 0) |
| 206 | return n; |
| 207 | } |
| 208 | |
| 209 | uri[0] = repo; |
| 210 | return 1; |
| 211 | } |
| 212 | |
| 213 | static int do_push(const char *repo) |
| 214 | { |
| 215 | const char *uri[MAX_URI]; |
Junio C Hamano | 7aaf83d | 2006-04-30 16:03:27 -0700 | [diff] [blame] | 216 | int i, n; |
Linus Torvalds | 755225d | 2006-04-29 21:22:49 -0700 | [diff] [blame] | 217 | int remote; |
| 218 | const char **argv; |
| 219 | int argc; |
| 220 | |
Junio C Hamano | 7aaf83d | 2006-04-30 16:03:27 -0700 | [diff] [blame] | 221 | n = read_config(repo, uri); |
Linus Torvalds | 755225d | 2006-04-29 21:22:49 -0700 | [diff] [blame] | 222 | if (n <= 0) |
| 223 | die("bad repository '%s'", repo); |
| 224 | |
Linus Torvalds | 755225d | 2006-04-29 21:22:49 -0700 | [diff] [blame] | 225 | argv = xmalloc((refspec_nr + 10) * sizeof(char *)); |
| 226 | argv[0] = "dummy-send-pack"; |
| 227 | argc = 1; |
| 228 | if (all) |
| 229 | argv[argc++] = "--all"; |
| 230 | if (force) |
| 231 | argv[argc++] = "--force"; |
| 232 | if (execute) |
| 233 | argv[argc++] = execute; |
| 234 | if (thin) |
| 235 | argv[argc++] = "--thin"; |
| 236 | remote = argc; |
| 237 | argv[argc++] = "dummy-remote"; |
| 238 | while (refspec_nr--) |
| 239 | argv[argc++] = *refspec++; |
| 240 | argv[argc] = NULL; |
| 241 | |
| 242 | for (i = 0; i < n; i++) { |
| 243 | int error; |
| 244 | const char *dest = uri[i]; |
| 245 | const char *sender = "git-send-pack"; |
| 246 | if (!strncmp(dest, "http://", 7) || |
| 247 | !strncmp(dest, "https://", 8)) |
| 248 | sender = "git-http-push"; |
| 249 | argv[0] = sender; |
| 250 | argv[remote] = dest; |
| 251 | error = run_command_v(argc, argv); |
| 252 | if (!error) |
| 253 | continue; |
| 254 | switch (error) { |
| 255 | case -ERR_RUN_COMMAND_FORK: |
| 256 | die("unable to fork for %s", sender); |
| 257 | case -ERR_RUN_COMMAND_EXEC: |
| 258 | die("unable to exec %s", sender); |
| 259 | case -ERR_RUN_COMMAND_WAITPID: |
| 260 | case -ERR_RUN_COMMAND_WAITPID_WRONG_PID: |
| 261 | case -ERR_RUN_COMMAND_WAITPID_SIGNAL: |
| 262 | case -ERR_RUN_COMMAND_WAITPID_NOEXIT: |
| 263 | die("%s died with strange error", sender); |
| 264 | default: |
| 265 | return -error; |
| 266 | } |
| 267 | } |
| 268 | return 0; |
| 269 | } |
| 270 | |
| 271 | int cmd_push(int argc, const char **argv, char **envp) |
| 272 | { |
| 273 | int i; |
| 274 | const char *repo = "origin"; // default repository |
| 275 | |
| 276 | for (i = 1; i < argc; i++) { |
| 277 | const char *arg = argv[i]; |
| 278 | |
| 279 | if (arg[0] != '-') { |
| 280 | repo = arg; |
| 281 | i++; |
| 282 | break; |
| 283 | } |
| 284 | if (!strcmp(arg, "--all")) { |
| 285 | all = 1; |
| 286 | continue; |
| 287 | } |
| 288 | if (!strcmp(arg, "--tags")) { |
| 289 | tags = 1; |
| 290 | continue; |
| 291 | } |
| 292 | if (!strcmp(arg, "--force")) { |
| 293 | force = 1; |
| 294 | continue; |
| 295 | } |
| 296 | if (!strcmp(arg, "--thin")) { |
| 297 | thin = 1; |
| 298 | continue; |
| 299 | } |
| 300 | if (!strcmp(arg, "--no-thin")) { |
| 301 | thin = 0; |
| 302 | continue; |
| 303 | } |
| 304 | if (!strncmp(arg, "--exec=", 7)) { |
| 305 | execute = arg; |
| 306 | continue; |
| 307 | } |
| 308 | usage(push_usage); |
| 309 | } |
| 310 | set_refspecs(argv + i, argc - i); |
| 311 | return do_push(repo); |
| 312 | } |