Merge branch 'cc/replace-with-the-same-type'
[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('^([^<>]+[^ \\\t<>])?\\b(?:[ \\t<>]*?)\\b([^ \\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             name = m.group(1)
197             mail = m.group(2)
198         else:
199             m = NAME_RE.match(user)
200             if m:
201                 name = m.group(1).strip()
202
203     if not name:
204         name = 'unknown'
205     if not mail:
206         mail = 'Unknown'
207
208     return '%s <%s>' % (name, mail)
209
210 def get_filechanges(cur, prev):
211     modified = {}
212     removed = {}
213
214     changes = cur.changes_from(prev)
215
216     def u(s):
217         return s.encode('utf-8')
218
219     for path, fid, kind in changes.added:
220         modified[u(path)] = fid
221     for path, fid, kind in changes.removed:
222         removed[u(path)] = None
223     for path, fid, kind, mod, _ in changes.modified:
224         modified[u(path)] = fid
225     for oldpath, newpath, fid, kind, mod, _ in changes.renamed:
226         removed[u(oldpath)] = None
227         if kind == 'directory':
228             lst = cur.list_files(from_dir=newpath, recursive=True)
229             for path, file_class, kind, fid, entry in lst:
230                 if kind != 'directory':
231                     modified[u(newpath + '/' + path)] = fid
232         else:
233             modified[u(newpath)] = fid
234
235     return modified, removed
236
237 def export_files(tree, files):
238     final = []
239     for path, fid in files.iteritems():
240         kind = tree.kind(fid)
241
242         h = tree.get_file_sha1(fid)
243
244         if kind == 'symlink':
245             d = tree.get_symlink_target(fid)
246             mode = '120000'
247         elif kind == 'file':
248
249             if tree.is_executable(fid):
250                 mode = '100755'
251             else:
252                 mode = '100644'
253
254             # is the blob already exported?
255             if h in filenodes:
256                 mark = filenodes[h]
257                 final.append((mode, mark, path))
258                 continue
259
260             d = tree.get_file_text(fid)
261         elif kind == 'directory':
262             continue
263         else:
264             die("Unhandled kind '%s' for path '%s'" % (kind, path))
265
266         mark = marks.next_mark()
267         filenodes[h] = mark
268
269         print "blob"
270         print "mark :%u" % mark
271         print "data %d" % len(d)
272         print d
273
274         final.append((mode, mark, path))
275
276     return final
277
278 def export_branch(repo, name):
279     ref = '%s/heads/%s' % (prefix, name)
280     tip = marks.get_tip(name)
281
282     branch = get_remote_branch(name)
283     repo = branch.repository
284
285     branch.lock_read()
286     revs = branch.iter_merge_sorted_revisions(None, tip, 'exclude', 'forward')
287     try:
288         tip_revno = branch.revision_id_to_revno(tip)
289         last_revno, _ = branch.last_revision_info()
290         total = last_revno - tip_revno
291     except bzrlib.errors.NoSuchRevision:
292         tip_revno = 0
293         total = 0
294
295     for revid, _, seq, _ in revs:
296
297         if marks.is_marked(revid):
298             continue
299
300         rev = repo.get_revision(revid)
301         revno = seq[0]
302
303         parents = rev.parent_ids
304         time = rev.timestamp
305         tz = rev.timezone
306         committer = rev.committer.encode('utf-8')
307         committer = "%s %u %s" % (fixup_user(committer), time, gittz(tz))
308         authors = rev.get_apparent_authors()
309         if authors:
310             author = authors[0].encode('utf-8')
311             author = "%s %u %s" % (fixup_user(author), time, gittz(tz))
312         else:
313             author = committer
314         msg = rev.message.encode('utf-8')
315
316         msg += '\n'
317
318         if len(parents) == 0:
319             parent = bzrlib.revision.NULL_REVISION
320         else:
321             parent = parents[0]
322
323         cur_tree = repo.revision_tree(revid)
324         prev = repo.revision_tree(parent)
325         modified, removed = get_filechanges(cur_tree, prev)
326
327         modified_final = export_files(cur_tree, modified)
328
329         if len(parents) == 0:
330             print 'reset %s' % ref
331
332         print "commit %s" % ref
333         print "mark :%d" % (marks.get_mark(revid))
334         print "author %s" % (author)
335         print "committer %s" % (committer)
336         print "data %d" % (len(msg))
337         print msg
338
339         for i, p in enumerate(parents):
340             try:
341                 m = rev_to_mark(p)
342             except KeyError:
343                 # ghost?
344                 continue
345             if i == 0:
346                 print "from :%s" % m
347             else:
348                 print "merge :%s" % m
349
350         for f in removed:
351             print "D %s" % (f,)
352         for f in modified_final:
353             print "M %s :%u %s" % f
354         print
355
356         if len(seq) > 1:
357             # let's skip branch revisions from the progress report
358             continue
359
360         progress = (revno - tip_revno)
361         if (progress % 100 == 0):
362             if total:
363                 print "progress revision %d '%s' (%d/%d)" % (revno, name, progress, total)
364             else:
365                 print "progress revision %d '%s' (%d)" % (revno, name, progress)
366
367     branch.unlock()
368
369     revid = branch.last_revision()
370
371     # make sure the ref is updated
372     print "reset %s" % ref
373     print "from :%u" % rev_to_mark(revid)
374     print
375
376     marks.set_tip(name, revid)
377
378 def export_tag(repo, name):
379     ref = '%s/tags/%s' % (prefix, name)
380     print "reset %s" % ref
381     print "from :%u" % rev_to_mark(tags[name])
382     print
383
384 def do_import(parser):
385     repo = parser.repo
386     path = os.path.join(dirname, 'marks-git')
387
388     print "feature done"
389     if os.path.exists(path):
390         print "feature import-marks=%s" % path
391     print "feature export-marks=%s" % path
392     print "feature force"
393     sys.stdout.flush()
394
395     while parser.check('import'):
396         ref = parser[1]
397         if ref.startswith('refs/heads/'):
398             name = ref[len('refs/heads/'):]
399             export_branch(repo, name)
400         if ref.startswith('refs/tags/'):
401             name = ref[len('refs/tags/'):]
402             export_tag(repo, name)
403         parser.next()
404
405     print 'done'
406
407     sys.stdout.flush()
408
409 def parse_blob(parser):
410     parser.next()
411     mark = parser.get_mark()
412     parser.next()
413     data = parser.get_data()
414     blob_marks[mark] = data
415     parser.next()
416
417 class CustomTree():
418
419     def __init__(self, branch, revid, parents, files):
420         self.updates = {}
421         self.branch = branch
422
423         def copy_tree(revid):
424             files = files_cache[revid] = {}
425             branch.lock_read()
426             tree = branch.repository.revision_tree(revid)
427             try:
428                 for path, entry in tree.iter_entries_by_dir():
429                     files[path] = [entry.file_id, None]
430             finally:
431                 branch.unlock()
432             return files
433
434         if len(parents) == 0:
435             self.base_id = bzrlib.revision.NULL_REVISION
436             self.base_files = {}
437         else:
438             self.base_id = parents[0]
439             self.base_files = files_cache.get(self.base_id, None)
440             if not self.base_files:
441                 self.base_files = copy_tree(self.base_id)
442
443         self.files = files_cache[revid] = self.base_files.copy()
444         self.rev_files = {}
445
446         for path, data in self.files.iteritems():
447             fid, mark = data
448             self.rev_files[fid] = [path, mark]
449
450         for path, f in files.iteritems():
451             fid, mark = self.files.get(path, [None, None])
452             if not fid:
453                 fid = bzrlib.generate_ids.gen_file_id(path)
454             f['path'] = path
455             self.rev_files[fid] = [path, mark]
456             self.updates[fid] = f
457
458     def last_revision(self):
459         return self.base_id
460
461     def iter_changes(self):
462         changes = []
463
464         def get_parent(dirname, basename):
465             parent_fid, mark = self.base_files.get(dirname, [None, None])
466             if parent_fid:
467                 return parent_fid
468             parent_fid, mark = self.files.get(dirname, [None, None])
469             if parent_fid:
470                 return parent_fid
471             if basename == '':
472                 return None
473             fid = bzrlib.generate_ids.gen_file_id(path)
474             add_entry(fid, dirname, 'directory')
475             return fid
476
477         def add_entry(fid, path, kind, mode=None):
478             dirname, basename = os.path.split(path)
479             parent_fid = get_parent(dirname, basename)
480
481             executable = False
482             if mode == '100755':
483                 executable = True
484             elif mode == '120000':
485                 kind = 'symlink'
486
487             change = (fid,
488                     (None, path),
489                     True,
490                     (False, True),
491                     (None, parent_fid),
492                     (None, basename),
493                     (None, kind),
494                     (None, executable))
495             self.files[path] = [change[0], None]
496             changes.append(change)
497
498         def update_entry(fid, path, kind, mode=None):
499             dirname, basename = os.path.split(path)
500             parent_fid = get_parent(dirname, basename)
501
502             executable = False
503             if mode == '100755':
504                 executable = True
505             elif mode == '120000':
506                 kind = 'symlink'
507
508             change = (fid,
509                     (path, path),
510                     True,
511                     (True, True),
512                     (None, parent_fid),
513                     (None, basename),
514                     (None, kind),
515                     (None, executable))
516             self.files[path] = [change[0], None]
517             changes.append(change)
518
519         def remove_entry(fid, path, kind):
520             dirname, basename = os.path.split(path)
521             parent_fid = get_parent(dirname, basename)
522             change = (fid,
523                     (path, None),
524                     True,
525                     (True, False),
526                     (parent_fid, None),
527                     (None, None),
528                     (None, None),
529                     (None, None))
530             del self.files[path]
531             changes.append(change)
532
533         for fid, f in self.updates.iteritems():
534             path = f['path']
535
536             if 'deleted' in f:
537                 remove_entry(fid, path, 'file')
538                 continue
539
540             if path in self.base_files:
541                 update_entry(fid, path, 'file', f['mode'])
542             else:
543                 add_entry(fid, path, 'file', f['mode'])
544
545             self.files[path][1] = f['mark']
546             self.rev_files[fid][1] = f['mark']
547
548         return changes
549
550     def get_content(self, file_id):
551         path, mark = self.rev_files[file_id]
552         if mark:
553             return blob_marks[mark]
554
555         # last resort
556         tree = self.branch.repository.revision_tree(self.base_id)
557         return tree.get_file_text(file_id)
558
559     def get_file_with_stat(self, file_id, path=None):
560         content = self.get_content(file_id)
561         return (StringIO.StringIO(content), None)
562
563     def get_symlink_target(self, file_id):
564         return self.get_content(file_id)
565
566     def id2path(self, file_id):
567         path, mark = self.rev_files[file_id]
568         return path
569
570 def c_style_unescape(string):
571     if string[0] == string[-1] == '"':
572         return string.decode('string-escape')[1:-1]
573     return string
574
575 def parse_commit(parser):
576     parents = []
577
578     ref = parser[1]
579     parser.next()
580
581     if ref.startswith('refs/heads/'):
582         name = ref[len('refs/heads/'):]
583         branch = get_remote_branch(name)
584     else:
585         die('unknown ref')
586
587     commit_mark = parser.get_mark()
588     parser.next()
589     author = parser.get_author()
590     parser.next()
591     committer = parser.get_author()
592     parser.next()
593     data = parser.get_data()
594     parser.next()
595     if parser.check('from'):
596         parents.append(parser.get_mark())
597         parser.next()
598     while parser.check('merge'):
599         parents.append(parser.get_mark())
600         parser.next()
601
602     # fast-export adds an extra newline
603     if data[-1] == '\n':
604         data = data[:-1]
605
606     files = {}
607
608     for line in parser:
609         if parser.check('M'):
610             t, m, mark_ref, path = line.split(' ', 3)
611             mark = int(mark_ref[1:])
612             f = { 'mode' : m, 'mark' : mark }
613         elif parser.check('D'):
614             t, path = line.split(' ', 1)
615             f = { 'deleted' : True }
616         else:
617             die('Unknown file command: %s' % line)
618         path = c_style_unescape(path).decode('utf-8')
619         files[path] = f
620
621     committer, date, tz = committer
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
627     mtree = CustomTree(branch, revid, parents, files)
628     changes = mtree.iter_changes()
629
630     branch.lock_write()
631     try:
632         builder = branch.get_commit_builder(parents, None, date, tz, committer, props, revid)
633         try:
634             list(builder.record_iter_changes(mtree, mtree.last_revision(), changes))
635             builder.finish_inventory()
636             builder.commit(data.decode('utf-8', 'replace'))
637         except Exception, e:
638             builder.abort()
639             raise
640     finally:
641         branch.unlock()
642
643     parsed_refs[ref] = revid
644     marks.new_mark(revid, commit_mark)
645
646 def parse_reset(parser):
647     ref = parser[1]
648     parser.next()
649
650     # ugh
651     if parser.check('commit'):
652         parse_commit(parser)
653         return
654     if not parser.check('from'):
655         return
656     from_mark = parser.get_mark()
657     parser.next()
658
659     parsed_refs[ref] = mark_to_rev(from_mark)
660
661 def do_export(parser):
662     parser.next()
663
664     for line in parser.each_block('done'):
665         if parser.check('blob'):
666             parse_blob(parser)
667         elif parser.check('commit'):
668             parse_commit(parser)
669         elif parser.check('reset'):
670             parse_reset(parser)
671         elif parser.check('tag'):
672             pass
673         elif parser.check('feature'):
674             pass
675         else:
676             die('unhandled export command: %s' % line)
677
678     for ref, revid in parsed_refs.iteritems():
679         if ref.startswith('refs/heads/'):
680             name = ref[len('refs/heads/'):]
681             branch = get_remote_branch(name)
682             branch.generate_revision_history(revid, marks.get_tip(name))
683
684             if name in peers:
685                 peer = bzrlib.branch.Branch.open(peers[name],
686                                                  possible_transports=transports)
687                 try:
688                     peer.bzrdir.push_branch(branch, revision_id=revid)
689                 except bzrlib.errors.DivergedBranches:
690                     print "error %s non-fast forward" % ref
691                     continue
692
693             try:
694                 wt = branch.bzrdir.open_workingtree()
695                 wt.update()
696             except bzrlib.errors.NoWorkingTree:
697                 pass
698         elif ref.startswith('refs/tags/'):
699             # TODO: implement tag push
700             print "error %s pushing tags not supported" % ref
701             continue
702         else:
703             # transport-helper/fast-export bugs
704             continue
705
706         print "ok %s" % ref
707
708     print
709
710 def do_capabilities(parser):
711     print "import"
712     print "export"
713     print "refspec refs/heads/*:%s/heads/*" % prefix
714     print "refspec refs/tags/*:%s/tags/*" % prefix
715
716     path = os.path.join(dirname, 'marks-git')
717
718     if os.path.exists(path):
719         print "*import-marks %s" % path
720     print "*export-marks %s" % path
721
722     print
723
724 def ref_is_valid(name):
725     return not True in [c in name for c in '~^: \\']
726
727 def do_list(parser):
728     master_branch = None
729
730     for name in branches:
731         if not master_branch:
732             master_branch = name
733         print "? refs/heads/%s" % name
734
735     branch = get_remote_branch(master_branch)
736     branch.lock_read()
737     for tag, revid in branch.tags.get_tag_dict().items():
738         try:
739             branch.revision_id_to_dotted_revno(revid)
740         except bzrlib.errors.NoSuchRevision:
741             continue
742         if not ref_is_valid(tag):
743             continue
744         print "? refs/tags/%s" % tag
745         tags[tag] = revid
746     branch.unlock()
747
748     print "@refs/heads/%s HEAD" % master_branch
749     print
750
751 def clone(path, remote_branch):
752     try:
753         bdir = bzrlib.bzrdir.BzrDir.create(path, possible_transports=transports)
754     except bzrlib.errors.AlreadyControlDirError:
755         bdir = bzrlib.bzrdir.BzrDir.open(path, possible_transports=transports)
756     repo = bdir.find_repository()
757     repo.fetch(remote_branch.repository)
758     return remote_branch.sprout(bdir, repository=repo)
759
760 def get_remote_branch(name):
761     remote_branch = bzrlib.branch.Branch.open(branches[name],
762                                               possible_transports=transports)
763     if isinstance(remote_branch.user_transport, bzrlib.transport.local.LocalTransport):
764         return remote_branch
765
766     branch_path = os.path.join(dirname, 'clone', name)
767
768     try:
769         branch = bzrlib.branch.Branch.open(branch_path,
770                                            possible_transports=transports)
771     except bzrlib.errors.NotBranchError:
772         # clone
773         branch = clone(branch_path, remote_branch)
774     else:
775         # pull
776         try:
777             branch.pull(remote_branch, overwrite=True)
778         except bzrlib.errors.DivergedBranches:
779             # use remote branch for now
780             return remote_branch
781
782     return branch
783
784 def find_branches(repo):
785     transport = repo.bzrdir.root_transport
786
787     for fn in transport.iter_files_recursive():
788         if not fn.endswith('.bzr/branch-format'):
789             continue
790
791         name = subdir = fn[:-len('/.bzr/branch-format')]
792         name = name if name != '' else 'master'
793         name = name.replace('/', '+')
794
795         try:
796             cur = transport.clone(subdir)
797             branch = bzrlib.branch.Branch.open_from_transport(cur)
798         except bzrlib.errors.NotBranchError:
799             continue
800         else:
801             yield name, branch.base
802
803 def get_repo(url, alias):
804     normal_url = bzrlib.urlutils.normalize_url(url)
805     origin = bzrlib.bzrdir.BzrDir.open(url, possible_transports=transports)
806     is_local = isinstance(origin.transport, bzrlib.transport.local.LocalTransport)
807
808     shared_path = os.path.join(gitdir, 'bzr')
809     try:
810         shared_dir = bzrlib.bzrdir.BzrDir.open(shared_path,
811                                                possible_transports=transports)
812     except bzrlib.errors.NotBranchError:
813         shared_dir = bzrlib.bzrdir.BzrDir.create(shared_path,
814                                                  possible_transports=transports)
815     try:
816         shared_repo = shared_dir.open_repository()
817     except bzrlib.errors.NoRepositoryPresent:
818         shared_repo = shared_dir.create_repository(shared=True)
819
820     if not is_local:
821         clone_path = os.path.join(dirname, 'clone')
822         if not os.path.exists(clone_path):
823             os.mkdir(clone_path)
824         else:
825             # check and remove old organization
826             try:
827                 bdir = bzrlib.bzrdir.BzrDir.open(clone_path,
828                                                  possible_transports=transports)
829                 bdir.destroy_repository()
830             except bzrlib.errors.NotBranchError:
831                 pass
832             except bzrlib.errors.NoRepositoryPresent:
833                 pass
834
835     wanted = get_config('remote.%s.bzr-branches' % alias).rstrip().split(', ')
836     # stupid python
837     wanted = [e for e in wanted if e]
838     if not wanted:
839         wanted = get_config('remote-bzr.branches').rstrip().split(', ')
840         # stupid python
841         wanted = [e for e in wanted if e]
842
843     if not wanted:
844         try:
845             repo = origin.open_repository()
846             if not repo.user_transport.listable():
847                 # this repository is not usable for us
848                 raise bzrlib.errors.NoRepositoryPresent(repo.bzrdir)
849         except bzrlib.errors.NoRepositoryPresent:
850             wanted = ['master']
851
852     if wanted:
853         def list_wanted(url, wanted):
854             for name in wanted:
855                 subdir = name if name != 'master' else ''
856                 yield name, bzrlib.urlutils.join(url, subdir)
857
858         branch_list = list_wanted(url, wanted)
859     else:
860         branch_list = find_branches(repo)
861
862     for name, url in branch_list:
863         if not is_local:
864             peers[name] = url
865         branches[name] = url
866
867     return origin
868
869 def fix_path(alias, orig_url):
870     url = urlparse.urlparse(orig_url, 'file')
871     if url.scheme != 'file' or os.path.isabs(url.path):
872         return
873     abs_url = urlparse.urljoin("%s/" % os.getcwd(), orig_url)
874     cmd = ['git', 'config', 'remote.%s.url' % alias, "bzr::%s" % abs_url]
875     subprocess.call(cmd)
876
877 def main(args):
878     global marks, prefix, gitdir, dirname
879     global tags, filenodes
880     global blob_marks
881     global parsed_refs
882     global files_cache
883     global is_tmp
884     global branches, peers
885     global transports
886
887     alias = args[1]
888     url = args[2]
889
890     tags = {}
891     filenodes = {}
892     blob_marks = {}
893     parsed_refs = {}
894     files_cache = {}
895     marks = None
896     branches = {}
897     peers = {}
898     transports = []
899
900     if alias[5:] == url:
901         is_tmp = True
902         alias = hashlib.sha1(alias).hexdigest()
903     else:
904         is_tmp = False
905
906     prefix = 'refs/bzr/%s' % alias
907     gitdir = os.environ['GIT_DIR']
908     dirname = os.path.join(gitdir, 'bzr', alias)
909
910     if not is_tmp:
911         fix_path(alias, url)
912
913     if not os.path.exists(dirname):
914         os.makedirs(dirname)
915
916     if hasattr(bzrlib.ui.ui_factory, 'be_quiet'):
917         bzrlib.ui.ui_factory.be_quiet(True)
918
919     repo = get_repo(url, alias)
920
921     marks_path = os.path.join(dirname, 'marks-int')
922     marks = Marks(marks_path)
923
924     parser = Parser(repo)
925     for line in parser:
926         if parser.check('capabilities'):
927             do_capabilities(parser)
928         elif parser.check('list'):
929             do_list(parser)
930         elif parser.check('import'):
931             do_import(parser)
932         elif parser.check('export'):
933             do_export(parser)
934         else:
935             die('unhandled command: %s' % line)
936         sys.stdout.flush()
937
938 def bye():
939     if not marks:
940         return
941     if not is_tmp:
942         marks.store()
943     else:
944         shutil.rmtree(dirname)
945
946 atexit.register(bye)
947 sys.exit(main(sys.argv))