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 785976bc..c5f3bcc9 100644 --- a/manifest_xml.py +++ b/manifest_xml.py | |||
| @@ -215,8 +215,9 @@ class XmlManifest(object): | |||
| 215 | root.appendChild(doc.createTextNode('')) | 215 | root.appendChild(doc.createTextNode('')) |
| 216 | 216 | ||
| 217 | def output_projects(parent, parent_node, projects): | 217 | def output_projects(parent, parent_node, projects): |
| 218 | for p in projects: | 218 | for project_name in projects: |
| 219 | output_project(parent, parent_node, self.projects[p]) | 219 | for project in self._projects[project_name]: |
| 220 | output_project(parent, parent_node, project) | ||
| 220 | 221 | ||
| 221 | def output_project(parent, parent_node, p): | 222 | def output_project(parent, parent_node, p): |
| 222 | if not p.MatchesGroups(groups): | 223 | if not p.MatchesGroups(groups): |
| @@ -277,13 +278,11 @@ class XmlManifest(object): | |||
| 277 | e.setAttribute('sync-s', 'true') | 278 | e.setAttribute('sync-s', 'true') |
| 278 | 279 | ||
| 279 | if p.subprojects: | 280 | if p.subprojects: |
| 280 | sort_projects = list(sorted([subp.name for subp in p.subprojects])) | 281 | subprojects = set(subp.name for subp in p.subprojects) |
| 281 | output_projects(p, e, sort_projects) | 282 | output_projects(p, e, list(sorted(subprojects))) |
| 282 | 283 | ||
| 283 | sort_projects = list(sorted([key for key, value in self.projects.items() | 284 | projects = set(p.name for p in self._paths.values() if not p.parent) |
| 284 | if not value.parent])) | 285 | output_projects(None, root, list(sorted(projects))) |
| 285 | sort_projects.sort() | ||
| 286 | output_projects(None, root, sort_projects) | ||
| 287 | 286 | ||
| 288 | if self._repo_hooks_project: | 287 | if self._repo_hooks_project: |
| 289 | root.appendChild(doc.createTextNode('')) | 288 | root.appendChild(doc.createTextNode('')) |
| @@ -296,9 +295,14 @@ class XmlManifest(object): | |||
| 296 | doc.writexml(fd, '', ' ', '\n', 'UTF-8') | 295 | doc.writexml(fd, '', ' ', '\n', 'UTF-8') |
| 297 | 296 | ||
| 298 | @property | 297 | @property |
| 298 | def paths(self): | ||
| 299 | self._Load() | ||
| 300 | return self._paths | ||
| 301 | |||
| 302 | @property | ||
| 299 | def projects(self): | 303 | def projects(self): |
| 300 | self._Load() | 304 | self._Load() |
| 301 | return self._projects | 305 | return self._paths.values() |
| 302 | 306 | ||
| 303 | @property | 307 | @property |
| 304 | def remotes(self): | 308 | def remotes(self): |
| @@ -336,6 +340,7 @@ class XmlManifest(object): | |||
| 336 | def _Unload(self): | 340 | def _Unload(self): |
| 337 | self._loaded = False | 341 | self._loaded = False |
| 338 | self._projects = {} | 342 | self._projects = {} |
| 343 | self._paths = {} | ||
| 339 | self._remotes = {} | 344 | self._remotes = {} |
| 340 | self._default = None | 345 | self._default = None |
| 341 | self._repo_hooks_project = None | 346 | self._repo_hooks_project = None |
| @@ -467,11 +472,17 @@ class XmlManifest(object): | |||
| 467 | self._manifest_server = url | 472 | self._manifest_server = url |
| 468 | 473 | ||
| 469 | def recursively_add_projects(project): | 474 | def recursively_add_projects(project): |
| 470 | if self._projects.get(project.name): | 475 | projects = self._projects.setdefault(project.name, []) |
| 476 | if project.relpath is None: | ||
| 471 | raise ManifestParseError( | 477 | raise ManifestParseError( |
| 472 | 'duplicate project %s in %s' % | 478 | 'missing path for %s in %s' % |
| 473 | (project.name, self.manifestFile)) | 479 | (project.name, self.manifestFile)) |
| 474 | self._projects[project.name] = project | 480 | if project.relpath in self._paths: |
| 481 | raise ManifestParseError( | ||
| 482 | 'duplicate path %s in %s' % | ||
| 483 | (project.relpath, self.manifestFile)) | ||
| 484 | self._paths[project.relpath] = project | ||
| 485 | projects.append(project) | ||
| 475 | for subproject in project.subprojects: | 486 | for subproject in project.subprojects: |
| 476 | recursively_add_projects(subproject) | 487 | recursively_add_projects(subproject) |
| 477 | 488 | ||
| @@ -492,12 +503,18 @@ class XmlManifest(object): | |||
| 492 | 503 | ||
| 493 | # Store a reference to the Project. | 504 | # Store a reference to the Project. |
| 494 | try: | 505 | try: |
| 495 | self._repo_hooks_project = self._projects[repo_hooks_project] | 506 | repo_hooks_projects = self._projects[repo_hooks_project] |
| 496 | except KeyError: | 507 | except KeyError: |
| 497 | raise ManifestParseError( | 508 | raise ManifestParseError( |
| 498 | 'project %s not found for repo-hooks' % | 509 | 'project %s not found for repo-hooks' % |
| 499 | (repo_hooks_project)) | 510 | (repo_hooks_project)) |
| 500 | 511 | ||
| 512 | if len(repo_hooks_projects) != 1: | ||
| 513 | raise ManifestParseError( | ||
| 514 | 'internal error parsing repo-hooks in %s' % | ||
| 515 | (self.manifestFile)) | ||
| 516 | self._repo_hooks_project = repo_hooks_projects[0] | ||
| 517 | |||
| 501 | # Store the enabled hooks in the Project object. | 518 | # Store the enabled hooks in the Project object. |
| 502 | self._repo_hooks_project.enabled_repo_hooks = enabled_repo_hooks | 519 | self._repo_hooks_project.enabled_repo_hooks = enabled_repo_hooks |
| 503 | if node.nodeName == 'remove-project': | 520 | if node.nodeName == 'remove-project': |
| @@ -544,11 +561,12 @@ class XmlManifest(object): | |||
| 544 | name = name, | 561 | name = name, |
| 545 | remote = remote.ToRemoteSpec(name), | 562 | remote = remote.ToRemoteSpec(name), |
| 546 | gitdir = gitdir, | 563 | gitdir = gitdir, |
| 564 | objdir = gitdir, | ||
| 547 | worktree = None, | 565 | worktree = None, |
| 548 | relpath = None, | 566 | relpath = None, |
| 549 | revisionExpr = m.revisionExpr, | 567 | revisionExpr = m.revisionExpr, |
| 550 | revisionId = None) | 568 | revisionId = None) |
| 551 | self._projects[project.name] = project | 569 | self._projects[project.name] = [project] |
| 552 | 570 | ||
| 553 | def _ParseRemote(self, node): | 571 | def _ParseRemote(self, node): |
| 554 | """ | 572 | """ |
| @@ -708,9 +726,10 @@ class XmlManifest(object): | |||
| 708 | groups = [x for x in re.split(r'[,\s]+', groups) if x] | 726 | groups = [x for x in re.split(r'[,\s]+', groups) if x] |
| 709 | 727 | ||
| 710 | if parent is None: | 728 | if parent is None: |
| 711 | relpath, worktree, gitdir = self.GetProjectPaths(name, path) | 729 | relpath, worktree, gitdir, objdir = self.GetProjectPaths(name, path) |
| 712 | else: | 730 | else: |
| 713 | relpath, worktree, gitdir = self.GetSubprojectPaths(parent, path) | 731 | relpath, worktree, gitdir, objdir = \ |
| 732 | self.GetSubprojectPaths(parent, name, path) | ||
| 714 | 733 | ||
| 715 | default_groups = ['all', 'name:%s' % name, 'path:%s' % relpath] | 734 | default_groups = ['all', 'name:%s' % name, 'path:%s' % relpath] |
| 716 | groups.extend(set(default_groups).difference(groups)) | 735 | groups.extend(set(default_groups).difference(groups)) |
| @@ -723,6 +742,7 @@ class XmlManifest(object): | |||
| 723 | name = name, | 742 | name = name, |
| 724 | remote = remote.ToRemoteSpec(name), | 743 | remote = remote.ToRemoteSpec(name), |
| 725 | gitdir = gitdir, | 744 | gitdir = gitdir, |
| 745 | objdir = objdir, | ||
| 726 | worktree = worktree, | 746 | worktree = worktree, |
| 727 | relpath = relpath, | 747 | relpath = relpath, |
| 728 | revisionExpr = revisionExpr, | 748 | revisionExpr = revisionExpr, |
| @@ -751,10 +771,15 @@ class XmlManifest(object): | |||
| 751 | if self.IsMirror: | 771 | if self.IsMirror: |
| 752 | worktree = None | 772 | worktree = None |
| 753 | gitdir = os.path.join(self.topdir, '%s.git' % name) | 773 | gitdir = os.path.join(self.topdir, '%s.git' % name) |
| 774 | objdir = gitdir | ||
| 754 | else: | 775 | else: |
| 755 | worktree = os.path.join(self.topdir, path).replace('\\', '/') | 776 | worktree = os.path.join(self.topdir, path).replace('\\', '/') |
| 756 | gitdir = os.path.join(self.repodir, 'projects', '%s.git' % path) | 777 | gitdir = os.path.join(self.repodir, 'projects', '%s.git' % path) |
| 757 | return relpath, worktree, gitdir | 778 | objdir = os.path.join(self.repodir, 'project-objects', '%s.git' % name) |
| 779 | return relpath, worktree, gitdir, objdir | ||
| 780 | |||
| 781 | def GetProjectsWithName(self, name): | ||
| 782 | return self._projects.get(name, []) | ||
| 758 | 783 | ||
| 759 | def GetSubprojectName(self, parent, submodule_path): | 784 | def GetSubprojectName(self, parent, submodule_path): |
| 760 | return os.path.join(parent.name, submodule_path) | 785 | return os.path.join(parent.name, submodule_path) |
| @@ -765,14 +790,15 @@ class XmlManifest(object): | |||
| 765 | def _UnjoinRelpath(self, parent_relpath, relpath): | 790 | def _UnjoinRelpath(self, parent_relpath, relpath): |
| 766 | return os.path.relpath(relpath, parent_relpath) | 791 | return os.path.relpath(relpath, parent_relpath) |
| 767 | 792 | ||
| 768 | def GetSubprojectPaths(self, parent, path): | 793 | def GetSubprojectPaths(self, parent, name, path): |
| 769 | relpath = self._JoinRelpath(parent.relpath, path) | 794 | relpath = self._JoinRelpath(parent.relpath, path) |
| 770 | gitdir = os.path.join(parent.gitdir, 'subprojects', '%s.git' % path) | 795 | gitdir = os.path.join(parent.gitdir, 'subprojects', '%s.git' % path) |
| 796 | objdir = os.path.join(parent.gitdir, 'subproject-objects', '%s.git' % name) | ||
| 771 | if self.IsMirror: | 797 | if self.IsMirror: |
| 772 | worktree = None | 798 | worktree = None |
| 773 | else: | 799 | else: |
| 774 | worktree = os.path.join(parent.worktree, path).replace('\\', '/') | 800 | worktree = os.path.join(parent.worktree, path).replace('\\', '/') |
| 775 | return relpath, worktree, gitdir | 801 | return relpath, worktree, gitdir, objdir |
| 776 | 802 | ||
| 777 | def _ParseCopyFile(self, project, node): | 803 | def _ParseCopyFile(self, project, node): |
| 778 | src = self._reqatt(node, 'src') | 804 | src = self._reqatt(node, 'src') |
| @@ -488,6 +488,7 @@ class Project(object): | |||
| 488 | name, | 488 | name, |
| 489 | remote, | 489 | remote, |
| 490 | gitdir, | 490 | gitdir, |
| 491 | objdir, | ||
| 491 | worktree, | 492 | worktree, |
| 492 | relpath, | 493 | relpath, |
| 493 | revisionExpr, | 494 | revisionExpr, |
| @@ -508,6 +509,7 @@ class Project(object): | |||
| 508 | name: The `name` attribute of manifest.xml's project element. | 509 | name: The `name` attribute of manifest.xml's project element. |
| 509 | remote: RemoteSpec object specifying its remote's properties. | 510 | remote: RemoteSpec object specifying its remote's properties. |
| 510 | gitdir: Absolute path of git directory. | 511 | gitdir: Absolute path of git directory. |
| 512 | objdir: Absolute path of directory to store git objects. | ||
| 511 | worktree: Absolute path of git working tree. | 513 | worktree: Absolute path of git working tree. |
| 512 | relpath: Relative path of git working tree to repo's top directory. | 514 | relpath: Relative path of git working tree to repo's top directory. |
| 513 | revisionExpr: The `revision` attribute of manifest.xml's project element. | 515 | revisionExpr: The `revision` attribute of manifest.xml's project element. |
| @@ -526,6 +528,7 @@ class Project(object): | |||
| 526 | self.name = name | 528 | self.name = name |
| 527 | self.remote = remote | 529 | self.remote = remote |
| 528 | self.gitdir = gitdir.replace('\\', '/') | 530 | self.gitdir = gitdir.replace('\\', '/') |
| 531 | self.objdir = objdir.replace('\\', '/') | ||
| 529 | if worktree: | 532 | if worktree: |
| 530 | self.worktree = worktree.replace('\\', '/') | 533 | self.worktree = worktree.replace('\\', '/') |
| 531 | else: | 534 | else: |
| @@ -558,11 +561,12 @@ class Project(object): | |||
| 558 | defaults = self.manifest.globalConfig) | 561 | defaults = self.manifest.globalConfig) |
| 559 | 562 | ||
| 560 | if self.worktree: | 563 | if self.worktree: |
| 561 | self.work_git = self._GitGetByExec(self, bare=False) | 564 | self.work_git = self._GitGetByExec(self, bare=False, gitdir=gitdir) |
| 562 | else: | 565 | else: |
| 563 | self.work_git = None | 566 | self.work_git = None |
| 564 | self.bare_git = self._GitGetByExec(self, bare=True) | 567 | self.bare_git = self._GitGetByExec(self, bare=True, gitdir=gitdir) |
| 565 | self.bare_ref = GitRefs(gitdir) | 568 | self.bare_ref = GitRefs(gitdir) |
| 569 | self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=objdir) | ||
| 566 | self.dest_branch = dest_branch | 570 | self.dest_branch = dest_branch |
| 567 | 571 | ||
| 568 | # This will be filled in if a project is later identified to be the | 572 | # This will be filled in if a project is later identified to be the |
| @@ -1117,6 +1121,7 @@ class Project(object): | |||
| 1117 | """Perform only the local IO portion of the sync process. | 1121 | """Perform only the local IO portion of the sync process. |
| 1118 | Network access is not required. | 1122 | Network access is not required. |
| 1119 | """ | 1123 | """ |
| 1124 | self._InitWorkTree() | ||
| 1120 | all_refs = self.bare_ref.all | 1125 | all_refs = self.bare_ref.all |
| 1121 | self.CleanPublishedCache(all_refs) | 1126 | self.CleanPublishedCache(all_refs) |
| 1122 | revid = self.GetRevisionId(all_refs) | 1127 | revid = self.GetRevisionId(all_refs) |
| @@ -1125,7 +1130,6 @@ class Project(object): | |||
| 1125 | self._FastForward(revid) | 1130 | self._FastForward(revid) |
| 1126 | self._CopyFiles() | 1131 | self._CopyFiles() |
| 1127 | 1132 | ||
| 1128 | self._InitWorkTree() | ||
| 1129 | head = self.work_git.GetHead() | 1133 | head = self.work_git.GetHead() |
| 1130 | if head.startswith(R_HEADS): | 1134 | if head.startswith(R_HEADS): |
| 1131 | branch = head[len(R_HEADS):] | 1135 | branch = head[len(R_HEADS):] |
| @@ -1592,11 +1596,13 @@ class Project(object): | |||
| 1592 | return result | 1596 | return result |
| 1593 | for rev, path, url in self._GetSubmodules(): | 1597 | for rev, path, url in self._GetSubmodules(): |
| 1594 | name = self.manifest.GetSubprojectName(self, path) | 1598 | name = self.manifest.GetSubprojectName(self, path) |
| 1595 | project = self.manifest.projects.get(name) | 1599 | relpath, worktree, gitdir, objdir = \ |
| 1600 | self.manifest.GetSubprojectPaths(self, name, path) | ||
| 1601 | project = self.manifest.paths.get(relpath) | ||
| 1596 | if project: | 1602 | if project: |
| 1597 | result.extend(project.GetDerivedSubprojects()) | 1603 | result.extend(project.GetDerivedSubprojects()) |
| 1598 | continue | 1604 | continue |
| 1599 | relpath, worktree, gitdir = self.manifest.GetSubprojectPaths(self, path) | 1605 | |
| 1600 | remote = RemoteSpec(self.remote.name, | 1606 | remote = RemoteSpec(self.remote.name, |
| 1601 | url = url, | 1607 | url = url, |
| 1602 | review = self.remote.review) | 1608 | review = self.remote.review) |
| @@ -1604,6 +1610,7 @@ class Project(object): | |||
| 1604 | name = name, | 1610 | name = name, |
| 1605 | remote = remote, | 1611 | remote = remote, |
| 1606 | gitdir = gitdir, | 1612 | gitdir = gitdir, |
| 1613 | objdir = objdir, | ||
| 1607 | worktree = worktree, | 1614 | worktree = worktree, |
| 1608 | relpath = relpath, | 1615 | relpath = relpath, |
| 1609 | revisionExpr = self.revisionExpr, | 1616 | revisionExpr = self.revisionExpr, |
| @@ -1966,8 +1973,17 @@ class Project(object): | |||
| 1966 | 1973 | ||
| 1967 | def _InitGitDir(self, mirror_git=None): | 1974 | def _InitGitDir(self, mirror_git=None): |
| 1968 | if not os.path.exists(self.gitdir): | 1975 | if not os.path.exists(self.gitdir): |
| 1969 | os.makedirs(self.gitdir) | 1976 | |
| 1970 | self.bare_git.init() | 1977 | # Initialize the bare repository, which contains all of the objects. |
| 1978 | if not os.path.exists(self.objdir): | ||
| 1979 | os.makedirs(self.objdir) | ||
| 1980 | self.bare_objdir.init() | ||
| 1981 | |||
| 1982 | # If we have a separate directory to hold refs, initialize it as well. | ||
| 1983 | if self.objdir != self.gitdir: | ||
| 1984 | os.makedirs(self.gitdir) | ||
| 1985 | self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False, | ||
| 1986 | copy_all=True) | ||
| 1971 | 1987 | ||
| 1972 | mp = self.manifest.manifestProject | 1988 | mp = self.manifest.manifestProject |
| 1973 | ref_dir = mp.config.GetString('repo.reference') or '' | 1989 | ref_dir = mp.config.GetString('repo.reference') or '' |
| @@ -2083,33 +2099,61 @@ class Project(object): | |||
| 2083 | msg = 'manifest set to %s' % self.revisionExpr | 2099 | msg = 'manifest set to %s' % self.revisionExpr |
| 2084 | self.bare_git.symbolic_ref('-m', msg, ref, dst) | 2100 | self.bare_git.symbolic_ref('-m', msg, ref, dst) |
| 2085 | 2101 | ||
| 2102 | def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all): | ||
| 2103 | """Update |dotgit| to reference |gitdir|, using symlinks where possible. | ||
| 2104 | |||
| 2105 | Args: | ||
| 2106 | gitdir: The bare git repository. Must already be initialized. | ||
| 2107 | dotgit: The repository you would like to initialize. | ||
| 2108 | share_refs: If true, |dotgit| will store its refs under |gitdir|. | ||
| 2109 | Only one work tree can store refs under a given |gitdir|. | ||
| 2110 | copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|. | ||
| 2111 | This saves you the effort of initializing |dotgit| yourself. | ||
| 2112 | """ | ||
| 2113 | # These objects can be shared between several working trees. | ||
| 2114 | symlink_files = ['description', 'info'] | ||
| 2115 | symlink_dirs = ['hooks', 'objects', 'rr-cache', 'svn'] | ||
| 2116 | if share_refs: | ||
| 2117 | # These objects can only be used by a single working tree. | ||
| 2118 | symlink_files += ['config', 'packed-refs'] | ||
| 2119 | symlink_dirs += ['logs', 'refs'] | ||
| 2120 | to_symlink = symlink_files + symlink_dirs | ||
| 2121 | |||
| 2122 | to_copy = [] | ||
| 2123 | if copy_all: | ||
| 2124 | to_copy = os.listdir(gitdir) | ||
| 2125 | |||
| 2126 | for name in set(to_copy).union(to_symlink): | ||
| 2127 | try: | ||
| 2128 | src = os.path.realpath(os.path.join(gitdir, name)) | ||
| 2129 | dst = os.path.realpath(os.path.join(dotgit, name)) | ||
| 2130 | |||
| 2131 | if os.path.lexists(dst) and not os.path.islink(dst): | ||
| 2132 | raise GitError('cannot overwrite a local work tree') | ||
| 2133 | |||
| 2134 | # If the source dir doesn't exist, create an empty dir. | ||
| 2135 | if name in symlink_dirs and not os.path.lexists(src): | ||
| 2136 | os.makedirs(src) | ||
| 2137 | |||
| 2138 | if name in to_symlink: | ||
| 2139 | os.symlink(os.path.relpath(src, os.path.dirname(dst)), dst) | ||
| 2140 | elif copy_all and not os.path.islink(dst): | ||
| 2141 | if os.path.isdir(src): | ||
| 2142 | shutil.copytree(src, dst) | ||
| 2143 | elif os.path.isfile(src): | ||
| 2144 | shutil.copy(src, dst) | ||
| 2145 | except OSError as e: | ||
| 2146 | if e.errno == errno.EPERM: | ||
| 2147 | raise GitError('filesystem must support symlinks') | ||
| 2148 | else: | ||
| 2149 | raise | ||
| 2150 | |||
| 2086 | def _InitWorkTree(self): | 2151 | def _InitWorkTree(self): |
| 2087 | dotgit = os.path.join(self.worktree, '.git') | 2152 | dotgit = os.path.join(self.worktree, '.git') |
| 2088 | if not os.path.exists(dotgit): | 2153 | if not os.path.exists(dotgit): |
| 2089 | os.makedirs(dotgit) | 2154 | os.makedirs(dotgit) |
| 2090 | 2155 | self._ReferenceGitDir(self.gitdir, dotgit, share_refs=True, | |
| 2091 | for name in ['config', | 2156 | copy_all=False) |
| 2092 | 'description', | ||
| 2093 | 'hooks', | ||
| 2094 | 'info', | ||
| 2095 | 'logs', | ||
| 2096 | 'objects', | ||
| 2097 | 'packed-refs', | ||
| 2098 | 'refs', | ||
| 2099 | 'rr-cache', | ||
| 2100 | 'svn']: | ||
| 2101 | try: | ||
| 2102 | src = os.path.join(self.gitdir, name) | ||
| 2103 | dst = os.path.join(dotgit, name) | ||
| 2104 | if os.path.islink(dst) or not os.path.exists(dst): | ||
| 2105 | os.symlink(os.path.relpath(src, os.path.dirname(dst)), dst) | ||
| 2106 | else: | ||
| 2107 | raise GitError('cannot overwrite a local work tree') | ||
| 2108 | except OSError as e: | ||
| 2109 | if e.errno == errno.EPERM: | ||
| 2110 | raise GitError('filesystem must support symlinks') | ||
| 2111 | else: | ||
| 2112 | raise | ||
| 2113 | 2157 | ||
| 2114 | _lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId()) | 2158 | _lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId()) |
| 2115 | 2159 | ||
| @@ -2119,14 +2163,10 @@ class Project(object): | |||
| 2119 | if GitCommand(self, cmd).Wait() != 0: | 2163 | if GitCommand(self, cmd).Wait() != 0: |
| 2120 | raise GitError("cannot initialize work tree") | 2164 | raise GitError("cannot initialize work tree") |
| 2121 | 2165 | ||
| 2122 | rr_cache = os.path.join(self.gitdir, 'rr-cache') | ||
| 2123 | if not os.path.exists(rr_cache): | ||
| 2124 | os.makedirs(rr_cache) | ||
| 2125 | |||
| 2126 | self._CopyFiles() | 2166 | self._CopyFiles() |
| 2127 | 2167 | ||
| 2128 | def _gitdir_path(self, path): | 2168 | def _gitdir_path(self, path): |
| 2129 | return os.path.join(self.gitdir, path) | 2169 | return os.path.realpath(os.path.join(self.gitdir, path)) |
| 2130 | 2170 | ||
| 2131 | def _revlist(self, *args, **kw): | 2171 | def _revlist(self, *args, **kw): |
| 2132 | a = [] | 2172 | a = [] |
| @@ -2139,9 +2179,10 @@ class Project(object): | |||
| 2139 | return self.bare_ref.all | 2179 | return self.bare_ref.all |
| 2140 | 2180 | ||
| 2141 | class _GitGetByExec(object): | 2181 | class _GitGetByExec(object): |
| 2142 | def __init__(self, project, bare): | 2182 | def __init__(self, project, bare, gitdir): |
| 2143 | self._project = project | 2183 | self._project = project |
| 2144 | self._bare = bare | 2184 | self._bare = bare |
| 2185 | self._gitdir = gitdir | ||
| 2145 | 2186 | ||
| 2146 | def LsOthers(self): | 2187 | def LsOthers(self): |
| 2147 | p = GitCommand(self._project, | 2188 | p = GitCommand(self._project, |
| @@ -2150,6 +2191,7 @@ class Project(object): | |||
| 2150 | '--others', | 2191 | '--others', |
| 2151 | '--exclude-standard'], | 2192 | '--exclude-standard'], |
| 2152 | bare = False, | 2193 | bare = False, |
| 2194 | gitdir=self._gitdir, | ||
| 2153 | capture_stdout = True, | 2195 | capture_stdout = True, |
| 2154 | capture_stderr = True) | 2196 | capture_stderr = True) |
| 2155 | if p.Wait() == 0: | 2197 | if p.Wait() == 0: |
| @@ -2165,6 +2207,7 @@ class Project(object): | |||
| 2165 | cmd.extend(args) | 2207 | cmd.extend(args) |
| 2166 | p = GitCommand(self._project, | 2208 | p = GitCommand(self._project, |
| 2167 | cmd, | 2209 | cmd, |
| 2210 | gitdir=self._gitdir, | ||
| 2168 | bare = False, | 2211 | bare = False, |
| 2169 | capture_stdout = True, | 2212 | capture_stdout = True, |
| 2170 | capture_stderr = True) | 2213 | capture_stderr = True) |
| @@ -2274,6 +2317,7 @@ class Project(object): | |||
| 2274 | p = GitCommand(self._project, | 2317 | p = GitCommand(self._project, |
| 2275 | cmdv, | 2318 | cmdv, |
| 2276 | bare = self._bare, | 2319 | bare = self._bare, |
| 2320 | gitdir=self._gitdir, | ||
| 2277 | capture_stdout = True, | 2321 | capture_stdout = True, |
| 2278 | capture_stderr = True) | 2322 | capture_stderr = True) |
| 2279 | r = [] | 2323 | r = [] |
| @@ -2326,6 +2370,7 @@ class Project(object): | |||
| 2326 | p = GitCommand(self._project, | 2370 | p = GitCommand(self._project, |
| 2327 | cmdv, | 2371 | cmdv, |
| 2328 | bare = self._bare, | 2372 | bare = self._bare, |
| 2373 | gitdir=self._gitdir, | ||
| 2329 | capture_stdout = True, | 2374 | capture_stdout = True, |
| 2330 | capture_stderr = True) | 2375 | capture_stderr = True) |
| 2331 | if p.Wait() != 0: | 2376 | if p.Wait() != 0: |
| @@ -2459,6 +2504,7 @@ class MetaProject(Project): | |||
| 2459 | manifest = manifest, | 2504 | manifest = manifest, |
| 2460 | name = name, | 2505 | name = name, |
| 2461 | gitdir = gitdir, | 2506 | gitdir = gitdir, |
| 2507 | objdir = gitdir, | ||
| 2462 | worktree = worktree, | 2508 | worktree = worktree, |
| 2463 | remote = RemoteSpec('origin'), | 2509 | remote = RemoteSpec('origin'), |
| 2464 | relpath = '.repo/%s' % name, | 2510 | 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 0279ff60..5e7385db 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)) |
| @@ -304,20 +325,24 @@ later is required to fix a server side protocol bug. | |||
| 304 | else: | 325 | else: |
| 305 | sys.exit(1) | 326 | sys.exit(1) |
| 306 | else: | 327 | else: |
| 328 | objdir_project_map = dict() | ||
| 329 | for project in projects: | ||
| 330 | objdir_project_map.setdefault(project.objdir, []).append(project) | ||
| 331 | |||
| 307 | threads = set() | 332 | threads = set() |
| 308 | lock = _threading.Lock() | 333 | lock = _threading.Lock() |
| 309 | sem = _threading.Semaphore(self.jobs) | 334 | sem = _threading.Semaphore(self.jobs) |
| 310 | err_event = _threading.Event() | 335 | err_event = _threading.Event() |
| 311 | for project in projects: | 336 | for project_list in objdir_project_map.values(): |
| 312 | # Check for any errors before starting any new threads. | 337 | # Check for any errors before starting any new threads. |
| 313 | # ...we'll let existing threads finish, though. | 338 | # ...we'll let existing threads finish, though. |
| 314 | if err_event.isSet(): | 339 | if err_event.isSet(): |
| 315 | break | 340 | break |
| 316 | 341 | ||
| 317 | sem.acquire() | 342 | sem.acquire() |
| 318 | t = _threading.Thread(target = self._FetchHelper, | 343 | t = _threading.Thread(target = self._FetchProjectList, |
| 319 | args = (opt, | 344 | args = (opt, |
| 320 | project, | 345 | project_list, |
| 321 | lock, | 346 | lock, |
| 322 | fetched, | 347 | fetched, |
| 323 | pm, | 348 | pm, |
| @@ -345,6 +370,10 @@ later is required to fix a server side protocol bug. | |||
| 345 | return fetched | 370 | return fetched |
| 346 | 371 | ||
| 347 | def _GCProjects(self, projects): | 372 | def _GCProjects(self, projects): |
| 373 | gitdirs = {} | ||
| 374 | for project in projects: | ||
| 375 | gitdirs[project.gitdir] = project.bare_git | ||
| 376 | |||
| 348 | has_dash_c = git_require((1, 7, 2)) | 377 | has_dash_c = git_require((1, 7, 2)) |
| 349 | if multiprocessing and has_dash_c: | 378 | if multiprocessing and has_dash_c: |
| 350 | cpu_count = multiprocessing.cpu_count() | 379 | cpu_count = multiprocessing.cpu_count() |
| @@ -353,8 +382,8 @@ later is required to fix a server side protocol bug. | |||
| 353 | jobs = min(self.jobs, cpu_count) | 382 | jobs = min(self.jobs, cpu_count) |
| 354 | 383 | ||
| 355 | if jobs < 2: | 384 | if jobs < 2: |
| 356 | for project in projects: | 385 | for bare_git in gitdirs.values(): |
| 357 | project.bare_git.gc('--auto') | 386 | bare_git.gc('--auto') |
| 358 | return | 387 | return |
| 359 | 388 | ||
| 360 | config = {'pack.threads': cpu_count / jobs if cpu_count > jobs else 1} | 389 | config = {'pack.threads': cpu_count / jobs if cpu_count > jobs else 1} |
| @@ -363,10 +392,10 @@ later is required to fix a server side protocol bug. | |||
| 363 | sem = _threading.Semaphore(jobs) | 392 | sem = _threading.Semaphore(jobs) |
| 364 | err_event = _threading.Event() | 393 | err_event = _threading.Event() |
| 365 | 394 | ||
| 366 | def GC(project): | 395 | def GC(bare_git): |
| 367 | try: | 396 | try: |
| 368 | try: | 397 | try: |
| 369 | project.bare_git.gc('--auto', config=config) | 398 | bare_git.gc('--auto', config=config) |
| 370 | except GitError: | 399 | except GitError: |
| 371 | err_event.set() | 400 | err_event.set() |
| 372 | except: | 401 | except: |
| @@ -375,11 +404,11 @@ later is required to fix a server side protocol bug. | |||
| 375 | finally: | 404 | finally: |
| 376 | sem.release() | 405 | sem.release() |
| 377 | 406 | ||
| 378 | for project in projects: | 407 | for bare_git in gitdirs.values(): |
| 379 | if err_event.isSet(): | 408 | if err_event.isSet(): |
| 380 | break | 409 | break |
| 381 | sem.acquire() | 410 | sem.acquire() |
| 382 | t = _threading.Thread(target=GC, args=(project,)) | 411 | t = _threading.Thread(target=GC, args=(bare_git,)) |
| 383 | t.daemon = True | 412 | t.daemon = True |
| 384 | threads.add(t) | 413 | threads.add(t) |
| 385 | t.start() | 414 | t.start() |
| @@ -419,12 +448,13 @@ later is required to fix a server side protocol bug. | |||
| 419 | if path not in new_project_paths: | 448 | if path not in new_project_paths: |
| 420 | # If the path has already been deleted, we don't need to do it | 449 | # If the path has already been deleted, we don't need to do it |
| 421 | if os.path.exists(self.manifest.topdir + '/' + path): | 450 | if os.path.exists(self.manifest.topdir + '/' + path): |
| 451 | gitdir = os.path.join(self.manifest.topdir, path, '.git') | ||
| 422 | project = Project( | 452 | project = Project( |
| 423 | manifest = self.manifest, | 453 | manifest = self.manifest, |
| 424 | name = path, | 454 | name = path, |
| 425 | remote = RemoteSpec('origin'), | 455 | remote = RemoteSpec('origin'), |
| 426 | gitdir = os.path.join(self.manifest.topdir, | 456 | gitdir = gitdir, |
| 427 | path, '.git'), | 457 | objdir = gitdir, |
| 428 | worktree = os.path.join(self.manifest.topdir, path), | 458 | worktree = os.path.join(self.manifest.topdir, path), |
| 429 | relpath = path, | 459 | relpath = path, |
| 430 | revisionExpr = 'HEAD', | 460 | revisionExpr = 'HEAD', |
diff --git a/subcmds/upload.py b/subcmds/upload.py index 7f7585ae..56212408 100644 --- a/subcmds/upload.py +++ b/subcmds/upload.py | |||
| @@ -432,8 +432,10 @@ Gerrit Code Review: http://code.google.com/p/gerrit/ | |||
| 432 | hook = RepoHook('pre-upload', self.manifest.repo_hooks_project, | 432 | hook = RepoHook('pre-upload', self.manifest.repo_hooks_project, |
| 433 | self.manifest.topdir, abort_if_user_denies=True) | 433 | self.manifest.topdir, abort_if_user_denies=True) |
| 434 | pending_proj_names = [project.name for (project, avail) in pending] | 434 | pending_proj_names = [project.name for (project, avail) in pending] |
| 435 | pending_worktrees = [project.worktree for (project, avail) in pending] | ||
| 435 | try: | 436 | try: |
| 436 | hook.Run(opt.allow_all_hooks, project_list=pending_proj_names) | 437 | hook.Run(opt.allow_all_hooks, project_list=pending_proj_names, |
| 438 | worktree_list=pending_worktrees) | ||
| 437 | except HookError as e: | 439 | except HookError as e: |
| 438 | print("ERROR: %s" % str(e), file=sys.stderr) | 440 | print("ERROR: %s" % str(e), file=sys.stderr) |
| 439 | return | 441 | return |
