std::string Client::diff(const Path & tmpPath, const Path & path, const Revision & pegRevision, 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_peg(options, path.c_str(), pegRevision.revision(), revision1.revision(), 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_peg( 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_peg_revision }, { false, name_revision_start }, { false, name_revision_end }, { false, name_recurse }, { false, name_ignore_ancestry }, { false, name_diff_deleted }, #if defined( PYSVN_HAS_CLIENT_DIFF_PEG2 ) { false, name_ignore_content_type }, #endif #if defined( PYSVN_HAS_CLIENT_DIFF_PEG3 ) { false, name_header_encoding }, { false, name_diff_options }, #endif #if defined( PYSVN_HAS_CLIENT_DIFF_PEG4 ) { false, name_depth }, { false, name_relative_to_dir }, { false, name_changelists }, #endif { false, NULL } }; FunctionArguments args( "diff_peg", args_desc, a_args, a_kws ); args.check(); std::string tmp_path( args.getUtf8String( name_tmp_path ) ); std::string path( args.getUtf8String( name_url_or_path ) ); svn_opt_revision_t revision_start = args.getRevision( name_revision_start, svn_opt_revision_base ); svn_opt_revision_t revision_end = args.getRevision( name_revision_end, svn_opt_revision_working ); svn_opt_revision_t peg_revision = args.getRevision( name_peg_revision, revision_end ); SvnPool pool( m_context ); #if defined( PYSVN_HAS_CLIENT_DIFF_PEG4 ) svn_depth_t depth = args.getDepth( name_depth, name_recurse, svn_depth_infinity, svn_depth_infinity, svn_depth_files ); std::string std_relative_to_dir; const char *relative_to_dir = NULL; if( args.hasArg( name_relative_to_dir ) ) { std_relative_to_dir = args.getBytes( 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 ); } #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_DIFF_PEG2 ) bool ignore_content_type = args.getBoolean( name_ignore_content_type, false ); #endif #if defined( PYSVN_HAS_CLIENT_DIFF_PEG3 ) 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 bool is_url = is_svn_url( path ); revisionKindCompatibleCheck( is_url, peg_revision, name_peg_revision, name_url_or_path ); revisionKindCompatibleCheck( is_url, revision_start, name_revision_start, name_url_or_path ); revisionKindCompatibleCheck( is_url, revision_end, name_revision_end, name_url_or_path ); svn_stringbuf_t *stringbuf = NULL; try { std::string norm_tmp_path( svnNormalisedIfPath( tmp_path, pool ) ); std::string norm_path( svnNormalisedIfPath( path, pool ) ); checkThreadPermission(); PythonAllowThreads permission( m_context ); 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 ); // std::cout << "peg_revision " << peg_revision.kind << " " << peg_revision.value.number << std::endl; // std::cout << "revision_start " << revision_start.kind << " " << revision_start.value.number << std::endl; // std::cout << "revision_end " << revision_end.kind << " " << revision_end.value.number << std::endl; #if defined( PYSVN_HAS_CLIENT_DIFF_PEG4 ) svn_error_t *error = svn_client_diff_peg4 ( options, norm_path.c_str(), &peg_revision, &revision_start, &revision_end, 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_DIFF_PEG3 ) svn_error_t *error = svn_client_diff_peg3 ( options, norm_path.c_str(), &peg_revision, &revision_start, &revision_end, 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_DIFF_PEG2 ) svn_error_t *error = svn_client_diff_peg2 ( options, norm_path.c_str(), &peg_revision, &revision_start, &revision_end, 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_peg ( options, norm_path.c_str(), &peg_revision, &revision_start, &revision_end, 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 ); }