⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 diff.c

📁 linux subdivision ying gai ke yi le ba
💻 C
📖 第 1 页 / 共 4 页
字号:
/*
 * diff.c -- The diff editor for comparing the working copy against the
 *           repository.
 *
 * ====================================================================
 * Copyright (c) 2000-2004 CollabNet.  All rights reserved.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution.  The terms
 * are also available at http://subversion.tigris.org/license-1.html.
 * If newer versions of this license are posted there, you may use a
 * newer version instead, at your option.
 *
 * This software consists of voluntary contributions made by many
 * individuals.  For exact contribution history, see the revision
 * history and logs, available at http://subversion.tigris.org/.
 * ====================================================================
 */

/*
 * This code uses an svn_delta_editor_t editor driven by
 * svn_wc_crawl_revisions (like the update command) to retrieve the
 * differences between the working copy and the requested repository
 * version. Rather than updating the working copy, this new editor creates
 * temporary files that contain the pristine repository versions. When the
 * crawler closes the files the editor calls back to a client layer
 * function to compare the working copy and the temporary file. There is
 * only ever one temporary file in existence at any time.
 *
 * When the crawler closes a directory, the editor then calls back to the
 * client layer to compare any remaining files that may have been modified
 * locally. Added directories do not have corresponding temporary
 * directories created, as they are not needed.
 *
 * ### TODO: It might be better if the temporary files were not created in
 * the admin's temp area, but in a more general area (/tmp, $TMPDIR) as
 * then diff could be run on a read-only working copy.
 *
 * ### TODO: Replacements where the node kind changes needs support. It
 * mostly works when the change is in the repository, but not when it is
 * in the working copy.
 *
 * ### TODO: Do we need to support copyfrom?
 *
 */

#include <assert.h>

#include <apr_hash.h>

#include "svn_pools.h"
#include "svn_path.h"

#include "wc.h"
#include "props.h"
#include "adm_files.h"


/*-------------------------------------------------------------------------*/
/* A little helper function.

   You see, when we ask the server to update us to a certain revision,
   we construct the new fulltext, and then run 

         'diff <repos_fulltext> <working_fulltext>'

   which is, of course, actually backwards from the repository's point
   of view.  It thinks we want to move from working->repos.

   So when the server sends property changes, they're effectively
   backwards from what we want.  We don't want working->repos, but
   repos->working.  So this little helper "reverses" the value in
   BASEPROPS and PROPCHANGES before we pass them off to the
   prop_changed() diff-callback.  */
static void
reverse_propchanges (apr_hash_t *baseprops,
                     apr_array_header_t *propchanges,
                     apr_pool_t *pool)
{
  int i;

  /* ### todo: research lifetimes for property values below */

  for (i = 0; i < propchanges->nelts; i++)
    {
      svn_prop_t *propchange
        = &APR_ARRAY_IDX (propchanges, i, svn_prop_t);
      
      const svn_string_t *original_value =
        apr_hash_get (baseprops, propchange->name, APR_HASH_KEY_STRING);
     
      if ((original_value == NULL) && (propchange->value != NULL)) 
        {
          /* found an addition.  make it look like a deletion. */
          apr_hash_set (baseprops, propchange->name, APR_HASH_KEY_STRING,
                        svn_string_dup (propchange->value, pool));
          propchange->value = NULL;
        }

      else if ((original_value != NULL) && (propchange->value == NULL)) 
        {
          /* found a deletion.  make it look like an addition. */
          propchange->value = svn_string_dup (original_value, pool);
          apr_hash_set (baseprops, propchange->name, APR_HASH_KEY_STRING,
                        NULL);
        }

      else if ((original_value != NULL) && (propchange->value != NULL)) 
        {
          /* found a change.  just swap the values.  */
          const svn_string_t *str = svn_string_dup (propchange->value, pool);
          propchange->value = svn_string_dup (original_value, pool);
          apr_hash_set (baseprops, propchange->name, APR_HASH_KEY_STRING, str);
        }
    }
}


/*-------------------------------------------------------------------------*/

/* Overall crawler editor baton.
 */
struct edit_baton {
  /* ANCHOR/TARGET represent the base of the hierarchy to be compared. */
  svn_wc_adm_access_t *anchor;
  const char *anchor_path;
  const char *target;

  /* Target revision */
  svn_revnum_t revnum;

  /* Was the root opened? */
  svn_boolean_t root_opened;

  /* The callbacks and callback argument that implement the file comparison
     functions */
  const svn_wc_diff_callbacks_t *callbacks;
  void *callback_baton;

  /* Flags whether to diff recursively or not. If set the diff is
     recursive. */
  svn_boolean_t recurse;

  /* Should this diff ignore node ancestry. */
  svn_boolean_t ignore_ancestry;

  /* Possibly diff repos against text-bases instead of working files. */
  svn_boolean_t use_text_base;

  /* Possibly show the diffs backwards. */
  svn_boolean_t reverse_order;

  apr_pool_t *pool;
};

/* Directory level baton.
 */
struct dir_baton {
  /* Gets set if the directory is added rather than replaced/unchanged. */
  svn_boolean_t added;

  /* The "correct" path of the directory, but it may not exist in the
     working copy. */
  const char *path;

 /* Identifies those directory elements that get compared while running the
    crawler. These elements should not be compared again when recursively
    looking for local only diffs. */
  apr_hash_t *compared;

  /* The baton for the parent directory, or null if this is the root of the
     hierarchy to be compared. */
  struct dir_baton *dir_baton;

  /* The original property hash, and the list of incoming propchanges. */
  apr_hash_t *baseprops;
  apr_array_header_t *propchanges;
  svn_boolean_t fetched_baseprops; /* did we get the working props yet? */

  /* The overall crawler editor baton. */
  struct edit_baton *edit_baton;

  apr_pool_t *pool;
};

/* File level baton.
 */
struct file_baton {
  /* Gets set if the file is added rather than replaced. */
  svn_boolean_t added;

  /* PATH is the "correct" path of the file, but it may not exist in the
     working copy.  WC_PATH is a path we can use to make temporary files
     or open empty files; it doesn't necessarily exist either, but the
     directory part of it does. */
  const char *path;
  const char *wc_path;

 /* When constructing the requested repository version of the file,
    ORIGINAL_FILE is version of the file in the working copy. TEMP_FILE is
    the pristine repository file obtained by applying the repository diffs
    to ORIGINAL_FILE. */
  apr_file_t *original_file;
  apr_file_t *temp_file;

  /* The original property hash, and the list of incoming propchanges. */
  apr_hash_t *baseprops;
  apr_array_header_t *propchanges;
  svn_boolean_t fetched_baseprops; /* did we get the working props yet? */

  /* APPLY_HANDLER/APPLY_BATON represent the delta applcation baton. */
  svn_txdelta_window_handler_t apply_handler;
  void *apply_baton;

  /* Is this file scheduled to be deleted? */
  svn_boolean_t schedule_delete;

  /* The overall crawler editor baton. */
  struct edit_baton *edit_baton;

  apr_pool_t *pool;
};


/* Create a new edit baton. TARGET/ANCHOR are working copy paths that
 * describe the root of the comparison. CALLBACKS/CALLBACK_BATON
 * define the callbacks to compare files. RECURSE defines whether to
 * descend into subdirectories.  IGNORE_ANCESTRY defines whether to
 * utilize node ancestry when calculating diffs.  USE_TEXT_BASE
 * defines whether to compare against working files or text-bases.
 * REVERSE_ORDER defines which direction to perform the diff.
 */
static struct edit_baton *
make_editor_baton (svn_wc_adm_access_t *anchor,
                   const char *target,
                   const svn_wc_diff_callbacks_t *callbacks,
                   void *callback_baton,
                   svn_boolean_t recurse,
                   svn_boolean_t ignore_ancestry,
                   svn_boolean_t use_text_base,
                   svn_boolean_t reverse_order,
                   apr_pool_t *pool)
{
  struct edit_baton *eb = apr_pcalloc (pool, sizeof (*eb));

  eb->anchor = anchor;
  eb->anchor_path = svn_wc_adm_access_path (anchor);
  eb->target = apr_pstrdup (pool, target);
  eb->callbacks = callbacks;
  eb->callback_baton = callback_baton;
  eb->recurse = recurse;
  eb->ignore_ancestry = ignore_ancestry;
  eb->use_text_base = use_text_base;
  eb->reverse_order = reverse_order;
  eb->pool = pool;

  return eb;
}


/* Create a new directory baton.  PATH is the directory path,
 * including anchor_path.  ADDED is set if this directory is being
 * added rather than replaced.  PARENT_BATON is the baton of the
 * parent directory, it will be null if this is the root of the
 * comparison hierarchy.  The directory and its parent may or may not
 * exist in the working copy.  EDIT_BATON is the overall crawler
 * editor baton.
 */
static struct dir_baton *
make_dir_baton (const char *path,
                struct dir_baton *parent_baton,
                struct edit_baton *edit_baton,
                svn_boolean_t added,
                apr_pool_t *pool)
{
  struct dir_baton *dir_baton = apr_pcalloc (pool, sizeof (*dir_baton));

  dir_baton->dir_baton = parent_baton;
  dir_baton->edit_baton = edit_baton;
  dir_baton->added = added;
  dir_baton->pool = pool;
  dir_baton->baseprops = apr_hash_make (dir_baton->pool);
  dir_baton->propchanges = apr_array_make (pool, 1, sizeof (svn_prop_t));
  dir_baton->compared = apr_hash_make (dir_baton->pool);
  dir_baton->path = path;

  return dir_baton;
}

/* Create a new file baton.  PATH is the file path, including
 * anchor_path.  ADDED is set if this file is being added rather than
 * replaced.  PARENT_BATON is the baton of the parent directory.  The
 * directory and its parent may or may not exist in the working copy.
 */
static struct file_baton *
make_file_baton (const char *path,
                 svn_boolean_t added,
                 struct dir_baton *parent_baton,
                 apr_pool_t *pool)
{
  struct file_baton *file_baton = apr_pcalloc (pool, sizeof (*file_baton));
  struct edit_baton *edit_baton = parent_baton->edit_baton;

  file_baton->edit_baton = edit_baton;
  file_baton->added = added;
  file_baton->pool = pool;
  file_baton->baseprops = apr_hash_make (file_baton->pool);
  file_baton->propchanges  = apr_array_make (pool, 1, sizeof (svn_prop_t));
  file_baton->path = path;
  file_baton->schedule_delete = FALSE;

  /* If the parent directory is added rather than replaced it does not
     exist in the working copy.  Determine a working copy path whose
     directory part does exist; we can use that to create temporary
     files.  It doesn't matter whether the file part exists in the
     directory. */
  if (parent_baton->added)
    {
      struct dir_baton *wc_dir_baton = parent_baton;

      /* Ascend until a directory is not being added, this will be a
         directory that does exist. This must terminate since the root of
         the comparison cannot be added. */
      while (wc_dir_baton->added)
        wc_dir_baton = wc_dir_baton->dir_baton;

      file_baton->wc_path = svn_path_join (wc_dir_baton->path, "unimportant",
                                           file_baton->pool);
    }
  else
    {
      file_baton->wc_path = file_baton->path;
    }

  return file_baton;
}


/* Helper function:  load a file_baton's base_props. */
static void
load_base_props (struct file_baton *b)
{
  /* the 'base' props to compare against, in this case, are
     actually the working props.  that's what we do with texts,
     anyway, in the 'svn diff -rN foo' case.  */
  
  /* also notice we're ignoring error here;  there's a chance that
     this path might not exist in the working copy, in which case
     the baseprops remains an empty hash. */
  svn_error_t *err = svn_wc_prop_list (&(b->baseprops), b->path,
                                       b->edit_baton->anchor, b->pool);
  if (err)
    svn_error_clear (err);
  b->fetched_baseprops = TRUE;
}


/* Helper function for retrieving svn:mime-type properties, if
   present, on file PATH.  File baton B is optional:  if present,
   assume it refers to PATH, and use its caching abilities.

   If PRISTINE_MIMETYPE is non-NULL, then set *PRISTINE_MIMETYPE to
   the value of svn:mime-type if available.  Else set to NULL.  Search

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -