merge.c

来自「linux subdivision ying gai ke yi le ba」· C语言 代码 · 共 616 行 · 第 1/2 页

C
616
字号
/*
 * merge.c :  routines for performing a MERGE server requests
 *
 * ====================================================================
 * 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/.
 * ====================================================================
 */



#include <apr_pools.h>
#include <apr_hash.h>
#define APR_WANT_STRFUNC
#include <apr_want.h>

#include <ne_socket.h>
#include <ne_xml.h>
#include <ne_request.h>


#include "svn_string.h"
#include "svn_error.h"
#include "svn_path.h"
#include "svn_ra.h"
#include "svn_pools.h"

#include "svn_private_config.h"

#include "ra_dav.h"


static const svn_ra_dav__xml_elm_t merge_elements[] =
{
  { "DAV:", "updated-set", ELEM_updated_set, 0 },
  { "DAV:", "merged-set", ELEM_merged_set, 0 },
  { "DAV:", "ignored-set", ELEM_ignored_set, 0 },
  { "DAV:", "href", ELEM_href, SVN_RA_DAV__XML_CDATA },
  { "DAV:", "merge-response", ELEM_merge_response, 0 },
  { "DAV:", "checked-in", ELEM_checked_in, 0 },
  { "DAV:", "response", ELEM_response, 0 },
  { "DAV:", "propstat", ELEM_propstat, 0 },
  { "DAV:", "status", ELEM_status, SVN_RA_DAV__XML_CDATA },
  { "DAV:", "responsedescription", ELEM_responsedescription,
    SVN_RA_DAV__XML_CDATA },
  { "DAV:", "prop", ELEM_prop, 0 },
  { "DAV:", "resourcetype", ELEM_resourcetype, 0 },
  { "DAV:", "collection", ELEM_collection, 0 },
  { "DAV:", "baseline", ELEM_baseline, 0 },
  { "DAV:", "version-name", ELEM_version_name, SVN_RA_DAV__XML_CDATA },
  { "DAV:", "creationdate", ELEM_creationdate, SVN_RA_DAV__XML_CDATA },
  { "DAV:", "creator-displayname", ELEM_creator_displayname,
    SVN_RA_DAV__XML_CDATA },

  { NULL }
};

enum merge_rtype {
  RTYPE_UNKNOWN,    /* unknown (haven't seen it in the response yet) */
  RTYPE_REGULAR,    /* a regular (member) resource */
  RTYPE_COLLECTION, /* a collection resource */
  RTYPE_BASELINE    /* a baseline resource */
};

typedef struct {
  apr_pool_t *pool;

  /* a clearable subpool of pool, for loops.  Do not use for anything
     that must persist beyond the scope of your function! */
  apr_pool_t *scratchpool;

  /* any error that may have occurred during the MERGE response handling */
  svn_error_t *err;

  /* the BASE_HREF contains the merge target. as resources are specified in
     the merge response, we make their URLs relative to this URL, thus giving
     us a path for use in the commit callbacks. */
  const char *base_href;
  apr_size_t base_len;

  svn_revnum_t rev;        /* the new/target revision number for this commit */

  svn_boolean_t response_has_error;
  int response_parent;     /* what element did DAV:response appear within? */

  int href_parent;         /* what element is the DAV:href appearing within? */
  svn_stringbuf_t *href;   /* current response */

  int status;              /* HTTP status for this DAV:propstat */
  enum merge_rtype rtype;  /* DAV:resourcetype of this resource */

  svn_stringbuf_t *vsn_name;       /* DAV:version-name for this resource */
  svn_stringbuf_t *vsn_url;        /* DAV:checked-in for this resource */
  svn_stringbuf_t *committed_date; /* DAV:creationdate for this resource */
  svn_stringbuf_t *last_author;    /* DAV:creator-displayname for this
                                      resource */

  /* We only invoke set_prop() on targets listed in valid_targets.
     Some entities (such as directories that have had changes
     committed underneath but are not themselves targets) will be
     mentioned in the merge response but not appear in
     valid_targets. */
  apr_hash_t *valid_targets;

  /* Client callbacks */
  svn_ra_push_wc_prop_func_t push_prop;
  void *cb_baton;  /* baton for above */

} merge_ctx_t;


static void add_ignored(merge_ctx_t *mc, const char *cdata)
{
  /* ### the server didn't check in the file(!) */
  /* ### remember the file and issue a report/warning later */
}


static svn_boolean_t okay_to_bump_path (const char *path,
                                        apr_hash_t *valid_targets,
                                        apr_pool_t *pool)
{
  svn_stringbuf_t *parent_path;
  enum svn_recurse_kind r;

  /* Easy check:  if path itself is in the hash, then it's legit. */
  if (apr_hash_get (valid_targets, path, APR_HASH_KEY_STRING))
    return TRUE;

  /* Otherwise, this path is bumpable IFF one of its parents is in the
     hash and marked with a 'recursion' flag. */
  parent_path = svn_stringbuf_create (path, pool);
  
  do {
    apr_size_t len = parent_path->len;
    svn_path_remove_component (parent_path);
    if (len == parent_path->len)
      break;
    r = (enum svn_recurse_kind) apr_hash_get (valid_targets,
                                              parent_path->data,
                                              APR_HASH_KEY_STRING);
    if (r == svn_recursive)
      return TRUE;

  } while (! svn_path_is_empty (parent_path->data));

  /* Default answer: if we get here, don't allow the bumping. */
  return FALSE;
}


/* If committed PATH appears in MC->valid_targets, and an MC->push_prop
 * function exists, then store VSN_URL as the SVN_RA_DAV__LP_VSN_URL
 * property on PATH.  Use POOL for all allocations. 
 *
 * Otherwise, just return SVN_NO_ERROR.
 */
static svn_error_t *bump_resource(merge_ctx_t *mc,
                                  const char *path,
                                  char *vsn_url,
                                  apr_pool_t *pool)
{
  /* no sense in doing any more work if there's no property setting
     function at our disposal. */
  if (mc->push_prop == NULL)
    return SVN_NO_ERROR;

  /* Only invoke a client callback on PATH if PATH counts as a
     committed target.  The commit-tracking editor built this list for
     us, and took care not to include directories unless they were
     directly committed (i.e., received a property change). */
  if (! okay_to_bump_path (path, mc->valid_targets, pool))
    return SVN_NO_ERROR;

  /* Okay, NOW set the new version url. */
  {
    svn_string_t vsn_url_str;  /* prop setter wants an svn_string_t */

    vsn_url_str.data = vsn_url;
    vsn_url_str.len = strlen(vsn_url);

    SVN_ERR( (*mc->push_prop)(mc->cb_baton, path,
                              SVN_RA_DAV__LP_VSN_URL, &vsn_url_str,
                              pool) );
  }

  return SVN_NO_ERROR;
}

static svn_error_t * handle_resource(merge_ctx_t *mc,
                                     apr_pool_t *pool)
{
  const char *relative;

  if (mc->response_has_error)
    {
      /* ### what to do? */
      /* ### return "no error", presuming whatever set response_has_error
         ### has already handled the problem. */
      return SVN_NO_ERROR;
    }
  if (mc->response_parent == ELEM_merged_set)
    {
      /* ### shouldn't have happened. we told the server "don't merge" */
      /* ### need something better than APR_EGENERAL */
      return svn_error_createf(APR_EGENERAL, NULL,
                               _("Protocol error: we told the server not to "
                                 "auto-merge any resources, but it said that "
                                 "'%s' was merged"), mc->href->data);
    }
  if (mc->response_parent != ELEM_updated_set)
    {
      /* ### unknown parent for this response(!) */
      /* ### need something better than APR_EGENERAL */
      return svn_error_createf(APR_EGENERAL, NULL,
                               _("Internal error: there is an unknown parent "
                                 "(%d) for the 'DAV:response' element within"
                                 " the MERGE response"), mc->response_parent);
    }
#if 0
  /* ### right now, the server isn't sending everything for all resources.
     ### just skip this requirement. */
  if (mc->href->len == 0
      || mc->vsn_name->len == 0
      || mc->vsn_url->len == 0
      || mc->rtype == RTYPE_UNKNOWN)
    {
      /* one or more properties were missing in the DAV:response for the
         resource. */
      return svn_error_createf(APR_EGENERAL, NULL,
                               _("Protocol error: the MERGE response for the "
                                 "'%s' resource did not return all of the "
                                 "properties that we asked for (and need to "
                                 "complete the commit)"), mc->href->data);
    }
#endif

  if (mc->rtype == RTYPE_BASELINE)
    {
      /* cool. the DAV:version-name tells us the new revision */
      mc->rev = SVN_STR_TO_REV(mc->vsn_name->data);
      return SVN_NO_ERROR;
    }

  /* a collection or regular resource */
  if (mc->href->len < mc->base_len)
    {
      /* ### need something better than APR_EGENERAL */
      return svn_error_createf(APR_EGENERAL, NULL,
                               _("A MERGE response for '%s' is not a child "
                                 "of the destination ('%s')"),
                               mc->href->data, mc->base_href);
    }

  /* given HREF of the form: BASE "/" RELATIVE, extract the relative portion */
  if (mc->base_len == mc->href->len)
    relative = "";
  else
    relative = mc->href->data + mc->base_len + 1;

  /* bump the resource */
  relative = svn_path_uri_decode (relative, pool);
  return bump_resource(mc, relative, mc->vsn_url->data, pool);
}

static int validate_element(void *userdata, svn_ra_dav__xml_elmid parent,
                            svn_ra_dav__xml_elmid child)
{
  if ((child == ELEM_collection || child == ELEM_baseline)
      && parent != ELEM_resourcetype) {
    /* ### technically, they could occur elsewhere, but screw it */
    return SVN_RA_DAV__XML_INVALID;
  }

  switch (parent)
    {
    case ELEM_root:
      if (child == ELEM_merge_response)
        return SVN_RA_DAV__XML_VALID;
      else
        return SVN_RA_DAV__XML_INVALID;

    case ELEM_merge_response:
      if (child == ELEM_updated_set
          || child == ELEM_merged_set
          || child == ELEM_ignored_set)
        return SVN_RA_DAV__XML_VALID;
      else
        return SVN_RA_DAV__XML_DECLINE; /* any child is allowed */

    case ELEM_updated_set:
    case ELEM_merged_set:
      if (child == ELEM_response)
        return SVN_RA_DAV__XML_VALID;
      else
        return SVN_RA_DAV__XML_DECLINE; /* ignore if something else
                                           was in there */

    case ELEM_ignored_set:

⌨️ 快捷键说明

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