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 2014/06/03 16:07:22 UTC

svn commit: r1599552 - in /subversion/trunk/subversion: include/svn_diff.h libsvn_client/diff.c libsvn_diff/binary_diff.c

Author: rhuijben
Date: Tue Jun  3 14:07:22 2014
New Revision: 1599552

URL: http://svn.apache.org/r1599552
Log:
Add some initial code to support creating git-like binary file diffs when
running svn diff --git.

Currently the output appears to be not 100% identical to that of git, but I'm
not sure if that is caused by different compression settings or by a bug in
the new code.

* subversion/include/svn_diff.h
  (svn_diff_output_binary): New function.

* subversion/libsvn_client/diff.c
  (diff_content_changed): Produce git binary diffs instead of an error when
    requested.

* subversion/libsvn_diff/binary_diff.c
  New file.
  (create_compressed,
   write_literal,
   svn_diff_output_binary): New functions.

Added:
    subversion/trunk/subversion/libsvn_diff/binary_diff.c   (with props)
Modified:
    subversion/trunk/subversion/include/svn_diff.h
    subversion/trunk/subversion/libsvn_client/diff.c

Modified: subversion/trunk/subversion/include/svn_diff.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_diff.h?rev=1599552&r1=1599551&r2=1599552&view=diff
==============================================================================
--- subversion/trunk/subversion/include/svn_diff.h (original)
+++ subversion/trunk/subversion/include/svn_diff.h Tue Jun  3 14:07:22 2014
@@ -749,7 +749,25 @@ svn_diff_file_output_merge(svn_stream_t 
                            svn_boolean_t display_resolved_conflicts,
                            apr_pool_t *pool);
 
-
+/** Creates a git-like binary diff hunk describing the differences between
+ * @a original and @a latest. It does this by either producing either the
+ * literal content of both versions in a compressed format, or by describing
+ * one way transforms.
+ *
+ * Either @a original or @a latest may be NULL to describe that the version
+ * didn't exist.
+ *
+ * Writes the ouput to @a output_stream.
+ *
+ * @since New in 1.9.
+ */
+svn_error_t *
+svn_diff_output_binary(svn_stream_t *output_stream,
+                       svn_stream_t *original,
+                       svn_stream_t *latest,
+                       svn_cancel_func_t cancel_func,
+                       void *cancel_baton,
+                       apr_pool_t *scratch_pool);
 
 /* Diffs on in-memory structures */
 

Modified: subversion/trunk/subversion/libsvn_client/diff.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/diff.c?rev=1599552&r1=1599551&r2=1599552&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/diff.c (original)
+++ subversion/trunk/subversion/libsvn_client/diff.c Tue Jun  3 14:07:22 2014
@@ -706,31 +706,50 @@ diff_content_changed(svn_boolean_t *wrot
 
       /* ### Print git diff headers. */
 
-      SVN_ERR(svn_stream_printf_from_utf8(outstream,
-               dwi->header_encoding, scratch_pool,
-               _("Cannot display: file marked as a binary type.%s"),
-               APR_EOL_STR));
+      if (dwi->use_git_diff_format)
+        {
+          svn_stream_t *left_stream;
+          svn_stream_t *right_stream;
+
+          /* ### We might miss some git headers? */
 
-      if (mt1_binary && !mt2_binary)
-        SVN_ERR(svn_stream_printf_from_utf8(outstream,
-                 dwi->header_encoding, scratch_pool,
-                 "svn:mime-type = %s" APR_EOL_STR, mimetype1));
-      else if (mt2_binary && !mt1_binary)
-        SVN_ERR(svn_stream_printf_from_utf8(outstream,
-                 dwi->header_encoding, scratch_pool,
-                 "svn:mime-type = %s" APR_EOL_STR, mimetype2));
-      else if (mt1_binary && mt2_binary)
+          SVN_ERR(svn_stream_open_readonly(&left_stream, tmpfile1,
+                                           scratch_pool, scratch_pool));
+          SVN_ERR(svn_stream_open_readonly(&right_stream, tmpfile2,
+                                           scratch_pool, scratch_pool));
+          SVN_ERR(svn_diff_output_binary(outstream,
+                                         left_stream, right_stream,
+                                         dwi->cancel_func, dwi->cancel_baton,
+                                         scratch_pool));
+        }
+      else
         {
-          if (strcmp(mimetype1, mimetype2) == 0)
+          SVN_ERR(svn_stream_printf_from_utf8(outstream,
+                   dwi->header_encoding, scratch_pool,
+                   _("Cannot display: file marked as a binary type.%s"),
+                   APR_EOL_STR));
+
+          if (mt1_binary && !mt2_binary)
             SVN_ERR(svn_stream_printf_from_utf8(outstream,
                      dwi->header_encoding, scratch_pool,
-                     "svn:mime-type = %s" APR_EOL_STR,
-                     mimetype1));
-          else
+                     "svn:mime-type = %s" APR_EOL_STR, mimetype1));
+          else if (mt2_binary && !mt1_binary)
             SVN_ERR(svn_stream_printf_from_utf8(outstream,
                      dwi->header_encoding, scratch_pool,
-                     "svn:mime-type = (%s, %s)" APR_EOL_STR,
-                     mimetype1, mimetype2));
+                     "svn:mime-type = %s" APR_EOL_STR, mimetype2));
+          else if (mt1_binary && mt2_binary)
+            {
+              if (strcmp(mimetype1, mimetype2) == 0)
+                SVN_ERR(svn_stream_printf_from_utf8(outstream,
+                         dwi->header_encoding, scratch_pool,
+                         "svn:mime-type = %s" APR_EOL_STR,
+                         mimetype1));
+              else
+                SVN_ERR(svn_stream_printf_from_utf8(outstream,
+                         dwi->header_encoding, scratch_pool,
+                         "svn:mime-type = (%s, %s)" APR_EOL_STR,
+                         mimetype1, mimetype2));
+            }
         }
 
       /* Exit early. */

Added: subversion/trunk/subversion/libsvn_diff/binary_diff.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_diff/binary_diff.c?rev=1599552&view=auto
==============================================================================
--- subversion/trunk/subversion/libsvn_diff/binary_diff.c (added)
+++ subversion/trunk/subversion/libsvn_diff/binary_diff.c Tue Jun  3 14:07:22 2014
@@ -0,0 +1,220 @@
+/*
+ * binary_diff.c:  handling of git like binary diffs
+ *
+ * ====================================================================
+ *    Licensed to the Apache Software Foundation (ASF) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The ASF licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ */
+
+#include <apr.h>
+
+#include "svn_pools.h"
+#include "svn_error.h"
+#include "svn_diff.h"
+#include "svn_types.h"
+
+/* Copies the data from ORIGINAL_STREAM to a temporary file, returning both
+   the original and compressed size. */
+static svn_error_t *
+create_compressed(apr_file_t **result,
+                  svn_filesize_t *full_size,
+                  svn_filesize_t *compressed_size,
+                  svn_stream_t *original_stream,
+                  svn_cancel_func_t cancel_func,
+                  void *cancel_baton,
+                  apr_pool_t *result_pool,
+                  apr_pool_t *scratch_pool)
+{
+  svn_stream_t *compressed;
+  svn_filesize_t bytes_read = 0;
+  apr_finfo_t finfo;
+  apr_size_t rd;
+
+  SVN_ERR(svn_io_open_uniquely_named(result, NULL, NULL, "diffgz",
+                                     NULL, svn_io_file_del_on_pool_cleanup,
+                                     result_pool, scratch_pool));
+
+  compressed = svn_stream_compressed(
+                  svn_stream_from_aprfile2(*result, TRUE, scratch_pool),
+                  scratch_pool);
+
+  if (original_stream)
+    do
+    {
+      char buffer[SVN_STREAM_CHUNK_SIZE];
+      rd = sizeof(buffer);
+
+      if (cancel_func)
+        SVN_ERR(cancel_func(cancel_baton));
+
+      SVN_ERR(svn_stream_read_full(original_stream, buffer, &rd));
+
+      bytes_read += rd;
+      SVN_ERR(svn_stream_write(compressed, buffer, &rd));
+    }
+    while(rd == SVN_STREAM_CHUNK_SIZE);
+  else
+    {
+      apr_size_t zero = 0;
+      SVN_ERR(svn_stream_write(compressed, NULL, &zero));
+    }
+
+  SVN_ERR(svn_stream_close(compressed)); /* Flush compression */
+
+  *full_size = bytes_read;
+  SVN_ERR(svn_io_file_info_get(&finfo, APR_FINFO_SIZE, *result, scratch_pool));
+  *compressed_size = finfo.size;
+
+  return SVN_NO_ERROR;
+}
+
+#define GIT_BASE85_CHUNKSIZE 52
+
+/* Git Base-85 table for write_literal */
+static const char b85str[] =
+    "0123456789"
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+    "abcdefghijklmnopqrstuvwxyz"
+    "!#$%&()*+-;<=>?@^_`{|}~";
+
+/* Git length encoding table for write_literal */
+static const char b85lenstr[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+    "abcdefghijklmnopqrstuvwxyz";
+
+/* Writes out a git-like literal output of the compressed data in
+   COMPRESSED_DATA to OUTPUT_STREAM, describing that its normal length is
+   UNCOMPRESSED_SIZE. */
+static svn_error_t *
+write_literal(svn_filesize_t uncompressed_size,
+              svn_stream_t *compressed_data,
+              svn_stream_t *output_stream,
+              svn_cancel_func_t cancel_func,
+              void *cancel_baton,
+              apr_pool_t *scratch_pool)
+{
+  apr_size_t rd;
+  SVN_ERR(svn_stream_seek(compressed_data, NULL)); /* Seek to start */
+
+  SVN_ERR(svn_stream_printf(output_stream, scratch_pool,
+                            "literal %" SVN_FILESIZE_T_FMT APR_EOL_STR,
+                            uncompressed_size));
+
+  do
+  {
+    char chunk[GIT_BASE85_CHUNKSIZE];
+    rd = sizeof(chunk);
+    const char *next;
+    int left;
+
+    if (cancel_func)
+      SVN_ERR(cancel_func(cancel_baton));
+
+    SVN_ERR(svn_stream_read_full(compressed_data, chunk, &rd));
+
+    {
+      apr_size_t one = 1;
+      SVN_ERR(svn_stream_write(output_stream, &b85lenstr[rd-1], &one));
+    }
+
+    left = rd;
+    next = chunk;
+    while (left)
+    {
+      char five[5];
+      unsigned info = 0;
+      int n;
+      apr_size_t five_sz;
+
+      /* Push 4 bytes into the 32 bit info, when available */
+      for (n = 24; n >= 0 && left; n -= 8, next++, left--)
+      {
+          info |= (*next) << n;
+      }
+
+      /* Write out info as base85 */
+      for (n = 4; n >= 0; n--)
+      {
+          five[n] = b85str[info % 85];
+          info /= 85;
+      }
+
+      five_sz = 5;
+      SVN_ERR(svn_stream_write(output_stream, five, &five_sz));
+    }
+
+    SVN_ERR(svn_stream_puts(output_stream, APR_EOL_STR));
+  }
+  while (rd == GIT_BASE85_CHUNKSIZE);
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_diff_output_binary(svn_stream_t *output_stream,
+                       svn_stream_t *original,
+                       svn_stream_t *latest,
+                       svn_cancel_func_t cancel_func,
+                       void *cancel_baton,
+                       apr_pool_t *scratch_pool)
+{
+  apr_file_t *original_apr;
+  svn_filesize_t original_full;
+  svn_filesize_t original_deflated;
+  apr_file_t *latest_apr;
+  svn_filesize_t latest_full;
+  svn_filesize_t latest_deflated;
+  apr_pool_t *subpool = svn_pool_create(scratch_pool);
+
+  SVN_ERR(create_compressed(&original_apr, &original_full, &original_deflated,
+                            original, cancel_func, cancel_baton,
+                            scratch_pool, subpool));
+  svn_pool_clear(subpool);
+
+  SVN_ERR(create_compressed(&latest_apr, &latest_full, &latest_deflated,
+                            latest,  cancel_func, cancel_baton,
+                            scratch_pool, subpool));
+  svn_pool_clear(subpool);
+
+  SVN_ERR(svn_stream_puts(output_stream, "GIT binary patch" APR_EOL_STR));
+
+  /* ### git would first calculate if a git-delta original->latest would be
+         shorter than the zipped data. For now lets assume that it is not
+         and just dump the literal data */
+  SVN_ERR(write_literal(original_full,
+                        svn_stream_from_aprfile2(original_apr, FALSE, subpool),
+                        output_stream,
+                        cancel_func, cancel_baton,
+                        scratch_pool));
+  svn_pool_clear(subpool);
+  SVN_ERR(svn_stream_puts(output_stream, APR_EOL_STR));
+
+  /* ### git would first calculate if a git-delta latest->original would be
+         shorter than the zipped data. For now lets assume that it is not
+         and just dump the literal data */
+  SVN_ERR(write_literal(latest_full,
+                        svn_stream_from_aprfile2(latest_apr, FALSE, subpool),
+                        output_stream,
+                        cancel_func, cancel_baton,
+                        scratch_pool));
+  svn_pool_destroy(subpool);
+
+  SVN_ERR(svn_stream_puts(output_stream, APR_EOL_STR));
+
+  return SVN_NO_ERROR;
+}
\ No newline at end of file

Propchange: subversion/trunk/subversion/libsvn_diff/binary_diff.c
------------------------------------------------------------------------------
    svn:eol-style = native