17
17
# along with this program; if not, write to the Free Software
18
18
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21
from errors import NoPyBaz
23
from baz_import import import_version, UserError
25
print >> sys.stderr, "This command requires PyBaz. Please ensure that it is installed."
24
print "This command requires PyBaz. Please ensure that it is installed."
27
from pybaz.backends.baz import null_cmd
33
from bzrlib.errors import BzrError
38
from progress import *
40
def add_id(files, id=None):
41
"""Adds an explicit id to a list of files.
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
50
args.extend(["--id", id])
56
>>> q = test_environ()
59
>>> os.path.exists(os.path.join(q, "home", ".arch-params"))
61
>>> teardown_environ(q)
65
tdir = tempfile.mkdtemp(prefix="testdir-")
66
os.environ["HOME"] = os.path.join(tdir, "home")
67
os.mkdir(os.environ["HOME"])
68
arch_dir = os.path.join(tdir, "archive_dir")
69
pybaz.make_archive("test@example.com", arch_dir)
70
work_dir = os.path.join(tdir, "work_dir")
73
pybaz.init_tree(work_dir, "test@example.com/test--test--0")
74
lib_dir = os.path.join(tdir, "lib_dir")
76
pybaz.register_revision_library(lib_dir)
77
pybaz.set_my_id("Test User<test@example.org>")
80
def add_file(path, text, id):
82
>>> q = test_environ()
83
>>> add_file("path with space", "text", "lalala")
84
>>> tree = pybaz.tree_root(".")
85
>>> inv = list(tree.iter_inventory_ids(source=True, both=True))
86
>>> ("x_lalala", "path with space") in inv
88
>>> teardown_environ(q)
90
file(path, "wb").write(text)
94
def add_dir(path, id):
96
>>> q = test_environ()
97
>>> add_dir("path with\(sp) space", "lalala")
98
>>> tree = pybaz.tree_root(".")
99
>>> inv = list(tree.iter_inventory_ids(source=True, both=True))
100
>>> ("x_lalala", "path with\(sp) space") in inv
102
>>> teardown_environ(q)
107
def teardown_environ(tdir):
111
def timport(tree, summary):
112
msg = tree.log_message()
113
msg["summary"] = summary
116
def commit(tree, summary):
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()]
126
'test@example.com/test--test--0--base-0'
128
'test@example.com/test--test--0--patch-1'
129
>>> teardown_environ(q)
131
msg = tree.log_message()
132
msg["summary"] = summary
135
def commit_test_revisions():
137
>>> q = test_environ()
138
>>> commit_test_revisions()
139
>>> a = pybaz.Archive("test@example.com")
140
>>> revisions = list(a.iter_revisions("test--test--0"))
143
>>> str(revisions[2])
144
'test@example.com/test--test--0--base-0'
145
>>> str(revisions[1])
146
'test@example.com/test--test--0--patch-1'
147
>>> str(revisions[0])
148
'test@example.com/test--test--0--patch-2'
149
>>> teardown_environ(q)
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")
156
add_file("ofile", "this is another file", "ofile by aaron")
157
commit(tree, "altered mainfile")
160
def commit_more_test_revisions():
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"))
169
>>> str(revisions[0])
170
'test@example.com/test--test--0--patch-3'
171
>>> teardown_environ(q)
173
tree = pybaz.tree_root(".")
174
add_file("trainfile", "void train(void){}", "trainfile by aaron")
175
commit(tree, "altered trainfile")
177
class NoSuchVersion(Exception):
178
def __init__(self, version):
179
Exception.__init__(self, "The version %s does not exist." % version)
180
self.version = version
182
def version_ancestry(version):
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'
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.
196
>>> teardown_environ(q)
199
revision = version.iter_revisions(reverse=True).next()
201
if not version.exists():
202
raise NoSuchVersion(version)
205
ancestors = list(revision.iter_ancestors(metoo=True))
209
def get_last_revision(branch):
210
last_patch = branch.last_patch()
212
return arch_revision(last_patch)
213
except NotArchRevision:
215
"Directory \"%s\" already exists, and the last revision is not"
216
" an Arch revision (%s)" % (output_dir, last_patch))
219
def get_remaining_revisions(output_dir, version):
222
if os.path.exists(output_dir):
223
# We are starting from an existing directory, figure out what
224
# the current version is
225
branch = find_branch(output_dir)
226
last_patch = get_last_revision(branch)
228
version = last_patch.version
229
elif version is None:
230
raise UserError("No version specified, and directory does not exist.")
233
ancestors = version_ancestry(version)
234
except NoSuchVersion, e:
238
for i in range(len(ancestors)):
239
if ancestors[i] == last_patch:
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]
248
old_revno = find_branch(output_dir).revno()
249
ancestors = ancestors[i+1:]
250
return ancestors, old_revno
252
def import_version(output_dir, version, fancy=True, fast=False, verbose=False,
253
dry_run=False, max_count=None, skip_symlinks=False):
255
>>> q = test_environ()
256
>>> result_path = os.path.join(q, "result")
257
>>> commit_test_revisions()
258
>>> version = pybaz.Version("test@example.com/test--test--0.1")
259
>>> import_version('/', version, fancy=False, dry_run=True)
260
Traceback (most recent call last):
261
UserError: / exists, but is not a bzr branch.
262
>>> import_version(result_path, version, fancy=False, dry_run=True)
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)
269
Dry run, not modifying output_dir
271
>>> import_version(result_path, version, fancy=False)
276
>>> import_version(result_path, version, fancy=False)
277
Tree is up-to-date with test@example.com/test--test--0--patch-2
278
>>> commit_more_test_revisions()
279
>>> import_version(result_path, version, fancy=False)
284
>>> teardown_environ(q)
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)
290
if len(ancestors) == 0:
291
last_revision = get_last_revision(find_branch(output_dir))
292
print 'Tree is up-to-date with %s' % last_revision
295
progress_bar = ProgressBar()
296
tempdir = tempfile.mkdtemp(prefix="baz2bzr-",
297
dir=os.path.dirname(output_dir))
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):
308
sys.stdout.write('.')
313
sys.stdout.write('\n')
316
print 'Dry run, not modifying output_dir'
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')
322
bzr_dir = os.path.join(output_dir, '.bzr')
323
new_bzr_dir = os.path.join(tempdir, "rd", '.bzr')
325
os.rename(bzr_dir, tmp_bzr_dir) # Move the original bzr out of the way
326
os.rename(new_bzr_dir, bzr_dir)
328
bzrlib.merge.merge((output_dir, -1), (output_dir, old_revno),
329
check_clean=False, this_dir=output_dir,
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)
337
revdir = os.path.join(tempdir, "rd")
338
os.rename(revdir, output_dir)
342
shutil.rmtree(tempdir)
343
print "Import complete."
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
351
Exception.__init__(self, message)
353
def revision_id(arch_revision):
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.
359
:param arch_revision: The Arch revision to generate an ID for.
361
>>> revision_id(pybaz.Revision("you@example.com/cat--br--0--base-0"))
362
'Arch-x:you@example.com%cat--br--0--base-0'
364
return "Arch-x:%s" % str(arch_revision).replace('/', '%')
366
class NotArchRevision(Exception):
367
def __init__(self, revision_id):
368
msg = "The revision id %s does not look like it came from Arch."\
370
Exception.__init__(self, msg)
372
def arch_revision(revision_id):
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'
383
if revision_id is None:
385
if revision_id[:7] != 'Arch-x:':
386
raise NotArchRevision(revision_id)
389
return pybaz.Revision(revision_id[7:].replace('%', '/'))
390
except pybaz.errors.NamespaceError, e:
391
raise NotArchRevision(revision_id)
393
def iter_import_version(output_dir, ancestors, tempdir, fast=False,
394
verbose=False, dry_run=False, max_count=None,
395
skip_symlinks=False):
398
# Uncomment this for testing, it basically just has baz2bzr only update
399
# 5 patches at a time
401
ancestors = ancestors[:max_count]
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.
408
# print 'Adding the following revisions:'
409
# for a in ancestors:
412
previous_version=None
414
for i in range(len(ancestors)):
415
revision = ancestors[i]
417
version = str(revision.version)
418
if version != previous_version:
420
print '\rOn version: %s' % version
421
yield Progress(str(revision.patchlevel), i, len(ancestors))
422
previous_version = version
424
yield Progress("revisions", i, len(ancestors))
426
revdir = os.path.join(tempdir, "rd")
427
baz_inv, log = get_revision(revdir, revision,
428
skip_symlinks=skip_symlinks)
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
439
branch = find_branch(revdir)
441
branch = bzrlib.Branch(revdir, init=True)
443
old = os.path.join(revdir, ".bzr")
444
new = os.path.join(tempdir, ".bzr")
446
baz_inv, log = apply_revision(revdir, revision,
447
skip_symlinks=skip_symlinks)
449
branch = find_branch(revdir)
450
timestamp = email.Utils.mktime_tz(log.date + (0,))
451
rev_id = revision_id(revision)
454
branch.set_inventory(baz_inv)
455
bzrlib.trace.silent = True
456
branch.commit(log.summary, verbose=False, committer=log.creator,
457
timestamp=timestamp, timezone=0, rev_id=rev_id)
459
bzrlib.trace.silent = False
461
yield Progress("revisions", len(ancestors), len(ancestors))
462
unlink_unversioned(branch, revdir)
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):
472
def get_log(tree, revision):
473
log = tree.iter_logs(version=revision.version, reverse=True).next()
474
assert log.revision == revision
477
def get_revision(revdir, revision, skip_symlinks=False):
479
tree = pybaz.tree_root(revdir)
480
log = get_log(tree, revision)
482
return bzr_inventory_data(tree, skip_symlinks=skip_symlinks), log
483
except BadFileKind, e:
484
raise UserError("Cannot convert %s because %s is a %s" % (revision,e.path, e.kind) )
487
def apply_revision(revdir, revision, skip_symlinks=False):
488
tree = pybaz.tree_root(revdir)
490
log = get_log(tree, revision)
492
return bzr_inventory_data(tree, skip_symlinks=skip_symlinks), log
493
except BadFileKind, e:
494
raise UserError("Cannot convert %s because %s is a %s" % (revision,e.path, e.kind) )
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
505
Exception.__init__(self, "File %s is of forbidden type %s" %
506
(os.path.join(tree_root, path), kind))
508
def bzr_inventory_data(tree, skip_symlinks=False):
509
inv_iter = tree.iter_inventory_ids(source=True, both=True)
511
for file_id, path in inv_iter:
512
inv_map[path] = file_id
515
for path, file_id in inv_map.iteritems():
516
full_path = os.path.join(tree, path)
517
kind = bzrlib.osutils.file_kind(full_path)
518
if skip_symlinks and kind == "symlink":
520
if kind not in ("file", "directory"):
521
raise BadFileKind(tree, path, kind)
522
parent_dir = os.path.dirname(path)
524
parent_id = inv_map[parent_dir]
526
parent_id = bzrlib.inventory.ROOT_ID
527
bzr_inv.append((path, file_id, parent_id, kind))
32
532
"""Just the main() function for this script.