git-remote-hg: use internal clone's hgrc
[git] / contrib / remote-helpers / git-remote-bzr
1 #!/usr/bin/env python
2 #
3 # Copyright (c) 2012 Felipe Contreras
4 #
5
6 #
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
10 #
11 # For example:
12 # % git clone bzr::$HOME/myrepo
13 # or
14 # % git clone bzr::lp:myrepo
15 #
16 # If you want to specify which branches you want to track (per repo):
17 # % git config remote.origin.bzr-branches 'trunk, devel, test'
18 #
19 # Where 'origin' is the name of the repository you want to specify the
20 # branches.
21 #
22
23 import sys
24
25 import bzrlib
26 if hasattr(bzrlib, "initialize"):
27     bzrlib.initialize()
28
29 import bzrlib.plugin
30 bzrlib.plugin.load_plugins()
31
32 import bzrlib.generate_ids
33 import bzrlib.transport
34 import bzrlib.errors
35 import bzrlib.ui
36 import bzrlib.urlutils
37 import bzrlib.branch
38
39 import sys
40 import os
41 import json
42 import re
43 import StringIO
44 import atexit, shutil, hashlib, urlparse, subprocess
45
46 NAME_RE = re.compile('^([^<>]+)')
47 AUTHOR_RE = re.compile('^([^<>]+?)? ?[<>]([^<>]*)(?:$|>)')
48 EMAIL_RE = re.compile(r'([^ \t<>]+@[^ \t<>]+)')
49 RAW_AUTHOR_RE = re.compile('^(\w+) (.+)? <(.*)> (\d+) ([+-]\d+)')
50
51 def die(msg, *args):
52     sys.stderr.write('ERROR: %s\n' % (msg % args))
53     sys.exit(1)
54
55 def warn(msg, *args):
56     sys.stderr.write('WARNING: %s\n' % (msg % args))
57
58 def gittz(tz):
59     return '%+03d%02d' % (tz / 3600, tz % 3600 / 60)
60
61 def get_config(config):
62     cmd = ['git', 'config', '--get', config]
63     process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
64     output, _ = process.communicate()
65     return output
66
67 class Marks:
68
69     def __init__(self, path):
70         self.path = path
71         self.tips = {}
72         self.marks = {}
73         self.rev_marks = {}
74         self.last_mark = 0
75         self.load()
76
77     def load(self):
78         if not os.path.exists(self.path):
79             return
80
81         tmp = json.load(open(self.path))
82         self.tips = tmp['tips']
83         self.marks = tmp['marks']
84         self.last_mark = tmp['last-mark']
85
86         for rev, mark in self.marks.iteritems():
87             self.rev_marks[mark] = rev
88
89     def dict(self):
90         return { 'tips': self.tips, 'marks': self.marks, 'last-mark' : self.last_mark }
91
92     def store(self):
93         json.dump(self.dict(), open(self.path, 'w'))
94
95     def __str__(self):
96         return str(self.dict())
97
98     def from_rev(self, rev):
99         return self.marks[rev]
100
101     def to_rev(self, mark):
102         return str(self.rev_marks[mark])
103
104     def next_mark(self):
105         self.last_mark += 1
106         return self.last_mark
107
108     def get_mark(self, rev):
109         self.last_mark += 1
110         self.marks[rev] = self.last_mark
111         return self.last_mark
112
113     def is_marked(self, rev):
114         return rev in self.marks
115
116     def new_mark(self, rev, mark):
117         self.marks[rev] = mark
118         self.rev_marks[mark] = rev
119         self.last_mark = mark
120
121     def get_tip(self, branch):
122         try:
123             return str(self.tips[branch])
124         except KeyError:
125             return None
126
127     def set_tip(self, branch, tip):
128         self.tips[branch] = tip
129
130 class Parser:
131
132     def __init__(self, repo):
133         self.repo = repo
134         self.line = self.get_line()
135
136     def get_line(self):
137         return sys.stdin.readline().strip()
138
139     def __getitem__(self, i):
140         return self.line.split()[i]
141
142     def check(self, word):
143         return self.line.startswith(word)
144
145     def each_block(self, separator):
146         while self.line != separator:
147             yield self.line
148             self.line = self.get_line()
149
150     def __iter__(self):
151         return self.each_block('')
152
153     def next(self):
154         self.line = self.get_line()
155         if self.line == 'done':
156             self.line = None
157
158     def get_mark(self):
159         i = self.line.index(':') + 1
160         return int(self.line[i:])
161
162     def get_data(self):
163         if not self.check('data'):
164             return None
165         i = self.line.index(' ') + 1
166         size = int(self.line[i:])
167         return sys.stdin.read(size)
168
169     def get_author(self):
170         m = RAW_AUTHOR_RE.match(self.line)
171         if not m:
172             return None
173         _, name, email, date, tz = m.groups()
174         name = name.decode('utf-8')
175         committer = '%s <%s>' % (name, email)
176         tz = int(tz)
177         tz = ((tz / 100) * 3600) + ((tz % 100) * 60)
178         return (committer, int(date), tz)
179
180 def rev_to_mark(rev):
181     return marks.from_rev(rev)
182
183 def mark_to_rev(mark):
184     return marks.to_rev(mark)
185
186 def fixup_user(user):
187     name = mail = None
188     user = user.replace('"', '')
189     m = AUTHOR_RE.match(user)
190     if m:
191         name = m.group(1)
192         mail = m.group(2).strip()
193     else:
194         m = EMAIL_RE.match(user)
195         if m:
196             mail = m.group(1)
197         else:
198             m = NAME_RE.match(user)
199             if m:
200                 name = m.group(1).strip()
201
202     if not name:
203         name = 'unknown'
204     if not mail:
205         mail = 'Unknown'
206
207     return '%s <%s>' % (name, mail)
208
209 def get_filechanges(cur, prev):
210     modified = {}
211     removed = {}
212
213     changes = cur.changes_from(prev)
214
215     def u(s):
216         return s.encode('utf-8')
217
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
233
234     return modified, removed
235
236 def export_files(tree, files):
237     final = []
238     for path, fid in files.iteritems():
239         kind = tree.kind(fid)
240
241         h = tree.get_file_sha1(fid)
242
243         if kind == 'symlink':
244             d = tree.get_symlink_target(fid)
245             mode = '120000'
246         elif kind == 'file':
247
248             if tree.is_executable(fid):
249                 mode = '100755'
250             else:
251                 mode = '100644'
252
253             # is the blob already exported?
254             if h in filenodes:
255                 mark = filenodes[h]
256                 final.append((mode, mark, path))
257                 continue
258
259             d = tree.get_file_text(fid)
260         elif kind == 'directory':
261             continue
262         else:
263             die("Unhandled kind '%s' for path '%s'" % (kind, path))
264
265         mark = marks.next_mark()
266         filenodes[h] = mark
267
268         print "blob"
269         print "mark :%u" % mark
270         print "data %d" % len(d)
271         print d
272
273         final.append((mode, mark, path))
274
275     return final
276
277 def export_branch(repo, name):
278     ref = '%s/heads/%s' % (prefix, name)
279     tip = marks.get_tip(name)
280
281     branch = get_remote_branch(name)
282     repo = branch.repository
283
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
293
294     for revid, _, seq, _ in revs:
295
296         if marks.is_marked(revid):
297             continue
298
299         rev = repo.get_revision(revid)
300         revno = seq[0]
301
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')
314
315         msg += '\n'
316
317         if len(parents) == 0:
318             parent = bzrlib.revision.NULL_REVISION
319         else:
320             parent = parents[0]
321
322         cur_tree = repo.revision_tree(revid)
323         prev = repo.revision_tree(parent)
324         modified, removed = get_filechanges(cur_tree, prev)
325
326         modified_final = export_files(cur_tree, modified)
327
328         if len(parents) == 0:
329             print 'reset %s' % ref
330
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
337
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
348
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
354
355         if len(seq) > 1:
356             # let's skip branch revisions from the progress report
357             continue
358
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)
365
366     branch.unlock()
367
368     revid = branch.last_revision()
369
370     # make sure the ref is updated
371     print "reset %s" % ref
372     print "from :%u" % rev_to_mark(revid)
373     print
374
375     marks.set_tip(name, revid)
376
377 def export_tag(repo, name):
378     ref = '%s/tags/%s' % (prefix, name)
379     print "reset %s" % ref
380     print "from :%u" % rev_to_mark(tags[name])
381     print
382
383 def do_import(parser):
384     repo = parser.repo
385     path = os.path.join(dirname, 'marks-git')
386
387     print "feature done"
388     if os.path.exists(path):
389         print "feature import-marks=%s" % path
390     print "feature export-marks=%s" % path
391     print "feature force"
392     sys.stdout.flush()
393
394     while parser.check('import'):
395         ref = parser[1]
396         if ref.startswith('refs/heads/'):
397             name = ref[len('refs/heads/'):]
398             export_branch(repo, name)
399         if ref.startswith('refs/tags/'):
400             name = ref[len('refs/tags/'):]
401             export_tag(repo, name)
402         parser.next()
403
404     print 'done'
405
406     sys.stdout.flush()
407
408 def parse_blob(parser):
409     parser.next()
410     mark = parser.get_mark()
411     parser.next()
412     data = parser.get_data()
413     blob_marks[mark] = data
414     parser.next()
415
416 class CustomTree():
417
418     def __init__(self, branch, revid, parents, files):
419         self.updates = {}
420         self.branch = branch
421
422         def copy_tree(revid):
423             files = files_cache[revid] = {}
424             branch.lock_read()
425             tree = branch.repository.revision_tree(revid)
426             try:
427                 for path, entry in tree.iter_entries_by_dir():
428                     files[path] = [entry.file_id, None]
429             finally:
430                 branch.unlock()
431             return files
432
433         if len(parents) == 0:
434             self.base_id = bzrlib.revision.NULL_REVISION
435             self.base_files = {}
436         else:
437             self.base_id = parents[0]
438             self.base_files = files_cache.get(self.base_id, None)
439             if not self.base_files:
440                 self.base_files = copy_tree(self.base_id)
441
442         self.files = files_cache[revid] = self.base_files.copy()
443         self.rev_files = {}
444
445         for path, data in self.files.iteritems():
446             fid, mark = data
447             self.rev_files[fid] = [path, mark]
448
449         for path, f in files.iteritems():
450             fid, mark = self.files.get(path, [None, None])
451             if not fid:
452                 fid = bzrlib.generate_ids.gen_file_id(path)
453             f['path'] = path
454             self.rev_files[fid] = [path, mark]
455             self.updates[fid] = f
456
457     def last_revision(self):
458         return self.base_id
459
460     def iter_changes(self):
461         changes = []
462
463         def get_parent(dirname, basename):
464             parent_fid, mark = self.base_files.get(dirname, [None, None])
465             if parent_fid:
466                 return parent_fid
467             parent_fid, mark = self.files.get(dirname, [None, None])
468             if parent_fid:
469                 return parent_fid
470             if basename == '':
471                 return None
472             fid = bzrlib.generate_ids.gen_file_id(path)
473             add_entry(fid, dirname, 'directory')
474             return fid
475
476         def add_entry(fid, path, kind, mode=None):
477             dirname, basename = os.path.split(path)
478             parent_fid = get_parent(dirname, basename)
479
480             executable = False
481             if mode == '100755':
482                 executable = True
483             elif mode == '120000':
484                 kind = 'symlink'
485
486             change = (fid,
487                     (None, path),
488                     True,
489                     (False, True),
490                     (None, parent_fid),
491                     (None, basename),
492                     (None, kind),
493                     (None, executable))
494             self.files[path] = [change[0], None]
495             changes.append(change)
496
497         def update_entry(fid, path, kind, mode=None):
498             dirname, basename = os.path.split(path)
499             parent_fid = get_parent(dirname, basename)
500
501             executable = False
502             if mode == '100755':
503                 executable = True
504             elif mode == '120000':
505                 kind = 'symlink'
506
507             change = (fid,
508                     (path, path),
509                     True,
510                     (True, True),
511                     (None, parent_fid),
512                     (None, basename),
513                     (None, kind),
514                     (None, executable))
515             self.files[path] = [change[0], None]
516             changes.append(change)
517
518         def remove_entry(fid, path, kind):
519             dirname, basename = os.path.split(path)
520             parent_fid = get_parent(dirname, basename)
521             change = (fid,
522                     (path, None),
523                     True,
524                     (True, False),
525                     (parent_fid, None),
526                     (None, None),
527                     (None, None),
528                     (None, None))
529             del self.files[path]
530             changes.append(change)
531
532         for fid, f in self.updates.iteritems():
533             path = f['path']
534
535             if 'deleted' in f:
536                 remove_entry(fid, path, 'file')
537                 continue
538
539             if path in self.base_files:
540                 update_entry(fid, path, 'file', f['mode'])
541             else:
542                 add_entry(fid, path, 'file', f['mode'])
543
544             self.files[path][1] = f['mark']
545             self.rev_files[fid][1] = f['mark']
546
547         return changes
548
549     def get_content(self, file_id):
550         path, mark = self.rev_files[file_id]
551         if mark:
552             return blob_marks[mark]
553
554         # last resort
555         tree = self.branch.repository.revision_tree(self.base_id)
556         return tree.get_file_text(file_id)
557
558     def get_file_with_stat(self, file_id, path=None):
559         content = self.get_content(file_id)
560         return (StringIO.StringIO(content), None)
561
562     def get_symlink_target(self, file_id):
563         return self.get_content(file_id)
564
565     def id2path(self, file_id):
566         path, mark = self.rev_files[file_id]
567         return path
568
569 def c_style_unescape(string):
570     if string[0] == string[-1] == '"':
571         return string.decode('string-escape')[1:-1]
572     return string
573
574 def parse_commit(parser):
575     parents = []
576
577     ref = parser[1]
578     parser.next()
579
580     if ref.startswith('refs/heads/'):
581         name = ref[len('refs/heads/'):]
582         branch = get_remote_branch(name)
583     else:
584         die('unknown ref')
585
586     commit_mark = parser.get_mark()
587     parser.next()
588     author = parser.get_author()
589     parser.next()
590     committer = parser.get_author()
591     parser.next()
592     data = parser.get_data()
593     parser.next()
594     if parser.check('from'):
595         parents.append(parser.get_mark())
596         parser.next()
597     while parser.check('merge'):
598         parents.append(parser.get_mark())
599         parser.next()
600
601     # fast-export adds an extra newline
602     if data[-1] == '\n':
603         data = data[:-1]
604
605     files = {}
606
607     for line in parser:
608         if parser.check('M'):
609             t, m, mark_ref, path = line.split(' ', 3)
610             mark = int(mark_ref[1:])
611             f = { 'mode' : m, 'mark' : mark }
612         elif parser.check('D'):
613             t, path = line.split(' ', 1)
614             f = { 'deleted' : True }
615         else:
616             die('Unknown file command: %s' % line)
617         path = c_style_unescape(path).decode('utf-8')
618         files[path] = f
619
620     committer, date, tz = committer
621     author, _, _ = author
622     parents = [mark_to_rev(p) for p in parents]
623     revid = bzrlib.generate_ids.gen_revision_id(committer, date)
624     props = {}
625     props['branch-nick'] = branch.nick
626     props['authors'] = author
627
628     mtree = CustomTree(branch, revid, parents, files)
629     changes = mtree.iter_changes()
630
631     branch.lock_write()
632     try:
633         builder = branch.get_commit_builder(parents, None, date, tz, committer, props, revid)
634         try:
635             list(builder.record_iter_changes(mtree, mtree.last_revision(), changes))
636             builder.finish_inventory()
637             builder.commit(data.decode('utf-8', 'replace'))
638         except Exception, e:
639             builder.abort()
640             raise
641     finally:
642         branch.unlock()
643
644     parsed_refs[ref] = revid
645     marks.new_mark(revid, commit_mark)
646
647 def parse_reset(parser):
648     ref = parser[1]
649     parser.next()
650
651     # ugh
652     if parser.check('commit'):
653         parse_commit(parser)
654         return
655     if not parser.check('from'):
656         return
657     from_mark = parser.get_mark()
658     parser.next()
659
660     parsed_refs[ref] = mark_to_rev(from_mark)
661
662 def do_export(parser):
663     parser.next()
664
665     for line in parser.each_block('done'):
666         if parser.check('blob'):
667             parse_blob(parser)
668         elif parser.check('commit'):
669             parse_commit(parser)
670         elif parser.check('reset'):
671             parse_reset(parser)
672         elif parser.check('tag'):
673             pass
674         elif parser.check('feature'):
675             pass
676         else:
677             die('unhandled export command: %s' % line)
678
679     for ref, revid in parsed_refs.iteritems():
680         if ref.startswith('refs/heads/'):
681             name = ref[len('refs/heads/'):]
682             branch = get_remote_branch(name)
683             branch.generate_revision_history(revid, marks.get_tip(name))
684
685             if name in peers:
686                 peer = bzrlib.branch.Branch.open(peers[name],
687                                                  possible_transports=transports)
688                 try:
689                     peer.bzrdir.push_branch(branch, revision_id=revid)
690                 except bzrlib.errors.DivergedBranches:
691                     print "error %s non-fast forward" % ref
692                     continue
693
694             try:
695                 wt = branch.bzrdir.open_workingtree()
696                 wt.update()
697             except bzrlib.errors.NoWorkingTree:
698                 pass
699         elif ref.startswith('refs/tags/'):
700             # TODO: implement tag push
701             print "error %s pushing tags not supported" % ref
702             continue
703         else:
704             # transport-helper/fast-export bugs
705             continue
706
707         print "ok %s" % ref
708
709     print
710
711 def do_capabilities(parser):
712     print "import"
713     print "export"
714     print "refspec refs/heads/*:%s/heads/*" % prefix
715     print "refspec refs/tags/*:%s/tags/*" % prefix
716
717     path = os.path.join(dirname, 'marks-git')
718
719     if os.path.exists(path):
720         print "*import-marks %s" % path
721     print "*export-marks %s" % path
722
723     print
724
725 def ref_is_valid(name):
726     return not True in [c in name for c in '~^: \\']
727
728 def do_list(parser):
729     master_branch = None
730
731     for name in branches:
732         if not master_branch:
733             master_branch = name
734         print "? refs/heads/%s" % name
735
736     branch = get_remote_branch(master_branch)
737     branch.lock_read()
738     for tag, revid in branch.tags.get_tag_dict().items():
739         try:
740             branch.revision_id_to_dotted_revno(revid)
741         except bzrlib.errors.NoSuchRevision:
742             continue
743         if not ref_is_valid(tag):
744             continue
745         print "? refs/tags/%s" % tag
746         tags[tag] = revid
747     branch.unlock()
748
749     print "@refs/heads/%s HEAD" % master_branch
750     print
751
752 def clone(path, remote_branch):
753     try:
754         bdir = bzrlib.bzrdir.BzrDir.create(path, possible_transports=transports)
755     except bzrlib.errors.AlreadyControlDirError:
756         bdir = bzrlib.bzrdir.BzrDir.open(path, possible_transports=transports)
757     repo = bdir.find_repository()
758     repo.fetch(remote_branch.repository)
759     return remote_branch.sprout(bdir, repository=repo)
760
761 def get_remote_branch(name):
762     remote_branch = bzrlib.branch.Branch.open(branches[name],
763                                               possible_transports=transports)
764     if isinstance(remote_branch.bzrdir.root_transport, bzrlib.transport.local.LocalTransport):
765         return remote_branch
766
767     branch_path = os.path.join(dirname, 'clone', name)
768
769     try:
770         branch = bzrlib.branch.Branch.open(branch_path,
771                                            possible_transports=transports)
772     except bzrlib.errors.NotBranchError:
773         # clone
774         branch = clone(branch_path, remote_branch)
775     else:
776         # pull
777         try:
778             branch.pull(remote_branch, overwrite=True)
779         except bzrlib.errors.DivergedBranches:
780             # use remote branch for now
781             return remote_branch
782
783     return branch
784
785 def find_branches(repo):
786     transport = repo.bzrdir.root_transport
787
788     for fn in transport.iter_files_recursive():
789         if not fn.endswith('.bzr/branch-format'):
790             continue
791
792         name = subdir = fn[:-len('/.bzr/branch-format')]
793         name = name if name != '' else 'master'
794         name = name.replace('/', '+')
795
796         try:
797             cur = transport.clone(subdir)
798             branch = bzrlib.branch.Branch.open_from_transport(cur)
799         except bzrlib.errors.NotBranchError:
800             continue
801         else:
802             yield name, branch.base
803
804 def get_repo(url, alias):
805     normal_url = bzrlib.urlutils.normalize_url(url)
806     origin = bzrlib.bzrdir.BzrDir.open(url, possible_transports=transports)
807     is_local = isinstance(origin.transport, bzrlib.transport.local.LocalTransport)
808
809     shared_path = os.path.join(gitdir, 'bzr')
810     try:
811         shared_dir = bzrlib.bzrdir.BzrDir.open(shared_path,
812                                                possible_transports=transports)
813     except bzrlib.errors.NotBranchError:
814         shared_dir = bzrlib.bzrdir.BzrDir.create(shared_path,
815                                                  possible_transports=transports)
816     try:
817         shared_repo = shared_dir.open_repository()
818     except bzrlib.errors.NoRepositoryPresent:
819         shared_repo = shared_dir.create_repository(shared=True)
820
821     if not is_local:
822         clone_path = os.path.join(dirname, 'clone')
823         if not os.path.exists(clone_path):
824             os.mkdir(clone_path)
825         else:
826             # check and remove old organization
827             try:
828                 bdir = bzrlib.bzrdir.BzrDir.open(clone_path,
829                                                  possible_transports=transports)
830                 bdir.destroy_repository()
831             except bzrlib.errors.NotBranchError:
832                 pass
833             except bzrlib.errors.NoRepositoryPresent:
834                 pass
835
836     wanted = get_config('remote.%s.bzr-branches' % alias).rstrip().split(', ')
837     # stupid python
838     wanted = [e for e in wanted if e]
839     if not wanted:
840         wanted = get_config('remote-bzr.branches').rstrip().split(', ')
841         # stupid python
842         wanted = [e for e in wanted if e]
843
844     if not wanted:
845         try:
846             repo = origin.open_repository()
847             if not repo.bzrdir.root_transport.listable():
848                 # this repository is not usable for us
849                 raise bzrlib.errors.NoRepositoryPresent(repo.bzrdir)
850         except bzrlib.errors.NoRepositoryPresent:
851             wanted = ['master']
852
853     if wanted:
854         def list_wanted(url, wanted):
855             for name in wanted:
856                 subdir = name if name != 'master' else ''
857                 yield name, bzrlib.urlutils.join(url, subdir)
858
859         branch_list = list_wanted(url, wanted)
860     else:
861         branch_list = find_branches(repo)
862
863     for name, url in branch_list:
864         if not is_local:
865             peers[name] = url
866         branches[name] = url
867
868     return origin
869
870 def fix_path(alias, orig_url):
871     url = urlparse.urlparse(orig_url, 'file')
872     if url.scheme != 'file' or os.path.isabs(url.path):
873         return
874     abs_url = urlparse.urljoin("%s/" % os.getcwd(), orig_url)
875     cmd = ['git', 'config', 'remote.%s.url' % alias, "bzr::%s" % abs_url]
876     subprocess.call(cmd)
877
878 def main(args):
879     global marks, prefix, gitdir, dirname
880     global tags, filenodes
881     global blob_marks
882     global parsed_refs
883     global files_cache
884     global is_tmp
885     global branches, peers
886     global transports
887
888     marks = None
889     is_tmp = False
890     gitdir = os.environ.get('GIT_DIR', None)
891
892     if len(args) < 3:
893         die('Not enough arguments.')
894
895     if not gitdir:
896         die('GIT_DIR not set')
897
898     alias = args[1]
899     url = args[2]
900
901     tags = {}
902     filenodes = {}
903     blob_marks = {}
904     parsed_refs = {}
905     files_cache = {}
906     branches = {}
907     peers = {}
908     transports = []
909
910     if alias[5:] == url:
911         is_tmp = True
912         alias = hashlib.sha1(alias).hexdigest()
913
914     prefix = 'refs/bzr/%s' % alias
915     dirname = os.path.join(gitdir, 'bzr', alias)
916
917     if not is_tmp:
918         fix_path(alias, url)
919
920     if not os.path.exists(dirname):
921         os.makedirs(dirname)
922
923     if hasattr(bzrlib.ui.ui_factory, 'be_quiet'):
924         bzrlib.ui.ui_factory.be_quiet(True)
925
926     repo = get_repo(url, alias)
927
928     marks_path = os.path.join(dirname, 'marks-int')
929     marks = Marks(marks_path)
930
931     parser = Parser(repo)
932     for line in parser:
933         if parser.check('capabilities'):
934             do_capabilities(parser)
935         elif parser.check('list'):
936             do_list(parser)
937         elif parser.check('import'):
938             do_import(parser)
939         elif parser.check('export'):
940             do_export(parser)
941         else:
942             die('unhandled command: %s' % line)
943         sys.stdout.flush()
944
945     marks.store()
946
947 def bye():
948     if is_tmp:
949         shutil.rmtree(dirname)
950
951 atexit.register(bye)
952 sys.exit(main(sys.argv))