Esempio n. 1
0
/* Inspect the data and/or prop reps of revision REVNUM in FS.  Store
 * reference count tallies in passed hashes (allocated in RESULT_POOL).
 *
 * If PROP_REPS or DATA_REPS is NULL, the respective kind of reps are not
 * tallied.
 *
 * Print progress report to STDERR unless QUIET is true.
 *
 * Use SCRATCH_POOL for temporary allocations.
 */
static svn_error_t *
process_one_revision(svn_fs_t *fs,
                     svn_revnum_t revnum,
                     svn_boolean_t quiet,
                     apr_hash_t *prop_reps,
                     apr_hash_t *data_reps,
                     apr_hash_t *both_reps,
                     apr_pool_t *result_pool,
                     apr_pool_t *scratch_pool)
{
  svn_fs_root_t *rev_root;
  apr_hash_t *paths_changed;
  apr_hash_index_t *hi;

  if (! quiet)
    SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool,
                                "processing r%ld\n", revnum));

  /* Get the changed paths. */
  SVN_ERR(svn_fs_revision_root(&rev_root, fs, revnum, scratch_pool));
  SVN_ERR(svn_fs_paths_changed2(&paths_changed, rev_root, scratch_pool));

  /* Iterate them. */
  /* ### use iterpool? */
  for (hi = apr_hash_first(scratch_pool, paths_changed);
       hi; hi = apr_hash_next(hi))
    {
      const char *path;
      const svn_fs_path_change2_t *change;
      const svn_fs_id_t *node_rev_id1, *node_rev_id2;
      const svn_fs_id_t *the_id;

      node_revision_t *node_rev;

      path = svn__apr_hash_index_key(hi);
      change = svn__apr_hash_index_val(hi);
      if (! quiet)
        SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool,
                                    "processing r%ld:%s\n", revnum, path));

      if (change->change_kind == svn_fs_path_change_delete)
        /* Can't ask for reps of PATH at REVNUM if the path no longer exists
         * at that revision! */
        continue;

      /* Okay, we have two node_rev id's for this change: the txn one and
       * the revision one.  We'll use the latter. */
      node_rev_id1 = change->node_rev_id;
      SVN_ERR(svn_fs_node_id(&node_rev_id2, rev_root, path, scratch_pool));

      SVN_ERR_ASSERT(svn_fs_fs__id_txn_id(node_rev_id1) != NULL);
      SVN_ERR_ASSERT(svn_fs_fs__id_rev(node_rev_id2) != SVN_INVALID_REVNUM);

      the_id = node_rev_id2;

      /* Get the node_rev using the chosen node_rev_id. */
      SVN_ERR(svn_fs_fs__get_node_revision(&node_rev, fs, the_id, scratch_pool));

      /* Maybe record the sha1's. */
      SVN_ERR(record(prop_reps, node_rev->prop_rep, result_pool));
      SVN_ERR(record(data_reps, node_rev->data_rep, result_pool));
      SVN_ERR(record(both_reps, node_rev->prop_rep, result_pool));
      SVN_ERR(record(both_reps, node_rev->data_rep, result_pool));
    }

  return SVN_NO_ERROR;
}
Esempio n. 2
0
/* The caller wants to modify REVISION of FSPATH. Is that allowed?  */
static svn_error_t *
can_modify(svn_fs_root_t *txn_root,
           const char *fspath,
           svn_revnum_t revision,
           apr_pool_t *scratch_pool)
{
    svn_revnum_t created_rev;

    /* Out-of-dateness check:  compare the created-rev of the node
       in the txn against the created-rev of FSPATH.  */
    SVN_ERR(svn_fs_node_created_rev(&created_rev, txn_root, fspath,
                                    scratch_pool));

    /* Uncommitted nodes (eg. a descendent of a copy/move/rotate destination)
       have no (committed) revision number. Let the caller go ahead and
       modify these nodes.

       Note: strictly speaking, they might be performing an "illegal" edit
       in certain cases, but let's just assume they're Good Little Boys.

       If CREATED_REV is invalid, that means it's already mutable in the
       txn, which means it has already passed this out-of-dateness check.
       (Usually, this happens when looking at a parent directory of an
       already-modified node)  */
    if (!SVN_IS_VALID_REVNUM(created_rev))
        return SVN_NO_ERROR;

    /* If the node is immutable (has a revision), then the caller should
       have supplied a valid revision number [that they expect to change].
       The checks further below will determine the out-of-dateness of the
       specified revision.  */
    /* ### ugh. descendents of copy/move/rotate destinations carry along
       ### their original immutable state and (thus) a valid CREATED_REV.
       ### but they are logically uncommitted, so the caller will pass
       ### SVN_INVALID_REVNUM. (technically, the caller could provide
       ### ORIGINAL_REV, but that is semantically incorrect for the Ev2
       ### API).
       ###
       ### for now, we will assume the caller knows what they are doing
       ### and an invalid revision implies such a descendent. in the
       ### future, we could examine the ancestor chain looking for a
       ### copy/move/rotate-here node and allow the modification (and the
       ### converse: if no such ancestor, the caller must specify the
       ### correct/intended revision to modify).
    */
#if 1
    if (!SVN_IS_VALID_REVNUM(revision))
        return SVN_NO_ERROR;
#else
    if (!SVN_IS_VALID_REVNUM(revision))
        /* ### use a custom error code?  */
        return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
                                 _("Revision for modifying '%s' is required"),
                                 fspath);
#endif

    if (revision < created_rev)
    {
        /* We asked to change a node that is *older* than what we found
           in the transaction. The client is out of date.  */
        return svn_error_createf(SVN_ERR_FS_OUT_OF_DATE, NULL,
                                 _("'%s' is out of date; try updating"),
                                 fspath);
    }

    if (revision > created_rev)
    {
        /* We asked to change a node that is *newer* than what we found
           in the transaction. Given that the transaction was based off
           of 'youngest', then either:
           - the caller asked to modify a future node
           - the caller has committed more revisions since this txn
           was constructed, and is asking to modify a node in one
           of those new revisions.
           In either case, the node may not have changed in those new
           revisions; use the node's ID to determine this case.  */
        const svn_fs_id_t *txn_noderev_id;
        svn_fs_root_t *rev_root;
        const svn_fs_id_t *new_noderev_id;

        /* The ID of the node that we would be modifying in the txn  */
        SVN_ERR(svn_fs_node_id(&txn_noderev_id, txn_root, fspath,
                               scratch_pool));

        /* Get the ID from the future/new revision.  */
        SVN_ERR(svn_fs_revision_root(&rev_root, svn_fs_root_fs(txn_root),
                                     revision, scratch_pool));
        SVN_ERR(svn_fs_node_id(&new_noderev_id, rev_root, fspath,
                               scratch_pool));
        svn_fs_close_root(rev_root);

        /* Has the target node changed in the future?  */
        if (svn_fs_compare_ids(txn_noderev_id, new_noderev_id) != 0)
        {
            /* Restarting the commit will base the txn on the future/new
               revision, allowing the modification at REVISION.  */
            /* ### use a custom error code  */
            return svn_error_createf(SVN_ERR_FS_CONFLICT, NULL,
                                     _("'%s' has been modified since the "
                                       "commit began (restart the commit)"),
                                     fspath);
        }
    }

    return SVN_NO_ERROR;
}