svn_error_t * svn_wc_copy3(svn_wc_context_t *wc_ctx, const char *src_abspath, const char *dst_abspath, svn_boolean_t metadata_only, svn_cancel_func_t cancel_func, void *cancel_baton, svn_wc_notify_func2_t notify_func, void *notify_baton, apr_pool_t *scratch_pool) { /* Verify that we have the required write lock. */ SVN_ERR(svn_wc__write_check(wc_ctx->db, svn_dirent_dirname(dst_abspath, scratch_pool), scratch_pool)); return svn_error_trace(copy_or_move(NULL, wc_ctx, src_abspath, dst_abspath, metadata_only, FALSE /* is_move */, TRUE /* allow_mixed_revisions */, cancel_func, cancel_baton, notify_func, notify_baton, scratch_pool)); }
svn_error_t * svn_fs_apply_textdelta(svn_txdelta_window_handler_t *contents_p, void **contents_baton_p, svn_fs_root_t *root, const char *path, const char *base_checksum, const char *result_checksum, apr_pool_t *pool) { svn_checksum_t *base, *result; /* TODO: If we ever rev this API, we should make the supplied checksums svn_checksum_t structs. */ SVN_ERR(svn_checksum_parse_hex(&base, svn_checksum_md5, base_checksum, pool)); SVN_ERR(svn_checksum_parse_hex(&result, svn_checksum_md5, result_checksum, pool)); return svn_error_trace(root->vtable->apply_textdelta(contents_p, contents_baton_p, root, path, base, result, pool)); }
svn_error_t * svn_cl__time_cstring_to_human_cstring(const char **human_cstring, const char *data, apr_pool_t *pool) { svn_error_t *err; apr_time_t when; err = svn_time_from_cstring(&when, data, pool); if (err && err->apr_err == SVN_ERR_BAD_DATE) { svn_error_clear(err); *human_cstring = _("(invalid date)"); return SVN_NO_ERROR; } else if (err) return svn_error_trace(err); *human_cstring = svn_time_to_human_cstring(when, pool); return SVN_NO_ERROR; }
svn_error_t * svn_wc__db_pristine_get_sha1(const svn_checksum_t **sha1_checksum, svn_wc__db_t *db, const char *wri_abspath, const svn_checksum_t *md5_checksum, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { svn_wc__db_wcroot_t *wcroot; const char *local_relpath; svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath)); SVN_ERR_ASSERT(sha1_checksum != NULL); SVN_ERR_ASSERT(md5_checksum->kind == svn_checksum_md5); SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db, wri_abspath, scratch_pool, scratch_pool)); VERIFY_USABLE_WCROOT(wcroot); SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_PRISTINE_BY_MD5)); SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, md5_checksum, scratch_pool)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); if (!have_row) return svn_error_createf(SVN_ERR_WC_DB_ERROR, svn_sqlite__reset(stmt), _("The pristine text with MD5 checksum '%s' was " "not found"), svn_checksum_to_cstring_display(md5_checksum, scratch_pool)); SVN_ERR(svn_sqlite__column_checksum(sha1_checksum, stmt, 0, result_pool)); SVN_ERR_ASSERT((*sha1_checksum)->kind == svn_checksum_sha1); return svn_error_trace(svn_sqlite__reset(stmt)); }
svn_error_t * svn_client__get_inheritable_props(apr_hash_t **wcroot_iprops, const char *local_abspath, svn_revnum_t revision, svn_depth_t depth, svn_ra_session_t *ra_session, svn_client_ctx_t *ctx, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { const char *old_session_url; svn_error_t *err; if (!SVN_IS_VALID_REVNUM(revision)) return SVN_NO_ERROR; if (ra_session) SVN_ERR(svn_ra_get_session_url(ra_session, &old_session_url, scratch_pool)); /* We just wrap a simple helper function, as it is to easy to leave the ra session rooted at some wrong path without a wrapper like this. During development we had problems where some now deleted switched path made the update try to update to that url instead of the intended url */ err = get_inheritable_props(wcroot_iprops, local_abspath, revision, depth, ra_session, ctx, result_pool, scratch_pool); if (ra_session) { err = svn_error_compose_create( err, svn_ra_reparent(ra_session, old_session_url, scratch_pool)); } return svn_error_trace(err); }
/* (Note: All arguments are in the baton above.) Attempt to revert LOCAL_ABSPATH. If DEPTH is svn_depth_empty, revert just the properties on the directory; else if svn_depth_files, revert the properties and any files immediately under the directory; else if svn_depth_immediates, revert all of the preceding plus properties on immediate subdirectories; else if svn_depth_infinity, revert path and everything under it fully recursively. CHANGELISTS is an array of const char * changelist names, used as a restrictive filter on items reverted; that is, don't revert any item unless it's a member of one of those changelists. If CHANGELISTS is empty (or altogether NULL), no changelist filtering occurs. Consult CTX to determine whether or not to revert timestamp to the time of last commit ('use-commit-times = yes'). If PATH is unversioned, return SVN_ERR_UNVERSIONED_RESOURCE. */ static svn_error_t * revert(void *baton, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { struct revert_with_write_lock_baton *b = baton; svn_error_t *err; err = svn_wc_revert4(b->ctx->wc_ctx, b->local_abspath, b->depth, b->use_commit_times, b->changelists, b->ctx->cancel_func, b->ctx->cancel_baton, b->ctx->notify_func2, b->ctx->notify_baton2, scratch_pool); if (err) { /* If target isn't versioned, just send a 'skip' notification and move on. */ if (err->apr_err == SVN_ERR_ENTRY_NOT_FOUND || err->apr_err == SVN_ERR_UNVERSIONED_RESOURCE || err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) { if (b->ctx->notify_func2) (*b->ctx->notify_func2)( b->ctx->notify_baton2, svn_wc_create_notify(b->local_abspath, svn_wc_notify_skip, scratch_pool), scratch_pool); svn_error_clear(err); } else return svn_error_trace(err); } return SVN_NO_ERROR; }
svn_error_t * svn_subst_translate_stream3(svn_stream_t *src_stream, svn_stream_t *dst_stream, const char *eol_str, svn_boolean_t repair, apr_hash_t *keywords, svn_boolean_t expand, apr_pool_t *pool) { /* The docstring requires that *some* translation be requested. */ SVN_ERR_ASSERT(eol_str || keywords); /* We don't want the copy3 to close the provided streams. */ src_stream = svn_stream_disown(src_stream, pool); dst_stream = svn_stream_disown(dst_stream, pool); /* Wrap the destination stream with our translation stream. It is more efficient than wrapping the source stream. */ dst_stream = svn_subst_stream_translated(dst_stream, eol_str, repair, keywords, expand, pool); return svn_error_trace(svn_stream_copy3(src_stream, dst_stream, NULL, NULL, pool)); }
/* An implementation of svn_wc__proplist_receiver_t. */ static svn_error_t * recursive_proplist_receiver(void *baton, const char *local_abspath, apr_hash_t *props, apr_pool_t *scratch_pool) { struct recursive_proplist_receiver_baton *b = baton; const char *path; /* Attempt to convert absolute paths to relative paths for * presentation purposes, if needed. */ if (b->anchor && b->anchor_abspath) { path = svn_dirent_join(b->anchor, svn_dirent_skip_ancestor(b->anchor_abspath, local_abspath), scratch_pool); } else path = local_abspath; return svn_error_trace(b->wrapped_receiver(b->wrapped_receiver_baton, path, props, scratch_pool)); }
/* This implements svn_editor_cb_abort_t */ static svn_error_t * abort_cb(void *baton, apr_pool_t *scratch_pool) { struct edit_baton *eb = baton; svn_error_t *err; /* Don't allow a following call to svn_fs_editor_commit(). */ eb->completed = TRUE; if (eb->root != NULL) { svn_fs_close_root(eb->root); eb->root = NULL; } /* ### should we examine the error and attempt svn_fs_purge_txn() ? */ err = svn_fs_abort_txn(eb->txn, scratch_pool); /* For safety, clear the now-useless txn. */ eb->txn = NULL; return svn_error_trace(err); }
/* Like svn_io_dir_file_copy(), but doesn't copy files that exist at * the destination and do not differ in terms of kind, size, and mtime. * Set *SKIPPED_P to FALSE only if the file was copied, do not change * the value in *SKIPPED_P otherwise. SKIPPED_P may be NULL if not * required. */ static svn_error_t * hotcopy_io_dir_file_copy(svn_boolean_t *skipped_p, const char *src_path, const char *dst_path, const char *file, apr_pool_t *scratch_pool) { const svn_io_dirent2_t *src_dirent; const svn_io_dirent2_t *dst_dirent; const char *src_target; const char *dst_target; /* Does the destination already exist? If not, we must copy it. */ dst_target = svn_dirent_join(dst_path, file, scratch_pool); SVN_ERR(svn_io_stat_dirent2(&dst_dirent, dst_target, FALSE, TRUE, scratch_pool, scratch_pool)); if (dst_dirent->kind != svn_node_none) { /* If the destination's stat information indicates that the file * is equal to the source, don't bother copying the file again. */ src_target = svn_dirent_join(src_path, file, scratch_pool); SVN_ERR(svn_io_stat_dirent2(&src_dirent, src_target, FALSE, FALSE, scratch_pool, scratch_pool)); if (src_dirent->kind == dst_dirent->kind && src_dirent->special == dst_dirent->special && src_dirent->filesize == dst_dirent->filesize && src_dirent->mtime <= dst_dirent->mtime) return SVN_NO_ERROR; } if (skipped_p) *skipped_p = FALSE; return svn_error_trace(svn_io_dir_file_copy(src_path, dst_path, file, scratch_pool)); }
static svn_error_t * fetch_base_func(const char **filename, void *baton, const char *path, svn_revnum_t base_revision, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { struct revision_baton *rb = baton; svn_stream_t *fstream; svn_error_t *err; if (! SVN_IS_VALID_REVNUM(base_revision)) base_revision = rb->rev - 1; SVN_ERR(svn_stream_open_unique(&fstream, filename, NULL, svn_io_file_del_on_pool_cleanup, result_pool, scratch_pool)); err = svn_ra_get_file(rb->pb->aux_session, path, base_revision, fstream, NULL, NULL, scratch_pool); if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND) { svn_error_clear(err); SVN_ERR(svn_stream_close(fstream)); *filename = NULL; return SVN_NO_ERROR; } else if (err) return svn_error_trace(err); SVN_ERR(svn_stream_close(fstream)); return SVN_NO_ERROR; }
static svn_error_t * base_hotcopy(svn_fs_t *src_fs, svn_fs_t *dst_fs, const char *src_path, const char *dest_path, svn_boolean_t clean_logs, svn_boolean_t incremental, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *pool) { svn_error_t *err; u_int32_t pagesize; svn_boolean_t log_autoremove = FALSE; int format; if (incremental) return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, _("BDB repositories do not support incremental " "hotcopy")); /* Check the FS format number to be certain that we know how to hotcopy this FS. Pre-1.2 filesystems did not have a format file (you could say they were format "0"), so we will error here. This is not optimal, but since this has been the case since 1.2.0, and no one has complained, it apparently isn't much of a concern. (We did not check the 'format' file in 1.2.x, but we did blindly try to copy 'locks', which would have errored just the same.) */ SVN_ERR(svn_io_read_version_file( &format, svn_dirent_join(src_path, FORMAT_FILE, pool), pool)); SVN_ERR(check_format(format)); /* If using Berkeley DB 4.2 or later, note whether the DB_LOG_AUTO_REMOVE feature is on. If it is, we have a potential race condition: another process might delete a logfile while we're in the middle of copying all the logfiles. (This is not a huge deal; at worst, the hotcopy fails with a file-not-found error.) */ #if SVN_BDB_VERSION_AT_LEAST(4, 2) err = check_env_flags(&log_autoremove, #if SVN_BDB_VERSION_AT_LEAST(4, 7) DB_LOG_AUTO_REMOVE, /* DB_LOG_AUTO_REMOVE was named DB_LOG_AUTOREMOVE before Berkeley DB 4.7. */ #else DB_LOG_AUTOREMOVE, #endif src_path, pool); #endif SVN_ERR(err); /* Copy the DB_CONFIG file. */ SVN_ERR(svn_io_dir_file_copy(src_path, dest_path, "DB_CONFIG", pool)); /* In order to copy the database files safely and atomically, we must copy them in chunks which are multiples of the page-size used by BDB. See sleepycat docs for details, or svn issue #1818. */ #if SVN_BDB_VERSION_AT_LEAST(4, 2) SVN_ERR(get_db_pagesize(&pagesize, src_path, pool)); if (pagesize < SVN__STREAM_CHUNK_SIZE) { /* use the largest multiple of BDB pagesize we can. */ int multiple = SVN__STREAM_CHUNK_SIZE / pagesize; pagesize *= multiple; } #else /* default to 128K chunks, which should be safe. BDB almost certainly uses a power-of-2 pagesize. */ pagesize = (4096 * 32); #endif /* Copy the databases. */ SVN_ERR(copy_db_file_safely(src_path, dest_path, "nodes", pagesize, FALSE, pool)); SVN_ERR(copy_db_file_safely(src_path, dest_path, "transactions", pagesize, FALSE, pool)); SVN_ERR(copy_db_file_safely(src_path, dest_path, "revisions", pagesize, FALSE, pool)); SVN_ERR(copy_db_file_safely(src_path, dest_path, "copies", pagesize, FALSE, pool)); SVN_ERR(copy_db_file_safely(src_path, dest_path, "changes", pagesize, FALSE, pool)); SVN_ERR(copy_db_file_safely(src_path, dest_path, "representations", pagesize, FALSE, pool)); SVN_ERR(copy_db_file_safely(src_path, dest_path, "strings", pagesize, FALSE, pool)); SVN_ERR(copy_db_file_safely(src_path, dest_path, "uuids", pagesize, TRUE, pool)); SVN_ERR(copy_db_file_safely(src_path, dest_path, "locks", pagesize, TRUE, pool)); SVN_ERR(copy_db_file_safely(src_path, dest_path, "lock-tokens", pagesize, TRUE, pool)); SVN_ERR(copy_db_file_safely(src_path, dest_path, "node-origins", pagesize, TRUE, pool)); SVN_ERR(copy_db_file_safely(src_path, dest_path, "checksum-reps", pagesize, TRUE, pool)); SVN_ERR(copy_db_file_safely(src_path, dest_path, "miscellaneous", pagesize, TRUE, pool)); { apr_array_header_t *logfiles; int idx; apr_pool_t *subpool; SVN_ERR(base_bdb_logfiles(&logfiles, src_path, FALSE, /* All logs */ pool)); /* Process log files. */ subpool = svn_pool_create(pool); for (idx = 0; idx < logfiles->nelts; idx++) { svn_pool_clear(subpool); err = svn_io_dir_file_copy(src_path, dest_path, APR_ARRAY_IDX(logfiles, idx, const char *), subpool); if (err) { if (log_autoremove) return svn_error_quick_wrap (err, _("Error copying logfile; the DB_LOG_AUTOREMOVE feature\n" "may be interfering with the hotcopy algorithm. If\n" "the problem persists, try deactivating this feature\n" "in DB_CONFIG")); else return svn_error_trace(err); } } svn_pool_destroy(subpool); } /* Since this is a copy we will have exclusive access to the repository. */ err = bdb_recover(dest_path, TRUE, pool); if (err) { if (log_autoremove) return svn_error_quick_wrap (err, _("Error running catastrophic recovery on hotcopy; the\n" "DB_LOG_AUTOREMOVE feature may be interfering with the\n" "hotcopy algorithm. If the problem persists, try deactivating\n" "this feature in DB_CONFIG")); else return svn_error_trace(err); } /* Only now that the hotcopied filesystem is complete, stamp it with a format file. */ SVN_ERR(svn_io_write_version_file( svn_dirent_join(dest_path, FORMAT_FILE, pool), format, pool)); if (clean_logs) SVN_ERR(svn_fs_base__clean_logs(src_path, dest_path, pool)); return SVN_NO_ERROR; }
/* Called when an external that is in the EXTERNALS table is no longer referenced from an svn:externals property */ static svn_error_t * handle_external_item_removal(const svn_client_ctx_t *ctx, const char *defining_abspath, const char *local_abspath, apr_pool_t *scratch_pool) { svn_error_t *err; svn_node_kind_t external_kind; svn_node_kind_t kind; svn_boolean_t removed = FALSE; /* local_abspath should be a wcroot or a file external */ SVN_ERR(svn_wc__read_external_info(&external_kind, NULL, NULL, NULL, NULL, ctx->wc_ctx, defining_abspath, local_abspath, FALSE, scratch_pool, scratch_pool)); SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, local_abspath, TRUE, FALSE, scratch_pool)); if (external_kind != kind) external_kind = svn_node_none; /* Only remove the registration */ err = remove_external(&removed, ctx->wc_ctx, defining_abspath, local_abspath, external_kind, ctx->cancel_func, ctx->cancel_baton, scratch_pool); if (err && err->apr_err == SVN_ERR_WC_NOT_LOCKED && removed) { svn_error_clear(err); err = NULL; /* We removed the working copy, so we can't release the lock that was stored inside */ } if (ctx->notify_func2) { svn_wc_notify_t *notify = svn_wc_create_notify(local_abspath, svn_wc_notify_update_external_removed, scratch_pool); notify->kind = kind; notify->err = err; (ctx->notify_func2)(ctx->notify_baton2, notify, scratch_pool); if (err && err->apr_err == SVN_ERR_WC_LEFT_LOCAL_MOD) { notify = svn_wc_create_notify(local_abspath, svn_wc_notify_left_local_modifications, scratch_pool); notify->kind = svn_node_dir; notify->err = err; (ctx->notify_func2)(ctx->notify_baton2, notify, scratch_pool); } } if (err && err->apr_err == SVN_ERR_WC_LEFT_LOCAL_MOD) { svn_error_clear(err); err = NULL; } return svn_error_trace(err); }
/* Recursively opens directories on the stack in EB, until LOCAL_ABSPATH is reached. If RECURSIVE_SKIP is TRUE, don't open LOCAL_ABSPATH itself, but create it marked with skip+skip_children. */ static svn_error_t * ensure_state(struct diff_baton *eb, const char *local_abspath, svn_boolean_t recursive_skip, apr_pool_t *scratch_pool) { struct node_state_t *ns; apr_pool_t *ns_pool; if (!eb->cur) { if (!svn_dirent_is_ancestor(eb->anchor_abspath, local_abspath)) return SVN_NO_ERROR; SVN_ERR(ensure_state(eb, svn_dirent_dirname(local_abspath,scratch_pool), FALSE, scratch_pool)); } else if (svn_dirent_is_child(eb->cur->local_abspath, local_abspath, NULL)) SVN_ERR(ensure_state(eb, svn_dirent_dirname(local_abspath,scratch_pool), FALSE, scratch_pool)); else return SVN_NO_ERROR; if (eb->cur && eb->cur->skip_children) return SVN_NO_ERROR; ns_pool = svn_pool_create(eb->cur ? eb->cur->pool : eb->pool); ns = apr_pcalloc(ns_pool, sizeof(*ns)); ns->pool = ns_pool; ns->local_abspath = apr_pstrdup(ns_pool, local_abspath); ns->relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, ns->local_abspath); ns->parent = eb->cur; eb->cur = ns; if (recursive_skip) { ns->skip = TRUE; ns->skip_children = TRUE; return SVN_NO_ERROR; } { svn_revnum_t revision; svn_error_t *err; err = svn_wc__db_base_get_info(NULL, NULL, &revision, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, eb->db, local_abspath, scratch_pool, scratch_pool); if (err) { if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) return svn_error_trace(err); svn_error_clear(err); revision = 0; /* Use original revision? */ } ns->left_src = svn_diff__source_create(revision, ns->pool); ns->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, ns->pool); SVN_ERR(eb->processor->dir_opened(&ns->baton, &ns->skip, &ns->skip_children, ns->relpath, ns->left_src, ns->right_src, NULL /* copyfrom_source */, ns->parent ? ns->parent->baton : NULL, eb->processor, ns->pool, scratch_pool)); } return SVN_NO_ERROR; }
svn_error_t * svn_cl__get_log_message(const char **log_msg, const char **tmp_file, const apr_array_header_t *commit_items, void *baton, apr_pool_t *pool) { svn_stringbuf_t *default_msg = NULL; struct log_msg_baton *lmb = baton; svn_stringbuf_t *message = NULL; svn_config_t *cfg; const char *mfc_after, *sponsored_by; cfg = lmb->config ? svn_hash_gets(lmb->config, SVN_CONFIG_CATEGORY_CONFIG) : NULL; /* Set default message. */ default_msg = svn_stringbuf_create(APR_EOL_STR, pool); svn_stringbuf_appendcstr(default_msg, APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "PR:\t\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Submitted by:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Reported by:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Reviewed by:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Approved by:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Obtained from:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "MFC after:\t"); svn_config_get(cfg, &mfc_after, SVN_CONFIG_SECTION_MISCELLANY, "freebsd-mfc-after", NULL); if (mfc_after != NULL) svn_stringbuf_appendcstr(default_msg, mfc_after); svn_stringbuf_appendcstr(default_msg, APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "MFH:\t\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Relnotes:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Security:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Sponsored by:\t"); svn_config_get(cfg, &sponsored_by, SVN_CONFIG_SECTION_MISCELLANY, "freebsd-sponsored-by", #ifdef HAS_ORGANIZATION_NAME ORGANIZATION_NAME); #else NULL); #endif if (sponsored_by != NULL) svn_stringbuf_appendcstr(default_msg, sponsored_by); svn_stringbuf_appendcstr(default_msg, APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Differential Revision:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, EDITOR_EOF_PREFIX); svn_stringbuf_appendcstr(default_msg, APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Description of fields to fill in above: 76 columns --|" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> PR: If and which Problem Report is related." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Submitted by: If someone else sent in the change." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Reported by: If someone else reported the issue." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Reviewed by: If someone else reviewed your modification." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Approved by: If you needed approval for this commit." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Obtained from: If the change is from a third party." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> MFC after: N [day[s]|week[s]|month[s]]. Request a reminder email." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> MFH: Ports tree branch name. Request approval for merge." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Relnotes: Set to 'yes' for mention in release notes." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Security: Vulnerability reference (one per line) or description." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Sponsored by: If the change was sponsored by an organization." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Differential Revision: https://reviews.freebsd.org/D### (*full* phabric URL needed)." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Empty fields above will be automatically removed." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, APR_EOL_STR); *tmp_file = NULL; if (lmb->message) { svn_string_t *log_msg_str = svn_string_create(lmb->message, pool); SVN_ERR_W(svn_subst_translate_string2(&log_msg_str, NULL, NULL, log_msg_str, lmb->message_encoding, FALSE, pool, pool), _("Error normalizing log message to internal format")); /* Strip off the EOF marker text and the junk that follows it. */ truncate_buffer_at_prefix(&(log_msg_str->len), (char *)log_msg_str->data, EDITOR_EOF_PREFIX); cleanmsg(&(log_msg_str->len), (char*)log_msg_str->data); *log_msg = log_msg_str->data; return SVN_NO_ERROR; } if (! commit_items->nelts) { *log_msg = ""; return SVN_NO_ERROR; } while (! message) { /* We still don't have a valid commit message. Use $EDITOR to get one. Note that svn_cl__edit_string_externally will still return a UTF-8'ized log message. */ int i; svn_stringbuf_t *tmp_message = svn_stringbuf_dup(default_msg, pool); svn_error_t *err = SVN_NO_ERROR; svn_string_t *msg_string = svn_string_create_empty(pool); for (i = 0; i < commit_items->nelts; i++) { svn_client_commit_item3_t *item = APR_ARRAY_IDX(commit_items, i, svn_client_commit_item3_t *); const char *path = item->path; char text_mod = '_', prop_mod = ' ', unlock = ' '; if (! path) path = item->url; else if (lmb->base_dir) path = svn_dirent_is_child(lmb->base_dir, path, pool); /* If still no path, then just use current directory. */ if (! path || !*path) path = "."; if ((item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE) && (item->state_flags & SVN_CLIENT_COMMIT_ITEM_ADD)) text_mod = 'R'; else if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_ADD) text_mod = 'A'; else if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE) text_mod = 'D'; else if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_TEXT_MODS) text_mod = 'M'; if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_PROP_MODS) prop_mod = 'M'; if (! lmb->keep_locks && item->state_flags & SVN_CLIENT_COMMIT_ITEM_LOCK_TOKEN) unlock = 'U'; svn_stringbuf_appendbyte(tmp_message, text_mod); svn_stringbuf_appendbyte(tmp_message, prop_mod); svn_stringbuf_appendbyte(tmp_message, unlock); if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_IS_COPY) /* History included via copy/move. */ svn_stringbuf_appendcstr(tmp_message, "+ "); else svn_stringbuf_appendcstr(tmp_message, " "); svn_stringbuf_appendcstr(tmp_message, path); svn_stringbuf_appendcstr(tmp_message, APR_EOL_STR); } msg_string->data = tmp_message->data; msg_string->len = tmp_message->len; /* Use the external edit to get a log message. */ if (! lmb->non_interactive) { err = svn_cmdline__edit_string_externally(&msg_string, &lmb->tmpfile_left, lmb->editor_cmd, lmb->base_dir ? lmb->base_dir : "", msg_string, "svn-commit", lmb->config, TRUE, lmb->message_encoding, pool); } else /* non_interactive flag says we can't pop up an editor, so error */ { return svn_error_create (SVN_ERR_CL_INSUFFICIENT_ARGS, NULL, _("Cannot invoke editor to get log message " "when non-interactive")); } /* Dup the tmpfile path into its baton's pool. */ *tmp_file = lmb->tmpfile_left = apr_pstrdup(lmb->pool, lmb->tmpfile_left); /* If the edit returned an error, handle it. */ if (err) { if (err->apr_err == SVN_ERR_CL_NO_EXTERNAL_EDITOR) err = svn_error_quick_wrap (err, _("Could not use external editor to fetch log message; " "consider setting the $SVN_EDITOR environment variable " "or using the --message (-m) or --file (-F) options")); return svn_error_trace(err); } if (msg_string) message = svn_stringbuf_create_from_string(msg_string, pool); /* Strip off the EOF marker text and the junk that follows it. */ if (message) truncate_buffer_at_prefix(&message->len, message->data, EDITOR_EOF_PREFIX); /* * Since we're adding freebsd-specific tokens to the log message, * clean out any leftovers to avoid accidently sending them to other * projects that won't be expecting them. */ if (message) cleanmsg(&message->len, message->data); if (message) { /* We did get message, now check if it is anything more than just white space as we will consider white space only as empty */ apr_size_t len; for (len = 0; len < message->len; len++) { /* FIXME: should really use an UTF-8 whitespace test rather than svn_ctype_isspace, which is ASCII only */ if (! svn_ctype_isspace(message->data[len])) break; } if (len == message->len) message = NULL; } if (! message) { const char *reply; SVN_ERR(svn_cmdline_prompt_user2 (&reply, _("\nLog message unchanged or not specified\n" "(a)bort, (c)ontinue, (e)dit:\n"), NULL, pool)); if (reply) { int letter = apr_tolower(reply[0]); /* If the user chooses to abort, we cleanup the temporary file and exit the loop with a NULL message. */ if ('a' == letter) { SVN_ERR(svn_io_remove_file2(lmb->tmpfile_left, FALSE, pool)); *tmp_file = lmb->tmpfile_left = NULL; break; } /* If the user chooses to continue, we make an empty message, which will cause us to exit the loop. We also cleanup the temporary file. */ if ('c' == letter) { SVN_ERR(svn_io_remove_file2(lmb->tmpfile_left, FALSE, pool)); *tmp_file = lmb->tmpfile_left = NULL; message = svn_stringbuf_create_empty(pool); } /* If the user chooses anything else, the loop will continue on the NULL message. */ } } } *log_msg = message ? message->data : NULL; return SVN_NO_ERROR; }
/* Like svn_ra_stat() but with a compatibility hack for pre-1.2 svnserve. */ static svn_error_t * ra_stat_compatible(svn_ra_session_t *ra_session, svn_revnum_t rev, svn_dirent_t **dirent_p, apr_uint32_t dirent_fields, svn_client_ctx_t *ctx, apr_pool_t *pool) { const char *repos_root_URL, *url; svn_error_t *err; svn_dirent_t *the_ent; SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_URL, pool)); SVN_ERR(svn_ra_get_session_url(ra_session, &url, pool)); err = svn_ra_stat(ra_session, "", rev, &the_ent, pool); /* svn_ra_stat() will work against old versions of mod_dav_svn, but not old versions of svnserve. In the case of a pre-1.2 svnserve, catch the specific error it throws:*/ if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED) { /* Fall back to pre-1.2 strategy for fetching dirent's URL. */ svn_node_kind_t url_kind; svn_ra_session_t *parent_ra_session; apr_hash_t *parent_ents; const char *parent_url, *base_name; if (strcmp(url, repos_root_URL) == 0) { /* In this universe, there's simply no way to fetch information about the repository's root directory! */ return err; } svn_error_clear(err); SVN_ERR(svn_ra_check_path(ra_session, "", rev, &url_kind, pool)); if (url_kind == svn_node_none) return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, _("URL '%s' non-existent in revision %ld"), url, rev); /* Open a new RA session to the item's parent. */ svn_uri_split(&parent_url, &base_name, url, pool); SVN_ERR(svn_client__open_ra_session_internal(&parent_ra_session, NULL, parent_url, NULL, NULL, FALSE, TRUE, ctx, pool)); /* Get all parent's entries, and find the item's dirent in the hash. */ SVN_ERR(svn_ra_get_dir2(parent_ra_session, &parent_ents, NULL, NULL, "", rev, dirent_fields, pool)); the_ent = apr_hash_get(parent_ents, base_name, APR_HASH_KEY_STRING); if (the_ent == NULL) return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, _("URL '%s' non-existent in revision %ld"), url, rev); } else if (err) { return svn_error_trace(err); } *dirent_p = the_ent; return SVN_NO_ERROR; }
svn_error_t * svn_client_info3(const char *abspath_or_url, const svn_opt_revision_t *peg_revision, const svn_opt_revision_t *revision, svn_depth_t depth, svn_boolean_t fetch_excluded, svn_boolean_t fetch_actual_only, const apr_array_header_t *changelists, svn_client_info_receiver2_t receiver, void *receiver_baton, svn_client_ctx_t *ctx, apr_pool_t *pool) { svn_ra_session_t *ra_session; svn_client__pathrev_t *pathrev; svn_lock_t *lock; svn_boolean_t related; const char *base_name; svn_dirent_t *the_ent; svn_client_info2_t *info; svn_error_t *err; if (depth == svn_depth_unknown) depth = svn_depth_empty; if ((revision == NULL || revision->kind == svn_opt_revision_unspecified) && (peg_revision == NULL || peg_revision->kind == svn_opt_revision_unspecified)) { /* Do all digging in the working copy. */ wc_info_receiver_baton_t b; b.client_receiver_func = receiver; b.client_receiver_baton = receiver_baton; return svn_error_trace( svn_wc__get_info(ctx->wc_ctx, abspath_or_url, depth, fetch_excluded, fetch_actual_only, changelists, wc_info_receiver, &b, ctx->cancel_func, ctx->cancel_baton, pool)); } /* Go repository digging instead. */ /* Trace rename history (starting at path_or_url@peg_revision) and return RA session to the possibly-renamed URL as it exists in REVISION. The ra_session returned will be anchored on this "final" URL. */ SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &pathrev, abspath_or_url, NULL, peg_revision, revision, ctx, pool)); svn_uri_split(NULL, &base_name, pathrev->url, pool); /* Get the dirent for the URL itself. */ err = ra_stat_compatible(ra_session, pathrev->rev, &the_ent, DIRENT_FIELDS, ctx, pool); if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED) { svn_error_clear(err); /* If we're recursing, degrade gracefully: rather than throw an error, return no information about the repos root. */ if (depth > svn_depth_empty) goto pre_1_2_recurse; /* Otherwise, we really are stuck. Better tell the user what's going on. */ return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, _("Server does not support retrieving " "information about the repository root")); } else if (err) { return svn_error_trace(err); } if (! the_ent) return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, _("URL '%s' non-existent in revision %ld"), pathrev->url, pathrev->rev); /* Check if the URL exists in HEAD and refers to the same resource. In this case, we check the repository for a lock on this URL. ### There is a possible race here, since HEAD might have changed since ### we checked it. A solution to this problem could be to do the below ### check in a loop which only terminates if the HEAD revision is the same ### before and after this check. That could, however, lead to a ### starvation situation instead. */ SVN_ERR(same_resource_in_head(&related, pathrev->url, pathrev->rev, ra_session, ctx, pool)); if (related) { err = svn_ra_get_lock(ra_session, &lock, "", pool); /* An old mod_dav_svn will always work; there's nothing wrong with doing a PROPFIND for a property named "DAV:supportedlock". But an old svnserve will error. */ if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED) { svn_error_clear(err); lock = NULL; } else if (err) return svn_error_trace(err); } else lock = NULL; /* Push the URL's dirent (and lock) at the callback.*/ SVN_ERR(build_info_from_dirent(&info, the_ent, lock, pathrev, pool)); SVN_ERR(receiver(receiver_baton, base_name, info, pool)); /* Possibly recurse, using the original RA session. */ if (depth > svn_depth_empty && (the_ent->kind == svn_node_dir)) { apr_hash_t *locks; pre_1_2_recurse: if (peg_revision->kind == svn_opt_revision_head) { err = svn_ra_get_locks2(ra_session, &locks, "", depth, pool); /* Catch specific errors thrown by old mod_dav_svn or svnserve. */ if (err && (err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED || err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE)) { svn_error_clear(err); locks = apr_hash_make(pool); /* use an empty hash */ } else if (err) return svn_error_trace(err); } else locks = apr_hash_make(pool); /* use an empty hash */ SVN_ERR(push_dir_info(ra_session, pathrev, "", receiver, receiver_baton, depth, ctx, locks, pool)); } return SVN_NO_ERROR; }
/* Remove the directory at LOCAL_ABSPATH from revision control, and do the * same to any revision controlled directories underneath LOCAL_ABSPATH * (including directories not referred to by parent svn administrative areas); * then if LOCAL_ABSPATH is empty afterwards, remove it, else rename it to a * unique name in the same parent directory. * * Pass CANCEL_FUNC, CANCEL_BATON to svn_wc_remove_from_revision_control. * * Use SCRATCH_POOL for all temporary allocation. */ static svn_error_t * relegate_dir_external(svn_wc_context_t *wc_ctx, const char *wri_abspath, const char *local_abspath, svn_cancel_func_t cancel_func, void *cancel_baton, svn_wc_notify_func2_t notify_func, void *notify_baton, apr_pool_t *scratch_pool) { svn_error_t *err = SVN_NO_ERROR; SVN_ERR(svn_wc__acquire_write_lock(NULL, wc_ctx, local_abspath, FALSE, scratch_pool, scratch_pool)); err = svn_wc__external_remove(wc_ctx, wri_abspath, local_abspath, FALSE, cancel_func, cancel_baton, scratch_pool); if (err && (err->apr_err == SVN_ERR_WC_LEFT_LOCAL_MOD)) { const char *parent_dir; const char *dirname; const char *new_path; svn_error_clear(err); err = SVN_NO_ERROR; svn_dirent_split(&parent_dir, &dirname, local_abspath, scratch_pool); /* Reserve the new dir name. */ SVN_ERR(svn_io_open_uniquely_named(NULL, &new_path, parent_dir, dirname, ".OLD", svn_io_file_del_none, scratch_pool, scratch_pool)); /* Sigh... We must fall ever so slightly from grace. Ideally, there would be no window, however brief, when we don't have a reservation on the new name. Unfortunately, at least in the Unix (Linux?) version of apr_file_rename(), you can't rename a directory over a file, because it's just calling stdio rename(), which says: ENOTDIR A component used as a directory in oldpath or newpath path is not, in fact, a directory. Or, oldpath is a directory, and newpath exists but is not a directory So instead, we get the name, then remove the file (ugh), then rename the directory, hoping that nobody has gotten that name in the meantime -- which would never happen in real life, so no big deal. */ /* Do our best, but no biggy if it fails. The rename will fail. */ svn_error_clear(svn_io_remove_file2(new_path, TRUE, scratch_pool)); /* Rename. If this is still a working copy we should use the working copy rename function (to release open handles) */ err = svn_wc__rename_wc(wc_ctx, local_abspath, new_path, scratch_pool); if (err && err->apr_err == SVN_ERR_WC_PATH_UNEXPECTED_STATUS) { svn_error_clear(err); /* And if it is no longer a working copy, we should just rename it */ err = svn_io_file_rename(local_abspath, new_path, scratch_pool); } /* ### TODO: We should notify the user about the rename */ if (notify_func) { svn_wc_notify_t *notify; notify = svn_wc_create_notify(err ? local_abspath : new_path, svn_wc_notify_left_local_modifications, scratch_pool); notify->kind = svn_node_dir; notify->err = err; notify_func(notify_baton, notify, scratch_pool); } } return svn_error_trace(err); }
svn_error_t * svn_ra_svn__stream_write(svn_ra_svn__stream_t *stream, const char *data, apr_size_t *len) { return svn_error_trace(svn_stream_write(stream->out_stream, data, len)); }
/* Make an unversioned copy of the versioned file at FROM_ABSPATH. Copy it * to the destination path TO_ABSPATH. * * If REVISION is svn_opt_revision_working, copy the working version, * otherwise copy the base version. * * Expand the file's keywords according to the source file's 'svn:keywords' * property, if present. If copying a locally modified working version, * append 'M' to the revision number and use '(local)' for the author. * * Translate the file's line endings according to the source file's * 'svn:eol-style' property, if present. If NATIVE_EOL is not NULL, use it * in place of the native EOL style. Throw an error if the source file has * inconsistent line endings and EOL translation is attempted. * * Set the destination file's modification time to the source file's * modification time if copying the working version and the working version * is locally modified; otherwise set it to the versioned file's last * changed time. * * Set the destination file's 'executable' flag according to the source * file's 'svn:executable' property. */ static svn_error_t * copy_one_versioned_file(const char *from_abspath, const char *to_abspath, svn_client_ctx_t *ctx, const svn_opt_revision_t *revision, const char *native_eol, svn_boolean_t ignore_keywords, apr_pool_t *scratch_pool) { apr_hash_t *kw = NULL; svn_subst_eol_style_t style; apr_hash_t *props; svn_string_t *eol_style, *keywords, *executable, *special; const char *eol = NULL; svn_boolean_t local_mod = FALSE; apr_time_t tm; svn_stream_t *source; svn_stream_t *dst_stream; const char *dst_tmp; svn_error_t *err; svn_boolean_t is_deleted; svn_wc_context_t *wc_ctx = ctx->wc_ctx; SVN_ERR(svn_wc__node_is_status_deleted(&is_deleted, wc_ctx, from_abspath, scratch_pool)); /* Don't export 'deleted' files and directories unless it's a revision other than WORKING. These files and directories don't really exist in WORKING. */ if (revision->kind == svn_opt_revision_working && is_deleted) return SVN_NO_ERROR; if (revision->kind != svn_opt_revision_working) { /* Only export 'added' files when the revision is WORKING. This is not WORKING, so skip the 'added' files, since they didn't exist in the BASE revision and don't have an associated text-base. 'replaced' files are technically the same as 'added' files. ### TODO: Handle replaced nodes properly. ### svn_opt_revision_base refers to the "new" ### base of the node. That means, if a node is locally ### replaced, export skips this node, as if it was locally ### added, because svn_opt_revision_base refers to the base ### of the added node, not to the node that was deleted. ### In contrast, when the node is copied-here or moved-here, ### the copy/move source's content will be exported. ### It is currently not possible to export the revert-base ### when a node is locally replaced. We need a new ### svn_opt_revision_ enum value for proper distinction ### between revert-base and commit-base. Copied-/moved-here nodes have a base, so export both added and replaced files when they involve a copy-/move-here. We get all this for free from evaluating SOURCE == NULL: */ SVN_ERR(svn_wc_get_pristine_contents2(&source, wc_ctx, from_abspath, scratch_pool, scratch_pool)); if (source == NULL) return SVN_NO_ERROR; SVN_ERR(svn_wc_get_pristine_props(&props, wc_ctx, from_abspath, scratch_pool, scratch_pool)); } else { svn_wc_status3_t *status; /* ### hmm. this isn't always a specialfile. this will simply open ### the file readonly if it is a regular file. */ SVN_ERR(svn_subst_read_specialfile(&source, from_abspath, scratch_pool, scratch_pool)); SVN_ERR(svn_wc_prop_list2(&props, wc_ctx, from_abspath, scratch_pool, scratch_pool)); SVN_ERR(svn_wc_status3(&status, wc_ctx, from_abspath, scratch_pool, scratch_pool)); if (status->text_status != svn_wc_status_normal) local_mod = TRUE; } /* We can early-exit if we're creating a special file. */ special = apr_hash_get(props, SVN_PROP_SPECIAL, APR_HASH_KEY_STRING); if (special != NULL) { /* Create the destination as a special file, and copy the source details into the destination stream. */ SVN_ERR(svn_subst_create_specialfile(&dst_stream, to_abspath, scratch_pool, scratch_pool)); return svn_error_trace( svn_stream_copy3(source, dst_stream, NULL, NULL, scratch_pool)); } eol_style = apr_hash_get(props, SVN_PROP_EOL_STYLE, APR_HASH_KEY_STRING); keywords = apr_hash_get(props, SVN_PROP_KEYWORDS, APR_HASH_KEY_STRING); executable = apr_hash_get(props, SVN_PROP_EXECUTABLE, APR_HASH_KEY_STRING); if (eol_style) SVN_ERR(get_eol_style(&style, &eol, eol_style->data, native_eol)); if (local_mod) { /* Use the modified time from the working copy of the file */ SVN_ERR(svn_io_file_affected_time(&tm, from_abspath, scratch_pool)); } else { SVN_ERR(svn_wc__node_get_changed_info(NULL, &tm, NULL, wc_ctx, from_abspath, scratch_pool, scratch_pool)); } if (keywords) { svn_revnum_t changed_rev; const char *suffix; const char *url; const char *author; SVN_ERR(svn_wc__node_get_changed_info(&changed_rev, NULL, &author, wc_ctx, from_abspath, scratch_pool, scratch_pool)); if (local_mod) { /* For locally modified files, we'll append an 'M' to the revision number, and set the author to "(local)" since we can't always determine the current user's username */ suffix = "M"; author = _("(local)"); } else { suffix = ""; } SVN_ERR(svn_wc__node_get_url(&url, wc_ctx, from_abspath, scratch_pool, scratch_pool)); SVN_ERR(svn_subst_build_keywords2 (&kw, keywords->data, apr_psprintf(scratch_pool, "%ld%s", changed_rev, suffix), url, tm, author, scratch_pool)); } /* For atomicity, we translate to a tmp file and then rename the tmp file over the real destination. */ SVN_ERR(svn_stream_open_unique(&dst_stream, &dst_tmp, svn_dirent_dirname(to_abspath, scratch_pool), svn_io_file_del_none, scratch_pool, scratch_pool)); /* If some translation is needed, then wrap the output stream (this is more efficient than wrapping the input). */ if (eol || (kw && (apr_hash_count(kw) > 0))) dst_stream = svn_subst_stream_translated(dst_stream, eol, FALSE /* repair */, kw, ! ignore_keywords /* expand */, scratch_pool); /* ###: use cancel func/baton in place of NULL/NULL below. */ err = svn_stream_copy3(source, dst_stream, NULL, NULL, scratch_pool); if (!err && executable) err = svn_io_set_file_executable(dst_tmp, TRUE, FALSE, scratch_pool); if (!err) err = svn_io_set_file_affected_time(tm, dst_tmp, scratch_pool); if (err) return svn_error_compose_create(err, svn_io_remove_file2(dst_tmp, FALSE, scratch_pool)); /* Now that dst_tmp contains the translated data, do the atomic rename. */ SVN_ERR(svn_io_file_rename(dst_tmp, to_abspath, scratch_pool)); if (ctx->notify_func2) { svn_wc_notify_t *notify = svn_wc_create_notify(to_abspath, svn_wc_notify_update_add, scratch_pool); notify->kind = svn_node_file; (*ctx->notify_func2)(ctx->notify_baton2, notify, scratch_pool); } return SVN_NO_ERROR; }
/* The main logic of the public svn_client_add4. * * EXISTING_PARENT_ABSPATH is the absolute path to the first existing * parent directory of local_abspath. If not NULL, all missing parents * of LOCAL_ABSPATH must be created before LOCAL_ABSPATH can be added. */ static svn_error_t * add(const char *local_abspath, svn_depth_t depth, svn_boolean_t force, svn_boolean_t no_ignore, const char *existing_parent_abspath, svn_client_ctx_t *ctx, apr_pool_t *scratch_pool) { svn_node_kind_t kind; svn_error_t *err; svn_magic__cookie_t *magic_cookie; svn_magic__init(&magic_cookie, scratch_pool); if (existing_parent_abspath) { const char *parent_abspath; const char *child_relpath; apr_array_header_t *components; int i; apr_pool_t *iterpool; parent_abspath = existing_parent_abspath; child_relpath = svn_dirent_is_child(existing_parent_abspath, local_abspath, NULL); components = svn_path_decompose(child_relpath, scratch_pool); iterpool = svn_pool_create(scratch_pool); for (i = 0; i < components->nelts - 1; i++) { const char *component; svn_node_kind_t disk_kind; svn_pool_clear(iterpool); if (ctx->cancel_func) SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); component = APR_ARRAY_IDX(components, i, const char *); parent_abspath = svn_dirent_join(parent_abspath, component, scratch_pool); SVN_ERR(svn_io_check_path(parent_abspath, &disk_kind, iterpool)); if (disk_kind != svn_node_none && disk_kind != svn_node_dir) return svn_error_createf(SVN_ERR_CLIENT_NO_VERSIONED_PARENT, NULL, _("'%s' prevents creating parent of '%s'"), parent_abspath, local_abspath); SVN_ERR(svn_io_make_dir_recursively(parent_abspath, scratch_pool)); SVN_ERR(svn_wc_add_from_disk(ctx->wc_ctx, parent_abspath, ctx->notify_func2, ctx->notify_baton2, scratch_pool)); } svn_pool_destroy(iterpool); } SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool)); if (kind == svn_node_dir) { /* We use add_dir_recursive for all directory targets and pass depth along no matter what it is, so that the target's depth will be set correctly. */ err = add_dir_recursive(local_abspath, depth, force, no_ignore, magic_cookie, ctx, scratch_pool); } else if (kind == svn_node_file) err = add_file(local_abspath, magic_cookie, ctx, scratch_pool); else if (kind == svn_node_none) { svn_boolean_t tree_conflicted; /* Provide a meaningful error message if the node does not exist * on disk but is a tree conflict victim. */ err = svn_wc_conflicted_p3(NULL, NULL, &tree_conflicted, ctx->wc_ctx, local_abspath, scratch_pool); if (err) svn_error_clear(err); else if (tree_conflicted) return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL, _("'%s' is an existing item in conflict; " "please mark the conflict as resolved " "before adding a new item here"), svn_dirent_local_style(local_abspath, scratch_pool)); return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, _("'%s' not found"), svn_dirent_local_style(local_abspath, scratch_pool)); } else return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, _("Unsupported node kind for path '%s'"), svn_dirent_local_style(local_abspath, scratch_pool)); /* Ignore SVN_ERR_ENTRY_EXISTS when FORCE is set. */ if (err && err->apr_err == SVN_ERR_ENTRY_EXISTS && force) { svn_error_clear(err); err = SVN_NO_ERROR; } return svn_error_trace(err); }
/* Only call this if the on-disk node kind is a file. */ static svn_error_t * add_file(const char *local_abspath, svn_magic__cookie_t *magic_cookie, svn_client_ctx_t *ctx, apr_pool_t *pool) { apr_hash_t* properties; apr_hash_index_t *hi; const char *mimetype; svn_node_kind_t kind; svn_boolean_t is_special; /* Check to see if this is a special file. */ SVN_ERR(svn_io_check_special_path(local_abspath, &kind, &is_special, pool)); if (is_special) mimetype = NULL; else /* Get automatic properties */ /* This may fail on write-only files: we open them to estimate file type. That's why we postpone the add until after this step. */ SVN_ERR(svn_client__get_auto_props(&properties, &mimetype, local_abspath, magic_cookie, ctx, pool)); /* Add the file */ SVN_ERR(svn_wc_add_from_disk(ctx->wc_ctx, local_abspath, NULL, NULL, pool)); if (is_special) /* This must be a special file. */ SVN_ERR(svn_wc_prop_set4(ctx->wc_ctx, local_abspath, SVN_PROP_SPECIAL, svn_string_create(SVN_PROP_BOOLEAN_TRUE, pool), svn_depth_empty, FALSE, NULL, NULL, NULL /* cancellation */, NULL, NULL /* notification */, pool)); else if (properties) { /* loop through the hashtable and add the properties */ for (hi = apr_hash_first(pool, properties); hi != NULL; hi = apr_hash_next(hi)) { const char *pname = svn__apr_hash_index_key(hi); const svn_string_t *pval = svn__apr_hash_index_val(hi); svn_error_t *err; /* It's probably best to pass 0 for force, so that if the autoprops say to set some weird combination, we just error and let the user sort it out. */ err = svn_wc_prop_set4(ctx->wc_ctx, local_abspath, pname, pval, svn_depth_empty, FALSE, NULL, NULL, NULL /* cancellation */, NULL, NULL /* notification */, pool); if (err) { /* Don't leave the job half-done. If we fail to set a property, * (try to) un-add the file. */ svn_error_clear(svn_wc_revert4(ctx->wc_ctx, local_abspath, svn_depth_empty, FALSE /* use_commit_times */, NULL /* changelists */, NULL, NULL, NULL, NULL, pool)); return svn_error_trace(err); } } } /* Report the addition to the caller. */ if (ctx->notify_func2 != NULL) { svn_wc_notify_t *notify = svn_wc_create_notify(local_abspath, svn_wc_notify_add, pool); notify->kind = svn_node_file; notify->mime_type = mimetype; (*ctx->notify_func2)(ctx->notify_baton2, notify, pool); } return SVN_NO_ERROR; }
/* Schedule directory DIR_ABSPATH, and some of the tree under it, for * addition. DEPTH is the depth at this * point in the descent (it may be changed for recursive calls). * * If DIR_ABSPATH (or any item below DIR_ABSPATH) is already scheduled for * addition, add will fail and return an error unless FORCE is TRUE. * * Files and directories that match ignore patterns will not be added unless * NO_IGNORE is TRUE. * * Use MAGIC_COOKIE (which may be NULL) to detect the mime-type of files * if necessary. * * If CTX->CANCEL_FUNC is non-null, call it with CTX->CANCEL_BATON to allow * the user to cancel the operation */ static svn_error_t * add_dir_recursive(const char *dir_abspath, svn_depth_t depth, svn_boolean_t force, svn_boolean_t no_ignore, svn_magic__cookie_t *magic_cookie, svn_client_ctx_t *ctx, apr_pool_t *scratch_pool) { svn_error_t *err; apr_pool_t *iterpool; apr_array_header_t *ignores; apr_hash_t *dirents; apr_hash_index_t *hi; /* Check cancellation; note that this catches recursive calls too. */ if (ctx->cancel_func) SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); iterpool = svn_pool_create(scratch_pool); /* Add this directory to revision control. */ err = svn_wc_add_from_disk(ctx->wc_ctx, dir_abspath, ctx->notify_func2, ctx->notify_baton2, iterpool); if (err && err->apr_err == SVN_ERR_ENTRY_EXISTS && force) svn_error_clear(err); else if (err) return svn_error_trace(err); if (!no_ignore) { SVN_ERR(svn_wc_get_ignores2(&ignores, ctx->wc_ctx, dir_abspath, ctx->config, scratch_pool, iterpool)); } SVN_ERR(svn_io_get_dirents3(&dirents, dir_abspath, TRUE, scratch_pool, iterpool)); /* Read the directory entries one by one and add those things to version control. */ for (hi = apr_hash_first(scratch_pool, dirents); hi; hi = apr_hash_next(hi)) { const char *name = svn__apr_hash_index_key(hi); svn_io_dirent2_t *dirent = svn__apr_hash_index_val(hi); const char *abspath; svn_pool_clear(iterpool); /* Check cancellation so you can cancel during an * add of a directory with lots of files. */ if (ctx->cancel_func) SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); /* Skip over SVN admin directories. */ if (svn_wc_is_adm_dir(name, iterpool)) continue; if ((!no_ignore) && svn_wc_match_ignore_list(name, ignores, iterpool)) continue; /* Construct the full path of the entry. */ abspath = svn_dirent_join(dir_abspath, name, iterpool); /* Recurse on directories; add files; ignore the rest. */ if (dirent->kind == svn_node_dir && depth >= svn_depth_immediates) { svn_depth_t depth_below_here = depth; if (depth == svn_depth_immediates) depth_below_here = svn_depth_empty; SVN_ERR(add_dir_recursive(abspath, depth_below_here, force, no_ignore, magic_cookie, ctx, iterpool)); } else if ((dirent->kind == svn_node_file || dirent->special) && depth >= svn_depth_files) { err = add_file(abspath, magic_cookie, ctx, iterpool); if (err && err->apr_err == SVN_ERR_ENTRY_EXISTS && force) svn_error_clear(err); else SVN_ERR(err); } } /* Destroy the per-iteration pool. */ svn_pool_destroy(iterpool); return SVN_NO_ERROR; }
static svn_error_t * handle_external_item_change(svn_client_ctx_t *ctx, const char *repos_root_url, const char *parent_dir_abspath, const char *parent_dir_url, const char *local_abspath, const char *old_defining_abspath, const svn_wc_external_item2_t *new_item, svn_boolean_t *timestamp_sleep, apr_pool_t *scratch_pool) { svn_ra_session_t *ra_session; svn_client__pathrev_t *new_loc; const char *new_url; svn_node_kind_t ext_kind; SVN_ERR_ASSERT(repos_root_url && parent_dir_url); SVN_ERR_ASSERT(new_item != NULL); /* Don't bother to check status, since we'll get that for free by attempting to retrieve the hash values anyway. */ /* When creating the absolute URL, use the pool and not the iterpool, since the hash table values outlive the iterpool and any pointers they have should also outlive the iterpool. */ SVN_ERR(svn_wc__resolve_relative_external_url(&new_url, new_item, repos_root_url, parent_dir_url, scratch_pool, scratch_pool)); /* Determine if the external is a file or directory. */ /* Get the RA connection. */ SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &new_loc, new_url, NULL, &(new_item->peg_revision), &(new_item->revision), ctx, scratch_pool)); SVN_ERR(svn_ra_check_path(ra_session, "", new_loc->rev, &ext_kind, scratch_pool)); if (svn_node_none == ext_kind) return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, _("URL '%s' at revision %ld doesn't exist"), new_loc->url, new_loc->rev); if (svn_node_dir != ext_kind && svn_node_file != ext_kind) return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, _("URL '%s' at revision %ld is not a file " "or a directory"), new_loc->url, new_loc->rev); /* Not protecting against recursive externals. Detecting them in the global case is hard, and it should be pretty obvious to a user when it happens. Worst case: your disk fills up :-). */ /* First notify that we're about to handle an external. */ if (ctx->notify_func2) { (*ctx->notify_func2)( ctx->notify_baton2, svn_wc_create_notify(local_abspath, svn_wc_notify_update_external, scratch_pool), scratch_pool); } if (! old_defining_abspath) { /* The target dir might have multiple components. Guarantee the path leading down to the last component. */ SVN_ERR(svn_io_make_dir_recursively(svn_dirent_dirname(local_abspath, scratch_pool), scratch_pool)); } switch (ext_kind) { case svn_node_dir: SVN_ERR(switch_dir_external(local_abspath, new_loc->url, new_item->url, &(new_item->peg_revision), &(new_item->revision), parent_dir_abspath, timestamp_sleep, ctx, scratch_pool)); break; case svn_node_file: if (strcmp(repos_root_url, new_loc->repos_root_url)) { const char *local_repos_root_url; const char *local_repos_uuid; const char *ext_repos_relpath; svn_error_t *err; /* * The working copy library currently requires that all files * in the working copy have the same repository root URL. * The URL from the file external's definition differs from the * one used by the working copy. As a workaround, replace the * root URL portion of the file external's URL, after making * sure both URLs point to the same repository. See issue #4087. */ err = svn_wc__node_get_repos_info(NULL, NULL, &local_repos_root_url, &local_repos_uuid, ctx->wc_ctx, parent_dir_abspath, scratch_pool, scratch_pool); if (err) { if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND && err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY) return svn_error_trace(err); svn_error_clear(err); local_repos_root_url = NULL; local_repos_uuid = NULL; } ext_repos_relpath = svn_uri_skip_ancestor(new_loc->repos_root_url, new_url, scratch_pool); if (local_repos_uuid == NULL || local_repos_root_url == NULL || ext_repos_relpath == NULL || strcmp(local_repos_uuid, new_loc->repos_uuid) != 0) return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, _("Unsupported external: URL of file external '%s' " "is not in repository '%s'"), new_url, repos_root_url); new_url = svn_path_url_add_component2(local_repos_root_url, ext_repos_relpath, scratch_pool); SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &new_loc, new_url, NULL, &(new_item->peg_revision), &(new_item->revision), ctx, scratch_pool)); } SVN_ERR(switch_file_external(local_abspath, new_url, &new_item->peg_revision, &new_item->revision, parent_dir_abspath, ra_session, ctx, scratch_pool)); break; default: SVN_ERR_MALFUNCTION(); break; } return SVN_NO_ERROR; }
static svn_error_t * svn_fs_bdb__open_internal(bdb_env_baton_t **bdb_batonp, const char *path, u_int32_t flags, int mode, apr_pool_t *pool) { bdb_env_key_t key; bdb_env_t *bdb; svn_boolean_t panic; /* We can safely discard the open DB_CONFIG file handle. If the environment descriptor is in the cache, the key's immutability is guaranteed. If it's not, we don't care if the key changes, between here and the actual insertion of the newly-created environment into the cache, because no other thread can touch the cache in the meantime. */ SVN_ERR(bdb_cache_key(&key, NULL, path, pool)); bdb = bdb_cache_get(&key, &panic); if (panic) return svn_error_create(SVN_ERR_FS_BERKELEY_DB, NULL, db_strerror(DB_RUNRECOVERY)); /* Make sure that the environment's open flags haven't changed. */ if (bdb && bdb->flags != flags) { /* Handle changes to the DB_PRIVATE flag specially */ if ((flags ^ bdb->flags) & DB_PRIVATE) { if (flags & DB_PRIVATE) return svn_error_create(SVN_ERR_FS_BERKELEY_DB, NULL, "Reopening a public Berkeley DB" " environment with private attributes"); else return svn_error_create(SVN_ERR_FS_BERKELEY_DB, NULL, "Reopening a private Berkeley DB" " environment with public attributes"); } /* Otherwise return a generic "flags-mismatch" error. */ return svn_error_create(SVN_ERR_FS_BERKELEY_DB, NULL, "Reopening a Berkeley DB environment" " with different attributes"); } if (!bdb) { svn_error_t *err; SVN_ERR(create_env(&bdb, path, svn_pool_create(bdb_cache_pool))); err = bdb_open(bdb, flags, mode); if (err) { /* Clean up, and we can't do anything about returned errors. */ svn_error_clear(bdb_close(bdb)); return svn_error_trace(err); } apr_hash_set(bdb_cache, &bdb->key, sizeof bdb->key, bdb); bdb->flags = flags; bdb->refcount = 1; } else { ++bdb->refcount; } *bdb_batonp = apr_palloc(pool, sizeof **bdb_batonp); (*bdb_batonp)->env = bdb->env; (*bdb_batonp)->bdb = bdb; (*bdb_batonp)->error_info = get_error_info(bdb); ++(*bdb_batonp)->error_info->refcount; apr_pool_cleanup_register(pool, *bdb_batonp, cleanup_env_baton, apr_pool_cleanup_null); return SVN_NO_ERROR; }
svn_error_t * svn_client__handle_externals(apr_hash_t *externals_new, apr_hash_t *ambient_depths, const char *repos_root_url, const char *target_abspath, svn_depth_t requested_depth, svn_boolean_t *timestamp_sleep, svn_client_ctx_t *ctx, apr_pool_t *scratch_pool) { apr_hash_t *old_external_defs; apr_hash_index_t *hi; apr_pool_t *iterpool; SVN_ERR_ASSERT(repos_root_url); iterpool = svn_pool_create(scratch_pool); SVN_ERR(svn_wc__externals_defined_below(&old_external_defs, ctx->wc_ctx, target_abspath, scratch_pool, iterpool)); for (hi = apr_hash_first(scratch_pool, externals_new); hi; hi = apr_hash_next(hi)) { const char *local_abspath = svn__apr_hash_index_key(hi); const char *desc_text = svn__apr_hash_index_val(hi); svn_depth_t ambient_depth = svn_depth_infinity; svn_pool_clear(iterpool); if (ambient_depths) { const char *ambient_depth_w; ambient_depth_w = apr_hash_get(ambient_depths, local_abspath, svn__apr_hash_index_klen(hi)); if (ambient_depth_w == NULL) { return svn_error_createf( SVN_ERR_WC_CORRUPT, NULL, _("Traversal of '%s' found no ambient depth"), svn_dirent_local_style(local_abspath, scratch_pool)); } else { ambient_depth = svn_depth_from_word(ambient_depth_w); } } SVN_ERR(handle_externals_change(ctx, repos_root_url, timestamp_sleep, local_abspath, desc_text, old_external_defs, ambient_depth, requested_depth, iterpool)); } /* Remove the remaining externals */ for (hi = apr_hash_first(scratch_pool, old_external_defs); hi; hi = apr_hash_next(hi)) { const char *item_abspath = svn__apr_hash_index_key(hi); const char *defining_abspath = svn__apr_hash_index_val(hi); const char *parent_abspath; svn_pool_clear(iterpool); SVN_ERR(wrap_external_error( ctx, item_abspath, handle_external_item_removal(ctx, defining_abspath, item_abspath, iterpool), iterpool)); /* Are there any unversioned directories between the removed * external and the DEFINING_ABSPATH which we can remove? */ parent_abspath = item_abspath; do { svn_node_kind_t kind; parent_abspath = svn_dirent_dirname(parent_abspath, iterpool); SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, parent_abspath, FALSE /* show_deleted*/, FALSE /* show_hidden */, iterpool)); if (kind == svn_node_none) { svn_error_t *err; err = svn_io_dir_remove_nonrecursive(parent_abspath, iterpool); if (err) { if (APR_STATUS_IS_ENOTEMPTY(err->apr_err)) { svn_error_clear(err); break; /* No parents to delete */ } else if (APR_STATUS_IS_ENOENT(err->apr_err) || APR_STATUS_IS_ENOTDIR(err->apr_err)) { svn_error_clear(err); /* Fall through; parent dir might be unversioned */ } else return svn_error_trace(err); } } } while (strcmp(parent_abspath, defining_abspath) != 0); } svn_pool_destroy(iterpool); return SVN_NO_ERROR; }
/* Make an unversioned copy of the versioned file or directory tree at the * source path FROM_ABSPATH. Copy it to the destination path TO_ABSPATH. * * If REVISION is svn_opt_revision_working, copy the working version, * otherwise copy the base version. * * See copy_one_versioned_file() for details of file copying behaviour, * including IGNORE_KEYWORDS and NATIVE_EOL. * * Include externals unless IGNORE_EXTERNALS is true. * * Recurse according to DEPTH. * */ static svn_error_t * copy_versioned_files(const char *from_abspath, const char *to_abspath, const svn_opt_revision_t *revision, svn_boolean_t force, svn_boolean_t ignore_externals, svn_boolean_t ignore_keywords, svn_depth_t depth, const char *native_eol, svn_client_ctx_t *ctx, apr_pool_t *pool) { svn_error_t *err; apr_pool_t *iterpool; const apr_array_header_t *children; svn_node_kind_t from_kind; svn_depth_t node_depth; SVN_ERR_ASSERT(svn_dirent_is_absolute(from_abspath)); SVN_ERR_ASSERT(svn_dirent_is_absolute(to_abspath)); /* Only export 'added' and 'replaced' files when the revision is WORKING; when the revision is BASE (i.e. != WORKING), only export 'added' and 'replaced' files when they are part of a copy-/move-here. Otherwise, skip them, since they don't have an associated text-base. This condition for added/replaced simply is an optimization. Added and replaced files would be handled similarly by svn_wc_get_pristine_contents2(), which would return NULL if they have no base associated. TODO: We may prefer not to duplicate this condition and rather use svn_wc_get_pristine_contents2() or a dedicated new function instead. Don't export 'deleted' files and directories unless it's a revision other than WORKING. These files and directories don't really exist in WORKING. */ if (revision->kind != svn_opt_revision_working) { svn_boolean_t is_added; const char *repos_relpath; SVN_ERR(svn_wc__node_get_origin(&is_added, NULL, &repos_relpath, NULL, NULL, NULL, ctx->wc_ctx, from_abspath, FALSE, pool, pool)); if (is_added && !repos_relpath) return SVN_NO_ERROR; /* Local addition */ } else { svn_boolean_t is_deleted; SVN_ERR(svn_wc__node_is_status_deleted(&is_deleted, ctx->wc_ctx, from_abspath, pool)); if (is_deleted) return SVN_NO_ERROR; } SVN_ERR(svn_wc_read_kind(&from_kind, ctx->wc_ctx, from_abspath, FALSE, pool)); if (from_kind == svn_node_dir) { apr_fileperms_t perm = APR_OS_DEFAULT; int j; /* Try to make the new directory. If this fails because the directory already exists, check our FORCE flag to see if we care. */ /* Keep the source directory's permissions if applicable. Skip retrieving the umask on windows. Apr does not implement setting filesystem privileges on Windows. Retrieving the file permissions with APR_FINFO_PROT | APR_FINFO_OWNER is documented to be 'incredibly expensive' */ #ifndef WIN32 if (revision->kind == svn_opt_revision_working) { apr_finfo_t finfo; SVN_ERR(svn_io_stat(&finfo, from_abspath, APR_FINFO_PROT, pool)); perm = finfo.protection; } #endif err = svn_io_dir_make(to_abspath, perm, pool); if (err) { if (! APR_STATUS_IS_EEXIST(err->apr_err)) return svn_error_trace(err); if (! force) SVN_ERR_W(err, _("Destination directory exists, and will not be " "overwritten unless forced")); else svn_error_clear(err); } SVN_ERR(svn_wc__node_get_children(&children, ctx->wc_ctx, from_abspath, FALSE, pool, pool)); iterpool = svn_pool_create(pool); for (j = 0; j < children->nelts; j++) { const char *child_abspath = APR_ARRAY_IDX(children, j, const char *); const char *child_name = svn_dirent_basename(child_abspath, NULL); const char *target_abspath; svn_node_kind_t child_kind; svn_pool_clear(iterpool); if (ctx->cancel_func) SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); target_abspath = svn_dirent_join(to_abspath, child_name, iterpool); SVN_ERR(svn_wc_read_kind(&child_kind, ctx->wc_ctx, child_abspath, FALSE, iterpool)); if (child_kind == svn_node_dir) { if (depth == svn_depth_infinity || depth == svn_depth_immediates) { if (ctx->notify_func2) { svn_wc_notify_t *notify = svn_wc_create_notify(target_abspath, svn_wc_notify_update_add, pool); notify->kind = svn_node_dir; (*ctx->notify_func2)(ctx->notify_baton2, notify, pool); } if (depth == svn_depth_infinity) SVN_ERR(copy_versioned_files(child_abspath, target_abspath, revision, force, ignore_externals, ignore_keywords, depth, native_eol, ctx, iterpool)); else SVN_ERR(svn_io_make_dir_recursively(target_abspath, iterpool)); } } else if (child_kind == svn_node_file && depth >= svn_depth_files) { svn_node_kind_t external_kind; SVN_ERR(svn_wc__read_external_info(&external_kind, NULL, NULL, NULL, NULL, ctx->wc_ctx, child_abspath, child_abspath, TRUE, pool, pool)); if (external_kind != svn_node_file) SVN_ERR(copy_one_versioned_file(child_abspath, target_abspath, ctx, revision, native_eol, ignore_keywords, iterpool)); } } SVN_ERR(svn_wc__node_get_depth(&node_depth, ctx->wc_ctx, from_abspath, pool)); /* Handle externals. */ if (! ignore_externals && depth == svn_depth_infinity && node_depth == svn_depth_infinity) { apr_array_header_t *ext_items; const svn_string_t *prop_val; SVN_ERR(svn_wc_prop_get2(&prop_val, ctx->wc_ctx, from_abspath, SVN_PROP_EXTERNALS, pool, pool)); if (prop_val != NULL) { int i; SVN_ERR(svn_wc_parse_externals_description3(&ext_items, from_abspath, prop_val->data, FALSE, pool)); for (i = 0; i < ext_items->nelts; ++i) { svn_wc_external_item2_t *ext_item; const char *new_from, *new_to; svn_pool_clear(iterpool); ext_item = APR_ARRAY_IDX(ext_items, i, svn_wc_external_item2_t *); new_from = svn_dirent_join(from_abspath, ext_item->target_dir, iterpool); new_to = svn_dirent_join(to_abspath, ext_item->target_dir, iterpool); /* The target dir might have parents that don't exist. Guarantee the path upto the last component. */ if (!svn_dirent_is_root(ext_item->target_dir, strlen(ext_item->target_dir))) { const char *parent = svn_dirent_dirname(new_to, iterpool); SVN_ERR(svn_io_make_dir_recursively(parent, iterpool)); } SVN_ERR(copy_versioned_files(new_from, new_to, revision, force, FALSE, ignore_keywords, svn_depth_infinity, native_eol, ctx, iterpool)); } }
/* Write a single change entry, path PATH, change CHANGE, to STREAM. Only include the node kind field if INCLUDE_NODE_KIND is true. Only include the mergeinfo-mod field if INCLUDE_MERGEINFO_MODS is true. All temporary allocations are in SCRATCH_POOL. */ static svn_error_t * write_change_entry(svn_stream_t *stream, const char *path, svn_fs_path_change2_t *change, svn_boolean_t include_node_kind, svn_boolean_t include_mergeinfo_mods, apr_pool_t *scratch_pool) { const char *idstr; const char *change_string = NULL; const char *kind_string = ""; const char *mergeinfo_string = ""; svn_stringbuf_t *buf; apr_size_t len; switch (change->change_kind) { case svn_fs_path_change_modify: change_string = ACTION_MODIFY; break; case svn_fs_path_change_add: change_string = ACTION_ADD; break; case svn_fs_path_change_delete: change_string = ACTION_DELETE; break; case svn_fs_path_change_replace: change_string = ACTION_REPLACE; break; case svn_fs_path_change_reset: change_string = ACTION_RESET; break; default: return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, _("Invalid change type %d"), change->change_kind); } if (change->node_rev_id) idstr = svn_fs_fs__id_unparse(change->node_rev_id, scratch_pool)->data; else idstr = ACTION_RESET; if (include_node_kind) { SVN_ERR_ASSERT(change->node_kind == svn_node_dir || change->node_kind == svn_node_file); kind_string = apr_psprintf(scratch_pool, "-%s", change->node_kind == svn_node_dir ? SVN_FS_FS__KIND_DIR : SVN_FS_FS__KIND_FILE); } if (include_mergeinfo_mods && change->mergeinfo_mod != svn_tristate_unknown) mergeinfo_string = apr_psprintf(scratch_pool, " %s", change->mergeinfo_mod == svn_tristate_true ? FLAG_TRUE : FLAG_FALSE); buf = svn_stringbuf_createf(scratch_pool, "%s %s%s %s %s%s %s\n", idstr, change_string, kind_string, change->text_mod ? FLAG_TRUE : FLAG_FALSE, change->prop_mod ? FLAG_TRUE : FLAG_FALSE, mergeinfo_string, path); if (SVN_IS_VALID_REVNUM(change->copyfrom_rev)) { svn_stringbuf_appendcstr(buf, apr_psprintf(scratch_pool, "%ld %s", change->copyfrom_rev, change->copyfrom_path)); } svn_stringbuf_appendbyte(buf, '\n'); /* Write all change info in one write call. */ len = buf->len; return svn_error_trace(svn_stream_write(stream, buf->data, &len)); }
static svn_error_t * switch_internal(svn_revnum_t *result_rev, const char *local_abspath, const char *anchor_abspath, const char *switch_url, const svn_opt_revision_t *peg_revision, const svn_opt_revision_t *revision, svn_depth_t depth, svn_boolean_t depth_is_sticky, svn_boolean_t ignore_externals, svn_boolean_t allow_unver_obstructions, svn_boolean_t ignore_ancestry, svn_boolean_t *timestamp_sleep, svn_client_ctx_t *ctx, apr_pool_t *pool) { const svn_ra_reporter3_t *reporter; void *report_baton; const char *url, *target, *source_root, *switch_rev_url; svn_ra_session_t *ra_session; svn_revnum_t revnum; svn_error_t *err = SVN_NO_ERROR; const char *diff3_cmd; svn_boolean_t use_commit_times; svn_boolean_t sleep_here = FALSE; svn_boolean_t *use_sleep = timestamp_sleep ? timestamp_sleep : &sleep_here; const svn_delta_editor_t *switch_editor; void *switch_edit_baton; const char *preserved_exts_str; apr_array_header_t *preserved_exts; svn_boolean_t server_supports_depth; struct svn_client__dirent_fetcher_baton_t dfb; svn_config_t *cfg = ctx->config ? apr_hash_get(ctx->config, SVN_CONFIG_CATEGORY_CONFIG, APR_HASH_KEY_STRING) : NULL; /* An unknown depth can't be sticky. */ if (depth == svn_depth_unknown) depth_is_sticky = FALSE; /* Do not support the situation of both exclude and switch a target. */ if (depth == svn_depth_exclude) return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, _("Cannot both exclude and switch a path")); /* Get the external diff3, if any. */ svn_config_get(cfg, &diff3_cmd, SVN_CONFIG_SECTION_HELPERS, SVN_CONFIG_OPTION_DIFF3_CMD, NULL); if (diff3_cmd != NULL) SVN_ERR(svn_path_cstring_to_utf8(&diff3_cmd, diff3_cmd, pool)); /* See if the user wants last-commit timestamps instead of current ones. */ SVN_ERR(svn_config_get_bool(cfg, &use_commit_times, SVN_CONFIG_SECTION_MISCELLANY, SVN_CONFIG_OPTION_USE_COMMIT_TIMES, FALSE)); { svn_boolean_t has_working; SVN_ERR(svn_wc__node_has_working(&has_working, ctx->wc_ctx, local_abspath, pool)); if (has_working) return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, _("Cannot switch '%s' because it is not in the " "repository yet"), svn_dirent_local_style(local_abspath, pool)); } /* See which files the user wants to preserve the extension of when conflict files are made. */ svn_config_get(cfg, &preserved_exts_str, SVN_CONFIG_SECTION_MISCELLANY, SVN_CONFIG_OPTION_PRESERVED_CF_EXTS, ""); preserved_exts = *preserved_exts_str ? svn_cstring_split(preserved_exts_str, "\n\r\t\v ", FALSE, pool) : NULL; /* Sanity check. Without these, the switch is meaningless. */ SVN_ERR_ASSERT(switch_url && (switch_url[0] != '\0')); if (strcmp(local_abspath, anchor_abspath)) target = svn_dirent_basename(local_abspath, pool); else target = ""; SVN_ERR(svn_wc__node_get_url(&url, ctx->wc_ctx, anchor_abspath, pool, pool)); if (! url) return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL, _("Directory '%s' has no URL"), svn_dirent_local_style(anchor_abspath, pool)); /* We may need to crop the tree if the depth is sticky */ if (depth_is_sticky && depth < svn_depth_infinity) { svn_node_kind_t target_kind; if (depth == svn_depth_exclude) { SVN_ERR(svn_wc_exclude(ctx->wc_ctx, local_abspath, ctx->cancel_func, ctx->cancel_baton, ctx->notify_func2, ctx->notify_baton2, pool)); /* Target excluded, we are done now */ return SVN_NO_ERROR; } SVN_ERR(svn_wc_read_kind(&target_kind, ctx->wc_ctx, local_abspath, TRUE, pool)); if (target_kind == svn_node_dir) SVN_ERR(svn_wc_crop_tree2(ctx->wc_ctx, local_abspath, depth, ctx->cancel_func, ctx->cancel_baton, ctx->notify_func2, ctx->notify_baton2, pool)); } /* Open an RA session to 'source' URL */ SVN_ERR(svn_client__ra_session_from_path(&ra_session, &revnum, &switch_rev_url, switch_url, anchor_abspath, peg_revision, revision, ctx, pool)); SVN_ERR(svn_ra_get_repos_root2(ra_session, &source_root, pool)); /* Disallow a switch operation to change the repository root of the target. */ if (! svn_uri__is_ancestor(source_root, url)) return svn_error_createf(SVN_ERR_WC_INVALID_SWITCH, NULL, _("'%s'\nis not the same repository as\n'%s'"), url, source_root); /* If we're not ignoring ancestry, then error out if the switch source and target don't have a common ancestory. ### We're acting on the anchor here, not the target. Is that ### okay? */ if (! ignore_ancestry) { const char *target_url, *yc_path; svn_revnum_t target_rev, yc_rev; SVN_ERR(svn_wc__node_get_url(&target_url, ctx->wc_ctx, local_abspath, pool, pool)); SVN_ERR(svn_wc__node_get_base_rev(&target_rev, ctx->wc_ctx, local_abspath, pool)); /* ### It would be nice if this function could reuse the existing ra session instead of opening two for its own use. */ SVN_ERR(svn_client__get_youngest_common_ancestor(&yc_path, &yc_rev, switch_rev_url, revnum, target_url, target_rev, ctx, pool)); if (! (yc_path && SVN_IS_VALID_REVNUM(yc_rev))) return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL, _("'%s' shares no common ancestry with '%s'"), switch_url, local_abspath); } SVN_ERR(svn_ra_reparent(ra_session, url, pool)); /* Fetch the switch (update) editor. If REVISION is invalid, that's okay; the RA driver will call editor->set_target_revision() later on. */ SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth, SVN_RA_CAPABILITY_DEPTH, pool)); dfb.ra_session = ra_session; SVN_ERR(svn_ra_get_session_url(ra_session, &dfb.anchor_url, pool)); dfb.target_revision = revnum; SVN_ERR(svn_wc_get_switch_editor4(&switch_editor, &switch_edit_baton, &revnum, ctx->wc_ctx, anchor_abspath, target, switch_rev_url, use_commit_times, depth, depth_is_sticky, allow_unver_obstructions, server_supports_depth, diff3_cmd, preserved_exts, svn_client__dirent_fetcher, &dfb, ctx->conflict_func2, ctx->conflict_baton2, NULL, NULL, ctx->cancel_func, ctx->cancel_baton, ctx->notify_func2, ctx->notify_baton2, pool, pool)); /* Tell RA to do an update of URL+TARGET to REVISION; if we pass an invalid revnum, that means RA will use the latest revision. */ SVN_ERR(svn_ra_do_switch2(ra_session, &reporter, &report_baton, revnum, target, depth_is_sticky ? depth : svn_depth_unknown, switch_rev_url, switch_editor, switch_edit_baton, pool)); /* Drive the reporter structure, describing the revisions within PATH. When we call reporter->finish_report, the update_editor will be driven by svn_repos_dir_delta2. We pass in an external_func for recording all externals. It shouldn't be needed for a switch if it wasn't for the relative externals of type '../path'. All of those must be resolved to the new location. */ err = svn_wc_crawl_revisions5(ctx->wc_ctx, local_abspath, reporter, report_baton, TRUE, depth, (! depth_is_sticky), (! server_supports_depth), use_commit_times, ctx->cancel_func, ctx->cancel_baton, ctx->notify_func2, ctx->notify_baton2, pool); if (err) { /* Don't rely on the error handling to handle the sleep later, do it now */ svn_io_sleep_for_timestamps(local_abspath, pool); return svn_error_trace(err); } *use_sleep = TRUE; /* We handle externals after the switch is complete, so that handling external items (and any errors therefrom) doesn't delay the primary operation. */ if (SVN_DEPTH_IS_RECURSIVE(depth) && (! ignore_externals)) { apr_hash_t *new_externals; apr_hash_t *new_depths; SVN_ERR(svn_wc__externals_gather_definitions(&new_externals, &new_depths, ctx->wc_ctx, local_abspath, depth, pool, pool)); SVN_ERR(svn_client__handle_externals(new_externals, new_depths, source_root, local_abspath, depth, use_sleep, ctx, pool)); } /* Sleep to ensure timestamp integrity (we do this regardless of errors in the actual switch operation(s)). */ if (sleep_here) svn_io_sleep_for_timestamps(local_abspath, pool); /* Return errors we might have sustained. */ if (err) return svn_error_trace(err); /* Let everyone know we're finished here. */ if (ctx->notify_func2) { svn_wc_notify_t *notify = svn_wc_create_notify(anchor_abspath, svn_wc_notify_update_completed, pool); notify->kind = svn_node_none; notify->content_state = notify->prop_state = svn_wc_notify_state_inapplicable; notify->lock_state = svn_wc_notify_lock_state_inapplicable; notify->revision = revnum; (*ctx->notify_func2)(ctx->notify_baton2, notify, pool); } /* If the caller wants the result revision, give it to them. */ if (result_rev) *result_rev = revnum; return SVN_NO_ERROR; }
/* Core implementation of svn_fs_fs__open_pack_or_rev_file working on an * existing, initialized FILE structure. If WRITABLE is TRUE, give write * access to the file - temporarily resetting the r/o state if necessary. */ static svn_error_t * open_pack_or_rev_file(svn_fs_x__revision_file_t *file, svn_fs_t *fs, svn_revnum_t rev, svn_boolean_t writable, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { svn_error_t *err; svn_boolean_t retry = FALSE; do { const char *path = svn_fs_x__path_rev_absolute(fs, rev, scratch_pool); apr_file_t *apr_file; apr_int32_t flags = writable ? APR_READ | APR_WRITE | APR_BUFFERED : APR_READ | APR_BUFFERED; /* We may have to *temporarily* enable write access. */ err = writable ? auto_make_writable(path, result_pool, scratch_pool) : SVN_NO_ERROR; /* open the revision file in buffered r/o or r/w mode */ if (!err) err = svn_io_file_open(&apr_file, path, flags, APR_OS_DEFAULT, result_pool); if (!err) { file->file = apr_file; file->stream = svn_stream_from_aprfile2(apr_file, TRUE, result_pool); return SVN_NO_ERROR; } if (err && APR_STATUS_IS_ENOENT(err->apr_err)) { /* Could not open the file. This may happen if the * file once existed but got packed later. */ svn_error_clear(err); /* if that was our 2nd attempt, leave it at that. */ if (retry) return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL, _("No such revision %ld"), rev); /* We failed for the first time. Refresh cache & retry. */ SVN_ERR(svn_fs_x__update_min_unpacked_rev(fs, scratch_pool)); file->start_revision = svn_fs_x__packed_base_rev(fs, rev); retry = TRUE; } else { retry = FALSE; } } while (retry); return svn_error_trace(err); }