예제 #1
0
/* This implements svn_editor_cb_complete_t */
static svn_error_t *
complete_cb(void *baton,
            apr_pool_t *scratch_pool)
{
  struct editor_baton *eb = baton;
  apr_array_header_t *sorted_hash;
  int i;

  /* Sort the paths touched by this edit.
   * Ev2 doesn't really have any particular need for depth-first-ness, but
   * we want to ensure all parent directories are handled before children in
   * the case of adds (which does introduce an element of depth-first-ness). */
  sorted_hash = svn_sort__hash(eb->paths, svn_sort_compare_items_as_paths,
                               scratch_pool);

  for (i = 0; i < sorted_hash->nelts; i++)
    {
      svn_sort__item_t *item = &APR_ARRAY_IDX(sorted_hash, i, svn_sort__item_t);
      const char *path = item->key;

      /* ### We should actually do something here, but for now... */
    }

  return svn_error_trace(eb->deditor->close_edit(eb->dedit_baton,
                                                 scratch_pool));
}
예제 #2
0
static svn_error_t *
print_properties_xml(const char *pname,
                     apr_hash_t *props,
                     apr_pool_t *pool)
{
  apr_array_header_t *sorted_props;
  int i;
  apr_pool_t *iterpool = svn_pool_create(pool);

  sorted_props = svn_sort__hash(props, svn_sort_compare_items_as_paths, pool);
  for (i = 0; i < sorted_props->nelts; i++)
    {
      svn_sort__item_t item = APR_ARRAY_IDX(sorted_props, i, svn_sort__item_t);
      const char *filename = item.key;
      svn_string_t *propval = item.value;
      svn_stringbuf_t *sb = NULL;

      svn_pool_clear(iterpool);

      svn_xml_make_open_tag(&sb, iterpool, svn_xml_normal, "target",
                        "path", filename, NULL);
      svn_cmdline__print_xml_prop(&sb, pname, propval, iterpool);
      svn_xml_make_close_tag(&sb, iterpool, "target");

      SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout));
    }

  svn_pool_destroy(iterpool);

  return SVN_NO_ERROR;
}
예제 #3
0
static svn_error_t *
ev2_close_edit(void *edit_baton,
               apr_pool_t *scratch_pool)
{
  struct ev2_edit_baton *eb = edit_baton;
  apr_array_header_t *sorted_hash;
  apr_pool_t *iterpool;
  int i;

  /* Sort the paths touched by this edit.
   * Ev2 doesn't really have any particular need for depth-first-ness, but
   * we want to ensure all parent directories are handled before children in
   * the case of adds (which does introduce an element of depth-first-ness). */
  sorted_hash = svn_sort__hash(eb->paths, svn_sort_compare_items_as_paths,
                               scratch_pool);

  iterpool = svn_pool_create(scratch_pool);
  for (i = 0; i < sorted_hash->nelts; i++)
    {
      svn_sort__item_t *item = &APR_ARRAY_IDX(sorted_hash, i, svn_sort__item_t);
      apr_array_header_t *actions = item->value;
      const char *path = item->key;

      svn_pool_clear(iterpool);
      SVN_ERR(process_actions(edit_baton, path, actions, iterpool));
    }
  svn_pool_destroy(iterpool);

  return svn_error_trace(svn_editor_complete(eb->editor));
}
예제 #4
0
파일: low_level.c 프로젝트: 2asoft/freebsd
svn_error_t *
svn_fs_fs__write_changes(svn_stream_t *stream,
                         svn_fs_t *fs,
                         apr_hash_t *changes,
                         svn_boolean_t terminate_list,
                         apr_pool_t *scratch_pool)
{
  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
  fs_fs_data_t *ffd = fs->fsap_data;
  svn_boolean_t include_node_kinds =
      ffd->format >= SVN_FS_FS__MIN_KIND_IN_CHANGED_FORMAT;
  svn_boolean_t include_mergeinfo_mods =
      ffd->format >= SVN_FS_FS__MIN_MERGEINFO_IN_CHANGED_FORMAT;
  apr_array_header_t *sorted_changed_paths;
  int i;

  /* For the sake of the repository administrator sort the changes so
     that the final file is deterministic and repeatable, however the
     rest of the FSFS code doesn't require any particular order here.

     Also, this sorting is only effective in writing all entries with
     a single call as write_final_changed_path_info() does.  For the
     list being written incrementally during transaction, we actually
     *must not* change the order of entries from different calls.
   */
  sorted_changed_paths = svn_sort__hash(changes,
                                        svn_sort_compare_items_lexically,
                                        scratch_pool);

  /* Write all items to disk in the new order. */
  for (i = 0; i < sorted_changed_paths->nelts; ++i)
    {
      svn_fs_path_change2_t *change;
      const char *path;

      svn_pool_clear(iterpool);

      change = APR_ARRAY_IDX(sorted_changed_paths, i, svn_sort__item_t).value;
      path = APR_ARRAY_IDX(sorted_changed_paths, i, svn_sort__item_t).key;

      /* Write out the new entry into the final rev-file. */
      SVN_ERR(write_change_entry(stream, path, change, include_node_kinds,
                                 include_mergeinfo_mods, iterpool));
    }

  if (terminate_list)
    svn_stream_puts(stream, "\n");

  svn_pool_destroy(iterpool);

  return SVN_NO_ERROR;
}
예제 #5
0
svn_error_t* CSVNLogQuery::LogReceiver ( void *baton
                                       , svn_log_entry_t *log_entry
                                       , apr_pool_t *pool)
{
    // a few globals

    static const std::string svnLog (SVN_PROP_REVISION_LOG);
    static const std::string svnDate (SVN_PROP_REVISION_DATE);
    static const std::string svnAuthor (SVN_PROP_REVISION_AUTHOR);

    // just in case ...

    if (log_entry == NULL)
        return NULL;

    // where to send the pre-processed in-format

    SBaton* receiverBaton = reinterpret_cast<SBaton*>(baton);
    ILogReceiver* receiver = receiverBaton->receiver;
    assert (receiver != NULL);

    // parse revprops

    std::string author;
    std::string message;
    __time64_t timeStamp = 0;

    UserRevPropArray userRevProps;

    try
    {
        if (   (log_entry->revision != SVN_INVALID_REVNUM)
            && (log_entry->revprops != NULL))
        {
            for ( apr_hash_index_t *index
                    = apr_hash_first (pool, log_entry->revprops)
                ; index != NULL
                ; index = apr_hash_next (index))
            {
                // extract next entry from hash

                const char* key = NULL;
                ptrdiff_t keyLen;
                const char** val = NULL;

                apr_hash_this ( index
                              , reinterpret_cast<const void**>(&key)
                              , &keyLen
                              , reinterpret_cast<void**>(&val));

                // decode / dispatch it

                std::string name = key;
                std::string value = *val;

                if (name == svnLog)
                    message = value;
                else if (name == svnAuthor)
                    author = value;
                else if (name == svnDate)
                {
                    timeStamp = NULL;
                    if (value[0])
                        SVN_ERR (svn_time_from_cstring (&timeStamp, *val, pool));
                }
                else
                {
                    userRevProps.Add (name, value);
                }
            }
        }
    }
    catch (CMemoryException * e)
    {
        e->Delete();
    }

    StandardRevProps standardRevProps (author, message, timeStamp);

    // the individual changes

    TChangedPaths changedPaths;
    try
    {
        if (log_entry->changed_paths2 != NULL)
        {
            apr_array_header_t *sorted_paths
                = svn_sort__hash (log_entry->changed_paths2, svn_sort_compare_items_as_paths, pool);

            for (int i = 0, count = sorted_paths->nelts; i < count; ++i)
            {
                changedPaths.push_back (SChangedPath());
                SChangedPath& entry = changedPaths.back();

                // find the item in the hash

                svn_sort__item_t *item = &(APR_ARRAY_IDX ( sorted_paths
                    , i
                    , svn_sort__item_t));

                // extract the path name

                entry.path = SVN::MakeUIUrlOrPath ((const char *)item->key);

                // decode the action

                svn_log_changed_path2_t *log_item
                    = (svn_log_changed_path2_t *) apr_hash_get ( log_entry->changed_paths2
                    , item->key
                    , item->klen);
                static const char actionKeys[7] = "AMRDVE";
                const char* actionKey = strchr (actionKeys, log_item->action);

                entry.action = actionKey == NULL
                    ? 0
                    : 1 << (actionKey - actionKeys);

                // node type

                entry.nodeKind = log_item->node_kind;

                // decode copy-from info

                if (    log_item->copyfrom_path
                     && SVN_IS_VALID_REVNUM (log_item->copyfrom_rev))
                {
                    entry.copyFromPath = SVN::MakeUIUrlOrPath (log_item->copyfrom_path);
                    entry.copyFromRev = log_item->copyfrom_rev;
                }
                else
                {
                    entry.copyFromRev = 0;
                }

                entry.text_modified = log_item->text_modified;
                entry.props_modified = log_item->props_modified;
            }
        }
        else if (log_entry->changed_paths != NULL)
        {
            apr_array_header_t *sorted_paths
                = svn_sort__hash (log_entry->changed_paths, svn_sort_compare_items_as_paths, pool);

            for (int i = 0, count = sorted_paths->nelts; i < count; ++i)
            {
                changedPaths.push_back (SChangedPath());
                SChangedPath& entry = changedPaths.back();

                // find the item in the hash

                svn_sort__item_t *item = &(APR_ARRAY_IDX ( sorted_paths
                                                         , i
                                                         , svn_sort__item_t));

                // extract the path name

                entry.path = SVN::MakeUIUrlOrPath ((const char *)item->key);

                // decode the action

                svn_log_changed_path_t *log_item
                    = (svn_log_changed_path_t *) apr_hash_get ( log_entry->changed_paths
                                                              , item->key
                                                              , item->klen);
                static const char actionKeys[7] = "AMRDVE";
                const char* actionKey = strchr (actionKeys, log_item->action);

                entry.action = actionKey == NULL
                    ? 0
                    : 1 << (actionKey - actionKeys);

                // node type

                entry.nodeKind = svn_node_unknown;

                // decode copy-from info

                if (    log_item->copyfrom_path
                     && SVN_IS_VALID_REVNUM (log_item->copyfrom_rev))
                {
                    entry.copyFromPath = SVN::MakeUIUrlOrPath (log_item->copyfrom_path);
                    entry.copyFromRev = log_item->copyfrom_rev;
                }
                else
                {
                    entry.copyFromRev = 0;
                }

                entry.text_modified = svn_tristate_unknown;
                entry.props_modified = svn_tristate_unknown;
            }
        }
    }
    catch (CMemoryException * e)
    {
        e->Delete();
    }

    // now, report the change

    try
    {
        // treat revision 0 special: only report/show it if either the author or a message is set, or if user props are set
        if (   log_entry->revision
            || userRevProps.GetCount()
            || !standardRevProps.GetAuthor().empty()
            || !standardRevProps.GetMessage().empty())
        {
            MergeInfo mergeInfo = { log_entry->has_children != FALSE
                                  , log_entry->non_inheritable != FALSE
                                  , log_entry->subtractive_merge != FALSE };

            receiver->ReceiveLog ( receiverBaton->includeChanges
                                       ? &changedPaths
                                       : NULL
                                 , log_entry->revision
                                 , receiverBaton->includeStandardRevProps
                                       ? &standardRevProps
                                       : NULL
                                 , receiverBaton->includeUserRevProps
                                       ? &userRevProps
                                       : NULL
                                 , &mergeInfo);
        }
    }
    catch (SVNError& e)
    {
        return svn_error_create (e.GetCode(), NULL, e.GetMessage());
    }
    catch (...)
    {
        // we must not leak exceptions back into SVN
    }

    return NULL;
}
예제 #6
0
/* Print the properties in PROPS to the stream OUT. PROPS is a hash mapping
 * (const char *) path to (svn_string_t) property value.
 * If IS_URL is true, all paths are URLs, else all paths are local paths.
 * PNAME_UTF8 is the property name of all the properties.
 * If PRINT_FILENAMES is true, print the item's path before each property.
 * If OMIT_NEWLINE is true, don't add a newline at the end of each property.
 * If LIKE_PROPLIST is true, print everything in a more verbose format
 * like "svn proplist -v" does.
 * */
static svn_error_t *
print_properties(svn_stream_t *out,
                 svn_boolean_t is_url,
                 const char *pname_utf8,
                 apr_hash_t *props,
                 svn_boolean_t print_filenames,
                 svn_boolean_t omit_newline,
                 svn_boolean_t like_proplist,
                 apr_pool_t *pool)
{
  apr_array_header_t *sorted_props;
  int i;
  apr_pool_t *iterpool = svn_pool_create(pool);
  const char *path_prefix;

  SVN_ERR(svn_dirent_get_absolute(&path_prefix, "", pool));

  sorted_props = svn_sort__hash(props, svn_sort_compare_items_as_paths, pool);
  for (i = 0; i < sorted_props->nelts; i++)
    {
      svn_sort__item_t item = APR_ARRAY_IDX(sorted_props, i, svn_sort__item_t);
      const char *filename = item.key;
      svn_string_t *propval = item.value;

      svn_pool_clear(iterpool);

      if (print_filenames)
        {
          const char *header;

          /* Print the file name. */

          if (! is_url)
            filename = svn_cl__local_style_skip_ancestor(path_prefix, filename,
                                                         iterpool);

          /* In verbose mode, print exactly same as "proplist" does;
           * otherwise, print a brief header. */
          header = apr_psprintf(iterpool, like_proplist
                                ? _("Properties on '%s':\n")
                                : "%s - ", filename);
          SVN_ERR(svn_cmdline_cstring_from_utf8(&header, header, iterpool));
          SVN_ERR(svn_subst_translate_cstring2(header, &header,
                                               APR_EOL_STR,  /* 'native' eol */
                                               FALSE, /* no repair */
                                               NULL,  /* no keywords */
                                               FALSE, /* no expansion */
                                               iterpool));
          SVN_ERR(stream_write(out, header, strlen(header)));
        }

      if (like_proplist)
        {
          /* Print the property name and value just as "proplist -v" does */
          apr_hash_t *hash = apr_hash_make(iterpool);

          apr_hash_set(hash, pname_utf8, APR_HASH_KEY_STRING, propval);
          SVN_ERR(svn_cl__print_prop_hash(out, hash, FALSE, iterpool));
        }
      else
        {
          /* If this is a special Subversion property, it is stored as
             UTF8, so convert to the native format. */
          if (svn_prop_needs_translation(pname_utf8))
            SVN_ERR(svn_subst_detranslate_string(&propval, propval,
                                                 TRUE, iterpool));

          SVN_ERR(stream_write(out, propval->data, propval->len));

          if (! omit_newline)
            SVN_ERR(stream_write(out, APR_EOL_STR,
                                 strlen(APR_EOL_STR)));
        }
    }

  svn_pool_destroy(iterpool);

  return SVN_NO_ERROR;
}
예제 #7
0
Py::Object pysvn_client::cmd_ls( const Py::Tuple &a_args, const Py::Dict &a_kws )
{
    static argument_description args_desc[] =
    {
    { true,  name_url_or_path },
    { false, name_revision },
    { false, name_recurse },
#if defined( PYSVN_HAS_CLIENT_LS2 )
    { false, name_peg_revision },
#endif
    { false, NULL }
    };
    FunctionArguments args( "ls", args_desc, a_args, a_kws );
    args.check();

    std::string path( args.getUtf8String( name_url_or_path ) );
    bool recurse = args.getBoolean( name_recurse, false );
    svn_opt_revision_t revision = args.getRevision( name_revision, svn_opt_revision_head );

    SvnPool pool( m_context );
    apr_hash_t *hash = NULL;
    std::string norm_path( svnNormalisedIfPath( path, pool ) );
#if defined( PYSVN_HAS_CLIENT_LS2 )
    svn_opt_revision_t peg_revision = args.getRevision( name_peg_revision, revision );
#endif

    bool is_url = is_svn_url( path );
#if defined( PYSVN_HAS_CLIENT_LS2 )
    revisionKindCompatibleCheck( is_url, peg_revision, name_peg_revision, name_url_or_path );
#endif
    revisionKindCompatibleCheck( is_url, revision, name_revision, name_url_or_path );

    try
    {
        checkThreadPermission();

        PythonAllowThreads permission( m_context );

#if defined( PYSVN_HAS_CLIENT_LS2 )
        svn_error_t *error = svn_client_ls2
            (
            &hash,
            norm_path.c_str(),
            &peg_revision,
            &revision,
            recurse,
            m_context,
            pool
            );
#else
        svn_error_t *error = svn_client_ls
            (
            &hash,
            norm_path.c_str(),
            &revision,
            recurse,
            m_context,
            pool
            );
#endif
        permission.allowThisThread();
        if( error != 0 )
            throw SvnException( error );
    }
    catch( SvnException &e )
    {
        // use callback error over ClientException
        m_context.checkForError( m_module.client_error );

        throw_client_error( e );
    }


    apr_array_header_t *array = svn_sort__hash( hash, compare_items_as_paths, pool );

    std::string base_path;
    if( !norm_path.empty() )
    {
        base_path = norm_path;
        base_path += '/';
    }

    // convert the entries into python objects
    Py::List entries_list;

    for( int i = 0; i < array->nelts; ++i )
    {
        svn_sort__item_t *item = &APR_ARRAY_IDX( array, i, svn_sort__item_t );

        const char *utf8_entryname = static_cast<const char *>( item->key );
        svn_dirent_t *dirent = static_cast<svn_dirent_t *>( apr_hash_get( hash, utf8_entryname, item->klen ) );

        std::string full_name( base_path );
        full_name += utf8_entryname;

        Py::Dict entry_dict;
        entry_dict[ *py_name_name ] = Py::String( full_name, name_utf8 );
        entry_dict[ *py_name_kind ] = toEnumValue( dirent->kind );
        entry_dict[ *py_name_has_props ] = Py::Int( dirent->has_props );
        entry_dict[ *py_name_size ] = Py::Long( Py::Float( double( static_cast<signed_int64>( dirent->size ) ) ) );
        entry_dict[ *py_name_created_rev ] = Py::asObject( new pysvn_revision( svn_opt_revision_number, 0, dirent->created_rev ) );
        entry_dict[ *py_name_time ] = toObject( dirent->time );
        entry_dict[ *py_name_last_author ] = utf8_string_or_none( dirent->last_author );

        entries_list.append( m_wrapper_dirent.wrapDict( entry_dict ) );
    }

    return entries_list;
}
예제 #8
0
/* Implements svn_hash_write2 and svn_hash_write_incremental. */
static svn_error_t *
hash_write(apr_hash_t *hash, apr_hash_t *oldhash, svn_stream_t *stream,
           const char *terminator, apr_pool_t *pool)
{
  apr_pool_t *subpool;
  apr_size_t len;
  apr_array_header_t *list;
  int i;

  subpool = svn_pool_create(pool);

  list = svn_sort__hash(hash, svn_sort_compare_items_lexically, pool);
  for (i = 0; i < list->nelts; i++)
    {
      svn_sort__item_t *item = &APR_ARRAY_IDX(list, i, svn_sort__item_t);
      svn_string_t *valstr = item->value;

      svn_pool_clear(subpool);

      /* Don't output entries equal to the ones in oldhash, if present. */
      if (oldhash)
        {
          svn_string_t *oldstr = apr_hash_get(oldhash, item->key, item->klen);

          if (oldstr && svn_string_compare(valstr, oldstr))
            continue;
        }

      /* Write it out. */
      SVN_ERR(svn_stream_printf(stream, subpool,
                                "K %" APR_SSIZE_T_FMT "\n%s\n"
                                "V %" APR_SIZE_T_FMT "\n",
                                item->klen, (const char *) item->key,
                                valstr->len));
      len = valstr->len;
      SVN_ERR(svn_stream_write(stream, valstr->data, &len));
      SVN_ERR(svn_stream_printf(stream, subpool, "\n"));
    }

  if (oldhash)
    {
      /* Output a deletion entry for each property in oldhash but not hash. */
      list = svn_sort__hash(oldhash, svn_sort_compare_items_lexically,
                            pool);
      for (i = 0; i < list->nelts; i++)
        {
          svn_sort__item_t *item = &APR_ARRAY_IDX(list, i, svn_sort__item_t);

          svn_pool_clear(subpool);

          /* If it's not present in the new hash, write out a D entry. */
          if (! apr_hash_get(hash, item->key, item->klen))
            SVN_ERR(svn_stream_printf(stream, subpool,
                                      "D %" APR_SSIZE_T_FMT "\n%s\n",
                                      item->klen, (const char *) item->key));
        }
    }

  if (terminator)
    SVN_ERR(svn_stream_printf(stream, subpool, "%s\n", terminator));

  svn_pool_destroy(subpool);
  return SVN_NO_ERROR;
}
예제 #9
0
/* Perform status operations on each external in EXTERNAL_MAP, a const char *
   local_abspath of all externals mapping to the const char* defining_abspath.
   All other options are the same as those passed to svn_client_status().

   If ANCHOR_ABSPATH and ANCHOR-RELPATH are not null, use them to provide
   properly formatted relative paths */
static svn_error_t *
do_external_status(svn_client_ctx_t *ctx,
                   apr_hash_t *external_map,
                   svn_depth_t depth,
                   svn_boolean_t get_all,
                   svn_boolean_t check_out_of_date,
                   svn_boolean_t check_working_copy,
                   svn_boolean_t no_ignore,
                   const apr_array_header_t *changelists,
                   const char *anchor_abspath,
                   const char *anchor_relpath,
                   svn_client_status_func_t status_func,
                   void *status_baton,
                   apr_pool_t *scratch_pool)
{
    apr_pool_t *iterpool = svn_pool_create(scratch_pool);
    apr_array_header_t *externals;
    int i;

    externals = svn_sort__hash(external_map, svn_sort_compare_items_lexically,
                               scratch_pool);

    /* Loop over the hash of new values (we don't care about the old
       ones).  This is a mapping of versioned directories to property
       values. */
    for (i = 0; i < externals->nelts; i++)
    {
        svn_node_kind_t external_kind;
        svn_sort__item_t item = APR_ARRAY_IDX(externals, i, svn_sort__item_t);
        const char *local_abspath = item.key;
        const char *defining_abspath = item.value;
        svn_node_kind_t kind;
        svn_opt_revision_t opt_rev;
        const char *status_path;

        svn_pool_clear(iterpool);

        /* Obtain information on the expected external. */
        SVN_ERR(svn_wc__read_external_info(&external_kind, NULL, NULL, NULL,
                                           &opt_rev.value.number,
                                           ctx->wc_ctx, defining_abspath,
                                           local_abspath, FALSE,
                                           iterpool, iterpool));

        if (external_kind != svn_node_dir)
            continue;

        SVN_ERR(svn_io_check_path(local_abspath, &kind, iterpool));
        if (kind != svn_node_dir)
            continue;

        if (SVN_IS_VALID_REVNUM(opt_rev.value.number))
            opt_rev.kind = svn_opt_revision_number;
        else
            opt_rev.kind = svn_opt_revision_unspecified;

        /* Tell the client we're starting an external status set. */
        if (ctx->notify_func2)
            ctx->notify_func2(
                ctx->notify_baton2,
                svn_wc_create_notify(local_abspath,
                                     svn_wc_notify_status_external,
                                     iterpool), iterpool);

        status_path = local_abspath;
        if (anchor_abspath)
        {
            status_path = svn_dirent_join(anchor_relpath,
                                          svn_dirent_skip_ancestor(anchor_abspath,
                                                  status_path),
                                          iterpool);
        }

        /* And then do the status. */
        SVN_ERR(svn_client_status6(NULL, ctx, status_path, &opt_rev, depth,
                                   get_all, check_out_of_date,
                                   check_working_copy, no_ignore,
                                   FALSE /* ignore_exernals */,
                                   FALSE /* depth_as_sticky */,
                                   changelists, status_func, status_baton,
                                   iterpool));
    }

    /* Destroy SUBPOOL and (implicitly) ITERPOOL. */
    svn_pool_destroy(iterpool);

    return SVN_NO_ERROR;
}
예제 #10
0
파일: log-cmd.c 프로젝트: niallo/mwbuild
/* Implement `svn_log_entry_receiver_t', printing the logs in
 * a human-readable and machine-parseable format.
 *
 * BATON is of type `struct log_receiver_baton'.
 *
 * First, print a header line.  Then if CHANGED_PATHS is non-null,
 * print all affected paths in a list headed "Changed paths:\n",
 * immediately following the header line.  Then print a newline
 * followed by the message body, unless BATON->omit_log_message is true.
 *
 * Here are some examples of the output:
 *
 * $ svn log -r1847:1846
 * ------------------------------------------------------------------------
 * rev 1847:  cmpilato | Wed 1 May 2002 15:44:26 | 7 lines
 *
 * Fix for Issue #694.
 *
 * * subversion/libsvn_repos/delta.c
 *   (delta_files): Rework the logic in this function to only call
 * send_text_deltas if there are deltas to send, and within that case,
 * only use a real delta stream if the caller wants real text deltas.
 *
 * ------------------------------------------------------------------------
 * rev 1846:  whoever | Wed 1 May 2002 15:23:41 | 1 line
 *
 * imagine an example log message here
 * ------------------------------------------------------------------------
 *
 * Or:
 *
 * $ svn log -r1847:1846 -v
 * ------------------------------------------------------------------------
 * rev 1847:  cmpilato | Wed 1 May 2002 15:44:26 | 7 lines
 * Changed paths:
 *    M /trunk/subversion/libsvn_repos/delta.c
 *
 * Fix for Issue #694.
 *
 * * subversion/libsvn_repos/delta.c
 *   (delta_files): Rework the logic in this function to only call
 * send_text_deltas if there are deltas to send, and within that case,
 * only use a real delta stream if the caller wants real text deltas.
 *
 * ------------------------------------------------------------------------
 * rev 1846:  whoever | Wed 1 May 2002 15:23:41 | 1 line
 * Changed paths:
 *    M /trunk/notes/fs_dumprestore.txt
 *    M /trunk/subversion/libsvn_repos/dump.c
 *
 * imagine an example log message here
 * ------------------------------------------------------------------------
 *
 * Or:
 *
 * $ svn log -r1847:1846 -q
 * ------------------------------------------------------------------------
 * rev 1847:  cmpilato | Wed 1 May 2002 15:44:26
 * ------------------------------------------------------------------------
 * rev 1846:  whoever | Wed 1 May 2002 15:23:41
 * ------------------------------------------------------------------------
 *
 * Or:
 *
 * $ svn log -r1847:1846 -qv
 * ------------------------------------------------------------------------
 * rev 1847:  cmpilato | Wed 1 May 2002 15:44:26
 * Changed paths:
 *    M /trunk/subversion/libsvn_repos/delta.c
 * ------------------------------------------------------------------------
 * rev 1846:  whoever | Wed 1 May 2002 15:23:41
 * Changed paths:
 *    M /trunk/notes/fs_dumprestore.txt
 *    M /trunk/subversion/libsvn_repos/dump.c
 * ------------------------------------------------------------------------
 *
 */
static svn_error_t *
log_entry_receiver(void *baton,
                   svn_log_entry_t *log_entry,
                   apr_pool_t *pool)
{
  struct log_receiver_baton *lb = baton;
  const char *author;
  const char *date;
  const char *message;

  /* Number of lines in the msg. */
  int lines;

  if (lb->cancel_func)
    SVN_ERR(lb->cancel_func(lb->cancel_baton));

  svn_compat_log_revprops_out(&author, &date, &message, log_entry->revprops);

  if (log_entry->revision == 0 && message == NULL)
    return SVN_NO_ERROR;

  if (! SVN_IS_VALID_REVNUM(log_entry->revision))
    {
      apr_array_pop(lb->merge_stack);
      return SVN_NO_ERROR;
    }

  /* ### See http://subversion.tigris.org/issues/show_bug.cgi?id=807
     for more on the fallback fuzzy conversions below. */

  if (author == NULL)
    author = _("(no author)");

  if (date && date[0])
    /* Convert date to a format for humans. */
    SVN_ERR(svn_cl__time_cstring_to_human_cstring(&date, date, pool));
  else
    date = _("(no date)");

  if (! lb->omit_log_message && message == NULL)
    message = "";

  SVN_ERR(svn_cmdline_printf(pool,
                             SEP_STRING "r%ld | %s | %s",
                             log_entry->revision, author, date));

  if (message != NULL)
    {
      lines = svn_cstring_count_newlines(message) + 1;
      SVN_ERR(svn_cmdline_printf(pool,
                                 (lines != 1)
                                 ? " | %d lines"
                                 : " | %d line", lines));
    }

  SVN_ERR(svn_cmdline_printf(pool, "\n"));

  if (log_entry->changed_paths)
    {
      apr_array_header_t *sorted_paths;
      int i;

      /* Get an array of sorted hash keys. */
      sorted_paths = svn_sort__hash(log_entry->changed_paths,
                                    svn_sort_compare_items_as_paths, pool);

      SVN_ERR(svn_cmdline_printf(pool,
                                 _("Changed paths:\n")));
      for (i = 0; i < sorted_paths->nelts; i++)
        {
          svn_sort__item_t *item = &(APR_ARRAY_IDX(sorted_paths, i,
                                                   svn_sort__item_t));
          const char *path = item->key;
          svn_log_changed_path_t *log_item
            = apr_hash_get(log_entry->changed_paths, item->key, item->klen);
          const char *copy_data = "";

          if (log_item->copyfrom_path
              && SVN_IS_VALID_REVNUM(log_item->copyfrom_rev))
            {
              copy_data
                = apr_psprintf(pool,
                               _(" (from %s:%ld)"),
                               log_item->copyfrom_path,
                               log_item->copyfrom_rev);
            }
          SVN_ERR(svn_cmdline_printf(pool, "   %c %s%s\n",
                                     log_item->action, path,
                                     copy_data));
        }
    }

  if (lb->merge_stack->nelts > 0)
    {
      int i;

      /* Print the result of merge line */
      SVN_ERR(svn_cmdline_printf(pool, _("Merged via:")));
      for (i = 0; i < lb->merge_stack->nelts; i++)
        {
          svn_revnum_t rev = APR_ARRAY_IDX(lb->merge_stack, i, svn_revnum_t);

          SVN_ERR(svn_cmdline_printf(pool, " r%ld%c", rev,
                                     i == lb->merge_stack->nelts - 1 ?
                                                                  '\n' : ','));
        }
    }

  if (message != NULL)
    {
      /* A blank line always precedes the log message. */
      SVN_ERR(svn_cmdline_printf(pool, "\n%s\n", message));
    }

  SVN_ERR(svn_cmdline_fflush(stdout));

  if (log_entry->has_children)
    APR_ARRAY_PUSH(lb->merge_stack, svn_revnum_t) = log_entry->revision;

  return SVN_NO_ERROR;
}
예제 #11
0
/**
 * Callback called for a single log message
 */
svn_error_t *
LogMessageCallback::singleMessage(svn_log_entry_t *log_entry, apr_pool_t *pool)
{
  JNIEnv *env = JNIUtil::getEnv();

  // The method id will not change during the time this library is
  // loaded, so it can be cached.
  static jmethodID sm_mid = 0;
  if (sm_mid == 0)
    {
      jclass clazz = env->FindClass(JAVA_PACKAGE"/LogMessageCallback");
      if (JNIUtil::isJavaExceptionThrown())
        return SVN_NO_ERROR;

      sm_mid = env->GetMethodID(clazz,
                                "singleMessage",
                                "([L"JAVA_PACKAGE"/ChangePath;"
                                "JLjava/util/Map;Z)V");
      if (JNIUtil::isJavaExceptionThrown())
        return SVN_NO_ERROR;

      env->DeleteLocalRef(clazz);
      if (JNIUtil::isJavaExceptionThrown())
        return SVN_NO_ERROR;
    }

  jclass clazzCP = env->FindClass(JAVA_PACKAGE"/ChangePath");
  if (JNIUtil::isJavaExceptionThrown())
    return SVN_NO_ERROR;

  static jmethodID midCP = 0;
  if (midCP == 0)
    {
      midCP = env->GetMethodID(clazzCP,
                               "<init>",
                               "(Ljava/lang/String;JLjava/lang/String;C)V");
      if (JNIUtil::isJavaExceptionThrown())
        return SVN_NO_ERROR;
    }

  jobjectArray jChangedPaths = NULL;
  if (log_entry->changed_paths)
    {
      apr_array_header_t *sorted_paths;
      int i;

      /* Get an array of sorted hash keys. */
      sorted_paths = svn_sort__hash(log_entry->changed_paths,
                                    svn_sort_compare_items_as_paths,
                                    pool);

      jChangedPaths = env->NewObjectArray(sorted_paths->nelts,
                                          clazzCP,
                                          NULL);

      for (i = 0; i < sorted_paths->nelts; ++i)
        {
          svn_sort__item_t *item = &(APR_ARRAY_IDX(sorted_paths, i,
                                                   svn_sort__item_t));
          const char *path = (const char *)item->key;
          svn_log_changed_path_t *log_item
            = (svn_log_changed_path_t *)
            apr_hash_get(log_entry->changed_paths, item->key, item->klen);

          jstring jpath = JNIUtil::makeJString(path);
          if (JNIUtil::isJavaExceptionThrown())
            return SVN_NO_ERROR;

          jstring jcopyFromPath =
            JNIUtil::makeJString(log_item->copyfrom_path);
          if (JNIUtil::isJavaExceptionThrown())
            return SVN_NO_ERROR;

          jlong jcopyFromRev = log_item->copyfrom_rev;
          jchar jaction = log_item->action;

          jobject cp = env->NewObject(clazzCP, midCP, jpath, jcopyFromRev,
                                      jcopyFromPath, jaction);
          if (JNIUtil::isJavaExceptionThrown())
            return SVN_NO_ERROR;

          env->SetObjectArrayElement(jChangedPaths, i, cp);
          if (JNIUtil::isJavaExceptionThrown())
            return SVN_NO_ERROR;

          env->DeleteLocalRef(cp);
          if (JNIUtil::isJavaExceptionThrown())
            return SVN_NO_ERROR;

          env->DeleteLocalRef(jpath);
          if (JNIUtil::isJavaExceptionThrown())
            return SVN_NO_ERROR;

          env->DeleteLocalRef(jcopyFromPath);
          if (JNIUtil::isJavaExceptionThrown())
            return SVN_NO_ERROR;
        }
    }

  jobject jrevprops = NULL;
  if (log_entry->revprops != NULL && apr_hash_count(log_entry->revprops) > 0)
    jrevprops = ProplistCallback::makeMapFromHash(log_entry->revprops, pool);

  env->CallVoidMethod(m_callback,
                      sm_mid,
                      jChangedPaths,
                      (jlong)log_entry->revision,
                      jrevprops,
                      (jboolean)log_entry->has_children);
  if (JNIUtil::isJavaExceptionThrown())
    return SVN_NO_ERROR;

  env->DeleteLocalRef(jChangedPaths);
  if (JNIUtil::isJavaExceptionThrown())
    return SVN_NO_ERROR;

  env->DeleteLocalRef(jrevprops);
  // No need to check for an exception here, because we return anyway.

  return SVN_NO_ERROR;
}
예제 #12
0
svn_error_t *
svn_client__prev_log_path (const char **prev_path_p,
                           char *action_p,
                           svn_revnum_t *copyfrom_rev_p,
                           apr_hash_t *changed_paths,
                           const char *path,
                           svn_node_kind_t kind,
                           svn_revnum_t revision,
                           apr_pool_t *pool)
{
  svn_log_changed_path_t *change;
  const char *prev_path = NULL;

  /* It's impossible to find the predecessor path of a NULL path. */
  assert(path);

  /* Initialize our return values for the action and copyfrom_rev in
     case we have an unhandled case later on. */
  if (action_p)
    *action_p = 'M';
  if (copyfrom_rev_p)
    *copyfrom_rev_p = SVN_INVALID_REVNUM;

  /* See if PATH was explicitly changed in this revision. */
  change = apr_hash_get (changed_paths, path, APR_HASH_KEY_STRING);
  if (change)
    {
      /* If PATH was not newly added in this revision, then it may or may
         not have also been part of a moved subtree.  In this case, set a
         default previous path, but still look through the parents of this
         path for a possible copy event. */
      if (change->action != 'A' && change->action != 'R')
        {
          prev_path = path;
        }
      else
        {
          /* PATH is new in this revision.  This means it cannot have been
             part of a copied subtree. */
          if (change->copyfrom_path)
            prev_path = apr_pstrdup (pool, change->copyfrom_path);
          else
            prev_path = NULL;
          
          *prev_path_p = prev_path;
          if (action_p)
            *action_p = change->action;
          if (copyfrom_rev_p)
            *copyfrom_rev_p = change->copyfrom_rev;
          return SVN_NO_ERROR;
        }
    }
  
  if (apr_hash_count (changed_paths))
    {
      /* The path was not explicitly changed in this revision.  The
         fact that we're hearing about this revision implies, then,
         that the path was a child of some copied directory.  We need
         to find that directory, and effectively "re-base" our path on
         that directory's copyfrom_path. */
      int i;
      apr_array_header_t *paths;

      /* Build a sorted list of the changed paths. */
      paths = svn_sort__hash (changed_paths,
                              svn_sort_compare_items_as_paths, pool);

      /* Now, walk the list of paths backwards, looking a parent of
         our path that has copyfrom information. */
      for (i = paths->nelts; i > 0; i--)
        {
          svn_sort__item_t item = APR_ARRAY_IDX (paths,
                                                 i - 1, svn_sort__item_t);
          const char *ch_path = item.key;
          int len = strlen (ch_path);

          /* See if our path is the child of this change path.  If
             not, keep looking.  */
          if (! ((strncmp (ch_path, path, len) == 0) && (path[len] == '/')))
            continue;

          /* Okay, our path *is* a child of this change path.  If
             this change was copied, we just need to apply the
             portion of our path that is relative to this change's
             path, to the change's copyfrom path.  Otherwise, this
             change isn't really interesting to us, and our search
             continues. */
          change = apr_hash_get (changed_paths, ch_path, len);
          if (change->copyfrom_path)
            {
              if (action_p)
                *action_p = change->action;
              if (copyfrom_rev_p)
                *copyfrom_rev_p = change->copyfrom_rev;
              prev_path = svn_path_join (change->copyfrom_path, 
                                         path + len + 1, pool);
              break;
            }
        }
    }

  /* If we didn't find what we expected to find, return an error.
     (Because directories bubble-up, we get a bunch of logs we might
     not want.  Be forgiving in that case.)  */
  if (! prev_path)
    {
      if (kind == svn_node_dir)
        prev_path = apr_pstrdup (pool, path);
      else
        return svn_error_createf (SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
                                  ("Missing changed-path information for "
                                    "'%s' in revision %ld"),
                                  path, revision);
    }
  
  *prev_path_p = prev_path;
  return SVN_NO_ERROR;
}
예제 #13
0
파일: diff_local.c 프로젝트: ceama/freebsd
/* Produce a diff of depth DEPTH for the directory at LOCAL_ABSPATH,
 * using information from the arbitrary_diff_walker_baton B.
 * LOCAL_ABSPATH is the path being crawled and can be on either side
 * of the diff depending on baton->recursing_within_added_subtree. */
static svn_error_t *
arbitrary_diff_this_dir(struct arbitrary_diff_walker_baton *b,
                        const char *local_abspath,
                        svn_depth_t depth,
                        apr_pool_t *scratch_pool)
{
  const char *local_abspath1;
  const char *local_abspath2;
  svn_node_kind_t kind1;
  svn_node_kind_t kind2;
  const char *child_relpath;
  apr_hash_t *dirents1;
  apr_hash_t *dirents2;
  apr_hash_t *merged_dirents;
  apr_array_header_t *sorted_dirents;
  int i;
  apr_pool_t *iterpool;

  if (b->recursing_within_adm_dir)
    {
      if (svn_dirent_skip_ancestor(b->adm_dir_abspath, local_abspath))
        return SVN_NO_ERROR;
      else
        {
          b->recursing_within_adm_dir = FALSE;
          b->adm_dir_abspath = NULL;
        }
    }
  else if (svn_wc_is_adm_dir(svn_dirent_basename(local_abspath, NULL),
                             scratch_pool))
    {
      b->recursing_within_adm_dir = TRUE;
      b->adm_dir_abspath = apr_pstrdup(b->pool, local_abspath);
      return SVN_NO_ERROR;
    }

  if (b->recursing_within_added_subtree)
    child_relpath = svn_dirent_skip_ancestor(b->root2_abspath, local_abspath);
  else
    child_relpath = svn_dirent_skip_ancestor(b->root1_abspath, local_abspath);
  if (!child_relpath)
    return SVN_NO_ERROR;

  local_abspath1 = svn_dirent_join(b->root1_abspath, child_relpath,
                                   scratch_pool);
  SVN_ERR(svn_io_check_resolved_path(local_abspath1, &kind1, scratch_pool));

  local_abspath2 = svn_dirent_join(b->root2_abspath, child_relpath,
                                   scratch_pool);
  SVN_ERR(svn_io_check_resolved_path(local_abspath2, &kind2, scratch_pool));

  if (depth > svn_depth_empty)
    {
      if (kind1 == svn_node_dir)
        SVN_ERR(svn_io_get_dirents3(&dirents1, local_abspath1,
                                    TRUE, /* only_check_type */
                                    scratch_pool, scratch_pool));
      else
        dirents1 = apr_hash_make(scratch_pool);
    }

  if (kind2 == svn_node_dir)
    {
      apr_hash_t *original_props;
      apr_hash_t *modified_props;
      apr_array_header_t *prop_changes;

      /* Show any property changes for this directory. */
      SVN_ERR(get_props(&original_props, local_abspath1, b->ctx->wc_ctx,
                        scratch_pool, scratch_pool));
      SVN_ERR(get_props(&modified_props, local_abspath2, b->ctx->wc_ctx,
                        scratch_pool, scratch_pool));
      SVN_ERR(svn_prop_diffs(&prop_changes, modified_props, original_props,
                             scratch_pool));
      if (prop_changes->nelts > 0)
        SVN_ERR(b->callbacks->dir_props_changed(NULL, NULL, child_relpath,
                                                FALSE /* was_added */,
                                                prop_changes, original_props,
                                                b->diff_baton,
                                                scratch_pool));

      if (depth > svn_depth_empty)
        {
          /* Read directory entries. */
          SVN_ERR(svn_io_get_dirents3(&dirents2, local_abspath2,
                                      TRUE, /* only_check_type */
                                      scratch_pool, scratch_pool));
        }
    }
  else if (depth > svn_depth_empty)
    dirents2 = apr_hash_make(scratch_pool);

  if (depth <= svn_depth_empty)
    return SVN_NO_ERROR;

  /* Compare dirents1 to dirents2 and show added/deleted/changed files. */
  merged_dirents = apr_hash_merge(scratch_pool, dirents1, dirents2,
                                  NULL, NULL);
  sorted_dirents = svn_sort__hash(merged_dirents,
                                  svn_sort_compare_items_as_paths,
                                  scratch_pool);
  iterpool = svn_pool_create(scratch_pool);
  for (i = 0; i < sorted_dirents->nelts; i++)
    {
      svn_sort__item_t elt = APR_ARRAY_IDX(sorted_dirents, i, svn_sort__item_t);
      const char *name = elt.key;
      svn_io_dirent2_t *dirent1;
      svn_io_dirent2_t *dirent2;
      const char *child1_abspath;
      const char *child2_abspath;

      svn_pool_clear(iterpool);

      if (b->ctx->cancel_func)
        SVN_ERR(b->ctx->cancel_func(b->ctx->cancel_baton));

      if (strcmp(name, SVN_WC_ADM_DIR_NAME) == 0)
        continue;

      dirent1 = svn_hash_gets(dirents1, name);
      if (!dirent1)
        {
          dirent1 = svn_io_dirent2_create(iterpool);
          dirent1->kind = svn_node_none;
        }
      dirent2 = svn_hash_gets(dirents2, name);
      if (!dirent2)
        {
          dirent2 = svn_io_dirent2_create(iterpool);
          dirent2->kind = svn_node_none;
        }

      child1_abspath = svn_dirent_join(local_abspath1, name, iterpool);
      child2_abspath = svn_dirent_join(local_abspath2, name, iterpool);

      if (dirent1->special)
        SVN_ERR(svn_io_check_resolved_path(child1_abspath, &dirent1->kind,
                                           iterpool));
      if (dirent2->special)
        SVN_ERR(svn_io_check_resolved_path(child1_abspath, &dirent2->kind,
                                           iterpool));

      if (dirent1->kind == svn_node_dir &&
          dirent2->kind == svn_node_dir)
        {
          if (depth == svn_depth_immediates)
            {
              /* Not using the walker, so show property diffs on these dirs. */
              SVN_ERR(do_arbitrary_dirs_diff(child1_abspath, child2_abspath,
                                             b->root1_abspath, b->root2_abspath,
                                             svn_depth_empty,
                                             b->callbacks, b->diff_baton,
                                             b->ctx, iterpool));
            }
          else
            {
              /* Either the walker will visit these directories (with
               * depth=infinity) and they will be processed as 'this dir'
               * later, or we're showing file children only (depth=files). */
              continue;
            }

        }

      /* Files that exist only in dirents1. */
      if (dirent1->kind == svn_node_file &&
          (dirent2->kind == svn_node_dir || dirent2->kind == svn_node_none))
        SVN_ERR(do_arbitrary_files_diff(child1_abspath, b->empty_file_abspath,
                                        svn_relpath_join(child_relpath, name,
                                                         iterpool),
                                        FALSE, TRUE, NULL,
                                        b->callbacks, b->diff_baton,
                                        b->ctx, iterpool));

      /* Files that exist only in dirents2. */
      if (dirent2->kind == svn_node_file &&
          (dirent1->kind == svn_node_dir || dirent1->kind == svn_node_none))
        {
          apr_hash_t *original_props;

          SVN_ERR(get_props(&original_props, child1_abspath, b->ctx->wc_ctx,
                            scratch_pool, scratch_pool));
          SVN_ERR(do_arbitrary_files_diff(b->empty_file_abspath, child2_abspath,
                                          svn_relpath_join(child_relpath, name,
                                                           iterpool),
                                          TRUE, FALSE, original_props,
                                          b->callbacks, b->diff_baton,
                                          b->ctx, iterpool));
        }

      /* Files that exist in dirents1 and dirents2. */
      if (dirent1->kind == svn_node_file && dirent2->kind == svn_node_file)
        SVN_ERR(do_arbitrary_files_diff(child1_abspath, child2_abspath,
                                        svn_relpath_join(child_relpath, name,
                                                         iterpool),
                                        FALSE, FALSE, NULL,
                                        b->callbacks, b->diff_baton,
                                        b->ctx, scratch_pool));

      /* Directories that only exist in dirents2. These aren't crawled
       * by this walker so we have to crawl them separately. */
      if (depth > svn_depth_files &&
          dirent2->kind == svn_node_dir &&
          (dirent1->kind == svn_node_file || dirent1->kind == svn_node_none))
        SVN_ERR(do_arbitrary_dirs_diff(child1_abspath, child2_abspath,
                                       b->root1_abspath, b->root2_abspath,
                                       depth <= svn_depth_immediates
                                         ? svn_depth_empty
                                         : svn_depth_infinity ,
                                       b->callbacks, b->diff_baton,
                                       b->ctx, iterpool));
    }

  svn_pool_destroy(iterpool);

  return SVN_NO_ERROR;
}
예제 #14
0
Py::Object pysvn_client::cmd_status( const Py::Tuple &a_args, const Py::Dict &a_kws )
{
    static argument_description args_desc[] =
    {
    { true,  name_path },
    { false, name_recurse },
    { false, name_get_all },
    { false, name_update },
    { false, name_ignore },
#if defined( PYSVN_HAS_CLIENT_STATUS2 )
    { false, name_ignore_externals },
#endif
#if defined( PYSVN_HAS_CLIENT_STATUS3 )
    { false, name_depth },
    { false, name_changelists },
#endif
    { false, NULL }
    };
    FunctionArguments args( "status", args_desc, a_args, a_kws );
    args.check();

    Py::String path( args.getUtf8String( name_path ) );

    SvnPool pool( m_context );

#if defined( PYSVN_HAS_CLIENT_STATUS3 )
    apr_array_header_t *changelists = NULL;

    if( args.hasArg( name_changelists ) )
    {
        changelists = arrayOfStringsFromListOfStrings( args.getArg( name_changelists ), pool );
    }

    svn_depth_t depth = args.getDepth( name_depth, name_recurse, svn_depth_infinity, svn_depth_infinity, svn_depth_immediates );
#else
    bool recurse = args.getBoolean( name_recurse, true );
#endif
    bool get_all = args.getBoolean( name_get_all, true );
    bool update = args.getBoolean( name_update, false );
    bool ignore = args.getBoolean( name_ignore, false );
#if defined( PYSVN_HAS_CLIENT_STATUS2 )
    bool ignore_externals = args.getBoolean( name_ignore_externals, false );
#endif

    apr_hash_t *status_hash = NULL;

    Py::List entries_list;
    try
    {
        std::string norm_path( svnNormalisedIfPath( path, pool ) );

        checkThreadPermission();

        PythonAllowThreads permission( m_context );

        svn_revnum_t revnum;
        svn_opt_revision_t rev = { svn_opt_revision_head, {0} };

        StatusEntriesBaton baton;

        status_hash = apr_hash_make( pool );
        baton.hash = status_hash;
        baton.pool = pool;

#if defined( PYSVN_HAS_CLIENT_STATUS4 )
        svn_error_t *error = svn_client_status4
            (
            &revnum,            // revnum
            norm_path.c_str(),  // path
            &rev,
            StatusEntriesFunc,  // status func
            &baton,             // status baton
            depth,
            get_all,
            update,
            !ignore,
            ignore_externals,
            changelists,
            m_context,
            pool
            );
#elif defined( PYSVN_HAS_CLIENT_STATUS3 )
        svn_error_t *error = svn_client_status3
            (
            &revnum,            // revnum
            norm_path.c_str(),  // path
            &rev,
            StatusEntriesFunc,  // status func
            &baton,             // status baton
            depth,
            get_all,
            update,
            !ignore,
            ignore_externals,
            changelists,
            m_context,
            pool
            );
#elif defined( PYSVN_HAS_CLIENT_STATUS2 )
        svn_error_t *error = svn_client_status2
            (
            &revnum,            // revnum
            norm_path.c_str(),  // path
            &rev,
            StatusEntriesFunc,  // status func
            &baton,             // status baton
            recurse,
            get_all,
            update,
            !ignore,
            ignore_externals,
            m_context,
            pool
            );
#else
        svn_error_t *error = svn_client_status
            (
            &revnum,            // revnum
            norm_path.c_str(),  // path
            &rev,
            StatusEntriesFunc,  // status func
            &baton,             // status baton
            recurse,
            get_all,
            update,
            !ignore,
            m_context,
            pool
            );
#endif
        permission.allowThisThread();
        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 );
    }

    apr_array_header_t *statusarray = svn_sort__hash( status_hash, svn_sort_compare_items_as_paths, pool );

    // Loop over array, printing each name/status-structure
    for (int i = statusarray->nelts-1; i >= 0; i--)
    {
        const svn_sort__item_t *item = &APR_ARRAY_IDX( statusarray, i, const svn_sort__item_t );
        pysvn_wc_status_t *status = (pysvn_wc_status_t *)item->value;

        entries_list.append( toObject(
                Py::String( osNormalisedPath( (const char *)item->key, pool ), "UTF-8" ),
                *status,
                pool,
                m_wrapper_status,
                m_wrapper_entry,
                m_wrapper_lock ) );
    }

    return entries_list;
}