From: Ramkumar Ramachandra Date: Fri, 11 Feb 2011 15:49:15 GMT Subject: Re: Plans for the vcs-svn-pu branch Message-ID: <20110211154910.GB7335@kytes> In-Reply-To: <20110211090931.GA27410@elie> Hi, Jonathan Nieder writes: > Jonathan Nieder wrote: > > > Here are the topics that are cooking in vcs-svn-pu. Thanks for the elaborate email. Some updates from my side: - I've rewritten most of the svnload parser to resemble fast-import, and I'd like some preliminary feedback on the design. - Although most of the dependent infrastructure is in place now, the remote-helper branch is still lagging. I'll shortly look into this. Signed-off-by: Ramkumar Ramachandra --8<-- /* * Produce a dumpfile v3 from a fast-import stream. * Load the dump into the SVN repository with: * svnrdump load 1170199019 +0530 */ strbuf_reset(&(identp->name)); strbuf_reset(&(identp->email)); if (!buf) goto error; if (!(tz_off = strrchr(buf, ' '))) goto error; *tz_off++ = '\0'; if (!(t = strrchr(buf, ' '))) goto error; *(t - 1) = '\0'; /* Ignore '>' from email */ t++; tz_off_buf = atoi(tz_off); if (tz_off_buf > 1200 || tz_off_buf < -1200) goto error; tm_time = time_to_tm(strtoul(t, NULL, 10), tz_off_buf); strftime(identp->date, SVN_DATE_LEN + 1, SVN_DATE_FORMAT, tm_time); if (!(t = strchr(buf, '<'))) goto error; *(t - 1) = '\0'; /* Ignore ' <' from email */ t++; strbuf_add(&(identp->email), t, strlen(t)); strbuf_add(&(identp->name), buf, strlen(buf)); return; error: die("Malformed ident line: %s", buf); } static void skip_optional_lf(void) { int term_char = fgetc(stdin); if (term_char != '\n' && term_char != EOF) ungetc(term_char, stdin); } static void parse_data(struct strbuf *dst) { if (prefixcmp(command_buf.buf, "data ")) die("Expected 'data n' command, found: %s", command_buf.buf); if (!prefixcmp(command_buf.buf + 5, "<<")) { char *term = xstrdup(command_buf.buf + 5 + 2); size_t term_len = command_buf.len - 5 - 2; strbuf_reset(&command_buf); for (;;) { if (read_next_command() == EOF) die("EOF in data (terminator '%s' not found)", term); if (term_len == command_buf.len && !memcmp(term, command_buf.buf, term_len)) break; if (dst) { strbuf_addbuf(dst, &command_buf); strbuf_addch(dst, '\n'); } else printf("%s\n", command_buf.buf); } free(term); } else { uintmax_t length; length = strtoumax(command_buf.buf + 5, NULL, 10); if ((size_t)length < length) die("Data is too large to use in this context"); if (!dst) { strbuf_reset(&command_buf); /* buffer_copy_bytes(&command_buf, (size_t)length); */ } else strbuf_fread(dst, (size_t)length, stdin); } skip_optional_lf(); } static const char *get_mode(const char *str, uint16_t *modep) { unsigned char c; uint16_t mode = 0; while ((c = *str++) != ' ') { if (c < '0' || c > '7') return NULL; mode = (mode << 3) + (c - '0'); } *modep = mode; return str; } static void file_change_m(void) { const char *p; const char *endp; uint16_t mode; enum node_kind kind; p = get_mode(command_buf.buf + 2, &mode); if (!p) die("Corrupt mode: %s", command_buf.buf); switch (mode) { case 0644: case 0755: mode |= S_IFREG; case S_IFREG | 0644: kind = NODE_KIND_NORMAL; break; case S_IFREG | 0755: kind = NODE_KIND_EXECUTABLE; break; case S_IFLNK: kind = NODE_KIND_SYMLINK; break; case S_IFGITLINK: die("Gitlinks unsupported"); /* TODO */ case S_IFDIR: die("Subdirectories unsupported"); /* TODO */ default: die("Corrupt mode: %s", command_buf.buf); } if (!prefixcmp(p, "inline")) p += 6; else die ("Non-inlined data unsupported"); if (*p++ != ' ') die("Missing space after dataref: %s", command_buf.buf); /* parse out path into path_d */ strbuf_reset(&path_d); if (!unquote_c_style(&path_d, p, &endp)) { if (*endp) die("Garbage after path in: %s", command_buf.buf); } else strbuf_addstr(&path_d, p); dump_export_m(path_d.buf, kind); read_next_command(); parse_data(NULL); /* parse data and write it to stdout */ } static void file_change_d(void) { const char *p; const char *endp; p = command_buf.buf + 2; /* parse out path into path_d */ strbuf_reset(&path_d); if (!unquote_c_style(&path_d, p, &endp)) { if (*endp) die("Garbage after path in: %s", command_buf.buf); } else strbuf_addstr(&path_d, p); dump_export_d(path_d.buf); } static void file_change_cr(int rename) { const char *p; const char *endp; p = command_buf.buf + 2; strbuf_reset(&path_s); if (!unquote_c_style(&path_s, p, &endp)) { if (*endp != ' ') die("Missing space after source: %s", command_buf.buf); } else { endp = strchr(p, ' '); if (!endp) die("Missing space after source: %s", command_buf.buf); strbuf_add(&path_s, p, endp - p); } endp++; if (!*endp) die("Missing destination: %s", command_buf.buf); p = endp; strbuf_reset(&path_d); if (!unquote_c_style(&path_d, p, &endp)) { if (*endp) die("Garbage after destination in: %s", command_buf.buf); } else strbuf_addstr(&path_d, p); /* TODO: Check C "path/to/subdir" "" */ if (rename) dump_export_d(path_s.buf); dump_export_c(path_d.buf, path_s.buf, 0); } static void parse_new_commit() { char *branch; /* parse and ignore branch name */ branch = strchr(command_buf.buf, ' ') + 1; read_next_command(); if (!prefixcmp(command_buf.buf, "mark :")) /* parse and ignore mark line */ read_next_command(); if (!prefixcmp(command_buf.buf, "author ")) { parse_ident(command_buf.buf + 7, &author); read_next_command(); } if (!prefixcmp(command_buf.buf, "committer ")) { parse_ident(command_buf.buf + 10, &committer); read_next_command(); } if (!committer.name.len) die("Missing committer line in stream"); parse_data(&log_buf); read_next_command(); if (!prefixcmp(command_buf.buf, "from ")) /* TODO: Support copyfrom */ read_next_command(); while (!prefixcmp(command_buf.buf, "merge ")) /* TODO: Support merges */ read_next_command(); /* file_change_* */ while (command_buf.len > 0) { if (!prefixcmp(command_buf.buf, "M ")) file_change_m(); else if (!prefixcmp(command_buf.buf, "D ")) file_change_d(); else if (!prefixcmp(command_buf.buf, "R ")) file_change_cr(1); else if (!prefixcmp(command_buf.buf, "C ")) file_change_cr(0); else if (!prefixcmp(command_buf.buf, "N ")) ; /* ignored */ else if (!prefixcmp(command_buf.buf, "ls ")) goto error; /* TODO */ else if (!strcmp("deleteall", command_buf.buf)) goto error; /* TODO */ else break; if (read_next_command() == EOF) break; } return; error: die("Unsupported command: %s", command_buf.buf); } void parse_new_tag() { /* TODO: Support tags */ return; } void parse_reset_branch() { /* TODO */ return; } void build_svn_author(struct ident *author, struct ident *committer) { char *t, *email; strbuf_reset(&svn_author); email = author->email.len ? author->email.buf : committer->email.buf; if ((t = strchr(email, '@'))) strbuf_add(&svn_author, email, t - email); else strbuf_addstr(&svn_author, t); } void svnload_read(void) { char *val; while (read_next_command() != EOF) { if ((val = strchr(command_buf.buf, ' '))) *val++ = '\0'; if (!strcmp("blob", command_buf.buf)) die("Non-inlined blobs unsupported"); else if (!prefixcmp(command_buf.buf, "ls ")) goto error; /* TODO */ else if (!prefixcmp(command_buf.buf, "cat-blob ")) goto error; /* TODO */ else if (!prefixcmp(command_buf.buf, "commit ")) parse_new_commit(val); else if (!prefixcmp(command_buf.buf, "tag ")) parse_new_tag(val); else if (!prefixcmp(command_buf.buf, "reset ")) parse_reset_branch(val); else if (!strcmp(command_buf.buf, "checkpoint") || !prefixcmp(command_buf.buf, "progress ") || !prefixcmp(command_buf.buf, "feature ") || !prefixcmp(command_buf.buf, "option ")) ; /* ignored */ else goto error; } error: die("Unsupported command: %s", command_buf.buf); } int svnload_init(const char *filename) { if (!(infile = filename ? fopen(filename, "r") : stdin)) die("Cannot open %s: %s", filename, strerror(errno)); dump_export_init(); return 0; }