Index: subversion/include/svn_repos.h
===================================================================
--- subversion/include/svn_repos.h	(revision 10134)
+++ subversion/include/svn_repos.h	(working copy)
@@ -538,6 +538,25 @@
                    apr_pool_t *pool);
 
 /**
+ * @since New in 1.1
+ *
+ * Call @a history_func (with @a history_baton) for each interesting
+ * history location in the lifetime of @a path in @a fs, for @a num_back
+ * revisions starting at @a start and going backwards.  Only cross
+ * filesystem copy history if @a cross_copies is @c TRUE.  And do all
+ * of this in @a pool.
+ */
+svn_error_t *
+svn_repos_history_bounded (svn_fs_t *fs,
+                           const char *path,
+                           svn_repos_history_func_t history_func,
+                           void *history_baton,
+                           svn_revnum_t start,
+                           unsigned int num_back,
+                           svn_boolean_t cross_copies,
+                           apr_pool_t *pool);
+
+/**
  * @since New in 1.1.
  *
  * Set @a *locations to be a mapping of the revisions to the paths of
@@ -626,6 +645,18 @@
                     apr_pool_t *pool);
 
 
+svn_error_t *
+svn_repos_get_logs_bounded (svn_repos_t *repos,
+                            const char *path,
+                            svn_revnum_t start,
+                            unsigned int num_back,
+                            svn_boolean_t discover_changed_paths,
+                            svn_boolean_t strict_node_history,
+                            svn_log_message_receiver_t receiver,
+                            void *receiver_baton,
+                            apr_pool_t *pool);
+
+
 /* ---------------------------------------------------------------*/
 
 /**
Index: subversion/include/svn_ra.h
===================================================================
--- subversion/include/svn_ra.h	(revision 10134)
+++ subversion/include/svn_ra.h	(working copy)
@@ -733,6 +733,15 @@
                                  apr_array_header_t *location_revisions,
                                  apr_pool_t *pool);
 
+  svn_error_t *(*get_log_bounded) (void *session_baton,
+                                   const char *path,
+                                   svn_revnum_t start,
+                                   unsigned int num_back,
+                                   svn_boolean_t discover_changed_paths,
+                                   svn_boolean_t strict_node_history,
+                                   svn_log_message_receiver_t receiver,
+                                   void *receiver_baton,
+                                   apr_pool_t *pool);
 } svn_ra_plugin_t;
 
 
Index: subversion/include/svn_opt.h
===================================================================
--- subversion/include/svn_opt.h	(revision 10134)
+++ subversion/include/svn_opt.h	(working copy)
@@ -197,7 +197,10 @@
   svn_opt_revision_working,
 
   /** repository youngest */
-  svn_opt_revision_head
+  svn_opt_revision_head,
+
+  /** an offset relative to another revision */
+  svn_opt_revision_offset
 };
 
 
@@ -214,7 +217,7 @@
 
 /**
  * Set @a *start_revision and/or @a *end_revision according to @a arg, 
- * where @a arg is "N" or "N:M", like so:
+ * where @a arg is "N" or "N:M" or "N:-M", like so:
  * 
  *    - If @a arg is "N", set @a *start_revision's kind to
  *      @c svn_opt_revision_number and its value to the number N; and
@@ -224,6 +227,10 @@
  *      kinds to @c svn_opt_revision_number and values to N and M
  *      respectively. 
  * 
+ *    - If @a arg is "N:-M", set @a *start_revision's kind to 
+ *      @c svn_opt_revision_number and value to N, and set @a *end_revision's 
+ *      kind to @c svn_opt_revision_offset and value to M.
+ *
  * N and/or M may be one of the special revision descriptors
  * recognized by @c revision_from_word().
  *
Index: subversion/libsvn_subr/opt.c
===================================================================
--- subversion/libsvn_subr/opt.c	(revision 10134)
+++ subversion/libsvn_subr/opt.c	(working copy)
@@ -390,10 +390,23 @@
   right_rev = parse_one_rev (start_revision, left_rev, pool);
   if (right_rev && *right_rev == ':')
     {
+      svn_boolean_t found_offset = FALSE;
+
       right_rev++;
+
+      if (right_rev && *right_rev == '-')
+        {
+          found_offset = TRUE;
+          right_rev++;
+        }
+
       end = parse_one_rev (end_revision, right_rev, pool);
-      if (!end || *end != '\0')
+      if (!end || *end != '\0'
+          || (found_offset && end_revision->kind != svn_opt_revision_number))
         return -1;
+
+      if (found_offset)
+        end_revision->kind = svn_opt_revision_offset;
     }
   else if (!right_rev || *right_rev != '\0')
     return -1;
Index: subversion/libsvn_ra_local/ra_plugin.c
===================================================================
--- subversion/libsvn_ra_local/ra_plugin.c	(revision 10134)
+++ subversion/libsvn_ra_local/ra_plugin.c	(working copy)
@@ -601,6 +601,35 @@
 
 
 static svn_error_t *
+svn_ra_local__get_log_bounded (void *session_baton,
+                               const char *path,
+                               svn_revnum_t start,
+                               unsigned int num_back,
+                               svn_boolean_t discover_changed_paths,
+                               svn_boolean_t strict_node_history,
+                               svn_log_message_receiver_t receiver,
+                               void *receiver_baton,
+                               apr_pool_t *pool)
+{
+  svn_ra_local__session_baton_t *sbaton = session_baton;
+  const char *abs_path;
+  int i;
+
+  abs_path = svn_path_join (sbaton->fs_path, path, sbaton->pool);
+ 
+  return svn_repos_get_logs_bounded (sbaton->repos,
+                                     abs_path,
+                                     start,
+                                     num_back,
+                                     discover_changed_paths,
+                                     strict_node_history,
+                                     receiver,
+                                     receiver_baton,
+                                     sbaton->pool);
+}
+
+
+static svn_error_t *
 svn_ra_local__do_check_path (void *session_baton,
                              const char *path,
                              svn_revnum_t revision,
@@ -891,7 +920,8 @@
   svn_ra_local__do_check_path,
   svn_ra_local__get_uuid,
   svn_ra_local__get_repos_root,
-  svn_ra_local__get_locations
+  svn_ra_local__get_locations,
+  svn_ra_local__get_log_bounded
 };
 
 
Index: subversion/libsvn_client/log.c
===================================================================
--- subversion/libsvn_client/log.c	(revision 10134)
+++ subversion/libsvn_client/log.c	(working copy)
@@ -74,6 +74,11 @@
          _("Missing required revision specification"));
     }
 
+  if (end->kind == svn_opt_revision_offset && targets->nelts != 1)
+    return svn_error_create
+      (SVN_ERR_INCORRECT_PARAMS, NULL,
+       _("Multiple targets makes no sense with an offset revision"));
+
   start_revnum = end_revnum = SVN_INVALID_REVNUM;
 
   path = (APR_ARRAY_IDX(targets, 0, const char *));
@@ -205,7 +210,7 @@
       SVN_ERR (svn_client__get_revision_number
                (&start_revnum, ra_lib, session, start, base_name, pool));
 
-    if (! end_is_local)
+    if (! end_is_local && end->kind != svn_opt_revision_offset)
       SVN_ERR (svn_client__get_revision_number
                (&end_revnum, ra_lib, session, end, base_name, pool));
 
@@ -243,35 +248,64 @@
             if (start_is_local)
               SVN_ERR (svn_client__get_revision_number
                        (&start_revnum, ra_lib, session, start, target, pool));
-            
-            if (end_is_local)
-              SVN_ERR (svn_client__get_revision_number
-                       (&end_revnum, ra_lib, session, end, target, pool));
 
-            err = ra_lib->get_log (session,
-                                   condensed_targets,
-                                   start_revnum,
-                                   end_revnum,
-                                   discover_changed_paths,
-                                   strict_node_history,
-                                   receiver,
-                                   receiver_baton,
-                                   pool);
+            if (end->kind != svn_opt_revision_offset)
+              { 
+                if (end_is_local)
+                  SVN_ERR (svn_client__get_revision_number
+                           (&end_revnum, ra_lib, session, end, target, pool));
+
+                err = ra_lib->get_log (session,
+                                       condensed_targets,
+                                       start_revnum,
+                                       end_revnum,
+                                       discover_changed_paths,
+                                       strict_node_history,
+                                       receiver,
+                                       receiver_baton,
+                                       pool);
+              }
+            else
+              {
+                err = ra_lib->get_log_bounded (session,
+                                               APR_ARRAY_IDX(condensed_targets,
+                                                             0, const char *),
+                                               start_revnum,
+                                               end->value.number,
+                                               discover_changed_paths,
+                                               strict_node_history,
+                                               receiver,
+                                               receiver_baton,
+                                               pool);
+              }
+
             if (err)
               break;
           }
       }
     else  /* both revisions are static, so no loop needed */
       {
-        err = ra_lib->get_log (session,
-                               condensed_targets,
-                               start_revnum,
-                               end_revnum,
-                               discover_changed_paths,
-                               strict_node_history,
-                               receiver,
-                               receiver_baton,
-                               pool);
+        if (end->kind != svn_opt_revision_offset)
+          err = ra_lib->get_log (session,
+                                 condensed_targets,
+                                 start_revnum,
+                                 end_revnum,
+                                 discover_changed_paths,
+                                 strict_node_history,
+                                 receiver,
+                                 receiver_baton,
+                                 pool);
+        else
+          err = ra_lib->get_log_bounded (session,
+                                         APR_ARRAY_IDX(condensed_targets,
+                                                       0, const char *),
+                                         start_revnum,
+                                         end->value.number,
+                                         discover_changed_paths,
+                                         strict_node_history,
+                                         receiver,
+                                         receiver_baton,
+                                         pool);
       }
     
     /* Special case: If there have been no commits, we'll get an error
Index: subversion/tests/libsvn_repos/repos-test.c
===================================================================
--- subversion/tests/libsvn_repos/repos-test.c	(revision 10134)
+++ subversion/tests/libsvn_repos/repos-test.c	(working copy)
@@ -691,6 +691,59 @@
       }
   }
 
+  {
+    apr_array_header_t *revs = apr_array_make (spool, 10, 
+                                               sizeof (svn_revnum_t));
+
+    SVN_ERR (svn_repos_history_bounded (fs, "A/D", history_to_revs_array, revs,
+                                        5, 3, TRUE, spool));
+
+    if (revs->nelts != 3)
+      return svn_error_createf (SVN_ERR_FS_GENERAL, NULL,
+                                "Got incorrect number of revs from "
+                                "svn_repos_history_bounded.  Got %d, expected "
+                                "%d", revs->nelts, 3);
+    else
+      {
+        svn_revnum_t rev1 = ((svn_revnum_t *)revs->elts)[0];
+        svn_revnum_t rev2 = ((svn_revnum_t *)revs->elts)[1];
+        svn_revnum_t rev3 = ((svn_revnum_t *)revs->elts)[2];
+
+        if (rev1 != 5)
+          return svn_error_createf (SVN_ERR_FS_GENERAL, NULL,
+                                    "Expected revision 5 here, got %d", rev1);
+        if (rev2 != 4)
+          return svn_error_createf (SVN_ERR_FS_GENERAL, NULL,
+                                    "Expected revision 4 here, got %d", rev2);
+        if (rev3 != 3)
+          return svn_error_createf (SVN_ERR_FS_GENERAL, NULL,
+                                    "Expected revision 3 here, got %d", rev3);
+      }
+
+    revs = apr_array_make (spool, 10, sizeof (svn_revnum_t));
+
+    SVN_ERR (svn_repos_history_bounded (fs, "A/D/gamma",
+                                        history_to_revs_array, revs,
+                                        8, 2, TRUE, spool));
+    if (revs->nelts != 2)
+      return svn_error_createf (SVN_ERR_FS_GENERAL, NULL,
+                                "Got incorrect number of revs from "
+                                "svn_repos_history_bounded.  Got %d, expected "
+                                "%d", revs->nelts, 2);
+    else
+      {
+        svn_revnum_t rev1 = ((svn_revnum_t *)revs->elts)[0];
+        svn_revnum_t rev2 = ((svn_revnum_t *)revs->elts)[1];
+
+        if (rev1 != 8)
+          return svn_error_createf (SVN_ERR_FS_GENERAL, NULL,
+                                    "Expected revision 8 here, got %d", rev1);
+        if (rev2 != 6)
+          return svn_error_createf (SVN_ERR_FS_GENERAL, NULL,
+                                    "Expected revision 6 here, got %d", rev2);
+      }
+  }
+
   /* Destroy the subpool. */
   svn_pool_destroy (spool);
 
Index: subversion/libsvn_repos/log.c
===================================================================
--- subversion/libsvn_repos/log.c	(revision 10134)
+++ subversion/libsvn_repos/log.c	(working copy)
@@ -311,3 +311,78 @@
 
   return SVN_NO_ERROR;
 }
+
+svn_error_t *
+svn_repos_get_logs_bounded (svn_repos_t *repos,
+                            const char *path,
+                            svn_revnum_t start,
+                            unsigned int num_back,
+                            svn_boolean_t discover_changed_paths,
+                            svn_boolean_t strict_node_history,
+                            svn_log_message_receiver_t receiver,
+                            void *receiver_baton,
+                            apr_pool_t *pool)
+{
+  svn_revnum_t this_rev, head = SVN_INVALID_REVNUM;
+  apr_pool_t *subpool = svn_pool_create (pool);
+  svn_fs_t *fs = repos->fs;
+  apr_array_header_t *revs = NULL;
+  int idx;
+
+  SVN_ERR (svn_fs_youngest_rev (&head, fs, pool));
+
+  if (! SVN_IS_VALID_REVNUM (start))
+    start = head;
+
+  /* Check that revisions are sane before ever invoking receiver. */
+  if (start > head)
+    return svn_error_createf
+      (SVN_ERR_FS_NO_SUCH_REVISION, 0,
+       _("No such revision %ld"), start);
+
+  /* Get the changed revisions for this path. */
+  revs = apr_array_make (pool, 64, sizeof (svn_revnum_t));
+  SVN_ERR (svn_repos_history_bounded (fs, path, history_to_revs_array, 
+                                      revs, start, num_back, 
+                                      strict_node_history ? FALSE : TRUE, 
+                                      pool));
+
+  for (idx = 0; idx < revs->nelts; ++idx)
+    {
+      svn_string_t *author, *date, *message;
+      apr_hash_t *changed_paths = NULL;
+      this_rev = ((svn_revnum_t *)(revs->elts))[idx];
+
+      SVN_ERR (svn_fs_revision_prop
+               (&author, fs, this_rev, SVN_PROP_REVISION_AUTHOR, subpool));
+      SVN_ERR (svn_fs_revision_prop
+               (&date, fs, this_rev, SVN_PROP_REVISION_DATE, subpool));
+      SVN_ERR (svn_fs_revision_prop
+               (&message, fs, this_rev, SVN_PROP_REVISION_LOG, subpool));
+
+      /* ### Below, we discover changed paths if the user requested
+         them (i.e., "svn log -v" means `discover_changed_paths' will
+         be non-zero here).  */
+
+      if ((this_rev > 0) && discover_changed_paths)
+        {
+          svn_fs_root_t *newroot;
+          SVN_ERR (svn_fs_revision_root (&newroot, fs, this_rev, subpool));
+          SVN_ERR (detect_changed (&changed_paths, newroot, subpool));
+        }
+
+      SVN_ERR ((*receiver) (receiver_baton,
+                            changed_paths,
+                            this_rev,
+                            author ? author->data : NULL,
+                            date ? date->data : NULL,
+                            message ? message->data : NULL,
+                            subpool));
+      
+      svn_pool_clear (subpool);
+    }
+
+  svn_pool_destroy (subpool);
+
+  return SVN_NO_ERROR;
+}
Index: subversion/libsvn_repos/rev_hunt.c
===================================================================
--- subversion/libsvn_repos/rev_hunt.c	(revision 10134)
+++ subversion/libsvn_repos/rev_hunt.c	(working copy)
@@ -253,6 +253,73 @@
   return SVN_NO_ERROR;
 }
 
+svn_error_t *
+svn_repos_history_bounded (svn_fs_t *fs,
+                           const char *path,
+                           svn_repos_history_func_t history_func,
+                           void *history_baton,
+                           svn_revnum_t start,
+                           unsigned int num_back,
+                           svn_boolean_t cross_copies,
+                           apr_pool_t *pool)
+{
+  svn_fs_history_t *history;
+  apr_pool_t *oldpool = svn_pool_create (pool);
+  apr_pool_t *newpool = svn_pool_create (pool);
+  const char *history_path;
+  svn_revnum_t history_rev;
+  svn_fs_root_t *root;
+
+  /* Validate the revision. */
+  if (! SVN_IS_VALID_REVNUM (start))
+    return svn_error_createf 
+      (SVN_ERR_FS_NO_SUCH_REVISION, 0, 
+       _("Invalid start revision %ld"), start);
+
+  /* Hey, look, we're done! */
+  if (num_back == 0)
+    return;
+
+  /* Get a revision root for START, and an initial HISTORY baton.  */
+  SVN_ERR (svn_fs_revision_root (&root, fs, start, pool));
+  SVN_ERR (svn_fs_node_history (&history, root, path, oldpool));
+
+  /* Now, we loop over the history items, calling svn_fs_history_prev(). */
+  do
+    {
+      apr_pool_t *tmppool;
+
+      /* Note that we have to do some crazy pool work here.  We can't
+         get rid of the old history until we use it to get the new, so
+         we alternate back and forth between our subpools.  */
+      SVN_ERR (svn_fs_history_prev (&history, history, cross_copies, newpool));
+
+      /* Only continue if there is further history to deal with. */
+      if (! history)
+        break;
+
+      /* Fetch the location information for this history step. */
+      SVN_ERR (svn_fs_history_location (&history_path, &history_rev,
+                                        history, newpool));
+      
+      /* Call the user-provided callback function. */
+      SVN_ERR (history_func (history_baton, history_path, 
+                             history_rev, newpool));
+
+      /* We're done with the old history item, so we can clear its
+         pool, and then toggle our notion of "the old pool". */
+      svn_pool_clear (oldpool);
+      tmppool = oldpool;
+      oldpool = newpool;
+      newpool = tmppool;
+    }
+  while (--num_back);
+
+  svn_pool_destroy (oldpool);
+  svn_pool_destroy (newpool);
+  return SVN_NO_ERROR;
+}
+
 /* Compare revision numbers for sorting in decreasing order. */
 static int
 compare_revnums (const void *p_a, const void *p_b)
Index: subversion/libsvn_ra_svn/client.c
===================================================================
--- subversion/libsvn_ra_svn/client.c	(revision 10134)
+++ subversion/libsvn_ra_svn/client.c	(working copy)
@@ -1045,6 +1045,19 @@
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *ra_svn_log_bounded(void *baton,
+                                       const char *path,
+                                       svn_revnum_t start,
+                                       unsigned int num_back,
+                                       svn_boolean_t discover_changed_paths,
+                                       svn_boolean_t strict_node_history,
+                                       svn_log_message_receiver_t receiver,
+                                       void *receiver_baton, apr_pool_t *pool)
+{
+  return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL,
+                          "need to add get_log_bounded to ra_svn");
+}
+
 static svn_error_t *ra_svn_check_path(void *baton, const char *path,
                                       svn_revnum_t rev, svn_node_kind_t *kind,
                                       apr_pool_t *pool)
@@ -1143,7 +1156,8 @@
   ra_svn_check_path,
   ra_svn_get_uuid,
   ra_svn_get_repos_root,
-  ra_svn_get_locations
+  ra_svn_get_locations,
+  ra_svn_log_bounded
 };
 
 svn_error_t *svn_ra_svn_init(int abi_version, apr_pool_t *pool,
Index: subversion/libsvn_ra_dav/log.c
===================================================================
--- subversion/libsvn_ra_dav/log.c	(revision 10134)
+++ subversion/libsvn_ra_dav/log.c	(working copy)
@@ -431,3 +431,17 @@
 
   return SVN_NO_ERROR;
 }
+
+svn_error_t * svn_ra_dav__get_log_bounded(void *session_baton,
+                                          const char *path,
+                                          svn_revnum_t start,
+                                          unsigned int num_back,
+                                          svn_boolean_t discover_changed_paths,
+                                          svn_boolean_t strict_node_history,
+                                          svn_log_message_receiver_t receiver,
+                                          void *receiver_baton,
+                                          apr_pool_t *pool)
+{
+  return svn_error_create (SVN_ERR_RA_NOT_IMPLEMENTED, NULL,
+                           "haven't implemented get_log_bounded for ra_dav");
+}
Index: subversion/libsvn_ra_dav/ra_dav.h
===================================================================
--- subversion/libsvn_ra_dav/ra_dav.h	(revision 10134)
+++ subversion/libsvn_ra_dav/ra_dav.h	(working copy)
@@ -709,6 +709,17 @@
                            apr_array_header_t *location_revisions,
                            apr_pool_t *pool);
 
+svn_error_t *
+svn_ra_dav__get_log_bounded (void *session_baton,
+                             const char *path,
+                             svn_revnum_t start,
+                             unsigned int num_back,
+                             svn_boolean_t discover_changed_paths,
+                             svn_boolean_t strict_node_history,
+                             svn_log_message_receiver_t receiver,
+                             void *receiver_baton,
+                             apr_pool_t *pool);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
Index: subversion/libsvn_ra_dav/session.c
===================================================================
--- subversion/libsvn_ra_dav/session.c	(revision 10134)
+++ subversion/libsvn_ra_dav/session.c	(working copy)
@@ -853,7 +853,8 @@
   svn_ra_dav__do_check_path,
   svn_ra_dav__do_get_uuid,
   svn_ra_dav__get_repos_root,
-  svn_ra_dav__get_locations
+  svn_ra_dav__get_locations,
+  svn_ra_dav__get_log_bounded
 };
 
 
