📄 status.c
字号:
svn_wc_status_func_t status_func,
void *status_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
apr_hash_t *entries;
apr_hash_index_t *hi;
const svn_wc_entry_t *dir_entry;
const char *fullpath, *path = svn_wc_adm_access_path (adm_access);
apr_hash_t *dirents;
apr_array_header_t *patterns = NULL;
apr_pool_t *iterpool, *subpool = svn_pool_create (pool);
/* See if someone wants to cancel this operation. */
if (cancel_func)
SVN_ERR (cancel_func (cancel_baton));
/* Load entries file for the directory into the requested pool. */
SVN_ERR (svn_wc_entries_read (&entries, adm_access, FALSE, subpool));
/* Read PATH's dirents. */
SVN_ERR (svn_io_get_dirents (&dirents, path, subpool));
/* Get this directory's entry. */
SVN_ERR (svn_wc_entry (&dir_entry, path, adm_access, FALSE, subpool));
/* Unless specified, add default ignore regular expressions and try
to add any svn:ignore properties from the parent directory. */
if (ignores)
{
patterns = apr_array_make (subpool, 1, sizeof (const char *));
SVN_ERR (collect_ignore_patterns (patterns, ignores,
adm_access, subpool));
}
/* If "this dir" has "svn:externals" property set on it, store its
name and value in traversal_info. Also, we want to track the
externals internally so we can report status more accurately. */
if (eb->traversal_info)
{
const svn_string_t *prop_val;
SVN_ERR (svn_wc_prop_get (&prop_val, SVN_PROP_EXTERNALS, path,
adm_access, subpool));
if (prop_val)
{
apr_pool_t *dup_pool = eb->traversal_info->pool;
const char *dup_path = apr_pstrdup (dup_pool, path);
const char *dup_val = apr_pstrmemdup (dup_pool, prop_val->data,
prop_val->len);
apr_array_header_t *ext_items;
int i;
/* First things first -- we put the externals information
into the "global" traversal info structure. */
apr_hash_set (eb->traversal_info->externals_old,
dup_path, APR_HASH_KEY_STRING, dup_val);
apr_hash_set (eb->traversal_info->externals_new,
dup_path, APR_HASH_KEY_STRING, dup_val);
/* Now, parse the thing, and copy the parsed results into
our "global" externals hash. */
SVN_ERR (svn_wc_parse_externals_description2 (&ext_items, path,
dup_val, dup_pool));
for (i = 0; ext_items && i < ext_items->nelts; i++)
{
svn_wc_external_item_t *item;
item = APR_ARRAY_IDX (ext_items, i, svn_wc_external_item_t *);
apr_hash_set (eb->externals, svn_path_join (path,
item->target_dir,
dup_pool),
APR_HASH_KEY_STRING, item);
}
}
}
/* Early out -- our caller only cares about a single ENTRY in this
directory. */
if (entry)
{
const svn_wc_entry_t *entry_entry;
entry_entry = apr_hash_get (entries, entry, APR_HASH_KEY_STRING);
/* If ENTRY is versioned, send its versioned status. */
if (entry_entry)
{
SVN_ERR (handle_dir_entry (eb, adm_access, entry, dir_entry,
entry_entry, ignores, descend, get_all,
no_ignore, status_func, status_baton,
cancel_func, cancel_baton, subpool));
}
/* Otherwise, if it exists, send its unversioned status. */
else if (apr_hash_get (dirents, entry, APR_HASH_KEY_STRING))
{
svn_node_kind_t kind;
fullpath = svn_path_join (path, entry, subpool);
SVN_ERR (svn_io_check_path (path, &kind, subpool));
SVN_ERR (send_unversioned_item (entry, kind, adm_access,
patterns, eb->externals, no_ignore,
status_func, status_baton, subpool));
}
/* Regardless, we're done here. Let's go home. */
return SVN_NO_ERROR;
}
/** If we get here, ENTRY is NULL and we are handling all the
directory entries. */
/* Make our iteration pool. */
iterpool = svn_pool_create (subpool);
/* Add empty status structures for each of the unversioned things. */
for (hi = apr_hash_first (subpool, dirents); hi; hi = apr_hash_next (hi))
{
const void *key;
apr_ssize_t klen;
void *val;
svn_node_kind_t *path_kind;
apr_hash_this (hi, &key, &klen, &val);
/* Skip versioned things, and skip the administrative
directory. */
if ((apr_hash_get (entries, key, klen))
|| (strcmp (key, SVN_WC_ADM_DIR_NAME) == 0))
continue;
/* Clear the iteration subpool. */
svn_pool_clear (iterpool);
/* Make an unversioned status item for KEY, and put it into our
return hash. */
path_kind = val;
SVN_ERR (send_unversioned_item (key, *path_kind, adm_access,
patterns, eb->externals, no_ignore,
status_func, status_baton, iterpool));
}
/* Handle "this-dir" first. */
if (! skip_this_dir)
SVN_ERR (send_status_structure (path, adm_access, dir_entry,
parent_entry, svn_node_dir,
get_all, FALSE, status_func,
status_baton, subpool));
/* Loop over entries hash */
for (hi = apr_hash_first (pool, entries); hi; hi = apr_hash_next (hi))
{
const void *key;
void *val;
/* Get the next dirent */
apr_hash_this (hi, &key, NULL, &val);
/* ### todo: What if the subdir is from another repository? */
/* Skip "this-dir". */
if (strcmp (key, SVN_WC_ENTRY_THIS_DIR) == 0)
continue;
/* Clear the iteration subpool. */
svn_pool_clear (iterpool);
/* Handle this directory entry (possibly recursing). */
SVN_ERR (handle_dir_entry (eb, adm_access, key, dir_entry, val, ignores,
descend, get_all, no_ignore,
status_func, status_baton, cancel_func,
cancel_baton, iterpool));
}
/* Destroy our subpools. */
svn_pool_destroy (subpool);
return SVN_NO_ERROR;
}
/*** Helpers ***/
/* A faux status callback function for stashing STATUS item in an hash
(which is the BATON), keyed on PATH. This implements the
svn_wc_status_func_t interface. */
static void
hash_stash (void *baton,
const char *path,
svn_wc_status_t *status)
{
apr_hash_t *stat_hash = baton;
apr_pool_t *hash_pool = apr_hash_pool_get (stat_hash);
assert (! apr_hash_get (stat_hash, path, APR_HASH_KEY_STRING));
apr_hash_set (stat_hash, apr_pstrdup (hash_pool, path),
APR_HASH_KEY_STRING, svn_wc_dup_status (status, hash_pool));
}
/* Look up the key PATH in STATUSHASH. 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
"network" fields. */
static svn_error_t *
tweak_statushash (apr_hash_t *statushash,
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_wc_status_t *statstruct;
apr_pool_t *pool = apr_hash_pool_get (statushash);
/* Is PATH already a hash-key? */
statstruct = apr_hash_get (statushash, path, APR_HASH_KEY_STRING);
/* If not, make it so. */
if (! statstruct)
{
/* This should only be missing from the hash if it's being added
from the repository status drive. */
assert (repos_text_status == svn_wc_status_added);
/* Use the public API to get a statstruct, and put it into the hash. */
SVN_ERR (svn_wc_status (&statstruct, path, NULL, pool));
apr_hash_set (statushash, apr_pstrdup (pool, path),
APR_HASH_KEY_STRING, statstruct);
}
/* Merge a repos "delete" + "add" into a single "replace". */
if ((repos_text_status == svn_wc_status_added)
&& (statstruct->repos_text_status == svn_wc_status_deleted))
repos_text_status = svn_wc_status_replaced;
/* Tweak the structure's repos fields. */
if (repos_text_status)
statstruct->repos_text_status = repos_text_status;
if (repos_prop_status)
statstruct->repos_prop_status = repos_prop_status;
return SVN_NO_ERROR;
}
/* Create a new dir_baton for subdir PATH. */
static svn_error_t *
make_dir_baton (void **dir_baton,
const char *path,
struct edit_baton *edit_baton,
struct dir_baton *parent_baton,
apr_pool_t *pool)
{
struct dir_baton *pb = parent_baton;
struct edit_baton *eb = edit_baton;
struct dir_baton *d = apr_pcalloc (pool, sizeof (*d));
const char *full_path;
svn_wc_status_t *parent_status;
/* Don't do this. Just do NOT do this to me. */
if (pb && (! path))
abort();
/* Construct the full path of this directory. */
if (pb)
full_path = svn_path_join (eb->anchor, path, pool);
else
full_path = apr_pstrdup (pool, eb->anchor);
/* Finish populating the baton members. */
d->path = full_path;
d->name = path ? (svn_path_basename (path, pool)) : NULL;
d->edit_baton = edit_baton;
d->parent_baton = parent_baton;
d->pool = pool;
d->statii = apr_hash_make (pool);
/* Get the status for this path's children. Of course, we only want
to do this if the path is versioned as a directory. */
if (pb)
parent_status = apr_hash_get (pb->statii, d->path, APR_HASH_KEY_STRING);
else
parent_status = eb->anchor_status;
if (parent_status
&& (parent_status->text_status != svn_wc_status_unversioned)
&& (parent_status->text_status != svn_wc_status_deleted)
&& (parent_status->text_status != svn_wc_status_missing)
&& (parent_status->text_status != svn_wc_status_obstructed)
&& (parent_status->entry->kind == svn_node_dir)
&& (eb->descend || (! pb)))
{
svn_wc_adm_access_t *dir_access;
apr_array_header_t *ignores = eb->ignores;
SVN_ERR (svn_wc_adm_retrieve (&dir_access, eb->adm_access,
d->path, pool));
SVN_ERR (get_dir_status (eb, parent_status->entry, dir_access, NULL,
ignores, FALSE, TRUE, TRUE, TRUE, hash_stash,
d->statii, NULL, NULL, pool));
}
*dir_baton = d;
return SVN_NO_ERROR;
}
/* Make a file baton, using a new subpool of PARENT_DIR_BATON's pool.
NAME is just one component, not a path. */
static struct file_baton *
make_file_baton (struct dir_baton *parent_dir_baton,
const char *path,
apr_pool_t *pool)
{
struct dir_baton *pb = parent_dir_baton;
struct edit_baton *eb = pb->edit_baton;
struct file_baton *f = apr_pcalloc (pool, sizeof (*f));
const char *full_path;
/* Construct the full path of this directory. */
if (pb)
full_path = svn_path_join (eb->anchor, path, pool);
else
full_path = apr_pstrdup (pool, eb->anchor);
/* Finish populating the baton members. */
f->path = full_path;
f->name = svn_path_basename (path, pool);
f->pool = pool;
f->dir_baton = pb;
f->edit_baton = eb;
return f;
}
/* Return a boolean answer to the question "Is STATUS something that
should be reported?". EB is the edit baton. */
static svn_boolean_t
is_sendable_status (svn_wc_status_t *status,
struct edit_baton *eb)
{
/* If the repository status was touched at all, it's interesting. */
if (status->repos_text_status != svn_wc_status_none)
return TRUE;
if (status->repos_prop_status != svn_wc_status_none)
return TRUE;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -