diff options
| -rw-r--r-- | command.py | 27 | ||||
| -rw-r--r-- | docs/manifest_submodule.txt | 136 | ||||
| -rw-r--r-- | docs/manifest_xml.txt (renamed from docs/manifest-format.txt) | 2 | ||||
| -rw-r--r-- | git_config.py | 8 | ||||
| -rw-r--r-- | git_refs.py | 1 | ||||
| -rwxr-xr-x | main.py | 4 | ||||
| -rw-r--r-- | manifest.py | 59 | ||||
| -rw-r--r-- | manifest_loader.py | 34 | ||||
| -rw-r--r-- | manifest_submodule.py | 481 | ||||
| -rw-r--r-- | manifest_xml.py | 80 | ||||
| -rw-r--r-- | project.py | 77 | ||||
| -rwxr-xr-x | repo | 7 | ||||
| -rw-r--r-- | subcmds/help.py | 1 | ||||
| -rw-r--r-- | subcmds/init.py | 76 | ||||
| -rw-r--r-- | subcmds/manifest.py | 81 | ||||
| -rw-r--r-- | subcmds/rebase.py | 2 | ||||
| -rw-r--r-- | subcmds/sync.py | 23 |
17 files changed, 972 insertions, 127 deletions
| @@ -17,6 +17,8 @@ import os | |||
| 17 | import optparse | 17 | import optparse |
| 18 | import sys | 18 | import sys |
| 19 | 19 | ||
| 20 | import manifest_loader | ||
| 21 | |||
| 20 | from error import NoSuchProjectError | 22 | from error import NoSuchProjectError |
| 21 | 23 | ||
| 22 | class Command(object): | 24 | class Command(object): |
| @@ -24,7 +26,6 @@ class Command(object): | |||
| 24 | """ | 26 | """ |
| 25 | 27 | ||
| 26 | common = False | 28 | common = False |
| 27 | manifest = None | ||
| 28 | _optparse = None | 29 | _optparse = None |
| 29 | 30 | ||
| 30 | def WantPager(self, opt): | 31 | def WantPager(self, opt): |
| @@ -57,10 +58,25 @@ class Command(object): | |||
| 57 | """ | 58 | """ |
| 58 | raise NotImplementedError | 59 | raise NotImplementedError |
| 59 | 60 | ||
| 61 | @property | ||
| 62 | def manifest(self): | ||
| 63 | return self.GetManifest() | ||
| 64 | |||
| 65 | def GetManifest(self, reparse=False, type=None): | ||
| 66 | return manifest_loader.GetManifest(self.repodir, | ||
| 67 | reparse=reparse, | ||
| 68 | type=type) | ||
| 69 | |||
| 60 | def GetProjects(self, args, missing_ok=False): | 70 | def GetProjects(self, args, missing_ok=False): |
| 61 | """A list of projects that match the arguments. | 71 | """A list of projects that match the arguments. |
| 62 | """ | 72 | """ |
| 63 | all = self.manifest.projects | 73 | all = self.manifest.projects |
| 74 | |||
| 75 | mp = self.manifest.manifestProject | ||
| 76 | if mp.relpath == '.': | ||
| 77 | all = dict(all) | ||
| 78 | all[mp.name] = mp | ||
| 79 | |||
| 64 | result = [] | 80 | result = [] |
| 65 | 81 | ||
| 66 | if not args: | 82 | if not args: |
| @@ -81,7 +97,9 @@ class Command(object): | |||
| 81 | for p in all.values(): | 97 | for p in all.values(): |
| 82 | by_path[p.worktree] = p | 98 | by_path[p.worktree] = p |
| 83 | 99 | ||
| 84 | if os.path.exists(path): | 100 | try: |
| 101 | project = by_path[path] | ||
| 102 | except KeyError: | ||
| 85 | oldpath = None | 103 | oldpath = None |
| 86 | while path \ | 104 | while path \ |
| 87 | and path != oldpath \ | 105 | and path != oldpath \ |
| @@ -92,11 +110,6 @@ class Command(object): | |||
| 92 | except KeyError: | 110 | except KeyError: |
| 93 | oldpath = path | 111 | oldpath = path |
| 94 | path = os.path.dirname(path) | 112 | path = os.path.dirname(path) |
| 95 | else: | ||
| 96 | try: | ||
| 97 | project = by_path[path] | ||
| 98 | except KeyError: | ||
| 99 | pass | ||
| 100 | 113 | ||
| 101 | if not project: | 114 | if not project: |
| 102 | raise NoSuchProjectError(arg) | 115 | raise NoSuchProjectError(arg) |
diff --git a/docs/manifest_submodule.txt b/docs/manifest_submodule.txt new file mode 100644 index 00000000..1718284b --- /dev/null +++ b/docs/manifest_submodule.txt | |||
| @@ -0,0 +1,136 @@ | |||
| 1 | repo Manifest Format (submodule) | ||
| 2 | ================================ | ||
| 3 | |||
| 4 | A repo manifest describes the structure of a repo client; that is | ||
| 5 | the directories that are visible and where they should be obtained | ||
| 6 | from with git. | ||
| 7 | |||
| 8 | The basic structure of a manifest is a bare Git repository holding | ||
| 9 | a 'gitmodules' file in the top level directory, and one or more | ||
| 10 | gitlink references pointing at commits from the referenced projects. | ||
| 11 | This is the same structure as used by 'git submodule'. | ||
| 12 | |||
| 13 | Manifests are inherently version controlled, since they are kept | ||
| 14 | within a Git repository. Updates to manifests are automatically | ||
| 15 | obtained by clients during `repo sync`. | ||
| 16 | |||
| 17 | .gitmodules | ||
| 18 | =========== | ||
| 19 | |||
| 20 | The '.gitmodules' file, located in the top-level directory of the | ||
| 21 | client's working tree (or manifest repository), is a text file with | ||
| 22 | a syntax matching the requirements of 'git config'. | ||
| 23 | |||
| 24 | This file contains one subsection per project (also called a | ||
| 25 | submodule by git), and the subsection value is a unique name to | ||
| 26 | describe the project. Each submodule section must contain the | ||
| 27 | following required keys: | ||
| 28 | |||
| 29 | * path | ||
| 30 | * url | ||
| 31 | |||
| 32 | submodule.<name>.path | ||
| 33 | --------------------- | ||
| 34 | |||
| 35 | Defines the path, relative to the top-level directory of the client's | ||
| 36 | working tree, where the project is expected to be checked out. The | ||
| 37 | path name must not end with a '/'. All paths must be unique within | ||
| 38 | the .gitmodules file. | ||
| 39 | |||
| 40 | At the specified path within the manifest repository a gitlink | ||
| 41 | tree entry (an entry with file mode 160000) must exist referencing | ||
| 42 | a commit SHA-1 from the project. This tree entry specifies the | ||
| 43 | exact version of the project that `repo sync` will synchronize the | ||
| 44 | client's working tree to. | ||
| 45 | |||
| 46 | submodule.<name>.url | ||
| 47 | -------------------- | ||
| 48 | |||
| 49 | Defines a URL from where the project repository can be cloned. | ||
| 50 | By default `repo sync` will clone from this URL whenever a user | ||
| 51 | needs to access this project. | ||
| 52 | |||
| 53 | submodule.<name>.revision | ||
| 54 | ------------------------- | ||
| 55 | |||
| 56 | Name of the branch in the project repository that Gerrit Code Review | ||
| 57 | should automatically refresh the project's gitlink entry from. | ||
| 58 | |||
| 59 | If set, during submit of a change within the referenced project, | ||
| 60 | Gerrit Code Review will automatically update the manifest | ||
| 61 | repository's corresponding gitlink to the new commit SHA-1 of | ||
| 62 | this branch. | ||
| 63 | |||
| 64 | Valid values are a short branch name (e.g. 'master'), a full ref | ||
| 65 | name (e.g. 'refs/heads/master'), or '.' to request using the same | ||
| 66 | branch name as the manifest branch itself. Since '.' automatically | ||
| 67 | uses the manifest branch, '.' is the recommended value. | ||
| 68 | |||
| 69 | If this key is not set, Gerrit Code Review will NOT automatically | ||
| 70 | update the gitlink. An unset key requires the manifest maintainer | ||
| 71 | to manually update the gitlink when it is necessary to reference | ||
| 72 | a different revision of the project. | ||
| 73 | |||
| 74 | submodule.<name>.update | ||
| 75 | ----------------------- | ||
| 76 | |||
| 77 | This key is not supported by repo. If set, it will be ignored. | ||
| 78 | |||
| 79 | repo.notice | ||
| 80 | ----------- | ||
| 81 | |||
| 82 | A message displayed when repo sync uses this manifest. | ||
| 83 | |||
| 84 | |||
| 85 | .review | ||
| 86 | ======= | ||
| 87 | |||
| 88 | The optional '.review' file, located in the top-level directory of | ||
| 89 | the client's working tree (or manifest repository), is a text file | ||
| 90 | with a syntax matching the requirements of 'git config'. | ||
| 91 | |||
| 92 | This file describes how `repo upload` should interact with the | ||
| 93 | project's preferred code review system. | ||
| 94 | |||
| 95 | review.url | ||
| 96 | ---------- | ||
| 97 | |||
| 98 | URL of the default Gerrit Code Review server. If a project does | ||
| 99 | not have a specific URL in the '.review' file, this default URL | ||
| 100 | will be used instead. | ||
| 101 | |||
| 102 | review.<name>.url | ||
| 103 | ----------------- | ||
| 104 | |||
| 105 | Project specific URL of the Gerrit Code Review server, for the | ||
| 106 | submodule whose project name is <name>. | ||
| 107 | |||
| 108 | Example | ||
| 109 | ======= | ||
| 110 | |||
| 111 | $ cat .gitmodules | ||
| 112 | [submodule "app/Clock"] | ||
| 113 | path = clock | ||
| 114 | url = git://vcs.example.com/ClockWidget.git | ||
| 115 | revision = . | ||
| 116 | [submodule "app/Browser"] | ||
| 117 | path = net/browser | ||
| 118 | url = git://netgroup.example.com/network/web/Browser.git | ||
| 119 | revision = . | ||
| 120 | |||
| 121 | $ cat .review | ||
| 122 | [review] | ||
| 123 | url = vcs-gerrit.example.com | ||
| 124 | [review "app/Browser"] | ||
| 125 | url = netgroup.example.com | ||
| 126 | |||
| 127 | In the above example, the app/Clock project will send its code | ||
| 128 | reviews to the default server, vcs-gerrit.example.com, while | ||
| 129 | app/Browser will send its code reviews to netgroup.example.com. | ||
| 130 | |||
| 131 | See Also | ||
| 132 | ======== | ||
| 133 | |||
| 134 | * http://www.kernel.org/pub/software/scm/git/docs/gitmodules.html | ||
| 135 | * http://www.kernel.org/pub/software/scm/git/docs/git-config.html | ||
| 136 | * http://code.google.com/p/gerrit/ | ||
diff --git a/docs/manifest-format.txt b/docs/manifest_xml.txt index 2e1c8c35..37fbd5cd 100644 --- a/docs/manifest-format.txt +++ b/docs/manifest_xml.txt | |||
| @@ -37,7 +37,7 @@ following DTD: | |||
| 37 | <!ELEMENT default (EMPTY)> | 37 | <!ELEMENT default (EMPTY)> |
| 38 | <!ATTLIST default remote IDREF #IMPLIED> | 38 | <!ATTLIST default remote IDREF #IMPLIED> |
| 39 | <!ATTLIST default revision CDATA #IMPLIED> | 39 | <!ATTLIST default revision CDATA #IMPLIED> |
| 40 | 40 | ||
| 41 | <!ELEMENT manifest-server (EMPTY)> | 41 | <!ELEMENT manifest-server (EMPTY)> |
| 42 | <!ATTLIST url CDATA #REQUIRED> | 42 | <!ATTLIST url CDATA #REQUIRED> |
| 43 | 43 | ||
diff --git a/git_config.py b/git_config.py index 19c19f19..ff815e35 100644 --- a/git_config.py +++ b/git_config.py | |||
| @@ -80,6 +80,14 @@ class GitConfig(object): | |||
| 80 | else: | 80 | else: |
| 81 | self._pickle = pickleFile | 81 | self._pickle = pickleFile |
| 82 | 82 | ||
| 83 | def ClearCache(self): | ||
| 84 | if os.path.exists(self._pickle): | ||
| 85 | os.remove(self._pickle) | ||
| 86 | self._cache_dict = None | ||
| 87 | self._section_dict = None | ||
| 88 | self._remotes = {} | ||
| 89 | self._branches = {} | ||
| 90 | |||
| 83 | def Has(self, name, include_defaults = True): | 91 | def Has(self, name, include_defaults = True): |
| 84 | """Return true if this configuration file has the key. | 92 | """Return true if this configuration file has the key. |
| 85 | """ | 93 | """ |
diff --git a/git_refs.py b/git_refs.py index ac8ed0c1..b24a0b4e 100644 --- a/git_refs.py +++ b/git_refs.py | |||
| @@ -21,7 +21,6 @@ HEAD = 'HEAD' | |||
| 21 | R_HEADS = 'refs/heads/' | 21 | R_HEADS = 'refs/heads/' |
| 22 | R_TAGS = 'refs/tags/' | 22 | R_TAGS = 'refs/tags/' |
| 23 | R_PUB = 'refs/published/' | 23 | R_PUB = 'refs/published/' |
| 24 | R_M = 'refs/remotes/m/' | ||
| 25 | 24 | ||
| 26 | 25 | ||
| 27 | class GitRefs(object): | 26 | class GitRefs(object): |
| @@ -32,11 +32,9 @@ from git_config import init_ssh, close_ssh | |||
| 32 | from command import InteractiveCommand | 32 | from command import InteractiveCommand |
| 33 | from command import MirrorSafeCommand | 33 | from command import MirrorSafeCommand |
| 34 | from command import PagedCommand | 34 | from command import PagedCommand |
| 35 | from editor import Editor | ||
| 36 | from error import ManifestInvalidRevisionError | 35 | from error import ManifestInvalidRevisionError |
| 37 | from error import NoSuchProjectError | 36 | from error import NoSuchProjectError |
| 38 | from error import RepoChangedException | 37 | from error import RepoChangedException |
| 39 | from manifest_xml import XmlManifest | ||
| 40 | from pager import RunPager | 38 | from pager import RunPager |
| 41 | 39 | ||
| 42 | from subcmds import all as all_commands | 40 | from subcmds import all as all_commands |
| @@ -99,8 +97,6 @@ class _Repo(object): | |||
| 99 | sys.exit(1) | 97 | sys.exit(1) |
| 100 | 98 | ||
| 101 | cmd.repodir = self.repodir | 99 | cmd.repodir = self.repodir |
| 102 | cmd.manifest = XmlManifest(cmd.repodir) | ||
| 103 | Editor.globalConfig = cmd.manifest.globalConfig | ||
| 104 | 100 | ||
| 105 | if not isinstance(cmd, MirrorSafeCommand) and cmd.manifest.IsMirror: | 101 | if not isinstance(cmd, MirrorSafeCommand) and cmd.manifest.IsMirror: |
| 106 | print >>sys.stderr, \ | 102 | print >>sys.stderr, \ |
diff --git a/manifest.py b/manifest.py new file mode 100644 index 00000000..c03cb4a7 --- /dev/null +++ b/manifest.py | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | # | ||
| 2 | # Copyright (C) 2009 The Android Open Source Project | ||
| 3 | # | ||
| 4 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | # you may not use this file except in compliance with the License. | ||
| 6 | # You may obtain a copy of the License at | ||
| 7 | # | ||
| 8 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | # | ||
| 10 | # Unless required by applicable law or agreed to in writing, software | ||
| 11 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | # See the License for the specific language governing permissions and | ||
| 14 | # limitations under the License. | ||
| 15 | |||
| 16 | import os | ||
| 17 | |||
| 18 | from error import ManifestParseError | ||
| 19 | from editor import Editor | ||
| 20 | from git_config import GitConfig | ||
| 21 | from project import MetaProject | ||
| 22 | |||
| 23 | class Manifest(object): | ||
| 24 | """any manifest format""" | ||
| 25 | |||
| 26 | def __init__(self, repodir): | ||
| 27 | self.repodir = os.path.abspath(repodir) | ||
| 28 | self.topdir = os.path.dirname(self.repodir) | ||
| 29 | self.globalConfig = GitConfig.ForUser() | ||
| 30 | Editor.globalConfig = self.globalConfig | ||
| 31 | |||
| 32 | self.repoProject = MetaProject(self, 'repo', | ||
| 33 | gitdir = os.path.join(repodir, 'repo/.git'), | ||
| 34 | worktree = os.path.join(repodir, 'repo')) | ||
| 35 | |||
| 36 | @property | ||
| 37 | def IsMirror(self): | ||
| 38 | return self.manifestProject.config.GetBoolean('repo.mirror') | ||
| 39 | |||
| 40 | @property | ||
| 41 | def projects(self): | ||
| 42 | return {} | ||
| 43 | |||
| 44 | @property | ||
| 45 | def notice(self): | ||
| 46 | return None | ||
| 47 | |||
| 48 | @property | ||
| 49 | def manifest_server(self): | ||
| 50 | return None | ||
| 51 | |||
| 52 | def InitBranch(self): | ||
| 53 | pass | ||
| 54 | |||
| 55 | def SetMRefs(self, project): | ||
| 56 | pass | ||
| 57 | |||
| 58 | def Upgrade_Local(self, old): | ||
| 59 | raise ManifestParseError, 'unsupported upgrade path' | ||
diff --git a/manifest_loader.py b/manifest_loader.py new file mode 100644 index 00000000..467cb42a --- /dev/null +++ b/manifest_loader.py | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | # | ||
| 2 | # Copyright (C) 2009 The Android Open Source Project | ||
| 3 | # | ||
| 4 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | # you may not use this file except in compliance with the License. | ||
| 6 | # You may obtain a copy of the License at | ||
| 7 | # | ||
| 8 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | # | ||
| 10 | # Unless required by applicable law or agreed to in writing, software | ||
| 11 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | # See the License for the specific language governing permissions and | ||
| 14 | # limitations under the License. | ||
| 15 | |||
| 16 | from manifest_submodule import SubmoduleManifest | ||
| 17 | from manifest_xml import XmlManifest | ||
| 18 | |||
| 19 | def ParseManifest(repodir, type=None): | ||
| 20 | if type: | ||
| 21 | return type(repodir) | ||
| 22 | if SubmoduleManifest.Is(repodir): | ||
| 23 | return SubmoduleManifest(repodir) | ||
| 24 | return XmlManifest(repodir) | ||
| 25 | |||
| 26 | _manifest = None | ||
| 27 | |||
| 28 | def GetManifest(repodir, reparse=False, type=None): | ||
| 29 | global _manifest | ||
| 30 | if _manifest is None \ | ||
| 31 | or reparse \ | ||
| 32 | or (type and _manifest.__class__ != type): | ||
| 33 | _manifest = ParseManifest(repodir, type=type) | ||
| 34 | return _manifest | ||
diff --git a/manifest_submodule.py b/manifest_submodule.py new file mode 100644 index 00000000..cac271cd --- /dev/null +++ b/manifest_submodule.py | |||
| @@ -0,0 +1,481 @@ | |||
| 1 | # | ||
| 2 | # Copyright (C) 2009 The Android Open Source Project | ||
| 3 | # | ||
| 4 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | # you may not use this file except in compliance with the License. | ||
| 6 | # You may obtain a copy of the License at | ||
| 7 | # | ||
| 8 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | # | ||
| 10 | # Unless required by applicable law or agreed to in writing, software | ||
| 11 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | # See the License for the specific language governing permissions and | ||
| 14 | # limitations under the License. | ||
| 15 | |||
| 16 | import sys | ||
| 17 | import os | ||
| 18 | import shutil | ||
| 19 | |||
| 20 | from error import GitError | ||
| 21 | from error import ManifestParseError | ||
| 22 | from git_command import GitCommand | ||
| 23 | from git_config import GitConfig | ||
| 24 | from git_config import IsId | ||
| 25 | from manifest import Manifest | ||
| 26 | from progress import Progress | ||
| 27 | from project import RemoteSpec | ||
| 28 | from project import Project | ||
| 29 | from project import MetaProject | ||
| 30 | from project import R_HEADS | ||
| 31 | from project import HEAD | ||
| 32 | from project import _lwrite | ||
| 33 | |||
| 34 | import manifest_xml | ||
| 35 | |||
| 36 | GITLINK = '160000' | ||
| 37 | |||
| 38 | def _rmdir(dir, top): | ||
| 39 | while dir != top: | ||
| 40 | try: | ||
| 41 | os.rmdir(dir) | ||
| 42 | except OSError: | ||
| 43 | break | ||
| 44 | dir = os.path.dirname(dir) | ||
| 45 | |||
| 46 | def _rmref(gitdir, ref): | ||
| 47 | os.remove(os.path.join(gitdir, ref)) | ||
| 48 | log = os.path.join(gitdir, 'logs', ref) | ||
| 49 | if os.path.exists(log): | ||
| 50 | os.remove(log) | ||
| 51 | _rmdir(os.path.dirname(log), gitdir) | ||
| 52 | |||
| 53 | def _has_gitmodules(d): | ||
| 54 | return os.path.exists(os.path.join(d, '.gitmodules')) | ||
| 55 | |||
| 56 | class SubmoduleManifest(Manifest): | ||
| 57 | """manifest from .gitmodules file""" | ||
| 58 | |||
| 59 | @classmethod | ||
| 60 | def Is(cls, repodir): | ||
| 61 | return _has_gitmodules(os.path.dirname(repodir)) \ | ||
| 62 | or _has_gitmodules(os.path.join(repodir, 'manifest')) \ | ||
| 63 | or _has_gitmodules(os.path.join(repodir, 'manifests')) | ||
| 64 | |||
| 65 | @classmethod | ||
| 66 | def IsBare(cls, p): | ||
| 67 | try: | ||
| 68 | p.bare_git.cat_file('-e', '%s:.gitmodules' % p.GetRevisionId()) | ||
| 69 | except GitError: | ||
| 70 | return False | ||
| 71 | return True | ||
| 72 | |||
| 73 | def __init__(self, repodir): | ||
| 74 | Manifest.__init__(self, repodir) | ||
| 75 | |||
| 76 | gitdir = os.path.join(repodir, 'manifest.git') | ||
| 77 | config = GitConfig.ForRepository(gitdir = gitdir) | ||
| 78 | |||
| 79 | if config.GetBoolean('repo.mirror'): | ||
| 80 | worktree = os.path.join(repodir, 'manifest') | ||
| 81 | relpath = None | ||
| 82 | else: | ||
| 83 | worktree = self.topdir | ||
| 84 | relpath = '.' | ||
| 85 | |||
| 86 | self.manifestProject = MetaProject(self, '__manifest__', | ||
| 87 | gitdir = gitdir, | ||
| 88 | worktree = worktree, | ||
| 89 | relpath = relpath) | ||
| 90 | self._modules = GitConfig(os.path.join(worktree, '.gitmodules'), | ||
| 91 | pickleFile = os.path.join( | ||
| 92 | repodir, '.repopickle_gitmodules' | ||
| 93 | )) | ||
| 94 | self._review = GitConfig(os.path.join(worktree, '.review'), | ||
| 95 | pickleFile = os.path.join( | ||
| 96 | repodir, '.repopickle_review' | ||
| 97 | )) | ||
| 98 | self._Unload() | ||
| 99 | |||
| 100 | @property | ||
| 101 | def projects(self): | ||
| 102 | self._Load() | ||
| 103 | return self._projects | ||
| 104 | |||
| 105 | @property | ||
| 106 | def notice(self): | ||
| 107 | return self._modules.GetString('repo.notice') | ||
| 108 | |||
| 109 | def InitBranch(self): | ||
| 110 | m = self.manifestProject | ||
| 111 | if m.CurrentBranch is None: | ||
| 112 | b = m.revisionExpr | ||
| 113 | if b.startswith(R_HEADS): | ||
| 114 | b = b[len(R_HEADS):] | ||
| 115 | return m.StartBranch(b) | ||
| 116 | return True | ||
| 117 | |||
| 118 | def SetMRefs(self, project): | ||
| 119 | if project.revisionId is None: | ||
| 120 | # Special project, e.g. the manifest or repo executable. | ||
| 121 | # | ||
| 122 | return | ||
| 123 | |||
| 124 | ref = 'refs/remotes/m' | ||
| 125 | cur = project.bare_ref.get(ref) | ||
| 126 | exp = project.revisionId | ||
| 127 | if cur != exp: | ||
| 128 | msg = 'manifest set to %s' % exp | ||
| 129 | project.bare_git.UpdateRef(ref, exp, message = msg, detach = True) | ||
| 130 | |||
| 131 | ref = 'refs/remotes/m-revision' | ||
| 132 | cur = project.bare_ref.symref(ref) | ||
| 133 | exp = project.revisionExpr | ||
| 134 | if exp is None: | ||
| 135 | if cur: | ||
| 136 | _rmref(project.gitdir, ref) | ||
| 137 | elif cur != exp: | ||
| 138 | remote = project.GetRemote(project.remote.name) | ||
| 139 | dst = remote.ToLocal(exp) | ||
| 140 | msg = 'manifest set to %s (%s)' % (exp, dst) | ||
| 141 | project.bare_git.symbolic_ref('-m', msg, ref, dst) | ||
| 142 | |||
| 143 | def Upgrade_Local(self, old): | ||
| 144 | if isinstance(old, manifest_xml.XmlManifest): | ||
| 145 | self.FromXml_Local_1(old, checkout=True) | ||
| 146 | self.FromXml_Local_2(old) | ||
| 147 | else: | ||
| 148 | raise ManifestParseError, 'cannot upgrade manifest' | ||
| 149 | |||
| 150 | def FromXml_Local_1(self, old, checkout): | ||
| 151 | os.rename(old.manifestProject.gitdir, | ||
| 152 | os.path.join(old.repodir, 'manifest.git')) | ||
| 153 | |||
| 154 | oldmp = old.manifestProject | ||
| 155 | oldBranch = oldmp.CurrentBranch | ||
| 156 | b = oldmp.GetBranch(oldBranch).merge | ||
| 157 | if not b: | ||
| 158 | raise ManifestParseError, 'cannot upgrade manifest' | ||
| 159 | if b.startswith(R_HEADS): | ||
| 160 | b = b[len(R_HEADS):] | ||
| 161 | |||
| 162 | newmp = self.manifestProject | ||
| 163 | self._CleanOldMRefs(newmp) | ||
| 164 | if oldBranch != b: | ||
| 165 | newmp.bare_git.branch('-m', oldBranch, b) | ||
| 166 | newmp.config.ClearCache() | ||
| 167 | |||
| 168 | old_remote = newmp.GetBranch(b).remote.name | ||
| 169 | act_remote = self._GuessRemoteName(old) | ||
| 170 | if old_remote != act_remote: | ||
| 171 | newmp.bare_git.remote('rename', old_remote, act_remote) | ||
| 172 | newmp.config.ClearCache() | ||
| 173 | newmp.remote.name = act_remote | ||
| 174 | print >>sys.stderr, "Assuming remote named '%s'" % act_remote | ||
| 175 | |||
| 176 | if checkout: | ||
| 177 | for p in old.projects.values(): | ||
| 178 | for c in p.copyfiles: | ||
| 179 | if os.path.exists(c.abs_dest): | ||
| 180 | os.remove(c.abs_dest) | ||
| 181 | newmp._InitWorkTree() | ||
| 182 | else: | ||
| 183 | newmp._LinkWorkTree() | ||
| 184 | |||
| 185 | _lwrite(os.path.join(newmp.worktree,'.git',HEAD), | ||
| 186 | 'ref: refs/heads/%s\n' % b) | ||
| 187 | |||
| 188 | def _GuessRemoteName(self, old): | ||
| 189 | used = {} | ||
| 190 | for p in old.projects.values(): | ||
| 191 | n = p.remote.name | ||
| 192 | used[n] = used.get(n, 0) + 1 | ||
| 193 | |||
| 194 | remote_name = 'origin' | ||
| 195 | remote_used = 0 | ||
| 196 | for n in used.keys(): | ||
| 197 | if remote_used < used[n]: | ||
| 198 | remote_used = used[n] | ||
| 199 | remote_name = n | ||
| 200 | return remote_name | ||
| 201 | |||
| 202 | def FromXml_Local_2(self, old): | ||
| 203 | shutil.rmtree(old.manifestProject.worktree) | ||
| 204 | os.remove(old._manifestFile) | ||
| 205 | |||
| 206 | my_remote = self._Remote().name | ||
| 207 | new_base = os.path.join(self.repodir, 'projects') | ||
| 208 | old_base = os.path.join(self.repodir, 'projects.old') | ||
| 209 | os.rename(new_base, old_base) | ||
| 210 | os.makedirs(new_base) | ||
| 211 | |||
| 212 | info = [] | ||
| 213 | pm = Progress('Converting projects', len(self.projects)) | ||
| 214 | for p in self.projects.values(): | ||
| 215 | pm.update() | ||
| 216 | |||
| 217 | old_p = old.projects.get(p.name) | ||
| 218 | old_gitdir = os.path.join(old_base, '%s.git' % p.relpath) | ||
| 219 | if not os.path.isdir(old_gitdir): | ||
| 220 | continue | ||
| 221 | |||
| 222 | parent = os.path.dirname(p.gitdir) | ||
| 223 | if not os.path.isdir(parent): | ||
| 224 | os.makedirs(parent) | ||
| 225 | os.rename(old_gitdir, p.gitdir) | ||
| 226 | _rmdir(os.path.dirname(old_gitdir), self.repodir) | ||
| 227 | |||
| 228 | if not os.path.isdir(p.worktree): | ||
| 229 | os.makedirs(p.worktree) | ||
| 230 | |||
| 231 | if os.path.isdir(os.path.join(p.worktree, '.git')): | ||
| 232 | p._LinkWorkTree(relink=True) | ||
| 233 | |||
| 234 | self._CleanOldMRefs(p) | ||
| 235 | if old_p and old_p.remote.name != my_remote: | ||
| 236 | info.append("%s/: renamed remote '%s' to '%s'" \ | ||
| 237 | % (p.relpath, old_p.remote.name, my_remote)) | ||
| 238 | p.bare_git.remote('rename', old_p.remote.name, my_remote) | ||
| 239 | p.config.ClearCache() | ||
| 240 | |||
| 241 | self.SetMRefs(p) | ||
| 242 | pm.end() | ||
| 243 | for i in info: | ||
| 244 | print >>sys.stderr, i | ||
| 245 | |||
| 246 | def _CleanOldMRefs(self, p): | ||
| 247 | all_refs = p._allrefs | ||
| 248 | for ref in all_refs.keys(): | ||
| 249 | if ref.startswith(manifest_xml.R_M): | ||
| 250 | if p.bare_ref.symref(ref) != '': | ||
| 251 | _rmref(p.gitdir, ref) | ||
| 252 | else: | ||
| 253 | p.bare_git.DeleteRef(ref, all_refs[ref]) | ||
| 254 | |||
| 255 | def FromXml_Definition(self, old): | ||
| 256 | """Convert another manifest representation to this one. | ||
| 257 | """ | ||
| 258 | mp = self.manifestProject | ||
| 259 | gm = self._modules | ||
| 260 | gr = self._review | ||
| 261 | |||
| 262 | fd = open(os.path.join(mp.worktree, '.gitignore'), 'ab') | ||
| 263 | fd.write('/.repo\n') | ||
| 264 | fd.close() | ||
| 265 | |||
| 266 | sort_projects = list(old.projects.keys()) | ||
| 267 | sort_projects.sort() | ||
| 268 | |||
| 269 | b = mp.GetBranch(mp.CurrentBranch).merge | ||
| 270 | if b.startswith(R_HEADS): | ||
| 271 | b = b[len(R_HEADS):] | ||
| 272 | |||
| 273 | if old.notice: | ||
| 274 | gm.SetString('repo.notice', old.notice) | ||
| 275 | |||
| 276 | info = [] | ||
| 277 | pm = Progress('Converting manifest', len(sort_projects)) | ||
| 278 | for p in sort_projects: | ||
| 279 | pm.update() | ||
| 280 | p = old.projects[p] | ||
| 281 | |||
| 282 | gm.SetString('submodule.%s.path' % p.name, p.relpath) | ||
| 283 | gm.SetString('submodule.%s.url' % p.name, p.remote.url) | ||
| 284 | |||
| 285 | if gr.GetString('review.url') is None: | ||
| 286 | gr.SetString('review.url', p.remote.review) | ||
| 287 | elif gr.GetString('review.url') != p.remote.review: | ||
| 288 | gr.SetString('review.%s.url' % p.name, p.remote.review) | ||
| 289 | |||
| 290 | r = p.revisionExpr | ||
| 291 | if r and not IsId(r): | ||
| 292 | if r.startswith(R_HEADS): | ||
| 293 | r = r[len(R_HEADS):] | ||
| 294 | if r == b: | ||
| 295 | r = '.' | ||
| 296 | gm.SetString('submodule.%s.revision' % p.name, r) | ||
| 297 | |||
| 298 | for c in p.copyfiles: | ||
| 299 | info.append('Moved %s out of %s' % (c.src, p.relpath)) | ||
| 300 | c._Copy() | ||
| 301 | p.work_git.rm(c.src) | ||
| 302 | mp.work_git.add(c.dest) | ||
| 303 | |||
| 304 | self.SetRevisionId(p.relpath, p.GetRevisionId()) | ||
| 305 | mp.work_git.add('.gitignore', '.gitmodules', '.review') | ||
| 306 | pm.end() | ||
| 307 | for i in info: | ||
| 308 | print >>sys.stderr, i | ||
| 309 | |||
| 310 | def _Unload(self): | ||
| 311 | self._loaded = False | ||
| 312 | self._projects = {} | ||
| 313 | self._revisionIds = None | ||
| 314 | self.branch = None | ||
| 315 | |||
| 316 | def _Load(self): | ||
| 317 | if not self._loaded: | ||
| 318 | f = os.path.join(self.repodir, manifest_xml.LOCAL_MANIFEST_NAME) | ||
| 319 | if os.path.exists(f): | ||
| 320 | print >>sys.stderr, 'warning: ignoring %s' % f | ||
| 321 | |||
| 322 | m = self.manifestProject | ||
| 323 | b = m.CurrentBranch | ||
| 324 | if not b: | ||
| 325 | raise ManifestParseError, 'manifest cannot be on detached HEAD' | ||
| 326 | b = m.GetBranch(b).merge | ||
| 327 | if b.startswith(R_HEADS): | ||
| 328 | b = b[len(R_HEADS):] | ||
| 329 | self.branch = b | ||
| 330 | m.remote.name = self._Remote().name | ||
| 331 | |||
| 332 | self._ParseModules() | ||
| 333 | |||
| 334 | if self.IsMirror: | ||
| 335 | self._AddMetaProjectMirror(self.repoProject) | ||
| 336 | self._AddMetaProjectMirror(self.manifestProject) | ||
| 337 | |||
| 338 | self._loaded = True | ||
| 339 | |||
| 340 | def _ParseModules(self): | ||
| 341 | byPath = dict() | ||
| 342 | for name in self._modules.GetSubSections('submodule'): | ||
| 343 | p = self._ParseProject(name) | ||
| 344 | if self._projects.get(p.name): | ||
| 345 | raise ManifestParseError, 'duplicate project "%s"' % p.name | ||
| 346 | if byPath.get(p.relpath): | ||
| 347 | raise ManifestParseError, 'duplicate path "%s"' % p.relpath | ||
| 348 | self._projects[p.name] = p | ||
| 349 | byPath[p.relpath] = p | ||
| 350 | |||
| 351 | for relpath in self._allRevisionIds.keys(): | ||
| 352 | if relpath not in byPath: | ||
| 353 | raise ManifestParseError, \ | ||
| 354 | 'project "%s" not in .gitmodules' \ | ||
| 355 | % relpath | ||
| 356 | |||
| 357 | def _Remote(self): | ||
| 358 | m = self.manifestProject | ||
| 359 | b = m.GetBranch(m.CurrentBranch) | ||
| 360 | return b.remote | ||
| 361 | |||
| 362 | def _ResolveUrl(self, url): | ||
| 363 | if url.startswith('./') or url.startswith('../'): | ||
| 364 | base = self._Remote().url | ||
| 365 | try: | ||
| 366 | base = base[:base.rindex('/')+1] | ||
| 367 | except ValueError: | ||
| 368 | base = base[:base.rindex(':')+1] | ||
| 369 | if url.startswith('./'): | ||
| 370 | url = url[2:] | ||
| 371 | while '/' in base and url.startswith('../'): | ||
| 372 | base = base[:base.rindex('/')+1] | ||
| 373 | url = url[3:] | ||
| 374 | return base + url | ||
| 375 | return url | ||
| 376 | |||
| 377 | def _GetRevisionId(self, path): | ||
| 378 | return self._allRevisionIds.get(path) | ||
| 379 | |||
| 380 | @property | ||
| 381 | def _allRevisionIds(self): | ||
| 382 | if self._revisionIds is None: | ||
| 383 | a = dict() | ||
| 384 | p = GitCommand(self.manifestProject, | ||
| 385 | ['ls-files','-z','--stage'], | ||
| 386 | capture_stdout = True) | ||
| 387 | for line in p.process.stdout.read().split('\0')[:-1]: | ||
| 388 | l_info, l_path = line.split('\t', 2) | ||
| 389 | l_mode, l_id, l_stage = l_info.split(' ', 2) | ||
| 390 | if l_mode == GITLINK and l_stage == '0': | ||
| 391 | a[l_path] = l_id | ||
| 392 | p.Wait() | ||
| 393 | self._revisionIds = a | ||
| 394 | return self._revisionIds | ||
| 395 | |||
| 396 | def SetRevisionId(self, path, id): | ||
| 397 | self.manifestProject.work_git.update_index( | ||
| 398 | '--add','--cacheinfo', GITLINK, id, path) | ||
| 399 | |||
| 400 | def _ParseProject(self, name): | ||
| 401 | gm = self._modules | ||
| 402 | gr = self._review | ||
| 403 | |||
| 404 | path = gm.GetString('submodule.%s.path' % name) | ||
| 405 | if not path: | ||
| 406 | path = name | ||
| 407 | |||
| 408 | revId = self._GetRevisionId(path) | ||
| 409 | if not revId: | ||
| 410 | raise ManifestParseError( | ||
| 411 | 'submodule "%s" has no revision at "%s"' \ | ||
| 412 | % (name, path)) | ||
| 413 | |||
| 414 | url = gm.GetString('submodule.%s.url' % name) | ||
| 415 | if not url: | ||
| 416 | url = name | ||
| 417 | url = self._ResolveUrl(url) | ||
| 418 | |||
| 419 | review = gr.GetString('review.%s.url' % name) | ||
| 420 | if not review: | ||
| 421 | review = gr.GetString('review.url') | ||
| 422 | if not review: | ||
| 423 | review = self._Remote().review | ||
| 424 | |||
| 425 | remote = RemoteSpec(self._Remote().name, url, review) | ||
| 426 | revExpr = gm.GetString('submodule.%s.revision' % name) | ||
| 427 | if revExpr == '.': | ||
| 428 | revExpr = self.branch | ||
| 429 | |||
| 430 | if self.IsMirror: | ||
| 431 | relpath = None | ||
| 432 | worktree = None | ||
| 433 | gitdir = os.path.join(self.topdir, '%s.git' % name) | ||
| 434 | else: | ||
| 435 | worktree = os.path.join(self.topdir, path) | ||
| 436 | gitdir = os.path.join(self.repodir, 'projects/%s.git' % name) | ||
| 437 | |||
| 438 | return Project(manifest = self, | ||
| 439 | name = name, | ||
| 440 | remote = remote, | ||
| 441 | gitdir = gitdir, | ||
| 442 | worktree = worktree, | ||
| 443 | relpath = path, | ||
| 444 | revisionExpr = revExpr, | ||
| 445 | revisionId = revId) | ||
| 446 | |||
| 447 | def _AddMetaProjectMirror(self, m): | ||
| 448 | m_url = m.GetRemote(m.remote.name).url | ||
| 449 | if m_url.endswith('/.git'): | ||
| 450 | raise ManifestParseError, 'refusing to mirror %s' % m_url | ||
| 451 | |||
| 452 | name = self._GuessMetaName(m_url) | ||
| 453 | if name.endswith('.git'): | ||
| 454 | name = name[:-4] | ||
| 455 | |||
| 456 | if name not in self._projects: | ||
| 457 | m.PreSync() | ||
| 458 | gitdir = os.path.join(self.topdir, '%s.git' % name) | ||
| 459 | project = Project(manifest = self, | ||
| 460 | name = name, | ||
| 461 | remote = RemoteSpec(self._Remote().name, m_url), | ||
| 462 | gitdir = gitdir, | ||
| 463 | worktree = None, | ||
| 464 | relpath = None, | ||
| 465 | revisionExpr = m.revisionExpr, | ||
| 466 | revisionId = None) | ||
| 467 | self._projects[project.name] = project | ||
| 468 | |||
| 469 | def _GuessMetaName(self, m_url): | ||
| 470 | parts = m_url.split('/') | ||
| 471 | name = parts[-1] | ||
| 472 | parts = parts[0:-1] | ||
| 473 | s = len(parts) - 1 | ||
| 474 | while s > 0: | ||
| 475 | l = '/'.join(parts[0:s]) + '/' | ||
| 476 | r = '/'.join(parts[s:]) + '/' | ||
| 477 | for p in self._projects.values(): | ||
| 478 | if p.name.startswith(r) and p.remote.url.startswith(l): | ||
| 479 | return r + name | ||
| 480 | s -= 1 | ||
| 481 | return m_url[m_url.rindex('/') + 1:] | ||
diff --git a/manifest_xml.py b/manifest_xml.py index 0103cf55..1d02f9d4 100644 --- a/manifest_xml.py +++ b/manifest_xml.py | |||
| @@ -17,12 +17,19 @@ import os | |||
| 17 | import sys | 17 | import sys |
| 18 | import xml.dom.minidom | 18 | import xml.dom.minidom |
| 19 | 19 | ||
| 20 | from git_config import GitConfig, IsId | 20 | from git_config import GitConfig |
| 21 | from project import RemoteSpec, Project, MetaProject, R_HEADS, HEAD | 21 | from git_config import IsId |
| 22 | from manifest import Manifest | ||
| 23 | from project import RemoteSpec | ||
| 24 | from project import Project | ||
| 25 | from project import MetaProject | ||
| 26 | from project import R_HEADS | ||
| 27 | from project import HEAD | ||
| 22 | from error import ManifestParseError | 28 | from error import ManifestParseError |
| 23 | 29 | ||
| 24 | MANIFEST_FILE_NAME = 'manifest.xml' | 30 | MANIFEST_FILE_NAME = 'manifest.xml' |
| 25 | LOCAL_MANIFEST_NAME = 'local_manifest.xml' | 31 | LOCAL_MANIFEST_NAME = 'local_manifest.xml' |
| 32 | R_M = 'refs/remotes/m/' | ||
| 26 | 33 | ||
| 27 | class _Default(object): | 34 | class _Default(object): |
| 28 | """Project defaults within the manifest.""" | 35 | """Project defaults within the manifest.""" |
| @@ -46,19 +53,13 @@ class _XmlRemote(object): | |||
| 46 | url += '/%s.git' % projectName | 53 | url += '/%s.git' % projectName |
| 47 | return RemoteSpec(self.name, url, self.reviewUrl) | 54 | return RemoteSpec(self.name, url, self.reviewUrl) |
| 48 | 55 | ||
| 49 | class XmlManifest(object): | 56 | class XmlManifest(Manifest): |
| 50 | """manages the repo configuration file""" | 57 | """manages the repo configuration file""" |
| 51 | 58 | ||
| 52 | def __init__(self, repodir): | 59 | def __init__(self, repodir): |
| 53 | self.repodir = os.path.abspath(repodir) | 60 | Manifest.__init__(self, repodir) |
| 54 | self.topdir = os.path.dirname(self.repodir) | ||
| 55 | self.manifestFile = os.path.join(self.repodir, MANIFEST_FILE_NAME) | ||
| 56 | self.globalConfig = GitConfig.ForUser() | ||
| 57 | |||
| 58 | self.repoProject = MetaProject(self, 'repo', | ||
| 59 | gitdir = os.path.join(repodir, 'repo/.git'), | ||
| 60 | worktree = os.path.join(repodir, 'repo')) | ||
| 61 | 61 | ||
| 62 | self._manifestFile = os.path.join(repodir, MANIFEST_FILE_NAME) | ||
| 62 | self.manifestProject = MetaProject(self, 'manifests', | 63 | self.manifestProject = MetaProject(self, 'manifests', |
| 63 | gitdir = os.path.join(repodir, 'manifests.git'), | 64 | gitdir = os.path.join(repodir, 'manifests.git'), |
| 64 | worktree = os.path.join(repodir, 'manifests')) | 65 | worktree = os.path.join(repodir, 'manifests')) |
| @@ -72,13 +73,13 @@ class XmlManifest(object): | |||
| 72 | if not os.path.isfile(path): | 73 | if not os.path.isfile(path): |
| 73 | raise ManifestParseError('manifest %s not found' % name) | 74 | raise ManifestParseError('manifest %s not found' % name) |
| 74 | 75 | ||
| 75 | old = self.manifestFile | 76 | old = self._manifestFile |
| 76 | try: | 77 | try: |
| 77 | self.manifestFile = path | 78 | self._manifestFile = path |
| 78 | self._Unload() | 79 | self._Unload() |
| 79 | self._Load() | 80 | self._Load() |
| 80 | finally: | 81 | finally: |
| 81 | self.manifestFile = old | 82 | self._manifestFile = old |
| 82 | 83 | ||
| 83 | def Link(self, name): | 84 | def Link(self, name): |
| 84 | """Update the repo metadata to use a different manifest. | 85 | """Update the repo metadata to use a different manifest. |
| @@ -86,9 +87,9 @@ class XmlManifest(object): | |||
| 86 | self.Override(name) | 87 | self.Override(name) |
| 87 | 88 | ||
| 88 | try: | 89 | try: |
| 89 | if os.path.exists(self.manifestFile): | 90 | if os.path.exists(self._manifestFile): |
| 90 | os.remove(self.manifestFile) | 91 | os.remove(self._manifestFile) |
| 91 | os.symlink('manifests/%s' % name, self.manifestFile) | 92 | os.symlink('manifests/%s' % name, self._manifestFile) |
| 92 | except OSError, e: | 93 | except OSError, e: |
| 93 | raise ManifestParseError('cannot link manifest %s' % name) | 94 | raise ManifestParseError('cannot link manifest %s' % name) |
| 94 | 95 | ||
| @@ -198,9 +199,15 @@ class XmlManifest(object): | |||
| 198 | self._Load() | 199 | self._Load() |
| 199 | return self._manifest_server | 200 | return self._manifest_server |
| 200 | 201 | ||
| 201 | @property | 202 | def InitBranch(self): |
| 202 | def IsMirror(self): | 203 | m = self.manifestProject |
| 203 | return self.manifestProject.config.GetBoolean('repo.mirror') | 204 | if m.CurrentBranch is None: |
| 205 | return m.StartBranch('default') | ||
| 206 | return True | ||
| 207 | |||
| 208 | def SetMRefs(self, project): | ||
| 209 | if self.branch: | ||
| 210 | project._InitAnyMRef(R_M + self.branch) | ||
| 204 | 211 | ||
| 205 | def _Unload(self): | 212 | def _Unload(self): |
| 206 | self._loaded = False | 213 | self._loaded = False |
| @@ -214,7 +221,10 @@ class XmlManifest(object): | |||
| 214 | def _Load(self): | 221 | def _Load(self): |
| 215 | if not self._loaded: | 222 | if not self._loaded: |
| 216 | m = self.manifestProject | 223 | m = self.manifestProject |
| 217 | b = m.GetBranch(m.CurrentBranch).merge | 224 | b = m.GetBranch(m.CurrentBranch) |
| 225 | if b.remote and b.remote.name: | ||
| 226 | m.remote.name = b.remote.name | ||
| 227 | b = b.merge | ||
| 218 | if b is not None and b.startswith(R_HEADS): | 228 | if b is not None and b.startswith(R_HEADS): |
| 219 | b = b[len(R_HEADS):] | 229 | b = b[len(R_HEADS):] |
| 220 | self.branch = b | 230 | self.branch = b |
| @@ -224,11 +234,11 @@ class XmlManifest(object): | |||
| 224 | local = os.path.join(self.repodir, LOCAL_MANIFEST_NAME) | 234 | local = os.path.join(self.repodir, LOCAL_MANIFEST_NAME) |
| 225 | if os.path.exists(local): | 235 | if os.path.exists(local): |
| 226 | try: | 236 | try: |
| 227 | real = self.manifestFile | 237 | real = self._manifestFile |
| 228 | self.manifestFile = local | 238 | self._manifestFile = local |
| 229 | self._ParseManifest(False) | 239 | self._ParseManifest(False) |
| 230 | finally: | 240 | finally: |
| 231 | self.manifestFile = real | 241 | self._manifestFile = real |
| 232 | 242 | ||
| 233 | if self.IsMirror: | 243 | if self.IsMirror: |
| 234 | self._AddMetaProjectMirror(self.repoProject) | 244 | self._AddMetaProjectMirror(self.repoProject) |
| @@ -237,17 +247,17 @@ class XmlManifest(object): | |||
| 237 | self._loaded = True | 247 | self._loaded = True |
| 238 | 248 | ||
| 239 | def _ParseManifest(self, is_root_file): | 249 | def _ParseManifest(self, is_root_file): |
| 240 | root = xml.dom.minidom.parse(self.manifestFile) | 250 | root = xml.dom.minidom.parse(self._manifestFile) |
| 241 | if not root or not root.childNodes: | 251 | if not root or not root.childNodes: |
| 242 | raise ManifestParseError, \ | 252 | raise ManifestParseError, \ |
| 243 | "no root node in %s" % \ | 253 | "no root node in %s" % \ |
| 244 | self.manifestFile | 254 | self._manifestFile |
| 245 | 255 | ||
| 246 | config = root.childNodes[0] | 256 | config = root.childNodes[0] |
| 247 | if config.nodeName != 'manifest': | 257 | if config.nodeName != 'manifest': |
| 248 | raise ManifestParseError, \ | 258 | raise ManifestParseError, \ |
| 249 | "no <manifest> in %s" % \ | 259 | "no <manifest> in %s" % \ |
| 250 | self.manifestFile | 260 | self._manifestFile |
| 251 | 261 | ||
| 252 | for node in config.childNodes: | 262 | for node in config.childNodes: |
| 253 | if node.nodeName == 'remove-project': | 263 | if node.nodeName == 'remove-project': |
| @@ -265,7 +275,7 @@ class XmlManifest(object): | |||
| 265 | if self._remotes.get(remote.name): | 275 | if self._remotes.get(remote.name): |
| 266 | raise ManifestParseError, \ | 276 | raise ManifestParseError, \ |
| 267 | 'duplicate remote %s in %s' % \ | 277 | 'duplicate remote %s in %s' % \ |
| 268 | (remote.name, self.manifestFile) | 278 | (remote.name, self._manifestFile) |
| 269 | self._remotes[remote.name] = remote | 279 | self._remotes[remote.name] = remote |
| 270 | 280 | ||
| 271 | for node in config.childNodes: | 281 | for node in config.childNodes: |
| @@ -273,7 +283,7 @@ class XmlManifest(object): | |||
| 273 | if self._default is not None: | 283 | if self._default is not None: |
| 274 | raise ManifestParseError, \ | 284 | raise ManifestParseError, \ |
| 275 | 'duplicate default in %s' % \ | 285 | 'duplicate default in %s' % \ |
| 276 | (self.manifestFile) | 286 | (self._manifestFile) |
| 277 | self._default = self._ParseDefault(node) | 287 | self._default = self._ParseDefault(node) |
| 278 | if self._default is None: | 288 | if self._default is None: |
| 279 | self._default = _Default() | 289 | self._default = _Default() |
| @@ -301,7 +311,7 @@ class XmlManifest(object): | |||
| 301 | if self._projects.get(project.name): | 311 | if self._projects.get(project.name): |
| 302 | raise ManifestParseError, \ | 312 | raise ManifestParseError, \ |
| 303 | 'duplicate project %s in %s' % \ | 313 | 'duplicate project %s in %s' % \ |
| 304 | (project.name, self.manifestFile) | 314 | (project.name, self._manifestFile) |
| 305 | self._projects[project.name] = project | 315 | self._projects[project.name] = project |
| 306 | 316 | ||
| 307 | def _AddMetaProjectMirror(self, m): | 317 | def _AddMetaProjectMirror(self, m): |
| @@ -412,7 +422,7 @@ class XmlManifest(object): | |||
| 412 | if remote is None: | 422 | if remote is None: |
| 413 | raise ManifestParseError, \ | 423 | raise ManifestParseError, \ |
| 414 | "no remote for project %s within %s" % \ | 424 | "no remote for project %s within %s" % \ |
| 415 | (name, self.manifestFile) | 425 | (name, self._manifestFile) |
| 416 | 426 | ||
| 417 | revisionExpr = node.getAttribute('revision') | 427 | revisionExpr = node.getAttribute('revision') |
| 418 | if not revisionExpr: | 428 | if not revisionExpr: |
| @@ -420,7 +430,7 @@ class XmlManifest(object): | |||
| 420 | if not revisionExpr: | 430 | if not revisionExpr: |
| 421 | raise ManifestParseError, \ | 431 | raise ManifestParseError, \ |
| 422 | "no revision for project %s within %s" % \ | 432 | "no revision for project %s within %s" % \ |
| 423 | (name, self.manifestFile) | 433 | (name, self._manifestFile) |
| 424 | 434 | ||
| 425 | path = node.getAttribute('path') | 435 | path = node.getAttribute('path') |
| 426 | if not path: | 436 | if not path: |
| @@ -428,7 +438,7 @@ class XmlManifest(object): | |||
| 428 | if path.startswith('/'): | 438 | if path.startswith('/'): |
| 429 | raise ManifestParseError, \ | 439 | raise ManifestParseError, \ |
| 430 | "project %s path cannot be absolute in %s" % \ | 440 | "project %s path cannot be absolute in %s" % \ |
| 431 | (name, self.manifestFile) | 441 | (name, self._manifestFile) |
| 432 | 442 | ||
| 433 | if self.IsMirror: | 443 | if self.IsMirror: |
| 434 | relpath = None | 444 | relpath = None |
| @@ -470,7 +480,7 @@ class XmlManifest(object): | |||
| 470 | if not v: | 480 | if not v: |
| 471 | raise ManifestParseError, \ | 481 | raise ManifestParseError, \ |
| 472 | "remote %s not defined in %s" % \ | 482 | "remote %s not defined in %s" % \ |
| 473 | (name, self.manifestFile) | 483 | (name, self._manifestFile) |
| 474 | return v | 484 | return v |
| 475 | 485 | ||
| 476 | def _reqatt(self, node, attname): | 486 | def _reqatt(self, node, attname): |
| @@ -481,5 +491,5 @@ class XmlManifest(object): | |||
| 481 | if not v: | 491 | if not v: |
| 482 | raise ManifestParseError, \ | 492 | raise ManifestParseError, \ |
| 483 | "no %s in <%s> within %s" % \ | 493 | "no %s in <%s> within %s" % \ |
| 484 | (attname, node.nodeName, self.manifestFile) | 494 | (attname, node.nodeName, self._manifestFile) |
| 485 | return v | 495 | return v |
| @@ -27,7 +27,7 @@ from git_config import GitConfig, IsId | |||
| 27 | from error import GitError, ImportError, UploadError | 27 | from error import GitError, ImportError, UploadError |
| 28 | from error import ManifestInvalidRevisionError | 28 | from error import ManifestInvalidRevisionError |
| 29 | 29 | ||
| 30 | from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M | 30 | from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB |
| 31 | 31 | ||
| 32 | def _lwrite(path, content): | 32 | def _lwrite(path, content): |
| 33 | lock = '%s.lock' % path | 33 | lock = '%s.lock' % path |
| @@ -640,7 +640,7 @@ class Project(object): | |||
| 640 | self._RemoteFetch(None, rev[len(R_TAGS):], quiet=quiet) | 640 | self._RemoteFetch(None, rev[len(R_TAGS):], quiet=quiet) |
| 641 | 641 | ||
| 642 | if self.worktree: | 642 | if self.worktree: |
| 643 | self._InitMRef() | 643 | self.manifest.SetMRefs(self) |
| 644 | else: | 644 | else: |
| 645 | self._InitMirrorHead() | 645 | self._InitMirrorHead() |
| 646 | try: | 646 | try: |
| @@ -1228,10 +1228,6 @@ class Project(object): | |||
| 1228 | remote.ResetFetch(mirror=True) | 1228 | remote.ResetFetch(mirror=True) |
| 1229 | remote.Save() | 1229 | remote.Save() |
| 1230 | 1230 | ||
| 1231 | def _InitMRef(self): | ||
| 1232 | if self.manifest.branch: | ||
| 1233 | self._InitAnyMRef(R_M + self.manifest.branch) | ||
| 1234 | |||
| 1235 | def _InitMirrorHead(self): | 1231 | def _InitMirrorHead(self): |
| 1236 | self._InitAnyMRef(HEAD) | 1232 | self._InitAnyMRef(HEAD) |
| 1237 | 1233 | ||
| @@ -1250,33 +1246,40 @@ class Project(object): | |||
| 1250 | msg = 'manifest set to %s' % self.revisionExpr | 1246 | msg = 'manifest set to %s' % self.revisionExpr |
| 1251 | self.bare_git.symbolic_ref('-m', msg, ref, dst) | 1247 | self.bare_git.symbolic_ref('-m', msg, ref, dst) |
| 1252 | 1248 | ||
| 1253 | def _InitWorkTree(self): | 1249 | def _LinkWorkTree(self, relink=False): |
| 1254 | dotgit = os.path.join(self.worktree, '.git') | 1250 | dotgit = os.path.join(self.worktree, '.git') |
| 1255 | if not os.path.exists(dotgit): | 1251 | if not relink: |
| 1256 | os.makedirs(dotgit) | 1252 | os.makedirs(dotgit) |
| 1257 | 1253 | ||
| 1258 | for name in ['config', | 1254 | for name in ['config', |
| 1259 | 'description', | 1255 | 'description', |
| 1260 | 'hooks', | 1256 | 'hooks', |
| 1261 | 'info', | 1257 | 'info', |
| 1262 | 'logs', | 1258 | 'logs', |
| 1263 | 'objects', | 1259 | 'objects', |
| 1264 | 'packed-refs', | 1260 | 'packed-refs', |
| 1265 | 'refs', | 1261 | 'refs', |
| 1266 | 'rr-cache', | 1262 | 'rr-cache', |
| 1267 | 'svn']: | 1263 | 'svn']: |
| 1268 | try: | 1264 | try: |
| 1269 | src = os.path.join(self.gitdir, name) | 1265 | src = os.path.join(self.gitdir, name) |
| 1270 | dst = os.path.join(dotgit, name) | 1266 | dst = os.path.join(dotgit, name) |
| 1271 | if os.path.islink(dst) or not os.path.exists(dst): | 1267 | if relink: |
| 1272 | os.symlink(relpath(src, dst), dst) | 1268 | os.remove(dst) |
| 1273 | else: | 1269 | if os.path.islink(dst) or not os.path.exists(dst): |
| 1274 | raise GitError('cannot overwrite a local work tree') | 1270 | os.symlink(relpath(src, dst), dst) |
| 1275 | except OSError, e: | 1271 | else: |
| 1276 | if e.errno == errno.EPERM: | 1272 | raise GitError('cannot overwrite a local work tree') |
| 1277 | raise GitError('filesystem must support symlinks') | 1273 | except OSError, e: |
| 1278 | else: | 1274 | if e.errno == errno.EPERM: |
| 1279 | raise | 1275 | raise GitError('filesystem must support symlinks') |
| 1276 | else: | ||
| 1277 | raise | ||
| 1278 | |||
| 1279 | def _InitWorkTree(self): | ||
| 1280 | dotgit = os.path.join(self.worktree, '.git') | ||
| 1281 | if not os.path.exists(dotgit): | ||
| 1282 | self._LinkWorkTree() | ||
| 1280 | 1283 | ||
| 1281 | _lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId()) | 1284 | _lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId()) |
| 1282 | 1285 | ||
| @@ -1574,15 +1577,17 @@ class SyncBuffer(object): | |||
| 1574 | class MetaProject(Project): | 1577 | class MetaProject(Project): |
| 1575 | """A special project housed under .repo. | 1578 | """A special project housed under .repo. |
| 1576 | """ | 1579 | """ |
| 1577 | def __init__(self, manifest, name, gitdir, worktree): | 1580 | def __init__(self, manifest, name, gitdir, worktree, relpath=None): |
| 1578 | repodir = manifest.repodir | 1581 | repodir = manifest.repodir |
| 1582 | if relpath is None: | ||
| 1583 | relpath = '.repo/%s' % name | ||
| 1579 | Project.__init__(self, | 1584 | Project.__init__(self, |
| 1580 | manifest = manifest, | 1585 | manifest = manifest, |
| 1581 | name = name, | 1586 | name = name, |
| 1582 | gitdir = gitdir, | 1587 | gitdir = gitdir, |
| 1583 | worktree = worktree, | 1588 | worktree = worktree, |
| 1584 | remote = RemoteSpec('origin'), | 1589 | remote = RemoteSpec('origin'), |
| 1585 | relpath = '.repo/%s' % name, | 1590 | relpath = relpath, |
| 1586 | revisionExpr = 'refs/heads/master', | 1591 | revisionExpr = 'refs/heads/master', |
| 1587 | revisionId = None) | 1592 | revisionId = None) |
| 1588 | 1593 | ||
| @@ -1590,10 +1595,12 @@ class MetaProject(Project): | |||
| 1590 | if self.Exists: | 1595 | if self.Exists: |
| 1591 | cb = self.CurrentBranch | 1596 | cb = self.CurrentBranch |
| 1592 | if cb: | 1597 | if cb: |
| 1593 | base = self.GetBranch(cb).merge | 1598 | cb = self.GetBranch(cb) |
| 1594 | if base: | 1599 | if cb.merge: |
| 1595 | self.revisionExpr = base | 1600 | self.revisionExpr = cb.merge |
| 1596 | self.revisionId = None | 1601 | self.revisionId = None |
| 1602 | if cb.remote and cb.remote.name: | ||
| 1603 | self.remote.name = cb.remote.name | ||
| 1597 | 1604 | ||
| 1598 | @property | 1605 | @property |
| 1599 | def LastFetch(self): | 1606 | def LastFetch(self): |
| @@ -109,12 +109,17 @@ group = init_optparse.add_option_group('Manifest options') | |||
| 109 | group.add_option('-u', '--manifest-url', | 109 | group.add_option('-u', '--manifest-url', |
| 110 | dest='manifest_url', | 110 | dest='manifest_url', |
| 111 | help='manifest repository location', metavar='URL') | 111 | help='manifest repository location', metavar='URL') |
| 112 | group.add_option('-o', '--origin', | ||
| 113 | dest='manifest_origin', | ||
| 114 | help="use REMOTE instead of 'origin' to track upstream", | ||
| 115 | metavar='REMOTE') | ||
| 112 | group.add_option('-b', '--manifest-branch', | 116 | group.add_option('-b', '--manifest-branch', |
| 113 | dest='manifest_branch', | 117 | dest='manifest_branch', |
| 114 | help='manifest branch or revision', metavar='REVISION') | 118 | help='manifest branch or revision', metavar='REVISION') |
| 115 | group.add_option('-m', '--manifest-name', | 119 | group.add_option('-m', '--manifest-name', |
| 116 | dest='manifest_name', | 120 | dest='manifest_name', |
| 117 | help='initial manifest file', metavar='NAME.xml') | 121 | help='initial manifest file (deprecated)', |
| 122 | metavar='NAME.xml') | ||
| 118 | group.add_option('--mirror', | 123 | group.add_option('--mirror', |
| 119 | dest='mirror', action='store_true', | 124 | dest='mirror', action='store_true', |
| 120 | help='mirror the forrest') | 125 | help='mirror the forrest') |
diff --git a/subcmds/help.py b/subcmds/help.py index 90b817db..e2f3074c 100644 --- a/subcmds/help.py +++ b/subcmds/help.py | |||
| @@ -165,6 +165,7 @@ See 'repo help --all' for a complete list of recognized commands. | |||
| 165 | print >>sys.stderr, "repo: '%s' is not a repo command." % name | 165 | print >>sys.stderr, "repo: '%s' is not a repo command." % name |
| 166 | sys.exit(1) | 166 | sys.exit(1) |
| 167 | 167 | ||
| 168 | cmd.repodir = self.repodir | ||
| 168 | self._PrintCommandHelp(cmd) | 169 | self._PrintCommandHelp(cmd) |
| 169 | 170 | ||
| 170 | else: | 171 | else: |
diff --git a/subcmds/init.py b/subcmds/init.py index 17edfa05..2ca4e163 100644 --- a/subcmds/init.py +++ b/subcmds/init.py | |||
| @@ -21,6 +21,9 @@ from command import InteractiveCommand, MirrorSafeCommand | |||
| 21 | from error import ManifestParseError | 21 | from error import ManifestParseError |
| 22 | from project import SyncBuffer | 22 | from project import SyncBuffer |
| 23 | from git_command import git_require, MIN_GIT_VERSION | 23 | from git_command import git_require, MIN_GIT_VERSION |
| 24 | from manifest_submodule import SubmoduleManifest | ||
| 25 | from manifest_xml import XmlManifest | ||
| 26 | from subcmds.sync import _ReloadManifest | ||
| 24 | 27 | ||
| 25 | class Init(InteractiveCommand, MirrorSafeCommand): | 28 | class Init(InteractiveCommand, MirrorSafeCommand): |
| 26 | common = True | 29 | common = True |
| @@ -72,9 +75,15 @@ to update the working directory files. | |||
| 72 | g.add_option('-b', '--manifest-branch', | 75 | g.add_option('-b', '--manifest-branch', |
| 73 | dest='manifest_branch', | 76 | dest='manifest_branch', |
| 74 | help='manifest branch or revision', metavar='REVISION') | 77 | help='manifest branch or revision', metavar='REVISION') |
| 75 | g.add_option('-m', '--manifest-name', | 78 | g.add_option('-o', '--origin', |
| 76 | dest='manifest_name', default='default.xml', | 79 | dest='manifest_origin', |
| 77 | help='initial manifest file', metavar='NAME.xml') | 80 | help="use REMOTE instead of 'origin' to track upstream", |
| 81 | metavar='REMOTE') | ||
| 82 | if isinstance(self.manifest, XmlManifest) \ | ||
| 83 | or not self.manifest.manifestProject.Exists: | ||
| 84 | g.add_option('-m', '--manifest-name', | ||
| 85 | dest='manifest_name', default='default.xml', | ||
| 86 | help='initial manifest file', metavar='NAME.xml') | ||
| 78 | g.add_option('--mirror', | 87 | g.add_option('--mirror', |
| 79 | dest='mirror', action='store_true', | 88 | dest='mirror', action='store_true', |
| 80 | help='mirror the forrest') | 89 | help='mirror the forrest') |
| @@ -94,30 +103,42 @@ to update the working directory files. | |||
| 94 | dest='no_repo_verify', action='store_true', | 103 | dest='no_repo_verify', action='store_true', |
| 95 | help='do not verify repo source code') | 104 | help='do not verify repo source code') |
| 96 | 105 | ||
| 97 | def _SyncManifest(self, opt): | 106 | def _ApplyOptions(self, opt, is_new): |
| 98 | m = self.manifest.manifestProject | 107 | m = self.manifest.manifestProject |
| 99 | is_new = not m.Exists | ||
| 100 | 108 | ||
| 101 | if is_new: | 109 | if is_new: |
| 102 | if not opt.manifest_url: | 110 | if opt.manifest_origin: |
| 103 | print >>sys.stderr, 'fatal: manifest url (-u) is required.' | 111 | m.remote.name = opt.manifest_origin |
| 104 | sys.exit(1) | ||
| 105 | |||
| 106 | if not opt.quiet: | ||
| 107 | print >>sys.stderr, 'Getting manifest ...' | ||
| 108 | print >>sys.stderr, ' from %s' % opt.manifest_url | ||
| 109 | m._InitGitDir() | ||
| 110 | 112 | ||
| 111 | if opt.manifest_branch: | 113 | if opt.manifest_branch: |
| 112 | m.revisionExpr = opt.manifest_branch | 114 | m.revisionExpr = opt.manifest_branch |
| 113 | else: | 115 | else: |
| 114 | m.revisionExpr = 'refs/heads/master' | 116 | m.revisionExpr = 'refs/heads/master' |
| 115 | else: | 117 | else: |
| 118 | if opt.manifest_origin: | ||
| 119 | print >>sys.stderr, 'fatal: cannot change origin name' | ||
| 120 | sys.exit(1) | ||
| 121 | |||
| 116 | if opt.manifest_branch: | 122 | if opt.manifest_branch: |
| 117 | m.revisionExpr = opt.manifest_branch | 123 | m.revisionExpr = opt.manifest_branch |
| 118 | else: | 124 | else: |
| 119 | m.PreSync() | 125 | m.PreSync() |
| 120 | 126 | ||
| 127 | def _SyncManifest(self, opt): | ||
| 128 | m = self.manifest.manifestProject | ||
| 129 | is_new = not m.Exists | ||
| 130 | |||
| 131 | if is_new: | ||
| 132 | if not opt.manifest_url: | ||
| 133 | print >>sys.stderr, 'fatal: manifest url (-u) is required.' | ||
| 134 | sys.exit(1) | ||
| 135 | |||
| 136 | if not opt.quiet: | ||
| 137 | print >>sys.stderr, 'Getting manifest ...' | ||
| 138 | print >>sys.stderr, ' from %s' % opt.manifest_url | ||
| 139 | m._InitGitDir() | ||
| 140 | |||
| 141 | self._ApplyOptions(opt, is_new) | ||
| 121 | if opt.manifest_url: | 142 | if opt.manifest_url: |
| 122 | r = m.GetRemote(m.remote.name) | 143 | r = m.GetRemote(m.remote.name) |
| 123 | r.url = opt.manifest_url | 144 | r.url = opt.manifest_url |
| @@ -130,6 +151,7 @@ to update the working directory files. | |||
| 130 | if opt.mirror: | 151 | if opt.mirror: |
| 131 | if is_new: | 152 | if is_new: |
| 132 | m.config.SetString('repo.mirror', 'true') | 153 | m.config.SetString('repo.mirror', 'true') |
| 154 | m.config.ClearCache() | ||
| 133 | else: | 155 | else: |
| 134 | print >>sys.stderr, 'fatal: --mirror not supported on existing client' | 156 | print >>sys.stderr, 'fatal: --mirror not supported on existing client' |
| 135 | sys.exit(1) | 157 | sys.exit(1) |
| @@ -139,14 +161,33 @@ to update the working directory files. | |||
| 139 | print >>sys.stderr, 'fatal: cannot obtain manifest %s' % r.url | 161 | print >>sys.stderr, 'fatal: cannot obtain manifest %s' % r.url |
| 140 | sys.exit(1) | 162 | sys.exit(1) |
| 141 | 163 | ||
| 164 | if is_new and SubmoduleManifest.IsBare(m): | ||
| 165 | new = self.GetManifest(reparse=True, type=SubmoduleManifest) | ||
| 166 | if m.gitdir != new.manifestProject.gitdir: | ||
| 167 | os.rename(m.gitdir, new.manifestProject.gitdir) | ||
| 168 | new = self.GetManifest(reparse=True, type=SubmoduleManifest) | ||
| 169 | m = new.manifestProject | ||
| 170 | self._ApplyOptions(opt, is_new) | ||
| 171 | |||
| 172 | if not is_new: | ||
| 173 | # Force the manifest to load if it exists, the old graph | ||
| 174 | # may be needed inside of _ReloadManifest(). | ||
| 175 | # | ||
| 176 | self.manifest.projects | ||
| 177 | |||
| 142 | syncbuf = SyncBuffer(m.config) | 178 | syncbuf = SyncBuffer(m.config) |
| 143 | m.Sync_LocalHalf(syncbuf) | 179 | m.Sync_LocalHalf(syncbuf) |
| 144 | syncbuf.Finish() | 180 | syncbuf.Finish() |
| 145 | 181 | ||
| 146 | if is_new or m.CurrentBranch is None: | 182 | if isinstance(self.manifest, XmlManifest): |
| 147 | if not m.StartBranch('default'): | 183 | self._LinkManifest(opt.manifest_name) |
| 148 | print >>sys.stderr, 'fatal: cannot create default in manifest' | 184 | _ReloadManifest(self) |
| 149 | sys.exit(1) | 185 | |
| 186 | self._ApplyOptions(opt, is_new) | ||
| 187 | |||
| 188 | if not self.manifest.InitBranch(): | ||
| 189 | print >>sys.stderr, 'fatal: cannot create branch in manifest' | ||
| 190 | sys.exit(1) | ||
| 150 | 191 | ||
| 151 | def _LinkManifest(self, name): | 192 | def _LinkManifest(self, name): |
| 152 | if not name: | 193 | if not name: |
| @@ -229,7 +270,6 @@ to update the working directory files. | |||
| 229 | def Execute(self, opt, args): | 270 | def Execute(self, opt, args): |
| 230 | git_require(MIN_GIT_VERSION, fail=True) | 271 | git_require(MIN_GIT_VERSION, fail=True) |
| 231 | self._SyncManifest(opt) | 272 | self._SyncManifest(opt) |
| 232 | self._LinkManifest(opt.manifest_name) | ||
| 233 | 273 | ||
| 234 | if os.isatty(0) and os.isatty(1) and not self.manifest.IsMirror: | 274 | if os.isatty(0) and os.isatty(1) and not self.manifest.IsMirror: |
| 235 | self._ConfigureUser() | 275 | self._ConfigureUser() |
diff --git a/subcmds/manifest.py b/subcmds/manifest.py index 4374a9d0..dcd3df17 100644 --- a/subcmds/manifest.py +++ b/subcmds/manifest.py | |||
| @@ -17,14 +17,25 @@ import os | |||
| 17 | import sys | 17 | import sys |
| 18 | 18 | ||
| 19 | from command import PagedCommand | 19 | from command import PagedCommand |
| 20 | from manifest_submodule import SubmoduleManifest | ||
| 21 | from manifest_xml import XmlManifest | ||
| 22 | |||
| 23 | def _doc(name): | ||
| 24 | r = os.path.dirname(__file__) | ||
| 25 | r = os.path.dirname(r) | ||
| 26 | fd = open(os.path.join(r, 'docs', name)) | ||
| 27 | try: | ||
| 28 | return fd.read() | ||
| 29 | finally: | ||
| 30 | fd.close() | ||
| 20 | 31 | ||
| 21 | class Manifest(PagedCommand): | 32 | class Manifest(PagedCommand): |
| 22 | common = False | 33 | common = False |
| 23 | helpSummary = "Manifest inspection utility" | 34 | helpSummary = "Manifest inspection utility" |
| 24 | helpUsage = """ | 35 | helpUsage = """ |
| 25 | %prog [-o {-|NAME.xml} [-r]] | 36 | %prog [options] |
| 26 | """ | 37 | """ |
| 27 | _helpDescription = """ | 38 | _xmlHelp = """ |
| 28 | 39 | ||
| 29 | With the -o option, exports the current manifest for inspection. | 40 | With the -o option, exports the current manifest for inspection. |
| 30 | The manifest and (if present) local_manifest.xml are combined | 41 | The manifest and (if present) local_manifest.xml are combined |
| @@ -35,23 +46,30 @@ in a Git repository for use during future 'repo init' invocations. | |||
| 35 | 46 | ||
| 36 | @property | 47 | @property |
| 37 | def helpDescription(self): | 48 | def helpDescription(self): |
| 38 | help = self._helpDescription + '\n' | 49 | help = '' |
| 39 | r = os.path.dirname(__file__) | 50 | if isinstance(self.manifest, XmlManifest): |
| 40 | r = os.path.dirname(r) | 51 | help += self._xmlHelp + '\n' + _doc('manifest_xml.txt') |
| 41 | fd = open(os.path.join(r, 'docs', 'manifest-format.txt')) | 52 | if isinstance(self.manifest, SubmoduleManifest): |
| 42 | for line in fd: | 53 | help += _doc('manifest_submodule.txt') |
| 43 | help += line | ||
| 44 | fd.close() | ||
| 45 | return help | 54 | return help |
| 46 | 55 | ||
| 47 | def _Options(self, p): | 56 | def _Options(self, p): |
| 48 | p.add_option('-r', '--revision-as-HEAD', | 57 | if isinstance(self.manifest, XmlManifest): |
| 49 | dest='peg_rev', action='store_true', | 58 | p.add_option('--upgrade', |
| 50 | help='Save revisions as current HEAD') | 59 | dest='upgrade', action='store_true', |
| 51 | p.add_option('-o', '--output-file', | 60 | help='Upgrade XML manifest to submodule') |
| 52 | dest='output_file', | 61 | p.add_option('-r', '--revision-as-HEAD', |
| 53 | help='File to save the manifest to', | 62 | dest='peg_rev', action='store_true', |
| 54 | metavar='-|NAME.xml') | 63 | help='Save revisions as current HEAD') |
| 64 | p.add_option('-o', '--output-file', | ||
| 65 | dest='output_file', | ||
| 66 | help='File to save the manifest to', | ||
| 67 | metavar='-|NAME.xml') | ||
| 68 | |||
| 69 | def WantPager(self, opt): | ||
| 70 | if isinstance(self.manifest, XmlManifest) and opt.upgrade: | ||
| 71 | return False | ||
| 72 | return True | ||
| 55 | 73 | ||
| 56 | def _Output(self, opt): | 74 | def _Output(self, opt): |
| 57 | if opt.output_file == '-': | 75 | if opt.output_file == '-': |
| @@ -64,13 +82,38 @@ in a Git repository for use during future 'repo init' invocations. | |||
| 64 | if opt.output_file != '-': | 82 | if opt.output_file != '-': |
| 65 | print >>sys.stderr, 'Saved manifest to %s' % opt.output_file | 83 | print >>sys.stderr, 'Saved manifest to %s' % opt.output_file |
| 66 | 84 | ||
| 85 | def _Upgrade(self): | ||
| 86 | old = self.manifest | ||
| 87 | |||
| 88 | if isinstance(old, SubmoduleManifest): | ||
| 89 | print >>sys.stderr, 'error: already upgraded' | ||
| 90 | sys.exit(1) | ||
| 91 | |||
| 92 | old._Load() | ||
| 93 | for p in old.projects.values(): | ||
| 94 | if not os.path.exists(p.gitdir) \ | ||
| 95 | or not os.path.exists(p.worktree): | ||
| 96 | print >>sys.stderr, 'fatal: project "%s" missing' % p.relpath | ||
| 97 | sys.exit(1) | ||
| 98 | |||
| 99 | new = SubmoduleManifest(old.repodir) | ||
| 100 | new.FromXml_Local_1(old, checkout=False) | ||
| 101 | new.FromXml_Definition(old) | ||
| 102 | new.FromXml_Local_2(old) | ||
| 103 | print >>sys.stderr, 'upgraded manifest; commit result manually' | ||
| 104 | |||
| 67 | def Execute(self, opt, args): | 105 | def Execute(self, opt, args): |
| 68 | if args: | 106 | if args: |
| 69 | self.Usage() | 107 | self.Usage() |
| 70 | 108 | ||
| 71 | if opt.output_file is not None: | 109 | if isinstance(self.manifest, XmlManifest): |
| 72 | self._Output(opt) | 110 | if opt.upgrade: |
| 73 | return | 111 | self._Upgrade() |
| 112 | return | ||
| 113 | |||
| 114 | if opt.output_file is not None: | ||
| 115 | self._Output(opt) | ||
| 116 | return | ||
| 74 | 117 | ||
| 75 | print >>sys.stderr, 'error: no operation to perform' | 118 | print >>sys.stderr, 'error: no operation to perform' |
| 76 | print >>sys.stderr, 'error: see repo help manifest' | 119 | print >>sys.stderr, 'error: see repo help manifest' |
diff --git a/subcmds/rebase.py b/subcmds/rebase.py index 7c8e9389..e341296d 100644 --- a/subcmds/rebase.py +++ b/subcmds/rebase.py | |||
| @@ -17,7 +17,7 @@ import sys | |||
| 17 | 17 | ||
| 18 | from command import Command | 18 | from command import Command |
| 19 | from git_command import GitCommand | 19 | from git_command import GitCommand |
| 20 | from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M | 20 | from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB |
| 21 | from error import GitError | 21 | from error import GitError |
| 22 | 22 | ||
| 23 | class Rebase(Command): | 23 | class Rebase(Command): |
diff --git a/subcmds/sync.py b/subcmds/sync.py index 36ef16db..16f1d189 100644 --- a/subcmds/sync.py +++ b/subcmds/sync.py | |||
| @@ -336,7 +336,14 @@ uncommitted changes are present' % project.relpath | |||
| 336 | # bail out now; the rest touches the working tree | 336 | # bail out now; the rest touches the working tree |
| 337 | return | 337 | return |
| 338 | 338 | ||
| 339 | self.manifest._Unload() | 339 | if mp.HasChanges: |
| 340 | syncbuf = SyncBuffer(mp.config) | ||
| 341 | mp.Sync_LocalHalf(syncbuf) | ||
| 342 | if not syncbuf.Finish(): | ||
| 343 | sys.exit(1) | ||
| 344 | _ReloadManifest(self) | ||
| 345 | mp = self.manifest.manifestProject | ||
| 346 | |||
| 340 | all = self.GetProjects(args, missing_ok=True) | 347 | all = self.GetProjects(args, missing_ok=True) |
| 341 | missing = [] | 348 | missing = [] |
| 342 | for project in all: | 349 | for project in all: |
| @@ -363,10 +370,16 @@ uncommitted changes are present' % project.relpath | |||
| 363 | if not syncbuf.Finish(): | 370 | if not syncbuf.Finish(): |
| 364 | sys.exit(1) | 371 | sys.exit(1) |
| 365 | 372 | ||
| 366 | # If there's a notice that's supposed to print at the end of the sync, print | 373 | def _ReloadManifest(cmd): |
| 367 | # it now... | 374 | old = cmd.manifest |
| 368 | if self.manifest.notice: | 375 | new = cmd.GetManifest(reparse=True) |
| 369 | print self.manifest.notice | 376 | |
| 377 | if old.__class__ != new.__class__: | ||
| 378 | print >>sys.stderr, 'NOTICE: manifest format has changed ***' | ||
| 379 | new.Upgrade_Local(old) | ||
| 380 | else: | ||
| 381 | if new.notice: | ||
| 382 | print new.notice | ||
| 370 | 383 | ||
| 371 | def _PostRepoUpgrade(manifest): | 384 | def _PostRepoUpgrade(manifest): |
| 372 | for project in manifest.projects.values(): | 385 | for project in manifest.projects.values(): |
