| /* |
| * reporter.c : `reporter' vtable routines for updates. |
| * |
| * ==================================================================== |
| * Copyright (c) 2000-2002 CollabNet. All rights reserved. |
| * |
| * This software is licensed as described in the file COPYING, which |
| * you should have received as part of this distribution. The terms |
| * are also available at http://subversion.tigris.org/license-1.html. |
| * If newer versions of this license are posted there, you may use a |
| * newer version instead, at your option. |
| * |
| * This software consists of voluntary contributions made by many |
| * individuals. For exact contribution history, see the revision |
| * history and logs, available at http://subversion.tigris.org/. |
| * ==================================================================== |
| */ |
| |
| #include "svn_path.h" |
| #include "svn_fs.h" |
| #include "svn_repos.h" |
| #include "repos.h" |
| |
| /* A structure used by the routines within the `reporter' vtable, |
| driven by the client as it describes its working copy revisions. */ |
| typedef struct svn_repos_report_baton_t |
| { |
| /* The transaction being built in the repository, a mirror of the |
| working copy. */ |
| svn_repos_t *repos; |
| svn_fs_txn_t *txn; |
| svn_fs_root_t *txn_root; |
| |
| /* Which user is doing the update (building the temporary txn) */ |
| const char *username; |
| |
| /* The fs path under which all reporting will happen */ |
| const char *base_path; |
| |
| /* The actual target of the report */ |
| svn_stringbuf_t *target; |
| |
| /* -- These items are used by finish_report() when it calls |
| svn_repos_dir_delta(): -- */ |
| |
| /* whether or not to generate text-deltas */ |
| svn_boolean_t text_deltas; |
| |
| /* which revision to compare against */ |
| svn_revnum_t revnum_to_update_to; |
| |
| /* The fs path that will be the 'target' of dir_delta. |
| In the case of 'svn switch', this is probably distinct from BASE_PATH. |
| In the case of 'svn update', this is probably identical to BASE_PATH */ |
| const char *tgt_path; |
| |
| /* Whether or not to recurse into the directories */ |
| svn_boolean_t recurse; |
| |
| /* the editor to drive */ |
| const svn_delta_edit_fns_t *update_editor; |
| void *update_edit_baton; |
| |
| /* This hash describes the mixed revisions in the transaction; it |
| maps pathnames (char *) to revision numbers (svn_revnum_t). */ |
| apr_hash_t *path_rev_hash; |
| |
| /* Pool from the session baton. */ |
| apr_pool_t *pool; |
| |
| } svn_repos_report_baton_t; |
| |
| |
| svn_error_t * |
| svn_repos_set_path (void *report_baton, |
| const char *path, |
| svn_revnum_t revision) |
| { |
| svn_fs_root_t *from_root; |
| svn_stringbuf_t *from_path; |
| svn_repos_report_baton_t *rbaton = report_baton; |
| svn_revnum_t *rev_ptr = apr_palloc (rbaton->pool, sizeof(*rev_ptr)); |
| |
| /* If this is the very first call, no txn exists yet. */ |
| if (! rbaton->txn) |
| { |
| /* ### need to change svn_path_is_empty() */ |
| svn_stringbuf_t *pathbuf = svn_stringbuf_create (path, rbaton->pool); |
| |
| /* Sanity check: make that we didn't call this with real data |
| before simply informing the reporter of our base revision. */ |
| if (! svn_path_is_empty (pathbuf)) |
| return |
| svn_error_create |
| (SVN_ERR_RA_BAD_REVISION_REPORT, 0, NULL, rbaton->pool, |
| "svn_repos_set_path: initial revision report was bogus."); |
| |
| /* Start a transaction based on REVISION. */ |
| SVN_ERR (svn_repos_fs_begin_txn_for_update (&(rbaton->txn), |
| rbaton->repos, |
| revision, |
| rbaton->username, |
| rbaton->pool)); |
| SVN_ERR (svn_fs_txn_root (&(rbaton->txn_root), rbaton->txn, |
| rbaton->pool)); |
| |
| /* In our hash, map the root of the txn ("") to the initial base |
| revision. */ |
| *rev_ptr = revision; |
| apr_hash_set (rbaton->path_rev_hash, "", APR_HASH_KEY_STRING, rev_ptr); |
| } |
| |
| else /* this is not the first call to set_path. */ |
| { |
| /* Create the "from" root and path. */ |
| SVN_ERR (svn_fs_revision_root (&from_root, rbaton->repos->fs, |
| revision, rbaton->pool)); |
| |
| /* The path we are dealing with is the anchor (where the |
| reporter is rooted) + target (the top-level thing being |
| reported) + path (stuff relative to the target...this is the |
| empty string in the file case since the target is the file |
| itself, not a directory containing the file). */ |
| from_path = svn_stringbuf_create (rbaton->base_path, rbaton->pool); |
| if (rbaton->target) |
| svn_path_add_component (from_path, rbaton->target); |
| svn_path_add_component_nts (from_path, path); |
| |
| /* Copy into our txn. */ |
| SVN_ERR (svn_fs_link (from_root, from_path->data, |
| rbaton->txn_root, from_path->data, rbaton->pool)); |
| |
| /* Remember this path in our hashtable. */ |
| *rev_ptr = revision; |
| apr_hash_set (rbaton->path_rev_hash, from_path->data, |
| from_path->len, rev_ptr); |
| |
| } |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| |
| svn_error_t * |
| svn_repos_delete_path (void *report_baton, |
| const char *path) |
| { |
| svn_stringbuf_t *delete_path; |
| svn_repos_report_baton_t *rbaton = report_baton; |
| |
| /* The path we are dealing with is the anchor (where the |
| reporter is rooted) + target (the top-level thing being |
| reported) + path (stuff relative to the target...this is the |
| empty string in the file case since the target is the file |
| itself, not a directory containing the file). */ |
| delete_path = svn_stringbuf_create (rbaton->base_path, rbaton->pool); |
| if (rbaton->target) |
| svn_path_add_component (delete_path, rbaton->target); |
| svn_path_add_component_nts (delete_path, path); |
| |
| |
| /* Remove the file or directory (recursively) from the txn. */ |
| SVN_ERR (svn_fs_delete_tree (rbaton->txn_root, delete_path->data, |
| rbaton->pool)); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| |
| |
| svn_error_t * |
| svn_repos_finish_report (void *report_baton) |
| { |
| svn_fs_root_t *rev_root; |
| svn_repos_report_baton_t *rbaton = (svn_repos_report_baton_t *) report_baton; |
| |
| /* If nothing was described, then we have an error */ |
| if (rbaton->txn == NULL) |
| return svn_error_create(SVN_ERR_REPOS_NO_DATA_FOR_REPORT, 0, NULL, |
| rbaton->pool, |
| "svn_repos_finish_report: no transaction was " |
| "present, meaning no data was provided."); |
| |
| /* Get the root of the revision we want to update to. */ |
| SVN_ERR (svn_fs_revision_root (&rev_root, rbaton->repos->fs, |
| rbaton->revnum_to_update_to, |
| rbaton->pool)); |
| |
| /* Drive the update-editor. */ |
| SVN_ERR (svn_repos_dir_delta (rbaton->txn_root, |
| rbaton->base_path, |
| rbaton->target ? |
| rbaton->target->data : NULL, |
| rbaton->path_rev_hash, |
| rev_root, |
| rbaton->tgt_path, |
| rbaton->update_editor, |
| rbaton->update_edit_baton, |
| rbaton->text_deltas, |
| rbaton->recurse, |
| FALSE, |
| rbaton->pool)); |
| |
| /* Still here? Great! Throw out the transaction. */ |
| SVN_ERR (svn_fs_abort_txn (rbaton->txn)); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| |
| svn_error_t * |
| svn_repos_abort_report (void *report_baton) |
| { |
| svn_repos_report_baton_t *rbaton = (svn_repos_report_baton_t *) report_baton; |
| |
| /* if we have a transaction, then abort it */ |
| if (rbaton->txn != NULL) |
| { |
| SVN_ERR (svn_fs_abort_txn (rbaton->txn)); |
| } |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| |
| |
| svn_error_t * |
| svn_repos_begin_report (void **report_baton, |
| svn_revnum_t revnum, |
| const char *username, |
| svn_repos_t *repos, |
| const char *fs_base, |
| const char *target, |
| const char *tgt_path, |
| svn_boolean_t text_deltas, |
| svn_boolean_t recurse, |
| const svn_delta_edit_fns_t *editor, |
| void *edit_baton, |
| apr_pool_t *pool) |
| { |
| svn_repos_report_baton_t *rbaton; |
| |
| /* Build a reporter baton. */ |
| rbaton = apr_pcalloc (pool, sizeof(*rbaton)); |
| rbaton->revnum_to_update_to = revnum; |
| rbaton->update_editor = editor; |
| rbaton->update_edit_baton = edit_baton; |
| rbaton->path_rev_hash = apr_hash_make (pool); |
| rbaton->repos = repos; |
| rbaton->text_deltas = text_deltas; |
| rbaton->recurse = recurse; |
| rbaton->pool = pool; |
| |
| /* Copy these since we're keeping them past the end of this function call. |
| We don't know what the caller might do with them after we return... */ |
| rbaton->username = apr_pstrdup (pool, username); |
| rbaton->base_path = apr_pstrdup (pool, fs_base); |
| rbaton->target = target ? svn_stringbuf_create (target, pool) : NULL; |
| rbaton->tgt_path = apr_pstrdup (pool, tgt_path); |
| |
| /* Hand reporter back to client. */ |
| *report_baton = rbaton; |
| return SVN_NO_ERROR; |
| } |
| |
| |
| |
| /* ---------------------------------------------------------------- |
| * local variables: |
| * eval: (load-file "../../tools/dev/svn-dev.el") |
| * end: */ |