Modified: subversion/branches/better-pristines/subversion/svn/cl.h URL: http://svn.apache.org/viewvc/subversion/branches/better-pristines/subversion/svn/cl.h?rev=1826407&r1=1826406&r2=1826407&view=diff ============================================================================== --- subversion/branches/better-pristines/subversion/svn/cl.h (original) +++ subversion/branches/better-pristines/subversion/svn/cl.h Sat Mar 10 15:27:24 2018 @@ -256,7 +256,7 @@ typedef struct svn_cl__opt_state_t const char *show_item; /* print only the given item */ svn_boolean_t adds_as_modification; /* update 'add vs add' no tree conflict */ svn_boolean_t vacuum_pristines; /* remove unreferenced pristines */ - svn_boolean_t list; + svn_boolean_t drop; /* drop shelf after successful unshelve */ svn_version_t *compatible_version; /* working copy compatibility version */ } svn_cl__opt_state_t; @@ -308,6 +308,7 @@ svn_opt_subcommand_t svn_cl__shelf_diff, svn_cl__shelf_drop, svn_cl__shelf_list, + svn_cl__shelf_list_by_paths, svn_cl__shelf_log, svn_cl__shelf_save, svn_cl__shelf_shelve, @@ -320,7 +321,7 @@ svn_opt_subcommand_t /* See definition in svn.c for documentation. */ -extern const svn_opt_subcommand_desc2_t svn_cl__cmd_table[]; +extern const svn_opt_subcommand_desc3_t svn_cl__cmd_table[]; /* See definition in svn.c for documentation. */ extern const int svn_cl__global_options[];
Modified: subversion/branches/better-pristines/subversion/svn/help-cmd.c URL: http://svn.apache.org/viewvc/subversion/branches/better-pristines/subversion/svn/help-cmd.c?rev=1826407&r1=1826406&r2=1826407&view=diff ============================================================================== --- subversion/branches/better-pristines/subversion/svn/help-cmd.c (original) +++ subversion/branches/better-pristines/subversion/svn/help-cmd.c Sat Mar 10 15:27:24 2018 @@ -203,7 +203,7 @@ svn_cl__help(apr_getopt_t *os, svn_stringbuf_appendcstr(version_footer, "* KWallet (KDE)\n"); #endif - return svn_opt_print_help4(os, + return svn_opt_print_help5(os, "svn", /* ### erm, derive somehow? */ opt_state ? opt_state->version : FALSE, opt_state ? opt_state->quiet : FALSE, Modified: subversion/branches/better-pristines/subversion/svn/shelf-cmd.c URL: http://svn.apache.org/viewvc/subversion/branches/better-pristines/subversion/svn/shelf-cmd.c?rev=1826407&r1=1826406&r2=1826407&view=diff ============================================================================== --- subversion/branches/better-pristines/subversion/svn/shelf-cmd.c (original) +++ subversion/branches/better-pristines/subversion/svn/shelf-cmd.c Sat Mar 10 15:27:24 2018 @@ -28,7 +28,9 @@ #include "svn_client.h" #include "svn_error_codes.h" #include "svn_error.h" +#include "svn_hash.h" #include "svn_path.h" +#include "svn_props.h" #include "svn_utf.h" #include "cl.h" @@ -53,6 +55,79 @@ get_next_argument(const char **arg, return SVN_NO_ERROR; } +/* Parse the remaining arguments as paths relative to a WC. + * + * TARGETS are relative to current working directory. + * + * Set *targets_by_wcroot to a hash mapping (char *)wcroot_abspath to + * (apr_array_header_t *)array of relpaths relative to that WC root. + */ +static svn_error_t * +targets_relative_to_wcs(apr_hash_t **targets_by_wcroot_p, + apr_array_header_t *targets, + svn_client_ctx_t *ctx, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_hash_t *targets_by_wcroot = apr_hash_make(result_pool); + int i; + + /* Make each target relative to the WC root. */ + for (i = 0; i < targets->nelts; i++) + { + const char *target = APR_ARRAY_IDX(targets, i, const char *); + const char *wcroot_abspath; + apr_array_header_t *paths; + + SVN_ERR(svn_dirent_get_absolute(&target, target, result_pool)); + SVN_ERR(svn_client_get_wc_root(&wcroot_abspath, target, + ctx, result_pool, scratch_pool)); + paths = svn_hash_gets(targets_by_wcroot, wcroot_abspath); + if (! paths) + { + paths = apr_array_make(result_pool, 0, sizeof(char *)); + svn_hash_sets(targets_by_wcroot, wcroot_abspath, paths); + } + target = svn_dirent_skip_ancestor(wcroot_abspath, target); + + if (target) + APR_ARRAY_PUSH(paths, const char *) = target; + } + *targets_by_wcroot_p = targets_by_wcroot; + return SVN_NO_ERROR; +} + +/* Return targets relative to a WC. Error if they refer to more than one WC. */ +static svn_error_t * +targets_relative_to_a_wc(const char **wc_root_abspath_p, + apr_array_header_t **paths_p, + apr_getopt_t *os, + const apr_array_header_t *known_targets, + svn_client_ctx_t *ctx, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_array_header_t *targets; + apr_hash_t *targets_by_wcroot; + apr_hash_index_t *hi; + + SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os, + known_targets, + ctx, FALSE, result_pool)); + svn_opt_push_implicit_dot_target(targets, result_pool); + + SVN_ERR(targets_relative_to_wcs(&targets_by_wcroot, targets, + ctx, result_pool, scratch_pool)); + if (apr_hash_count(targets_by_wcroot) != 1) + return svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL, + _("All targets must be in the same WC")); + + hi = apr_hash_first(scratch_pool, targets_by_wcroot); + *wc_root_abspath_p = apr_hash_this_key(hi); + *paths_p = apr_hash_this_val(hi); + return SVN_NO_ERROR; +} + /* Return a human-friendly description of DURATION. */ static char * @@ -516,8 +591,31 @@ shelve(int *new_version_p, dry_run, scratch_pool)); } - SVN_ERR(svn_client_shelf_set_log_message(shelf, revprop_table, - dry_run, scratch_pool)); + /* Fetch the log message and any other revprops */ + if (ctx->log_msg_func3) + { + const char *tmp_file; + apr_array_header_t *commit_items + = apr_array_make(scratch_pool, 1, sizeof(void *)); + const char *message = ""; + + SVN_ERR(ctx->log_msg_func3(&message, &tmp_file, commit_items, + ctx->log_msg_baton3, scratch_pool)); + /* Abort the shelving if the log message callback requested so. */ + if (! message) + return SVN_NO_ERROR; + + if (message && !dry_run) + { + svn_string_t *propval = svn_string_create(message, scratch_pool); + + if (! revprop_table) + revprop_table = apr_hash_make(scratch_pool); + svn_hash_sets(revprop_table, SVN_PROP_REVISION_LOG, propval); + } + } + + SVN_ERR(svn_client_shelf_revprop_set_all(shelf, revprop_table, scratch_pool)); if (new_version_p) *new_version_p = shelf->max_version; @@ -583,6 +681,27 @@ check_no_modified_paths(const char *path return SVN_NO_ERROR; } +/* Intercept patch notifications to detect when there is a conflict */ +struct patch_notify_baton_t +{ + svn_wc_notify_func2_t notify_func; + void *notify_baton; + svn_boolean_t rejects; +}; + +/* Intercept patch notifications to detect when there is a conflict */ +static void +patch_notify(void *baton, + const svn_wc_notify_t *notify, + apr_pool_t *pool) +{ + struct patch_notify_baton_t *b = baton; + + if (notify->action == svn_wc_notify_patch_rejected_hunk) + b->rejects = TRUE; + b->notify_func(b->notify_baton, notify, pool); +} + /** Restore/unshelve a given or newest version of changes. * * Restore local modifications from shelf @a name version @a arg, @@ -603,6 +722,7 @@ shelf_restore(const char *name, apr_time_t time_now = apr_time_now(); svn_client_shelf_t *shelf; svn_client_shelf_version_t *shelf_version; + struct patch_notify_baton_t b; SVN_ERR(svn_client_shelf_open_existing(&shelf, name, local_abspath, ctx, scratch_pool)); @@ -631,8 +751,22 @@ shelf_restore(const char *name, SVN_ERR(check_no_modified_paths(shelf->wc_root_abspath, shelf_version, quiet, ctx, scratch_pool)); + b.rejects = FALSE; + b.notify_func = ctx->notify_func2; + b.notify_baton = ctx->notify_baton2; + ctx->notify_func2 = patch_notify; + ctx->notify_baton2 = &b; + SVN_ERR(svn_client_shelf_apply(shelf_version, dry_run, scratch_pool)); + ctx->notify_func2 = b.notify_func; + ctx->notify_baton2 = b.notify_baton; + + if (b.rejects) + { + return svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL, + _("Unshelve/restore failed due to conflicts")); + } if (! dry_run) { @@ -859,6 +993,12 @@ svn_cl__shelf_unshelve(apr_getopt_t *os, opt_state->dry_run, opt_state->quiet, local_abspath, ctx, scratch_pool)); + if (opt_state->drop) + { + SVN_ERR(shelf_drop(name, local_abspath, + opt_state->dry_run, opt_state->quiet, + ctx, scratch_pool)); + } return SVN_NO_ERROR; } @@ -885,6 +1025,112 @@ svn_cl__shelf_list(apr_getopt_t *os, return SVN_NO_ERROR; } +/* "svn shelf-list-by-paths [PATH...]" + * + * TARGET_RELPATHS are all within the same WC, relative to WC_ROOT_ABSPATH. + */ +static svn_error_t * +shelf_list_by_paths(apr_array_header_t *target_relpaths, + const char *wc_root_abspath, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool) +{ + apr_array_header_t *shelves; + apr_hash_t *paths_to_shelf_name = apr_hash_make(scratch_pool); + apr_array_header_t *array; + int i; + + SVN_ERR(list_sorted_by_date(&shelves, + wc_root_abspath, ctx, scratch_pool)); + + /* Check paths are valid */ + for (i = 0; i < target_relpaths->nelts; i++) + { + char *target_relpath = APR_ARRAY_IDX(target_relpaths, i, char *); + + if (svn_path_is_url(target_relpath)) + return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, + _("'%s' is not a local path"), target_relpath); + SVN_ERR_ASSERT(svn_relpath_is_canonical(target_relpath)); + } + + /* Find the most recent shelf for each affected path */ + for (i = 0; i < shelves->nelts; i++) + { + svn_sort__item_t *item = &APR_ARRAY_IDX(shelves, i, svn_sort__item_t); + const char *name = item->key; + svn_client_shelf_t *shelf; + svn_client_shelf_version_t *shelf_version; + apr_hash_t *shelf_paths; + int j; + + SVN_ERR(svn_client_shelf_open_existing(&shelf, + name, wc_root_abspath, + ctx, scratch_pool)); + SVN_ERR(svn_client_shelf_version_open(&shelf_version, + shelf, shelf->max_version, + scratch_pool, scratch_pool)); + SVN_ERR(svn_client_shelf_paths_changed(&shelf_paths, + shelf_version, + scratch_pool, scratch_pool)); + for (j = 0; j < target_relpaths->nelts; j++) + { + char *target_relpath = APR_ARRAY_IDX(target_relpaths, j, char *); + apr_hash_index_t *hi; + + for (hi = apr_hash_first(scratch_pool, shelf_paths); + hi; hi = apr_hash_next(hi)) + { + const char *shelf_path = apr_hash_this_key(hi); + + if (svn_relpath_skip_ancestor(target_relpath, shelf_path)) + { + if (! svn_hash_gets(paths_to_shelf_name, shelf_path)) + { + svn_hash_sets(paths_to_shelf_name, shelf_path, shelf->name); + } + } + } + } + } + + /* Print the results. */ + array = svn_sort__hash(paths_to_shelf_name, + svn_sort_compare_items_as_paths, + scratch_pool); + for (i = 0; i < array->nelts; i++) + { + svn_sort__item_t *item = &APR_ARRAY_IDX(array, i, svn_sort__item_t); + const char *path = item->key; + const char *name = item->value; + + SVN_ERR(svn_cmdline_printf(scratch_pool, "%-20.20s %s\n", + name, + svn_dirent_local_style(path, scratch_pool))); + } + return SVN_NO_ERROR; +} + +/* This implements the `svn_opt_subcommand_t' interface. */ +svn_error_t * +svn_cl__shelf_list_by_paths(apr_getopt_t *os, + void *baton, + apr_pool_t *pool) +{ + svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state; + svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx; + const char *wc_root_abspath; + apr_array_header_t *targets; + + /* Parse the remaining arguments as paths. */ + SVN_ERR(targets_relative_to_a_wc(&wc_root_abspath, &targets, + os, opt_state->targets, + ctx, pool, pool)); + + SVN_ERR(shelf_list_by_paths(targets, wc_root_abspath, ctx, pool)); + return SVN_NO_ERROR; +} + /* This implements the `svn_opt_subcommand_t' interface. */ svn_error_t * svn_cl__shelf_diff(apr_getopt_t *os,
