The attached brute-force patch seems to fix many of the cases where changelist filtering was missing or wrong, but I don't much like it and haven't properly tested it.
I'm attaching it for us to consider, but not necessarily to commit. If anyone wants to kick it around a bit (test it) and report back, or preferably write some proper tests for it, that would be great. Bert Huijben wrote: > Julian Foad wrote: >> (In the longer term, I would like it if we could implement this changelist >> filtering in one place instead of scattering these "if" conditions around. >> I would like to see a code structure more analogous to "find /wc/path <node >> selection conditions: depth, changelists, etc.> | xargs diff", probably using >> the existing svn_wc__internal_walk_children() function or something similar >> to it.) > > You do know that this kind of restructuring would make it +- impossible to > handle tree changes? [...] No, I don't know that. > Personally I would wish we could just do the changelist filtering in the > output layer, instead of in all the diff drivers separately... But the > current code doesn't properly guarantee access to the local working copy > paths to do that kind of processing there. > > In many cases it prefers to just pass something url like. (Not necessary the > proper url) If filtering at the output layer is easier that's totally fine with me, anything as long as it's simple and consistent. Can we say the same thing in a more positive way: "It should be possible to do the filtering in the output layer. One thing that needs to be fixed to make this possible is that the WC paths need to be communicated consistently." - Julian
Fix 'changelist' filtering in repos-WC and WC-WC diffs. When a changelist was specified, the diff code was failing to filter out several kinds of changes, especially adds and deletes and directory property changes. In one common case when a changelist was specified, it crashed (reported by Peter Galcik). Note: I'm not confident that this patch covers all possible cases. It needs some tests, and preferably needs to be rewritten in a more modular form. * subversion/libsvn_wc/diff_editor.c (svn_wc__diff_base_working_diff): Fix a crash when attempting to use a changelist filter when the file is not a member of any changelist. (walk_local_nodes_diff): Apply changelist filtering to deletes. (svn_wc__diff_local_only_file): Fix changelist filtering: skip the locally added file if it is not a member of any changelist when changelist filtering is active. (svn_wc__diff_local_only_dir, svn_wc__diff_base_only_file, svn_wc__diff_base_only_dir): Implement changelist filtering. * subversion/libsvn_wc/diff.h (svn_wc__diff_base_only_file, svn_wc__diff_base_only_dir): Support changelist filtering. * subversion/libsvn_wc/diff_local.c (diff_status_callback): Apply changelist filtering to repository-only nodes. --This line, and those below, will be ignored-- Index: subversion/libsvn_wc/diff_editor.c =================================================================== --- subversion/libsvn_wc/diff_editor.c (revision 1621760) +++ subversion/libsvn_wc/diff_editor.c (working copy) @@ -432,13 +432,14 @@ svn_wc__diff_base_working_diff(svn_wc__d assert(status == svn_wc__db_status_normal || status == svn_wc__db_status_added || (status == svn_wc__db_status_deleted && diff_pristine)); /* If the item is not a member of a specified changelist (and there are some specified changelists), skip it. */ - if (changelist_hash && !svn_hash_gets(changelist_hash, changelist)) + if (changelist_hash + && (!changelist || !svn_hash_gets(changelist_hash, changelist))) return SVN_NO_ERROR; if (status != svn_wc__db_status_normal) { SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &db_revision, @@ -789,18 +790,20 @@ walk_local_nodes_diff(struct edit_baton_ { /* Report repository form deleted */ if (base_kind == svn_node_file && diff_files) SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath, child_relpath, eb->revnum, eb->processor, dir_baton, + eb->changelist_hash, iterpool)); else if (base_kind == svn_node_dir && diff_dirs) SVN_ERR(svn_wc__diff_base_only_dir(db, child_abspath, child_relpath, eb->revnum, depth_below_here, eb->processor, dir_baton, + eb->changelist_hash, eb->cancel_func, eb->cancel_baton, iterpool)); } else if (!local_only) /* Not local only nor remote only */ { @@ -944,14 +947,14 @@ svn_wc__diff_local_only_file(svn_wc__db_ assert(kind == svn_node_file && (status == svn_wc__db_status_normal || status == svn_wc__db_status_added || (status == svn_wc__db_status_deleted && diff_pristine))); - if (changelist && changelist_hash - && !svn_hash_gets(changelist_hash, changelist)) + if (changelist_hash + && (!changelist || !svn_hash_gets(changelist_hash, changelist))) return SVN_NO_ERROR; if (status == svn_wc__db_status_deleted) { assert(diff_pristine); @@ -1124,12 +1127,13 @@ svn_wc__diff_local_only_dir(svn_wc__db_t NULL, right_src, copyfrom_src, processor_parent_baton, processor, scratch_pool, iterpool)); + /* ### skip_children is not used */ SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts, db, local_abspath, FALSE /* base_tree_only */, scratch_pool, iterpool)); if (depth_below_here == svn_depth_immediates) @@ -1195,30 +1199,42 @@ svn_wc__diff_local_only_dir(svn_wc__db_t break; } } if (!skip) { - apr_hash_t *right_props; - if (diff_pristine) - SVN_ERR(svn_wc__db_read_pristine_props(&right_props, db, local_abspath, - scratch_pool, scratch_pool)); - else - SVN_ERR(svn_wc__get_actual_props(&right_props, db, local_abspath, - scratch_pool, scratch_pool)); + if (!changelist_hash) + { + apr_hash_t *right_props; + if (diff_pristine) + SVN_ERR(svn_wc__db_read_pristine_props(&right_props, db, local_abspath, + scratch_pool, scratch_pool)); + else + SVN_ERR(svn_wc__get_actual_props(&right_props, db, local_abspath, + scratch_pool, scratch_pool)); - SVN_ERR(processor->dir_added(relpath, - copyfrom_src, - right_src, - copyfrom_src - ? pristine_props - : NULL, - right_props, - pdb, - processor, - iterpool)); + SVN_ERR(processor->dir_added(relpath, + copyfrom_src, + right_src, + copyfrom_src + ? pristine_props + : NULL, + right_props, + pdb, + processor, + iterpool)); + } + else + { + SVN_ERR(processor->dir_closed(relpath, + NULL, + right_src, + pdb, + processor, + iterpool)); + } } svn_pool_destroy(iterpool); return SVN_NO_ERROR; } @@ -1306,18 +1322,20 @@ svn_error_t * svn_wc__diff_base_only_file(svn_wc__db_t *db, const char *local_abspath, const char *relpath, svn_revnum_t revision, const svn_diff_tree_processor_t *processor, void *processor_parent_baton, + apr_hash_t *changelist_hash, apr_pool_t *scratch_pool) { svn_wc__db_status_t status; svn_node_kind_t kind; const svn_checksum_t *checksum; apr_hash_t *props; + const char *changelist; void *file_baton = NULL; svn_boolean_t skip = FALSE; svn_diff_source_t *left_src; const char *pristine_file; SVN_ERR(svn_wc__db_base_get_info(&status, &kind, @@ -1329,12 +1347,23 @@ svn_wc__diff_base_only_file(svn_wc__db_t scratch_pool, scratch_pool)); SVN_ERR_ASSERT(status == svn_wc__db_status_normal && kind == svn_node_file && checksum); + SVN_ERR(svn_wc__db_read_info(NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, &changelist, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + db, local_abspath, + scratch_pool, scratch_pool)); + if (changelist_hash + && (!changelist || !svn_hash_gets(changelist_hash, changelist))) + return SVN_NO_ERROR; + left_src = svn_diff__source_create(revision, scratch_pool); SVN_ERR(processor->file_opened(&file_baton, &skip, relpath, left_src, NULL /* right_src */, @@ -1366,12 +1395,13 @@ svn_wc__diff_base_only_dir(svn_wc__db_t const char *local_abspath, const char *relpath, svn_revnum_t revision, svn_depth_t depth, const svn_diff_tree_processor_t *processor, void *processor_parent_baton, + apr_hash_t *changelist_hash, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *scratch_pool) { void *dir_baton = NULL; svn_boolean_t skip = FALSE; @@ -1435,12 +1465,13 @@ svn_wc__diff_base_only_dir(svn_wc__db_t case svn_node_file: case svn_node_symlink: SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath, child_relpath, revision, processor, dir_baton, + changelist_hash, iterpool)); break; case svn_node_dir: if (depth > svn_depth_files || depth == svn_depth_unknown) { svn_depth_t depth_below_here = depth; @@ -1450,12 +1481,13 @@ svn_wc__diff_base_only_dir(svn_wc__db_t SVN_ERR(svn_wc__diff_base_only_dir(db, child_abspath, child_relpath, revision, depth_below_here, processor, dir_baton, + changelist_hash, cancel_func, cancel_baton, iterpool)); } break; @@ -1464,22 +1496,34 @@ svn_wc__diff_base_only_dir(svn_wc__db_t } } } if (!skip) { - apr_hash_t *props; - SVN_ERR(svn_wc__db_base_get_props(&props, db, local_abspath, - scratch_pool, scratch_pool)); + if (!changelist_hash) + { + apr_hash_t *props; + SVN_ERR(svn_wc__db_base_get_props(&props, db, local_abspath, + scratch_pool, scratch_pool)); - SVN_ERR(processor->dir_deleted(relpath, - left_src, - props, - dir_baton, - processor, - scratch_pool)); + SVN_ERR(processor->dir_deleted(relpath, + left_src, + props, + dir_baton, + processor, + scratch_pool)); + } + else + { + SVN_ERR(processor->dir_closed(relpath, + left_src, + NULL, + dir_baton, + processor, + scratch_pool)); + } } return SVN_NO_ERROR; } /* An svn_delta_editor_t function. */ Index: subversion/libsvn_wc/diff.h =================================================================== --- subversion/libsvn_wc/diff.h (revision 1621760) +++ subversion/libsvn_wc/diff.h (working copy) @@ -100,12 +100,13 @@ svn_error_t * svn_wc__diff_base_only_file(svn_wc__db_t *db, const char *local_abspath, const char *relpath, svn_revnum_t revision, const svn_diff_tree_processor_t *processor, void *processor_parent_baton, + apr_hash_t *changelist_hash, apr_pool_t *scratch_pool); /* Reports the BASE-directory LOCAL_ABSPATH and everything below it (limited by DEPTH) as deleted to PROCESSOR with relpath RELPATH and parent baton PROCESSOR_PARENT_BATON. @@ -117,12 +118,13 @@ svn_wc__diff_base_only_dir(svn_wc__db_t const char *local_abspath, const char *relpath, svn_revnum_t revision, svn_depth_t depth, const svn_diff_tree_processor_t *processor, void *processor_parent_baton, + apr_hash_t *changelist_hash, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *scratch_pool); /* Diff the file PATH against the text base of its BASE layer. At this * stage we are dealing with a file that does exist in the working copy. Index: subversion/libsvn_wc/diff_local.c =================================================================== --- subversion/libsvn_wc/diff_local.c (revision 1621760) +++ subversion/libsvn_wc/diff_local.c (working copy) @@ -335,20 +335,22 @@ diff_status_callback(void *baton, if (base_kind == svn_node_file) SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath, child_relpath, SVN_INVALID_REVNUM, eb->processor, eb->cur ? eb->cur->baton : NULL, + eb->changelist_hash, scratch_pool)); else if (base_kind == svn_node_dir) SVN_ERR(svn_wc__diff_base_only_dir(db, child_abspath, child_relpath, SVN_INVALID_REVNUM, depth_below_here, eb->processor, eb->cur ? eb->cur->baton : NULL, + eb->changelist_hash, eb->cancel_func, eb->cancel_baton, scratch_pool)); } else if (!local_only) {