632 |
632 |
633 def _githavelocally(self, revision): |
633 def _githavelocally(self, revision): |
634 out, code = self._gitdir(['cat-file', '-e', revision]) |
634 out, code = self._gitdir(['cat-file', '-e', revision]) |
635 return code == 0 |
635 return code == 0 |
636 |
636 |
|
637 def _gitbranchmap(self): |
|
638 'returns the current branch and a map from git revision to branch[es]' |
|
639 bm = {} |
|
640 redirects = {} |
|
641 current = None |
|
642 out = self._gitcommand(['branch', '-a', '--no-color', |
|
643 '--verbose', '--abbrev=40']) |
|
644 for line in out.split('\n'): |
|
645 if not line: |
|
646 continue |
|
647 if line[2:].startswith('(no branch)'): |
|
648 continue |
|
649 branch, revision = line[2:].split()[:2] |
|
650 if revision == '->': |
|
651 continue # ignore remote/HEAD redirects |
|
652 if line[0] == '*': |
|
653 current = branch |
|
654 bm.setdefault(revision, []).append(branch) |
|
655 return current, bm |
|
656 |
637 def _fetch(self, source, revision): |
657 def _fetch(self, source, revision): |
638 if not os.path.exists('%s/.git' % self._path): |
658 if not os.path.exists('%s/.git' % self._path): |
639 self._ui.status(_('cloning subrepo %s\n') % self._relpath) |
659 self._ui.status(_('cloning subrepo %s\n') % self._relpath) |
640 self._gitnodir(['clone', source, self._path]) |
660 self._gitnodir(['clone', source, self._path]) |
641 if self._githavelocally(revision): |
661 if self._githavelocally(revision): |
656 return bool(changed.strip()) |
676 return bool(changed.strip()) |
657 |
677 |
658 def get(self, state): |
678 def get(self, state): |
659 source, revision, kind = state |
679 source, revision, kind = state |
660 self._fetch(source, revision) |
680 self._fetch(source, revision) |
661 if self._gitstate() != revision: |
681 if self._gitstate() == revision: |
|
682 return |
|
683 current, bm = self._gitbranchmap() |
|
684 if revision not in bm: |
|
685 # no branch to checkout, check it out with no branch |
662 self._ui.warn(_('checking out detached HEAD in subrepo %s\n') % |
686 self._ui.warn(_('checking out detached HEAD in subrepo %s\n') % |
663 self._relpath) |
687 self._relpath) |
664 self._ui.warn(_('check out a git branch if you intend ' |
688 self._ui.warn(_('check out a git branch if you intend ' |
665 'to make changes\n')) |
689 'to make changes\n')) |
666 self._gitcommand(['checkout', '-q', revision]) |
690 self._gitcommand(['checkout', '-q', revision]) |
|
691 return |
|
692 branches = bm[revision] |
|
693 firstlocalbranch = None |
|
694 for b in branches: |
|
695 if b == 'master': |
|
696 # master trumps all other branches |
|
697 self._gitcommand(['checkout', 'master']) |
|
698 return |
|
699 if not firstlocalbranch and not b.startswith('remotes/'): |
|
700 firstlocalbranch = b |
|
701 if firstlocalbranch: |
|
702 self._gitcommand(['checkout', firstlocalbranch]) |
|
703 else: |
|
704 remote = branches[0] |
|
705 local = remote.split('/')[-1] |
|
706 self._gitcommand(['checkout', '-b', local, remote]) |
667 |
707 |
668 def commit(self, text, user, date): |
708 def commit(self, text, user, date): |
669 cmd = ['commit', '-a', '-m', text] |
709 cmd = ['commit', '-a', '-m', text] |
670 if user: |
710 if user: |
671 cmd += ['--author', user] |
711 cmd += ['--author', user] |
690 |
730 |
691 def push(self, force): |
731 def push(self, force): |
692 cmd = ['push'] |
732 cmd = ['push'] |
693 if force: |
733 if force: |
694 cmd.append('--force') |
734 cmd.append('--force') |
695 # as subrepos have no notion of "where to push to" we |
735 # push the currently checked out branch |
696 # assume origin master. This is git's default |
736 current, bm = self._gitbranchmap() |
697 self._gitcommand(cmd + ['origin', 'master', '-q']) |
737 if current: |
698 return True |
738 self._gitcommand(cmd + ['origin', current, '-q']) |
|
739 return True |
|
740 else: |
|
741 self._ui.warn(_('no branch checked out in subrepo %s\n' |
|
742 'nothing to push') % self._relpath) |
|
743 return False |
699 |
744 |
700 types = { |
745 types = { |
701 'hg': hgsubrepo, |
746 'hg': hgsubrepo, |
702 'svn': svnsubrepo, |
747 'svn': svnsubrepo, |
703 'git': gitsubrepo, |
748 'git': gitsubrepo, |