std::string Client::diff(const Path & tmpPath, const Path & path, const Revision & revision1, const Revision & revision2, const bool recurse, const bool ignoreAncestry, const bool noDiffDeleted) throw(ClientException) { Pool pool; svn_error_t * error; apr_status_t status; apr_file_t * outfile = nullptr; const char * outfileName = nullptr; apr_file_t * errfile = nullptr; const char * errfileName = nullptr; apr_array_header_t * options; svn_stringbuf_t * stringbuf; // svn_client_diff needs an options array, even if it is empty options = apr_array_make(pool, 0, 0); // svn_client_diff needs a temporary file to write diff output to error = svn_io_open_unique_file(&outfile, &outfileName, tmpPath.c_str(), ".tmp", false, pool); if (error != nullptr) { diffCleanup(outfile, outfileName, errfile, errfileName, pool); throw ClientException(error); } // and another one to write errors to error = svn_io_open_unique_file(&errfile, &errfileName, tmpPath.c_str(), ".tmp", false, pool); if (error != nullptr) { diffCleanup(outfile, outfileName, errfile, errfileName, pool); throw ClientException(error); } // run diff error = svn_client_diff(options, path.c_str(), revision1.revision(), path.c_str(), revision2.revision(), recurse, ignoreAncestry, noDiffDeleted, outfile, errfile, *m_context, pool); if (error != nullptr) { diffCleanup(outfile, outfileName, errfile, errfileName, pool); throw ClientException(error); } // then we reopen outfile for reading status = apr_file_close(outfile); if (status) { diffCleanup(outfile, outfileName, errfile, errfileName, pool); fail(pool, status, "failed to close '%s'", outfileName); } status = apr_file_open(&outfile, outfileName, APR_READ, APR_OS_DEFAULT, pool); if (status) { diffCleanup(outfile, outfileName, errfile, errfileName, pool); fail(pool, status, "failed to open '%s'", outfileName); } // now we can read the diff output from outfile and return that error = svn_stringbuf_from_aprfile(&stringbuf, outfile, pool); if (error != nullptr) { diffCleanup(outfile, outfileName, errfile, errfileName, pool); throw ClientException(error); } diffCleanup(outfile, outfileName, errfile, errfileName, pool); return stringbuf->data; }
Py::Object pysvn_client::cmd_diff( const Py::Tuple &a_args, const Py::Dict &a_kws ) { static argument_description args_desc[] = { { true, name_tmp_path }, { true, name_url_or_path }, { false, name_revision1 }, { false, name_url_or_path2 }, { false, name_revision2 }, { false, name_recurse }, { false, name_ignore_ancestry }, { false, name_diff_deleted }, #if defined( PYSVN_HAS_CLIENT_DIFF2 ) { false, name_ignore_content_type }, #endif #if defined( PYSVN_HAS_CLIENT_DIFF3 ) { false, name_header_encoding }, { false, name_diff_options }, #endif #if defined( PYSVN_HAS_CLIENT_DIFF4 ) { false, name_depth }, { false, name_relative_to_dir }, { false, name_changelists }, #endif { false, NULL } }; FunctionArguments args( "diff", args_desc, a_args, a_kws ); args.check(); std::string tmp_path( args.getUtf8String( name_tmp_path ) ); std::string path1( args.getUtf8String( name_url_or_path ) ); svn_opt_revision_t revision1 = args.getRevision( name_revision1, svn_opt_revision_base ); std::string path2( args.getUtf8String( name_url_or_path2, path1 ) ); svn_opt_revision_t revision2 = args.getRevision( name_revision2, svn_opt_revision_working ); #if defined( PYSVN_HAS_CLIENT_DIFF4 ) svn_depth_t depth = args.getDepth( name_depth, name_recurse, svn_depth_infinity, svn_depth_infinity, svn_depth_files ); #else bool recurse = args.getBoolean( name_recurse, true ); #endif bool ignore_ancestry = args.getBoolean( name_ignore_ancestry, true ); bool diff_deleted = args.getBoolean( name_diff_deleted, true ); #if defined( PYSVN_HAS_CLIENT_DIFF2 ) bool ignore_content_type = args.getBoolean( name_ignore_content_type, false ); #endif SvnPool pool( m_context ); #if defined( PYSVN_HAS_CLIENT_DIFF3 ) std::string header_encoding( args.getUtf8String( name_header_encoding, empty_string ) ); const char *header_encoding_ptr = APR_LOCALE_CHARSET; if( !header_encoding.empty() ) header_encoding_ptr = header_encoding.c_str(); apr_array_header_t *options = NULL; if( args.hasArg( name_diff_options ) ) { options = arrayOfStringsFromListOfStrings( args.getArg( name_diff_options ), pool ); } else { options = apr_array_make( pool, 0, sizeof( const char * ) ); } #else apr_array_header_t *options = apr_array_make( pool, 0, sizeof( const char * ) ); #endif #if defined( PYSVN_HAS_CLIENT_DIFF4 ) std::string std_relative_to_dir; const char *relative_to_dir = NULL; if( args.hasArg( name_relative_to_dir ) ) { std_relative_to_dir = args.getUtf8String( name_relative_to_dir ); relative_to_dir = std_relative_to_dir.c_str(); } apr_array_header_t *changelists = NULL; if( args.hasArg( name_changelists ) ) { changelists = arrayOfStringsFromListOfStrings( args.getArg( name_changelists ), pool ); } #endif svn_stringbuf_t *stringbuf = NULL; try { std::string norm_tmp_path( svnNormalisedIfPath( tmp_path, pool ) ); std::string norm_path1( svnNormalisedIfPath( path1, pool ) ); std::string norm_path2( svnNormalisedIfPath( path2, pool ) ); checkThreadPermission(); pysvn_apr_file output_file( pool ); pysvn_apr_file error_file( pool ); output_file.open_unique_file( norm_tmp_path ); error_file.open_unique_file( norm_tmp_path ); PythonAllowThreads permission( m_context ); #if defined( PYSVN_HAS_CLIENT_DIFF4 ) svn_error_t *error = svn_client_diff4 ( options, norm_path1.c_str(), &revision1, norm_path2.c_str(), &revision2, relative_to_dir, depth, ignore_ancestry, !diff_deleted, ignore_content_type, header_encoding_ptr, output_file.file(), error_file.file(), changelists, m_context, pool ); #elif defined( PYSVN_HAS_CLIENT_DIFF3 ) svn_error_t *error = svn_client_diff3 ( options, norm_path1.c_str(), &revision1, norm_path2.c_str(), &revision2, recurse, ignore_ancestry, !diff_deleted, ignore_content_type, header_encoding_ptr, output_file.file(), error_file.file(), m_context, pool ); #elif defined( PYSVN_HAS_CLIENT_DIFF2 ) svn_error_t *error = svn_client_diff2 ( options, norm_path1.c_str(), &revision1, norm_path2.c_str(), &revision2, recurse, ignore_ancestry, !diff_deleted, ignore_content_type, output_file.file(), error_file.file(), m_context, pool ); #else svn_error_t *error = svn_client_diff ( options, norm_path1.c_str(), &revision1, norm_path2.c_str(), &revision2, recurse, ignore_ancestry, !diff_deleted, output_file.file(), error_file.file(), m_context, pool ); #endif permission.allowThisThread(); if( error != NULL ) throw SvnException( error ); output_file.close(); output_file.open_tmp_file(); error = svn_stringbuf_from_aprfile( &stringbuf, output_file.file(), pool ); if( error != NULL ) throw SvnException( error ); } catch( SvnException &e ) { // use callback error over ClientException m_context.checkForError( m_module.client_error ); throw_client_error( e ); } // cannot convert to Unicode as we have no idea of the encoding of the bytes return Py::String( stringbuf->data, (int)stringbuf->len ); }