📄 copy.c
字号:
apr_array_header_t *commit_items = apr_array_make(pool, 2, sizeof(item)); item = apr_pcalloc(pool, sizeof(*item)); item->url = svn_path_join(top_url, dst_rel, pool); item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD; APR_ARRAY_PUSH(commit_items, svn_client_commit_item2_t *) = item; if (is_move && (! resurrection)) { item = apr_pcalloc(pool, sizeof(*item)); item->url = svn_path_join(top_url, src_rel, pool); item->state_flags = SVN_CLIENT_COMMIT_ITEM_DELETE; APR_ARRAY_PUSH(commit_items, svn_client_commit_item2_t *) = item; } SVN_ERR(svn_client__get_log_msg(&message, &tmp_file, commit_items, ctx, pool)); if (! message) return SVN_NO_ERROR; } else message = ""; /* Fetch RA commit editor. */ SVN_ERR(svn_client__commit_get_baton(&commit_baton, commit_info_p, pool)); SVN_ERR(svn_ra_get_commit_editor2(ra_session, &editor, &edit_baton, message, svn_client__commit_callback, commit_baton, NULL, TRUE, /* No lock tokens */ pool)); /* Setup our PATHS for the path-based editor drive. */ APR_ARRAY_PUSH(paths, const char *) = dst_rel; if (is_move && (! resurrection)) APR_ARRAY_PUSH(paths, const char *) = src_rel; /* Setup the callback baton. */ cb_baton.editor = editor; cb_baton.edit_baton = edit_baton; cb_baton.src_kind = src_kind; cb_baton.src_url = src_url; cb_baton.src_path = src_rel; cb_baton.dst_path = dst_rel; cb_baton.is_move = is_move; cb_baton.src_revnum = src_revnum; cb_baton.resurrection = resurrection; /* Call the path-based editor driver. */ err = svn_delta_path_driver(editor, edit_baton, youngest, paths, path_driver_cb_func, &cb_baton, pool); if (err) { /* At least try to abort the edit (and fs txn) before throwing err. */ svn_error_clear(editor->abort_edit(edit_baton, pool)); return err; } /* Close the edit. */ SVN_ERR(editor->close_edit(edit_baton, pool)); return SVN_NO_ERROR;}static svn_error_t *remove_tmpfiles(apr_hash_t *tempfiles, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *pool){ apr_hash_index_t *hi; /* Split if there's nothing to be done. */ if (! tempfiles) return SVN_NO_ERROR; /* Clean up any tempfiles. */ for (hi = apr_hash_first(pool, tempfiles); hi; hi = apr_hash_next(hi)) { const void *key; apr_ssize_t keylen; void *val; svn_node_kind_t kind; if (cancel_func) SVN_ERR(cancel_func(cancel_baton)); apr_hash_this(hi, &key, &keylen, &val); SVN_ERR(svn_io_check_path((const char *)key, &kind, pool)); if (kind == svn_node_file) SVN_ERR(svn_io_remove_file((const char *)key, pool)); } return SVN_NO_ERROR;}static svn_error_t *reconcile_errors(svn_error_t *commit_err, svn_error_t *unlock_err, svn_error_t *cleanup_err, apr_pool_t *pool){ svn_error_t *err; /* Early release (for good behavior). */ if (! (commit_err || unlock_err || cleanup_err)) return SVN_NO_ERROR; /* If there was a commit error, start off our error chain with that. */ if (commit_err) { commit_err = svn_error_quick_wrap (commit_err, _("Commit failed (details follow):")); err = commit_err; } /* Else, create a new "general" error that will lead off the errors that follow. */ else err = svn_error_create(SVN_ERR_BASE, NULL, _("Commit succeeded, but other errors follow:")); /* If there was an unlock error... */ if (unlock_err) { /* Wrap the error with some headers. */ unlock_err = svn_error_quick_wrap (unlock_err, _("Error unlocking locked dirs (details follow):")); /* Append this error to the chain. */ svn_error_compose(err, unlock_err); } /* If there was a cleanup error... */ if (cleanup_err) { /* Wrap the error with some headers. */ cleanup_err = svn_error_quick_wrap (cleanup_err, _("Error in post-commit clean-up (details follow):")); /* Append this error to the chain. */ svn_error_compose(err, cleanup_err); } return err;}static svn_error_t *wc_to_repos_copy(svn_commit_info_t **commit_info_p, const char *src_path, const char *dst_url, svn_client_ctx_t *ctx, apr_pool_t *pool){ const char *anchor, *target, *message; svn_ra_session_t *ra_session; const svn_delta_editor_t *editor; void *edit_baton; svn_node_kind_t src_kind, dst_kind; void *commit_baton; apr_hash_t *committables, *tempfiles = NULL; svn_wc_adm_access_t *adm_access, *dir_access; apr_array_header_t *commit_items; svn_error_t *cmt_err = SVN_NO_ERROR; svn_error_t *unlock_err = SVN_NO_ERROR; svn_error_t *cleanup_err = SVN_NO_ERROR; const char *base_path; /* The commit process uses absolute paths, so we need to open the access baton using absolute paths, and so we really need to use absolute paths everywhere. */ SVN_ERR(svn_path_get_absolute(&base_path, src_path, pool)); SVN_ERR(svn_wc_adm_probe_open3(&adm_access, NULL, base_path, FALSE, -1, ctx->cancel_func, ctx->cancel_baton, pool)); /* Split the DST_URL into an anchor and target. */ svn_path_split(dst_url, &anchor, &target, pool); /* Open an RA session for the anchor URL. */ SVN_ERR(svn_client__open_ra_session_internal(&ra_session, anchor, svn_wc_adm_access_path (adm_access), adm_access, NULL, TRUE, TRUE, ctx, pool)); /* Figure out the basename that will result from this operation. */ SVN_ERR(svn_ra_check_path(ra_session, svn_path_uri_decode(target, pool), SVN_INVALID_REVNUM, &dst_kind, pool)); if (dst_kind != svn_node_none) { return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL, _("Path '%s' already exists"), dst_url); } /* Create a new commit item and add it to the array. */ if (ctx->log_msg_func || ctx->log_msg_func2) { svn_client_commit_item2_t *item; const char *tmp_file; commit_items = apr_array_make(pool, 1, sizeof(item)); item = apr_pcalloc(pool, sizeof(*item)); item->url = dst_url; item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD; APR_ARRAY_PUSH(commit_items, svn_client_commit_item2_t *) = item; SVN_ERR(svn_client__get_log_msg(&message, &tmp_file, commit_items, ctx, pool)); if (! message) return SVN_NO_ERROR; } else message = ""; /* Crawl the working copy for commit items. */ SVN_ERR(svn_io_check_path(base_path, &src_kind, pool)); if (src_kind == svn_node_dir) SVN_ERR(svn_wc_adm_retrieve(&dir_access, adm_access, base_path, pool)); else dir_access = adm_access; if ((cmt_err = svn_client__get_copy_committables(&committables, dst_url, base_path, dir_access, ctx, pool))) goto cleanup; /* ### todo: There should be only one hash entry, which currently has a hacked name until we have the entries files storing canonical repository URLs. Then, the hacked name can go away and be replaced with a entry->repos (or whereever the entry's canonical repos URL is stored). */ if (! ((commit_items = apr_hash_get(committables, SVN_CLIENT__SINGLE_REPOS_NAME, APR_HASH_KEY_STRING)))) goto cleanup; /* Sort and condense our COMMIT_ITEMS. */ if ((cmt_err = svn_client__condense_commit_items(&dst_url, commit_items, pool))) goto cleanup; /* Open an RA session to DST_URL. */ if ((cmt_err = svn_client__open_ra_session_internal(&ra_session, dst_url, NULL, NULL, commit_items, FALSE, FALSE, ctx, pool))) goto cleanup; /* Fetch RA commit editor. */ SVN_ERR(svn_client__commit_get_baton(&commit_baton, commit_info_p, pool)); if ((cmt_err = svn_ra_get_commit_editor2(ra_session, &editor, &edit_baton, message, svn_client__commit_callback, commit_baton, NULL, TRUE, /* No lock tokens */ pool))) goto cleanup; /* Perform the commit. */ cmt_err = svn_client__do_commit(dst_url, commit_items, adm_access, editor, edit_baton, 0, /* ### any notify_path_offset needed? */ &tempfiles, NULL, ctx, pool); /* Sleep to ensure timestamp integrity. */ svn_sleep_for_timestamps(); cleanup: /* It's only a read lock, so unlocking is harmless. */ unlock_err = svn_wc_adm_close(adm_access); /* Remove any outstanding temporary text-base files. */ if (tempfiles) cleanup_err = remove_tmpfiles(tempfiles, ctx->cancel_func, ctx->cancel_baton, pool); return reconcile_errors(cmt_err, unlock_err, cleanup_err, pool);}static svn_error_t *repos_to_wc_copy(const char *src_url, const svn_opt_revision_t *src_revision, const char *dst_path, svn_client_ctx_t *ctx, apr_pool_t *pool){ svn_ra_session_t *ra_session; svn_node_kind_t src_kind, dst_kind, dst_parent_kind; svn_revnum_t src_revnum; svn_wc_adm_access_t *adm_access; const char *dst_parent; const char *src_uuid = NULL, *dst_uuid = NULL; svn_boolean_t same_repositories; svn_opt_revision_t revision; /* Open a repository session to the given URL. We do not (yet) have a working copy, so we don't have a corresponding path and tempfiles cannot go into the admin area. */ SVN_ERR(svn_client__open_ra_session_internal(&ra_session, src_url, NULL, NULL, NULL, FALSE, TRUE, ctx, pool)); /* Pass null for the path, to ensure error if trying to get a revision based on the working copy. And additionally, we can't pass an 'unspecified' revnum to the update reporter; assume HEAD if not specified. */ revision.kind = src_revision->kind; revision.value = src_revision->value; if (revision.kind == svn_opt_revision_unspecified) revision.kind = svn_opt_revision_head; SVN_ERR(svn_client__get_revision_number (&src_revnum, ra_session, &revision, NULL, pool)); /* Verify that SRC_URL exists in the repository. */ SVN_ERR(svn_ra_check_path(ra_session, "", src_revnum, &src_kind, pool)); if (src_kind == svn_node_none) { if (SVN_IS_VALID_REVNUM(src_revnum)) return svn_error_createf (SVN_ERR_FS_NOT_FOUND, NULL, _("Path '%s' not found in revision %ld"), src_url, src_revnum); else return svn_error_createf (SVN_ERR_FS_NOT_FOUND, NULL, _("Path '%s' not found in head revision"), src_url); } /* First, figure out about dst. */ SVN_ERR(svn_io_check_path(dst_path, &dst_kind, pool)); if (dst_kind != svn_node_none) { return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL, _("Path '%s' already exists"), svn_path_local_style(dst_path, pool)); } /* Make sure the destination parent is a directory and produce a clear error message if it is not. */ dst_parent = svn_path_dirname(dst_path, pool); SVN_ERR(svn_io_check_path(svn_path_dirname(dst_path, pool), &dst_parent_kind, pool)); if (dst_parent_kind != svn_node_dir) return svn_error_createf(SVN_ERR_WC_NOT_DIRECTORY, NULL, _("Path '%s' is not a directory"), svn_path_local_style(dst_parent, pool)); SVN_ERR(svn_wc_adm_probe_open3(&adm_access, NULL, dst_path, TRUE, 0, ctx->cancel_func, ctx->cancel_baton, pool)); /* We've already checked for physical obstruction by a working file. But there could also be logical obstruction by an entry whose working file happens to be missing.*/ { const svn_wc_entry_t *ent; SVN_ERR(svn_wc_entry(&ent, dst_path, adm_access, FALSE, pool)); if (ent && (ent->kind != svn_node_dir) && (ent->schedule != svn_wc_schedule_delete)) return svn_error_createf (SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, _("Entry for '%s' exists (though the working file is missing)"), svn_path_local_style(dst_path, pool)); } /* Decide whether the two repositories are the same or not. */ { svn_error_t *src_err, *dst_err; const char *parent; /* Get the repository uuid of SRC_URL */ src_err = svn_ra_get_uuid(ra_session, &src_uuid, pool); if (src_err && src_err->apr_err != SVN_ERR_RA_NO_REPOS_UUID) return src_err; /* Get repository uuid of dst's parent directory, since dst may not exist. ### TODO: we should probably walk up the wc here, in case the parent dir has an imaginary URL. */ svn_path_split(dst_path, &parent, NULL, pool); dst_err = svn_client_uuid_from_path(&dst_uuid, parent, adm_access, ctx, pool); if (dst_err && dst_err->apr_err != SVN_ERR_RA_NO_REPOS_UUID) return dst_err; /* If either of the UUIDs are nonexistent, then at least one of the repositories must be very old. Rather than punish the user, just assume the repositories are different, so no
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -