diff_local.c revision 272461
1168404Spjd/*
2168404Spjd * diff_pristine.c -- A simple diff walker which compares local files against
3168404Spjd *                    their pristine versions.
4168404Spjd *
5185029Spjd * ====================================================================
6185029Spjd *    Licensed to the Apache Software Foundation (ASF) under one
7168404Spjd *    or more contributor license agreements.  See the NOTICE file
8168404Spjd *    distributed with this work for additional information
9168404Spjd *    regarding copyright ownership.  The ASF licenses this file
10168404Spjd *    to you under the Apache License, Version 2.0 (the
11168404Spjd *    "License"); you may not use this file except in compliance
12168404Spjd *    with the License.  You may obtain a copy of the License at
13168404Spjd *
14168404Spjd *      http://www.apache.org/licenses/LICENSE-2.0
15168404Spjd *
16168404Spjd *    Unless required by applicable law or agreed to in writing,
17168404Spjd *    software distributed under the License is distributed on an
18168404Spjd *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
19168404Spjd *    KIND, either express or implied.  See the License for the
20168404Spjd *    specific language governing permissions and limitations
21168404Spjd *    under the License.
22168404Spjd * ====================================================================
23168404Spjd *
24168404Spjd * This is the simple working copy diff algorithm which is used when you
25168404Spjd * just use 'svn diff PATH'. It shows what is modified in your working copy
26185029Spjd * since a node was checked out or copied but doesn't show most kinds of
27168404Spjd * restructuring operations.
28168404Spjd *
29168404Spjd * You can look at this as another form of the status walker.
30168404Spjd */
31168404Spjd
32168404Spjd#include <apr_hash.h>
33168404Spjd
34219089Spjd#include "svn_error.h"
35168404Spjd#include "svn_pools.h"
36168404Spjd#include "svn_dirent_uri.h"
37168404Spjd#include "svn_path.h"
38168404Spjd#include "svn_hash.h"
39168404Spjd
40168404Spjd#include "private/svn_wc_private.h"
41168404Spjd#include "private/svn_diff_tree.h"
42168404Spjd
43168404Spjd#include "wc.h"
44168404Spjd#include "props.h"
45168404Spjd#include "translate.h"
46168404Spjd#include "diff.h"
47168404Spjd
48168404Spjd#include "svn_private_config.h"
49168404Spjd
50168404Spjd/*-------------------------------------------------------------------------*/
51168404Spjd
52168404Spjd/* Baton containing the state of a directory
53168404Spjd   reported open via a diff processor */
54168404Spjdstruct node_state_t
55168404Spjd{
56168404Spjd  struct node_state_t *parent;
57168404Spjd
58168404Spjd  apr_pool_t *pool;
59168404Spjd
60168404Spjd  const char *local_abspath;
61219089Spjd  const char *relpath;
62219089Spjd  void *baton;
63219089Spjd
64168404Spjd  svn_diff_source_t *left_src;
65168404Spjd  svn_diff_source_t *right_src;
66168404Spjd  svn_diff_source_t *copy_src;
67168404Spjd
68168404Spjd  svn_boolean_t skip;
69168404Spjd  svn_boolean_t skip_children;
70168404Spjd
71168404Spjd  apr_hash_t *left_props;
72168404Spjd  apr_hash_t *right_props;
73168404Spjd  const apr_array_header_t *propchanges;
74168404Spjd};
75168404Spjd
76168404Spjd/* The diff baton */
77168404Spjdstruct diff_baton
78168404Spjd{
79168404Spjd  /* A wc db. */
80168404Spjd  svn_wc__db_t *db;
81168404Spjd
82168404Spjd  /* Report editor paths relative from this directory */
83168404Spjd  const char *anchor_abspath;
84168404Spjd
85168404Spjd  struct node_state_t *cur;
86168404Spjd
87168404Spjd  const svn_diff_tree_processor_t *processor;
88168404Spjd
89168404Spjd  /* Should this diff ignore node ancestry? */
90168404Spjd  svn_boolean_t ignore_ancestry;
91168404Spjd
92168404Spjd  /* Should this diff not compare copied files with their source? */
93168404Spjd  svn_boolean_t show_copies_as_adds;
94168404Spjd
95168404Spjd  /* Hash whose keys are const char * changelist names. */
96168404Spjd  apr_hash_t *changelist_hash;
97168404Spjd
98168404Spjd  /* Cancel function/baton */
99168404Spjd  svn_cancel_func_t cancel_func;
100168404Spjd  void *cancel_baton;
101168404Spjd
102168404Spjd  apr_pool_t *pool;
103168404Spjd};
104168404Spjd
105168404Spjd/* Recursively opens directories on the stack in EB, until LOCAL_ABSPATH
106168404Spjd   is reached. If RECURSIVE_SKIP is TRUE, don't open LOCAL_ABSPATH itself,
107168404Spjd   but create it marked with skip+skip_children.
108168404Spjd */
109168404Spjdstatic svn_error_t *
110168404Spjdensure_state(struct diff_baton *eb,
111168404Spjd             const char *local_abspath,
112168404Spjd             svn_boolean_t recursive_skip,
113168404Spjd             apr_pool_t *scratch_pool)
114168404Spjd{
115219089Spjd  struct node_state_t *ns;
116168404Spjd  apr_pool_t *ns_pool;
117168404Spjd  if (!eb->cur)
118168404Spjd    {
119168404Spjd      const char *relpath;
120168404Spjd
121168404Spjd      relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, local_abspath);
122168404Spjd      if (! relpath)
123168404Spjd        return SVN_NO_ERROR;
124168404Spjd
125168404Spjd      /* Don't recurse on the anchor, as that might loop infinately because
126168404Spjd            svn_dirent_dirname("/",...)   -> "/"
127168404Spjd            svn_dirent_dirname("C:/",...) -> "C:/" (Windows) */
128168404Spjd      if (*relpath)
129168404Spjd        SVN_ERR(ensure_state(eb,
130168404Spjd                             svn_dirent_dirname(local_abspath,scratch_pool),
131168404Spjd                             FALSE,
132168404Spjd                             scratch_pool));
133168404Spjd    }
134168404Spjd  else if (svn_dirent_is_child(eb->cur->local_abspath, local_abspath, NULL))
135168404Spjd    SVN_ERR(ensure_state(eb, svn_dirent_dirname(local_abspath,scratch_pool),
136168404Spjd                         FALSE,
137168404Spjd                         scratch_pool));
138168404Spjd  else
139168404Spjd    return SVN_NO_ERROR;
140168404Spjd
141168404Spjd  if (eb->cur && eb->cur->skip_children)
142168404Spjd    return SVN_NO_ERROR;
143168404Spjd
144168404Spjd  ns_pool = svn_pool_create(eb->cur ? eb->cur->pool : eb->pool);
145168404Spjd  ns = apr_pcalloc(ns_pool, sizeof(*ns));
146168404Spjd
147168404Spjd  ns->pool = ns_pool;
148168404Spjd  ns->local_abspath = apr_pstrdup(ns_pool, local_abspath);
149168404Spjd  ns->relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, ns->local_abspath);
150168404Spjd  ns->parent = eb->cur;
151168404Spjd  eb->cur = ns;
152168404Spjd
153168404Spjd  if (recursive_skip)
154168404Spjd    {
155168404Spjd      ns->skip = TRUE;
156168404Spjd      ns->skip_children = TRUE;
157168404Spjd      return SVN_NO_ERROR;
158168404Spjd    }
159168404Spjd
160168404Spjd  {
161168404Spjd    svn_revnum_t revision;
162168404Spjd    svn_error_t *err;
163168404Spjd
164168404Spjd    err = svn_wc__db_base_get_info(NULL, NULL, &revision, NULL, NULL, NULL,
165168404Spjd                                   NULL, NULL, NULL, NULL, NULL, NULL, NULL,
166168404Spjd                                   NULL, NULL, NULL,
167168404Spjd                                   eb->db, local_abspath,
168168404Spjd                                   scratch_pool, scratch_pool);
169168404Spjd
170168404Spjd    if (err)
171168404Spjd      {
172168404Spjd        if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
173168404Spjd          return svn_error_trace(err);
174168404Spjd        svn_error_clear(err);
175174049Sjb
176174049Sjb        revision = 0; /* Use original revision? */
177168404Spjd      }
178168404Spjd    ns->left_src = svn_diff__source_create(revision, ns->pool);
179168404Spjd    ns->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, ns->pool);
180168404Spjd
181168404Spjd    SVN_ERR(eb->processor->dir_opened(&ns->baton, &ns->skip,
182168404Spjd                                      &ns->skip_children,
183168404Spjd                                      ns->relpath,
184168404Spjd                                      ns->left_src,
185168404Spjd                                      ns->right_src,
186168404Spjd                                      NULL /* copyfrom_source */,
187168404Spjd                                      ns->parent ? ns->parent->baton : NULL,
188168404Spjd                                      eb->processor,
189168404Spjd                                      ns->pool, scratch_pool));
190168404Spjd  }
191168404Spjd
192168404Spjd  return SVN_NO_ERROR;
193168404Spjd}
194168404Spjd
195168404Spjd/* Implements svn_wc_status_func3_t */
196168404Spjdstatic svn_error_t *
197168404Spjddiff_status_callback(void *baton,
198168404Spjd                     const char *local_abspath,
199168404Spjd                     const svn_wc_status3_t *status,
200168404Spjd                     apr_pool_t *scratch_pool)
201168404Spjd{
202168404Spjd  struct diff_baton *eb = baton;
203168404Spjd  svn_wc__db_t *db = eb->db;
204168404Spjd
205168404Spjd  if (! status->versioned)
206168404Spjd    return SVN_NO_ERROR; /* unversioned (includes dir externals) */
207168404Spjd
208168404Spjd  if (status->node_status == svn_wc_status_conflicted
209168404Spjd      && status->text_status == svn_wc_status_none
210168404Spjd      && status->prop_status == svn_wc_status_none)
211168404Spjd    {
212168404Spjd      /* Node is an actual only node describing a tree conflict */
213168404Spjd      return SVN_NO_ERROR;
214168404Spjd    }
215168404Spjd
216168404Spjd  /* Not text/prop modified, not copied. Easy out */
217168404Spjd  if (status->node_status == svn_wc_status_normal && !status->copied)
218168404Spjd    return SVN_NO_ERROR;
219168404Spjd
220168404Spjd  /* Mark all directories where we are no longer inside as closed */
221168404Spjd  while (eb->cur
222168404Spjd         && !svn_dirent_is_ancestor(eb->cur->local_abspath, local_abspath))
223168404Spjd    {
224168404Spjd      struct node_state_t *ns = eb->cur;
225168404Spjd
226168404Spjd      if (!ns->skip)
227168404Spjd        {
228168404Spjd          if (ns->propchanges)
229185029Spjd            SVN_ERR(eb->processor->dir_changed(ns->relpath,
230185029Spjd                                               ns->left_src,
231168404Spjd                                               ns->right_src,
232185029Spjd                                               ns->left_props,
233185029Spjd                                               ns->right_props,
234185029Spjd                                               ns->propchanges,
235185029Spjd                                               ns->baton,
236185029Spjd                                               eb->processor,
237185029Spjd                                               ns->pool));
238185029Spjd          else
239185029Spjd            SVN_ERR(eb->processor->dir_closed(ns->relpath,
240168404Spjd                                              ns->left_src,
241185029Spjd                                              ns->right_src,
242185029Spjd                                              ns->baton,
243185029Spjd                                              eb->processor,
244185029Spjd                                              ns->pool));
245185029Spjd        }
246185029Spjd      eb->cur = ns->parent;
247168404Spjd      svn_pool_clear(ns->pool);
248185029Spjd    }
249185029Spjd  SVN_ERR(ensure_state(eb, svn_dirent_dirname(local_abspath, scratch_pool),
250185029Spjd                       FALSE, scratch_pool));
251185029Spjd
252185029Spjd  if (eb->cur && eb->cur->skip_children)
253185029Spjd    return SVN_NO_ERROR;
254185029Spjd
255168404Spjd  if (eb->changelist_hash != NULL
256185029Spjd      && (!status->changelist
257185029Spjd          || ! svn_hash_gets(eb->changelist_hash, status->changelist)))
258185029Spjd    return SVN_NO_ERROR; /* Filtered via changelist */
259185029Spjd
260185029Spjd  /* This code does about the same thing as the inner body of
261185029Spjd     walk_local_nodes_diff() in diff_editor.c, except that
262168404Spjd     it is already filtered by the status walker, doesn't have to
263185029Spjd     account for remote changes (and many tiny other details) */
264185029Spjd
265185029Spjd  {
266185029Spjd    svn_boolean_t repos_only;
267185029Spjd    svn_boolean_t local_only;
268185029Spjd    svn_wc__db_status_t db_status;
269168404Spjd    svn_boolean_t have_base;
270185029Spjd    svn_node_kind_t base_kind;
271185029Spjd    svn_node_kind_t db_kind = status->kind;
272185029Spjd    svn_depth_t depth_below_here = svn_depth_unknown;
273185029Spjd
274185029Spjd    const char *child_abspath = local_abspath;
275185029Spjd    const char *child_relpath = svn_dirent_skip_ancestor(eb->anchor_abspath,
276185029Spjd                                                         local_abspath);
277168404Spjd
278185029Spjd
279168404Spjd    repos_only = FALSE;
280185029Spjd    local_only = FALSE;
281185029Spjd
282185029Spjd    /* ### optimize away this call using status info. Should
283185029Spjd           be possible in almost every case (except conflict, missing, obst.)*/
284168404Spjd    SVN_ERR(svn_wc__db_read_info(&db_status, NULL, NULL, NULL, NULL, NULL,
285185029Spjd                                 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
286185029Spjd                                 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
287185029Spjd                                 NULL, NULL, NULL, NULL,
288185029Spjd                                 &have_base, NULL, NULL,
289185029Spjd                                 eb->db, local_abspath,
290185029Spjd                                 scratch_pool, scratch_pool));
291185029Spjd    if (!have_base)
292185029Spjd      {
293168404Spjd        local_only = TRUE; /* Only report additions */
294168404Spjd      }
295168404Spjd    else if (db_status == svn_wc__db_status_normal)
296168404Spjd      {
297168404Spjd        /* Simple diff */
298168404Spjd        base_kind = db_kind;
299168404Spjd      }
300168404Spjd    else if (db_status == svn_wc__db_status_deleted)
301168404Spjd      {
302168404Spjd        svn_wc__db_status_t base_status;
303168404Spjd        repos_only = TRUE;
304168404Spjd        SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL,
305168404Spjd                                         NULL, NULL, NULL, NULL, NULL,
306168404Spjd                                         NULL, NULL, NULL, NULL, NULL,
307168404Spjd                                         NULL, NULL, NULL,
308168404Spjd                                         eb->db, local_abspath,
309168404Spjd                                         scratch_pool, scratch_pool));
310168404Spjd
311168404Spjd        if (base_status != svn_wc__db_status_normal)
312168404Spjd          return SVN_NO_ERROR;
313168404Spjd      }
314168404Spjd    else
315168404Spjd      {
316168404Spjd        /* working status is either added or deleted */
317168404Spjd        svn_wc__db_status_t base_status;
318168404Spjd
319168404Spjd        SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL,
320168404Spjd                                         NULL, NULL, NULL, NULL, NULL,
321168404Spjd                                         NULL, NULL, NULL, NULL, NULL,
322168404Spjd                                         NULL, NULL, NULL,
323168404Spjd                                         eb->db, local_abspath,
324168404Spjd                                         scratch_pool, scratch_pool));
325168404Spjd
326168404Spjd        if (base_status != svn_wc__db_status_normal)
327168404Spjd          local_only = TRUE;
328168404Spjd        else if (base_kind != db_kind || !eb->ignore_ancestry)
329168404Spjd          {
330185029Spjd            repos_only = TRUE;
331185029Spjd            local_only = TRUE;
332185029Spjd          }
333185029Spjd      }
334185029Spjd
335185029Spjd    if (repos_only)
336185029Spjd      {
337185029Spjd        /* Report repository form deleted */
338185029Spjd        if (base_kind == svn_node_file)
339185029Spjd          SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath,
340185029Spjd                                              child_relpath,
341185029Spjd                                              SVN_INVALID_REVNUM,
342185029Spjd                                              eb->processor,
343185029Spjd                                              eb->cur ? eb->cur->baton : NULL,
344185029Spjd                                              scratch_pool));
345185029Spjd        else if (base_kind == svn_node_dir)
346185029Spjd          SVN_ERR(svn_wc__diff_base_only_dir(db, child_abspath,
347185029Spjd                                             child_relpath,
348185029Spjd                                             SVN_INVALID_REVNUM,
349185029Spjd                                             depth_below_here,
350185029Spjd                                             eb->processor,
351185029Spjd                                             eb->cur ? eb->cur->baton : NULL,
352185029Spjd                                             eb->cancel_func,
353185029Spjd                                             eb->cancel_baton,
354185029Spjd                                             scratch_pool));
355185029Spjd      }
356185029Spjd    else if (!local_only)
357185029Spjd      {
358185029Spjd        /* Diff base against actual */
359185029Spjd        if (db_kind == svn_node_file)
360185029Spjd          {
361185029Spjd            SVN_ERR(svn_wc__diff_base_working_diff(db, child_abspath,
362185029Spjd                                                   child_relpath,
363185029Spjd                                                   SVN_INVALID_REVNUM,
364185029Spjd                                                   eb->changelist_hash,
365185029Spjd                                                   eb->processor,
366185029Spjd                                                   eb->cur
367185029Spjd                                                        ? eb->cur->baton
368185029Spjd                                                        : NULL,
369185029Spjd                                                   FALSE,
370168404Spjd                                                   eb->cancel_func,
371168404Spjd                                                   eb->cancel_baton,
372168404Spjd                                                   scratch_pool));
373168404Spjd          }
374168404Spjd        else if (db_kind == svn_node_dir)
375168404Spjd          {
376168404Spjd            SVN_ERR(ensure_state(eb, local_abspath, FALSE, scratch_pool));
377219089Spjd
378219089Spjd            if (status->prop_status != svn_wc_status_none
379219089Spjd                && status->prop_status != svn_wc_status_normal)
380219089Spjd              {
381219089Spjd                apr_array_header_t *propchanges;
382219089Spjd                SVN_ERR(svn_wc__db_base_get_props(&eb->cur->left_props,
383219089Spjd                                                  eb->db, local_abspath,
384219089Spjd                                                  eb->cur->pool,
385219089Spjd                                                  scratch_pool));
386219089Spjd                SVN_ERR(svn_wc__db_read_props(&eb->cur->right_props,
387219089Spjd                                              eb->db, local_abspath,
388219089Spjd                                              eb->cur->pool,
389219089Spjd                                              scratch_pool));
390219089Spjd
391219089Spjd                SVN_ERR(svn_prop_diffs(&propchanges,
392219089Spjd                                       eb->cur->right_props,
393219089Spjd                                       eb->cur->left_props,
394219089Spjd                                       eb->cur->pool));
395219089Spjd
396219089Spjd                eb->cur->propchanges = propchanges;
397219089Spjd              }
398219089Spjd          }
399219089Spjd      }
400219089Spjd
401219089Spjd    if (local_only && (db_status != svn_wc__db_status_deleted))
402219089Spjd      {
403219089Spjd        if (db_kind == svn_node_file)
404219089Spjd          SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
405219089Spjd                                               child_relpath,
406219089Spjd                                               eb->processor,
407219089Spjd                                               eb->cur ? eb->cur->baton : NULL,
408219089Spjd                                               eb->changelist_hash,
409219089Spjd                                               FALSE,
410219089Spjd                                               eb->cancel_func,
411219089Spjd                                               eb->cancel_baton,
412265751Sdelphij                                               scratch_pool));
413265751Sdelphij        else if (db_kind == svn_node_dir)
414265751Sdelphij          SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
415265751Sdelphij                                              child_relpath, depth_below_here,
416265751Sdelphij                                              eb->processor,
417265751Sdelphij                                              eb->cur ? eb->cur->baton : NULL,
418265751Sdelphij                                              eb->changelist_hash,
419265751Sdelphij                                              FALSE,
420265751Sdelphij                                              eb->cancel_func,
421265751Sdelphij                                              eb->cancel_baton,
422265751Sdelphij                                              scratch_pool));
423265751Sdelphij      }
424265751Sdelphij
425265751Sdelphij    if (db_kind == svn_node_dir && (local_only || repos_only))
426265751Sdelphij      SVN_ERR(ensure_state(eb, local_abspath, TRUE /* skip */, scratch_pool));
427265751Sdelphij  }
428265751Sdelphij
429265751Sdelphij  return SVN_NO_ERROR;
430265751Sdelphij}
431265751Sdelphij
432265751Sdelphij
433265751Sdelphij/* Public Interface */
434265751Sdelphijsvn_error_t *
435265751Sdelphijsvn_wc_diff6(svn_wc_context_t *wc_ctx,
436265751Sdelphij             const char *local_abspath,
437265751Sdelphij             const svn_wc_diff_callbacks4_t *callbacks,
438265751Sdelphij             void *callback_baton,
439265751Sdelphij             svn_depth_t depth,
440265751Sdelphij             svn_boolean_t ignore_ancestry,
441265751Sdelphij             svn_boolean_t show_copies_as_adds,
442265751Sdelphij             svn_boolean_t use_git_diff_format,
443265751Sdelphij             const apr_array_header_t *changelist_filter,
444168404Spjd             svn_cancel_func_t cancel_func,
445168404Spjd             void *cancel_baton,
446168404Spjd             apr_pool_t *scratch_pool)
447168404Spjd{
448168404Spjd  struct diff_baton eb = { 0 };
449  svn_node_kind_t kind;
450  svn_boolean_t get_all;
451  const svn_diff_tree_processor_t *processor;
452
453  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
454  SVN_ERR(svn_wc__db_read_kind(&kind, wc_ctx->db, local_abspath,
455                               FALSE /* allow_missing */,
456                               TRUE /* show_deleted */,
457                               FALSE /* show_hidden */,
458                               scratch_pool));
459
460  if (kind == svn_node_dir)
461    eb.anchor_abspath = local_abspath;
462  else
463    eb.anchor_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
464
465  SVN_ERR(svn_wc__wrap_diff_callbacks(&processor,
466                                      callbacks, callback_baton, TRUE,
467                                      scratch_pool, scratch_pool));
468
469  if (use_git_diff_format)
470    show_copies_as_adds = TRUE;
471  if (show_copies_as_adds)
472    ignore_ancestry = FALSE;
473
474
475
476  /*
477  if (reverse_order)
478    processor = svn_diff__tree_processor_reverse_create(processor, NULL, pool);
479   */
480
481  if (! show_copies_as_adds && !use_git_diff_format)
482    processor = svn_diff__tree_processor_copy_as_changed_create(processor,
483                                                                scratch_pool);
484
485  eb.db = wc_ctx->db;
486  eb.processor = processor;
487  eb.ignore_ancestry = ignore_ancestry;
488  eb.show_copies_as_adds = show_copies_as_adds;
489  eb.pool = scratch_pool;
490
491  if (changelist_filter && changelist_filter->nelts)
492    SVN_ERR(svn_hash_from_cstring_keys(&eb.changelist_hash, changelist_filter,
493                                       scratch_pool));
494
495  if (show_copies_as_adds || use_git_diff_format || !ignore_ancestry)
496    get_all = TRUE; /* We need unmodified descendants of copies */
497  else
498    get_all = FALSE;
499
500  /* Walk status handles files and directories */
501  SVN_ERR(svn_wc__internal_walk_status(wc_ctx->db, local_abspath, depth,
502                                       get_all,
503                                       TRUE /* no_ignore */,
504                                       FALSE /* ignore_text_mods */,
505                                       NULL /* ignore_patterns */,
506                                       diff_status_callback, &eb,
507                                       cancel_func, cancel_baton,
508                                       scratch_pool));
509
510  /* Close the remaining open directories */
511  while (eb.cur)
512    {
513      struct node_state_t *ns = eb.cur;
514
515      if (!ns->skip)
516        {
517          if (ns->propchanges)
518            SVN_ERR(processor->dir_changed(ns->relpath,
519                                           ns->left_src,
520                                           ns->right_src,
521                                           ns->left_props,
522                                           ns->right_props,
523                                           ns->propchanges,
524                                           ns->baton,
525                                           processor,
526                                           ns->pool));
527          else
528            SVN_ERR(processor->dir_closed(ns->relpath,
529                                          ns->left_src,
530                                          ns->right_src,
531                                          ns->baton,
532                                          processor,
533                                          ns->pool));
534        }
535      eb.cur = ns->parent;
536      svn_pool_clear(ns->pool);
537    }
538
539  return SVN_NO_ERROR;
540}
541