📄 commit_util.c
字号:
if (entry->kind == svn_node_file) { /* Check for text mods. If EOL_PROP_CHANGED is TRUE, then we need to force a translated byte-for-byte comparison against the text-base so that a timestamp comparison won't bail out early. Depending on how the svn:eol-style prop was changed, we might have to send new text to the server to match the new newline style. */ if (state_flags & SVN_CLIENT_COMMIT_ITEM_IS_COPY) SVN_ERR(svn_wc_text_modified_p(&text_mod, path, eol_prop_changed, adm_access, pool)); else text_mod = TRUE; } } /* Else, if we aren't deleting this item, we'll have to look for local text or property mods to determine if the path might be committable. */ else if (! (state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE)) { svn_boolean_t eol_prop_changed; /* See if there are property modifications to send. */ SVN_ERR(check_prop_mods(&prop_mod, &eol_prop_changed, path, adm_access, pool)); /* Check for text mods on files. If EOL_PROP_CHANGED is TRUE, then we need to force a translated byte-for-byte comparison against the text-base so that a timestamp comparison won't bail out early. Depending on how the svn:eol-style prop was changed, we might have to send new text to the server to match the new newline style. */ if (entry->kind == svn_node_file) SVN_ERR(svn_wc_text_modified_p(&text_mod, path, eol_prop_changed, adm_access, pool)); } /* Set text/prop modification flags accordingly. */ if (text_mod) state_flags |= SVN_CLIENT_COMMIT_ITEM_TEXT_MODS; if (prop_mod) state_flags |= SVN_CLIENT_COMMIT_ITEM_PROP_MODS; /* If the entry has a lock token and it is already a commit candidate, or the caller wants unmodified locked items to be treated as such, note this fact. */ if (entry->lock_token && (state_flags || just_locked)) state_flags |= SVN_CLIENT_COMMIT_ITEM_LOCK_TOKEN; /* Now, if this is something to commit, add it to our list. */ if (state_flags) { /* Finally, add the committable item. */ add_committable(committables, path, entry->kind, url, entry->revision, cf_url, cf_rev, state_flags); if (lock_tokens && entry->lock_token) apr_hash_set(lock_tokens, apr_pstrdup(token_pool, url), APR_HASH_KEY_STRING, apr_pstrdup(token_pool, entry->lock_token)); } /* For directories, recursively handle each of their entries (except when the directory is being deleted, unless the deletion is part of a replacement ... how confusing). Oh, and don't recurse at all if this is a nonrecursive commit. ### We'll probably make the whole 'nonrecursive' concept go away soon and be replaced with the more sophisticated Depth0|Depth1|DepthInfinity. */ if (entries && (! nonrecursive) && ((! (state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE)) || (state_flags & SVN_CLIENT_COMMIT_ITEM_ADD))) { apr_hash_index_t *hi; const svn_wc_entry_t *this_entry; apr_pool_t *loop_pool = svn_pool_create(pool); /* Loop over all other entries in this directory, skipping the "this dir" entry. */ for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi)) { const void *key; void *val; const char *name; const char *full_path; const char *used_url = NULL; const char *name_uri = NULL; const char *this_cf_url = cf_url ? cf_url : copyfrom_url; svn_wc_adm_access_t *dir_access = adm_access; svn_pool_clear(loop_pool); /* Get the next entry. Name is an entry name; value is an entry structure. */ apr_hash_this(hi, &key, NULL, &val); name = key; /* Skip "this dir" */ if (! strcmp(name, SVN_WC_ENTRY_THIS_DIR)) continue; this_entry = val; name_uri = svn_path_uri_encode(name, loop_pool); full_path = svn_path_join(path, name, loop_pool); if (this_cf_url) this_cf_url = svn_path_join(this_cf_url, name_uri, loop_pool); /* We'll use the entry's URL if it has one and if we aren't in copy_mode, else, we'll just extend the parent's URL with the entry's basename. */ if ((! this_entry->url) || (copy_mode)) used_url = svn_path_join(url, name_uri, loop_pool); /* Recurse. */ if (this_entry->kind == svn_node_dir) { svn_error_t *lockerr; lockerr = svn_wc_adm_retrieve(&dir_access, adm_access, full_path, loop_pool); if (lockerr) { if (lockerr->apr_err == SVN_ERR_WC_NOT_LOCKED) { /* A missing, schedule-delete child dir is allowable. Just don't try to recurse. */ svn_node_kind_t childkind; svn_error_t *err = svn_io_check_path(full_path, &childkind, loop_pool); if (! err && childkind == svn_node_none && this_entry->schedule == svn_wc_schedule_delete) { add_committable(committables, full_path, this_entry->kind, used_url, SVN_INVALID_REVNUM, NULL, SVN_INVALID_REVNUM, SVN_CLIENT_COMMIT_ITEM_DELETE); svn_error_clear(lockerr); continue; /* don't recurse! */ } else { svn_error_clear(err); return lockerr; } } else return lockerr; } } else dir_access = adm_access; SVN_ERR(harvest_committables (committables, lock_tokens, full_path, dir_access, used_url ? used_url : this_entry->url, this_cf_url, this_entry, entry, adds_only, copy_mode, FALSE, just_locked, ctx, loop_pool)); } svn_pool_destroy(loop_pool); } /* Fetch lock tokens for descendants of deleted directories. */ if (lock_tokens && entry->kind == svn_node_dir && (state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE)) { SVN_ERR(svn_wc_walk_entries2(path, adm_access, &add_tokens_callbacks, lock_tokens, FALSE, ctx->cancel_func, ctx->cancel_baton, pool)); } return SVN_NO_ERROR;}svn_error_t *svn_client__harvest_committables(apr_hash_t **committables, apr_hash_t **lock_tokens, svn_wc_adm_access_t *parent_dir, apr_array_header_t *targets, svn_boolean_t nonrecursive, svn_boolean_t just_locked, svn_client_ctx_t *ctx, apr_pool_t *pool){ int i = 0; svn_wc_adm_access_t *dir_access; apr_pool_t *subpool = svn_pool_create(pool); /* It's possible that one of the named targets has a parent that is * itself scheduled for addition or replacement -- that is, the * parent is not yet versioned in the repository. This is okay, as * long as the parent itself is part of this same commit, either * directly, or by virtue of a grandparent, great-grandparent, etc, * being part of the commit. * * Since we don't know what's included in the commit until we've * harvested all the targets, we can't reliably check this as we * go. So in `danglers', we record named targets whose parents * are unversioned, then after harvesting the total commit group, we * check to make sure those parents are included. * * Each key of danglers is an unversioned parent. The (const char *) * value is one of that parent's children which is named as part of * the commit; the child is included only to make a better error * message. * * (The reason we don't bother to check unnamed -- i.e, implicit -- * targets is that they can only join the commit if their parents * did too, so this situation can't arise for them.) */ apr_hash_t *danglers = apr_hash_make(pool); /* Create the COMMITTABLES hash. */ *committables = apr_hash_make(pool); /* And the LOCK_TOKENS dito. */ *lock_tokens = apr_hash_make(pool); do { svn_wc_adm_access_t *adm_access; const svn_wc_entry_t *entry; const char *target; svn_pool_clear(subpool); /* Add the relative portion of our full path (if there are no relative paths, TARGET will just be PARENT_DIR for a single iteration. */ target = svn_path_join_many(subpool, svn_wc_adm_access_path(parent_dir), targets->nelts ? (((const char **) targets->elts)[i]) : NULL, NULL); /* No entry? This TARGET isn't even under version control! */ SVN_ERR(svn_wc_adm_probe_retrieve(&adm_access, parent_dir, target, subpool)); SVN_ERR(svn_wc_entry(&entry, target, adm_access, FALSE, subpool)); if (! entry) return svn_error_createf (SVN_ERR_ENTRY_NOT_FOUND, NULL, _("'%s' is not under version control"), target); if (! entry->url) return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, _("Entry for '%s' has no URL"), svn_path_local_style(target, pool)); /* We have to be especially careful around entries scheduled for addition or replacement. */ if ((entry->schedule == svn_wc_schedule_add) || (entry->schedule == svn_wc_schedule_replace)) { const char *parent, *base_name; svn_wc_adm_access_t *parent_access; const svn_wc_entry_t *p_entry = NULL; svn_error_t *err; svn_path_split(target, &parent, &base_name, subpool); err = svn_wc_adm_retrieve(&parent_access, parent_dir, parent, subpool); if (err && err->apr_err == SVN_ERR_WC_NOT_LOCKED) { svn_error_clear(err); SVN_ERR(svn_wc_adm_open3(&parent_access, NULL, parent, FALSE, 0, ctx->cancel_func, ctx->cancel_baton, subpool)); } else if (err) { return err; } SVN_ERR(svn_wc_entry(&p_entry, parent, parent_access, FALSE, subpool)); if (! p_entry) return svn_error_createf (SVN_ERR_WC_CORRUPT, NULL, _("'%s' is scheduled for addition within unversioned parent"), svn_path_local_style(target, pool)); if ((p_entry->schedule == svn_wc_schedule_add) || (p_entry->schedule == svn_wc_schedule_replace)) { /* Copy the parent and target into pool; subpool lasts only for this loop iteration, and we check danglers after the loop is over. */ apr_hash_set(danglers, apr_pstrdup(pool, parent), APR_HASH_KEY_STRING, apr_pstrdup(pool, target)); } } /* If this entry is marked as 'copied' but scheduled normally, then it should be the child of something else marked for addition with history. */ if ((entry->copied) && (entry->schedule == svn_wc_schedule_normal)) return svn_error_createf (SVN_ERR_ILLEGAL_TARGET, NULL, _("Entry for '%s' is marked as 'copied' but is not itself scheduled" "\nfor addition. Perhaps you're committing a target that is\n" "inside an unversioned (or not-yet-versioned) directory?"), svn_path_local_style(target, pool)); /* Handle our TARGET. */ SVN_ERR(svn_wc_adm_retrieve(&dir_access, parent_dir, (entry->kind == svn_node_dir ? target : svn_path_dirname(target, subpool)), subpool)); SVN_ERR(harvest_committables(*committables, *lock_tokens, target, dir_access, entry->url, NULL, entry, NULL, FALSE, FALSE, nonrecursive, just_locked, ctx, subpool)); i++; } while (i < targets->nelts); /* Make sure that every path in danglers is part of the commit. */ { apr_hash_index_t *hi; for (hi = apr_hash_first(pool, danglers); hi; hi = apr_hash_next(hi)) { const void *key; void *val; const char *dangling_parent, *dangling_child; /* Get the next entry. Name is an entry name; value is an entry structure. */ apr_hash_this(hi, &key, NULL, &val); dangling_parent = key; dangling_child = val; if (! look_up_committable(*committables, dangling_parent, pool)) { return svn_error_createf (SVN_ERR_ILLEGAL_TARGET, NULL, _("'%s' is not under version control " "and is not part of the commit, " "yet its child '%s' is part of the commit"), /* Probably one or both of these is an entry, but safest to local_stylize just in case. */ svn_path_local_style(dangling_parent, pool), svn_path_local_style(dangling_child, pool)); } } } svn_pool_destroy(subpool); return SVN_NO_ERROR;}svn_error_t *svn_client__get_copy_committables(apr_hash_t **committables, const char *new_url, const char *target, svn_wc_adm_access_t *adm_access, svn_client_ctx_t *ctx, apr_pool_t *pool){ const svn_wc_entry_t *entry; /* Create the COMMITTABLES hash. */ *committables = apr_hash_make(pool); /* Read the entry for TARGET. */ SVN_ERR(svn_wc_entry(&entry, target, adm_access, FALSE, pool)); if (! entry) return svn_error_createf (SVN_ERR_ENTRY_NOT_FOUND, NULL, _("'%s' is not under version control"), svn_path_local_style(target, pool)); /* Handle our TARGET. */ SVN_ERR(harvest_committables(*committables, NULL, target, adm_access, new_url, entry->url, entry, NULL, FALSE, TRUE, FALSE, FALSE, ctx, pool)); return SVN_NO_ERROR;}int svn_client__sort_commit_item_urls(const void *a, const void *b){ const svn_client_commit_item2_t *item1 = *((const svn_client_commit_item2_t * const *) a); const svn_client_commit_item2_t *item2 = *((const svn_client_commit_item2_t * const *) b); return svn_path_compare_paths(item1->url, item2->url);}svn_error_t *svn_client__condense_commit_items(const char **base_url, apr_array_header_t *commit_items, apr_pool_t *pool){ apr_array_header_t *ci = commit_items; /* convenience */ const char *url; svn_client_commit_item2_t *item, *last_item = NULL; int i; assert(ci && ci->nelts);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -