/* Make a relative path containing '..' elements as needed. RELATIVE_TO_PATH must be the path to a directory (not a file!) and TARGET_PATH must be the path to any file or directory. Both RELATIVE_TO_PATH and TARGET_PATH must be based on the same parent path, i.e. they can either both be absolute or they can both be relative to the same parent directory. Both paths are expected to be canonical. If above conditions are met, a relative path that leads to TARGET_ABSPATH from RELATIVE_TO_PATH is returned, but there is no error checking involved. The returned path is allocated from RESULT_POOL, all other allocations are made in SCRATCH_POOL. */ static const char * make_relpath(const char *relative_to_path, const char *target_path, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { const char *la; const char *parent_dir_els = ""; /* An example: * relative_to_path = /a/b/c * target_path = /a/x/y/z * result = ../../x/y/z * * Another example (Windows specific): * relative_to_path = F:/wc * target_path = C:/wc * result = C:/wc */ /* Skip the common ancestor of both paths, here '/a'. */ la = svn_dirent_get_longest_ancestor(relative_to_path, target_path, scratch_pool); if (*la == '\0') { /* Nothing in common: E.g. C:/ vs F:/ on Windows */ return apr_pstrdup(result_pool, target_path); } relative_to_path = svn_dirent_skip_ancestor(la, relative_to_path); target_path = svn_dirent_skip_ancestor(la, target_path); /* In above example, we'd now have: * relative_to_path = b/c * target_path = x/y/z */ /* Count the elements of relative_to_path and prepend as many '..' elements * to target_path. */ while (*relative_to_path) { svn_dirent_split(&relative_to_path, NULL, relative_to_path, scratch_pool); parent_dir_els = svn_dirent_join(parent_dir_els, "..", scratch_pool); } return svn_dirent_join(parent_dir_els, target_path, result_pool); }
const char * svn_cl__local_style_skip_ancestor(const char *parent_path, const char *path, apr_pool_t *pool) { const char *relpath = NULL; if (parent_path) relpath = svn_dirent_skip_ancestor(parent_path, path); return svn_dirent_local_style(relpath ? relpath : path, pool); }
/* A status callback function which wraps the *real* status function/baton. This sucker takes care of any status tweaks we need to make (such as noting that the target of the status is missing from HEAD in the repository). This implements the 'svn_wc_status_func4_t' function type. */ static svn_error_t * tweak_status(void *baton, const char *local_abspath, const svn_wc_status3_t *status, apr_pool_t *scratch_pool) { struct status_baton *sb = baton; const char *path = local_abspath; svn_client_status_t *cst; if (sb->anchor_abspath) path = svn_dirent_join(sb->anchor_relpath, svn_dirent_skip_ancestor(sb->anchor_abspath, path), scratch_pool); /* If the status item has an entry, but doesn't belong to one of the changelists our caller is interested in, we filter out this status transmission. */ if (sb->changelist_hash && (! status->changelist || ! svn_hash_gets(sb->changelist_hash, status->changelist))) { return SVN_NO_ERROR; } /* If we know that the target was deleted in HEAD of the repository, we need to note that fact in all the status structures that come through here. */ if (sb->deleted_in_repos) { svn_wc_status3_t *new_status = svn_wc_dup_status3(status, scratch_pool); new_status->repos_node_status = svn_wc_status_deleted; status = new_status; } SVN_ERR(svn_client__create_status(&cst, sb->wc_ctx, local_abspath, status, scratch_pool, scratch_pool)); /* Call the real status function/baton. */ return sb->real_status_func(sb->real_status_baton, path, cst, scratch_pool); }
/* svn_wc__node_found_func_t implementation for directory_dump */ static svn_error_t * print_dir(const char *local_abspath, svn_node_kind_t kind, void *walk_baton, apr_pool_t *scratch_pool) { struct directory_walk_baton *bt = walk_baton; const char *path; if (kind != svn_node_dir) return SVN_NO_ERROR; /* If LOCAL_ABSPATH a child of or equal to ROOT_ABSPATH, then display a relative path starting with PREFIX_PATH. */ path = svn_dirent_skip_ancestor(bt->root_abspath, local_abspath); if (path) path = svn_dirent_join(bt->prefix_path, path, scratch_pool); else path = local_abspath; printf("%s\n", svn_dirent_local_style(path, scratch_pool)); return SVN_NO_ERROR; }
static svn_error_t * tree_dump_dir(const char *local_abspath, svn_node_kind_t kind, void *walk_baton, apr_pool_t *scratch_pool) { struct directory_walk_baton *bt = walk_baton; const char *path; if (kind != svn_node_dir) return SVN_NO_ERROR; if (strcmp(local_abspath, bt->root_abspath) != 0) { svn_boolean_t is_wcroot; SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, bt->wc_ctx->db, local_abspath, scratch_pool)); if (is_wcroot) return SVN_NO_ERROR; /* Report the stub, but not the data */ } /* If LOCAL_ABSPATH a child of or equal to ROOT_ABSPATH, then display a relative path starting with PREFIX_PATH. */ path = svn_dirent_skip_ancestor(bt->root_abspath, local_abspath); if (path) path = svn_dirent_join(bt->prefix_path, path, scratch_pool); else path = local_abspath; printf("entries = {}\n"); SVN_ERR(entries_dump(path, bt->adm_access, scratch_pool)); printf("dirs['%s'] = entries\n", path); return SVN_NO_ERROR; }
/* An implementation of svn_wc__proplist_receiver_t. */ static svn_error_t * recursive_proplist_receiver(void *baton, const char *local_abspath, apr_hash_t *props, apr_pool_t *scratch_pool) { struct recursive_proplist_receiver_baton *b = baton; const char *path; /* Attempt to convert absolute paths to relative paths for * presentation purposes, if needed. */ if (b->anchor && b->anchor_abspath) { path = svn_dirent_join(b->anchor, svn_dirent_skip_ancestor(b->anchor_abspath, local_abspath), scratch_pool); } else path = local_abspath; return svn_error_trace(b->wrapped_receiver(b->wrapped_receiver_baton, path, props, scratch_pool)); }
/* Implements svn_wc_status_func3_t */ static svn_error_t * diff_status_callback(void *baton, const char *local_abspath, const svn_wc_status3_t *status, apr_pool_t *scratch_pool) { struct diff_baton *eb = baton; svn_wc__db_t *db = eb->db; if (! status->versioned) return SVN_NO_ERROR; /* unversioned (includes dir externals) */ if (status->node_status == svn_wc_status_conflicted && status->text_status == svn_wc_status_none && status->prop_status == svn_wc_status_none) { /* Node is an actual only node describing a tree conflict */ return SVN_NO_ERROR; } /* Not text/prop modified, not copied. Easy out */ if (status->node_status == svn_wc_status_normal && !status->copied) return SVN_NO_ERROR; /* Mark all directories where we are no longer inside as closed */ while (eb->cur && !svn_dirent_is_ancestor(eb->cur->local_abspath, local_abspath)) { struct node_state_t *ns = eb->cur; if (!ns->skip) { if (ns->propchanges) SVN_ERR(eb->processor->dir_changed(ns->relpath, ns->left_src, ns->right_src, ns->left_props, ns->right_props, ns->propchanges, ns->baton, eb->processor, ns->pool)); else SVN_ERR(eb->processor->dir_closed(ns->relpath, ns->left_src, ns->right_src, ns->baton, eb->processor, ns->pool)); } eb->cur = ns->parent; svn_pool_clear(ns->pool); } SVN_ERR(ensure_state(eb, svn_dirent_dirname(local_abspath, scratch_pool), FALSE, scratch_pool)); if (eb->cur && eb->cur->skip_children) return SVN_NO_ERROR; if (eb->changelist_hash != NULL && (!status->changelist || ! svn_hash_gets(eb->changelist_hash, status->changelist))) return SVN_NO_ERROR; /* Filtered via changelist */ /* This code does about the same thing as the inner body of walk_local_nodes_diff() in diff_editor.c, except that it is already filtered by the status walker, doesn't have to account for remote changes (and many tiny other details) */ { svn_boolean_t repos_only; svn_boolean_t local_only; svn_wc__db_status_t db_status; svn_boolean_t have_base; svn_node_kind_t base_kind; svn_node_kind_t db_kind = status->kind; svn_depth_t depth_below_here = svn_depth_unknown; const char *child_abspath = local_abspath; const char *child_relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, local_abspath); repos_only = FALSE; local_only = FALSE; /* ### optimize away this call using status info. Should be possible in almost every case (except conflict, missing, obst.)*/ SVN_ERR(svn_wc__db_read_info(&db_status, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &have_base, NULL, NULL, eb->db, local_abspath, scratch_pool, scratch_pool)); if (!have_base) { local_only = TRUE; /* Only report additions */ } else if (db_status == svn_wc__db_status_normal) { /* Simple diff */ base_kind = db_kind; } else if (db_status == svn_wc__db_status_deleted) { svn_wc__db_status_t base_status; repos_only = TRUE; SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, eb->db, local_abspath, scratch_pool, scratch_pool)); if (base_status != svn_wc__db_status_normal) return SVN_NO_ERROR; } else { /* working status is either added or deleted */ svn_wc__db_status_t base_status; SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, eb->db, local_abspath, scratch_pool, scratch_pool)); if (base_status != svn_wc__db_status_normal) local_only = TRUE; else if (base_kind != db_kind || !eb->ignore_ancestry) { repos_only = TRUE; local_only = TRUE; } } if (repos_only) { /* Report repository form deleted */ 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, 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->cancel_func, eb->cancel_baton, scratch_pool)); } else if (!local_only) { /* Diff base against actual */ if (db_kind == svn_node_file) { SVN_ERR(svn_wc__diff_base_working_diff(db, child_abspath, child_relpath, SVN_INVALID_REVNUM, eb->changelist_hash, eb->processor, eb->cur ? eb->cur->baton : NULL, FALSE, eb->cancel_func, eb->cancel_baton, scratch_pool)); } else if (db_kind == svn_node_dir) { SVN_ERR(ensure_state(eb, local_abspath, FALSE, scratch_pool)); if (status->prop_status != svn_wc_status_none && status->prop_status != svn_wc_status_normal) { apr_array_header_t *propchanges; SVN_ERR(svn_wc__db_base_get_props(&eb->cur->left_props, eb->db, local_abspath, eb->cur->pool, scratch_pool)); SVN_ERR(svn_wc__db_read_props(&eb->cur->right_props, eb->db, local_abspath, eb->cur->pool, scratch_pool)); SVN_ERR(svn_prop_diffs(&propchanges, eb->cur->right_props, eb->cur->left_props, eb->cur->pool)); eb->cur->propchanges = propchanges; } } } if (local_only && (db_status != svn_wc__db_status_deleted)) { if (db_kind == svn_node_file) SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath, child_relpath, eb->processor, eb->cur ? eb->cur->baton : NULL, eb->changelist_hash, FALSE, eb->cancel_func, eb->cancel_baton, scratch_pool)); else if (db_kind == svn_node_dir) SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath, child_relpath, depth_below_here, eb->processor, eb->cur ? eb->cur->baton : NULL, eb->changelist_hash, FALSE, eb->cancel_func, eb->cancel_baton, scratch_pool)); } if (db_kind == svn_node_dir && (local_only || repos_only)) SVN_ERR(ensure_state(eb, local_abspath, TRUE /* skip */, scratch_pool)); } return SVN_NO_ERROR; }
/* Recursively opens directories on the stack in EB, until LOCAL_ABSPATH is reached. If RECURSIVE_SKIP is TRUE, don't open LOCAL_ABSPATH itself, but create it marked with skip+skip_children. */ static svn_error_t * ensure_state(struct diff_baton *eb, const char *local_abspath, svn_boolean_t recursive_skip, apr_pool_t *scratch_pool) { struct node_state_t *ns; apr_pool_t *ns_pool; if (!eb->cur) { if (!svn_dirent_is_ancestor(eb->anchor_abspath, local_abspath)) return SVN_NO_ERROR; SVN_ERR(ensure_state(eb, svn_dirent_dirname(local_abspath,scratch_pool), FALSE, scratch_pool)); } else if (svn_dirent_is_child(eb->cur->local_abspath, local_abspath, NULL)) SVN_ERR(ensure_state(eb, svn_dirent_dirname(local_abspath,scratch_pool), FALSE, scratch_pool)); else return SVN_NO_ERROR; if (eb->cur && eb->cur->skip_children) return SVN_NO_ERROR; ns_pool = svn_pool_create(eb->cur ? eb->cur->pool : eb->pool); ns = apr_pcalloc(ns_pool, sizeof(*ns)); ns->pool = ns_pool; ns->local_abspath = apr_pstrdup(ns_pool, local_abspath); ns->relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, ns->local_abspath); ns->parent = eb->cur; eb->cur = ns; if (recursive_skip) { ns->skip = TRUE; ns->skip_children = TRUE; return SVN_NO_ERROR; } { svn_revnum_t revision; svn_error_t *err; err = svn_wc__db_base_get_info(NULL, NULL, &revision, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, eb->db, local_abspath, scratch_pool, scratch_pool); if (err) { if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) return svn_error_trace(err); svn_error_clear(err); revision = 0; /* Use original revision? */ } ns->left_src = svn_diff__source_create(revision, ns->pool); ns->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, ns->pool); SVN_ERR(eb->processor->dir_opened(&ns->baton, &ns->skip, &ns->skip_children, ns->relpath, ns->left_src, ns->right_src, NULL /* copyfrom_source */, ns->parent ? ns->parent->baton : NULL, eb->processor, ns->pool, scratch_pool)); } return SVN_NO_ERROR; }
svn_error_t * svn_client__export_externals(apr_hash_t *externals, const char *from_url, const char *to_abspath, const char *repos_root_url, svn_depth_t requested_depth, const char *native_eol, svn_boolean_t ignore_keywords, svn_client_ctx_t *ctx, apr_pool_t *scratch_pool) { apr_pool_t *iterpool = svn_pool_create(scratch_pool); apr_pool_t *sub_iterpool = svn_pool_create(scratch_pool); apr_hash_index_t *hi; SVN_ERR_ASSERT(svn_dirent_is_absolute(to_abspath)); for (hi = apr_hash_first(scratch_pool, externals); hi; hi = apr_hash_next(hi)) { const char *local_abspath = svn__apr_hash_index_key(hi); const char *desc_text = svn__apr_hash_index_val(hi); const char *local_relpath; const char *dir_url; apr_array_header_t *items; int i; svn_pool_clear(iterpool); SVN_ERR(svn_wc_parse_externals_description3(&items, local_abspath, desc_text, FALSE, iterpool)); if (! items->nelts) continue; local_relpath = svn_dirent_skip_ancestor(to_abspath, local_abspath); dir_url = svn_path_url_add_component2(from_url, local_relpath, scratch_pool); for (i = 0; i < items->nelts; i++) { const char *item_abspath; const char *new_url; svn_boolean_t under_root; svn_wc_external_item2_t *item = APR_ARRAY_IDX(items, i, svn_wc_external_item2_t *); svn_pool_clear(sub_iterpool); SVN_ERR(svn_dirent_is_under_root(&under_root, &item_abspath, local_abspath, item->target_dir, sub_iterpool)); if (! under_root) { return svn_error_createf( SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, _("Path '%s' is not in the working copy"), svn_dirent_local_style( svn_dirent_join(local_abspath, item->target_dir, sub_iterpool), sub_iterpool)); } SVN_ERR(svn_wc__resolve_relative_external_url(&new_url, item, repos_root_url, dir_url, sub_iterpool, sub_iterpool)); /* The target dir might have multiple components. Guarantee the path leading down to the last component. */ SVN_ERR(svn_io_make_dir_recursively(svn_dirent_dirname(item_abspath, sub_iterpool), sub_iterpool)); SVN_ERR(wrap_external_error( ctx, item_abspath, svn_client_export5(NULL, new_url, item_abspath, &item->peg_revision, &item->revision, TRUE, FALSE, ignore_keywords, svn_depth_infinity, native_eol, ctx, sub_iterpool), sub_iterpool)); } } svn_pool_destroy(sub_iterpool); svn_pool_destroy(iterpool); return SVN_NO_ERROR; }
/* Perform status operations on each external in EXTERNAL_MAP, a const char * local_abspath of all externals mapping to the const char* defining_abspath. All other options are the same as those passed to svn_client_status(). If ANCHOR_ABSPATH and ANCHOR-RELPATH are not null, use them to provide properly formatted relative paths */ static svn_error_t * do_external_status(svn_client_ctx_t *ctx, apr_hash_t *external_map, svn_depth_t depth, svn_boolean_t get_all, svn_boolean_t check_out_of_date, svn_boolean_t check_working_copy, svn_boolean_t no_ignore, const apr_array_header_t *changelists, const char *anchor_abspath, const char *anchor_relpath, svn_client_status_func_t status_func, void *status_baton, apr_pool_t *scratch_pool) { apr_pool_t *iterpool = svn_pool_create(scratch_pool); apr_array_header_t *externals; int i; externals = svn_sort__hash(external_map, svn_sort_compare_items_lexically, scratch_pool); /* Loop over the hash of new values (we don't care about the old ones). This is a mapping of versioned directories to property values. */ for (i = 0; i < externals->nelts; i++) { svn_node_kind_t external_kind; svn_sort__item_t item = APR_ARRAY_IDX(externals, i, svn_sort__item_t); const char *local_abspath = item.key; const char *defining_abspath = item.value; svn_node_kind_t kind; svn_opt_revision_t opt_rev; const char *status_path; svn_pool_clear(iterpool); /* Obtain information on the expected external. */ SVN_ERR(svn_wc__read_external_info(&external_kind, NULL, NULL, NULL, &opt_rev.value.number, ctx->wc_ctx, defining_abspath, local_abspath, FALSE, iterpool, iterpool)); if (external_kind != svn_node_dir) continue; SVN_ERR(svn_io_check_path(local_abspath, &kind, iterpool)); if (kind != svn_node_dir) continue; if (SVN_IS_VALID_REVNUM(opt_rev.value.number)) opt_rev.kind = svn_opt_revision_number; else opt_rev.kind = svn_opt_revision_unspecified; /* Tell the client we're starting an external status set. */ if (ctx->notify_func2) ctx->notify_func2( ctx->notify_baton2, svn_wc_create_notify(local_abspath, svn_wc_notify_status_external, iterpool), iterpool); status_path = local_abspath; if (anchor_abspath) { status_path = svn_dirent_join(anchor_relpath, svn_dirent_skip_ancestor(anchor_abspath, status_path), iterpool); } /* And then do the status. */ SVN_ERR(svn_client_status6(NULL, ctx, status_path, &opt_rev, depth, get_all, check_out_of_date, check_working_copy, no_ignore, FALSE /* ignore_exernals */, FALSE /* depth_as_sticky */, changelists, status_func, status_baton, iterpool)); } /* Destroy SUBPOOL and (implicitly) ITERPOOL. */ svn_pool_destroy(iterpool); return SVN_NO_ERROR; }
/* Implements svn_wc_status_func3_t */ static svn_error_t * diff_status_callback(void *baton, const char *local_abspath, const svn_wc_status3_t *status, apr_pool_t *scratch_pool) { struct diff_baton *eb = baton; switch (status->node_status) { case svn_wc_status_unversioned: case svn_wc_status_ignored: return SVN_NO_ERROR; /* No diff */ case svn_wc_status_obstructed: case svn_wc_status_missing: return SVN_NO_ERROR; /* ### What should we do here? */ default: break; /* Go check other conditions */ } /* Filter items by changelist. */ /* ### duplicated in ../libsvn_client/status.c */ if (eb->changelist_hash) { if (status->changelist) { /* Skip unless the caller requested this changelist. */ if (! apr_hash_get(eb->changelist_hash, status->changelist, APR_HASH_KEY_STRING)) return SVN_NO_ERROR; } else { /* Skip unless the caller requested changelist-lacking items. */ if (! apr_hash_get(eb->changelist_hash, "", APR_HASH_KEY_STRING)) return SVN_NO_ERROR; } } /* ### The following checks should probably be reversed as it should decide when *not* to show a diff, because generally all changed nodes should have a diff. */ if (status->kind == svn_node_file) { /* Show a diff when * - The text is modified * - Or the properties are modified * - Or when the node has been replaced * - Or (if in copies as adds or git mode) when a node is copied */ if (status->text_status == svn_wc_status_modified || status->prop_status == svn_wc_status_modified || status->node_status == svn_wc_status_deleted || status->node_status == svn_wc_status_replaced || ((eb->show_copies_as_adds || eb->use_git_diff_format) && status->copied)) { const char *path = svn_dirent_skip_ancestor(eb->anchor_abspath, local_abspath); SVN_ERR(file_diff(eb, local_abspath, path, scratch_pool)); } } else /* it's a directory */ { const char *path = svn_dirent_skip_ancestor(eb->anchor_abspath, local_abspath); /* Report the directory as deleted and/or opened or added. */ if (status->node_status == svn_wc_status_deleted || status->node_status == svn_wc_status_replaced) SVN_ERR(eb->callbacks->dir_deleted(NULL, NULL, path, eb->callback_baton, scratch_pool)); if (status->node_status == svn_wc_status_added || status->node_status == svn_wc_status_replaced) SVN_ERR(eb->callbacks->dir_added(NULL, NULL, NULL, NULL, path, status->revision, path, status->revision /* ### ? */, eb->callback_baton, scratch_pool)); else SVN_ERR(eb->callbacks->dir_opened(NULL, NULL, NULL, path, status->revision, eb->callback_baton, scratch_pool)); /* Report the prop change. */ /* ### This case should probably be extended for git-diff, but this is what the old diff code provided */ if (status->node_status == svn_wc_status_deleted || status->node_status == svn_wc_status_replaced || status->prop_status == svn_wc_status_modified) { apr_array_header_t *propchanges; apr_hash_t *baseprops; SVN_ERR(svn_wc__internal_propdiff(&propchanges, &baseprops, eb->db, local_abspath, scratch_pool, scratch_pool)); SVN_ERR(eb->callbacks->dir_props_changed(NULL, NULL, path, FALSE /* ### ? */, propchanges, baseprops, eb->callback_baton, scratch_pool)); } /* Close the dir. * ### This should be done after all children have been processed, not * yet. The current Subversion-internal callers don't care. */ SVN_ERR(eb->callbacks->dir_closed( NULL, NULL, NULL, path, (status->node_status == svn_wc_status_added || status->node_status == svn_wc_status_replaced), eb->callback_baton, scratch_pool)); } return SVN_NO_ERROR; }
/* Produce a diff of depth DEPTH for the directory at LOCAL_ABSPATH, * using information from the arbitrary_diff_walker_baton B. * LOCAL_ABSPATH is the path being crawled and can be on either side * of the diff depending on baton->recursing_within_added_subtree. */ static svn_error_t * arbitrary_diff_this_dir(struct arbitrary_diff_walker_baton *b, const char *local_abspath, svn_depth_t depth, apr_pool_t *scratch_pool) { const char *local_abspath1; const char *local_abspath2; svn_node_kind_t kind1; svn_node_kind_t kind2; const char *child_relpath; apr_hash_t *dirents1; apr_hash_t *dirents2; apr_hash_t *merged_dirents; apr_array_header_t *sorted_dirents; int i; apr_pool_t *iterpool; if (b->recursing_within_adm_dir) { if (svn_dirent_skip_ancestor(b->adm_dir_abspath, local_abspath)) return SVN_NO_ERROR; else { b->recursing_within_adm_dir = FALSE; b->adm_dir_abspath = NULL; } } else if (svn_wc_is_adm_dir(svn_dirent_basename(local_abspath, NULL), scratch_pool)) { b->recursing_within_adm_dir = TRUE; b->adm_dir_abspath = apr_pstrdup(b->pool, local_abspath); return SVN_NO_ERROR; } if (b->recursing_within_added_subtree) child_relpath = svn_dirent_skip_ancestor(b->root2_abspath, local_abspath); else child_relpath = svn_dirent_skip_ancestor(b->root1_abspath, local_abspath); if (!child_relpath) return SVN_NO_ERROR; local_abspath1 = svn_dirent_join(b->root1_abspath, child_relpath, scratch_pool); SVN_ERR(svn_io_check_resolved_path(local_abspath1, &kind1, scratch_pool)); local_abspath2 = svn_dirent_join(b->root2_abspath, child_relpath, scratch_pool); SVN_ERR(svn_io_check_resolved_path(local_abspath2, &kind2, scratch_pool)); if (depth > svn_depth_empty) { if (kind1 == svn_node_dir) SVN_ERR(svn_io_get_dirents3(&dirents1, local_abspath1, TRUE, /* only_check_type */ scratch_pool, scratch_pool)); else dirents1 = apr_hash_make(scratch_pool); } if (kind2 == svn_node_dir) { apr_hash_t *original_props; apr_hash_t *modified_props; apr_array_header_t *prop_changes; /* Show any property changes for this directory. */ SVN_ERR(get_props(&original_props, local_abspath1, b->ctx->wc_ctx, scratch_pool, scratch_pool)); SVN_ERR(get_props(&modified_props, local_abspath2, b->ctx->wc_ctx, scratch_pool, scratch_pool)); SVN_ERR(svn_prop_diffs(&prop_changes, modified_props, original_props, scratch_pool)); if (prop_changes->nelts > 0) SVN_ERR(b->callbacks->dir_props_changed(NULL, NULL, child_relpath, FALSE /* was_added */, prop_changes, original_props, b->diff_baton, scratch_pool)); if (depth > svn_depth_empty) { /* Read directory entries. */ SVN_ERR(svn_io_get_dirents3(&dirents2, local_abspath2, TRUE, /* only_check_type */ scratch_pool, scratch_pool)); } } else if (depth > svn_depth_empty) dirents2 = apr_hash_make(scratch_pool); if (depth <= svn_depth_empty) return SVN_NO_ERROR; /* Compare dirents1 to dirents2 and show added/deleted/changed files. */ merged_dirents = apr_hash_merge(scratch_pool, dirents1, dirents2, NULL, NULL); sorted_dirents = svn_sort__hash(merged_dirents, svn_sort_compare_items_as_paths, scratch_pool); iterpool = svn_pool_create(scratch_pool); for (i = 0; i < sorted_dirents->nelts; i++) { svn_sort__item_t elt = APR_ARRAY_IDX(sorted_dirents, i, svn_sort__item_t); const char *name = elt.key; svn_io_dirent2_t *dirent1; svn_io_dirent2_t *dirent2; const char *child1_abspath; const char *child2_abspath; svn_pool_clear(iterpool); if (b->ctx->cancel_func) SVN_ERR(b->ctx->cancel_func(b->ctx->cancel_baton)); if (strcmp(name, SVN_WC_ADM_DIR_NAME) == 0) continue; dirent1 = svn_hash_gets(dirents1, name); if (!dirent1) { dirent1 = svn_io_dirent2_create(iterpool); dirent1->kind = svn_node_none; } dirent2 = svn_hash_gets(dirents2, name); if (!dirent2) { dirent2 = svn_io_dirent2_create(iterpool); dirent2->kind = svn_node_none; } child1_abspath = svn_dirent_join(local_abspath1, name, iterpool); child2_abspath = svn_dirent_join(local_abspath2, name, iterpool); if (dirent1->special) SVN_ERR(svn_io_check_resolved_path(child1_abspath, &dirent1->kind, iterpool)); if (dirent2->special) SVN_ERR(svn_io_check_resolved_path(child1_abspath, &dirent2->kind, iterpool)); if (dirent1->kind == svn_node_dir && dirent2->kind == svn_node_dir) { if (depth == svn_depth_immediates) { /* Not using the walker, so show property diffs on these dirs. */ SVN_ERR(do_arbitrary_dirs_diff(child1_abspath, child2_abspath, b->root1_abspath, b->root2_abspath, svn_depth_empty, b->callbacks, b->diff_baton, b->ctx, iterpool)); } else { /* Either the walker will visit these directories (with * depth=infinity) and they will be processed as 'this dir' * later, or we're showing file children only (depth=files). */ continue; } } /* Files that exist only in dirents1. */ if (dirent1->kind == svn_node_file && (dirent2->kind == svn_node_dir || dirent2->kind == svn_node_none)) SVN_ERR(do_arbitrary_files_diff(child1_abspath, b->empty_file_abspath, svn_relpath_join(child_relpath, name, iterpool), FALSE, TRUE, NULL, b->callbacks, b->diff_baton, b->ctx, iterpool)); /* Files that exist only in dirents2. */ if (dirent2->kind == svn_node_file && (dirent1->kind == svn_node_dir || dirent1->kind == svn_node_none)) { apr_hash_t *original_props; SVN_ERR(get_props(&original_props, child1_abspath, b->ctx->wc_ctx, scratch_pool, scratch_pool)); SVN_ERR(do_arbitrary_files_diff(b->empty_file_abspath, child2_abspath, svn_relpath_join(child_relpath, name, iterpool), TRUE, FALSE, original_props, b->callbacks, b->diff_baton, b->ctx, iterpool)); } /* Files that exist in dirents1 and dirents2. */ if (dirent1->kind == svn_node_file && dirent2->kind == svn_node_file) SVN_ERR(do_arbitrary_files_diff(child1_abspath, child2_abspath, svn_relpath_join(child_relpath, name, iterpool), FALSE, FALSE, NULL, b->callbacks, b->diff_baton, b->ctx, scratch_pool)); /* Directories that only exist in dirents2. These aren't crawled * by this walker so we have to crawl them separately. */ if (depth > svn_depth_files && dirent2->kind == svn_node_dir && (dirent1->kind == svn_node_file || dirent1->kind == svn_node_none)) SVN_ERR(do_arbitrary_dirs_diff(child1_abspath, child2_abspath, b->root1_abspath, b->root2_abspath, depth <= svn_depth_immediates ? svn_depth_empty : svn_depth_infinity , b->callbacks, b->diff_baton, b->ctx, iterpool)); } svn_pool_destroy(iterpool); return SVN_NO_ERROR; }
svn_error_t * svn_path_remove_redundancies(apr_array_header_t **pcondensed_targets, const apr_array_header_t *targets, apr_pool_t *pool) { apr_pool_t *temp_pool; apr_array_header_t *abs_targets; apr_array_header_t *rel_targets; int i; if ((targets->nelts <= 0) || (! pcondensed_targets)) { /* No targets or no place to store our work means this function really has nothing to do. */ if (pcondensed_targets) *pcondensed_targets = NULL; return SVN_NO_ERROR; } /* Initialize our temporary pool. */ temp_pool = svn_pool_create(pool); /* Create our list of absolute paths for our "keepers" */ abs_targets = apr_array_make(temp_pool, targets->nelts, sizeof(const char *)); /* Create our list of untainted paths for our "keepers" */ rel_targets = apr_array_make(pool, targets->nelts, sizeof(const char *)); /* For each target in our list we do the following: 1. Calculate its absolute path (ABS_PATH). 2. See if any of the keepers in ABS_TARGETS is a parent of, or is the same path as, ABS_PATH. If so, we ignore this target. If not, however, add this target's absolute path to ABS_TARGETS and its original path to REL_TARGETS. */ for (i = 0; i < targets->nelts; i++) { const char *rel_path = APR_ARRAY_IDX(targets, i, const char *); const char *abs_path; int j; svn_boolean_t is_url, keep_me; /* Get the absolute path for this target. */ is_url = svn_path_is_url(rel_path); if (is_url) abs_path = rel_path; else SVN_ERR(svn_dirent_get_absolute(&abs_path, rel_path, temp_pool)); /* For each keeper in ABS_TARGETS, see if this target is the same as or a child of that keeper. */ keep_me = TRUE; for (j = 0; j < abs_targets->nelts; j++) { const char *keeper = APR_ARRAY_IDX(abs_targets, j, const char *); svn_boolean_t keeper_is_url = svn_path_is_url(keeper); const char *child_relpath; /* If KEEPER hasn't the same is-url-ness as ABS_PATH, we know they aren't equal and that one isn't the child of the other. */ if (is_url != keeper_is_url) continue; /* Quit here if this path is the same as or a child of one of the keepers. */ if (is_url) child_relpath = svn_uri_skip_ancestor(keeper, abs_path, temp_pool); else child_relpath = svn_dirent_skip_ancestor(keeper, abs_path); if (child_relpath) { keep_me = FALSE; break; } } /* If this is a new keeper, add its absolute path to ABS_TARGETS and its original path to REL_TARGETS. */ if (keep_me) { APR_ARRAY_PUSH(abs_targets, const char *) = abs_path; APR_ARRAY_PUSH(rel_targets, const char *) = rel_path; } } /* Destroy our temporary pool. */ svn_pool_destroy(temp_pool); /* Make sure we return the list of untainted keeper paths. */ *pcondensed_targets = rel_targets; return SVN_NO_ERROR; }