blob: c61c3bb49350848f356a02562d343cf848ba5771 [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
Shawn O. Pearcef6906872009-04-18 10:49:00 -070021import time
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070022
23from git_command import GIT
Shawn O. Pearcee756c412009-04-13 11:51:15 -070024from project import HEAD
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080025from command import Command, MirrorSafeCommand
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070026from error import RepoChangedException, GitError
27from project import R_HEADS
Shawn O. Pearce350cde42009-04-16 11:21:18 -070028from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070029from progress import Progress
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070030
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080031class Sync(Command, MirrorSafeCommand):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070032 common = True
33 helpSummary = "Update working tree to the latest revision"
34 helpUsage = """
35%prog [<project>...]
36"""
37 helpDescription = """
38The '%prog' command synchronizes local project directories
39with the remote repositories specified in the manifest. If a local
40project does not yet exist, it will clone a new local directory from
41the remote repository and set up tracking branches as specified in
42the manifest. If the local project already exists, '%prog'
43will update the remote branches and rebase any new local changes
44on top of the new remote changes.
45
46'%prog' will synchronize all projects listed at the command
47line. Projects can be specified either by name, or by a relative
48or absolute path to the project's local directory. If no projects
49are specified, '%prog' will synchronize all projects listed in
50the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -070051
52The -d/--detach option can be used to switch specified projects
53back to the manifest revision. This option is especially helpful
54if the project is currently on a topic branch, but the manifest
55revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -070056
57SSH Connections
58---------------
59
60If at least one project remote URL uses an SSH connection (ssh://,
61git+ssh://, or user@host:path syntax) repo will automatically
62enable the SSH ControlMaster option when connecting to that host.
63This feature permits other projects in the same '%prog' session to
64reuse the same SSH tunnel, saving connection setup overheads.
65
66To disable this behavior on UNIX platforms, set the GIT_SSH
67environment variable to 'ssh'. For example:
68
69 export GIT_SSH=ssh
70 %prog
71
72Compatibility
73~~~~~~~~~~~~~
74
75This feature is automatically disabled on Windows, due to the lack
76of UNIX domain socket support.
77
78This feature is not compatible with url.insteadof rewrites in the
79user's ~/.gitconfig. '%prog' is currently not able to perform the
80rewrite early enough to establish the ControlMaster tunnel.
81
82If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
83later is required to fix a server side protocol bug.
84
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070085"""
86
87 def _Options(self, p):
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -070088 p.add_option('-l','--local-only',
89 dest='local_only', action='store_true',
90 help="only update working tree, don't fetch")
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -070091 p.add_option('-n','--network-only',
92 dest='network_only', action='store_true',
93 help="fetch only, don't update working tree")
Shawn O. Pearce3e768c92009-04-10 16:59:36 -070094 p.add_option('-d','--detach',
95 dest='detach_head', action='store_true',
96 help='detach projects back to manifest revision')
97
Shawn O. Pearcefd89b672009-04-18 11:28:57 -070098 g = p.add_option_group('repo Version options')
99 g.add_option('--no-repo-verify',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700100 dest='no_repo_verify', action='store_true',
101 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700102 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800103 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700104 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700105
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700106 def _Fetch(self, projects):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700107 fetched = set()
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700108 pm = Progress('Fetching projects', len(projects))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700109 for project in projects:
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700110 pm.update()
111
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700112 if project.Sync_NetworkHalf():
113 fetched.add(project.gitdir)
114 else:
115 print >>sys.stderr, 'error: Cannot fetch %s' % project.name
116 sys.exit(1)
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700117 pm.end()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700118 return fetched
119
120 def Execute(self, opt, args):
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700121 if opt.network_only and opt.detach_head:
122 print >>sys.stderr, 'error: cannot combine -n and -d'
123 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700124 if opt.network_only and opt.local_only:
125 print >>sys.stderr, 'error: cannot combine -n and -l'
126 sys.exit(1)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700127
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700128 rp = self.manifest.repoProject
129 rp.PreSync()
130
131 mp = self.manifest.manifestProject
132 mp.PreSync()
133
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800134 if opt.repo_upgraded:
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700135 _PostRepoUpgrade(self.manifest)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800136
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700137 all = self.GetProjects(args, missing_ok=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700138
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700139 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700140 to_fetch = []
141 now = time.time()
142 if (24 * 60 * 60) <= (now - rp.LastFetch):
143 to_fetch.append(rp)
144 to_fetch.append(mp)
145 to_fetch.extend(all)
146
147 fetched = self._Fetch(to_fetch)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700148 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700149 if opt.network_only:
150 # bail out now; the rest touches the working tree
151 return
152
153 if mp.HasChanges:
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700154 syncbuf = SyncBuffer(mp.config)
155 mp.Sync_LocalHalf(syncbuf)
156 if not syncbuf.Finish():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700157 sys.exit(1)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700158
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700159 self.manifest._Unload()
160 all = self.GetProjects(args, missing_ok=True)
161 missing = []
162 for project in all:
163 if project.gitdir not in fetched:
164 missing.append(project)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700165 self._Fetch(missing)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700166
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700167 syncbuf = SyncBuffer(mp.config,
168 detach_head = opt.detach_head)
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700169 pm = Progress('Syncing work tree', len(all))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700170 for project in all:
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700171 pm.update()
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800172 if project.worktree:
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700173 project.Sync_LocalHalf(syncbuf)
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700174 pm.end()
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700175 print >>sys.stderr
176 if not syncbuf.Finish():
177 sys.exit(1)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700178
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700179
180def _PostRepoUpgrade(manifest):
181 for project in manifest.projects.values():
182 if project.Exists:
183 project.PostRepoUpgrade()
184
185def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
186 if rp.HasChanges:
187 print >>sys.stderr, 'info: A new version of repo is available'
188 print >>sys.stderr, ''
189 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700190 syncbuf = SyncBuffer(rp.config)
191 rp.Sync_LocalHalf(syncbuf)
192 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700193 sys.exit(1)
194 print >>sys.stderr, 'info: Restarting repo with latest version'
195 raise RepoChangedException(['--repo-upgraded'])
196 else:
197 print >>sys.stderr, 'warning: Skipped upgrade to unverified version'
198 else:
199 if verbose:
200 print >>sys.stderr, 'repo version %s is current' % rp.work_git.describe(HEAD)
201
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700202def _VerifyTag(project):
203 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
204 if not os.path.exists(gpg_dir):
205 print >>sys.stderr,\
206"""warning: GnuPG was not available during last "repo init"
207warning: Cannot automatically authenticate repo."""
208 return True
209
210 remote = project.GetRemote(project.remote.name)
211 ref = remote.ToLocal(project.revision)
212
213 try:
214 cur = project.bare_git.describe(ref)
215 except GitError:
216 cur = None
217
218 if not cur \
219 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
220 rev = project.revision
221 if rev.startswith(R_HEADS):
222 rev = rev[len(R_HEADS):]
223
224 print >>sys.stderr
225 print >>sys.stderr,\
226 "warning: project '%s' branch '%s' is not signed" \
227 % (project.name, rev)
228 return False
229
230 env = dict(os.environ)
231 env['GIT_DIR'] = project.gitdir
232 env['GNUPGHOME'] = gpg_dir
233
234 cmd = [GIT, 'tag', '-v', cur]
235 proc = subprocess.Popen(cmd,
236 stdout = subprocess.PIPE,
237 stderr = subprocess.PIPE,
238 env = env)
239 out = proc.stdout.read()
240 proc.stdout.close()
241
242 err = proc.stderr.read()
243 proc.stderr.close()
244
245 if proc.wait() != 0:
246 print >>sys.stderr
247 print >>sys.stderr, out
248 print >>sys.stderr, err
249 print >>sys.stderr
250 return False
251 return True