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