/* Produce a diff between two arbitrary files at LOCAL_ABSPATH1 and * LOCAL_ABSPATH2, using the diff callbacks from CALLBACKS. * Use PATH as the name passed to diff callbacks. * FILE1_IS_EMPTY and FILE2_IS_EMPTY are used as hints which diff callback * function to use to compare the files (added/deleted/changed). * * If ORIGINAL_PROPS_OVERRIDE is not NULL, use it as original properties * instead of reading properties from LOCAL_ABSPATH1. This is required when * a file replaces a directory, where LOCAL_ABSPATH1 is an empty file that * file content must be diffed against, but properties to diff against come * from the replaced directory. */ static svn_error_t * do_arbitrary_files_diff(const char *local_abspath1, const char *local_abspath2, const char *path, svn_boolean_t file1_is_empty, svn_boolean_t file2_is_empty, apr_hash_t *original_props_override, const svn_wc_diff_callbacks4_t *callbacks, void *diff_baton, svn_client_ctx_t *ctx, apr_pool_t *scratch_pool) { apr_hash_t *original_props; apr_hash_t *modified_props; apr_array_header_t *prop_changes; svn_string_t *original_mime_type = NULL; svn_string_t *modified_mime_type = NULL; if (ctx->cancel_func) SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); /* Try to get properties from either file. It's OK if the files do not * have properties, or if they are unversioned. */ if (original_props_override) original_props = original_props_override; else SVN_ERR(get_props(&original_props, local_abspath1, ctx->wc_ctx, scratch_pool, scratch_pool)); SVN_ERR(get_props(&modified_props, local_abspath2, ctx->wc_ctx, scratch_pool, scratch_pool)); SVN_ERR(svn_prop_diffs(&prop_changes, modified_props, original_props, scratch_pool)); /* Try to determine the mime-type of each file. */ original_mime_type = svn_hash_gets(original_props, SVN_PROP_MIME_TYPE); if (!file1_is_empty && !original_mime_type) { const char *mime_type; SVN_ERR(svn_io_detect_mimetype2(&mime_type, local_abspath1, ctx->mimetypes_map, scratch_pool)); if (mime_type) original_mime_type = svn_string_create(mime_type, scratch_pool); } modified_mime_type = svn_hash_gets(modified_props, SVN_PROP_MIME_TYPE); if (!file2_is_empty && !modified_mime_type) { const char *mime_type; SVN_ERR(svn_io_detect_mimetype2(&mime_type, local_abspath1, ctx->mimetypes_map, scratch_pool)); if (mime_type) modified_mime_type = svn_string_create(mime_type, scratch_pool); } /* Produce the diff. */ if (file1_is_empty && !file2_is_empty) SVN_ERR(callbacks->file_added(NULL, NULL, NULL, path, local_abspath1, local_abspath2, /* ### TODO get real revision info * for versioned files? */ SVN_INVALID_REVNUM, SVN_INVALID_REVNUM, original_mime_type ? original_mime_type->data : NULL, modified_mime_type ? modified_mime_type->data : NULL, /* ### TODO get copyfrom? */ NULL, SVN_INVALID_REVNUM, prop_changes, original_props, diff_baton, scratch_pool)); else if (!file1_is_empty && file2_is_empty) SVN_ERR(callbacks->file_deleted(NULL, NULL, path, local_abspath1, local_abspath2, original_mime_type ? original_mime_type->data : NULL, modified_mime_type ? modified_mime_type->data : NULL, original_props, diff_baton, scratch_pool)); else { svn_stream_t *file1; svn_stream_t *file2; svn_boolean_t same; svn_string_t *val; /* We have two files, which may or may not be the same. ### Our caller assumes that we should ignore symlinks here and handle them as normal paths. Perhaps that should change? */ SVN_ERR(svn_stream_open_readonly(&file1, local_abspath1, scratch_pool, scratch_pool)); SVN_ERR(svn_stream_open_readonly(&file2, local_abspath2, scratch_pool, scratch_pool)); /* Wrap with normalization, etc. if necessary */ if (original_props) { val = svn_hash_gets(original_props, SVN_PROP_EOL_STYLE); if (val) { svn_subst_eol_style_t style; const char *eol; svn_subst_eol_style_from_value(&style, &eol, val->data); /* ### Ignoring keywords */ if (eol) file1 = svn_subst_stream_translated(file1, eol, TRUE, NULL, FALSE, scratch_pool); } } if (modified_props) { val = svn_hash_gets(modified_props, SVN_PROP_EOL_STYLE); if (val) { svn_subst_eol_style_t style; const char *eol; svn_subst_eol_style_from_value(&style, &eol, val->data); /* ### Ignoring keywords */ if (eol) file2 = svn_subst_stream_translated(file2, eol, TRUE, NULL, FALSE, scratch_pool); } } SVN_ERR(svn_stream_contents_same2(&same, file1, file2, scratch_pool)); if (! same || prop_changes->nelts > 0) { /* ### We should probably pass the normalized data we created using the subst streams as that is what diff users expect */ SVN_ERR(callbacks->file_changed(NULL, NULL, NULL, path, same ? NULL : local_abspath1, same ? NULL : local_abspath2, /* ### TODO get real revision info * for versioned files? */ SVN_INVALID_REVNUM /* rev1 */, SVN_INVALID_REVNUM /* rev2 */, original_mime_type ? original_mime_type->data : NULL, modified_mime_type ? modified_mime_type->data : NULL, prop_changes, original_props, diff_baton, scratch_pool)); } } return SVN_NO_ERROR; }
/* Exercise the pristine text API with a simple write and read. */ static svn_error_t * pristine_write_read(const svn_test_opts_t *opts, apr_pool_t *pool) { svn_wc__db_t *db; const char *wc_abspath; svn_wc__db_install_data_t *install_data; svn_stream_t *pristine_stream; apr_size_t sz; const char data[] = "Blah"; svn_string_t *data_string = svn_string_create(data, pool); svn_checksum_t *data_sha1, *data_md5; SVN_ERR(create_repos_and_wc(&wc_abspath, &db, "pristine_write_read", opts, pool)); /* Write DATA into a new temporary pristine file, set PRISTINE_TMP_ABSPATH * to its path and set DATA_SHA1 and DATA_MD5 to its checksums. */ SVN_ERR(svn_wc__db_pristine_prepare_install(&pristine_stream, &install_data, &data_sha1, &data_md5, db, wc_abspath, pool, pool)); sz = strlen(data); SVN_ERR(svn_stream_write(pristine_stream, data, &sz)); SVN_ERR(svn_stream_close(pristine_stream)); /* Ensure it's not already in the store. */ { svn_boolean_t present; SVN_ERR(svn_wc__db_pristine_check(&present, db, wc_abspath, data_sha1, pool)); SVN_TEST_ASSERT(! present); } /* Install the new pristine file, referenced by its checksum. */ SVN_ERR(svn_wc__db_pristine_install(install_data, data_sha1, data_md5, pool)); /* Ensure it is now found in the store. */ { svn_boolean_t present; SVN_ERR(svn_wc__db_pristine_check(&present, db, wc_abspath, data_sha1, pool)); SVN_TEST_ASSERT(present); } /* Look up its MD-5 from its SHA-1, and check it's the same MD-5. */ { const svn_checksum_t *looked_up_md5; SVN_ERR(svn_wc__db_pristine_get_md5(&looked_up_md5, db, wc_abspath, data_sha1, pool, pool)); SVN_TEST_ASSERT(looked_up_md5->kind == svn_checksum_md5); SVN_TEST_ASSERT(svn_checksum_match(data_md5, looked_up_md5)); } /* Read the pristine text back and verify it's the same content. */ { svn_stream_t *data_stream = svn_stream_from_string(data_string, pool); svn_stream_t *data_read_back; svn_boolean_t same; SVN_ERR(svn_wc__db_pristine_read(&data_read_back, NULL, db, wc_abspath, data_sha1, pool, pool)); SVN_ERR(svn_stream_contents_same2(&same, data_read_back, data_stream, pool)); SVN_TEST_ASSERT(same); } /* Trivially test the "remove if unreferenced" API: it's not referenced so we should be able to remove it. */ { svn_error_t *err; svn_stream_t *data_read_back; SVN_ERR(svn_wc__db_pristine_remove(db, wc_abspath, data_sha1, pool)); err = svn_wc__db_pristine_read(&data_read_back, NULL, db, wc_abspath, data_sha1, pool, pool); SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_PATH_NOT_FOUND); } /* Ensure it's no longer found in the store. */ { svn_boolean_t present; SVN_ERR(svn_wc__db_pristine_check(&present, db, wc_abspath, data_sha1, pool)); SVN_TEST_ASSERT(! present); } return SVN_NO_ERROR; }