You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by hw...@apache.org on 2011/07/25 21:56:36 UTC

svn commit: r1150868 - /subversion/trunk/tools/client-side/detatch.py

Author: hwright
Date: Mon Jul 25 19:56:35 2011
New Revision: 1150868

URL: http://svn.apache.org/viewvc?rev=1150868&view=rev
Log:
Add a rudimentary detatch script to the client-side tools.  This is far from
comprehensive or exhaustively tested, but should work in most scenarios.

Improvements, patches, testing, etc welcome and encouraged.

* tools/client-side/detatch.py:
  New.

Added:
    subversion/trunk/tools/client-side/detatch.py   (with props)

Added: subversion/trunk/tools/client-side/detatch.py
URL: http://svn.apache.org/viewvc/subversion/trunk/tools/client-side/detatch.py?rev=1150868&view=auto
==============================================================================
--- subversion/trunk/tools/client-side/detatch.py (added)
+++ subversion/trunk/tools/client-side/detatch.py Mon Jul 25 19:56:35 2011
@@ -0,0 +1,271 @@
+#!/usr/bin/env python
+#
+# ====================================================================
+#    Licensed to the Apache Software Foundation (ASF) under one
+#    or more contributor license agreements.  See the NOTICE file
+#    distributed with this work for additional information
+#    regarding copyright ownership.  The ASF licenses this file
+#    to you under the Apache License, Version 2.0 (the
+#    "License"); you may not use this file except in compliance
+#    with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing,
+#    software distributed under the License is distributed on an
+#    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#    KIND, either express or implied.  See the License for the
+#    specific language governing permissions and limitations
+#    under the License.
+# ====================================================================
+
+# TODO: if this was part of core subversion, we'd have all sorts of nifty
+#       checks, and could use a lot of existing code.
+
+import os
+import re
+import sys
+import shutil
+import sqlite3
+
+
+def usage():
+  print("""usage: %s WC_SRC TARGET
+
+Detatch the working copy subdirectory given by WC_SRC to TARGET.  This is
+equivalent to copying WC_SRC to TARGET, but it inserts a new set of Subversion
+metadata into TARGET/.svn, making TARGET a proper independent working copy.
+""" % sys.argv[0])
+  sys.exit(1)
+
+
+def find_wcroot(wcdir):
+  wcroot = os.path.abspath(wcdir)
+  old_wcroot = ''
+  while wcroot != old_wcroot:
+    if os.path.exists(os.path.join(wcroot, '.svn', 'wc.db')):
+      return wcroot
+
+    old_wcroot = wcroot
+    wcroot = os.path.dirname(wcroot)
+
+  return None
+
+
+def  migrate_sqlite(wc_src, target, wcroot):
+  src_conn = sqlite3.connect(os.path.join(wcroot, '.svn', 'wc.db'))
+  dst_conn = sqlite3.connect(os.path.join(target, '.svn', 'wc.db'))
+
+  local_relsrc = os.path.relpath(wc_src, wcroot)
+
+  src_c = src_conn.cursor()
+  dst_c = dst_conn.cursor()
+
+  # We're only going to attempt this if there are no locks or work queue
+  # items in the source database
+  ### This could probably be tightened up, but for now this suffices
+  src_c.execute('select count(*) from wc_lock')
+  count = int(src_c.fetchone()[0])
+  assert count == 0
+    
+  src_c.execute('select count(*) from work_queue')
+  count = int(src_c.fetchone()[0])
+  assert count == 0
+
+  # Copy over the schema
+  src_c.execute('pragma user_version')
+  user_version = src_c.fetchone()[0]
+  # We only know how to handle format 29 working copies
+  assert user_version == 29
+  ### For some reason, sqlite doesn't like to parameterize the pragma statement
+  dst_c.execute('pragma user_version = %d' % user_version)
+
+  src_c.execute('select name, sql from sqlite_master')
+  for row in src_c:
+    if not row[0].startswith('sqlite_'):
+      dst_c.execute(row[1])
+
+  # Insert wcroot row
+  dst_c.execute('insert into wcroot (id, local_abspath) values (?, ?)',
+                (1, None))
+
+  # Copy repositories rows
+  ### Perhaps prune the repositories based upon the new NODES set?
+  src_c.execute('select * from repository')
+  for row in src_c:
+    dst_c.execute('insert into repository values (?, ?, ?)',
+                  row)
+
+  # Copy the root node
+  src_c.execute('select * from nodes where local_relpath = ?',
+                local_relsrc)
+  row = list(src_c.fetchone())
+  row[1] = ''
+  row[3] = None
+  dst_c.execute('''insert into nodes values
+                  (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
+                   ?, ?, ?, ?, ?, ?, ?, ?)''', row)
+
+  # Copy children nodes rows
+  src_c.execute('select * from nodes where local_relpath like ?',
+                (local_relsrc + '/%', ))
+  for row in src_c:
+    row = list(row)
+    row[1] = row[1][len(local_relsrc) + 1:]
+    row[3] = row[3][len(local_relsrc) + 1:]
+    dst_c.execute('''insert into nodes values
+                  (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
+                   ?, ?, ?, ?, ?, ?, ?, ?)''',
+                  row)
+
+  # Copy root actual_node
+  src_c.execute('select * from actual_node where local_relpath = ?',
+                local_relsrc)
+  row = src_c.fetchone()
+  if row:
+    row = list(row)
+    row[1] = ''
+    row[2] = None
+    dst_c.execute('''insert into actual_node values
+                     (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)''', row)
+
+  src_c.execute('select * from actual_node where local_relpath like ?',
+                (local_relsrc + '/%', ))
+  for row in src_c:
+    row = list(row)
+    row[1] = row[1][len(local_relsrc) + 1:]
+    row[2] = row[2][len(local_relsrc) + 1:]
+    dst_c.execute('''insert into actual_node values
+                     (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)''', row)
+
+  # Hard to know which locks we care about, so just copy 'em all (there aren't
+  # likely to be many)
+  src_c.execute('select * from lock')
+  for row in src_c:
+    dst_c.execute('insert into locks values (?, ?, ?, ?, ?, ?)', row)
+
+  # EXTERNALS
+  src_c.execute('select * from externals where local_relpath = ?',
+                local_relsrc)
+  row = src_c.fetchone()
+  if row:
+    row = list(row)
+    row[1] = ''
+    row[2] = None
+    dst_c.execute('''insert into externals values
+                     (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)''', row)
+
+  src_c.execute('select * from externals where local_relpath like ?',
+                (local_relsrc + '/%', ))
+  for row in src_c:
+    row = list(row)
+    row[1] = row[1][len(local_relsrc) + 1:]
+    row[2] = row[2][len(local_relsrc) + 1:]
+    dst_c.execute('''insert into externals values
+                     (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)''', row)
+
+  dst_conn.commit()
+  src_conn.close()
+  dst_conn.close()
+
+
+def migrate_pristines(wc_src, target, wcroot):
+  src_conn = sqlite3.connect(os.path.join(wcroot, '.svn', 'wc.db'))
+  dst_conn = sqlite3.connect(os.path.join(target, '.svn', 'wc.db'))
+
+  src_c = src_conn.cursor()
+  dst_c = dst_conn.cursor()
+
+  regex = re.compile('\$((?:md5)|(?:sha1))\$(.*)')
+  src_proot = os.path.join(wcroot, '.svn', 'pristine')
+  target_proot = os.path.join(target, '.svn', 'pristine')
+
+  checksums = {}
+
+  # Grab anything which needs a pristine
+  src_c.execute('''select checksum from nodes
+                   union
+                   select older_checksum from actual_node
+                   union
+                   select left_checksum from actual_node
+                   union
+                   select right_checksum from actual_node''')
+  for row in src_c:
+    if row[0]:
+      match = regex.match(row[0])
+      assert match
+
+      pristine = match.group(2)
+      if pristine in checksums:
+        checksums[pristine] += 1
+      else:
+        checksums[pristine] = 1
+
+  for pristine, count in checksums.items():
+    # Copy the pristines themselves over
+    pdir = os.path.join(target_proot, pristine[0:2])
+    if not os.path.exists(pdir):
+      os.mkdir(pdir)
+    path = os.path.join(pristine[0:2], pristine + '.svn-base')
+    if os.path.exists(os.path.join(target_proot, path)):
+      dst_c.execute
+    else:
+      shutil.copy2(os.path.join(src_proot, path),
+                   os.path.join(target_proot, path))
+
+    src_c.execute('select size, md5_checksum from pristine where checksum=?',
+                  ('$sha1$' + pristine, ) )
+    (size, md5) = src_c.fetchone()
+
+    # Insert a db row for the pristine
+    dst_c.execute('insert into pristine values (?, NULL, ?, ?, ?)',
+                  ('$sha1$' + pristine, size, count, md5))
+
+  dst_conn.commit()
+  src_conn.close()
+  dst_conn.close()
+
+
+def migrate_metadata(wc_src, target, wcroot):
+  # Make paths
+  os.mkdir(os.path.join(target, '.svn'))
+  os.mkdir(os.path.join(target, '.svn', 'tmp'))
+  os.mkdir(os.path.join(target, '.svn', 'pristine'))
+  open(os.path.join(target, '.svn', 'format'), 'w').write('12')
+  open(os.path.join(target, '.svn', 'entries'), 'w').write('12')
+
+  # Two major bits: sqlite data and pristines
+  migrate_sqlite(wc_src, os.path.abspath(target), wcroot)
+  migrate_pristines(wc_src, target, wcroot)
+
+
+def main():
+  if len(sys.argv) < 3:
+    usage()
+
+  wc_src = os.path.normpath(sys.argv[1])
+  if not os.path.isdir(wc_src):
+    print("%s does not exist or is not a directory" % wc_src)
+    sys.exit(1)
+
+  target = os.path.normpath(sys.argv[2])
+  if os.path.exists(target):
+    print("Target '%s' already exists" % target)
+    sys.exit(1)
+
+  wcroot = find_wcroot(wc_src)
+  if not wcroot:
+    print("'%s' is not part of a working copy" % wc_src)
+    sys.exit(1)
+
+  # Use the OS to copy the subdirectory over to the target
+  shutil.copytree(wc_src, target)
+
+  # Now migrate the worky copy data
+  migrate_metadata(wc_src, target, wcroot)
+
+
+if __name__ == '__main__':
+  #raise Exception("""This script is unfinished and not ready to be used on live data.
+  #  Trust us.""")
+  main()

Propchange: subversion/trunk/tools/client-side/detatch.py
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: subversion/trunk/tools/client-side/detatch.py
------------------------------------------------------------------------------
    svn:executable = *