Merge branch 'db/vcs-svn-incremental' into svn-fe

This teaches svn-fe to incrementally import into an existing
repository (at last!) at the expense of less convenient UI.  Think of
it as growing pains.  This opens the door to many excellent things,
and it would be a bad idea to discourage people from building on it
for much longer.

* db/vcs-svn-incremental:
  vcs-svn: avoid using ls command twice
  vcs-svn: use mark from previous import for parent commit
  vcs-svn: handle filenames with dq correctly
  vcs-svn: quote paths correctly for ls command
  vcs-svn: eliminate repo_tree structure
  vcs-svn: add a comment before each commit
  vcs-svn: save marks for imported commits
  vcs-svn: use higher mark numbers for blobs
  vcs-svn: set up channel to read fast-import cat-blob response

Conflicts:
	t/t9010-svn-fe.sh
	vcs-svn/fast_export.c
	vcs-svn/fast_export.h
	vcs-svn/repo_tree.c
	vcs-svn/svndump.c
This commit is contained in:
Jonathan Nieder
2011-05-26 01:51:38 -05:00
9 changed files with 422 additions and 413 deletions

View File

@ -20,15 +20,19 @@
*/
#define constcmp(s, ref) memcmp(s, ref, sizeof(ref) - 1)
#define REPORT_FILENO 3
#define NODEACT_REPLACE 4
#define NODEACT_DELETE 3
#define NODEACT_ADD 2
#define NODEACT_CHANGE 1
#define NODEACT_UNKNOWN 0
#define DUMP_CTX 0
#define REV_CTX 1
#define NODE_CTX 2
/* States: */
#define DUMP_CTX 0 /* dump metadata */
#define REV_CTX 1 /* revision metadata */
#define NODE_CTX 2 /* node metadata */
#define INTERNODE_CTX 3 /* between nodes */
#define LENGTH_UNKNOWN (~0)
#define DATE_RFC2822_LEN 31
@ -201,15 +205,21 @@ static void read_props(void)
static void handle_node(void)
{
uint32_t mark = 0;
const uint32_t type = node_ctx.type;
const int have_props = node_ctx.propLength != LENGTH_UNKNOWN;
const int have_text = node_ctx.textLength != LENGTH_UNKNOWN;
/*
* Old text for this node:
* NULL - directory or bug
* empty_blob - empty
* "<dataref>" - data retrievable from fast-import
*/
static const char *const empty_blob = "::empty::";
const char *old_data = NULL;
if (node_ctx.text_delta)
die("text deltas not supported");
if (have_text)
mark = next_blob_mark();
if (node_ctx.action == NODEACT_DELETE) {
if (have_text || have_props || node_ctx.srcRev)
die("invalid dump: deletion node has "
@ -230,23 +240,26 @@ static void handle_node(void)
die("invalid dump: directories cannot have text attached");
/*
* Decide on the new content (mark) and mode (node_ctx.type).
* Find old content (old_data) and decide on the new mode.
*/
if (node_ctx.action == NODEACT_CHANGE && !~*node_ctx.dst) {
if (type != REPO_MODE_DIR)
die("invalid dump: root of tree is not a regular file");
old_data = NULL;
} else if (node_ctx.action == NODEACT_CHANGE) {
uint32_t mode;
if (!have_text)
mark = repo_read_path(node_ctx.dst);
mode = repo_read_mode(node_ctx.dst);
old_data = repo_read_path(node_ctx.dst, &mode);
if (mode == REPO_MODE_DIR && type != REPO_MODE_DIR)
die("invalid dump: cannot modify a directory into a file");
if (mode != REPO_MODE_DIR && type == REPO_MODE_DIR)
die("invalid dump: cannot modify a file into a directory");
node_ctx.type = mode;
} else if (node_ctx.action == NODEACT_ADD) {
if (!have_text && type != REPO_MODE_DIR)
if (type == REPO_MODE_DIR)
old_data = NULL;
else if (have_text)
old_data = empty_blob;
else
die("invalid dump: adds node without text");
} else {
die("invalid dump: Node-path block lacks Node-action");
@ -265,18 +278,35 @@ static void handle_node(void)
/*
* Save the result.
*/
repo_add(node_ctx.dst, node_ctx.type, mark);
if (have_text)
fast_export_blob(node_ctx.type, mark,
node_ctx.textLength, &input);
if (type == REPO_MODE_DIR) /* directories are not tracked. */
return;
assert(old_data);
if (old_data == empty_blob)
/* For the fast_export_* functions, NULL means empty. */
old_data = NULL;
if (!have_text) {
fast_export_modify(REPO_MAX_PATH_DEPTH, node_ctx.dst,
node_ctx.type, old_data);
return;
}
fast_export_modify(REPO_MAX_PATH_DEPTH, node_ctx.dst,
node_ctx.type, "inline");
fast_export_data(node_ctx.type, node_ctx.textLength, &input);
}
static void handle_revision(void)
static void begin_revision(void)
{
if (!rev_ctx.revision) /* revision 0 gets no git commit. */
return;
fast_export_begin_commit(rev_ctx.revision, rev_ctx.author.buf,
&rev_ctx.log, dump_ctx.uuid.buf, dump_ctx.url.buf,
rev_ctx.timestamp);
}
static void end_revision(void)
{
if (rev_ctx.revision)
repo_commit(rev_ctx.revision, rev_ctx.author.buf,
&rev_ctx.log, dump_ctx.uuid.buf, dump_ctx.url.buf,
rev_ctx.timestamp);
fast_export_end_commit(rev_ctx.revision);
}
void svndump_read(const char *url)
@ -317,8 +347,10 @@ void svndump_read(const char *url)
continue;
if (active_ctx == NODE_CTX)
handle_node();
if (active_ctx == REV_CTX)
begin_revision();
if (active_ctx != DUMP_CTX)
handle_revision();
end_revision();
active_ctx = REV_CTX;
reset_rev_ctx(atoi(val));
break;
@ -328,6 +360,8 @@ void svndump_read(const char *url)
if (!constcmp(t + strlen("Node-"), "path")) {
if (active_ctx == NODE_CTX)
handle_node();
if (active_ctx == REV_CTX)
begin_revision();
active_ctx = NODE_CTX;
reset_node_ctx(val);
break;
@ -398,7 +432,7 @@ void svndump_read(const char *url)
read_props();
} else if (active_ctx == NODE_CTX) {
handle_node();
active_ctx = REV_CTX;
active_ctx = INTERNODE_CTX;
} else {
fprintf(stderr, "Unexpected content length header: %"PRIu32"\n", len);
if (buffer_skip_bytes(&input, len) != len)
@ -410,15 +444,17 @@ void svndump_read(const char *url)
die_short_read();
if (active_ctx == NODE_CTX)
handle_node();
if (active_ctx == REV_CTX)
begin_revision();
if (active_ctx != DUMP_CTX)
handle_revision();
end_revision();
}
int svndump_init(const char *filename)
{
if (buffer_init(&input, filename))
return error("cannot open %s: %s", filename, strerror(errno));
repo_init();
fast_export_init(REPORT_FILENO);
strbuf_init(&dump_ctx.uuid, 4096);
strbuf_init(&dump_ctx.url, 4096);
strbuf_init(&rev_ctx.log, 4096);
@ -431,7 +467,7 @@ int svndump_init(const char *filename)
void svndump_deinit(void)
{
repo_reset();
fast_export_deinit();
reset_dump_ctx(NULL);
reset_rev_ctx(0);
reset_node_ctx(NULL);
@ -444,8 +480,8 @@ void svndump_deinit(void)
void svndump_reset(void)
{
fast_export_reset();
buffer_reset(&input);
repo_reset();
strbuf_release(&dump_ctx.uuid);
strbuf_release(&dump_ctx.url);
strbuf_release(&rev_ctx.log);