Philip Martin <philip.mar...@wandisco.com> writes: > Noorul Islam K M <noo...@collab.net> writes: > >> Philip Martin <philip.mar...@wandisco.com> writes: >> >>> Noorul Islam K M <noo...@collab.net> writes: >>> >>>> +static svn_error_t * >>>> +props_only_receiver(void *baton, svn_log_entry_t *log_entry, apr_pool_t >>>> *pool) >>>> +{ >>>> + props_only_receiver_baton_t *rb = baton; >>>> + >>>> + if (log_entry->changed_paths2) >>>> + { >>>> + apr_array_header_t *sorted_paths; >>>> + int i; >>>> + svn_boolean_t text_modified = FALSE; >>>> + >>>> + /* Get an array of sorted hash keys. */ >>>> + sorted_paths = svn_sort__hash(log_entry->changed_paths2, >>>> + svn_sort_compare_items_as_paths, >>>> pool); >>> >>> Why sort? >>> >> >> I just used an existing function. If this is going to be performance hit >> then I can modify this part. > > Sorting when not necessary is a performance penalty. Why not simply > iterate over the hash? > >> >>>> + >>>> + for (i = 0; i < sorted_paths->nelts; i++) >>>> + { >>>> + svn_sort__item_t *item = &(APR_ARRAY_IDX(sorted_paths, i, >>>> + svn_sort__item_t)); >>>> + svn_log_changed_path2_t *log_item >>>> + = apr_hash_get(log_entry->changed_paths2, item->key, >>>> item->klen); >>>> + >>>> + if (log_item->text_modified == svn_tristate_true) >>>> + { >>>> + text_modified = TRUE; >>>> + break; >>>> + } >>>> + >>>> + } >>>> + >>>> + if ((text_modified && rb->props_only) >>>> + || (! text_modified && rb->ignore_props_only)) >>>> + return SVN_NO_ERROR; >>>> + } >>>> + >>>> + if (! rb->discover_changed_paths) >>>> + log_entry->changed_paths2 = NULL; >>> >>> Set changed_paths as well? >>> >> >> This is necessary because, if the user is not passing the verbose option >> then they are not requesting this particular detail. >> >> But I have to pass discover_changed_paths as TRUE for these new options >> because changed_paths2 information is required to filter. > > If you set changed_paths2 to NULL I think you need to set changed_paths > to NULL as well.
Thank you! I agree with what you said. I updated the patch incorporating you review comments. Log [[[ Make 'svn log' take --ignore-props-only and --props-only options. If passed --ignore-props-only, log will ignore revisions that has only property changes. If passed --props-only, log will retrieve revisions that has only property changes. * subversion/svn/cl.h (svn_cl__opt_state_t): New props_only and ignore_props_only members. * subversion/svn/main.c (svn_cl__longopt_t): Add opt_props_only and opt_ignore_props_only members. (svn_cl__options): Document new flags. (svn_cl__cmd_table): Accept new flags for log operations. (main): If given --ignore-props-only/--props-only, set the option flags to TRUE. Disallow simultaneous use of both --ignore-props and --props-only. * subversion/include/svn_client.h (svn_client_log6): New log variant with new flags. (svn_client_log5): Deprecate. * subversion/libsvn_client/deprecated.c (svn_client_log5): New deprecated wrapper. * subversion/libsvn_client/log.c (props_only_receiver_baton_t): Struct for props_only_receiver. (props_only_receiver): Receiver which implements --props-only and --ignore-props-only functionality. (svn_client_log5): Rename. (svn_client_log6): New API with new flags. * subversion/svn/log-cmd.c (svn_cl__log): Call the new client API, with new flags. * subversion/tests/cmdline/log_tests.py (log_props_only): New test. (test_list): Add new test. * subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout (): add --props-only and --ignore-props-only help text. Patch by: Noorul Islam K M <noorul{_AT_}collab.net> ]]]
Index: subversion/tests/cmdline/log_tests.py =================================================================== --- subversion/tests/cmdline/log_tests.py (revision 1148663) +++ subversion/tests/cmdline/log_tests.py (working copy) @@ -2036,6 +2036,41 @@ svntest.actions.run_and_verify_svn(None, None, expected_error, 'log', '-q', bad_path_default_rev) +def log_props_only(sbox): + "svn log --ignore-props-only and --props-only" + sbox.build() + wc_dir = sbox.wc_dir + iota_file = os.path.join(wc_dir, 'iota') + svntest.main.run_svn(None, 'propset', 'foo', 'bar', iota_file) + svntest.main.run_svn(None, 'ci', '-m', + 'Set property "foo" to "bar" on A/iota', wc_dir) + + svntest.main.run_svn(None, 'propset', 'bar', 'foo', iota_file) + svntest.main.run_svn(None, 'ci', '-m', + 'Set property "bar" to "foo" on A/iota', wc_dir) + + svntest.main.file_write(iota_file, "Add dummy contents.\n") + svntest.main.run_svn(None, 'propset', 'dummy', 'dummy_value', iota_file) + svntest.main.run_svn(None, 'ci', '-m', + 'Modify iota and add property dummy', wc_dir) + svntest.main.run_svn(None, 'up', wc_dir) + + exit_code, output, error = svntest.main.run_svn(0, 'log', + '--ignore-props-only', + wc_dir) + + expected_output_re = re.compile(".*Set property.*") + if expected_output_re.search("".join(output), re.MULTILINE): + raise svntest.Failure('Log with --ignore-props-only failed') + + exit_code, output, error = svntest.main.run_svn(0, 'log', + '--props-only', + wc_dir) + + expected_output_re = re.compile(".*Modify iota.*revision 1.*") + if expected_output_re.search("".join(output), re.MULTILINE): + raise svntest.Failure('Log with --props-only failed') + ######################################################################## # Run the tests @@ -2075,6 +2110,7 @@ merge_sensitive_log_ignores_cyclic_merges, log_with_unrelated_peg_and_operative_revs, log_on_nonexistent_path_and_valid_rev, + log_props_only, ] if __name__ == '__main__': Index: subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout =================================================================== --- subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout (revision 1148663) +++ subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout (working copy) @@ -81,6 +81,8 @@ Ignore changes in EOL style. -p (--show-c-function): Show C function name in diff output. + --ignore-props-only : ignore property only revisions + --props-only : retrieve property only revisions Global options: --username ARG : specify a username ARG Index: subversion/svn/cl.h =================================================================== --- subversion/svn/cl.h (revision 1148663) +++ subversion/svn/cl.h (working copy) @@ -230,6 +230,10 @@ svn_boolean_t internal_diff; /* override diff_cmd in config file */ svn_boolean_t use_git_diff_format; /* Use git's extended diff format */ svn_boolean_t allow_mixed_rev; /* Allow operation on mixed-revision WC */ + svn_boolean_t ignore_props_only; /* Ignore revisions with only property + changes */ + svn_boolean_t props_only; /* Retrieve revisions with only property + changes */ } svn_cl__opt_state_t; Index: subversion/svn/log-cmd.c =================================================================== --- subversion/svn/log-cmd.c (revision 1148663) +++ subversion/svn/log-cmd.c (working copy) @@ -721,7 +721,7 @@ if (!opt_state->quiet) APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_LOG; } - SVN_ERR(svn_client_log5(targets, + SVN_ERR(svn_client_log6(targets, &peg_revision, opt_state->revision_ranges, opt_state->limit, @@ -729,6 +729,8 @@ opt_state->stop_on_copy, opt_state->use_merge_history, revprops, + opt_state->ignore_props_only, + opt_state->props_only, log_entry_receiver_xml, &lb, ctx, @@ -744,7 +746,7 @@ APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_DATE; if (!opt_state->quiet) APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_LOG; - SVN_ERR(svn_client_log5(targets, + SVN_ERR(svn_client_log6(targets, &peg_revision, opt_state->revision_ranges, opt_state->limit, @@ -752,6 +754,8 @@ opt_state->stop_on_copy, opt_state->use_merge_history, revprops, + opt_state->ignore_props_only, + opt_state->props_only, log_entry_receiver, &lb, ctx, Index: subversion/svn/main.c =================================================================== --- subversion/svn/main.c (revision 1148663) +++ subversion/svn/main.c (working copy) @@ -125,6 +125,8 @@ opt_internal_diff, opt_use_git_diff_format, opt_allow_mixed_revisions, + opt_ignore_props_only, + opt_props_only, } svn_cl__longopt_t; @@ -340,6 +342,10 @@ "Use of this option is not recommended!\n" " " "Please run 'svn update' instead.")}, + {"ignore-props-only", opt_ignore_props_only, 0, + N_("ignore property only revisions")}, + {"props-only", opt_props_only, 0, + N_("retrieve property only revisions")}, /* Long-opt Aliases * @@ -653,7 +659,8 @@ " svn log http://www.example.com/repo/project@50 foo.c bar.c\n"), {'r', 'q', 'v', 'g', 'c', opt_targets, opt_stop_on_copy, opt_incremental, opt_xml, 'l', opt_with_all_revprops, opt_with_no_revprops, opt_with_revprop, - opt_depth, opt_diff, opt_diff_cmd, opt_internal_diff, 'x'}, + opt_depth, opt_diff, opt_diff_cmd, opt_internal_diff, 'x', + opt_ignore_props_only, opt_props_only}, {{opt_with_revprop, N_("retrieve revision property ARG")}, {'c', N_("the change made in revision ARG")}} }, @@ -2025,6 +2032,12 @@ case opt_allow_mixed_revisions: opt_state.allow_mixed_rev = TRUE; break; + case opt_ignore_props_only: + opt_state.ignore_props_only = TRUE; + break; + case opt_props_only: + opt_state.props_only = TRUE; + break; default: /* Hmmm. Perhaps this would be a good place to squirrel away opts that commands like svn diff might need. Hmmm indeed. */ @@ -2204,6 +2217,16 @@ return svn_cmdline_handle_exit_error(err, pool, "svn: "); } + /* Disallow simultaneous use of both --ignore-props-only and + --props-only. */ + if (opt_state.props_only && opt_state.ignore_props_only) + { + err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--props-only and --ignore-props-only " + "are mutually exclusive")); + return svn_cmdline_handle_exit_error(err, pool, "svn: "); + } + /* Ensure that 'revision_ranges' has at least one item, and make 'start_revision' and 'end_revision' match that item. */ if (opt_state.revision_ranges->nelts == 0) Index: subversion/include/svn_client.h =================================================================== --- subversion/include/svn_client.h (revision 1148663) +++ subversion/include/svn_client.h (working copy) @@ -2436,6 +2436,12 @@ * If @a revprops is NULL, retrieve all revprops; else, retrieve only the * revprops named in the array (i.e. retrieve none if the array is empty). * + * If @a ignore_props_only is set, log information for revisions which + * has property changes alone will be ignored. + * + * If @a props_only is set, log information for revisions which + * has property changes alone will be returned. + * * Use @a pool for any temporary allocation. * * @par Important: @@ -2448,8 +2454,33 @@ * If @a ctx->notify_func2 is non-NULL, then call @a ctx->notify_func2/baton2 * with a 'skip' signal on any unversioned targets. * + * @since New in 1.8. + */ +svn_error_t * +svn_client_log6(const apr_array_header_t *targets, + const svn_opt_revision_t *peg_revision, + const apr_array_header_t *revision_ranges, + int limit, + svn_boolean_t discover_changed_paths, + svn_boolean_t strict_node_history, + svn_boolean_t include_merged_revisions, + const apr_array_header_t *revprops, + svn_boolean_t ignore_props_only, + svn_boolean_t props_only, + svn_log_entry_receiver_t receiver, + void *receiver_baton, + svn_client_ctx_t *ctx, + apr_pool_t *pool); + +/** + * Similar to svn_client_log6(), but with @a ignore_props_only + * and @a props_only always @c FALSE. + * + * @deprecated Provided for compatibility with the 1.6 API. + * * @since New in 1.6. */ +SVN_DEPRECATED svn_error_t * svn_client_log5(const apr_array_header_t *targets, const svn_opt_revision_t *peg_revision, Index: subversion/libsvn_client/deprecated.c =================================================================== --- subversion/libsvn_client/deprecated.c (revision 1148663) +++ subversion/libsvn_client/deprecated.c (working copy) @@ -1272,6 +1272,26 @@ /*** From log.c ***/ svn_error_t * +svn_client_log5(const apr_array_header_t *targets, + const svn_opt_revision_t *peg_revision, + const apr_array_header_t *revision_ranges, + int limit, + svn_boolean_t discover_changed_paths, + svn_boolean_t strict_node_history, + svn_boolean_t include_merged_revisions, + const apr_array_header_t *revprops, + svn_log_entry_receiver_t receiver, + void *receiver_baton, + svn_client_ctx_t *ctx, + apr_pool_t *pool) +{ + return svn_client_log6(targets, peg_revision, revision_ranges, limit, + discover_changed_paths, strict_node_history, + include_merged_revisions, revprops, FALSE, FALSE, + receiver, receiver_baton, ctx, pool); +} + +svn_error_t * svn_client_log4(const apr_array_header_t *targets, const svn_opt_revision_t *peg_revision, const svn_opt_revision_t *start, Index: subversion/libsvn_client/log.c =================================================================== --- subversion/libsvn_client/log.c (revision 1148663) +++ subversion/libsvn_client/log.c (working copy) @@ -262,11 +262,59 @@ } + +/* properties only receiver */ +typedef struct props_only_receiver_baton_t +{ + svn_boolean_t props_only; + svn_boolean_t ignore_props_only; + svn_boolean_t discover_changed_paths; + svn_log_entry_receiver_t receiver; + void *baton; +} props_only_receiver_baton_t; + +static svn_error_t * +props_only_receiver(void *baton, svn_log_entry_t *log_entry, apr_pool_t *pool) +{ + props_only_receiver_baton_t *rb = baton; + + if (log_entry->changed_paths2) + { + apr_hash_index_t *hi; + svn_boolean_t text_modified = FALSE; + + for (hi = apr_hash_first(pool, log_entry->changed_paths2); + hi; hi = apr_hash_next(hi)) + { + svn_log_changed_path2_t *log_item = svn__apr_hash_index_val(hi); + + if (log_item->text_modified == svn_tristate_true) + { + text_modified = TRUE; + break; + } + + } + + if ((text_modified && rb->props_only) + || (! text_modified && rb->ignore_props_only)) + return SVN_NO_ERROR; + } + + if (! rb->discover_changed_paths) + { + log_entry->changed_paths2 = NULL; + log_entry->changed_paths = NULL; + } + + return rb->receiver(rb->baton, log_entry, pool); +} + /*** Public Interface. ***/ svn_error_t * -svn_client_log5(const apr_array_header_t *targets, +svn_client_log6(const apr_array_header_t *targets, const svn_opt_revision_t *peg_revision, const apr_array_header_t *revision_ranges, int limit, @@ -274,6 +322,8 @@ svn_boolean_t strict_node_history, svn_boolean_t include_merged_revisions, const apr_array_header_t *revprops, + svn_boolean_t ignore_props_only, + svn_boolean_t props_only, svn_log_entry_receiver_t real_receiver, void *real_receiver_baton, svn_client_ctx_t *ctx, @@ -562,6 +612,7 @@ svn_log_entry_receiver_t passed_receiver; void *passed_receiver_baton; const apr_array_header_t *passed_receiver_revprops; + props_only_receiver_baton_t pob; svn_pool_clear(iterpool); @@ -608,6 +659,19 @@ passed_receiver_baton = &lb; } + if (ignore_props_only || props_only) + { + pob.props_only = props_only; + pob.ignore_props_only = ignore_props_only; + pob.discover_changed_paths = discover_changed_paths; + discover_changed_paths = TRUE; + pob.receiver = passed_receiver; + pob.baton = passed_receiver_baton; + + passed_receiver = props_only_receiver; + passed_receiver_baton = &pob; + } + SVN_ERR(svn_ra_get_log2(ra_session, condensed_targets, start_revnum,