blob: a21f52fa5e7156174a7b9cdc29714897e5489b45 [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, \
Ningning Xiac2fbc782016-08-22 14:24:39 -070034 ID_RE, RefSpec
Kevin Degiabaa7f32014-11-12 11:27:45 -070035from error import GitError, HookError, UploadError, DownloadError
Ningning Xiac2fbc782016-08-22 14:24:39 -070036from error import CacheApplyError
Shawn O. Pearce559b8462009-03-02 12:56:08 -080037from error import ManifestInvalidRevisionError
Conley Owens75ee0572012-11-15 17:33:11 -080038from error import NoManifestException
Renaud Paquayd5cec5e2016-11-01 11:24:03 -070039import platform_utils
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -070040from trace import IsTrace, Trace
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070041
Shawn O. Pearced237b692009-04-17 18:49:50 -070042from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070043
David Pursehouse59bbb582013-05-17 10:49:33 +090044from pyversion import is_python3
Mike Frysinger40252c22016-08-15 21:23:44 -040045if is_python3():
46 import urllib.parse
47else:
48 import imp
49 import urlparse
50 urllib = imp.new_module('urllib')
51 urllib.parse = urlparse
David Pursehouse59bbb582013-05-17 10:49:33 +090052 # pylint:disable=W0622
Chirayu Desai217ea7d2013-03-01 19:14:38 +053053 input = raw_input
David Pursehouse59bbb582013-05-17 10:49:33 +090054 # pylint:enable=W0622
Chirayu Desai217ea7d2013-03-01 19:14:38 +053055
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070056
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070057def _lwrite(path, content):
58 lock = '%s.lock' % path
59
Chirayu Desai303a82f2014-08-19 22:57:17 +053060 fd = open(lock, 'w')
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070061 try:
62 fd.write(content)
63 finally:
64 fd.close()
65
66 try:
Renaud Paquayad1abcb2016-11-01 11:34:55 -070067 platform_utils.rename(lock, path)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070068 except OSError:
Renaud Paquay010fed72016-11-11 14:25:29 -080069 platform_utils.remove(lock)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070070 raise
71
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070072
Shawn O. Pearce48244782009-04-16 08:25:57 -070073def _error(fmt, *args):
74 msg = fmt % args
Sarah Owenscecd1d82012-11-01 22:59:27 -070075 print('error: %s' % msg, file=sys.stderr)
Shawn O. Pearce48244782009-04-16 08:25:57 -070076
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070077
David Pursehousef33929d2015-08-24 14:39:14 +090078def _warn(fmt, *args):
79 msg = fmt % args
80 print('warn: %s' % msg, file=sys.stderr)
81
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070082
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070083def not_rev(r):
84 return '^' + r
85
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070086
Shawn O. Pearceb54a3922009-01-05 16:18:58 -080087def sq(r):
88 return "'" + r.replace("'", "'\''") + "'"
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -080089
Jonathan Nieder93719792015-03-17 11:29:58 -070090_project_hook_list = None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070091
92
Jonathan Nieder93719792015-03-17 11:29:58 -070093def _ProjectHooks():
94 """List the hooks present in the 'hooks' directory.
95
96 These hooks are project hooks and are copied to the '.git/hooks' directory
97 of all subprojects.
98
99 This function caches the list of hooks (based on the contents of the
100 'repo/hooks' directory) on the first call.
101
102 Returns:
103 A list of absolute paths to all of the files in the hooks directory.
104 """
105 global _project_hook_list
106 if _project_hook_list is None:
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700107 d = platform_utils.realpath(os.path.abspath(os.path.dirname(__file__)))
Jonathan Nieder93719792015-03-17 11:29:58 -0700108 d = os.path.join(d, 'hooks')
109 _project_hook_list = [os.path.join(d, x) for x in os.listdir(d)]
110 return _project_hook_list
111
112
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700113class DownloadedChange(object):
114 _commit_cache = None
115
116 def __init__(self, project, base, change_id, ps_id, commit):
117 self.project = project
118 self.base = base
119 self.change_id = change_id
120 self.ps_id = ps_id
121 self.commit = commit
122
123 @property
124 def commits(self):
125 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700126 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
127 '--abbrev-commit',
128 '--pretty=oneline',
129 '--reverse',
130 '--date-order',
131 not_rev(self.base),
132 self.commit,
133 '--')
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700134 return self._commit_cache
135
136
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700137class ReviewableBranch(object):
138 _commit_cache = None
139
140 def __init__(self, project, branch, base):
141 self.project = project
142 self.branch = branch
143 self.base = base
144
145 @property
146 def name(self):
147 return self.branch.name
148
149 @property
150 def commits(self):
151 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700152 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
153 '--abbrev-commit',
154 '--pretty=oneline',
155 '--reverse',
156 '--date-order',
157 not_rev(self.base),
158 R_HEADS + self.name,
159 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700160 return self._commit_cache
161
162 @property
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800163 def unabbrev_commits(self):
164 r = dict()
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700165 for commit in self.project.bare_git.rev_list(not_rev(self.base),
166 R_HEADS + self.name,
167 '--'):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800168 r[commit[0:8]] = commit
169 return r
170
171 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700172 def date(self):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700173 return self.project.bare_git.log('--pretty=format:%cd',
174 '-n', '1',
175 R_HEADS + self.name,
176 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700177
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700178 def UploadForReview(self, people,
179 auto_topic=False,
180 draft=False,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200181 private=False,
Vadim Bendebury75bcd242018-10-31 13:48:01 -0700182 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200183 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200184 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800185 validate_certs=True,
186 push_options=None):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800187 self.project.UploadForReview(self.name,
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700188 people,
Brian Harring435370c2012-07-28 15:37:04 -0700189 auto_topic=auto_topic,
Bryan Jacobsf609f912013-05-06 13:36:24 -0400190 draft=draft,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200191 private=private,
Vadim Bendebury75bcd242018-10-31 13:48:01 -0700192 notify=notify,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200193 wip=wip,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200194 dest_branch=dest_branch,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800195 validate_certs=validate_certs,
196 push_options=push_options)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700197
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700198 def GetPublishedRefs(self):
199 refs = {}
200 output = self.project.bare_git.ls_remote(
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700201 self.branch.remote.SshReviewUrl(self.project.UserEmail),
202 'refs/changes/*')
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700203 for line in output.split('\n'):
204 try:
205 (sha, ref) = line.split()
206 refs[sha] = ref
207 except ValueError:
208 pass
209
210 return refs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700211
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700212
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700213class StatusColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700214
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700215 def __init__(self, config):
216 Coloring.__init__(self, config, 'status')
Anthony King7bdac712014-07-16 12:56:40 +0100217 self.project = self.printer('header', attr='bold')
218 self.branch = self.printer('header', attr='bold')
219 self.nobranch = self.printer('nobranch', fg='red')
220 self.important = self.printer('important', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700221
Anthony King7bdac712014-07-16 12:56:40 +0100222 self.added = self.printer('added', fg='green')
223 self.changed = self.printer('changed', fg='red')
224 self.untracked = self.printer('untracked', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700225
226
227class DiffColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700228
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700229 def __init__(self, config):
230 Coloring.__init__(self, config, 'diff')
Anthony King7bdac712014-07-16 12:56:40 +0100231 self.project = self.printer('header', attr='bold')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700232
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700233
Anthony King7bdac712014-07-16 12:56:40 +0100234class _Annotation(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700235
James W. Mills24c13082012-04-12 15:04:13 -0500236 def __init__(self, name, value, keep):
237 self.name = name
238 self.value = value
239 self.keep = keep
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700240
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700241
Anthony King7bdac712014-07-16 12:56:40 +0100242class _CopyFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700243
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800244 def __init__(self, src, dest, abssrc, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700245 self.src = src
246 self.dest = dest
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800247 self.abs_src = abssrc
248 self.abs_dest = absdest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700249
250 def _Copy(self):
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800251 src = self.abs_src
252 dest = self.abs_dest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700253 # copy file if it does not exist or is out of date
254 if not os.path.exists(dest) or not filecmp.cmp(src, dest):
255 try:
256 # remove existing file first, since it might be read-only
257 if os.path.exists(dest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800258 platform_utils.remove(dest)
Matthew Buckett2daf6672009-07-11 09:43:47 -0400259 else:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200260 dest_dir = os.path.dirname(dest)
261 if not os.path.isdir(dest_dir):
262 os.makedirs(dest_dir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700263 shutil.copy(src, dest)
264 # make the file read-only
265 mode = os.stat(dest)[stat.ST_MODE]
266 mode = mode & ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
267 os.chmod(dest, mode)
268 except IOError:
Shawn O. Pearce48244782009-04-16 08:25:57 -0700269 _error('Cannot copy file %s to %s', src, dest)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700270
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700271
Anthony King7bdac712014-07-16 12:56:40 +0100272class _LinkFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700273
Wink Saville4c426ef2015-06-03 08:05:17 -0700274 def __init__(self, git_worktree, src, dest, relsrc, absdest):
275 self.git_worktree = git_worktree
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500276 self.src = src
277 self.dest = dest
Colin Cross0184dcc2015-05-05 00:24:54 -0700278 self.src_rel_to_dest = relsrc
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500279 self.abs_dest = absdest
280
Wink Saville4c426ef2015-06-03 08:05:17 -0700281 def __linkIt(self, relSrc, absDest):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500282 # link file if it does not exist or is out of date
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700283 if not platform_utils.islink(absDest) or (platform_utils.readlink(absDest) != relSrc):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500284 try:
285 # remove existing file first, since it might be read-only
Dan Willemsene1e0bd12015-11-18 16:49:38 -0800286 if os.path.lexists(absDest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800287 platform_utils.remove(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500288 else:
Wink Saville4c426ef2015-06-03 08:05:17 -0700289 dest_dir = os.path.dirname(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500290 if not os.path.isdir(dest_dir):
291 os.makedirs(dest_dir)
Renaud Paquayd5cec5e2016-11-01 11:24:03 -0700292 platform_utils.symlink(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500293 except IOError:
Wink Saville4c426ef2015-06-03 08:05:17 -0700294 _error('Cannot link file %s to %s', relSrc, absDest)
295
296 def _Link(self):
297 """Link the self.rel_src_to_dest and self.abs_dest. Handles wild cards
298 on the src linking all of the files in the source in to the destination
299 directory.
300 """
301 # We use the absSrc to handle the situation where the current directory
302 # is not the root of the repo
303 absSrc = os.path.join(self.git_worktree, self.src)
304 if os.path.exists(absSrc):
305 # Entity exists so just a simple one to one link operation
306 self.__linkIt(self.src_rel_to_dest, self.abs_dest)
307 else:
308 # Entity doesn't exist assume there is a wild card
309 absDestDir = self.abs_dest
310 if os.path.exists(absDestDir) and not os.path.isdir(absDestDir):
311 _error('Link error: src with wildcard, %s must be a directory',
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700312 absDestDir)
Wink Saville4c426ef2015-06-03 08:05:17 -0700313 else:
314 absSrcFiles = glob.glob(absSrc)
315 for absSrcFile in absSrcFiles:
316 # Create a releative path from source dir to destination dir
317 absSrcDir = os.path.dirname(absSrcFile)
318 relSrcDir = os.path.relpath(absSrcDir, absDestDir)
319
320 # Get the source file name
321 srcFile = os.path.basename(absSrcFile)
322
323 # Now form the final full paths to srcFile. They will be
324 # absolute for the desintaiton and relative for the srouce.
325 absDest = os.path.join(absDestDir, srcFile)
326 relSrc = os.path.join(relSrcDir, srcFile)
327 self.__linkIt(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500328
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700329
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700330class RemoteSpec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700331
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700332 def __init__(self,
333 name,
Anthony King7bdac712014-07-16 12:56:40 +0100334 url=None,
Steve Raed6480452016-08-10 15:00:00 -0700335 pushUrl=None,
Anthony King7bdac712014-07-16 12:56:40 +0100336 review=None,
Dan Willemsen96c2d652016-04-06 16:03:54 -0700337 revision=None,
David Rileye0684ad2017-04-05 00:02:59 -0700338 orig_name=None,
339 fetchUrl=None):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700340 self.name = name
341 self.url = url
Steve Raed6480452016-08-10 15:00:00 -0700342 self.pushUrl = pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700343 self.review = review
Anthony King36ea2fb2014-05-06 11:54:01 +0100344 self.revision = revision
Dan Willemsen96c2d652016-04-06 16:03:54 -0700345 self.orig_name = orig_name
David Rileye0684ad2017-04-05 00:02:59 -0700346 self.fetchUrl = fetchUrl
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700347
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700348
Doug Anderson37282b42011-03-04 11:54:18 -0800349class RepoHook(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700350
Doug Anderson37282b42011-03-04 11:54:18 -0800351 """A RepoHook contains information about a script to run as a hook.
352
353 Hooks are used to run a python script before running an upload (for instance,
354 to run presubmit checks). Eventually, we may have hooks for other actions.
355
356 This shouldn't be confused with files in the 'repo/hooks' directory. Those
357 files are copied into each '.git/hooks' folder for each project. Repo-level
358 hooks are associated instead with repo actions.
359
360 Hooks are always python. When a hook is run, we will load the hook into the
361 interpreter and execute its main() function.
362 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700363
Doug Anderson37282b42011-03-04 11:54:18 -0800364 def __init__(self,
365 hook_type,
366 hooks_project,
367 topdir,
Mike Frysinger40252c22016-08-15 21:23:44 -0400368 manifest_url,
Doug Anderson37282b42011-03-04 11:54:18 -0800369 abort_if_user_denies=False):
370 """RepoHook constructor.
371
372 Params:
373 hook_type: A string representing the type of hook. This is also used
374 to figure out the name of the file containing the hook. For
375 example: 'pre-upload'.
376 hooks_project: The project containing the repo hooks. If you have a
377 manifest, this is manifest.repo_hooks_project. OK if this is None,
378 which will make the hook a no-op.
379 topdir: Repo's top directory (the one containing the .repo directory).
380 Scripts will run with CWD as this directory. If you have a manifest,
381 this is manifest.topdir
Mike Frysinger40252c22016-08-15 21:23:44 -0400382 manifest_url: The URL to the manifest git repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800383 abort_if_user_denies: If True, we'll throw a HookError() if the user
384 doesn't allow us to run the hook.
385 """
386 self._hook_type = hook_type
387 self._hooks_project = hooks_project
Mike Frysinger40252c22016-08-15 21:23:44 -0400388 self._manifest_url = manifest_url
Doug Anderson37282b42011-03-04 11:54:18 -0800389 self._topdir = topdir
390 self._abort_if_user_denies = abort_if_user_denies
391
392 # Store the full path to the script for convenience.
393 if self._hooks_project:
394 self._script_fullpath = os.path.join(self._hooks_project.worktree,
395 self._hook_type + '.py')
396 else:
397 self._script_fullpath = None
398
399 def _GetHash(self):
400 """Return a hash of the contents of the hooks directory.
401
402 We'll just use git to do this. This hash has the property that if anything
403 changes in the directory we will return a different has.
404
405 SECURITY CONSIDERATION:
406 This hash only represents the contents of files in the hook directory, not
407 any other files imported or called by hooks. Changes to imported files
408 can change the script behavior without affecting the hash.
409
410 Returns:
411 A string representing the hash. This will always be ASCII so that it can
412 be printed to the user easily.
413 """
414 assert self._hooks_project, "Must have hooks to calculate their hash."
415
416 # We will use the work_git object rather than just calling GetRevisionId().
417 # That gives us a hash of the latest checked in version of the files that
418 # the user will actually be executing. Specifically, GetRevisionId()
419 # doesn't appear to change even if a user checks out a different version
420 # of the hooks repo (via git checkout) nor if a user commits their own revs.
421 #
422 # NOTE: Local (non-committed) changes will not be factored into this hash.
423 # I think this is OK, since we're really only worried about warning the user
424 # about upstream changes.
425 return self._hooks_project.work_git.rev_parse('HEAD')
426
427 def _GetMustVerb(self):
428 """Return 'must' if the hook is required; 'should' if not."""
429 if self._abort_if_user_denies:
430 return 'must'
431 else:
432 return 'should'
433
434 def _CheckForHookApproval(self):
435 """Check to see whether this hook has been approved.
436
Mike Frysinger40252c22016-08-15 21:23:44 -0400437 We'll accept approval of manifest URLs if they're using secure transports.
438 This way the user can say they trust the manifest hoster. For insecure
439 hosts, we fall back to checking the hash of the hooks repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800440
441 Note that we ask permission for each individual hook even though we use
442 the hash of all hooks when detecting changes. We'd like the user to be
443 able to approve / deny each hook individually. We only use the hash of all
444 hooks because there is no other easy way to detect changes to local imports.
445
446 Returns:
447 True if this hook is approved to run; False otherwise.
448
449 Raises:
450 HookError: Raised if the user doesn't approve and abort_if_user_denies
451 was passed to the consturctor.
452 """
Mike Frysinger40252c22016-08-15 21:23:44 -0400453 if self._ManifestUrlHasSecureScheme():
454 return self._CheckForHookApprovalManifest()
455 else:
456 return self._CheckForHookApprovalHash()
457
458 def _CheckForHookApprovalHelper(self, subkey, new_val, main_prompt,
459 changed_prompt):
460 """Check for approval for a particular attribute and hook.
461
462 Args:
463 subkey: The git config key under [repo.hooks.<hook_type>] to store the
464 last approved string.
465 new_val: The new value to compare against the last approved one.
466 main_prompt: Message to display to the user to ask for approval.
467 changed_prompt: Message explaining why we're re-asking for approval.
468
469 Returns:
470 True if this hook is approved to run; False otherwise.
471
472 Raises:
473 HookError: Raised if the user doesn't approve and abort_if_user_denies
474 was passed to the consturctor.
475 """
Doug Anderson37282b42011-03-04 11:54:18 -0800476 hooks_config = self._hooks_project.config
Mike Frysinger40252c22016-08-15 21:23:44 -0400477 git_approval_key = 'repo.hooks.%s.%s' % (self._hook_type, subkey)
Doug Anderson37282b42011-03-04 11:54:18 -0800478
Mike Frysinger40252c22016-08-15 21:23:44 -0400479 # Get the last value that the user approved for this hook; may be None.
480 old_val = hooks_config.GetString(git_approval_key)
Doug Anderson37282b42011-03-04 11:54:18 -0800481
Mike Frysinger40252c22016-08-15 21:23:44 -0400482 if old_val is not None:
Doug Anderson37282b42011-03-04 11:54:18 -0800483 # User previously approved hook and asked not to be prompted again.
Mike Frysinger40252c22016-08-15 21:23:44 -0400484 if new_val == old_val:
Doug Anderson37282b42011-03-04 11:54:18 -0800485 # Approval matched. We're done.
486 return True
487 else:
488 # Give the user a reason why we're prompting, since they last told
489 # us to "never ask again".
Mike Frysinger40252c22016-08-15 21:23:44 -0400490 prompt = 'WARNING: %s\n\n' % (changed_prompt,)
Doug Anderson37282b42011-03-04 11:54:18 -0800491 else:
492 prompt = ''
493
494 # Prompt the user if we're not on a tty; on a tty we'll assume "no".
495 if sys.stdout.isatty():
Mike Frysinger40252c22016-08-15 21:23:44 -0400496 prompt += main_prompt + ' (yes/always/NO)? '
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530497 response = input(prompt).lower()
David Pursehouse98ffba12012-11-14 11:18:00 +0900498 print()
Doug Anderson37282b42011-03-04 11:54:18 -0800499
500 # User is doing a one-time approval.
501 if response in ('y', 'yes'):
502 return True
Mike Frysinger40252c22016-08-15 21:23:44 -0400503 elif response == 'always':
504 hooks_config.SetString(git_approval_key, new_val)
Doug Anderson37282b42011-03-04 11:54:18 -0800505 return True
506
507 # For anything else, we'll assume no approval.
508 if self._abort_if_user_denies:
509 raise HookError('You must allow the %s hook or use --no-verify.' %
510 self._hook_type)
511
512 return False
513
Mike Frysinger40252c22016-08-15 21:23:44 -0400514 def _ManifestUrlHasSecureScheme(self):
515 """Check if the URI for the manifest is a secure transport."""
516 secure_schemes = ('file', 'https', 'ssh', 'persistent-https', 'sso', 'rpc')
517 parse_results = urllib.parse.urlparse(self._manifest_url)
518 return parse_results.scheme in secure_schemes
519
520 def _CheckForHookApprovalManifest(self):
521 """Check whether the user has approved this manifest host.
522
523 Returns:
524 True if this hook is approved to run; False otherwise.
525 """
526 return self._CheckForHookApprovalHelper(
527 'approvedmanifest',
528 self._manifest_url,
529 'Run hook scripts from %s' % (self._manifest_url,),
530 'Manifest URL has changed since %s was allowed.' % (self._hook_type,))
531
532 def _CheckForHookApprovalHash(self):
533 """Check whether the user has approved the hooks repo.
534
535 Returns:
536 True if this hook is approved to run; False otherwise.
537 """
538 prompt = ('Repo %s run the script:\n'
539 ' %s\n'
540 '\n'
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700541 'Do you want to allow this script to run')
Mike Frysinger40252c22016-08-15 21:23:44 -0400542 return self._CheckForHookApprovalHelper(
543 'approvedhash',
544 self._GetHash(),
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700545 prompt % (self._GetMustVerb(), self._script_fullpath),
Mike Frysinger40252c22016-08-15 21:23:44 -0400546 'Scripts have changed since %s was allowed.' % (self._hook_type,))
547
Doug Anderson37282b42011-03-04 11:54:18 -0800548 def _ExecuteHook(self, **kwargs):
549 """Actually execute the given hook.
550
551 This will run the hook's 'main' function in our python interpreter.
552
553 Args:
554 kwargs: Keyword arguments to pass to the hook. These are often specific
555 to the hook type. For instance, pre-upload hooks will contain
556 a project_list.
557 """
558 # Keep sys.path and CWD stashed away so that we can always restore them
559 # upon function exit.
560 orig_path = os.getcwd()
561 orig_syspath = sys.path
562
563 try:
564 # Always run hooks with CWD as topdir.
565 os.chdir(self._topdir)
566
567 # Put the hook dir as the first item of sys.path so hooks can do
568 # relative imports. We want to replace the repo dir as [0] so
569 # hooks can't import repo files.
570 sys.path = [os.path.dirname(self._script_fullpath)] + sys.path[1:]
571
572 # Exec, storing global context in the context dict. We catch exceptions
573 # and convert to a HookError w/ just the failing traceback.
Mike Frysinger4aa4b212016-03-04 15:03:00 -0500574 context = {'__file__': self._script_fullpath}
Doug Anderson37282b42011-03-04 11:54:18 -0800575 try:
Anthony King70f68902014-05-05 21:15:34 +0100576 exec(compile(open(self._script_fullpath).read(),
577 self._script_fullpath, 'exec'), context)
Doug Anderson37282b42011-03-04 11:54:18 -0800578 except Exception:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700579 raise HookError('%s\nFailed to import %s hook; see traceback above.' %
580 (traceback.format_exc(), self._hook_type))
Doug Anderson37282b42011-03-04 11:54:18 -0800581
582 # Running the script should have defined a main() function.
583 if 'main' not in context:
584 raise HookError('Missing main() in: "%s"' % self._script_fullpath)
585
Doug Anderson37282b42011-03-04 11:54:18 -0800586 # Add 'hook_should_take_kwargs' to the arguments to be passed to main.
587 # We don't actually want hooks to define their main with this argument--
588 # it's there to remind them that their hook should always take **kwargs.
589 # For instance, a pre-upload hook should be defined like:
590 # def main(project_list, **kwargs):
591 #
592 # This allows us to later expand the API without breaking old hooks.
593 kwargs = kwargs.copy()
594 kwargs['hook_should_take_kwargs'] = True
595
596 # Call the main function in the hook. If the hook should cause the
597 # build to fail, it will raise an Exception. We'll catch that convert
598 # to a HookError w/ just the failing traceback.
599 try:
600 context['main'](**kwargs)
601 except Exception:
602 raise HookError('%s\nFailed to run main() for %s hook; see traceback '
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700603 'above.' % (traceback.format_exc(),
604 self._hook_type))
Doug Anderson37282b42011-03-04 11:54:18 -0800605 finally:
606 # Restore sys.path and CWD.
607 sys.path = orig_syspath
608 os.chdir(orig_path)
609
610 def Run(self, user_allows_all_hooks, **kwargs):
611 """Run the hook.
612
613 If the hook doesn't exist (because there is no hooks project or because
614 this particular hook is not enabled), this is a no-op.
615
616 Args:
617 user_allows_all_hooks: If True, we will never prompt about running the
618 hook--we'll just assume it's OK to run it.
619 kwargs: Keyword arguments to pass to the hook. These are often specific
620 to the hook type. For instance, pre-upload hooks will contain
621 a project_list.
622
623 Raises:
624 HookError: If there was a problem finding the hook or the user declined
625 to run a required hook (from _CheckForHookApproval).
626 """
627 # No-op if there is no hooks project or if hook is disabled.
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700628 if ((not self._hooks_project) or (self._hook_type not in
629 self._hooks_project.enabled_repo_hooks)):
Doug Anderson37282b42011-03-04 11:54:18 -0800630 return
631
632 # Bail with a nice error if we can't find the hook.
633 if not os.path.isfile(self._script_fullpath):
634 raise HookError('Couldn\'t find repo hook: "%s"' % self._script_fullpath)
635
636 # Make sure the user is OK with running the hook.
637 if (not user_allows_all_hooks) and (not self._CheckForHookApproval()):
638 return
639
640 # Run the hook with the same version of python we're using.
641 self._ExecuteHook(**kwargs)
642
643
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700644class Project(object):
Kevin Degi384b3c52014-10-16 16:02:58 -0600645 # These objects can be shared between several working trees.
646 shareable_files = ['description', 'info']
647 shareable_dirs = ['hooks', 'objects', 'rr-cache', 'svn']
648 # These objects can only be used by a single working tree.
649 working_tree_files = ['config', 'packed-refs', 'shallow']
650 working_tree_dirs = ['logs', 'refs']
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700651
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700652 def __init__(self,
653 manifest,
654 name,
655 remote,
656 gitdir,
David James8d201162013-10-11 17:03:19 -0700657 objdir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700658 worktree,
659 relpath,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700660 revisionExpr,
Mike Pontillod3153822012-02-28 11:53:24 -0800661 revisionId,
Anthony King7bdac712014-07-16 12:56:40 +0100662 rebase=True,
663 groups=None,
664 sync_c=False,
665 sync_s=False,
666 clone_depth=None,
667 upstream=None,
668 parent=None,
669 is_derived=False,
David Pursehouseb1553542014-09-04 21:28:09 +0900670 dest_branch=None,
Simran Basib9a1b732015-08-20 12:19:28 -0700671 optimized_fetch=False,
672 old_revision=None):
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800673 """Init a Project object.
674
675 Args:
676 manifest: The XmlManifest object.
677 name: The `name` attribute of manifest.xml's project element.
678 remote: RemoteSpec object specifying its remote's properties.
679 gitdir: Absolute path of git directory.
David James8d201162013-10-11 17:03:19 -0700680 objdir: Absolute path of directory to store git objects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800681 worktree: Absolute path of git working tree.
682 relpath: Relative path of git working tree to repo's top directory.
683 revisionExpr: The `revision` attribute of manifest.xml's project element.
684 revisionId: git commit id for checking out.
685 rebase: The `rebase` attribute of manifest.xml's project element.
686 groups: The `groups` attribute of manifest.xml's project element.
687 sync_c: The `sync-c` attribute of manifest.xml's project element.
688 sync_s: The `sync-s` attribute of manifest.xml's project element.
689 upstream: The `upstream` attribute of manifest.xml's project element.
690 parent: The parent Project object.
691 is_derived: False if the project was explicitly defined in the manifest;
692 True if the project is a discovered submodule.
Bryan Jacobsf609f912013-05-06 13:36:24 -0400693 dest_branch: The branch to which to push changes for review by default.
David Pursehouseb1553542014-09-04 21:28:09 +0900694 optimized_fetch: If True, when a project is set to a sha1 revision, only
695 fetch from the remote if the sha1 is not present locally.
Simran Basib9a1b732015-08-20 12:19:28 -0700696 old_revision: saved git commit id for open GITC projects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800697 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700698 self.manifest = manifest
699 self.name = name
700 self.remote = remote
Anthony Newnamdf14a702011-01-09 17:31:57 -0800701 self.gitdir = gitdir.replace('\\', '/')
David James8d201162013-10-11 17:03:19 -0700702 self.objdir = objdir.replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800703 if worktree:
Renaud Paquayfef9f212016-11-01 18:28:01 -0700704 self.worktree = os.path.normpath(worktree).replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800705 else:
706 self.worktree = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700707 self.relpath = relpath
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700708 self.revisionExpr = revisionExpr
709
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700710 if revisionId is None \
711 and revisionExpr \
712 and IsId(revisionExpr):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700713 self.revisionId = revisionExpr
714 else:
715 self.revisionId = revisionId
716
Mike Pontillod3153822012-02-28 11:53:24 -0800717 self.rebase = rebase
Colin Cross5acde752012-03-28 20:15:45 -0700718 self.groups = groups
Anatol Pomazau79770d22012-04-20 14:41:59 -0700719 self.sync_c = sync_c
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800720 self.sync_s = sync_s
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,
Bryan Jacobsf609f912013-05-06 13:36:24 -04001121 draft=False,
Changcheng Xiao87984c62017-08-02 16:55:03 +02001122 private=False,
Vadim Bendebury75bcd242018-10-31 13:48:01 -07001123 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +02001124 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +02001125 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001126 validate_certs=True,
1127 push_options=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001128 """Uploads the named branch for code review.
1129 """
1130 if branch is None:
1131 branch = self.CurrentBranch
1132 if branch is None:
1133 raise GitError('not currently on a branch')
1134
1135 branch = self.GetBranch(branch)
1136 if not branch.LocalMerge:
1137 raise GitError('branch %s does not track a remote' % branch.name)
1138 if not branch.remote.review:
1139 raise GitError('remote %s has no review url' % branch.remote.name)
1140
Bryan Jacobsf609f912013-05-06 13:36:24 -04001141 if dest_branch is None:
1142 dest_branch = self.dest_branch
1143 if dest_branch is None:
1144 dest_branch = branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001145 if not dest_branch.startswith(R_HEADS):
1146 dest_branch = R_HEADS + dest_branch
1147
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -08001148 if not branch.remote.projectname:
1149 branch.remote.projectname = self.name
1150 branch.remote.Save()
1151
Łukasz Gardońbed59ce2017-08-08 10:18:11 +02001152 url = branch.remote.ReviewUrl(self.UserEmail, validate_certs)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001153 if url is None:
1154 raise UploadError('review not configured')
1155 cmd = ['push']
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001156
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001157 if url.startswith('ssh://'):
Jonathan Nieder81df7e12018-11-05 13:21:52 -08001158 cmd.append('--receive-pack=gerrit receive-pack')
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001159
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001160 for push_option in (push_options or []):
1161 cmd.append('-o')
1162 cmd.append(push_option)
1163
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001164 cmd.append(url)
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001165
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001166 if dest_branch.startswith(R_HEADS):
1167 dest_branch = dest_branch[len(R_HEADS):]
Brian Harring435370c2012-07-28 15:37:04 -07001168
1169 upload_type = 'for'
1170 if draft:
1171 upload_type = 'drafts'
1172
1173 ref_spec = '%s:refs/%s/%s' % (R_HEADS + branch.name, upload_type,
1174 dest_branch)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001175 if auto_topic:
1176 ref_spec = ref_spec + '/' + branch.name
Changcheng Xiao87984c62017-08-02 16:55:03 +02001177
Jonathan Nieder81df7e12018-11-05 13:21:52 -08001178 opts = ['r=%s' % p for p in people[0]]
1179 opts += ['cc=%s' % p for p in people[1]]
Vadim Bendebury75bcd242018-10-31 13:48:01 -07001180 if notify:
1181 opts += ['notify=' + notify]
Jonathan Nieder81df7e12018-11-05 13:21:52 -08001182 if private:
1183 opts += ['private']
1184 if wip:
1185 opts += ['wip']
1186 if opts:
1187 ref_spec = ref_spec + '%' + ','.join(opts)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001188 cmd.append(ref_spec)
1189
Anthony King7bdac712014-07-16 12:56:40 +01001190 if GitCommand(self, cmd, bare=True).Wait() != 0:
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001191 raise UploadError('Upload failed')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001192
1193 msg = "posted to %s for %s" % (branch.remote.review, dest_branch)
1194 self.bare_git.UpdateRef(R_PUB + branch.name,
1195 R_HEADS + branch.name,
Anthony King7bdac712014-07-16 12:56:40 +01001196 message=msg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001197
1198
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001199# Sync ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001200
Julien Campergue335f5ef2013-10-16 11:02:35 +02001201 def _ExtractArchive(self, tarpath, path=None):
1202 """Extract the given tar on its current location
1203
1204 Args:
1205 - tarpath: The path to the actual tar file
1206
1207 """
1208 try:
1209 with tarfile.open(tarpath, 'r') as tar:
1210 tar.extractall(path=path)
1211 return True
1212 except (IOError, tarfile.TarError) as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001213 _error("Cannot extract archive %s: %s", tarpath, str(e))
Julien Campergue335f5ef2013-10-16 11:02:35 +02001214 return False
1215
Ningning Xiac2fbc782016-08-22 14:24:39 -07001216 def CachePopulate(self, cache_dir, url):
1217 """Populate cache in the cache_dir.
1218
1219 Args:
1220 cache_dir: Directory to cache git files from Google Storage.
1221 url: Git url of current repository.
1222
1223 Raises:
1224 CacheApplyError if it fails to populate the git cache.
1225 """
1226 cmd = ['cache', 'populate', '--ignore_locks', '-v',
1227 '--cache-dir', cache_dir, url]
1228
1229 if GitCommand(self, cmd, cwd=cache_dir).Wait() != 0:
1230 raise CacheApplyError('Failed to populate cache. cache_dir: %s '
1231 'url: %s' % (cache_dir, url))
1232
1233 def CacheExists(self, cache_dir, url):
1234 """Check the existence of the cache files.
1235
1236 Args:
1237 cache_dir: Directory to cache git files.
1238 url: Git url of current repository.
1239
1240 Raises:
1241 CacheApplyError if the cache files do not exist.
1242 """
1243 cmd = ['cache', 'exists', '--quiet', '--cache-dir', cache_dir, url]
1244
1245 exist = GitCommand(self, cmd, cwd=self.gitdir, capture_stdout=True)
1246 if exist.Wait() != 0:
1247 raise CacheApplyError('Failed to execute git cache exists cmd. '
1248 'cache_dir: %s url: %s' % (cache_dir, url))
1249
1250 if not exist.stdout or not exist.stdout.strip():
1251 raise CacheApplyError('Failed to find cache. cache_dir: %s '
1252 'url: %s' % (cache_dir, url))
1253 return exist.stdout.strip()
1254
1255 def CacheApply(self, cache_dir):
1256 """Apply git cache files populated from Google Storage buckets.
1257
1258 Args:
1259 cache_dir: Directory to cache git files.
1260
1261 Raises:
1262 CacheApplyError if it fails to apply git caches.
1263 """
1264 remote = self.GetRemote(self.remote.name)
1265
1266 self.CachePopulate(cache_dir, remote.url)
1267
1268 mirror_dir = self.CacheExists(cache_dir, remote.url)
1269
1270 refspec = RefSpec(True, 'refs/heads/*',
1271 'refs/remotes/%s/*' % remote.name)
1272
1273 fetch_cache_cmd = ['fetch', mirror_dir, str(refspec)]
1274 if GitCommand(self, fetch_cache_cmd, self.gitdir).Wait() != 0:
1275 raise CacheApplyError('Failed to fetch refs %s from %s' %
1276 (mirror_dir, str(refspec)))
1277
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001278 def Sync_NetworkHalf(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001279 quiet=False,
1280 is_new=None,
1281 current_branch_only=False,
1282 force_sync=False,
1283 clone_bundle=True,
1284 no_tags=False,
1285 archive=False,
1286 optimized_fetch=False,
Ningning Xiac2fbc782016-08-22 14:24:39 -07001287 prune=False,
Mike Frysingerde72f6a2018-12-13 03:20:31 -05001288 submodules=False,
Ningning Xiac2fbc782016-08-22 14:24:39 -07001289 cache_dir=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001290 """Perform only the network IO portion of the sync process.
1291 Local working directory/branch state is not affected.
1292 """
Julien Campergue335f5ef2013-10-16 11:02:35 +02001293 if archive and not isinstance(self, MetaProject):
1294 if self.remote.url.startswith(('http://', 'https://')):
David Pursehousef33929d2015-08-24 14:39:14 +09001295 _error("%s: Cannot fetch archives from http/https remotes.", self.name)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001296 return False
1297
1298 name = self.relpath.replace('\\', '/')
1299 name = name.replace('/', '_')
1300 tarpath = '%s.tar' % name
1301 topdir = self.manifest.topdir
1302
1303 try:
1304 self._FetchArchive(tarpath, cwd=topdir)
1305 except GitError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001306 _error('%s', e)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001307 return False
1308
1309 # From now on, we only need absolute tarpath
1310 tarpath = os.path.join(topdir, tarpath)
1311
1312 if not self._ExtractArchive(tarpath, path=topdir):
1313 return False
1314 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001315 platform_utils.remove(tarpath)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001316 except OSError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001317 _warn("Cannot remove archive %s: %s", tarpath, str(e))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001318 self._CopyAndLinkFiles()
Julien Campergue335f5ef2013-10-16 11:02:35 +02001319 return True
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001320 if is_new is None:
1321 is_new = not self.Exists
Shawn O. Pearce88443382010-10-08 10:02:09 +02001322 if is_new:
Kevin Degiabaa7f32014-11-12 11:27:45 -07001323 self._InitGitDir(force_sync=force_sync)
Jimmie Westera0444582012-10-24 13:44:42 +02001324 else:
1325 self._UpdateHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001326 self._InitRemote()
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001327
1328 if is_new:
1329 alt = os.path.join(self.gitdir, 'objects/info/alternates')
1330 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07001331 fd = open(alt)
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001332 try:
1333 alt_dir = fd.readline().rstrip()
1334 finally:
1335 fd.close()
1336 except IOError:
1337 alt_dir = None
1338 else:
1339 alt_dir = None
1340
Ningning Xiac2fbc782016-08-22 14:24:39 -07001341 applied_cache = False
1342 # If cache_dir is provided, and it's a new repository without
1343 # alternative_dir, bootstrap this project repo with the git
1344 # cache files.
1345 if cache_dir is not None and is_new and alt_dir is None:
1346 try:
1347 self.CacheApply(cache_dir)
1348 applied_cache = True
1349 is_new = False
1350 except CacheApplyError as e:
1351 _error('Could not apply git cache: %s', e)
1352 _error('Please check if you have the right GS credentials.')
1353 _error('Please check if the cache files exist in GS.')
1354
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001355 if clone_bundle \
Ningning Xiac2fbc782016-08-22 14:24:39 -07001356 and not applied_cache \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001357 and alt_dir is None \
1358 and self._ApplyCloneBundle(initial=is_new, quiet=quiet):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001359 is_new = False
1360
Shawn O. Pearce6ba6ba02012-05-24 09:46:50 -07001361 if not current_branch_only:
1362 if self.sync_c:
1363 current_branch_only = True
1364 elif not self.manifest._loaded:
1365 # Manifest cannot check defaults until it syncs.
1366 current_branch_only = False
1367 elif self.manifest.default.sync_c:
1368 current_branch_only = True
1369
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001370 if self.clone_depth:
1371 depth = self.clone_depth
1372 else:
1373 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1374
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001375 need_to_fetch = not (optimized_fetch and
1376 (ID_RE.match(self.revisionExpr) and
Zac Livingstone4332262017-06-16 08:56:09 -06001377 self._CheckForImmutableRevision()))
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001378 if (need_to_fetch and
1379 not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir,
1380 current_branch_only=current_branch_only,
Martin Kellye4e94d22017-03-21 16:05:12 -07001381 no_tags=no_tags, prune=prune, depth=depth,
1382 submodules=submodules)):
Anthony King7bdac712014-07-16 12:56:40 +01001383 return False
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001384
1385 if self.worktree:
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001386 self._InitMRef()
1387 else:
1388 self._InitMirrorHead()
1389 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001390 platform_utils.remove(os.path.join(self.gitdir, 'FETCH_HEAD'))
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001391 except OSError:
1392 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001393 return True
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001394
1395 def PostRepoUpgrade(self):
1396 self._InitHooks()
1397
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001398 def _CopyAndLinkFiles(self):
Simran Basib9a1b732015-08-20 12:19:28 -07001399 if self.manifest.isGitcClient:
1400 return
David Pursehouse8a68ff92012-09-24 12:15:13 +09001401 for copyfile in self.copyfiles:
1402 copyfile._Copy()
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001403 for linkfile in self.linkfiles:
1404 linkfile._Link()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001405
Julien Camperguedd654222014-01-09 16:21:37 +01001406 def GetCommitRevisionId(self):
1407 """Get revisionId of a commit.
1408
1409 Use this method instead of GetRevisionId to get the id of the commit rather
1410 than the id of the current git object (for example, a tag)
1411
1412 """
1413 if not self.revisionExpr.startswith(R_TAGS):
1414 return self.GetRevisionId(self._allrefs)
1415
1416 try:
1417 return self.bare_git.rev_list(self.revisionExpr, '-1')[0]
1418 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001419 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1420 (self.revisionExpr, self.name))
Julien Camperguedd654222014-01-09 16:21:37 +01001421
David Pursehouse8a68ff92012-09-24 12:15:13 +09001422 def GetRevisionId(self, all_refs=None):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001423 if self.revisionId:
1424 return self.revisionId
1425
1426 rem = self.GetRemote(self.remote.name)
1427 rev = rem.ToLocal(self.revisionExpr)
1428
David Pursehouse8a68ff92012-09-24 12:15:13 +09001429 if all_refs is not None and rev in all_refs:
1430 return all_refs[rev]
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001431
1432 try:
1433 return self.bare_git.rev_parse('--verify', '%s^0' % rev)
1434 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001435 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1436 (self.revisionExpr, self.name))
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001437
Martin Kellye4e94d22017-03-21 16:05:12 -07001438 def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001439 """Perform only the local IO portion of the sync process.
1440 Network access is not required.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001441 """
Martin Kellye4e94d22017-03-21 16:05:12 -07001442 self._InitWorkTree(force_sync=force_sync, submodules=submodules)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001443 all_refs = self.bare_ref.all
1444 self.CleanPublishedCache(all_refs)
1445 revid = self.GetRevisionId(all_refs)
Skyler Kaufman835cd682011-03-08 12:14:41 -08001446
David Pursehouse1d947b32012-10-25 12:23:11 +09001447 def _doff():
1448 self._FastForward(revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001449 self._CopyAndLinkFiles()
David Pursehouse1d947b32012-10-25 12:23:11 +09001450
Martin Kellye4e94d22017-03-21 16:05:12 -07001451 def _dosubmodules():
1452 self._SyncSubmodules(quiet=True)
1453
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001454 head = self.work_git.GetHead()
1455 if head.startswith(R_HEADS):
1456 branch = head[len(R_HEADS):]
1457 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001458 head = all_refs[head]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001459 except KeyError:
1460 head = None
1461 else:
1462 branch = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001463
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001464 if branch is None or syncbuf.detach_head:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001465 # Currently on a detached HEAD. The user is assumed to
1466 # not have any local modifications worth worrying about.
1467 #
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001468 if self.IsRebaseInProgress():
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001469 syncbuf.fail(self, _PriorSyncFailedError())
1470 return
1471
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001472 if head == revid:
1473 # No changes; don't do anything further.
Florian Vallee7cf1b362012-06-07 17:11:42 +02001474 # Except if the head needs to be detached
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001475 #
Florian Vallee7cf1b362012-06-07 17:11:42 +02001476 if not syncbuf.detach_head:
Dan Willemsen029eaf32015-09-03 12:52:28 -07001477 # The copy/linkfile config may have changed.
1478 self._CopyAndLinkFiles()
Florian Vallee7cf1b362012-06-07 17:11:42 +02001479 return
1480 else:
1481 lost = self._revlist(not_rev(revid), HEAD)
1482 if lost:
1483 syncbuf.info(self, "discarding %d commits", len(lost))
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001484
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001485 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001486 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001487 if submodules:
1488 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001489 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001490 syncbuf.fail(self, e)
1491 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001492 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001493 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001494
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001495 if head == revid:
1496 # No changes; don't do anything further.
1497 #
Dan Willemsen029eaf32015-09-03 12:52:28 -07001498 # The copy/linkfile config may have changed.
1499 self._CopyAndLinkFiles()
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001500 return
1501
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001502 branch = self.GetBranch(branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001503
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001504 if not branch.LocalMerge:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001505 # The current branch has no tracking configuration.
Anatol Pomazau2a32f6a2011-08-30 10:52:33 -07001506 # Jump off it to a detached HEAD.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001507 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001508 syncbuf.info(self,
1509 "leaving %s; does not track upstream",
1510 branch.name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001511 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001512 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001513 if submodules:
1514 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001515 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001516 syncbuf.fail(self, e)
1517 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001518 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001519 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001520
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001521 upstream_gain = self._revlist(not_rev(HEAD), revid)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001522 pub = self.WasPublished(branch.name, all_refs)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001523 if pub:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001524 not_merged = self._revlist(not_rev(revid), pub)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001525 if not_merged:
1526 if upstream_gain:
1527 # The user has published this branch and some of those
1528 # commits are not yet merged upstream. We do not want
1529 # to rewrite the published commits so we punt.
1530 #
Daniel Sandler4c50dee2010-03-02 15:38:03 -05001531 syncbuf.fail(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001532 "branch %s is published (but not merged) and is now "
1533 "%d commits behind" % (branch.name, len(upstream_gain)))
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001534 return
Shawn O. Pearce05f66b62009-04-21 08:26:32 -07001535 elif pub == head:
1536 # All published commits are merged, and thus we are a
1537 # strict subset. We can fast-forward safely.
Shawn O. Pearcea54c5272008-10-30 11:03:00 -07001538 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001539 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001540 if submodules:
1541 syncbuf.later1(self, _dosubmodules)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001542 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001543
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001544 # Examine the local commits not in the remote. Find the
1545 # last one attributed to this user, if any.
1546 #
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001547 local_changes = self._revlist(not_rev(revid), HEAD, format='%H %ce')
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001548 last_mine = None
1549 cnt_mine = 0
1550 for commit in local_changes:
Chirayu Desai0eb35cb2013-11-19 18:46:29 +05301551 commit_id, committer_email = commit.decode('utf-8').split(' ', 1)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001552 if committer_email == self.UserEmail:
1553 last_mine = commit_id
1554 cnt_mine += 1
1555
Shawn O. Pearceda88ff42009-06-03 11:09:12 -07001556 if not upstream_gain and cnt_mine == len(local_changes):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001557 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001558
1559 if self.IsDirty(consider_untracked=False):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001560 syncbuf.fail(self, _DirtyError())
1561 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001562
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001563 # If the upstream switched on us, warn the user.
1564 #
1565 if branch.merge != self.revisionExpr:
1566 if branch.merge and self.revisionExpr:
1567 syncbuf.info(self,
1568 'manifest switched %s...%s',
1569 branch.merge,
1570 self.revisionExpr)
1571 elif branch.merge:
1572 syncbuf.info(self,
1573 'manifest no longer tracks %s',
1574 branch.merge)
1575
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001576 if cnt_mine < len(local_changes):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001577 # Upstream rebased. Not everything in HEAD
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001578 # was created by this user.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001579 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001580 syncbuf.info(self,
1581 "discarding %d commits removed from upstream",
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001582 len(local_changes) - cnt_mine)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001583
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001584 branch.remote = self.GetRemote(self.remote.name)
Anatol Pomazaucd7c5de2012-03-20 13:45:00 -07001585 if not ID_RE.match(self.revisionExpr):
1586 # in case of manifest sync the revisionExpr might be a SHA1
1587 branch.merge = self.revisionExpr
Conley Owens04f2f0e2014-10-01 17:22:46 -07001588 if not branch.merge.startswith('refs/'):
1589 branch.merge = R_HEADS + branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001590 branch.Save()
1591
Mike Pontillod3153822012-02-28 11:53:24 -08001592 if cnt_mine > 0 and self.rebase:
Martin Kellye4e94d22017-03-21 16:05:12 -07001593 def _docopyandlink():
1594 self._CopyAndLinkFiles()
1595
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001596 def _dorebase():
Anthony King7bdac712014-07-16 12:56:40 +01001597 self._Rebase(upstream='%s^1' % last_mine, onto=revid)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001598 syncbuf.later2(self, _dorebase)
Martin Kellye4e94d22017-03-21 16:05:12 -07001599 if submodules:
1600 syncbuf.later2(self, _dosubmodules)
1601 syncbuf.later2(self, _docopyandlink)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001602 elif local_changes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001603 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001604 self._ResetHard(revid)
Martin Kellye4e94d22017-03-21 16:05:12 -07001605 if submodules:
1606 self._SyncSubmodules(quiet=True)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001607 self._CopyAndLinkFiles()
Sarah Owensa5be53f2012-09-09 15:37:57 -07001608 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001609 syncbuf.fail(self, e)
1610 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001611 else:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001612 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001613 if submodules:
1614 syncbuf.later1(self, _dosubmodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001615
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001616 def AddCopyFile(self, src, dest, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001617 # dest should already be an absolute path, but src is project relative
1618 # make src an absolute path
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001619 abssrc = os.path.join(self.worktree, src)
1620 self.copyfiles.append(_CopyFile(src, dest, abssrc, absdest))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001621
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001622 def AddLinkFile(self, src, dest, absdest):
1623 # dest should already be an absolute path, but src is project relative
Colin Cross0184dcc2015-05-05 00:24:54 -07001624 # make src relative path to dest
1625 absdestdir = os.path.dirname(absdest)
1626 relsrc = os.path.relpath(os.path.join(self.worktree, src), absdestdir)
Wink Saville4c426ef2015-06-03 08:05:17 -07001627 self.linkfiles.append(_LinkFile(self.worktree, src, dest, relsrc, absdest))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001628
James W. Mills24c13082012-04-12 15:04:13 -05001629 def AddAnnotation(self, name, value, keep):
1630 self.annotations.append(_Annotation(name, value, keep))
1631
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001632 def DownloadPatchSet(self, change_id, patch_id):
1633 """Download a single patch set of a single change to FETCH_HEAD.
1634 """
1635 remote = self.GetRemote(self.remote.name)
1636
1637 cmd = ['fetch', remote.name]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001638 cmd.append('refs/changes/%2.2d/%d/%d'
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001639 % (change_id % 100, change_id, patch_id))
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001640 if GitCommand(self, cmd, bare=True).Wait() != 0:
1641 return None
1642 return DownloadedChange(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001643 self.GetRevisionId(),
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001644 change_id,
1645 patch_id,
1646 self.bare_git.rev_parse('FETCH_HEAD'))
1647
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001648
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001649# Branch Management ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001650
Simran Basib9a1b732015-08-20 12:19:28 -07001651 def StartBranch(self, name, branch_merge=''):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001652 """Create a new branch off the manifest's revision.
1653 """
Simran Basib9a1b732015-08-20 12:19:28 -07001654 if not branch_merge:
1655 branch_merge = self.revisionExpr
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001656 head = self.work_git.GetHead()
1657 if head == (R_HEADS + name):
1658 return True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001659
David Pursehouse8a68ff92012-09-24 12:15:13 +09001660 all_refs = self.bare_ref.all
Anthony King7bdac712014-07-16 12:56:40 +01001661 if R_HEADS + name in all_refs:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001662 return GitCommand(self,
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001663 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001664 capture_stdout=True,
1665 capture_stderr=True).Wait() == 0
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001666
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001667 branch = self.GetBranch(name)
1668 branch.remote = self.GetRemote(self.remote.name)
Simran Basib9a1b732015-08-20 12:19:28 -07001669 branch.merge = branch_merge
1670 if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge):
1671 branch.merge = R_HEADS + branch_merge
David Pursehouse8a68ff92012-09-24 12:15:13 +09001672 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001673
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001674 if head.startswith(R_HEADS):
1675 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001676 head = all_refs[head]
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001677 except KeyError:
1678 head = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001679 if revid and head and revid == head:
1680 ref = os.path.join(self.gitdir, R_HEADS + name)
1681 try:
1682 os.makedirs(os.path.dirname(ref))
1683 except OSError:
1684 pass
1685 _lwrite(ref, '%s\n' % revid)
1686 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1687 'ref: %s%s\n' % (R_HEADS, name))
1688 branch.Save()
1689 return True
1690
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001691 if GitCommand(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001692 ['checkout', '-b', branch.name, revid],
Anthony King7bdac712014-07-16 12:56:40 +01001693 capture_stdout=True,
1694 capture_stderr=True).Wait() == 0:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001695 branch.Save()
1696 return True
1697 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001698
Wink Saville02d79452009-04-10 13:01:24 -07001699 def CheckoutBranch(self, name):
1700 """Checkout a local topic branch.
Doug Anderson3ba5f952011-04-07 12:51:04 -07001701
1702 Args:
1703 name: The name of the branch to checkout.
1704
1705 Returns:
1706 True if the checkout succeeded; False if it didn't; None if the branch
1707 didn't exist.
Wink Saville02d79452009-04-10 13:01:24 -07001708 """
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001709 rev = R_HEADS + name
1710 head = self.work_git.GetHead()
1711 if head == rev:
1712 # Already on the branch
1713 #
1714 return True
Wink Saville02d79452009-04-10 13:01:24 -07001715
David Pursehouse8a68ff92012-09-24 12:15:13 +09001716 all_refs = self.bare_ref.all
Wink Saville02d79452009-04-10 13:01:24 -07001717 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001718 revid = all_refs[rev]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001719 except KeyError:
1720 # Branch does not exist in this project
1721 #
Doug Anderson3ba5f952011-04-07 12:51:04 -07001722 return None
Wink Saville02d79452009-04-10 13:01:24 -07001723
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001724 if head.startswith(R_HEADS):
1725 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001726 head = all_refs[head]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001727 except KeyError:
1728 head = None
1729
1730 if head == revid:
1731 # Same revision; just update HEAD to point to the new
1732 # target branch, but otherwise take no other action.
1733 #
1734 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1735 'ref: %s%s\n' % (R_HEADS, name))
1736 return True
1737
1738 return GitCommand(self,
1739 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001740 capture_stdout=True,
1741 capture_stderr=True).Wait() == 0
Wink Saville02d79452009-04-10 13:01:24 -07001742
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001743 def AbandonBranch(self, name):
1744 """Destroy a local topic branch.
Doug Andersondafb1d62011-04-07 11:46:59 -07001745
1746 Args:
1747 name: The name of the branch to abandon.
1748
1749 Returns:
1750 True if the abandon succeeded; False if it didn't; None if the branch
1751 didn't exist.
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001752 """
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001753 rev = R_HEADS + name
David Pursehouse8a68ff92012-09-24 12:15:13 +09001754 all_refs = self.bare_ref.all
1755 if rev not in all_refs:
Doug Andersondafb1d62011-04-07 11:46:59 -07001756 # Doesn't exist
1757 return None
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001758
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001759 head = self.work_git.GetHead()
1760 if head == rev:
1761 # We can't destroy the branch while we are sitting
1762 # on it. Switch to a detached HEAD.
1763 #
David Pursehouse8a68ff92012-09-24 12:15:13 +09001764 head = all_refs[head]
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001765
David Pursehouse8a68ff92012-09-24 12:15:13 +09001766 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001767 if head == revid:
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001768 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1769 '%s\n' % revid)
1770 else:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001771 self._Checkout(revid, quiet=True)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001772
1773 return GitCommand(self,
1774 ['branch', '-D', name],
Anthony King7bdac712014-07-16 12:56:40 +01001775 capture_stdout=True,
1776 capture_stderr=True).Wait() == 0
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001777
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001778 def PruneHeads(self):
1779 """Prune any topic branches already merged into upstream.
1780 """
1781 cb = self.CurrentBranch
1782 kill = []
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001783 left = self._allrefs
1784 for name in left.keys():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001785 if name.startswith(R_HEADS):
1786 name = name[len(R_HEADS):]
1787 if cb is None or name != cb:
1788 kill.append(name)
1789
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001790 rev = self.GetRevisionId(left)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001791 if cb is not None \
1792 and not self._revlist(HEAD + '...' + rev) \
Anthony King7bdac712014-07-16 12:56:40 +01001793 and not self.IsDirty(consider_untracked=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001794 self.work_git.DetachHead(HEAD)
1795 kill.append(cb)
1796
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001797 if kill:
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07001798 old = self.bare_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001799
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001800 try:
1801 self.bare_git.DetachHead(rev)
1802
1803 b = ['branch', '-d']
1804 b.extend(kill)
1805 b = GitCommand(self, b, bare=True,
1806 capture_stdout=True,
1807 capture_stderr=True)
1808 b.Wait()
1809 finally:
Dan Willemsen1a799d12015-12-15 13:40:05 -08001810 if ID_RE.match(old):
1811 self.bare_git.DetachHead(old)
1812 else:
1813 self.bare_git.SetHead(old)
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001814 left = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001815
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001816 for branch in kill:
1817 if (R_HEADS + branch) not in left:
1818 self.CleanPublishedCache()
1819 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001820
1821 if cb and cb not in kill:
1822 kill.append(cb)
Shawn O. Pearce7c6c64d2009-03-02 12:38:13 -08001823 kill.sort()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001824
1825 kept = []
1826 for branch in kill:
Anthony King7bdac712014-07-16 12:56:40 +01001827 if R_HEADS + branch in left:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001828 branch = self.GetBranch(branch)
1829 base = branch.LocalMerge
1830 if not base:
1831 base = rev
1832 kept.append(ReviewableBranch(self, branch, base))
1833 return kept
1834
1835
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001836# Submodule Management ##
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001837
1838 def GetRegisteredSubprojects(self):
1839 result = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001840
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001841 def rec(subprojects):
1842 if not subprojects:
1843 return
1844 result.extend(subprojects)
1845 for p in subprojects:
1846 rec(p.subprojects)
1847 rec(self.subprojects)
1848 return result
1849
1850 def _GetSubmodules(self):
1851 # Unfortunately we cannot call `git submodule status --recursive` here
1852 # because the working tree might not exist yet, and it cannot be used
1853 # without a working tree in its current implementation.
1854
1855 def get_submodules(gitdir, rev):
1856 # Parse .gitmodules for submodule sub_paths and sub_urls
1857 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1858 if not sub_paths:
1859 return []
1860 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1861 # revision of submodule repository
1862 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1863 submodules = []
1864 for sub_path, sub_url in zip(sub_paths, sub_urls):
1865 try:
1866 sub_rev = sub_revs[sub_path]
1867 except KeyError:
1868 # Ignore non-exist submodules
1869 continue
1870 submodules.append((sub_rev, sub_path, sub_url))
1871 return submodules
1872
1873 re_path = re.compile(r'^submodule\.([^.]+)\.path=(.*)$')
1874 re_url = re.compile(r'^submodule\.([^.]+)\.url=(.*)$')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001875
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001876 def parse_gitmodules(gitdir, rev):
1877 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1878 try:
Anthony King7bdac712014-07-16 12:56:40 +01001879 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1880 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001881 except GitError:
1882 return [], []
1883 if p.Wait() != 0:
1884 return [], []
1885
1886 gitmodules_lines = []
1887 fd, temp_gitmodules_path = tempfile.mkstemp()
1888 try:
1889 os.write(fd, p.stdout)
1890 os.close(fd)
1891 cmd = ['config', '--file', temp_gitmodules_path, '--list']
Anthony King7bdac712014-07-16 12:56:40 +01001892 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1893 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001894 if p.Wait() != 0:
1895 return [], []
1896 gitmodules_lines = p.stdout.split('\n')
1897 except GitError:
1898 return [], []
1899 finally:
Renaud Paquay010fed72016-11-11 14:25:29 -08001900 platform_utils.remove(temp_gitmodules_path)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001901
1902 names = set()
1903 paths = {}
1904 urls = {}
1905 for line in gitmodules_lines:
1906 if not line:
1907 continue
1908 m = re_path.match(line)
1909 if m:
1910 names.add(m.group(1))
1911 paths[m.group(1)] = m.group(2)
1912 continue
1913 m = re_url.match(line)
1914 if m:
1915 names.add(m.group(1))
1916 urls[m.group(1)] = m.group(2)
1917 continue
1918 names = sorted(names)
1919 return ([paths.get(name, '') for name in names],
1920 [urls.get(name, '') for name in names])
1921
1922 def git_ls_tree(gitdir, rev, paths):
1923 cmd = ['ls-tree', rev, '--']
1924 cmd.extend(paths)
1925 try:
Anthony King7bdac712014-07-16 12:56:40 +01001926 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1927 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001928 except GitError:
1929 return []
1930 if p.Wait() != 0:
1931 return []
1932 objects = {}
1933 for line in p.stdout.split('\n'):
1934 if not line.strip():
1935 continue
1936 object_rev, object_path = line.split()[2:4]
1937 objects[object_path] = object_rev
1938 return objects
1939
1940 try:
1941 rev = self.GetRevisionId()
1942 except GitError:
1943 return []
1944 return get_submodules(self.gitdir, rev)
1945
1946 def GetDerivedSubprojects(self):
1947 result = []
1948 if not self.Exists:
1949 # If git repo does not exist yet, querying its submodules will
1950 # mess up its states; so return here.
1951 return result
1952 for rev, path, url in self._GetSubmodules():
1953 name = self.manifest.GetSubprojectName(self, path)
David James8d201162013-10-11 17:03:19 -07001954 relpath, worktree, gitdir, objdir = \
1955 self.manifest.GetSubprojectPaths(self, name, path)
1956 project = self.manifest.paths.get(relpath)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001957 if project:
1958 result.extend(project.GetDerivedSubprojects())
1959 continue
David James8d201162013-10-11 17:03:19 -07001960
Shouheng Zhang02c0ee62017-12-08 09:54:58 +08001961 if url.startswith('..'):
1962 url = urllib.parse.urljoin("%s/" % self.remote.url, url)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001963 remote = RemoteSpec(self.remote.name,
Anthony King7bdac712014-07-16 12:56:40 +01001964 url=url,
Steve Raed6480452016-08-10 15:00:00 -07001965 pushUrl=self.remote.pushUrl,
Anthony King7bdac712014-07-16 12:56:40 +01001966 review=self.remote.review,
1967 revision=self.remote.revision)
1968 subproject = Project(manifest=self.manifest,
1969 name=name,
1970 remote=remote,
1971 gitdir=gitdir,
1972 objdir=objdir,
1973 worktree=worktree,
1974 relpath=relpath,
Aymen Bouaziz2598ed02016-06-24 14:34:08 +02001975 revisionExpr=rev,
Anthony King7bdac712014-07-16 12:56:40 +01001976 revisionId=rev,
1977 rebase=self.rebase,
1978 groups=self.groups,
1979 sync_c=self.sync_c,
1980 sync_s=self.sync_s,
1981 parent=self,
1982 is_derived=True)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001983 result.append(subproject)
1984 result.extend(subproject.GetDerivedSubprojects())
1985 return result
1986
1987
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001988# Direct Git Commands ##
Zac Livingstone4332262017-06-16 08:56:09 -06001989 def _CheckForImmutableRevision(self):
Chris AtLee2fb64662014-01-16 21:32:33 -05001990 try:
1991 # if revision (sha or tag) is not present then following function
1992 # throws an error.
1993 self.bare_git.rev_parse('--verify', '%s^0' % self.revisionExpr)
1994 return True
1995 except GitError:
1996 # There is no such persistent revision. We have to fetch it.
1997 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001998
Julien Campergue335f5ef2013-10-16 11:02:35 +02001999 def _FetchArchive(self, tarpath, cwd=None):
2000 cmd = ['archive', '-v', '-o', tarpath]
2001 cmd.append('--remote=%s' % self.remote.url)
2002 cmd.append('--prefix=%s/' % self.relpath)
2003 cmd.append(self.revisionExpr)
2004
2005 command = GitCommand(self, cmd, cwd=cwd,
2006 capture_stdout=True,
2007 capture_stderr=True)
2008
2009 if command.Wait() != 0:
2010 raise GitError('git archive %s: %s' % (self.name, command.stderr))
2011
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002012 def _RemoteFetch(self, name=None,
2013 current_branch_only=False,
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002014 initial=False,
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002015 quiet=False,
Mitchel Humpherys597868b2012-10-29 10:18:34 -07002016 alt_dir=None,
David Pursehouse74cfd272015-10-14 10:50:15 +09002017 no_tags=False,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002018 prune=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07002019 depth=None,
2020 submodules=False):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002021
2022 is_sha1 = False
2023 tag_name = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09002024 # The depth should not be used when fetching to a mirror because
2025 # it will result in a shallow repository that cannot be cloned or
2026 # fetched from.
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002027 # The repo project should also never be synced with partial depth.
2028 if self.manifest.IsMirror or self.relpath == '.repo/repo':
2029 depth = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09002030
Shawn Pearce69e04d82014-01-29 12:48:54 -08002031 if depth:
2032 current_branch_only = True
2033
Nasser Grainawi909d58b2014-09-19 12:13:04 -06002034 if ID_RE.match(self.revisionExpr) is not None:
2035 is_sha1 = True
2036
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002037 if current_branch_only:
Nasser Grainawi909d58b2014-09-19 12:13:04 -06002038 if self.revisionExpr.startswith(R_TAGS):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002039 # this is a tag and its sha1 value should never change
2040 tag_name = self.revisionExpr[len(R_TAGS):]
2041
2042 if is_sha1 or tag_name is not None:
Zac Livingstone4332262017-06-16 08:56:09 -06002043 if self._CheckForImmutableRevision():
2044 print('Skipped fetching project %s (already have persistent ref)'
2045 % self.name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002046 return True
Bertrand SIMONNET3000cda2014-11-25 16:19:29 -08002047 if is_sha1 and not depth:
2048 # When syncing a specific commit and --depth is not set:
2049 # * if upstream is explicitly specified and is not a sha1, fetch only
2050 # upstream as users expect only upstream to be fetch.
2051 # Note: The commit might not be in upstream in which case the sync
2052 # will fail.
2053 # * otherwise, fetch all branches to make sure we end up with the
2054 # specific commit.
Aymen Bouaziz037040f2016-06-28 12:27:23 +02002055 if self.upstream:
2056 current_branch_only = not ID_RE.match(self.upstream)
2057 else:
2058 current_branch_only = False
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002059
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002060 if not name:
2061 name = self.remote.name
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002062
2063 ssh_proxy = False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002064 remote = self.GetRemote(name)
2065 if remote.PreConnectFetch():
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002066 ssh_proxy = True
2067
Shawn O. Pearce88443382010-10-08 10:02:09 +02002068 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002069 if alt_dir and 'objects' == os.path.basename(alt_dir):
2070 ref_dir = os.path.dirname(alt_dir)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002071 packed_refs = os.path.join(self.gitdir, 'packed-refs')
2072 remote = self.GetRemote(name)
2073
David Pursehouse8a68ff92012-09-24 12:15:13 +09002074 all_refs = self.bare_ref.all
2075 ids = set(all_refs.values())
Shawn O. Pearce88443382010-10-08 10:02:09 +02002076 tmp = set()
2077
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302078 for r, ref_id in GitRefs(ref_dir).all.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09002079 if r not in all_refs:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002080 if r.startswith(R_TAGS) or remote.WritesTo(r):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002081 all_refs[r] = ref_id
2082 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002083 continue
2084
David Pursehouse8a68ff92012-09-24 12:15:13 +09002085 if ref_id in ids:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002086 continue
2087
David Pursehouse8a68ff92012-09-24 12:15:13 +09002088 r = 'refs/_alt/%s' % ref_id
2089 all_refs[r] = ref_id
2090 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002091 tmp.add(r)
2092
heping3d7bbc92017-04-12 19:51:47 +08002093 tmp_packed_lines = []
2094 old_packed_lines = []
Shawn O. Pearce88443382010-10-08 10:02:09 +02002095
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302096 for r in sorted(all_refs):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002097 line = '%s %s\n' % (all_refs[r], r)
heping3d7bbc92017-04-12 19:51:47 +08002098 tmp_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002099 if r not in tmp:
heping3d7bbc92017-04-12 19:51:47 +08002100 old_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002101
heping3d7bbc92017-04-12 19:51:47 +08002102 tmp_packed = ''.join(tmp_packed_lines)
2103 old_packed = ''.join(old_packed_lines)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002104 _lwrite(packed_refs, tmp_packed)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002105 else:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002106 alt_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002107
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002108 cmd = ['fetch']
Doug Anderson30d45292011-05-04 15:01:04 -07002109
Conley Owensf97e8382015-01-21 11:12:46 -08002110 if depth:
Doug Anderson30d45292011-05-04 15:01:04 -07002111 cmd.append('--depth=%s' % depth)
Dan Willemseneeab6862015-08-03 13:11:53 -07002112 else:
2113 # If this repo has shallow objects, then we don't know which refs have
2114 # shallow objects or not. Tell git to unshallow all fetched refs. Don't
2115 # do this with projects that don't have shallow objects, since it is less
2116 # efficient.
2117 if os.path.exists(os.path.join(self.gitdir, 'shallow')):
2118 cmd.append('--depth=2147483647')
Doug Anderson30d45292011-05-04 15:01:04 -07002119
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002120 if quiet:
2121 cmd.append('--quiet')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002122 if not self.worktree:
2123 cmd.append('--update-head-ok')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002124 cmd.append(name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002125
Mitchel Humpherys26c45a72014-03-10 14:21:59 -07002126 # If using depth then we should not get all the tags since they may
2127 # be outside of the depth.
2128 if no_tags or depth:
2129 cmd.append('--no-tags')
2130 else:
2131 cmd.append('--tags')
2132
David Pursehouse74cfd272015-10-14 10:50:15 +09002133 if prune:
2134 cmd.append('--prune')
2135
Martin Kellye4e94d22017-03-21 16:05:12 -07002136 if submodules:
2137 cmd.append('--recurse-submodules=on-demand')
2138
Conley Owens80b87fe2014-05-09 17:13:44 -07002139 spec = []
Brian Harring14a66742012-09-28 20:21:57 -07002140 if not current_branch_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002141 # Fetch whole repo
Conley Owens80b87fe2014-05-09 17:13:44 -07002142 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002143 elif tag_name is not None:
Conley Owens80b87fe2014-05-09 17:13:44 -07002144 spec.append('tag')
2145 spec.append(tag_name)
Nasser Grainawi04e52d62014-09-30 13:34:52 -06002146
David Pursehouse403b64e2015-04-27 10:41:33 +09002147 if not self.manifest.IsMirror:
2148 branch = self.revisionExpr
Kevin Degi679bac42015-06-22 15:31:26 -06002149 if is_sha1 and depth and git_require((1, 8, 3)):
David Pursehouse403b64e2015-04-27 10:41:33 +09002150 # Shallow checkout of a specific commit, fetch from that commit and not
2151 # the heads only as the commit might be deeper in the history.
2152 spec.append(branch)
2153 else:
2154 if is_sha1:
2155 branch = self.upstream
2156 if branch is not None and branch.strip():
2157 if not branch.startswith('refs/'):
2158 branch = R_HEADS + branch
2159 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch)))
Conley Owens80b87fe2014-05-09 17:13:44 -07002160 cmd.extend(spec)
2161
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002162 ok = False
David Pursehouse8a68ff92012-09-24 12:15:13 +09002163 for _i in range(2):
John L. Villalovos9c76f672015-03-16 20:49:10 -07002164 gitcmd = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy)
John L. Villalovos126e2982015-01-29 21:58:12 -08002165 ret = gitcmd.Wait()
Brian Harring14a66742012-09-28 20:21:57 -07002166 if ret == 0:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002167 ok = True
2168 break
John L. Villalovos126e2982015-01-29 21:58:12 -08002169 # If needed, run the 'git remote prune' the first time through the loop
2170 elif (not _i and
2171 "error:" in gitcmd.stderr and
2172 "git remote prune" in gitcmd.stderr):
2173 prunecmd = GitCommand(self, ['remote', 'prune', name], bare=True,
John L. Villalovos9c76f672015-03-16 20:49:10 -07002174 ssh_proxy=ssh_proxy)
John L. Villalovose30f46b2015-02-25 14:27:02 -08002175 ret = prunecmd.Wait()
John L. Villalovose30f46b2015-02-25 14:27:02 -08002176 if ret:
John L. Villalovos126e2982015-01-29 21:58:12 -08002177 break
2178 continue
Brian Harring14a66742012-09-28 20:21:57 -07002179 elif current_branch_only and is_sha1 and ret == 128:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002180 # Exit code 128 means "couldn't find the ref you asked for"; if we're
2181 # in sha1 mode, we just tried sync'ing from the upstream field; it
2182 # doesn't exist, thus abort the optimization attempt and do a full sync.
Brian Harring14a66742012-09-28 20:21:57 -07002183 break
Colin Crossc4b301f2015-05-13 00:10:02 -07002184 elif ret < 0:
2185 # Git died with a signal, exit immediately
2186 break
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002187 time.sleep(random.randint(30, 45))
Shawn O. Pearce88443382010-10-08 10:02:09 +02002188
2189 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002190 if alt_dir:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002191 if old_packed != '':
2192 _lwrite(packed_refs, old_packed)
2193 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002194 platform_utils.remove(packed_refs)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002195 self.bare_git.pack_refs('--all', '--prune')
Brian Harring14a66742012-09-28 20:21:57 -07002196
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002197 if is_sha1 and current_branch_only:
Brian Harring14a66742012-09-28 20:21:57 -07002198 # We just synced the upstream given branch; verify we
2199 # got what we wanted, else trigger a second run of all
2200 # refs.
Zac Livingstone4332262017-06-16 08:56:09 -06002201 if not self._CheckForImmutableRevision():
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002202 if current_branch_only and depth:
2203 # Sync the current branch only with depth set to None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002204 return self._RemoteFetch(name=name,
2205 current_branch_only=current_branch_only,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002206 initial=False, quiet=quiet, alt_dir=alt_dir,
2207 depth=None)
2208 else:
2209 # Avoid infinite recursion: sync all branches with depth set to None
2210 return self._RemoteFetch(name=name, current_branch_only=False,
2211 initial=False, quiet=quiet, alt_dir=alt_dir,
2212 depth=None)
Brian Harring14a66742012-09-28 20:21:57 -07002213
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002214 return ok
Shawn O. Pearce88443382010-10-08 10:02:09 +02002215
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002216 def _ApplyCloneBundle(self, initial=False, quiet=False):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002217 if initial and \
2218 (self.manifest.manifestProject.config.GetString('repo.depth') or
2219 self.clone_depth):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002220 return False
2221
2222 remote = self.GetRemote(self.remote.name)
2223 bundle_url = remote.url + '/clone.bundle'
2224 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002225 if GetSchemeFromUrl(bundle_url) not in ('http', 'https',
2226 'persistent-http',
2227 'persistent-https'):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002228 return False
2229
2230 bundle_dst = os.path.join(self.gitdir, 'clone.bundle')
2231 bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp')
2232
2233 exist_dst = os.path.exists(bundle_dst)
2234 exist_tmp = os.path.exists(bundle_tmp)
2235
2236 if not initial and not exist_dst and not exist_tmp:
2237 return False
2238
2239 if not exist_dst:
2240 exist_dst = self._FetchBundle(bundle_url, bundle_tmp, bundle_dst, quiet)
2241 if not exist_dst:
2242 return False
2243
2244 cmd = ['fetch']
2245 if quiet:
2246 cmd.append('--quiet')
2247 if not self.worktree:
2248 cmd.append('--update-head-ok')
2249 cmd.append(bundle_dst)
2250 for f in remote.fetch:
2251 cmd.append(str(f))
2252 cmd.append('refs/tags/*:refs/tags/*')
2253
2254 ok = GitCommand(self, cmd, bare=True).Wait() == 0
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002255 if os.path.exists(bundle_dst):
Renaud Paquay010fed72016-11-11 14:25:29 -08002256 platform_utils.remove(bundle_dst)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002257 if os.path.exists(bundle_tmp):
Renaud Paquay010fed72016-11-11 14:25:29 -08002258 platform_utils.remove(bundle_tmp)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002259 return ok
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002260
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002261 def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet):
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002262 if os.path.exists(dstPath):
Renaud Paquay010fed72016-11-11 14:25:29 -08002263 platform_utils.remove(dstPath)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002264
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002265 cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002266 if quiet:
2267 cmd += ['--silent']
2268 if os.path.exists(tmpPath):
2269 size = os.stat(tmpPath).st_size
2270 if size >= 1024:
2271 cmd += ['--continue-at', '%d' % (size,)]
2272 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002273 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002274 if 'http_proxy' in os.environ and 'darwin' == sys.platform:
2275 cmd += ['--proxy', os.environ['http_proxy']]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002276 with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, _proxy):
Dave Borowitz137d0132015-01-02 11:12:54 -08002277 if cookiefile:
Dave Borowitz4abf8e62015-01-02 11:39:04 -08002278 cmd += ['--cookie', cookiefile, '--cookie-jar', cookiefile]
Dave Borowitz137d0132015-01-02 11:12:54 -08002279 if srcUrl.startswith('persistent-'):
2280 srcUrl = srcUrl[len('persistent-'):]
2281 cmd += [srcUrl]
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002282
Dave Borowitz137d0132015-01-02 11:12:54 -08002283 if IsTrace():
2284 Trace('%s', ' '.join(cmd))
2285 try:
2286 proc = subprocess.Popen(cmd)
2287 except OSError:
2288 return False
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002289
Dave Borowitz137d0132015-01-02 11:12:54 -08002290 curlret = proc.wait()
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002291
Dave Borowitz137d0132015-01-02 11:12:54 -08002292 if curlret == 22:
2293 # From curl man page:
2294 # 22: HTTP page not retrieved. The requested url was not found or
2295 # returned another error with the HTTP error code being 400 or above.
2296 # This return code only appears if -f, --fail is used.
2297 if not quiet:
2298 print("Server does not provide clone.bundle; ignoring.",
2299 file=sys.stderr)
2300 return False
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002301
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002302 if os.path.exists(tmpPath):
Kris Giesingc8d882a2014-12-23 13:02:32 -08002303 if curlret == 0 and self._IsValidBundle(tmpPath, quiet):
Renaud Paquayad1abcb2016-11-01 11:34:55 -07002304 platform_utils.rename(tmpPath, dstPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002305 return True
2306 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002307 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002308 return False
2309 else:
2310 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002311
Kris Giesingc8d882a2014-12-23 13:02:32 -08002312 def _IsValidBundle(self, path, quiet):
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002313 try:
2314 with open(path) as f:
2315 if f.read(16) == '# v2 git bundle\n':
2316 return True
2317 else:
Kris Giesingc8d882a2014-12-23 13:02:32 -08002318 if not quiet:
2319 print("Invalid clone.bundle file; ignoring.", file=sys.stderr)
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002320 return False
2321 except OSError:
2322 return False
2323
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002324 def _Checkout(self, rev, quiet=False):
2325 cmd = ['checkout']
2326 if quiet:
2327 cmd.append('-q')
2328 cmd.append(rev)
2329 cmd.append('--')
2330 if GitCommand(self, cmd).Wait() != 0:
2331 if self._allrefs:
2332 raise GitError('%s checkout %s ' % (self.name, rev))
2333
Anthony King7bdac712014-07-16 12:56:40 +01002334 def _CherryPick(self, rev):
Pierre Tardye5a21222011-03-24 16:28:18 +01002335 cmd = ['cherry-pick']
2336 cmd.append(rev)
2337 cmd.append('--')
2338 if GitCommand(self, cmd).Wait() != 0:
2339 if self._allrefs:
2340 raise GitError('%s cherry-pick %s ' % (self.name, rev))
2341
Anthony King7bdac712014-07-16 12:56:40 +01002342 def _Revert(self, rev):
Erwan Mahea94f1622011-08-19 13:56:09 +02002343 cmd = ['revert']
2344 cmd.append('--no-edit')
2345 cmd.append(rev)
2346 cmd.append('--')
2347 if GitCommand(self, cmd).Wait() != 0:
2348 if self._allrefs:
2349 raise GitError('%s revert %s ' % (self.name, rev))
2350
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002351 def _ResetHard(self, rev, quiet=True):
2352 cmd = ['reset', '--hard']
2353 if quiet:
2354 cmd.append('-q')
2355 cmd.append(rev)
2356 if GitCommand(self, cmd).Wait() != 0:
2357 raise GitError('%s reset --hard %s ' % (self.name, rev))
2358
Martin Kellye4e94d22017-03-21 16:05:12 -07002359 def _SyncSubmodules(self, quiet=True):
2360 cmd = ['submodule', 'update', '--init', '--recursive']
2361 if quiet:
2362 cmd.append('-q')
2363 if GitCommand(self, cmd).Wait() != 0:
2364 raise GitError('%s submodule update --init --recursive %s ' % self.name)
2365
Anthony King7bdac712014-07-16 12:56:40 +01002366 def _Rebase(self, upstream, onto=None):
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002367 cmd = ['rebase']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002368 if onto is not None:
2369 cmd.extend(['--onto', onto])
2370 cmd.append(upstream)
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002371 if GitCommand(self, cmd).Wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002372 raise GitError('%s rebase %s ' % (self.name, upstream))
2373
Pierre Tardy3d125942012-05-04 12:18:12 +02002374 def _FastForward(self, head, ffonly=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002375 cmd = ['merge', head]
Pierre Tardy3d125942012-05-04 12:18:12 +02002376 if ffonly:
2377 cmd.append("--ff-only")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002378 if GitCommand(self, cmd).Wait() != 0:
2379 raise GitError('%s merge %s ' % (self.name, head))
2380
Kevin Degiabaa7f32014-11-12 11:27:45 -07002381 def _InitGitDir(self, mirror_git=None, force_sync=False):
Kevin Degi384b3c52014-10-16 16:02:58 -06002382 init_git_dir = not os.path.exists(self.gitdir)
2383 init_obj_dir = not os.path.exists(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002384 try:
2385 # Initialize the bare repository, which contains all of the objects.
2386 if init_obj_dir:
2387 os.makedirs(self.objdir)
2388 self.bare_objdir.init()
David James8d201162013-10-11 17:03:19 -07002389
Kevin Degib1a07b82015-07-27 13:33:43 -06002390 # If we have a separate directory to hold refs, initialize it as well.
2391 if self.objdir != self.gitdir:
2392 if init_git_dir:
2393 os.makedirs(self.gitdir)
2394
2395 if init_obj_dir or init_git_dir:
2396 self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False,
2397 copy_all=True)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002398 try:
2399 self._CheckDirReference(self.objdir, self.gitdir, share_refs=False)
2400 except GitError as e:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002401 if force_sync:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002402 print("Retrying clone after deleting %s" %
2403 self.gitdir, file=sys.stderr)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002404 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002405 platform_utils.rmtree(platform_utils.realpath(self.gitdir))
2406 if self.worktree and os.path.exists(platform_utils.realpath
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002407 (self.worktree)):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002408 platform_utils.rmtree(platform_utils.realpath(self.worktree))
Kevin Degiabaa7f32014-11-12 11:27:45 -07002409 return self._InitGitDir(mirror_git=mirror_git, force_sync=False)
2410 except:
2411 raise e
2412 raise e
Kevin Degib1a07b82015-07-27 13:33:43 -06002413
Kevin Degi384b3c52014-10-16 16:02:58 -06002414 if init_git_dir:
Kevin Degib1a07b82015-07-27 13:33:43 -06002415 mp = self.manifest.manifestProject
2416 ref_dir = mp.config.GetString('repo.reference') or ''
Kevin Degi384b3c52014-10-16 16:02:58 -06002417
Kevin Degib1a07b82015-07-27 13:33:43 -06002418 if ref_dir or mirror_git:
2419 if not mirror_git:
2420 mirror_git = os.path.join(ref_dir, self.name + '.git')
2421 repo_git = os.path.join(ref_dir, '.repo', 'projects',
2422 self.relpath + '.git')
Shawn O. Pearce2816d4f2009-03-03 17:53:18 -08002423
Kevin Degib1a07b82015-07-27 13:33:43 -06002424 if os.path.exists(mirror_git):
2425 ref_dir = mirror_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002426
Kevin Degib1a07b82015-07-27 13:33:43 -06002427 elif os.path.exists(repo_git):
2428 ref_dir = repo_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002429
Kevin Degib1a07b82015-07-27 13:33:43 -06002430 else:
2431 ref_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002432
Kevin Degib1a07b82015-07-27 13:33:43 -06002433 if ref_dir:
2434 _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'),
2435 os.path.join(ref_dir, 'objects') + '\n')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002436
Kevin Degib1a07b82015-07-27 13:33:43 -06002437 self._UpdateHooks()
2438
2439 m = self.manifest.manifestProject.config
2440 for key in ['user.name', 'user.email']:
2441 if m.Has(key, include_defaults=False):
2442 self.config.SetString(key, m.GetString(key))
David Pursehouse76a4a9d2016-08-16 12:11:12 +09002443 self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
Kevin Degib1a07b82015-07-27 13:33:43 -06002444 if self.manifest.IsMirror:
2445 self.config.SetString('core.bare', 'true')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002446 else:
Kevin Degib1a07b82015-07-27 13:33:43 -06002447 self.config.SetString('core.bare', None)
2448 except Exception:
2449 if init_obj_dir and os.path.exists(self.objdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002450 platform_utils.rmtree(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002451 if init_git_dir and os.path.exists(self.gitdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002452 platform_utils.rmtree(self.gitdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002453 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002454
Jimmie Westera0444582012-10-24 13:44:42 +02002455 def _UpdateHooks(self):
2456 if os.path.exists(self.gitdir):
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002457 self._InitHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002458
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002459 def _InitHooks(self):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002460 hooks = platform_utils.realpath(self._gitdir_path('hooks'))
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002461 if not os.path.exists(hooks):
2462 os.makedirs(hooks)
Jonathan Nieder93719792015-03-17 11:29:58 -07002463 for stock_hook in _ProjectHooks():
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002464 name = os.path.basename(stock_hook)
2465
Victor Boivie65e0f352011-04-18 11:23:29 +02002466 if name in ('commit-msg',) and not self.remote.review \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002467 and self is not self.manifest.manifestProject:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002468 # Don't install a Gerrit Code Review hook if this
2469 # project does not appear to use it for reviews.
2470 #
Victor Boivie65e0f352011-04-18 11:23:29 +02002471 # Since the manifest project is one of those, but also
2472 # managed through gerrit, it's excluded
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002473 continue
2474
2475 dst = os.path.join(hooks, name)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002476 if platform_utils.islink(dst):
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002477 continue
2478 if os.path.exists(dst):
2479 if filecmp.cmp(stock_hook, dst, shallow=False):
Renaud Paquay010fed72016-11-11 14:25:29 -08002480 platform_utils.remove(dst)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002481 else:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002482 _warn("%s: Not replacing locally modified %s hook",
2483 self.relpath, name)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002484 continue
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002485 try:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002486 platform_utils.symlink(
2487 os.path.relpath(stock_hook, os.path.dirname(dst)), dst)
Sarah Owensa5be53f2012-09-09 15:37:57 -07002488 except OSError as e:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002489 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002490 raise GitError(self._get_symlink_error_message())
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002491 else:
2492 raise
2493
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002494 def _InitRemote(self):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002495 if self.remote.url:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002496 remote = self.GetRemote(self.remote.name)
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002497 remote.url = self.remote.url
Steve Raed6480452016-08-10 15:00:00 -07002498 remote.pushUrl = self.remote.pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002499 remote.review = self.remote.review
2500 remote.projectname = self.name
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002501
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002502 if self.worktree:
2503 remote.ResetFetch(mirror=False)
2504 else:
2505 remote.ResetFetch(mirror=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002506 remote.Save()
2507
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002508 def _InitMRef(self):
2509 if self.manifest.branch:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002510 self._InitAnyMRef(R_M + self.manifest.branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002511
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002512 def _InitMirrorHead(self):
Shawn O. Pearcefe200ee2009-06-01 15:28:21 -07002513 self._InitAnyMRef(HEAD)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002514
2515 def _InitAnyMRef(self, ref):
2516 cur = self.bare_ref.symref(ref)
2517
2518 if self.revisionId:
2519 if cur != '' or self.bare_ref.get(ref) != self.revisionId:
2520 msg = 'manifest set to %s' % self.revisionId
2521 dst = self.revisionId + '^0'
Anthony King7bdac712014-07-16 12:56:40 +01002522 self.bare_git.UpdateRef(ref, dst, message=msg, detach=True)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002523 else:
2524 remote = self.GetRemote(self.remote.name)
2525 dst = remote.ToLocal(self.revisionExpr)
2526 if cur != dst:
2527 msg = 'manifest set to %s' % self.revisionExpr
2528 self.bare_git.symbolic_ref('-m', msg, ref, dst)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002529
Kevin Degi384b3c52014-10-16 16:02:58 -06002530 def _CheckDirReference(self, srcdir, destdir, share_refs):
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002531 symlink_files = self.shareable_files[:]
2532 symlink_dirs = self.shareable_dirs[:]
Kevin Degi384b3c52014-10-16 16:02:58 -06002533 if share_refs:
2534 symlink_files += self.working_tree_files
2535 symlink_dirs += self.working_tree_dirs
2536 to_symlink = symlink_files + symlink_dirs
2537 for name in set(to_symlink):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002538 dst = platform_utils.realpath(os.path.join(destdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002539 if os.path.lexists(dst):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002540 src = platform_utils.realpath(os.path.join(srcdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002541 # Fail if the links are pointing to the wrong place
2542 if src != dst:
Marc Herbertec287902016-10-27 12:58:26 -07002543 _error('%s is different in %s vs %s', name, destdir, srcdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002544 raise GitError('--force-sync not enabled; cannot overwrite a local '
Simon Ruggierf9b76832015-07-31 17:18:34 -04002545 'work tree. If you\'re comfortable with the '
2546 'possibility of losing the work tree\'s git metadata,'
2547 ' use `repo sync --force-sync {0}` to '
2548 'proceed.'.format(self.relpath))
Kevin Degi384b3c52014-10-16 16:02:58 -06002549
David James8d201162013-10-11 17:03:19 -07002550 def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all):
2551 """Update |dotgit| to reference |gitdir|, using symlinks where possible.
2552
2553 Args:
2554 gitdir: The bare git repository. Must already be initialized.
2555 dotgit: The repository you would like to initialize.
2556 share_refs: If true, |dotgit| will store its refs under |gitdir|.
2557 Only one work tree can store refs under a given |gitdir|.
2558 copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
2559 This saves you the effort of initializing |dotgit| yourself.
2560 """
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002561 symlink_files = self.shareable_files[:]
2562 symlink_dirs = self.shareable_dirs[:]
David James8d201162013-10-11 17:03:19 -07002563 if share_refs:
Kevin Degi384b3c52014-10-16 16:02:58 -06002564 symlink_files += self.working_tree_files
2565 symlink_dirs += self.working_tree_dirs
David James8d201162013-10-11 17:03:19 -07002566 to_symlink = symlink_files + symlink_dirs
2567
2568 to_copy = []
2569 if copy_all:
2570 to_copy = os.listdir(gitdir)
2571
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002572 dotgit = platform_utils.realpath(dotgit)
David James8d201162013-10-11 17:03:19 -07002573 for name in set(to_copy).union(to_symlink):
2574 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002575 src = platform_utils.realpath(os.path.join(gitdir, name))
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002576 dst = os.path.join(dotgit, name)
David James8d201162013-10-11 17:03:19 -07002577
Kevin Degi384b3c52014-10-16 16:02:58 -06002578 if os.path.lexists(dst):
2579 continue
David James8d201162013-10-11 17:03:19 -07002580
2581 # If the source dir doesn't exist, create an empty dir.
2582 if name in symlink_dirs and not os.path.lexists(src):
2583 os.makedirs(src)
2584
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002585 if name in to_symlink:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002586 platform_utils.symlink(
2587 os.path.relpath(src, os.path.dirname(dst)), dst)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002588 elif copy_all and not platform_utils.islink(dst):
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002589 if os.path.isdir(src):
2590 shutil.copytree(src, dst)
2591 elif os.path.isfile(src):
2592 shutil.copy(src, dst)
2593
Conley Owens80b87fe2014-05-09 17:13:44 -07002594 # If the source file doesn't exist, ensure the destination
2595 # file doesn't either.
2596 if name in symlink_files and not os.path.lexists(src):
2597 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08002598 platform_utils.remove(dst)
Conley Owens80b87fe2014-05-09 17:13:44 -07002599 except OSError:
2600 pass
2601
David James8d201162013-10-11 17:03:19 -07002602 except OSError as e:
2603 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002604 raise DownloadError(self._get_symlink_error_message())
David James8d201162013-10-11 17:03:19 -07002605 else:
2606 raise
2607
Martin Kellye4e94d22017-03-21 16:05:12 -07002608 def _InitWorkTree(self, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002609 dotgit = os.path.join(self.worktree, '.git')
Kevin Degi384b3c52014-10-16 16:02:58 -06002610 init_dotgit = not os.path.exists(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002611 try:
2612 if init_dotgit:
2613 os.makedirs(dotgit)
2614 self._ReferenceGitDir(self.gitdir, dotgit, share_refs=True,
2615 copy_all=False)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002616
Kevin Degiabaa7f32014-11-12 11:27:45 -07002617 try:
2618 self._CheckDirReference(self.gitdir, dotgit, share_refs=True)
2619 except GitError as e:
2620 if force_sync:
2621 try:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002622 platform_utils.rmtree(dotgit)
Martin Kellye4e94d22017-03-21 16:05:12 -07002623 return self._InitWorkTree(force_sync=False, submodules=submodules)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002624 except:
2625 raise e
2626 raise e
Kevin Degi384b3c52014-10-16 16:02:58 -06002627
Kevin Degib1a07b82015-07-27 13:33:43 -06002628 if init_dotgit:
2629 _lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002630
Kevin Degib1a07b82015-07-27 13:33:43 -06002631 cmd = ['read-tree', '--reset', '-u']
2632 cmd.append('-v')
2633 cmd.append(HEAD)
2634 if GitCommand(self, cmd).Wait() != 0:
2635 raise GitError("cannot initialize work tree")
Victor Boivie0960b5b2010-11-26 13:42:13 +01002636
Martin Kellye4e94d22017-03-21 16:05:12 -07002637 if submodules:
2638 self._SyncSubmodules(quiet=True)
Kevin Degib1a07b82015-07-27 13:33:43 -06002639 self._CopyAndLinkFiles()
2640 except Exception:
2641 if init_dotgit:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002642 platform_utils.rmtree(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002643 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002644
Renaud Paquay788e9622017-01-27 11:41:12 -08002645 def _get_symlink_error_message(self):
2646 if platform_utils.isWindows():
2647 return ('Unable to create symbolic link. Please re-run the command as '
2648 'Administrator, or see '
2649 'https://github.com/git-for-windows/git/wiki/Symbolic-Links '
2650 'for other options.')
2651 return 'filesystem must support symlinks'
2652
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002653 def _gitdir_path(self, path):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002654 return platform_utils.realpath(os.path.join(self.gitdir, path))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002655
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002656 def _revlist(self, *args, **kw):
2657 a = []
2658 a.extend(args)
2659 a.append('--')
2660 return self.work_git.rev_list(*a, **kw)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002661
2662 @property
2663 def _allrefs(self):
Shawn O. Pearced237b692009-04-17 18:49:50 -07002664 return self.bare_ref.all
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002665
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002666 def _getLogs(self, rev1, rev2, oneline=False, color=True, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002667 """Get logs between two revisions of this project."""
2668 comp = '..'
2669 if rev1:
2670 revs = [rev1]
2671 if rev2:
2672 revs.extend([comp, rev2])
2673 cmd = ['log', ''.join(revs)]
2674 out = DiffColoring(self.config)
2675 if out.is_on and color:
2676 cmd.append('--color')
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002677 if pretty_format is not None:
2678 cmd.append('--pretty=format:%s' % pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002679 if oneline:
2680 cmd.append('--oneline')
2681
2682 try:
2683 log = GitCommand(self, cmd, capture_stdout=True, capture_stderr=True)
2684 if log.Wait() == 0:
2685 return log.stdout
2686 except GitError:
2687 # worktree may not exist if groups changed for example. In that case,
2688 # try in gitdir instead.
2689 if not os.path.exists(self.worktree):
2690 return self.bare_git.log(*cmd[1:])
2691 else:
2692 raise
2693 return None
2694
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002695 def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True,
2696 pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002697 """Get the list of logs from this revision to given revisionId"""
2698 logs = {}
2699 selfId = self.GetRevisionId(self._allrefs)
2700 toId = toProject.GetRevisionId(toProject._allrefs)
2701
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002702 logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color,
2703 pretty_format=pretty_format)
2704 logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color,
2705 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002706 return logs
2707
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002708 class _GitGetByExec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002709
David James8d201162013-10-11 17:03:19 -07002710 def __init__(self, project, bare, gitdir):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002711 self._project = project
2712 self._bare = bare
David James8d201162013-10-11 17:03:19 -07002713 self._gitdir = gitdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002714
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002715 def LsOthers(self):
2716 p = GitCommand(self._project,
2717 ['ls-files',
2718 '-z',
2719 '--others',
2720 '--exclude-standard'],
Anthony King7bdac712014-07-16 12:56:40 +01002721 bare=False,
David James8d201162013-10-11 17:03:19 -07002722 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002723 capture_stdout=True,
2724 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002725 if p.Wait() == 0:
2726 out = p.stdout
2727 if out:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002728 # Backslash is not anomalous
David Pursehouse1d947b32012-10-25 12:23:11 +09002729 return out[:-1].split('\0') # pylint: disable=W1401
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002730 return []
2731
2732 def DiffZ(self, name, *args):
2733 cmd = [name]
2734 cmd.append('-z')
2735 cmd.extend(args)
2736 p = GitCommand(self._project,
2737 cmd,
David James8d201162013-10-11 17:03:19 -07002738 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002739 bare=False,
2740 capture_stdout=True,
2741 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002742 try:
2743 out = p.process.stdout.read()
2744 r = {}
2745 if out:
David Pursehouse1d947b32012-10-25 12:23:11 +09002746 out = iter(out[:-1].split('\0')) # pylint: disable=W1401
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002747 while out:
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002748 try:
Anthony King2cd1f042014-05-05 21:24:05 +01002749 info = next(out)
2750 path = next(out)
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002751 except StopIteration:
2752 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002753
2754 class _Info(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002755
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002756 def __init__(self, path, omode, nmode, oid, nid, state):
2757 self.path = path
2758 self.src_path = None
2759 self.old_mode = omode
2760 self.new_mode = nmode
2761 self.old_id = oid
2762 self.new_id = nid
2763
2764 if len(state) == 1:
2765 self.status = state
2766 self.level = None
2767 else:
2768 self.status = state[:1]
2769 self.level = state[1:]
2770 while self.level.startswith('0'):
2771 self.level = self.level[1:]
2772
2773 info = info[1:].split(' ')
David Pursehouse8f62fb72012-11-14 12:09:38 +09002774 info = _Info(path, *info)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002775 if info.status in ('R', 'C'):
2776 info.src_path = info.path
Anthony King2cd1f042014-05-05 21:24:05 +01002777 info.path = next(out)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002778 r[info.path] = info
2779 return r
2780 finally:
2781 p.Wait()
2782
2783 def GetHead(self):
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002784 if self._bare:
2785 path = os.path.join(self._project.gitdir, HEAD)
2786 else:
2787 path = os.path.join(self._project.worktree, '.git', HEAD)
Conley Owens75ee0572012-11-15 17:33:11 -08002788 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002789 fd = open(path)
Dan Sandler53e902a2014-03-09 13:20:02 -04002790 except IOError as e:
2791 raise NoManifestException(path, str(e))
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002792 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002793 line = fd.readline()
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002794 finally:
2795 fd.close()
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302796 try:
2797 line = line.decode()
2798 except AttributeError:
2799 pass
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002800 if line.startswith('ref: '):
2801 return line[5:-1]
2802 return line[:-1]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002803
2804 def SetHead(self, ref, message=None):
2805 cmdv = []
2806 if message is not None:
2807 cmdv.extend(['-m', message])
2808 cmdv.append(HEAD)
2809 cmdv.append(ref)
2810 self.symbolic_ref(*cmdv)
2811
2812 def DetachHead(self, new, message=None):
2813 cmdv = ['--no-deref']
2814 if message is not None:
2815 cmdv.extend(['-m', message])
2816 cmdv.append(HEAD)
2817 cmdv.append(new)
2818 self.update_ref(*cmdv)
2819
2820 def UpdateRef(self, name, new, old=None,
2821 message=None,
2822 detach=False):
2823 cmdv = []
2824 if message is not None:
2825 cmdv.extend(['-m', message])
2826 if detach:
2827 cmdv.append('--no-deref')
2828 cmdv.append(name)
2829 cmdv.append(new)
2830 if old is not None:
2831 cmdv.append(old)
2832 self.update_ref(*cmdv)
2833
2834 def DeleteRef(self, name, old=None):
2835 if not old:
2836 old = self.rev_parse(name)
2837 self.update_ref('-d', name, old)
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07002838 self._project.bare_ref.deleted(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002839
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002840 def rev_list(self, *args, **kw):
2841 if 'format' in kw:
2842 cmdv = ['log', '--pretty=format:%s' % kw['format']]
2843 else:
2844 cmdv = ['rev-list']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002845 cmdv.extend(args)
2846 p = GitCommand(self._project,
2847 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002848 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002849 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002850 capture_stdout=True,
2851 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002852 r = []
2853 for line in p.process.stdout:
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002854 if line[-1] == '\n':
2855 line = line[:-1]
2856 r.append(line)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002857 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002858 raise GitError('%s rev-list %s: %s' %
2859 (self._project.name, str(args), p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002860 return r
2861
2862 def __getattr__(self, name):
Doug Anderson37282b42011-03-04 11:54:18 -08002863 """Allow arbitrary git commands using pythonic syntax.
2864
2865 This allows you to do things like:
2866 git_obj.rev_parse('HEAD')
2867
2868 Since we don't have a 'rev_parse' method defined, the __getattr__ will
2869 run. We'll replace the '_' with a '-' and try to run a git command.
Dave Borowitz091f8932012-10-23 17:01:04 -07002870 Any other positional arguments will be passed to the git command, and the
2871 following keyword arguments are supported:
2872 config: An optional dict of git config options to be passed with '-c'.
Doug Anderson37282b42011-03-04 11:54:18 -08002873
2874 Args:
2875 name: The name of the git command to call. Any '_' characters will
2876 be replaced with '-'.
2877
2878 Returns:
2879 A callable object that will try to call git with the named command.
2880 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002881 name = name.replace('_', '-')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002882
Dave Borowitz091f8932012-10-23 17:01:04 -07002883 def runner(*args, **kwargs):
2884 cmdv = []
2885 config = kwargs.pop('config', None)
2886 for k in kwargs:
2887 raise TypeError('%s() got an unexpected keyword argument %r'
2888 % (name, k))
2889 if config is not None:
Dave Borowitzb42b4742012-10-31 12:27:27 -07002890 if not git_require((1, 7, 2)):
2891 raise ValueError('cannot set config on command line for %s()'
2892 % name)
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302893 for k, v in config.items():
Dave Borowitz091f8932012-10-23 17:01:04 -07002894 cmdv.append('-c')
2895 cmdv.append('%s=%s' % (k, v))
2896 cmdv.append(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002897 cmdv.extend(args)
2898 p = GitCommand(self._project,
2899 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002900 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002901 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002902 capture_stdout=True,
2903 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002904 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002905 raise GitError('%s %s: %s' %
2906 (self._project.name, name, p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002907 r = p.stdout
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302908 try:
Conley Owensedd01512013-09-26 12:59:58 -07002909 r = r.decode('utf-8')
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302910 except AttributeError:
2911 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002912 if r.endswith('\n') and r.index('\n') == len(r) - 1:
2913 return r[:-1]
2914 return r
2915 return runner
2916
2917
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002918class _PriorSyncFailedError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002919
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002920 def __str__(self):
2921 return 'prior sync failed; rebase still in progress'
2922
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002923
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002924class _DirtyError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002925
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002926 def __str__(self):
2927 return 'contains uncommitted changes'
2928
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002929
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002930class _InfoMessage(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002931
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002932 def __init__(self, project, text):
2933 self.project = project
2934 self.text = text
2935
2936 def Print(self, syncbuf):
2937 syncbuf.out.info('%s/: %s', self.project.relpath, self.text)
2938 syncbuf.out.nl()
2939
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002940
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002941class _Failure(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002942
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002943 def __init__(self, project, why):
2944 self.project = project
2945 self.why = why
2946
2947 def Print(self, syncbuf):
2948 syncbuf.out.fail('error: %s/: %s',
2949 self.project.relpath,
2950 str(self.why))
2951 syncbuf.out.nl()
2952
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002953
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002954class _Later(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002955
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002956 def __init__(self, project, action):
2957 self.project = project
2958 self.action = action
2959
2960 def Run(self, syncbuf):
2961 out = syncbuf.out
2962 out.project('project %s/', self.project.relpath)
2963 out.nl()
2964 try:
2965 self.action()
2966 out.nl()
2967 return True
David Pursehouse8a68ff92012-09-24 12:15:13 +09002968 except GitError:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002969 out.nl()
2970 return False
2971
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002972
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002973class _SyncColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002974
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002975 def __init__(self, config):
2976 Coloring.__init__(self, config, 'reposync')
Anthony King7bdac712014-07-16 12:56:40 +01002977 self.project = self.printer('header', attr='bold')
2978 self.info = self.printer('info')
2979 self.fail = self.printer('fail', fg='red')
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002980
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002981
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002982class SyncBuffer(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002983
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002984 def __init__(self, config, detach_head=False):
2985 self._messages = []
2986 self._failures = []
2987 self._later_queue1 = []
2988 self._later_queue2 = []
2989
2990 self.out = _SyncColoring(config)
2991 self.out.redirect(sys.stderr)
2992
2993 self.detach_head = detach_head
2994 self.clean = True
David Rileye0684ad2017-04-05 00:02:59 -07002995 self.recent_clean = True
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002996
2997 def info(self, project, fmt, *args):
2998 self._messages.append(_InfoMessage(project, fmt % args))
2999
3000 def fail(self, project, err=None):
3001 self._failures.append(_Failure(project, err))
David Rileye0684ad2017-04-05 00:02:59 -07003002 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003003
3004 def later1(self, project, what):
3005 self._later_queue1.append(_Later(project, what))
3006
3007 def later2(self, project, what):
3008 self._later_queue2.append(_Later(project, what))
3009
3010 def Finish(self):
3011 self._PrintMessages()
3012 self._RunLater()
3013 self._PrintMessages()
3014 return self.clean
3015
David Rileye0684ad2017-04-05 00:02:59 -07003016 def Recently(self):
3017 recent_clean = self.recent_clean
3018 self.recent_clean = True
3019 return recent_clean
3020
3021 def _MarkUnclean(self):
3022 self.clean = False
3023 self.recent_clean = False
3024
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003025 def _RunLater(self):
3026 for q in ['_later_queue1', '_later_queue2']:
3027 if not self._RunQueue(q):
3028 return
3029
3030 def _RunQueue(self, queue):
3031 for m in getattr(self, queue):
3032 if not m.Run(self):
David Rileye0684ad2017-04-05 00:02:59 -07003033 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003034 return False
3035 setattr(self, queue, [])
3036 return True
3037
3038 def _PrintMessages(self):
3039 for m in self._messages:
3040 m.Print(self)
3041 for m in self._failures:
3042 m.Print(self)
3043
3044 self._messages = []
3045 self._failures = []
3046
3047
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003048class MetaProject(Project):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003049
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003050 """A special project housed under .repo.
3051 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003052
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003053 def __init__(self, manifest, name, gitdir, worktree):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003054 Project.__init__(self,
Anthony King7bdac712014-07-16 12:56:40 +01003055 manifest=manifest,
3056 name=name,
3057 gitdir=gitdir,
3058 objdir=gitdir,
3059 worktree=worktree,
3060 remote=RemoteSpec('origin'),
3061 relpath='.repo/%s' % name,
3062 revisionExpr='refs/heads/master',
3063 revisionId=None,
3064 groups=None)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003065
3066 def PreSync(self):
3067 if self.Exists:
3068 cb = self.CurrentBranch
3069 if cb:
3070 base = self.GetBranch(cb).merge
3071 if base:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003072 self.revisionExpr = base
3073 self.revisionId = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003074
Martin Kelly224a31a2017-07-10 14:46:25 -07003075 def MetaBranchSwitch(self, submodules=False):
Florian Vallee5d016502012-06-07 17:19:26 +02003076 """ Prepare MetaProject for manifest branch switch
3077 """
3078
3079 # detach and delete manifest branch, allowing a new
3080 # branch to take over
Anthony King7bdac712014-07-16 12:56:40 +01003081 syncbuf = SyncBuffer(self.config, detach_head=True)
Martin Kelly224a31a2017-07-10 14:46:25 -07003082 self.Sync_LocalHalf(syncbuf, submodules=submodules)
Florian Vallee5d016502012-06-07 17:19:26 +02003083 syncbuf.Finish()
3084
3085 return GitCommand(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003086 ['update-ref', '-d', 'refs/heads/default'],
3087 capture_stdout=True,
3088 capture_stderr=True).Wait() == 0
Florian Vallee5d016502012-06-07 17:19:26 +02003089
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003090 @property
Shawn O. Pearcef6906872009-04-18 10:49:00 -07003091 def LastFetch(self):
3092 try:
3093 fh = os.path.join(self.gitdir, 'FETCH_HEAD')
3094 return os.path.getmtime(fh)
3095 except OSError:
3096 return 0
3097
3098 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003099 def HasChanges(self):
3100 """Has the remote received new commits not yet checked out?
3101 """
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003102 if not self.remote or not self.revisionExpr:
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003103 return False
3104
David Pursehouse8a68ff92012-09-24 12:15:13 +09003105 all_refs = self.bare_ref.all
3106 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003107 head = self.work_git.GetHead()
3108 if head.startswith(R_HEADS):
3109 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09003110 head = all_refs[head]
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003111 except KeyError:
3112 head = None
3113
3114 if revid == head:
3115 return False
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003116 elif self._revlist(not_rev(HEAD), revid):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003117 return True
3118 return False