You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by ju...@apache.org on 2010/08/17 16:14:01 UTC

svn commit: r986316 - /subversion/trunk/tools/dev/wc-ng/bump-to-19.py

Author: julianfoad
Date: Tue Aug 17 14:14:01 2010
New Revision: 986316

URL: http://svn.apache.org/viewvc?rev=986316&view=rev
Log:
Add a script that will convert a development WC to single-DB, bumping its
format from 18 to 19.

* tools/dev/wc-ng/bump-to-19.py
  New file.

Added:
    subversion/trunk/tools/dev/wc-ng/bump-to-19.py   (with props)

Added: subversion/trunk/tools/dev/wc-ng/bump-to-19.py
URL: http://svn.apache.org/viewvc/subversion/trunk/tools/dev/wc-ng/bump-to-19.py?rev=986316&view=auto
==============================================================================
--- subversion/trunk/tools/dev/wc-ng/bump-to-19.py (added)
+++ subversion/trunk/tools/dev/wc-ng/bump-to-19.py Tue Aug 17 14:14:01 2010
@@ -0,0 +1,232 @@
+#!/usr/bin/env python
+
+"""This program converts a Subversion WC from 1.7-dev format 18 to
+   1.7-dev format 19 by migrating data from multiple DBs to a single DB.
+
+   Usage: bump-to-19.py WC_ROOT_DIR
+   where WC_ROOT_DIR is the path to the WC root directory."""
+
+import sys, os, sqlite3
+
+dot_svn = '.svn'
+
+def db_path(wc_path):
+  return os.path.join(wc_path, dot_svn, 'wc.db')
+
+def pristine_path(wc_path):
+  return os.path.join(wc_path, dot_svn, 'pristine')
+
+class NotASubversionWC:
+  pass
+
+STMT_COPY_BASE_NODE_TABLE_TO_WCROOT_DB1 = \
+  "INSERT OR REPLACE INTO root.BASE_NODE ( " \
+  "    wc_id, local_relpath, repos_id, repos_relpath, parent_relpath, " \
+  "    presence, kind, revnum, checksum, translated_size, changed_rev, " \
+  "    changed_date, changed_author, depth, symlink_target, last_mod_time, " \
+  "    properties, dav_cache, incomplete_children, file_external ) " \
+  "SELECT wc_id, ?1, repos_id, repos_relpath, ?2 AS parent_relpath, " \
+  "    presence, kind, revnum, checksum, translated_size, changed_rev, " \
+  "    changed_date, changed_author, depth, symlink_target, last_mod_time, " \
+  "    properties, dav_cache, incomplete_children, file_external " \
+  "FROM BASE_NODE WHERE local_relpath = ''; "
+
+STMT_COPY_BASE_NODE_TABLE_TO_WCROOT_DB2 = \
+  "INSERT INTO root.BASE_NODE ( " \
+  "    wc_id, local_relpath, repos_id, repos_relpath, parent_relpath, " \
+  "    presence, kind, revnum, checksum, translated_size, changed_rev, " \
+  "    changed_date, changed_author, depth, symlink_target, last_mod_time, " \
+  "    properties, dav_cache, incomplete_children, file_external ) " \
+  "SELECT wc_id, ?1 || '/' || local_relpath, repos_id, repos_relpath, " \
+  "    ?1 AS parent_relpath, " \
+  "    presence, kind, revnum, checksum, translated_size, changed_rev, " \
+  "    changed_date, changed_author, depth, symlink_target, last_mod_time, " \
+  "    properties, dav_cache, incomplete_children, file_external " \
+  "FROM BASE_NODE WHERE local_relpath != ''; "
+
+STMT_COPY_WORKING_NODE_TABLE_TO_WCROOT_DB1 = \
+  "INSERT OR REPLACE INTO root.WORKING_NODE ( " \
+  "    wc_id, local_relpath, parent_relpath, presence, kind, checksum, " \
+  "    translated_size, changed_rev, changed_date, changed_author, depth, " \
+  "    symlink_target, copyfrom_repos_id, copyfrom_repos_path, copyfrom_revnum, " \
+  "    moved_here, moved_to, last_mod_time, properties, keep_local ) " \
+  "SELECT wc_id, ?1, ?2 AS parent_relpath, " \
+  "    presence, kind, checksum, " \
+  "    translated_size, changed_rev, changed_date, changed_author, depth, " \
+  "    symlink_target, copyfrom_repos_id, copyfrom_repos_path, copyfrom_revnum, " \
+  "    moved_here, moved_to, last_mod_time, properties, keep_local " \
+  "FROM WORKING_NODE WHERE local_relpath = ''; "
+
+STMT_COPY_WORKING_NODE_TABLE_TO_WCROOT_DB2 = \
+  "INSERT INTO root.WORKING_NODE ( " \
+  "    wc_id, local_relpath, parent_relpath, presence, kind, checksum, " \
+  "    translated_size, changed_rev, changed_date, changed_author, depth, " \
+  "    symlink_target, copyfrom_repos_id, copyfrom_repos_path, copyfrom_revnum, " \
+  "    moved_here, moved_to, last_mod_time, properties, keep_local ) " \
+  "SELECT wc_id, ?1 || '/' || local_relpath, ?1 AS parent_relpath, " \
+  "    presence, kind, checksum, " \
+  "    translated_size, changed_rev, changed_date, changed_author, depth, " \
+  "    symlink_target, copyfrom_repos_id, copyfrom_repos_path, copyfrom_revnum, " \
+  "    moved_here, moved_to, last_mod_time, properties, keep_local " \
+  "FROM WORKING_NODE WHERE local_relpath != ''; "
+
+STMT_COPY_ACTUAL_NODE_TABLE_TO_WCROOT_DB1 = \
+  "INSERT OR REPLACE INTO root.ACTUAL_NODE ( " \
+  "    wc_id, local_relpath, parent_relpath, properties, " \
+  "    conflict_old, conflict_new, conflict_working, " \
+  "    prop_reject, changelist, text_mod, tree_conflict_data, " \
+  "    conflict_data, older_checksum, left_checksum, right_checksum ) " \
+  "SELECT wc_id, ?1, ?2 AS parent_relpath, properties, " \
+  "    conflict_old, conflict_new, conflict_working, " \
+  "    prop_reject, changelist, text_mod, tree_conflict_data, " \
+  "    conflict_data, older_checksum, left_checksum, right_checksum " \
+  "FROM ACTUAL_NODE WHERE local_relpath = ''; "
+
+STMT_COPY_ACTUAL_NODE_TABLE_TO_WCROOT_DB2 = \
+  "INSERT INTO root.ACTUAL_NODE ( " \
+  "    wc_id, local_relpath, parent_relpath, properties, " \
+  "    conflict_old, conflict_new, conflict_working, " \
+  "    prop_reject, changelist, text_mod, tree_conflict_data, " \
+  "    conflict_data, older_checksum, left_checksum, right_checksum ) " \
+  "SELECT wc_id, ?1 || '/' || local_relpath, ?1 AS parent_relpath, properties, " \
+  "    conflict_old, conflict_new, conflict_working, " \
+  "    prop_reject, changelist, text_mod, tree_conflict_data, " \
+  "    conflict_data, older_checksum, left_checksum, right_checksum " \
+  "FROM ACTUAL_NODE WHERE local_relpath != ''; "
+
+STMT_COPY_LOCK_TABLE_TO_WCROOT_DB = \
+  "INSERT INTO root.LOCK " \
+  "SELECT * FROM LOCK; "
+
+STMT_COPY_PRISTINE_TABLE_TO_WCROOT_DB = \
+  "INSERT OR REPLACE INTO root.PRISTINE " \
+  "SELECT * FROM PRISTINE; "
+
+
+def copy_db_rows_to_wcroot(wc_subdir_relpath):
+  """Copy all relevant table rows from the $PWD/WC_SUBDIR_RELPATH/.svn/wc.db
+     into $PWD/.svn/wc.db."""
+
+  wc_root_path = ''
+  wc_subdir_path = wc_subdir_relpath
+  wc_subdir_parent_relpath = os.path.dirname(wc_subdir_relpath)
+
+  try:
+    db = sqlite3.connect(db_path(wc_subdir_path))
+  except:
+    raise NotASubversionWC
+  c = db.cursor()
+
+  c.execute("ATTACH '" + db_path(wc_root_path) + "' AS 'root'")
+
+  ### TODO: the REPOSITORY table. At present we assume there is only one
+  # repository in use and its repos_id is consistent throughout the WC.
+  # That's not always true - e.g. "svn switch --relocate" creates repos_id
+  # 2, and then "svn mkdir" uses repos_id 1 in the subdirectory. */
+
+  c.execute(STMT_COPY_BASE_NODE_TABLE_TO_WCROOT_DB1,
+            (wc_subdir_relpath, wc_subdir_parent_relpath))
+  c.execute(STMT_COPY_BASE_NODE_TABLE_TO_WCROOT_DB2,
+            (wc_subdir_relpath, ))
+  c.execute(STMT_COPY_WORKING_NODE_TABLE_TO_WCROOT_DB1,
+            (wc_subdir_relpath, wc_subdir_parent_relpath))
+  c.execute(STMT_COPY_WORKING_NODE_TABLE_TO_WCROOT_DB2,
+            (wc_subdir_relpath, ))
+  c.execute(STMT_COPY_ACTUAL_NODE_TABLE_TO_WCROOT_DB1,
+            (wc_subdir_relpath, wc_subdir_parent_relpath))
+  c.execute(STMT_COPY_ACTUAL_NODE_TABLE_TO_WCROOT_DB2,
+            (wc_subdir_relpath, ))
+  c.execute(STMT_COPY_LOCK_TABLE_TO_WCROOT_DB)
+  c.execute(STMT_COPY_PRISTINE_TABLE_TO_WCROOT_DB)
+
+  db.commit()
+  db.close()
+
+
+def shard_pristine_files(wc_path):
+  """Move all pristine text files from 'WC_PATH/.svn/pristine/'
+     into shard directories: 'WC_PATH/.svn/pristine/??/', creating those
+     shard dirs where necessary."""
+
+  pristine_dir = pristine_path(wc_path)
+
+  for basename in os.listdir(pristine_dir):
+    shard = basename[:2]
+    old = os.path.join(pristine_dir, basename)
+    new = os.path.join(pristine_dir, shard, basename)
+    os.renames(old, new)
+
+
+def move_and_shard_pristine_files(old_wc_path, new_wc_path):
+  """Move all pristine text files from 'OLD_WC_PATH/.svn/pristine/'
+     into 'NEW_WC_PATH/.svn/pristine/??/', creating shard dirs where
+     necessary."""
+
+  old_pristine_dir = pristine_path(old_wc_path)
+  new_pristine_dir = pristine_path(new_wc_path)
+
+  for basename in os.listdir(old_pristine_dir):
+    shard = basename[:2]
+    old = os.path.join(old_pristine_dir, basename)
+    new = os.path.join(new_pristine_dir, shard, basename)
+    os.renames(old, new)
+
+
+def migrate_wc_subdirs(wc_root_path):
+  """Move Subversion metadata from the admin dir of each subdirectory
+     below WC_ROOT_PATH into WC_ROOT_PATH's own admin dir."""
+
+  old_cwd = os.getcwd()
+  os.chdir(wc_root_path)
+
+  for dir_path, dirs, files in os.walk('.'):
+
+    # don't walk into the '.svn' subdirectory
+    try:
+      dirs.remove(dot_svn)
+    except ValueError:
+      # a non-WC dir: don't walk into any subdirectories
+      print "skipped: not a WC dir: '" + dir_path + "'"
+      del dirs[:]
+      continue
+
+    # Try to migrate each other subdirectory
+    for dir in dirs[:]:  # copy so we can remove some
+      wc_subdir_path = os.path.join(dir_path, dir)
+      if wc_subdir_path.startswith('./'):
+        wc_subdir_path = wc_subdir_path[2:]
+      try:
+        print "moving data from subdir '" + wc_subdir_path + "'"
+        copy_db_rows_to_wcroot(wc_subdir_path)
+        print "deleting DB ... ",
+        os.remove(db_path(wc_subdir_path))
+        print "moving pristines ... ",
+        move_and_shard_pristine_files(wc_subdir_path, '.')
+        print "done"
+      except NotASubversionWC:
+        print "skipped: no WC DB found: '" + wc_subdir_path + "'"
+        # a non-WC dir: don't walk into it
+        dirs.remove(dir)
+
+  os.chdir(old_cwd)
+
+
+def bump_wc_format_number(wc_root_path):
+  """Bump the WC format number of the WC dir WC_ROOT_PATH to 19."""
+
+  root_db_path = os.path.join(wc_root_path, dot_svn, 'wc.db')
+  db = sqlite3.connect(root_db_path)
+  c = db.cursor()
+  c.execute("PRAGMA user_version = 19;")
+  db.commit()
+  db.close()
+
+
+if __name__ == '__main__':
+
+  wc_root_path = sys.argv[1]
+  print "merging subdir DBs into single DB '" + wc_root_path + "'"
+  shard_pristine_files(wc_root_path)
+  migrate_wc_subdirs(wc_root_path)
+  bump_wc_format_number(wc_root_path)
+

Propchange: subversion/trunk/tools/dev/wc-ng/bump-to-19.py
------------------------------------------------------------------------------
    svn:executable = *

Propchange: subversion/trunk/tools/dev/wc-ng/bump-to-19.py
------------------------------------------------------------------------------
    svn:mime-type = text/x-python