📄 adm_ops.c
字号:
/*
* adm_ops.c: routines for affecting working copy administrative
* information. NOTE: this code doesn't know where the adm
* info is actually stored. Instead, generic handles to
* adm data are requested via a reference to some PATH
* (PATH being a regular, non-administrative directory or
* file in the working copy).
*
* ====================================================================
* 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 <string.h>
#include <apr_pools.h>
#include <apr_hash.h>
#include <apr_md5.h>
#include <apr_file_io.h>
#include <apr_time.h>
#include "svn_types.h"
#include "svn_pools.h"
#include "svn_string.h"
#include "svn_error.h"
#include "svn_hash.h"
#include "svn_path.h"
#include "svn_wc.h"
#include "svn_io.h"
#include "svn_md5.h"
#include "svn_xml.h"
#include "wc.h"
#include "log.h"
#include "adm_files.h"
#include "adm_ops.h"
#include "entries.h"
#include "props.h"
#include "translate.h"
#include "svn_private_config.h"
/*** Finishing updates and commits. ***/
/* The main body of svn_wc__do_update_cleanup. */
static svn_error_t *
tweak_entries (svn_wc_adm_access_t *dirpath,
const char *base_url,
svn_revnum_t new_rev,
svn_wc_notify_func_t notify_func,
void *notify_baton,
svn_boolean_t remove_missing_dirs,
svn_boolean_t recurse,
apr_pool_t *pool)
{
apr_hash_t *entries;
apr_hash_index_t *hi;
apr_pool_t *subpool = svn_pool_create (pool);
svn_boolean_t write_required = FALSE;
/* Read DIRPATH's entries. */
SVN_ERR (svn_wc_entries_read (&entries, dirpath, TRUE, pool));
/* Tweak "this_dir" */
SVN_ERR (svn_wc__tweak_entry (entries, SVN_WC_ENTRY_THIS_DIR,
base_url, new_rev, &write_required,
svn_wc_adm_access_pool (dirpath)));
/* Recursively loop over all children. */
for (hi = apr_hash_first (pool, entries); hi; hi = apr_hash_next (hi))
{
const void *key;
void *val;
const char *name;
svn_wc_entry_t *current_entry;
const char *child_url = NULL;
svn_pool_clear (subpool);
apr_hash_this (hi, &key, NULL, &val);
name = key;
current_entry = val;
/* Ignore the "this dir" entry. */
if (! strcmp (name, SVN_WC_ENTRY_THIS_DIR))
continue;
/* Derive the new URL for the current (child) entry */
if (base_url)
child_url = svn_path_url_add_component (base_url, name, subpool);
/* If a file, or deleted or absent dir in recursive mode, then tweak the
entry but don't recurse. */
if ((current_entry->kind == svn_node_file)
|| (recurse && (current_entry->deleted || current_entry->absent)))
{
SVN_ERR (svn_wc__tweak_entry (entries, name,
child_url, new_rev, &write_required,
svn_wc_adm_access_pool (dirpath)));
}
/* If a directory and recursive... */
else if (recurse && (current_entry->kind == svn_node_dir))
{
const char *child_path
= svn_path_join (svn_wc_adm_access_path (dirpath), name, subpool);
/* If the directory is 'missing', remove it. This is safe as
long as this function is only called as a helper to
svn_wc__do_update_cleanup, since the update will already have
restored any missing items that it didn't want to delete. */
if (remove_missing_dirs
&& svn_wc__adm_missing (dirpath, child_path))
{
if (current_entry->schedule != svn_wc_schedule_add)
{
svn_wc__entry_remove (entries, name);
if (notify_func)
(* notify_func) (notify_baton, child_path,
svn_wc_notify_delete,
current_entry->kind, NULL,
svn_wc_notify_state_unknown,
svn_wc_notify_state_unknown,
SVN_INVALID_REVNUM);
}
/* Else if missing item is schedule-add, do nothing. */
}
/* Not missing, deleted, or absent, so recurse. */
else
{
svn_wc_adm_access_t *child_access;
SVN_ERR (svn_wc_adm_retrieve (&child_access, dirpath, child_path,
subpool));
SVN_ERR (tweak_entries
(child_access, child_url, new_rev, notify_func,
notify_baton, remove_missing_dirs, recurse, subpool));
}
}
}
/* Write a shiny new entries file to disk. */
if (write_required)
SVN_ERR (svn_wc__entries_write (entries, dirpath, subpool));
/* Cleanup */
svn_pool_destroy (subpool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__do_update_cleanup (const char *path,
svn_wc_adm_access_t *adm_access,
svn_boolean_t recursive,
const char *base_url,
svn_revnum_t new_revision,
svn_wc_notify_func_t notify_func,
void *notify_baton,
svn_boolean_t remove_missing_dirs,
apr_pool_t *pool)
{
apr_hash_t *entries;
const svn_wc_entry_t *entry;
SVN_ERR (svn_wc_entry (&entry, path, adm_access, TRUE, pool));
if (entry == NULL)
return SVN_NO_ERROR;
if (entry->kind == svn_node_file)
{
const char *parent, *base_name;
svn_wc_adm_access_t *dir_access;
svn_boolean_t write_required = FALSE;
svn_path_split (path, &parent, &base_name, pool);
SVN_ERR (svn_wc_adm_retrieve (&dir_access, adm_access, parent, pool));
SVN_ERR (svn_wc_entries_read (&entries, dir_access, TRUE, pool));
SVN_ERR (svn_wc__tweak_entry (entries, base_name,
base_url, new_revision, &write_required,
svn_wc_adm_access_pool (dir_access)));
if (write_required)
SVN_ERR (svn_wc__entries_write (entries, dir_access, pool));
}
else if (entry->kind == svn_node_dir)
{
svn_wc_adm_access_t *dir_access;
SVN_ERR (svn_wc_adm_retrieve (&dir_access, adm_access, path, pool));
SVN_ERR (tweak_entries (dir_access, base_url, new_revision,
notify_func, notify_baton, remove_missing_dirs,
recursive, pool));
}
else
return svn_error_createf (SVN_ERR_NODE_UNKNOWN_KIND, NULL,
_("Unrecognized node kind: '%s'"), path);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc_process_committed (const char *path,
svn_wc_adm_access_t *adm_access,
svn_boolean_t recurse,
svn_revnum_t new_revnum,
const char *rev_date,
const char *rev_author,
apr_array_header_t *wcprop_changes,
apr_pool_t *pool)
{
const char *base_name;
svn_stringbuf_t *logtags;
apr_file_t *log_fp = NULL;
char *revstr = apr_psprintf (pool, "%ld", new_revnum);
const char *hex_digest = NULL;
SVN_ERR (svn_wc__adm_write_check (adm_access));
/* Set PATH's working revision to NEW_REVNUM; if REV_DATE and
REV_AUTHOR are both non-NULL, then set the 'committed-rev',
'committed-date', and 'last-author' entry values; and set the
checksum if a file. */
/* Open a log file in the administrative directory */
SVN_ERR (svn_wc__open_adm_file (&log_fp,
svn_wc_adm_access_path (adm_access),
SVN_WC__ADM_LOG,
(APR_WRITE | APR_CREATE),
pool));
base_name = svn_path_is_child (svn_wc_adm_access_path (adm_access), path,
pool);
if (base_name)
{
/* PATH must be some sort of file */
const char *latest_base;
svn_node_kind_t kind;
/* There may be a new text base is sitting in the adm tmp area by
now, because the commit succeeded. A file that is copied, but not
otherwise modified, doesn't have a new text base, so we use
the unmodified text base.
### Does this mean that a file committed with only prop mods
### will still get its text base checksum recomputed? Yes it
### does, sadly. But it's not enough to just check for that
### condition, because in the case of an added file, there
### may not be a pre-existing checksum in the entry.
### Probably the best solution is to compute (or copy) the
### checksum at 'svn add' (or 'svn cp') time, instead of
### waiting until commit time.
*/
latest_base = svn_wc__text_base_path (path, TRUE, pool);
SVN_ERR (svn_io_check_path (latest_base, &kind, pool));
if (kind == svn_node_none)
{
latest_base = svn_wc__text_base_path (path, FALSE, pool);
SVN_ERR (svn_io_check_path (latest_base, &kind, pool));
}
if (kind == svn_node_file)
{
/* It would be more efficient to compute the checksum as part of
some other operation that has to process all the bytes anyway
(such as copying or translation). But that would make a lot
of other code more complex, since the relevant copy and/or
translation operations happened elsewhere, a long time ago.
If we were to obtain the checksum then/there, we'd still have
to somehow preserve it until now/here, which would result in
unexpected and hard-to-maintain dependencies. Ick.
So instead we just do the checksum from scratch. Ick. */
unsigned char digest[APR_MD5_DIGESTSIZE];
SVN_ERR (svn_io_file_checksum (digest, latest_base, pool));
hex_digest = svn_md5_digest_to_cstring (digest, pool);
}
/* Oh, and recursing at this point isn't really sensible. */
recurse = FALSE;
}
else
{
/* PATH must be a dir */
base_name = SVN_WC_ENTRY_THIS_DIR;
}
logtags = svn_stringbuf_create ("", pool);
/* Append a log command to set (overwrite) the 'committed-rev',
'committed-date', 'last-author', and possibly `checksum'
attributes in the entry.
Note: it's important that this log command come *before* the
LOG_COMMITTED command, because log_do_committed() might actually
remove the entry! */
if (rev_date)
svn_xml_make_open_tag (&logtags, pool, svn_xml_self_closing,
SVN_WC__LOG_MODIFY_ENTRY,
SVN_WC__LOG_ATTR_NAME, base_name,
SVN_WC__ENTRY_ATTR_CMT_REV,
revstr,
SVN_WC__ENTRY_ATTR_CMT_DATE,
rev_date,
rev_author ? SVN_WC__ENTRY_ATTR_CMT_AUTHOR : NULL,
rev_author,
NULL);
else if (rev_author)
svn_xml_make_open_tag (&logtags, pool, svn_xml_self_closing,
SVN_WC__LOG_MODIFY_ENTRY,
SVN_WC__LOG_ATTR_NAME, base_name,
SVN_WC__ENTRY_ATTR_CMT_REV,
revstr,
SVN_WC__ENTRY_ATTR_CMT_AUTHOR,
rev_author,
NULL);
if (hex_digest)
svn_xml_make_open_tag (&logtags, pool, svn_xml_self_closing,
SVN_WC__LOG_MODIFY_ENTRY,
SVN_WC__LOG_ATTR_NAME, base_name,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -