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 |
