contacts: reduce git-blame invocations
[git.git] / contrib / remote-helpers / git-remote-bzr
blobc3a3cac77b875b2cb925971e2baca141f9c4f163
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
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 try:
704 peer.bzrdir.push_branch(branch, revision_id=revid)
705 except bzrlib.errors.DivergedBranches:
706 print "error %s non-fast forward" % ref
707 continue
709 try:
710 wt = branch.bzrdir.open_workingtree()
711 wt.update()
712 except bzrlib.errors.NoWorkingTree:
713 pass
714 elif ref.startswith('refs/tags/'):
715 # TODO: implement tag push
716 print "error %s pushing tags not supported" % ref
717 continue
718 else:
719 # transport-helper/fast-export bugs
720 continue
722 print "ok %s" % ref
724 print
726 def do_capabilities(parser):
727 global dirname
729 print "import"
730 print "export"
731 print "refspec refs/heads/*:%s/heads/*" % prefix
732 print "refspec refs/tags/*:%s/tags/*" % prefix
734 path = os.path.join(dirname, 'marks-git')
736 if os.path.exists(path):
737 print "*import-marks %s" % path
738 print "*export-marks %s" % path
740 print
742 def ref_is_valid(name):
743 return not True in [c in name for c in '~^: \\']
745 def do_list(parser):
746 global tags
748 master_branch = None
750 for name in branches:
751 if not master_branch:
752 master_branch = name
753 print "? refs/heads/%s" % name
755 branch = get_remote_branch(master_branch)
756 branch.lock_read()
757 for tag, revid in branch.tags.get_tag_dict().items():
758 try:
759 branch.revision_id_to_dotted_revno(revid)
760 except bzrlib.errors.NoSuchRevision:
761 continue
762 if not ref_is_valid(tag):
763 continue
764 print "? refs/tags/%s" % tag
765 tags[tag] = revid
766 branch.unlock()
768 print "@refs/heads/%s HEAD" % master_branch
769 print
771 def clone(path, remote_branch):
772 try:
773 bdir = bzrlib.bzrdir.BzrDir.create(path)
774 except bzrlib.errors.AlreadyControlDirError:
775 bdir = bzrlib.bzrdir.BzrDir.open(path)
776 repo = bdir.find_repository()
777 repo.fetch(remote_branch.repository)
778 return remote_branch.sprout(bdir, repository=repo)
780 def get_remote_branch(name):
781 global dirname, branches
783 remote_branch = bzrlib.branch.Branch.open(branches[name])
784 if isinstance(remote_branch.user_transport, bzrlib.transport.local.LocalTransport):
785 return remote_branch
787 branch_path = os.path.join(dirname, 'clone', name)
789 try:
790 branch = bzrlib.branch.Branch.open(branch_path)
791 except bzrlib.errors.NotBranchError:
792 # clone
793 branch = clone(branch_path, remote_branch)
794 else:
795 # pull
796 try:
797 branch.pull(remote_branch, overwrite=True)
798 except bzrlib.errors.DivergedBranches:
799 # use remote branch for now
800 return remote_branch
802 return branch
804 def find_branches(repo):
805 transport = repo.bzrdir.root_transport
807 for fn in transport.iter_files_recursive():
808 if not fn.endswith('.bzr/branch-format'):
809 continue
811 name = subdir = fn[:-len('/.bzr/branch-format')]
812 name = name if name != '' else 'master'
813 name = name.replace('/', '+')
815 try:
816 cur = transport.clone(subdir)
817 branch = bzrlib.branch.Branch.open_from_transport(cur)
818 except bzrlib.errors.NotBranchError:
819 continue
820 else:
821 yield name, branch.base
823 def get_repo(url, alias):
824 global dirname, peer, branches
826 normal_url = bzrlib.urlutils.normalize_url(url)
827 origin = bzrlib.bzrdir.BzrDir.open(url)
828 is_local = isinstance(origin.transport, bzrlib.transport.local.LocalTransport)
830 shared_path = os.path.join(gitdir, 'bzr')
831 try:
832 shared_dir = bzrlib.bzrdir.BzrDir.open(shared_path)
833 except bzrlib.errors.NotBranchError:
834 shared_dir = bzrlib.bzrdir.BzrDir.create(shared_path)
835 try:
836 shared_repo = shared_dir.open_repository()
837 except bzrlib.errors.NoRepositoryPresent:
838 shared_repo = shared_dir.create_repository(shared=True)
840 if not is_local:
841 clone_path = os.path.join(dirname, 'clone')
842 if not os.path.exists(clone_path):
843 os.mkdir(clone_path)
844 else:
845 # check and remove old organization
846 try:
847 bdir = bzrlib.bzrdir.BzrDir.open(clone_path)
848 bdir.destroy_repository()
849 except bzrlib.errors.NotBranchError:
850 pass
851 except bzrlib.errors.NoRepositoryPresent:
852 pass
854 wanted = get_config('remote-bzr.branches').rstrip().split(', ')
855 # stupid python
856 wanted = [e for e in wanted if e]
858 if not wanted:
859 try:
860 repo = origin.open_repository()
861 if not repo.user_transport.listable():
862 # this repository is not usable for us
863 raise bzrlib.errors.NoRepositoryPresent(repo.bzrdir)
864 except bzrlib.errors.NoRepositoryPresent:
865 wanted = ['master']
867 if wanted:
868 def list_wanted(url, wanted):
869 for name in wanted:
870 subdir = name if name != 'master' else ''
871 yield name, bzrlib.urlutils.join(url, subdir)
873 branch_list = list_wanted(url, wanted)
874 else:
875 branch_list = find_branches(repo)
877 for name, url in branch_list:
878 if not is_local:
879 peers[name] = url
880 branches[name] = url
882 return origin
884 def fix_path(alias, orig_url):
885 url = urlparse.urlparse(orig_url, 'file')
886 if url.scheme != 'file' or os.path.isabs(url.path):
887 return
888 abs_url = urlparse.urljoin("%s/" % os.getcwd(), orig_url)
889 cmd = ['git', 'config', 'remote.%s.url' % alias, "bzr::%s" % abs_url]
890 subprocess.call(cmd)
892 def main(args):
893 global marks, prefix, gitdir, dirname
894 global tags, filenodes
895 global blob_marks
896 global parsed_refs
897 global files_cache
898 global is_tmp
899 global branches, peers
901 alias = args[1]
902 url = args[2]
904 tags = {}
905 filenodes = {}
906 blob_marks = {}
907 parsed_refs = {}
908 files_cache = {}
909 marks = None
910 branches = {}
911 peers = {}
913 if alias[5:] == url:
914 is_tmp = True
915 alias = hashlib.sha1(alias).hexdigest()
916 else:
917 is_tmp = False
919 prefix = 'refs/bzr/%s' % alias
920 gitdir = os.environ['GIT_DIR']
921 dirname = os.path.join(gitdir, 'bzr', alias)
923 if not is_tmp:
924 fix_path(alias, url)
926 if not os.path.exists(dirname):
927 os.makedirs(dirname)
929 if hasattr(bzrlib.ui.ui_factory, 'be_quiet'):
930 bzrlib.ui.ui_factory.be_quiet(True)
932 repo = get_repo(url, alias)
934 marks_path = os.path.join(dirname, 'marks-int')
935 marks = Marks(marks_path)
937 parser = Parser(repo)
938 for line in parser:
939 if parser.check('capabilities'):
940 do_capabilities(parser)
941 elif parser.check('list'):
942 do_list(parser)
943 elif parser.check('import'):
944 do_import(parser)
945 elif parser.check('export'):
946 do_export(parser)
947 else:
948 die('unhandled command: %s' % line)
949 sys.stdout.flush()
951 def bye():
952 if not marks:
953 return
954 if not is_tmp:
955 marks.store()
956 else:
957 shutil.rmtree(dirname)
959 atexit.register(bye)
960 sys.exit(main(sys.argv))