Merge from trunk
git-svn-id: https://svn.apache.org/repos/asf/subversion/branches/svndiff1@857694 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/configure.in b/configure.in
index cba7379..9fc67df 100644
--- a/configure.in
+++ b/configure.in
@@ -503,20 +503,12 @@
])
-AC_ARG_WITH(zlib,
-AC_HELP_STRING([--with-zlib], [enable zlib support]),
-[
-
- if test "$withval" = "yes" ; then
- AC_CHECK_HEADER(zlib.h, [
+AC_CHECK_HEADER(zlib.h, [
AC_CHECK_LIB(z, inflate, [
AC_DEFINE([SVN_HAVE_ZLIB], [1], [Is zlib support enabled?])
LIBS="$LIBS -lz"
- ])
+ ], [AC_MSG_ERROR([subversion requires zlib])])
])
- fi
-
-])
MOD_ACTIVATION="-a"
AC_ARG_ENABLE(mod-activation,
diff --git a/notes/svndiff b/notes/svndiff
index 574baef..258549a 100644
--- a/notes/svndiff
+++ b/notes/svndiff
@@ -1,7 +1,7 @@
-This file describes the svndiff format used by the Subversion code.
-Its design borrows many ideas from the vdelta and vcdiff encoding
-formats from AT&T Research Labs, but it is much simpler and thus a
-little less compact.
+This file describes the svndiff version 0 and 1 format used by the
+Subversion code. Its design borrows many ideas from the vdelta and
+vcdiff encoding formats from AT&T Research Labs, but it is much
+simpler and thus a little less compact.
From the point of view of svndiff, a delta is a sequence of windows,
each containing a list of instructions for reconstructing a contiguous
@@ -28,13 +28,21 @@
The target view length
The length of the instructions in bytes
The length of the new data in bytes
- The window's instructions
- The window's new data (as raw data)
+ The window's instructions section
+ The window's new data (as raw data) section
-Integers (including the first five items listed above) are encoded
-using a variable-length format. The high bit of each byte is used as
-a continuation bit; 1 indicates that there is more data and 0
-indicates the final byte. The other seven bits of each byte are data.
+In svndiff version 1, the instructions, and new data
+sections may be compressed by zlib. In order to determine the
+original size, an integer is appended to the beginning of each of the
+sections. If the original size matches the encoded size (minus the
+length of the original size integer)from the header, the data is not
+compressed. If the original size is different than the encoded size
+from the header, the remaining data in the section.
+
+Integers (including the integers described bove) are encoded using a
+variable-length format. The high bit of each byte is used as a
+continuation bit; 1 indicates that there is more data and 0 indicates
+the final byte. The other seven bits of each byte are data.
Higher-order bits are encoded before lower-order bits. As an example,
130 would be encoded as two bytes, 10000001 followed by 00000010.
diff --git a/notes/svndiff-v1 b/notes/svndiff-v1
new file mode 100644
index 0000000..79614d4
--- /dev/null
+++ b/notes/svndiff-v1
@@ -0,0 +1,222 @@
+This email is long, so i sectionized it into "Introduction and
+justification", "Changes I made", "Sparkly numbers", "Time Costs and
+Backwards Compatibility"
+:)
+
+Introduction and justification:
+
+First, I should probably explain what svndiff is, and what it looks like
+( which requires a bit of history), and why it needs improving:
+
+Basically, when subversion started, it was decided to keep the internal
+diff format stored on disk and transmitted over the wire
+different from the actual diff instructions, which was a good idea.
+In other words, the instructions that make up a binary diff are add and
+copy.
+How the binary diff is actually encoded, stored, and transmitted over
+the wire is different than that, however.
+
+Thus, if you look at the diffs as they are transmitted over the network,
+or stored in the actual repository revisions, you are looking at
+"svndiff" encoded data.
+
+Back in the old days, when there was discussion about what to make this
+format look like people were on a DAV kick, and the binary delta
+algorithm vdelta was chosen as the delta algorithm.
+At the same time, a format called "VCDIFF" was being standardized.
+VCDIFF is now rfc 3284 (http://www.faqs.org/rfcs/rfc3284.html).
+VCDIFF defines a delta encoding format that is space efficient, but
+reasonably easy to decode.
+However, VCDIFF was seen as overkill for the internal diff format
+It was assumed we would store it in some nicer thing, and if we wanted,
+transform that to VCDIFF to be sent over the wire
+This was the right decision, and for most things, VCDIFF *is* overkill
+So some things were taken from VCDIFF, and the delta format we use,
+called "SVNDIFF", was born.
+It turns out, hoewver, that svndiff0 (the current version) is not so
+efficient in some particular use cases that are not uncommon.
+Prior to now, We relied on vdelta to compress these cases, instead of
+relying on having good delta encoding, which in hindsight, was probably
+not a good idea.
+Vdelta did an okay job, but generated horrible to combine deltas in some
+cases, which caused us to move to xdelta.
+As noted in 1.2 release notes, we pay about a 15% size penalty for using
+xdelta in the common case.
+
+The not-so-uncommon case where our encoding is not efficientis when you
+are repeatedly merging things to branches. and adding new data from
+trunk.
+To understand why, you need to know a bit more about the internal
+machinations of svndiff:
+
+svndiff is split into sections:
+
+There is an instruction section, which contains encoded copy and new
+data instructions (IE copy from this part of the source, for this
+length, or "add this new data").
+
+There is also new data section, which contains the new data used by the
+"add this new data" instructions.
+
+When you repeatedly merge things from trunk to a branch, like say, a
+ChangeLog, you end up with a lot of data new to that branch. This in
+turns, ends up completely uncompressed in the new data section of
+svndiff.
+
+If you stare at something like the on-disk gcc repository revision
+files, which have a lot of branch data, you will see there is just tons
+of files that large pieces of plaintext data that represent merges from
+trunk to branch.
+
+In fact, we have revision files that are 30 meg each, and in each case,
+80% of the data is just the new data section of a bunch of deltas.
+
+This is pretty bad.
+
+Changes I made:
+
+To alleviate this problem, back when the earth's crust was cooling
+(2002), i came up with an svndiff version 1, which compressed the new
+data section using a secondary compressor. At the time, it was a
+standalone range encoder.
+
+I've recently revived that patch, and brought it up to date. I've
+changed the compressor to use zlib, which is now *everywhere*.
+
+Sparkly numbers:
+This change buys about 40% of the disk space on the gcc repository.
+8.5 gig to 5.2
+
+A repository consisting of just gcc's changelog file, from all branches,
+used to take up:
+
+583178 db/revs
+
+With the svndiff 1 patch, it now takes up
+
+356591 gccrepo/db/revs
+
+a 39% savings.
+
+Time Costs and Backwards Compatibility
+
+I should first note the cost is essentially zero in terms of time. We
+only bother to compress if the new data section ends up being bigger
+than some minimum size (currently 1024), and even then, the
+compression/decompression time is completely lost in the noise, AFAICT.
+
+In fact, for gcc's repo, it's actually faster, since it used to read 30
+meg of revision file, and now only needs to read 5 meg, and the i/o was
+slower than the decompression time.
+
+As far as backwards compatibility, let me start by assuaging the most
+common concern:
+
+svndiff0 works perfectly, and we can always tell whether we have
+svndiff1 or 0. This was actually not true in 2002, but part of the
+patch back then (which made it in as part of some work c-mike as doing)
+was to make the 'SVN<byte> header contain the version number in byte.
+All our current code understands and respects this version number,
+except for one small thing:
+
+svn_txdelta_to_svndiff doesn't take a version number.
+
+I've simply rev'd it to take a version number (IE made
+svn_txdelta_to_svndiff2), and made the current function always use
+version 0.
+
+
+Parsing doesn't need to take an explicit version number, we can read it
+out of the header.
+
+
+The first thing i should note is that we now simply require zlib. I
+haven't yet copied any configure magic necessary to let people point at
+a place for it. But i don't think this is an unreasonable requirement,
+as *everyone* ships zlib.
+
+We have two things we *actually* need to worry about
+
+Communicating with clients/servers using svndiff1 over the wire
+
+and
+
+Storing svndiff1 inside fsfs and bdb repositories.
+
+The first case is actually easy to make backwards compatible, and i have
+done so.
+
+svnserve has a capabilities list it outputs from the server, and sends
+from the client. We can simply check to make sure the side we want to
+send svndiff1 to, supports it, by introducing a new capability and
+checking for it. Unless we find a capability called "svndiff1", we only
+send svndiff0.
+
+mod_dav_svn + ra_dav lets us do a similiar thing with headers (I haven't
+quite started this one yet, but this is what i am told)
+
+
+The more interesting question is the stuff stored in the repo.
+
+If we store svndiff1 in fsfs or bdb, only things that know about
+svndiff1 can read it. This means older clients trying to directly
+access the repo would fail.
+
+We currently have no easy way to tell what version is in an svn
+repository, other than the "format" file.
+
+So we have two options:
+
+1. Rev the format, allow creation of old/new format (defaulting to old),
+and require dump and load to take advantage of svndiff1.
+
+or a more interesting solution that came to my head, which is to borrow
+an idea from real fs'en, and introduce a "features" file.
+
+As an example, ext3 has feature bits set on the filesystem. There is a
+feature bit for dir indexing, for whether the fs has a journal, etc.
+
+If we added a feature file to the fsfs format (and bdb format), we could
+actually let people control the features contained in their fs, and only
+need a dump + load to change between features, instead of *requiring*
+that they use certain features.
+
+IOW, later on, if we add hash-indexing, we could make it optional simply
+by adding a feature name for it, and putting that in the feature file.
+
+If you wanted to get rid of hash indexing, you simply create a repo
+without that feature (svnadmin would be extended to turn features on and
+off, and tell you whether you need dump/load to do this right), dump the
+old repo, and load into the new one.
+
+Boom, no more hash indexing, and we didn't need to change the format
+file back to and older revision.
+
+The same could be true of svndiff1. We can simply make it optional, and
+instead of revving the format, make it a feature, much like how in
+svnserve it is simply a capability.
+
+The advantage here is that for something like svndiff1, if you don't
+want to dump/load, if you added it to the features, it would just cause
+newer revisions to use svndiff1, and the older ones would stay the way
+they are.
+Dunno whether we care or not.
+
+If we go with this, the features file would mostly be a written hash
+containing the features, and read on fs open. If we find a feature we
+can't support, we error out.
+
+Or we could just rev the format :)
+
+Any hoo, i've attached the current patch, which i've cross-tested 1.2.x
+against patched 1.4.x servers, patched 1.4.x against 1.2.x servers, and
+the same type of deal for 1.3.x.
+
+I've not dealt with backwards compat for repos yet. If you use this
+patch, you're created repos will use svndiff1.
+
+I obviously have no plans to commit this until we work out these issues,
+and revise the patch. Thus, style nits, etc, are not necessary. I'm
+aware it's non-perfect :)
+
+--Dan
\ No newline at end of file
diff --git a/subversion/include/svn_delta.h b/subversion/include/svn_delta.h
index c7ee24d..3b94409 100644
--- a/subversion/include/svn_delta.h
+++ b/subversion/include/svn_delta.h
@@ -242,6 +242,16 @@
svn_txdelta_stream_t *stream,
apr_pool_t *pool);
+/** Set @a *window to a pointer to the next window from the delta stream
+ * @a stream. When we have completely reconstructed the target string,
+ * set @a *window to zero.
+ *
+ * The window will be allocated in @a pool.
+ */
+svn_error_t *svn_txdelta_next_window (svn_txdelta_window_t **window,
+ svn_txdelta_stream_t *stream,
+ apr_pool_t *pool);
+
/** Return the @a md5 digest for the complete fulltext deltified by
* @a stream, or @c NULL if @a stream has not yet returned its final
@@ -355,6 +365,19 @@
* @a output is a writable generic stream to write the svndiff data to.
* Allocation takes place in a sub-pool of @a pool. On return, @a *handler
* is set to a window handler function and @a *handler_baton is set to
+ * the value to pass as the @a baton argument to @a *handler. The svndiff
+ * version is @a version.
+ */
+
+void svn_txdelta_to_svndiff2 (svn_stream_t *output,
+ apr_pool_t *pool,
+ svn_txdelta_window_handler_t *handler,
+ void **handler_baton, unsigned int version);
+
+/** Prepare to produce an svndiff-format diff from text delta windows.
+ * @a output is a writable generic stream to write the svndiff data to.
+ * Allocation takes place in a sub-pool of @a pool. On return, @a *handler
+ * is set to a window handler function and @a *handler_baton is set to
* the value to pass as the @a baton argument to @a *handler.
*/
void svn_txdelta_to_svndiff (svn_stream_t *output,
diff --git a/subversion/include/svn_error_codes.h b/subversion/include/svn_error_codes.h
index 7338fc3..e63d016 100644
--- a/subversion/include/svn_error_codes.h
+++ b/subversion/include/svn_error_codes.h
@@ -819,6 +819,15 @@
SVN_ERRDEF (SVN_ERR_SVNDIFF_UNEXPECTED_END,
SVN_ERR_SVNDIFF_CATEGORY_START + 4,
"Svndiff data ends unexpectedly")
+
+ SVN_ERRDEF (SVN_ERR_SVNDIFF_INVALID_VERSION,
+ SVN_ERR_SVNDIFF_CATEGORY_START + 5,
+ "Svndiff version greater than known max")
+
+ SVN_ERRDEF (SVN_ERR_SVNDIFF_INVALID_COMPRESSED_DATA,
+ SVN_ERR_SVNDIFF_CATEGORY_START + 6,
+ "Svndiff compressed data is invalid")
+
/* mod_dav_svn errors */
diff --git a/subversion/include/svn_fs.h b/subversion/include/svn_fs.h
index 6fde21b..7c65a73 100644
--- a/subversion/include/svn_fs.h
+++ b/subversion/include/svn_fs.h
@@ -66,6 +66,12 @@
#define SVN_FS_TYPE_BDB "bdb"
/** @since New in 1.1. */
#define SVN_FS_TYPE_FSFS "fsfs"
+
+/** Don't allow svndiff1 to be used in the on-disk storage
+ *
+ * @since New in 1.4.
+ */
+#define SVN_FS_CONFIG_NO_SVNDIFF1 "no-svndiff1"
/** @} */
diff --git a/subversion/include/svn_ra_svn.h b/subversion/include/svn_ra_svn.h
index a5edd8a..9fd211f 100644
--- a/subversion/include/svn_ra_svn.h
+++ b/subversion/include/svn_ra_svn.h
@@ -41,6 +41,7 @@
/** Currently-defined capabilities. */
#define SVN_RA_SVN_CAP_EDIT_PIPELINE "edit-pipeline"
+#define SVN_RA_SVN_CAP_SVNDIFF1 "svndiff1"
/** ra_svn passes @c svn_dirent_t fields over the wire as a list of
* words, these are the values used to represent each field.
diff --git a/subversion/libsvn_delta/svndiff.c b/subversion/libsvn_delta/svndiff.c
index 662f012..c2365c5 100644
--- a/subversion/libsvn_delta/svndiff.c
+++ b/subversion/libsvn_delta/svndiff.c
@@ -24,7 +24,11 @@
#include "delta.h"
#include "svn_pools.h"
#include "svn_private_config.h"
+#include <zlib.h>
+/* For svndiff1, address/instruction/new data under this size will not
+ be compressed using zlib as a secondary compressor. */
+#define MIN_COMPRESS_SIZE 512
#define NORMAL_BITS 7
#define LENGTH_BITS 5
@@ -37,10 +41,10 @@
struct encoder_baton {
svn_stream_t *output;
svn_boolean_t header_done;
+ unsigned int version;
apr_pool_t *pool;
};
-
/* Encode VAL into the buffer P using the variable-length svndiff
integer format. Return the incremented value of P after the
encoded bytes have been written.
@@ -99,6 +103,41 @@
svn_stringbuf_appendbytes (header, buf, p - buf);
}
+static svn_error_t *
+zlib_encode (svn_stringbuf_t *in, svn_stringbuf_t *out)
+{
+ unsigned long endlen;
+ unsigned int intlen;
+
+ append_encoded_int (out, in->len, NULL);
+ intlen = out->len;
+
+ if (in->len < MIN_COMPRESS_SIZE)
+ {
+ svn_stringbuf_appendstr (out, in);
+ }
+ else
+ {
+ svn_stringbuf_ensure (out, compressBound (in->len) + intlen);
+ endlen = out->blocksize;
+
+ if (compress2 ((unsigned char *)out->data + intlen, &endlen,
+ (const unsigned char *)in->data, in->len, 5) != Z_OK)
+
+ return svn_error_create (SVN_ERR_SVNDIFF_INVALID_COMPRESSED_DATA,
+ NULL,
+ _("compression of svndiff data failed"));
+
+ /* Compression didn't help :(, just append the original text */
+ if (endlen > in->len)
+ {
+ svn_stringbuf_appendstr (out, in);
+ return SVN_NO_ERROR;
+ }
+ out->len = endlen + intlen;
+ }
+ return SVN_NO_ERROR;
+}
static svn_error_t *
window_handler (svn_txdelta_window_t *window, void *baton)
@@ -106,17 +145,23 @@
struct encoder_baton *eb = baton;
apr_pool_t *pool = svn_pool_create (eb->pool);
svn_stringbuf_t *instructions = svn_stringbuf_create ("", pool);
+ svn_stringbuf_t *i1 = svn_stringbuf_create ("", pool);
svn_stringbuf_t *header = svn_stringbuf_create ("", pool);
+ svn_stringbuf_t *newdata = svn_stringbuf_create ("", pool);
char ibuf[128], *ip;
+ char abuf[128], *ap;
const svn_txdelta_op_t *op;
- svn_error_t *err;
apr_size_t len;
+ apr_size_t lastoffset = 0;
+ apr_size_t bits = 0;
/* Make sure we write the header. */
if (eb->header_done == FALSE)
{
+ char svnver[4] = "SVN\0";
len = 4;
- SVN_ERR (svn_stream_write (eb->output, "SVN\0", &len));
+ svnver[3] = eb->version;
+ SVN_ERR (svn_stream_write (eb->output, svnver, &len));
eb->header_done = TRUE;
}
@@ -146,6 +191,7 @@
{
/* Encode the action code and length. */
ip = ibuf;
+ ap = abuf;
switch (op->action_code)
{
case svn_txdelta_source: *ip = (char)0; break;
@@ -156,8 +202,11 @@
*ip++ |= op->length;
else
ip = encode_int (ip + 1, op->length);
+
if (op->action_code != svn_txdelta_new)
- ip = encode_int (ip, op->offset);
+ {
+ ip = encode_int (ip, op->offset);
+ }
svn_stringbuf_appendbytes (instructions, ibuf, ip - ibuf);
}
@@ -165,25 +214,58 @@
append_encoded_int (header, window->sview_offset, pool);
append_encoded_int (header, window->sview_len, pool);
append_encoded_int (header, window->tview_len, pool);
+ if (eb->version == 1)
+ {
+ SVN_ERR (zlib_encode (instructions, i1));
+ instructions = i1;
+ }
append_encoded_int (header, instructions->len, pool);
+ if (eb->version == 1)
+ {
+ svn_stringbuf_t *temp;
+ temp = svn_stringbuf_create_from_string (window->new_data, pool);
+ SVN_ERR (zlib_encode (temp, newdata));
+ window->new_data = svn_string_create_from_buf (newdata, pool);
+ }
+
append_encoded_int (header, window->new_data->len, pool);
/* Write out the window. */
len = header->len;
- err = svn_stream_write (eb->output, header->data, &len);
- if (err == SVN_NO_ERROR && instructions->len > 0)
+ SVN_ERR (svn_stream_write (eb->output, header->data, &len));
+ if (instructions->len > 0)
{
len = instructions->len;
- err = svn_stream_write (eb->output, instructions->data, &len);
+ SVN_ERR (svn_stream_write (eb->output, instructions->data, &len));
}
- if (err == SVN_NO_ERROR && window->new_data->len > 0)
+ if (window->new_data->len > 0)
{
len = window->new_data->len;
- err = svn_stream_write (eb->output, window->new_data->data, &len);
+ SVN_ERR (svn_stream_write (eb->output, window->new_data->data, &len));
}
svn_pool_destroy (pool);
- return err;
+ return SVN_NO_ERROR;
+}
+
+void
+svn_txdelta_to_svndiff2 (svn_stream_t *output,
+ apr_pool_t *pool,
+ svn_txdelta_window_handler_t *handler,
+ void **handler_baton,
+ unsigned int version)
+{
+ apr_pool_t *subpool = svn_pool_create (pool);
+ struct encoder_baton *eb;
+
+ eb = apr_palloc (subpool, sizeof (*eb));
+ eb->output = output;
+ eb->header_done = FALSE;
+ eb->pool = subpool;
+ eb->version = version;
+
+ *handler = window_handler;
+ *handler_baton = eb;
}
void
@@ -199,6 +281,7 @@
eb->output = output;
eb->header_done = FALSE;
eb->pool = subpool;
+ eb->version = 0;
*handler = window_handler;
*handler_baton = eb;
@@ -240,6 +323,10 @@
not transmit the whole svndiff data stream, you will want this to
be FALSE. */
svn_boolean_t error_on_early_close;
+
+ /* svndiff version in use by delta. */
+ unsigned char version;
+
};
@@ -283,6 +370,48 @@
return NULL;
}
+static svn_error_t *
+zlib_decode (svn_stringbuf_t *in, svn_stringbuf_t *out)
+{
+ apr_size_t len;
+ unsigned long zliblen;
+ unsigned long origlen;
+ char *oldplace = in->data;
+
+ assert (sizeof (zliblen) >= sizeof (len));
+ /* First thing in the string is the original length. */
+ in->data = (char *)decode_size (&len, (unsigned char *)in->data,
+ (unsigned char *)in->data+in->len);
+ /* We need to subtract the size of the encoded original length off the
+ * still remaining input length. */
+ in->len -= (in->data - oldplace);
+ if (in->len == len)
+ {
+ svn_stringbuf_appendstr (out, in);
+ return SVN_NO_ERROR;
+ }
+ else
+ {
+ svn_stringbuf_ensure (out, len);
+
+ origlen = len;
+ zliblen = len;
+ if (uncompress ((unsigned char *)out->data, &zliblen,
+ (const unsigned char *)in->data, in->len) != Z_OK)
+ return svn_error_create (SVN_ERR_SVNDIFF_INVALID_COMPRESSED_DATA,
+ NULL,
+ _("decompression of svndiff data failed"));
+
+ /* Zlib should not produce something that has a different size than the
+ original length we stored. */
+ assert (zliblen == origlen);
+ out->len = zliblen;
+ }
+ return SVN_NO_ERROR;
+}
+
+
+
/* Decode an instruction into OP, returning a pointer to the text
after the instruction. Note that if the action code is
@@ -405,30 +534,55 @@
static svn_error_t *
decode_window (svn_txdelta_window_t *window, svn_filesize_t sview_offset,
apr_size_t sview_len, apr_size_t tview_len, apr_size_t inslen,
- apr_size_t newlen, const unsigned char *data, apr_pool_t *pool)
+ apr_size_t newlen, const unsigned char *data, apr_pool_t *pool,
+ unsigned int version)
{
- const unsigned char *end;
+ const unsigned char *insend;
int ninst;
+ apr_size_t saved_inslen, saved_newlen;
apr_size_t npos;
svn_txdelta_op_t *ops, *op;
svn_string_t *new_data;
+ svn_stringbuf_t *instin, *instout;
+ svn_stringbuf_t *ndin, *ndout;
window->sview_offset = sview_offset;
window->sview_len = sview_len;
window->tview_len = tview_len;
+ saved_inslen = inslen;
+ saved_newlen = newlen;
+ insend = data + inslen;
+
+ if (version == 1)
+ {
+ instin = svn_stringbuf_ncreate ((const char *)data, insend - data, pool);
+ instout = svn_stringbuf_create ("", pool);
+ SVN_ERR (zlib_decode (instin, instout));
+
+ ndin = svn_stringbuf_ncreate ((const char *)insend, newlen, pool);
+ ndout = svn_stringbuf_create ("", pool);
+ SVN_ERR (zlib_decode (ndin, ndout));
+
+ newlen = ndout->len;
+ data = (unsigned char *)instout->data;
+ insend = (unsigned char *)instout->data + instout->len;
+ }
+
/* Count the instructions and make sure they are all valid. */
- end = data + inslen;
- SVN_ERR (count_and_verify_instructions (&ninst, data, end, sview_len,
- tview_len, newlen));
+
+ SVN_ERR (count_and_verify_instructions (&ninst, data, insend,
+ sview_len, tview_len, newlen));
+
/* Allocate a buffer for the instructions and decode them. */
ops = apr_palloc (pool, ninst * sizeof (*ops));
npos = 0;
+
window->src_ops = 0;
for (op = ops; op < ops + ninst; op++)
{
- data = decode_instruction (op, data, end);
+ data = decode_instruction (op, data, insend);
if (op->action_code == svn_txdelta_source)
++window->src_ops;
else if (op->action_code == svn_txdelta_new)
@@ -442,7 +596,12 @@
window->num_ops = ninst;
new_data = apr_palloc (pool, sizeof (*new_data));
- new_data->data = (const char *) data;
+
+ if (version == 1)
+ new_data->data = (const char *)ndout->data;
+ else
+ new_data->data = (const char *) data;
+
new_data->len = newlen;
window->new_data = new_data;
@@ -466,7 +625,8 @@
apr_size_t nheader = 4 - db->header_bytes;
if (nheader > buflen)
nheader = buflen;
- if (memcmp (buffer, "SVN\0" + db->header_bytes, nheader) != 0)
+ db->version = buffer[3];
+ if (memcmp (buffer, "SVN" + db->header_bytes, nheader - 1) != 0)
return svn_error_create (SVN_ERR_SVNDIFF_INVALID_HEADER, NULL,
_("Svndiff has invalid header"));
buflen -= nheader;
@@ -541,7 +701,8 @@
/* Decode the window and send it off. */
SVN_ERR (decode_window (&window, sview_offset, sview_len, tview_len,
- inslen, newlen, p, db->subpool));
+ inslen, newlen, p, db->subpool,
+ db->version));
SVN_ERR (db->consumer_func (&window, db->consumer_baton));
/* Make a new subpool and buffer, saving aside the remaining
@@ -651,7 +812,8 @@
/* Read a window header from STREAM and check it for integer overflow. */
static svn_error_t *
-read_window_header (svn_stream_t *stream, svn_filesize_t *sview_offset,
+read_window_header (svn_stream_t *stream, unsigned int version,
+ svn_filesize_t *sview_offset,
apr_size_t *sview_len, apr_size_t *tview_len,
apr_size_t *inslen, apr_size_t *newlen)
{
@@ -667,7 +829,7 @@
break;
}
- /* Read the four size fields. */
+ /* Read the four size fields. */
SVN_ERR (read_one_size (sview_len, stream));
SVN_ERR (read_one_size (tview_len, stream));
SVN_ERR (read_one_size (inslen, stream));
@@ -692,8 +854,9 @@
svn_filesize_t sview_offset;
apr_size_t sview_len, tview_len, inslen, newlen, len;
unsigned char *buf;
-
- SVN_ERR (read_window_header (stream, &sview_offset, &sview_len, &tview_len,
+
+ SVN_ERR (read_window_header (stream, svndiff_version,
+ &sview_offset, &sview_len, &tview_len,
&inslen, &newlen));
len = inslen + newlen;
buf = apr_palloc(pool, len);
@@ -702,8 +865,9 @@
return svn_error_create (SVN_ERR_SVNDIFF_UNEXPECTED_END, NULL,
_("Unexpected end of svndiff input"));
*window = apr_palloc (pool, sizeof(**window));
+
SVN_ERR (decode_window (*window, sview_offset, sview_len, tview_len, inslen,
- newlen, buf, pool));
+ newlen, buf, pool, svndiff_version));
return SVN_NO_ERROR;
}
@@ -718,7 +882,8 @@
apr_size_t sview_len, tview_len, inslen, newlen;
apr_off_t offset;
- SVN_ERR (read_window_header (stream, &sview_offset, &sview_len, &tview_len,
+ SVN_ERR (read_window_header (stream, svndiff_version,
+ &sview_offset, &sview_len, &tview_len,
&inslen, &newlen));
offset = inslen + newlen;
diff --git a/subversion/libsvn_fs_base/fs.c b/subversion/libsvn_fs_base/fs.c
index acbded8..595f875 100644
--- a/subversion/libsvn_fs_base/fs.c
+++ b/subversion/libsvn_fs_base/fs.c
@@ -568,6 +568,8 @@
svn_error_t *svn_err;
const char *path_native;
base_fs_data_t *bfd;
+ const char *formatval;
+ int format = SVN_FS_BASE__FORMAT_NUMBER;
SVN_ERR (check_already_open (fs));
@@ -646,10 +648,15 @@
svn_err = svn_fs_base__dag_init_fs (fs);
if (svn_err) goto error;
+ /* See if we had an explicitly specified no svndiff1. */
+ formatval = apr_hash_get (fs->config, SVN_FS_CONFIG_NO_SVNDIFF1,
+ APR_HASH_KEY_STRING);
+ if (formatval)
+ format = 1;
+
/* This filesystem is ready. Stamp it with a format number. */
svn_err = svn_io_write_version_file
- (svn_path_join (fs->path, FORMAT_FILE, pool),
- SVN_FS_BASE__FORMAT_NUMBER, pool);
+ (svn_path_join (fs->path, FORMAT_FILE, pool), format, pool);
if (svn_err) goto error;
return SVN_NO_ERROR;
@@ -669,6 +676,10 @@
static svn_error_t *
check_format (int format)
{
+ /* We support format 1 and 2 simultaneously. */
+ if (format == 1 && SVN_FS_BASE__FORMAT_NUMBER == 2)
+ return SVN_NO_ERROR;
+
if (format != SVN_FS_BASE__FORMAT_NUMBER)
{
return svn_error_createf
diff --git a/subversion/libsvn_fs_base/fs.h b/subversion/libsvn_fs_base/fs.h
index 076845c..4f68a5b 100644
--- a/subversion/libsvn_fs_base/fs.h
+++ b/subversion/libsvn_fs_base/fs.h
@@ -36,7 +36,10 @@
/* The format number of this filesystem.
This is independent of the repository format number, and
independent of any other FS back ends. */
-#define SVN_FS_BASE__FORMAT_NUMBER 1
+#define SVN_FS_BASE__FORMAT_NUMBER 2
+
+/* Minimum format number that supports svndiff version 1. */
+#define SVN_FS_BASE__MIN_SVNDIFF1_FORMAT 2
#define BDB_ERRCALL_BATON_ERRPFX_STRING "svn (bdb): "
typedef struct
diff --git a/subversion/libsvn_fs_base/reps-strings.c b/subversion/libsvn_fs_base/reps-strings.c
index e2ccbb8..fa709c6 100644
--- a/subversion/libsvn_fs_base/reps-strings.c
+++ b/subversion/libsvn_fs_base/reps-strings.c
@@ -33,6 +33,8 @@
#include "bdb/reps-table.h"
#include "bdb/strings-table.h"
+#include "../libsvn_fs/fs-loader.h"
+
#include "svn_private_config.h"
@@ -1360,6 +1362,7 @@
trail_t *trail,
apr_pool_t *pool)
{
+ base_fs_data_t *bfd = fs->fsap_data;
svn_stream_t *source_stream; /* stream to read the source */
svn_stream_t *target_stream; /* stream to read the target */
svn_txdelta_stream_t *txdelta_stream; /* stream to read delta windows */
@@ -1424,8 +1427,15 @@
/* Setup a stream to convert the textdelta data into svndiff windows. */
svn_txdelta (&txdelta_stream, source_stream, target_stream, pool);
- svn_txdelta_to_svndiff (new_target_stream, pool,
- &new_target_handler, &new_target_handler_baton);
+
+ if (bfd->format >= SVN_FS_BASE__MIN_SVNDIFF1_FORMAT)
+ svn_txdelta_to_svndiff2 (new_target_stream, pool,
+ &new_target_handler,
+ &new_target_handler_baton, 1);
+ else
+ svn_txdelta_to_svndiff2 (new_target_stream, pool,
+ &new_target_handler,
+ &new_target_handler_baton, 0);
/* subpool for the windows */
wpool = svn_pool_create (pool);
diff --git a/subversion/libsvn_fs_base/util/fs_skels.c b/subversion/libsvn_fs_base/util/fs_skels.c
index b9b6ec4..a1c50e3 100644
--- a/subversion/libsvn_fs_base/util/fs_skels.c
+++ b/subversion/libsvn_fs_base/util/fs_skels.c
@@ -144,7 +144,8 @@
diff = window->children;
if ((svn_fs_base__list_length (diff) == 3)
&& (svn_fs_base__matches_atom (diff->children, "svndiff"))
- && (svn_fs_base__matches_atom (diff->children->next, "0"))
+ && ((svn_fs_base__matches_atom (diff->children->next, "0"))
+ || (svn_fs_base__matches_atom (diff->children->next, "1")))
&& (diff->children->next->next->is_atom))
return TRUE;
diff --git a/subversion/libsvn_fs_fs/fs.h b/subversion/libsvn_fs_fs/fs.h
index 56b61ec..4a7bcdd 100644
--- a/subversion/libsvn_fs_fs/fs.h
+++ b/subversion/libsvn_fs_fs/fs.h
@@ -34,7 +34,10 @@
/* The format number of this filesystem.
This is independent of the repository format number, and
independent of any other FS back ends. */
-#define SVN_FS_FS__FORMAT_NUMBER 1
+#define SVN_FS_FS__FORMAT_NUMBER 2
+
+/* The minimum format number that supports svndiff version 1. */
+#define SVN_FS_FS__MIN_SVNDIFF1_FORMAT 2
/* Maximum number of directories to cache dirents for.
This *must* be a power of 2 for DIR_CACHE_ENTRIES_INDEX
diff --git a/subversion/libsvn_fs_fs/fs_fs.c b/subversion/libsvn_fs_fs/fs_fs.c
index 2540f9b..2288a48 100644
--- a/subversion/libsvn_fs_fs/fs_fs.c
+++ b/subversion/libsvn_fs_fs/fs_fs.c
@@ -265,6 +265,11 @@
static svn_error_t *
check_format (int format)
{
+
+ /* We support format 1 and 2 simultaneously */
+ if (format == 1 && SVN_FS_FS__FORMAT_NUMBER == 2)
+ return SVN_NO_ERROR;
+
if (format != SVN_FS_FS__FORMAT_NUMBER)
{
return svn_error_createf
@@ -3025,6 +3030,7 @@
const char *txn_id, *header;
svn_txdelta_window_handler_t wh;
void *whb;
+ fs_fs_data_t *ffd = fs->fsap_data;
b = apr_pcalloc (pool, sizeof (*b));
@@ -3072,7 +3078,11 @@
SVN_ERR (get_file_offset (&b->delta_start, file, b->pool));
/* Prepare to write the svndiff data. */
- svn_txdelta_to_svndiff (b->rep_stream, pool, &wh, &whb);
+ if (ffd->format >= SVN_FS_FS__MIN_SVNDIFF1_FORMAT)
+ svn_txdelta_to_svndiff2 (b->rep_stream, pool, &wh, &whb, 1);
+ else
+ svn_txdelta_to_svndiff2 (b->rep_stream, pool, &wh, &whb, 0);
+
b->delta_stream = svn_txdelta_target_push (wh, whb, source, b->pool);
*wb_p = b;
@@ -3947,7 +3957,8 @@
{
char buffer [APR_UUID_FORMATTED_LENGTH + 1];
apr_uuid_t uuid;
-
+ const char *formatval;
+ int format = SVN_FS_FS__FORMAT_NUMBER;
fs->path = apr_pstrdup (pool, path);
SVN_ERR (svn_io_make_dir_recursively (svn_path_join (path, PATH_REVS_DIR,
@@ -3967,11 +3978,17 @@
svn_fs_fs__set_uuid (fs, buffer, pool);
SVN_ERR (svn_fs_fs__dag_init_fs (fs));
-
+
+ /* See if we had an explicitly requested no svndiff1. */
+ formatval = apr_hash_get (fs->config, SVN_FS_CONFIG_NO_SVNDIFF1,
+ APR_HASH_KEY_STRING);
+ if (formatval)
+ format = 1;
+
/* This filesystem is ready. Stamp it with a format number. */
SVN_ERR (svn_io_write_version_file
- (path_format (fs, pool), SVN_FS_FS__FORMAT_NUMBER, pool));
- ((fs_fs_data_t *) fs->fsap_data)->format = SVN_FS_FS__FORMAT_NUMBER;
+ (path_format (fs, pool), format, pool));
+ ((fs_fs_data_t *) fs->fsap_data)->format = format;
return SVN_NO_ERROR;
}
diff --git a/subversion/libsvn_ra_dav/fetch.c b/subversion/libsvn_ra_dav/fetch.c
index a5c6903..935012a 100644
--- a/subversion/libsvn_ra_dav/fetch.c
+++ b/subversion/libsvn_ra_dav/fetch.c
@@ -2949,6 +2949,10 @@
report_baton_t *rb = report_baton;
svn_error_t *err;
const char *vcc;
+ apr_hash_t *request_headers = apr_hash_make (pool);
+ apr_hash_set(request_headers, "Accept-Encoding", APR_HASH_KEY_STRING,
+ "svndiff1;q=0.9,svndiff;q=0.8");
+
#define SVN_RA_DAV__REPORT_TAIL "</S:update-report>" DEBUG_CR
/* write the final closing gunk to our request body. */
@@ -2981,7 +2985,7 @@
cdata_handler,
end_element,
rb,
- NULL, NULL,
+ request_headers, NULL,
rb->spool_response, pool);
/* we're done with the file */
diff --git a/subversion/libsvn_ra_dav/file_revs.c b/subversion/libsvn_ra_dav/file_revs.c
index 3b7d984..601482e 100644
--- a/subversion/libsvn_ra_dav/file_revs.c
+++ b/subversion/libsvn_ra_dav/file_revs.c
@@ -306,6 +306,9 @@
int http_status = 0;
struct report_baton rb;
svn_error_t *err;
+ apr_hash_t *request_headers = apr_hash_make (pool);
+ apr_hash_set(request_headers, "Accept-Encoding", APR_HASH_KEY_STRING,
+ "svndiff1;q=0.9,svndiff;q=0.8");
static const char request_head[]
= "<S:file-revs-report xmlns:S=\"" SVN_XML_NAMESPACE "\">" DEBUG_CR;
@@ -350,7 +353,7 @@
err = svn_ra_dav__parsed_request (ras->sess, "REPORT", final_bc_url,
request_body->data, NULL, NULL,
start_element, cdata_handler, end_element,
- &rb, NULL, &http_status, FALSE,
+ &rb, request_headers, &http_status, FALSE,
pool);
/* Map status 501: Method Not Implemented to our not implemented error.
diff --git a/subversion/libsvn_ra_svn/client.c b/subversion/libsvn_ra_svn/client.c
index 3142565..01802ec 100644
--- a/subversion/libsvn_ra_svn/client.c
+++ b/subversion/libsvn_ra_svn/client.c
@@ -273,9 +273,9 @@
svn_boolean_t compat)
{
if (compat)
- return svn_ra_svn_write_tuple(conn, pool, "nw(?c)(w)", (apr_uint64_t) 1,
+ return svn_ra_svn_write_tuple(conn, pool, "nw(?c)(ww)", (apr_uint64_t) 1,
mech, mech_arg,
- SVN_RA_SVN_CAP_EDIT_PIPELINE);
+ SVN_RA_SVN_CAP_EDIT_PIPELINE, SVN_RA_SVN_CAP_SVNDIFF1);
else
return svn_ra_svn_write_tuple(conn, pool, "w(?c)", mech, mech_arg);
}
@@ -674,8 +674,8 @@
}
else
{
- SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "n(w)c", (apr_uint64_t) 2,
- SVN_RA_SVN_CAP_EDIT_PIPELINE, url));
+ SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "n(ww)c", (apr_uint64_t) 2,
+ SVN_RA_SVN_CAP_EDIT_PIPELINE, SVN_RA_SVN_CAP_SVNDIFF1, url));
SVN_ERR(handle_auth_request(sess, pool));
}
diff --git a/subversion/libsvn_ra_svn/editor.c b/subversion/libsvn_ra_svn/editor.c
index 897db9b..22afa83 100644
--- a/subversion/libsvn_ra_svn/editor.c
+++ b/subversion/libsvn_ra_svn/editor.c
@@ -255,7 +255,10 @@
diff_stream = svn_stream_create(b, pool);
svn_stream_set_write(diff_stream, ra_svn_svndiff_handler);
svn_stream_set_close(diff_stream, ra_svn_svndiff_close_handler);
- svn_txdelta_to_svndiff(diff_stream, pool, wh, wh_baton);
+ if (svn_ra_svn_has_capability(b->conn, SVN_RA_SVN_CAP_SVNDIFF1))
+ svn_txdelta_to_svndiff2(diff_stream, pool, wh, wh_baton, 1);
+ else
+ svn_txdelta_to_svndiff2(diff_stream, pool, wh, wh_baton, 0);
return SVN_NO_ERROR;
}
diff --git a/subversion/libsvn_ra_svn/editorp.c b/subversion/libsvn_ra_svn/editorp.c
index 29c68cf..f556424 100644
--- a/subversion/libsvn_ra_svn/editorp.c
+++ b/subversion/libsvn_ra_svn/editorp.c
@@ -294,7 +294,10 @@
diff_stream = svn_stream_create(b, pool);
svn_stream_set_write(diff_stream, ra_svn_svndiff_handler);
svn_stream_set_close(diff_stream, ra_svn_svndiff_close_handler);
- svn_txdelta_to_svndiff(diff_stream, pool, wh, wh_baton);
+ if (svn_ra_svn_has_capability(b->conn, SVN_RA_SVN_CAP_SVNDIFF1))
+ svn_txdelta_to_svndiff2(diff_stream, pool, wh, wh_baton, 1);
+ else
+ svn_txdelta_to_svndiff2(diff_stream, pool, wh, wh_baton, 0);
return SVN_NO_ERROR;
}
diff --git a/subversion/libsvn_ra_svn/protocol b/subversion/libsvn_ra_svn/protocol
index 84ab3a9..1d3b3ff 100644
--- a/subversion/libsvn_ra_svn/protocol
+++ b/subversion/libsvn_ra_svn/protocol
@@ -164,6 +164,9 @@
edit-pipeline If both the client and server present this
capability, edit operations will use pipelining.
See section 3.1.2.
+ svndiff1 If both the client and server support svndiff version
+ 1, this will be used as the on-the-wire format for svndiff
+ instead of svndiff version 0.
3. Commands
-----------
diff --git a/subversion/libsvn_repos/dump.c b/subversion/libsvn_repos/dump.c
index 69a020a..38e609b 100644
--- a/subversion/libsvn_repos/dump.c
+++ b/subversion/libsvn_repos/dump.c
@@ -148,7 +148,7 @@
/* Compute the delta and send it to the temporary file. */
SVN_ERR (svn_fs_get_file_delta_stream (&delta_stream, oldroot, oldpath,
newroot, newpath, pool));
- svn_txdelta_to_svndiff (temp_stream, pool, &wh, &whb);
+ svn_txdelta_to_svndiff2 (temp_stream, pool, &wh, &whb, 1);
SVN_ERR (svn_txdelta_send_txstream (delta_stream, wh, whb, pool));
/* Get the length of the temporary file and rewind it. */
diff --git a/subversion/mod_dav_svn/dav_svn.h b/subversion/mod_dav_svn/dav_svn.h
index a630a7f..9c7a74e 100644
--- a/subversion/mod_dav_svn/dav_svn.h
+++ b/subversion/mod_dav_svn/dav_svn.h
@@ -211,6 +211,9 @@
/* ### record the base for computing a delta during a GET */
const char *delta_base;
+ /* SVNDIFF version we can transmit to the client. */
+ int svndiff_version;
+
/* the value of any SVN_DAV_OPTIONS_HEADER that came in the request */
const char *svn_client_options;
diff --git a/subversion/mod_dav_svn/file_revs.c b/subversion/mod_dav_svn/file_revs.c
index b31b7aa..294a9d9 100644
--- a/subversion/mod_dav_svn/file_revs.c
+++ b/subversion/mod_dav_svn/file_revs.c
@@ -41,6 +41,9 @@
writes to support mod_dav-based error handling. */
svn_boolean_t needs_header;
+ /* SVNDIFF version to use when sending to client. */
+ int svndiff_version;
+
/* Used by the delta iwndow handler. */
svn_txdelta_window_handler_t window_handler;
void *window_baton;
@@ -176,8 +179,8 @@
base64_stream = dav_svn_make_base64_output_stream(frb->bb, frb->output,
pool);
- svn_txdelta_to_svndiff(base64_stream, pool, &frb->window_handler,
- &frb->window_baton);
+ svn_txdelta_to_svndiff2(base64_stream, pool, &frb->window_handler,
+ &frb->window_baton, frb->svndiff_version);
*window_handler = delta_window_handler;
*window_baton = frb;
/* Start the txdelta element wich will be terminated by the window
@@ -255,6 +258,7 @@
output->c->bucket_alloc);
frb.output = output;
frb.needs_header = TRUE;
+ frb.svndiff_version = resource->info->svndiff_version;
/* file_rev_handler will send header first time it is called. */
diff --git a/subversion/mod_dav_svn/repos.c b/subversion/mod_dav_svn/repos.c
index 275a479..3411a06 100644
--- a/subversion/mod_dav_svn/repos.c
+++ b/subversion/mod_dav_svn/repos.c
@@ -29,6 +29,7 @@
#include <apr_want.h>
#include <apr_strings.h>
#include <apr_hash.h>
+#include <apr_lib.h>
#include "svn_types.h"
#include "svn_pools.h"
@@ -1241,6 +1242,182 @@
return NULL;
}
+/* --------------- Borrowed from httpd's mod_negotiation.c -------------- */
+
+typedef struct accept_rec {
+ char *name; /* MUST be lowercase */
+ float quality;
+} accept_rec;
+
+/*
+ * Get a single mime type entry --- one media type and parameters;
+ * enter the values we recognize into the argument accept_rec
+ */
+
+static const char *get_entry(apr_pool_t *p, accept_rec *result,
+ const char *accept_line)
+{
+ result->quality = 1.0f;
+
+ /*
+ * Note that this handles what I gather is the "old format",
+ *
+ * Accept: text/html text/plain moo/zot
+ *
+ * without any compatibility kludges --- if the token after the
+ * MIME type begins with a semicolon, we know we're looking at parms,
+ * otherwise, we know we aren't. (So why all the pissing and moaning
+ * in the CERN server code? I must be missing something).
+ */
+
+ result->name = ap_get_token(p, &accept_line, 0);
+ ap_str_tolower(result->name); /* You want case insensitive,
+ * you'll *get* case insensitive.
+ */
+
+ while (*accept_line == ';')
+ {
+ /* Parameters ... */
+
+ char *parm;
+ char *cp;
+ char *end;
+
+ ++accept_line;
+ parm = ap_get_token(p, &accept_line, 1);
+
+ /* Look for 'var = value' --- and make sure the var is in lcase. */
+
+ for (cp = parm; (*cp && !apr_isspace(*cp) && *cp != '='); ++cp)
+ {
+ *cp = apr_tolower(*cp);
+ }
+
+ if (!*cp)
+ {
+ continue; /* No '='; just ignore it. */
+ }
+
+ *cp++ = '\0'; /* Delimit var */
+ while (*cp && (apr_isspace(*cp) || *cp == '='))
+ {
+ ++cp;
+ }
+
+ if (*cp == '"')
+ {
+ ++cp;
+ for (end = cp;
+ (*end && *end != '\n' && *end != '\r' && *end != '\"');
+ end++);
+ }
+ else
+ {
+ for (end = cp; (*end && !apr_isspace(*end)); end++);
+ }
+ if (*end)
+ {
+ *end = '\0'; /* strip ending quote or return */
+ }
+ ap_str_tolower(cp);
+
+ if (parm[0] == 'q'
+ && (parm[1] == '\0' || (parm[1] == 's' && parm[2] == '\0')))
+ {
+ result->quality = atof(cp);
+ }
+ }
+
+ if (*accept_line == ',')
+ {
+ ++accept_line;
+ }
+
+ return accept_line;
+}
+
+/* @a accept_line is the Accept-Encoding header, which is of the
+ format:
+
+ Accept-Language: name; q=N;
+*/
+static apr_array_header_t *do_header_line(apr_pool_t *p,
+ const char *accept_line)
+{
+ apr_array_header_t *accept_recs;
+
+ if (!accept_line)
+ return NULL;
+
+ accept_recs = apr_array_make(p, 10, sizeof(accept_rec));
+
+ while (*accept_line)
+ {
+ accept_rec *prefs = (accept_rec *) apr_array_push(accept_recs);
+ accept_line = get_entry(p, prefs, accept_line);
+ }
+
+ return accept_recs;
+}
+
+/* ---------------------------------------------------------------------- */
+
+
+/* qsort implementation for the quality field of the accept_rec
+ structure */
+static int sort_encoding_pref(const void *accept_rec1, const void *accept_rec2)
+{
+ float diff = ((const accept_rec *) accept_rec1)->quality -
+ ((const accept_rec *) accept_rec2)->quality;
+ return (diff == 0 ? 0 : (diff > 0 ? -1 : 1));
+}
+
+/* It would be nice if this function could be unit-tested. Paul
+ Querna suggested
+ http://svn.apache.org/repos/asf/httpd/httpd/trunk/server/request.c's
+ make_sub_request(), and noted that httpd manually constructs
+ request_rec's in a few spots. */
+
+static void
+svn_dav__negotiate_encoding_prefs(request_rec *r,
+ int *svndiff_version)
+{
+ /* It would be nice if mod_negotiation
+ <http://httpd.apache.org/docs-2.1/mod/mod_negotiation.html> could
+ handle the Accept-Encoding header parsing for us. Sadly, it
+ looks like its data structures and routines are private (see
+ httpd/modules/mappers/mod_negotiation.c). Thus, we duplicate the
+ necessary ones in this file. */
+ size_t i;
+ const apr_array_header_t *encoding_prefs;
+ encoding_prefs = do_header_line(r->pool,
+ apr_table_get(r->headers_in,
+ "Accept-Encoding"));
+
+ if (encoding_prefs && apr_is_empty_array(encoding_prefs))
+ {
+ *svndiff_version = 0;
+ return;
+ }
+ *svndiff_version = 0;
+ qsort(encoding_prefs->elts, (size_t) encoding_prefs->nelts,
+ sizeof(accept_rec), sort_encoding_pref);
+ for (i = 0; i < encoding_prefs->nelts; i++)
+ {
+ struct accept_rec rec = APR_ARRAY_IDX (encoding_prefs, i,
+ struct accept_rec);
+ if (strcmp (rec.name, "svndiff1") == 0)
+ {
+ *svndiff_version = 1;
+ break;
+ }
+ else if (strcmp (rec.name, "svndiff") == 0)
+ {
+ *svndiff_version = 0;
+ break;
+ }
+ }
+}
static dav_error * dav_svn_get_resource(request_rec *r,
@@ -1336,6 +1513,7 @@
&& strcmp(ct, SVN_SVNDIFF_MIME_TYPE) == 0;
}
+ svn_dav__negotiate_encoding_prefs (r, &comb->priv.svndiff_version);
/* ### and another hack for computing diffs to send to the client */
comb->priv.delta_base = apr_table_get(r->headers_in,
SVN_DAV_DELTA_BASE_HEADER);
@@ -2627,7 +2805,8 @@
svn_stream_set_close(o_stream, dav_svn_close_filter);
/* get a handler/baton for writing into the output stream */
- svn_txdelta_to_svndiff(o_stream, resource->pool, &handler, &h_baton);
+ svn_txdelta_to_svndiff2(o_stream, resource->pool, &handler, &h_baton,
+ resource->info->svndiff_version);
/* got everything set up. read in delta windows and shove them into
the handler, which pushes data into the output stream, which goes
diff --git a/subversion/mod_dav_svn/update.c b/subversion/mod_dav_svn/update.c
index c5a211b..6e48929 100644
--- a/subversion/mod_dav_svn/update.c
+++ b/subversion/mod_dav_svn/update.c
@@ -74,6 +74,9 @@
/* True iff client requested all data inline in the report. */
svn_boolean_t send_all;
+ /* SVNDIFF version to send to client. */
+ int svndiff_version;
+
} update_ctx_t;
typedef struct item_baton_t {
@@ -951,8 +954,9 @@
base64_stream = dav_svn_make_base64_output_stream(wb->uc->bb, wb->uc->output,
file->pool);
- svn_txdelta_to_svndiff(base64_stream, file->pool,
- &(wb->handler), &(wb->handler_baton));
+ svn_txdelta_to_svndiff2(base64_stream, file->pool,
+ &(wb->handler), &(wb->handler_baton),
+ file->uc->svndiff_version);
*handler = window_handler;
*handler_baton = wb;
@@ -1191,6 +1195,7 @@
resource->pool);
}
+ uc.svndiff_version = resource->info->svndiff_version;
uc.resource = resource;
uc.output = output;
uc.anchor = src_path;
diff --git a/subversion/svnadmin/main.c b/subversion/svnadmin/main.c
index b33e8a4..5228a22 100644
--- a/subversion/svnadmin/main.c
+++ b/subversion/svnadmin/main.c
@@ -213,7 +213,8 @@
svnadmin__use_pre_commit_hook,
svnadmin__use_post_commit_hook,
svnadmin__clean_logs,
- svnadmin__wait
+ svnadmin__wait,
+ svnadmin__no_svndiff1,
};
/* Option codes and descriptions.
@@ -283,6 +284,9 @@
N_("wait instead of exit if the repository is in\n"
" use by another process")},
+ {"no-svndiff1", svnadmin__no_svndiff1, 0,
+ N_("disallow use of SVNDIFF1 in on-disk storage, for backwards compatibility")},
+
{NULL}
};
@@ -302,7 +306,7 @@
("usage: svnadmin create REPOS_PATH\n\n"
"Create a new, empty repository at REPOS_PATH.\n"),
{svnadmin__bdb_txn_nosync, svnadmin__bdb_log_keep,
- svnadmin__config_dir, svnadmin__fs_type} },
+ svnadmin__config_dir, svnadmin__fs_type, svnadmin__no_svndiff1} },
{"deltify", subcommand_deltify, {0}, N_
("usage: svnadmin deltify [-r LOWER[:UPPER]] REPOS_PATH\n\n"
@@ -410,6 +414,7 @@
const char *repository_path;
const char *new_repository_path; /* hotcopy dest. path */
const char *fs_type; /* --fs-type */
+ svn_boolean_t no_svndiff1; /* --no-svndiff1 */
svn_opt_revision_t start_revision, end_revision; /* -r X[:Y] */
svn_boolean_t help; /* --help or -? */
svn_boolean_t version; /* --version */
@@ -483,6 +488,11 @@
APR_HASH_KEY_STRING,
opt_state->fs_type);
+ if (opt_state->no_svndiff1)
+ apr_hash_set (fs_config, SVN_FS_CONFIG_NO_SVNDIFF1,
+ APR_HASH_KEY_STRING,
+ "1");
+
SVN_ERR (svn_config_get_config (&config, opt_state->config_dir, pool));
SVN_ERR (svn_repos_create (&repos, opt_state->repository_path,
NULL, NULL,
@@ -1253,6 +1263,9 @@
case svnadmin__force_uuid:
opt_state.uuid_action = svn_repos_load_uuid_force;
break;
+ case svnadmin__no_svndiff1:
+ opt_state.no_svndiff1 = TRUE;
+ break;
case svnadmin__fs_type:
err = svn_utf_cstring_to_utf8 (&opt_state.fs_type, opt_arg, pool);
if (err)
diff --git a/subversion/svnserve/serve.c b/subversion/svnserve/serve.c
index ebdc4ac..8f65e4d 100644
--- a/subversion/svnserve/serve.c
+++ b/subversion/svnserve/serve.c
@@ -2166,8 +2166,8 @@
SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "w(nn(!", "success",
(apr_uint64_t) 1, (apr_uint64_t) 2));
SVN_ERR(send_mechs(conn, pool, &b, READ_ACCESS, FALSE));
- SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "!)(w))",
- SVN_RA_SVN_CAP_EDIT_PIPELINE));
+ SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "!)(ww))",
+ SVN_RA_SVN_CAP_EDIT_PIPELINE, SVN_RA_SVN_CAP_SVNDIFF1));
/* Read client response. Because the client response form changed
* between version 1 and version 2, we have to do some of this by
diff --git a/subversion/tests/libsvn_delta/random-test.c b/subversion/tests/libsvn_delta/random-test.c
index 291e997..3cec8d7 100644
--- a/subversion/tests/libsvn_delta/random-test.c
+++ b/subversion/tests/libsvn_delta/random-test.c
@@ -337,7 +337,7 @@
delta_pool);
/* Make stage 2: encode the text delta in svndiff format. */
- svn_txdelta_to_svndiff (stream, delta_pool, &handler, &handler_baton);
+ svn_txdelta_to_svndiff2 (stream, delta_pool, &handler, &handler_baton, 1);
/* Make stage 1: create the text delta. */
svn_txdelta (&txdelta_stream,
@@ -429,7 +429,7 @@
delta_pool);
/* Make stage 2: encode the text delta in svndiff format. */
- svn_txdelta_to_svndiff (stream, delta_pool, &handler, &handler_baton);
+ svn_txdelta_to_svndiff2 (stream, delta_pool, &handler, &handler_baton, 1);
/* Make stage 1: create the text deltas. */
diff --git a/subversion/tests/libsvn_delta/svndiff-test.c b/subversion/tests/libsvn_delta/svndiff-test.c
index b32c455..06dbff6 100644
--- a/subversion/tests/libsvn_delta/svndiff-test.c
+++ b/subversion/tests/libsvn_delta/svndiff-test.c
@@ -39,10 +39,11 @@
svn_stream_t *encoder;
void *svndiff_baton;
apr_pool_t *pool;
+ int version = 0;
if (argc < 3)
{
- printf ("usage: %s source target\n", argv[0]);
+ printf ("usage: %s source target [version]\n", argv[0]);
exit (0);
}
@@ -63,6 +64,8 @@
fprintf (stderr, "unable to open \"%s\" for reading\n", argv[2]);
exit (1);
}
+ if (argc == 4)
+ version = atoi (argv[3]);
svn_txdelta (&txdelta_stream,
svn_stream_from_aprfile (source_file, pool),
@@ -78,7 +81,8 @@
#else
encoder = svn_base64_encode (stdout_stream, pool);
#endif
- svn_txdelta_to_svndiff (encoder, pool, &svndiff_handler, &svndiff_baton);
+ svn_txdelta_to_svndiff2 (encoder, pool, &svndiff_handler, &svndiff_baton,
+ version);
err = svn_txdelta_send_txstream (txdelta_stream,
svndiff_handler,
svndiff_baton,