vcs-svn: skeleton of an svn delta parser
authorJonathan Nieder <jrnieder@gmail.com>
Sat, 25 Dec 2010 11:11:32 +0000 (05:11 -0600)
committerJonathan Nieder <jrnieder@gmail.com>
Mon, 28 Mar 2011 03:41:38 +0000 (22:41 -0500)
A delta in the subversion delta (svndiff0) format consists of the
magic bytes SVN\0 followed by a sequence of windows of a certain well
specified format (starting with five integers).

Add an svndiff0_apply function and test-svn-fe -d commandline tool to
parse such a delta in the special case of not including any windows.

Later patches will add features to turn this into a fully functional
delta applier for svn-fe to use to parse the streams produced by
"svnrdump dump" and "svnadmin dump --deltas".

The content of symlinks starts with the word "link " in Subversion's
worldview, so we need to be able to prepend that text to input for the
sake of delta application.  So initialization of the input state of
the delta preimage is left to the calling program, giving callers a
chance to seed the buffer with text of their choice.

Improved-by: Ramkumar Ramachandra <artagnon@gmail.com>
Improved-by: David Barr <david.barr@cordelta.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Makefile
t/t9011-svn-da.sh [new file with mode: 0755]
test-svn-fe.c
vcs-svn/svndiff.c [new file with mode: 0644]
vcs-svn/svndiff.h [new file with mode: 0644]

index 5cd141d5afb16b7cc2dcef3ab15d14895af1d0b8..cd7dd9723d23f4b8cdceb17a5e850523886f99b7 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1854,6 +1854,7 @@ VCSSVN_OBJS += vcs-svn/line_buffer.o
 VCSSVN_OBJS += vcs-svn/sliding_window.o
 VCSSVN_OBJS += vcs-svn/repo_tree.o
 VCSSVN_OBJS += vcs-svn/fast_export.o
+VCSSVN_OBJS += vcs-svn/svndiff.o
 VCSSVN_OBJS += vcs-svn/svndump.o
 
 VCSSVN_TEST_OBJS += test-obj-pool.o
@@ -1997,6 +1998,7 @@ VCSSVN_H += vcs-svn/line_buffer.h
 VCSSVN_H += vcs-svn/sliding_window.h
 VCSSVN_H += vcs-svn/repo_tree.h
 VCSSVN_H += vcs-svn/fast_export.h
+VCSSVN_H += vcs-svn/svndiff.h
 VCSSVN_H += vcs-svn/svndump.h
 
 $(VCSSVN_OBJS) $(VCSSVN_TEST_OBJS): $(LIB_H) $(VCSSVN_H)
diff --git a/t/t9011-svn-da.sh b/t/t9011-svn-da.sh
new file mode 100755 (executable)
index 0000000..ee0c1e2
--- /dev/null
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+test_description='test parsing of svndiff0 files
+
+Using the "test-svn-fe -d" helper, check that svn-fe correctly
+interprets deltas using various facilities (some from the spec,
+some only learned from practice).
+'
+. ./test-lib.sh
+
+>empty
+printf foo >preimage
+
+test_expect_success 'reject empty delta' '
+       test_must_fail test-svn-fe -d preimage empty 0
+'
+
+test_expect_success 'delta can empty file' '
+       printf "SVNQ" | q_to_nul >clear.delta &&
+       test-svn-fe -d preimage clear.delta 4 >actual &&
+       test_cmp empty actual
+'
+
+test_expect_success 'reject svndiff2' '
+       printf "SVN\002" >bad.filetype &&
+       test_must_fail test-svn-fe -d preimage bad.filetype 4
+'
+
+test_expect_failure 'one-window empty delta' '
+       printf "SVNQ%s" "QQQQQ" | q_to_nul >clear.onewindow &&
+       test-svn-fe -d preimage clear.onewindow 9 >actual &&
+       test_cmp empty actual
+'
+
+test_done
index b42ba789b176160285844e49cb834d60170d9a54..66bd04022dab32e894c5e6ca18702b69ad0a0ed0 100644 (file)
@@ -4,15 +4,41 @@
 
 #include "git-compat-util.h"
 #include "vcs-svn/svndump.h"
+#include "vcs-svn/svndiff.h"
+#include "vcs-svn/sliding_window.h"
+#include "vcs-svn/line_buffer.h"
 
 int main(int argc, char *argv[])
 {
-       if (argc != 2)
-               usage("test-svn-fe <file>");
-       if (svndump_init(argv[1]))
-               return 1;
-       svndump_read(NULL);
-       svndump_deinit();
-       svndump_reset();
-       return 0;
+       static const char test_svnfe_usage[] =
+               "test-svn-fe (<dumpfile> | [-d] <preimage> <delta> <len>)";
+       if (argc == 2) {
+               if (svndump_init(argv[1]))
+                       return 1;
+               svndump_read(NULL);
+               svndump_deinit();
+               svndump_reset();
+               return 0;
+       }
+       if (argc == 5 && !strcmp(argv[1], "-d")) {
+               struct line_buffer preimage = LINE_BUFFER_INIT;
+               struct line_buffer delta = LINE_BUFFER_INIT;
+               struct sliding_view preimage_view = SLIDING_VIEW_INIT(&preimage);
+               if (buffer_init(&preimage, argv[2]))
+                       die_errno("cannot open preimage");
+               if (buffer_init(&delta, argv[3]))
+                       die_errno("cannot open delta");
+               if (svndiff0_apply(&delta, (off_t) strtoull(argv[4], NULL, 0),
+                                       &preimage_view, stdout))
+                       return 1;
+               if (buffer_deinit(&preimage))
+                       die_errno("cannot close preimage");
+               if (buffer_deinit(&delta))
+                       die_errno("cannot close delta");
+               buffer_reset(&preimage);
+               strbuf_release(&preimage_view.buf);
+               buffer_reset(&delta);
+               return 0;
+       }
+       usage(test_svnfe_usage);
 }
diff --git a/vcs-svn/svndiff.c b/vcs-svn/svndiff.c
new file mode 100644 (file)
index 0000000..5916036
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Licensed under a two-clause BSD-style license.
+ * See LICENSE for details.
+ */
+
+#include "git-compat-util.h"
+#include "line_buffer.h"
+#include "svndiff.h"
+
+/*
+ * svndiff0 applier
+ *
+ * See http://svn.apache.org/repos/asf/subversion/trunk/notes/svndiff.
+ *
+ * svndiff0 ::= 'SVN\0' window*
+ */
+
+static int error_short_read(struct line_buffer *input)
+{
+       if (buffer_ferror(input))
+               return error("error reading delta: %s", strerror(errno));
+       return error("invalid delta: unexpected end of file");
+}
+
+static int read_magic(struct line_buffer *in, off_t *len)
+{
+       static const char magic[] = {'S', 'V', 'N', '\0'};
+       struct strbuf sb = STRBUF_INIT;
+
+       if (*len < sizeof(magic) ||
+           buffer_read_binary(in, &sb, sizeof(magic)) != sizeof(magic))
+               return error_short_read(in);
+
+       if (memcmp(sb.buf, magic, sizeof(magic)))
+               return error("invalid delta: unrecognized file type");
+
+       *len -= sizeof(magic);
+       strbuf_release(&sb);
+       return 0;
+}
+
+int svndiff0_apply(struct line_buffer *delta, off_t delta_len,
+                       struct sliding_view *preimage, FILE *postimage)
+{
+       assert(delta && preimage && postimage);
+
+       if (read_magic(delta, &delta_len))
+               return -1;
+       if (delta_len)
+               return error("What do you think I am?  A delta applier?");
+       return 0;
+}
diff --git a/vcs-svn/svndiff.h b/vcs-svn/svndiff.h
new file mode 100644 (file)
index 0000000..74eb464
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef SVNDIFF_H_
+#define SVNDIFF_H_
+
+struct line_buffer;
+struct sliding_view;
+
+extern int svndiff0_apply(struct line_buffer *delta, off_t delta_len,
+               struct sliding_view *preimage, FILE *postimage);
+
+#endif