You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by ju...@apache.org on 2018/07/09 09:53:37 UTC

svn commit: r1835398 - in /subversion/trunk/subversion: include/private/svn_client_private.h include/svn_client.h libsvn_client/diff.c libsvn_client/shelf.c svn/shelf-cmd.c tests/cmdline/shelf_tests.py

Author: julianfoad
Date: Mon Jul  9 09:53:37 2018
New Revision: 1835398

URL: http://svn.apache.org/viewvc?rev=1835398&view=rev
Log:
Shelving: Implement 'svn shelf-diff', issue SVN-4745.

This is a first pass. It drives only the file methods in the diff processor
API, not the open- and close-directory methods, and not reporting changes on
directories.

* subversion/include/svn_client.h
  (svn_client_shelf_export_patch): Deprecate.

* subversion/include/private/svn_client_private.h
  (svn_client__get_diff_writer_unidiff): New.
  (svn_client__shelf_diff): New.

* subversion/libsvn_client/diff.c
  (svn_client__get_diff_writer_unidiff): New.

* subversion/libsvn_client/shelf.c
  (file_changed, file_deleted, file_added): New.
  (diff_baton_t, diff_visitor): Drive a diff-processor from shelved changes.
  (svn_client__shelf_diff): New.
  (svn_client_shelf_export_patch): Delete the body; it is deprecated and did
    nothing before.

* subversion/svn/shelf-cmd.c
  (shelf_diff): First obtain a diff writer. Then call the shelf-diff API,
    driving that writer.

* subversion/tests/cmdline/shelf_tests.py
  (shelf_diff_simple): New test.
  (test_list): Run it.

Modified:
    subversion/trunk/subversion/include/private/svn_client_private.h
    subversion/trunk/subversion/include/svn_client.h
    subversion/trunk/subversion/libsvn_client/diff.c
    subversion/trunk/subversion/libsvn_client/shelf.c
    subversion/trunk/subversion/svn/shelf-cmd.c
    subversion/trunk/subversion/tests/cmdline/shelf_tests.py

Modified: subversion/trunk/subversion/include/private/svn_client_private.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/private/svn_client_private.h?rev=1835398&r1=1835397&r2=1835398&view=diff
==============================================================================
--- subversion/trunk/subversion/include/private/svn_client_private.h (original)
+++ subversion/trunk/subversion/include/private/svn_client_private.h Mon Jul  9 09:53:37 2018
@@ -341,6 +341,51 @@ svn_client__mergeinfo_log(svn_boolean_t
                           apr_pool_t *result_pool,
                           apr_pool_t *scratch_pool);
 
+/** Return a diff processor that will print a Subversion-style
+ * (not git-style) diff.
+ *
+ * @a anchor is optional (may be null), and is the 'anchor' path to prefix
+ * to the diff-processor paths before displaying.
+ *
+ * @a orig_path_1 and @a orig_path_2 are the two main root paths to be
+ * diffed; each may be a URL, a local WC path or a local unversioned path.
+ *
+ * Other arguments are as for svn_client_diff7() etc.
+ */
+svn_error_t *
+svn_client__get_diff_writer_svn(
+                svn_diff_tree_processor_t **diff_processor,
+                const char *anchor,
+                const char *orig_path_1,
+                const char *orig_path_2,
+                const apr_array_header_t *options,
+                const char *relative_to_dir,
+                svn_boolean_t no_diff_added,
+                svn_boolean_t no_diff_deleted,
+                svn_boolean_t show_copies_as_adds,
+                svn_boolean_t ignore_content_type,
+                svn_boolean_t ignore_properties,
+                svn_boolean_t properties_only,
+                svn_boolean_t pretty_print_mergeinfo,
+                const char *header_encoding,
+                svn_stream_t *outstream,
+                svn_stream_t *errstream,
+                svn_client_ctx_t *ctx,
+                apr_pool_t *pool);
+
+/** Output the subtree of @a shelf_version rooted at @a shelf_relpath
+ * as a diff to @a diff_processor.
+ *
+ * @since New in 1.X.
+ * @warning EXPERIMENTAL.
+ */
+SVN_EXPERIMENTAL
+svn_error_t *
+svn_client__shelf_diff(svn_client_shelf_version_t *shelf_version,
+                       const char *shelf_relpath,
+                       svn_diff_tree_processor_t *diff_processor,
+                       apr_pool_t *scratch_pool);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */

Modified: subversion/trunk/subversion/include/svn_client.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_client.h?rev=1835398&r1=1835397&r2=1835398&view=diff
==============================================================================
--- subversion/trunk/subversion/include/svn_client.h (original)
+++ subversion/trunk/subversion/include/svn_client.h Mon Jul  9 09:53:37 2018
@@ -7152,12 +7152,10 @@ svn_client_shelf_unapply(svn_client_shel
                          svn_boolean_t dry_run,
                          apr_pool_t *scratch_pool);
 
-/** Output @a shelf_version as a patch to @a outstream.
- *
- * @since New in 1.X.
+/** @deprecated Use svn_client__shelf_diff() instead.
  * @warning EXPERIMENTAL.
  */
-SVN_EXPERIMENTAL
+SVN_DEPRECATED
 svn_error_t *
 svn_client_shelf_export_patch(svn_client_shelf_version_t *shelf_version,
                               svn_stream_t *outstream,

Modified: subversion/trunk/subversion/libsvn_client/diff.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/diff.c?rev=1835398&r1=1835397&r2=1835398&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/diff.c (original)
+++ subversion/trunk/subversion/libsvn_client/diff.c Mon Jul  9 09:53:37 2018
@@ -2580,6 +2580,49 @@ get_diff_processor(svn_diff_tree_process
   return SVN_NO_ERROR;
 }
 
+svn_error_t *
+svn_client__get_diff_writer_svn(
+                svn_diff_tree_processor_t **diff_processor,
+                const char *anchor,
+                const char *orig_path_1,
+                const char *orig_path_2,
+                const apr_array_header_t *options,
+                const char *relative_to_dir,
+                svn_boolean_t no_diff_added,
+                svn_boolean_t no_diff_deleted,
+                svn_boolean_t show_copies_as_adds,
+                svn_boolean_t ignore_content_type,
+                svn_boolean_t ignore_properties,
+                svn_boolean_t properties_only,
+                svn_boolean_t pretty_print_mergeinfo,
+                const char *header_encoding,
+                svn_stream_t *outstream,
+                svn_stream_t *errstream,
+                svn_client_ctx_t *ctx,
+                apr_pool_t *pool)
+{
+  struct diff_driver_info_t *ddi;
+
+  SVN_ERR(get_diff_processor(diff_processor, &ddi,
+                             options,
+                             relative_to_dir,
+                             no_diff_added,
+                             no_diff_deleted,
+                             show_copies_as_adds,
+                             ignore_content_type,
+                             ignore_properties,
+                             properties_only,
+                             FALSE /*use_git_diff_format*/,
+                             pretty_print_mergeinfo,
+                             header_encoding,
+                             outstream, errstream,
+                             ctx, pool));
+  ddi->anchor = anchor;
+  ddi->orig_path_1 = orig_path_1;
+  ddi->orig_path_2 = orig_path_2;
+  return SVN_NO_ERROR;
+}
+
 /*----------------------------------------------------------------------- */
 
 /*** Public Interfaces. ***/

Modified: subversion/trunk/subversion/libsvn_client/shelf.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/shelf.c?rev=1835398&r1=1835397&r2=1835398&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/shelf.c (original)
+++ subversion/trunk/subversion/libsvn_client/shelf.c Mon Jul  9 09:53:37 2018
@@ -413,6 +413,9 @@ shelf_write_current(svn_client_shelf_t *
   return SVN_NO_ERROR;
 }
 
+/*-------------------------------------------------------------------------*/
+/* Status Reporting */
+
 /* Create a status struct with all fields initialized to valid values
  * representing 'uninteresting' or 'unknown' status.
  */
@@ -728,6 +731,9 @@ svn_client_shelf_version_status_walk(svn
   return SVN_NO_ERROR;
 }
 
+/*-------------------------------------------------------------------------*/
+/* Shelf Storage */
+
 /* A baton for use with write_changes_visitor(). */
 typedef struct write_changes_baton_t {
   const char *wc_root_abspath;
@@ -1609,23 +1615,204 @@ apply_file_visitor(void *baton,
   return SVN_NO_ERROR;
 }
 
+/*-------------------------------------------------------------------------*/
+/* Diff */
+
+/*  */
+static svn_error_t *
+file_changed(svn_client_shelf_version_t *shelf_version,
+             const char *relpath,
+             svn_wc_status3_t *s,
+             svn_diff_tree_processor_t *diff_processor,
+             svn_diff_source_t *left_source,
+             svn_diff_source_t *right_source,
+             const char *left_stored_abspath,
+             const char *right_stored_abspath,
+             void *dir_baton,
+             apr_pool_t *scratch_pool)
+{
+  void *fb;
+  svn_boolean_t skip = FALSE;
+
+  SVN_ERR(diff_processor->file_opened(&fb, &skip, relpath,
+                                      left_source, right_source,
+                                      NULL /*copyfrom*/,
+                                      dir_baton, diff_processor,
+                                      scratch_pool, scratch_pool));
+  if (!skip)
+    {
+      apr_hash_t *left_props, *right_props;
+      apr_array_header_t *prop_changes;
+
+      SVN_ERR(read_props_from_shelf(&left_props, &right_props,
+                                    s->node_status, shelf_version, relpath,
+                                    scratch_pool, scratch_pool));
+      SVN_ERR(svn_prop_diffs(&prop_changes, right_props, left_props,
+                             scratch_pool));
+      SVN_ERR(diff_processor->file_changed(
+                relpath,
+                left_source, right_source,
+                left_stored_abspath, right_stored_abspath,
+                left_props, right_props,
+                TRUE /*file_modified*/, prop_changes,
+                fb, diff_processor, scratch_pool));
+    }
+
+  return SVN_NO_ERROR;
+}
+
+/*  */
+static svn_error_t *
+file_deleted(svn_client_shelf_version_t *shelf_version,
+             const char *relpath,
+             svn_wc_status3_t *s,
+             svn_diff_tree_processor_t *diff_processor,
+             svn_diff_source_t *left_source,
+             const char *left_stored_abspath,
+             void *dir_baton,
+             apr_pool_t *scratch_pool)
+{
+  void *fb;
+  svn_boolean_t skip = FALSE;
+
+  SVN_ERR(diff_processor->file_opened(&fb, &skip, relpath,
+                                      left_source, NULL, NULL /*copyfrom*/,
+                                      dir_baton, diff_processor,
+                                      scratch_pool, scratch_pool));
+  if (!skip)
+    {
+      apr_hash_t *left_props, *right_props;
+
+      SVN_ERR(read_props_from_shelf(&left_props, &right_props,
+                                    s->node_status, shelf_version, relpath,
+                                    scratch_pool, scratch_pool));
+      SVN_ERR(diff_processor->file_deleted(relpath,
+                                           left_source,
+                                           left_stored_abspath,
+                                           left_props,
+                                           fb, diff_processor,
+                                           scratch_pool));
+    }
+
+  return SVN_NO_ERROR;
+}
+
+/*  */
+static svn_error_t *
+file_added(svn_client_shelf_version_t *shelf_version,
+           const char *relpath,
+           svn_wc_status3_t *s,
+           svn_diff_tree_processor_t *diff_processor,
+           svn_diff_source_t *right_source,
+           const char *right_stored_abspath,
+           void *dir_baton,
+           apr_pool_t *scratch_pool)
+{
+  void *fb;
+  svn_boolean_t skip = FALSE;
+
+  SVN_ERR(diff_processor->file_opened(&fb, &skip, relpath,
+                                      NULL, right_source, NULL /*copyfrom*/,
+                                      dir_baton, diff_processor,
+                                      scratch_pool, scratch_pool));
+  if (!skip)
+    {
+      apr_hash_t *left_props, *right_props;
+
+      SVN_ERR(read_props_from_shelf(&left_props, &right_props,
+                                    s->node_status, shelf_version, relpath,
+                                    scratch_pool, scratch_pool));
+      SVN_ERR(diff_processor->file_added(
+                relpath,
+                NULL /*copyfrom_source*/, right_source,
+                NULL /*copyfrom_abspath*/, right_stored_abspath,
+                NULL /*copyfrom_props*/, right_props,
+                fb, diff_processor, scratch_pool));
+    }
+
+  return SVN_NO_ERROR;
+}
+
 /* Baton for diff_visitor(). */
 struct diff_baton_t
 {
   svn_client_shelf_version_t *shelf_version;
-  svn_stream_t *outstream;
+  const char *top_relpath;  /* top of diff, relative to shelf */
+  const char *walk_root_abspath;
+  svn_diff_tree_processor_t *diff_processor;
 };
 
-/* Write a diff to BATON->outstream.
- * Implements shelved_files_walk_func_t. */
+/* Drive BATON->diff_processor.
+ * Implements svn_io_walk_func_t. */
 static svn_error_t *
 diff_visitor(void *baton,
-             const char *relpath,
-             svn_wc_status3_t *s,
+             const char *abspath,
+             const apr_finfo_t *finfo,
              apr_pool_t *scratch_pool)
 {
-  /* ### TODO */
+  struct diff_baton_t *b = baton;
+  const char *relpath;
 
+  relpath = svn_dirent_skip_ancestor(b->walk_root_abspath, abspath);
+  if (finfo->filetype == APR_REG
+      && (strlen(relpath) >= 5 && strcmp(relpath+strlen(relpath)-5, ".meta") == 0))
+    {
+      svn_wc_status3_t *s;
+      void *db = NULL;
+      svn_diff_source_t *left_source;
+      svn_diff_source_t *right_source;
+      char *left_stored_abspath, *right_stored_abspath;
+
+      relpath = apr_pstrndup(scratch_pool, relpath, strlen(relpath) - 5);
+      if (!svn_relpath_skip_ancestor(b->top_relpath, relpath))
+        return SVN_NO_ERROR;
+
+      SVN_ERR(status_read(&s, b->shelf_version, relpath,
+                          scratch_pool, scratch_pool));
+
+      left_source = svn_diff__source_create(s->revision, scratch_pool);
+      right_source = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
+      SVN_ERR(get_base_file_abspath(&left_stored_abspath,
+                                    b->shelf_version, relpath,
+                                    scratch_pool, scratch_pool));
+      SVN_ERR(get_working_file_abspath(&right_stored_abspath,
+                                       b->shelf_version, relpath,
+                                       scratch_pool, scratch_pool));
+
+      switch (s->node_status)
+        {
+        case svn_wc_status_modified:
+          SVN_ERR(file_changed(b->shelf_version, relpath, s,
+                               b->diff_processor,
+                               left_source, right_source,
+                               left_stored_abspath, right_stored_abspath,
+                               db, scratch_pool));
+          break;
+        case svn_wc_status_added:
+          SVN_ERR(file_added(b->shelf_version, relpath, s,
+                             b->diff_processor,
+                             right_source, right_stored_abspath,
+                             db, scratch_pool));
+          break;
+        case svn_wc_status_deleted:
+          SVN_ERR(file_deleted(b->shelf_version, relpath, s,
+                               b->diff_processor,
+                               left_source, left_stored_abspath,
+                               db, scratch_pool));
+          break;
+        case svn_wc_status_replaced:
+          SVN_ERR(file_deleted(b->shelf_version, relpath, s,
+                               b->diff_processor,
+                               left_source, left_stored_abspath,
+                               db, scratch_pool));
+          SVN_ERR(file_added(b->shelf_version, relpath, s,
+                             b->diff_processor,
+                             right_source, right_stored_abspath,
+                             db, scratch_pool));
+        default:
+          break;
+        }
+    }
   return SVN_NO_ERROR;
 }
 
@@ -1726,17 +1913,34 @@ svn_client_shelf_delete_newer_versions(s
 }
 
 svn_error_t *
+svn_client__shelf_diff(svn_client_shelf_version_t *shelf_version,
+                       const char *shelf_relpath,
+                       svn_diff_tree_processor_t *diff_processor,
+                       apr_pool_t *scratch_pool)
+{
+  struct diff_baton_t baton;
+  svn_error_t *err;
+
+  baton.shelf_version = shelf_version;
+  baton.top_relpath = shelf_relpath;
+  baton.walk_root_abspath = shelf_version->files_dir_abspath;
+  baton.diff_processor = diff_processor;
+  err = svn_io_dir_walk2(baton.walk_root_abspath, 0 /*wanted*/,
+                         diff_visitor, &baton,
+                         scratch_pool);
+  if (err && APR_STATUS_IS_ENOENT(err->apr_err))
+    svn_error_clear(err);
+  else
+    SVN_ERR(err);
+
+  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)
 {
-  struct diff_baton_t baton = {0};
-
-  baton.shelf_version = shelf_version;
-  baton.outstream = outstream;
-  SVN_ERR(shelf_status_walk(shelf_version, "",
-                            diff_visitor, &baton,
-                            scratch_pool));
   return SVN_NO_ERROR;
 }
 

Modified: subversion/trunk/subversion/svn/shelf-cmd.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/shelf-cmd.c?rev=1835398&r1=1835397&r2=1835398&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/shelf-cmd.c (original)
+++ subversion/trunk/subversion/svn/shelf-cmd.c Mon Jul  9 09:53:37 2018
@@ -38,6 +38,7 @@
 
 #include "svn_private_config.h"
 #include "private/svn_sorts_private.h"
+#include "private/svn_client_private.h"
 
 
 /* Open the newest version of SHELF; error if no versions found. */
@@ -709,7 +710,8 @@ shelf_diff(const char *name,
 {
   svn_client_shelf_t *shelf;
   svn_client_shelf_version_t *shelf_version;
-  svn_stream_t *stream;
+  svn_stream_t *stream, *errstream;
+  svn_diff_tree_processor_t *diff_processor;
 
   SVN_ERR(svn_client_shelf_open_existing(&shelf, name, local_abspath,
                                          ctx, scratch_pool));
@@ -730,8 +732,27 @@ shelf_diff(const char *name,
     }
 
   SVN_ERR(svn_stream_for_stdout(&stream, scratch_pool));
-  SVN_ERR(svn_client_shelf_export_patch(shelf_version, stream,
-                                        scratch_pool));
+  errstream = svn_stream_empty(scratch_pool);
+
+  SVN_ERR(svn_client__get_diff_writer_svn(
+            &diff_processor,
+            NULL /*anchor*/,
+            "", "", /*orig_path_1, orig_path_2,*/
+            NULL /*options*/,
+            "" /*relative_to_dir*/,
+            FALSE /*no_diff_added*/,
+            FALSE /*no_diff_deleted*/,
+            FALSE /*show_copies_as_adds*/,
+            FALSE /*ignore_content_type*/,
+            FALSE /*ignore_properties*/,
+            FALSE /*properties_only*/,
+            TRUE /*pretty_print_mergeinfo*/,
+            svn_cmdline_output_encoding(scratch_pool),
+            stream, errstream,
+            ctx, scratch_pool));
+
+  SVN_ERR(svn_client__shelf_diff(shelf_version, "", diff_processor,
+                                 scratch_pool));
   SVN_ERR(svn_stream_close(stream));
 
   SVN_ERR(svn_client_shelf_close(shelf, scratch_pool));

Modified: subversion/trunk/subversion/tests/cmdline/shelf_tests.py
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/shelf_tests.py?rev=1835398&r1=1835397&r2=1835398&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/cmdline/shelf_tests.py (original)
+++ subversion/trunk/subversion/tests/cmdline/shelf_tests.py Mon Jul  9 09:53:37 2018
@@ -32,6 +32,10 @@ logger = logging.getLogger()
 # Our testing module
 import svntest
 from svntest import wc
+from svntest.verify import make_diff_header, make_no_diff_deleted_header, \
+                           make_git_diff_header, make_diff_prop_header, \
+                           make_diff_prop_val, make_diff_prop_deleted, \
+                           make_diff_prop_added, make_diff_prop_modified
 
 # (abbreviation)
 Skip = svntest.testcase.Skip_deco
@@ -874,6 +878,46 @@ B>>>>>>> (incoming 'changed to' value)
 
   unshelve_with_merge(sbox, setup, modifier1, modifier2, tweak_expected_state)
 
+#----------------------------------------------------------------------
+
+# Exercise a very basic case of shelf-diff.
+def shelf_diff_simple(sbox):
+  "shelf diff simple"
+
+  sbox.build()
+  was_cwd = os.getcwd()
+  os.chdir(sbox.wc_dir)
+  sbox.wc_dir = ''
+  wc_dir = sbox.wc_dir
+
+  def setup(sbox):
+    sbox.simple_propset('p1', 'v', 'A/mu')
+    sbox.simple_propset('p2', 'v', 'A/mu')
+
+  def modifier1(sbox):
+    sbox.simple_append('A/mu', 'New line.\n')
+    sbox.simple_propset('p1', 'changed', 'A/mu')
+
+  setup(sbox)
+  sbox.simple_commit()
+  initial_state = get_wc_state(wc_dir)
+
+  # Make some changes to the working copy
+  modifier1(sbox)
+  modified_state = get_wc_state(wc_dir)
+
+  svntest.actions.run_and_verify_svn(None, [],
+                                     'shelf-save', 'foo')
+
+  expected_output = make_diff_header('A/mu', 'revision 2', 'working copy') + [
+                      "@@ -1 +1,2 @@\n",
+                      " This is the file 'mu'.\n",
+                      "+New line.\n",
+                    ] + make_diff_prop_header('A/mu') \
+                    + make_diff_prop_modified('p1', 'v', 'changed')
+  svntest.actions.run_and_verify_svn(expected_output, [],
+                                     'shelf-diff', 'foo')
+
 
 ########################################################################
 # Run the tests
@@ -910,6 +954,7 @@ test_list = [ None,
               unshelve_binary_mod_conflict,
               unshelve_text_prop_merge,
               unshelve_text_prop_conflict,
+              shelf_diff_simple,
              ]
 
 if __name__ == '__main__':