diff options
-rw-r--r-- | command.py | 26 | ||||
-rw-r--r-- | manifest_xml.py | 64 | ||||
-rw-r--r-- | project.py | 118 | ||||
-rw-r--r-- | subcmds/rebase.py | 3 | ||||
-rw-r--r-- | subcmds/sync.py | 54 | ||||
-rw-r--r-- | subcmds/upload.py | 4 |
6 files changed, 190 insertions, 79 deletions
@@ -129,7 +129,7 @@ class Command(object): | |||
129 | def GetProjects(self, args, missing_ok=False, submodules_ok=False): | 129 | def GetProjects(self, args, missing_ok=False, submodules_ok=False): |
130 | """A list of projects that match the arguments. | 130 | """A list of projects that match the arguments. |
131 | """ | 131 | """ |
132 | all_projects = self.manifest.projects | 132 | all_projects_list = self.manifest.projects |
133 | result = [] | 133 | result = [] |
134 | 134 | ||
135 | mp = self.manifest.manifestProject | 135 | mp = self.manifest.manifestProject |
@@ -140,7 +140,6 @@ class Command(object): | |||
140 | groups = [x for x in re.split(r'[,\s]+', groups) if x] | 140 | groups = [x for x in re.split(r'[,\s]+', groups) if x] |
141 | 141 | ||
142 | if not args: | 142 | if not args: |
143 | all_projects_list = list(all_projects.values()) | ||
144 | derived_projects = {} | 143 | derived_projects = {} |
145 | for project in all_projects_list: | 144 | for project in all_projects_list: |
146 | if submodules_ok or project.sync_s: | 145 | if submodules_ok or project.sync_s: |
@@ -152,12 +151,12 @@ class Command(object): | |||
152 | project.MatchesGroups(groups)): | 151 | project.MatchesGroups(groups)): |
153 | result.append(project) | 152 | result.append(project) |
154 | else: | 153 | else: |
155 | self._ResetPathToProjectMap(all_projects.values()) | 154 | self._ResetPathToProjectMap(all_projects_list) |
156 | 155 | ||
157 | for arg in args: | 156 | for arg in args: |
158 | project = all_projects.get(arg) | 157 | projects = self.manifest.GetProjectsWithName(arg) |
159 | 158 | ||
160 | if not project: | 159 | if not projects: |
161 | path = os.path.abspath(arg).replace('\\', '/') | 160 | path = os.path.abspath(arg).replace('\\', '/') |
162 | project = self._GetProjectByPath(path) | 161 | project = self._GetProjectByPath(path) |
163 | 162 | ||
@@ -172,14 +171,19 @@ class Command(object): | |||
172 | if search_again: | 171 | if search_again: |
173 | project = self._GetProjectByPath(path) or project | 172 | project = self._GetProjectByPath(path) or project |
174 | 173 | ||
175 | if not project: | 174 | if project: |
176 | raise NoSuchProjectError(arg) | 175 | projects = [project] |
177 | if not missing_ok and not project.Exists: | 176 | |
177 | if not projects: | ||
178 | raise NoSuchProjectError(arg) | 178 | raise NoSuchProjectError(arg) |
179 | if not project.MatchesGroups(groups): | ||
180 | raise InvalidProjectGroupsError(arg) | ||
181 | 179 | ||
182 | result.append(project) | 180 | for project in projects: |
181 | if not missing_ok and not project.Exists: | ||
182 | raise NoSuchProjectError(arg) | ||
183 | if not project.MatchesGroups(groups): | ||
184 | raise InvalidProjectGroupsError(arg) | ||
185 | |||
186 | result.extend(projects) | ||
183 | 187 | ||
184 | def _getpath(x): | 188 | def _getpath(x): |
185 | return x.relpath | 189 | return x.relpath |
diff --git a/manifest_xml.py b/manifest_xml.py index bdbb1d40..647e89f9 100644 --- a/manifest_xml.py +++ b/manifest_xml.py | |||
@@ -209,8 +209,9 @@ class XmlManifest(object): | |||
209 | root.appendChild(doc.createTextNode('')) | 209 | root.appendChild(doc.createTextNode('')) |
210 | 210 | ||
211 | def output_projects(parent, parent_node, projects): | 211 | def output_projects(parent, parent_node, projects): |
212 | for p in projects: | 212 | for project_name in projects: |
213 | output_project(parent, parent_node, self.projects[p]) | 213 | for project in self._projects[project_name]: |
214 | output_project(parent, parent_node, project) | ||
214 | 215 | ||
215 | def output_project(parent, parent_node, p): | 216 | def output_project(parent, parent_node, p): |
216 | if not p.MatchesGroups(groups): | 217 | if not p.MatchesGroups(groups): |
@@ -269,13 +270,11 @@ class XmlManifest(object): | |||
269 | e.setAttribute('sync-s', 'true') | 270 | e.setAttribute('sync-s', 'true') |
270 | 271 | ||
271 | if p.subprojects: | 272 | if p.subprojects: |
272 | sort_projects = list(sorted([subp.name for subp in p.subprojects])) | 273 | subprojects = set(subp.name for subp in p.subprojects) |
273 | output_projects(p, e, sort_projects) | 274 | output_projects(p, e, list(sorted(subprojects))) |
274 | 275 | ||
275 | sort_projects = list(sorted([key for key, value in self.projects.items() | 276 | projects = set(p.name for p in self._paths.values() if not p.parent) |
276 | if not value.parent])) | 277 | output_projects(None, root, list(sorted(projects))) |
277 | sort_projects.sort() | ||
278 | output_projects(None, root, sort_projects) | ||
279 | 278 | ||
280 | if self._repo_hooks_project: | 279 | if self._repo_hooks_project: |
281 | root.appendChild(doc.createTextNode('')) | 280 | root.appendChild(doc.createTextNode('')) |
@@ -288,9 +287,14 @@ class XmlManifest(object): | |||
288 | doc.writexml(fd, '', ' ', '\n', 'UTF-8') | 287 | doc.writexml(fd, '', ' ', '\n', 'UTF-8') |
289 | 288 | ||
290 | @property | 289 | @property |
290 | def paths(self): | ||
291 | self._Load() | ||
292 | return self._paths | ||
293 | |||
294 | @property | ||
291 | def projects(self): | 295 | def projects(self): |
292 | self._Load() | 296 | self._Load() |
293 | return self._projects | 297 | return self._paths.values() |
294 | 298 | ||
295 | @property | 299 | @property |
296 | def remotes(self): | 300 | def remotes(self): |
@@ -324,6 +328,7 @@ class XmlManifest(object): | |||
324 | def _Unload(self): | 328 | def _Unload(self): |
325 | self._loaded = False | 329 | self._loaded = False |
326 | self._projects = {} | 330 | self._projects = {} |
331 | self._paths = {} | ||
327 | self._remotes = {} | 332 | self._remotes = {} |
328 | self._default = None | 333 | self._default = None |
329 | self._repo_hooks_project = None | 334 | self._repo_hooks_project = None |
@@ -453,11 +458,17 @@ class XmlManifest(object): | |||
453 | self._manifest_server = url | 458 | self._manifest_server = url |
454 | 459 | ||
455 | def recursively_add_projects(project): | 460 | def recursively_add_projects(project): |
456 | if self._projects.get(project.name): | 461 | projects = self._projects.setdefault(project.name, []) |
462 | if project.relpath is None: | ||
457 | raise ManifestParseError( | 463 | raise ManifestParseError( |
458 | 'duplicate project %s in %s' % | 464 | 'missing path for %s in %s' % |
459 | (project.name, self.manifestFile)) | 465 | (project.name, self.manifestFile)) |
460 | self._projects[project.name] = project | 466 | if project.relpath in self._paths: |
467 | raise ManifestParseError( | ||
468 | 'duplicate path %s in %s' % | ||
469 | (project.relpath, self.manifestFile)) | ||
470 | self._paths[project.relpath] = project | ||
471 | projects.append(project) | ||
461 | for subproject in project.subprojects: | 472 | for subproject in project.subprojects: |
462 | recursively_add_projects(subproject) | 473 | recursively_add_projects(subproject) |
463 | 474 | ||
@@ -478,12 +489,18 @@ class XmlManifest(object): | |||
478 | 489 | ||
479 | # Store a reference to the Project. | 490 | # Store a reference to the Project. |
480 | try: | 491 | try: |
481 | self._repo_hooks_project = self._projects[repo_hooks_project] | 492 | repo_hooks_projects = self._projects[repo_hooks_project] |
482 | except KeyError: | 493 | except KeyError: |
483 | raise ManifestParseError( | 494 | raise ManifestParseError( |
484 | 'project %s not found for repo-hooks' % | 495 | 'project %s not found for repo-hooks' % |
485 | (repo_hooks_project)) | 496 | (repo_hooks_project)) |
486 | 497 | ||
498 | if len(repo_hooks_projects) != 1: | ||
499 | raise ManifestParseError( | ||
500 | 'internal error parsing repo-hooks in %s' % | ||
501 | (self.manifestFile)) | ||
502 | self._repo_hooks_project = repo_hooks_projects[0] | ||
503 | |||
487 | # Store the enabled hooks in the Project object. | 504 | # Store the enabled hooks in the Project object. |
488 | self._repo_hooks_project.enabled_repo_hooks = enabled_repo_hooks | 505 | self._repo_hooks_project.enabled_repo_hooks = enabled_repo_hooks |
489 | if node.nodeName == 'remove-project': | 506 | if node.nodeName == 'remove-project': |
@@ -530,11 +547,12 @@ class XmlManifest(object): | |||
530 | name = name, | 547 | name = name, |
531 | remote = remote.ToRemoteSpec(name), | 548 | remote = remote.ToRemoteSpec(name), |
532 | gitdir = gitdir, | 549 | gitdir = gitdir, |
550 | objdir = gitdir, | ||
533 | worktree = None, | 551 | worktree = None, |
534 | relpath = None, | 552 | relpath = None, |
535 | revisionExpr = m.revisionExpr, | 553 | revisionExpr = m.revisionExpr, |
536 | revisionId = None) | 554 | revisionId = None) |
537 | self._projects[project.name] = project | 555 | self._projects[project.name] = [project] |
538 | 556 | ||
539 | def _ParseRemote(self, node): | 557 | def _ParseRemote(self, node): |
540 | """ | 558 | """ |
@@ -694,9 +712,10 @@ class XmlManifest(object): | |||
694 | groups = [x for x in re.split(r'[,\s]+', groups) if x] | 712 | groups = [x for x in re.split(r'[,\s]+', groups) if x] |
695 | 713 | ||
696 | if parent is None: | 714 | if parent is None: |
697 | relpath, worktree, gitdir = self.GetProjectPaths(name, path) | 715 | relpath, worktree, gitdir, objdir = self.GetProjectPaths(name, path) |
698 | else: | 716 | else: |
699 | relpath, worktree, gitdir = self.GetSubprojectPaths(parent, path) | 717 | relpath, worktree, gitdir, objdir = \ |
718 | self.GetSubprojectPaths(parent, name, path) | ||
700 | 719 | ||
701 | default_groups = ['all', 'name:%s' % name, 'path:%s' % relpath] | 720 | default_groups = ['all', 'name:%s' % name, 'path:%s' % relpath] |
702 | groups.extend(set(default_groups).difference(groups)) | 721 | groups.extend(set(default_groups).difference(groups)) |
@@ -709,6 +728,7 @@ class XmlManifest(object): | |||
709 | name = name, | 728 | name = name, |
710 | remote = remote.ToRemoteSpec(name), | 729 | remote = remote.ToRemoteSpec(name), |
711 | gitdir = gitdir, | 730 | gitdir = gitdir, |
731 | objdir = objdir, | ||
712 | worktree = worktree, | 732 | worktree = worktree, |
713 | relpath = relpath, | 733 | relpath = relpath, |
714 | revisionExpr = revisionExpr, | 734 | revisionExpr = revisionExpr, |
@@ -737,10 +757,15 @@ class XmlManifest(object): | |||
737 | if self.IsMirror: | 757 | if self.IsMirror: |
738 | worktree = None | 758 | worktree = None |
739 | gitdir = os.path.join(self.topdir, '%s.git' % name) | 759 | gitdir = os.path.join(self.topdir, '%s.git' % name) |
760 | objdir = gitdir | ||
740 | else: | 761 | else: |
741 | worktree = os.path.join(self.topdir, path).replace('\\', '/') | 762 | worktree = os.path.join(self.topdir, path).replace('\\', '/') |
742 | gitdir = os.path.join(self.repodir, 'projects', '%s.git' % path) | 763 | gitdir = os.path.join(self.repodir, 'projects', '%s.git' % path) |
743 | return relpath, worktree, gitdir | 764 | objdir = os.path.join(self.repodir, 'project-objects', '%s.git' % name) |
765 | return relpath, worktree, gitdir, objdir | ||
766 | |||
767 | def GetProjectsWithName(self, name): | ||
768 | return self._projects.get(name, []) | ||
744 | 769 | ||
745 | def GetSubprojectName(self, parent, submodule_path): | 770 | def GetSubprojectName(self, parent, submodule_path): |
746 | return os.path.join(parent.name, submodule_path) | 771 | return os.path.join(parent.name, submodule_path) |
@@ -751,14 +776,15 @@ class XmlManifest(object): | |||
751 | def _UnjoinRelpath(self, parent_relpath, relpath): | 776 | def _UnjoinRelpath(self, parent_relpath, relpath): |
752 | return os.path.relpath(relpath, parent_relpath) | 777 | return os.path.relpath(relpath, parent_relpath) |
753 | 778 | ||
754 | def GetSubprojectPaths(self, parent, path): | 779 | def GetSubprojectPaths(self, parent, name, path): |
755 | relpath = self._JoinRelpath(parent.relpath, path) | 780 | relpath = self._JoinRelpath(parent.relpath, path) |
756 | gitdir = os.path.join(parent.gitdir, 'subprojects', '%s.git' % path) | 781 | gitdir = os.path.join(parent.gitdir, 'subprojects', '%s.git' % path) |
782 | objdir = os.path.join(parent.gitdir, 'subproject-objects', '%s.git' % name) | ||
757 | if self.IsMirror: | 783 | if self.IsMirror: |
758 | worktree = None | 784 | worktree = None |
759 | else: | 785 | else: |
760 | worktree = os.path.join(parent.worktree, path).replace('\\', '/') | 786 | worktree = os.path.join(parent.worktree, path).replace('\\', '/') |
761 | return relpath, worktree, gitdir | 787 | return relpath, worktree, gitdir, objdir |
762 | 788 | ||
763 | def _ParseCopyFile(self, project, node): | 789 | def _ParseCopyFile(self, project, node): |
764 | src = self._reqatt(node, 'src') | 790 | src = self._reqatt(node, 'src') |
@@ -487,6 +487,7 @@ class Project(object): | |||
487 | name, | 487 | name, |
488 | remote, | 488 | remote, |
489 | gitdir, | 489 | gitdir, |
490 | objdir, | ||
490 | worktree, | 491 | worktree, |
491 | relpath, | 492 | relpath, |
492 | revisionExpr, | 493 | revisionExpr, |
@@ -507,6 +508,7 @@ class Project(object): | |||
507 | name: The `name` attribute of manifest.xml's project element. | 508 | name: The `name` attribute of manifest.xml's project element. |
508 | remote: RemoteSpec object specifying its remote's properties. | 509 | remote: RemoteSpec object specifying its remote's properties. |
509 | gitdir: Absolute path of git directory. | 510 | gitdir: Absolute path of git directory. |
511 | objdir: Absolute path of directory to store git objects. | ||
510 | worktree: Absolute path of git working tree. | 512 | worktree: Absolute path of git working tree. |
511 | relpath: Relative path of git working tree to repo's top directory. | 513 | relpath: Relative path of git working tree to repo's top directory. |
512 | revisionExpr: The `revision` attribute of manifest.xml's project element. | 514 | revisionExpr: The `revision` attribute of manifest.xml's project element. |
@@ -525,6 +527,7 @@ class Project(object): | |||
525 | self.name = name | 527 | self.name = name |
526 | self.remote = remote | 528 | self.remote = remote |
527 | self.gitdir = gitdir.replace('\\', '/') | 529 | self.gitdir = gitdir.replace('\\', '/') |
530 | self.objdir = objdir.replace('\\', '/') | ||
528 | if worktree: | 531 | if worktree: |
529 | self.worktree = worktree.replace('\\', '/') | 532 | self.worktree = worktree.replace('\\', '/') |
530 | else: | 533 | else: |
@@ -557,11 +560,12 @@ class Project(object): | |||
557 | defaults = self.manifest.globalConfig) | 560 | defaults = self.manifest.globalConfig) |
558 | 561 | ||
559 | if self.worktree: | 562 | if self.worktree: |
560 | self.work_git = self._GitGetByExec(self, bare=False) | 563 | self.work_git = self._GitGetByExec(self, bare=False, gitdir=gitdir) |
561 | else: | 564 | else: |
562 | self.work_git = None | 565 | self.work_git = None |
563 | self.bare_git = self._GitGetByExec(self, bare=True) | 566 | self.bare_git = self._GitGetByExec(self, bare=True, gitdir=gitdir) |
564 | self.bare_ref = GitRefs(gitdir) | 567 | self.bare_ref = GitRefs(gitdir) |
568 | self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=objdir) | ||
565 | self.dest_branch = dest_branch | 569 | self.dest_branch = dest_branch |
566 | 570 | ||
567 | # This will be filled in if a project is later identified to be the | 571 | # This will be filled in if a project is later identified to be the |
@@ -1069,6 +1073,7 @@ class Project(object): | |||
1069 | """Perform only the local IO portion of the sync process. | 1073 | """Perform only the local IO portion of the sync process. |
1070 | Network access is not required. | 1074 | Network access is not required. |
1071 | """ | 1075 | """ |
1076 | self._InitWorkTree() | ||
1072 | all_refs = self.bare_ref.all | 1077 | all_refs = self.bare_ref.all |
1073 | self.CleanPublishedCache(all_refs) | 1078 | self.CleanPublishedCache(all_refs) |
1074 | revid = self.GetRevisionId(all_refs) | 1079 | revid = self.GetRevisionId(all_refs) |
@@ -1077,7 +1082,6 @@ class Project(object): | |||
1077 | self._FastForward(revid) | 1082 | self._FastForward(revid) |
1078 | self._CopyFiles() | 1083 | self._CopyFiles() |
1079 | 1084 | ||
1080 | self._InitWorkTree() | ||
1081 | head = self.work_git.GetHead() | 1085 | head = self.work_git.GetHead() |
1082 | if head.startswith(R_HEADS): | 1086 | if head.startswith(R_HEADS): |
1083 | branch = head[len(R_HEADS):] | 1087 | branch = head[len(R_HEADS):] |
@@ -1544,11 +1548,13 @@ class Project(object): | |||
1544 | return result | 1548 | return result |
1545 | for rev, path, url in self._GetSubmodules(): | 1549 | for rev, path, url in self._GetSubmodules(): |
1546 | name = self.manifest.GetSubprojectName(self, path) | 1550 | name = self.manifest.GetSubprojectName(self, path) |
1547 | project = self.manifest.projects.get(name) | 1551 | relpath, worktree, gitdir, objdir = \ |
1552 | self.manifest.GetSubprojectPaths(self, name, path) | ||
1553 | project = self.manifest.paths.get(relpath) | ||
1548 | if project: | 1554 | if project: |
1549 | result.extend(project.GetDerivedSubprojects()) | 1555 | result.extend(project.GetDerivedSubprojects()) |
1550 | continue | 1556 | continue |
1551 | relpath, worktree, gitdir = self.manifest.GetSubprojectPaths(self, path) | 1557 | |
1552 | remote = RemoteSpec(self.remote.name, | 1558 | remote = RemoteSpec(self.remote.name, |
1553 | url = url, | 1559 | url = url, |
1554 | review = self.remote.review) | 1560 | review = self.remote.review) |
@@ -1556,6 +1562,7 @@ class Project(object): | |||
1556 | name = name, | 1562 | name = name, |
1557 | remote = remote, | 1563 | remote = remote, |
1558 | gitdir = gitdir, | 1564 | gitdir = gitdir, |
1565 | objdir = objdir, | ||
1559 | worktree = worktree, | 1566 | worktree = worktree, |
1560 | relpath = relpath, | 1567 | relpath = relpath, |
1561 | revisionExpr = self.revisionExpr, | 1568 | revisionExpr = self.revisionExpr, |
@@ -1905,8 +1912,17 @@ class Project(object): | |||
1905 | 1912 | ||
1906 | def _InitGitDir(self, mirror_git=None): | 1913 | def _InitGitDir(self, mirror_git=None): |
1907 | if not os.path.exists(self.gitdir): | 1914 | if not os.path.exists(self.gitdir): |
1908 | os.makedirs(self.gitdir) | 1915 | |
1909 | self.bare_git.init() | 1916 | # Initialize the bare repository, which contains all of the objects. |
1917 | if not os.path.exists(self.objdir): | ||
1918 | os.makedirs(self.objdir) | ||
1919 | self.bare_objdir.init() | ||
1920 | |||
1921 | # If we have a separate directory to hold refs, initialize it as well. | ||
1922 | if self.objdir != self.gitdir: | ||
1923 | os.makedirs(self.gitdir) | ||
1924 | self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False, | ||
1925 | copy_all=True) | ||
1910 | 1926 | ||
1911 | mp = self.manifest.manifestProject | 1927 | mp = self.manifest.manifestProject |
1912 | ref_dir = mp.config.GetString('repo.reference') or '' | 1928 | ref_dir = mp.config.GetString('repo.reference') or '' |
@@ -2022,33 +2038,61 @@ class Project(object): | |||
2022 | msg = 'manifest set to %s' % self.revisionExpr | 2038 | msg = 'manifest set to %s' % self.revisionExpr |
2023 | self.bare_git.symbolic_ref('-m', msg, ref, dst) | 2039 | self.bare_git.symbolic_ref('-m', msg, ref, dst) |
2024 | 2040 | ||
2041 | def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all): | ||
2042 | """Update |dotgit| to reference |gitdir|, using symlinks where possible. | ||
2043 | |||
2044 | Args: | ||
2045 | gitdir: The bare git repository. Must already be initialized. | ||
2046 | dotgit: The repository you would like to initialize. | ||
2047 | share_refs: If true, |dotgit| will store its refs under |gitdir|. | ||
2048 | Only one work tree can store refs under a given |gitdir|. | ||
2049 | copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|. | ||
2050 | This saves you the effort of initializing |dotgit| yourself. | ||
2051 | """ | ||
2052 | # These objects can be shared between several working trees. | ||
2053 | symlink_files = ['description', 'info'] | ||
2054 | symlink_dirs = ['hooks', 'objects', 'rr-cache', 'svn'] | ||
2055 | if share_refs: | ||
2056 | # These objects can only be used by a single working tree. | ||
2057 | symlink_files += ['config', 'packed-refs'] | ||
2058 | symlink_dirs += ['logs', 'refs'] | ||
2059 | to_symlink = symlink_files + symlink_dirs | ||
2060 | |||
2061 | to_copy = [] | ||
2062 | if copy_all: | ||
2063 | to_copy = os.listdir(gitdir) | ||
2064 | |||
2065 | for name in set(to_copy).union(to_symlink): | ||
2066 | try: | ||
2067 | src = os.path.realpath(os.path.join(gitdir, name)) | ||
2068 | dst = os.path.realpath(os.path.join(dotgit, name)) | ||
2069 | |||
2070 | if os.path.lexists(dst) and not os.path.islink(dst): | ||
2071 | raise GitError('cannot overwrite a local work tree') | ||
2072 | |||
2073 | # If the source dir doesn't exist, create an empty dir. | ||
2074 | if name in symlink_dirs and not os.path.lexists(src): | ||
2075 | os.makedirs(src) | ||
2076 | |||
2077 | if name in to_symlink: | ||
2078 | os.symlink(os.path.relpath(src, os.path.dirname(dst)), dst) | ||
2079 | elif copy_all and not os.path.islink(dst): | ||
2080 | if os.path.isdir(src): | ||
2081 | shutil.copytree(src, dst) | ||
2082 | elif os.path.isfile(src): | ||
2083 | shutil.copy(src, dst) | ||
2084 | except OSError as e: | ||
2085 | if e.errno == errno.EPERM: | ||
2086 | raise GitError('filesystem must support symlinks') | ||
2087 | else: | ||
2088 | raise | ||
2089 | |||
2025 | def _InitWorkTree(self): | 2090 | def _InitWorkTree(self): |
2026 | dotgit = os.path.join(self.worktree, '.git') | 2091 | dotgit = os.path.join(self.worktree, '.git') |
2027 | if not os.path.exists(dotgit): | 2092 | if not os.path.exists(dotgit): |
2028 | os.makedirs(dotgit) | 2093 | os.makedirs(dotgit) |
2029 | 2094 | self._ReferenceGitDir(self.gitdir, dotgit, share_refs=True, | |
2030 | for name in ['config', | 2095 | copy_all=False) |
2031 | 'description', | ||
2032 | 'hooks', | ||
2033 | 'info', | ||
2034 | 'logs', | ||
2035 | 'objects', | ||
2036 | 'packed-refs', | ||
2037 | 'refs', | ||
2038 | 'rr-cache', | ||
2039 | 'svn']: | ||
2040 | try: | ||
2041 | src = os.path.join(self.gitdir, name) | ||
2042 | dst = os.path.join(dotgit, name) | ||
2043 | if os.path.islink(dst) or not os.path.exists(dst): | ||
2044 | os.symlink(os.path.relpath(src, os.path.dirname(dst)), dst) | ||
2045 | else: | ||
2046 | raise GitError('cannot overwrite a local work tree') | ||
2047 | except OSError as e: | ||
2048 | if e.errno == errno.EPERM: | ||
2049 | raise GitError('filesystem must support symlinks') | ||
2050 | else: | ||
2051 | raise | ||
2052 | 2096 | ||
2053 | _lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId()) | 2097 | _lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId()) |
2054 | 2098 | ||
@@ -2058,14 +2102,10 @@ class Project(object): | |||
2058 | if GitCommand(self, cmd).Wait() != 0: | 2102 | if GitCommand(self, cmd).Wait() != 0: |
2059 | raise GitError("cannot initialize work tree") | 2103 | raise GitError("cannot initialize work tree") |
2060 | 2104 | ||
2061 | rr_cache = os.path.join(self.gitdir, 'rr-cache') | ||
2062 | if not os.path.exists(rr_cache): | ||
2063 | os.makedirs(rr_cache) | ||
2064 | |||
2065 | self._CopyFiles() | 2105 | self._CopyFiles() |
2066 | 2106 | ||
2067 | def _gitdir_path(self, path): | 2107 | def _gitdir_path(self, path): |
2068 | return os.path.join(self.gitdir, path) | 2108 | return os.path.realpath(os.path.join(self.gitdir, path)) |
2069 | 2109 | ||
2070 | def _revlist(self, *args, **kw): | 2110 | def _revlist(self, *args, **kw): |
2071 | a = [] | 2111 | a = [] |
@@ -2078,9 +2118,10 @@ class Project(object): | |||
2078 | return self.bare_ref.all | 2118 | return self.bare_ref.all |
2079 | 2119 | ||
2080 | class _GitGetByExec(object): | 2120 | class _GitGetByExec(object): |
2081 | def __init__(self, project, bare): | 2121 | def __init__(self, project, bare, gitdir): |
2082 | self._project = project | 2122 | self._project = project |
2083 | self._bare = bare | 2123 | self._bare = bare |
2124 | self._gitdir = gitdir | ||
2084 | 2125 | ||
2085 | def LsOthers(self): | 2126 | def LsOthers(self): |
2086 | p = GitCommand(self._project, | 2127 | p = GitCommand(self._project, |
@@ -2089,6 +2130,7 @@ class Project(object): | |||
2089 | '--others', | 2130 | '--others', |
2090 | '--exclude-standard'], | 2131 | '--exclude-standard'], |
2091 | bare = False, | 2132 | bare = False, |
2133 | gitdir=self._gitdir, | ||
2092 | capture_stdout = True, | 2134 | capture_stdout = True, |
2093 | capture_stderr = True) | 2135 | capture_stderr = True) |
2094 | if p.Wait() == 0: | 2136 | if p.Wait() == 0: |
@@ -2104,6 +2146,7 @@ class Project(object): | |||
2104 | cmd.extend(args) | 2146 | cmd.extend(args) |
2105 | p = GitCommand(self._project, | 2147 | p = GitCommand(self._project, |
2106 | cmd, | 2148 | cmd, |
2149 | gitdir=self._gitdir, | ||
2107 | bare = False, | 2150 | bare = False, |
2108 | capture_stdout = True, | 2151 | capture_stdout = True, |
2109 | capture_stderr = True) | 2152 | capture_stderr = True) |
@@ -2213,6 +2256,7 @@ class Project(object): | |||
2213 | p = GitCommand(self._project, | 2256 | p = GitCommand(self._project, |
2214 | cmdv, | 2257 | cmdv, |
2215 | bare = self._bare, | 2258 | bare = self._bare, |
2259 | gitdir=self._gitdir, | ||
2216 | capture_stdout = True, | 2260 | capture_stdout = True, |
2217 | capture_stderr = True) | 2261 | capture_stderr = True) |
2218 | r = [] | 2262 | r = [] |
@@ -2265,6 +2309,7 @@ class Project(object): | |||
2265 | p = GitCommand(self._project, | 2309 | p = GitCommand(self._project, |
2266 | cmdv, | 2310 | cmdv, |
2267 | bare = self._bare, | 2311 | bare = self._bare, |
2312 | gitdir=self._gitdir, | ||
2268 | capture_stdout = True, | 2313 | capture_stdout = True, |
2269 | capture_stderr = True) | 2314 | capture_stderr = True) |
2270 | if p.Wait() != 0: | 2315 | if p.Wait() != 0: |
@@ -2398,6 +2443,7 @@ class MetaProject(Project): | |||
2398 | manifest = manifest, | 2443 | manifest = manifest, |
2399 | name = name, | 2444 | name = name, |
2400 | gitdir = gitdir, | 2445 | gitdir = gitdir, |
2446 | objdir = gitdir, | ||
2401 | worktree = worktree, | 2447 | worktree = worktree, |
2402 | remote = RemoteSpec('origin'), | 2448 | remote = RemoteSpec('origin'), |
2403 | relpath = '.repo/%s' % name, | 2449 | relpath = '.repo/%s' % name, |
diff --git a/subcmds/rebase.py b/subcmds/rebase.py index b9a7774d..1bdc1f0b 100644 --- a/subcmds/rebase.py +++ b/subcmds/rebase.py | |||
@@ -62,6 +62,9 @@ branch but need to incorporate new upstream changes "underneath" them. | |||
62 | if opt.interactive and not one_project: | 62 | if opt.interactive and not one_project: |
63 | print('error: interactive rebase not supported with multiple projects', | 63 | print('error: interactive rebase not supported with multiple projects', |
64 | file=sys.stderr) | 64 | file=sys.stderr) |
65 | if len(args) == 1: | ||
66 | print('note: project %s is mapped to more than one path' % (args[0],), | ||
67 | file=sys.stderr) | ||
65 | return -1 | 68 | return -1 |
66 | 69 | ||
67 | for project in all_projects: | 70 | for project in all_projects: |
diff --git a/subcmds/sync.py b/subcmds/sync.py index e9d52b7b..d1a06412 100644 --- a/subcmds/sync.py +++ b/subcmds/sync.py | |||
@@ -219,9 +219,25 @@ later is required to fix a server side protocol bug. | |||
219 | dest='repo_upgraded', action='store_true', | 219 | dest='repo_upgraded', action='store_true', |
220 | help=SUPPRESS_HELP) | 220 | help=SUPPRESS_HELP) |
221 | 221 | ||
222 | def _FetchHelper(self, opt, project, lock, fetched, pm, sem, err_event): | 222 | def _FetchProjectList(self, opt, projects, *args): |
223 | """Main function of the fetch threads when jobs are > 1. | 223 | """Main function of the fetch threads when jobs are > 1. |
224 | 224 | ||
225 | Delegates most of the work to _FetchHelper. | ||
226 | |||
227 | Args: | ||
228 | opt: Program options returned from optparse. See _Options(). | ||
229 | projects: Projects to fetch. | ||
230 | *args: Remaining arguments to pass to _FetchHelper. See the | ||
231 | _FetchHelper docstring for details. | ||
232 | """ | ||
233 | for project in projects: | ||
234 | success = self._FetchHelper(opt, project, *args) | ||
235 | if not success and not opt.force_broken: | ||
236 | break | ||
237 | |||
238 | def _FetchHelper(self, opt, project, lock, fetched, pm, sem, err_event): | ||
239 | """Fetch git objects for a single project. | ||
240 | |||
225 | Args: | 241 | Args: |
226 | opt: Program options returned from optparse. See _Options(). | 242 | opt: Program options returned from optparse. See _Options(). |
227 | project: Project object for the project to fetch. | 243 | project: Project object for the project to fetch. |
@@ -235,6 +251,9 @@ later is required to fix a server side protocol bug. | |||
235 | can be started up. | 251 | can be started up. |
236 | err_event: We'll set this event in the case of an error (after printing | 252 | err_event: We'll set this event in the case of an error (after printing |
237 | out info about the error). | 253 | out info about the error). |
254 | |||
255 | Returns: | ||
256 | Whether the fetch was successful. | ||
238 | """ | 257 | """ |
239 | # We'll set to true once we've locked the lock. | 258 | # We'll set to true once we've locked the lock. |
240 | did_lock = False | 259 | did_lock = False |
@@ -281,6 +300,8 @@ later is required to fix a server side protocol bug. | |||
281 | lock.release() | 300 | lock.release() |
282 | sem.release() | 301 | sem.release() |
283 | 302 | ||
303 | return success | ||
304 | |||
284 | def _Fetch(self, projects, opt): | 305 | def _Fetch(self, projects, opt): |
285 | fetched = set() | 306 | fetched = set() |
286 | pm = Progress('Fetching projects', len(projects)) | 307 | pm = Progress('Fetching projects', len(projects)) |
@@ -303,20 +324,24 @@ later is required to fix a server side protocol bug. | |||
303 | else: | 324 | else: |
304 | sys.exit(1) | 325 | sys.exit(1) |
305 | else: | 326 | else: |
327 | objdir_project_map = dict() | ||
328 | for project in projects: | ||
329 | objdir_project_map.setdefault(project.objdir, []).append(project) | ||
330 | |||
306 | threads = set() | 331 | threads = set() |
307 | lock = _threading.Lock() | 332 | lock = _threading.Lock() |
308 | sem = _threading.Semaphore(self.jobs) | 333 | sem = _threading.Semaphore(self.jobs) |
309 | err_event = _threading.Event() | 334 | err_event = _threading.Event() |
310 | for project in projects: | 335 | for project_list in objdir_project_map.values(): |
311 | # Check for any errors before starting any new threads. | 336 | # Check for any errors before starting any new threads. |
312 | # ...we'll let existing threads finish, though. | 337 | # ...we'll let existing threads finish, though. |
313 | if err_event.isSet(): | 338 | if err_event.isSet(): |
314 | break | 339 | break |
315 | 340 | ||
316 | sem.acquire() | 341 | sem.acquire() |
317 | t = _threading.Thread(target = self._FetchHelper, | 342 | t = _threading.Thread(target = self._FetchProjectList, |
318 | args = (opt, | 343 | args = (opt, |
319 | project, | 344 | project_list, |
320 | lock, | 345 | lock, |
321 | fetched, | 346 | fetched, |
322 | pm, | 347 | pm, |
@@ -342,6 +367,10 @@ later is required to fix a server side protocol bug. | |||
342 | return fetched | 367 | return fetched |
343 | 368 | ||
344 | def _GCProjects(self, projects): | 369 | def _GCProjects(self, projects): |
370 | gitdirs = {} | ||
371 | for project in projects: | ||
372 | gitdirs[project.gitdir] = project.bare_git | ||
373 | |||
345 | has_dash_c = git_require((1, 7, 2)) | 374 | has_dash_c = git_require((1, 7, 2)) |
346 | if multiprocessing and has_dash_c: | 375 | if multiprocessing and has_dash_c: |
347 | cpu_count = multiprocessing.cpu_count() | 376 | cpu_count = multiprocessing.cpu_count() |
@@ -350,8 +379,8 @@ later is required to fix a server side protocol bug. | |||
350 | jobs = min(self.jobs, cpu_count) | 379 | jobs = min(self.jobs, cpu_count) |
351 | 380 | ||
352 | if jobs < 2: | 381 | if jobs < 2: |
353 | for project in projects: | 382 | for bare_git in gitdirs.values(): |
354 | project.bare_git.gc('--auto') | 383 | bare_git.gc('--auto') |
355 | return | 384 | return |
356 | 385 | ||
357 | config = {'pack.threads': cpu_count / jobs if cpu_count > jobs else 1} | 386 | config = {'pack.threads': cpu_count / jobs if cpu_count > jobs else 1} |
@@ -360,10 +389,10 @@ later is required to fix a server side protocol bug. | |||
360 | sem = _threading.Semaphore(jobs) | 389 | sem = _threading.Semaphore(jobs) |
361 | err_event = _threading.Event() | 390 | err_event = _threading.Event() |
362 | 391 | ||
363 | def GC(project): | 392 | def GC(bare_git): |
364 | try: | 393 | try: |
365 | try: | 394 | try: |
366 | project.bare_git.gc('--auto', config=config) | 395 | bare_git.gc('--auto', config=config) |
367 | except GitError: | 396 | except GitError: |
368 | err_event.set() | 397 | err_event.set() |
369 | except: | 398 | except: |
@@ -372,11 +401,11 @@ later is required to fix a server side protocol bug. | |||
372 | finally: | 401 | finally: |
373 | sem.release() | 402 | sem.release() |
374 | 403 | ||
375 | for project in projects: | 404 | for bare_git in gitdirs.values(): |
376 | if err_event.isSet(): | 405 | if err_event.isSet(): |
377 | break | 406 | break |
378 | sem.acquire() | 407 | sem.acquire() |
379 | t = _threading.Thread(target=GC, args=(project,)) | 408 | t = _threading.Thread(target=GC, args=(bare_git,)) |
380 | t.daemon = True | 409 | t.daemon = True |
381 | threads.add(t) | 410 | threads.add(t) |
382 | t.start() | 411 | t.start() |
@@ -416,12 +445,13 @@ later is required to fix a server side protocol bug. | |||
416 | if path not in new_project_paths: | 445 | if path not in new_project_paths: |
417 | # If the path has already been deleted, we don't need to do it | 446 | # If the path has already been deleted, we don't need to do it |
418 | if os.path.exists(self.manifest.topdir + '/' + path): | 447 | if os.path.exists(self.manifest.topdir + '/' + path): |
448 | gitdir = os.path.join(self.manifest.topdir, path, '.git') | ||
419 | project = Project( | 449 | project = Project( |
420 | manifest = self.manifest, | 450 | manifest = self.manifest, |
421 | name = path, | 451 | name = path, |
422 | remote = RemoteSpec('origin'), | 452 | remote = RemoteSpec('origin'), |
423 | gitdir = os.path.join(self.manifest.topdir, | 453 | gitdir = gitdir, |
424 | path, '.git'), | 454 | objdir = gitdir, |
425 | worktree = os.path.join(self.manifest.topdir, path), | 455 | worktree = os.path.join(self.manifest.topdir, path), |
426 | relpath = path, | 456 | relpath = path, |
427 | revisionExpr = 'HEAD', | 457 | revisionExpr = 'HEAD', |
diff --git a/subcmds/upload.py b/subcmds/upload.py index 8d801e08..9ad55d79 100644 --- a/subcmds/upload.py +++ b/subcmds/upload.py | |||
@@ -431,8 +431,10 @@ Gerrit Code Review: http://code.google.com/p/gerrit/ | |||
431 | hook = RepoHook('pre-upload', self.manifest.repo_hooks_project, | 431 | hook = RepoHook('pre-upload', self.manifest.repo_hooks_project, |
432 | self.manifest.topdir, abort_if_user_denies=True) | 432 | self.manifest.topdir, abort_if_user_denies=True) |
433 | pending_proj_names = [project.name for (project, avail) in pending] | 433 | pending_proj_names = [project.name for (project, avail) in pending] |
434 | pending_worktrees = [project.worktree for (project, avail) in pending] | ||
434 | try: | 435 | try: |
435 | hook.Run(opt.allow_all_hooks, project_list=pending_proj_names) | 436 | hook.Run(opt.allow_all_hooks, project_list=pending_proj_names, |
437 | worktree_list=pending_worktrees) | ||
436 | except HookError as e: | 438 | except HookError as e: |
437 | print("ERROR: %s" % str(e), file=sys.stderr) | 439 | print("ERROR: %s" % str(e), file=sys.stderr) |
438 | return | 440 | return |