📄 commit_util.c
字号:
/* Sort our commit items by their URLs. */ qsort(ci->elts, ci->nelts, ci->elt_size, svn_client__sort_commit_item_urls); /* Loop through the URLs, finding the longest usable ancestor common to all of them, and making sure there are no duplicate URLs. */ for (i = 0; i < ci->nelts; i++) { item = APR_ARRAY_IDX(ci, i, svn_client_commit_item2_t *); url = item->url; if ((last_item) && (strcmp(last_item->url, url) == 0)) return svn_error_createf (SVN_ERR_CLIENT_DUPLICATE_COMMIT_URL, NULL, _("Cannot commit both '%s' and '%s' as they refer to the same URL"), svn_path_local_style(item->path, pool), svn_path_local_style(last_item->path, pool)); /* In the first iteration, our BASE_URL is just our only encountered commit URL to date. After that, we find the longest ancestor between the current BASE_URL and the current commit URL. */ if (i == 0) *base_url = apr_pstrdup(pool, url); else *base_url = svn_path_get_longest_ancestor(*base_url, url, pool); /* If our BASE_URL is itself a to-be-committed item, and it is anything other than an already-versioned directory with property mods, we'll call its parent directory URL the BASE_URL. Why? Because we can't have a file URL as our base -- period -- and all other directory operations (removal, addition, etc.) require that we open that directory's parent dir first. */ /* ### I don't understand the strlen()s here, hmmm. -kff */ if ((strlen(*base_url) == strlen(url)) && (! ((item->kind == svn_node_dir) && item->state_flags == SVN_CLIENT_COMMIT_ITEM_PROP_MODS))) *base_url = svn_path_dirname(*base_url, pool); /* Stash our item here for the next iteration. */ last_item = item; } /* Now that we've settled on a *BASE_URL, go hack that base off of all of our URLs. */ for (i = 0; i < ci->nelts; i++) { svn_client_commit_item2_t *this_item = APR_ARRAY_IDX(ci, i, svn_client_commit_item2_t *); int url_len = strlen(this_item->url); int base_url_len = strlen(*base_url); if (url_len > base_url_len) this_item->url = apr_pstrdup(pool, this_item->url + base_url_len + 1); else this_item->url = ""; }#ifdef SVN_CLIENT_COMMIT_DEBUG /* ### TEMPORARY CODE ### */ fprintf(stderr, "COMMITTABLES: (base URL=%s)\n", *base_url); fprintf(stderr, " FLAGS REV REL-URL (COPY-URL)\n"); for (i = 0; i < ci->nelts; i++) { svn_client_commit_item2_t *this_item = APR_ARRAY_IDX(ci, i, svn_client_commit_item2_t *); char flags[6]; flags[0] = (this_item->state_flags & SVN_CLIENT_COMMIT_ITEM_ADD) ? 'a' : '-'; flags[1] = (this_item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE) ? 'd' : '-'; flags[2] = (this_item->state_flags & SVN_CLIENT_COMMIT_ITEM_TEXT_MODS) ? 't' : '-'; flags[3] = (this_item->state_flags & SVN_CLIENT_COMMIT_ITEM_PROP_MODS) ? 'p' : '-'; flags[4] = (this_item->state_flags & SVN_CLIENT_COMMIT_ITEM_IS_COPY) ? 'c' : '-'; flags[5] = '\0'; fprintf(stderr, " %s %6ld '%s' (%s)\n", flags, this_item->revision, this_item->url ? this_item->url : "", this_item->copyfrom_url ? this_item->copyfrom_url : "none"); } #endif /* SVN_CLIENT_COMMIT_DEBUG */ return SVN_NO_ERROR;}struct file_mod_t{ svn_client_commit_item2_t *item; void *file_baton;};/* A baton for use with the path-based editor driver */struct path_driver_cb_baton{ svn_wc_adm_access_t *adm_access; /* top-level access baton */ const svn_delta_editor_t *editor; /* commit editor */ void *edit_baton; /* commit editor's baton */ apr_hash_t *file_mods; /* hash: path->file_mod_t */ apr_hash_t *tempfiles; /* hash of tempfiles created */ const char *notify_path_prefix; /* notification path prefix */ svn_client_ctx_t *ctx; /* client context baton */ apr_hash_t *commit_items; /* the committables */};/* This implements svn_delta_path_driver_cb_func_t */static svn_error_t *do_item_commit(void **dir_baton, void *parent_baton, void *callback_baton, const char *path, apr_pool_t *pool){ struct path_driver_cb_baton *cb_baton = callback_baton; svn_client_commit_item2_t *item = apr_hash_get(cb_baton->commit_items, path, APR_HASH_KEY_STRING); svn_node_kind_t kind = item->kind; void *file_baton = NULL; const char *copyfrom_url = NULL; apr_pool_t *file_pool = NULL; svn_wc_adm_access_t *adm_access = cb_baton->adm_access; const svn_delta_editor_t *editor = cb_baton->editor; apr_hash_t *file_mods = cb_baton->file_mods; apr_hash_t *tempfiles = cb_baton->tempfiles; const char *notify_path_prefix = cb_baton->notify_path_prefix; svn_client_ctx_t *ctx = cb_baton->ctx; /* Do some initializations. */ *dir_baton = NULL; if (item->copyfrom_url) copyfrom_url = item->copyfrom_url; /* If this is a file with textual mods, we'll be keeping its baton around until the end of the commit. So just lump its memory into a single, big, all-the-file-batons-in-here pool. Otherwise, we can just use POOL, and trust our caller to clean that mess up. */ if ((kind == svn_node_file) && (item->state_flags & SVN_CLIENT_COMMIT_ITEM_TEXT_MODS)) file_pool = apr_hash_pool_get(file_mods); else file_pool = pool; /* Call the cancellation function. */ if (ctx->cancel_func) SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); /* Validation. */ if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_IS_COPY) { if (! copyfrom_url) 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 (! SVN_IS_VALID_REVNUM(item->copyfrom_rev)) return svn_error_createf (SVN_ERR_CLIENT_BAD_REVISION, NULL, _("Commit item '%s' has copy flag but an invalid revision"), svn_path_local_style(path, pool)); } /* If a feedback table was supplied by the application layer, describe what we're about to do to this item. */ if (ctx->notify_func2) { /* Convert an absolute path into a relative one (if possible.) */ const char *npath = NULL; svn_wc_notify_t *notify; if (notify_path_prefix) { if (strcmp(notify_path_prefix, item->path)) npath = svn_path_is_child(notify_path_prefix, item->path, pool); else npath = "."; } if (! npath) npath = item->path; /* Otherwise just use full path */ if ((item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE) && (item->state_flags & SVN_CLIENT_COMMIT_ITEM_ADD)) { /* We don't print the "(bin)" notice for binary files when replacing, only when adding. So we don't bother to get the mime-type here. */ notify = svn_wc_create_notify(npath, svn_wc_notify_commit_replaced, pool); } else if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE) { notify = svn_wc_create_notify(npath, svn_wc_notify_commit_deleted, pool); } else if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_ADD) { notify = svn_wc_create_notify(npath, svn_wc_notify_commit_added, pool); if (item->kind == svn_node_file) { const svn_string_t *propval; SVN_ERR(svn_wc_prop_get (&propval, SVN_PROP_MIME_TYPE, item->path, adm_access, pool)); if (propval) notify->mime_type = propval->data; } } else if ((item->state_flags & SVN_CLIENT_COMMIT_ITEM_TEXT_MODS) || (item->state_flags & SVN_CLIENT_COMMIT_ITEM_PROP_MODS)) { notify = svn_wc_create_notify(npath, svn_wc_notify_commit_modified, pool); if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_TEXT_MODS) notify->content_state = svn_wc_notify_state_changed; else notify->content_state = svn_wc_notify_state_unchanged; if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_PROP_MODS) notify->prop_state = svn_wc_notify_state_changed; else notify->prop_state = svn_wc_notify_state_unchanged; } else notify = NULL; if (notify) { notify->kind = item->kind; (*ctx->notify_func2)(ctx->notify_baton2, notify, pool); } } /* If this item is supposed to be deleted, do so. */ if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE) { assert(parent_baton); SVN_ERR(editor->delete_entry(path, item->revision, parent_baton, pool)); } /* If this item is supposed to be added, do so. */ if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_ADD) { if (kind == svn_node_file) { assert(parent_baton); SVN_ERR(editor->add_file (path, parent_baton, copyfrom_url, copyfrom_url ? item->copyfrom_rev : SVN_INVALID_REVNUM, file_pool, &file_baton)); } else { assert(parent_baton); SVN_ERR(editor->add_directory (path, parent_baton, copyfrom_url, copyfrom_url ? item->copyfrom_rev : SVN_INVALID_REVNUM, pool, dir_baton)); } } /* Now handle property mods. */ if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_PROP_MODS) { const char *tempfile; const svn_wc_entry_t *tmp_entry; if (kind == svn_node_file) { if (! file_baton) { assert(parent_baton); SVN_ERR(editor->open_file(path, parent_baton, item->revision, file_pool, &file_baton)); } } else { if (! *dir_baton) { if (! parent_baton) { SVN_ERR(editor->open_root (cb_baton->edit_baton, item->revision, pool, dir_baton)); } else { SVN_ERR(editor->open_directory (path, parent_baton, item->revision, pool, dir_baton)); } } } SVN_ERR(svn_wc_entry(&tmp_entry, item->path, adm_access, TRUE, pool)); SVN_ERR(svn_wc_transmit_prop_deltas (item->path, adm_access, tmp_entry, editor, (kind == svn_node_dir) ? *dir_baton : file_baton, &tempfile, pool)); if (tempfile && tempfiles) { tempfile = apr_pstrdup(apr_hash_pool_get(tempfiles), tempfile); apr_hash_set(tempfiles, tempfile, APR_HASH_KEY_STRING, (void *)1); } } /* Finally, handle text mods (in that we need to open a file if it hasn't already been opened, and we need to put the file baton in our FILES hash). */ if ((kind == svn_node_file) && (item->state_flags & SVN_CLIENT_COMMIT_ITEM_TEXT_MODS)) { struct file_mod_t *mod = apr_palloc(file_pool, sizeof(*mod)); if (! file_baton) { assert(parent_baton); SVN_ERR(editor->open_file(path, parent_baton, item->revision, file_pool, &file_baton)); } /* Add this file mod to the FILE_MODS hash. */ mod->item = item; mod->file_baton = file_baton; apr_hash_set(file_mods, item->url, APR_HASH_KEY_STRING, mod); } else if (file_baton) { /* Close any outstanding file batons that didn't get caught by the "has local mods" conditional above. */ SVN_ERR(editor->close_file(file_baton, NULL, file_pool)); } return SVN_NO_ERROR;}#ifdef SVN_CLIENT_COMMIT_DEBUG/* Prototype for function below */static svn_error_t *get_test_editor(const svn_delta_editor_t **editor, void **edit_baton, const svn_delta_editor_t *real_editor, void *real_eb, const char *base_url, apr_pool_t *pool);#endif /* SVN_CLIENT_COMMIT_DEBUG */svn_error_t *svn_client__do_commit(const char *base_url, apr_array_header_t *commit_items, svn_wc_adm_access_t *adm_access, const svn_delta_editor_t *editor, void *edit_baton, const char *notify_path_prefix, apr_hash_t **tempfiles, apr_hash_t **digests, svn_client_ctx_t *ctx, apr_pool_t *pool){ apr_hash_t *file_mods = apr_hash_make(pool); apr_hash_t *items_hash = apr_hash_make(pool); apr_pool_t *subpool = svn_pool_create(pool); apr_hash_index_t *hi; int i; struct path_driver_cb_baton cb_baton; apr_array_header_t *paths = apr_array_make(pool, commit_items->nelts, sizeof(const char *));#ifdef SVN_CLIENT_COMMIT_DEBUG { SVN_ERR(get_test_editor(&editor, &edit_baton, editor, edit_baton, base_url, pool)); }#endif /* SVN_CLIENT_COMMIT_DEBUG */ /* If the caller wants us to track temporary file creation, create a hash to store those paths in. */ if (tempfiles) *tempfiles = apr_hash_make(pool); /* Ditto for the md5 digests. */ if (digests) *digests = apr_hash_make(pool); /* Build a hash from our COMMIT_ITEMS array, keyed on the URI-decoded relative paths (which come from the item URLs). And keep an array of those decoded paths, too. */ for (i = 0; i < commit_items->nelts; i++) { svn_client_commit_item2_t *item = APR_ARRAY_IDX(commit_items, i, svn_client_commit_item2_t *); const char *path = svn_path_uri_decode(item->url, pool); apr_hash_set(items_hash, path, APR_HASH_KEY_STRING, item); APR_ARRAY_PUSH(paths, const char *) = path; } /* Setup the callback baton. */ cb_baton.adm_access = adm_access; cb_baton.editor = editor; cb_baton.edit_baton = edit_baton; cb_baton.file_mods = file_mods; cb_baton.tempfiles = tempfiles ? *tempfiles : NULL; cb_baton.notify_path_prefix = notify_path_prefix; cb_baton.ctx = ctx; cb_baton.commit_items = items_hash; /* Drive the commit editor! */ SVN_ERR(svn_delta_path_driver(editor, edit_baton, SVN_INVALID_REVNUM, paths, do_item_commit, &cb_baton, pool)); /* Transmit outstanding text deltas. */ for (hi = apr_hash_first(pool, file_mods); hi; hi = apr_hash_next(hi)) { const void *key; apr_ssize_t klen;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -