Index: build.conf
===================================================================
--- build.conf	(revision 9476)
+++ build.conf	(working copy)
@@ -61,6 +61,7 @@
         subversion/tests/clients/cmdline/svnadmin_tests.py
         subversion/tests/clients/cmdline/svnlook_tests.py
         subversion/tests/clients/cmdline/svnversion_tests.py
+        subversion/tests/clients/cmdline/past_loc_tests.py
 
 [swig-dirs]
 paths =
@@ -639,6 +640,15 @@
 # disabled for now, BROKEN.
 testing = skip
 
+[ra-get-past-location]
+type = exe
+path = subversion/tests/libsvn_ra_local
+sources = get-past-location.c
+install = fs-test
+libs = libsvn_ra libsvn_fs libsvn_delta libsvn_subr libsvn_client
+	aprutil apriconv apr neon
+# disabled for now, BROKEN.
+testing = skip
 
 ### Tests that don't use the C framework (rewrite?) ----------
 
Index: subversion/include/svn_repos.h
===================================================================
--- subversion/include/svn_repos.h	(revision 9476)
+++ subversion/include/svn_repos.h	(working copy)
@@ -537,6 +537,24 @@
                    svn_boolean_t cross_copies,
                    apr_pool_t *pool);
 
+/** Set @a *locations to be a mapping of the revisions to the paths of
+ * the file @a fs_path present at the repository in revision
+ * @a peg_revision, where the revisions are takes out of the array
+ * @a location_revisions.
+ *
+ * @a location_revisions is an array of svn_revnum_t's and @a *locations
+ * maps svn_revnum_t's to const char *.
+ *
+ * @a pool is used for temporary allocations.
+ */
+svn_error_t *
+svn_repos_trace_node_locations (svn_fs_t *fs,
+                                apr_hash_t **locations,
+                                const char *fs_path,
+                                svn_revnum_t peg_revision,
+                                apr_array_header_t *location_revisions,
+                                apr_pool_t *pool);
+
 /* ### other queries we can do someday --
 
      * fetch the last revision created by <user>
Index: subversion/include/svn_ra.h
===================================================================
--- subversion/include/svn_ra.h	(revision 9476)
+++ subversion/include/svn_ra.h	(working copy)
@@ -711,6 +711,22 @@
                                   const char **url,
                                   apr_pool_t *pool);
 
+  /** Set @a *locations to the location 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.
+   *
+   * Use @a pool for temporary 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_fs/tree.c
===================================================================
--- subversion/libsvn_fs/tree.c	(revision 9476)
+++ subversion/libsvn_fs/tree.c	(working copy)
@@ -4293,3 +4293,4 @@
   *revision = history->revision;
   return SVN_NO_ERROR;
 }
+
Index: subversion/libsvn_ra_local/ra_plugin.c
===================================================================
--- subversion/libsvn_ra_local/ra_plugin.c	(revision 9476)
+++ subversion/libsvn_ra_local/ra_plugin.c	(working copy)
@@ -90,9 +90,8 @@
   repos_url_len = strlen(rbaton->session->repos_url);
   if (strncmp(url, rbaton->session->repos_url, repos_url_len) != 0)
     return svn_error_createf (SVN_ERR_RA_ILLEGAL_URL, NULL,
-                              "'%s'\n"
-                              "is not the same repository as\n"
-                              "'%s'", url, rbaton->session->repos_url);
+                              "'%s' is not the same repository as '%s'",
+                              url, rbaton->session->repos_url);
   fs_path = url + repos_url_len;
 
   return svn_repos_link_path (rbaton->report_baton, path,
@@ -840,8 +839,32 @@
   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 paths 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 **/
@@ -866,7 +889,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/log.c
===================================================================
--- subversion/mod_dav_svn/log.c	(revision 9476)
+++ subversion/mod_dav_svn/log.c	(working copy)
@@ -333,3 +333,159 @@
                                resource->pool);
   return derr;
 }
+
+static svn_error_t * send_get_locations_report (struct log_receiver_baton
+                                                *lrb,
+                                                const dav_resource *resource,
+                                                apr_hash_t * fs_locations)
+{
+  apr_hash_index_t *iter;
+
+  SVN_ERR (send_xml (lrb, "%s",
+                     "<S:get-locations-report xmlns:S=\"" SVN_XML_NAMESPACE "\" "
+                     "xmlns:D=\"DAV:\">"));
+  if (fs_locations)
+    {
+      for (iter = apr_hash_first (resource->pool, fs_locations) ;
+           iter ;
+           iter = apr_hash_next (iter))
+        {
+          const void * key;
+          apr_ssize_t key_len; /* Unused */
+          void * value;
+          const char * past_url;
+          apr_hash_this (iter, &key, &key_len, &value);
+          SVN_ERR (send_xml (lrb, "<S:location>"
+                             "<S:location-revision>%" SVN_REVNUM_T_FMT
+                             "</S:location-revision>"
+                             "<S:path>%s</S:path>"
+                             "</S:location>",
+                             (*(svn_revnum_t *)key),
+                             (const char *)value));
+        }
+    }
+  SVN_ERR (send_xml (lrb, "%s", "</S:get-locations-report>"));
+
+  return SVN_NO_ERROR;
+}
+
+/*
+ * I'm putting it here temporarily so I can make use of the send_xml
+ * function.
+ * */
+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;
+
+  /* The parameters to do the operation on. */
+  const char *relative_path = NULL;
+  const char *abs_path;
+  svn_revnum_t peg_revision = SVN_INVALID_REVNUM;
+  svn_revnum_t past_revision = SVN_INVALID_REVNUM;
+  apr_array_header_t *location_revisions;
+
+  /* XML Parsing Variables */
+  int ns;
+  apr_xml_elem *child;
+
+  apr_hash_t *fs_locations = NULL;
+
+  struct log_receiver_baton lrb;
+
+  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.");
+    }
+
+  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)
+        {
+          if (strcmp(child->name, "peg-revision") == 0)
+            {
+              /* ### assume no white space, no child elems, etc */
+              peg_revision = SVN_STR_TO_REV (child->first_cdata.first->text);
+            }
+          else if (strcmp(child->name, "location-revision") == 0)
+            {
+              /* ### assume no white space, no child elems, etc */
+              past_revision = SVN_STR_TO_REV (child->first_cdata.first->text);
+              APR_ARRAY_PUSH( location_revisions, svn_revnum_t) =
+                  past_revision;
+            }
+          else if (strcmp(child->name, "path") == 0)
+            {
+              relative_path = apr_pstrdup (resource->pool,
+                                           child->first_cdata.first->text);
+            }
+        }
+    }
+  /* Now we should have the parameters ready - lets'
+   * check if they are all present.
+   * */
+  if (relative_path && (peg_revision != SVN_INVALID_REVNUM) &&
+      (past_revision != SVN_INVALID_REVNUM))
+    {
+      /* Do nothing - everything is A. OK. */
+    }
+  else
+    {
+      /* Throw an error */
+      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_BAD_REQUEST, serr->message,
+                                 resource->pool);
+    }
+
+  lrb.bb = apr_brigade_create (resource->pool,
+                               output->c->bucket_alloc);
+  lrb.output = output;
+
+  serr = send_get_locations_report(&lrb, resource, fs_locations);
+
+  if (serr)
+    {
+      return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
+                                 serr->message, 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, lrb.bb))) && (! derr))
+    return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
+                               HTTP_INTERNAL_SERVER_ERROR,
+                               "Error flushing brigade.",
+                               resource->pool);
+
+  return NULL;
+}
+
Index: subversion/mod_dav_svn/version.c
===================================================================
--- subversion/mod_dav_svn/version.c	(revision 9476)
+++ subversion/mod_dav_svn/version.c	(working copy)
@@ -906,6 +906,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 9476)
+++ subversion/mod_dav_svn/dav_svn.h	(working copy)
@@ -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/tests/libsvn_ra_local/get-past-location.c
===================================================================
--- subversion/tests/libsvn_ra_local/get-past-location.c	(revision 0)
+++ subversion/tests/libsvn_ra_local/get-past-location.c	(revision 0)
@@ -0,0 +1,241 @@
+#include <stdlib.h>
+
+#include "svn_ra.h"
+#include "svn_pools.h"
+#include "svn_config.h"
+
+/* Display a prompt and read a one-line response into the provided buffer,
+   removing a trailing newline if present. */
+static svn_error_t *
+prompt_and_read_line(const char *prompt,
+                     char *buffer,
+                     size_t max)
+{
+  int len;
+  printf("%s: ", prompt);
+  if (fgets(buffer, max, stdin) == NULL)
+    return svn_error_create(0, NULL, "error reading stdin");
+  len = strlen(buffer);
+  if (len > 0 && buffer[len-1] == '\n')
+    buffer[len-1] = 0;
+  return SVN_NO_ERROR;
+}
+
+
+/* A tiny callback function of type 'svn_auth_simple_prompt_func_t'. For
+   a much better example, see svn_cl__auth_simple_prompt in the official
+   svn cmdline client. */
+static svn_error_t *
+my_simple_prompt_callback (svn_auth_cred_simple_t **cred,
+                           void *baton,
+                           const char *realm,
+                           const char *username,
+                           svn_boolean_t may_save,
+                           apr_pool_t *pool)
+{
+  svn_auth_cred_simple_t *ret = apr_pcalloc (pool, sizeof (*ret));
+#if 0
+  char answerbuf[100];
+
+  if (realm)
+    {
+      printf ("Authentication realm: %s\n", realm);
+    }
+
+  if (username)
+    ret->username = apr_pstrdup (pool, username);
+  else
+    {
+      SVN_ERR (prompt_and_read_line("Username", answerbuf, sizeof(answerbuf)));
+      ret->username = apr_pstrdup (pool, answerbuf);
+    }
+
+  SVN_ERR (prompt_and_read_line("Password", answerbuf, sizeof(answerbuf)));
+  ret->password = apr_pstrdup (pool, answerbuf);
+#endif
+  ret->username = apr_pstrdup(pool, "jrandom");
+  ret->password = apr_pstrdup(pool, "rayjandom");
+  *cred = ret;
+  return SVN_NO_ERROR;
+}
+
+
+/* A tiny callback function of type 'svn_auth_username_prompt_func_t'. For
+   a much better example, see svn_cl__auth_username_prompt in the official
+   svn cmdline client. */
+static svn_error_t *
+my_username_prompt_callback (svn_auth_cred_username_t **cred,
+                             void *baton,
+                             const char *realm,
+                             svn_boolean_t may_save,
+                             apr_pool_t *pool)
+{
+  svn_auth_cred_username_t *ret = apr_pcalloc (pool, sizeof (*ret));
+#if 0
+  char answerbuf[100];
+
+  if (realm)
+    {
+      printf ("Authentication realm: %s\n", realm);
+    }
+
+  SVN_ERR (prompt_and_read_line("Username", answerbuf, sizeof(answerbuf)));
+#endif
+  ret->username = apr_pstrdup(pool, "jrandom");
+#if 0
+  ret->username = apr_pstrdup (pool, answerbuf);
+#endif
+  *cred = ret;
+  return SVN_NO_ERROR;
+}
+
+
+static svn_error_t * svn_main(apr_pool_t *pool, int argc, char *argv[])
+{
+  void *ra_baton;
+  svn_ra_plugin_t *plugin;
+  const char *repos_url, *file_url, *past_file_url, *file_path;
+  svn_revnum_t peg_rev, past_rev;
+  void *session_baton;
+  svn_ra_callbacks_t my_ra_callbacks = { NULL, NULL, NULL, NULL, NULL, NULL };
+  void *callback_baton;
+  svn_error_t *error;
+  apr_hash_t *config;
+  int arg_idx, rev_idx;
+
+  /* TODO: check that I do not get past argc. Not very critical, because
+   * this is a test helper.
+   * */
+  arg_idx = 1;
+  repos_url = argv[arg_idx++];
+  file_url = argv[arg_idx++];
+  peg_rev = atoi(argv[arg_idx++]);
+#if 0
+  past_rev = atoi(argv[4]);
+#endif
+
+  callback_baton = NULL;
+
+  SVN_ERR (svn_config_get_config (&(config), NULL, pool));
+
+    /* Make the client_ctx capable of authenticating users */
+    {
+      /* There are many different kinds of authentication back-end
+         "providers".  See svn_auth.h for a full overview. */
+      svn_auth_provider_object_t *provider;
+      apr_array_header_t *providers;
+
+      providers = apr_array_make (pool, 4, sizeof (svn_auth_provider_object_t *));
+
+      svn_client_get_simple_prompt_provider (&provider,
+                                             my_simple_prompt_callback,
+                                             NULL, /* baton */
+                                             2, /* retry limit */ pool);
+      APR_ARRAY_PUSH (providers, svn_auth_provider_object_t *) = provider;
+
+      svn_client_get_username_prompt_provider (&provider,
+                                               my_username_prompt_callback,
+                                               NULL, /* baton */
+                                               2, /* retry limit */ pool);
+      APR_ARRAY_PUSH (providers, svn_auth_provider_object_t *) = provider;
+
+      /* Register the auth-providers into the context's auth_baton. */
+      svn_auth_open (&(my_ra_callbacks.auth_baton), providers, pool);
+    }
+
+
+  /* Load all available RA implementations. */
+  SVN_ERR (svn_ra_init_ra_libs (&ra_baton, pool));
+
+  /* Get the plugin which handles "file:" URLs */
+  SVN_ERR (svn_ra_get_ra_library (&plugin, ra_baton, repos_url, pool));
+
+  SVN_ERR (plugin->open (&session_baton,
+                        repos_url,
+                        &my_ra_callbacks,
+                        callback_baton,
+                        config,
+                        pool));
+
+    {
+      apr_hash_t *old_locations;
+      apr_array_header_t *past_revs_array;
+      svn_boolean_t not_present;
+      int num_revs;
+
+      past_revs_array = apr_array_make (pool,
+                                        argc-arg_idx,
+                                        sizeof (past_rev));
+
+      num_revs = 0;
+      for(;arg_idx<argc;arg_idx++)
+        {
+          past_rev = atoi(argv[arg_idx]);
+          APR_ARRAY_PUSH( past_revs_array, svn_revnum_t) = past_rev;
+          num_revs++;
+        }
+
+      file_path = file_url + strlen(repos_url);
+
+      SVN_ERR (plugin->get_locations (session_baton,
+                                      &old_locations,
+                                      file_path,
+                                      peg_rev,
+                                      past_revs_array,
+                                      pool));
+
+      if (old_locations)
+        {
+          for(rev_idx = 0 ; rev_idx < num_revs ; rev_idx++)
+            {
+              const char *past_path;
+              past_rev = ((svn_revnum_t *)past_revs_array->elts)[rev_idx];
+              past_path = (const char *)apr_hash_get (old_locations,
+                                                      (const void*)&past_rev,
+                                                      sizeof (past_rev));
+
+              if (past_path)
+                {
+                  printf ("%s%s\n", repos_url, past_path);
+                }
+              else
+                {
+                  printf ("%s", "notexist:\n");
+                }
+            }
+        }
+      else
+        {
+          for(rev_idx = 0 ; rev_idx<num_revs ; rev_idx++)
+            {
+              printf ("%s", "notexist:\n");
+            }
+        }
+    }
+
+  return SVN_NO_ERROR;
+}
+
+int main(int argc, char *argv[])
+{
+  apr_pool_t *pool;
+  svn_error_t *error;
+
+  /* Initialize the app.  Send all error messages to 'stderr'.  */
+  if (svn_cmdline_init ("get_past_revision", stderr) != EXIT_SUCCESS)
+    return EXIT_FAILURE;
+
+
+  pool = svn_pool_create (NULL);
+
+  error = svn_main (pool, argc, argv);
+
+  if (error)
+    {
+      svn_handle_error (error, stderr, 0);
+      return EXIT_FAILURE;
+    }
+
+  return EXIT_SUCCESS;
+}
+
Index: subversion/tests/clients/cmdline/past_loc_tests.py
===================================================================
--- subversion/tests/clients/cmdline/past_loc_tests.py	(revision 0)
+++ subversion/tests/clients/cmdline/past_loc_tests.py	(revision 0)
@@ -0,0 +1,397 @@
+#!/usr/bin/env python
+#
+#  past_loc_tests.py:  testing the past location tracing function
+#
+#  Subversion is a tool for revision control.
+#  See http://subversion.tigris.org for more information.
+#
+# ====================================================================
+# Copyright (c) 2000-2004 CollabNet.  All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution.  The terms
+# are also available at http://subversion.tigris.org/license-1.html.
+# If newer versions of this license are posted there, you may use a
+# newer version instead, at your option.
+#
+######################################################################
+
+# General modules
+import stat, string, sys, os, shutil, re
+
+# Our testing module
+import svntest
+from svntest import SVNAnyOutput
+
+# (abbreviation)
+Skip = svntest.testcase.Skip
+XFail = svntest.testcase.XFail
+Item = svntest.wc.StateItem
+
+
+######################################################################
+# Utilities
+#
+
+
+def get_repos_rev(sbox):
+  wc_dir = sbox.wc_dir;
+
+  out, err = svntest.actions.run_and_verify_svn("Getting Repository Revision",
+                                                None, [], "up", wc_dir)
+
+  mo=re.match("At revision (\\d+)\\.", out[-1])
+  if mo:
+    return int(mo.group(1))
+  else:
+    raise svntest.Failure
+
+def get_past_url(sbox, new_url, peg_revision, past_revision):
+  get_past_location_path = os.path.join("..", "..", "libsvn_ra_local",
+                                        "ra-get-past-location")
+
+  repos_url = svntest.main.current_repo_url
+
+  is_scalar = (1==0)
+
+  if (not isinstance(past_revision, list)):
+    past_revision = [past_revision]
+    is_scalar = (1==1)
+
+  args = [get_past_location_path, 0, 0, repos_url, new_url,
+          peg_revision] + past_revision
+
+  out, err = svntest.main.run_command(*args)
+
+  if (len(out) == 0):
+    print "ra-get-past-location exited with error:"
+    for i in err:
+      print i
+    raise svntest.Failure
+
+  ret = []
+  for url in out:
+    if (url[-1] == "\n"):
+      url = url[:-1]
+    ret = ret + [url]
+
+  if (is_scalar):
+    return ret[0]
+  return ret
+
+
+######################################################################
+# Tests
+#
+#   Each test must return on success or raise on failure.
+
+
+def get_past_loc_for_move(sbox):
+  "get the past url for a moved file"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir;
+
+  starting_revision = get_repos_rev(sbox)
+
+  mu_path = os.path.join(wc_dir, 'A', 'mu')
+  new_mu_path = os.path.join(wc_dir, "mu.new")
+
+  svntest.actions.run_and_verify_svn(None, None, [], "mv",
+                                     mu_path, new_mu_path)
+
+  svntest.actions.run_and_verify_svn(None, None, [], 'ci',
+                                     "-m", "Commiting Move",
+                                     wc_dir)
+
+  current_revision = starting_revision + 1
+
+  old_mu_url = svntest.main.current_repo_url + "/A/mu"
+  new_mu_url = svntest.main.current_repo_url + "/mu.new"
+
+  returned_old_mu_url = get_past_url(sbox, new_mu_url, current_revision,
+                                     starting_revision)
+
+  if (returned_old_mu_url != old_mu_url):
+    print "old_mu_url (\"" + old_mu_url + "\") is not " + \
+          "returned_old_mu_url (\"" + returned_old_mu_url + "\")"
+    raise svntest.Failure
+
+
+def get_past_loc_for_copy(sbox):
+  "get the past url for a copied file"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir;
+
+  starting_revision = get_repos_rev(sbox)
+
+  mu_path = os.path.join(wc_dir, 'A', 'mu')
+  new_mu_path = os.path.join(wc_dir, "mu.new")
+
+  svntest.actions.run_and_verify_svn(None, None, [], "cp",
+                                     mu_path, new_mu_path)
+
+  svntest.actions.run_and_verify_svn(None, None, [], 'ci',
+                                     "-m", "Commiting Move",
+                                     wc_dir)
+
+  current_revision = starting_revision + 1
+
+  old_mu_url = svntest.main.current_repo_url + "/A/mu"
+  new_mu_url = svntest.main.current_repo_url + "/mu.new"
+
+  returned_old_mu_url = get_past_url(sbox, new_mu_url, current_revision,
+                                     starting_revision)
+
+  if (returned_old_mu_url != old_mu_url):
+    print "old_mu_url (\"" + old_mu_url + "\") is not " + \
+          "returned_old_mu_url (\"" + returned_old_mu_url + "\")"
+    raise svntest.Failure
+
+
+def get_past_loc_for_file_in_moved_dir(sbox):
+  "get the past url for file in moved dir"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir;
+
+  starting_revision = get_repos_rev(sbox)
+
+  b_path = os.path.join(wc_dir, 'A', 'B')
+  new_b_path = os.path.join(wc_dir, "B-mynew")
+
+  svntest.actions.run_and_verify_svn(None, None, [], "mv",
+                                     b_path, new_b_path)
+
+  svntest.actions.run_and_verify_svn(None, None, [], 'ci',
+                                     "-m", "Commiting Move",
+                                     wc_dir)
+
+  current_revision = starting_revision + 1
+
+  old_alpha_url = svntest.main.current_repo_url + "/A/B/E/alpha"
+  new_alpha_url = svntest.main.current_repo_url + "/B-mynew/E/alpha"
+
+  returned_old_alpha_url = get_past_url(sbox, new_alpha_url, current_revision,
+                                        starting_revision)
+
+  if (returned_old_alpha_url != old_alpha_url):
+    print "old_mu_url (\"" + old_alpha_url + "\") is not " + \
+          "returned_old_mu_url (\"" + returned_old_alpha_url + "\")"
+    raise svntest.Failure
+
+
+def get_past_loc_for_file_in_copied_dir(sbox):
+  "get the past url for file in copied dir"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir;
+
+  starting_revision = get_repos_rev(sbox)
+
+  b_path = os.path.join(wc_dir, 'A', 'B')
+  new_b_path = os.path.join(wc_dir, "B-mynew")
+
+  svntest.actions.run_and_verify_svn(None, None, [], "cp",
+                                     b_path, new_b_path)
+
+  svntest.actions.run_and_verify_svn(None, None, [], 'ci',
+                                     "-m", "Commiting Move",
+                                     wc_dir)
+
+  current_revision = starting_revision + 1
+
+  old_alpha_url = svntest.main.current_repo_url + "/A/B/E/alpha"
+  new_alpha_url = svntest.main.current_repo_url + "/B-mynew/E/alpha"
+
+  returned_old_alpha_url = get_past_url(sbox, new_alpha_url, current_revision,
+                                        starting_revision)
+
+  if (returned_old_alpha_url != old_alpha_url):
+    print "old_mu_url (\"" + old_alpha_url + "\") is not " + \
+          "returned_old_mu_url (\"" + returned_old_alpha_url + "\")"
+    raise svntest.Failure
+
+def non_existent_file(sbox):
+  "get the past URL of a then nonexistent file"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir;
+
+  starting_revision = get_repos_rev(sbox)
+
+  new_file_path = os.path.join(wc_dir, "new_file")
+
+  f = open(new_file_path, "w")
+  f.write("One Two Three Four\n");
+  f.close();
+
+  svntest.actions.run_and_verify_svn(None, None, [], "add",
+                                     new_file_path)
+
+  svntest.actions.run_and_verify_svn(None, None, [], 'ci',
+                                     "-m", "Commiting Move",
+                                     wc_dir)
+
+  current_revision = starting_revision + 1
+
+  new_file_url = svntest.main.current_repo_url + "/new_file";
+
+  returned_old_new_file_url = get_past_url(sbox,
+                                           new_file_url, current_revision,
+                                           starting_revision)
+
+  if (returned_old_new_file_url != "notexist:"):
+    print "URL is \"" + returned_old_new_file_url + "\" instead of notexist:"
+    raise svntest.Failure
+
+
+def future_rev(sbox):
+  "get location for a future rev"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir;
+
+  urls = []
+
+  starting_revision = get_repos_rev(sbox)
+
+  mu_path = os.path.join(wc_dir, 'A', 'mu')
+  mu_new_path = os.path.join(wc_dir, "mu.new")
+
+  mu_url = svntest.main.current_repo_url + "/A/mu"
+  urls = urls + [[starting_revision, mu_url]]
+
+  svntest.actions.run_and_verify_svn(None, None, [], "mv",
+                                     mu_path, mu_new_path)
+
+  svntest.actions.run_and_verify_svn(None, None, [], 'ci',
+                                     "-m", "Commiting Move",
+                                     wc_dir)
+
+  mu_new_first_revision = starting_revision + 1
+
+  mu_new_url = svntest.main.current_repo_url + "/mu.new";
+  # Cancelled because this is the peg revision
+  # urls = urls + [[mu_new_first_revision, mu_new_url]];
+
+  mu_temp_path = os.path.join(wc_dir, "mu.temp")
+
+  svntest.actions.run_and_verify_svn(None, None, [], "mv",
+                                     mu_new_path, mu_temp_path)
+
+  svntest.actions.run_and_verify_svn(None, None, [], 'ci',
+                                     "-m", "Commiting Move",
+                                     wc_dir)
+
+  mu_temp_revision = mu_new_first_revision + 1
+
+  mu_temp_url = svntest.main.current_repo_url + "/mu.temp";
+
+  urls = urls + [[mu_temp_revision, mu_temp_url]];
+
+  svntest.actions.run_and_verify_svn(None, None, [], "mv",
+                                     mu_temp_path, mu_new_path)
+
+  svntest.actions.run_and_verify_svn(None, None, [], 'ci',
+                                     "-m", "Commiting Move",
+                                     wc_dir)
+
+  mu_new_second_revision = mu_temp_revision + 1
+
+  urls = urls + [[mu_new_second_revision, mu_new_url]];
+
+  mu_inaccess_path = os.path.join(wc_dir, "mu-inaccess-path")
+
+  svntest.actions.run_and_verify_svn(None, None, [], "mv",
+                                     mu_new_path, mu_inaccess_path)
+
+  svntest.actions.run_and_verify_svn(None, None, [], 'ci',
+                                     "-m", "Commiting Move",
+                                     wc_dir)
+
+  mu_inaccess_revision = mu_new_second_revision + 1
+
+  urls = urls + [[mu_inaccess_revision, "notexist:"]];
+
+  ret_urls = get_past_url(sbox, mu_new_url, mu_new_first_revision,
+                          map( (lambda x: x[0]), urls))
+
+  for i in range(0, len(urls)-1):
+    if (urls[i][1] != ret_urls[i]):
+      print "urls[" + str(i) + "] (\"" + urls[i][1] + "\") is not " + \
+            "ret_urls[" + str(i) + "] (\"" + ret_urls[i] + "\")"
+      raise svntest.Failure
+
+#
+# Here we move the original file, and create a new file with its path. Then
+# we trace the future location of the moved file with the revision in the
+# future. The get_locations() callbacks should detect they are not the
+# same file.
+#
+def future_false_file(sbox):
+  "get the future url for a future false file"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir;
+
+  starting_revision = get_repos_rev(sbox)
+
+  mu_path = os.path.join(wc_dir, 'A', 'mu')
+  new_mu_path = os.path.join(wc_dir, "mu.new")
+
+  svntest.actions.run_and_verify_svn(None, None, [], "mv",
+                                     mu_path, new_mu_path)
+
+  svntest.actions.run_and_verify_svn(None, None, [], 'ci',
+                                     "-m", "Commiting Move",
+                                     wc_dir)
+
+  intermediate_revision = starting_revision + 1
+
+  f = open(mu_path, "w");
+  f.write("Hello Moi! This is your captain speaking.\n\n");
+  f.close();
+
+  svntest.actions.run_and_verify_svn(None, None, [], "add",
+                                     mu_path)
+
+  svntest.actions.run_and_verify_svn(None, None, [], 'ci',
+                                     "-m", "Commiting Add of mu",
+                                     wc_dir)
+
+  current_revision = intermediate_revision + 1
+
+  old_mu_url = svntest.main.current_repo_url + "/A/mu"
+  new_mu_url = svntest.main.current_repo_url + "/mu.new"
+
+  returned_url = get_past_url(sbox, old_mu_url, starting_revision,
+                              current_revision)
+
+  if (returned_url != "notexist:"):
+    print "returned_url (\"" + returned_url + "\") is not notexist:"
+    raise svntest.Failure
+
+
+########################################################################
+# Run the tests
+
+
+# list all tests here, starting with None:
+test_list = [ None,
+              get_past_loc_for_move,
+              get_past_loc_for_copy,
+              get_past_loc_for_file_in_moved_dir,
+              get_past_loc_for_file_in_copied_dir,
+              non_existent_file,
+              future_rev,
+              future_false_file,
+             ]
+
+if __name__ == '__main__':
+  svntest.main.run_tests(test_list)
+  # NOTREACHED
+
+
+### End of file.

Property changes on: subversion/tests/clients/cmdline/past_loc_tests.py
___________________________________________________________________
Name: svn:executable
   + *

Index: subversion/libsvn_repos/rev_hunt.c
===================================================================
--- subversion/libsvn_repos/rev_hunt.c	(revision 9476)
+++ subversion/libsvn_repos/rev_hunt.c	(working copy)
@@ -252,4 +252,176 @@
   return SVN_NO_ERROR;
 }
 
-                             
+/* Makes sure that the revision numbers are sorted in decreasing order. */
+static int
+compare_revnums (const void *p_a, const void *p_b)
+{
+  svn_revnum_t a, b;
+  a = *(svn_revnum_t *)p_a;
+  b = *(svn_revnum_t *)p_b;
+
+  return (a < b) ? 1 : (a > b) ? -1 : 0;
+}
+
+/*
+ * The purpose of this function is to discover if fs_path@future_rev
+ * is derived from fs_path@peg_rev. The return is place in *is_ancestor.
+ * */
+static svn_error_t *
+check_ancestry_of_peg_path (svn_boolean_t *is_ancestor,
+                            svn_fs_t *fs,
+                            const char *fs_path,
+                            svn_revnum_t peg_revision,
+                            svn_revnum_t future_revision,
+                            apr_pool_t *pool)
+{
+  svn_fs_root_t *root;
+  svn_node_kind_t kind;
+  svn_fs_history_t *history, *new_history;
+  const char *path;
+  svn_revnum_t revision;
+
+  SVN_ERR (svn_fs_revision_root (&root, fs, future_revision, pool));
+  SVN_ERR (svn_fs_check_path (&kind, root, fs_path, pool));
+
+  if (kind == svn_node_none)
+    {
+      *is_ancestor = FALSE;
+      return SVN_NO_ERROR;
+    }
+
+  SVN_ERR (svn_fs_node_history (&history, root, fs_path, pool));
+  SVN_ERR (svn_fs_history_location (&path, &revision, history, pool));
+
+  while (history && (revision > peg_revision))
+    {
+      SVN_ERR (svn_fs_history_prev (&new_history, history, TRUE, pool));
+      history = new_history;
+      if (history)
+        {
+          SVN_ERR (svn_fs_history_location (&path, &revision, history, pool));
+        }
+    }
+  if (! history)
+    {
+      *is_ancestor = FALSE;
+    }
+  else
+    {
+      *is_ancestor = (!strcmp(path, fs_path));
+    }
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_repos_trace_node_locations (svn_fs_t *fs,
+                                apr_hash_t **locations,
+                                const char *fs_path,
+                                svn_revnum_t peg_revision,
+                                apr_array_header_t *location_revisions_orig,
+                                apr_pool_t *pool)
+{
+  apr_array_header_t *location_revisions;
+  svn_revnum_t *revision_ptr, *revision_ptr_end;
+  svn_fs_root_t *root;
+  svn_node_kind_t kind;
+  svn_fs_history_t *history, *new_history;
+  const char *path;
+  svn_revnum_t revision;
+  svn_boolean_t is_ancestor;
+  apr_hash_t *locations_ret;
+  apr_pool_t *subpool;
+
+
+  /* We allocate a subpool for the temporary operations that will no longer
+   * be relevant after the termination of this function. Note which operations
+   * are done with pool and which with subpool
+   * */
+  subpool = svn_pool_create(pool);
+
+  /* TODO: What should be done if the elt_size of the array is not
+   * sizeof(svn_revnum_t)? For the time being, I'm consistenly using
+   * sizeof(svn_renum_t) because its easier. */
+
+  /* First - let's sort the array of the revisions from the greatest revision
+   * downward, so it will be easier to search on. */
+  location_revisions = apr_array_copy (subpool, location_revisions_orig);
+  qsort (location_revisions->elts,
+         location_revisions->nelts,
+         sizeof(*revision_ptr),
+         compare_revnums);
+
+  revision_ptr = (svn_revnum_t *)location_revisions->elts;
+  revision_ptr_end = revision_ptr + location_revisions->nelts;
+
+  *locations = NULL;
+  locations_ret = apr_hash_make(pool);
+
+
+
+  /* Check if we have a greater revision number than the peg revision. In that
+   * case we should check the existence of the path in the future of this
+   * revision.
+   * */
+  is_ancestor = FALSE;
+  if ((*revision_ptr) > peg_revision)
+    {
+      for (;
+           ((*revision_ptr) > peg_revision) &&
+           (revision_ptr < revision_ptr_end);
+           revision_ptr++)
+        {
+          SVN_ERR (check_ancestry_of_peg_path (&is_ancestor, fs, fs_path,
+                                               peg_revision, *revision_ptr,
+                                               subpool));
+          if (is_ancestor)
+            {
+              break;
+            }
+        }
+    }
+
+
+  SVN_ERR (svn_fs_revision_root (&root, fs,
+                                 (is_ancestor ?
+                                  (*revision_ptr) :
+                                  peg_revision),
+                                 subpool));
+  SVN_ERR (svn_fs_check_path (&kind, root, fs_path, subpool));
+  SVN_ERR (svn_fs_node_history (&history, root, fs_path, subpool));
+  SVN_ERR (svn_fs_history_location (&path, &revision, history, subpool));
+
+  while (history && (revision_ptr < revision_ptr_end))
+    {
+      while ((revision_ptr < revision_ptr_end) && (*revision_ptr >= revision))
+        {
+          const void * rev_copy;
+
+          /* We are duplicating revision_ptr because it is allocated out of
+           * the subpool that will be destroyed.
+           * */
+          rev_copy = apr_pmemdup (pool, (const void *)revision_ptr,
+                                  sizeof (*revision_ptr));
+          apr_hash_set (locations_ret, rev_copy, sizeof (*revision_ptr),
+                        (const void *)apr_pstrdup (pool, path));
+          revision_ptr++;
+        }
+      if (revision_ptr == revision_ptr_end)
+        {
+          continue;
+        }
+      SVN_ERR (svn_fs_history_prev (&new_history, history, TRUE, subpool));
+      history = new_history;
+      if (history)
+        {
+          SVN_ERR (svn_fs_history_location (&path, &revision, history, pool));
+        }
+    }
+
+  *locations = locations_ret;
+  apr_pool_destroy (subpool);
+
+  return SVN_NO_ERROR;
+}
+
Index: subversion/libsvn_ra_svn/client.c
===================================================================
--- subversion/libsvn_ra_svn/client.c	(revision 9476)
+++ subversion/libsvn_ra_svn/client.c	(working copy)
@@ -1059,6 +1059,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 past_revision;
+  svn_ra_svn_item_t *item;
+  int is_done;
+  int i;
+  const char *ret_path;
+  apr_hash_t *fs_locations;
+
+  *locations = NULL;
+
+  /* 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++)
+    {
+      past_revision = ((svn_revnum_t *)location_revisions->elts)[i];
+      SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "!r!", past_revision));
+    }
+
+  SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "!))"));
+
+  SVN_ERR(handle_auth_request(sess, pool));
+
+  /* Read the hash items. */
+  is_done = 0;
+
+  fs_locations = apr_hash_make(pool);
+
+  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",
+                                          &past_revision, &ret_path));
+          apr_hash_set(fs_locations, apr_pmemdup(pool,
+                                                 &past_revision,
+                                                 sizeof(past_revision)),
+                       sizeof(past_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, ""));
+
+  *locations = fs_locations;
+
+  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 +1144,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_dav/ra_dav.h
===================================================================
--- subversion/libsvn_ra_dav/ra_dav.h	(revision 9476)
+++ subversion/libsvn_ra_dav/ra_dav.h	(working copy)
@@ -626,7 +626,11 @@
   ELEM_name_creator_displayname,
   ELEM_svn_error,
   ELEM_human_readable,
-  ELEM_repository_uuid
+  ELEM_repository_uuid,
+  ELEM_get_locations_report,
+  ELEM_location,
+  ELEM_location_revision,
+  ELEM_path
 };
 
 /* ### docco */
@@ -696,6 +700,16 @@
                              int okay_2,
                              apr_pool_t *pool);
 
+/*
+ * Handler for the get_locations ra_plugin_t callback.
+ * */
+svn_error_t *
+svn_ra_dav__get_locations (void *session_baton,
+                           apr_hash_t **locations,
+                           const char *url,
+                           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 9476)
+++ subversion/libsvn_ra_dav/session.c	(working copy)
@@ -851,7 +851,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 9476)
+++ subversion/libsvn_ra_dav/fetch.c	(working copy)
@@ -1091,7 +1091,196 @@
   return SVN_NO_ERROR;
 }
 
+typedef struct {
+  svn_ra_session_t * ras;
+  apr_hash_t * hash;
+  apr_pool_t *pool;
+  svn_error_t * error;
+  svn_revnum_t rev;
+  const char * path;
+} get_locations_baton_t;
 
+/*
+ * Plan for processing the XML. The XML will be of the form:
+ *
+ <S:get-location-report xmlns...>
+      <S:location>
+        <S:location-revision>45</S:location-revision>
+        <D:href>http://localhost/svn/repos/my-old-file.txt</D:href>
+      </S:location>
+      <S:location>
+        <S:location-revision>56</S:location-revision>
+        <D:href>http://localhost/svn/repos/my-other-old-file.txt</D:href>
+      </S:location>
+ </S:get-location-report>
+ *
+ * What we are going to do is:
+ * 1. Upon S:location start tag we will nullify both "rev" and "url".
+ * 2. Upon S:location-revision end we will process the CDATA and assign it to
+ * "rev".
+ * 3. Upon D:href end we will process the CDATA and assign it to "url". (after
+ * checking that it is under "repos_url".
+ * 4. Upon S:location end we will see if both "url" and "rev" were assigned
+ * and if so, place them in the hash.
+ * */
+/*
+ * TODO: Write this thing and fill in the handlers logic.
+ * */
+/* 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 },
+  { SVN_XML_NAMESPACE, "location-revision", ELEM_location_revision, SVN_RA_DAV__XML_CDATA },
+  { SVN_XML_NAMESPACE, "path", ELEM_path, SVN_RA_DAV__XML_CDATA },
+  { NULL }
+};
+
+/* This implements the `svn_ra_dav__xml_validate_cb' prototype. */
+static int gloc_validate_element(void *userdata, svn_ra_dav__xml_elmid parent,
+                                 svn_ra_dav__xml_elmid child)
+{
+  return SVN_RA_DAV__XML_VALID;
+}
+
+/* This implements the `svn_ra_dav__xml_startelm_cb' prototype. */
+static int gloc_start_element(void *userdata, const svn_ra_dav__xml_elm_t *elm,
+                              const char **atts)
+{
+  if (elm->id == ELEM_location)
+    {
+      get_locations_baton_t * baton = (get_locations_baton_t *)userdata;
+      baton->rev = SVN_INVALID_REVNUM;
+      baton->path = NULL;
+    }
+  return SVN_RA_DAV__XML_VALID;
+}
+
+/* This implements the `svn_ra_dav__xml_endelm_cb' prototype. */
+static int gloc_end_element(void *userdata, const svn_ra_dav__xml_elm_t *elm,
+                            const char *cdata)
+{
+  get_locations_baton_t * baton = (get_locations_baton_t *)userdata;
+  /* TODO: check if the revision and path were already specified and
+   * if so throw an error (or should we?)
+   * */
+  if (elm->id == ELEM_location_revision)
+    {
+      baton->rev = SVN_STR_TO_REV(cdata);
+    }
+  else if (elm->id == ELEM_path)
+    {
+      baton->path = apr_pstrdup(baton->pool, cdata);
+    }
+  else if (elm->id == ELEM_location)
+    {
+      /* Check if path and rev were both specified */
+      if (baton->path && (baton->rev != SVN_INVALID_REVNUM))
+        {
+          /* Yippi Ya Yay - we can register a location in the hash. */
+          apr_hash_set (baton->hash,
+                        apr_pmemdup (baton->pool, (const void *)&(baton->rev),
+                                     sizeof (baton->rev)),
+                        sizeof (baton->rev),
+                        (const void *)baton->path
+                       );
+          /*
+           * Now, let's nullify path and rev just in case.
+           * */
+          baton->path = NULL;
+          baton->rev = SVN_INVALID_REVNUM;
+        }
+      /* If not - throw an exception */
+      else
+        {
+          baton->error =
+              svn_error_createf (SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
+                                 "%s", "Improper XML passed from server");
+          return SVN_RA_DAV__XML_INVALID; /* ### Any other way to express
+                                                 an err? */
+        }
+    }
+
+  return SVN_RA_DAV__XML_VALID;
+}
+
+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;
+  int repos_url_len;
+  const char *repos_url;
+  get_locations_baton_t request_baton;
+  apr_hash_t *fs_locations;
+  svn_revnum_t *revision_ptr, *revision_ptr_end;
+
+  *locations = NULL;
+
+  request_body = svn_stringbuf_create ("", pool);
+  svn_stringbuf_appendcstr (request_body,
+                            "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+                            "<S:get-locations xmlns:S=\"" SVN_XML_NAMESPACE "\" "
+                            "xmlns:D=\"DAV:\">");
+
+
+  svn_stringbuf_appendcstr (request_body,
+                            apr_psprintf (pool,
+                                          "<S:path>%s</S:path>"
+                                          "<S:peg-revision>%" SVN_REVNUM_T_FMT "</S:peg-revision>",
+                                          relative_path, peg_revision));
+
+  revision_ptr = (svn_revnum_t *)location_revisions->elts;
+  revision_ptr_end = revision_ptr + location_revisions->nelts;
+
+  for(;revision_ptr<revision_ptr_end;revision_ptr++)
+    {
+      svn_stringbuf_appendcstr (request_body,
+                                apr_psprintf (pool,
+                                              "<S:location-revision>%"
+                                              SVN_REVNUM_T_FMT
+                                              "</S:location-revision>",
+                                              *revision_ptr));
+    }
+
+  svn_stringbuf_appendcstr (request_body, "</S:get-locations>");
+
+
+  fs_locations = apr_hash_make (pool);
+
+  request_baton.ras = ras;
+  request_baton.hash = fs_locations;
+  request_baton.pool = pool;
+  request_baton.error = NULL;
+  request_baton.rev = SVN_INVALID_REVNUM;
+  request_baton.path = NULL;
+
+  SVN_ERR (svn_ra_dav__parsed_request_compat (ras->sess, "REPORT",
+                                              ras->root.path,
+                                              request_body->data, NULL, NULL,
+                                              gloc_report_elements,
+                                              gloc_validate_element,
+                                              gloc_start_element,
+                                              gloc_end_element,
+                                              &request_baton, NULL, NULL,
+                                              pool));
+
+  if (request_baton.error)
+    {
+      return request_baton.error;
+    }
+
+  *locations = fs_locations;
+
+  return SVN_NO_ERROR;
+}
+
 /* 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 9476)
+++ subversion/svnserve/serve.c	(working copy)
@@ -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 past_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;
+  const char *fs_path;
+  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");
+      past_revision = (svn_revnum_t)(elt->u.number);
+      APR_ARRAY_PUSH(location_revisions, svn_revnum_t) = past_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 accept
+   * 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 }
 };
 


