summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--command.py5
-rw-r--r--gitc_utils.py69
-rw-r--r--manifest_xml.py3
-rw-r--r--project.py17
-rwxr-xr-xrepo27
-rw-r--r--subcmds/forall.py5
-rw-r--r--subcmds/gitc_init.py80
-rw-r--r--subcmds/list.py5
-rw-r--r--subcmds/sync.py29
9 files changed, 223 insertions, 17 deletions
diff --git a/command.py b/command.py
index 207ef46b..38cacd3b 100644
--- a/command.py
+++ b/command.py
@@ -126,7 +126,7 @@ class Command(object):
126 pass 126 pass
127 return project 127 return project
128 128
129 def GetProjects(self, args, missing_ok=False, submodules_ok=False): 129 def GetProjects(self, args, groups='', missing_ok=False, submodules_ok=False):
130 """A list of projects that match the arguments. 130 """A list of projects that match the arguments.
131 """ 131 """
132 all_projects_list = self.manifest.projects 132 all_projects_list = self.manifest.projects
@@ -134,7 +134,8 @@ class Command(object):
134 134
135 mp = self.manifest.manifestProject 135 mp = self.manifest.manifestProject
136 136
137 groups = mp.config.GetString('manifest.groups') 137 if not groups:
138 groups = mp.config.GetString('manifest.groups')
138 if not groups: 139 if not groups:
139 groups = 'default,platform-' + platform.system().lower() 140 groups = 'default,platform-' + platform.system().lower()
140 groups = [x for x in re.split(r'[,\s]+', groups) if x] 141 groups = [x for x in re.split(r'[,\s]+', groups) if x]
diff --git a/gitc_utils.py b/gitc_utils.py
new file mode 100644
index 00000000..bf79bd28
--- /dev/null
+++ b/gitc_utils.py
@@ -0,0 +1,69 @@
1#
2# Copyright (C) 2015 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
16from __future__ import print_function
17import os
18import shutil
19
20import git_command
21import git_config
22
23
24# TODO (sbasi) - Remove this constant and fetch manifest dir from /gitc/.config
25GITC_MANIFEST_DIR = '/usr/local/google/gitc/'
26GITC_FS_ROOT_DIR = '/gitc/manifest-rw/'
27NUM_BATCH_RETRIEVE_REVISIONID = 300
28
29def _set_project_revisions(projects):
30 """Sets the revisionExpr for a list of projects.
31
32 Because of the limit of open file descriptors allowed, length of projects
33 should not be overly large. Recommend calling this function multiple times
34 with each call not exceeding NUM_BATCH_RETRIEVE_REVISIONID projects.
35
36 @param projects: List of project objects to set the revionExpr for.
37 """
38 # Retrieve the commit id for each project based off of it's current
39 # revisionExpr and it is not already a commit id.
40 project_gitcmds = [(
41 project, git_command.GitCommand(None,
42 ['ls-remote',
43 project.remote.url,
44 project.revisionExpr],
45 capture_stdout=True, cwd='/tmp'))
46 for project in projects if not git_config.IsId(project.revisionExpr)]
47 for proj, gitcmd in project_gitcmds:
48 if gitcmd.Wait():
49 print('FATAL: Failed to retrieve revisionExpr for %s' % project)
50 sys.exit(1)
51 proj.revisionExpr = gitcmd.stdout.split('\t')[0]
52
53def generate_gitc_manifest(client_dir, manifest):
54 """Generate a manifest for shafsd to use for this GITC client.
55
56 @param client_dir: GITC client directory to install the .manifest file in.
57 @param manifest: XmlManifest object representing the repo manifest.
58 """
59 print('Generating GITC Manifest by fetching revision SHAs for each '
60 'project.')
61 project_gitcmd_dict = {}
62 index = 0
63 while index < len(manifest.projects):
64 _set_project_revisions(
65 manifest.projects[index:(index+NUM_BATCH_RETRIEVE_REVISIONID)])
66 index += NUM_BATCH_RETRIEVE_REVISIONID
67 # Save the manifest.
68 with open(os.path.join(client_dir, '.manifest'), 'w') as f:
69 manifest.Save(f)
diff --git a/manifest_xml.py b/manifest_xml.py
index 7e719600..6dc01a47 100644
--- a/manifest_xml.py
+++ b/manifest_xml.py
@@ -303,6 +303,9 @@ class XmlManifest(object):
303 if p.sync_s: 303 if p.sync_s:
304 e.setAttribute('sync-s', 'true') 304 e.setAttribute('sync-s', 'true')
305 305
306 if p.clone_depth:
307 e.setAttribute('clone-depth', str(p.clone_depth))
308
306 if p.subprojects: 309 if p.subprojects:
307 subprojects = set(subp.name for subp in p.subprojects) 310 subprojects = set(subp.name for subp in p.subprojects)
308 output_projects(p, e, list(sorted(subprojects))) 311 output_projects(p, e, list(sorted(subprojects)))
diff --git a/project.py b/project.py
index fb9df2aa..82fa8487 100644
--- a/project.py
+++ b/project.py
@@ -1877,6 +1877,13 @@ class Project(object):
1877 1877
1878 if depth: 1878 if depth:
1879 cmd.append('--depth=%s' % depth) 1879 cmd.append('--depth=%s' % depth)
1880 else:
1881 # If this repo has shallow objects, then we don't know which refs have
1882 # shallow objects or not. Tell git to unshallow all fetched refs. Don't
1883 # do this with projects that don't have shallow objects, since it is less
1884 # efficient.
1885 if os.path.exists(os.path.join(self.gitdir, 'shallow')):
1886 cmd.append('--depth=2147483647')
1880 1887
1881 if quiet: 1888 if quiet:
1882 cmd.append('--quiet') 1889 cmd.append('--quiet')
@@ -1914,16 +1921,6 @@ class Project(object):
1914 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch))) 1921 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch)))
1915 cmd.extend(spec) 1922 cmd.extend(spec)
1916 1923
1917 shallowfetch = self.config.GetString('repo.shallowfetch')
1918 if shallowfetch and shallowfetch != ' '.join(spec):
1919 GitCommand(self, ['fetch', '--depth=2147483647', name]
1920 + shallowfetch.split(),
1921 bare=True, ssh_proxy=ssh_proxy).Wait()
1922 if depth:
1923 self.config.SetString('repo.shallowfetch', ' '.join(spec))
1924 else:
1925 self.config.SetString('repo.shallowfetch', None)
1926
1927 ok = False 1924 ok = False
1928 for _i in range(2): 1925 for _i in range(2):
1929 gitcmd = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy) 1926 gitcmd = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy)
diff --git a/repo b/repo
index f12354a4..bf8fa3dc 100755
--- a/repo
+++ b/repo
@@ -108,6 +108,7 @@ S_repo = 'repo' # special repo repository
108S_manifests = 'manifests' # special manifest repository 108S_manifests = 'manifests' # special manifest repository
109REPO_MAIN = S_repo + '/main.py' # main script 109REPO_MAIN = S_repo + '/main.py' # main script
110MIN_PYTHON_VERSION = (2, 6) # minimum supported python version 110MIN_PYTHON_VERSION = (2, 6) # minimum supported python version
111GITC_MANIFEST_DIR = '/usr/local/google/gitc'
111 112
112 113
113import errno 114import errno
@@ -212,14 +213,25 @@ group.add_option('--config-name',
212 dest='config_name', action="store_true", default=False, 213 dest='config_name', action="store_true", default=False,
213 help='Always prompt for name/e-mail') 214 help='Always prompt for name/e-mail')
214 215
216def _GitcInitOptions(init_optparse):
217 g = init_optparse.add_option_group('GITC options')
218 g.add_option('-f', '--manifest-file',
219 dest='manifest_file',
220 help='Optional manifest file to use for this GITC client.')
221 g.add_option('-c', '--gitc-client',
222 dest='gitc_client',
223 help='The name for the new gitc_client instance.')
224
215class CloneFailure(Exception): 225class CloneFailure(Exception):
216 """Indicate the remote clone of repo itself failed. 226 """Indicate the remote clone of repo itself failed.
217 """ 227 """
218 228
219 229
220def _Init(args): 230def _Init(args, gitc_init=False):
221 """Installs repo by cloning it over the network. 231 """Installs repo by cloning it over the network.
222 """ 232 """
233 if gitc_init:
234 _GitcInitOptions(init_optparse)
223 opt, args = init_optparse.parse_args(args) 235 opt, args = init_optparse.parse_args(args)
224 if args: 236 if args:
225 init_optparse.print_usage() 237 init_optparse.print_usage()
@@ -242,6 +254,15 @@ def _Init(args):
242 raise CloneFailure() 254 raise CloneFailure()
243 255
244 try: 256 try:
257 if gitc_init:
258 client_dir = os.path.join(GITC_MANIFEST_DIR, opt.gitc_client)
259 if not os.path.exists(client_dir):
260 os.makedirs(client_dir)
261 os.chdir(client_dir)
262 if os.path.exists(repodir):
263 # This GITC Client has already initialized repo so continue.
264 return
265
245 os.mkdir(repodir) 266 os.mkdir(repodir)
246 except OSError as e: 267 except OSError as e:
247 if e.errno != errno.EEXIST: 268 if e.errno != errno.EEXIST:
@@ -732,11 +753,11 @@ def main(orig_args):
732 _Help(args) 753 _Help(args)
733 if not cmd: 754 if not cmd:
734 _NotInstalled() 755 _NotInstalled()
735 if cmd == 'init': 756 if cmd == 'init' or cmd == 'gitc-init':
736 if my_git: 757 if my_git:
737 _SetDefaultsTo(my_git) 758 _SetDefaultsTo(my_git)
738 try: 759 try:
739 _Init(args) 760 _Init(args, gitc_init=(cmd == 'gitc-init'))
740 except CloneFailure: 761 except CloneFailure:
741 shutil.rmtree(os.path.join(repodir, S_repo), ignore_errors=True) 762 shutil.rmtree(os.path.join(repodir, S_repo), ignore_errors=True)
742 sys.exit(1) 763 sys.exit(1)
diff --git a/subcmds/forall.py b/subcmds/forall.py
index b93cd6d0..96dc99d1 100644
--- a/subcmds/forall.py
+++ b/subcmds/forall.py
@@ -120,6 +120,9 @@ without iterating through the remaining projects.
120 p.add_option('-r', '--regex', 120 p.add_option('-r', '--regex',
121 dest='regex', action='store_true', 121 dest='regex', action='store_true',
122 help="Execute the command only on projects matching regex or wildcard expression") 122 help="Execute the command only on projects matching regex or wildcard expression")
123 p.add_option('-g', '--groups',
124 dest='groups',
125 help="Execute the command only on projects matching the specified groups")
123 p.add_option('-c', '--command', 126 p.add_option('-c', '--command',
124 help='Command (and arguments) to execute', 127 help='Command (and arguments) to execute',
125 dest='command', 128 dest='command',
@@ -213,7 +216,7 @@ without iterating through the remaining projects.
213 self.manifest.Override(smart_sync_manifest_path) 216 self.manifest.Override(smart_sync_manifest_path)
214 217
215 if not opt.regex: 218 if not opt.regex:
216 projects = self.GetProjects(args) 219 projects = self.GetProjects(args, groups=opt.groups)
217 else: 220 else:
218 projects = self.FindProjects(args) 221 projects = self.FindProjects(args)
219 222
diff --git a/subcmds/gitc_init.py b/subcmds/gitc_init.py
new file mode 100644
index 00000000..03d8cc30
--- /dev/null
+++ b/subcmds/gitc_init.py
@@ -0,0 +1,80 @@
1#
2# Copyright (C) 2015 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
16from __future__ import print_function
17import os
18import shutil
19import sys
20
21import gitc_utils
22from subcmds import init
23
24
25class GitcInit(init.Init):
26 common = True
27 helpSummary = "Initialize a GITC Client."
28 helpUsage = """
29%prog [options] [client name]
30"""
31 helpDescription = """
32The '%prog' command is ran to initialize a new GITC client for use
33with the GITC file system.
34
35This command will setup the client directory, initialize repo, just
36like repo init does, and then downloads the manifest collection
37and installs in in the .repo/directory of the GITC client.
38
39Once this is done, a GITC manifest is generated by pulling the HEAD
40SHA for each project and generates the properly formatted XML file
41and installs it as .manifest in the GITC client directory.
42
43The -c argument is required to specify the GITC client name.
44
45The optional -f argument can be used to specify the manifest file to
46use for this GITC client.
47"""
48
49 def _Options(self, p):
50 super(GitcInit, self)._Options(p)
51 g = p.add_option_group('GITC options')
52 g.add_option('-f', '--manifest-file',
53 dest='manifest_file',
54 help='Optional manifest file to use for this GITC client.')
55 g.add_option('-c', '--gitc-client',
56 dest='gitc_client',
57 help='The name for the new gitc_client instance.')
58
59 def Execute(self, opt, args):
60 if not opt.gitc_client:
61 print('fatal: gitc client (-c) is required', file=sys.stderr)
62 sys.exit(1)
63 self.client_dir = os.path.join(gitc_utils.GITC_MANIFEST_DIR,
64 opt.gitc_client)
65 if not os.path.exists(gitc_utils.GITC_MANIFEST_DIR):
66 os.makedirs(gitc_utils.GITC_MANIFEST_DIR)
67 if not os.path.exists(self.client_dir):
68 os.mkdir(self.client_dir)
69 super(GitcInit, self).Execute(opt, args)
70 # Make the destination manifest file a symlink to repo's so both repo and
71 # GITC refer to the same manifest.
72 if opt.manifest_file:
73 if not os.path.exists(opt.manifest_file):
74 print('fatal: Specified manifest file %s does not exist.' %
75 opt.manifest_file)
76 sys.exit(1)
77 self.manifest.Override(opt.manifest_file)
78 gitc_utils.generate_gitc_manifest(self.client_dir, self.manifest)
79 print('Please run `cd %s` to view your GITC client.' %
80 os.path.join(gitc_utils.GITC_FS_ROOT_DIR, opt.gitc_client)) \ No newline at end of file
diff --git a/subcmds/list.py b/subcmds/list.py
index 945c28d8..ca51c5f7 100644
--- a/subcmds/list.py
+++ b/subcmds/list.py
@@ -35,6 +35,9 @@ This is similar to running: repo forall -c 'echo "$REPO_PATH : $REPO_PROJECT"'.
35 p.add_option('-r', '--regex', 35 p.add_option('-r', '--regex',
36 dest='regex', action='store_true', 36 dest='regex', action='store_true',
37 help="Filter the project list based on regex or wildcard matching of strings") 37 help="Filter the project list based on regex or wildcard matching of strings")
38 p.add_option('-g', '--groups',
39 dest='groups',
40 help="Filter the project list based on the groups the project is in")
38 p.add_option('-f', '--fullpath', 41 p.add_option('-f', '--fullpath',
39 dest='fullpath', action='store_true', 42 dest='fullpath', action='store_true',
40 help="Display the full work tree path instead of the relative path") 43 help="Display the full work tree path instead of the relative path")
@@ -62,7 +65,7 @@ This is similar to running: repo forall -c 'echo "$REPO_PATH : $REPO_PROJECT"'.
62 sys.exit(1) 65 sys.exit(1)
63 66
64 if not opt.regex: 67 if not opt.regex:
65 projects = self.GetProjects(args) 68 projects = self.GetProjects(args, groups=opt.groups)
66 else: 69 else:
67 projects = self.FindProjects(args) 70 projects = self.FindProjects(args)
68 71
diff --git a/subcmds/sync.py b/subcmds/sync.py
index 43d450be..652a0c0d 100644
--- a/subcmds/sync.py
+++ b/subcmds/sync.py
@@ -58,6 +58,7 @@ except ImportError:
58 58
59from git_command import GIT, git_require 59from git_command import GIT, git_require
60from git_refs import R_HEADS, HEAD 60from git_refs import R_HEADS, HEAD
61import gitc_utils
61from project import Project 62from project import Project
62from project import RemoteSpec 63from project import RemoteSpec
63from command import Command, MirrorSafeCommand 64from command import Command, MirrorSafeCommand
@@ -184,6 +185,9 @@ later is required to fix a server side protocol bug.
184 help="overwrite an existing git directory if it needs to " 185 help="overwrite an existing git directory if it needs to "
185 "point to a different object directory. WARNING: this " 186 "point to a different object directory. WARNING: this "
186 "may cause loss of data") 187 "may cause loss of data")
188 p.add_option('--force-gitc',
189 dest='force_gitc', action='store_true',
190 help="actually sync sources in the gitc client directory.")
187 p.add_option('-l', '--local-only', 191 p.add_option('-l', '--local-only',
188 dest='local_only', action='store_true', 192 dest='local_only', action='store_true',
189 help="only update working tree, don't fetch") 193 help="only update working tree, don't fetch")
@@ -526,6 +530,25 @@ later is required to fix a server side protocol bug.
526 print('error: both -u and -p must be given', file=sys.stderr) 530 print('error: both -u and -p must be given', file=sys.stderr)
527 sys.exit(1) 531 sys.exit(1)
528 532
533 cwd = os.getcwd()
534 if cwd.startswith(gitc_utils.GITC_MANIFEST_DIR) and not opt.force_gitc:
535 print('WARNING this will pull all the sources like a normal repo sync.\n'
536 '\nIf you want to update your GITC Client View please rerun this '
537 'command in \n%s%s.\nOr if you actually want to pull the sources, '
538 'rerun with --force-gitc.' %
539 (gitc_utils.GITC_FS_ROOT_DIR,
540 cwd.split(gitc_utils.GITC_MANIFEST_DIR)[1]))
541 sys.exit(1)
542
543 self._gitc_sync = False
544 if cwd.startswith(gitc_utils.GITC_FS_ROOT_DIR):
545 self._gitc_sync = True
546 self._client_name = cwd.split(gitc_utils.GITC_FS_ROOT_DIR)[1].split(
547 '/')[0]
548 self._client_dir = os.path.join(gitc_utils.GITC_MANIFEST_DIR,
549 self._client_name)
550 print('Updating GITC client: %s' % self._client_name)
551
529 if opt.manifest_name: 552 if opt.manifest_name:
530 self.manifest.Override(opt.manifest_name) 553 self.manifest.Override(opt.manifest_name)
531 554
@@ -642,6 +665,12 @@ later is required to fix a server side protocol bug.
642 if opt.repo_upgraded: 665 if opt.repo_upgraded:
643 _PostRepoUpgrade(self.manifest, quiet=opt.quiet) 666 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
644 667
668 if self._gitc_sync:
669 gitc_utils.generate_gitc_manifest(self._client_dir, self.manifest)
670 print('GITC client successfully synced.')
671 return
672
673
645 if not opt.local_only: 674 if not opt.local_only:
646 mp.Sync_NetworkHalf(quiet=opt.quiet, 675 mp.Sync_NetworkHalf(quiet=opt.quiet,
647 current_branch_only=opt.current_branch_only, 676 current_branch_only=opt.current_branch_only,