| # Copyright (C) 2008 The Android Open Source Project |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| import functools |
| import os |
| import sys |
| |
| from command import Command, DEFAULT_LOCAL_JOBS |
| from git_config import IsImmutable |
| from git_command import git |
| import gitc_utils |
| from progress import Progress |
| from project import SyncBuffer |
| |
| |
| class Start(Command): |
| COMMON = True |
| helpSummary = "Start a new branch for development" |
| helpUsage = """ |
| %prog <newbranchname> [--all | <project>...] |
| """ |
| helpDescription = """ |
| '%prog' begins a new branch of development, starting from the |
| revision specified in the manifest. |
| """ |
| PARALLEL_JOBS = DEFAULT_LOCAL_JOBS |
| |
| def _Options(self, p): |
| p.add_option( |
| "--all", |
| dest="all", |
| action="store_true", |
| help="begin branch in all projects", |
| ) |
| p.add_option( |
| "-r", |
| "--rev", |
| "--revision", |
| dest="revision", |
| help="point branch at this revision instead of upstream", |
| ) |
| p.add_option( |
| "--head", |
| "--HEAD", |
| dest="revision", |
| action="store_const", |
| const="HEAD", |
| help="abbreviation for --rev HEAD", |
| ) |
| |
| def ValidateOptions(self, opt, args): |
| if not args: |
| self.Usage() |
| |
| nb = args[0] |
| if not git.check_ref_format("heads/%s" % nb): |
| self.OptionParser.error("'%s' is not a valid name" % nb) |
| |
| def _ExecuteOne(self, revision, nb, project): |
| """Start one project.""" |
| # If the current revision is immutable, such as a SHA1, a tag or |
| # a change, then we can't push back to it. Substitute with |
| # dest_branch, if defined; or with manifest default revision instead. |
| branch_merge = "" |
| if IsImmutable(project.revisionExpr): |
| if project.dest_branch: |
| branch_merge = project.dest_branch |
| else: |
| branch_merge = self.manifest.default.revisionExpr |
| |
| try: |
| ret = project.StartBranch( |
| nb, branch_merge=branch_merge, revision=revision |
| ) |
| except Exception as e: |
| print( |
| "error: unable to checkout %s: %s" % (project.name, e), |
| file=sys.stderr, |
| ) |
| ret = False |
| return (ret, project) |
| |
| def Execute(self, opt, args): |
| nb = args[0] |
| err = [] |
| projects = [] |
| if not opt.all: |
| projects = args[1:] |
| if len(projects) < 1: |
| projects = ["."] # start it in the local project by default |
| |
| all_projects = self.GetProjects( |
| projects, |
| missing_ok=bool(self.gitc_manifest), |
| all_manifests=not opt.this_manifest_only, |
| ) |
| |
| # This must happen after we find all_projects, since GetProjects may |
| # need the local directory, which will disappear once we save the GITC |
| # manifest. |
| if self.gitc_manifest: |
| gitc_projects = self.GetProjects( |
| projects, manifest=self.gitc_manifest, missing_ok=True |
| ) |
| for project in gitc_projects: |
| if project.old_revision: |
| project.already_synced = True |
| else: |
| project.already_synced = False |
| project.old_revision = project.revisionExpr |
| project.revisionExpr = None |
| # Save the GITC manifest. |
| gitc_utils.save_manifest(self.gitc_manifest) |
| |
| # Make sure we have a valid CWD. |
| if not os.path.exists(os.getcwd()): |
| os.chdir(self.manifest.topdir) |
| |
| pm = Progress("Syncing %s" % nb, len(all_projects), quiet=opt.quiet) |
| for project in all_projects: |
| gitc_project = self.gitc_manifest.paths[project.relpath] |
| # Sync projects that have not been opened. |
| if not gitc_project.already_synced: |
| proj_localdir = os.path.join( |
| self.gitc_manifest.gitc_client_dir, project.relpath |
| ) |
| project.worktree = proj_localdir |
| if not os.path.exists(proj_localdir): |
| os.makedirs(proj_localdir) |
| project.Sync_NetworkHalf() |
| sync_buf = SyncBuffer(self.manifest.manifestProject.config) |
| project.Sync_LocalHalf(sync_buf) |
| project.revisionId = gitc_project.old_revision |
| pm.update(msg="") |
| pm.end() |
| |
| def _ProcessResults(_pool, pm, results): |
| for result, project in results: |
| if not result: |
| err.append(project) |
| pm.update(msg="") |
| |
| self.ExecuteInParallel( |
| opt.jobs, |
| functools.partial(self._ExecuteOne, opt.revision, nb), |
| all_projects, |
| callback=_ProcessResults, |
| output=Progress( |
| "Starting %s" % (nb,), len(all_projects), quiet=opt.quiet |
| ), |
| ) |
| |
| if err: |
| for p in err: |
| print( |
| "error: %s/: cannot start %s" |
| % (p.RelPath(local=opt.this_manifest_only), nb), |
| file=sys.stderr, |
| ) |
| msg_fmt = "cannot start %d project(s)" |
| self.git_event_log.ErrorEvent(msg_fmt % (len(err)), msg_fmt) |
| sys.exit(1) |