Index: subversion/include/svn_ra.h
===================================================================
--- subversion/include/svn_ra.h	(revision 10026)
+++ subversion/include/svn_ra.h	(arbetskopia)
@@ -711,6 +711,28 @@
                                   const char **url,
                                   apr_pool_t *pool);
 
+  /** @since New in 1.1.
+   *
+   * Set @a *locations to the locations at the repository revisions
+   * @a location_revisions of the file @a path present at the repository in
+   * revision @a peg_revision.  @a path is a path relative to the URL to which
+   * the RA session was opened. @a location_revisions is an array of
+   * svn_revnum_t's. @a *locations will be a mapping from the revisions to
+   * their appropriate absolute paths.  If the file doesn't exist in a
+   * in location_revision, that revision will be ignored.
+   *
+   * NOTE: For servers older than 1.1, this function will return an
+   * SVN_ERR_RA_NOT_IMPLEMENTED error.
+   *
+   * Use @a pool for all allocations.
+   */
+  svn_error_t *(*get_locations) (void *session_baton,
+                                 apr_hash_t **locations,
+                                 const char *path,
+                                 svn_revnum_t peg_revision,
+                                 apr_array_header_t *location_revisions,
+                                 apr_pool_t *pool);
+
 } svn_ra_plugin_t;
 
 
Index: subversion/libsvn_ra_local/ra_plugin.c
===================================================================
--- subversion/libsvn_ra_local/ra_plugin.c	(revision 10026)
+++ subversion/libsvn_ra_local/ra_plugin.c	(arbetskopia)
@@ -843,8 +843,29 @@
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+svn_ra_local__get_locations (void *session_baton,
+                             apr_hash_t **locations,
+                             const char *relative_path,
+                             svn_revnum_t peg_revision,
+                             apr_array_header_t *location_revisions,
+                             apr_pool_t *pool)
+{
+  svn_ra_local__session_baton_t *sbaton = session_baton;
+  const char *abs_path;
 
+  /* Append the relative path to the base FS path to get an
+     absolute repository path. */
+  abs_path = svn_path_join (sbaton->fs_path, relative_path, pool);
 
+  SVN_ERR (svn_repos_trace_node_locations (sbaton->fs, locations, abs_path,
+                                           peg_revision, location_revisions,
+                                           pool));
+
+  return SVN_NO_ERROR;
+}
+
+
 /*----------------------------------------------------------------*/
 
 /** The ra_plugin **/
@@ -869,7 +890,8 @@
   svn_ra_local__get_log,
   svn_ra_local__do_check_path,
   svn_ra_local__get_uuid,
-  svn_ra_local__get_repos_root
+  svn_ra_local__get_repos_root,
+  svn_ra_local__get_locations
 };
 
 
Index: subversion/mod_dav_svn/version.c
===================================================================
--- subversion/mod_dav_svn/version.c	(revision 10026)
+++ subversion/mod_dav_svn/version.c	(arbetskopia)
@@ -882,6 +882,138 @@
   return derr;
 }
 
+static apr_status_t send_get_locations_report(ap_filter_t *output,
+                                              apr_bucket_brigade *bb,
+                                              const dav_resource *resource,
+                                              apr_hash_t *fs_locations)
+{
+  apr_hash_index_t *hi;
+  apr_pool_t *pool;
+  apr_status_t apr_err;
+
+  pool = resource->pool;
+
+  apr_err = ap_fprintf(output, bb, DAV_XML_HEADER DEBUG_CR
+                       "<S:get-locations-report xmlns:S=\"" SVN_XML_NAMESPACE
+                       "\" xmlns:D=\"DAV:\">" DEBUG_CR);
+  if (apr_err)
+    return apr_err;
+
+  for (hi = apr_hash_first(pool, fs_locations); hi; hi = apr_hash_next (hi))
+    {
+      const void *key;
+      void *value;
+      const char *path_quoted;
+
+      apr_hash_this(hi, &key, NULL, &value);
+      path_quoted = apr_xml_quote_string(pool, value, 1);
+      apr_err = ap_fprintf(output, bb, "<S:location "
+                           "rev=\"%ld\" path=\"%s\"/>" DEBUG_CR,
+                           *(svn_revnum_t *)key, path_quoted);
+      if (apr_err)
+        return apr_err;
+    }
+  return ap_fprintf(output, bb, "</S:get-locations-report>" DEBUG_CR);
+}
+
+dav_error *dav_svn__get_locations_report(const dav_resource *resource,
+                                         const apr_xml_doc *doc,
+                                         ap_filter_t *output)
+{
+  svn_error_t *serr;
+  dav_error *derr = NULL;
+  apr_status_t apr_err;
+  apr_bucket_brigade *bb;
+
+  /* The parameters to do the operation on. */
+  const char *relative_path = NULL;
+  const char *abs_path;
+  svn_revnum_t peg_revision = SVN_INVALID_REVNUM;
+  apr_array_header_t *location_revisions;
+
+  /* XML Parsing Variables */
+  int ns;
+  apr_xml_elem *child;
+
+  apr_hash_t *fs_locations;
+
+  location_revisions = apr_array_make(resource->pool, 0,
+                                      sizeof(svn_revnum_t));
+
+  /* Sanity check. */
+  ns = dav_svn_find_ns(doc->namespaces, SVN_XML_NAMESPACE);
+  if (ns == -1)
+    {
+      return dav_new_error(resource->pool, HTTP_BAD_REQUEST, 0,
+                           "The request does not contain the 'svn:' "
+                           "namespace, so it is not going to have certain "
+                           "required elements.");
+    }
+
+  /* Gather the parameters. */
+  for (child = doc->root->first_child; child != NULL; child = child->next)
+    {
+      /* If this element isn't one of ours, then skip it. */
+      if (child->ns != ns)
+        continue;
+
+      if (strcmp(child->name, "peg-revision") == 0)
+        peg_revision = SVN_STR_TO_REV(dav_xml_get_cdata(child,
+                                                        resource->pool, 1));
+      else if (strcmp(child->name, "location-revision") == 0)
+        {
+          svn_revnum_t revision
+            = SVN_STR_TO_REV(dav_xml_get_cdata(child, resource->pool, 1));
+          APR_ARRAY_PUSH(location_revisions, svn_revnum_t) = revision;
+        }
+      else if (strcmp(child->name, "path") == 0)
+        relative_path = dav_xml_get_cdata(child, resource->pool, 0);
+    }
+
+  /* Now we should have the parameters ready - let's
+     check if they are all present. */
+  if (! (relative_path && SVN_IS_VALID_REVNUM(peg_revision)))
+    {
+      return dav_new_error(resource->pool, HTTP_BAD_REQUEST, 0,
+                           "Not all parameters passed.");
+    }
+
+  /* Append the relative paths to the base FS path to get an
+     absolute repository path. */
+  abs_path = svn_path_join(resource->info->repos_path, relative_path,
+                           resource->pool);
+
+  serr = svn_repos_trace_node_locations(resource->info->repos->fs,
+                                        &fs_locations, abs_path, peg_revision,
+                                        location_revisions, resource->pool);
+
+  if (serr)
+    {
+      return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
+                                 serr->message, resource->pool);
+    }
+
+  bb = apr_brigade_create(resource->pool, output->c->bucket_alloc);
+
+  apr_err = send_get_locations_report(output, bb, resource, fs_locations);
+
+  if (apr_err)
+    derr = dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
+                               HTTP_INTERNAL_SERVER_ERROR,
+                               "Error writing REPORT response.",
+                               resource->pool);
+
+  /* Flush the contents of the brigade (returning an error only if we
+     don't already have one). */
+  if (((apr_err = ap_fflush(output, bb))) && (! derr))
+    return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
+                               HTTP_INTERNAL_SERVER_ERROR,
+                               "Error flushing brigade.",
+                               resource->pool);
+
+  return derr;
+}
+
 static dav_error *dav_svn_deliver_report(request_rec *r,
                                          const dav_resource *resource,
                                          const apr_xml_doc *doc,
@@ -905,6 +1037,10 @@
         {
           return dav_svn__drev_report(resource, doc, output);
         }
+      else if (strcmp(doc->root->name, "get-locations") == 0)
+        {
+          return dav_svn__get_locations_report(resource, doc, output);
+        }
     }
 
   /* ### what is a good error for an unknown report? */
Index: subversion/mod_dav_svn/dav_svn.h
===================================================================
--- subversion/mod_dav_svn/dav_svn.h	(revision 10026)
+++ subversion/mod_dav_svn/dav_svn.h	(arbetskopia)
@@ -504,8 +504,13 @@
                                     enum dav_svn_time_format format,
                                     apr_pool_t *pool);
 
+dav_error * dav_svn__get_locations_report(const dav_resource *resource,
+                                          const apr_xml_doc *doc,
+                                          ap_filter_t *output);
 
 
+
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
Index: subversion/libsvn_ra_svn/client.c
===================================================================
--- subversion/libsvn_ra_svn/client.c	(revision 10026)
+++ subversion/libsvn_ra_svn/client.c	(arbetskopia)
@@ -33,6 +33,7 @@
 #include "svn_path.h"
 #include "svn_pools.h"
 #include "svn_config.h"
+#include "svn_private_config.h"
 #include "svn_ra.h"
 #include "svn_ra_svn.h"
 #include "svn_md5.h"
@@ -1059,6 +1060,72 @@
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *ra_svn_get_locations(void *session_baton,
+                                         apr_hash_t **locations,
+                                         const char *path,
+                                         svn_revnum_t peg_revision,
+                                         apr_array_header_t *location_revisions,
+                                         apr_pool_t *pool)
+{
+  ra_svn_session_baton_t *sess = session_baton;
+  svn_ra_svn_conn_t *conn = sess->conn;
+  svn_revnum_t revision;
+  svn_ra_svn_item_t *item;
+  svn_boolean_t is_done;
+  int i;
+  const char *ret_path;
+  svn_error_t *err;
+
+  /* Transmit the parameters. */
+  SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "w(cr(!",
+                                 "get-locations", path, peg_revision));
+  for (i = 0; i < location_revisions->nelts; i++)
+    {
+      revision = ((svn_revnum_t *)location_revisions->elts)[i];
+      SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "!r!", revision));
+    }
+
+  SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "!))"));
+
+  err = handle_auth_request(sess, pool);
+
+  /* Servers before 1.1 don't support this command. Check for this here. */
+  if (err && err->apr_err == SVN_ERR_RA_SVN_UNKNOWN_CMD)
+    return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, err,
+                             _("get-locations not implemented"));
+  SVN_ERR(err);
+
+  /* Read the hash items. */
+  is_done = FALSE;
+
+  *locations = apr_hash_make(pool);
+
+  /* ### Check for error and return not implemented. */
+  while (!is_done)
+    {
+      SVN_ERR(svn_ra_svn_read_item(conn, pool, &item));
+      if (item->kind == SVN_RA_SVN_WORD && strcmp(item->u.word, "done") == 0)
+        is_done = 1;
+      else if (item->kind != SVN_RA_SVN_LIST)
+        return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
+                                "Location entry not a list");
+      else
+        {
+          SVN_ERR(svn_ra_svn_parse_tuple (item->u.list, pool, "rc",
+                                          &revision, &ret_path));
+          apr_hash_set(*locations, apr_pmemdup(pool, &revision,
+                                               sizeof(revision)),
+                       sizeof(revision), ret_path);
+        }
+    }
+
+  /* Read the response. This is so the server would have a chance to
+   * report an error. */
+  SVN_ERR(svn_ra_svn_read_cmd_response(conn, pool, ""));
+
+  return SVN_NO_ERROR;
+}
+
 static const svn_ra_plugin_t ra_svn_plugin = {
   "ra_svn",
   "Module for accessing a repository using the svn network protocol.",
@@ -1078,7 +1145,8 @@
   ra_svn_log,
   ra_svn_check_path,
   ra_svn_get_uuid,
-  ra_svn_get_repos_root
+  ra_svn_get_repos_root,
+  ra_svn_get_locations
 };
 
 svn_error_t *svn_ra_svn_init(int abi_version, apr_pool_t *pool,
Index: subversion/libsvn_ra_svn/protocol
===================================================================
--- subversion/libsvn_ra_svn/protocol	(revision 10026)
+++ subversion/libsvn_ra_svn/protocol	(arbetskopia)
@@ -280,6 +280,12 @@
                           [ copy-rev:number ] )
     response: ( )
 
+  get-locations
+    params:   ( path:string peg-rev:number ( rev:number ... ) )
+    Before sending response, server sends location entries, ending with "done".
+    location-entry: ( rev:number abs-path:number ) | done
+    response: ( )
+
 3.1.2. Editor Command Set
 
 If edit pipelining is negotiated (see section 2.1), than an edit
Index: subversion/libsvn_ra_dav/ra_dav.h
===================================================================
--- subversion/libsvn_ra_dav/ra_dav.h	(revision 10026)
+++ subversion/libsvn_ra_dav/ra_dav.h	(arbetskopia)
@@ -626,7 +626,9 @@
   ELEM_name_creator_displayname,
   ELEM_svn_error,
   ELEM_human_readable,
-  ELEM_repository_uuid
+  ELEM_repository_uuid,
+  ELEM_get_locations_report,
+  ELEM_location
 };
 
 /* ### docco */
@@ -697,6 +699,15 @@
                              int okay_2,
                              apr_pool_t *pool);
 
+/*
+ * Implements the get_locations RA layer function. */
+svn_error_t *
+svn_ra_dav__get_locations (void *session_baton,
+                           apr_hash_t **locations,
+                           const char *path,
+                           svn_revnum_t peg_revision,
+                           apr_array_header_t *location_revisions,
+                           apr_pool_t *pool);
 
 #ifdef __cplusplus
 }
Index: subversion/libsvn_ra_dav/session.c
===================================================================
--- subversion/libsvn_ra_dav/session.c	(revision 10026)
+++ subversion/libsvn_ra_dav/session.c	(arbetskopia)
@@ -852,7 +852,8 @@
   svn_ra_dav__get_log,
   svn_ra_dav__do_check_path,
   svn_ra_dav__do_get_uuid,
-  svn_ra_dav__get_repos_root
+  svn_ra_dav__get_repos_root,
+  svn_ra_dav__get_locations
 };
 
 
Index: subversion/libsvn_ra_dav/fetch.c
===================================================================
--- subversion/libsvn_ra_dav/fetch.c	(revision 10026)
+++ subversion/libsvn_ra_dav/fetch.c	(arbetskopia)
@@ -26,6 +26,7 @@
 #include <apr_strings.h>
 #include <apr_md5.h>
 #include <apr_portable.h>
+#include <apr_xml.h>
 
 #include <ne_socket.h>
 #include <ne_basic.h>
@@ -1097,7 +1098,147 @@
   return SVN_NO_ERROR;
 }
 
+typedef struct {
+  svn_ra_session_t *ras;
+  apr_hash_t *hash;
+  apr_pool_t *pool;
+} get_locations_baton_t;
 
+/*
+ * Plan for processing the XML. The XML will be of the form:
+ *
+ * <S:get-locations-report xmlns...>
+ *     <S:location rev="..." path="..."/>
+ *     ...
+ * </S:get-locations-report>
+ *
+ * We extract what we want at the start of <S:location>. */
+/* Elements used in a get-locations response */
+static const svn_ra_dav__xml_elm_t gloc_report_elements[] =
+{
+  { SVN_XML_NAMESPACE, "get-locations-report", ELEM_get_locations_report, 0 },
+  { SVN_XML_NAMESPACE, "location", ELEM_location, 0 },
+  { NULL }
+};
+
+static const char *get_attr(const char **atts, const char *which);
+
+/* This implements the `ne_xml_startelem_cb' prototype. */
+static int gloc_start_element(void *userdata, int parent_state, const char *ns,
+                              const char *ln, const char **atts)
+{
+  get_locations_baton_t *baton = userdata;
+  const svn_ra_dav__xml_elm_t *elm;
+
+  elm = svn_ra_dav__lookup_xml_elem(gloc_report_elements, ns, ln);
+
+  /* Just skip unknown elements. */
+  if (!elm)
+    return NE_XML_DECLINE;
+
+  if (parent_state == ELEM_get_locations_report
+      && elm->id == ELEM_location)
+    {
+      svn_revnum_t rev = SVN_INVALID_REVNUM;
+      const char *path;
+      const char *r;
+
+      r = get_attr(atts, "rev");
+      if (r)
+        rev = SVN_STR_TO_REV(r);
+
+      path = get_attr(atts, "path");
+
+      if (SVN_IS_VALID_REVNUM(rev) && path)
+        apr_hash_set(baton->hash,
+                     apr_pmemdup(baton->pool, &rev, sizeof (rev)),
+                     sizeof(rev), apr_pstrdup(baton->pool, path));
+      else
+        return NE_XML_ABORT;
+    }
+
+  return elm->id;
+}
+
+svn_error_t *
+svn_ra_dav__get_locations(void *session_baton,
+                          apr_hash_t **locations,
+                          const char *relative_path,
+                          svn_revnum_t peg_revision,
+                          apr_array_header_t *location_revisions,
+                          apr_pool_t *pool)
+{
+  svn_ra_session_t *ras = session_baton;
+  svn_stringbuf_t *request_body;
+  svn_error_t *err;
+  get_locations_baton_t request_baton;
+  const char *relative_path_quoted;
+  svn_string_t bc_url, bc_relative;
+  const char *final_bc_url;
+  int i;
+  int status_code = 0;
+
+  *locations = apr_hash_make (pool);
+
+  request_body = svn_stringbuf_create("", pool);
+  svn_stringbuf_appendcstr(request_body,
+                           "<?xml version=\"1.0\" encoding=\"utf-8\"?>" DEBUG_CR
+                           "<S:get-locations xmlns:S=\"" SVN_XML_NAMESPACE
+                           "\" xmlns:D=\"DAV:\">" DEBUG_CR);
+
+  svn_stringbuf_appendcstr(request_body, "<S:path>");
+  /* We need to escape the path XML-wise. */
+  relative_path_quoted = apr_xml_quote_string(pool, relative_path, 0);
+  svn_stringbuf_appendcstr(request_body, relative_path_quoted);
+  svn_stringbuf_appendcstr(request_body, "</S:path>" DEBUG_CR);
+  svn_stringbuf_appendcstr(request_body,
+                           apr_psprintf(pool,
+                                        "<S:peg-revision>%ld"
+                                        "</S:peg-revision>" DEBUG_CR,
+                                        peg_revision));
+
+  for (i = 0; i < location_revisions->nelts; ++i)
+    {
+      svn_revnum_t rev = APR_ARRAY_IDX(location_revisions, i, svn_revnum_t);
+      svn_stringbuf_appendcstr(request_body,
+                               apr_psprintf(pool,
+                                            "<S:location-revision>%ld"
+                                            "</S:location-revision>" DEBUG_CR,
+                                            rev));
+    }
+
+  svn_stringbuf_appendcstr (request_body, "</S:get-locations>");
+
+  request_baton.ras = ras;
+  request_baton.hash = *locations;
+  request_baton.pool = pool;
+
+  /* ras's URL may not exist in HEAD, and thus it's not safe to send
+     it as the main argument to the REPORT request; it might cause
+     dav_get_resource() to choke on the server.  So instead, we pass a
+     baseline-collection URL, which we get from the largest of the
+     START and END revisions. */
+  SVN_ERR( svn_ra_dav__get_baseline_info(NULL, &bc_url, &bc_relative, NULL,
+                                         ras->sess, ras->url, peg_revision,
+                                         ras->pool) );
+  final_bc_url = svn_path_url_add_component(bc_url.data, bc_relative.data,
+                                            ras->pool);
+
+  err = svn_ra_dav__parsed_request(ras->sess, "REPORT", final_bc_url,
+                                   request_body->data, NULL, NULL,
+                                   gloc_start_element, NULL, NULL,
+                                   &request_baton, NULL, &status_code,
+                                   pool);
+
+  /* Map status 501: Method Not Implemented to our not implemented error.
+     1.0.x servers and older don't support this report. */
+  if (status_code == 501)
+    return svn_error_create (SVN_ERR_RA_NOT_IMPLEMENTED, err,
+                             _("get-locations REPORT not implemented"));
+
+  return err;
+}
+
 /* Populate the members of ne_propname structure *PROP for the
    Subversion property NAME.  */
 static void
Index: subversion/svnserve/serve.c
===================================================================
--- subversion/svnserve/serve.c	(revision 10026)
+++ subversion/svnserve/serve.c	(arbetskopia)
@@ -908,6 +908,84 @@
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *get_locations(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
+                                  apr_array_header_t *params, void *baton)
+{
+  svn_error_t *err, *write_err;
+  server_baton_t *b = baton;
+  svn_revnum_t revision;
+  apr_array_header_t *location_revisions, *loc_revs_proto;
+  svn_ra_svn_item_t *elt;
+  int i;
+  const char *relative_path;
+  svn_revnum_t peg_revision;
+  apr_hash_t *fs_locations;
+  apr_hash_index_t *iter;
+  const char *abs_path;
+  const void *iter_key;
+  void *iter_value;
+
+  /* Parse the arguments. */
+  SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "crl", &relative_path,
+                                 &peg_revision,
+                                 &loc_revs_proto));
+
+  abs_path = svn_path_join(b->fs_path, relative_path, pool);
+
+  location_revisions = apr_array_make(pool, loc_revs_proto->nelts,
+                                      sizeof(svn_revnum_t));
+  for (i = 0; i < loc_revs_proto->nelts; i++)
+    {
+      elt = &APR_ARRAY_IDX(loc_revs_proto, i, svn_ra_svn_item_t);
+      if (elt->kind != SVN_RA_SVN_NUMBER)
+        return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
+                                "Get-locations location revisions entry "
+                                "not a revision number");
+      revision = (svn_revnum_t)(elt->u.number);
+      APR_ARRAY_PUSH(location_revisions, svn_revnum_t) = revision;
+    }
+  SVN_ERR(trivial_auth_request(conn, pool, b));
+
+  /* All the parameters are fine - let's perform the query against the
+   * repository. */
+
+  /* We store both err and write_err here, so the client will get
+   * the "done" even if there was an error in fetching the results. */
+
+  err = svn_repos_trace_node_locations(b->fs, &fs_locations, abs_path,
+                                       peg_revision, location_revisions,
+                                       pool);
+
+  /* Now, write the results to the connection. */
+  if (!err)
+    {
+      if (fs_locations)
+        {
+          for (iter = apr_hash_first(pool, fs_locations); iter;
+              iter = apr_hash_next(iter))
+            {
+              apr_hash_this(iter, &iter_key, NULL, &iter_value);
+              SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "rc",
+                                             *(const svn_revnum_t *)iter_key,
+                                             (const char *)iter_value));
+            }
+        }
+    }
+
+  write_err = svn_ra_svn_write_word(conn, pool, "done");
+  if (write_err)
+    {
+      svn_error_clear(err);
+      return write_err;
+    }
+  SVN_CMD_ERR(err);
+
+  SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, ""));
+
+  return SVN_NO_ERROR;
+}
+
+
 static const svn_ra_svn_cmd_entry_t main_commands[] = {
   { "get-latest-rev",  get_latest_rev },
   { "get-dated-rev",   get_dated_rev },
@@ -923,6 +1001,7 @@
   { "diff",            diff },
   { "log",             log_cmd },
   { "check-path",      check_path },
+  { "get-locations",   get_locations },
   { NULL }
 };
 
