comparison git_handler.py @ 183:469e80d3142a

Reorder methods by their functionality.
author Abderrahim Kitouni <a.kitouni@gmail.com>
date Tue, 16 Jun 2009 13:39:11 +0100
parents 9bdd8e76bbab
children 7bf98d3085f4
comparison
equal deleted inserted replaced
182:9bdd8e76bbab 183:469e80d3142a
73 file.write("%s %s\n" % (gitsha, hgsha)) 73 file.write("%s %s\n" % (gitsha, hgsha))
74 file.rename() 74 file.rename()
75 75
76 ## END FILE LOAD AND SAVE METHODS 76 ## END FILE LOAD AND SAVE METHODS
77 77
78 ## COMMANDS METHODS
79
78 def import_commits(self, remote_name): 80 def import_commits(self, remote_name):
79 self.import_git_objects(remote_name) 81 self.import_git_objects(remote_name)
80 self.save_map() 82 self.save_map()
81 83
82 def fetch(self, remote_name): 84 def fetch(self, remote_name):
100 self.ui.status(_("pushing to : %s\n") % remote_name) 102 self.ui.status(_("pushing to : %s\n") % remote_name)
101 self.export_commits(False) 103 self.export_commits(False)
102 self.update_remote_references(remote_name) 104 self.update_remote_references(remote_name)
103 self.upload_pack(remote_name) 105 self.upload_pack(remote_name)
104 106
105 def update_references(self): 107 def clear(self):
106 try: 108 mapfile = self.repo.join(self.mapfile)
107 # We only care about bookmarks of the form 'name', 109 if os.path.exists(self.gitdir):
108 # not 'remote/name'. 110 for root, dirs, files in os.walk(self.gitdir, topdown=False):
109 def is_local_ref(item): return item[0].count('/') == 0 111 for name in files:
110 bms = bookmarks.parse(self.repo) 112 os.remove(os.path.join(root, name))
111 bms = dict(filter(is_local_ref, bms.items())) 113 for name in dirs:
112 114 os.rmdir(os.path.join(root, name))
113 # Create a local Git branch name for each 115 os.rmdir(self.gitdir)
114 # Mercurial bookmark. 116 if os.path.exists(mapfile):
115 for key in bms: 117 os.remove(mapfile)
116 hg_sha = hex(bms[key]) 118
117 git_sha = self.map_git_get(hg_sha) 119 ## CHANGESET CONVERSION METHODS
118 self.git.set_ref('refs/heads/' + key, git_sha)
119 except AttributeError:
120 # No bookmarks extension
121 pass
122
123 c = self.map_git_get(hex(self.repo.changelog.tip()))
124 self.git.set_ref(self.exportbranch, c)
125
126 def export_hg_tags(self):
127 for tag, sha in self.repo.tags().iteritems():
128 if tag[-3:] == '^{}':
129 continue
130 if tag == 'tip':
131 continue
132 self.git.set_ref('refs/tags/' + tag, self.map_git_get(hex(sha)))
133
134 # Make sure there's a refs/remotes/remote_name/name
135 # for every refs/heads/name
136 def update_remote_references(self, remote_name):
137 self.git.set_remote_refs(self.local_heads(), remote_name)
138
139 def local_heads(self):
140 def is_local_head(item): return item[0].startswith('refs/heads')
141 refs = self.git.get_refs()
142 return dict(filter(is_local_head, refs.items()))
143 120
144 def export_git_objects(self): 121 def export_git_objects(self):
145 self.previous_entries = {} 122 self.previous_entries = {}
146 self.written_trees = {} 123 self.written_trees = {}
147 self.ui.status(_("importing Hg objects into Git\n")) 124 self.ui.status(_("importing Hg objects into Git\n"))
390 tree_shas[dirnm] = tree_sha 367 tree_shas[dirnm] = tree_sha
391 self.written_trees[listsha] = tree_sha 368 self.written_trees[listsha] = tree_sha
392 369
393 return (tree_sha, renames) # should be the last root tree sha 370 return (tree_sha, renames) # should be the last root tree sha
394 371
395 def remote_head(self, remote_name): 372 def import_git_objects(self, remote_name=None, refs=None):
396 for head, sha in self.git.remote_refs(remote_name).iteritems(): 373 self.ui.status(_("importing Git objects into Hg\n"))
397 if head == 'HEAD': 374 # import heads and fetched tags as remote references
398 return self.map_hg_get(sha) 375 todo = []
399 return None 376 done = set()
377 convert_list = {}
378
379 # get a list of all the head shas
380 if refs:
381 for head, sha in refs.iteritems():
382 todo.append(sha)
383 else:
384 if remote_name:
385 todo = self.git.remote_refs(remote_name).values()[:]
386 elif self.importbranch:
387 branches = self.importbranch.split(',')
388 todo = [self.git.ref(i.strip()) for i in branches]
389 else:
390 todo = self.git.heads().values()[:]
391
392 # traverse the heads getting a list of all the unique commits
393 while todo:
394 sha = todo.pop()
395 assert isinstance(sha, str)
396 if sha in done:
397 continue
398 done.add(sha)
399 obj = self.git.get_object(sha)
400 if isinstance (obj, Commit):
401 convert_list[sha] = obj
402 todo.extend([p for p in obj.parents if p not in done])
403 if isinstance(obj, Tag):
404 (obj_type, obj_sha) = obj.get_object()
405 obj = self.git.get_object(obj_sha)
406 if isinstance (obj, Commit):
407 convert_list[sha] = obj
408 todo.extend([p for p in obj.parents if p not in done])
409
410 # sort the commits
411 commits = toposort.TopoSort(convert_list).items()
412
413 # import each of the commits, oldest first
414 total = len(commits)
415 magnitude = int(math.log(total, 10)) + 1 if total else 1
416 for i, csha in enumerate(commits):
417 if i%100 == 0:
418 self.ui.status(_("at: %*d/%d\n") % (magnitude, i, total))
419 commit = convert_list[csha]
420 if not self.map_hg_get(csha): # it's already here
421 self.import_git_commit(commit)
422
423 self.update_hg_bookmarks(remote_name)
424
425 def import_git_commit(self, commit):
426 self.ui.debug(_("importing: %s\n") % commit.id)
427 # TODO : Do something less coarse-grained than try/except on the
428 # get_file call for removed files
429
430 (strip_message, hg_renames, hg_branch, force_files, extra) = self.extract_hg_metadata(commit.message)
431
432 # get a list of the changed, added, removed files
433 files = self.git.get_files_changed(commit)
434
435 date = (commit.author_time, -commit.author_timezone)
436 text = strip_message
437
438 def getfilectx(repo, memctx, f):
439 try:
440 (mode, sha, data) = self.git.get_file(commit, f)
441 e = self.convert_git_int_mode(mode)
442 except TypeError:
443 raise IOError()
444 if f in hg_renames:
445 copied_path = hg_renames[f]
446 else:
447 copied_path = None
448 return context.memfilectx(f, data, 'l' in e, 'x' in e, copied_path)
449
450 gparents = map(self.map_hg_get, commit.parents)
451 p1, p2 = (nullid, nullid)
452 octopus = False
453
454 if len(gparents) > 1:
455 # merge, possibly octopus
456 def commit_octopus(p1, p2):
457 ctx = context.memctx(self.repo, (p1, p2), text, files, getfilectx,
458 commit.author, date, {'hg-git': 'octopus'})
459 return hex(self.repo.commitctx(ctx))
460
461 octopus = len(gparents) > 2
462 p2 = gparents.pop()
463 p1 = gparents.pop()
464 while len(gparents) > 0:
465 p2 = commit_octopus(p1, p2)
466 p1 = gparents.pop()
467 else:
468 if gparents:
469 p1 = gparents.pop()
470
471 files = list(set(files))
472
473 pa = None
474 if not (p2 == nullid):
475 node1 = self.repo.changectx(p1)
476 node2 = self.repo.changectx(p2)
477 pa = node1.ancestor(node2)
478
479 author = commit.author
480
481 # convert extra data back to the end
482 if ' ext:' in commit.author:
483 regex = re.compile('^(.*?)\ ext:\((.*)\) <(.*)\>$')
484 m = regex.match(commit.author)
485 if m:
486 name = m.group(1)
487 ex = urllib.unquote(m.group(2))
488 email = m.group(3)
489 author = name + ' <' + email + '>' + ex
490
491 if ' <none@none>' in commit.author:
492 author = commit.author[:-12]
493
494 # if named branch, add to extra
495 if hg_branch:
496 extra['branch'] = hg_branch
497
498 # if committer is different than author, add it to extra
499 if not commit._author_raw == commit._committer_raw:
500 extra['committer'] = "%s %d %d" % (commit.committer, commit.commit_time, -commit.commit_timezone)
501
502 if commit._encoding:
503 extra['encoding'] = commit._encoding
504
505 if hg_branch:
506 extra['branch'] = hg_branch
507
508 if octopus:
509 extra['hg-git'] ='octopus-done'
510
511 ctx = context.memctx(self.repo, (p1, p2), text, files, getfilectx,
512 author, date, extra)
513
514 node = self.repo.commit_import_ctx(ctx, pa, force_files)
515
516 # save changeset to mapping file
517 cs = hex(node)
518 self.map_set(commit.id, cs)
519
520 ## PACK UPLOADING AND FETCHING
400 521
401 def upload_pack(self, remote_name): 522 def upload_pack(self, remote_name):
402 client, path = self.get_transport_and_path(remote_name) 523 client, path = self.get_transport_and_path(remote_name)
403 changed = self.get_changed_refs 524 changed = self.get_changed_refs
404 genpack = self.generate_pack_contents 525 genpack = self.generate_pack_contents
513 else: 634 else:
514 self.ui.status(_("nothing new on the server\n")) 635 self.ui.status(_("nothing new on the server\n"))
515 return refs 636 return refs
516 finally: 637 finally:
517 f.close() 638 f.close()
639
640 ## REFERENCES HANDLING
641
642 def update_references(self):
643 try:
644 # We only care about bookmarks of the form 'name',
645 # not 'remote/name'.
646 def is_local_ref(item): return item[0].count('/') == 0
647 bms = bookmarks.parse(self.repo)
648 bms = dict(filter(is_local_ref, bms.items()))
649
650 # Create a local Git branch name for each
651 # Mercurial bookmark.
652 for key in bms:
653 hg_sha = hex(bms[key])
654 git_sha = self.map_git_get(hg_sha)
655 self.git.set_ref('refs/heads/' + key, git_sha)
656 except AttributeError:
657 # No bookmarks extension
658 pass
659
660 c = self.map_git_get(hex(self.repo.changelog.tip()))
661 self.git.set_ref(self.exportbranch, c)
662
663 def export_hg_tags(self):
664 for tag, sha in self.repo.tags().iteritems():
665 if tag[-3:] == '^{}':
666 continue
667 if tag == 'tip':
668 continue
669 self.git.set_ref('refs/tags/' + tag, self.map_git_get(hex(sha)))
670
671 # Make sure there's a refs/remotes/remote_name/name
672 # for every refs/heads/name
673 def update_remote_references(self, remote_name):
674 self.git.set_remote_refs(self.local_heads(), remote_name)
675
676 def local_heads(self):
677 def is_local_head(item): return item[0].startswith('refs/heads')
678 refs = self.git.get_refs()
679 return dict(filter(is_local_head, refs.items()))
518 680
519 # take refs just fetched, add local tags for all tags not in .hgtags 681 # take refs just fetched, add local tags for all tags not in .hgtags
520 def import_local_tags(self, refs): 682 def import_local_tags(self, refs):
521 keys = refs.keys() 683 keys = refs.keys()
522 if not keys: 684 if not keys:
539 if isinstance (obj, Commit): 701 if isinstance (obj, Commit):
540 sha = self.map_hg_get(obj_sha) 702 sha = self.map_hg_get(obj_sha)
541 if sha: 703 if sha:
542 self.repo.tag(ref_name, hex_to_sha(sha), '', True, None, None) 704 self.repo.tag(ref_name, hex_to_sha(sha), '', True, None, None)
543 705
544 def import_git_objects(self, remote_name=None, refs=None):
545 self.ui.status(_("importing Git objects into Hg\n"))
546 # import heads and fetched tags as remote references
547 todo = []
548 done = set()
549 convert_list = {}
550
551 # get a list of all the head shas
552 if refs:
553 for head, sha in refs.iteritems():
554 todo.append(sha)
555 else:
556 if remote_name:
557 todo = self.git.remote_refs(remote_name).values()[:]
558 elif self.importbranch:
559 branches = self.importbranch.split(',')
560 todo = [self.git.ref(i.strip()) for i in branches]
561 else:
562 todo = self.git.heads().values()[:]
563
564 # traverse the heads getting a list of all the unique commits
565 while todo:
566 sha = todo.pop()
567 assert isinstance(sha, str)
568 if sha in done:
569 continue
570 done.add(sha)
571 obj = self.git.get_object(sha)
572 if isinstance (obj, Commit):
573 convert_list[sha] = obj
574 todo.extend([p for p in obj.parents if p not in done])
575 if isinstance(obj, Tag):
576 (obj_type, obj_sha) = obj.get_object()
577 obj = self.git.get_object(obj_sha)
578 if isinstance (obj, Commit):
579 convert_list[sha] = obj
580 todo.extend([p for p in obj.parents if p not in done])
581
582 # sort the commits
583 commits = toposort.TopoSort(convert_list).items()
584
585 # import each of the commits, oldest first
586 total = len(commits)
587 magnitude = int(math.log(total, 10)) + 1 if total else 1
588 for i, csha in enumerate(commits):
589 if i%100 == 0:
590 self.ui.status(_("at: %*d/%d\n") % (magnitude, i, total))
591 commit = convert_list[csha]
592 if not self.map_hg_get(csha): # it's already here
593 self.import_git_commit(commit)
594
595 self.update_hg_bookmarks(remote_name)
596
597 def update_hg_bookmarks(self, remote_name): 706 def update_hg_bookmarks(self, remote_name):
598 try: 707 try:
599 bms = bookmarks.parse(self.repo) 708 bms = bookmarks.parse(self.repo)
600 if remote_name: 709 if remote_name:
601 heads = self.git.remote_refs(remote_name) 710 heads = self.git.remote_refs(remote_name)
616 bookmarks.write(self.repo, bms) 725 bookmarks.write(self.repo, bms)
617 726
618 except AttributeError: 727 except AttributeError:
619 self.ui.warn(_('creating bookmarks failed, do you have' 728 self.ui.warn(_('creating bookmarks failed, do you have'
620 ' bookmarks enabled?\n')) 729 ' bookmarks enabled?\n'))
730
731 ## UTILITY FUNCTIONS
621 732
622 def convert_git_int_mode(self, mode): 733 def convert_git_int_mode(self, mode):
623 # TODO : make these into constants 734 # TODO : make these into constants
624 convert = { 735 convert = {
625 0100644: '', 736 0100644: '',
656 if command == 'extra': 767 if command == 'extra':
657 before, after = data.split(" : ", 1) 768 before, after = data.split(" : ", 1)
658 extra[before] = urllib.unquote(after) 769 extra[before] = urllib.unquote(after)
659 return (message, renames, branch, files, extra) 770 return (message, renames, branch, files, extra)
660 771
661 def import_git_commit(self, commit):
662 self.ui.debug(_("importing: %s\n") % commit.id)
663 # TODO : Do something less coarse-grained than try/except on the
664 # get_file call for removed files
665
666 (strip_message, hg_renames, hg_branch, force_files, extra) = self.extract_hg_metadata(commit.message)
667
668 # get a list of the changed, added, removed files
669 files = self.git.get_files_changed(commit)
670
671 date = (commit.author_time, -commit.author_timezone)
672 text = strip_message
673
674 def getfilectx(repo, memctx, f):
675 try:
676 (mode, sha, data) = self.git.get_file(commit, f)
677 e = self.convert_git_int_mode(mode)
678 except TypeError:
679 raise IOError()
680 if f in hg_renames:
681 copied_path = hg_renames[f]
682 else:
683 copied_path = None
684 return context.memfilectx(f, data, 'l' in e, 'x' in e, copied_path)
685
686 gparents = map(self.map_hg_get, commit.parents)
687 p1, p2 = (nullid, nullid)
688 octopus = False
689
690 if len(gparents) > 1:
691 # merge, possibly octopus
692 def commit_octopus(p1, p2):
693 ctx = context.memctx(self.repo, (p1, p2), text, files, getfilectx,
694 commit.author, date, {'hg-git': 'octopus'})
695 return hex(self.repo.commitctx(ctx))
696
697 octopus = len(gparents) > 2
698 p2 = gparents.pop()
699 p1 = gparents.pop()
700 while len(gparents) > 0:
701 p2 = commit_octopus(p1, p2)
702 p1 = gparents.pop()
703 else:
704 if gparents:
705 p1 = gparents.pop()
706
707 files = list(set(files))
708
709 pa = None
710 if not (p2 == nullid):
711 node1 = self.repo.changectx(p1)
712 node2 = self.repo.changectx(p2)
713 pa = node1.ancestor(node2)
714
715 author = commit.author
716
717 # convert extra data back to the end
718 if ' ext:' in commit.author:
719 regex = re.compile('^(.*?)\ ext:\((.*)\) <(.*)\>$')
720 m = regex.match(commit.author)
721 if m:
722 name = m.group(1)
723 ex = urllib.unquote(m.group(2))
724 email = m.group(3)
725 author = name + ' <' + email + '>' + ex
726
727 if ' <none@none>' in commit.author:
728 author = commit.author[:-12]
729
730 # if named branch, add to extra
731 if hg_branch:
732 extra['branch'] = hg_branch
733
734 # if committer is different than author, add it to extra
735 if not commit._author_raw == commit._committer_raw:
736 extra['committer'] = "%s %d %d" % (commit.committer, commit.commit_time, -commit.commit_timezone)
737
738 if commit._encoding:
739 extra['encoding'] = commit._encoding
740
741 if hg_branch:
742 extra['branch'] = hg_branch
743
744 if octopus:
745 extra['hg-git'] ='octopus-done'
746
747 ctx = context.memctx(self.repo, (p1, p2), text, files, getfilectx,
748 author, date, extra)
749
750 node = self.repo.commit_import_ctx(ctx, pa, force_files)
751
752 # save changeset to mapping file
753 cs = hex(node)
754 self.map_set(commit.id, cs)
755
756 def check_bookmarks(self): 772 def check_bookmarks(self):
757 if self.ui.config('extensions', 'hgext.bookmarks') is not None: 773 if self.ui.config('extensions', 'hgext.bookmarks') is not None:
758 self.ui.warn("YOU NEED TO SETUP BOOKMARKS\n") 774 self.ui.warn("YOU NEED TO SETUP BOOKMARKS\n")
759 775
760 def get_transport_and_path(self, uri): 776 def get_transport_and_path(self, uri):
767 else: 783 else:
768 host, path = uri[len(handler):].split("/", 1) 784 host, path = uri[len(handler):].split("/", 1)
769 return transport(host), '/' + path 785 return transport(host), '/' + path
770 # if its not git or git+ssh, try a local url.. 786 # if its not git or git+ssh, try a local url..
771 return SubprocessGitClient(), uri 787 return SubprocessGitClient(), uri
772
773 def clear(self):
774 mapfile = self.repo.join(self.mapfile)
775 if os.path.exists(self.gitdir):
776 for root, dirs, files in os.walk(self.gitdir, topdown=False):
777 for name in files:
778 os.remove(os.path.join(root, name))
779 for name in dirs:
780 os.rmdir(os.path.join(root, name))
781 os.rmdir(self.gitdir)
782 if os.path.exists(mapfile):
783 os.remove(mapfile)