X-Git-Url: https://git.octo.it/?a=blobdiff_plain;ds=sidebyside;f=send-pack.c;h=02d1d52c2475759f2b206912bbd626f749a6b750;hb=d289d13625ffa568c3007c3620eaafeb521ac06d;hp=93674d8e4f037088926e6a558fafefa91b8eff96;hpb=d0efc8a71da1855c705fd2074b219bcb158b6dbd;p=git.git diff --git a/send-pack.c b/send-pack.c index 93674d8e..02d1d52c 100644 --- a/send-pack.c +++ b/send-pack.c @@ -1,31 +1,11 @@ #include "cache.h" +#include "refs.h" #include "pkt-line.h" -#include -static const char send_pack_usage[] = "git-send-pack [--exec=other] destination [heads]*"; +static const char send_pack_usage[] = +"git-send-pack [--exec=git-receive-pack] [host:]directory [heads]*"; static const char *exec = "git-receive-pack"; -static int path_match(const char *path, int nr, char **match) -{ - int i; - int pathlen = strlen(path); - - for (i = 0; i < nr; i++) { - char *s = match[i]; - int len = strlen(s); - - if (!len || len > pathlen) - continue; - if (memcmp(path + pathlen - len, s, len)) - continue; - if (pathlen > len && path[pathlen - len - 1] != '/') - continue; - *s = 0; - return 1; - } - return 0; -} - struct ref { struct ref *next; unsigned char old_sha1[20]; @@ -33,6 +13,17 @@ struct ref { char name[0]; }; +static int is_zero_sha1(const unsigned char *sha1) +{ + int i; + + for (i = 0; i < 20; i++) { + if (*sha1++) + return 0; + } + return 1; +} + static void exec_pack_objects(void) { static char *args[] = { @@ -55,11 +46,15 @@ static void exec_rev_list(struct ref *refs) char *buf = malloc(100); if (i > 900) die("git-rev-list environment overflow"); - args[i++] = buf; - snprintf(buf, 50, "^%s", sha1_to_hex(refs->old_sha1)); - buf += 50; - args[i++] = buf; - snprintf(buf, 50, "%s", sha1_to_hex(refs->new_sha1)); + if (!is_zero_sha1(refs->old_sha1)) { + args[i++] = buf; + snprintf(buf, 50, "^%s", sha1_to_hex(refs->old_sha1)); + buf += 50; + } + if (!is_zero_sha1(refs->new_sha1)) { + args[i++] = buf; + snprintf(buf, 50, "%s", sha1_to_hex(refs->new_sha1)); + } refs = refs->next; } args[i] = NULL; @@ -108,17 +103,15 @@ static int pack_objects(int fd, struct ref *refs) * We don't wait for the rev-list pipeline in the parent: * we end up waiting for the other end instead */ + return 0; } static int read_ref(const char *ref, unsigned char *sha1) { int fd, ret; - static char pathname[PATH_MAX]; char buffer[60]; - const char *git_dir = gitenv(GIT_DIR_ENVIRONMENT) ? : DEFAULT_GIT_DIR_ENVIRONMENT; - snprintf(pathname, sizeof(pathname), "%s/%s", git_dir, ref); - fd = open(pathname, O_RDONLY); + fd = open(git_path("%s", ref), O_RDONLY); if (fd < 0) return -1; ret = -1; @@ -128,14 +121,54 @@ static int read_ref(const char *ref, unsigned char *sha1) return ret; } +static int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1) +{ + if (!has_sha1_file(old_sha1)) + return 0; + /* + * FIXME! It is not correct to say that the new one is newer + * just because we don't have the old one! + * + * We should really see if we can reach the old_sha1 commit + * from the new_sha1 one. + */ + return 1; +} + +static int local_ref_nr_match; +static char **local_ref_match; +static struct ref **local_ref_list; + +static int try_to_match(const char *refname, const unsigned char *sha1) +{ + struct ref *ref; + int len; + + if (!path_match(refname, local_ref_nr_match, local_ref_match)) + return 0; + + len = strlen(refname)+1; + ref = xmalloc(sizeof(*ref) + len); + memset(ref->old_sha1, 0, 20); + memcpy(ref->new_sha1, sha1, 20); + memcpy(ref->name, refname, len); + ref->next = NULL; + *local_ref_list = ref; + local_ref_list = &ref->next; + return 0; +} + static int send_pack(int in, int out, int nr_match, char **match) { struct ref *ref_list = NULL, **last_ref = &ref_list; struct ref *ref; + int new_refs; + /* + * Read all the refs from the other end + */ for (;;) { unsigned char old_sha1[20]; - unsigned char new_sha1[20]; static char buffer[1000]; char *name; int len; @@ -149,27 +182,62 @@ static int send_pack(int in, int out, int nr_match, char **match) if (len < 42 || get_sha1_hex(buffer, old_sha1) || buffer[40] != ' ') die("protocol error: expected sha/ref, got '%s'", buffer); name = buffer + 41; + ref = xmalloc(sizeof(*ref) + len - 40); + memcpy(ref->old_sha1, old_sha1, 20); + memset(ref->new_sha1, 0, 20); + memcpy(ref->name, buffer + 41, len - 40); + ref->next = NULL; + *last_ref = ref; + last_ref = &ref->next; + } + + /* + * Go through the refs, see if we want to update + * any of them.. + */ + for (ref = ref_list; ref; ref = ref->next) { + unsigned char new_sha1[20]; + char *name = ref->name; + if (nr_match && !path_match(name, nr_match, match)) continue; + if (read_ref(name, new_sha1) < 0) - return error("no such local reference '%s'", name); - if (!has_sha1_file(old_sha1)) - return error("remote '%s' points to object I don't have", name); - if (!memcmp(old_sha1, new_sha1, 20)) { + continue; + + if (!memcmp(ref->old_sha1, new_sha1, 20)) { fprintf(stderr, "'%s' unchanged\n", name); continue; } - ref = xmalloc(sizeof(*ref) + len - 40); - memcpy(ref->old_sha1, old_sha1, 20); + + if (!ref_newer(new_sha1, ref->old_sha1)) { + error("remote '%s' points to object I don't have", name); + continue; + } + + /* Ok, mark it for update */ memcpy(ref->new_sha1, new_sha1, 20); - memcpy(ref->name, buffer + 41, len - 40); - ref->next = NULL; - *last_ref = ref; - last_ref = &ref->next; } + /* + * See if we have any refs that the other end didn't have + */ + if (nr_match) { + local_ref_nr_match = nr_match; + local_ref_match = match; + local_ref_list = last_ref; + for_each_ref(try_to_match); + } + + /* + * Finally, tell the other end! + */ + new_refs = 0; for (ref = ref_list; ref; ref = ref->next) { char old_hex[60], *new_hex; + if (is_zero_sha1(ref->new_sha1)) + continue; + new_refs++; strcpy(old_hex, sha1_to_hex(ref->old_sha1)); new_hex = sha1_to_hex(ref->new_sha1); packet_write(out, "%s %s %s", old_hex, new_hex, ref->name); @@ -177,82 +245,12 @@ static int send_pack(int in, int out, int nr_match, char **match) } packet_flush(out); - if (ref_list) + if (new_refs) pack_objects(out, ref_list); close(out); return 0; } -/* - * First, make it shell-safe. We do this by just disallowing any - * special characters. Somebody who cares can do escaping and let - * through the rest. But since we're doing to feed this to ssh as - * a command line, we're going to be pretty damn anal for now. - */ -static char *shell_safe(char *url) -{ - char *n = url; - unsigned char c; - static const char flags[256] = { - ['0'...'9'] = 1, - ['a'...'z'] = 1, - ['A'...'Z'] = 1, - ['.'] = 1, ['/'] = 1, - ['-'] = 1, ['+'] = 1, - [':'] = 1 - }; - - while ((c = *n++) != 0) { - if (flags[c] != 1) - die("I don't like '%c'. Sue me.", c); - } - return url; -} - -/* - * Yeah, yeah, fixme. Need to pass in the heads etc. - */ -static int setup_connection(int fd[2], char *url, char **heads) -{ - char command[1024]; - const char *host, *path; - char *colon; - int pipefd[2][2]; - pid_t pid; - - url = shell_safe(url); - host = NULL; - path = url; - colon = strchr(url, ':'); - if (colon) { - *colon = 0; - host = url; - path = colon+1; - } - snprintf(command, sizeof(command), "%s %s", exec, path); - if (pipe(pipefd[0]) < 0 || pipe(pipefd[1]) < 0) - die("unable to create pipe pair for communication"); - pid = fork(); - if (!pid) { - dup2(pipefd[1][0], 0); - dup2(pipefd[0][1], 1); - close(pipefd[0][0]); - close(pipefd[0][1]); - close(pipefd[1][0]); - close(pipefd[1][1]); - if (host) - execlp("ssh", "ssh", host, command, NULL); - else - execlp("sh", "sh", "-c", command, NULL); - die("exec failed"); - } - fd[0] = pipefd[0][0]; - fd[1] = pipefd[1][1]; - close(pipefd[0][1]); - close(pipefd[1][0]); - return pid; -} - int main(int argc, char **argv) { int i, nr_heads = 0; @@ -279,12 +277,12 @@ int main(int argc, char **argv) } if (!dest) usage(send_pack_usage); - pid = setup_connection(fd, dest, heads); + pid = git_connect(fd, dest, exec); if (pid < 0) return 1; ret = send_pack(fd[0], fd[1], nr_heads, heads); close(fd[0]); close(fd[1]); - waitpid(pid, NULL, 0); + finish_connect(pid); return ret; }