6712ef
# ./pullrev.sh r1564900
6712ef
6712ef
http://svn.apache.org/viewvc?view=revision&revision=r1564900
6712ef
6712ef
https://bugzilla.redhat.com/show_bug.cgi?id=1378178
6712ef
6712ef
--- subversion-1.7.14/subversion/tests/cmdline/diff_tests.py
6712ef
+++ subversion-1.7.14/subversion/tests/cmdline/diff_tests.py
6712ef
@@ -45,16 +45,39 @@
6712ef
 ######################################################################
6712ef
 # Generate expected output
6712ef
 
6712ef
-def make_diff_header(path, old_tag, new_tag):
6712ef
+def is_absolute_url(target):
6712ef
+  return (target.startswith('file://')
6712ef
+          or target.startswith('http://')
6712ef
+          or target.startswith('https://')
6712ef
+          or target.startswith('svn://')
6712ef
+          or target.startswith('svn+ssh://'))
6712ef
+
6712ef
+def make_diff_header(path, old_tag, new_tag, src_label=None, dst_label=None):
6712ef
   """Generate the expected diff header for file PATH, with its old and new
6712ef
-  versions described in parentheses by OLD_TAG and NEW_TAG. Return the header
6712ef
-  as an array of newline-terminated strings."""
6712ef
+  versions described in parentheses by OLD_TAG and NEW_TAG. SRC_LABEL and
6712ef
+  DST_LABEL are paths or urls that are added to the diff labels if we're
6712ef
+  diffing against the repository or diffing two arbitrary paths.
6712ef
+  Return the header as an array of newline-terminated strings."""
6712ef
+  if src_label:
6712ef
+    src_label = src_label.replace('\\', '/')
6712ef
+    if not is_absolute_url(src_label):
6712ef
+      src_label = '.../' + src_label
6712ef
+    src_label = '\t(' + src_label + ')'
6712ef
+  else:
6712ef
+    src_label = ''
6712ef
+  if dst_label:
6712ef
+    dst_label = dst_label.replace('\\', '/')
6712ef
+    if not is_absolute_url(dst_label):
6712ef
+      dst_label = '.../' + dst_label
6712ef
+    dst_label = '\t(' + dst_label + ')'
6712ef
+  else:
6712ef
+    dst_label = ''
6712ef
   path_as_shown = path.replace('\\', '/')
6712ef
   return [
6712ef
     "Index: " + path_as_shown + "\n",
6712ef
     "===================================================================\n",
6712ef
-    "--- " + path_as_shown + "\t(" + old_tag + ")\n",
6712ef
-    "+++ " + path_as_shown + "\t(" + new_tag + ")\n",
6712ef
+    "--- " + path_as_shown + src_label + "\t(" + old_tag + ")\n",
6712ef
+    "+++ " + path_as_shown + dst_label + "\t(" + new_tag + ")\n",
6712ef
     ]
6712ef
 
6712ef
 def make_no_diff_deleted_header(path, old_tag, new_tag):
6712ef
@@ -3867,6 +3890,122 @@
6712ef
                                      'diff', '-c2',
6712ef
                                      sbox.repo_url + '/A/D/H')
6712ef
 
6712ef
+@Issue(4460)
6712ef
+def diff_repo_wc_file_props(sbox):
6712ef
+  "diff repo to wc file target with props"
6712ef
+  sbox.build()
6712ef
+  iota = sbox.ospath('iota')
6712ef
+
6712ef
+  # add a mime-type and a line to iota to test the binary check
6712ef
+  sbox.simple_propset('svn:mime-type', 'text/plain', 'iota')
6712ef
+  sbox.simple_append('iota','second line\n')
6712ef
+
6712ef
+  # test that we get the line and the property add
6712ef
+  expected_output = make_diff_header(iota, 'revision 1', 'working copy') + \
6712ef
+                    [ '@@ -1 +1,2 @@\n',
6712ef
+                      " This is the file 'iota'.\n",
6712ef
+                      "+second line\n", ] + \
6712ef
+                    make_diff_prop_header(iota) + \
6712ef
+                    make_diff_prop_added('svn:mime-type', 'text/plain')
6712ef
+  svntest.actions.run_and_verify_svn(None, expected_output, [],
6712ef
+                                     'diff', '-r1', iota)
6712ef
+
6712ef
+  # reverse the diff, should get a property delete and line delete
6712ef
+  # skip actually testing the output since apparently 1.7 is busted
6712ef
+  # this isn't related to issue #4460, older versions of 1.7 had the issue
6712ef
+  # as well
6712ef
+  #expected_output = make_diff_header(iota, 'working copy', 'revision 1') + \
6712ef
+  #                  [ '@@ -1,2 +1 @@\n',
6712ef
+  #                    " This is the file 'iota'.\n",
6712ef
+  #                    "-second line\n", ] + \
6712ef
+  #                  make_diff_prop_header(iota) + \
6712ef
+  #                  make_diff_prop_deleted('svn:mime-type', 'text/plain')
6712ef
+  expected_output = None
6712ef
+  svntest.actions.run_and_verify_svn(None, expected_output, [],
6712ef
+                                     'diff', '--old', iota,
6712ef
+                                     '--new', iota + '@1')
6712ef
+
6712ef
+  # copy iota to test with --show-copies as adds
6712ef
+  sbox.simple_copy('iota', 'iota_copy')
6712ef
+  iota_copy = sbox.ospath('iota_copy')
6712ef
+
6712ef
+  # test that we get all lines as added and the property added
6712ef
+  # TODO: We only test that this test doesn't error out because of Issue #4464
6712ef
+  # if and when that issue is fixed this test should check output
6712ef
+  svntest.actions.run_and_verify_svn(None, None, [], 'diff',
6712ef
+                                     '--show-copies-as-adds', '-r1', iota_copy)
6712ef
+
6712ef
+  # reverse the diff, should get all lines as a delete and no property
6712ef
+  # TODO: We only test that this test doesn't error out because of Issue #4464
6712ef
+  # if and when that issue is fixed this test should check output
6712ef
+  svntest.actions.run_and_verify_svn(None, None, [], 'diff',
6712ef
+                                     '--show-copies-as-adds',
6712ef
+                                     '--old', iota_copy,
6712ef
+                                     '--new', iota + '@1')
6712ef
+
6712ef
+  # revert and commit with the eol-style of LF and then update so
6712ef
+  # that we can see a change on either windows or *nix.
6712ef
+  sbox.simple_revert('iota', 'iota_copy')
6712ef
+  sbox.simple_propset('svn:eol-style', 'LF', 'iota')
6712ef
+  sbox.simple_commit() #r2
6712ef
+  sbox.simple_update()
6712ef
+
6712ef
+  # now that we have a LF file on disk switch to CRLF
6712ef
+  sbox.simple_propset('svn:eol-style', 'CRLF', 'iota')
6712ef
+
6712ef
+  # test that not only the property but also the file changes
6712ef
+  # i.e. that the line endings substitution works
6712ef
+  if svntest.main.is_os_windows():
6712ef
+    # test suite normalizes crlf output into just lf on Windows.
6712ef
+    # so we have to assume it worked because there is an add and
6712ef
+    # remove line with the same content.  Fortunately, it does't
6712ef
+    # do this on *nix so we can be pretty sure that it works right.
6712ef
+    # TODO: Provide a way to handle this better
6712ef
+    crlf = '\n'
6712ef
+  else:
6712ef
+    crlf = '\r\n'
6712ef
+  expected_output = make_diff_header(iota, 'revision 1', 'working copy') + \
6712ef
+                    [ '@@ -1 +1 @@\n',
6712ef
+                      "-This is the file 'iota'.\n",
6712ef
+                      "+This is the file 'iota'." + crlf ] + \
6712ef
+                    make_diff_prop_header(iota) + \
6712ef
+                    make_diff_prop_added('svn:eol-style', 'CRLF')
6712ef
+
6712ef
+  svntest.actions.run_and_verify_svn(None, expected_output, [],
6712ef
+                                     'diff', '-r1', iota)
6712ef
+
6712ef
+
6712ef
+@Issue(4460)
6712ef
+def diff_repo_repo_added_file_mime_type(sbox):
6712ef
+    "diff repo to repo added file with mime-type"
6712ef
+    sbox.build()
6712ef
+    wc_dir = sbox.wc_dir
6712ef
+    newfile = sbox.ospath('newfile')
6712ef
+
6712ef
+    # add a file with a mime-type
6712ef
+    sbox.simple_append('newfile', "This is the file 'newfile'.\n")
6712ef
+    sbox.simple_add('newfile')
6712ef
+    sbox.simple_propset('svn:mime-type', 'text/plain', 'newfile')
6712ef
+    sbox.simple_commit() # r2
6712ef
+
6712ef
+    # try to diff across the addition
6712ef
+    expected_output = make_diff_header(newfile, 'revision 1', 'revision 2') + \
6712ef
+                      [ '@@ -0,0 +1 @@\n',
6712ef
+                        "+This is the file 'newfile'.\n" ] + \
6712ef
+                      make_diff_prop_header(newfile) + \
6712ef
+                      make_diff_prop_added('svn:mime-type', 'text/plain')
6712ef
+
6712ef
+    svntest.actions.run_and_verify_svn(None, expected_output, [], 'diff',
6712ef
+                                       '-r1:2', newfile)
6712ef
+
6712ef
+    # reverse the diff to diff across a deletion
6712ef
+    # Note no property delete is printed when whole file is deleted
6712ef
+    expected_output = make_diff_header(newfile, 'revision 2', 'revision 1') + \
6712ef
+                      [ '@@ -1, +0,0 @@\n',
6712ef
+                        "-This is the file 'newfile'.\n" ]
6712ef
+    svntest.actions.run_and_verify_svn(None, None, [], 'diff',
6712ef
+                                       '-r2:1', newfile)
6712ef
+
6712ef
 ########################################################################
6712ef
 #Run the tests
6712ef
 
6712ef
@@ -3935,6 +4074,8 @@
6712ef
               no_spurious_conflict,
6712ef
               diff_deleted_url,
6712ef
               diff_git_format_wc_wc_dir_mv,
6712ef
+              diff_repo_wc_file_props,
6712ef
+              diff_repo_repo_added_file_mime_type,
6712ef
               ]
6712ef
 
6712ef
 if __name__ == '__main__':
6712ef
--- subversion-1.7.14/subversion/libsvn_client/diff.c
6712ef
+++ subversion-1.7.14/subversion/libsvn_client/diff.c
6712ef
@@ -1892,6 +1892,7 @@
6712ef
   const char *file_abspath;
6712ef
   svn_stream_t *content;
6712ef
   apr_hash_t *prop_hash;
6712ef
+  svn_string_t *mimetype;
6712ef
 
6712ef
   SVN_ERR(svn_stream_open_unique(&content, &file_abspath, NULL,
6712ef
                                  svn_io_file_del_on_pool_cleanup,
6712ef
@@ -1900,13 +1901,13 @@
6712ef
                           &prop_hash, scratch_pool));
6712ef
   SVN_ERR(svn_stream_close(content));
6712ef
 
6712ef
+  mimetype = apr_hash_get(prop_hash, SVN_PROP_MIME_TYPE, APR_HASH_KEY_STRING);
6712ef
+
6712ef
   if (show_deletion)
6712ef
     {
6712ef
       SVN_ERR(callbacks->file_deleted(NULL, NULL,
6712ef
                                       target, file_abspath, empty_file,
6712ef
-                                      apr_hash_get(prop_hash,
6712ef
-                                                   SVN_PROP_MIME_TYPE,
6712ef
-                                                   APR_HASH_KEY_STRING),
6712ef
+                                      mimetype ? mimetype->data : NULL,
6712ef
                                       NULL,
6712ef
                                       make_regular_props_hash(
6712ef
                                         prop_hash, scratch_pool, scratch_pool),
6712ef
@@ -1917,8 +1918,7 @@
6712ef
       SVN_ERR(callbacks->file_added(NULL, NULL, NULL,
6712ef
                                     target, empty_file, file_abspath,
6712ef
                                     rev1, rev2, NULL,
6712ef
-                                    apr_hash_get(prop_hash, SVN_PROP_MIME_TYPE,
6712ef
-                                                 APR_HASH_KEY_STRING),
6712ef
+                                    mimetype ? mimetype->data : NULL,
6712ef
                                     NULL, SVN_INVALID_REVNUM,
6712ef
                                     make_regular_props_array(prop_hash,
6712ef
                                                              scratch_pool,
6712ef
@@ -2243,6 +2243,7 @@
6712ef
   apr_hash_t *file1_props = NULL;
6712ef
   apr_hash_t *file2_props;
6712ef
   svn_boolean_t is_copy = FALSE;
6712ef
+  svn_string_t *mimetype1, *mimetype2;
6712ef
 
6712ef
   /* Get content and props of file 1 (the remote file). */
6712ef
   SVN_ERR(svn_stream_open_unique(&file1_content, &file1_abspath, NULL,
6712ef
@@ -2292,6 +2293,7 @@
6712ef
     {
6712ef
       apr_hash_t *keywords = NULL;
6712ef
       svn_string_t *keywords_prop;
6712ef
+      svn_string_t *eol_prop;
6712ef
       svn_subst_eol_style_t eol_style;
6712ef
       const char *eol_str;
6712ef
 
6712ef
@@ -2299,10 +2301,10 @@
6712ef
                                 scratch_pool, scratch_pool));
6712ef
 
6712ef
       /* We might have to create a normalised version of the working file. */
6712ef
+      eol_prop = apr_hash_get(file2_props, SVN_PROP_EOL_STYLE,
6712ef
+                              APR_HASH_KEY_STRING);
6712ef
       svn_subst_eol_style_from_value(&eol_style, &eol_str,
6712ef
-                                     apr_hash_get(file2_props,
6712ef
-                                                  SVN_PROP_EOL_STYLE,
6712ef
-                                                  APR_HASH_KEY_STRING));
6712ef
+                                     eol_prop ? eol_prop->data : NULL);
6712ef
       keywords_prop = apr_hash_get(file2_props, SVN_PROP_KEYWORDS,
6712ef
                                    APR_HASH_KEY_STRING);
6712ef
       if (keywords_prop)
6712ef
@@ -2309,7 +2311,7 @@
6712ef
         SVN_ERR(svn_subst_build_keywords2(&keywords, keywords_prop->data,
6712ef
                                           NULL, NULL, 0, NULL,
6712ef
                                           scratch_pool));
6712ef
-      if (svn_subst_translation_required(eol_style, SVN_SUBST_NATIVE_EOL_STR,
6712ef
+      if (svn_subst_translation_required(eol_style, eol_str,
6712ef
                                          keywords, FALSE, TRUE))
6712ef
         {
6712ef
           svn_stream_t *working_content;
6712ef
@@ -2323,7 +2325,7 @@
6712ef
                                          svn_io_file_del_on_pool_cleanup,
6712ef
                                          scratch_pool, scratch_pool));
6712ef
           normalized_content = svn_subst_stream_translated(
6712ef
-                                 file2_content, SVN_SUBST_NATIVE_EOL_STR,
6712ef
+                                 file2_content, eol_str,
6712ef
                                  TRUE, keywords, FALSE, scratch_pool);
6712ef
           SVN_ERR(svn_stream_copy3(working_content, normalized_content,
6712ef
                                    ctx->cancel_func, ctx->cancel_baton,
6712ef
@@ -2331,42 +2333,46 @@
6712ef
         }
6712ef
     }
6712ef
 
6712ef
+  mimetype1 = file1_props ? apr_hash_get(file1_props, SVN_PROP_MIME_TYPE,
6712ef
+                                         APR_HASH_KEY_STRING)
6712ef
+                          : NULL;
6712ef
+  mimetype2 = apr_hash_get(file2_props, SVN_PROP_MIME_TYPE,
6712ef
+                           APR_HASH_KEY_STRING);
6712ef
+
6712ef
   if (kind1 == svn_node_file && !(show_copies_as_adds && is_copy))
6712ef
     {
6712ef
+      apr_array_header_t *propchanges;
6712ef
+
6712ef
       SVN_ERR(callbacks->file_opened(NULL, NULL, target,
6712ef
                                      reverse ? SVN_INVALID_REVNUM : rev,
6712ef
                                      callback_baton, scratch_pool));
6712ef
 
6712ef
       if (reverse)
6712ef
-        SVN_ERR(callbacks->file_changed(NULL, NULL, NULL, target,
6712ef
-                                        file2_abspath, file1_abspath,
6712ef
-                                        SVN_INVALID_REVNUM, rev,
6712ef
-                                        apr_hash_get(file2_props,
6712ef
-                                                     SVN_PROP_MIME_TYPE,
6712ef
-                                                     APR_HASH_KEY_STRING),
6712ef
-                                        apr_hash_get(file1_props,
6712ef
-                                                     SVN_PROP_MIME_TYPE,
6712ef
-                                                     APR_HASH_KEY_STRING),
6712ef
-                                        make_regular_props_array(
6712ef
-                                          file1_props, scratch_pool,
6712ef
-                                          scratch_pool),
6712ef
-                                        file2_props,
6712ef
-                                        callback_baton, scratch_pool));
6712ef
+        {
6712ef
+          SVN_ERR(svn_prop_diffs(&propchanges, file1_props, file2_props,
6712ef
+                                 scratch_pool));
6712ef
+
6712ef
+          SVN_ERR(callbacks->file_changed(NULL, NULL, NULL, target,
6712ef
+                                          file2_abspath, file1_abspath,
6712ef
+                                          SVN_INVALID_REVNUM, rev,
6712ef
+                                          mimetype2 ? mimetype2->data : NULL,
6712ef
+                                          mimetype1 ? mimetype1->data : NULL,
6712ef
+                                          propchanges, file2_props,
6712ef
+                                          callback_baton, scratch_pool));
6712ef
+        }
6712ef
       else
6712ef
-        SVN_ERR(callbacks->file_changed(NULL, NULL, NULL, target,
6712ef
-                                        file1_abspath, file2_abspath,
6712ef
-                                        rev, SVN_INVALID_REVNUM,
6712ef
-                                        apr_hash_get(file1_props,
6712ef
-                                                     SVN_PROP_MIME_TYPE,
6712ef
-                                                     APR_HASH_KEY_STRING),
6712ef
-                                        apr_hash_get(file2_props,
6712ef
-                                                     SVN_PROP_MIME_TYPE,
6712ef
-                                                     APR_HASH_KEY_STRING),
6712ef
-                                        make_regular_props_array(
6712ef
-                                          file2_props, scratch_pool,
6712ef
-                                          scratch_pool),
6712ef
-                                        file1_props,
6712ef
-                                        callback_baton, scratch_pool));
6712ef
+        {
6712ef
+          SVN_ERR(svn_prop_diffs(&propchanges, file2_props, file1_props,
6712ef
+                                 scratch_pool));
6712ef
+
6712ef
+          SVN_ERR(callbacks->file_changed(NULL, NULL, NULL, target,
6712ef
+                                          file1_abspath, file2_abspath,
6712ef
+                                          rev, SVN_INVALID_REVNUM,
6712ef
+                                          mimetype1 ? mimetype1->data : NULL,
6712ef
+                                          mimetype2 ? mimetype2->data : NULL,
6712ef
+                                          propchanges, file1_props,
6712ef
+                                          callback_baton, scratch_pool));
6712ef
+        }
6712ef
     }
6712ef
   else
6712ef
     {
6712ef
@@ -2374,9 +2380,7 @@
6712ef
         {
6712ef
           SVN_ERR(callbacks->file_deleted(NULL, NULL,
6712ef
                                           target, file2_abspath, file1_abspath,
6712ef
-                                          apr_hash_get(file2_props,
6712ef
-                                                       SVN_PROP_MIME_TYPE,
6712ef
-                                                       APR_HASH_KEY_STRING),
6712ef
+                                          mimetype2 ? mimetype2->data : NULL,
6712ef
                                           NULL,
6712ef
                                           make_regular_props_hash(
6712ef
                                             file2_props, scratch_pool,
6712ef
@@ -2389,9 +2393,7 @@
6712ef
                                         file1_abspath, file2_abspath,
6712ef
                                         rev, SVN_INVALID_REVNUM,
6712ef
                                         NULL,
6712ef
-                                        apr_hash_get(file2_props,
6712ef
-                                                     SVN_PROP_MIME_TYPE,
6712ef
-                                                     APR_HASH_KEY_STRING),
6712ef
+                                        mimetype2 ? mimetype2->data : NULL,
6712ef
                                         NULL, SVN_INVALID_REVNUM,
6712ef
                                         make_regular_props_array(
6712ef
                                           file2_props, scratch_pool,