You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2013/02/04 21:48:13 UTC

svn commit: r1442344 [9/39] - in /subversion/branches/fsfs-format7: ./ build/ build/ac-macros/ build/generator/ build/generator/templates/ build/win32/ contrib/client-side/emacs/ contrib/server-side/fsfsfixer/fixer/ contrib/server-side/svncutter/ doc/ ...

Modified: subversion/branches/fsfs-format7/subversion/libsvn_client/mergeinfo.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/libsvn_client/mergeinfo.c?rev=1442344&r1=1442343&r2=1442344&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_client/mergeinfo.c (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_client/mergeinfo.c Mon Feb  4 20:48:05 2013
@@ -911,18 +911,15 @@ elide_mergeinfo(svn_mergeinfo_t parent_m
 
 
 svn_error_t *
-svn_client__elide_mergeinfo(const char *target_wcpath,
-                            const char *wc_elision_limit_path,
+svn_client__elide_mergeinfo(const char *target_abspath,
+                            const char *wc_elision_limit_abspath,
                             svn_client_ctx_t *ctx,
                             apr_pool_t *pool)
 {
-  const char *target_abspath;
-  const char *limit_abspath = NULL;
+  const char *limit_abspath = wc_elision_limit_abspath;
 
-  SVN_ERR(svn_dirent_get_absolute(&target_abspath, target_wcpath, pool));
-  if (wc_elision_limit_path)
-    SVN_ERR(svn_dirent_get_absolute(&limit_abspath, wc_elision_limit_path,
-                                    pool));
+  SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
+  SVN_ERR_ASSERT(!wc_elision_limit_abspath || svn_dirent_is_absolute(wc_elision_limit_abspath));
 
   /* Check for first easy out: We are already at the limit path. */
   if (!limit_abspath
@@ -985,12 +982,12 @@ svn_client__elide_mergeinfo(const char *
       /* If TARGET_WCPATH inherited no mergeinfo from the WC and we are
          not limiting our search to the working copy then check if it
          inherits any from the repos. */
-      if (!mergeinfo && !wc_elision_limit_path)
+      if (!mergeinfo && !wc_elision_limit_abspath)
         {
           err = svn_client__get_wc_or_repos_mergeinfo(
             &mergeinfo, NULL, NULL, TRUE,
             svn_mergeinfo_nearest_ancestor,
-            NULL, target_wcpath, ctx, pool);
+            NULL, target_abspath, ctx, pool);
           if (err)
             {
               if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
@@ -1009,7 +1006,7 @@ svn_client__elide_mergeinfo(const char *
 
       /* If there is nowhere to elide TARGET_WCPATH's mergeinfo to and
          the elision is limited, then we are done.*/
-      if (!mergeinfo && wc_elision_limit_path)
+      if (!mergeinfo && wc_elision_limit_abspath)
         return SVN_NO_ERROR;
 
       SVN_ERR(elide_mergeinfo(mergeinfo, target_mergeinfo, target_abspath,

Modified: subversion/branches/fsfs-format7/subversion/libsvn_client/mergeinfo.h
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/libsvn_client/mergeinfo.h?rev=1442344&r1=1442343&r2=1442344&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_client/mergeinfo.h (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_client/mergeinfo.h Mon Feb  4 20:48:05 2013
@@ -292,9 +292,10 @@ svn_client__get_wc_or_repos_mergeinfo_ca
 /* Set *MERGEINFO_P to a mergeinfo constructed solely from the
    natural history of PATHREV.
 
-   If RANGE_YOUNGEST and RANGE_OLDEST are valid, use them to bound the
-   revision ranges of returned mergeinfo.  They are governed by the same
-   rules as the PEG_REVISION, START_REV, and END_REV parameters of
+   If RANGE_YOUNGEST and RANGE_OLDEST are valid, use them as inclusive
+   bounds on the revision ranges of returned mergeinfo.  PATHREV->rev,
+   RANGE_YOUNGEST and RANGE_OLDEST are governed by the same rules as the
+   PEG_REVISION, START_REV, and END_REV parameters (respectively) of
    svn_ra_get_location_segments().
 
    If HAS_REV_ZERO_HISTORY is not NULL, then set *HAS_REV_ZERO_HISTORY to
@@ -345,42 +346,42 @@ svn_client__record_wc_mergeinfo_catalog(
                                         svn_client_ctx_t *ctx,
                                         apr_pool_t *scratch_pool);
 
-/* Elide any svn:mergeinfo set on TARGET_WCPATH to its nearest working
+/* Elide any svn:mergeinfo set on TARGET_ABSPATH to its nearest working
    copy (or possibly repository) ancestor with equivalent mergeinfo.
 
-   If WC_ELISION_LIMIT_PATH is NULL check up to the root of the
+   If WC_ELISION_LIMIT_ABSPATH is NULL check up to the root of the
    working copy or the nearest switched parent for an elision
    destination, if none is found check the repository, otherwise check
-   as far as WC_ELISION_LIMIT_PATH within the working copy.
-   TARGET_WCPATH and WC_ELISION_LIMIT_PATH, if it exists, must both be
+   as far as WC_ELISION_LIMIT_ABSPATH within the working copy.
+   TARGET_WCPATH and WC_ELISION_LIMIT_ABSPATH, if it exists, must both be
    absolute or relative to the working directory.
 
    Elision occurs if:
 
-     A) TARGET_WCPATH has empty mergeinfo and no parent path with
+     A) TARGET_ABSPATH has empty mergeinfo and no parent path with
         explicit mergeinfo can be found in either the WC or the
         repository (WC_ELISION_LIMIT_PATH must be NULL for this to
         occur).
 
-     B) TARGET_WCPATH has empty mergeinfo and its nearest parent also
+     B) TARGET_ABSPATH has empty mergeinfo and its nearest parent also
         has empty mergeinfo.
 
-     C) TARGET_WCPATH has the same mergeinfo as its nearest parent
+     C) TARGET_ABSPATH has the same mergeinfo as its nearest parent
         when that parent's mergeinfo is adjusted for the path
         difference between the two, e.g.:
 
-           TARGET_WCPATH                = A_COPY/D/H
-           TARGET_WCPATH's mergeinfo    = '/A/D/H:3'
-           TARGET_WCPATH nearest parent = A_COPY
-           Parent's mergeinfo           = '/A:3'
-           Path differece               = 'D/H'
-           Parent's adjusted mergeinfo  = '/A/D/H:3'
+           TARGET_ABSPATH                = A_COPY/D/H
+           TARGET_ABSPATH's mergeinfo    = '/A/D/H:3'
+           TARGET_ABSPATH nearest parent = A_COPY
+           Parent's mergeinfo            = '/A:3'
+           Path difference               = 'D/H'
+           Parent's adjusted mergeinfo   = '/A/D/H:3'
 
    If Elision occurs remove the svn:mergeinfo property from
-   TARGET_WCPATH. */
+   TARGET_ABSPATH. */
 svn_error_t *
-svn_client__elide_mergeinfo(const char *target_wcpath,
-                            const char *wc_elision_limit_path,
+svn_client__elide_mergeinfo(const char *target_abspath,
+                            const char *wc_elision_limit_abspath,
                             svn_client_ctx_t *ctx,
                             apr_pool_t *pool);
 

Modified: subversion/branches/fsfs-format7/subversion/libsvn_client/patch.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/libsvn_client/patch.c?rev=1442344&r1=1442343&r2=1442344&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_client/patch.c (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_client/patch.c Mon Feb  4 20:48:05 2013
@@ -175,6 +175,9 @@ typedef struct patch_target_t {
    * CONTENT->existed). */
   apr_file_t *file;
 
+  /* The target file is a symlink */
+  svn_boolean_t is_symlink;
+
   /* The patched file.
    * This is equivalent to the target, except that in appropriate
    * places it contains the modified text as it appears in the patch file.
@@ -437,48 +440,37 @@ resolve_target_path(patch_target_t *targ
                        result_pool, scratch_pool);
   if (err)
     {
-      if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
-        svn_error_clear(err);
-      else
+      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
         return svn_error_trace(err);
+
+      svn_error_clear(err);
+
+      target->locally_deleted = TRUE;
+      target->db_kind = svn_node_none;
+      status = NULL;
     }
   else if (status->node_status == svn_wc_status_ignored ||
            status->node_status == svn_wc_status_unversioned ||
            status->node_status == svn_wc_status_missing ||
-           status->node_status == svn_wc_status_obstructed)
+           status->node_status == svn_wc_status_obstructed ||
+           status->conflicted)
     {
       target->skipped = TRUE;
       return SVN_NO_ERROR;
     }
-
-  SVN_ERR(svn_io_check_path(target->local_abspath,
-                            &target->kind_on_disk, scratch_pool));
-  err = svn_wc__node_is_status_deleted(&target->locally_deleted,
-                                       wc_ctx, target->local_abspath,
-                                       scratch_pool);
-  if (err)
+  else if (status->node_status == svn_wc_status_deleted)
     {
-      if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
-        {
-          svn_error_clear(err);
-          target->locally_deleted = FALSE;
-        }
-      else
-        return svn_error_trace(err);
+      target->locally_deleted = TRUE;
     }
-  SVN_ERR(svn_wc_read_kind(&target->db_kind, wc_ctx, target->local_abspath,
-                           FALSE, scratch_pool));
 
-  /* If the target is a versioned directory present on disk,
-   * and there are only property changes in the patch, we accept
-   * a directory target. Else, we skip directories. */
-  if (target->db_kind == svn_node_dir && ! prop_changes_only)
-    {
-      /* ### We cannot yet replace a locally deleted dir with a file,
-       * ### but some day we might want to allow it. */
-      target->skipped = TRUE;
-      return SVN_NO_ERROR;
-    }
+  if (status && (status->kind != svn_node_unknown))
+    target->db_kind = status->kind;
+  else
+    target->db_kind = svn_node_none;
+
+  SVN_ERR(svn_io_check_special_path(target->local_abspath,
+                                    &target->kind_on_disk, &target->is_symlink,
+                                    scratch_pool));
 
   if (target->locally_deleted)
     {
@@ -498,8 +490,10 @@ resolve_target_path(patch_target_t *targ
           /* As far as we are concerned this target is not locally deleted. */
           target->locally_deleted = FALSE;
 
-          SVN_ERR(svn_io_check_path(target->local_abspath,
-                                    &target->kind_on_disk, scratch_pool));
+          SVN_ERR(svn_io_check_special_path(target->local_abspath,
+                                            &target->kind_on_disk,
+                                            &target->is_symlink,
+                                            scratch_pool));
         }
       else if (target->kind_on_disk != svn_node_none)
         {
@@ -523,6 +517,8 @@ typedef struct prop_read_baton_t {
  * the property value runs out in which case *EOF is set to TRUE.
  * The line-terminator is not stored in *STRINGBUF.
  *
+ * If the line is empty or could not be read, *line is set to NULL.
+ *
  * The line-terminator is detected automatically and stored in *EOL
  * if EOL is not NULL. If the end of the property value is reached
  * and does not end with a newline character, and EOL is not NULL,
@@ -536,17 +532,15 @@ readline_prop(void *baton, svn_stringbuf
               apr_pool_t *scratch_pool)
 {
   prop_read_baton_t *b = (prop_read_baton_t *)baton;
-  svn_stringbuf_t *str;
+  svn_stringbuf_t *str = NULL;
   const char *c;
   svn_boolean_t found_eof;
 
-  str = svn_stringbuf_create_ensure(80, result_pool);
-
   if ((apr_uint64_t)b->offset >= (apr_uint64_t)b->value->len)
     {
       *eol_str = NULL;
       *eof = TRUE;
-      *line = str;
+      *line = NULL;
       return SVN_NO_ERROR;
     }
 
@@ -578,7 +572,11 @@ readline_prop(void *baton, svn_stringbuf
             }
         }
       else
-        svn_stringbuf_appendbyte(str, *c);
+        {
+          if (str == NULL)
+            str = svn_stringbuf_create_ensure(80, result_pool);
+          svn_stringbuf_appendbyte(str, *c);
+        }
 
       if (*eol_str)
         break;
@@ -652,7 +650,7 @@ init_prop_target(prop_patch_target_t **p
   content->hunks = apr_array_make(result_pool, 0, sizeof(hunk_info_t *));
   content->keywords = apr_hash_make(result_pool);
 
-  new_prop_target = apr_palloc(result_pool, sizeof(*new_prop_target));
+  new_prop_target = apr_pcalloc(result_pool, sizeof(*new_prop_target));
   new_prop_target->name = apr_pstrdup(result_pool, prop_name);
   new_prop_target->operation = operation;
   new_prop_target->content = content;
@@ -675,7 +673,7 @@ init_prop_target(prop_patch_target_t **p
 
 
   /* Wire up the read and write callbacks. */
-  prop_read_baton = apr_palloc(result_pool, sizeof(*prop_read_baton));
+  prop_read_baton = apr_pcalloc(result_pool, sizeof(*prop_read_baton));
   prop_read_baton->value = value;
   prop_read_baton->offset = 0;
   content->readline = readline_prop;
@@ -696,6 +694,8 @@ init_prop_target(prop_patch_target_t **p
  * or if EOF is reached in which case *EOF is set to TRUE.
  * The line-terminator is not stored in *STRINGBUF.
  *
+ * If the line is empty or could not be read, *line is set to NULL.
+ *
  * The line-terminator is detected automatically and stored in *EOL
  * if EOL is not NULL. If EOF is reached and FILE does not end
  * with a newline character, and EOL is not NULL, *EOL is set to NULL.
@@ -708,13 +708,11 @@ readline_file(void *baton, svn_stringbuf
               apr_pool_t *scratch_pool)
 {
   apr_file_t *file = (apr_file_t *)baton;
-  svn_stringbuf_t *str;
+  svn_stringbuf_t *str = NULL;
   apr_size_t numbytes;
   char c;
   svn_boolean_t found_eof;
 
-  str = svn_stringbuf_create_ensure(80, result_pool);
-
   /* Read bytes into STR up to and including, but not storing,
    * the next EOL sequence. */
   *eol_str = NULL;
@@ -761,7 +759,11 @@ readline_file(void *baton, svn_stringbuf
             }
         }
       else
-        svn_stringbuf_appendbyte(str, c);
+        {
+          if (str == NULL)
+            str = svn_stringbuf_create_ensure(80, result_pool);
+          svn_stringbuf_appendbyte(str, c);
+        }
 
       if (*eol_str)
         break;
@@ -807,6 +809,118 @@ write_file(void *baton, const char *buf,
   return SVN_NO_ERROR;
 }
 
+/* Handling symbolic links:
+ *
+ * In Subversion, symlinks can be represented on disk in two distinct ways.
+ * On systems which support symlinks, a symlink is created on disk.
+ * On systems which do not support symlink, a file is created on disk
+ * which contains the "normal form" of the symlink, which looks like:
+ *   link TARGET
+ * where TARGET is the file the symlink points to.
+ *
+ * When reading symlinks (i.e. the link itself, not the file the symlink
+ * is pointing to) through the svn_subst_create_specialfile() function
+ * into a buffer, the buffer always contains the "normal form" of the symlink.
+ * Due to this representation symlinks always contain a single line of text.
+ *
+ * The functions below are needed to deal with the case where a patch
+ * wants to change the TARGET that a symlink points to.
+ */
+
+/* Baton for the (readline|tell|seek|write)_symlink functions. */
+struct symlink_baton_t
+{
+  /* The path to the symlink on disk (not the path to the target of the link) */
+  const char *local_abspath;
+
+  /* Indicates whether the "normal form" of the symlink has been read. */
+  svn_boolean_t at_eof;
+};
+
+/* Allocate *STRINGBUF in RESULT_POOL, and store into it the "normal form"
+ * of the symlink accessed via BATON.
+ *
+ * Otherwise behaves like readline_file(), which see.
+ */
+static svn_error_t *
+readline_symlink(void *baton, svn_stringbuf_t **line, const char **eol_str,
+                 svn_boolean_t *eof, apr_pool_t *result_pool,
+                 apr_pool_t *scratch_pool)
+{
+  struct symlink_baton_t *sb = baton;
+
+  if (eof)
+    *eof = TRUE;
+  if (eol_str)
+    *eol_str = NULL;
+
+  if (sb->at_eof)
+    {
+      *line = NULL;
+    }
+  else
+    {
+      svn_string_t *dest;
+
+      SVN_ERR(svn_io_read_link(&dest, sb->local_abspath, scratch_pool));
+      *line = svn_stringbuf_createf(result_pool, "link %s", dest->data);
+      sb->at_eof = TRUE;
+    }
+
+  return SVN_NO_ERROR;
+}
+
+/* Set *OFFSET to 1 or 0 depending on whether the "normal form" of
+ * the symlink has already been read. */
+static svn_error_t *
+tell_symlink(void *baton, apr_off_t *offset, apr_pool_t *scratch_pool)
+{
+  struct symlink_baton_t *sb = baton;
+
+  *offset = sb->at_eof ? 1 : 0;
+  return SVN_NO_ERROR;
+}
+
+/* If offset is non-zero, mark the symlink as having been read in its
+ * "normal form". Else, mark the symlink as not having been read yet. */
+static svn_error_t *
+seek_symlink(void *baton, apr_off_t offset, apr_pool_t *scratch_pool)
+{
+  struct symlink_baton_t *sb = baton;
+
+  sb->at_eof = (offset != 0);
+  return SVN_NO_ERROR;
+}
+
+
+/* Set the target of the symlink accessed via BATON.
+ * The contents of BUF must be a valid "normal form" of a symlink. */
+static svn_error_t *
+write_symlink(void *baton, const char *buf, apr_size_t len,
+              apr_pool_t *scratch_pool)
+{
+  const char *target_abspath = baton;
+  const char *new_name;
+  const char *link = apr_pstrndup(scratch_pool, buf, len);
+
+  if (strncmp(link, "link ", 5) != 0)
+    return svn_error_create(SVN_ERR_IO_WRITE_ERROR, NULL,
+                            _("Invalid link representation"));
+
+  link += 5; /* Skip "link " */
+
+  /* We assume the entire symlink is written at once, as the patch
+     format is line based */
+
+  SVN_ERR(svn_io_create_unique_link(&new_name, target_abspath, link,
+                                    ".tmp", scratch_pool));
+
+  SVN_ERR(svn_io_file_rename(new_name, target_abspath, scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
+
 /* Return a suitable filename for the target of PATCH.
  * Examine the ``old'' and ``new'' file names, and choose the file name
  * with the fewest path components, the shortest basename, and the shortest
@@ -912,10 +1026,24 @@ init_patch_target(patch_target_t **patch
 
       /* Create a temporary file to write the patched result to.
        * Also grab various bits of information about the file. */
-      if (target->kind_on_disk == svn_node_file)
+      if (target->is_symlink)
+        {
+          struct symlink_baton_t *sb = apr_pcalloc(result_pool, sizeof(*sb));
+          content->existed = TRUE;
+
+          sb->local_abspath = target->local_abspath;
+
+          /* Wire up the read callbacks. */
+          content->read_baton = sb;
+
+          content->readline = readline_symlink;
+          content->seek = seek_symlink;
+          content->tell = tell_symlink;
+        }
+      else if (target->kind_on_disk == svn_node_file)
         {
           SVN_ERR(svn_io_file_open(&target->file, target->local_abspath,
-                                   APR_READ | APR_BINARY | APR_BUFFERED,
+                                   APR_READ | APR_BUFFERED,
                                    APR_OS_DEFAULT, result_pool));
           SVN_ERR(svn_wc_text_modified_p2(&target->local_mods, wc_ctx,
                                           target->local_abspath, FALSE,
@@ -950,17 +1078,34 @@ init_patch_target(patch_target_t **patch
       else if (patch->operation == svn_diff_op_deleted)
         target->deleted = TRUE;
 
-      /* Open a temporary file to write the patched result to. */
-      SVN_ERR(svn_io_open_unique_file3(&target->patched_file,
-                                       &target->patched_path, NULL,
-                                       remove_tempfiles ?
-                                         svn_io_file_del_on_pool_cleanup :
-                                         svn_io_file_del_none,
-                                       result_pool, scratch_pool));
+      if (! target->is_symlink)
+        {
+          /* Open a temporary file to write the patched result to. */
+          SVN_ERR(svn_io_open_unique_file3(&target->patched_file,
+                                           &target->patched_path, NULL,
+                                           remove_tempfiles ?
+                                             svn_io_file_del_on_pool_cleanup :
+                                             svn_io_file_del_none,
+                                           result_pool, scratch_pool));
+
+          /* Put the write callback in place. */
+          content->write = write_file;
+          content->write_baton = target->patched_file;
+        }
+      else
+        {
+          /* Put the write callback in place. */
+          SVN_ERR(svn_io_open_unique_file3(NULL,
+                                           &target->patched_path, NULL,
+                                           remove_tempfiles ?
+                                             svn_io_file_del_on_pool_cleanup :
+                                             svn_io_file_del_none,
+                                           result_pool, scratch_pool));
+
+          content->write_baton = (void*)target->patched_path;
 
-      /* Put the write callback in place. */
-      content->write = write_file;
-      content->write_baton = target->patched_file;
+          content->write = write_symlink;
+        }
 
       /* Open a temporary file to write rejected hunks to. */
       SVN_ERR(svn_io_open_unique_file3(&target->reject_file,
@@ -1009,7 +1154,9 @@ init_patch_target(patch_target_t **patch
 }
 
 /* Read a *LINE from CONTENT. If the line has not been read before
- * mark the line in CONTENT->LINES. Allocate *LINE in RESULT_POOL.
+ * mark the line in CONTENT->LINES.
+ * If a line could be read successfully, increase CONTENT->CURRENT_LINE,
+ * and allocate *LINE in RESULT_POOL.
  * Do temporary allocations in SCRATCH_POOL.
  */
 static svn_error_t *
@@ -1044,14 +1191,22 @@ readline(target_content_t *content,
   if (content->eol_style == svn_subst_eol_style_none)
     content->eol_str = eol_str;
 
-  /* Contract keywords. */
-  SVN_ERR(svn_subst_translate_cstring2(line_raw->data, line,
-                                       NULL, FALSE,
-                                       content->keywords, FALSE,
-                                       result_pool));
-  if (! content->eof)
+  if (line_raw)
+    {
+      /* Contract keywords. */
+      SVN_ERR(svn_subst_translate_cstring2(line_raw->data, line,
+                                           NULL, FALSE,
+                                           content->keywords, FALSE,
+                                           result_pool));
+    }
+  else
+    *line = "";
+
+  if ((line_raw && line_raw->len > 0) || eol_str)
     content->current_line++;
 
+  SVN_ERR_ASSERT(content->current_line > 0);
+
   return SVN_NO_ERROR;
 }
 
@@ -1383,26 +1538,45 @@ get_hunk_info(hunk_info_t **hi, patch_ta
    * 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 && ! is_prop_hunk)
+  if (original_start == 0 && fuzz > 0)
+    {
+      matched_line = 0; /* reject any fuzz for new files */
+    }
+  else if (original_start == 0 && ! is_prop_hunk)
     {
       if (target->kind_on_disk == svn_node_file)
         {
-          if (target->db_kind == svn_node_file)
+          const svn_io_dirent2_t *dirent;
+          SVN_ERR(svn_io_stat_dirent2(&dirent, target->local_abspath, FALSE,
+                                      TRUE, scratch_pool, scratch_pool));
+
+          if (dirent->kind == svn_node_file
+              && !dirent->special
+              && dirent->filesize == 0)
+            {
+              matched_line = 1; /* Matched an on-disk empty file */
+            }
+          else
             {
-              svn_boolean_t file_matches;
+              if (target->db_kind == svn_node_file)
+                {
+                  svn_boolean_t file_matches;
 
-              SVN_ERR(match_existing_target(&file_matches, content, hunk,
+                  /* ### I can't reproduce anything but a no-match here.
+                         The content is already at eof, so any hunk fails */
+                  SVN_ERR(match_existing_target(&file_matches, content, hunk,
                                             scratch_pool));
-              if (file_matches)
-                {
-                  matched_line = 1;
-                  already_applied = TRUE;
+                  if (file_matches)
+                    {
+                      matched_line = 1;
+                      already_applied = TRUE;
+                    }
+                  else
+                    matched_line = 0; /* reject */
                 }
               else
                 matched_line = 0; /* reject */
             }
-          else
-            matched_line = 0; /* reject */
         }
       else
         matched_line = 1;
@@ -1518,7 +1692,7 @@ get_hunk_info(hunk_info_t **hi, patch_ta
       matched_line = 0;
     }
 
-  (*hi) = apr_palloc(result_pool, sizeof(hunk_info_t));
+  (*hi) = apr_pcalloc(result_pool, sizeof(hunk_info_t));
   (*hi)->hunk = hunk;
   (*hi)->matched_line = matched_line;
   (*hi)->rejected = (matched_line == 0);
@@ -2094,13 +2268,17 @@ apply_one_patch(patch_target_t **patch_t
 
   svn_pool_destroy(iterpool);
 
-  /* Now close files we don't need any longer to get their contents
-   * flushed to disk.
-   * But we're not closing the reject file -- it still needed and
-   * will be closed later in write_out_rejected_hunks(). */
-  if (target->kind_on_disk == svn_node_file)
-    SVN_ERR(svn_io_file_close(target->file, scratch_pool));
-  SVN_ERR(svn_io_file_close(target->patched_file, scratch_pool));
+  if (!target->is_symlink)
+    {
+      /* Now close files we don't need any longer to get their contents
+       * flushed to disk.
+       * But we're not closing the reject file -- it still needed and
+       * will be closed later in write_out_rejected_hunks(). */
+      if (target->kind_on_disk == svn_node_file)
+        SVN_ERR(svn_io_file_close(target->file, scratch_pool));
+  
+      SVN_ERR(svn_io_file_close(target->patched_file, scratch_pool));
+    }
 
   if (! target->skipped)
     {
@@ -2111,10 +2289,10 @@ apply_one_patch(patch_target_t **patch_t
        * We'll need those to figure out whether we should delete the
        * patched file. */
       SVN_ERR(svn_io_stat(&patched_file, target->patched_path,
-                          APR_FINFO_SIZE, scratch_pool));
+                          APR_FINFO_SIZE | APR_FINFO_LINK, scratch_pool));
       if (target->kind_on_disk == svn_node_file)
         SVN_ERR(svn_io_stat(&working_file, target->local_abspath,
-                            APR_FINFO_SIZE, scratch_pool));
+                            APR_FINFO_SIZE | APR_FINFO_LINK, scratch_pool));
       else
         working_file.size = 0;
 
@@ -2288,9 +2466,10 @@ create_missing_parents(patch_target_t *t
               if (ctx->cancel_func)
                 SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
 
-              SVN_ERR(svn_wc_add_from_disk(ctx->wc_ctx, local_abspath,
-                                           ctx->notify_func2, ctx->notify_baton2,
-                                           iterpool));
+              SVN_ERR(svn_wc_add_from_disk2(ctx->wc_ctx, local_abspath,
+                                            NULL /*props*/,
+                                            ctx->notify_func2, ctx->notify_baton2,
+                                            iterpool));
             }
         }
     }
@@ -2402,8 +2581,9 @@ install_patched_target(patch_target_t *t
                * Suppress notification, we'll do that later (and also
                * during dry-run). Don't allow cancellation because
                * we'd rather notify about what we did before aborting. */
-              SVN_ERR(svn_wc_add_from_disk(ctx->wc_ctx, target->local_abspath,
-                                           NULL, NULL, pool));
+              SVN_ERR(svn_wc_add_from_disk2(ctx->wc_ctx, target->local_abspath,
+                                            NULL /*props*/,
+                                            NULL, NULL, pool));
             }
 
           /* Restore the target's executable bit if necessary. */
@@ -2494,10 +2674,11 @@ install_patched_prop_targets(patch_targe
             {
               SVN_ERR(svn_io_file_create(target->local_abspath, "",
                                          scratch_pool));
-              SVN_ERR(svn_wc_add_from_disk(ctx->wc_ctx, target->local_abspath,
-                                           /* suppress notification */
-                                           NULL, NULL,
-                                           iterpool));
+              SVN_ERR(svn_wc_add_from_disk2(ctx->wc_ctx, target->local_abspath,
+                                            NULL /*props*/,
+                                            /* suppress notification */
+                                            NULL, NULL,
+                                            iterpool));
             }
           target->added = TRUE;
         }
@@ -2571,265 +2752,129 @@ install_patched_prop_targets(patch_targe
   return SVN_NO_ERROR;
 }
 
-/* Baton for find_existing_children() */
-struct status_baton
+/* Baton for can_delete_callback */
+struct can_delete_baton_t
 {
-  apr_array_header_t *existing_targets;
-  const char *parent_path;
-  apr_pool_t *result_pool;
+  svn_boolean_t must_keep;
+  const apr_array_header_t *targets_info;
+  const char *local_abspath;
 };
 
 /* Implements svn_wc_status_func4_t. */
 static svn_error_t *
-find_existing_children(void *baton,
-                       const char *abspath,
-                       const svn_wc_status3_t *status,
-                       apr_pool_t *pool)
+can_delete_callback(void *baton,
+                    const char *abspath,
+                    const svn_wc_status3_t *status,
+                    apr_pool_t *pool)
 {
-  struct status_baton *btn = baton;
+  struct can_delete_baton_t *cb = baton;
+  int i;
 
-  if (status->node_status != svn_wc_status_none
-      && status->node_status != svn_wc_status_deleted
-      && strcmp(abspath, btn->parent_path))
+  switch(status->node_status)
     {
-      APR_ARRAY_PUSH(btn->existing_targets,
-                     const char *) = apr_pstrdup(btn->result_pool,
-                                                 abspath);
-    }
+      case svn_wc_status_none:
+      case svn_wc_status_deleted:
+        return SVN_NO_ERROR;
 
-  return SVN_NO_ERROR;
-}
+      default:
+        if (! strcmp(cb->local_abspath, abspath))
+          return SVN_NO_ERROR; /* Only interested in descendants */
 
-/* Indicate in *EMPTY whether the directory at LOCAL_ABSPATH has any
- * versioned or unversioned children. Consider any DELETED_TARGETS,
- * as well as paths occuring as keys of DELETED_ABSPATHS_HASH (which may
- * be NULL) as already deleted. Use WC_CTX as the working copy context.
- * Do temporary allocations in SCRATCH_POOL. */
-static svn_error_t *
-check_dir_empty(svn_boolean_t *empty, const char *local_abspath,
-                svn_wc_context_t *wc_ctx,
-                apr_array_header_t *deleted_targets,
-                apr_hash_t *deleted_abspath_hash,
-                apr_pool_t *scratch_pool)
-{
-  struct status_baton btn;
-  svn_boolean_t is_wc_root;
-  int i;
+        for (i = 0; i < cb->targets_info->nelts; i++)
+          {
+            const patch_target_info_t *target_info =
+               APR_ARRAY_IDX(cb->targets_info, i, const patch_target_info_t *);
 
-  /* Working copy root cannot be deleted, so never consider it empty. */
-  SVN_ERR(svn_wc__strictly_is_wc_root(&is_wc_root, wc_ctx, local_abspath,
-                                      scratch_pool));
-  if (is_wc_root)
-    {
-      *empty = FALSE;
-      return SVN_NO_ERROR;
-    }
+            if (! strcmp(target_info->local_abspath, abspath))
+              {
+                if (target_info->deleted)
+                  return SVN_NO_ERROR;
 
-  /* Find existing children of the directory. */
-  btn.existing_targets = apr_array_make(scratch_pool, 0,
-                                        sizeof(patch_target_t *));
-  btn.parent_path = local_abspath;
-  btn.result_pool = scratch_pool;
-  SVN_ERR(svn_wc_walk_status(wc_ctx, local_abspath, svn_depth_immediates,
-                             TRUE, TRUE, FALSE, NULL, find_existing_children,
-                             &btn, NULL, NULL, scratch_pool));
-  *empty = TRUE;
-
-  /* Do we delete all children? */
-  for (i = 0; i < btn.existing_targets->nelts; i++)
-    {
-      int j;
-      const char *found;
-      svn_boolean_t deleted;
-
-      deleted = FALSE;
-      found = APR_ARRAY_IDX(btn.existing_targets, i, const char *);
-
-      for (j = 0; j < deleted_targets->nelts; j++)
-        {
-          patch_target_info_t *target_info;
-
-          target_info = APR_ARRAY_IDX(deleted_targets, j,
-                                      patch_target_info_t *);
-          if (! svn_path_compare_paths(found, target_info->local_abspath))
-           {
-              deleted = TRUE;
-              break;
-           }
-        }
-      if (! deleted && deleted_abspath_hash)
-        {
-          apr_hash_index_t *hi;
+                break; /* Cease invocation; must keep */
+              }
+          }
 
-          for (hi = apr_hash_first(scratch_pool, deleted_abspath_hash);
-               hi;
-               hi = apr_hash_next(hi))
-            {
-              const char *abspath;
+        cb->must_keep = TRUE;
 
-              abspath = svn__apr_hash_index_key(hi);
-              if (! svn_path_compare_paths(found, abspath))
-               {
-                  deleted = TRUE;
-                  break;
-               }
-            }
-        }
-      if (! deleted)
-        {
-          *empty = FALSE;
-          break;
-        }
+        return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
     }
-
-  return SVN_NO_ERROR;
 }
 
-/* Delete all directories from the working copy which are left empty
- * by deleted TARGETS. Use client context CTX.
- * If DRY_RUN is TRUE, do not modify the working copy.
- * Do temporary allocations in SCRATCH_POOL. */
 static svn_error_t *
-delete_empty_dirs(apr_array_header_t *targets_info, svn_client_ctx_t *ctx,
-                  svn_boolean_t dry_run, apr_pool_t *scratch_pool)
+check_ancestor_delete(const char *deleted_target,
+                      apr_array_header_t *targets_info,
+                      const char *apply_root,
+                      svn_boolean_t dry_run,
+                      svn_client_ctx_t *ctx,
+                      apr_pool_t *result_pool,
+                      apr_pool_t *scratch_pool)
 {
-  apr_hash_t *empty_dirs;
-  apr_hash_t *non_empty_dirs;
-  apr_array_header_t *deleted_targets;
-  apr_pool_t *iterpool;
-  svn_boolean_t again;
-  int i;
-  apr_hash_index_t *hi;
-
-  /* Get a list of all deleted targets. */
-  deleted_targets = apr_array_make(scratch_pool, 0, sizeof(patch_target_t *));
-  for (i = 0; i < targets_info->nelts; i++)
-    {
-      patch_target_info_t *target_info;
-
-      target_info = APR_ARRAY_IDX(targets_info, i, patch_target_info_t *);
-      if (target_info->deleted)
-        APR_ARRAY_PUSH(deleted_targets, patch_target_info_t *) = target_info;
-    }
-
-  /* We have nothing to do if there aren't any deleted targets. */
-  if (deleted_targets->nelts == 0)
-    return SVN_NO_ERROR;
-
-  /* Look for empty parent directories of deleted targets. */
-  empty_dirs = apr_hash_make(scratch_pool);
-  non_empty_dirs = apr_hash_make(scratch_pool);
-  iterpool = svn_pool_create(scratch_pool);
-  for (i = 0; i < deleted_targets->nelts; i++)
-    {
-      svn_boolean_t parent_empty;
-      patch_target_info_t *target_info;
-      const char *parent;
-
-      svn_pool_clear(iterpool);
-
-      if (ctx->cancel_func)
-        SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
-
-      target_info = APR_ARRAY_IDX(deleted_targets, i, patch_target_info_t *);
-
-      parent = svn_dirent_dirname(target_info->local_abspath, iterpool);
-
-      if (apr_hash_get(non_empty_dirs, parent, APR_HASH_KEY_STRING))
-        continue;
-      else if (apr_hash_get(empty_dirs, parent, APR_HASH_KEY_STRING))
-        continue;
+  struct can_delete_baton_t cb;
+  svn_error_t *err;
+  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
 
-      SVN_ERR(check_dir_empty(&parent_empty, parent, ctx->wc_ctx,
-                              deleted_targets, NULL, iterpool));
-      if (parent_empty)
-        apr_hash_set(empty_dirs, apr_pstrdup(scratch_pool, parent),
-                     APR_HASH_KEY_STRING, "");
-      else
-        apr_hash_set(non_empty_dirs, apr_pstrdup(scratch_pool, parent),
-                     APR_HASH_KEY_STRING, "");
-    }
+  const char *dir_abspath = svn_dirent_dirname(deleted_target, scratch_pool);
 
-  /* We have nothing to do if there aren't any empty directories. */
-  if (apr_hash_count(empty_dirs) == 0)
+  while (svn_dirent_is_child(apply_root, dir_abspath, iterpool))
     {
-      svn_pool_destroy(iterpool);
-      return SVN_NO_ERROR;
-    }
-
-  /* Determine the minimal set of empty directories we need to delete. */
-  do
-    {
-      apr_hash_t *empty_dirs_copy;
-
       svn_pool_clear(iterpool);
 
-      if (ctx->cancel_func)
-        SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
-
-      /* Rebuild the empty dirs list, replacing empty dirs which have
-       * an empty parent with their parent. */
-      again = FALSE;
-      empty_dirs_copy = apr_hash_copy(iterpool, empty_dirs);
-      SVN_ERR(svn_hash__clear(empty_dirs, iterpool));
-
-      for (hi = apr_hash_first(iterpool, empty_dirs_copy);
-           hi;
-           hi = apr_hash_next(hi))
-        {
-          svn_boolean_t parent_empty;
-          const char *empty_dir;
-          const char *parent;
+      cb.local_abspath = dir_abspath;
+      cb.must_keep = FALSE;
+      cb.targets_info = targets_info;
+    
+      err = svn_wc_walk_status(ctx->wc_ctx, dir_abspath, svn_depth_infinity,
+                               TRUE, FALSE, FALSE, NULL,
+                               can_delete_callback, &cb,
+                               ctx->cancel_func, ctx->cancel_baton,
+                               iterpool);
+    
+      if (err)
+        {
+          if (err->apr_err != SVN_ERR_CEASE_INVOCATION)
+            return svn_error_trace(err);
 
-          empty_dir = svn__apr_hash_index_key(hi);
-          parent = svn_dirent_dirname(empty_dir, iterpool);
+          svn_error_clear(err);
+        }
 
-          if (apr_hash_get(empty_dirs, parent, APR_HASH_KEY_STRING))
-            continue;
+      if (cb.must_keep)
+      {
+        break;
+      }
 
-          SVN_ERR(check_dir_empty(&parent_empty, parent, ctx->wc_ctx,
-                                  deleted_targets, empty_dirs_copy,
-                                  iterpool));
-          if (parent_empty)
-            {
-              again = TRUE;
-              apr_hash_set(empty_dirs, apr_pstrdup(scratch_pool, parent),
-                           APR_HASH_KEY_STRING, "");
-            }
-          else
-            apr_hash_set(empty_dirs, apr_pstrdup(scratch_pool, empty_dir),
-                         APR_HASH_KEY_STRING, "");
+      if (! dry_run)
+        {
+          SVN_ERR(svn_wc_delete4(ctx->wc_ctx, dir_abspath, FALSE, FALSE,
+                                 ctx->cancel_func, ctx->cancel_baton,
+                                 NULL, NULL,
+                                 scratch_pool));
         }
-    }
-  while (again);
 
-  /* Finally, delete empty directories. */
-  for (hi = apr_hash_first(scratch_pool, empty_dirs);
-       hi;
-       hi = apr_hash_next(hi))
-    {
-      const char *empty_dir;
+      {
+        patch_target_info_t *pti = apr_pcalloc(result_pool, sizeof(*pti));
 
-      svn_pool_clear(iterpool);
+        pti->local_abspath = apr_pstrdup(result_pool, dir_abspath);
+        pti->deleted = TRUE;
+
+        APR_ARRAY_PUSH(targets_info, patch_target_info_t *) = pti;
+      }
 
-      if (ctx->cancel_func)
-        SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
 
-      empty_dir = svn__apr_hash_index_key(hi);
-      if (! dry_run)
-        SVN_ERR(svn_wc_delete4(ctx->wc_ctx, empty_dir, FALSE, FALSE,
-                               ctx->cancel_func, ctx->cancel_baton,
-                               NULL, NULL, /* no duplicate notification */
-                               iterpool));
       if (ctx->notify_func2)
         {
           svn_wc_notify_t *notify;
 
-          notify = svn_wc_create_notify(empty_dir, svn_wc_notify_delete,
-                                        iterpool);
-          (*ctx->notify_func2)(ctx->notify_baton2, notify, iterpool);
+          notify = svn_wc_create_notify(dir_abspath, svn_wc_notify_delete,
+                                    iterpool);
+          notify->kind = svn_node_dir;
+
+          ctx->notify_func2(ctx->notify_baton2, notify, iterpool);
         }
+
+      /* And check if we must also delete the parent */
+      dir_abspath = svn_dirent_dirname(dir_abspath, scratch_pool);
     }
+
   svn_pool_destroy(iterpool);
 
   return SVN_NO_ERROR;
@@ -2894,7 +2939,7 @@ apply_patches(/* The path to the patch f
             {
               /* Save info we'll still need when we're done patching. */
               patch_target_info_t *target_info =
-                apr_palloc(scratch_pool, sizeof(patch_target_info_t));
+                apr_pcalloc(scratch_pool, sizeof(patch_target_info_t));
               target_info->local_abspath = apr_pstrdup(scratch_pool,
                                                        target->local_abspath);
               target_info->deleted = target->deleted;
@@ -2917,14 +2962,19 @@ apply_patches(/* The path to the patch f
                   SVN_ERR(write_out_rejected_hunks(target, dry_run, iterpool));
                 }
               SVN_ERR(send_patch_notification(target, ctx, iterpool));
+
+              if (target->deleted && !target->skipped)
+                {
+                  SVN_ERR(check_ancestor_delete(target_info->local_abspath,
+                                                targets_info, abs_wc_path,
+                                                dry_run, ctx,
+                                                scratch_pool, iterpool));
+                }
             }
         }
     }
   while (patch);
 
-  /* Delete directories which are empty after patching, if any. */
-  SVN_ERR(delete_empty_dirs(targets_info, ctx, dry_run, scratch_pool));
-
   SVN_ERR(svn_diff_close_patch_file(patch_file, iterpool));
   svn_pool_destroy(iterpool);
 

Modified: subversion/branches/fsfs-format7/subversion/libsvn_client/prop_commands.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/libsvn_client/prop_commands.c?rev=1442344&r1=1442343&r2=1442344&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_client/prop_commands.c (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_client/prop_commands.c Mon Feb  4 20:48:05 2013
@@ -48,29 +48,6 @@
 
 /*** Code. ***/
 
-/* Check whether NAME is a revision property name.
- *
- * Return TRUE if it is.
- * Return FALSE if it is not.
- */
-static svn_boolean_t
-is_revision_prop_name(const char *name)
-{
-  apr_size_t i;
-  static const char *revision_props[] =
-    {
-      SVN_PROP_REVISION_ALL_PROPS
-    };
-
-  for (i = 0; i < sizeof(revision_props) / sizeof(revision_props[0]); i++)
-    {
-      if (strcmp(name, revision_props[i]) == 0)
-        return TRUE;
-    }
-  return FALSE;
-}
-
-
 /* Return an SVN_ERR_CLIENT_PROPERTY_NAME error if NAME is a wcprop,
    else return SVN_NO_ERROR. */
 static svn_error_t *
@@ -285,7 +262,7 @@ static svn_error_t *
 check_prop_name(const char *propname,
                 const svn_string_t *propval)
 {
-  if (is_revision_prop_name(propname))
+  if (svn_prop_is_known_svn_rev_prop(propname))
     return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
                              _("Revision property '%s' not allowed "
                                "in this context"), propname);
@@ -659,11 +636,19 @@ remote_propget(apr_hash_t *props,
 
   if (inherited_props)
     {
+      const char *repos_root_url;
+
       /* We will filter out all but PROPNAME later, making a final copy
-         in RESULT_POOL, so pass SCRATCH_POOL for both pools. */
+         in RESULT_POOL, so pass SCRATCH_POOL for all pools. */
       SVN_ERR(svn_ra_get_inherited_props(ra_session, inherited_props,
                                          target_relative, revnum,
                                          scratch_pool, scratch_pool));
+      SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url,
+                                     scratch_pool));
+      SVN_ERR(svn_client__iprop_relpaths_to_urls(*inherited_props,
+                                                 repos_root_url,
+                                                 scratch_pool,
+                                                 scratch_pool));
     }
 
   /* Make a copy of any inherited PROPNAME properties in RESULT_POOL. */
@@ -913,9 +898,20 @@ svn_client_propget5(apr_hash_t **props,
         return svn_error_trace(err);
 
       if (inherited_props && local_iprops)
-        SVN_ERR(svn_wc__get_iprops(inherited_props, ctx->wc_ctx,
-                                   target, propname,
-                                   result_pool, scratch_pool));
+        {
+          const char *repos_root_url;
+
+          SVN_ERR(svn_wc__get_iprops(inherited_props, ctx->wc_ctx,
+                                     target, propname,
+                                     result_pool, scratch_pool));
+          SVN_ERR(svn_client_get_repos_root(&repos_root_url, NULL,
+                                            target, ctx, scratch_pool,
+                                            scratch_pool));
+          SVN_ERR(svn_client__iprop_relpaths_to_urls(*inherited_props,
+                                                     repos_root_url,
+                                                     result_pool,
+                                                     scratch_pool));
+        }
 
       SVN_ERR(get_prop_from_wc(props, propname, target,
                                pristine, kind,
@@ -1147,11 +1143,23 @@ remote_proplist(const char *target_prefi
     }
 
   if (get_target_inherited_props)
-    SVN_ERR(svn_ra_get_inherited_props(ra_session, &inherited_props,
-                                       target_relative, revnum,
-                                       result_pool, scratch_pool));
+    {
+      const char *repos_root_url;
+
+      SVN_ERR(svn_ra_get_inherited_props(ra_session, &inherited_props,
+                                         target_relative, revnum,
+                                         result_pool, scratch_pool));
+      SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url,
+                                     scratch_pool));
+      SVN_ERR(svn_client__iprop_relpaths_to_urls(inherited_props,
+                                                 repos_root_url,
+                                                 result_pool,
+                                                 scratch_pool));
+    }
   else
-    inherited_props = NULL;
+    {
+      inherited_props = NULL;
+    }
 
   if (get_explicit_props)
     {
@@ -1431,9 +1439,15 @@ get_local_props(const char *path_or_url,
   if (get_target_inherited_props)
     {
       apr_array_header_t *iprops;
+      const char *repos_root_url;
 
       SVN_ERR(svn_wc__get_iprops(&iprops, ctx->wc_ctx, local_abspath,
                                  NULL, scratch_pool, scratch_pool));
+      SVN_ERR(svn_client_get_repos_root(&repos_root_url, NULL, local_abspath,
+                                        ctx, scratch_pool, scratch_pool));
+      SVN_ERR(svn_client__iprop_relpaths_to_urls(iprops, repos_root_url,
+                                                 scratch_pool,
+                                                 scratch_pool));
       SVN_ERR(call_receiver(path_or_url, NULL, iprops, receiver,
                             receiver_baton, scratch_pool));
     }