Mercurial > hg > hg-git
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) |