[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

[PATCH][merge-tracking]sqlite mergeinfo indexing for bdb repositories

From: Kamesh Jayachandran <kamesh_at_collab.net>
Date: 2006-10-06 12:49:05 CEST

Hi All,
Find the attached patch and log.

With regards
Kamesh Jayachandran

Index: subversion/libsvn_fs_base/tree.c
===================================================================
--- subversion/libsvn_fs_base/tree.c (revision 21795)
+++ subversion/libsvn_fs_base/tree.c (working copy)
@@ -38,6 +38,7 @@
 #include "svn_error.h"
 #include "svn_path.h"
 #include "svn_md5.h"
+#include "svn_mergeinfo.h"
 #include "svn_fs.h"
 #include "svn_sorts.h"
 #include "fs.h"
@@ -1281,7 +1282,44 @@
   return SVN_NO_ERROR;
 }
 
+struct change_merge_info_args {
+ svn_fs_root_t *root;
+ const char *path;
+ const svn_string_t *value;
+};
 
+static svn_error_t *
+txn_body_change_merge_info(void *baton,
+ trail_t *trail)
+{
+ struct change_merge_info_args *args = baton;
+ SVN_ERR(svn_fs_base__set_txn_merge_info(args->root->fs, args->root->txn,
+ args->path, args->value,
+ trail, trail->pool));
+ return SVN_NO_ERROR;
+}
+
+/* Change the merge info for a given path. */
+static svn_error_t *
+base_change_merge_info(svn_fs_root_t *root,
+ const char *path,
+ apr_hash_t *merge_info,
+ apr_pool_t *pool)
+{
+ svn_stringbuf_t *minfostr;
+ struct change_merge_info_args args;
+
+ if (! root->is_txn_root)
+ return NOT_TXN(root);
+ SVN_ERR(svn_mergeinfo_to_string(&minfostr, merge_info, pool));
+ args.root = root;
+ args.path = path;
+ args.value = svn_string_create_from_buf(minfostr, pool);
+ SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_change_merge_info, &args,
+ pool));
+ return SVN_NO_ERROR;
+}
+
 struct change_node_prop_args {
   svn_fs_root_t *root;
   const char *path;
@@ -1291,6 +1329,24 @@
 
 
 static svn_error_t *
+change_txn_merge_info(struct change_node_prop_args *args, trail_t *trail)
+{
+ const char *txn_id = args->root->txn;
+
+ /* At least for single file merges, nodes which are direct
+ children of the root are received without a leading slash
+ (e.g. "/file.txt" is received as "file.txt"), so must be made
+ absolute. */
+ const char *canon_path = svn_fs_base__canonicalize_abspath(args->path,
+ trail->pool);
+ SVN_ERR(svn_fs_base__set_txn_merge_info(args->root->fs, txn_id, canon_path,
+ args->value, trail, trail->pool));
+
+ return SVN_NO_ERROR;
+}
+
+
+static svn_error_t *
 txn_body_change_node_prop(void *baton,
                           trail_t *trail)
 {
@@ -1321,6 +1377,9 @@
   if (! proplist)
     proplist = apr_hash_make(trail->pool);
 
+ if (strcmp (args->name, SVN_PROP_MERGE_INFO) == 0)
+ SVN_ERR(change_txn_merge_info(args, trail));
+
   /* Set the property. */
   apr_hash_set(proplist, args->name, APR_HASH_KEY_STRING, args->value);
 
@@ -2382,8 +2441,7 @@
   SVN_ERR(verify_locks(txn_name, trail, trail->pool));
 
   /* Else, commit the txn. */
- SVN_ERR(svn_fs_base__dag_commit_txn(&(args->new_rev), fs, txn_name,
- trail, trail->pool));
+ SVN_ERR(svn_fs_base__dag_commit_txn(&(args->new_rev), txn, trail));
 
   return SVN_NO_ERROR;
 }
@@ -4374,7 +4432,7 @@
   base_contents_changed,
   base_get_file_delta_stream,
   base_merge,
- NULL,
+ base_change_merge_info,
   svn_fs_merge_info__get_merge_info
 };
 
Index: subversion/libsvn_fs_base/dag.h
===================================================================
--- subversion/libsvn_fs_base/dag.h (revision 21795)
+++ subversion/libsvn_fs_base/dag.h (working copy)
@@ -214,10 +214,8 @@
    Consume temporary space at most proportional to the maximum depth
    of SVN_TXN's tree of mutable nodes. */
 svn_error_t *svn_fs_base__dag_commit_txn(svn_revnum_t *new_rev,
- svn_fs_t *fs,
- const char *txn_id,
- trail_t *trail,
- apr_pool_t *pool);
+ svn_fs_txn_t *txn,
+ trail_t *trail);
 
 
 
Index: subversion/libsvn_fs_base/tree.h
===================================================================
--- subversion/libsvn_fs_base/tree.h (revision 21795)
+++ subversion/libsvn_fs_base/tree.h (working copy)
@@ -22,6 +22,14 @@
 extern "C" {
 #endif /* __cplusplus */
 
+/* A property specific for bdb repositories holding this property name
+ * against skel form of apr_hash_t (target wc path char*-> actual merge info)*/
+
+#include "svn_props.h"
+#include "trail.h"
+
+#define SVN_FS_PROP_TXN_MERGEINFO SVN_PROP_PREFIX "txn-mergeinfo"
+
 
 
 /* These functions implement some of the calls in the FS loader
Index: subversion/libsvn_fs_base/revs-txns.c
===================================================================
--- subversion/libsvn_fs_base/revs-txns.c (revision 21795)
+++ subversion/libsvn_fs_base/revs-txns.c (working copy)
@@ -558,7 +558,50 @@
   return put_txn(fs, txn, txn_name, trail, pool);
 }
 
+svn_error_t *
+svn_fs_base__set_txn_merge_info(svn_fs_t *fs,
+ const char *txn_name,
+ const char *path,
+ const svn_string_t *value,
+ trail_t *trail,
+ apr_pool_t *pool)
+{
+ transaction_t *txn;
+ apr_hash_t *mergetarget_mergeinfo;
 
+ SVN_ERR(get_txn(&txn, fs, txn_name, FALSE, trail, pool));
+ if (txn->kind != transaction_kind_normal)
+ {
+ return svn_fs_base__err_txn_not_mutable(fs, txn_name);
+ }
+
+ /* If there's no proplist, but we're just deleting a property, exit now. */
+ if ((! txn->proplist) && (! value))
+ {
+ return SVN_NO_ERROR;
+ }
+
+ /* Now, if there's no proplist, we know we need to make one. */
+ if (! txn->proplist)
+ txn->proplist = apr_hash_make(pool);
+
+
+ mergetarget_mergeinfo = apr_hash_get(txn->proplist, SVN_FS_PROP_TXN_MERGEINFO,
+ APR_HASH_KEY_STRING);
+
+ if (! mergetarget_mergeinfo)
+ mergetarget_mergeinfo = apr_hash_make(pool);
+ /* Set the mergeinfo for the path. */
+ apr_hash_set(mergetarget_mergeinfo, path, APR_HASH_KEY_STRING, value);
+
+ /* Set the property. */
+ apr_hash_set(txn->proplist, SVN_FS_PROP_TXN_MERGEINFO,
+ APR_HASH_KEY_STRING, mergetarget_mergeinfo);
+
+ /* Now overwrite the transaction. */
+ return put_txn(fs, txn, txn_name, trail, pool);
+}
+
 static svn_error_t *
 txn_body_change_txn_prop(void *baton, trail_t *trail)
 {
@@ -587,7 +630,37 @@
   return SVN_NO_ERROR;
 }
 
+/* txn_vtable's get_mergeinfo hook */
 
+static svn_error_t *
+base_txn_merge_info(apr_hash_t **table_p,
+ svn_fs_txn_t *txn,
+ apr_pool_t *pool)
+{
+ apr_hash_t *txnprops = NULL;
+ apr_hash_t *mergetarget_mergeinfo = NULL;
+ struct txn_proplist_args args;
+ svn_fs_t *fs = txn->fs;
+
+ SVN_ERR(svn_fs_base__check_fs(fs));
+
+ args.table_p = &txnprops;
+ args.id = txn->id;
+ SVN_ERR(svn_fs_base__retry(fs, txn_body_txn_proplist, &args, pool));
+
+ if (txnprops)
+ {
+ mergetarget_mergeinfo = apr_hash_get(txnprops, SVN_FS_PROP_TXN_MERGEINFO,
+ APR_HASH_KEY_STRING);
+ if (!mergetarget_mergeinfo)
+ mergetarget_mergeinfo = apr_hash_make(pool);
+ }
+ *table_p = mergetarget_mergeinfo;
+
+ return SVN_NO_ERROR;
+}
+
+
 
 /* Creating a transaction */
 
@@ -598,7 +671,7 @@
   svn_fs_base__txn_proplist,
   svn_fs_base__change_txn_prop,
   svn_fs_base__txn_root,
- NULL
+ base_txn_merge_info
 };
 
 
Index: subversion/libsvn_fs_base/revs-txns.h
===================================================================
--- subversion/libsvn_fs_base/revs-txns.h (revision 21795)
+++ subversion/libsvn_fs_base/revs-txns.h (working copy)
@@ -144,6 +144,20 @@
                                        apr_pool_t *pool);
 
 
+/* Set a PATH to VALUE on transaction TXN_NAME's proplist's
+ SVN_FS_PROP_TXN_MERGEINFO hash table in FS as part
+ of TRAIL. Use POOL for any necessary allocations.
+
+ Returns SVN_ERR_FS_TRANSACTION_NOT_MUTABLE if TXN_NAME refers to a
+ transaction that has already been committed. */
+svn_error_t *
+svn_fs_base__set_txn_merge_info(svn_fs_t *fs,
+ const char *txn_name,
+ const char *path,
+ const svn_string_t *value,
+ trail_t *trail,
+ apr_pool_t *pool);
+
 /* Set a property NAME to VALUE on transaction TXN_NAME in FS as part
    of TRAIL. Use POOL for any necessary allocations.
 
Index: subversion/libsvn_fs_base/dag.c
===================================================================
--- subversion/libsvn_fs_base/dag.c (revision 21795)
+++ subversion/libsvn_fs_base/dag.c (working copy)
@@ -34,6 +34,7 @@
 #include "reps-strings.h"
 #include "revs-txns.h"
 #include "id.h"
+#include "tree.h" //needed for SVN_FS_PROP_TXN_MERGEINFO
 
 #include "util/fs_skels.h"
 
@@ -44,6 +45,7 @@
 #include "bdb/reps-table.h"
 #include "bdb/strings-table.h"
 
+#include "private/svn_fs_merge_info.h"
 #include "../libsvn_fs/fs-loader.h"
 
 #include "svn_private_config.h"
@@ -1416,18 +1418,35 @@
 
 svn_error_t *
 svn_fs_base__dag_commit_txn(svn_revnum_t *new_rev,
- svn_fs_t *fs,
- const char *txn_id,
- trail_t *trail,
- apr_pool_t *pool)
+ svn_fs_txn_t *txn,
+ trail_t *trail)
 {
   revision_t revision;
   svn_string_t date;
   apr_hash_t *txnprops;
+ svn_fs_t *fs = txn->fs;
+ const char *txn_id = txn->id;
+ apr_pool_t *pool = trail->pool;
+ apr_hash_t *mergetarget_mergeinfo=NULL;
 
   /* Remove any temporary transaction properties initially created by
      begin_txn(). */
   SVN_ERR(svn_fs_base__txn_proplist_in_trail(&txnprops, txn_id, trail));
+
+ /* Add new revision entry to `revisions' table. */
+ revision.txn_id = txn_id;
+ *new_rev = SVN_INVALID_REVNUM;
+ SVN_ERR(svn_fs_bdb__put_rev(new_rev, fs, &revision, trail, pool));
+
+ mergetarget_mergeinfo = apr_hash_get(txnprops, SVN_FS_PROP_TXN_MERGEINFO,
+ APR_HASH_KEY_STRING);
+ if (mergetarget_mergeinfo)
+ {
+ SVN_ERR(svn_fs_merge_info__update_index(txn, *new_rev, TRUE, pool));
+ SVN_ERR(svn_fs_base__set_txn_prop
+ (fs, txn_id, SVN_FS_PROP_TXN_MERGEINFO, NULL, trail, pool));
+ }
+
   if (txnprops)
     {
       if (apr_hash_get(txnprops, SVN_FS_PROP_TXN_CHECK_OOD,
@@ -1440,12 +1459,6 @@
         SVN_ERR(svn_fs_base__set_txn_prop
                 (fs, txn_id, SVN_FS_PROP_TXN_CHECK_LOCKS, NULL, trail, pool));
     }
-
- /* Add new revision entry to `revisions' table. */
- revision.txn_id = txn_id;
- *new_rev = SVN_INVALID_REVNUM;
- SVN_ERR(svn_fs_bdb__put_rev(new_rev, fs, &revision, trail, pool));
-
   /* Promote the unfinished transaction to a committed one. */
   SVN_ERR(svn_fs_base__txn_make_committed(fs, txn_id, *new_rev,
                                           trail, pool));
Index: subversion/libsvn_fs_base/util/fs_skels.c
===================================================================
--- subversion/libsvn_fs_base/util/fs_skels.c (revision 21795)
+++ subversion/libsvn_fs_base/util/fs_skels.c (working copy)
@@ -23,6 +23,7 @@
 #include "fs_skels.h"
 #include "skel.h"
 #include "../id.h"
+#include "../tree.h" //needed for SVN_FS_PROP_TXN_MERGEINFO
 
 
 static svn_error_t *
@@ -56,15 +57,35 @@
 is_valid_proplist_skel(skel_t *skel)
 {
   int len = svn_fs_base__list_length(skel);
+ const char *propname = NULL;
+ apr_size_t propname_len = 0;
+ apr_size_t txn_mergeinfo_prop_length = strlen(SVN_FS_PROP_TXN_MERGEINFO);
 
   if ((len >= 0) && (len & 1) == 0)
     {
       skel_t *elt;
 
       for (elt = skel->children; elt; elt = elt->next)
- if (! elt->is_atom)
- return FALSE;
-
+ {
+ if ((propname_len == txn_mergeinfo_prop_length) &&
+ strncmp(propname,
+ SVN_FS_PROP_TXN_MERGEINFO,
+ txn_mergeinfo_prop_length) == 0)
+ {
+ if (elt->is_atom)
+ return FALSE;
+ len = svn_fs_base__list_length(elt);
+ if (len & 1 != 0)
+ return FALSE;
+ }
+ else
+ {
+ if (! elt->is_atom)
+ return FALSE;
+ }
+ propname = elt->data;
+ propname_len = elt->len;
+ }
       return TRUE;
     }
 
@@ -361,6 +382,33 @@
 
 /*** Parsing (conversion from skeleton to native FS type) ***/
 
+static svn_error_t *
+parse_txn_merge_info_skel(apr_hash_t **mergetarget_mergeinfo_p,
+ skel_t *skel, apr_pool_t *pool)
+{
+ apr_hash_t *mergetarget_mergeinfo = NULL;
+ skel_t *elt;
+
+ /* Create the returned structure */
+ if (skel->children)
+ mergetarget_mergeinfo = apr_hash_make(pool);
+ for (elt = skel->children; elt; elt = elt->next->next)
+ {
+ svn_string_t *value = svn_string_ncreate(elt->next->data,
+ elt->next->len, pool);
+ apr_hash_set(mergetarget_mergeinfo,
+ apr_pstrmemdup(pool, elt->data, elt->len),
+ elt->len,
+ value);
+ }
+
+ /* Return the structure. */
+ *mergetarget_mergeinfo_p = mergetarget_mergeinfo;
+ return SVN_NO_ERROR;
+}
+
+/*** Parsing (conversion from skeleton to native FS type) ***/
+
 svn_error_t *
 svn_fs_base__parse_proplist_skel(apr_hash_t **proplist_p,
                                  skel_t *skel,
@@ -368,6 +416,7 @@
 {
   apr_hash_t *proplist = NULL;
   skel_t *elt;
+ apr_size_t txn_mergeinfo_prop_length = strlen(SVN_FS_PROP_TXN_MERGEINFO);
 
   /* Validate the skel. */
   if (! is_valid_proplist_skel(skel))
@@ -378,12 +427,27 @@
     proplist = apr_hash_make(pool);
   for (elt = skel->children; elt; elt = elt->next->next)
     {
- svn_string_t *value = svn_string_ncreate(elt->next->data,
- elt->next->len, pool);
- apr_hash_set(proplist,
- apr_pstrmemdup(pool, elt->data, elt->len),
- elt->len,
- value);
+ if ((elt->len == txn_mergeinfo_prop_length) &&
+ strncmp(elt->data, SVN_FS_PROP_TXN_MERGEINFO,
+ txn_mergeinfo_prop_length) == 0)
+ {
+ apr_hash_t *mergetarget_mergeinfo;
+ SVN_ERR(parse_txn_merge_info_skel(&mergetarget_mergeinfo,
+ elt->next, pool));
+ apr_hash_set(proplist,
+ apr_pstrmemdup(pool, elt->data, elt->len),
+ elt->len,
+ mergetarget_mergeinfo);
+ }
+ else
+ {
+ svn_string_t *value = svn_string_ncreate(elt->next->data,
+ elt->next->len, pool);
+ apr_hash_set(proplist,
+ apr_pstrmemdup(pool, elt->data, elt->len),
+ elt->len,
+ value);
+ }
     }
 
   /* Return the structure. */
@@ -864,12 +928,53 @@
 /*** Unparsing (conversion from native FS type to skeleton) ***/
 
 svn_error_t *
+unparse_txn_merge_info_skel(skel_t **skel_p, apr_hash_t *mergetarget_mergeinfo,
+ apr_pool_t *pool)
+{
+ skel_t *skel = svn_fs_base__make_empty_list(pool);
+ apr_hash_index_t *hi;
+
+ /* Create the skel. */
+ if (mergetarget_mergeinfo)
+ {
+ /* Loop over hash entries */
+ for (hi = apr_hash_first(pool, mergetarget_mergeinfo);
+ hi;
+ hi = apr_hash_next(hi))
+ {
+ const void *key;
+ void *val;
+ apr_ssize_t klen;
+ svn_string_t *value;
+
+ apr_hash_this(hi, &key, &klen, &val);
+ value = val;
+
+ /* MERGEINFO */
+ svn_fs_base__prepend(svn_fs_base__mem_atom(value->data,
+ value->len, pool),
+ skel);
+
+ /* PATH */
+ svn_fs_base__prepend(svn_fs_base__mem_atom(key, klen, pool), skel);
+ }
+ }
+
+ /* Validate and return the skel. */
+ if (! is_valid_proplist_skel(skel))
+ return skel_err("proplist");
+ *skel_p = skel;
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
 svn_fs_base__unparse_proplist_skel(skel_t **skel_p,
                                    apr_hash_t *proplist,
                                    apr_pool_t *pool)
 {
   skel_t *skel = svn_fs_base__make_empty_list(pool);
   apr_hash_index_t *hi;
+ apr_size_t txn_mergeinfo_prop_length = strlen(SVN_FS_PROP_TXN_MERGEINFO);
 
   /* Create the skel. */
   if (proplist)
@@ -886,9 +991,28 @@
           value = val;
 
           /* VALUE */
- svn_fs_base__prepend(svn_fs_base__mem_atom(value->data,
- value->len, pool),
- skel);
+ if ((klen == txn_mergeinfo_prop_length) &&
+ strncmp(key,
+ SVN_FS_PROP_TXN_MERGEINFO,
+ txn_mergeinfo_prop_length) == 0)
+ {
+ apr_hash_t *mergetarget_mergeinfo = val;
+ skel_t *mergetarget_mergeinfo_skel;
+ if (apr_hash_count(mergetarget_mergeinfo))
+ {
+ unparse_txn_merge_info_skel(&mergetarget_mergeinfo_skel,
+ mergetarget_mergeinfo, pool);
+ svn_fs_base__prepend(mergetarget_mergeinfo_skel, skel);
+ }
+ else
+ continue;
+ }
+ else
+ {
+ svn_fs_base__prepend(svn_fs_base__mem_atom(value->data,
+ value->len, pool),
+ skel);
+ }
 
           /* NAME */
           svn_fs_base__prepend(svn_fs_base__mem_atom(key, klen, pool), skel);

[[[
Patch by: Kamesh Jayachandran <kamesh@collab.net>

Implement 'txn-mergeinfo' change, storage and retrieval for 'bdb' repositories.

The changes can be summarised as follows,

1. We now store the txn-mergeinfo as a temporary transaction property
   'svn:txn-mergeinfo' with a value as list skel having
   (path1 revlist1 path2 revlist2......)
    
2. Changing the signature of svn_fs_base__dag_commit_txn to accept
   'svn_fs_txn_t *'(which is needed by svn_fs_merge_info__update_index).
   Along with done one more refactoring to pass 'trail' alone as 'pool' can
   be extracted out of trail.
   From 'svn_fs_txn_t *' we can derive 'svn_fs_t*' and 'const char *txn_id'
   so not passing them explicitly.

* subversion/libsvn_fs_base/dag.h
   (svn_fs_base__dag_commit_txn): Changing the signature to accept
   'svn_fs_txn_t *'(which is needed by svn_fs_merge_info__update_index).
   Along with this done one more refactoring to pass 'trail' alone as 'pool' can
   be extracted out of trail.
   From 'svn_fs_txn_t *' we can derive 'svn_fs_t*' and 'const char *txn_id'
   so not passing them explicitly.

* subversion/libsvn_fs_base/tree.c
  (global): including "svn_mergeinfo.h" as we need this for mergeinfo alzebra.
  (struct change_merge_info_args): New structure to hold the baton for
   'txn_body_change_merge_info'
  (txn_body_change_merge_info): New helper function for base_change_merge_info.
  (base_change_merge_info): New hook function implementing root_vtable_t's
   'change_merge_info' hook.
  (change_txn_merge_info): New function to update the 'txn mergeinfo' upon
   change property of 'svn:mergeinfo'.
  (txn_body_change_node_prop): calls 'change_txn_merge_info' if property that
   is getting modified is 'svn:mergeinfo'.
  (txn_body_commit): calls svn_fs_base__dag_commit_txn with a new signature.
  (root_vtable): Setting 'change_merge_info' hook to base_change_merge_info.

* subversion/libsvn_fs_base/tree.h
  (global):
   including svn_props.h to use 'SVN_PROP_PREFIX'
   including trail.h to make sure 'trail_t' is defined so that 'trail unaware'
   consumers like subversion/libsvn_fs_base/util/fs-skel.c can consume tree.h.
  (SVN_FS_PROP_TXN_MERGEINFO): New macro.

* subversion/libsvn_fs_base/revs-txns.c
  (svn_fs_base__set_txn_merge_info): New function records the txn mergeinfo.
  (base_txn_merge_info): New function that retrieves the stored
   txn mergeinfo and gives to libsvn_fs_util which records the mergeinfo in the
   persistent mergeinfo db.
  (txn_vtable): set 'get_mergeinfo' hook as 'base_txn_merge_info'.
 
* subversion/libsvn_fs_base/revs-txns.h
  (global): prototype svn_fs_base__set_txn_merge_info.

* subversion/libsvn_fs_base/dag.c
  (global):
   include "tree.h" to make use of 'SVN_FS_PROP_TXN_MERGEINFO'.
   include "private/svn_fs_merge_info.h" to make use of
   'svn_fs_merge_info__update_index'.
  (svn_fs_base__dag_commit_txn):
   signature change as described by summary 2 above.
   record mergeinfo by calling svn_fs_merge_info__update_index.

* subversion/libsvn_fs_base/util/fs_skels.c
  (global): include "tree.h" to make use of 'SVN_FS_PROP_TXN_MERGEINFO'.
  (is_valid_proplist_skel):
   Changes as summarised in item 1 above.
  (parse_txn_merge_info_skel): New function to parse the list skel of
   'SVN_FS_PROP_TXN_MERGEINFO'.
  (svn_fs_base__parse_proplist_skel): Changes as summarised at item 1 above.
  (unparse_txn_merge_info_skel): New function to unparse the list skel of
   'SVN_FS_PROP_TXN_MERGEINFO'.
  (svn_fs_base__unparse_proplist_skel): Changes as summarised at item 1 above.
]]]

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Fri Oct 6 12:48:41 2006

This is an archived mail posted to the Subversion Dev mailing list.

This site is subject to the Apache Privacy Policy and the Apache Public Forum Archive Policy.