Josh Pieper wrote:
>   1. diff [-r M[:N]] [TARGET[@REV]...]
> 
>   2. diff --old=OLD-TGT[@OLDREV] --new=NEW-TGT[@NEWREV] [PATH...]
> 
>   3. diff OLD-URL[@OLDREV] NEW-URL[@NEWREV]
> 		               
> Is this syntax agreeable to everyone?  Are there any threads or
> important points that I've missed?
Well, due to the resounding lack of response, I am assuming that there
is at least some partial agreement that this syntax is workable and
have started a patch to implement it.  Since the patch does change the
syntax and isn't easily broken up, I'm posting a work-in-progress
here.
The patch covers nearly all of the new syntax except the case of
comparing a repository URL to a working copy.  That code used the
update editor to generate a diff and I am not sure how best to rework
it to handle pegged diffs.  I am open to suggestions on how to proceed
on that front.
-Josh
--------------------
Issue 1093: Change the syntax for 'svn diff' and allow it to operate
on pegged paths, thus enabling copy history to be traversed.  The new
syntax has two forms and a third shortcut format.
  1.  diff [-r N[:M]] [TARGET[@REV]...]
  2.  diff --old=OLD-TGT[@OLDREV] --new=NEW-TGT[@NEWREV] [PATH...]
  3.  diff OLD-URL[@OLDREV] NEW-URL[@NEWREV]
Where case 1 performs pegged diffs, and case 3 is a shortcut for case
2 when no PATHs are specified and both targets are URLs.  It should be
noted that only case 1 will accept the -r option now, to specify a
revision with case 2 or 3, use the @REV syntax.
* subversion/include/client.h
* subversion/libsvn_client/diff.c
  (diff_repos_repos, diff_repos_wc): Accept a peg revision option that
    will cause the actual paths to be diffed to be selected by
    following copy history.
  (do_diff): Let the helper functions know this is not a pegged diff.
  (do_diff_peg): New, implements the logic of svn_client_diff_peg.
  (svn_client_diff_peg): New, performs a pegged diff on a filesystem
    object between two revisions.
* subversion/clients/cmdline/diff-cmd.c
  (parse_path): New, copied from libsvn_subr/opt.c, parses out the
    optional @REV from the end of paths.
  (svn_cl__diff): Implement the new syntax.
* subversion/clients/cmdline/main.c
  (svn_cl__cmd_table): Add new help text for 'svn diff'.
Index: subversion/include/svn_client.h
===================================================================
--- subversion/include/svn_client.h	(revision 9649)
+++ subversion/include/svn_client.h	(working copy)
@@ -803,6 +803,28 @@
                               apr_pool_t *pool);
 
 
+/** Produce diff output which describes the delta between the
+ * filesystem object @a path in peg revision @a peg_revision, as it
+ * changed between @a revision1 and @a revision2.  Print the output of
+ * the diff to @a outfile, and any errors to @a errfile.  @a path can
+ * be either a working-copy path or URL.
+ *
+ * All other options are handled identically to svn_client_diff.
+ */
+svn_error_t *svn_client_diff_peg (const apr_array_header_t *diff_options,
+                                  const char *path,
+                                  const svn_opt_revision_t *peg_revision,
+                                  const svn_opt_revision_t *start_revision,
+                                  const svn_opt_revision_t *end_revision,
+                                  svn_boolean_t recurse,
+                                  svn_boolean_t ignore_ancestry,
+                                  svn_boolean_t no_diff_deleted,
+                                  apr_file_t *outfile,
+                                  apr_file_t *errfile,
+                                  svn_client_ctx_t *ctx,
+                                  apr_pool_t *pool);
+
+
 /** Merge changes from @a source1/@a revision1 to @a source2/@a revision2 into 
  * the working-copy path @a target_wcpath.
  *
Index: subversion/libsvn_client/diff.c
===================================================================
--- subversion/libsvn_client/diff.c	(revision 9649)
+++ subversion/libsvn_client/diff.c	(working copy)
@@ -1532,7 +1532,10 @@
 /* Perform a diff between two repository paths.  
    
    PATH1 and PATH2 may be either URLs or the working copy paths.
-   REVISION1 and REVISION2 are their respective revisions.
+   REVISION1 and REVISION2 are their respective revisions.  If
+   PEG_REVISION is specified, PATH2 is the path at the peg revision,
+   and the actual two paths compared are determined by following copy
+   history from PATH2.
 
    All other options are the same as those passed to svn_client_diff(). */
 static svn_error_t *
@@ -1541,6 +1544,7 @@
                   const svn_opt_revision_t *revision1,
                   const char *path2,
                   const svn_opt_revision_t *revision2,
+                  const svn_opt_revision_t *peg_revision,
                   svn_boolean_t recurse,
                   svn_boolean_t ignore_ancestry,
                   const svn_wc_diff_callbacks_t *callbacks,
@@ -1579,6 +1583,27 @@
   SVN_ERR (svn_ra_init_ra_libs (&ra_baton, pool));
   SVN_ERR (svn_ra_get_ra_library (&ra_lib, ra_baton, url1, pool));
 
+  /* If we are performing a pegged diff, we need to find out what our
+     actual URLs will be. */
+  if (peg_revision->kind != svn_opt_revision_unspecified)
+    {
+      void *sessionpeg;
+      svn_opt_revision_t *start_ignore, *end_ignore;
+      
+      /* Open an RA session to the peg URL. */
+      SVN_ERR (svn_client__open_ra_session (&sessionpeg, ra_lib, url2, NULL,
+                                            NULL, NULL, FALSE, TRUE,
+                                            ctx, temppool));
+
+      SVN_ERR (svn_client__repos_locations (&url1, &start_ignore,
+                                            &url2, &end_ignore,
+                                            path2,
+                                            peg_revision,
+                                            revision1, revision2,
+                                            ra_lib, sessionpeg,
+                                            ctx, pool));
+    }
+  
   /* Open temporary RA sessions to each URL. */
   SVN_ERR (svn_client__open_ra_session (&session1, ra_lib, url1, NULL,
                                         NULL, NULL, FALSE, TRUE, 
@@ -1678,12 +1703,16 @@
    PATH1 may be either a URL or a working copy path.  PATH2 is a
    working copy path.  REVISION1 and REVISION2 are their respective
    revisions.  If REVERSE is TRUE, the diff will be done in reverse.
+   If PEG_REVISION is specified, then PATH1 is the path in the peg
+   revision, and the actual repository path to be compared is
+   determined by following copy history.
 
    All other options are the same as those passed to svn_client_diff(). */
 static svn_error_t *
 diff_repos_wc (const apr_array_header_t *options,
                const char *path1,
                const svn_opt_revision_t *revision1,
+               const svn_opt_revision_t *peg_revision,
                const char *path2,
                const svn_opt_revision_t *revision2,
                svn_boolean_t reverse,
@@ -1713,26 +1742,52 @@
   /* Figure out URL1. */
   SVN_ERR (convert_to_url (&url1, path1, pool));
 
+  /* Establish RA session to URL1's anchor */
+  SVN_ERR (svn_ra_init_ra_libs (&ra_baton, pool));
+  SVN_ERR (svn_ra_get_ra_library (&ra_lib, ra_baton, url1, pool));
+
+  /* If we are performing a pegged diff, we need to find out what our
+     actual URL will be. */
+  if (peg_revision->kind != svn_opt_revision_unspecified)
+    {
+      void *sessionpeg;
+      svn_opt_revision_t *start_ignore, *end_ignore, end;
+      const char *url_ignore;
+
+      end.kind = svn_opt_revision_unspecified;
+      
+      /* Open an RA session to the peg URL. */
+      SVN_ERR (svn_client__open_ra_session (&sessionpeg, ra_lib, url1, NULL,
+                                            NULL, NULL, FALSE, TRUE,
+                                            ctx, pool));
+
+      SVN_ERR (svn_client__repos_locations (&url1, &start_ignore,
+                                            &url_ignore, &end_ignore,
+                                            path1,
+                                            peg_revision,
+                                            revision1, &end,
+                                            ra_lib, sessionpeg,
+                                            ctx, pool));
+    }
+
   /* Possibly split up PATH2 into anchor/target.  If we do so, then we
      must split URL1 as well. */
   anchor1 = url1;
   anchor2 = path2;
   target1 = "";
   target2 = "";
+
   SVN_ERR (svn_io_check_path (path2, &kind, pool));
   if (kind == svn_node_file)
     {
       svn_path_split (path2, &anchor2, &target2, pool);
       svn_path_split (url1, &anchor1, &target1, pool);
     }
-
-  /* Establish RA session to URL1's anchor */
-  SVN_ERR (svn_ra_init_ra_libs (&ra_baton, pool));
-  SVN_ERR (svn_ra_get_ra_library (&ra_lib, ra_baton, anchor1, pool));
+  
   SVN_ERR (svn_client__open_ra_session (&session, ra_lib, anchor1,
                                         NULL, NULL, NULL, FALSE, TRUE,
                                         ctx, pool));
-      
+  
   /* Set up diff editor according to path2's anchor/target. */
   SVN_ERR (svn_wc_adm_open2 (&adm_access, NULL, anchor2, FALSE,
                              (recurse && (! *target2)) ? -1 : 0, pool));
@@ -1805,7 +1860,11 @@
 {
   svn_boolean_t is_local_rev1, is_local_rev2;
   svn_boolean_t is_repos_path1, is_repos_path2;
+  svn_opt_revision_t peg_revision;
 
+  /* We will never do a pegged diff from here. */
+  peg_revision.kind = svn_opt_revision_unspecified;
+
   /* Either path could be a URL or a working copy path.  Let's figure
      out what's what. */
   is_repos_path1 = svn_path_is_url (path1);
@@ -1838,13 +1897,15 @@
       if (is_repos_path2) /* path2 is (effectively) a URL */
         {
           SVN_ERR (diff_repos_repos (options, path1, revision1, path2, 
-                                     revision2, recurse, ignore_ancestry, 
-                                     callbacks, callback_baton, ctx, pool));
+                                     revision2, &peg_revision, recurse,
+                                     ignore_ancestry, callbacks,
+                                     callback_baton, ctx, pool));
         }
       else /* path2 is a working copy path */
         {
-          SVN_ERR (diff_repos_wc (options, path1, revision1, path2, revision2,
-                                  FALSE, recurse, ignore_ancestry, callbacks,
+          SVN_ERR (diff_repos_wc (options, path1, revision1, &peg_revision,
+                                  path2, revision2, FALSE, recurse,
+                                  ignore_ancestry, callbacks,
                                   callback_baton, ctx, pool));
         }
     }
@@ -1852,8 +1913,9 @@
     {
       if (is_repos_path2) /* path2 is (effectively) a URL */
         {
-          SVN_ERR (diff_repos_wc (options, path2, revision2, path1, revision1,
-                                  TRUE, recurse, ignore_ancestry, callbacks,
+          SVN_ERR (diff_repos_wc (options, path2, revision2, &peg_revision,
+                                  path1, revision1, TRUE, recurse,
+                                  ignore_ancestry, callbacks,
                                   callback_baton, ctx, pool));
         }
       else /* path2 is a working copy path */
@@ -1866,9 +1928,81 @@
   return SVN_NO_ERROR;
 }
 
+/* This is basically just the guts of svn_client_diff_peg(). */
+static svn_error_t *
+do_diff_peg (const apr_array_header_t *options,
+             const char *path,
+             const svn_opt_revision_t *peg_revision,
+             const svn_opt_revision_t *revision1,
+             const svn_opt_revision_t *revision2,
+             svn_boolean_t recurse,
+             svn_boolean_t ignore_ancestry,
+             const svn_wc_diff_callbacks_t *callbacks,
+             struct diff_cmd_baton *callback_baton,
+             svn_client_ctx_t *ctx,
+             apr_pool_t *pool)
+{
+  svn_boolean_t is_local_rev1, is_local_rev2;
+  svn_boolean_t is_repos_path;
 
+  /* Either path could be a URL or a working copy path.  Let's figure
+     out what's what. */
+  is_repos_path = svn_path_is_url (path);
 
+  /* Verify our revision arguments in light of the paths. */
+  if ((revision1->kind == svn_opt_revision_unspecified)
+      || (revision2->kind == svn_opt_revision_unspecified))
+    return svn_error_create (SVN_ERR_CLIENT_BAD_REVISION, NULL,
+                             _("Not all required revisions are specified"));
 
+  /* Revisions can be said to be local or remote.  BASE and WORKING,
+     for example, are local.  */
+  is_local_rev1 = ((revision1->kind == svn_opt_revision_base)
+                   || (revision1->kind == svn_opt_revision_working));
+  is_local_rev2 = ((revision2->kind == svn_opt_revision_base)
+                   || (revision2->kind == svn_opt_revision_working));
+
+  if (is_local_rev1 && is_local_rev2)
+    return svn_error_create (SVN_ERR_CLIENT_BAD_REVISION, NULL,
+                             _("At least one revision must be non-local for "
+                               "a pegged diff."));
+
+  if (! is_local_rev1) /* path1 is (effectively) a URL */
+    {
+      if (! is_local_rev2) /* path2 is (effectively) a URL */
+        {
+          SVN_ERR (diff_repos_repos (options, path, revision1, path, 
+                                     revision2, peg_revision, recurse,
+                                     ignore_ancestry, callbacks,
+                                     callback_baton, ctx, pool));
+        }
+      else /* path2 is a working copy path */
+        {
+          SVN_ERR (diff_repos_wc (options, path, revision1, peg_revision, path,
+                                  revision2, FALSE, recurse, ignore_ancestry,
+                                  callbacks, callback_baton, ctx, pool));
+        }
+    }
+  else /* path1 is a working copy path */
+    {
+      if (! is_local_rev2) /* path2 is (effectively) a URL */
+        {
+          SVN_ERR (diff_repos_wc (options, path, revision2, peg_revision,
+                                  path, revision1, TRUE, recurse,
+                                  ignore_ancestry, callbacks,
+                                  callback_baton, ctx, pool));
+        }
+      else
+        {
+          SVN_ERR (diff_wc_wc (options, path, revision1, path, revision2,
+                               recurse, callbacks, callback_baton, ctx, pool));
+        }
+    }
+
+  return SVN_NO_ERROR;
+}
+
+
 /*----------------------------------------------------------------------- */
 
 /*** Public Interfaces. ***/
@@ -1963,6 +2097,54 @@
 
 
 svn_error_t *
+svn_client_diff_peg (const apr_array_header_t *options,
+                     const char *path,
+                     const svn_opt_revision_t *peg_revision,
+                     const svn_opt_revision_t *start_revision,
+                     const svn_opt_revision_t *end_revision,
+                     svn_boolean_t recurse,
+                     svn_boolean_t ignore_ancestry,
+                     svn_boolean_t no_diff_deleted,
+                     apr_file_t *outfile,
+                     apr_file_t *errfile,
+                     svn_client_ctx_t *ctx,
+                     apr_pool_t *pool)
+{
+  struct diff_cmd_baton diff_cmd_baton;
+  svn_wc_diff_callbacks_t diff_callbacks;
+
+  diff_callbacks.file_changed = diff_file_changed;
+  diff_callbacks.file_added = diff_file_added;
+  diff_callbacks.file_deleted = no_diff_deleted ? diff_file_deleted_no_diff :
+                                                  diff_file_deleted_with_diff;
+  diff_callbacks.dir_added =  diff_dir_added;
+  diff_callbacks.dir_deleted = diff_dir_deleted;
+  diff_callbacks.props_changed = diff_props_changed;
+    
+  diff_cmd_baton.orig_path_1 = path;
+  diff_cmd_baton.orig_path_2 = path;
+
+  diff_cmd_baton.options = options;
+  diff_cmd_baton.pool = pool;
+  diff_cmd_baton.outfile = outfile;
+  diff_cmd_baton.errfile = errfile;
+  diff_cmd_baton.revnum1 = SVN_INVALID_REVNUM;
+  diff_cmd_baton.revnum2 = SVN_INVALID_REVNUM;
+
+  diff_cmd_baton.config = ctx->config;
+
+  return do_diff_peg (options,
+                      path, peg_revision,
+                      start_revision, end_revision,
+                      recurse,
+                      ignore_ancestry,
+                      &diff_callbacks, &diff_cmd_baton,
+                      ctx,
+                      pool);
+}
+
+
+svn_error_t *
 svn_client_merge (const char *source1,
                   const svn_opt_revision_t *revision1,
                   const char *source2,
Index: subversion/clients/cmdline/diff-cmd.c
===================================================================
--- subversion/clients/cmdline/diff-cmd.c	(revision 9649)
+++ subversion/clients/cmdline/diff-cmd.c	(working copy)
@@ -30,6 +30,7 @@
 #include "svn_delta.h"
 #include "svn_error.h"
 #include "svn_types.h"
+#include "svn_utf.h"
 #include "cl.h"
 
 #include "svn_private_config.h"
@@ -37,6 +38,67 @@
 
 /*** Code. ***/
 
+/* Parse a working-copy or url PATH, looking for an "@" sign, e.g.
+
+         foo/bar/baz@13
+         http://blah/bloo@27
+         blarg/snarf@HEAD
+
+   If an "@" is found, return the two halves in *TRUEPATH and *REV,
+   allocating in POOL.
+
+   If no "@" is found, set *TRUEPATH to PATH and *REV to kind 'unspecified'.
+*/
+static svn_error_t *
+parse_path (svn_opt_revision_t *rev,
+            const char **truepath,
+            const char *path /* UTF-8! */,
+            apr_pool_t *pool)
+{
+  int i;
+  apr_pool_t *subpool = svn_pool_create (pool);
+  svn_opt_revision_t start_revision, end_revision;
+
+  /* scanning from right to left, just to be friendly to any
+     screwed-up filenames that might *actually* contain @-signs.  :-) */
+  for (i = (strlen (path) - 1); i >= 0; i--)
+    {
+      /* If we hit a path separator, stop looking. */
+      if (path[i] == '/')
+        break;
+
+      if (path[i] == '@')
+        {
+          const char *native_rev;
+
+          SVN_ERR (svn_utf_cstring_from_utf8 (&native_rev, path + i + 1,
+                                              subpool));
+
+          if (svn_opt_parse_revision (&start_revision,
+                                      &end_revision,
+                                      native_rev, subpool))
+            return svn_error_createf (SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+                                      _("Syntax error parsing revision '%s'"),
+                                      path + i + 1);
+
+          *truepath = apr_pstrndup (pool, path, i);
+          rev->kind = start_revision.kind;
+          rev->value = start_revision.value;
+
+          svn_pool_destroy (subpool);
+          return SVN_NO_ERROR;
+        }
+    }
+
+  /* Didn't find an @-sign. */
+  *truepath = path;
+  rev->kind = svn_opt_revision_unspecified;
+
+  svn_pool_destroy (subpool);
+  return SVN_NO_ERROR;
+}
+
+
 /* An svn_opt_subcommand_t to handle the 'diff' command.
    This implements the `svn_opt_subcommand_t' interface. */
 svn_error_t *
@@ -51,6 +113,7 @@
   apr_status_t status;
   const char *old_target, *new_target;
   apr_pool_t *subpool;
+  svn_boolean_t pegged_diff = FALSE;
   int i;
 
   /* Fall back to "" to get options initialized either way. */
@@ -67,36 +130,62 @@
     return svn_error_wrap_apr (status, _("Can't open stderr"));
 
   if (! opt_state->old_target && ! opt_state->new_target
-      && opt_state->start_revision.kind != svn_opt_revision_unspecified
-      && opt_state->end_revision.kind != svn_opt_revision_unspecified
-      && (os->argc - os->ind == 1)
-      && svn_path_is_url (os->argv[os->ind]))
+      && (os->argc - os->ind == 2)
+      && svn_path_is_url (os->argv[os->ind])
+      && svn_path_is_url (os->argv[os->ind + 1]))
     {
-      /* The 'svn diff -rN:M URL' case (matches 'svn merge'). */
+      if (opt_state->start_revision.kind != svn_opt_revision_unspecified
+           || opt_state->end_revision.kind != svn_opt_revision_unspecified)
+        return svn_error_create
+          (SVN_ERR_CLIENT_BAD_REVISION, NULL,
+           _("This form of diff cannot accept a -r argument."));
+      
+      /* The 'svn diff OLD_URL[@OLDREV] NEW_URL[@NEWREV]' case matches. */
       SVN_ERR (svn_opt_args_to_target_array (&targets, os,
                                              opt_state->targets,
                                              &(opt_state->start_revision),
                                              &(opt_state->end_revision),
-                                             FALSE, /* no @revs */ pool));
+                                             TRUE, /* extract @revs */ pool));
 
-      old_target = new_target = APR_ARRAY_IDX(targets, 0, const char *);
+      old_target = APR_ARRAY_IDX (targets, 0, const char *);
+      new_target = APR_ARRAY_IDX (targets, 1, const char *);
       targets->nelts = 0;
+      
+      if (opt_state->start_revision.kind == svn_opt_revision_unspecified)
+        opt_state->start_revision.kind = svn_opt_revision_head;
+      if (opt_state->end_revision.kind == svn_opt_revision_unspecified)
+        opt_state->end_revision.kind = svn_opt_revision_head;
     }
-  else if (! opt_state->old_target && ! opt_state->new_target
-           && (os->argc - os->ind == 2)
-           && svn_path_is_url (os->argv[os->ind])
-           && svn_path_is_url (os->argv[os->ind + 1]))
+  else if (opt_state->old_target && opt_state->new_target)
     {
-      /* The 'svn diff URL1[@N] URL2[@M]' case (matches 'svn merge'). */
+      apr_array_header_t *tmp, *tmp2;
+      
+      if (opt_state->start_revision.kind != svn_opt_revision_unspecified
+           || opt_state->end_revision.kind != svn_opt_revision_unspecified)
+        return svn_error_create
+          (SVN_ERR_CLIENT_BAD_REVISION, NULL,
+           _("This form of diff cannot accept a -r argument."));
+
+      /* The 'svn diff --old=OLD[@OLDREV] --new=NEW[@NEWREV]
+         [PATH...]' case matches. */
+
       SVN_ERR (svn_opt_args_to_target_array (&targets, os,
                                              opt_state->targets,
+                                             NULL,
+                                             NULL,
+                                             FALSE, pool));
+      
+      tmp = apr_array_make (pool, 2, sizeof (const char *));
+      APR_ARRAY_PUSH (tmp, const char *) = (opt_state->old_target);
+      APR_ARRAY_PUSH (tmp, const char *) = (opt_state->new_target);
+
+      SVN_ERR (svn_opt_args_to_target_array (&tmp2, os, tmp,
                                              &(opt_state->start_revision),
                                              &(opt_state->end_revision),
                                              TRUE, /* extract @revs */ pool));
 
-      old_target = APR_ARRAY_IDX(targets, 0, const char *);
-      new_target = APR_ARRAY_IDX(targets, 1, const char *);
-      targets->nelts = 0;
+      old_target = APR_ARRAY_IDX (tmp2, 0, const char *);
+      new_target = APR_ARRAY_IDX (tmp2, 1, const char *);
 
       if (opt_state->start_revision.kind == svn_opt_revision_unspecified)
         opt_state->start_revision.kind = svn_opt_revision_head;
@@ -105,68 +194,109 @@
     }
   else
     {
-      /* The 'svn diff [-rN[:M]] [--old OLD] [--new NEW] [PATH ...]' case */
-      apr_array_header_t *tmp = apr_array_make (pool, 2, sizeof (const char *));
-      apr_array_header_t *tmp2;
+      apr_array_header_t *tmp, *tmp2;
+      svn_boolean_t working_copy_present = FALSE;
+      
+      /* The 'svn diff [-r M[:N]] [TARGET[@REV]...]' case matches. */
 
+      /* Here each target is a pegged object. Find out the starting
+         and ending paths for each target. */
       SVN_ERR (svn_opt_args_to_target_array (&targets, os,
                                              opt_state->targets,
-                                             &(opt_state->start_revision),
-                                             &(opt_state->end_revision),
-                                             FALSE, /* no @revs */ pool));
+                                             NULL,
+                                             NULL,
+                                             FALSE, pool));
 
-      APR_ARRAY_PUSH (tmp, const char *) = (opt_state->old_target
-                                            ? opt_state->old_target : ".");
-      APR_ARRAY_PUSH (tmp, const char *) = (opt_state->new_target
-                                            ? opt_state->new_target
-                                            :  APR_ARRAY_IDX(tmp, 0,
-                                                             const char *));
+      svn_opt_push_implicit_dot_target (targets, pool);
 
+      tmp = apr_array_make (pool, 2, sizeof (const char *));
+      APR_ARRAY_PUSH (tmp, const char *) = ".";
+      APR_ARRAY_PUSH (tmp, const char *) = ".";
+      
       SVN_ERR (svn_opt_args_to_target_array (&tmp2, os, tmp,
                                              &(opt_state->start_revision),
                                              &(opt_state->end_revision),
                                              TRUE, /* extract @revs */ pool));
 
-      old_target = APR_ARRAY_IDX(tmp2, 0, const char *);
-      new_target = APR_ARRAY_IDX(tmp2, 1, const char *);
+      old_target = APR_ARRAY_IDX (tmp2, 0, const char *);
+      new_target = APR_ARRAY_IDX (tmp2, 1, const char *);
 
-      /* Default to HEAD for an URL, BASE otherwise */
-      if (opt_state->start_revision.kind == svn_opt_revision_unspecified)
-        opt_state->start_revision.kind = (svn_path_is_url (old_target)
-                                          ? svn_opt_revision_head
-                                          : svn_opt_revision_base);
+      /* Check to see if at least one of our paths is a working copy
+         path. */
+      for (i = 0; i < targets->nelts; ++i)
+        {
+          const char *path = APR_ARRAY_IDX (targets, i, const char *);
+          if (! svn_path_is_url (path))
+            working_copy_present = TRUE;
+        }
+      
+      if (opt_state->start_revision.kind == svn_opt_revision_unspecified
+          && working_copy_present)
+          opt_state->start_revision.kind = svn_opt_revision_base;
+      if (opt_state->end_revision.kind == svn_opt_revision_unspecified)
+        opt_state->end_revision.kind = working_copy_present
+          ? svn_opt_revision_working : svn_opt_revision_head;
 
-      /* Default to HEAD for an URL, WORKING otherwise */
-      if (opt_state->end_revision.kind == svn_opt_revision_unspecified)
-        opt_state->end_revision.kind = (svn_path_is_url (new_target)
-                                        ? svn_opt_revision_head
-                                        : svn_opt_revision_working);
+      /* Determine if we need to do pegged diffs. */
+      if (opt_state->start_revision.kind != svn_opt_revision_base
+          && opt_state->start_revision.kind != svn_opt_revision_working)
+          pegged_diff = TRUE;
+
     }
 
-  svn_opt_push_implicit_dot_target (targets, pool);
-
   subpool = svn_pool_create (pool);
   for (i = 0; i < targets->nelts; ++i)
     {
-      const char *path = APR_ARRAY_IDX(targets, i, const char *);
+      const char *path = APR_ARRAY_IDX (targets, i, const char *);
       const char *target1, *target2;
 
-      svn_pool_clear (subpool);
-      target1 = svn_path_join (old_target, path, subpool);
-      target2 = svn_path_join (new_target, path, subpool);
+      if (! pegged_diff)
+        {
+          svn_pool_clear (subpool);
+          target1 = svn_path_join (old_target, path, subpool);
+          target2 = svn_path_join (new_target, path, subpool);
+          
+          SVN_ERR (svn_client_diff (options,
+                                    target1,
+                                    &(opt_state->start_revision),
+                                    target2,
+                                    &(opt_state->end_revision),
+                                    opt_state->nonrecursive ? FALSE : TRUE,
+                                    opt_state->notice_ancestry ? FALSE : TRUE,
+                                    opt_state->no_diff_deleted,
+                                    outfile,
+                                    errfile,
+                                    ((svn_cl__cmd_baton_t *)baton)->ctx,
+                                    pool));
+        }
+      else
+        {
+          const char *truepath;
+          svn_opt_revision_t peg_revision;
+          
+          /* First check for a peg revision. */
+          SVN_ERR (parse_path (&peg_revision, &truepath, path, pool));
+          
+          /* Set the default peg revision if one was not specified. */
+          if (peg_revision.kind == svn_opt_revision_unspecified)
+            peg_revision.kind = svn_path_is_url (path)
+              ? svn_opt_revision_head : svn_opt_revision_working;
 
-      SVN_ERR (svn_client_diff (options,
-                                target1,
-                                &(opt_state->start_revision),
-                                target2,
-                                &(opt_state->end_revision),
-                                opt_state->nonrecursive ? FALSE : TRUE,
-                                opt_state->notice_ancestry ? FALSE : TRUE,
-                                opt_state->no_diff_deleted,
-                                outfile,
-                                errfile,
-                                ((svn_cl__cmd_baton_t *)baton)->ctx,
-                                pool));
+          SVN_ERR (svn_client_diff_peg (options,
+                                        path,
+                                        &peg_revision,
+                                        &opt_state->start_revision,
+                                        &opt_state->end_revision,
+                                        opt_state->nonrecursive
+                                        ? FALSE : TRUE,
+                                        opt_state->notice_ancestry
+                                        ? FALSE : TRUE,
+                                        opt_state->no_diff_deleted,
+                                        outfile,
+                                        errfile,
+                                        ((svn_cl__cmd_baton_t *)baton)->ctx,
+                                        pool));
+        }
     }
   svn_pool_destroy (subpool);
 
Index: subversion/clients/cmdline/main.c
===================================================================
--- subversion/clients/cmdline/main.c	(revision 9649)
+++ subversion/clients/cmdline/main.c	(working copy)
@@ -237,32 +237,32 @@
   
   { "diff", svn_cl__diff, {"di"},
     N_("Display the differences between two paths.\n"
-       "usage: 1. diff [-r N[:M]] [--old OLD-TGT] [--new NEW-TGT] [PATH...]\n"
-       "       2. diff -r N:M URL\n"
-       "       3. diff [-r N[:M]] URL1[@N] URL2[@M]\n"
+       "usage: 1. diff [-r N[:M]] [TARGET[@REV]...]\n"
+       "       2. diff --old=OLD-TGT[@OLDREV] --new=NEW-TGT[@NEWREV] [PATH...]\n"
+       "       3. diff OLD-URL[@OLDREV] NEW-URL[@NEWREV]\n"
        "\n"
-       "  1. Display the differences between OLD-TGT and NEW-TGT.  PATHs, if\n"
-       "     given, are relative to OLD-TGT and NEW-TGT and restrict the "
-       "output\n"
-       "     to differences for those paths.  OLD-TGT and NEW-TGT may "
-       "be working\n"
-       "     copy paths or URL[@REV].\n"
+       "  1. Display the changes made to TARGETs as they are seen in REV "
+       "between\n"
+       "     two revisions.  TARGETs may working copy paths or URL[@REV].\n"
        "\n"
-       "     OLD-TGT defaults to the path '.' and NEW-TGT defaults to "
-       "OLD-TGT.\n"
-       "     N defaults to BASE or, if OLD-TGT is an URL, to HEAD.\n"
-       "     M defaults to the current working version or, if NEW-TGT is "
-       "an URL,\n"
-       "     to HEAD.\n"
+       "     N defaults to BASE if any TARGET is a working copy path, otherwise "
+       "it\n"
+       "     must be specified.  M defaults to the current working version if "
+       "any\n"
+       "     TARGET is a working copy path, otherwise it defaults to HEAD.\n"
        "\n"
-       "     '-r N' sets the revision of OLD-TGT to N, '-r N:M' also "
-       "sets the\n"
-       "     revision of NEW-TGT to M.\n"
+       "  2. Display the differences between OLD-TGT as it was seen in OLDREV "
+       "and\n"
+       "     NEW-TGT as it was seen in NEWREV.  PATHs, if given, are relative "
+       "to\n"
+       "     OLD-TGT and NEW-TGT and restrict the output to differences for "
+       "those\n"
+       "     paths.  OLD-TGT and NEW-TGT may be working copy paths or "
+       "URL[@REV].\n"
        "\n"
-       "  2. Shorthand for 'svn diff -r N:M --old=URL --new=URL'.\n"
+       "  3. Shorthand for 'svn diff --old=OLD-URL[@OLDREV] "
+       "--new=NEW-URL[@NEWREV]'\n"
        "\n"
-       "  3. Shorthand for 'svn diff [-r N[:M]] --old=URL1 --new=URL2'\n"
-       "\n"
        "  Use just 'svn diff' to display local modifications in "
        "a working copy.\n"),
     {'r', svn_cl__old_cmd_opt, svn_cl__new_cmd_opt, 'x', 'N',
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Sat May  8 16:59:54 2004