[prev in list] [next in list] [prev in thread] [next in thread] 

List:       subversion-commits
Subject:    svn commit: r1829347 [2/7] - in /subversion/branches/better-pristines: ./ build/ build/ac-macros/ co
From:       brane () apache ! org
Date:       2018-04-17 9:19:06
Message-ID: 20180417091910.C2E253A0168 () svn01-us-west ! apache ! org
[Download RAW message or body]

Modified: subversion/branches/better-pristines/subversion/libsvn_client/diff.c
URL: http://svn.apache.org/viewvc/subversion/branches/better-pristines/subversion/libsvn_client/diff.c?rev=1829347&r1=1829346&r2=1829347&view=diff
 ==============================================================================
--- subversion/branches/better-pristines/subversion/libsvn_client/diff.c (original)
+++ subversion/branches/better-pristines/subversion/libsvn_client/diff.c Tue Apr 17 \
09:19:05 2018 @@ -66,6 +66,26 @@
                           _("Path '%s' must be an immediate child of " \
                             "the directory '%s'"), path, relative_to_dir)
 
+/* State provided by the diff drivers; used by the diff writer */
+typedef struct diff_driver_info_t
+{
+  /* The anchor to prefix before wc paths */
+  const char *anchor;
+
+   /* Relative path of ra session from repos_root_url */
+  const char *session_relpath;
+
+  svn_wc_context_t *wc_ctx;
+
+  /* The original targets passed to the diff command.  We may need
+     these to construct distinctive diff labels when comparing the
+     same relative path in the same revision, under different anchors
+     (for example, when comparing a trunk against a branch). */
+  const char *orig_path_1;
+  const char *orig_path_2;
+} diff_driver_info_t;
+
+
 /* Calculate the repository relative path of DIFF_RELPATH, using
  * SESSION_RELPATH and WC_CTX, and return the result in *REPOS_RELPATH.
  * ORIG_TARGET is the related original target passed to the diff command,
@@ -383,28 +403,33 @@ maybe_print_mode_change(svn_stream_t *os
 }
 
 /* Print a git diff header showing the OPERATION to the stream OS using
- * HEADER_ENCODING. Return suitable diff labels for the git diff in *LABEL1
- * and *LABEL2. REPOS_RELPATH1 and REPOS_RELPATH2 are relative to reposroot.
- * are the paths passed to the original diff command. REV1 and REV2 are
- * revisions being diffed. COPYFROM_PATH and COPYFROM_REV indicate where the
+ * HEADER_ENCODING.
+ *
+ * Return suitable diff labels for the git diff in *LABEL1 and *LABEL2.
+ *
+ * REV1 and REV2 are the revisions being diffed.
+ * COPYFROM_PATH and COPYFROM_REV indicate where the
  * diffed item was copied from.
  * Use SCRATCH_POOL for temporary allocations. */
 static svn_error_t *
 print_git_diff_header(svn_stream_t *os,
                       const char **label1, const char **label2,
                       svn_diff_operation_kind_t operation,
-                      const char *repos_relpath1,
-                      const char *repos_relpath2,
                       svn_revnum_t rev1,
                       svn_revnum_t rev2,
+                      const char *diff_relpath,
                       const char *copyfrom_path,
                       svn_revnum_t copyfrom_rev,
                       apr_hash_t *left_props,
                       apr_hash_t *right_props,
                       const char *git_index_shas,
                       const char *header_encoding,
+                      const diff_driver_info_t *ddi,
                       apr_pool_t *scratch_pool)
 {
+  const char *repos_relpath1;
+  const char *repos_relpath2;
+  const char *copyfrom_repos_relpath = NULL;
   svn_boolean_t exec_bit1 = (svn_prop_get_value(left_props,
                                                 SVN_PROP_EXECUTABLE) != NULL);
   svn_boolean_t exec_bit2 = (svn_prop_get_value(right_props,
@@ -414,6 +439,26 @@ print_git_diff_header(svn_stream_t *os,
   svn_boolean_t symlink_bit2 = (svn_prop_get_value(right_props,
                                                    SVN_PROP_SPECIAL) != NULL);
 
+  SVN_ERR(make_repos_relpath(&repos_relpath1, diff_relpath,
+                             ddi->orig_path_1,
+                             ddi->session_relpath,
+                             ddi->wc_ctx,
+                             ddi->anchor,
+                             scratch_pool, scratch_pool));
+  SVN_ERR(make_repos_relpath(&repos_relpath2, diff_relpath,
+                             ddi->orig_path_2,
+                             ddi->session_relpath,
+                             ddi->wc_ctx,
+                             ddi->anchor,
+                             scratch_pool, scratch_pool));
+  if (copyfrom_path)
+    SVN_ERR(make_repos_relpath(&copyfrom_repos_relpath, copyfrom_path,
+                               ddi->orig_path_2,
+                               ddi->session_relpath,
+                               ddi->wc_ctx,
+                               ddi->anchor,
+                               scratch_pool, scratch_pool));
+
   if (operation == svn_diff_op_deleted)
     {
       SVN_ERR(print_git_diff_header_deleted(os, header_encoding,
@@ -494,24 +539,20 @@ print_git_diff_header(svn_stream_t *os,
    ### FIXME needs proper docstring
 
    If USE_GIT_DIFF_FORMAT is TRUE, pring git diff headers, which always
-   show paths relative to the repository root. RA_SESSION and WC_CTX are
-   needed to normalize paths relative the repository root, and are ignored
-   if USE_GIT_DIFF_FORMAT is FALSE.
+   show paths relative to the repository root. DDI->session_relpath and
+   DDI->wc_ctx are needed to normalize paths relative the repository root,
+   and are ignored if USE_GIT_DIFF_FORMAT is FALSE.
 
    If @a pretty_print_mergeinfo is true, then describe 'svn:mergeinfo'
    property changes in a human-readable form that says what changes were
    merged or reverse merged; otherwise (or if the mergeinfo property values
    don't parse correctly) display them just like any other property.
-
-   ANCHOR is the local path where the diff editor is anchored. */
+ */
 static svn_error_t *
 display_prop_diffs(const apr_array_header_t *propchanges,
                    apr_hash_t *left_props,
                    apr_hash_t *right_props,
                    const char *diff_relpath,
-                   const char *anchor,
-                   const char *orig_path1,
-                   const char *orig_path2,
                    svn_revnum_t rev1,
                    svn_revnum_t rev2,
                    const char *encoding,
@@ -520,32 +561,27 @@ display_prop_diffs(const apr_array_heade
                    svn_boolean_t show_diff_header,
                    svn_boolean_t use_git_diff_format,
                    svn_boolean_t pretty_print_mergeinfo,
-                   const char *ra_session_relpath,
+                   const diff_driver_info_t *ddi,
                    svn_cancel_func_t cancel_func,
                    void *cancel_baton,
-                   svn_wc_context_t *wc_ctx,
                    apr_pool_t *scratch_pool)
 {
   const char *repos_relpath1 = NULL;
-  const char *repos_relpath2 = NULL;
   const char *index_path = diff_relpath;
-  const char *adjusted_path1 = orig_path1;
-  const char *adjusted_path2 = orig_path2;
+  const char *adjusted_path1 = ddi->orig_path_1;
+  const char *adjusted_path2 = ddi->orig_path_2;
 
   if (use_git_diff_format)
     {
-      SVN_ERR(make_repos_relpath(&repos_relpath1, diff_relpath, orig_path1,
-                                 ra_session_relpath, wc_ctx, anchor,
-                                 scratch_pool, scratch_pool));
-      SVN_ERR(make_repos_relpath(&repos_relpath2, diff_relpath, orig_path2,
-                                 ra_session_relpath, wc_ctx, anchor,
+      SVN_ERR(make_repos_relpath(&repos_relpath1, diff_relpath, ddi->orig_path_1,
+                                 ddi->session_relpath, ddi->wc_ctx, ddi->anchor,
                                  scratch_pool, scratch_pool));
     }
 
   /* If we're creating a diff on the wc root, path would be empty. */
   SVN_ERR(adjust_paths_for_diff_labels(&index_path, &adjusted_path1,
                                        &adjusted_path2,
-                                       relative_to_dir, anchor,
+                                       relative_to_dir, ddi->anchor,
                                        scratch_pool, scratch_pool));
 
   if (show_diff_header)
@@ -567,13 +603,12 @@ display_prop_diffs(const apr_array_heade
       if (use_git_diff_format)
         SVN_ERR(print_git_diff_header(outstream, &label1, &label2,
                                       svn_diff_op_modified,
-                                      repos_relpath1, repos_relpath2,
-                                      rev1, rev2, NULL,
-                                      SVN_INVALID_REVNUM,
-                                      left_props,
-                                      right_props,
+                                      rev1, rev2,
+                                      diff_relpath,
+                                      NULL, SVN_INVALID_REVNUM,
+                                      left_props, right_props,
                                       NULL,
-                                      encoding, scratch_pool));
+                                      encoding, ddi, scratch_pool));
 
       /* --- label1
        * +++ label2 */
@@ -605,24 +640,6 @@ display_prop_diffs(const apr_array_heade
 
 /*** Callbacks for 'svn diff', invoked by the repos-diff editor. ***/
 
-/* State provided by the diff drivers; used by the diff writer */
-typedef struct diff_driver_info_t
-{
-  /* The anchor to prefix before wc paths */
-  const char *anchor;
-
-   /* Relative path of ra session from repos_root_url */
-  const char *session_relpath;
-
-  /* The original targets passed to the diff command.  We may need
-     these to construct distinctive diff labels when comparing the
-     same relative path in the same revision, under different anchors
-     (for example, when comparing a trunk against a branch). */
-  const char *orig_path_1;
-  const char *orig_path_2;
-} diff_driver_info_t;
-
-
 /* Diff writer state */
 typedef struct diff_writer_info_t
 {
@@ -680,8 +697,6 @@ typedef struct diff_writer_info_t
   /* Empty files for creating diffs or NULL if not used yet */
   const char *empty_file;
 
-  svn_wc_context_t *wc_ctx;
-
   svn_cancel_func_t cancel_func;
   void *cancel_baton;
 
@@ -717,9 +732,6 @@ diff_props_changed(const char *diff_relp
        * dir_props_changed(). */
       SVN_ERR(display_prop_diffs(props, left_props, right_props,
                                  diff_relpath,
-                                 dwi->ddi.anchor,
-                                 dwi->ddi.orig_path_1,
-                                 dwi->ddi.orig_path_2,
                                  rev1,
                                  rev2,
                                  dwi->header_encoding,
@@ -728,10 +740,9 @@ diff_props_changed(const char *diff_relp
                                  show_diff_header,
                                  dwi->use_git_diff_format,
                                  dwi->pretty_print_mergeinfo,
-                                 dwi->ddi.session_relpath,
+                                 &dwi->ddi,
                                  dwi->cancel_func,
                                  dwi->cancel_baton,
-                                 dwi->wc_ctx,
                                  scratch_pool));
     }
 
@@ -795,9 +806,12 @@ transform_link_to_git(const char **new_t
 }
 
 /* Show differences between TMPFILE1 and TMPFILE2. DIFF_RELPATH, REV1, and
-   REV2 are used in the headers to indicate the file and revisions.  If either
-   MIMETYPE1 or MIMETYPE2 indicate binary content, don't show a diff,
-   but instead print a warning message.
+   REV2 are used in the headers to indicate the file and revisions.
+
+   If either side has an svn:mime-type property that indicates 'binary'
+   content, then if DWI->force_binary is set, attempt to produce the
+   diff in the usual way, otherwise produce a 'GIT binary diff' in git mode
+   or print a warning message in non-git mode.
 
    If FORCE_DIFF is TRUE, always write a diff, even for empty diffs.
 
@@ -893,40 +907,17 @@ diff_content_changed(svn_boolean_t *wrot
         {
           svn_stream_t *left_stream;
           svn_stream_t *right_stream;
-          const char *repos_relpath1;
-          const char *repos_relpath2;
-          const char *copyfrom_repos_relpath = NULL;
-
-          SVN_ERR(make_repos_relpath(&repos_relpath1, diff_relpath,
-                                      dwi->ddi.orig_path_1,
-                                      dwi->ddi.session_relpath,
-                                      dwi->wc_ctx,
-                                      dwi->ddi.anchor,
-                                      scratch_pool, scratch_pool));
-          SVN_ERR(make_repos_relpath(&repos_relpath2, diff_relpath,
-                                      dwi->ddi.orig_path_2,
-                                      dwi->ddi.session_relpath,
-                                      dwi->wc_ctx,
-                                      dwi->ddi.anchor,
-                                      scratch_pool, scratch_pool));
-          if (copyfrom_path)
-            SVN_ERR(make_repos_relpath(&copyfrom_repos_relpath, copyfrom_path,
-                                        dwi->ddi.orig_path_2,
-                                        dwi->ddi.session_relpath,
-                                        dwi->wc_ctx,
-                                        dwi->ddi.anchor,
-                                        scratch_pool, scratch_pool));
-          SVN_ERR(print_git_diff_header(outstream, &label1, &label2,
+
+          SVN_ERR(print_git_diff_header(outstream,
+                                        &label1, &label2,
                                         operation,
-                                        repos_relpath1, repos_relpath2,
                                         rev1, rev2,
-                                        copyfrom_repos_relpath,
-                                        copyfrom_rev,
-                                        left_props,
-                                        right_props,
+                                        diff_relpath,
+                                        copyfrom_path, copyfrom_rev,
+                                        left_props, right_props,
                                         index_shas,
                                         dwi->header_encoding,
-                                        scratch_pool));
+                                        &dwi->ddi, scratch_pool));
 
           SVN_ERR(svn_stream_open_readonly(&left_stream, tmpfile1,
                                            scratch_pool, scratch_pool));
@@ -1066,41 +1057,16 @@ diff_content_changed(svn_boolean_t *wrot
 
           if (dwi->use_git_diff_format)
             {
-              const char *repos_relpath1;
-              const char *repos_relpath2;
-              const char *copyfrom_repos_relpath = NULL;
-
-              SVN_ERR(make_repos_relpath(&repos_relpath1, diff_relpath,
-                                         dwi->ddi.orig_path_1,
-                                         dwi->ddi.session_relpath,
-                                         dwi->wc_ctx,
-                                         dwi->ddi.anchor,
-                                         scratch_pool, scratch_pool));
-              SVN_ERR(make_repos_relpath(&repos_relpath2, diff_relpath,
-                                         dwi->ddi.orig_path_2,
-                                         dwi->ddi.session_relpath,
-                                         dwi->wc_ctx,
-                                         dwi->ddi.anchor,
-                                         scratch_pool, scratch_pool));
-              if (copyfrom_path)
-                SVN_ERR(make_repos_relpath(&copyfrom_repos_relpath,
-                                           copyfrom_path,
-                                           dwi->ddi.orig_path_2,
-                                           dwi->ddi.session_relpath,
-                                           dwi->wc_ctx,
-                                           dwi->ddi.anchor,
-                                           scratch_pool, scratch_pool));
-              SVN_ERR(print_git_diff_header(outstream, &label1, &label2,
+              SVN_ERR(print_git_diff_header(outstream,
+                                            &label1, &label2,
                                             operation,
-                                            repos_relpath1, repos_relpath2,
                                             rev1, rev2,
-                                            copyfrom_repos_relpath,
-                                            copyfrom_rev,
-                                            left_props,
-                                            right_props,
+                                            diff_relpath,
+                                            copyfrom_path, copyfrom_rev,
+                                            left_props, right_props,
                                             index_shas,
                                             dwi->header_encoding,
-                                            scratch_pool));
+                                            &dwi->ddi, scratch_pool));
             }
 
           /* Output the actual diff */
@@ -1547,11 +1513,22 @@ check_diff_target_exists(const char *url
 
 /** Prepare a repos repos diff between PATH_OR_URL1 and
  * PATH_OR_URL2@PEG_REVISION, in the revision range REVISION1:REVISION2.
- * Return URLs and peg revisions in *URL1, *REV1 and in *URL2, *REV2.
- * Return suitable anchors in *ANCHOR1 and *ANCHOR2, and targets in
- * *TARGET1 and *TARGET2, based on *URL1 and *URL2.
- * Indicate the corresponding node kinds in *KIND1 and *KIND2, and verify
+ *
+ * Return the resolved URL and peg revision pairs in *URL1, *REV1 and in
+ * *URL2, *REV2.
+ *
+ * Return suitable anchor URL and target pairs in *ANCHOR1, *TARGET1 and
+ * in *ANCHOR2, *TARGET2, corresponding to *URL1 and *URL2.
+ *
+ * (The choice of anchor URLs here appears to be: start with *URL1, *URL2;
+ * then take the parent dir on both sides, unless either of *URL1 or *URL2
+ * is the repository root or the parent dir of *URL1 is unreadable.)
+ *
+ * Set *KIND1 and *KIND2 to the node kinds of *URL1 and *URL2, and verify
  * that at least one of the diff targets exists.
+ *
+ * Set *RA_SESSION to an RA session parented at the URL *ANCHOR1.
+ *
  * Use client context CTX. Do all allocations in POOL. */
 static svn_error_t *
 diff_prepare_repos_repos(const char **url1,
@@ -1823,6 +1800,16 @@ unsupported_diff_error(svn_error_t *chil
    PATH1 and PATH2 are both working copy paths.  REVISION1 and
    REVISION2 are their respective revisions.
 
+   For now, require PATH1=PATH2, REVISION1='base', REVISION2='working',
+   otherwise return an error.
+
+   Set *ROOT_RELPATH to "" if PATH1 is a WC root, else to basename(PATH1).
+   Set *ROOT_IS_DIR to TRUE if the working version of PATH1 is
+   a directory, else to FALSE.
+
+   If DDI is non-null: Set DDI->anchor to the parent of PATH1 if the working
+   version of PATH1 is a dir, else to PATH1; set DDI->orig_path* to PATH*.
+
    All other options are the same as those passed to svn_client_diff7(). */
 static svn_error_t *
 diff_wc_wc(const char **root_relpath,
@@ -1870,6 +1857,9 @@ diff_wc_wc(const char **root_relpath,
         ddi->anchor = svn_dirent_dirname(path1, scratch_pool);
       else
         ddi->anchor = path1;
+
+      ddi->orig_path_1 = path1;
+      ddi->orig_path_2 = path2;
     }
 
   SVN_ERR(svn_wc__diff7(root_relpath, root_is_dir,
@@ -1889,6 +1879,20 @@ diff_wc_wc(const char **root_relpath,
    and the actual two paths compared are determined by following copy
    history from PATH_OR_URL2.
 
+   If DDI is non-null: Set DDI->orig_path_* to the two diff target URLs as
+   resolved at the given revisions; set DDI->anchor to an anchor WC path
+   if either of PATH_OR_URL* is given as a WC path, else to null; set
+   DDI->session_relpath to the repository-relpath of the anchor URL for
+   DDI->orig_path_1.
+
+   (The choice of WC anchor implementated here for DDI->anchor appears to
+   be: choose PATH_OR_URL2 (if it's a WC path) or else PATH_OR_URL1 (if
+   it's a WC path); then take its parent dir unless both resolved URLs
+   refer to directories.)
+
+   (For the choice of URL anchor for DDI->session_relpath, see
+   diff_prepare_repos_repos().)
+
    All other options are the same as those passed to svn_client_diff7(). */
 static svn_error_t *
 diff_repos_repos(const char **root_relpath,
@@ -2059,6 +2063,11 @@ diff_repos_repos(const char **root_relpa
 
    If REVERSE is TRUE, the diff will be reported in reverse.
 
+   If DDI is non-null: Set DDI->orig_path_* to the URLs of the two diff
+   targets as resolved at the given revisions; set DDI->anchor to a WC path
+   anchor for PATH2; set DDI->session_relpath to the repository-relpath of
+   the URL of that same anchor WC path.
+
    All other options are the same as those passed to svn_client_diff7(). */
 static svn_error_t *
 diff_repos_wc(const char **root_relpath,
@@ -2405,7 +2414,12 @@ do_diff(const char **root_relpath,
               SVN_ERR(svn_dirent_get_absolute(&abspath2, path_or_url2,
                                               scratch_pool));
 
-              /* ### What about ddi? */
+              if (ddi)
+                {
+                  ddi->orig_path_1 = path_or_url1;
+                  ddi->orig_path_2 = path_or_url2;
+                }
+
               /* Ignores changelists, ignore_ancestry */
               SVN_ERR(svn_client__arbitrary_nodes_diff(root_relpath, root_is_dir,
                                                        abspath1, abspath2,
@@ -2500,8 +2514,6 @@ static svn_error_t *
 get_diff_processor(svn_diff_tree_processor_t **diff_processor,
                    struct diff_driver_info_t **ddi,
                    const apr_array_header_t *options,
-                   const char *path_or_url1,
-                   const char *path_or_url2,
                    const char *relative_to_dir,
                    svn_boolean_t no_diff_added,
                    svn_boolean_t no_diff_deleted,
@@ -2521,8 +2533,6 @@ get_diff_processor(svn_diff_tree_process
   svn_diff_tree_processor_t *processor;
 
   /* setup callback and baton */
-  dwi->ddi.orig_path_1 = path_or_url1;
-  dwi->ddi.orig_path_2 = path_or_url2;
 
   SVN_ERR(create_diff_writer_info(dwi, options,
                                   ctx->config, pool));
@@ -2544,7 +2554,7 @@ get_diff_processor(svn_diff_tree_process
   dwi->cancel_func = ctx->cancel_func;
   dwi->cancel_baton = ctx->cancel_baton;
 
-  dwi->wc_ctx = ctx->wc_ctx;
+  dwi->ddi.wc_ctx = ctx->wc_ctx;
   dwi->ddi.session_relpath = NULL;
   dwi->ddi.anchor = NULL;
 
@@ -2641,7 +2651,6 @@ svn_client_diff7(const apr_array_header_
 
   SVN_ERR(get_diff_processor(&diff_processor, &ddi,
                              options,
-                             path_or_url1, path_or_url2,
                              relative_to_dir,
                              no_diff_added,
                              no_diff_deleted,
@@ -2702,7 +2711,6 @@ svn_client_diff_peg7(const apr_array_hea
 
   SVN_ERR(get_diff_processor(&diff_processor, &ddi,
                              options,
-                             path_or_url, path_or_url,
                              relative_to_dir,
                              no_diff_added,
                              no_diff_deleted,

Modified: subversion/branches/better-pristines/subversion/libsvn_client/merge.c
URL: http://svn.apache.org/viewvc/subversion/branches/better-pristines/subversion/libsvn_client/merge.c?rev=1829347&r1=1829346&r2=1829347&view=diff
 ==============================================================================
--- subversion/branches/better-pristines/subversion/libsvn_client/merge.c (original)
+++ subversion/branches/better-pristines/subversion/libsvn_client/merge.c Tue Apr 17 \
09:19:05 2018 @@ -242,7 +242,7 @@ typedef struct merge_cmd_baton_t {
 
   /* Rangelist containing single range which describes the gap, if any,
      in the natural history of the merge source currently being processed.
-     See http://subversion.tigris.org/issues/show_bug.cgi?id=3432.
+     See https://issues.apache.org/jira/browse/SVN-3432.
      Updated during each call to do_directory_merge().  May be NULL if there
      is no gap. */
   svn_rangelist_t *implicit_src_gap;
@@ -3621,7 +3621,7 @@ notify_merge_completed(const char *targe
 
 
 /* Remove merge source gaps from range used for merge notifications.
-   See http://subversion.tigris.org/issues/show_bug.cgi?id=4138
+   See https://issues.apache.org/jira/browse/SVN-4138
 
    If IMPLICIT_SRC_GAP is not NULL then it is a rangelist containing a
    single range (see the implicit_src_gap member of merge_cmd_baton_t).
@@ -5445,7 +5445,7 @@ record_skips_in_mergeinfo(const char *me
          ### TODO: An empty range is fine if the skipped path doesn't
          ### inherit any mergeinfo from a parent, but if it does
          ### we need to account for that.  See issue #3440
-         ### http://subversion.tigris.org/issues/show_bug.cgi?id=3440. */
+         ### https://issues.apache.org/jira/browse/SVN-3440. */
       svn_hash_sets(merges, skipped_abspath,
                     apr_array_make(scratch_pool, 0,
                                    sizeof(svn_merge_range_t *)));
@@ -7878,7 +7878,7 @@ process_children_with_new_mergeinfo(merg
          was added (with preexisting mergeinfo) by the merge.  That's actually
          more correct, since the inherited mergeinfo likely describes
          non-existent or unrelated merge history, but it's not quite so simple
-         as that, see http://subversion.tigris.org/issues/show_bug.cgi?id=4309
+         as that, see https://issues.apache.org/jira/browse/SVN-4309
          */
 
       /* Get the path's new explicit mergeinfo... */

Modified: subversion/branches/better-pristines/subversion/libsvn_client/patch.c
URL: http://svn.apache.org/viewvc/subversion/branches/better-pristines/subversion/libsvn_client/patch.c?rev=1829347&r1=1829346&r2=1829347&view=diff
 ==============================================================================
--- subversion/branches/better-pristines/subversion/libsvn_client/patch.c (original)
+++ subversion/branches/better-pristines/subversion/libsvn_client/patch.c Tue Apr 17 \
09:19:05 2018 @@ -343,7 +343,9 @@ strip_path(const char **result, const ch
   components = svn_path_decompose(path, scratch_pool);
   if (strip_count > components->nelts)
     return svn_error_createf(SVN_ERR_CLIENT_PATCH_BAD_STRIP_COUNT, NULL,
-                             _("Cannot strip %u components from '%s'"),
+                             Q_("Cannot strip %u component from '%s'",
+                                "Cannot strip %u components from '%s'",
+                                strip_count),
                              strip_count,
                              svn_dirent_local_style(path, scratch_pool));
 

Modified: subversion/branches/better-pristines/subversion/libsvn_client/repos_diff.c
URL: http://svn.apache.org/viewvc/subversion/branches/better-pristines/subversion/libsvn_client/repos_diff.c?rev=1829347&r1=1829346&r2=1829347&view=diff
 ==============================================================================
--- subversion/branches/better-pristines/subversion/libsvn_client/repos_diff.c \
                (original)
+++ subversion/branches/better-pristines/subversion/libsvn_client/repos_diff.c Tue \
Apr 17 09:19:05 2018 @@ -380,7 +380,7 @@ get_file_from_ra(struct file_baton *fb,
      way.  Hence this little hack:  We populate FILE_BATON->PROPCHANGES only
      with *actual* property changes.
 
-     See http://subversion.tigris.org/issues/show_bug.cgi?id=3657#desc9 and
+     See https://issues.apache.org/jira/browse/SVN-3657#desc9 and
      http://svn.haxx.se/dev/archive-2010-08/0351.shtml for more details.
  */
 static void

Modified: subversion/branches/better-pristines/subversion/libsvn_client/shelf.c
URL: http://svn.apache.org/viewvc/subversion/branches/better-pristines/subversion/libsvn_client/shelf.c?rev=1829347&r1=1829346&r2=1829347&view=diff
 ==============================================================================
--- subversion/branches/better-pristines/subversion/libsvn_client/shelf.c (original)
+++ subversion/branches/better-pristines/subversion/libsvn_client/shelf.c Tue Apr 17 \
09:19:05 2018 @@ -59,7 +59,7 @@ shelf_name_encode(char **encoded_name_p,
 
   while (*name)
     {
-      apr_snprintf(out_pos, 3, "%02x", *name++);
+      apr_snprintf(out_pos, 3, "%02x", (unsigned char)(*name++));
       out_pos += 2;
     }
   *encoded_name_p = encoded_name;
@@ -187,7 +187,9 @@ get_log_abspath(char **log_abspath,
   return SVN_NO_ERROR;
 }
 
-/* Set SHELF->revprops by reading from its file storage.
+/* Set SHELF->revprops by reading from its storage (the '.log' file).
+ * Set SHELF->revprops to empty if the storage file does not exist; this
+ * is not an error.
  */
 static svn_error_t *
 shelf_read_revprops(svn_client_shelf_t *shelf,
@@ -295,7 +297,9 @@ get_current_abspath(char **current_abspa
   return SVN_NO_ERROR;
 }
 
-/*  */
+/* Read SHELF->max_version from its storage (the '.current' file).
+ * Set SHELF->max_version to -1 if that file does not exist.
+ */
 static svn_error_t *
 shelf_read_current(svn_client_shelf_t *shelf,
                    apr_pool_t *scratch_pool)
@@ -307,7 +311,7 @@ shelf_read_current(svn_client_shelf_t *s
   fp = fopen(current_abspath, "r");
   if (! fp)
     {
-      shelf->max_version = 0;
+      shelf->max_version = -1;
       return SVN_NO_ERROR;
     }
   fscanf(fp, "%d", &shelf->max_version);
@@ -330,6 +334,186 @@ shelf_write_current(svn_client_shelf_t *
   return SVN_NO_ERROR;
 }
 
+/* A baton for use with walk_callback(). */
+typedef struct walk_baton_t {
+  apr_hash_t *changelist_hash;
+  const char *wc_root_abspath;
+  svn_stream_t *outstream;
+  svn_stream_t *errstream;
+  svn_client_ctx_t *ctx;
+  svn_boolean_t any_shelved;  /* were any paths successfully shelved? */
+  apr_array_header_t *unshelvable;  /* paths unshelvable */
+  apr_pool_t *pool;  /* pool for data in 'unshelvable', etc. */
+} walk_baton_t;
+
+/*  */
+static svn_error_t *
+note_shelved(apr_array_header_t *shelved,
+             const char *relpath,
+             apr_pool_t *pool)
+{
+  APR_ARRAY_PUSH(shelved, const char *) = apr_pstrdup(pool, relpath);
+  return SVN_NO_ERROR;
+}
+
+/* Set *IS_BINARY to true iff the pristine or working version of
+ * LOCAL_ABSPATH has a MIME-type that we regard as 'binary'.
+ */
+static svn_error_t *
+is_binary_file(svn_boolean_t *is_binary,
+               const char *local_abspath,
+               svn_client_ctx_t *ctx,
+               apr_pool_t *scratch_pool)
+{
+  apr_hash_t *props;
+  const svn_string_t *value;
+
+  SVN_ERR(svn_wc_get_pristine_props(&props, ctx->wc_ctx,
+                                    local_abspath,
+                                    scratch_pool, scratch_pool));
+  value = props ? svn_hash_gets(props, SVN_PROP_MIME_TYPE)
+                : NULL;
+  *is_binary = value && svn_mime_type_is_binary(value->data);
+
+  SVN_ERR(svn_wc_prop_get2(&value, ctx->wc_ctx, local_abspath,
+                           SVN_PROP_MIME_TYPE,
+                           scratch_pool, scratch_pool));
+  if (value && svn_mime_type_is_binary(value->data))
+    *is_binary = TRUE;
+
+  return SVN_NO_ERROR;
+}
+
+/* An implementation of svn_wc_status_func4_t. */
+static svn_error_t *
+walk_callback(void *baton,
+              const char *local_abspath,
+              const svn_wc_status3_t *status,
+              apr_pool_t *scratch_pool)
+{
+  walk_baton_t *wb = baton;
+  svn_opt_revision_t peg_revision = {svn_opt_revision_unspecified, {0}};
+  svn_opt_revision_t start_revision = {svn_opt_revision_base, {0}};
+  svn_opt_revision_t end_revision = {svn_opt_revision_working, {0}};
+  const char *wc_relpath = svn_dirent_skip_ancestor(wb->wc_root_abspath,
+                                                    local_abspath);
+
+  /* If the status item has an entry, but doesn't belong to one of the
+     changelists our caller is interested in, we filter out this status
+     transmission.  */
+  if (wb->changelist_hash
+      && (! status->changelist
+          || ! svn_hash_gets(wb->changelist_hash, status->changelist)))
+    {
+      return SVN_NO_ERROR;
+    }
+
+  switch (status->node_status)
+    {
+      case svn_wc_status_modified:
+      case svn_wc_status_deleted:
+      case svn_wc_status_added:
+      case svn_wc_status_replaced:
+      {
+        svn_boolean_t binary = FALSE;
+        if (status->kind == svn_node_file)
+          {
+            SVN_ERR(is_binary_file(&binary, local_abspath,
+                                   wb->ctx, scratch_pool));
+          }
+        /* For binary files, use git diff binary literal format.
+           This works for a stop-gap, but is inefficient for large files. */
+        SVN_ERR(svn_client_diff_peg7(NULL /*options*/,
+                                     local_abspath,
+                                     &peg_revision,
+                                     &start_revision,
+                                     &end_revision,
+                                     wb->wc_root_abspath,
+                                     svn_depth_empty,
+                                     TRUE /*notice_ancestry*/,
+                                     FALSE /*no_diff_added*/,
+                                     FALSE /*no_diff_deleted*/,
+                                     TRUE /*show_copies_as_adds*/,
+                                     FALSE /*ignore_content_type: FALSE -> omit \
binary files*/, +                                     FALSE /*ignore_properties*/,
+                                     FALSE /*properties_only*/,
+                                     binary /*use_git_diff_format*/,
+                                     FALSE /*pretty_print_mergeinfo*/,
+                                     SVN_APR_LOCALE_CHARSET,
+                                     wb->outstream,
+                                     wb->errstream,
+                                     NULL /*changelists*/,
+                                     wb->ctx, scratch_pool));
+        wb->any_shelved = TRUE;
+        break;
+      }
+
+      case svn_wc_status_incomplete:
+        if ((status->text_status != svn_wc_status_normal
+             && status->text_status != svn_wc_status_none)
+            || (status->prop_status != svn_wc_status_normal
+                && status->prop_status != svn_wc_status_none))
+          {
+            /* Incomplete, but local modifications */
+            SVN_ERR(note_shelved(wb->unshelvable, wc_relpath, wb->pool));
+          }
+        break;
+
+      case svn_wc_status_conflicted:
+      case svn_wc_status_missing:
+      case svn_wc_status_obstructed:
+        SVN_ERR(note_shelved(wb->unshelvable, wc_relpath, wb->pool));
+        break;
+
+      case svn_wc_status_normal:
+      case svn_wc_status_ignored:
+      case svn_wc_status_none:
+      case svn_wc_status_external:
+      case svn_wc_status_unversioned:
+      default:
+        break;
+    }
+
+  return SVN_NO_ERROR;
+}
+
+/*
+ * Walk the tree rooted at PATHS, to depth DEPTH.
+ *
+ * PATHS are absolute, or relative to CWD.
+ */
+static svn_error_t *
+wc_walk_status_multi(const apr_array_header_t *paths,
+                     svn_depth_t depth,
+                     /*const apr_array_header_t *changelists,*/
+                     svn_wc_status_func4_t status_func,
+                     void *status_baton,
+                     svn_client_ctx_t *ctx,
+                     apr_pool_t *scratch_pool)
+{
+  int i;
+
+  for (i = 0; i < paths->nelts; i++)
+    {
+      const char *path = APR_ARRAY_IDX(paths, i, const char *);
+
+      if (svn_path_is_url(path))
+        return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+                                 _("'%s' is not a local path"), path);
+      SVN_ERR(svn_dirent_get_absolute(&path, path, scratch_pool));
+
+      SVN_ERR(svn_wc_walk_status(ctx->wc_ctx, path, depth,
+                                 FALSE /*get_all*/, FALSE /*no_ignore*/,
+                                 FALSE /*ignore_text_mods*/,
+                                 NULL /*ignore_patterns*/,
+                                 status_func, status_baton,
+                                 ctx->cancel_func, ctx->cancel_baton,
+                                 scratch_pool));
+    }
+
+  return SVN_NO_ERROR;
+}
+
 /** Write local changes to a patch file.
  *
  * @a paths, @a depth, @a changelists: The selection of local paths to diff.
@@ -341,23 +525,30 @@ shelf_write_current(svn_client_shelf_t *
  *     This might also solve the buffering problem.
  */
 static svn_error_t *
-write_patch(const char *patch_abspath,
+write_patch(svn_boolean_t *any_shelved,
+            apr_array_header_t **unshelvable,
+            const char *patch_abspath,
             const apr_array_header_t *paths,
             svn_depth_t depth,
             const apr_array_header_t *changelists,
             const char *wc_root_abspath,
             svn_client_ctx_t *ctx,
+            apr_pool_t *result_pool,
             apr_pool_t *scratch_pool)
 {
+  walk_baton_t walk_baton = { 0 };
   apr_int32_t flag;
   apr_file_t *outfile;
-  svn_stream_t *outstream;
-  svn_stream_t *errstream;
-  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
-  int i;
-  svn_opt_revision_t peg_revision = {svn_opt_revision_unspecified, {0}};
-  svn_opt_revision_t start_revision = {svn_opt_revision_base, {0}};
-  svn_opt_revision_t end_revision = {svn_opt_revision_working, {0}};
+
+  if (changelists && changelists->nelts)
+    SVN_ERR(svn_hash_from_cstring_keys(&walk_baton.changelist_hash,
+                                       changelists, scratch_pool));
+
+  walk_baton.wc_root_abspath = wc_root_abspath;
+  walk_baton.ctx = ctx;
+  walk_baton.any_shelved = FALSE;
+  walk_baton.unshelvable = apr_array_make(result_pool, 0, sizeof(char *));
+  walk_baton.pool = result_pool;
 
   /* Get streams for the output and any error output of the diff. */
   /* ### svn_stream_open_writable() doesn't work here: the buffering
@@ -366,44 +557,20 @@ write_patch(const char *patch_abspath,
   flag = APR_FOPEN_WRITE | APR_FOPEN_CREATE | APR_FOPEN_TRUNCATE;
   SVN_ERR(svn_io_file_open(&outfile, patch_abspath,
                            flag, APR_FPROT_OS_DEFAULT, scratch_pool));
-  outstream = svn_stream_from_aprfile2(outfile, FALSE /*disown*/, scratch_pool);
-  errstream = svn_stream_empty(scratch_pool);
-
-  for (i = 0; i < paths->nelts; i++)
-    {
-      const char *path = APR_ARRAY_IDX(paths, i, const char *);
+  walk_baton.outstream = svn_stream_from_aprfile2(outfile, FALSE /*disown*/,
+                                                  scratch_pool);
+  walk_baton.errstream = svn_stream_empty(scratch_pool);
+
+  /* Walk the WC */
+  SVN_ERR(wc_walk_status_multi(paths, depth, /*changelists,*/
+                               walk_callback, &walk_baton,
+                               ctx, scratch_pool));
 
-      if (svn_path_is_url(path))
-        return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
-                                 _("'%s' is not a local path"), path);
-      SVN_ERR(svn_dirent_get_absolute(&path, path, scratch_pool));
-
-      SVN_ERR(svn_client_diff_peg7(
-                     NULL /*options*/,
-                     path,
-                     &peg_revision,
-                     &start_revision,
-                     &end_revision,
-                     wc_root_abspath,
-                     depth,
-                     TRUE /*notice_ancestry*/,
-                     FALSE /*no_diff_added*/,
-                     FALSE /*no_diff_deleted*/,
-                     TRUE /*show_copies_as_adds*/,
-                     FALSE /*ignore_content_type: FALSE -> omit binary files*/,
-                     FALSE /*ignore_properties*/,
-                     FALSE /*properties_only*/,
-                     FALSE /*use_git_diff_format*/,
-                     FALSE /*pretty_print_mergeinfo*/,
-                     SVN_APR_LOCALE_CHARSET,
-                     outstream,
-                     errstream,
-                     changelists,
-                     ctx, iterpool));
-    }
-  SVN_ERR(svn_stream_close(outstream));
-  SVN_ERR(svn_stream_close(errstream));
+  SVN_ERR(svn_stream_close(walk_baton.outstream));
+  SVN_ERR(svn_stream_close(walk_baton.errstream));
 
+  *any_shelved = walk_baton.any_shelved;
+  *unshelvable = walk_baton.unshelvable;
   return SVN_NO_ERROR;
 }
 
@@ -449,7 +616,7 @@ svn_client_shelf_open_existing(svn_clien
                           local_abspath, ctx, result_pool));
   SVN_ERR(shelf_read_revprops(*shelf_p, result_pool));
   SVN_ERR(shelf_read_current(*shelf_p, result_pool));
-  if ((*shelf_p)->max_version <= 0)
+  if ((*shelf_p)->max_version < 0)
     {
       return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
                                _("Shelf '%s' not found"),
@@ -465,10 +632,18 @@ svn_client_shelf_open_or_create(svn_clie
                                 svn_client_ctx_t *ctx,
                                 apr_pool_t *result_pool)
 {
-  SVN_ERR(shelf_construct(shelf_p, name,
+  svn_client_shelf_t *shelf;
+
+  SVN_ERR(shelf_construct(&shelf, name,
                           local_abspath, ctx, result_pool));
-  SVN_ERR(shelf_read_revprops(*shelf_p, result_pool));
-  SVN_ERR(shelf_read_current(*shelf_p, result_pool));
+  SVN_ERR(shelf_read_revprops(shelf, result_pool));
+  SVN_ERR(shelf_read_current(shelf, result_pool));
+  if (shelf->max_version < 0)
+    {
+      shelf->max_version = 0;
+      SVN_ERR(shelf_write_current(shelf, result_pool));
+    }
+  *shelf_p = shelf;
   return SVN_NO_ERROR;
 }
 
@@ -580,6 +755,82 @@ svn_client_shelf_paths_changed(apr_hash_
   return SVN_NO_ERROR;
 }
 
+/* A filter to only apply the patch to a particular file. */
+struct patch_filter_baton_t
+{
+  /* The single path to be selected for patching */
+  const char *path;
+};
+
+static svn_error_t *
+patch_filter(void *baton,
+             svn_boolean_t *filtered,
+             const char *canon_path_from_patchfile,
+             const char *patch_abspath,
+             const char *reject_abspath,
+             apr_pool_t *scratch_pool)
+{
+  struct patch_filter_baton_t *fb = baton;
+
+  *filtered = (strcmp(canon_path_from_patchfile, fb->path) != 0);
+  return SVN_NO_ERROR;
+}
+
+/* Intercept patch notifications to detect when there is a conflict */
+struct patch_notify_baton_t
+{
+  svn_boolean_t conflict;
+};
+
+/* Intercept patch notifications to detect when there is a conflict */
+static void
+patch_notify(void *baton,
+             const svn_wc_notify_t *notify,
+             apr_pool_t *pool)
+{
+  struct patch_notify_baton_t *nb = baton;
+
+  if (notify->action == svn_wc_notify_patch_rejected_hunk
+      || notify->action == svn_wc_notify_skip)
+    nb->conflict = TRUE;
+}
+
+svn_error_t *
+svn_client_shelf_test_apply_file(svn_boolean_t *conflict_p,
+                                 svn_client_shelf_version_t *shelf_version,
+                                 const char *file_relpath,
+                                 apr_pool_t *scratch_pool)
+{
+  svn_client_ctx_t *ctx = shelf_version->shelf->ctx;
+  svn_wc_notify_func2_t ctx_notify_func;
+  void *ctx_notify_baton;
+  struct patch_filter_baton_t fb;
+  struct patch_notify_baton_t nb;
+
+  fb.path = file_relpath;
+
+  nb.conflict = FALSE;
+  ctx_notify_func = ctx->notify_func2;
+  ctx_notify_baton = ctx->notify_baton2;
+  ctx->notify_func2 = patch_notify;
+  ctx->notify_baton2 = &nb;
+
+  SVN_ERR(svn_client_patch(shelf_version->patch_abspath,
+                           shelf_version->shelf->wc_root_abspath,
+                           TRUE /*dry_run*/, 0 /*strip*/,
+                           FALSE /*reverse*/,
+                           FALSE /*ignore_whitespace*/,
+                           TRUE /*remove_tempfiles*/,
+                           patch_filter, &fb,
+                           shelf_version->shelf->ctx, scratch_pool));
+
+  ctx->notify_func2 = ctx_notify_func;
+  ctx->notify_baton2 = ctx_notify_baton;
+
+  *conflict_p = nb.conflict;
+  return SVN_NO_ERROR;
+}
+
 svn_error_t *
 svn_client_shelf_apply(svn_client_shelf_version_t *shelf_version,
                        svn_boolean_t dry_run,
@@ -620,32 +871,38 @@ svn_client_shelf_unapply(svn_client_shel
 
 svn_error_t *
 svn_client_shelf_set_current_version(svn_client_shelf_t *shelf,
-                                     int version,
+                                     int version_number,
                                      apr_pool_t *scratch_pool)
 {
+  svn_client_shelf_version_t *shelf_version;
+
+  SVN_ERR(svn_client_shelf_version_open(&shelf_version, shelf, version_number,
+                                        scratch_pool, scratch_pool));
+  SVN_ERR(svn_client_shelf_delete_newer_versions(shelf, shelf_version,
+                                                 scratch_pool));
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client_shelf_delete_newer_versions(svn_client_shelf_t *shelf,
+                                       svn_client_shelf_version_t *shelf_version,
+                                       apr_pool_t *scratch_pool)
+{
+  int previous_version = shelf_version ? shelf_version->version_number : 0;
   int i;
 
   /* Delete any newer checkpoints */
-  for (i = shelf->max_version; i > version; i--)
+  for (i = shelf->max_version; i > previous_version; i--)
     {
       SVN_ERR(shelf_delete_patch_file(shelf, i, scratch_pool));
     }
 
-  shelf->max_version = version;
+  shelf->max_version = previous_version;
   SVN_ERR(shelf_write_current(shelf, scratch_pool));
   return SVN_NO_ERROR;
 }
 
 svn_error_t *
-svn_client_shelf_get_patch_abspath(const char **patch_abspath,
-                                   svn_client_shelf_version_t *shelf_version,
-                                   apr_pool_t *scratch_pool)
-{
-  *patch_abspath = shelf_version->patch_abspath;
-  return SVN_NO_ERROR;
-}
-
-svn_error_t *
 svn_client_shelf_export_patch(svn_client_shelf_version_t *shelf_version,
                               svn_stream_t *outstream,
                               apr_pool_t *scratch_pool)
@@ -661,33 +918,66 @@ svn_client_shelf_export_patch(svn_client
 }
 
 svn_error_t *
-svn_client_shelf_save_new_version(svn_client_shelf_t *shelf,
-                                  const apr_array_header_t *paths,
-                                  svn_depth_t depth,
-                                  const apr_array_header_t *changelists,
-                                  apr_pool_t *scratch_pool)
+svn_client_shelf_save_new_version2(svn_client_shelf_version_t **new_version_p,
+                                   svn_client_shelf_t *shelf,
+                                   const apr_array_header_t *paths,
+                                   svn_depth_t depth,
+                                   const apr_array_header_t *changelists,
+                                   apr_pool_t *scratch_pool)
 {
   int next_version = shelf->max_version + 1;
   const char *patch_abspath;
-  apr_finfo_t file_info;
+  svn_boolean_t any_shelved;
+  apr_array_header_t *unshelvable;
 
   SVN_ERR(get_patch_abspath(&patch_abspath, shelf, next_version,
                             scratch_pool, scratch_pool));
-  SVN_ERR(write_patch(patch_abspath,
+  SVN_ERR(write_patch(&any_shelved, &unshelvable,
+                      patch_abspath,
                       paths, depth, changelists,
                       shelf->wc_root_abspath,
-                      shelf->ctx, scratch_pool));
+                      shelf->ctx, scratch_pool, scratch_pool));
+
+  if (unshelvable->nelts > 0)
+    {
+      return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+                               Q_("%d path could not be shelved",
+                                  "%d paths could not be shelved",
+                                  unshelvable->nelts),
+                               unshelvable->nelts);
+    }
+
+  if (any_shelved)
+    {
+      shelf->max_version = next_version;
+      SVN_ERR(shelf_write_current(shelf, scratch_pool));
 
-  SVN_ERR(svn_io_stat(&file_info, patch_abspath, APR_FINFO_MTIME, scratch_pool));
-  if (file_info.size > 0)
+      if (new_version_p)
+        SVN_ERR(svn_client_shelf_version_open(new_version_p, shelf, next_version,
+                                              scratch_pool, scratch_pool));
+    }
+  else
     {
-      SVN_ERR(svn_client_shelf_set_current_version(shelf, next_version,
-                                                   scratch_pool));
+      if (new_version_p)
+        *new_version_p = NULL;
     }
   return SVN_NO_ERROR;
 }
 
 svn_error_t *
+svn_client_shelf_save_new_version(svn_client_shelf_t *shelf,
+                                  const apr_array_header_t *paths,
+                                  svn_depth_t depth,
+                                  const apr_array_header_t *changelists,
+                                  apr_pool_t *scratch_pool)
+{
+  SVN_ERR(svn_client_shelf_save_new_version2(NULL, shelf,
+                                             paths, depth, changelists,
+                                             scratch_pool));
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
 svn_client_shelf_get_log_message(char **log_message,
                                  svn_client_shelf_t *shelf,
                                  apr_pool_t *result_pool)
@@ -759,7 +1049,7 @@ svn_client_shelf_list(apr_hash_t **shelf
 svn_error_t *
 svn_client_shelf_version_open(svn_client_shelf_version_t **shelf_version_p,
                               svn_client_shelf_t *shelf,
-                              int version,
+                              int version_number,
                               apr_pool_t *result_pool,
                               apr_pool_t *scratch_pool)
 {
@@ -769,7 +1059,7 @@ svn_client_shelf_version_open(svn_client
 
   shelf_version->shelf = shelf;
   SVN_ERR(get_existing_patch_abspath(&shelf_version->patch_abspath,
-                                     shelf, version,
+                                     shelf, version_number,
                                      result_pool, scratch_pool));
   SVN_ERR(svn_io_stat_dirent2(&dirent,
                               shelf_version->patch_abspath,
@@ -777,6 +1067,48 @@ svn_client_shelf_version_open(svn_client
                               TRUE /*ignore_enoent*/,
                               result_pool, scratch_pool));
   shelf_version->mtime = dirent->mtime;
+  shelf_version->version_number = version_number;
   *shelf_version_p = shelf_version;
   return SVN_NO_ERROR;
 }
+
+svn_error_t *
+svn_client_shelf_get_newest_version(svn_client_shelf_version_t **shelf_version_p,
+                                    svn_client_shelf_t *shelf,
+                                    apr_pool_t *result_pool,
+                                    apr_pool_t *scratch_pool)
+{
+  if (shelf->max_version == 0)
+    {
+      *shelf_version_p = NULL;
+      return SVN_NO_ERROR;
+    }
+
+  SVN_ERR(svn_client_shelf_version_open(shelf_version_p,
+                                        shelf, shelf->max_version,
+                                        result_pool, scratch_pool));
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client_shelf_get_all_versions(apr_array_header_t **versions_p,
+                                  svn_client_shelf_t *shelf,
+                                  apr_pool_t *result_pool,
+                                  apr_pool_t *scratch_pool)
+{
+  int i;
+
+  *versions_p = apr_array_make(result_pool, shelf->max_version - 1,
+                               sizeof(svn_client_shelf_version_t *));
+
+  for (i = 1; i <= shelf->max_version; i++)
+    {
+      svn_client_shelf_version_t *shelf_version;
+
+      SVN_ERR(svn_client_shelf_version_open(&shelf_version,
+                                            shelf, i,
+                                            result_pool, scratch_pool));
+      APR_ARRAY_PUSH(*versions_p, svn_client_shelf_version_t *) = shelf_version;
+    }
+  return SVN_NO_ERROR;
+}

Modified: subversion/branches/better-pristines/subversion/libsvn_client/update.c
URL: http://svn.apache.org/viewvc/subversion/branches/better-pristines/subversion/libsvn_client/update.c?rev=1829347&r1=1829346&r2=1829347&view=diff
 ==============================================================================
--- subversion/branches/better-pristines/subversion/libsvn_client/update.c (original)
+++ subversion/branches/better-pristines/subversion/libsvn_client/update.c Tue Apr 17 \
09:19:05 2018 @@ -567,7 +567,7 @@ svn_client__update_internal(svn_revnum_t
 {
   const char *anchor_abspath, *lockroot_abspath;
   svn_error_t *err;
-  svn_opt_revision_t peg_revision = *revision;
+  svn_opt_revision_t opt_rev = *revision;  /* operative revision */
   apr_hash_t *conflicted_paths
     = ctx->conflict_func2 ? apr_hash_make(pool) : NULL;
 
@@ -620,7 +620,7 @@ svn_client__update_internal(svn_revnum_t
 
           err = update_internal(result_rev, timestamp_sleep, conflicted_paths,
                                 &ra_session, missing_parent,
-                                anchor_abspath, &peg_revision, svn_depth_empty,
+                                anchor_abspath, &opt_rev, svn_depth_empty,
                                 FALSE, ignore_externals,
                                 allow_unver_obstructions, adds_as_modification,
                                 FALSE, ctx, pool, iterpool);
@@ -631,8 +631,8 @@ svn_client__update_internal(svn_revnum_t
           /* If we successfully updated a missing parent, let's re-use
              the returned revision number for future updates for the
              sake of consistency. */
-          peg_revision.kind = svn_opt_revision_number;
-          peg_revision.value.number = *result_rev;
+          opt_rev.kind = svn_opt_revision_number;
+          opt_rev.value.number = *result_rev;
         }
 
       svn_pool_destroy(iterpool);
@@ -648,7 +648,7 @@ svn_client__update_internal(svn_revnum_t
   err = update_internal(result_rev, timestamp_sleep, conflicted_paths,
                         &ra_session,
                         local_abspath, anchor_abspath,
-                        &peg_revision, depth, depth_is_sticky,
+                        &opt_rev, depth, depth_is_sticky,
                         ignore_externals, allow_unver_obstructions,
                         adds_as_modification,
                         TRUE, ctx, pool, pool);

Modified: subversion/branches/better-pristines/subversion/libsvn_delta/svndiff.c
URL: http://svn.apache.org/viewvc/subversion/branches/better-pristines/subversion/libsvn_delta/svndiff.c?rev=1829347&r1=1829346&r2=1829347&view=diff
 ==============================================================================
--- subversion/branches/better-pristines/subversion/libsvn_delta/svndiff.c (original)
+++ subversion/branches/better-pristines/subversion/libsvn_delta/svndiff.c Tue Apr 17 \
09:19:05 2018 @@ -1026,11 +1026,11 @@ svndiff_stream_read_fn(void *baton, char
   apr_size_t left = *len;
   apr_size_t read = 0;
 
-  while (left && !b->hit_eof)
+  while (left)
     {
       apr_size_t chunk_size;
 
-      if (b->read_pos == b->window_buffer->len)
+      if (b->read_pos == b->window_buffer->len && !b->hit_eof)
         {
           svn_txdelta_window_t *window;
 
@@ -1050,6 +1050,9 @@ svndiff_stream_read_fn(void *baton, char
       else
         chunk_size = left;
 
+      if (!chunk_size)
+          break;
+
       memcpy(buffer, b->window_buffer->data + b->read_pos, chunk_size);
       b->read_pos += chunk_size;
       buffer += chunk_size;

Modified: subversion/branches/better-pristines/subversion/libsvn_fs_fs/cached_data.c
URL: http://svn.apache.org/viewvc/subversion/branches/better-pristines/subversion/libsvn_fs_fs/cached_data.c?rev=1829347&r1=1829346&r2=1829347&view=diff
 ==============================================================================
--- subversion/branches/better-pristines/subversion/libsvn_fs_fs/cached_data.c \
                (original)
+++ subversion/branches/better-pristines/subversion/libsvn_fs_fs/cached_data.c Tue \
Apr 17 09:19:05 2018 @@ -1268,7 +1268,7 @@ parse_raw_window(void **out,
   stream = svn_stream_from_string(&raw_window, result_pool);
 
   /* parse it */
-  SVN_ERR(svn_txdelta_read_svndiff_window(&result->window, stream, 1,
+  SVN_ERR(svn_txdelta_read_svndiff_window(&result->window, stream, window->ver,
                                           result_pool));
 
   /* complete the window and return it */
@@ -2103,13 +2103,14 @@ skip_contents(struct rep_read_baton *bat
 
 /* BATON is of type `rep_read_baton'; read the next *LEN bytes of the
    representation and store them in *BUF.  Sum as we read and verify
-   the MD5 sum at the end. */
+   the MD5 sum at the end.  This is a READ_FULL_FN for svn_stream_t. */
 static svn_error_t *
 rep_read_contents(void *baton,
                   char *buf,
                   apr_size_t *len)
 {
   struct rep_read_baton *rb = baton;
+  apr_size_t len_requested = *len;
 
   /* Get data from the fulltext cache for as long as we can. */
   if (rb->fulltext_cache)
@@ -2150,6 +2151,28 @@ rep_read_contents(void *baton,
   if (rb->current_fulltext)
     svn_stringbuf_appendbytes(rb->current_fulltext, buf, *len);
 
+  /* This is a FULL_READ_FN so a short read implies EOF and we can
+     verify the length. */
+  rb->off += *len;
+  if (*len < len_requested && rb->off != rb->len)
+      {
+        /* A warning rather than an error to allow the data to be
+           retrieved when the length is wrong but the data is
+           present, i.e. if repository corruption has stored the wrong
+           expanded length. */
+        svn_error_t *err = svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+                            _("Length mismatch while reading representation:"
+                              " expected %s,"
+                              " got %s"),
+                            apr_psprintf(rb->pool, "%" SVN_FILESIZE_T_FMT,
+                                         rb->len),
+                            apr_psprintf(rb->pool, "%" SVN_FILESIZE_T_FMT,
+                                         rb->off));
+
+        rb->fs->warning(rb->fs->warning_baton, err);
+        svn_error_clear(err);
+      }
+
   /* Perform checksumming.  We want to check the checksum as soon as
      the last byte of data is read, in case the caller never performs
      a short read, but we don't want to finalize the MD5 context
@@ -2157,7 +2180,6 @@ rep_read_contents(void *baton,
   if (!rb->checksum_finalized)
     {
       SVN_ERR(svn_checksum_update(rb->md5_checksum_ctx, buf, *len));
-      rb->off += *len;
       if (rb->off == rb->len)
         {
           svn_checksum_t *md5_checksum;
@@ -3206,7 +3228,7 @@ init_rep_state(rep_state_t *rs,
   rs->start = entry->offset + rs->header_size;
   rs->current = rep_header->type == svn_fs_fs__rep_plain ? 0 : 4;
   rs->size = entry->size - rep_header->header_size - 7;
-  rs->ver = 1;
+  rs->ver = -1;
   rs->chunk_index = 0;
   rs->raw_window_cache = ffd->raw_window_cache;
   rs->window_cache = ffd->txdelta_window_cache;
@@ -3264,6 +3286,9 @@ cache_windows(svn_fs_t *fs,
               apr_pool_t *pool)
 {
   apr_pool_t *iterpool = svn_pool_create(pool);
+
+  SVN_ERR(auto_read_diff_version(rs, iterpool));
+
   while (rs->current < rs->size)
     {
       apr_off_t end_offset;
@@ -3324,6 +3349,7 @@ cache_windows(svn_fs_t *fs,
           window.end_offset = rs->current;
           window.window.len = window_len;
           window.window.data = buf;
+          window.ver = rs->ver;
 
           /* cache the window now */
           SVN_ERR(svn_cache__set(rs->raw_window_cache, &key, &window,

Modified: subversion/branches/better-pristines/subversion/libsvn_fs_fs/dag.c
URL: http://svn.apache.org/viewvc/subversion/branches/better-pristines/subversion/libsvn_fs_fs/dag.c?rev=1829347&r1=1829346&r2=1829347&view=diff
 ==============================================================================
--- subversion/branches/better-pristines/subversion/libsvn_fs_fs/dag.c (original)
+++ subversion/branches/better-pristines/subversion/libsvn_fs_fs/dag.c Tue Apr 17 \
09:19:05 2018 @@ -1166,7 +1166,7 @@ svn_fs_fs__dag_serialize(void **data,
                                 (const void * const *)&node->node_pool);
 
   /* serialize other sub-structures */
-  svn_fs_fs__id_serialize(context, (const svn_fs_id_t **)&node->id);
+  svn_fs_fs__id_serialize(context, (const svn_fs_id_t *const *)&node->id);
   svn_fs_fs__id_serialize(context, &node->fresh_root_predecessor_id);
   svn_temp_serializer__add_string(context, &node->created_path);
 

Modified: subversion/branches/better-pristines/subversion/libsvn_fs_fs/index.c
URL: http://svn.apache.org/viewvc/subversion/branches/better-pristines/subversion/libsvn_fs_fs/index.c?rev=1829347&r1=1829346&r2=1829347&view=diff
 ==============================================================================
--- subversion/branches/better-pristines/subversion/libsvn_fs_fs/index.c (original)
+++ subversion/branches/better-pristines/subversion/libsvn_fs_fs/index.c Tue Apr 17 \
09:19:05 2018 @@ -3191,9 +3191,9 @@ compare_p2l_entry_revision(const void *l
                            const void *rhs)
 {
   const svn_fs_fs__p2l_entry_t *lhs_entry
-    =*(const svn_fs_fs__p2l_entry_t **)lhs;
+    =*(const svn_fs_fs__p2l_entry_t *const *)lhs;
   const svn_fs_fs__p2l_entry_t *rhs_entry
-    =*(const svn_fs_fs__p2l_entry_t **)rhs;
+    =*(const svn_fs_fs__p2l_entry_t *const *)rhs;
 
   if (lhs_entry->item.revision < rhs_entry->item.revision)
     return -1;

Modified: subversion/branches/better-pristines/subversion/libsvn_fs_fs/load-index.c
URL: http://svn.apache.org/viewvc/subversion/branches/better-pristines/subversion/libsvn_fs_fs/load-index.c?rev=1829347&r1=1829346&r2=1829347&view=diff
 ==============================================================================
--- subversion/branches/better-pristines/subversion/libsvn_fs_fs/load-index.c \
                (original)
+++ subversion/branches/better-pristines/subversion/libsvn_fs_fs/load-index.c Tue Apr \
17 09:19:05 2018 @@ -83,9 +83,9 @@ compare_p2l_entry_revision(const void *l
                            const void *rhs)
 {
   const svn_fs_fs__p2l_entry_t *lhs_entry
-    =*(const svn_fs_fs__p2l_entry_t **)lhs;
+    =*(const svn_fs_fs__p2l_entry_t *const *)lhs;
   const svn_fs_fs__p2l_entry_t *rhs_entry
-    =*(const svn_fs_fs__p2l_entry_t **)rhs;
+    =*(const svn_fs_fs__p2l_entry_t *const *)rhs;
 
   if (lhs_entry->offset < rhs_entry->offset)
     return -1;

Modified: subversion/branches/better-pristines/subversion/libsvn_fs_fs/temp_serializer.h
                
URL: http://svn.apache.org/viewvc/subversion/branches/better-pristines/subversion/libsvn_fs_fs/temp_serializer.h?rev=1829347&r1=1829346&r2=1829347&view=diff
 ==============================================================================
--- subversion/branches/better-pristines/subversion/libsvn_fs_fs/temp_serializer.h \
                (original)
+++ subversion/branches/better-pristines/subversion/libsvn_fs_fs/temp_serializer.h \
Tue Apr 17 09:19:05 2018 @@ -53,13 +53,16 @@ svn_fs_fs__noderev_deserialize(void *buf
 /**
  * Adds position information to the raw window data in WINDOW.
  */
-typedef struct
+typedef struct svn_fs_fs__raw_cached_window_t
 {
   /* the (unprocessed) txdelta window byte sequence cached / to be cached */
   svn_string_t window;
 
   /* the offset within the representation right after reading the window */
   apr_off_t end_offset;
+
+  /* svndiff version */
+  int ver;
 } svn_fs_fs__raw_cached_window_t;
 
 /**
@@ -86,7 +89,7 @@ svn_fs_fs__deserialize_raw_window(void *
  * #svn_txdelta_window_t is not sufficient for caching the data it
  * represents because data read process needs auxiliary information.
  */
-typedef struct
+typedef struct svn_fs_fs__txdelta_cached_window_t
 {
   /* the txdelta window information cached / to be cached */
   svn_txdelta_window_t *window;

Modified: subversion/branches/better-pristines/subversion/libsvn_ra_serf/commit.c
URL: http://svn.apache.org/viewvc/subversion/branches/better-pristines/subversion/libsvn_ra_serf/commit.c?rev=1829347&r1=1829346&r2=1829347&view=diff
 ==============================================================================
--- subversion/branches/better-pristines/subversion/libsvn_ra_serf/commit.c \
                (original)
+++ subversion/branches/better-pristines/subversion/libsvn_ra_serf/commit.c Tue Apr \
17 09:19:05 2018 @@ -311,7 +311,7 @@ checkout_node(const char **working_url,
    fails due to an SVN_ERR_APMOD_BAD_BASELINE error return from the
    server.
 
-   See http://subversion.tigris.org/issues/show_bug.cgi?id=4127 for
+   See https://issues.apache.org/jira/browse/SVN-4127 for
    details.
 */
 static svn_error_t *
@@ -677,7 +677,7 @@ write_prop_xml(const proppatch_context_t
    explicitly deleted in this commit already, then mod_dav removed its
    lock token when it fielded the DELETE request, so we don't want to
    set the lock precondition again.  (See
-   http://subversion.tigris.org/issues/show_bug.cgi?id=3674 for details.)
+   https://issues.apache.org/jira/browse/SVN-3674 for details.)
 */
 static svn_error_t *
 maybe_set_lock_token_header(serf_bucket_t *headers,

Modified: subversion/branches/better-pristines/subversion/libsvn_ra_serf/replay.c
URL: http://svn.apache.org/viewvc/subversion/branches/better-pristines/subversion/libsvn_ra_serf/replay.c?rev=1829347&r1=1829346&r2=1829347&view=diff
 ==============================================================================
--- subversion/branches/better-pristines/subversion/libsvn_ra_serf/replay.c \
                (original)
+++ subversion/branches/better-pristines/subversion/libsvn_ra_serf/replay.c Tue Apr \
17 09:19:05 2018 @@ -701,7 +701,7 @@ svn_ra_serf__replay_range(svn_ra_session
      wish for the best.
 
      See issue #4287:
-     http://subversion.tigris.org/issues/show_bug.cgi?id=4287
+     https://issues.apache.org/jira/browse/SVN-4287
   */
   if (session->supports_rev_rsrc_replay)
     {

Modified: subversion/branches/better-pristines/subversion/libsvn_ra_serf/update.c
URL: http://svn.apache.org/viewvc/subversion/branches/better-pristines/subversion/libsvn_ra_serf/update.c?rev=1829347&r1=1829346&r2=1829347&view=diff
 ==============================================================================
--- subversion/branches/better-pristines/subversion/libsvn_ra_serf/update.c \
                (original)
+++ subversion/branches/better-pristines/subversion/libsvn_ra_serf/update.c Tue Apr \
17 09:19:05 2018 @@ -603,7 +603,7 @@ get_best_connection(report_context_t *ct
      ### simply can't handle the way ra_serf violates the editor v1
      ### drive ordering requirements.
      ###
-     ### See http://subversion.tigris.org/issues/show_bug.cgi?id=4116.
+     ### See https://issues.apache.org/jira/browse/SVN-4116.
   */
   if (ctx->report_received && (ctx->sess->max_connections > 2))
     first_conn = 0;

Modified: subversion/branches/better-pristines/subversion/libsvn_repos/load-fs-vtable.c
                
URL: http://svn.apache.org/viewvc/subversion/branches/better-pristines/subversion/libsvn_repos/load-fs-vtable.c?rev=1829347&r1=1829346&r2=1829347&view=diff
 ==============================================================================
--- subversion/branches/better-pristines/subversion/libsvn_repos/load-fs-vtable.c \
                (original)
+++ subversion/branches/better-pristines/subversion/libsvn_repos/load-fs-vtable.c Tue \
Apr 17 09:19:05 2018 @@ -75,7 +75,7 @@ struct parse_baton
      (svn_revnum_t *) in the dump stream to their corresponding revisions
      (svn_revnum_t *) in the loaded repository.  The hash and its
      contents are allocated in POOL. */
-  /* ### See http://subversion.tigris.org/issues/show_bug.cgi?id=3903
+  /* ### See https://issues.apache.org/jira/browse/SVN-3903
      ### for discussion about improving the memory costs of this mapping. */
   apr_hash_t *rev_map;
 
@@ -253,7 +253,7 @@ renumber_mergeinfo_revs(svn_string_t **f
   SVN_ERR(svn_mergeinfo_parse(&mergeinfo, initial_val->data, subpool));
 
   /* Issue #3020
-     http://subversion.tigris.org/issues/show_bug.cgi?id=3020#desc16
+     https://issues.apache.org/jira/browse/SVN-3020#desc16
      Remove mergeinfo older than the oldest revision in the dump stream
      and adjust its revisions by the difference between the head rev of
      the target repository and the current dump stream rev. */
@@ -323,7 +323,7 @@ renumber_mergeinfo_revs(svn_string_t **f
                  mergeinfo with a start rev > end rev.  If that gets into the
                  repository then a world of bustage breaks loose anytime that
                  bogus mergeinfo is parsed.  See
-                 http://subversion.tigris.org/issues/show_bug.cgi?id=3020#desc16.
+                 https://issues.apache.org/jira/browse/SVN-3020#desc16.
                  */
               continue;
             }

Modified: subversion/branches/better-pristines/subversion/libsvn_repos/log.c
URL: http://svn.apache.org/viewvc/subversion/branches/better-pristines/subversion/libsvn_repos/log.c?rev=1829347&r1=1829346&r2=1829347&view=diff
 ==============================================================================
--- subversion/branches/better-pristines/subversion/libsvn_repos/log.c (original)
+++ subversion/branches/better-pristines/subversion/libsvn_repos/log.c Tue Apr 17 \
09:19:05 2018 @@ -2447,7 +2447,7 @@ svn_repos_get_logs5(svn_repos_t *repos,
      represents all of PATHS' history between START and END.  We will use
      this later to squelch duplicate log revisions that might exist in
      both natural history and merged-in history.  See
-     http://subversion.tigris.org/issues/show_bug.cgi?id=3650#desc5 */
+     https://issues.apache.org/jira/browse/SVN-3650#desc5 */
   if (include_merged_revisions)
     {
       apr_pool_t *subpool = svn_pool_create(scratch_pool);

Modified: subversion/branches/better-pristines/subversion/libsvn_repos/repos.c
URL: http://svn.apache.org/viewvc/subversion/branches/better-pristines/subversion/libsvn_repos/repos.c?rev=1829347&r1=1829346&r2=1829347&view=diff
 ==============================================================================
--- subversion/branches/better-pristines/subversion/libsvn_repos/repos.c (original)
+++ subversion/branches/better-pristines/subversion/libsvn_repos/repos.c Tue Apr 17 \
09:19:05 2018 @@ -1721,7 +1721,7 @@ svn_repos_recover4(const char *path,
 }
 
 struct freeze_baton_t {
-  apr_array_header_t *paths;
+  const apr_array_header_t *paths;
   int counter;
   svn_repos_freeze_func_t freeze_func;
   void *freeze_baton;
@@ -1788,7 +1788,7 @@ multi_freeze(void *baton,
    and an SQLite reserved lock which means the repository is readable
    while frozen. */
 svn_error_t *
-svn_repos_freeze(apr_array_header_t *paths,
+svn_repos_freeze(const apr_array_header_t *paths,
                  svn_repos_freeze_func_t freeze_func,
                  void *freeze_baton,
                  apr_pool_t *pool)

Modified: subversion/branches/better-pristines/subversion/libsvn_subr/config_win.c
URL: http://svn.apache.org/viewvc/subversion/branches/better-pristines/subversion/libsvn_subr/config_win.c?rev=1829347&r1=1829346&r2=1829347&view=diff
 ==============================================================================
--- subversion/branches/better-pristines/subversion/libsvn_subr/config_win.c \
                (original)
+++ subversion/branches/better-pristines/subversion/libsvn_subr/config_win.c Tue Apr \
17 09:19:05 2018 @@ -137,7 +137,7 @@ parse_section(svn_config_t *cfg, HKEY hk
                                 _("Can't enumerate registry values"));
 
       /* Ignore option names that start with '#', see
-         http://subversion.tigris.org/issues/show_bug.cgi?id=671 */
+         https://issues.apache.org/jira/browse/SVN-671 */
       if (type == REG_SZ && option->data[0] != '#')
         {
           DWORD value_len = (DWORD)value->blocksize;

Modified: subversion/branches/better-pristines/subversion/libsvn_subr/deprecated.c
URL: http://svn.apache.org/viewvc/subversion/branches/better-pristines/subversion/libsvn_subr/deprecated.c?rev=1829347&r1=1829346&r2=1829347&view=diff
 ==============================================================================
--- subversion/branches/better-pristines/subversion/libsvn_subr/deprecated.c \
                (original)
+++ subversion/branches/better-pristines/subversion/libsvn_subr/deprecated.c Tue Apr \
17 09:19:05 2018 @@ -390,6 +390,30 @@ print_command_info(const svn_opt_subcomm
   return SVN_NO_ERROR;
 }
 
+const svn_opt_subcommand_desc2_t *
+svn_opt_get_canonical_subcommand2(const svn_opt_subcommand_desc2_t *table,
+                                  const char *cmd_name)
+{
+  int i = 0;
+
+  if (cmd_name == NULL)
+    return NULL;
+
+  while (table[i].name) {
+    int j;
+    if (strcmp(cmd_name, table[i].name) == 0)
+      return table + i;
+    for (j = 0; (j < SVN_OPT_MAX_ALIASES) && table[i].aliases[j]; j++)
+      if (strcmp(cmd_name, table[i].aliases[j]) == 0)
+        return table + i;
+
+    i++;
+  }
+
+  /* If we get here, there was no matching subcommand name or alias. */
+  return NULL;
+}
+
 const svn_opt_subcommand_desc_t *
 svn_opt_get_canonical_subcommand(const svn_opt_subcommand_desc_t *table,
                                  const char *cmd_name)
@@ -414,6 +438,344 @@ svn_opt_get_canonical_subcommand(const s
   return NULL;
 }
 
+const apr_getopt_option_t *
+svn_opt_get_option_from_code2(int code,
+                              const apr_getopt_option_t *option_table,
+                              const svn_opt_subcommand_desc2_t *command,
+                              apr_pool_t *pool)
+{
+  apr_size_t i;
+
+  for (i = 0; option_table[i].optch; i++)
+    if (option_table[i].optch == code)
+      {
+        if (command)
+          {
+            int j;
+
+            for (j = 0; ((j < SVN_OPT_MAX_OPTIONS) &&
+                         command->desc_overrides[j].optch); j++)
+              if (command->desc_overrides[j].optch == code)
+                {
+                  apr_getopt_option_t *tmpopt =
+                      apr_palloc(pool, sizeof(*tmpopt));
+                  *tmpopt = option_table[i];
+                  tmpopt->description = command->desc_overrides[j].desc;
+                  return tmpopt;
+                }
+          }
+        return &(option_table[i]);
+      }
+
+  return NULL;
+}
+
+const apr_getopt_option_t *
+svn_opt_get_option_from_code(int code,
+                             const apr_getopt_option_t *option_table)
+{
+  apr_size_t i;
+
+  for (i = 0; option_table[i].optch; i++)
+    if (option_table[i].optch == code)
+      return &(option_table[i]);
+
+  return NULL;
+}
+
+/* Like svn_opt_get_option_from_code2(), but also, if CODE appears a second
+ * time in OPTION_TABLE with a different name, then set *LONG_ALIAS to that
+ * second name, else set it to NULL. */
+static const apr_getopt_option_t *
+get_option_from_code(const char **long_alias,
+                     int code,
+                     const apr_getopt_option_t *option_table,
+                     const svn_opt_subcommand_desc2_t *command,
+                     apr_pool_t *pool)
+{
+  const apr_getopt_option_t *i;
+  const apr_getopt_option_t *opt
+    = svn_opt_get_option_from_code2(code, option_table, command, pool);
+
+  /* Find a long alias in the table, if there is one. */
+  *long_alias = NULL;
+  for (i = option_table; i->optch; i++)
+    {
+      if (i->optch == code && i->name != opt->name)
+        {
+          *long_alias = i->name;
+          break;
+        }
+    }
+
+  return opt;
+}
+
+/* Print an option OPT nicely into a STRING allocated in POOL.
+ * If OPT has a single-character short form, then print OPT->name (if not
+ * NULL) as an alias, else print LONG_ALIAS (if not NULL) as an alias.
+ * If DOC is set, include the generic documentation string of OPT,
+ * localized to the current locale if a translation is available.
+ */
+static void
+format_option(const char **string,
+              const apr_getopt_option_t *opt,
+              const char *long_alias,
+              svn_boolean_t doc,
+              apr_pool_t *pool)
+{
+  char *opts;
+
+  if (opt == NULL)
+    {
+      *string = "?";
+      return;
+    }
+
+  /* We have a valid option which may or may not have a "short
+     name" (a single-character alias for the long option). */
+  if (opt->optch <= 255)
+    opts = apr_psprintf(pool, "-%c [--%s]", opt->optch, opt->name);
+  else if (long_alias)
+    opts = apr_psprintf(pool, "--%s [--%s]", opt->name, long_alias);
+  else
+    opts = apr_psprintf(pool, "--%s", opt->name);
+
+  if (opt->has_arg)
+    opts = apr_pstrcat(pool, opts, _(" ARG"), SVN_VA_NULL);
+
+  if (doc)
+    opts = apr_psprintf(pool, "%-24s : %s", opts, _(opt->description));
+
+  *string = opts;
+}
+
+/* Print the canonical command name for CMD, and all its aliases, to
+   STREAM.  If HELP is set, print CMD's help string too, in which case
+   obtain option usage from OPTIONS_TABLE. */
+static svn_error_t *
+print_command_info2(const svn_opt_subcommand_desc2_t *cmd,
+                    const apr_getopt_option_t *options_table,
+                    const int *global_options,
+                    svn_boolean_t help,
+                    apr_pool_t *pool,
+                    FILE *stream)
+{
+  svn_boolean_t first_time;
+  apr_size_t i;
+
+  /* Print the canonical command name. */
+  SVN_ERR(svn_cmdline_fputs(cmd->name, stream, pool));
+
+  /* Print the list of aliases. */
+  first_time = TRUE;
+  for (i = 0; i < SVN_OPT_MAX_ALIASES; i++)
+    {
+      if (cmd->aliases[i] == NULL)
+        break;
+
+      if (first_time) {
+        SVN_ERR(svn_cmdline_fputs(" (", stream, pool));
+        first_time = FALSE;
+      }
+      else
+        SVN_ERR(svn_cmdline_fputs(", ", stream, pool));
+
+      SVN_ERR(svn_cmdline_fputs(cmd->aliases[i], stream, pool));
+    }
+
+  if (! first_time)
+    SVN_ERR(svn_cmdline_fputs(")", stream, pool));
+
+  if (help)
+    {
+      const apr_getopt_option_t *option;
+      const char *long_alias;
+      svn_boolean_t have_options = FALSE;
+
+      SVN_ERR(svn_cmdline_fprintf(stream, pool, ": %s", _(cmd->help)));
+
+      /* Loop over all valid option codes attached to the subcommand */
+      for (i = 0; i < SVN_OPT_MAX_OPTIONS; i++)
+        {
+          if (cmd->valid_options[i])
+            {
+              if (!have_options)
+                {
+                  SVN_ERR(svn_cmdline_fputs(_("\nValid options:\n"),
+                                            stream, pool));
+                  have_options = TRUE;
+                }
+
+              /* convert each option code into an option */
+              option = get_option_from_code(&long_alias, cmd->valid_options[i],
+                                            options_table, cmd, pool);
+
+              /* print the option's docstring */
+              if (option && option->description)
+                {
+                  const char *optstr;
+                  format_option(&optstr, option, long_alias, TRUE, pool);
+                  SVN_ERR(svn_cmdline_fprintf(stream, pool, "  %s\n",
+                                              optstr));
+                }
+            }
+        }
+      /* And global options too */
+      if (global_options && *global_options)
+        {
+          SVN_ERR(svn_cmdline_fputs(_("\nGlobal options:\n"),
+                                    stream, pool));
+          have_options = TRUE;
+
+          for (i = 0; global_options[i]; i++)
+            {
+
+              /* convert each option code into an option */
+              option = get_option_from_code(&long_alias, global_options[i],
+                                            options_table, cmd, pool);
+
+              /* print the option's docstring */
+              if (option && option->description)
+                {
+                  const char *optstr;
+                  format_option(&optstr, option, long_alias, TRUE, pool);
+                  SVN_ERR(svn_cmdline_fprintf(stream, pool, "  %s\n",
+                                              optstr));
+                }
+            }
+        }
+
+      if (have_options)
+        SVN_ERR(svn_cmdline_fprintf(stream, pool, "\n"));
+    }
+
+  return SVN_NO_ERROR;
+}
+
+/* The body for svn_opt_print_generic_help2() function with standard error
+ * handling semantic. Handling of errors implemented at caller side. */
+static svn_error_t *
+print_generic_help_body(const char *header,
+                        const svn_opt_subcommand_desc2_t *cmd_table,
+                        const apr_getopt_option_t *opt_table,
+                        const char *footer,
+                        apr_pool_t *pool, FILE *stream)
+{
+  int i = 0;
+
+  if (header)
+    SVN_ERR(svn_cmdline_fputs(header, stream, pool));
+
+  while (cmd_table[i].name)
+    {
+      SVN_ERR(svn_cmdline_fputs("   ", stream, pool));
+      SVN_ERR(print_command_info2(cmd_table + i, opt_table,
+                                  NULL, FALSE,
+                                  pool, stream));
+      SVN_ERR(svn_cmdline_fputs("\n", stream, pool));
+      i++;
+    }
+
+  SVN_ERR(svn_cmdline_fputs("\n", stream, pool));
+
+  if (footer)
+    SVN_ERR(svn_cmdline_fputs(footer, stream, pool));
+
+  return SVN_NO_ERROR;
+}
+
+void
+svn_opt_print_generic_help2(const char *header,
+                            const svn_opt_subcommand_desc2_t *cmd_table,
+                            const apr_getopt_option_t *opt_table,
+                            const char *footer,
+                            apr_pool_t *pool, FILE *stream)
+{
+  svn_error_t *err;
+
+  err = print_generic_help_body(header, cmd_table, opt_table, footer, pool,
+                                stream);
+
+  /* Issue #3014:
+   * Don't print anything on broken pipes. The pipe was likely
+   * closed by the process at the other end. We expect that
+   * process to perform error reporting as necessary.
+   *
+   * ### This assumes that there is only one error in a chain for
+   * ### SVN_ERR_IO_PIPE_WRITE_ERROR. See svn_cmdline_fputs(). */
+  if (err && err->apr_err != SVN_ERR_IO_PIPE_WRITE_ERROR)
+    svn_handle_error2(err, stderr, FALSE, "svn: ");
+  svn_error_clear(err);
+}
+
+svn_boolean_t
+svn_opt_subcommand_takes_option3(const svn_opt_subcommand_desc2_t *command,
+                                 int option_code,
+                                 const int *global_options)
+{
+  apr_size_t i;
+
+  for (i = 0; i < SVN_OPT_MAX_OPTIONS; i++)
+    if (command->valid_options[i] == option_code)
+      return TRUE;
+
+  if (global_options)
+    for (i = 0; global_options[i]; i++)
+      if (global_options[i] == option_code)
+        return TRUE;
+
+  return FALSE;
+}
+
+svn_boolean_t
+svn_opt_subcommand_takes_option2(const svn_opt_subcommand_desc2_t *command,
+                                 int option_code)
+{
+  return svn_opt_subcommand_takes_option3(command,
+                                          option_code,
+                                          NULL);
+}
+
+svn_boolean_t
+svn_opt_subcommand_takes_option(const svn_opt_subcommand_desc_t *command,
+                                int option_code)
+{
+  apr_size_t i;
+
+  for (i = 0; i < SVN_OPT_MAX_OPTIONS; i++)
+    if (command->valid_options[i] == option_code)
+      return TRUE;
+
+  return FALSE;
+}
+
+void
+svn_opt_subcommand_help3(const char *subcommand,
+                         const svn_opt_subcommand_desc2_t *table,
+                         const apr_getopt_option_t *options_table,
+                         const int *global_options,
+                         apr_pool_t *pool)
+{
+  const svn_opt_subcommand_desc2_t *cmd =
+    svn_opt_get_canonical_subcommand2(table, subcommand);
+  svn_error_t *err;
+
+  if (cmd)
+    err = print_command_info2(cmd, options_table, global_options,
+                              TRUE, pool, stdout);
+  else
+    err = svn_cmdline_fprintf(stderr, pool,
+                              _("\"%s\": unknown command.\n\n"), subcommand);
+
+  if (err) {
+    /* Issue #3014: Don't print anything on broken pipes. */
+    if (err->apr_err != SVN_ERR_IO_PIPE_WRITE_ERROR)
+      svn_handle_error2(err, stderr, FALSE, "svn: ");
+    svn_error_clear(err);
+  }
+}
+
 void
 svn_opt_subcommand_help2(const char *subcommand,
                          const svn_opt_subcommand_desc2_t *table,
@@ -521,6 +883,56 @@ svn_opt_args_to_target_array(apr_array_h
   return SVN_NO_ERROR;
 }
 
+svn_error_t *
+svn_opt_print_help4(apr_getopt_t *os,
+                    const char *pgm_name,
+                    svn_boolean_t print_version,
+                    svn_boolean_t quiet,
+                    svn_boolean_t verbose,
+                    const char *version_footer,
+                    const char *header,
+                    const svn_opt_subcommand_desc2_t *cmd_table,
+                    const apr_getopt_option_t *option_table,
+                    const int *global_options,
+                    const char *footer,
+                    apr_pool_t *pool)
+{
+  apr_array_header_t *targets = NULL;
+
+  if (os)
+    SVN_ERR(svn_opt_parse_all_args(&targets, os, pool));
+
+  if (os && targets->nelts)  /* help on subcommand(s) requested */
+    {
+      int i;
+
+      for (i = 0; i < targets->nelts; i++)
+        {
+          svn_opt_subcommand_help3(APR_ARRAY_IDX(targets, i, const char *),
+                                   cmd_table, option_table,
+                                   global_options, pool);
+        }
+    }
+  else if (print_version)   /* just --version */
+    {
+      SVN_ERR(svn_opt__print_version_info(pgm_name, version_footer,
+                                          svn_version_extended(verbose, pool),
+                                          quiet, verbose, pool));
+    }
+  else if (os && !targets->nelts)            /* `-h', `--help', or `help' */
+    svn_opt_print_generic_help2(header,
+                                cmd_table,
+                                option_table,
+                                footer,
+                                pool,
+                                stdout);
+  else                                       /* unknown option or cmd */
+    SVN_ERR(svn_cmdline_fprintf(stderr, pool,
+                                _("Type '%s help' for usage.\n"), pgm_name));
+
+  return SVN_NO_ERROR;
+}
+
 svn_error_t *
 svn_opt_print_help3(apr_getopt_t *os,
                     const char *pgm_name,

Modified: subversion/branches/better-pristines/subversion/libsvn_subr/io.c
URL: http://svn.apache.org/viewvc/subversion/branches/better-pristines/subversion/libsvn_subr/io.c?rev=1829347&r1=1829346&r2=1829347&view=diff
 ==============================================================================
--- subversion/branches/better-pristines/subversion/libsvn_subr/io.c (original)
+++ subversion/branches/better-pristines/subversion/libsvn_subr/io.c Tue Apr 17 \
09:19:05 2018 @@ -2707,8 +2707,8 @@ svn_io_remove_dir(const char *path, apr_
  directory scan.  A previous workaround involving rewinddir is
  problematic on Win32 and some NFS clients, notably NetBSD.
 
- See http://subversion.tigris.org/issues/show_bug.cgi?id=1896 and
- http://subversion.tigris.org/issues/show_bug.cgi?id=3501.
+ See https://issues.apache.org/jira/browse/SVN-1896 and
+ https://issues.apache.org/jira/browse/SVN-3501.
 */
 
 /* Neither windows nor unix allows us to delete a non-empty


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic