2
by abentley
Added baz2bzr file(doh!) |
1 |
#!/usr/bin/env python
|
14
by abentley
GPLed the project, ignored files |
2 |
|
3 |
# Copyright (C) 2005 Aaron Bentley
|
|
4 |
# <aaron.bentley@utoronto.ca>
|
|
5 |
#
|
|
6 |
# This program is free software; you can redistribute it and/or modify
|
|
7 |
# it under the terms of the GNU General Public License as published by
|
|
8 |
# the Free Software Foundation; either version 2 of the License, or
|
|
9 |
# (at your option) any later version.
|
|
10 |
#
|
|
11 |
# This program is distributed in the hope that it will be useful,
|
|
12 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14 |
# GNU General Public License for more details.
|
|
15 |
#
|
|
16 |
# You should have received a copy of the GNU General Public License
|
|
17 |
# along with this program; if not, write to the Free Software
|
|
18 |
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
19 |
||
2
by abentley
Added baz2bzr file(doh!) |
20 |
try: |
21 |
import pybaz |
|
61
by Aaron Bentley
Factored out bzr to baz id conversion |
22 |
import pybaz.errors |
2
by abentley
Added baz2bzr file(doh!) |
23 |
except ImportError: |
24 |
print "This command requires PyBaz. Please ensure that it is installed." |
|
25 |
import sys |
|
26 |
sys.exit(1) |
|
3
by abentley
Added add_file and add_dir functions |
27 |
from pybaz.backends.baz import null_cmd |
2
by abentley
Added baz2bzr file(doh!) |
28 |
import tempfile |
29 |
import os |
|
30 |
import os.path |
|
31 |
import shutil |
|
7
by abentley
Completed initial construction |
32 |
import bzrlib |
70
by Aaron Bentley
Friendlier error, when dir is not a bzr branch |
33 |
from bzrlib.errors import BzrError |
45
by Aaron Bentley
Silenced commits |
34 |
import bzrlib.trace |
57
by Aaron Bentley
Added John Meinel's update-with-no-{arch} patch |
35 |
import bzrlib.merge |
8
by abentley
Added ability to run from the commandline |
36 |
import sys |
37
by Aaron Bentley
More log fixups for baz2bzr |
37 |
import email.Utils |
11
by abentley
refactored out progress.py |
38 |
from progress import * |
2
by abentley
Added baz2bzr file(doh!) |
39 |
|
3
by abentley
Added add_file and add_dir functions |
40 |
def add_id(files, id=None): |
41 |
"""Adds an explicit id to a list of files.
|
|
42 |
||
43 |
:param files: the name of the file to add an id to
|
|
44 |
:type files: list of str
|
|
45 |
:param id: tag one file using the specified id, instead of generating id
|
|
46 |
:type id: str
|
|
47 |
"""
|
|
48 |
args = ["add-id"] |
|
49 |
if id is not None: |
|
50 |
args.extend(["--id", id]) |
|
51 |
args.extend(files) |
|
52 |
return null_cmd(args) |
|
53 |
||
2
by abentley
Added baz2bzr file(doh!) |
54 |
def test_environ(): |
55 |
"""
|
|
56 |
>>> q = test_environ()
|
|
57 |
>>> os.path.exists(q)
|
|
58 |
True
|
|
59 |
>>> os.path.exists(os.path.join(q, "home", ".arch-params"))
|
|
60 |
True
|
|
61 |
>>> teardown_environ(q)
|
|
62 |
>>> os.path.exists(q)
|
|
63 |
False
|
|
64 |
"""
|
|
69
by Aaron Bentley
Fixed updating-revision bogosity, updated tests |
65 |
tdir = tempfile.mkdtemp(prefix="testdir-") |
2
by abentley
Added baz2bzr file(doh!) |
66 |
os.environ["HOME"] = os.path.join(tdir, "home") |
67 |
os.mkdir(os.environ["HOME"]) |
|
3
by abentley
Added add_file and add_dir functions |
68 |
arch_dir = os.path.join(tdir, "archive_dir") |
69 |
pybaz.make_archive("test@example.com", arch_dir) |
|
2
by abentley
Added baz2bzr file(doh!) |
70 |
work_dir = os.path.join(tdir, "work_dir") |
71 |
os.mkdir(work_dir) |
|
72 |
os.chdir(work_dir) |
|
3
by abentley
Added add_file and add_dir functions |
73 |
pybaz.init_tree(work_dir, "test@example.com/test--test--0") |
2
by abentley
Added baz2bzr file(doh!) |
74 |
lib_dir = os.path.join(tdir, "lib_dir") |
75 |
os.mkdir(lib_dir) |
|
76 |
pybaz.register_revision_library(lib_dir) |
|
6
by abentley
added commit, timport, commit_test_revisions |
77 |
pybaz.set_my_id("Test User<test@example.org>") |
2
by abentley
Added baz2bzr file(doh!) |
78 |
return tdir |
79 |
||
5
by abentley
changed add_file, add_dir interfaces |
80 |
def add_file(path, text, id): |
3
by abentley
Added add_file and add_dir functions |
81 |
"""
|
82 |
>>> q = test_environ()
|
|
5
by abentley
changed add_file, add_dir interfaces |
83 |
>>> add_file("path with space", "text", "lalala")
|
3
by abentley
Added add_file and add_dir functions |
84 |
>>> tree = pybaz.tree_root(".")
|
85 |
>>> inv = list(tree.iter_inventory_ids(source=True, both=True))
|
|
86 |
>>> ("x_lalala", "path with space") in inv
|
|
87 |
True
|
|
88 |
>>> teardown_environ(q)
|
|
89 |
"""
|
|
90 |
file(path, "wb").write(text) |
|
5
by abentley
changed add_file, add_dir interfaces |
91 |
add_id([path], id) |
92 |
||
93 |
||
94 |
def add_dir(path, id): |
|
3
by abentley
Added add_file and add_dir functions |
95 |
"""
|
96 |
>>> q = test_environ()
|
|
5
by abentley
changed add_file, add_dir interfaces |
97 |
>>> add_dir("path with\(sp) space", "lalala")
|
3
by abentley
Added add_file and add_dir functions |
98 |
>>> tree = pybaz.tree_root(".")
|
99 |
>>> inv = list(tree.iter_inventory_ids(source=True, both=True))
|
|
4
by abentley
made test case trickier, by using pika escaping sequence in filename. |
100 |
>>> ("x_lalala", "path with\(sp) space") in inv
|
3
by abentley
Added add_file and add_dir functions |
101 |
True
|
102 |
>>> teardown_environ(q)
|
|
103 |
"""
|
|
104 |
os.mkdir(path) |
|
5
by abentley
changed add_file, add_dir interfaces |
105 |
add_id([path], id) |
3
by abentley
Added add_file and add_dir functions |
106 |
|
2
by abentley
Added baz2bzr file(doh!) |
107 |
def teardown_environ(tdir): |
108 |
os.chdir("/") |
|
109 |
shutil.rmtree(tdir) |
|
110 |
||
6
by abentley
added commit, timport, commit_test_revisions |
111 |
def timport(tree, summary): |
112 |
msg = tree.log_message() |
|
113 |
msg["summary"] = summary |
|
114 |
tree.import_(msg) |
|
115 |
||
116 |
def commit(tree, summary): |
|
117 |
"""
|
|
118 |
>>> q = test_environ()
|
|
119 |
>>> tree = pybaz.tree_root(".")
|
|
120 |
>>> timport(tree, "import")
|
|
121 |
>>> commit(tree, "commit")
|
|
122 |
>>> logs = [str(l.revision) for l in tree.iter_logs()]
|
|
123 |
>>> len(logs)
|
|
124 |
2
|
|
125 |
>>> logs[0]
|
|
126 |
'test@example.com/test--test--0--base-0'
|
|
127 |
>>> logs[1]
|
|
128 |
'test@example.com/test--test--0--patch-1'
|
|
129 |
>>> teardown_environ(q)
|
|
130 |
"""
|
|
131 |
msg = tree.log_message() |
|
132 |
msg["summary"] = summary |
|
133 |
tree.commit(msg) |
|
134 |
||
135 |
def commit_test_revisions(): |
|
136 |
"""
|
|
137 |
>>> q = test_environ()
|
|
138 |
>>> commit_test_revisions()
|
|
139 |
>>> a = pybaz.Archive("test@example.com")
|
|
140 |
>>> revisions = list(a.iter_revisions("test--test--0"))
|
|
141 |
>>> len(revisions)
|
|
7
by abentley
Completed initial construction |
142 |
3
|
143 |
>>> str(revisions[2])
|
|
144 |
'test@example.com/test--test--0--base-0'
|
|
6
by abentley
added commit, timport, commit_test_revisions |
145 |
>>> str(revisions[1])
|
7
by abentley
Completed initial construction |
146 |
'test@example.com/test--test--0--patch-1'
|
6
by abentley
added commit, timport, commit_test_revisions |
147 |
>>> str(revisions[0])
|
7
by abentley
Completed initial construction |
148 |
'test@example.com/test--test--0--patch-2'
|
6
by abentley
added commit, timport, commit_test_revisions |
149 |
>>> teardown_environ(q)
|
150 |
"""
|
|
151 |
tree = pybaz.tree_root(".") |
|
152 |
add_file("mainfile", "void main(void){}", "mainfile by aaron") |
|
153 |
timport(tree, "Created mainfile") |
|
154 |
file("mainfile", "wb").write("or something like that") |
|
155 |
commit(tree, "altered mainfile") |
|
7
by abentley
Completed initial construction |
156 |
add_file("ofile", "this is another file", "ofile by aaron") |
157 |
commit(tree, "altered mainfile") |
|
158 |
||
68
by Aaron Bentley
got dry run and continued import under test |
159 |
|
160 |
def commit_more_test_revisions(): |
|
161 |
"""
|
|
162 |
>>> q = test_environ()
|
|
163 |
>>> commit_test_revisions()
|
|
164 |
>>> commit_more_test_revisions()
|
|
165 |
>>> a = pybaz.Archive("test@example.com")
|
|
166 |
>>> revisions = list(a.iter_revisions("test--test--0"))
|
|
167 |
>>> len(revisions)
|
|
168 |
4
|
|
169 |
>>> str(revisions[0])
|
|
170 |
'test@example.com/test--test--0--patch-3'
|
|
171 |
>>> teardown_environ(q)
|
|
172 |
"""
|
|
173 |
tree = pybaz.tree_root(".") |
|
174 |
add_file("trainfile", "void train(void){}", "trainfile by aaron") |
|
175 |
commit(tree, "altered trainfile") |
|
176 |
||
71
by Aaron Bentley
Improved errors for invalid or missing versions |
177 |
class NoSuchVersion(Exception): |
178 |
def __init__(self, version): |
|
179 |
Exception.__init__(self, "The version %s does not exist." % version) |
|
180 |
self.version = version |
|
181 |
||
7
by abentley
Completed initial construction |
182 |
def version_ancestry(version): |
183 |
"""
|
|
184 |
>>> q = test_environ()
|
|
185 |
>>> commit_test_revisions()
|
|
186 |
>>> version = pybaz.Version("test@example.com/test--test--0")
|
|
187 |
>>> ancestors = version_ancestry(version)
|
|
188 |
>>> str(ancestors[0])
|
|
189 |
'test@example.com/test--test--0--base-0'
|
|
190 |
>>> str(ancestors[1])
|
|
191 |
'test@example.com/test--test--0--patch-1'
|
|
71
by Aaron Bentley
Improved errors for invalid or missing versions |
192 |
>>> version = pybaz.Version("test@example.com/test--test--0.5")
|
193 |
>>> ancestors = version_ancestry(version)
|
|
194 |
Traceback (most recent call last):
|
|
195 |
NoSuchVersion: The version test@example.com/test--test--0.5 does not exist.
|
|
7
by abentley
Completed initial construction |
196 |
>>> teardown_environ(q)
|
197 |
"""
|
|
71
by Aaron Bentley
Improved errors for invalid or missing versions |
198 |
try: |
199 |
revision = version.iter_revisions(reverse=True).next() |
|
200 |
except: |
|
201 |
if not version.exists(): |
|
202 |
raise NoSuchVersion(version) |
|
203 |
else: |
|
204 |
raise
|
|
31
by Aaron Bentley
Further updates |
205 |
ancestors = list(revision.iter_ancestors(metoo=True)) |
206 |
ancestors.reverse() |
|
7
by abentley
Completed initial construction |
207 |
return ancestors |
208 |
||
66
by Aaron Bentley
Continued symlink fix, cleaned up merge, error message |
209 |
def get_last_revision(branch): |
210 |
last_patch = branch.last_patch() |
|
211 |
try: |
|
212 |
return arch_revision(last_patch) |
|
213 |
except NotArchRevision: |
|
214 |
raise UserError( |
|
215 |
"Directory \"%s\" already exists, and the last revision is not" |
|
216 |
" an Arch revision (%s)" % (output_dir, last_patch)) |
|
217 |
||
218 |
||
62
by Aaron Bentley
Refactored, made version optional |
219 |
def get_remaining_revisions(output_dir, version): |
220 |
last_patch = None |
|
221 |
old_revno = None |
|
222 |
if os.path.exists(output_dir): |
|
223 |
# We are starting from an existing directory, figure out what
|
|
224 |
# the current version is
|
|
70
by Aaron Bentley
Friendlier error, when dir is not a bzr branch |
225 |
branch = find_branch(output_dir) |
66
by Aaron Bentley
Continued symlink fix, cleaned up merge, error message |
226 |
last_patch = get_last_revision(branch) |
62
by Aaron Bentley
Refactored, made version optional |
227 |
if version is None: |
228 |
version = last_patch.version |
|
229 |
elif version is None: |
|
230 |
raise UserError("No version specified, and directory does not exist.") |
|
231 |
||
71
by Aaron Bentley
Improved errors for invalid or missing versions |
232 |
try: |
233 |
ancestors = version_ancestry(version) |
|
234 |
except NoSuchVersion, e: |
|
235 |
raise UserError(e) |
|
62
by Aaron Bentley
Refactored, made version optional |
236 |
|
237 |
if last_patch: |
|
238 |
for i in range(len(ancestors)): |
|
239 |
if ancestors[i] == last_patch: |
|
240 |
break
|
|
241 |
else: |
|
242 |
raise UserError("Directory \"%s\" already exists, and the last " |
|
243 |
"revision (%s) is not in the ancestry of %s" % |
|
244 |
(output_dir, last_patch, version)) |
|
245 |
# Strip off all of the ancestors which are already present
|
|
246 |
# And get a directory starting with the latest ancestor
|
|
247 |
latest_ancestor = ancestors[i] |
|
70
by Aaron Bentley
Friendlier error, when dir is not a bzr branch |
248 |
old_revno = find_branch(output_dir).revno() |
62
by Aaron Bentley
Refactored, made version optional |
249 |
ancestors = ancestors[i+1:] |
250 |
return ancestors, old_revno |
|
10
by abentley
Refactored, made progress bar |
251 |
|
60
by Aaron Bentley
Made symlink-skipping an option |
252 |
def import_version(output_dir, version, fancy=True, fast=False, verbose=False, |
253 |
dry_run=False, max_count=None, skip_symlinks=False): |
|
7
by abentley
Completed initial construction |
254 |
"""
|
255 |
>>> q = test_environ()
|
|
256 |
>>> result_path = os.path.join(q, "result")
|
|
257 |
>>> commit_test_revisions()
|
|
71
by Aaron Bentley
Improved errors for invalid or missing versions |
258 |
>>> version = pybaz.Version("test@example.com/test--test--0.1")
|
70
by Aaron Bentley
Friendlier error, when dir is not a bzr branch |
259 |
>>> import_version('/', version, fancy=False, dry_run=True)
|
260 |
Traceback (most recent call last):
|
|
261 |
UserError: / exists, but is not a bzr branch.
|
|
68
by Aaron Bentley
got dry run and continued import under test |
262 |
>>> import_version(result_path, version, fancy=False, dry_run=True)
|
71
by Aaron Bentley
Improved errors for invalid or missing versions |
263 |
Traceback (most recent call last):
|
264 |
UserError: The version test@example.com/test--test--0.1 does not exist.
|
|
265 |
>>> version = pybaz.Version("test@example.com/test--test--0")
|
|
266 |
>>> import_version(result_path, version, fancy=False, dry_run=True)
|
|
68
by Aaron Bentley
got dry run and continued import under test |
267 |
not fancy
|
268 |
....
|
|
269 |
Dry run, not modifying output_dir
|
|
270 |
Cleaning up
|
|
35
by Aaron Bentley
Fixed, and got test cases passing |
271 |
>>> import_version(result_path, version, fancy=False)
|
272 |
not fancy
|
|
273 |
....
|
|
59
by Aaron Bentley
Applied John Meinel's options patch |
274 |
Cleaning up
|
35
by Aaron Bentley
Fixed, and got test cases passing |
275 |
Import complete.
|
68
by Aaron Bentley
got dry run and continued import under test |
276 |
>>> import_version(result_path, version, fancy=False)
|
277 |
Tree is up-to-date with test@example.com/test--test--0--patch-2
|
|
69
by Aaron Bentley
Fixed updating-revision bogosity, updated tests |
278 |
>>> commit_more_test_revisions()
|
279 |
>>> import_version(result_path, version, fancy=False)
|
|
280 |
not fancy
|
|
281 |
..
|
|
282 |
Cleaning up
|
|
283 |
Import complete.
|
|
7
by abentley
Completed initial construction |
284 |
>>> teardown_environ(q)
|
285 |
"""
|
|
70
by Aaron Bentley
Friendlier error, when dir is not a bzr branch |
286 |
try: |
287 |
ancestors, old_revno = get_remaining_revisions(output_dir, version) |
|
288 |
except NotInABranch, e: |
|
289 |
raise UserError("%s exists, but is not a bzr branch." % e.path) |
|
62
by Aaron Bentley
Refactored, made version optional |
290 |
if len(ancestors) == 0: |
70
by Aaron Bentley
Friendlier error, when dir is not a bzr branch |
291 |
last_revision = get_last_revision(find_branch(output_dir)) |
68
by Aaron Bentley
got dry run and continued import under test |
292 |
print 'Tree is up-to-date with %s' % last_revision |
293 |
return
|
|
62
by Aaron Bentley
Refactored, made version optional |
294 |
|
47
by Aaron Bentley
merged ETA changes |
295 |
progress_bar = ProgressBar() |
51
by Aaron Bentley
Ensured temp dir is on same filesystem as output dir |
296 |
tempdir = tempfile.mkdtemp(prefix="baz2bzr-", |
297 |
dir=os.path.dirname(output_dir)) |
|
7
by abentley
Completed initial construction |
298 |
try: |
35
by Aaron Bentley
Fixed, and got test cases passing |
299 |
if not fancy: |
300 |
print "not fancy" |
|
68
by Aaron Bentley
got dry run and continued import under test |
301 |
try: |
302 |
for result in iter_import_version(output_dir, ancestors, tempdir, |
|
303 |
fast=fast, verbose=verbose, dry_run=dry_run, |
|
304 |
max_count=max_count, skip_symlinks=skip_symlinks): |
|
305 |
if fancy: |
|
306 |
progress_bar(result) |
|
307 |
else: |
|
308 |
sys.stdout.write('.') |
|
309 |
finally: |
|
35
by Aaron Bentley
Fixed, and got test cases passing |
310 |
if fancy: |
68
by Aaron Bentley
got dry run and continued import under test |
311 |
clear_progress_bar() |
35
by Aaron Bentley
Fixed, and got test cases passing |
312 |
else: |
68
by Aaron Bentley
got dry run and continued import under test |
313 |
sys.stdout.write('\n') |
62
by Aaron Bentley
Refactored, made version optional |
314 |
|
315 |
if dry_run: |
|
68
by Aaron Bentley
got dry run and continued import under test |
316 |
print 'Dry run, not modifying output_dir' |
317 |
return
|
|
62
by Aaron Bentley
Refactored, made version optional |
318 |
if os.path.exists(output_dir): |
319 |
# Move the bzr control directory back, and update the working tree
|
|
320 |
tmp_bzr_dir = os.path.join(tempdir, '.bzr') |
|
321 |
||
322 |
bzr_dir = os.path.join(output_dir, '.bzr') |
|
323 |
new_bzr_dir = os.path.join(tempdir, "rd", '.bzr') |
|
324 |
||
325 |
os.rename(bzr_dir, tmp_bzr_dir) # Move the original bzr out of the way |
|
326 |
os.rename(new_bzr_dir, bzr_dir) |
|
327 |
try: |
|
69
by Aaron Bentley
Fixed updating-revision bogosity, updated tests |
328 |
bzrlib.merge.merge((output_dir, -1), (output_dir, old_revno), |
66
by Aaron Bentley
Continued symlink fix, cleaned up merge, error message |
329 |
check_clean=False, this_dir=output_dir, |
330 |
ignore_zero=True) |
|
62
by Aaron Bentley
Refactored, made version optional |
331 |
except: |
332 |
# If something failed, move back the original bzr directory
|
|
333 |
os.rename(bzr_dir, new_bzr_dir) |
|
334 |
os.rename(tmp_bzr_dir, bzr_dir) |
|
335 |
raise
|
|
336 |
else: |
|
67
by Aaron Bentley
Ensured all tests pass |
337 |
revdir = os.path.join(tempdir, "rd") |
62
by Aaron Bentley
Refactored, made version optional |
338 |
os.rename(revdir, output_dir) |
339 |
||
29
by Aaron Bentley
Nicer handling of unsupported file types |
340 |
finally: |
59
by Aaron Bentley
Applied John Meinel's options patch |
341 |
print 'Cleaning up' |
7
by abentley
Completed initial construction |
342 |
shutil.rmtree(tempdir) |
29
by Aaron Bentley
Nicer handling of unsupported file types |
343 |
print "Import complete." |
10
by abentley
Refactored, made progress bar |
344 |
|
12
by abentley
Error early when the user specifies an output directory that already exists |
345 |
class UserError(Exception): |
346 |
def __init__(self, message): |
|
347 |
"""Exception to throw when a user makes an impossible request
|
|
348 |
:param message: The message to emit when printing this exception
|
|
349 |
:type message: string
|
|
350 |
"""
|
|
351 |
Exception.__init__(self, message) |
|
352 |
||
46
by Aaron Bentley
Made bzr revision ids predictable |
353 |
def revision_id(arch_revision): |
354 |
"""
|
|
355 |
Generate a Bzr revision id from an Arch revision id. 'x' in the id
|
|
356 |
designates a revision imported with an experimental algorithm. A number
|
|
357 |
would indicate a particular standardized version.
|
|
358 |
||
359 |
:param arch_revision: The Arch revision to generate an ID for.
|
|
360 |
||
361 |
>>> revision_id(pybaz.Revision("you@example.com/cat--br--0--base-0"))
|
|
362 |
'Arch-x:you@example.com%cat--br--0--base-0'
|
|
363 |
"""
|
|
364 |
return "Arch-x:%s" % str(arch_revision).replace('/', '%') |
|
365 |
||
61
by Aaron Bentley
Factored out bzr to baz id conversion |
366 |
class NotArchRevision(Exception): |
367 |
def __init__(self, revision_id): |
|
368 |
msg = "The revision id %s does not look like it came from Arch."\ |
|
369 |
% revision_id |
|
370 |
Exception.__init__(self, msg) |
|
371 |
||
372 |
def arch_revision(revision_id): |
|
373 |
"""
|
|
67
by Aaron Bentley
Ensured all tests pass |
374 |
>>> str(arch_revision("Arch-x:jrandom@example.com%test--test--0"))
|
375 |
Traceback (most recent call last):
|
|
376 |
NotArchRevision: The revision id Arch-x:jrandom@example.com%test--test--0 does not look like it came from Arch.
|
|
377 |
>>> str(arch_revision("Arch-x:jrandom@example.com%test--test--0--base-5"))
|
|
378 |
Traceback (most recent call last):
|
|
379 |
NotArchRevision: The revision id Arch-x:jrandom@example.com%test--test--0--base-5 does not look like it came from Arch.
|
|
380 |
>>> str(arch_revision("Arch-x:jrandom@example.com%test--test--0--patch-5"))
|
|
381 |
'jrandom@example.com/test--test--0--patch-5'
|
|
61
by Aaron Bentley
Factored out bzr to baz id conversion |
382 |
"""
|
383 |
if revision_id is None: |
|
384 |
return None |
|
385 |
if revision_id[:7] != 'Arch-x:': |
|
386 |
raise NotArchRevision(revision_id) |
|
387 |
else: |
|
388 |
try: |
|
389 |
return pybaz.Revision(revision_id[7:].replace('%', '/')) |
|
390 |
except pybaz.errors.NamespaceError, e: |
|
391 |
raise NotArchRevision(revision_id) |
|
392 |
||
62
by Aaron Bentley
Refactored, made version optional |
393 |
def iter_import_version(output_dir, ancestors, tempdir, fast=False, |
60
by Aaron Bentley
Made symlink-skipping an option |
394 |
verbose=False, dry_run=False, max_count=None, |
395 |
skip_symlinks=False): |
|
10
by abentley
Refactored, made progress bar |
396 |
revdir = None |
57
by Aaron Bentley
Added John Meinel's update-with-no-{arch} patch |
397 |
|
56
by Aaron Bentley
Applied John Meinel's 'update' patch |
398 |
# Uncomment this for testing, it basically just has baz2bzr only update
|
399 |
# 5 patches at a time
|
|
59
by Aaron Bentley
Applied John Meinel's options patch |
400 |
if max_count: |
401 |
ancestors = ancestors[:max_count] |
|
402 |
||
403 |
# Not sure if I want this output. basically it tells you ahead of time
|
|
404 |
# what it is going to do, but then later it tells you as it is doing it.
|
|
405 |
# what probably would be best would be to collapse it into ranges, so that
|
|
406 |
# this gives the simple view, and then later it gives the blow by blow.
|
|
407 |
#if verbose:
|
|
408 |
# print 'Adding the following revisions:'
|
|
409 |
# for a in ancestors:
|
|
410 |
# print '\t%s' % a
|
|
411 |
||
412 |
previous_version=None |
|
56
by Aaron Bentley
Applied John Meinel's 'update' patch |
413 |
|
10
by abentley
Refactored, made progress bar |
414 |
for i in range(len(ancestors)): |
415 |
revision = ancestors[i] |
|
59
by Aaron Bentley
Applied John Meinel's options patch |
416 |
if verbose: |
417 |
version = str(revision.version) |
|
418 |
if version != previous_version: |
|
419 |
clear_progress_bar() |
|
420 |
print '\rOn version: %s' % version |
|
421 |
yield Progress(str(revision.patchlevel), i, len(ancestors)) |
|
422 |
previous_version = version |
|
423 |
else: |
|
424 |
yield Progress("revisions", i, len(ancestors)) |
|
10
by abentley
Refactored, made progress bar |
425 |
if revdir is None: |
426 |
revdir = os.path.join(tempdir, "rd") |
|
60
by Aaron Bentley
Made symlink-skipping an option |
427 |
baz_inv, log = get_revision(revdir, revision, |
428 |
skip_symlinks=skip_symlinks) |
|
62
by Aaron Bentley
Refactored, made version optional |
429 |
if os.path.exists(output_dir): |
430 |
bzr_dir = os.path.join(output_dir, '.bzr') |
|
431 |
new_bzr_dir = os.path.join(tempdir, "rd", '.bzr') |
|
432 |
# This would be much faster with a simple os.rename(), but if
|
|
433 |
# we fail, we have corrupted the original .bzr directory. Is
|
|
434 |
# that a big problem, as we can just back out the last
|
|
435 |
# revisions in .bzr/revision_history I don't really know
|
|
436 |
shutil.copytree(bzr_dir, new_bzr_dir) |
|
437 |
# Now revdir should have a tree with the latest .bzr, and the
|
|
438 |
# next revision of the baz tree
|
|
70
by Aaron Bentley
Friendlier error, when dir is not a bzr branch |
439 |
branch = find_branch(revdir) |
62
by Aaron Bentley
Refactored, made version optional |
440 |
else: |
441 |
branch = bzrlib.Branch(revdir, init=True) |
|
10
by abentley
Refactored, made progress bar |
442 |
else: |
443 |
old = os.path.join(revdir, ".bzr") |
|
444 |
new = os.path.join(tempdir, ".bzr") |
|
445 |
os.rename(old, new) |
|
60
by Aaron Bentley
Made symlink-skipping an option |
446 |
baz_inv, log = apply_revision(revdir, revision, |
447 |
skip_symlinks=skip_symlinks) |
|
10
by abentley
Refactored, made progress bar |
448 |
os.rename(new, old) |
70
by Aaron Bentley
Friendlier error, when dir is not a bzr branch |
449 |
branch = find_branch(revdir) |
37
by Aaron Bentley
More log fixups for baz2bzr |
450 |
timestamp = email.Utils.mktime_tz(log.date + (0,)) |
46
by Aaron Bentley
Made bzr revision ids predictable |
451 |
rev_id = revision_id(revision) |
52
by Aaron Bentley
Updated for new Branch locking |
452 |
branch.lock_write() |
45
by Aaron Bentley
Silenced commits |
453 |
try: |
52
by Aaron Bentley
Updated for new Branch locking |
454 |
branch.set_inventory(baz_inv) |
455 |
bzrlib.trace.silent = True |
|
45
by Aaron Bentley
Silenced commits |
456 |
branch.commit(log.summary, verbose=False, committer=log.creator, |
46
by Aaron Bentley
Made bzr revision ids predictable |
457 |
timestamp=timestamp, timezone=0, rev_id=rev_id) |
45
by Aaron Bentley
Silenced commits |
458 |
finally: |
459 |
bzrlib.trace.silent = False |
|
52
by Aaron Bentley
Updated for new Branch locking |
460 |
branch.unlock() |
10
by abentley
Refactored, made progress bar |
461 |
yield Progress("revisions", len(ancestors), len(ancestors)) |
57
by Aaron Bentley
Added John Meinel's update-with-no-{arch} patch |
462 |
unlink_unversioned(branch, revdir) |
10
by abentley
Refactored, made progress bar |
463 |
|
464 |
def unlink_unversioned(branch, revdir): |
|
465 |
for unversioned in branch.working_tree().extras(): |
|
466 |
path = os.path.join(revdir, unversioned) |
|
467 |
if os.path.isdir(path): |
|
468 |
shutil.rmtree(path) |
|
469 |
else: |
|
470 |
os.unlink(path) |
|
7
by abentley
Completed initial construction |
471 |
|
37
by Aaron Bentley
More log fixups for baz2bzr |
472 |
def get_log(tree, revision): |
473 |
log = tree.iter_logs(version=revision.version, reverse=True).next() |
|
474 |
assert log.revision == revision |
|
475 |
return log |
|
476 |
||
60
by Aaron Bentley
Made symlink-skipping an option |
477 |
def get_revision(revdir, revision, skip_symlinks=False): |
7
by abentley
Completed initial construction |
478 |
revision.get(revdir) |
479 |
tree = pybaz.tree_root(revdir) |
|
37
by Aaron Bentley
More log fixups for baz2bzr |
480 |
log = get_log(tree, revision) |
29
by Aaron Bentley
Nicer handling of unsupported file types |
481 |
try: |
60
by Aaron Bentley
Made symlink-skipping an option |
482 |
return bzr_inventory_data(tree, skip_symlinks=skip_symlinks), log |
29
by Aaron Bentley
Nicer handling of unsupported file types |
483 |
except BadFileKind, e: |
484 |
raise UserError("Cannot convert %s because %s is a %s" % (revision,e.path, e.kind) ) |
|
485 |
||
7
by abentley
Completed initial construction |
486 |
|
60
by Aaron Bentley
Made symlink-skipping an option |
487 |
def apply_revision(revdir, revision, skip_symlinks=False): |
13
by abentley
Used replay to get next revision |
488 |
tree = pybaz.tree_root(revdir) |
489 |
revision.apply(tree) |
|
37
by Aaron Bentley
More log fixups for baz2bzr |
490 |
log = get_log(tree, revision) |
29
by Aaron Bentley
Nicer handling of unsupported file types |
491 |
try: |
60
by Aaron Bentley
Made symlink-skipping an option |
492 |
return bzr_inventory_data(tree, skip_symlinks=skip_symlinks), log |
29
by Aaron Bentley
Nicer handling of unsupported file types |
493 |
except BadFileKind, e: |
494 |
raise UserError("Cannot convert %s because %s is a %s" % (revision,e.path, e.kind) ) |
|
495 |
||
496 |
||
497 |
||
498 |
||
499 |
class BadFileKind(Exception): |
|
500 |
"""The file kind is not permitted in bzr inventories"""
|
|
501 |
def __init__(self, tree_root, path, kind): |
|
502 |
self.tree_root = tree_root |
|
503 |
self.path = path |
|
504 |
self.kind = kind |
|
505 |
Exception.__init__(self, "File %s is of forbidden type %s" % |
|
506 |
(os.path.join(tree_root, path), kind)) |
|
13
by abentley
Used replay to get next revision |
507 |
|
60
by Aaron Bentley
Made symlink-skipping an option |
508 |
def bzr_inventory_data(tree, skip_symlinks=False): |
7
by abentley
Completed initial construction |
509 |
inv_iter = tree.iter_inventory_ids(source=True, both=True) |
510 |
inv_map = {} |
|
511 |
for file_id, path in inv_iter: |
|
8
by abentley
Added ability to run from the commandline |
512 |
inv_map[path] = file_id |
7
by abentley
Completed initial construction |
513 |
|
514 |
bzr_inv = [] |
|
8
by abentley
Added ability to run from the commandline |
515 |
for path, file_id in inv_map.iteritems(): |
29
by Aaron Bentley
Nicer handling of unsupported file types |
516 |
full_path = os.path.join(tree, path) |
517 |
kind = bzrlib.osutils.file_kind(full_path) |
|
60
by Aaron Bentley
Made symlink-skipping an option |
518 |
if skip_symlinks and kind == "symlink": |
55
by Aaron Bentley
Skip symlinks while importing |
519 |
continue
|
29
by Aaron Bentley
Nicer handling of unsupported file types |
520 |
if kind not in ("file", "directory"): |
521 |
raise BadFileKind(tree, path, kind) |
|
7
by abentley
Completed initial construction |
522 |
parent_dir = os.path.dirname(path) |
523 |
if parent_dir != "": |
|
524 |
parent_id = inv_map[parent_dir] |
|
525 |
else: |
|
526 |
parent_id = bzrlib.inventory.ROOT_ID |
|
527 |
bzr_inv.append((path, file_id, parent_id, kind)) |
|
528 |
bzr_inv.sort() |
|
529 |
return bzr_inv |
|
6
by abentley
added commit, timport, commit_test_revisions |
530 |
|
59
by Aaron Bentley
Applied John Meinel's options patch |
531 |
def main(args): |
532 |
"""Just the main() function for this script.
|
|
533 |
||
534 |
By separating it into a function, this can be called as a child from some other
|
|
535 |
script.
|
|
536 |
||
537 |
:param args: The arguments to this script. Essentially sys.argv[1:]
|
|
538 |
"""
|
|
539 |
import optparse |
|
62
by Aaron Bentley
Refactored, made version optional |
540 |
parser = optparse.OptionParser(usage='%prog [options] [VERSION] OUTDIR' |
59
by Aaron Bentley
Applied John Meinel's options patch |
541 |
'\n VERSION is the arch version to import.' |
542 |
'\n OUTDIR can be an existing directory to be updated' |
|
543 |
'\n or a new directory which will be created from scratch.') |
|
544 |
parser.add_option('--verbose', action='store_true' |
|
545 |
, help='Get chatty') |
|
546 |
||
60
by Aaron Bentley
Made symlink-skipping an option |
547 |
parser.add_option('--skip-symlinks', action="store_true", |
548 |
dest="skip_symlinks", |
|
549 |
help="Ignore any symlinks present in the Arch tree.") |
|
550 |
||
59
by Aaron Bentley
Applied John Meinel's options patch |
551 |
g = optparse.OptionGroup(parser, 'Test options', 'Options useful while testing process.') |
552 |
g.add_option('--test', action='store_true' |
|
553 |
, help='Run the self-tests and exit.') |
|
554 |
g.add_option('--dry-run', action='store_true' |
|
555 |
, help='Do the update, but don\'t copy the result to OUTDIR') |
|
556 |
g.add_option('--max-count', type='int', metavar='COUNT', default=None |
|
557 |
, help='At most, add COUNT patches.') |
|
558 |
g.add_option('--safe', action='store_false', dest='fast') |
|
559 |
g.add_option('--fast', action='store_true', default=False |
|
560 |
, help='By default the .bzr control directory will be copied, so that an error' |
|
561 |
' does not modify the original. --fast allows the directory to be renamed instead.') |
|
562 |
parser.add_option_group(g) |
|
563 |
||
564 |
(opts, args) = parser.parse_args(args) |
|
565 |
||
566 |
if opts.test: |
|
567 |
print "Running tests" |
|
568 |
import doctest |
|
569 |
nfail, ntests = doctest.testmod(verbose=opts.verbose) |
|
570 |
if nfail > 0: |
|
571 |
return 1 |
|
572 |
else: |
|
573 |
return 0 |
|
62
by Aaron Bentley
Refactored, made version optional |
574 |
if len(args) == 2: |
71
by Aaron Bentley
Improved errors for invalid or missing versions |
575 |
output_dir = os.path.realpath(args[1]) |
576 |
try: |
|
577 |
version = pybaz.Version(args[0]) |
|
578 |
except pybaz.errors.NamespaceError: |
|
579 |
print "%s is not a valid Arch branch." % args[0] |
|
580 |
return 1 |
|
581 |
||
62
by Aaron Bentley
Refactored, made version optional |
582 |
elif len(args) == 1: |
583 |
output_dir = os.path.realpath(args[0]) |
|
584 |
version = None |
|
585 |
else: |
|
59
by Aaron Bentley
Applied John Meinel's options patch |
586 |
print 'Invalid number of arguments, try --help for more info' |
587 |
return 1 |
|
62
by Aaron Bentley
Refactored, made version optional |
588 |
|
589 |
try: |
|
590 |
||
591 |
import_version(output_dir, version, |
|
592 |
verbose=opts.verbose, fast=opts.fast, |
|
66
by Aaron Bentley
Continued symlink fix, cleaned up merge, error message |
593 |
dry_run=opts.dry_run, max_count=opts.max_count, |
594 |
skip_symlinks=opts.skip_symlinks) |
|
62
by Aaron Bentley
Refactored, made version optional |
595 |
return 0 |
596 |
except UserError, e: |
|
597 |
print e |
|
598 |
return 1 |
|
599 |
except KeyboardInterrupt: |
|
600 |
print "Aborted." |
|
601 |
return 1 |
|
59
by Aaron Bentley
Applied John Meinel's options patch |
602 |
|
70
by Aaron Bentley
Friendlier error, when dir is not a bzr branch |
603 |
class NotInABranch(Exception): |
604 |
def __init__(self, path): |
|
605 |
Exception.__init__(self, "%s is not in a branch." % path) |
|
606 |
self.path = path |
|
607 |
||
608 |
||
609 |
def find_branch(path): |
|
610 |
"""
|
|
611 |
>>> find_branch('/')
|
|
612 |
Traceback (most recent call last):
|
|
613 |
NotInABranch: / is not in a branch.
|
|
614 |
>>> sb = bzrlib.ScratchBranch()
|
|
615 |
>>> isinstance(find_branch(sb.base), bzrlib.Branch)
|
|
616 |
True
|
|
617 |
"""
|
|
618 |
try: |
|
619 |
return bzrlib.Branch(path) |
|
620 |
except BzrError, e: |
|
621 |
if e.args[0].endswith("' is not in a branch"): |
|
622 |
raise NotInABranch(path) |
|
623 |
||
59
by Aaron Bentley
Applied John Meinel's options patch |
624 |
|
625 |
if __name__ == '__main__': |
|
626 |
sys.exit(main(sys.argv[1:])) |
|
627 |