Code

vcs-svn: handle log message with embedded NUL
authorJonathan Nieder <jrnieder@gmail.com>
Sat, 26 Mar 2011 05:49:37 +0000 (00:49 -0500)
committerJonathan Nieder <jrnieder@gmail.com>
Sat, 26 Mar 2011 05:49:37 +0000 (00:49 -0500)
Pass the log message by strbuf instead of as a C-style string and use
fwrite instead of printf to write it to fast-import so embedded '\0'
bytes can be preserved.

Currently "git log" doesn't show the embedded NULs but "git cat-file
commit" can.

While at it, stop including system headers from repo_tree.h.  git
source files need to include git-compat-util.h (or cache.h or
builtin.h) sooner to ensure the appropriate feature test macros are
defined.

Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
t/t9010-svn-fe.sh
vcs-svn/fast_export.c
vcs-svn/fast_export.h
vcs-svn/repo_tree.c
vcs-svn/repo_tree.h
vcs-svn/svndump.c

index 5e2b123080747a77f8004c6cfaef9fcb7eb9e26f..478c860647b78518820cd244042584131c485914 100755 (executable)
@@ -397,6 +397,83 @@ test_expect_success 'NUL in property value' '
        test_cmp expect.message actual.message
 '
 
+test_expect_success 'NUL in log message, file content, and property name' '
+       # Caveat: svnadmin 1.6.16 (r1073529) truncates at \0 in the
+       # svn:specialQnotreally example.
+       reinit_git &&
+       cat >expect <<-\EOF &&
+       OBJID
+       :100644 100644 OBJID OBJID M    greeting
+       OBJID
+       :000000 100644 OBJID OBJID A    greeting
+       EOF
+       printf "\n%s" "something with an ASCII NUL (Q)" >expect.message &&
+       printf "%s\n" "helQo" >expect.hello1 &&
+       printf "%s\n" "link hello" >expect.hello2 &&
+       {
+               properties svn:log "something with an ASCII NUL (Q)" &&
+               echo PROPS-END
+       } |
+       q_to_nul >props &&
+       {
+               q_to_nul <<-\EOF &&
+               SVN-fs-dump-format-version: 3
+
+               Revision-number: 1
+               Prop-content-length: 10
+               Content-length: 10
+
+               PROPS-END
+
+               Node-path: greeting
+               Node-kind: file
+               Node-action: add
+               Prop-content-length: 10
+               Text-content-length: 6
+               Content-length: 16
+
+               PROPS-END
+               helQo
+
+               Revision-number: 2
+               EOF
+               echo Prop-content-length: $(wc -c <props) &&
+               echo Content-length: $(wc -c <props) &&
+               echo &&
+               cat props &&
+               q_to_nul <<-\EOF
+
+               Node-path: greeting
+               Node-kind: file
+               Node-action: change
+               Prop-content-length: 43
+               Text-content-length: 11
+               Content-length: 54
+
+               K 21
+               svn:specialQnotreally
+               V 1
+               *
+               PROPS-END
+               link hello
+               EOF
+       } >8bitclean.dump &&
+       test-svn-fe 8bitclean.dump >stream &&
+       git fast-import <stream &&
+       {
+               git rev-list HEAD |
+               git diff-tree --root --stdin |
+               sed "s/$_x40/OBJID/g"
+       } >actual &&
+       git cat-file commit HEAD | nul_to_q | sed -ne "/^\$/,\$ p" >actual.message &&
+       git cat-file blob HEAD^:greeting | nul_to_q >actual.hello1 &&
+       git cat-file blob HEAD:greeting | nul_to_q >actual.hello2 &&
+       test_cmp expect actual &&
+       test_cmp expect.message actual.message &&
+       test_cmp expect.hello1 actual.hello1 &&
+       test_cmp expect.hello2 actual.hello2
+'
+
 test_expect_success 'change file mode and reiterate content' '
        reinit_git &&
        cat >expect <<-\EOF &&
index a4d4d9993dac74f0e751009f30588aef62d3a573..2e5bb6725568c06125d9e856f9bddd0b50b407c2 100644 (file)
@@ -31,12 +31,14 @@ void fast_export_modify(uint32_t depth, uint32_t *path, uint32_t mode,
 }
 
 static char gitsvnline[MAX_GITSVN_LINE_LEN];
-void fast_export_commit(uint32_t revision, const char *author, char *log,
+void fast_export_commit(uint32_t revision, const char *author,
+                       const struct strbuf *log,
                        const char *uuid, const char *url,
                        unsigned long timestamp)
 {
+       static const struct strbuf empty = STRBUF_INIT;
        if (!log)
-               log = "";
+               log = &empty;
        if (*uuid && *url) {
                snprintf(gitsvnline, MAX_GITSVN_LINE_LEN,
                                "\n\ngit-svn-id: %s@%"PRIu32" %s\n",
@@ -49,9 +51,9 @@ void fast_export_commit(uint32_t revision, const char *author, char *log,
                   *author ? author : "nobody",
                   *author ? author : "nobody",
                   *uuid ? uuid : "local", timestamp);
-       printf("data %"PRIu32"\n%s%s\n",
-                  (uint32_t) (strlen(log) + strlen(gitsvnline)),
-                  log, gitsvnline);
+       printf("data %"PRIuMAX"\n", log->len + strlen(gitsvnline));
+       fwrite(log->buf, log->len, 1, stdout);
+       printf("%s\n", gitsvnline);
        if (!first_commit_done) {
                if (revision > 1)
                        printf("from refs/heads/master^0\n");
index 05cf97f3a7854e3ddf7060b361278ca82adc844b..33a8fe996f5fc025f587c1d09ae3f81e1786dbbb 100644 (file)
@@ -2,13 +2,14 @@
 #define FAST_EXPORT_H_
 
 #include "line_buffer.h"
+struct strbuf;
 
 void fast_export_delete(uint32_t depth, uint32_t *path);
 void fast_export_modify(uint32_t depth, uint32_t *path, uint32_t mode,
                        uint32_t mark);
-void fast_export_commit(uint32_t revision, const char *author, char *log,
-                       const char *uuid, const char *url,
-                       unsigned long timestamp);
+void fast_export_commit(uint32_t revision, const char *author,
+                       const struct strbuf *log, const char *uuid,
+                       const char *url, unsigned long timestamp);
 void fast_export_blob(uint32_t mode, uint32_t mark, uint32_t len,
                      struct line_buffer *input);
 
index d722e3212fa7db5e3b3f6ddbd32765b5172b7063..8caa0159d1f40e25303c5624ec7d17471435cf47 100644 (file)
@@ -278,8 +278,9 @@ void repo_diff(uint32_t r1, uint32_t r2)
                    repo_commit_root_dir(commit_pointer(r2)));
 }
 
-void repo_commit(uint32_t revision, const char *author, char *log,
-                const char *uuid, const char *url, unsigned long timestamp)
+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();
index a1b0e87651b7576cd4e1842261d745664331793a..37bde2e37484071998edbe790d38d7b19eb24fb5 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef REPO_TREE_H_
 #define REPO_TREE_H_
 
-#include "git-compat-util.h"
+struct strbuf;
 
 #define REPO_MODE_DIR 0040000
 #define REPO_MODE_BLB 0100644
@@ -18,7 +18,7 @@ uint32_t repo_read_path(const uint32_t *path);
 uint32_t repo_read_mode(const uint32_t *path);
 void repo_delete(uint32_t *path);
 void repo_commit(uint32_t revision, const char *author,
-               char *log, const char *uuid, const char *url,
+               const struct strbuf *log, const char *uuid, const char *url,
                long unsigned timestamp);
 void repo_diff(uint32_t r1, uint32_t r2);
 void repo_init(void);
index 88ecef1066efe3e1d0a6a6d7a08f691321095297..eef49ca1928d21f7bb0d038693fd92c2a6e5ac85 100644 (file)
@@ -274,7 +274,7 @@ static void handle_revision(void)
 {
        if (rev_ctx.revision)
                repo_commit(rev_ctx.revision, rev_ctx.author.buf,
-                       rev_ctx.log.buf, dump_ctx.uuid.buf, dump_ctx.url.buf,
+                       &rev_ctx.log, dump_ctx.uuid.buf, dump_ctx.url.buf,
                        rev_ctx.timestamp);
 }