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

diff against the repository

From: Philip Martin <pmartin_at_uklinux.net>
Date: 2001-11-02 19:40:59 CET

Hi

Some days ago I posted a patch that implemented 'svn diff' output for
added, but not committed files. This patch used an empty file in the
adm directory [which has problems but more of that later] but it
needed to repeatedly retrieve the directory entries to determine which
files were added. Kevin suggested that an editor/crawler based
approach to diff would avoid this problem, Karl also chipped in.

Well, I've been trying things out, and some of it works!. Here is a
scenario

  $ svn st -uv include/svn_hash.h
  M * 365 include/svn_hash.h
  Head revision: 378

showing I've modified the file thus

  $ svn diff include/svn_hash.h
  Index: include/svn_hash.h
  ===================================================================
  --- include/.svn/text-base/svn_hash.h Fri Nov 2 16:54:37 2001
  +++ include/svn_hash.h Fri Nov 2 16:55:21 2001
  @@ -1,3 +1,4 @@
  +some arbitrary change
   /*
    * svn_hash.h : dumping and reading hash tables to/from files.
    *

The file is also out of date wrt the repository. My new code allows me
to do this

  $ svn rdiff -r378 include/svn_hash.h
  Index: include/svn_hash.h
  ===================================================================
  --- include/svn_hash.h Fri Nov 2 16:55:21 2001
  +++ include/.svn/tmp/text-base/svn_hash.h Fri Nov 2 17:08:14 2001
  @@ -1,4 +1,3 @@
  -some arbitrary change
   /*
    * svn_hash.h : dumping and reading hash tables to/from files.
    *
  @@ -87,6 +86,9 @@
    */
   void *svn_pack_bytestring (size_t len, const char *val, apr_pool_t *pool);
   
  +#ifdef __cplusplus
  +}
  +#endif /* __cplusplus */
   
   #endif /* SVN_HASH_H */
   
which shows the diffs between the working copy and the repository
version. Ha ha! It works!

The code uses an svn_delta_edit_fns_t editor (like the update command)
and svn_wc_crawl_revisions to retrieve the differences between the
working copy and the requested repository version. Rather than
updating the working copy, the new editor creates temporary files that
duplicate the repository versions. When the crawler closes the files
the editor calls back to a client layer function to display the
difference between the working copy and the temporary file. When the
crawler closes the directory, the editor then calls back to the client
layer to diff any remaining files that have only been modified
locally. This last bit is the optimisation that initiated the whole
editor/crawler approach.

This basically works, the patch in this message was produced using my
new code. However, testing it revealed a problem related to my earlier
'empty file in the adm area' patch, the one I mentioned at the start
of this message. What happens is that the external diff gets invoked
with the empty file and the temporary file, so although the file
changes are correctly represented in the output, the file *paths* are
not. This makes it difficult to use the output as an input to
patch. The new code also uses the empty file, but it also provides an
additional label option to diff, something my earlier patch failed to
do.

The following patch is "incorect, incomplet and inCONsisteNt", there
are bits that aren't finished, and cases it doesn't handle. It's here
to find out what you think of the approach, and what I would need to
do to get a patch accepted. It's currently implemented as a new
'rdiff' command because it always makes a repository connection even
when the requested revision is the same as the working copy. Thus diff
remains a 'local only' diff.

Things I know about: need to consider multiple pools, need to track
new directories to handle the addition of files in new directories,
need to pass command line options through the diff command baton, need
to consider error handling, need to considered locking of the working
copy, need some tests.

Philip

Index: svn/subversion/include/.svn/text-base/svn_wc.h
===================================================================
--- svn/subversion/include/.svn/text-base/svn_wc.h Wed Oct 31 19:28:59 2001
+++ svn/subversion/include/svn_wc.h Fri Nov 2 17:44:53 2001
@@ -550,7 +550,23 @@
                                        void **edit_baton,
                                        apr_pool_t *pool);
 
-
+typedef svn_error_t *(*svn_wc_diff_cmd)(svn_stringbuf_t *path1,
+ svn_stringbuf_t *path2,
+ svn_stringbuf_t *label,
+ void *baton,
+ apr_pool_t *pool);
+/*
+ * Return an editor for diffing a working copy against the repository.
+ *
+ */
+svn_error_t *svn_wc_get_diff_editor (svn_stringbuf_t *anchor,
+ svn_stringbuf_t *target,
+ svn_wc_diff_cmd diff_cmd,
+ void *diff_cmd_baton,
+ svn_boolean_t recurse,
+ const svn_delta_edit_fns_t **editor,
+ void **edit_baton,
+ apr_pool_t *pool);
 /*
  * Set *WC_ROOT to TRUE if PATH represents a "working copy root",
  * FALSE otherwise. Use POOL for any intermediate allocations.
Index: svn/subversion/include/.svn/text-base/svn_client.h
===================================================================
--- svn/subversion/include/.svn/text-base/svn_client.h Wed Oct 31 19:28:58 2001
+++ svn/subversion/include/svn_client.h Fri Nov 2 14:17:08 2001
@@ -396,6 +396,17 @@
                       svn_stringbuf_t **pristine_copy_path,
                       apr_pool_t *pool);
 
+/* Given a path in the working copy, compare the elements against the given
+ revision in the repository.
+
+ TODO: Pass in time as well as revision.
+*/
+svn_error_t *svn_client_diff (svn_stringbuf_t *path,
+ svn_client_auth_baton_t *auth_baton,
+ svn_revnum_t revision,
+ svn_boolean_t recurse,
+ apr_pool_t *pool);
+
 
 /* Recursively cleanup a working copy directory DIR, finishing any
    incomplete operations, removing lockfiles, etc. */
Index: svn/subversion/libsvn_wc/.svn/empty-file
===================================================================
--- svn/subversion/libsvn_wc/.svn/empty-file Wed Oct 31 19:31:45 2001
+++ svn/subversion/libsvn_wc/diff.c Fri Nov 2 17:49:05 2001
@@ -0,0 +1,458 @@
+/*
+ * diff.c -- The diff editor for comparing the working copy against the
+ * repository. This implemtation is based on the update editor
+ * svn_wc_get_update_editor.
+ *
+ * ====================================================================
+ * Copyright (c) 2001 Philip Martin. 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/.
+ * ====================================================================
+ */
+
+#include <apr_hash.h>
+#include "wc.h"
+#include "svn_pools.h"
+
+#define printf(...)
+
+struct edit_baton {
+ svn_stringbuf_t *anchor;
+ svn_wc_diff_cmd diff_cmd;
+ void *diff_cmd_baton;
+ svn_boolean_t recurse;
+ apr_pool_t *pool;
+};
+
+struct dir_baton {
+ svn_stringbuf_t *path;
+ apr_hash_t *entries;
+ struct dir_baton *dir_baton;
+ struct edit_baton *edit_baton;
+};
+
+struct file_baton {
+ svn_boolean_t added;
+ apr_file_t *original_file;
+ apr_file_t *temp_file;
+ svn_txdelta_window_handler_t apply_handler;
+ void *apply_baton;
+ svn_stringbuf_t *path;
+ struct dir_baton *dir_baton;
+ struct edit_baton *edit_baton;
+};
+
+static struct dir_baton*
+make_dir_baton (svn_stringbuf_t *name,
+ struct dir_baton *parent_baton,
+ struct edit_baton *edit_baton,
+ apr_pool_t *pool)
+{
+ struct dir_baton *dir_baton = apr_pcalloc (pool, sizeof (*dir_baton));
+
+ dir_baton->dir_baton = parent_baton;
+ dir_baton->edit_baton = edit_baton;
+
+ dir_baton->entries = apr_hash_make (dir_baton->edit_baton->pool);
+
+ if (parent_baton)
+ dir_baton->path = svn_stringbuf_dup (parent_baton->path, pool);
+ else
+ dir_baton->path = svn_stringbuf_dup (edit_baton->anchor, pool);
+
+ if (name)
+ svn_path_add_component (dir_baton->path, name, svn_path_local_style);
+
+ return dir_baton;
+}
+
+static struct file_baton*
+make_file_baton (svn_stringbuf_t *name,
+ struct dir_baton *parent_baton)
+{
+ struct file_baton *file_baton = apr_pcalloc (parent_baton->edit_baton->pool,
+ sizeof (*file_baton));
+
+ file_baton->dir_baton = parent_baton;
+ file_baton->edit_baton = parent_baton->edit_baton;
+
+ file_baton->path = svn_stringbuf_dup (parent_baton->path,
+ parent_baton->edit_baton->pool);
+ svn_path_add_component (file_baton->path, name, svn_path_local_style);
+
+ return file_baton;
+}
+
+static svn_error_t *
+directory_elements_diff (struct dir_baton *dir_baton)
+{
+ apr_hash_t *entries;
+ apr_hash_index_t *hi;
+ svn_boolean_t modified;
+ svn_wc_diff_cmd diff_cmd = dir_baton->edit_baton->diff_cmd;
+ svn_stringbuf_t *pristine_copy;
+ svn_stringbuf_t *name;
+ struct dir_baton *subdir_baton;
+
+ SVN_ERR (svn_wc_entries_read (&entries, dir_baton->path,
+ dir_baton->edit_baton->pool));
+
+ for (hi = apr_hash_first (dir_baton->edit_baton->pool, entries); hi;
+ hi = apr_hash_next (hi))
+ {
+ svn_wc_entry_t *entry;
+ const char *key;
+ svn_stringbuf_t *path;
+
+ apr_hash_this(hi, (const void **)&key, NULL, (void **)&entry);
+
+ /* skip entry for the directory itself */
+ if (strcmp (key, SVN_WC_ENTRY_THIS_DIR) == 0)
+ continue;
+
+ path = svn_stringbuf_dup (dir_baton->path, dir_baton->edit_baton->pool);
+ svn_path_add_component_nts (path, key, svn_path_local_style);
+
+ /* skip entry if it is in the list of entries already diff'd */
+ if (apr_hash_get (dir_baton->entries, path->data, path->len))
+ continue;
+
+ switch (entry->kind)
+ {
+ case svn_node_file:
+ SVN_ERR (svn_wc_text_modified_p (&modified, path,
+ dir_baton->edit_baton->pool));
+ if (modified)
+ {
+ if (entry->schedule == svn_wc_schedule_add)
+ pristine_copy =
+ svn_wc__empty_file_path (path, dir_baton->edit_baton->pool);
+ else
+ pristine_copy =
+ svn_wc__text_base_path (path, FALSE,
+ dir_baton->edit_baton->pool);
+
+ SVN_ERR (diff_cmd (pristine_copy, path, NULL,
+ dir_baton->edit_baton->diff_cmd_baton,
+ dir_baton->edit_baton->pool));
+ }
+ break;
+ case svn_node_dir:
+ svn_path_split (path, NULL, &name, svn_path_local_style,
+ dir_baton->edit_baton->pool);
+ subdir_baton = make_dir_baton (name, dir_baton,
+ dir_baton->edit_baton,
+ dir_baton->edit_baton->pool);
+
+ SVN_ERR (directory_elements_diff (subdir_baton));
+ break;
+ default:
+ break;
+ }
+ }
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+set_target_revision (void *edit_baton,
+ svn_revnum_t target_revision)
+{
+ printf ("set_target_revision: %d\n", target_revision);
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+replace_root (void *edit_baton,
+ svn_revnum_t base_revision,
+ void **root_baton)
+{
+ struct edit_baton *eb = edit_baton;
+ struct dir_baton *b;
+
+ b = make_dir_baton (NULL, NULL, eb, eb->pool);
+ *root_baton = b;
+
+ printf ("replace_root: %s %d\n", b->path->data, base_revision);
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+delete_entry (svn_stringbuf_t *name,
+ void *parent_baton)
+{
+ printf ("delete_entry: %s\n", name->data);
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+add_directory (svn_stringbuf_t *name,
+ void *parent_baton,
+ svn_stringbuf_t *copyfrom_path,
+ svn_revnum_t copyfrom_revision,
+ void **child_baton)
+{
+ struct dir_baton *pb = parent_baton;
+ struct dir_baton *b;
+
+ b = make_dir_baton (name, pb, pb->edit_baton, pb->edit_baton->pool);
+ *child_baton = b;
+
+ printf ("add_directory: %s %s %d\n",
+ b->path->data,
+ (copyfrom_path ? copyfrom_path->data : ""), copyfrom_revision);
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+replace_directory (svn_stringbuf_t *name,
+ void *parent_baton,
+ svn_revnum_t base_revision,
+ void **child_baton)
+{
+ struct dir_baton *pb = parent_baton;
+ struct dir_baton *b;
+
+ b = make_dir_baton (name, pb, pb->edit_baton, pb->edit_baton->pool);
+ *child_baton = b;
+
+ printf ("replace_directory: %s %d\n", b->path->data, base_revision);
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+change_dir_prop (void *dir_baton,
+ svn_stringbuf_t *name,
+ svn_stringbuf_t *value)
+{
+ printf ("change_dir_prop: %s %s\n", name->data, value->data);
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+close_directory (void *dir_baton)
+{
+ struct dir_baton *b = dir_baton;
+
+ SVN_ERR (directory_elements_diff (dir_baton));
+
+ if (b->dir_baton)
+ apr_hash_set (b->dir_baton->entries, b->path->data, b->path->len,
+ (void*)TRUE);
+
+ printf ("close_directory:\n");
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+add_file (svn_stringbuf_t *name,
+ void *parent_baton,
+ svn_stringbuf_t *copyfrom_path,
+ svn_revnum_t copyfrom_revision,
+ void **file_baton)
+{
+ struct dir_baton *pb = parent_baton;
+ struct file_baton *b;
+ svn_stringbuf_t *temp_loc;
+
+ b = make_file_baton (name, pb);
+ *file_baton = b;
+ b->added = TRUE;
+
+ SVN_ERR (svn_wc__open_empty_file (&b->original_file, b->path,
+ b->edit_baton->pool));
+
+ /* This will be the new version in the temporary directory */
+ SVN_ERR (svn_wc__open_text_base (&b->temp_file, b->path,
+ (APR_WRITE | APR_TRUNCATE | APR_CREATE),
+ b->edit_baton->pool));
+
+ svn_txdelta_apply (svn_stream_from_aprfile (b->original_file,
+ b->edit_baton->pool),
+ svn_stream_from_aprfile (b->temp_file,
+ b->edit_baton->pool),
+ b->edit_baton->pool,
+ &b->apply_handler, &b->apply_baton);
+
+ printf ("add_file: %s %s %d\n", b->path->data,
+ (copyfrom_path ? copyfrom_path->data : ""), copyfrom_revision);
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+replace_file (svn_stringbuf_t *name,
+ void *parent_baton,
+ svn_revnum_t base_revision,
+ void **file_baton)
+{
+ struct dir_baton *pb = parent_baton;
+ struct file_baton *b;
+
+ b = make_file_baton (name, pb);
+ *file_baton = b;
+ b->added = FALSE;
+
+ /* The current text-base is the starting point for the new version */
+ SVN_ERR (svn_wc__open_text_base (&b->original_file, b->path,
+ APR_READ, b->edit_baton->pool));
+
+ /* This will be the new version in the temporary directory */
+ SVN_ERR (svn_wc__open_text_base (&b->temp_file, b->path,
+ (APR_WRITE | APR_TRUNCATE | APR_CREATE),
+ b->edit_baton->pool));
+
+ svn_txdelta_apply (svn_stream_from_aprfile (b->original_file,
+ b->edit_baton->pool),
+ svn_stream_from_aprfile (b->temp_file,
+ b->edit_baton->pool),
+ b->edit_baton->pool,
+ &b->apply_handler, &b->apply_baton);
+
+ printf ("replace_file: %s %d\n", b->path->data, base_revision);
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+window_handler (svn_txdelta_window_t *window,
+ void *window_baton)
+{
+ struct file_baton *b = window_baton;
+
+ SVN_ERR (b->apply_handler (window, b->apply_baton));
+
+ printf ("window_handler:\n");
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+apply_textdelta (void *file_baton,
+ svn_txdelta_window_handler_t *handler,
+ void **handler_baton)
+{
+ printf ("apply_textdelta:\n");
+ *handler = window_handler;
+ *handler_baton = file_baton;
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+change_file_prop (void *file_baton,
+ svn_stringbuf_t *name,
+ svn_stringbuf_t *value)
+{
+ printf ("change_file_prop: %s %s\n", name->data, value->data);
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+close_file (void *file_baton)
+{
+ struct file_baton *b = file_baton;
+ svn_wc_diff_cmd diff_cmd = b->edit_baton->diff_cmd;
+ svn_stringbuf_t *temp_file_path =
+ svn_wc__text_base_path (b->path, TRUE, b->edit_baton->pool);
+
+ SVN_ERR (svn_wc__close_text_base (b->original_file, b->path, 0,
+ b->edit_baton->pool));
+ SVN_ERR (svn_wc__close_text_base (b->temp_file, b->path, 0,
+ b->edit_baton->pool));
+
+ if (b->added)
+ {
+ /* TODO: need an empty file at the correct place in the hierarchy to
+ get an correct diff. b->path does not exist in the working
+ copy. */
+ SVN_ERR (diff_cmd (svn_wc__empty_file_path (b->path, b->edit_baton->pool),
+ temp_file_path, b->path,
+ b->edit_baton->diff_cmd_baton,
+ b->edit_baton->pool));
+
+ }
+ else
+ {
+ SVN_ERR (diff_cmd (b->path, temp_file_path, NULL,
+ b->edit_baton->diff_cmd_baton,
+ b->edit_baton->pool));
+ }
+
+ apr_hash_set (b->dir_baton->entries, b->path->data, b->path->len,
+ (void*)TRUE);
+
+ apr_file_remove (temp_file_path->data, b->edit_baton->pool);
+
+ printf ("close_file:\n");
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+close_edit (void *edit_baton)
+{
+ printf ("close_edit:\n");
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+abort_edit (void *edit_baton)
+{
+ printf ("abort_edit:\n");
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_wc_get_diff_editor (svn_stringbuf_t *anchor,
+ svn_stringbuf_t *target,
+ svn_wc_diff_cmd diff_cmd,
+ void *diff_cmd_baton,
+ svn_boolean_t recurse,
+ const svn_delta_edit_fns_t **editor,
+ void **edit_baton,
+ apr_pool_t *pool)
+{
+ apr_pool_t *subpool = svn_pool_create (pool);
+ svn_delta_edit_fns_t *tree_editor = svn_delta_default_editor (pool);
+ struct edit_baton *eb;
+
+ eb = apr_palloc (subpool, sizeof (*eb));
+ eb->anchor = anchor;
+ eb->diff_cmd = diff_cmd;
+ eb->diff_cmd_baton = diff_cmd_baton;
+ eb->recurse = recurse;
+ eb->pool = subpool;
+
+ /* I know I don't need to replace all the functions, but I wanted to see
+ when they were called :-) */
+ tree_editor->set_target_revision = set_target_revision;
+ tree_editor->replace_root = replace_root;
+ tree_editor->delete_entry = delete_entry;
+ tree_editor->add_directory = add_directory;
+ tree_editor->replace_directory = replace_directory;
+ tree_editor->change_dir_prop = change_dir_prop;
+ tree_editor->close_directory = close_directory;
+ tree_editor->add_file = add_file;
+ tree_editor->replace_file = replace_file;
+ tree_editor->apply_textdelta = apply_textdelta;
+ tree_editor->change_file_prop = change_file_prop;
+ tree_editor->close_file = close_file;
+ tree_editor->close_edit = close_edit;
+ tree_editor->abort_edit = abort_edit;
+
+ *edit_baton = eb;
+ *editor = tree_editor;
+
+ return SVN_NO_ERROR;
+}
+
+
+/* ----------------------------------------------------------------
+ * local variables:
+ * eval: (load-file "../svn-dev.el")
+ * end:
+ */
Index: svn/subversion/libsvn_wc/.svn/text-base/wc.h
===================================================================
--- svn/subversion/libsvn_wc/.svn/text-base/wc.h Wed Oct 31 19:31:48 2001
+++ svn/subversion/libsvn_wc/wc.h Fri Nov 2 12:30:48 2001
@@ -100,6 +100,7 @@
 #define SVN_WC__ADM_LOG "log"
 #define SVN_WC__ADM_KILLME "KILLME"
 #define SVN_WC__ADM_AUTH_DIR "auth"
+#define SVN_WC__ADM_EMPTY_FILE "empty-file"
 
 
 /* The basename of the ".prej" file, if a directory ever has property
@@ -110,6 +111,10 @@
 /* Return a string containing the admin subdir name. */
 svn_stringbuf_t *svn_wc__adm_subdir (apr_pool_t *pool);
 
+/* Return a string containing the admin empty file. */
+svn_stringbuf_t *svn_wc__empty_file_path (const svn_stringbuf_t *path,
+ apr_pool_t *pool);
+
 
 /* Return a path to something in PATH's administrative area.
  * Return path to the thing in the tmp area if TMP is non-zero.
@@ -182,6 +187,18 @@
 svn_error_t *svn_wc__remove_adm_file (svn_stringbuf_t *path,
                                       apr_pool_t *pool,
                                       ...);
+
+/* Open *readonly* the empty file in the in adm area of PATH */
+svn_error_t *svn_wc__open_empty_file (apr_file_t **handle,
+ svn_stringbuf_t *path,
+ apr_pool_t *pool);
+
+/* Close the empty file in the adm area of PATH
+ * FP was obtain from svn_wc__open_empty_file().
+ */
+svn_error_t *svn_wc__close_empty_file (apr_file_t *fp,
+ svn_stringbuf_t *path,
+ apr_pool_t *pool);
 
 /* Open the text-base for FILE.
  * FILE can be any kind of path ending with a filename.
Index: svn/subversion/libsvn_wc/.svn/text-base/adm_files.c
===================================================================
--- svn/subversion/libsvn_wc/.svn/text-base/adm_files.c Wed Oct 31 19:31:52 2001
+++ svn/subversion/libsvn_wc/adm_files.c Fri Nov 2 12:30:29 2001
@@ -376,6 +376,16 @@
   return thing_path (path, SVN_WC__ADM_TEXT_BASE, tmp, pool);
 }
 
+svn_stringbuf_t *
+svn_wc__empty_file_path (const svn_stringbuf_t *path,
+ apr_pool_t *pool)
+{
+ svn_stringbuf_t *empty_file_path = svn_stringbuf_dup (path, pool);
+ svn_path_remove_component (empty_file_path, svn_path_local_style);
+ extend_with_adm_name (empty_file_path, 0, pool, SVN_WC__ADM_EMPTY_FILE, NULL);
+ return empty_file_path;
+}
+
 
 static svn_error_t *
 prop_path_internal (svn_stringbuf_t **prop_path,
@@ -705,6 +715,27 @@
   return close_adm_file (fp, (svn_stringbuf_t *) path, sync, pool, fname, NULL);
 }
 
+svn_error_t *
+svn_wc__open_empty_file (apr_file_t **handle,
+ svn_stringbuf_t *path,
+ apr_pool_t *pool)
+{
+ svn_stringbuf_t *newpath, *basename;
+ svn_path_split (path, &newpath, &basename, svn_path_local_style, pool);
+ return open_adm_file (handle, newpath, APR_READ, pool,
+ SVN_WC__ADM_EMPTY_FILE, NULL);
+}
+
+svn_error_t *
+svn_wc__close_empty_file (apr_file_t *fp,
+ svn_stringbuf_t *path,
+ apr_pool_t *pool)
+{
+ svn_stringbuf_t *newpath, *basename;
+ svn_path_split (path, &newpath, &basename, svn_path_local_style, pool);
+ return close_adm_file (fp, newpath, 0, pool,
+ SVN_WC__ADM_EMPTY_FILE, NULL);
+}
 
 svn_error_t *
 svn_wc__open_text_base (apr_file_t **handle,
@@ -1180,6 +1211,10 @@
 
   /* SVN_WC__ADM_ENTRIES */
   SVN_ERR (svn_wc__entries_init (path, ancestor_path, pool));
+
+ /* SVN_WC__ADM_EMPTY_FILE */
+ SVN_ERR (svn_wc__make_adm_thing (path, SVN_WC__ADM_EMPTY_FILE, svn_node_file,
+ APR_UREAD, 0, pool));
 
   /* THIS FILE MUST BE CREATED LAST:
      After this exists, the dir is considered complete. */
Index: svn/subversion/libsvn_client/.svn/text-base/diff.c
===================================================================
--- svn/subversion/libsvn_client/.svn/text-base/diff.c Wed Oct 31 19:31:31 2001
+++ svn/subversion/libsvn_client/diff.c Fri Nov 2 17:47:56 2001
@@ -1,5 +1,5 @@
 /*
- * diff.c: return two temporary file paths that can be diffed
+ * diff.c: Compare working copy with text-base or repository.
  *
  * ====================================================================
  * Copyright (c) 2000-2001 CollabNet. All rights reserved.
@@ -33,7 +33,72 @@
 #include "svn_path.h"
 #include "svn_test.h"
 #include "svn_io.h"
+#include "svn_pools.h"
+#include "client.h"
+#include "svn_private_config.h" /* for SVN_CLIENT_DIFF */
+#include <assert.h>
+
+static svn_error_t *
+svn_client_diff_cmd (svn_stringbuf_t *path1,
+ svn_stringbuf_t *path2,
+ svn_stringbuf_t *label,
+ void *baton,
+ apr_pool_t *pool)
+{
+ apr_status_t status;
+ const char **args;
+ int i = 0;
+ int nargs = 5;
+ apr_file_t *outhandle = NULL;
+
+ /* Get an apr_file_t representing stdout, which is where we'll have
+ the diff program print to. */
+ status = apr_file_open_stdout (&outhandle, pool);
+ if (status)
+ return svn_error_create (status, 0, NULL, pool,
+ "error: can't open handle to stdout");
+
+ /* Execute local diff command on these two paths, print to stdout. */
+
+ if (label)
+ nargs += 2;
+ args = apr_palloc(pool, nargs*sizeof(char*));
+
+ args[i++] = SVN_CLIENT_DIFF; /* the autoconfiscated system diff program */
+ args[i++] = "-u";
+ if (label)
+ {
+ args[i++] = "-L";
+ args[i++] = label->data;
+ }
+ args[i++] = path1->data;
+ args[i++] = path2->data;
+ args[i++] = NULL;
+ assert (i==nargs);
+
+ /* todo: This printf is NOT "my final answer" -- placeholder for
+ real work to be done. */
+ apr_file_printf (outhandle, "Index: %s\n", path1->data);
+ apr_file_printf (outhandle, "===================================================================\n");
+
+ SVN_ERR(svn_io_run_cmd (".", SVN_CLIENT_DIFF, args, NULL, NULL,
+ NULL, outhandle, NULL, pool));
+
+ /* TODO: Handle exit code == 2 (i.e. errors with diff) here */
+
+ /* TODO: someday we'll need to worry about two things here:
 
+ 1. svn_client_file_diff may be returning a file from RA instead
+ of the WC's text-base. If this is so, it will need to provide a
+ "clean up" routine to remove the temporary file created by RA.
+
+ 2. we're going to need to write a diff plug-in mechanism that
+ makes use of the two paths, instead of just blindly running
+ SVN_CLIENT_DIFF.
+ */
+
+ return SVN_NO_ERROR;
+}
 
 
 
@@ -60,9 +125,59 @@
 }
 
 
+svn_error_t *
+svn_client_diff (svn_stringbuf_t *path,
+ svn_client_auth_baton_t *auth_baton,
+ svn_revnum_t revision,
+ svn_boolean_t recurse,
+ apr_pool_t *pool)
+{
+ svn_stringbuf_t *anchor, *target;
+ svn_wc_entry_t *entry;
+ svn_stringbuf_t *URL;
+ void *ra_baton, *session, *cb_baton;
+ svn_ra_plugin_t *ra_lib;
+ svn_ra_callbacks_t *ra_callbacks;
+ const svn_ra_reporter_t *reporter;
+ void *report_baton;
+ const svn_delta_edit_fns_t *diff_editor;
+ void *diff_edit_baton;
+
+ SVN_ERR (svn_wc_get_actual_target (path, &anchor, &target, pool));
+ SVN_ERR (svn_wc_entry (&entry, anchor, pool));
+ URL = svn_stringbuf_create (entry->ancestor->data, pool);
+
+ SVN_ERR (svn_ra_init_ra_libs (&ra_baton, pool));
+ SVN_ERR (svn_ra_get_ra_library (&ra_lib, ra_baton, URL->data, pool));
+
+ SVN_ERR (svn_client__get_ra_callbacks (&ra_callbacks, &cb_baton,
+ auth_baton,
+ anchor,
+ TRUE,
+ TRUE,
+ pool));
+ SVN_ERR (ra_lib->open (&session, URL, ra_callbacks, cb_baton, pool));
+
+ SVN_ERR (svn_wc_get_diff_editor (anchor, target,
+ svn_client_diff_cmd, NULL,
+ recurse,
+ &diff_editor, &diff_edit_baton,
+ pool));
+
+ SVN_ERR (ra_lib->do_update (session,
+ &reporter, &report_baton,
+ revision,
+ target,
+ recurse,
+ diff_editor, diff_edit_baton));
 
+ SVN_ERR (svn_wc_crawl_revisions (path, reporter, report_baton,
+ FALSE, FALSE, recurse, pool));
 
+ SVN_ERR (ra_lib->close (session));
 
+ return SVN_NO_ERROR;
+}
 
 
 
Index: svn/subversion/clients/cmdline/.svn/text-base/cl.h
===================================================================
--- svn/subversion/clients/cmdline/.svn/text-base/cl.h Wed Oct 31 19:30:46 2001
+++ svn/subversion/clients/cmdline/cl.h Fri Nov 2 00:34:54 2001
@@ -151,6 +151,7 @@
   svn_cl__revert,
   svn_cl__status,
   svn_cl__diff,
+ svn_cl__rdiff,
   svn_cl__update;
 
 
Index: svn/subversion/clients/cmdline/.svn/text-base/main.c
===================================================================
--- svn/subversion/clients/cmdline/.svn/text-base/main.c Wed Oct 31 19:30:48 2001
+++ svn/subversion/clients/cmdline/main.c Fri Nov 2 00:34:24 2001
@@ -134,6 +134,11 @@
   { "stat", TRUE, NULL, NULL },
   { "st", TRUE, NULL, NULL },
  
+ { "rdiff", FALSE, svn_cl__rdiff,
+ "Display changes between the working copy and the repository\n"
+ "as contextual diffs.\n"
+ "usage: rdiff [-r REV] [TARGETS]\n" },
+
   { "diff", FALSE, svn_cl__diff,
     "Display local file changes as contextual diffs.\n"
     "usage: diff [TARGETS]\n" },
Index: svn/subversion/clients/cmdline/.svn/empty-file
===================================================================
--- svn/subversion/clients/cmdline/.svn/empty-file Wed Oct 31 19:30:42 2001
+++ svn/subversion/clients/cmdline/rdiff-cmd.c Fri Nov 2 17:31:35 2001
@@ -0,0 +1,66 @@
+/*
+ * rdiff-cmd.c -- Compare the working copy against the repository. This
+ * is based on the update command.
+ *
+ * ====================================================================
+ * Copyright (c) 2001 Philip Martin. 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/.
+ * ====================================================================
+ */
+
+#include "svn_path.h"
+#include "svn_wc.h"
+#include "svn_pools.h"
+#include "cl.h"
+
+
+svn_error_t *
+svn_cl__rdiff (apr_getopt_t *os,
+ svn_cl__opt_state_t *opt_state,
+ apr_pool_t *pool)
+{
+ apr_array_header_t *targets;
+ apr_array_header_t *condensed_targets;
+ svn_client_auth_baton_t *auth_baton;
+ int i;
+
+ targets = svn_cl__args_to_target_array (os, pool);
+ svn_cl__push_implicit_dot_target (targets, pool);
+ SVN_ERR (svn_path_remove_redundancies (&condensed_targets,
+ targets,
+ svn_path_local_style,
+ pool));
+ auth_baton = svn_cl__make_auth_baton (opt_state, pool);
+
+ for (i = 0; i < condensed_targets->nelts; ++i)
+ {
+ svn_stringbuf_t *target =
+ ((svn_stringbuf_t **) (condensed_targets->elts))[i];
+ svn_stringbuf_t *parent_dir, *entry;
+
+ SVN_ERR (svn_wc_get_actual_target (target, &parent_dir, &entry, pool));
+
+ SVN_ERR (svn_client_diff (target,
+ auth_baton,
+ opt_state->start_revision,
+ TRUE,
+ pool));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* ----------------------------------------------------------------
+ * local variables:
+ * eval: (load-file "../../svn-dev.el")
+ * end:
+ */

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Sat Oct 21 14:36:47 2006

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