git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: Plans for the vcs-svn-pu branch

From
Ramkumar Ramachandra <artagnon@gmail.com>
Date
Feb 11, 2011, 15:49 UTC
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 <artagnon@gmail.com>
--8<--
/*
 * Produce a dumpfile v3 from a fast-import stream.
 * Load the dump into the SVN repository with:
 * svnrdump load <URL> <dumpfile
 *
 * Licensed under a two-clause BSD-style license.
 * See LICENSE for details.
 */

#include "cache.h" #include "quote.h" #include "git-compat-util.h" #include "dump_export.h" #include "dir_cache.h"

#define SVN_DATE_FORMAT "%Y-%m-%dT%H:%M:%S.000000Z" #define SVN_DATE_LEN 27

struct ident
{
	struct strbuf name, email;
	char date[SVN_DATE_LEN + 1];
};

static FILE *infile; static struct strbuf command_buf = STRBUF_INIT; static struct strbuf log_buf = STRBUF_INIT; static struct strbuf path_s = STRBUF_INIT; static struct strbuf path_d = STRBUF_INIT; static struct strbuf svn_author = STRBUF_INIT; static struct ident author = {STRBUF_INIT, STRBUF_INIT, ""}; static struct ident committer = {STRBUF_INIT, STRBUF_INIT, ""};

static int read_next_command(void)
{
	return strbuf_getline(&command_buf, infile, '\n');
}
static void populate_revprops(struct strbuf *props, size_t author_len,
			const char *author, size_t log_len, const char *log,
			size_t date_len, const char *date)
{
	strbuf_reset(props);
	strbuf_addf(props, "K 10\nsvn:author\nV %lu\n%s\n", author_len, author);
	strbuf_addf(props, "K 7\nsvn:log\nV %lu\n%s\n", log_len, log);
	if (date_len)
		/* SVN doesn't like an empty svn:date value */
		strbuf_addf(props, "K 8\nsvn:date\nV %lu\n%s\n", date_len, date);
	strbuf_add(props, "PROPS-END\n", 10);
}
static void parse_ident(const char *buf, struct ident *identp)
{
	char *t, *tz_off;
	int tz_off_buf;
	const struct tm *tm_time;
	/* John Doe <johndoe@email.com> 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;
}
Previous: Jonathan Nieder
Message 22 of 22 in “Status of the svn remote helper project (Nov, 2010)”
  1. Jonathan NiederNov 7, 2010
  2. David Michael BarrNov 7, 2010
  3. David BarrNov 8, 2010
  4. Jonathan NiederNov 8, 2010
  5. David BarrNov 8, 2010
  6. Ramkumar RamachandraNov 7, 2010
  7. Jonathan NiederNov 7, 2010
  8. Status of the svn remote helper project (Nov 2010, #2)Jonathan Nieder, Nov 21, 2010
  9. David Michael BarrNov 21, 2010
  10. Jonathan NiederNov 21, 2010
  11. David BarrNov 22, 2010
  12. Status of the svn remote helper project (Dec 2010, #1)Jonathan Nieder, Dec 5, 2010
  13. Tomas CarneckyDec 8, 2010
  14. fast-import tweaks for remote helpers (Re: Status of the svn remote helper project (Dec 2010, #1))Jonathan Nieder, Dec 12, 2010
  15. Sam VilainDec 12, 2010
  16. Jonathan NiederDec 12, 2010
  17. fast-import --report-fd (Re: fast-import tweaks for remote helpers)Jonathan Nieder, Jan 5, 2011
  18. Status of the svn remote helper project (Jan 2011, #1)Jonathan Nieder, Jan 5, 2011
  19. David Michael BarrJan 7, 2011
  20. Plans for the vcs-svn-pu branchJonathan Nieder, Feb 11, 2011
  21. svn-fe: warn about experimental statusJonathan Nieder, Feb 11, 2011
  22. Ramkumar RamachandraFeb 11, 2011

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.