Modified: subversion/branches/pin-externals/subversion/svnrdump/dump_editor.c URL: http://svn.apache.org/viewvc/subversion/branches/pin-externals/subversion/svnrdump/dump_editor.c?rev=1653578&r1=1653577&r2=1653578&view=diff ============================================================================== --- subversion/branches/pin-externals/subversion/svnrdump/dump_editor.c (original) +++ subversion/branches/pin-externals/subversion/svnrdump/dump_editor.c Wed Jan 21 16:22:19 2015 @@ -30,6 +30,7 @@ #include "svn_subst.h" #include "svn_dirent_uri.h" +#include "private/svn_repos_private.h" #include "private/svn_subr_private.h" #include "private/svn_dep_compat.h" #include "private/svn_editor.h" @@ -50,17 +51,10 @@ struct dir_baton { struct dump_edit_baton *eb; - struct dir_baton *parent_dir_baton; /* Pool for per-directory allocations */ apr_pool_t *pool; - /* is this directory a new addition to this revision? */ - svn_boolean_t added; - - /* has this directory been written to the output stream? */ - svn_boolean_t written_out; - /* the path to this directory */ const char *repos_relpath; /* a relpath */ @@ -90,7 +84,6 @@ struct dir_baton struct file_baton { struct dump_edit_baton *eb; - struct dir_baton *parent_dir_baton; /* Pool for per-file allocations */ apr_pool_t *pool; @@ -164,53 +157,42 @@ struct dump_edit_baton { * copy source. * * PB is the directory baton of this directory's parent, or NULL if - * this is the top-level directory of the edit. ADDED indicates if - * this directory is newly added in this revision. Perform all - * allocations in POOL. */ + * this is the top-level directory of the edit. + * + * Perform all allocations in POOL. */ static struct dir_baton * make_dir_baton(const char *path, const char *copyfrom_path, svn_revnum_t copyfrom_rev, void *edit_baton, struct dir_baton *pb, - svn_boolean_t added, apr_pool_t *pool) { struct dump_edit_baton *eb = edit_baton; struct dir_baton *new_db = apr_pcalloc(pool, sizeof(*new_db)); const char *repos_relpath; - apr_pool_t *dir_pool; /* Construct the full path of this node. */ if (pb) - { - dir_pool = svn_pool_create(pb->pool); - repos_relpath = svn_relpath_canonicalize(path, dir_pool); - } + repos_relpath = svn_relpath_canonicalize(path, pool); else - { - dir_pool = svn_pool_create(eb->pool); - repos_relpath = ""; - } + repos_relpath = ""; /* Strip leading slash from copyfrom_path so that the path is canonical and svn_relpath_join can be used */ if (copyfrom_path) - copyfrom_path = svn_relpath_canonicalize(copyfrom_path, dir_pool); + copyfrom_path = svn_relpath_canonicalize(copyfrom_path, pool); new_db->eb = eb; - new_db->parent_dir_baton = pb; - new_db->pool = dir_pool; + new_db->pool = pool; new_db->repos_relpath = repos_relpath; new_db->copyfrom_path = copyfrom_path - ? svn_relpath_canonicalize(copyfrom_path, dir_pool) + ? svn_relpath_canonicalize(copyfrom_path, pool) : NULL; new_db->copyfrom_rev = copyfrom_rev; - new_db->added = added; - new_db->written_out = FALSE; - new_db->props = apr_hash_make(dir_pool); - new_db->deleted_props = apr_hash_make(dir_pool); - new_db->deleted_entries = apr_hash_make(dir_pool); + new_db->props = apr_hash_make(pool); + new_db->deleted_props = apr_hash_make(pool); + new_db->deleted_entries = apr_hash_make(pool); return new_db; } @@ -224,15 +206,13 @@ make_file_baton(const char *path, struct dir_baton *pb, apr_pool_t *pool) { - apr_pool_t *file_pool = svn_pool_create(pb->pool); - struct file_baton *new_fb = apr_pcalloc(file_pool, sizeof(*new_fb)); + struct file_baton *new_fb = apr_pcalloc(pool, sizeof(*new_fb)); new_fb->eb = pb->eb; - new_fb->parent_dir_baton = pb; - new_fb->pool = file_pool; - new_fb->repos_relpath = svn_relpath_canonicalize(path, file_pool); - new_fb->props = apr_hash_make(file_pool); - new_fb->deleted_props = apr_hash_make(file_pool); + new_fb->pool = pool; + new_fb->repos_relpath = svn_relpath_canonicalize(path, pool); + new_fb->props = apr_hash_make(pool); + new_fb->deleted_props = apr_hash_make(pool); new_fb->is_copy = FALSE; new_fb->copyfrom_path = NULL; new_fb->copyfrom_rev = SVN_INVALID_REVNUM; @@ -241,9 +221,11 @@ make_file_baton(const char *path, return new_fb; } -/* Return in *HEADER and *CONTENT the headers and content for PROPS. */ +/* Append to HEADERS the required headers, and set *CONTENT to the property + * content section, to represent the property delta of PROPS/DELETED_PROPS. + */ static svn_error_t * -get_props_content(svn_stringbuf_t **header, +get_props_content(apr_array_header_t *headers, svn_stringbuf_t **content, apr_hash_t *props, apr_hash_t *deleted_props, @@ -252,10 +234,8 @@ get_props_content(svn_stringbuf_t **head { svn_stream_t *content_stream; apr_hash_t *normal_props; - const char *buf; *content = svn_stringbuf_create_empty(result_pool); - *header = svn_stringbuf_create_empty(result_pool); content_stream = svn_stream_from_stringbuf(*content, scratch_pool); @@ -266,71 +246,13 @@ get_props_content(svn_stringbuf_t **head SVN_ERR(svn_stream_close(content_stream)); /* Prop-delta: true */ - *header = svn_stringbuf_createf(result_pool, SVN_REPOS_DUMPFILE_PROP_DELTA - ": true\n"); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_PROP_DELTA, "true"); /* Prop-content-length: 193 */ - buf = apr_psprintf(scratch_pool, SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH - ": %" APR_SIZE_T_FMT "\n", (*content)->len); - svn_stringbuf_appendcstr(*header, buf); - - return SVN_NO_ERROR; -} - -/* Extract and dump properties stored in PROPS and property deletions - * stored in DELETED_PROPS. If TRIGGER_VAR is not NULL, it is set to - * FALSE. - * - * If PROPSTRING is non-NULL, set *PROPSTRING to a string containing - * the content block of the property changes; otherwise, dump that to - * the stream, too. - */ -static svn_error_t * -do_dump_props(svn_stringbuf_t **propstring, - svn_stream_t *stream, - apr_hash_t *props, - apr_hash_t *deleted_props, - svn_boolean_t *trigger_var, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - svn_stringbuf_t *header; - svn_stringbuf_t *content; - apr_size_t len; - - if (trigger_var && !*trigger_var) - return SVN_NO_ERROR; - - SVN_ERR(get_props_content(&header, &content, props, deleted_props, - result_pool, scratch_pool)); - len = header->len; - SVN_ERR(svn_stream_write(stream, header->data, &len)); - - if (propstring) - { - *propstring = content; - } - else - { - /* Content-length: 14 */ - SVN_ERR(svn_stream_printf(stream, scratch_pool, - SVN_REPOS_DUMPFILE_CONTENT_LENGTH - ": %" APR_SIZE_T_FMT "\n\n", - content->len)); - - len = content->len; - SVN_ERR(svn_stream_write(stream, content->data, &len)); - - /* No text is going to be dumped. Write a couple of newlines and - wait for the next node/ revision. */ - SVN_ERR(svn_stream_puts(stream, "\n\n")); - - /* Cleanup so that data is never dumped twice. */ - apr_hash_clear(props); - apr_hash_clear(deleted_props); - if (trigger_var) - *trigger_var = FALSE; - } + svn_repos__dumpfile_header_pushf( + headers, SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH, + "%" APR_SIZE_T_FMT, (*content)->len); return SVN_NO_ERROR; } @@ -349,15 +271,17 @@ do_dump_newlines(struct dump_edit_baton } /* - * Write out a node record for PATH of type KIND under EB->FS_ROOT. + * Write out a partial node record for PATH of type KIND. * ACTION describes what is happening to the node (see enum - * svn_node_action). Write record to writable EB->STREAM, using - * EB->BUFFER to write in chunks. + * svn_node_action). Write record to writable EB->STREAM. * * If the node was itself copied, IS_COPY is TRUE and the * path/revision of the copy source are in COPYFROM_PATH/COPYFROM_REV. * If IS_COPY is FALSE, yet COPYFROM_PATH/COPYFROM_REV are valid, this * node is part of a copied subtree. + + * Note: only when ACTION=delete, write a complete dumpfile record + * terminated with blank lines. */ static svn_error_t * dump_node(struct dump_edit_baton *eb, @@ -371,6 +295,7 @@ dump_node(struct dump_edit_baton *eb, apr_pool_t *pool) { const char *node_relpath = repos_relpath; + apr_array_header_t *headers = svn_repos__dumpfile_headers_create(pool); assert(svn_relpath_is_canonical(repos_relpath)); assert(!copyfrom_path || svn_relpath_is_canonical(copyfrom_path)); @@ -382,17 +307,16 @@ dump_node(struct dump_edit_baton *eb, node_relpath, pool); /* Node-path: ... */ - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_NODE_PATH ": %s\n", - node_relpath)); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_PATH, node_relpath); /* Node-kind: "file" | "dir" */ if (fb) - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_NODE_KIND ": file\n")); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_KIND, "file"); else if (db) - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_NODE_KIND ": dir\n")); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_KIND, "dir"); /* Write the appropriate Node-action header */ @@ -404,26 +328,32 @@ dump_node(struct dump_edit_baton *eb, do here but print node action information. Node-action: change. */ - SVN_ERR(svn_stream_puts(eb->stream, - SVN_REPOS_DUMPFILE_NODE_ACTION ": change\n")); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "change"); break; case svn_node_action_replace: if (is_copy) { - /* Delete the original, and then re-add the replacement as a - copy using recursive calls into this function. */ - SVN_ERR(dump_node(eb, repos_relpath, db, fb, svn_node_action_delete, - FALSE, NULL, SVN_INVALID_REVNUM, pool)); + /* More complex case: is_copy is true, and copyfrom_path/ + copyfrom_rev are present: delete the original, and then re-add + it */ + + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "delete"); + + SVN_ERR(svn_repos__dump_headers(eb->stream, headers, TRUE, pool)); + + /* Recurse: Print an additional add-with-history record. */ SVN_ERR(dump_node(eb, repos_relpath, db, fb, svn_node_action_add, is_copy, copyfrom_path, copyfrom_rev, pool)); + return SVN_NO_ERROR; } else { /* Node-action: replace */ - SVN_ERR(svn_stream_puts(eb->stream, - SVN_REPOS_DUMPFILE_NODE_ACTION - ": replace\n")); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "replace"); /* Wait for a change_*_prop to be called before dumping anything */ @@ -436,30 +366,29 @@ dump_node(struct dump_edit_baton *eb, case svn_node_action_delete: /* Node-action: delete */ - SVN_ERR(svn_stream_puts(eb->stream, - SVN_REPOS_DUMPFILE_NODE_ACTION ": delete\n")); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "delete"); /* We can leave this routine quietly now. Nothing more to do- - print a couple of newlines because we're not dumping props or - text. */ - SVN_ERR(svn_stream_puts(eb->stream, "\n\n")); + print the headers terminated by one blank line, and an extra + blank line because we're not dumping props or text. */ + SVN_ERR(svn_repos__dump_headers(eb->stream, headers, TRUE, pool)); + SVN_ERR(svn_stream_puts(eb->stream, "\n")); - break; + return SVN_NO_ERROR; case svn_node_action_add: /* Node-action: add */ - SVN_ERR(svn_stream_puts(eb->stream, - SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n")); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "add"); if (is_copy) { /* Node-copyfrom-rev / Node-copyfrom-path */ - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV - ": %ld\n" - SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH - ": %s\n", - copyfrom_rev, copyfrom_path)); + svn_repos__dumpfile_header_pushf( + headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV, "%ld", copyfrom_rev); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH, copyfrom_path); /* Ugly hack: If a directory was copied from a previous revision, nothing like close_file() will be called to write two @@ -497,6 +426,11 @@ dump_node(struct dump_edit_baton *eb, break; } + + /* Write the headers so far. We don't necessarily have all the headers + yet -- there may be property-related and content length headers to + come -- so don't write a terminating blank line. */ + SVN_ERR(svn_repos__dump_headers(eb->stream, headers, FALSE, pool)); return SVN_NO_ERROR; } @@ -505,34 +439,30 @@ dump_mkdir(struct dump_edit_baton *eb, const char *repos_relpath, apr_pool_t *pool) { - svn_stringbuf_t *prop_header, *prop_content; + svn_stringbuf_t *prop_content; apr_size_t len; - const char *buf; + apr_array_header_t *headers = svn_repos__dumpfile_headers_create(pool); /* Node-path: ... */ - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_NODE_PATH ": %s\n", - repos_relpath)); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_PATH, repos_relpath); /* Node-kind: dir */ - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_NODE_KIND ": dir\n")); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_KIND, "dir"); /* Node-action: add */ - SVN_ERR(svn_stream_puts(eb->stream, - SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n")); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "add"); /* Dump the (empty) property block. */ - SVN_ERR(get_props_content(&prop_header, &prop_content, + SVN_ERR(get_props_content(headers, &prop_content, apr_hash_make(pool), apr_hash_make(pool), pool, pool)); - len = prop_header->len; - SVN_ERR(svn_stream_write(eb->stream, prop_header->data, &len)); len = prop_content->len; - buf = apr_psprintf(pool, SVN_REPOS_DUMPFILE_CONTENT_LENGTH - ": %" APR_SIZE_T_FMT "\n", len); - SVN_ERR(svn_stream_puts(eb->stream, buf)); - SVN_ERR(svn_stream_puts(eb->stream, "\n")); + svn_repos__dumpfile_header_pushf(headers, SVN_REPOS_DUMPFILE_CONTENT_LENGTH, + "%" APR_SIZE_T_FMT, len); + SVN_ERR(svn_repos__dump_headers(eb->stream, headers, TRUE, pool)); SVN_ERR(svn_stream_write(eb->stream, prop_content->data, &len)); /* Newlines to tie it all off. */ @@ -547,31 +477,79 @@ static svn_error_t * dump_pending(struct dump_edit_baton *eb, apr_pool_t *scratch_pool) { + svn_boolean_t dump_props = FALSE; + apr_hash_t *props, *deleted_props; + if (! eb->pending_baton) return SVN_NO_ERROR; + /* Some pending properties to dump? */ if (eb->pending_kind == svn_node_dir) { struct dir_baton *db = eb->pending_baton; - /* Some pending properties to dump? */ - SVN_ERR(do_dump_props(NULL, eb->stream, db->props, db->deleted_props, - &(db->dump_props), db->pool, scratch_pool)); + if (db->dump_props) + { + dump_props = TRUE; + props = db->props; + deleted_props = db->deleted_props; - /* Some pending newlines to dump? */ - SVN_ERR(do_dump_newlines(eb, &(db->dump_newlines), scratch_pool)); + db->dump_props = FALSE; + } } else if (eb->pending_kind == svn_node_file) { struct file_baton *fb = eb->pending_baton; - /* Some pending properties to dump? */ - SVN_ERR(do_dump_props(NULL, eb->stream, fb->props, fb->deleted_props, - &(fb->dump_props), fb->pool, scratch_pool)); + if (fb->dump_props) + { + dump_props = TRUE; + props = fb->props; + deleted_props = fb->deleted_props; + fb->dump_props = FALSE; + } } else abort(); + if (dump_props) + { + apr_array_header_t *headers + = svn_repos__dumpfile_headers_create(scratch_pool); + svn_stringbuf_t *content; + apr_size_t len; + + SVN_ERR(get_props_content(headers, &content, props, deleted_props, + scratch_pool, scratch_pool)); + + /* Content-length: 14 */ + svn_repos__dumpfile_header_pushf( + headers, SVN_REPOS_DUMPFILE_CONTENT_LENGTH, + "%" APR_SIZE_T_FMT, content->len); + + SVN_ERR(svn_repos__dump_headers(eb->stream, headers, TRUE, + scratch_pool)); + + len = content->len; + SVN_ERR(svn_stream_write(eb->stream, content->data, &len)); + + /* No text is going to be dumped. Write a couple of newlines and + wait for the next node/ revision. */ + SVN_ERR(svn_stream_puts(eb->stream, "\n\n")); + + /* Cleanup so that data is never dumped twice. */ + apr_hash_clear(props); + apr_hash_clear(deleted_props); + } + + if (eb->pending_kind == svn_node_dir) + { + struct dir_baton *db = eb->pending_baton; + + /* Some pending newlines to dump? */ + SVN_ERR(do_dump_newlines(eb, &(db->dump_newlines), scratch_pool)); + } + /* Anything that was pending is pending no longer. */ eb->pending_baton = NULL; eb->pending_kind = svn_node_none; @@ -629,14 +607,13 @@ open_root(void *edit_baton, /* ... but for the source directory itself, we'll defer to letting the typical plumbing handle this task. */ new_db = make_dir_baton(NULL, NULL, SVN_INVALID_REVNUM, - edit_baton, NULL, TRUE, pool); + edit_baton, NULL, pool); SVN_ERR(dump_node(eb, new_db->repos_relpath, new_db, NULL, svn_node_action_add, FALSE, NULL, SVN_INVALID_REVNUM, pool)); /* Remember that we've started but not yet finished handling this directory. */ - new_db->written_out = TRUE; eb->pending_baton = new_db; eb->pending_kind = svn_node_dir; } @@ -647,7 +624,7 @@ open_root(void *edit_baton, if (! new_db) { new_db = make_dir_baton(NULL, NULL, SVN_INVALID_REVNUM, - edit_baton, NULL, FALSE, pool); + edit_baton, NULL, pool); } *root_baton = new_db; @@ -684,7 +661,7 @@ add_directory(const char *path, void **child_baton) { struct dir_baton *pb = parent_baton; - void *val; + void *was_deleted; struct dir_baton *new_db; svn_boolean_t is_copy; @@ -693,29 +670,28 @@ add_directory(const char *path, SVN_ERR(dump_pending(pb->eb, pool)); new_db = make_dir_baton(path, copyfrom_path, copyfrom_rev, pb->eb, - pb, TRUE, pb->pool); + pb, pb->pool); /* This might be a replacement -- is the path already deleted? */ - val = svn_hash_gets(pb->deleted_entries, path); + was_deleted = svn_hash_gets(pb->deleted_entries, path); /* Detect an add-with-history */ is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev); /* Dump the node */ SVN_ERR(dump_node(pb->eb, new_db->repos_relpath, new_db, NULL, - val ? svn_node_action_replace : svn_node_action_add, + was_deleted ? svn_node_action_replace : svn_node_action_add, is_copy, is_copy ? new_db->copyfrom_path : NULL, is_copy ? copyfrom_rev : SVN_INVALID_REVNUM, pool)); - if (val) + if (was_deleted) /* Delete the path, it's now been dumped */ svn_hash_sets(pb->deleted_entries, path, NULL); /* Remember that we've started, but not yet finished handling this directory. */ - new_db->written_out = TRUE; pb->eb->pending_baton = new_db; pb->eb->pending_kind = svn_node_dir; @@ -750,7 +726,7 @@ open_directory(const char *path, } new_db = make_dir_baton(path, copyfrom_path, copyfrom_rev, pb->eb, pb, - FALSE, pb->pool); + pb->pool); *child_baton = new_db; return SVN_NO_ERROR; @@ -801,7 +777,7 @@ close_directory(void *dir_baton, } /* ### should be unnecessary */ - svn_pool_destroy(db->pool); + apr_hash_clear(db->deleted_entries); return SVN_NO_ERROR; } @@ -816,7 +792,7 @@ add_file(const char *path, { struct dir_baton *pb = parent_baton; struct file_baton *fb; - void *val; + void *was_deleted; LDR_DBG(("add_file %s\n", path)); @@ -826,7 +802,7 @@ add_file(const char *path, fb = make_file_baton(path, pb, pool); /* This might be a replacement -- is the path already deleted? */ - val = svn_hash_gets(pb->deleted_entries, path); + was_deleted = svn_hash_gets(pb->deleted_entries, path); /* Detect add-with-history. */ if (ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev)) @@ -835,10 +811,10 @@ add_file(const char *path, fb->copyfrom_rev = copyfrom_rev; fb->is_copy = TRUE; } - fb->action = val ? svn_node_action_replace : svn_node_action_add; + fb->action = was_deleted ? svn_node_action_replace : svn_node_action_add; /* Delete the path, it's now been dumped. */ - if (val) + if (was_deleted) svn_hash_sets(pb->deleted_entries, path, NULL); *file_baton = fb; @@ -975,6 +951,7 @@ close_file(void *file_baton, struct dump_edit_baton *eb = fb->eb; apr_finfo_t *info = apr_pcalloc(pool, sizeof(apr_finfo_t)); svn_stringbuf_t *propstring; + apr_array_header_t *headers = svn_repos__dumpfile_headers_create(pool); LDR_DBG(("close_file %p\n", file_baton)); @@ -988,8 +965,12 @@ close_file(void *file_baton, /* Some pending properties to dump? We'll dump just the headers for now, then dump the actual propchange content only after dumping the text headers too (if present). */ - SVN_ERR(do_dump_props(&propstring, eb->stream, fb->props, fb->deleted_props, - &(fb->dump_props), pool, pool)); + if (fb->dump_props) + { + SVN_ERR(get_props_content(headers, &propstring, + fb->props, fb->deleted_props, + pool, pool)); + } /* Dump the text headers */ if (fb->dump_text) @@ -997,9 +978,8 @@ close_file(void *file_baton, apr_status_t err; /* Text-delta: true */ - SVN_ERR(svn_stream_puts(eb->stream, - SVN_REPOS_DUMPFILE_TEXT_DELTA - ": true\n")); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_TEXT_DELTA, "true"); err = apr_file_info_get(info, APR_FINFO_SIZE, eb->delta_file); if (err) @@ -1007,36 +987,31 @@ close_file(void *file_baton, if (fb->base_checksum) /* Text-delta-base-md5: */ - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_MD5 - ": %s\n", - fb->base_checksum)); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_MD5, fb->base_checksum); /* Text-content-length: 39 */ - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_TEXT_CONTENT_LENGTH - ": %lu\n", - (unsigned long)info->size)); + svn_repos__dumpfile_header_pushf( + headers, SVN_REPOS_DUMPFILE_TEXT_CONTENT_LENGTH, + "%lu", (unsigned long)info->size); /* Text-content-md5: 82705804337e04dcd0e586bfa2389a7f */ - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_TEXT_CONTENT_MD5 - ": %s\n", - text_checksum)); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_TEXT_CONTENT_MD5, text_checksum); } /* Content-length: 1549 */ /* If both text and props are absent, skip this header */ if (fb->dump_props) - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_CONTENT_LENGTH - ": %ld\n\n", - (unsigned long)info->size + propstring->len)); + svn_repos__dumpfile_header_pushf( + headers, SVN_REPOS_DUMPFILE_CONTENT_LENGTH, + "%lu", (unsigned long)(info->size + propstring->len)); else if (fb->dump_text) - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_CONTENT_LENGTH - ": %ld\n\n", - (unsigned long)info->size)); + svn_repos__dumpfile_header_pushf( + headers, SVN_REPOS_DUMPFILE_CONTENT_LENGTH, + "%lu", (unsigned long)info->size); + + SVN_ERR(svn_repos__dump_headers(eb->stream, headers, TRUE, pool)); /* Dump the props now */ if (fb->dump_props) @@ -1074,8 +1049,6 @@ close_file(void *file_baton, dump` */ SVN_ERR(svn_stream_puts(eb->stream, "\n\n")); - svn_pool_clear(fb->pool); - return SVN_NO_ERROR; }
Modified: subversion/branches/pin-externals/subversion/svnrdump/load_editor.c URL: http://svn.apache.org/viewvc/subversion/branches/pin-externals/subversion/svnrdump/load_editor.c?rev=1653578&r1=1653577&r2=1653578&view=diff ============================================================================== --- subversion/branches/pin-externals/subversion/svnrdump/load_editor.c (original) +++ subversion/branches/pin-externals/subversion/svnrdump/load_editor.c Wed Jan 21 16:22:19 2015 @@ -66,9 +66,6 @@ struct parse_baton /* To bleep, or not to bleep? (What kind of question is that?) */ svn_boolean_t quiet; - /* UUID found in the dumpstream, if any; NULL otherwise. */ - const char *uuid; - /* Root URL of the target repository. */ const char *root_url; @@ -99,13 +96,12 @@ struct parse_baton /** * Use to wrap the dir_context_t in commit.c so we can keep track of - * depth, relpath and parent for open_directory and close_directory. + * relpath and parent for open_directory and close_directory. */ struct directory_baton { void *baton; const char *relpath; - int depth; struct directory_baton *parent; }; @@ -175,169 +171,6 @@ get_revision_mapping(apr_hash_t *rev_map } -/* Prepend the mergeinfo source paths in MERGEINFO_ORIG with - PARENT_DIR, and return it in *MERGEINFO_VAL. */ -/* ### FIXME: Consider somehow sharing code with - ### libsvn_repos/load-fs-vtable.c:prefix_mergeinfo_paths() */ -static svn_error_t * -prefix_mergeinfo_paths(svn_string_t **mergeinfo_val, - const svn_string_t *mergeinfo_orig, - const char *parent_dir, - apr_pool_t *pool) -{ - apr_hash_t *prefixed_mergeinfo, *mergeinfo; - apr_hash_index_t *hi; - void *rangelist; - - SVN_ERR(svn_mergeinfo_parse(&mergeinfo, mergeinfo_orig->data, pool)); - prefixed_mergeinfo = apr_hash_make(pool); - for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi)) - { - const void *key; - const char *path, *merge_source; - - apr_hash_this(hi, &key, NULL, &rangelist); - merge_source = svn_relpath_canonicalize(key, pool); - - /* The svn:mergeinfo property syntax demands a repos abspath */ - path = svn_fspath__canonicalize(svn_relpath_join(parent_dir, - merge_source, pool), - pool); - svn_hash_sets(prefixed_mergeinfo, path, rangelist); - } - return svn_mergeinfo_to_string(mergeinfo_val, prefixed_mergeinfo, pool); -} - - -/* Examine the mergeinfo in INITIAL_VAL, renumber revisions in rangelists - as appropriate, and return the (possibly new) mergeinfo in *FINAL_VAL - (allocated from POOL). */ -/* ### FIXME: Consider somehow sharing code with - ### libsvn_repos/load-fs-vtable.c:renumber_mergeinfo_revs() */ -static svn_error_t * -renumber_mergeinfo_revs(svn_string_t **final_val, - const svn_string_t *initial_val, - struct revision_baton *rb, - apr_pool_t *pool) -{ - apr_pool_t *subpool = svn_pool_create(pool); - svn_mergeinfo_t mergeinfo, predates_stream_mergeinfo; - svn_mergeinfo_t final_mergeinfo = apr_hash_make(subpool); - apr_hash_index_t *hi; - - SVN_ERR(svn_mergeinfo_parse(&mergeinfo, initial_val->data, subpool)); - - /* Issue #3020 - http://subversion.tigris.org/issues/show_bug.cgi?id=3020#desc16 - Remove mergeinfo older than the oldest revision in the dump stream - and adjust its revisions by the difference between the head rev of - the target repository and the current dump stream rev. */ - if (rb->pb->oldest_dumpstream_rev > 1) - { - SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges( - &predates_stream_mergeinfo, mergeinfo, - rb->pb->oldest_dumpstream_rev - 1, 0, - TRUE, subpool, subpool)); - SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges( - &mergeinfo, mergeinfo, - rb->pb->oldest_dumpstream_rev - 1, 0, - FALSE, subpool, subpool)); - SVN_ERR(svn_mergeinfo__adjust_mergeinfo_rangelists( - &predates_stream_mergeinfo, - predates_stream_mergeinfo, - -rb->rev_offset, subpool, subpool)); - } - else - { - predates_stream_mergeinfo = NULL; - } - - for (hi = apr_hash_first(subpool, mergeinfo); hi; hi = apr_hash_next(hi)) - { - svn_rangelist_t *rangelist; - struct parse_baton *pb = rb->pb; - int i; - const void *path; - apr_ssize_t pathlen; - void *val; - - apr_hash_this(hi, &path, &pathlen, &val); - rangelist = val; - - /* Possibly renumber revisions in merge source's rangelist. */ - for (i = 0; i < rangelist->nelts; i++) - { - svn_revnum_t rev_from_map; - svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, i, - svn_merge_range_t *); - rev_from_map = get_revision_mapping(pb->rev_map, range->start); - if (SVN_IS_VALID_REVNUM(rev_from_map)) - { - range->start = rev_from_map; - } - else if (range->start == pb->oldest_dumpstream_rev - 1) - { - /* Since the start revision of svn_merge_range_t are not - inclusive there is one possible valid start revision that - won't be found in the PB->REV_MAP mapping of load stream - revsions to loaded revisions: The revision immediately - preceding the oldest revision from the load stream. - This is a valid revision for mergeinfo, but not a valid - copy from revision (which PB->REV_MAP also maps for) so it - will never be in the mapping. - - If that is what we have here, then find the mapping for the - oldest rev from the load stream and subtract 1 to get the - renumbered, non-inclusive, start revision. */ - rev_from_map = get_revision_mapping(pb->rev_map, - pb->oldest_dumpstream_rev); - if (SVN_IS_VALID_REVNUM(rev_from_map)) - range->start = rev_from_map - 1; - } - else - { - /* If we can't remap the start revision then don't even bother - trying to remap the end revision. It's possible we might - actually succeed at the latter, which can result in invalid - mergeinfo with a start rev > end rev. If that gets into the - repository then a world of bustage breaks loose anytime that - bogus mergeinfo is parsed. See - http://subversion.tigris.org/issues/show_bug.cgi?id=3020#desc16. - */ - continue; - } - - rev_from_map = get_revision_mapping(pb->rev_map, range->end); - if (SVN_IS_VALID_REVNUM(rev_from_map)) - range->end = rev_from_map; - } - apr_hash_set(final_mergeinfo, path, pathlen, rangelist); - } - - if (predates_stream_mergeinfo) - { - SVN_ERR(svn_mergeinfo_merge2(final_mergeinfo, predates_stream_mergeinfo, - subpool, subpool)); - } - - SVN_ERR(svn_mergeinfo_sort(final_mergeinfo, subpool)); - - /* Mergeinfo revision sources for r0 and r1 are invalid; you can't merge r0 - or r1. However, svndumpfilter can be abused to produce r1 merge source - revs. So if we encounter any, then strip them out, no need to put them - into the load target. */ - SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(&final_mergeinfo, - final_mergeinfo, - 1, 0, FALSE, - subpool, subpool)); - - SVN_ERR(svn_mergeinfo_to_string(final_val, final_mergeinfo, pool)); - svn_pool_destroy(subpool); - - return SVN_NO_ERROR; -} - - static svn_error_t * commit_callback(const svn_commit_info_t *commit_info, void *baton, @@ -599,9 +432,6 @@ uuid_record(const char *uuid, void *parse_baton, apr_pool_t *pool) { - struct parse_baton *pb; - pb = parse_baton; - pb->uuid = apr_pstrdup(pool, uuid); return SVN_NO_ERROR; } @@ -661,7 +491,6 @@ new_node_record(void **node_baton, /* child_db corresponds to the root directory baton here */ child_db = apr_pcalloc(rb->pool, sizeof(*child_db)); child_db->baton = child_baton; - child_db->depth = 0; child_db->relpath = ""; child_db->parent = NULL; rb->db = child_db; @@ -745,7 +574,6 @@ new_node_record(void **node_baton, LDR_DBG(("Opened dir %p\n", child_baton)); child_db = apr_pcalloc(rb->pool, sizeof(*child_db)); child_db->baton = child_baton; - child_db->depth = rb->db->depth + 1; child_db->relpath = relpath_compose; child_db->parent = rb->db; rb->db = child_db; @@ -813,7 +641,6 @@ new_node_record(void **node_baton, nb->path, rb->db->baton, child_baton)); child_db = apr_pcalloc(rb->pool, sizeof(*child_db)); child_db->baton = child_baton; - child_db->depth = rb->db->depth + 1; child_db->relpath = apr_pstrdup(rb->pool, nb->path); child_db->parent = rb->db; rb->db = child_db; @@ -836,7 +663,6 @@ new_node_record(void **node_baton, rb->pool, &child_baton)); child_db = apr_pcalloc(rb->pool, sizeof(*child_db)); child_db->baton = child_baton; - child_db->depth = rb->db->depth + 1; child_db->relpath = apr_pstrdup(rb->pool, nb->path); child_db->parent = rb->db; rb->db = child_db; @@ -893,51 +719,30 @@ set_node_property(void *baton, const svn_string_t *value) { struct node_baton *nb = baton; + struct revision_baton *rb = nb->rb; + struct parse_baton *pb = rb->pb; const struct svn_delta_editor_t *commit_editor = nb->rb->pb->commit_editor; apr_pool_t *pool = nb->rb->pool; if (value && strcmp(name, SVN_PROP_MERGEINFO) == 0) { - svn_string_t *renumbered_mergeinfo; - svn_string_t prop_val; + svn_string_t *new_value; + svn_error_t *err; - /* Tolerate mergeinfo with "\r\n" line endings because some - dumpstream sources might contain as much. If so normalize - the line endings to '\n' and make a notification to - PARSE_BATON->FEEDBACK_STREAM that we have made this - correction. */ - if (strstr(value->data, "\r")) + err = svn_repos__adjust_mergeinfo_property(&new_value, value, + pb->parent_dir, + pb->rev_map, + pb->oldest_dumpstream_rev, + rb->rev_offset, + NULL, NULL, /*notify*/ + pool, pool); + if (err) { - const char *prop_eol_normalized; - - SVN_ERR(svn_subst_translate_cstring2(value->data, - &prop_eol_normalized, - "\n", /* translate to LF */ - FALSE, /* no repair */ - NULL, /* no keywords */ - FALSE, /* no expansion */ - pool)); - prop_val.data = prop_eol_normalized; - prop_val.len = strlen(prop_eol_normalized); - value = &prop_val; - - /* ### TODO: notify? */ + return svn_error_quick_wrap(err, + _("Invalid svn:mergeinfo value")); } - /* Renumber mergeinfo as appropriate. */ - SVN_ERR(renumber_mergeinfo_revs(&renumbered_mergeinfo, value, - nb->rb, pool)); - value = renumbered_mergeinfo; - - if (nb->rb->pb->parent_dir) - { - /* Prefix the merge source paths with PB->parent_dir. */ - /* ASSUMPTION: All source paths are included in the dump stream. */ - svn_string_t *mergeinfo_val; - SVN_ERR(prefix_mergeinfo_paths(&mergeinfo_val, value, - nb->rb->pb->parent_dir, pool)); - value = mergeinfo_val; - } + value = new_value; } SVN_ERR(svn_rdump__normalize_prop(name, &value, pool)); Modified: subversion/branches/pin-externals/subversion/svnrdump/svnrdump.c URL: http://svn.apache.org/viewvc/subversion/branches/pin-externals/subversion/svnrdump/svnrdump.c?rev=1653578&r1=1653577&r2=1653578&view=diff ============================================================================== --- subversion/branches/pin-externals/subversion/svnrdump/svnrdump.c (original) +++ subversion/branches/pin-externals/subversion/svnrdump/svnrdump.c Wed Jan 21 16:22:19 2015 @@ -39,6 +39,7 @@ #include "svnrdump.h" +#include "private/svn_repos_private.h" #include "private/svn_cmdline_private.h" #include "private/svn_ra_private.h" @@ -229,38 +230,13 @@ replay_revstart(svn_revnum_t revision, { struct replay_baton *rb = replay_baton; apr_hash_t *normal_props; - svn_stringbuf_t *propstring; - svn_stream_t *stdout_stream; - svn_stream_t *revprop_stream; - - SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool)); - /* Revision-number: 19 */ - SVN_ERR(svn_stream_printf(stdout_stream, pool, - SVN_REPOS_DUMPFILE_REVISION_NUMBER - ": %ld\n", revision)); + /* Normalize and dump the revprops */ SVN_ERR(svn_rdump__normalize_props(&normal_props, rev_props, pool)); - propstring = svn_stringbuf_create_ensure(0, pool); - revprop_stream = svn_stream_from_stringbuf(propstring, pool); - SVN_ERR(svn_hash_write2(normal_props, revprop_stream, "PROPS-END", pool)); - SVN_ERR(svn_stream_close(revprop_stream)); - - /* Prop-content-length: 13 */ - SVN_ERR(svn_stream_printf(stdout_stream, pool, - SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH - ": %" APR_SIZE_T_FMT "\n", propstring->len)); - - /* Content-length: 29 */ - SVN_ERR(svn_stream_printf(stdout_stream, pool, - SVN_REPOS_DUMPFILE_CONTENT_LENGTH - ": %" APR_SIZE_T_FMT "\n\n", propstring->len)); - - /* Property data. */ - SVN_ERR(svn_stream_write(stdout_stream, propstring->data, - &(propstring->len))); - - SVN_ERR(svn_stream_puts(stdout_stream, "\n")); - SVN_ERR(svn_stream_close(stdout_stream)); + SVN_ERR(svn_repos__dump_revision_record(rb->stdout_stream, revision, NULL, + normal_props, + TRUE /*props_section_always*/, + pool)); SVN_ERR(svn_rdump__get_dump_editor(editor, edit_baton, revision, rb->stdout_stream, rb->extra_ra_session, @@ -303,38 +279,13 @@ replay_revstart_v2(svn_revnum_t revision { struct replay_baton *rb = replay_baton; apr_hash_t *normal_props; - svn_stringbuf_t *propstring; - svn_stream_t *stdout_stream; - svn_stream_t *revprop_stream; - SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool)); - - /* Revision-number: 19 */ - SVN_ERR(svn_stream_printf(stdout_stream, pool, - SVN_REPOS_DUMPFILE_REVISION_NUMBER - ": %ld\n", revision)); + /* Normalize and dump the revprops */ SVN_ERR(svn_rdump__normalize_props(&normal_props, rev_props, pool)); - propstring = svn_stringbuf_create_ensure(0, pool); - revprop_stream = svn_stream_from_stringbuf(propstring, pool); - SVN_ERR(svn_hash_write2(normal_props, revprop_stream, "PROPS-END", pool)); - SVN_ERR(svn_stream_close(revprop_stream)); - - /* Prop-content-length: 13 */ - SVN_ERR(svn_stream_printf(stdout_stream, pool, - SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH - ": %" APR_SIZE_T_FMT "\n", propstring->len)); - - /* Content-length: 29 */ - SVN_ERR(svn_stream_printf(stdout_stream, pool, - SVN_REPOS_DUMPFILE_CONTENT_LENGTH - ": %" APR_SIZE_T_FMT "\n\n", propstring->len)); - - /* Property data. */ - SVN_ERR(svn_stream_write(stdout_stream, propstring->data, - &(propstring->len))); - - SVN_ERR(svn_stream_puts(stdout_stream, "\n")); - SVN_ERR(svn_stream_close(stdout_stream)); + SVN_ERR(svn_repos__dump_revision_record(rb->stdout_stream, revision, + normal_props, + TRUE /*props_section_always*/, + pool)); SVN_ERR(svn_rdump__get_dump_editor_v2(editor, revision, rb->stdout_stream, @@ -471,35 +422,12 @@ dump_revision_header(svn_ra_session_t *s apr_pool_t *pool) { apr_hash_t *prophash; - svn_stringbuf_t *propstring; - svn_stream_t *propstream; - SVN_ERR(svn_stream_printf(stdout_stream, pool, - SVN_REPOS_DUMPFILE_REVISION_NUMBER - ": %ld\n", revision)); - - prophash = apr_hash_make(pool); - propstring = svn_stringbuf_create_empty(pool); SVN_ERR(svn_ra_rev_proplist(session, revision, &prophash, pool)); - - propstream = svn_stream_from_stringbuf(propstring, pool); - SVN_ERR(svn_hash_write2(prophash, propstream, "PROPS-END", pool)); - SVN_ERR(svn_stream_close(propstream)); - - /* Property-content-length: 14; Content-length: 14 */ - SVN_ERR(svn_stream_printf(stdout_stream, pool, - SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH - ": %" APR_SIZE_T_FMT "\n", - propstring->len)); - SVN_ERR(svn_stream_printf(stdout_stream, pool, - SVN_REPOS_DUMPFILE_CONTENT_LENGTH - ": %" APR_SIZE_T_FMT "\n\n", - propstring->len)); - /* The properties */ - SVN_ERR(svn_stream_write(stdout_stream, propstring->data, - &(propstring->len))); - SVN_ERR(svn_stream_puts(stdout_stream, "\n")); - + SVN_ERR(svn_repos__dump_revision_record(stdout_stream, revision, NULL, + prophash, + TRUE /*props_section_always*/, + pool)); return SVN_NO_ERROR; } @@ -1176,7 +1104,7 @@ sub_main(int *exit_code, int argc, const if (strcmp(subcommand->name, "load") == 0) { - /* + /* * By default (no --*-interactive options given), the 'load' subcommand * is interactive unless username and password were provided on the * command line. This allows prompting for auth creds to work without Modified: subversion/branches/pin-externals/subversion/svnserve/logger.c URL: http://svn.apache.org/viewvc/subversion/branches/pin-externals/subversion/svnserve/logger.c?rev=1653578&r1=1653577&r2=1653578&view=diff ============================================================================== --- subversion/branches/pin-externals/subversion/svnserve/logger.c (original) +++ subversion/branches/pin-externals/subversion/svnserve/logger.c Wed Jan 21 16:22:19 2015 @@ -134,7 +134,10 @@ logger__log_error(logger_t *logger, if (len > sizeof(errstr) - sizeof(APR_EOL_STR)) { len = sizeof(errstr) - sizeof(APR_EOL_STR); } + memcpy(errstr + len, APR_EOL_STR, sizeof(APR_EOL_STR)); + len += sizeof(APR_EOL_STR) -1; /* add NL, ex terminating NUL */ + svn_error_clear(svn_stream_write(logger->stream, errstr, &len)); continuation = "-"; Modified: subversion/branches/pin-externals/subversion/svnserve/svnserve.c URL: http://svn.apache.org/viewvc/subversion/branches/pin-externals/subversion/svnserve/svnserve.c?rev=1653578&r1=1653577&r2=1653578&view=diff ============================================================================== --- subversion/branches/pin-externals/subversion/svnserve/svnserve.c (original) +++ subversion/branches/pin-externals/subversion/svnserve/svnserve.c Wed Jan 21 16:22:19 2015 @@ -271,6 +271,8 @@ static const apr_getopt_option_t svnserv " " "Default is 16.\n" " " + "0 switches to dynamically sized caches.\n" + " " "[used for FSFS and FSX repositories only]")}, {"cache-txdeltas", SVNSERVE_OPT_CACHE_TXDELTAS, 1, N_("enable or disable caching of deltas between older\n" Modified: subversion/branches/pin-externals/subversion/svnsync/sync.c URL: http://svn.apache.org/viewvc/subversion/branches/pin-externals/subversion/svnsync/sync.c?rev=1653578&r1=1653577&r2=1653578&view=diff ============================================================================== --- subversion/branches/pin-externals/subversion/svnsync/sync.c (original) +++ subversion/branches/pin-externals/subversion/svnsync/sync.c Wed Jan 21 16:22:19 2015 @@ -34,6 +34,8 @@ #include "svn_subst.h" #include "svn_string.h" +#include "private/svn_string_private.h" + #include "sync.h" #include "svn_private_config.h" @@ -83,6 +85,90 @@ normalize_string(const svn_string_t **st return SVN_NO_ERROR; } +/* Remove r0 references from the mergeinfo string *STR. + * + * r0 was never a valid mergeinfo reference and cannot be committed with + * recent servers, but can be committed through a server older than 1.6.18 + * for HTTP or older than 1.6.17 for the other protocols. See issue #4476 + * "Mergeinfo containing r0 makes svnsync and dump and load fail". + * + * Set *WAS_CHANGED to TRUE if *STR was changed, otherwise to FALSE. + */ +static svn_error_t * +remove_r0_mergeinfo(const svn_string_t **str, + svn_boolean_t *was_changed, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_stringbuf_t *new_str = svn_stringbuf_create_empty(result_pool); + apr_array_header_t *lines; + int i; + + SVN_ERR_ASSERT(*str && (*str)->data); + + *was_changed = FALSE; + + /* for each line */ + lines = svn_cstring_split((*str)->data, "\n", FALSE, scratch_pool); + + for (i = 0; i < lines->nelts; i++) + { + char *line = APR_ARRAY_IDX(lines, i, char *); + char *colon; + + /* split at the last colon */ + colon = strrchr(line, ':'); + + if (! colon) + return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, + _("Missing colon in svn:mergeinfo " + "property")); + + /* remove r0 */ + if (colon[1] == '0') + { + char *rangelist; + + rangelist = colon + 1; + + if (strncmp(rangelist, "0*,", 3) == 0) + { + rangelist += 3; + } + else if (strcmp(rangelist, "0*") == 0 + || strncmp(rangelist, "0,", 2) == 0 + || strncmp(rangelist, "0-1*", 4) == 0 + || strncmp(rangelist, "0-1,", 4) == 0 + || strcmp(rangelist, "0-1") == 0) + { + rangelist += 2; + } + else if (strcmp(rangelist, "0") == 0) + { + rangelist += 1; + } + else if (strncmp(rangelist, "0-", 2) == 0) + { + rangelist[0] = '1'; + } + + /* reassemble */ + if (new_str->len) + svn_stringbuf_appendbyte(new_str, '\n'); + svn_stringbuf_appendbytes(new_str, line, colon + 1 - line); + svn_stringbuf_appendcstr(new_str, rangelist); + } + } + + if (strcmp((*str)->data, new_str->data) != 0) + { + *was_changed = TRUE; + } + + *str = svn_stringbuf__morph_into_string(new_str); + return SVN_NO_ERROR; +} + /* Normalize the encoding and line ending style of the values of properties * in REV_PROPS that "need translation" (according to @@ -153,6 +239,7 @@ typedef struct edit_baton_t { svn_boolean_t got_textdeltas; svn_revnum_t base_revision; svn_boolean_t quiet; + svn_boolean_t mergeinfo_tweaked; /* Did we tweak svn:mergeinfo? */ svn_boolean_t strip_mergeinfo; /* Are we stripping svn:mergeinfo? */ svn_boolean_t migrate_svnmerge; /* Are we converting svnmerge.py data? */ svn_boolean_t mergeinfo_stripped; /* Did we strip svn:mergeinfo? */ @@ -414,8 +501,19 @@ change_file_prop(void *file_baton, if (svn_prop_needs_translation(name)) { svn_boolean_t was_normalized; + svn_boolean_t mergeinfo_tweaked = FALSE; + + /* Normalize encoding to UTF-8, and EOL style to LF. */ SVN_ERR(normalize_string(&value, &was_normalized, eb->source_prop_encoding, pool, pool)); + /* Correct malformed mergeinfo. */ + if (strcmp(name, SVN_PROP_MERGEINFO) == 0) + { + SVN_ERR(remove_r0_mergeinfo(&value, &mergeinfo_tweaked, + pool, pool)); + if (mergeinfo_tweaked) + eb->mergeinfo_tweaked = TRUE; + } if (was_normalized) (*(eb->normalized_node_props_counter))++; } @@ -513,8 +611,19 @@ change_dir_prop(void *dir_baton, if (svn_prop_needs_translation(name)) { svn_boolean_t was_normalized; + svn_boolean_t mergeinfo_tweaked = FALSE; + + /* Normalize encoding to UTF-8, and EOL style to LF. */ SVN_ERR(normalize_string(&value, &was_normalized, eb->source_prop_encoding, pool, pool)); + /* Maybe adjust svn:mergeinfo. */ + if (strcmp(name, SVN_PROP_MERGEINFO) == 0) + { + SVN_ERR(remove_r0_mergeinfo(&value, &mergeinfo_tweaked, + pool, pool)); + if (mergeinfo_tweaked) + eb->mergeinfo_tweaked = TRUE; + } if (was_normalized) (*(eb->normalized_node_props_counter))++; } @@ -548,6 +657,10 @@ close_edit(void *edit_baton, { if (eb->got_textdeltas) SVN_ERR(svn_cmdline_printf(pool, "\n")); + if (eb->mergeinfo_tweaked) + SVN_ERR(svn_cmdline_printf(pool, + "NOTE: Adjusted Subversion mergeinfo in " + "this revision.\n")); if (eb->mergeinfo_stripped) SVN_ERR(svn_cmdline_printf(pool, "NOTE: Dropped Subversion mergeinfo " Modified: subversion/branches/pin-externals/subversion/tests/cmdline/basic_tests.py URL: http://svn.apache.org/viewvc/subversion/branches/pin-externals/subversion/tests/cmdline/basic_tests.py?rev=1653578&r1=1653577&r2=1653578&view=diff ============================================================================== --- subversion/branches/pin-externals/subversion/tests/cmdline/basic_tests.py (original) +++ subversion/branches/pin-externals/subversion/tests/cmdline/basic_tests.py Wed Jan 21 16:22:19 2015 @@ -3146,6 +3146,31 @@ def basic_youngest(sbox): 'youngest', path) +# With 'svn mkdir --parents' the target directory may already exist on disk. +# In that case it was wrongly performing a recursive 'add' on its contents. +def mkdir_parents_target_exists_on_disk(sbox): + "mkdir parents target exists on disk" + + sbox.build() + wc_dir = sbox.wc_dir + + Y_path = sbox.ospath('Y') + Y_Z_path = sbox.ospath('Y/Z') + + os.mkdir(Y_path) + os.mkdir(Y_Z_path) + svntest.actions.run_and_verify_svn(None, None, [], + 'mkdir', '--parents', Y_path) + + # Y should be added, and Z should not. There was a regression in which Z + # was also added. + expected_status = svntest.actions.get_virginal_state(sbox.wc_dir, 1) + expected_status.add({ + 'Y' : Item(status='A ', wc_rev=0), + }) + svntest.actions.run_and_verify_status(wc_dir, expected_status) + + ######################################################################## # Run the tests @@ -3215,6 +3240,7 @@ test_list = [ None, delete_conflicts_one_of_many, peg_rev_on_non_existent_wc_path, basic_youngest, + mkdir_parents_target_exists_on_disk, ] if __name__ == '__main__': Modified: subversion/branches/pin-externals/subversion/tests/cmdline/copy_tests.py URL: http://svn.apache.org/viewvc/subversion/branches/pin-externals/subversion/tests/cmdline/copy_tests.py?rev=1653578&r1=1653577&r2=1653578&view=diff ============================================================================== --- subversion/branches/pin-externals/subversion/tests/cmdline/copy_tests.py (original) +++ subversion/branches/pin-externals/subversion/tests/cmdline/copy_tests.py Wed Jan 21 16:22:19 2015 @@ -560,7 +560,7 @@ def no_copy_overwrites(sbox): # Repeat the last command. It should *fail* because A/D/H/G already exists. svntest.actions.run_and_verify_svn( "Whoa, I was able to overwrite a directory!", - None, svntest.verify.AnyOutput, + None, ".*'/A/D/H/G'.*", 'cp', dirURL1, dirURL2, '-m', 'fooogle') Modified: subversion/branches/pin-externals/subversion/tests/cmdline/externals_tests.py URL: http://svn.apache.org/viewvc/subversion/branches/pin-externals/subversion/tests/cmdline/externals_tests.py?rev=1653578&r1=1653577&r2=1653578&view=diff ============================================================================== --- subversion/branches/pin-externals/subversion/tests/cmdline/externals_tests.py (original) +++ subversion/branches/pin-externals/subversion/tests/cmdline/externals_tests.py Wed Jan 21 16:22:19 2015 @@ -3479,6 +3479,74 @@ def switch_relative_externals(sbox): svntest.actions.run_and_verify_svn(None, None, [], 'up', wc) + +def copy_file_external_to_repo(sbox): + "explicitly copy file external to repo" + + sbox.build() + wc_dir = sbox.wc_dir + + change_external(sbox.ospath('A'), '^/A/mu ext') + sbox.simple_update() + + svntest.actions.run_and_verify_svn(None, None, [], 'cp', + '--message', 'external copy', + sbox.ospath('A/ext'), + sbox.repo_url + '/ext_copy') + + expected_output = svntest.wc.State(wc_dir, { + 'ext_copy' : Item(status='A '), + }) + expected_disk = svntest.main.greek_state.copy() + expected_disk.add({ + 'A/ext' : Item('This is the file \'mu\'.\n'), + 'ext_copy' : Item('This is the file \'mu\'.\n'), + }) + svntest.actions.run_and_verify_update(wc_dir, + expected_output, expected_disk, None) + +@Issue(4550) +def replace_tree_with_foreign_external(sbox): + "replace tree with foreign external" + + sbox.build() + wc_dir = sbox.wc_dir + repo_dir = sbox.repo_dir + + other_repo_dir, other_repo_url = sbox.add_repo_path('other') + svntest.main.copy_repos(repo_dir, other_repo_dir, 1) + + sbox.simple_propset('svn:externals', other_repo_url + '/A/B X', 'A') + sbox.simple_commit() + sbox.simple_propdel('svn:externals', 'A') + sbox.simple_mkdir('A/X') + sbox.simple_mkdir('A/X/E') + sbox.simple_commit() + sbox.simple_update() + + expected_output = svntest.wc.State(wc_dir, { + 'A/X' : Item(status='D '), + 'A' : Item(status=' U'), + 'A/X/lambda' : Item(status='A '), + 'A/X/E' : Item(status='A '), + 'A/X/E/alpha' : Item(status='A '), + 'A/X/E/beta' : Item(status='A '), + 'A/X/F' : Item(status='A '), + }) + expected_status = svntest.actions.get_virginal_state(wc_dir, 2) + expected_status.add({ + 'A/X' : Item(status=' ', wc_rev=1, prev_status='X '), + 'A/X/E' : Item(status=' ', wc_rev=1, prev_status=' '), + 'A/X/E/alpha' : Item(status=' ', wc_rev=1), + 'A/X/E/beta' : Item(status=' ', wc_rev=1), + 'A/X/F' : Item(status=' ', wc_rev=1), + 'A/X/lambda' : Item(status=' ', wc_rev=1), + }) + svntest.actions.run_and_verify_update(wc_dir, + expected_output, None, expected_status, + None, None, None, None, None, 1, + '-r', '2', wc_dir) + def pin_externals(sbox): "test svn copy --pin-externals" @@ -3564,6 +3632,8 @@ test_list = [ None, update_external_peg_rev, update_deletes_file_external, switch_relative_externals, + copy_file_external_to_repo, + replace_tree_with_foreign_external, pin_externals, ]
