Hi Stefan!
A few questions and a WIP patch for making the patch code deal with
property targets being dirs.
* How should we deal with SVN_ERR_ILLEGAL_TARGET? svn_wc_prop_set()
throws one of those if we for instance try to set svn:executable on a
dir or svn:ignore on a file. Should we do some kind of skipped prop
notification?
* Are you ok with the change I made in resolve_target_path()? If we only
have property changes to a file than it's ok to have a dir as a
target.
* What should we do if the target dir for a property is locally_deleted?
I think we should skip it (that's what the code does right now).
I said to you earlier on IRC that I didn't quite understand how we
determined if we had a valid patch target. Do you agree with my doc
change for resolve_target_path() and the two sections below?
* Do you agree on this definition:
! target->skipped => We have an add/del/mod for target
target->node_kind_on_disk => We have a mod for target. (special case:
if we're adding to an existing file we might have an add if the target
already is the expected result after patching).
* For properties I have:
! target->skipped && is_prop_hunk => We have an add/del/mod for
prop_target.
prop_target->content_info->stream => We have a mod for prop_target.
My recent work has been an attempt to make the matching/applying part as
generic as possible, e.g. work for both properties and texts. My limited
testing suggests I've succeeded with that. But I'll have to introduce a
couple of if statements and flags to distinguish between props and text
changes.
[[[
Enable the patch code to use dirs as targets if we only have property
changes.
* subversion/libsvn_client/patch.c
(patch_target_t): Add fields 'has_text_changes' and 'has_prop_changes'
to be able to decide if we should install tmp files for text and/or
props. It's needed since we may have a dir as a target for a
property. If we'd try to copy the tmp file for the text changes onto
a dir we get an error.
(resolve_target_path): Add new parameter 'only_prop_changes' to help
us determine the case when a dir is a valid target.
(init_patch_target): Update caller of resolve_target_path().
(apply_hunk): Record if we've done any changes to props or text
content.
(apply_patches): Only call install_patched_target() if we have changed
the text contents.
]]]
Thanks,
Daniel
Index: subversion/libsvn_client/patch.c
===================================================================
--- subversion/libsvn_client/patch.c (revision 979179)
+++ subversion/libsvn_client/patch.c (arbetskopia)
@@ -186,6 +186,12 @@ typedef struct patch_target_t {
/* True if the target has the executable bit set. */
svn_boolean_t executable;
+ /* True if the patch changes the text of the target */
+ svn_boolean_t has_text_changes;
+
+ /* True if the patch changes any of the properties of the target */
+ svn_boolean_t has_prop_changes;
+
/* All the information that is specifict to the content of the target. */
target_content_info_t *content_info;
@@ -303,6 +309,9 @@ obtain_eol_and_keywords_for_file(apr_hash_t **keyw
* If possible, determine TARGET->WC_PATH, TARGET->ABS_PATH, TARGET->KIND,
* TARGET->ADDED, and TARGET->PARENT_DIR_EXISTS.
* Indicate in TARGET->SKIPPED whether the target should be skipped.
+ * The target should be skipped if the versioned path does not exist or does
+ * not match our expected type, e.g. it should be a file unless we're
+ * dealing with a patch that has ONLY_PROP_CHANGES.
* STRIP_COUNT specifies the number of leading path components
* which should be stripped from target paths in the patch.
* Use RESULT_POOL for allocations of fields in TARGET.
@@ -312,6 +321,7 @@ resolve_target_path(patch_target_t *target,
const char *path_from_patchfile,
const char *local_abspath,
int strip_count,
+ svn_boolean_t only_prop_changes,
svn_wc_context_t *wc_ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
@@ -341,6 +351,8 @@ resolve_target_path(patch_target_t *target,
{
target->local_relpath = svn_dirent_is_child(local_abspath, stripped_path,
result_pool);
+
+ /* ### We need to allow setting props on the wc root dir */
if (! target->local_relpath)
{
/* The target path is either outside of the working copy
@@ -403,7 +415,9 @@ resolve_target_path(patch_target_t *target,
}
SVN_ERR(svn_wc_read_kind(&target->db_kind, wc_ctx, target->local_abspath,
FALSE, scratch_pool));
- if (target->db_kind == svn_node_dir)
+
+ /* We allow properties to have dirs as targets */
+ if (target->db_kind == svn_node_dir && ! only_prop_changes)
{
/* ### We cannot yet replace a locally deleted dir with a file,
* ### but some day we might want to allow it. */
@@ -502,6 +516,7 @@ init_patch_target(patch_target_t **patch_target,
{
patch_target_t *target;
target_content_info_t *content_info;
+ svn_boolean_t only_prop_changes = patch->hunks->nelts == 0 ? TRUE : FALSE;
content_info = apr_pcalloc(result_pool, sizeof(*content_info));
@@ -525,8 +540,8 @@ init_patch_target(patch_target_t **patch_target,
target->pool = result_pool;
SVN_ERR(resolve_target_path(target, patch->new_filename,
- base_dir, strip_count, wc_ctx,
- result_pool, scratch_pool));
+ base_dir, strip_count, only_prop_changes,
+ wc_ctx, result_pool, scratch_pool));
if (! target->skipped)
{
const char *diff_header;
@@ -1350,6 +1365,11 @@ apply_hunk(patch_target_t *target, target_content_
while (! eof);
svn_pool_destroy(iterpool);
+ if (is_prop_hunk)
+ target->has_prop_changes = TRUE;
+ else
+ target->has_text_changes = TRUE;
+
return SVN_NO_ERROR;
}
@@ -2328,9 +2348,14 @@ apply_patches(void *baton,
patch_target_info_t *) = target_info;
if (! target->skipped)
- SVN_ERR(install_patched_target(target, btn->abs_wc_path,
- btn->ctx, btn->dry_run,
- iterpool));
+ {
+ if (target->has_text_changes)
+ SVN_ERR(install_patched_target(target, btn->abs_wc_path,
+ btn->ctx, btn->dry_run,
+ iterpool));
+ if (target->has_prop_changes)
+ ; /* ### Here we'll going to call install_patched_prop_target() */
+ }
SVN_ERR(send_patch_notification(target, btn->ctx, iterpool));
}