blob: 6ef7d3d3731a6b58af63c39177ff0a2cb9fb9ce6 [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001# Copyright (C) 2008 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
Sarah Owenscecd1d82012-11-01 22:59:27 -070015from __future__ import print_function
Shawn O. Pearce438ee1c2008-11-03 09:59:36 -080016import errno
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070017import filecmp
Wink Saville4c426ef2015-06-03 08:05:17 -070018import glob
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070019import os
Shawn O. Pearcec325dc32011-10-03 08:30:24 -070020import random
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070021import re
22import shutil
23import stat
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -070024import subprocess
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070025import sys
Julien Campergue335f5ef2013-10-16 11:02:35 +020026import tarfile
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +080027import tempfile
Shawn O. Pearcec325dc32011-10-03 08:30:24 -070028import time
Dave Borowitz137d0132015-01-02 11:12:54 -080029import traceback
Shawn O. Pearcedf5ee522011-10-11 14:05:21 -070030
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070031from color import Coloring
Dave Borowitzb42b4742012-10-31 12:27:27 -070032from git_command import GitCommand, git_require
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070033from git_config import GitConfig, IsId, GetSchemeFromUrl, GetUrlCookieFile, \
34 ID_RE
Kevin Degiabaa7f32014-11-12 11:27:45 -070035from error import GitError, HookError, UploadError, DownloadError
Shawn O. Pearce559b8462009-03-02 12:56:08 -080036from error import ManifestInvalidRevisionError
Conley Owens75ee0572012-11-15 17:33:11 -080037from error import NoManifestException
Renaud Paquayd5cec5e2016-11-01 11:24:03 -070038import platform_utils
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -070039from trace import IsTrace, Trace
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070040
Shawn O. Pearced237b692009-04-17 18:49:50 -070041from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070042
David Pursehouse59bbb582013-05-17 10:49:33 +090043from pyversion import is_python3
Mike Frysinger40252c22016-08-15 21:23:44 -040044if is_python3():
45 import urllib.parse
46else:
47 import imp
48 import urlparse
49 urllib = imp.new_module('urllib')
50 urllib.parse = urlparse
David Pursehouse59bbb582013-05-17 10:49:33 +090051 # pylint:disable=W0622
Chirayu Desai217ea7d2013-03-01 19:14:38 +053052 input = raw_input
David Pursehouse59bbb582013-05-17 10:49:33 +090053 # pylint:enable=W0622
Chirayu Desai217ea7d2013-03-01 19:14:38 +053054
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070055
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070056def _lwrite(path, content):
57 lock = '%s.lock' % path
58
Chirayu Desai303a82f2014-08-19 22:57:17 +053059 fd = open(lock, 'w')
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070060 try:
61 fd.write(content)
62 finally:
63 fd.close()
64
65 try:
Renaud Paquayad1abcb2016-11-01 11:34:55 -070066 platform_utils.rename(lock, path)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070067 except OSError:
Renaud Paquay010fed72016-11-11 14:25:29 -080068 platform_utils.remove(lock)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070069 raise
70
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070071
Shawn O. Pearce48244782009-04-16 08:25:57 -070072def _error(fmt, *args):
73 msg = fmt % args
Sarah Owenscecd1d82012-11-01 22:59:27 -070074 print('error: %s' % msg, file=sys.stderr)
Shawn O. Pearce48244782009-04-16 08:25:57 -070075
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070076
David Pursehousef33929d2015-08-24 14:39:14 +090077def _warn(fmt, *args):
78 msg = fmt % args
79 print('warn: %s' % msg, file=sys.stderr)
80
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070081
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070082def not_rev(r):
83 return '^' + r
84
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070085
Shawn O. Pearceb54a3922009-01-05 16:18:58 -080086def sq(r):
87 return "'" + r.replace("'", "'\''") + "'"
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -080088
Jonathan Nieder93719792015-03-17 11:29:58 -070089_project_hook_list = None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070090
91
Jonathan Nieder93719792015-03-17 11:29:58 -070092def _ProjectHooks():
93 """List the hooks present in the 'hooks' directory.
94
95 These hooks are project hooks and are copied to the '.git/hooks' directory
96 of all subprojects.
97
98 This function caches the list of hooks (based on the contents of the
99 'repo/hooks' directory) on the first call.
100
101 Returns:
102 A list of absolute paths to all of the files in the hooks directory.
103 """
104 global _project_hook_list
105 if _project_hook_list is None:
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700106 d = platform_utils.realpath(os.path.abspath(os.path.dirname(__file__)))
Jonathan Nieder93719792015-03-17 11:29:58 -0700107 d = os.path.join(d, 'hooks')
108 _project_hook_list = [os.path.join(d, x) for x in os.listdir(d)]
109 return _project_hook_list
110
111
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700112class DownloadedChange(object):
113 _commit_cache = None
114
115 def __init__(self, project, base, change_id, ps_id, commit):
116 self.project = project
117 self.base = base
118 self.change_id = change_id
119 self.ps_id = ps_id
120 self.commit = commit
121
122 @property
123 def commits(self):
124 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700125 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
126 '--abbrev-commit',
127 '--pretty=oneline',
128 '--reverse',
129 '--date-order',
130 not_rev(self.base),
131 self.commit,
132 '--')
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700133 return self._commit_cache
134
135
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700136class ReviewableBranch(object):
137 _commit_cache = None
138
139 def __init__(self, project, branch, base):
140 self.project = project
141 self.branch = branch
142 self.base = base
143
144 @property
145 def name(self):
146 return self.branch.name
147
148 @property
149 def commits(self):
150 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700151 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
152 '--abbrev-commit',
153 '--pretty=oneline',
154 '--reverse',
155 '--date-order',
156 not_rev(self.base),
157 R_HEADS + self.name,
158 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700159 return self._commit_cache
160
161 @property
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800162 def unabbrev_commits(self):
163 r = dict()
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700164 for commit in self.project.bare_git.rev_list(not_rev(self.base),
165 R_HEADS + self.name,
166 '--'):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800167 r[commit[0:8]] = commit
168 return r
169
170 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700171 def date(self):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700172 return self.project.bare_git.log('--pretty=format:%cd',
173 '-n', '1',
174 R_HEADS + self.name,
175 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700176
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700177 def UploadForReview(self, people,
178 auto_topic=False,
Jonathan Niederc94d6eb2017-08-08 18:34:53 +0000179 draft=False,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200180 private=False,
181 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200182 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800183 validate_certs=True,
184 push_options=None):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800185 self.project.UploadForReview(self.name,
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700186 people,
Brian Harring435370c2012-07-28 15:37:04 -0700187 auto_topic=auto_topic,
Jonathan Niederc94d6eb2017-08-08 18:34:53 +0000188 draft=draft,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200189 private=private,
190 wip=wip,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200191 dest_branch=dest_branch,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800192 validate_certs=validate_certs,
193 push_options=push_options)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700194
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700195 def GetPublishedRefs(self):
196 refs = {}
197 output = self.project.bare_git.ls_remote(
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700198 self.branch.remote.SshReviewUrl(self.project.UserEmail),
199 'refs/changes/*')
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700200 for line in output.split('\n'):
201 try:
202 (sha, ref) = line.split()
203 refs[sha] = ref
204 except ValueError:
205 pass
206
207 return refs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700208
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700209
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700210class StatusColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700211
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700212 def __init__(self, config):
213 Coloring.__init__(self, config, 'status')
Anthony King7bdac712014-07-16 12:56:40 +0100214 self.project = self.printer('header', attr='bold')
215 self.branch = self.printer('header', attr='bold')
216 self.nobranch = self.printer('nobranch', fg='red')
217 self.important = self.printer('important', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700218
Anthony King7bdac712014-07-16 12:56:40 +0100219 self.added = self.printer('added', fg='green')
220 self.changed = self.printer('changed', fg='red')
221 self.untracked = self.printer('untracked', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700222
223
224class DiffColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700225
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700226 def __init__(self, config):
227 Coloring.__init__(self, config, 'diff')
Anthony King7bdac712014-07-16 12:56:40 +0100228 self.project = self.printer('header', attr='bold')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700229
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700230
Anthony King7bdac712014-07-16 12:56:40 +0100231class _Annotation(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700232
James W. Mills24c13082012-04-12 15:04:13 -0500233 def __init__(self, name, value, keep):
234 self.name = name
235 self.value = value
236 self.keep = keep
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700237
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700238
Anthony King7bdac712014-07-16 12:56:40 +0100239class _CopyFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700240
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800241 def __init__(self, src, dest, abssrc, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700242 self.src = src
243 self.dest = dest
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800244 self.abs_src = abssrc
245 self.abs_dest = absdest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700246
247 def _Copy(self):
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800248 src = self.abs_src
249 dest = self.abs_dest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700250 # copy file if it does not exist or is out of date
251 if not os.path.exists(dest) or not filecmp.cmp(src, dest):
252 try:
253 # remove existing file first, since it might be read-only
254 if os.path.exists(dest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800255 platform_utils.remove(dest)
Matthew Buckett2daf6672009-07-11 09:43:47 -0400256 else:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200257 dest_dir = os.path.dirname(dest)
258 if not os.path.isdir(dest_dir):
259 os.makedirs(dest_dir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700260 shutil.copy(src, dest)
261 # make the file read-only
262 mode = os.stat(dest)[stat.ST_MODE]
263 mode = mode & ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
264 os.chmod(dest, mode)
265 except IOError:
Shawn O. Pearce48244782009-04-16 08:25:57 -0700266 _error('Cannot copy file %s to %s', src, dest)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700267
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700268
Anthony King7bdac712014-07-16 12:56:40 +0100269class _LinkFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700270
Wink Saville4c426ef2015-06-03 08:05:17 -0700271 def __init__(self, git_worktree, src, dest, relsrc, absdest):
272 self.git_worktree = git_worktree
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500273 self.src = src
274 self.dest = dest
Colin Cross0184dcc2015-05-05 00:24:54 -0700275 self.src_rel_to_dest = relsrc
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500276 self.abs_dest = absdest
277
Wink Saville4c426ef2015-06-03 08:05:17 -0700278 def __linkIt(self, relSrc, absDest):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500279 # link file if it does not exist or is out of date
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700280 if not platform_utils.islink(absDest) or (platform_utils.readlink(absDest) != relSrc):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500281 try:
282 # remove existing file first, since it might be read-only
Dan Willemsene1e0bd12015-11-18 16:49:38 -0800283 if os.path.lexists(absDest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800284 platform_utils.remove(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500285 else:
Wink Saville4c426ef2015-06-03 08:05:17 -0700286 dest_dir = os.path.dirname(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500287 if not os.path.isdir(dest_dir):
288 os.makedirs(dest_dir)
Renaud Paquayd5cec5e2016-11-01 11:24:03 -0700289 platform_utils.symlink(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500290 except IOError:
Wink Saville4c426ef2015-06-03 08:05:17 -0700291 _error('Cannot link file %s to %s', relSrc, absDest)
292
293 def _Link(self):
294 """Link the self.rel_src_to_dest and self.abs_dest. Handles wild cards
295 on the src linking all of the files in the source in to the destination
296 directory.
297 """
298 # We use the absSrc to handle the situation where the current directory
299 # is not the root of the repo
300 absSrc = os.path.join(self.git_worktree, self.src)
301 if os.path.exists(absSrc):
302 # Entity exists so just a simple one to one link operation
303 self.__linkIt(self.src_rel_to_dest, self.abs_dest)
304 else:
305 # Entity doesn't exist assume there is a wild card
306 absDestDir = self.abs_dest
307 if os.path.exists(absDestDir) and not os.path.isdir(absDestDir):
308 _error('Link error: src with wildcard, %s must be a directory',
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700309 absDestDir)
Wink Saville4c426ef2015-06-03 08:05:17 -0700310 else:
311 absSrcFiles = glob.glob(absSrc)
312 for absSrcFile in absSrcFiles:
313 # Create a releative path from source dir to destination dir
314 absSrcDir = os.path.dirname(absSrcFile)
315 relSrcDir = os.path.relpath(absSrcDir, absDestDir)
316
317 # Get the source file name
318 srcFile = os.path.basename(absSrcFile)
319
320 # Now form the final full paths to srcFile. They will be
321 # absolute for the desintaiton and relative for the srouce.
322 absDest = os.path.join(absDestDir, srcFile)
323 relSrc = os.path.join(relSrcDir, srcFile)
324 self.__linkIt(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500325
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700326
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700327class RemoteSpec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700328
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700329 def __init__(self,
330 name,
Anthony King7bdac712014-07-16 12:56:40 +0100331 url=None,
Steve Raed6480452016-08-10 15:00:00 -0700332 pushUrl=None,
Anthony King7bdac712014-07-16 12:56:40 +0100333 review=None,
Dan Willemsen96c2d652016-04-06 16:03:54 -0700334 revision=None,
David Rileye0684ad2017-04-05 00:02:59 -0700335 orig_name=None,
336 fetchUrl=None):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700337 self.name = name
338 self.url = url
Steve Raed6480452016-08-10 15:00:00 -0700339 self.pushUrl = pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700340 self.review = review
Anthony King36ea2fb2014-05-06 11:54:01 +0100341 self.revision = revision
Dan Willemsen96c2d652016-04-06 16:03:54 -0700342 self.orig_name = orig_name
David Rileye0684ad2017-04-05 00:02:59 -0700343 self.fetchUrl = fetchUrl
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700344
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700345
Doug Anderson37282b42011-03-04 11:54:18 -0800346class RepoHook(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700347
Doug Anderson37282b42011-03-04 11:54:18 -0800348 """A RepoHook contains information about a script to run as a hook.
349
350 Hooks are used to run a python script before running an upload (for instance,
351 to run presubmit checks). Eventually, we may have hooks for other actions.
352
353 This shouldn't be confused with files in the 'repo/hooks' directory. Those
354 files are copied into each '.git/hooks' folder for each project. Repo-level
355 hooks are associated instead with repo actions.
356
357 Hooks are always python. When a hook is run, we will load the hook into the
358 interpreter and execute its main() function.
359 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700360
Doug Anderson37282b42011-03-04 11:54:18 -0800361 def __init__(self,
362 hook_type,
363 hooks_project,
364 topdir,
Mike Frysinger40252c22016-08-15 21:23:44 -0400365 manifest_url,
Doug Anderson37282b42011-03-04 11:54:18 -0800366 abort_if_user_denies=False):
367 """RepoHook constructor.
368
369 Params:
370 hook_type: A string representing the type of hook. This is also used
371 to figure out the name of the file containing the hook. For
372 example: 'pre-upload'.
373 hooks_project: The project containing the repo hooks. If you have a
374 manifest, this is manifest.repo_hooks_project. OK if this is None,
375 which will make the hook a no-op.
376 topdir: Repo's top directory (the one containing the .repo directory).
377 Scripts will run with CWD as this directory. If you have a manifest,
378 this is manifest.topdir
Mike Frysinger40252c22016-08-15 21:23:44 -0400379 manifest_url: The URL to the manifest git repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800380 abort_if_user_denies: If True, we'll throw a HookError() if the user
381 doesn't allow us to run the hook.
382 """
383 self._hook_type = hook_type
384 self._hooks_project = hooks_project
Mike Frysinger40252c22016-08-15 21:23:44 -0400385 self._manifest_url = manifest_url
Doug Anderson37282b42011-03-04 11:54:18 -0800386 self._topdir = topdir
387 self._abort_if_user_denies = abort_if_user_denies
388
389 # Store the full path to the script for convenience.
390 if self._hooks_project:
391 self._script_fullpath = os.path.join(self._hooks_project.worktree,
392 self._hook_type + '.py')
393 else:
394 self._script_fullpath = None
395
396 def _GetHash(self):
397 """Return a hash of the contents of the hooks directory.
398
399 We'll just use git to do this. This hash has the property that if anything
400 changes in the directory we will return a different has.
401
402 SECURITY CONSIDERATION:
403 This hash only represents the contents of files in the hook directory, not
404 any other files imported or called by hooks. Changes to imported files
405 can change the script behavior without affecting the hash.
406
407 Returns:
408 A string representing the hash. This will always be ASCII so that it can
409 be printed to the user easily.
410 """
411 assert self._hooks_project, "Must have hooks to calculate their hash."
412
413 # We will use the work_git object rather than just calling GetRevisionId().
414 # That gives us a hash of the latest checked in version of the files that
415 # the user will actually be executing. Specifically, GetRevisionId()
416 # doesn't appear to change even if a user checks out a different version
417 # of the hooks repo (via git checkout) nor if a user commits their own revs.
418 #
419 # NOTE: Local (non-committed) changes will not be factored into this hash.
420 # I think this is OK, since we're really only worried about warning the user
421 # about upstream changes.
422 return self._hooks_project.work_git.rev_parse('HEAD')
423
424 def _GetMustVerb(self):
425 """Return 'must' if the hook is required; 'should' if not."""
426 if self._abort_if_user_denies:
427 return 'must'
428 else:
429 return 'should'
430
431 def _CheckForHookApproval(self):
432 """Check to see whether this hook has been approved.
433
Mike Frysinger40252c22016-08-15 21:23:44 -0400434 We'll accept approval of manifest URLs if they're using secure transports.
435 This way the user can say they trust the manifest hoster. For insecure
436 hosts, we fall back to checking the hash of the hooks repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800437
438 Note that we ask permission for each individual hook even though we use
439 the hash of all hooks when detecting changes. We'd like the user to be
440 able to approve / deny each hook individually. We only use the hash of all
441 hooks because there is no other easy way to detect changes to local imports.
442
443 Returns:
444 True if this hook is approved to run; False otherwise.
445
446 Raises:
447 HookError: Raised if the user doesn't approve and abort_if_user_denies
448 was passed to the consturctor.
449 """
Mike Frysinger40252c22016-08-15 21:23:44 -0400450 if self._ManifestUrlHasSecureScheme():
451 return self._CheckForHookApprovalManifest()
452 else:
453 return self._CheckForHookApprovalHash()
454
455 def _CheckForHookApprovalHelper(self, subkey, new_val, main_prompt,
456 changed_prompt):
457 """Check for approval for a particular attribute and hook.
458
459 Args:
460 subkey: The git config key under [repo.hooks.<hook_type>] to store the
461 last approved string.
462 new_val: The new value to compare against the last approved one.
463 main_prompt: Message to display to the user to ask for approval.
464 changed_prompt: Message explaining why we're re-asking for approval.
465
466 Returns:
467 True if this hook is approved to run; False otherwise.
468
469 Raises:
470 HookError: Raised if the user doesn't approve and abort_if_user_denies
471 was passed to the consturctor.
472 """
Doug Anderson37282b42011-03-04 11:54:18 -0800473 hooks_config = self._hooks_project.config
Mike Frysinger40252c22016-08-15 21:23:44 -0400474 git_approval_key = 'repo.hooks.%s.%s' % (self._hook_type, subkey)
Doug Anderson37282b42011-03-04 11:54:18 -0800475
Mike Frysinger40252c22016-08-15 21:23:44 -0400476 # Get the last value that the user approved for this hook; may be None.
477 old_val = hooks_config.GetString(git_approval_key)
Doug Anderson37282b42011-03-04 11:54:18 -0800478
Mike Frysinger40252c22016-08-15 21:23:44 -0400479 if old_val is not None:
Doug Anderson37282b42011-03-04 11:54:18 -0800480 # User previously approved hook and asked not to be prompted again.
Mike Frysinger40252c22016-08-15 21:23:44 -0400481 if new_val == old_val:
Doug Anderson37282b42011-03-04 11:54:18 -0800482 # Approval matched. We're done.
483 return True
484 else:
485 # Give the user a reason why we're prompting, since they last told
486 # us to "never ask again".
Mike Frysinger40252c22016-08-15 21:23:44 -0400487 prompt = 'WARNING: %s\n\n' % (changed_prompt,)
Doug Anderson37282b42011-03-04 11:54:18 -0800488 else:
489 prompt = ''
490
491 # Prompt the user if we're not on a tty; on a tty we'll assume "no".
492 if sys.stdout.isatty():
Mike Frysinger40252c22016-08-15 21:23:44 -0400493 prompt += main_prompt + ' (yes/always/NO)? '
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530494 response = input(prompt).lower()
David Pursehouse98ffba12012-11-14 11:18:00 +0900495 print()
Doug Anderson37282b42011-03-04 11:54:18 -0800496
497 # User is doing a one-time approval.
498 if response in ('y', 'yes'):
499 return True
Mike Frysinger40252c22016-08-15 21:23:44 -0400500 elif response == 'always':
501 hooks_config.SetString(git_approval_key, new_val)
Doug Anderson37282b42011-03-04 11:54:18 -0800502 return True
503
504 # For anything else, we'll assume no approval.
505 if self._abort_if_user_denies:
506 raise HookError('You must allow the %s hook or use --no-verify.' %
507 self._hook_type)
508
509 return False
510
Mike Frysinger40252c22016-08-15 21:23:44 -0400511 def _ManifestUrlHasSecureScheme(self):
512 """Check if the URI for the manifest is a secure transport."""
513 secure_schemes = ('file', 'https', 'ssh', 'persistent-https', 'sso', 'rpc')
514 parse_results = urllib.parse.urlparse(self._manifest_url)
515 return parse_results.scheme in secure_schemes
516
517 def _CheckForHookApprovalManifest(self):
518 """Check whether the user has approved this manifest host.
519
520 Returns:
521 True if this hook is approved to run; False otherwise.
522 """
523 return self._CheckForHookApprovalHelper(
524 'approvedmanifest',
525 self._manifest_url,
526 'Run hook scripts from %s' % (self._manifest_url,),
527 'Manifest URL has changed since %s was allowed.' % (self._hook_type,))
528
529 def _CheckForHookApprovalHash(self):
530 """Check whether the user has approved the hooks repo.
531
532 Returns:
533 True if this hook is approved to run; False otherwise.
534 """
535 prompt = ('Repo %s run the script:\n'
536 ' %s\n'
537 '\n'
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700538 'Do you want to allow this script to run')
Mike Frysinger40252c22016-08-15 21:23:44 -0400539 return self._CheckForHookApprovalHelper(
540 'approvedhash',
541 self._GetHash(),
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700542 prompt % (self._GetMustVerb(), self._script_fullpath),
Mike Frysinger40252c22016-08-15 21:23:44 -0400543 'Scripts have changed since %s was allowed.' % (self._hook_type,))
544
Doug Anderson37282b42011-03-04 11:54:18 -0800545 def _ExecuteHook(self, **kwargs):
546 """Actually execute the given hook.
547
548 This will run the hook's 'main' function in our python interpreter.
549
550 Args:
551 kwargs: Keyword arguments to pass to the hook. These are often specific
552 to the hook type. For instance, pre-upload hooks will contain
553 a project_list.
554 """
555 # Keep sys.path and CWD stashed away so that we can always restore them
556 # upon function exit.
557 orig_path = os.getcwd()
558 orig_syspath = sys.path
559
560 try:
561 # Always run hooks with CWD as topdir.
562 os.chdir(self._topdir)
563
564 # Put the hook dir as the first item of sys.path so hooks can do
565 # relative imports. We want to replace the repo dir as [0] so
566 # hooks can't import repo files.
567 sys.path = [os.path.dirname(self._script_fullpath)] + sys.path[1:]
568
569 # Exec, storing global context in the context dict. We catch exceptions
570 # and convert to a HookError w/ just the failing traceback.
Mike Frysinger4aa4b212016-03-04 15:03:00 -0500571 context = {'__file__': self._script_fullpath}
Doug Anderson37282b42011-03-04 11:54:18 -0800572 try:
Anthony King70f68902014-05-05 21:15:34 +0100573 exec(compile(open(self._script_fullpath).read(),
574 self._script_fullpath, 'exec'), context)
Doug Anderson37282b42011-03-04 11:54:18 -0800575 except Exception:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700576 raise HookError('%s\nFailed to import %s hook; see traceback above.' %
577 (traceback.format_exc(), self._hook_type))
Doug Anderson37282b42011-03-04 11:54:18 -0800578
579 # Running the script should have defined a main() function.
580 if 'main' not in context:
581 raise HookError('Missing main() in: "%s"' % self._script_fullpath)
582
Doug Anderson37282b42011-03-04 11:54:18 -0800583 # Add 'hook_should_take_kwargs' to the arguments to be passed to main.
584 # We don't actually want hooks to define their main with this argument--
585 # it's there to remind them that their hook should always take **kwargs.
586 # For instance, a pre-upload hook should be defined like:
587 # def main(project_list, **kwargs):
588 #
589 # This allows us to later expand the API without breaking old hooks.
590 kwargs = kwargs.copy()
591 kwargs['hook_should_take_kwargs'] = True
592
593 # Call the main function in the hook. If the hook should cause the
594 # build to fail, it will raise an Exception. We'll catch that convert
595 # to a HookError w/ just the failing traceback.
596 try:
597 context['main'](**kwargs)
598 except Exception:
599 raise HookError('%s\nFailed to run main() for %s hook; see traceback '
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700600 'above.' % (traceback.format_exc(),
601 self._hook_type))
Doug Anderson37282b42011-03-04 11:54:18 -0800602 finally:
603 # Restore sys.path and CWD.
604 sys.path = orig_syspath
605 os.chdir(orig_path)
606
607 def Run(self, user_allows_all_hooks, **kwargs):
608 """Run the hook.
609
610 If the hook doesn't exist (because there is no hooks project or because
611 this particular hook is not enabled), this is a no-op.
612
613 Args:
614 user_allows_all_hooks: If True, we will never prompt about running the
615 hook--we'll just assume it's OK to run it.
616 kwargs: Keyword arguments to pass to the hook. These are often specific
617 to the hook type. For instance, pre-upload hooks will contain
618 a project_list.
619
620 Raises:
621 HookError: If there was a problem finding the hook or the user declined
622 to run a required hook (from _CheckForHookApproval).
623 """
624 # No-op if there is no hooks project or if hook is disabled.
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700625 if ((not self._hooks_project) or (self._hook_type not in
626 self._hooks_project.enabled_repo_hooks)):
Doug Anderson37282b42011-03-04 11:54:18 -0800627 return
628
629 # Bail with a nice error if we can't find the hook.
630 if not os.path.isfile(self._script_fullpath):
631 raise HookError('Couldn\'t find repo hook: "%s"' % self._script_fullpath)
632
633 # Make sure the user is OK with running the hook.
634 if (not user_allows_all_hooks) and (not self._CheckForHookApproval()):
635 return
636
637 # Run the hook with the same version of python we're using.
638 self._ExecuteHook(**kwargs)
639
640
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700641class Project(object):
Kevin Degi384b3c52014-10-16 16:02:58 -0600642 # These objects can be shared between several working trees.
643 shareable_files = ['description', 'info']
644 shareable_dirs = ['hooks', 'objects', 'rr-cache', 'svn']
645 # These objects can only be used by a single working tree.
646 working_tree_files = ['config', 'packed-refs', 'shallow']
647 working_tree_dirs = ['logs', 'refs']
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700648
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700649 def __init__(self,
650 manifest,
651 name,
652 remote,
653 gitdir,
David James8d201162013-10-11 17:03:19 -0700654 objdir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700655 worktree,
656 relpath,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700657 revisionExpr,
Mike Pontillod3153822012-02-28 11:53:24 -0800658 revisionId,
Anthony King7bdac712014-07-16 12:56:40 +0100659 rebase=True,
660 groups=None,
661 sync_c=False,
662 sync_s=False,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900663 sync_tags=True,
Anthony King7bdac712014-07-16 12:56:40 +0100664 clone_depth=None,
665 upstream=None,
666 parent=None,
667 is_derived=False,
David Pursehouseb1553542014-09-04 21:28:09 +0900668 dest_branch=None,
Simran Basib9a1b732015-08-20 12:19:28 -0700669 optimized_fetch=False,
670 old_revision=None):
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800671 """Init a Project object.
672
673 Args:
674 manifest: The XmlManifest object.
675 name: The `name` attribute of manifest.xml's project element.
676 remote: RemoteSpec object specifying its remote's properties.
677 gitdir: Absolute path of git directory.
David James8d201162013-10-11 17:03:19 -0700678 objdir: Absolute path of directory to store git objects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800679 worktree: Absolute path of git working tree.
680 relpath: Relative path of git working tree to repo's top directory.
681 revisionExpr: The `revision` attribute of manifest.xml's project element.
682 revisionId: git commit id for checking out.
683 rebase: The `rebase` attribute of manifest.xml's project element.
684 groups: The `groups` attribute of manifest.xml's project element.
685 sync_c: The `sync-c` attribute of manifest.xml's project element.
686 sync_s: The `sync-s` attribute of manifest.xml's project element.
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900687 sync_tags: The `sync-tags` attribute of manifest.xml's project element.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800688 upstream: The `upstream` attribute of manifest.xml's project element.
689 parent: The parent Project object.
690 is_derived: False if the project was explicitly defined in the manifest;
691 True if the project is a discovered submodule.
Bryan Jacobsf609f912013-05-06 13:36:24 -0400692 dest_branch: The branch to which to push changes for review by default.
David Pursehouseb1553542014-09-04 21:28:09 +0900693 optimized_fetch: If True, when a project is set to a sha1 revision, only
694 fetch from the remote if the sha1 is not present locally.
Simran Basib9a1b732015-08-20 12:19:28 -0700695 old_revision: saved git commit id for open GITC projects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800696 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700697 self.manifest = manifest
698 self.name = name
699 self.remote = remote
Anthony Newnamdf14a702011-01-09 17:31:57 -0800700 self.gitdir = gitdir.replace('\\', '/')
David James8d201162013-10-11 17:03:19 -0700701 self.objdir = objdir.replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800702 if worktree:
Renaud Paquayfef9f212016-11-01 18:28:01 -0700703 self.worktree = os.path.normpath(worktree).replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800704 else:
705 self.worktree = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700706 self.relpath = relpath
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700707 self.revisionExpr = revisionExpr
708
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700709 if revisionId is None \
710 and revisionExpr \
711 and IsId(revisionExpr):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700712 self.revisionId = revisionExpr
713 else:
714 self.revisionId = revisionId
715
Mike Pontillod3153822012-02-28 11:53:24 -0800716 self.rebase = rebase
Colin Cross5acde752012-03-28 20:15:45 -0700717 self.groups = groups
Anatol Pomazau79770d22012-04-20 14:41:59 -0700718 self.sync_c = sync_c
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800719 self.sync_s = sync_s
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900720 self.sync_tags = sync_tags
David Pursehouseede7f122012-11-27 22:25:30 +0900721 self.clone_depth = clone_depth
Brian Harring14a66742012-09-28 20:21:57 -0700722 self.upstream = upstream
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800723 self.parent = parent
724 self.is_derived = is_derived
David Pursehouseb1553542014-09-04 21:28:09 +0900725 self.optimized_fetch = optimized_fetch
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800726 self.subprojects = []
Mike Pontillod3153822012-02-28 11:53:24 -0800727
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700728 self.snapshots = {}
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700729 self.copyfiles = []
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500730 self.linkfiles = []
James W. Mills24c13082012-04-12 15:04:13 -0500731 self.annotations = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700732 self.config = GitConfig.ForRepository(gitdir=self.gitdir,
733 defaults=self.manifest.globalConfig)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700734
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800735 if self.worktree:
David James8d201162013-10-11 17:03:19 -0700736 self.work_git = self._GitGetByExec(self, bare=False, gitdir=gitdir)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800737 else:
738 self.work_git = None
David James8d201162013-10-11 17:03:19 -0700739 self.bare_git = self._GitGetByExec(self, bare=True, gitdir=gitdir)
Shawn O. Pearced237b692009-04-17 18:49:50 -0700740 self.bare_ref = GitRefs(gitdir)
David James8d201162013-10-11 17:03:19 -0700741 self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=objdir)
Bryan Jacobsf609f912013-05-06 13:36:24 -0400742 self.dest_branch = dest_branch
Simran Basib9a1b732015-08-20 12:19:28 -0700743 self.old_revision = old_revision
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700744
Doug Anderson37282b42011-03-04 11:54:18 -0800745 # This will be filled in if a project is later identified to be the
746 # project containing repo hooks.
747 self.enabled_repo_hooks = []
748
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700749 @property
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800750 def Derived(self):
751 return self.is_derived
752
753 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700754 def Exists(self):
Kevin Degi384b3c52014-10-16 16:02:58 -0600755 return os.path.isdir(self.gitdir) and os.path.isdir(self.objdir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700756
757 @property
758 def CurrentBranch(self):
759 """Obtain the name of the currently checked out branch.
760 The branch name omits the 'refs/heads/' prefix.
761 None is returned if the project is on a detached HEAD.
762 """
Shawn O. Pearce5b23f242009-04-17 18:43:33 -0700763 b = self.work_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700764 if b.startswith(R_HEADS):
765 return b[len(R_HEADS):]
766 return None
767
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700768 def IsRebaseInProgress(self):
769 w = self.worktree
770 g = os.path.join(w, '.git')
771 return os.path.exists(os.path.join(g, 'rebase-apply')) \
772 or os.path.exists(os.path.join(g, 'rebase-merge')) \
773 or os.path.exists(os.path.join(w, '.dotest'))
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200774
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700775 def IsDirty(self, consider_untracked=True):
776 """Is the working directory modified in some way?
777 """
778 self.work_git.update_index('-q',
779 '--unmerged',
780 '--ignore-missing',
781 '--refresh')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900782 if self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700783 return True
784 if self.work_git.DiffZ('diff-files'):
785 return True
786 if consider_untracked and self.work_git.LsOthers():
787 return True
788 return False
789
790 _userident_name = None
791 _userident_email = None
792
793 @property
794 def UserName(self):
795 """Obtain the user's personal name.
796 """
797 if self._userident_name is None:
798 self._LoadUserIdentity()
799 return self._userident_name
800
801 @property
802 def UserEmail(self):
803 """Obtain the user's email address. This is very likely
804 to be their Gerrit login.
805 """
806 if self._userident_email is None:
807 self._LoadUserIdentity()
808 return self._userident_email
809
810 def _LoadUserIdentity(self):
David Pursehousec1b86a22012-11-14 11:36:51 +0900811 u = self.bare_git.var('GIT_COMMITTER_IDENT')
812 m = re.compile("^(.*) <([^>]*)> ").match(u)
813 if m:
814 self._userident_name = m.group(1)
815 self._userident_email = m.group(2)
816 else:
817 self._userident_name = ''
818 self._userident_email = ''
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700819
820 def GetRemote(self, name):
821 """Get the configuration for a single remote.
822 """
823 return self.config.GetRemote(name)
824
825 def GetBranch(self, name):
826 """Get the configuration for a single branch.
827 """
828 return self.config.GetBranch(name)
829
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700830 def GetBranches(self):
831 """Get all existing local branches.
832 """
833 current = self.CurrentBranch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900834 all_refs = self._allrefs
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700835 heads = {}
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700836
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530837 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700838 if name.startswith(R_HEADS):
839 name = name[len(R_HEADS):]
840 b = self.GetBranch(name)
841 b.current = name == current
842 b.published = None
David Pursehouse8a68ff92012-09-24 12:15:13 +0900843 b.revision = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700844 heads[name] = b
845
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530846 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700847 if name.startswith(R_PUB):
848 name = name[len(R_PUB):]
849 b = heads.get(name)
850 if b:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900851 b.published = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700852
853 return heads
854
Colin Cross5acde752012-03-28 20:15:45 -0700855 def MatchesGroups(self, manifest_groups):
856 """Returns true if the manifest groups specified at init should cause
857 this project to be synced.
858 Prefixing a manifest group with "-" inverts the meaning of a group.
Conley Owensbb1b5f52012-08-13 13:11:18 -0700859 All projects are implicitly labelled with "all".
Conley Owens971de8e2012-04-16 10:36:08 -0700860
861 labels are resolved in order. In the example case of
Conley Owensbb1b5f52012-08-13 13:11:18 -0700862 project_groups: "all,group1,group2"
Conley Owens971de8e2012-04-16 10:36:08 -0700863 manifest_groups: "-group1,group2"
864 the project will be matched.
David Holmer0a1c6a12012-11-14 19:19:00 -0500865
866 The special manifest group "default" will match any project that
867 does not have the special project group "notdefault"
Colin Cross5acde752012-03-28 20:15:45 -0700868 """
David Holmer0a1c6a12012-11-14 19:19:00 -0500869 expanded_manifest_groups = manifest_groups or ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700870 expanded_project_groups = ['all'] + (self.groups or [])
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700871 if 'notdefault' not in expanded_project_groups:
David Holmer0a1c6a12012-11-14 19:19:00 -0500872 expanded_project_groups += ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700873
Conley Owens971de8e2012-04-16 10:36:08 -0700874 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700875 for group in expanded_manifest_groups:
876 if group.startswith('-') and group[1:] in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700877 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700878 elif group in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700879 matched = True
Colin Cross5acde752012-03-28 20:15:45 -0700880
Conley Owens971de8e2012-04-16 10:36:08 -0700881 return matched
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700882
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700883# Status Display ##
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700884 def UncommitedFiles(self, get_all=True):
885 """Returns a list of strings, uncommitted files in the git tree.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700886
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700887 Args:
888 get_all: a boolean, if True - get information about all different
889 uncommitted files. If False - return as soon as any kind of
890 uncommitted files is detected.
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500891 """
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700892 details = []
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500893 self.work_git.update_index('-q',
894 '--unmerged',
895 '--ignore-missing',
896 '--refresh')
897 if self.IsRebaseInProgress():
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700898 details.append("rebase in progress")
899 if not get_all:
900 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500901
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700902 changes = self.work_git.DiffZ('diff-index', '--cached', HEAD).keys()
903 if changes:
904 details.extend(changes)
905 if not get_all:
906 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500907
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700908 changes = self.work_git.DiffZ('diff-files').keys()
909 if changes:
910 details.extend(changes)
911 if not get_all:
912 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500913
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700914 changes = self.work_git.LsOthers()
915 if changes:
916 details.extend(changes)
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500917
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700918 return details
919
920 def HasChanges(self):
921 """Returns true if there are uncommitted changes.
922 """
923 if self.UncommitedFiles(get_all=False):
924 return True
925 else:
926 return False
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500927
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600928 def PrintWorkTreeStatus(self, output_redir=None, quiet=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700929 """Prints the status of the repository to stdout.
Terence Haddock4655e812011-03-31 12:33:34 +0200930
931 Args:
932 output: If specified, redirect the output to this object.
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600933 quiet: If True then only print the project name. Do not print
934 the modified files, branch name, etc.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700935 """
936 if not os.path.isdir(self.worktree):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700937 if output_redir is None:
Terence Haddock4655e812011-03-31 12:33:34 +0200938 output_redir = sys.stdout
Sarah Owenscecd1d82012-11-01 22:59:27 -0700939 print(file=output_redir)
940 print('project %s/' % self.relpath, file=output_redir)
941 print(' missing (run "repo sync")', file=output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700942 return
943
944 self.work_git.update_index('-q',
945 '--unmerged',
946 '--ignore-missing',
947 '--refresh')
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700948 rb = self.IsRebaseInProgress()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700949 di = self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD)
950 df = self.work_git.DiffZ('diff-files')
951 do = self.work_git.LsOthers()
Ali Utku Selen76abcc12012-01-25 10:51:12 +0100952 if not rb and not di and not df and not do and not self.CurrentBranch:
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700953 return 'CLEAN'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700954
955 out = StatusColoring(self.config)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700956 if output_redir is not None:
Terence Haddock4655e812011-03-31 12:33:34 +0200957 out.redirect(output_redir)
Jakub Vrana0402cd82014-09-09 15:39:15 -0700958 out.project('project %-40s', self.relpath + '/ ')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700959
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600960 if quiet:
961 out.nl()
962 return 'DIRTY'
963
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700964 branch = self.CurrentBranch
965 if branch is None:
966 out.nobranch('(*** NO BRANCH ***)')
967 else:
968 out.branch('branch %s', branch)
969 out.nl()
970
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700971 if rb:
972 out.important('prior sync failed; rebase still in progress')
973 out.nl()
974
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700975 paths = list()
976 paths.extend(di.keys())
977 paths.extend(df.keys())
978 paths.extend(do)
979
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530980 for p in sorted(set(paths)):
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900981 try:
982 i = di[p]
983 except KeyError:
984 i = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700985
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900986 try:
987 f = df[p]
988 except KeyError:
989 f = None
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200990
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900991 if i:
992 i_status = i.status.upper()
993 else:
994 i_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700995
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900996 if f:
997 f_status = f.status.lower()
998 else:
999 f_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001000
1001 if i and i.src_path:
Shawn O. Pearcefe086752009-03-03 13:49:48 -08001002 line = ' %s%s\t%s => %s (%s%%)' % (i_status, f_status,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001003 i.src_path, p, i.level)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001004 else:
1005 line = ' %s%s\t%s' % (i_status, f_status, p)
1006
1007 if i and not f:
1008 out.added('%s', line)
1009 elif (i and f) or (not i and f):
1010 out.changed('%s', line)
1011 elif not i and not f:
1012 out.untracked('%s', line)
1013 else:
1014 out.write('%s', line)
1015 out.nl()
Terence Haddock4655e812011-03-31 12:33:34 +02001016
Shawn O. Pearce161f4452009-04-10 17:41:44 -07001017 return 'DIRTY'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001018
pelyad67872d2012-03-28 14:49:58 +03001019 def PrintWorkTreeDiff(self, absolute_paths=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001020 """Prints the status of the repository to stdout.
1021 """
1022 out = DiffColoring(self.config)
1023 cmd = ['diff']
1024 if out.is_on:
1025 cmd.append('--color')
1026 cmd.append(HEAD)
pelyad67872d2012-03-28 14:49:58 +03001027 if absolute_paths:
1028 cmd.append('--src-prefix=a/%s/' % self.relpath)
1029 cmd.append('--dst-prefix=b/%s/' % self.relpath)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001030 cmd.append('--')
1031 p = GitCommand(self,
1032 cmd,
Anthony King7bdac712014-07-16 12:56:40 +01001033 capture_stdout=True,
1034 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001035 has_diff = False
1036 for line in p.process.stdout:
1037 if not has_diff:
1038 out.nl()
1039 out.project('project %s/' % self.relpath)
1040 out.nl()
1041 has_diff = True
Sarah Owenscecd1d82012-11-01 22:59:27 -07001042 print(line[:-1])
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001043 p.Wait()
1044
1045
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001046# Publish / Upload ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001047
David Pursehouse8a68ff92012-09-24 12:15:13 +09001048 def WasPublished(self, branch, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001049 """Was the branch published (uploaded) for code review?
1050 If so, returns the SHA-1 hash of the last published
1051 state for the branch.
1052 """
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001053 key = R_PUB + branch
David Pursehouse8a68ff92012-09-24 12:15:13 +09001054 if all_refs is None:
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001055 try:
1056 return self.bare_git.rev_parse(key)
1057 except GitError:
1058 return None
1059 else:
1060 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001061 return all_refs[key]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001062 except KeyError:
1063 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001064
David Pursehouse8a68ff92012-09-24 12:15:13 +09001065 def CleanPublishedCache(self, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001066 """Prunes any stale published refs.
1067 """
David Pursehouse8a68ff92012-09-24 12:15:13 +09001068 if all_refs is None:
1069 all_refs = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001070 heads = set()
1071 canrm = {}
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301072 for name, ref_id in all_refs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001073 if name.startswith(R_HEADS):
1074 heads.add(name)
1075 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001076 canrm[name] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001077
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301078 for name, ref_id in canrm.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001079 n = name[len(R_PUB):]
1080 if R_HEADS + n not in heads:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001081 self.bare_git.DeleteRef(name, ref_id)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001082
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001083 def GetUploadableBranches(self, selected_branch=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001084 """List any branches which can be uploaded for review.
1085 """
1086 heads = {}
1087 pubed = {}
1088
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301089 for name, ref_id in self._allrefs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001090 if name.startswith(R_HEADS):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001091 heads[name[len(R_HEADS):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001092 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001093 pubed[name[len(R_PUB):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001094
1095 ready = []
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301096 for branch, ref_id in heads.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09001097 if branch in pubed and pubed[branch] == ref_id:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001098 continue
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001099 if selected_branch and branch != selected_branch:
1100 continue
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001101
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001102 rb = self.GetUploadableBranch(branch)
1103 if rb:
1104 ready.append(rb)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001105 return ready
1106
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001107 def GetUploadableBranch(self, branch_name):
1108 """Get a single uploadable branch, or None.
1109 """
1110 branch = self.GetBranch(branch_name)
1111 base = branch.LocalMerge
1112 if branch.LocalMerge:
1113 rb = ReviewableBranch(self, branch, base)
1114 if rb.commits:
1115 return rb
1116 return None
1117
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001118 def UploadForReview(self, branch=None,
Anthony King7bdac712014-07-16 12:56:40 +01001119 people=([], []),
Brian Harring435370c2012-07-28 15:37:04 -07001120 auto_topic=False,
Jonathan Niederc94d6eb2017-08-08 18:34:53 +00001121 draft=False,
Changcheng Xiao87984c62017-08-02 16:55:03 +02001122 private=False,
1123 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +02001124 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001125 validate_certs=True,
1126 push_options=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001127 """Uploads the named branch for code review.
1128 """
1129 if branch is None:
1130 branch = self.CurrentBranch
1131 if branch is None:
1132 raise GitError('not currently on a branch')
1133
1134 branch = self.GetBranch(branch)
1135 if not branch.LocalMerge:
1136 raise GitError('branch %s does not track a remote' % branch.name)
1137 if not branch.remote.review:
1138 raise GitError('remote %s has no review url' % branch.remote.name)
1139
Bryan Jacobsf609f912013-05-06 13:36:24 -04001140 if dest_branch is None:
1141 dest_branch = self.dest_branch
1142 if dest_branch is None:
1143 dest_branch = branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001144 if not dest_branch.startswith(R_HEADS):
1145 dest_branch = R_HEADS + dest_branch
1146
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -08001147 if not branch.remote.projectname:
1148 branch.remote.projectname = self.name
1149 branch.remote.Save()
1150
Łukasz Gardońbed59ce2017-08-08 10:18:11 +02001151 url = branch.remote.ReviewUrl(self.UserEmail, validate_certs)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001152 if url is None:
1153 raise UploadError('review not configured')
1154 cmd = ['push']
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001155
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001156 if url.startswith('ssh://'):
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001157 rp = ['gerrit receive-pack']
1158 for e in people[0]:
1159 rp.append('--reviewer=%s' % sq(e))
1160 for e in people[1]:
1161 rp.append('--cc=%s' % sq(e))
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001162 cmd.append('--receive-pack=%s' % " ".join(rp))
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001163
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001164 for push_option in (push_options or []):
1165 cmd.append('-o')
1166 cmd.append(push_option)
1167
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001168 cmd.append(url)
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001169
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001170 if dest_branch.startswith(R_HEADS):
1171 dest_branch = dest_branch[len(R_HEADS):]
Brian Harring435370c2012-07-28 15:37:04 -07001172
Jonathan Niederc94d6eb2017-08-08 18:34:53 +00001173 upload_type = 'for'
1174 if draft:
1175 upload_type = 'drafts'
1176
1177 ref_spec = '%s:refs/%s/%s' % (R_HEADS + branch.name, upload_type,
1178 dest_branch)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001179 if auto_topic:
1180 ref_spec = ref_spec + '/' + branch.name
Changcheng Xiao87984c62017-08-02 16:55:03 +02001181
Shawn Pearce45d21682013-02-28 00:35:51 -08001182 if not url.startswith('ssh://'):
1183 rp = ['r=%s' % p for p in people[0]] + \
1184 ['cc=%s' % p for p in people[1]]
Changcheng Xiao87984c62017-08-02 16:55:03 +02001185 if private:
1186 rp = rp + ['private']
1187 if wip:
1188 rp = rp + ['wip']
Shawn Pearce45d21682013-02-28 00:35:51 -08001189 if rp:
1190 ref_spec = ref_spec + '%' + ','.join(rp)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001191 cmd.append(ref_spec)
1192
Anthony King7bdac712014-07-16 12:56:40 +01001193 if GitCommand(self, cmd, bare=True).Wait() != 0:
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001194 raise UploadError('Upload failed')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001195
1196 msg = "posted to %s for %s" % (branch.remote.review, dest_branch)
1197 self.bare_git.UpdateRef(R_PUB + branch.name,
1198 R_HEADS + branch.name,
Anthony King7bdac712014-07-16 12:56:40 +01001199 message=msg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001200
1201
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001202# Sync ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001203
Julien Campergue335f5ef2013-10-16 11:02:35 +02001204 def _ExtractArchive(self, tarpath, path=None):
1205 """Extract the given tar on its current location
1206
1207 Args:
1208 - tarpath: The path to the actual tar file
1209
1210 """
1211 try:
1212 with tarfile.open(tarpath, 'r') as tar:
1213 tar.extractall(path=path)
1214 return True
1215 except (IOError, tarfile.TarError) as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001216 _error("Cannot extract archive %s: %s", tarpath, str(e))
Julien Campergue335f5ef2013-10-16 11:02:35 +02001217 return False
1218
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001219 def Sync_NetworkHalf(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001220 quiet=False,
1221 is_new=None,
1222 current_branch_only=False,
1223 force_sync=False,
1224 clone_bundle=True,
1225 no_tags=False,
1226 archive=False,
1227 optimized_fetch=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07001228 prune=False,
1229 submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001230 """Perform only the network IO portion of the sync process.
1231 Local working directory/branch state is not affected.
1232 """
Julien Campergue335f5ef2013-10-16 11:02:35 +02001233 if archive and not isinstance(self, MetaProject):
1234 if self.remote.url.startswith(('http://', 'https://')):
David Pursehousef33929d2015-08-24 14:39:14 +09001235 _error("%s: Cannot fetch archives from http/https remotes.", self.name)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001236 return False
1237
1238 name = self.relpath.replace('\\', '/')
1239 name = name.replace('/', '_')
1240 tarpath = '%s.tar' % name
1241 topdir = self.manifest.topdir
1242
1243 try:
1244 self._FetchArchive(tarpath, cwd=topdir)
1245 except GitError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001246 _error('%s', e)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001247 return False
1248
1249 # From now on, we only need absolute tarpath
1250 tarpath = os.path.join(topdir, tarpath)
1251
1252 if not self._ExtractArchive(tarpath, path=topdir):
1253 return False
1254 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001255 platform_utils.remove(tarpath)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001256 except OSError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001257 _warn("Cannot remove archive %s: %s", tarpath, str(e))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001258 self._CopyAndLinkFiles()
Julien Campergue335f5ef2013-10-16 11:02:35 +02001259 return True
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001260 if is_new is None:
1261 is_new = not self.Exists
Shawn O. Pearce88443382010-10-08 10:02:09 +02001262 if is_new:
Kevin Degiabaa7f32014-11-12 11:27:45 -07001263 self._InitGitDir(force_sync=force_sync)
Jimmie Westera0444582012-10-24 13:44:42 +02001264 else:
1265 self._UpdateHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001266 self._InitRemote()
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001267
1268 if is_new:
1269 alt = os.path.join(self.gitdir, 'objects/info/alternates')
1270 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07001271 fd = open(alt)
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001272 try:
1273 alt_dir = fd.readline().rstrip()
1274 finally:
1275 fd.close()
1276 except IOError:
1277 alt_dir = None
1278 else:
1279 alt_dir = None
1280
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001281 if clone_bundle \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001282 and alt_dir is None \
1283 and self._ApplyCloneBundle(initial=is_new, quiet=quiet):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001284 is_new = False
1285
Shawn O. Pearce6ba6ba02012-05-24 09:46:50 -07001286 if not current_branch_only:
1287 if self.sync_c:
1288 current_branch_only = True
1289 elif not self.manifest._loaded:
1290 # Manifest cannot check defaults until it syncs.
1291 current_branch_only = False
1292 elif self.manifest.default.sync_c:
1293 current_branch_only = True
1294
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001295 if not no_tags:
1296 if not self.sync_tags:
1297 no_tags = True
1298
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001299 if self.clone_depth:
1300 depth = self.clone_depth
1301 else:
1302 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1303
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001304 need_to_fetch = not (optimized_fetch and
1305 (ID_RE.match(self.revisionExpr) and
Zac Livingstone4332262017-06-16 08:56:09 -06001306 self._CheckForImmutableRevision()))
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001307 if (need_to_fetch and
1308 not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir,
1309 current_branch_only=current_branch_only,
Martin Kellye4e94d22017-03-21 16:05:12 -07001310 no_tags=no_tags, prune=prune, depth=depth,
1311 submodules=submodules)):
Anthony King7bdac712014-07-16 12:56:40 +01001312 return False
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001313
1314 if self.worktree:
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001315 self._InitMRef()
1316 else:
1317 self._InitMirrorHead()
1318 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001319 platform_utils.remove(os.path.join(self.gitdir, 'FETCH_HEAD'))
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001320 except OSError:
1321 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001322 return True
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001323
1324 def PostRepoUpgrade(self):
1325 self._InitHooks()
1326
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001327 def _CopyAndLinkFiles(self):
Simran Basib9a1b732015-08-20 12:19:28 -07001328 if self.manifest.isGitcClient:
1329 return
David Pursehouse8a68ff92012-09-24 12:15:13 +09001330 for copyfile in self.copyfiles:
1331 copyfile._Copy()
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001332 for linkfile in self.linkfiles:
1333 linkfile._Link()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001334
Julien Camperguedd654222014-01-09 16:21:37 +01001335 def GetCommitRevisionId(self):
1336 """Get revisionId of a commit.
1337
1338 Use this method instead of GetRevisionId to get the id of the commit rather
1339 than the id of the current git object (for example, a tag)
1340
1341 """
1342 if not self.revisionExpr.startswith(R_TAGS):
1343 return self.GetRevisionId(self._allrefs)
1344
1345 try:
1346 return self.bare_git.rev_list(self.revisionExpr, '-1')[0]
1347 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001348 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1349 (self.revisionExpr, self.name))
Julien Camperguedd654222014-01-09 16:21:37 +01001350
David Pursehouse8a68ff92012-09-24 12:15:13 +09001351 def GetRevisionId(self, all_refs=None):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001352 if self.revisionId:
1353 return self.revisionId
1354
1355 rem = self.GetRemote(self.remote.name)
1356 rev = rem.ToLocal(self.revisionExpr)
1357
David Pursehouse8a68ff92012-09-24 12:15:13 +09001358 if all_refs is not None and rev in all_refs:
1359 return all_refs[rev]
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001360
1361 try:
1362 return self.bare_git.rev_parse('--verify', '%s^0' % rev)
1363 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001364 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1365 (self.revisionExpr, self.name))
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001366
Martin Kellye4e94d22017-03-21 16:05:12 -07001367 def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001368 """Perform only the local IO portion of the sync process.
1369 Network access is not required.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001370 """
Martin Kellye4e94d22017-03-21 16:05:12 -07001371 self._InitWorkTree(force_sync=force_sync, submodules=submodules)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001372 all_refs = self.bare_ref.all
1373 self.CleanPublishedCache(all_refs)
1374 revid = self.GetRevisionId(all_refs)
Skyler Kaufman835cd682011-03-08 12:14:41 -08001375
David Pursehouse1d947b32012-10-25 12:23:11 +09001376 def _doff():
1377 self._FastForward(revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001378 self._CopyAndLinkFiles()
David Pursehouse1d947b32012-10-25 12:23:11 +09001379
Martin Kellye4e94d22017-03-21 16:05:12 -07001380 def _dosubmodules():
1381 self._SyncSubmodules(quiet=True)
1382
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001383 head = self.work_git.GetHead()
1384 if head.startswith(R_HEADS):
1385 branch = head[len(R_HEADS):]
1386 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001387 head = all_refs[head]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001388 except KeyError:
1389 head = None
1390 else:
1391 branch = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001392
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001393 if branch is None or syncbuf.detach_head:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001394 # Currently on a detached HEAD. The user is assumed to
1395 # not have any local modifications worth worrying about.
1396 #
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001397 if self.IsRebaseInProgress():
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001398 syncbuf.fail(self, _PriorSyncFailedError())
1399 return
1400
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001401 if head == revid:
1402 # No changes; don't do anything further.
Florian Vallee7cf1b362012-06-07 17:11:42 +02001403 # Except if the head needs to be detached
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001404 #
Florian Vallee7cf1b362012-06-07 17:11:42 +02001405 if not syncbuf.detach_head:
Dan Willemsen029eaf32015-09-03 12:52:28 -07001406 # The copy/linkfile config may have changed.
1407 self._CopyAndLinkFiles()
Florian Vallee7cf1b362012-06-07 17:11:42 +02001408 return
1409 else:
1410 lost = self._revlist(not_rev(revid), HEAD)
1411 if lost:
1412 syncbuf.info(self, "discarding %d commits", len(lost))
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001413
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001414 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001415 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001416 if submodules:
1417 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001418 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001419 syncbuf.fail(self, e)
1420 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001421 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001422 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001423
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001424 if head == revid:
1425 # No changes; don't do anything further.
1426 #
Dan Willemsen029eaf32015-09-03 12:52:28 -07001427 # The copy/linkfile config may have changed.
1428 self._CopyAndLinkFiles()
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001429 return
1430
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001431 branch = self.GetBranch(branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001432
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001433 if not branch.LocalMerge:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001434 # The current branch has no tracking configuration.
Anatol Pomazau2a32f6a2011-08-30 10:52:33 -07001435 # Jump off it to a detached HEAD.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001436 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001437 syncbuf.info(self,
1438 "leaving %s; does not track upstream",
1439 branch.name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001440 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001441 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001442 if submodules:
1443 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001444 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001445 syncbuf.fail(self, e)
1446 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001447 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001448 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001449
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001450 upstream_gain = self._revlist(not_rev(HEAD), revid)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001451 pub = self.WasPublished(branch.name, all_refs)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001452 if pub:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001453 not_merged = self._revlist(not_rev(revid), pub)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001454 if not_merged:
1455 if upstream_gain:
1456 # The user has published this branch and some of those
1457 # commits are not yet merged upstream. We do not want
1458 # to rewrite the published commits so we punt.
1459 #
Daniel Sandler4c50dee2010-03-02 15:38:03 -05001460 syncbuf.fail(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001461 "branch %s is published (but not merged) and is now "
1462 "%d commits behind" % (branch.name, len(upstream_gain)))
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001463 return
Shawn O. Pearce05f66b62009-04-21 08:26:32 -07001464 elif pub == head:
1465 # All published commits are merged, and thus we are a
1466 # strict subset. We can fast-forward safely.
Shawn O. Pearcea54c5272008-10-30 11:03:00 -07001467 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001468 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001469 if submodules:
1470 syncbuf.later1(self, _dosubmodules)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001471 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001472
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001473 # Examine the local commits not in the remote. Find the
1474 # last one attributed to this user, if any.
1475 #
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001476 local_changes = self._revlist(not_rev(revid), HEAD, format='%H %ce')
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001477 last_mine = None
1478 cnt_mine = 0
1479 for commit in local_changes:
Chirayu Desai0eb35cb2013-11-19 18:46:29 +05301480 commit_id, committer_email = commit.decode('utf-8').split(' ', 1)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001481 if committer_email == self.UserEmail:
1482 last_mine = commit_id
1483 cnt_mine += 1
1484
Shawn O. Pearceda88ff42009-06-03 11:09:12 -07001485 if not upstream_gain and cnt_mine == len(local_changes):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001486 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001487
1488 if self.IsDirty(consider_untracked=False):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001489 syncbuf.fail(self, _DirtyError())
1490 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001491
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001492 # If the upstream switched on us, warn the user.
1493 #
1494 if branch.merge != self.revisionExpr:
1495 if branch.merge and self.revisionExpr:
1496 syncbuf.info(self,
1497 'manifest switched %s...%s',
1498 branch.merge,
1499 self.revisionExpr)
1500 elif branch.merge:
1501 syncbuf.info(self,
1502 'manifest no longer tracks %s',
1503 branch.merge)
1504
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001505 if cnt_mine < len(local_changes):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001506 # Upstream rebased. Not everything in HEAD
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001507 # was created by this user.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001508 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001509 syncbuf.info(self,
1510 "discarding %d commits removed from upstream",
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001511 len(local_changes) - cnt_mine)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001512
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001513 branch.remote = self.GetRemote(self.remote.name)
Anatol Pomazaucd7c5de2012-03-20 13:45:00 -07001514 if not ID_RE.match(self.revisionExpr):
1515 # in case of manifest sync the revisionExpr might be a SHA1
1516 branch.merge = self.revisionExpr
Conley Owens04f2f0e2014-10-01 17:22:46 -07001517 if not branch.merge.startswith('refs/'):
1518 branch.merge = R_HEADS + branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001519 branch.Save()
1520
Mike Pontillod3153822012-02-28 11:53:24 -08001521 if cnt_mine > 0 and self.rebase:
Martin Kellye4e94d22017-03-21 16:05:12 -07001522 def _docopyandlink():
1523 self._CopyAndLinkFiles()
1524
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001525 def _dorebase():
Anthony King7bdac712014-07-16 12:56:40 +01001526 self._Rebase(upstream='%s^1' % last_mine, onto=revid)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001527 syncbuf.later2(self, _dorebase)
Martin Kellye4e94d22017-03-21 16:05:12 -07001528 if submodules:
1529 syncbuf.later2(self, _dosubmodules)
1530 syncbuf.later2(self, _docopyandlink)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001531 elif local_changes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001532 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001533 self._ResetHard(revid)
Martin Kellye4e94d22017-03-21 16:05:12 -07001534 if submodules:
1535 self._SyncSubmodules(quiet=True)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001536 self._CopyAndLinkFiles()
Sarah Owensa5be53f2012-09-09 15:37:57 -07001537 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001538 syncbuf.fail(self, e)
1539 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001540 else:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001541 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001542 if submodules:
1543 syncbuf.later1(self, _dosubmodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001544
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001545 def AddCopyFile(self, src, dest, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001546 # dest should already be an absolute path, but src is project relative
1547 # make src an absolute path
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001548 abssrc = os.path.join(self.worktree, src)
1549 self.copyfiles.append(_CopyFile(src, dest, abssrc, absdest))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001550
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001551 def AddLinkFile(self, src, dest, absdest):
1552 # dest should already be an absolute path, but src is project relative
Colin Cross0184dcc2015-05-05 00:24:54 -07001553 # make src relative path to dest
1554 absdestdir = os.path.dirname(absdest)
1555 relsrc = os.path.relpath(os.path.join(self.worktree, src), absdestdir)
Wink Saville4c426ef2015-06-03 08:05:17 -07001556 self.linkfiles.append(_LinkFile(self.worktree, src, dest, relsrc, absdest))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001557
James W. Mills24c13082012-04-12 15:04:13 -05001558 def AddAnnotation(self, name, value, keep):
1559 self.annotations.append(_Annotation(name, value, keep))
1560
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001561 def DownloadPatchSet(self, change_id, patch_id):
1562 """Download a single patch set of a single change to FETCH_HEAD.
1563 """
1564 remote = self.GetRemote(self.remote.name)
1565
1566 cmd = ['fetch', remote.name]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001567 cmd.append('refs/changes/%2.2d/%d/%d'
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001568 % (change_id % 100, change_id, patch_id))
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001569 if GitCommand(self, cmd, bare=True).Wait() != 0:
1570 return None
1571 return DownloadedChange(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001572 self.GetRevisionId(),
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001573 change_id,
1574 patch_id,
1575 self.bare_git.rev_parse('FETCH_HEAD'))
1576
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001577
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001578# Branch Management ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001579
Simran Basib9a1b732015-08-20 12:19:28 -07001580 def StartBranch(self, name, branch_merge=''):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001581 """Create a new branch off the manifest's revision.
1582 """
Simran Basib9a1b732015-08-20 12:19:28 -07001583 if not branch_merge:
1584 branch_merge = self.revisionExpr
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001585 head = self.work_git.GetHead()
1586 if head == (R_HEADS + name):
1587 return True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001588
David Pursehouse8a68ff92012-09-24 12:15:13 +09001589 all_refs = self.bare_ref.all
Anthony King7bdac712014-07-16 12:56:40 +01001590 if R_HEADS + name in all_refs:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001591 return GitCommand(self,
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001592 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001593 capture_stdout=True,
1594 capture_stderr=True).Wait() == 0
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001595
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001596 branch = self.GetBranch(name)
1597 branch.remote = self.GetRemote(self.remote.name)
Simran Basib9a1b732015-08-20 12:19:28 -07001598 branch.merge = branch_merge
1599 if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge):
1600 branch.merge = R_HEADS + branch_merge
David Pursehouse8a68ff92012-09-24 12:15:13 +09001601 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001602
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001603 if head.startswith(R_HEADS):
1604 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001605 head = all_refs[head]
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001606 except KeyError:
1607 head = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001608 if revid and head and revid == head:
1609 ref = os.path.join(self.gitdir, R_HEADS + name)
1610 try:
1611 os.makedirs(os.path.dirname(ref))
1612 except OSError:
1613 pass
1614 _lwrite(ref, '%s\n' % revid)
1615 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1616 'ref: %s%s\n' % (R_HEADS, name))
1617 branch.Save()
1618 return True
1619
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001620 if GitCommand(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001621 ['checkout', '-b', branch.name, revid],
Anthony King7bdac712014-07-16 12:56:40 +01001622 capture_stdout=True,
1623 capture_stderr=True).Wait() == 0:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001624 branch.Save()
1625 return True
1626 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001627
Wink Saville02d79452009-04-10 13:01:24 -07001628 def CheckoutBranch(self, name):
1629 """Checkout a local topic branch.
Doug Anderson3ba5f952011-04-07 12:51:04 -07001630
1631 Args:
1632 name: The name of the branch to checkout.
1633
1634 Returns:
1635 True if the checkout succeeded; False if it didn't; None if the branch
1636 didn't exist.
Wink Saville02d79452009-04-10 13:01:24 -07001637 """
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001638 rev = R_HEADS + name
1639 head = self.work_git.GetHead()
1640 if head == rev:
1641 # Already on the branch
1642 #
1643 return True
Wink Saville02d79452009-04-10 13:01:24 -07001644
David Pursehouse8a68ff92012-09-24 12:15:13 +09001645 all_refs = self.bare_ref.all
Wink Saville02d79452009-04-10 13:01:24 -07001646 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001647 revid = all_refs[rev]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001648 except KeyError:
1649 # Branch does not exist in this project
1650 #
Doug Anderson3ba5f952011-04-07 12:51:04 -07001651 return None
Wink Saville02d79452009-04-10 13:01:24 -07001652
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001653 if head.startswith(R_HEADS):
1654 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001655 head = all_refs[head]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001656 except KeyError:
1657 head = None
1658
1659 if head == revid:
1660 # Same revision; just update HEAD to point to the new
1661 # target branch, but otherwise take no other action.
1662 #
1663 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1664 'ref: %s%s\n' % (R_HEADS, name))
1665 return True
1666
1667 return GitCommand(self,
1668 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001669 capture_stdout=True,
1670 capture_stderr=True).Wait() == 0
Wink Saville02d79452009-04-10 13:01:24 -07001671
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001672 def AbandonBranch(self, name):
1673 """Destroy a local topic branch.
Doug Andersondafb1d62011-04-07 11:46:59 -07001674
1675 Args:
1676 name: The name of the branch to abandon.
1677
1678 Returns:
1679 True if the abandon succeeded; False if it didn't; None if the branch
1680 didn't exist.
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001681 """
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001682 rev = R_HEADS + name
David Pursehouse8a68ff92012-09-24 12:15:13 +09001683 all_refs = self.bare_ref.all
1684 if rev not in all_refs:
Doug Andersondafb1d62011-04-07 11:46:59 -07001685 # Doesn't exist
1686 return None
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001687
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001688 head = self.work_git.GetHead()
1689 if head == rev:
1690 # We can't destroy the branch while we are sitting
1691 # on it. Switch to a detached HEAD.
1692 #
David Pursehouse8a68ff92012-09-24 12:15:13 +09001693 head = all_refs[head]
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001694
David Pursehouse8a68ff92012-09-24 12:15:13 +09001695 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001696 if head == revid:
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001697 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1698 '%s\n' % revid)
1699 else:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001700 self._Checkout(revid, quiet=True)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001701
1702 return GitCommand(self,
1703 ['branch', '-D', name],
Anthony King7bdac712014-07-16 12:56:40 +01001704 capture_stdout=True,
1705 capture_stderr=True).Wait() == 0
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001706
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001707 def PruneHeads(self):
1708 """Prune any topic branches already merged into upstream.
1709 """
1710 cb = self.CurrentBranch
1711 kill = []
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001712 left = self._allrefs
1713 for name in left.keys():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001714 if name.startswith(R_HEADS):
1715 name = name[len(R_HEADS):]
1716 if cb is None or name != cb:
1717 kill.append(name)
1718
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001719 rev = self.GetRevisionId(left)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001720 if cb is not None \
1721 and not self._revlist(HEAD + '...' + rev) \
Anthony King7bdac712014-07-16 12:56:40 +01001722 and not self.IsDirty(consider_untracked=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001723 self.work_git.DetachHead(HEAD)
1724 kill.append(cb)
1725
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001726 if kill:
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07001727 old = self.bare_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001728
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001729 try:
1730 self.bare_git.DetachHead(rev)
1731
1732 b = ['branch', '-d']
1733 b.extend(kill)
1734 b = GitCommand(self, b, bare=True,
1735 capture_stdout=True,
1736 capture_stderr=True)
1737 b.Wait()
1738 finally:
Dan Willemsen1a799d12015-12-15 13:40:05 -08001739 if ID_RE.match(old):
1740 self.bare_git.DetachHead(old)
1741 else:
1742 self.bare_git.SetHead(old)
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001743 left = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001744
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001745 for branch in kill:
1746 if (R_HEADS + branch) not in left:
1747 self.CleanPublishedCache()
1748 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001749
1750 if cb and cb not in kill:
1751 kill.append(cb)
Shawn O. Pearce7c6c64d2009-03-02 12:38:13 -08001752 kill.sort()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001753
1754 kept = []
1755 for branch in kill:
Anthony King7bdac712014-07-16 12:56:40 +01001756 if R_HEADS + branch in left:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001757 branch = self.GetBranch(branch)
1758 base = branch.LocalMerge
1759 if not base:
1760 base = rev
1761 kept.append(ReviewableBranch(self, branch, base))
1762 return kept
1763
1764
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001765# Submodule Management ##
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001766
1767 def GetRegisteredSubprojects(self):
1768 result = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001769
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001770 def rec(subprojects):
1771 if not subprojects:
1772 return
1773 result.extend(subprojects)
1774 for p in subprojects:
1775 rec(p.subprojects)
1776 rec(self.subprojects)
1777 return result
1778
1779 def _GetSubmodules(self):
1780 # Unfortunately we cannot call `git submodule status --recursive` here
1781 # because the working tree might not exist yet, and it cannot be used
1782 # without a working tree in its current implementation.
1783
1784 def get_submodules(gitdir, rev):
1785 # Parse .gitmodules for submodule sub_paths and sub_urls
1786 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1787 if not sub_paths:
1788 return []
1789 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1790 # revision of submodule repository
1791 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1792 submodules = []
1793 for sub_path, sub_url in zip(sub_paths, sub_urls):
1794 try:
1795 sub_rev = sub_revs[sub_path]
1796 except KeyError:
1797 # Ignore non-exist submodules
1798 continue
1799 submodules.append((sub_rev, sub_path, sub_url))
1800 return submodules
1801
1802 re_path = re.compile(r'^submodule\.([^.]+)\.path=(.*)$')
1803 re_url = re.compile(r'^submodule\.([^.]+)\.url=(.*)$')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001804
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001805 def parse_gitmodules(gitdir, rev):
1806 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1807 try:
Anthony King7bdac712014-07-16 12:56:40 +01001808 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1809 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001810 except GitError:
1811 return [], []
1812 if p.Wait() != 0:
1813 return [], []
1814
1815 gitmodules_lines = []
1816 fd, temp_gitmodules_path = tempfile.mkstemp()
1817 try:
1818 os.write(fd, p.stdout)
1819 os.close(fd)
1820 cmd = ['config', '--file', temp_gitmodules_path, '--list']
Anthony King7bdac712014-07-16 12:56:40 +01001821 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1822 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001823 if p.Wait() != 0:
1824 return [], []
1825 gitmodules_lines = p.stdout.split('\n')
1826 except GitError:
1827 return [], []
1828 finally:
Renaud Paquay010fed72016-11-11 14:25:29 -08001829 platform_utils.remove(temp_gitmodules_path)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001830
1831 names = set()
1832 paths = {}
1833 urls = {}
1834 for line in gitmodules_lines:
1835 if not line:
1836 continue
1837 m = re_path.match(line)
1838 if m:
1839 names.add(m.group(1))
1840 paths[m.group(1)] = m.group(2)
1841 continue
1842 m = re_url.match(line)
1843 if m:
1844 names.add(m.group(1))
1845 urls[m.group(1)] = m.group(2)
1846 continue
1847 names = sorted(names)
1848 return ([paths.get(name, '') for name in names],
1849 [urls.get(name, '') for name in names])
1850
1851 def git_ls_tree(gitdir, rev, paths):
1852 cmd = ['ls-tree', rev, '--']
1853 cmd.extend(paths)
1854 try:
Anthony King7bdac712014-07-16 12:56:40 +01001855 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1856 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001857 except GitError:
1858 return []
1859 if p.Wait() != 0:
1860 return []
1861 objects = {}
1862 for line in p.stdout.split('\n'):
1863 if not line.strip():
1864 continue
1865 object_rev, object_path = line.split()[2:4]
1866 objects[object_path] = object_rev
1867 return objects
1868
1869 try:
1870 rev = self.GetRevisionId()
1871 except GitError:
1872 return []
1873 return get_submodules(self.gitdir, rev)
1874
1875 def GetDerivedSubprojects(self):
1876 result = []
1877 if not self.Exists:
1878 # If git repo does not exist yet, querying its submodules will
1879 # mess up its states; so return here.
1880 return result
1881 for rev, path, url in self._GetSubmodules():
1882 name = self.manifest.GetSubprojectName(self, path)
David James8d201162013-10-11 17:03:19 -07001883 relpath, worktree, gitdir, objdir = \
1884 self.manifest.GetSubprojectPaths(self, name, path)
1885 project = self.manifest.paths.get(relpath)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001886 if project:
1887 result.extend(project.GetDerivedSubprojects())
1888 continue
David James8d201162013-10-11 17:03:19 -07001889
Shouheng Zhang02c0ee62017-12-08 09:54:58 +08001890 if url.startswith('..'):
1891 url = urllib.parse.urljoin("%s/" % self.remote.url, url)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001892 remote = RemoteSpec(self.remote.name,
Anthony King7bdac712014-07-16 12:56:40 +01001893 url=url,
Steve Raed6480452016-08-10 15:00:00 -07001894 pushUrl=self.remote.pushUrl,
Anthony King7bdac712014-07-16 12:56:40 +01001895 review=self.remote.review,
1896 revision=self.remote.revision)
1897 subproject = Project(manifest=self.manifest,
1898 name=name,
1899 remote=remote,
1900 gitdir=gitdir,
1901 objdir=objdir,
1902 worktree=worktree,
1903 relpath=relpath,
Aymen Bouaziz2598ed02016-06-24 14:34:08 +02001904 revisionExpr=rev,
Anthony King7bdac712014-07-16 12:56:40 +01001905 revisionId=rev,
1906 rebase=self.rebase,
1907 groups=self.groups,
1908 sync_c=self.sync_c,
1909 sync_s=self.sync_s,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001910 sync_tags=self.sync_tags,
Anthony King7bdac712014-07-16 12:56:40 +01001911 parent=self,
1912 is_derived=True)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001913 result.append(subproject)
1914 result.extend(subproject.GetDerivedSubprojects())
1915 return result
1916
1917
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001918# Direct Git Commands ##
Zac Livingstone4332262017-06-16 08:56:09 -06001919 def _CheckForImmutableRevision(self):
Chris AtLee2fb64662014-01-16 21:32:33 -05001920 try:
1921 # if revision (sha or tag) is not present then following function
1922 # throws an error.
1923 self.bare_git.rev_parse('--verify', '%s^0' % self.revisionExpr)
1924 return True
1925 except GitError:
1926 # There is no such persistent revision. We have to fetch it.
1927 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001928
Julien Campergue335f5ef2013-10-16 11:02:35 +02001929 def _FetchArchive(self, tarpath, cwd=None):
1930 cmd = ['archive', '-v', '-o', tarpath]
1931 cmd.append('--remote=%s' % self.remote.url)
1932 cmd.append('--prefix=%s/' % self.relpath)
1933 cmd.append(self.revisionExpr)
1934
1935 command = GitCommand(self, cmd, cwd=cwd,
1936 capture_stdout=True,
1937 capture_stderr=True)
1938
1939 if command.Wait() != 0:
1940 raise GitError('git archive %s: %s' % (self.name, command.stderr))
1941
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001942 def _RemoteFetch(self, name=None,
1943 current_branch_only=False,
Shawn O. Pearce16614f82010-10-29 12:05:43 -07001944 initial=False,
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001945 quiet=False,
Mitchel Humpherys597868b2012-10-29 10:18:34 -07001946 alt_dir=None,
David Pursehouse74cfd272015-10-14 10:50:15 +09001947 no_tags=False,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001948 prune=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07001949 depth=None,
1950 submodules=False):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001951
1952 is_sha1 = False
1953 tag_name = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09001954 # The depth should not be used when fetching to a mirror because
1955 # it will result in a shallow repository that cannot be cloned or
1956 # fetched from.
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001957 # The repo project should also never be synced with partial depth.
1958 if self.manifest.IsMirror or self.relpath == '.repo/repo':
1959 depth = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09001960
Shawn Pearce69e04d82014-01-29 12:48:54 -08001961 if depth:
1962 current_branch_only = True
1963
Nasser Grainawi909d58b2014-09-19 12:13:04 -06001964 if ID_RE.match(self.revisionExpr) is not None:
1965 is_sha1 = True
1966
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001967 if current_branch_only:
Nasser Grainawi909d58b2014-09-19 12:13:04 -06001968 if self.revisionExpr.startswith(R_TAGS):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001969 # this is a tag and its sha1 value should never change
1970 tag_name = self.revisionExpr[len(R_TAGS):]
1971
1972 if is_sha1 or tag_name is not None:
Zac Livingstone4332262017-06-16 08:56:09 -06001973 if self._CheckForImmutableRevision():
1974 print('Skipped fetching project %s (already have persistent ref)'
1975 % self.name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001976 return True
Bertrand SIMONNET3000cda2014-11-25 16:19:29 -08001977 if is_sha1 and not depth:
1978 # When syncing a specific commit and --depth is not set:
1979 # * if upstream is explicitly specified and is not a sha1, fetch only
1980 # upstream as users expect only upstream to be fetch.
1981 # Note: The commit might not be in upstream in which case the sync
1982 # will fail.
1983 # * otherwise, fetch all branches to make sure we end up with the
1984 # specific commit.
Aymen Bouaziz037040f2016-06-28 12:27:23 +02001985 if self.upstream:
1986 current_branch_only = not ID_RE.match(self.upstream)
1987 else:
1988 current_branch_only = False
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001989
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001990 if not name:
1991 name = self.remote.name
Shawn O. Pearcefb231612009-04-10 18:53:46 -07001992
1993 ssh_proxy = False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001994 remote = self.GetRemote(name)
1995 if remote.PreConnectFetch():
Shawn O. Pearcefb231612009-04-10 18:53:46 -07001996 ssh_proxy = True
1997
Shawn O. Pearce88443382010-10-08 10:02:09 +02001998 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001999 if alt_dir and 'objects' == os.path.basename(alt_dir):
2000 ref_dir = os.path.dirname(alt_dir)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002001 packed_refs = os.path.join(self.gitdir, 'packed-refs')
2002 remote = self.GetRemote(name)
2003
David Pursehouse8a68ff92012-09-24 12:15:13 +09002004 all_refs = self.bare_ref.all
2005 ids = set(all_refs.values())
Shawn O. Pearce88443382010-10-08 10:02:09 +02002006 tmp = set()
2007
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302008 for r, ref_id in GitRefs(ref_dir).all.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09002009 if r not in all_refs:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002010 if r.startswith(R_TAGS) or remote.WritesTo(r):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002011 all_refs[r] = ref_id
2012 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002013 continue
2014
David Pursehouse8a68ff92012-09-24 12:15:13 +09002015 if ref_id in ids:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002016 continue
2017
David Pursehouse8a68ff92012-09-24 12:15:13 +09002018 r = 'refs/_alt/%s' % ref_id
2019 all_refs[r] = ref_id
2020 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002021 tmp.add(r)
2022
heping3d7bbc92017-04-12 19:51:47 +08002023 tmp_packed_lines = []
2024 old_packed_lines = []
Shawn O. Pearce88443382010-10-08 10:02:09 +02002025
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302026 for r in sorted(all_refs):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002027 line = '%s %s\n' % (all_refs[r], r)
heping3d7bbc92017-04-12 19:51:47 +08002028 tmp_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002029 if r not in tmp:
heping3d7bbc92017-04-12 19:51:47 +08002030 old_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002031
heping3d7bbc92017-04-12 19:51:47 +08002032 tmp_packed = ''.join(tmp_packed_lines)
2033 old_packed = ''.join(old_packed_lines)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002034 _lwrite(packed_refs, tmp_packed)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002035 else:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002036 alt_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002037
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002038 cmd = ['fetch']
Doug Anderson30d45292011-05-04 15:01:04 -07002039
Conley Owensf97e8382015-01-21 11:12:46 -08002040 if depth:
Doug Anderson30d45292011-05-04 15:01:04 -07002041 cmd.append('--depth=%s' % depth)
Dan Willemseneeab6862015-08-03 13:11:53 -07002042 else:
2043 # If this repo has shallow objects, then we don't know which refs have
2044 # shallow objects or not. Tell git to unshallow all fetched refs. Don't
2045 # do this with projects that don't have shallow objects, since it is less
2046 # efficient.
2047 if os.path.exists(os.path.join(self.gitdir, 'shallow')):
2048 cmd.append('--depth=2147483647')
Doug Anderson30d45292011-05-04 15:01:04 -07002049
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002050 if quiet:
2051 cmd.append('--quiet')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002052 if not self.worktree:
2053 cmd.append('--update-head-ok')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002054 cmd.append(name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002055
Mitchel Humpherys26c45a72014-03-10 14:21:59 -07002056 # If using depth then we should not get all the tags since they may
2057 # be outside of the depth.
2058 if no_tags or depth:
2059 cmd.append('--no-tags')
2060 else:
2061 cmd.append('--tags')
2062
David Pursehouse74cfd272015-10-14 10:50:15 +09002063 if prune:
2064 cmd.append('--prune')
2065
Martin Kellye4e94d22017-03-21 16:05:12 -07002066 if submodules:
2067 cmd.append('--recurse-submodules=on-demand')
2068
Conley Owens80b87fe2014-05-09 17:13:44 -07002069 spec = []
Brian Harring14a66742012-09-28 20:21:57 -07002070 if not current_branch_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002071 # Fetch whole repo
Conley Owens80b87fe2014-05-09 17:13:44 -07002072 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002073 elif tag_name is not None:
Conley Owens80b87fe2014-05-09 17:13:44 -07002074 spec.append('tag')
2075 spec.append(tag_name)
Nasser Grainawi04e52d62014-09-30 13:34:52 -06002076
David Pursehouse403b64e2015-04-27 10:41:33 +09002077 if not self.manifest.IsMirror:
2078 branch = self.revisionExpr
Kevin Degi679bac42015-06-22 15:31:26 -06002079 if is_sha1 and depth and git_require((1, 8, 3)):
David Pursehouse403b64e2015-04-27 10:41:33 +09002080 # Shallow checkout of a specific commit, fetch from that commit and not
2081 # the heads only as the commit might be deeper in the history.
2082 spec.append(branch)
2083 else:
2084 if is_sha1:
2085 branch = self.upstream
2086 if branch is not None and branch.strip():
2087 if not branch.startswith('refs/'):
2088 branch = R_HEADS + branch
2089 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch)))
Conley Owens80b87fe2014-05-09 17:13:44 -07002090 cmd.extend(spec)
2091
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002092 ok = False
David Pursehouse8a68ff92012-09-24 12:15:13 +09002093 for _i in range(2):
John L. Villalovos9c76f672015-03-16 20:49:10 -07002094 gitcmd = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy)
John L. Villalovos126e2982015-01-29 21:58:12 -08002095 ret = gitcmd.Wait()
Brian Harring14a66742012-09-28 20:21:57 -07002096 if ret == 0:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002097 ok = True
2098 break
John L. Villalovos126e2982015-01-29 21:58:12 -08002099 # If needed, run the 'git remote prune' the first time through the loop
2100 elif (not _i and
2101 "error:" in gitcmd.stderr and
2102 "git remote prune" in gitcmd.stderr):
2103 prunecmd = GitCommand(self, ['remote', 'prune', name], bare=True,
John L. Villalovos9c76f672015-03-16 20:49:10 -07002104 ssh_proxy=ssh_proxy)
John L. Villalovose30f46b2015-02-25 14:27:02 -08002105 ret = prunecmd.Wait()
John L. Villalovose30f46b2015-02-25 14:27:02 -08002106 if ret:
John L. Villalovos126e2982015-01-29 21:58:12 -08002107 break
2108 continue
Brian Harring14a66742012-09-28 20:21:57 -07002109 elif current_branch_only and is_sha1 and ret == 128:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002110 # Exit code 128 means "couldn't find the ref you asked for"; if we're
2111 # in sha1 mode, we just tried sync'ing from the upstream field; it
2112 # doesn't exist, thus abort the optimization attempt and do a full sync.
Brian Harring14a66742012-09-28 20:21:57 -07002113 break
Colin Crossc4b301f2015-05-13 00:10:02 -07002114 elif ret < 0:
2115 # Git died with a signal, exit immediately
2116 break
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002117 time.sleep(random.randint(30, 45))
Shawn O. Pearce88443382010-10-08 10:02:09 +02002118
2119 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002120 if alt_dir:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002121 if old_packed != '':
2122 _lwrite(packed_refs, old_packed)
2123 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002124 platform_utils.remove(packed_refs)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002125 self.bare_git.pack_refs('--all', '--prune')
Brian Harring14a66742012-09-28 20:21:57 -07002126
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002127 if is_sha1 and current_branch_only:
Brian Harring14a66742012-09-28 20:21:57 -07002128 # We just synced the upstream given branch; verify we
2129 # got what we wanted, else trigger a second run of all
2130 # refs.
Zac Livingstone4332262017-06-16 08:56:09 -06002131 if not self._CheckForImmutableRevision():
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002132 if current_branch_only and depth:
2133 # Sync the current branch only with depth set to None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002134 return self._RemoteFetch(name=name,
2135 current_branch_only=current_branch_only,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002136 initial=False, quiet=quiet, alt_dir=alt_dir,
2137 depth=None)
2138 else:
2139 # Avoid infinite recursion: sync all branches with depth set to None
2140 return self._RemoteFetch(name=name, current_branch_only=False,
2141 initial=False, quiet=quiet, alt_dir=alt_dir,
2142 depth=None)
Brian Harring14a66742012-09-28 20:21:57 -07002143
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002144 return ok
Shawn O. Pearce88443382010-10-08 10:02:09 +02002145
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002146 def _ApplyCloneBundle(self, initial=False, quiet=False):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002147 if initial and \
2148 (self.manifest.manifestProject.config.GetString('repo.depth') or
2149 self.clone_depth):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002150 return False
2151
2152 remote = self.GetRemote(self.remote.name)
2153 bundle_url = remote.url + '/clone.bundle'
2154 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002155 if GetSchemeFromUrl(bundle_url) not in ('http', 'https',
2156 'persistent-http',
2157 'persistent-https'):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002158 return False
2159
2160 bundle_dst = os.path.join(self.gitdir, 'clone.bundle')
2161 bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp')
2162
2163 exist_dst = os.path.exists(bundle_dst)
2164 exist_tmp = os.path.exists(bundle_tmp)
2165
2166 if not initial and not exist_dst and not exist_tmp:
2167 return False
2168
2169 if not exist_dst:
2170 exist_dst = self._FetchBundle(bundle_url, bundle_tmp, bundle_dst, quiet)
2171 if not exist_dst:
2172 return False
2173
2174 cmd = ['fetch']
2175 if quiet:
2176 cmd.append('--quiet')
2177 if not self.worktree:
2178 cmd.append('--update-head-ok')
2179 cmd.append(bundle_dst)
2180 for f in remote.fetch:
2181 cmd.append(str(f))
2182 cmd.append('refs/tags/*:refs/tags/*')
2183
2184 ok = GitCommand(self, cmd, bare=True).Wait() == 0
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002185 if os.path.exists(bundle_dst):
Renaud Paquay010fed72016-11-11 14:25:29 -08002186 platform_utils.remove(bundle_dst)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002187 if os.path.exists(bundle_tmp):
Renaud Paquay010fed72016-11-11 14:25:29 -08002188 platform_utils.remove(bundle_tmp)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002189 return ok
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002190
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002191 def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet):
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002192 if os.path.exists(dstPath):
Renaud Paquay010fed72016-11-11 14:25:29 -08002193 platform_utils.remove(dstPath)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002194
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002195 cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002196 if quiet:
2197 cmd += ['--silent']
2198 if os.path.exists(tmpPath):
2199 size = os.stat(tmpPath).st_size
2200 if size >= 1024:
2201 cmd += ['--continue-at', '%d' % (size,)]
2202 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002203 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002204 if 'http_proxy' in os.environ and 'darwin' == sys.platform:
2205 cmd += ['--proxy', os.environ['http_proxy']]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002206 with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, _proxy):
Dave Borowitz137d0132015-01-02 11:12:54 -08002207 if cookiefile:
Dave Borowitz4abf8e62015-01-02 11:39:04 -08002208 cmd += ['--cookie', cookiefile, '--cookie-jar', cookiefile]
Dave Borowitz137d0132015-01-02 11:12:54 -08002209 if srcUrl.startswith('persistent-'):
2210 srcUrl = srcUrl[len('persistent-'):]
2211 cmd += [srcUrl]
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002212
Dave Borowitz137d0132015-01-02 11:12:54 -08002213 if IsTrace():
2214 Trace('%s', ' '.join(cmd))
2215 try:
2216 proc = subprocess.Popen(cmd)
2217 except OSError:
2218 return False
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002219
Dave Borowitz137d0132015-01-02 11:12:54 -08002220 curlret = proc.wait()
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002221
Dave Borowitz137d0132015-01-02 11:12:54 -08002222 if curlret == 22:
2223 # From curl man page:
2224 # 22: HTTP page not retrieved. The requested url was not found or
2225 # returned another error with the HTTP error code being 400 or above.
2226 # This return code only appears if -f, --fail is used.
2227 if not quiet:
2228 print("Server does not provide clone.bundle; ignoring.",
2229 file=sys.stderr)
2230 return False
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002231
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002232 if os.path.exists(tmpPath):
Kris Giesingc8d882a2014-12-23 13:02:32 -08002233 if curlret == 0 and self._IsValidBundle(tmpPath, quiet):
Renaud Paquayad1abcb2016-11-01 11:34:55 -07002234 platform_utils.rename(tmpPath, dstPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002235 return True
2236 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002237 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002238 return False
2239 else:
2240 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002241
Kris Giesingc8d882a2014-12-23 13:02:32 -08002242 def _IsValidBundle(self, path, quiet):
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002243 try:
2244 with open(path) as f:
2245 if f.read(16) == '# v2 git bundle\n':
2246 return True
2247 else:
Kris Giesingc8d882a2014-12-23 13:02:32 -08002248 if not quiet:
2249 print("Invalid clone.bundle file; ignoring.", file=sys.stderr)
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002250 return False
2251 except OSError:
2252 return False
2253
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002254 def _Checkout(self, rev, quiet=False):
2255 cmd = ['checkout']
2256 if quiet:
2257 cmd.append('-q')
2258 cmd.append(rev)
2259 cmd.append('--')
2260 if GitCommand(self, cmd).Wait() != 0:
2261 if self._allrefs:
2262 raise GitError('%s checkout %s ' % (self.name, rev))
2263
Anthony King7bdac712014-07-16 12:56:40 +01002264 def _CherryPick(self, rev):
Pierre Tardye5a21222011-03-24 16:28:18 +01002265 cmd = ['cherry-pick']
2266 cmd.append(rev)
2267 cmd.append('--')
2268 if GitCommand(self, cmd).Wait() != 0:
2269 if self._allrefs:
2270 raise GitError('%s cherry-pick %s ' % (self.name, rev))
2271
Anthony King7bdac712014-07-16 12:56:40 +01002272 def _Revert(self, rev):
Erwan Mahea94f1622011-08-19 13:56:09 +02002273 cmd = ['revert']
2274 cmd.append('--no-edit')
2275 cmd.append(rev)
2276 cmd.append('--')
2277 if GitCommand(self, cmd).Wait() != 0:
2278 if self._allrefs:
2279 raise GitError('%s revert %s ' % (self.name, rev))
2280
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002281 def _ResetHard(self, rev, quiet=True):
2282 cmd = ['reset', '--hard']
2283 if quiet:
2284 cmd.append('-q')
2285 cmd.append(rev)
2286 if GitCommand(self, cmd).Wait() != 0:
2287 raise GitError('%s reset --hard %s ' % (self.name, rev))
2288
Martin Kellye4e94d22017-03-21 16:05:12 -07002289 def _SyncSubmodules(self, quiet=True):
2290 cmd = ['submodule', 'update', '--init', '--recursive']
2291 if quiet:
2292 cmd.append('-q')
2293 if GitCommand(self, cmd).Wait() != 0:
2294 raise GitError('%s submodule update --init --recursive %s ' % self.name)
2295
Anthony King7bdac712014-07-16 12:56:40 +01002296 def _Rebase(self, upstream, onto=None):
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002297 cmd = ['rebase']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002298 if onto is not None:
2299 cmd.extend(['--onto', onto])
2300 cmd.append(upstream)
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002301 if GitCommand(self, cmd).Wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002302 raise GitError('%s rebase %s ' % (self.name, upstream))
2303
Pierre Tardy3d125942012-05-04 12:18:12 +02002304 def _FastForward(self, head, ffonly=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002305 cmd = ['merge', head]
Pierre Tardy3d125942012-05-04 12:18:12 +02002306 if ffonly:
2307 cmd.append("--ff-only")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002308 if GitCommand(self, cmd).Wait() != 0:
2309 raise GitError('%s merge %s ' % (self.name, head))
2310
Kevin Degiabaa7f32014-11-12 11:27:45 -07002311 def _InitGitDir(self, mirror_git=None, force_sync=False):
Kevin Degi384b3c52014-10-16 16:02:58 -06002312 init_git_dir = not os.path.exists(self.gitdir)
2313 init_obj_dir = not os.path.exists(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002314 try:
2315 # Initialize the bare repository, which contains all of the objects.
2316 if init_obj_dir:
2317 os.makedirs(self.objdir)
2318 self.bare_objdir.init()
David James8d201162013-10-11 17:03:19 -07002319
Kevin Degib1a07b82015-07-27 13:33:43 -06002320 # If we have a separate directory to hold refs, initialize it as well.
2321 if self.objdir != self.gitdir:
2322 if init_git_dir:
2323 os.makedirs(self.gitdir)
2324
2325 if init_obj_dir or init_git_dir:
2326 self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False,
2327 copy_all=True)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002328 try:
2329 self._CheckDirReference(self.objdir, self.gitdir, share_refs=False)
2330 except GitError as e:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002331 if force_sync:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002332 print("Retrying clone after deleting %s" %
2333 self.gitdir, file=sys.stderr)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002334 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002335 platform_utils.rmtree(platform_utils.realpath(self.gitdir))
2336 if self.worktree and os.path.exists(platform_utils.realpath
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002337 (self.worktree)):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002338 platform_utils.rmtree(platform_utils.realpath(self.worktree))
Kevin Degiabaa7f32014-11-12 11:27:45 -07002339 return self._InitGitDir(mirror_git=mirror_git, force_sync=False)
2340 except:
2341 raise e
2342 raise e
Kevin Degib1a07b82015-07-27 13:33:43 -06002343
Kevin Degi384b3c52014-10-16 16:02:58 -06002344 if init_git_dir:
Kevin Degib1a07b82015-07-27 13:33:43 -06002345 mp = self.manifest.manifestProject
2346 ref_dir = mp.config.GetString('repo.reference') or ''
Kevin Degi384b3c52014-10-16 16:02:58 -06002347
Kevin Degib1a07b82015-07-27 13:33:43 -06002348 if ref_dir or mirror_git:
2349 if not mirror_git:
2350 mirror_git = os.path.join(ref_dir, self.name + '.git')
2351 repo_git = os.path.join(ref_dir, '.repo', 'projects',
2352 self.relpath + '.git')
Shawn O. Pearce2816d4f2009-03-03 17:53:18 -08002353
Kevin Degib1a07b82015-07-27 13:33:43 -06002354 if os.path.exists(mirror_git):
2355 ref_dir = mirror_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002356
Kevin Degib1a07b82015-07-27 13:33:43 -06002357 elif os.path.exists(repo_git):
2358 ref_dir = repo_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002359
Kevin Degib1a07b82015-07-27 13:33:43 -06002360 else:
2361 ref_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002362
Kevin Degib1a07b82015-07-27 13:33:43 -06002363 if ref_dir:
2364 _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'),
2365 os.path.join(ref_dir, 'objects') + '\n')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002366
Kevin Degib1a07b82015-07-27 13:33:43 -06002367 self._UpdateHooks()
2368
2369 m = self.manifest.manifestProject.config
2370 for key in ['user.name', 'user.email']:
2371 if m.Has(key, include_defaults=False):
2372 self.config.SetString(key, m.GetString(key))
David Pursehouse76a4a9d2016-08-16 12:11:12 +09002373 self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
Kevin Degib1a07b82015-07-27 13:33:43 -06002374 if self.manifest.IsMirror:
2375 self.config.SetString('core.bare', 'true')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002376 else:
Kevin Degib1a07b82015-07-27 13:33:43 -06002377 self.config.SetString('core.bare', None)
2378 except Exception:
2379 if init_obj_dir and os.path.exists(self.objdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002380 platform_utils.rmtree(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002381 if init_git_dir and os.path.exists(self.gitdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002382 platform_utils.rmtree(self.gitdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002383 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002384
Jimmie Westera0444582012-10-24 13:44:42 +02002385 def _UpdateHooks(self):
2386 if os.path.exists(self.gitdir):
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002387 self._InitHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002388
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002389 def _InitHooks(self):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002390 hooks = platform_utils.realpath(self._gitdir_path('hooks'))
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002391 if not os.path.exists(hooks):
2392 os.makedirs(hooks)
Jonathan Nieder93719792015-03-17 11:29:58 -07002393 for stock_hook in _ProjectHooks():
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002394 name = os.path.basename(stock_hook)
2395
Victor Boivie65e0f352011-04-18 11:23:29 +02002396 if name in ('commit-msg',) and not self.remote.review \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002397 and self is not self.manifest.manifestProject:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002398 # Don't install a Gerrit Code Review hook if this
2399 # project does not appear to use it for reviews.
2400 #
Victor Boivie65e0f352011-04-18 11:23:29 +02002401 # Since the manifest project is one of those, but also
2402 # managed through gerrit, it's excluded
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002403 continue
2404
2405 dst = os.path.join(hooks, name)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002406 if platform_utils.islink(dst):
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002407 continue
2408 if os.path.exists(dst):
2409 if filecmp.cmp(stock_hook, dst, shallow=False):
Renaud Paquay010fed72016-11-11 14:25:29 -08002410 platform_utils.remove(dst)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002411 else:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002412 _warn("%s: Not replacing locally modified %s hook",
2413 self.relpath, name)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002414 continue
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002415 try:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002416 platform_utils.symlink(
2417 os.path.relpath(stock_hook, os.path.dirname(dst)), dst)
Sarah Owensa5be53f2012-09-09 15:37:57 -07002418 except OSError as e:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002419 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002420 raise GitError(self._get_symlink_error_message())
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002421 else:
2422 raise
2423
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002424 def _InitRemote(self):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002425 if self.remote.url:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002426 remote = self.GetRemote(self.remote.name)
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002427 remote.url = self.remote.url
Steve Raed6480452016-08-10 15:00:00 -07002428 remote.pushUrl = self.remote.pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002429 remote.review = self.remote.review
2430 remote.projectname = self.name
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002431
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002432 if self.worktree:
2433 remote.ResetFetch(mirror=False)
2434 else:
2435 remote.ResetFetch(mirror=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002436 remote.Save()
2437
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002438 def _InitMRef(self):
2439 if self.manifest.branch:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002440 self._InitAnyMRef(R_M + self.manifest.branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002441
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002442 def _InitMirrorHead(self):
Shawn O. Pearcefe200ee2009-06-01 15:28:21 -07002443 self._InitAnyMRef(HEAD)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002444
2445 def _InitAnyMRef(self, ref):
2446 cur = self.bare_ref.symref(ref)
2447
2448 if self.revisionId:
2449 if cur != '' or self.bare_ref.get(ref) != self.revisionId:
2450 msg = 'manifest set to %s' % self.revisionId
2451 dst = self.revisionId + '^0'
Anthony King7bdac712014-07-16 12:56:40 +01002452 self.bare_git.UpdateRef(ref, dst, message=msg, detach=True)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002453 else:
2454 remote = self.GetRemote(self.remote.name)
2455 dst = remote.ToLocal(self.revisionExpr)
2456 if cur != dst:
2457 msg = 'manifest set to %s' % self.revisionExpr
2458 self.bare_git.symbolic_ref('-m', msg, ref, dst)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002459
Kevin Degi384b3c52014-10-16 16:02:58 -06002460 def _CheckDirReference(self, srcdir, destdir, share_refs):
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002461 symlink_files = self.shareable_files[:]
2462 symlink_dirs = self.shareable_dirs[:]
Kevin Degi384b3c52014-10-16 16:02:58 -06002463 if share_refs:
2464 symlink_files += self.working_tree_files
2465 symlink_dirs += self.working_tree_dirs
2466 to_symlink = symlink_files + symlink_dirs
2467 for name in set(to_symlink):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002468 dst = platform_utils.realpath(os.path.join(destdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002469 if os.path.lexists(dst):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002470 src = platform_utils.realpath(os.path.join(srcdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002471 # Fail if the links are pointing to the wrong place
2472 if src != dst:
Marc Herbertec287902016-10-27 12:58:26 -07002473 _error('%s is different in %s vs %s', name, destdir, srcdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002474 raise GitError('--force-sync not enabled; cannot overwrite a local '
Simon Ruggierf9b76832015-07-31 17:18:34 -04002475 'work tree. If you\'re comfortable with the '
2476 'possibility of losing the work tree\'s git metadata,'
2477 ' use `repo sync --force-sync {0}` to '
2478 'proceed.'.format(self.relpath))
Kevin Degi384b3c52014-10-16 16:02:58 -06002479
David James8d201162013-10-11 17:03:19 -07002480 def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all):
2481 """Update |dotgit| to reference |gitdir|, using symlinks where possible.
2482
2483 Args:
2484 gitdir: The bare git repository. Must already be initialized.
2485 dotgit: The repository you would like to initialize.
2486 share_refs: If true, |dotgit| will store its refs under |gitdir|.
2487 Only one work tree can store refs under a given |gitdir|.
2488 copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
2489 This saves you the effort of initializing |dotgit| yourself.
2490 """
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002491 symlink_files = self.shareable_files[:]
2492 symlink_dirs = self.shareable_dirs[:]
David James8d201162013-10-11 17:03:19 -07002493 if share_refs:
Kevin Degi384b3c52014-10-16 16:02:58 -06002494 symlink_files += self.working_tree_files
2495 symlink_dirs += self.working_tree_dirs
David James8d201162013-10-11 17:03:19 -07002496 to_symlink = symlink_files + symlink_dirs
2497
2498 to_copy = []
2499 if copy_all:
2500 to_copy = os.listdir(gitdir)
2501
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002502 dotgit = platform_utils.realpath(dotgit)
David James8d201162013-10-11 17:03:19 -07002503 for name in set(to_copy).union(to_symlink):
2504 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002505 src = platform_utils.realpath(os.path.join(gitdir, name))
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002506 dst = os.path.join(dotgit, name)
David James8d201162013-10-11 17:03:19 -07002507
Kevin Degi384b3c52014-10-16 16:02:58 -06002508 if os.path.lexists(dst):
2509 continue
David James8d201162013-10-11 17:03:19 -07002510
2511 # If the source dir doesn't exist, create an empty dir.
2512 if name in symlink_dirs and not os.path.lexists(src):
2513 os.makedirs(src)
2514
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002515 if name in to_symlink:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002516 platform_utils.symlink(
2517 os.path.relpath(src, os.path.dirname(dst)), dst)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002518 elif copy_all and not platform_utils.islink(dst):
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002519 if os.path.isdir(src):
2520 shutil.copytree(src, dst)
2521 elif os.path.isfile(src):
2522 shutil.copy(src, dst)
2523
Conley Owens80b87fe2014-05-09 17:13:44 -07002524 # If the source file doesn't exist, ensure the destination
2525 # file doesn't either.
2526 if name in symlink_files and not os.path.lexists(src):
2527 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08002528 platform_utils.remove(dst)
Conley Owens80b87fe2014-05-09 17:13:44 -07002529 except OSError:
2530 pass
2531
David James8d201162013-10-11 17:03:19 -07002532 except OSError as e:
2533 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002534 raise DownloadError(self._get_symlink_error_message())
David James8d201162013-10-11 17:03:19 -07002535 else:
2536 raise
2537
Martin Kellye4e94d22017-03-21 16:05:12 -07002538 def _InitWorkTree(self, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002539 dotgit = os.path.join(self.worktree, '.git')
Kevin Degi384b3c52014-10-16 16:02:58 -06002540 init_dotgit = not os.path.exists(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002541 try:
2542 if init_dotgit:
2543 os.makedirs(dotgit)
2544 self._ReferenceGitDir(self.gitdir, dotgit, share_refs=True,
2545 copy_all=False)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002546
Kevin Degiabaa7f32014-11-12 11:27:45 -07002547 try:
2548 self._CheckDirReference(self.gitdir, dotgit, share_refs=True)
2549 except GitError as e:
2550 if force_sync:
2551 try:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002552 platform_utils.rmtree(dotgit)
Martin Kellye4e94d22017-03-21 16:05:12 -07002553 return self._InitWorkTree(force_sync=False, submodules=submodules)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002554 except:
2555 raise e
2556 raise e
Kevin Degi384b3c52014-10-16 16:02:58 -06002557
Kevin Degib1a07b82015-07-27 13:33:43 -06002558 if init_dotgit:
2559 _lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002560
Kevin Degib1a07b82015-07-27 13:33:43 -06002561 cmd = ['read-tree', '--reset', '-u']
2562 cmd.append('-v')
2563 cmd.append(HEAD)
2564 if GitCommand(self, cmd).Wait() != 0:
2565 raise GitError("cannot initialize work tree")
Victor Boivie0960b5b2010-11-26 13:42:13 +01002566
Martin Kellye4e94d22017-03-21 16:05:12 -07002567 if submodules:
2568 self._SyncSubmodules(quiet=True)
Kevin Degib1a07b82015-07-27 13:33:43 -06002569 self._CopyAndLinkFiles()
2570 except Exception:
2571 if init_dotgit:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002572 platform_utils.rmtree(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002573 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002574
Renaud Paquay788e9622017-01-27 11:41:12 -08002575 def _get_symlink_error_message(self):
2576 if platform_utils.isWindows():
2577 return ('Unable to create symbolic link. Please re-run the command as '
2578 'Administrator, or see '
2579 'https://github.com/git-for-windows/git/wiki/Symbolic-Links '
2580 'for other options.')
2581 return 'filesystem must support symlinks'
2582
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002583 def _gitdir_path(self, path):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002584 return platform_utils.realpath(os.path.join(self.gitdir, path))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002585
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002586 def _revlist(self, *args, **kw):
2587 a = []
2588 a.extend(args)
2589 a.append('--')
2590 return self.work_git.rev_list(*a, **kw)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002591
2592 @property
2593 def _allrefs(self):
Shawn O. Pearced237b692009-04-17 18:49:50 -07002594 return self.bare_ref.all
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002595
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002596 def _getLogs(self, rev1, rev2, oneline=False, color=True, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002597 """Get logs between two revisions of this project."""
2598 comp = '..'
2599 if rev1:
2600 revs = [rev1]
2601 if rev2:
2602 revs.extend([comp, rev2])
2603 cmd = ['log', ''.join(revs)]
2604 out = DiffColoring(self.config)
2605 if out.is_on and color:
2606 cmd.append('--color')
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002607 if pretty_format is not None:
2608 cmd.append('--pretty=format:%s' % pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002609 if oneline:
2610 cmd.append('--oneline')
2611
2612 try:
2613 log = GitCommand(self, cmd, capture_stdout=True, capture_stderr=True)
2614 if log.Wait() == 0:
2615 return log.stdout
2616 except GitError:
2617 # worktree may not exist if groups changed for example. In that case,
2618 # try in gitdir instead.
2619 if not os.path.exists(self.worktree):
2620 return self.bare_git.log(*cmd[1:])
2621 else:
2622 raise
2623 return None
2624
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002625 def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True,
2626 pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002627 """Get the list of logs from this revision to given revisionId"""
2628 logs = {}
2629 selfId = self.GetRevisionId(self._allrefs)
2630 toId = toProject.GetRevisionId(toProject._allrefs)
2631
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002632 logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color,
2633 pretty_format=pretty_format)
2634 logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color,
2635 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002636 return logs
2637
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002638 class _GitGetByExec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002639
David James8d201162013-10-11 17:03:19 -07002640 def __init__(self, project, bare, gitdir):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002641 self._project = project
2642 self._bare = bare
David James8d201162013-10-11 17:03:19 -07002643 self._gitdir = gitdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002644
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002645 def LsOthers(self):
2646 p = GitCommand(self._project,
2647 ['ls-files',
2648 '-z',
2649 '--others',
2650 '--exclude-standard'],
Anthony King7bdac712014-07-16 12:56:40 +01002651 bare=False,
David James8d201162013-10-11 17:03:19 -07002652 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002653 capture_stdout=True,
2654 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002655 if p.Wait() == 0:
2656 out = p.stdout
2657 if out:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002658 # Backslash is not anomalous
David Pursehouse1d947b32012-10-25 12:23:11 +09002659 return out[:-1].split('\0') # pylint: disable=W1401
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002660 return []
2661
2662 def DiffZ(self, name, *args):
2663 cmd = [name]
2664 cmd.append('-z')
2665 cmd.extend(args)
2666 p = GitCommand(self._project,
2667 cmd,
David James8d201162013-10-11 17:03:19 -07002668 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002669 bare=False,
2670 capture_stdout=True,
2671 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002672 try:
2673 out = p.process.stdout.read()
2674 r = {}
2675 if out:
David Pursehouse1d947b32012-10-25 12:23:11 +09002676 out = iter(out[:-1].split('\0')) # pylint: disable=W1401
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002677 while out:
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002678 try:
Anthony King2cd1f042014-05-05 21:24:05 +01002679 info = next(out)
2680 path = next(out)
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002681 except StopIteration:
2682 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002683
2684 class _Info(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002685
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002686 def __init__(self, path, omode, nmode, oid, nid, state):
2687 self.path = path
2688 self.src_path = None
2689 self.old_mode = omode
2690 self.new_mode = nmode
2691 self.old_id = oid
2692 self.new_id = nid
2693
2694 if len(state) == 1:
2695 self.status = state
2696 self.level = None
2697 else:
2698 self.status = state[:1]
2699 self.level = state[1:]
2700 while self.level.startswith('0'):
2701 self.level = self.level[1:]
2702
2703 info = info[1:].split(' ')
David Pursehouse8f62fb72012-11-14 12:09:38 +09002704 info = _Info(path, *info)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002705 if info.status in ('R', 'C'):
2706 info.src_path = info.path
Anthony King2cd1f042014-05-05 21:24:05 +01002707 info.path = next(out)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002708 r[info.path] = info
2709 return r
2710 finally:
2711 p.Wait()
2712
2713 def GetHead(self):
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002714 if self._bare:
2715 path = os.path.join(self._project.gitdir, HEAD)
2716 else:
2717 path = os.path.join(self._project.worktree, '.git', HEAD)
Conley Owens75ee0572012-11-15 17:33:11 -08002718 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002719 fd = open(path)
Dan Sandler53e902a2014-03-09 13:20:02 -04002720 except IOError as e:
2721 raise NoManifestException(path, str(e))
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002722 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002723 line = fd.readline()
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002724 finally:
2725 fd.close()
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302726 try:
2727 line = line.decode()
2728 except AttributeError:
2729 pass
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002730 if line.startswith('ref: '):
2731 return line[5:-1]
2732 return line[:-1]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002733
2734 def SetHead(self, ref, message=None):
2735 cmdv = []
2736 if message is not None:
2737 cmdv.extend(['-m', message])
2738 cmdv.append(HEAD)
2739 cmdv.append(ref)
2740 self.symbolic_ref(*cmdv)
2741
2742 def DetachHead(self, new, message=None):
2743 cmdv = ['--no-deref']
2744 if message is not None:
2745 cmdv.extend(['-m', message])
2746 cmdv.append(HEAD)
2747 cmdv.append(new)
2748 self.update_ref(*cmdv)
2749
2750 def UpdateRef(self, name, new, old=None,
2751 message=None,
2752 detach=False):
2753 cmdv = []
2754 if message is not None:
2755 cmdv.extend(['-m', message])
2756 if detach:
2757 cmdv.append('--no-deref')
2758 cmdv.append(name)
2759 cmdv.append(new)
2760 if old is not None:
2761 cmdv.append(old)
2762 self.update_ref(*cmdv)
2763
2764 def DeleteRef(self, name, old=None):
2765 if not old:
2766 old = self.rev_parse(name)
2767 self.update_ref('-d', name, old)
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07002768 self._project.bare_ref.deleted(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002769
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002770 def rev_list(self, *args, **kw):
2771 if 'format' in kw:
2772 cmdv = ['log', '--pretty=format:%s' % kw['format']]
2773 else:
2774 cmdv = ['rev-list']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002775 cmdv.extend(args)
2776 p = GitCommand(self._project,
2777 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002778 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002779 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002780 capture_stdout=True,
2781 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002782 r = []
2783 for line in p.process.stdout:
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002784 if line[-1] == '\n':
2785 line = line[:-1]
2786 r.append(line)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002787 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002788 raise GitError('%s rev-list %s: %s' %
2789 (self._project.name, str(args), p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002790 return r
2791
2792 def __getattr__(self, name):
Doug Anderson37282b42011-03-04 11:54:18 -08002793 """Allow arbitrary git commands using pythonic syntax.
2794
2795 This allows you to do things like:
2796 git_obj.rev_parse('HEAD')
2797
2798 Since we don't have a 'rev_parse' method defined, the __getattr__ will
2799 run. We'll replace the '_' with a '-' and try to run a git command.
Dave Borowitz091f8932012-10-23 17:01:04 -07002800 Any other positional arguments will be passed to the git command, and the
2801 following keyword arguments are supported:
2802 config: An optional dict of git config options to be passed with '-c'.
Doug Anderson37282b42011-03-04 11:54:18 -08002803
2804 Args:
2805 name: The name of the git command to call. Any '_' characters will
2806 be replaced with '-'.
2807
2808 Returns:
2809 A callable object that will try to call git with the named command.
2810 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002811 name = name.replace('_', '-')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002812
Dave Borowitz091f8932012-10-23 17:01:04 -07002813 def runner(*args, **kwargs):
2814 cmdv = []
2815 config = kwargs.pop('config', None)
2816 for k in kwargs:
2817 raise TypeError('%s() got an unexpected keyword argument %r'
2818 % (name, k))
2819 if config is not None:
Dave Borowitzb42b4742012-10-31 12:27:27 -07002820 if not git_require((1, 7, 2)):
2821 raise ValueError('cannot set config on command line for %s()'
2822 % name)
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302823 for k, v in config.items():
Dave Borowitz091f8932012-10-23 17:01:04 -07002824 cmdv.append('-c')
2825 cmdv.append('%s=%s' % (k, v))
2826 cmdv.append(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002827 cmdv.extend(args)
2828 p = GitCommand(self._project,
2829 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002830 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002831 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002832 capture_stdout=True,
2833 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002834 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002835 raise GitError('%s %s: %s' %
2836 (self._project.name, name, p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002837 r = p.stdout
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302838 try:
Conley Owensedd01512013-09-26 12:59:58 -07002839 r = r.decode('utf-8')
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302840 except AttributeError:
2841 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002842 if r.endswith('\n') and r.index('\n') == len(r) - 1:
2843 return r[:-1]
2844 return r
2845 return runner
2846
2847
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002848class _PriorSyncFailedError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002849
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002850 def __str__(self):
2851 return 'prior sync failed; rebase still in progress'
2852
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002853
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002854class _DirtyError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002855
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002856 def __str__(self):
2857 return 'contains uncommitted changes'
2858
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002859
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002860class _InfoMessage(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002861
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002862 def __init__(self, project, text):
2863 self.project = project
2864 self.text = text
2865
2866 def Print(self, syncbuf):
2867 syncbuf.out.info('%s/: %s', self.project.relpath, self.text)
2868 syncbuf.out.nl()
2869
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002870
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002871class _Failure(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002872
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002873 def __init__(self, project, why):
2874 self.project = project
2875 self.why = why
2876
2877 def Print(self, syncbuf):
2878 syncbuf.out.fail('error: %s/: %s',
2879 self.project.relpath,
2880 str(self.why))
2881 syncbuf.out.nl()
2882
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002883
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002884class _Later(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002885
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002886 def __init__(self, project, action):
2887 self.project = project
2888 self.action = action
2889
2890 def Run(self, syncbuf):
2891 out = syncbuf.out
2892 out.project('project %s/', self.project.relpath)
2893 out.nl()
2894 try:
2895 self.action()
2896 out.nl()
2897 return True
David Pursehouse8a68ff92012-09-24 12:15:13 +09002898 except GitError:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002899 out.nl()
2900 return False
2901
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002902
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002903class _SyncColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002904
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002905 def __init__(self, config):
2906 Coloring.__init__(self, config, 'reposync')
Anthony King7bdac712014-07-16 12:56:40 +01002907 self.project = self.printer('header', attr='bold')
2908 self.info = self.printer('info')
2909 self.fail = self.printer('fail', fg='red')
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002910
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002911
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002912class SyncBuffer(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002913
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002914 def __init__(self, config, detach_head=False):
2915 self._messages = []
2916 self._failures = []
2917 self._later_queue1 = []
2918 self._later_queue2 = []
2919
2920 self.out = _SyncColoring(config)
2921 self.out.redirect(sys.stderr)
2922
2923 self.detach_head = detach_head
2924 self.clean = True
David Rileye0684ad2017-04-05 00:02:59 -07002925 self.recent_clean = True
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002926
2927 def info(self, project, fmt, *args):
2928 self._messages.append(_InfoMessage(project, fmt % args))
2929
2930 def fail(self, project, err=None):
2931 self._failures.append(_Failure(project, err))
David Rileye0684ad2017-04-05 00:02:59 -07002932 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002933
2934 def later1(self, project, what):
2935 self._later_queue1.append(_Later(project, what))
2936
2937 def later2(self, project, what):
2938 self._later_queue2.append(_Later(project, what))
2939
2940 def Finish(self):
2941 self._PrintMessages()
2942 self._RunLater()
2943 self._PrintMessages()
2944 return self.clean
2945
David Rileye0684ad2017-04-05 00:02:59 -07002946 def Recently(self):
2947 recent_clean = self.recent_clean
2948 self.recent_clean = True
2949 return recent_clean
2950
2951 def _MarkUnclean(self):
2952 self.clean = False
2953 self.recent_clean = False
2954
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002955 def _RunLater(self):
2956 for q in ['_later_queue1', '_later_queue2']:
2957 if not self._RunQueue(q):
2958 return
2959
2960 def _RunQueue(self, queue):
2961 for m in getattr(self, queue):
2962 if not m.Run(self):
David Rileye0684ad2017-04-05 00:02:59 -07002963 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002964 return False
2965 setattr(self, queue, [])
2966 return True
2967
2968 def _PrintMessages(self):
2969 for m in self._messages:
2970 m.Print(self)
2971 for m in self._failures:
2972 m.Print(self)
2973
2974 self._messages = []
2975 self._failures = []
2976
2977
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002978class MetaProject(Project):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002979
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002980 """A special project housed under .repo.
2981 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002982
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002983 def __init__(self, manifest, name, gitdir, worktree):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002984 Project.__init__(self,
Anthony King7bdac712014-07-16 12:56:40 +01002985 manifest=manifest,
2986 name=name,
2987 gitdir=gitdir,
2988 objdir=gitdir,
2989 worktree=worktree,
2990 remote=RemoteSpec('origin'),
2991 relpath='.repo/%s' % name,
2992 revisionExpr='refs/heads/master',
2993 revisionId=None,
2994 groups=None)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002995
2996 def PreSync(self):
2997 if self.Exists:
2998 cb = self.CurrentBranch
2999 if cb:
3000 base = self.GetBranch(cb).merge
3001 if base:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003002 self.revisionExpr = base
3003 self.revisionId = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003004
Martin Kelly224a31a2017-07-10 14:46:25 -07003005 def MetaBranchSwitch(self, submodules=False):
Florian Vallee5d016502012-06-07 17:19:26 +02003006 """ Prepare MetaProject for manifest branch switch
3007 """
3008
3009 # detach and delete manifest branch, allowing a new
3010 # branch to take over
Anthony King7bdac712014-07-16 12:56:40 +01003011 syncbuf = SyncBuffer(self.config, detach_head=True)
Martin Kelly224a31a2017-07-10 14:46:25 -07003012 self.Sync_LocalHalf(syncbuf, submodules=submodules)
Florian Vallee5d016502012-06-07 17:19:26 +02003013 syncbuf.Finish()
3014
3015 return GitCommand(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003016 ['update-ref', '-d', 'refs/heads/default'],
3017 capture_stdout=True,
3018 capture_stderr=True).Wait() == 0
Florian Vallee5d016502012-06-07 17:19:26 +02003019
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003020 @property
Shawn O. Pearcef6906872009-04-18 10:49:00 -07003021 def LastFetch(self):
3022 try:
3023 fh = os.path.join(self.gitdir, 'FETCH_HEAD')
3024 return os.path.getmtime(fh)
3025 except OSError:
3026 return 0
3027
3028 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003029 def HasChanges(self):
3030 """Has the remote received new commits not yet checked out?
3031 """
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003032 if not self.remote or not self.revisionExpr:
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003033 return False
3034
David Pursehouse8a68ff92012-09-24 12:15:13 +09003035 all_refs = self.bare_ref.all
3036 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003037 head = self.work_git.GetHead()
3038 if head.startswith(R_HEADS):
3039 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09003040 head = all_refs[head]
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003041 except KeyError:
3042 head = None
3043
3044 if revid == head:
3045 return False
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003046 elif self._revlist(not_rev(HEAD), revid):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003047 return True
3048 return False