Index: subversion/include/svn_io.h =================================================================== --- subversion/include/svn_io.h (revision 17323) +++ subversion/include/svn_io.h (working copy) @@ -677,6 +677,18 @@ svn_error_t *svn_io_remove_file (const c /** Recursively remove directory @a path. @a path is utf8-encoded. */ svn_error_t *svn_io_remove_dir (const char *path, apr_pool_t *pool); +/** Read all of the disk entries in directory @a path, a utf8-encoded + * path. Set @a *dirents to a hash mapping dirent names (char *) to + * dirent names, allocated in @a pool. + * + * @note The `.' and `..' directories normally returned by + * apr_dir_read() are NOT returned in the hash. + * + * @since New in 1.4. + */ +svn_error_t *svn_io_get_dir_filenames (apr_hash_t **dirents, + const char *path, + apr_pool_t *pool); /** Read all of the disk entries in directory @a path, a utf8-encoded * path. Set @a *dirents to a hash mapping dirent names (char *) to Index: subversion/libsvn_wc/adm_crawler.c =================================================================== --- subversion/libsvn_wc/adm_crawler.c (revision 17323) +++ subversion/libsvn_wc/adm_crawler.c (working copy) @@ -184,7 +184,7 @@ report_revisions (svn_wc_adm_access_t *a dir_path, subpool); SVN_ERR (svn_wc_adm_retrieve (&dir_access, adm_access, full_path, subpool)); SVN_ERR (svn_wc_entries_read (&entries, dir_access, TRUE, subpool)); - SVN_ERR (svn_io_get_dirents2 (&dirents, full_path, subpool)); + SVN_ERR (svn_io_get_dir_filenames (&dirents, full_path, subpool)); /*** Do the real reporting and recursing. ***/ @@ -267,8 +267,6 @@ report_revisions (svn_wc_adm_access_t *a if (dirent_kind == svn_node_none) missing = TRUE; } - else - dirent_kind = dirent->kind; /* From here on out, ignore any entry scheduled for addition */ if (current_entry->schedule == svn_wc_schedule_add) @@ -277,17 +275,6 @@ report_revisions (svn_wc_adm_access_t *a /*** Files ***/ if (current_entry->kind == svn_node_file) { - /* If the dirent changed kind, report it as missing and - move on to the next entry. Later on, the update - editor will return an 'obstructed update' error. :) */ - if ((dirent_kind != svn_node_none) - && (dirent_kind != svn_node_file) - && (! report_everything)) - { - SVN_ERR (reporter->delete_path (report_baton, this_path, - iterpool)); - continue; - } /* If the item is missing from disk, and we're supposed to restore missing things, and it isn't missing as a result @@ -367,19 +354,6 @@ report_revisions (svn_wc_adm_access_t *a iterpool)); continue; } - - /* No excuses here. If the user changed a versioned - directory into something else, the working copy is hosed. - It can't receive updates within this dir anymore. Throw - a real error. */ - if ((dirent_kind != svn_node_none) && (dirent_kind != svn_node_dir)) - { - return svn_error_createf - (SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, - _("The entry '%s' is no longer a directory; " - "remove the entry before updating"), - svn_path_local_style (this_path, iterpool)); - } /* We need to read the full entry of the directory from its own "this dir", if available. */ Index: subversion/libsvn_wc/lock.c =================================================================== --- subversion/libsvn_wc/lock.c (revision 17323) +++ subversion/libsvn_wc/lock.c (working copy) @@ -675,10 +675,89 @@ svn_wc_adm_retrieve (svn_wc_adm_access_t generally makes the calling code simpler as it doesn't need to check for NULL batons. */ if (! *adm_access) - return svn_error_createf (SVN_ERR_WC_NOT_LOCKED, NULL, - _("Working copy '%s' is missing or not locked"), - svn_path_local_style (path, pool)); + { + const char *wcpath; + const svn_wc_entry_t *subdir_entry; + svn_node_kind_t wckind; + svn_node_kind_t kind; + svn_error_t *err; + + err = svn_wc_entry (&subdir_entry, path, associated, TRUE, pool); + + /* If we can't get an entry here, we are in pretty bad shape, + and will have to fall back to using just regular old paths to + see what's going on. */ + if (err) + { + err = NULL; + subdir_entry = NULL; + } + + err = svn_io_check_path (path, &kind, pool); + + /* If we can't check the path, we can't make a good error + message. */ + if (err) + { + svn_error_clear (err); + return svn_error_createf (SVN_ERR_WC_NOT_LOCKED, NULL, + _("Unable to check path existence for %s"), + svn_path_local_style (path, pool)); + } + + if (subdir_entry) + { + if (subdir_entry->kind == svn_node_dir + && kind == svn_node_file) + { + return svn_error_createf (SVN_ERR_WC_NOT_LOCKED, NULL, + _("Expected %s to be a directory but found a file"), + svn_path_local_style (path, pool)); + } + else if (subdir_entry->kind == svn_node_file + && kind == svn_node_dir) + { + return svn_error_createf (SVN_ERR_WC_NOT_LOCKED, NULL, + _("Expected %s to be a file but found a directory"), + svn_path_local_style (path, pool)); + } + } + + wcpath = svn_wc__adm_path (path, FALSE, pool, NULL); + err = svn_io_check_path (wcpath, &wckind, pool); + + /* If we can't check the path, we can't make a good error + message. */ + if (err) + { + svn_error_clear (err); + return svn_error_createf (SVN_ERR_WC_NOT_LOCKED, NULL, + _("Unable to check path existence for %s"), + svn_path_local_style (wcpath, pool)); + } + + if (kind == svn_node_none) + return svn_error_createf (SVN_ERR_WC_NOT_LOCKED, NULL, + _("Directory '%s' is missing"), + svn_path_local_style (path, pool)); + + else if (kind == svn_node_dir && wckind == svn_node_none) + return svn_error_createf (SVN_ERR_WC_NOT_LOCKED, NULL, + _("Directory '%s' containing working copy admin area is missing"), + svn_path_local_style (wcpath, pool)); + + else if (kind == svn_node_dir && wckind == svn_node_dir) + return svn_error_createf (SVN_ERR_WC_NOT_LOCKED, NULL, + _("Unable to lock %s"), + svn_path_local_style (path, pool)); + + /* If all else fails, return our useless generic error. */ + return svn_error_createf (SVN_ERR_WC_NOT_LOCKED, NULL, + _("Working copy '%s' is missing or not locked"), + svn_path_local_style (path, pool)); + + } return SVN_NO_ERROR; } Index: subversion/libsvn_subr/io.c =================================================================== --- subversion/libsvn_subr/io.c (revision 17323) +++ subversion/libsvn_subr/io.c (working copy) @@ -1718,7 +1718,50 @@ svn_io_remove_dir (const char *path, apr return APR_SUCCESS; } +svn_error_t * +svn_io_get_dir_filenames (apr_hash_t **dirents, + const char *path, + apr_pool_t *pool) +{ + apr_status_t status; + apr_dir_t *this_dir; + apr_finfo_t this_entry; + apr_int32_t flags = APR_FINFO_NAME; + + *dirents = apr_hash_make (pool); + + SVN_ERR (svn_io_dir_open (&this_dir, path, pool)); + + for (status = apr_dir_read (&this_entry, flags, this_dir); + status == APR_SUCCESS; + status = apr_dir_read (&this_entry, flags, this_dir)) + { + if ((this_entry.name[0] == '.') + && ((this_entry.name[1] == '\0') + || ((this_entry.name[1] == '.') + && (this_entry.name[2] == '\0')))) + { + continue; + } + else + { + const char *name; + SVN_ERR (svn_path_cstring_to_utf8 (&name, this_entry.name, pool)); + apr_hash_set (*dirents, name, APR_HASH_KEY_STRING, name); + } + } + + if (! (APR_STATUS_IS_ENOENT (status))) + return svn_error_wrap_apr (status, _("Can't read directory '%s'"), + svn_path_local_style (path, pool)); + status = apr_dir_close (this_dir); + if (status) + return svn_error_wrap_apr (status, _("Error closing directory '%s'"), + svn_path_local_style (path, pool)); + + return SVN_NO_ERROR; +} svn_error_t * svn_io_get_dirents2 (apr_hash_t **dirents,