You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by rh...@apache.org on 2015/10/02 14:26:27 UTC

svn commit: r1706384 - in /subversion/trunk/subversion: libsvn_client/diff.c libsvn_diff/parse-diff.c tests/cmdline/diff_tests.py

Author: rhuijben
Date: Fri Oct  2 12:26:27 2015
New Revision: 1706384

URL: http://svn.apache.org/viewvc?rev=1706384&view=rev
Log:
When parsing diffs note a possible file mode on git index lines to allow
seeing if we are processing git symlinks or Subversion symlinks in all
scenarios.

Extend our diff code to produce proper index lines when we are diffing
symlinks.

* subversion/libsvn_client/diff.c
  (maybe_print_mode_change): Print index line when there is no mode change and
    shas are available.
  (print_git_diff_header): Pass shas.
  (display_prop_diffs): Update caller.
  (transform_link_to_git): Calculate sha over the link when transformed.
  (diff_content_changed): Update caller.

* subversion/libsvn_diff/parse-diff.c
  (git_index): New function.
  (transitions): Add parser transitions to note index line.
  (svn_diff_parse_next_patch): Remove "index " hacks.

* subversion/tests/cmdline/diff_tests.py
  (diff_symlinks): Update expected result.

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

Modified: subversion/trunk/subversion/libsvn_client/diff.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/diff.c?rev=1706384&r1=1706383&r2=1706384&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/diff.c (original)
+++ subversion/trunk/subversion/libsvn_client/diff.c Fri Oct  2 12:26:27 2015
@@ -359,6 +359,7 @@ maybe_print_mode_change(svn_stream_t *os
                         svn_boolean_t exec_bit2,
                         svn_boolean_t symlink_bit1,
                         svn_boolean_t symlink_bit2,
+                        const char *git_index_shas,
                         apr_pool_t *scratch_pool)
 {
   int old_mode = (exec_bit1 ? exec_mode : noexec_mode)
@@ -366,7 +367,13 @@ maybe_print_mode_change(svn_stream_t *os
   int new_mode = (exec_bit2 ? exec_mode : noexec_mode)
                  | (symlink_bit2 ? kind_symlink_mode : kind_file_mode);
   if (old_mode == new_mode)
-    return SVN_NO_ERROR;
+    {
+      if (git_index_shas)
+        SVN_ERR(svn_stream_printf_from_utf8(os, header_encoding, scratch_pool,
+                                            "index %s %06o" APR_EOL_STR,
+                                            git_index_shas, old_mode));
+      return SVN_NO_ERROR;
+    }
 
   SVN_ERR(svn_stream_printf_from_utf8(os, header_encoding, scratch_pool,
                                       "old mode %06o" APR_EOL_STR, old_mode));
@@ -394,6 +401,7 @@ print_git_diff_header(svn_stream_t *os,
                       svn_revnum_t copyfrom_rev,
                       apr_hash_t *left_props,
                       apr_hash_t *right_props,
+                      const char *git_index_shas,
                       const char *header_encoding,
                       apr_pool_t *scratch_pool)
 {
@@ -430,6 +438,7 @@ print_git_diff_header(svn_stream_t *os,
       SVN_ERR(maybe_print_mode_change(os, header_encoding,
                                       exec_bit1, exec_bit2,
                                       symlink_bit1, symlink_bit2,
+                                      git_index_shas,
                                       scratch_pool));
     }
   else if (operation == svn_diff_op_added)
@@ -454,6 +463,7 @@ print_git_diff_header(svn_stream_t *os,
       SVN_ERR(maybe_print_mode_change(os, header_encoding,
                                       exec_bit1, exec_bit2,
                                       symlink_bit1, symlink_bit2,
+                                      git_index_shas,
                                       scratch_pool));
     }
   else if (operation == svn_diff_op_moved)
@@ -468,6 +478,7 @@ print_git_diff_header(svn_stream_t *os,
       SVN_ERR(maybe_print_mode_change(os, header_encoding,
                                       exec_bit1, exec_bit2,
                                       symlink_bit1, symlink_bit2,
+                                      git_index_shas,
                                       scratch_pool));
     }
 
@@ -553,6 +564,7 @@ display_prop_diffs(const apr_array_heade
                                       SVN_INVALID_REVNUM,
                                       left_props,
                                       right_props,
+                                      NULL,
                                       encoding, scratch_pool));
 
       /* --- label1
@@ -718,6 +730,7 @@ diff_props_changed(const char *diff_relp
    as long as RESULT_POOL, containing the git-like represention of TMPFILE */
 static svn_error_t *
 transform_link_to_git(const char **new_tmpfile,
+                      const char **git_sha1,
                       const char *tmpfile,
                       apr_pool_t *result_pool,
                       apr_pool_t *scratch_pool)
@@ -726,6 +739,8 @@ transform_link_to_git(const char **new_t
   apr_file_t *gitlike;
   svn_stringbuf_t *line;
 
+  *git_sha1 = NULL;
+
   SVN_ERR(svn_io_file_open(&orig, tmpfile, APR_READ, APR_OS_DEFAULT,
                            scratch_pool));
   SVN_ERR(svn_io_open_unique_file3(&gitlike, new_tmpfile, NULL,
@@ -737,8 +752,23 @@ transform_link_to_git(const char **new_t
 
   if (line->len > 5 && !strncmp(line->data, "link ", 5))
     {
-      SVN_ERR(svn_io_file_write_full(gitlike, line->data + 5, line->len - 5,
+      const char *sz_str;
+      svn_checksum_t *checksum;
+
+      svn_stringbuf_remove(line, 0, 5);
+
+      SVN_ERR(svn_io_file_write_full(gitlike, line->data, line->len,
                                      NULL, scratch_pool));
+
+      /* git calculates the sha over "blob X\0" + the actual data,
+         where X is the decimal size of the blob. */
+      sz_str = apr_psprintf(scratch_pool, "blob %u", line->len);
+      svn_stringbuf_insert(line, 0, sz_str, strlen(sz_str) + 1);
+
+      SVN_ERR(svn_checksum(&checksum, svn_checksum_sha1,
+                           line->data, line->len, scratch_pool));
+
+      *git_sha1 = svn_checksum_to_cstring(checksum, result_pool);
     }
   else
     {
@@ -784,6 +814,7 @@ diff_content_changed(svn_boolean_t *wrot
   const char *path2 = dwi->ddi.orig_path_2;
   const char *mimetype1 = svn_prop_get_value(left_props, SVN_PROP_MIME_TYPE);
   const char *mimetype2 = svn_prop_get_value(right_props, SVN_PROP_MIME_TYPE);
+  const char *index_shas = NULL;
 
   /* If only property differences are shown, there's nothing to do. */
   if (dwi->properties_only)
@@ -807,13 +838,29 @@ diff_content_changed(svn_boolean_t *wrot
 
   if (dwi->use_git_diff_format)
     {
+      const char *l_hash = NULL;
+      const char *r_hash = NULL;
+
       /* Change symlinks to their 'git like' plain format */
       if (svn_prop_get_value(left_props, SVN_PROP_SPECIAL))
-        SVN_ERR(transform_link_to_git(&tmpfile1, tmpfile1,
+        SVN_ERR(transform_link_to_git(&tmpfile1, &l_hash, tmpfile1,
                                       scratch_pool, scratch_pool));
       if (svn_prop_get_value(right_props, SVN_PROP_SPECIAL))
-        SVN_ERR(transform_link_to_git(&tmpfile2, tmpfile2,
+        SVN_ERR(transform_link_to_git(&tmpfile2, &r_hash, tmpfile2,
                                       scratch_pool, scratch_pool));
+
+      if (l_hash && r_hash)
+        {
+          /* The symlink has changed. But we can't tell the user of the
+             diff whether we are writing git diffs or svn diffs of the
+             symlink... except when we add a git-like index line */
+
+          l_hash = apr_pstrndup(scratch_pool, l_hash, 8);
+          r_hash = apr_pstrndup(scratch_pool, r_hash, 8);
+
+          index_shas = apr_psprintf(scratch_pool, "%8s..%8s",
+                                    l_hash, r_hash);
+        }
     }
 
   if (! dwi->force_binary && (mt1_binary || mt2_binary))
@@ -856,6 +903,7 @@ diff_content_changed(svn_boolean_t *wrot
                                         copyfrom_rev,
                                         left_props,
                                         right_props,
+                                        index_shas,
                                         dwi->header_encoding,
                                         scratch_pool));
 
@@ -1019,6 +1067,7 @@ diff_content_changed(svn_boolean_t *wrot
                                             copyfrom_rev,
                                             left_props,
                                             right_props,
+                                            index_shas,
                                             dwi->header_encoding,
                                             scratch_pool));
             }

Modified: subversion/trunk/subversion/libsvn_diff/parse-diff.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_diff/parse-diff.c?rev=1706384&r1=1706383&r2=1706384&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_diff/parse-diff.c (original)
+++ subversion/trunk/subversion/libsvn_diff/parse-diff.c Fri Oct  2 12:26:27 2015
@@ -1149,12 +1149,12 @@ parse_next_hunk(svn_diff_hunk_t **hunk,
                 }
 
               SVN_ERR(svn_io_file_seek(apr_file, APR_SET, &pos, iterpool));
+              /* Set for the type and context by using != the other type */
+              if (last_line_type != modified_line)
+                original_no_final_eol = TRUE;
+              if (last_line_type != original_line)
+                modified_no_final_eol = TRUE;
             }
-          /* Set for the type and context by using != the other type */
-          if (last_line_type != modified_line)
-            original_no_final_eol = TRUE;
-          if (last_line_type != original_line)
-            modified_no_final_eol = TRUE;
 
           continue;
         }
@@ -1699,6 +1699,39 @@ git_new_mode(enum parse_state *new_state
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+git_index(enum parse_state *new_state, char *line, svn_patch_t *patch,
+          apr_pool_t *result_pool, apr_pool_t *scratch_pool)
+{
+  /* We either have something like "index 33e5b38..0000000" (which we just
+     ignore as we are not interested in git specific shas) or something like
+     "index 33e5b38..0000000 120000" which tells us the mode, that isn't
+     changed by applying this patch.
+
+     If the mode would have changed then we would see 'old mode' and 'new mode'
+     lines.
+  */
+  line = strchr(line + STRLEN_LITERAL("index "), ' ');
+
+  if (line && patch->new_executable_bit == svn_tristate_unknown
+           && patch->new_symlink_bit == svn_tristate_unknown
+           && patch->operation != svn_diff_op_added
+           && patch->operation != svn_diff_op_deleted)
+    {
+      SVN_ERR(parse_git_mode_bits(&patch->new_executable_bit,
+                                  &patch->new_symlink_bit,
+                                  line + 1));
+
+      /* There is no change.. so set the old values to the new values */
+      patch->old_executable_bit = patch->new_executable_bit;
+      patch->old_symlink_bit = patch->new_symlink_bit;
+    }
+
+  /* This function doesn't change the state! */
+  /* *new_state = *new_state */
+  return SVN_NO_ERROR;
+}
+
 /* Parse the 'rename from ' line of a git extended unidiff. */
 static svn_error_t *
 git_move_from(enum parse_state *new_state, char *line, svn_patch_t *patch,
@@ -2062,6 +2095,10 @@ static struct transition transitions[] =
 
   {"deleted file ",     state_git_diff_seen,    git_deleted_file},
 
+  {"index ",            state_git_diff_seen,    git_index},
+  {"index ",            state_git_tree_seen,    git_index},
+  {"index ",            state_git_mode_seen,    git_index},
+
   {"GIT binary patch",  state_git_diff_seen,    binary_patch_start},
   {"GIT binary patch",  state_git_tree_seen,    binary_patch_start},
   {"GIT binary patch",  state_git_mode_seen,    binary_patch_start},
@@ -2144,17 +2181,13 @@ svn_diff_parse_next_patch(svn_patch_t **
                && line_after_tree_header_read
                && !valid_header_line)
         {
-          /* git patches can contain an index line after the file mode line */
-          if (!starts_with(line->data, "index "))
-          {
-            /* We have a valid diff header for a patch with only tree changes.
-             * Rewind to the start of the line just read, so subsequent calls
-             * to this function don't end up skipping the line -- it may
-             * contain a patch. */
-            SVN_ERR(svn_io_file_seek(patch_file->apr_file, APR_SET, &last_line,
-                    scratch_pool));
-            break;
-          }
+          /* We have a valid diff header for a patch with only tree changes.
+           * Rewind to the start of the line just read, so subsequent calls
+           * to this function don't end up skipping the line -- it may
+           * contain a patch. */
+          SVN_ERR(svn_io_file_seek(patch_file->apr_file, APR_SET, &last_line,
+                                   scratch_pool));
+          break;
         }
       else if (state == state_git_tree_seen
                || state == state_git_mode_seen)
@@ -2162,8 +2195,7 @@ svn_diff_parse_next_patch(svn_patch_t **
           line_after_tree_header_read = TRUE;
         }
       else if (! valid_header_line && state != state_start
-               && state != state_git_diff_seen
-               && !starts_with(line->data, "index "))
+               && state != state_git_diff_seen)
         {
           /* We've encountered an invalid diff header.
            *

Modified: subversion/trunk/subversion/tests/cmdline/diff_tests.py
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/diff_tests.py?rev=1706384&r1=1706383&r2=1706384&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/cmdline/diff_tests.py (original)
+++ subversion/trunk/subversion/tests/cmdline/diff_tests.py Fri Oct  2 12:26:27 2015
@@ -5077,6 +5077,7 @@ def diff_symlinks(sbox):
     'Index: %s\n' % sbox.path('to-iota'),
     '===================================================================\n',
     'diff --git a/to-iota b/to-iota\n',
+    'index 3ef26e44..9930f9a0 120644\n',
     '--- a/to-iota\t(revision 2)\n',
     '+++ b/to-iota\t(working copy)\n',
     '@@ -1 +1 @@\n',