diff --git a/command.py b/command.py index d2129381..0205932d 100644 --- a/command.py +++ b/command.py @@ -75,7 +75,6 @@ class Command(object): repodir=None, client=None, manifest=None, - gitc_manifest=None, git_event_log=None, outer_client=None, outer_manifest=None, @@ -84,7 +83,6 @@ class Command(object): self.client = client self.outer_client = outer_client or client self.manifest = manifest - self.gitc_manifest = gitc_manifest self.git_event_log = git_event_log self.outer_manifest = outer_manifest @@ -506,11 +504,5 @@ class MirrorSafeCommand(object): """ -class GitcAvailableCommand(object): - """Command that requires GITC to be available, but does not require the - local client to be a GITC client. - """ - - class GitcClientCommand(object): """Command that requires the local client to be a GITC client.""" diff --git a/error.py b/error.py index cee977f9..7958a0a1 100644 --- a/error.py +++ b/error.py @@ -107,6 +107,10 @@ class GitError(RepoError): return self.message +class GitcUnsupportedError(RepoExitError): + """Gitc no longer supported.""" + + class UploadError(RepoError): """A bundle upload to Gerrit did not succeed.""" diff --git a/gitc_utils.py b/gitc_utils.py deleted file mode 100644 index 7b72048f..00000000 --- a/gitc_utils.py +++ /dev/null @@ -1,166 +0,0 @@ -# Copyright (C) 2015 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 os -import multiprocessing -import re -import sys -import time - -import git_command -import git_config -import wrapper - -from error import ManifestParseError - -NUM_BATCH_RETRIEVE_REVISIONID = 32 - - -def get_gitc_manifest_dir(): - return wrapper.Wrapper().get_gitc_manifest_dir() - - -def parse_clientdir(gitc_fs_path): - return wrapper.Wrapper().gitc_parse_clientdir(gitc_fs_path) - - -def _get_project_revision(args): - """Worker for _set_project_revisions to lookup one project remote.""" - (i, url, expr) = args - gitcmd = git_command.GitCommand( - None, ["ls-remote", url, expr], capture_stdout=True, cwd="/tmp" - ) - rc = gitcmd.Wait() - return (i, rc, gitcmd.stdout.split("\t", 1)[0]) - - -def _set_project_revisions(projects): - """Sets the revisionExpr for a list of projects. - - Because of the limit of open file descriptors allowed, length of projects - should not be overly large. Recommend calling this function multiple times - with each call not exceeding NUM_BATCH_RETRIEVE_REVISIONID projects. - - Args: - projects: List of project objects to set the revionExpr for. - """ - # Retrieve the commit id for each project based off of its current - # revisionExpr and it is not already a commit id. - with multiprocessing.Pool(NUM_BATCH_RETRIEVE_REVISIONID) as pool: - results_iter = pool.imap_unordered( - _get_project_revision, - ( - (i, project.remote.url, project.revisionExpr) - for i, project in enumerate(projects) - if not git_config.IsId(project.revisionExpr) - ), - chunksize=8, - ) - for i, rc, revisionExpr in results_iter: - project = projects[i] - if rc: - print( - "FATAL: Failed to retrieve revisionExpr for %s" - % project.name - ) - pool.terminate() - sys.exit(1) - if not revisionExpr: - pool.terminate() - raise ManifestParseError( - "Invalid SHA-1 revision project %s (%s)" - % (project.remote.url, project.revisionExpr) - ) - project.revisionExpr = revisionExpr - - -def generate_gitc_manifest(gitc_manifest, manifest, paths=None): - """Generate a manifest for shafsd to use for this GITC client. - - Args: - gitc_manifest: Current gitc manifest, or None if there isn't one yet. - manifest: A GitcManifest object loaded with the current repo manifest. - paths: List of project paths we want to update. - """ - - print( - "Generating GITC Manifest by fetching revision SHAs for each " - "project." - ) - if paths is None: - paths = list(manifest.paths.keys()) - - groups = [x for x in re.split(r"[,\s]+", manifest.GetGroupsStr()) if x] - - # Convert the paths to projects, and filter them to the matched groups. - projects = [manifest.paths[p] for p in paths] - projects = [p for p in projects if p.MatchesGroups(groups)] - - if gitc_manifest is not None: - for path, proj in manifest.paths.items(): - if not proj.MatchesGroups(groups): - continue - - if not proj.upstream and not git_config.IsId(proj.revisionExpr): - proj.upstream = proj.revisionExpr - - if path not in gitc_manifest.paths: - # Any new projects need their first revision, even if we weren't - # asked for them. - projects.append(proj) - elif path not in paths: - # And copy revisions from the previous manifest if we're not - # updating them now. - gitc_proj = gitc_manifest.paths[path] - if gitc_proj.old_revision: - proj.revisionExpr = None - proj.old_revision = gitc_proj.old_revision - else: - proj.revisionExpr = gitc_proj.revisionExpr - - _set_project_revisions(projects) - - if gitc_manifest is not None: - for path, proj in gitc_manifest.paths.items(): - if proj.old_revision and path in paths: - # If we updated a project that has been started, keep the - # old-revision updated. - repo_proj = manifest.paths[path] - repo_proj.old_revision = repo_proj.revisionExpr - repo_proj.revisionExpr = None - - # Convert URLs from relative to absolute. - for _name, remote in manifest.remotes.items(): - remote.fetchUrl = remote.resolvedFetchUrl - - # Save the manifest. - save_manifest(manifest) - - -def save_manifest(manifest, client_dir=None): - """Save the manifest file in the client_dir. - - Args: - manifest: Manifest object to save. - client_dir: Client directory to save the manifest in. - """ - if not client_dir: - manifest_file = manifest.manifestFile - else: - manifest_file = os.path.join(client_dir, ".manifest") - with open(manifest_file, "w") as f: - manifest.Save(f, groups=manifest.GetGroupsStr()) - # TODO(sbasi/jorg): Come up with a solution to remove the sleep below. - # Give the GITC filesystem time to register the manifest changes. - time.sleep(3) diff --git a/main.py b/main.py index ffed0b72..126a4b61 100755 --- a/main.py +++ b/main.py @@ -45,7 +45,6 @@ from git_config import RepoConfig from git_trace2_event_log import EventLog from command import InteractiveCommand from command import MirrorSafeCommand -from command import GitcAvailableCommand, GitcClientCommand from subcmds.version import Version from editor import Editor from error import DownloadError @@ -58,8 +57,8 @@ from error import RepoExitError from error import RepoUnhandledExceptionError from error import RepoError from error import SilentRepoExitError -import gitc_utils -from manifest_xml import GitcClient, RepoClient +from error import GitcUnsupportedError +from manifest_xml import RepoClient from pager import RunPager, TerminatePager from wrapper import WrapperPath, Wrapper @@ -304,11 +303,10 @@ class _Repo(object): submanifest_path=gopts.submanifest_path, outer_client=outer_client, ) - gitc_manifest = None - gitc_client_name = gitc_utils.parse_clientdir(os.getcwd()) - if gitc_client_name: - gitc_manifest = GitcClient(self.repodir, gitc_client_name) - repo_client.isGitcClient = True + + if Wrapper().gitc_parse_clientdir(os.getcwd()): + print("GITC is not supported.", file=sys.stderr) + raise GitcUnsupportedError() try: cmd = self.commands[name]( @@ -317,7 +315,6 @@ class _Repo(object): manifest=repo_client.manifest, outer_client=outer_client, outer_manifest=outer_client.manifest, - gitc_manifest=gitc_manifest, git_event_log=git_trace2_event_log, ) except KeyError: @@ -336,20 +333,6 @@ class _Repo(object): ) return 1 - if ( - isinstance(cmd, GitcAvailableCommand) - and not gitc_utils.get_gitc_manifest_dir() - ): - print( - "fatal: '%s' requires GITC to be available" % name, - file=sys.stderr, - ) - return 1 - - if isinstance(cmd, GitcClientCommand) and not gitc_client_name: - print("fatal: '%s' requires a GITC client" % name, file=sys.stderr) - return 1 - try: copts, cargs = cmd.OptionParser.parse_args(argv) copts = cmd.ReadEnvironmentOptions(copts) diff --git a/manifest_xml.py b/manifest_xml.py index 73be1b6e..80e563a5 100644 --- a/manifest_xml.py +++ b/manifest_xml.py @@ -21,7 +21,6 @@ import sys import xml.dom.minidom import urllib.parse -import gitc_utils from git_config import GitConfig from git_refs import R_HEADS, HEAD from git_superproject import Superproject @@ -2248,21 +2247,6 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md return diff -class GitcManifest(XmlManifest): - """Parser for GitC (git-in-the-cloud) manifests.""" - - def _ParseProject(self, node, parent=None): - """Override _ParseProject and add support for GITC specific attributes.""" # noqa: E501 - return super()._ParseProject( - node, parent=parent, old_revision=node.getAttribute("old-revision") - ) - - def _output_manifest_project_extras(self, p, e): - """Output GITC Specific Project attributes""" - if p.old_revision: - e.setAttribute("old-revision", str(p.old_revision)) - - class RepoClient(XmlManifest): """Manages a repo client checkout.""" @@ -2315,19 +2299,3 @@ class RepoClient(XmlManifest): # TODO: Completely separate manifest logic out of the client. self.manifest = self - - -class GitcClient(RepoClient, GitcManifest): - """Manages a GitC client checkout.""" - - def __init__(self, repodir, gitc_client_name): - """Initialize the GitcManifest object.""" - self.gitc_client_name = gitc_client_name - self.gitc_client_dir = os.path.join( - gitc_utils.get_gitc_manifest_dir(), gitc_client_name - ) - - super().__init__( - repodir, os.path.join(self.gitc_client_dir, ".manifest") - ) - self.isGitcClient = True diff --git a/repo b/repo index dbc96da7..b9b1928a 100755 --- a/repo +++ b/repo @@ -149,7 +149,7 @@ if not REPO_REV: BUG_URL = 'https://issues.gerritcodereview.com/issues/new?component=1370071' # increment this whenever we make important changes to this script -VERSION = (2, 35) +VERSION = (2, 36) # increment this if the MAINTAINER_KEYS block is modified KEYRING_VERSION = (2, 3) @@ -273,16 +273,16 @@ gpg_dir = os.path.join(home_dot_repo, 'gnupg') def GetParser(gitc_init=False): """Setup the CLI parser.""" if gitc_init: - usage = 'repo gitc-init -c client [options] [-u] url' + sys.exit('repo: fatal: GITC not supported.') else: usage = 'repo init [options] [-u] url' parser = optparse.OptionParser(usage=usage) - InitParser(parser, gitc_init=gitc_init) + InitParser(parser) return parser -def InitParser(parser, gitc_init=False): +def InitParser(parser): """Setup the CLI parser.""" # NB: Keep in sync with command.py:_CommonOptions(). @@ -325,12 +325,8 @@ def InitParser(parser, gitc_init=False): # Options that only affect manifest project, and not any of the projects # specified in the manifest itself. group = parser.add_option_group('Manifest (only) checkout options') - cbr_opts = ['--current-branch'] - # The gitc-init subcommand allocates -c itself, but a lot of init users - # want -c, so try to satisfy both as best we can. - if not gitc_init: - cbr_opts += ['-c'] - group.add_option(*cbr_opts, default=True, + + group.add_option('--current-branch', '-c', default=True, dest='current_branch_only', action='store_true', help='fetch only current manifest branch from server (default)') group.add_option('--no-current-branch', @@ -411,14 +407,6 @@ def InitParser(parser, gitc_init=False): action='store_true', default=False, help='Always prompt for name/e-mail') - # gitc-init specific settings. - if gitc_init: - group = parser.add_option_group('GITC options') - group.add_option('-f', '--manifest-file', - help='Optional manifest file to use for this GITC client.') - group.add_option('-c', '--gitc-client', - help='Name of the gitc_client instance to create or modify.') - return parser @@ -582,26 +570,6 @@ def _Init(args, gitc_init=False): rev = opt.repo_rev or REPO_REV try: - if gitc_init: - gitc_manifest_dir = get_gitc_manifest_dir() - if not gitc_manifest_dir: - print('fatal: GITC filesystem is not available. Exiting...', - file=sys.stderr) - sys.exit(1) - gitc_client = opt.gitc_client - if not gitc_client: - gitc_client = gitc_parse_clientdir(os.getcwd()) - if not gitc_client: - print('fatal: GITC client (-c) is required.', file=sys.stderr) - sys.exit(1) - client_dir = os.path.join(gitc_manifest_dir, gitc_client) - if not os.path.exists(client_dir): - os.makedirs(client_dir) - os.chdir(client_dir) - if os.path.exists(repodir): - # This GITC Client has already initialized repo so continue. - return - os.mkdir(repodir) except OSError as e: if e.errno != errno.EEXIST: diff --git a/subcmds/gitc_delete.py b/subcmds/gitc_delete.py deleted file mode 100644 index ae9d4d1f..00000000 --- a/subcmds/gitc_delete.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (C) 2015 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 sys - -from command import Command, GitcClientCommand -import platform_utils - - -class GitcDelete(Command, GitcClientCommand): - COMMON = True - visible_everywhere = False - helpSummary = "Delete a GITC Client." - helpUsage = """ -%prog -""" - helpDescription = """ -This subcommand deletes the current GITC client, deleting the GITC manifest -and all locally downloaded sources. -""" - - def _Options(self, p): - p.add_option( - "-f", - "--force", - dest="force", - action="store_true", - help="force the deletion (no prompt)", - ) - - def Execute(self, opt, args): - if not opt.force: - prompt = ( - "This will delete GITC client: %s\nAre you sure? (yes/no) " - % self.gitc_manifest.gitc_client_name - ) - response = input(prompt).lower() - if not response == "yes": - print('Response was not "yes"\n Exiting...') - sys.exit(1) - platform_utils.rmtree(self.gitc_manifest.gitc_client_dir) diff --git a/subcmds/gitc_init.py b/subcmds/gitc_init.py deleted file mode 100644 index 54791d58..00000000 --- a/subcmds/gitc_init.py +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright (C) 2015 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 os -import sys - -import gitc_utils -from command import GitcAvailableCommand -from manifest_xml import GitcManifest -from subcmds import init -import wrapper - - -class GitcInit(init.Init, GitcAvailableCommand): - COMMON = True - MULTI_MANIFEST_SUPPORT = False - helpSummary = "Initialize a GITC Client." - helpUsage = """ -%prog [options] [client name] -""" - helpDescription = """ -The '%prog' command is ran to initialize a new GITC client for use -with the GITC file system. - -This command will setup the client directory, initialize repo, just -like repo init does, and then downloads the manifest collection -and installs it in the .repo/directory of the GITC client. - -Once this is done, a GITC manifest is generated by pulling the HEAD -SHA for each project and generates the properly formatted XML file -and installs it as .manifest in the GITC client directory. - -The -c argument is required to specify the GITC client name. - -The optional -f argument can be used to specify the manifest file to -use for this GITC client. -""" - - def _Options(self, p): - super()._Options(p, gitc_init=True) - - def Execute(self, opt, args): - gitc_client = gitc_utils.parse_clientdir(os.getcwd()) - if not gitc_client or ( - opt.gitc_client and gitc_client != opt.gitc_client - ): - print( - "fatal: Please update your repo command. See go/gitc for " - "instructions.", - file=sys.stderr, - ) - sys.exit(1) - self.client_dir = os.path.join( - gitc_utils.get_gitc_manifest_dir(), gitc_client - ) - super().Execute(opt, args) - - manifest_file = self.manifest.manifestFile - if opt.manifest_file: - if not os.path.exists(opt.manifest_file): - print( - "fatal: Specified manifest file %s does not exist." - % opt.manifest_file - ) - sys.exit(1) - manifest_file = opt.manifest_file - - manifest = GitcManifest( - self.repodir, os.path.join(self.client_dir, ".manifest") - ) - manifest.Override(manifest_file) - gitc_utils.generate_gitc_manifest(None, manifest) - print( - "Please run `cd %s` to view your GITC client." - % os.path.join(wrapper.Wrapper().GITC_FS_ROOT_DIR, gitc_client) - ) diff --git a/subcmds/help.py b/subcmds/help.py index 593bf676..0d7b664e 100644 --- a/subcmds/help.py +++ b/subcmds/help.py @@ -21,10 +21,7 @@ from color import Coloring from command import ( PagedCommand, MirrorSafeCommand, - GitcAvailableCommand, - GitcClientCommand, ) -import gitc_utils from wrapper import Wrapper from error import RepoExitError @@ -79,26 +76,9 @@ Displays detailed usage information about a command. def PrintCommonCommandsBody(self): print("The most commonly used repo commands are:") - def gitc_supported(cmd): - if not isinstance(cmd, GitcAvailableCommand) and not isinstance( - cmd, GitcClientCommand - ): - return True - if self.client.isGitcClient: - return True - if isinstance(cmd, GitcClientCommand): - return False - if gitc_utils.get_gitc_manifest_dir(): - return True - return False - commandNames = list( sorted( - [ - name - for name, command in all_commands.items() - if command.COMMON and gitc_supported(command) - ] + name for name, command in all_commands.items() if command.COMMON ) ) self._PrintCommands(commandNames) diff --git a/subcmds/init.py b/subcmds/init.py index 868d339e..c5a2c54c 100644 --- a/subcmds/init.py +++ b/subcmds/init.py @@ -84,8 +84,8 @@ to update the working directory files. def _CommonOptions(self, p): """Disable due to re-use of Wrapper().""" - def _Options(self, p, gitc_init=False): - Wrapper().InitParser(p, gitc_init=gitc_init) + def _Options(self, p): + Wrapper().InitParser(p) m = p.add_option_group("Multi-manifest") m.add_option( "--outer-manifest", diff --git a/subcmds/start.py b/subcmds/start.py index 67ac7df9..481d9ef2 100644 --- a/subcmds/start.py +++ b/subcmds/start.py @@ -13,15 +13,13 @@ # 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, Project +from project import Project from typing import NamedTuple from error import RepoExitError @@ -115,49 +113,9 @@ revision specified in the manifest. 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 in results: if result.error: diff --git a/subcmds/sync.py b/subcmds/sync.py index 3fa6efa5..df536892 100644 --- a/subcmds/sync.py +++ b/subcmds/sync.py @@ -54,7 +54,6 @@ from git_command import git_require from git_config import GetUrlCookieFile from git_refs import R_HEADS, HEAD import git_superproject -import gitc_utils from project import Project from project import RemoteSpec from command import ( @@ -77,7 +76,6 @@ from progress import Progress, elapsed_str, jobs_str from repo_trace import Trace import ssh from wrapper import Wrapper -from manifest_xml import GitcManifest _ONE_DAY_S = 24 * 60 * 60 @@ -1678,50 +1676,6 @@ later is required to fix a server side protocol bug. opt, args, superproject_logging_data, manifest ) - if self.gitc_manifest: - gitc_manifest_projects = self.GetProjects(args, missing_ok=True) - gitc_projects = [] - opened_projects = [] - for project in gitc_manifest_projects: - if ( - project.relpath in self.gitc_manifest.paths - and self.gitc_manifest.paths[project.relpath].old_revision - ): - opened_projects.append(project.relpath) - else: - gitc_projects.append(project.relpath) - - if not args: - gitc_projects = None - - if gitc_projects != [] and not opt.local_only: - print( - "Updating GITC client: %s" - % self.gitc_manifest.gitc_client_name - ) - manifest = GitcManifest( - self.repodir, self.gitc_manifest.gitc_client_name - ) - if manifest_name: - manifest.Override(manifest_name) - else: - manifest.Override(manifest.manifestFile) - gitc_utils.generate_gitc_manifest( - self.gitc_manifest, manifest, gitc_projects - ) - print("GITC client successfully synced.") - - # The opened projects need to be synced as normal, therefore we - # generate a new args list to represent the opened projects. - # TODO: make this more reliable -- if there's a project name/path - # overlap, this may choose the wrong project. - args = [ - os.path.relpath(manifest.paths[path].worktree, os.getcwd()) - for path in opened_projects - ] - if not args: - return - all_projects = self.GetProjects( args, missing_ok=True, diff --git a/tests/test_wrapper.py b/tests/test_wrapper.py index 21fa094d..4e8263b2 100644 --- a/tests/test_wrapper.py +++ b/tests/test_wrapper.py @@ -76,11 +76,9 @@ class RepoWrapperUnitTest(RepoWrapperTestCase): self.assertIsNone(opts.manifest_url) def test_gitc_init_parser(self): - """Make sure 'gitc-init' GetParser works.""" - parser = self.wrapper.GetParser(gitc_init=True) - opts, args = parser.parse_args([]) - self.assertEqual([], args) - self.assertIsNone(opts.manifest_file) + """Make sure 'gitc-init' GetParser raises.""" + with self.assertRaises(SystemExit): + self.wrapper.GetParser(gitc_init=True) def test_get_gitc_manifest_dir_no_gitc(self): """