blob: f6eb2a08a8926cda790248ddf78535bb119cc23f [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001#
2# Copyright (C) 2008 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
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -070016from optparse import SUPPRESS_HELP
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070017import os
18import re
19import subprocess
20import sys
21
22from git_command import GIT
Shawn O. Pearcee756c412009-04-13 11:51:15 -070023from project import HEAD
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080024from command import Command, MirrorSafeCommand
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070025from error import RepoChangedException, GitError
26from project import R_HEADS
Shawn O. Pearce68194f42009-04-10 16:48:52 -070027from progress import Progress
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070028
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080029class Sync(Command, MirrorSafeCommand):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070030 common = True
31 helpSummary = "Update working tree to the latest revision"
32 helpUsage = """
33%prog [<project>...]
34"""
35 helpDescription = """
36The '%prog' command synchronizes local project directories
37with the remote repositories specified in the manifest. If a local
38project does not yet exist, it will clone a new local directory from
39the remote repository and set up tracking branches as specified in
40the manifest. If the local project already exists, '%prog'
41will update the remote branches and rebase any new local changes
42on top of the new remote changes.
43
44'%prog' will synchronize all projects listed at the command
45line. Projects can be specified either by name, or by a relative
46or absolute path to the project's local directory. If no projects
47are specified, '%prog' will synchronize all projects listed in
48the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -070049
50The -d/--detach option can be used to switch specified projects
51back to the manifest revision. This option is especially helpful
52if the project is currently on a topic branch, but the manifest
53revision is temporarily needed.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070054"""
55
56 def _Options(self, p):
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -070057 p.add_option('-l','--local-only',
58 dest='local_only', action='store_true',
59 help="only update working tree, don't fetch")
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -070060 p.add_option('-n','--network-only',
61 dest='network_only', action='store_true',
62 help="fetch only, don't update working tree")
Shawn O. Pearce3e768c92009-04-10 16:59:36 -070063 p.add_option('-d','--detach',
64 dest='detach_head', action='store_true',
65 help='detach projects back to manifest revision')
66
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070067 p.add_option('--no-repo-verify',
68 dest='no_repo_verify', action='store_true',
69 help='do not verify repo source code')
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -080070 p.add_option('--repo-upgraded',
71 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -070072 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070073
74 def _Fetch(self, *projects):
75 fetched = set()
Shawn O. Pearce68194f42009-04-10 16:48:52 -070076 pm = Progress('Fetching projects', len(projects))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070077 for project in projects:
Shawn O. Pearce68194f42009-04-10 16:48:52 -070078 pm.update()
79
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070080 if project.Sync_NetworkHalf():
81 fetched.add(project.gitdir)
82 else:
83 print >>sys.stderr, 'error: Cannot fetch %s' % project.name
84 sys.exit(1)
Shawn O. Pearce68194f42009-04-10 16:48:52 -070085 pm.end()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070086 return fetched
87
88 def Execute(self, opt, args):
Shawn O. Pearce3e768c92009-04-10 16:59:36 -070089 if opt.network_only and opt.detach_head:
90 print >>sys.stderr, 'error: cannot combine -n and -d'
91 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -070092 if opt.network_only and opt.local_only:
93 print >>sys.stderr, 'error: cannot combine -n and -l'
94 sys.exit(1)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -070095
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070096 rp = self.manifest.repoProject
97 rp.PreSync()
98
99 mp = self.manifest.manifestProject
100 mp.PreSync()
101
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800102 if opt.repo_upgraded:
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700103 _PostRepoUpgrade(self.manifest)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800104
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700105 all = self.GetProjects(args, missing_ok=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700106
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700107 if not opt.local_only:
108 fetched = self._Fetch(rp, mp, *all)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700109 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700110 if opt.network_only:
111 # bail out now; the rest touches the working tree
112 return
113
114 if mp.HasChanges:
115 if not mp.Sync_LocalHalf():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700116 sys.exit(1)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700117
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700118 self.manifest._Unload()
119 all = self.GetProjects(args, missing_ok=True)
120 missing = []
121 for project in all:
122 if project.gitdir not in fetched:
123 missing.append(project)
124 self._Fetch(*missing)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700125
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700126 pm = Progress('Syncing work tree', len(all))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700127 for project in all:
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700128 pm.update()
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800129 if project.worktree:
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700130 if not project.Sync_LocalHalf(
131 detach_head=opt.detach_head):
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800132 sys.exit(1)
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700133 pm.end()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700134
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700135
136def _PostRepoUpgrade(manifest):
137 for project in manifest.projects.values():
138 if project.Exists:
139 project.PostRepoUpgrade()
140
141def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
142 if rp.HasChanges:
143 print >>sys.stderr, 'info: A new version of repo is available'
144 print >>sys.stderr, ''
145 if no_repo_verify or _VerifyTag(rp):
146 if not rp.Sync_LocalHalf():
147 sys.exit(1)
148 print >>sys.stderr, 'info: Restarting repo with latest version'
149 raise RepoChangedException(['--repo-upgraded'])
150 else:
151 print >>sys.stderr, 'warning: Skipped upgrade to unverified version'
152 else:
153 if verbose:
154 print >>sys.stderr, 'repo version %s is current' % rp.work_git.describe(HEAD)
155
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700156def _VerifyTag(project):
157 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
158 if not os.path.exists(gpg_dir):
159 print >>sys.stderr,\
160"""warning: GnuPG was not available during last "repo init"
161warning: Cannot automatically authenticate repo."""
162 return True
163
164 remote = project.GetRemote(project.remote.name)
165 ref = remote.ToLocal(project.revision)
166
167 try:
168 cur = project.bare_git.describe(ref)
169 except GitError:
170 cur = None
171
172 if not cur \
173 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
174 rev = project.revision
175 if rev.startswith(R_HEADS):
176 rev = rev[len(R_HEADS):]
177
178 print >>sys.stderr
179 print >>sys.stderr,\
180 "warning: project '%s' branch '%s' is not signed" \
181 % (project.name, rev)
182 return False
183
184 env = dict(os.environ)
185 env['GIT_DIR'] = project.gitdir
186 env['GNUPGHOME'] = gpg_dir
187
188 cmd = [GIT, 'tag', '-v', cur]
189 proc = subprocess.Popen(cmd,
190 stdout = subprocess.PIPE,
191 stderr = subprocess.PIPE,
192 env = env)
193 out = proc.stdout.read()
194 proc.stdout.close()
195
196 err = proc.stderr.read()
197 proc.stderr.close()
198
199 if proc.wait() != 0:
200 print >>sys.stderr
201 print >>sys.stderr, out
202 print >>sys.stderr, err
203 print >>sys.stderr
204 return False
205 return True