Return-Path: X-Original-To: apmail-subversion-commits-archive@minotaur.apache.org Delivered-To: apmail-subversion-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 8B5FE1021F for ; Tue, 3 Jun 2014 14:07:44 +0000 (UTC) Received: (qmail 97359 invoked by uid 500); 3 Jun 2014 14:07:44 -0000 Delivered-To: apmail-subversion-commits-archive@subversion.apache.org Received: (qmail 97332 invoked by uid 500); 3 Jun 2014 14:07:44 -0000 Mailing-List: contact commits-help@subversion.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@subversion.apache.org Delivered-To: mailing list commits@subversion.apache.org Received: (qmail 97325 invoked by uid 99); 3 Jun 2014 14:07:44 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 03 Jun 2014 14:07:44 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 03 Jun 2014 14:07:42 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 8D399238896F; Tue, 3 Jun 2014 14:07:22 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1599552 - in /subversion/trunk/subversion: include/svn_diff.h libsvn_client/diff.c libsvn_diff/binary_diff.c Date: Tue, 03 Jun 2014 14:07:22 -0000 To: commits@subversion.apache.org From: rhuijben@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20140603140722.8D399238896F@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org 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 + +#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