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