remote-bzr: reuse bzrlib transports when possible
[git.git] / contrib / remote-helpers / git-remote-bzr
blob1e0044b69fc5c87ec03833b6fb1bf434a9e0acc0
1 #!/usr/bin/env python
3 # Copyright (c) 2012 Felipe Contreras
7 # Just copy to your ~/bin, or anywhere in your $PATH.
8 # Then you can clone with:
9 # % git clone bzr::/path/to/bzr/repo/or/url
11 # For example:
12 # % git clone bzr::$HOME/myrepo
13 # or
14 # % git clone bzr::lp:myrepo
16 # If you want to specify which branches you want track (per repo):
17 # git config remote-bzr.branches 'trunk, devel, test'
20 import sys
22 import bzrlib
23 if hasattr(bzrlib, "initialize"):
24 bzrlib.initialize()
26 import bzrlib.plugin
27 bzrlib.plugin.load_plugins()
29 import bzrlib.generate_ids
30 import bzrlib.transport
31 import bzrlib.errors
32 import bzrlib.ui
33 import bzrlib.urlutils
34 import bzrlib.branch
36 import sys
37 import os
38 import json
39 import re
40 import StringIO
41 import atexit, shutil, hashlib, urlparse, subprocess
43 NAME_RE = re.compile('^([^<>]+)')
44 AUTHOR_RE = re.compile('^([^<>]+?)? ?<([^<>]*)>$')
45 EMAIL_RE = re.compile('^([^<>]+[^ \\\t<>])?\\b(?:[ \\t<>]*?)\\b([^ \\t<>]+@[^ \\t<>]+)')
46 RAW_AUTHOR_RE = re.compile('^(\w+) (.+)? <(.*)> (\d+) ([+-]\d+)')
48 def die(msg, *args):
49 sys.stderr.write('ERROR: %s\n' % (msg % args))
50 sys.exit(1)
52 def warn(msg, *args):
53 sys.stderr.write('WARNING: %s\n' % (msg % args))
55 def gittz(tz):
56 return '%+03d%02d' % (tz / 3600, tz % 3600 / 60)
58 def get_config(config):
59 cmd = ['git', 'config', '--get', config]
60 process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
61 output, _ = process.communicate()
62 return output
64 class Marks:
66 def __init__(self, path):
67 self.path = path
68 self.tips = {}
69 self.marks = {}
70 self.rev_marks = {}
71 self.last_mark = 0
72 self.load()
74 def load(self):
75 if not os.path.exists(self.path):
76 return
78 tmp = json.load(open(self.path))
79 self.tips = tmp['tips']
80 self.marks = tmp['marks']
81 self.last_mark = tmp['last-mark']
83 for rev, mark in self.marks.iteritems():
84 self.rev_marks[mark] = rev
86 def dict(self):
87 return { 'tips': self.tips, 'marks': self.marks, 'last-mark' : self.last_mark }
89 def store(self):
90 json.dump(self.dict(), open(self.path, 'w'))
92 def __str__(self):
93 return str(self.dict())
95 def from_rev(self, rev):
96 return self.marks[rev]
98 def to_rev(self, mark):
99 return str(self.rev_marks[mark])
101 def next_mark(self):
102 self.last_mark += 1
103 return self.last_mark
105 def get_mark(self, rev):
106 self.last_mark += 1
107 self.marks[rev] = self.last_mark
108 return self.last_mark
110 def is_marked(self, rev):
111 return rev in self.marks
113 def new_mark(self, rev, mark):
114 self.marks[rev] = mark
115 self.rev_marks[mark] = rev
116 self.last_mark = mark
118 def get_tip(self, branch):
119 try:
120 return str(self.tips[branch])
121 except KeyError:
122 return None
124 def set_tip(self, branch, tip):
125 self.tips[branch] = tip
127 class Parser:
129 def __init__(self, repo):
130 self.repo = repo
131 self.line = self.get_line()
133 def get_line(self):
134 return sys.stdin.readline().strip()
136 def __getitem__(self, i):
137 return self.line.split()[i]
139 def check(self, word):
140 return self.line.startswith(word)
142 def each_block(self, separator):
143 while self.line != separator:
144 yield self.line
145 self.line = self.get_line()
147 def __iter__(self):
148 return self.each_block('')
150 def next(self):
151 self.line = self.get_line()
152 if self.line == 'done':
153 self.line = None
155 def get_mark(self):
156 i = self.line.index(':') + 1
157 return int(self.line[i:])
159 def get_data(self):
160 if not self.check('data'):
161 return None
162 i = self.line.index(' ') + 1
163 size = int(self.line[i:])
164 return sys.stdin.read(size)
166 def get_author(self):
167 m = RAW_AUTHOR_RE.match(self.line)
168 if not m:
169 return None
170 _, name, email, date, tz = m.groups()
171 committer = '%s <%s>' % (name, email)
172 tz = int(tz)
173 tz = ((tz / 100) * 3600) + ((tz % 100) * 60)
174 return (committer, int(date), tz)
176 def rev_to_mark(rev):
177 global marks
178 return marks.from_rev(rev)
180 def mark_to_rev(mark):
181 global marks
182 return marks.to_rev(mark)
184 def fixup_user(user):
185 name = mail = None
186 user = user.replace('"', '')
187 m = AUTHOR_RE.match(user)
188 if m:
189 name = m.group(1)
190 mail = m.group(2).strip()
191 else:
192 m = EMAIL_RE.match(user)
193 if m:
194 name = m.group(1)
195 mail = m.group(2)
196 else:
197 m = NAME_RE.match(user)
198 if m:
199 name = m.group(1).strip()
201 if not name:
202 name = 'unknown'
203 if not mail:
204 mail = 'Unknown'
206 return '%s <%s>' % (name, mail)
208 def get_filechanges(cur, prev):
209 modified = {}
210 removed = {}
212 changes = cur.changes_from(prev)
214 def u(s):
215 return s.encode('utf-8')
217 for path, fid, kind in changes.added:
218 modified[u(path)] = fid
219 for path, fid, kind in changes.removed:
220 removed[u(path)] = None
221 for path, fid, kind, mod, _ in changes.modified:
222 modified[u(path)] = fid
223 for oldpath, newpath, fid, kind, mod, _ in changes.renamed:
224 removed[u(oldpath)] = None
225 if kind == 'directory':
226 lst = cur.list_files(from_dir=newpath, recursive=True)
227 for path, file_class, kind, fid, entry in lst:
228 if kind != 'directory':
229 modified[u(newpath + '/' + path)] = fid
230 else:
231 modified[u(newpath)] = fid
233 return modified, removed
235 def export_files(tree, files):
236 global marks, filenodes
238 final = []
239 for path, fid in files.iteritems():
240 kind = tree.kind(fid)
242 h = tree.get_file_sha1(fid)
244 if kind == 'symlink':
245 d = tree.get_symlink_target(fid)
246 mode = '120000'
247 elif kind == 'file':
249 if tree.is_executable(fid):
250 mode = '100755'
251 else:
252 mode = '100644'
254 # is the blob already exported?
255 if h in filenodes:
256 mark = filenodes[h]
257 final.append((mode, mark, path))
258 continue
260 d = tree.get_file_text(fid)
261 elif kind == 'directory':
262 continue
263 else:
264 die("Unhandled kind '%s' for path '%s'" % (kind, path))
266 mark = marks.next_mark()
267 filenodes[h] = mark
269 print "blob"
270 print "mark :%u" % mark
271 print "data %d" % len(d)
272 print d
274 final.append((mode, mark, path))
276 return final
278 def export_branch(repo, name):
279 global prefix
281 ref = '%s/heads/%s' % (prefix, name)
282 tip = marks.get_tip(name)
284 branch = get_remote_branch(name)
285 repo = branch.repository
287 branch.lock_read()
288 revs = branch.iter_merge_sorted_revisions(None, tip, 'exclude', 'forward')
289 try:
290 tip_revno = branch.revision_id_to_revno(tip)
291 last_revno, _ = branch.last_revision_info()
292 total = last_revno - tip_revno
293 except bzrlib.errors.NoSuchRevision:
294 tip_revno = 0
295 total = 0
297 for revid, _, seq, _ in revs:
299 if marks.is_marked(revid):
300 continue
302 rev = repo.get_revision(revid)
303 revno = seq[0]
305 parents = rev.parent_ids
306 time = rev.timestamp
307 tz = rev.timezone
308 committer = rev.committer.encode('utf-8')
309 committer = "%s %u %s" % (fixup_user(committer), time, gittz(tz))
310 authors = rev.get_apparent_authors()
311 if authors:
312 author = authors[0].encode('utf-8')
313 author = "%s %u %s" % (fixup_user(author), time, gittz(tz))
314 else:
315 author = committer
316 msg = rev.message.encode('utf-8')
318 msg += '\n'
320 if len(parents) == 0:
321 parent = bzrlib.revision.NULL_REVISION
322 else:
323 parent = parents[0]
325 cur_tree = repo.revision_tree(revid)
326 prev = repo.revision_tree(parent)
327 modified, removed = get_filechanges(cur_tree, prev)
329 modified_final = export_files(cur_tree, modified)
331 if len(parents) == 0:
332 print 'reset %s' % ref
334 print "commit %s" % ref
335 print "mark :%d" % (marks.get_mark(revid))
336 print "author %s" % (author)
337 print "committer %s" % (committer)
338 print "data %d" % (len(msg))
339 print msg
341 for i, p in enumerate(parents):
342 try:
343 m = rev_to_mark(p)
344 except KeyError:
345 # ghost?
346 continue
347 if i == 0:
348 print "from :%s" % m
349 else:
350 print "merge :%s" % m
352 for f in removed:
353 print "D %s" % (f,)
354 for f in modified_final:
355 print "M %s :%u %s" % f
356 print
358 if len(seq) > 1:
359 # let's skip branch revisions from the progress report
360 continue
362 progress = (revno - tip_revno)
363 if (progress % 100 == 0):
364 if total:
365 print "progress revision %d '%s' (%d/%d)" % (revno, name, progress, total)
366 else:
367 print "progress revision %d '%s' (%d)" % (revno, name, progress)
369 branch.unlock()
371 revid = branch.last_revision()
373 # make sure the ref is updated
374 print "reset %s" % ref
375 print "from :%u" % rev_to_mark(revid)
376 print
378 marks.set_tip(name, revid)
380 def export_tag(repo, name):
381 global tags, prefix
383 ref = '%s/tags/%s' % (prefix, name)
384 print "reset %s" % ref
385 print "from :%u" % rev_to_mark(tags[name])
386 print
388 def do_import(parser):
389 global dirname
391 repo = parser.repo
392 path = os.path.join(dirname, 'marks-git')
394 print "feature done"
395 if os.path.exists(path):
396 print "feature import-marks=%s" % path
397 print "feature export-marks=%s" % path
398 print "feature force"
399 sys.stdout.flush()
401 while parser.check('import'):
402 ref = parser[1]
403 if ref.startswith('refs/heads/'):
404 name = ref[len('refs/heads/'):]
405 export_branch(repo, name)
406 if ref.startswith('refs/tags/'):
407 name = ref[len('refs/tags/'):]
408 export_tag(repo, name)
409 parser.next()
411 print 'done'
413 sys.stdout.flush()
415 def parse_blob(parser):
416 global blob_marks
418 parser.next()
419 mark = parser.get_mark()
420 parser.next()
421 data = parser.get_data()
422 blob_marks[mark] = data
423 parser.next()
425 class CustomTree():
427 def __init__(self, branch, revid, parents, files):
428 global files_cache
430 self.updates = {}
431 self.branch = branch
433 def copy_tree(revid):
434 files = files_cache[revid] = {}
435 branch.lock_read()
436 tree = branch.repository.revision_tree(revid)
437 try:
438 for path, entry in tree.iter_entries_by_dir():
439 files[path] = [entry.file_id, None]
440 finally:
441 branch.unlock()
442 return files
444 if len(parents) == 0:
445 self.base_id = bzrlib.revision.NULL_REVISION
446 self.base_files = {}
447 else:
448 self.base_id = parents[0]
449 self.base_files = files_cache.get(self.base_id, None)
450 if not self.base_files:
451 self.base_files = copy_tree(self.base_id)
453 self.files = files_cache[revid] = self.base_files.copy()
454 self.rev_files = {}
456 for path, data in self.files.iteritems():
457 fid, mark = data
458 self.rev_files[fid] = [path, mark]
460 for path, f in files.iteritems():
461 fid, mark = self.files.get(path, [None, None])
462 if not fid:
463 fid = bzrlib.generate_ids.gen_file_id(path)
464 f['path'] = path
465 self.rev_files[fid] = [path, mark]
466 self.updates[fid] = f
468 def last_revision(self):
469 return self.base_id
471 def iter_changes(self):
472 changes = []
474 def get_parent(dirname, basename):
475 parent_fid, mark = self.base_files.get(dirname, [None, None])
476 if parent_fid:
477 return parent_fid
478 parent_fid, mark = self.files.get(dirname, [None, None])
479 if parent_fid:
480 return parent_fid
481 if basename == '':
482 return None
483 fid = bzrlib.generate_ids.gen_file_id(path)
484 add_entry(fid, dirname, 'directory')
485 return fid
487 def add_entry(fid, path, kind, mode = None):
488 dirname, basename = os.path.split(path)
489 parent_fid = get_parent(dirname, basename)
491 executable = False
492 if mode == '100755':
493 executable = True
494 elif mode == '120000':
495 kind = 'symlink'
497 change = (fid,
498 (None, path),
499 True,
500 (False, True),
501 (None, parent_fid),
502 (None, basename),
503 (None, kind),
504 (None, executable))
505 self.files[path] = [change[0], None]
506 changes.append(change)
508 def update_entry(fid, path, kind, mode = None):
509 dirname, basename = os.path.split(path)
510 parent_fid = get_parent(dirname, basename)
512 executable = False
513 if mode == '100755':
514 executable = True
515 elif mode == '120000':
516 kind = 'symlink'
518 change = (fid,
519 (path, path),
520 True,
521 (True, True),
522 (None, parent_fid),
523 (None, basename),
524 (None, kind),
525 (None, executable))
526 self.files[path] = [change[0], None]
527 changes.append(change)
529 def remove_entry(fid, path, kind):
530 dirname, basename = os.path.split(path)
531 parent_fid = get_parent(dirname, basename)
532 change = (fid,
533 (path, None),
534 True,
535 (True, False),
536 (parent_fid, None),
537 (None, None),
538 (None, None),
539 (None, None))
540 del self.files[path]
541 changes.append(change)
543 for fid, f in self.updates.iteritems():
544 path = f['path']
546 if 'deleted' in f:
547 remove_entry(fid, path, 'file')
548 continue
550 if path in self.base_files:
551 update_entry(fid, path, 'file', f['mode'])
552 else:
553 add_entry(fid, path, 'file', f['mode'])
555 self.files[path][1] = f['mark']
556 self.rev_files[fid][1] = f['mark']
558 return changes
560 def get_content(self, file_id):
561 path, mark = self.rev_files[file_id]
562 if mark:
563 return blob_marks[mark]
565 # last resort
566 tree = self.branch.repository.revision_tree(self.base_id)
567 return tree.get_file_text(file_id)
569 def get_file_with_stat(self, file_id, path=None):
570 content = self.get_content(file_id)
571 return (StringIO.StringIO(content), None)
573 def get_symlink_target(self, file_id):
574 return self.get_content(file_id)
576 def id2path(self, file_id):
577 path, mark = self.rev_files[file_id]
578 return path
580 def c_style_unescape(string):
581 if string[0] == string[-1] == '"':
582 return string.decode('string-escape')[1:-1]
583 return string
585 def parse_commit(parser):
586 global marks, blob_marks, parsed_refs
587 global mode
589 parents = []
591 ref = parser[1]
592 parser.next()
594 if ref.startswith('refs/heads/'):
595 name = ref[len('refs/heads/'):]
596 branch = get_remote_branch(name)
597 else:
598 die('unknown ref')
600 commit_mark = parser.get_mark()
601 parser.next()
602 author = parser.get_author()
603 parser.next()
604 committer = parser.get_author()
605 parser.next()
606 data = parser.get_data()
607 parser.next()
608 if parser.check('from'):
609 parents.append(parser.get_mark())
610 parser.next()
611 while parser.check('merge'):
612 parents.append(parser.get_mark())
613 parser.next()
615 # fast-export adds an extra newline
616 if data[-1] == '\n':
617 data = data[:-1]
619 files = {}
621 for line in parser:
622 if parser.check('M'):
623 t, m, mark_ref, path = line.split(' ', 3)
624 mark = int(mark_ref[1:])
625 f = { 'mode' : m, 'mark' : mark }
626 elif parser.check('D'):
627 t, path = line.split(' ', 1)
628 f = { 'deleted' : True }
629 else:
630 die('Unknown file command: %s' % line)
631 path = c_style_unescape(path).decode('utf-8')
632 files[path] = f
634 committer, date, tz = committer
635 parents = [mark_to_rev(p) for p in parents]
636 revid = bzrlib.generate_ids.gen_revision_id(committer, date)
637 props = {}
638 props['branch-nick'] = branch.nick
640 mtree = CustomTree(branch, revid, parents, files)
641 changes = mtree.iter_changes()
643 branch.lock_write()
644 try:
645 builder = branch.get_commit_builder(parents, None, date, tz, committer, props, revid)
646 try:
647 list(builder.record_iter_changes(mtree, mtree.last_revision(), changes))
648 builder.finish_inventory()
649 builder.commit(data.decode('utf-8', 'replace'))
650 except Exception, e:
651 builder.abort()
652 raise
653 finally:
654 branch.unlock()
656 parsed_refs[ref] = revid
657 marks.new_mark(revid, commit_mark)
659 def parse_reset(parser):
660 global parsed_refs
662 ref = parser[1]
663 parser.next()
665 # ugh
666 if parser.check('commit'):
667 parse_commit(parser)
668 return
669 if not parser.check('from'):
670 return
671 from_mark = parser.get_mark()
672 parser.next()
674 parsed_refs[ref] = mark_to_rev(from_mark)
676 def do_export(parser):
677 global parsed_refs, dirname, transports
679 parser.next()
681 for line in parser.each_block('done'):
682 if parser.check('blob'):
683 parse_blob(parser)
684 elif parser.check('commit'):
685 parse_commit(parser)
686 elif parser.check('reset'):
687 parse_reset(parser)
688 elif parser.check('tag'):
689 pass
690 elif parser.check('feature'):
691 pass
692 else:
693 die('unhandled export command: %s' % line)
695 for ref, revid in parsed_refs.iteritems():
696 if ref.startswith('refs/heads/'):
697 name = ref[len('refs/heads/'):]
698 branch = get_remote_branch(name)
699 branch.generate_revision_history(revid, marks.get_tip(name))
701 if name in peers:
702 peer = bzrlib.branch.Branch.open(peers[name],
703 possible_transports=transports)
704 try:
705 peer.bzrdir.push_branch(branch, revision_id=revid)
706 except bzrlib.errors.DivergedBranches:
707 print "error %s non-fast forward" % ref
708 continue
710 try:
711 wt = branch.bzrdir.open_workingtree()
712 wt.update()
713 except bzrlib.errors.NoWorkingTree:
714 pass
715 elif ref.startswith('refs/tags/'):
716 # TODO: implement tag push
717 print "error %s pushing tags not supported" % ref
718 continue
719 else:
720 # transport-helper/fast-export bugs
721 continue
723 print "ok %s" % ref
725 print
727 def do_capabilities(parser):
728 global dirname
730 print "import"
731 print "export"
732 print "refspec refs/heads/*:%s/heads/*" % prefix
733 print "refspec refs/tags/*:%s/tags/*" % prefix
735 path = os.path.join(dirname, 'marks-git')
737 if os.path.exists(path):
738 print "*import-marks %s" % path
739 print "*export-marks %s" % path
741 print
743 def ref_is_valid(name):
744 return not True in [c in name for c in '~^: \\']
746 def do_list(parser):
747 global tags
749 master_branch = None
751 for name in branches:
752 if not master_branch:
753 master_branch = name
754 print "? refs/heads/%s" % name
756 branch = get_remote_branch(master_branch)
757 branch.lock_read()
758 for tag, revid in branch.tags.get_tag_dict().items():
759 try:
760 branch.revision_id_to_dotted_revno(revid)
761 except bzrlib.errors.NoSuchRevision:
762 continue
763 if not ref_is_valid(tag):
764 continue
765 print "? refs/tags/%s" % tag
766 tags[tag] = revid
767 branch.unlock()
769 print "@refs/heads/%s HEAD" % master_branch
770 print
772 def clone(path, remote_branch):
773 global transports
774 try:
775 bdir = bzrlib.bzrdir.BzrDir.create(path, possible_transports=transports)
776 except bzrlib.errors.AlreadyControlDirError:
777 bdir = bzrlib.bzrdir.BzrDir.open(path, possible_transports=transports)
778 repo = bdir.find_repository()
779 repo.fetch(remote_branch.repository)
780 return remote_branch.sprout(bdir, repository=repo)
782 def get_remote_branch(name):
783 global dirname, branches, transports
785 remote_branch = bzrlib.branch.Branch.open(branches[name],
786 possible_transports=transports)
787 if isinstance(remote_branch.user_transport, bzrlib.transport.local.LocalTransport):
788 return remote_branch
790 branch_path = os.path.join(dirname, 'clone', name)
792 try:
793 branch = bzrlib.branch.Branch.open(branch_path,
794 possible_transports=transports)
795 except bzrlib.errors.NotBranchError:
796 # clone
797 branch = clone(branch_path, remote_branch)
798 else:
799 # pull
800 try:
801 branch.pull(remote_branch, overwrite=True)
802 except bzrlib.errors.DivergedBranches:
803 # use remote branch for now
804 return remote_branch
806 return branch
808 def find_branches(repo):
809 transport = repo.bzrdir.root_transport
811 for fn in transport.iter_files_recursive():
812 if not fn.endswith('.bzr/branch-format'):
813 continue
815 name = subdir = fn[:-len('/.bzr/branch-format')]
816 name = name if name != '' else 'master'
817 name = name.replace('/', '+')
819 try:
820 cur = transport.clone(subdir)
821 branch = bzrlib.branch.Branch.open_from_transport(cur)
822 except bzrlib.errors.NotBranchError:
823 continue
824 else:
825 yield name, branch.base
827 def get_repo(url, alias):
828 global dirname, peer, branches, transports
830 normal_url = bzrlib.urlutils.normalize_url(url)
831 origin = bzrlib.bzrdir.BzrDir.open(url, possible_transports=transports)
832 is_local = isinstance(origin.transport, bzrlib.transport.local.LocalTransport)
834 shared_path = os.path.join(gitdir, 'bzr')
835 try:
836 shared_dir = bzrlib.bzrdir.BzrDir.open(shared_path,
837 possible_transports=transports)
838 except bzrlib.errors.NotBranchError:
839 shared_dir = bzrlib.bzrdir.BzrDir.create(shared_path,
840 possible_transports=transports)
841 try:
842 shared_repo = shared_dir.open_repository()
843 except bzrlib.errors.NoRepositoryPresent:
844 shared_repo = shared_dir.create_repository(shared=True)
846 if not is_local:
847 clone_path = os.path.join(dirname, 'clone')
848 if not os.path.exists(clone_path):
849 os.mkdir(clone_path)
850 else:
851 # check and remove old organization
852 try:
853 bdir = bzrlib.bzrdir.BzrDir.open(clone_path,
854 possible_transports=transports)
855 bdir.destroy_repository()
856 except bzrlib.errors.NotBranchError:
857 pass
858 except bzrlib.errors.NoRepositoryPresent:
859 pass
861 wanted = get_config('remote-bzr.branches').rstrip().split(', ')
862 # stupid python
863 wanted = [e for e in wanted if e]
865 if not wanted:
866 try:
867 repo = origin.open_repository()
868 if not repo.user_transport.listable():
869 # this repository is not usable for us
870 raise bzrlib.errors.NoRepositoryPresent(repo.bzrdir)
871 except bzrlib.errors.NoRepositoryPresent:
872 wanted = ['master']
874 if wanted:
875 def list_wanted(url, wanted):
876 for name in wanted:
877 subdir = name if name != 'master' else ''
878 yield name, bzrlib.urlutils.join(url, subdir)
880 branch_list = list_wanted(url, wanted)
881 else:
882 branch_list = find_branches(repo)
884 for name, url in branch_list:
885 if not is_local:
886 peers[name] = url
887 branches[name] = url
889 return origin
891 def fix_path(alias, orig_url):
892 url = urlparse.urlparse(orig_url, 'file')
893 if url.scheme != 'file' or os.path.isabs(url.path):
894 return
895 abs_url = urlparse.urljoin("%s/" % os.getcwd(), orig_url)
896 cmd = ['git', 'config', 'remote.%s.url' % alias, "bzr::%s" % abs_url]
897 subprocess.call(cmd)
899 def main(args):
900 global marks, prefix, gitdir, dirname
901 global tags, filenodes
902 global blob_marks
903 global parsed_refs
904 global files_cache
905 global is_tmp
906 global branches, peers
907 global transports
909 alias = args[1]
910 url = args[2]
912 tags = {}
913 filenodes = {}
914 blob_marks = {}
915 parsed_refs = {}
916 files_cache = {}
917 marks = None
918 branches = {}
919 peers = {}
920 transports = []
922 if alias[5:] == url:
923 is_tmp = True
924 alias = hashlib.sha1(alias).hexdigest()
925 else:
926 is_tmp = False
928 prefix = 'refs/bzr/%s' % alias
929 gitdir = os.environ['GIT_DIR']
930 dirname = os.path.join(gitdir, 'bzr', alias)
932 if not is_tmp:
933 fix_path(alias, url)
935 if not os.path.exists(dirname):
936 os.makedirs(dirname)
938 if hasattr(bzrlib.ui.ui_factory, 'be_quiet'):
939 bzrlib.ui.ui_factory.be_quiet(True)
941 repo = get_repo(url, alias)
943 marks_path = os.path.join(dirname, 'marks-int')
944 marks = Marks(marks_path)
946 parser = Parser(repo)
947 for line in parser:
948 if parser.check('capabilities'):
949 do_capabilities(parser)
950 elif parser.check('list'):
951 do_list(parser)
952 elif parser.check('import'):
953 do_import(parser)
954 elif parser.check('export'):
955 do_export(parser)
956 else:
957 die('unhandled command: %s' % line)
958 sys.stdout.flush()
960 def bye():
961 if not marks:
962 return
963 if not is_tmp:
964 marks.store()
965 else:
966 shutil.rmtree(dirname)
968 atexit.register(bye)
969 sys.exit(main(sys.argv))