[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

Lame mailing list software (was: svn commit: r36404 ...)

From: Greg Stein <gstein_at_gmail.com>
Date: Sat, 7 Mar 2009 19:55:12 -0800 (PST)

For efficiency reasons, the system has converted the large body of this message into an attachment.

attached mail follows:


Jack,

Months ago, when the mailing list software was "upgraded" into this
broken state, you said there were people working on it with the
highest priority and non-stop. What is the status of that work? When
are we going to get a mailing list that doesn't turn large emails into
attachments? (you know, like *every other* piece of mailing list
software on the planet)

The commit below has been made unreviewable by the software on tigris.org.

I have chosen Gmail as my mail client. For very large emails, it clips
the message and then offers a link if you'd like to see the full
message. However, since the email below is a *short* email, but with a
very large inline attachment, I *do not* get that choice. Instead, it
is simply clipped.

How am I supposed to do my work here? Why should we continue to stick
with mailing list software that seems to break us every few weeks?
(witness the loss of mail headers last weekend, after an unannounced
downtime and "upgrade"; or the half-day "scheduled maintenance" a
month ago?)

"Choose another mail client" is not a very good answer, I'd think,
since no other piece of mailing list software has ever made me do
that. Nor do I see it as a good option for our project to dictate my
choices of a mail client. There are other options for our mailing list
that do not cause this kind of pain, this amount of downtime, or this
lack of reliability.

Seriously. Answers? Help?

-g

---------- Forwarded message ----------
From: Arfrever Frehtes Taifersar Arahesis <Arfrever.FTA_at_gmail.com>
Date: Sun, Mar 8, 2009 at 00:42
Subject: svn commit: r36404 - in trunk: . notes subversion/include
subversion/libsvn_client subversion/libsvn_subr subversion/libsvn_wc
subversion/svn subversion/tests/cmdline
subversion/tests/cmdline/getopt_tests_data
subversion/tests/cmdline/svntest
To: svn_at_subversion.tigris.org

For efficiency reasons, the system has converted the large body of
this message into an attachment.

---------- Forwarded message ----------
From: arfrever_at_tigris.org
To: svn_at_subversion.tigris.org
Date: Sat, 7 Mar 2009 15:41:43 -0800
Subject: svn commit: r36404 - in trunk: . notes subversion/include
subversion/libsvn_client subversion/libsvn_subr subversion/libsvn_wc
subversion/svn subversion/tests/cmdline
subversion/tests/cmdline/getopt_tests_data
subversion/tests/cmdline/svntest
Author: arfrever
Date: Sat Mar  7 15:41:42 2009
New Revision: 36404

Log:
Merge the 'svnpatch-diff' branch to trunk.

Added:
  trunk/notes/svnpatch
     - copied unchanged from r36403, branches/svnpatch-diff/notes/svnpatch
  trunk/subversion/libsvn_client/patch.c
     - copied unchanged from r36403,
branches/svnpatch-diff/subversion/libsvn_client/patch.c
  trunk/subversion/libsvn_wc/patch.c
     - copied unchanged from r36403,
branches/svnpatch-diff/subversion/libsvn_wc/patch.c
  trunk/subversion/svn/patch-cmd.c
     - copied unchanged from r36403,
branches/svnpatch-diff/subversion/svn/patch-cmd.c
  trunk/subversion/tests/cmdline/patch_tests.py
     - copied unchanged from r36403,
branches/svnpatch-diff/subversion/tests/cmdline/patch_tests.py
Modified:
  trunk/   (props changed)
  trunk/build.conf
  trunk/subversion/include/svn_client.h
  trunk/subversion/include/svn_config.h
  trunk/subversion/include/svn_error_codes.h
  trunk/subversion/include/svn_wc.h
  trunk/subversion/libsvn_client/client.h
  trunk/subversion/libsvn_client/deprecated.c
  trunk/subversion/libsvn_client/diff.c
  trunk/subversion/libsvn_client/merge.c
  trunk/subversion/libsvn_client/repos_diff.c
  trunk/subversion/libsvn_subr/config_file.c
  trunk/subversion/libsvn_wc/copy.c
  trunk/subversion/libsvn_wc/deprecated.c
  trunk/subversion/libsvn_wc/diff.c
  trunk/subversion/libsvn_wc/util.c
  trunk/subversion/svn/cl.h
  trunk/subversion/svn/diff-cmd.c
  trunk/subversion/svn/main.c
  trunk/subversion/svn/notify.c
  trunk/subversion/tests/cmdline/diff_tests.py
  trunk/subversion/tests/cmdline/getopt_tests_data/svn--help_stdout
  trunk/subversion/tests/cmdline/getopt_tests_data/svn_help_stdout
  trunk/subversion/tests/cmdline/svntest/actions.py

Merged:
  /branches/svnpatch-diff:r25664-31830,31832-31911,31913-36403

Modified: trunk/build.conf
URL: http://svn.collab.net/viewvc/svn/trunk/build.conf?pathrev=36404&r1=36403&r2=36404
==============================================================================
--- trunk/build.conf    Sat Mar  7 14:54:27 2009        (r36403)
+++ trunk/build.conf    Sat Mar  7 15:41:42 2009        (r36404)
@@ -98,6 +98,7 @@ test-scripts =
        subversion/tests/cmdline/svnsync_tests.py
        subversion/tests/cmdline/authz_tests.py
        subversion/tests/cmdline/depth_tests.py
+        subversion/tests/cmdline/patch_tests.py
        subversion/tests/cmdline/svndumpfilter_tests.py
        subversion/tests/cmdline/changelist_tests.py
        subversion/tests/cmdline/info_tests.py

Copied: trunk/notes/svnpatch (from r36403,
branches/svnpatch-diff/notes/svnpatch)
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ trunk/notes/svnpatch        Sat Mar  7 15:41:42 2009
(r36404, copy of r36403, branches/svnpatch-diff/notes/svnpatch)
@@ -0,0 +1,203 @@
+This file documents the 'svnpatch' format that's used with both diff and patch
+subcommands.
+
+I HISTORY
+  -------
+
+Subversion's diff facility by default generates an unidiff format output.  The
+unidiff format is famous with tools like diff(1) and has been used for decades
+to produce contextual differences between files.  We also often associate it
+with patch(1) to apply those contextual differences.  When it comes to
+non-contextual changes like moving a directory, adding a property to a file, or
+modifying an image, unidiff is helpless.  Enters the svnpatch format.  It
+enables capturing all non-contextual changes into a WC-portable output so that
+it is possible to create rich patches to apply across working copies.  Another
+way to look at it is as an "offline merge", in which one dumps the diffs,
+passes them as a patch on to her peer who then applies it, without ever
+interacting with the repository.
+
+The svnpatch format is in fact a simplified version of the Subversion protocol
+-- see subversion/libsvn_ra_svn/protocol -- that meets our needs.  The
+advantage here is that changes are serialized into a language that Subversion
+already speaks and only a few minor tweaks were needed to accommodate.  As an
+example, revisions have been stripped from the protocol to allow fuzzing.
+
+The implementation with the command line client uses `svn diff --svnpatch' to
+generate the rich diffs and `svn patch' to apply the diffs against a working
+copy.  Other frontends can also take advantage of svnpatch in the same way
+through the usual API's svn_client_diff5 and svn_client_patch that use files to
+communicate.
+
+
+II SVNPATCH FORMAT IN A NUTSHELL
+   -----------------------------
+
+First off, let's define it.  svnpatch format is made of two ordered parts:
+  * (a) human-readable: made of unidiff bytes
+  * (b) computer-readable: made of svn protocol bytes (ra_svn), gzip'ed,
+        base64-encoded
+
+But, as we're not in a client/server configuration:
+  - (b) only uses the svn protocol's Editor Command Set, there's no need for
+    the Main Command Set nor the Report Command Set
+  - a client reads Editor Commands from the patch, i.e. the patch silently
+    drives the client's editor
+  - the only direction the information takes is from the patch to the client
+  - svndiff1 is solely used instead of being able to choose between svndiff1
+    and svndiff0 (e.g. binary-change needs svndiff)
+
+Such a format can be seen as a subset of the svn protocol which:
+  - Capabilities and Edit Pipelining have nothing to do with as we can't adjust
+    once the patch is rock-hard written in the file nor negotiate anything
+  - commands are restricted to the Editor Command Set
+  - lacks revision numbers and checksums except for binary files (see VI
+    FUZZING)
+
+For more about Command Sets, consult libsvn_ra_svn/protocol.
+
+
+III BOUNDARIES BETWEEN THE TWO PARTS
+    --------------------------------
+
+Now since the svn protocol would be happy to handle just any change that a
+working copy comes with, rules have to be set up so that we meet our goals (see
+I HISTORY).
+
+Concretely, what's in each part?
+
+In (a):
+ - contextual differences
+ - property-changes (in a similar way to 'svn diff')
+ - new non-binary-file content
+
+In (b):
+ - tree-changes ({add,del,move,copy}-directory, {add,del,move,copy}-file)
+ - property-changes
+ - binary-changes
+
+Consequences are we face cases where one change's representation lives in the
+two parts of the patch. e.g. a modified-file move: the move is represented
+within (b) while contextual differences within (a);  a file add: an add-file
+Editor Command in (b) plus its content in (a).
+
+Furthermore, we never end up with redundant information but with
+property-changes.  A file copy with modifications generates (a) contextual
+diff, (b) add-file w/ copy-path.
+
+The only thing that's left unreadable is tree-changes as defined above.
+However, a higher level layer (e.g. GUIs) would perfectly be able to
+base64-decode, uncompress and read operations to visually-render the changes.
+
+The (b) block starts with a header and its version.
+
+Here's what a directory add, a file add and a propset would look like:
+
+[[[
+Index: bar
+===================================================================
+--- bar
++++ bar
+@@ -0,0 +1,2 @@
++This is bar content.
++
+
+Property changes on: bar
+___________________________________________________________________
+Name: newprop
+   + propval
+
+======================== SVNPATCH BLOCK 1 =========================
+H4sICOz0mEYAA291dABtjsEKwyAMhu97Co/tQejcoZC3cU26CWLElu31l0ZXulE8GL//8yed4UzJ
+FubVdHJ64wAHuXp5eEQ7h0gy3uDuS80cTFc1qzQ9fXqQejYXzoLUGCHRu4ERtuHl4/5rq8ZQtHlm
+/jajOzZHXqhZGp3i4Qe3df931IwwrBVePlyTX//3AAAA
+]]]
+
+Let's uncompress and decode the above base64 block (lines are wrapped):
+
+( open-root ( ( ) 2:d0 ) ) ( add-file ( 3:bar 2:d0 2:c1 ( ) ) ) (
+change-file-prop ( 2:c1 7:newprop ( 7:propval ) ) ) ( add-dir ( 3:foo 2:d0 2:d2
+( ) ) ) ( close-dir ( 2:d2 ) ) ( close-dir ( 2:d0 ) ) ( close-file ( 2:c1 ( ) )
+) ( close-edit ( ) )
+
+Further examples can be found in subversion/tests/cmdline/diff_tests.py
+test-suite.
+
+
+IV SVNPATCH EDIT-ABILITY
+   ---------------------
+
+Because encoded and compressed, the computer-readable chunk (b) is not directly
+editable.  Should it be in cleartext, the user would still have to go through
+svn protocol writing manually -- calculate checksums and strings length, and
+place tokens, assumed to be not so friendly for the end-user.  However, there's
+a much easier workaround: apply the patch, and then start editing the working
+copy with regular svn subcommands.
+
+
+V PATCHING
+  --------
+
+When it comes to applying an svnpatch patch (RAS syndrom), the 'svn patch'
+subcommand is a good friend.  We don't currently support applying (a) Unidiffs
+internally.  We instead call an external program to do the job for us which the
+user may specify with the 'patch-cmd' entry in the run-time
configuration.  'svn
+patch' also has a --patch-cmd flag that overrides this run-time config.  When
+none of those flags are specified, the ultimate fallback is to try to call
+'patch'.  Since GNU patch is widely available on many UNIX-like platforms, this
+workaround should cover a fairly large amount of users.  No decision nor
+consensus have been taken towards this issue yet.
+On the other hand, (b) is handled with internal routines that read and drive
+editor functions out from the patch file much like what's being performed by
+libsvn_ra_svn with a network stream.
+
+Put differently, for now 'svn patch' will gladly apply any
(full-featured-)patch
+on GNU-patch-enabled environments while it will bail out with a message on a
+GNU-patch-less one.  In the latter, the user is left the burden to apply the
+Unidiff with his own tools.
+
+Now some words about the order to process (a) and (b).  There might be cases
+when operations to a single file live in the two parts of the patch
(see above).
+Since Unidiff indexes are made against the most up-to-date file name, it makes
+sense that 'svn patch' first deals with the svnpatch block and then the Unidiff
+block.  E.g. consider a WC with a file copy from foo to bar and then contextual
+modifications to bar.  The patch that represents this WC changes would show
+diffs against 'bar' file.  So 'svn patch' first has to
schedule-add-with-history
+bar from foo and then apply contextual diffs, which would not work
the other way
+around.
+
+When the Editor Command Set comes to be extended, 'svn patch' will face
+unexpected commands and/or syntax.  As in libsvn_ra_svn, we warn the user with
+'unsupported command' messages and ignore its application.
+
+
+VI FUZZING a.k.a. DYSTOPIA
+   -----------------------
+
+The svn protocol is not very sensitive to fuzzing since most operations include
+a revision number.  However, to stick with this policy would widely lower the
+patch-application scope we're expecting.  For instance, 'svn patch' would fail
+at deleting dir_at_REV when REV is different from the one that comes with the
+delete-entry Editor Command.  Obviously we need loose here, and the solution is
+to free the svn protocol from revision numbers and checksums in our
+implementation for every change but binary-changes (for the checksums).  (It
+would be insane to associate binary stuff with fuzzing in this world.)  Now
+dealing with (b) patching is similar in many ways to GNU Patch's: we end up
+trying by all methods to drive the editor in the dark jungle, possibly failing
+in few cases shooting 'hunk failed' warnings.
+
+
+VII PATCH AND MERGE IN SUBVERSION
+    -----------------------------
+
+'svn patch' is similar in many ways to 'svn merge'.  Basically, we have a
+tree-delta in hand that we want to apply to a working-tree.  Thus it's not
+surprising to see they have a lot in common when comparing both
implementations.
+'patch' uses a mix of revamped merge_callbacks (see libsvn_client/merge.c) and
+repos-repos editor functions (see libsvn_client/repos_diff.c).  Why not merge
+those two together then, for code-share sake?  Well, although they
share a close
+logic, to join the two implies having one single file (repos_diff.c) to handle
+at least three burdens:  repos-repos diff, merge, and patch.  Such a design
+can't be achieved without a myriad of tests/conditions and a large amount of
+blurry mess at mixing three different tools in one place.  In the end, what was
+supposed to enhance software maintainability turned out to cause a
lot of damage
+at tightening different things together.

Modified: trunk/subversion/include/svn_client.h
URL: http://svn.collab.net/viewvc/svn/trunk/subversion/include/svn_client.h?pathrev=36404&r1=36403&r2=36404
==============================================================================
--- trunk/subversion/include/svn_client.h       Sat Mar  7 14:54:27
2009        (r36403)
+++ trunk/subversion/include/svn_client.h       Sat Mar  7 15:41:42
2009        (r36404)
@@ -1,7 +1,7 @@
 /**
 * @copyright
 * ====================================================================
- * Copyright (c) 2000-2008 CollabNet.  All rights reserved.
+ * Copyright (c) 2000-2009 CollabNet.  All rights reserved.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution.  The terms
@@ -2291,11 +2291,41 @@ svn_client_blame(const char *path_or_url
 * @note @a header_encoding doesn't affect headers generated by external
 * diff programs.
 *
+ * @a svnpatch_format set to @c TRUE enables the svnpatch format diff.
+ *
 * @note @a relative_to_dir doesn't affect the path index generated by
 * external diff programs.
 *
+ * @since New in 1.7.
+ */
+svn_error_t *
+svn_client_diff5(const apr_array_header_t *diff_options,
+                 const char *path1,
+                 const svn_opt_revision_t *revision1,
+                 const char *path2,
+                 const svn_opt_revision_t *revision2,
+                 const char *relative_to_dir,
+                 svn_depth_t depth,
+                 svn_boolean_t ignore_ancestry,
+                 svn_boolean_t no_diff_deleted,
+                 svn_boolean_t ignore_content_type,
+                 svn_boolean_t svnpatch_format,
+                 const char *header_encoding,
+                 apr_file_t *outfile,
+                 apr_file_t *errfile,
+                 const apr_array_header_t *changelists,
+                 svn_client_ctx_t *ctx,
+                 apr_pool_t *pool);
+
+
+/**
+ * Similar to svn_client_diff5(), but with @a svnpatch_format set to FALSE.
+ *
+ * @deprecated Provided for backward compatibility with the 1.6 API.
+ *
 * @since New in 1.5.
 */
+SVN_DEPRECATED
 svn_error_t *
 svn_client_diff4(const apr_array_header_t *diff_options,
                 const char *path1,
@@ -2395,13 +2425,42 @@ svn_client_diff(const apr_array_header_t
 * be either a working-copy path or URL.
 *
 * If @a peg_revision is @c svn_opt_revision_unspecified, behave
- * identically to svn_client_diff4(), using @a path for both of that
+ * identically to svn_client_diff5(), using @a path for both of that
 * function's @a path1 and @a path2 argments.
 *
- * All other options are handled identically to svn_client_diff4().
+ * All other options are handled identically to svn_client_diff5().
+ *
+ * @since New in 1.7.
+ */
+svn_error_t *
+svn_client_diff_peg5(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,
+                     const char *relative_to_dir,
+                     svn_depth_t depth,
+                     svn_boolean_t ignore_ancestry,
+                     svn_boolean_t no_diff_deleted,
+                     svn_boolean_t ignore_content_type,
+                     svn_boolean_t svnpatch_format,
+                     const char *header_encoding,
+                     apr_file_t *outfile,
+                     apr_file_t *errfile,
+                     const apr_array_header_t *changelists,
+                     svn_client_ctx_t *ctx,
+                     apr_pool_t *pool);
+
+
+/**
+ * Similar to svn_client_diff_peg5(), but with @a svnpatch_format set to
+ * FALSE.
+ *
+ * @deprecated Provided for backward compatibility with the 1.6 API.
 *
 * @since New in 1.5.
 */
+SVN_DEPRECATED
 svn_error_t *
 svn_client_diff_peg4(const apr_array_header_t *diff_options,
                     const char *path,
@@ -2505,7 +2564,7 @@ svn_client_diff_peg(const apr_array_head
 * Calls @a summarize_func with @a summarize_baton for each difference
 * with a @c svn_client_diff_summarize_t structure describing the difference.
 *
- * See svn_client_diff4() for a description of the other parameters.
+ * See svn_client_diff5() for a description of the other parameters.
 *
 * @since New in 1.5.
 */
@@ -2561,7 +2620,7 @@ svn_client_diff_summarize(const char *pa
 * Call @a summarize_func with @a summarize_baton for each difference
 * with a @c svn_client_diff_summarize_t structure describing the difference.
 *
- * See svn_client_diff_peg4() for a description of the other parameters.
+ * See svn_client_diff_peg5() for a description of the other parameters.
 *
 * @since New in 1.5.
 */
@@ -4585,6 +4644,41 @@ svn_client_info(const char *path_or_url,

 /** @} */

+
+/**
+ * @defgroup Patch
+ *
+ * @{
+ */
+
+/**
+ * Apply a patch that's located at @a patch_path against a working copy
+ * pointed to by @a wc_path.
+ *
+ * The patch might carry Unified diffs, svnpatch diffs, or both.
+ * However, 'svn patch' doesn't yet support Unidiff application
+ * internally: we rather delegate this task to an external program.
+ * See svn_wc_apply_unidiff() or notes/svnpatch.
+ * Note: hopefuly this is temporary and we'll have our own
+ * implementation one day to cut off the messy dependency.
+ *
+ * If @a force is not set and the patch involves deleting locally modified or
+ * unversioned items the operation will fail.  If @a force is set such items
+ * will be deleted.
+ *
+ * @since New in 1.7.
+ */
+svn_error_t *
+svn_client_patch(const char *patch_path,
+                 const char *wc_path,
+                 svn_boolean_t force,
+                 apr_file_t *outfile,
+                 apr_file_t *errfile,
+                 svn_client_ctx_t *ctx,
+                 apr_pool_t *pool);
+
+/** @} */
+
 /** @} end group: Client working copy management */

 /**

Modified: trunk/subversion/include/svn_config.h
URL: http://svn.collab.net/viewvc/svn/trunk/subversion/include/svn_config.h?pathrev=36404&r1=36403&r2=36404
==============================================================================
--- trunk/subversion/include/svn_config.h       Sat Mar  7 14:54:27
2009        (r36403)
+++ trunk/subversion/include/svn_config.h       Sat Mar  7 15:41:42
2009        (r36404)
@@ -95,6 +95,7 @@ typedef struct svn_config_t svn_config_t
 #define SVN_CONFIG_OPTION_DIFF3_CMD                 "diff3-cmd"
 #define SVN_CONFIG_OPTION_DIFF3_HAS_PROGRAM_ARG     "diff3-has-program-arg"
 #define SVN_CONFIG_OPTION_MERGE_TOOL_CMD            "merge-tool-cmd"
+#define SVN_CONFIG_OPTION_PATCH_CMD                 "patch-cmd"
 #define SVN_CONFIG_SECTION_MISCELLANY           "miscellany"
 #define SVN_CONFIG_OPTION_GLOBAL_IGNORES            "global-ignores"
 #define SVN_CONFIG_OPTION_LOG_ENCODING              "log-encoding"

Modified: trunk/subversion/include/svn_error_codes.h
URL: http://svn.collab.net/viewvc/svn/trunk/subversion/include/svn_error_codes.h?pathrev=36404&r1=36403&r2=36404
==============================================================================
--- trunk/subversion/include/svn_error_codes.h  Sat Mar  7 14:54:27
2009        (r36403)
+++ trunk/subversion/include/svn_error_codes.h  Sat Mar  7 15:41:42
2009        (r36404)
@@ -1253,6 +1253,11 @@ SVN_ERROR_START
             SVN_ERR_MISC_CATEGORY_START + 32,
             "Unsupported schema found in SQLite db")

+  /** @since New in 1.7. */
+  SVN_ERRDEF(SVN_ERR_EXTERNAL_PROGRAM_MISSING,
+             SVN_ERR_MISC_CATEGORY_START + 33,
+             "External program is missing")
+
  /* command-line client errors */

  SVN_ERRDEF(SVN_ERR_CL_ARG_PARSING_ERROR,

Modified: trunk/subversion/include/svn_wc.h
URL: http://svn.collab.net/viewvc/svn/trunk/subversion/include/svn_wc.h?pathrev=36404&r1=36403&r2=36404
==============================================================================
--- trunk/subversion/include/svn_wc.h   Sat Mar  7 14:54:27 2009        (r36403)
+++ trunk/subversion/include/svn_wc.h   Sat Mar  7 15:41:42 2009        (r36404)
@@ -1,7 +1,7 @@
 /**
 * @copyright
 * ====================================================================
- * Copyright (c) 2000-2008 CollabNet.  All rights reserved.
+ * Copyright (c) 2000-2009 CollabNet.  All rights reserved.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution.  The terms
@@ -55,6 +55,7 @@
 #include "svn_delta.h"     /* for svn_stream_t */
 #include "svn_opt.h"
 #include "svn_ra.h"        /* for svn_ra_reporter_t type */
+#include "svn_ra_svn.h"    /* for svn_ra_svn_item_t type */
 #include "svn_version.h"

 #ifdef __cplusplus
@@ -896,7 +897,10 @@ typedef enum svn_wc_notify_state_t
  svn_wc_notify_state_merged,

  /** Modified state got conflicting mods. */
-  svn_wc_notify_state_conflicted
+  svn_wc_notify_state_conflicted,
+
+  /** The source to copy the file from is missing. */
+  svn_wc_notify_state_source_missing

 } svn_wc_notify_state_t;

@@ -1585,9 +1589,9 @@ typedef svn_error_t *(*svn_wc_conflict_r

 /**
- * A callback vtable invoked by our diff-editors, as they receive
- * diffs from the server.  'svn diff' and 'svn merge' both implement
- * their own versions of this table.
+ * A callback vtable invoked by our diff-editors, as they receive diffs
+ * from the server.  'svn diff', 'svn merge' and 'svn patch' all
+ * implement their own versions of this vtable.
 *
 * Common parameters:
 *
@@ -1608,9 +1612,9 @@ typedef svn_error_t *(*svn_wc_conflict_r
 * state, this is only useful with merge, not diff; diff callbacks
 * should set this to false.)
 *
- * @since New in 1.6.
+ * @since New in 1.7.
 */
-typedef struct svn_wc_diff_callbacks3_t
+typedef struct svn_wc_diff_callbacks4_t
 {
  /**
   * A file @a path has changed.  If @a tmpfile2 is non-NULL, the
@@ -1659,6 +1663,9 @@ typedef struct svn_wc_diff_callbacks3_t
   * any elements, the original list of properties is provided in
   * @a originalprops, which is a hash of @c svn_string_t values, keyed on the
   * property name.
+   * If @a copyfrom_path is non-_at_c NULL, this add has history (i.e., is a
+   * copy), and the origin of the copy may be recorded as
+   * @a copyfrom_path under @a copyfrom_revision.
   */
  svn_error_t *(*file_added)(svn_wc_adm_access_t *adm_access,
                             svn_wc_notify_state_t *contentstate,
@@ -1671,6 +1678,8 @@ typedef struct svn_wc_diff_callbacks3_t
                             svn_revnum_t rev2,
                             const char *mimetype1,
                             const char *mimetype2,
+                             const char *copyfrom_path,
+                             svn_revnum_t copyfrom_revision,
                             const apr_array_header_t *propchanges,
                             apr_hash_t *originalprops,
                             void *diff_baton);
@@ -1700,12 +1709,18 @@ typedef struct svn_wc_diff_callbacks3_t
  /**
   * A directory @a path was added.  @a rev is the revision that the
   * directory came from.
+   *
+   * If @a copyfrom_path is non-_at_c NULL, this add has history (i.e., is a
+   * copy), and the origin of the copy may be recorded as
+   * @a copyfrom_path under @a copyfrom_revision.
   */
  svn_error_t *(*dir_added)(svn_wc_adm_access_t *adm_access,
                            svn_wc_notify_state_t *state,
                            svn_boolean_t *tree_conflicted,
                            const char *path,
                            svn_revnum_t rev,
+                            const char *copyfrom_path,
+                            svn_revnum_t copyfrom_revision,
                            void *diff_baton);

  /**
@@ -1758,11 +1773,109 @@ typedef struct svn_wc_diff_callbacks3_t
                             const char *path,
                             void *diff_baton);

+} svn_wc_diff_callbacks4_t;
+
+
+/**
+ * Similar to @c svn_wc_diff_callbacks4_t, but without @a copyfrom_path and
+ * @a copyfrom_revision arguments to @c file_added and @c dir_added functions.
+ *
+ * @since New in 1.6.
+ * @deprecated Provided for backward compatibility with the 1.6 API.
+ */
+typedef struct svn_wc_diff_callbacks3_t
+{
+  /** The same as @c file_changed in @c svn_wc_diff_callbacks4_t. */
+  svn_error_t *(*file_changed)(svn_wc_adm_access_t *adm_access,
+                               svn_wc_notify_state_t *contentstate,
+                               svn_wc_notify_state_t *propstate,
+                               svn_boolean_t *tree_conflicted,
+                               const char *path,
+                               const char *tmpfile1,
+                               const char *tmpfile2,
+                               svn_revnum_t rev1,
+                               svn_revnum_t rev2,
+                               const char *mimetype1,
+                               const char *mimetype2,
+                               const apr_array_header_t *propchanges,
+                               apr_hash_t *originalprops,
+                               void *diff_baton);
+
+  /** Similar to @c svn_wc_diff_callbacks4_t's @c file_added but without
+   * @a copyfrom_path and @a copyfrom_revision arguments. */
+  svn_error_t *(*file_added)(svn_wc_adm_access_t *adm_access,
+                             svn_wc_notify_state_t *contentstate,
+                             svn_wc_notify_state_t *propstate,
+                             svn_boolean_t *tree_conflicted,
+                             const char *path,
+                             const char *tmpfile1,
+                             const char *tmpfile2,
+                             svn_revnum_t rev1,
+                             svn_revnum_t rev2,
+                             const char *mimetype1,
+                             const char *mimetype2,
+                             const apr_array_header_t *propchanges,
+                             apr_hash_t *originalprops,
+                             void *diff_baton);
+
+  /** The same as @c file_deleted in @c svn_wc_diff_callbacks4_t. */
+  svn_error_t *(*file_deleted)(svn_wc_adm_access_t *adm_access,
+                               svn_wc_notify_state_t *state,
+                               svn_boolean_t *tree_conflicted,
+                               const char *path,
+                               const char *tmpfile1,
+                               const char *tmpfile2,
+                               const char *mimetype1,
+                               const char *mimetype2,
+                               apr_hash_t *originalprops,
+                               void *diff_baton);
+
+  /** Similar to @c svn_wc_diff_callbacks4_t's @c dir_added but without
+   * @a copyfrom_path and @a copyfrom_revision arguments. */
+  svn_error_t *(*dir_added)(svn_wc_adm_access_t *adm_access,
+                            svn_wc_notify_state_t *state,
+                            svn_boolean_t *tree_conflicted,
+                            const char *path,
+                            svn_revnum_t rev,
+                            void *diff_baton);
+
+  /** The same as @c dir_deleted in @c svn_wc_diff_callbacks4_t. */
+  svn_error_t *(*dir_deleted)(svn_wc_adm_access_t *adm_access,
+                              svn_wc_notify_state_t *state,
+                              svn_boolean_t *tree_conflicted,
+                              const char *path,
+                              void *diff_baton);
+
+  /** The same as @c dir_props_changed in @c svn_wc_diff_callbacks4_t. */
+  svn_error_t *(*dir_props_changed)(svn_wc_adm_access_t *adm_access,
+                                    svn_wc_notify_state_t *propstate,
+                                    svn_boolean_t *tree_conflicted,
+                                    const char *path,
+                                    const apr_array_header_t *propchanges,
+                                    apr_hash_t *original_props,
+                                    void *diff_baton);
+
+  /** The same as @c dir_opened in @c svn_wc_diff_callbacks4_t. */
+  svn_error_t *(*dir_opened)(svn_wc_adm_access_t *adm_access,
+                             svn_boolean_t *tree_conflicted,
+                             const char *path,
+                             svn_revnum_t rev,
+                             void *diff_baton);
+
+  /** The same as @c dir_closed in @c svn_wc_diff_callbacks4_t. */
+  svn_error_t *(*dir_closed)(svn_wc_adm_access_t *adm_access,
+                             svn_wc_notify_state_t *contentstate,
+                             svn_wc_notify_state_t *propstate,
+                             svn_boolean_t *tree_conflicted,
+                             const char *path,
+                             void *diff_baton);
+
 } svn_wc_diff_callbacks3_t;

 /**
- * Similar to @c svn_wc_diff_callbacks3_t, but without the dir_opened()
- * function, and without the 'tree_conflicted' argument to the functions.
+ * Similar to @c svn_wc_diff_callbacks3_t, but without the @c dir_opened
+ * and @c dir_closed functions, and without the @a tree_conflicted argument
+ * to the functions.
 *
 * @deprecated Provided for backward compatibility with the 1.2 API.
 */
@@ -3153,7 +3266,9 @@ svn_wc_status_set_repos_locks(void *set_
 * @a src must be a file or directory under version control; @a dst_parent
 * must be a directory under version control in the same working copy;
 * @a dst_basename will be the name of the copied item, and it must not
- * exist already.
+ * exist already.  Note that when @a src points to a versioned file, the
+ * working file doesn't necessarily exist in which case its text-base is
+ * used instead.
 *
 * If @a cancel_func is non-NULL, call it with @a cancel_baton at
 * various points during the operation.  If it returns an error
@@ -4501,14 +4616,47 @@ svn_wc_canonicalize_svn_prop(const svn_s
 * If @a cancel_func is non-NULL, it will be used along with @a cancel_baton
 * to periodically check if the client has canceled the operation.
 *
+ * @a svnpatch_file is the temporary file to which the function dumps
+ * serialized ra_svn protocol editor commands.  It somehow determines whether
+ * or not to utilize svnpatch format in the diff output when checked against @c
+ * NULL.  The caller must allocate the file handler, open and close the file
+ * respectively before and after the call.
+ *
 * @a changelists is an array of <tt>const char *</tt> changelist
 * names, used as a restrictive filter on items whose differences are
 * reported; that is, don't generate diffs about any item unless
 * it's a member of one of those changelists.  If @a changelists is
 * empty (or altogether @c NULL), no changelist filtering occurs.
 *
+ * @since New in 1.7.
+ */
+svn_error_t *
+svn_wc_get_diff_editor6(svn_wc_adm_access_t *anchor,
+                        const char *target,
+                        const svn_wc_diff_callbacks4_t *callbacks,
+                        void *callback_baton,
+                        svn_depth_t depth,
+                        svn_boolean_t ignore_ancestry,
+                        svn_boolean_t use_text_base,
+                        svn_boolean_t reverse_order,
+                        svn_cancel_func_t cancel_func,
+                        void *cancel_baton,
+                        const apr_array_header_t *changelists,
+                        const svn_delta_editor_t **editor,
+                        void **edit_baton,
+                        apr_file_t *svnpatch_file,
+                        apr_pool_t *pool);
+
+/**
+ * Similar to svn_wc_get_diff_editor6(), but with an
+ * @c svn_wc_diff_callbacks3_t instead of @c svn_wc_diff_callbacks4_t,
+ * and @a svnpatch_file set to @c NULL.
+ *
 * @since New in 1.6.
+ *
+ * @deprecated Provided for backward compatibility with the 1.6 API.
 */
+SVN_DEPRECATED
 svn_error_t *
 svn_wc_get_diff_editor5(svn_wc_adm_access_t *anchor,
                        const char *target,
@@ -4640,14 +4788,42 @@ svn_wc_get_diff_editor(svn_wc_adm_access
 * @a ignore_ancestry is @c FALSE, then any discontinuous node ancestry will
 * result in the diff given as a full delete followed by an add.
 *
+ * @a svnpatch_file is the temporary file to which the function dumps
+ * serialized ra_svn protocol editor commands.  It somehow determines whether
+ * or not to utilize svnpatch format in the diff output when checked against @c
+ * NULL.  The caller must allocate the file handler, open and close the file
+ * respectively before and after the call.
+ *
 * @a changelists is an array of <tt>const char *</tt> changelist
 * names, used as a restrictive filter on items whose differences are
 * reported; that is, don't generate diffs about any item unless
 * it's a member of one of those changelists.  If @a changelists is
 * empty (or altogether @c NULL), no changelist filtering occurs.
 *
+ * @since New in 1.7.
+ */
+svn_error_t *
+svn_wc_diff6(svn_wc_adm_access_t *anchor,
+             const char *target,
+             const svn_wc_diff_callbacks4_t *callbacks,
+             void *callback_baton,
+             svn_depth_t depth,
+             svn_boolean_t ignore_ancestry,
+             const apr_array_header_t *changelists,
+             apr_file_t *svnpatch_file,
+             apr_pool_t *pool);
+
+/**
+ * Similar to svn_wc_diff6(), but with a @c svn_wc_diff_callbacks3_t argument
+ * instead of @c svn_wc_diff_callbacks4_t.
+ *
+ * @a svnpatch_file is always set to @c NULL.
+ *
 * @since New in 1.6.
+ *
+ * @deprecated Provided for backward compatibility with the 1.6 API.
 */
+SVN_DEPRECATED
 svn_error_t *
 svn_wc_diff5(svn_wc_adm_access_t *anchor,
             const char *target,
@@ -4675,7 +4851,6 @@ svn_wc_diff4(svn_wc_adm_access_t *anchor
             const apr_array_header_t *changelists,
             apr_pool_t *pool);

-
 /**
 * Similar to svn_wc_diff4(), but with @a changelists passed @c NULL,
 * and @a depth set to @c svn_depth_infinity if @a recurse is TRUE, or
@@ -5600,6 +5775,116 @@ svn_wc_crop_tree(svn_wc_adm_access_t *an

 /** @} */

+/**
+ *
+ * @defgroup svn_wc_svnpatch svnpatch related functions
+ *
+ * @{
+ *
+ */
+
+/* Output -- Writing */
+
+/* Append @a number into @a target stream. */
+svn_error_t *
+svn_wc_write_number(svn_stream_t *target,
+                    apr_pool_t *pool,
+                    const apr_uint64_t number);
+
+/* Append @a str into @a target stream.  Is binary-able. */
+svn_error_t *
+svn_wc_write_string(svn_stream_t *target,
+                    apr_pool_t *pool,
+                    const svn_string_t *str);
+
+/* Append @a s cstring into @a target stream. */
+svn_error_t *
+svn_wc_write_cstring(svn_stream_t *target,
+                     apr_pool_t *pool,
+                     const char *s);
+
+/* Append @a word into @a target stream. */
+svn_error_t *
+svn_wc_write_word(svn_stream_t *target,
+                  apr_pool_t *pool,
+                  const char *word);
+
+/* Append a list of properties @a props into @a target. */
+svn_error_t *
+svn_wc_write_proplist(svn_stream_t *target,
+                      apr_hash_t *props,
+                      apr_pool_t *pool);
+
+/* Begin a list, appended into @target */
+svn_error_t *
+svn_wc_start_list(svn_stream_t *target);
+
+/* End a list, appended into @target */
+svn_error_t *
+svn_wc_end_list(svn_stream_t *target);
+
+/* Append a tuple into @target in a printf-like fashion.
+ * @see svn_ra_svn_write_tuple() for further details with the format. */
+svn_error_t *
+svn_wc_write_tuple(svn_stream_t *target,
+                   apr_pool_t *pool,
+                   const char *fmt, ...);
+
+/* Append a command into @target, using the same format notation as
+ * svn_wc_write_tuple(). */
+svn_error_t *
+svn_wc_write_cmd(svn_stream_t *target,
+                 apr_pool_t *pool,
+                 const char *cmdname,
+                 const char *fmt, ...);
+
+/* Input -- Reading */
+
+svn_error_t *
+svn_wc_read_item(svn_stream_t *from,
+                 apr_pool_t *pool,
+                 svn_ra_svn_item_t **item);
+
+svn_error_t *
+svn_wc_parse_tuple(apr_array_header_t *list,
+                   apr_pool_t *pool,
+                   const char *fmt, ...);
+
+svn_error_t *
+svn_wc_read_tuple(svn_stream_t *from,
+                  apr_pool_t *pool,
+                  const char *fmt, ...);
+
+/* Drive @a diff_editor against @a decoded_patch_file's clear-text
+ * Editor Commands. */
+svn_error_t *
+svn_wc_apply_svnpatch(apr_file_t *decoded_patch_file,
+                      const svn_delta_editor_t *diff_editor,
+                      void *diff_edit_baton,
+                      apr_pool_t *pool);
+
+/* Run an external patch program against @a patch_path patch file.  @a
+ * outfile and @a errfile are respectively connected to the external
+ * program's stdout and stderr pipes when executed.  @a config is looked
+ * up for the SVN_CONFIG_OPTION_PATCH_CMD entry to use as the patch
+ * program.  If missing or @a config is @c NULL, the function tries to
+ * execute 'patch' literally, which should work on most *NIX systems at
+ * least.  This involves searching into $PATH.  The external program is
+ * given the patch file via its stdin pipe.
+ *
+ * The program is passed the '--force' argument when @a force is set.
+ */
+svn_error_t *
+svn_wc_apply_unidiff(const char *patch_path,
+                     svn_boolean_t force,
+                     apr_file_t *outfile,
+                     apr_file_t *errfile,
+                     apr_hash_t *config,
+                     apr_pool_t *pool);
+
+/** @} end group: svnpatch related functions */
+
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */

Modified: trunk/subversion/libsvn_client/client.h
URL: http://svn.collab.net/viewvc/svn/trunk/subversion/libsvn_client/client.h?pathrev=36404&r1=36403&r2=36404
==============================================================================
--- trunk/subversion/libsvn_client/client.h     Sat Mar  7 14:54:27
2009        (r36403)
+++ trunk/subversion/libsvn_client/client.h     Sat Mar  7 15:41:42
2009        (r36404)
@@ -2,7 +2,7 @@
 * client.h :  shared stuff internal to the client library.
 *
 * ====================================================================
- * Copyright (c) 2000-2008 CollabNet.  All rights reserved.
+ * Copyright (c) 2000-2009 CollabNet.  All rights reserved.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution.  The terms
@@ -34,6 +34,8 @@
 extern "C" {
 #endif /* __cplusplus */

+#define SVN_CLIENT_SVNPATCH_VERSION   1
+

 /* Set *URL and *PEG_REVNUM (the latter is ignored if NULL) to the
   repository URL of PATH_OR_URL.  If PATH_OR_URL is a WC path and
@@ -687,11 +689,17 @@ svn_client__switch_internal(svn_revnum_t
   If NOTIFY_FUNC is non-null, invoke it with NOTIFY_BATON for each
   file and directory operated on during the edit.

-   EDITOR/EDIT_BATON return the newly created editor and baton/  */
+   EDITOR/EDIT_BATON return the newly created editor and baton/
+
+   SVNPATCH_FILE is the temporary file to which the library dumps
+   serialized ra_svn protocol Editor Commands.  It somehow determines
+   whether or not to utilize svnpatch format in the diff output when
+   checked against NULL.  The caller must allocate the file handler,
+   open and close the file respectively before and after the call. */
 svn_error_t *
 svn_client__get_diff_editor(const char *target,
                            svn_wc_adm_access_t *adm_access,
-                            const svn_wc_diff_callbacks3_t *diff_cmd,
+                            const svn_wc_diff_callbacks4_t *diff_cmd,
                            void *diff_cmd_baton,
                            svn_depth_t depth,
                            svn_boolean_t dry_run,
@@ -703,6 +711,7 @@ svn_client__get_diff_editor(const char *
                            void *cancel_baton,
                            const svn_delta_editor_t **editor,
                            void **edit_baton,
+                            apr_file_t *svnpatch_file,
                            apr_pool_t *pool);

Modified: trunk/subversion/libsvn_client/deprecated.c
URL: http://svn.collab.net/viewvc/svn/trunk/subversion/libsvn_client/deprecated.c?pathrev=36404&r1=36403&r2=36404
==============================================================================
--- trunk/subversion/libsvn_client/deprecated.c Sat Mar  7 14:54:27
2009        (r36403)
+++ trunk/subversion/libsvn_client/deprecated.c Sat Mar  7 15:41:42
2009        (r36404)
@@ -612,6 +612,31 @@ svn_client_delete(svn_client_commit_info
 /*** From diff.c ***/

 svn_error_t *
+svn_client_diff4(const apr_array_header_t *options,
+                 const char *path1,
+                 const svn_opt_revision_t *revision1,
+                 const char *path2,
+                 const svn_opt_revision_t *revision2,
+                 const char *relative_to_dir,
+                 svn_depth_t depth,
+                 svn_boolean_t ignore_ancestry,
+                 svn_boolean_t no_diff_deleted,
+                 svn_boolean_t ignore_content_type,
+                 const char *header_encoding,
+                 apr_file_t *outfile,
+                 apr_file_t *errfile,
+                 const apr_array_header_t *changelists,
+                 svn_client_ctx_t *ctx,
+                 apr_pool_t *pool)
+{
+  return svn_client_diff5(options, path1, revision1, path2, revision2,
+                          relative_to_dir, depth, ignore_ancestry,
+                          no_diff_deleted, ignore_content_type, FALSE,
+                          header_encoding, outfile, errfile, changelists,
+                          ctx, pool);
+}
+
+svn_error_t *
 svn_client_diff3(const apr_array_header_t *options,
                 const char *path1,
                 const svn_opt_revision_t *revision1,
@@ -676,6 +701,31 @@ svn_client_diff(const apr_array_header_t
 }

 svn_error_t *
+svn_client_diff_peg4(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,
+                     const char *relative_to_dir,
+                     svn_depth_t depth,
+                     svn_boolean_t ignore_ancestry,
+                     svn_boolean_t no_diff_deleted,
+                     svn_boolean_t ignore_content_type,
+                     const char *header_encoding,
+                     apr_file_t *outfile,
+                     apr_file_t *errfile,
+                     const apr_array_header_t *changelists,
+                     svn_client_ctx_t *ctx,
+                     apr_pool_t *pool)
+{
+  return svn_client_diff_peg5(options, path, peg_revision, start_revision,
+                              end_revision, relative_to_dir, depth,
+                              ignore_ancestry, no_diff_deleted,
+                              ignore_content_type, FALSE, header_encoding,
+                              outfile, errfile, changelists, ctx, pool);
+}
+
+svn_error_t *
 svn_client_diff_peg3(const apr_array_header_t *options,
                     const char *path,
                     const svn_opt_revision_t *peg_revision,

Modified: trunk/subversion/libsvn_client/diff.c
URL: http://svn.collab.net/viewvc/svn/trunk/subversion/libsvn_client/diff.c?pathrev=36404&r1=36403&r2=36404
==============================================================================
--- trunk/subversion/libsvn_client/diff.c       Sat Mar  7 14:54:27
2009        (r36403)
+++ trunk/subversion/libsvn_client/diff.c       Sat Mar  7 15:41:42
2009        (r36404)
@@ -2,7 +2,7 @@
 * diff.c: comparing
 *
 * ====================================================================
- * Copyright (c) 2000-2007, 2009 CollabNet.  All rights reserved.
+ * Copyright (c) 2000-2009 CollabNet.  All rights reserved.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution.  The terms
@@ -42,12 +42,19 @@
 #include "svn_props.h"
 #include "svn_time.h"
 #include "svn_sorts.h"
+#include "svn_base64.h"
 #include "client.h"

 #include "private/svn_wc_private.h"

 #include "svn_private_config.h"

+#ifdef SVN_DEBUG
+#define SVNPATCH_DELETE_WHEN svn_io_file_del_none
+#else
+#define SVNPATCH_DELETE_WHEN svn_io_file_del_on_close
+#endif
+

 /*
 * Constant separator strings
@@ -166,7 +173,7 @@ display_mergeinfo_diff(const char *old_m

 /* A helper func that writes out verbal descriptions of property diffs
   to FILE.   Of course, the apr_file_t will probably be the 'outfile'
-   passed to svn_client_diff4, which is probably stdout. */
+   passed to svn_client_diff5, which is probably stdout. */
 static svn_error_t *
 display_prop_diffs(const apr_array_header_t *propchanges,
                   apr_hash_t *original_props,
@@ -328,8 +335,8 @@ struct diff_cmd_baton {
  const char *orig_path_2;

  /* These are the numeric representations of the revisions passed to
-     svn_client_diff4, either may be SVN_INVALID_REVNUM.  We need these
-     because some of the svn_wc_diff_callbacks3_t don't get revision
+     svn_client_diff5, either may be SVN_INVALID_REVNUM.  We need these
+     because some of the svn_wc_diff_callbacks4_t don't get revision
     arguments.

     ### Perhaps we should change the callback signatures and eliminate
@@ -345,12 +352,75 @@ struct diff_cmd_baton {
     unconditionally, even if the diffs are empty. */
  svn_boolean_t force_empty;

+  /* The svnpatch temporary storage area. */
+  apr_file_t *svnpatch_file;
+
  /* The directory that diff target paths should be considered as
     relative to for output generation (see issue #2723). */
  const char *relative_to_dir;
 };

+/* Dump gzip'ed-base64'ed svnpatch bytes to @c callback_baton->outfile
+ * from clear-text @c callback_baton->svnpatch_file. */
+static svn_error_t *
+dump_svnpatch(struct diff_cmd_baton *callback_baton,
+              apr_pool_t *pool)
+{
+  svn_stream_t *svnpatch_stream;
+  svn_stream_t *out_stream;
+  apr_off_t offset = 0;
+  apr_finfo_t finfo;
+  const char *fname;
+
+  /* We're not going to write anymore to the file, let's flush it all as
+   * we need a consistent file. */
+  SVN_ERR(svn_io_file_flush_to_disk(callback_baton->svnpatch_file,
+                                    pool));
+
+  /* Easy out if the file is empty. */
+  apr_file_name_get(&fname, callback_baton->svnpatch_file);
+  SVN_ERR(svn_io_stat(&finfo, fname, APR_FINFO_SIZE, pool));
+  if (finfo.size < 1)
+    return SVN_NO_ERROR;
+
+  /* Print svnpatch header block. */
+  SVN_ERR(file_printf_from_utf8
+          (callback_baton->outfile,
+           callback_baton->header_encoding,
+           "%s SVNPATCH%d BLOCK %s" APR_EOL_STR,
+           equal_string + 42,
+           SVN_CLIENT_SVNPATCH_VERSION,
+           equal_string + 42));
+
+  /* Rewind to the start of the file. */
+  SVN_ERR(svn_io_file_seek(callback_baton->svnpatch_file,
+                           APR_SET, &offset, pool));
+
+  /* Streamy dump of @c svnpatch_file -- holds clear-text ra_svn editor
+   * commands -- to @c outfile -- gzip'ed-base64'ed.  Wrap apr
+   * write-handler in a base64'ed one which is in turn wrapped in
+   * zlib's.  In other words, the copy operation invokes these two
+   * functions in a streamy way: read(svnpatch_file),
+   * write(zlib(base64(outfile))).  */
+  svnpatch_stream = svn_stream_from_aprfile2
+                     (callback_baton->svnpatch_file,
+                      FALSE, pool);
+  out_stream = svn_stream_compressed
+                (svn_base64_encode(
+                 svn_stream_from_aprfile2
+                  (callback_baton->outfile,
+                   FALSE, pool),
+                 pool), pool);
+  SVN_ERR(svn_stream_copy(svnpatch_stream, out_stream, pool));
+
+  /* Squeeze out. */
+  SVN_ERR(svn_stream_close(out_stream));
+
+  return SVN_NO_ERROR;
+}
+
+
 /* Generate a label for the diff output for file PATH at revision REVNUM.
   If REVNUM is invalid then it is assumed to be the current working
   copy.  Assumes the paths are already in the desired style (local
@@ -369,7 +439,7 @@ diff_label(const char *path,
  return label;
 }

-/* An svn_wc_diff_callbacks3_t function.  Used for both file and directory
+/* An svn_wc_diff_callbacks4_t function.  Used for both file and directory
   property diffs. */
 static svn_error_t *
 diff_props_changed(svn_wc_adm_access_t *adm_access,
@@ -602,7 +672,7 @@ diff_content_changed(const char *path,
  return SVN_NO_ERROR;
 }

-/* An svn_wc_diff_callbacks3_t function. */
+/* An svn_wc_diff_callbacks4_t function. */
 static svn_error_t *
 diff_file_changed(svn_wc_adm_access_t *adm_access,
                  svn_wc_notify_state_t *content_state,
@@ -640,7 +710,7 @@ diff_file_changed(svn_wc_adm_access_t *a
   each of these next two functions, they can be dumb wrappers around
   the main workhorse routine. */

-/* An svn_wc_diff_callbacks3_t function. */
+/* An svn_wc_diff_callbacks4_t function. */
 static svn_error_t *
 diff_file_added(svn_wc_adm_access_t *adm_access,
                svn_wc_notify_state_t *content_state,
@@ -653,6 +723,8 @@ diff_file_added(svn_wc_adm_access_t *adm
                svn_revnum_t rev2,
                const char *mimetype1,
                const char *mimetype2,
+                const char *copyfrom_path,
+                svn_revnum_t copyfrom_revision,
                const apr_array_header_t *prop_changes,
                apr_hash_t *original_props,
                void *diff_baton)
@@ -678,7 +750,7 @@ diff_file_added(svn_wc_adm_access_t *adm
  return SVN_NO_ERROR;
 }

-/* An svn_wc_diff_callbacks3_t function. */
+/* An svn_wc_diff_callbacks4_t function. */
 static svn_error_t *
 diff_file_deleted_with_diff(svn_wc_adm_access_t *adm_access,
                            svn_wc_notify_state_t *state,
@@ -703,7 +775,7 @@ diff_file_deleted_with_diff(svn_wc_adm_a
                           apr_hash_make(diff_cmd_baton->pool), diff_baton);
 }

-/* An svn_wc_diff_callbacks3_t function. */
+/* An svn_wc_diff_callbacks4_t function. */
 static svn_error_t *
 diff_file_deleted_no_diff(svn_wc_adm_access_t *adm_access,
                          svn_wc_notify_state_t *state,
@@ -730,13 +802,15 @@ diff_file_deleted_no_diff(svn_wc_adm_acc
           path, equal_string);
 }

-/* An svn_wc_diff_callbacks3_t function. */
+/* An svn_wc_diff_callbacks4_t function. */
 static svn_error_t *
 diff_dir_added(svn_wc_adm_access_t *adm_access,
               svn_wc_notify_state_t *state,
               svn_boolean_t *tree_conflicted,
               const char *path,
               svn_revnum_t rev,
+               const char *copyfrom_path,
+               svn_revnum_t copyfrom_revision,
               void *diff_baton)
 {
  if (state)
@@ -749,7 +823,7 @@ diff_dir_added(svn_wc_adm_access_t *adm_
  return SVN_NO_ERROR;
 }

-/* An svn_wc_diff_callbacks3_t function. */
+/* An svn_wc_diff_callbacks4_t function. */
 static svn_error_t *
 diff_dir_deleted(svn_wc_adm_access_t *adm_access,
                 svn_wc_notify_state_t *state,
@@ -767,7 +841,7 @@ diff_dir_deleted(svn_wc_adm_access_t *ad
  return SVN_NO_ERROR;
 }

-/* An svn_wc_diff_callbacks3_t function. */
+/* An svn_wc_diff_callbacks4_t function. */
 static svn_error_t *
 diff_dir_opened(svn_wc_adm_access_t *adm_access,
                svn_boolean_t *tree_conflicted,
@@ -783,7 +857,7 @@ diff_dir_opened(svn_wc_adm_access_t *adm
  return SVN_NO_ERROR;
 }

-/* An svn_wc_diff_callbacks3_t function. */
+/* An svn_wc_diff_callbacks4_t function. */
 static svn_error_t *
 diff_dir_closed(svn_wc_adm_access_t *adm_access,
                svn_wc_notify_state_t *contentstate,
@@ -1085,8 +1159,8 @@ diff_prepare_repos_repos(const struct di

 /* A Theoretical Note From Ben, regarding do_diff().

-   This function is really svn_client_diff4().  If you read the public
-   API description for svn_client_diff4(), it sounds quite Grand.  It
+   This function is really svn_client_diff5().  If you read the public
+   API description for svn_client_diff5(), it sounds quite Grand.  It
   sounds really generalized and abstract and beautiful: that it will
   diff any two paths, be they working-copy paths or URLs, at any two
   revisions.
@@ -1106,7 +1180,7 @@ diff_prepare_repos_repos(const struct di
   pigeonholed into one of these three use-cases, we currently bail
   with a friendly apology.

-   Perhaps someday a brave soul will truly make svn_client_diff4
+   Perhaps someday a brave soul will truly make svn_client_diff5
   perfectly general.  For now, we live with the 90% case.  Certainly,
   the commandline client only calls this function in legal ways.
   When there are other users of svn_client.h, maybe this will become
@@ -1119,7 +1193,7 @@ static svn_error_t *
 unsupported_diff_error(svn_error_t *child_err)
 {
  return svn_error_create(SVN_ERR_INCORRECT_PARAMS, child_err,
-                          _("Sorry, svn_client_diff4 was called in a way "
+                          _("Sorry, svn_client_diff5 was called in a way "
                            "that is not yet supported"));
 }

@@ -1129,7 +1203,7 @@ unsupported_diff_error(svn_error_t *chil
   PATH1 and PATH2 are both working copy paths.  REVISION1 and
   REVISION2 are their respective revisions.

-   All other options are the same as those passed to svn_client_diff4(). */
+   All other options are the same as those passed to svn_client_diff5(). */
 static svn_error_t *
 diff_wc_wc(const char *path1,
           const svn_opt_revision_t *revision1,
@@ -1138,7 +1212,7 @@ diff_wc_wc(const char *path1,
           svn_depth_t depth,
           svn_boolean_t ignore_ancestry,
           const apr_array_header_t *changelists,
-           const svn_wc_diff_callbacks3_t *callbacks,
+           const svn_wc_diff_callbacks4_t *callbacks,
           struct diff_cmd_baton *callback_baton,
           svn_client_ctx_t *ctx,
           apr_pool_t *pool)
@@ -1171,8 +1245,9 @@ diff_wc_wc(const char *path1,
          (&callback_baton->revnum1, NULL, NULL, revision1, path1, pool));
  callback_baton->revnum2 = SVN_INVALID_REVNUM;  /* WC */

-  SVN_ERR(svn_wc_diff5(adm_access, target, callbacks, callback_baton,
-                       depth, ignore_ancestry, changelists, pool));
+  SVN_ERR(svn_wc_diff6(adm_access, target, callbacks, callback_baton,
+                       depth, ignore_ancestry, changelists,
+                       callback_baton->svnpatch_file, pool));
  return svn_wc_adm_close2(adm_access, pool);
 }

@@ -1185,10 +1260,10 @@ diff_wc_wc(const char *path1,
   DIFF_PARAM.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_diff4(). */
+   All other options are the same as those passed to svn_client_diff5(). */
 static svn_error_t *
 diff_repos_repos(const struct diff_parameters *diff_param,
-                 const svn_wc_diff_callbacks3_t *callbacks,
+                 const svn_wc_diff_callbacks4_t *callbacks,
                 struct diff_cmd_baton *callback_baton,
                 svn_client_ctx_t *ctx,
                 apr_pool_t *pool)
@@ -1229,7 +1304,8 @@ diff_repos_repos(const struct diff_param
           FALSE /* doesn't matter for diff */, extra_ra_session, drr.rev1,
           NULL /* no notify_func */, NULL /* no notify_baton */,
           ctx->cancel_func, ctx->cancel_baton,
-           &diff_editor, &diff_edit_baton, pool));
+           &diff_editor, &diff_edit_baton,
+           callback_baton->svnpatch_file, pool));

  /* We want to switch our txn into URL2 */
  SVN_ERR(svn_ra_do_diff3
@@ -1255,7 +1331,7 @@ diff_repos_repos(const struct diff_param
   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_diff4(). */
+   All other options are the same as those passed to svn_client_diff5(). */
 static svn_error_t *
 diff_repos_wc(const char *path1,
              const svn_opt_revision_t *revision1,
@@ -1266,7 +1342,7 @@ diff_repos_wc(const char *path1,
              svn_depth_t depth,
              svn_boolean_t ignore_ancestry,
              const apr_array_header_t *changelists,
-              const svn_wc_diff_callbacks3_t *callbacks,
+              const svn_wc_diff_callbacks4_t *callbacks,
              struct diff_cmd_baton *callback_baton,
              svn_client_ctx_t *ctx,
              apr_pool_t *pool)
@@ -1337,7 +1413,7 @@ diff_repos_wc(const char *path1,
                                               NULL, NULL, NULL, FALSE, TRUE,
                                               ctx, pool));

-  SVN_ERR(svn_wc_get_diff_editor5(adm_access, target,
+  SVN_ERR(svn_wc_get_diff_editor6(adm_access, target,
                                  callbacks, callback_baton,
                                  depth,
                                  ignore_ancestry,
@@ -1346,6 +1422,7 @@ diff_repos_wc(const char *path1,
                                  ctx->cancel_func, ctx->cancel_baton,
                                  changelists,
                                  &diff_editor, &diff_edit_baton,
+                                  callback_baton->svnpatch_file,
                                  pool));

  /* Tell the RA layer we want a delta to change our txn to URL1 */
@@ -1383,10 +1460,10 @@ diff_repos_wc(const char *path1,
 }

-/* This is basically just the guts of svn_client_diff[_peg]3(). */
+/* This is basically just the guts of svn_client_diff[_peg]5(). */
 static svn_error_t *
 do_diff(const struct diff_parameters *diff_param,
-        const svn_wc_diff_callbacks3_t *callbacks,
+        const svn_wc_diff_callbacks4_t *callbacks,
        struct diff_cmd_baton *callback_baton,
        svn_client_ctx_t *ctx,
        apr_pool_t *pool)
@@ -1400,42 +1477,47 @@ do_diff(const struct diff_parameters *di
    {
      if (diff_paths.is_repos2)
        {
-          return diff_repos_repos(diff_param, callbacks, callback_baton,
-                                  ctx, pool);
+          SVN_ERR(diff_repos_repos(diff_param, callbacks, callback_baton,
+                                   ctx, pool));
        }
      else /* path2 is a working copy path */
        {
-          return diff_repos_wc(diff_param->path1, diff_param->revision1,
-                               diff_param->peg_revision,
-                               diff_param->path2, diff_param->revision2,
-                               FALSE, diff_param->depth,
-                               diff_param->ignore_ancestry,
-                               diff_param->changelists,
-                               callbacks, callback_baton, ctx, pool);
+          SVN_ERR(diff_repos_wc(diff_param->path1, diff_param->revision1,
+                                diff_param->peg_revision,
+                                diff_param->path2, diff_param->revision2,
+                                FALSE, diff_param->depth,
+                                diff_param->ignore_ancestry,
+                                diff_param->changelists,
+                                callbacks, callback_baton, ctx, pool));
        }
    }
  else /* path1 is a working copy path */
    {
      if (diff_paths.is_repos2)
        {
-          return diff_repos_wc(diff_param->path2, diff_param->revision2,
-                               diff_param->peg_revision,
-                               diff_param->path1, diff_param->revision1,
-                               TRUE, diff_param->depth,
-                               diff_param->ignore_ancestry,
-                               diff_param->changelists,
-                               callbacks, callback_baton, ctx, pool);
+          SVN_ERR(diff_repos_wc(diff_param->path2, diff_param->revision2,
+                                diff_param->peg_revision,
+                                diff_param->path1, diff_param->revision1,
+                                TRUE, diff_param->depth,
+                                diff_param->ignore_ancestry,
+                                diff_param->changelists,
+                                callbacks, callback_baton, ctx, pool));
        }
      else /* path2 is a working copy path */
        {
-          return diff_wc_wc(diff_param->path1, diff_param->revision1,
-                            diff_param->path2, diff_param->revision2,
-                            diff_param->depth,
-                            diff_param->ignore_ancestry,
-                            diff_param->changelists,
-                            callbacks, callback_baton, ctx, pool);
+          SVN_ERR(diff_wc_wc(diff_param->path1, diff_param->revision1,
+                             diff_param->path2, diff_param->revision2,
+                             diff_param->depth,
+                             diff_param->ignore_ancestry,
+                             diff_param->changelists,
+                             callbacks, callback_baton, ctx, pool));
        }
    }
+
+  if (callback_baton->svnpatch_file)
+    SVN_ERR(dump_svnpatch(callback_baton, pool));
+
+  return SVN_NO_ERROR;
 }

 /* Perform a diff summary between two repository paths. */
@@ -1594,7 +1676,7 @@ set_up_diff_cmd_and_options(struct diff_
      * These cases require server communication.
 */
 svn_error_t *
-svn_client_diff4(const apr_array_header_t *options,
+svn_client_diff5(const apr_array_header_t *options,
                 const char *path1,
                 const svn_opt_revision_t *revision1,
                 const char *path2,
@@ -1604,6 +1686,7 @@ svn_client_diff4(const apr_array_header_
                 svn_boolean_t ignore_ancestry,
                 svn_boolean_t no_diff_deleted,
                 svn_boolean_t ignore_content_type,
+                 svn_boolean_t svnpatch_format,
                 const char *header_encoding,
                 apr_file_t *outfile,
                 apr_file_t *errfile,
@@ -1614,7 +1697,7 @@ svn_client_diff4(const apr_array_header_
  struct diff_parameters diff_params;

  struct diff_cmd_baton diff_cmd_baton;
-  svn_wc_diff_callbacks3_t diff_callbacks;
+  svn_wc_diff_callbacks4_t diff_callbacks;

  /* We will never do a pegged diff from here. */
  svn_opt_revision_t peg_revision;
@@ -1656,13 +1739,24 @@ svn_client_diff4(const apr_array_header_

  diff_cmd_baton.force_empty = FALSE;
  diff_cmd_baton.force_binary = ignore_content_type;
+  diff_cmd_baton.svnpatch_file = NULL;
  diff_cmd_baton.relative_to_dir = relative_to_dir;

+  if (svnpatch_format)
+    {
+      svn_wc_adm_access_t *adm_access;
+      SVN_ERR(svn_wc_adm_open3(&adm_access, NULL, "", TRUE,
+                               0, NULL, NULL, pool));
+      SVN_ERR(svn_wc_create_tmp_file2(&(diff_cmd_baton.svnpatch_file), NULL,
+                                      svn_wc_adm_access_path(adm_access),
+                                      SVNPATCH_DELETE_WHEN, pool));
+    }
+
  return do_diff(&diff_params, &diff_callbacks, &diff_cmd_baton, ctx, pool);
 }

 svn_error_t *
-svn_client_diff_peg4(const apr_array_header_t *options,
+svn_client_diff_peg5(const apr_array_header_t *options,
                     const char *path,
                     const svn_opt_revision_t *peg_revision,
                     const svn_opt_revision_t *start_revision,
@@ -1672,6 +1766,7 @@ svn_client_diff_peg4(const apr_array_hea
                     svn_boolean_t ignore_ancestry,
                     svn_boolean_t no_diff_deleted,
                     svn_boolean_t ignore_content_type,
+                     svn_boolean_t svnpatch_format,
                     const char *header_encoding,
                     apr_file_t *outfile,
                     apr_file_t *errfile,
@@ -1682,7 +1777,7 @@ svn_client_diff_peg4(const apr_array_hea
  struct diff_parameters diff_params;

  struct diff_cmd_baton diff_cmd_baton;
-  svn_wc_diff_callbacks3_t diff_callbacks;
+  svn_wc_diff_callbacks4_t diff_callbacks;

  if (svn_path_is_url(path) &&
        (start_revision->kind == svn_opt_revision_base
@@ -1727,8 +1822,19 @@ svn_client_diff_peg4(const apr_array_hea

  diff_cmd_baton.force_empty = FALSE;
  diff_cmd_baton.force_binary = ignore_content_type;
+  diff_cmd_baton.svnpatch_file = NULL;
  diff_cmd_baton.relative_to_dir = relative_to_dir;

+  if (svnpatch_format)
+    {
+      svn_wc_adm_access_t *adm_access;
+      SVN_ERR(svn_wc_adm_open3(&adm_access, NULL, "", TRUE,
+                               0, NULL, NULL, pool));
+      SVN_ERR(svn_wc_create_tmp_file2(&(diff_cmd_baton.svnpatch_file), NULL,
+                                      svn_wc_adm_access_path(adm_access),
+                                      SVNPATCH_DELETE_WHEN, pool));
+    }
+
  return do_diff(&diff_params, &diff_callbacks, &diff_cmd_baton, ctx, pool);
 }

Modified: trunk/subversion/libsvn_client/merge.c
URL: http://svn.collab.net/viewvc/svn/trunk/subversion/libsvn_client/merge.c?pathrev=36404&r1=36403&r2=36404
==============================================================================
--- trunk/subversion/libsvn_client/merge.c      Sat Mar  7 14:54:27
2009        (r36403)
+++ trunk/subversion/libsvn_client/merge.c      Sat Mar  7 15:41:42
2009        (r36404)
@@ -2,7 +2,7 @@
 * merge.c: merging
 *
 * ====================================================================
- * Copyright (c) 2000-2007 CollabNet.  All rights reserved.
+ * Copyright (c) 2000-2009 CollabNet.  All rights reserved.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution.  The terms
@@ -908,7 +908,7 @@ filter_self_referential_mergeinfo(apr_ar
  return SVN_NO_ERROR;
 }

-/* An svn_wc_diff_callbacks3_t function.  Used for both file and directory
+/* An svn_wc_diff_callbacks4_t function.  Used for both file and directory
   property merges. */
 static svn_error_t *
 merge_props_changed(svn_wc_adm_access_t *adm_access,
@@ -1096,7 +1096,7 @@ conflict_resolver(svn_wc_conflict_result
  return err;
 }

-/* An svn_wc_diff_callbacks3_t function. */
+/* An svn_wc_diff_callbacks4_t function. */
 static svn_error_t *
 merge_file_changed(svn_wc_adm_access_t *adm_access,
                   svn_wc_notify_state_t *content_state,
@@ -1306,7 +1306,7 @@ merge_file_changed(svn_wc_adm_access_t *
  return SVN_NO_ERROR;
 }

-/* An svn_wc_diff_callbacks3_t function. */
+/* An svn_wc_diff_callbacks4_t function. */
 static svn_error_t *
 merge_file_added(svn_wc_adm_access_t *adm_access,
                 svn_wc_notify_state_t *content_state,
@@ -1319,6 +1319,8 @@ merge_file_added(svn_wc_adm_access_t *ad
                 svn_revnum_t rev2,
                 const char *mimetype1,
                 const char *mimetype2,
+                 const char *copyfrom_path,
+                 svn_revnum_t copyfrom_revision,
                 const apr_array_header_t *prop_changes,
                 apr_hash_t *original_props,
                 void *baton)
@@ -1571,7 +1573,7 @@ files_same_p(svn_boolean_t *same,
  return SVN_NO_ERROR;
 }

-/* An svn_wc_diff_callbacks3_t function. */
+/* An svn_wc_diff_callbacks4_t function. */
 static svn_error_t *
 merge_file_deleted(svn_wc_adm_access_t *adm_access,
                   svn_wc_notify_state_t *state,
@@ -1696,13 +1698,15 @@ merge_file_deleted(svn_wc_adm_access_t *
  return SVN_NO_ERROR;
 }

-/* An svn_wc_diff_callbacks3_t function. */
+/* An svn_wc_diff_callbacks4_t function. */
 static svn_error_t *
 merge_dir_added(svn_wc_adm_access_t *adm_access,
                svn_wc_notify_state_t *state,
                svn_boolean_t *tree_conflicted,
                const char *path,
                svn_revnum_t rev,
+                const char *copyfrom_path,
+                svn_revnum_t copyfrom_revision,
                void *baton)
 {
  merge_cmd_baton_t *merge_b = baton;
@@ -1878,7 +1882,7 @@ merge_dir_added(svn_wc_adm_access_t *adm
  return SVN_NO_ERROR;
 }

-/* An svn_wc_diff_callbacks3_t function. */
+/* An svn_wc_diff_callbacks4_t function. */
 static svn_error_t *
 merge_dir_deleted(svn_wc_adm_access_t *adm_access,
                  svn_wc_notify_state_t *state,
@@ -2006,7 +2010,7 @@ merge_dir_deleted(svn_wc_adm_access_t *a
  return SVN_NO_ERROR;
 }

-/* An svn_wc_diff_callbacks3_t function. */
+/* An svn_wc_diff_callbacks4_t function. */
 static svn_error_t *
 merge_dir_opened(svn_wc_adm_access_t *adm_access,
                 svn_boolean_t *tree_conflicted,
@@ -2059,7 +2063,7 @@ merge_dir_opened(svn_wc_adm_access_t *ad
  return SVN_NO_ERROR;
 }

-/* An svn_wc_diff_callbacks3_t function. */
+/* An svn_wc_diff_callbacks4_t function. */
 static svn_error_t *
 merge_dir_closed(svn_wc_adm_access_t *adm_access,
                 svn_wc_notify_state_t *contentstate,
@@ -2081,7 +2085,7 @@ merge_dir_closed(svn_wc_adm_access_t *ad
 }

 /* The main callback table for 'svn merge'.  */
-static const svn_wc_diff_callbacks3_t
+static const svn_wc_diff_callbacks4_t
 merge_callbacks =
  {
    merge_file_changed,
@@ -3637,7 +3641,7 @@ remove_children_with_deleted_mergeinfo(m

   DEPTH, NOTIFY_B, ADM_ACCESS, and MERGE_B are cascasded from
   do_directory_merge(), see that function for more info.  CALLBACKS are the
-   svn merge versions of the svn_wc_diff_callbacks3_t callbacks invoked by
+   svn merge versions of the svn_wc_diff_callbacks4_t callbacks invoked by
   the editor.

   If MERGE_B->sources_ancestral is set, then URL1_at_REVISION1 must be a
@@ -3655,7 +3659,7 @@ drive_merge_report_editor(const char *ta
                          svn_depth_t depth,
                          notification_receiver_baton_t *notify_b,
                          svn_wc_adm_access_t *adm_access,
-                          const svn_wc_diff_callbacks3_t *callbacks,
+                          const svn_wc_diff_callbacks4_t *callbacks,
                          merge_cmd_baton_t *merge_b,
                          apr_pool_t *pool)
 {
@@ -3739,6 +3743,7 @@ drive_merge_report_editor(const char *ta
                                      merge_b->ctx->cancel_func,
                                      merge_b->ctx->cancel_baton,
                                      &diff_editor, &diff_edit_baton,
+                                      NULL, /* disable svnpatch */
                                      pool));
  SVN_ERR(svn_ra_do_diff3(merge_b->ra_session1,
                          &reporter, &report_baton, revision2,
@@ -5578,6 +5583,7 @@ do_file_merge(const char *url1,
                                       r->start,
                                       r->end,
                                       mimetype1, mimetype2,
+                                       NULL, SVN_INVALID_REVNUM, /* XXX API */
                                       propchanges, props1,
                                       merge_b));
              single_file_merge_notify(notify_b, target_wcpath,

Copied: trunk/subversion/libsvn_client/patch.c (from r36403,
branches/svnpatch-diff/subversion/libsvn_client/patch.c)
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ trunk/subversion/libsvn_client/patch.c      Sat Mar  7 15:41:42
2009        (r36404, copy of r36403,
branches/svnpatch-diff/subversion/libsvn_client/patch.c)
@@ -0,0 +1,1781 @@
+/*
+ * patch.c:  wrapper around wc patch functionality.
+ *
+ * ====================================================================
+ * Copyright (c) 2007-2009 CollabNet.  All rights reserved.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution.  The terms
+ * are also available at http://subversion.tigris.org/license-1.html.
+ * If newer versions of this license are posted there, you may use a
+ * newer version instead, at your option.
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals.  For exact contribution history, see the revision
+ * history and logs, available at http://subversion.tigris.org/.
+ * ====================================================================
+ */
+
+/* ==================================================================== */
+
+
+
+/*** Includes. ***/
+
+#include "svn_time.h"
+#include "svn_wc.h"
+#include "svn_client.h"
+#include "svn_config.h"
+#include "client.h"
+#include "svn_io.h"
+#include "svn_path.h"
+#include "svn_pools.h"
+#include "svn_base64.h"
+#include "svn_props.h"
+#include "svn_string.h"
+#include "svn_hash.h"
+#include <assert.h>
+
+#include "svn_private_config.h"
+
+
+/*** Code. ***/
+
+#ifdef SVN_DEBUG
+#define SVNPATCH_DELETE_WHEN svn_io_file_del_none
+#else
+#define SVNPATCH_DELETE_WHEN svn_io_file_del_on_close
+#endif
+
+static const char equal_string[] = "=========================";
+
+/*-----------------------------------------------------------------------*/
+
+/*** Utilities. ***/
+/* Sanity check -- ensure that we have valid revisions to look at. */
+#define ENSURE_VALID_REVISION_KINDS(rev1_kind, rev2_kind) \
+  if ((rev1_kind == svn_opt_revision_unspecified) \
+      || (rev2_kind == svn_opt_revision_unspecified)) \
+    { \
+      return svn_error_create \
+        (SVN_ERR_CLIENT_BAD_REVISION, NULL, \
+         _("Not all required revisions are specified")); \
+    }
+
+/*-----------------------------------------------------------------------*/
+
+
+struct patch_cmd_baton {
+  svn_boolean_t force;
+  svn_boolean_t dry_run;
+
+  /* Set to the dir path whenever the dir is added as a child of a
+   * versioned dir (dry-run only). */
+  const char *added_path;
+
+  /* Working copy target path. */
+  const char *target;
+
+  /* Client context for callbacks, etc. */
+  svn_client_ctx_t *ctx;
+
+  /* The list of paths for entries we've deleted, used only when in
+   * dry_run mode. */
+  apr_hash_t *dry_run_deletions;
+
+  apr_pool_t *pool;
+};
+
+/* Used to avoid spurious notifications (e.g. conflicts) from a merge
+   attempt into an existing target which would have been deleted if we
+   weren't in dry_run mode (issue #2584).  Assumes that WCPATH is
+   still versioned (e.g. has an associated entry). */
+static APR_INLINE svn_boolean_t
+dry_run_deleted_p(struct patch_cmd_baton *patch_b, const char *wcpath)
+{
+  return (patch_b->dry_run &&
+          apr_hash_get(patch_b->dry_run_deletions, wcpath,
+                       APR_HASH_KEY_STRING) != NULL);
+}
+
+
+/* A svn_wc_diff_callbacks4_t function.  Used for both file and directory
+   property merges. */
+static svn_error_t *
+merge_props_changed(svn_wc_adm_access_t *adm_access,
+                    svn_wc_notify_state_t *state,
+                    svn_boolean_t *tree_conflicted,
+                    const char *path,
+                    const apr_array_header_t *propchanges,
+                    apr_hash_t *original_props,
+                    void *baton)
+{
+  apr_array_header_t *props;
+  struct patch_cmd_baton *patch_b = baton;
+  apr_pool_t *subpool = svn_pool_create(patch_b->pool);
+  svn_error_t *err;
+
+  SVN_ERR(svn_categorize_props(propchanges, NULL, NULL, &props, subpool));
+
+  /* We only want to merge "regular" version properties:  by
+     definition, 'svn merge' shouldn't touch any data within .svn/,
+     so does 'svn patch'. */
+  if (props->nelts)
+    {
+      /* svn_wc_merge_props() requires ADM_ACCESS to be the access for
+         the parent of PATH. Since the advent of merge tracking,
+         discover_and_merge_children() may call this (indirectly) with
+         the access for the patch_b->target instead (issue #2781).
+         So, if we have the wrong access, get the right one. */
+      if (svn_path_compare_paths(svn_wc_adm_access_path(adm_access),
+                                 path) != 0)
+        SVN_ERR(svn_wc_adm_probe_try3(&adm_access, adm_access, path,
+                                      TRUE, -1, patch_b->ctx->cancel_func,
+                                      patch_b->ctx->cancel_baton, subpool));
+
+      err = svn_wc_merge_props(state, path, adm_access, original_props, props,
+                               FALSE, patch_b->dry_run, subpool);
+      if (err && (err->apr_err == SVN_ERR_ENTRY_NOT_FOUND
+                  || err->apr_err == SVN_ERR_UNVERSIONED_RESOURCE))
+        {
+          /* if the entry doesn't exist in the wc, just 'skip' over
+             this part of the tree-delta. */
+          if (state)
+            *state = svn_wc_notify_state_missing;
+          svn_error_clear(err);
+          svn_pool_destroy(subpool);
+          return SVN_NO_ERROR;
+        }
+      else if (err)
+        return err;
+    }
+
+  svn_pool_destroy(subpool);
+  return SVN_NO_ERROR;
+}
+
+/* A svn_wc_diff_callbacks4_t function. */
+static svn_error_t *
+merge_file_changed(svn_wc_adm_access_t *adm_access,
+                   svn_wc_notify_state_t *content_state,
+                   svn_wc_notify_state_t *prop_state,
+                   svn_boolean_t *tree_conflicted,
+                   const char *mine,
+                   const char *older,
+                   const char *yours,
+                   svn_revnum_t older_rev,
+                   svn_revnum_t yours_rev,
+                   const char *mimetype1,
+                   const char *mimetype2,
+                   const apr_array_header_t *prop_changes,
+                   apr_hash_t *original_props,
+                   void *baton)
+{
+  struct patch_cmd_baton *patch_b = baton;
+  apr_pool_t *subpool = svn_pool_create(patch_b->pool);
+  svn_boolean_t merge_required = (mimetype2
+                                  && svn_mime_type_is_binary(mimetype2));
+  enum svn_wc_merge_outcome_t merge_outcome;
+
+  /* Easy out:  no access baton means there ain't no merge target */
+  if (adm_access == NULL)
+    {
+      if (content_state)
+        *content_state = svn_wc_notify_state_missing;
+      if (prop_state)
+        *prop_state = svn_wc_notify_state_missing;
+      svn_pool_destroy(subpool);
+      return SVN_NO_ERROR;
+    }
+
+  /* Other easy outs:  if the merge target isn't under version
+     control, or is just missing from disk, fogettaboutit.  There's no
+     way svn_wc_merge3() can do the merge. */
+  {
+    const svn_wc_entry_t *entry;
+    svn_node_kind_t kind;
+
+    SVN_ERR(svn_wc_entry(&entry, mine, adm_access, FALSE, subpool));
+    SVN_ERR(svn_io_check_path(mine, &kind, subpool));
+
+    /* ### a future thought:  if the file is under version control,
+       but the working file is missing, maybe we can 'restore' the
+       working file from the text-base, and then allow the merge to run?  */
+
+    if ((! entry) || (kind != svn_node_file))
+      {
+        if (content_state)
+          *content_state = svn_wc_notify_state_missing;
+        if (prop_state)
+          *prop_state = svn_wc_notify_state_missing;
+        svn_pool_destroy(subpool);
+        return SVN_NO_ERROR;
+      }
+  }
+
+  /* Do property merge before content merge so that keyword expansion takes
+     into account the new property values. */
+  if (prop_changes->nelts > 0)
+    SVN_ERR(merge_props_changed(adm_access, prop_state, tree_conflicted, mine,
+                                prop_changes, original_props, baton));
+  else
+    if (prop_state)
+      *prop_state = svn_wc_notify_state_unchanged;
+
+  /* Now with content modifications */
+  {
+    svn_boolean_t has_local_mods;
+    SVN_ERR(svn_wc_text_modified_p(&has_local_mods, mine, FALSE,
+                                   adm_access, subpool));
+
+    /* Special case:  if a binary file isn't locally modified, and is
+       exactly identical to the file content from the patch, then don't
+       allow svn_wc_merge to produce a conflict.  Instead, just
+       overwrite the working file with the one from the patch. */
+  if (!has_local_mods
+      && (mimetype2 && svn_mime_type_is_binary(mimetype2)))
+    {
+      if (!patch_b->dry_run)
+        SVN_ERR(svn_io_file_rename(yours, mine, subpool));
+      merge_outcome = svn_wc_merge_merged;
+      merge_required = FALSE;
+    }
+
+  /* The binary file has local modifications, we'll use svn_wc_merge
+   * conflict facility to prompt the user and spawn backup files.
+   * Workaround: since svn_wc_merge needs 3 input files, we create an
+   * empty file which we remove when returning from svn_wc_merge. */
+  if (merge_required)
+    {
+      const char *target_label = _(".working");
+      const char *right_label = _(".patch");
+      const char *left_label = _(".empty");
+      const char *left;
+      SVN_ERR(svn_wc_create_tmp_file2
+              (NULL, &left,
+               svn_wc_adm_access_path(adm_access),
+               svn_io_file_del_on_pool_cleanup, subpool));
+      SVN_ERR(svn_wc_merge3(&merge_outcome,
+                            left, yours, mine, adm_access,
+                            left_label, right_label, target_label,
+                            patch_b->dry_run,
+                            NULL, /* no diff3 */
+                            NULL, /* no merge_options */
+                            prop_changes,
+                            patch_b->ctx->conflict_func,
+                            patch_b->ctx->conflict_baton,
+                            subpool));
+      SVN_ERR(svn_io_remove_file
+              (apr_pstrcat(subpool, mine, left_label, NULL),
+               subpool));
+    }
+
+    if (content_state)
+      {
+        if (merge_outcome == svn_wc_merge_conflict)
+          *content_state = svn_wc_notify_state_conflicted;
+        else if (has_local_mods
+                 && merge_outcome != svn_wc_merge_unchanged)
+          *content_state = svn_wc_notify_state_merged;
+        else if (merge_outcome == svn_wc_merge_merged)
+          *content_state = svn_wc_notify_state_changed;
+        else if (merge_outcome == svn_wc_merge_no_merge)
+          *content_state = svn_wc_notify_state_missing;
+        else /* merge_outcome == svn_wc_merge_unchanged */
+          *content_state = svn_wc_notify_state_unchanged;
+      }
+  }
+
+  svn_pool_destroy(subpool);
+  return SVN_NO_ERROR;
+}
+
+/* A svn_wc_diff_callbacks4_t function. */
+static svn_error_t *
+merge_file_added(svn_wc_adm_access_t *adm_access,
+                 svn_wc_notify_state_t *content_state,
+                 svn_wc_notify_state_t *prop_state,
+                 svn_boolean_t *tree_conflicted,
+                 const char *mine,
+                 const char *older,
+                 const char *yours,
+                 svn_revnum_t rev1,
+                 svn_revnum_t rev2,
+                 const char *mimetype1,
+                 const char *mimetype2,
+                 const char *copyfrom_path,
+                 svn_revnum_t copyfrom_rev,
+                 const apr_array_header_t *prop_changes,
+                 apr_hash_t *original_props,
+                 void *baton)
+{
+  struct patch_cmd_baton *patch_b = baton;
+  apr_pool_t *subpool = svn_pool_create(patch_b->pool);
+  svn_node_kind_t kind;
+  int i;
+  apr_hash_t *new_props;
+  const char *path_basename = svn_path_basename(mine, subpool);
+
+  /* This new file can't have any original prop in this offline context. */
+  original_props = apr_hash_make(subpool);
+
+  /* In most cases, we just leave prop_state as unknown, and let the
+     content_state what happened, so we set prop_state here to avoid that
+     below. */
+  if (prop_state)
+    *prop_state = svn_wc_notify_state_unknown;
+
+  /* Apply the prop changes to a new hash table. */
+  new_props = apr_hash_make(subpool);
+  for (i = 0; i < prop_changes->nelts; ++i)
+    {
+      const svn_prop_t *prop = &APR_ARRAY_IDX(prop_changes, i, svn_prop_t);
+      apr_hash_set(new_props, prop->name, APR_HASH_KEY_STRING, prop->value);
+    }
+
+  /* Easy out:  if we have no adm_access for the parent directory,
+     then this portion of the tree-delta "patch" must be inapplicable.
+     Send a 'missing' state back;  the repos-diff editor should then
+     send a 'skip' notification. */
+  if (! adm_access)
+    {
+      if (patch_b->dry_run && patch_b->added_path
+          && svn_path_is_child(patch_b->added_path, mine, subpool))
+        {
+          if (content_state)
+            *content_state = svn_wc_notify_state_changed;
+          if (prop_state && apr_hash_count(new_props))
+            *prop_state = svn_wc_notify_state_changed;
+        }
+      else
+        *content_state = svn_wc_notify_state_missing;
+      svn_pool_destroy(subpool);
+      return SVN_NO_ERROR;
+    }
+
+  SVN_ERR(svn_io_check_path(mine, &kind, subpool));
+  switch (kind)
+    {
+    case svn_node_none:
+      {
+        const svn_wc_entry_t *entry;
+        SVN_ERR(svn_wc_entry(&entry, mine, adm_access, FALSE, subpool));
+        if (entry && entry->schedule != svn_wc_schedule_delete)
+          {
+            /* It's versioned but missing. */
+            if (content_state)
+              *content_state = svn_wc_notify_state_obstructed;
+            svn_pool_destroy(subpool);
+            return SVN_NO_ERROR;
+          }
+
+        if (! patch_b->dry_run)
+          {
+            if (copyfrom_path) /* schedule-add-with-history */
+              {
+                svn_error_t *err;
+                err = svn_wc_copy2(copyfrom_path, adm_access,
+                                   path_basename,
+                                   patch_b->ctx->cancel_func,
+                                   patch_b->ctx->cancel_baton,
+                                   NULL, NULL, /* no notification */
+                                   subpool);
+                if (err)
+                  {
+                    switch (err->apr_err)
+                      {
+                      case SVN_ERR_CANCELLED:
+                        return err; /* may be allocated in subpool */
+
+                      /* XXX: assume the following ENTRY is the source
+                       * path.  How reliable is that? */
+                      case SVN_ERR_ENTRY_NOT_FOUND:
+                      case SVN_ERR_WC_COPYFROM_PATH_NOT_FOUND:
+                        if (content_state)
+                          *content_state = svn_wc_notify_state_source_missing;
+                        break;
+
+                      /* TODO: any other errors?  There are plenty, possibly
+                       * all svn_wc_copy2 callees.. */
+
+                      default:
+                        if (content_state)
+                          *content_state = svn_wc_notify_state_obstructed;
+                      }
+                    svn_error_clear(err);
+                    svn_pool_destroy(subpool);
+                    return SVN_NO_ERROR;
+                  }
+              }
+            else /* schedule-add */
+              {
+                /* Copy the cached empty file and schedule-add it.  The
+                 * contents will come in either via apply-textdelta
+                 * following calls if this is a binary file or with
+                 * unidiff for text files. */
+                SVN_ERR(svn_io_copy_file(yours, mine, TRUE, subpool));
+                SVN_ERR(svn_wc_add3(mine, adm_access, svn_depth_infinity,
+                                    NULL, SVN_IGNORED_REVNUM,
+                                    patch_b->ctx->cancel_func,
+                                    patch_b->ctx->cancel_baton,
+                                    NULL, NULL, /* no notification */
+                                    subpool));
+              }
+
+          }
+
+        /* Now regardless of the schedule-add nature, merge properties. */
+        if (prop_changes->nelts > 0)
+          SVN_ERR(merge_props_changed(adm_access, prop_state, tree_conflicted,
+                                      mine, prop_changes,
+                                      original_props, baton));
+        else
+          if (prop_state)
+            *prop_state = svn_wc_notify_state_unchanged;
+
+        if (content_state)
+          *content_state = svn_wc_notify_state_changed;
+        if (prop_state && apr_hash_count(new_props))
+          *prop_state = svn_wc_notify_state_changed;
+      }
+      break;
+    case svn_node_dir:
+      if (content_state)
+        {
+          /* directory already exists, is it under version control? */
+          const svn_wc_entry_t *entry;
+          SVN_ERR(svn_wc_entry(&entry, mine, adm_access, FALSE, subpool));
+
+          if (entry && dry_run_deleted_p(patch_b, mine))
+            *content_state = svn_wc_notify_state_changed;
+          else
+            /* this will make the repos_editor send a 'skipped' message */
+            *content_state = svn_wc_notify_state_obstructed;
+        }
+      break;
+    case svn_node_file:
+      {
+        /* file already exists, is it under version control? */
+        const svn_wc_entry_t *entry;
+        SVN_ERR(svn_wc_entry(&entry, mine, adm_access, FALSE, subpool));
+
+        /* If it's an unversioned file, don't touch it.  If it's scheduled
+           for deletion, then rm removed it from the working copy and the
+           user must have recreated it, don't touch it */
+        if (!entry || entry->schedule == svn_wc_schedule_delete)
+          {
+            /* this will make the repos_editor send a 'skipped' message */
+            if (content_state)
+              *content_state = svn_wc_notify_state_obstructed;
+          }
+        else
+          {
+            if (dry_run_deleted_p(patch_b, mine))
+              {
+                if (content_state)
+                  *content_state = svn_wc_notify_state_changed;
+              }
+            else
+              {
+                  SVN_ERR(merge_file_changed
+                          (adm_access, content_state,
+                           prop_state, tree_conflicted, mine, NULL, yours,
+                           SVN_IGNORED_REVNUM, SVN_IGNORED_REVNUM,
+                           mimetype1, mimetype2,
+                           prop_changes, original_props,
+                           baton));
+              }
+          }
+        break;
+      }
+    default:
+      if (content_state)
+        *content_state = svn_wc_notify_state_unknown;
+      break;
+    }
+
+  svn_pool_destroy(subpool);
+  return SVN_NO_ERROR;
+}
+
+/* A svn_wc_diff_callbacks4_t function. */
+static svn_error_t *
+merge_file_deleted(svn_wc_adm_access_t *adm_access,
+                   svn_wc_notify_state_t *state,
+                   svn_boolean_t *tree_conflicted,
+                   const char *mine,
+                   const char *older,
+                   const char *yours,
+                   const char *mimetype1,
+                   const char *mimetype2,
+                   apr_hash_t *original_props,
+                   void *baton)
+{
+  struct patch_cmd_baton *patch_b = baton;
+  apr_pool_t *subpool = svn_pool_create(patch_b->pool);
+  svn_node_kind_t kind;
+  svn_wc_adm_access_t *parent_access;
+  const char *parent_path;
+  svn_error_t *err;
+  svn_boolean_t has_local_mods;
+
+  /* Easy out:  if we have no adm_access for the parent directory,
+     then this portion of the tree-delta "patch" must be inapplicable.
+     Send a 'missing' state back;  the repos-diff editor should then
+     send a 'skip' notification. */
+  if (! adm_access)
+    {
+      if (state)
+        *state = svn_wc_notify_state_missing;
+      svn_pool_destroy(subpool);
+      return SVN_NO_ERROR;
+    }
+
+  SVN_ERR(svn_io_check_path(mine, &kind, subpool));
+  switch (kind)
+    {
+    case svn_node_file:
+      svn_path_split(mine, &parent_path, NULL, subpool);
+      SVN_ERR(svn_wc_adm_retrieve(&parent_access, adm_access, parent_path,
+                                  subpool));
+
+      SVN_ERR(svn_wc_text_modified_p(&has_local_mods, mine, TRUE,
+                                     adm_access, subpool));
+      /* Passing NULL for the notify_func and notify_baton because
+         delete_entry() will do it for us. */
+      err = svn_client__wc_delete(mine, parent_access, patch_b->force,
+                                  patch_b->dry_run,
+                                  has_local_mods ? TRUE : FALSE,
+                                  NULL, NULL,
+                                  patch_b->ctx, subpool);
+      if (err && state)
+        {
+          *state = svn_wc_notify_state_obstructed;
+          svn_error_clear(err);
+        }
+      else if (state)
+        {
+          *state = svn_wc_notify_state_changed;
+        }
+      break;
+    case svn_node_dir:
+      if (state)
+        *state = svn_wc_notify_state_obstructed;
+      break;
+    case svn_node_none:
+      /* file is already non-existent, this is a no-op. */
+      if (state)
+        *state = svn_wc_notify_state_missing;
+      break;
+    default:
+      if (state)
+        *state = svn_wc_notify_state_unknown;
+      break;
+    }
+
+  svn_pool_destroy(subpool);
+  return SVN_NO_ERROR;
+}
+
+/* A svn_wc_diff_callbacks4_t function. */
+static svn_error_t *
+merge_dir_added(svn_wc_adm_access_t *adm_access,
+                svn_wc_notify_state_t *state,
+                svn_boolean_t *tree_conflicted,
+                const char *path,
+                svn_revnum_t rev,
+                const char *copyfrom_path,
+                svn_revnum_t copyfrom_rev,
+                void *baton)
+{
+  struct patch_cmd_baton *patch_b = baton;
+  apr_pool_t *subpool = svn_pool_create(patch_b->pool);
+  svn_node_kind_t kind;
+  const svn_wc_entry_t *entry;
+  const char *copyfrom_url, *child;
+
+  /* Easy out:  if we have no adm_access for the parent directory,
+     then this portion of the tree-delta "patch" must be inapplicable.
+     Send a 'missing' state back;  the repos-diff editor should then
+     send a 'skip' notification. */
+  if (! adm_access)
+    {
+      if (state)
+        {
+          if (patch_b->dry_run && patch_b->added_path
+              && svn_path_is_child(patch_b->added_path, path, subpool))
+            *state = svn_wc_notify_state_changed;
+          else
+            *state = svn_wc_notify_state_missing;
+        }
+      svn_pool_destroy(subpool);
+      return SVN_NO_ERROR;
+    }
+
+  child = svn_path_is_child(patch_b->target, path, subpool);
+  assert(child != NULL);
+
+  SVN_ERR(svn_io_check_path(path, &kind, subpool));
+  switch (kind)
+    {
+    case svn_node_none:
+      SVN_ERR(svn_wc_entry(&entry, path, adm_access, FALSE, subpool));
+      if (entry && entry->schedule != svn_wc_schedule_delete)
+        {
+          /* Versioned but missing */
+          if (state)
+            *state = svn_wc_notify_state_obstructed;
+          svn_pool_destroy(subpool);
+          return SVN_NO_ERROR;
+        }
+      if (! patch_b->dry_run)
+        {
+          SVN_ERR(svn_io_make_dir_recursively(path, subpool));
+          SVN_ERR(svn_wc_add3(path, adm_access, svn_depth_infinity,
+                              NULL, SVN_IGNORED_REVNUM,
+                              patch_b->ctx->cancel_func,
+                              patch_b->ctx->cancel_baton,
+                              NULL, NULL, /* don't pass notification func! */
+                              subpool));
+
+        }
+      if (patch_b->dry_run)
+        patch_b->added_path = apr_pstrdup(patch_b->pool, path);
+      if (state)
+        *state = svn_wc_notify_state_changed;
+      break;
+    case svn_node_dir:
+      /* Adding an unversioned directory doesn't destroy data */
+      SVN_ERR(svn_wc_entry(&entry, path, adm_access, TRUE, subpool));
+      if (! entry || entry->schedule == svn_wc_schedule_delete)
+        {
+          if (!patch_b->dry_run)
+            SVN_ERR(svn_wc_add3(path, adm_access, svn_depth_infinity,
+                                copyfrom_url, rev,
+                                patch_b->ctx->cancel_func,
+                                patch_b->ctx->cancel_baton,
+                                NULL, NULL, /* no notification func! */
+                                subpool));
+          if (patch_b->dry_run)
+            patch_b->added_path = apr_pstrdup(patch_b->pool, path);
+          if (state)
+            *state = svn_wc_notify_state_changed;
+        }
+      else if (state)
+        {
+          if (dry_run_deleted_p(patch_b, path))
+            *state = svn_wc_notify_state_changed;
+          else
+            *state = svn_wc_notify_state_obstructed;
+        }
+      break;
+    case svn_node_file:
+      if (patch_b->dry_run)
+        patch_b->added_path = NULL;
+
+      if (state)
+        {
+          SVN_ERR(svn_wc_entry(&entry, path, adm_access, FALSE, subpool));
+
+          if (entry && dry_run_deleted_p(patch_b, path))
+            /* ### TODO: Retain record of this dir being added to
+               ### avoid problems from subsequent edits which try to
+               ### add children. */
+            *state = svn_wc_notify_state_changed;
+          else
+            *state = svn_wc_notify_state_obstructed;
+        }
+      break;
+    default:
+      if (patch_b->dry_run)
+        patch_b->added_path = NULL;
+      if (state)
+        *state = svn_wc_notify_state_unknown;
+      break;
+    }
+
+  svn_pool_destroy(subpool);
+  return SVN_NO_ERROR;
+}
+
+/* Struct used for as the baton for calling merge_delete_notify_func(). */
+typedef struct merge_delete_notify_baton_t
+{
+  svn_client_ctx_t *ctx;
+
+  /* path to skip */
+  const char *path_skip;
+} merge_delete_notify_baton_t;
+
+/* Notify callback function that wraps the normal callback
+ * function to remove a notification that will be sent twice
+ * and set the proper action. */
+static void
+merge_delete_notify_func(void *baton,
+                         const svn_wc_notify_t *notify,
+                         apr_pool_t *pool)
+{
+  merge_delete_notify_baton_t *mdb = baton;
+  svn_wc_notify_t *new_notify;
+
+  /* Skip the notification for the path we called svn_client__wc_delete() with,
+   * because it will be outputed by repos_diff.c:delete_item */
+  if (strcmp(notify->path, mdb->path_skip) == 0)
+    return;
+
+  /* svn_client__wc_delete() is written primarily for scheduling operations not
+   * update operations.  Since merges are update operations we need to alter
+   * the delete notification to show as an update not a schedule so alter
+   * the action. */
+  if (notify->action == svn_wc_notify_delete)
+    {
+      /* We need to copy it since notify is const. */
+      new_notify = svn_wc_dup_notify(notify, pool);
+      new_notify->action = svn_wc_notify_update_delete;
+      notify = new_notify;
+    }
+
+  if (mdb->ctx->notify_func2)
+    (*mdb->ctx->notify_func2)(mdb->ctx->notify_baton2, notify, pool);
+}
+
+/* A svn_wc_diff_callbacks4_t function. */
+static svn_error_t *
+merge_dir_deleted(svn_wc_adm_access_t *adm_access,
+                  svn_wc_notify_state_t *state,
+                  svn_boolean_t *tree_conflicted,
+                  const char *path,
+                  void *baton)
+{
+  struct patch_cmd_baton *patch_b = baton;
+  apr_pool_t *subpool = svn_pool_create(patch_b->pool);
+  svn_node_kind_t kind;
+  svn_wc_adm_access_t *parent_access;
+  const char *parent_path;
+  svn_error_t *err;
+
+  /* Easy out:  if we have no adm_access for the parent directory,
+     then this portion of the tree-delta "patch" must be inapplicable.
+     Send a 'missing' state back;  the repos-diff editor should then
+     send a 'skip' notification. */
+  if (! adm_access)
+    {
+      if (state)
+        *state = svn_wc_notify_state_missing;
+      svn_pool_destroy(subpool);
+      return SVN_NO_ERROR;
+    }
+
+  SVN_ERR(svn_io_check_path(path, &kind, subpool));
+  switch (kind)
+    {
+    case svn_node_dir:
+      {
+        merge_delete_notify_baton_t mdb;
+
+        mdb.ctx = patch_b->ctx;
+        mdb.path_skip = path;
+
+        svn_path_split(path, &parent_path, NULL, subpool);
+        SVN_ERR(svn_wc_adm_retrieve(&parent_access, adm_access, parent_path,
+                                    subpool));
+        err = svn_client__wc_delete(path, parent_access, patch_b->force,
+                                    patch_b->dry_run, FALSE,
+                                    merge_delete_notify_func, &mdb,
+                                    patch_b->ctx, subpool);
+        if (err && state)
+          {
+            *state = svn_wc_notify_state_obstructed;
+            svn_error_clear(err);
+          }
+        else if (state)
+          {
+            *state = svn_wc_notify_state_changed;
+          }
+      }
+      break;
+    case svn_node_file:
+      if (state)
+        *state = svn_wc_notify_state_obstructed;
+      break;
+    case svn_node_none:
+      /* dir is already non-existent, this is a no-op. */
+      if (state)
+        *state = svn_wc_notify_state_missing;
+      break;
+    default:
+      if (state)
+        *state = svn_wc_notify_state_unknown;
+      break;
+    }
+
+  svn_pool_destroy(subpool);
+  return SVN_NO_ERROR;
+}
+
+/* The main callback table for 'svn patch'.  We leave merge callback
+ * names as (a) they are pretty much merge operations (b) even if
+ * tweaked them to meet 'svn patch' needs, they do pretty much what
+ * their real sibblings do. */
+static const svn_wc_diff_callbacks4_t
+patch_callbacks =
+  {
+    merge_file_changed,
+    merge_file_added,
+    merge_file_deleted,
+    merge_dir_added,
+    merge_dir_deleted,
+    merge_props_changed
+  };
+
+struct edit_baton {
+  /* Directory against which 'svn patch' is run. */
+  const char *target;
+
+  /* ADM_ACCESS is an access b
Received on 2009-03-08 04:55:35 CET

This is an archived mail posted to the Subversion Dev mailing list.

This site is subject to the Apache Privacy Policy and the Apache Public Forum Archive Policy.