You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by hw...@apache.org on 2010/09/06 21:13:44 UTC

svn commit: r993127 [3/11] - in /subversion/branches/performance: ./ build/ build/generator/ notes/ notes/tree-conflicts/ subversion/bindings/javahl/native/ subversion/bindings/javahl/src/org/apache/subversion/javahl/ subversion/bindings/javahl/src/org...

Modified: subversion/branches/performance/subversion/libsvn_client/patch.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_client/patch.c?rev=993127&r1=993126&r2=993127&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_client/patch.c (original)
+++ subversion/branches/performance/subversion/libsvn_client/patch.c Mon Sep  6 19:13:39 2010
@@ -65,28 +65,9 @@ typedef struct hunk_info_t {
   int fuzz;
 } hunk_info_t;
 
-typedef struct patch_target_t {
-  /* The patch being applied. */
-  const svn_patch_t *patch;
-
-  /* The target path as it appeared in the patch file,
-   * but in canonicalised form. */
-  const char *canon_path_from_patchfile;
-
-  /* The target path, relative to the working copy directory the
-   * patch is being applied to. A patch strip count applies to this
-   * and only this path. This is never NULL. */
-  const char *local_relpath;
-
-  /* The absolute path of the target on the filesystem.
-   * Any symlinks the path from the patch file may contain are resolved.
-   * Is not always known, so it may be NULL. */
-  char *local_abspath;
-
-  /* The target file, read-only, seekable. This is NULL in case the target
-   * file did not exist prior to patch application. */
-  apr_file_t *file;
-
+/* A struct carrying the information related to the content of a target, be it
+ * a property or the the text of a file. */
+typedef struct target_content_info_t {
   /* A stream to read lines form the target file. This is NULL in case
    * the target file did not exist prior to patch application. */
   svn_stream_t *stream;
@@ -98,19 +79,10 @@ typedef struct patch_target_t {
    * so EOL transformation and keyword contraction is done transparently. */
   svn_stream_t *patched;
 
-  /* The patched stream, without EOL transformation and keyword expansion. */
-  svn_stream_t *patched_raw;
-
-  /* Path to the temporary file underlying the result stream. */
-  const char *patched_path;
-
   /* The reject stream, write-only, not seekable.
    * Hunks that are rejected will be written to this stream. */
   svn_stream_t *reject;
 
-  /* Path to the temporary file underlying the reject stream. */
-  const char *reject_path;
-
   /* The line last read from the target file. */
   svn_linenum_t current_line;
 
@@ -130,6 +102,53 @@ typedef struct patch_target_t {
   /* An array containing hunk_info_t structures for hunks already matched. */
   apr_array_header_t *hunks;
 
+  /* True if end-of-file was reached while reading from the target. */
+  svn_boolean_t eof;
+
+  /* The keywords of the target. */
+  apr_hash_t *keywords;
+
+  /* The pool the target_info is allocated in. */
+  apr_pool_t *pool;
+} target_content_info_t;
+
+typedef struct prop_patch_target_t {
+  const char *name;
+  target_content_info_t *content_info;
+  const char *patched_path;
+
+  /* ### Here we'll add flags telling if the prop was added, deleted,
+   * ### had_rejects, had_local_mods prior to patching and so on. */
+} prop_patch_target_t;
+
+typedef struct patch_target_t {
+  /* The patch being applied. */
+  const svn_patch_t *patch;
+
+  /* The target path as it appeared in the patch file,
+   * but in canonicalised form. */
+  const char *canon_path_from_patchfile;
+
+  /* The target path, relative to the working copy directory the
+   * patch is being applied to. A patch strip count applies to this
+   * and only this path. This is never NULL. */
+  const char *local_relpath;
+
+  /* The absolute path of the target on the filesystem.
+   * Any symlinks the path from the patch file may contain are resolved.
+   * Is not always known, so it may be NULL. */
+  char *local_abspath;
+
+  /* The target file, read-only, seekable. This is NULL in case the target
+   * file did not exist prior to patch application. */
+  apr_file_t *file;
+
+  /* Path to the temporary file underlying the result stream. */
+  const char *patched_path;
+
+  /* Path to the temporary file underlying the reject stream. */
+  const char *reject_path;
+
   /* The node kind of the target as found in WC-DB prior
    * to patch application. */
   svn_node_kind_t db_kind;
@@ -140,9 +159,6 @@ typedef struct patch_target_t {
   /* True if the target was locally deleted prior to patching. */
   svn_boolean_t locally_deleted;
 
-  /* True if end-of-file was reached while reading from the target. */
-  svn_boolean_t eof;
-
   /* True if the target had to be skipped for some reason. */
   svn_boolean_t skipped;
 
@@ -170,8 +186,11 @@ typedef struct patch_target_t {
   /* True if the target has the executable bit set. */
   svn_boolean_t executable;
 
-  /* The keywords of the target. */
-  apr_hash_t *keywords;
+  /* All the information that is specifict to the content of the target. */
+  target_content_info_t *content_info;
+
+  /* A hash table of prop_patch_target_t objects keyed by property names. */
+  apr_hash_t *prop_targets;
 
   /* The pool the target is allocated in. */
   apr_pool_t *pool;
@@ -220,16 +239,17 @@ strip_path(const char **result, const ch
   return SVN_NO_ERROR;
 }
 
-/* Obtain eol and keywords information for LOCAL_ABSPATH, from WC_CTX and
- * store the obtained information in *TARGET.
+/* Obtain KEYWORDS, EOL_STYLE and EOL_STR for LOCAL_ABSPATH, from WC_CTX.
  * Use RESULT_POOL for allocations of fields in TARGET.
  * Use SCRATCH_POOL for all other allocations. */
 static svn_error_t *
-resolve_target_wc_file_info(svn_wc_context_t *wc_ctx,
-                            const char *local_abspath,
-                            patch_target_t *target,
-                            apr_pool_t *result_pool,
-                            apr_pool_t *scratch_pool)
+obtain_eol_and_keywords_for_file(apr_hash_t **keywords,
+                                 svn_subst_eol_style_t *eol_style,
+                                 const char **eol_str,
+                                 svn_wc_context_t *wc_ctx,
+                                 const char *local_abspath,
+                                 apr_pool_t *result_pool,
+                                 apr_pool_t *scratch_pool)
 {
   apr_hash_t *props;
   svn_string_t *keywords_val, *eol_style_val;
@@ -255,9 +275,9 @@ resolve_target_wc_file_info(svn_wc_conte
                                             scratch_pool));
       rev_str = apr_psprintf(scratch_pool, "%ld", changed_rev);
       SVN_ERR(svn_wc__node_get_url(&url, wc_ctx,
-                                   target->local_abspath,
+                                   local_abspath,
                                    scratch_pool, scratch_pool));
-      SVN_ERR(svn_subst_build_keywords2(&target->keywords,
+      SVN_ERR(svn_subst_build_keywords2(keywords,
                                         keywords_val->data,
                                         rev_str, url, changed_date,
                                         author, result_pool));
@@ -267,8 +287,8 @@ resolve_target_wc_file_info(svn_wc_conte
                                APR_HASH_KEY_STRING);
   if (eol_style_val)
     {
-      svn_subst_eol_style_from_value(&target->eol_style,
-                                     &target->eol_str,
+      svn_subst_eol_style_from_value(eol_style,
+                                     eol_str,
                                      eol_style_val->data);
     }
 
@@ -285,8 +305,6 @@ resolve_target_wc_file_info(svn_wc_conte
  * Indicate in TARGET->SKIPPED whether the target should be skipped.
  * STRIP_COUNT specifies the number of leading path components
  * which should be stripped from target paths in the patch.
- * If the path is not skipped also obtain eol-style and keywords
- * information and store this in *TARGET.
  * Use RESULT_POOL for allocations of fields in TARGET.
  * Use SCRATCH_POOL for all other allocations. */
 static svn_error_t *
@@ -351,7 +369,7 @@ resolve_target_path(patch_target_t *targ
 
   /* Skip things we should not be messing with. */
   err = svn_wc_status3(&status, wc_ctx, target->local_abspath,
-                         result_pool, scratch_pool);
+                       result_pool, scratch_pool);
   if (err)
     {
       if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
@@ -400,9 +418,66 @@ resolve_target_path(patch_target_t *targ
       return SVN_NO_ERROR;
     }
 
-  if (target->kind_on_disk == svn_node_file)
-    SVN_ERR(resolve_target_wc_file_info(wc_ctx, target->local_abspath,
-                                        target, result_pool, scratch_pool));
+  return SVN_NO_ERROR;
+}
+
+/* Initialize a PROP_TARGET structure for PROP_NAME. Use working copy
+ * context WC_CTX. Use the REJECT stream that is shared with all content
+ * info structures.
+ * REMOVE_TEMPFILES as in svn_client_patch().
+ */
+static svn_error_t *
+init_prop_target(prop_patch_target_t **prop_target,
+                 const char *prop_name,
+                 svn_stream_t *reject,
+                 svn_boolean_t remove_tempfiles,
+                 svn_wc_context_t *wc_ctx,
+                 const char *local_abspath,
+                 apr_pool_t *result_pool, apr_pool_t *scratch_pool)
+{
+  prop_patch_target_t *new_prop_target;
+  target_content_info_t *content_info; 
+  const svn_string_t *value;
+  const char *patched_path;
+
+
+  content_info = apr_pcalloc(result_pool, sizeof(*content_info));
+
+  /* All other fields in are FALSE or NULL due to apr_pcalloc().*/
+  content_info->current_line = 1;
+  content_info->eol_style = svn_subst_eol_style_none;
+  content_info->lines = apr_array_make(result_pool, 0,
+                                       sizeof(svn_stream_mark_t *));
+  content_info->hunks = apr_array_make(result_pool, 0, sizeof(hunk_info_t *));
+  content_info->keywords = apr_hash_make(result_pool);
+  content_info->reject = reject;
+  content_info->pool = result_pool;
+
+  new_prop_target = apr_palloc(result_pool, sizeof(*new_prop_target));
+  new_prop_target->name = apr_pstrdup(result_pool, prop_name);
+  new_prop_target->content_info = content_info;
+
+  SVN_ERR(svn_wc_prop_get2(&value, wc_ctx, local_abspath, prop_name, 
+                           result_pool, scratch_pool));
+
+  if (value)
+    {
+      /* We assume that a property is small enough to be kept in memory during
+       * the patch process. */
+      content_info->stream = svn_stream_from_string(value, result_pool);
+    }
+
+  /* Create a temporary file to write the patched result to. For properties,
+   * we don't have to worrry about different eol-style. */
+  SVN_ERR(svn_stream_open_unique(&content_info->patched,
+                                 &patched_path, NULL,
+                                 remove_tempfiles ?
+                                   svn_io_file_del_on_pool_cleanup :
+                                   svn_io_file_del_none,
+                                 result_pool, scratch_pool));
+
+  new_prop_target->patched_path = patched_path;
+  *prop_target = new_prop_target;
 
   return SVN_NO_ERROR;
 }
@@ -411,8 +486,10 @@ resolve_target_path(patch_target_t *targ
  * described by PATCH. Use working copy context WC_CTX.
  * STRIP_COUNT specifies the number of leading path components
  * which should be stripped from target paths in the patch.
- * Upon success, allocate the patch target structure in RESULT_POOL.
- * Else, set *target to NULL.
+ * The patch target structure is allocated in RESULT_POOL, but if the target
+ * should be skipped, PATCH_TARGET->SKIPPED is set and the target should be
+ * treated as not fully initialized, e.g. the caller should not not do any
+ * further operations on the target if it is marked to be skipped.
  * REMOVE_TEMPFILES as in svn_client_patch().
  * Use SCRATCH_POOL for all other allocations. */
 static svn_error_t *
@@ -424,18 +501,27 @@ init_patch_target(patch_target_t **patch
                   apr_pool_t *result_pool, apr_pool_t *scratch_pool)
 {
   patch_target_t *target;
+  target_content_info_t *content_info; 
+
+  content_info = apr_pcalloc(result_pool, sizeof(*content_info));
+
+  /* All other fields in content_info are FALSE or NULL due to apr_pcalloc().*/
+  content_info->current_line = 1;
+  content_info->eol_style = svn_subst_eol_style_none;
+  content_info->lines = apr_array_make(result_pool, 0,
+                                       sizeof(svn_stream_mark_t *));
+  content_info->hunks = apr_array_make(result_pool, 0, sizeof(hunk_info_t *));
+  content_info->keywords = apr_hash_make(result_pool);
+  content_info->pool = result_pool;
 
   target = apr_pcalloc(result_pool, sizeof(*target));
 
-  /* All other fields are FALSE or NULL due to apr_pcalloc(). */
+  /* All other fields in target are FALSE or NULL due to apr_pcalloc(). */
   target->patch = patch;
-  target->current_line = 1;
-  target->eol_style = svn_subst_eol_style_none;
-  target->lines = apr_array_make(result_pool, 0,
-                                 sizeof(svn_stream_mark_t *));
-  target->hunks = apr_array_make(result_pool, 0, sizeof(hunk_info_t *));
   target->db_kind = svn_node_none;
   target->kind_on_disk = svn_node_none;
+  target->content_info = content_info;
+  target->prop_targets = apr_hash_make(result_pool);
   target->pool = result_pool;
 
   SVN_ERR(resolve_target_path(target, patch->new_filename,
@@ -446,6 +532,7 @@ init_patch_target(patch_target_t **patch
       const char *diff_header;
       svn_boolean_t repair_eol;
       apr_size_t len;
+      svn_stream_t *patched_raw;
 
       if (target->kind_on_disk == svn_node_file)
         {
@@ -455,8 +542,8 @@ init_patch_target(patch_target_t **patch
                                    APR_OS_DEFAULT, result_pool));
 
           /* Create a stream to read from the target. */
-          target->stream = svn_stream_from_aprfile2(target->file,
-                                                    FALSE, result_pool);
+          content_info->stream = svn_stream_from_aprfile2(target->file,
+                                                          FALSE, result_pool);
 
           /* Also check the file for local mods and the Xbit. */
           SVN_ERR(svn_wc_text_modified_p2(&target->local_mods, wc_ctx,
@@ -465,10 +552,18 @@ init_patch_target(patch_target_t **patch
           SVN_ERR(svn_io_is_file_executable(&target->executable,
                                             target->local_abspath,
                                             scratch_pool));
+
+          SVN_ERR(obtain_eol_and_keywords_for_file(&content_info->keywords,
+                                                   &content_info->eol_style,
+                                                   &content_info->eol_str,
+                                                   wc_ctx,
+                                                   target->local_abspath,
+                                                   result_pool,
+                                                   scratch_pool));
         }
 
       /* Create a temporary file to write the patched result to. */
-      SVN_ERR(svn_stream_open_unique(&target->patched_raw,
+      SVN_ERR(svn_stream_open_unique(&patched_raw,
                                      &target->patched_path, NULL,
                                      remove_tempfiles ?
                                        svn_io_file_del_on_pool_cleanup :
@@ -477,16 +572,16 @@ init_patch_target(patch_target_t **patch
 
       /* Expand keywords in the patched file.
        * Repair newlines if svn:eol-style dictates a particular style. */
-      repair_eol = (target->eol_style == svn_subst_eol_style_fixed ||
-                    target->eol_style == svn_subst_eol_style_native);
-      target->patched = svn_subst_stream_translated(
-                              target->patched_raw, target->eol_str, repair_eol,
-                              target->keywords, TRUE, result_pool);
+      repair_eol = (content_info->eol_style == svn_subst_eol_style_fixed 
+                    || content_info->eol_style == svn_subst_eol_style_native);
+      content_info->patched = svn_subst_stream_translated(
+                              patched_raw, content_info->eol_str, repair_eol,
+                              content_info->keywords, TRUE, result_pool);
 
       /* We'll also need a stream to write rejected hunks to.
        * We don't expand keywords, nor normalise line-endings,
        * in reject files. */
-      SVN_ERR(svn_stream_open_unique(&target->reject,
+      SVN_ERR(svn_stream_open_unique(&content_info->reject,
                                      &target->reject_path, NULL,
                                      remove_tempfiles ?
                                        svn_io_file_del_on_pool_cleanup :
@@ -500,19 +595,45 @@ init_patch_target(patch_target_t **patch
                                  target->canon_path_from_patchfile,
                                  APR_EOL_STR);
       len = strlen(diff_header);
-      SVN_ERR(svn_stream_write(target->reject, diff_header, &len));
+      SVN_ERR(svn_stream_write(content_info->reject, diff_header, &len));
+
+      /* Time for the properties */
+      if (! target->skipped)
+        {
+          apr_hash_index_t *hi;
+
+          for (hi = apr_hash_first(result_pool, patch->prop_patches);
+               hi;
+               hi = apr_hash_next(hi))
+            {
+              const char *prop_name = svn__apr_hash_index_key(hi);
+              prop_patch_target_t *prop_target;
+
+              /* Obtain info about this property */
+              SVN_ERR(init_prop_target(&prop_target,
+                                       prop_name,
+                                       content_info->reject,
+                                       remove_tempfiles,
+                                       wc_ctx, target->local_abspath,
+                                       result_pool, scratch_pool));
+
+              /* Store the info */
+              apr_hash_set(target->prop_targets, prop_name,
+                           APR_HASH_KEY_STRING, prop_target);
+            }
+        }
     }
 
   *patch_target = target;
   return SVN_NO_ERROR;
 }
 
-/* Read a *LINE from TARGET. If the line has not been read before
- * mark the line in TARGET->LINES. Allocate *LINE in RESULT_POOL.
+/* Read a *LINE from CONTENT_INFO. If the line has not been read before
+ * mark the line in CONTENT_INFO->LINES. Allocate *LINE in RESULT_POOL.
  * Do temporary allocations in SCRATCH_POOL.
  */
 static svn_error_t *
-read_line(patch_target_t *target,
+read_line(target_content_info_t *content_info,
           const char **line,
           apr_pool_t *scratch_pool,
           apr_pool_t *result_pool)
@@ -520,43 +641,44 @@ read_line(patch_target_t *target,
   svn_stringbuf_t *line_raw;
   const char *eol_str;
 
-  if (target->eof)
+  if (content_info->eof)
     {
       *line = "";
       return SVN_NO_ERROR;
     }
 
-  SVN_ERR_ASSERT(target->current_line <= target->lines->nelts + 1);
-  if (target->current_line == target->lines->nelts + 1)
+  SVN_ERR_ASSERT(content_info->current_line <= content_info->lines->nelts + 1);
+  if (content_info->current_line == content_info->lines->nelts + 1)
     {
       svn_stream_mark_t *mark;
-      SVN_ERR(svn_stream_mark(target->stream, &mark, target->pool));
-      APR_ARRAY_PUSH(target->lines, svn_stream_mark_t *) = mark;
+      SVN_ERR(svn_stream_mark(content_info->stream, &mark,
+                              content_info->pool));
+      APR_ARRAY_PUSH(content_info->lines, svn_stream_mark_t *) = mark;
     }
 
-  SVN_ERR(svn_stream_readline_detect_eol(target->stream, &line_raw,
-                                         &eol_str, &target->eof,
+  SVN_ERR(svn_stream_readline_detect_eol(content_info->stream, &line_raw,
+                                         &eol_str, &content_info->eof,
                                          scratch_pool));
-  if (target->eol_style == svn_subst_eol_style_none)
-    target->eol_str = eol_str;
+  if (content_info->eol_style == svn_subst_eol_style_none)
+    content_info->eol_str = eol_str;
 
   /* Contract keywords. */
   SVN_ERR(svn_subst_translate_cstring2(line_raw->data, line,
                                        NULL, FALSE,
-                                       target->keywords, FALSE,
+                                       content_info->keywords, FALSE,
                                        result_pool));
-  if (! target->eof)
-    target->current_line++;
+  if (! content_info->eof)
+    content_info->current_line++;
 
   return SVN_NO_ERROR;
 }
 
-/* Seek to the specified LINE in TARGET.
- * Mark any lines not read before in TARGET->LINES.
+/* Seek to the specified LINE in CONTENT_INFO.
+ * Mark any lines not read before in CONTENT_INFO->LINES.
  * Do temporary allocations in SCRATCH_POOL.
  */
 static svn_error_t *
-seek_to_line(patch_target_t *target, svn_linenum_t line,
+seek_to_line(target_content_info_t *content_info, svn_linenum_t line,
              apr_pool_t *scratch_pool)
 {
   svn_linenum_t saved_line;
@@ -564,50 +686,50 @@ seek_to_line(patch_target_t *target, svn
 
   SVN_ERR_ASSERT(line > 0);
 
-  if (line == target->current_line)
+  if (line == content_info->current_line)
     return SVN_NO_ERROR;
 
-  saved_line = target->current_line;
-  saved_eof = target->eof;
+  saved_line = content_info->current_line;
+  saved_eof = content_info->eof;
 
-  if (line <= target->lines->nelts)
+  if (line <= content_info->lines->nelts)
     {
       svn_stream_mark_t *mark;
 
-      mark = APR_ARRAY_IDX(target->lines, line - 1, svn_stream_mark_t *);
-      SVN_ERR(svn_stream_seek(target->stream, mark));
-      target->current_line = line;
+      mark = APR_ARRAY_IDX(content_info->lines, line - 1, svn_stream_mark_t *);
+      SVN_ERR(svn_stream_seek(content_info->stream, mark));
+      content_info->current_line = line;
     }
   else
     {
       const char *dummy;
       apr_pool_t *iterpool = svn_pool_create(scratch_pool);
 
-      while (! target->eof && target->current_line < line)
+      while (! content_info->eof && content_info->current_line < line)
         {
           svn_pool_clear(iterpool);
-          SVN_ERR(read_line(target, &dummy, iterpool, iterpool));
+          SVN_ERR(read_line(content_info, &dummy, iterpool, iterpool));
         }
       svn_pool_destroy(iterpool);
     }
 
   /* After seeking backwards from EOF position clear EOF indicator. */
-  if (saved_eof && saved_line > target->current_line)
-    target->eof = FALSE;
+  if (saved_eof && saved_line > content_info->current_line)
+    content_info->eof = FALSE;
 
   return SVN_NO_ERROR;
 }
 
 /* Indicate in *MATCHED whether the original text of HUNK matches the patch
- * TARGET at its current line. Lines within FUZZ lines of the start or end
- * of HUNK will always match. If IGNORE_WHITESPACE is set, we ignore
+ * CONTENT_INFO at its current line. Lines within FUZZ lines of the start or
+ * end of HUNK will always match. If IGNORE_WHITESPACE is set, we ignore
  * whitespace when doing the matching. When this function returns, neither
- * TARGET->CURRENT_LINE nor the file offset in the target file will have
- * changed. If MATCH_MODIFIED is TRUE, match the modified hunk text,
+ * CONTENT_INFO->CURRENT_LINE nor the file offset in the target file will 
+ * have changed. If MATCH_MODIFIED is TRUE, match the modified hunk text,
  * rather than the original hunk text.
  * Do temporary allocations in POOL. */
 static svn_error_t *
-match_hunk(svn_boolean_t *matched, patch_target_t *target,
+match_hunk(svn_boolean_t *matched, target_content_info_t *content_info,
            const svn_hunk_t *hunk, int fuzz, 
            svn_boolean_t ignore_whitespace,
            svn_boolean_t match_modified, apr_pool_t *pool)
@@ -625,10 +747,10 @@ match_hunk(svn_boolean_t *matched, patch
 
   *matched = FALSE;
 
-  if (target->eof)
+  if (content_info->eof)
     return SVN_NO_ERROR;
 
-  saved_line = target->current_line;
+  saved_line = content_info->current_line;
   lines_read = 0;
   lines_matched = FALSE;
   original_length = svn_diff_hunk_get_original_length(hunk);
@@ -658,10 +780,10 @@ match_hunk(svn_boolean_t *matched, patch
       SVN_ERR(svn_subst_translate_cstring2(hunk_line->data,
                                            &hunk_line_translated,
                                            NULL, FALSE,
-                                           target->keywords, FALSE,
+                                           content_info->keywords, FALSE,
                                            iterpool));
       lines_read++;
-      SVN_ERR(read_line(target, &target_line, iterpool, iterpool));
+      SVN_ERR(read_line(content_info, &target_line, iterpool, iterpool));
       if (! hunk_eof)
         {
           if (lines_read <= fuzz && leading_context > fuzz)
@@ -688,11 +810,11 @@ match_hunk(svn_boolean_t *matched, patch
             }
         }
     }
-  while (lines_matched && ! (hunk_eof || target->eof));
+  while (lines_matched && ! (hunk_eof || content_info->eof));
 
   if (hunk_eof)
     *matched = lines_matched;
-  else if (target->eof)
+  else if (content_info->eof)
     {
       /* If the target has no newline at end-of-file, we get an EOF
        * indication for the target earlier than we do get it for the hunk. */
@@ -704,19 +826,23 @@ match_hunk(svn_boolean_t *matched, patch
         SVN_ERR(svn_diff_hunk_readline_original_text(hunk, &hunk_line,
                                                      NULL, &hunk_eof,
                                                      iterpool, iterpool));
-      if (hunk_line->len == 0 && hunk_eof)
+
+      /* When comparing modified text we require that all lines match, else
+       * a hunk that adds a newline at the end will be treated as already
+       * applied even if it isn't. */
+      if (! match_modified && hunk_line->len == 0 && hunk_eof)
         *matched = lines_matched;
       else
         *matched = FALSE;
     }
-  SVN_ERR(seek_to_line(target, saved_line, iterpool));
+  SVN_ERR(seek_to_line(content_info, saved_line, iterpool));
 
   svn_pool_destroy(iterpool);
 
   return SVN_NO_ERROR;
 }
 
-/* Scan lines of TARGET for a match of the original text of HUNK,
+/* Scan lines of CONTENT_INFO for a match of the original text of HUNK,
  * up to but not including the specified UPPER_LINE. Use fuzz factor FUZZ.
  * If UPPER_LINE is zero scan until EOF occurs when reading from TARGET.
  * Return the line at which HUNK was matched in *MATCHED_LINE.
@@ -731,7 +857,8 @@ match_hunk(svn_boolean_t *matched, patch
  * Call cancel CANCEL_FUNC with baton CANCEL_BATON to trigger cancellation.
  * Do all allocations in POOL. */
 static svn_error_t *
-scan_for_match(svn_linenum_t *matched_line, patch_target_t *target,
+scan_for_match(svn_linenum_t *matched_line, 
+               target_content_info_t *content_info,
                const svn_hunk_t *hunk, svn_boolean_t match_first,
                svn_linenum_t upper_line, int fuzz, 
                svn_boolean_t ignore_whitespace,
@@ -743,8 +870,8 @@ scan_for_match(svn_linenum_t *matched_li
 
   *matched_line = 0;
   iterpool = svn_pool_create(pool);
-  while ((target->current_line < upper_line || upper_line == 0) &&
-         ! target->eof)
+  while ((content_info->current_line < upper_line || upper_line == 0) &&
+         ! content_info->eof)
     {
       svn_boolean_t matched;
       int i;
@@ -754,19 +881,19 @@ scan_for_match(svn_linenum_t *matched_li
       if (cancel_func)
         SVN_ERR((cancel_func)(cancel_baton));
 
-      SVN_ERR(match_hunk(&matched, target, hunk, fuzz, ignore_whitespace,
+      SVN_ERR(match_hunk(&matched, content_info, hunk, fuzz, ignore_whitespace,
                          match_modified, iterpool));
       if (matched)
         {
           svn_boolean_t taken = FALSE;
 
           /* Don't allow hunks to match at overlapping locations. */
-          for (i = 0; i < target->hunks->nelts; i++)
+          for (i = 0; i < content_info->hunks->nelts; i++)
             {
               const hunk_info_t *hi;
               svn_linenum_t length;
 
-              hi = APR_ARRAY_IDX(target->hunks, i, const hunk_info_t *);
+              hi = APR_ARRAY_IDX(content_info->hunks, i, const hunk_info_t *);
 
               if (match_modified)
                 length = svn_diff_hunk_get_modified_length(hi->hunk);
@@ -774,49 +901,45 @@ scan_for_match(svn_linenum_t *matched_li
                 length = svn_diff_hunk_get_original_length(hi->hunk);
 
               taken = (! hi->rejected &&
-                       target->current_line >= hi->matched_line &&
-                       target->current_line < (hi->matched_line + length));
+                       content_info->current_line >= hi->matched_line &&
+                       content_info->current_line < (hi->matched_line + length));
               if (taken)
                 break;
             }
 
           if (! taken)
             {
-              *matched_line = target->current_line;
+              *matched_line = content_info->current_line;
               if (match_first)
                 break;
             }
         }
 
-      if (! target->eof)
-        SVN_ERR(seek_to_line(target, target->current_line + 1, iterpool));
+      if (! content_info->eof)
+        SVN_ERR(seek_to_line(content_info, content_info->current_line + 1,
+                             iterpool));
     }
   svn_pool_destroy(iterpool);
 
   return SVN_NO_ERROR;
 }
 
-/* Indicate in *MATCH whether the file at TARGET->abspath matches the
- * modified text of HUNK. Use SCRATCH_POOL for temporary allocations. */
+/* Indicate in *MATCH whether the STREAM matches the modified text of HUNK.
+ * Use CONTENT_INFO for retrieving information on eol and keywords that is
+ * needed for the comparison.
+ * Use SCRATCH_POOL for temporary allocations. */
 static svn_error_t *
-match_existing_file(svn_boolean_t *match,
-                    patch_target_t *target,
-                    const svn_hunk_t *hunk,
-                    apr_pool_t *scratch_pool)
+match_existing_target(svn_boolean_t *match,
+                      target_content_info_t *content_info,
+                      const svn_hunk_t *hunk,
+                      svn_stream_t *stream,
+                      apr_pool_t *scratch_pool)
 {
-  apr_file_t *file;
-  svn_stream_t *stream;
   svn_boolean_t lines_matched;
   apr_pool_t *iterpool;
   svn_boolean_t eof;
   svn_boolean_t hunk_eof;
 
-  SVN_ERR(svn_io_file_open(&file, target->local_abspath,
-                           APR_READ | APR_BINARY, APR_OS_DEFAULT,
-                           scratch_pool));
-  stream = svn_stream_from_aprfile2(file, FALSE, scratch_pool);
-
-
   SVN_ERR(svn_diff_hunk_reset_modified_text(hunk));
 
   iterpool = svn_pool_create(scratch_pool);
@@ -836,18 +959,19 @@ match_existing_file(svn_boolean_t *match
                                                    iterpool, iterpool));
       /* Contract keywords. */
       SVN_ERR(svn_subst_translate_cstring2(line->data, &line_translated,
-                                           NULL, FALSE, target->keywords,
+                                           NULL, FALSE, 
+                                           content_info->keywords,
                                            FALSE, iterpool));
       SVN_ERR(svn_subst_translate_cstring2(hunk_line->data,
                                            &hunk_line_translated,
-                                           NULL, FALSE, target->keywords,
+                                           NULL, FALSE,
+                                           content_info->keywords,
                                            FALSE, iterpool));
       lines_matched = ! strcmp(line_translated, hunk_line_translated);
       if (eof != hunk_eof)
         {
           svn_pool_destroy(iterpool);
           *match = FALSE;
-          svn_stream_close(stream);
           return SVN_NO_ERROR;
         }
       }
@@ -855,23 +979,24 @@ match_existing_file(svn_boolean_t *match
     svn_pool_destroy(iterpool);
 
     *match = (lines_matched && eof == hunk_eof);
-    svn_stream_close(stream);
     return SVN_NO_ERROR;
 }
 
-/* Determine the line at which a HUNK applies to the TARGET file,
- * and return an appropriate hunk_info object in *HI, allocated from
+/* Determine the line at which a HUNK applies to the CONTENT_INFO of TARGET
+ * file, and return an appropriate hunk_info object in *HI, allocated from
  * RESULT_POOL. Use fuzz factor FUZZ. Set HI->FUZZ to FUZZ. If no correct
  * line can be determined, set HI->REJECTED to TRUE.
  * IGNORE_WHITESPACE tells whether whitespace should be considered when
- * matching. When this function returns, neither TARGET->CURRENT_LINE nor
+ * matching. When this function returns, neither CONTENT_INFO->CURRENT_LINE nor
  * the file offset in the target file will have changed.
  * Call cancel CANCEL_FUNC with baton CANCEL_BATON to trigger cancellation.
  * Do temporary allocations in POOL. */
 static svn_error_t *
 get_hunk_info(hunk_info_t **hi, patch_target_t *target,
+              target_content_info_t *content_info,
               const svn_hunk_t *hunk, int fuzz, 
               svn_boolean_t ignore_whitespace,
+              svn_boolean_t is_prop_hunk,
               svn_cancel_func_t cancel_func, void *cancel_baton,
               apr_pool_t *result_pool, apr_pool_t *scratch_pool)
 {
@@ -886,17 +1011,27 @@ get_hunk_info(hunk_info_t **hi, patch_ta
    * a new file. Don't bother matching hunks in that case, since
    * the hunk applies at line 1. If the file already exists, the hunk
    * is rejected, unless the file is versioned and its content matches
-   * the file the patch wants to create. */
-  if (original_start == 0)
+   * the file the patch wants to create.  */
+  if (original_start == 0 && ! is_prop_hunk)
     {
       if (target->kind_on_disk == svn_node_file)
         {
           if (target->db_kind == svn_node_file)
             {
               svn_boolean_t file_matches;
+              apr_file_t *file;
+              svn_stream_t *stream;
+
+              /* ### dannas: Why not use content_info-stream here? */
+              SVN_ERR(svn_io_file_open(&file, target->local_abspath,
+                                       APR_READ | APR_BINARY, APR_OS_DEFAULT,
+                                       scratch_pool));
+              stream = svn_stream_from_aprfile2(file, FALSE, scratch_pool);
+
+              SVN_ERR(match_existing_target(&file_matches, content_info, hunk,
+                                            stream, scratch_pool));
+              svn_stream_close(stream);
 
-              SVN_ERR(match_existing_file(&file_matches, target, hunk,
-                                          scratch_pool));
               if (file_matches)
                 {
                   matched_line = 1;
@@ -911,9 +1046,38 @@ get_hunk_info(hunk_info_t **hi, patch_ta
       else
         matched_line = 1;
     }
-  else if (original_start > 0 && target->kind_on_disk == svn_node_file)
+  /* Same conditions apply as for the file case above. 
+   *
+   * ### Since the hunk says the prop should be added we just assume so for
+   * ### now and don't bother with storing the previous lines and such. When
+   * ### we have the diff operation available we can just check for adds. */
+  else if (original_start == 0 && is_prop_hunk)
     {
-      svn_linenum_t saved_line = target->current_line;
+      if (content_info->stream)
+        {
+          svn_boolean_t prop_matches;
+
+          SVN_ERR(match_existing_target(&prop_matches, content_info, hunk,
+                                        content_info->stream, scratch_pool));
+
+          if (prop_matches)
+            {
+              matched_line = 1;
+              already_applied = TRUE;
+            }
+          else
+            matched_line = 0; /* reject */
+        }
+      else
+        matched_line = 1;
+    }
+  /* ### We previously checked for target->kind_on_disk == svn_node_file
+   * ### here. But that wasn't generic enough to cope with properties. How
+   * ### better describe that content_info->stream is only set if we have an
+   * ### existing target? */
+  else if (original_start > 0 && content_info->stream)
+    {
+      svn_linenum_t saved_line = content_info->current_line;
       svn_linenum_t modified_start;
 
       /* Check if the hunk is already applied.
@@ -930,8 +1094,10 @@ get_hunk_info(hunk_info_t **hi, patch_ta
             }
           else
             {
-              SVN_ERR(seek_to_line(target, modified_start, scratch_pool));
-              SVN_ERR(scan_for_match(&matched_line, target, hunk, TRUE,
+              SVN_ERR(seek_to_line(content_info, modified_start,
+                                   scratch_pool));
+              SVN_ERR(scan_for_match(&matched_line, content_info,
+                                     hunk, TRUE,
                                      modified_start + 1,
                                      fuzz, ignore_whitespace, TRUE,
                                      cancel_func, cancel_baton,
@@ -946,14 +1112,14 @@ get_hunk_info(hunk_info_t **hi, patch_ta
         {
           /* Scan for a match at the line where the hunk thinks it
            * should be going. */
-          SVN_ERR(seek_to_line(target, original_start, scratch_pool));
-          if (target->current_line != original_start)
+          SVN_ERR(seek_to_line(content_info, original_start, scratch_pool));
+          if (content_info->current_line != original_start)
             {
               /* Seek failed. */
               matched_line = 0;
             }
           else
-            SVN_ERR(scan_for_match(&matched_line, target, hunk, TRUE,
+            SVN_ERR(scan_for_match(&matched_line, content_info, hunk, TRUE,
                                    original_start + 1, fuzz,
                                    ignore_whitespace, FALSE,
                                    cancel_func, cancel_baton,
@@ -962,11 +1128,11 @@ get_hunk_info(hunk_info_t **hi, patch_ta
           if (matched_line != original_start)
             {
               /* Scan the whole file again from the start. */
-              SVN_ERR(seek_to_line(target, 1, scratch_pool));
+              SVN_ERR(seek_to_line(content_info, 1, scratch_pool));
 
               /* Scan forward towards the hunk's line and look for a line
                * where the hunk matches. */
-              SVN_ERR(scan_for_match(&matched_line, target, hunk, FALSE,
+              SVN_ERR(scan_for_match(&matched_line, content_info, hunk, FALSE,
                                      original_start, fuzz,
                                      ignore_whitespace, FALSE,
                                      cancel_func, cancel_baton,
@@ -978,15 +1144,15 @@ get_hunk_info(hunk_info_t **hi, patch_ta
                 {
                   /* Scan forward towards the end of the file and look
                    * for a line where the hunk matches. */
-                  SVN_ERR(scan_for_match(&matched_line, target, hunk, TRUE, 0,
-                                         fuzz, ignore_whitespace, FALSE,
-                                         cancel_func, cancel_baton,
+                  SVN_ERR(scan_for_match(&matched_line, content_info, hunk,
+                                         TRUE, 0, fuzz, ignore_whitespace,
+                                         FALSE, cancel_func, cancel_baton,
                                          scratch_pool));
                 }
             }
         }
 
-      SVN_ERR(seek_to_line(target, saved_line, scratch_pool));
+      SVN_ERR(seek_to_line(content_info, saved_line, scratch_pool));
     }
   else
     {
@@ -1028,23 +1194,25 @@ try_stream_write(svn_stream_t *stream, c
  * If LINE is zero, copy lines until end-of-file has been reached.
  * Do all allocations in POOL. */
 static svn_error_t *
-copy_lines_to_target(patch_target_t *target, svn_linenum_t line,
-                     apr_pool_t *pool)
+copy_lines_to_target(target_content_info_t *content_info, svn_linenum_t line,
+                     const char *patched_path, apr_pool_t *pool)
 {
   apr_pool_t *iterpool;
 
   iterpool = svn_pool_create(pool);
-  while ((target->current_line < line || line == 0) && ! target->eof)
+  while ((content_info->current_line < line || line == 0) 
+         && ! content_info->eof)
     {
       const char *target_line;
 
       svn_pool_clear(iterpool);
 
-      SVN_ERR(read_line(target, &target_line, iterpool, iterpool));
-      if (! target->eof)
-        target_line = apr_pstrcat(iterpool, target_line, target->eol_str, NULL);
+      SVN_ERR(read_line(content_info, &target_line, iterpool, iterpool));
+      if (! content_info->eof)
+        target_line = apr_pstrcat(iterpool, target_line, content_info->eol_str,
+                                  NULL);
 
-      SVN_ERR(try_stream_write(target->patched, target->patched_path,
+      SVN_ERR(try_stream_write(content_info->patched, patched_path,
                                target_line, strlen(target_line), iterpool));
     }
   svn_pool_destroy(iterpool);
@@ -1053,10 +1221,11 @@ copy_lines_to_target(patch_target_t *tar
 }
 
 /* Write the diff text of the hunk described by HI to the
- * reject stream of TARGET, and mark TARGET as having had rejects.
+ * reject stream of CONTENT_INFO, and mark TARGET as having had rejects.
  * Do temporary allocations in POOL. */
 static svn_error_t *
-reject_hunk(patch_target_t *target, hunk_info_t *hi, apr_pool_t *pool)
+reject_hunk(patch_target_t *target, target_content_info_t *content_info, 
+            hunk_info_t *hi, svn_boolean_t is_prop_hunk,  apr_pool_t *pool)
 {
   const char *hunk_header;
   apr_size_t len;
@@ -1070,7 +1239,7 @@ reject_hunk(patch_target_t *target, hunk
                              svn_diff_hunk_get_modified_length(hi->hunk),
                              APR_EOL_STR);
   len = strlen(hunk_header);
-  SVN_ERR(svn_stream_write(target->reject, hunk_header, &len));
+  SVN_ERR(svn_stream_write(content_info->reject, hunk_header, &len));
 
   iterpool = svn_pool_create(pool);
   do
@@ -1085,32 +1254,38 @@ reject_hunk(patch_target_t *target, hunk
       if (! eof)
         {
           if (hunk_line->len >= 1)
-            SVN_ERR(try_stream_write(target->reject, target->reject_path,
+            SVN_ERR(try_stream_write(content_info->reject, target->reject_path,
                                      hunk_line->data, hunk_line->len,
                                      iterpool));
           if (eol_str)
-            SVN_ERR(try_stream_write(target->reject, target->reject_path,
+            SVN_ERR(try_stream_write(content_info->reject, target->reject_path,
                                      eol_str, strlen(eol_str), iterpool));
         }
     }
   while (! eof);
   svn_pool_destroy(iterpool);
 
-  target->had_rejects = TRUE;
+  /* ### had_rejects is used for notification. We haven't yet turned on
+   * ### notification for properties. */
+  if (! is_prop_hunk)
+    target->had_rejects = TRUE;
 
   return SVN_NO_ERROR;
 }
 
 /* Write the modified text of hunk described by HI to the patched
- * stream of TARGET. Do temporary allocations in POOL. */
+ * stream of CONTENT_INFO. Do temporary allocations in POOL. */
 static svn_error_t *
-apply_hunk(patch_target_t *target, hunk_info_t *hi, apr_pool_t *pool)
+apply_hunk(patch_target_t *target, target_content_info_t *content_info,  
+           hunk_info_t *hi, svn_boolean_t is_prop_hunk, apr_pool_t *pool)
 {
   svn_linenum_t lines_read;
   svn_boolean_t eof;
   apr_pool_t *iterpool;
 
-  if (target->kind_on_disk == svn_node_file)
+  /* ### Is there a cleaner way to describe if we have an existing target?
+   */
+  if (target->kind_on_disk == svn_node_file || is_prop_hunk)
     {
       svn_linenum_t line;
 
@@ -1118,19 +1293,19 @@ apply_hunk(patch_target_t *target, hunk_
        * Also copy leading lines of context which matched with fuzz.
        * The target has changed on the fuzzy-matched lines,
        * so we should retain the target's version of those lines. */
-      SVN_ERR(copy_lines_to_target(target, hi->matched_line + hi->fuzz,
-                                   pool));
+      SVN_ERR(copy_lines_to_target(content_info, hi->matched_line + hi->fuzz,
+                                   target->patched_path, pool));
 
       /* Skip the target's version of the hunk.
        * Don't skip trailing lines which matched with fuzz. */
-      line = target->current_line +
+      line = content_info->current_line +
              svn_diff_hunk_get_original_length(hi->hunk) - (2 * hi->fuzz);
-      SVN_ERR(seek_to_line(target, line, pool));
-      if (target->current_line != line && ! target->eof)
+      SVN_ERR(seek_to_line(content_info, line, pool));
+      if (content_info->current_line != line && ! content_info->eof)
         {
           /* Seek failed, reject this hunk. */
           hi->rejected = TRUE;
-          SVN_ERR(reject_hunk(target, hi, pool));
+          SVN_ERR(reject_hunk(target, content_info, hi, is_prop_hunk, pool));
           return SVN_NO_ERROR;
         }
     }
@@ -1155,18 +1330,20 @@ apply_hunk(patch_target_t *target, hunk_
           lines_read <= svn_diff_hunk_get_modified_length(hi->hunk) - hi->fuzz)
         {
           if (hunk_line->len >= 1)
-            SVN_ERR(try_stream_write(target->patched, target->patched_path,
+            SVN_ERR(try_stream_write(content_info->patched, 
+                                     target->patched_path,
                                      hunk_line->data, hunk_line->len,
                                      iterpool));
           if (eol_str)
             {
               /* Use the EOL as it was read from the patch file,
                * unless the target's EOL style is set by svn:eol-style */
-              if (target->eol_style != svn_subst_eol_style_none)
-                eol_str = target->eol_str;
+              if (content_info->eol_style != svn_subst_eol_style_none)
+                eol_str = content_info->eol_str;
 
-              SVN_ERR(try_stream_write(target->patched, target->patched_path,
-                                       eol_str, strlen(eol_str), iterpool));
+              SVN_ERR(try_stream_write(content_info->patched,
+                                       target->patched_path, eol_str,
+                                       strlen(eol_str), iterpool));
             }
         }
     }
@@ -1231,13 +1408,13 @@ send_patch_notification(const patch_targ
       apr_pool_t *iterpool;
 
       iterpool = svn_pool_create(pool);
-      for (i = 0; i < target->hunks->nelts; i++)
+      for (i = 0; i < target->content_info->hunks->nelts; i++)
         {
           hunk_info_t *hi;
 
           svn_pool_clear(iterpool);
 
-          hi = APR_ARRAY_IDX(target->hunks, i, hunk_info_t *);
+          hi = APR_ARRAY_IDX(target->content_info->hunks, i, hunk_info_t *);
 
           if (hi->already_applied)
             action = svn_wc_notify_patch_hunk_already_applied;
@@ -1296,6 +1473,7 @@ apply_one_patch(patch_target_t **patch_t
   apr_pool_t *iterpool;
   int i;
   static const int MAX_FUZZ = 2;
+  apr_hash_index_t *hash_index;
 
   SVN_ERR(init_patch_target(&target, patch, abs_wc_path, wc_ctx, strip_count,
                             remove_tempfiles, result_pool, scratch_pool));
@@ -1337,39 +1515,43 @@ apply_one_patch(patch_target_t **patch_t
        * If no match is found initially, try with fuzz. */
       do
         {
-          SVN_ERR(get_hunk_info(&hi, target, hunk, fuzz,
+          SVN_ERR(get_hunk_info(&hi, target, target->content_info, hunk, fuzz,
                                 ignore_whitespace,
+                                FALSE /* is_prop_hunk */,
                                 cancel_func, cancel_baton,
                                 result_pool, iterpool));
           fuzz++;
         }
       while (hi->rejected && fuzz <= MAX_FUZZ && ! hi->already_applied);
 
-      APR_ARRAY_PUSH(target->hunks, hunk_info_t *) = hi;
+      APR_ARRAY_PUSH(target->content_info->hunks, hunk_info_t *) = hi;
     }
 
   /* Apply or reject hunks. */
-  for (i = 0; i < target->hunks->nelts; i++)
+  for (i = 0; i < target->content_info->hunks->nelts; i++)
     {
       hunk_info_t *hi;
 
       svn_pool_clear(iterpool);
 
-      hi = APR_ARRAY_IDX(target->hunks, i, hunk_info_t *);
+      hi = APR_ARRAY_IDX(target->content_info->hunks, i, hunk_info_t *);
       if (hi->already_applied)
         continue;
       else if (hi->rejected)
-        SVN_ERR(reject_hunk(target, hi, iterpool));
+        SVN_ERR(reject_hunk(target, target->content_info, hi,
+                            FALSE /* is_prop_hunk */, 
+                            iterpool));
       else
-        SVN_ERR(apply_hunk(target, hi, iterpool));
+        SVN_ERR(apply_hunk(target, target->content_info, hi,
+                           FALSE /* is_prop_hunk */, iterpool));
     }
-  svn_pool_destroy(iterpool);
 
   if (target->kind_on_disk == svn_node_file)
     {
       /* Copy any remaining lines to target. */
-      SVN_ERR(copy_lines_to_target(target, 0, scratch_pool));
-      if (! target->eof)
+      SVN_ERR(copy_lines_to_target(target->content_info, 0,
+                                   target->patched_path, scratch_pool));
+      if (! target->content_info->eof)
         {
           /* We could not copy the entire target file to the temporary file,
            * and would truncate the target if we copied the temporary file
@@ -1378,12 +1560,142 @@ apply_one_patch(patch_target_t **patch_t
         }
     }
 
-  /* Close the streams of the target so that their content is flushed
-   * to disk. This will also close underlying streams and files. */
-  if (target->kind_on_disk == svn_node_file)
-    SVN_ERR(svn_stream_close(target->stream));
-  SVN_ERR(svn_stream_close(target->patched));
-  SVN_ERR(svn_stream_close(target->reject));
+  /* Match property hunks. */
+  for (hash_index = apr_hash_first(result_pool, patch->prop_patches);
+       hash_index;
+       hash_index = apr_hash_next(hash_index))
+    {
+      svn_prop_patch_t *prop_patch;
+      const char *prop_name;
+      prop_patch_target_t *prop_target;
+      target_content_info_t *prop_content_info;
+        
+      /* Fetching the parsed info for one property */
+      prop_name = svn__apr_hash_index_key(hash_index);
+      prop_patch = svn__apr_hash_index_val(hash_index);
+
+      /* Fetch the prop_content_info we'll use to store the matched hunks
+       * in. */
+      prop_target = apr_hash_get(target->prop_targets, prop_name, 
+                                 APR_HASH_KEY_STRING);
+      prop_content_info = prop_target->content_info;
+
+      for (i = 0; i < prop_patch->hunks->nelts; i++)
+        {
+          svn_hunk_t *hunk;
+          hunk_info_t *hi;
+          int fuzz = 0;
+
+          svn_pool_clear(iterpool);
+
+          if (cancel_func)
+            SVN_ERR((cancel_func)(cancel_baton));
+
+          hunk = APR_ARRAY_IDX(prop_patch->hunks, i, svn_hunk_t *);
+
+          /* Determine the line the hunk should be applied at.
+           * If no match is found initially, try with fuzz. */
+          do
+            {
+              SVN_ERR(get_hunk_info(&hi, target, prop_content_info, hunk, fuzz,
+                                    ignore_whitespace,
+                                    TRUE /* is_prop_hunk */,
+                                    cancel_func, cancel_baton,
+                                    result_pool, iterpool));
+              fuzz++;
+            }
+          while (hi->rejected && fuzz <= MAX_FUZZ && ! hi->already_applied);
+
+          APR_ARRAY_PUSH(prop_content_info->hunks, hunk_info_t *) = hi;
+        }
+    }
+
+  /* Apply or reject property hunks. */
+  for (hash_index = apr_hash_first(result_pool, target->prop_targets);
+       hash_index;
+       hash_index = apr_hash_next(hash_index))
+    {
+      prop_patch_target_t *prop_target;
+      target_content_info_t *prop_content_info;
+      const char *prop_patched_path;
+
+      prop_target = svn__apr_hash_index_val(hash_index);
+      prop_content_info = prop_target->content_info;
+      prop_patched_path = prop_target->patched_path;
+
+      for (i = 0; i < prop_content_info->hunks->nelts; i++)
+        {
+          hunk_info_t *hi;
+
+          svn_pool_clear(iterpool);
+
+          hi = APR_ARRAY_IDX(prop_content_info->hunks, i, hunk_info_t *);
+          if (hi->already_applied)
+            continue;
+          else if (hi->rejected)
+            SVN_ERR(reject_hunk(target, prop_content_info, hi,
+                                TRUE /* is_prop_hunk */, 
+                                iterpool));
+          else
+            SVN_ERR(apply_hunk(target, prop_content_info, hi, 
+                               TRUE /* is_prop_hunk */,
+                               iterpool));
+        }
+
+        if (prop_content_info->stream)
+          {
+            /* Copy any remaining lines to target. */
+            SVN_ERR(copy_lines_to_target(prop_content_info, 0,
+                                         prop_patched_path, scratch_pool));
+            if (! prop_content_info->eof)
+              {
+                /* We could not copy the entire target property to the
+                 * temporary file, and would truncate the target if we
+                 * copied the temporary file on top of it. Skip this target. 
+                 *
+                 * ### dannas: Do we really want to skip an entire target
+                 * ### if one of the properties does not apply cleanly,
+                 * ### e.g. both text changes and all prop changes will not be
+                 * ### installed. */
+                target->skipped = TRUE;
+              }
+          }
+      }
+
+  svn_pool_destroy(iterpool);
+
+    {
+      apr_hash_index_t *hi;
+      target_content_info_t *prop_content_info;
+
+      /* Close the streams of the target so that their content is flushed
+       * to disk. This will also close underlying streams and files. 
+       * First the streams belonging to properties .. */
+          for (hi = apr_hash_first(result_pool, target->prop_targets);
+               hi;
+               hi = apr_hash_next(hi))
+            {
+              prop_patch_target_t *prop_target;
+              prop_target = svn__apr_hash_index_val(hi);
+              prop_content_info = prop_target->content_info;
+
+              /* ### If the prop did not exist pre-patching we'll not have a
+               * ### stream to read from. Find a better way to store info on
+               * ### the existence of the target prop. */
+              if (prop_content_info->stream)
+                svn_stream_close(prop_content_info->stream);
+
+              svn_stream_close(prop_content_info->patched);
+            }
+
+
+       /* .. And then streams associted with the file. The reject stream is
+        * shared between all target_content_info structures. */
+      if (target->kind_on_disk == svn_node_file)
+        SVN_ERR(svn_stream_close(target->content_info->stream));
+      SVN_ERR(svn_stream_close(target->content_info->patched));
+      SVN_ERR(svn_stream_close(target->content_info->reject));
+    }
 
   if (! target->skipped)
     {
@@ -2068,7 +2380,8 @@ svn_client_patch(const char *patch_abspa
   baton.patch_func = patch_func;
   baton.patch_baton = patch_baton;
 
-  return svn_error_return(svn_wc__call_with_write_lock(apply_patches, &baton,
-                                       ctx->wc_ctx, local_abspath,
-                                       result_pool, scratch_pool));
+  return svn_error_return(
+           svn_wc__call_with_write_lock(apply_patches, &baton,
+                                        ctx->wc_ctx, local_abspath, FALSE,
+                                        result_pool, scratch_pool));
 }

Modified: subversion/branches/performance/subversion/libsvn_client/prop_commands.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_client/prop_commands.c?rev=993127&r1=993126&r2=993127&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_client/prop_commands.c (original)
+++ subversion/branches/performance/subversion/libsvn_client/prop_commands.c Mon Sep  6 19:13:39 2010
@@ -97,7 +97,7 @@ struct propset_walk_baton
   void *notify_baton;
 };
 
-/* An node-walk callback for svn_client_propset3.
+/* An node-walk callback for svn_client_propset4.
  *
  * For LOCAL_ABSPATH, set the property named wb->PROPNAME to the value
  * wb->PROPVAL, where "wb" is the WALK_BATON of type "struct
@@ -189,8 +189,7 @@ do_url_propset(const char *propname,
 }
 
 static svn_error_t *
-propset_on_url(svn_commit_info_t **commit_info_p,
-               const char *propname,
+propset_on_url(const char *propname,
                const svn_string_t *propval,
                const char *target,
                svn_boolean_t skip_checks,
@@ -204,7 +203,7 @@ propset_on_url(svn_commit_info_t **commi
   svn_node_kind_t node_kind;
   const char *message;
   const svn_delta_editor_t *editor;
-  void *commit_baton, *edit_baton;
+  void *edit_baton;
   apr_hash_t *commit_revprops;
   svn_error_t *err;
 
@@ -268,11 +267,10 @@ propset_on_url(svn_commit_info_t **commi
                                            message, ctx, pool));
 
   /* Fetch RA commit editor. */
-  SVN_ERR(svn_client__commit_get_baton(&commit_baton, commit_info_p, pool));
   SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
                                     commit_revprops,
-                                    svn_client__commit_callback,
-                                    commit_baton,
+                                    ctx->commit_callback2,
+                                    ctx->commit_baton,
                                     NULL, TRUE, /* No lock tokens */
                                     pool));
 
@@ -290,10 +288,56 @@ propset_on_url(svn_commit_info_t **commi
   return editor->close_edit(edit_baton, pool);
 }
 
+/* Baton for set_props_cb */
+struct set_props_baton
+{
+  svn_client_ctx_t *ctx;
+  const char *local_abspath;
+  svn_depth_t depth;
+  svn_node_kind_t kind;
+  const char *propname;
+  const svn_string_t *propval;
+  svn_boolean_t skip_checks;
+  apr_hash_t *changelist_hash;
+};
+
+/* Working copy lock callback for svn_client_propset4 */
+static svn_error_t *
+set_props_cb(void *baton,
+             apr_pool_t *result_pool,
+             apr_pool_t *scratch_pool)
+{
+  struct set_props_baton *bt = baton;
+
+  if (bt->depth >= svn_depth_files && bt->kind == svn_node_dir)
+    {
+      struct propset_walk_baton wb;
+
+      wb.wc_ctx = bt->ctx->wc_ctx;
+      wb.propname = bt->propname;
+      wb.propval = bt->propval;
+      wb.force = bt->skip_checks;
+      wb.changelist_hash = bt->changelist_hash;
+      wb.notify_func = bt->ctx->notify_func2;
+      wb.notify_baton = bt->ctx->notify_baton2;
+      SVN_ERR(svn_wc__node_walk_children(bt->ctx->wc_ctx, bt->local_abspath,
+                                         FALSE, propset_walk_cb, &wb,
+                                         bt->depth, bt->ctx->cancel_func,
+                                         bt->ctx->cancel_baton, scratch_pool));
+    }
+  else if (svn_wc__changelist_match(bt->ctx->wc_ctx, bt->local_abspath,
+                                    bt->changelist_hash, scratch_pool))
+    {
+      SVN_ERR(svn_wc_prop_set4(bt->ctx->wc_ctx, bt->local_abspath,
+                               bt->propname, bt->propval, bt->skip_checks,
+                               bt->ctx->notify_func2, bt->ctx->notify_baton2,
+                               scratch_pool));
+    }
+  return SVN_NO_ERROR;
+}
 
 svn_error_t *
-svn_client_propset3(svn_commit_info_t **commit_info_p,
-                    const char *propname,
+svn_client_propset4(const char *propname,
                     const svn_string_t *propval,
                     const char *target,
                     svn_depth_t depth,
@@ -351,9 +395,8 @@ svn_client_propset3(svn_commit_info_t **
                                  _("Setting property '%s' on non-local target "
                                    "'%s' is not supported"), propname, target);
 
-      return propset_on_url(commit_info_p, propname, propval, target,
-                            skip_checks, base_revision_for_url, revprop_table,
-                            ctx, pool);
+      return propset_on_url(propname, propval, target, skip_checks,
+                            base_revision_for_url, revprop_table, ctx, pool);
     }
   else
     {
@@ -361,6 +404,7 @@ svn_client_propset3(svn_commit_info_t **
       apr_hash_t *changelist_hash = NULL;
       const char *target_abspath;
       svn_error_t *err;
+      struct set_props_baton baton;
 
       SVN_ERR(svn_dirent_get_absolute(&target_abspath, target, pool));
 
@@ -383,29 +427,18 @@ svn_client_propset3(svn_commit_info_t **
       else
         SVN_ERR(err);
 
-      if (depth >= svn_depth_files && kind == svn_node_dir)
-        {
-          struct propset_walk_baton wb;
-
-          wb.wc_ctx = ctx->wc_ctx;
-          wb.propname = propname;
-          wb.propval = propval;
-          wb.force = skip_checks;
-          wb.changelist_hash = changelist_hash;
-          wb.notify_func = ctx->notify_func2;
-          wb.notify_baton = ctx->notify_baton2;
-          SVN_ERR(svn_wc__node_walk_children(ctx->wc_ctx, target_abspath,
-                                             FALSE, propset_walk_cb, &wb,
-                                             depth, ctx->cancel_func,
-                                             ctx->cancel_baton, pool));
-        }
-      else if (svn_wc__changelist_match(ctx->wc_ctx, target_abspath,
-                                        changelist_hash, pool))
-        {
-          SVN_ERR(svn_wc_prop_set4(ctx->wc_ctx, target_abspath, propname,
-                                   propval, skip_checks, ctx->notify_func2,
-                                   ctx->notify_baton2, pool));
-        }
+      baton.ctx = ctx;
+      baton.local_abspath = target_abspath;
+      baton.depth = depth;
+      baton.kind = kind;
+      baton.propname = propname;
+      baton.propval = propval;
+      baton.skip_checks = skip_checks;
+      baton.changelist_hash = changelist_hash;
+
+      SVN_ERR(svn_wc__call_with_write_lock(set_props_cb, &baton,
+                                           ctx->wc_ctx, target_abspath, FALSE,
+                                           pool, pool));
 
       return SVN_NO_ERROR;
     }

Modified: subversion/branches/performance/subversion/libsvn_client/repos_diff.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_client/repos_diff.c?rev=993127&r1=993126&r2=993127&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_client/repos_diff.c (original)
+++ subversion/branches/performance/subversion/libsvn_client/repos_diff.c Mon Sep  6 19:13:39 2010
@@ -1057,6 +1057,27 @@ change_file_prop(void *file_baton,
   if (b->skip)
     return SVN_NO_ERROR;
 
+  /* Issue #3657 'phantom svn:eol-style changes cause spurious merge text
+     conflicts'.  When communicating with the repository via ra_serf and
+     ra_neon, the change_dir_prop and change_file_prop svn_delta_editor_t
+     callbacks are called (obviously) when a directory or file property has
+     changed between the start and end of the edit.  Less obvious however,
+     is that these callbacks may be made describing *all* of the properties
+     on FILE_BATON->PATH when using the DAV providers, not just the change(s).
+     (Specifically ra_neon does this for diff/merge and ra_serf does it
+     for diff/merge/update/switch).
+
+     Normally this is fairly harmless, but if it appears that the
+     svn:eol-style property has changed on a file, then we can get spurious
+     text conflicts (i.e. Issue #3657).  To prevent this, we populate
+     FILE_BATON->PRISTINE_PROPS only with actual property changes. */
+  if (value)
+    {
+      const char *current_prop = svn_prop_get_value(b->pristine_props, name);
+      if (current_prop && strcmp(current_prop, value->data) == 0)
+        return SVN_NO_ERROR;
+    }
+
   propchange = apr_array_push(b->propchanges);
   propchange->name = apr_pstrdup(b->pool, name);
   propchange->value = value ? svn_string_dup(value, b->pool) : NULL;

Modified: subversion/branches/performance/subversion/libsvn_client/revert.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_client/revert.c?rev=993127&r1=993126&r2=993127&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_client/revert.c (original)
+++ subversion/branches/performance/subversion/libsvn_client/revert.c Mon Sep  6 19:13:39 2010
@@ -151,7 +151,7 @@ svn_client_revert2(const apr_array_heade
       baton.changelists = changelists;
       baton.ctx = ctx;
       err = svn_wc__call_with_write_lock(revert, &baton, ctx->wc_ctx,
-                                         local_abspath, pool, pool);
+                                         local_abspath, TRUE, pool, pool);
       if (err)
         goto errorful;
     }

Modified: subversion/branches/performance/subversion/libsvn_client/switch.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_client/switch.c?rev=993127&r1=993126&r2=993127&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_client/switch.c (original)
+++ subversion/branches/performance/subversion/libsvn_client/switch.c Mon Sep  6 19:13:39 2010
@@ -294,8 +294,9 @@ svn_client__switch_internal(svn_revnum_t
 
   /* Rely on svn_wc__acquire_write_lock setting ANCHOR_ABSPATH even
      when it returns SVN_ERR_WC_LOCKED */
-  err = svn_wc__acquire_write_lock(&anchor_abspath, ctx->wc_ctx,
-                                   local_abspath, pool, pool);
+  err = svn_wc__acquire_write_lock(&anchor_abspath,
+                                   ctx->wc_ctx, local_abspath, TRUE,
+                                   pool, pool);
   if (err && err->apr_err != SVN_ERR_WC_LOCKED)
     return svn_error_return(err);
 

Modified: subversion/branches/performance/subversion/libsvn_client/update.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_client/update.c?rev=993127&r1=993126&r2=993127&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_client/update.c (original)
+++ subversion/branches/performance/subversion/libsvn_client/update.c Mon Sep  6 19:13:39 2010
@@ -156,9 +156,6 @@ update_internal(svn_revnum_t *result_rev
                                  pool));
 
           /* Target excluded, we are done now */
-          SVN_ERR(svn_wc__release_write_lock(ctx->wc_ctx, anchor_abspath,
-                                             pool));
-
           return SVN_NO_ERROR;
         }
 
@@ -282,8 +279,6 @@ update_internal(svn_revnum_t *result_rev
   if (sleep_here)
     svn_io_sleep_for_timestamps(local_abspath, pool);
 
-  SVN_ERR(svn_wc__release_write_lock(ctx->wc_ctx, anchor_abspath, pool));
-
   /* Let everyone know we're finished here. */
   if (ctx->notify_func2)
     {
@@ -320,31 +315,30 @@ svn_client__update_internal(svn_revnum_t
                             apr_pool_t *pool)
 {
   const char *anchor_abspath;
-  svn_error_t *err1, *err2;
+  svn_error_t *err;
 
   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
 
   if (!innerupdate)
-    {
-      SVN_ERR(svn_wc__acquire_write_lock(&anchor_abspath, ctx->wc_ctx,
-                                         local_abspath, pool, pool));
-    }
+    SVN_ERR(svn_wc__acquire_write_lock(&anchor_abspath,
+                                       ctx->wc_ctx, local_abspath, TRUE,
+                                       pool, pool));
   else
-    {
-      SVN_ERR(svn_wc__acquire_write_lock(NULL, ctx->wc_ctx,
-                                         local_abspath, pool, pool));
-      anchor_abspath = local_abspath;
-    }
+    SVN_ERR(svn_wc__acquire_write_lock(&anchor_abspath,
+                                       ctx->wc_ctx, local_abspath, FALSE,
+                                       pool, pool));
 
-  err1 = update_internal(result_rev, local_abspath, anchor_abspath,
+  err = update_internal(result_rev, local_abspath, anchor_abspath,
                          revision, depth, depth_is_sticky,
                          ignore_externals, allow_unver_obstructions,
                          timestamp_sleep, send_copyfrom_args,
                          innerupdate, ctx, pool);
 
-  err2 = svn_wc__release_write_lock(ctx->wc_ctx, anchor_abspath, pool);
+  err = svn_error_compose_create(
+            err,
+            svn_wc__release_write_lock(ctx->wc_ctx, anchor_abspath, pool));
 
-  return svn_error_compose_create(err1, err2);
+  return svn_error_return(err);
 }
 
 

Modified: subversion/branches/performance/subversion/libsvn_diff/parse-diff.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_diff/parse-diff.c?rev=993127&r1=993126&r2=993127&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_diff/parse-diff.c (original)
+++ subversion/branches/performance/subversion/libsvn_diff/parse-diff.c Mon Sep  6 19:13:39 2010
@@ -500,15 +500,19 @@ parse_prop_name(const char **prop_name, 
 }
 
 /* Return the next *HUNK from a PATCH, using STREAM to read data
- * from the patch file. If no hunk can be found, set *HUNK to NULL. If we
- * have a property hunk, PROP_NAME will be set. If we have a text hunk,
- * PROP_NAME will be NULL. If REVERSE is TRUE, invert the hunk while
- * parsing it. If IGNORE_WHiTESPACES is TRUE, let lines without leading
- * spaces be recognized as context lines.  Allocate results in RESULT_POOL.
- * Use SCRATCH_POOL for all other allocations. */
+ * from the patch file. If no hunk can be found, set *HUNK to NULL. Set
+ * IS_PROPERTY to TRUE if we have a property hunk. If the returned HUNK is
+ * the first belonging to a certain property, then PROP_NAME and
+ * PROP_OPERATION will be set too. If we have a text hunk, PROP_NAME will be
+ * NULL. If REVERSE is TRUE, invert the hunk while parsing it. If
+ * IGNORE_WHiTESPACES is TRUE, let lines without leading spaces be
+ * recognized as context lines.  Allocate results in
+ * RESULT_POOL.  Use SCRATCH_POOL for all other allocations. */
 static svn_error_t *
 parse_next_hunk(svn_hunk_t **hunk,
+                svn_boolean_t *is_property,
                 const char **prop_name,
+                svn_diff_operation_kind_t *prop_operation,
                 svn_patch_t *patch,
                 svn_stream_t *stream,
                 svn_boolean_t reverse,
@@ -533,11 +537,11 @@ parse_next_hunk(svn_hunk_t **hunk,
   svn_boolean_t changed_line_seen;
   apr_pool_t *iterpool;
 
-  /* We only set this if we have a property hunk. 
-   * ### prop_name acts as both a state flag inside this function and a
-   * ### qualifier to discriminate between props and text hunks. Is that
-   * ### kind of overloading ok? */
+  *prop_operation = svn_diff_op_unchanged;
+
+  /* We only set this if we have a property hunk header. */
   *prop_name = NULL;
+  *is_property = FALSE;
 
   if (apr_file_eof(patch->patch_file) == APR_EOF)
     {
@@ -666,10 +670,10 @@ parse_next_hunk(svn_hunk_t **hunk,
                 {
                   original_lines = (*hunk)->original_length;
                   modified_lines = (*hunk)->modified_length;
-                  *prop_name = NULL;
+                  *is_property = FALSE;
                 }
               }
-          else if (starts_with(line->data, prop_atat) && *prop_name)
+          else if (starts_with(line->data, prop_atat))
             {
               /* Looks like we have a property hunk header, try to rip it
                * apart. */
@@ -679,22 +683,26 @@ parse_next_hunk(svn_hunk_t **hunk,
                 {
                   original_lines = (*hunk)->original_length;
                   modified_lines = (*hunk)->modified_length;
+                  *is_property = TRUE;
                 }
             }
           else if (starts_with(line->data, "Added: "))
             {
               SVN_ERR(parse_prop_name(prop_name, line->data, "Added: ",
                                       result_pool));
+              *prop_operation = svn_diff_op_added;
             }
           else if (starts_with(line->data, "Deleted: "))
             {
               SVN_ERR(parse_prop_name(prop_name, line->data, "Deleted: ",
                                       result_pool));
+              *prop_operation = svn_diff_op_deleted;
             }
           else if (starts_with(line->data, "Modified: "))
             {
               SVN_ERR(parse_prop_name(prop_name, line->data, "Modified: ",
                                       result_pool));
+              *prop_operation = svn_diff_op_modified;
             }
           else if (starts_with(line->data, minus)
                    || starts_with(line->data, "git --diff "))
@@ -1042,6 +1050,32 @@ git_deleted_file(enum parse_state *new_s
   return SVN_NO_ERROR;
 }
 
+/* Add a HUNK associated with the property PROP_NAME to PATCH. */
+static svn_error_t *
+add_property_hunk(svn_patch_t *patch, const char *prop_name, 
+                  svn_hunk_t *hunk, svn_diff_operation_kind_t operation,
+                  apr_pool_t *result_pool)
+{
+  svn_prop_patch_t *prop_patch;
+
+  prop_patch = apr_hash_get(patch->prop_patches, prop_name,
+                            APR_HASH_KEY_STRING);
+
+  if (! prop_patch)
+    {
+      prop_patch = apr_palloc(result_pool, sizeof(svn_prop_patch_t));
+      prop_patch->name = prop_name;
+      prop_patch->operation = operation;
+      prop_patch->hunks = apr_array_make(result_pool, 1, sizeof(svn_hunk_t *));
+
+      apr_hash_set(patch->prop_patches, prop_name, APR_HASH_KEY_STRING,
+                   prop_patch);
+    }
+
+  APR_ARRAY_PUSH(prop_patch->hunks, svn_hunk_t *) = hunk;
+
+  return SVN_NO_ERROR;
+}
 
 svn_error_t *
 svn_diff_parse_next_patch(svn_patch_t **patch,
@@ -1181,36 +1215,37 @@ svn_diff_parse_next_patch(svn_patch_t **
   else
     {
       svn_hunk_t *hunk;
+      svn_boolean_t is_property;
+      const char *last_prop_name;
       const char *prop_name;
+      svn_diff_operation_kind_t prop_operation;
 
       /* Parse hunks. */
       (*patch)->hunks = apr_array_make(result_pool, 10, sizeof(svn_hunk_t *));
-      (*patch)->property_hunks = apr_hash_make(result_pool);
+      (*patch)->prop_patches = apr_hash_make(result_pool);
       do
         {
           svn_pool_clear(iterpool);
 
-          SVN_ERR(parse_next_hunk(&hunk, &prop_name, *patch, stream,
-                                  reverse, ignore_whitespace,
+          SVN_ERR(parse_next_hunk(&hunk, &is_property, &prop_name,
+                                  &prop_operation, *patch, stream, reverse,
+                                  ignore_whitespace,
                                   result_pool, iterpool));
-          if (hunk && prop_name)
-            {
-              apr_array_header_t *hunks;
-
-              hunks = apr_hash_get((*patch)->property_hunks, prop_name,
-                                    APR_HASH_KEY_STRING);
-              if (! hunks)
-                {
-                  hunks = apr_array_make(result_pool, 1, 
-                                          sizeof(svn_hunk_t *));
-                  apr_hash_set((*patch)->property_hunks, prop_name,
-                                       APR_HASH_KEY_STRING, hunks);
-                }
 
-              APR_ARRAY_PUSH(hunks, svn_hunk_t *) = hunk;
+          if (hunk && is_property)
+            {
+              if (! prop_name)
+                prop_name = last_prop_name;
+              else
+                last_prop_name = prop_name;
+              SVN_ERR(add_property_hunk(*patch, prop_name, hunk, prop_operation,
+                                        result_pool));
             }
           else if (hunk)
-            APR_ARRAY_PUSH((*patch)->hunks, svn_hunk_t *) = hunk;
+            {
+              APR_ARRAY_PUSH((*patch)->hunks, svn_hunk_t *) = hunk;
+              last_prop_name = NULL;
+            }
 
         }
       while (hunk);

Modified: subversion/branches/performance/subversion/libsvn_fs_fs/fs_fs.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_fs_fs/fs_fs.c?rev=993127&r1=993126&r2=993127&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_fs_fs/fs_fs.c (original)
+++ subversion/branches/performance/subversion/libsvn_fs_fs/fs_fs.c Mon Sep  6 19:13:39 2010
@@ -263,6 +263,14 @@ svn_fs_fs__path_rev_absolute(const char 
                              svn_revnum_t rev,
                              apr_pool_t *pool)
 {
+  fs_fs_data_t *ffd = fs->fsap_data;
+
+  if (ffd->format < SVN_FS_FS__MIN_PACKED_FORMAT)
+    {
+      *path = path_rev(fs, rev, pool);
+      return SVN_NO_ERROR;
+    }
+
   if (! is_packed_rev(fs, rev))
     {
       fs_fs_data_t *ffd = fs->fsap_data;
@@ -1198,6 +1206,8 @@ update_min_unpacked_rev(svn_fs_t *fs, ap
 {
   fs_fs_data_t *ffd = fs->fsap_data;
 
+  SVN_ERR_ASSERT(ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT);
+
   return read_min_unpacked_rev(&ffd->min_unpacked_rev,
                                path_min_unpacked_rev(fs, pool),
                                pool);
@@ -1208,6 +1218,8 @@ update_min_unpacked_revprop(svn_fs_t *fs
 {
   fs_fs_data_t *ffd = fs->fsap_data;
 
+  SVN_ERR_ASSERT(ffd->format >= SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT);
+
   return read_min_unpacked_rev(&ffd->min_unpacked_revprop,
                                path_min_unpacked_revprop(fs, pool),
                                pool);
@@ -2964,9 +2976,11 @@ svn_fs_fs__revision_proplist(apr_hash_t 
                              apr_pool_t *pool)
 {
   svn_error_t *err;
+  fs_fs_data_t *ffd = fs->fsap_data;
 
   err = revision_proplist(proplist_p, fs, rev, pool);
-  if (err && err->apr_err == SVN_ERR_FS_NO_SUCH_REVISION)
+  if (err && err->apr_err == SVN_ERR_FS_NO_SUCH_REVISION
+      && ffd->format >= SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT)
     {
       /* If a pack is occurring simultaneously, the min-unpacked-revprop value
          could change, so reload it and then attempt to fetch these revprops
@@ -7371,7 +7385,7 @@ svn_fs_fs__list_transactions(apr_array_h
   txn_dir = svn_dirent_join(fs->path, PATH_TXNS_DIR, pool);
 
   /* Now find a listing of this directory. */
-  SVN_ERR(svn_io_get_dirents2(&dirents, txn_dir, pool));
+  SVN_ERR(svn_io_get_dirents3(&dirents, txn_dir, TRUE, pool, pool));
 
   /* Loop through all the entries and return anything that ends with '.txn'. */
   for (hi = apr_hash_first(pool, dirents); hi; hi = apr_hash_next(hi))

Modified: subversion/branches/performance/subversion/libsvn_fs_fs/structure
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_fs_fs/structure?rev=993127&r1=993126&r2=993127&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_fs_fs/structure (original)
+++ subversion/branches/performance/subversion/libsvn_fs_fs/structure Mon Sep  6 19:13:39 2010
@@ -59,7 +59,7 @@ repository) is:
   format              File containing the format number of this filesystem
   fsfs.conf           Configuration file
   min-unpacked-rev    File containing the oldest revision not in a pack file
-  min-unpacked_revprop File containing the oldest revision of unpacked revprop
+  min-unpacked-revprop File containing the oldest revision of unpacked revprop
   rep-cache.db        SQLite database mapping rep checksums to locations
   revprops.db         SQLite database of the packed revision properties
 

Propchange: subversion/branches/performance/subversion/libsvn_fs_fs/temp_serializer.c
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Mon Sep  6 19:13:39 2010
@@ -0,0 +1 @@
+/subversion/trunk/subversion/libsvn_fs_fs/temp_serializer.c:962911-970000

Propchange: subversion/branches/performance/subversion/libsvn_fs_fs/temp_serializer.h
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Mon Sep  6 19:13:39 2010
@@ -34,3 +34,4 @@
 /subversion/branches/tc_url_rev/subversion/libsvn_fs_fs/fs_fs.h:874351-874483
 /subversion/branches/tree-conflicts/subversion/libsvn_fs_fs/fs_fs.h:868291-873154
 /subversion/branches/tree-conflicts-notify/subversion/libsvn_fs_fs/fs_fs.h:873926-874008
+/subversion/trunk/subversion/libsvn_fs_fs/temp_serializer.h:962911-970000

Propchange: subversion/branches/performance/subversion/libsvn_fs_util/caching.c
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Mon Sep  6 19:13:39 2010
@@ -34,3 +34,4 @@
 /subversion/branches/tc_url_rev/subversion/libsvn_fs/caching.c:874351-874483
 /subversion/branches/tree-conflicts/subversion/libsvn_fs/caching.c:868291-873154
 /subversion/branches/tree-conflicts-notify/subversion/libsvn_fs/caching.c:873926-874008
+/subversion/trunk/subversion/libsvn_fs_util/caching.c:962911-970000

Modified: subversion/branches/performance/subversion/libsvn_ra/compat.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_ra/compat.c?rev=993127&r1=993126&r2=993127&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_ra/compat.c (original)
+++ subversion/branches/performance/subversion/libsvn_ra/compat.c Mon Sep  6 19:13:39 2010
@@ -663,24 +663,24 @@ svn_ra__file_revs_from_log(svn_ra_sessio
   svn_stream_t *last_stream;
   apr_pool_t *currpool, *lastpool;
 
+  /* Check to make sure we're dealing with a file. */
+  SVN_ERR(svn_ra_check_path(ra_session, path, end, &kind, pool));
+
+  if (kind == svn_node_dir)
+    return svn_error_createf(SVN_ERR_FS_NOT_FILE, NULL,
+                             _("'%s' is not a file"), path);
+
   SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_url, pool));
   SVN_ERR(svn_ra_get_session_url(ra_session, &session_url, pool));
 
   /* Create the initial path, using the repos_url and session_url */
-  tmp = svn_path_is_child(repos_url, session_url, pool);
+  tmp = svn_uri_is_child(repos_url, session_url, pool);
   repos_abs_path = apr_palloc(pool, strlen(tmp) + 1);
   repos_abs_path[0] = '/';
   memcpy(repos_abs_path + 1, tmp, strlen(tmp));
 
-  /* Check to make sure we're dealing with a file. */
-  SVN_ERR(svn_ra_check_path(ra_session, "", end, &kind, pool));
-
-  if (kind == svn_node_dir)
-    return svn_error_createf(SVN_ERR_FS_NOT_FILE, NULL,
-                             _("'%s' is not a file"), repos_abs_path);
-
   condensed_targets = apr_array_make(pool, 1, sizeof(const char *));
-  APR_ARRAY_PUSH(condensed_targets, const char *) = "";
+  APR_ARRAY_PUSH(condensed_targets, const char *) = path;
 
   lmb.path = svn_path_uri_decode(repos_abs_path, pool);
   lmb.eldest = NULL;

Modified: subversion/branches/performance/subversion/libsvn_ra_local/ra_plugin.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_ra_local/ra_plugin.c?rev=993127&r1=993126&r2=993127&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_ra_local/ra_plugin.c (original)
+++ subversion/branches/performance/subversion/libsvn_ra_local/ra_plugin.c Mon Sep  6 19:13:39 2010
@@ -341,14 +341,16 @@ deltify_etc(const svn_commit_info_t *com
             void *baton, apr_pool_t *pool)
 {
   struct deltify_etc_baton *db = baton;
-  svn_error_t *err1, *err2;
+  svn_error_t *err1 = SVN_NO_ERROR;
+  svn_error_t *err2;
   apr_hash_index_t *hi;
   apr_pool_t *iterpool;
 
   /* Invoke the original callback first, in case someone's waiting to
      know the revision number so they can go off and annotate an
      issue or something. */
-  err1 = (*db->callback)(commit_info, db->callback_baton, pool);
+  if (*db->callback)
+    err1 = (*db->callback)(commit_info, db->callback_baton, pool);
 
   /* Maybe unlock the paths. */
   if (db->lock_tokens)

Modified: subversion/branches/performance/subversion/libsvn_ra_neon/commit.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_ra_neon/commit.c?rev=993127&r1=993126&r2=993127&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_ra_neon/commit.c (original)
+++ subversion/branches/performance/subversion/libsvn_ra_neon/commit.c Mon Sep  6 19:13:39 2010
@@ -1357,7 +1357,7 @@ static svn_error_t * commit_close_edit(v
   SVN_ERR(delete_activity(edit_baton, pool));
   SVN_ERR(svn_ra_neon__maybe_store_auth_info(cc->ras, pool));
 
-  if (commit_info->revision != SVN_INVALID_REVNUM)
+  if (cc->callback && commit_info->revision != SVN_INVALID_REVNUM)
     SVN_ERR(cc->callback(commit_info, cc->callback_baton, pool));
 
   return SVN_NO_ERROR;

Modified: subversion/branches/performance/subversion/libsvn_ra_serf/auth.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_ra_serf/auth.c?rev=993127&r1=993126&r2=993127&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_ra_serf/auth.c (original)
+++ subversion/branches/performance/subversion/libsvn_ra_serf/auth.c Mon Sep  6 19:13:39 2010
@@ -54,6 +54,7 @@ setup_request_basic_auth(svn_ra_serf__co
                          serf_bucket_t *hdrs_bkt);
 #endif
 
+#if ! SERF_VERSION_AT_LEAST(0, 4, 0)
 static svn_error_t *
 handle_proxy_basic_auth(svn_ra_serf__handler_t *ctx,
                         serf_request_t *request,
@@ -73,6 +74,9 @@ setup_request_proxy_basic_auth(svn_ra_se
                                const char *uri,
                                serf_bucket_t *hdrs_bkt);
 
+#endif
+
+#if ! SERF_VERSION_AT_LEAST(0, 4, 0) || defined(SVN_RA_SERF_SSPI_ENABLED)
 static svn_error_t *
 default_auth_response_handler(svn_ra_serf__handler_t *ctx,
                               serf_request_t *request,
@@ -81,6 +85,7 @@ default_auth_response_handler(svn_ra_ser
 {
   return SVN_NO_ERROR;
 }
+#endif
 
 /*** Global variables. ***/
 static const svn_ra_serf__auth_protocol_t serf_auth_protocols[] = {
@@ -166,10 +171,11 @@ svn_ra_serf__encode_auth_header(const ch
                                 const char *data, apr_size_t data_len,
                                 apr_pool_t *pool)
 {
-  apr_size_t encoded_len, proto_len;
+  int encoded_len;
+  size_t proto_len;
   char *ptr;
 
-  encoded_len = apr_base64_encode_len(data_len);
+  encoded_len = apr_base64_encode_len((int) data_len);
   proto_len = strlen(protocol);
 
   ptr = apr_palloc(pool, encoded_len + proto_len + 1);
@@ -179,7 +185,7 @@ svn_ra_serf__encode_auth_header(const ch
   ptr += proto_len;
   *ptr++ = ' ';
 
-  apr_base64_encode(ptr, data, data_len);
+  apr_base64_encode(ptr, data, (int) data_len);
 }
 
 /**
@@ -506,6 +512,7 @@ setup_request_basic_auth(svn_ra_serf__co
 }
 #endif
 
+#if ! SERF_VERSION_AT_LEAST(0, 4, 0)
 static svn_error_t *
 handle_proxy_basic_auth(svn_ra_serf__handler_t *ctx,
                         serf_request_t *request,
@@ -574,3 +581,4 @@ setup_request_proxy_basic_auth(svn_ra_se
 
   return SVN_NO_ERROR;
 }
+#endif

Modified: subversion/branches/performance/subversion/libsvn_ra_serf/commit.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_ra_serf/commit.c?rev=993127&r1=993126&r2=993127&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_ra_serf/commit.c (original)
+++ subversion/branches/performance/subversion/libsvn_ra_serf/commit.c Mon Sep  6 19:13:39 2010
@@ -1992,8 +1992,9 @@ close_edit(void *edit_baton,
     }
 
   /* Inform the WC that we did a commit.  */
-  SVN_ERR(ctx->callback(svn_ra_serf__merge_get_commit_info(merge_ctx),
-                        ctx->callback_baton, pool));
+  if (ctx->callback)
+    SVN_ERR(ctx->callback(svn_ra_serf__merge_get_commit_info(merge_ctx),
+                          ctx->callback_baton, pool));
 
   /* If we're using activities, DELETE our completed activity.  */
   if (ctx->activity_url)