remote-bzr: include authors field in pushed commits
[git/git-svn.git] / contrib / remote-helpers / git-remote-bzr
blob6ca1e975c3e01f1b414d9b186f97492e5f9e44c6
1 #!/usr/bin/env python
3 # Copyright (c) 2012 Felipe Contreras
7 # Just copy to your ~/bin, or anywhere in your $PATH.
8 # Then you can clone with:
9 # % git clone bzr::/path/to/bzr/repo/or/url
11 # For example:
12 # % git clone bzr::$HOME/myrepo
13 # or
14 # % git clone bzr::lp:myrepo
16 # If you want to specify which branches you want to track (per repo):
17 # % git config remote.origin.bzr-branches 'trunk, devel, test'
19 # Where 'origin' is the name of the repository you want to specify the
20 # branches.
23 import sys
25 import bzrlib
26 if hasattr(bzrlib, "initialize"):
27 bzrlib.initialize()
29 import bzrlib.plugin
30 bzrlib.plugin.load_plugins()
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
39 import sys
40 import os
41 import json
42 import re
43 import StringIO
44 import atexit, shutil, hashlib, urlparse, subprocess
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+)')
51 def die(msg, *args):
52 sys.stderr.write('ERROR: %s\n' % (msg % args))
53 sys.exit(1)
55 def warn(msg, *args):
56 sys.stderr.write('WARNING: %s\n' % (msg % args))
58 def gittz(tz):
59 return '%+03d%02d' % (tz / 3600, tz % 3600 / 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
67 class Marks:
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()
77 def load(self):
78 if not os.path.exists(self.path):
79 return
81 tmp = json.load(open(self.path))
82 self.tips = tmp['tips']
83 self.marks = tmp['marks']
84 self.last_mark = tmp['last-mark']
86 for rev, mark in self.marks.iteritems():
87 self.rev_marks[mark] = rev
89 def dict(self):
90 return { 'tips': self.tips, 'marks': self.marks, 'last-mark' : self.last_mark }
92 def store(self):
93 json.dump(self.dict(), open(self.path, 'w'))
95 def __str__(self):
96 return str(self.dict())
98 def from_rev(self, rev):
99 return self.marks[rev]
101 def to_rev(self, mark):
102 return str(self.rev_marks[mark])
104 def next_mark(self):
105 self.last_mark += 1
106 return self.last_mark
108 def get_mark(self, rev):
109 self.last_mark += 1
110 self.marks[rev] = self.last_mark
111 return self.last_mark
113 def is_marked(self, rev):
114 return rev in self.marks
116 def new_mark(self, rev, mark):
117 self.marks[rev] = mark
118 self.rev_marks[mark] = rev
119 self.last_mark = mark
121 def get_tip(self, branch):
122 try:
123 return str(self.tips[branch])
124 except KeyError:
125 return None
127 def set_tip(self, branch, tip):
128 self.tips[branch] = tip
130 class Parser:
132 def __init__(self, repo):
133 self.repo = repo
134 self.line = self.get_line()
136 def get_line(self):
137 return sys.stdin.readline().strip()
139 def __getitem__(self, i):
140 return self.line.split()[i]
142 def check(self, word):
143 return self.line.startswith(word)
145 def each_block(self, separator):
146 while self.line != separator:
147 yield self.line
148 self.line = self.get_line()
150 def __iter__(self):
151 return self.each_block('')
153 def next(self):
154 self.line = self.get_line()
155 if self.line == 'done':
156 self.line = None
158 def get_mark(self):
159 i = self.line.index(':') + 1
160 return int(self.line[i:])
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)
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)
180 def rev_to_mark(rev):
181 return marks.from_rev(rev)
183 def mark_to_rev(mark):
184 return marks.to_rev(mark)
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()
202 if not name:
203 name = 'unknown'
204 if not mail:
205 mail = 'Unknown'
207 return '%s <%s>' % (name, mail)
209 def get_filechanges(cur, prev):
210 modified = {}
211 removed = {}
213 changes = cur.changes_from(prev)
215 def u(s):
216 return s.encode('utf-8')
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
234 return modified, removed
236 def export_files(tree, files):
237 final = []
238 for path, fid in files.iteritems():
239 kind = tree.kind(fid)
241 h = tree.get_file_sha1(fid)
243 if kind == 'symlink':
244 d = tree.get_symlink_target(fid)
245 mode = '120000'
246 elif kind == 'file':
248 if tree.is_executable(fid):
249 mode = '100755'
250 else:
251 mode = '100644'
253 # is the blob already exported?
254 if h in filenodes:
255 mark = filenodes[h]
256 final.append((mode, mark, path))
257 continue
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))
265 mark = marks.next_mark()
266 filenodes[h] = mark
268 print "blob"
269 print "mark :%u" % mark
270 print "data %d" % len(d)
271 print d
273 final.append((mode, mark, path))
275 return final
277 def export_branch(repo, name):
278 ref = '%s/heads/%s' % (prefix, name)
279 tip = marks.get_tip(name)
281 branch = get_remote_branch(name)
282 repo = branch.repository
284 branch.lock_read()
285 revs = branch.iter_merge_sorted_revisions(None, tip, 'exclude', 'forward')
286 try:
287 tip_revno = branch.revision_id_to_revno(tip)
288 last_revno, _ = branch.last_revision_info()
289 total = last_revno - tip_revno
290 except bzrlib.errors.NoSuchRevision:
291 tip_revno = 0
292 total = 0
294 for revid, _, seq, _ in revs:
296 if marks.is_marked(revid):
297 continue
299 rev = repo.get_revision(revid)
300 revno = seq[0]
302 parents = rev.parent_ids
303 time = rev.timestamp
304 tz = rev.timezone
305 committer = rev.committer.encode('utf-8')
306 committer = "%s %u %s" % (fixup_user(committer), time, gittz(tz))
307 authors = rev.get_apparent_authors()
308 if authors:
309 author = authors[0].encode('utf-8')
310 author = "%s %u %s" % (fixup_user(author), time, gittz(tz))
311 else:
312 author = committer
313 msg = rev.message.encode('utf-8')
315 msg += '\n'
317 if len(parents) == 0:
318 parent = bzrlib.revision.NULL_REVISION
319 else:
320 parent = parents[0]
322 cur_tree = repo.revision_tree(revid)
323 prev = repo.revision_tree(parent)
324 modified, removed = get_filechanges(cur_tree, prev)
326 modified_final = export_files(cur_tree, modified)
328 if len(parents) == 0:
329 print 'reset %s' % ref
331 print "commit %s" % ref
332 print "mark :%d" % (marks.get_mark(revid))
333 print "author %s" % (author)
334 print "committer %s" % (committer)
335 print "data %d" % (len(msg))
336 print msg
338 for i, p in enumerate(parents):
339 try:
340 m = rev_to_mark(p)
341 except KeyError:
342 # ghost?
343 continue
344 if i == 0:
345 print "from :%s" % m
346 else:
347 print "merge :%s" % m
349 for f in removed:
350 print "D %s" % (f,)
351 for f in modified_final:
352 print "M %s :%u %s" % f
353 print
355 if len(seq) > 1:
356 # let's skip branch revisions from the progress report
357 continue
359 progress = (revno - tip_revno)
360 if (progress % 100 == 0):
361 if total:
362 print "progress revision %d '%s' (%d/%d)" % (revno, name, progress, total)
363 else:
364 print "progress revision %d '%s' (%d)" % (revno, name, progress)
366 branch.unlock()
368 revid = branch.last_revision()
370 # make sure the ref is updated
371 print "reset %s" % ref
372 print "from :%u" % rev_to_mark(revid)
373 print
375 marks.set_tip(name, revid)
377 def export_tag(repo, name):
378 ref = '%s/tags/%s' % (prefix, name)
379 print "reset %s" % ref
380 print "from :%u" % rev_to_mark(tags[name])
381 print
383 def do_import(parser):
384 repo = parser.repo
385 path = os.path.join(dirname, 'marks-git')
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()
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()
404 print 'done'
406 sys.stdout.flush()
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()
416 class CustomTree():
418 def __init__(self, branch, revid, parents, files):
419 self.updates = {}
420 self.branch = branch
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
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)
442 self.files = files_cache[revid] = self.base_files.copy()
443 self.rev_files = {}
445 for path, data in self.files.iteritems():
446 fid, mark = data
447 self.rev_files[fid] = [path, mark]
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
457 def last_revision(self):
458 return self.base_id
460 def iter_changes(self):
461 changes = []
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
476 def add_entry(fid, path, kind, mode=None):
477 dirname, basename = os.path.split(path)
478 parent_fid = get_parent(dirname, basename)
480 executable = False
481 if mode == '100755':
482 executable = True
483 elif mode == '120000':
484 kind = 'symlink'
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)
497 def update_entry(fid, path, kind, mode=None):
498 dirname, basename = os.path.split(path)
499 parent_fid = get_parent(dirname, basename)
501 executable = False
502 if mode == '100755':
503 executable = True
504 elif mode == '120000':
505 kind = 'symlink'
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)
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)
532 for fid, f in self.updates.iteritems():
533 path = f['path']
535 if 'deleted' in f:
536 remove_entry(fid, path, 'file')
537 continue
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'])
544 self.files[path][1] = f['mark']
545 self.rev_files[fid][1] = f['mark']
547 return changes
549 def get_content(self, file_id):
550 path, mark = self.rev_files[file_id]
551 if mark:
552 return blob_marks[mark]
554 # last resort
555 tree = self.branch.repository.revision_tree(self.base_id)
556 return tree.get_file_text(file_id)
558 def get_file_with_stat(self, file_id, path=None):
559 content = self.get_content(file_id)
560 return (StringIO.StringIO(content), None)
562 def get_symlink_target(self, file_id):
563 return self.get_content(file_id)
565 def id2path(self, file_id):
566 path, mark = self.rev_files[file_id]
567 return path
569 def c_style_unescape(string):
570 if string[0] == string[-1] == '"':
571 return string.decode('string-escape')[1:-1]
572 return string
574 def parse_commit(parser):
575 parents = []
577 ref = parser[1]
578 parser.next()
580 if ref.startswith('refs/heads/'):
581 name = ref[len('refs/heads/'):]
582 branch = get_remote_branch(name)
583 else:
584 die('unknown ref')
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()
601 # fast-export adds an extra newline
602 if data[-1] == '\n':
603 data = data[:-1]
605 files = {}
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
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
628 mtree = CustomTree(branch, revid, parents, files)
629 changes = mtree.iter_changes()
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()
644 parsed_refs[ref] = revid
645 marks.new_mark(revid, commit_mark)
647 def parse_reset(parser):
648 ref = parser[1]
649 parser.next()
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()
660 parsed_refs[ref] = mark_to_rev(from_mark)
662 def do_export(parser):
663 parser.next()
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)
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))
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
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
707 print "ok %s" % ref
709 print
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
717 path = os.path.join(dirname, 'marks-git')
719 if os.path.exists(path):
720 print "*import-marks %s" % path
721 print "*export-marks %s" % path
723 print
725 def ref_is_valid(name):
726 return not True in [c in name for c in '~^: \\']
728 def do_list(parser):
729 master_branch = None
731 for name in branches:
732 if not master_branch:
733 master_branch = name
734 print "? refs/heads/%s" % name
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()
749 print "@refs/heads/%s HEAD" % master_branch
750 print
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)
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
767 branch_path = os.path.join(dirname, 'clone', name)
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
783 return branch
785 def find_branches(repo):
786 transport = repo.bzrdir.root_transport
788 for fn in transport.iter_files_recursive():
789 if not fn.endswith('.bzr/branch-format'):
790 continue
792 name = subdir = fn[:-len('/.bzr/branch-format')]
793 name = name if name != '' else 'master'
794 name = name.replace('/', '+')
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
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)
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)
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
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]
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']
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)
859 branch_list = list_wanted(url, wanted)
860 else:
861 branch_list = find_branches(repo)
863 for name, url in branch_list:
864 if not is_local:
865 peers[name] = url
866 branches[name] = url
868 return origin
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)
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
888 marks = None
889 is_tmp = False
890 gitdir = os.environ.get('GIT_DIR', None)
892 if len(args) < 3:
893 die('Not enough arguments.')
895 if not gitdir:
896 die('GIT_DIR not set')
898 alias = args[1]
899 url = args[2]
901 tags = {}
902 filenodes = {}
903 blob_marks = {}
904 parsed_refs = {}
905 files_cache = {}
906 branches = {}
907 peers = {}
908 transports = []
910 if alias[5:] == url:
911 is_tmp = True
912 alias = hashlib.sha1(alias).hexdigest()
914 prefix = 'refs/bzr/%s' % alias
915 dirname = os.path.join(gitdir, 'bzr', alias)
917 if not is_tmp:
918 fix_path(alias, url)
920 if not os.path.exists(dirname):
921 os.makedirs(dirname)
923 if hasattr(bzrlib.ui.ui_factory, 'be_quiet'):
924 bzrlib.ui.ui_factory.be_quiet(True)
926 repo = get_repo(url, alias)
928 marks_path = os.path.join(dirname, 'marks-int')
929 marks = Marks(marks_path)
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()
945 def bye():
946 if not marks:
947 return
948 if not is_tmp:
949 marks.store()
950 else:
951 shutil.rmtree(dirname)
953 atexit.register(bye)
954 sys.exit(main(sys.argv))