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/01/24 19:48:22 UTC

svn commit: r1822151 - in /subversion/trunk/subversion: libsvn_diff/parse-diff.c tests/cmdline/patch_tests.py

Author: julianfoad
Date: Wed Jan 24 19:48:22 2018
New Revision: 1822151

URL: http://svn.apache.org/viewvc?rev=1822151&view=rev
Log:
Teach 'patch' to read an svn:mergeinfo property change formatted as a normal
property diff.

Previously it could only read the pretty-printed format.

Part of issue #3747 "'svn diff' and 'svn patch' support of svn:mergeinfo".

* subversion/libsvn_diff/parse-diff.c
  (parse_pretty_mergeinfo_line): Rename from 'parse_mergeinfo'. Don't munge
    the hunk header object. Improve documentation.
  (parse_next_hunk): When parsing pretty-printed mergeinfo, allow to fall
    through to normal processing if pretty-print format is not found. If
    a pretty-print hunk is found, return a hunk object. (Previously it
    quietly processed it and moved on to the next hunk without informing
    the caller.)
  (parse_hunks): Instead of skipping storing any mergeinfo hunks, only
    skip storing pretty-print-mergeinfo hunks, but do store those formatted
    in the regular property diff format.

* subversion/tests/cmdline/patch_tests.py
  (patch_mergeinfo_in_regular_prop_format): New test.
  (test_list): Run it.

Modified:
    subversion/trunk/subversion/libsvn_diff/parse-diff.c
    subversion/trunk/subversion/tests/cmdline/patch_tests.py

Modified: subversion/trunk/subversion/libsvn_diff/parse-diff.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_diff/parse-diff.c?rev=1822151&r1=1822150&r2=1822151&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_diff/parse-diff.c (original)
+++ subversion/trunk/subversion/libsvn_diff/parse-diff.c Wed Jan 24 19:48:22 2018
@@ -69,6 +69,11 @@ struct svn_diff_hunk_t {
   /* APR file handle to the patch file this hunk came from. */
   apr_file_t *apr_file;
 
+  /* Whether the hunk was interpreted as pretty-print mergeinfo. If so,
+     the hunk content is in PATCH and the rest of this hunk object is
+     mostly uninitialized. */
+  svn_boolean_t is_pretty_print_mergeinfo;
+
   /* Ranges used to keep track of this hunk's texts positions within
    * the patch file. */
   struct svn_diff__hunk_range diff_text_range;
@@ -899,10 +904,6 @@ parse_prop_name(const char **prop_name,
  * The hunk header has the following format:
  * ## -0,NUMBER_OF_REVERSE_MERGES +0,NUMBER_OF_FORWARD_MERGES ##
  *
- * At this point, the number of reverse merges has already been
- * parsed into HUNK->ORIGINAL_LENGTH, and the number of forward
- * merges has been parsed into HUNK->MODIFIED_LENGTH.
- *
  * The header is followed by a list of mergeinfo, one path per line.
  * This function parses such lines. Lines describing reverse merges
  * appear first, and then all lines describing forward merges appear.
@@ -914,18 +915,27 @@ parse_prop_name(const char **prop_name,
  * ":r", which in turn is followed by a mergeinfo revision range,
  *  which is terminated by whitespace or end-of-string.
  *
- * If the current line meets the above criteria and we're able
- * to parse valid mergeinfo from it, the resulting mergeinfo
- * is added to patch->mergeinfo or patch->reverse_mergeinfo,
- * and we proceed to the next line.
+ * *NUMBER_OF_REVERSE_MERGES and *NUMBER_OF_FORWARD_MERGES are the
+ * numbers of reverse and forward merges remaining to be read. This
+ * function decrements *NUMBER_OF_REVERSE_MERGES for each LINE
+ * parsed until that is zero, then *NUMBER_OF_FORWARD_MERGES for
+ * each LINE parsed until that is zero. If both are zero, it parses
+ * and discards LINE.
+ *
+ * If LINE is successfully parsed, *FOUND_MERGEINFO is set to TRUE,
+ * otherwise to FALSE.
+ *
+ * If LINE is successfully parsed and counted, the resulting mergeinfo
+ * is added to PATCH->mergeinfo or PATCH->reverse_mergeinfo.
  */
 static svn_error_t *
-parse_mergeinfo(svn_boolean_t *found_mergeinfo,
-                svn_stringbuf_t *line,
-                svn_diff_hunk_t *hunk,
-                svn_patch_t *patch,
-                apr_pool_t *result_pool,
-                apr_pool_t *scratch_pool)
+parse_pretty_mergeinfo_line(svn_boolean_t *found_mergeinfo,
+                            svn_linenum_t *number_of_reverse_merges,
+                            svn_linenum_t *number_of_forward_merges,
+                            svn_stringbuf_t *line,
+                            svn_patch_t *patch,
+                            apr_pool_t *result_pool,
+                            apr_pool_t *scratch_pool)
 {
   char *slash = strchr(line->data, '/');
   char *colon = strrchr(line->data, ':');
@@ -972,7 +982,7 @@ parse_mergeinfo(svn_boolean_t *found_mer
 
       if (mergeinfo)
         {
-          if (hunk->original_length > 0) /* reverse merges */
+          if (*number_of_reverse_merges > 0) /* reverse merges */
             {
               if (patch->reverse)
                 {
@@ -994,9 +1004,9 @@ parse_mergeinfo(svn_boolean_t *found_mer
                                                  result_pool,
                                                  scratch_pool));
                 }
-              hunk->original_length--;
+              (*number_of_reverse_merges)--;
             }
-          else if (hunk->modified_length > 0) /* forward merges */
+          else if (number_of_forward_merges > 0) /* forward merges */
             {
               if (patch->reverse)
                 {
@@ -1018,7 +1028,7 @@ parse_mergeinfo(svn_boolean_t *found_mer
                                                  result_pool,
                                                  scratch_pool));
                 }
-              hunk->modified_length--;
+              (*number_of_forward_merges)--;
             }
 
           *found_mergeinfo = TRUE;
@@ -1165,18 +1175,48 @@ parse_next_hunk(svn_diff_hunk_t **hunk,
       if (in_hunk && *is_property && *prop_name &&
           strcmp(*prop_name, SVN_PROP_MERGEINFO) == 0)
         {
-          svn_boolean_t found_mergeinfo;
+          svn_boolean_t found_pretty_mergeinfo_line;
 
-          SVN_ERR(parse_mergeinfo(&found_mergeinfo, line, *hunk, patch,
-                                  result_pool, iterpool));
-          if (found_mergeinfo)
-            continue; /* Proceed to the next line in the svn:mergeinfo hunk. */
-          else
+          if (! hunk_seen)
+            {
+              /* We're reading the first line of the hunk, so the start
+               * of the line just read is the hunk text's byte offset. */
+              start = last_line;
+            }
+
+          SVN_ERR(parse_pretty_mergeinfo_line(&found_pretty_mergeinfo_line,
+                                              &original_lines, &modified_lines,
+                                              line, patch,
+                                              result_pool, iterpool));
+          if (found_pretty_mergeinfo_line)
             {
-              /* Perhaps we can also use original_lines/modified_lines here */
+              hunk_seen = TRUE;
+              (*hunk)->is_pretty_print_mergeinfo = TRUE;
+              continue; /* Proceed to the next line in the svn:mergeinfo hunk. */
+            }
 
-              in_hunk = FALSE; /* On to next property */
+          if ((*hunk)->is_pretty_print_mergeinfo)
+            {
+              /* We have reached the end of the pretty-print-mergeinfo hunk.
+                 (This format uses only one hunk.) */
+              if (eof)
+                {
+                  /* The hunk ends at EOF. */
+                  end = pos;
+                }
+              else
+                {
+                  /* The start of the current line marks the first byte
+                   * after the hunk text. */
+                  end = last_line;
+                }
+              original_end = end;
+              modified_end = end;
+              break;
             }
+
+          /* Otherwise, this is a property diff in the
+             regular format so fall through to normal processing. */
         }
 
       if (in_hunk)
@@ -1971,10 +2011,10 @@ parse_hunks(svn_patch_t *patch, apr_file
           else
             last_prop_name = prop_name;
 
-          /* Skip svn:mergeinfo properties.
-           * Mergeinfo data cannot be represented as a hunk and
+          /* Skip pretty-printed svn:mergeinfo property hunks.
+           * Pretty-printed mergeinfo data cannot be represented as a hunk and
            * is therefore stored in PATCH itself. */
-          if (strcmp(prop_name, SVN_PROP_MERGEINFO) == 0)
+          if (hunk->is_pretty_print_mergeinfo)
             continue;
 
           SVN_ERR(add_property_hunk(patch, prop_name, hunk, prop_operation,

Modified: subversion/trunk/subversion/tests/cmdline/patch_tests.py
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/patch_tests.py?rev=1822151&r1=1822150&r2=1822151&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/cmdline/patch_tests.py (original)
+++ subversion/trunk/subversion/tests/cmdline/patch_tests.py Wed Jan 24 19:48:22 2018
@@ -7791,6 +7791,68 @@ def patch_merge(sbox):
                                        expected_disk, None,
                                        expected_skip)
 
+def patch_mergeinfo_in_regular_prop_format(sbox):
+  "patch mergeinfo in regular prop format"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+  strip_count = wc_dir.count(os.path.sep)+1
+
+  sbox.simple_copy('A/B/E', 'E')
+  sbox.simple_append('A/B/E/alpha', 'extra\nlines\n')
+  sbox.simple_commit()
+
+  sbox.simple_propset('a', 'A', 'E') # 'a' < 'svn:mergeinfo'
+  sbox.simple_propset('z', 'Z', 'E') # 'z' > 'svn:mergeinfo'
+
+  svntest.actions.run_and_verify_svn(None, [],
+                                     'merge', '^/A/B/E', sbox.ospath('E'))
+  # Rename 'svn:mergeinfo' to 'svn_mergeinfo' so that 'diff' doesn't
+  # pretty-print it; then rename it back before we run it through 'patch'.
+  # (Alternatively, we could disable pretty-printing when we implement a
+  # command-line switch to do so.)
+  mergeinfo_value = sbox.simple_propget('svn:mergeinfo', 'E')
+  sbox.simple_propdel('svn:mergeinfo', 'E')
+  sbox.simple_propset('svn_mergeinfo', mergeinfo_value, 'E')
+
+  _, diff, _ = svntest.actions.run_and_verify_svn(None, [],
+                                                  'diff', wc_dir)
+  diff = re.sub('svn_mergeinfo', 'svn:mergeinfo', ''.join(diff))
+
+  sbox.simple_revert('E', 'E/alpha')
+
+  patch = sbox.get_tempname('recurse.patch')
+  svntest.main.file_write(patch, diff, mode='wb')
+
+  expected_output = wc.State(wc_dir, {
+    'E'                 : Item(status=' U'),
+    'E/alpha'           : Item(status='U '),
+  })
+  expected_skip = wc.State(wc_dir, {})
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+  expected_status.add({
+    'E'                 : Item(status=' M', wc_rev='2'),
+    'E/alpha'           : Item(status='M ', wc_rev='2'),
+    'E/beta'            : Item(status='  ', wc_rev='2'),
+  })
+  expected_status.tweak('A/B/E/alpha', wc_rev=2)
+  expected_disk = svntest.main.greek_state.copy()
+  expected_disk.tweak('A/B/E/alpha', contents="This is the file 'alpha'.\nextra\nlines\n")
+  expected_disk.add({
+    'E'                 : Item(props={'a': 'A',
+                                      # here is the correctly patched mergeinfo
+                                      'svn:mergeinfo': '/A/B/E:2',
+                                      'z': 'Z'}),
+    'E/beta'            : Item(contents="This is the file 'beta'.\n"),
+    'E/alpha'           : Item(contents="This is the file 'alpha'.\nextra\nlines\n"),
+  })
+
+  svntest.actions.run_and_verify_patch(wc_dir, patch,
+                                       expected_output, expected_disk,
+                                       expected_status, expected_skip,
+                                       [], True, True,
+                                       '--strip', strip_count)
+
 ########################################################################
 #Run the tests
 
@@ -7874,6 +7936,7 @@ test_list = [ None,
               missing_trailing_context,
               patch_missed_trail,
               patch_merge,
+              patch_mergeinfo_in_regular_prop_format,
             ]
 
 if __name__ == '__main__':