remote-bzr: fix export of utf-8 authors
[git/mingw.git] / contrib / remote-helpers / git-remote-bzr
blob08b0b61ee38eef5e901bfe7ee2b382cf422c9c32
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 name = name.decode('utf-8')
172 committer = '%s <%s>' % (name, email)
173 tz = int(tz)
174 tz = ((tz / 100) * 3600) + ((tz % 100) * 60)
175 return (committer, int(date), tz)
177 def rev_to_mark(rev):
178 global marks
179 return marks.from_rev(rev)
181 def mark_to_rev(mark):
182 global marks
183 return marks.to_rev(mark)
185 def fixup_user(user):
186 name = mail = None
187 user = user.replace('"', '')
188 m = AUTHOR_RE.match(user)
189 if m:
190 name = m.group(1)
191 mail = m.group(2).strip()
192 else:
193 m = EMAIL_RE.match(user)
194 if m:
195 name = m.group(1)
196 mail = m.group(2)
197 else:
198 m = NAME_RE.match(user)
199 if m:
200 name = m.group(1).strip()
202 if not name:
203 name = 'unknown'
204 if not mail:
205 mail = 'Unknown'
207 return '%s <%s>' % (name, mail)
209 def get_filechanges(cur, prev):
210 modified = {}
211 removed = {}
213 changes = cur.changes_from(prev)
215 def u(s):
216 return s.encode('utf-8')
218 for path, fid, kind in changes.added:
219 modified[u(path)] = fid
220 for path, fid, kind in changes.removed:
221 removed[u(path)] = None
222 for path, fid, kind, mod, _ in changes.modified:
223 modified[u(path)] = fid
224 for oldpath, newpath, fid, kind, mod, _ in changes.renamed:
225 removed[u(oldpath)] = None
226 if kind == 'directory':
227 lst = cur.list_files(from_dir=newpath, recursive=True)
228 for path, file_class, kind, fid, entry in lst:
229 if kind != 'directory':
230 modified[u(newpath + '/' + path)] = fid
231 else:
232 modified[u(newpath)] = fid
234 return modified, removed
236 def export_files(tree, files):
237 global marks, filenodes
239 final = []
240 for path, fid in files.iteritems():
241 kind = tree.kind(fid)
243 h = tree.get_file_sha1(fid)
245 if kind == 'symlink':
246 d = tree.get_symlink_target(fid)
247 mode = '120000'
248 elif kind == 'file':
250 if tree.is_executable(fid):
251 mode = '100755'
252 else:
253 mode = '100644'
255 # is the blob already exported?
256 if h in filenodes:
257 mark = filenodes[h]
258 final.append((mode, mark, path))
259 continue
261 d = tree.get_file_text(fid)
262 elif kind == 'directory':
263 continue
264 else:
265 die("Unhandled kind '%s' for path '%s'" % (kind, path))
267 mark = marks.next_mark()
268 filenodes[h] = mark
270 print "blob"
271 print "mark :%u" % mark
272 print "data %d" % len(d)
273 print d
275 final.append((mode, mark, path))
277 return final
279 def export_branch(repo, name):
280 global prefix
282 ref = '%s/heads/%s' % (prefix, name)
283 tip = marks.get_tip(name)
285 branch = get_remote_branch(name)
286 repo = branch.repository
288 branch.lock_read()
289 revs = branch.iter_merge_sorted_revisions(None, tip, 'exclude', 'forward')
290 try:
291 tip_revno = branch.revision_id_to_revno(tip)
292 last_revno, _ = branch.last_revision_info()
293 total = last_revno - tip_revno
294 except bzrlib.errors.NoSuchRevision:
295 tip_revno = 0
296 total = 0
298 for revid, _, seq, _ in revs:
300 if marks.is_marked(revid):
301 continue
303 rev = repo.get_revision(revid)
304 revno = seq[0]
306 parents = rev.parent_ids
307 time = rev.timestamp
308 tz = rev.timezone
309 committer = rev.committer.encode('utf-8')
310 committer = "%s %u %s" % (fixup_user(committer), time, gittz(tz))
311 authors = rev.get_apparent_authors()
312 if authors:
313 author = authors[0].encode('utf-8')
314 author = "%s %u %s" % (fixup_user(author), time, gittz(tz))
315 else:
316 author = committer
317 msg = rev.message.encode('utf-8')
319 msg += '\n'
321 if len(parents) == 0:
322 parent = bzrlib.revision.NULL_REVISION
323 else:
324 parent = parents[0]
326 cur_tree = repo.revision_tree(revid)
327 prev = repo.revision_tree(parent)
328 modified, removed = get_filechanges(cur_tree, prev)
330 modified_final = export_files(cur_tree, modified)
332 if len(parents) == 0:
333 print 'reset %s' % ref
335 print "commit %s" % ref
336 print "mark :%d" % (marks.get_mark(revid))
337 print "author %s" % (author)
338 print "committer %s" % (committer)
339 print "data %d" % (len(msg))
340 print msg
342 for i, p in enumerate(parents):
343 try:
344 m = rev_to_mark(p)
345 except KeyError:
346 # ghost?
347 continue
348 if i == 0:
349 print "from :%s" % m
350 else:
351 print "merge :%s" % m
353 for f in removed:
354 print "D %s" % (f,)
355 for f in modified_final:
356 print "M %s :%u %s" % f
357 print
359 if len(seq) > 1:
360 # let's skip branch revisions from the progress report
361 continue
363 progress = (revno - tip_revno)
364 if (progress % 100 == 0):
365 if total:
366 print "progress revision %d '%s' (%d/%d)" % (revno, name, progress, total)
367 else:
368 print "progress revision %d '%s' (%d)" % (revno, name, progress)
370 branch.unlock()
372 revid = branch.last_revision()
374 # make sure the ref is updated
375 print "reset %s" % ref
376 print "from :%u" % rev_to_mark(revid)
377 print
379 marks.set_tip(name, revid)
381 def export_tag(repo, name):
382 global tags, prefix
384 ref = '%s/tags/%s' % (prefix, name)
385 print "reset %s" % ref
386 print "from :%u" % rev_to_mark(tags[name])
387 print
389 def do_import(parser):
390 global dirname
392 repo = parser.repo
393 path = os.path.join(dirname, 'marks-git')
395 print "feature done"
396 if os.path.exists(path):
397 print "feature import-marks=%s" % path
398 print "feature export-marks=%s" % path
399 print "feature force"
400 sys.stdout.flush()
402 while parser.check('import'):
403 ref = parser[1]
404 if ref.startswith('refs/heads/'):
405 name = ref[len('refs/heads/'):]
406 export_branch(repo, name)
407 if ref.startswith('refs/tags/'):
408 name = ref[len('refs/tags/'):]
409 export_tag(repo, name)
410 parser.next()
412 print 'done'
414 sys.stdout.flush()
416 def parse_blob(parser):
417 global blob_marks
419 parser.next()
420 mark = parser.get_mark()
421 parser.next()
422 data = parser.get_data()
423 blob_marks[mark] = data
424 parser.next()
426 class CustomTree():
428 def __init__(self, branch, revid, parents, files):
429 global files_cache
431 self.updates = {}
432 self.branch = branch
434 def copy_tree(revid):
435 files = files_cache[revid] = {}
436 branch.lock_read()
437 tree = branch.repository.revision_tree(revid)
438 try:
439 for path, entry in tree.iter_entries_by_dir():
440 files[path] = [entry.file_id, None]
441 finally:
442 branch.unlock()
443 return files
445 if len(parents) == 0:
446 self.base_id = bzrlib.revision.NULL_REVISION
447 self.base_files = {}
448 else:
449 self.base_id = parents[0]
450 self.base_files = files_cache.get(self.base_id, None)
451 if not self.base_files:
452 self.base_files = copy_tree(self.base_id)
454 self.files = files_cache[revid] = self.base_files.copy()
455 self.rev_files = {}
457 for path, data in self.files.iteritems():
458 fid, mark = data
459 self.rev_files[fid] = [path, mark]
461 for path, f in files.iteritems():
462 fid, mark = self.files.get(path, [None, None])
463 if not fid:
464 fid = bzrlib.generate_ids.gen_file_id(path)
465 f['path'] = path
466 self.rev_files[fid] = [path, mark]
467 self.updates[fid] = f
469 def last_revision(self):
470 return self.base_id
472 def iter_changes(self):
473 changes = []
475 def get_parent(dirname, basename):
476 parent_fid, mark = self.base_files.get(dirname, [None, None])
477 if parent_fid:
478 return parent_fid
479 parent_fid, mark = self.files.get(dirname, [None, None])
480 if parent_fid:
481 return parent_fid
482 if basename == '':
483 return None
484 fid = bzrlib.generate_ids.gen_file_id(path)
485 add_entry(fid, dirname, 'directory')
486 return fid
488 def add_entry(fid, path, kind, mode = None):
489 dirname, basename = os.path.split(path)
490 parent_fid = get_parent(dirname, basename)
492 executable = False
493 if mode == '100755':
494 executable = True
495 elif mode == '120000':
496 kind = 'symlink'
498 change = (fid,
499 (None, path),
500 True,
501 (False, True),
502 (None, parent_fid),
503 (None, basename),
504 (None, kind),
505 (None, executable))
506 self.files[path] = [change[0], None]
507 changes.append(change)
509 def update_entry(fid, path, kind, mode = None):
510 dirname, basename = os.path.split(path)
511 parent_fid = get_parent(dirname, basename)
513 executable = False
514 if mode == '100755':
515 executable = True
516 elif mode == '120000':
517 kind = 'symlink'
519 change = (fid,
520 (path, path),
521 True,
522 (True, True),
523 (None, parent_fid),
524 (None, basename),
525 (None, kind),
526 (None, executable))
527 self.files[path] = [change[0], None]
528 changes.append(change)
530 def remove_entry(fid, path, kind):
531 dirname, basename = os.path.split(path)
532 parent_fid = get_parent(dirname, basename)
533 change = (fid,
534 (path, None),
535 True,
536 (True, False),
537 (parent_fid, None),
538 (None, None),
539 (None, None),
540 (None, None))
541 del self.files[path]
542 changes.append(change)
544 for fid, f in self.updates.iteritems():
545 path = f['path']
547 if 'deleted' in f:
548 remove_entry(fid, path, 'file')
549 continue
551 if path in self.base_files:
552 update_entry(fid, path, 'file', f['mode'])
553 else:
554 add_entry(fid, path, 'file', f['mode'])
556 self.files[path][1] = f['mark']
557 self.rev_files[fid][1] = f['mark']
559 return changes
561 def get_content(self, file_id):
562 path, mark = self.rev_files[file_id]
563 if mark:
564 return blob_marks[mark]
566 # last resort
567 tree = self.branch.repository.revision_tree(self.base_id)
568 return tree.get_file_text(file_id)
570 def get_file_with_stat(self, file_id, path=None):
571 content = self.get_content(file_id)
572 return (StringIO.StringIO(content), None)
574 def get_symlink_target(self, file_id):
575 return self.get_content(file_id)
577 def id2path(self, file_id):
578 path, mark = self.rev_files[file_id]
579 return path
581 def c_style_unescape(string):
582 if string[0] == string[-1] == '"':
583 return string.decode('string-escape')[1:-1]
584 return string
586 def parse_commit(parser):
587 global marks, blob_marks, parsed_refs
588 global mode
590 parents = []
592 ref = parser[1]
593 parser.next()
595 if ref.startswith('refs/heads/'):
596 name = ref[len('refs/heads/'):]
597 branch = get_remote_branch(name)
598 else:
599 die('unknown ref')
601 commit_mark = parser.get_mark()
602 parser.next()
603 author = parser.get_author()
604 parser.next()
605 committer = parser.get_author()
606 parser.next()
607 data = parser.get_data()
608 parser.next()
609 if parser.check('from'):
610 parents.append(parser.get_mark())
611 parser.next()
612 while parser.check('merge'):
613 parents.append(parser.get_mark())
614 parser.next()
616 # fast-export adds an extra newline
617 if data[-1] == '\n':
618 data = data[:-1]
620 files = {}
622 for line in parser:
623 if parser.check('M'):
624 t, m, mark_ref, path = line.split(' ', 3)
625 mark = int(mark_ref[1:])
626 f = { 'mode' : m, 'mark' : mark }
627 elif parser.check('D'):
628 t, path = line.split(' ', 1)
629 f = { 'deleted' : True }
630 else:
631 die('Unknown file command: %s' % line)
632 path = c_style_unescape(path).decode('utf-8')
633 files[path] = f
635 committer, date, tz = committer
636 parents = [mark_to_rev(p) for p in parents]
637 revid = bzrlib.generate_ids.gen_revision_id(committer, date)
638 props = {}
639 props['branch-nick'] = branch.nick
641 mtree = CustomTree(branch, revid, parents, files)
642 changes = mtree.iter_changes()
644 branch.lock_write()
645 try:
646 builder = branch.get_commit_builder(parents, None, date, tz, committer, props, revid)
647 try:
648 list(builder.record_iter_changes(mtree, mtree.last_revision(), changes))
649 builder.finish_inventory()
650 builder.commit(data.decode('utf-8', 'replace'))
651 except Exception, e:
652 builder.abort()
653 raise
654 finally:
655 branch.unlock()
657 parsed_refs[ref] = revid
658 marks.new_mark(revid, commit_mark)
660 def parse_reset(parser):
661 global parsed_refs
663 ref = parser[1]
664 parser.next()
666 # ugh
667 if parser.check('commit'):
668 parse_commit(parser)
669 return
670 if not parser.check('from'):
671 return
672 from_mark = parser.get_mark()
673 parser.next()
675 parsed_refs[ref] = mark_to_rev(from_mark)
677 def do_export(parser):
678 global parsed_refs, dirname
680 parser.next()
682 for line in parser.each_block('done'):
683 if parser.check('blob'):
684 parse_blob(parser)
685 elif parser.check('commit'):
686 parse_commit(parser)
687 elif parser.check('reset'):
688 parse_reset(parser)
689 elif parser.check('tag'):
690 pass
691 elif parser.check('feature'):
692 pass
693 else:
694 die('unhandled export command: %s' % line)
696 for ref, revid in parsed_refs.iteritems():
697 if ref.startswith('refs/heads/'):
698 name = ref[len('refs/heads/'):]
699 branch = get_remote_branch(name)
700 branch.generate_revision_history(revid, marks.get_tip(name))
702 if name in peers:
703 peer = bzrlib.branch.Branch.open(peers[name])
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 try:
774 bdir = bzrlib.bzrdir.BzrDir.create(path)
775 except bzrlib.errors.AlreadyControlDirError:
776 bdir = bzrlib.bzrdir.BzrDir.open(path)
777 repo = bdir.find_repository()
778 repo.fetch(remote_branch.repository)
779 return remote_branch.sprout(bdir, repository=repo)
781 def get_remote_branch(name):
782 global dirname, branches
784 remote_branch = bzrlib.branch.Branch.open(branches[name])
785 if isinstance(remote_branch.user_transport, bzrlib.transport.local.LocalTransport):
786 return remote_branch
788 branch_path = os.path.join(dirname, 'clone', name)
790 try:
791 branch = bzrlib.branch.Branch.open(branch_path)
792 except bzrlib.errors.NotBranchError:
793 # clone
794 branch = clone(branch_path, remote_branch)
795 else:
796 # pull
797 try:
798 branch.pull(remote_branch, overwrite=True)
799 except bzrlib.errors.DivergedBranches:
800 # use remote branch for now
801 return remote_branch
803 return branch
805 def find_branches(repo):
806 transport = repo.bzrdir.root_transport
808 for fn in transport.iter_files_recursive():
809 if not fn.endswith('.bzr/branch-format'):
810 continue
812 name = subdir = fn[:-len('/.bzr/branch-format')]
813 name = name if name != '' else 'master'
814 name = name.replace('/', '+')
816 try:
817 cur = transport.clone(subdir)
818 branch = bzrlib.branch.Branch.open_from_transport(cur)
819 except bzrlib.errors.NotBranchError:
820 continue
821 else:
822 yield name, branch.base
824 def get_repo(url, alias):
825 global dirname, peer, branches
827 normal_url = bzrlib.urlutils.normalize_url(url)
828 origin = bzrlib.bzrdir.BzrDir.open(url)
829 is_local = isinstance(origin.transport, bzrlib.transport.local.LocalTransport)
831 shared_path = os.path.join(gitdir, 'bzr')
832 try:
833 shared_dir = bzrlib.bzrdir.BzrDir.open(shared_path)
834 except bzrlib.errors.NotBranchError:
835 shared_dir = bzrlib.bzrdir.BzrDir.create(shared_path)
836 try:
837 shared_repo = shared_dir.open_repository()
838 except bzrlib.errors.NoRepositoryPresent:
839 shared_repo = shared_dir.create_repository(shared=True)
841 if not is_local:
842 clone_path = os.path.join(dirname, 'clone')
843 if not os.path.exists(clone_path):
844 os.mkdir(clone_path)
845 else:
846 # check and remove old organization
847 try:
848 bdir = bzrlib.bzrdir.BzrDir.open(clone_path)
849 bdir.destroy_repository()
850 except bzrlib.errors.NotBranchError:
851 pass
852 except bzrlib.errors.NoRepositoryPresent:
853 pass
855 wanted = get_config('remote-bzr.branches').rstrip().split(', ')
856 # stupid python
857 wanted = [e for e in wanted if e]
859 if not wanted:
860 try:
861 repo = origin.open_repository()
862 if not repo.user_transport.listable():
863 # this repository is not usable for us
864 raise bzrlib.errors.NoRepositoryPresent(repo.bzrdir)
865 except bzrlib.errors.NoRepositoryPresent:
866 wanted = ['master']
868 if wanted:
869 def list_wanted(url, wanted):
870 for name in wanted:
871 subdir = name if name != 'master' else ''
872 yield name, bzrlib.urlutils.join(url, subdir)
874 branch_list = list_wanted(url, wanted)
875 else:
876 branch_list = find_branches(repo)
878 for name, url in branch_list:
879 if not is_local:
880 peers[name] = url
881 branches[name] = url
883 return origin
885 def fix_path(alias, orig_url):
886 url = urlparse.urlparse(orig_url, 'file')
887 if url.scheme != 'file' or os.path.isabs(url.path):
888 return
889 abs_url = urlparse.urljoin("%s/" % os.getcwd(), orig_url)
890 cmd = ['git', 'config', 'remote.%s.url' % alias, "bzr::%s" % abs_url]
891 subprocess.call(cmd)
893 def main(args):
894 global marks, prefix, gitdir, dirname
895 global tags, filenodes
896 global blob_marks
897 global parsed_refs
898 global files_cache
899 global is_tmp
900 global branches, peers
902 alias = args[1]
903 url = args[2]
905 tags = {}
906 filenodes = {}
907 blob_marks = {}
908 parsed_refs = {}
909 files_cache = {}
910 marks = None
911 branches = {}
912 peers = {}
914 if alias[5:] == url:
915 is_tmp = True
916 alias = hashlib.sha1(alias).hexdigest()
917 else:
918 is_tmp = False
920 prefix = 'refs/bzr/%s' % alias
921 gitdir = os.environ['GIT_DIR']
922 dirname = os.path.join(gitdir, 'bzr', alias)
924 if not is_tmp:
925 fix_path(alias, url)
927 if not os.path.exists(dirname):
928 os.makedirs(dirname)
930 if hasattr(bzrlib.ui.ui_factory, 'be_quiet'):
931 bzrlib.ui.ui_factory.be_quiet(True)
933 repo = get_repo(url, alias)
935 marks_path = os.path.join(dirname, 'marks-int')
936 marks = Marks(marks_path)
938 parser = Parser(repo)
939 for line in parser:
940 if parser.check('capabilities'):
941 do_capabilities(parser)
942 elif parser.check('list'):
943 do_list(parser)
944 elif parser.check('import'):
945 do_import(parser)
946 elif parser.check('export'):
947 do_export(parser)
948 else:
949 die('unhandled command: %s' % line)
950 sys.stdout.flush()
952 def bye():
953 if not marks:
954 return
955 if not is_tmp:
956 marks.store()
957 else:
958 shutil.rmtree(dirname)
960 atexit.register(bye)
961 sys.exit(main(sys.argv))