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
commit 9ecfa8ae4c
9 changed files with 422 additions and 413 deletions

View File

@ -7,7 +7,11 @@ svn-fe - convert an SVN "dumpfile" to a fast-import stream
SYNOPSIS SYNOPSIS
-------- --------
svnadmin dump --incremental REPO | svn-fe [url] | git fast-import [verse]
mkfifo backchannel &&
svnadmin dump --incremental REPO |
svn-fe [url] 3<backchannel |
git fast-import --cat-blob-fd=3 3>backchannel
DESCRIPTION DESCRIPTION
----------- -----------

View File

@ -5,8 +5,26 @@ test_description='check svn dumpfile importer'
. ./test-lib.sh . ./test-lib.sh
reinit_git () { reinit_git () {
if ! test_declared_prereq PIPE
then
echo >&4 "reinit_git: need to declare PIPE prerequisite"
return 127
fi
rm -fr .git && rm -fr .git &&
git init rm -f stream backflow &&
git init &&
mkfifo stream backflow
}
try_dump () {
input=$1 &&
maybe_fail=${2:+test_$2} &&
{
$maybe_fail test-svn-fe "$input" >stream 3<backflow &
} &&
git fast-import --cat-blob-fd=3 <stream 3>backflow &&
wait $!
} }
properties () { properties () {
@ -35,21 +53,27 @@ text_no_props () {
>empty >empty
test_expect_success 'empty dump' ' test_expect_success 'setup: have pipes?' '
rm -f frob &&
if mkfifo frob
then
test_set_prereq PIPE
fi
'
test_expect_success PIPE 'empty dump' '
reinit_git && reinit_git &&
echo "SVN-fs-dump-format-version: 2" >input && echo "SVN-fs-dump-format-version: 2" >input &&
test-svn-fe input >stream && try_dump input
git fast-import <stream
' '
test_expect_success 'v4 dumps not supported' ' test_expect_success PIPE 'v4 dumps not supported' '
reinit_git && reinit_git &&
echo "SVN-fs-dump-format-version: 4" >v4.dump && echo "SVN-fs-dump-format-version: 4" >v4.dump &&
test_must_fail test-svn-fe v4.dump >stream && try_dump v4.dump must_fail
test_cmp empty stream
' '
test_expect_failure 'empty revision' ' test_expect_failure PIPE 'empty revision' '
reinit_git && reinit_git &&
printf "rev <nobody, nobody@local>: %s\n" "" "" >expect && printf "rev <nobody, nobody@local>: %s\n" "" "" >expect &&
cat >emptyrev.dump <<-\EOF && cat >emptyrev.dump <<-\EOF &&
@ -64,13 +88,12 @@ test_expect_failure 'empty revision' '
Content-length: 0 Content-length: 0
EOF EOF
test-svn-fe emptyrev.dump >stream && try_dump emptyrev.dump &&
git fast-import <stream &&
git log -p --format="rev <%an, %ae>: %s" HEAD >actual && git log -p --format="rev <%an, %ae>: %s" HEAD >actual &&
test_cmp expect actual test_cmp expect actual
' '
test_expect_success 'empty properties' ' test_expect_success PIPE 'empty properties' '
reinit_git && reinit_git &&
printf "rev <nobody, nobody@local>: %s\n" "" "" >expect && printf "rev <nobody, nobody@local>: %s\n" "" "" >expect &&
cat >emptyprop.dump <<-\EOF && cat >emptyprop.dump <<-\EOF &&
@ -88,13 +111,12 @@ test_expect_success 'empty properties' '
PROPS-END PROPS-END
EOF EOF
test-svn-fe emptyprop.dump >stream && try_dump emptyprop.dump &&
git fast-import <stream &&
git log -p --format="rev <%an, %ae>: %s" HEAD >actual && git log -p --format="rev <%an, %ae>: %s" HEAD >actual &&
test_cmp expect actual test_cmp expect actual
' '
test_expect_success 'author name and commit message' ' test_expect_success PIPE 'author name and commit message' '
reinit_git && reinit_git &&
echo "<author@example.com, author@example.com@local>" >expect.author && echo "<author@example.com, author@example.com@local>" >expect.author &&
cat >message <<-\EOF && cat >message <<-\EOF &&
@ -121,15 +143,14 @@ test_expect_success 'author name and commit message' '
echo && echo &&
cat props cat props
} >log.dump && } >log.dump &&
test-svn-fe log.dump >stream && try_dump log.dump &&
git fast-import <stream &&
git log -p --format="%B" HEAD >actual.log && git log -p --format="%B" HEAD >actual.log &&
git log --format="<%an, %ae>" >actual.author && git log --format="<%an, %ae>" >actual.author &&
test_cmp message actual.log && test_cmp message actual.log &&
test_cmp expect.author actual.author test_cmp expect.author actual.author
' '
test_expect_success 'unsupported properties are ignored' ' test_expect_success PIPE 'unsupported properties are ignored' '
reinit_git && reinit_git &&
echo author >expect && echo author >expect &&
cat >extraprop.dump <<-\EOF && cat >extraprop.dump <<-\EOF &&
@ -149,13 +170,12 @@ test_expect_success 'unsupported properties are ignored' '
author author
PROPS-END PROPS-END
EOF EOF
test-svn-fe extraprop.dump >stream && try_dump extraprop.dump &&
git fast-import <stream &&
git log -p --format=%an HEAD >actual && git log -p --format=%an HEAD >actual &&
test_cmp expect actual test_cmp expect actual
' '
test_expect_failure 'timestamp and empty file' ' test_expect_failure PIPE 'timestamp and empty file' '
echo author@example.com >expect.author && echo author@example.com >expect.author &&
echo 1999-01-01 >expect.date && echo 1999-01-01 >expect.date &&
echo file >expect.files && echo file >expect.files &&
@ -186,8 +206,7 @@ test_expect_failure 'timestamp and empty file' '
EOF EOF
} >emptyfile.dump && } >emptyfile.dump &&
test-svn-fe emptyfile.dump >stream && try_dump emptyfile.dump &&
git fast-import <stream &&
git log --format=%an HEAD >actual.author && git log --format=%an HEAD >actual.author &&
git log --date=short --format=%ad HEAD >actual.date && git log --date=short --format=%ad HEAD >actual.date &&
git ls-tree -r --name-only HEAD >actual.files && git ls-tree -r --name-only HEAD >actual.files &&
@ -198,7 +217,7 @@ test_expect_failure 'timestamp and empty file' '
test_cmp empty file test_cmp empty file
' '
test_expect_success 'directory with files' ' test_expect_success PIPE 'directory with files' '
reinit_git && reinit_git &&
printf "%s\n" directory/file1 directory/file2 >expect.files && printf "%s\n" directory/file1 directory/file2 >expect.files &&
echo hi >hi && echo hi >hi &&
@ -242,8 +261,7 @@ test_expect_success 'directory with files' '
EOF EOF
text_no_props hi text_no_props hi
} >directory.dump && } >directory.dump &&
test-svn-fe directory.dump >stream && try_dump directory.dump &&
git fast-import <stream &&
git ls-tree -r --name-only HEAD >actual.files && git ls-tree -r --name-only HEAD >actual.files &&
git checkout HEAD directory && git checkout HEAD directory &&
@ -252,7 +270,107 @@ test_expect_success 'directory with files' '
test_cmp hi directory/file2 test_cmp hi directory/file2
' '
test_expect_success 'node without action' ' test_expect_success PIPE 'branch name with backslash' '
reinit_git &&
sort <<-\EOF >expect.branch-files &&
trunk/file1
trunk/file2
"branches/UpdateFOPto094\\/file1"
"branches/UpdateFOPto094\\/file2"
EOF
echo hi >hi &&
echo hello >hello &&
{
properties \
svn:author author@example.com \
svn:date "1999-02-02T00:01:02.000000Z" \
svn:log "add directory with some files in it" &&
echo PROPS-END
} >props.setup &&
{
properties \
svn:author brancher@example.com \
svn:date "2007-12-06T21:38:34.000000Z" \
svn:log "Updating fop to .94 and adjust fo-stylesheets" &&
echo PROPS-END
} >props.branch &&
{
cat <<-EOF &&
SVN-fs-dump-format-version: 3
Revision-number: 1
EOF
echo Prop-content-length: $(wc -c <props.setup) &&
echo Content-length: $(wc -c <props.setup) &&
echo &&
cat props.setup &&
cat <<-\EOF &&
Node-path: trunk
Node-kind: dir
Node-action: add
Prop-content-length: 10
Content-length: 10
PROPS-END
Node-path: branches
Node-kind: dir
Node-action: add
Prop-content-length: 10
Content-length: 10
PROPS-END
Node-path: trunk/file1
Node-kind: file
Node-action: add
EOF
text_no_props hello &&
cat <<-\EOF &&
Node-path: trunk/file2
Node-kind: file
Node-action: add
EOF
text_no_props hi &&
cat <<-\EOF &&
Revision-number: 2
EOF
echo Prop-content-length: $(wc -c <props.branch) &&
echo Content-length: $(wc -c <props.branch) &&
echo &&
cat props.branch &&
cat <<-\EOF
Node-path: branches/UpdateFOPto094\
Node-kind: dir
Node-action: add
Node-copyfrom-rev: 1
Node-copyfrom-path: trunk
Node-kind: dir
Node-action: add
Prop-content-length: 34
Content-length: 34
K 13
svn:mergeinfo
V 0
PROPS-END
EOF
} >branch.dump &&
try_dump branch.dump &&
git ls-tree -r --name-only HEAD |
sort >actual.branch-files &&
test_cmp expect.branch-files actual.branch-files
'
test_expect_success PIPE 'node without action' '
reinit_git &&
cat >inaction.dump <<-\EOF && cat >inaction.dump <<-\EOF &&
SVN-fs-dump-format-version: 3 SVN-fs-dump-format-version: 3
@ -269,10 +387,11 @@ test_expect_success 'node without action' '
PROPS-END PROPS-END
EOF EOF
test_must_fail test-svn-fe inaction.dump try_dump inaction.dump must_fail
' '
test_expect_success 'action: add node without text' ' test_expect_success PIPE 'action: add node without text' '
reinit_git &&
cat >textless.dump <<-\EOF && cat >textless.dump <<-\EOF &&
SVN-fs-dump-format-version: 3 SVN-fs-dump-format-version: 3
@ -290,10 +409,10 @@ test_expect_success 'action: add node without text' '
PROPS-END PROPS-END
EOF EOF
test_must_fail test-svn-fe textless.dump try_dump textless.dump must_fail
' '
test_expect_failure 'change file mode but keep old content' ' test_expect_failure PIPE 'change file mode but keep old content' '
reinit_git && reinit_git &&
cat >expect <<-\EOF && cat >expect <<-\EOF &&
OBJID OBJID
@ -356,8 +475,7 @@ test_expect_failure 'change file mode but keep old content' '
PROPS-END PROPS-END
EOF EOF
test-svn-fe filemode.dump >stream && try_dump filemode.dump &&
git fast-import <stream &&
{ {
git rev-list HEAD | git rev-list HEAD |
git diff-tree --root --stdin | git diff-tree --root --stdin |
@ -370,7 +488,7 @@ test_expect_failure 'change file mode but keep old content' '
test_cmp hello actual.target test_cmp hello actual.target
' '
test_expect_success 'NUL in property value' ' test_expect_success PIPE 'NUL in property value' '
reinit_git && reinit_git &&
echo "commit message" >expect.message && echo "commit message" >expect.message &&
{ {
@ -391,13 +509,12 @@ test_expect_success 'NUL in property value' '
echo && echo &&
cat props cat props
} >nulprop.dump && } >nulprop.dump &&
test-svn-fe nulprop.dump >stream && try_dump nulprop.dump &&
git fast-import <stream &&
git diff-tree --always -s --format=%s HEAD >actual.message && git diff-tree --always -s --format=%s HEAD >actual.message &&
test_cmp expect.message actual.message test_cmp expect.message actual.message
' '
test_expect_success 'NUL in log message, file content, and property name' ' test_expect_success PIPE 'NUL in log message, file content, and property name' '
# Caveat: svnadmin 1.6.16 (r1073529) truncates at \0 in the # Caveat: svnadmin 1.6.16 (r1073529) truncates at \0 in the
# svn:specialQnotreally example. # svn:specialQnotreally example.
reinit_git && reinit_git &&
@ -458,8 +575,7 @@ test_expect_success 'NUL in log message, file content, and property name' '
link hello link hello
EOF EOF
} >8bitclean.dump && } >8bitclean.dump &&
test-svn-fe 8bitclean.dump >stream && try_dump 8bitclean.dump &&
git fast-import <stream &&
{ {
git rev-list HEAD | git rev-list HEAD |
git diff-tree --root --stdin | git diff-tree --root --stdin |
@ -478,7 +594,7 @@ test_expect_success 'NUL in log message, file content, and property name' '
test_cmp expect.hello2 actual.hello2 test_cmp expect.hello2 actual.hello2
' '
test_expect_success 'change file mode and reiterate content' ' test_expect_success PIPE 'change file mode and reiterate content' '
reinit_git && reinit_git &&
cat >expect <<-\EOF && cat >expect <<-\EOF &&
OBJID OBJID
@ -490,7 +606,7 @@ test_expect_success 'change file mode and reiterate content' '
EOF EOF
echo "link hello" >expect.blob && echo "link hello" >expect.blob &&
echo hello >hello && echo hello >hello &&
cat >filemode.dump <<-\EOF && cat >filemode2.dump <<-\EOF &&
SVN-fs-dump-format-version: 3 SVN-fs-dump-format-version: 3
Revision-number: 1 Revision-number: 1
@ -545,8 +661,7 @@ test_expect_success 'change file mode and reiterate content' '
PROPS-END PROPS-END
link hello link hello
EOF EOF
test-svn-fe filemode.dump >stream && try_dump filemode2.dump &&
git fast-import <stream &&
{ {
git rev-list HEAD | git rev-list HEAD |
git diff-tree --root --stdin | git diff-tree --root --stdin |
@ -559,7 +674,8 @@ test_expect_success 'change file mode and reiterate content' '
test_cmp hello actual.target test_cmp hello actual.target
' '
test_expect_success 'deltas not supported' ' test_expect_success PIPE 'deltas not supported' '
reinit_git &&
{ {
# (old) h + (inline) ello + (old) \n # (old) h + (inline) ello + (old) \n
printf "SVNQ%b%b%s" "Q\003\006\005\004" "\001Q\0204\001\002" "ello" | printf "SVNQ%b%b%s" "Q\003\006\005\004" "\001Q\0204\001\002" "ello" |
@ -619,10 +735,10 @@ test_expect_success 'deltas not supported' '
echo PROPS-END && echo PROPS-END &&
cat delta cat delta
} >delta.dump && } >delta.dump &&
test_must_fail test-svn-fe delta.dump test_must_fail try_dump delta.dump
' '
test_expect_success 'property deltas supported' ' test_expect_success PIPE 'property deltas supported' '
reinit_git && reinit_git &&
cat >expect <<-\EOF && cat >expect <<-\EOF &&
OBJID OBJID
@ -678,8 +794,7 @@ test_expect_success 'property deltas supported' '
PROPS-END PROPS-END
EOF EOF
} >propdelta.dump && } >propdelta.dump &&
test-svn-fe propdelta.dump >stream && try_dump propdelta.dump &&
git fast-import <stream &&
{ {
git rev-list HEAD | git rev-list HEAD |
git diff-tree --stdin | git diff-tree --stdin |
@ -688,7 +803,7 @@ test_expect_success 'property deltas supported' '
test_cmp expect actual test_cmp expect actual
' '
test_expect_success 'properties on /' ' test_expect_success PIPE 'properties on /' '
reinit_git && reinit_git &&
cat <<-\EOF >expect && cat <<-\EOF >expect &&
OBJID OBJID
@ -733,8 +848,7 @@ test_expect_success 'properties on /' '
PROPS-END PROPS-END
EOF EOF
test-svn-fe changeroot.dump >stream && try_dump changeroot.dump &&
git fast-import <stream &&
{ {
git rev-list HEAD | git rev-list HEAD |
git diff-tree --root --always --stdin | git diff-tree --root --always --stdin |
@ -743,7 +857,7 @@ test_expect_success 'properties on /' '
test_cmp expect actual test_cmp expect actual
' '
test_expect_success 'deltas for typechange' ' test_expect_success PIPE 'deltas for typechange' '
reinit_git && reinit_git &&
cat >expect <<-\EOF && cat >expect <<-\EOF &&
OBJID OBJID
@ -819,8 +933,7 @@ test_expect_success 'deltas for typechange' '
PROPS-END PROPS-END
link testing 321 link testing 321
EOF EOF
test-svn-fe deleteprop.dump >stream && try_dump deleteprop.dump &&
git fast-import <stream &&
{ {
git rev-list HEAD | git rev-list HEAD |
git diff-tree --root --stdin | git diff-tree --root --stdin |
@ -844,12 +957,12 @@ test_expect_success 'set up svn repo' '
fi fi
' '
test_expect_success SVNREPO 't9135/svn.dump' ' test_expect_success SVNREPO,PIPE 't9135/svn.dump' '
git init simple-git && mkdir -p simple-git &&
test-svn-fe "$TEST_DIRECTORY/t9135/svn.dump" >simple.fe &&
( (
cd simple-git && cd simple-git &&
git fast-import <../simple.fe reinit_git &&
try_dump "$TEST_DIRECTORY/t9135/svn.dump"
) && ) &&
( (
cd simple-svnco && cd simple-svnco &&

View File

@ -8,30 +8,58 @@
#include "line_buffer.h" #include "line_buffer.h"
#include "repo_tree.h" #include "repo_tree.h"
#include "string_pool.h" #include "string_pool.h"
#include "strbuf.h"
#define MAX_GITSVN_LINE_LEN 4096 #define MAX_GITSVN_LINE_LEN 4096
static uint32_t first_commit_done; static uint32_t first_commit_done;
static struct line_buffer report_buffer = LINE_BUFFER_INIT;
void fast_export_delete(uint32_t depth, uint32_t *path) void fast_export_init(int fd)
{ {
putchar('D'); if (buffer_fdinit(&report_buffer, fd))
putchar(' '); die_errno("cannot read from file descriptor %d", fd);
pool_print_seq(depth, path, '/', stdout);
putchar('\n');
} }
void fast_export_modify(uint32_t depth, uint32_t *path, uint32_t mode, void fast_export_deinit(void)
uint32_t mark) {
if (buffer_deinit(&report_buffer))
die_errno("error closing fast-import feedback stream");
}
void fast_export_reset(void)
{
buffer_reset(&report_buffer);
}
void fast_export_delete(uint32_t depth, const uint32_t *path)
{
printf("D \"");
pool_print_seq_q(depth, path, '/', stdout);
printf("\"\n");
}
static void fast_export_truncate(uint32_t depth, const uint32_t *path, uint32_t mode)
{
fast_export_modify(depth, path, mode, "inline");
printf("data 0\n\n");
}
void fast_export_modify(uint32_t depth, const uint32_t *path, uint32_t mode,
const char *dataref)
{ {
/* Mode must be 100644, 100755, 120000, or 160000. */ /* Mode must be 100644, 100755, 120000, or 160000. */
printf("M %06"PRIo32" :%"PRIu32" ", mode, mark); if (!dataref) {
pool_print_seq(depth, path, '/', stdout); fast_export_truncate(depth, path, mode);
putchar('\n'); return;
}
printf("M %06"PRIo32" %s \"", mode, dataref);
pool_print_seq_q(depth, path, '/', stdout);
printf("\"\n");
} }
static char gitsvnline[MAX_GITSVN_LINE_LEN]; static char gitsvnline[MAX_GITSVN_LINE_LEN];
void fast_export_commit(uint32_t revision, const char *author, void fast_export_begin_commit(uint32_t revision, const char *author,
const struct strbuf *log, const struct strbuf *log,
const char *uuid, const char *url, const char *uuid, const char *url,
unsigned long timestamp) unsigned long timestamp)
@ -47,6 +75,7 @@ void fast_export_commit(uint32_t revision, const char *author,
*gitsvnline = '\0'; *gitsvnline = '\0';
} }
printf("commit refs/heads/master\n"); printf("commit refs/heads/master\n");
printf("mark :%"PRIu32"\n", revision);
printf("committer %s <%s@%s> %ld +0000\n", printf("committer %s <%s@%s> %ld +0000\n",
*author ? author : "nobody", *author ? author : "nobody",
*author ? author : "nobody", *author ? author : "nobody",
@ -57,15 +86,44 @@ void fast_export_commit(uint32_t revision, const char *author,
printf("%s\n", gitsvnline); printf("%s\n", gitsvnline);
if (!first_commit_done) { if (!first_commit_done) {
if (revision > 1) if (revision > 1)
printf("from refs/heads/master^0\n"); printf("from :%"PRIu32"\n", revision - 1);
first_commit_done = 1; first_commit_done = 1;
} }
repo_diff(revision - 1, revision); }
fputc('\n', stdout);
void fast_export_end_commit(uint32_t revision)
{
printf("progress Imported commit %"PRIu32".\n\n", revision); printf("progress Imported commit %"PRIu32".\n\n", revision);
} }
static void ls_from_rev(uint32_t rev, uint32_t depth, const uint32_t *path)
{
/* ls :5 path/to/old/file */
printf("ls :%"PRIu32" \"", rev);
pool_print_seq_q(depth, path, '/', stdout);
printf("\"\n");
fflush(stdout);
}
static void ls_from_active_commit(uint32_t depth, const uint32_t *path)
{
/* ls "path/to/file" */
printf("ls \"");
pool_print_seq_q(depth, path, '/', stdout);
printf("\"\n");
fflush(stdout);
}
static const char *get_response_line(void)
{
const char *line = buffer_read_line(&report_buffer);
if (line)
return line;
if (buffer_ferror(&report_buffer))
die_errno("error reading from fast-import");
die("unexpected end of fast-import feedback");
}
static void die_short_read(struct line_buffer *input) static void die_short_read(struct line_buffer *input)
{ {
if (buffer_ferror(input)) if (buffer_ferror(input))
@ -73,7 +131,7 @@ static void die_short_read(struct line_buffer *input)
die("invalid dump: unexpected end of file"); die("invalid dump: unexpected end of file");
} }
void fast_export_blob(uint32_t mode, uint32_t mark, uint32_t len, struct line_buffer *input) void fast_export_data(uint32_t mode, uint32_t len, struct line_buffer *input)
{ {
if (mode == REPO_MODE_LNK) { if (mode == REPO_MODE_LNK) {
/* svn symlink blobs start with "link " */ /* svn symlink blobs start with "link " */
@ -81,8 +139,63 @@ void fast_export_blob(uint32_t mode, uint32_t mark, uint32_t len, struct line_bu
if (buffer_skip_bytes(input, 5) != 5) if (buffer_skip_bytes(input, 5) != 5)
die_short_read(input); die_short_read(input);
} }
printf("blob\nmark :%"PRIu32"\ndata %"PRIu32"\n", mark, len); printf("data %"PRIu32"\n", len);
if (buffer_copy_bytes(input, len) != len) if (buffer_copy_bytes(input, len) != len)
die_short_read(input); die_short_read(input);
fputc('\n', stdout); fputc('\n', stdout);
} }
static int parse_ls_response(const char *response, uint32_t *mode,
struct strbuf *dataref)
{
const char *tab;
const char *response_end;
assert(response);
response_end = response + strlen(response);
if (*response == 'm') { /* Missing. */
errno = ENOENT;
return -1;
}
/* Mode. */
if (response_end - response < strlen("100644") ||
response[strlen("100644")] != ' ')
die("invalid ls response: missing mode: %s", response);
*mode = 0;
for (; *response != ' '; response++) {
char ch = *response;
if (ch < '0' || ch > '7')
die("invalid ls response: mode is not octal: %s", response);
*mode *= 8;
*mode += ch - '0';
}
/* ' blob ' or ' tree ' */
if (response_end - response < strlen(" blob ") ||
(response[1] != 'b' && response[1] != 't'))
die("unexpected ls response: not a tree or blob: %s", response);
response += strlen(" blob ");
/* Dataref. */
tab = memchr(response, '\t', response_end - response);
if (!tab)
die("invalid ls response: missing tab: %s", response);
strbuf_add(dataref, response, tab - response);
return 0;
}
int fast_export_ls_rev(uint32_t rev, uint32_t depth, const uint32_t *path,
uint32_t *mode, struct strbuf *dataref)
{
ls_from_rev(rev, depth, path);
return parse_ls_response(get_response_line(), mode, dataref);
}
int fast_export_ls(uint32_t depth, const uint32_t *path,
uint32_t *mode, struct strbuf *dataref)
{
ls_from_active_commit(depth, path);
return parse_ls_response(get_response_line(), mode, dataref);
}

View File

@ -1,16 +1,26 @@
#ifndef FAST_EXPORT_H_ #ifndef FAST_EXPORT_H_
#define FAST_EXPORT_H_ #define FAST_EXPORT_H_
#include "line_buffer.h"
struct strbuf; struct strbuf;
struct line_buffer;
void fast_export_delete(uint32_t depth, uint32_t *path); void fast_export_init(int fd);
void fast_export_modify(uint32_t depth, uint32_t *path, uint32_t mode, void fast_export_deinit(void);
uint32_t mark); void fast_export_reset(void);
void fast_export_commit(uint32_t revision, const char *author,
void fast_export_delete(uint32_t depth, const uint32_t *path);
void fast_export_modify(uint32_t depth, const uint32_t *path,
uint32_t mode, const char *dataref);
void fast_export_begin_commit(uint32_t revision, const char *author,
const struct strbuf *log, const char *uuid, const struct strbuf *log, const char *uuid,
const char *url, unsigned long timestamp); const char *url, unsigned long timestamp);
void fast_export_blob(uint32_t mode, uint32_t mark, uint32_t len, void fast_export_end_commit(uint32_t revision);
struct line_buffer *input); void fast_export_data(uint32_t mode, uint32_t len, struct line_buffer *input);
/* If there is no such file at that rev, returns -1, errno == ENOENT. */
int fast_export_ls_rev(uint32_t rev, uint32_t depth, const uint32_t *path,
uint32_t *mode_out, struct strbuf *dataref_out);
int fast_export_ls(uint32_t depth, const uint32_t *path,
uint32_t *mode_out, struct strbuf *dataref_out);
#endif #endif

View File

@ -4,323 +4,45 @@
*/ */
#include "git-compat-util.h" #include "git-compat-util.h"
#include "strbuf.h"
#include "string_pool.h"
#include "repo_tree.h" #include "repo_tree.h"
#include "obj_pool.h"
#include "fast_export.h" #include "fast_export.h"
#include "trp.h" const char *repo_read_path(const uint32_t *path, uint32_t *mode_out)
struct repo_dirent {
uint32_t name_offset;
struct trp_node children;
uint32_t mode;
uint32_t content_offset;
};
struct repo_dir {
struct trp_root entries;
};
struct repo_commit {
uint32_t root_dir_offset;
};
/* Memory pools for commit, dir and dirent */
obj_pool_gen(commit, struct repo_commit, 4096)
obj_pool_gen(dir, struct repo_dir, 4096)
obj_pool_gen(dent, struct repo_dirent, 4096)
static uint32_t active_commit;
static uint32_t mark;
static int repo_dirent_name_cmp(const void *a, const void *b);
/* Treap for directory entries */
trp_gen(static, dent_, struct repo_dirent, children, dent, repo_dirent_name_cmp)
uint32_t next_blob_mark(void)
{ {
return mark++; int err;
} static struct strbuf buf = STRBUF_INIT;
static struct repo_dir *repo_commit_root_dir(struct repo_commit *commit) strbuf_reset(&buf);
{ err = fast_export_ls(REPO_MAX_PATH_DEPTH, path, mode_out, &buf);
return dir_pointer(commit->root_dir_offset); if (err) {
} if (errno != ENOENT)
die_errno("BUG: unexpected fast_export_ls error");
static struct repo_dirent *repo_first_dirent(struct repo_dir *dir) /* Treat missing paths as directories. */
{ *mode_out = REPO_MODE_DIR;
return dent_first(&dir->entries);
}
static int repo_dirent_name_cmp(const void *a, const void *b)
{
const struct repo_dirent *dent1 = a, *dent2 = b;
uint32_t a_offset = dent1->name_offset;
uint32_t b_offset = dent2->name_offset;
return (a_offset > b_offset) - (a_offset < b_offset);
}
static int repo_dirent_is_dir(struct repo_dirent *dent)
{
return dent != NULL && dent->mode == REPO_MODE_DIR;
}
static struct repo_dir *repo_dir_from_dirent(struct repo_dirent *dent)
{
if (!repo_dirent_is_dir(dent))
return NULL; return NULL;
return dir_pointer(dent->content_offset);
}
static struct repo_dir *repo_clone_dir(struct repo_dir *orig_dir)
{
uint32_t orig_o, new_o;
orig_o = dir_offset(orig_dir);
if (orig_o >= dir_pool.committed)
return orig_dir;
new_o = dir_alloc(1);
orig_dir = dir_pointer(orig_o);
*dir_pointer(new_o) = *orig_dir;
return dir_pointer(new_o);
}
static struct repo_dirent *repo_read_dirent(uint32_t revision,
const uint32_t *path)
{
uint32_t name = 0;
struct repo_dirent *key = dent_pointer(dent_alloc(1));
struct repo_dir *dir = NULL;
struct repo_dirent *dent = NULL;
dir = repo_commit_root_dir(commit_pointer(revision));
while (~(name = *path++)) {
key->name_offset = name;
dent = dent_search(&dir->entries, key);
if (dent == NULL || !repo_dirent_is_dir(dent))
break;
dir = repo_dir_from_dirent(dent);
} }
dent_free(1); return buf.buf;
return dent;
}
static void repo_write_dirent(const uint32_t *path, uint32_t mode,
uint32_t content_offset, uint32_t del)
{
uint32_t name, revision, dir_o = ~0, parent_dir_o = ~0;
struct repo_dir *dir;
struct repo_dirent *key;
struct repo_dirent *dent = NULL;
revision = active_commit;
dir = repo_commit_root_dir(commit_pointer(revision));
dir = repo_clone_dir(dir);
commit_pointer(revision)->root_dir_offset = dir_offset(dir);
while (~(name = *path++)) {
parent_dir_o = dir_offset(dir);
key = dent_pointer(dent_alloc(1));
key->name_offset = name;
dent = dent_search(&dir->entries, key);
if (dent == NULL)
dent = key;
else
dent_free(1);
if (dent == key) {
dent->mode = REPO_MODE_DIR;
dent->content_offset = 0;
dent = dent_insert(&dir->entries, dent);
}
if (dent_offset(dent) < dent_pool.committed) {
dir_o = repo_dirent_is_dir(dent) ?
dent->content_offset : ~0;
dent_remove(&dir->entries, dent);
dent = dent_pointer(dent_alloc(1));
dent->name_offset = name;
dent->mode = REPO_MODE_DIR;
dent->content_offset = dir_o;
dent = dent_insert(&dir->entries, dent);
}
dir = repo_dir_from_dirent(dent);
dir = repo_clone_dir(dir);
dent->content_offset = dir_offset(dir);
}
if (dent == NULL)
return;
dent->mode = mode;
dent->content_offset = content_offset;
if (del && ~parent_dir_o)
dent_remove(&dir_pointer(parent_dir_o)->entries, dent);
}
uint32_t repo_read_path(const uint32_t *path)
{
uint32_t content_offset = 0;
struct repo_dirent *dent = repo_read_dirent(active_commit, path);
if (dent != NULL)
content_offset = dent->content_offset;
return content_offset;
}
uint32_t repo_read_mode(const uint32_t *path)
{
struct repo_dirent *dent = repo_read_dirent(active_commit, path);
if (dent == NULL)
die("invalid dump: path to be modified is missing");
return dent->mode;
} }
void repo_copy(uint32_t revision, const uint32_t *src, const uint32_t *dst) void repo_copy(uint32_t revision, const uint32_t *src, const uint32_t *dst)
{ {
uint32_t mode = 0, content_offset = 0; int err;
struct repo_dirent *src_dent; uint32_t mode;
src_dent = repo_read_dirent(revision, src); static struct strbuf data = STRBUF_INIT;
if (src_dent != NULL) {
mode = src_dent->mode;
content_offset = src_dent->content_offset;
repo_write_dirent(dst, mode, content_offset, 0);
}
}
void repo_add(uint32_t *path, uint32_t mode, uint32_t blob_mark) strbuf_reset(&data);
{ err = fast_export_ls_rev(revision, REPO_MAX_PATH_DEPTH, src, &mode, &data);
repo_write_dirent(path, mode, blob_mark, 0); if (err) {
if (errno != ENOENT)
die_errno("BUG: unexpected fast_export_ls_rev error");
fast_export_delete(REPO_MAX_PATH_DEPTH, dst);
return;
}
fast_export_modify(REPO_MAX_PATH_DEPTH, dst, mode, data.buf);
} }
void repo_delete(uint32_t *path) void repo_delete(uint32_t *path)
{ {
repo_write_dirent(path, 0, 0, 1); fast_export_delete(REPO_MAX_PATH_DEPTH, path);
}
static void repo_git_add_r(uint32_t depth, uint32_t *path, struct repo_dir *dir);
static void repo_git_add(uint32_t depth, uint32_t *path, struct repo_dirent *dent)
{
if (repo_dirent_is_dir(dent))
repo_git_add_r(depth, path, repo_dir_from_dirent(dent));
else
fast_export_modify(depth, path,
dent->mode, dent->content_offset);
}
static void repo_git_add_r(uint32_t depth, uint32_t *path, struct repo_dir *dir)
{
struct repo_dirent *de = repo_first_dirent(dir);
while (de) {
path[depth] = de->name_offset;
repo_git_add(depth + 1, path, de);
de = dent_next(&dir->entries, de);
}
}
static void repo_diff_r(uint32_t depth, uint32_t *path, struct repo_dir *dir1,
struct repo_dir *dir2)
{
struct repo_dirent *de1, *de2;
de1 = repo_first_dirent(dir1);
de2 = repo_first_dirent(dir2);
while (de1 && de2) {
if (de1->name_offset < de2->name_offset) {
path[depth] = de1->name_offset;
fast_export_delete(depth + 1, path);
de1 = dent_next(&dir1->entries, de1);
continue;
}
if (de1->name_offset > de2->name_offset) {
path[depth] = de2->name_offset;
repo_git_add(depth + 1, path, de2);
de2 = dent_next(&dir2->entries, de2);
continue;
}
path[depth] = de1->name_offset;
if (de1->mode == de2->mode &&
de1->content_offset == de2->content_offset) {
; /* No change. */
} else if (repo_dirent_is_dir(de1) && repo_dirent_is_dir(de2)) {
repo_diff_r(depth + 1, path,
repo_dir_from_dirent(de1),
repo_dir_from_dirent(de2));
} else if (!repo_dirent_is_dir(de1) && !repo_dirent_is_dir(de2)) {
repo_git_add(depth + 1, path, de2);
} else {
fast_export_delete(depth + 1, path);
repo_git_add(depth + 1, path, de2);
}
de1 = dent_next(&dir1->entries, de1);
de2 = dent_next(&dir2->entries, de2);
}
while (de1) {
path[depth] = de1->name_offset;
fast_export_delete(depth + 1, path);
de1 = dent_next(&dir1->entries, de1);
}
while (de2) {
path[depth] = de2->name_offset;
repo_git_add(depth + 1, path, de2);
de2 = dent_next(&dir2->entries, de2);
}
}
static uint32_t path_stack[REPO_MAX_PATH_DEPTH];
void repo_diff(uint32_t r1, uint32_t r2)
{
repo_diff_r(0,
path_stack,
repo_commit_root_dir(commit_pointer(r1)),
repo_commit_root_dir(commit_pointer(r2)));
}
void repo_commit(uint32_t revision, const char *author,
const struct strbuf *log, const char *uuid, const char *url,
unsigned long timestamp)
{
fast_export_commit(revision, author, log, uuid, url, timestamp);
dent_commit();
dir_commit();
active_commit = commit_alloc(1);
commit_pointer(active_commit)->root_dir_offset =
commit_pointer(active_commit - 1)->root_dir_offset;
}
static void mark_init(void)
{
uint32_t i;
mark = 0;
for (i = 0; i < dent_pool.size; i++)
if (!repo_dirent_is_dir(dent_pointer(i)) &&
dent_pointer(i)->content_offset > mark)
mark = dent_pointer(i)->content_offset;
mark++;
}
void repo_init(void)
{
mark_init();
if (commit_pool.size == 0) {
/* Create empty tree for commit 0. */
commit_alloc(1);
commit_pointer(0)->root_dir_offset = dir_alloc(1);
dir_pointer(0)->entries.trp_root = ~0;
dir_commit();
}
/* Preallocate next commit, ready for changes. */
active_commit = commit_alloc(1);
commit_pointer(active_commit)->root_dir_offset =
commit_pointer(active_commit - 1)->root_dir_offset;
}
void repo_reset(void)
{
pool_reset();
commit_reset();
dir_reset();
dent_reset();
} }

View File

@ -14,8 +14,7 @@ struct strbuf;
uint32_t next_blob_mark(void); uint32_t next_blob_mark(void);
void repo_copy(uint32_t revision, const uint32_t *src, const uint32_t *dst); void repo_copy(uint32_t revision, const uint32_t *src, const uint32_t *dst);
void repo_add(uint32_t *path, uint32_t mode, uint32_t blob_mark); void repo_add(uint32_t *path, uint32_t mode, uint32_t blob_mark);
uint32_t repo_read_path(const uint32_t *path); const char *repo_read_path(const uint32_t *path, uint32_t *mode_out);
uint32_t repo_read_mode(const uint32_t *path);
void repo_delete(uint32_t *path); void repo_delete(uint32_t *path);
void repo_commit(uint32_t revision, const char *author, void repo_commit(uint32_t revision, const char *author,
const struct strbuf *log, const char *uuid, const char *url, const struct strbuf *log, const char *uuid, const char *url,

View File

@ -4,6 +4,7 @@
*/ */
#include "git-compat-util.h" #include "git-compat-util.h"
#include "quote.h"
#include "trp.h" #include "trp.h"
#include "obj_pool.h" #include "obj_pool.h"
#include "string_pool.h" #include "string_pool.h"
@ -65,7 +66,7 @@ uint32_t pool_tok_r(char *str, const char *delim, char **saveptr)
return token ? pool_intern(token) : ~0; return token ? pool_intern(token) : ~0;
} }
void pool_print_seq(uint32_t len, uint32_t *seq, char delim, FILE *stream) void pool_print_seq(uint32_t len, const uint32_t *seq, char delim, FILE *stream)
{ {
uint32_t i; uint32_t i;
for (i = 0; i < len && ~seq[i]; i++) { for (i = 0; i < len && ~seq[i]; i++) {
@ -75,6 +76,16 @@ void pool_print_seq(uint32_t len, uint32_t *seq, char delim, FILE *stream)
} }
} }
void pool_print_seq_q(uint32_t len, const uint32_t *seq, char delim, FILE *stream)
{
uint32_t i;
for (i = 0; i < len && ~seq[i]; i++) {
quote_c_style(pool_fetch(seq[i]), NULL, stream, 1);
if (i < len - 1 && ~seq[i + 1])
fputc(delim, stream);
}
}
uint32_t pool_tok_seq(uint32_t sz, uint32_t *seq, const char *delim, char *str) uint32_t pool_tok_seq(uint32_t sz, uint32_t *seq, const char *delim, char *str)
{ {
char *context = NULL; char *context = NULL;

View File

@ -4,7 +4,8 @@
uint32_t pool_intern(const char *key); uint32_t pool_intern(const char *key);
const char *pool_fetch(uint32_t entry); const char *pool_fetch(uint32_t entry);
uint32_t pool_tok_r(char *str, const char *delim, char **saveptr); uint32_t pool_tok_r(char *str, const char *delim, char **saveptr);
void pool_print_seq(uint32_t len, uint32_t *seq, char delim, FILE *stream); void pool_print_seq(uint32_t len, const uint32_t *seq, char delim, FILE *stream);
void pool_print_seq_q(uint32_t len, const uint32_t *seq, char delim, FILE *stream);
uint32_t pool_tok_seq(uint32_t sz, uint32_t *seq, const char *delim, char *str); uint32_t pool_tok_seq(uint32_t sz, uint32_t *seq, const char *delim, char *str);
void pool_reset(void); void pool_reset(void);

View File

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