summaryrefslogtreecommitdiffstats
path: root/project.py
diff options
context:
space:
mode:
Diffstat (limited to 'project.py')
-rw-r--r--project.py164
1 files changed, 120 insertions, 44 deletions
diff --git a/project.py b/project.py
index 22e4a5d6..dec21ab1 100644
--- a/project.py
+++ b/project.py
@@ -36,6 +36,12 @@ from trace import IsTrace, Trace
36 36
37from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M 37from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M
38 38
39from pyversion import is_python3
40if not is_python3():
41 # pylint:disable=W0622
42 input = raw_input
43 # pylint:enable=W0622
44
39def _lwrite(path, content): 45def _lwrite(path, content):
40 lock = '%s.lock' % path 46 lock = '%s.lock' % path
41 47
@@ -78,7 +84,7 @@ def _ProjectHooks():
78 if _project_hook_list is None: 84 if _project_hook_list is None:
79 d = os.path.abspath(os.path.dirname(__file__)) 85 d = os.path.abspath(os.path.dirname(__file__))
80 d = os.path.join(d , 'hooks') 86 d = os.path.join(d , 'hooks')
81 _project_hook_list = map(lambda x: os.path.join(d, x), os.listdir(d)) 87 _project_hook_list = [os.path.join(d, x) for x in os.listdir(d)]
82 return _project_hook_list 88 return _project_hook_list
83 89
84 90
@@ -151,11 +157,12 @@ class ReviewableBranch(object):
151 R_HEADS + self.name, 157 R_HEADS + self.name,
152 '--') 158 '--')
153 159
154 def UploadForReview(self, people, auto_topic=False, draft=False): 160 def UploadForReview(self, people, auto_topic=False, draft=False, dest_branch=None):
155 self.project.UploadForReview(self.name, 161 self.project.UploadForReview(self.name,
156 people, 162 people,
157 auto_topic=auto_topic, 163 auto_topic=auto_topic,
158 draft=draft) 164 draft=draft,
165 dest_branch=dest_branch)
159 166
160 def GetPublishedRefs(self): 167 def GetPublishedRefs(self):
161 refs = {} 168 refs = {}
@@ -361,7 +368,7 @@ class RepoHook(object):
361 'Do you want to allow this script to run ' 368 'Do you want to allow this script to run '
362 '(yes/yes-never-ask-again/NO)? ') % ( 369 '(yes/yes-never-ask-again/NO)? ') % (
363 self._GetMustVerb(), self._script_fullpath) 370 self._GetMustVerb(), self._script_fullpath)
364 response = raw_input(prompt).lower() 371 response = input(prompt).lower()
365 print() 372 print()
366 373
367 # User is doing a one-time approval. 374 # User is doing a one-time approval.
@@ -488,9 +495,11 @@ class Project(object):
488 groups = None, 495 groups = None,
489 sync_c = False, 496 sync_c = False,
490 sync_s = False, 497 sync_s = False,
498 clone_depth = None,
491 upstream = None, 499 upstream = None,
492 parent = None, 500 parent = None,
493 is_derived = False): 501 is_derived = False,
502 dest_branch = None):
494 """Init a Project object. 503 """Init a Project object.
495 504
496 Args: 505 Args:
@@ -510,6 +519,7 @@ class Project(object):
510 parent: The parent Project object. 519 parent: The parent Project object.
511 is_derived: False if the project was explicitly defined in the manifest; 520 is_derived: False if the project was explicitly defined in the manifest;
512 True if the project is a discovered submodule. 521 True if the project is a discovered submodule.
522 dest_branch: The branch to which to push changes for review by default.
513 """ 523 """
514 self.manifest = manifest 524 self.manifest = manifest
515 self.name = name 525 self.name = name
@@ -533,6 +543,7 @@ class Project(object):
533 self.groups = groups 543 self.groups = groups
534 self.sync_c = sync_c 544 self.sync_c = sync_c
535 self.sync_s = sync_s 545 self.sync_s = sync_s
546 self.clone_depth = clone_depth
536 self.upstream = upstream 547 self.upstream = upstream
537 self.parent = parent 548 self.parent = parent
538 self.is_derived = is_derived 549 self.is_derived = is_derived
@@ -551,6 +562,7 @@ class Project(object):
551 self.work_git = None 562 self.work_git = None
552 self.bare_git = self._GitGetByExec(self, bare=True) 563 self.bare_git = self._GitGetByExec(self, bare=True)
553 self.bare_ref = GitRefs(gitdir) 564 self.bare_ref = GitRefs(gitdir)
565 self.dest_branch = dest_branch
554 566
555 # This will be filled in if a project is later identified to be the 567 # This will be filled in if a project is later identified to be the
556 # project containing repo hooks. 568 # project containing repo hooks.
@@ -644,7 +656,7 @@ class Project(object):
644 all_refs = self._allrefs 656 all_refs = self._allrefs
645 heads = {} 657 heads = {}
646 658
647 for name, ref_id in all_refs.iteritems(): 659 for name, ref_id in all_refs.items():
648 if name.startswith(R_HEADS): 660 if name.startswith(R_HEADS):
649 name = name[len(R_HEADS):] 661 name = name[len(R_HEADS):]
650 b = self.GetBranch(name) 662 b = self.GetBranch(name)
@@ -653,7 +665,7 @@ class Project(object):
653 b.revision = ref_id 665 b.revision = ref_id
654 heads[name] = b 666 heads[name] = b
655 667
656 for name, ref_id in all_refs.iteritems(): 668 for name, ref_id in all_refs.items():
657 if name.startswith(R_PUB): 669 if name.startswith(R_PUB):
658 name = name[len(R_PUB):] 670 name = name[len(R_PUB):]
659 b = heads.get(name) 671 b = heads.get(name)
@@ -672,9 +684,14 @@ class Project(object):
672 project_groups: "all,group1,group2" 684 project_groups: "all,group1,group2"
673 manifest_groups: "-group1,group2" 685 manifest_groups: "-group1,group2"
674 the project will be matched. 686 the project will be matched.
687
688 The special manifest group "default" will match any project that
689 does not have the special project group "notdefault"
675 """ 690 """
676 expanded_manifest_groups = manifest_groups or ['all', '-notdefault'] 691 expanded_manifest_groups = manifest_groups or ['default']
677 expanded_project_groups = ['all'] + (self.groups or []) 692 expanded_project_groups = ['all'] + (self.groups or [])
693 if not 'notdefault' in expanded_project_groups:
694 expanded_project_groups += ['default']
678 695
679 matched = False 696 matched = False
680 for group in expanded_manifest_groups: 697 for group in expanded_manifest_groups:
@@ -754,10 +771,7 @@ class Project(object):
754 paths.extend(df.keys()) 771 paths.extend(df.keys())
755 paths.extend(do) 772 paths.extend(do)
756 773
757 paths = list(set(paths)) 774 for p in sorted(set(paths)):
758 paths.sort()
759
760 for p in paths:
761 try: 775 try:
762 i = di[p] 776 i = di[p]
763 except KeyError: 777 except KeyError:
@@ -849,13 +863,13 @@ class Project(object):
849 all_refs = self._allrefs 863 all_refs = self._allrefs
850 heads = set() 864 heads = set()
851 canrm = {} 865 canrm = {}
852 for name, ref_id in all_refs.iteritems(): 866 for name, ref_id in all_refs.items():
853 if name.startswith(R_HEADS): 867 if name.startswith(R_HEADS):
854 heads.add(name) 868 heads.add(name)
855 elif name.startswith(R_PUB): 869 elif name.startswith(R_PUB):
856 canrm[name] = ref_id 870 canrm[name] = ref_id
857 871
858 for name, ref_id in canrm.iteritems(): 872 for name, ref_id in canrm.items():
859 n = name[len(R_PUB):] 873 n = name[len(R_PUB):]
860 if R_HEADS + n not in heads: 874 if R_HEADS + n not in heads:
861 self.bare_git.DeleteRef(name, ref_id) 875 self.bare_git.DeleteRef(name, ref_id)
@@ -866,14 +880,14 @@ class Project(object):
866 heads = {} 880 heads = {}
867 pubed = {} 881 pubed = {}
868 882
869 for name, ref_id in self._allrefs.iteritems(): 883 for name, ref_id in self._allrefs.items():
870 if name.startswith(R_HEADS): 884 if name.startswith(R_HEADS):
871 heads[name[len(R_HEADS):]] = ref_id 885 heads[name[len(R_HEADS):]] = ref_id
872 elif name.startswith(R_PUB): 886 elif name.startswith(R_PUB):
873 pubed[name[len(R_PUB):]] = ref_id 887 pubed[name[len(R_PUB):]] = ref_id
874 888
875 ready = [] 889 ready = []
876 for branch, ref_id in heads.iteritems(): 890 for branch, ref_id in heads.items():
877 if branch in pubed and pubed[branch] == ref_id: 891 if branch in pubed and pubed[branch] == ref_id:
878 continue 892 continue
879 if selected_branch and branch != selected_branch: 893 if selected_branch and branch != selected_branch:
@@ -898,7 +912,8 @@ class Project(object):
898 def UploadForReview(self, branch=None, 912 def UploadForReview(self, branch=None,
899 people=([],[]), 913 people=([],[]),
900 auto_topic=False, 914 auto_topic=False,
901 draft=False): 915 draft=False,
916 dest_branch=None):
902 """Uploads the named branch for code review. 917 """Uploads the named branch for code review.
903 """ 918 """
904 if branch is None: 919 if branch is None:
@@ -912,7 +927,10 @@ class Project(object):
912 if not branch.remote.review: 927 if not branch.remote.review:
913 raise GitError('remote %s has no review url' % branch.remote.name) 928 raise GitError('remote %s has no review url' % branch.remote.name)
914 929
915 dest_branch = branch.merge 930 if dest_branch is None:
931 dest_branch = self.dest_branch
932 if dest_branch is None:
933 dest_branch = branch.merge
916 if not dest_branch.startswith(R_HEADS): 934 if not dest_branch.startswith(R_HEADS):
917 dest_branch = R_HEADS + dest_branch 935 dest_branch = R_HEADS + dest_branch
918 936
@@ -977,6 +995,8 @@ class Project(object):
977 is_new = not self.Exists 995 is_new = not self.Exists
978 if is_new: 996 if is_new:
979 self._InitGitDir() 997 self._InitGitDir()
998 else:
999 self._UpdateHooks()
980 self._InitRemote() 1000 self._InitRemote()
981 1001
982 if is_new: 1002 if is_new:
@@ -1216,7 +1236,6 @@ class Project(object):
1216 cmd = ['fetch', remote.name] 1236 cmd = ['fetch', remote.name]
1217 cmd.append('refs/changes/%2.2d/%d/%d' \ 1237 cmd.append('refs/changes/%2.2d/%d/%d' \
1218 % (change_id % 100, change_id, patch_id)) 1238 % (change_id % 100, change_id, patch_id))
1219 cmd.extend(map(str, remote.fetch))
1220 if GitCommand(self, cmd, bare=True).Wait() != 0: 1239 if GitCommand(self, cmd, bare=True).Wait() != 0:
1221 return None 1240 return None
1222 return DownloadedChange(self, 1241 return DownloadedChange(self,
@@ -1605,7 +1624,7 @@ class Project(object):
1605 ids = set(all_refs.values()) 1624 ids = set(all_refs.values())
1606 tmp = set() 1625 tmp = set()
1607 1626
1608 for r, ref_id in GitRefs(ref_dir).all.iteritems(): 1627 for r, ref_id in GitRefs(ref_dir).all.items():
1609 if r not in all_refs: 1628 if r not in all_refs:
1610 if r.startswith(R_TAGS) or remote.WritesTo(r): 1629 if r.startswith(R_TAGS) or remote.WritesTo(r):
1611 all_refs[r] = ref_id 1630 all_refs[r] = ref_id
@@ -1620,13 +1639,10 @@ class Project(object):
1620 ids.add(ref_id) 1639 ids.add(ref_id)
1621 tmp.add(r) 1640 tmp.add(r)
1622 1641
1623 ref_names = list(all_refs.keys())
1624 ref_names.sort()
1625
1626 tmp_packed = '' 1642 tmp_packed = ''
1627 old_packed = '' 1643 old_packed = ''
1628 1644
1629 for r in ref_names: 1645 for r in sorted(all_refs):
1630 line = '%s %s\n' % (all_refs[r], r) 1646 line = '%s %s\n' % (all_refs[r], r)
1631 tmp_packed += line 1647 tmp_packed += line
1632 if r not in tmp: 1648 if r not in tmp:
@@ -1640,7 +1656,10 @@ class Project(object):
1640 1656
1641 # The --depth option only affects the initial fetch; after that we'll do 1657 # The --depth option only affects the initial fetch; after that we'll do
1642 # full fetches of changes. 1658 # full fetches of changes.
1643 depth = self.manifest.manifestProject.config.GetString('repo.depth') 1659 if self.clone_depth:
1660 depth = self.clone_depth
1661 else:
1662 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1644 if depth and initial: 1663 if depth and initial:
1645 cmd.append('--depth=%s' % depth) 1664 cmd.append('--depth=%s' % depth)
1646 1665
@@ -1652,11 +1671,13 @@ class Project(object):
1652 1671
1653 if not current_branch_only: 1672 if not current_branch_only:
1654 # Fetch whole repo 1673 # Fetch whole repo
1655 if no_tags: 1674 # If using depth then we should not get all the tags since they may
1675 # be outside of the depth.
1676 if no_tags or depth:
1656 cmd.append('--no-tags') 1677 cmd.append('--no-tags')
1657 else: 1678 else:
1658 cmd.append('--tags') 1679 cmd.append('--tags')
1659 cmd.append((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')) 1680 cmd.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
1660 elif tag_name is not None: 1681 elif tag_name is not None:
1661 cmd.append('tag') 1682 cmd.append('tag')
1662 cmd.append(tag_name) 1683 cmd.append(tag_name)
@@ -1666,7 +1687,7 @@ class Project(object):
1666 branch = self.upstream 1687 branch = self.upstream
1667 if branch.startswith(R_HEADS): 1688 if branch.startswith(R_HEADS):
1668 branch = branch[len(R_HEADS):] 1689 branch = branch[len(R_HEADS):]
1669 cmd.append((u'+refs/heads/%s:' % branch) + remote.ToLocal('refs/heads/%s' % branch)) 1690 cmd.append(str((u'+refs/heads/%s:' % branch) + remote.ToLocal('refs/heads/%s' % branch)))
1670 1691
1671 ok = False 1692 ok = False
1672 for _i in range(2): 1693 for _i in range(2):
@@ -1700,15 +1721,14 @@ class Project(object):
1700 return ok 1721 return ok
1701 1722
1702 def _ApplyCloneBundle(self, initial=False, quiet=False): 1723 def _ApplyCloneBundle(self, initial=False, quiet=False):
1703 if initial and self.manifest.manifestProject.config.GetString('repo.depth'): 1724 if initial and (self.manifest.manifestProject.config.GetString('repo.depth') or self.clone_depth):
1704 return False 1725 return False
1705 1726
1706 remote = self.GetRemote(self.remote.name) 1727 remote = self.GetRemote(self.remote.name)
1707 bundle_url = remote.url + '/clone.bundle' 1728 bundle_url = remote.url + '/clone.bundle'
1708 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url) 1729 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
1709 if GetSchemeFromUrl(bundle_url) in ('persistent-http', 'persistent-https'): 1730 if GetSchemeFromUrl(bundle_url) not in (
1710 bundle_url = bundle_url[len('persistent-'):] 1731 'http', 'https', 'persistent-http', 'persistent-https'):
1711 if GetSchemeFromUrl(bundle_url) not in ('http', 'https'):
1712 return False 1732 return False
1713 1733
1714 bundle_dst = os.path.join(self.gitdir, 'clone.bundle') 1734 bundle_dst = os.path.join(self.gitdir, 'clone.bundle')
@@ -1757,9 +1777,11 @@ class Project(object):
1757 os.remove(tmpPath) 1777 os.remove(tmpPath)
1758 if 'http_proxy' in os.environ and 'darwin' == sys.platform: 1778 if 'http_proxy' in os.environ and 'darwin' == sys.platform:
1759 cmd += ['--proxy', os.environ['http_proxy']] 1779 cmd += ['--proxy', os.environ['http_proxy']]
1760 cookiefile = GitConfig.ForUser().GetString('http.cookiefile') 1780 cookiefile = self._GetBundleCookieFile(srcUrl)
1761 if cookiefile: 1781 if cookiefile:
1762 cmd += ['--cookie', cookiefile] 1782 cmd += ['--cookie', cookiefile]
1783 if srcUrl.startswith('persistent-'):
1784 srcUrl = srcUrl[len('persistent-'):]
1763 cmd += [srcUrl] 1785 cmd += [srcUrl]
1764 1786
1765 if IsTrace(): 1787 if IsTrace():
@@ -1782,7 +1804,7 @@ class Project(object):
1782 return False 1804 return False
1783 1805
1784 if os.path.exists(tmpPath): 1806 if os.path.exists(tmpPath):
1785 if curlret == 0 and os.stat(tmpPath).st_size > 16: 1807 if curlret == 0 and self._IsValidBundle(tmpPath):
1786 os.rename(tmpPath, dstPath) 1808 os.rename(tmpPath, dstPath)
1787 return True 1809 return True
1788 else: 1810 else:
@@ -1791,6 +1813,46 @@ class Project(object):
1791 else: 1813 else:
1792 return False 1814 return False
1793 1815
1816 def _IsValidBundle(self, path):
1817 try:
1818 with open(path) as f:
1819 if f.read(16) == '# v2 git bundle\n':
1820 return True
1821 else:
1822 print("Invalid clone.bundle file; ignoring.", file=sys.stderr)
1823 return False
1824 except OSError:
1825 return False
1826
1827 def _GetBundleCookieFile(self, url):
1828 if url.startswith('persistent-'):
1829 try:
1830 p = subprocess.Popen(
1831 ['git-remote-persistent-https', '-print_config', url],
1832 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
1833 stderr=subprocess.PIPE)
1834 p.stdin.close() # Tell subprocess it's ok to close.
1835 prefix = 'http.cookiefile='
1836 cookiefile = None
1837 for line in p.stdout:
1838 line = line.strip()
1839 if line.startswith(prefix):
1840 cookiefile = line[len(prefix):]
1841 break
1842 if p.wait():
1843 line = iter(p.stderr).next()
1844 if ' -print_config' in line:
1845 pass # Persistent proxy doesn't support -print_config.
1846 else:
1847 print(line + p.stderr.read(), file=sys.stderr)
1848 if cookiefile:
1849 return cookiefile
1850 except OSError as e:
1851 if e.errno == errno.ENOENT:
1852 pass # No persistent proxy.
1853 raise
1854 return GitConfig.ForUser().GetString('http.cookiefile')
1855
1794 def _Checkout(self, rev, quiet=False): 1856 def _Checkout(self, rev, quiet=False):
1795 cmd = ['checkout'] 1857 cmd = ['checkout']
1796 if quiet: 1858 if quiet:
@@ -1841,16 +1903,17 @@ class Project(object):
1841 if GitCommand(self, cmd).Wait() != 0: 1903 if GitCommand(self, cmd).Wait() != 0:
1842 raise GitError('%s merge %s ' % (self.name, head)) 1904 raise GitError('%s merge %s ' % (self.name, head))
1843 1905
1844 def _InitGitDir(self): 1906 def _InitGitDir(self, mirror_git=None):
1845 if not os.path.exists(self.gitdir): 1907 if not os.path.exists(self.gitdir):
1846 os.makedirs(self.gitdir) 1908 os.makedirs(self.gitdir)
1847 self.bare_git.init() 1909 self.bare_git.init()
1848 1910
1849 mp = self.manifest.manifestProject 1911 mp = self.manifest.manifestProject
1850 ref_dir = mp.config.GetString('repo.reference') 1912 ref_dir = mp.config.GetString('repo.reference') or ''
1851 1913
1852 if ref_dir: 1914 if ref_dir or mirror_git:
1853 mirror_git = os.path.join(ref_dir, self.name + '.git') 1915 if not mirror_git:
1916 mirror_git = os.path.join(ref_dir, self.name + '.git')
1854 repo_git = os.path.join(ref_dir, '.repo', 'projects', 1917 repo_git = os.path.join(ref_dir, '.repo', 'projects',
1855 self.relpath + '.git') 1918 self.relpath + '.git')
1856 1919
@@ -1867,11 +1930,21 @@ class Project(object):
1867 _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'), 1930 _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'),
1868 os.path.join(ref_dir, 'objects') + '\n') 1931 os.path.join(ref_dir, 'objects') + '\n')
1869 1932
1933 self._UpdateHooks()
1934
1935 m = self.manifest.manifestProject.config
1936 for key in ['user.name', 'user.email']:
1937 if m.Has(key, include_defaults = False):
1938 self.config.SetString(key, m.GetString(key))
1870 if self.manifest.IsMirror: 1939 if self.manifest.IsMirror:
1871 self.config.SetString('core.bare', 'true') 1940 self.config.SetString('core.bare', 'true')
1872 else: 1941 else:
1873 self.config.SetString('core.bare', None) 1942 self.config.SetString('core.bare', None)
1874 1943
1944 def _UpdateHooks(self):
1945 if os.path.exists(self.gitdir):
1946 # Always recreate hooks since they can have been changed
1947 # since the latest update.
1875 hooks = self._gitdir_path('hooks') 1948 hooks = self._gitdir_path('hooks')
1876 try: 1949 try:
1877 to_rm = os.listdir(hooks) 1950 to_rm = os.listdir(hooks)
@@ -1881,11 +1954,6 @@ class Project(object):
1881 os.remove(os.path.join(hooks, old_hook)) 1954 os.remove(os.path.join(hooks, old_hook))
1882 self._InitHooks() 1955 self._InitHooks()
1883 1956
1884 m = self.manifest.manifestProject.config
1885 for key in ['user.name', 'user.email']:
1886 if m.Has(key, include_defaults = False):
1887 self.config.SetString(key, m.GetString(key))
1888
1889 def _InitHooks(self): 1957 def _InitHooks(self):
1890 hooks = self._gitdir_path('hooks') 1958 hooks = self._gitdir_path('hooks')
1891 if not os.path.exists(hooks): 1959 if not os.path.exists(hooks):
@@ -2092,6 +2160,10 @@ class Project(object):
2092 line = fd.read() 2160 line = fd.read()
2093 finally: 2161 finally:
2094 fd.close() 2162 fd.close()
2163 try:
2164 line = line.decode()
2165 except AttributeError:
2166 pass
2095 if line.startswith('ref: '): 2167 if line.startswith('ref: '):
2096 return line[5:-1] 2168 return line[5:-1]
2097 return line[:-1] 2169 return line[:-1]
@@ -2185,7 +2257,7 @@ class Project(object):
2185 if not git_require((1, 7, 2)): 2257 if not git_require((1, 7, 2)):
2186 raise ValueError('cannot set config on command line for %s()' 2258 raise ValueError('cannot set config on command line for %s()'
2187 % name) 2259 % name)
2188 for k, v in config.iteritems(): 2260 for k, v in config.items():
2189 cmdv.append('-c') 2261 cmdv.append('-c')
2190 cmdv.append('%s=%s' % (k, v)) 2262 cmdv.append('%s=%s' % (k, v))
2191 cmdv.append(name) 2263 cmdv.append(name)
@@ -2201,6 +2273,10 @@ class Project(object):
2201 name, 2273 name,
2202 p.stderr)) 2274 p.stderr))
2203 r = p.stdout 2275 r = p.stdout
2276 try:
2277 r = r.decode('utf-8')
2278 except AttributeError:
2279 pass
2204 if r.endswith('\n') and r.index('\n') == len(r) - 1: 2280 if r.endswith('\n') and r.index('\n') == len(r) - 1:
2205 return r[:-1] 2281 return r[:-1]
2206 return r 2282 return r