Exemple #1
0
/* 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;
}