Index: notes/wc-ng/pristine-store =================================================================== --- notes/wc-ng/pristine-store (revision 1844566) +++ notes/wc-ng/pristine-store (working copy) @@ -1,6 +1,7 @@ This spec defines how the Pristine Store works (part A) and how the WC uses it (part B). +TODO: Update A. THE PRISTINE STORE ===================== Index: subversion/include/private/svn_io_private.h =================================================================== --- subversion/include/private/svn_io_private.h (revision 1844566) +++ subversion/include/private/svn_io_private.h (working copy) @@ -119,14 +119,6 @@ svn_error_t * svn_stream__install_delete(svn_stream_t *install_stream, apr_pool_t *scratch_pool); -/* Optimized apr_file_stat / apr_file_info_get operating on a closed - install stream */ -svn_error_t * -svn_stream__install_get_info(apr_finfo_t *finfo, - svn_stream_t *install_stream, - apr_int32_t wanted, - apr_pool_t *scratch_pool); - /* Internal version of svn_stream_from_aprfile2() supporting the additional TRUNCATE_ON_SEEK argument. */ svn_stream_t * Index: subversion/include/private/svn_subr_private.h =================================================================== --- subversion/include/private/svn_subr_private.h (revision 1844566) +++ subversion/include/private/svn_subr_private.h (working copy) @@ -591,6 +591,12 @@ svn__decompress_lz4(const void *data, apr_size_t l svn_stringbuf_t *out, apr_size_t limit); +svn_stream_t * +svn_stream__lz4_compress(svn_stream_t *stream, apr_pool_t *pool); + +svn_stream_t * +svn_stream__lz4_decompress(svn_stream_t *stream, apr_pool_t *pool); + /** @} */ /** Index: subversion/libsvn_subr/compress_lz4.c =================================================================== --- subversion/libsvn_subr/compress_lz4.c (revision 1844566) +++ subversion/libsvn_subr/compress_lz4.c (working copy) @@ -23,9 +23,14 @@ #include +#define APR_WANT_BYTEFUNC +#include + +#include "private/svn_string_private.h" #include "private/svn_subr_private.h" #include "svn_private_config.h" +#include "svn_pools.h" #ifdef SVN_INTERNAL_LZ4 #include "lz4/lz4internal.h" @@ -142,3 +147,322 @@ svn_lz4__runtime_version(void) { return LZ4_versionNumber(); } + +typedef svn_error_t * +(*stream_generator_next_substream_fn_t)(svn_stream_t **stream_p, + void *baton, + apr_pool_t *pool); + +typedef struct stream_generator_baton_t +{ + stream_generator_next_substream_fn_t next_fn; + void *next_fn_baton; + svn_stream_t *substream; + apr_pool_t *substream_pool; + svn_boolean_t hit_eof; +} stream_generator_baton_t; + +static svn_error_t * +read_helper(svn_stream_t *stream, + char *buffer, + apr_size_t *len, + svn_boolean_t *hit_eof) +{ + apr_size_t to_read = *len; + SVN_ERR(svn_stream_read_full(stream, buffer, len)); + *hit_eof = *len < to_read; + + return SVN_NO_ERROR; +} + +static svn_error_t * +stream_generator_read_full_fn(void *baton, char *buffer, apr_size_t *len) +{ + stream_generator_baton_t *b = baton; + apr_size_t remaining = *len; + + *len = 0; + while (remaining && !b->hit_eof) + { + apr_size_t read_len; + svn_boolean_t hit_eof; + + if (!b->substream) + { + svn_stream_t *substream; + + SVN_ERR(b->next_fn(&substream, b->next_fn_baton, b->substream_pool)); + + if (substream) + b->substream = substream; + else + { + b->hit_eof = TRUE; + break; + } + } + + read_len = remaining; + SVN_ERR(read_helper(b->substream, buffer, &read_len, &hit_eof)); + buffer += read_len; + remaining -= read_len; + *len += read_len; + + if (hit_eof) + { + SVN_ERR(svn_stream_close(b->substream)); + b->substream = NULL; + svn_pool_clear(b->substream_pool); + } + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +stream_generator_close_fn(void *baton) +{ + stream_generator_baton_t *b = baton; + + if (b->substream) + { + SVN_ERR(svn_stream_close(b->substream)); + b->substream = NULL; + } + + svn_pool_destroy(b->substream_pool); + return SVN_NO_ERROR; +} + +static svn_stream_t * +stream_generator_create(stream_generator_next_substream_fn_t next_fn, + void *next_fn_baton, + apr_pool_t *pool) +{ + stream_generator_baton_t *baton; + svn_stream_t *stream; + + baton = apr_pcalloc(pool, sizeof(*baton)); + baton->next_fn = next_fn; + baton->next_fn_baton = next_fn_baton; + baton->substream = NULL; + baton->substream_pool = svn_pool_create(pool); + baton->hit_eof = FALSE; + + stream = svn_stream_create(baton, pool); + svn_stream_set_read2(stream, NULL, stream_generator_read_full_fn); + svn_stream_set_close(stream, stream_generator_close_fn); + + return stream; +} + +typedef struct lz4_compress_stream_baton_t +{ + svn_stream_t *stream; + svn_stringbuf_t *buf; + svn_stringbuf_t *tmpbuf; +} lz4_compress_stream_baton_t; + +static const apr_size_t LZ4_STREAM_MAX_BLOCKLEN = 128 * 1024; + +static svn_error_t * +write_helper(svn_stream_t *stream, const void *data, apr_size_t len) +{ + return svn_stream_write(stream, data, &len); +} + +static svn_error_t * +write_uint32(svn_stream_t *stream, apr_uint32_t val) +{ + apr_uint32_t nval = htonl(val); + + SVN_ERR(write_helper(stream, &nval, sizeof(nval))); + + return SVN_NO_ERROR; +} + +static svn_error_t * +read_uint32(apr_uint32_t *val_p, svn_stream_t *stream) +{ + apr_uint32_t nval; + apr_size_t len = sizeof(nval); + + SVN_ERR(svn_stream_read_full(stream, (char*)&nval, &len)); + if (len < sizeof(nval)) + return svn_error_create(SVN_ERR_STREAM_MALFORMED_DATA, NULL, NULL); + + *val_p = ntohl(nval); + return SVN_NO_ERROR; +} + +static svn_error_t * +flush_output(lz4_compress_stream_baton_t *b) +{ + int uncompressed_len = (int)b->buf->len; + int compressed_len; + + if (!uncompressed_len) + return SVN_NO_ERROR; + + svn_stringbuf_ensure(b->tmpbuf, LZ4_compressBound(uncompressed_len)); + compressed_len = LZ4_compress_default(b->buf->data, + b->tmpbuf->data, + uncompressed_len, + (int)b->tmpbuf->blocksize); + if (!compressed_len) + return svn_error_create(SVN_ERR_LZ4_COMPRESSION_FAILED, NULL, NULL); + + if (compressed_len >= uncompressed_len) + { + SVN_ERR(write_uint32(b->stream, uncompressed_len)); + SVN_ERR(write_uint32(b->stream, uncompressed_len)); + SVN_ERR(write_helper(b->stream, b->buf->data, uncompressed_len)); + } + else + { + SVN_ERR(write_uint32(b->stream, uncompressed_len)); + SVN_ERR(write_uint32(b->stream, compressed_len)); + SVN_ERR(write_helper(b->stream, b->tmpbuf->data, compressed_len)); + } + + b->buf->len = 0; + return SVN_NO_ERROR; +} + +static svn_error_t * +lz4_compress_stream_write_fn(void *baton, const char *data, apr_size_t *len) +{ + lz4_compress_stream_baton_t *b = baton; + apr_size_t remaining = *len; + + while (remaining) + { + if (b->buf->len + remaining < LZ4_STREAM_MAX_BLOCKLEN) + { + svn_stringbuf_appendbytes(b->buf, data, remaining); + data += remaining; + remaining = 0; + } + else + { + apr_size_t to_copy = LZ4_STREAM_MAX_BLOCKLEN - b->buf->len; + + svn_stringbuf_appendbytes(b->buf, data, to_copy); + data += to_copy; + remaining -= to_copy; + SVN_ERR(flush_output(b)); + } + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +lz4_compress_stream_close_fn(void *baton) +{ + lz4_compress_stream_baton_t *b = baton; + + SVN_ERR(flush_output(b)); + SVN_ERR(write_uint32(b->stream, 0)); + SVN_ERR(svn_stream_close(b->stream)); + + return SVN_NO_ERROR; +} + +svn_stream_t * +svn_stream__lz4_compress(svn_stream_t *stream, apr_pool_t *pool) +{ + svn_stream_t *result; + lz4_compress_stream_baton_t *baton; + + baton = apr_pcalloc(pool, sizeof(*baton)); + baton->stream = stream; + baton->buf = svn_stringbuf_create_empty(pool); + baton->tmpbuf = svn_stringbuf_create_empty(pool); + + result = svn_stream_create(baton, pool); + svn_stream_set_write(result, lz4_compress_stream_write_fn); + svn_stream_set_close(result, lz4_compress_stream_close_fn); + + return result; +} + +static svn_error_t * +ensure_eos(svn_stream_t *stream) +{ + char dummy; + apr_size_t len = sizeof(dummy); + + SVN_ERR(svn_stream_read_full(stream, &dummy, &len)); + if (len != 0) + return svn_error_create(SVN_ERR_STREAM_MALFORMED_DATA, NULL, NULL); + + return SVN_NO_ERROR; +} + +static svn_error_t * +lz4_decompress_next_substream_fn(svn_stream_t **stream_p, + void *baton, + apr_pool_t *pool) +{ + svn_stream_t *stream = baton; + apr_uint32_t uncompressed_len; + apr_uint32_t compressed_len; + svn_stringbuf_t *buf; + apr_size_t len; + + SVN_ERR(read_uint32(&uncompressed_len, stream)); + + if (uncompressed_len > LZ4_STREAM_MAX_BLOCKLEN) + return svn_error_create(SVN_ERR_STREAM_MALFORMED_DATA, NULL, NULL); + + if (uncompressed_len == 0) + { + SVN_ERR(ensure_eos(stream)); + SVN_ERR(svn_stream_close(stream)); + + *stream_p = NULL; + return SVN_NO_ERROR; + } + + SVN_ERR(read_uint32(&compressed_len, stream)); + buf = svn_stringbuf_create_ensure(compressed_len, pool); + len = compressed_len; + SVN_ERR(svn_stream_read_full(stream, buf->data, &len)); + buf->len = len; + buf->data[buf->len] = 0; + if (len < compressed_len) + return svn_error_create(SVN_ERR_STREAM_MALFORMED_DATA, NULL, NULL); + + if (uncompressed_len == compressed_len) + { + *stream_p = svn_stream_from_stringbuf(buf, pool); + } + else + { + svn_stringbuf_t *outbuf; + int rv; + + outbuf = svn_stringbuf_create_ensure(uncompressed_len, pool); + rv = LZ4_decompress_safe(buf->data, outbuf->data, + compressed_len, uncompressed_len); + if (rv < 0) + return svn_error_create(SVN_ERR_LZ4_DECOMPRESSION_FAILED, NULL, NULL); + + if (rv != (int) uncompressed_len) + return svn_error_create(SVN_ERR_LZ4_DECOMPRESSION_FAILED, NULL, + _("Size of uncompressed data " + "does not match stored original length")); + outbuf->len = uncompressed_len; + outbuf->data[outbuf->len] = 0; + *stream_p = svn_stream_from_stringbuf(outbuf, pool); + } + + return SVN_NO_ERROR; +} + +svn_stream_t * +svn_stream__lz4_decompress(svn_stream_t *stream, apr_pool_t *pool) +{ + return stream_generator_create(lz4_decompress_next_substream_fn, stream, pool); +} Index: subversion/libsvn_subr/stream.c =================================================================== --- subversion/libsvn_subr/stream.c (revision 1844566) +++ subversion/libsvn_subr/stream.c (working copy) @@ -2399,23 +2399,6 @@ svn_stream__install_stream(svn_stream_t *install_s } svn_error_t * -svn_stream__install_get_info(apr_finfo_t *finfo, - svn_stream_t *install_stream, - apr_int32_t wanted, - apr_pool_t *scratch_pool) -{ - struct install_baton_t *ib = install_stream->baton; - apr_status_t status; - - status = apr_file_info_get(finfo, wanted, ib->baton_apr.file); - - if (status) - return svn_error_wrap_apr(status, NULL); - - return SVN_NO_ERROR; -} - -svn_error_t * svn_stream__install_delete(svn_stream_t *install_stream, apr_pool_t *scratch_pool) { Index: subversion/libsvn_wc/adm_files.c =================================================================== --- subversion/libsvn_wc/adm_files.c (revision 1844566) +++ subversion/libsvn_wc/adm_files.c (working copy) @@ -210,8 +210,9 @@ svn_wc__text_base_path_to_read(const char **result _("Node '%s' has no pristine text"), svn_dirent_local_style(local_abspath, scratch_pool)); - SVN_ERR(svn_wc__db_pristine_get_path(result_abspath, db, local_abspath, - checksum, + SVN_ERR(svn_wc__db_pristine_get_path(result_abspath, db, + local_abspath, checksum, + svn_io_file_del_on_pool_cleanup, result_pool, scratch_pool)); return SVN_NO_ERROR; } Index: subversion/libsvn_wc/diff_editor.c =================================================================== --- subversion/libsvn_wc/diff_editor.c (revision 1844566) +++ subversion/libsvn_wc/diff_editor.c (working copy) @@ -478,6 +478,7 @@ svn_wc__diff_base_working_diff(svn_wc__db_t *db, SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file, db, local_abspath, checksum, + svn_io_file_del_on_pool_cleanup, scratch_pool, scratch_pool)); if (diff_pristine) @@ -484,6 +485,7 @@ svn_wc__diff_base_working_diff(svn_wc__db_t *db, SVN_ERR(svn_wc__db_pristine_get_path(&local_file, db, local_abspath, working_checksum, + svn_io_file_del_on_pool_cleanup, scratch_pool, scratch_pool)); else if (! (had_props || props_mod)) local_file = local_abspath; @@ -1019,8 +1021,10 @@ svn_wc__diff_local_only_file(svn_wc__db_t *db, right_props = svn_prop_hash_dup(pristine_props, scratch_pool); if (checksum) - SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file, db, local_abspath, - checksum, scratch_pool, scratch_pool)); + SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file, db, + local_abspath, checksum, + svn_io_file_del_on_pool_cleanup, + scratch_pool, scratch_pool)); else pristine_file = NULL; @@ -1417,6 +1421,7 @@ svn_wc__diff_base_only_file(svn_wc__db_t *db, SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file, db, local_abspath, checksum, + svn_io_file_del_on_pool_cleanup, scratch_pool, scratch_pool)); SVN_ERR(processor->file_deleted(relpath, @@ -2218,6 +2223,7 @@ close_file(void *file_baton, SVN_ERR(svn_wc__db_pristine_get_path(&repos_file, eb->db, eb->anchor_abspath, fb->base_checksum, + svn_io_file_del_on_pool_cleanup, scratch_pool, scratch_pool)); } } @@ -2254,6 +2260,7 @@ close_file(void *file_baton, SVN_ERR(svn_wc__db_pristine_get_path(&localfile, eb->db, eb->anchor_abspath, checksum, + svn_io_file_del_on_pool_cleanup, scratch_pool, scratch_pool)); } else Index: subversion/libsvn_wc/update_editor.c =================================================================== --- subversion/libsvn_wc/update_editor.c (revision 1844566) +++ subversion/libsvn_wc/update_editor.c (working copy) @@ -451,7 +451,8 @@ struct handler_baton ### file name to create later. A better way may not be readily available. */ static svn_error_t * -get_empty_tmp_file(const char **tmp_filename, +get_empty_tmp_file(const char **tmp_filename_p, + svn_skel_t **cleanup_work_item_p, svn_wc__db_t *db, const char *wri_abspath, apr_pool_t *result_pool, @@ -458,13 +459,26 @@ static svn_error_t * apr_pool_t *scratch_pool) { const char *temp_dir_abspath; + const char *tmp_filename; + svn_skel_t *work_item; + svn_error_t *err; SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, db, wri_abspath, scratch_pool, scratch_pool)); - SVN_ERR(svn_io_open_unique_file3(NULL, tmp_filename, temp_dir_abspath, + SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_filename, temp_dir_abspath, svn_io_file_del_none, scratch_pool, scratch_pool)); + err = svn_wc__wq_build_file_remove(&work_item, db, wri_abspath, + tmp_filename, + result_pool, scratch_pool); + if (err) + { + return svn_error_compose_create(err, + svn_io_remove_file2(tmp_filename, TRUE, scratch_pool)); + } + *tmp_filename_p = tmp_filename; + *cleanup_work_item_p = work_item; return SVN_NO_ERROR; } @@ -3873,18 +3887,15 @@ svn_wc__perform_file_merge(svn_skel_t **work_items the textual changes into the working file. */ const char *oldrev_str, *newrev_str, *mine_str; const char *merge_left; - svn_boolean_t delete_left = FALSE; const char *path_ext = ""; const char *new_pristine_abspath; enum svn_wc_merge_outcome_t merge_outcome = svn_wc_merge_unchanged; svn_skel_t *work_item; + svn_skel_t *cleanup_queue = NULL; + svn_error_t *err; *work_items = NULL; - SVN_ERR(svn_wc__db_pristine_get_path(&new_pristine_abspath, - db, wri_abspath, new_checksum, - scratch_pool, scratch_pool)); - /* If we have any file extensions we're supposed to preserve in generated conflict file names, then find this path's extension. But then, if it isn't one of @@ -3917,45 +3928,57 @@ svn_wc__perform_file_merge(svn_skel_t **work_items if (! original_checksum) { - SVN_ERR(get_empty_tmp_file(&merge_left, db, wri_abspath, + SVN_ERR(get_empty_tmp_file(&merge_left, &work_item, + db, wri_abspath, result_pool, scratch_pool)); - delete_left = TRUE; } else - SVN_ERR(svn_wc__db_pristine_get_path(&merge_left, db, wri_abspath, - original_checksum, - result_pool, scratch_pool)); + { + SVN_ERR(svn_wc__get_pristine_file_with_wq(&merge_left, &work_item, + db, wri_abspath, + original_checksum, + result_pool, scratch_pool)); + } + cleanup_queue = svn_wc__wq_merge(cleanup_queue, work_item, result_pool); + err = svn_wc__get_pristine_file_with_wq(&new_pristine_abspath, &work_item, + db, wri_abspath, new_checksum, + result_pool, scratch_pool); + if (err) + { + return svn_error_compose_create(err, + svn_wc__db_wq_add(db, wri_abspath, cleanup_queue, scratch_pool)); + } + cleanup_queue = svn_wc__wq_merge(cleanup_queue, work_item, result_pool); + /* Merge the changes from the old textbase to the new textbase into the file we're updating. Remember that this function wants full paths! */ - SVN_ERR(svn_wc__internal_merge(&work_item, - conflict_skel, - &merge_outcome, - db, - merge_left, - new_pristine_abspath, - local_abspath, - wri_abspath, - oldrev_str, newrev_str, mine_str, - old_actual_props, - FALSE /* dry_run */, - diff3_cmd, NULL, propchanges, - cancel_func, cancel_baton, - result_pool, scratch_pool)); + err = svn_wc__internal_merge(&work_item, + conflict_skel, + &merge_outcome, + db, + merge_left, + new_pristine_abspath, + local_abspath, + wri_abspath, + oldrev_str, newrev_str, mine_str, + old_actual_props, + FALSE /* dry_run */, + diff3_cmd, NULL, propchanges, + cancel_func, cancel_baton, + result_pool, scratch_pool); + if (err) + { + return svn_error_compose_create(err, + svn_wc__db_wq_add(db, wri_abspath, cleanup_queue, scratch_pool)); + } *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); + /* Queue the cleanup after the merge work item. */ + *work_items = svn_wc__wq_merge(*work_items, cleanup_queue, result_pool); *found_conflict = (merge_outcome == svn_wc_merge_conflict); - /* If we created a temporary left merge file, get rid of it. */ - if (delete_left) - { - SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, wri_abspath, - merge_left, - result_pool, scratch_pool)); - *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); - } - return SVN_NO_ERROR; } Index: subversion/libsvn_wc/util.c =================================================================== --- subversion/libsvn_wc/util.c (revision 1844566) +++ subversion/libsvn_wc/util.c (working copy) @@ -37,6 +37,7 @@ #include "wc.h" /* just for prototypes of things in this .c file */ #include "entries.h" +#include "workqueue.h" #include "private/svn_wc_private.h" #include "svn_private_config.h" @@ -505,8 +506,42 @@ svn_wc__fetch_base_func(const char **filename, return SVN_NO_ERROR; } - SVN_ERR(svn_wc__db_pristine_get_path(filename, sfb->db, local_abspath, - checksum, scratch_pool, scratch_pool)); + SVN_ERR(svn_wc__db_pristine_get_path(filename, sfb->db, + local_abspath, checksum, + svn_io_file_del_on_pool_cleanup, + result_pool, scratch_pool)); return SVN_NO_ERROR; } + +svn_error_t * +svn_wc__get_pristine_file_with_wq(const char **pristine_abspath_p, + svn_skel_t **cleanup_work_item_p, + svn_wc__db_t *db, + const char *wri_abspath, + const svn_checksum_t *sha1_checksum, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_error_t *err; + const char *pristine_abspath; + svn_skel_t *work_item; + + SVN_ERR(svn_wc__db_pristine_get_path(&pristine_abspath, db, + wri_abspath, sha1_checksum, + svn_io_file_del_none, + result_pool, scratch_pool)); + err = svn_wc__wq_build_file_remove(&work_item, db, wri_abspath, + pristine_abspath, + result_pool, scratch_pool); + if (err) + { + return svn_error_compose_create(err, + svn_io_remove_file2(pristine_abspath, TRUE, scratch_pool)); + } + + *pristine_abspath_p = pristine_abspath; + *cleanup_work_item_p = work_item; + return SVN_NO_ERROR; +} + Index: subversion/libsvn_wc/wc-metadata.sql =================================================================== --- subversion/libsvn_wc/wc-metadata.sql (revision 1844566) +++ subversion/libsvn_wc/wc-metadata.sql (working copy) @@ -80,7 +80,7 @@ CREATE UNIQUE INDEX I_LOCAL_ABSPATH ON WCROOT (loc derived from the 'checksum' column. Each pristine text is referenced by any number of rows in the NODES and ACTUAL_NODE tables. - In future, the pristine text file may be compressed. + TODO: COMPATIBILITY: Add a separate statement for older WC formats. */ CREATE TABLE PRISTINE ( /* The SHA-1 checksum of the pristine text. This is a unique key. The @@ -93,8 +93,7 @@ CREATE TABLE PRISTINE ( and the pristine text is stored verbatim in the file. */ compression INTEGER, - /* The size in bytes of the file in which the pristine text is stored. - Used to verify the pristine file is "proper". */ + /* The size of uncompressed pristine content. */ size INTEGER NOT NULL, /* The number of rows in the NODES table that have a 'checksum' column @@ -104,9 +103,13 @@ CREATE TABLE PRISTINE ( /* Alternative MD5 checksum used for communicating with older repositories. Not strictly guaranteed to be unique among table rows. */ - md5_checksum TEXT NOT NULL + md5_checksum TEXT NOT NULL, + + data BLOB ); +/* TODO: While here, perhaps add `WITHOUT ROWID` for the new schema table? */ + CREATE INDEX I_PRISTINE_MD5 ON PRISTINE (md5_checksum); /* ------------------------------------------------------------------------- */ Index: subversion/libsvn_wc/wc-queries.sql =================================================================== --- subversion/libsvn_wc/wc-queries.sql (revision 1844566) +++ subversion/libsvn_wc/wc-queries.sql (working copy) @@ -884,12 +884,15 @@ SELECT id, work FROM work_queue ORDER BY id LIMIT DELETE FROM work_queue WHERE id = ?1 -- STMT_INSERT_OR_IGNORE_PRISTINE -INSERT OR IGNORE INTO pristine (checksum, md5_checksum, size, refcount) -VALUES (?1, ?2, ?3, 0) +/* TODO: COMPATIBILITY: Add a separate statement for older WC formats. */ +INSERT OR IGNORE INTO pristine ( + checksum, md5_checksum, size, refcount, compression, data ) +VALUES (?1, ?2, ?3, 0, ?4, null) -- STMT_INSERT_PRISTINE -INSERT INTO pristine (checksum, md5_checksum, size, refcount) -VALUES (?1, ?2, ?3, 0) +/* TODO: COMPATIBILITY: Add a separate statement for older WC formats. */ +INSERT INTO pristine (checksum, md5_checksum, size, refcount, compression, data) +VALUES (?1, ?2, ?3, 0, ?4, ?5) -- STMT_SELECT_PRISTINE SELECT md5_checksum @@ -896,8 +899,9 @@ SELECT md5_checksum FROM pristine WHERE checksum = ?1 --- STMT_SELECT_PRISTINE_SIZE -SELECT size +-- STMT_SELECT_PRISTINE_INFO +/* TODO: COMPATIBILITY: Add a separate statement for older WC formats. */ +SELECT size, compression, data FROM pristine WHERE checksum = ?1 LIMIT 1 @@ -916,8 +920,9 @@ DELETE FROM pristine WHERE checksum = ?1 AND refcount = 0 -- STMT_SELECT_COPY_PRISTINES +/* TODO: COMPATIBILITY: Add a separate statement for older WC formats. */ /* For the root itself */ -SELECT n.checksum, md5_checksum, size +SELECT n.checksum, md5_checksum, size, compression, data FROM nodes_current n LEFT JOIN pristine p ON n.checksum = p.checksum WHERE wc_id = ?1 @@ -925,7 +930,7 @@ WHERE wc_id = ?1 AND n.checksum IS NOT NULL UNION ALL /* And all descendants */ -SELECT n.checksum, md5_checksum, size +SELECT n.checksum, md5_checksum, size, compression, data FROM nodes n LEFT JOIN pristine p ON n.checksum = p.checksum WHERE wc_id = ?1 Index: subversion/libsvn_wc/wc.h =================================================================== --- subversion/libsvn_wc/wc.h (revision 1844566) +++ subversion/libsvn_wc/wc.h (working copy) @@ -760,6 +760,15 @@ svn_wc__node_has_local_mods(svn_boolean_t *modifie void *cancel_baton, apr_pool_t *scratch_pool); +svn_error_t * +svn_wc__get_pristine_file_with_wq(const char **pristine_abspath_p, + svn_skel_t **cleanup_work_item_p, + svn_wc__db_t *db, + const char *wri_abspath, + const svn_checksum_t *sha1_checksum, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + #ifdef __cplusplus } #endif /* __cplusplus */ Index: subversion/libsvn_wc/wc_db.h =================================================================== --- subversion/libsvn_wc/wc_db.h (revision 1844566) +++ subversion/libsvn_wc/wc_db.h (working copy) @@ -915,6 +915,7 @@ svn_wc__db_pristine_get_path(const char **pristine svn_wc__db_t *db, const char *wri_abspath, const svn_checksum_t *checksum, + svn_io_file_del_t delete_when, apr_pool_t *result_pool, apr_pool_t *scratch_pool); @@ -965,10 +966,10 @@ typedef struct svn_wc__db_install_data_t Allocate the new stream, path and checksums in RESULT_POOL. */ svn_error_t * -svn_wc__db_pristine_prepare_install(svn_stream_t **stream, - svn_wc__db_install_data_t **install_data, - svn_checksum_t **sha1_checksum, - svn_checksum_t **md5_checksum, +svn_wc__db_pristine_prepare_install(svn_stream_t **stream_p, + svn_wc__db_install_data_t **install_data_p, + svn_checksum_t **sha1_checksum_p, + svn_checksum_t **md5_checksum_p, svn_wc__db_t *db, const char *wri_abspath, apr_pool_t *result_pool, Index: subversion/libsvn_wc/wc_db_pristine.c =================================================================== --- subversion/libsvn_wc/wc_db_pristine.c (revision 1844566) +++ subversion/libsvn_wc/wc_db_pristine.c (working copy) @@ -30,6 +30,7 @@ #include "svn_dirent_uri.h" #include "private/svn_io_private.h" +#include "private/svn_subr_private.h" #include "wc.h" #include "wc_db.h" @@ -98,6 +99,7 @@ svn_wc__db_pristine_get_path(const char **pristine svn_wc__db_t *db, const char *wri_abspath, const svn_checksum_t *sha1_checksum, + svn_io_file_del_t delete_when, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { @@ -104,10 +106,17 @@ svn_wc__db_pristine_get_path(const char **pristine svn_wc__db_wcroot_t *wcroot; const char *local_relpath; svn_boolean_t present; + svn_stream_t *contents; + const char *tmpdir; + svn_stream_t *tmpstream; + svn_error_t *err; SVN_ERR_ASSERT(pristine_abspath != NULL); SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath)); SVN_ERR_ASSERT(sha1_checksum != NULL); + SVN_ERR_ASSERT(delete_when == svn_io_file_del_none || + delete_when == svn_io_file_del_on_pool_cleanup); + /* ### Transitional: accept MD-5 and look up the SHA-1. Return an error * if the pristine text is not in the store. */ if (sha1_checksum->kind != svn_checksum_sha1) @@ -130,11 +139,22 @@ svn_wc__db_pristine_get_path(const char **pristine svn_checksum_to_cstring_display(sha1_checksum, scratch_pool)); - SVN_ERR(get_pristine_fname(pristine_abspath, wcroot->abspath, - sha1_checksum, - result_pool, scratch_pool)); + /* TODO: COMPATIBILITY: Not required for older WC formats: */ + SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmpdir, db, wri_abspath, + scratch_pool, scratch_pool)); + SVN_ERR(svn_wc__db_pristine_read(&contents, NULL, db, wri_abspath, + sha1_checksum, scratch_pool, + scratch_pool)); + SVN_ERR(svn_stream_open_unique(&tmpstream, pristine_abspath, tmpdir, + delete_when, result_pool, scratch_pool)); + err = svn_stream_copy3(contents, tmpstream, NULL, NULL, scratch_pool); + if (err && delete_when == svn_io_file_del_none) + { + err = svn_error_compose_create(err, + svn_io_remove_file2(*pristine_abspath, TRUE, scratch_pool)); + } - return SVN_NO_ERROR; + return svn_error_trace(err); } svn_error_t * @@ -150,6 +170,31 @@ svn_wc__db_pristine_get_future_path(const char **p return SVN_NO_ERROR; } +/* Matches the allowed values of the PRISTINE.compression field. */ +typedef enum compression_type_t +{ + compression_type_none = 0, + compression_type_lz4 = 1 +} compression_type_t; + +static svn_error_t * +parse_compression_type(compression_type_t *type_p, int value) +{ + switch (value) + { + case compression_type_none: + break; + case compression_type_lz4: + /* TODO: Check if this value is allowed by the WC format. */ + break; + default: + return svn_error_create(SVN_ERR_INVALID_INPUT, NULL, NULL); + } + + *type_p = (compression_type_t) value; + return SVN_NO_ERROR; +} + /* Set *CONTENTS to a readable stream from which the pristine text * identified by SHA1_CHECKSUM and PRISTINE_ABSPATH can be read from the * pristine store of WCROOT. If SIZE is not null, set *SIZE to the size @@ -176,11 +221,11 @@ pristine_read_txn(svn_stream_t **contents, { svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; + compression_type_t compression_type; - /* Check that this pristine text is present in the store. (The presence - * of the file is not sufficient.) */ + /* Check that this pristine text is present in the store. */ SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_PRISTINE_SIZE)); + STMT_SELECT_PRISTINE_INFO)); SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); @@ -187,7 +232,6 @@ pristine_read_txn(svn_stream_t **contents, if (size) *size = svn_sqlite__column_int64(stmt, 0); - SVN_ERR(svn_sqlite__reset(stmt)); if (! have_row) { return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, @@ -196,21 +240,50 @@ pristine_read_txn(svn_stream_t **contents, sha1_checksum, scratch_pool)); } - /* Open the file as a readable stream. It will remain readable even when - * deleted from disk; APR guarantees that on Windows as well as Unix. - * - * We also don't enable APR_BUFFERED on this file to maximize throughput - * e.g. for fulltext comparison. As we use SVN__STREAM_CHUNK_SIZE buffers - * where needed in streams, there is no point in having another layer of - * buffers. */ + SVN_ERR(parse_compression_type(&compression_type, + svn_sqlite__column_int(stmt, 1))); + if (contents) { - apr_file_t *file; - SVN_ERR(svn_io_file_open(&file, pristine_abspath, APR_READ, - APR_OS_DEFAULT, result_pool)); - *contents = svn_stream_from_aprfile2(file, FALSE, result_pool); + if (svn_sqlite__column_is_null(stmt, 2)) + { + apr_file_t *file; + /* Open the file as a readable stream. It will remain readable even when + * deleted from disk; APR guarantees that on Windows as well as Unix. + * + * We also don't enable APR_BUFFERED on this file to maximize throughput + * e.g. for fulltext comparison. As we use SVN__STREAM_CHUNK_SIZE buffers + * where needed in streams, there is no point in having another layer of + * buffers. */ + SVN_ERR(svn_io_file_open(&file, pristine_abspath, APR_READ, + APR_OS_DEFAULT, result_pool)); + *contents = svn_stream_from_aprfile2(file, FALSE, result_pool); + } + else + { + apr_size_t data_len; + const void *data; + svn_string_t *str; + + data = svn_sqlite__column_blob(stmt, 2, &data_len, NULL); + str = svn_string_ncreate(data, data_len, result_pool); + *contents = svn_stream_from_string(str, result_pool); + } + + switch (compression_type) + { + case compression_type_none: + break; + case compression_type_lz4: + *contents = svn_stream__lz4_decompress(*contents, result_pool); + break; + default: + SVN_ERR_MALFUNCTION(); + } } + SVN_ERR(svn_sqlite__reset(stmt)); + return SVN_NO_ERROR; } @@ -268,6 +341,19 @@ pristine_get_tempdir(svn_wc__db_wcroot_t *wcroot, PRISTINE_TEMPDIR_RELPATH, SVN_VA_NULL); } +struct svn_wc__db_install_data_t +{ + svn_wc__db_wcroot_t *wcroot; + svn_stream_t *install_stream; + svn_stringbuf_t *buf; + svn_stream_t *inner_stream; + compression_type_t compression_type; + svn_filesize_t uncompressed_size; + const char *temp_dir_abspath; + int blob_size_limit; + apr_pool_t *pool; +}; + /* Install the pristine text described by BATON into the pristine store of * SDB. If it is already stored then just delete the new file * BATON->tempfile_abspath. @@ -279,8 +365,7 @@ pristine_get_tempdir(svn_wc__db_wcroot_t *wcroot, */ static svn_error_t * pristine_install_txn(svn_sqlite__db_t *sdb, - /* The path to the source file that is to be moved into place. */ - svn_stream_t *install_stream, + svn_wc__db_install_data_t *install_data, /* The target path for the file (within the pristine store). */ const char *pristine_abspath, /* The pristine text's SHA-1 checksum. */ @@ -301,7 +386,7 @@ pristine_install_txn(svn_sqlite__db_t *sdb, if (have_row) { -#ifdef SVN_DEBUG +#if 0 /* Consistency checks. Verify both files exist and match. * ### We could check much more. */ { @@ -325,47 +410,129 @@ pristine_install_txn(svn_sqlite__db_t *sdb, #endif /* Remove the temp file: it's already there */ - SVN_ERR(svn_stream__install_delete(install_stream, scratch_pool)); + if (install_data->install_stream) + SVN_ERR(svn_stream__install_delete(install_data->install_stream, scratch_pool)); return SVN_NO_ERROR; } /* Move the file to its target location. (If it is already there, it is * an orphan file and it doesn't matter if we overwrite it.) */ - { - apr_finfo_t finfo; - SVN_ERR(svn_stream__install_get_info(&finfo, install_stream, - APR_FINFO_SIZE, scratch_pool)); - SVN_ERR(svn_stream__install_stream(install_stream, pristine_abspath, - TRUE, scratch_pool)); + if (install_data->install_stream) + SVN_ERR(svn_stream__install_stream(install_data->install_stream, + pristine_abspath, TRUE, + scratch_pool)); - SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_PRISTINE)); - SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool)); - SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, md5_checksum, scratch_pool)); - SVN_ERR(svn_sqlite__bind_int64(stmt, 3, finfo.size)); - SVN_ERR(svn_sqlite__insert(NULL, stmt)); + SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_PRISTINE)); + SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool)); + SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, md5_checksum, scratch_pool)); + SVN_ERR(svn_sqlite__bind_int64(stmt, 3, install_data->uncompressed_size)); + SVN_ERR(svn_sqlite__bind_int(stmt, 4, install_data->compression_type)); + if (!install_data->install_stream) + SVN_ERR(svn_sqlite__bind_blob(stmt, 5, install_data->buf->data, install_data->buf->len)); + + SVN_ERR(svn_sqlite__insert(NULL, stmt)); + + if (install_data->install_stream) SVN_ERR(svn_io_set_file_read_only(pristine_abspath, FALSE, scratch_pool)); + + return SVN_NO_ERROR; +} + +static svn_error_t * +inner_stream_write_fn(void *baton, const char *data, apr_size_t *len) +{ + svn_wc__db_install_data_t *install_data = baton; + + if (!install_data->install_stream && + install_data->buf->len + *len > install_data->blob_size_limit) + { + apr_size_t wlen; + + SVN_ERR_W(svn_stream__create_for_install(&install_data->install_stream, + install_data->temp_dir_abspath, + install_data->pool, + /* TODO */ install_data->pool), + _("Unable to create pristine install stream")); + wlen = install_data->buf->len; + SVN_ERR(svn_stream_write(install_data->install_stream, + install_data->buf->data, &wlen)); + install_data->buf = NULL; } + if (install_data->install_stream) + SVN_ERR(svn_stream_write(install_data->install_stream, data, len)); + else + svn_stringbuf_appendbytes(install_data->buf, data, *len); + return SVN_NO_ERROR; } -struct svn_wc__db_install_data_t +static svn_error_t * +inner_stream_seek_fn(void *baton, const svn_stream_mark_t *mark) { - svn_wc__db_wcroot_t *wcroot; - svn_stream_t *inner_stream; -}; + /* TODO: Implement this. */ + return svn_error_create(APR_ENOTIMPL, NULL, NULL); +} +static svn_error_t * +inner_stream_close_fn(void *baton) +{ + svn_wc__db_install_data_t *install_data = baton; + + if (install_data->install_stream) + SVN_ERR(svn_stream_close(install_data->install_stream)); + + return SVN_NO_ERROR; +} + +static svn_error_t * +install_stream_write_fn(void *baton, const char *data, apr_size_t *len) +{ + svn_wc__db_install_data_t *install_data = baton; + + SVN_ERR(svn_stream_write(install_data->inner_stream, data, len)); + install_data->uncompressed_size += *len; + + return SVN_NO_ERROR; +} + +static svn_error_t * +install_stream_seek_fn(void *baton, const svn_stream_mark_t *mark) +{ + svn_wc__db_install_data_t *install_data = baton; + + if (!mark) + return svn_error_create(SVN_ERR_STREAM_SEEK_NOT_SUPPORTED, NULL, NULL); + + SVN_ERR(svn_stream_reset(install_data->inner_stream)); + install_data->uncompressed_size = 0; + + return SVN_NO_ERROR; +} + +static svn_error_t * +install_stream_close_fn(void *baton) +{ + svn_wc__db_install_data_t *install_data = baton; + + SVN_ERR(svn_stream_close(install_data->inner_stream)); + + return SVN_NO_ERROR; +} + svn_error_t * -svn_wc__db_pristine_prepare_install(svn_stream_t **stream, - svn_wc__db_install_data_t **install_data, - svn_checksum_t **sha1_checksum, - svn_checksum_t **md5_checksum, +svn_wc__db_pristine_prepare_install(svn_stream_t **stream_p, + svn_wc__db_install_data_t **install_data_p, + svn_checksum_t **sha1_checksum_p, + svn_checksum_t **md5_checksum_p, svn_wc__db_t *db, const char *wri_abspath, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { + svn_stream_t *stream; + svn_wc__db_install_data_t *install_data; svn_wc__db_wcroot_t *wcroot; const char *local_relpath; const char *temp_dir_abspath; @@ -378,23 +545,49 @@ svn_error_t * temp_dir_abspath = pristine_get_tempdir(wcroot, scratch_pool, scratch_pool); - *install_data = apr_pcalloc(result_pool, sizeof(**install_data)); - (*install_data)->wcroot = wcroot; + install_data = apr_pcalloc(result_pool, sizeof(*install_data)); + install_data->wcroot = wcroot; + /* TODO: Determine the compression type based on the WC format, etc. */ + install_data->compression_type = compression_type_lz4; + install_data->uncompressed_size = 0; + install_data->install_stream = NULL; + install_data->temp_dir_abspath = apr_pstrdup(result_pool, temp_dir_abspath); + install_data->pool = result_pool; + install_data->blob_size_limit = 16 * 1024; + install_data->buf = svn_stringbuf_create_ensure(install_data->blob_size_limit, + result_pool); - SVN_ERR_W(svn_stream__create_for_install(stream, - temp_dir_abspath, - result_pool, scratch_pool), - _("Unable to create pristine install stream")); + install_data->inner_stream = svn_stream_create(install_data, result_pool); + svn_stream_set_write(install_data->inner_stream, inner_stream_write_fn); + svn_stream_set_seek(install_data->inner_stream, inner_stream_seek_fn); + svn_stream_set_close(install_data->inner_stream, inner_stream_close_fn); - (*install_data)->inner_stream = *stream; + switch (install_data->compression_type) + { + case compression_type_none: + break; + case compression_type_lz4: + install_data->inner_stream = + svn_stream__lz4_compress(install_data->inner_stream, result_pool); + break; + default: + SVN_ERR_MALFUNCTION(); + } - if (md5_checksum) - *stream = svn_stream_checksummed2(*stream, NULL, md5_checksum, - svn_checksum_md5, FALSE, result_pool); - if (sha1_checksum) - *stream = svn_stream_checksummed2(*stream, NULL, sha1_checksum, - svn_checksum_sha1, FALSE, result_pool); + stream = svn_stream_create(install_data, result_pool); + svn_stream_set_write(stream, install_stream_write_fn); + svn_stream_set_seek(stream, install_stream_seek_fn); + svn_stream_set_close(stream, install_stream_close_fn); + if (md5_checksum_p) + stream = svn_stream_checksummed2(stream, NULL, md5_checksum_p, + svn_checksum_md5, FALSE, result_pool); + if (sha1_checksum_p) + stream = svn_stream_checksummed2(stream, NULL, sha1_checksum_p, + svn_checksum_sha1, FALSE, result_pool); + + *stream_p = stream; + *install_data_p = install_data; return SVN_NO_ERROR; } @@ -420,7 +613,7 @@ svn_wc__db_pristine_install(svn_wc__db_install_dat * at the disk, to ensure no concurrent pristine install/delete txn. */ SVN_SQLITE__WITH_IMMEDIATE_TXN( pristine_install_txn(wcroot->sdb, - install_data->inner_stream, pristine_abspath, + install_data, pristine_abspath, sha1_checksum, md5_checksum, scratch_pool), wcroot->sdb); @@ -432,7 +625,10 @@ svn_error_t * svn_wc__db_pristine_install_abort(svn_wc__db_install_data_t *install_data, apr_pool_t *scratch_pool) { - return svn_error_trace(svn_stream__install_delete(install_data->inner_stream, + if (!install_data->install_stream) + return SVN_NO_ERROR; + + return svn_error_trace(svn_stream__install_delete(install_data->install_stream, scratch_pool)); } @@ -521,6 +717,7 @@ maybe_transfer_one_pristine(svn_wc__db_wcroot_t *s const svn_checksum_t *checksum, const svn_checksum_t *md5_checksum, apr_int64_t size, + compression_type_t compression, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *scratch_pool) @@ -539,6 +736,7 @@ maybe_transfer_one_pristine(svn_wc__db_wcroot_t *s SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, checksum, scratch_pool)); SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, md5_checksum, scratch_pool)); SVN_ERR(svn_sqlite__bind_int64(stmt, 3, size)); + SVN_ERR(svn_sqlite__bind_int(stmt, 4, compression)); SVN_ERR(svn_sqlite__update(&affected_rows, stmt)); @@ -624,6 +822,7 @@ pristine_transfer_txn(svn_wc__db_wcroot_t *src_wcr const svn_checksum_t *md5_checksum; apr_int64_t size; svn_error_t *err; + compression_type_t compression; svn_pool_clear(iterpool); @@ -630,9 +829,13 @@ pristine_transfer_txn(svn_wc__db_wcroot_t *src_wcr SVN_ERR(svn_sqlite__column_checksum(&checksum, stmt, 0, iterpool)); SVN_ERR(svn_sqlite__column_checksum(&md5_checksum, stmt, 1, iterpool)); size = svn_sqlite__column_int64(stmt, 2); + SVN_ERR(parse_compression_type(&compression, + svn_sqlite__column_int(stmt, 3))); + /* TODO: Update for intra-SQLite pristines, doesn't work yet. */ err = maybe_transfer_one_pristine(src_wcroot, dst_wcroot, checksum, md5_checksum, size, + compression, cancel_func, cancel_baton, iterpool); @@ -908,6 +1111,8 @@ svn_wc__db_pristine_check(svn_boolean_t *present, wri_abspath, scratch_pool, scratch_pool)); VERIFY_USABLE_WCROOT(wcroot); + /* TODO: COMPATIBILITY: Restore this optimization for older WC formats: */ +#if 0 /* A filestat is much cheaper than a sqlite transaction especially on NFS, so first check if there is a pristine file and then if we are allowed to use it. */ @@ -941,6 +1146,7 @@ svn_wc__db_pristine_check(svn_boolean_t *present, return SVN_NO_ERROR; } } +#endif /* Check that there is an entry in the PRISTINE table. */ SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_PRISTINE)); Index: subversion/libsvn_wc/wc_db_update_move.c =================================================================== --- subversion/libsvn_wc/wc_db_update_move.c (revision 1844566) +++ subversion/libsvn_wc/wc_db_update_move.c (working copy) @@ -1317,8 +1317,6 @@ tc_editor_alter_file(node_move_baton_t *nmb, const char *local_abspath = svn_dirent_join(b->wcroot->abspath, dst_relpath, scratch_pool); - const char *old_pristine_abspath; - const char *new_pristine_abspath; svn_skel_t *conflict_skel = NULL; apr_hash_t *actual_props; apr_array_header_t *propchanges; @@ -1382,6 +1380,10 @@ tc_editor_alter_file(node_move_baton_t *nmb, } else { + const char *old_pristine_abspath; + const char *new_pristine_abspath; + svn_skel_t *cleanup_queue = NULL; + svn_error_t *err; /* * Run a 3-way merge to update the file, using the pre-update * pristine text as the merge base, the post-update pristine @@ -1388,30 +1390,50 @@ tc_editor_alter_file(node_move_baton_t *nmb, * text as the merge-left version, and the current content of the * moved-here working file as the merge-right version. */ - SVN_ERR(svn_wc__db_pristine_get_path(&old_pristine_abspath, - b->db, b->wcroot->abspath, - old_version.checksum, - scratch_pool, scratch_pool)); - SVN_ERR(svn_wc__db_pristine_get_path(&new_pristine_abspath, - b->db, b->wcroot->abspath, - new_version.checksum, - scratch_pool, scratch_pool)); - SVN_ERR(svn_wc__internal_merge(&work_item, &conflict_skel, - &merge_outcome, b->db, - old_pristine_abspath, - new_pristine_abspath, - local_abspath, - local_abspath, - NULL, NULL, NULL, /* diff labels */ - actual_props, - FALSE, /* dry-run */ - NULL, /* diff3-cmd */ - NULL, /* merge options */ - propchanges, - b->cancel_func, b->cancel_baton, - scratch_pool, scratch_pool)); + SVN_ERR(svn_wc__get_pristine_file_with_wq(&old_pristine_abspath, + &work_item, b->db, + b->wcroot->abspath, + old_version.checksum, + scratch_pool, scratch_pool)); + cleanup_queue = svn_wc__wq_merge(cleanup_queue, work_item, scratch_pool); + err = svn_wc__get_pristine_file_with_wq(&new_pristine_abspath, + &work_item, b->db, + b->wcroot->abspath, + new_version.checksum, + scratch_pool, scratch_pool); + if (err) + { + return svn_error_compose_create(err, + svn_wc__db_wq_add(b->db, b->wcroot->abspath, cleanup_queue, + scratch_pool)); + } + cleanup_queue = svn_wc__wq_merge(cleanup_queue, work_item, scratch_pool); + + err = svn_wc__internal_merge(&work_item, &conflict_skel, + &merge_outcome, b->db, + old_pristine_abspath, + new_pristine_abspath, + local_abspath, + local_abspath, + NULL, NULL, NULL, /* diff labels */ + actual_props, + FALSE, /* dry-run */ + NULL, /* diff3-cmd */ + NULL, /* merge options */ + propchanges, + b->cancel_func, b->cancel_baton, + scratch_pool, scratch_pool); + if (err) + { + return svn_error_compose_create(err, + svn_wc__db_wq_add(b->db, b->wcroot->abspath, cleanup_queue, + scratch_pool)); + } + work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); + /* Queue the cleanup after the merge work item. */ + work_items = svn_wc__wq_merge(work_items, cleanup_queue, scratch_pool); if (merge_outcome == svn_wc_merge_conflict) content_state = svn_wc_notify_state_conflicted; @@ -1610,6 +1632,8 @@ tc_editor_update_incoming_moved_file(node_move_bat SVN_ERR(svn_wc__db_pristine_get_path(&old_pristine_abspath, b->db, b->wcroot->abspath, src_checksum, + /* TODO: svn_wc__get_pristine_file_with_wq() */ + svn_io_file_del_on_pool_cleanup, scratch_pool, scratch_pool)); src_abspath = svn_dirent_join(b->wcroot->abspath, src_relpath, scratch_pool); @@ -3168,17 +3192,19 @@ tc_editor_update_add_merge_files(added_node_baton_ * and the locally added content of the working file as the merge-right * version. */ + + /* Create a property diff which shows all props as added. */ + SVN_ERR(svn_prop_diffs(&propchanges, working_props, + apr_hash_make(scratch_pool), scratch_pool)); + SVN_ERR(svn_io_open_unique_file3(NULL, &empty_file_abspath, NULL, svn_io_file_del_on_pool_cleanup, scratch_pool, scratch_pool)); SVN_ERR(svn_wc__db_pristine_get_path(&pristine_abspath, b->db, b->wcroot->abspath, base_checksum, + /* TODO: svn_wc__get_pristine_file_with_wq() */ + svn_io_file_del_on_pool_cleanup, scratch_pool, scratch_pool)); - - /* Create a property diff which shows all props as added. */ - SVN_ERR(svn_prop_diffs(&propchanges, working_props, - apr_hash_make(scratch_pool), scratch_pool)); - SVN_ERR(svn_wc__internal_merge(&work_item, &conflict_skel, &merge_outcome, b->db, empty_file_abspath, Index: subversion/libsvn_wc/workqueue.c =================================================================== --- subversion/libsvn_wc/workqueue.c (revision 1844566) +++ subversion/libsvn_wc/workqueue.c (working copy) @@ -499,6 +499,8 @@ run_file_install(work_item_baton_t *wqb, SVN_ERR(svn_wc__db_from_relpath(&source_abspath, db, wri_abspath, local_relpath, scratch_pool, scratch_pool)); + SVN_ERR(svn_stream_open_readonly(&src_stream, source_abspath, + scratch_pool, scratch_pool)); } else if (! checksum) { @@ -518,15 +520,13 @@ run_file_install(work_item_baton_t *wqb, } else { - SVN_ERR(svn_wc__db_pristine_get_future_path(&source_abspath, - wcroot_abspath, - checksum, - scratch_pool, scratch_pool)); + /* TODO: Potential regression here: this now requires an additional + * transaction, whereas previously we did not have to consult the DB. + * This may be optimized by storing the additional data in the workitem. */ + SVN_ERR(svn_wc__db_pristine_read(&src_stream, NULL, db, local_abspath, + checksum, scratch_pool, scratch_pool)); } - SVN_ERR(svn_stream_open_readonly(&src_stream, source_abspath, - scratch_pool, scratch_pool)); - /* Fetch all the translation bits. */ SVN_ERR(svn_wc__get_translate_info(&style, &eol, &keywords, Index: subversion/svn/svn.c =================================================================== --- subversion/svn/svn.c (revision 1844566) +++ subversion/svn/svn.c (working copy) @@ -2233,8 +2233,8 @@ parse_compatible_version(svn_cl__opt_state_t* opt_ release, and svn_client_supported_wc_version() should return such a value. */ const svn_version_t *supported = svn_client_supported_wc_version(); - const svn_version_t *current = svn_client_version(); - const svn_version_t latest = {current->major, current->minor, 0, NULL}; + /* Plumbing the non-constant aggregate initialization here: */ + const svn_version_t latest = {SVN_VER_MAJOR, SVN_VER_MINOR, 0, NULL}; /* Double check that the oldest supported version is sane. */ SVN_ERR_ASSERT(supported->patch == 0);