svn_error_t * svn_client__oldest_rev_at_path(svn_revnum_t *oldest_rev, svn_ra_session_t *ra_session, const char *rel_path, svn_revnum_t rev, apr_pool_t *pool) { apr_array_header_t *rel_paths = apr_array_make(pool, 1, sizeof(rel_path)); apr_array_header_t *revprops = apr_array_make(pool, 0, sizeof(char *)); *oldest_rev = SVN_INVALID_REVNUM; APR_ARRAY_PUSH(rel_paths, const char *) = rel_path; /* Trace back in history to find the revision at which this node was created (copied or added). */ return svn_ra_get_log2(ra_session, rel_paths, 1, rev, 1, FALSE, TRUE, FALSE, revprops, revnum_receiver, oldest_rev, pool); }
svn_error_t * svn_ra__get_deleted_rev_from_log(svn_ra_session_t *session, const char *rel_deleted_path, svn_revnum_t peg_revision, svn_revnum_t end_revision, svn_revnum_t *revision_deleted, apr_pool_t *pool) { const char *fs_path; log_path_del_rev_t log_path_deleted_baton; /* Fetch the absolute FS path associated with PATH. */ SVN_ERR(get_fs_path(&fs_path, session, rel_deleted_path, pool)); if (!SVN_IS_VALID_REVNUM(peg_revision)) return svn_error_createf(SVN_ERR_CLIENT_BAD_REVISION, NULL, _("Invalid peg revision %ld"), peg_revision); if (!SVN_IS_VALID_REVNUM(end_revision)) return svn_error_createf(SVN_ERR_CLIENT_BAD_REVISION, NULL, _("Invalid end revision %ld"), end_revision); if (end_revision <= peg_revision) return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, _("Peg revision must precede end revision")); log_path_deleted_baton.path = fs_path; log_path_deleted_baton.revision_deleted = SVN_INVALID_REVNUM; /* Examine the logs of SESSION's URL to find when DELETED_PATH was first deleted or replaced. */ SVN_ERR(svn_ra_get_log2(session, NULL, peg_revision, end_revision, 0, TRUE, TRUE, FALSE, apr_array_make(pool, 0, sizeof(char *)), log_path_del_receiver, &log_path_deleted_baton, pool)); *revision_deleted = log_path_deleted_baton.revision_deleted; return SVN_NO_ERROR; }
svn_error_t * svn_ra__location_segments_from_log(svn_ra_session_t *session, const char *path, svn_revnum_t peg_revision, svn_revnum_t start_rev, svn_revnum_t end_rev, svn_location_segment_receiver_t receiver, void *receiver_baton, apr_pool_t *pool) { struct gls_log_receiver_baton lrb = { 0 }; apr_array_header_t *targets; svn_node_kind_t kind; svn_revnum_t youngest_rev = SVN_INVALID_REVNUM; const char *fs_path; /* Fetch the absolute FS path associated with PATH. */ SVN_ERR(get_fs_path(&fs_path, session, path, pool)); /* If PEG_REVISION is invalid, it means HEAD. If START_REV is invalid, it means HEAD. If END_REV is SVN_INVALID_REVNUM, we'll use 0. */ if (! SVN_IS_VALID_REVNUM(peg_revision)) { SVN_ERR(svn_ra_get_latest_revnum(session, &youngest_rev, pool)); peg_revision = youngest_rev; } if (! SVN_IS_VALID_REVNUM(start_rev)) { if (SVN_IS_VALID_REVNUM(youngest_rev)) start_rev = youngest_rev; else SVN_ERR(svn_ra_get_latest_revnum(session, &start_rev, pool)); } if (! SVN_IS_VALID_REVNUM(end_rev)) { end_rev = 0; } /* The API demands a certain ordering of our revision inputs. Enforce it. */ SVN_ERR_ASSERT((peg_revision >= start_rev) && (start_rev >= end_rev)); /* Sanity check: verify that the peg-object exists in repos. */ SVN_ERR(svn_ra_check_path(session, path, peg_revision, &kind, pool)); if (kind == svn_node_none) return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, _("Path '%s' doesn't exist in revision %ld"), fs_path, start_rev); /* Populate most of our log receiver baton structure. */ lrb.kind = kind; lrb.last_path = fs_path; lrb.done = FALSE; lrb.start_rev = start_rev; lrb.range_end = start_rev; lrb.receiver = receiver; lrb.receiver_baton = receiver_baton; lrb.pool = pool; /* Let the RA layer drive our log information handler, which will do the work of finding the actual locations for our resource. Notice that we always run on the youngest rev of the 3 inputs. */ targets = apr_array_make(pool, 1, sizeof(const char *)); APR_ARRAY_PUSH(targets, const char *) = path; SVN_ERR(svn_ra_get_log2(session, targets, peg_revision, end_rev, 0, TRUE, FALSE, FALSE, apr_array_make(pool, 0, sizeof(const char *)), gls_log_receiver, &lrb, pool)); /* If we didn't finish, we need to do so with a final segment send. */ if (! lrb.done) SVN_ERR(maybe_crop_and_send_segment(lrb.last_path, start_rev, end_rev, lrb.range_end, receiver, receiver_baton, pool)); return SVN_NO_ERROR; }
svn_error_t * svn_ra__file_revs_from_log(svn_ra_session_t *ra_session, const char *path, svn_revnum_t start, svn_revnum_t end, svn_file_rev_handler_t handler, void *handler_baton, apr_pool_t *pool) { svn_node_kind_t kind; const char *repos_url, *session_url, *fs_path; apr_array_header_t *condensed_targets; struct fr_log_message_baton lmb; struct rev *rev; apr_hash_t *last_props; svn_stream_t *last_stream; apr_pool_t *currpool, *lastpool; /* Fetch the absolute FS path associated with PATH. */ SVN_ERR(get_fs_path(&fs_path, ra_session, path, pool)); /* Check to make sure we're dealing with a file. */ SVN_ERR(svn_ra_check_path(ra_session, path, end, &kind, pool)); if (kind == svn_node_dir) return svn_error_createf(SVN_ERR_FS_NOT_FILE, NULL, _("'%s' is not a file"), fs_path); condensed_targets = apr_array_make(pool, 1, sizeof(const char *)); APR_ARRAY_PUSH(condensed_targets, const char *) = path; lmb.path = fs_path; lmb.eldest = NULL; lmb.pool = pool; /* Accumulate revision metadata by walking the revisions backwards; this allows us to follow moves/copies correctly. */ SVN_ERR(svn_ra_get_log2(ra_session, condensed_targets, end, start, 0, /* no limit */ TRUE, FALSE, FALSE, NULL, fr_log_message_receiver, &lmb, pool)); /* Reparent the session while we go back through the history. */ SVN_ERR(svn_ra_get_session_url(ra_session, &session_url, pool)); SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_url, pool)); SVN_ERR(svn_ra_reparent(ra_session, repos_url, pool)); currpool = svn_pool_create(pool); lastpool = svn_pool_create(pool); /* We want the first txdelta to be against the empty file. */ last_props = apr_hash_make(lastpool); last_stream = svn_stream_empty(lastpool); /* Walk the revision list in chronological order, downloading each fulltext, diffing it with its predecessor, and calling the file_revs handler for each one. Use two iteration pools rather than one, because the diff routines need to look at a sliding window of revisions. Two pools gives us a ring buffer of sorts. */ for (rev = lmb.eldest; rev; rev = rev->next) { const char *temp_path; apr_pool_t *tmppool; apr_hash_t *props; apr_file_t *file; svn_stream_t *stream; apr_array_header_t *prop_diffs; svn_txdelta_stream_t *delta_stream; svn_txdelta_window_handler_t delta_handler = NULL; void *delta_baton = NULL; svn_pool_clear(currpool); /* Get the contents of the file from the repository, and put them in a temporary local file. */ SVN_ERR(svn_stream_open_unique(&stream, &temp_path, NULL, svn_io_file_del_on_pool_cleanup, currpool, currpool)); SVN_ERR(svn_ra_get_file(ra_session, rev->path + 1, rev->revision, stream, NULL, &props, currpool)); SVN_ERR(svn_stream_close(stream)); /* Open up a stream to the local file. */ SVN_ERR(svn_io_file_open(&file, temp_path, APR_READ, APR_OS_DEFAULT, currpool)); stream = svn_stream_from_aprfile2(file, FALSE, currpool); /* Calculate the property diff */ SVN_ERR(svn_prop_diffs(&prop_diffs, props, last_props, lastpool)); /* Call the file_rev handler */ SVN_ERR(handler(handler_baton, rev->path, rev->revision, rev->props, FALSE, /* merged revision */ &delta_handler, &delta_baton, prop_diffs, lastpool)); /* Compute and send delta if client asked for it. */ if (delta_handler) { /* Get the content delta. Don't calculate checksums as we don't * use them. */ svn_txdelta2(&delta_stream, last_stream, stream, FALSE, lastpool); /* And send. */ SVN_ERR(svn_txdelta_send_txstream(delta_stream, delta_handler, delta_baton, lastpool)); } /* Switch the pools and data for the next iteration */ tmppool = currpool; currpool = lastpool; lastpool = tmppool; SVN_ERR(svn_stream_close(last_stream)); last_stream = stream; last_props = props; } SVN_ERR(svn_stream_close(last_stream)); svn_pool_destroy(currpool); svn_pool_destroy(lastpool); /* Reparent the session back to the original URL. */ return svn_ra_reparent(ra_session, session_url, pool); }
svn_error_t * svn_ra__locations_from_log(svn_ra_session_t *session, apr_hash_t **locations_p, const char *path, svn_revnum_t peg_revision, const apr_array_header_t *location_revisions, apr_pool_t *pool) { apr_hash_t *locations = apr_hash_make(pool); struct log_receiver_baton lrb = { 0 }; apr_array_header_t *targets; svn_revnum_t youngest_requested, oldest_requested, youngest, oldest; svn_node_kind_t kind; const char *fs_path; apr_array_header_t *sorted_location_revisions; /* Fetch the absolute FS path associated with PATH. */ SVN_ERR(get_fs_path(&fs_path, session, path, pool)); /* Sanity check: verify that the peg-object exists in repos. */ SVN_ERR(svn_ra_check_path(session, path, peg_revision, &kind, pool)); if (kind == svn_node_none) return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, _("Path '%s' doesn't exist in revision %ld"), fs_path, peg_revision); /* Easy out: no location revisions. */ if (! location_revisions->nelts) { *locations_p = locations; return SVN_NO_ERROR; } /* Figure out the youngest and oldest revs (amongst the set of requested revisions + the peg revision) so we can avoid unnecessary log parsing. */ sorted_location_revisions = apr_array_copy(pool, location_revisions); svn_sort__array(sorted_location_revisions, compare_revisions); oldest_requested = APR_ARRAY_IDX(sorted_location_revisions, 0, svn_revnum_t); youngest_requested = APR_ARRAY_IDX(sorted_location_revisions, sorted_location_revisions->nelts - 1, svn_revnum_t); youngest = peg_revision; youngest = (oldest_requested > youngest) ? oldest_requested : youngest; youngest = (youngest_requested > youngest) ? youngest_requested : youngest; oldest = peg_revision; oldest = (oldest_requested < oldest) ? oldest_requested : oldest; oldest = (youngest_requested < oldest) ? youngest_requested : oldest; /* Populate most of our log receiver baton structure. */ lrb.kind = kind; lrb.last_path = fs_path; lrb.location_revisions = apr_array_copy(pool, sorted_location_revisions); lrb.peg_revision = peg_revision; lrb.peg_path = NULL; lrb.locations = locations; lrb.pool = pool; /* Let the RA layer drive our log information handler, which will do the work of finding the actual locations for our resource. Notice that we always run on the youngest rev of the 3 inputs. */ targets = apr_array_make(pool, 1, sizeof(const char *)); APR_ARRAY_PUSH(targets, const char *) = path; SVN_ERR(svn_ra_get_log2(session, targets, youngest, oldest, 0, TRUE, FALSE, FALSE, apr_array_make(pool, 0, sizeof(const char *)), log_receiver, &lrb, pool)); /* If the received log information did not cover any of the requested revisions, use the last known path. (This normally just means that FS_PATH was not modified between the requested revision and OLDEST. If the file was created at some point after OLDEST, then lrb.last_path should be NULL.) */ if (! lrb.peg_path) lrb.peg_path = lrb.last_path; if (lrb.last_path) { int i; for (i = 0; i < sorted_location_revisions->nelts; i++) { svn_revnum_t rev = APR_ARRAY_IDX(sorted_location_revisions, i, svn_revnum_t); if (! apr_hash_get(locations, &rev, sizeof(rev))) apr_hash_set(locations, apr_pmemdup(pool, &rev, sizeof(rev)), sizeof(rev), apr_pstrdup(pool, lrb.last_path)); } } /* Check that we got the peg path. */ if (! lrb.peg_path) return svn_error_createf (APR_EGENERAL, NULL, _("Unable to find repository location for '%s' in revision %ld"), fs_path, peg_revision); /* Sanity check: make sure that our calculated peg path is the same as what we expected it to be. */ if (strcmp(fs_path, lrb.peg_path) != 0) return svn_error_createf (SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL, _("'%s' in revision %ld is an unrelated object"), fs_path, youngest); *locations_p = locations; return SVN_NO_ERROR; }