remote-bzr: add fallback check for a partial clone
[git.git] / contrib / remote-helpers / git-remote-bzr
blob3cd65723c3ad04e0b339d17b36ece64d537f23f7
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 = get_remote_branch(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 = get_remote_branch(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(' ', 1)
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 = get_remote_branch(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 = get_remote_branch(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 clone(path, remote_branch):
769 try:
770 bdir = bzrlib.bzrdir.BzrDir.create(path)
771 except bzrlib.errors.AlreadyControlDirError:
772 bdir = bzrlib.bzrdir.BzrDir.open(path)
773 repo = bdir.find_repository()
774 repo.fetch(remote_branch.repository)
775 return remote_branch.sprout(bdir, repository=repo)
777 def get_remote_branch(name):
778 global dirname, branches
780 remote_branch = bzrlib.branch.Branch.open(branches[name])
781 if isinstance(remote_branch.user_transport, bzrlib.transport.local.LocalTransport):
782 return remote_branch
784 branch_path = os.path.join(dirname, 'clone', name)
786 try:
787 branch = bzrlib.branch.Branch.open(branch_path)
788 except bzrlib.errors.NotBranchError:
789 # clone
790 branch = clone(branch_path, remote_branch)
791 else:
792 # pull
793 try:
794 branch.pull(remote_branch, overwrite=True)
795 except bzrlib.errors.DivergedBranches:
796 # use remote branch for now
797 return remote_branch
799 return branch
801 def find_branches(repo):
802 transport = repo.bzrdir.root_transport
804 for fn in transport.iter_files_recursive():
805 if not fn.endswith('.bzr/branch-format'):
806 continue
808 name = subdir = fn[:-len('/.bzr/branch-format')]
809 name = name if name != '' else 'master'
810 name = name.replace('/', '+')
812 try:
813 cur = transport.clone(subdir)
814 branch = bzrlib.branch.Branch.open_from_transport(cur)
815 except bzrlib.errors.NotBranchError:
816 continue
817 else:
818 yield name, branch.base
820 def get_repo(url, alias):
821 global dirname, peer, branches
823 normal_url = bzrlib.urlutils.normalize_url(url)
824 origin = bzrlib.bzrdir.BzrDir.open(url)
825 is_local = isinstance(origin.transport, bzrlib.transport.local.LocalTransport)
827 shared_path = os.path.join(gitdir, 'bzr')
828 try:
829 shared_dir = bzrlib.bzrdir.BzrDir.open(shared_path)
830 except bzrlib.errors.NotBranchError:
831 shared_dir = bzrlib.bzrdir.BzrDir.create(shared_path)
832 try:
833 shared_repo = shared_dir.open_repository()
834 except bzrlib.errors.NoRepositoryPresent:
835 shared_repo = shared_dir.create_repository(shared=True)
837 if not is_local:
838 clone_path = os.path.join(dirname, 'clone')
839 if not os.path.exists(clone_path):
840 os.mkdir(clone_path)
841 else:
842 # check and remove old organization
843 try:
844 bdir = bzrlib.bzrdir.BzrDir.open(clone_path)
845 bdir.destroy_repository()
846 except bzrlib.errors.NotBranchError:
847 pass
848 except bzrlib.errors.NoRepositoryPresent:
849 pass
851 wanted = get_config('remote-bzr.branches').rstrip().split(', ')
852 # stupid python
853 wanted = [e for e in wanted if e]
855 if not wanted:
856 try:
857 repo = origin.open_repository()
858 if not repo.user_transport.listable():
859 # this repository is not usable for us
860 raise bzrlib.errors.NoRepositoryPresent(repo.bzrdir)
861 except bzrlib.errors.NoRepositoryPresent:
862 wanted = ['master']
864 if wanted:
865 def list_wanted(url, wanted):
866 for name in wanted:
867 subdir = name if name != 'master' else ''
868 yield name, bzrlib.urlutils.join(url, subdir)
870 branch_list = list_wanted(url, wanted)
871 else:
872 branch_list = find_branches(repo)
874 for name, url in branch_list:
875 if not is_local:
876 peers[name] = url
877 branches[name] = url
879 return origin
881 def fix_path(alias, orig_url):
882 url = urlparse.urlparse(orig_url, 'file')
883 if url.scheme != 'file' or os.path.isabs(url.path):
884 return
885 abs_url = urlparse.urljoin("%s/" % os.getcwd(), orig_url)
886 cmd = ['git', 'config', 'remote.%s.url' % alias, "bzr::%s" % abs_url]
887 subprocess.call(cmd)
889 def main(args):
890 global marks, prefix, gitdir, dirname
891 global tags, filenodes
892 global blob_marks
893 global parsed_refs
894 global files_cache
895 global is_tmp
896 global branches, peers
898 alias = args[1]
899 url = args[2]
901 tags = {}
902 filenodes = {}
903 blob_marks = {}
904 parsed_refs = {}
905 files_cache = {}
906 marks = None
907 branches = {}
908 peers = {}
910 if alias[5:] == url:
911 is_tmp = True
912 alias = hashlib.sha1(alias).hexdigest()
913 else:
914 is_tmp = False
916 prefix = 'refs/bzr/%s' % alias
917 gitdir = os.environ['GIT_DIR']
918 dirname = os.path.join(gitdir, 'bzr', alias)
920 if not is_tmp:
921 fix_path(alias, url)
923 if not os.path.exists(dirname):
924 os.makedirs(dirname)
926 if hasattr(bzrlib.ui.ui_factory, 'be_quiet'):
927 bzrlib.ui.ui_factory.be_quiet(True)
929 repo = get_repo(url, alias)
931 marks_path = os.path.join(dirname, 'marks-int')
932 marks = Marks(marks_path)
934 parser = Parser(repo)
935 for line in parser:
936 if parser.check('capabilities'):
937 do_capabilities(parser)
938 elif parser.check('list'):
939 do_list(parser)
940 elif parser.check('import'):
941 do_import(parser)
942 elif parser.check('export'):
943 do_export(parser)
944 else:
945 die('unhandled command: %s' % line)
946 sys.stdout.flush()
948 def bye():
949 if not marks:
950 return
951 if not is_tmp:
952 marks.store()
953 else:
954 shutil.rmtree(dirname)
956 atexit.register(bye)
957 sys.exit(main(sys.argv))