diff --git a/t/t9010-svn-fe.sh b/t/t9010-svn-fe.sh
index b7eed248..45706bde 100755
--- a/t/t9010-svn-fe.sh
+++ b/t/t9010-svn-fe.sh
@@ -271,6 +271,75 @@ test_expect_success PIPE 'directory with files' '
test_cmp hi directory/file2
'
+test_expect_success PIPE 'copy from root to directory' '
+ reinit_git &&
+ echo hello >hello &&
+ hello_blob=$(git hash-object -w -t blob hello) &&
+ subtree=$(
+ echo "100644 blob $hello_blob README.txt" |
+ git mktree
+ ) &&
+ expect=$(
+ git mktree <<-EOF
+ 100644 blob $hello_blob README.txt
+ 040000 tree $subtree trunk
+ EOF
+ ) &&
+
+ {
+ properties \
+ svn:author author@example.com \
+ svn:date "2012-10-10T00:01:003.000000Z" \
+ svn:log "created README.txt" &&
+ echo PROPS-END
+ } >r1.props &&
+ {
+ properties \
+ svn:author author@example.com \
+ svn:date "2012-10-10T00:02:005.000000Z" \
+ svn:log "created trunk" &&
+ echo PROPS-END
+ } >r2.props &&
+ {
+ cat <<-EOF &&
+ SVN-fs-dump-format-version: 3
+
+ Revision-number: 1
+ EOF
+ echo Prop-content-length: $(wc -c <r1.props) &&
+ echo Content-length: $(wc -c <r1.props) &&
+ echo &&
+ cat r1.props &&
+ cat <<-\EOF &&
+
+ Node-path: README.txt
+ Node-kind: file
+ Node-action: add
+ EOF
+ text_no_props hello &&
+ echo Revision-number: 2
+ echo Prop-content-length: $(wc -c <r2.props) &&
+ echo Content-length: $(wc -c <r2.props) &&
+ echo &&
+ cat r2.props &&
+ sed -e "s/X\$//" <<-\EOF
+
+ Node-path: trunk
+ Node-kind: dir
+ Node-action: add
+ Node-copyfrom-rev: 1
+ Node-copyfrom-path: X
+ Prop-content-length: 10
+ Content-length: 10
+
+ PROPS-END
+ EOF
+ } >copy-root.dump &&
+ try_dump copy-root.dump &&
+
+ git diff-tree --exit-code $expect HEAD
+'
+
test_expect_success PIPE 'branch name with backslash' '
reinit_git &&
sort <<-\EOF >expect.branch-files &&diff --git a/vcs-svn/fast_export.c b/vcs-svn/fast_export.c
index 19d7c34c..24232618 100644
--- a/vcs-svn/fast_export.c
+++ b/vcs-svn/fast_export.c
@@ -49,6 +49,12 @@ void fast_export_reset(void)
void fast_export_delete(const char *path)
{
+ /* delete("") means to return to a clean slate. */
+ if (!*path) {
+ printf("deleteall\n");
+ return;
+ }
+
putchar('D');
putchar(' ');
quote_c_style(path, NULL, stdout, 0);@@ -113,6 +119,8 @@ void fast_export_end_commit(uint32_t revision)
static void ls_from_rev(uint32_t rev, const char *path)
{
+ assert(*path);
+
/* ls :5 path/to/old/file */
printf("ls :%"PRIu32" ", rev);
quote_c_style(path, NULL, stdout, 0);@@ -122,6 +130,8 @@ static void ls_from_rev(uint32_t rev, const char *path)
static void ls_from_active_commit(const char *path)
{
+ assert(*path);
+
/* ls "path/to/file" */
printf("ls \"");
quote_c_style(path, NULL, stdout, 1);@@ -285,12 +295,25 @@ static int parse_ls_response(const char *response, uint32_t *mode,
int fast_export_ls_rev(uint32_t rev, const char *path,
uint32_t *mode, struct strbuf *dataref)
{
+ if (!*path) {
+ /*
+ * The easy case: when path is "", the caller can use
+ * :<rev> directly to refer to the root of the tree. (*)
+ */
+ strbuf_addf(dataref, ":%"PRIu32, rev);
+ *mode = REPO_MODE_DIR;
+ return 0;
+ }
+
ls_from_rev(rev, path);
return parse_ls_response(get_response_line(), mode, dataref);
}
-int fast_export_ls(const char *path, uint32_t *mode, struct strbuf *dataref)
+int fast_export_ls_nonroot(const char *path, uint32_t *mode,
+ struct strbuf *dataref)
{
+ assert(*path);
+
ls_from_active_commit(path);
return parse_ls_response(get_response_line(), mode, dataref);
}diff --git a/vcs-svn/fast_export.h b/vcs-svn/fast_export.h
index 43d05b65..53e208e2 100644
--- a/vcs-svn/fast_export.h
+++ b/vcs-svn/fast_export.h
@@ -22,7 +22,7 @@ void fast_export_blob_delta(uint32_t mode,
/* If there is no such file at that rev, returns -1, errno == ENOENT. */
int fast_export_ls_rev(uint32_t rev, const char *path,
uint32_t *mode_out, struct strbuf *dataref_out);
-int fast_export_ls(const char *path,
+int fast_export_ls_nonroot(const char *path,
uint32_t *mode_out, struct strbuf *dataref_out);
#endif
diff --git a/vcs-svn/repo_tree.c b/vcs-svn/repo_tree.c
index 67d27f0b..1547b8ce 100644
--- a/vcs-svn/repo_tree.c
+++ b/vcs-svn/repo_tree.c
@@ -8,13 +8,15 @@
#include "repo_tree.h"
#include "fast_export.h"
-const char *repo_read_path(const char *path, uint32_t *mode_out)
+const char *repo_read_nonroot_path(const char *path, uint32_t *mode_out)
{
int err;
static struct strbuf buf = STRBUF_INIT;
+ assert(*path);
+
strbuf_reset(&buf);
- err = fast_export_ls(path, mode_out, &buf);
+ err = fast_export_ls_nonroot(path, mode_out, &buf);
if (err) {
if (errno != ENOENT)
die_errno("BUG: unexpected fast_export_ls error");diff --git a/vcs-svn/repo_tree.h b/vcs-svn/repo_tree.h
index 889c6a3c..466d5a63 100644
--- a/vcs-svn/repo_tree.h
+++ b/vcs-svn/repo_tree.h
@@ -11,7 +11,7 @@ struct strbuf;
uint32_t next_blob_mark(void);
void repo_copy(uint32_t revision, const char *src, const char *dst);
void repo_add(const char *path, uint32_t mode, uint32_t blob_mark);
-const char *repo_read_path(const char *path, uint32_t *mode_out);
+const char *repo_read_nonroot_path(const char *path, uint32_t *mode_out);
void repo_delete(const char *path);
void repo_commit(uint32_t revision, const char *author,
const struct strbuf *log, const char *uuid, const char *url,
diff --git a/vcs-svn/svndump.c b/vcs-svn/svndump.c
index ca63760f..d610184d 100644
--- a/vcs-svn/svndump.c
+++ b/vcs-svn/svndump.c
@@ -248,7 +248,8 @@ static void handle_node(void)
old_data = NULL;
} else if (node_ctx.action == NODEACT_CHANGE) {
uint32_t mode;
- old_data = repo_read_path(node_ctx.dst.buf, &mode);
+ assert(*node_ctx.dst.buf);
+ old_data = repo_read_nonroot_path(node_ctx.dst.buf, &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)--
1.7.9.2