Index: 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