diff options
39 files changed, 1529 insertions, 737 deletions
| @@ -53,7 +53,7 @@ load-plugins= | |||
| 53 | enable=RP0004 | 53 | enable=RP0004 |
| 54 | 54 | ||
| 55 | # Disable the message(s) with the given id(s). | 55 | # Disable the message(s) with the given id(s). |
| 56 | disable=R0903,R0912,R0913,R0914,R0915,W0141,C0111,C0103,C0323,C0322,C0324,W0603,W0703,R0911,C0301,C0302,R0902,R0904,W0142,W0212,E1101,E1103,R0201,W0201,W0122,W0232,W0311,RP0001,RP0003,RP0101,RP0002,RP0401,RP0701,RP0801 | 56 | disable=R0903,R0912,R0913,R0914,R0915,W0141,C0111,C0103,W0603,W0703,R0911,C0301,C0302,R0902,R0904,W0142,W0212,E1101,E1103,R0201,W0201,W0122,W0232,RP0001,RP0003,RP0101,RP0002,RP0401,RP0701,RP0801 |
| 57 | 57 | ||
| 58 | [REPORTS] | 58 | [REPORTS] |
| 59 | 59 | ||
| @@ -36,50 +36,51 @@ ATTRS = {None :-1, | |||
| 36 | 'blink' : 5, | 36 | 'blink' : 5, |
| 37 | 'reverse': 7} | 37 | 'reverse': 7} |
| 38 | 38 | ||
| 39 | RESET = "\033[m" | 39 | RESET = "\033[m" # pylint: disable=W1401 |
| 40 | # backslash is not anomalous | ||
| 40 | 41 | ||
| 41 | def is_color(s): | 42 | def is_color(s): |
| 42 | return s in COLORS | 43 | return s in COLORS |
| 43 | 44 | ||
| 44 | def is_attr(s): | 45 | def is_attr(s): |
| 45 | return s in ATTRS | 46 | return s in ATTRS |
| 46 | 47 | ||
| 47 | def _Color(fg = None, bg = None, attr = None): | 48 | def _Color(fg = None, bg = None, attr = None): |
| 48 | fg = COLORS[fg] | 49 | fg = COLORS[fg] |
| 49 | bg = COLORS[bg] | 50 | bg = COLORS[bg] |
| 50 | attr = ATTRS[attr] | 51 | attr = ATTRS[attr] |
| 51 | 52 | ||
| 52 | if attr >= 0 or fg >= 0 or bg >= 0: | 53 | if attr >= 0 or fg >= 0 or bg >= 0: |
| 53 | need_sep = False | 54 | need_sep = False |
| 54 | code = "\033[" | 55 | code = "\033[" #pylint: disable=W1401 |
| 55 | 56 | ||
| 56 | if attr >= 0: | 57 | if attr >= 0: |
| 57 | code += chr(ord('0') + attr) | 58 | code += chr(ord('0') + attr) |
| 58 | need_sep = True | 59 | need_sep = True |
| 59 | 60 | ||
| 60 | if fg >= 0: | 61 | if fg >= 0: |
| 61 | if need_sep: | 62 | if need_sep: |
| 62 | code += ';' | 63 | code += ';' |
| 63 | need_sep = True | 64 | need_sep = True |
| 64 | 65 | ||
| 65 | if fg < 8: | 66 | if fg < 8: |
| 66 | code += '3%c' % (ord('0') + fg) | 67 | code += '3%c' % (ord('0') + fg) |
| 67 | else: | 68 | else: |
| 68 | code += '38;5;%d' % fg | 69 | code += '38;5;%d' % fg |
| 69 | 70 | ||
| 70 | if bg >= 0: | 71 | if bg >= 0: |
| 71 | if need_sep: | 72 | if need_sep: |
| 72 | code += ';' | 73 | code += ';' |
| 73 | need_sep = True | 74 | need_sep = True |
| 74 | 75 | ||
| 75 | if bg < 8: | 76 | if bg < 8: |
| 76 | code += '4%c' % (ord('0') + bg) | 77 | code += '4%c' % (ord('0') + bg) |
| 77 | else: | 78 | else: |
| 78 | code += '48;5;%d' % bg | 79 | code += '48;5;%d' % bg |
| 79 | code += 'm' | 80 | code += 'm' |
| 80 | else: | 81 | else: |
| 81 | code = '' | 82 | code = '' |
| 82 | return code | 83 | return code |
| 83 | 84 | ||
| 84 | 85 | ||
| 85 | class Coloring(object): | 86 | class Coloring(object): |
| @@ -22,6 +22,7 @@ import sys | |||
| 22 | from error import NoSuchProjectError | 22 | from error import NoSuchProjectError |
| 23 | from error import InvalidProjectGroupsError | 23 | from error import InvalidProjectGroupsError |
| 24 | 24 | ||
| 25 | |||
| 25 | class Command(object): | 26 | class Command(object): |
| 26 | """Base class for any command line action in repo. | 27 | """Base class for any command line action in repo. |
| 27 | """ | 28 | """ |
| @@ -33,6 +34,27 @@ class Command(object): | |||
| 33 | def WantPager(self, opt): | 34 | def WantPager(self, opt): |
| 34 | return False | 35 | return False |
| 35 | 36 | ||
| 37 | def ReadEnvironmentOptions(self, opts): | ||
| 38 | """ Set options from environment variables. """ | ||
| 39 | |||
| 40 | env_options = self._RegisteredEnvironmentOptions() | ||
| 41 | |||
| 42 | for env_key, opt_key in env_options.items(): | ||
| 43 | # Get the user-set option value if any | ||
| 44 | opt_value = getattr(opts, opt_key) | ||
| 45 | |||
| 46 | # If the value is set, it means the user has passed it as a command | ||
| 47 | # line option, and we should use that. Otherwise we can try to set it | ||
| 48 | # with the value from the corresponding environment variable. | ||
| 49 | if opt_value is not None: | ||
| 50 | continue | ||
| 51 | |||
| 52 | env_value = os.environ.get(env_key) | ||
| 53 | if env_value is not None: | ||
| 54 | setattr(opts, opt_key, env_value) | ||
| 55 | |||
| 56 | return opts | ||
| 57 | |||
| 36 | @property | 58 | @property |
| 37 | def OptionParser(self): | 59 | def OptionParser(self): |
| 38 | if self._optparse is None: | 60 | if self._optparse is None: |
| @@ -49,6 +71,24 @@ class Command(object): | |||
| 49 | """Initialize the option parser. | 71 | """Initialize the option parser. |
| 50 | """ | 72 | """ |
| 51 | 73 | ||
| 74 | def _RegisteredEnvironmentOptions(self): | ||
| 75 | """Get options that can be set from environment variables. | ||
| 76 | |||
| 77 | Return a dictionary mapping environment variable name | ||
| 78 | to option key name that it can override. | ||
| 79 | |||
| 80 | Example: {'REPO_MY_OPTION': 'my_option'} | ||
| 81 | |||
| 82 | Will allow the option with key value 'my_option' to be set | ||
| 83 | from the value in the environment variable named 'REPO_MY_OPTION'. | ||
| 84 | |||
| 85 | Note: This does not work properly for options that are explicitly | ||
| 86 | set to None by the user, or options that are defined with a | ||
| 87 | default value other than None. | ||
| 88 | |||
| 89 | """ | ||
| 90 | return {} | ||
| 91 | |||
| 52 | def Usage(self): | 92 | def Usage(self): |
| 53 | """Display usage and terminate. | 93 | """Display usage and terminate. |
| 54 | """ | 94 | """ |
| @@ -60,7 +100,33 @@ class Command(object): | |||
| 60 | """ | 100 | """ |
| 61 | raise NotImplementedError | 101 | raise NotImplementedError |
| 62 | 102 | ||
| 63 | def GetProjects(self, args, missing_ok=False): | 103 | def _ResetPathToProjectMap(self, projects): |
| 104 | self._by_path = dict((p.worktree, p) for p in projects) | ||
| 105 | |||
| 106 | def _UpdatePathToProjectMap(self, project): | ||
| 107 | self._by_path[project.worktree] = project | ||
| 108 | |||
| 109 | def _GetProjectByPath(self, path): | ||
| 110 | project = None | ||
| 111 | if os.path.exists(path): | ||
| 112 | oldpath = None | ||
| 113 | while path \ | ||
| 114 | and path != oldpath \ | ||
| 115 | and path != self.manifest.topdir: | ||
| 116 | try: | ||
| 117 | project = self._by_path[path] | ||
| 118 | break | ||
| 119 | except KeyError: | ||
| 120 | oldpath = path | ||
| 121 | path = os.path.dirname(path) | ||
| 122 | else: | ||
| 123 | try: | ||
| 124 | project = self._by_path[path] | ||
| 125 | except KeyError: | ||
| 126 | pass | ||
| 127 | return project | ||
| 128 | |||
| 129 | def GetProjects(self, args, missing_ok=False, submodules_ok=False): | ||
| 64 | """A list of projects that match the arguments. | 130 | """A list of projects that match the arguments. |
| 65 | """ | 131 | """ |
| 66 | all_projects = self.manifest.projects | 132 | all_projects = self.manifest.projects |
| @@ -71,43 +137,40 @@ class Command(object): | |||
| 71 | groups = mp.config.GetString('manifest.groups') | 137 | groups = mp.config.GetString('manifest.groups') |
| 72 | if not groups: | 138 | if not groups: |
| 73 | groups = 'all,-notdefault,platform-' + platform.system().lower() | 139 | groups = 'all,-notdefault,platform-' + platform.system().lower() |
| 74 | groups = [x for x in re.split('[,\s]+', groups) if x] | 140 | groups = [x for x in re.split(r'[,\s]+', groups) if x] |
| 75 | 141 | ||
| 76 | if not args: | 142 | if not args: |
| 77 | for project in all_projects.values(): | 143 | all_projects_list = all_projects.values() |
| 144 | derived_projects = {} | ||
| 145 | for project in all_projects_list: | ||
| 146 | if submodules_ok or project.sync_s: | ||
| 147 | derived_projects.update((p.name, p) | ||
| 148 | for p in project.GetDerivedSubprojects()) | ||
| 149 | all_projects_list.extend(derived_projects.values()) | ||
| 150 | for project in all_projects_list: | ||
| 78 | if ((missing_ok or project.Exists) and | 151 | if ((missing_ok or project.Exists) and |
| 79 | project.MatchesGroups(groups)): | 152 | project.MatchesGroups(groups)): |
| 80 | result.append(project) | 153 | result.append(project) |
| 81 | else: | 154 | else: |
| 82 | by_path = None | 155 | self._ResetPathToProjectMap(all_projects.values()) |
| 83 | 156 | ||
| 84 | for arg in args: | 157 | for arg in args: |
| 85 | project = all_projects.get(arg) | 158 | project = all_projects.get(arg) |
| 86 | 159 | ||
| 87 | if not project: | 160 | if not project: |
| 88 | path = os.path.abspath(arg).replace('\\', '/') | 161 | path = os.path.abspath(arg).replace('\\', '/') |
| 89 | 162 | project = self._GetProjectByPath(path) | |
| 90 | if not by_path: | 163 | |
| 91 | by_path = dict() | 164 | # If it's not a derived project, update path->project mapping and |
| 92 | for p in all_projects.values(): | 165 | # search again, as arg might actually point to a derived subproject. |
| 93 | by_path[p.worktree] = p | 166 | if (project and not project.Derived and |
| 94 | 167 | (submodules_ok or project.sync_s)): | |
| 95 | if os.path.exists(path): | 168 | search_again = False |
| 96 | oldpath = None | 169 | for subproject in project.GetDerivedSubprojects(): |
| 97 | while path \ | 170 | self._UpdatePathToProjectMap(subproject) |
| 98 | and path != oldpath \ | 171 | search_again = True |
| 99 | and path != self.manifest.topdir: | 172 | if search_again: |
| 100 | try: | 173 | project = self._GetProjectByPath(path) or project |
| 101 | project = by_path[path] | ||
| 102 | break | ||
| 103 | except KeyError: | ||
| 104 | oldpath = path | ||
| 105 | path = os.path.dirname(path) | ||
| 106 | else: | ||
| 107 | try: | ||
| 108 | project = by_path[path] | ||
| 109 | except KeyError: | ||
| 110 | pass | ||
| 111 | 174 | ||
| 112 | if not project: | 175 | if not project: |
| 113 | raise NoSuchProjectError(arg) | 176 | raise NoSuchProjectError(arg) |
diff --git a/docs/manifest-format.txt b/docs/manifest-format.txt index f499868c..0bf09f6f 100644 --- a/docs/manifest-format.txt +++ b/docs/manifest-format.txt | |||
| @@ -41,17 +41,21 @@ following DTD: | |||
| 41 | <!ATTLIST default revision CDATA #IMPLIED> | 41 | <!ATTLIST default revision CDATA #IMPLIED> |
| 42 | <!ATTLIST default sync-j CDATA #IMPLIED> | 42 | <!ATTLIST default sync-j CDATA #IMPLIED> |
| 43 | <!ATTLIST default sync-c CDATA #IMPLIED> | 43 | <!ATTLIST default sync-c CDATA #IMPLIED> |
| 44 | <!ATTLIST default sync-s CDATA #IMPLIED> | ||
| 44 | 45 | ||
| 45 | <!ELEMENT manifest-server (EMPTY)> | 46 | <!ELEMENT manifest-server (EMPTY)> |
| 46 | <!ATTLIST url CDATA #REQUIRED> | 47 | <!ATTLIST url CDATA #REQUIRED> |
| 47 | 48 | ||
| 48 | <!ELEMENT project (annotation?)> | 49 | <!ELEMENT project (annotation?, |
| 50 | project*)> | ||
| 49 | <!ATTLIST project name CDATA #REQUIRED> | 51 | <!ATTLIST project name CDATA #REQUIRED> |
| 50 | <!ATTLIST project path CDATA #IMPLIED> | 52 | <!ATTLIST project path CDATA #IMPLIED> |
| 51 | <!ATTLIST project remote IDREF #IMPLIED> | 53 | <!ATTLIST project remote IDREF #IMPLIED> |
| 52 | <!ATTLIST project revision CDATA #IMPLIED> | 54 | <!ATTLIST project revision CDATA #IMPLIED> |
| 53 | <!ATTLIST project groups CDATA #IMPLIED> | 55 | <!ATTLIST project groups CDATA #IMPLIED> |
| 54 | <!ATTLIST project sync-c CDATA #IMPLIED> | 56 | <!ATTLIST project sync-c CDATA #IMPLIED> |
| 57 | <!ATTLIST project sync-s CDATA #IMPLIED> | ||
| 58 | <!ATTLIST project upstream CDATA #IMPLIED> | ||
| 55 | 59 | ||
| 56 | <!ELEMENT annotation (EMPTY)> | 60 | <!ELEMENT annotation (EMPTY)> |
| 57 | <!ATTLIST annotation name CDATA #REQUIRED> | 61 | <!ATTLIST annotation name CDATA #REQUIRED> |
| @@ -119,6 +123,15 @@ Attribute `revision`: Name of a Git branch (e.g. `master` or | |||
| 119 | `refs/heads/master`). Project elements lacking their own | 123 | `refs/heads/master`). Project elements lacking their own |
| 120 | revision attribute will use this revision. | 124 | revision attribute will use this revision. |
| 121 | 125 | ||
| 126 | Attribute `sync_j`: Number of parallel jobs to use when synching. | ||
| 127 | |||
| 128 | Attribute `sync_c`: Set to true to only sync the given Git | ||
| 129 | branch (specified in the `revision` attribute) rather than the | ||
| 130 | whole ref space. Project elements lacking a sync_c element of | ||
| 131 | their own will use this value. | ||
| 132 | |||
| 133 | Attribute `sync_s`: Set to true to also sync sub-projects. | ||
| 134 | |||
| 122 | 135 | ||
| 123 | Element manifest-server | 136 | Element manifest-server |
| 124 | ----------------------- | 137 | ----------------------- |
| @@ -152,7 +165,10 @@ Element project | |||
| 152 | 165 | ||
| 153 | One or more project elements may be specified. Each element | 166 | One or more project elements may be specified. Each element |
| 154 | describes a single Git repository to be cloned into the repo | 167 | describes a single Git repository to be cloned into the repo |
| 155 | client workspace. | 168 | client workspace. You may specify Git-submodules by creating a |
| 169 | nested project. Git-submodules will be automatically | ||
| 170 | recognized and inherit their parent's attributes, but those | ||
| 171 | may be overridden by an explicitly specified project element. | ||
| 156 | 172 | ||
| 157 | Attribute `name`: A unique name for this project. The project's | 173 | Attribute `name`: A unique name for this project. The project's |
| 158 | name is appended onto its remote's fetch URL to generate the actual | 174 | name is appended onto its remote's fetch URL to generate the actual |
| @@ -163,7 +179,8 @@ URL to configure the Git remote with. The URL gets formed as: | |||
| 163 | where ${remote_fetch} is the remote's fetch attribute and | 179 | where ${remote_fetch} is the remote's fetch attribute and |
| 164 | ${project_name} is the project's name attribute. The suffix ".git" | 180 | ${project_name} is the project's name attribute. The suffix ".git" |
| 165 | is always appended as repo assumes the upstream is a forest of | 181 | is always appended as repo assumes the upstream is a forest of |
| 166 | bare Git repositories. | 182 | bare Git repositories. If the project has a parent element, its |
| 183 | name will be prefixed by the parent's. | ||
| 167 | 184 | ||
| 168 | The project name must match the name Gerrit knows, if Gerrit is | 185 | The project name must match the name Gerrit knows, if Gerrit is |
| 169 | being used for code reviews. | 186 | being used for code reviews. |
| @@ -171,6 +188,8 @@ being used for code reviews. | |||
| 171 | Attribute `path`: An optional path relative to the top directory | 188 | Attribute `path`: An optional path relative to the top directory |
| 172 | of the repo client where the Git working directory for this project | 189 | of the repo client where the Git working directory for this project |
| 173 | should be placed. If not supplied the project name is used. | 190 | should be placed. If not supplied the project name is used. |
| 191 | If the project has a parent element, its path will be prefixed | ||
| 192 | by the parent's. | ||
| 174 | 193 | ||
| 175 | Attribute `remote`: Name of a previously defined remote element. | 194 | Attribute `remote`: Name of a previously defined remote element. |
| 176 | If not supplied the remote given by the default element is used. | 195 | If not supplied the remote given by the default element is used. |
| @@ -190,6 +209,18 @@ its name:`name` and path:`path`. E.g. for | |||
| 190 | definition is implicitly in the following manifest groups: | 209 | definition is implicitly in the following manifest groups: |
| 191 | default, name:monkeys, and path:barrel-of. If you place a project in the | 210 | default, name:monkeys, and path:barrel-of. If you place a project in the |
| 192 | group "notdefault", it will not be automatically downloaded by repo. | 211 | group "notdefault", it will not be automatically downloaded by repo. |
| 212 | If the project has a parent element, the `name` and `path` here | ||
| 213 | are the prefixed ones. | ||
| 214 | |||
| 215 | Attribute `sync_c`: Set to true to only sync the given Git | ||
| 216 | branch (specified in the `revision` attribute) rather than the | ||
| 217 | whole ref space. | ||
| 218 | |||
| 219 | Attribute `sync_s`: Set to true to also sync sub-projects. | ||
| 220 | |||
| 221 | Attribute `upstream`: Name of the Git branch in which a sha1 | ||
| 222 | can be found. Used when syncing a revision locked manifest in | ||
| 223 | -c mode to avoid having to sync the entire ref space. | ||
| 193 | 224 | ||
| 194 | Element annotation | 225 | Element annotation |
| 195 | ------------------ | 226 | ------------------ |
| @@ -209,7 +240,7 @@ Deletes the named project from the internal manifest table, possibly | |||
| 209 | allowing a subsequent project element in the same manifest file to | 240 | allowing a subsequent project element in the same manifest file to |
| 210 | replace the project with a different source. | 241 | replace the project with a different source. |
| 211 | 242 | ||
| 212 | This element is mostly useful in the local_manifest.xml, where | 243 | This element is mostly useful in a local manifest file, where |
| 213 | the user can remove a project, and possibly replace it with their | 244 | the user can remove a project, and possibly replace it with their |
| 214 | own definition. | 245 | own definition. |
| 215 | 246 | ||
| @@ -218,21 +249,25 @@ Element include | |||
| 218 | 249 | ||
| 219 | This element provides the capability of including another manifest | 250 | This element provides the capability of including another manifest |
| 220 | file into the originating manifest. Normal rules apply for the | 251 | file into the originating manifest. Normal rules apply for the |
| 221 | target manifest to include- it must be a usable manifest on it's own. | 252 | target manifest to include - it must be a usable manifest on its own. |
| 222 | 253 | ||
| 223 | Attribute `name`; the manifest to include, specified relative to | 254 | Attribute `name`: the manifest to include, specified relative to |
| 224 | the manifest repositories root. | 255 | the manifest repository's root. |
| 225 | 256 | ||
| 226 | 257 | ||
| 227 | Local Manifest | 258 | Local Manifests |
| 228 | ============== | 259 | =============== |
| 229 | 260 | ||
| 230 | Additional remotes and projects may be added through a local | 261 | Additional remotes and projects may be added through local manifest |
| 231 | manifest, stored in `$TOP_DIR/.repo/local_manifest.xml`. | 262 | files stored in `$TOP_DIR/.repo/local_manifests/*.xml`. |
| 232 | 263 | ||
| 233 | For example: | 264 | For example: |
| 234 | 265 | ||
| 235 | $ cat .repo/local_manifest.xml | 266 | $ ls .repo/local_manifests |
| 267 | local_manifest.xml | ||
| 268 | another_local_manifest.xml | ||
| 269 | |||
| 270 | $ cat .repo/local_manifests/local_manifest.xml | ||
| 236 | <?xml version="1.0" encoding="UTF-8"?> | 271 | <?xml version="1.0" encoding="UTF-8"?> |
| 237 | <manifest> | 272 | <manifest> |
| 238 | <project path="manifest" | 273 | <project path="manifest" |
| @@ -241,6 +276,17 @@ For example: | |||
| 241 | name="platform/manifest" /> | 276 | name="platform/manifest" /> |
| 242 | </manifest> | 277 | </manifest> |
| 243 | 278 | ||
| 244 | Users may add projects to the local manifest prior to a `repo sync` | 279 | Users may add projects to the local manifest(s) prior to a `repo sync` |
| 245 | invocation, instructing repo to automatically download and manage | 280 | invocation, instructing repo to automatically download and manage |
| 246 | these extra projects. | 281 | these extra projects. |
| 282 | |||
| 283 | Manifest files stored in `$TOP_DIR/.repo/local_manifests/*.xml` will | ||
| 284 | be loaded in alphabetical order. | ||
| 285 | |||
| 286 | Additional remotes and projects may also be added through a local | ||
| 287 | manifest, stored in `$TOP_DIR/.repo/local_manifest.xml`. This method | ||
| 288 | is deprecated in favor of using multiple manifest files as mentioned | ||
| 289 | above. | ||
| 290 | |||
| 291 | If `$TOP_DIR/.repo/local_manifest.xml` exists, it will be loaded before | ||
| 292 | any manifest files stored in `$TOP_DIR/.repo/local_manifests/*.xml`. | ||
| @@ -13,6 +13,7 @@ | |||
| 13 | # See the License for the specific language governing permissions and | 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. | 14 | # limitations under the License. |
| 15 | 15 | ||
| 16 | from __future__ import print_function | ||
| 16 | import os | 17 | import os |
| 17 | import re | 18 | import re |
| 18 | import sys | 19 | import sys |
| @@ -53,10 +54,10 @@ class Editor(object): | |||
| 53 | return e | 54 | return e |
| 54 | 55 | ||
| 55 | if os.getenv('TERM') == 'dumb': | 56 | if os.getenv('TERM') == 'dumb': |
| 56 | print >>sys.stderr,\ | 57 | print( |
| 57 | """No editor specified in GIT_EDITOR, core.editor, VISUAL or EDITOR. | 58 | """No editor specified in GIT_EDITOR, core.editor, VISUAL or EDITOR. |
| 58 | Tried to fall back to vi but terminal is dumb. Please configure at | 59 | Tried to fall back to vi but terminal is dumb. Please configure at |
| 59 | least one of these before using this command.""" | 60 | least one of these before using this command.""", file=sys.stderr) |
| 60 | sys.exit(1) | 61 | sys.exit(1) |
| 61 | 62 | ||
| 62 | return 'vi' | 63 | return 'vi' |
| @@ -67,7 +68,7 @@ least one of these before using this command.""" | |||
| 67 | 68 | ||
| 68 | Args: | 69 | Args: |
| 69 | data : the text to edit | 70 | data : the text to edit |
| 70 | 71 | ||
| 71 | Returns: | 72 | Returns: |
| 72 | new value of edited text; None if editing did not succeed | 73 | new value of edited text; None if editing did not succeed |
| 73 | """ | 74 | """ |
| @@ -21,6 +21,10 @@ class ManifestInvalidRevisionError(Exception): | |||
| 21 | """The revision value in a project is incorrect. | 21 | """The revision value in a project is incorrect. |
| 22 | """ | 22 | """ |
| 23 | 23 | ||
| 24 | class NoManifestException(Exception): | ||
| 25 | """The required manifest does not exist. | ||
| 26 | """ | ||
| 27 | |||
| 24 | class EditorError(Exception): | 28 | class EditorError(Exception): |
| 25 | """Unspecified error from the user's text editor. | 29 | """Unspecified error from the user's text editor. |
| 26 | """ | 30 | """ |
diff --git a/git_command.py b/git_command.py index a40e6c05..d347dd61 100644 --- a/git_command.py +++ b/git_command.py | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | # See the License for the specific language governing permissions and | 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. | 14 | # limitations under the License. |
| 15 | 15 | ||
| 16 | from __future__ import print_function | ||
| 16 | import os | 17 | import os |
| 17 | import sys | 18 | import sys |
| 18 | import subprocess | 19 | import subprocess |
| @@ -88,11 +89,11 @@ class _GitCall(object): | |||
| 88 | ver_str = git.version() | 89 | ver_str = git.version() |
| 89 | if ver_str.startswith('git version '): | 90 | if ver_str.startswith('git version '): |
| 90 | _git_version = tuple( | 91 | _git_version = tuple( |
| 91 | map(lambda x: int(x), | 92 | map(int, |
| 92 | ver_str[len('git version '):].strip().split('-')[0].split('.')[0:3] | 93 | ver_str[len('git version '):].strip().split('-')[0].split('.')[0:3] |
| 93 | )) | 94 | )) |
| 94 | else: | 95 | else: |
| 95 | print >>sys.stderr, 'fatal: "%s" unsupported' % ver_str | 96 | print('fatal: "%s" unsupported' % ver_str, file=sys.stderr) |
| 96 | sys.exit(1) | 97 | sys.exit(1) |
| 97 | return _git_version | 98 | return _git_version |
| 98 | 99 | ||
| @@ -110,8 +111,8 @@ def git_require(min_version, fail=False): | |||
| 110 | if min_version <= git_version: | 111 | if min_version <= git_version: |
| 111 | return True | 112 | return True |
| 112 | if fail: | 113 | if fail: |
| 113 | need = '.'.join(map(lambda x: str(x), min_version)) | 114 | need = '.'.join(map(str, min_version)) |
| 114 | print >>sys.stderr, 'fatal: git %s or later required' % need | 115 | print('fatal: git %s or later required' % need, file=sys.stderr) |
| 115 | sys.exit(1) | 116 | sys.exit(1) |
| 116 | return False | 117 | return False |
| 117 | 118 | ||
| @@ -132,15 +133,15 @@ class GitCommand(object): | |||
| 132 | gitdir = None): | 133 | gitdir = None): |
| 133 | env = os.environ.copy() | 134 | env = os.environ.copy() |
| 134 | 135 | ||
| 135 | for e in [REPO_TRACE, | 136 | for key in [REPO_TRACE, |
| 136 | GIT_DIR, | 137 | GIT_DIR, |
| 137 | 'GIT_ALTERNATE_OBJECT_DIRECTORIES', | 138 | 'GIT_ALTERNATE_OBJECT_DIRECTORIES', |
| 138 | 'GIT_OBJECT_DIRECTORY', | 139 | 'GIT_OBJECT_DIRECTORY', |
| 139 | 'GIT_WORK_TREE', | 140 | 'GIT_WORK_TREE', |
| 140 | 'GIT_GRAFT_FILE', | 141 | 'GIT_GRAFT_FILE', |
| 141 | 'GIT_INDEX_FILE']: | 142 | 'GIT_INDEX_FILE']: |
| 142 | if e in env: | 143 | if key in env: |
| 143 | del env[e] | 144 | del env[key] |
| 144 | 145 | ||
| 145 | if disable_editor: | 146 | if disable_editor: |
| 146 | _setenv(env, 'GIT_EDITOR', ':') | 147 | _setenv(env, 'GIT_EDITOR', ':') |
diff --git a/git_config.py b/git_config.py index ae288558..56cc6a24 100644 --- a/git_config.py +++ b/git_config.py | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | # See the License for the specific language governing permissions and | 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. | 14 | # limitations under the License. |
| 15 | 15 | ||
| 16 | from __future__ import print_function | ||
| 16 | import cPickle | 17 | import cPickle |
| 17 | import os | 18 | import os |
| 18 | import re | 19 | import re |
| @@ -23,7 +24,18 @@ try: | |||
| 23 | except ImportError: | 24 | except ImportError: |
| 24 | import dummy_threading as _threading | 25 | import dummy_threading as _threading |
| 25 | import time | 26 | import time |
| 26 | import urllib2 | 27 | try: |
| 28 | import urllib2 | ||
| 29 | except ImportError: | ||
| 30 | # For python3 | ||
| 31 | import urllib.request | ||
| 32 | import urllib.error | ||
| 33 | else: | ||
| 34 | # For python2 | ||
| 35 | import imp | ||
| 36 | urllib = imp.new_module('urllib') | ||
| 37 | urllib.request = urllib2 | ||
| 38 | urllib.error = urllib2 | ||
| 27 | 39 | ||
| 28 | from signal import SIGTERM | 40 | from signal import SIGTERM |
| 29 | from error import GitError, UploadError | 41 | from error import GitError, UploadError |
| @@ -35,7 +47,7 @@ from git_command import terminate_ssh_clients | |||
| 35 | 47 | ||
| 36 | R_HEADS = 'refs/heads/' | 48 | R_HEADS = 'refs/heads/' |
| 37 | R_TAGS = 'refs/tags/' | 49 | R_TAGS = 'refs/tags/' |
| 38 | ID_RE = re.compile('^[0-9a-f]{40}$') | 50 | ID_RE = re.compile(r'^[0-9a-f]{40}$') |
| 39 | 51 | ||
| 40 | REVIEW_CACHE = dict() | 52 | REVIEW_CACHE = dict() |
| 41 | 53 | ||
| @@ -157,7 +169,7 @@ class GitConfig(object): | |||
| 157 | elif old != value: | 169 | elif old != value: |
| 158 | self._cache[key] = list(value) | 170 | self._cache[key] = list(value) |
| 159 | self._do('--replace-all', name, value[0]) | 171 | self._do('--replace-all', name, value[0]) |
| 160 | for i in xrange(1, len(value)): | 172 | for i in range(1, len(value)): |
| 161 | self._do('--add', name, value[i]) | 173 | self._do('--add', name, value[i]) |
| 162 | 174 | ||
| 163 | elif len(old) != 1 or old[0] != value: | 175 | elif len(old) != 1 or old[0] != value: |
| @@ -288,12 +300,13 @@ class GitConfig(object): | |||
| 288 | d = self._do('--null', '--list') | 300 | d = self._do('--null', '--list') |
| 289 | if d is None: | 301 | if d is None: |
| 290 | return c | 302 | return c |
| 291 | for line in d.rstrip('\0').split('\0'): | 303 | for line in d.rstrip('\0').split('\0'): # pylint: disable=W1401 |
| 304 | # Backslash is not anomalous | ||
| 292 | if '\n' in line: | 305 | if '\n' in line: |
| 293 | key, val = line.split('\n', 1) | 306 | key, val = line.split('\n', 1) |
| 294 | else: | 307 | else: |
| 295 | key = line | 308 | key = line |
| 296 | val = None | 309 | val = None |
| 297 | 310 | ||
| 298 | if key in c: | 311 | if key in c: |
| 299 | c[key].append(val) | 312 | c[key].append(val) |
| @@ -418,7 +431,7 @@ def _open_ssh(host, port=None): | |||
| 418 | '-o','ControlPath %s' % ssh_sock(), | 431 | '-o','ControlPath %s' % ssh_sock(), |
| 419 | host] | 432 | host] |
| 420 | if port is not None: | 433 | if port is not None: |
| 421 | command_base[1:1] = ['-p',str(port)] | 434 | command_base[1:1] = ['-p', str(port)] |
| 422 | 435 | ||
| 423 | # Since the key wasn't in _master_keys, we think that master isn't running. | 436 | # Since the key wasn't in _master_keys, we think that master isn't running. |
| 424 | # ...but before actually starting a master, we'll double-check. This can | 437 | # ...but before actually starting a master, we'll double-check. This can |
| @@ -451,9 +464,8 @@ def _open_ssh(host, port=None): | |||
| 451 | p = subprocess.Popen(command) | 464 | p = subprocess.Popen(command) |
| 452 | except Exception as e: | 465 | except Exception as e: |
| 453 | _ssh_master = False | 466 | _ssh_master = False |
| 454 | print >>sys.stderr, \ | 467 | print('\nwarn: cannot enable ssh control master for %s:%s\n%s' |
| 455 | '\nwarn: cannot enable ssh control master for %s:%s\n%s' \ | 468 | % (host,port, str(e)), file=sys.stderr) |
| 456 | % (host,port, str(e)) | ||
| 457 | return False | 469 | return False |
| 458 | 470 | ||
| 459 | _master_processes.append(p) | 471 | _master_processes.append(p) |
| @@ -525,7 +537,7 @@ class Remote(object): | |||
| 525 | self.url = self._Get('url') | 537 | self.url = self._Get('url') |
| 526 | self.review = self._Get('review') | 538 | self.review = self._Get('review') |
| 527 | self.projectname = self._Get('projectname') | 539 | self.projectname = self._Get('projectname') |
| 528 | self.fetch = map(lambda x: RefSpec.FromString(x), | 540 | self.fetch = map(RefSpec.FromString, |
| 529 | self._Get('fetch', all_keys=True)) | 541 | self._Get('fetch', all_keys=True)) |
| 530 | self._review_url = None | 542 | self._review_url = None |
| 531 | 543 | ||
| @@ -579,7 +591,7 @@ class Remote(object): | |||
| 579 | else: | 591 | else: |
| 580 | try: | 592 | try: |
| 581 | info_url = u + 'ssh_info' | 593 | info_url = u + 'ssh_info' |
| 582 | info = urllib2.urlopen(info_url).read() | 594 | info = urllib.request.urlopen(info_url).read() |
| 583 | if '<' in info: | 595 | if '<' in info: |
| 584 | # Assume the server gave us some sort of HTML | 596 | # Assume the server gave us some sort of HTML |
| 585 | # response back, like maybe a login page. | 597 | # response back, like maybe a login page. |
| @@ -592,9 +604,9 @@ class Remote(object): | |||
| 592 | else: | 604 | else: |
| 593 | host, port = info.split() | 605 | host, port = info.split() |
| 594 | self._review_url = self._SshReviewUrl(userEmail, host, port) | 606 | self._review_url = self._SshReviewUrl(userEmail, host, port) |
| 595 | except urllib2.HTTPError as e: | 607 | except urllib.error.HTTPError as e: |
| 596 | raise UploadError('%s: %s' % (self.review, str(e))) | 608 | raise UploadError('%s: %s' % (self.review, str(e))) |
| 597 | except urllib2.URLError as e: | 609 | except urllib.error.URLError as e: |
| 598 | raise UploadError('%s: %s' % (self.review, str(e))) | 610 | raise UploadError('%s: %s' % (self.review, str(e))) |
| 599 | 611 | ||
| 600 | REVIEW_CACHE[u] = self._review_url | 612 | REVIEW_CACHE[u] = self._review_url |
| @@ -645,7 +657,7 @@ class Remote(object): | |||
| 645 | self._Set('url', self.url) | 657 | self._Set('url', self.url) |
| 646 | self._Set('review', self.review) | 658 | self._Set('review', self.review) |
| 647 | self._Set('projectname', self.projectname) | 659 | self._Set('projectname', self.projectname) |
| 648 | self._Set('fetch', map(lambda x: str(x), self.fetch)) | 660 | self._Set('fetch', map(str, self.fetch)) |
| 649 | 661 | ||
| 650 | def _Set(self, key, value): | 662 | def _Set(self, key, value): |
| 651 | key = 'remote.%s.%s' % (self.name, key) | 663 | key = 'remote.%s.%s' % (self.name, key) |
diff --git a/git_refs.py b/git_refs.py index 18c9230c..cfeffba9 100644 --- a/git_refs.py +++ b/git_refs.py | |||
| @@ -138,14 +138,14 @@ class GitRefs(object): | |||
| 138 | def _ReadLoose1(self, path, name): | 138 | def _ReadLoose1(self, path, name): |
| 139 | try: | 139 | try: |
| 140 | fd = open(path, 'rb') | 140 | fd = open(path, 'rb') |
| 141 | except: | 141 | except IOError: |
| 142 | return | 142 | return |
| 143 | 143 | ||
| 144 | try: | 144 | try: |
| 145 | try: | 145 | try: |
| 146 | mtime = os.path.getmtime(path) | 146 | mtime = os.path.getmtime(path) |
| 147 | ref_id = fd.readline() | 147 | ref_id = fd.readline() |
| 148 | except: | 148 | except (IOError, OSError): |
| 149 | return | 149 | return |
| 150 | finally: | 150 | finally: |
| 151 | fd.close() | 151 | fd.close() |
diff --git a/hooks/commit-msg b/hooks/commit-msg index 172a1781..b37dfaa4 100755 --- a/hooks/commit-msg +++ b/hooks/commit-msg | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | #!/bin/sh | 1 | #!/bin/sh |
| 2 | # From Gerrit Code Review 2.5-rc0 | 2 | # From Gerrit Code Review 2.5.2 |
| 3 | # | 3 | # |
| 4 | # Part of Gerrit Code Review (http://code.google.com/p/gerrit/) | 4 | # Part of Gerrit Code Review (http://code.google.com/p/gerrit/) |
| 5 | # | 5 | # |
| @@ -18,6 +18,8 @@ | |||
| 18 | # limitations under the License. | 18 | # limitations under the License. |
| 19 | # | 19 | # |
| 20 | 20 | ||
| 21 | unset GREP_OPTIONS | ||
| 22 | |||
| 21 | CHANGE_ID_AFTER="Bug|Issue" | 23 | CHANGE_ID_AFTER="Bug|Issue" |
| 22 | MSG="$1" | 24 | MSG="$1" |
| 23 | 25 | ||
| @@ -1,4 +1,4 @@ | |||
| 1 | #!/bin/sh | 1 | #!/usr/bin/env python |
| 2 | # | 2 | # |
| 3 | # Copyright (C) 2008 The Android Open Source Project | 3 | # Copyright (C) 2008 The Android Open Source Project |
| 4 | # | 4 | # |
| @@ -14,23 +14,23 @@ | |||
| 14 | # See the License for the specific language governing permissions and | 14 | # See the License for the specific language governing permissions and |
| 15 | # limitations under the License. | 15 | # limitations under the License. |
| 16 | 16 | ||
| 17 | magic='--calling-python-from-/bin/sh--' | 17 | from __future__ import print_function |
| 18 | """exec" python -E "$0" "$@" """#$magic" | ||
| 19 | if __name__ == '__main__': | ||
| 20 | import sys | ||
| 21 | if sys.argv[-1] == '#%s' % magic: | ||
| 22 | del sys.argv[-1] | ||
| 23 | del magic | ||
| 24 | |||
| 25 | import getpass | 18 | import getpass |
| 26 | import imp | 19 | import imp |
| 27 | import netrc | 20 | import netrc |
| 28 | import optparse | 21 | import optparse |
| 29 | import os | 22 | import os |
| 30 | import re | ||
| 31 | import sys | 23 | import sys |
| 32 | import time | 24 | import time |
| 33 | import urllib2 | 25 | try: |
| 26 | import urllib2 | ||
| 27 | except ImportError: | ||
| 28 | # For python3 | ||
| 29 | import urllib.request | ||
| 30 | else: | ||
| 31 | # For python2 | ||
| 32 | urllib = imp.new_module('urllib') | ||
| 33 | urllib.request = urllib2 | ||
| 34 | 34 | ||
| 35 | from trace import SetTrace | 35 | from trace import SetTrace |
| 36 | from git_command import git, GitCommand | 36 | from git_command import git, GitCommand |
| @@ -41,6 +41,8 @@ from subcmds.version import Version | |||
| 41 | from editor import Editor | 41 | from editor import Editor |
| 42 | from error import DownloadError | 42 | from error import DownloadError |
| 43 | from error import ManifestInvalidRevisionError | 43 | from error import ManifestInvalidRevisionError |
| 44 | from error import ManifestParseError | ||
| 45 | from error import NoManifestException | ||
| 44 | from error import NoSuchProjectError | 46 | from error import NoSuchProjectError |
| 45 | from error import RepoChangedException | 47 | from error import RepoChangedException |
| 46 | from manifest_xml import XmlManifest | 48 | from manifest_xml import XmlManifest |
| @@ -79,7 +81,7 @@ class _Repo(object): | |||
| 79 | name = None | 81 | name = None |
| 80 | glob = [] | 82 | glob = [] |
| 81 | 83 | ||
| 82 | for i in xrange(0, len(argv)): | 84 | for i in range(len(argv)): |
| 83 | if not argv[i].startswith('-'): | 85 | if not argv[i].startswith('-'): |
| 84 | name = argv[i] | 86 | name = argv[i] |
| 85 | if i > 0: | 87 | if i > 0: |
| @@ -98,15 +100,14 @@ class _Repo(object): | |||
| 98 | if name == 'help': | 100 | if name == 'help': |
| 99 | name = 'version' | 101 | name = 'version' |
| 100 | else: | 102 | else: |
| 101 | print >>sys.stderr, 'fatal: invalid usage of --version' | 103 | print('fatal: invalid usage of --version', file=sys.stderr) |
| 102 | return 1 | 104 | return 1 |
| 103 | 105 | ||
| 104 | try: | 106 | try: |
| 105 | cmd = self.commands[name] | 107 | cmd = self.commands[name] |
| 106 | except KeyError: | 108 | except KeyError: |
| 107 | print >>sys.stderr,\ | 109 | print("repo: '%s' is not a repo command. See 'repo help'." % name, |
| 108 | "repo: '%s' is not a repo command. See 'repo help'."\ | 110 | file=sys.stderr) |
| 109 | % name | ||
| 110 | return 1 | 111 | return 1 |
| 111 | 112 | ||
| 112 | cmd.repodir = self.repodir | 113 | cmd.repodir = self.repodir |
| @@ -114,12 +115,12 @@ class _Repo(object): | |||
| 114 | Editor.globalConfig = cmd.manifest.globalConfig | 115 | Editor.globalConfig = cmd.manifest.globalConfig |
| 115 | 116 | ||
| 116 | if not isinstance(cmd, MirrorSafeCommand) and cmd.manifest.IsMirror: | 117 | if not isinstance(cmd, MirrorSafeCommand) and cmd.manifest.IsMirror: |
| 117 | print >>sys.stderr, \ | 118 | print("fatal: '%s' requires a working directory" % name, |
| 118 | "fatal: '%s' requires a working directory"\ | 119 | file=sys.stderr) |
| 119 | % name | ||
| 120 | return 1 | 120 | return 1 |
| 121 | 121 | ||
| 122 | copts, cargs = cmd.OptionParser.parse_args(argv) | 122 | copts, cargs = cmd.OptionParser.parse_args(argv) |
| 123 | copts = cmd.ReadEnvironmentOptions(copts) | ||
| 123 | 124 | ||
| 124 | if not gopts.no_pager and not isinstance(cmd, InteractiveCommand): | 125 | if not gopts.no_pager and not isinstance(cmd, InteractiveCommand): |
| 125 | config = cmd.manifest.globalConfig | 126 | config = cmd.manifest.globalConfig |
| @@ -132,33 +133,35 @@ class _Repo(object): | |||
| 132 | if use_pager: | 133 | if use_pager: |
| 133 | RunPager(config) | 134 | RunPager(config) |
| 134 | 135 | ||
| 136 | start = time.time() | ||
| 135 | try: | 137 | try: |
| 136 | start = time.time() | 138 | result = cmd.Execute(copts, cargs) |
| 137 | try: | ||
| 138 | result = cmd.Execute(copts, cargs) | ||
| 139 | finally: | ||
| 140 | elapsed = time.time() - start | ||
| 141 | hours, remainder = divmod(elapsed, 3600) | ||
| 142 | minutes, seconds = divmod(remainder, 60) | ||
| 143 | if gopts.time: | ||
| 144 | if hours == 0: | ||
| 145 | print >>sys.stderr, 'real\t%dm%.3fs' \ | ||
| 146 | % (minutes, seconds) | ||
| 147 | else: | ||
| 148 | print >>sys.stderr, 'real\t%dh%dm%.3fs' \ | ||
| 149 | % (hours, minutes, seconds) | ||
| 150 | except DownloadError as e: | 139 | except DownloadError as e: |
| 151 | print >>sys.stderr, 'error: %s' % str(e) | 140 | print('error: %s' % str(e), file=sys.stderr) |
| 152 | return 1 | 141 | result = 1 |
| 153 | except ManifestInvalidRevisionError as e: | 142 | except ManifestInvalidRevisionError as e: |
| 154 | print >>sys.stderr, 'error: %s' % str(e) | 143 | print('error: %s' % str(e), file=sys.stderr) |
| 155 | return 1 | 144 | result = 1 |
| 145 | except NoManifestException as e: | ||
| 146 | print('error: manifest required for this command -- please run init', | ||
| 147 | file=sys.stderr) | ||
| 148 | result = 1 | ||
| 156 | except NoSuchProjectError as e: | 149 | except NoSuchProjectError as e: |
| 157 | if e.name: | 150 | if e.name: |
| 158 | print >>sys.stderr, 'error: project %s not found' % e.name | 151 | print('error: project %s not found' % e.name, file=sys.stderr) |
| 159 | else: | 152 | else: |
| 160 | print >>sys.stderr, 'error: no project in current directory' | 153 | print('error: no project in current directory', file=sys.stderr) |
| 161 | return 1 | 154 | result = 1 |
| 155 | finally: | ||
| 156 | elapsed = time.time() - start | ||
| 157 | hours, remainder = divmod(elapsed, 3600) | ||
| 158 | minutes, seconds = divmod(remainder, 60) | ||
| 159 | if gopts.time: | ||
| 160 | if hours == 0: | ||
| 161 | print('real\t%dm%.3fs' % (minutes, seconds), file=sys.stderr) | ||
| 162 | else: | ||
| 163 | print('real\t%dh%dm%.3fs' % (hours, minutes, seconds), | ||
| 164 | file=sys.stderr) | ||
| 162 | 165 | ||
| 163 | return result | 166 | return result |
| 164 | 167 | ||
| @@ -183,36 +186,35 @@ def _CheckWrapperVersion(ver, repo_path): | |||
| 183 | repo_path = '~/bin/repo' | 186 | repo_path = '~/bin/repo' |
| 184 | 187 | ||
| 185 | if not ver: | 188 | if not ver: |
| 186 | print >>sys.stderr, 'no --wrapper-version argument' | 189 | print('no --wrapper-version argument', file=sys.stderr) |
| 187 | sys.exit(1) | 190 | sys.exit(1) |
| 188 | 191 | ||
| 189 | exp = _CurrentWrapperVersion() | 192 | exp = _CurrentWrapperVersion() |
| 190 | ver = tuple(map(lambda x: int(x), ver.split('.'))) | 193 | ver = tuple(map(int, ver.split('.'))) |
| 191 | if len(ver) == 1: | 194 | if len(ver) == 1: |
| 192 | ver = (0, ver[0]) | 195 | ver = (0, ver[0]) |
| 193 | 196 | ||
| 197 | exp_str = '.'.join(map(str, exp)) | ||
| 194 | if exp[0] > ver[0] or ver < (0, 4): | 198 | if exp[0] > ver[0] or ver < (0, 4): |
| 195 | exp_str = '.'.join(map(lambda x: str(x), exp)) | 199 | print(""" |
| 196 | print >>sys.stderr, """ | ||
| 197 | !!! A new repo command (%5s) is available. !!! | 200 | !!! A new repo command (%5s) is available. !!! |
| 198 | !!! You must upgrade before you can continue: !!! | 201 | !!! You must upgrade before you can continue: !!! |
| 199 | 202 | ||
| 200 | cp %s %s | 203 | cp %s %s |
| 201 | """ % (exp_str, _MyWrapperPath(), repo_path) | 204 | """ % (exp_str, _MyWrapperPath(), repo_path), file=sys.stderr) |
| 202 | sys.exit(1) | 205 | sys.exit(1) |
| 203 | 206 | ||
| 204 | if exp > ver: | 207 | if exp > ver: |
| 205 | exp_str = '.'.join(map(lambda x: str(x), exp)) | 208 | print(""" |
| 206 | print >>sys.stderr, """ | ||
| 207 | ... A new repo command (%5s) is available. | 209 | ... A new repo command (%5s) is available. |
| 208 | ... You should upgrade soon: | 210 | ... You should upgrade soon: |
| 209 | 211 | ||
| 210 | cp %s %s | 212 | cp %s %s |
| 211 | """ % (exp_str, _MyWrapperPath(), repo_path) | 213 | """ % (exp_str, _MyWrapperPath(), repo_path), file=sys.stderr) |
| 212 | 214 | ||
| 213 | def _CheckRepoDir(repo_dir): | 215 | def _CheckRepoDir(repo_dir): |
| 214 | if not repo_dir: | 216 | if not repo_dir: |
| 215 | print >>sys.stderr, 'no --repo-dir argument' | 217 | print('no --repo-dir argument', file=sys.stderr) |
| 216 | sys.exit(1) | 218 | sys.exit(1) |
| 217 | 219 | ||
| 218 | def _PruneOptions(argv, opt): | 220 | def _PruneOptions(argv, opt): |
| @@ -264,11 +266,11 @@ def _UserAgent(): | |||
| 264 | _user_agent = 'git-repo/%s (%s) git/%s Python/%d.%d.%d' % ( | 266 | _user_agent = 'git-repo/%s (%s) git/%s Python/%d.%d.%d' % ( |
| 265 | repo_version, | 267 | repo_version, |
| 266 | os_name, | 268 | os_name, |
| 267 | '.'.join(map(lambda d: str(d), git.version_tuple())), | 269 | '.'.join(map(str, git.version_tuple())), |
| 268 | py_version[0], py_version[1], py_version[2]) | 270 | py_version[0], py_version[1], py_version[2]) |
| 269 | return _user_agent | 271 | return _user_agent |
| 270 | 272 | ||
| 271 | class _UserAgentHandler(urllib2.BaseHandler): | 273 | class _UserAgentHandler(urllib.request.BaseHandler): |
| 272 | def http_request(self, req): | 274 | def http_request(self, req): |
| 273 | req.add_header('User-Agent', _UserAgent()) | 275 | req.add_header('User-Agent', _UserAgent()) |
| 274 | return req | 276 | return req |
| @@ -278,22 +280,22 @@ class _UserAgentHandler(urllib2.BaseHandler): | |||
| 278 | return req | 280 | return req |
| 279 | 281 | ||
| 280 | def _AddPasswordFromUserInput(handler, msg, req): | 282 | def _AddPasswordFromUserInput(handler, msg, req): |
| 281 | # If repo could not find auth info from netrc, try to get it from user input | 283 | # If repo could not find auth info from netrc, try to get it from user input |
| 282 | url = req.get_full_url() | 284 | url = req.get_full_url() |
| 283 | user, password = handler.passwd.find_user_password(None, url) | 285 | user, password = handler.passwd.find_user_password(None, url) |
| 284 | if user is None: | 286 | if user is None: |
| 285 | print msg | 287 | print(msg) |
| 286 | try: | 288 | try: |
| 287 | user = raw_input('User: ') | 289 | user = raw_input('User: ') |
| 288 | password = getpass.getpass() | 290 | password = getpass.getpass() |
| 289 | except KeyboardInterrupt: | 291 | except KeyboardInterrupt: |
| 290 | return | 292 | return |
| 291 | handler.passwd.add_password(None, url, user, password) | 293 | handler.passwd.add_password(None, url, user, password) |
| 292 | 294 | ||
| 293 | class _BasicAuthHandler(urllib2.HTTPBasicAuthHandler): | 295 | class _BasicAuthHandler(urllib.request.HTTPBasicAuthHandler): |
| 294 | def http_error_401(self, req, fp, code, msg, headers): | 296 | def http_error_401(self, req, fp, code, msg, headers): |
| 295 | _AddPasswordFromUserInput(self, msg, req) | 297 | _AddPasswordFromUserInput(self, msg, req) |
| 296 | return urllib2.HTTPBasicAuthHandler.http_error_401( | 298 | return urllib.request.HTTPBasicAuthHandler.http_error_401( |
| 297 | self, req, fp, code, msg, headers) | 299 | self, req, fp, code, msg, headers) |
| 298 | 300 | ||
| 299 | def http_error_auth_reqed(self, authreq, host, req, headers): | 301 | def http_error_auth_reqed(self, authreq, host, req, headers): |
| @@ -303,7 +305,7 @@ class _BasicAuthHandler(urllib2.HTTPBasicAuthHandler): | |||
| 303 | val = val.replace('\n', '') | 305 | val = val.replace('\n', '') |
| 304 | old_add_header(name, val) | 306 | old_add_header(name, val) |
| 305 | req.add_header = _add_header | 307 | req.add_header = _add_header |
| 306 | return urllib2.AbstractBasicAuthHandler.http_error_auth_reqed( | 308 | return urllib.request.AbstractBasicAuthHandler.http_error_auth_reqed( |
| 307 | self, authreq, host, req, headers) | 309 | self, authreq, host, req, headers) |
| 308 | except: | 310 | except: |
| 309 | reset = getattr(self, 'reset_retry_count', None) | 311 | reset = getattr(self, 'reset_retry_count', None) |
| @@ -313,10 +315,10 @@ class _BasicAuthHandler(urllib2.HTTPBasicAuthHandler): | |||
| 313 | self.retried = 0 | 315 | self.retried = 0 |
| 314 | raise | 316 | raise |
| 315 | 317 | ||
| 316 | class _DigestAuthHandler(urllib2.HTTPDigestAuthHandler): | 318 | class _DigestAuthHandler(urllib.request.HTTPDigestAuthHandler): |
| 317 | def http_error_401(self, req, fp, code, msg, headers): | 319 | def http_error_401(self, req, fp, code, msg, headers): |
| 318 | _AddPasswordFromUserInput(self, msg, req) | 320 | _AddPasswordFromUserInput(self, msg, req) |
| 319 | return urllib2.HTTPDigestAuthHandler.http_error_401( | 321 | return urllib.request.HTTPDigestAuthHandler.http_error_401( |
| 320 | self, req, fp, code, msg, headers) | 322 | self, req, fp, code, msg, headers) |
| 321 | 323 | ||
| 322 | def http_error_auth_reqed(self, auth_header, host, req, headers): | 324 | def http_error_auth_reqed(self, auth_header, host, req, headers): |
| @@ -326,7 +328,7 @@ class _DigestAuthHandler(urllib2.HTTPDigestAuthHandler): | |||
| 326 | val = val.replace('\n', '') | 328 | val = val.replace('\n', '') |
| 327 | old_add_header(name, val) | 329 | old_add_header(name, val) |
| 328 | req.add_header = _add_header | 330 | req.add_header = _add_header |
| 329 | return urllib2.AbstractDigestAuthHandler.http_error_auth_reqed( | 331 | return urllib.request.AbstractDigestAuthHandler.http_error_auth_reqed( |
| 330 | self, auth_header, host, req, headers) | 332 | self, auth_header, host, req, headers) |
| 331 | except: | 333 | except: |
| 332 | reset = getattr(self, 'reset_retry_count', None) | 334 | reset = getattr(self, 'reset_retry_count', None) |
| @@ -339,7 +341,7 @@ class _DigestAuthHandler(urllib2.HTTPDigestAuthHandler): | |||
| 339 | def init_http(): | 341 | def init_http(): |
| 340 | handlers = [_UserAgentHandler()] | 342 | handlers = [_UserAgentHandler()] |
| 341 | 343 | ||
| 342 | mgr = urllib2.HTTPPasswordMgrWithDefaultRealm() | 344 | mgr = urllib.request.HTTPPasswordMgrWithDefaultRealm() |
| 343 | try: | 345 | try: |
| 344 | n = netrc.netrc() | 346 | n = netrc.netrc() |
| 345 | for host in n.hosts: | 347 | for host in n.hosts: |
| @@ -355,11 +357,11 @@ def init_http(): | |||
| 355 | 357 | ||
| 356 | if 'http_proxy' in os.environ: | 358 | if 'http_proxy' in os.environ: |
| 357 | url = os.environ['http_proxy'] | 359 | url = os.environ['http_proxy'] |
| 358 | handlers.append(urllib2.ProxyHandler({'http': url, 'https': url})) | 360 | handlers.append(urllib.request.ProxyHandler({'http': url, 'https': url})) |
| 359 | if 'REPO_CURL_VERBOSE' in os.environ: | 361 | if 'REPO_CURL_VERBOSE' in os.environ: |
| 360 | handlers.append(urllib2.HTTPHandler(debuglevel=1)) | 362 | handlers.append(urllib.request.HTTPHandler(debuglevel=1)) |
| 361 | handlers.append(urllib2.HTTPSHandler(debuglevel=1)) | 363 | handlers.append(urllib.request.HTTPSHandler(debuglevel=1)) |
| 362 | urllib2.install_opener(urllib2.build_opener(*handlers)) | 364 | urllib.request.install_opener(urllib.request.build_opener(*handlers)) |
| 363 | 365 | ||
| 364 | def _Main(argv): | 366 | def _Main(argv): |
| 365 | result = 0 | 367 | result = 0 |
| @@ -389,6 +391,10 @@ def _Main(argv): | |||
| 389 | finally: | 391 | finally: |
| 390 | close_ssh() | 392 | close_ssh() |
| 391 | except KeyboardInterrupt: | 393 | except KeyboardInterrupt: |
| 394 | print('aborted by user', file=sys.stderr) | ||
| 395 | result = 1 | ||
| 396 | except ManifestParseError as mpe: | ||
| 397 | print('fatal: %s' % mpe, file=sys.stderr) | ||
| 392 | result = 1 | 398 | result = 1 |
| 393 | except RepoChangedException as rce: | 399 | except RepoChangedException as rce: |
| 394 | # If repo changed, re-exec ourselves. | 400 | # If repo changed, re-exec ourselves. |
| @@ -398,8 +404,8 @@ def _Main(argv): | |||
| 398 | try: | 404 | try: |
| 399 | os.execv(__file__, argv) | 405 | os.execv(__file__, argv) |
| 400 | except OSError as e: | 406 | except OSError as e: |
| 401 | print >>sys.stderr, 'fatal: cannot restart repo after upgrade' | 407 | print('fatal: cannot restart repo after upgrade', file=sys.stderr) |
| 402 | print >>sys.stderr, 'fatal: %s' % e | 408 | print('fatal: %s' % e, file=sys.stderr) |
| 403 | result = 128 | 409 | result = 128 |
| 404 | 410 | ||
| 405 | sys.exit(result) | 411 | sys.exit(result) |
diff --git a/manifest_xml.py b/manifest_xml.py index dd163bed..53f33537 100644 --- a/manifest_xml.py +++ b/manifest_xml.py | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | # See the License for the specific language governing permissions and | 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. | 14 | # limitations under the License. |
| 15 | 15 | ||
| 16 | from __future__ import print_function | ||
| 16 | import itertools | 17 | import itertools |
| 17 | import os | 18 | import os |
| 18 | import re | 19 | import re |
| @@ -27,6 +28,7 @@ from error import ManifestParseError | |||
| 27 | 28 | ||
| 28 | MANIFEST_FILE_NAME = 'manifest.xml' | 29 | MANIFEST_FILE_NAME = 'manifest.xml' |
| 29 | LOCAL_MANIFEST_NAME = 'local_manifest.xml' | 30 | LOCAL_MANIFEST_NAME = 'local_manifest.xml' |
| 31 | LOCAL_MANIFESTS_DIR_NAME = 'local_manifests' | ||
| 30 | 32 | ||
| 31 | urlparse.uses_relative.extend(['ssh', 'git']) | 33 | urlparse.uses_relative.extend(['ssh', 'git']) |
| 32 | urlparse.uses_netloc.extend(['ssh', 'git']) | 34 | urlparse.uses_netloc.extend(['ssh', 'git']) |
| @@ -38,6 +40,7 @@ class _Default(object): | |||
| 38 | remote = None | 40 | remote = None |
| 39 | sync_j = 1 | 41 | sync_j = 1 |
| 40 | sync_c = False | 42 | sync_c = False |
| 43 | sync_s = False | ||
| 41 | 44 | ||
| 42 | class _XmlRemote(object): | 45 | class _XmlRemote(object): |
| 43 | def __init__(self, | 46 | def __init__(self, |
| @@ -53,15 +56,28 @@ class _XmlRemote(object): | |||
| 53 | self.reviewUrl = review | 56 | self.reviewUrl = review |
| 54 | self.resolvedFetchUrl = self._resolveFetchUrl() | 57 | self.resolvedFetchUrl = self._resolveFetchUrl() |
| 55 | 58 | ||
| 59 | def __eq__(self, other): | ||
| 60 | return self.__dict__ == other.__dict__ | ||
| 61 | |||
| 62 | def __ne__(self, other): | ||
| 63 | return self.__dict__ != other.__dict__ | ||
| 64 | |||
| 56 | def _resolveFetchUrl(self): | 65 | def _resolveFetchUrl(self): |
| 57 | url = self.fetchUrl.rstrip('/') | 66 | url = self.fetchUrl.rstrip('/') |
| 58 | manifestUrl = self.manifestUrl.rstrip('/') | 67 | manifestUrl = self.manifestUrl.rstrip('/') |
| 68 | p = manifestUrl.startswith('persistent-http') | ||
| 69 | if p: | ||
| 70 | manifestUrl = manifestUrl[len('persistent-'):] | ||
| 71 | |||
| 59 | # urljoin will get confused if there is no scheme in the base url | 72 | # urljoin will get confused if there is no scheme in the base url |
| 60 | # ie, if manifestUrl is of the form <hostname:port> | 73 | # ie, if manifestUrl is of the form <hostname:port> |
| 61 | if manifestUrl.find(':') != manifestUrl.find('/') - 1: | 74 | if manifestUrl.find(':') != manifestUrl.find('/') - 1: |
| 62 | manifestUrl = 'gopher://' + manifestUrl | 75 | manifestUrl = 'gopher://' + manifestUrl |
| 63 | url = urlparse.urljoin(manifestUrl, url) | 76 | url = urlparse.urljoin(manifestUrl, url) |
| 64 | return re.sub(r'^gopher://', '', url) | 77 | url = re.sub(r'^gopher://', '', url) |
| 78 | if p: | ||
| 79 | url = 'persistent-' + url | ||
| 80 | return url | ||
| 65 | 81 | ||
| 66 | def ToRemoteSpec(self, projectName): | 82 | def ToRemoteSpec(self, projectName): |
| 67 | url = self.resolvedFetchUrl.rstrip('/') + '/' + projectName | 83 | url = self.resolvedFetchUrl.rstrip('/') + '/' + projectName |
| @@ -110,11 +126,11 @@ class XmlManifest(object): | |||
| 110 | self.Override(name) | 126 | self.Override(name) |
| 111 | 127 | ||
| 112 | try: | 128 | try: |
| 113 | if os.path.exists(self.manifestFile): | 129 | if os.path.lexists(self.manifestFile): |
| 114 | os.remove(self.manifestFile) | 130 | os.remove(self.manifestFile) |
| 115 | os.symlink('manifests/%s' % name, self.manifestFile) | 131 | os.symlink('manifests/%s' % name, self.manifestFile) |
| 116 | except OSError: | 132 | except OSError as e: |
| 117 | raise ManifestParseError('cannot link manifest %s' % name) | 133 | raise ManifestParseError('cannot link manifest %s: %s' % (name, str(e))) |
| 118 | 134 | ||
| 119 | def _RemoteToXml(self, r, doc, root): | 135 | def _RemoteToXml(self, r, doc, root): |
| 120 | e = doc.createElement('remote') | 136 | e = doc.createElement('remote') |
| @@ -130,9 +146,8 @@ class XmlManifest(object): | |||
| 130 | mp = self.manifestProject | 146 | mp = self.manifestProject |
| 131 | 147 | ||
| 132 | groups = mp.config.GetString('manifest.groups') | 148 | groups = mp.config.GetString('manifest.groups') |
| 133 | if not groups: | 149 | if groups: |
| 134 | groups = 'all' | 150 | groups = [x for x in re.split(r'[,\s]+', groups) if x] |
| 135 | groups = [x for x in re.split(r'[,\s]+', groups) if x] | ||
| 136 | 151 | ||
| 137 | doc = xml.dom.minidom.Document() | 152 | doc = xml.dom.minidom.Document() |
| 138 | root = doc.createElement('manifest') | 153 | root = doc.createElement('manifest') |
| @@ -170,6 +185,9 @@ class XmlManifest(object): | |||
| 170 | if d.sync_c: | 185 | if d.sync_c: |
| 171 | have_default = True | 186 | have_default = True |
| 172 | e.setAttribute('sync-c', 'true') | 187 | e.setAttribute('sync-c', 'true') |
| 188 | if d.sync_s: | ||
| 189 | have_default = True | ||
| 190 | e.setAttribute('sync-s', 'true') | ||
| 173 | if have_default: | 191 | if have_default: |
| 174 | root.appendChild(e) | 192 | root.appendChild(e) |
| 175 | root.appendChild(doc.createTextNode('')) | 193 | root.appendChild(doc.createTextNode('')) |
| @@ -180,20 +198,25 @@ class XmlManifest(object): | |||
| 180 | root.appendChild(e) | 198 | root.appendChild(e) |
| 181 | root.appendChild(doc.createTextNode('')) | 199 | root.appendChild(doc.createTextNode('')) |
| 182 | 200 | ||
| 183 | sort_projects = list(self.projects.keys()) | 201 | def output_projects(parent, parent_node, projects): |
| 184 | sort_projects.sort() | 202 | for p in projects: |
| 185 | 203 | output_project(parent, parent_node, self.projects[p]) | |
| 186 | for p in sort_projects: | ||
| 187 | p = self.projects[p] | ||
| 188 | 204 | ||
| 205 | def output_project(parent, parent_node, p): | ||
| 189 | if not p.MatchesGroups(groups): | 206 | if not p.MatchesGroups(groups): |
| 190 | continue | 207 | return |
| 208 | |||
| 209 | name = p.name | ||
| 210 | relpath = p.relpath | ||
| 211 | if parent: | ||
| 212 | name = self._UnjoinName(parent.name, name) | ||
| 213 | relpath = self._UnjoinRelpath(parent.relpath, relpath) | ||
| 191 | 214 | ||
| 192 | e = doc.createElement('project') | 215 | e = doc.createElement('project') |
| 193 | root.appendChild(e) | 216 | parent_node.appendChild(e) |
| 194 | e.setAttribute('name', p.name) | 217 | e.setAttribute('name', name) |
| 195 | if p.relpath != p.name: | 218 | if relpath != name: |
| 196 | e.setAttribute('path', p.relpath) | 219 | e.setAttribute('path', relpath) |
| 197 | if not d.remote or p.remote.name != d.remote.name: | 220 | if not d.remote or p.remote.name != d.remote.name: |
| 198 | e.setAttribute('remote', p.remote.name) | 221 | e.setAttribute('remote', p.remote.name) |
| 199 | if peg_rev: | 222 | if peg_rev: |
| @@ -231,6 +254,19 @@ class XmlManifest(object): | |||
| 231 | if p.sync_c: | 254 | if p.sync_c: |
| 232 | e.setAttribute('sync-c', 'true') | 255 | e.setAttribute('sync-c', 'true') |
| 233 | 256 | ||
| 257 | if p.sync_s: | ||
| 258 | e.setAttribute('sync-s', 'true') | ||
| 259 | |||
| 260 | if p.subprojects: | ||
| 261 | sort_projects = [subp.name for subp in p.subprojects] | ||
| 262 | sort_projects.sort() | ||
| 263 | output_projects(p, e, sort_projects) | ||
| 264 | |||
| 265 | sort_projects = [key for key in self.projects.keys() | ||
| 266 | if not self.projects[key].parent] | ||
| 267 | sort_projects.sort() | ||
| 268 | output_projects(None, root, sort_projects) | ||
| 269 | |||
| 234 | if self._repo_hooks_project: | 270 | if self._repo_hooks_project: |
| 235 | root.appendChild(doc.createTextNode('')) | 271 | root.appendChild(doc.createTextNode('')) |
| 236 | e = doc.createElement('repo-hooks') | 272 | e = doc.createElement('repo-hooks') |
| @@ -299,9 +335,30 @@ class XmlManifest(object): | |||
| 299 | 335 | ||
| 300 | local = os.path.join(self.repodir, LOCAL_MANIFEST_NAME) | 336 | local = os.path.join(self.repodir, LOCAL_MANIFEST_NAME) |
| 301 | if os.path.exists(local): | 337 | if os.path.exists(local): |
| 338 | print('warning: %s is deprecated; put local manifests in %s instead' | ||
| 339 | % (LOCAL_MANIFEST_NAME, LOCAL_MANIFESTS_DIR_NAME), | ||
| 340 | file=sys.stderr) | ||
| 302 | nodes.append(self._ParseManifestXml(local, self.repodir)) | 341 | nodes.append(self._ParseManifestXml(local, self.repodir)) |
| 303 | 342 | ||
| 304 | self._ParseManifest(nodes) | 343 | local_dir = os.path.abspath(os.path.join(self.repodir, LOCAL_MANIFESTS_DIR_NAME)) |
| 344 | try: | ||
| 345 | for local_file in sorted(os.listdir(local_dir)): | ||
| 346 | if local_file.endswith('.xml'): | ||
| 347 | try: | ||
| 348 | local = os.path.join(local_dir, local_file) | ||
| 349 | nodes.append(self._ParseManifestXml(local, self.repodir)) | ||
| 350 | except ManifestParseError as e: | ||
| 351 | print('%s' % str(e), file=sys.stderr) | ||
| 352 | except OSError: | ||
| 353 | pass | ||
| 354 | |||
| 355 | try: | ||
| 356 | self._ParseManifest(nodes) | ||
| 357 | except ManifestParseError as e: | ||
| 358 | # There was a problem parsing, unload ourselves in case they catch | ||
| 359 | # this error and try again later, we will show the correct error | ||
| 360 | self._Unload() | ||
| 361 | raise e | ||
| 305 | 362 | ||
| 306 | if self.IsMirror: | 363 | if self.IsMirror: |
| 307 | self._AddMetaProjectMirror(self.repoProject) | 364 | self._AddMetaProjectMirror(self.repoProject) |
| @@ -310,7 +367,11 @@ class XmlManifest(object): | |||
| 310 | self._loaded = True | 367 | self._loaded = True |
| 311 | 368 | ||
| 312 | def _ParseManifestXml(self, path, include_root): | 369 | def _ParseManifestXml(self, path, include_root): |
| 313 | root = xml.dom.minidom.parse(path) | 370 | try: |
| 371 | root = xml.dom.minidom.parse(path) | ||
| 372 | except (OSError, xml.parsers.expat.ExpatError) as e: | ||
| 373 | raise ManifestParseError("error parsing manifest %s: %s" % (path, e)) | ||
| 374 | |||
| 314 | if not root or not root.childNodes: | 375 | if not root or not root.childNodes: |
| 315 | raise ManifestParseError("no root node in %s" % (path,)) | 376 | raise ManifestParseError("no root node in %s" % (path,)) |
| 316 | 377 | ||
| @@ -323,35 +384,38 @@ class XmlManifest(object): | |||
| 323 | nodes = [] | 384 | nodes = [] |
| 324 | for node in manifest.childNodes: # pylint:disable=W0631 | 385 | for node in manifest.childNodes: # pylint:disable=W0631 |
| 325 | # We only get here if manifest is initialised | 386 | # We only get here if manifest is initialised |
| 326 | if node.nodeName == 'include': | 387 | if node.nodeName == 'include': |
| 327 | name = self._reqatt(node, 'name') | 388 | name = self._reqatt(node, 'name') |
| 328 | fp = os.path.join(include_root, name) | 389 | fp = os.path.join(include_root, name) |
| 329 | if not os.path.isfile(fp): | 390 | if not os.path.isfile(fp): |
| 330 | raise ManifestParseError, \ | 391 | raise ManifestParseError, \ |
| 331 | "include %s doesn't exist or isn't a file" % \ | 392 | "include %s doesn't exist or isn't a file" % \ |
| 332 | (name,) | 393 | (name,) |
| 333 | try: | 394 | try: |
| 334 | nodes.extend(self._ParseManifestXml(fp, include_root)) | 395 | nodes.extend(self._ParseManifestXml(fp, include_root)) |
| 335 | # should isolate this to the exact exception, but that's | 396 | # should isolate this to the exact exception, but that's |
| 336 | # tricky. actual parsing implementation may vary. | 397 | # tricky. actual parsing implementation may vary. |
| 337 | except (KeyboardInterrupt, RuntimeError, SystemExit): | 398 | except (KeyboardInterrupt, RuntimeError, SystemExit): |
| 338 | raise | 399 | raise |
| 339 | except Exception as e: | 400 | except Exception as e: |
| 340 | raise ManifestParseError( | 401 | raise ManifestParseError( |
| 341 | "failed parsing included manifest %s: %s", (name, e)) | 402 | "failed parsing included manifest %s: %s", (name, e)) |
| 342 | else: | 403 | else: |
| 343 | nodes.append(node) | 404 | nodes.append(node) |
| 344 | return nodes | 405 | return nodes |
| 345 | 406 | ||
| 346 | def _ParseManifest(self, node_list): | 407 | def _ParseManifest(self, node_list): |
| 347 | for node in itertools.chain(*node_list): | 408 | for node in itertools.chain(*node_list): |
| 348 | if node.nodeName == 'remote': | 409 | if node.nodeName == 'remote': |
| 349 | remote = self._ParseRemote(node) | 410 | remote = self._ParseRemote(node) |
| 350 | if self._remotes.get(remote.name): | 411 | if remote: |
| 351 | raise ManifestParseError( | 412 | if remote.name in self._remotes: |
| 352 | 'duplicate remote %s in %s' % | 413 | if remote != self._remotes[remote.name]: |
| 353 | (remote.name, self.manifestFile)) | 414 | raise ManifestParseError( |
| 354 | self._remotes[remote.name] = remote | 415 | 'remote %s already exists with different attributes' % |
| 416 | (remote.name)) | ||
| 417 | else: | ||
| 418 | self._remotes[remote.name] = remote | ||
| 355 | 419 | ||
| 356 | for node in itertools.chain(*node_list): | 420 | for node in itertools.chain(*node_list): |
| 357 | if node.nodeName == 'default': | 421 | if node.nodeName == 'default': |
| @@ -375,19 +439,24 @@ class XmlManifest(object): | |||
| 375 | if node.nodeName == 'manifest-server': | 439 | if node.nodeName == 'manifest-server': |
| 376 | url = self._reqatt(node, 'url') | 440 | url = self._reqatt(node, 'url') |
| 377 | if self._manifest_server is not None: | 441 | if self._manifest_server is not None: |
| 378 | raise ManifestParseError( | 442 | raise ManifestParseError( |
| 379 | 'duplicate manifest-server in %s' % | 443 | 'duplicate manifest-server in %s' % |
| 380 | (self.manifestFile)) | 444 | (self.manifestFile)) |
| 381 | self._manifest_server = url | 445 | self._manifest_server = url |
| 382 | 446 | ||
| 447 | def recursively_add_projects(project): | ||
| 448 | if self._projects.get(project.name): | ||
| 449 | raise ManifestParseError( | ||
| 450 | 'duplicate project %s in %s' % | ||
| 451 | (project.name, self.manifestFile)) | ||
| 452 | self._projects[project.name] = project | ||
| 453 | for subproject in project.subprojects: | ||
| 454 | recursively_add_projects(subproject) | ||
| 455 | |||
| 383 | for node in itertools.chain(*node_list): | 456 | for node in itertools.chain(*node_list): |
| 384 | if node.nodeName == 'project': | 457 | if node.nodeName == 'project': |
| 385 | project = self._ParseProject(node) | 458 | project = self._ParseProject(node) |
| 386 | if self._projects.get(project.name): | 459 | recursively_add_projects(project) |
| 387 | raise ManifestParseError( | ||
| 388 | 'duplicate project %s in %s' % | ||
| 389 | (project.name, self.manifestFile)) | ||
| 390 | self._projects[project.name] = project | ||
| 391 | if node.nodeName == 'repo-hooks': | 460 | if node.nodeName == 'repo-hooks': |
| 392 | # Get the name of the project and the (space-separated) list of enabled. | 461 | # Get the name of the project and the (space-separated) list of enabled. |
| 393 | repo_hooks_project = self._reqatt(node, 'in-project') | 462 | repo_hooks_project = self._reqatt(node, 'in-project') |
| @@ -414,9 +483,8 @@ class XmlManifest(object): | |||
| 414 | try: | 483 | try: |
| 415 | del self._projects[name] | 484 | del self._projects[name] |
| 416 | except KeyError: | 485 | except KeyError: |
| 417 | raise ManifestParseError( | 486 | raise ManifestParseError('remove-project element specifies non-existent ' |
| 418 | 'project %s not found' % | 487 | 'project: %s' % name) |
| 419 | (name)) | ||
| 420 | 488 | ||
| 421 | # If the manifest removes the hooks project, treat it as if it deleted | 489 | # If the manifest removes the hooks project, treat it as if it deleted |
| 422 | # the repo-hooks element too. | 490 | # the repo-hooks element too. |
| @@ -496,6 +564,12 @@ class XmlManifest(object): | |||
| 496 | d.sync_c = False | 564 | d.sync_c = False |
| 497 | else: | 565 | else: |
| 498 | d.sync_c = sync_c.lower() in ("yes", "true", "1") | 566 | d.sync_c = sync_c.lower() in ("yes", "true", "1") |
| 567 | |||
| 568 | sync_s = node.getAttribute('sync-s') | ||
| 569 | if not sync_s: | ||
| 570 | d.sync_s = False | ||
| 571 | else: | ||
| 572 | d.sync_s = sync_s.lower() in ("yes", "true", "1") | ||
| 499 | return d | 573 | return d |
| 500 | 574 | ||
| 501 | def _ParseNotice(self, node): | 575 | def _ParseNotice(self, node): |
| @@ -537,11 +611,19 @@ class XmlManifest(object): | |||
| 537 | 611 | ||
| 538 | return '\n'.join(cleanLines) | 612 | return '\n'.join(cleanLines) |
| 539 | 613 | ||
| 540 | def _ParseProject(self, node): | 614 | def _JoinName(self, parent_name, name): |
| 615 | return os.path.join(parent_name, name) | ||
| 616 | |||
| 617 | def _UnjoinName(self, parent_name, name): | ||
| 618 | return os.path.relpath(name, parent_name) | ||
| 619 | |||
| 620 | def _ParseProject(self, node, parent = None): | ||
| 541 | """ | 621 | """ |
| 542 | reads a <project> element from the manifest file | 622 | reads a <project> element from the manifest file |
| 543 | """ | 623 | """ |
| 544 | name = self._reqatt(node, 'name') | 624 | name = self._reqatt(node, 'name') |
| 625 | if parent: | ||
| 626 | name = self._JoinName(parent.name, name) | ||
| 545 | 627 | ||
| 546 | remote = self._get_remote(node) | 628 | remote = self._get_remote(node) |
| 547 | if remote is None: | 629 | if remote is None: |
| @@ -579,44 +661,80 @@ class XmlManifest(object): | |||
| 579 | else: | 661 | else: |
| 580 | sync_c = sync_c.lower() in ("yes", "true", "1") | 662 | sync_c = sync_c.lower() in ("yes", "true", "1") |
| 581 | 663 | ||
| 664 | sync_s = node.getAttribute('sync-s') | ||
| 665 | if not sync_s: | ||
| 666 | sync_s = self._default.sync_s | ||
| 667 | else: | ||
| 668 | sync_s = sync_s.lower() in ("yes", "true", "1") | ||
| 669 | |||
| 582 | upstream = node.getAttribute('upstream') | 670 | upstream = node.getAttribute('upstream') |
| 583 | 671 | ||
| 584 | groups = '' | 672 | groups = '' |
| 585 | if node.hasAttribute('groups'): | 673 | if node.hasAttribute('groups'): |
| 586 | groups = node.getAttribute('groups') | 674 | groups = node.getAttribute('groups') |
| 587 | groups = [x for x in re.split('[,\s]+', groups) if x] | 675 | groups = [x for x in re.split(r'[,\s]+', groups) if x] |
| 588 | |||
| 589 | default_groups = ['all', 'name:%s' % name, 'path:%s' % path] | ||
| 590 | groups.extend(set(default_groups).difference(groups)) | ||
| 591 | 676 | ||
| 592 | if self.IsMirror: | 677 | if parent is None: |
| 593 | worktree = None | 678 | relpath, worktree, gitdir = self.GetProjectPaths(name, path) |
| 594 | gitdir = os.path.join(self.topdir, '%s.git' % name) | ||
| 595 | else: | 679 | else: |
| 596 | worktree = os.path.join(self.topdir, path).replace('\\', '/') | 680 | relpath, worktree, gitdir = self.GetSubprojectPaths(parent, path) |
| 597 | gitdir = os.path.join(self.repodir, 'projects/%s.git' % path) | 681 | |
| 682 | default_groups = ['all', 'name:%s' % name, 'path:%s' % relpath] | ||
| 683 | groups.extend(set(default_groups).difference(groups)) | ||
| 598 | 684 | ||
| 599 | project = Project(manifest = self, | 685 | project = Project(manifest = self, |
| 600 | name = name, | 686 | name = name, |
| 601 | remote = remote.ToRemoteSpec(name), | 687 | remote = remote.ToRemoteSpec(name), |
| 602 | gitdir = gitdir, | 688 | gitdir = gitdir, |
| 603 | worktree = worktree, | 689 | worktree = worktree, |
| 604 | relpath = path, | 690 | relpath = relpath, |
| 605 | revisionExpr = revisionExpr, | 691 | revisionExpr = revisionExpr, |
| 606 | revisionId = None, | 692 | revisionId = None, |
| 607 | rebase = rebase, | 693 | rebase = rebase, |
| 608 | groups = groups, | 694 | groups = groups, |
| 609 | sync_c = sync_c, | 695 | sync_c = sync_c, |
| 610 | upstream = upstream) | 696 | sync_s = sync_s, |
| 697 | upstream = upstream, | ||
| 698 | parent = parent) | ||
| 611 | 699 | ||
| 612 | for n in node.childNodes: | 700 | for n in node.childNodes: |
| 613 | if n.nodeName == 'copyfile': | 701 | if n.nodeName == 'copyfile': |
| 614 | self._ParseCopyFile(project, n) | 702 | self._ParseCopyFile(project, n) |
| 615 | if n.nodeName == 'annotation': | 703 | if n.nodeName == 'annotation': |
| 616 | self._ParseAnnotation(project, n) | 704 | self._ParseAnnotation(project, n) |
| 705 | if n.nodeName == 'project': | ||
| 706 | project.subprojects.append(self._ParseProject(n, parent = project)) | ||
| 617 | 707 | ||
| 618 | return project | 708 | return project |
| 619 | 709 | ||
| 710 | def GetProjectPaths(self, name, path): | ||
| 711 | relpath = path | ||
| 712 | if self.IsMirror: | ||
| 713 | worktree = None | ||
| 714 | gitdir = os.path.join(self.topdir, '%s.git' % name) | ||
| 715 | else: | ||
| 716 | worktree = os.path.join(self.topdir, path).replace('\\', '/') | ||
| 717 | gitdir = os.path.join(self.repodir, 'projects', '%s.git' % path) | ||
| 718 | return relpath, worktree, gitdir | ||
| 719 | |||
| 720 | def GetSubprojectName(self, parent, submodule_path): | ||
| 721 | return os.path.join(parent.name, submodule_path) | ||
| 722 | |||
| 723 | def _JoinRelpath(self, parent_relpath, relpath): | ||
| 724 | return os.path.join(parent_relpath, relpath) | ||
| 725 | |||
| 726 | def _UnjoinRelpath(self, parent_relpath, relpath): | ||
| 727 | return os.path.relpath(relpath, parent_relpath) | ||
| 728 | |||
| 729 | def GetSubprojectPaths(self, parent, path): | ||
| 730 | relpath = self._JoinRelpath(parent.relpath, path) | ||
| 731 | gitdir = os.path.join(parent.gitdir, 'subprojects', '%s.git' % path) | ||
| 732 | if self.IsMirror: | ||
| 733 | worktree = None | ||
| 734 | else: | ||
| 735 | worktree = os.path.join(parent.worktree, path).replace('\\', '/') | ||
| 736 | return relpath, worktree, gitdir | ||
| 737 | |||
| 620 | def _ParseCopyFile(self, project, node): | 738 | def _ParseCopyFile(self, project, node): |
| 621 | src = self._reqatt(node, 'src') | 739 | src = self._reqatt(node, 'src') |
| 622 | dest = self._reqatt(node, 'dest') | 740 | dest = self._reqatt(node, 'dest') |
| @@ -13,6 +13,7 @@ | |||
| 13 | # See the License for the specific language governing permissions and | 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. | 14 | # limitations under the License. |
| 15 | 15 | ||
| 16 | from __future__ import print_function | ||
| 16 | import os | 17 | import os |
| 17 | import select | 18 | import select |
| 18 | import sys | 19 | import sys |
| @@ -49,7 +50,7 @@ def RunPager(globalConfig): | |||
| 49 | 50 | ||
| 50 | _BecomePager(pager) | 51 | _BecomePager(pager) |
| 51 | except Exception: | 52 | except Exception: |
| 52 | print >>sys.stderr, "fatal: cannot start pager '%s'" % pager | 53 | print("fatal: cannot start pager '%s'" % pager, file=sys.stderr) |
| 53 | sys.exit(255) | 54 | sys.exit(255) |
| 54 | 55 | ||
| 55 | def _SelectPager(globalConfig): | 56 | def _SelectPager(globalConfig): |
| @@ -12,6 +12,7 @@ | |||
| 12 | # See the License for the specific language governing permissions and | 12 | # See the License for the specific language governing permissions and |
| 13 | # limitations under the License. | 13 | # limitations under the License. |
| 14 | 14 | ||
| 15 | from __future__ import print_function | ||
| 15 | import traceback | 16 | import traceback |
| 16 | import errno | 17 | import errno |
| 17 | import filecmp | 18 | import filecmp |
| @@ -22,13 +23,15 @@ import shutil | |||
| 22 | import stat | 23 | import stat |
| 23 | import subprocess | 24 | import subprocess |
| 24 | import sys | 25 | import sys |
| 26 | import tempfile | ||
| 25 | import time | 27 | import time |
| 26 | 28 | ||
| 27 | from color import Coloring | 29 | from color import Coloring |
| 28 | from git_command import GitCommand | 30 | from git_command import GitCommand, git_require |
| 29 | from git_config import GitConfig, IsId, GetSchemeFromUrl, ID_RE | 31 | from git_config import GitConfig, IsId, GetSchemeFromUrl, ID_RE |
| 30 | from error import GitError, HookError, UploadError | 32 | from error import GitError, HookError, UploadError |
| 31 | from error import ManifestInvalidRevisionError | 33 | from error import ManifestInvalidRevisionError |
| 34 | from error import NoManifestException | ||
| 32 | from trace import IsTrace, Trace | 35 | from trace import IsTrace, Trace |
| 33 | 36 | ||
| 34 | from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M | 37 | from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M |
| @@ -50,7 +53,7 @@ def _lwrite(path, content): | |||
| 50 | 53 | ||
| 51 | def _error(fmt, *args): | 54 | def _error(fmt, *args): |
| 52 | msg = fmt % args | 55 | msg = fmt % args |
| 53 | print >>sys.stderr, 'error: %s' % msg | 56 | print('error: %s' % msg, file=sys.stderr) |
| 54 | 57 | ||
| 55 | def not_rev(r): | 58 | def not_rev(r): |
| 56 | return '^' + r | 59 | return '^' + r |
| @@ -359,7 +362,7 @@ class RepoHook(object): | |||
| 359 | '(yes/yes-never-ask-again/NO)? ') % ( | 362 | '(yes/yes-never-ask-again/NO)? ') % ( |
| 360 | self._GetMustVerb(), self._script_fullpath) | 363 | self._GetMustVerb(), self._script_fullpath) |
| 361 | response = raw_input(prompt).lower() | 364 | response = raw_input(prompt).lower() |
| 362 | 365 | print() | |
| 363 | 366 | ||
| 364 | # User is doing a one-time approval. | 367 | # User is doing a one-time approval. |
| 365 | if response in ('y', 'yes'): | 368 | if response in ('y', 'yes'): |
| @@ -484,7 +487,30 @@ class Project(object): | |||
| 484 | rebase = True, | 487 | rebase = True, |
| 485 | groups = None, | 488 | groups = None, |
| 486 | sync_c = False, | 489 | sync_c = False, |
| 487 | upstream = None): | 490 | sync_s = False, |
| 491 | upstream = None, | ||
| 492 | parent = None, | ||
| 493 | is_derived = False): | ||
| 494 | """Init a Project object. | ||
| 495 | |||
| 496 | Args: | ||
| 497 | manifest: The XmlManifest object. | ||
| 498 | name: The `name` attribute of manifest.xml's project element. | ||
| 499 | remote: RemoteSpec object specifying its remote's properties. | ||
| 500 | gitdir: Absolute path of git directory. | ||
| 501 | worktree: Absolute path of git working tree. | ||
| 502 | relpath: Relative path of git working tree to repo's top directory. | ||
| 503 | revisionExpr: The `revision` attribute of manifest.xml's project element. | ||
| 504 | revisionId: git commit id for checking out. | ||
| 505 | rebase: The `rebase` attribute of manifest.xml's project element. | ||
| 506 | groups: The `groups` attribute of manifest.xml's project element. | ||
| 507 | sync_c: The `sync-c` attribute of manifest.xml's project element. | ||
| 508 | sync_s: The `sync-s` attribute of manifest.xml's project element. | ||
| 509 | upstream: The `upstream` attribute of manifest.xml's project element. | ||
| 510 | parent: The parent Project object. | ||
| 511 | is_derived: False if the project was explicitly defined in the manifest; | ||
| 512 | True if the project is a discovered submodule. | ||
| 513 | """ | ||
| 488 | self.manifest = manifest | 514 | self.manifest = manifest |
| 489 | self.name = name | 515 | self.name = name |
| 490 | self.remote = remote | 516 | self.remote = remote |
| @@ -506,7 +532,11 @@ class Project(object): | |||
| 506 | self.rebase = rebase | 532 | self.rebase = rebase |
| 507 | self.groups = groups | 533 | self.groups = groups |
| 508 | self.sync_c = sync_c | 534 | self.sync_c = sync_c |
| 535 | self.sync_s = sync_s | ||
| 509 | self.upstream = upstream | 536 | self.upstream = upstream |
| 537 | self.parent = parent | ||
| 538 | self.is_derived = is_derived | ||
| 539 | self.subprojects = [] | ||
| 510 | 540 | ||
| 511 | self.snapshots = {} | 541 | self.snapshots = {} |
| 512 | self.copyfiles = [] | 542 | self.copyfiles = [] |
| @@ -527,6 +557,10 @@ class Project(object): | |||
| 527 | self.enabled_repo_hooks = [] | 557 | self.enabled_repo_hooks = [] |
| 528 | 558 | ||
| 529 | @property | 559 | @property |
| 560 | def Derived(self): | ||
| 561 | return self.is_derived | ||
| 562 | |||
| 563 | @property | ||
| 530 | def Exists(self): | 564 | def Exists(self): |
| 531 | return os.path.isdir(self.gitdir) | 565 | return os.path.isdir(self.gitdir) |
| 532 | 566 | ||
| @@ -555,7 +589,7 @@ class Project(object): | |||
| 555 | '--unmerged', | 589 | '--unmerged', |
| 556 | '--ignore-missing', | 590 | '--ignore-missing', |
| 557 | '--refresh') | 591 | '--refresh') |
| 558 | if self.work_git.DiffZ('diff-index','-M','--cached',HEAD): | 592 | if self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD): |
| 559 | return True | 593 | return True |
| 560 | if self.work_git.DiffZ('diff-files'): | 594 | if self.work_git.DiffZ('diff-files'): |
| 561 | return True | 595 | return True |
| @@ -584,14 +618,14 @@ class Project(object): | |||
| 584 | return self._userident_email | 618 | return self._userident_email |
| 585 | 619 | ||
| 586 | def _LoadUserIdentity(self): | 620 | def _LoadUserIdentity(self): |
| 587 | u = self.bare_git.var('GIT_COMMITTER_IDENT') | 621 | u = self.bare_git.var('GIT_COMMITTER_IDENT') |
| 588 | m = re.compile("^(.*) <([^>]*)> ").match(u) | 622 | m = re.compile("^(.*) <([^>]*)> ").match(u) |
| 589 | if m: | 623 | if m: |
| 590 | self._userident_name = m.group(1) | 624 | self._userident_name = m.group(1) |
| 591 | self._userident_email = m.group(2) | 625 | self._userident_email = m.group(2) |
| 592 | else: | 626 | else: |
| 593 | self._userident_name = '' | 627 | self._userident_name = '' |
| 594 | self._userident_email = '' | 628 | self._userident_email = '' |
| 595 | 629 | ||
| 596 | def GetRemote(self, name): | 630 | def GetRemote(self, name): |
| 597 | """Get the configuration for a single remote. | 631 | """Get the configuration for a single remote. |
| @@ -683,9 +717,9 @@ class Project(object): | |||
| 683 | if not os.path.isdir(self.worktree): | 717 | if not os.path.isdir(self.worktree): |
| 684 | if output_redir == None: | 718 | if output_redir == None: |
| 685 | output_redir = sys.stdout | 719 | output_redir = sys.stdout |
| 686 | print >>output_redir, '' | 720 | print(file=output_redir) |
| 687 | print >>output_redir, 'project %s/' % self.relpath | 721 | print('project %s/' % self.relpath, file=output_redir) |
| 688 | print >>output_redir, ' missing (run "repo sync")' | 722 | print(' missing (run "repo sync")', file=output_redir) |
| 689 | return | 723 | return |
| 690 | 724 | ||
| 691 | self.work_git.update_index('-q', | 725 | self.work_git.update_index('-q', |
| @@ -785,7 +819,7 @@ class Project(object): | |||
| 785 | out.project('project %s/' % self.relpath) | 819 | out.project('project %s/' % self.relpath) |
| 786 | out.nl() | 820 | out.nl() |
| 787 | has_diff = True | 821 | has_diff = True |
| 788 | print line[:-1] | 822 | print(line[:-1]) |
| 789 | p.Wait() | 823 | p.Wait() |
| 790 | 824 | ||
| 791 | 825 | ||
| @@ -1012,6 +1046,10 @@ class Project(object): | |||
| 1012 | self.CleanPublishedCache(all_refs) | 1046 | self.CleanPublishedCache(all_refs) |
| 1013 | revid = self.GetRevisionId(all_refs) | 1047 | revid = self.GetRevisionId(all_refs) |
| 1014 | 1048 | ||
| 1049 | def _doff(): | ||
| 1050 | self._FastForward(revid) | ||
| 1051 | self._CopyFiles() | ||
| 1052 | |||
| 1015 | self._InitWorkTree() | 1053 | self._InitWorkTree() |
| 1016 | head = self.work_git.GetHead() | 1054 | head = self.work_git.GetHead() |
| 1017 | if head.startswith(R_HEADS): | 1055 | if head.startswith(R_HEADS): |
| @@ -1090,9 +1128,6 @@ class Project(object): | |||
| 1090 | # All published commits are merged, and thus we are a | 1128 | # All published commits are merged, and thus we are a |
| 1091 | # strict subset. We can fast-forward safely. | 1129 | # strict subset. We can fast-forward safely. |
| 1092 | # | 1130 | # |
| 1093 | def _doff(): | ||
| 1094 | self._FastForward(revid) | ||
| 1095 | self._CopyFiles() | ||
| 1096 | syncbuf.later1(self, _doff) | 1131 | syncbuf.later1(self, _doff) |
| 1097 | return | 1132 | return |
| 1098 | 1133 | ||
| @@ -1155,9 +1190,6 @@ class Project(object): | |||
| 1155 | syncbuf.fail(self, e) | 1190 | syncbuf.fail(self, e) |
| 1156 | return | 1191 | return |
| 1157 | else: | 1192 | else: |
| 1158 | def _doff(): | ||
| 1159 | self._FastForward(revid) | ||
| 1160 | self._CopyFiles() | ||
| 1161 | syncbuf.later1(self, _doff) | 1193 | syncbuf.later1(self, _doff) |
| 1162 | 1194 | ||
| 1163 | def AddCopyFile(self, src, dest, absdest): | 1195 | def AddCopyFile(self, src, dest, absdest): |
| @@ -1177,7 +1209,7 @@ class Project(object): | |||
| 1177 | cmd = ['fetch', remote.name] | 1209 | cmd = ['fetch', remote.name] |
| 1178 | cmd.append('refs/changes/%2.2d/%d/%d' \ | 1210 | cmd.append('refs/changes/%2.2d/%d/%d' \ |
| 1179 | % (change_id % 100, change_id, patch_id)) | 1211 | % (change_id % 100, change_id, patch_id)) |
| 1180 | cmd.extend(map(lambda x: str(x), remote.fetch)) | 1212 | cmd.extend(map(str, remote.fetch)) |
| 1181 | if GitCommand(self, cmd, bare=True).Wait() != 0: | 1213 | if GitCommand(self, cmd, bare=True).Wait() != 0: |
| 1182 | return None | 1214 | return None |
| 1183 | return DownloadedChange(self, | 1215 | return DownloadedChange(self, |
| @@ -1370,6 +1402,149 @@ class Project(object): | |||
| 1370 | return kept | 1402 | return kept |
| 1371 | 1403 | ||
| 1372 | 1404 | ||
| 1405 | ## Submodule Management ## | ||
| 1406 | |||
| 1407 | def GetRegisteredSubprojects(self): | ||
| 1408 | result = [] | ||
| 1409 | def rec(subprojects): | ||
| 1410 | if not subprojects: | ||
| 1411 | return | ||
| 1412 | result.extend(subprojects) | ||
| 1413 | for p in subprojects: | ||
| 1414 | rec(p.subprojects) | ||
| 1415 | rec(self.subprojects) | ||
| 1416 | return result | ||
| 1417 | |||
| 1418 | def _GetSubmodules(self): | ||
| 1419 | # Unfortunately we cannot call `git submodule status --recursive` here | ||
| 1420 | # because the working tree might not exist yet, and it cannot be used | ||
| 1421 | # without a working tree in its current implementation. | ||
| 1422 | |||
| 1423 | def get_submodules(gitdir, rev): | ||
| 1424 | # Parse .gitmodules for submodule sub_paths and sub_urls | ||
| 1425 | sub_paths, sub_urls = parse_gitmodules(gitdir, rev) | ||
| 1426 | if not sub_paths: | ||
| 1427 | return [] | ||
| 1428 | # Run `git ls-tree` to read SHAs of submodule object, which happen to be | ||
| 1429 | # revision of submodule repository | ||
| 1430 | sub_revs = git_ls_tree(gitdir, rev, sub_paths) | ||
| 1431 | submodules = [] | ||
| 1432 | for sub_path, sub_url in zip(sub_paths, sub_urls): | ||
| 1433 | try: | ||
| 1434 | sub_rev = sub_revs[sub_path] | ||
| 1435 | except KeyError: | ||
| 1436 | # Ignore non-exist submodules | ||
| 1437 | continue | ||
| 1438 | submodules.append((sub_rev, sub_path, sub_url)) | ||
| 1439 | return submodules | ||
| 1440 | |||
| 1441 | re_path = re.compile(r'^submodule\.([^.]+)\.path=(.*)$') | ||
| 1442 | re_url = re.compile(r'^submodule\.([^.]+)\.url=(.*)$') | ||
| 1443 | def parse_gitmodules(gitdir, rev): | ||
| 1444 | cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev] | ||
| 1445 | try: | ||
| 1446 | p = GitCommand(None, cmd, capture_stdout = True, capture_stderr = True, | ||
| 1447 | bare = True, gitdir = gitdir) | ||
| 1448 | except GitError: | ||
| 1449 | return [], [] | ||
| 1450 | if p.Wait() != 0: | ||
| 1451 | return [], [] | ||
| 1452 | |||
| 1453 | gitmodules_lines = [] | ||
| 1454 | fd, temp_gitmodules_path = tempfile.mkstemp() | ||
| 1455 | try: | ||
| 1456 | os.write(fd, p.stdout) | ||
| 1457 | os.close(fd) | ||
| 1458 | cmd = ['config', '--file', temp_gitmodules_path, '--list'] | ||
| 1459 | p = GitCommand(None, cmd, capture_stdout = True, capture_stderr = True, | ||
| 1460 | bare = True, gitdir = gitdir) | ||
| 1461 | if p.Wait() != 0: | ||
| 1462 | return [], [] | ||
| 1463 | gitmodules_lines = p.stdout.split('\n') | ||
| 1464 | except GitError: | ||
| 1465 | return [], [] | ||
| 1466 | finally: | ||
| 1467 | os.remove(temp_gitmodules_path) | ||
| 1468 | |||
| 1469 | names = set() | ||
| 1470 | paths = {} | ||
| 1471 | urls = {} | ||
| 1472 | for line in gitmodules_lines: | ||
| 1473 | if not line: | ||
| 1474 | continue | ||
| 1475 | m = re_path.match(line) | ||
| 1476 | if m: | ||
| 1477 | names.add(m.group(1)) | ||
| 1478 | paths[m.group(1)] = m.group(2) | ||
| 1479 | continue | ||
| 1480 | m = re_url.match(line) | ||
| 1481 | if m: | ||
| 1482 | names.add(m.group(1)) | ||
| 1483 | urls[m.group(1)] = m.group(2) | ||
| 1484 | continue | ||
| 1485 | names = sorted(names) | ||
| 1486 | return ([paths.get(name, '') for name in names], | ||
| 1487 | [urls.get(name, '') for name in names]) | ||
| 1488 | |||
| 1489 | def git_ls_tree(gitdir, rev, paths): | ||
| 1490 | cmd = ['ls-tree', rev, '--'] | ||
| 1491 | cmd.extend(paths) | ||
| 1492 | try: | ||
| 1493 | p = GitCommand(None, cmd, capture_stdout = True, capture_stderr = True, | ||
| 1494 | bare = True, gitdir = gitdir) | ||
| 1495 | except GitError: | ||
| 1496 | return [] | ||
| 1497 | if p.Wait() != 0: | ||
| 1498 | return [] | ||
| 1499 | objects = {} | ||
| 1500 | for line in p.stdout.split('\n'): | ||
| 1501 | if not line.strip(): | ||
| 1502 | continue | ||
| 1503 | object_rev, object_path = line.split()[2:4] | ||
| 1504 | objects[object_path] = object_rev | ||
| 1505 | return objects | ||
| 1506 | |||
| 1507 | try: | ||
| 1508 | rev = self.GetRevisionId() | ||
| 1509 | except GitError: | ||
| 1510 | return [] | ||
| 1511 | return get_submodules(self.gitdir, rev) | ||
| 1512 | |||
| 1513 | def GetDerivedSubprojects(self): | ||
| 1514 | result = [] | ||
| 1515 | if not self.Exists: | ||
| 1516 | # If git repo does not exist yet, querying its submodules will | ||
| 1517 | # mess up its states; so return here. | ||
| 1518 | return result | ||
| 1519 | for rev, path, url in self._GetSubmodules(): | ||
| 1520 | name = self.manifest.GetSubprojectName(self, path) | ||
| 1521 | project = self.manifest.projects.get(name) | ||
| 1522 | if project: | ||
| 1523 | result.extend(project.GetDerivedSubprojects()) | ||
| 1524 | continue | ||
| 1525 | relpath, worktree, gitdir = self.manifest.GetSubprojectPaths(self, path) | ||
| 1526 | remote = RemoteSpec(self.remote.name, | ||
| 1527 | url = url, | ||
| 1528 | review = self.remote.review) | ||
| 1529 | subproject = Project(manifest = self.manifest, | ||
| 1530 | name = name, | ||
| 1531 | remote = remote, | ||
| 1532 | gitdir = gitdir, | ||
| 1533 | worktree = worktree, | ||
| 1534 | relpath = relpath, | ||
| 1535 | revisionExpr = self.revisionExpr, | ||
| 1536 | revisionId = rev, | ||
| 1537 | rebase = self.rebase, | ||
| 1538 | groups = self.groups, | ||
| 1539 | sync_c = self.sync_c, | ||
| 1540 | sync_s = self.sync_s, | ||
| 1541 | parent = self, | ||
| 1542 | is_derived = True) | ||
| 1543 | result.append(subproject) | ||
| 1544 | result.extend(subproject.GetDerivedSubprojects()) | ||
| 1545 | return result | ||
| 1546 | |||
| 1547 | |||
| 1373 | ## Direct Git Commands ## | 1548 | ## Direct Git Commands ## |
| 1374 | 1549 | ||
| 1375 | def _RemoteFetch(self, name=None, | 1550 | def _RemoteFetch(self, name=None, |
| @@ -1382,14 +1557,14 @@ class Project(object): | |||
| 1382 | tag_name = None | 1557 | tag_name = None |
| 1383 | 1558 | ||
| 1384 | def CheckForSha1(): | 1559 | def CheckForSha1(): |
| 1385 | try: | 1560 | try: |
| 1386 | # if revision (sha or tag) is not present then following function | 1561 | # if revision (sha or tag) is not present then following function |
| 1387 | # throws an error. | 1562 | # throws an error. |
| 1388 | self.bare_git.rev_parse('--verify', '%s^0' % self.revisionExpr) | 1563 | self.bare_git.rev_parse('--verify', '%s^0' % self.revisionExpr) |
| 1389 | return True | 1564 | return True |
| 1390 | except GitError: | 1565 | except GitError: |
| 1391 | # There is no such persistent revision. We have to fetch it. | 1566 | # There is no such persistent revision. We have to fetch it. |
| 1392 | return False | 1567 | return False |
| 1393 | 1568 | ||
| 1394 | if current_branch_only: | 1569 | if current_branch_only: |
| 1395 | if ID_RE.match(self.revisionExpr) is not None: | 1570 | if ID_RE.match(self.revisionExpr) is not None: |
| @@ -1571,6 +1746,9 @@ class Project(object): | |||
| 1571 | os.remove(tmpPath) | 1746 | os.remove(tmpPath) |
| 1572 | if 'http_proxy' in os.environ and 'darwin' == sys.platform: | 1747 | if 'http_proxy' in os.environ and 'darwin' == sys.platform: |
| 1573 | cmd += ['--proxy', os.environ['http_proxy']] | 1748 | cmd += ['--proxy', os.environ['http_proxy']] |
| 1749 | cookiefile = GitConfig.ForUser().GetString('http.cookiefile') | ||
| 1750 | if cookiefile: | ||
| 1751 | cmd += ['--cookie', cookiefile] | ||
| 1574 | cmd += [srcUrl] | 1752 | cmd += [srcUrl] |
| 1575 | 1753 | ||
| 1576 | if IsTrace(): | 1754 | if IsTrace(): |
| @@ -1588,7 +1766,8 @@ class Project(object): | |||
| 1588 | # returned another error with the HTTP error code being 400 or above. | 1766 | # returned another error with the HTTP error code being 400 or above. |
| 1589 | # This return code only appears if -f, --fail is used. | 1767 | # This return code only appears if -f, --fail is used. |
| 1590 | if not quiet: | 1768 | if not quiet: |
| 1591 | print >> sys.stderr, "Server does not provide clone.bundle; ignoring." | 1769 | print("Server does not provide clone.bundle; ignoring.", |
| 1770 | file=sys.stderr) | ||
| 1592 | return False | 1771 | return False |
| 1593 | 1772 | ||
| 1594 | if os.path.exists(tmpPath): | 1773 | if os.path.exists(tmpPath): |
| @@ -1836,7 +2015,8 @@ class Project(object): | |||
| 1836 | if p.Wait() == 0: | 2015 | if p.Wait() == 0: |
| 1837 | out = p.stdout | 2016 | out = p.stdout |
| 1838 | if out: | 2017 | if out: |
| 1839 | return out[:-1].split("\0") | 2018 | return out[:-1].split('\0') # pylint: disable=W1401 |
| 2019 | # Backslash is not anomalous | ||
| 1840 | return [] | 2020 | return [] |
| 1841 | 2021 | ||
| 1842 | def DiffZ(self, name, *args): | 2022 | def DiffZ(self, name, *args): |
| @@ -1852,7 +2032,7 @@ class Project(object): | |||
| 1852 | out = p.process.stdout.read() | 2032 | out = p.process.stdout.read() |
| 1853 | r = {} | 2033 | r = {} |
| 1854 | if out: | 2034 | if out: |
| 1855 | out = iter(out[:-1].split('\0')) | 2035 | out = iter(out[:-1].split('\0')) # pylint: disable=W1401 |
| 1856 | while out: | 2036 | while out: |
| 1857 | try: | 2037 | try: |
| 1858 | info = out.next() | 2038 | info = out.next() |
| @@ -1879,7 +2059,7 @@ class Project(object): | |||
| 1879 | self.level = self.level[1:] | 2059 | self.level = self.level[1:] |
| 1880 | 2060 | ||
| 1881 | info = info[1:].split(' ') | 2061 | info = info[1:].split(' ') |
| 1882 | info =_Info(path, *info) | 2062 | info = _Info(path, *info) |
| 1883 | if info.status in ('R', 'C'): | 2063 | if info.status in ('R', 'C'): |
| 1884 | info.src_path = info.path | 2064 | info.src_path = info.path |
| 1885 | info.path = out.next() | 2065 | info.path = out.next() |
| @@ -1893,7 +2073,10 @@ class Project(object): | |||
| 1893 | path = os.path.join(self._project.gitdir, HEAD) | 2073 | path = os.path.join(self._project.gitdir, HEAD) |
| 1894 | else: | 2074 | else: |
| 1895 | path = os.path.join(self._project.worktree, '.git', HEAD) | 2075 | path = os.path.join(self._project.worktree, '.git', HEAD) |
| 1896 | fd = open(path, 'rb') | 2076 | try: |
| 2077 | fd = open(path, 'rb') | ||
| 2078 | except IOError: | ||
| 2079 | raise NoManifestException(path) | ||
| 1897 | try: | 2080 | try: |
| 1898 | line = fd.read() | 2081 | line = fd.read() |
| 1899 | finally: | 2082 | finally: |
| @@ -1988,6 +2171,9 @@ class Project(object): | |||
| 1988 | raise TypeError('%s() got an unexpected keyword argument %r' | 2171 | raise TypeError('%s() got an unexpected keyword argument %r' |
| 1989 | % (name, k)) | 2172 | % (name, k)) |
| 1990 | if config is not None: | 2173 | if config is not None: |
| 2174 | if not git_require((1, 7, 2)): | ||
| 2175 | raise ValueError('cannot set config on command line for %s()' | ||
| 2176 | % name) | ||
| 1991 | for k, v in config.iteritems(): | 2177 | for k, v in config.iteritems(): |
| 1992 | cmdv.append('-c') | 2178 | cmdv.append('-c') |
| 1993 | cmdv.append('%s=%s' % (k, v)) | 2179 | cmdv.append('%s=%s' % (k, v)) |
| @@ -1,9 +1,10 @@ | |||
| 1 | #!/bin/sh | 1 | #!/usr/bin/env python |
| 2 | 2 | ||
| 3 | ## repo default configuration | 3 | ## repo default configuration |
| 4 | ## | 4 | ## |
| 5 | REPO_URL='https://gerrit.googlesource.com/git-repo' | 5 | from __future__ import print_function |
| 6 | REPO_REV='stable' | 6 | REPO_URL = 'https://gerrit.googlesource.com/git-repo' |
| 7 | REPO_REV = 'stable' | ||
| 7 | 8 | ||
| 8 | # Copyright (C) 2008 Google Inc. | 9 | # Copyright (C) 2008 Google Inc. |
| 9 | # | 10 | # |
| @@ -19,19 +20,11 @@ REPO_REV='stable' | |||
| 19 | # See the License for the specific language governing permissions and | 20 | # See the License for the specific language governing permissions and |
| 20 | # limitations under the License. | 21 | # limitations under the License. |
| 21 | 22 | ||
| 22 | magic='--calling-python-from-/bin/sh--' | ||
| 23 | """exec" python -E "$0" "$@" """#$magic" | ||
| 24 | if __name__ == '__main__': | ||
| 25 | import sys | ||
| 26 | if sys.argv[-1] == '#%s' % magic: | ||
| 27 | del sys.argv[-1] | ||
| 28 | del magic | ||
| 29 | |||
| 30 | # increment this whenever we make important changes to this script | 23 | # increment this whenever we make important changes to this script |
| 31 | VERSION = (1, 18) | 24 | VERSION = (1, 19) |
| 32 | 25 | ||
| 33 | # increment this if the MAINTAINER_KEYS block is modified | 26 | # increment this if the MAINTAINER_KEYS block is modified |
| 34 | KEYRING_VERSION = (1,1) | 27 | KEYRING_VERSION = (1, 1) |
| 35 | MAINTAINER_KEYS = """ | 28 | MAINTAINER_KEYS = """ |
| 36 | 29 | ||
| 37 | Repo Maintainer <repo@android.kernel.org> | 30 | Repo Maintainer <repo@android.kernel.org> |
| @@ -110,7 +103,7 @@ V6pfUgqKLWa/aK7/N1ZHnPdFLD8Xt0Dmy4BPwrKC | |||
| 110 | """ | 103 | """ |
| 111 | 104 | ||
| 112 | GIT = 'git' # our git command | 105 | GIT = 'git' # our git command |
| 113 | MIN_GIT_VERSION = (1, 5, 4) # minimum supported git version | 106 | MIN_GIT_VERSION = (1, 7, 2) # minimum supported git version |
| 114 | repodir = '.repo' # name of repo's private directory | 107 | repodir = '.repo' # name of repo's private directory |
| 115 | S_repo = 'repo' # special repo repository | 108 | S_repo = 'repo' # special repo repository |
| 116 | S_manifests = 'manifests' # special manifest repository | 109 | S_manifests = 'manifests' # special manifest repository |
| @@ -120,9 +113,21 @@ REPO_MAIN = S_repo + '/main.py' # main script | |||
| 120 | import optparse | 113 | import optparse |
| 121 | import os | 114 | import os |
| 122 | import re | 115 | import re |
| 116 | import stat | ||
| 123 | import subprocess | 117 | import subprocess |
| 124 | import sys | 118 | import sys |
| 125 | import urllib2 | 119 | try: |
| 120 | import urllib2 | ||
| 121 | except ImportError: | ||
| 122 | # For python3 | ||
| 123 | import urllib.request | ||
| 124 | import urllib.error | ||
| 125 | else: | ||
| 126 | # For python2 | ||
| 127 | import imp | ||
| 128 | urllib = imp.new_module('urllib') | ||
| 129 | urllib.request = urllib2 | ||
| 130 | urllib.error = urllib2 | ||
| 126 | 131 | ||
| 127 | home_dot_repo = os.path.expanduser('~/.repoconfig') | 132 | home_dot_repo = os.path.expanduser('~/.repoconfig') |
| 128 | gpg_dir = os.path.join(home_dot_repo, 'gnupg') | 133 | gpg_dir = os.path.join(home_dot_repo, 'gnupg') |
| @@ -149,7 +154,8 @@ group.add_option('-m', '--manifest-name', | |||
| 149 | help='initial manifest file', metavar='NAME.xml') | 154 | help='initial manifest file', metavar='NAME.xml') |
| 150 | group.add_option('--mirror', | 155 | group.add_option('--mirror', |
| 151 | dest='mirror', action='store_true', | 156 | dest='mirror', action='store_true', |
| 152 | help='mirror the forrest') | 157 | help='create a replica of the remote repositories ' |
| 158 | 'rather than a client working directory') | ||
| 153 | group.add_option('--reference', | 159 | group.add_option('--reference', |
| 154 | dest='reference', | 160 | dest='reference', |
| 155 | help='location of mirror directory', metavar='DIR') | 161 | help='location of mirror directory', metavar='DIR') |
| @@ -211,17 +217,16 @@ def _Init(args): | |||
| 211 | if branch.startswith('refs/heads/'): | 217 | if branch.startswith('refs/heads/'): |
| 212 | branch = branch[len('refs/heads/'):] | 218 | branch = branch[len('refs/heads/'):] |
| 213 | if branch.startswith('refs/'): | 219 | if branch.startswith('refs/'): |
| 214 | print >>sys.stderr, "fatal: invalid branch name '%s'" % branch | 220 | print("fatal: invalid branch name '%s'" % branch, file=sys.stderr) |
| 215 | raise CloneFailure() | 221 | raise CloneFailure() |
| 216 | 222 | ||
| 217 | if not os.path.isdir(repodir): | 223 | if not os.path.isdir(repodir): |
| 218 | try: | 224 | try: |
| 219 | os.mkdir(repodir) | 225 | os.mkdir(repodir) |
| 220 | except OSError as e: | 226 | except OSError as e: |
| 221 | print >>sys.stderr, \ | 227 | print('fatal: cannot make %s directory: %s' |
| 222 | 'fatal: cannot make %s directory: %s' % ( | 228 | % (repodir, e.strerror), file=sys.stderr) |
| 223 | repodir, e.strerror) | 229 | # Don't raise CloneFailure; that would delete the |
| 224 | # Don't faise CloneFailure; that would delete the | ||
| 225 | # name. Instead exit immediately. | 230 | # name. Instead exit immediately. |
| 226 | # | 231 | # |
| 227 | sys.exit(1) | 232 | sys.exit(1) |
| @@ -244,8 +249,8 @@ def _Init(args): | |||
| 244 | _Checkout(dst, branch, rev, opt.quiet) | 249 | _Checkout(dst, branch, rev, opt.quiet) |
| 245 | except CloneFailure: | 250 | except CloneFailure: |
| 246 | if opt.quiet: | 251 | if opt.quiet: |
| 247 | print >>sys.stderr, \ | 252 | print('fatal: repo init failed; run without --quiet to see why', |
| 248 | 'fatal: repo init failed; run without --quiet to see why' | 253 | file=sys.stderr) |
| 249 | raise | 254 | raise |
| 250 | 255 | ||
| 251 | 256 | ||
| @@ -254,12 +259,12 @@ def _CheckGitVersion(): | |||
| 254 | try: | 259 | try: |
| 255 | proc = subprocess.Popen(cmd, stdout=subprocess.PIPE) | 260 | proc = subprocess.Popen(cmd, stdout=subprocess.PIPE) |
| 256 | except OSError as e: | 261 | except OSError as e: |
| 257 | print >>sys.stderr | 262 | print(file=sys.stderr) |
| 258 | print >>sys.stderr, "fatal: '%s' is not available" % GIT | 263 | print("fatal: '%s' is not available" % GIT, file=sys.stderr) |
| 259 | print >>sys.stderr, 'fatal: %s' % e | 264 | print('fatal: %s' % e, file=sys.stderr) |
| 260 | print >>sys.stderr | 265 | print(file=sys.stderr) |
| 261 | print >>sys.stderr, 'Please make sure %s is installed'\ | 266 | print('Please make sure %s is installed and in your path.' % GIT, |
| 262 | ' and in your path.' % GIT | 267 | file=sys.stderr) |
| 263 | raise CloneFailure() | 268 | raise CloneFailure() |
| 264 | 269 | ||
| 265 | ver_str = proc.stdout.read().strip() | 270 | ver_str = proc.stdout.read().strip() |
| @@ -267,14 +272,14 @@ def _CheckGitVersion(): | |||
| 267 | proc.wait() | 272 | proc.wait() |
| 268 | 273 | ||
| 269 | if not ver_str.startswith('git version '): | 274 | if not ver_str.startswith('git version '): |
| 270 | print >>sys.stderr, 'error: "%s" unsupported' % ver_str | 275 | print('error: "%s" unsupported' % ver_str, file=sys.stderr) |
| 271 | raise CloneFailure() | 276 | raise CloneFailure() |
| 272 | 277 | ||
| 273 | ver_str = ver_str[len('git version '):].strip() | 278 | ver_str = ver_str[len('git version '):].strip() |
| 274 | ver_act = tuple(map(lambda x: int(x), ver_str.split('.')[0:3])) | 279 | ver_act = tuple(map(int, ver_str.split('.')[0:3])) |
| 275 | if ver_act < MIN_GIT_VERSION: | 280 | if ver_act < MIN_GIT_VERSION: |
| 276 | need = '.'.join(map(lambda x: str(x), MIN_GIT_VERSION)) | 281 | need = '.'.join(map(str, MIN_GIT_VERSION)) |
| 277 | print >>sys.stderr, 'fatal: git %s or later required' % need | 282 | print('fatal: git %s or later required' % need, file=sys.stderr) |
| 278 | raise CloneFailure() | 283 | raise CloneFailure() |
| 279 | 284 | ||
| 280 | 285 | ||
| @@ -290,7 +295,7 @@ def NeedSetupGnuPG(): | |||
| 290 | if not kv: | 295 | if not kv: |
| 291 | return True | 296 | return True |
| 292 | 297 | ||
| 293 | kv = tuple(map(lambda x: int(x), kv.split('.'))) | 298 | kv = tuple(map(int, kv.split('.'))) |
| 294 | if kv < KEYRING_VERSION: | 299 | if kv < KEYRING_VERSION: |
| 295 | return True | 300 | return True |
| 296 | return False | 301 | return False |
| @@ -301,18 +306,16 @@ def SetupGnuPG(quiet): | |||
| 301 | try: | 306 | try: |
| 302 | os.mkdir(home_dot_repo) | 307 | os.mkdir(home_dot_repo) |
| 303 | except OSError as e: | 308 | except OSError as e: |
| 304 | print >>sys.stderr, \ | 309 | print('fatal: cannot make %s directory: %s' |
| 305 | 'fatal: cannot make %s directory: %s' % ( | 310 | % (home_dot_repo, e.strerror), file=sys.stderr) |
| 306 | home_dot_repo, e.strerror) | ||
| 307 | sys.exit(1) | 311 | sys.exit(1) |
| 308 | 312 | ||
| 309 | if not os.path.isdir(gpg_dir): | 313 | if not os.path.isdir(gpg_dir): |
| 310 | try: | 314 | try: |
| 311 | os.mkdir(gpg_dir, 0700) | 315 | os.mkdir(gpg_dir, stat.S_IRWXU) |
| 312 | except OSError as e: | 316 | except OSError as e: |
| 313 | print >>sys.stderr, \ | 317 | print('fatal: cannot make %s directory: %s' % (gpg_dir, e.strerror), |
| 314 | 'fatal: cannot make %s directory: %s' % ( | 318 | file=sys.stderr) |
| 315 | gpg_dir, e.strerror) | ||
| 316 | sys.exit(1) | 319 | sys.exit(1) |
| 317 | 320 | ||
| 318 | env = os.environ.copy() | 321 | env = os.environ.copy() |
| @@ -325,21 +328,21 @@ def SetupGnuPG(quiet): | |||
| 325 | stdin = subprocess.PIPE) | 328 | stdin = subprocess.PIPE) |
| 326 | except OSError as e: | 329 | except OSError as e: |
| 327 | if not quiet: | 330 | if not quiet: |
| 328 | print >>sys.stderr, 'warning: gpg (GnuPG) is not available.' | 331 | print('warning: gpg (GnuPG) is not available.', file=sys.stderr) |
| 329 | print >>sys.stderr, 'warning: Installing it is strongly encouraged.' | 332 | print('warning: Installing it is strongly encouraged.', file=sys.stderr) |
| 330 | print >>sys.stderr | 333 | print(file=sys.stderr) |
| 331 | return False | 334 | return False |
| 332 | 335 | ||
| 333 | proc.stdin.write(MAINTAINER_KEYS) | 336 | proc.stdin.write(MAINTAINER_KEYS) |
| 334 | proc.stdin.close() | 337 | proc.stdin.close() |
| 335 | 338 | ||
| 336 | if proc.wait() != 0: | 339 | if proc.wait() != 0: |
| 337 | print >>sys.stderr, 'fatal: registering repo maintainer keys failed' | 340 | print('fatal: registering repo maintainer keys failed', file=sys.stderr) |
| 338 | sys.exit(1) | 341 | sys.exit(1) |
| 339 | 342 | print() | |
| 340 | 343 | ||
| 341 | fd = open(os.path.join(home_dot_repo, 'keyring-version'), 'w') | 344 | fd = open(os.path.join(home_dot_repo, 'keyring-version'), 'w') |
| 342 | fd.write('.'.join(map(lambda x: str(x), KEYRING_VERSION)) + '\n') | 345 | fd.write('.'.join(map(str, KEYRING_VERSION)) + '\n') |
| 343 | fd.close() | 346 | fd.close() |
| 344 | return True | 347 | return True |
| 345 | 348 | ||
| @@ -355,7 +358,7 @@ def _SetConfig(local, name, value): | |||
| 355 | def _InitHttp(): | 358 | def _InitHttp(): |
| 356 | handlers = [] | 359 | handlers = [] |
| 357 | 360 | ||
| 358 | mgr = urllib2.HTTPPasswordMgrWithDefaultRealm() | 361 | mgr = urllib.request.HTTPPasswordMgrWithDefaultRealm() |
| 359 | try: | 362 | try: |
| 360 | import netrc | 363 | import netrc |
| 361 | n = netrc.netrc() | 364 | n = netrc.netrc() |
| @@ -365,20 +368,20 @@ def _InitHttp(): | |||
| 365 | mgr.add_password(p[1], 'https://%s/' % host, p[0], p[2]) | 368 | mgr.add_password(p[1], 'https://%s/' % host, p[0], p[2]) |
| 366 | except: | 369 | except: |
| 367 | pass | 370 | pass |
| 368 | handlers.append(urllib2.HTTPBasicAuthHandler(mgr)) | 371 | handlers.append(urllib.request.HTTPBasicAuthHandler(mgr)) |
| 369 | handlers.append(urllib2.HTTPDigestAuthHandler(mgr)) | 372 | handlers.append(urllib.request.HTTPDigestAuthHandler(mgr)) |
| 370 | 373 | ||
| 371 | if 'http_proxy' in os.environ: | 374 | if 'http_proxy' in os.environ: |
| 372 | url = os.environ['http_proxy'] | 375 | url = os.environ['http_proxy'] |
| 373 | handlers.append(urllib2.ProxyHandler({'http': url, 'https': url})) | 376 | handlers.append(urllib.request.ProxyHandler({'http': url, 'https': url})) |
| 374 | if 'REPO_CURL_VERBOSE' in os.environ: | 377 | if 'REPO_CURL_VERBOSE' in os.environ: |
| 375 | handlers.append(urllib2.HTTPHandler(debuglevel=1)) | 378 | handlers.append(urllib.request.HTTPHandler(debuglevel=1)) |
| 376 | handlers.append(urllib2.HTTPSHandler(debuglevel=1)) | 379 | handlers.append(urllib.request.HTTPSHandler(debuglevel=1)) |
| 377 | urllib2.install_opener(urllib2.build_opener(*handlers)) | 380 | urllib.request.install_opener(urllib.request.build_opener(*handlers)) |
| 378 | 381 | ||
| 379 | def _Fetch(url, local, src, quiet): | 382 | def _Fetch(url, local, src, quiet): |
| 380 | if not quiet: | 383 | if not quiet: |
| 381 | print >>sys.stderr, 'Get %s' % url | 384 | print('Get %s' % url, file=sys.stderr) |
| 382 | 385 | ||
| 383 | cmd = [GIT, 'fetch'] | 386 | cmd = [GIT, 'fetch'] |
| 384 | if quiet: | 387 | if quiet: |
| @@ -423,20 +426,20 @@ def _DownloadBundle(url, local, quiet): | |||
| 423 | dest = open(os.path.join(local, '.git', 'clone.bundle'), 'w+b') | 426 | dest = open(os.path.join(local, '.git', 'clone.bundle'), 'w+b') |
| 424 | try: | 427 | try: |
| 425 | try: | 428 | try: |
| 426 | r = urllib2.urlopen(url) | 429 | r = urllib.request.urlopen(url) |
| 427 | except urllib2.HTTPError as e: | 430 | except urllib.error.HTTPError as e: |
| 428 | if e.code == 404: | 431 | if e.code in [403, 404]: |
| 429 | return False | 432 | return False |
| 430 | print >>sys.stderr, 'fatal: Cannot get %s' % url | 433 | print('fatal: Cannot get %s' % url, file=sys.stderr) |
| 431 | print >>sys.stderr, 'fatal: HTTP error %s' % e.code | 434 | print('fatal: HTTP error %s' % e.code, file=sys.stderr) |
| 432 | raise CloneFailure() | 435 | raise CloneFailure() |
| 433 | except urllib2.URLError as e: | 436 | except urllib.error.URLError as e: |
| 434 | print >>sys.stderr, 'fatal: Cannot get %s' % url | 437 | print('fatal: Cannot get %s' % url, file=sys.stderr) |
| 435 | print >>sys.stderr, 'fatal: error %s' % e.reason | 438 | print('fatal: error %s' % e.reason, file=sys.stderr) |
| 436 | raise CloneFailure() | 439 | raise CloneFailure() |
| 437 | try: | 440 | try: |
| 438 | if not quiet: | 441 | if not quiet: |
| 439 | print >>sys.stderr, 'Get %s' % url | 442 | print('Get %s' % url, file=sys.stderr) |
| 440 | while True: | 443 | while True: |
| 441 | buf = r.read(8192) | 444 | buf = r.read(8192) |
| 442 | if buf == '': | 445 | if buf == '': |
| @@ -460,24 +463,23 @@ def _Clone(url, local, quiet): | |||
| 460 | try: | 463 | try: |
| 461 | os.mkdir(local) | 464 | os.mkdir(local) |
| 462 | except OSError as e: | 465 | except OSError as e: |
| 463 | print >>sys.stderr, \ | 466 | print('fatal: cannot make %s directory: %s' % (local, e.strerror), |
| 464 | 'fatal: cannot make %s directory: %s' \ | 467 | file=sys.stderr) |
| 465 | % (local, e.strerror) | ||
| 466 | raise CloneFailure() | 468 | raise CloneFailure() |
| 467 | 469 | ||
| 468 | cmd = [GIT, 'init', '--quiet'] | 470 | cmd = [GIT, 'init', '--quiet'] |
| 469 | try: | 471 | try: |
| 470 | proc = subprocess.Popen(cmd, cwd = local) | 472 | proc = subprocess.Popen(cmd, cwd = local) |
| 471 | except OSError as e: | 473 | except OSError as e: |
| 472 | print >>sys.stderr | 474 | print(file=sys.stderr) |
| 473 | print >>sys.stderr, "fatal: '%s' is not available" % GIT | 475 | print("fatal: '%s' is not available" % GIT, file=sys.stderr) |
| 474 | print >>sys.stderr, 'fatal: %s' % e | 476 | print('fatal: %s' % e, file=sys.stderr) |
| 475 | print >>sys.stderr | 477 | print(file=sys.stderr) |
| 476 | print >>sys.stderr, 'Please make sure %s is installed'\ | 478 | print('Please make sure %s is installed and in your path.' % GIT, |
| 477 | ' and in your path.' % GIT | 479 | file=sys.stderr) |
| 478 | raise CloneFailure() | 480 | raise CloneFailure() |
| 479 | if proc.wait() != 0: | 481 | if proc.wait() != 0: |
| 480 | print >>sys.stderr, 'fatal: could not create %s' % local | 482 | print('fatal: could not create %s' % local, file=sys.stderr) |
| 481 | raise CloneFailure() | 483 | raise CloneFailure() |
| 482 | 484 | ||
| 483 | _InitHttp() | 485 | _InitHttp() |
| @@ -505,21 +507,18 @@ def _Verify(cwd, branch, quiet): | |||
| 505 | proc.stderr.close() | 507 | proc.stderr.close() |
| 506 | 508 | ||
| 507 | if proc.wait() != 0 or not cur: | 509 | if proc.wait() != 0 or not cur: |
| 508 | print >>sys.stderr | 510 | print(file=sys.stderr) |
| 509 | print >>sys.stderr,\ | 511 | print("fatal: branch '%s' has not been signed" % branch, file=sys.stderr) |
| 510 | "fatal: branch '%s' has not been signed" \ | ||
| 511 | % branch | ||
| 512 | raise CloneFailure() | 512 | raise CloneFailure() |
| 513 | 513 | ||
| 514 | m = re.compile(r'^(.*)-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur) | 514 | m = re.compile(r'^(.*)-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur) |
| 515 | if m: | 515 | if m: |
| 516 | cur = m.group(1) | 516 | cur = m.group(1) |
| 517 | if not quiet: | 517 | if not quiet: |
| 518 | print >>sys.stderr | 518 | print(file=sys.stderr) |
| 519 | print >>sys.stderr, \ | 519 | print("info: Ignoring branch '%s'; using tagged release '%s'" |
| 520 | "info: Ignoring branch '%s'; using tagged release '%s'" \ | 520 | % (branch, cur), file=sys.stderr) |
| 521 | % (branch, cur) | 521 | print(file=sys.stderr) |
| 522 | print >>sys.stderr | ||
| 523 | 522 | ||
| 524 | env = os.environ.copy() | 523 | env = os.environ.copy() |
| 525 | env['GNUPGHOME'] = gpg_dir.encode() | 524 | env['GNUPGHOME'] = gpg_dir.encode() |
| @@ -537,10 +536,10 @@ def _Verify(cwd, branch, quiet): | |||
| 537 | proc.stderr.close() | 536 | proc.stderr.close() |
| 538 | 537 | ||
| 539 | if proc.wait() != 0: | 538 | if proc.wait() != 0: |
| 540 | print >>sys.stderr | 539 | print(file=sys.stderr) |
| 541 | print >>sys.stderr, out | 540 | print(out, file=sys.stderr) |
| 542 | print >>sys.stderr, err | 541 | print(err, file=sys.stderr) |
| 543 | print >>sys.stderr | 542 | print(file=sys.stderr) |
| 544 | raise CloneFailure() | 543 | raise CloneFailure() |
| 545 | return '%s^0' % cur | 544 | return '%s^0' % cur |
| 546 | 545 | ||
| @@ -594,7 +593,7 @@ def _ParseArguments(args): | |||
| 594 | opt = _Options() | 593 | opt = _Options() |
| 595 | arg = [] | 594 | arg = [] |
| 596 | 595 | ||
| 597 | for i in xrange(0, len(args)): | 596 | for i in range(len(args)): |
| 598 | a = args[i] | 597 | a = args[i] |
| 599 | if a == '-h' or a == '--help': | 598 | if a == '-h' or a == '--help': |
| 600 | opt.help = True | 599 | opt.help = True |
| @@ -607,7 +606,7 @@ def _ParseArguments(args): | |||
| 607 | 606 | ||
| 608 | 607 | ||
| 609 | def _Usage(): | 608 | def _Usage(): |
| 610 | print >>sys.stderr,\ | 609 | print( |
| 611 | """usage: repo COMMAND [ARGS] | 610 | """usage: repo COMMAND [ARGS] |
| 612 | 611 | ||
| 613 | repo is not yet installed. Use "repo init" to install it here. | 612 | repo is not yet installed. Use "repo init" to install it here. |
| @@ -618,7 +617,7 @@ The most commonly used repo commands are: | |||
| 618 | help Display detailed help on a command | 617 | help Display detailed help on a command |
| 619 | 618 | ||
| 620 | For access to the full online help, install repo ("repo init"). | 619 | For access to the full online help, install repo ("repo init"). |
| 621 | """ | 620 | """, file=sys.stderr) |
| 622 | sys.exit(1) | 621 | sys.exit(1) |
| 623 | 622 | ||
| 624 | 623 | ||
| @@ -628,25 +627,23 @@ def _Help(args): | |||
| 628 | init_optparse.print_help() | 627 | init_optparse.print_help() |
| 629 | sys.exit(0) | 628 | sys.exit(0) |
| 630 | else: | 629 | else: |
| 631 | print >>sys.stderr,\ | 630 | print("error: '%s' is not a bootstrap command.\n" |
| 632 | "error: '%s' is not a bootstrap command.\n"\ | 631 | ' For access to online help, install repo ("repo init").' |
| 633 | ' For access to online help, install repo ("repo init").'\ | 632 | % args[0], file=sys.stderr) |
| 634 | % args[0] | ||
| 635 | else: | 633 | else: |
| 636 | _Usage() | 634 | _Usage() |
| 637 | sys.exit(1) | 635 | sys.exit(1) |
| 638 | 636 | ||
| 639 | 637 | ||
| 640 | def _NotInstalled(): | 638 | def _NotInstalled(): |
| 641 | print >>sys.stderr,\ | 639 | print('error: repo is not installed. Use "repo init" to install it here.', |
| 642 | 'error: repo is not installed. Use "repo init" to install it here.' | 640 | file=sys.stderr) |
| 643 | sys.exit(1) | 641 | sys.exit(1) |
| 644 | 642 | ||
| 645 | 643 | ||
| 646 | def _NoCommands(cmd): | 644 | def _NoCommands(cmd): |
| 647 | print >>sys.stderr,\ | 645 | print("""error: command '%s' requires repo to be installed first. |
| 648 | """error: command '%s' requires repo to be installed first. | 646 | Use "repo init" to install it here.""" % cmd, file=sys.stderr) |
| 649 | Use "repo init" to install it here.""" % cmd | ||
| 650 | sys.exit(1) | 647 | sys.exit(1) |
| 651 | 648 | ||
| 652 | 649 | ||
| @@ -683,7 +680,7 @@ def _SetDefaultsTo(gitdir): | |||
| 683 | proc.stderr.close() | 680 | proc.stderr.close() |
| 684 | 681 | ||
| 685 | if proc.wait() != 0: | 682 | if proc.wait() != 0: |
| 686 | print >>sys.stderr, 'fatal: %s has no current branch' % gitdir | 683 | print('fatal: %s has no current branch' % gitdir, file=sys.stderr) |
| 687 | sys.exit(1) | 684 | sys.exit(1) |
| 688 | 685 | ||
| 689 | 686 | ||
| @@ -721,7 +718,7 @@ def main(orig_args): | |||
| 721 | if my_main: | 718 | if my_main: |
| 722 | repo_main = my_main | 719 | repo_main = my_main |
| 723 | 720 | ||
| 724 | ver_str = '.'.join(map(lambda x: str(x), VERSION)) | 721 | ver_str = '.'.join(map(str, VERSION)) |
| 725 | me = [repo_main, | 722 | me = [repo_main, |
| 726 | '--repo-dir=%s' % rel_repo_dir, | 723 | '--repo-dir=%s' % rel_repo_dir, |
| 727 | '--wrapper-version=%s' % ver_str, | 724 | '--wrapper-version=%s' % ver_str, |
| @@ -732,8 +729,8 @@ def main(orig_args): | |||
| 732 | try: | 729 | try: |
| 733 | os.execv(repo_main, me) | 730 | os.execv(repo_main, me) |
| 734 | except OSError as e: | 731 | except OSError as e: |
| 735 | print >>sys.stderr, "fatal: unable to start %s" % repo_main | 732 | print("fatal: unable to start %s" % repo_main, file=sys.stderr) |
| 736 | print >>sys.stderr, "fatal: %s" % e | 733 | print("fatal: %s" % e, file=sys.stderr) |
| 737 | sys.exit(148) | 734 | sys.exit(148) |
| 738 | 735 | ||
| 739 | 736 | ||
diff --git a/subcmds/abandon.py b/subcmds/abandon.py index e17ab2b6..b94ccdd3 100644 --- a/subcmds/abandon.py +++ b/subcmds/abandon.py | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | # See the License for the specific language governing permissions and | 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. | 14 | # limitations under the License. |
| 15 | 15 | ||
| 16 | from __future__ import print_function | ||
| 16 | import sys | 17 | import sys |
| 17 | from command import Command | 18 | from command import Command |
| 18 | from git_command import git | 19 | from git_command import git |
| @@ -36,7 +37,7 @@ It is equivalent to "git branch -D <branchname>". | |||
| 36 | 37 | ||
| 37 | nb = args[0] | 38 | nb = args[0] |
| 38 | if not git.check_ref_format('heads/%s' % nb): | 39 | if not git.check_ref_format('heads/%s' % nb): |
| 39 | print >>sys.stderr, "error: '%s' is not a valid name" % nb | 40 | print("error: '%s' is not a valid name" % nb, file=sys.stderr) |
| 40 | sys.exit(1) | 41 | sys.exit(1) |
| 41 | 42 | ||
| 42 | nb = args[0] | 43 | nb = args[0] |
| @@ -58,13 +59,13 @@ It is equivalent to "git branch -D <branchname>". | |||
| 58 | 59 | ||
| 59 | if err: | 60 | if err: |
| 60 | for p in err: | 61 | for p in err: |
| 61 | print >>sys.stderr,\ | 62 | print("error: %s/: cannot abandon %s" % (p.relpath, nb), |
| 62 | "error: %s/: cannot abandon %s" \ | 63 | file=sys.stderr) |
| 63 | % (p.relpath, nb) | ||
| 64 | sys.exit(1) | 64 | sys.exit(1) |
| 65 | elif not success: | 65 | elif not success: |
| 66 | print >>sys.stderr, 'error: no project has branch %s' % nb | 66 | print('error: no project has branch %s' % nb, file=sys.stderr) |
| 67 | sys.exit(1) | 67 | sys.exit(1) |
| 68 | else: | 68 | else: |
| 69 | print >>sys.stderr, 'Abandoned in %d project(s):\n %s' % ( | 69 | print('Abandoned in %d project(s):\n %s' |
| 70 | len(success), '\n '.join(p.relpath for p in success)) | 70 | % (len(success), '\n '.join(p.relpath for p in success)), |
| 71 | file=sys.stderr) | ||
diff --git a/subcmds/branches.py b/subcmds/branches.py index a7ba3d6d..06d45abe 100644 --- a/subcmds/branches.py +++ b/subcmds/branches.py | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | # See the License for the specific language governing permissions and | 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. | 14 | # limitations under the License. |
| 15 | 15 | ||
| 16 | from __future__ import print_function | ||
| 16 | import sys | 17 | import sys |
| 17 | from color import Coloring | 18 | from color import Coloring |
| 18 | from command import Command | 19 | from command import Command |
| @@ -107,7 +108,7 @@ is shown, then the branch appears in all projects. | |||
| 107 | names.sort() | 108 | names.sort() |
| 108 | 109 | ||
| 109 | if not names: | 110 | if not names: |
| 110 | print >>sys.stderr, ' (no branches)' | 111 | print(' (no branches)', file=sys.stderr) |
| 111 | return | 112 | return |
| 112 | 113 | ||
| 113 | width = 25 | 114 | width = 25 |
diff --git a/subcmds/checkout.py b/subcmds/checkout.py index bfbe9921..cbbca106 100644 --- a/subcmds/checkout.py +++ b/subcmds/checkout.py | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | # See the License for the specific language governing permissions and | 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. | 14 | # limitations under the License. |
| 15 | 15 | ||
| 16 | from __future__ import print_function | ||
| 16 | import sys | 17 | import sys |
| 17 | from command import Command | 18 | from command import Command |
| 18 | from progress import Progress | 19 | from progress import Progress |
| @@ -55,10 +56,9 @@ The command is equivalent to: | |||
| 55 | 56 | ||
| 56 | if err: | 57 | if err: |
| 57 | for p in err: | 58 | for p in err: |
| 58 | print >>sys.stderr,\ | 59 | print("error: %s/: cannot checkout %s" % (p.relpath, nb), |
| 59 | "error: %s/: cannot checkout %s" \ | 60 | file=sys.stderr) |
| 60 | % (p.relpath, nb) | ||
| 61 | sys.exit(1) | 61 | sys.exit(1) |
| 62 | elif not success: | 62 | elif not success: |
| 63 | print >>sys.stderr, 'error: no project has branch %s' % nb | 63 | print('error: no project has branch %s' % nb, file=sys.stderr) |
| 64 | sys.exit(1) | 64 | sys.exit(1) |
diff --git a/subcmds/cherry_pick.py b/subcmds/cherry_pick.py index 7a6d4c20..01b97e07 100644 --- a/subcmds/cherry_pick.py +++ b/subcmds/cherry_pick.py | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | # See the License for the specific language governing permissions and | 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. | 14 | # limitations under the License. |
| 15 | 15 | ||
| 16 | from __future__ import print_function | ||
| 16 | import re | 17 | import re |
| 17 | import sys | 18 | import sys |
| 18 | from command import Command | 19 | from command import Command |
| @@ -46,13 +47,13 @@ change id will be added. | |||
| 46 | capture_stdout = True, | 47 | capture_stdout = True, |
| 47 | capture_stderr = True) | 48 | capture_stderr = True) |
| 48 | if p.Wait() != 0: | 49 | if p.Wait() != 0: |
| 49 | print >>sys.stderr, p.stderr | 50 | print(p.stderr, file=sys.stderr) |
| 50 | sys.exit(1) | 51 | sys.exit(1) |
| 51 | sha1 = p.stdout.strip() | 52 | sha1 = p.stdout.strip() |
| 52 | 53 | ||
| 53 | p = GitCommand(None, ['cat-file', 'commit', sha1], capture_stdout=True) | 54 | p = GitCommand(None, ['cat-file', 'commit', sha1], capture_stdout=True) |
| 54 | if p.Wait() != 0: | 55 | if p.Wait() != 0: |
| 55 | print >>sys.stderr, "error: Failed to retrieve old commit message" | 56 | print("error: Failed to retrieve old commit message", file=sys.stderr) |
| 56 | sys.exit(1) | 57 | sys.exit(1) |
| 57 | old_msg = self._StripHeader(p.stdout) | 58 | old_msg = self._StripHeader(p.stdout) |
| 58 | 59 | ||
| @@ -62,8 +63,8 @@ change id will be added. | |||
| 62 | capture_stderr = True) | 63 | capture_stderr = True) |
| 63 | status = p.Wait() | 64 | status = p.Wait() |
| 64 | 65 | ||
| 65 | print >>sys.stdout, p.stdout | 66 | print(p.stdout, file=sys.stdout) |
| 66 | print >>sys.stderr, p.stderr | 67 | print(p.stderr, file=sys.stderr) |
| 67 | 68 | ||
| 68 | if status == 0: | 69 | if status == 0: |
| 69 | # The cherry-pick was applied correctly. We just need to edit the | 70 | # The cherry-pick was applied correctly. We just need to edit the |
| @@ -76,16 +77,14 @@ change id will be added. | |||
| 76 | capture_stderr = True) | 77 | capture_stderr = True) |
| 77 | p.stdin.write(new_msg) | 78 | p.stdin.write(new_msg) |
| 78 | if p.Wait() != 0: | 79 | if p.Wait() != 0: |
| 79 | print >>sys.stderr, "error: Failed to update commit message" | 80 | print("error: Failed to update commit message", file=sys.stderr) |
| 80 | sys.exit(1) | 81 | sys.exit(1) |
| 81 | 82 | ||
| 82 | else: | 83 | else: |
| 83 | print >>sys.stderr, """\ | 84 | print('NOTE: When committing (please see above) and editing the commit' |
| 84 | NOTE: When committing (please see above) and editing the commit message, | 85 | 'message, please remove the old Change-Id-line and add:') |
| 85 | please remove the old Change-Id-line and add: | 86 | print(self._GetReference(sha1), file=sys.stderr) |
| 86 | """ | 87 | print(file=sys.stderr) |
| 87 | print >>sys.stderr, self._GetReference(sha1) | ||
| 88 | print >>sys.stderr | ||
| 89 | 88 | ||
| 90 | def _IsChangeId(self, line): | 89 | def _IsChangeId(self, line): |
| 91 | return CHANGE_ID_RE.match(line) | 90 | return CHANGE_ID_RE.match(line) |
diff --git a/subcmds/download.py b/subcmds/download.py index 0abe90d0..471e88b5 100644 --- a/subcmds/download.py +++ b/subcmds/download.py | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | # See the License for the specific language governing permissions and | 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. | 14 | # limitations under the License. |
| 15 | 15 | ||
| 16 | from __future__ import print_function | ||
| 16 | import re | 17 | import re |
| 17 | import sys | 18 | import sys |
| 18 | 19 | ||
| @@ -32,13 +33,13 @@ makes it available in your project's local working directory. | |||
| 32 | """ | 33 | """ |
| 33 | 34 | ||
| 34 | def _Options(self, p): | 35 | def _Options(self, p): |
| 35 | p.add_option('-c','--cherry-pick', | 36 | p.add_option('-c', '--cherry-pick', |
| 36 | dest='cherrypick', action='store_true', | 37 | dest='cherrypick', action='store_true', |
| 37 | help="cherry-pick instead of checkout") | 38 | help="cherry-pick instead of checkout") |
| 38 | p.add_option('-r','--revert', | 39 | p.add_option('-r', '--revert', |
| 39 | dest='revert', action='store_true', | 40 | dest='revert', action='store_true', |
| 40 | help="revert instead of checkout") | 41 | help="revert instead of checkout") |
| 41 | p.add_option('-f','--ff-only', | 42 | p.add_option('-f', '--ff-only', |
| 42 | dest='ffonly', action='store_true', | 43 | dest='ffonly', action='store_true', |
| 43 | help="force fast-forward merge") | 44 | help="force fast-forward merge") |
| 44 | 45 | ||
| @@ -68,23 +69,23 @@ makes it available in your project's local working directory. | |||
| 68 | for project, change_id, ps_id in self._ParseChangeIds(args): | 69 | for project, change_id, ps_id in self._ParseChangeIds(args): |
| 69 | dl = project.DownloadPatchSet(change_id, ps_id) | 70 | dl = project.DownloadPatchSet(change_id, ps_id) |
| 70 | if not dl: | 71 | if not dl: |
| 71 | print >>sys.stderr, \ | 72 | print('[%s] change %d/%d not found' |
| 72 | '[%s] change %d/%d not found' \ | 73 | % (project.name, change_id, ps_id), |
| 73 | % (project.name, change_id, ps_id) | 74 | file=sys.stderr) |
| 74 | sys.exit(1) | 75 | sys.exit(1) |
| 75 | 76 | ||
| 76 | if not opt.revert and not dl.commits: | 77 | if not opt.revert and not dl.commits: |
| 77 | print >>sys.stderr, \ | 78 | print('[%s] change %d/%d has already been merged' |
| 78 | '[%s] change %d/%d has already been merged' \ | 79 | % (project.name, change_id, ps_id), |
| 79 | % (project.name, change_id, ps_id) | 80 | file=sys.stderr) |
| 80 | continue | 81 | continue |
| 81 | 82 | ||
| 82 | if len(dl.commits) > 1: | 83 | if len(dl.commits) > 1: |
| 83 | print >>sys.stderr, \ | 84 | print('[%s] %d/%d depends on %d unmerged changes:' \ |
| 84 | '[%s] %d/%d depends on %d unmerged changes:' \ | 85 | % (project.name, change_id, ps_id, len(dl.commits)), |
| 85 | % (project.name, change_id, ps_id, len(dl.commits)) | 86 | file=sys.stderr) |
| 86 | for c in dl.commits: | 87 | for c in dl.commits: |
| 87 | print >>sys.stderr, ' %s' % (c) | 88 | print(' %s' % (c), file=sys.stderr) |
| 88 | if opt.cherrypick: | 89 | if opt.cherrypick: |
| 89 | project._CherryPick(dl.commit) | 90 | project._CherryPick(dl.commit) |
| 90 | elif opt.revert: | 91 | elif opt.revert: |
diff --git a/subcmds/forall.py b/subcmds/forall.py index b633b7d4..4c1c9ff8 100644 --- a/subcmds/forall.py +++ b/subcmds/forall.py | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | # See the License for the specific language governing permissions and | 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. | 14 | # limitations under the License. |
| 15 | 15 | ||
| 16 | from __future__ import print_function | ||
| 16 | import fcntl | 17 | import fcntl |
| 17 | import re | 18 | import re |
| 18 | import os | 19 | import os |
| @@ -92,6 +93,9 @@ following <command>. | |||
| 92 | 93 | ||
| 93 | Unless -p is used, stdin, stdout, stderr are inherited from the | 94 | Unless -p is used, stdin, stdout, stderr are inherited from the |
| 94 | terminal and are not redirected. | 95 | terminal and are not redirected. |
| 96 | |||
| 97 | If -e is used, when a command exits unsuccessfully, '%prog' will abort | ||
| 98 | without iterating through the remaining projects. | ||
| 95 | """ | 99 | """ |
| 96 | 100 | ||
| 97 | def _Options(self, p): | 101 | def _Options(self, p): |
| @@ -104,6 +108,9 @@ terminal and are not redirected. | |||
| 104 | dest='command', | 108 | dest='command', |
| 105 | action='callback', | 109 | action='callback', |
| 106 | callback=cmd) | 110 | callback=cmd) |
| 111 | p.add_option('-e', '--abort-on-errors', | ||
| 112 | dest='abort_on_errors', action='store_true', | ||
| 113 | help='Abort if a command exits unsuccessfully') | ||
| 107 | 114 | ||
| 108 | g = p.add_option_group('Output') | 115 | g = p.add_option_group('Output') |
| 109 | g.add_option('-p', | 116 | g.add_option('-p', |
| @@ -183,7 +190,7 @@ terminal and are not redirected. | |||
| 183 | if not os.path.exists(cwd): | 190 | if not os.path.exists(cwd): |
| 184 | if (opt.project_header and opt.verbose) \ | 191 | if (opt.project_header and opt.verbose) \ |
| 185 | or not opt.project_header: | 192 | or not opt.project_header: |
| 186 | print >>sys.stderr, 'skipping %s/' % project.relpath | 193 | print('skipping %s/' % project.relpath, file=sys.stderr) |
| 187 | continue | 194 | continue |
| 188 | 195 | ||
| 189 | if opt.project_header: | 196 | if opt.project_header: |
| @@ -254,7 +261,12 @@ terminal and are not redirected. | |||
| 254 | s.dest.flush() | 261 | s.dest.flush() |
| 255 | 262 | ||
| 256 | r = p.wait() | 263 | r = p.wait() |
| 257 | if r != 0 and r != rc: | 264 | if r != 0: |
| 258 | rc = r | 265 | if r != rc: |
| 266 | rc = r | ||
| 267 | if opt.abort_on_errors: | ||
| 268 | print("error: %s: Aborting due to previous error" % project.relpath, | ||
| 269 | file=sys.stderr) | ||
| 270 | sys.exit(r) | ||
| 259 | if rc != 0: | 271 | if rc != 0: |
| 260 | sys.exit(rc) | 272 | sys.exit(rc) |
diff --git a/subcmds/grep.py b/subcmds/grep.py index 0dc8f9f6..dd391cfa 100644 --- a/subcmds/grep.py +++ b/subcmds/grep.py | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | # See the License for the specific language governing permissions and | 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. | 14 | # limitations under the License. |
| 15 | 15 | ||
| 16 | from __future__ import print_function | ||
| 16 | import sys | 17 | import sys |
| 17 | from color import Coloring | 18 | from color import Coloring |
| 18 | from command import PagedCommand | 19 | from command import PagedCommand |
| @@ -51,7 +52,7 @@ Examples | |||
| 51 | 52 | ||
| 52 | Look for a line that has '#define' and either 'MAX_PATH or 'PATH_MAX': | 53 | Look for a line that has '#define' and either 'MAX_PATH or 'PATH_MAX': |
| 53 | 54 | ||
| 54 | repo grep -e '#define' --and -\( -e MAX_PATH -e PATH_MAX \) | 55 | repo grep -e '#define' --and -\\( -e MAX_PATH -e PATH_MAX \\) |
| 55 | 56 | ||
| 56 | Look for a line that has 'NODE' or 'Unexpected' in files that | 57 | Look for a line that has 'NODE' or 'Unexpected' in files that |
| 57 | contain a line that matches both expressions: | 58 | contain a line that matches both expressions: |
| @@ -84,7 +85,7 @@ contain a line that matches both expressions: | |||
| 84 | g.add_option('--cached', | 85 | g.add_option('--cached', |
| 85 | action='callback', callback=carry, | 86 | action='callback', callback=carry, |
| 86 | help='Search the index, instead of the work tree') | 87 | help='Search the index, instead of the work tree') |
| 87 | g.add_option('-r','--revision', | 88 | g.add_option('-r', '--revision', |
| 88 | dest='revision', action='append', metavar='TREEish', | 89 | dest='revision', action='append', metavar='TREEish', |
| 89 | help='Search TREEish, instead of the work tree') | 90 | help='Search TREEish, instead of the work tree') |
| 90 | 91 | ||
| @@ -96,7 +97,7 @@ contain a line that matches both expressions: | |||
| 96 | g.add_option('-i', '--ignore-case', | 97 | g.add_option('-i', '--ignore-case', |
| 97 | action='callback', callback=carry, | 98 | action='callback', callback=carry, |
| 98 | help='Ignore case differences') | 99 | help='Ignore case differences') |
| 99 | g.add_option('-a','--text', | 100 | g.add_option('-a', '--text', |
| 100 | action='callback', callback=carry, | 101 | action='callback', callback=carry, |
| 101 | help="Process binary files as if they were text") | 102 | help="Process binary files as if they were text") |
| 102 | g.add_option('-I', | 103 | g.add_option('-I', |
| @@ -125,7 +126,7 @@ contain a line that matches both expressions: | |||
| 125 | g.add_option('--and', '--or', '--not', | 126 | g.add_option('--and', '--or', '--not', |
| 126 | action='callback', callback=carry, | 127 | action='callback', callback=carry, |
| 127 | help='Boolean operators to combine patterns') | 128 | help='Boolean operators to combine patterns') |
| 128 | g.add_option('-(','-)', | 129 | g.add_option('-(', '-)', |
| 129 | action='callback', callback=carry, | 130 | action='callback', callback=carry, |
| 130 | help='Boolean operator grouping') | 131 | help='Boolean operator grouping') |
| 131 | 132 | ||
| @@ -145,10 +146,10 @@ contain a line that matches both expressions: | |||
| 145 | action='callback', callback=carry, | 146 | action='callback', callback=carry, |
| 146 | metavar='CONTEXT', type='str', | 147 | metavar='CONTEXT', type='str', |
| 147 | help='Show CONTEXT lines after match') | 148 | help='Show CONTEXT lines after match') |
| 148 | g.add_option('-l','--name-only','--files-with-matches', | 149 | g.add_option('-l', '--name-only', '--files-with-matches', |
| 149 | action='callback', callback=carry, | 150 | action='callback', callback=carry, |
| 150 | help='Show only file names containing matching lines') | 151 | help='Show only file names containing matching lines') |
| 151 | g.add_option('-L','--files-without-match', | 152 | g.add_option('-L', '--files-without-match', |
| 152 | action='callback', callback=carry, | 153 | action='callback', callback=carry, |
| 153 | help='Show only file names not containing matching lines') | 154 | help='Show only file names not containing matching lines') |
| 154 | 155 | ||
| @@ -157,9 +158,9 @@ contain a line that matches both expressions: | |||
| 157 | out = GrepColoring(self.manifest.manifestProject.config) | 158 | out = GrepColoring(self.manifest.manifestProject.config) |
| 158 | 159 | ||
| 159 | cmd_argv = ['grep'] | 160 | cmd_argv = ['grep'] |
| 160 | if out.is_on and git_require((1,6,3)): | 161 | if out.is_on and git_require((1, 6, 3)): |
| 161 | cmd_argv.append('--color') | 162 | cmd_argv.append('--color') |
| 162 | cmd_argv.extend(getattr(opt,'cmd_argv',[])) | 163 | cmd_argv.extend(getattr(opt, 'cmd_argv', [])) |
| 163 | 164 | ||
| 164 | if '-e' not in cmd_argv: | 165 | if '-e' not in cmd_argv: |
| 165 | if not args: | 166 | if not args: |
| @@ -178,8 +179,7 @@ contain a line that matches both expressions: | |||
| 178 | have_rev = False | 179 | have_rev = False |
| 179 | if opt.revision: | 180 | if opt.revision: |
| 180 | if '--cached' in cmd_argv: | 181 | if '--cached' in cmd_argv: |
| 181 | print >>sys.stderr,\ | 182 | print('fatal: cannot combine --cached and --revision', file=sys.stderr) |
| 182 | 'fatal: cannot combine --cached and --revision' | ||
| 183 | sys.exit(1) | 183 | sys.exit(1) |
| 184 | have_rev = True | 184 | have_rev = True |
| 185 | cmd_argv.extend(opt.revision) | 185 | cmd_argv.extend(opt.revision) |
| @@ -230,13 +230,13 @@ contain a line that matches both expressions: | |||
| 230 | out.nl() | 230 | out.nl() |
| 231 | else: | 231 | else: |
| 232 | for line in r: | 232 | for line in r: |
| 233 | print line | 233 | print(line) |
| 234 | 234 | ||
| 235 | if have_match: | 235 | if have_match: |
| 236 | sys.exit(0) | 236 | sys.exit(0) |
| 237 | elif have_rev and bad_rev: | 237 | elif have_rev and bad_rev: |
| 238 | for r in opt.revision: | 238 | for r in opt.revision: |
| 239 | print >>sys.stderr, "error: can't search revision %s" % r | 239 | print("error: can't search revision %s" % r, file=sys.stderr) |
| 240 | sys.exit(1) | 240 | sys.exit(1) |
| 241 | else: | 241 | else: |
| 242 | sys.exit(1) | 242 | sys.exit(1) |
diff --git a/subcmds/help.py b/subcmds/help.py index 375d04d2..15aab7f9 100644 --- a/subcmds/help.py +++ b/subcmds/help.py | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | # See the License for the specific language governing permissions and | 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. | 14 | # limitations under the License. |
| 15 | 15 | ||
| 16 | from __future__ import print_function | ||
| 16 | import re | 17 | import re |
| 17 | import sys | 18 | import sys |
| 18 | from formatter import AbstractFormatter, DumbWriter | 19 | from formatter import AbstractFormatter, DumbWriter |
| @@ -31,10 +32,8 @@ Displays detailed usage information about a command. | |||
| 31 | """ | 32 | """ |
| 32 | 33 | ||
| 33 | def _PrintAllCommands(self): | 34 | def _PrintAllCommands(self): |
| 34 | print 'usage: repo COMMAND [ARGS]' | 35 | print('usage: repo COMMAND [ARGS]') |
| 35 | print """ | 36 | print('The complete list of recognized repo commands are:') |
| 36 | The complete list of recognized repo commands are: | ||
| 37 | """ | ||
| 38 | commandNames = self.commands.keys() | 37 | commandNames = self.commands.keys() |
| 39 | commandNames.sort() | 38 | commandNames.sort() |
| 40 | 39 | ||
| @@ -49,17 +48,14 @@ The complete list of recognized repo commands are: | |||
| 49 | summary = command.helpSummary.strip() | 48 | summary = command.helpSummary.strip() |
| 50 | except AttributeError: | 49 | except AttributeError: |
| 51 | summary = '' | 50 | summary = '' |
| 52 | print fmt % (name, summary) | 51 | print(fmt % (name, summary)) |
| 53 | print """ | 52 | print("See 'repo help <command>' for more information on a" |
| 54 | See 'repo help <command>' for more information on a specific command. | 53 | 'specific command.') |
| 55 | """ | ||
| 56 | 54 | ||
| 57 | def _PrintCommonCommands(self): | 55 | def _PrintCommonCommands(self): |
| 58 | print 'usage: repo COMMAND [ARGS]' | 56 | print('usage: repo COMMAND [ARGS]') |
| 59 | print """ | 57 | print('The most commonly used repo commands are:') |
| 60 | The most commonly used repo commands are: | 58 | commandNames = [name |
| 61 | """ | ||
| 62 | commandNames = [name | ||
| 63 | for name in self.commands.keys() | 59 | for name in self.commands.keys() |
| 64 | if self.commands[name].common] | 60 | if self.commands[name].common] |
| 65 | commandNames.sort() | 61 | commandNames.sort() |
| @@ -75,11 +71,10 @@ The most commonly used repo commands are: | |||
| 75 | summary = command.helpSummary.strip() | 71 | summary = command.helpSummary.strip() |
| 76 | except AttributeError: | 72 | except AttributeError: |
| 77 | summary = '' | 73 | summary = '' |
| 78 | print fmt % (name, summary) | 74 | print(fmt % (name, summary)) |
| 79 | print """ | 75 | print( |
| 80 | See 'repo help <command>' for more information on a specific command. | 76 | "See 'repo help <command>' for more information on a specific command.\n" |
| 81 | See 'repo help --all' for a complete list of recognized commands. | 77 | "See 'repo help --all' for a complete list of recognized commands.") |
| 82 | """ | ||
| 83 | 78 | ||
| 84 | def _PrintCommandHelp(self, cmd): | 79 | def _PrintCommandHelp(self, cmd): |
| 85 | class _Out(Coloring): | 80 | class _Out(Coloring): |
| @@ -131,7 +126,7 @@ See 'repo help --all' for a complete list of recognized commands. | |||
| 131 | 126 | ||
| 132 | p('%s', title) | 127 | p('%s', title) |
| 133 | self.nl() | 128 | self.nl() |
| 134 | p('%s', ''.ljust(len(title),section_type[0])) | 129 | p('%s', ''.ljust(len(title), section_type[0])) |
| 135 | self.nl() | 130 | self.nl() |
| 136 | continue | 131 | continue |
| 137 | 132 | ||
| @@ -162,7 +157,7 @@ See 'repo help --all' for a complete list of recognized commands. | |||
| 162 | try: | 157 | try: |
| 163 | cmd = self.commands[name] | 158 | cmd = self.commands[name] |
| 164 | except KeyError: | 159 | except KeyError: |
| 165 | print >>sys.stderr, "repo: '%s' is not a repo command." % name | 160 | print("repo: '%s' is not a repo command." % name, file=sys.stderr) |
| 166 | sys.exit(1) | 161 | sys.exit(1) |
| 167 | 162 | ||
| 168 | cmd.manifest = self.manifest | 163 | cmd.manifest = self.manifest |
diff --git a/subcmds/info.py b/subcmds/info.py new file mode 100644 index 00000000..a6eba889 --- /dev/null +++ b/subcmds/info.py | |||
| @@ -0,0 +1,195 @@ | |||
| 1 | # | ||
| 2 | # Copyright (C) 2012 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 command import PagedCommand | ||
| 17 | from color import Coloring | ||
| 18 | from error import NoSuchProjectError | ||
| 19 | from git_refs import R_M | ||
| 20 | |||
| 21 | class _Coloring(Coloring): | ||
| 22 | def __init__(self, config): | ||
| 23 | Coloring.__init__(self, config, "status") | ||
| 24 | |||
| 25 | class Info(PagedCommand): | ||
| 26 | common = True | ||
| 27 | helpSummary = "Get info on the manifest branch, current branch or unmerged branches" | ||
| 28 | helpUsage = "%prog [-dl] [-o [-b]] [<project>...]" | ||
| 29 | |||
| 30 | def _Options(self, p, show_smart=True): | ||
| 31 | p.add_option('-d', '--diff', | ||
| 32 | dest='all', action='store_true', | ||
| 33 | help="show full info and commit diff including remote branches") | ||
| 34 | p.add_option('-o', '--overview', | ||
| 35 | dest='overview', action='store_true', | ||
| 36 | help='show overview of all local commits') | ||
| 37 | p.add_option('-b', '--current-branch', | ||
| 38 | dest="current_branch", action="store_true", | ||
| 39 | help="consider only checked out branches") | ||
| 40 | p.add_option('-l', '--local-only', | ||
| 41 | dest="local", action="store_true", | ||
| 42 | help="Disable all remote operations") | ||
| 43 | |||
| 44 | |||
| 45 | def Execute(self, opt, args): | ||
| 46 | self.out = _Coloring(self.manifest.globalConfig) | ||
| 47 | self.heading = self.out.printer('heading', attr = 'bold') | ||
| 48 | self.headtext = self.out.printer('headtext', fg = 'yellow') | ||
| 49 | self.redtext = self.out.printer('redtext', fg = 'red') | ||
| 50 | self.sha = self.out.printer("sha", fg = 'yellow') | ||
| 51 | self.text = self.out.printer('text') | ||
| 52 | self.dimtext = self.out.printer('dimtext', attr = 'dim') | ||
| 53 | |||
| 54 | self.opt = opt | ||
| 55 | |||
| 56 | mergeBranch = self.manifest.manifestProject.config.GetBranch("default").merge | ||
| 57 | |||
| 58 | self.heading("Manifest branch: ") | ||
| 59 | self.headtext(self.manifest.default.revisionExpr) | ||
| 60 | self.out.nl() | ||
| 61 | self.heading("Manifest merge branch: ") | ||
| 62 | self.headtext(mergeBranch) | ||
| 63 | self.out.nl() | ||
| 64 | |||
| 65 | self.printSeparator() | ||
| 66 | |||
| 67 | if not opt.overview: | ||
| 68 | self.printDiffInfo(args) | ||
| 69 | else: | ||
| 70 | self.printCommitOverview(args) | ||
| 71 | |||
| 72 | def printSeparator(self): | ||
| 73 | self.text("----------------------------") | ||
| 74 | self.out.nl() | ||
| 75 | |||
| 76 | def printDiffInfo(self, args): | ||
| 77 | try: | ||
| 78 | projs = self.GetProjects(args) | ||
| 79 | except NoSuchProjectError: | ||
| 80 | return | ||
| 81 | |||
| 82 | for p in projs: | ||
| 83 | self.heading("Project: ") | ||
| 84 | self.headtext(p.name) | ||
| 85 | self.out.nl() | ||
| 86 | |||
| 87 | self.heading("Mount path: ") | ||
| 88 | self.headtext(p.worktree) | ||
| 89 | self.out.nl() | ||
| 90 | |||
| 91 | self.heading("Current revision: ") | ||
| 92 | self.headtext(p.revisionExpr) | ||
| 93 | self.out.nl() | ||
| 94 | |||
| 95 | localBranches = p.GetBranches().keys() | ||
| 96 | self.heading("Local Branches: ") | ||
| 97 | self.redtext(str(len(localBranches))) | ||
| 98 | if len(localBranches) > 0: | ||
| 99 | self.text(" [") | ||
| 100 | self.text(", ".join(localBranches)) | ||
| 101 | self.text("]") | ||
| 102 | self.out.nl() | ||
| 103 | |||
| 104 | if self.opt.all: | ||
| 105 | self.findRemoteLocalDiff(p) | ||
| 106 | |||
| 107 | self.printSeparator() | ||
| 108 | |||
| 109 | def findRemoteLocalDiff(self, project): | ||
| 110 | #Fetch all the latest commits | ||
| 111 | if not self.opt.local: | ||
| 112 | project.Sync_NetworkHalf(quiet=True, current_branch_only=True) | ||
| 113 | |||
| 114 | logTarget = R_M + self.manifest.manifestProject.config.GetBranch("default").merge | ||
| 115 | |||
| 116 | bareTmp = project.bare_git._bare | ||
| 117 | project.bare_git._bare = False | ||
| 118 | localCommits = project.bare_git.rev_list( | ||
| 119 | '--abbrev=8', | ||
| 120 | '--abbrev-commit', | ||
| 121 | '--pretty=oneline', | ||
| 122 | logTarget + "..", | ||
| 123 | '--') | ||
| 124 | |||
| 125 | originCommits = project.bare_git.rev_list( | ||
| 126 | '--abbrev=8', | ||
| 127 | '--abbrev-commit', | ||
| 128 | '--pretty=oneline', | ||
| 129 | ".." + logTarget, | ||
| 130 | '--') | ||
| 131 | project.bare_git._bare = bareTmp | ||
| 132 | |||
| 133 | self.heading("Local Commits: ") | ||
| 134 | self.redtext(str(len(localCommits))) | ||
| 135 | self.dimtext(" (on current branch)") | ||
| 136 | self.out.nl() | ||
| 137 | |||
| 138 | for c in localCommits: | ||
| 139 | split = c.split() | ||
| 140 | self.sha(split[0] + " ") | ||
| 141 | self.text(" ".join(split[1:])) | ||
| 142 | self.out.nl() | ||
| 143 | |||
| 144 | self.printSeparator() | ||
| 145 | |||
| 146 | self.heading("Remote Commits: ") | ||
| 147 | self.redtext(str(len(originCommits))) | ||
| 148 | self.out.nl() | ||
| 149 | |||
| 150 | for c in originCommits: | ||
| 151 | split = c.split() | ||
| 152 | self.sha(split[0] + " ") | ||
| 153 | self.text(" ".join(split[1:])) | ||
| 154 | self.out.nl() | ||
| 155 | |||
| 156 | def printCommitOverview(self, args): | ||
| 157 | all_branches = [] | ||
| 158 | for project in self.GetProjects(args): | ||
| 159 | br = [project.GetUploadableBranch(x) | ||
| 160 | for x in project.GetBranches().keys()] | ||
| 161 | br = [x for x in br if x] | ||
| 162 | if self.opt.current_branch: | ||
| 163 | br = [x for x in br if x.name == project.CurrentBranch] | ||
| 164 | all_branches.extend(br) | ||
| 165 | |||
| 166 | if not all_branches: | ||
| 167 | return | ||
| 168 | |||
| 169 | self.out.nl() | ||
| 170 | self.heading('Projects Overview') | ||
| 171 | project = None | ||
| 172 | |||
| 173 | for branch in all_branches: | ||
| 174 | if project != branch.project: | ||
| 175 | project = branch.project | ||
| 176 | self.out.nl() | ||
| 177 | self.headtext(project.relpath) | ||
| 178 | self.out.nl() | ||
| 179 | |||
| 180 | commits = branch.commits | ||
| 181 | date = branch.date | ||
| 182 | self.text('%s %-33s (%2d commit%s, %s)' % ( | ||
| 183 | branch.name == project.CurrentBranch and '*' or ' ', | ||
| 184 | branch.name, | ||
| 185 | len(commits), | ||
| 186 | len(commits) != 1 and 's' or '', | ||
| 187 | date)) | ||
| 188 | self.out.nl() | ||
| 189 | |||
| 190 | for commit in commits: | ||
| 191 | split = commit.split() | ||
| 192 | self.text('{0:38}{1} '.format('','-')) | ||
| 193 | self.sha(split[0] + " ") | ||
| 194 | self.text(" ".join(split[1:])) | ||
| 195 | self.out.nl() | ||
diff --git a/subcmds/init.py b/subcmds/init.py index b6b98076..11312601 100644 --- a/subcmds/init.py +++ b/subcmds/init.py | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | # See the License for the specific language governing permissions and | 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. | 14 | # limitations under the License. |
| 15 | 15 | ||
| 16 | from __future__ import print_function | ||
| 16 | import os | 17 | import os |
| 17 | import platform | 18 | import platform |
| 18 | import re | 19 | import re |
| @@ -117,18 +118,22 @@ to update the working directory files. | |||
| 117 | dest='config_name', action="store_true", default=False, | 118 | dest='config_name', action="store_true", default=False, |
| 118 | help='Always prompt for name/e-mail') | 119 | help='Always prompt for name/e-mail') |
| 119 | 120 | ||
| 121 | def _RegisteredEnvironmentOptions(self): | ||
| 122 | return {'REPO_MANIFEST_URL': 'manifest_url', | ||
| 123 | 'REPO_MIRROR_LOCATION': 'reference'} | ||
| 124 | |||
| 120 | def _SyncManifest(self, opt): | 125 | def _SyncManifest(self, opt): |
| 121 | m = self.manifest.manifestProject | 126 | m = self.manifest.manifestProject |
| 122 | is_new = not m.Exists | 127 | is_new = not m.Exists |
| 123 | 128 | ||
| 124 | if is_new: | 129 | if is_new: |
| 125 | if not opt.manifest_url: | 130 | if not opt.manifest_url: |
| 126 | print >>sys.stderr, 'fatal: manifest url (-u) is required.' | 131 | print('fatal: manifest url (-u) is required.', file=sys.stderr) |
| 127 | sys.exit(1) | 132 | sys.exit(1) |
| 128 | 133 | ||
| 129 | if not opt.quiet: | 134 | if not opt.quiet: |
| 130 | print >>sys.stderr, 'Get %s' \ | 135 | print('Get %s' % GitConfig.ForUser().UrlInsteadOf(opt.manifest_url), |
| 131 | % GitConfig.ForUser().UrlInsteadOf(opt.manifest_url) | 136 | file=sys.stderr) |
| 132 | m._InitGitDir() | 137 | m._InitGitDir() |
| 133 | 138 | ||
| 134 | if opt.manifest_branch: | 139 | if opt.manifest_branch: |
| @@ -147,7 +152,7 @@ to update the working directory files. | |||
| 147 | r.ResetFetch() | 152 | r.ResetFetch() |
| 148 | r.Save() | 153 | r.Save() |
| 149 | 154 | ||
| 150 | groups = re.split('[,\s]+', opt.groups) | 155 | groups = re.split(r'[,\s]+', opt.groups) |
| 151 | all_platforms = ['linux', 'darwin'] | 156 | all_platforms = ['linux', 'darwin'] |
| 152 | platformize = lambda x: 'platform-' + x | 157 | platformize = lambda x: 'platform-' + x |
| 153 | if opt.platform == 'auto': | 158 | if opt.platform == 'auto': |
| @@ -159,7 +164,7 @@ to update the working directory files. | |||
| 159 | elif opt.platform in all_platforms: | 164 | elif opt.platform in all_platforms: |
| 160 | groups.extend(platformize(opt.platform)) | 165 | groups.extend(platformize(opt.platform)) |
| 161 | elif opt.platform != 'none': | 166 | elif opt.platform != 'none': |
| 162 | print >>sys.stderr, 'fatal: invalid platform flag' | 167 | print('fatal: invalid platform flag', file=sys.stderr) |
| 163 | sys.exit(1) | 168 | sys.exit(1) |
| 164 | 169 | ||
| 165 | groups = [x for x in groups if x] | 170 | groups = [x for x in groups if x] |
| @@ -175,12 +180,15 @@ to update the working directory files. | |||
| 175 | if is_new: | 180 | if is_new: |
| 176 | m.config.SetString('repo.mirror', 'true') | 181 | m.config.SetString('repo.mirror', 'true') |
| 177 | else: | 182 | else: |
| 178 | print >>sys.stderr, 'fatal: --mirror not supported on existing client' | 183 | print('fatal: --mirror is only supported when initializing a new ' |
| 184 | 'workspace.', file=sys.stderr) | ||
| 185 | print('Either delete the .repo folder in this workspace, or initialize ' | ||
| 186 | 'in another location.', file=sys.stderr) | ||
| 179 | sys.exit(1) | 187 | sys.exit(1) |
| 180 | 188 | ||
| 181 | if not m.Sync_NetworkHalf(is_new=is_new): | 189 | if not m.Sync_NetworkHalf(is_new=is_new): |
| 182 | r = m.GetRemote(m.remote.name) | 190 | r = m.GetRemote(m.remote.name) |
| 183 | print >>sys.stderr, 'fatal: cannot obtain manifest %s' % r.url | 191 | print('fatal: cannot obtain manifest %s' % r.url, file=sys.stderr) |
| 184 | 192 | ||
| 185 | # Better delete the manifest git dir if we created it; otherwise next | 193 | # Better delete the manifest git dir if we created it; otherwise next |
| 186 | # time (when user fixes problems) we won't go through the "is_new" logic. | 194 | # time (when user fixes problems) we won't go through the "is_new" logic. |
| @@ -197,19 +205,19 @@ to update the working directory files. | |||
| 197 | 205 | ||
| 198 | if is_new or m.CurrentBranch is None: | 206 | if is_new or m.CurrentBranch is None: |
| 199 | if not m.StartBranch('default'): | 207 | if not m.StartBranch('default'): |
| 200 | print >>sys.stderr, 'fatal: cannot create default in manifest' | 208 | print('fatal: cannot create default in manifest', file=sys.stderr) |
| 201 | sys.exit(1) | 209 | sys.exit(1) |
| 202 | 210 | ||
| 203 | def _LinkManifest(self, name): | 211 | def _LinkManifest(self, name): |
| 204 | if not name: | 212 | if not name: |
| 205 | print >>sys.stderr, 'fatal: manifest name (-m) is required.' | 213 | print('fatal: manifest name (-m) is required.', file=sys.stderr) |
| 206 | sys.exit(1) | 214 | sys.exit(1) |
| 207 | 215 | ||
| 208 | try: | 216 | try: |
| 209 | self.manifest.Link(name) | 217 | self.manifest.Link(name) |
| 210 | except ManifestParseError as e: | 218 | except ManifestParseError as e: |
| 211 | print >>sys.stderr, "fatal: manifest '%s' not available" % name | 219 | print("fatal: manifest '%s' not available" % name, file=sys.stderr) |
| 212 | print >>sys.stderr, 'fatal: %s' % str(e) | 220 | print('fatal: %s' % str(e), file=sys.stderr) |
| 213 | sys.exit(1) | 221 | sys.exit(1) |
| 214 | 222 | ||
| 215 | def _Prompt(self, prompt, value): | 223 | def _Prompt(self, prompt, value): |
| @@ -231,24 +239,24 @@ to update the working directory files. | |||
| 231 | mp.config.SetString('user.name', gc.GetString('user.name')) | 239 | mp.config.SetString('user.name', gc.GetString('user.name')) |
| 232 | mp.config.SetString('user.email', gc.GetString('user.email')) | 240 | mp.config.SetString('user.email', gc.GetString('user.email')) |
| 233 | 241 | ||
| 234 | print '' | 242 | print() |
| 235 | print 'Your identity is: %s <%s>' % (mp.config.GetString('user.name'), | 243 | print('Your identity is: %s <%s>' % (mp.config.GetString('user.name'), |
| 236 | mp.config.GetString('user.email')) | 244 | mp.config.GetString('user.email'))) |
| 237 | print 'If you want to change this, please re-run \'repo init\' with --config-name' | 245 | print('If you want to change this, please re-run \'repo init\' with --config-name') |
| 238 | return False | 246 | return False |
| 239 | 247 | ||
| 240 | def _ConfigureUser(self): | 248 | def _ConfigureUser(self): |
| 241 | mp = self.manifest.manifestProject | 249 | mp = self.manifest.manifestProject |
| 242 | 250 | ||
| 243 | while True: | 251 | while True: |
| 244 | print '' | 252 | print() |
| 245 | name = self._Prompt('Your Name', mp.UserName) | 253 | name = self._Prompt('Your Name', mp.UserName) |
| 246 | email = self._Prompt('Your Email', mp.UserEmail) | 254 | email = self._Prompt('Your Email', mp.UserEmail) |
| 247 | 255 | ||
| 248 | print '' | 256 | print() |
| 249 | print 'Your identity is: %s <%s>' % (name, email) | 257 | print('Your identity is: %s <%s>' % (name, email)) |
| 250 | sys.stdout.write('is this correct [y/N]? ') | 258 | sys.stdout.write('is this correct [y/N]? ') |
| 251 | a = sys.stdin.readline().strip() | 259 | a = sys.stdin.readline().strip().lower() |
| 252 | if a in ('yes', 'y', 't', 'true'): | 260 | if a in ('yes', 'y', 't', 'true'): |
| 253 | break | 261 | break |
| 254 | 262 | ||
| @@ -274,17 +282,17 @@ to update the working directory files. | |||
| 274 | self._on = True | 282 | self._on = True |
| 275 | out = _Test() | 283 | out = _Test() |
| 276 | 284 | ||
| 277 | print '' | 285 | print() |
| 278 | print "Testing colorized output (for 'repo diff', 'repo status'):" | 286 | print("Testing colorized output (for 'repo diff', 'repo status'):") |
| 279 | 287 | ||
| 280 | for c in ['black','red','green','yellow','blue','magenta','cyan']: | 288 | for c in ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan']: |
| 281 | out.write(' ') | 289 | out.write(' ') |
| 282 | out.printer(fg=c)(' %-6s ', c) | 290 | out.printer(fg=c)(' %-6s ', c) |
| 283 | out.write(' ') | 291 | out.write(' ') |
| 284 | out.printer(fg='white', bg='black')(' %s ' % 'white') | 292 | out.printer(fg='white', bg='black')(' %s ' % 'white') |
| 285 | out.nl() | 293 | out.nl() |
| 286 | 294 | ||
| 287 | for c in ['bold','dim','ul','reverse']: | 295 | for c in ['bold', 'dim', 'ul', 'reverse']: |
| 288 | out.write(' ') | 296 | out.write(' ') |
| 289 | out.printer(fg='black', attr=c)(' %-6s ', c) | 297 | out.printer(fg='black', attr=c)(' %-6s ', c) |
| 290 | out.nl() | 298 | out.nl() |
| @@ -313,6 +321,23 @@ to update the working directory files. | |||
| 313 | # We store the depth in the main manifest project. | 321 | # We store the depth in the main manifest project. |
| 314 | self.manifest.manifestProject.config.SetString('repo.depth', depth) | 322 | self.manifest.manifestProject.config.SetString('repo.depth', depth) |
| 315 | 323 | ||
| 324 | def _DisplayResult(self): | ||
| 325 | if self.manifest.IsMirror: | ||
| 326 | init_type = 'mirror ' | ||
| 327 | else: | ||
| 328 | init_type = '' | ||
| 329 | |||
| 330 | print() | ||
| 331 | print('repo %shas been initialized in %s' | ||
| 332 | % (init_type, self.manifest.topdir)) | ||
| 333 | |||
| 334 | current_dir = os.getcwd() | ||
| 335 | if current_dir != self.manifest.topdir: | ||
| 336 | print('If this is not the directory in which you want to initialize ' | ||
| 337 | 'repo, please run:') | ||
| 338 | print(' rm -r %s/.repo' % self.manifest.topdir) | ||
| 339 | print('and try again.') | ||
| 340 | |||
| 316 | def Execute(self, opt, args): | 341 | def Execute(self, opt, args): |
| 317 | git_require(MIN_GIT_VERSION, fail=True) | 342 | git_require(MIN_GIT_VERSION, fail=True) |
| 318 | 343 | ||
| @@ -329,10 +354,4 @@ to update the working directory files. | |||
| 329 | 354 | ||
| 330 | self._ConfigureDepth(opt) | 355 | self._ConfigureDepth(opt) |
| 331 | 356 | ||
| 332 | if self.manifest.IsMirror: | 357 | self._DisplayResult() |
| 333 | init_type = 'mirror ' | ||
| 334 | else: | ||
| 335 | init_type = '' | ||
| 336 | |||
| 337 | print '' | ||
| 338 | print 'repo %sinitialized in %s' % (init_type, self.manifest.topdir) | ||
diff --git a/subcmds/list.py b/subcmds/list.py index 6058a755..0d5c27f7 100644 --- a/subcmds/list.py +++ b/subcmds/list.py | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | # See the License for the specific language governing permissions and | 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. | 14 | # limitations under the License. |
| 15 | 15 | ||
| 16 | from __future__ import print_function | ||
| 16 | import re | 17 | import re |
| 17 | 18 | ||
| 18 | from command import Command, MirrorSafeCommand | 19 | from command import Command, MirrorSafeCommand |
| @@ -64,7 +65,7 @@ This is similar to running: repo forall -c 'echo "$REPO_PATH : $REPO_PROJECT"'. | |||
| 64 | lines.append("%s : %s" % (_getpath(project), project.name)) | 65 | lines.append("%s : %s" % (_getpath(project), project.name)) |
| 65 | 66 | ||
| 66 | lines.sort() | 67 | lines.sort() |
| 67 | print '\n'.join(lines) | 68 | print('\n'.join(lines)) |
| 68 | 69 | ||
| 69 | def FindProjects(self, args): | 70 | def FindProjects(self, args): |
| 70 | result = [] | 71 | result = [] |
diff --git a/subcmds/manifest.py b/subcmds/manifest.py index 5592a37d..5ceeb12f 100644 --- a/subcmds/manifest.py +++ b/subcmds/manifest.py | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | # See the License for the specific language governing permissions and | 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. | 14 | # limitations under the License. |
| 15 | 15 | ||
| 16 | from __future__ import print_function | ||
| 16 | import os | 17 | import os |
| 17 | import sys | 18 | import sys |
| 18 | 19 | ||
| @@ -69,7 +70,7 @@ in a Git repository for use during future 'repo init' invocations. | |||
| 69 | peg_rev_upstream = opt.peg_rev_upstream) | 70 | peg_rev_upstream = opt.peg_rev_upstream) |
| 70 | fd.close() | 71 | fd.close() |
| 71 | if opt.output_file != '-': | 72 | if opt.output_file != '-': |
| 72 | print >>sys.stderr, 'Saved manifest to %s' % opt.output_file | 73 | print('Saved manifest to %s' % opt.output_file, file=sys.stderr) |
| 73 | 74 | ||
| 74 | def Execute(self, opt, args): | 75 | def Execute(self, opt, args): |
| 75 | if args: | 76 | if args: |
| @@ -79,6 +80,6 @@ in a Git repository for use during future 'repo init' invocations. | |||
| 79 | self._Output(opt) | 80 | self._Output(opt) |
| 80 | return | 81 | return |
| 81 | 82 | ||
| 82 | print >>sys.stderr, 'error: no operation to perform' | 83 | print('error: no operation to perform', file=sys.stderr) |
| 83 | print >>sys.stderr, 'error: see repo help manifest' | 84 | print('error: see repo help manifest', file=sys.stderr) |
| 84 | sys.exit(1) | 85 | sys.exit(1) |
diff --git a/subcmds/overview.py b/subcmds/overview.py index a509bd9a..418459ae 100644 --- a/subcmds/overview.py +++ b/subcmds/overview.py | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | # See the License for the specific language governing permissions and | 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. | 14 | # limitations under the License. |
| 15 | 15 | ||
| 16 | from __future__ import print_function | ||
| 16 | from color import Coloring | 17 | from color import Coloring |
| 17 | from command import PagedCommand | 18 | from command import PagedCommand |
| 18 | 19 | ||
| @@ -54,8 +55,11 @@ are displayed. | |||
| 54 | def __init__(self, config): | 55 | def __init__(self, config): |
| 55 | Coloring.__init__(self, config, 'status') | 56 | Coloring.__init__(self, config, 'status') |
| 56 | self.project = self.printer('header', attr='bold') | 57 | self.project = self.printer('header', attr='bold') |
| 58 | self.text = self.printer('text') | ||
| 57 | 59 | ||
| 58 | out = Report(all_branches[0].project.config) | 60 | out = Report(all_branches[0].project.config) |
| 61 | out.text("Deprecated. See repo info -o.") | ||
| 62 | out.nl() | ||
| 59 | out.project('Projects Overview') | 63 | out.project('Projects Overview') |
| 60 | out.nl() | 64 | out.nl() |
| 61 | 65 | ||
| @@ -70,11 +74,11 @@ are displayed. | |||
| 70 | 74 | ||
| 71 | commits = branch.commits | 75 | commits = branch.commits |
| 72 | date = branch.date | 76 | date = branch.date |
| 73 | print '%s %-33s (%2d commit%s, %s)' % ( | 77 | print('%s %-33s (%2d commit%s, %s)' % ( |
| 74 | branch.name == project.CurrentBranch and '*' or ' ', | 78 | branch.name == project.CurrentBranch and '*' or ' ', |
| 75 | branch.name, | 79 | branch.name, |
| 76 | len(commits), | 80 | len(commits), |
| 77 | len(commits) != 1 and 's' or ' ', | 81 | len(commits) != 1 and 's' or ' ', |
| 78 | date) | 82 | date)) |
| 79 | for commit in commits: | 83 | for commit in commits: |
| 80 | print '%-35s - %s' % ('', commit) | 84 | print('%-35s - %s' % ('', commit)) |
diff --git a/subcmds/prune.py b/subcmds/prune.py index c50a5507..39c571a4 100644 --- a/subcmds/prune.py +++ b/subcmds/prune.py | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | # See the License for the specific language governing permissions and | 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. | 14 | # limitations under the License. |
| 15 | 15 | ||
| 16 | from __future__ import print_function | ||
| 16 | from color import Coloring | 17 | from color import Coloring |
| 17 | from command import PagedCommand | 18 | from command import PagedCommand |
| 18 | 19 | ||
| @@ -51,9 +52,9 @@ class Prune(PagedCommand): | |||
| 51 | 52 | ||
| 52 | commits = branch.commits | 53 | commits = branch.commits |
| 53 | date = branch.date | 54 | date = branch.date |
| 54 | print '%s %-33s (%2d commit%s, %s)' % ( | 55 | print('%s %-33s (%2d commit%s, %s)' % ( |
| 55 | branch.name == project.CurrentBranch and '*' or ' ', | 56 | branch.name == project.CurrentBranch and '*' or ' ', |
| 56 | branch.name, | 57 | branch.name, |
| 57 | len(commits), | 58 | len(commits), |
| 58 | len(commits) != 1 and 's' or ' ', | 59 | len(commits) != 1 and 's' or ' ', |
| 59 | date) | 60 | date)) |
diff --git a/subcmds/rebase.py b/subcmds/rebase.py index a8d58cdb..06cda22c 100644 --- a/subcmds/rebase.py +++ b/subcmds/rebase.py | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | # See the License for the specific language governing permissions and | 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. | 14 | # limitations under the License. |
| 15 | 15 | ||
| 16 | from __future__ import print_function | ||
| 16 | import sys | 17 | import sys |
| 17 | 18 | ||
| 18 | from command import Command | 19 | from command import Command |
| @@ -59,14 +60,16 @@ branch but need to incorporate new upstream changes "underneath" them. | |||
| 59 | one_project = len(all_projects) == 1 | 60 | one_project = len(all_projects) == 1 |
| 60 | 61 | ||
| 61 | if opt.interactive and not one_project: | 62 | if opt.interactive and not one_project: |
| 62 | print >>sys.stderr, 'error: interactive rebase not supported with multiple projects' | 63 | print('error: interactive rebase not supported with multiple projects', |
| 64 | file=sys.stderr) | ||
| 63 | return -1 | 65 | return -1 |
| 64 | 66 | ||
| 65 | for project in all_projects: | 67 | for project in all_projects: |
| 66 | cb = project.CurrentBranch | 68 | cb = project.CurrentBranch |
| 67 | if not cb: | 69 | if not cb: |
| 68 | if one_project: | 70 | if one_project: |
| 69 | print >>sys.stderr, "error: project %s has a detatched HEAD" % project.relpath | 71 | print("error: project %s has a detatched HEAD" % project.relpath, |
| 72 | file=sys.stderr) | ||
| 70 | return -1 | 73 | return -1 |
| 71 | # ignore branches with detatched HEADs | 74 | # ignore branches with detatched HEADs |
| 72 | continue | 75 | continue |
| @@ -74,7 +77,8 @@ branch but need to incorporate new upstream changes "underneath" them. | |||
| 74 | upbranch = project.GetBranch(cb) | 77 | upbranch = project.GetBranch(cb) |
| 75 | if not upbranch.LocalMerge: | 78 | if not upbranch.LocalMerge: |
| 76 | if one_project: | 79 | if one_project: |
| 77 | print >>sys.stderr, "error: project %s does not track any remote branches" % project.relpath | 80 | print("error: project %s does not track any remote branches" |
| 81 | % project.relpath, file=sys.stderr) | ||
| 78 | return -1 | 82 | return -1 |
| 79 | # ignore branches without remotes | 83 | # ignore branches without remotes |
| 80 | continue | 84 | continue |
| @@ -101,8 +105,8 @@ branch but need to incorporate new upstream changes "underneath" them. | |||
| 101 | 105 | ||
| 102 | args.append(upbranch.LocalMerge) | 106 | args.append(upbranch.LocalMerge) |
| 103 | 107 | ||
| 104 | print >>sys.stderr, '# %s: rebasing %s -> %s' % \ | 108 | print('# %s: rebasing %s -> %s' |
| 105 | (project.relpath, cb, upbranch.LocalMerge) | 109 | % (project.relpath, cb, upbranch.LocalMerge), file=sys.stderr) |
| 106 | 110 | ||
| 107 | needs_stash = False | 111 | needs_stash = False |
| 108 | if opt.auto_stash: | 112 | if opt.auto_stash: |
diff --git a/subcmds/selfupdate.py b/subcmds/selfupdate.py index 46aa3a19..d12e08d0 100644 --- a/subcmds/selfupdate.py +++ b/subcmds/selfupdate.py | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | # See the License for the specific language governing permissions and | 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. | 14 | # limitations under the License. |
| 15 | 15 | ||
| 16 | from __future__ import print_function | ||
| 16 | from optparse import SUPPRESS_HELP | 17 | from optparse import SUPPRESS_HELP |
| 17 | import sys | 18 | import sys |
| 18 | 19 | ||
| @@ -52,7 +53,7 @@ need to be performed by an end-user. | |||
| 52 | 53 | ||
| 53 | else: | 54 | else: |
| 54 | if not rp.Sync_NetworkHalf(): | 55 | if not rp.Sync_NetworkHalf(): |
| 55 | print >>sys.stderr, "error: can't update repo" | 56 | print("error: can't update repo", file=sys.stderr) |
| 56 | sys.exit(1) | 57 | sys.exit(1) |
| 57 | 58 | ||
| 58 | rp.bare_git.gc('--auto') | 59 | rp.bare_git.gc('--auto') |
diff --git a/subcmds/stage.py b/subcmds/stage.py index 2ec48069..ff15ee0c 100644 --- a/subcmds/stage.py +++ b/subcmds/stage.py | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | # See the License for the specific language governing permissions and | 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. | 14 | # limitations under the License. |
| 15 | 15 | ||
| 16 | from __future__ import print_function | ||
| 16 | import sys | 17 | import sys |
| 17 | 18 | ||
| 18 | from color import Coloring | 19 | from color import Coloring |
| @@ -50,7 +51,7 @@ The '%prog' command stages files to prepare the next commit. | |||
| 50 | def _Interactive(self, opt, args): | 51 | def _Interactive(self, opt, args): |
| 51 | all_projects = filter(lambda x: x.IsDirty(), self.GetProjects(args)) | 52 | all_projects = filter(lambda x: x.IsDirty(), self.GetProjects(args)) |
| 52 | if not all_projects: | 53 | if not all_projects: |
| 53 | print >>sys.stderr,'no projects have uncommitted modifications' | 54 | print('no projects have uncommitted modifications', file=sys.stderr) |
| 54 | return | 55 | return |
| 55 | 56 | ||
| 56 | out = _ProjectList(self.manifest.manifestProject.config) | 57 | out = _ProjectList(self.manifest.manifestProject.config) |
| @@ -58,7 +59,7 @@ The '%prog' command stages files to prepare the next commit. | |||
| 58 | out.header(' %s', 'project') | 59 | out.header(' %s', 'project') |
| 59 | out.nl() | 60 | out.nl() |
| 60 | 61 | ||
| 61 | for i in xrange(0, len(all_projects)): | 62 | for i in range(len(all_projects)): |
| 62 | p = all_projects[i] | 63 | p = all_projects[i] |
| 63 | out.write('%3d: %s', i + 1, p.relpath + '/') | 64 | out.write('%3d: %s', i + 1, p.relpath + '/') |
| 64 | out.nl() | 65 | out.nl() |
| @@ -101,7 +102,7 @@ The '%prog' command stages files to prepare the next commit. | |||
| 101 | if len(p) == 1: | 102 | if len(p) == 1: |
| 102 | _AddI(p[0]) | 103 | _AddI(p[0]) |
| 103 | continue | 104 | continue |
| 104 | print 'Bye.' | 105 | print('Bye.') |
| 105 | 106 | ||
| 106 | def _AddI(project): | 107 | def _AddI(project): |
| 107 | p = GitCommand(project, ['add', '--interactive'], bare=False) | 108 | p = GitCommand(project, ['add', '--interactive'], bare=False) |
diff --git a/subcmds/start.py b/subcmds/start.py index be645314..2d723fc2 100644 --- a/subcmds/start.py +++ b/subcmds/start.py | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | # See the License for the specific language governing permissions and | 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. | 14 | # limitations under the License. |
| 15 | 15 | ||
| 16 | from __future__ import print_function | ||
| 16 | import sys | 17 | import sys |
| 17 | from command import Command | 18 | from command import Command |
| 18 | from git_config import IsId | 19 | from git_config import IsId |
| @@ -41,7 +42,7 @@ revision specified in the manifest. | |||
| 41 | 42 | ||
| 42 | nb = args[0] | 43 | nb = args[0] |
| 43 | if not git.check_ref_format('heads/%s' % nb): | 44 | if not git.check_ref_format('heads/%s' % nb): |
| 44 | print >>sys.stderr, "error: '%s' is not a valid name" % nb | 45 | print("error: '%s' is not a valid name" % nb, file=sys.stderr) |
| 45 | sys.exit(1) | 46 | sys.exit(1) |
| 46 | 47 | ||
| 47 | err = [] | 48 | err = [] |
| @@ -49,7 +50,7 @@ revision specified in the manifest. | |||
| 49 | if not opt.all: | 50 | if not opt.all: |
| 50 | projects = args[1:] | 51 | projects = args[1:] |
| 51 | if len(projects) < 1: | 52 | if len(projects) < 1: |
| 52 | print >>sys.stderr, "error: at least one project must be specified" | 53 | print("error: at least one project must be specified", file=sys.stderr) |
| 53 | sys.exit(1) | 54 | sys.exit(1) |
| 54 | 55 | ||
| 55 | all_projects = self.GetProjects(projects) | 56 | all_projects = self.GetProjects(projects) |
| @@ -67,7 +68,6 @@ revision specified in the manifest. | |||
| 67 | 68 | ||
| 68 | if err: | 69 | if err: |
| 69 | for p in err: | 70 | for p in err: |
| 70 | print >>sys.stderr,\ | 71 | print("error: %s/: cannot start %s" % (p.relpath, nb), |
| 71 | "error: %s/: cannot start %s" \ | 72 | file=sys.stderr) |
| 72 | % (p.relpath, nb) | ||
| 73 | sys.exit(1) | 73 | sys.exit(1) |
diff --git a/subcmds/status.py b/subcmds/status.py index 7611621e..cce00c81 100644 --- a/subcmds/status.py +++ b/subcmds/status.py | |||
| @@ -20,10 +20,14 @@ try: | |||
| 20 | except ImportError: | 20 | except ImportError: |
| 21 | import dummy_threading as _threading | 21 | import dummy_threading as _threading |
| 22 | 22 | ||
| 23 | import glob | ||
| 23 | import itertools | 24 | import itertools |
| 25 | import os | ||
| 24 | import sys | 26 | import sys |
| 25 | import StringIO | 27 | import StringIO |
| 26 | 28 | ||
| 29 | from color import Coloring | ||
| 30 | |||
| 27 | class Status(PagedCommand): | 31 | class Status(PagedCommand): |
| 28 | common = True | 32 | common = True |
| 29 | helpSummary = "Show the working tree status" | 33 | helpSummary = "Show the working tree status" |
| @@ -39,6 +43,13 @@ is a difference between these three states. | |||
| 39 | The -j/--jobs option can be used to run multiple status queries | 43 | The -j/--jobs option can be used to run multiple status queries |
| 40 | in parallel. | 44 | in parallel. |
| 41 | 45 | ||
| 46 | The -o/--orphans option can be used to show objects that are in | ||
| 47 | the working directory, but not associated with a repo project. | ||
| 48 | This includes unmanaged top-level files and directories, but also | ||
| 49 | includes deeper items. For example, if dir/subdir/proj1 and | ||
| 50 | dir/subdir/proj2 are repo projects, dir/subdir/proj3 will be shown | ||
| 51 | if it is not known to repo. | ||
| 52 | |||
| 42 | Status Display | 53 | Status Display |
| 43 | -------------- | 54 | -------------- |
| 44 | 55 | ||
| @@ -76,6 +87,9 @@ the following meanings: | |||
| 76 | p.add_option('-j', '--jobs', | 87 | p.add_option('-j', '--jobs', |
| 77 | dest='jobs', action='store', type='int', default=2, | 88 | dest='jobs', action='store', type='int', default=2, |
| 78 | help="number of projects to check simultaneously") | 89 | help="number of projects to check simultaneously") |
| 90 | p.add_option('-o', '--orphans', | ||
| 91 | dest='orphans', action='store_true', | ||
| 92 | help="include objects in working directory outside of repo projects") | ||
| 79 | 93 | ||
| 80 | def _StatusHelper(self, project, clean_counter, sem, output): | 94 | def _StatusHelper(self, project, clean_counter, sem, output): |
| 81 | """Obtains the status for a specific project. | 95 | """Obtains the status for a specific project. |
| @@ -97,6 +111,22 @@ the following meanings: | |||
| 97 | finally: | 111 | finally: |
| 98 | sem.release() | 112 | sem.release() |
| 99 | 113 | ||
| 114 | def _FindOrphans(self, dirs, proj_dirs, proj_dirs_parents, outstring): | ||
| 115 | """find 'dirs' that are present in 'proj_dirs_parents' but not in 'proj_dirs'""" | ||
| 116 | status_header = ' --\t' | ||
| 117 | for item in dirs: | ||
| 118 | if not os.path.isdir(item): | ||
| 119 | outstring.write(''.join([status_header, item])) | ||
| 120 | continue | ||
| 121 | if item in proj_dirs: | ||
| 122 | continue | ||
| 123 | if item in proj_dirs_parents: | ||
| 124 | self._FindOrphans(glob.glob('%s/.*' % item) + \ | ||
| 125 | glob.glob('%s/*' % item), \ | ||
| 126 | proj_dirs, proj_dirs_parents, outstring) | ||
| 127 | continue | ||
| 128 | outstring.write(''.join([status_header, item, '/'])) | ||
| 129 | |||
| 100 | def Execute(self, opt, args): | 130 | def Execute(self, opt, args): |
| 101 | all_projects = self.GetProjects(args) | 131 | all_projects = self.GetProjects(args) |
| 102 | counter = itertools.count() | 132 | counter = itertools.count() |
| @@ -129,4 +159,46 @@ the following meanings: | |||
| 129 | output.dump(sys.stdout) | 159 | output.dump(sys.stdout) |
| 130 | output.close() | 160 | output.close() |
| 131 | if len(all_projects) == counter.next(): | 161 | if len(all_projects) == counter.next(): |
| 132 | print 'nothing to commit (working directory clean)' | 162 | print('nothing to commit (working directory clean)') |
| 163 | |||
| 164 | if opt.orphans: | ||
| 165 | proj_dirs = set() | ||
| 166 | proj_dirs_parents = set() | ||
| 167 | for project in self.GetProjects(None, missing_ok=True): | ||
| 168 | proj_dirs.add(project.relpath) | ||
| 169 | (head, _tail) = os.path.split(project.relpath) | ||
| 170 | while head != "": | ||
| 171 | proj_dirs_parents.add(head) | ||
| 172 | (head, _tail) = os.path.split(head) | ||
| 173 | proj_dirs.add('.repo') | ||
| 174 | |||
| 175 | class StatusColoring(Coloring): | ||
| 176 | def __init__(self, config): | ||
| 177 | Coloring.__init__(self, config, 'status') | ||
| 178 | self.project = self.printer('header', attr = 'bold') | ||
| 179 | self.untracked = self.printer('untracked', fg = 'red') | ||
| 180 | |||
| 181 | orig_path = os.getcwd() | ||
| 182 | try: | ||
| 183 | os.chdir(self.manifest.topdir) | ||
| 184 | |||
| 185 | outstring = StringIO.StringIO() | ||
| 186 | self._FindOrphans(glob.glob('.*') + \ | ||
| 187 | glob.glob('*'), \ | ||
| 188 | proj_dirs, proj_dirs_parents, outstring) | ||
| 189 | |||
| 190 | if outstring.buflist: | ||
| 191 | output = StatusColoring(self.manifest.globalConfig) | ||
| 192 | output.project('Objects not within a project (orphans)') | ||
| 193 | output.nl() | ||
| 194 | for entry in outstring.buflist: | ||
| 195 | output.untracked(entry) | ||
| 196 | output.nl() | ||
| 197 | else: | ||
| 198 | print('No orphan files or directories') | ||
| 199 | |||
| 200 | outstring.close() | ||
| 201 | |||
| 202 | finally: | ||
| 203 | # Restore CWD. | ||
| 204 | os.chdir(orig_path) | ||
diff --git a/subcmds/sync.py b/subcmds/sync.py index d4637d0c..228a279a 100644 --- a/subcmds/sync.py +++ b/subcmds/sync.py | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | # See the License for the specific language governing permissions and | 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. | 14 | # limitations under the License. |
| 15 | 15 | ||
| 16 | from __future__ import print_function | ||
| 16 | import netrc | 17 | import netrc |
| 17 | from optparse import SUPPRESS_HELP | 18 | from optparse import SUPPRESS_HELP |
| 18 | import os | 19 | import os |
| @@ -44,13 +45,13 @@ try: | |||
| 44 | except ImportError: | 45 | except ImportError: |
| 45 | multiprocessing = None | 46 | multiprocessing = None |
| 46 | 47 | ||
| 47 | from git_command import GIT | 48 | from git_command import GIT, git_require |
| 48 | from git_refs import R_HEADS, HEAD | 49 | from git_refs import R_HEADS, HEAD |
| 49 | from main import WrapperModule | 50 | from main import WrapperModule |
| 50 | from project import Project | 51 | from project import Project |
| 51 | from project import RemoteSpec | 52 | from project import RemoteSpec |
| 52 | from command import Command, MirrorSafeCommand | 53 | from command import Command, MirrorSafeCommand |
| 53 | from error import RepoChangedException, GitError | 54 | from error import RepoChangedException, GitError, ManifestParseError |
| 54 | from project import SyncBuffer | 55 | from project import SyncBuffer |
| 55 | from progress import Progress | 56 | from progress import Progress |
| 56 | 57 | ||
| @@ -113,6 +114,9 @@ resumeable bundle file on a content delivery network. This | |||
| 113 | may be necessary if there are problems with the local Python | 114 | may be necessary if there are problems with the local Python |
| 114 | HTTP client or proxy configuration, but the Git binary works. | 115 | HTTP client or proxy configuration, but the Git binary works. |
| 115 | 116 | ||
| 117 | The --fetch-submodules option enables fetching Git submodules | ||
| 118 | of a project from server. | ||
| 119 | |||
| 116 | SSH Connections | 120 | SSH Connections |
| 117 | --------------- | 121 | --------------- |
| 118 | 122 | ||
| @@ -144,27 +148,30 @@ later is required to fix a server side protocol bug. | |||
| 144 | """ | 148 | """ |
| 145 | 149 | ||
| 146 | def _Options(self, p, show_smart=True): | 150 | def _Options(self, p, show_smart=True): |
| 147 | self.jobs = self.manifest.default.sync_j | 151 | try: |
| 152 | self.jobs = self.manifest.default.sync_j | ||
| 153 | except ManifestParseError: | ||
| 154 | self.jobs = 1 | ||
| 148 | 155 | ||
| 149 | p.add_option('-f', '--force-broken', | 156 | p.add_option('-f', '--force-broken', |
| 150 | dest='force_broken', action='store_true', | 157 | dest='force_broken', action='store_true', |
| 151 | help="continue sync even if a project fails to sync") | 158 | help="continue sync even if a project fails to sync") |
| 152 | p.add_option('-l','--local-only', | 159 | p.add_option('-l', '--local-only', |
| 153 | dest='local_only', action='store_true', | 160 | dest='local_only', action='store_true', |
| 154 | help="only update working tree, don't fetch") | 161 | help="only update working tree, don't fetch") |
| 155 | p.add_option('-n','--network-only', | 162 | p.add_option('-n', '--network-only', |
| 156 | dest='network_only', action='store_true', | 163 | dest='network_only', action='store_true', |
| 157 | help="fetch only, don't update working tree") | 164 | help="fetch only, don't update working tree") |
| 158 | p.add_option('-d','--detach', | 165 | p.add_option('-d', '--detach', |
| 159 | dest='detach_head', action='store_true', | 166 | dest='detach_head', action='store_true', |
| 160 | help='detach projects back to manifest revision') | 167 | help='detach projects back to manifest revision') |
| 161 | p.add_option('-c','--current-branch', | 168 | p.add_option('-c', '--current-branch', |
| 162 | dest='current_branch_only', action='store_true', | 169 | dest='current_branch_only', action='store_true', |
| 163 | help='fetch only current branch from server') | 170 | help='fetch only current branch from server') |
| 164 | p.add_option('-q','--quiet', | 171 | p.add_option('-q', '--quiet', |
| 165 | dest='quiet', action='store_true', | 172 | dest='quiet', action='store_true', |
| 166 | help='be more quiet') | 173 | help='be more quiet') |
| 167 | p.add_option('-j','--jobs', | 174 | p.add_option('-j', '--jobs', |
| 168 | dest='jobs', action='store', type='int', | 175 | dest='jobs', action='store', type='int', |
| 169 | help="projects to fetch simultaneously (default %d)" % self.jobs) | 176 | help="projects to fetch simultaneously (default %d)" % self.jobs) |
| 170 | p.add_option('-m', '--manifest-name', | 177 | p.add_option('-m', '--manifest-name', |
| @@ -173,6 +180,15 @@ later is required to fix a server side protocol bug. | |||
| 173 | p.add_option('--no-clone-bundle', | 180 | p.add_option('--no-clone-bundle', |
| 174 | dest='no_clone_bundle', action='store_true', | 181 | dest='no_clone_bundle', action='store_true', |
| 175 | help='disable use of /clone.bundle on HTTP/HTTPS') | 182 | help='disable use of /clone.bundle on HTTP/HTTPS') |
| 183 | p.add_option('-u', '--manifest-server-username', action='store', | ||
| 184 | dest='manifest_server_username', | ||
| 185 | help='username to authenticate with the manifest server') | ||
| 186 | p.add_option('-p', '--manifest-server-password', action='store', | ||
| 187 | dest='manifest_server_password', | ||
| 188 | help='password to authenticate with the manifest server') | ||
| 189 | p.add_option('--fetch-submodules', | ||
| 190 | dest='fetch_submodules', action='store_true', | ||
| 191 | help='fetch submodules from server') | ||
| 176 | if show_smart: | 192 | if show_smart: |
| 177 | p.add_option('-s', '--smart-sync', | 193 | p.add_option('-s', '--smart-sync', |
| 178 | dest='smart_sync', action='store_true', | 194 | dest='smart_sync', action='store_true', |
| @@ -180,12 +196,6 @@ later is required to fix a server side protocol bug. | |||
| 180 | p.add_option('-t', '--smart-tag', | 196 | p.add_option('-t', '--smart-tag', |
| 181 | dest='smart_tag', action='store', | 197 | dest='smart_tag', action='store', |
| 182 | help='smart sync using manifest from a known tag') | 198 | help='smart sync using manifest from a known tag') |
| 183 | p.add_option('-u', '--manifest-server-username', action='store', | ||
| 184 | dest='manifest_server_username', | ||
| 185 | help='username to authenticate with the manifest server') | ||
| 186 | p.add_option('-p', '--manifest-server-password', action='store', | ||
| 187 | dest='manifest_server_password', | ||
| 188 | help='password to authenticate with the manifest server') | ||
| 189 | 199 | ||
| 190 | g = p.add_option_group('repo Version options') | 200 | g = p.add_option_group('repo Version options') |
| 191 | g.add_option('--no-repo-verify', | 201 | g.add_option('--no-repo-verify', |
| @@ -196,61 +206,62 @@ later is required to fix a server side protocol bug. | |||
| 196 | help=SUPPRESS_HELP) | 206 | help=SUPPRESS_HELP) |
| 197 | 207 | ||
| 198 | def _FetchHelper(self, opt, project, lock, fetched, pm, sem, err_event): | 208 | def _FetchHelper(self, opt, project, lock, fetched, pm, sem, err_event): |
| 199 | """Main function of the fetch threads when jobs are > 1. | 209 | """Main function of the fetch threads when jobs are > 1. |
| 200 | 210 | ||
| 201 | Args: | 211 | Args: |
| 202 | opt: Program options returned from optparse. See _Options(). | 212 | opt: Program options returned from optparse. See _Options(). |
| 203 | project: Project object for the project to fetch. | 213 | project: Project object for the project to fetch. |
| 204 | lock: Lock for accessing objects that are shared amongst multiple | 214 | lock: Lock for accessing objects that are shared amongst multiple |
| 205 | _FetchHelper() threads. | 215 | _FetchHelper() threads. |
| 206 | fetched: set object that we will add project.gitdir to when we're done | 216 | fetched: set object that we will add project.gitdir to when we're done |
| 207 | (with our lock held). | 217 | (with our lock held). |
| 208 | pm: Instance of a Project object. We will call pm.update() (with our | 218 | pm: Instance of a Project object. We will call pm.update() (with our |
| 209 | lock held). | 219 | lock held). |
| 210 | sem: We'll release() this semaphore when we exit so that another thread | 220 | sem: We'll release() this semaphore when we exit so that another thread |
| 211 | can be started up. | 221 | can be started up. |
| 212 | err_event: We'll set this event in the case of an error (after printing | 222 | err_event: We'll set this event in the case of an error (after printing |
| 213 | out info about the error). | 223 | out info about the error). |
| 214 | """ | 224 | """ |
| 215 | # We'll set to true once we've locked the lock. | 225 | # We'll set to true once we've locked the lock. |
| 216 | did_lock = False | 226 | did_lock = False |
| 217 | 227 | ||
| 218 | # Encapsulate everything in a try/except/finally so that: | 228 | # Encapsulate everything in a try/except/finally so that: |
| 219 | # - We always set err_event in the case of an exception. | 229 | # - We always set err_event in the case of an exception. |
| 220 | # - We always make sure we call sem.release(). | 230 | # - We always make sure we call sem.release(). |
| 221 | # - We always make sure we unlock the lock if we locked it. | 231 | # - We always make sure we unlock the lock if we locked it. |
| 232 | try: | ||
| 222 | try: | 233 | try: |
| 223 | try: | 234 | start = time.time() |
| 224 | start = time.time() | 235 | success = project.Sync_NetworkHalf( |
| 225 | success = project.Sync_NetworkHalf( | 236 | quiet=opt.quiet, |
| 226 | quiet=opt.quiet, | 237 | current_branch_only=opt.current_branch_only, |
| 227 | current_branch_only=opt.current_branch_only, | 238 | clone_bundle=not opt.no_clone_bundle) |
| 228 | clone_bundle=not opt.no_clone_bundle) | 239 | self._fetch_times.Set(project, time.time() - start) |
| 229 | self._fetch_times.Set(project, time.time() - start) | 240 | |
| 230 | 241 | # Lock around all the rest of the code, since printing, updating a set | |
| 231 | # Lock around all the rest of the code, since printing, updating a set | 242 | # and Progress.update() are not thread safe. |
| 232 | # and Progress.update() are not thread safe. | 243 | lock.acquire() |
| 233 | lock.acquire() | 244 | did_lock = True |
| 234 | did_lock = True | 245 | |
| 235 | 246 | if not success: | |
| 236 | if not success: | 247 | print('error: Cannot fetch %s' % project.name, file=sys.stderr) |
| 237 | print >>sys.stderr, 'error: Cannot fetch %s' % project.name | 248 | if opt.force_broken: |
| 238 | if opt.force_broken: | 249 | print('warn: --force-broken, continuing to sync', |
| 239 | print >>sys.stderr, 'warn: --force-broken, continuing to sync' | 250 | file=sys.stderr) |
| 240 | else: | 251 | else: |
| 241 | raise _FetchError() | 252 | raise _FetchError() |
| 242 | 253 | ||
| 243 | fetched.add(project.gitdir) | 254 | fetched.add(project.gitdir) |
| 244 | pm.update() | 255 | pm.update() |
| 245 | except _FetchError: | 256 | except _FetchError: |
| 246 | err_event.set() | 257 | err_event.set() |
| 247 | except: | 258 | except: |
| 248 | err_event.set() | 259 | err_event.set() |
| 249 | raise | 260 | raise |
| 250 | finally: | 261 | finally: |
| 251 | if did_lock: | 262 | if did_lock: |
| 252 | lock.release() | 263 | lock.release() |
| 253 | sem.release() | 264 | sem.release() |
| 254 | 265 | ||
| 255 | def _Fetch(self, projects, opt): | 266 | def _Fetch(self, projects, opt): |
| 256 | fetched = set() | 267 | fetched = set() |
| @@ -265,9 +276,9 @@ later is required to fix a server side protocol bug. | |||
| 265 | clone_bundle=not opt.no_clone_bundle): | 276 | clone_bundle=not opt.no_clone_bundle): |
| 266 | fetched.add(project.gitdir) | 277 | fetched.add(project.gitdir) |
| 267 | else: | 278 | else: |
| 268 | print >>sys.stderr, 'error: Cannot fetch %s' % project.name | 279 | print('error: Cannot fetch %s' % project.name, file=sys.stderr) |
| 269 | if opt.force_broken: | 280 | if opt.force_broken: |
| 270 | print >>sys.stderr, 'warn: --force-broken, continuing to sync' | 281 | print('warn: --force-broken, continuing to sync', file=sys.stderr) |
| 271 | else: | 282 | else: |
| 272 | sys.exit(1) | 283 | sys.exit(1) |
| 273 | else: | 284 | else: |
| @@ -300,7 +311,7 @@ later is required to fix a server side protocol bug. | |||
| 300 | 311 | ||
| 301 | # If we saw an error, exit with code 1 so that other scripts can check. | 312 | # If we saw an error, exit with code 1 so that other scripts can check. |
| 302 | if err_event.isSet(): | 313 | if err_event.isSet(): |
| 303 | print >>sys.stderr, '\nerror: Exited sync due to fetch errors' | 314 | print('\nerror: Exited sync due to fetch errors', file=sys.stderr) |
| 304 | sys.exit(1) | 315 | sys.exit(1) |
| 305 | 316 | ||
| 306 | pm.end() | 317 | pm.end() |
| @@ -310,7 +321,8 @@ later is required to fix a server side protocol bug. | |||
| 310 | return fetched | 321 | return fetched |
| 311 | 322 | ||
| 312 | def _GCProjects(self, projects): | 323 | def _GCProjects(self, projects): |
| 313 | if multiprocessing: | 324 | has_dash_c = git_require((1, 7, 2)) |
| 325 | if multiprocessing and has_dash_c: | ||
| 314 | cpu_count = multiprocessing.cpu_count() | 326 | cpu_count = multiprocessing.cpu_count() |
| 315 | else: | 327 | else: |
| 316 | cpu_count = 1 | 328 | cpu_count = 1 |
| @@ -352,7 +364,7 @@ later is required to fix a server side protocol bug. | |||
| 352 | t.join() | 364 | t.join() |
| 353 | 365 | ||
| 354 | if err_event.isSet(): | 366 | if err_event.isSet(): |
| 355 | print >>sys.stderr, '\nerror: Exited sync due to gc errors' | 367 | print('\nerror: Exited sync due to gc errors', file=sys.stderr) |
| 356 | sys.exit(1) | 368 | sys.exit(1) |
| 357 | 369 | ||
| 358 | def UpdateProjectList(self): | 370 | def UpdateProjectList(self): |
| @@ -376,34 +388,36 @@ later is required to fix a server side protocol bug. | |||
| 376 | if path not in new_project_paths: | 388 | if path not in new_project_paths: |
| 377 | # If the path has already been deleted, we don't need to do it | 389 | # If the path has already been deleted, we don't need to do it |
| 378 | if os.path.exists(self.manifest.topdir + '/' + path): | 390 | if os.path.exists(self.manifest.topdir + '/' + path): |
| 379 | project = Project( | 391 | project = Project( |
| 380 | manifest = self.manifest, | 392 | manifest = self.manifest, |
| 381 | name = path, | 393 | name = path, |
| 382 | remote = RemoteSpec('origin'), | 394 | remote = RemoteSpec('origin'), |
| 383 | gitdir = os.path.join(self.manifest.topdir, | 395 | gitdir = os.path.join(self.manifest.topdir, |
| 384 | path, '.git'), | 396 | path, '.git'), |
| 385 | worktree = os.path.join(self.manifest.topdir, path), | 397 | worktree = os.path.join(self.manifest.topdir, path), |
| 386 | relpath = path, | 398 | relpath = path, |
| 387 | revisionExpr = 'HEAD', | 399 | revisionExpr = 'HEAD', |
| 388 | revisionId = None, | 400 | revisionId = None, |
| 389 | groups = None) | 401 | groups = None) |
| 390 | 402 | ||
| 391 | if project.IsDirty(): | 403 | if project.IsDirty(): |
| 392 | print >>sys.stderr, 'error: Cannot remove project "%s": \ | 404 | print('error: Cannot remove project "%s": uncommitted changes' |
| 393 | uncommitted changes are present' % project.relpath | 405 | 'are present' % project.relpath, file=sys.stderr) |
| 394 | print >>sys.stderr, ' commit changes, then run sync again' | 406 | print(' commit changes, then run sync again', |
| 395 | return -1 | 407 | file=sys.stderr) |
| 396 | else: | 408 | return -1 |
| 397 | print >>sys.stderr, 'Deleting obsolete path %s' % project.worktree | 409 | else: |
| 398 | shutil.rmtree(project.worktree) | 410 | print('Deleting obsolete path %s' % project.worktree, |
| 399 | # Try deleting parent subdirs if they are empty | 411 | file=sys.stderr) |
| 400 | project_dir = os.path.dirname(project.worktree) | 412 | shutil.rmtree(project.worktree) |
| 401 | while project_dir != self.manifest.topdir: | 413 | # Try deleting parent subdirs if they are empty |
| 402 | try: | 414 | project_dir = os.path.dirname(project.worktree) |
| 403 | os.rmdir(project_dir) | 415 | while project_dir != self.manifest.topdir: |
| 404 | except OSError: | 416 | try: |
| 405 | break | 417 | os.rmdir(project_dir) |
| 406 | project_dir = os.path.dirname(project_dir) | 418 | except OSError: |
| 419 | break | ||
| 420 | project_dir = os.path.dirname(project_dir) | ||
| 407 | 421 | ||
| 408 | new_project_paths.sort() | 422 | new_project_paths.sort() |
| 409 | fd = open(file_path, 'w') | 423 | fd = open(file_path, 'w') |
| @@ -422,24 +436,24 @@ uncommitted changes are present' % project.relpath | |||
| 422 | self.jobs = min(self.jobs, (soft_limit - 5) / 3) | 436 | self.jobs = min(self.jobs, (soft_limit - 5) / 3) |
| 423 | 437 | ||
| 424 | if opt.network_only and opt.detach_head: | 438 | if opt.network_only and opt.detach_head: |
| 425 | print >>sys.stderr, 'error: cannot combine -n and -d' | 439 | print('error: cannot combine -n and -d', file=sys.stderr) |
| 426 | sys.exit(1) | 440 | sys.exit(1) |
| 427 | if opt.network_only and opt.local_only: | 441 | if opt.network_only and opt.local_only: |
| 428 | print >>sys.stderr, 'error: cannot combine -n and -l' | 442 | print('error: cannot combine -n and -l', file=sys.stderr) |
| 429 | sys.exit(1) | 443 | sys.exit(1) |
| 430 | if opt.manifest_name and opt.smart_sync: | 444 | if opt.manifest_name and opt.smart_sync: |
| 431 | print >>sys.stderr, 'error: cannot combine -m and -s' | 445 | print('error: cannot combine -m and -s', file=sys.stderr) |
| 432 | sys.exit(1) | 446 | sys.exit(1) |
| 433 | if opt.manifest_name and opt.smart_tag: | 447 | if opt.manifest_name and opt.smart_tag: |
| 434 | print >>sys.stderr, 'error: cannot combine -m and -t' | 448 | print('error: cannot combine -m and -t', file=sys.stderr) |
| 435 | sys.exit(1) | 449 | sys.exit(1) |
| 436 | if opt.manifest_server_username or opt.manifest_server_password: | 450 | if opt.manifest_server_username or opt.manifest_server_password: |
| 437 | if not (opt.smart_sync or opt.smart_tag): | 451 | if not (opt.smart_sync or opt.smart_tag): |
| 438 | print >>sys.stderr, 'error: -u and -p may only be combined with ' \ | 452 | print('error: -u and -p may only be combined with -s or -t', |
| 439 | '-s or -t' | 453 | file=sys.stderr) |
| 440 | sys.exit(1) | 454 | sys.exit(1) |
| 441 | if None in [opt.manifest_server_username, opt.manifest_server_password]: | 455 | if None in [opt.manifest_server_username, opt.manifest_server_password]: |
| 442 | print >>sys.stderr, 'error: both -u and -p must be given' | 456 | print('error: both -u and -p must be given', file=sys.stderr) |
| 443 | sys.exit(1) | 457 | sys.exit(1) |
| 444 | 458 | ||
| 445 | if opt.manifest_name: | 459 | if opt.manifest_name: |
| @@ -447,8 +461,8 @@ uncommitted changes are present' % project.relpath | |||
| 447 | 461 | ||
| 448 | if opt.smart_sync or opt.smart_tag: | 462 | if opt.smart_sync or opt.smart_tag: |
| 449 | if not self.manifest.manifest_server: | 463 | if not self.manifest.manifest_server: |
| 450 | print >>sys.stderr, \ | 464 | print('error: cannot smart sync: no manifest server defined in' |
| 451 | 'error: cannot smart sync: no manifest server defined in manifest' | 465 | 'manifest', file=sys.stderr) |
| 452 | sys.exit(1) | 466 | sys.exit(1) |
| 453 | 467 | ||
| 454 | manifest_server = self.manifest.manifest_server | 468 | manifest_server = self.manifest.manifest_server |
| @@ -463,7 +477,8 @@ uncommitted changes are present' % project.relpath | |||
| 463 | try: | 477 | try: |
| 464 | info = netrc.netrc() | 478 | info = netrc.netrc() |
| 465 | except IOError: | 479 | except IOError: |
| 466 | print >>sys.stderr, '.netrc file does not exist or could not be opened' | 480 | print('.netrc file does not exist or could not be opened', |
| 481 | file=sys.stderr) | ||
| 467 | else: | 482 | else: |
| 468 | try: | 483 | try: |
| 469 | parse_result = urlparse.urlparse(manifest_server) | 484 | parse_result = urlparse.urlparse(manifest_server) |
| @@ -473,10 +488,10 @@ uncommitted changes are present' % project.relpath | |||
| 473 | except TypeError: | 488 | except TypeError: |
| 474 | # TypeError is raised when the given hostname is not present | 489 | # TypeError is raised when the given hostname is not present |
| 475 | # in the .netrc file. | 490 | # in the .netrc file. |
| 476 | print >>sys.stderr, 'No credentials found for %s in .netrc' % \ | 491 | print('No credentials found for %s in .netrc' |
| 477 | parse_result.hostname | 492 | % parse_result.hostname, file=sys.stderr) |
| 478 | except netrc.NetrcParseError as e: | 493 | except netrc.NetrcParseError as e: |
| 479 | print >>sys.stderr, 'Error parsing .netrc file: %s' % e | 494 | print('Error parsing .netrc file: %s' % e, file=sys.stderr) |
| 480 | 495 | ||
| 481 | if (username and password): | 496 | if (username and password): |
| 482 | manifest_server = manifest_server.replace('://', '://%s:%s@' % | 497 | manifest_server = manifest_server.replace('://', '://%s:%s@' % |
| @@ -515,20 +530,21 @@ uncommitted changes are present' % project.relpath | |||
| 515 | finally: | 530 | finally: |
| 516 | f.close() | 531 | f.close() |
| 517 | except IOError: | 532 | except IOError: |
| 518 | print >>sys.stderr, 'error: cannot write manifest to %s' % \ | 533 | print('error: cannot write manifest to %s' % manifest_path, |
| 519 | manifest_path | 534 | file=sys.stderr) |
| 520 | sys.exit(1) | 535 | sys.exit(1) |
| 521 | self.manifest.Override(manifest_name) | 536 | self.manifest.Override(manifest_name) |
| 522 | else: | 537 | else: |
| 523 | print >>sys.stderr, 'error: %s' % manifest_str | 538 | print('error: %s' % manifest_str, file=sys.stderr) |
| 524 | sys.exit(1) | 539 | sys.exit(1) |
| 525 | except (socket.error, IOError, xmlrpclib.Fault) as e: | 540 | except (socket.error, IOError, xmlrpclib.Fault) as e: |
| 526 | print >>sys.stderr, 'error: cannot connect to manifest server %s:\n%s' % ( | 541 | print('error: cannot connect to manifest server %s:\n%s' |
| 527 | self.manifest.manifest_server, e) | 542 | % (self.manifest.manifest_server, e), file=sys.stderr) |
| 528 | sys.exit(1) | 543 | sys.exit(1) |
| 529 | except xmlrpclib.ProtocolError as e: | 544 | except xmlrpclib.ProtocolError as e: |
| 530 | print >>sys.stderr, 'error: cannot connect to manifest server %s:\n%d %s' % ( | 545 | print('error: cannot connect to manifest server %s:\n%d %s' |
| 531 | self.manifest.manifest_server, e.errcode, e.errmsg) | 546 | % (self.manifest.manifest_server, e.errcode, e.errmsg), |
| 547 | file=sys.stderr) | ||
| 532 | sys.exit(1) | 548 | sys.exit(1) |
| 533 | 549 | ||
| 534 | rp = self.manifest.repoProject | 550 | rp = self.manifest.repoProject |
| @@ -552,7 +568,9 @@ uncommitted changes are present' % project.relpath | |||
| 552 | self.manifest._Unload() | 568 | self.manifest._Unload() |
| 553 | if opt.jobs is None: | 569 | if opt.jobs is None: |
| 554 | self.jobs = self.manifest.default.sync_j | 570 | self.jobs = self.manifest.default.sync_j |
| 555 | all_projects = self.GetProjects(args, missing_ok=True) | 571 | all_projects = self.GetProjects(args, |
| 572 | missing_ok=True, | ||
| 573 | submodules_ok=opt.fetch_submodules) | ||
| 556 | 574 | ||
| 557 | self._fetch_times = _FetchTimes(self.manifest) | 575 | self._fetch_times = _FetchTimes(self.manifest) |
| 558 | if not opt.local_only: | 576 | if not opt.local_only: |
| @@ -563,12 +581,33 @@ uncommitted changes are present' % project.relpath | |||
| 563 | to_fetch.extend(all_projects) | 581 | to_fetch.extend(all_projects) |
| 564 | to_fetch.sort(key=self._fetch_times.Get, reverse=True) | 582 | to_fetch.sort(key=self._fetch_times.Get, reverse=True) |
| 565 | 583 | ||
| 566 | self._Fetch(to_fetch, opt) | 584 | fetched = self._Fetch(to_fetch, opt) |
| 567 | _PostRepoFetch(rp, opt.no_repo_verify) | 585 | _PostRepoFetch(rp, opt.no_repo_verify) |
| 568 | if opt.network_only: | 586 | if opt.network_only: |
| 569 | # bail out now; the rest touches the working tree | 587 | # bail out now; the rest touches the working tree |
| 570 | return | 588 | return |
| 571 | 589 | ||
| 590 | # Iteratively fetch missing and/or nested unregistered submodules | ||
| 591 | previously_missing_set = set() | ||
| 592 | while True: | ||
| 593 | self.manifest._Unload() | ||
| 594 | all_projects = self.GetProjects(args, | ||
| 595 | missing_ok=True, | ||
| 596 | submodules_ok=opt.fetch_submodules) | ||
| 597 | missing = [] | ||
| 598 | for project in all_projects: | ||
| 599 | if project.gitdir not in fetched: | ||
| 600 | missing.append(project) | ||
| 601 | if not missing: | ||
| 602 | break | ||
| 603 | # Stop us from non-stopped fetching actually-missing repos: If set of | ||
| 604 | # missing repos has not been changed from last fetch, we break. | ||
| 605 | missing_set = set(p.name for p in missing) | ||
| 606 | if previously_missing_set == missing_set: | ||
| 607 | break | ||
| 608 | previously_missing_set = missing_set | ||
| 609 | fetched.update(self._Fetch(missing, opt)) | ||
| 610 | |||
| 572 | if self.manifest.IsMirror: | 611 | if self.manifest.IsMirror: |
| 573 | # bail out now, we have no working tree | 612 | # bail out now, we have no working tree |
| 574 | return | 613 | return |
| @@ -584,14 +623,14 @@ uncommitted changes are present' % project.relpath | |||
| 584 | if project.worktree: | 623 | if project.worktree: |
| 585 | project.Sync_LocalHalf(syncbuf) | 624 | project.Sync_LocalHalf(syncbuf) |
| 586 | pm.end() | 625 | pm.end() |
| 587 | print >>sys.stderr | 626 | print(file=sys.stderr) |
| 588 | if not syncbuf.Finish(): | 627 | if not syncbuf.Finish(): |
| 589 | sys.exit(1) | 628 | sys.exit(1) |
| 590 | 629 | ||
| 591 | # If there's a notice that's supposed to print at the end of the sync, print | 630 | # If there's a notice that's supposed to print at the end of the sync, print |
| 592 | # it now... | 631 | # it now... |
| 593 | if self.manifest.notice: | 632 | if self.manifest.notice: |
| 594 | print self.manifest.notice | 633 | print(self.manifest.notice) |
| 595 | 634 | ||
| 596 | def _PostRepoUpgrade(manifest, quiet=False): | 635 | def _PostRepoUpgrade(manifest, quiet=False): |
| 597 | wrapper = WrapperModule() | 636 | wrapper = WrapperModule() |
| @@ -603,27 +642,28 @@ def _PostRepoUpgrade(manifest, quiet=False): | |||
| 603 | 642 | ||
| 604 | def _PostRepoFetch(rp, no_repo_verify=False, verbose=False): | 643 | def _PostRepoFetch(rp, no_repo_verify=False, verbose=False): |
| 605 | if rp.HasChanges: | 644 | if rp.HasChanges: |
| 606 | print >>sys.stderr, 'info: A new version of repo is available' | 645 | print('info: A new version of repo is available', file=sys.stderr) |
| 607 | print >>sys.stderr, '' | 646 | print(file=sys.stderr) |
| 608 | if no_repo_verify or _VerifyTag(rp): | 647 | if no_repo_verify or _VerifyTag(rp): |
| 609 | syncbuf = SyncBuffer(rp.config) | 648 | syncbuf = SyncBuffer(rp.config) |
| 610 | rp.Sync_LocalHalf(syncbuf) | 649 | rp.Sync_LocalHalf(syncbuf) |
| 611 | if not syncbuf.Finish(): | 650 | if not syncbuf.Finish(): |
| 612 | sys.exit(1) | 651 | sys.exit(1) |
| 613 | print >>sys.stderr, 'info: Restarting repo with latest version' | 652 | print('info: Restarting repo with latest version', file=sys.stderr) |
| 614 | raise RepoChangedException(['--repo-upgraded']) | 653 | raise RepoChangedException(['--repo-upgraded']) |
| 615 | else: | 654 | else: |
| 616 | print >>sys.stderr, 'warning: Skipped upgrade to unverified version' | 655 | print('warning: Skipped upgrade to unverified version', file=sys.stderr) |
| 617 | else: | 656 | else: |
| 618 | if verbose: | 657 | if verbose: |
| 619 | print >>sys.stderr, 'repo version %s is current' % rp.work_git.describe(HEAD) | 658 | print('repo version %s is current' % rp.work_git.describe(HEAD), |
| 659 | file=sys.stderr) | ||
| 620 | 660 | ||
| 621 | def _VerifyTag(project): | 661 | def _VerifyTag(project): |
| 622 | gpg_dir = os.path.expanduser('~/.repoconfig/gnupg') | 662 | gpg_dir = os.path.expanduser('~/.repoconfig/gnupg') |
| 623 | if not os.path.exists(gpg_dir): | 663 | if not os.path.exists(gpg_dir): |
| 624 | print >>sys.stderr,\ | 664 | print('warning: GnuPG was not available during last "repo init"\n' |
| 625 | """warning: GnuPG was not available during last "repo init" | 665 | 'warning: Cannot automatically authenticate repo."""', |
| 626 | warning: Cannot automatically authenticate repo.""" | 666 | file=sys.stderr) |
| 627 | return True | 667 | return True |
| 628 | 668 | ||
| 629 | try: | 669 | try: |
| @@ -637,10 +677,9 @@ warning: Cannot automatically authenticate repo.""" | |||
| 637 | if rev.startswith(R_HEADS): | 677 | if rev.startswith(R_HEADS): |
| 638 | rev = rev[len(R_HEADS):] | 678 | rev = rev[len(R_HEADS):] |
| 639 | 679 | ||
| 640 | print >>sys.stderr | 680 | print(file=sys.stderr) |
| 641 | print >>sys.stderr,\ | 681 | print("warning: project '%s' branch '%s' is not signed" |
| 642 | "warning: project '%s' branch '%s' is not signed" \ | 682 | % (project.name, rev), file=sys.stderr) |
| 643 | % (project.name, rev) | ||
| 644 | return False | 683 | return False |
| 645 | 684 | ||
| 646 | env = os.environ.copy() | 685 | env = os.environ.copy() |
| @@ -659,10 +698,10 @@ warning: Cannot automatically authenticate repo.""" | |||
| 659 | proc.stderr.close() | 698 | proc.stderr.close() |
| 660 | 699 | ||
| 661 | if proc.wait() != 0: | 700 | if proc.wait() != 0: |
| 662 | print >>sys.stderr | 701 | print(file=sys.stderr) |
| 663 | print >>sys.stderr, out | 702 | print(out, file=sys.stderr) |
| 664 | print >>sys.stderr, err | 703 | print(err, file=sys.stderr) |
| 665 | print >>sys.stderr | 704 | print(file=sys.stderr) |
| 666 | return False | 705 | return False |
| 667 | return True | 706 | return True |
| 668 | 707 | ||
| @@ -696,7 +735,7 @@ class _FetchTimes(object): | |||
| 696 | try: | 735 | try: |
| 697 | try: | 736 | try: |
| 698 | self._times = pickle.load(f) | 737 | self._times = pickle.load(f) |
| 699 | except: | 738 | except IOError: |
| 700 | try: | 739 | try: |
| 701 | os.remove(self._path) | 740 | os.remove(self._path) |
| 702 | except OSError: | 741 | except OSError: |
diff --git a/subcmds/upload.py b/subcmds/upload.py index 84a5e440..e314032a 100644 --- a/subcmds/upload.py +++ b/subcmds/upload.py | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | # See the License for the specific language governing permissions and | 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. | 14 | # limitations under the License. |
| 15 | 15 | ||
| 16 | from __future__ import print_function | ||
| 16 | import copy | 17 | import copy |
| 17 | import re | 18 | import re |
| 18 | import sys | 19 | import sys |
| @@ -26,16 +27,18 @@ UNUSUAL_COMMIT_THRESHOLD = 5 | |||
| 26 | 27 | ||
| 27 | def _ConfirmManyUploads(multiple_branches=False): | 28 | def _ConfirmManyUploads(multiple_branches=False): |
| 28 | if multiple_branches: | 29 | if multiple_branches: |
| 29 | print "ATTENTION: One or more branches has an unusually high number of commits." | 30 | print('ATTENTION: One or more branches has an unusually high number' |
| 31 | 'of commits.') | ||
| 30 | else: | 32 | else: |
| 31 | print "ATTENTION: You are uploading an unusually high number of commits." | 33 | print('ATTENTION: You are uploading an unusually high number of commits.') |
| 32 | print "YOU PROBABLY DO NOT MEAN TO DO THIS. (Did you rebase across branches?)" | 34 | print('YOU PROBABLY DO NOT MEAN TO DO THIS. (Did you rebase across' |
| 35 | 'branches?)') | ||
| 33 | answer = raw_input("If you are sure you intend to do this, type 'yes': ").strip() | 36 | answer = raw_input("If you are sure you intend to do this, type 'yes': ").strip() |
| 34 | return answer == "yes" | 37 | return answer == "yes" |
| 35 | 38 | ||
| 36 | def _die(fmt, *args): | 39 | def _die(fmt, *args): |
| 37 | msg = fmt % args | 40 | msg = fmt % args |
| 38 | print >>sys.stderr, 'error: %s' % msg | 41 | print('error: %s' % msg, file=sys.stderr) |
| 39 | sys.exit(1) | 42 | sys.exit(1) |
| 40 | 43 | ||
| 41 | def _SplitEmails(values): | 44 | def _SplitEmails(values): |
| @@ -47,7 +50,7 @@ def _SplitEmails(values): | |||
| 47 | class Upload(InteractiveCommand): | 50 | class Upload(InteractiveCommand): |
| 48 | common = True | 51 | common = True |
| 49 | helpSummary = "Upload changes for code review" | 52 | helpSummary = "Upload changes for code review" |
| 50 | helpUsage=""" | 53 | helpUsage = """ |
| 51 | %prog [--re --cc] [<project>]... | 54 | %prog [--re --cc] [<project>]... |
| 52 | """ | 55 | """ |
| 53 | helpDescription = """ | 56 | helpDescription = """ |
| @@ -176,18 +179,18 @@ Gerrit Code Review: http://code.google.com/p/gerrit/ | |||
| 176 | date = branch.date | 179 | date = branch.date |
| 177 | commit_list = branch.commits | 180 | commit_list = branch.commits |
| 178 | 181 | ||
| 179 | print 'Upload project %s/ to remote branch %s:' % (project.relpath, project.revisionExpr) | 182 | print('Upload project %s/ to remote branch %s:' % (project.relpath, project.revisionExpr)) |
| 180 | print ' branch %s (%2d commit%s, %s):' % ( | 183 | print(' branch %s (%2d commit%s, %s):' % ( |
| 181 | name, | 184 | name, |
| 182 | len(commit_list), | 185 | len(commit_list), |
| 183 | len(commit_list) != 1 and 's' or '', | 186 | len(commit_list) != 1 and 's' or '', |
| 184 | date) | 187 | date)) |
| 185 | for commit in commit_list: | 188 | for commit in commit_list: |
| 186 | print ' %s' % commit | 189 | print(' %s' % commit) |
| 187 | 190 | ||
| 188 | sys.stdout.write('to %s (y/N)? ' % remote.review) | 191 | sys.stdout.write('to %s (y/N)? ' % remote.review) |
| 189 | answer = sys.stdin.readline().strip() | 192 | answer = sys.stdin.readline().strip().lower() |
| 190 | answer = answer in ('y', 'Y', 'yes', '1', 'true', 't') | 193 | answer = answer in ('y', 'yes', '1', 'true', 't') |
| 191 | 194 | ||
| 192 | if answer: | 195 | if answer: |
| 193 | if len(branch.commits) > UNUSUAL_COMMIT_THRESHOLD: | 196 | if len(branch.commits) > UNUSUAL_COMMIT_THRESHOLD: |
| @@ -297,7 +300,7 @@ Gerrit Code Review: http://code.google.com/p/gerrit/ | |||
| 297 | try: | 300 | try: |
| 298 | # refs/changes/XYZ/N --> XYZ | 301 | # refs/changes/XYZ/N --> XYZ |
| 299 | return refs.get(last_pub).split('/')[-2] | 302 | return refs.get(last_pub).split('/')[-2] |
| 300 | except: | 303 | except (AttributeError, IndexError): |
| 301 | return "" | 304 | return "" |
| 302 | 305 | ||
| 303 | def _UploadAndReport(self, opt, todo, original_people): | 306 | def _UploadAndReport(self, opt, todo, original_people): |
| @@ -309,23 +312,23 @@ Gerrit Code Review: http://code.google.com/p/gerrit/ | |||
| 309 | 312 | ||
| 310 | # Check if there are local changes that may have been forgotten | 313 | # Check if there are local changes that may have been forgotten |
| 311 | if branch.project.HasChanges(): | 314 | if branch.project.HasChanges(): |
| 312 | key = 'review.%s.autoupload' % branch.project.remote.review | 315 | key = 'review.%s.autoupload' % branch.project.remote.review |
| 313 | answer = branch.project.config.GetBoolean(key) | 316 | answer = branch.project.config.GetBoolean(key) |
| 314 | 317 | ||
| 315 | # if they want to auto upload, let's not ask because it could be automated | 318 | # if they want to auto upload, let's not ask because it could be automated |
| 316 | if answer is None: | 319 | if answer is None: |
| 317 | sys.stdout.write('Uncommitted changes in ' + branch.project.name + ' (did you forget to amend?). Continue uploading? (y/N) ') | 320 | sys.stdout.write('Uncommitted changes in ' + branch.project.name + ' (did you forget to amend?). Continue uploading? (y/N) ') |
| 318 | a = sys.stdin.readline().strip().lower() | 321 | a = sys.stdin.readline().strip().lower() |
| 319 | if a not in ('y', 'yes', 't', 'true', 'on'): | 322 | if a not in ('y', 'yes', 't', 'true', 'on'): |
| 320 | print >>sys.stderr, "skipping upload" | 323 | print("skipping upload", file=sys.stderr) |
| 321 | branch.uploaded = False | 324 | branch.uploaded = False |
| 322 | branch.error = 'User aborted' | 325 | branch.error = 'User aborted' |
| 323 | continue | 326 | continue |
| 324 | 327 | ||
| 325 | # Check if topic branches should be sent to the server during upload | 328 | # Check if topic branches should be sent to the server during upload |
| 326 | if opt.auto_topic is not True: | 329 | if opt.auto_topic is not True: |
| 327 | key = 'review.%s.uploadtopic' % branch.project.remote.review | 330 | key = 'review.%s.uploadtopic' % branch.project.remote.review |
| 328 | opt.auto_topic = branch.project.config.GetBoolean(key) | 331 | opt.auto_topic = branch.project.config.GetBoolean(key) |
| 329 | 332 | ||
| 330 | branch.UploadForReview(people, auto_topic=opt.auto_topic, draft=opt.draft) | 333 | branch.UploadForReview(people, auto_topic=opt.auto_topic, draft=opt.draft) |
| 331 | branch.uploaded = True | 334 | branch.uploaded = True |
| @@ -334,8 +337,8 @@ Gerrit Code Review: http://code.google.com/p/gerrit/ | |||
| 334 | branch.uploaded = False | 337 | branch.uploaded = False |
| 335 | have_errors = True | 338 | have_errors = True |
| 336 | 339 | ||
| 337 | print >>sys.stderr, '' | 340 | print(file=sys.stderr) |
| 338 | print >>sys.stderr, '----------------------------------------------------------------------' | 341 | print('----------------------------------------------------------------------', file=sys.stderr) |
| 339 | 342 | ||
| 340 | if have_errors: | 343 | if have_errors: |
| 341 | for branch in todo: | 344 | for branch in todo: |
| @@ -344,17 +347,19 @@ Gerrit Code Review: http://code.google.com/p/gerrit/ | |||
| 344 | fmt = ' (%s)' | 347 | fmt = ' (%s)' |
| 345 | else: | 348 | else: |
| 346 | fmt = '\n (%s)' | 349 | fmt = '\n (%s)' |
| 347 | print >>sys.stderr, ('[FAILED] %-15s %-15s' + fmt) % ( | 350 | print(('[FAILED] %-15s %-15s' + fmt) % ( |
| 348 | branch.project.relpath + '/', \ | 351 | branch.project.relpath + '/', \ |
| 349 | branch.name, \ | 352 | branch.name, \ |
| 350 | str(branch.error)) | 353 | str(branch.error)), |
| 351 | print >>sys.stderr, '' | 354 | file=sys.stderr) |
| 355 | print() | ||
| 352 | 356 | ||
| 353 | for branch in todo: | 357 | for branch in todo: |
| 354 | if branch.uploaded: | 358 | if branch.uploaded: |
| 355 | print >>sys.stderr, '[OK ] %-15s %s' % ( | 359 | print('[OK ] %-15s %s' % ( |
| 356 | branch.project.relpath + '/', | 360 | branch.project.relpath + '/', |
| 357 | branch.name) | 361 | branch.name), |
| 362 | file=sys.stderr) | ||
| 358 | 363 | ||
| 359 | if have_errors: | 364 | if have_errors: |
| 360 | sys.exit(1) | 365 | sys.exit(1) |
| @@ -385,17 +390,17 @@ Gerrit Code Review: http://code.google.com/p/gerrit/ | |||
| 385 | try: | 390 | try: |
| 386 | hook.Run(opt.allow_all_hooks, project_list=pending_proj_names) | 391 | hook.Run(opt.allow_all_hooks, project_list=pending_proj_names) |
| 387 | except HookError as e: | 392 | except HookError as e: |
| 388 | print >>sys.stderr, "ERROR: %s" % str(e) | 393 | print("ERROR: %s" % str(e), file=sys.stderr) |
| 389 | return | 394 | return |
| 390 | 395 | ||
| 391 | if opt.reviewers: | 396 | if opt.reviewers: |
| 392 | reviewers = _SplitEmails(opt.reviewers) | 397 | reviewers = _SplitEmails(opt.reviewers) |
| 393 | if opt.cc: | 398 | if opt.cc: |
| 394 | cc = _SplitEmails(opt.cc) | 399 | cc = _SplitEmails(opt.cc) |
| 395 | people = (reviewers,cc) | 400 | people = (reviewers, cc) |
| 396 | 401 | ||
| 397 | if not pending: | 402 | if not pending: |
| 398 | print >>sys.stdout, "no branches ready for upload" | 403 | print("no branches ready for upload", file=sys.stderr) |
| 399 | elif len(pending) == 1 and len(pending[0][1]) == 1: | 404 | elif len(pending) == 1 and len(pending[0][1]) == 1: |
| 400 | self._SingleBranch(opt, pending[0][1][0], people) | 405 | self._SingleBranch(opt, pending[0][1][0], people) |
| 401 | else: | 406 | else: |
diff --git a/subcmds/version.py b/subcmds/version.py index 243e3676..01b7fd8c 100644 --- a/subcmds/version.py +++ b/subcmds/version.py | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | # See the License for the specific language governing permissions and | 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. | 14 | # limitations under the License. |
| 15 | 15 | ||
| 16 | from __future__ import print_function | ||
| 16 | import sys | 17 | import sys |
| 17 | from command import Command, MirrorSafeCommand | 18 | from command import Command, MirrorSafeCommand |
| 18 | from git_command import git | 19 | from git_command import git |
| @@ -32,12 +33,12 @@ class Version(Command, MirrorSafeCommand): | |||
| 32 | rp = self.manifest.repoProject | 33 | rp = self.manifest.repoProject |
| 33 | rem = rp.GetRemote(rp.remote.name) | 34 | rem = rp.GetRemote(rp.remote.name) |
| 34 | 35 | ||
| 35 | print 'repo version %s' % rp.work_git.describe(HEAD) | 36 | print('repo version %s' % rp.work_git.describe(HEAD)) |
| 36 | print ' (from %s)' % rem.url | 37 | print(' (from %s)' % rem.url) |
| 37 | 38 | ||
| 38 | if Version.wrapper_path is not None: | 39 | if Version.wrapper_path is not None: |
| 39 | print 'repo launcher version %s' % Version.wrapper_version | 40 | print('repo launcher version %s' % Version.wrapper_version) |
| 40 | print ' (from %s)' % Version.wrapper_path | 41 | print(' (from %s)' % Version.wrapper_path) |
| 41 | 42 | ||
| 42 | print git.version().strip() | 43 | print(git.version().strip()) |
| 43 | print 'Python %s' % sys.version | 44 | print('Python %s' % sys.version) |
diff --git a/tests/test_git_config.py b/tests/test_git_config.py index 5b1770e7..3d4b9970 100644 --- a/tests/test_git_config.py +++ b/tests/test_git_config.py | |||
| @@ -4,49 +4,49 @@ import unittest | |||
| 4 | import git_config | 4 | import git_config |
| 5 | 5 | ||
| 6 | def fixture(*paths): | 6 | def fixture(*paths): |
| 7 | """Return a path relative to test/fixtures. | 7 | """Return a path relative to test/fixtures. |
| 8 | """ | 8 | """ |
| 9 | return os.path.join(os.path.dirname(__file__), 'fixtures', *paths) | 9 | return os.path.join(os.path.dirname(__file__), 'fixtures', *paths) |
| 10 | 10 | ||
| 11 | class GitConfigUnitTest(unittest.TestCase): | 11 | class GitConfigUnitTest(unittest.TestCase): |
| 12 | """Tests the GitConfig class. | 12 | """Tests the GitConfig class. |
| 13 | """ | ||
| 14 | def setUp(self): | ||
| 15 | """Create a GitConfig object using the test.gitconfig fixture. | ||
| 16 | """ | ||
| 17 | config_fixture = fixture('test.gitconfig') | ||
| 18 | self.config = git_config.GitConfig(config_fixture) | ||
| 19 | |||
| 20 | def test_GetString_with_empty_config_values(self): | ||
| 21 | """ | ||
| 22 | Test config entries with no value. | ||
| 23 | |||
| 24 | [section] | ||
| 25 | empty | ||
| 26 | |||
| 27 | """ | ||
| 28 | val = self.config.GetString('section.empty') | ||
| 29 | self.assertEqual(val, None) | ||
| 30 | |||
| 31 | def test_GetString_with_true_value(self): | ||
| 32 | """ | ||
| 33 | Test config entries with a string value. | ||
| 34 | |||
| 35 | [section] | ||
| 36 | nonempty = true | ||
| 37 | |||
| 38 | """ | ||
| 39 | val = self.config.GetString('section.nonempty') | ||
| 40 | self.assertEqual(val, 'true') | ||
| 41 | |||
| 42 | def test_GetString_from_missing_file(self): | ||
| 43 | """ | ||
| 44 | Test missing config file | ||
| 13 | """ | 45 | """ |
| 14 | def setUp(self): | 46 | config_fixture = fixture('not.present.gitconfig') |
| 15 | """Create a GitConfig object using the test.gitconfig fixture. | 47 | config = git_config.GitConfig(config_fixture) |
| 16 | """ | 48 | val = config.GetString('empty') |
| 17 | config_fixture = fixture('test.gitconfig') | 49 | self.assertEqual(val, None) |
| 18 | self.config = git_config.GitConfig(config_fixture) | ||
| 19 | |||
| 20 | def test_GetString_with_empty_config_values(self): | ||
| 21 | """ | ||
| 22 | Test config entries with no value. | ||
| 23 | |||
| 24 | [section] | ||
| 25 | empty | ||
| 26 | |||
| 27 | """ | ||
| 28 | val = self.config.GetString('section.empty') | ||
| 29 | self.assertEqual(val, None) | ||
| 30 | |||
| 31 | def test_GetString_with_true_value(self): | ||
| 32 | """ | ||
| 33 | Test config entries with a string value. | ||
| 34 | |||
| 35 | [section] | ||
| 36 | nonempty = true | ||
| 37 | |||
| 38 | """ | ||
| 39 | val = self.config.GetString('section.nonempty') | ||
| 40 | self.assertEqual(val, 'true') | ||
| 41 | |||
| 42 | def test_GetString_from_missing_file(self): | ||
| 43 | """ | ||
| 44 | Test missing config file | ||
| 45 | """ | ||
| 46 | config_fixture = fixture('not.present.gitconfig') | ||
| 47 | config = git_config.GitConfig(config_fixture) | ||
| 48 | val = config.GetString('empty') | ||
| 49 | self.assertEqual(val, None) | ||
| 50 | 50 | ||
| 51 | if __name__ == '__main__': | 51 | if __name__ == '__main__': |
| 52 | unittest.main() | 52 | unittest.main() |
| @@ -13,6 +13,7 @@ | |||
| 13 | # See the License for the specific language governing permissions and | 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. | 14 | # limitations under the License. |
| 15 | 15 | ||
| 16 | from __future__ import print_function | ||
| 16 | import sys | 17 | import sys |
| 17 | import os | 18 | import os |
| 18 | REPO_TRACE = 'REPO_TRACE' | 19 | REPO_TRACE = 'REPO_TRACE' |
| @@ -31,4 +32,4 @@ def SetTrace(): | |||
| 31 | 32 | ||
| 32 | def Trace(fmt, *args): | 33 | def Trace(fmt, *args): |
| 33 | if IsTrace(): | 34 | if IsTrace(): |
| 34 | print >>sys.stderr, fmt % args | 35 | print(fmt % args, file=sys.stderr) |
