rendered paste bodyIndex: tests/cmdline/changelist_tests.py===================================================================--- tests/cmdline/changelist_tests.py (revision 29609)+++ tests/cmdline/changelist_tests.py (working copy)@@ -99,22 +99,33 @@ def clname_from_lastchar_cb(full_path): # Regular expressions for 'svn changelist' output.+_re_cl_skip = re.compile("Skipped '(.*)'") _re_cl_add = re.compile("Path '(.*)' is now a member of changelist '(.*)'.") _re_cl_rem = re.compile("Path '(.*)' is no longer a member of a changelist.") def verify_changelist_output(output, expected_adds=None,- expected_removals=None):+ expected_removals=None,+ expected_skips=None): """Compare lines of OUTPUT from 'svn changelist' against- EXPECTED_ADDS (a dictionary mapping paths to changelist names) and- EXPECTED_REMOVALS (a dictionary mapping paths to ... whatever)."""+ EXPECTED_ADDS (a dictionary mapping paths to changelist names),+ EXPECTED_SKIPS (a dictionary mapping paths to ... whatever), and+ EXPECTED_REMOVALS (a dictionary mapping paths to ... whatever).+ + EXPECTED_SKIPS is ignored if None.""" num_expected = 0 if expected_adds: num_expected += len(expected_adds) if expected_removals: num_expected += len(expected_removals)- + if expected_skips:+ num_expected += len(expected_skips)++ if not expected_skips:+ output = filter(lambda line: (not _re_cl_skip.match(line)), output)+ if len(output) != num_expected:+ sys.stderr.write("".join(output)) raise svntest.Failure("Unexpected number of 'svn changelist' output lines") for line in output:@@ -133,6 +144,13 @@ def verify_changelist_output(output, expected_adds continue elif match: raise svntest.Failure("Unexpected changelist add line: " + line) + match = _re_cl_skip.match(line)+ if match \+ and expected_skips \+ and expected_skips.has_key(match.group(1)):+ continue+ elif match:+ raise svntest.Failure("Unexpected changelist skip line: " + line) raise svntest.Failure("Unexpected line: " + line) def verify_pget_output(output, expected_props):@@ -165,10 +183,50 @@ def add_remove_changelists(sbox): sbox.build() wc_dir = sbox.wc_dir + ### 'Skip' notifications+ def expected_skips_under(*greek_path):+ "return a dict mapping Greek-tree directories below GREEK_PATH to None"+ expected_skips = {}+ for path in expected_skips_all:+ if path.startswith(os.path.join(wc_dir, *greek_path)):+ expected_skips[path] = None+ return expected_skips++ def all_parents(expected_adds):+ """return a dict mapping Greek-tree directories above directories in+ EXPECTED_ADDS to None"""+ + expected_skips = {}+ for path in expected_adds.keys():+ if not os.path.isdir(path):+ path = os.path.dirname(path)+ + while path != wc_dir:+ expected_skips[path] = None+ path = os.path.dirname(path)+ + expected_skips[wc_dir] = None+ return expected_skips++ # all dirs in the Greek tree+ expected_skips_all = dict.fromkeys([+ os.path.join(wc_dir),+ os.path.join(wc_dir, 'A'),+ os.path.join(wc_dir, 'A', 'B'),+ os.path.join(wc_dir, 'A', 'B', 'E'),+ os.path.join(wc_dir, 'A', 'B', 'F'),+ os.path.join(wc_dir, 'A', 'C'),+ os.path.join(wc_dir, 'A', 'D'),+ os.path.join(wc_dir, 'A', 'D', 'G'),+ os.path.join(wc_dir, 'A', 'D', 'H'),+ ])++ expected_skips_wc_dir = { wc_dir : None }+ ### First, we play with just adding to changelists ### - # svn changelist foo WC_DIR- output, errput = svntest.main.run_svn(None, "changelist", "foo",+ # svn changelist foo WC_DIR --non-recursive+ output, errput = svntest.main.run_svn(None, "changelist", "foo", "-N", wc_dir) verify_changelist_output(output) # nothing expected @@ -265,8 +323,8 @@ def add_remove_changelists(sbox): ### Okay. Time to remove some stuff from changelists now. ### - # svn changelist --remove WC_DIR- output, errput = svntest.main.run_svn(None, "changelist", "--remove",+ # svn changelist --remove WC_DIR --non-recursive+ output, errput = svntest.main.run_svn(None, "changelist", "--remove", "-N", wc_dir) verify_changelist_output(output) # nothing expected @@ -798,7 +856,27 @@ def update_with_changelists(sbox): "--depth", "infinity", wc_dir) +#---------------------------------------------------------------------- +def changelist_without_arguments(sbox):+ "changelist without path arguments"++ sbox.build(read_only = True)+ wc_dir = sbox.wc_dir++ os.chdir(wc_dir)+ # "svn cl --remove;"+ svntest.actions.run_and_verify_svn(None,+ None,+ '.*Not enough arguments.*',+ 'changelist', '--remove')+ # "svn cl clname;"+ svntest.actions.run_and_verify_svn(None,+ None,+ '.*Not enough arguments.*',+ 'changelist', 'CLNAME')++ ######################################################################## # Run the tests @@ -812,6 +890,7 @@ test_list = [ None, propmods_with_changelists, revert_with_changelists, update_with_changelists,+ changelist_without_arguments, ] if __name__ == '__main__':Index: libsvn_client/changelist.c===================================================================--- libsvn_client/changelist.c (revision 29609)+++ libsvn_client/changelist.c (working copy)@@ -55,7 +55,17 @@ set_entry_changelist(const char *path, /* We only care about files right now. */ if (entry->kind != svn_node_file)- return SVN_NO_ERROR;+ {+ /* http://pastebin.ca/raw/921005 */+ 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;+ } /* See if this entry passes our changelist filtering. */ if (! SVN_WC__CL_MATCH(b->changelist_hash, entry))Index: include/svn_wc.h===================================================================--- include/svn_wc.h (revision 29609)+++ include/svn_wc.h (working copy)@@ -4701,7 +4701,7 @@ svn_wc_revision_status(svn_wc_revision_status_t ** * @note For now, directories are NOT allowed to be associated with * changelists; there is confusion about whether they should behave * as depth-0 or depth-infinity objects. If @a path is a directory,- * return @c SVN_ERR_UNSUPPORTED_FEATURE.+ * raise @c svn_wc_notify_skip. * * @note This metadata is purely a client-side "bookkeeping" * convenience, and is entirely managed by the working copy.Index: svn/changelist-cmd.c===================================================================--- svn/changelist-cmd.c (revision 29609)+++ svn/changelist-cmd.c (working copy)@@ -62,8 +62,9 @@ svn_cl__changelist(apr_getopt_t *os, opt_state->targets, pool)); - /* Add "." if user passed 0 arguments */- svn_opt_push_implicit_dot_target(targets, pool);+ /* Error out if user passed 0 arguments. */+ if (targets->nelts == 0)+ return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, NULL, NULL); if (! opt_state->quiet) svn_cl__get_notifier(&ctx->notify_func2, &ctx->notify_baton2, FALSE,@@ -90,7 +91,7 @@ svn_cl__changelist(apr_getopt_t *os, ctx->notify_func2 = NULL; if (depth == svn_depth_unknown)- depth = svn_depth_empty;+ depth = svn_depth_infinity; if (changelist_name) {Index: svn/main.c===================================================================--- svn/main.c (revision 29609)+++ svn/main.c (working copy)@@ -346,9 +346,9 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table { "changelist", svn_cl__changelist, {"cl"}, N_ ("Associate (or dissociate) changelist CLNAME with the named files.\n"- "usage: 1. changelist CLNAME TARGET...\n"- " 2. changelist --remove TARGET...\n"),- { 'q', 'R', opt_depth, opt_remove, opt_targets, opt_changelist} },+ "usage: 1. changelist CLNAME [PATH...]\n"+ " 2. changelist --remove [PATH...]\n"),+ { 'q', 'R', 'N', opt_depth, opt_remove, opt_targets, opt_changelist} }, { "checkout", svn_cl__checkout, {"co"}, N_ ("Check out a working copy from a repository.\n"@@ -1729,8 +1729,9 @@ main(int argc, const char *argv[]) { opt_state.depth = SVN_DEPTH_INFINITY_OR_IMMEDIATES(FALSE); }- else if (subcommand->cmd_func == svn_cl__revert- || subcommand->cmd_func == svn_cl__add)+ else if (subcommand->cmd_func == svn_cl__add+ || subcommand->cmd_func == svn_cl__changelist+ || subcommand->cmd_func == svn_cl__revert) { /* In pre-1.5 Subversion, some commands treated -N like --depth=empty, so . Also, with revert it makes sense to be