remote-bzr: fix for files with spaces
[alt-git.git] / contrib / remote-helpers / git-remote-bzr
blob35664c65236c45e15f525f9c65d9ab9d866e3dd8
1 #!/usr/bin/env python
3 # Copyright (c) 2012 Felipe Contreras
7 # Just copy to your ~/bin, or anywhere in your $PATH.
8 # Then you can clone with:
9 # % git clone bzr::/path/to/bzr/repo/or/url
11 # For example:
12 # % git clone bzr::$HOME/myrepo
13 # or
14 # % git clone bzr::lp:myrepo
16 # If you want to specify which branches you want track (per repo):
17 # git config remote-bzr.branches 'trunk, devel, test'
20 import sys
22 import bzrlib
23 if hasattr(bzrlib, "initialize"):
24 bzrlib.initialize()
26 import bzrlib.plugin
27 bzrlib.plugin.load_plugins()
29 import bzrlib.generate_ids
30 import bzrlib.transport
31 import bzrlib.errors
32 import bzrlib.ui
33 import bzrlib.urlutils
34 import bzrlib.branch
36 import sys
37 import os
38 import json
39 import re
40 import StringIO
41 import atexit, shutil, hashlib, urlparse, subprocess
43 NAME_RE = re.compile('^([^<>]+)')
44 AUTHOR_RE = re.compile('^([^<>]+?)? ?<([^<>]*)>$')
45 EMAIL_RE = re.compile('^([^<>]+[^ \\\t<>])?\\b(?:[ \\t<>]*?)\\b([^ \\t<>]+@[^ \\t<>]+)')
46 RAW_AUTHOR_RE = re.compile('^(\w+) (.+)? <(.*)> (\d+) ([+-]\d+)')
48 def die(msg, *args):
49 sys.stderr.write('ERROR: %s\n' % (msg % args))
50 sys.exit(1)
52 def warn(msg, *args):
53 sys.stderr.write('WARNING: %s\n' % (msg % args))
55 def gittz(tz):
56 return '%+03d%02d' % (tz / 3600, tz % 3600 / 60)
58 def get_config(config):
59 cmd = ['git', 'config', '--get', config]
60 process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
61 output, _ = process.communicate()
62 return output
64 class Marks:
66 def __init__(self, path):
67 self.path = path
68 self.tips = {}
69 self.marks = {}
70 self.rev_marks = {}
71 self.last_mark = 0
72 self.load()
74 def load(self):
75 if not os.path.exists(self.path):
76 return
78 tmp = json.load(open(self.path))
79 self.tips = tmp['tips']
80 self.marks = tmp['marks']
81 self.last_mark = tmp['last-mark']
83 for rev, mark in self.marks.iteritems():
84 self.rev_marks[mark] = rev
86 def dict(self):
87 return { 'tips': self.tips, 'marks': self.marks, 'last-mark' : self.last_mark }
89 def store(self):
90 json.dump(self.dict(), open(self.path, 'w'))
92 def __str__(self):
93 return str(self.dict())
95 def from_rev(self, rev):
96 return self.marks[rev]
98 def to_rev(self, mark):
99 return str(self.rev_marks[mark])
101 def next_mark(self):
102 self.last_mark += 1
103 return self.last_mark
105 def get_mark(self, rev):
106 self.last_mark += 1
107 self.marks[rev] = self.last_mark
108 return self.last_mark
110 def is_marked(self, rev):
111 return rev in self.marks
113 def new_mark(self, rev, mark):
114 self.marks[rev] = mark
115 self.rev_marks[mark] = rev
116 self.last_mark = mark
118 def get_tip(self, branch):
119 return self.tips.get(branch, None)
121 def set_tip(self, branch, tip):
122 self.tips[branch] = tip
124 class Parser:
126 def __init__(self, repo):
127 self.repo = repo
128 self.line = self.get_line()
130 def get_line(self):
131 return sys.stdin.readline().strip()
133 def __getitem__(self, i):
134 return self.line.split()[i]
136 def check(self, word):
137 return self.line.startswith(word)
139 def each_block(self, separator):
140 while self.line != separator:
141 yield self.line
142 self.line = self.get_line()
144 def __iter__(self):
145 return self.each_block('')
147 def next(self):
148 self.line = self.get_line()
149 if self.line == 'done':
150 self.line = None
152 def get_mark(self):
153 i = self.line.index(':') + 1
154 return int(self.line[i:])
156 def get_data(self):
157 if not self.check('data'):
158 return None
159 i = self.line.index(' ') + 1
160 size = int(self.line[i:])
161 return sys.stdin.read(size)
163 def get_author(self):
164 m = RAW_AUTHOR_RE.match(self.line)
165 if not m:
166 return None
167 _, name, email, date, tz = m.groups()
168 committer = '%s <%s>' % (name, email)
169 tz = int(tz)
170 tz = ((tz / 100) * 3600) + ((tz % 100) * 60)
171 return (committer, int(date), tz)
173 def rev_to_mark(rev):
174 global marks
175 return marks.from_rev(rev)
177 def mark_to_rev(mark):
178 global marks
179 return marks.to_rev(mark)
181 def fixup_user(user):
182 name = mail = None
183 user = user.replace('"', '')
184 m = AUTHOR_RE.match(user)
185 if m:
186 name = m.group(1)
187 mail = m.group(2).strip()
188 else:
189 m = EMAIL_RE.match(user)
190 if m:
191 name = m.group(1)
192 mail = m.group(2)
193 else:
194 m = NAME_RE.match(user)
195 if m:
196 name = m.group(1).strip()
198 if not name:
199 name = 'unknown'
200 if not mail:
201 mail = 'Unknown'
203 return '%s <%s>' % (name, mail)
205 def get_filechanges(cur, prev):
206 modified = {}
207 removed = {}
209 changes = cur.changes_from(prev)
211 def u(s):
212 return s.encode('utf-8')
214 for path, fid, kind in changes.added:
215 modified[u(path)] = fid
216 for path, fid, kind in changes.removed:
217 removed[u(path)] = None
218 for path, fid, kind, mod, _ in changes.modified:
219 modified[u(path)] = fid
220 for oldpath, newpath, fid, kind, mod, _ in changes.renamed:
221 removed[u(oldpath)] = None
222 if kind == 'directory':
223 lst = cur.list_files(from_dir=newpath, recursive=True)
224 for path, file_class, kind, fid, entry in lst:
225 if kind != 'directory':
226 modified[u(newpath + '/' + path)] = fid
227 else:
228 modified[u(newpath)] = fid
230 return modified, removed
232 def export_files(tree, files):
233 global marks, filenodes
235 final = []
236 for path, fid in files.iteritems():
237 kind = tree.kind(fid)
239 h = tree.get_file_sha1(fid)
241 if kind == 'symlink':
242 d = tree.get_symlink_target(fid)
243 mode = '120000'
244 elif kind == 'file':
246 if tree.is_executable(fid):
247 mode = '100755'
248 else:
249 mode = '100644'
251 # is the blob already exported?
252 if h in filenodes:
253 mark = filenodes[h]
254 final.append((mode, mark, path))
255 continue
257 d = tree.get_file_text(fid)
258 elif kind == 'directory':
259 continue
260 else:
261 die("Unhandled kind '%s' for path '%s'" % (kind, path))
263 mark = marks.next_mark()
264 filenodes[h] = mark
266 print "blob"
267 print "mark :%u" % mark
268 print "data %d" % len(d)
269 print d
271 final.append((mode, mark, path))
273 return final
275 def export_branch(repo, name):
276 global prefix
278 ref = '%s/heads/%s' % (prefix, name)
279 tip = marks.get_tip(name)
281 branch = bzrlib.branch.Branch.open(branches[name])
282 repo = branch.repository
284 branch.lock_read()
285 revs = branch.iter_merge_sorted_revisions(None, tip, 'exclude', 'forward')
286 try:
287 tip_revno = branch.revision_id_to_revno(tip)
288 last_revno, _ = branch.last_revision_info()
289 total = last_revno - tip_revno
290 except bzrlib.errors.NoSuchRevision:
291 tip_revno = 0
292 total = 0
294 for revid, _, seq, _ in revs:
296 if marks.is_marked(revid):
297 continue
299 rev = repo.get_revision(revid)
300 revno = seq[0]
302 parents = rev.parent_ids
303 time = rev.timestamp
304 tz = rev.timezone
305 committer = rev.committer.encode('utf-8')
306 committer = "%s %u %s" % (fixup_user(committer), time, gittz(tz))
307 authors = rev.get_apparent_authors()
308 if authors:
309 author = authors[0].encode('utf-8')
310 author = "%s %u %s" % (fixup_user(author), time, gittz(tz))
311 else:
312 author = committer
313 msg = rev.message.encode('utf-8')
315 msg += '\n'
317 if len(parents) == 0:
318 parent = bzrlib.revision.NULL_REVISION
319 else:
320 parent = parents[0]
322 cur_tree = repo.revision_tree(revid)
323 prev = repo.revision_tree(parent)
324 modified, removed = get_filechanges(cur_tree, prev)
326 modified_final = export_files(cur_tree, modified)
328 if len(parents) == 0:
329 print 'reset %s' % ref
331 print "commit %s" % ref
332 print "mark :%d" % (marks.get_mark(revid))
333 print "author %s" % (author)
334 print "committer %s" % (committer)
335 print "data %d" % (len(msg))
336 print msg
338 for i, p in enumerate(parents):
339 try:
340 m = rev_to_mark(p)
341 except KeyError:
342 # ghost?
343 continue
344 if i == 0:
345 print "from :%s" % m
346 else:
347 print "merge :%s" % m
349 for f in removed:
350 print "D %s" % (f,)
351 for f in modified_final:
352 print "M %s :%u %s" % f
353 print
355 if len(seq) > 1:
356 # let's skip branch revisions from the progress report
357 continue
359 progress = (revno - tip_revno)
360 if (progress % 100 == 0):
361 if total:
362 print "progress revision %d '%s' (%d/%d)" % (revno, name, progress, total)
363 else:
364 print "progress revision %d '%s' (%d)" % (revno, name, progress)
366 branch.unlock()
368 revid = branch.last_revision()
370 # make sure the ref is updated
371 print "reset %s" % ref
372 print "from :%u" % rev_to_mark(revid)
373 print
375 marks.set_tip(name, revid)
377 def export_tag(repo, name):
378 global tags, prefix
380 ref = '%s/tags/%s' % (prefix, name)
381 print "reset %s" % ref
382 print "from :%u" % rev_to_mark(tags[name])
383 print
385 def do_import(parser):
386 global dirname
388 repo = parser.repo
389 path = os.path.join(dirname, 'marks-git')
391 print "feature done"
392 if os.path.exists(path):
393 print "feature import-marks=%s" % path
394 print "feature export-marks=%s" % path
395 print "feature force"
396 sys.stdout.flush()
398 while parser.check('import'):
399 ref = parser[1]
400 if ref.startswith('refs/heads/'):
401 name = ref[len('refs/heads/'):]
402 export_branch(repo, name)
403 if ref.startswith('refs/tags/'):
404 name = ref[len('refs/tags/'):]
405 export_tag(repo, name)
406 parser.next()
408 print 'done'
410 sys.stdout.flush()
412 def parse_blob(parser):
413 global blob_marks
415 parser.next()
416 mark = parser.get_mark()
417 parser.next()
418 data = parser.get_data()
419 blob_marks[mark] = data
420 parser.next()
422 class CustomTree():
424 def __init__(self, branch, revid, parents, files):
425 global files_cache
427 self.updates = {}
428 self.branch = branch
430 def copy_tree(revid):
431 files = files_cache[revid] = {}
432 branch.lock_read()
433 tree = branch.repository.revision_tree(revid)
434 try:
435 for path, entry in tree.iter_entries_by_dir():
436 files[path] = [entry.file_id, None]
437 finally:
438 branch.unlock()
439 return files
441 if len(parents) == 0:
442 self.base_id = bzrlib.revision.NULL_REVISION
443 self.base_files = {}
444 else:
445 self.base_id = parents[0]
446 self.base_files = files_cache.get(self.base_id, None)
447 if not self.base_files:
448 self.base_files = copy_tree(self.base_id)
450 self.files = files_cache[revid] = self.base_files.copy()
451 self.rev_files = {}
453 for path, data in self.files.iteritems():
454 fid, mark = data
455 self.rev_files[fid] = [path, mark]
457 for path, f in files.iteritems():
458 fid, mark = self.files.get(path, [None, None])
459 if not fid:
460 fid = bzrlib.generate_ids.gen_file_id(path)
461 f['path'] = path
462 self.rev_files[fid] = [path, mark]
463 self.updates[fid] = f
465 def last_revision(self):
466 return self.base_id
468 def iter_changes(self):
469 changes = []
471 def get_parent(dirname, basename):
472 parent_fid, mark = self.base_files.get(dirname, [None, None])
473 if parent_fid:
474 return parent_fid
475 parent_fid, mark = self.files.get(dirname, [None, None])
476 if parent_fid:
477 return parent_fid
478 if basename == '':
479 return None
480 fid = bzrlib.generate_ids.gen_file_id(path)
481 add_entry(fid, dirname, 'directory')
482 return fid
484 def add_entry(fid, path, kind, mode = None):
485 dirname, basename = os.path.split(path)
486 parent_fid = get_parent(dirname, basename)
488 executable = False
489 if mode == '100755':
490 executable = True
491 elif mode == '120000':
492 kind = 'symlink'
494 change = (fid,
495 (None, path),
496 True,
497 (False, True),
498 (None, parent_fid),
499 (None, basename),
500 (None, kind),
501 (None, executable))
502 self.files[path] = [change[0], None]
503 changes.append(change)
505 def update_entry(fid, path, kind, mode = None):
506 dirname, basename = os.path.split(path)
507 parent_fid = get_parent(dirname, basename)
509 executable = False
510 if mode == '100755':
511 executable = True
512 elif mode == '120000':
513 kind = 'symlink'
515 change = (fid,
516 (path, path),
517 True,
518 (True, True),
519 (None, parent_fid),
520 (None, basename),
521 (None, kind),
522 (None, executable))
523 self.files[path] = [change[0], None]
524 changes.append(change)
526 def remove_entry(fid, path, kind):
527 dirname, basename = os.path.split(path)
528 parent_fid = get_parent(dirname, basename)
529 change = (fid,
530 (path, None),
531 True,
532 (True, False),
533 (parent_fid, None),
534 (None, None),
535 (None, None),
536 (None, None))
537 del self.files[path]
538 changes.append(change)
540 for fid, f in self.updates.iteritems():
541 path = f['path']
543 if 'deleted' in f:
544 remove_entry(fid, path, 'file')
545 continue
547 if path in self.base_files:
548 update_entry(fid, path, 'file', f['mode'])
549 else:
550 add_entry(fid, path, 'file', f['mode'])
552 self.files[path][1] = f['mark']
553 self.rev_files[fid][1] = f['mark']
555 return changes
557 def get_content(self, file_id):
558 path, mark = self.rev_files[file_id]
559 if mark:
560 return blob_marks[mark]
562 # last resort
563 tree = self.branch.repository.revision_tree(self.base_id)
564 return tree.get_file_text(file_id)
566 def get_file_with_stat(self, file_id, path=None):
567 content = self.get_content(file_id)
568 return (StringIO.StringIO(content), None)
570 def get_symlink_target(self, file_id):
571 return self.get_content(file_id)
573 def id2path(self, file_id):
574 path, mark = self.rev_files[file_id]
575 return path
577 def c_style_unescape(string):
578 if string[0] == string[-1] == '"':
579 return string.decode('string-escape')[1:-1]
580 return string
582 def parse_commit(parser):
583 global marks, blob_marks, parsed_refs
584 global mode
586 parents = []
588 ref = parser[1]
589 parser.next()
591 if ref.startswith('refs/heads/'):
592 name = ref[len('refs/heads/'):]
593 branch = bzrlib.branch.Branch.open(branches[name])
594 else:
595 die('unknown ref')
597 commit_mark = parser.get_mark()
598 parser.next()
599 author = parser.get_author()
600 parser.next()
601 committer = parser.get_author()
602 parser.next()
603 data = parser.get_data()
604 parser.next()
605 if parser.check('from'):
606 parents.append(parser.get_mark())
607 parser.next()
608 while parser.check('merge'):
609 parents.append(parser.get_mark())
610 parser.next()
612 # fast-export adds an extra newline
613 if data[-1] == '\n':
614 data = data[:-1]
616 files = {}
618 for line in parser:
619 if parser.check('M'):
620 t, m, mark_ref, path = line.split(' ', 3)
621 mark = int(mark_ref[1:])
622 f = { 'mode' : m, 'mark' : mark }
623 elif parser.check('D'):
624 t, path = line.split(' ', 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 = bzrlib.branch.Branch.open(branches[name])
696 branch.generate_revision_history(revid, marks.get_tip(name))
698 if name in peers:
699 peer = bzrlib.branch.Branch.open(peers[name])
700 try:
701 peer.bzrdir.push_branch(branch, revision_id=revid)
702 except bzrlib.errors.DivergedBranches:
703 print "error %s non-fast forward" % ref
704 continue
706 try:
707 wt = branch.bzrdir.open_workingtree()
708 wt.update()
709 except bzrlib.errors.NoWorkingTree:
710 pass
711 elif ref.startswith('refs/tags/'):
712 # TODO: implement tag push
713 print "error %s pushing tags not supported" % ref
714 continue
715 else:
716 # transport-helper/fast-export bugs
717 continue
719 print "ok %s" % ref
721 print
723 def do_capabilities(parser):
724 global dirname
726 print "import"
727 print "export"
728 print "refspec refs/heads/*:%s/heads/*" % prefix
729 print "refspec refs/tags/*:%s/tags/*" % prefix
731 path = os.path.join(dirname, 'marks-git')
733 if os.path.exists(path):
734 print "*import-marks %s" % path
735 print "*export-marks %s" % path
737 print
739 def ref_is_valid(name):
740 return not True in [c in name for c in '~^: \\']
742 def do_list(parser):
743 global tags
745 master_branch = None
747 for name in branches:
748 if not master_branch:
749 master_branch = name
750 print "? refs/heads/%s" % name
752 branch = bzrlib.branch.Branch.open(branches[master_branch])
753 branch.lock_read()
754 for tag, revid in branch.tags.get_tag_dict().items():
755 try:
756 branch.revision_id_to_dotted_revno(revid)
757 except bzrlib.errors.NoSuchRevision:
758 continue
759 if not ref_is_valid(tag):
760 continue
761 print "? refs/tags/%s" % tag
762 tags[tag] = revid
763 branch.unlock()
765 print "@refs/heads/%s HEAD" % master_branch
766 print
768 def get_remote_branch(origin, remote_branch, name):
769 global dirname, peers
771 branch_path = os.path.join(dirname, 'clone', name)
773 try:
774 d = bzrlib.bzrdir.BzrDir.open(branch_path)
775 branch = d.open_branch()
776 except bzrlib.errors.NotBranchError:
777 # clone
778 d = origin.sprout(branch_path, None,
779 hardlink=True, create_tree_if_local=False,
780 force_new_repo=False,
781 source_branch=remote_branch)
782 branch = d.open_branch()
783 else:
784 # pull
785 try:
786 branch.pull(remote_branch, [], None, False)
787 except bzrlib.errors.DivergedBranches:
788 # use remote branch for now
789 return remote_branch
791 return branch
793 def find_branches(repo, wanted):
794 transport = repo.bzrdir.root_transport
796 for fn in transport.iter_files_recursive():
797 if not fn.endswith('.bzr/branch-format'):
798 continue
800 name = subdir = fn[:-len('/.bzr/branch-format')]
801 name = name if name != '' else 'master'
802 name = name.replace('/', '+')
804 if wanted and not name in wanted:
805 continue
807 try:
808 cur = transport.clone(subdir)
809 branch = bzrlib.branch.Branch.open_from_transport(cur)
810 except bzrlib.errors.NotBranchError:
811 continue
812 else:
813 yield name, branch
815 def get_repo(url, alias):
816 global dirname, peer, branches
818 normal_url = bzrlib.urlutils.normalize_url(url)
819 origin = bzrlib.bzrdir.BzrDir.open(url)
820 is_local = isinstance(origin.transport, bzrlib.transport.local.LocalTransport)
822 shared_path = os.path.join(gitdir, 'bzr')
823 try:
824 shared_dir = bzrlib.bzrdir.BzrDir.open(shared_path)
825 except bzrlib.errors.NotBranchError:
826 shared_dir = bzrlib.bzrdir.BzrDir.create(shared_path)
827 try:
828 shared_repo = shared_dir.open_repository()
829 except bzrlib.errors.NoRepositoryPresent:
830 shared_repo = shared_dir.create_repository(shared=True)
832 if not is_local:
833 clone_path = os.path.join(dirname, 'clone')
834 if not os.path.exists(clone_path):
835 os.mkdir(clone_path)
836 else:
837 # check and remove old organization
838 try:
839 bdir = bzrlib.bzrdir.BzrDir.open(clone_path)
840 bdir.destroy_repository()
841 except bzrlib.errors.NotBranchError:
842 pass
843 except bzrlib.errors.NoRepositoryPresent:
844 pass
846 try:
847 repo = origin.open_repository()
848 if not repo.user_transport.listable():
849 # this repository is not usable for us
850 raise bzrlib.errors.NoRepositoryPresent(repo.bzrdir)
851 except bzrlib.errors.NoRepositoryPresent:
852 # branch
854 name = 'master'
855 remote_branch = origin.open_branch()
857 if not is_local:
858 peers[name] = remote_branch.base
859 branch = get_remote_branch(origin, remote_branch, name)
860 else:
861 branch = remote_branch
863 branches[name] = branch.base
865 return branch.repository
866 else:
867 # repository
869 wanted = get_config('remote-bzr.branches').rstrip().split(', ')
870 # stupid python
871 wanted = [e for e in wanted if e]
873 for name, remote_branch in find_branches(repo, wanted):
875 if not is_local:
876 peers[name] = remote_branch.base
877 branch = get_remote_branch(origin, remote_branch, name)
878 else:
879 branch = remote_branch
881 branches[name] = branch.base
883 return repo
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))