You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by br...@apache.org on 2013/01/27 01:01:59 UTC

svn commit: r1438999 [3/7] - in /subversion/branches/windows-build-update: ./ build/generator/ subversion/include/ subversion/include/private/ subversion/libsvn_client/ subversion/libsvn_diff/ subversion/libsvn_ra_serf/ subversion/libsvn_repos/ subvers...

Modified: subversion/branches/windows-build-update/subversion/libsvn_client/repos_diff.c
URL: http://svn.apache.org/viewvc/subversion/branches/windows-build-update/subversion/libsvn_client/repos_diff.c?rev=1438999&r1=1438998&r2=1438999&view=diff
==============================================================================
--- subversion/branches/windows-build-update/subversion/libsvn_client/repos_diff.c (original)
+++ subversion/branches/windows-build-update/subversion/libsvn_client/repos_diff.c Sun Jan 27 00:01:57 2013
@@ -56,10 +56,8 @@ struct edit_baton {
   /* The passed depth */
   svn_depth_t depth;
 
-  /* The callback and calback argument that implement the file comparison
-     function */
-  const svn_wc_diff_callbacks4_t *diff_callbacks;
-  void *diff_cmd_baton;
+  /* The result processor */
+  svn_diff_tree_processor_t *processor;
 
   /* RA_SESSION is the open session for making requests to the RA layer */
   svn_ra_session_t *ra_session;
@@ -79,15 +77,6 @@ struct edit_baton {
   /* Empty hash used for adds. */
   apr_hash_t *empty_hash;
 
-  /* Hash used to check replaced paths. Key is path relative CWD,
-   * Value is *deleted_path_notify_t.
-   * All allocations are from edit_baton's pool. */
-  apr_hash_t *deleted_paths;
-
-  /* If the func is non-null, send notifications of actions. */
-  svn_wc_notify_func2_t notify_func;
-  void *notify_baton;
-
   /* TRUE if the operation needs to walk deleted dirs on the "old" side.
      FALSE otherwise. */
   svn_boolean_t walk_deleted_repos_dirs;
@@ -147,6 +136,11 @@ struct dir_baton {
   /* Boolean indicating whether a node property was changed */
   svn_boolean_t has_propchange;
 
+  /* Baton for svn_diff_tree_processor_t */
+  void *pdb;
+  svn_diff_source_t *left_source;
+  svn_diff_source_t *right_source;
+
   /* The pool passed in by add_dir, open_dir, or open_root.
      Also, the pool this dir baton is allocated in. */
   apr_pool_t *pool;
@@ -206,6 +200,11 @@ struct file_baton {
   /* Boolean indicating whether a node property was changed */
   svn_boolean_t has_propchange;
 
+  /* Baton for svn_diff_tree_processor_t */
+  void *pfb;
+  svn_diff_source_t *left_source;
+  svn_diff_source_t *right_source;
+
   /* The pool passed in by add_file or open_file.
      Also, the pool this file_baton is allocated in. */
   apr_pool_t *pool;
@@ -269,49 +268,6 @@ make_file_baton(const char *path,
   return file_baton;
 }
 
-/* Helper function: return up to two svn:mime-type values buried
- * within a file baton.  Set *MIMETYPE1 to the value within the file's
- * pristine properties, or NULL if not available.  Set *MIMETYPE2 to
- * the value within the "new" file's propchanges, or NULL if not
- * available.
- */
-static void
-get_file_mime_types(const char **mimetype1,
-                    const char **mimetype2,
-                    struct file_baton *fb)
-{
-  /* Defaults */
-  *mimetype1 = NULL;
-  *mimetype2 = NULL;
-
-  if (fb->pristine_props)
-    {
-      svn_string_t *pristine_val;
-      pristine_val = apr_hash_get(fb->pristine_props, SVN_PROP_MIME_TYPE,
-                                  strlen(SVN_PROP_MIME_TYPE));
-      if (pristine_val)
-        *mimetype2 = *mimetype1 = pristine_val->data;
-    }
-
-  if (fb->propchanges)
-    {
-      int i;
-      svn_prop_t *propchange;
-
-      for (i = 0; i < fb->propchanges->nelts; i++)
-        {
-          propchange = &APR_ARRAY_IDX(fb->propchanges, i, svn_prop_t);
-          if (strcmp(propchange->name, SVN_PROP_MIME_TYPE) == 0)
-            {
-              if (propchange->value)
-                *mimetype2 = propchange->value->data;
-              break;
-            }
-        }
-    }
-}
-
-
 /* Get revision FB->base_revision of the file described by FB from the
  * repository, through FB->edit_baton->ra_session.
  *
@@ -427,7 +383,6 @@ remove_non_prop_changes(apr_hash_t *pris
     }
 }
 
-
 /* Get the empty file associated with the edit baton. This is cached so
  * that it can be reused, all empty files are the same.
  */
@@ -471,6 +426,9 @@ open_root(void *edit_baton,
   struct dir_baton *db = make_dir_baton("", NULL, eb, FALSE, base_revision,
                                         pool);
 
+  db->left_source = svn_diff__source_create(eb->revision, db->pool);
+  db->right_source = svn_diff__source_create(eb->target_revision, db->pool);
+
   *root_baton = db;
   return SVN_NO_ERROR;
 }
@@ -478,34 +436,43 @@ open_root(void *edit_baton,
 /* Compare a file being deleted against an empty file.
  */
 static svn_error_t *
-diff_deleted_file(svn_wc_notify_state_t *state_p,
-                  svn_boolean_t *tree_conflicted_p,
-                  const char *path,
+diff_deleted_file(const char *path,
+                  void *ppdb,
                   struct edit_baton *eb,
                   apr_pool_t *scratch_pool)
 {
   struct file_baton *fb = make_file_baton(path, FALSE, eb, scratch_pool);
-/*  struct edit_baton *eb = fb->edit_baton;*/
-  const char *mimetype1, *mimetype2;
+  svn_boolean_t skip = FALSE;
+  svn_diff_source_t *left_source = svn_diff__source_create(eb->revision,
+                                                           scratch_pool);
 
   if (eb->cancel_func)
     SVN_ERR(eb->cancel_func(eb->cancel_baton));
 
-  if (eb->text_deltas)
-    SVN_ERR(get_file_from_ra(fb, FALSE, scratch_pool));
-  else
-    SVN_ERR(get_empty_file(eb, &fb->path_start_revision));
-  SVN_ERR(get_empty_file(eb, &fb->path_end_revision));
-  get_file_mime_types(&mimetype1, &mimetype2, fb);
-
-  SVN_ERR(eb->diff_callbacks->file_deleted(state_p, tree_conflicted_p,
-                                           fb->path,
-                                           fb->path_start_revision,
-                                           fb->path_end_revision,
-                                           mimetype1, mimetype2,
-                                           fb->pristine_props,
-                                           eb->diff_cmd_baton,
-                                           scratch_pool));
+  SVN_ERR(eb->processor->file_opened(&fb->pfb, &skip, path,
+                                     left_source,
+                                     NULL /* right_source */,
+                                     NULL /* copyfrom_source */,
+                                     ppdb,
+                                     eb->processor,
+                                     scratch_pool, scratch_pool));
+
+  if (eb->cancel_func)
+    SVN_ERR(eb->cancel_func(eb->cancel_baton));
+
+  if (skip)
+    return SVN_NO_ERROR;
+
+  SVN_ERR(get_file_from_ra(fb, ! eb->text_deltas, scratch_pool));
+
+  SVN_ERR(eb->processor->file_deleted(fb->path,
+                                      left_source,
+                                      fb->path_start_revision,
+                                      fb->pristine_props,
+                                      fb->pfb,
+                                      eb->processor,
+                                      scratch_pool));
+
   return SVN_NO_ERROR;
 }
 
@@ -519,63 +486,82 @@ diff_deleted_file(svn_wc_notify_state_t 
  */
 /* ### TODO: Handle depth. */
 static svn_error_t *
-diff_deleted_dir(svn_wc_notify_state_t *state_p,
-                 svn_boolean_t *tree_conflicted_p,
-                 const char *dir,
+diff_deleted_dir(const char *path,
+                 void *ppdb,
                  struct edit_baton *eb,
-                 apr_pool_t *pool)
+                 apr_pool_t *scratch_pool)
 {
-  apr_hash_t *dirents;
-  apr_pool_t *iterpool = svn_pool_create(pool);
-  apr_hash_index_t *hi;
+  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+  svn_boolean_t skip = FALSE;
+  svn_boolean_t skip_children = FALSE;
+  apr_hash_t *dirents = NULL;
+  apr_hash_t *left_props = NULL;
+  svn_diff_source_t *left_source = svn_diff__source_create(eb->revision,
+                                                           scratch_pool);
+  void *pdb;
 
   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(eb->revision));
 
   if (eb->cancel_func)
     SVN_ERR(eb->cancel_func(eb->cancel_baton));
 
-  SVN_ERR(eb->diff_callbacks->dir_deleted(
-                        state_p, tree_conflicted_p, dir,
-                        eb->diff_cmd_baton, pool));
+  SVN_ERR(eb->processor->dir_opened(&pdb, &skip, &skip_children,
+                                    path,
+                                    left_source,
+                                    NULL /* right_source */,
+                                    NULL /* copyfrom_source */,
+                                    ppdb,
+                                    eb->processor,
+                                    scratch_pool, iterpool));
+
+  if (!skip || !skip_children)
+    SVN_ERR(svn_ra_get_dir2(eb->ra_session,
+                            skip_children ? NULL : &dirents,
+                            NULL,
+                            skip ? NULL : &left_props,
+                            path,
+                            eb->revision,
+                            SVN_DIRENT_KIND,
+                            scratch_pool));
 
   /* The "old" dir will be skipped by the repository report.  If required,
    * crawl it recursively, diffing each file against the empty file.  This
    * is a workaround for issue 2333 "'svn diff URL1 URL2' not reverse of
    * 'svn diff URL2 URL1'". */
-  if (! eb->walk_deleted_repos_dirs)
+  if (! skip_children && eb->walk_deleted_repos_dirs)
     {
-      svn_pool_destroy(iterpool);
-      return SVN_NO_ERROR;
-    }
-
-  SVN_ERR(svn_ra_get_dir2(eb->ra_session,
-                          &dirents,
-                          NULL, NULL,
-                          dir,
-                          eb->revision,
-                          SVN_DIRENT_KIND,
-                          pool));
+      apr_hash_index_t *hi;
 
-  for (hi = apr_hash_first(pool, dirents); hi;
-       hi = apr_hash_next(hi))
-    {
-      const char *path;
-      const char *name = svn__apr_hash_index_key(hi);
-      svn_dirent_t *dirent = svn__apr_hash_index_val(hi);
+      for (hi = apr_hash_first(scratch_pool, dirents); hi;
+           hi = apr_hash_next(hi))
+        {
+          const char *child_path;
+          const char *name = svn__apr_hash_index_key(hi);
+          svn_dirent_t *dirent = svn__apr_hash_index_val(hi);
 
-      svn_pool_clear(iterpool);
+          svn_pool_clear(iterpool);
 
-      path = svn_relpath_join(dir, name, iterpool);
+          child_path = svn_relpath_join(path, name, iterpool);
 
-      if (dirent->kind == svn_node_file)
-        {
-          SVN_ERR(diff_deleted_file(NULL, NULL, path, eb, iterpool));
+          if (dirent->kind == svn_node_file)
+            {
+              SVN_ERR(diff_deleted_file(child_path, pdb, eb, iterpool));
+            }
+          else if (dirent->kind == svn_node_dir)
+            {
+              SVN_ERR(diff_deleted_dir(child_path, pdb, eb, iterpool));
+            }
         }
+    }
 
-      if (dirent->kind == svn_node_dir)
-        {
-          SVN_ERR(diff_deleted_dir(NULL, NULL, path, eb, iterpool));
-        }
+  if (! skip)
+    {
+      SVN_ERR(eb->processor->dir_deleted(path,
+                                         left_source,
+                                         left_props,
+                                         pdb,
+                                         eb->processor,
+                                         scratch_pool));
     }
 
   svn_pool_destroy(iterpool);
@@ -592,9 +578,6 @@ delete_entry(const char *path,
   struct dir_baton *pb = parent_baton;
   struct edit_baton *eb = pb->edit_baton;
   svn_node_kind_t kind;
-  svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
-  svn_wc_notify_action_t action = svn_wc_notify_skip;
-  svn_boolean_t tree_conflicted = FALSE;
   apr_pool_t *scratch_pool;
 
   /* Process skips. */
@@ -611,39 +594,18 @@ delete_entry(const char *path,
     {
     case svn_node_file:
       {
-        SVN_ERR(diff_deleted_file(&state, &tree_conflicted, path, eb,
-                                  scratch_pool));
+        SVN_ERR(diff_deleted_file(path, pb->pdb, eb, scratch_pool));
         break;
       }
     case svn_node_dir:
       {
-        SVN_ERR(diff_deleted_dir(&state, &tree_conflicted, path, eb,
-                                 scratch_pool));
+        SVN_ERR(diff_deleted_dir(path, pb->pdb, eb, scratch_pool));
         break;
       }
     default:
       break;
     }
 
-  if ((state != svn_wc_notify_state_missing)
-      && (state != svn_wc_notify_state_obstructed)
-      && !tree_conflicted)
-    {
-      action = svn_wc_notify_update_delete;
-    }
-
-  if (eb->notify_func)
-    {
-      const char *deleted_path = apr_pstrdup(eb->pool, path);
-      deleted_path_notify_t *dpn = apr_pcalloc(eb->pool, sizeof(*dpn));
-
-      dpn->kind = kind;
-      dpn->action = tree_conflicted ? svn_wc_notify_tree_conflict : action;
-      dpn->state = state;
-      dpn->tree_conflicted = tree_conflicted;
-      apr_hash_set(eb->deleted_paths, deleted_path, APR_HASH_KEY_STRING, dpn);
-    }
-
   svn_pool_destroy(scratch_pool);
 
   return SVN_NO_ERROR;
@@ -661,7 +623,6 @@ add_directory(const char *path,
   struct dir_baton *pb = parent_baton;
   struct edit_baton *eb = pb->edit_baton;
   struct dir_baton *db;
-  svn_wc_notify_state_t state;
 
   /* ### TODO: support copyfrom? */
 
@@ -677,64 +638,19 @@ add_directory(const char *path,
       return SVN_NO_ERROR;
     }
 
+  db->right_source = svn_diff__source_create(eb->target_revision,
+                                             db->pool);
 
-  SVN_ERR(eb->diff_callbacks->dir_added(
-                &state, &db->tree_conflicted,
-                &db->skip, &db->skip_children, db->path,
-                eb->target_revision, copyfrom_path, copyfrom_revision,
-                eb->diff_cmd_baton, pool));
-
-  /* Notifications for directories are done at close_directory time.
-   * But for paths at which the editor drive adds directories, we make an
-   * exception to this rule, so that the path appears in the output before
-   * any children of the newly added directory. Since a deletion at this path
-   * must have happened before this addition, we can safely notify about
-   * replaced directories here, too. */
-  if (eb->notify_func)
-    {
-      deleted_path_notify_t *dpn;
-      svn_wc_notify_t *notify;
-      svn_wc_notify_action_t action;
-      svn_node_kind_t kind = svn_node_dir;
-
-      /* Find out if a pending delete notification for this path is
-       * still around. */
-      dpn = apr_hash_get(eb->deleted_paths, db->path, APR_HASH_KEY_STRING);
-      if (dpn)
-        {
-          /* If any was found, we will handle the pending 'deleted path
-           * notification' (DPN) here. Remove it from the list. */
-          apr_hash_set(eb->deleted_paths, db->path,
-                       APR_HASH_KEY_STRING, NULL);
-
-          /* the pending delete might be on a different node kind. */
-          kind = dpn->kind;
-          state = dpn->state;
-        }
-
-      /* Determine what the notification (ACTION) should be.
-       * In case of a pending 'delete', this might become a 'replace'. */
-      if (db->tree_conflicted)
-        action = svn_wc_notify_tree_conflict;
-      else if (dpn)
-        {
-          if (dpn->action == svn_wc_notify_update_delete)
-            action = svn_wc_notify_update_replace;
-          else
-            /* Note: dpn->action might be svn_wc_notify_tree_conflict */
-            action = dpn->action;
-        }
-      else if (state == svn_wc_notify_state_missing ||
-               state == svn_wc_notify_state_obstructed)
-        action = svn_wc_notify_skip;
-      else
-        action = svn_wc_notify_update_add;
-
-      notify = svn_wc_create_notify(db->path, action, pool);
-      notify->kind = kind;
-      notify->content_state = notify->prop_state = state;
-      (*eb->notify_func)(eb->notify_baton, notify, pool);
-    }
+  SVN_ERR(eb->processor->dir_opened(&db->pdb,
+                                    &db->skip,
+                                    &db->skip_children,
+                                    db->path,
+                                    NULL,
+                                    db->right_source,
+                                    NULL /* copyfrom_source */,
+                                    pb->pdb,
+                                    eb->processor,
+                                    db->pool, db->pool));
 
   return SVN_NO_ERROR;
 }
@@ -763,10 +679,18 @@ open_directory(const char *path,
       return SVN_NO_ERROR;
     }
 
-  SVN_ERR(eb->diff_callbacks->dir_opened(
-                &db->tree_conflicted, &db->skip,
-                &db->skip_children, db->path, base_revision,
-                eb->diff_cmd_baton, pool));
+  db->left_source = svn_diff__source_create(eb->revision, db->pool);
+  db->right_source = svn_diff__source_create(eb->target_revision, db->pool);
+
+  SVN_ERR(eb->processor->dir_opened(&db->pdb,
+                                    &db->skip, &db->skip_children,
+                                    path,
+                                    db->left_source,
+                                    db->right_source,
+                                    NULL /* copyfrom */,
+                                    pb ? pb->pdb : NULL,
+                                    eb->processor,
+                                    db->pool, db->pool));
 
   return SVN_NO_ERROR;
 }
@@ -782,6 +706,7 @@ add_file(const char *path,
          void **file_baton)
 {
   struct dir_baton *pb = parent_baton;
+  struct edit_baton *eb = pb->edit_baton;
   struct file_baton *fb;
 
   /* ### TODO: support copyfrom? */
@@ -798,6 +723,18 @@ add_file(const char *path,
 
   fb->pristine_props = pb->edit_baton->empty_hash;
 
+  fb->right_source = svn_diff__source_create(eb->target_revision, fb->pool);
+
+  SVN_ERR(eb->processor->file_opened(&fb->pfb,
+                                     &fb->skip,
+                                     path,
+                                     NULL,
+                                     fb->right_source,
+                                     NULL /* copy source */,
+                                     pb->pdb,
+                                     eb->processor,
+                                     fb->pool, fb->pool));
+
   return SVN_NO_ERROR;
 }
 
@@ -824,9 +761,18 @@ open_file(const char *path,
 
   fb->base_revision = base_revision;
 
-  SVN_ERR(eb->diff_callbacks->file_opened(
-                   &fb->tree_conflicted, &fb->skip,
-                   fb->path, base_revision, eb->diff_cmd_baton, pool));
+  fb->left_source = svn_diff__source_create(eb->revision, fb->pool);
+  fb->right_source = svn_diff__source_create(eb->target_revision, fb->pool);
+
+  SVN_ERR(eb->processor->file_opened(&fb->pfb,
+                                     &fb->skip,
+                                     path,
+                                     fb->left_source,
+                                     fb->right_source,
+                                     NULL /* copy source */,
+                                     pb->pdb,
+                                     eb->processor,
+                                     fb->pool, fb->pool));
 
   return SVN_NO_ERROR;
 }
@@ -947,8 +893,6 @@ close_file(void *file_baton,
 {
   struct file_baton *fb = file_baton;
   struct edit_baton *eb = fb->edit_baton;
-  svn_wc_notify_state_t content_state = svn_wc_notify_state_unknown;
-  svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
   apr_pool_t *scratch_pool;
 
   /* Skip *everything* within a newly tree-conflicted directory. */
@@ -976,9 +920,9 @@ close_file(void *file_baton,
                                       fb->path));
     }
 
-  if (fb->path_end_revision || fb->has_propchange)
+  if (fb->added || fb->path_end_revision || fb->has_propchange)
     {
-      const char *mimetype1, *mimetype2;
+      apr_hash_t *right_props;
 
       if (!fb->added && !fb->pristine_props)
         {
@@ -990,85 +934,35 @@ close_file(void *file_baton,
       if (fb->pristine_props)
         remove_non_prop_changes(fb->pristine_props, fb->propchanges);
 
-      get_file_mime_types(&mimetype1, &mimetype2, fb);
-
+      right_props = svn_prop__patch(fb->pristine_props, fb->propchanges,
+                                    fb->pool);
 
       if (fb->added)
-        SVN_ERR(eb->diff_callbacks->file_added(
-                 &content_state, &prop_state, &fb->tree_conflicted,
-                 fb->path,
-                 fb->path_end_revision ? fb->path_start_revision : NULL,
-                 fb->path_end_revision,
-                 0,
-                 eb->target_revision,
-                 mimetype1, mimetype2,
-                 NULL, SVN_INVALID_REVNUM,
-                 fb->propchanges, fb->pristine_props,
-                 eb->diff_cmd_baton,
-                 scratch_pool));
-      else
-        SVN_ERR(eb->diff_callbacks->file_changed(
-                 &content_state, &prop_state,
-                 &fb->tree_conflicted, fb->path,
-                 fb->path_end_revision ? fb->path_start_revision : NULL,
-                 fb->path_end_revision,
-                 eb->revision,
-                 eb->target_revision,
-                 mimetype1, mimetype2,
-                 fb->propchanges, fb->pristine_props,
-                 eb->diff_cmd_baton,
-                 scratch_pool));
-    }
-
-
-  if (eb->notify_func)
-    {
-      deleted_path_notify_t *dpn;
-      svn_wc_notify_t *notify;
-      svn_wc_notify_action_t action;
-      svn_node_kind_t kind = svn_node_file;
-
-      /* Find out if a pending delete notification for this path is
-       * still around. */
-      dpn = apr_hash_get(eb->deleted_paths, fb->path, APR_HASH_KEY_STRING);
-      if (dpn)
-        {
-          /* If any was found, we will handle the pending 'deleted path
-           * notification' (DPN) here. Remove it from the list. */
-          apr_hash_set(eb->deleted_paths, fb->path,
-                       APR_HASH_KEY_STRING, NULL);
-
-          /* the pending delete might be on a different node kind. */
-          kind = dpn->kind;
-          content_state = prop_state = dpn->state;
-        }
-
-      /* Determine what the notification (ACTION) should be.
-       * In case of a pending 'delete', this might become a 'replace'. */
-      if (fb->tree_conflicted)
-        action = svn_wc_notify_tree_conflict;
-      else if (dpn)
-        {
-          if (dpn->action == svn_wc_notify_update_delete
-              && fb->added)
-            action = svn_wc_notify_update_replace;
-          else
-            /* Note: dpn->action might be svn_wc_notify_tree_conflict */
-            action = dpn->action;
-        }
-      else if ((content_state == svn_wc_notify_state_missing)
-                || (content_state == svn_wc_notify_state_obstructed))
-        action = svn_wc_notify_skip;
-      else if (fb->added)
-        action = svn_wc_notify_update_add;
+        SVN_ERR(eb->processor->file_added(fb->path,
+                                          NULL /* copyfrom_src */,
+                                          fb->right_source,
+                                          NULL /* copyfrom_file */,
+                                          fb->path_end_revision,
+                                          NULL /* copyfrom_props */,
+                                          right_props,
+                                          fb->pfb,
+                                          eb->processor,
+                                          fb->pool));
       else
-        action = svn_wc_notify_update_update;
-
-      notify = svn_wc_create_notify(fb->path, action, scratch_pool);
-      notify->kind = kind;
-      notify->content_state = content_state;
-      notify->prop_state = prop_state;
-      (*eb->notify_func)(eb->notify_baton, notify, scratch_pool);
+        SVN_ERR(eb->processor->file_changed(fb->path,
+                                            fb->left_source,
+                                            fb->right_source,
+                                            fb->path_end_revision
+                                                    ? fb->path_start_revision
+                                                    : NULL,
+                                            fb->path_end_revision,
+                                            fb->pristine_props,
+                                            right_props,
+                                            (fb->path_end_revision != NULL),
+                                            fb->propchanges,
+                                            fb->pfb,
+                                            eb->processor,
+                                            fb->pool));
     }
 
   svn_pool_destroy(fb->pool); /* Destroy file and scratch pool */
@@ -1089,15 +983,13 @@ close_directory(void *dir_baton,
 {
   struct dir_baton *db = dir_baton;
   struct edit_baton *eb = db->edit_baton;
-  svn_wc_notify_state_t content_state = svn_wc_notify_state_unknown;
-  svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
-  svn_boolean_t skipped = FALSE;
   apr_pool_t *scratch_pool;
   apr_hash_t *pristine_props;
+  svn_boolean_t send_changed = FALSE;
 
   scratch_pool = db->pool;
 
-  if (db->has_propchange && !db->skip)
+  if ((db->has_propchange || db->added) && !db->skip)
     {
       if (db->added)
         {
@@ -1114,86 +1006,49 @@ close_directory(void *dir_baton,
           remove_non_prop_changes(pristine_props, db->propchanges);
         }
 
-      if (db->propchanges->nelts > 0)
+      if (db->propchanges->nelts > 0 || db->added)
         {
-          svn_boolean_t tree_conflicted = FALSE;
-          SVN_ERR(eb->diff_callbacks->dir_props_changed(
-                   &prop_state, &tree_conflicted,
-                   db->path, db->added,
-                   db->propchanges, pristine_props,
-                   eb->diff_cmd_baton, scratch_pool));
-          if (tree_conflicted)
-            db->tree_conflicted = TRUE;
+          apr_hash_t *right_props;
+
+          right_props = svn_prop__patch(pristine_props, db->propchanges,
+                                        scratch_pool);
 
-          if (prop_state == svn_wc_notify_state_obstructed
-              || prop_state == svn_wc_notify_state_missing)
+          if (db->added)
             {
-              content_state = prop_state;
-              skipped = TRUE;
+              SVN_ERR(eb->processor->dir_added(db->path,
+                                           NULL /* copyfrom */,
+                                           db->right_source,
+                                           NULL /* copyfrom props */,
+                                           right_props,
+                                           db->pdb,
+                                           eb->processor,
+                                           db->pool));
+            }
+          else
+            {
+              SVN_ERR(eb->processor->dir_changed(db->path,
+                                                 db->left_source,
+                                                 db->right_source,
+                                                 pristine_props,
+                                                 right_props,
+                                                 db->propchanges,
+                                                 db->pdb,
+                                                 eb->processor,
+                                                 db->pool));
             }
-        }
-    }
-
-  SVN_ERR(eb->diff_callbacks->dir_closed(NULL, NULL, NULL,
-                                         db->path, db->added,
-                                         eb->diff_cmd_baton,
-                                         scratch_pool));
-
-  /* Notify about any deleted paths within this directory that have not
-   * already been notified. */
-  if (!skipped && !db->added && eb->notify_func)
-    {
-      apr_hash_index_t *hi;
 
-      for (hi = apr_hash_first(pool, eb->deleted_paths); hi;
-           hi = apr_hash_next(hi))
-        {
-          svn_wc_notify_t *notify;
-          const char *deleted_path = svn__apr_hash_index_key(hi);
-          deleted_path_notify_t *dpn = svn__apr_hash_index_val(hi);
-
-          /* Ignore paths which are not children of bb->path.  (There
-             should be none due to editor ordering constraints, but
-             ra_serf drops the ball here -- see issue #3802 for
-             details.) */
-          if (! svn_relpath_skip_ancestor(db->path, deleted_path))
-            continue;
-
-          notify = svn_wc_create_notify(deleted_path, dpn->action, pool);
-          notify->kind = dpn->kind;
-          notify->content_state = notify->prop_state = dpn->state;
-          notify->lock_state = svn_wc_notify_lock_state_inapplicable;
-          (*eb->notify_func)(eb->notify_baton, notify, pool);
-          apr_hash_set(eb->deleted_paths, deleted_path,
-                       APR_HASH_KEY_STRING, NULL);
+          send_changed = TRUE; /* Skip dir_closed */
         }
     }
 
-  /* Notify about this directory itself (unless it was added, in which
-   * case the notification was done at that time). */
-  if (!db->added && eb->notify_func && !db->skip)
+  if (! db->skip && !send_changed)
     {
-      svn_wc_notify_t *notify;
-      svn_wc_notify_action_t action;
-
-      if (db->tree_conflicted)
-        action = svn_wc_notify_tree_conflict;
-      else if (skipped)
-        action = svn_wc_notify_skip;
-      else
-        action = svn_wc_notify_update_update;
-
-      notify = svn_wc_create_notify(db->path, action, pool);
-      notify->kind = svn_node_dir;
-
-      /* In case of a tree conflict during merge, the diff callback
-       * sets content_state appropriately. So copy the state into the
-       * notify_t to make sure conflicts get displayed. */
-      notify->content_state = content_state;
-
-      notify->prop_state = prop_state;
-      notify->lock_state = svn_wc_notify_lock_state_inapplicable;
-      (*eb->notify_func)(eb->notify_baton, notify, scratch_pool);
+      SVN_ERR(eb->processor->dir_closed(db->path,
+                                        db->left_source,
+                                        db->right_source,
+                                        db->pdb,
+                                        eb->processor,
+                                        db->pool));
     }
 
   svn_pool_destroy(db->pool); /* Destroy baton and scratch_pool */
@@ -1285,18 +1140,7 @@ absent_directory(const char *path,
   struct dir_baton *pb = parent_baton;
   struct edit_baton *eb = pb->edit_baton;
 
-  /* ### TODO: Raise a tree-conflict?? I sure hope not.*/
-
-  if (eb->notify_func)
-    {
-      svn_wc_notify_t *notify
-        = svn_wc_create_notify(path, svn_wc_notify_skip, pool);
-
-      notify->kind = svn_node_dir;
-      notify->content_state = notify->prop_state
-        = svn_wc_notify_state_missing;
-      (*eb->notify_func)(eb->notify_baton, notify, pool);
-    }
+  SVN_ERR(eb->processor->node_absent(path, pb->pdb, eb->processor, pool));
 
   return SVN_NO_ERROR;
 }
@@ -1312,18 +1156,7 @@ absent_file(const char *path,
   struct dir_baton *pb = parent_baton;
   struct edit_baton *eb = pb->edit_baton;
 
-  /* ### TODO: Raise a tree-conflict?? I sure hope not.*/
-
-  if (eb->notify_func)
-    {
-      svn_wc_notify_t *notify
-        = svn_wc_create_notify(path, svn_wc_notify_skip, pool);
-
-      notify->kind = svn_node_file;
-      notify->content_state = notify->prop_state
-        = svn_wc_notify_state_missing;
-      (*eb->notify_func)(eb->notify_baton, notify, pool);
-    }
+  SVN_ERR(eb->processor->node_absent(path, pb->pdb, eb->processor, pool));
 
   return SVN_NO_ERROR;
 }
@@ -1427,6 +1260,244 @@ fetch_base_func(const char **filename,
   return SVN_NO_ERROR;
 }
 
+/* Baton for diff_state_* functions */
+struct diff_notify_baton_t
+{
+  /* Hash used to check replaced paths. Key is path relative CWD,
+   * Value is *deleted_path_notify_t.
+   * All allocations are from edit_baton's pool. */
+  apr_hash_t *deleted_paths;
+
+  /* If the func is non-null, send notifications of actions. */
+  svn_wc_notify_func2_t notify_func;
+  void *notify_baton;
+
+  /* If not NULL collects the absent paths */
+  apr_hash_t *absent_relpaths;
+
+  apr_pool_t *pool;
+};
+
+/** Callback for the svn_diff_tree_processor_t wrapper, to allow handling
+ *  notifications like how the repos diff in libsvn_client does.
+ *
+ * Probably only necessary while transitioning to svn_diff_tree_processor_t
+ */
+static svn_error_t *
+diff_state_handle(svn_boolean_t tree_conflicted,
+                  svn_wc_notify_state_t *state,
+                  svn_wc_notify_state_t *prop_state,
+                  const char *relpath,
+                  svn_kind_t kind,
+                  svn_boolean_t before_operation,
+                  svn_boolean_t for_add,
+                  svn_boolean_t for_delete,
+                  void *state_baton,
+                  apr_pool_t *scratch_pool)
+{
+  struct diff_notify_baton_t *dnb = state_baton;
+  svn_wc_notify_state_t notify_content_state = svn_wc_notify_state_inapplicable;
+  svn_wc_notify_state_t notify_prop_state = svn_wc_notify_state_inapplicable;
+
+  if (! dnb->notify_func)
+    return SVN_NO_ERROR;
+
+  if ((for_delete && before_operation && !tree_conflicted)
+      || (for_add && kind == svn_kind_dir && !before_operation))
+    return SVN_NO_ERROR;
+
+  if (for_delete)
+    {
+      const char *deleted_path;
+      deleted_path_notify_t *dpn;
+      svn_wc_notify_action_t action;
+
+      deleted_path = apr_pstrdup(dnb->pool, relpath);
+      dpn = apr_pcalloc(dnb->pool, sizeof(*dpn));
+
+      if (!tree_conflicted
+          && state
+             && (*state != svn_wc_notify_state_missing)
+             && (*state != svn_wc_notify_state_obstructed))
+        {
+          action = svn_wc_notify_update_delete;
+        }
+      else
+        action = svn_wc_notify_skip;
+
+      dpn->kind = (kind == svn_kind_dir) ? svn_node_dir : svn_node_file;
+      dpn->action = tree_conflicted ? svn_wc_notify_tree_conflict : action;
+      dpn->state = state ? *state : svn_wc_notify_state_inapplicable;
+      dpn->tree_conflicted = tree_conflicted;
+      apr_hash_set(dnb->deleted_paths, deleted_path, APR_HASH_KEY_STRING, dpn);
+
+      return SVN_NO_ERROR;
+    }
+
+  if (tree_conflicted)
+    {
+      svn_wc_notify_t *notify;
+      deleted_path_notify_t *dpn;
+      svn_node_kind_t notify_kind;
+
+      apr_hash_set(dnb->deleted_paths, relpath,
+                   APR_HASH_KEY_STRING, NULL);
+
+      notify = svn_wc_create_notify(relpath, svn_wc_notify_tree_conflict,
+                                    scratch_pool);
+
+      dpn = apr_hash_get(dnb->deleted_paths, relpath, APR_HASH_KEY_STRING);
+      if (dpn)
+        {
+          /* If any was found, we will handle the pending 'deleted path
+          * notification' (DPN) here. Remove it from the list. */
+          apr_hash_set(dnb->deleted_paths, relpath,
+              APR_HASH_KEY_STRING, NULL);
+
+          /* the pending delete might be on a different node kind. */
+          notify_kind = dpn->kind;
+        }
+
+      notify->kind = notify_kind;
+      (*dnb->notify_func)(dnb->notify_baton, notify, scratch_pool);
+      return SVN_NO_ERROR;
+    }
+
+  if (state)
+    notify_content_state = *state;
+  if (prop_state)
+    notify_prop_state = *prop_state;
+
+  /* These states apply to properties (dirs) and content (files) at the same
+     time, so handle them as the same whatever way we got them. */
+  if (notify_prop_state == svn_wc_notify_state_obstructed
+      || notify_prop_state == svn_wc_notify_state_missing)
+    {
+      notify_content_state = notify_prop_state;
+    }
+
+  if (notify_content_state == svn_wc_notify_state_obstructed
+      || notify_content_state == svn_wc_notify_state_missing)
+    {
+      svn_wc_notify_t *notify;
+
+      notify = svn_wc_create_notify(relpath, svn_wc_notify_skip,
+                                    scratch_pool);
+
+      notify->kind = (kind == svn_kind_dir) ? svn_node_dir : svn_node_file;
+      notify->content_state = notify_content_state;
+      notify->prop_state = notify_prop_state;
+      (*dnb->notify_func)(dnb->notify_baton, notify, scratch_pool);
+      return SVN_NO_ERROR;
+    }
+
+  /* This code is only used from the merge api, and should really be
+     integrated there. */
+
+  {
+    deleted_path_notify_t *dpn;
+    svn_wc_notify_t *notify;
+    svn_wc_notify_action_t action;
+    svn_node_kind_t notify_kind = (kind == svn_kind_dir) ? svn_node_dir
+                                                         : svn_node_file;
+
+    if (for_add)
+      action = svn_wc_notify_update_add;
+    else
+      action = svn_wc_notify_update_update;
+
+    /* Find out if a pending delete notification for this path is
+    * still around. */
+    dpn = apr_hash_get(dnb->deleted_paths, relpath, APR_HASH_KEY_STRING);
+    if (dpn)
+      {
+        /* If any was found, we will handle the pending 'deleted path
+        * notification' (DPN) here. Remove it from the list. */
+        apr_hash_set(dnb->deleted_paths, relpath,
+                     APR_HASH_KEY_STRING, NULL);
+
+        /* the pending delete might be on a different node kind. */
+        notify_kind = dpn->kind;
+        notify_content_state = notify_prop_state = dpn->state;
+
+        if (for_add && dpn->action == svn_wc_notify_update_delete)
+          action = svn_wc_notify_update_replace;
+      }
+
+    notify = svn_wc_create_notify(relpath, action, scratch_pool);
+    notify->kind = notify_kind;
+    notify->content_state = notify_content_state;
+    notify->prop_state = notify_prop_state;
+    (*dnb->notify_func)(dnb->notify_baton, notify, scratch_pool);
+  }
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+diff_state_close(const char *relpath,
+                 svn_kind_t kind,
+                 void *state_baton,
+                 apr_pool_t *scratch_pool)
+{
+  struct diff_notify_baton_t *dnb = state_baton;
+  apr_hash_index_t *hi;
+
+  if (! dnb->notify_func)
+    return SVN_NO_ERROR;
+
+  for (hi = apr_hash_first(scratch_pool, dnb->deleted_paths); hi;
+       hi = apr_hash_next(hi))
+    {
+      svn_wc_notify_t *notify;
+      const char *deleted_path = svn__apr_hash_index_key(hi);
+      deleted_path_notify_t *dpn = svn__apr_hash_index_val(hi);
+
+      /* Ignore paths which are not children of bb->path.  (There
+         should be none due to editor ordering constraints, but
+         ra_serf drops the ball here -- see issue #3802 for
+         details.) */
+      if (! svn_relpath_skip_ancestor(relpath, deleted_path))
+        continue;
+
+      notify = svn_wc_create_notify(deleted_path, dpn->action, scratch_pool);
+      notify->kind = dpn->kind;
+      notify->content_state = notify->prop_state = dpn->state;
+      notify->lock_state = svn_wc_notify_lock_state_inapplicable;
+      (*dnb->notify_func)(dnb->notify_baton, notify, scratch_pool);
+      apr_hash_set(dnb->deleted_paths, deleted_path,
+                   APR_HASH_KEY_STRING, NULL);
+    }
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+diff_state_absent(const char *relpath,
+                  void *state_baton,
+                  apr_pool_t *scratch_pool)
+{
+  struct diff_notify_baton_t *dnb = state_baton;
+
+  if (dnb->notify_func)
+    {
+      svn_wc_notify_t *notify
+        = svn_wc_create_notify(relpath, svn_wc_notify_skip, scratch_pool);
+
+      notify->kind = svn_node_unknown;
+      notify->content_state = notify->prop_state
+        = svn_wc_notify_state_missing;
+      (*dnb->notify_func)(dnb->notify_baton, notify, scratch_pool);
+    }
+
+  if (dnb->absent_relpaths)
+    apr_hash_set(dnb->absent_relpaths,
+                 apr_pstrdup(apr_hash_pool_get(dnb->absent_relpaths), relpath),
+                 APR_HASH_KEY_STRING,
+                 "");
+
+  return SVN_NO_ERROR;
+}
+
 /* Create a repository diff editor and baton.  */
 svn_error_t *
 svn_client__get_diff_editor(const svn_delta_editor_t **editor,
@@ -1436,6 +1507,7 @@ svn_client__get_diff_editor(const svn_de
                             svn_revnum_t revision,
                             svn_boolean_t walk_deleted_dirs,
                             svn_boolean_t text_deltas,
+                            apr_hash_t *absent_relpaths,
                             const svn_wc_diff_callbacks4_t *diff_callbacks,
                             void *diff_cmd_baton,
                             svn_cancel_func_t cancel_func,
@@ -1447,21 +1519,32 @@ svn_client__get_diff_editor(const svn_de
   apr_pool_t *editor_pool = svn_pool_create(result_pool);
   svn_delta_editor_t *tree_editor = svn_delta_default_editor(editor_pool);
   struct edit_baton *eb = apr_pcalloc(editor_pool, sizeof(*eb));
+  struct diff_notify_baton_t *dnb = apr_pcalloc(editor_pool, sizeof(*eb));
   svn_delta_shim_callbacks_t *shim_callbacks =
                                 svn_delta_shim_callbacks_default(editor_pool);
 
   eb->pool = editor_pool;
   eb->depth = depth;
-  eb->diff_callbacks = diff_callbacks;
-  eb->diff_cmd_baton = diff_cmd_baton;
+
+  dnb->pool = editor_pool;
+  dnb->deleted_paths = apr_hash_make(eb->pool);
+  dnb->notify_func = notify_func;
+  dnb->notify_baton = notify_baton;
+  dnb->absent_relpaths = absent_relpaths;
+
+  SVN_ERR(svn_wc__wrap_diff_callbacks(&eb->processor,
+                                      diff_callbacks, diff_cmd_baton,
+                                      diff_state_handle,
+                                      diff_state_close,
+                                      diff_state_absent,
+                                      dnb,
+                                      result_pool, result_pool));
+
   eb->ra_session = ra_session;
 
   eb->revision = revision;
   eb->empty_file = NULL;
   eb->empty_hash = apr_hash_make(eb->pool);
-  eb->deleted_paths = apr_hash_make(eb->pool);
-  eb->notify_func = notify_func;
-  eb->notify_baton = notify_baton;
   eb->walk_deleted_repos_dirs = walk_deleted_dirs;
   eb->text_deltas = text_deltas;
   eb->cancel_func = cancel_func;

Modified: subversion/branches/windows-build-update/subversion/libsvn_diff/diff_file.c
URL: http://svn.apache.org/viewvc/subversion/branches/windows-build-update/subversion/libsvn_diff/diff_file.c?rev=1438999&r1=1438998&r2=1438999&view=diff
==============================================================================
--- subversion/branches/windows-build-update/subversion/libsvn_diff/diff_file.c (original)
+++ subversion/branches/windows-build-update/subversion/libsvn_diff/diff_file.c Sun Jan 27 00:01:57 2013
@@ -762,7 +762,9 @@ datasources_open(void *baton,
   struct file_info files[4];
   apr_finfo_t finfo[4];
   apr_off_t length[4];
+#ifndef SVN_DISABLE_PREFIX_SUFFIX_SCANNING
   svn_boolean_t reached_one_eof;
+#endif
   apr_size_t i;
 
   /* Make sure prefix_lines and suffix_lines are set correctly, even if we
@@ -1566,12 +1568,26 @@ output_unified_line(svn_diff__file_outpu
   return SVN_NO_ERROR;
 }
 
+static APR_INLINE svn_error_t *
+output_unified_diff_range(svn_diff__file_output_baton_t *output_baton,
+                          int source,
+                          svn_diff__file_output_unified_type_e type,
+                          apr_off_t until)
+{
+  while (output_baton->current_line[source] < until)
+    {
+      SVN_ERR(output_unified_line(output_baton, type, source));
+    }
+  return SVN_NO_ERROR;
+}
+
 static svn_error_t *
 output_unified_flush_hunk(svn_diff__file_output_baton_t *baton)
 {
   apr_off_t target_line;
   apr_size_t hunk_len;
-  int i;
+  apr_off_t old_start;
+  apr_off_t new_start;
 
   if (svn_stringbuf_isempty(baton->hunk))
     {
@@ -1583,25 +1599,25 @@ output_unified_flush_hunk(svn_diff__file
                 + SVN_DIFF__UNIFIED_CONTEXT_SIZE;
 
   /* Add trailing context to the hunk */
-  while (baton->current_line[0] < target_line)
-    {
-      SVN_ERR(output_unified_line
-              (baton, svn_diff__file_output_unified_context, 0));
-    }
+  SVN_ERR(output_unified_diff_range(baton, 0 /* original */,
+                                    svn_diff__file_output_unified_context,
+                                    target_line));
+
+  old_start = baton->hunk_start[0];
+  new_start = baton->hunk_start[1];
 
   /* If the file is non-empty, convert the line indexes from
      zero based to one based */
-  for (i = 0; i < 2; i++)
-    {
-      if (baton->hunk_length[i] > 0)
-        baton->hunk_start[i]++;
-    }
+  if (baton->hunk_length[0])
+    old_start++;
+  if (baton->hunk_length[1])
+    new_start++;
 
   /* Write the hunk header */
   SVN_ERR(svn_diff__unified_write_hunk_header(
             baton->output_stream, baton->header_encoding, "@@",
-            baton->hunk_start[0], baton->hunk_length[0],
-            baton->hunk_start[1], baton->hunk_length[1],
+            old_start, baton->hunk_length[0],
+            new_start, baton->hunk_length[1],
             baton->hunk_extra_context,
             baton->pool));
 
@@ -1613,6 +1629,8 @@ output_unified_flush_hunk(svn_diff__file
   /* Prepare for the next hunk */
   baton->hunk_length[0] = 0;
   baton->hunk_length[1] = 0;
+  baton->hunk_start[0] = 0;
+  baton->hunk_start[1] = 0;
   svn_stringbuf_setempty(baton->hunk);
 
   return SVN_NO_ERROR;
@@ -1625,96 +1643,121 @@ output_unified_diff_modified(void *baton
   apr_off_t latest_start, apr_off_t latest_length)
 {
   svn_diff__file_output_baton_t *output_baton = baton;
-  apr_off_t target_line[2];
-  int i;
+  apr_off_t context_prefix_length;
+  apr_off_t prev_context_end;
+  svn_boolean_t init_hunk = FALSE;
 
-  target_line[0] = original_start >= SVN_DIFF__UNIFIED_CONTEXT_SIZE
-                   ? original_start - SVN_DIFF__UNIFIED_CONTEXT_SIZE : 0;
-  target_line[1] = modified_start;
-
-  /* If the changed ranges are far enough apart (no overlapping or connecting
-     context), flush the current hunk, initialize the next hunk and skip the
-     lines not in context.  Also do this when this is the first hunk.
-   */
-  if (output_baton->current_line[0] < target_line[0]
-      && (output_baton->hunk_start[0] + output_baton->hunk_length[0]
-          + SVN_DIFF__UNIFIED_CONTEXT_SIZE < target_line[0]
-          || output_baton->hunk_length[0] == 0))
-    {
-      SVN_ERR(output_unified_flush_hunk(output_baton));
-
-      output_baton->hunk_start[0] = target_line[0];
-      output_baton->hunk_start[1] = target_line[1] + target_line[0]
-                                    - original_start;
-
-      /* Skip lines until we are at the beginning of the context we want to
-         display */
-      while (output_baton->current_line[0] < target_line[0])
-        {
-          SVN_ERR(output_unified_line(output_baton,
-                                      svn_diff__file_output_unified_skip, 0));
-        }
-
-      if (output_baton->show_c_function)
-        {
-          apr_size_t p;
-          const char *invalid_character;
-
-          /* Save the extra context for later use.
-           * Note that the last byte of the hunk_extra_context array is never
-           * touched after it is zero-initialized, so the array is always
-           * 0-terminated. */
-          strncpy(output_baton->hunk_extra_context,
-                  output_baton->extra_context->data,
-                  SVN_DIFF__EXTRA_CONTEXT_LENGTH);
-          /* Trim whitespace at the end, most notably to get rid of any
-           * newline characters. */
-          p = strlen(output_baton->hunk_extra_context);
-          while (p > 0
-                 && svn_ctype_isspace(output_baton->hunk_extra_context[p - 1]))
-            {
-              output_baton->hunk_extra_context[--p] = '\0';
-            }
-          invalid_character =
-            svn_utf__last_valid(output_baton->hunk_extra_context,
-                                SVN_DIFF__EXTRA_CONTEXT_LENGTH);
-          for (p = invalid_character - output_baton->hunk_extra_context;
-               p < SVN_DIFF__EXTRA_CONTEXT_LENGTH; p++)
-            {
-              output_baton->hunk_extra_context[p] = '\0';
-            }
-        }
-    }
+  if (original_start > SVN_DIFF__UNIFIED_CONTEXT_SIZE)
+    context_prefix_length = SVN_DIFF__UNIFIED_CONTEXT_SIZE;
+  else
+    context_prefix_length = original_start;
 
-  /* Skip lines until we are at the start of the changed range */
-  while (output_baton->current_line[1] < target_line[1])
+  /* Calculate where the previous hunk will end if we would write it now
+     (including the necessary context at the end) */
+  if (output_baton->hunk_length[0] > 0 || output_baton->hunk_length[1] > 0)
+    {
+      prev_context_end = output_baton->hunk_start[0]
+                         + output_baton->hunk_length[0]
+                         + SVN_DIFF__UNIFIED_CONTEXT_SIZE;
+    }
+  else
     {
-      SVN_ERR(output_unified_line(output_baton,
-                                  svn_diff__file_output_unified_skip, 1));
+      prev_context_end = -1;
+
+      if (output_baton->hunk_start[0] == 0
+          && (original_length > 0 || modified_length > 0))
+        init_hunk = TRUE;
     }
 
-  /* Output the context preceding the changed range */
-  while (output_baton->current_line[0] < original_start)
+  /* If the changed range is far enough from the previous range, flush the current
+     hunk. */
+  {
+    apr_off_t new_hunk_start = (original_start - context_prefix_length);
+
+    if (output_baton->current_line[0] < new_hunk_start
+          && prev_context_end <= new_hunk_start)
+      {
+        SVN_ERR(output_unified_flush_hunk(output_baton));
+        init_hunk = TRUE;
+      }
+    else if (output_baton->hunk_length[0] > 0
+             || output_baton->hunk_length[1] > 0)
+      {
+        /* We extend the current hunk */
+
+
+        /* Original: Output the context preceding the changed range */
+        SVN_ERR(output_unified_diff_range(output_baton, 0 /* original */,
+                                          svn_diff__file_output_unified_context,
+                                          original_start));
+      }
+  }
+
+  /* Original: Skip lines until we are at the beginning of the context we want
+     to display */
+  SVN_ERR(output_unified_diff_range(output_baton, 0 /* original */,
+                                    svn_diff__file_output_unified_skip,
+                                    original_start - context_prefix_length));
+
+  /* Note that the above skip stores data for the show_c_function support below */
+
+  if (init_hunk)
     {
-      SVN_ERR(output_unified_line(output_baton,
-                                  svn_diff__file_output_unified_context, 0));
-    }
+      SVN_ERR_ASSERT(output_baton->hunk_length[0] == 0
+                     && output_baton->hunk_length[1] == 0);
 
-  target_line[0] = original_start + original_length;
-  target_line[1] = modified_start + modified_length;
+      output_baton->hunk_start[0] = original_start - context_prefix_length;
+      output_baton->hunk_start[1] = modified_start - context_prefix_length;
+    }
 
-  /* Output the changed range */
-  for (i = 0; i < 2; i++)
+  if (init_hunk && output_baton->show_c_function)
     {
-      while (output_baton->current_line[i] < target_line[i])
+      apr_size_t p;
+      const char *invalid_character;
+
+      /* Save the extra context for later use.
+       * Note that the last byte of the hunk_extra_context array is never
+       * touched after it is zero-initialized, so the array is always
+       * 0-terminated. */
+      strncpy(output_baton->hunk_extra_context,
+              output_baton->extra_context->data,
+              SVN_DIFF__EXTRA_CONTEXT_LENGTH);
+      /* Trim whitespace at the end, most notably to get rid of any
+       * newline characters. */
+      p = strlen(output_baton->hunk_extra_context);
+      while (p > 0
+             && svn_ctype_isspace(output_baton->hunk_extra_context[p - 1]))
+        {
+          output_baton->hunk_extra_context[--p] = '\0';
+        }
+      invalid_character =
+        svn_utf__last_valid(output_baton->hunk_extra_context,
+                            SVN_DIFF__EXTRA_CONTEXT_LENGTH);
+      for (p = invalid_character - output_baton->hunk_extra_context;
+           p < SVN_DIFF__EXTRA_CONTEXT_LENGTH; p++)
         {
-          SVN_ERR(output_unified_line
-                          (output_baton,
-                           i == 0 ? svn_diff__file_output_unified_delete
-                                  : svn_diff__file_output_unified_insert, i));
+          output_baton->hunk_extra_context[p] = '\0';
         }
     }
 
+  /* Modified: Skip lines until we are at the start of the changed range */
+  SVN_ERR(output_unified_diff_range(output_baton, 1 /* modified */,
+                                    svn_diff__file_output_unified_skip,
+                                    modified_start));
+
+  /* Original: Output the context preceding the changed range */
+  SVN_ERR(output_unified_diff_range(output_baton, 0 /* original */,
+                                    svn_diff__file_output_unified_context,
+                                    original_start));
+
+  /* Both: Output the changed range */
+  SVN_ERR(output_unified_diff_range(output_baton, 0 /* original */,
+                                    svn_diff__file_output_unified_delete,
+                                    original_start + original_length));
+  SVN_ERR(output_unified_diff_range(output_baton, 1 /* modified */,
+                                    svn_diff__file_output_unified_insert,
+                                    modified_start + modified_length));
+
   return SVN_NO_ERROR;
 }
 
@@ -1767,7 +1810,6 @@ svn_diff_file_output_unified3(svn_stream
   if (svn_diff_contains_diffs(diff))
     {
       svn_diff__file_output_baton_t baton;
-      const char **c;
       int i;
 
       memset(&baton, 0, sizeof(baton));
@@ -1779,14 +1821,15 @@ svn_diff_file_output_unified3(svn_stream
       baton.hunk = svn_stringbuf_create_empty(pool);
       baton.show_c_function = show_c_function;
       baton.extra_context = svn_stringbuf_create_empty(pool);
-      baton.extra_skip_match = apr_array_make(pool, 3, sizeof(char **));
 
-      c = apr_array_push(baton.extra_skip_match);
-      *c = "public:*";
-      c = apr_array_push(baton.extra_skip_match);
-      *c = "private:*";
-      c = apr_array_push(baton.extra_skip_match);
-      *c = "protected:*";
+      if (show_c_function)
+        {
+          baton.extra_skip_match = apr_array_make(pool, 3, sizeof(char **));
+
+          APR_ARRAY_PUSH(baton.extra_skip_match, const char *) = "public:*";
+          APR_ARRAY_PUSH(baton.extra_skip_match, const char *) = "private:*";
+          APR_ARRAY_PUSH(baton.extra_skip_match, const char *) = "protected:*";
+        }
 
       SVN_ERR(svn_utf_cstring_from_utf8_ex2(&baton.context_str, " ",
                                             header_encoding, pool));
@@ -1810,7 +1853,7 @@ svn_diff_file_output_unified3(svn_stream
               else
                 return svn_error_createf(
                                    SVN_ERR_BAD_RELATIVE_PATH, NULL,
-                                   _("Path '%s' must be an immediate child of "
+                                   _("Path '%s' must be inside "
                                      "the directory '%s'"),
                                    svn_dirent_local_style(original_path, pool),
                                    svn_dirent_local_style(relative_to_dir,
@@ -1826,7 +1869,7 @@ svn_diff_file_output_unified3(svn_stream
               else
                 return svn_error_createf(
                                    SVN_ERR_BAD_RELATIVE_PATH, NULL,
-                                   _("Path '%s' must be an immediate child of "
+                                   _("Path '%s' must be inside "
                                      "the directory '%s'"),
                                    svn_dirent_local_style(modified_path, pool),
                                    svn_dirent_local_style(relative_to_dir,
@@ -1842,19 +1885,19 @@ svn_diff_file_output_unified3(svn_stream
 
       if (original_header == NULL)
         {
-          SVN_ERR(output_unified_default_hdr
-                  (&original_header, original_path, pool));
+          SVN_ERR(output_unified_default_hdr(&original_header, original_path,
+                                             pool));
         }
 
       if (modified_header == NULL)
         {
-          SVN_ERR(output_unified_default_hdr
-                  (&modified_header, modified_path, pool));
+          SVN_ERR(output_unified_default_hdr(&modified_header, modified_path,
+                                             pool));
         }
 
-      SVN_ERR(svn_diff__unidiff_write_header(
-                output_stream, header_encoding,
-                original_header, modified_header, pool));
+      SVN_ERR(svn_diff__unidiff_write_header(output_stream, header_encoding,
+                                             original_header, modified_header,
+                                             pool));
 
       SVN_ERR(svn_diff_output(diff, &baton,
                               &svn_diff__file_output_unified_vtable));

Modified: subversion/branches/windows-build-update/subversion/libsvn_diff/diff_memory.c
URL: http://svn.apache.org/viewvc/subversion/branches/windows-build-update/subversion/libsvn_diff/diff_memory.c?rev=1438999&r1=1438998&r2=1438999&view=diff
==============================================================================
--- subversion/branches/windows-build-update/subversion/libsvn_diff/diff_memory.c (original)
+++ subversion/branches/windows-build-update/subversion/libsvn_diff/diff_memory.c Sun Jan 27 00:01:57 2013
@@ -27,6 +27,8 @@
 #include <apr_want.h>
 #include <apr_tables.h>
 
+#include <assert.h>
+
 #include "svn_diff.h"
 #include "svn_pools.h"
 #include "svn_types.h"
@@ -342,7 +344,8 @@ typedef enum unified_output_e
 {
   unified_output_context = 0,
   unified_output_delete,
-  unified_output_insert
+  unified_output_insert,
+  unified_output_skip
 } unified_output_e;
 
 /* Baton for generating unified diffs */
@@ -351,7 +354,7 @@ typedef struct unified_output_baton_t
   svn_stream_t *output_stream;
   const char *header_encoding;
   source_tokens_t sources[2]; /* 0 == original; 1 == modified */
-  apr_off_t next_token; /* next token in original source */
+  apr_off_t current_token[2]; /* current token per source */
 
   /* Cached markers, in header_encoding,
      indexed using unified_output_e */
@@ -382,31 +385,28 @@ static svn_error_t *
 output_unified_token_range(output_baton_t *btn,
                            int tokens,
                            unified_output_e type,
-                           apr_off_t first,
-                           apr_off_t past_last)
+                           apr_off_t until)
 {
   source_tokens_t *source = &btn->sources[tokens];
-  apr_off_t idx;
-
-  past_last = (past_last > source->tokens->nelts)
-    ? source->tokens->nelts : past_last;
 
-  if (tokens == 0)
-    /* We get context from the original source, don't expect
-       to be asked to output a block which starts before
-       what we already have written. */
-    first = (first < btn->next_token) ? btn->next_token : first;
+  if (until > source->tokens->nelts)
+    until = source->tokens->nelts;
 
-  if (first >= past_last)
+  if (until <= btn->current_token[tokens])
     return SVN_NO_ERROR;
 
   /* Do the loop with prefix and token */
-  for (idx = first; idx < past_last; idx++)
+  while (TRUE)
     {
-      svn_string_t *token
-        = APR_ARRAY_IDX(source->tokens, idx, svn_string_t *);
-      svn_stringbuf_appendcstr(btn->hunk, btn->prefix_str[type]);
-      svn_stringbuf_appendbytes(btn->hunk, token->data, token->len);
+      svn_string_t *token =
+        APR_ARRAY_IDX(source->tokens, btn->current_token[tokens],
+                      svn_string_t *);
+
+      if (type != unified_output_skip)
+        {
+          svn_stringbuf_appendcstr(btn->hunk, btn->prefix_str[type]);
+          svn_stringbuf_appendbytes(btn->hunk, token->data, token->len);
+        }
 
       if (type == unified_output_context)
         {
@@ -415,11 +415,18 @@ output_unified_token_range(output_baton_
         }
       else if (type == unified_output_delete)
         btn->hunk_length[0]++;
-      else
+      else if (type == unified_output_insert)
         btn->hunk_length[1]++;
 
+      /* ### TODO: Add skip processing for -p handling? */
+
+      btn->current_token[tokens]++;
+      if (btn->current_token[tokens] == until)
+        break;
     }
-  if (past_last == source->tokens->nelts && source->ends_without_eol)
+
+  if (btn->current_token[tokens] == source->tokens->nelts 
+      && source->ends_without_eol)
     {
       const char *out_str;
 
@@ -429,8 +436,7 @@ output_unified_token_range(output_baton_
       svn_stringbuf_appendcstr(btn->hunk, out_str);
     }
 
-  if (tokens == 0)
-    btn->next_token = past_last;
+  
 
   return SVN_NO_ERROR;
 }
@@ -445,6 +451,8 @@ output_unified_flush_hunk(output_baton_t
 {
   apr_off_t target_token;
   apr_size_t hunk_len;
+  apr_off_t old_start;
+  apr_off_t new_start;
 
   if (svn_stringbuf_isempty(baton->hunk))
     return SVN_NO_ERROR;
@@ -453,24 +461,28 @@ output_unified_flush_hunk(output_baton_t
 
   /* Write the trailing context */
   target_token = baton->hunk_start[0] + baton->hunk_length[0]
-    + SVN_DIFF__UNIFIED_CONTEXT_SIZE;
+                 + SVN_DIFF__UNIFIED_CONTEXT_SIZE;
   SVN_ERR(output_unified_token_range(baton, 0 /*original*/,
                                      unified_output_context,
-                                     baton->next_token, target_token));
+                                     target_token));
   if (hunk_delimiter == NULL)
     hunk_delimiter = "@@";
 
-  /* Convert our 0-based line numbers into unidiff 1-based numbers */
-  if (baton->hunk_length[0] > 0)
-    baton->hunk_start[0]++;
-  if (baton->hunk_length[1] > 0)
-    baton->hunk_start[1]++;
+  old_start = baton->hunk_start[0];
+  new_start = baton->hunk_start[1];
+
+  /* If the file is non-empty, convert the line indexes from
+     zero based to one based */
+  if (baton->hunk_length[0])
+    old_start++;
+  if (baton->hunk_length[1])
+    new_start++;
 
   /* Write the hunk header */
   SVN_ERR(svn_diff__unified_write_hunk_header(
             baton->output_stream, baton->header_encoding, hunk_delimiter,
-            baton->hunk_start[0], baton->hunk_length[0],
-            baton->hunk_start[1], baton->hunk_length[1],
+            old_start, baton->hunk_length[0],
+            new_start, baton->hunk_length[1],
             NULL /* hunk_extra_context */,
             baton->pool));
 
@@ -478,7 +490,11 @@ output_unified_flush_hunk(output_baton_t
   SVN_ERR(svn_stream_write(baton->output_stream,
                            baton->hunk->data, &hunk_len));
 
-  baton->hunk_length[0] = baton->hunk_length[1] = 0;
+  /* Prepare for the next hunk */
+  baton->hunk_length[0] = 0;
+  baton->hunk_length[1] = 0;
+  baton->hunk_start[0] = 0;
+  baton->hunk_start[1] = 0;
   svn_stringbuf_setempty(baton->hunk);
 
   return SVN_NO_ERROR;
@@ -494,38 +510,91 @@ output_unified_diff_modified(void *baton
                              apr_off_t latest_start,
                              apr_off_t latest_length)
 {
-  output_baton_t *btn = baton;
-  apr_off_t targ_orig, targ_mod;
+  output_baton_t *output_baton = baton;
+  apr_off_t context_prefix_length;
+  apr_off_t prev_context_end;
+  svn_boolean_t init_hunk = FALSE;
+
+  if (original_start > SVN_DIFF__UNIFIED_CONTEXT_SIZE)
+    context_prefix_length = SVN_DIFF__UNIFIED_CONTEXT_SIZE;
+  else
+    context_prefix_length = original_start;
 
-  targ_orig = original_start - SVN_DIFF__UNIFIED_CONTEXT_SIZE;
-  targ_orig = (targ_orig < 0) ? 0 : targ_orig;
-  targ_mod = modified_start;
-
-  /* If the changed ranges are far enough apart (no overlapping or
-   * connecting context), flush the current hunk. */
-  if (btn->next_token + SVN_DIFF__UNIFIED_CONTEXT_SIZE < targ_orig)
-    SVN_ERR(output_unified_flush_hunk(btn, btn->hunk_delimiter));
-  /* Adjust offset if it's not the first hunk. */
-  else if (btn->hunk_length[0] != 0)
-    targ_orig = btn->next_token;
+  /* Calculate where the previous hunk will end if we would write it now
+     (including the necessary context at the end) */
+  if (output_baton->hunk_length[0] > 0 || output_baton->hunk_length[1] > 0)
+    {
+      prev_context_end = output_baton->hunk_start[0]
+                         + output_baton->hunk_length[0]
+                         + SVN_DIFF__UNIFIED_CONTEXT_SIZE;
+    }
+  else
+    {
+      prev_context_end = -1;
+
+      if (output_baton->hunk_start[0] == 0
+          && (original_length > 0 || modified_length > 0))
+        init_hunk = TRUE;
+    }
 
-  if (btn->hunk_length[0] == 0
-      && btn->hunk_length[1] == 0)
+  /* If the changed range is far enough from the previous range, flush the current
+     hunk. */
+  {
+    apr_off_t new_hunk_start = (original_start - context_prefix_length);
+
+    if (output_baton->current_token[0] < new_hunk_start
+          && prev_context_end <= new_hunk_start)
+      {
+        SVN_ERR(output_unified_flush_hunk(output_baton,
+                                          output_baton->hunk_delimiter));
+        init_hunk = TRUE;
+      }
+    else if (output_baton->hunk_length[0] > 0
+             || output_baton->hunk_length[1] > 0)
+      {
+        /* We extend the current hunk */
+
+        /* Original: Output the context preceding the changed range */
+        SVN_ERR(output_unified_token_range(output_baton, 0 /* original */,
+                                           unified_output_context,
+                                           original_start));
+      }
+  }
+
+  /* Original: Skip lines until we are at the beginning of the context we want
+     to display */
+  SVN_ERR(output_unified_token_range(output_baton, 0 /* original */,
+                                     unified_output_skip,
+                                     original_start - context_prefix_length));
+
+  if (init_hunk)
     {
-      btn->hunk_start[0] = targ_orig;
-      btn->hunk_start[1] = targ_mod + targ_orig - original_start;
+      SVN_ERR_ASSERT(output_baton->hunk_length[0] == 0
+                     && output_baton->hunk_length[1] == 0);
+
+      output_baton->hunk_start[0] = original_start - context_prefix_length;
+      output_baton->hunk_start[1] = modified_start - context_prefix_length;
     }
 
-  SVN_ERR(output_unified_token_range(btn, 0/*original*/,
-                                     unified_output_context,
-                                     targ_orig, original_start));
-  SVN_ERR(output_unified_token_range(btn, 0/*original*/,
+  /* Modified: Skip lines until we are at the start of the changed range */
+  SVN_ERR(output_unified_token_range(output_baton, 1 /* modified */,
+                                     unified_output_skip,
+                                     modified_start));
+
+  /* Original: Output the context preceding the changed range */
+  SVN_ERR(output_unified_token_range(output_baton, 0 /* original */,
+                                    unified_output_context,
+                                    original_start));
+
+  /* Both: Output the changed range */
+  SVN_ERR(output_unified_token_range(output_baton, 0 /* original */,
                                      unified_output_delete,
-                                     original_start,
                                      original_start + original_length));
-  return output_unified_token_range(btn, 1/*modified*/, unified_output_insert,
-                                    modified_start,
-                                    modified_start + modified_length);
+  SVN_ERR(output_unified_token_range(output_baton, 1 /* modified */,
+                                     unified_output_insert,
+                                     modified_start + modified_length));
+
+  return SVN_NO_ERROR;
 }
 
 static const svn_diff_output_fns_t mem_output_unified_vtable =
@@ -656,7 +725,7 @@ typedef struct merge_output_baton_t
 
   /* Tokenized source text */
   source_tokens_t sources[3];
-  apr_off_t next_token;
+  apr_off_t next_token[3];
 
   /* Markers for marking conflicted sections */
   const char *markers[4]; /* 0 = original, 1 = modified,

Modified: subversion/branches/windows-build-update/subversion/libsvn_ra_serf/update.c
URL: http://svn.apache.org/viewvc/subversion/branches/windows-build-update/subversion/libsvn_ra_serf/update.c?rev=1438999&r1=1438998&r2=1438999&view=diff
==============================================================================
--- subversion/branches/windows-build-update/subversion/libsvn_ra_serf/update.c (original)
+++ subversion/branches/windows-build-update/subversion/libsvn_ra_serf/update.c Sun Jan 27 00:01:57 2013
@@ -2738,6 +2738,28 @@ create_update_report_body(serf_bucket_t 
   return SVN_NO_ERROR;
 }
 
+/* Serf callback to setup update request headers. */
+static svn_error_t *
+setup_update_report_headers(serf_bucket_t *headers,
+                            void *baton,
+                            apr_pool_t *pool)
+{
+  report_context_t *report = baton;
+
+  if (report->sess->using_compression)
+    {
+      serf_bucket_headers_setn(headers, "Accept-Encoding",
+                               "gzip;svndiff1;q=0.9,svndiff;q=0.8");
+    }
+  else
+    {
+      serf_bucket_headers_setn(headers, "Accept-Encoding",
+                               "svndiff1;q=0.9,svndiff;q=0.8");
+    }
+
+  return SVN_NO_ERROR;
+}
+
 static svn_error_t *
 finish_report(void *report_baton,
               apr_pool_t *pool)
@@ -2783,6 +2805,9 @@ finish_report(void *report_baton,
   handler->body_delegate = create_update_report_body;
   handler->body_delegate_baton = report;
   handler->body_type = "text/xml";
+  handler->custom_accept_encoding = TRUE;
+  handler->header_delegate = setup_update_report_headers;
+  handler->header_delegate_baton = report;
   handler->conn = sess->conns[0];
   handler->session = sess;
 

Modified: subversion/branches/windows-build-update/subversion/libsvn_repos/authz.c
URL: http://svn.apache.org/viewvc/subversion/branches/windows-build-update/subversion/libsvn_repos/authz.c?rev=1438999&r1=1438998&r2=1438999&view=diff
==============================================================================
--- subversion/branches/windows-build-update/subversion/libsvn_repos/authz.c (original)
+++ subversion/branches/windows-build-update/subversion/libsvn_repos/authz.c Sun Jan 27 00:01:57 2013
@@ -849,7 +849,7 @@ authz_retrieve_config_repo(svn_config_t 
   /* Add the URL to the error stack since the parser doesn't have it. */
   if (err != SVN_NO_ERROR)
     return svn_error_createf(err->apr_err, err, 
-                             "Error parsing config file: '%s' in repo '%s':",
+                             "Error while parsing config file: '%s' in repo '%s':",
                              fs_path, repos_root_dirent);
 
   return SVN_NO_ERROR;
@@ -919,20 +919,81 @@ authz_retrieve_config(svn_config_t **cfg
   return SVN_NO_ERROR;
 }
 
+
+/* Callback to copy (name, value) group into the "groups" section
+   of another configuration. */
+static svn_boolean_t
+authz_copy_group(const char *name, const char *value,
+                 void *baton, apr_pool_t *pool)
+{
+  svn_config_t *authz_cfg = baton;
+
+  svn_config_set(authz_cfg, SVN_CONFIG_SECTION_GROUPS, name, value);
+
+  return TRUE;
+}
+
+/* Copy group definitions from GROUPS_CFG to the resulting AUTHZ.
+ * If AUTHZ already contains any group definition, report an error.
+ * Use POOL for temporary allocations. */
+static svn_error_t *
+authz_copy_groups(svn_authz_t *authz, svn_config_t *groups_cfg,
+                  apr_pool_t *pool)
+{
+  /* Easy out: we prohibit local groups in the authz file when global
+     groups are being used. */
+  if (svn_config_has_section(authz->cfg, SVN_CONFIG_SECTION_GROUPS))
+    {
+      return svn_error_create(SVN_ERR_AUTHZ_INVALID_CONFIG, NULL,
+                              "Authz file cannot contain any groups "
+                              "when global groups are being used.");
+    }
+
+  svn_config_enumerate2(groups_cfg, SVN_CONFIG_SECTION_GROUPS,
+                        authz_copy_group, authz->cfg, pool);
+
+  return SVN_NO_ERROR;
+}
+
 svn_error_t *
 svn_repos__authz_read(svn_authz_t **authz_p, const char *path,
-                      svn_boolean_t must_exist, svn_boolean_t accept_urls,
-                      const char *repos_root, apr_pool_t *pool)
+                      const char *groups_path, svn_boolean_t must_exist,
+                      svn_boolean_t accept_urls, const char *repos_root,
+                      apr_pool_t *pool)
 {
   svn_authz_t *authz = apr_palloc(pool, sizeof(*authz));
 
-  /* Load the rule file */
+  /* Load the authz file */
   if (accept_urls)
     SVN_ERR(authz_retrieve_config(&authz->cfg, path, must_exist, repos_root,
                                   pool));
   else
     SVN_ERR(svn_config_read2(&authz->cfg, path, must_exist, TRUE, pool));
 
+  if (groups_path)
+    {
+      svn_config_t *groups_cfg;
+      svn_error_t *err;
+
+      /* Load the groups file */
+      if (accept_urls)
+        SVN_ERR(authz_retrieve_config(&groups_cfg, groups_path, must_exist,
+                                      repos_root, pool));
+      else
+        SVN_ERR(svn_config_read2(&groups_cfg, groups_path, must_exist,
+                                 TRUE, pool));
+
+      /* Copy the groups from groups_cfg into authz. */
+      err = authz_copy_groups(authz, groups_cfg, pool);
+
+      /* Add the paths to the error stack since the authz_copy_groups
+         routine knows nothing about them. */
+      if (err != SVN_NO_ERROR)
+        return svn_error_createf(err->apr_err, err,
+                                 "Error reading authz file '%s' with "
+                                 "groups file '%s':", path, groups_path);
+    }
+
   /* Make sure there are no errors in the configuration. */
   SVN_ERR(authz_validate(authz, pool));
 
@@ -946,23 +1007,33 @@ svn_repos__authz_read(svn_authz_t **auth
 
 svn_error_t *
 svn_repos_authz_read2(svn_authz_t **authz_p, const char *path,
-                      svn_boolean_t must_exist, const char *repos_root,
-                      apr_pool_t *pool)
+                      const char *groups_path, svn_boolean_t must_exist,
+                      const char *repos_root, apr_pool_t *pool)
 {
-  return svn_repos__authz_read(authz_p, path, must_exist, TRUE, repos_root,
-                               pool);
+  return svn_repos__authz_read(authz_p, path, groups_path, must_exist,
+                               TRUE, repos_root, pool);
 }
 
 
 svn_error_t *
 svn_repos_authz_parse(svn_authz_t **authz_p, svn_stream_t *stream, 
-                      apr_pool_t *pool)
+                      svn_stream_t *groups_stream, apr_pool_t *pool)
 {
   svn_authz_t *authz = apr_palloc(pool, sizeof(*authz));
 
-  /* Parse the stream */
+  /* Parse the authz stream */
   SVN_ERR(svn_config_parse(&authz->cfg, stream, TRUE, pool));
 
+  if (groups_stream)
+    {
+      svn_config_t *groups_cfg;
+
+      /* Parse the groups stream */
+      SVN_ERR(svn_config_parse(&groups_cfg, groups_stream, TRUE, pool));
+
+      SVN_ERR(authz_copy_groups(authz, groups_cfg, pool));
+    }
+
   /* Make sure there are no errors in the configuration. */
   SVN_ERR(authz_validate(authz, pool));
 

Modified: subversion/branches/windows-build-update/subversion/libsvn_repos/deprecated.c
URL: http://svn.apache.org/viewvc/subversion/branches/windows-build-update/subversion/libsvn_repos/deprecated.c?rev=1438999&r1=1438998&r2=1438999&view=diff
==============================================================================
--- subversion/branches/windows-build-update/subversion/libsvn_repos/deprecated.c (original)
+++ subversion/branches/windows-build-update/subversion/libsvn_repos/deprecated.c Sun Jan 27 00:01:57 2013
@@ -1013,5 +1013,6 @@ svn_error_t *
 svn_repos_authz_read(svn_authz_t **authz_p, const char *file,
                      svn_boolean_t must_exist, apr_pool_t *pool)
 {
-  return svn_repos__authz_read(authz_p, file, must_exist, FALSE, NULL, pool);
+  return svn_repos__authz_read(authz_p, file, NULL, must_exist,
+                               FALSE, NULL, pool);
 }

Modified: subversion/branches/windows-build-update/subversion/libsvn_repos/repos.c
URL: http://svn.apache.org/viewvc/subversion/branches/windows-build-update/subversion/libsvn_repos/repos.c?rev=1438999&r1=1438998&r2=1438999&view=diff
==============================================================================
--- subversion/branches/windows-build-update/subversion/libsvn_repos/repos.c (original)
+++ subversion/branches/windows-build-update/subversion/libsvn_repos/repos.c Sun Jan 27 00:01:57 2013
@@ -1025,6 +1025,12 @@ create_conf(svn_repos_t *repos, apr_pool
 "### no path-based access control is done."                                  NL
 "### Uncomment the line below to use the default authorization file."        NL
 "# authz-db = " SVN_REPOS__CONF_AUTHZ                                        NL
+"### The groups-db option controls the location of the groups file."         NL
+"### Unless you specify a path starting with a /, the file's location is"    NL
+"### relative to the directory containing this file.  The specified path"    NL
+"### may be a repository relative URL (^/) or an absolute file:// URL to a"  NL
+"### text file in a Subversion repository."                                  NL
+"# groups-db = " SVN_REPOS__CONF_GROUPS                                      NL
 "### This option specifies the authentication realm of the repository."      NL
 "### If two repositories have the same authentication realm, they should"    NL
 "### have the same password database, and vice versa.  The default realm"    NL

Modified: subversion/branches/windows-build-update/subversion/libsvn_repos/repos.h
URL: http://svn.apache.org/viewvc/subversion/branches/windows-build-update/subversion/libsvn_repos/repos.h?rev=1438999&r1=1438998&r2=1438999&view=diff
==============================================================================
--- subversion/branches/windows-build-update/subversion/libsvn_repos/repos.h (original)
+++ subversion/branches/windows-build-update/subversion/libsvn_repos/repos.h Sun Jan 27 00:01:57 2013
@@ -95,10 +95,11 @@ extern "C" {
 #define SVN_REPOS__CONF_SVNSERVE_CONF "svnserve.conf"
 
 /* In the svnserve default configuration, these are the suggested
-   locations for the passwd and authz files (in the repository conf
-   directory), and we put example templates there. */
+   locations for the passwd, authz and groups files (in the repository
+   conf directory), and we put example templates there. */
 #define SVN_REPOS__CONF_PASSWD "passwd"
 #define SVN_REPOS__CONF_AUTHZ "authz"
+#define SVN_REPOS__CONF_GROUPS "groups"
 
 /* The Repository object, created by svn_repos_open2() and
    svn_repos_create(). */
@@ -307,22 +308,24 @@ svn_repos__hooks_post_unlock(svn_repos_t
 /*** Authz Functions ***/
 
 /* Read authz configuration data from PATH into *AUTHZ_P, allocated
-   in POOL.
-  
-   PATH may be a file or a registry path and iff ACCEPT_URLS is set
-   it may also be a repos relative url or an absolute file url.  When
+   in POOL.  If GROUPS_PATH is set, use the global groups parsed from it.
+
+   PATH and GROUPS_PATH may be a file or a registry path and iff ACCEPT_URLS
+   is set it may also be a repos relative url or an absolute file url.  When
    ACCEPT_URLS is FALSE REPOS_ROOT can be NULL.
-  
-   If PATH is not a valid authz rule file, then return 
+
+   If PATH or GROUPS_PATH is not a valid authz rule file, then return 
    SVN_AUTHZ_INVALID_CONFIG.  The contents of *AUTHZ_P is then
-   undefined.  If MUST_EXIST is TRUE, a missing authz file is also
-   an error.
-  
+   undefined.  If MUST_EXIST is TRUE, a missing authz or global groups file
+   is also an error.
+
    If PATH is a repos relative URL then REPOS_ROOT must be set to
-   the root of the repository the authz configuration will be used with. */
+   the root of the repository the authz configuration will be used with.
+   The same applies to GROUPS_PATH if it is being used. */
 svn_error_t *
 svn_repos__authz_read(svn_authz_t **authz_p,
                       const char *path,
+                      const char *groups_path,
                       svn_boolean_t must_exist,
                       svn_boolean_t accept_urls,
                       const char *repos_root,

Modified: subversion/branches/windows-build-update/subversion/libsvn_subr/io.c
URL: http://svn.apache.org/viewvc/subversion/branches/windows-build-update/subversion/libsvn_subr/io.c?rev=1438999&r1=1438998&r2=1438999&view=diff
==============================================================================
--- subversion/branches/windows-build-update/subversion/libsvn_subr/io.c (original)
+++ subversion/branches/windows-build-update/subversion/libsvn_subr/io.c Sun Jan 27 00:01:57 2013
@@ -2506,7 +2506,7 @@ svn_io_stat_dirent2(const svn_io_dirent2
   apr_int32_t wanted = APR_FINFO_TYPE | APR_FINFO_LINK
                        | APR_FINFO_SIZE | APR_FINFO_MTIME;
 
-#if defined(WIN32) || defined(__OS2__) || defined(DARWIN)
+#if defined(WIN32) || defined(__OS2__)
   if (verify_truename)
     wanted |= APR_FINFO_NAME;
 #endif
@@ -2535,6 +2535,7 @@ svn_io_stat_dirent2(const svn_io_dirent2
         {
           /* No parent directory. No need to stat/verify */
         }
+#if defined(WIN32) || defined(__OS2__)
       else if (finfo.name)
         {
           const char *name_on_disk;
@@ -2555,8 +2556,9 @@ svn_io_stat_dirent2(const svn_io_dirent2
                           name_on_disk);
             }
         }
-#if defined(DARWIN)
-      /* Currently apr doesn't set finfo.name on DARWIN.
+#elif defined(DARWIN)
+      /* Currently apr doesn't set finfo.name on DARWIN, returning
+                   APR_INCOMPLETE.
          ### Can we optimize this in another way? */
       else
         {