1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
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()
|