📄 commit_util.c
字号:
/* * commit_util.c: Driver for the WC commit process. * * ==================================================================== * Copyright (c) 2000-2006 CollabNet. All rights reserved. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://subversion.tigris.org/license-1.html. * If newer versions of this license are posted there, you may use a * newer version instead, at your option. * * This software consists of voluntary contributions made by many * individuals. For exact contribution history, see the revision * history and logs, available at http://subversion.tigris.org/. * ==================================================================== *//* ==================================================================== */#include <string.h>#include <apr_pools.h>#include <apr_hash.h>#include "client.h"#include "svn_path.h"#include "svn_types.h"#include "svn_pools.h"#include "svn_wc.h"#include "svn_props.h"#include "svn_md5.h"#include <assert.h>#include <stdlib.h> /* for qsort() */#include "svn_private_config.h"/*** Uncomment this to turn on commit driver debugging. ***//*#define SVN_CLIENT_COMMIT_DEBUG*//*** Harvesting Commit Candidates ***//* Add a new commit candidate (described by all parameters except `COMMITTABLES') to the COMMITABLES hash. */static voidadd_committable(apr_hash_t *committables, const char *path, svn_node_kind_t kind, const char *url, svn_revnum_t revision, const char *copyfrom_url, svn_revnum_t copyfrom_rev, apr_byte_t state_flags){ apr_pool_t *pool = apr_hash_pool_get(committables); const char *repos_name = SVN_CLIENT__SINGLE_REPOS_NAME; apr_array_header_t *array; svn_client_commit_item2_t *new_item; /* Sanity checks. */ assert(path && url); /* ### todo: Get the canonical repository for this item, which will be the real key for the COMMITTABLES hash, instead of the above bogosity. */ array = apr_hash_get(committables, repos_name, APR_HASH_KEY_STRING); /* E-gads! There is no array for this repository yet! Oh, no problem, we'll just create (and add to the hash) one. */ if (array == NULL) { array = apr_array_make(pool, 1, sizeof(new_item)); apr_hash_set(committables, repos_name, APR_HASH_KEY_STRING, array); } /* Now update pointer values, ensuring that their allocations live in POOL. */ new_item = apr_pcalloc(pool, sizeof(*new_item)); new_item->path = apr_pstrdup(pool, path); new_item->kind = kind; new_item->url = apr_pstrdup(pool, url); new_item->revision = revision; new_item->copyfrom_url = copyfrom_url ? apr_pstrdup(pool, copyfrom_url) : NULL; new_item->copyfrom_rev = copyfrom_rev; new_item->state_flags = state_flags; new_item->wcprop_changes = apr_array_make(pool, 1, sizeof(svn_prop_t *)); /* Now, add the commit item to the array. */ APR_ARRAY_PUSH(array, svn_client_commit_item2_t *) = new_item;}static svn_error_t *check_prop_mods(svn_boolean_t *props_changed, svn_boolean_t *eol_prop_changed, const char *path, svn_wc_adm_access_t *adm_access, apr_pool_t *pool){ apr_array_header_t *prop_mods; int i; *eol_prop_changed = *props_changed = FALSE; SVN_ERR(svn_wc_props_modified_p(props_changed, path, adm_access, pool)); if (! *props_changed) return SVN_NO_ERROR; SVN_ERR(svn_wc_get_prop_diffs(&prop_mods, NULL, path, adm_access, pool)); for (i = 0; i < prop_mods->nelts; i++) { svn_prop_t *prop_mod = &APR_ARRAY_IDX(prop_mods, i, svn_prop_t); if (strcmp(prop_mod->name, SVN_PROP_EOL_STYLE) == 0) *eol_prop_changed = TRUE; } return SVN_NO_ERROR;}/* If there is a commit item for PATH in COMMITTABLES, return it, else return NULL. Use POOL for temporary allocation only. */static svn_client_commit_item2_t *look_up_committable(apr_hash_t *committables, const char *path, apr_pool_t *pool){ apr_hash_index_t *hi; for (hi = apr_hash_first(pool, committables); hi; hi = apr_hash_next(hi)) { const void *key; void *val; apr_array_header_t *these_committables; int i; apr_hash_this(hi, &key, NULL, &val); these_committables = val; for (i = 0; i < these_committables->nelts; i++) { svn_client_commit_item2_t *this_committable = APR_ARRAY_IDX(these_committables, i, svn_client_commit_item2_t *); if (strcmp(this_committable->path, path) == 0) return this_committable; } } return NULL;}/* This implements the svn_wc_entry_callbacks_t->found_entry interface. */static svn_error_t *add_lock_token(const char *path, const svn_wc_entry_t *entry, void *walk_baton, apr_pool_t *pool){ apr_hash_t *lock_tokens = walk_baton; apr_pool_t *token_pool = apr_hash_pool_get(lock_tokens); /* I want every lock-token I can get my dirty hands on! If this entry is switched, so what. We will send an irrelevant lock token. */ if (entry->url && entry->lock_token) apr_hash_set(lock_tokens, apr_pstrdup(token_pool, entry->url), APR_HASH_KEY_STRING, apr_pstrdup(token_pool, entry->lock_token)); return SVN_NO_ERROR;}/* Entry walker callback table to add lock tokens in an hierarchy. */static svn_wc_entry_callbacks_t add_tokens_callbacks = { add_lock_token};/* Recursively search for commit candidates in (and under) PATH (with entry ENTRY and ancestry URL), and add those candidates to COMMITTABLES. If in ADDS_ONLY modes, only new additions are recognized. COPYFROM_URL is the default copyfrom-url for children of copied directories. NONRECURSIVE indicates that this function will not recurse into subdirectories of PATH when PATH is itself a directory. Lock tokens of candidates will be added to LOCK_TOKENS, if non-NULL. JUST_LOCKED indicates whether to treat non-modified items with lock tokens as commit candidates. If in COPY_MODE, treat the entry as if it is destined to be added with history as URL, and add 'deleted' entries to COMMITTABLES as items to delete in the copy destination. If CTX->CANCEL_FUNC is non-null, call it with CTX->CANCEL_BATON to see if the user has cancelled the operation. */static svn_error_t *harvest_committables(apr_hash_t *committables, apr_hash_t *lock_tokens, const char *path, svn_wc_adm_access_t *adm_access, const char *url, const char *copyfrom_url, const svn_wc_entry_t *entry, const svn_wc_entry_t *parent_entry, svn_boolean_t adds_only, svn_boolean_t copy_mode, svn_boolean_t nonrecursive, svn_boolean_t just_locked, svn_client_ctx_t *ctx, apr_pool_t *pool){ apr_hash_t *entries = NULL; svn_boolean_t text_mod = FALSE, prop_mod = FALSE; apr_byte_t state_flags = 0; svn_node_kind_t kind; const char *p_path; svn_boolean_t tc, pc; const char *cf_url = NULL; svn_revnum_t cf_rev = entry->copyfrom_rev; const svn_string_t *propval; svn_boolean_t is_special; apr_pool_t *token_pool = (lock_tokens ? apr_hash_pool_get(lock_tokens) : NULL); /* Early out if the item is already marked as committable. */ if (look_up_committable(committables, path, pool)) return SVN_NO_ERROR; assert(entry); assert(url); if (ctx->cancel_func) SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); /* Make P_PATH the parent dir. */ p_path = svn_path_dirname(path, pool); /* Return error on unknown path kinds. We check both the entry and the node itself, since a path might have changed kind since its entry was written. */ if ((entry->kind != svn_node_file) && (entry->kind != svn_node_dir)) return svn_error_createf (SVN_ERR_NODE_UNKNOWN_KIND, NULL, _("Unknown entry kind for '%s'"), svn_path_local_style(path, pool)); SVN_ERR(svn_io_check_special_path(path, &kind, &is_special, pool)); if ((kind != svn_node_file) && (kind != svn_node_dir) && (kind != svn_node_none)) { return svn_error_createf (SVN_ERR_NODE_UNKNOWN_KIND, NULL, _("Unknown entry kind for '%s'"), svn_path_local_style(path, pool)); } /* Verify that the node's type has not changed before attempting to commit. */ SVN_ERR(svn_wc_prop_get(&propval, SVN_PROP_SPECIAL, path, adm_access, pool)); if ((((! propval) && (is_special))#ifdef HAVE_SYMLINK || ((propval) && (! is_special))#endif /* HAVE_SYMLINK */ ) && (kind != svn_node_none)) { return svn_error_createf (SVN_ERR_NODE_UNEXPECTED_KIND, NULL, _("Entry '%s' has unexpectedly changed special status"), svn_path_local_style(path, pool)); } /* Get a fully populated entry for PATH if we can, and check for conflicts. If this is a directory ... */ if (entry->kind == svn_node_dir) { /* ... then try to read its own entries file so we have a full entry for it (we were going to have to do this eventually to recurse anyway, so... ) */ svn_error_t *err; const svn_wc_entry_t *e = NULL; err = svn_wc_entries_read(&entries, adm_access, copy_mode, pool); /* If we failed to get an entries hash for the directory, no sweat. Cleanup and move along. */ if (err) { svn_error_clear(err); entries = NULL; } /* If we got an entries hash, and the "this dir" entry is present, override our current ENTRY with it, and check for conflicts. */ if ((entries) && ((e = apr_hash_get(entries, SVN_WC_ENTRY_THIS_DIR, APR_HASH_KEY_STRING)))) { entry = e; SVN_ERR(svn_wc_conflicted_p(&tc, &pc, path, entry, pool)); } /* No new entry? Just check the parent's pointer for conflicts. */ else { SVN_ERR(svn_wc_conflicted_p(&tc, &pc, p_path, entry, pool)); } } /* If this is not a directory, check for conflicts using the parent's path. */ else { SVN_ERR(svn_wc_conflicted_p(&tc, &pc, p_path, entry, pool)); } /* Bail now if any conflicts exist for the ENTRY. */ if (tc || pc) return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL, _("Aborting commit: '%s' remains in conflict"), svn_path_local_style(path, pool)); /* If we have our own URL, and we're NOT in COPY_MODE, it wins over the telescoping one(s). In COPY_MODE, URL will always be the URL-to-be of the copied item. */ if ((entry->url) && (! copy_mode)) url = entry->url; /* Check for the deletion case. Deletes occur only when not in "adds-only mode". We use the SVN_CLIENT_COMMIT_ITEM_DELETE flag to represent two slightly different conditions: - The entry is marked as 'deleted'. When copying a mixed-rev wc, we still need to send a delete for that entry, otherwise the object will wrongly exist in the repository copy. - The entry is scheduled for deletion or replacement, which case we need to send a delete either way. */ if ((! adds_only) && ((entry->deleted && entry->schedule == svn_wc_schedule_normal) || (entry->schedule == svn_wc_schedule_delete) || (entry->schedule == svn_wc_schedule_replace))) { state_flags |= SVN_CLIENT_COMMIT_ITEM_DELETE; } /* Check for the trivial addition case. Adds can be explicit (schedule == add) or implicit (schedule == replace ::= delete+add). We also note whether or not this is an add with history here. */ if ((entry->schedule == svn_wc_schedule_add) || (entry->schedule == svn_wc_schedule_replace)) { state_flags |= SVN_CLIENT_COMMIT_ITEM_ADD; if (entry->copyfrom_url) { state_flags |= SVN_CLIENT_COMMIT_ITEM_IS_COPY; cf_url = entry->copyfrom_url; adds_only = FALSE; } else { adds_only = TRUE; } } /* Check for the copied-subtree addition case. */ if ((entry->copied || copy_mode) && (! entry->deleted) && (entry->schedule == svn_wc_schedule_normal)) { svn_revnum_t p_rev = entry->revision - 1; /* arbitrary non-equal value */ svn_boolean_t wc_root = FALSE; /* If this is not a WC root then its parent's revision is admissible for comparitive purposes. */ SVN_ERR(svn_wc_is_wc_root(&wc_root, path, adm_access, pool)); if (! wc_root) { if (parent_entry) p_rev = parent_entry->revision; } else if (! copy_mode) return svn_error_createf (SVN_ERR_WC_CORRUPT, NULL, _("Did not expect '%s' to be a working copy root"), svn_path_local_style(path, pool)); /* If the ENTRY's revision differs from that of its parent, we have to explicitly commit ENTRY as a copy. */ if (entry->revision != p_rev) { state_flags |= SVN_CLIENT_COMMIT_ITEM_ADD; state_flags |= SVN_CLIENT_COMMIT_ITEM_IS_COPY; adds_only = FALSE; cf_rev = entry->revision; if (copy_mode) cf_url = entry->url; else if (copyfrom_url) cf_url = copyfrom_url; else /* ### See issue #830 */ return svn_error_createf (SVN_ERR_BAD_URL, NULL, _("Commit item '%s' has copy flag but no copyfrom URL"), svn_path_local_style(path, pool)); } } /* If an add is scheduled to occur, dig around for some more information about it. */ if (state_flags & SVN_CLIENT_COMMIT_ITEM_ADD) { 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)); /* Regular adds of files have text mods, but for copies we have to test for textual mods. Directories simply don't have text! */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -