Index: subversion/bindings/java/javahl/src/org/tigris/subversion/javahl/tests/BasicTests.java =================================================================== --- subversion/bindings/java/javahl/src/org/tigris/subversion/javahl/tests/BasicTests.java (revision 21914) +++ subversion/bindings/java/javahl/src/org/tigris/subversion/javahl/tests/BasicTests.java (working copy) @@ -473,16 +473,7 @@ thisTest.getWc().setItemOODInfo("", psiCommitRev, psiAuthor, psiCommitDate, NodeKind.dir); - try - { - thisTest.checkStatus(true); - } - catch (junit.framework.AssertionFailedError xfail) - { - // FIXME: This test is currently expected to fail due to a - // bug in libsvn_client regarding retrieval of "out of - // date" information from the repository. - } + thisTest.checkStatus(true); } /** Index: subversion/include/svn_repos.h =================================================================== --- subversion/include/svn_repos.h (revision 21914) +++ subversion/include/svn_repos.h (working copy) @@ -835,6 +835,24 @@ apr_pool_t *pool); +/** + * Set @a *deleted to the revision @a path was most recently deleted + * in @a fs, within the inclusive revision bounds set by @a start and + * @a end. If @a path does not exist in @a root within the @a start and + * @a end bounds, or is not deleted within those bounds, set @a *deleted + * to SVN_INVALID_REVNUM. Use @a pool for memory allocation. + * + * @since New in 1.5. + */ +svn_error_t * +svn_repos_deleted_rev(svn_fs_t *fs, + const char *path, + svn_revnum_t start, + svn_revnum_t end, + svn_revnum_t *deleted, + apr_pool_t *pool); + + /** Callback type for use with svn_repos_history(). @a path and @a * revision represent interesting history locations in the lifetime * of the path passed to svn_repos_history(). @a baton is the same Index: subversion/libsvn_repos/reporter.c =================================================================== --- subversion/libsvn_repos/reporter.c (revision 21914) +++ subversion/libsvn_repos/reporter.c (working copy) @@ -625,7 +625,12 @@ /* If there's a source and it's not related to the target, nuke it. */ if (s_entry && !related) { - SVN_ERR(b->editor->delete_entry(e_path, SVN_INVALID_REVNUM, dir_baton, + svn_revnum_t deleted_rev; + + SVN_ERR(svn_repos_deleted_rev(svn_fs_root_fs(b->t_root), t_path, + s_rev, b->t_rev, &deleted_rev, + pool)); + SVN_ERR(b->editor->delete_entry(e_path, deleted_rev, dir_baton, pool)); s_path = NULL; } @@ -765,11 +770,19 @@ if (apr_hash_get(t_entries, s_entry->name, APR_HASH_KEY_STRING) == NULL) { + svn_revnum_t deleted_rev; + /* There is no corresponding target entry, so delete. */ e_fullpath = svn_path_join(e_path, s_entry->name, subpool); + SVN_ERR(svn_repos_deleted_rev(svn_fs_root_fs(b->t_root), + svn_path_join(t_path, + s_entry->name, + subpool), + s_rev, b->t_rev, + &deleted_rev, subpool)); if (b->recurse || s_entry->kind != svn_node_dir) SVN_ERR(b->editor->delete_entry(e_fullpath, - SVN_INVALID_REVNUM, + deleted_rev, dir_baton, subpool)); } } Index: subversion/libsvn_repos/rev_hunt.c =================================================================== --- subversion/libsvn_repos/rev_hunt.c (revision 21914) +++ subversion/libsvn_repos/rev_hunt.c (working copy) @@ -299,6 +299,68 @@ } +svn_error_t * +svn_repos_deleted_rev(svn_fs_t *fs, + const char *path, + svn_revnum_t start, + svn_revnum_t end, + svn_revnum_t *deleted, + apr_pool_t *pool) +{ + apr_pool_t *subpool; + svn_fs_root_t *root; + svn_revnum_t curr_rev; + *deleted = SVN_INVALID_REVNUM; + + /* Validate the revision range. */ + if (! SVN_IS_VALID_REVNUM(start)) + return svn_error_createf + (SVN_ERR_FS_NO_SUCH_REVISION, 0, + _("Invalid start revision %ld"), start); + if (! SVN_IS_VALID_REVNUM(end)) + return svn_error_createf + (SVN_ERR_FS_NO_SUCH_REVISION, 0, + _("Invalid end revision %ld"), end); + + subpool = svn_pool_create(pool); + + /* Ensure that the input is ordered. */ + if (start > end) + { + svn_revnum_t tmprev = start; + start = end; + end = tmprev; + } + + curr_rev = end; + + while (curr_rev >= start) + { + svn_node_kind_t kind_p; + svn_pool_clear(subpool); + + /* Get a revision root for curr_rev. */ + SVN_ERR(svn_fs_revision_root(&root, fs, curr_rev, subpool)); + + SVN_ERR(svn_fs_check_path(&kind_p, root, path, subpool)); + + if (kind_p == svn_node_none) + { + curr_rev--; + } + else + { + if (curr_rev != end) + *deleted = curr_rev + 1; + break; + } + } + + svn_pool_destroy(subpool); + return SVN_NO_ERROR; +} + + /* Helper func: return SVN_ERR_AUTHZ_UNREADABLE if ROOT/PATH is unreadable. */ static svn_error_t * Index: subversion/libsvn_wc/status.c =================================================================== --- subversion/libsvn_wc/status.c (revision 21914) +++ subversion/libsvn_wc/status.c (working copy) @@ -999,18 +999,42 @@ /* Look up the key PATH in BATON->STATII. IS_DIR_BATON indicates whether baton is a struct *dir_baton or struct *file_baton. If the value doesn't yet exist, and the REPOS_TEXT_STATUS indicates that this is an - addition, create a new status struct using the hash's pool. Merge - REPOS_TEXT_STATUS and REPOS_PROP_STATUS into the status structure's + addition, create a new status struct using the hash's pool. + + If IS_DIR_BATON is true, THIS_DIR_BATON is a *dir_baton cotaining the out + of date (ood) information we want to set in BATON. This is necessary + because this function tweaks the status of out of date directories + (BATON == THIS_DIR_BATON) and out of date directories' parents + (BATON == THIS_DIR_BATON->parent_baton). In the latter case THIS_DIR_BATON + contains the ood info we want to bubble up to ancestor directories so these + accurately reflect the fact they have an ood descendent. + + Merge REPOS_TEXT_STATUS and REPOS_PROP_STATUS into the status structure's "network" fields. + + Iff IS_DIR_BATON is true, DELETED_REV is used as follows, otherwise it + is ignored: + + If REPOS_TEXT_STATUS is svn_wc_status_deleted then DELETED_REV is + optionally the revision path was deleted, in all other cases it must + be set to SVN_INVALID_REVNUM. If DELETED_REV is not + SVN_INVALID_REVNUM and REPOS_TEXT_STATUS is svn_wc_status_deleted, + then use DELETED_REV to set PATH's ood_last_cmt_rev field in BATON. + If DELETED_REV is SVN_INVALID_REVNUM and REPOS_TEXT_STATUS is + svn_wc_status_deleted, set PATH's ood_last_cmt_rev to it's parent's + ood_last_cmt_rev value - see comment below. + If a new struct was added, set the repos_lock to REPOS_LOCK. */ static svn_error_t * tweak_statushash(void *baton, + void *this_dir_baton, svn_boolean_t is_dir_baton, svn_wc_adm_access_t *adm_access, const char *path, svn_boolean_t is_dir, enum svn_wc_status_kind repos_text_status, enum svn_wc_status_kind repos_prop_status, + svn_revnum_t deleted_rev, svn_lock_t *repos_lock) { svn_wc_status2_t *statstruct; @@ -1064,20 +1088,50 @@ /* Copy out of date info. */ if (is_dir_baton) { - struct dir_baton *b = baton; + struct dir_baton *b = this_dir_baton; + if (b->url) - statstruct->url = apr_pstrdup(pool, b->url); - statstruct->ood_kind = b->ood_kind; - /* The last committed rev, date, and author for deleted items + { + if (statstruct->repos_text_status == svn_wc_status_deleted) + { + /* When deleting PATH, BATON is for PATH's parent, + so we must construct PATH's real statstruct->url. */ + statstruct->url = + svn_path_url_add_component(b->url, + svn_path_basename(path, pool), + pool); + } + else + statstruct->url = apr_pstrdup(pool, b->url); + } + + /* The last committed date, and author for deleted items isn't available. */ - if (statstruct->repos_text_status != svn_wc_status_deleted) + if (statstruct->repos_text_status == svn_wc_status_deleted) { + statstruct->ood_kind = is_dir ? svn_node_dir : svn_node_file; + + /* Pre 1.5 servers don't provide the revision a path was deleted. + So we punt and use the last committed revision of the path's + parent, which has some chance of being correct. At worse it + is a higher revision than the path was deleted, but this is + better than nothing... */ + if (deleted_rev == SVN_INVALID_REVNUM) + statstruct->ood_last_cmt_rev = + ((struct dir_baton *) baton)->ood_last_cmt_rev; + else + statstruct->ood_last_cmt_rev = deleted_rev; + } + else + { + statstruct->ood_kind = b->ood_kind; statstruct->ood_last_cmt_rev = b->ood_last_cmt_rev; statstruct->ood_last_cmt_date = b->ood_last_cmt_date; if (b->ood_last_cmt_author) statstruct->ood_last_cmt_author = apr_pstrdup(pool, b->ood_last_cmt_author); } + } else { @@ -1468,17 +1522,18 @@ SVN_ERR(svn_wc_entries_read(&entries, adm_access, FALSE, pool)); if (apr_hash_get(entries, hash_key, APR_HASH_KEY_STRING)) - SVN_ERR(tweak_statushash(db, TRUE, eb->adm_access, + SVN_ERR(tweak_statushash(db, db, TRUE, eb->adm_access, full_path, kind == svn_node_dir, - svn_wc_status_deleted, 0, NULL)); + svn_wc_status_deleted, 0, revision, NULL)); /* Mark the parent dir -- it lost an entry (unless that parent dir is the root node and we're not supposed to report on the root node). */ if (db->parent_baton && (! *eb->target)) - SVN_ERR(tweak_statushash(db->parent_baton, TRUE, eb->adm_access, + SVN_ERR(tweak_statushash(db->parent_baton, db, TRUE, eb->adm_access, db->path, kind == svn_node_dir, - svn_wc_status_modified, 0, NULL)); + svn_wc_status_modified, 0, SVN_INVALID_REVNUM, + NULL)); return SVN_NO_ERROR; } @@ -1560,8 +1615,10 @@ struct edit_baton *eb = db->edit_baton; svn_wc_status2_t *dir_status = NULL; - /* If nothing has changed, return. */ - if (db->added || db->prop_changed || db->text_changed) + /* If nothing has changed and directory has no out of + date descendents, return. */ + if (db->added || db->prop_changed || db->text_changed + || db->ood_last_cmt_rev != SVN_INVALID_REVNUM) { enum svn_wc_status_kind repos_text_status; enum svn_wc_status_kind repos_prop_status; @@ -1588,11 +1645,12 @@ { /* ### When we add directory locking, we need to find a ### directory lock here. */ - SVN_ERR(tweak_statushash(pb, TRUE, + SVN_ERR(tweak_statushash(pb, db, TRUE, eb->adm_access, db->path, TRUE, repos_text_status, - repos_prop_status, NULL)); + repos_prop_status, SVN_INVALID_REVNUM, + NULL)); } else { @@ -1601,6 +1659,16 @@ trigger invocation of the status callback below. */ eb->anchor_status->repos_prop_status = repos_prop_status; eb->anchor_status->repos_text_status = repos_text_status; + + /* If the root dir is out of date set the ood info directly too. */ + if (db->ood_last_cmt_rev != eb->anchor_status->entry->revision) + { + eb->anchor_status->ood_last_cmt_rev = db->ood_last_cmt_rev; + eb->anchor_status->ood_last_cmt_date = db->ood_last_cmt_date; + eb->anchor_status->ood_kind = db->ood_kind; + eb->anchor_status->ood_last_cmt_author = + apr_pstrdup(pool, db->ood_last_cmt_author); + } } } @@ -1799,11 +1867,11 @@ repos_prop_status = fb->prop_changed ? svn_wc_status_modified : 0; } - SVN_ERR(tweak_statushash(fb, FALSE, + SVN_ERR(tweak_statushash(fb, NULL, FALSE, fb->edit_baton->adm_access, fb->path, FALSE, repos_text_status, - repos_prop_status, + repos_prop_status, SVN_INVALID_REVNUM, repos_lock)); return SVN_NO_ERROR;