You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@subversion.apache.org by Josh Pieper <jp...@andrew.cmu.edu> on 2004/05/05 20:14:15 UTC

Issue #1093: diff syntax changes for history following

Working with sussman I am trying to finish up issue #1093 - make
subcommands follow copy history.  As it stands now, all subcommands do
this, with the exception of diff, merge, and perhaps log [1].  To make
diff and merge work, UI changes are necessary in order to handle all
the cases of peg revision and operating revision ranges.  This has
been discussed previously in the following threads:

([PATCH] Fix for Issue #1093 (pre-release))
http://www.contactor.se/~dast/svn/archive-2003-10/1405.shtml

(Diff syntax changes for issue #1093)
http://www.contactor.se/~dast/svn/archive-2003-10/1511.shtml

(Issue #1093.)
http://www.contactor.se/~dast/svn/archive-2003-12/0499.shtml

In these threads it seemed as if a consensus was reached as to the UI
for diff, so I will describe it here again using mostly Greg Hudson's
words.  Once we can come to an agreement on the syntax, work can
continue.

  1. diff [-r M[:N]] [TARGET[@REV]...]
  
     Each target may be a path or URL, but it is invalid to specify a
     URL with no -r option. Perform a pegged diff for each target. For
     paths, the default starting rev is BASE, the default ending rev
     is WC, and the peg rev is always WC. For URLs, there is no
     default starting rev (-r option must be present), the default peg
     revision is HEAD.  The default end revision for URLs was not
     decided. [2]

  2. diff --old=OLD-TGT[@OLDREV] --new=NEW-TGT[@NEWREV] [PATH...]

     Compare OLD-TGT and NEW-TGT, restricted to the paths PATH... if
     given.  Can be used to answers questions like "what changes were
     made to libsvn_ra_dav between the 0.32 branch and the trunk?"
     (Limitation: if libsvn_ra_dav was renamed between the 0.32 branch
     and the trunk, the user will have to go find out how; we can't
     answer complex questions like "what was the name of
     /trunk/subversion/libsvn_ra_dav under /branches/0.32?".)

  3. diff OLD-URL[@OLDREV] NEW-URL[@NEWREV]

     Syntactic sugar for 'diff --old=OLD-URL@OLDREV
     --new=NEW-URL@NEWREV'.  Easily distinguishable from the first
     usage because you can't use URLs without a -r option. Note "svn
     diff URL@R1 URL@R2" is rather different from "svn diff -rR1:R2
     URL", because the latter performs a pegged diff of URL@HEAD
     between R1 and R2, while the former looks up URL's pathname
     independently in R1 and R2 and diffs the results.
		               
Is this syntax agreeable to everyone?  Are there any threads or
important points that I've missed?

-Josh
			       

Notes:

[1] - 'svn log' follows copy history, but the peg revision is limited
to be the youngest revision of the operative revision range.

[2] - Greg originally proposed never mixing the -r notation with the
@REV notation in :
http://www.contactor.se/~dast/svn/archive-2003-10/1511.shtml
but then later changed his mind in:
http://www.contactor.se/~dast/svn/archive-2003-10/1520.shtml

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: [PATCH] Issue #1093: make diff follow history

Posted by Greg Hudson <gh...@MIT.EDU>.
On Thu, 2004-05-13 at 10:00, Josh Pieper wrote:
> usage: 1. diff [-r N[:M]] [TARGET[@REV]...]

>   1. Display the changes made to TARGETs as they are seen in REV between
>      two revisions.  TARGETs may working copy paths or URL[@REV].

TARGETs may be working copy paths or URLs.


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: [PATCH] Issue #1093: make diff follow history

Posted by Josh Pieper <jj...@pobox.com>.
Gerco Ballintijn wrote:
> Josh Pieper wrote:
> 
> >Ok, after looking at keeping backwards compatibility as much as
> >possible, I have the following help string for 'svn diff':
> >
> >----
> >diff (di): Display the differences between two paths.
> >usage: 1. diff [-r N[:M]] [TARGET[@REV]...]
> >
> > 1. Display the changes made to TARGETs as they are seen in REV between
> >    two revisions.  TARGETs may working copy paths or URL[@REV].
> >
> >    N defaults to BASE if any TARGET is a working copy path, otherwise it
> >    must be specified.  M defaults to the current working version if any
> >    TARGET is a working copy path, otherwise it defaults to HEAD.
> >
> 
> In your description, it looks like you can specify N, M, and REV at the
> same time. Is this intended? And if so, what would that mean?

Yes this is intended.  It allows you to find how a certain URL changed
between two revisions when it may have been renamed at different
points in time.  The @REV specifies the "peg" revision, or the URL to
start looking from.  Then the history of that path is traced to find
the path's contents at revision N and M, from which the diff is
generated.

-Josh

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: [PATCH] Issue #1093: make diff follow history

Posted by Gerco Ballintijn <Ge...@cwi.nl>.
Josh Pieper wrote:

>Ok, after looking at keeping backwards compatibility as much as
>possible, I have the following help string for 'svn diff':
>
>----
>diff (di): Display the differences between two paths.
>usage: 1. diff [-r N[:M]] [TARGET[@REV]...]
>
>  1. Display the changes made to TARGETs as they are seen in REV between
>     two revisions.  TARGETs may working copy paths or URL[@REV].
>
>     N defaults to BASE if any TARGET is a working copy path, otherwise it
>     must be specified.  M defaults to the current working version if any
>     TARGET is a working copy path, otherwise it defaults to HEAD.
>

In your description, it looks like you can specify N, M, and REV at the
same time. Is this intended? And if so, what would that mean?

Gerco.




---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: [PATCH] Issue #1093: make diff follow history

Posted by Josh Pieper <jj...@pobox.com>.
Ben Collins-Sussman wrote:
> On Thu, 2004-05-13 at 09:00, Josh Pieper wrote:
> 
> > The only syntax compatibility issue that I think this introduces is
> > with 'svn diff -r N:M URL1 URL2'.  Under the old syntax this would
> > result in diffing URL1@N with URL1@M.  Under the new syntax it would
> > be equivalent to 'svn diff -r N:M URL1; svn diff -r N:M URL2'.  Greg
> > Hudson and I talked on IRC about this and we both think that it is an
> > acceptable compromise.  Any other developers have thoughts on the
> > matter?
> 
> I think it's just fine, +1.

It seems like there is a decent consensus, or at least no negative
feedback, so I just went ahead and committed this as r9707.  May your
diffs follow renames from now on.

-Josh

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: [PATCH] Issue #1093: make diff follow history

Posted by Ben Collins-Sussman <su...@collab.net>.
On Thu, 2004-05-13 at 09:00, Josh Pieper wrote:

> The only syntax compatibility issue that I think this introduces is
> with 'svn diff -r N:M URL1 URL2'.  Under the old syntax this would
> result in diffing URL1@N with URL1@M.  Under the new syntax it would
> be equivalent to 'svn diff -r N:M URL1; svn diff -r N:M URL2'.  Greg
> Hudson and I talked on IRC about this and we both think that it is an
> acceptable compromise.  Any other developers have thoughts on the
> matter?

I think it's just fine, +1.




---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: [PATCH] Issue #1093: make diff follow history

Posted by Josh Pieper <jj...@pobox.com>.
Ok, after looking at keeping backwards compatibility as much as
possible, I have the following help string for 'svn diff':

----
diff (di): Display the differences between two paths.
usage: 1. diff [-r N[:M]] [TARGET[@REV]...]
       2. diff [-r N[:M]] --old=OLD-TGT[@OLDREV] [--new=NEW-TGT[@NEWREV]] \
               [PATH...]
       3. diff OLD-URL[@OLDREV] NEW-URL[@NEWREV]

  1. Display the changes made to TARGETs as they are seen in REV between
     two revisions.  TARGETs may working copy paths or URL[@REV].

     N defaults to BASE if any TARGET is a working copy path, otherwise it
     must be specified.  M defaults to the current working version if any
     TARGET is a working copy path, otherwise it defaults to HEAD.

  2. Display the differences between OLD-TGT as it was seen in OLDREV and
     NEW-TGT as it was seen in NEWREV.  PATHs, if given, are relative to
     OLD-TGT and NEW-TGT and restrict the output to differences for those
     paths.  OLD-TGT and NEW-TGT may be working copy paths or URL[@REV].
     NEW-TGT defaults to OLD-TGT if not specified.  -r N makes OLDREV default
     to N, -r N:M makes OLDREV default to N and NEWREV default to M.

  3. Shorthand for 'svn diff --old=OLD-URL[@OLDREV] --new=NEW-URL[@NEWREV]'

  Use just 'svn diff' to display local modifications in a working copy.
----

The only syntax compatibility issue that I think this introduces is
with 'svn diff -r N:M URL1 URL2'.  Under the old syntax this would
result in diffing URL1@N with URL1@M.  Under the new syntax it would
be equivalent to 'svn diff -r N:M URL1; svn diff -r N:M URL2'.  Greg
Hudson and I talked on IRC about this and we both think that it is an
acceptable compromise.  Any other developers have thoughts on the
matter?

Also, regarding UI compatibility concerns, some of the diff tests
check for the exact wording of error messages when files do not exist
in a revision.  Using the new peg-revision diff code,
svn_client__repos_locations gets to create the error message if a file
is non-existant in an old revision.  Its message differs slightly from
the standard path not found message.  Should repos_locations be made
to return the same message, or are multiple textual messages for the
same basic error OK?

Regarding the patch, it is all done and passes all tests locally and
over DAV.  I will post a version for review once these two issues are
resolved.

-Josh

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: [PATCH] Issue #1093: make diff follow history

Posted by Josh Pieper <jj...@pobox.com>.
Josh Pieper wrote:
> kfogel@collab.net wrote:
> > Ben Collins-Sussman and I spent some time this morning going over the
> > both the old and new syntax, and we're +1 on the new.  This proposal
> > solves the peg problem.
> > 
> > But, does this mean we'd lose the old syntaxes?  The old syntaxes are:
> > 
> >    1. diff [-r N[:M]] [--old OLD-TGT] [--new NEW-TGT] [PATH...]
> >    2. diff -r N:M URL
> >    3. diff [-r N[:M]] URL1[@N] URL2[@M]
> > 
> > The new ones you're proposing are
> > 
> >    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]
> > 
> > I'm just thinking about UI compatability and release scheduling.  Can
> > we release a diff syntax change in 1.1?  That seems a bit much, unless
> > the change *only* adds new syntaxes, doesn't take away any of the
> > existing ones.
> 
> The only incompatible change the new syntax has is removing support
> for the -r option in case 2 and 3.  It would be no extra
> implementation work to accept those options with their pre1.1
> behavior.  As far as I could tell, they were removed to prevent
> confusion, since only case 1 really is guaranteed to diff the same
> object at two different revisions.  Perhaps we could mark them as
> deprecated?

I believe I was mistaken in my first look at the compatibility issue.
If -r options were accepted for all three newly proposed formats for
diff, then there would be no way to distinguish case 3 from case 1.
What to do about it, I am not sure.

-Josh

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: [PATCH] Issue #1093: make diff follow history

Posted by Greg Hudson <gh...@MIT.EDU>.
On Tue, 2004-05-11 at 22:20, Josh Pieper wrote:
> I think the problem is definitely related to ancestry.  The editor
> driven by the ra function do_update does seem to ignore ancestroy.
> From my investigation, the editor for updates seems to be driven by
> the reporter code, not by svn_repos_dir_delta.  The reporter code
> doesn't seem to pay attention to ancestry AFAICT.

svn_repos_dir_delta and the reporter code both have a flag which
controls whether they ignore ancestry.


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: [PATCH] Issue #1093: make diff follow history

Posted by Josh Pieper <jj...@pobox.com>.
Ben Collins-Sussman wrote:
> Josh Pieper wrote:
> >Yes, but that is only half the problem.  If the file itself is
> >renamed, the update driver always issues a complete removal of the old
> >file, which causes the diff output to be less than useful.
> 
> Hold on:  svn_repos_dir_delta() is the ultimate driver of all editors, 
> if you trace back far enough in any stack.  My memory tells me that it 
> has a mode which tells it to "be stupid", i.e. ignore ancestry.  For 
> example, when you pass the --ignore-ancestry switch to 'svn merge', it 
> will never delete and re-add something;  it just does ignorant 
> path-based comparisons.
> 
> Or is the problem completely unrelated to ancestry?

I think the problem is definitely related to ancestry.  The editor
driven by the ra function do_update does seem to ignore ancestroy.

Re: [PATCH] Issue #1093: make diff follow history

Posted by Ben Collins-Sussman <su...@collab.net>.
Josh Pieper wrote:

> kfogel@collab.net wrote:
> 
>>>The patch could be applied nearly as is with no loss in functionality,
>>>but you won't get much benefit since diffs over renames still won't be
>>>what you expect.  If that approach sounds good, I will clean up the
>>>work so far and post it again for review.  Otherwise we can wait until
>>>the update driver issue is figured out.
>>
>>Can we anchor the update editor higher up, maybe, like at root?
>>
>>(Thinking out loud, not able to do in-depth analysis atm...)
> 
> 
> Yes, but that is only half the problem.  If the file itself is
> renamed, the update driver always issues a complete removal of the old
> file, which causes the diff output to be less than useful.

Hold on:  svn_repos_dir_delta() is the ultimate driver of all editors, 
if you trace back far enough in any stack.  My memory tells me that it 
has a mode which tells it to "be stupid", i.e. ignore ancestry.  For 
example, when you pass the --ignore-ancestry switch to 'svn merge', it 
will never delete and re-add something;  it just does ignorant 
path-based comparisons.

Or is the problem completely unrelated to ancestry?


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: [PATCH] Issue #1093: make diff follow history

Posted by Josh Pieper <jj...@pobox.com>.
kfogel@collab.net wrote:
> > The patch could be applied nearly as is with no loss in functionality,
> > but you won't get much benefit since diffs over renames still won't be
> > what you expect.  If that approach sounds good, I will clean up the
> > work so far and post it again for review.  Otherwise we can wait until
> > the update driver issue is figured out.
> 
> Can we anchor the update editor higher up, maybe, like at root?
> 
> (Thinking out loud, not able to do in-depth analysis atm...)

Yes, but that is only half the problem.  If the file itself is
renamed, the update driver always issues a complete removal of the old
file, which causes the diff output to be less than useful.

-Josh

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: [PATCH] Issue #1093: make diff follow history

Posted by kf...@collab.net.
Josh Pieper <jj...@pobox.com> writes:
> The only incompatible change the new syntax has is removing support
> for the -r option in case 2 and 3.  It would be no extra
> implementation work to accept those options with their pre1.1
> behavior.  As far as I could tell, they were removed to prevent
> confusion, since only case 1 really is guaranteed to diff the same
> object at two different revisions.  Perhaps we could mark them as
> deprecated?

I think that would be good.  (In this case, "mark" as deprecated means
document them that way in the book, and add a note to issue #1783.)

> outside of my computer.  The "update driver" problem is what is
> holding me up now, as reproduced in current HEAD in this thread:
> 
> http://www.contactor.se/~dast/svn/archive-2004-05/0389.shtml
> 
> The patch could be applied nearly as is with no loss in functionality,
> but you won't get much benefit since diffs over renames still won't be
> what you expect.  If that approach sounds good, I will clean up the
> work so far and post it again for review.  Otherwise we can wait until
> the update driver issue is figured out.

Can we anchor the update editor higher up, maybe, like at root?

(Thinking out loud, not able to do in-depth analysis atm...)

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: [PATCH] Issue #1093: make diff follow history

Posted by Josh Pieper <jj...@pobox.com>.
kfogel@collab.net wrote:
> Ben Collins-Sussman and I spent some time this morning going over the
> both the old and new syntax, and we're +1 on the new.  This proposal
> solves the peg problem.
> 
> But, does this mean we'd lose the old syntaxes?  The old syntaxes are:
> 
>    1. diff [-r N[:M]] [--old OLD-TGT] [--new NEW-TGT] [PATH...]
>    2. diff -r N:M URL
>    3. diff [-r N[:M]] URL1[@N] URL2[@M]
> 
> The new ones you're proposing are
> 
>    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]
> 
> I'm just thinking about UI compatability and release scheduling.  Can
> we release a diff syntax change in 1.1?  That seems a bit much, unless
> the change *only* adds new syntaxes, doesn't take away any of the
> existing ones.

The only incompatible change the new syntax has is removing support
for the -r option in case 2 and 3.  It would be no extra
implementation work to accept those options with their pre1.1
behavior.  As far as I could tell, they were removed to prevent
confusion, since only case 1 really is guaranteed to diff the same
object at two different revisions.  Perhaps we could mark them as
deprecated?

> > 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.
> 
> You probably mean the "update driver", right?  The update editor is
> not used for diff, but one of two diff editors is.
> 
> (My apologies for not being able to review the patch itself, just got
> meetings and some other stuff going on here lately.)

No worries, the patch itself isn't that important.  As I was telling
Ben on IRC I mostly posted it so there would be some record of my work
outside of my computer.  The "update driver" problem is what is
holding me up now, as reproduced in current HEAD in this thread:

http://www.contactor.se/~dast/svn/archive-2004-05/0389.shtml

The patch could be applied nearly as is with no loss in functionality,
but you won't get much benefit since diffs over renames still won't be
what you expect.  If that approach sounds good, I will clean up the
work so far and post it again for review.  Otherwise we can wait until
the update driver issue is figured out.

-Josh

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: [PATCH] Issue #1093: make diff follow history

Posted by kf...@collab.net.
Josh Pieper <jj...@pobox.com> writes:
> 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.

Ben Collins-Sussman and I spent some time this morning going over the
both the old and new syntax, and we're +1 on the new.  This proposal
solves the peg problem.

But, does this mean we'd lose the old syntaxes?  The old syntaxes are:

   1. diff [-r N[:M]] [--old OLD-TGT] [--new NEW-TGT] [PATH...]
   2. diff -r N:M URL
   3. diff [-r N[:M]] URL1[@N] URL2[@M]

The new ones you're proposing are

   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]

I'm just thinking about UI compatability and release scheduling.  Can
we release a diff syntax change in 1.1?  That seems a bit much, unless
the change *only* adds new syntaxes, doesn't take away any of the
existing ones.

> 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.

You probably mean the "update driver", right?  The update editor is
not used for diff, but one of two diff editors is.

(My apologies for not being able to review the patch itself, just got
meetings and some other stuff going on here lately.)

-K

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

[PATCH] Issue #1093: make diff follow history

Posted by Josh Pieper <jj...@pobox.com>.
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