remote-bzr: fixes for older versions of bzr
[git.git] / contrib / remote-helpers / git-remote-bzr
blob10300c63d1a71aecd0f8d2273d276f36773e6b1e
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 return self.tips.get(branch, None)
121 def set_tip(self, branch, tip):
122 self.tips[branch] = tip
124 class Parser:
126 def __init__(self, repo):
127 self.repo = repo
128 self.line = self.get_line()
130 def get_line(self):
131 return sys.stdin.readline().strip()
133 def __getitem__(self, i):
134 return self.line.split()[i]
136 def check(self, word):
137 return self.line.startswith(word)
139 def each_block(self, separator):
140 while self.line != separator:
141 yield self.line
142 self.line = self.get_line()
144 def __iter__(self):
145 return self.each_block('')
147 def next(self):
148 self.line = self.get_line()
149 if self.line == 'done':
150 self.line = None
152 def get_mark(self):
153 i = self.line.index(':') + 1
154 return int(self.line[i:])
156 def get_data(self):
157 if not self.check('data'):
158 return None
159 i = self.line.index(' ') + 1
160 size = int(self.line[i:])
161 return sys.stdin.read(size)
163 def get_author(self):
164 m = RAW_AUTHOR_RE.match(self.line)
165 if not m:
166 return None
167 _, name, email, date, tz = m.groups()
168 committer = '%s <%s>' % (name, email)
169 tz = int(tz)
170 tz = ((tz / 100) * 3600) + ((tz % 100) * 60)
171 return (committer, int(date), tz)
173 def rev_to_mark(rev):
174 global marks
175 return marks.from_rev(rev)
177 def mark_to_rev(mark):
178 global marks
179 return marks.to_rev(mark)
181 def fixup_user(user):
182 name = mail = None
183 user = user.replace('"', '')
184 m = AUTHOR_RE.match(user)
185 if m:
186 name = m.group(1)
187 mail = m.group(2).strip()
188 else:
189 m = EMAIL_RE.match(user)
190 if m:
191 name = m.group(1)
192 mail = m.group(2)
193 else:
194 m = NAME_RE.match(user)
195 if m:
196 name = m.group(1).strip()
198 if not name:
199 name = 'unknown'
200 if not mail:
201 mail = 'Unknown'
203 return '%s <%s>' % (name, mail)
205 def get_filechanges(cur, prev):
206 modified = {}
207 removed = {}
209 changes = cur.changes_from(prev)
211 def u(s):
212 return s.encode('utf-8')
214 for path, fid, kind in changes.added:
215 modified[u(path)] = fid
216 for path, fid, kind in changes.removed:
217 removed[u(path)] = None
218 for path, fid, kind, mod, _ in changes.modified:
219 modified[u(path)] = fid
220 for oldpath, newpath, fid, kind, mod, _ in changes.renamed:
221 removed[u(oldpath)] = None
222 if kind == 'directory':
223 lst = cur.list_files(from_dir=newpath, recursive=True)
224 for path, file_class, kind, fid, entry in lst:
225 if kind != 'directory':
226 modified[u(newpath + '/' + path)] = fid
227 else:
228 modified[u(newpath)] = fid
230 return modified, removed
232 def export_files(tree, files):
233 global marks, filenodes
235 final = []
236 for path, fid in files.iteritems():
237 kind = tree.kind(fid)
239 h = tree.get_file_sha1(fid)
241 if kind == 'symlink':
242 d = tree.get_symlink_target(fid)
243 mode = '120000'
244 elif kind == 'file':
246 if tree.is_executable(fid):
247 mode = '100755'
248 else:
249 mode = '100644'
251 # is the blob already exported?
252 if h in filenodes:
253 mark = filenodes[h]
254 final.append((mode, mark, path))
255 continue
257 d = tree.get_file_text(fid)
258 elif kind == 'directory':
259 continue
260 else:
261 die("Unhandled kind '%s' for path '%s'" % (kind, path))
263 mark = marks.next_mark()
264 filenodes[h] = mark
266 print "blob"
267 print "mark :%u" % mark
268 print "data %d" % len(d)
269 print d
271 final.append((mode, mark, path))
273 return final
275 def export_branch(repo, name):
276 global prefix
278 ref = '%s/heads/%s' % (prefix, name)
279 tip = marks.get_tip(name)
281 branch = bzrlib.branch.Branch.open(branches[name])
282 repo = branch.repository
284 branch.lock_read()
285 revs = branch.iter_merge_sorted_revisions(None, tip, 'exclude', 'forward')
286 try:
287 tip_revno = branch.revision_id_to_revno(tip)
288 last_revno, _ = branch.last_revision_info()
289 total = last_revno - tip_revno
290 except bzrlib.errors.NoSuchRevision:
291 tip_revno = 0
292 total = 0
294 for revid, _, seq, _ in revs:
296 if marks.is_marked(revid):
297 continue
299 rev = repo.get_revision(revid)
300 revno = seq[0]
302 parents = rev.parent_ids
303 time = rev.timestamp
304 tz = rev.timezone
305 committer = rev.committer.encode('utf-8')
306 committer = "%s %u %s" % (fixup_user(committer), time, gittz(tz))
307 authors = rev.get_apparent_authors()
308 if authors:
309 author = authors[0].encode('utf-8')
310 author = "%s %u %s" % (fixup_user(author), time, gittz(tz))
311 else:
312 author = committer
313 msg = rev.message.encode('utf-8')
315 msg += '\n'
317 if len(parents) == 0:
318 parent = bzrlib.revision.NULL_REVISION
319 else:
320 parent = parents[0]
322 cur_tree = repo.revision_tree(revid)
323 prev = repo.revision_tree(parent)
324 modified, removed = get_filechanges(cur_tree, prev)
326 modified_final = export_files(cur_tree, modified)
328 if len(parents) == 0:
329 print 'reset %s' % ref
331 print "commit %s" % ref
332 print "mark :%d" % (marks.get_mark(revid))
333 print "author %s" % (author)
334 print "committer %s" % (committer)
335 print "data %d" % (len(msg))
336 print msg
338 for i, p in enumerate(parents):
339 try:
340 m = rev_to_mark(p)
341 except KeyError:
342 # ghost?
343 continue
344 if i == 0:
345 print "from :%s" % m
346 else:
347 print "merge :%s" % m
349 for f in removed:
350 print "D %s" % (f,)
351 for f in modified_final:
352 print "M %s :%u %s" % f
353 print
355 if len(seq) > 1:
356 # let's skip branch revisions from the progress report
357 continue
359 progress = (revno - tip_revno)
360 if (progress % 100 == 0):
361 if total:
362 print "progress revision %d '%s' (%d/%d)" % (revno, name, progress, total)
363 else:
364 print "progress revision %d '%s' (%d)" % (revno, name, progress)
366 branch.unlock()
368 revid = branch.last_revision()
370 # make sure the ref is updated
371 print "reset %s" % ref
372 print "from :%u" % rev_to_mark(revid)
373 print
375 marks.set_tip(name, revid)
377 def export_tag(repo, name):
378 global tags, prefix
380 ref = '%s/tags/%s' % (prefix, name)
381 print "reset %s" % ref
382 print "from :%u" % rev_to_mark(tags[name])
383 print
385 def do_import(parser):
386 global dirname
388 repo = parser.repo
389 path = os.path.join(dirname, 'marks-git')
391 print "feature done"
392 if os.path.exists(path):
393 print "feature import-marks=%s" % path
394 print "feature export-marks=%s" % path
395 print "feature force"
396 sys.stdout.flush()
398 while parser.check('import'):
399 ref = parser[1]
400 if ref.startswith('refs/heads/'):
401 name = ref[len('refs/heads/'):]
402 export_branch(repo, name)
403 if ref.startswith('refs/tags/'):
404 name = ref[len('refs/tags/'):]
405 export_tag(repo, name)
406 parser.next()
408 print 'done'
410 sys.stdout.flush()
412 def parse_blob(parser):
413 global blob_marks
415 parser.next()
416 mark = parser.get_mark()
417 parser.next()
418 data = parser.get_data()
419 blob_marks[mark] = data
420 parser.next()
422 class CustomTree():
424 def __init__(self, branch, revid, parents, files):
425 global files_cache
427 self.updates = {}
428 self.branch = branch
430 def copy_tree(revid):
431 files = files_cache[revid] = {}
432 branch.lock_read()
433 tree = branch.repository.revision_tree(revid)
434 try:
435 for path, entry in tree.iter_entries_by_dir():
436 files[path] = [entry.file_id, None]
437 finally:
438 branch.unlock()
439 return files
441 if len(parents) == 0:
442 self.base_id = bzrlib.revision.NULL_REVISION
443 self.base_files = {}
444 else:
445 self.base_id = parents[0]
446 self.base_files = files_cache.get(self.base_id, None)
447 if not self.base_files:
448 self.base_files = copy_tree(self.base_id)
450 self.files = files_cache[revid] = self.base_files.copy()
451 self.rev_files = {}
453 for path, data in self.files.iteritems():
454 fid, mark = data
455 self.rev_files[fid] = [path, mark]
457 for path, f in files.iteritems():
458 fid, mark = self.files.get(path, [None, None])
459 if not fid:
460 fid = bzrlib.generate_ids.gen_file_id(path)
461 f['path'] = path
462 self.rev_files[fid] = [path, mark]
463 self.updates[fid] = f
465 def last_revision(self):
466 return self.base_id
468 def iter_changes(self):
469 changes = []
471 def get_parent(dirname, basename):
472 parent_fid, mark = self.base_files.get(dirname, [None, None])
473 if parent_fid:
474 return parent_fid
475 parent_fid, mark = self.files.get(dirname, [None, None])
476 if parent_fid:
477 return parent_fid
478 if basename == '':
479 return None
480 fid = bzrlib.generate_ids.gen_file_id(path)
481 add_entry(fid, dirname, 'directory')
482 return fid
484 def add_entry(fid, path, kind, mode = None):
485 dirname, basename = os.path.split(path)
486 parent_fid = get_parent(dirname, basename)
488 executable = False
489 if mode == '100755':
490 executable = True
491 elif mode == '120000':
492 kind = 'symlink'
494 change = (fid,
495 (None, path),
496 True,
497 (False, True),
498 (None, parent_fid),
499 (None, basename),
500 (None, kind),
501 (None, executable))
502 self.files[path] = [change[0], None]
503 changes.append(change)
505 def update_entry(fid, path, kind, mode = None):
506 dirname, basename = os.path.split(path)
507 parent_fid = get_parent(dirname, basename)
509 executable = False
510 if mode == '100755':
511 executable = True
512 elif mode == '120000':
513 kind = 'symlink'
515 change = (fid,
516 (path, path),
517 True,
518 (True, True),
519 (None, parent_fid),
520 (None, basename),
521 (None, kind),
522 (None, executable))
523 self.files[path] = [change[0], None]
524 changes.append(change)
526 def remove_entry(fid, path, kind):
527 dirname, basename = os.path.split(path)
528 parent_fid = get_parent(dirname, basename)
529 change = (fid,
530 (path, None),
531 True,
532 (True, False),
533 (parent_fid, None),
534 (None, None),
535 (None, None),
536 (None, None))
537 del self.files[path]
538 changes.append(change)
540 for fid, f in self.updates.iteritems():
541 path = f['path']
543 if 'deleted' in f:
544 remove_entry(fid, path, 'file')
545 continue
547 if path in self.base_files:
548 update_entry(fid, path, 'file', f['mode'])
549 else:
550 add_entry(fid, path, 'file', f['mode'])
552 self.files[path][1] = f['mark']
553 self.rev_files[fid][1] = f['mark']
555 return changes
557 def get_content(self, file_id):
558 path, mark = self.rev_files[file_id]
559 if mark:
560 return blob_marks[mark]
562 # last resort
563 tree = self.branch.repository.revision_tree(self.base_id)
564 return tree.get_file_text(file_id)
566 def get_file_with_stat(self, file_id, path=None):
567 content = self.get_content(file_id)
568 return (StringIO.StringIO(content), None)
570 def get_symlink_target(self, file_id):
571 return self.get_content(file_id)
573 def id2path(self, file_id):
574 path, mark = self.rev_files[file_id]
575 return path
577 def c_style_unescape(string):
578 if string[0] == string[-1] == '"':
579 return string.decode('string-escape')[1:-1]
580 return string
582 def parse_commit(parser):
583 global marks, blob_marks, parsed_refs
584 global mode
586 parents = []
588 ref = parser[1]
589 parser.next()
591 if ref.startswith('refs/heads/'):
592 name = ref[len('refs/heads/'):]
593 branch = bzrlib.branch.Branch.open(branches[name])
594 else:
595 die('unknown ref')
597 commit_mark = parser.get_mark()
598 parser.next()
599 author = parser.get_author()
600 parser.next()
601 committer = parser.get_author()
602 parser.next()
603 data = parser.get_data()
604 parser.next()
605 if parser.check('from'):
606 parents.append(parser.get_mark())
607 parser.next()
608 while parser.check('merge'):
609 parents.append(parser.get_mark())
610 parser.next()
612 # fast-export adds an extra newline
613 if data[-1] == '\n':
614 data = data[:-1]
616 files = {}
618 for line in parser:
619 if parser.check('M'):
620 t, m, mark_ref, path = line.split(' ', 3)
621 mark = int(mark_ref[1:])
622 f = { 'mode' : m, 'mark' : mark }
623 elif parser.check('D'):
624 t, path = line.split(' ')
625 f = { 'deleted' : True }
626 else:
627 die('Unknown file command: %s' % line)
628 path = c_style_unescape(path).decode('utf-8')
629 files[path] = f
631 committer, date, tz = committer
632 parents = [mark_to_rev(p) for p in parents]
633 revid = bzrlib.generate_ids.gen_revision_id(committer, date)
634 props = {}
635 props['branch-nick'] = branch.nick
637 mtree = CustomTree(branch, revid, parents, files)
638 changes = mtree.iter_changes()
640 branch.lock_write()
641 try:
642 builder = branch.get_commit_builder(parents, None, date, tz, committer, props, revid)
643 try:
644 list(builder.record_iter_changes(mtree, mtree.last_revision(), changes))
645 builder.finish_inventory()
646 builder.commit(data.decode('utf-8', 'replace'))
647 except Exception, e:
648 builder.abort()
649 raise
650 finally:
651 branch.unlock()
653 parsed_refs[ref] = revid
654 marks.new_mark(revid, commit_mark)
656 def parse_reset(parser):
657 global parsed_refs
659 ref = parser[1]
660 parser.next()
662 # ugh
663 if parser.check('commit'):
664 parse_commit(parser)
665 return
666 if not parser.check('from'):
667 return
668 from_mark = parser.get_mark()
669 parser.next()
671 parsed_refs[ref] = mark_to_rev(from_mark)
673 def do_export(parser):
674 global parsed_refs, dirname
676 parser.next()
678 for line in parser.each_block('done'):
679 if parser.check('blob'):
680 parse_blob(parser)
681 elif parser.check('commit'):
682 parse_commit(parser)
683 elif parser.check('reset'):
684 parse_reset(parser)
685 elif parser.check('tag'):
686 pass
687 elif parser.check('feature'):
688 pass
689 else:
690 die('unhandled export command: %s' % line)
692 for ref, revid in parsed_refs.iteritems():
693 if ref.startswith('refs/heads/'):
694 name = ref[len('refs/heads/'):]
695 branch = bzrlib.branch.Branch.open(branches[name])
696 branch.generate_revision_history(revid, marks.get_tip(name))
698 if name in peers:
699 peer = bzrlib.branch.Branch.open(peers[name])
700 try:
701 peer.bzrdir.push_branch(branch, revision_id=revid)
702 except bzrlib.errors.DivergedBranches:
703 print "error %s non-fast forward" % ref
704 continue
706 try:
707 wt = branch.bzrdir.open_workingtree()
708 wt.update()
709 except bzrlib.errors.NoWorkingTree:
710 pass
711 elif ref.startswith('refs/tags/'):
712 # TODO: implement tag push
713 print "error %s pushing tags not supported" % ref
714 continue
715 else:
716 # transport-helper/fast-export bugs
717 continue
719 print "ok %s" % ref
721 print
723 def do_capabilities(parser):
724 global dirname
726 print "import"
727 print "export"
728 print "refspec refs/heads/*:%s/heads/*" % prefix
729 print "refspec refs/tags/*:%s/tags/*" % prefix
731 path = os.path.join(dirname, 'marks-git')
733 if os.path.exists(path):
734 print "*import-marks %s" % path
735 print "*export-marks %s" % path
737 print
739 def ref_is_valid(name):
740 return not True in [c in name for c in '~^: \\']
742 def do_list(parser):
743 global tags
745 master_branch = None
747 for name in branches:
748 if not master_branch:
749 master_branch = name
750 print "? refs/heads/%s" % name
752 branch = bzrlib.branch.Branch.open(branches[master_branch])
753 branch.lock_read()
754 for tag, revid in branch.tags.get_tag_dict().items():
755 try:
756 branch.revision_id_to_dotted_revno(revid)
757 except bzrlib.errors.NoSuchRevision:
758 continue
759 if not ref_is_valid(tag):
760 continue
761 print "? refs/tags/%s" % tag
762 tags[tag] = revid
763 branch.unlock()
765 print "@refs/heads/%s HEAD" % master_branch
766 print
768 def get_remote_branch(origin, remote_branch, name):
769 global dirname, peers
771 branch_path = os.path.join(dirname, 'clone', name)
772 if os.path.exists(branch_path):
773 # pull
774 d = bzrlib.bzrdir.BzrDir.open(branch_path)
775 branch = d.open_branch()
776 try:
777 branch.pull(remote_branch, [], None, False)
778 except bzrlib.errors.DivergedBranches:
779 # use remote branch for now
780 return remote_branch
781 else:
782 # clone
783 d = origin.sprout(branch_path, None,
784 hardlink=True, create_tree_if_local=False,
785 force_new_repo=False,
786 source_branch=remote_branch)
787 branch = d.open_branch()
789 return branch
791 def find_branches(repo, wanted):
792 transport = repo.bzrdir.root_transport
794 for fn in transport.iter_files_recursive():
795 if not fn.endswith('.bzr/branch-format'):
796 continue
798 name = subdir = fn[:-len('/.bzr/branch-format')]
799 name = name if name != '' else 'master'
800 name = name.replace('/', '+')
802 if wanted and not name in wanted:
803 continue
805 try:
806 cur = transport.clone(subdir)
807 branch = bzrlib.branch.Branch.open_from_transport(cur)
808 except bzrlib.errors.NotBranchError:
809 continue
810 else:
811 yield name, branch
813 def get_repo(url, alias):
814 global dirname, peer, branches
816 normal_url = bzrlib.urlutils.normalize_url(url)
817 origin = bzrlib.bzrdir.BzrDir.open(url)
818 is_local = isinstance(origin.transport, bzrlib.transport.local.LocalTransport)
820 shared_path = os.path.join(gitdir, 'bzr')
821 try:
822 shared_dir = bzrlib.bzrdir.BzrDir.open(shared_path)
823 except bzrlib.errors.NotBranchError:
824 shared_dir = bzrlib.bzrdir.BzrDir.create(shared_path)
825 try:
826 shared_repo = shared_dir.open_repository()
827 except bzrlib.errors.NoRepositoryPresent:
828 shared_repo = shared_dir.create_repository(shared=True)
830 if not is_local:
831 clone_path = os.path.join(dirname, 'clone')
832 if not os.path.exists(clone_path):
833 os.mkdir(clone_path)
834 else:
835 # check and remove old organization
836 try:
837 bdir = bzrlib.bzrdir.BzrDir.open(clone_path)
838 bdir.destroy_repository()
839 except bzrlib.errors.NotBranchError:
840 pass
841 except bzrlib.errors.NoRepositoryPresent:
842 pass
844 try:
845 repo = origin.open_repository()
846 if not repo.user_transport.listable():
847 # this repository is not usable for us
848 raise bzrlib.errors.NoRepositoryPresent(repo.bzrdir)
849 except bzrlib.errors.NoRepositoryPresent:
850 # branch
852 name = 'master'
853 remote_branch = origin.open_branch()
855 if not is_local:
856 peers[name] = remote_branch.base
857 branch = get_remote_branch(origin, remote_branch, name)
858 else:
859 branch = remote_branch
861 branches[name] = branch.base
863 return branch.repository
864 else:
865 # repository
867 wanted = get_config('remote-bzr.branches').rstrip().split(', ')
868 # stupid python
869 wanted = [e for e in wanted if e]
871 for name, remote_branch in find_branches(repo, wanted):
873 if not is_local:
874 peers[name] = remote_branch.base
875 branch = get_remote_branch(origin, remote_branch, name)
876 else:
877 branch = remote_branch
879 branches[name] = branch.base
881 return repo
883 def fix_path(alias, orig_url):
884 url = urlparse.urlparse(orig_url, 'file')
885 if url.scheme != 'file' or os.path.isabs(url.path):
886 return
887 abs_url = urlparse.urljoin("%s/" % os.getcwd(), orig_url)
888 cmd = ['git', 'config', 'remote.%s.url' % alias, "bzr::%s" % abs_url]
889 subprocess.call(cmd)
891 def main(args):
892 global marks, prefix, gitdir, dirname
893 global tags, filenodes
894 global blob_marks
895 global parsed_refs
896 global files_cache
897 global is_tmp
898 global branches, peers
900 alias = args[1]
901 url = args[2]
903 tags = {}
904 filenodes = {}
905 blob_marks = {}
906 parsed_refs = {}
907 files_cache = {}
908 marks = None
909 branches = {}
910 peers = {}
912 if alias[5:] == url:
913 is_tmp = True
914 alias = hashlib.sha1(alias).hexdigest()
915 else:
916 is_tmp = False
918 prefix = 'refs/bzr/%s' % alias
919 gitdir = os.environ['GIT_DIR']
920 dirname = os.path.join(gitdir, 'bzr', alias)
922 if not is_tmp:
923 fix_path(alias, url)
925 if not os.path.exists(dirname):
926 os.makedirs(dirname)
928 if hasattr(bzrlib.ui.ui_factory, 'be_quiet'):
929 bzrlib.ui.ui_factory.be_quiet(True)
931 repo = get_repo(url, alias)
933 marks_path = os.path.join(dirname, 'marks-int')
934 marks = Marks(marks_path)
936 parser = Parser(repo)
937 for line in parser:
938 if parser.check('capabilities'):
939 do_capabilities(parser)
940 elif parser.check('list'):
941 do_list(parser)
942 elif parser.check('import'):
943 do_import(parser)
944 elif parser.check('export'):
945 do_export(parser)
946 else:
947 die('unhandled command: %s' % line)
948 sys.stdout.flush()
950 def bye():
951 if not marks:
952 return
953 if not is_tmp:
954 marks.store()
955 else:
956 shutil.rmtree(dirname)
958 atexit.register(bye)
959 sys.exit(main(sys.argv))