remote-bzr: fix cloning of non-listable repos
[git/jrn.git] / contrib / remote-helpers / git-remote-bzr
blobad42317ca095c960f5a8d87aee768d9690ef6eaa
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
35 import sys
36 import os
37 import json
38 import re
39 import StringIO
40 import atexit, shutil, hashlib, urlparse, subprocess
42 NAME_RE = re.compile('^([^<>]+)')
43 AUTHOR_RE = re.compile('^([^<>]+?)? ?<([^<>]*)>$')
44 EMAIL_RE = re.compile('^([^<>]+[^ \\\t<>])?\\b(?:[ \\t<>]*?)\\b([^ \\t<>]+@[^ \\t<>]+)')
45 RAW_AUTHOR_RE = re.compile('^(\w+) (.+)? <(.*)> (\d+) ([+-]\d+)')
47 def die(msg, *args):
48 sys.stderr.write('ERROR: %s\n' % (msg % args))
49 sys.exit(1)
51 def warn(msg, *args):
52 sys.stderr.write('WARNING: %s\n' % (msg % args))
54 def gittz(tz):
55 return '%+03d%02d' % (tz / 3600, tz % 3600 / 60)
57 def get_config(config):
58 cmd = ['git', 'config', '--get', config]
59 process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
60 output, _ = process.communicate()
61 return output
63 class Marks:
65 def __init__(self, path):
66 self.path = path
67 self.tips = {}
68 self.marks = {}
69 self.rev_marks = {}
70 self.last_mark = 0
71 self.load()
73 def load(self):
74 if not os.path.exists(self.path):
75 return
77 tmp = json.load(open(self.path))
78 self.tips = tmp['tips']
79 self.marks = tmp['marks']
80 self.last_mark = tmp['last-mark']
82 for rev, mark in self.marks.iteritems():
83 self.rev_marks[mark] = rev
85 def dict(self):
86 return { 'tips': self.tips, 'marks': self.marks, 'last-mark' : self.last_mark }
88 def store(self):
89 json.dump(self.dict(), open(self.path, 'w'))
91 def __str__(self):
92 return str(self.dict())
94 def from_rev(self, rev):
95 return self.marks[rev]
97 def to_rev(self, mark):
98 return str(self.rev_marks[mark])
100 def next_mark(self):
101 self.last_mark += 1
102 return self.last_mark
104 def get_mark(self, rev):
105 self.last_mark += 1
106 self.marks[rev] = self.last_mark
107 return self.last_mark
109 def is_marked(self, rev):
110 return rev in self.marks
112 def new_mark(self, rev, mark):
113 self.marks[rev] = mark
114 self.rev_marks[mark] = rev
115 self.last_mark = mark
117 def get_tip(self, branch):
118 return self.tips.get(branch, None)
120 def set_tip(self, branch, tip):
121 self.tips[branch] = tip
123 class Parser:
125 def __init__(self, repo):
126 self.repo = repo
127 self.line = self.get_line()
129 def get_line(self):
130 return sys.stdin.readline().strip()
132 def __getitem__(self, i):
133 return self.line.split()[i]
135 def check(self, word):
136 return self.line.startswith(word)
138 def each_block(self, separator):
139 while self.line != separator:
140 yield self.line
141 self.line = self.get_line()
143 def __iter__(self):
144 return self.each_block('')
146 def next(self):
147 self.line = self.get_line()
148 if self.line == 'done':
149 self.line = None
151 def get_mark(self):
152 i = self.line.index(':') + 1
153 return int(self.line[i:])
155 def get_data(self):
156 if not self.check('data'):
157 return None
158 i = self.line.index(' ') + 1
159 size = int(self.line[i:])
160 return sys.stdin.read(size)
162 def get_author(self):
163 m = RAW_AUTHOR_RE.match(self.line)
164 if not m:
165 return None
166 _, name, email, date, tz = m.groups()
167 committer = '%s <%s>' % (name, email)
168 tz = int(tz)
169 tz = ((tz / 100) * 3600) + ((tz % 100) * 60)
170 return (committer, int(date), tz)
172 def rev_to_mark(rev):
173 global marks
174 return marks.from_rev(rev)
176 def mark_to_rev(mark):
177 global marks
178 return marks.to_rev(mark)
180 def fixup_user(user):
181 name = mail = None
182 user = user.replace('"', '')
183 m = AUTHOR_RE.match(user)
184 if m:
185 name = m.group(1)
186 mail = m.group(2).strip()
187 else:
188 m = EMAIL_RE.match(user)
189 if m:
190 name = m.group(1)
191 mail = m.group(2)
192 else:
193 m = NAME_RE.match(user)
194 if m:
195 name = m.group(1).strip()
197 if not name:
198 name = 'unknown'
199 if not mail:
200 mail = 'Unknown'
202 return '%s <%s>' % (name, mail)
204 def get_filechanges(cur, prev):
205 modified = {}
206 removed = {}
208 changes = cur.changes_from(prev)
210 def u(s):
211 return s.encode('utf-8')
213 for path, fid, kind in changes.added:
214 modified[u(path)] = fid
215 for path, fid, kind in changes.removed:
216 removed[u(path)] = None
217 for path, fid, kind, mod, _ in changes.modified:
218 modified[u(path)] = fid
219 for oldpath, newpath, fid, kind, mod, _ in changes.renamed:
220 removed[u(oldpath)] = None
221 if kind == 'directory':
222 lst = cur.list_files(from_dir=newpath, recursive=True)
223 for path, file_class, kind, fid, entry in lst:
224 if kind != 'directory':
225 modified[u(newpath + '/' + path)] = fid
226 else:
227 modified[u(newpath)] = fid
229 return modified, removed
231 def export_files(tree, files):
232 global marks, filenodes
234 final = []
235 for path, fid in files.iteritems():
236 kind = tree.kind(fid)
238 h = tree.get_file_sha1(fid)
240 if kind == 'symlink':
241 d = tree.get_symlink_target(fid)
242 mode = '120000'
243 elif kind == 'file':
245 if tree.is_executable(fid):
246 mode = '100755'
247 else:
248 mode = '100644'
250 # is the blob already exported?
251 if h in filenodes:
252 mark = filenodes[h]
253 final.append((mode, mark, path))
254 continue
256 d = tree.get_file_text(fid)
257 elif kind == 'directory':
258 continue
259 else:
260 die("Unhandled kind '%s' for path '%s'" % (kind, path))
262 mark = marks.next_mark()
263 filenodes[h] = mark
265 print "blob"
266 print "mark :%u" % mark
267 print "data %d" % len(d)
268 print d
270 final.append((mode, mark, path))
272 return final
274 def export_branch(repo, name):
275 global prefix
277 ref = '%s/heads/%s' % (prefix, name)
278 tip = marks.get_tip(name)
280 branch = bzrlib.branch.Branch.open(branches[name])
281 repo = branch.repository
283 branch.lock_read()
284 revs = branch.iter_merge_sorted_revisions(None, tip, 'exclude', 'forward')
285 try:
286 tip_revno = branch.revision_id_to_revno(tip)
287 last_revno, _ = branch.last_revision_info()
288 total = last_revno - tip_revno
289 except bzrlib.errors.NoSuchRevision:
290 tip_revno = 0
291 total = 0
293 for revid, _, seq, _ in revs:
295 if marks.is_marked(revid):
296 continue
298 rev = repo.get_revision(revid)
299 revno = seq[0]
301 parents = rev.parent_ids
302 time = rev.timestamp
303 tz = rev.timezone
304 committer = rev.committer.encode('utf-8')
305 committer = "%s %u %s" % (fixup_user(committer), time, gittz(tz))
306 authors = rev.get_apparent_authors()
307 if authors:
308 author = authors[0].encode('utf-8')
309 author = "%s %u %s" % (fixup_user(author), time, gittz(tz))
310 else:
311 author = committer
312 msg = rev.message.encode('utf-8')
314 msg += '\n'
316 if len(parents) == 0:
317 parent = bzrlib.revision.NULL_REVISION
318 else:
319 parent = parents[0]
321 cur_tree = repo.revision_tree(revid)
322 prev = repo.revision_tree(parent)
323 modified, removed = get_filechanges(cur_tree, prev)
325 modified_final = export_files(cur_tree, modified)
327 if len(parents) == 0:
328 print 'reset %s' % ref
330 print "commit %s" % ref
331 print "mark :%d" % (marks.get_mark(revid))
332 print "author %s" % (author)
333 print "committer %s" % (committer)
334 print "data %d" % (len(msg))
335 print msg
337 for i, p in enumerate(parents):
338 try:
339 m = rev_to_mark(p)
340 except KeyError:
341 # ghost?
342 continue
343 if i == 0:
344 print "from :%s" % m
345 else:
346 print "merge :%s" % m
348 for f in removed:
349 print "D %s" % (f,)
350 for f in modified_final:
351 print "M %s :%u %s" % f
352 print
354 if len(seq) > 1:
355 # let's skip branch revisions from the progress report
356 continue
358 progress = (revno - tip_revno)
359 if (progress % 100 == 0):
360 if total:
361 print "progress revision %d '%s' (%d/%d)" % (revno, name, progress, total)
362 else:
363 print "progress revision %d '%s' (%d)" % (revno, name, progress)
365 branch.unlock()
367 revid = branch.last_revision()
369 # make sure the ref is updated
370 print "reset %s" % ref
371 print "from :%u" % rev_to_mark(revid)
372 print
374 marks.set_tip(name, revid)
376 def export_tag(repo, name):
377 global tags, prefix
379 ref = '%s/tags/%s' % (prefix, name)
380 print "reset %s" % ref
381 print "from :%u" % rev_to_mark(tags[name])
382 print
384 def do_import(parser):
385 global dirname
387 repo = parser.repo
388 path = os.path.join(dirname, 'marks-git')
390 print "feature done"
391 if os.path.exists(path):
392 print "feature import-marks=%s" % path
393 print "feature export-marks=%s" % path
394 print "feature force"
395 sys.stdout.flush()
397 while parser.check('import'):
398 ref = parser[1]
399 if ref.startswith('refs/heads/'):
400 name = ref[len('refs/heads/'):]
401 export_branch(repo, name)
402 if ref.startswith('refs/tags/'):
403 name = ref[len('refs/tags/'):]
404 export_tag(repo, name)
405 parser.next()
407 print 'done'
409 sys.stdout.flush()
411 def parse_blob(parser):
412 global blob_marks
414 parser.next()
415 mark = parser.get_mark()
416 parser.next()
417 data = parser.get_data()
418 blob_marks[mark] = data
419 parser.next()
421 class CustomTree():
423 def __init__(self, branch, revid, parents, files):
424 global files_cache
426 self.updates = {}
427 self.branch = branch
429 def copy_tree(revid):
430 files = files_cache[revid] = {}
431 branch.lock_read()
432 tree = branch.repository.revision_tree(revid)
433 try:
434 for path, entry in tree.iter_entries_by_dir():
435 files[path] = [entry.file_id, None]
436 finally:
437 branch.unlock()
438 return files
440 if len(parents) == 0:
441 self.base_id = bzrlib.revision.NULL_REVISION
442 self.base_files = {}
443 else:
444 self.base_id = parents[0]
445 self.base_files = files_cache.get(self.base_id, None)
446 if not self.base_files:
447 self.base_files = copy_tree(self.base_id)
449 self.files = files_cache[revid] = self.base_files.copy()
450 self.rev_files = {}
452 for path, data in self.files.iteritems():
453 fid, mark = data
454 self.rev_files[fid] = [path, mark]
456 for path, f in files.iteritems():
457 fid, mark = self.files.get(path, [None, None])
458 if not fid:
459 fid = bzrlib.generate_ids.gen_file_id(path)
460 f['path'] = path
461 self.rev_files[fid] = [path, mark]
462 self.updates[fid] = f
464 def last_revision(self):
465 return self.base_id
467 def iter_changes(self):
468 changes = []
470 def get_parent(dirname, basename):
471 parent_fid, mark = self.base_files.get(dirname, [None, None])
472 if parent_fid:
473 return parent_fid
474 parent_fid, mark = self.files.get(dirname, [None, None])
475 if parent_fid:
476 return parent_fid
477 if basename == '':
478 return None
479 fid = bzrlib.generate_ids.gen_file_id(path)
480 add_entry(fid, dirname, 'directory')
481 return fid
483 def add_entry(fid, path, kind, mode = None):
484 dirname, basename = os.path.split(path)
485 parent_fid = get_parent(dirname, basename)
487 executable = False
488 if mode == '100755':
489 executable = True
490 elif mode == '120000':
491 kind = 'symlink'
493 change = (fid,
494 (None, path),
495 True,
496 (False, True),
497 (None, parent_fid),
498 (None, basename),
499 (None, kind),
500 (None, executable))
501 self.files[path] = [change[0], None]
502 changes.append(change)
504 def update_entry(fid, path, kind, mode = None):
505 dirname, basename = os.path.split(path)
506 parent_fid = get_parent(dirname, basename)
508 executable = False
509 if mode == '100755':
510 executable = True
511 elif mode == '120000':
512 kind = 'symlink'
514 change = (fid,
515 (path, path),
516 True,
517 (True, True),
518 (None, parent_fid),
519 (None, basename),
520 (None, kind),
521 (None, executable))
522 self.files[path] = [change[0], None]
523 changes.append(change)
525 def remove_entry(fid, path, kind):
526 dirname, basename = os.path.split(path)
527 parent_fid = get_parent(dirname, basename)
528 change = (fid,
529 (path, None),
530 True,
531 (True, False),
532 (parent_fid, None),
533 (None, None),
534 (None, None),
535 (None, None))
536 del self.files[path]
537 changes.append(change)
539 for fid, f in self.updates.iteritems():
540 path = f['path']
542 if 'deleted' in f:
543 remove_entry(fid, path, 'file')
544 continue
546 if path in self.base_files:
547 update_entry(fid, path, 'file', f['mode'])
548 else:
549 add_entry(fid, path, 'file', f['mode'])
551 self.files[path][1] = f['mark']
552 self.rev_files[fid][1] = f['mark']
554 return changes
556 def get_content(self, file_id):
557 path, mark = self.rev_files[file_id]
558 if mark:
559 return blob_marks[mark]
561 # last resort
562 tree = self.branch.repository.revision_tree(self.base_id)
563 return tree.get_file_text(file_id)
565 def get_file_with_stat(self, file_id, path=None):
566 content = self.get_content(file_id)
567 return (StringIO.StringIO(content), None)
569 def get_symlink_target(self, file_id):
570 return self.get_content(file_id)
572 def id2path(self, file_id):
573 path, mark = self.rev_files[file_id]
574 return path
576 def c_style_unescape(string):
577 if string[0] == string[-1] == '"':
578 return string.decode('string-escape')[1:-1]
579 return string
581 def parse_commit(parser):
582 global marks, blob_marks, parsed_refs
583 global mode
585 parents = []
587 ref = parser[1]
588 parser.next()
590 if ref.startswith('refs/heads/'):
591 name = ref[len('refs/heads/'):]
592 branch = bzrlib.branch.Branch.open(branches[name])
593 else:
594 die('unknown ref')
596 commit_mark = parser.get_mark()
597 parser.next()
598 author = parser.get_author()
599 parser.next()
600 committer = parser.get_author()
601 parser.next()
602 data = parser.get_data()
603 parser.next()
604 if parser.check('from'):
605 parents.append(parser.get_mark())
606 parser.next()
607 while parser.check('merge'):
608 parents.append(parser.get_mark())
609 parser.next()
611 # fast-export adds an extra newline
612 if data[-1] == '\n':
613 data = data[:-1]
615 files = {}
617 for line in parser:
618 if parser.check('M'):
619 t, m, mark_ref, path = line.split(' ', 3)
620 mark = int(mark_ref[1:])
621 f = { 'mode' : m, 'mark' : mark }
622 elif parser.check('D'):
623 t, path = line.split(' ')
624 f = { 'deleted' : True }
625 else:
626 die('Unknown file command: %s' % line)
627 path = c_style_unescape(path).decode('utf-8')
628 files[path] = f
630 committer, date, tz = committer
631 parents = [mark_to_rev(p) for p in parents]
632 revid = bzrlib.generate_ids.gen_revision_id(committer, date)
633 props = {}
634 props['branch-nick'] = branch.nick
636 mtree = CustomTree(branch, revid, parents, files)
637 changes = mtree.iter_changes()
639 branch.lock_write()
640 try:
641 builder = branch.get_commit_builder(parents, None, date, tz, committer, props, revid)
642 try:
643 list(builder.record_iter_changes(mtree, mtree.last_revision(), changes))
644 builder.finish_inventory()
645 builder.commit(data.decode('utf-8', 'replace'))
646 except Exception, e:
647 builder.abort()
648 raise
649 finally:
650 branch.unlock()
652 parsed_refs[ref] = revid
653 marks.new_mark(revid, commit_mark)
655 def parse_reset(parser):
656 global parsed_refs
658 ref = parser[1]
659 parser.next()
661 # ugh
662 if parser.check('commit'):
663 parse_commit(parser)
664 return
665 if not parser.check('from'):
666 return
667 from_mark = parser.get_mark()
668 parser.next()
670 parsed_refs[ref] = mark_to_rev(from_mark)
672 def do_export(parser):
673 global parsed_refs, dirname
675 parser.next()
677 for line in parser.each_block('done'):
678 if parser.check('blob'):
679 parse_blob(parser)
680 elif parser.check('commit'):
681 parse_commit(parser)
682 elif parser.check('reset'):
683 parse_reset(parser)
684 elif parser.check('tag'):
685 pass
686 elif parser.check('feature'):
687 pass
688 else:
689 die('unhandled export command: %s' % line)
691 for ref, revid in parsed_refs.iteritems():
692 if ref.startswith('refs/heads/'):
693 name = ref[len('refs/heads/'):]
694 branch = bzrlib.branch.Branch.open(branches[name])
695 branch.generate_revision_history(revid, marks.get_tip(name))
697 if name in peers:
698 peer = bzrlib.branch.Branch.open(peers[name])
699 try:
700 peer.bzrdir.push_branch(branch, revision_id=revid)
701 except bzrlib.errors.DivergedBranches:
702 print "error %s non-fast forward" % ref
703 continue
705 try:
706 wt = branch.bzrdir.open_workingtree()
707 wt.update()
708 except bzrlib.errors.NoWorkingTree:
709 pass
710 elif ref.startswith('refs/tags/'):
711 # TODO: implement tag push
712 print "error %s pushing tags not supported" % ref
713 continue
714 else:
715 # transport-helper/fast-export bugs
716 continue
718 print "ok %s" % ref
720 print
722 def do_capabilities(parser):
723 global dirname
725 print "import"
726 print "export"
727 print "refspec refs/heads/*:%s/heads/*" % prefix
728 print "refspec refs/tags/*:%s/tags/*" % prefix
730 path = os.path.join(dirname, 'marks-git')
732 if os.path.exists(path):
733 print "*import-marks %s" % path
734 print "*export-marks %s" % path
736 print
738 def ref_is_valid(name):
739 return not True in [c in name for c in '~^: \\']
741 def do_list(parser):
742 global tags
744 master_branch = None
746 for name in branches:
747 if not master_branch:
748 master_branch = name
749 print "? refs/heads/%s" % name
751 branch = bzrlib.branch.Branch.open(branches[master_branch])
752 branch.lock_read()
753 for tag, revid in branch.tags.get_tag_dict().items():
754 try:
755 branch.revision_id_to_dotted_revno(revid)
756 except bzrlib.errors.NoSuchRevision:
757 continue
758 if not ref_is_valid(tag):
759 continue
760 print "? refs/tags/%s" % tag
761 tags[tag] = revid
762 branch.unlock()
764 print "@refs/heads/%s HEAD" % master_branch
765 print
767 def get_remote_branch(origin, remote_branch, name):
768 global dirname, peers
770 branch_path = os.path.join(dirname, 'clone', name)
771 if os.path.exists(branch_path):
772 # pull
773 d = bzrlib.bzrdir.BzrDir.open(branch_path)
774 branch = d.open_branch()
775 try:
776 branch.pull(remote_branch, [], None, False)
777 except bzrlib.errors.DivergedBranches:
778 # use remote branch for now
779 return remote_branch
780 else:
781 # clone
782 d = origin.sprout(branch_path, None,
783 hardlink=True, create_tree_if_local=False,
784 force_new_repo=False,
785 source_branch=remote_branch)
786 branch = d.open_branch()
788 return branch
790 def find_branches(repo, wanted):
791 transport = repo.user_transport
793 for fn in transport.iter_files_recursive():
794 if not fn.endswith('.bzr/branch-format'):
795 continue
797 name = subdir = fn[:-len('/.bzr/branch-format')]
798 name = name if name != '' else 'master'
799 name = name.replace('/', '+')
801 if wanted and not name in wanted:
802 continue
804 try:
805 cur = transport.clone(subdir)
806 branch = bzrlib.branch.Branch.open_from_transport(cur)
807 except bzrlib.errors.NotBranchError:
808 continue
809 else:
810 yield name, branch
812 def get_repo(url, alias):
813 global dirname, peer, branches
815 normal_url = bzrlib.urlutils.normalize_url(url)
816 origin = bzrlib.bzrdir.BzrDir.open(url)
817 is_local = isinstance(origin.transport, bzrlib.transport.local.LocalTransport)
819 shared_path = os.path.join(gitdir, 'bzr')
820 try:
821 shared_dir = bzrlib.bzrdir.BzrDir.open(shared_path)
822 except bzrlib.errors.NotBranchError:
823 shared_dir = bzrlib.bzrdir.BzrDir.create(shared_path)
824 try:
825 shared_repo = shared_dir.open_repository()
826 except bzrlib.errors.NoRepositoryPresent:
827 shared_repo = shared_dir.create_repository(shared=True)
829 if not is_local:
830 clone_path = os.path.join(dirname, 'clone')
831 if not os.path.exists(clone_path):
832 os.mkdir(clone_path)
833 else:
834 # check and remove old organization
835 try:
836 bdir = bzrlib.bzrdir.BzrDir.open(clone_path)
837 bdir.destroy_repository()
838 except bzrlib.errors.NotBranchError:
839 pass
841 try:
842 repo = origin.open_repository()
843 if not repo.user_transport.listable():
844 # this repository is not usable for us
845 raise bzrlib.errors.NoRepositoryPresent(repo.bzrdir)
846 except bzrlib.errors.NoRepositoryPresent:
847 # branch
849 name = 'master'
850 remote_branch = origin.open_branch()
852 if not is_local:
853 peers[name] = remote_branch.base
854 branch = get_remote_branch(origin, remote_branch, name)
855 else:
856 branch = remote_branch
858 branches[name] = branch.base
860 return branch.repository
861 else:
862 # repository
864 wanted = get_config('remote-bzr.branches').rstrip().split(', ')
865 # stupid python
866 wanted = [e for e in wanted if e]
868 for name, remote_branch in find_branches(repo, wanted):
870 if not is_local:
871 peers[name] = remote_branch.base
872 branch = get_remote_branch(origin, remote_branch, name)
873 else:
874 branch = remote_branch
876 branches[name] = branch.base
878 return repo
880 def fix_path(alias, orig_url):
881 url = urlparse.urlparse(orig_url, 'file')
882 if url.scheme != 'file' or os.path.isabs(url.path):
883 return
884 abs_url = urlparse.urljoin("%s/" % os.getcwd(), orig_url)
885 cmd = ['git', 'config', 'remote.%s.url' % alias, "bzr::%s" % abs_url]
886 subprocess.call(cmd)
888 def main(args):
889 global marks, prefix, gitdir, dirname
890 global tags, filenodes
891 global blob_marks
892 global parsed_refs
893 global files_cache
894 global is_tmp
895 global branches, peers
897 alias = args[1]
898 url = args[2]
900 tags = {}
901 filenodes = {}
902 blob_marks = {}
903 parsed_refs = {}
904 files_cache = {}
905 marks = None
906 branches = {}
907 peers = {}
909 if alias[5:] == url:
910 is_tmp = True
911 alias = hashlib.sha1(alias).hexdigest()
912 else:
913 is_tmp = False
915 prefix = 'refs/bzr/%s' % alias
916 gitdir = os.environ['GIT_DIR']
917 dirname = os.path.join(gitdir, 'bzr', alias)
919 if not is_tmp:
920 fix_path(alias, url)
922 if not os.path.exists(dirname):
923 os.makedirs(dirname)
925 bzrlib.ui.ui_factory.be_quiet(True)
927 repo = get_repo(url, alias)
929 marks_path = os.path.join(dirname, 'marks-int')
930 marks = Marks(marks_path)
932 parser = Parser(repo)
933 for line in parser:
934 if parser.check('capabilities'):
935 do_capabilities(parser)
936 elif parser.check('list'):
937 do_list(parser)
938 elif parser.check('import'):
939 do_import(parser)
940 elif parser.check('export'):
941 do_export(parser)
942 else:
943 die('unhandled command: %s' % line)
944 sys.stdout.flush()
946 def bye():
947 if not marks:
948 return
949 if not is_tmp:
950 marks.store()
951 else:
952 shutil.rmtree(dirname)
954 atexit.register(bye)
955 sys.exit(main(sys.argv))