/* * changelist.c: implementation of the 'changelist' command * * ==================================================================== * Copyright (c) 2006-2007 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/. * ==================================================================== */ /* ==================================================================== */ /*** Includes. ***/ #include "svn_client.h" #include "svn_wc.h" #include "svn_pools.h" #include "svn_path.h" #include "svn_hash.h" #include "client.h" #include "private/svn_wc_private.h" /* Entry-walker callback for svn_client_add_to_changelist() and svn_client_remove_from_changelist() below. */ struct set_cl_fe_baton { svn_wc_adm_access_t *adm_access; const char *changelist; /* NULL if removing changelists */ apr_hash_t *changelist_hash; svn_client_ctx_t *ctx; apr_pool_t *pool; }; /* This function -- which implements the 'found_entry' vtable member of svn_wc_entry_callbacks2_t -- associates PATH (via its ENTRY) with a new changelist (passed along in BATON->changelist), so long as ENTRY is deemed a valid target of that association. */ static svn_error_t * set_entry_changelist(const char *path, const svn_wc_entry_t *entry, void *baton, apr_pool_t *pool) { struct set_cl_fe_baton *b = (struct set_cl_fe_baton *)baton; svn_wc_adm_access_t *adm_access; /* See if this entry passes our changelist filtering. */ if (! SVN_WC__CL_MATCH(b->changelist_hash, entry)) return SVN_NO_ERROR; /* We only care about files right now. */ if (entry->kind != svn_node_file) { if ((strcmp(SVN_WC_ENTRY_THIS_DIR, entry->name) == 0) && (b->ctx->notify_func2)) b->ctx->notify_func2(b->ctx->notify_baton2, svn_wc_create_notify(path, svn_wc_notify_skip, pool), pool); return SVN_NO_ERROR; } /* Get the ADM_ACCESS for our file's parent directory, specifically. */ SVN_ERR(svn_wc_adm_retrieve(&adm_access, b->adm_access, svn_path_dirname(path, pool), pool)); return svn_wc_set_changelist(path, b->changelist, adm_access, b->ctx->cancel_func, b->ctx->cancel_baton, b->ctx->notify_func2, b->ctx->notify_baton2, pool); } static const svn_wc_entry_callbacks2_t set_cl_entry_callbacks = { set_entry_changelist, svn_client__default_walker_error_handler }; svn_error_t * svn_client_add_to_changelist(const apr_array_header_t *paths, const char *changelist, svn_depth_t depth, const apr_array_header_t *changelists, svn_client_ctx_t *ctx, apr_pool_t *pool) { /* ### Someday this routine might use a different underlying API to ### to make the associations in a centralized database. */ apr_pool_t *subpool = svn_pool_create(pool); apr_hash_t *changelist_hash = NULL; int i; if (changelists && changelists->nelts) SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelists, pool)); for (i = 0; i < paths->nelts; i++) { struct set_cl_fe_baton seb; svn_wc_adm_access_t *adm_access; const char *path = APR_ARRAY_IDX(paths, i, const char *); svn_pool_clear(subpool); SVN_ERR(svn_wc_adm_probe_open3(&adm_access, NULL, path, TRUE, /* write lock */ -1, /* infinity */ ctx->cancel_func, ctx->cancel_baton, subpool)); seb.adm_access = adm_access; seb.changelist = changelist; seb.changelist_hash = changelist_hash; seb.ctx = ctx; seb.pool = subpool; SVN_ERR(svn_wc_walk_entries3(path, adm_access, &set_cl_entry_callbacks, &seb, depth, FALSE, /* no hidden entries */ ctx->cancel_func, ctx->cancel_baton, subpool)); SVN_ERR(svn_wc_adm_close(adm_access)); } svn_pool_destroy(subpool); return SVN_NO_ERROR; } svn_error_t * svn_client_remove_from_changelists(const apr_array_header_t *paths, svn_depth_t depth, const apr_array_header_t *changelists, svn_client_ctx_t *ctx, apr_pool_t *pool) { /* ### Someday this routine might use a different underlying API to ### to make the associations in a centralized database. */ apr_pool_t *subpool = svn_pool_create(pool); apr_hash_t *changelist_hash = NULL; int i; if (changelists && changelists->nelts) SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelists, pool)); for (i = 0; i < paths->nelts; i++) { struct set_cl_fe_baton seb; svn_wc_adm_access_t *adm_access; const char *path = APR_ARRAY_IDX(paths, i, const char *); svn_pool_clear(subpool); SVN_ERR(svn_wc_adm_probe_open3(&adm_access, NULL, path, TRUE, /* write lock */ -1, /* infinity */ ctx->cancel_func, ctx->cancel_baton, subpool)); seb.adm_access = adm_access; seb.changelist = NULL; seb.changelist_hash = changelist_hash; seb.ctx = ctx; seb.pool = subpool; SVN_ERR(svn_wc_walk_entries3(path, adm_access, &set_cl_entry_callbacks, &seb, depth, FALSE, /* no hidden entries */ ctx->cancel_func, ctx->cancel_baton, subpool)); SVN_ERR(svn_wc_adm_close(adm_access)); } svn_pool_destroy(subpool); return SVN_NO_ERROR; } /* Entry-walker callback for svn_client_get_changelist() below. */ struct get_cl_fe_baton { svn_changelist_receiver_t callback_func; void *callback_baton; apr_hash_t *changelists; apr_pool_t *pool; }; static svn_error_t * get_entry_changelist(const char *path, const svn_wc_entry_t *entry, void *baton, apr_pool_t *pool) { struct get_cl_fe_baton *b = (struct get_cl_fe_baton *)baton; /* If the entry has a changelist, and is a file or is the "this-dir" entry for directory, and the changelist matches one that we're looking for (or we aren't looking for any in particular)... */ if (SVN_WC__CL_MATCH(b->changelists, entry) && ((entry->kind == svn_node_file) || ((entry->kind == svn_node_dir) && (strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR) == 0)))) { /* ...then call the callback function. */ SVN_ERR(b->callback_func(b->callback_baton, path, entry->changelist, pool)); } return SVN_NO_ERROR; } static const svn_wc_entry_callbacks2_t get_cl_entry_callbacks = { get_entry_changelist, svn_client__default_walker_error_handler }; svn_error_t * svn_client_get_changelists(const char *path, const apr_array_header_t *changelists, svn_depth_t depth, svn_changelist_receiver_t callback_func, void *callback_baton, svn_client_ctx_t *ctx, apr_pool_t *pool) { struct get_cl_fe_baton geb; svn_wc_adm_access_t *adm_access; geb.callback_func = callback_func; geb.callback_baton = callback_baton; geb.pool = pool; if (changelists) SVN_ERR(svn_hash_from_cstring_keys(&(geb.changelists), changelists, pool)); else geb.changelists = NULL; SVN_ERR(svn_wc_adm_probe_open3(&adm_access, NULL, path, FALSE, /* no write lock */ -1, /* levels to lock == infinity */ ctx->cancel_func, ctx->cancel_baton, pool)); SVN_ERR(svn_wc_walk_entries3(path, adm_access, &get_cl_entry_callbacks, &geb, depth, FALSE, /* don't show hidden entries */ ctx->cancel_func, ctx->cancel_baton, pool)); SVN_ERR(svn_wc_adm_close(adm_access)); return SVN_NO_ERROR; }